Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cce1ffa903 |
+1
-1
@@ -9,7 +9,7 @@
|
|||||||
#----------------------------------------------------------##
|
#----------------------------------------------------------##
|
||||||
#=--------------------------------------------------------------##
|
#=--------------------------------------------------------------##
|
||||||
+--------------------------+@#-%*-----------------------------------#*
|
+--------------------------+@#-%*-----------------------------------#*
|
||||||
+--------------------------%@@@@#-------------------------------------** BELUGA - VERSION 2.4
|
+--------------------------%@@@@#-------------------------------------** BELUGA - VERSION 2.3
|
||||||
*-=-------------------------#@@*---------------------------------------=%
|
*-=-------------------------#@@*---------------------------------------=%
|
||||||
%*#==--------------------------------------------------------------------+# ----- KEY-BINDS -----
|
%*#==--------------------------------------------------------------------+# ----- KEY-BINDS -----
|
||||||
*%%=-=--------------------------------------------------------------------=# CTRL-x CTRL-c leave
|
*%%=-=--------------------------------------------------------------------=# CTRL-x CTRL-c leave
|
||||||
|
|||||||
+4
-8
@@ -3,7 +3,6 @@
|
|||||||
(define TAB-LENGTH 4)
|
(define TAB-LENGTH 4)
|
||||||
(define QUIT-TIMES 1)
|
(define QUIT-TIMES 1)
|
||||||
(define THEME "dark")
|
(define THEME "dark")
|
||||||
(define LSP #t)
|
|
||||||
|
|
||||||
;; PACKAGES
|
;; PACKAGES
|
||||||
|
|
||||||
@@ -81,15 +80,12 @@
|
|||||||
(map-key "PAGE-DOWN" move-cursor-page-down "no-prefix")
|
(map-key "PAGE-DOWN" move-cursor-page-down "no-prefix")
|
||||||
(map-key "f" editor-open-file "user")
|
(map-key "f" editor-open-file "user")
|
||||||
(map-key "TAB" editor-insert-tab "no-prefix")
|
(map-key "TAB" editor-insert-tab "no-prefix")
|
||||||
|
(map-key "CTRL-k" editor-del-row "no-prefix")
|
||||||
(map-key "CTRL-s" buffer-find "no-prefix")
|
(map-key "CTRL-s" buffer-find "no-prefix")
|
||||||
(map-key "CTRL-r" buffer-find-reverse "no-prefix")
|
(map-key "CTRL-r" editor-move-to-end-of-word "no-prefix")
|
||||||
(map-key "ARROW-RIGHT" editor-switch-next-buffer "user")
|
(map-key "ARROW-RIGHT" editor-switch-next-buffer "user")
|
||||||
(map-key "\"" editor-split-screen-vertical "user")
|
(map-key "\"" editor-split-screen-vertical "user")
|
||||||
(map-key "o" editor-switch-next-pane "user")
|
(map-key "o" editor-switch-next-pane "user")
|
||||||
(map-key "*" editor-unify-panes "user")
|
(map-key "*" editor-unify-panes "user")
|
||||||
(map-key "CTRL-y" editor-paste "no-prefix")
|
(map-key "t" editor-create-terminal "user")
|
||||||
(map-key "CTRL-k" editor-cut-end-line "no-prefix")
|
|
||||||
(map-key "a" editor-move-cursor-beg-buffer "user")
|
|
||||||
(map-key "z" editor-move-cursor-end-buffer "user")
|
|
||||||
(map-key "CTRL-h" editor-auto-complete "no-prefix")
|
|
||||||
(map-key "C-x" lsp-complete "user")
|
|
||||||
|
|||||||
+13
-12
@@ -13,7 +13,7 @@
|
|||||||
* @param filename Path to the file
|
* @param filename Path to the file
|
||||||
* @return Buffer ID on success, -1 on failure
|
* @return Buffer ID on success, -1 on failure
|
||||||
*/
|
*/
|
||||||
int bufferCreate(const char *path, enum bufferStatus_e state);
|
int bufferCreate(const char *filename);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Switches to a specific buffer by ID
|
* @brief Switches to a specific buffer by ID
|
||||||
@@ -55,17 +55,18 @@ int bufferSave(void);
|
|||||||
*/
|
*/
|
||||||
int bufferSaveAll(void);
|
int bufferSaveAll(void);
|
||||||
|
|
||||||
void bufferFind(struct buffer_t *buf);
|
/**
|
||||||
void bufferFindReverse(struct buffer_t* buf);
|
* @brief Creates a new terminal buffer
|
||||||
void bufferInsertNewLine();
|
* @return Buffer ID on success, -1 on failure
|
||||||
void bufferInsertBytes(const char *src, int n);
|
*/
|
||||||
void bufferDelBytes();
|
int bufferCreateTerminal(void);
|
||||||
void bufferRowInsertBytes(struct buffer_t *buffer, row_t *row, int at, const char *src, int n);
|
|
||||||
void bufferRowDelByte(struct buffer_t *buffer, row_t *row, int at, int n);
|
|
||||||
void bufferInsertRow(struct buffer_t *buffer, int at, char *s, size_t len);
|
|
||||||
void bufferFreeRow(row_t *row);
|
|
||||||
char* bufferToText(struct buffer_t* buf);
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Executes a command in the terminal buffer
|
||||||
|
* @param buffer_id The terminal buffer ID
|
||||||
|
* @param command The command to execute
|
||||||
|
* @return 0 on success, -1 on failure
|
||||||
|
*/
|
||||||
|
int bufferExecuteTerminalCommand(int buffer_id, const char *command);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+7
-9
@@ -17,6 +17,7 @@ Lisp l_editorInsertNewLine(Lisp args, LispError* e, LispContext ctx);
|
|||||||
Lisp l_editorInserTab(Lisp args, LispError *e, LispContext ctx);
|
Lisp l_editorInserTab(Lisp args, LispError *e, LispContext ctx);
|
||||||
Lisp deletePreviousChar(Lisp args, LispError *e, LispContext ctx);
|
Lisp deletePreviousChar(Lisp args, LispError *e, LispContext ctx);
|
||||||
Lisp editorPrintC(Lisp args, LispError *e, LispContext ctx);
|
Lisp editorPrintC(Lisp args, LispError *e, LispContext ctx);
|
||||||
|
Lisp editorDelRow_L(Lisp args, LispError *e, LispContext ctx);
|
||||||
Lisp editorReadChar_L(Lisp args, LispError *e, LispContext ctx);
|
Lisp editorReadChar_L(Lisp args, LispError *e, LispContext ctx);
|
||||||
|
|
||||||
// Editor
|
// Editor
|
||||||
@@ -35,7 +36,6 @@ Lisp addPackage(Lisp args, LispError *e, LispContext ctx);
|
|||||||
|
|
||||||
Lisp editorSwitchNextBuffer(Lisp args, LispError *e, LispContext ctx);
|
Lisp editorSwitchNextBuffer(Lisp args, LispError *e, LispContext ctx);
|
||||||
Lisp bufferFind_L(Lisp args, LispError *e, LispContext ctx);
|
Lisp bufferFind_L(Lisp args, LispError *e, LispContext ctx);
|
||||||
Lisp bufferFindReverse_L(Lisp args, LispError *e, LispContext ctx);
|
|
||||||
|
|
||||||
|
|
||||||
// Pane
|
// Pane
|
||||||
@@ -45,18 +45,16 @@ Lisp editorSwitchNextPane(Lisp args, LispError *e, LispContext ctx);
|
|||||||
Lisp editorUnifiedPanes(Lisp args, LispError *e, LispContext ctx);
|
Lisp editorUnifiedPanes(Lisp args, LispError *e, LispContext ctx);
|
||||||
|
|
||||||
|
|
||||||
Lisp editorPaste(Lisp args, LispError *e, LispContext ctx);
|
|
||||||
Lisp editorCutEndLine(Lisp args, LispError *e, LispContext ctx);
|
|
||||||
|
|
||||||
Lisp editorMoveBegBuffer(Lisp args, LispError *e, LispContext ctx);
|
|
||||||
Lisp editorMoveEndBuffer(Lisp args, LispError *e, LispContext ctx);
|
|
||||||
|
|
||||||
// Other
|
// Other
|
||||||
|
|
||||||
Lisp editorAutoComplete(Lisp args, LispError *e, LispContext ctx);
|
|
||||||
// Lisp lspComplete(Lisp args, LispError *e, LispContext ctx);
|
|
||||||
Lisp lspDefinition(Lisp args, LispError *e, LispContext ctx);
|
|
||||||
|
|
||||||
void free_structs(void);
|
void free_structs(void);
|
||||||
|
|
||||||
|
// Terminal
|
||||||
|
|
||||||
|
Lisp editorCreateTerminal(Lisp args, LispError *e, LispContext ctx);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
-306
@@ -1,306 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef cJSON__h
|
|
||||||
#define cJSON__h
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C"
|
|
||||||
{
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
|
|
||||||
#define __WINDOWS__
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __WINDOWS__
|
|
||||||
|
|
||||||
/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options:
|
|
||||||
|
|
||||||
CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
|
|
||||||
CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
|
|
||||||
CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
|
|
||||||
|
|
||||||
For *nix builds that support visibility attribute, you can define similar behavior by
|
|
||||||
|
|
||||||
setting default visibility to hidden by adding
|
|
||||||
-fvisibility=hidden (for gcc)
|
|
||||||
or
|
|
||||||
-xldscope=hidden (for sun cc)
|
|
||||||
to CFLAGS
|
|
||||||
|
|
||||||
then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define CJSON_CDECL __cdecl
|
|
||||||
#define CJSON_STDCALL __stdcall
|
|
||||||
|
|
||||||
/* export symbols by default, this is necessary for copy pasting the C and header file */
|
|
||||||
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
|
|
||||||
#define CJSON_EXPORT_SYMBOLS
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(CJSON_HIDE_SYMBOLS)
|
|
||||||
#define CJSON_PUBLIC(type) type CJSON_STDCALL
|
|
||||||
#elif defined(CJSON_EXPORT_SYMBOLS)
|
|
||||||
#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
|
|
||||||
#elif defined(CJSON_IMPORT_SYMBOLS)
|
|
||||||
#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
|
|
||||||
#endif
|
|
||||||
#else /* !__WINDOWS__ */
|
|
||||||
#define CJSON_CDECL
|
|
||||||
#define CJSON_STDCALL
|
|
||||||
|
|
||||||
#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
|
|
||||||
#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
|
|
||||||
#else
|
|
||||||
#define CJSON_PUBLIC(type) type
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* project version */
|
|
||||||
#define CJSON_VERSION_MAJOR 1
|
|
||||||
#define CJSON_VERSION_MINOR 7
|
|
||||||
#define CJSON_VERSION_PATCH 19
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
/* cJSON Types: */
|
|
||||||
#define cJSON_Invalid (0)
|
|
||||||
#define cJSON_False (1 << 0)
|
|
||||||
#define cJSON_True (1 << 1)
|
|
||||||
#define cJSON_NULL (1 << 2)
|
|
||||||
#define cJSON_Number (1 << 3)
|
|
||||||
#define cJSON_String (1 << 4)
|
|
||||||
#define cJSON_Array (1 << 5)
|
|
||||||
#define cJSON_Object (1 << 6)
|
|
||||||
#define cJSON_Raw (1 << 7) /* raw json */
|
|
||||||
|
|
||||||
#define cJSON_IsReference 256
|
|
||||||
#define cJSON_StringIsConst 512
|
|
||||||
|
|
||||||
/* The cJSON structure: */
|
|
||||||
typedef struct cJSON
|
|
||||||
{
|
|
||||||
/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
|
|
||||||
struct cJSON *next;
|
|
||||||
struct cJSON *prev;
|
|
||||||
/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
|
|
||||||
struct cJSON *child;
|
|
||||||
|
|
||||||
/* The type of the item, as above. */
|
|
||||||
int type;
|
|
||||||
|
|
||||||
/* The item's string, if type==cJSON_String and type == cJSON_Raw */
|
|
||||||
char *valuestring;
|
|
||||||
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
|
|
||||||
int valueint;
|
|
||||||
/* The item's number, if type==cJSON_Number */
|
|
||||||
double valuedouble;
|
|
||||||
|
|
||||||
/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
|
|
||||||
char *string;
|
|
||||||
} cJSON;
|
|
||||||
|
|
||||||
typedef struct cJSON_Hooks
|
|
||||||
{
|
|
||||||
/* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
|
|
||||||
void *(CJSON_CDECL *malloc_fn)(size_t sz);
|
|
||||||
void (CJSON_CDECL *free_fn)(void *ptr);
|
|
||||||
} cJSON_Hooks;
|
|
||||||
|
|
||||||
typedef int cJSON_bool;
|
|
||||||
|
|
||||||
/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
|
|
||||||
* This is to prevent stack overflows. */
|
|
||||||
#ifndef CJSON_NESTING_LIMIT
|
|
||||||
#define CJSON_NESTING_LIMIT 1000
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Limits the length of circular references can be before cJSON rejects to parse them.
|
|
||||||
* This is to prevent stack overflows. */
|
|
||||||
#ifndef CJSON_CIRCULAR_LIMIT
|
|
||||||
#define CJSON_CIRCULAR_LIMIT 10000
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* returns the version of cJSON as a string */
|
|
||||||
CJSON_PUBLIC(const char*) cJSON_Version(void);
|
|
||||||
|
|
||||||
/* Supply malloc, realloc and free functions to cJSON */
|
|
||||||
CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
|
|
||||||
|
|
||||||
/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
|
|
||||||
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
|
|
||||||
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
|
|
||||||
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
|
|
||||||
|
|
||||||
/* Render a cJSON entity to text for transfer/storage. */
|
|
||||||
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
|
|
||||||
/* Render a cJSON entity to text for transfer/storage without any formatting. */
|
|
||||||
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
|
|
||||||
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
|
|
||||||
CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
|
|
||||||
/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
|
|
||||||
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
|
|
||||||
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
|
|
||||||
/* Delete a cJSON entity and all subentities. */
|
|
||||||
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
|
|
||||||
|
|
||||||
/* Returns the number of items in an array (or object). */
|
|
||||||
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
|
|
||||||
/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
|
|
||||||
/* Get item "string" from object. Case insensitive. */
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
|
|
||||||
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
|
|
||||||
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
|
|
||||||
CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
|
|
||||||
|
|
||||||
/* Check item type and return its value */
|
|
||||||
CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
|
|
||||||
CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item);
|
|
||||||
|
|
||||||
/* These functions check the type of an item */
|
|
||||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
|
|
||||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
|
|
||||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
|
|
||||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
|
|
||||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
|
|
||||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
|
|
||||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
|
|
||||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
|
|
||||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
|
|
||||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
|
|
||||||
|
|
||||||
/* These calls create a cJSON item of the appropriate type. */
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
|
|
||||||
/* raw json */
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
|
|
||||||
|
|
||||||
/* Create a string where valuestring references a string so
|
|
||||||
* it will not be freed by cJSON_Delete */
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
|
|
||||||
/* Create an object/array that only references it's elements so
|
|
||||||
* they will not be freed by cJSON_Delete */
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
|
|
||||||
|
|
||||||
/* These utilities create an Array of count items.
|
|
||||||
* The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
|
|
||||||
|
|
||||||
/* Append item to the specified array/object. */
|
|
||||||
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
|
|
||||||
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
|
|
||||||
/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
|
|
||||||
* WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
|
|
||||||
* writing to `item->string` */
|
|
||||||
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
|
|
||||||
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
|
|
||||||
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
|
|
||||||
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
|
|
||||||
|
|
||||||
/* Remove/Detach items from Arrays/Objects. */
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
|
|
||||||
CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
|
|
||||||
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
|
|
||||||
CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
|
|
||||||
|
|
||||||
/* Update array items. */
|
|
||||||
CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
|
|
||||||
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
|
|
||||||
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
|
|
||||||
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
|
|
||||||
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
|
|
||||||
|
|
||||||
/* Duplicate a cJSON item */
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
|
|
||||||
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
|
|
||||||
* need to be released. With recurse!=0, it will duplicate any children connected to the item.
|
|
||||||
* The item->next and ->prev pointers are always zero on return from Duplicate. */
|
|
||||||
/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
|
|
||||||
* case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
|
|
||||||
CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
|
|
||||||
|
|
||||||
/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
|
|
||||||
* The input pointer json cannot point to a read-only address area, such as a string constant,
|
|
||||||
* but should point to a readable and writable address area. */
|
|
||||||
CJSON_PUBLIC(void) cJSON_Minify(char *json);
|
|
||||||
|
|
||||||
/* Helper functions for creating and adding items to an object at the same time.
|
|
||||||
* They return the added item or NULL on failure. */
|
|
||||||
CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
|
|
||||||
CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
|
|
||||||
CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
|
|
||||||
CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
|
|
||||||
CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
|
|
||||||
CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
|
|
||||||
CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
|
|
||||||
CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
|
|
||||||
CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
|
|
||||||
|
|
||||||
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
|
|
||||||
#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
|
|
||||||
/* helper for the cJSON_SetNumberValue macro */
|
|
||||||
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
|
|
||||||
#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
|
|
||||||
/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
|
|
||||||
CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
|
|
||||||
|
|
||||||
/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/
|
|
||||||
#define cJSON_SetBoolValue(object, boolValue) ( \
|
|
||||||
(object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \
|
|
||||||
(object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \
|
|
||||||
cJSON_Invalid\
|
|
||||||
)
|
|
||||||
|
|
||||||
/* Macro for iterating over an array or object */
|
|
||||||
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
|
|
||||||
|
|
||||||
/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
|
|
||||||
CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
|
|
||||||
CJSON_PUBLIC(void) cJSON_free(void *object);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by Giorgio on 25/05/2026.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BELUGA_COMPLETION_H
|
|
||||||
#define BELUGA_COMPLETION_H
|
|
||||||
#include "data.h"
|
|
||||||
|
|
||||||
|
|
||||||
int lspStart(LspClient* lsp, const char* project_root);
|
|
||||||
void lspShutdown(LspClient* lsp);
|
|
||||||
|
|
||||||
// Document sync — call these from your buffer open/save/edit hooks
|
|
||||||
void lspDidOpen(LspClient* lsp, struct buffer_t* buf);
|
|
||||||
void lspDidChange(LspClient* lsp, struct buffer_t* buf);
|
|
||||||
void lspDidClose(LspClient* lsp, struct buffer_t* buf);
|
|
||||||
|
|
||||||
// Requests
|
|
||||||
void lspRequestCompletion(LspClient* lsp, struct buffer_t* buf,
|
|
||||||
int line, int col,
|
|
||||||
int screen_x, int screen_y);
|
|
||||||
void lspRequestHover(LspClient* lsp, struct buffer_t* buf, int line, int col);
|
|
||||||
void lspRequestDefinition(LspClient* lsp, struct buffer_t* buf, int line, int col);
|
|
||||||
|
|
||||||
|
|
||||||
#endif //BELUGA_COMPLETION_H
|
|
||||||
+101
-199
@@ -2,255 +2,159 @@
|
|||||||
#define DATA_H_
|
#define DATA_H_
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <termios.h>
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include "define.h"
|
#ifdef _WIN32
|
||||||
|
#include "termiWin.h"
|
||||||
|
#else
|
||||||
|
#include <termios.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "lisp.h"
|
#include "lisp.h"
|
||||||
|
|
||||||
typedef struct lsp_client_t LspClient;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \struct row_t
|
* \struct erow
|
||||||
* \brief Store one editor row
|
* \brief Store one editor row
|
||||||
* \param
|
* \param
|
||||||
* */
|
* */
|
||||||
|
|
||||||
typedef struct row
|
typedef struct frow {
|
||||||
{
|
int size; /**< Size of the line */
|
||||||
int size; /**< Size of the line */
|
int rsize; /**< Size of the render line */
|
||||||
int cap; /**< Size of the render line */
|
char *chars; /**< Characters of the line */
|
||||||
char* chars; /**< Characters of the line */
|
char *render; /**< The actual line we will print */
|
||||||
} row_t;
|
} frow;
|
||||||
|
|
||||||
typedef struct context_buffer_t
|
|
||||||
{
|
|
||||||
int editor_x, editor_y;
|
|
||||||
int width, height;
|
|
||||||
row_t* rows;
|
|
||||||
} ContextBuffer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Split modes for screen layout
|
* @brief Split modes for screen layout
|
||||||
*/
|
*/
|
||||||
typedef enum
|
typedef enum {
|
||||||
{
|
SPLIT_NONE = 0, // Single buffer fullscreen
|
||||||
SPLIT_NONE = 0, // Single buffer fullscreen
|
SPLIT_VERTICAL, // Left-right split
|
||||||
SPLIT_VERTICAL, // Left-right split
|
SPLIT_HORIZONTAL // Top-bottom split
|
||||||
SPLIT_HORIZONTAL // Top-bottom split
|
|
||||||
} SplitMode;
|
} SplitMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Represents an editor viewport/pane
|
* @brief Represents an editor viewport/pane
|
||||||
*/
|
*/
|
||||||
typedef struct
|
typedef struct {
|
||||||
{
|
int buffer_id; // Which buffer this pane displays
|
||||||
int buffer_id; // Which buffer this pane displays
|
int start_row; // Starting row on screen
|
||||||
int height; // Height of this pane
|
int start_col; // Starting column on screen
|
||||||
int width; // Width of this pane
|
int height; // Height of this pane
|
||||||
int origin_x, origin_y;
|
int width; // Width of this pane
|
||||||
int cursor_x; // Local cursor x in this pane
|
int cursor_x; // Local cursor x in this pane
|
||||||
int cursor_y; // Local cursor y in this pane
|
int cursor_y; // Local cursor y in this pane
|
||||||
int x_offset, y_offset;
|
int rx, ry;
|
||||||
int is_active; // Is this pane currently active
|
int row_offset; // Scroll offset for rows
|
||||||
|
int col_offset; // Scroll offset for columns
|
||||||
|
int is_active; // Is this pane currently active
|
||||||
} EditorPane;
|
} EditorPane;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Screen layout manager
|
* @brief Screen layout manager
|
||||||
*/
|
*/
|
||||||
typedef struct
|
typedef struct {
|
||||||
{
|
|
||||||
SplitMode mode;
|
SplitMode mode;
|
||||||
EditorPane* panes;
|
EditorPane *panes;
|
||||||
int num_panes;
|
int num_panes;
|
||||||
int active_pane; // Index of active pane
|
int active_pane; // Index of active pane
|
||||||
} ScreenLayout;
|
} ScreenLayout;
|
||||||
|
|
||||||
|
|
||||||
typedef struct theme
|
typedef struct theme {
|
||||||
{
|
char *BACKGROUND_COLOR;
|
||||||
char* BACKGROUND_COLOR;
|
char *COLOR_KEYWORD;
|
||||||
char* COLOR_KEYWORD;
|
char *COLOR_TYPE;
|
||||||
char* COLOR_TYPE;
|
char *COLOR_STRING;
|
||||||
char* COLOR_STRING;
|
char *COLOR_COMMENT;
|
||||||
char* COLOR_COMMENT;
|
char *COLOR_NUMBER;
|
||||||
char* COLOR_NUMBER;
|
char *COLOR_DEFAULT;
|
||||||
char* COLOR_DEFAULT;
|
|
||||||
} theme_t;
|
} theme_t;
|
||||||
|
|
||||||
enum bufferStatus_e
|
enum bufferStatus_e {
|
||||||
{
|
IDLE,
|
||||||
IDLE,
|
READ_ONLY,
|
||||||
READ_ONLY,
|
READ_AND_WRITE,
|
||||||
READ_AND_WRITE,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct const_t
|
struct const_t {
|
||||||
{
|
int TAB_LENGTH;
|
||||||
int TAB_LENGTH;
|
int QUIT_TIMES;
|
||||||
int QUIT_TIMES;
|
char *THEME;
|
||||||
char* THEME;
|
|
||||||
int LSP;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct prefix_t
|
struct prefix_t {
|
||||||
{
|
char prefix_name[64];
|
||||||
char prefix_name[64];
|
int prefix_id;
|
||||||
int prefix_id;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct keyBind_t
|
struct keyBind_t {
|
||||||
{
|
char *key_sequence;
|
||||||
char* key_sequence;
|
int prefix_id;
|
||||||
int prefix_id;
|
Lisp command;
|
||||||
Lisp command;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// In data.h — add these
|
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#define LSP_MAX_PENDING 64
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
LSP_NOT_STARTED = 0,
|
|
||||||
LSP_INITIALIZING,
|
|
||||||
LSP_READY,
|
|
||||||
LSP_SHUTTING_DOWN,
|
|
||||||
LSP_SHUTDOWN,
|
|
||||||
} LspState;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
int id;
|
|
||||||
void (*callback)(struct lsp_client_t* lsp, const char* json);
|
|
||||||
} LspPending;
|
|
||||||
|
|
||||||
typedef struct lsp_client_t
|
|
||||||
{
|
|
||||||
// ── Process ───────────────────────────────────────────────────────────────
|
|
||||||
pid_t pid;
|
|
||||||
int write_fd;
|
|
||||||
int read_fd;
|
|
||||||
int completion_just_arrived;
|
|
||||||
int completion_requested;
|
|
||||||
|
|
||||||
int wake_pipe[2]; // [0] = read end (main loop), [1] = write end (reader thread)
|
|
||||||
// ── State ─────────────────────────────────────────────────────────────────
|
|
||||||
LspState state;
|
|
||||||
int next_id;
|
|
||||||
|
|
||||||
// ── Pending requests ──────────────────────────────────────────────────────
|
|
||||||
LspPending pending[LSP_MAX_PENDING];
|
|
||||||
int pending_count;
|
|
||||||
|
|
||||||
// ── Threading ─────────────────────────────────────────────────────────────
|
|
||||||
pthread_t reader_thread;
|
|
||||||
pthread_mutex_t lock;
|
|
||||||
pthread_cond_t ready_cond; // signaled when state → LSP_READY
|
|
||||||
|
|
||||||
// ── Completion context ────────────────────────────────────────────────────
|
|
||||||
int completion_cursor_x; // screen position when
|
|
||||||
int completion_cursor_y; // completion was requested
|
|
||||||
} LspClient;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
char label[128]; // display text e.g. "printf"
|
|
||||||
char detail[64]; // type/sig hint e.g. "int (const char *, ...)"
|
|
||||||
int kind; // LSP CompletionItemKind (1=Text,2=Method,3=Function…)
|
|
||||||
} CompletionItem;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
CompletionItem items[COMPLETION_MAX_ITEMS];
|
|
||||||
int count;
|
|
||||||
int selected; // currently highlighted row
|
|
||||||
int visible; // is the popup shown?
|
|
||||||
int origin_x; // screen col where popup appears
|
|
||||||
int origin_y; // screen row where popup appears
|
|
||||||
} CompletionPopup;
|
|
||||||
|
|
||||||
typedef enum { DIAG_ERROR = 1, DIAG_WARNING, DIAG_HINT } DiagSeverity;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
int buffer_id;
|
|
||||||
int line; // 0-based
|
|
||||||
int col_start; // 0-based
|
|
||||||
int col_end;
|
|
||||||
DiagSeverity severity;
|
|
||||||
char message[256];
|
|
||||||
} Diagnostic;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
Diagnostic entries[DIAG_MAX];
|
|
||||||
int count;
|
|
||||||
} DiagnosticList;
|
|
||||||
|
|
||||||
enum buffer_type { FILE_BUFF, TERMINAL_BUFF };
|
enum buffer_type { FILE_BUFF, TERMINAL_BUFF };
|
||||||
|
|
||||||
struct buffer_t
|
struct buffer_t {
|
||||||
{
|
enum buffer_type type;
|
||||||
enum buffer_type type;
|
int buffer_id;
|
||||||
int buffer_id;
|
int width, height;
|
||||||
int b_lsp_open;
|
int x, y; /**< Position in the buffer (cursor) */
|
||||||
int x, y; /**< Position in the file */
|
frow *row;
|
||||||
row_t* row;
|
int numrows;
|
||||||
int numrows;
|
char *filename;
|
||||||
int b_has_changed;
|
enum bufferStatus_e state;
|
||||||
char* filename;
|
int dirty; /**< Has this buffer been modified since last save */
|
||||||
char* path;
|
int row_offset; /**< Scroll offset for rows in this buffer */
|
||||||
char * fullname;
|
int col_offset; /**< Scroll offset for columns in this buffer */
|
||||||
enum bufferStatus_e state;
|
|
||||||
int dirty; /**< Has this buffer been modified since last save */
|
// Terminal-specific data
|
||||||
|
char *terminal_command; /**< Current command being executed */
|
||||||
|
int terminal_pid; /**< Process ID of running terminal command */
|
||||||
|
char *terminal_output; /**< Captured terminal output */
|
||||||
|
int terminal_output_size; /**< Size of terminal output */
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \struct editorConfig
|
* \struct editorConfig
|
||||||
* \brief Containing our editor state.
|
* \brief Containing our editor state.
|
||||||
*/
|
*/
|
||||||
struct editorConfig
|
struct editorConfig {
|
||||||
{
|
int screenrows; /**< Terminal height*/
|
||||||
int cursor_x, cursor_y; /**< Cursor position */
|
int screencols; /**< Terminal width*/
|
||||||
int screenrows; /**< Terminal height*/
|
|
||||||
int screencols; /**< Terminal width*/
|
|
||||||
|
|
||||||
ScreenLayout layout;
|
ScreenLayout layout;
|
||||||
|
|
||||||
LspClient* lsp_client;
|
int dirty;
|
||||||
CompletionPopup lsp_completion;
|
int prefix_state;
|
||||||
DiagnosticList lsp_diagnostics;
|
char status_msg[80];
|
||||||
|
time_t status_msg_time;
|
||||||
|
struct termios orig_termios; /**< Terminal communication interface */
|
||||||
|
|
||||||
int dirty;
|
struct const_t constantes;
|
||||||
|
int quit_times_buffer;
|
||||||
|
|
||||||
char* status_msg;
|
char *init_file_path;
|
||||||
time_t status_msg_time;
|
FILE *fd_init_file;
|
||||||
struct termios orig_termios; /**< Terminal communication interface */
|
Lisp env;
|
||||||
|
LispContext ctx; /** Lisp context */
|
||||||
|
Lisp ctx_data; /** Lisp data context */
|
||||||
|
LispError ctx_error; /** Lisp ctx error */
|
||||||
|
|
||||||
struct const_t constantes;
|
struct keyBind_t *key_binds;
|
||||||
int quit_times_buffer;
|
int number_of_keybinds;
|
||||||
|
|
||||||
char* init_file_path;
|
struct prefix_t *prefix;
|
||||||
FILE* fd_init_file;
|
int number_of_prefix;
|
||||||
Lisp env;
|
|
||||||
LispContext ctx; /** Lisp context */
|
|
||||||
Lisp ctx_data; /** Lisp data context */
|
|
||||||
LispError ctx_error; /** Lisp ctx error */
|
|
||||||
|
|
||||||
struct keyBind_t* key_binds;
|
struct buffer_t buffers[64];
|
||||||
int number_of_keybinds;
|
int number_of_buffer;
|
||||||
|
|
||||||
struct prefix_t* prefix;
|
theme_t theme;
|
||||||
int number_of_prefix;
|
|
||||||
int prefix_state;
|
|
||||||
|
|
||||||
struct buffer_t buffers[64];
|
|
||||||
int number_of_buffer;
|
|
||||||
|
|
||||||
theme_t theme;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -258,13 +162,11 @@ struct editorConfig
|
|||||||
* \brief Contains text to add before writing to screen.
|
* \brief Contains text to add before writing to screen.
|
||||||
* */
|
* */
|
||||||
|
|
||||||
struct abuf
|
struct abuf {
|
||||||
{
|
char *b; /**< Text that will be printed */
|
||||||
char* b; /**< Text that will be printed */
|
int len; /**< Length of the text */
|
||||||
int len; /**< Length of the text */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct editorConfig E;
|
extern struct editorConfig E;
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+13
-41
@@ -2,56 +2,28 @@
|
|||||||
#define DEFINE_H_
|
#define DEFINE_H_
|
||||||
|
|
||||||
#define CTRL_KEY(k) ((k) & 0x1f)
|
#define CTRL_KEY(k) ((k) & 0x1f)
|
||||||
#define ALT(k) ((k) | 0x200) // set bit 9 to mark as Option combo
|
|
||||||
|
|
||||||
|
|
||||||
#define ESCAPE '\x1b'
|
#define ESCAPE '\x1b'
|
||||||
#define CURSOR_TOP_LEFT "\x1b[H"
|
#define CURSOR_TOP_LEFT "\x1b[H"
|
||||||
#define HIDE_CURSOR "\x1b[?25l"
|
#define HIDE_CURSOR "\x1b[?25l"
|
||||||
#define SHOW_CURSOR "\x1b[?25h"
|
#define SHOW_CURSOR "\x1b[?25h"
|
||||||
#define ERASE_END_LINE "\x1b[K"
|
#define ERASE_END_LINE "\x1b[K"
|
||||||
#define TAB "\t"
|
|
||||||
#define SPACE "\x20"
|
|
||||||
|
|
||||||
#ifndef PATH_MAX
|
enum editorKey {
|
||||||
#define PATH_MAX 1024
|
BACKSPACE = 127,
|
||||||
#endif
|
ARROW_LEFT = 1000,
|
||||||
|
ARROW_RIGHT,
|
||||||
/* Uncomment to see debug logs on stderr */
|
ARROW_UP,
|
||||||
#define APP_DEBUG
|
ARROW_DOWN,
|
||||||
|
DEL_KEY,
|
||||||
#define COMPLETION_MAX_ITEMS 16
|
BEG_LINE,
|
||||||
#define COMPLETION_MAX_WIDTH 40
|
END_LINE,
|
||||||
#define COMPLETION_POPUP_H 8 // max visible rows
|
PAGE_UP,
|
||||||
#define DIAG_MAX 128
|
PAGE_DOWN,
|
||||||
|
};
|
||||||
#define GUTTER_WIDTH 2
|
|
||||||
|
|
||||||
|
|
||||||
enum editorKey_e {
|
|
||||||
BACKSPACE = 127,
|
|
||||||
ARROW_LEFT = 1000,
|
|
||||||
ARROW_RIGHT,
|
|
||||||
ARROW_UP,
|
|
||||||
ARROW_DOWN,
|
|
||||||
DEL_KEY,
|
|
||||||
BEG_LINE,
|
|
||||||
END_LINE,
|
|
||||||
PAGE_UP,
|
|
||||||
PAGE_DOWN,
|
|
||||||
LSP_WAKE_KEY = 2000
|
|
||||||
};
|
|
||||||
|
|
||||||
#define ABUF_INIT {NULL, 0}
|
#define ABUF_INIT {NULL, 0}
|
||||||
|
|
||||||
#define BELUGA_VERSION "2.4"
|
#define BELUGA_VERSION "1.1"
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
#define CLIPBOARD_COPY_CMD "pbcopy"
|
|
||||||
#define CLIPBOARD_PASTE_CMD "pbpaste"
|
|
||||||
#else
|
|
||||||
#define CLIPBOARD_COPY_CMD "xclip -selection clipboard"
|
|
||||||
#define CLIPBOARD_PASTE_CMD "xclip -selection clipboard -o"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // DEFINE_H_
|
#endif // DEFINE_H_
|
||||||
|
|||||||
+5
-5
@@ -2,12 +2,12 @@
|
|||||||
#define EDITOR_OP_H_
|
#define EDITOR_OP_H_
|
||||||
|
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
|
void bufferInsertChar(int c);
|
||||||
|
|
||||||
|
void bufferInsertNewLine();
|
||||||
|
|
||||||
|
void bufferDelChar();
|
||||||
|
|
||||||
void editorSetStatusMessage(const char *fmt, ...);
|
void editorSetStatusMessage(const char *fmt, ...);
|
||||||
int editorRowCharCount(row_t *row, int x);
|
|
||||||
int editorRowCxToByte(const row_t *row, int cursor_x);
|
|
||||||
char *editorGetClipboard(void);
|
|
||||||
void editorSetClipboard(const char *text, int len);
|
|
||||||
|
|
||||||
|
|
||||||
#endif // EDITOR_OP_H_
|
#endif // EDITOR_OP_H_
|
||||||
|
|||||||
@@ -2,14 +2,21 @@
|
|||||||
#define FILE_IO_H_
|
#define FILE_IO_H_
|
||||||
|
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
|
#include "row_op.h"
|
||||||
|
#include "terminal.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
char *bufferRowsToString(struct buffer_t *buf,int *buffer_len);
|
||||||
|
|
||||||
|
|
||||||
void editorCloseFile(void);
|
void editorCloseFile(void);
|
||||||
|
|
||||||
void editorOpen(struct buffer_t *buffer);
|
void editorOpen(struct buffer_t *buffer);
|
||||||
|
|
||||||
void editorSave();
|
void editorSave();
|
||||||
|
|
||||||
|
void bufferFind(struct buffer_t *buf);
|
||||||
|
|
||||||
#endif // FILE_IO_H_
|
#endif // FILE_IO_H_
|
||||||
|
|||||||
+4
-3
@@ -1,6 +1,10 @@
|
|||||||
#ifndef INIT_H_
|
#ifndef INIT_H_
|
||||||
#define INIT_H_
|
#define INIT_H_
|
||||||
|
|
||||||
|
#include "builtins.h"
|
||||||
|
#include "data.h"
|
||||||
|
#include "terminal.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \fn void initEditor()
|
* \fn void initEditor()
|
||||||
@@ -11,7 +15,4 @@ void initBuiltins();
|
|||||||
|
|
||||||
void initEditor();
|
void initEditor();
|
||||||
|
|
||||||
void deInitEditor();
|
|
||||||
|
|
||||||
|
|
||||||
#endif // INIT_H_
|
#endif // INIT_H_
|
||||||
|
|||||||
+1
-1
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
char *editorPrompt(char *prompt, char * PlaceHolder, char bPathMode);
|
char *editorPrompt(char *prompt, char * PlaceHolder, char bPathMode);
|
||||||
|
|
||||||
const char *fileCompletion(const char *path);
|
char *key_to_string(int key);
|
||||||
|
|
||||||
int editorMoveCursor(int key);
|
int editorMoveCursor(int key);
|
||||||
|
|
||||||
|
|||||||
+7
-5
@@ -1527,7 +1527,7 @@ typedef enum
|
|||||||
TOKEN_CHAR,
|
TOKEN_CHAR,
|
||||||
TOKEN_BOOL,
|
TOKEN_BOOL,
|
||||||
TOKEN_HASH_L_PAREN,
|
TOKEN_HASH_L_PAREN,
|
||||||
} TokenType;
|
} LispTokenType;
|
||||||
|
|
||||||
/* for debug
|
/* for debug
|
||||||
static const char* token_type_name[] = {
|
static const char* token_type_name[] = {
|
||||||
@@ -1541,7 +1541,7 @@ typedef struct
|
|||||||
|
|
||||||
const char* token_start;
|
const char* token_start;
|
||||||
const char* token_end;
|
const char* token_end;
|
||||||
TokenType token;
|
LispTokenType token;
|
||||||
} Lexer;
|
} Lexer;
|
||||||
|
|
||||||
static
|
static
|
||||||
@@ -2178,9 +2178,11 @@ Lisp lisp_read_path(const char *path, LispError* out_error, LispContext ctx)
|
|||||||
|
|
||||||
return l;
|
return l;
|
||||||
#else
|
#else
|
||||||
FILE* file = fopen(path);
|
FILE* file = fopen(path, "r");
|
||||||
lisp_read_file(file, out_error, ctx);
|
if (file) {
|
||||||
fclose(path);
|
lisp_read_file(file, out_error, ctx);
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
return lisp_eof();
|
return lisp_eof();
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1163,7 +1163,7 @@ static Lisp sch_string_ref(Lisp args, LispError* e, LispContext ctx)
|
|||||||
return lisp_null();
|
return lisp_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
return lisp_make_char(lisp_string_ref(str, lisp_int(index)));
|
return lisp_make_char((int)lisp_string_ref(str, lisp_int(index)));
|
||||||
}
|
}
|
||||||
|
|
||||||
static Lisp sch_string_set(Lisp args, LispError* e, LispContext ctx)
|
static Lisp sch_string_set(Lisp args, LispError* e, LispContext ctx)
|
||||||
@@ -1737,7 +1737,7 @@ static Lisp sch_pseudo_rand(Lisp args, LispError* e, LispContext ctx)
|
|||||||
|
|
||||||
static Lisp sch_univeral_time(Lisp args, LispError* e, LispContext ctx)
|
static Lisp sch_univeral_time(Lisp args, LispError* e, LispContext ctx)
|
||||||
{
|
{
|
||||||
return lisp_make_int(time(NULL));
|
return lisp_make_int((LispInt)time(NULL));
|
||||||
}
|
}
|
||||||
|
|
||||||
static Lisp sch_is_table(Lisp args, LispError* e, LispContext ctx)
|
static Lisp sch_is_table(Lisp args, LispError* e, LispContext ctx)
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by Giorgio on 27/05/2026.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BELUGA_LSP_UI_H
|
|
||||||
#define BELUGA_LSP_UI_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "data.h"
|
|
||||||
#include "buffer.h" // for abuf
|
|
||||||
|
|
||||||
|
|
||||||
// ── Public API ────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
// Call from editorRefreshScreen() — appends popup/underlines to abuf
|
|
||||||
void lspUiDrawCompletion(struct abuf *ab, CompletionPopup *popup);
|
|
||||||
void lspUiDrawDiagnostics(struct abuf *ab, DiagnosticList *diags,
|
|
||||||
EditorPane *pane, struct buffer_t *buf);
|
|
||||||
|
|
||||||
// Input handling — returns 1 if the key was consumed by the popup
|
|
||||||
int lspUiHandleKey(CompletionPopup *popup, int key);
|
|
||||||
|
|
||||||
// Parse clangd JSON into our structs
|
|
||||||
void lspParseCompletion(const char *json, CompletionPopup *popup,
|
|
||||||
int screen_x, int screen_y);
|
|
||||||
void lspParseDiagnostics(const char *json, DiagnosticList *diags, int buffer_id);
|
|
||||||
|
|
||||||
void lspUiDrawGutter(struct abuf *ab, DiagnosticList *diags,
|
|
||||||
int buf_id, int line);
|
|
||||||
const char *lspUiDiagnosticAtCursor(DiagnosticList *diags,
|
|
||||||
int buffer_id, int cursor_line);
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@@ -9,6 +9,8 @@
|
|||||||
* \brief Draws left rows of the editor.
|
* \brief Draws left rows of the editor.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
void editorDrawRows(struct abuf *ab);
|
||||||
|
|
||||||
void editorRefreshScreen();
|
void editorRefreshScreen();
|
||||||
|
|
||||||
void editorScroll();
|
void editorScroll();
|
||||||
|
|||||||
@@ -8,8 +8,22 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int bufferRowCxToRx(frow *row, int cursor_x);
|
||||||
|
|
||||||
|
int bufferRowRxToCx(frow *row, int rx);
|
||||||
|
|
||||||
|
void bufferUpdatfrow(frow *row);
|
||||||
|
|
||||||
|
void bufferInsertRow(struct buffer_t *buffer, int at, char *s, size_t len);
|
||||||
|
|
||||||
|
void bufferFrefrow(frow *row);
|
||||||
|
|
||||||
|
void bufferDelRow(struct buffer_t *buffer, int at);
|
||||||
|
|
||||||
|
void bufferRowInsertChar(struct buffer_t *buffer, frow *row, int at, int c);
|
||||||
|
|
||||||
|
void bufferRowAppendString(struct buffer_t *buffer, frow *row, char *s, size_t len);
|
||||||
|
|
||||||
|
void bufferRowDelchar(struct buffer_t *buffer, frow *row, int at);
|
||||||
|
|
||||||
#endif // ROW_OP_H_
|
#endif // ROW_OP_H_
|
||||||
|
|||||||
@@ -59,8 +59,4 @@ ScreenLayout *splitScreenGetLayout(void);
|
|||||||
*/
|
*/
|
||||||
EditorPane *splitScreenGetActivePane(void);
|
EditorPane *splitScreenGetActivePane(void);
|
||||||
|
|
||||||
void freePane(EditorPane *pane);
|
|
||||||
void freeScreenLayout(ScreenLayout *layout);
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#ifndef SYNTAX_HIGHLIGHTER_H_
|
#ifndef SYNTAX_HIGHLIGHTER_H_
|
||||||
#define SYNTAX_HIGHLIGHTER_H_
|
#define SYNTAX_HIGHLIGHTER_H_
|
||||||
|
|
||||||
|
#include "color.h"
|
||||||
|
|
||||||
// Color codes that only affect foreground, preserving your background color
|
// Color codes that only affect foreground, preserving your background color
|
||||||
#define COLOR_RESET "\x1b[39m" // Reset all (4 bytes)
|
#define COLOR_RESET "\x1b[39m" // Reset all (4 bytes)
|
||||||
@@ -8,14 +9,16 @@
|
|||||||
// Token types
|
// Token types
|
||||||
typedef enum {
|
typedef enum {
|
||||||
TOKEN_KEYWORD,
|
TOKEN_KEYWORD,
|
||||||
TOKEN_TYPE,
|
TOKEN_DATATYPE,
|
||||||
TOKEN_STRING,
|
TOKEN_STRING,
|
||||||
TOKEN_COMMENT,
|
TOKEN_COMMENT,
|
||||||
TOKEN_NUMBER,
|
TOKEN_NUMBER,
|
||||||
TOKEN_OPERATOR,
|
TOKEN_OPERATOR,
|
||||||
TOKEN_DEFAULT
|
TOKEN_DEFAULT
|
||||||
} TokenType;
|
} SyntaxTokenType;
|
||||||
|
|
||||||
|
const char *get_color(SyntaxTokenType type);
|
||||||
char *highlight_line(const char * line, int *length);
|
char *highlight_line(const char * line, int *length);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,217 @@
|
|||||||
|
/* termiWin.h
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Christian Visintin - christian.visintin1997@gmail.com
|
||||||
|
*
|
||||||
|
* This file is part of "termiWin: a termios porting for Windows"
|
||||||
|
*
|
||||||
|
* termiWin is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* termiWin is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with termiWin. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef TERMIWIN_H_
|
||||||
|
#define TERMIWIN_H_
|
||||||
|
|
||||||
|
#define TERMIWIN_VERSION "1.2.0"
|
||||||
|
#define TERMIWIN_MAJOR_VERSION 1
|
||||||
|
#define TERMIWIN_MINOR_VERSION 2
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
#include <winsock2.h>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma comment(lib, "Ws2_32.lib")
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*Redefining functions from winsock to termiWin. This is very important since winsock2 defines functions such as close as closesocket we have to redefine it*/
|
||||||
|
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
#define TERMIWIN_DONOTREDEFINE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TERMIWIN_DONOTREDEFINE
|
||||||
|
#define read read_serial
|
||||||
|
#define write serial_write
|
||||||
|
#define open open_serial
|
||||||
|
#define close close_serial
|
||||||
|
#define select select_serial
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(SSIZE_MAX)
|
||||||
|
// ssize_t
|
||||||
|
#if SIZE_MAX == UINT_MAX
|
||||||
|
typedef int ssize_t; /* common 32 bit case */
|
||||||
|
#define SSIZE_MAX INT_MAX
|
||||||
|
#elif SIZE_MAX == ULONG_MAX
|
||||||
|
typedef long ssize_t; /* linux 64 bits */
|
||||||
|
#define SSIZE_MAX LONG_MAX
|
||||||
|
#elif SIZE_MAX == ULLONG_MAX
|
||||||
|
typedef long long ssize_t; /* windows 64 bits */
|
||||||
|
#define SSIZE_MAX LLONG_MAX
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//Serial options - Linux -> Windows
|
||||||
|
|
||||||
|
/*setAttr flags - ~ in front of flags -> deny them*/
|
||||||
|
|
||||||
|
//iFlag
|
||||||
|
|
||||||
|
#define INPCK 0x00004000 /*If this bit is set, input parity checking is enabled. If it is not set, no checking at all is done for parity errors on input; the characters are simply passed through to the application.*/
|
||||||
|
#define IGNPAR 0x00001000 /*If this bit is set, any byte with a framing or parity error is ignored. This is only useful if INPCK is also set.*/
|
||||||
|
#define PARMRK 0x00040000 /*If this bit is set, input bytes with parity or framing errors are marked when passed to the program. This bit is meaningful only when INPCK is set and IGNPAR is not set.*/
|
||||||
|
#define ISTRIP 0x00008000 /*If this bit is set, valid input bytes are stripped to seven bits; otherwise, all eight bits are available for programs to read. */
|
||||||
|
#define IGNBRK 0x00000400 /*If this bit is set, break conditions are ignored. */
|
||||||
|
#define BRKINT 0x00000100 /*If this bit is set and IGNBRK is not set, a break condition clears the terminal input and output queues and raises a SIGINT signal for the foreground process group associated with the terminal. */
|
||||||
|
#define IGNCR 0x00000800 /*If this bit is set, carriage return characters ('\r') are discarded on input. Discarding carriage return may be useful on terminals that send both carriage return and linefeed when you type the RET key. */
|
||||||
|
#define ICRNL 0x00000200 /*If this bit is set and IGNCR is not set, carriage return characters ('\r') received as input are passed to the application as newline characters ('\n').*/
|
||||||
|
#define INLCR 0x00002000 /*If this bit is set, newline characters ('\n') received as input are passed to the application as carriage return characters ('\r').*/
|
||||||
|
#define IXOFF 0x00010000 /*If this bit is set, start/stop control on input is enabled. In other words, the computer sends STOP and START characters as necessary to prevent input from coming in faster than programs are reading it. The idea is that the actual terminal hardware that is generating the input data responds to a STOP character by suspending transmission, and to a START character by resuming transmission.*/
|
||||||
|
#define IXON 0x00020000 /*If this bit is set, start/stop control on output is enabled. In other words, if the computer receives a STOP character, it suspends output until a START character is received. In this case, the STOP and START characters are never passed to the application program. If this bit is not set, then START and STOP can be read as ordinary characters.*/
|
||||||
|
|
||||||
|
//lFlag
|
||||||
|
|
||||||
|
#define ICANON 0x00001000 /*This bit, if set, enables canonical input processing mode. Otherwise, input is processed in noncanonical mode. */
|
||||||
|
#define ECHO 0x00000100 /*If this bit is set, echoing of input characters back to the terminal is enabled.*/
|
||||||
|
#define ECHOE 0x00000200 /*If this bit is set, echoing indicates erasure of input with the ERASE character by erasing the last character in the current line from the screen. Otherwise, the character erased is re-echoed to show what has happened (suitable for a printing terminal). */
|
||||||
|
#define ECHOK 0x00000400 /*This bit enables special display of the KILL character by moving to a new line after echoing the KILL character normally. The behavior of ECHOKE (below) is nicer to look at.*/
|
||||||
|
#define ECHONL 0x00000800 /*If this bit is set and the ICANON bit is also set, then the newline ('\n') character is echoed even if the ECHO bit is not set. */
|
||||||
|
#define ISIG 0x00004000 /*This bit controls whether the INTR, QUIT, and SUSP characters are recognized. The functions associated with these characters are performed if and only if this bit is set. Being in canonical or noncanonical input mode has no effect on the interpretation of these characters. */
|
||||||
|
#define IEXTEN 0x00002000 /*On BSD systems and GNU/Linux and GNU/Hurd systems, it enables the LNEXT and DISCARD characters.*/
|
||||||
|
#define NOFLSH 0x00008000 /*Normally, the INTR, QUIT, and SUSP characters cause input and output queues for the terminal to be cleared. If this bit is set, the queues are not cleared. */
|
||||||
|
#define TOSTOP 0x00010000 /*If this bit is set and the system supports job control, then SIGTTOU signals are generated by background processes that attempt to write to the terminal.*/
|
||||||
|
|
||||||
|
//cFlag
|
||||||
|
|
||||||
|
#define CSTOPB 0x00001000 /*If this bit is set, two stop bits are used. Otherwise, only one stop bit is used. */
|
||||||
|
#define PARENB 0x00004000 /*If this bit is set, generation and detection of a parity bit are enabled*/
|
||||||
|
#define PARODD 0x00008000 /*This bit is only useful if PARENB is set. If PARODD is set, odd parity is used, otherwise even parity is used. */
|
||||||
|
#define CSIZE 0x00000c00 /*This is a mask for the number of bits per character. */
|
||||||
|
#define CS5 0x00000000 /*This specifies five bits per byte. */
|
||||||
|
#define CS6 0x00000400 /*This specifies six bits per byte. */
|
||||||
|
#define CS7 0x00000800 /*This specifies seven bits per byte. */
|
||||||
|
#define CS8 0x00000c00 /*This specifies eight bits per byte. */
|
||||||
|
#define CLOCAL 0x00000000 /*Ignore modem control lines -> ignore data carrier detected - not implementable in windows*/
|
||||||
|
#define CREAD 0x00000000 /*Enable receiver - if is not set no character will be received*/
|
||||||
|
|
||||||
|
//oFlag
|
||||||
|
|
||||||
|
#define OPOST 0x00000100 /*If this bit is set, output data is processed in some unspecified way so that it is displayed appropriately on the terminal device. This typically includes mapping newline characters ('\n') onto carriage return and linefeed pairs. */
|
||||||
|
|
||||||
|
//cc
|
||||||
|
|
||||||
|
#define VEOF 0
|
||||||
|
#define VEOL 1
|
||||||
|
#define VERASE 2
|
||||||
|
#define VINTR 3
|
||||||
|
#define VKILL 4
|
||||||
|
#define VMIN 5 /*If set to 0, serial communication is NOT-BLOCKING, otherwise is BLOCKING*/
|
||||||
|
#define VQUIT 6
|
||||||
|
#define VSTART 7
|
||||||
|
#define VSTOP 8
|
||||||
|
#define VSUSP 9
|
||||||
|
#define VTIME 10
|
||||||
|
|
||||||
|
//END OF setAttr flags
|
||||||
|
|
||||||
|
/*Controls*/
|
||||||
|
#define TIOMBIC DTR_CONTROL_DISABLE
|
||||||
|
#define TIOMBIS DTR_CONTROL_ENABLE
|
||||||
|
#define CRTSCTS RTS_CONTROL_ENABLE
|
||||||
|
|
||||||
|
/*Others*/
|
||||||
|
#define NCCS 11
|
||||||
|
|
||||||
|
//Baud speed
|
||||||
|
#define B110 CBR_110
|
||||||
|
#define B300 CBR_300
|
||||||
|
#define B600 CBR_600
|
||||||
|
#define B1200 CBR_2400
|
||||||
|
#define B2400 CBR_2400
|
||||||
|
#define B4800 CBR_4800
|
||||||
|
#define B9600 CBR_9600
|
||||||
|
#define B19200 CBR_19200
|
||||||
|
#define B38400 CBR_38400
|
||||||
|
#define B57600 CBR_57600
|
||||||
|
#define B115200 CBR_115200
|
||||||
|
|
||||||
|
/*Attributes optional_actions*/
|
||||||
|
#define TCSANOW 0
|
||||||
|
#define TCSADRAIN 1
|
||||||
|
#define TCSAFLUSH 2
|
||||||
|
|
||||||
|
/*TCFLUSH options*/
|
||||||
|
#define TCIFLUSH 0
|
||||||
|
#define TCOFLUSH 1
|
||||||
|
#define TCIOFLUSH 2
|
||||||
|
|
||||||
|
/*TCFLOW optons*/
|
||||||
|
|
||||||
|
#define TCOOFF 0
|
||||||
|
#define TCOON 1
|
||||||
|
#define TCIOFF 2
|
||||||
|
#define TCION 3
|
||||||
|
|
||||||
|
//typdef
|
||||||
|
typedef unsigned tcflag_t; /*This is an unsigned integer type used to represent the various bit masks for terminal flags.*/
|
||||||
|
typedef unsigned cc_t; /*This is an unsigned integer type used to represent characters associated with various terminal control functions.*/
|
||||||
|
typedef unsigned speed_t; /*used for terminal baud rates*/
|
||||||
|
|
||||||
|
typedef struct termios
|
||||||
|
{
|
||||||
|
|
||||||
|
tcflag_t c_iflag; /*input modes*/
|
||||||
|
tcflag_t c_oflag; /*output modes*/
|
||||||
|
tcflag_t c_cflag; /*control modes*/
|
||||||
|
tcflag_t c_lflag; /*local modes*/
|
||||||
|
cc_t c_cc[NCCS]; /*special character*/
|
||||||
|
|
||||||
|
} termios;
|
||||||
|
|
||||||
|
//Serial configuration functions
|
||||||
|
|
||||||
|
int tcgetattr(int fd, struct termios *termios_p);
|
||||||
|
int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);
|
||||||
|
int tcsendbreak(int fd, int duration);
|
||||||
|
int tcdrain(int fd);
|
||||||
|
int tcflush(int fd, int queue_selector);
|
||||||
|
int tcflow(int fd, int action);
|
||||||
|
void cfmakeraw(struct termios *termios_p);
|
||||||
|
speed_t cfgetispeed(const struct termios *termios_p);
|
||||||
|
speed_t cfgetospeed(const struct termios *termios_p);
|
||||||
|
int cfsetispeed(struct termios *termios_p, speed_t speed);
|
||||||
|
int cfsetospeed(struct termios *termios_p, speed_t speed);
|
||||||
|
int cfsetspeed(struct termios * termios_p, speed_t speed);
|
||||||
|
|
||||||
|
//Write/Read/Open/Close/Select Functions
|
||||||
|
|
||||||
|
ssize_t read_serial(int fd, void* buffer, size_t count);
|
||||||
|
ssize_t write_serial(int fd, const void* buffer, size_t count);
|
||||||
|
int open_serial(const char* portname, int opt);
|
||||||
|
int close_serial(int fd);
|
||||||
|
int select_serial(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
|
||||||
|
|
||||||
|
//get Handle out of the COM structure
|
||||||
|
HANDLE getHandle();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#pragma message("-Warning: termiWin requires a Windows system!")
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
+3
-6
@@ -4,11 +4,12 @@
|
|||||||
|
|
||||||
/* includes */
|
/* includes */
|
||||||
|
|
||||||
|
#include "data.h"
|
||||||
|
#include "define.h"
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/ioctl.h>
|
// #include <sys/ioctl.h>
|
||||||
#include <termios.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -29,8 +30,4 @@ int getCursorPosition(int *rows, int *cols);
|
|||||||
|
|
||||||
int getWindowSize(int *rows, int *cols);
|
int getWindowSize(int *rows, int *cols);
|
||||||
|
|
||||||
char *keyToString(int key);
|
|
||||||
|
|
||||||
void appDebug(const char *fmt, ...);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/* termios.h
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Christian Visintin - christian.visintin1997@gmail.com
|
||||||
|
*
|
||||||
|
* This file is part of "termiWin: a termios porting for Windows"
|
||||||
|
*
|
||||||
|
* termiWin is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* termiWin is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with termiWin. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#ifndef TERMIOS_H
|
||||||
|
#define TERMIOS_H
|
||||||
|
|
||||||
|
#include "termiWin.h"
|
||||||
|
|
||||||
|
#endif // TERMIOS_H
|
||||||
|
|
||||||
|
#endif // _WIN32
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by Giorgio on 01/05/2026.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BELUGA_UTF8_H
|
|
||||||
#define BELUGA_UTF8_H
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
uint32_t readUtf8Char(void);
|
|
||||||
int utf8Encode(uint32_t cp, char *buf);
|
|
||||||
int utf8Seqlen(unsigned char c);
|
|
||||||
int codepointWidth(uint32_t codepoint);
|
|
||||||
uint32_t utf8Decode(const char** s);
|
|
||||||
int is_word_char(const char *s);
|
|
||||||
|
|
||||||
|
|
||||||
#endif //BELUGA_UTF8_H
|
|
||||||
Regular → Executable
@@ -5,64 +5,50 @@
|
|||||||
* interactions. \version 0.1 \date 21 septembre 2024
|
* interactions. \version 0.1 \date 21 septembre 2024
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "include/buffer.h"
|
||||||
|
#include "include/split_screen.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#define _DEFAULT_SOURCE
|
#define _DEFAULT_SOURCE
|
||||||
#define _BSD_SOURCE
|
#define _BSD_SOURCE
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
#include <libgen.h>
|
|
||||||
|
|
||||||
#include "include/buffer.h"
|
|
||||||
#include "include/split_screen.h"
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "include/data.h"
|
#include "include/data.h"
|
||||||
|
#include "include/file_io.h"
|
||||||
#include "include/init.h"
|
#include "include/init.h"
|
||||||
#include "include/input.h"
|
#include "include/input.h"
|
||||||
#include "include/editor_op.h"
|
#include "include/editor_op.h"
|
||||||
#include "include/terminal.h"
|
#include "include/terminal.h"
|
||||||
#include "include/completion.h"
|
|
||||||
#include <signal.h>
|
|
||||||
|
|
||||||
|
|
||||||
struct editorConfig E;
|
struct editorConfig E;
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
// Get HOME with NULL check
|
|
||||||
const char *home = getenv("HOME");
|
|
||||||
if (!home) {
|
|
||||||
fprintf(stderr, "Error: HOME environment variable not set\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate and build splash screen path safely
|
char * splash_screen = (char *) calloc(256, sizeof(char));
|
||||||
char *splash_screen;
|
|
||||||
if (asprintf(&splash_screen, "%s/.beluga/assets/beluga.txt", home) == -1) {
|
|
||||||
fprintf(stderr, "Error: Failed to allocate splash screen path\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
signal(SIGPIPE, SIG_IGN);
|
|
||||||
enableRawMode();
|
|
||||||
initEditor();
|
|
||||||
|
|
||||||
|
enableRawMode();
|
||||||
|
initEditor();
|
||||||
|
if (argc >= 2) {
|
||||||
EditorPane *active = splitScreenGetActivePane();
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
struct buffer_t *buf;
|
active->buffer_id = bufferCreate(argv[1]);
|
||||||
|
} else {
|
||||||
|
strcat(splash_screen, getenv("HOME"));
|
||||||
|
strcat(splash_screen, "/.beluga/assets/beluga.txt");
|
||||||
|
fprintf(stderr, "%s\n", splash_screen);
|
||||||
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
|
active->buffer_id = bufferCreate(splash_screen);
|
||||||
|
}
|
||||||
|
//free(splash_screen);
|
||||||
|
|
||||||
appDebug("splash : %s\n", splash_screen);
|
editorSetStatusMessage("HELP: Ctrl-x Ctrl-s = save | Ctrl-x Ctrl-c = quit");
|
||||||
active->buffer_id = bufferCreate(splash_screen, READ_ONLY);
|
|
||||||
|
|
||||||
if (argc >= 2) {
|
|
||||||
active->buffer_id = bufferCreate(argv[1], READ_AND_WRITE);
|
|
||||||
buf = &E.buffers[active->buffer_id];
|
|
||||||
}
|
|
||||||
|
|
||||||
free(splash_screen); // Now guaranteed safe
|
while (1) {
|
||||||
|
editorRefreshScreen();
|
||||||
editorSetStatusMessage("HELP: Ctrl-x Ctrl-s = save | Ctrl-x Ctrl-c = quit");
|
editorProcessKeypress();
|
||||||
while (1) {
|
}
|
||||||
editorRefreshScreen();
|
return 0;
|
||||||
editorProcessKeypress();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-8
@@ -1,13 +1,12 @@
|
|||||||
project('beluga', 'c',
|
project('beluga', 'c',
|
||||||
version : '2.3',
|
version : '2.3',
|
||||||
default_options : [
|
default_options : [
|
||||||
'c_std=c99',
|
'c_std=none',
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
cc = meson.get_compiler('c')
|
cc = meson.get_compiler('c')
|
||||||
m = cc.find_library('m', required: false)
|
m = cc.find_library('m', required: false)
|
||||||
thread_dep = dependency('threads')
|
|
||||||
|
|
||||||
# Source files
|
# Source files
|
||||||
src_files = files(
|
src_files = files(
|
||||||
@@ -23,15 +22,16 @@ src_files = files(
|
|||||||
'src/terminal.c',
|
'src/terminal.c',
|
||||||
'src/builtins.c',
|
'src/builtins.c',
|
||||||
'src/buffer.c',
|
'src/buffer.c',
|
||||||
'src/split_screen.c',
|
'src/split_screen.c'
|
||||||
'src/utf8.c',
|
|
||||||
'src/completion.c',
|
|
||||||
'src/lsp_ui.c',
|
|
||||||
'src/cJSON.c'
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Add termiWin.c for Windows builds
|
||||||
|
if host_machine.system() == 'windows'
|
||||||
|
src_files += files('src/termiWin.c')
|
||||||
|
endif
|
||||||
|
|
||||||
# Executable
|
# Executable
|
||||||
executable('beluga',
|
executable('beluga',
|
||||||
src_files,
|
src_files,
|
||||||
dependencies: [m, thread_dep]
|
dependencies: [m]
|
||||||
)
|
)
|
||||||
|
|||||||
+3
-5
@@ -1,5 +1,7 @@
|
|||||||
#include "../include/append_buffer.h"
|
#include "../include/append_buffer.h"
|
||||||
|
|
||||||
|
extern struct editorConfig E;
|
||||||
|
|
||||||
void abAppend(struct abuf *ab, const char *s, int len) {
|
void abAppend(struct abuf *ab, const char *s, int len) {
|
||||||
char *new = realloc(ab->b, ab->len + len);
|
char *new = realloc(ab->b, ab->len + len);
|
||||||
|
|
||||||
@@ -11,8 +13,4 @@ void abAppend(struct abuf *ab, const char *s, int len) {
|
|||||||
ab->len += len;
|
ab->len += len;
|
||||||
}
|
}
|
||||||
|
|
||||||
void abFree(struct abuf *ab) {
|
void abFree(struct abuf *ab) { free(ab->b); }
|
||||||
free(ab->b);
|
|
||||||
ab->b = NULL;
|
|
||||||
ab->len = 0;
|
|
||||||
}
|
|
||||||
|
|||||||
+140
-341
@@ -10,26 +10,19 @@
|
|||||||
#include "include/split_screen.h"
|
#include "include/split_screen.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <libgen.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#include "include/completion.h"
|
|
||||||
#include "include/input.h"
|
|
||||||
|
|
||||||
|
extern struct editorConfig E;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Finds a buffer by filename
|
* @brief Finds a buffer by filename
|
||||||
* @param filename The filename to search for
|
* @param filename The filename to search for
|
||||||
* @return Buffer ID if found, -1 if not found
|
* @return Buffer ID if found, -1 if not found
|
||||||
*/
|
*/
|
||||||
static int bufferFindByFilename(const char* filename)
|
static int bufferFindByFilename(const char *filename) {
|
||||||
{
|
for (int i = 0; i < E.number_of_buffer; i++) {
|
||||||
for (int i = 0; i < E.number_of_buffer; i++)
|
|
||||||
{
|
|
||||||
if (E.buffers[i].filename != NULL &&
|
if (E.buffers[i].filename != NULL &&
|
||||||
strcmp(E.buffers[i].filename, filename) == 0)
|
strcmp(E.buffers[i].filename, filename) == 0) {
|
||||||
{
|
|
||||||
return E.buffers[i].buffer_id;
|
return E.buffers[i].buffer_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -41,12 +34,9 @@ static int bufferFindByFilename(const char* filename)
|
|||||||
* @param buffer_id The buffer ID to find
|
* @param buffer_id The buffer ID to find
|
||||||
* @return Pointer to buffer, NULL if not found
|
* @return Pointer to buffer, NULL if not found
|
||||||
*/
|
*/
|
||||||
struct buffer_t* bufferFindById(int buffer_id)
|
struct buffer_t *bufferFindById(int buffer_id) {
|
||||||
{
|
for (int i = 0; i < E.number_of_buffer; i++) {
|
||||||
for (int i = 0; i < E.number_of_buffer; i++)
|
if (E.buffers[i].buffer_id == buffer_id) {
|
||||||
{
|
|
||||||
if (E.buffers[i].buffer_id == buffer_id)
|
|
||||||
{
|
|
||||||
return &E.buffers[i];
|
return &E.buffers[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -57,56 +47,38 @@ struct buffer_t* bufferFindById(int buffer_id)
|
|||||||
* @brief Creates a new buffer for a file
|
* @brief Creates a new buffer for a file
|
||||||
* @details Allocates buffer slot, loads file content, saves buffer metadata.
|
* @details Allocates buffer slot, loads file content, saves buffer metadata.
|
||||||
* If file is already open, switches to existing buffer instead.
|
* If file is already open, switches to existing buffer instead.
|
||||||
|
* @param filename Path to the file
|
||||||
* @return Buffer ID on success, -1 on failure
|
* @return Buffer ID on success, -1 on failure
|
||||||
*/
|
*/
|
||||||
int bufferCreate(const char* path, enum bufferStatus_e state)
|
int bufferCreate(const char *filename) {
|
||||||
{
|
|
||||||
appDebug("Creating new buffer");
|
|
||||||
char *path_cpy = strdup(path);
|
|
||||||
char* filename = basename((char*)path_cpy);
|
|
||||||
char fullname[PATH_MAX + 1];
|
|
||||||
// Check if file is already open
|
// Check if file is already open
|
||||||
const int existing_id = bufferFindByFilename(path);
|
int existing_id = bufferFindByFilename(filename);
|
||||||
if (existing_id != -1)
|
if (existing_id != -1) {
|
||||||
{
|
|
||||||
return bufferSwitch(existing_id);
|
return bufferSwitch(existing_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we have space for more buffers
|
// Check if we have space for more buffers
|
||||||
if (E.number_of_buffer >= 64)
|
if (E.number_of_buffer >= 64) {
|
||||||
{
|
|
||||||
editorSetStatusMessage("Error: maximum buffers reached (64)");
|
editorSetStatusMessage("Error: maximum buffers reached (64)");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize new buffer
|
// Initialize new buffer
|
||||||
struct buffer_t* new_buf = &E.buffers[E.number_of_buffer];
|
struct buffer_t *new_buf = &E.buffers[E.number_of_buffer];
|
||||||
new_buf->buffer_id = E.number_of_buffer;
|
new_buf->buffer_id = E.number_of_buffer;
|
||||||
new_buf->filename = strdup(filename);
|
new_buf->filename = strdup(filename);
|
||||||
if (!new_buf->filename)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
realpath(path, fullname);
|
|
||||||
new_buf->fullname = strdup(fullname);
|
|
||||||
new_buf->path = dirname(fullname);
|
|
||||||
new_buf->type = FILE_BUFF;
|
new_buf->type = FILE_BUFF;
|
||||||
new_buf->state = state;
|
new_buf->state = READ_AND_WRITE;
|
||||||
new_buf->x = 0;
|
new_buf->x = 0;
|
||||||
new_buf->y = 0;
|
new_buf->y = 0;
|
||||||
new_buf->dirty = 0; // New file starts clean
|
new_buf->row_offset = 0;
|
||||||
new_buf->b_lsp_open = 0;
|
new_buf->col_offset = 0;
|
||||||
|
new_buf->dirty = 0; // New file starts clean
|
||||||
|
|
||||||
// Load file content using existing editorOpen
|
// Load file content using existing editorOpen
|
||||||
editorOpen(new_buf);
|
editorOpen(new_buf);
|
||||||
|
|
||||||
E.number_of_buffer++;
|
E.number_of_buffer++;
|
||||||
if (new_buf->filename[strlen(new_buf->filename) - 1] == 'c')
|
|
||||||
{
|
|
||||||
if (E.lsp_client->state == LSP_SHUTDOWN)
|
|
||||||
lspStart(E.lsp_client, new_buf->path);
|
|
||||||
while (E.lsp_client->state != LSP_READY);
|
|
||||||
lspDidOpen(E.lsp_client, new_buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
editorSetStatusMessage("Opened: %s (buffer %d)", filename, new_buf->buffer_id);
|
editorSetStatusMessage("Opened: %s (buffer %d)", filename, new_buf->buffer_id);
|
||||||
return new_buf->buffer_id;
|
return new_buf->buffer_id;
|
||||||
@@ -118,19 +90,17 @@ int bufferCreate(const char* path, enum bufferStatus_e state)
|
|||||||
* @param buffer_id The buffer ID to switch to
|
* @param buffer_id The buffer ID to switch to
|
||||||
* @return 0 on success, -1 on failure
|
* @return 0 on success, -1 on failure
|
||||||
*/
|
*/
|
||||||
int bufferSwitch(int buffer_id)
|
int bufferSwitch(int buffer_id) {
|
||||||
{
|
struct buffer_t *target = bufferFindById(buffer_id);
|
||||||
struct buffer_t* target = bufferFindById(buffer_id);
|
if (target == NULL) {
|
||||||
if (target == NULL)
|
E.layout.panes[E.layout.active_pane].buffer_id = buffer_id;
|
||||||
{
|
|
||||||
E.layout.panes[E.layout.active_pane].buffer_id = buffer_id;
|
|
||||||
editorSetStatusMessage("Error: buffer not found");
|
editorSetStatusMessage("Error: buffer not found");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
editorSetStatusMessage("Switched to: %s (buffer %d)",
|
editorSetStatusMessage("Switched to: %s (buffer %d)",
|
||||||
target->filename, buffer_id);
|
target->filename, buffer_id);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,38 +112,30 @@ int bufferSwitch(int buffer_id)
|
|||||||
* @param buffer_id The buffer ID to close
|
* @param buffer_id The buffer ID to close
|
||||||
* @return 0 on success, -1 on failure
|
* @return 0 on success, -1 on failure
|
||||||
*/
|
*/
|
||||||
int bufferClose(int buffer_id)
|
int bufferClose(int buffer_id) {
|
||||||
{
|
struct buffer_t *buf = bufferFindById(buffer_id);
|
||||||
struct buffer_t* buf = bufferFindById(buffer_id);
|
if (buf == NULL) {
|
||||||
if (buf == NULL)
|
|
||||||
{
|
|
||||||
editorSetStatusMessage("Error: buffer not found");
|
editorSetStatusMessage("Error: buffer not found");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorPane* active = splitScreenGetActivePane();
|
EditorPane * active = splitScreenGetActivePane();
|
||||||
|
|
||||||
// If this is the current buffer, find next buffer to switch to
|
// If this is the current buffer, find next buffer to switch to
|
||||||
if (active->buffer_id == buffer_id)
|
if (active->buffer_id == buffer_id) {
|
||||||
{
|
|
||||||
int next_id = -1;
|
int next_id = -1;
|
||||||
|
|
||||||
// Try to switch to next buffer
|
// Try to switch to next buffer
|
||||||
for (int i = 0; i < E.number_of_buffer; i++)
|
for (int i = 0; i < E.number_of_buffer; i++) {
|
||||||
{
|
if (E.buffers[i].buffer_id != buffer_id) {
|
||||||
if (E.buffers[i].buffer_id != buffer_id)
|
|
||||||
{
|
|
||||||
next_id = E.buffers[i].buffer_id;
|
next_id = E.buffers[i].buffer_id;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (next_id != -1)
|
if (next_id != -1) {
|
||||||
{
|
|
||||||
bufferSwitch(next_id);
|
bufferSwitch(next_id);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// No other buffers, clear editor
|
// No other buffers, clear editor
|
||||||
editorCloseFile();
|
editorCloseFile();
|
||||||
}
|
}
|
||||||
@@ -185,12 +147,9 @@ int bufferClose(int buffer_id)
|
|||||||
buf->buffer_id = -1;
|
buf->buffer_id = -1;
|
||||||
|
|
||||||
// Remove from buffer list by shifting
|
// Remove from buffer list by shifting
|
||||||
for (int i = 0; i < E.number_of_buffer; i++)
|
for (int i = 0; i < E.number_of_buffer; i++) {
|
||||||
{
|
if (E.buffers[i].buffer_id == buffer_id) {
|
||||||
if (E.buffers[i].buffer_id == buffer_id)
|
for (int j = i; j < E.number_of_buffer - 1; j++) {
|
||||||
{
|
|
||||||
for (int j = i; j < E.number_of_buffer - 1; j++)
|
|
||||||
{
|
|
||||||
E.buffers[j] = E.buffers[j + 1];
|
E.buffers[j] = E.buffers[j + 1];
|
||||||
}
|
}
|
||||||
E.number_of_buffer--;
|
E.number_of_buffer--;
|
||||||
@@ -206,9 +165,8 @@ int bufferClose(int buffer_id)
|
|||||||
* @brief Gets the current active buffer
|
* @brief Gets the current active buffer
|
||||||
* @return Pointer to current buffer_t, NULL if none
|
* @return Pointer to current buffer_t, NULL if none
|
||||||
*/
|
*/
|
||||||
struct buffer_t* bufferGetCurrent(void)
|
struct buffer_t *bufferGetCurrent(void) {
|
||||||
{
|
EditorPane * active = splitScreenGetActivePane();
|
||||||
EditorPane* active = splitScreenGetActivePane();
|
|
||||||
return bufferFindById(active->buffer_id);
|
return bufferFindById(active->buffer_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,25 +175,22 @@ struct buffer_t* bufferGetCurrent(void)
|
|||||||
* @details Prints buffer information to status message
|
* @details Prints buffer information to status message
|
||||||
* @return Number of open buffers
|
* @return Number of open buffers
|
||||||
*/
|
*/
|
||||||
int bufferListAll(void)
|
int bufferListAll(void) {
|
||||||
{
|
if (E.number_of_buffer == 0) {
|
||||||
if (E.number_of_buffer == 0)
|
|
||||||
{
|
|
||||||
editorSetStatusMessage("No buffers open");
|
editorSetStatusMessage("No buffers open");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char buf[256] = "";
|
char buf[256] = "";
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
EditorPane* active = splitScreenGetActivePane();
|
EditorPane * active = splitScreenGetActivePane();
|
||||||
|
|
||||||
for (int i = 0; i < E.number_of_buffer; i++)
|
for (int i = 0; i < E.number_of_buffer; i++) {
|
||||||
{
|
struct buffer_t *b = &E.buffers[i];
|
||||||
struct buffer_t* b = &E.buffers[i];
|
|
||||||
char marker = (b->buffer_id == active->buffer_id) ? '*' : ' ';
|
char marker = (b->buffer_id == active->buffer_id) ? '*' : ' ';
|
||||||
offset += snprintf(&buf[offset], sizeof(buf) - offset,
|
offset += snprintf(&buf[offset], sizeof(buf) - offset,
|
||||||
"%c%d:%s ", marker, b->buffer_id,
|
"%c%d:%s ", marker, b->buffer_id,
|
||||||
b->filename ? b->filename : "[No Name]");
|
b->filename ? b->filename : "[No Name]");
|
||||||
}
|
}
|
||||||
|
|
||||||
editorSetStatusMessage("Buffers: %s", buf);
|
editorSetStatusMessage("Buffers: %s", buf);
|
||||||
@@ -246,11 +201,9 @@ int bufferListAll(void)
|
|||||||
* @brief Saves current buffer to disk
|
* @brief Saves current buffer to disk
|
||||||
* @return 0 on success, -1 on failure
|
* @return 0 on success, -1 on failure
|
||||||
*/
|
*/
|
||||||
int bufferSave(void)
|
int bufferSave(void) {
|
||||||
{
|
EditorPane * active = splitScreenGetActivePane();
|
||||||
EditorPane* active = splitScreenGetActivePane();
|
if (active->buffer_id == -1) {
|
||||||
if (active->buffer_id == -1)
|
|
||||||
{
|
|
||||||
editorSetStatusMessage("Error: no buffer active");
|
editorSetStatusMessage("Error: no buffer active");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -264,22 +217,16 @@ int bufferSave(void)
|
|||||||
* @details Iterates through all buffers, saves each one
|
* @details Iterates through all buffers, saves each one
|
||||||
* @return 0 on success, -1 on failure
|
* @return 0 on success, -1 on failure
|
||||||
*/
|
*/
|
||||||
int bufferSaveAll(void)
|
int bufferSaveAll(void) {
|
||||||
{
|
|
||||||
int saved = 0;
|
int saved = 0;
|
||||||
int failed = 0;
|
int failed = 0;
|
||||||
|
|
||||||
for (int i = 0; i < E.number_of_buffer; i++)
|
for (int i = 0; i < E.number_of_buffer; i++) {
|
||||||
{
|
if (bufferSwitch(E.buffers[i].buffer_id) == 0) {
|
||||||
if (bufferSwitch(E.buffers[i].buffer_id) == 0)
|
if (E.dirty && bufferSave() == 0) {
|
||||||
{
|
|
||||||
if (E.dirty && bufferSave() == 0)
|
|
||||||
{
|
|
||||||
saved++;
|
saved++;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
failed++;
|
failed++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -289,249 +236,101 @@ int bufferSaveAll(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Searches for a string in the document
|
* @brief Creates a new terminal buffer
|
||||||
* @details Prompts user for a search query, then searches forward from current
|
* @return Buffer ID on success, -1 on failure
|
||||||
* cursor position. Updates cursor position to the first match found.
|
|
||||||
* @note Updates global editor state E (cursor position, row_offset)
|
|
||||||
* @note Search is case-sensitive and operates on rendered line content
|
|
||||||
* @note Searches begin from the line after current cursor position
|
|
||||||
* @see editorPrompt()
|
|
||||||
* @see editorRowRxToCx()
|
|
||||||
*/
|
*/
|
||||||
void bufferFind(struct buffer_t* buf)
|
int bufferCreateTerminal(void) {
|
||||||
{
|
// Check if we have space for more buffers
|
||||||
appDebug("searching\n");
|
if (E.number_of_buffer >= 64) {
|
||||||
char* query = editorPrompt("Search: %s (ESC to cancel)", "", 0);
|
editorSetStatusMessage("Error: maximum buffers reached (64)");
|
||||||
|
return -1;
|
||||||
if (query == NULL)
|
|
||||||
return;
|
|
||||||
int i;
|
|
||||||
for (i = buf->y + 1; i < buf->numrows; i++)
|
|
||||||
{
|
|
||||||
row_t* row = &buf->row[i];
|
|
||||||
char* match = strstr(row->chars, query);
|
|
||||||
if (match)
|
|
||||||
{
|
|
||||||
buf->y = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
void bufferFindReverse(struct buffer_t* buf)
|
|
||||||
{
|
|
||||||
appDebug("searching\n");
|
|
||||||
char* query = editorPrompt("Reverse search: %s (ESC to cancel)", "", 0);
|
|
||||||
|
|
||||||
if (query == NULL)
|
|
||||||
return;
|
|
||||||
int i;
|
|
||||||
if (!buf->y)
|
|
||||||
return;
|
|
||||||
for (i = buf->y - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
row_t* row = &buf->row[i];
|
|
||||||
char* match = strstr(row->chars, query);
|
|
||||||
if (match)
|
|
||||||
{
|
|
||||||
buf->y = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
void bufferInsertRow(struct buffer_t* buffer, int at, char* s, size_t len)
|
|
||||||
{
|
|
||||||
if (at < 0 || at > buffer->numrows)
|
|
||||||
return;
|
|
||||||
|
|
||||||
row_t* tmp = realloc(buffer->row, sizeof(row_t) * (buffer->numrows + 1));
|
|
||||||
if (!tmp)
|
|
||||||
return;
|
|
||||||
buffer->row = tmp;
|
|
||||||
|
|
||||||
/* Shift existing rows to make room at 'at' — no at++ */
|
|
||||||
if (at < buffer->numrows)
|
|
||||||
{
|
|
||||||
memmove(&buffer->row[at + 1], &buffer->row[at],
|
|
||||||
sizeof(row_t) * (buffer->numrows - at)); /* not -at+1 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer->row[at].size = (int)len;
|
// Initialize new terminal buffer
|
||||||
buffer->row[at].cap = (int)len + 1;
|
struct buffer_t *new_buf = &E.buffers[E.number_of_buffer];
|
||||||
buffer->row[at].chars = malloc(len + 1);
|
new_buf->buffer_id = E.number_of_buffer;
|
||||||
if (!buffer->row[at].chars)
|
new_buf->filename = strdup("[Terminal]");
|
||||||
return;
|
new_buf->type = TERMINAL_BUFF;
|
||||||
memcpy(buffer->row[at].chars, s, len);
|
new_buf->state = READ_AND_WRITE;
|
||||||
buffer->row[at].chars[len] = '\0'; /* always NUL-terminate */
|
new_buf->x = 0;
|
||||||
|
new_buf->y = 0;
|
||||||
|
new_buf->row_offset = 0;
|
||||||
|
new_buf->col_offset = 0;
|
||||||
|
new_buf->dirty = 0;
|
||||||
|
new_buf->numrows = 0;
|
||||||
|
new_buf->row = NULL;
|
||||||
|
|
||||||
buffer->numrows++;
|
// Initialize terminal-specific data
|
||||||
buffer->dirty++;
|
new_buf->terminal_command = NULL;
|
||||||
}
|
new_buf->terminal_pid = -1;
|
||||||
|
new_buf->terminal_output = NULL;
|
||||||
|
new_buf->terminal_output_size = 0;
|
||||||
|
|
||||||
void bufferFreeRow(row_t* row) { free(row->chars); }
|
// Initialize terminal-specific data
|
||||||
|
new_buf->terminal_command = NULL;
|
||||||
|
new_buf->terminal_pid = -1;
|
||||||
|
new_buf->terminal_output = NULL;
|
||||||
|
new_buf->terminal_output_size = 0;
|
||||||
|
|
||||||
/**
|
E.number_of_buffer++;
|
||||||
* \fn editorRowInsertChar(erow *row, int at, int c)
|
|
||||||
* \param at Index of where we want to insert the char */
|
|
||||||
|
|
||||||
void bufferRowInsertBytes(struct buffer_t* buffer, row_t* row, int at,
|
editorSetStatusMessage("Created terminal buffer %d", new_buf->buffer_id);
|
||||||
const char* src, int n)
|
return new_buf->buffer_id;
|
||||||
{
|
|
||||||
if (buffer->state == READ_ONLY)
|
|
||||||
return;
|
|
||||||
if (at < 0 || at > row->size)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (row->size + n + 1 > row->cap)
|
|
||||||
{
|
|
||||||
size_t new_cap = row->cap == 0 ? 128 : row->cap * 2;
|
|
||||||
while (new_cap < row->size + n + 1) {
|
|
||||||
new_cap *= 2;
|
|
||||||
}
|
|
||||||
char *new_chars = realloc(row->chars, new_cap);
|
|
||||||
if (!new_chars) {
|
|
||||||
return; // Allocation failed
|
|
||||||
}
|
|
||||||
row->chars = new_chars;
|
|
||||||
row->cap = (int) new_cap;
|
|
||||||
}
|
|
||||||
memmove(row->chars + at + n, row->chars + at, row->size - at);
|
|
||||||
|
|
||||||
// Copy new bytes
|
|
||||||
memcpy(row->chars + at, src, n);
|
|
||||||
row->size += n;
|
|
||||||
|
|
||||||
row->chars[row->size] = '\0';
|
|
||||||
++buffer->dirty;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \fn bufferRowDelChar(struct bufferConfig *E, frow *frow, int at)
|
* @brief Executes a command in the terminal buffer
|
||||||
* \brief Delete the a char at the chosen position on the given row
|
* @param buffer_id The terminal buffer ID
|
||||||
* \param at Index of the char to delete
|
* @param command The command to execute
|
||||||
* \param row Row on operation is made */
|
* @return 0 on success, -1 on failure
|
||||||
void bufferRowDelByte(struct buffer_t* buffer, row_t* row, int at, int n)
|
*/
|
||||||
{
|
int bufferExecuteTerminalCommand(int buffer_id, const char *command) {
|
||||||
if (buffer->state == READ_ONLY)
|
struct buffer_t *buf = bufferFindById(buffer_id);
|
||||||
return;
|
if (buf == NULL || buf->type != TERMINAL_BUFF) {
|
||||||
if (at < 0 || at >= row->size)
|
editorSetStatusMessage("Error: buffer is not a terminal buffer");
|
||||||
return;
|
return -1;
|
||||||
memmove(row->chars + at, row->chars + at + n, row->size - at - n);
|
}
|
||||||
row->size -= n;
|
|
||||||
row->chars[row->size] = '\0';
|
// Free any existing terminal data
|
||||||
buffer->x -= n;
|
if (buf->terminal_command) {
|
||||||
++buffer->dirty;
|
free(buf->terminal_command);
|
||||||
}
|
}
|
||||||
|
if (buf->terminal_output) {
|
||||||
|
free(buf->terminal_output);
|
||||||
void bufferInsertBytes(const char* src, int n)
|
}
|
||||||
{
|
|
||||||
appDebug("bufferInsertBytes \r\n");
|
// Store the command
|
||||||
EditorPane* active = splitScreenGetActivePane();
|
buf->terminal_command = strdup(command);
|
||||||
struct buffer_t* buf = bufferFindById(active->buffer_id);
|
|
||||||
if (buf->y == buf->numrows)
|
// For now, we'll simulate terminal output
|
||||||
{
|
// In a real implementation, this would fork a process and capture output
|
||||||
bufferInsertRow(buf, buf->numrows, "", 0);
|
char output[256];
|
||||||
}
|
snprintf(output, sizeof(output), "$ %s\n[Command executed: %s]\n", command, command);
|
||||||
bufferRowInsertBytes(buf, &buf->row[buf->y], buf->x, src, n);
|
|
||||||
buf->x += n;
|
buf->terminal_output = strdup(output);
|
||||||
buf->b_has_changed = 1;
|
buf->terminal_output_size = strlen(output);
|
||||||
}
|
|
||||||
|
// Update the buffer content to show terminal output
|
||||||
void bufferDelBytes(void)
|
// Clear existing rows
|
||||||
{
|
for (int i = 0; i < buf->numrows; ++i) {
|
||||||
EditorPane* active = splitScreenGetActivePane();
|
free(buf->row[i].chars);
|
||||||
struct buffer_t* buf = bufferFindById(active->buffer_id);
|
free(buf->row[i].render);
|
||||||
|
}
|
||||||
/* Nothing to delete */
|
free(buf->row);
|
||||||
if (buf->numrows == 0) return;
|
buf->row = NULL;
|
||||||
if (buf->x == 0 && buf->y == 0) return;
|
buf->numrows = 0;
|
||||||
|
|
||||||
/* Use row_offset, not col_offset, for row indexing */
|
// Add terminal output as rows
|
||||||
row_t* r = &buf->row[buf->y];
|
// Make a copy for parsing since strtok modifies the string
|
||||||
|
char *output_copy = strdup(buf->terminal_output);
|
||||||
if (buf->x > 0)
|
char *line = strtok(output_copy, "\n");
|
||||||
{
|
while (line != NULL) {
|
||||||
int byte_end = editorRowCxToByte(r, buf->x);
|
bufferInsertRow(buf, buf->numrows, line, strlen(line));
|
||||||
int byte_start = editorRowCxToByte(r, buf->x - 1);
|
line = strtok(NULL, "\n");
|
||||||
int char_width = byte_end - byte_start; /* byte width of the character */
|
}
|
||||||
|
free(output_copy);
|
||||||
bufferRowDelByte(buf, r, byte_start, char_width);
|
|
||||||
E.dirty = 1;
|
buf->dirty = 1;
|
||||||
}
|
editorSetStatusMessage("Executed: %s", command);
|
||||||
else
|
return 0;
|
||||||
{
|
|
||||||
/* Merge current row into the previous one */
|
|
||||||
row_t* prev = &buf->row[buf->y - 1]; // FIX: was buf->y (same as r)
|
|
||||||
int prev_char_count = editorRowCharCount(prev, prev->size);
|
|
||||||
|
|
||||||
bufferRowInsertBytes(buf, prev, prev->size, r->chars, r->size);
|
|
||||||
free(r->chars);
|
|
||||||
r->chars = NULL;
|
|
||||||
|
|
||||||
memmove(&buf->row[buf->y],
|
|
||||||
&buf->row[buf->y + 1],
|
|
||||||
sizeof(row_t) * (buf->numrows - buf->y - 1));
|
|
||||||
buf->numrows--;
|
|
||||||
|
|
||||||
active->cursor_x = prev_char_count;
|
|
||||||
buf->x = prev_char_count;
|
|
||||||
active->cursor_y--;
|
|
||||||
buf->y--;
|
|
||||||
E.dirty = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void bufferInsertNewLine(void)
|
|
||||||
{
|
|
||||||
appDebug("Inserting new line\n");
|
|
||||||
EditorPane* active = splitScreenGetActivePane();
|
|
||||||
struct buffer_t* buf = bufferFindById(active->buffer_id);
|
|
||||||
|
|
||||||
|
|
||||||
if (buf->y >= buf->numrows)
|
|
||||||
{
|
|
||||||
/* Cursor is past the last row: just append a blank line */
|
|
||||||
bufferInsertRow(buf, buf->numrows, "", 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
row_t* row = &buf->row[buf->y];
|
|
||||||
|
|
||||||
/* Insert the tail (from cursor to end) as a new row below */
|
|
||||||
bufferInsertRow(buf, buf->y + 1, &row->chars[buf->x], row->size - buf->x);
|
|
||||||
|
|
||||||
/* Re-fetch: bRealloc inside bufferInsertRow may have moved the array */
|
|
||||||
row = &buf->row[buf->y];
|
|
||||||
|
|
||||||
/* Truncate the current row at the cursor */
|
|
||||||
row->size = buf->x;
|
|
||||||
row->chars[row->size] = '\0';
|
|
||||||
/* Do NOT touch row->cap — the allocation is still valid */
|
|
||||||
}
|
|
||||||
|
|
||||||
buf->y++;
|
|
||||||
buf->x = 0;
|
|
||||||
appDebug("Insert new line done\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
char* bufferToText(struct buffer_t* buf)
|
|
||||||
{
|
|
||||||
int total = 0;
|
|
||||||
for (int i = 0; i < buf->numrows; i++)
|
|
||||||
total += buf->row[i].size + 1; // +1 for \n
|
|
||||||
char* text = malloc(total + 1);
|
|
||||||
char* p = text;
|
|
||||||
for (int i = 0; i < buf->numrows; i++)
|
|
||||||
{
|
|
||||||
memcpy(p, buf->row[i].chars, buf->row[i].size);
|
|
||||||
p += buf->row[i].size;
|
|
||||||
*p++ = '\n';
|
|
||||||
}
|
|
||||||
*p = '\0';
|
|
||||||
return text;
|
|
||||||
}
|
}
|
||||||
|
|||||||
+261
-395
@@ -2,28 +2,23 @@
|
|||||||
* @file builtins.c
|
* @file builtins.c
|
||||||
* @brief Built-in Lisp functions for editor operations
|
* @brief Built-in Lisp functions for editor operations
|
||||||
* @details Provides Lisp bindings for core editor functionality including
|
* @details Provides Lisp bindings for core editor functionality including
|
||||||
* cursor movement, file operations, keybinding management, and text
|
* cursor movement, file operations, keybinding management, and text manipulation
|
||||||
* manipulation
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../include/builtins.h"
|
#include "../include/builtins.h"
|
||||||
#include "../include/buffer.h"
|
|
||||||
#include "../include/data.h"
|
#include "../include/data.h"
|
||||||
#include "../include/define.h"
|
#include "../include/define.h"
|
||||||
#include "../include/editor_op.h"
|
#include "../include/editor_op.h"
|
||||||
#include "../include/file_io.h"
|
#include "../include/file_io.h"
|
||||||
#include "../include/input.h"
|
#include "../include/input.h"
|
||||||
#include "../include/terminal.h"
|
#include "../include/row_op.h"
|
||||||
|
#include "../include/buffer.h"
|
||||||
#include "../include/split_screen.h"
|
#include "../include/split_screen.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "include/completion.h"
|
|
||||||
#include "include/init.h"
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Finds a prefix configuration by name
|
* @brief Finds a prefix configuration by name
|
||||||
* @details Searches the prefix array for a prefix matching the given name.
|
* @details Searches the prefix array for a prefix matching the given name.
|
||||||
@@ -32,17 +27,14 @@
|
|||||||
* @return Matching prefix_t structure, or E.prefix[0] if not found
|
* @return Matching prefix_t structure, or E.prefix[0] if not found
|
||||||
* @note Updates global editor state E
|
* @note Updates global editor state E
|
||||||
*/
|
*/
|
||||||
struct prefix_t find_prefix(const char prefix_name[64])
|
struct prefix_t find_prefix(const char prefix_name[64]) {
|
||||||
{
|
int i = E.number_of_prefix + 1;
|
||||||
int i = E.number_of_prefix + 1;
|
while (i--) {
|
||||||
while (i--)
|
if (!strcmp(prefix_name, E.prefix[i].prefix_name)) {
|
||||||
{
|
return E.prefix[i];
|
||||||
if (!strcmp(prefix_name, E.prefix[i].prefix_name))
|
|
||||||
{
|
|
||||||
return E.prefix[i];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return E.prefix[0];
|
}
|
||||||
|
return E.prefix[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,43 +50,54 @@ struct prefix_t find_prefix(const char prefix_name[64])
|
|||||||
* @return lisp_null()
|
* @return lisp_null()
|
||||||
* @note Updates global editor state E (key_binds array)
|
* @note Updates global editor state E (key_binds array)
|
||||||
*/
|
*/
|
||||||
Lisp mapKey(Lisp args, LispError* e, LispContext ctx)
|
Lisp mapKey(Lisp args, LispError *e, LispContext ctx) {
|
||||||
{
|
/*
|
||||||
/*
|
* 3 arguments keybind command prefix
|
||||||
* 3 arguments keybind command prefix
|
*/
|
||||||
*/
|
const char *key_sequence = lisp_string(lisp_car(args));
|
||||||
const char* key_sequence = lisp_string(lisp_car(args));
|
args = lisp_cdr(args);
|
||||||
void* memory_temp;
|
// second argument
|
||||||
args = lisp_cdr(args);
|
Lisp func = lisp_car(args);
|
||||||
// second argument
|
|
||||||
const Lisp func = lisp_car(args);
|
|
||||||
|
|
||||||
memory_temp = realloc(
|
E.key_binds = (struct keyBind_t *)realloc(
|
||||||
E.key_binds, ++E.number_of_keybinds * sizeof(struct keyBind_t));
|
E.key_binds, ++E.number_of_keybinds * sizeof(struct keyBind_t));
|
||||||
E.key_binds = (struct keyBind_t*)memory_temp;
|
E.key_binds[E.number_of_keybinds - 1].key_sequence =
|
||||||
if (!E.key_binds)
|
(char *)malloc(50 * sizeof(char));
|
||||||
editorQuit(args, e, ctx);
|
|
||||||
E.key_binds[E.number_of_keybinds - 1].key_sequence =
|
|
||||||
(char*)malloc(50 * sizeof(char));
|
|
||||||
|
|
||||||
strncpy(E.key_binds[E.number_of_keybinds - 1].key_sequence, key_sequence, 50);
|
strncpy(E.key_binds[E.number_of_keybinds - 1].key_sequence, key_sequence, 50);
|
||||||
|
|
||||||
E.key_binds[E.number_of_keybinds - 1].command = func;
|
E.key_binds[E.number_of_keybinds - 1].command = func;
|
||||||
|
|
||||||
// Third argument
|
// Third argument
|
||||||
args = lisp_cdr(args);
|
args = lisp_cdr(args);
|
||||||
const char* prefix_name = lisp_string(lisp_car(args));
|
const char *prefix_name = lisp_string(lisp_car(args));
|
||||||
struct prefix_t prefix = find_prefix(prefix_name);
|
struct prefix_t prefix = find_prefix(prefix_name);
|
||||||
|
|
||||||
E.key_binds[E.number_of_keybinds - 1].prefix_id = prefix.prefix_id;
|
E.key_binds[E.number_of_keybinds - 1].prefix_id = prefix.prefix_id;
|
||||||
|
|
||||||
return lisp_null();
|
return lisp_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Lisp function to create a new terminal buffer
|
||||||
|
* @details Creates a new terminal buffer and switches to it.
|
||||||
|
* @param args Lisp arguments (ignored)
|
||||||
|
* @param e Error pointer for Lisp error handling
|
||||||
|
* @param ctx Lisp context
|
||||||
|
* @return lisp_null()
|
||||||
|
* @note Updates global editor state E
|
||||||
|
*/
|
||||||
|
Lisp editorCreateTerminal(Lisp args, LispError *e, LispContext ctx) {
|
||||||
|
int buffer_id = bufferCreateTerminal();
|
||||||
|
if (buffer_id >= 0) {
|
||||||
|
bufferSwitch(buffer_id);
|
||||||
|
}
|
||||||
|
return lisp_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Lisp function to move cursor in a specified direction
|
* @brief Lisp function to move cursor in a specified direction
|
||||||
* @details Moves the editor cursor up, down, left, or right based on direction
|
* @details Moves the editor cursor up, down, left, or right based on direction string.
|
||||||
* string.
|
|
||||||
* @param args Lisp list with one argument: direction string
|
* @param args Lisp list with one argument: direction string
|
||||||
* - "u": up, "d": down, "r": right, "l": left
|
* - "u": up, "d": down, "r": right, "l": left
|
||||||
* @param e Error pointer for Lisp error handling
|
* @param e Error pointer for Lisp error handling
|
||||||
@@ -103,98 +106,85 @@ Lisp mapKey(Lisp args, LispError* e, LispContext ctx)
|
|||||||
* @note Updates global editor state E
|
* @note Updates global editor state E
|
||||||
* @see editorMoveCursor()
|
* @see editorMoveCursor()
|
||||||
*/
|
*/
|
||||||
Lisp moveCursor(Lisp args, LispError* e, LispContext ctx)
|
Lisp moveCursor(Lisp args, LispError *e, LispContext ctx) {
|
||||||
{
|
const char *direction = lisp_string(lisp_car(args));
|
||||||
const char* direction = lisp_string(lisp_car(args));
|
int is_in = 0;
|
||||||
int is_in = 0;
|
switch (direction[0]) {
|
||||||
switch (direction[0])
|
case 'u':
|
||||||
{
|
is_in = editorMoveCursor(ARROW_UP);
|
||||||
case 'u':
|
break;
|
||||||
is_in = editorMoveCursor(ARROW_UP);
|
case 'd':
|
||||||
break;
|
is_in = editorMoveCursor(ARROW_DOWN);
|
||||||
case 'd':
|
break;
|
||||||
is_in = editorMoveCursor(ARROW_DOWN);
|
case 'r':
|
||||||
break;
|
is_in = editorMoveCursor(ARROW_RIGHT);
|
||||||
case 'r':
|
break;
|
||||||
is_in = editorMoveCursor(ARROW_RIGHT);
|
case 'l':
|
||||||
break;
|
is_in = editorMoveCursor(ARROW_LEFT);
|
||||||
case 'l':
|
break;
|
||||||
is_in = editorMoveCursor(ARROW_LEFT);
|
}
|
||||||
break;
|
fprintf(stderr, "move lisp %d\n", is_in);
|
||||||
default:
|
return lisp_make_bool(is_in);
|
||||||
break;
|
|
||||||
}
|
|
||||||
appDebug("move lisp %d\n", is_in);
|
|
||||||
return lisp_make_bool(is_in);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief bFrees all dynamically allocated editor structures
|
* @brief Frees all dynamically allocated editor structures
|
||||||
* @details Releases memory for prefix table, keybinds, filename, and all rows.
|
* @details Releases memory for prefix table, keybinds, filename, and all rows.
|
||||||
* Called during shutdown to prevent memory leaks.
|
* Called during shutdown to prevent memory leaks.
|
||||||
* @note Updates global editor state E
|
* @note Updates global editor state E
|
||||||
*/
|
*/
|
||||||
void bFree_structs(void)
|
void free_structs(void) {
|
||||||
{
|
int i, j;
|
||||||
int i, j;
|
free(E.prefix);
|
||||||
free(E.prefix);
|
for (i = 0; i < E.number_of_keybinds; ++i) {
|
||||||
for (i = 0; i < E.number_of_keybinds; ++i) {
|
free(E.key_binds[i].key_sequence);
|
||||||
free(E.key_binds[i].key_sequence);
|
}
|
||||||
E.key_binds[i].key_sequence = NULL;
|
free(E.key_binds);
|
||||||
}
|
// free layout
|
||||||
free(E.key_binds);
|
free(E.layout.panes);
|
||||||
E.key_binds = NULL;
|
|
||||||
E.number_of_keybinds = 0;
|
|
||||||
|
|
||||||
// Similar fix for buffers
|
// Free buffers
|
||||||
for (i = 0; i < E.number_of_buffer; ++i) {
|
for (i = 0; i < E.number_of_buffer; ++i) {
|
||||||
struct buffer_t *b = &E.buffers[i];
|
free(E.buffers[i].filename);
|
||||||
for (j = 0; j < b->numrows; j++)
|
for (j = 0; j < E.buffers[i].numrows; ++j) {
|
||||||
free(b->row[j].chars); // ← free each row's content
|
free(E.buffers[i].row[j].render);
|
||||||
free(b->row); // ← then free the array
|
free(E.buffers[i].row[j].chars);
|
||||||
if (b->filename) free(b->filename);
|
|
||||||
// if (b->path) free(b->path);
|
|
||||||
if (b->fullname) free(b->fullname);
|
|
||||||
}
|
}
|
||||||
// bFree layout
|
free(E.buffers[i].row);
|
||||||
free(E.layout.panes);
|
}
|
||||||
free(E.status_msg);
|
|
||||||
|
free(E.init_file_path);
|
||||||
|
fclose(E.fd_init_file);
|
||||||
|
|
||||||
free(E.init_file_path);
|
|
||||||
fclose(E.fd_init_file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Lisp function to quit the editor
|
* @brief Lisp function to quit the editor
|
||||||
* @details Closes editor with unsaved changes protection. Prompts user to
|
* @details Closes editor with unsaved changes protection. Prompts user to confirm
|
||||||
* confirm quit after multiple attempts if file is dirty. Cleans up resources
|
* quit after multiple attempts if file is dirty. Cleans up resources and restores terminal.
|
||||||
* and restores terminal.
|
|
||||||
* @param args Lisp arguments (unused)
|
* @param args Lisp arguments (unused)
|
||||||
* @param e Error pointer for Lisp error handling
|
* @param e Error pointer for Lisp error handling
|
||||||
* @param ctx Lisp context
|
* @param ctx Lisp context
|
||||||
* @return lisp_null() (never returns on successful exit)
|
* @return lisp_null() (never returns on successful exit)
|
||||||
* @note Calls exit(0) to terminate program
|
* @note Calls exit(0) to terminate program
|
||||||
* @note Updates quit_times_buffer counter
|
* @note Updates quit_times_buffer counter
|
||||||
* @see bFree_structs()
|
* @see free_structs()
|
||||||
*/
|
*/
|
||||||
Lisp editorQuit(Lisp args, LispError* e, LispContext ctx)
|
Lisp editorQuit(Lisp args, LispError *e, LispContext ctx) {
|
||||||
{
|
if (E.dirty && E.quit_times_buffer > 0) {
|
||||||
if (E.dirty && E.quit_times_buffer > 0)
|
editorSetStatusMessage("WARNING! Changes hasn't been saved. Press Ctrl-Q "
|
||||||
{
|
"another time to quit.");
|
||||||
editorSetStatusMessage("WARNING! Changes hasn't been saved. Press Ctrl-Q "
|
--E.quit_times_buffer;
|
||||||
"another time to quit.");
|
return lisp_null();
|
||||||
--E.quit_times_buffer;
|
}
|
||||||
return lisp_null();
|
free_structs();
|
||||||
}
|
write(STDOUT_FILENO, "\x1b[2J", 4);
|
||||||
disableRawMode();
|
write(STDOUT_FILENO, CURSOR_TOP_LEFT, 3);
|
||||||
bFree_structs();
|
disableRawMode();
|
||||||
deInitEditor();
|
lisp_shutdown(E.ctx);
|
||||||
write(STDOUT_FILENO, "\x1b[2J", 4);
|
exit(0);
|
||||||
write(STDOUT_FILENO, CURSOR_TOP_LEFT, 3);
|
|
||||||
lspShutdown(E.lsp_client);
|
return lisp_null();
|
||||||
free(E.lsp_client);
|
|
||||||
lisp_shutdown(E.ctx);
|
|
||||||
exit(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -207,24 +197,20 @@ Lisp editorQuit(Lisp args, LispError* e, LispContext ctx)
|
|||||||
* @see editorSave()
|
* @see editorSave()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Lisp l_editorSplitScreenVertical(Lisp args, LispError* e, LispContext ctx)
|
Lisp l_editorSplitScreenVertical(Lisp args, LispError *e, LispContext ctx) {
|
||||||
{
|
if (E.number_of_buffer >= 2) {
|
||||||
if (E.number_of_buffer >= 2)
|
|
||||||
{
|
|
||||||
splitScreenVertical(E.buffers[0].buffer_id, E.buffers[1].buffer_id);
|
splitScreenVertical(E.buffers[0].buffer_id, E.buffers[1].buffer_id);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
editorSetStatusMessage("Need at least 2 buffers open");
|
editorSetStatusMessage("Need at least 2 buffers open");
|
||||||
}
|
}
|
||||||
return lisp_null();
|
return lisp_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
Lisp l_editorSave(Lisp args, LispError* e, LispContext ctx)
|
Lisp l_editorSave(Lisp args, LispError *e, LispContext ctx) {
|
||||||
{
|
|
||||||
appDebug("[EDITOR SAVE]");
|
editorSave();
|
||||||
editorSave();
|
|
||||||
return lisp_null();
|
return lisp_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -236,11 +222,11 @@ Lisp l_editorSave(Lisp args, LispError* e, LispContext ctx)
|
|||||||
* @return lisp_null()
|
* @return lisp_null()
|
||||||
* @see editorInsertNewLine()
|
* @see editorInsertNewLine()
|
||||||
*/
|
*/
|
||||||
Lisp l_editorInsertNewLine(Lisp args, LispError* e, LispContext ctx)
|
Lisp l_editorInsertNewLine(Lisp args, LispError *e, LispContext ctx) {
|
||||||
{
|
|
||||||
bufferInsertNewLine();
|
|
||||||
|
|
||||||
return lisp_null();
|
bufferInsertNewLine();
|
||||||
|
|
||||||
|
return lisp_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -252,14 +238,13 @@ Lisp l_editorInsertNewLine(Lisp args, LispError* e, LispContext ctx)
|
|||||||
* @return lisp_null()
|
* @return lisp_null()
|
||||||
* @note Uses E.constantes.TAB_LENGTH for indentation width
|
* @note Uses E.constantes.TAB_LENGTH for indentation width
|
||||||
*/
|
*/
|
||||||
Lisp l_editorInserTab(Lisp args, LispError* e, LispContext ctx)
|
Lisp l_editorInserTab(Lisp args, LispError *e, LispContext ctx) {
|
||||||
{
|
|
||||||
for (int i = 0; i < E.constantes.TAB_LENGTH; ++i)
|
|
||||||
{
|
|
||||||
bufferInsertBytes(" ", 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return lisp_null();
|
for (int i = 0; i<E.constantes.TAB_LENGTH; ++i) {
|
||||||
|
bufferInsertChar(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
return lisp_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -271,12 +256,9 @@ Lisp l_editorInserTab(Lisp args, LispError* e, LispContext ctx)
|
|||||||
* @return lisp_null()
|
* @return lisp_null()
|
||||||
* @note Updates global editor state E
|
* @note Updates global editor state E
|
||||||
*/
|
*/
|
||||||
Lisp moveCursorBeginLine(Lisp args, LispError* e, LispContext ctx)
|
Lisp moveCursorBeginLine(Lisp args, LispError *e, LispContext ctx) {
|
||||||
{
|
E.layout.panes[E.layout.active_pane].cursor_x = 0;
|
||||||
EditorPane* active = splitScreenGetActivePane();
|
return lisp_null();
|
||||||
struct buffer_t* buf = bufferFindById(active->buffer_id);
|
|
||||||
buf->x = 0;
|
|
||||||
return lisp_null();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -288,12 +270,13 @@ Lisp moveCursorBeginLine(Lisp args, LispError* e, LispContext ctx)
|
|||||||
* @return lisp_null()
|
* @return lisp_null()
|
||||||
* @note Updates global editor state E
|
* @note Updates global editor state E
|
||||||
*/
|
*/
|
||||||
Lisp moveCursorEndLine(Lisp args, LispError* e, LispContext ctx)
|
Lisp moveCursorEndLine(Lisp args, LispError *e, LispContext ctx) {
|
||||||
{
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
EditorPane* active = splitScreenGetActivePane();
|
struct buffer_t *buf = bufferFindById(active->buffer_id);
|
||||||
struct buffer_t* buf = bufferFindById(active->buffer_id);
|
if (active->cursor_y < buf->numrows) {
|
||||||
buf->x = buf->row[buf->y].size;
|
active->cursor_x = buf->row[active->cursor_y].size;
|
||||||
return lisp_null();
|
}
|
||||||
|
return lisp_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -305,16 +288,14 @@ Lisp moveCursorEndLine(Lisp args, LispError* e, LispContext ctx)
|
|||||||
* @return lisp_null()
|
* @return lisp_null()
|
||||||
* @see editorDelChar()
|
* @see editorDelChar()
|
||||||
*/
|
*/
|
||||||
Lisp deletePreviousChar(Lisp args, LispError* e, LispContext ctx)
|
Lisp deletePreviousChar(Lisp args, LispError *e, LispContext ctx) {
|
||||||
{
|
bufferDelChar();
|
||||||
bufferDelBytes();
|
return lisp_null();
|
||||||
return lisp_null();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Lisp function to move cursor up by one screen
|
* @brief Lisp function to move cursor up by one screen
|
||||||
* @details Scrolls up one full screen height, moving cursor to top of visible
|
* @details Scrolls up one full screen height, moving cursor to top of visible area.
|
||||||
* area.
|
|
||||||
* @param args Lisp arguments (unused)
|
* @param args Lisp arguments (unused)
|
||||||
* @param e Error pointer for Lisp error handling
|
* @param e Error pointer for Lisp error handling
|
||||||
* @param ctx Lisp context
|
* @param ctx Lisp context
|
||||||
@@ -322,22 +303,19 @@ Lisp deletePreviousChar(Lisp args, LispError* e, LispContext ctx)
|
|||||||
* @note Updates global editor state E
|
* @note Updates global editor state E
|
||||||
* @see editorMoveCursor()
|
* @see editorMoveCursor()
|
||||||
*/
|
*/
|
||||||
Lisp editorMoveCursorPageUp(Lisp args, LispError* e, LispContext ctx)
|
Lisp editorMoveCursorPageUp(Lisp args, LispError *e, LispContext ctx) {
|
||||||
{
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
EditorPane* active = splitScreenGetActivePane();
|
active->cursor_y = active->row_offset;
|
||||||
active->cursor_y = active->y_offset;
|
int times = E.screenrows;
|
||||||
int times = E.screenrows;
|
while (--times) {
|
||||||
while (--times)
|
editorMoveCursor(ARROW_UP);
|
||||||
{
|
}
|
||||||
editorMoveCursor(ARROW_UP);
|
return lisp_null();
|
||||||
}
|
|
||||||
return lisp_null();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Lisp function to move cursor down by one screen
|
* @brief Lisp function to move cursor down by one screen
|
||||||
* @details Scrolls down one full screen height, moving cursor to bottom of
|
* @details Scrolls down one full screen height, moving cursor to bottom of visible area.
|
||||||
* visible area.
|
|
||||||
* @param args Lisp arguments (unused)
|
* @param args Lisp arguments (unused)
|
||||||
* @param e Error pointer for Lisp error handling
|
* @param e Error pointer for Lisp error handling
|
||||||
* @param ctx Lisp context
|
* @param ctx Lisp context
|
||||||
@@ -345,22 +323,19 @@ Lisp editorMoveCursorPageUp(Lisp args, LispError* e, LispContext ctx)
|
|||||||
* @note Updates global editor state E
|
* @note Updates global editor state E
|
||||||
* @see editorMoveCursor()
|
* @see editorMoveCursor()
|
||||||
*/
|
*/
|
||||||
Lisp editorMoveCursorPageDown(Lisp args, LispError* e, LispContext ctx)
|
Lisp editorMoveCursorPageDown(Lisp args, LispError *e, LispContext ctx) {
|
||||||
{
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
EditorPane* active = splitScreenGetActivePane();
|
struct buffer_t *buffer = bufferFindById(active->buffer_id);
|
||||||
struct buffer_t* buffer = bufferFindById(active->buffer_id);
|
active->cursor_y = active->row_offset + E.screenrows - 1;
|
||||||
active->cursor_y = active->y_offset + E.screenrows - 1;
|
if (active->cursor_y > buffer->numrows) {
|
||||||
if (active->cursor_y > buffer->numrows)
|
active->cursor_y = buffer->numrows;
|
||||||
{
|
}
|
||||||
active->cursor_y = buffer->numrows;
|
int times = E.screenrows;
|
||||||
}
|
while (--times) {
|
||||||
int times = E.screenrows;
|
editorMoveCursor(ARROW_DOWN);
|
||||||
while (--times)
|
}
|
||||||
{
|
|
||||||
editorMoveCursor(ARROW_DOWN);
|
|
||||||
}
|
|
||||||
|
|
||||||
return lisp_null();
|
return lisp_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -374,35 +349,32 @@ Lisp editorMoveCursorPageDown(Lisp args, LispError* e, LispContext ctx)
|
|||||||
* @see editorOpen()
|
* @see editorOpen()
|
||||||
* @see editorPrompt()
|
* @see editorPrompt()
|
||||||
*/
|
*/
|
||||||
Lisp editorOpenFile(Lisp args, LispError* e, LispContext ctx)
|
Lisp editorOpenFile(Lisp args, LispError *e, LispContext ctx) {
|
||||||
{
|
char *filename = editorPrompt("Open : %s", getenv("PWD"), 1);
|
||||||
appDebug("[EDITOR OPEN FILE]\n");
|
if (filename){
|
||||||
char* filename = editorPrompt("Open : %s", getenv("PWD"), 1);
|
// editorOpen(filename);
|
||||||
if (filename)
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
{
|
active->buffer_id = bufferCreate(filename);
|
||||||
EditorPane* active = splitScreenGetActivePane();
|
}
|
||||||
active->buffer_id = bufferCreate(filename, READ_AND_WRITE);
|
free(filename);
|
||||||
}
|
|
||||||
free(filename);
|
|
||||||
|
|
||||||
return lisp_null();
|
|
||||||
|
return lisp_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Lisp function to insert a character
|
* @brief Lisp function to insert a character
|
||||||
* @details Extracts a character from Lisp string argument and inserts it at
|
* @details Extracts a character from Lisp string argument and inserts it at cursor.
|
||||||
* cursor.
|
|
||||||
* @param args Lisp list with one string argument
|
* @param args Lisp list with one string argument
|
||||||
* @param e Error pointer for Lisp error handling
|
* @param e Error pointer for Lisp error handling
|
||||||
* @param ctx Lisp context
|
* @param ctx Lisp context
|
||||||
* @return lisp_null()
|
* @return lisp_null()
|
||||||
* @note Uses first character of the string argument
|
* @note Uses first character of the string argument
|
||||||
*/
|
*/
|
||||||
Lisp editorPrintC(Lisp args, LispError* e, LispContext ctx)
|
Lisp editorPrintC(Lisp args, LispError *e, LispContext ctx) {
|
||||||
{
|
char c = lisp_string(lisp_car(args))[0];
|
||||||
const char* src = lisp_string(lisp_car(args));
|
bufferInsertChar(c);
|
||||||
bufferInsertBytes(src, (int) strlen(src));
|
return lisp_null();
|
||||||
return lisp_null();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -416,59 +388,63 @@ Lisp editorPrintC(Lisp args, LispError* e, LispContext ctx)
|
|||||||
* @note Package files must be valid Lisp code
|
* @note Package files must be valid Lisp code
|
||||||
* @note Package directory defaults to ~/.beluga/packages/
|
* @note Package directory defaults to ~/.beluga/packages/
|
||||||
*/
|
*/
|
||||||
Lisp addPackage(Lisp args, LispError* e, LispContext ctx)
|
Lisp addPackage(Lisp args, LispError *e, LispContext ctx) {
|
||||||
{
|
const char *package_name = lisp_string(lisp_car(args));
|
||||||
const char* package_name = lisp_string(lisp_car(args));
|
fprintf(stderr, "%s\n", package_name);
|
||||||
appDebug("%s\n", package_name);
|
char *package_dir = (char *)calloc(256, sizeof(char));
|
||||||
const char *home = getenv("HOME");
|
FILE *fd_package = NULL;
|
||||||
if (!home) {
|
strcat(package_dir, getenv("HOME"));
|
||||||
return lisp_null();
|
strcat(package_dir, "/.beluga/packages/");
|
||||||
}
|
strcat(package_dir, package_name);
|
||||||
|
strcat(package_dir, "/init.lisp");
|
||||||
|
fprintf(stderr, "%s\n", package_dir);
|
||||||
|
fd_package = fopen(package_dir, "r");
|
||||||
|
lisp_eval(lisp_read_file(fd_package, &E.ctx_error, E.ctx), &E.ctx_error,
|
||||||
|
E.ctx);
|
||||||
|
fclose(fd_package);
|
||||||
|
free(package_dir);
|
||||||
|
|
||||||
char *package_dir;
|
return lisp_null();
|
||||||
if (asprintf(&package_dir, "%s/.beluga/packages/%s/init.lisp", home, package_name) == -1) {
|
|
||||||
return lisp_null();
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE* fd_package = fopen(package_dir, "r");
|
|
||||||
if (!fd_package) {
|
|
||||||
free(package_dir);
|
|
||||||
return lisp_null();
|
|
||||||
}
|
|
||||||
|
|
||||||
lisp_eval(lisp_read_file(fd_package, &E.ctx_error, E.ctx), &E.ctx_error, E.ctx);
|
|
||||||
fclose(fd_package);
|
|
||||||
free(package_dir);
|
|
||||||
|
|
||||||
return lisp_null();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Lisp editorSwitchNextBuffer(Lisp args, LispError* e, LispContext ctx)
|
/**
|
||||||
{
|
* @brief Lisp function to delete the current row
|
||||||
appDebug("switch buffer\n");
|
* @details Removes the line at the current cursor position.
|
||||||
if (E.number_of_buffer > 0)
|
* @param args Lisp arguments (unused)
|
||||||
{
|
* @param e Error pointer for Lisp error handling
|
||||||
EditorPane* active = splitScreenGetActivePane();
|
* @param ctx Lisp context
|
||||||
int next_idx = (active->buffer_id + 1) % E.number_of_buffer;
|
* @return lisp_null()
|
||||||
active->buffer_id = next_idx;
|
* @note Updates global editor state E
|
||||||
}
|
* @see editorDelRow()
|
||||||
|
*/
|
||||||
return lisp_null();
|
Lisp editorDelRow_L(Lisp args, LispError *e, LispContext ctx) {
|
||||||
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
|
struct buffer_t *buffer = bufferFindById(active->buffer_id);
|
||||||
|
bufferDelRow(buffer, active->cursor_y);
|
||||||
|
return lisp_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
Lisp editorSwitchNextPane(Lisp args, LispError* e, LispContext ctx)
|
Lisp editorSwitchNextBuffer(Lisp args, LispError *e, LispContext ctx) {
|
||||||
{
|
fprintf(stderr, "switch buffer\n");
|
||||||
splitScreenSwitchPane();
|
if (E.number_of_buffer > 0) {
|
||||||
return lisp_null();
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
|
int next_idx = (active->buffer_id + 1) % E.number_of_buffer;
|
||||||
|
active->buffer_id = next_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lisp_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
Lisp editorUnifiedPanes(Lisp args, LispError* e, LispContext ctx)
|
Lisp editorSwitchNextPane(Lisp args, LispError *e, LispContext ctx) {
|
||||||
{
|
splitScreenSwitchPane();
|
||||||
if (E.layout.num_panes - 1)
|
return lisp_null();
|
||||||
{
|
}
|
||||||
splitScreenUnify();
|
|
||||||
}
|
Lisp editorUnifiedPanes(Lisp args, LispError *e, LispContext ctx) {
|
||||||
return lisp_null();
|
if (E.layout.num_panes - 1) {
|
||||||
|
splitScreenUnify();
|
||||||
|
}
|
||||||
|
return lisp_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -480,56 +456,34 @@ Lisp editorUnifiedPanes(Lisp args, LispError* e, LispContext ctx)
|
|||||||
* @return lisp_null()
|
* @return lisp_null()
|
||||||
* @see editorFind()
|
* @see editorFind()
|
||||||
*/
|
*/
|
||||||
Lisp bufferFind_L(Lisp args, LispError* e, LispContext ctx)
|
Lisp bufferFind_L(Lisp args, LispError *e, LispContext ctx) {
|
||||||
{
|
fprintf(stderr, "LispFind\n");
|
||||||
appDebug("LispFind\n");
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
EditorPane* active = splitScreenGetActivePane();
|
struct buffer_t *buffer = bufferFindById(active->buffer_id);
|
||||||
struct buffer_t* buffer = bufferFindById(active->buffer_id);
|
bufferFind(buffer);
|
||||||
bufferFind(buffer);
|
return lisp_null();
|
||||||
return lisp_null();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Lisp function to search for text
|
|
||||||
* @details Wrapper around editorFind() for use in Lisp keybindings.
|
|
||||||
* @param args Lisp arguments (unused)
|
|
||||||
* @param e Error pointer for Lisp error handling
|
|
||||||
* @param ctx Lisp context
|
|
||||||
* @return lisp_null()
|
|
||||||
* @see editorFind()
|
|
||||||
*/
|
|
||||||
Lisp bufferFindReverse_L(Lisp args, LispError* e, LispContext ctx)
|
|
||||||
{
|
|
||||||
appDebug("LispFind\n");
|
|
||||||
EditorPane* active = splitScreenGetActivePane();
|
|
||||||
struct buffer_t* buffer = bufferFindById(active->buffer_id);
|
|
||||||
bufferFindReverse(buffer);
|
|
||||||
return lisp_null();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Lisp function to read character at cursor
|
* @brief Lisp function to read character at cursor
|
||||||
* @details Returns the character at the current cursor position as a Lisp
|
* @details Returns the character at the current cursor position as a Lisp character.
|
||||||
* character. Returns 'a' if at end of line.
|
* Returns 'a' if at end of line.
|
||||||
* @param args Lisp arguments (unused)
|
* @param args Lisp arguments (unused)
|
||||||
* @param e Error pointer for Lisp error handling
|
* @param e Error pointer for Lisp error handling
|
||||||
* @param ctx Lisp context
|
* @param ctx Lisp context
|
||||||
* @return Lisp character object representing the character at cursor
|
* @return Lisp character object representing the character at cursor
|
||||||
*/
|
*/
|
||||||
Lisp editorReadChar_L(Lisp args, LispError* e, LispContext ctx)
|
Lisp editorReadChar_L(Lisp args, LispError *e, LispContext ctx) {
|
||||||
{
|
Lisp returned_char;
|
||||||
Lisp returned_char;
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
EditorPane* active = splitScreenGetActivePane();
|
struct buffer_t *buffer = bufferFindById(active->buffer_id);
|
||||||
struct buffer_t* buffer = bufferFindById(active->buffer_id);
|
if (buffer->row[active->cursor_y].render[active->cursor_x] == 0) {
|
||||||
if (buffer->row[buffer->y].chars[buffer->x] == 0)
|
returned_char = lisp_make_char('a');
|
||||||
{
|
} else {
|
||||||
returned_char = lisp_make_char('a');
|
|
||||||
}
|
returned_char = lisp_make_char(buffer->row[active->cursor_y].render[active->cursor_x]);
|
||||||
else
|
}
|
||||||
{
|
return returned_char;
|
||||||
returned_char = lisp_make_char(buffer->row[buffer->y].chars[buffer->x]);
|
|
||||||
}
|
|
||||||
return returned_char;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -543,18 +497,17 @@ Lisp editorReadChar_L(Lisp args, LispError* e, LispContext ctx)
|
|||||||
* @note Updates global editor state E (prefix_state)
|
* @note Updates global editor state E (prefix_state)
|
||||||
* @see find_prefix()
|
* @see find_prefix()
|
||||||
*/
|
*/
|
||||||
Lisp editorSetPrefix(Lisp args, LispError* e, LispContext ctx)
|
Lisp editorSetPrefix(Lisp args, LispError *e, LispContext ctx) {
|
||||||
{
|
/*
|
||||||
/*
|
* Set the prefix state of editor to the prefix in argument
|
||||||
* Set the prefix state of editor to the prefix in argument
|
*/
|
||||||
*/
|
const char *prefix_name = lisp_string(lisp_car(args));
|
||||||
const char* prefix_name = lisp_string(lisp_car(args));
|
struct prefix_t prefix = find_prefix(prefix_name);
|
||||||
struct prefix_t prefix = find_prefix(prefix_name);
|
E.prefix_state = prefix.prefix_id;
|
||||||
E.prefix_state = prefix.prefix_id;
|
editorSetStatusMessage("prefix %s", prefix.prefix_name);
|
||||||
editorSetStatusMessage("prefix %s", prefix.prefix_name);
|
fprintf(stderr, "%s set\n", prefix_name);
|
||||||
appDebug("%s set\n", prefix_name);
|
|
||||||
|
|
||||||
return lisp_null();
|
return lisp_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -567,98 +520,11 @@ Lisp editorSetPrefix(Lisp args, LispError* e, LispContext ctx)
|
|||||||
* @return lisp_null()
|
* @return lisp_null()
|
||||||
* @note Updates global editor state E (prefix array)
|
* @note Updates global editor state E (prefix array)
|
||||||
*/
|
*/
|
||||||
Lisp editorPrefix(Lisp args, LispError* e, LispContext ctx)
|
Lisp editorPrefix(Lisp args, LispError *e, LispContext ctx) {
|
||||||
{
|
E.prefix = (struct prefix_t *)realloc(E.prefix, (++(E.number_of_prefix) + 1) *
|
||||||
E.prefix = (struct prefix_t*)realloc(E.prefix, (++(E.number_of_prefix) + 1) *
|
sizeof(struct prefix_t));
|
||||||
sizeof(struct prefix_t));
|
E.prefix[E.number_of_prefix].prefix_id = E.number_of_prefix;
|
||||||
E.prefix[E.number_of_prefix].prefix_id = E.number_of_prefix;
|
strncpy(E.prefix[E.number_of_prefix].prefix_name, lisp_string(lisp_car(args)),
|
||||||
strncpy(E.prefix[E.number_of_prefix].prefix_name, lisp_string(lisp_car(args)),
|
64);
|
||||||
64);
|
return lisp_null();
|
||||||
return lisp_null();
|
|
||||||
}
|
|
||||||
|
|
||||||
Lisp editorPaste(Lisp args, LispError* e, LispContext ctx)
|
|
||||||
{
|
|
||||||
const char* char_to_paste = editorGetClipboard();
|
|
||||||
appDebug("editor-paste, %s\n", char_to_paste);
|
|
||||||
bufferInsertBytes(char_to_paste, (int)strlen(char_to_paste));
|
|
||||||
return lisp_null();
|
|
||||||
}
|
|
||||||
|
|
||||||
Lisp editorCutEndLine(Lisp args, LispError* e, LispContext ctx)
|
|
||||||
{
|
|
||||||
EditorPane* active = splitScreenGetActivePane();
|
|
||||||
struct buffer_t* buffer = bufferFindById(active->buffer_id);
|
|
||||||
int bytes_to_delete = editorRowCharCount(&buffer->row[buffer->y],
|
|
||||||
buffer->row[buffer->y].chars[buffer->row[buffer->y].size]) -
|
|
||||||
editorRowCharCount(&buffer->row[buffer->y], buffer->x);
|
|
||||||
editorSetClipboard(&buffer->row[buffer->y].chars[buffer->x], bytes_to_delete);
|
|
||||||
buffer->x = buffer->row[buffer->y].size;
|
|
||||||
while (bytes_to_delete--)
|
|
||||||
{
|
|
||||||
bufferDelBytes();
|
|
||||||
}
|
|
||||||
return lisp_null();
|
|
||||||
}
|
|
||||||
|
|
||||||
Lisp editorMoveBegBuffer(Lisp args, LispError* e, LispContext ctx)
|
|
||||||
{
|
|
||||||
EditorPane* active = splitScreenGetActivePane();
|
|
||||||
struct buffer_t* buffer = bufferFindById(active->buffer_id);
|
|
||||||
buffer->x = 0;
|
|
||||||
buffer->y = 0;
|
|
||||||
return lisp_null();
|
|
||||||
}
|
|
||||||
|
|
||||||
Lisp editorMoveEndBuffer(Lisp args, LispError* e, LispContext ctx)
|
|
||||||
{
|
|
||||||
EditorPane* active = splitScreenGetActivePane();
|
|
||||||
struct buffer_t* buffer = bufferFindById(active->buffer_id);
|
|
||||||
buffer->x = buffer->row[buffer->numrows - 1].size;
|
|
||||||
buffer->y = buffer->numrows - 1;
|
|
||||||
return lisp_null();
|
|
||||||
}
|
|
||||||
|
|
||||||
Lisp editorAutoComplete(Lisp args, LispError* e, LispContext ctx)
|
|
||||||
{
|
|
||||||
if (!E.constantes.LSP) {
|
|
||||||
return lisp_null();
|
|
||||||
}
|
|
||||||
// createContextBuffer(E.cursor_x - 2, E.cursor_y + 1, "hello");
|
|
||||||
appDebug("editor-auto-complete\n");
|
|
||||||
EditorPane* active = splitScreenGetActivePane();
|
|
||||||
struct buffer_t* buffer = bufferFindById(active->buffer_id);
|
|
||||||
E.lsp_completion.visible = 1;
|
|
||||||
lspRequestCompletion(
|
|
||||||
E.lsp_client,
|
|
||||||
buffer,
|
|
||||||
buffer->y, // file line
|
|
||||||
buffer->x, // file col
|
|
||||||
active->cursor_x + active->origin_x + GUTTER_WIDTH, // screen x
|
|
||||||
active->cursor_y + active->origin_y // screen y
|
|
||||||
);
|
|
||||||
return lisp_null();
|
|
||||||
}
|
|
||||||
|
|
||||||
Lisp lspDefinition(Lisp args, LispError* e, LispContext ctx)
|
|
||||||
{
|
|
||||||
if (!E.constantes.LSP) {
|
|
||||||
return lisp_null();
|
|
||||||
}
|
|
||||||
(void)args;
|
|
||||||
(void)e;
|
|
||||||
(void)ctx;
|
|
||||||
if (!E.lsp_client || E.lsp_client->state != LSP_READY)
|
|
||||||
return lisp_null();
|
|
||||||
|
|
||||||
EditorPane* active = splitScreenGetActivePane();
|
|
||||||
struct buffer_t* buf = bufferFindById(active->buffer_id);
|
|
||||||
if (!buf) return lisp_null();
|
|
||||||
|
|
||||||
lspRequestDefinition(
|
|
||||||
E.lsp_client, buf,
|
|
||||||
active->cursor_y + active->y_offset,
|
|
||||||
active->cursor_x + active->x_offset
|
|
||||||
);
|
|
||||||
return lisp_null();
|
|
||||||
}
|
}
|
||||||
|
|||||||
-3207
File diff suppressed because it is too large
Load Diff
@@ -1,563 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file completion.c
|
|
||||||
* @brief LSP (Language Server Protocol) client implementation for Beluga
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "../include/completion.h"
|
|
||||||
|
|
||||||
#include <signal.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include "../include/append_buffer.h"
|
|
||||||
#include "../include/cJSON.h"
|
|
||||||
#include "../include/data.h"
|
|
||||||
#include "../include/lsp_ui.h"
|
|
||||||
#include "../include/split_screen.h"
|
|
||||||
#include "../include/terminal.h"
|
|
||||||
#include "../include/buffer.h"
|
|
||||||
|
|
||||||
// ─── Static Functions ──────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
static void lsp_send(LspClient *lsp, const char *json)
|
|
||||||
{
|
|
||||||
int body_len = strlen(json);
|
|
||||||
char header[1024];
|
|
||||||
int header_len = snprintf(header, sizeof(header),
|
|
||||||
"Content-Length: %d\r\n\r\n", body_len);
|
|
||||||
|
|
||||||
// Write header + body atomically
|
|
||||||
write(lsp->read_fd, header, header_len);
|
|
||||||
write(lsp->read_fd, json, body_len);
|
|
||||||
|
|
||||||
// Log to stderr for debugging
|
|
||||||
appDebug("[LSP →] Content-Length: %d | %s\n", body_len, json);
|
|
||||||
fflush(stderr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *lsp_recv(LspClient *lsp)
|
|
||||||
{
|
|
||||||
char header[1024];
|
|
||||||
int content_length = 0;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
int i = 0;
|
|
||||||
char c;
|
|
||||||
int n;
|
|
||||||
|
|
||||||
// Read header line by line
|
|
||||||
while ((n = read(lsp->write_fd, &c, 1)) == 1 && c != '\n') {
|
|
||||||
if (i < (int)(sizeof(header) - 1))
|
|
||||||
header[i++] = c;
|
|
||||||
}
|
|
||||||
header[i] = '\0';
|
|
||||||
|
|
||||||
// Skip empty lines
|
|
||||||
if (i == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Parse Content-Length header
|
|
||||||
if (strncmp(header, "Content-Length: ", 16) == 0) {
|
|
||||||
content_length = atoi(header + 16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (content_length <= 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
// Read body
|
|
||||||
char *body = malloc(content_length + 1);
|
|
||||||
if (!body)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
int total_read = 0;
|
|
||||||
while (total_read < content_length) {
|
|
||||||
int n = read(lsp->write_fd, body + total_read, content_length - total_read);
|
|
||||||
if (n <= 0) {
|
|
||||||
free(body);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
total_read += n;
|
|
||||||
}
|
|
||||||
body[content_length] = '\0';
|
|
||||||
|
|
||||||
appDebug("[LSP ←] %s\n", body);
|
|
||||||
return body;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Public LSP Functions ──────────────────────────────────────────────────
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Starts the LSP server (clangd)
|
|
||||||
* @param client LSP client structure
|
|
||||||
* @param root_dir Project root directory
|
|
||||||
* @return 0 on success, -1 on failure
|
|
||||||
*/
|
|
||||||
int lspStart(LspClient *client, const char *root_dir)
|
|
||||||
{
|
|
||||||
if (!client || !root_dir)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
// Create pipes for communication with LSP server
|
|
||||||
int stdin_pipe[2];
|
|
||||||
int stdout_pipe[2];
|
|
||||||
|
|
||||||
if (pipe(stdin_pipe) == -1 || pipe(stdout_pipe) == -1) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create wake pipe for interrupting select()
|
|
||||||
if (pipe(client->wake_pipe) == -1) {
|
|
||||||
close(stdin_pipe[0]);
|
|
||||||
close(stdin_pipe[1]);
|
|
||||||
close(stdout_pipe[0]);
|
|
||||||
close(stdout_pipe[1]);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fork LSP server process
|
|
||||||
pid_t pid = fork();
|
|
||||||
if (pid == 0) {
|
|
||||||
// Child process - exec clangd
|
|
||||||
close(stdin_pipe[1]);
|
|
||||||
close(stdout_pipe[0]);
|
|
||||||
close(client->wake_pipe[0]);
|
|
||||||
close(client->wake_pipe[1]);
|
|
||||||
|
|
||||||
dup2(stdin_pipe[0], STDIN_FILENO);
|
|
||||||
dup2(stdout_pipe[1], STDOUT_FILENO);
|
|
||||||
|
|
||||||
close(stdin_pipe[0]);
|
|
||||||
close(stdout_pipe[1]);
|
|
||||||
|
|
||||||
// Find clangd in PATH
|
|
||||||
execlp("clangd", "clangd", NULL);
|
|
||||||
|
|
||||||
// If execlp fails
|
|
||||||
exit(1);
|
|
||||||
} else if (pid > 0) {
|
|
||||||
// Parent process
|
|
||||||
close(stdin_pipe[0]);
|
|
||||||
close(stdout_pipe[1]);
|
|
||||||
|
|
||||||
client->read_fd = stdin_pipe[1];
|
|
||||||
client->write_fd = stdout_pipe[0];
|
|
||||||
client->pid = pid;
|
|
||||||
client->next_id = 1;
|
|
||||||
client->state = LSP_INITIALIZING;
|
|
||||||
|
|
||||||
// Send initialize request
|
|
||||||
char root_uri[PATH_MAX + 8];
|
|
||||||
snprintf(root_uri, sizeof(root_uri), "file://%s", root_dir);
|
|
||||||
|
|
||||||
cJSON *req = cJSON_CreateObject();
|
|
||||||
cJSON_AddStringToObject(req, "jsonrpc", "2.0");
|
|
||||||
cJSON_AddNumberToObject(req, "id", client->next_id++);
|
|
||||||
cJSON_AddStringToObject(req, "method", "initialize");
|
|
||||||
|
|
||||||
cJSON *params = cJSON_CreateObject();
|
|
||||||
cJSON_AddNumberToObject(params, "processId", getpid());
|
|
||||||
cJSON_AddStringToObject(params, "rootUri", root_uri);
|
|
||||||
|
|
||||||
// Capabilities
|
|
||||||
cJSON *caps = cJSON_CreateObject();
|
|
||||||
cJSON *td_caps = cJSON_CreateObject();
|
|
||||||
cJSON *comp_caps = cJSON_CreateObject();
|
|
||||||
cJSON *comp_item = cJSON_CreateObject();
|
|
||||||
|
|
||||||
cJSON_AddBoolToObject(comp_item, "snippetSupport", 0);
|
|
||||||
cJSON_AddBoolToObject(comp_item, "commitCharactersSupport", 0);
|
|
||||||
cJSON_AddItemToObject(comp_caps, "completionItem", comp_item);
|
|
||||||
cJSON_AddItemToObject(td_caps, "completion", comp_caps);
|
|
||||||
cJSON_AddItemToObject(td_caps, "hover", cJSON_CreateObject());
|
|
||||||
cJSON_AddItemToObject(td_caps, "definition", cJSON_CreateObject());
|
|
||||||
cJSON_AddItemToObject(td_caps, "publishDiagnostics", cJSON_CreateObject());
|
|
||||||
cJSON_AddItemToObject(caps, "textDocument", td_caps);
|
|
||||||
cJSON_AddItemToObject(params, "capabilities", caps);
|
|
||||||
cJSON_AddItemToObject(req, "params", params);
|
|
||||||
|
|
||||||
char *msg = cJSON_PrintUnformatted(req);
|
|
||||||
lsp_send(client, msg);
|
|
||||||
|
|
||||||
free(msg);
|
|
||||||
cJSON_Delete(req);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Sends textDocument/didOpen notification
|
|
||||||
* @param client LSP client
|
|
||||||
* @param buf Buffer to open
|
|
||||||
*/
|
|
||||||
void lspDidOpen(LspClient* lsp, struct buffer_t* buf)
|
|
||||||
{
|
|
||||||
if (!lsp || !buf)
|
|
||||||
return;
|
|
||||||
|
|
||||||
char uri[PATH_MAX + 8];
|
|
||||||
snprintf(uri, sizeof(uri), "file://%s", buf->fullname);
|
|
||||||
|
|
||||||
cJSON* root = cJSON_CreateObject();
|
|
||||||
cJSON_AddStringToObject(root, "jsonrpc", "2.0");
|
|
||||||
cJSON_AddStringToObject(root, "method", "textDocument/didOpen");
|
|
||||||
|
|
||||||
cJSON* params = cJSON_CreateObject();
|
|
||||||
cJSON* textDoc = cJSON_CreateObject();
|
|
||||||
cJSON_AddStringToObject(textDoc, "uri", uri);
|
|
||||||
cJSON_AddStringToObject(textDoc, "languageId", "c");
|
|
||||||
cJSON_AddNumberToObject(textDoc, "version", 1);
|
|
||||||
cJSON_AddStringToObject(textDoc, "text", "");
|
|
||||||
cJSON_AddItemToObject(params, "textDocument", textDoc);
|
|
||||||
cJSON_AddItemToObject(root, "params", params);
|
|
||||||
|
|
||||||
char* msg = cJSON_PrintUnformatted(root);
|
|
||||||
lsp_send(lsp, msg);
|
|
||||||
|
|
||||||
free(msg);
|
|
||||||
cJSON_Delete(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Sends textDocument/didChange notification
|
|
||||||
* @param client LSP client
|
|
||||||
* @param buf Buffer that changed
|
|
||||||
*/
|
|
||||||
void lspDidChange(LspClient* lsp, struct buffer_t* buf)
|
|
||||||
{
|
|
||||||
if (!lsp || !buf)
|
|
||||||
return;
|
|
||||||
|
|
||||||
char uri[PATH_MAX + 8];
|
|
||||||
snprintf(uri, sizeof(uri), "file://%s", buf->fullname);
|
|
||||||
|
|
||||||
char* raw = bufferToText(buf);
|
|
||||||
|
|
||||||
cJSON* root = cJSON_CreateObject();
|
|
||||||
cJSON_AddStringToObject(root, "jsonrpc", "2.0");
|
|
||||||
cJSON_AddStringToObject(root, "method", "textDocument/didChange");
|
|
||||||
|
|
||||||
cJSON* params = cJSON_CreateObject();
|
|
||||||
|
|
||||||
cJSON* textDoc = cJSON_CreateObject();
|
|
||||||
cJSON_AddStringToObject(textDoc, "uri", uri);
|
|
||||||
cJSON_AddNumberToObject(textDoc, "version", buf->dirty);
|
|
||||||
cJSON_AddItemToObject(params, "textDocument", textDoc);
|
|
||||||
|
|
||||||
cJSON* changes = cJSON_CreateArray();
|
|
||||||
cJSON* change = cJSON_CreateObject();
|
|
||||||
cJSON_AddStringToObject(change, "text", raw);
|
|
||||||
cJSON_AddItemToArray(changes, change);
|
|
||||||
cJSON_AddItemToObject(params, "contentChanges", changes);
|
|
||||||
|
|
||||||
cJSON_AddItemToObject(root, "params", params);
|
|
||||||
|
|
||||||
char* msg = cJSON_PrintUnformatted(root);
|
|
||||||
lsp_send(lsp, msg);
|
|
||||||
|
|
||||||
free(msg);
|
|
||||||
cJSON_Delete(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Sends textDocument/didClose notification
|
|
||||||
* @param client LSP client
|
|
||||||
* @param buf Buffer to close
|
|
||||||
*/
|
|
||||||
void lspDidClose(LspClient* lsp, struct buffer_t* buf)
|
|
||||||
{
|
|
||||||
if (!lsp || !buf)
|
|
||||||
return;
|
|
||||||
|
|
||||||
char uri[PATH_MAX + 8];
|
|
||||||
snprintf(uri, sizeof(uri), "file://%s", buf->fullname);
|
|
||||||
|
|
||||||
cJSON* root = cJSON_CreateObject();
|
|
||||||
cJSON_AddStringToObject(root, "jsonrpc", "2.0");
|
|
||||||
cJSON_AddStringToObject(root, "method", "textDocument/didClose");
|
|
||||||
|
|
||||||
cJSON* params = cJSON_CreateObject();
|
|
||||||
cJSON* textDoc = cJSON_CreateObject();
|
|
||||||
cJSON_AddStringToObject(textDoc, "uri", uri);
|
|
||||||
cJSON_AddItemToObject(params, "textDocument", textDoc);
|
|
||||||
cJSON_AddItemToObject(root, "params", params);
|
|
||||||
|
|
||||||
char* msg = cJSON_PrintUnformatted(root);
|
|
||||||
lsp_send(lsp, msg);
|
|
||||||
|
|
||||||
free(msg);
|
|
||||||
cJSON_Delete(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Requests completion at cursor position
|
|
||||||
* @param client LSP client
|
|
||||||
* @param buf Buffer
|
|
||||||
* @param line Line number (0-based)
|
|
||||||
* @param col Column number (0-based)
|
|
||||||
*/
|
|
||||||
void lspRequestCompletion(LspClient *lsp, struct buffer_t *buf,
|
|
||||||
int line, int col,
|
|
||||||
int screen_x, int screen_y)
|
|
||||||
{
|
|
||||||
if (lsp->state != LSP_READY) return;
|
|
||||||
lsp->completion_cursor_x = screen_x;
|
|
||||||
lsp->completion_cursor_y = screen_y;
|
|
||||||
|
|
||||||
appDebug("LSP REQUEST COMP");
|
|
||||||
|
|
||||||
char* msg;
|
|
||||||
char uri[PATH_MAX + 8];
|
|
||||||
snprintf(uri, sizeof(uri), "file://%s", buf->fullname);
|
|
||||||
appDebug("FULLNAME : %s\n", buf->fullname);
|
|
||||||
|
|
||||||
cJSON* req = cJSON_CreateObject();
|
|
||||||
cJSON* params = cJSON_CreateObject();
|
|
||||||
cJSON* td = cJSON_CreateObject();
|
|
||||||
cJSON* position = cJSON_CreateObject();
|
|
||||||
|
|
||||||
cJSON_AddStringToObject(req, "jsonrpc", "2.0");
|
|
||||||
cJSON_AddNumberToObject(req, "id", lsp->next_id++);
|
|
||||||
cJSON_AddStringToObject(req, "method", "textDocument/completion");
|
|
||||||
|
|
||||||
cJSON_AddStringToObject(td, "uri", uri);
|
|
||||||
|
|
||||||
cJSON_AddNumberToObject(position, "line", line);
|
|
||||||
cJSON_AddNumberToObject(position, "character", col);
|
|
||||||
cJSON_AddItemToObject(params, "position", position);
|
|
||||||
|
|
||||||
cJSON_AddItemToObject(params, "textDocument", td);
|
|
||||||
cJSON_AddItemToObject(req, "params", params);
|
|
||||||
|
|
||||||
msg = cJSON_PrintUnformatted(req);
|
|
||||||
lsp_send(lsp, msg);
|
|
||||||
E.lsp_client->completion_requested = 1;
|
|
||||||
cJSON_Delete(req);
|
|
||||||
free(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Requests goto definition
|
|
||||||
* @param client LSP client
|
|
||||||
* @param buf Buffer
|
|
||||||
* @param line Line number (0-based)
|
|
||||||
* @param col Column number (0-based)
|
|
||||||
*/
|
|
||||||
void lspRequestDefinition(LspClient *lsp, struct buffer_t *buf,
|
|
||||||
int line, int col)
|
|
||||||
{
|
|
||||||
if (!lsp || lsp->state != LSP_READY || !buf)
|
|
||||||
return;
|
|
||||||
|
|
||||||
char uri[PATH_MAX + 8];
|
|
||||||
snprintf(uri, sizeof(uri), "file://%s", buf->fullname);
|
|
||||||
|
|
||||||
cJSON* req = cJSON_CreateObject();
|
|
||||||
cJSON_AddStringToObject(req, "jsonrpc", "2.0");
|
|
||||||
cJSON_AddNumberToObject(req, "id", lsp->next_id++);
|
|
||||||
cJSON_AddStringToObject(req, "method", "textDocument/definition");
|
|
||||||
|
|
||||||
cJSON* params = cJSON_CreateObject();
|
|
||||||
|
|
||||||
cJSON* textDoc = cJSON_CreateObject();
|
|
||||||
cJSON_AddStringToObject(textDoc, "uri", uri);
|
|
||||||
cJSON_AddItemToObject(params, "textDocument", textDoc);
|
|
||||||
|
|
||||||
cJSON* position = cJSON_CreateObject();
|
|
||||||
cJSON_AddNumberToObject(position, "line", line);
|
|
||||||
cJSON_AddNumberToObject(position, "character", col);
|
|
||||||
cJSON_AddItemToObject(params, "position", position);
|
|
||||||
|
|
||||||
cJSON_AddItemToObject(req, "params", params);
|
|
||||||
|
|
||||||
char* msg = cJSON_PrintUnformatted(req);
|
|
||||||
lsp_send(lsp, msg);
|
|
||||||
|
|
||||||
free(msg);
|
|
||||||
cJSON_Delete(req);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Reads and processes LSP messages (non-blocking)
|
|
||||||
* @param client LSP client
|
|
||||||
* @return 1 if message processed, 0 if no message, -1 on error
|
|
||||||
*/
|
|
||||||
int lspReadMessages(LspClient *client)
|
|
||||||
{
|
|
||||||
if (!client || client->state != LSP_READY)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
char *msg = lsp_recv(client);
|
|
||||||
if (!msg)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// Parse response
|
|
||||||
cJSON *root = cJSON_Parse(msg);
|
|
||||||
if (!root) {
|
|
||||||
free(msg);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle different message types
|
|
||||||
cJSON *id = cJSON_GetObjectItem(root, "id");
|
|
||||||
cJSON *method = cJSON_GetObjectItem(root, "method");
|
|
||||||
cJSON *result = cJSON_GetObjectItem(root, "result");
|
|
||||||
|
|
||||||
if (id) {
|
|
||||||
// This is a response - check if it's shutdown response
|
|
||||||
int msg_id = id->valueint;
|
|
||||||
if (msg_id == 0) { // Our shutdown request ID
|
|
||||||
client->state = LSP_SHUTDOWN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (method) {
|
|
||||||
const char *method_str = method->valuestring;
|
|
||||||
|
|
||||||
// Handle completion results
|
|
||||||
if (strcmp(method_str, "textDocument/completion") == 0) {
|
|
||||||
// Process completion items
|
|
||||||
// ... (completion handling code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cJSON_Delete(root);
|
|
||||||
free(msg);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Shuts down the LSP server gracefully with timeout
|
|
||||||
* @param client LSP client
|
|
||||||
*/
|
|
||||||
void lspShutdown(LspClient *client)
|
|
||||||
{
|
|
||||||
if (!client)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Send didClose for all open buffers first
|
|
||||||
for (int i = 0; i < E.number_of_buffer; i++) {
|
|
||||||
if (E.buffers[i].b_lsp_open) {
|
|
||||||
lspDidClose(client, &E.buffers[i]);
|
|
||||||
E.buffers[i].b_lsp_open = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only attempt shutdown if server is ready
|
|
||||||
if (client->state == LSP_READY) {
|
|
||||||
// Send shutdown request
|
|
||||||
cJSON *root = cJSON_CreateObject();
|
|
||||||
cJSON_AddStringToObject(root, "jsonrpc", "2.0");
|
|
||||||
cJSON_AddNumberToObject(root, "id", 1);
|
|
||||||
cJSON_AddStringToObject(root, "method", "shutdown");
|
|
||||||
|
|
||||||
char *msg = cJSON_PrintUnformatted(root);
|
|
||||||
lsp_send(client, msg);
|
|
||||||
free(msg);
|
|
||||||
cJSON_Delete(root);
|
|
||||||
|
|
||||||
client->state = LSP_SHUTTING_DOWN;
|
|
||||||
|
|
||||||
// Don't wait forever - timeout after 1 second
|
|
||||||
int timeout = 1000;
|
|
||||||
while (client->state == LSP_SHUTTING_DOWN && timeout > 0) {
|
|
||||||
usleep(10000);
|
|
||||||
timeout -= 10;
|
|
||||||
// Try to read any response
|
|
||||||
lspReadMessages(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send exit notification
|
|
||||||
root = cJSON_CreateObject();
|
|
||||||
cJSON_AddStringToObject(root, "jsonrpc", "2.0");
|
|
||||||
cJSON_AddStringToObject(root, "method", "exit");
|
|
||||||
|
|
||||||
msg = cJSON_PrintUnformatted(root);
|
|
||||||
lsp_send(client, msg);
|
|
||||||
free(msg);
|
|
||||||
cJSON_Delete(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Force state to shutdown
|
|
||||||
client->state = LSP_SHUTDOWN;
|
|
||||||
|
|
||||||
// ── Clean up pipes ──────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
// Close wake pipe
|
|
||||||
if (client->wake_pipe[0] > 0) {
|
|
||||||
close(client->wake_pipe[0]);
|
|
||||||
client->wake_pipe[0] = -1;
|
|
||||||
}
|
|
||||||
if (client->wake_pipe[1] > 0) {
|
|
||||||
close(client->wake_pipe[1]);
|
|
||||||
client->wake_pipe[1] = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close LSP communication pipes
|
|
||||||
if (client->write_fd > 0) {
|
|
||||||
close(client->write_fd);
|
|
||||||
client->pid = -1;
|
|
||||||
}
|
|
||||||
if (client->read_fd > 0) {
|
|
||||||
close(client->read_fd);
|
|
||||||
client->read_fd = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Kill LSP server process ──────────────────────────────────────────────
|
|
||||||
|
|
||||||
if (client->pid > 0) {
|
|
||||||
// First try graceful termination
|
|
||||||
kill(client->pid, SIGTERM);
|
|
||||||
|
|
||||||
// Wait briefly for process to exit
|
|
||||||
usleep(100000); // 100ms
|
|
||||||
|
|
||||||
int status;
|
|
||||||
pid_t result = waitpid(client->pid, &status, WNOHANG);
|
|
||||||
|
|
||||||
if (result == 0) {
|
|
||||||
// Process still running - force kill
|
|
||||||
kill(client->pid, SIGKILL);
|
|
||||||
waitpid(client->pid, NULL, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
client->pid = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Clean up mutex/cond ──────────────────────────────────────────────────
|
|
||||||
|
|
||||||
pthread_mutex_destroy(&client->lock);
|
|
||||||
pthread_cond_destroy(&client->ready_cond);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Wakes up the select() loop from another thread
|
|
||||||
* @param client LSP client
|
|
||||||
*/
|
|
||||||
void lspWake(LspClient *client)
|
|
||||||
{
|
|
||||||
if (!client || client->wake_pipe[1] <= 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
char c = 1;
|
|
||||||
write(client->wake_pipe[1], &c, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Cleans up LSP client resources
|
|
||||||
* @param client LSP client
|
|
||||||
*/
|
|
||||||
void lspCleanup(LspClient *client)
|
|
||||||
{
|
|
||||||
if (!client)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lspShutdown(client);
|
|
||||||
free(client);
|
|
||||||
}
|
|
||||||
+59
-119
@@ -1,13 +1,10 @@
|
|||||||
#include "../include/editor_op.h"
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#include "../include/editor_op.h"
|
||||||
#include "../include/row_op.h"
|
#include "../include/row_op.h"
|
||||||
#include "../include/buffer.h"
|
#include "include/buffer.h"
|
||||||
#include "../include/data.h"
|
#include "include/data.h"
|
||||||
#include "../include/split_screen.h"
|
#include "include/split_screen.h"
|
||||||
#include "../include/terminal.h"
|
|
||||||
#include "../include/utf8.h"
|
|
||||||
|
|
||||||
|
|
||||||
extern struct editorConfig E;
|
extern struct editorConfig E;
|
||||||
@@ -22,121 +19,64 @@ extern struct editorConfig E;
|
|||||||
* @note Updates global editor state E (status_msg, status_msg_time)
|
* @note Updates global editor state E (status_msg, status_msg_time)
|
||||||
* @see editorDrawMessageBar()
|
* @see editorDrawMessageBar()
|
||||||
*/
|
*/
|
||||||
void editorSetStatusMessage(const char* fmt, ...)
|
void editorSetStatusMessage(const char *fmt, ...) {
|
||||||
{
|
va_list ap;
|
||||||
va_list ap;
|
va_start(ap, fmt);
|
||||||
va_start(ap, fmt);
|
vsnprintf(E.status_msg, sizeof(E.status_msg), fmt, ap);
|
||||||
vsnprintf(E.status_msg, E.screencols, fmt, ap);
|
va_end(ap);
|
||||||
va_end(ap);
|
E.status_msg_time = time(NULL);
|
||||||
E.status_msg_time = time(NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Moves the cursor based on arrow key input
|
void bufferInsertChar(int c) {
|
||||||
* @details Updates cursor position (E.cursor_x, E.cursor_y) based on the given
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
* key direction. Handles line wrapping and boundary conditions. Prevents cursor
|
struct buffer_t *buf = bufferFindById(active->buffer_id);
|
||||||
* from exceeding line lengths.
|
if (active->cursor_y == buf->numrows) {
|
||||||
* @param key The arrow key code (ARROW_UP, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT)
|
bufferInsertRow(buf, buf->numrows, "", 0);
|
||||||
* @return 1 if cursor movement was valid, 0 if cursor was constrained to line boundary
|
}
|
||||||
* @note Updates global editor state E
|
bufferRowInsertChar(buf, &buf->row[active->cursor_y], active->cursor_x, c);
|
||||||
*/
|
active->cursor_x++;
|
||||||
int editorMoveCursor(int key) {
|
}
|
||||||
|
|
||||||
|
void bufferInsertNewLine() {
|
||||||
|
/*
|
||||||
|
* Add new line and place the cursor at the beginning of it
|
||||||
|
*/
|
||||||
|
fprintf(stderr, "Inserting new line\n");
|
||||||
EditorPane *active = splitScreenGetActivePane();
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
struct buffer_t *buf = bufferFindById(active->buffer_id);
|
struct buffer_t *buf = bufferFindById(active->buffer_id);
|
||||||
row_t *row = &buf->row[buf->y];
|
frow *row;
|
||||||
switch (key) {
|
if (!active->cursor_x) {
|
||||||
case ARROW_RIGHT:
|
bufferInsertRow(buf, active->cursor_y, "", 0);
|
||||||
if (row && buf->x < row->size) {
|
} else {
|
||||||
int len = utf8Seqlen(row->chars[buf->x]);
|
row = &buf->row[active->cursor_y];
|
||||||
buf->x += len;
|
bufferInsertRow(buf, active->cursor_y + 1, &row->chars[active->cursor_x],
|
||||||
} else if (row && buf->y < buf->numrows - 1) {
|
row->size - active->cursor_x);
|
||||||
buf->y++;
|
row = &buf->row[active->cursor_y];
|
||||||
buf->x = 0;
|
row->size = active->cursor_x;
|
||||||
}
|
row->chars[row->size] = '\0';
|
||||||
break;
|
bufferUpdatfrow(row);
|
||||||
case ARROW_DOWN:
|
}
|
||||||
if (buf->y < buf->numrows - 1) {
|
++active->cursor_y;
|
||||||
|
active->cursor_x = 0;
|
||||||
buf->y++;
|
fprintf(stderr, "Insert new line done\n");
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ARROW_UP:
|
|
||||||
if (buf->y != 0) {
|
|
||||||
--buf->y;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ARROW_LEFT:
|
|
||||||
if (buf->x != 0) {
|
|
||||||
--buf->x;
|
|
||||||
} else if (buf->y > 0) {
|
|
||||||
--buf->y;
|
|
||||||
buf->x = buf->row[buf->y].size;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char *editorGetClipboard(void) {
|
void bufferDelChar() {
|
||||||
FILE *pipe = popen(CLIPBOARD_PASTE_CMD, "r");
|
frow *row;
|
||||||
if (!pipe) return NULL;
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
|
struct buffer_t *buf = bufferFindById(active->buffer_id);
|
||||||
size_t cap = 4096;
|
if (active->cursor_y == buf->numrows || !(active->cursor_x || active->cursor_y)) {
|
||||||
size_t len = 0;
|
return;
|
||||||
char *buf = malloc(cap);
|
}
|
||||||
if (!buf) {
|
row = &buf->row[active->cursor_y];
|
||||||
pclose(pipe);
|
if (active->cursor_x > 0) {
|
||||||
return NULL;
|
bufferRowDelchar(buf, row, active->cursor_x - 1);
|
||||||
}
|
--active->cursor_x;
|
||||||
|
} else {
|
||||||
int c;
|
active->cursor_x = buf->row[active->cursor_y - 1].size;
|
||||||
while ((c = fgetc(pipe)) != EOF) {
|
bufferRowAppendString(buf, &buf->row[active->cursor_y - 1], row->chars, row->size);
|
||||||
if (len + 1 >= cap) {
|
bufferDelRow(buf, active->cursor_y);
|
||||||
cap *= 2;
|
--active->cursor_y;
|
||||||
char *new_buf = realloc(buf, cap);
|
}
|
||||||
if (!new_buf) {
|
|
||||||
free(buf);
|
|
||||||
pclose(pipe);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
buf = new_buf;
|
|
||||||
}
|
|
||||||
buf[len++] = (char)c;
|
|
||||||
}
|
|
||||||
buf[len] = '\0';
|
|
||||||
pclose(pipe);
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
void editorSetClipboard(const char *text, int len) {
|
|
||||||
FILE *pipe = popen(CLIPBOARD_COPY_CMD, "w");
|
|
||||||
if (!pipe) return;
|
|
||||||
fwrite(text, 1, len, pipe);
|
|
||||||
pclose(pipe);
|
|
||||||
}
|
|
||||||
|
|
||||||
int editorRowCxToByte(const row_t *row, int cursor_x) {
|
|
||||||
int i = 0, col = 0;
|
|
||||||
while (col < cursor_x && i < row->size) {
|
|
||||||
int sl = utf8Seqlen((unsigned char)row->chars[i]);
|
|
||||||
if (sl < 1)
|
|
||||||
sl = 1;
|
|
||||||
col++;
|
|
||||||
i += sl;
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
int editorRowCharCount(row_t *row, int x) {
|
|
||||||
int n = 0, i = 0;
|
|
||||||
while (i < x && i < row->size) {
|
|
||||||
int sl = utf8Seqlen((unsigned char)row->chars[i]);
|
|
||||||
if (sl < 1)
|
|
||||||
sl = 1;
|
|
||||||
n++;
|
|
||||||
i += sl;
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
}
|
||||||
+152
-29
@@ -9,17 +9,110 @@
|
|||||||
#include "../include/file_io.h"
|
#include "../include/file_io.h"
|
||||||
#include "../include/editor_op.h"
|
#include "../include/editor_op.h"
|
||||||
#include "../include/input.h"
|
#include "../include/input.h"
|
||||||
#include "../include/buffer.h"
|
#include "include/buffer.h"
|
||||||
#include "../include/data.h"
|
#include "include/data.h"
|
||||||
#include "../include/split_screen.h"
|
#include "include/split_screen.h"
|
||||||
#include "../include/row_op.h"
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
|
// Windows compatibility for getline
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <io.h>
|
||||||
|
|
||||||
|
ssize_t getline(char **lineptr, size_t *n, FILE *stream) {
|
||||||
|
char *bufptr = NULL;
|
||||||
|
char *p = bufptr;
|
||||||
|
size_t size;
|
||||||
|
int c;
|
||||||
|
|
||||||
|
if (lineptr == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (stream == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (n == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
bufptr = *lineptr;
|
||||||
|
size = *n;
|
||||||
|
|
||||||
|
c = fgetc(stream);
|
||||||
|
if (c == EOF) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bufptr == NULL) {
|
||||||
|
bufptr = malloc(128);
|
||||||
|
if (bufptr == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
size = 128;
|
||||||
|
}
|
||||||
|
p = bufptr;
|
||||||
|
while(c != EOF) {
|
||||||
|
if ((p - bufptr) > (size - 1)) {
|
||||||
|
size = size + 128;
|
||||||
|
bufptr = realloc(bufptr, size);
|
||||||
|
if (bufptr == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*p++ = c;
|
||||||
|
if (c == '\n') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
c = fgetc(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
*p++ = '\0';
|
||||||
|
*lineptr = bufptr;
|
||||||
|
*n = size;
|
||||||
|
|
||||||
|
return p - bufptr - 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern char *strdup(const char *);
|
||||||
|
extern ssize_t getline(char **restrict lineptr, size_t *restrict n,
|
||||||
|
FILE *restrict stream);
|
||||||
|
extern int ftruncate(int fd, off_t length);
|
||||||
|
extern struct editorConfig E;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converts all editor rows to a single string buffer
|
||||||
|
* @details Concatenates all row content into a single allocated buffer with
|
||||||
|
* newlines between rows. Useful for file saving and buffer operations.
|
||||||
|
* @param buffer_len Pointer to integer where total buffer length will be stored
|
||||||
|
* @return Pointer to dynamically allocated buffer containing all row data.
|
||||||
|
* Rows are separated by newline characters.
|
||||||
|
* @note Caller is responsible for freeing the returned buffer
|
||||||
|
*/
|
||||||
|
char *bufferRowsToString(struct buffer_t *buf, int *buffer_len) {
|
||||||
|
int tot_len = 0;
|
||||||
|
int j;
|
||||||
|
char *buffer;
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
for (j = 0; j < buf->numrows; ++j) {
|
||||||
|
tot_len += buf->row[j].size + 1;
|
||||||
|
}
|
||||||
|
*buffer_len = tot_len;
|
||||||
|
buffer = malloc(tot_len);
|
||||||
|
p = buffer;
|
||||||
|
for (j = 0; j < buf->numrows; ++j) {
|
||||||
|
memcpy(p, buf->row[j].chars, buf->row[j].size);
|
||||||
|
p += buf->row[j].size;
|
||||||
|
*p = '\n';
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Closes the current file and resets editor state
|
* @brief Closes the current file and resets editor state
|
||||||
@@ -32,10 +125,12 @@ void editorCloseFile(void) {
|
|||||||
struct buffer_t *buf = bufferFindById(active->buffer_id);
|
struct buffer_t *buf = bufferFindById(active->buffer_id);
|
||||||
active->cursor_x = 0;
|
active->cursor_x = 0;
|
||||||
active->cursor_y = 0;
|
active->cursor_y = 0;
|
||||||
active->x_offset = 0;
|
active->rx = 0;
|
||||||
active->y_offset = 0;
|
active->row_offset = 0;
|
||||||
|
active->col_offset = 0;
|
||||||
for (int i = 0; i < buf->numrows; ++i) {
|
for (int i = 0; i < buf->numrows; ++i) {
|
||||||
free(buf->row[i].chars);
|
free(buf->row[i].chars);
|
||||||
|
free(buf->row[i].render);
|
||||||
}
|
}
|
||||||
buf->numrows = 0;
|
buf->numrows = 0;
|
||||||
free(buf->row);
|
free(buf->row);
|
||||||
@@ -52,7 +147,7 @@ void editorCloseFile(void) {
|
|||||||
* @details Loads file content into editor rows, one line per row. If another
|
* @details Loads file content into editor rows, one line per row. If another
|
||||||
* file is already open, it is closed first (without saving). File is opened in
|
* file is already open, it is closed first (without saving). File is opened in
|
||||||
* a+ (read/append) mode to allow both reading and modification.
|
* a+ (read/append) mode to allow both reading and modification.
|
||||||
* @param buffer Path to the file to open (relative or absolute)
|
* @param filename Path to the file to open (relative or absolute)
|
||||||
* @note Updates global editor state E
|
* @note Updates global editor state E
|
||||||
* @note Calls die() on file open failure
|
* @note Calls die() on file open failure
|
||||||
* @note Newline characters are stripped from loaded lines
|
* @note Newline characters are stripped from loaded lines
|
||||||
@@ -60,7 +155,8 @@ void editorCloseFile(void) {
|
|||||||
*/
|
*/
|
||||||
void editorOpen(struct buffer_t* buffer) {
|
void editorOpen(struct buffer_t* buffer) {
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
fp = fopen(buffer->fullname, "r");
|
|
||||||
|
fp = fopen(buffer->filename, "a+");
|
||||||
if (!fp)
|
if (!fp)
|
||||||
die("fopen");
|
die("fopen");
|
||||||
|
|
||||||
@@ -68,20 +164,16 @@ void editorOpen(struct buffer_t* buffer) {
|
|||||||
size_t line_cap = 0;
|
size_t line_cap = 0;
|
||||||
ssize_t line_len;
|
ssize_t line_len;
|
||||||
|
|
||||||
rewind(fp);
|
|
||||||
|
|
||||||
while ((line_len = getline(&line, &line_cap, fp)) != -1) {
|
while ((line_len = getline(&line, &line_cap, fp)) != -1) {
|
||||||
while (line_len > 0 &&
|
while (line_len > 0 &&
|
||||||
(line[line_len - 1] == '\n' || line[line_len - 1] == '\r')) {
|
(line[line_len - 1] == '\n' || line[line_len - 1] == '\r')) {
|
||||||
--line_len;
|
--line_len;
|
||||||
}
|
}
|
||||||
bufferInsertRow(buffer, buffer->numrows, line, line_len);
|
bufferInsertRow(buffer, buffer->numrows, line, line_len);
|
||||||
free(line);
|
free(line);
|
||||||
line = NULL;
|
line = NULL;
|
||||||
line_cap = 0; // Reset for next iteration
|
|
||||||
}
|
}
|
||||||
free(line);
|
free(line);
|
||||||
line = NULL;
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
E.dirty = 0;
|
E.dirty = 0;
|
||||||
}
|
}
|
||||||
@@ -100,7 +192,8 @@ void editorSave() {
|
|||||||
EditorPane *active = splitScreenGetActivePane();
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
struct buffer_t *buffer = bufferFindById(active->buffer_id);
|
struct buffer_t *buffer = bufferFindById(active->buffer_id);
|
||||||
int len;
|
int len;
|
||||||
FILE *fd;
|
char *buf;
|
||||||
|
int fd;
|
||||||
if (buffer->filename == NULL) {
|
if (buffer->filename == NULL) {
|
||||||
buffer->filename = editorPrompt("Save as: %s (ESC to cancel)", "", 1);
|
buffer->filename = editorPrompt("Save as: %s (ESC to cancel)", "", 1);
|
||||||
if (buffer->filename == NULL) {
|
if (buffer->filename == NULL) {
|
||||||
@@ -108,21 +201,51 @@ void editorSave() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fd = fopen(buffer->fullname, "w");
|
buf = bufferRowsToString(buffer, &len);
|
||||||
if (fd != NULL) {
|
fd = open(buffer->filename, O_RDWR | O_CREAT, 0644);
|
||||||
for (int i = 0; i < buffer->numrows; ++i)
|
if (fd != -1) {
|
||||||
{
|
if (ftruncate(fd, len) != -1) {
|
||||||
len = (int) strlen(buffer->row[i].chars);
|
if (write(fd, buf, len) == len) {
|
||||||
if (fwrite(buffer->row[i].chars, 1, len, fd) != len) {
|
close(fd);
|
||||||
fclose(fd);
|
free(buf);
|
||||||
editorSetStatusMessage("Can't save! I/O error: %s", strerror(errno));
|
E.dirty = 0;
|
||||||
|
editorSetStatusMessage("%d bytes written to disk", len);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fwrite("\n", 1, 1, fd);
|
|
||||||
}
|
}
|
||||||
buffer->dirty = 0;
|
close(fd);
|
||||||
fclose(fd);
|
|
||||||
}
|
}
|
||||||
editorSetStatusMessage("File saved");
|
free(buf);
|
||||||
|
editorSetStatusMessage("Can't save! I/O error: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Searches for a string in the document
|
||||||
|
* @details Prompts user for a search query, then searches forward from current
|
||||||
|
* cursor position. Updates cursor position to the first match found.
|
||||||
|
* @note Updates global editor state E (cursor position, row_offset)
|
||||||
|
* @note Search is case-sensitive and operates on rendered line content
|
||||||
|
* @note Searches begin from the line after current cursor position
|
||||||
|
* @see editorPrompt()
|
||||||
|
* @see editorRowRxToCx()
|
||||||
|
*/
|
||||||
|
void bufferFind(struct buffer_t *buf) {
|
||||||
|
fprintf(stderr, "searching\n");
|
||||||
|
char *query = editorPrompt("Search: %s (ESC to cancel)", "", 0);
|
||||||
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
|
|
||||||
|
if (query == NULL)
|
||||||
|
return;
|
||||||
|
int i;
|
||||||
|
for (i = active->cursor_y + 1; i < buf->numrows; i++) {
|
||||||
|
frow *row = &buf->row[i];
|
||||||
|
char *match = strstr(row->render, query);
|
||||||
|
if (match) {
|
||||||
|
active->cursor_y = i;
|
||||||
|
active->cursor_x = bufferRowRxToCx(row, match - row->render);
|
||||||
|
buf->row_offset = buf->numrows;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(query);
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-50
@@ -3,7 +3,7 @@
|
|||||||
#include "../include/color.h"
|
#include "../include/color.h"
|
||||||
#include "../include/data.h"
|
#include "../include/data.h"
|
||||||
#include "../include/terminal.h"
|
#include "../include/terminal.h"
|
||||||
#include "../include/split_screen.h"
|
#include "include/split_screen.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -17,7 +17,6 @@ struct editorConfig;
|
|||||||
void registerBuiltin(char *key_sequence, LispCFunc f) {
|
void registerBuiltin(char *key_sequence, LispCFunc f) {
|
||||||
lisp_env_define(E.ctx.p->env, lisp_make_symbol(key_sequence, E.ctx),
|
lisp_env_define(E.ctx.p->env, lisp_make_symbol(key_sequence, E.ctx),
|
||||||
lisp_make_func(f), E.ctx);
|
lisp_make_func(f), E.ctx);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void initBuiltins() {
|
void initBuiltins() {
|
||||||
@@ -35,8 +34,8 @@ void initBuiltins() {
|
|||||||
registerBuiltin("editor-open-file", editorOpenFile);
|
registerBuiltin("editor-open-file", editorOpenFile);
|
||||||
registerBuiltin("editor-insert-char", editorPrintC);
|
registerBuiltin("editor-insert-char", editorPrintC);
|
||||||
registerBuiltin("add-package", addPackage);
|
registerBuiltin("add-package", addPackage);
|
||||||
|
registerBuiltin("editor-del-row", editorDelRow_L);
|
||||||
registerBuiltin("buffer-find", bufferFind_L);
|
registerBuiltin("buffer-find", bufferFind_L);
|
||||||
registerBuiltin("buffer-find-reverse", bufferFindReverse_L);
|
|
||||||
registerBuiltin("editor-read-char", editorReadChar_L);
|
registerBuiltin("editor-read-char", editorReadChar_L);
|
||||||
registerBuiltin("add-prefix", editorPrefix);
|
registerBuiltin("add-prefix", editorPrefix);
|
||||||
registerBuiltin("editor-set-prefix", editorSetPrefix);
|
registerBuiltin("editor-set-prefix", editorSetPrefix);
|
||||||
@@ -45,19 +44,12 @@ void initBuiltins() {
|
|||||||
registerBuiltin("editor-split-screen-vertical", l_editorSplitScreenVertical);
|
registerBuiltin("editor-split-screen-vertical", l_editorSplitScreenVertical);
|
||||||
registerBuiltin("editor-switch-next-pane", editorSwitchNextPane);
|
registerBuiltin("editor-switch-next-pane", editorSwitchNextPane);
|
||||||
registerBuiltin("editor-unify-panes", editorUnifiedPanes);
|
registerBuiltin("editor-unify-panes", editorUnifiedPanes);
|
||||||
registerBuiltin("editor-paste", editorPaste);
|
registerBuiltin("editor-create-terminal", editorCreateTerminal);
|
||||||
registerBuiltin("editor-cut-end-line", editorCutEndLine);
|
|
||||||
registerBuiltin("editor-move-cursor-beg-buffer", editorMoveBegBuffer);
|
|
||||||
registerBuiltin("editor-move-cursor-end-buffer", editorMoveEndBuffer);
|
|
||||||
registerBuiltin("editor-auto-complete", editorAutoComplete);
|
|
||||||
// registerBuiltin("lsp-complete", lspComplete);
|
|
||||||
registerBuiltin("lsp-definition", lspDefinition);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void initConfig() {
|
void initConfig() {
|
||||||
|
|
||||||
E.ctx = lisp_init();
|
E.ctx = lisp_init();
|
||||||
E.ctx.p->err_port = fopen("lisp_log.err", "w");
|
|
||||||
E.env = lisp_env(E.ctx);
|
E.env = lisp_env(E.ctx);
|
||||||
lisp_lib_load(E.ctx);
|
lisp_lib_load(E.ctx);
|
||||||
// Init builtins lisp functions
|
// Init builtins lisp functions
|
||||||
@@ -71,11 +63,9 @@ void initConfig() {
|
|||||||
lisp_eval(E.ctx_data, &E.ctx_error, E.ctx);
|
lisp_eval(E.ctx_data, &E.ctx_error, E.ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void initTheme() {
|
void init_theme() {
|
||||||
E.constantes.THEME = (char *)lisp_string(
|
E.constantes.THEME = (char *)lisp_string(
|
||||||
lisp_eval(lisp_read("THEME", &E.ctx_error, E.ctx), &E.ctx_error, E.ctx));
|
lisp_eval(lisp_read("THEME", &E.ctx_error, E.ctx), &E.ctx_error, E.ctx));
|
||||||
E.constantes.LSP = lisp_bool(lisp_eval(lisp_read("LSP", &E.ctx_error, E.ctx), &E.ctx_error, E.ctx));
|
|
||||||
appDebug("LSP ON : %d", E.constantes.LSP);
|
|
||||||
if (strcmp(E.constantes.THEME, "dark") == 0) {
|
if (strcmp(E.constantes.THEME, "dark") == 0) {
|
||||||
E.theme.BACKGROUND_COLOR = ANSI_BG_RGB(40, 44, 52);
|
E.theme.BACKGROUND_COLOR = ANSI_BG_RGB(40, 44, 52);
|
||||||
E.theme.COLOR_KEYWORD = ANSI_FG_RGB(198, 120, 221);
|
E.theme.COLOR_KEYWORD = ANSI_FG_RGB(198, 120, 221);
|
||||||
@@ -100,43 +90,23 @@ void initEditor() {
|
|||||||
if (getWindowSize(&E.screenrows, &E.screencols) == -1) {
|
if (getWindowSize(&E.screenrows, &E.screencols) == -1) {
|
||||||
die("getWindowSize");
|
die("getWindowSize");
|
||||||
}
|
}
|
||||||
appDebug("%d %d", E.screenrows, E.screencols);
|
|
||||||
E.screenrows -= 2;
|
E.screenrows -= 2;
|
||||||
|
|
||||||
|
|
||||||
// Init graphics variables
|
// Init graphics variables
|
||||||
splitScreenInit();
|
splitScreenInit();
|
||||||
EditorPane *active = splitScreenGetActivePane();
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
E.cursor_x = 0;
|
|
||||||
E.cursor_y = 0;
|
|
||||||
|
|
||||||
active->cursor_x = 0;
|
active->cursor_x = 0;
|
||||||
active->cursor_y = 0;
|
active->cursor_y = 0;
|
||||||
active->origin_x = 0;
|
active->rx = 0;
|
||||||
active->origin_y = 0;
|
active->row_offset = 0;
|
||||||
active->width = E.screencols;
|
active->col_offset = 0;
|
||||||
active->height = E.screenrows;
|
E.init_file_path = (char *)calloc(256, sizeof(char));
|
||||||
|
strcat(E.init_file_path, getenv("HOME"));
|
||||||
const char *home = getenv("HOME");
|
strcat(E.init_file_path, "/.beluga/config/init.lisp");
|
||||||
if (!home) {
|
|
||||||
die("HOME environment variable not set");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (asprintf(&E.init_file_path, "%s/.beluga/config/init.lisp", home) == -1) {
|
|
||||||
die("asprintf failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
E.fd_init_file = fopen(E.init_file_path, "r");
|
E.fd_init_file = fopen(E.init_file_path, "r");
|
||||||
if (!E.fd_init_file) {
|
|
||||||
// File might not exist, that's okay for some cases
|
|
||||||
// Handle accordingly
|
|
||||||
}
|
|
||||||
|
|
||||||
E.status_msg = calloc(E.screencols, sizeof(char));
|
// Status bar
|
||||||
if (!E.status_msg) {
|
|
||||||
die("malloc failed");
|
|
||||||
}
|
|
||||||
E.status_msg[0] = '\0';
|
|
||||||
E.status_msg[0] = '\0';
|
E.status_msg[0] = '\0';
|
||||||
E.status_msg_time = 0;
|
E.status_msg_time = 0;
|
||||||
|
|
||||||
@@ -149,11 +119,9 @@ void initEditor() {
|
|||||||
strncpy(E.prefix[0].prefix_name, "no-prefix", 64);
|
strncpy(E.prefix[0].prefix_name, "no-prefix", 64);
|
||||||
E.prefix_state = 0;
|
E.prefix_state = 0;
|
||||||
|
|
||||||
E.lsp_client = (LspClient*)calloc(1,sizeof(LspClient));
|
|
||||||
E.lsp_client->state = LSP_SHUTDOWN;
|
|
||||||
|
|
||||||
initConfig();
|
initConfig();
|
||||||
initTheme();
|
|
||||||
|
init_theme();
|
||||||
|
|
||||||
// To modify
|
// To modify
|
||||||
|
|
||||||
@@ -168,8 +136,3 @@ void initEditor() {
|
|||||||
|
|
||||||
E.quit_times_buffer = E.constantes.QUIT_TIMES;
|
E.quit_times_buffer = E.constantes.QUIT_TIMES;
|
||||||
}
|
}
|
||||||
|
|
||||||
void deInitEditor()
|
|
||||||
{
|
|
||||||
fclose(E.ctx.p->err_port);
|
|
||||||
}
|
|
||||||
|
|||||||
+212
-103
@@ -3,25 +3,22 @@
|
|||||||
#include "../include/editor_op.h"
|
#include "../include/editor_op.h"
|
||||||
#include "../include/output.h"
|
#include "../include/output.h"
|
||||||
#include "include/buffer.h"
|
#include "include/buffer.h"
|
||||||
#include "include/builtins.h"
|
|
||||||
#include "../include/completion.h"
|
|
||||||
#include "include/data.h"
|
#include "include/data.h"
|
||||||
#include "include/split_screen.h"
|
#include "include/split_screen.h"
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "include/terminal.h"
|
extern struct editorConfig E;
|
||||||
#include "include/utf8.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file input.c
|
* @file input.c
|
||||||
* @brief Input handling module for the Beluga text editor
|
* @brief Input handling module for the Beluga text editor
|
||||||
* @details Manages user input processing, key bindings, cursor movement, and
|
* @details Manages user input processing, key bindings, cursor movement, and file path completion
|
||||||
* file path completion
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,18 +27,17 @@
|
|||||||
* the first file or directory entry that matches the filename prefix.
|
* the first file or directory entry that matches the filename prefix.
|
||||||
* Appends a trailing slash for directory entries.
|
* Appends a trailing slash for directory entries.
|
||||||
* @param path The file path to complete (can be relative or absolute)
|
* @param path The file path to complete (can be relative or absolute)
|
||||||
* @return Pointer to the completed file path (dynamically allocated), or NULL
|
* @return Pointer to the completed file path (dynamically allocated), or NULL if:
|
||||||
* if:
|
|
||||||
* - path ends with '/' (already a directory)
|
* - path ends with '/' (already a directory)
|
||||||
* - no matching entries found
|
* - no matching entries found
|
||||||
* - directory cannot be opened
|
* - directory cannot be opened
|
||||||
* @note Caller is responsible for bFreeing the returned string
|
* @note Caller is responsible for freeing the returned string
|
||||||
* @note Uses static buffer internally; may return stale pointers across calls
|
* @note Uses static buffer internally; may return stale pointers across calls
|
||||||
*/
|
*/
|
||||||
const char *fileCompletion(const char *path) {
|
const char *file_completion(const char *path) {
|
||||||
DIR *dir;
|
DIR *dir;
|
||||||
struct dirent *entry;
|
struct dirent *entry;
|
||||||
char directory[256];
|
char directory[128];
|
||||||
char predict[128];
|
char predict[128];
|
||||||
const char *last_slash;
|
const char *last_slash;
|
||||||
int predict_len = 0;
|
int predict_len = 0;
|
||||||
@@ -49,8 +45,8 @@ const char *fileCompletion(const char *path) {
|
|||||||
|
|
||||||
// path is a directory
|
// path is a directory
|
||||||
if (path[strlen(path) - 1] == '/') {
|
if (path[strlen(path) - 1] == '/') {
|
||||||
appDebug("[FILE COMP] is dir\n");
|
fprintf(stderr, "[FILE COMP] is dir\n");
|
||||||
strncpy(directory, path, 256);
|
strncpy(directory, path, 128);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find dir name
|
// Find dir name
|
||||||
@@ -58,13 +54,13 @@ const char *fileCompletion(const char *path) {
|
|||||||
if (last_slash) {
|
if (last_slash) {
|
||||||
dir_len = last_slash - path + 1; // length of dir_path
|
dir_len = last_slash - path + 1; // length of dir_path
|
||||||
strncpy(directory, path, dir_len);
|
strncpy(directory, path, dir_len);
|
||||||
predict_len = (int)(strlen(path) - dir_len);
|
predict_len = strlen(path) - dir_len;
|
||||||
strncpy(predict, last_slash + 1, predict_len);
|
strncpy(predict, last_slash + 1, predict_len);
|
||||||
directory[dir_len] = '\0';
|
directory[dir_len] = '\0';
|
||||||
predict[predict_len] = '\0';
|
predict[predict_len] = '\0';
|
||||||
appDebug("%s %s\n", directory, predict);
|
fprintf(stderr, "%s %s\n", directory, predict);
|
||||||
} else {
|
} else {
|
||||||
appDebug("[FILE COMP] dir not found\n");
|
fprintf(stderr, "[FILE COMP] dir not found\n");
|
||||||
return strdup(path);
|
return strdup(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +70,7 @@ const char *fileCompletion(const char *path) {
|
|||||||
|
|
||||||
while ((entry = readdir(dir)) != NULL) {
|
while ((entry = readdir(dir)) != NULL) {
|
||||||
if (strncmp(entry->d_name, predict, predict_len) == 0) {
|
if (strncmp(entry->d_name, predict, predict_len) == 0) {
|
||||||
static char full_path[1024];
|
static char full_path[512];
|
||||||
snprintf(full_path, sizeof(full_path), "%s%s", directory, entry->d_name);
|
snprintf(full_path, sizeof(full_path), "%s%s", directory, entry->d_name);
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
@@ -91,27 +87,26 @@ const char *fileCompletion(const char *path) {
|
|||||||
closedir(dir);
|
closedir(dir);
|
||||||
dir = NULL;
|
dir = NULL;
|
||||||
free(entry);
|
free(entry);
|
||||||
appDebug("[FILE COMP] no entries\n");
|
fprintf(stderr, "[FILE COMP] no entries\n");
|
||||||
return strdup(path);
|
return strdup(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Displays an interactive prompt and returns user input
|
* @brief Displays an interactive prompt and returns user input
|
||||||
* @details Allows the user to enter text in a prompt with optional path
|
* @details Allows the user to enter text in a prompt with optional path completion
|
||||||
* completion via Tab key. Supports backspace, delete, and escape key handling.
|
* via Tab key. Supports backspace, delete, and escape key handling. Dynamically
|
||||||
* Dynamically allocates memory for the input buffer.
|
* allocates memory for the input buffer.
|
||||||
* @param prompt The prompt message format string (printf-style)
|
* @param prompt The prompt message format string (printf-style)
|
||||||
* @param placeHolder Initial text to display in the input buffer
|
* @param placeHolder Initial text to display in the input buffer
|
||||||
* @param bPathMode If non-zero, enables Tab key file path completion
|
* @param bPathMode If non-zero, enables Tab key file path completion
|
||||||
* @return Pointer to the user-entered text (dynamically allocated), or NULL if:
|
* @return Pointer to the user-entered text (dynamically allocated), or NULL if:
|
||||||
* - User pressed ESC to cancel
|
* - User pressed ESC to cancel
|
||||||
* - Input buffer is empty when Enter is pressed
|
* - Input buffer is empty when Enter is pressed
|
||||||
* @note Caller is responsible for bFreeing the returned string
|
* @note Caller is responsible for freeing the returned string
|
||||||
* @note Uses editorReadKey() for input and editorRefreshScreen() for display
|
* @note Uses editorReadKey() for input and editorRefreshScreen() for display
|
||||||
*/
|
*/
|
||||||
char *editorPrompt(char *prompt, char *placeHolder, char bPathMode) {
|
char *editorPrompt(char *prompt, char *placeHolder, char bPathMode) {
|
||||||
size_t buf_size = 256;
|
size_t buf_size = 128;
|
||||||
appDebug("[FILE COMP] %s %d\n", placeHolder, strlen(placeHolder));
|
|
||||||
char *buf = malloc(buf_size);
|
char *buf = malloc(buf_size);
|
||||||
size_t buf_len = 0;
|
size_t buf_len = 0;
|
||||||
int c = 0;
|
int c = 0;
|
||||||
@@ -137,36 +132,162 @@ char *editorPrompt(char *prompt, char *placeHolder, char bPathMode) {
|
|||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
} else if (bPathMode && c == '\t') {
|
} else if (bPathMode && c == '\t') {
|
||||||
char path[256];
|
char path[128];
|
||||||
char *pwd;
|
char *pwd;
|
||||||
if (buf[0] != '/') {
|
if (buf[0] != '/') {
|
||||||
pwd = getenv("PWD");
|
pwd = getenv("PWD");
|
||||||
appDebug("%s\n", pwd);
|
fprintf(stderr, "%s\n", pwd);
|
||||||
memcpy(path, pwd, strlen(pwd));
|
memcpy(path, pwd, strlen(pwd));
|
||||||
path[strlen(pwd)] = '/';
|
path[strlen(pwd)] = '/';
|
||||||
strncat(path, buf, buf_len);
|
strncat(path, buf, buf_len);
|
||||||
} else {
|
} else {
|
||||||
strcpy(path, buf);
|
strcpy(path, buf);
|
||||||
}
|
}
|
||||||
memset(buf, 0, 256);
|
memset(buf, 0, 128);
|
||||||
char *buf_complete = (char *)fileCompletion(path);
|
buf_len = 0;
|
||||||
|
char * buf_complete = (char *) file_completion(path);
|
||||||
strcpy(buf, buf_complete);
|
strcpy(buf, buf_complete);
|
||||||
free(buf_complete);
|
free(buf_complete);
|
||||||
buf_len = strlen(buf);
|
buf_len = strlen(buf);
|
||||||
buf[buf_len] = '\0';
|
buf[buf_len] = '\0';
|
||||||
|
|
||||||
} else if (!iscntrl(c) && c < 256) {
|
} else if (!iscntrl(c) && c < 128) {
|
||||||
if (buf_len == buf_size - 1) {
|
if (buf_len == buf_size - 1) {
|
||||||
buf_size *= 2;
|
buf_size *= 2;
|
||||||
char *new_buf = realloc(buf, buf_size);
|
buf = realloc(buf, buf_size);
|
||||||
if (!new_buf) {
|
}
|
||||||
free(buf);
|
buf[buf_len++] = c;
|
||||||
return NULL;
|
buf[buf_len] = '\0';
|
||||||
}
|
}
|
||||||
buf = new_buf;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converts a key code to its string representation
|
||||||
|
* @details Translates raw key codes (including special keys, control keys,
|
||||||
|
* and regular characters) into human-readable string formats suitable for
|
||||||
|
* display and keybinding configuration.
|
||||||
|
* @param key The key code to convert
|
||||||
|
* @return Pointer to static buffer containing the string representation.
|
||||||
|
* Examples: "ENTER", "ARROW-UP", "CTRL-a", "TAB", "DELETE", etc.
|
||||||
|
* @note Returns pointer to static buffer; string is overwritten on next call
|
||||||
|
* @note Non-printable characters are formatted as "KEY-<number>"
|
||||||
|
*/
|
||||||
|
char *key_to_string(int key) {
|
||||||
|
static char key_str[32];
|
||||||
|
|
||||||
|
char tmp[10];
|
||||||
|
sprintf(tmp, "%d", key);
|
||||||
|
|
||||||
|
// First test enter key
|
||||||
|
|
||||||
|
if (key == '\r') {
|
||||||
|
strcpy(key_str, "ENTER");
|
||||||
|
} else if (key == '\t') {
|
||||||
|
strcpy(key_str, "TAB");
|
||||||
|
} else if (key >= 1 && key <= 26) { // CTRL keys
|
||||||
|
snprintf(key_str, sizeof(key_str), "CTRL-%c", 'a' + key - 1);
|
||||||
|
} else {
|
||||||
|
switch (key) {
|
||||||
|
case ARROW_UP:
|
||||||
|
strcpy(key_str, "ARROW-UP");
|
||||||
|
break;
|
||||||
|
case ARROW_DOWN:
|
||||||
|
strcpy(key_str, "ARROW-DOWN");
|
||||||
|
break;
|
||||||
|
case ARROW_LEFT:
|
||||||
|
strcpy(key_str, "ARROW-LEFT");
|
||||||
|
break;
|
||||||
|
case ARROW_RIGHT:
|
||||||
|
strcpy(key_str, "ARROW-RIGHT");
|
||||||
|
break;
|
||||||
|
case PAGE_UP:
|
||||||
|
strcpy(key_str, "PAGE-UP");
|
||||||
|
fprintf(stderr, "pagr up\n");
|
||||||
|
break;
|
||||||
|
case PAGE_DOWN:
|
||||||
|
strcpy(key_str, "PAGE-DOWN");
|
||||||
|
break;
|
||||||
|
case DEL_KEY:
|
||||||
|
strcpy(key_str, "DEL");
|
||||||
|
|
||||||
|
break;
|
||||||
|
case BACKSPACE:
|
||||||
|
strcpy(key_str, "BACKSPACE");
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
strcpy(key_str, "ENTER");
|
||||||
|
break;
|
||||||
|
case '\x1b':
|
||||||
|
strcpy(key_str, "ESCAPE");
|
||||||
|
break;
|
||||||
|
case BEG_LINE:
|
||||||
|
strcpy(key_str, "HOME");
|
||||||
|
break;
|
||||||
|
case END_LINE:
|
||||||
|
strcpy(key_str, "END");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// For regular characters
|
||||||
|
if (isprint(key)) {
|
||||||
|
snprintf(key_str, sizeof(key_str), "%c", key);
|
||||||
|
} else {
|
||||||
|
snprintf(key_str, sizeof(key_str), "KEY-%d", key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return key_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Moves the cursor based on arrow key input
|
||||||
|
* @details Updates cursor position (E.cursor_x, E.cursor_y) based on the given
|
||||||
|
* key direction. Handles line wrapping and boundary conditions. Prevents cursor
|
||||||
|
* from exceeding line lengths.
|
||||||
|
* @param key The arrow key code (ARROW_UP, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT)
|
||||||
|
* @return 1 if cursor movement was valid, 0 if cursor was constrained to line boundary
|
||||||
|
* @note Updates global editor state E
|
||||||
|
*/
|
||||||
|
int editorMoveCursor(int key) {
|
||||||
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
|
struct buffer_t *buf = bufferFindById(active->buffer_id);
|
||||||
|
frow *row = (active->cursor_y + active->row_offset >= buf->numrows) ? NULL : &buf->row[active->cursor_y + active->row_offset];
|
||||||
|
int row_len;
|
||||||
|
int x = active->cursor_x + active->col_offset;
|
||||||
|
int y = active->cursor_y + active->row_offset;
|
||||||
|
switch (key) {
|
||||||
|
case ARROW_RIGHT:
|
||||||
|
if (row && x < row->size) {
|
||||||
|
active->cursor_x++;
|
||||||
|
|
||||||
|
} else if (row && y == row->size) {
|
||||||
|
active->cursor_y++;
|
||||||
|
active->cursor_x = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ARROW_DOWN:
|
||||||
|
if (y < buf->numrows) {
|
||||||
|
|
||||||
|
active->cursor_y++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ARROW_UP:
|
||||||
|
if (y != 0) {
|
||||||
|
--active->cursor_y;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ARROW_LEFT:
|
||||||
|
if (x != 0) {
|
||||||
|
--active->cursor_x;
|
||||||
|
} else if (y > 0) {
|
||||||
|
--active->cursor_y;
|
||||||
|
active->cursor_x = buf->row[y].size;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -181,7 +302,7 @@ char *editorPrompt(char *prompt, char *placeHolder, char bPathMode) {
|
|||||||
int executeKeyBind(char *key_sequence) {
|
int executeKeyBind(char *key_sequence) {
|
||||||
int i;
|
int i;
|
||||||
int previous_state = 0;
|
int previous_state = 0;
|
||||||
appDebug("pressed %s\n", key_sequence);
|
fprintf(stderr, "pressed %s\n", key_sequence);
|
||||||
for (i = 0; i < E.number_of_keybinds; ++i) {
|
for (i = 0; i < E.number_of_keybinds; ++i) {
|
||||||
if (!strcmp(key_sequence, E.key_binds[i].key_sequence)) {
|
if (!strcmp(key_sequence, E.key_binds[i].key_sequence)) {
|
||||||
if (E.prefix_state != E.key_binds[i].prefix_id) {
|
if (E.prefix_state != E.key_binds[i].prefix_id) {
|
||||||
@@ -199,86 +320,74 @@ int executeKeyBind(char *key_sequence) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handles terminal-specific key processing
|
||||||
|
* @details Processes key presses for terminal buffers, handling command input
|
||||||
|
* and execution.
|
||||||
|
* @param c The key code
|
||||||
|
* @return 1 if key was handled, 0 otherwise
|
||||||
|
*/
|
||||||
|
int handleTerminalKeypress(int c) {
|
||||||
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
|
struct buffer_t *buf = bufferFindById(active->buffer_id);
|
||||||
|
|
||||||
|
if (buf == NULL || buf->type != TERMINAL_BUFF) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle terminal-specific keys
|
||||||
|
if (c == '\r') { // Enter key - execute command
|
||||||
|
if (buf->terminal_command && strlen(buf->terminal_command) > 0) {
|
||||||
|
bufferExecuteTerminalCommand(buf->buffer_id, buf->terminal_command);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else if (c == BACKSPACE || c == CTRL_KEY('h')) { // Backspace
|
||||||
|
if (buf->terminal_command && strlen(buf->terminal_command) > 0) {
|
||||||
|
buf->terminal_command[strlen(buf->terminal_command) - 1] = '\0';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else if (c == CTRL_KEY('c')) { // Ctrl+C - clear command
|
||||||
|
if (buf->terminal_command) {
|
||||||
|
free(buf->terminal_command);
|
||||||
|
buf->terminal_command = NULL;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
} else if (!iscntrl(c) && c < 128) { // Regular character input
|
||||||
|
if (buf->terminal_command == NULL) {
|
||||||
|
buf->terminal_command = malloc(2);
|
||||||
|
buf->terminal_command[0] = c;
|
||||||
|
buf->terminal_command[1] = '\0';
|
||||||
|
} else {
|
||||||
|
int len = strlen(buf->terminal_command);
|
||||||
|
buf->terminal_command = realloc(buf->terminal_command, len + 2);
|
||||||
|
buf->terminal_command[len] = c;
|
||||||
|
buf->terminal_command[len + 1] = '\0';
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Processes a single keypress from the user
|
* @brief Processes a single keypress from the user
|
||||||
* @details Reads a key, checks if it matches any registered keybinding,
|
* @details Reads a key, checks if it matches any registered keybinding,
|
||||||
* and either executes the bound command or inserts the character. Resets
|
* and either executes the bound command or inserts the character. Resets
|
||||||
* the quit buffer counter on successful key processing.
|
* the quit buffer counter on successful key processing.
|
||||||
* @note Updates global editor state E
|
* @note Updates global editor state E
|
||||||
* @note Calls editorReadKey() to get input and editorInsertChar() for unbound
|
* @note Calls editorReadKey() to get input and editorInsertChar() for unbound keys
|
||||||
* keys
|
|
||||||
*/
|
*/
|
||||||
void editorProcessKeypress() {
|
void editorProcessKeypress() {
|
||||||
int c = editorReadKey();
|
int c = editorReadKey();
|
||||||
char key_sequence[8] = {0};
|
|
||||||
EditorPane *active = splitScreenGetActivePane();
|
|
||||||
struct buffer_t *buf = bufferFindById(active->buffer_id);
|
|
||||||
|
|
||||||
if (E.constantes.LSP && buf->b_lsp_open) {
|
// First try terminal-specific handling
|
||||||
if (c == LSP_WAKE_KEY)
|
if (handleTerminalKeypress(c)) {
|
||||||
return;
|
|
||||||
if (E.lsp_client && E.lsp_completion.visible) {
|
|
||||||
if (c == ARROW_UP || c == CTRL_KEY('p')) {
|
|
||||||
if (E.lsp_completion.selected > 0)
|
|
||||||
E.lsp_completion.selected--;
|
|
||||||
return; // consumed, redraw on next loop
|
|
||||||
}
|
|
||||||
if (c == ARROW_DOWN || c == CTRL_KEY('n')) {
|
|
||||||
if (E.lsp_completion.selected < E.lsp_completion.count - 1)
|
|
||||||
E.lsp_completion.selected++;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (c == '\r') {
|
|
||||||
CompletionItem *item =
|
|
||||||
&E.lsp_completion.items[E.lsp_completion.selected];
|
|
||||||
|
|
||||||
|
|
||||||
// Find how many chars the user already typed by looking at the
|
|
||||||
// current word (chars before cursor on the same line)
|
|
||||||
int file_col = active->cursor_x + active->x_offset;
|
|
||||||
row_t *row = &buf->row[active->cursor_y + active->y_offset];
|
|
||||||
|
|
||||||
// Walk backwards from cursor to find start of current word
|
|
||||||
int word_start = file_col;
|
|
||||||
while (word_start > 0 &&
|
|
||||||
(isalnum((unsigned char)row->chars[word_start - 1]) ||
|
|
||||||
row->chars[word_start - 1] == '_'))
|
|
||||||
word_start--;
|
|
||||||
|
|
||||||
int already_typed = file_col - word_start; // chars user already typed
|
|
||||||
|
|
||||||
// Insert only the suffix — what comes after what's already typed
|
|
||||||
const char *suffix = item->label + already_typed;
|
|
||||||
for (int i = 0; suffix[i]; i++)
|
|
||||||
bufferInsertBytes(&suffix[i], 1);
|
|
||||||
|
|
||||||
E.lsp_completion.visible = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (c == ESCAPE) {
|
|
||||||
E.lsp_completion.visible = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Any other key: dismiss popup and fall through to normal handling
|
|
||||||
E.lsp_completion.visible = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (executeKeyBind(keyToString(c))) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int seq_len = utf8Encode(c, key_sequence);
|
|
||||||
appDebug("key seq : %s\n", key_sequence);
|
|
||||||
bufferInsertBytes(key_sequence, seq_len);
|
|
||||||
if (buf->b_lsp_open && is_word_char(key_sequence)) {
|
|
||||||
|
|
||||||
if (E.lsp_client && E.lsp_client->state == LSP_READY) {
|
if (executeKeyBind(key_to_string(c))) {
|
||||||
lspDidChange(E.lsp_client, buf);
|
return;
|
||||||
|
|
||||||
E.lsp_client->completion_just_arrived = 0; // consume the flag
|
|
||||||
}
|
|
||||||
buf->b_has_changed = 0;
|
|
||||||
editorAutoComplete(lisp_null(), &E.ctx_error, E.ctx);
|
|
||||||
}
|
}
|
||||||
|
bufferInsertChar(c);
|
||||||
E.quit_times_buffer = E.constantes.QUIT_TIMES;
|
E.quit_times_buffer = E.constantes.QUIT_TIMES;
|
||||||
}
|
}
|
||||||
|
|||||||
-330
@@ -1,330 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by Giorgio on 27/05/2026.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "include/lsp_ui.h"
|
|
||||||
#include "include/data.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
// We use cJSON — drop cJSON.c + cJSON.h into src/ and include/
|
|
||||||
#include "include/cJSON.h"
|
|
||||||
#include "include/append_buffer.h"
|
|
||||||
#include "include/terminal.h"
|
|
||||||
|
|
||||||
// ─── ANSI helpers ─────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
// Move terminal cursor to absolute (col, row) — 1-based
|
|
||||||
static void ab_move(struct abuf *ab, int row, int col) {
|
|
||||||
char esc[32];
|
|
||||||
int n = snprintf(esc, sizeof(esc), "\x1b[%d;%dH", row, col);
|
|
||||||
abAppend(ab, esc, n); // your existing abAppend
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ab_sgr(struct abuf *ab, const char *codes) {
|
|
||||||
char esc[32];
|
|
||||||
int n = snprintf(esc, sizeof(esc), "\x1b[%sm", codes);
|
|
||||||
abAppend(ab, esc, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define AB_RESET(ab) ab_sgr(ab, "0")
|
|
||||||
#define AB_BOLD(ab) ab_sgr(ab, "1")
|
|
||||||
#define AB_DIM(ab) ab_sgr(ab, "2")
|
|
||||||
#define AB_FG(ab, c) ab_sgr(ab, c) // e.g. "32" = green
|
|
||||||
#define AB_BG(ab, c) ab_sgr(ab, c) // e.g. "44" = blue bg
|
|
||||||
|
|
||||||
// ─── Completion item kind → short tag ────────────────────────────────────────
|
|
||||||
|
|
||||||
static const char *kind_tag(int k) {
|
|
||||||
switch (k) {
|
|
||||||
case 2: return "mth"; // Method
|
|
||||||
case 3: return "fn "; // Function
|
|
||||||
case 4: return "ctr"; // Constructor
|
|
||||||
case 5: return "fld"; // Field
|
|
||||||
case 6: return "var"; // Variable
|
|
||||||
case 7: return "cls"; // Class
|
|
||||||
case 9: return "ifc"; // Interface
|
|
||||||
case 14: return "kwd"; // Keyword
|
|
||||||
default: return " ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *kind_color(int k) {
|
|
||||||
switch (k) {
|
|
||||||
case 3: return "33"; // yellow — functions
|
|
||||||
case 2: return "36"; // cyan — methods
|
|
||||||
case 7: return "35"; // magenta — classes
|
|
||||||
case 14: return "34"; // blue — keywords
|
|
||||||
case 5:
|
|
||||||
case 6: return "32"; // green — fields/vars
|
|
||||||
default: return "37"; // white
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Draw completion popup ───────────────────────────────────────────────────
|
|
||||||
//
|
|
||||||
// ┌─────────────────────────────────────┐
|
|
||||||
// │ fn printf int (const …) │ ← selected (bold, inverted)
|
|
||||||
// │ fn fprintf │
|
|
||||||
// │ fn sprintf │
|
|
||||||
// └─────────────────────────────────────┘
|
|
||||||
//
|
|
||||||
// Drawn using absolute cursor positioning — safe to call inside
|
|
||||||
// editorRefreshScreen() before the final write().
|
|
||||||
|
|
||||||
void lspUiDrawCompletion(struct abuf *ab, CompletionPopup *popup) {
|
|
||||||
appDebug("[DRAW] visible=%d count=%d origin=(%d,%d)\n",
|
|
||||||
popup->visible, popup->count,
|
|
||||||
popup->origin_x, popup->origin_y);
|
|
||||||
if (!popup->visible || popup->count == 0) return;
|
|
||||||
|
|
||||||
int visible_count = popup->count < COMPLETION_POPUP_H
|
|
||||||
? popup->count : COMPLETION_POPUP_H;
|
|
||||||
int box_w = COMPLETION_MAX_WIDTH;
|
|
||||||
|
|
||||||
// Scroll window so selected item is always visible
|
|
||||||
int scroll = 0;
|
|
||||||
if (popup->selected >= COMPLETION_POPUP_H)
|
|
||||||
scroll = popup->selected - COMPLETION_POPUP_H + 1;
|
|
||||||
// ── top border ────────────────────────────────────────────────────────────
|
|
||||||
ab_move(ab, popup->origin_y, popup->origin_x);
|
|
||||||
ab_sgr(ab, "0;37;40"); // white on black
|
|
||||||
abAppend(ab, "┌", 3); // UTF-8 box chars (3 bytes each)
|
|
||||||
for (int i = 0; i < box_w - 2; i++) abAppend(ab, "─", 3);
|
|
||||||
abAppend(ab, "┐", 3);
|
|
||||||
|
|
||||||
// ── rows ──────────────────────────────────────────────────────────────────
|
|
||||||
for (int i = 0; i < visible_count; i++) {
|
|
||||||
int idx = i + scroll;
|
|
||||||
CompletionItem *it = &popup->items[idx];
|
|
||||||
int is_sel = (idx == popup->selected);
|
|
||||||
|
|
||||||
ab_move(ab, popup->origin_y + 1 + i, popup->origin_x);
|
|
||||||
|
|
||||||
if (is_sel) ab_sgr(ab, "7"); // reverse video (highlight)
|
|
||||||
else ab_sgr(ab, "0;37;40");
|
|
||||||
|
|
||||||
abAppend(ab, "| ", 2);
|
|
||||||
|
|
||||||
// kind tag in color
|
|
||||||
if (!is_sel) ab_sgr(ab, kind_color(it->kind));
|
|
||||||
abAppend(ab, kind_tag(it->kind), 3);
|
|
||||||
abAppend(ab, " ", 1);
|
|
||||||
if (!is_sel) AB_RESET(ab);
|
|
||||||
if (!is_sel) ab_sgr(ab, "37;40");
|
|
||||||
|
|
||||||
// label — left column, fixed width 20
|
|
||||||
char label_col[21];
|
|
||||||
snprintf(label_col, sizeof(label_col), "%-20s", it->label);
|
|
||||||
if (is_sel) AB_BOLD(ab);
|
|
||||||
abAppend(ab, label_col, 20);
|
|
||||||
if (is_sel) ab_sgr(ab, "22"); // bold off
|
|
||||||
|
|
||||||
abAppend(ab, " ", 1);
|
|
||||||
|
|
||||||
// detail — right column, dimmed
|
|
||||||
if (!is_sel) AB_DIM(ab);
|
|
||||||
char detail_col[14];
|
|
||||||
snprintf(detail_col, sizeof(detail_col), "%-13s", it->detail);
|
|
||||||
abAppend(ab, detail_col, 13);
|
|
||||||
|
|
||||||
AB_RESET(ab);
|
|
||||||
if (is_sel) ab_sgr(ab, "7");
|
|
||||||
else ab_sgr(ab, "37;40");
|
|
||||||
abAppend(ab, " |", 2);
|
|
||||||
AB_RESET(ab);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── scroll indicator on right border if needed ────────────────────────────
|
|
||||||
if (popup->count > COMPLETION_POPUP_H) {
|
|
||||||
int bar_row = popup->origin_y + 1 +
|
|
||||||
(popup->selected * (COMPLETION_POPUP_H - 1))
|
|
||||||
/ (popup->count - 1);
|
|
||||||
ab_move(ab, bar_row, popup->origin_x + box_w - 1);
|
|
||||||
ab_sgr(ab, "0;37;40");
|
|
||||||
abAppend(ab, "█", 3);
|
|
||||||
AB_RESET(ab);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── bottom border ─────────────────────────────────────────────────────────
|
|
||||||
ab_move(ab, popup->origin_y + 1 + visible_count, popup->origin_x);
|
|
||||||
ab_sgr(ab, "0;37;40");
|
|
||||||
abAppend(ab, "└", 3);
|
|
||||||
for (int i = 0; i < box_w - 2; i++) abAppend(ab, "─", 3);
|
|
||||||
abAppend(ab, "┘", 3);
|
|
||||||
AB_RESET(ab);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Handle popup keys ────────────────────────────────────────────────────────
|
|
||||||
// Returns 1 if key was consumed, 0 if editor should handle it normally.
|
|
||||||
|
|
||||||
int lspUiHandleKey(CompletionPopup *popup, int key) {
|
|
||||||
if (!popup->visible) return 0;
|
|
||||||
|
|
||||||
switch (key) {
|
|
||||||
case '\x1b':
|
|
||||||
case '\r': // ESC — dismiss
|
|
||||||
popup->visible = 0;
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
// Arrow up / Ctrl-p
|
|
||||||
case 'A' + 0x80:
|
|
||||||
case 0x10:
|
|
||||||
if (popup->selected > 0) popup->selected--;
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
// Arrow down / Ctrl-n
|
|
||||||
case 'B' + 0x80:
|
|
||||||
case 0x0e:
|
|
||||||
if (popup->selected < popup->count - 1) popup->selected++;
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Any printable key: dismiss popup, let editor handle it
|
|
||||||
popup->visible = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static DiagSeverity diag_worst_on_line(DiagnosticList *diags,
|
|
||||||
int buffer_id, int line) {
|
|
||||||
DiagSeverity worst = 0;
|
|
||||||
for (int i = 0; i < diags->count; i++) {
|
|
||||||
Diagnostic *d = &diags->entries[i];
|
|
||||||
if (d->buffer_id == buffer_id && d->line == line) {
|
|
||||||
if (d->severity > worst) worst = d->severity; // ERROR > WARN > HINT
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return worst;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call this once per screen row, right before you draw the row text.
|
|
||||||
// ab : your append buffer
|
|
||||||
// diags : &E.lsp_diagnostics
|
|
||||||
// buf_id : active buffer id
|
|
||||||
// line : file line number (0-based) for this screen row
|
|
||||||
void lspUiDrawGutter(struct abuf *ab, DiagnosticList *diags,
|
|
||||||
int buf_id, int line) {
|
|
||||||
DiagSeverity s = diag_worst_on_line(diags, buf_id, line);
|
|
||||||
switch (s) {
|
|
||||||
case DIAG_ERROR:
|
|
||||||
ab_sgr(ab, "1;31"); // bold red
|
|
||||||
abAppend(ab, "●", 3);
|
|
||||||
break;
|
|
||||||
case DIAG_WARNING:
|
|
||||||
ab_sgr(ab, "1;33"); // bold yellow
|
|
||||||
abAppend(ab, "◆", 3);
|
|
||||||
break;
|
|
||||||
case DIAG_HINT:
|
|
||||||
ab_sgr(ab, "2;37"); // dim white
|
|
||||||
abAppend(ab, "·", 1);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
abAppend(ab, " ", 1); // no diagnostic
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
AB_RESET(ab);
|
|
||||||
abAppend(ab, " ", 1); // gutter padding
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the diagnostic message for the line the cursor is on, or NULL.
|
|
||||||
const char *lspUiDiagnosticAtCursor(DiagnosticList *diags,
|
|
||||||
int buffer_id, int cursor_line) {
|
|
||||||
for (int i = 0; i < diags->count; i++) {
|
|
||||||
Diagnostic *d = &diags->entries[i];
|
|
||||||
if (d->buffer_id == buffer_id && d->line == cursor_line)
|
|
||||||
return d->message;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Parse clangd JSON → our structs ─────────────────────────────────────────
|
|
||||||
|
|
||||||
void lspParseCompletion(const char *json, CompletionPopup *popup,
|
|
||||||
int screen_x, int screen_y) {
|
|
||||||
popup->count = 0;
|
|
||||||
popup->selected = 0;
|
|
||||||
popup->origin_x = screen_x + 1;
|
|
||||||
popup->origin_y = screen_y + 2; // one row below cursor
|
|
||||||
|
|
||||||
cJSON *root = cJSON_Parse(json);
|
|
||||||
if (!root) return;
|
|
||||||
cJSON *result = cJSON_GetObjectItem(root, "result");
|
|
||||||
if (!result) { cJSON_Delete(root); return; }
|
|
||||||
// result can be a list or {isIncomplete, items:[…]}
|
|
||||||
cJSON *items = cJSON_IsArray(result)
|
|
||||||
? result
|
|
||||||
: cJSON_GetObjectItem(result, "items");
|
|
||||||
if (!items) { cJSON_Delete(root); return; }
|
|
||||||
|
|
||||||
cJSON *item;
|
|
||||||
cJSON_ArrayForEach(item, items) {
|
|
||||||
if (popup->count >= COMPLETION_MAX_ITEMS) break;
|
|
||||||
CompletionItem *ci = &popup->items[popup->count++];
|
|
||||||
|
|
||||||
cJSON *label = cJSON_GetObjectItem(item, "label");
|
|
||||||
cJSON *detail = cJSON_GetObjectItem(item, "detail");
|
|
||||||
cJSON *kind = cJSON_GetObjectItem(item, "kind");
|
|
||||||
|
|
||||||
const char *raw_label = label ? label->valuestring : "";
|
|
||||||
if (raw_label[0] == ' ') raw_label++;
|
|
||||||
|
|
||||||
strncpy(ci->label, raw_label, 127);
|
|
||||||
strncpy(ci->detail, detail ? detail->valuestring : "", 63);
|
|
||||||
|
|
||||||
if (!kind)
|
|
||||||
ci->kind = 0;
|
|
||||||
else
|
|
||||||
ci->kind = kind->valueint;
|
|
||||||
}
|
|
||||||
|
|
||||||
popup->visible = (popup->count > 0);
|
|
||||||
cJSON_Delete(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
void lspParseDiagnostics(const char *json, DiagnosticList *diags,
|
|
||||||
int buffer_id) {
|
|
||||||
cJSON *root = cJSON_Parse(json);
|
|
||||||
if (!root) return;
|
|
||||||
|
|
||||||
cJSON *params = cJSON_GetObjectItem(root, "params");
|
|
||||||
if (!params) { cJSON_Delete(root); return; }
|
|
||||||
cJSON *diag_arr = cJSON_GetObjectItem(params, "diagnostics");
|
|
||||||
if (!diag_arr) { cJSON_Delete(root); return; }
|
|
||||||
|
|
||||||
// Remove old entries for this buffer
|
|
||||||
int w = 0;
|
|
||||||
for (int i = 0; i < diags->count; i++)
|
|
||||||
if (diags->entries[i].buffer_id != buffer_id)
|
|
||||||
diags->entries[w++] = diags->entries[i];
|
|
||||||
diags->count = w;
|
|
||||||
|
|
||||||
cJSON *d;
|
|
||||||
cJSON_ArrayForEach(d, diag_arr) {
|
|
||||||
if (diags->count >= DIAG_MAX) break;
|
|
||||||
Diagnostic *diag = &diags->entries[diags->count++];
|
|
||||||
diag->buffer_id = buffer_id;
|
|
||||||
|
|
||||||
cJSON *range = cJSON_GetObjectItem(d, "range");
|
|
||||||
cJSON *start = cJSON_GetObjectItem(range, "start");
|
|
||||||
cJSON *end_pos = cJSON_GetObjectItem(range, "end");
|
|
||||||
cJSON *sev = cJSON_GetObjectItem(d, "severity");
|
|
||||||
cJSON *msg = cJSON_GetObjectItem(d, "message");
|
|
||||||
|
|
||||||
diag->line = cJSON_GetObjectItem(start, "line")->valueint;
|
|
||||||
diag->col_start = cJSON_GetObjectItem(start, "character")->valueint;
|
|
||||||
diag->col_end = cJSON_GetObjectItem(end_pos, "character")->valueint;
|
|
||||||
diag->severity = sev ? (DiagSeverity)sev->valueint : DIAG_ERROR;
|
|
||||||
|
|
||||||
const char *raw = msg ? msg->valuestring : "";
|
|
||||||
int i = 0;
|
|
||||||
while (raw[i] && raw[i] != '\n' && i < 255)
|
|
||||||
{
|
|
||||||
diag->message[i] = raw[i];
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
diag->message[i] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
cJSON_Delete(root);
|
|
||||||
}
|
|
||||||
+231
-315
@@ -8,148 +8,171 @@
|
|||||||
#include "../include/output.h"
|
#include "../include/output.h"
|
||||||
#include "../include/append_buffer.h"
|
#include "../include/append_buffer.h"
|
||||||
#include "../include/buffer.h"
|
#include "../include/buffer.h"
|
||||||
#include "../include/editor_op.h"
|
|
||||||
#include "../include/data.h"
|
#include "../include/data.h"
|
||||||
#include "../include/define.h"
|
#include "../include/define.h"
|
||||||
#include "../include/row_op.h"
|
#include "../include/row_op.h"
|
||||||
#include "../include/split_screen.h"
|
#include "../include/split_screen.h"
|
||||||
#include "../include/syntax_highlighter.h"
|
#include "../include/syntax_highlighter.h"
|
||||||
#include "../include/terminal.h"
|
|
||||||
#include "../include/lsp_ui.h"
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
|
extern struct editorConfig E;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Renders a single pane with its buffer content
|
* @brief Renders a single pane with its buffer content
|
||||||
*/
|
*/
|
||||||
static void editorDrawPane(struct abuf* ab, EditorPane* pane)
|
static void editorDrawPane(struct abuf *ab, EditorPane *pane) {
|
||||||
{
|
if (pane == NULL || pane->buffer_id < 0)
|
||||||
int file_row;
|
return;
|
||||||
|
|
||||||
|
struct buffer_t *buf = bufferFindById(pane->buffer_id);
|
||||||
|
if (buf == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Draw content for this pane
|
||||||
|
for (int y = 0; y < pane->height; y++) {
|
||||||
|
int file_row = y + pane->row_offset;
|
||||||
|
|
||||||
|
// Position cursor at start of pane row
|
||||||
char pos_buf[32];
|
char pos_buf[32];
|
||||||
int pos_len;
|
int pos_len = snprintf(pos_buf, sizeof(pos_buf), "\x1b[%d;%dH",
|
||||||
int byte_len_to_print;
|
pane->start_row + y + 1, pane->start_col + 1);
|
||||||
int bytes_to_print;
|
abAppend(ab, pos_buf, pos_len);
|
||||||
char* highlighted;
|
|
||||||
|
|
||||||
if (pane == NULL || pane->buffer_id < 0)
|
// Apply background color (6 bytes for RGB format)
|
||||||
return;
|
abAppend(ab, E.theme.BACKGROUND_COLOR, strlen(E.theme.BACKGROUND_COLOR));
|
||||||
|
|
||||||
const struct buffer_t* buf = bufferFindById(pane->buffer_id);
|
int chars_printed = 0;
|
||||||
if (buf == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Draw all pane
|
if (file_row >= buf->numrows) {
|
||||||
for (int y = 0; y < pane->height; y++)
|
// Empty line - show tilde
|
||||||
{
|
if (buf->numrows == 0 && y == pane->height / 3) {
|
||||||
file_row = y + pane->y_offset;
|
char welcome[80];
|
||||||
|
int welcome_len;
|
||||||
|
|
||||||
// Set cursor at start of pane row
|
// Different welcome message for terminal buffers
|
||||||
pos_len = snprintf(pos_buf, sizeof(pos_buf), "\x1b[%d;%dH",
|
if (buf->type == TERMINAL_BUFF) {
|
||||||
pane->origin_y + y + 1, pane->origin_x + 1);
|
welcome_len = snprintf(welcome, sizeof(welcome), "Terminal %d", pane->buffer_id);
|
||||||
abAppend(ab, pos_buf, pos_len);
|
} else {
|
||||||
|
welcome_len = snprintf(welcome, sizeof(welcome), "Buffer %d", pane->buffer_id);
|
||||||
// Apply background color (6 bytes for RGB format)
|
|
||||||
abAppend(ab, E.theme.BACKGROUND_COLOR, (int)strlen(E.theme.BACKGROUND_COLOR));
|
|
||||||
|
|
||||||
|
|
||||||
// pane line is out of buffer
|
|
||||||
if (file_row >= buf->numrows)
|
|
||||||
{
|
|
||||||
// Empty line - show tilde
|
|
||||||
|
|
||||||
abAppend(ab, "~", 1);
|
|
||||||
for (int i = 0;
|
|
||||||
i < pane->width - 1;
|
|
||||||
++i)
|
|
||||||
{
|
|
||||||
abAppend(ab, " ", 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
if (E.constantes.LSP) {
|
|
||||||
lspUiDrawGutter(ab, &E.lsp_diagnostics, pane->buffer_id, file_row);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (welcome_len > pane->width)
|
||||||
|
welcome_len = pane->width;
|
||||||
|
|
||||||
if (buf->filename[strlen(buf->filename) - 1] == 'c' || buf->filename[strlen(buf->filename) - 1] == 'h')
|
int padding = (pane->width - welcome_len) / 2;
|
||||||
{
|
if (padding) {
|
||||||
// Render line with syntax highlighting, constrain to pane width
|
abAppend(ab, "~", 1);
|
||||||
highlighted = highlight_line(
|
chars_printed++;
|
||||||
&buf->row[file_row].chars[pane->x_offset], &byte_len_to_print);
|
padding--;
|
||||||
|
|
||||||
// Print only up to pane width
|
|
||||||
abAppend(ab, highlighted, byte_len_to_print);
|
|
||||||
free(highlighted);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Render basic line
|
|
||||||
bytes_to_print =
|
|
||||||
buf->row[file_row].size - pane->x_offset < pane->width
|
|
||||||
? buf->row[file_row].size - pane->x_offset
|
|
||||||
: pane->width;
|
|
||||||
abAppend(ab, &buf->row[file_row].chars[pane->x_offset], bytes_to_print);
|
|
||||||
}
|
|
||||||
// Fill remaining space with background color to pane width
|
|
||||||
for (int i = 0;
|
|
||||||
i < pane->width - editorRowCharCount(&buf->row[file_row], pane->width) + pane->x_offset;
|
|
||||||
++i)
|
|
||||||
{
|
|
||||||
abAppend(ab, " ", 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
while (padding-- && chars_printed < pane->width) {
|
||||||
|
abAppend(ab, " ", 1);
|
||||||
|
chars_printed++;
|
||||||
|
}
|
||||||
|
abAppend(ab, welcome, welcome_len);
|
||||||
|
chars_printed += welcome_len;
|
||||||
|
} else {
|
||||||
|
abAppend(ab, "~", 1);
|
||||||
|
chars_printed++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Render line with syntax highlighting, constrain to pane width
|
||||||
|
int start_offset = pane->col_offset;
|
||||||
|
int visible_len = buf->row[file_row].rsize - start_offset;
|
||||||
|
if (visible_len < 0)
|
||||||
|
visible_len = 0;
|
||||||
|
if (visible_len > pane->width)
|
||||||
|
visible_len = pane->width;
|
||||||
|
|
||||||
|
if (buf->type == TERMINAL_BUFF) {
|
||||||
|
// Terminal buffer - show prompt for input line
|
||||||
|
if (file_row == buf->numrows - 1) {
|
||||||
|
char prompt[16];
|
||||||
|
int prompt_len = snprintf(prompt, sizeof(prompt), "$ ");
|
||||||
|
abAppend(ab, prompt, prompt_len);
|
||||||
|
chars_printed += prompt_len;
|
||||||
|
|
||||||
|
// Show the command being typed (if any)
|
||||||
|
if (buf->terminal_command != NULL) {
|
||||||
|
int cmd_len = strlen(buf->terminal_command);
|
||||||
|
if (cmd_len > pane->width - prompt_len) {
|
||||||
|
cmd_len = pane->width - prompt_len;
|
||||||
|
}
|
||||||
|
abAppend(ab, buf->terminal_command, cmd_len);
|
||||||
|
chars_printed += cmd_len;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Show terminal output
|
||||||
|
abAppend(ab, &buf->row[file_row].render[start_offset], visible_len);
|
||||||
|
chars_printed = visible_len;
|
||||||
|
}
|
||||||
|
} else if (buf->filename && strlen(buf->filename) > 0 && buf->filename[strlen(buf->filename) - 1] == 'c') {
|
||||||
|
int byte_len_to_print;
|
||||||
|
|
||||||
|
char *highlighted = highlight_line(
|
||||||
|
&buf->row[file_row].render[start_offset], &byte_len_to_print);
|
||||||
|
|
||||||
|
// Print only up to pane width
|
||||||
|
|
||||||
|
abAppend(ab, highlighted, byte_len_to_print);
|
||||||
|
free(highlighted);
|
||||||
|
} else {
|
||||||
|
abAppend(ab, &buf->row[file_row].render[start_offset], buf->row[file_row].rsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
chars_printed = visible_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fill remaining space with background color to pane width
|
||||||
|
abAppend(ab, E.theme.BACKGROUND_COLOR, strlen(E.theme.BACKGROUND_COLOR));
|
||||||
|
while (chars_printed < pane->width) {
|
||||||
|
abAppend(ab, " ", 1);
|
||||||
|
chars_printed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore background color at end of line
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Renders all panes based on current split configuration
|
* @brief Renders all panes based on current split configuration
|
||||||
*/
|
*/
|
||||||
static void editorDrawAllPanes(struct abuf* ab)
|
static void editorDrawAllPanes(struct abuf *ab) {
|
||||||
{
|
ScreenLayout *layout = splitScreenGetLayout();
|
||||||
const ScreenLayout* layout = splitScreenGetLayout();
|
|
||||||
|
|
||||||
if (layout->num_panes == 1)
|
if (layout->num_panes == 1) {
|
||||||
{
|
// Single pane fullscreen
|
||||||
// Single pane fullscreen
|
editorDrawPane(ab, &layout->panes[0]);
|
||||||
editorDrawPane(ab, &layout->panes[0]);
|
} else if (layout->num_panes == 2) {
|
||||||
}
|
// Draw both panes
|
||||||
else if (layout->num_panes == 2)
|
for (int i = 0; i < 2; i++) {
|
||||||
{
|
editorDrawPane(ab, &layout->panes[i]);
|
||||||
// Draw both panes
|
|
||||||
for (int i = 0; i < 2; i++)
|
|
||||||
{
|
|
||||||
editorDrawPane(ab, &layout->panes[i]);
|
|
||||||
|
|
||||||
// Draw pane border/divider if not the last pane
|
// Draw pane border/divider if not the last pane
|
||||||
if (layout->mode == SPLIT_VERTICAL && i == 0)
|
if (layout->mode == SPLIT_VERTICAL && i == 0) {
|
||||||
{
|
// Draw vertical divider
|
||||||
// Draw vertical divider
|
int divider_col = layout->panes[0].width;
|
||||||
int divider_col = layout->panes[0].width;
|
for (int y = 0; y < layout->panes[0].height; y++) {
|
||||||
for (int y = 0; y < layout->panes[0].height; y++)
|
char pos_buf[32];
|
||||||
{
|
snprintf(pos_buf, sizeof(pos_buf), "\x1b[%d;%dH", y + 1, divider_col);
|
||||||
char pos_buf[32];
|
abAppend(ab, pos_buf, strlen(pos_buf));
|
||||||
snprintf(pos_buf, sizeof(pos_buf), "\x1b[%d;%dH", y + 1, divider_col);
|
abAppend(ab, "\x1b[1m|\x1b[0m", 9); // Bold pipe divider
|
||||||
abAppend(ab, pos_buf, (int)strlen(pos_buf));
|
|
||||||
abAppend(ab, "\x1b[1m|\x1b[0m", 9); // Bold pipe divider
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (layout->mode == SPLIT_HORIZONTAL && i == 0)
|
|
||||||
{
|
|
||||||
// Draw horizontal divider
|
|
||||||
int divider_row = layout->panes[0].height;
|
|
||||||
char pos_buf[32];
|
|
||||||
snprintf(pos_buf, sizeof(pos_buf), "\x1b[%d;%dH", divider_row + 1, 1);
|
|
||||||
abAppend(ab, pos_buf, (int)strlen(pos_buf));
|
|
||||||
for (int x = 0; x < E.screencols; x++)
|
|
||||||
{
|
|
||||||
abAppend(ab, "\x1b[1m-\x1b[0m", 9); // Bold dash divider
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else if (layout->mode == SPLIT_HORIZONTAL && i == 0) {
|
||||||
|
// Draw horizontal divider
|
||||||
|
int divider_row = layout->panes[0].height;
|
||||||
|
char pos_buf[32];
|
||||||
|
snprintf(pos_buf, sizeof(pos_buf), "\x1b[%d;%dH", divider_row + 1, 1);
|
||||||
|
abAppend(ab, pos_buf, strlen(pos_buf));
|
||||||
|
for (int x = 0; x < E.screencols; x++) {
|
||||||
|
abAppend(ab, "\x1b[1m-\x1b[0m", 9); // Bold dash divider
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -159,91 +182,35 @@ static void editorDrawAllPanes(struct abuf* ab)
|
|||||||
* @note Updates global editor state E
|
* @note Updates global editor state E
|
||||||
* @see editorRowCxToRx()
|
* @see editorRowCxToRx()
|
||||||
*/
|
*/
|
||||||
void editorScroll()
|
void editorScroll() {
|
||||||
{
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
EditorPane* active = splitScreenGetActivePane();
|
struct buffer_t *buf = bufferFindById(active->buffer_id);
|
||||||
struct buffer_t* buf = bufferFindById(active->buffer_id);
|
active->rx = active->cursor_x;
|
||||||
int rel_x, rel_y;
|
|
||||||
|
|
||||||
// compute relative coordinates
|
fprintf(stderr, "%d %d / %d %d\n", active->cursor_x, active->col_offset,
|
||||||
rel_x = editorRowCharCount(&buf->row[buf->y], buf->x);
|
active->cursor_y, active->row_offset);
|
||||||
appDebug("counting %d\n", rel_x);
|
|
||||||
rel_y = buf->y;
|
|
||||||
|
|
||||||
appDebug("%d %d / %d %d\n", active->cursor_x, active->x_offset,
|
if (active->cursor_x < 0) {
|
||||||
active->cursor_y, active->y_offset);
|
active->cursor_x = 0;
|
||||||
|
active->col_offset = active->col_offset == 0 ? 0 : --active->col_offset;
|
||||||
|
}
|
||||||
|
|
||||||
while (rel_x != active->cursor_x + active->x_offset ||
|
if (active->cursor_y < 0) {
|
||||||
rel_y != active->cursor_y + active->y_offset)
|
active->cursor_y = 0;
|
||||||
{
|
active->row_offset = active->row_offset == 0 ? 0 : --active->row_offset;
|
||||||
if (rel_x < active->cursor_x + active->x_offset)
|
}
|
||||||
{
|
|
||||||
// LEFT
|
|
||||||
if (active->cursor_x == 0 && active->x_offset)
|
|
||||||
{
|
|
||||||
active->x_offset--;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
active->cursor_x--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// RIGHT
|
|
||||||
if (rel_x > active->cursor_x + active->x_offset)
|
|
||||||
{
|
|
||||||
if (active->cursor_x == active->width - 1)
|
|
||||||
{
|
|
||||||
active->x_offset++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
active->cursor_x++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rel_y < active->cursor_y + active->y_offset)
|
if (active->cursor_x == active->width) {
|
||||||
{
|
|
||||||
if (active->cursor_y == 0 && active->y_offset)
|
|
||||||
{
|
|
||||||
active->y_offset--;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
active->cursor_y--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rel_y > active->cursor_y + active->y_offset)
|
active->cursor_x--;
|
||||||
{
|
active->col_offset++;
|
||||||
if (active->cursor_y == active->height - 1)
|
}
|
||||||
{
|
|
||||||
active->y_offset++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
active->cursor_y++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char* basename(char* path)
|
if (active->cursor_y == active->height) {
|
||||||
{
|
|
||||||
int len = (int)strlen(path);
|
|
||||||
|
|
||||||
|
active->cursor_y--;
|
||||||
for (int i = len - 1; i > 0; i--)
|
active->row_offset++;
|
||||||
{
|
}
|
||||||
if (path[i] == '/')
|
|
||||||
{
|
|
||||||
path = path + i + 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -254,60 +221,55 @@ char* basename(char* path)
|
|||||||
* @param ab Pointer to append buffer structure for accumulating output
|
* @param ab Pointer to append buffer structure for accumulating output
|
||||||
* @note Uses ANSI escape codes for color inversion
|
* @note Uses ANSI escape codes for color inversion
|
||||||
*/
|
*/
|
||||||
void editorDrawStatusBar(struct abuf* ab)
|
void editorDrawStatusBar(struct abuf *ab) {
|
||||||
{
|
int len, render_len;
|
||||||
// TO MODIFY
|
char status[80], render_status[80];
|
||||||
int len, render_len;
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
char status[E.screencols], render_status[E.screencols * 4];
|
struct buffer_t *buf = bufferFindById(active->buffer_id);
|
||||||
EditorPane* active = splitScreenGetActivePane();
|
|
||||||
struct buffer_t* buf = bufferFindById(active->buffer_id);
|
|
||||||
|
|
||||||
abAppend(ab, "\x1b[7m", 4); // inverting colors
|
abAppend(ab, "\x1b[7m", 4); // inverting colors
|
||||||
|
|
||||||
const char* mode_str = "";
|
const char *mode_str = "";
|
||||||
ScreenLayout* layout = splitScreenGetLayout();
|
ScreenLayout *layout = splitScreenGetLayout();
|
||||||
if (layout->mode == SPLIT_VERTICAL)
|
if (layout->mode == SPLIT_VERTICAL)
|
||||||
mode_str = " [V-SPLIT]";
|
mode_str = " [V-SPLIT]";
|
||||||
else if (layout->mode == SPLIT_HORIZONTAL)
|
else if (layout->mode == SPLIT_HORIZONTAL)
|
||||||
mode_str = " [H-SPLIT]";
|
mode_str = " [H-SPLIT]";
|
||||||
|
|
||||||
// Build buffer status showing all buffers with dirty indicators
|
// Build buffer status showing all buffers with dirty indicators
|
||||||
char buf_status[1024] = "";
|
char buf_status[200] = "";
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
for (int i = 0; i < E.number_of_buffer; i++)
|
for (int i = 0; i < E.number_of_buffer; i++) {
|
||||||
{
|
struct buffer_t *b = &E.buffers[i];
|
||||||
struct buffer_t* b = &E.buffers[i];
|
char marker = (b->buffer_id == active->buffer_id) ? '>' : ' ';
|
||||||
char marker = (b->buffer_id == active->buffer_id) ? '>' : ' ';
|
char dirty_marker = b->dirty ? '*' : ' ';
|
||||||
char dirty_marker = b->dirty ? '*' : ' ';
|
offset += snprintf(&buf_status[offset], sizeof(buf_status) - offset,
|
||||||
offset += snprintf(&buf_status[offset], sizeof(buf_status) - offset,
|
"%c%d:%s%c ", marker, b->buffer_id,
|
||||||
"%c%d:%s%c ", marker, b->buffer_id,
|
b->filename ? b->filename : "[No Name]", dirty_marker);
|
||||||
b->filename ? basename(b->filename) : "[No Name]", dirty_marker);
|
}
|
||||||
|
|
||||||
|
len = snprintf(status, sizeof(status), "%s%s", buf_status, mode_str);
|
||||||
|
|
||||||
|
render_len = snprintf(render_status, sizeof(render_status), "%d/%d",
|
||||||
|
active->cursor_y + 1, buf->numrows);
|
||||||
|
|
||||||
|
if (len > E.screencols) {
|
||||||
|
len = E.screencols;
|
||||||
|
}
|
||||||
|
|
||||||
|
abAppend(ab, status, len);
|
||||||
|
while (len < E.screencols) {
|
||||||
|
if (E.screencols - len == render_len) {
|
||||||
|
abAppend(ab, render_status, render_len);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
abAppend(ab, " ", 1);
|
||||||
|
++len;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
len = snprintf(status, sizeof(status), "%s%s", buf_status, mode_str);
|
abAppend(ab, "\x1b[m", 3); // normal text mode
|
||||||
|
abAppend(ab, "\r\n", 2);
|
||||||
render_len = snprintf(render_status, sizeof(render_status), "%d/%d",
|
|
||||||
active->cursor_y + 1, buf->numrows);
|
|
||||||
|
|
||||||
if (len > E.screencols)
|
|
||||||
{
|
|
||||||
len = E.screencols;
|
|
||||||
}
|
|
||||||
|
|
||||||
abAppend(ab, status, len);
|
|
||||||
while (len < E.screencols)
|
|
||||||
{
|
|
||||||
if (E.screencols - len == render_len + 1)
|
|
||||||
{
|
|
||||||
abAppend(ab, render_status, render_len);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
abAppend(ab, " ", 1);
|
|
||||||
++len;
|
|
||||||
}
|
|
||||||
|
|
||||||
abAppend(ab, "\x1b[m", 3); // normal text mode
|
|
||||||
abAppend(ab, "\r\n", 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -317,18 +279,15 @@ void editorDrawStatusBar(struct abuf* ab)
|
|||||||
* @param ab Pointer to append buffer structure for accumulating output
|
* @param ab Pointer to append buffer structure for accumulating output
|
||||||
* @note Messages are set by editorSetStatusMessage()
|
* @note Messages are set by editorSetStatusMessage()
|
||||||
*/
|
*/
|
||||||
void editorDrawMessageBar(struct abuf* ab)
|
void editorDrawMessageBar(struct abuf *ab) {
|
||||||
{
|
int msg_len = strlen(E.status_msg);
|
||||||
int msg_len = (int)strlen(E.status_msg);
|
abAppend(ab, ERASE_END_LINE, 3);
|
||||||
abAppend(ab, ERASE_END_LINE, 3);
|
if (msg_len > E.screencols) {
|
||||||
if (msg_len > E.screencols)
|
msg_len = E.screencols;
|
||||||
{
|
}
|
||||||
msg_len = E.screencols;
|
if (msg_len && time(NULL) - E.status_msg_time < 5) {
|
||||||
}
|
abAppend(ab, E.status_msg, msg_len);
|
||||||
if (msg_len && time(NULL) - E.status_msg_time < 5)
|
}
|
||||||
{
|
|
||||||
abAppend(ab, E.status_msg, msg_len);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -342,76 +301,33 @@ void editorDrawMessageBar(struct abuf* ab)
|
|||||||
* @see editorDrawMessageBar()
|
* @see editorDrawMessageBar()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void editorRefreshScreen()
|
void editorRefreshScreen() {
|
||||||
{
|
struct abuf ab = ABUF_INIT;
|
||||||
struct abuf ab = ABUF_INIT;
|
char buf[32];
|
||||||
char buf[32];
|
|
||||||
|
|
||||||
abAppend(&ab, HIDE_CURSOR, 6);
|
abAppend(&ab, HIDE_CURSOR, 6);
|
||||||
abAppend(&ab, CURSOR_TOP_LEFT, 3);
|
abAppend(&ab, CURSOR_TOP_LEFT, 3);
|
||||||
abAppend(&ab, E.theme.BACKGROUND_COLOR,
|
abAppend(&ab, E.theme.BACKGROUND_COLOR,
|
||||||
(int)strlen(E.theme.BACKGROUND_COLOR));
|
strlen(E.theme.BACKGROUND_COLOR)); // RGB background is 12 bytes
|
||||||
|
|
||||||
editorScroll();
|
// Draw all panes
|
||||||
|
editorScroll();
|
||||||
|
editorDrawAllPanes(&ab);
|
||||||
|
|
||||||
|
// Draw status bar and message bar
|
||||||
|
editorDrawStatusBar(&ab);
|
||||||
|
editorDrawMessageBar(&ab);
|
||||||
|
|
||||||
EditorPane* active = splitScreenGetActivePane();
|
// Position cursor in active pane
|
||||||
struct buffer_t* buffer = bufferFindById(active->buffer_id);
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
|
if (active != NULL) {
|
||||||
editorDrawAllPanes(&ab);
|
|
||||||
if (E.constantes.LSP) {
|
|
||||||
|
|
||||||
// ── LSP: draw completion popup every frame while visible ──────────────────
|
|
||||||
appDebug("[REFRESH] lsp_completion.visible=%d\n",
|
|
||||||
E.lsp_completion.visible);
|
|
||||||
while (E.lsp_client->completion_requested && !E.lsp_client->completion_just_arrived);
|
|
||||||
// reset flags
|
|
||||||
E.lsp_client->completion_just_arrived = 0;
|
|
||||||
E.lsp_client->completion_requested = 0;
|
|
||||||
|
|
||||||
// ── LSP: diagnostic for current line in status bar ────────────────────────
|
|
||||||
const char* diag = lspUiDiagnosticAtCursor(
|
|
||||||
&E.lsp_diagnostics,
|
|
||||||
active->buffer_id,
|
|
||||||
buffer->y);
|
|
||||||
if (diag) {
|
|
||||||
char single_line[512];
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
// Copy until newline, \0, or screen width
|
|
||||||
while (diag[i] && diag[i] != '\n' && i < E.screencols - 4)
|
|
||||||
{
|
|
||||||
single_line[i] = diag[i];
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If message was truncated, add ellipsis
|
|
||||||
if (diag[i] != '\0' && diag[i] != '\n')
|
|
||||||
{
|
|
||||||
single_line[i++] = '.';
|
|
||||||
single_line[i++] = '.';
|
|
||||||
single_line[i++] = '.';
|
|
||||||
}
|
|
||||||
single_line[i] = '\0';
|
|
||||||
editorSetStatusMessage("● %s", single_line);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
editorDrawStatusBar(&ab);
|
|
||||||
editorDrawMessageBar(&ab);
|
|
||||||
if (E.constantes.LSP && (E.lsp_client && E.lsp_client->state == LSP_READY))
|
|
||||||
{
|
|
||||||
lspUiDrawCompletion(&ab, &E.lsp_completion);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ── Position cursor (account for gutter width) ────────────────────────────
|
|
||||||
snprintf(buf, sizeof(buf), "\x1b[%d;%dH",
|
snprintf(buf, sizeof(buf), "\x1b[%d;%dH",
|
||||||
active->cursor_y + active->origin_y + 1,
|
active->cursor_y + active->start_row + 1,
|
||||||
active->cursor_x + active->origin_x + 1 + (E.constantes.LSP ? GUTTER_WIDTH : 0));
|
active->cursor_x + active->start_col + 1);
|
||||||
abAppend(&ab, buf, (int)strlen(buf));
|
abAppend(&ab, buf, strlen(buf));
|
||||||
|
}
|
||||||
|
|
||||||
abAppend(&ab, SHOW_CURSOR, 6);
|
abAppend(&ab, SHOW_CURSOR, 6);
|
||||||
write(STDOUT_FILENO, ab.b, ab.len);
|
write(STDOUT_FILENO, ab.b, ab.len);
|
||||||
abFree(&ab);
|
abFree(&ab);
|
||||||
}
|
}
|
||||||
|
|||||||
+142
-2
@@ -1,6 +1,4 @@
|
|||||||
#include "../include/row_op.h"
|
#include "../include/row_op.h"
|
||||||
#include "../include/data.h"
|
|
||||||
#include "../include/utf8.h"
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -8,5 +6,147 @@
|
|||||||
|
|
||||||
extern struct editorConfig E;
|
extern struct editorConfig E;
|
||||||
|
|
||||||
|
int bufferRowCxToRx(frow *row, int cursor_x) {
|
||||||
|
int render_x = 0;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < cursor_x; ++i) {
|
||||||
|
if (row->chars[i] == '\t') {
|
||||||
|
render_x += (E.constantes.TAB_LENGTH - 1) - (render_x % E.constantes.TAB_LENGTH);
|
||||||
|
}
|
||||||
|
render_x++;
|
||||||
|
}
|
||||||
|
return render_x;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bufferRowRxToCx(frow *row, int rx) {
|
||||||
|
int cur_rx = 0;
|
||||||
|
int cx;
|
||||||
|
for (cx = 0; cx < row->size; cx++) {
|
||||||
|
if (row->chars[cx] == '\t')
|
||||||
|
cur_rx += (E.constantes.TAB_LENGTH - 1) - (cur_rx % E.constantes.TAB_LENGTH);
|
||||||
|
cur_rx++;
|
||||||
|
if (cur_rx > rx) return cx;
|
||||||
|
}
|
||||||
|
return cx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn bufferUpdatfrow(frow *row)
|
||||||
|
* \brief Copy content of \p row in \p row->render.
|
||||||
|
* */
|
||||||
|
|
||||||
|
void bufferUpdatfrow(frow *row) {
|
||||||
|
int i, i_render;
|
||||||
|
int tabs = 0;
|
||||||
|
|
||||||
|
// counting number of tabs
|
||||||
|
|
||||||
|
for (i = 0; i < row->size; ++i) {
|
||||||
|
tabs +=
|
||||||
|
(row->chars[i] == '\t'); /**< increment tabs of 1 if chars[i] is one. */
|
||||||
|
}
|
||||||
|
|
||||||
|
free(row->render);
|
||||||
|
row->render = malloc(row->size + tabs * (E.constantes.TAB_LENGTH - 1) +
|
||||||
|
1); /**< Tabs needs E.constantes.TAB_LENGTH chars so E.constantes.TAB_LENGTH - 1
|
||||||
|
more than the first already counted. */
|
||||||
|
|
||||||
|
// end of counting
|
||||||
|
i_render = 0;
|
||||||
|
for (i = 0; i < row->size; ++i) {
|
||||||
|
if (row->chars[i] == '\t') {
|
||||||
|
row->render[i_render++] = ' ';
|
||||||
|
while (i_render % E.constantes.TAB_LENGTH) {
|
||||||
|
row->render[i_render++] =
|
||||||
|
' '; /**< Addind the right amount of spaces for tabs */
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
row->render[i_render++] = row->chars[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
row->render[i_render] = '\0'; // Don't forget the end of string character.
|
||||||
|
row->rsize = i_render;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bufferInsertRow(struct buffer_t *buffer, int at, char *s, size_t len) {
|
||||||
|
|
||||||
|
if (at < 0 || at > buffer->numrows) {
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
frow *tmp = (frow *)realloc(buffer->row, sizeof(frow) * (buffer->numrows + 1));
|
||||||
|
if (!tmp) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buffer->row = tmp;
|
||||||
|
memmove(&buffer->row[at + 1], &buffer->row[at], sizeof(frow) * (buffer->numrows - at));
|
||||||
|
|
||||||
|
buffer->row[at].size = len;
|
||||||
|
buffer->row[at].chars = malloc(len + 1);
|
||||||
|
memcpy(buffer->row[at].chars, s, len);
|
||||||
|
buffer->row[at].chars[len] = '\0';
|
||||||
|
|
||||||
|
buffer->row[at].rsize = 0;
|
||||||
|
buffer->row[at].render = NULL;
|
||||||
|
bufferUpdatfrow(&buffer->row[at]);
|
||||||
|
|
||||||
|
++buffer->numrows;
|
||||||
|
++buffer->dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bufferFrefrow(frow *row) {
|
||||||
|
free(row->render);
|
||||||
|
free(row->chars);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bufferDelRow(struct buffer_t *buffer, int at) {
|
||||||
|
if (at < 0 || at >= buffer->numrows) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bufferFrefrow(&buffer->row[at]);
|
||||||
|
memmove(&buffer->row[at], &buffer->row[at + 1], sizeof(frow) * (buffer->numrows - at - 1));
|
||||||
|
--buffer->numrows;
|
||||||
|
++buffer->dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn bufferRowInsertChar(frow *row, int at, int c)
|
||||||
|
* \param at Index of where we want to insert the char */
|
||||||
|
|
||||||
|
void bufferRowInsertChar(struct buffer_t *buffer, frow *row, int at, int c) {
|
||||||
|
if (buffer->state == READ_ONLY)
|
||||||
|
return;
|
||||||
|
if (at < 0 || at > row->size) {
|
||||||
|
at = row->size;
|
||||||
|
}
|
||||||
|
row->chars = realloc(row->chars, row->size + 2);
|
||||||
|
memmove(&row->chars[at + 1], &row->chars[at], row->size - at + 1);
|
||||||
|
++row->size;
|
||||||
|
row->chars[at] = c;
|
||||||
|
bufferUpdatfrow(row);
|
||||||
|
++buffer->dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bufferRowAppendString(struct buffer_t *buffer, frow *row, char *s, size_t len) {
|
||||||
|
row->chars = realloc(row->chars, row->size + len + 1);
|
||||||
|
memcpy(&row->chars[row->size], s, len);
|
||||||
|
row->size += len;
|
||||||
|
row->chars[row->size] = '\0';
|
||||||
|
bufferUpdatfrow(row);
|
||||||
|
++buffer->dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn bufferRowDelChar(struct bufferConfig *E, frow *frow, int at)
|
||||||
|
* \brief Delete the a char at the chosen position on the given row
|
||||||
|
* \param at Index of the char to delete
|
||||||
|
* \param row Row on operation is made */
|
||||||
|
void bufferRowDelchar(struct buffer_t *buffer, frow *row, int at) {
|
||||||
|
if (at < 0 || at >= row->size) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memmove(&row->chars[at], &row->chars[at + 1], row->size - at);
|
||||||
|
--row->size;
|
||||||
|
bufferUpdatfrow(row);
|
||||||
|
++buffer->dirty;
|
||||||
|
}
|
||||||
|
|||||||
+29
-47
@@ -9,7 +9,6 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
extern struct editorConfig E;
|
extern struct editorConfig E;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -20,23 +19,18 @@ void splitScreenInit(void) {
|
|||||||
E.layout.num_panes = 1;
|
E.layout.num_panes = 1;
|
||||||
E.layout.active_pane = 0;
|
E.layout.active_pane = 0;
|
||||||
|
|
||||||
EditorPane *new_panes = realloc(E.layout.panes, sizeof(EditorPane) * 2);
|
E.layout.panes = malloc(sizeof(EditorPane) * 2);
|
||||||
if (!new_panes) {
|
|
||||||
editorSetStatusMessage("Error: realloc failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
E.layout.panes = new_panes;
|
|
||||||
|
|
||||||
// Initialize single fullscreen pane
|
// Initialize single fullscreen pane
|
||||||
E.layout.panes[0].buffer_id = -1; // No buffer for now
|
E.layout.panes[0].buffer_id = -1; // No buffer for now
|
||||||
E.layout.panes[0].origin_y = 0;
|
E.layout.panes[0].start_row = 0;
|
||||||
E.layout.panes[0].origin_x = 0;
|
E.layout.panes[0].start_col = 0;
|
||||||
E.layout.panes[0].height = E.screenrows - 2; // Leave room for status bar
|
E.layout.panes[0].height = E.screenrows - 2; // Leave room for status bar
|
||||||
E.layout.panes[0].width = E.screencols;
|
E.layout.panes[0].width = E.screencols;
|
||||||
E.layout.panes[0].cursor_x = 0;
|
E.layout.panes[0].cursor_x = 0;
|
||||||
E.layout.panes[0].cursor_y = 0;
|
E.layout.panes[0].cursor_y = 0;
|
||||||
E.layout.panes[0].y_offset = 0;
|
E.layout.panes[0].row_offset = 0;
|
||||||
E.layout.panes[0].x_offset = 0;
|
E.layout.panes[0].col_offset = 0;
|
||||||
E.layout.panes[0].is_active = 1;
|
E.layout.panes[0].is_active = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,37 +48,37 @@ int splitScreenVertical(int buffer_id_left, int buffer_id_right) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// bReallocate panes array
|
// Reallocate panes array
|
||||||
E.layout.panes = realloc(E.layout.panes, sizeof(EditorPane) * 2);
|
E.layout.panes = realloc(E.layout.panes, sizeof(EditorPane) * 2);
|
||||||
E.layout.mode = SPLIT_VERTICAL;
|
E.layout.mode = SPLIT_VERTICAL;
|
||||||
E.layout.num_panes = 2;
|
E.layout.num_panes = 2;
|
||||||
E.layout.active_pane = 0;
|
E.layout.active_pane = 0;
|
||||||
|
|
||||||
int half_width = E.screencols / 2;
|
int half_width = E.screencols / 2;
|
||||||
int pane_height = E.screenrows; // Leave room for status bar
|
int pane_height = E.screenrows - 2; // Leave room for status bar
|
||||||
|
|
||||||
// Left pane
|
// Left pane
|
||||||
E.layout.panes[0].buffer_id = buffer_id_left;
|
E.layout.panes[0].buffer_id = buffer_id_left;
|
||||||
E.layout.panes[0].origin_y = 0;
|
E.layout.panes[0].start_row = 0;
|
||||||
E.layout.panes[0].origin_x = 0;
|
E.layout.panes[0].start_col = 0;
|
||||||
E.layout.panes[0].height = pane_height;
|
E.layout.panes[0].height = pane_height;
|
||||||
E.layout.panes[0].width = half_width;
|
E.layout.panes[0].width = half_width;
|
||||||
E.layout.panes[0].cursor_x = 0;
|
E.layout.panes[0].cursor_x = 0;
|
||||||
E.layout.panes[0].cursor_y = 0;
|
E.layout.panes[0].cursor_y = 0;
|
||||||
E.layout.panes[0].y_offset = 0;
|
E.layout.panes[0].row_offset = 0;
|
||||||
E.layout.panes[0].x_offset = 0;
|
E.layout.panes[0].col_offset = 0;
|
||||||
E.layout.panes[0].is_active = 1;
|
E.layout.panes[0].is_active = 1;
|
||||||
|
|
||||||
// Right pane
|
// Right pane
|
||||||
E.layout.panes[1].buffer_id = buffer_id_right;
|
E.layout.panes[1].buffer_id = buffer_id_right;
|
||||||
E.layout.panes[1].origin_y = 0;
|
E.layout.panes[1].start_row = 0;
|
||||||
E.layout.panes[1].origin_x = half_width;
|
E.layout.panes[1].start_col = half_width;
|
||||||
E.layout.panes[1].height = pane_height;
|
E.layout.panes[1].height = pane_height;
|
||||||
E.layout.panes[1].width = E.screencols - half_width;
|
E.layout.panes[1].width = E.screencols - half_width;
|
||||||
E.layout.panes[1].cursor_x = 0;
|
E.layout.panes[1].cursor_x = 0;
|
||||||
E.layout.panes[1].cursor_y = 0;
|
E.layout.panes[1].cursor_y = 0;
|
||||||
E.layout.panes[1].y_offset = 0;
|
E.layout.panes[1].row_offset = 0;
|
||||||
E.layout.panes[1].x_offset = 0;
|
E.layout.panes[1].col_offset = 0;
|
||||||
E.layout.panes[1].is_active = 0;
|
E.layout.panes[1].is_active = 0;
|
||||||
|
|
||||||
editorSetStatusMessage("Vertical split: %d | %d", buffer_id_left, buffer_id_right);
|
editorSetStatusMessage("Vertical split: %d | %d", buffer_id_left, buffer_id_right);
|
||||||
@@ -105,7 +99,7 @@ int splitScreenHorizontal(int buffer_id_top, int buffer_id_bottom) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// bReallocate panes array
|
// Reallocate panes array
|
||||||
E.layout.panes = realloc(E.layout.panes, sizeof(EditorPane) * 2);
|
E.layout.panes = realloc(E.layout.panes, sizeof(EditorPane) * 2);
|
||||||
E.layout.mode = SPLIT_HORIZONTAL;
|
E.layout.mode = SPLIT_HORIZONTAL;
|
||||||
E.layout.num_panes = 2;
|
E.layout.num_panes = 2;
|
||||||
@@ -115,26 +109,26 @@ int splitScreenHorizontal(int buffer_id_top, int buffer_id_bottom) {
|
|||||||
|
|
||||||
// Top pane
|
// Top pane
|
||||||
E.layout.panes[0].buffer_id = buffer_id_top;
|
E.layout.panes[0].buffer_id = buffer_id_top;
|
||||||
E.layout.panes[0].origin_y = 0;
|
E.layout.panes[0].start_row = 0;
|
||||||
E.layout.panes[0].origin_x = 0;
|
E.layout.panes[0].start_col = 0;
|
||||||
E.layout.panes[0].height = half_height;
|
E.layout.panes[0].height = half_height;
|
||||||
E.layout.panes[0].width = E.screencols;
|
E.layout.panes[0].width = E.screencols;
|
||||||
E.layout.panes[0].cursor_x = 0;
|
E.layout.panes[0].cursor_x = 0;
|
||||||
E.layout.panes[0].cursor_y = 0;
|
E.layout.panes[0].cursor_y = 0;
|
||||||
E.layout.panes[0].y_offset = 0;
|
E.layout.panes[0].row_offset = 0;
|
||||||
E.layout.panes[0].x_offset = 0;
|
E.layout.panes[0].col_offset = 0;
|
||||||
E.layout.panes[0].is_active = 1;
|
E.layout.panes[0].is_active = 1;
|
||||||
|
|
||||||
// Bottom pane
|
// Bottom pane
|
||||||
E.layout.panes[1].buffer_id = buffer_id_bottom;
|
E.layout.panes[1].buffer_id = buffer_id_bottom;
|
||||||
E.layout.panes[1].origin_y = half_height;
|
E.layout.panes[1].start_row = half_height;
|
||||||
E.layout.panes[1].origin_x = 0;
|
E.layout.panes[1].start_col = 0;
|
||||||
E.layout.panes[1].height = E.screenrows - 2 - half_height;
|
E.layout.panes[1].height = E.screenrows - 2 - half_height;
|
||||||
E.layout.panes[1].width = E.screencols;
|
E.layout.panes[1].width = E.screencols;
|
||||||
E.layout.panes[1].cursor_x = 0;
|
E.layout.panes[1].cursor_x = 0;
|
||||||
E.layout.panes[1].cursor_y = 0;
|
E.layout.panes[1].cursor_y = 0;
|
||||||
E.layout.panes[1].x_offset = 0;
|
E.layout.panes[1].row_offset = 0;
|
||||||
E.layout.panes[1].y_offset = 0;
|
E.layout.panes[1].col_offset = 0;
|
||||||
E.layout.panes[1].is_active = 0;
|
E.layout.panes[1].is_active = 0;
|
||||||
|
|
||||||
editorSetStatusMessage("Horizontal split: %d / %d", buffer_id_top, buffer_id_bottom);
|
editorSetStatusMessage("Horizontal split: %d / %d", buffer_id_top, buffer_id_bottom);
|
||||||
@@ -149,9 +143,9 @@ void splitScreenUnify(void) {
|
|||||||
E.layout.num_panes = 1;
|
E.layout.num_panes = 1;
|
||||||
E.layout.active_pane = 0;
|
E.layout.active_pane = 0;
|
||||||
|
|
||||||
E.layout.panes[0].origin_x = 0;
|
E.layout.panes[0].start_row = 0;
|
||||||
E.layout.panes[0].origin_y = 0;
|
E.layout.panes[0].start_col = 0;
|
||||||
E.layout.panes[0].height = E.screenrows;
|
E.layout.panes[0].height = E.screenrows - 2;
|
||||||
E.layout.panes[0].width = E.screencols;
|
E.layout.panes[0].width = E.screencols;
|
||||||
E.layout.panes[0].is_active = 1;
|
E.layout.panes[0].is_active = 1;
|
||||||
|
|
||||||
@@ -198,8 +192,8 @@ int splitScreenSetPaneBuffer(int buffer_id) {
|
|||||||
active->buffer_id = buffer_id;
|
active->buffer_id = buffer_id;
|
||||||
active->cursor_x = 0;
|
active->cursor_x = 0;
|
||||||
active->cursor_y = 0;
|
active->cursor_y = 0;
|
||||||
active->origin_x = 0;
|
active->row_offset = 0;
|
||||||
active->origin_y = 0;
|
active->col_offset = 0;
|
||||||
|
|
||||||
editorSetStatusMessage("Pane %d now showing buffer %d",
|
editorSetStatusMessage("Pane %d now showing buffer %d",
|
||||||
E.layout.active_pane, buffer_id);
|
E.layout.active_pane, buffer_id);
|
||||||
@@ -222,15 +216,3 @@ EditorPane *splitScreenGetActivePane(void) {
|
|||||||
if (E.layout.num_panes == 0) return NULL;
|
if (E.layout.num_panes == 0) return NULL;
|
||||||
return &E.layout.panes[E.layout.active_pane];
|
return &E.layout.panes[E.layout.active_pane];
|
||||||
}
|
}
|
||||||
|
|
||||||
void freeScreenLayout(ScreenLayout *layout) {
|
|
||||||
if (layout) {
|
|
||||||
free(layout->panes);
|
|
||||||
layout->panes = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void freePane(EditorPane *pane)
|
|
||||||
{
|
|
||||||
free(pane);
|
|
||||||
}
|
|
||||||
|
|||||||
+49
-104
@@ -1,16 +1,14 @@
|
|||||||
#include "../include/syntax_highlighter.h"
|
#include "../include/syntax_highlighter.h"
|
||||||
#include "../include/data.h"
|
#include "../include/data.h"
|
||||||
#include "../include/utf8.h"
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
extern struct editorConfig E;
|
extern struct editorConfig E;
|
||||||
|
|
||||||
const char *c_keywords[] = {
|
const char *c_keywords[] = {
|
||||||
"if", "else", "while", "for", "do", "switch",
|
"if", "else", "while", "for", "do", "switch",
|
||||||
"case", "break", "#include", "#define", "#if", "#endif", "#ifndef", "continue", "return",
|
"case", "break", "#include", "#define", "continue", "return",
|
||||||
"goto", "struct", "union", "enum", "typedef", "static",
|
"goto", "struct", "union", "enum", "typedef", "static",
|
||||||
"extern", "const", "volatile", "sizeof", "auto", "register",
|
"extern", "const", "volatile", "sizeof", "auto", "register",
|
||||||
"inline", "restrict", NULL};
|
"inline", "restrict", NULL};
|
||||||
@@ -20,52 +18,11 @@ const char *c_types[] = {"int", "char", "float", "double",
|
|||||||
"void", "long", "short", "unsigned",
|
"void", "long", "short", "unsigned",
|
||||||
"signed", "bool", NULL};
|
"signed", "bool", NULL};
|
||||||
|
|
||||||
// Returns the byte length of the UTF-8 character starting at s.
|
|
||||||
// Never returns 0 for a non-NUL byte, so callers won't infinite-loop.
|
|
||||||
static int utf8_char_len(const char *s)
|
|
||||||
{
|
|
||||||
unsigned char c = (unsigned char)*s;
|
|
||||||
if (c == 0) return 0;
|
|
||||||
if (c < 0x80) return 1;
|
|
||||||
if ((c & 0xE0) == 0xC0) return 2;
|
|
||||||
if ((c & 0xF0) == 0xE0) return 3;
|
|
||||||
if ((c & 0xF8) == 0xF0) return 4;
|
|
||||||
return 1; // continuation byte or invalid — advance 1 to avoid infinite loop
|
|
||||||
}
|
|
||||||
|
|
||||||
int is_word_char(const char *s)
|
|
||||||
{
|
|
||||||
uint32_t cp = utf8Decode(&s);
|
|
||||||
|
|
||||||
if ((cp >= 'a' && cp <= 'z') || (cp >= 'A' && cp <= 'Z') ||
|
|
||||||
(cp >= '0' && cp <= '9') || cp == '_' || cp == '#')
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
if (cp == 0xFFFD) return 0;
|
|
||||||
|
|
||||||
if (cp >= 0x00C0 && cp <= 0x017F) return 1;
|
|
||||||
if (cp >= 0x0370 && cp <= 0x03FF) return 1;
|
|
||||||
if (cp >= 0x0400 && cp <= 0x04FF) return 1;
|
|
||||||
if (cp >= 0x0600 && cp <= 0x06FF) return 1;
|
|
||||||
if (cp >= 0x05D0 && cp <= 0x05EA) return 1;
|
|
||||||
if (cp >= 0x0900 && cp <= 0x097F) return 1;
|
|
||||||
if (cp >= 0x4E00 && cp <= 0x9FFF) return 1;
|
|
||||||
if ((cp >= 0x3040 && cp <= 0x309F) ||
|
|
||||||
(cp >= 0x30A0 && cp <= 0x30FF)) return 1;
|
|
||||||
if (cp >= 0xAC00 && cp <= 0xD7A3) return 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy one full UTF-8 character from src+i into dst+pos, advance both indices.
|
|
||||||
static void copy_utf8_char(char *dst, int *dst_pos, const char *src, int *src_pos)
|
|
||||||
{
|
|
||||||
int len = utf8_char_len(&src[*src_pos]);
|
|
||||||
for (int b = 0; b < len; b++)
|
|
||||||
dst[(*dst_pos)++] = src[(*src_pos)++];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if character is alphanumeric or underscore
|
// Check if character is alphanumeric or underscore
|
||||||
|
int is_word_char(char c) {
|
||||||
|
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
||||||
|
(c >= '0' && c <= '9') || c == '_' || c == '#';
|
||||||
|
}
|
||||||
|
|
||||||
// Check if string is a keyword
|
// Check if string is a keyword
|
||||||
int is_keyword(const char *word) {
|
int is_keyword(const char *word) {
|
||||||
@@ -86,11 +43,11 @@ int is_type(const char *word) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get color code for token type
|
// Get color code for token type
|
||||||
const char *get_color(TokenType type) {
|
const char *get_color(SyntaxTokenType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case TOKEN_KEYWORD:
|
case TOKEN_KEYWORD:
|
||||||
return E.theme.COLOR_KEYWORD;
|
return E.theme.COLOR_KEYWORD;
|
||||||
case TOKEN_TYPE:
|
case TOKEN_DATATYPE:
|
||||||
return E.theme.COLOR_TYPE;
|
return E.theme.COLOR_TYPE;
|
||||||
case TOKEN_STRING:
|
case TOKEN_STRING:
|
||||||
return E.theme.COLOR_STRING;
|
return E.theme.COLOR_STRING;
|
||||||
@@ -108,142 +65,130 @@ int comment_section = 0;
|
|||||||
// Highlight a line of C code and return the highlighted string
|
// Highlight a line of C code and return the highlighted string
|
||||||
// Returns a newly allocated string that must be freed by the caller
|
// Returns a newly allocated string that must be freed by the caller
|
||||||
char *highlight_line(const char *line, int *length) {
|
char *highlight_line(const char *line, int *length) {
|
||||||
// Each byte can expand to at most (color_prefix + 4 bytes + color_reset).
|
char *result = malloc(1024); // Allocate space for result
|
||||||
// Allocate generously based on line length to avoid overflow.
|
|
||||||
int line_len = (int) strlen(line);
|
|
||||||
int buf_size = line_len * 32 + 256;
|
|
||||||
char *result = malloc(buf_size);
|
|
||||||
if (!result) {
|
|
||||||
*length = 0;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
int result_pos = 0;
|
int result_pos = 0;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
while (line[i] != '\0' && line[i] != '\n') {
|
while (line[i] != '\0' && line[i] != '\n') {
|
||||||
// Skip whitespace — copy full UTF-8 char (whitespace is always ASCII,
|
// Skip whitespace
|
||||||
// but using copy_utf8_char keeps the pattern consistent)
|
|
||||||
if (line[i] == ' ' || line[i] == '\t') {
|
if (line[i] == ' ' || line[i] == '\t') {
|
||||||
copy_utf8_char(result, &result_pos, line, &i);
|
result[result_pos++] = line[i++];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (comment_section) {
|
if (comment_section) {
|
||||||
result_pos += snprintf(&result[result_pos], 10, "%s", E.theme.COLOR_COMMENT);
|
result_pos += sprintf(&result[result_pos], "%s", E.theme.COLOR_COMMENT);
|
||||||
while (line[i] != '\0' && line[i] != '\n') {
|
while (line[i] != '\0' && line[i] != '\n') {
|
||||||
if (line[i] == '*' && line[i + 1] == '/') {
|
|
||||||
comment_section = 0;
|
if (line[i] == '*' && line[i + 1] == '/') {
|
||||||
}
|
comment_section = 0;
|
||||||
copy_utf8_char(result, &result_pos, line, &i);
|
}
|
||||||
|
|
||||||
|
result[result_pos++] = line[i++];
|
||||||
}
|
}
|
||||||
result_pos += snprintf(&result[result_pos], 10, "%s", COLOR_RESET);
|
result_pos += sprintf(&result[result_pos], "%s", COLOR_RESET);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle line comments
|
// Handle line comments
|
||||||
if (line[i] == '/' && line[i + 1] == '/') {
|
if (line[i] == '/' && line[i + 1] == '/') {
|
||||||
result_pos += snprintf(&result[result_pos], 10, "%s", E.theme.COLOR_COMMENT);
|
result_pos += sprintf(&result[result_pos], "%s", E.theme.COLOR_COMMENT);
|
||||||
while (line[i] != '\0' && line[i] != '\n') {
|
while (line[i] != '\0' && line[i] != '\n') {
|
||||||
copy_utf8_char(result, &result_pos, line, &i);
|
result[result_pos++] = line[i++];
|
||||||
}
|
}
|
||||||
result_pos += snprintf(&result[result_pos], 10, "%s", COLOR_RESET);
|
result_pos += sprintf(&result[result_pos], "%s", COLOR_RESET);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle block comments
|
// Handle block comments
|
||||||
if (line[i] == '/' && line[i + 1] == '*') {
|
if ((line[i] == '/' && line[i + 1] == '*')) {
|
||||||
comment_section = 1;
|
comment_section = 1;
|
||||||
result_pos += snprintf(&result[result_pos], 10, "%s", E.theme.COLOR_COMMENT);
|
result_pos += sprintf(&result[result_pos], "%s", E.theme.COLOR_COMMENT);
|
||||||
result[result_pos++] = line[i++];
|
result[result_pos++] = line[i++];
|
||||||
result[result_pos++] = line[i++];
|
result[result_pos++] = line[i++];
|
||||||
while (line[i] != '\0') {
|
while (line[i] != '\0') {
|
||||||
if (line[i] == '*' && line[i + 1] == '/') {
|
if (line[i] == '*' && line[i + 1] == '/') {
|
||||||
result[result_pos++] = line[i++];
|
result[result_pos++] = line[i++];
|
||||||
result[result_pos++] = line[i++];
|
result[result_pos++] = line[i++];
|
||||||
comment_section = 0;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
copy_utf8_char(result, &result_pos, line, &i);
|
result[result_pos++] = line[i++];
|
||||||
}
|
}
|
||||||
result_pos += snprintf(&result[result_pos], 10, "%s", COLOR_RESET);
|
result_pos += sprintf(&result[result_pos], "%s", COLOR_RESET);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle strings
|
// Handle strings
|
||||||
if (line[i] == '"') {
|
if (line[i] == '"') {
|
||||||
result_pos += snprintf(&result[result_pos], 10, "%s\"", E.theme.COLOR_STRING);
|
result_pos += sprintf(&result[result_pos], "%s\"", E.theme.COLOR_STRING);
|
||||||
i++;
|
i++;
|
||||||
while (line[i] != '\0' && line[i] != '"') {
|
while (line[i] != '\0' && line[i] != '"') {
|
||||||
if (line[i] == '\\') {
|
if (line[i] == '\\') {
|
||||||
result[result_pos++] = line[i++];
|
result[result_pos++] = line[i++];
|
||||||
copy_utf8_char(result, &result_pos, line, &i);
|
result[result_pos++] = line[i++];
|
||||||
} else {
|
} else {
|
||||||
copy_utf8_char(result, &result_pos, line, &i);
|
result[result_pos++] = line[i++];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (line[i] == '"')
|
if (line[i] == '"')
|
||||||
result[result_pos++] = line[i++];
|
result[result_pos++] = line[i++];
|
||||||
result_pos += snprintf(&result[result_pos], 10, "%s", COLOR_RESET);
|
result_pos += sprintf(&result[result_pos], "%s", COLOR_RESET);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle character literals
|
// Handle character literals
|
||||||
if (line[i] == '\'') {
|
if (line[i] == '\'') {
|
||||||
result_pos += snprintf(&result[result_pos], 10, "%s'", E.theme.COLOR_STRING);
|
result_pos += sprintf(&result[result_pos], "%s'", E.theme.COLOR_STRING);
|
||||||
i++;
|
i++;
|
||||||
while (line[i] != '\0' && line[i] != '\'') {
|
while (line[i] != '\0' && line[i] != '\'') {
|
||||||
if (line[i] == '\\') {
|
if (line[i] == '\\') {
|
||||||
result[result_pos++] = line[i++];
|
result[result_pos++] = line[i++];
|
||||||
copy_utf8_char(result, &result_pos, line, &i);
|
result[result_pos++] = line[i++];
|
||||||
} else {
|
} else {
|
||||||
copy_utf8_char(result, &result_pos, line, &i);
|
result[result_pos++] = line[i++];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (line[i] == '\'')
|
if (line[i] == '\'')
|
||||||
result[result_pos++] = line[i++];
|
result[result_pos++] = line[i++];
|
||||||
result_pos += snprintf(&result[result_pos], 10, "%s", COLOR_RESET);
|
result_pos += sprintf(&result[result_pos], "%s", COLOR_RESET);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle numbers
|
// Handle numbers
|
||||||
if (line[i] >= '0' && line[i] <= '9') {
|
if (line[i] >= '0' && line[i] <= '9') {
|
||||||
result_pos += snprintf(&result[result_pos], 10, "%s", E.theme.COLOR_NUMBER);
|
result_pos += sprintf(&result[result_pos], "%s", E.theme.COLOR_NUMBER);
|
||||||
while (is_word_char(&line[i]) || line[i] == '.') {
|
while (is_word_char(line[i]) || line[i] == '.') {
|
||||||
copy_utf8_char(result, &result_pos, line, &i);
|
result[result_pos++] = line[i++];
|
||||||
}
|
}
|
||||||
result_pos += snprintf(&result[result_pos], 10, "%s", COLOR_RESET);
|
result_pos += sprintf(&result[result_pos], "%s", COLOR_RESET);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle identifiers and keywords
|
// Handle identifiers and keywords
|
||||||
if (is_word_char(&line[i])) {
|
if (is_word_char(line[i])) {
|
||||||
int start = i;
|
int start = i;
|
||||||
// Advance by full UTF-8 character widths, not single bytes
|
while (is_word_char(line[i]))
|
||||||
while (line[i] != '\0' && is_word_char(&line[i]))
|
i++;
|
||||||
i += utf8_char_len(&line[i]);
|
|
||||||
|
|
||||||
char word[256];
|
char word[256];
|
||||||
int word_len = i - start;
|
strncpy(word, &line[start], i - start);
|
||||||
if (word_len >= (int)sizeof(word))
|
word[i - start] = '\0';
|
||||||
word_len = (int)sizeof(word) - 1;
|
|
||||||
strncpy(word, &line[start], word_len);
|
|
||||||
word[word_len] = '\0';
|
|
||||||
|
|
||||||
TokenType type = TOKEN_DEFAULT;
|
SyntaxTokenType type = TOKEN_DEFAULT;
|
||||||
if (is_keyword(word))
|
if (is_keyword(word))
|
||||||
type = TOKEN_KEYWORD;
|
type = TOKEN_KEYWORD;
|
||||||
else if (is_type(word))
|
else if (is_type(word))
|
||||||
type = TOKEN_TYPE;
|
type = TOKEN_DATATYPE;
|
||||||
|
|
||||||
result_pos += snprintf(&result[result_pos], 100, "%s%s%s", get_color(type),
|
result_pos += sprintf(&result[result_pos], "%s%s%s", get_color(type),
|
||||||
word, COLOR_RESET);
|
word, COLOR_RESET);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle operators and other characters (including non-ASCII multi-byte)
|
// Handle operators and other characters
|
||||||
result_pos += snprintf(&result[result_pos], 10, "%s", E.theme.COLOR_DEFAULT);
|
result_pos += sprintf(&result[result_pos], "%s", E.theme.COLOR_DEFAULT);
|
||||||
copy_utf8_char(result, &result_pos, line, &i);
|
result[result_pos++] = line[i++];
|
||||||
result_pos += snprintf(&result[result_pos], 10, "%s", COLOR_RESET);
|
result_pos += sprintf(&result[result_pos], "%s", COLOR_RESET);
|
||||||
}
|
}
|
||||||
|
|
||||||
result[result_pos] = '\0';
|
result[result_pos] = '\0';
|
||||||
|
|||||||
+556
@@ -0,0 +1,556 @@
|
|||||||
|
/* termiWin.c
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Christian Visintin - christian.visintin1997@gmail.com
|
||||||
|
*
|
||||||
|
* This file is part of "termiWin: a termios porting for Windows"
|
||||||
|
*
|
||||||
|
* termiWin is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* termiWin is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with termiWin. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../include/termiWin.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#ifdef __cplusplus > 201711L
|
||||||
|
#define TERMIWIN_MAYBE_UNUSED [[maybe_unused]]
|
||||||
|
#else
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#define TERMIWIN_MAYBE_UNUSED __attribute__((unused))
|
||||||
|
#else
|
||||||
|
#define TERMIWIN_MAYBE_UNUSED
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef struct COM {
|
||||||
|
HANDLE hComm;
|
||||||
|
int fd; //Actually it's completely useless
|
||||||
|
char port[128];
|
||||||
|
} COM;
|
||||||
|
|
||||||
|
DCB SerialParams = { 0 }; //Initializing DCB structure
|
||||||
|
struct COM com;
|
||||||
|
COMMTIMEOUTS timeouts = { 0 }; //Initializing COMMTIMEOUTS structure
|
||||||
|
|
||||||
|
//LOCAL functions
|
||||||
|
|
||||||
|
//nbyte 0->7
|
||||||
|
|
||||||
|
int getByte(tcflag_t flag, int nbyte, int nibble) {
|
||||||
|
|
||||||
|
int byte;
|
||||||
|
if (nibble == 1)
|
||||||
|
byte = (flag >> (8 * (nbyte)) & 0x0f);
|
||||||
|
else
|
||||||
|
byte = (flag >> (8 * (nbyte)) & 0xf0);
|
||||||
|
return byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
//INPUT FUNCTIONS
|
||||||
|
|
||||||
|
enum{
|
||||||
|
i_IXOFF = 0x01,
|
||||||
|
i_IXON = 0x02,
|
||||||
|
i_IXOFF_IXON = 0x03,
|
||||||
|
i_PARMRK = 0x04,
|
||||||
|
i_PARMRK_IXOFF = 0x05,
|
||||||
|
i_PARMRK_IXON = 0x06,
|
||||||
|
i_PARMRK_IXON_IXOFF = 0x07
|
||||||
|
};
|
||||||
|
|
||||||
|
int getIXOptions(tcflag_t flag) {
|
||||||
|
int byte = getByte(flag, 1, 1);
|
||||||
|
|
||||||
|
return byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
//LOCALOPT FUNCTIONS
|
||||||
|
|
||||||
|
enum{
|
||||||
|
l_NOECHO = 0x00,
|
||||||
|
l_ECHO = 0x01,
|
||||||
|
l_ECHO_ECHOE = 0x03,
|
||||||
|
l_ECHO_ECHOK = 0x05,
|
||||||
|
l_ECHO_ECHONL = 0x09,
|
||||||
|
l_ECHO_ECHOE_ECHOK = 0x07,
|
||||||
|
l_ECHO_ECHOE_ECHONL = 0x0b,
|
||||||
|
l_ECHO_ECHOE_ECHOK_ECHONL = 0x0f,
|
||||||
|
l_ECHO_ECHOK_ECHONL = 0x0d,
|
||||||
|
l_ECHOE = 0x02,
|
||||||
|
l_ECHOE_ECHOK = 0x06,
|
||||||
|
l_ECHOE_ECHONL = 0x0a,
|
||||||
|
l_ECHOE_ECHOK_ECHONL = 0x0e,
|
||||||
|
l_ECHOK = 0x04,
|
||||||
|
l_ECHOK_ECHONL = 0x0c,
|
||||||
|
l_ECHONL = 0x08
|
||||||
|
};
|
||||||
|
|
||||||
|
int getEchoOptions(tcflag_t flag) {
|
||||||
|
int byte = getByte(flag, 1, 1);
|
||||||
|
return byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum{
|
||||||
|
l_ICANON = 0x10,
|
||||||
|
l_ICANON_ISIG = 0x50,
|
||||||
|
l_ICANON_IEXTEN = 0x30,
|
||||||
|
l_ICANON_NOFLSH = 0x90,
|
||||||
|
l_ICANON_ISIG_IEXTEN = 0x70,
|
||||||
|
l_ICANON_ISIG_NOFLSH = 0xd0,
|
||||||
|
l_ICANON_IEXTEN_NOFLSH = 0xb0,
|
||||||
|
l_ICANON_ISIG_IEXTEN_NOFLSH = 0xf0,
|
||||||
|
l_ISIG = 0x40,
|
||||||
|
l_ISIG_IEXTEN = 0x60,
|
||||||
|
l_ISIG_NOFLSH = 0xc0,
|
||||||
|
l_ISIG_IEXTEN_NOFLSH = 0xe0,
|
||||||
|
l_IEXTEN = 0x20,
|
||||||
|
l_IEXTEN_NOFLSH = 0xa0,
|
||||||
|
l_NOFLSH = 0x80,
|
||||||
|
};
|
||||||
|
|
||||||
|
int getLocalOptions(tcflag_t flag) {
|
||||||
|
int byte = getByte(flag, 1, 0);
|
||||||
|
return byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum{
|
||||||
|
l_TOSTOP = 0x01
|
||||||
|
};
|
||||||
|
|
||||||
|
int getToStop(tcflag_t flag) {
|
||||||
|
int byte = getByte(flag, 1, 1);
|
||||||
|
return byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
//CONTROLOPT FUNCTIONS
|
||||||
|
|
||||||
|
int getCharSet(tcflag_t flag) {
|
||||||
|
|
||||||
|
//FLAG IS MADE UP OF 8 BYTES, A FLAG IS MADE UP OF A NIBBLE -> 4 BITS, WE NEED TO EXTRACT THE SECOND NIBBLE (1st) FROM THE FIFTH BYTE (6th).
|
||||||
|
int byte = getByte(flag, 1, 1);
|
||||||
|
|
||||||
|
switch (byte) {
|
||||||
|
|
||||||
|
case 0X0:
|
||||||
|
return CS5;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0X4:
|
||||||
|
return CS6;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0X8:
|
||||||
|
return CS7;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0Xc:
|
||||||
|
return CS8;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return CS8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum{
|
||||||
|
c_ALL_ENABLED = 0xd0,
|
||||||
|
c_PAREVEN_CSTOPB = 0x50,
|
||||||
|
c_PAREVEN_NOCSTOPB = 0x40,
|
||||||
|
c_PARODD_NOCSTOPB = 0xc0,
|
||||||
|
c_NOPARENB_CSTOPB = 0x10,
|
||||||
|
c_ALL_DISABLED = 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
int getControlOptions(tcflag_t flag) {
|
||||||
|
int byte = getByte(flag, 1, 0);
|
||||||
|
return byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
//LIBFUNCTIONS
|
||||||
|
|
||||||
|
int tcgetattr(int fd, struct termios* TERMIWIN_MAYBE_UNUSED termios_p) {
|
||||||
|
|
||||||
|
if (fd != com.fd) return -1;
|
||||||
|
int TERMIWIN_MAYBE_UNUSED ret = 0;
|
||||||
|
|
||||||
|
ret = GetCommState(com.hComm, &SerialParams);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tcsetattr(int fd, int TERMIWIN_MAYBE_UNUSED optional_actions, const struct termios* termios_p) {
|
||||||
|
|
||||||
|
if (fd != com.fd) return -1;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
//Store flags into local variables
|
||||||
|
tcflag_t iflag = termios_p->c_iflag;
|
||||||
|
tcflag_t lflag = termios_p->c_lflag;
|
||||||
|
tcflag_t cflag = termios_p->c_cflag;
|
||||||
|
tcflag_t TERMIWIN_MAYBE_UNUSED oflag = termios_p->c_oflag;
|
||||||
|
|
||||||
|
//iflag
|
||||||
|
|
||||||
|
int IX = getIXOptions(iflag);
|
||||||
|
|
||||||
|
if ((IX == i_IXOFF_IXON) || (IX == i_PARMRK_IXON_IXOFF)) {
|
||||||
|
|
||||||
|
SerialParams.fOutX = TRUE;
|
||||||
|
SerialParams.fInX = TRUE;
|
||||||
|
SerialParams.fTXContinueOnXoff = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//lflag
|
||||||
|
int TERMIWIN_MAYBE_UNUSED EchoOpt = getEchoOptions(lflag);
|
||||||
|
int TERMIWIN_MAYBE_UNUSED l_opt = getLocalOptions(lflag);
|
||||||
|
int TERMIWIN_MAYBE_UNUSED tostop = getToStop(lflag);
|
||||||
|
|
||||||
|
//Missing parameters...
|
||||||
|
|
||||||
|
//cflags
|
||||||
|
|
||||||
|
int CharSet = getCharSet(cflag);
|
||||||
|
int c_opt = getControlOptions(cflag);
|
||||||
|
|
||||||
|
switch (CharSet) {
|
||||||
|
|
||||||
|
case CS5:
|
||||||
|
SerialParams.ByteSize = 5;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CS6:
|
||||||
|
SerialParams.ByteSize = 6;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CS7:
|
||||||
|
SerialParams.ByteSize = 7;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CS8:
|
||||||
|
SerialParams.ByteSize = 8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (c_opt) {
|
||||||
|
|
||||||
|
case c_ALL_ENABLED:
|
||||||
|
SerialParams.Parity = ODDPARITY;
|
||||||
|
SerialParams.StopBits = TWOSTOPBITS;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case c_ALL_DISABLED:
|
||||||
|
SerialParams.Parity = NOPARITY;
|
||||||
|
SerialParams.StopBits = ONESTOPBIT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case c_PAREVEN_CSTOPB:
|
||||||
|
SerialParams.Parity = EVENPARITY;
|
||||||
|
SerialParams.StopBits = TWOSTOPBITS;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case c_PAREVEN_NOCSTOPB:
|
||||||
|
SerialParams.Parity = EVENPARITY;
|
||||||
|
SerialParams.StopBits = ONESTOPBIT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case c_PARODD_NOCSTOPB:
|
||||||
|
SerialParams.Parity = ODDPARITY;
|
||||||
|
SerialParams.StopBits = ONESTOPBIT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case c_NOPARENB_CSTOPB:
|
||||||
|
SerialParams.Parity = NOPARITY;
|
||||||
|
SerialParams.StopBits = TWOSTOPBITS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//aflags
|
||||||
|
|
||||||
|
/*
|
||||||
|
int OP;
|
||||||
|
if(oflag == OPOST)
|
||||||
|
else ...
|
||||||
|
*/
|
||||||
|
//Missing parameters...
|
||||||
|
|
||||||
|
//special characters
|
||||||
|
|
||||||
|
if (termios_p->c_cc[VEOF] != 0) SerialParams.EofChar = (char)termios_p->c_cc[VEOF];
|
||||||
|
if (termios_p->c_cc[VINTR] != 0) SerialParams.EvtChar = (char)termios_p->c_cc[VINTR];
|
||||||
|
|
||||||
|
if (termios_p->c_cc[VMIN] == 1) { //Blocking
|
||||||
|
|
||||||
|
timeouts.ReadIntervalTimeout = 0; // in milliseconds
|
||||||
|
timeouts.ReadTotalTimeoutConstant = 0; // in milliseconds
|
||||||
|
timeouts.ReadTotalTimeoutMultiplier = 0; // in milliseconds
|
||||||
|
timeouts.WriteTotalTimeoutConstant = 0; // in milliseconds
|
||||||
|
timeouts.WriteTotalTimeoutMultiplier = 0; // in milliseconds
|
||||||
|
|
||||||
|
} else { //Non blocking
|
||||||
|
|
||||||
|
timeouts.ReadIntervalTimeout = termios_p->c_cc[VTIME] * 100; // in milliseconds
|
||||||
|
timeouts.ReadTotalTimeoutConstant = termios_p->c_cc[VTIME] * 100; // in milliseconds
|
||||||
|
timeouts.ReadTotalTimeoutMultiplier = termios_p->c_cc[VTIME] * 100; // in milliseconds
|
||||||
|
timeouts.WriteTotalTimeoutConstant = termios_p->c_cc[VTIME] * 100; // in milliseconds
|
||||||
|
timeouts.WriteTotalTimeoutMultiplier = termios_p->c_cc[VTIME] * 100; // in milliseconds
|
||||||
|
}
|
||||||
|
|
||||||
|
SetCommTimeouts(com.hComm, &timeouts);
|
||||||
|
|
||||||
|
//EOF
|
||||||
|
|
||||||
|
ret = SetCommState(com.hComm, &SerialParams);
|
||||||
|
if (ret != 0)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tcsendbreak(int fd, int TERMIWIN_MAYBE_UNUSED duration) {
|
||||||
|
|
||||||
|
if (fd != com.fd) return -1;
|
||||||
|
|
||||||
|
int ret = 0;
|
||||||
|
ret = TransmitCommChar(com.hComm, '\x00');
|
||||||
|
if (ret != 0)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tcdrain(int fd) {
|
||||||
|
|
||||||
|
if (fd != com.fd) return -1;
|
||||||
|
return FlushFileBuffers(com.hComm);
|
||||||
|
}
|
||||||
|
|
||||||
|
int tcflush(int fd, int queue_selector) {
|
||||||
|
|
||||||
|
if (fd != com.fd) return -1;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
switch (queue_selector) {
|
||||||
|
|
||||||
|
case TCIFLUSH:
|
||||||
|
rc = PurgeComm(com.hComm, PURGE_RXCLEAR);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TCOFLUSH:
|
||||||
|
rc = PurgeComm(com.hComm, PURGE_TXCLEAR);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TCIOFLUSH:
|
||||||
|
rc = PurgeComm(com.hComm, PURGE_RXCLEAR);
|
||||||
|
rc *= PurgeComm(com.hComm, PURGE_TXCLEAR);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
rc = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc != 0)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tcflow(int fd, int action) {
|
||||||
|
|
||||||
|
if (fd != com.fd) return -1;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
|
||||||
|
case TCOOFF:
|
||||||
|
rc = PurgeComm(com.hComm, PURGE_TXABORT);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TCOON:
|
||||||
|
rc = ClearCommBreak(com.hComm);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TCIOFF:
|
||||||
|
rc = PurgeComm(com.hComm, PURGE_RXABORT);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TCION:
|
||||||
|
rc = ClearCommBreak(com.hComm);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
rc = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc != 0)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cfmakeraw(struct termios* TERMIWIN_MAYBE_UNUSED termios_p) {
|
||||||
|
|
||||||
|
SerialParams.ByteSize = 8;
|
||||||
|
SerialParams.StopBits = ONESTOPBIT;
|
||||||
|
SerialParams.Parity = NOPARITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
speed_t cfgetispeed(const struct termios* TERMIWIN_MAYBE_UNUSED termios_p) {
|
||||||
|
|
||||||
|
return SerialParams.BaudRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
speed_t cfgetospeed(const struct termios* TERMIWIN_MAYBE_UNUSED termios_p) {
|
||||||
|
|
||||||
|
return SerialParams.BaudRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cfsetispeed(struct termios* TERMIWIN_MAYBE_UNUSED termios_p, speed_t speed) {
|
||||||
|
|
||||||
|
SerialParams.BaudRate = speed;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cfsetospeed(struct termios* TERMIWIN_MAYBE_UNUSED termios_p, speed_t speed) {
|
||||||
|
|
||||||
|
SerialParams.BaudRate = speed;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cfsetspeed(struct termios* TERMIWIN_MAYBE_UNUSED termios_p, speed_t speed) {
|
||||||
|
|
||||||
|
SerialParams.BaudRate = speed;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t read_serial(int fd, void* buffer, size_t count) {
|
||||||
|
|
||||||
|
if (fd != com.fd) return -1;
|
||||||
|
int rc = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = ReadFile(com.hComm, buffer, count, &rc, NULL);
|
||||||
|
|
||||||
|
if (ret == 0)
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t write_serial(int fd, const void* buffer, size_t count) {
|
||||||
|
|
||||||
|
if (fd != com.fd) return -1;
|
||||||
|
int rc = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = WriteFile(com.hComm, buffer, count, &rc, NULL);
|
||||||
|
|
||||||
|
if (ret == 0)
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int open_serial(const char* portname, int opt) {
|
||||||
|
|
||||||
|
if (strlen(portname) < 4) return -1;
|
||||||
|
|
||||||
|
// Set to zero
|
||||||
|
memset(com.port, 0x00, 128);
|
||||||
|
|
||||||
|
//COMxx
|
||||||
|
size_t portSize = 0;
|
||||||
|
if (strlen(portname) > 4) {
|
||||||
|
portSize = sizeof(char) * strlen("\\\\.\\COM10") + 1;
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
strncat_s(com.port, portSize, "\\\\.\\", strlen("\\\\.\\"));
|
||||||
|
#else
|
||||||
|
strncat(com.port, "\\\\.\\", strlen("\\\\.\\"));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
//COMx
|
||||||
|
else {
|
||||||
|
portSize = sizeof(char) * 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
strncat_s(com.port, portSize, portname, 4);
|
||||||
|
#else
|
||||||
|
strncat(com.port, portname, 4);
|
||||||
|
#endif
|
||||||
|
com.port[portSize] = 0x00;
|
||||||
|
|
||||||
|
switch (opt) {
|
||||||
|
|
||||||
|
case O_RDWR:
|
||||||
|
com.hComm = CreateFile(com.port, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case O_RDONLY:
|
||||||
|
com.hComm = CreateFile(com.port, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case O_WRONLY:
|
||||||
|
com.hComm = CreateFile(com.port, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (com.hComm == INVALID_HANDLE_VALUE) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
com.fd = atoi(portname + 3); // COMx and COMxx
|
||||||
|
SerialParams.DCBlength = sizeof(SerialParams);
|
||||||
|
return com.fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
int close_serial(int TERMIWIN_MAYBE_UNUSED fd) {
|
||||||
|
|
||||||
|
int ret = CloseHandle(com.hComm);
|
||||||
|
if (ret != 0)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int select_serial(int TERMIWIN_MAYBE_UNUSED nfds, fd_set* readfds, fd_set* TERMIWIN_MAYBE_UNUSED writefds, fd_set* TERMIWIN_MAYBE_UNUSED exceptfds, struct timeval* TERMIWIN_MAYBE_UNUSED timeout) {
|
||||||
|
|
||||||
|
SetCommMask(com.hComm, EV_RXCHAR);
|
||||||
|
DWORD dwEventMask;
|
||||||
|
if (WaitCommEvent(com.hComm, &dwEventMask, NULL) == 0) {
|
||||||
|
return -1; // Return -1 if failed
|
||||||
|
}
|
||||||
|
if (dwEventMask == EV_RXCHAR) {
|
||||||
|
return com.fd;
|
||||||
|
} else {
|
||||||
|
if (readfds) {
|
||||||
|
// Clear file descriptor if event is not RXCHAR
|
||||||
|
FD_CLR(com.fd, readfds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// NOTE: write event not detectable!
|
||||||
|
// NOTE: no timeout
|
||||||
|
return 0; // No data
|
||||||
|
}
|
||||||
|
|
||||||
|
//Returns hComm from the COM structure
|
||||||
|
HANDLE getHandle() {
|
||||||
|
return com.hComm;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
+128
-286
@@ -1,321 +1,163 @@
|
|||||||
#include "../include/terminal.h"
|
#include "../include/terminal.h"
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
#include "../include/data.h"
|
#include "../include/data.h"
|
||||||
#include "../include/define.h"
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "../include/buffer.h"
|
#ifdef _WIN32
|
||||||
#include "../include/split_screen.h"
|
#include <windows.h>
|
||||||
#include "../include/utf8.h"
|
#endif
|
||||||
|
|
||||||
void die(const char* s)
|
void die(const char *s) {
|
||||||
{
|
write(STDOUT_FILENO, "\x1b[2J", 4);
|
||||||
write(STDOUT_FILENO, "\x1b[2J", 4);
|
write(STDOUT_FILENO, CURSOR_TOP_LEFT, 3);
|
||||||
write(STDOUT_FILENO, CURSOR_TOP_LEFT, 3);
|
lisp_shutdown(E.ctx);
|
||||||
lisp_shutdown(E.ctx);
|
perror(s);
|
||||||
perror(s);
|
exit(1);
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void disableRawMode()
|
void disableRawMode() {
|
||||||
{
|
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &E.orig_termios) == -1) {
|
||||||
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &E.orig_termios) == -1)
|
die("tcsetattr");
|
||||||
{
|
}
|
||||||
die("tcsetattr");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void enableRawMode()
|
void enableRawMode() {
|
||||||
{
|
if (tcgetattr(STDIN_FILENO, &E.orig_termios) == -1) {
|
||||||
if (tcgetattr(STDIN_FILENO, &E.orig_termios) == -1)
|
die("tcgetattr");
|
||||||
{
|
}
|
||||||
die("tcgetattr");
|
|
||||||
}
|
|
||||||
|
|
||||||
struct termios raw = E.orig_termios;
|
struct termios raw = E.orig_termios;
|
||||||
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
|
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
|
||||||
raw.c_oflag &= ~(OPOST);
|
raw.c_oflag &= ~(OPOST);
|
||||||
raw.c_cflag |= (CS8);
|
raw.c_cflag |= (CS8);
|
||||||
raw.c_lflag &= ~(ECHO | ICANON | ISIG | IEXTEN);
|
raw.c_lflag &= ~(ECHO | ICANON | ISIG | IEXTEN);
|
||||||
raw.c_cc[VMIN] = 0;
|
raw.c_cc[VMIN] = 0;
|
||||||
raw.c_cc[VTIME] = 1;
|
raw.c_cc[VTIME] = 1;
|
||||||
|
|
||||||
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1)
|
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) {
|
||||||
{
|
die("tcgetattr");
|
||||||
die("tcgetattr");
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <ctype.h> /* isprint */
|
int editorReadKey() {
|
||||||
|
int nread;
|
||||||
|
char c;
|
||||||
|
char seq[3];
|
||||||
|
while ((nread = read(STDIN_FILENO, &c, 1)) != 1) {
|
||||||
|
if (nread == -1 && errno != EAGAIN) {
|
||||||
|
die("read");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
char* keyToString(int key)
|
if (c == '\x1b') {
|
||||||
{
|
if (read(STDIN_FILENO, &seq[0], 1) != 1 ||
|
||||||
static char key_str[32];
|
read(STDIN_FILENO, &seq[1], 1) != 1) {
|
||||||
|
return '\x1b';
|
||||||
if (key == '\r')
|
|
||||||
{
|
|
||||||
strcpy(key_str, "ENTER");
|
|
||||||
}
|
}
|
||||||
else if (key == 0x09)
|
if (seq[0] == '[') {
|
||||||
{
|
if (seq[1] >= '0' && seq[1] <= '9') {
|
||||||
strcpy(key_str, "TAB");
|
if (read(STDIN_FILENO, &seq[2], 1) != 1) {
|
||||||
}
|
return '\x1b';
|
||||||
else if (key >= 1 && key <= 26)
|
|
||||||
{
|
|
||||||
snprintf(key_str, sizeof(key_str), "CTRL-%c", 'a' + key - 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
switch (key)
|
|
||||||
{
|
|
||||||
case ARROW_UP:
|
|
||||||
strcpy(key_str, "ARROW-UP");
|
|
||||||
break;
|
|
||||||
case ARROW_DOWN:
|
|
||||||
strcpy(key_str, "ARROW-DOWN");
|
|
||||||
break;
|
|
||||||
case ARROW_LEFT:
|
|
||||||
strcpy(key_str, "ARROW-LEFT");
|
|
||||||
break;
|
|
||||||
case ARROW_RIGHT:
|
|
||||||
strcpy(key_str, "ARROW-RIGHT");
|
|
||||||
break;
|
|
||||||
case PAGE_UP:
|
|
||||||
strcpy(key_str, "PAGE-UP");
|
|
||||||
break;
|
|
||||||
case PAGE_DOWN:
|
|
||||||
strcpy(key_str, "PAGE-DOWN");
|
|
||||||
break;
|
|
||||||
case DEL_KEY:
|
|
||||||
strcpy(key_str, "DEL");
|
|
||||||
break;
|
|
||||||
case BACKSPACE:
|
|
||||||
strcpy(key_str, "BACKSPACE");
|
|
||||||
break;
|
|
||||||
case BEG_LINE:
|
|
||||||
strcpy(key_str, "HOME");
|
|
||||||
break;
|
|
||||||
case END_LINE:
|
|
||||||
strcpy(key_str, "END");
|
|
||||||
break;
|
|
||||||
case '\x1b':
|
|
||||||
strcpy(key_str, "ESCAPE");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (key > 127)
|
|
||||||
{
|
|
||||||
/* UTF-8 code point — re-encode into the buffer */
|
|
||||||
char buf[5] = {0};
|
|
||||||
int n = utf8Encode((uint32_t)key, buf);
|
|
||||||
snprintf(key_str, sizeof(key_str), "%.*s", n, buf);
|
|
||||||
}
|
|
||||||
else if (isprint(key))
|
|
||||||
{
|
|
||||||
snprintf(key_str, sizeof(key_str), "%c", key);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
snprintf(key_str, sizeof(key_str), "KEY-%d", key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (seq[2] == '~') {
|
||||||
|
switch (seq[1]) {
|
||||||
|
case '1':
|
||||||
|
return BEG_LINE;
|
||||||
|
case '3':
|
||||||
|
return DEL_KEY;
|
||||||
|
case '4':
|
||||||
|
return END_LINE;
|
||||||
|
case '5':
|
||||||
|
return PAGE_UP;
|
||||||
|
case '6':
|
||||||
|
return PAGE_DOWN;
|
||||||
|
case '7':
|
||||||
|
return BEG_LINE;
|
||||||
|
case '8':
|
||||||
|
return END_LINE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
switch (seq[1]) {
|
||||||
|
case 'A':
|
||||||
|
return ARROW_UP;
|
||||||
|
case 'B':
|
||||||
|
return ARROW_DOWN;
|
||||||
|
case 'C':
|
||||||
|
return ARROW_RIGHT;
|
||||||
|
case 'D':
|
||||||
|
return ARROW_LEFT;
|
||||||
|
case 'H':
|
||||||
|
return BEG_LINE;
|
||||||
|
case 'F':
|
||||||
|
return END_LINE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (seq[0] == 'O') {
|
||||||
|
switch (seq[1]) {
|
||||||
|
case 'H':
|
||||||
|
return BEG_LINE;
|
||||||
|
case 'F':
|
||||||
|
return END_LINE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return key_str;
|
return '\x1b';
|
||||||
|
} else {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int editorReadKey()
|
int getCursorPosition(int *rows, int *cols) {
|
||||||
{
|
char buf[32];
|
||||||
char c;
|
unsigned int i = 0;
|
||||||
int nread;
|
|
||||||
|
|
||||||
while (1)
|
if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4) {
|
||||||
{
|
return -1;
|
||||||
fd_set fds;
|
}
|
||||||
FD_ZERO(&fds);
|
|
||||||
FD_SET(STDIN_FILENO, &fds);
|
|
||||||
|
|
||||||
int max_fd = STDIN_FILENO;
|
while (i < sizeof(buf) - 1) {
|
||||||
|
if (read(STDIN_FILENO, &buf[i], 1) != 1) {
|
||||||
// Only watch wake pipe if LSP is ready AND active buffer is open
|
break;
|
||||||
EditorPane* active = splitScreenGetActivePane();
|
|
||||||
struct buffer_t* buf = active ? bufferFindById(active->buffer_id) : NULL;
|
|
||||||
int lsp_active = E.lsp_client
|
|
||||||
&& E.lsp_client->wake_pipe[0] > 0
|
|
||||||
&& E.lsp_client->state == LSP_READY
|
|
||||||
&& buf
|
|
||||||
&& buf->b_lsp_open; // ← only if this buffer is tracked
|
|
||||||
|
|
||||||
if (lsp_active)
|
|
||||||
{
|
|
||||||
FD_SET(E.lsp_client->wake_pipe[0], &fds);
|
|
||||||
if (E.lsp_client->wake_pipe[0] > max_fd)
|
|
||||||
max_fd = E.lsp_client->wake_pipe[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
int ready = select(max_fd + 1, &fds, NULL, NULL, NULL);
|
|
||||||
if (ready <= 0) continue;
|
|
||||||
|
|
||||||
if (lsp_active && FD_ISSET(E.lsp_client->wake_pipe[0], &fds))
|
|
||||||
{
|
|
||||||
char tmp[16];
|
|
||||||
read(E.lsp_client->wake_pipe[0], tmp, sizeof(tmp));
|
|
||||||
return LSP_WAKE_KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FD_ISSET(STDIN_FILENO, &fds))
|
|
||||||
{
|
|
||||||
nread = read(STDIN_FILENO, &c, 1);
|
|
||||||
if (nread == 1) break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (buf[i] == 'R') {
|
||||||
|
break;
|
||||||
appDebug("f : %hhu %ld\r\n", c, 0x200);
|
|
||||||
|
|
||||||
if (c == '\x1b')
|
|
||||||
{
|
|
||||||
char seq[6] = {0};
|
|
||||||
/* try to read escape sequence */
|
|
||||||
if (read(STDIN_FILENO, &seq[0], 1) != 1)
|
|
||||||
return '\x1b';
|
|
||||||
if (read(STDIN_FILENO, &seq[1], 1) != 1)
|
|
||||||
return '\x1b';
|
|
||||||
appDebug("f2 : %s\r\n", seq);
|
|
||||||
|
|
||||||
if (seq[0] == '[')
|
|
||||||
{
|
|
||||||
if (seq[1] >= '0' && seq[1] <= '9')
|
|
||||||
{
|
|
||||||
if (read(STDIN_FILENO, &seq[2], 1) != 1)
|
|
||||||
return '\x1b';
|
|
||||||
if (seq[2] == '~')
|
|
||||||
{
|
|
||||||
switch (seq[1])
|
|
||||||
{
|
|
||||||
case '1':
|
|
||||||
return BEG_LINE;
|
|
||||||
case '3':
|
|
||||||
return DEL_KEY;
|
|
||||||
case '4':
|
|
||||||
return END_LINE;
|
|
||||||
case '5':
|
|
||||||
return PAGE_UP;
|
|
||||||
case '6':
|
|
||||||
return PAGE_DOWN;
|
|
||||||
case '7':
|
|
||||||
return BEG_LINE;
|
|
||||||
case '8':
|
|
||||||
return END_LINE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
switch (seq[1])
|
|
||||||
{
|
|
||||||
case 'A':
|
|
||||||
return ARROW_UP;
|
|
||||||
case 'B':
|
|
||||||
return ARROW_DOWN;
|
|
||||||
case 'C':
|
|
||||||
return ARROW_RIGHT;
|
|
||||||
case 'D':
|
|
||||||
return ARROW_LEFT;
|
|
||||||
case 'H':
|
|
||||||
return BEG_LINE;
|
|
||||||
case 'F':
|
|
||||||
return END_LINE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return '\x1b';
|
|
||||||
}
|
}
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
buf[i] = '\0';
|
||||||
|
|
||||||
/* multi-byte UTF-8: read remaining bytes */
|
if (buf[0] != '\x1b' || buf[1] != '[') {
|
||||||
int seqlen = utf8Seqlen((unsigned char)c);
|
return -1;
|
||||||
if (seqlen > 1)
|
}
|
||||||
{
|
if (sscanf(&buf[2], "%d;%d", rows, cols) != 2) {
|
||||||
/* pack into a pseudo-codepoint just to pass bytes through;
|
return -1;
|
||||||
we handle encoding/decoding at the row level */
|
}
|
||||||
char buf[4] = {c, 0, 0, 0};
|
|
||||||
for (int i = 1; i < seqlen; i++)
|
|
||||||
if (read(STDIN_FILENO, &buf[i], 1) != 1)
|
|
||||||
break;
|
|
||||||
/* decode and return as uint32, but we need int — use high range */
|
|
||||||
const char* p = buf;
|
|
||||||
uint32_t cp = utf8Decode(&p);
|
|
||||||
return (int)cp; /* caller re-encodes when inserting */
|
|
||||||
}
|
|
||||||
|
|
||||||
return (unsigned char)c;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getCursorPosition(int* rows, int* cols)
|
int getWindowSize(int *rows, int *cols) {
|
||||||
{
|
#ifdef _WIN32
|
||||||
char buf[32];
|
// Windows implementation
|
||||||
unsigned int i = 0;
|
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||||
|
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
|
||||||
|
*cols = csbi.srWindow.Right - csbi.srWindow.Left + 1;
|
||||||
|
*rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
// Unix implementation
|
||||||
|
struct winsize ws;
|
||||||
|
|
||||||
if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4)
|
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
|
||||||
{
|
if (write(STDOUT_FILENO, "\x1b[999C\x1b[999B", 12) != 12) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
|
||||||
|
|
||||||
while (i < sizeof(buf) - 1)
|
|
||||||
{
|
|
||||||
if (read(STDIN_FILENO, &buf[i], 1) != 1)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (buf[i] == 'R')
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
buf[i] = '\0';
|
|
||||||
|
|
||||||
if (buf[0] != '\x1b' || buf[1] != '[')
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (sscanf(&buf[2], "%d;%d", rows, cols) != 2)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getWindowSize(int* rows, int* cols)
|
|
||||||
{
|
|
||||||
struct winsize ws;
|
|
||||||
|
|
||||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0)
|
|
||||||
{
|
|
||||||
if (write(STDOUT_FILENO, "\x1b[999C\x1b[999B", 12) != 12)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return getCursorPosition(rows, cols);
|
|
||||||
}
|
}
|
||||||
|
return getCursorPosition(rows, cols);
|
||||||
|
} else {
|
||||||
*cols = ws.ws_col;
|
*cols = ws.ws_col;
|
||||||
*rows = ws.ws_row;
|
*rows = ws.ws_row;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void appDebug(const char* fmt, ...)
|
|
||||||
{
|
|
||||||
#ifdef APP_DEBUG
|
|
||||||
va_list ap;
|
|
||||||
char message[1024];
|
|
||||||
va_start(ap, fmt);
|
|
||||||
vsnprintf(message, 1024, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
fprintf(stderr, "%s\n", message);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
-147
@@ -1,147 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file utf8.c
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "../include/utf8.h"
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
|
|
||||||
uint32_t readUtf8Char(void)
|
|
||||||
{
|
|
||||||
unsigned char buf[4];
|
|
||||||
|
|
||||||
read(STDIN_FILENO, &buf[0], 1);
|
|
||||||
|
|
||||||
int extra;
|
|
||||||
uint32_t cp;
|
|
||||||
|
|
||||||
if (buf[0] < 0x80)
|
|
||||||
{
|
|
||||||
cp = buf[0];
|
|
||||||
extra = 0;
|
|
||||||
}
|
|
||||||
else if (buf[0] < 0xC0) { return 0xFFFD; } // stray continuation
|
|
||||||
else if (buf[0] < 0xE0)
|
|
||||||
{
|
|
||||||
cp = buf[0] & 0x1F;
|
|
||||||
extra = 1;
|
|
||||||
}
|
|
||||||
else if (buf[0] < 0xF0)
|
|
||||||
{
|
|
||||||
cp = buf[0] & 0x0F;
|
|
||||||
extra = 2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cp = buf[0] & 0x07;
|
|
||||||
extra = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (extra > 0)
|
|
||||||
{
|
|
||||||
read(STDIN_FILENO, &buf[1], extra); // read remaining bytes at once
|
|
||||||
for (int i = 0; i < extra; i++)
|
|
||||||
cp = (cp << 6) | (buf[1 + i] & 0x3F);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cp;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t utf8Decode(const char** s)
|
|
||||||
{
|
|
||||||
unsigned char c = (unsigned char)**s;
|
|
||||||
uint32_t cp;
|
|
||||||
int extra;
|
|
||||||
if (c < 0x80)
|
|
||||||
{
|
|
||||||
cp = c;
|
|
||||||
extra = 0;
|
|
||||||
}
|
|
||||||
else if (c < 0xC0)
|
|
||||||
{
|
|
||||||
(*s)++;
|
|
||||||
return 0xFFFD;
|
|
||||||
}
|
|
||||||
else if (c < 0xE0)
|
|
||||||
{
|
|
||||||
cp = c & 0x1F;
|
|
||||||
extra = 1;
|
|
||||||
}
|
|
||||||
else if (c < 0xF0)
|
|
||||||
{
|
|
||||||
cp = c & 0x0F;
|
|
||||||
extra = 2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cp = c & 0x07;
|
|
||||||
extra = 3;
|
|
||||||
}
|
|
||||||
(*s)++;
|
|
||||||
while (extra--)
|
|
||||||
{
|
|
||||||
c = (unsigned char)**s;
|
|
||||||
if ((c & 0xC0) != 0x80) return 0xFFFD;
|
|
||||||
cp = (cp << 6) | (c & 0x3F);
|
|
||||||
(*s)++;
|
|
||||||
}
|
|
||||||
return cp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// buf must have at least 4 bytes; returns bytes written
|
|
||||||
int utf8Encode(uint32_t cp, char* buf)
|
|
||||||
{
|
|
||||||
if (cp < 0x80)
|
|
||||||
{
|
|
||||||
buf[0] = cp;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (cp < 0x800)
|
|
||||||
{
|
|
||||||
buf[0] = 0xC0 | (cp >> 6);
|
|
||||||
buf[1] = 0x80 | (cp & 0x3F);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
if (cp < 0x10000)
|
|
||||||
{
|
|
||||||
buf[0] = 0xE0 | (cp >> 12);
|
|
||||||
buf[1] = 0x80 | ((cp >> 6) & 0x3F);
|
|
||||||
buf[2] = 0x80 | (cp & 0x3F);
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
buf[0] = 0xF0 | (cp >> 18);
|
|
||||||
buf[1] = 0x80 | ((cp >> 12) & 0x3F);
|
|
||||||
buf[2] = 0x80 | ((cp >> 6) & 0x3F);
|
|
||||||
buf[3] = 0x80 | (cp & 0x3F);
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
int utf8Seqlen(unsigned char c)
|
|
||||||
{
|
|
||||||
if (c < 0x80) return 1;
|
|
||||||
if (c < 0xC0) return 0; /* continuation — shouldn't be leading */
|
|
||||||
if (c < 0xE0) return 2;
|
|
||||||
if (c < 0xF0) return 3;
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param codepoint utf8 codepoint of a char
|
|
||||||
* @return length of the codepoint
|
|
||||||
*/
|
|
||||||
int codepointWidth(uint32_t codepoint)
|
|
||||||
{
|
|
||||||
if (codepoint < 0x20 || codepoint == 0x7F) return 0;
|
|
||||||
/* rough double-width ranges */
|
|
||||||
if ((codepoint >= 0x1100 && codepoint <= 0x115F) ||
|
|
||||||
(codepoint >= 0x2E80 && codepoint <= 0x303E) ||
|
|
||||||
(codepoint >= 0x3041 && codepoint <= 0x33BF) ||
|
|
||||||
(codepoint >= 0xAC00 && codepoint <= 0xD7AF) ||
|
|
||||||
(codepoint >= 0xF900 && codepoint <= 0xFAFF) ||
|
|
||||||
(codepoint >= 0xFF01 && codepoint <= 0xFF60) ||
|
|
||||||
(codepoint >= 0x1F300 && codepoint <= 0x1FAFF))
|
|
||||||
return 2;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
int main() {
|
|
||||||
printf("hello");
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user