First completion level is working (LSP connected)
This commit is contained in:
+2
-1
@@ -90,4 +90,5 @@
|
||||
(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 "x" editor-auto-complete "user")
|
||||
(map-key "CTRL-h" editor-auto-complete "no-prefix")
|
||||
(map-key "C-x" lsp-complete "user")
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
* @param filename Path to the file
|
||||
* @return Buffer ID on success, -1 on failure
|
||||
*/
|
||||
int bufferCreate(const char *filename, enum bufferStatus_e state);
|
||||
int bufferCreate(const char *path, enum bufferStatus_e state);
|
||||
|
||||
/**
|
||||
* @brief Switches to a specific buffer by ID
|
||||
|
||||
+2
-1
@@ -54,7 +54,8 @@ Lisp editorMoveEndBuffer(Lisp args, LispError *e, LispContext ctx);
|
||||
// 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);
|
||||
|
||||
|
||||
+306
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
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
|
||||
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// Created by Giorgio on 25/05/2026.
|
||||
//
|
||||
|
||||
#ifndef BELUGA_COMPLETION_H
|
||||
#define BELUGA_COMPLETION_H
|
||||
#include "data.h"
|
||||
|
||||
|
||||
|
||||
void createContextBuffer(const int x, const int y, const char * text);
|
||||
|
||||
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
|
||||
@@ -5,8 +5,12 @@
|
||||
#include <termios.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "define.h"
|
||||
|
||||
#include "lisp.h"
|
||||
|
||||
typedef struct lsp_client_t LspClient;
|
||||
|
||||
/**
|
||||
* \struct row_t
|
||||
* \brief Store one editor row
|
||||
@@ -94,15 +98,98 @@ struct keyBind_t {
|
||||
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_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 };
|
||||
|
||||
struct buffer_t {
|
||||
enum buffer_type type;
|
||||
int buffer_id;
|
||||
int b_lsp_open;
|
||||
int x, y; /**< Position in the file */
|
||||
row_t *row;
|
||||
int numrows;
|
||||
int b_has_changed;
|
||||
char *filename;
|
||||
char *path;
|
||||
enum bufferStatus_e state;
|
||||
int dirty; /**< Has this buffer been modified since last save */
|
||||
};
|
||||
@@ -122,6 +209,10 @@ struct editorConfig {
|
||||
|
||||
ContextBuffer* context_buffers;
|
||||
|
||||
LspClient *lsp_client;
|
||||
CompletionPopup lsp_completion;
|
||||
DiagnosticList lsp_diagnostics;
|
||||
|
||||
int dirty;
|
||||
|
||||
char *status_msg;
|
||||
|
||||
@@ -14,6 +14,13 @@
|
||||
#define SPACE "\x20"
|
||||
#define APP_DEBUG
|
||||
|
||||
#define COMPLETION_MAX_ITEMS 16
|
||||
#define COMPLETION_MAX_WIDTH 40
|
||||
#define COMPLETION_POPUP_H 8 // max visible rows
|
||||
#define DIAG_MAX 128
|
||||
|
||||
#define GUTTER_WIDTH 2
|
||||
|
||||
enum editorKey_e {
|
||||
BACKSPACE = 127,
|
||||
ARROW_LEFT = 1000,
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
#ifndef INIT_H_
|
||||
#define INIT_H_
|
||||
|
||||
#include "builtins.h"
|
||||
#include "data.h"
|
||||
#include "terminal.h"
|
||||
#include <stdio.h>
|
||||
|
||||
/**
|
||||
* \fn void initEditor()
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// Created by Giorgio on 28/05/2026.
|
||||
//
|
||||
|
||||
#ifndef BELUGA_UTILS_H
|
||||
#define BELUGA_UTILS_H
|
||||
#include <sys/_types/_size_t.h>
|
||||
|
||||
extern int beluga_alloc_counter;
|
||||
|
||||
|
||||
void * bAlloc(size_t size);
|
||||
void * bRealloc(void * ptr, size_t size);
|
||||
void * bFree(void * ptr);
|
||||
|
||||
#endif //BELUGA_UTILS_H
|
||||
@@ -9,6 +9,8 @@
|
||||
#define _BSD_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "include/utils.h"
|
||||
|
||||
#include "include/buffer.h"
|
||||
#include "include/split_screen.h"
|
||||
#include <string.h>
|
||||
@@ -19,18 +21,36 @@
|
||||
#include "include/input.h"
|
||||
#include "include/editor_op.h"
|
||||
#include "include/terminal.h"
|
||||
#include "include/completion.h"
|
||||
#include <signal.h>
|
||||
|
||||
#include "include/utils.h"
|
||||
|
||||
struct editorConfig E;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
char * splash_screen = (char *) calloc(256, sizeof(char));
|
||||
char * splash_screen = bAlloc(sizeof(char) * 512);
|
||||
|
||||
signal(SIGPIPE, SIG_IGN); // don't die on broken pipe, just get EPIPE from write()
|
||||
|
||||
enableRawMode();
|
||||
initEditor();
|
||||
if (argc >= 2) {
|
||||
EditorPane *active = splitScreenGetActivePane();
|
||||
active->buffer_id = bufferCreate(argv[1], READ_AND_WRITE);
|
||||
|
||||
char project_root[512];
|
||||
realpath(argv[1], project_root);
|
||||
char *slash = strrchr(project_root, '/');
|
||||
if (slash) *slash = '\0';
|
||||
|
||||
appDebug("peoject root : %s\n", project_root);
|
||||
|
||||
lspStart(E.lsp_client, project_root);
|
||||
struct buffer_t *buf = &E.buffers[active->buffer_id];
|
||||
lspDidOpen(E.lsp_client, buf);
|
||||
|
||||
} else {
|
||||
strcat(splash_screen, getenv("HOME"));
|
||||
strcat(splash_screen, "/.beluga/assets/beluga.txt");
|
||||
@@ -38,11 +58,16 @@ int main(int argc, char *argv[]) {
|
||||
appDebug("splash : %s\n", splash_screen);
|
||||
EditorPane *active = splitScreenGetActivePane();
|
||||
active->buffer_id = bufferCreate(splash_screen, READ_ONLY);
|
||||
struct buffer_t *buf = &E.buffers[active->buffer_id];
|
||||
lspStart(E.lsp_client, splash_screen);
|
||||
lspDidOpen(E.lsp_client, buf);
|
||||
|
||||
}
|
||||
free(splash_screen);
|
||||
|
||||
editorSetStatusMessage("HELP: Ctrl-x Ctrl-s = save | Ctrl-x Ctrl-c = quit");
|
||||
|
||||
appDebug("allocation : %d\n", beluga_alloc_counter);
|
||||
|
||||
while (1) {
|
||||
editorRefreshScreen();
|
||||
|
||||
+7
-2
@@ -7,6 +7,7 @@ project('beluga', 'c',
|
||||
|
||||
cc = meson.get_compiler('c')
|
||||
m = cc.find_library('m', required: false)
|
||||
thread_dep = dependency('threads')
|
||||
|
||||
# Source files
|
||||
src_files = files(
|
||||
@@ -23,11 +24,15 @@ src_files = files(
|
||||
'src/builtins.c',
|
||||
'src/buffer.c',
|
||||
'src/split_screen.c',
|
||||
'src/utf8.c'
|
||||
'src/utf8.c',
|
||||
'src/completion.c',
|
||||
'src/lsp_ui.c',
|
||||
'src/cJSON.c',
|
||||
'src/utils.c'
|
||||
)
|
||||
|
||||
# Executable
|
||||
executable('beluga',
|
||||
src_files,
|
||||
dependencies: [m]
|
||||
dependencies: [m, thread_dep]
|
||||
)
|
||||
|
||||
+3
-2
@@ -1,7 +1,8 @@
|
||||
#include "../include/append_buffer.h"
|
||||
#include "include/utils.h"
|
||||
|
||||
void abAppend(struct abuf *ab, const char *s, int len) {
|
||||
char *new = realloc(ab->b, ab->len + len);
|
||||
char *new = bRealloc(ab->b, ab->len + len);
|
||||
|
||||
if (new == NULL) {
|
||||
return;
|
||||
@@ -11,4 +12,4 @@ void abAppend(struct abuf *ab, const char *s, int len) {
|
||||
ab->len += len;
|
||||
}
|
||||
|
||||
void abFree(struct abuf *ab) { free(ab->b); }
|
||||
void abFree(struct abuf *ab) { bFree(ab->b); }
|
||||
|
||||
+20
-16
@@ -10,10 +10,12 @@
|
||||
#include "include/split_screen.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <libgen.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "include/input.h"
|
||||
#include "include/utils.h"
|
||||
|
||||
|
||||
/**
|
||||
@@ -55,18 +57,18 @@ struct buffer_t* bufferFindById(int buffer_id)
|
||||
* @brief Creates a new buffer for a file
|
||||
* @details Allocates buffer slot, loads file content, saves buffer metadata.
|
||||
* If file is already open, switches to existing buffer instead.
|
||||
* @param filename Path to the file
|
||||
* @return Buffer ID on success, -1 on failure
|
||||
*/
|
||||
int bufferCreate(const char* filename, enum bufferStatus_e state)
|
||||
int bufferCreate(const char* path, enum bufferStatus_e state)
|
||||
{
|
||||
char *filename = basename((char *) path);
|
||||
// Check if file is already open
|
||||
const int existing_id = bufferFindByFilename(filename);
|
||||
const int existing_id = bufferFindByFilename(path);
|
||||
path = dirname((char *) path);
|
||||
if (existing_id != -1)
|
||||
{
|
||||
return bufferSwitch(existing_id);
|
||||
}
|
||||
|
||||
// Check if we have space for more buffers
|
||||
if (E.number_of_buffer >= 64)
|
||||
{
|
||||
@@ -83,6 +85,7 @@ int bufferCreate(const char* filename, enum bufferStatus_e state)
|
||||
new_buf->x = 0;
|
||||
new_buf->y = 0;
|
||||
new_buf->dirty = 0; // New file starts clean
|
||||
new_buf->path = strdup(path);
|
||||
|
||||
// Load file content using existing editorOpen
|
||||
editorOpen(new_buf);
|
||||
@@ -161,7 +164,7 @@ int bufferClose(int buffer_id)
|
||||
}
|
||||
|
||||
// Free buffer resources
|
||||
free(buf->filename);
|
||||
bFree(buf->filename);
|
||||
buf->filename = NULL;
|
||||
buf->buffer_id = -1;
|
||||
|
||||
@@ -297,7 +300,7 @@ void bufferFind(struct buffer_t* buf)
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(query);
|
||||
bFree(query);
|
||||
}
|
||||
|
||||
void bufferFindReverse(struct buffer_t* buf)
|
||||
@@ -320,14 +323,14 @@ void bufferFindReverse(struct buffer_t* buf)
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(query);
|
||||
bFree(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));
|
||||
row_t *tmp = bRealloc(buffer->row, sizeof(row_t) * (buffer->numrows + 1));
|
||||
if (!tmp)
|
||||
return;
|
||||
buffer->row = tmp;
|
||||
@@ -338,9 +341,9 @@ void bufferInsertRow(struct buffer_t *buffer, int at, char *s, size_t len) {
|
||||
sizeof(row_t) * (buffer->numrows - at)); /* not -at+1 */
|
||||
}
|
||||
|
||||
buffer->row[at].size = len;
|
||||
buffer->row[at].cap = len + 1;
|
||||
buffer->row[at].chars = malloc(len + 1);
|
||||
buffer->row[at].size = (int) len;
|
||||
buffer->row[at].cap = (int) len + 1;
|
||||
buffer->row[at].chars = bAlloc(len + 1);
|
||||
if (!buffer->row[at].chars)
|
||||
return;
|
||||
memcpy(buffer->row[at].chars, s, len);
|
||||
@@ -350,7 +353,7 @@ void bufferInsertRow(struct buffer_t *buffer, int at, char *s, size_t len) {
|
||||
buffer->dirty++;
|
||||
}
|
||||
|
||||
void bufferFreeRow(row_t *row) { free(row->chars); }
|
||||
void bufferFreeRow(row_t *row) { bFree(row->chars); }
|
||||
|
||||
/**
|
||||
* \fn editorRowInsertChar(erow *row, int at, int c)
|
||||
@@ -362,12 +365,12 @@ void bufferRowInsertBytes(struct buffer_t *buffer, row_t *row, int at,
|
||||
return;
|
||||
if (row->size + n + 1 > row->cap) {
|
||||
row->cap = (row->size + n + 1) * 2;
|
||||
row->chars = realloc(row->chars, row->cap);
|
||||
row->chars = bRealloc(row->chars, row->cap);
|
||||
}
|
||||
memmove(row->chars + at + n, row->chars + at, row->size - at);
|
||||
memcpy(row->chars + at, src, n);
|
||||
row->size += n;
|
||||
row->chars = realloc(row->chars, row->size + 2);
|
||||
row->chars = bRealloc(row->chars, row->size + 2);
|
||||
++buffer->dirty;
|
||||
}
|
||||
|
||||
@@ -400,6 +403,7 @@ void bufferInsertBytes(const char* src, int n)
|
||||
}
|
||||
bufferRowInsertBytes(buf, &buf->row[buf->y], buf->x, src, n);
|
||||
buf->x += n;
|
||||
buf->b_has_changed = 1;
|
||||
}
|
||||
|
||||
void bufferDelBytes(void)
|
||||
@@ -430,7 +434,7 @@ void bufferDelBytes(void)
|
||||
int prev_char_count = editorRowCharCount(prev, prev->size);
|
||||
|
||||
bufferRowInsertBytes(buf, prev, prev->size, r->chars, r->size);
|
||||
free(r->chars);
|
||||
bFree(r->chars);
|
||||
r->chars = NULL;
|
||||
|
||||
memmove(&buf->row[buf->y],
|
||||
@@ -462,7 +466,7 @@ void bufferInsertNewLine(void) {
|
||||
/* 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: realloc inside bufferInsertRow may have moved the array */
|
||||
/* Re-fetch: bRealloc inside bufferInsertRow may have moved the array */
|
||||
row = &buf->row[buf->y];
|
||||
|
||||
/* Truncate the current row at the cursor */
|
||||
|
||||
+154
-67
@@ -20,6 +20,9 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "include/completion.h"
|
||||
#include "include/utils.h"
|
||||
|
||||
/**
|
||||
* @brief Finds a prefix configuration by name
|
||||
* @details Searches the prefix array for a prefix matching the given name.
|
||||
@@ -28,10 +31,13 @@
|
||||
* @return Matching prefix_t structure, or E.prefix[0] if not found
|
||||
* @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;
|
||||
while (i--) {
|
||||
if (!strcmp(prefix_name, E.prefix[i].prefix_name)) {
|
||||
while (i--)
|
||||
{
|
||||
if (!strcmp(prefix_name, E.prefix[i].prefix_name))
|
||||
{
|
||||
return E.prefix[i];
|
||||
}
|
||||
}
|
||||
@@ -51,19 +57,24 @@ struct prefix_t find_prefix(const char prefix_name[64]) {
|
||||
* @return lisp_null()
|
||||
* @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
|
||||
*/
|
||||
const char* key_sequence = lisp_string(lisp_car(args));
|
||||
void* memory_temp;
|
||||
args = lisp_cdr(args);
|
||||
// second argument
|
||||
const Lisp func = lisp_car(args);
|
||||
|
||||
E.key_binds = (struct keyBind_t *)realloc(
|
||||
memory_temp = (void*)bRealloc(
|
||||
E.key_binds, ++E.number_of_keybinds * sizeof(struct keyBind_t));
|
||||
E.key_binds = (struct keyBind_t*)memory_temp;
|
||||
if (!E.key_binds)
|
||||
editorQuit(args, e, ctx);
|
||||
E.key_binds[E.number_of_keybinds - 1].key_sequence =
|
||||
(char *)malloc(50 * sizeof(char));
|
||||
(char*)bAlloc(50 * sizeof(char));
|
||||
|
||||
strncpy(E.key_binds[E.number_of_keybinds - 1].key_sequence, key_sequence, 50);
|
||||
|
||||
@@ -91,10 +102,12 @@ Lisp mapKey(Lisp args, LispError *e, LispContext ctx) {
|
||||
* @note Updates global editor state E
|
||||
* @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));
|
||||
int is_in = 0;
|
||||
switch (direction[0]) {
|
||||
switch (direction[0])
|
||||
{
|
||||
case 'u':
|
||||
is_in = editorMoveCursor(ARROW_UP);
|
||||
break;
|
||||
@@ -107,37 +120,43 @@ Lisp moveCursor(Lisp args, LispError *e, LispContext ctx) {
|
||||
case 'l':
|
||||
is_in = editorMoveCursor(ARROW_LEFT);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
appDebug("move lisp %d\n", is_in);
|
||||
return lisp_make_bool(is_in);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Frees all dynamically allocated editor structures
|
||||
* @brief bFrees all dynamically allocated editor structures
|
||||
* @details Releases memory for prefix table, keybinds, filename, and all rows.
|
||||
* Called during shutdown to prevent memory leaks.
|
||||
* @note Updates global editor state E
|
||||
*/
|
||||
void free_structs(void) {
|
||||
void bFree_structs(void)
|
||||
{
|
||||
int i, j;
|
||||
free(E.prefix);
|
||||
for (i = 0; i < E.number_of_keybinds; ++i) {
|
||||
free(E.key_binds[i].key_sequence);
|
||||
bFree(E.prefix);
|
||||
for (i = 0; i < E.number_of_keybinds; ++i)
|
||||
{
|
||||
bFree(E.key_binds[i].key_sequence);
|
||||
}
|
||||
free(E.key_binds);
|
||||
// free layout
|
||||
free(E.layout.panes);
|
||||
bFree(E.key_binds);
|
||||
// bFree layout
|
||||
bFree(E.layout.panes);
|
||||
|
||||
// Free buffers
|
||||
for (i = 0; i < E.number_of_buffer; ++i) {
|
||||
free(E.buffers[i].filename);
|
||||
for (j = 0; j < E.buffers[i].numrows; ++j) {
|
||||
free(E.buffers[i].row[j].chars);
|
||||
// bFree buffers
|
||||
for (i = 0; i < E.number_of_buffer; ++i)
|
||||
{
|
||||
bFree(E.buffers[i].filename);
|
||||
for (j = 0; j < E.buffers[i].numrows; ++j)
|
||||
{
|
||||
bFree(E.buffers[i].row[j].chars);
|
||||
}
|
||||
free(E.buffers[i].row);
|
||||
bFree(E.buffers[i].row);
|
||||
}
|
||||
|
||||
free(E.init_file_path);
|
||||
bFree(E.init_file_path);
|
||||
fclose(E.fd_init_file);
|
||||
}
|
||||
|
||||
@@ -152,23 +171,25 @@ void free_structs(void) {
|
||||
* @return lisp_null() (never returns on successful exit)
|
||||
* @note Calls exit(0) to terminate program
|
||||
* @note Updates quit_times_buffer counter
|
||||
* @see free_structs()
|
||||
* @see bFree_structs()
|
||||
*/
|
||||
Lisp editorQuit(Lisp args, LispError *e, LispContext ctx) {
|
||||
if (E.dirty && E.quit_times_buffer > 0) {
|
||||
Lisp editorQuit(Lisp args, LispError* e, LispContext ctx)
|
||||
{
|
||||
if (E.dirty && E.quit_times_buffer > 0)
|
||||
{
|
||||
editorSetStatusMessage("WARNING! Changes hasn't been saved. Press Ctrl-Q "
|
||||
"another time to quit.");
|
||||
--E.quit_times_buffer;
|
||||
return lisp_null();
|
||||
}
|
||||
free_structs();
|
||||
bFree_structs();
|
||||
write(STDOUT_FILENO, "\x1b[2J", 4);
|
||||
write(STDOUT_FILENO, CURSOR_TOP_LEFT, 3);
|
||||
disableRawMode();
|
||||
lspShutdown(E.lsp_client);
|
||||
lisp_shutdown(E.ctx);
|
||||
appDebug("Rest alloc %d\n", beluga_alloc_counter);
|
||||
exit(0);
|
||||
|
||||
return lisp_null();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -181,17 +202,21 @@ Lisp editorQuit(Lisp args, LispError *e, LispContext ctx) {
|
||||
* @see editorSave()
|
||||
*/
|
||||
|
||||
Lisp l_editorSplitScreenVertical(Lisp args, LispError *e, LispContext ctx) {
|
||||
if (E.number_of_buffer >= 2) {
|
||||
Lisp l_editorSplitScreenVertical(Lisp args, LispError* e, LispContext ctx)
|
||||
{
|
||||
if (E.number_of_buffer >= 2)
|
||||
{
|
||||
splitScreenVertical(E.buffers[0].buffer_id, E.buffers[1].buffer_id);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
editorSetStatusMessage("Need at least 2 buffers open");
|
||||
}
|
||||
return lisp_null();
|
||||
}
|
||||
|
||||
Lisp l_editorSave(Lisp args, LispError *e, LispContext ctx) {
|
||||
|
||||
Lisp l_editorSave(Lisp args, LispError* e, LispContext ctx)
|
||||
{
|
||||
editorSave();
|
||||
|
||||
return lisp_null();
|
||||
@@ -206,8 +231,8 @@ Lisp l_editorSave(Lisp args, LispError *e, LispContext ctx) {
|
||||
* @return lisp_null()
|
||||
* @see editorInsertNewLine()
|
||||
*/
|
||||
Lisp l_editorInsertNewLine(Lisp args, LispError *e, LispContext ctx) {
|
||||
|
||||
Lisp l_editorInsertNewLine(Lisp args, LispError* e, LispContext ctx)
|
||||
{
|
||||
bufferInsertNewLine();
|
||||
|
||||
return lisp_null();
|
||||
@@ -222,8 +247,10 @@ Lisp l_editorInsertNewLine(Lisp args, LispError *e, LispContext ctx) {
|
||||
* @return lisp_null()
|
||||
* @note Uses E.constantes.TAB_LENGTH for indentation width
|
||||
*/
|
||||
Lisp l_editorInserTab(Lisp args, LispError *e, LispContext ctx) {
|
||||
for (int i = 0; i < E.constantes.TAB_LENGTH; ++i) {
|
||||
Lisp l_editorInserTab(Lisp args, LispError* e, LispContext ctx)
|
||||
{
|
||||
for (int i = 0; i < E.constantes.TAB_LENGTH; ++i)
|
||||
{
|
||||
bufferInsertBytes(" ", 1);
|
||||
}
|
||||
|
||||
@@ -239,7 +266,8 @@ Lisp l_editorInserTab(Lisp args, LispError *e, LispContext ctx) {
|
||||
* @return lisp_null()
|
||||
* @note Updates global editor state E
|
||||
*/
|
||||
Lisp moveCursorBeginLine(Lisp args, LispError *e, LispContext ctx) {
|
||||
Lisp moveCursorBeginLine(Lisp args, LispError* e, LispContext ctx)
|
||||
{
|
||||
EditorPane* active = splitScreenGetActivePane();
|
||||
struct buffer_t* buf = bufferFindById(active->buffer_id);
|
||||
buf->x = 0;
|
||||
@@ -255,7 +283,8 @@ Lisp moveCursorBeginLine(Lisp args, LispError *e, LispContext ctx) {
|
||||
* @return lisp_null()
|
||||
* @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();
|
||||
struct buffer_t* buf = bufferFindById(active->buffer_id);
|
||||
buf->x = buf->row[buf->y].size;
|
||||
@@ -271,7 +300,8 @@ Lisp moveCursorEndLine(Lisp args, LispError *e, LispContext ctx) {
|
||||
* @return lisp_null()
|
||||
* @see editorDelChar()
|
||||
*/
|
||||
Lisp deletePreviousChar(Lisp args, LispError *e, LispContext ctx) {
|
||||
Lisp deletePreviousChar(Lisp args, LispError* e, LispContext ctx)
|
||||
{
|
||||
bufferDelBytes();
|
||||
return lisp_null();
|
||||
}
|
||||
@@ -287,11 +317,13 @@ Lisp deletePreviousChar(Lisp args, LispError *e, LispContext ctx) {
|
||||
* @note Updates global editor state E
|
||||
* @see editorMoveCursor()
|
||||
*/
|
||||
Lisp editorMoveCursorPageUp(Lisp args, LispError *e, LispContext ctx) {
|
||||
Lisp editorMoveCursorPageUp(Lisp args, LispError* e, LispContext ctx)
|
||||
{
|
||||
EditorPane* active = splitScreenGetActivePane();
|
||||
active->cursor_y = active->y_offset;
|
||||
int times = E.screenrows;
|
||||
while (--times) {
|
||||
while (--times)
|
||||
{
|
||||
editorMoveCursor(ARROW_UP);
|
||||
}
|
||||
return lisp_null();
|
||||
@@ -308,15 +340,18 @@ Lisp editorMoveCursorPageUp(Lisp args, LispError *e, LispContext ctx) {
|
||||
* @note Updates global editor state E
|
||||
* @see editorMoveCursor()
|
||||
*/
|
||||
Lisp editorMoveCursorPageDown(Lisp args, LispError *e, LispContext ctx) {
|
||||
Lisp editorMoveCursorPageDown(Lisp args, LispError* e, LispContext ctx)
|
||||
{
|
||||
EditorPane* active = splitScreenGetActivePane();
|
||||
struct buffer_t* buffer = bufferFindById(active->buffer_id);
|
||||
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;
|
||||
}
|
||||
int times = E.screenrows;
|
||||
while (--times) {
|
||||
while (--times)
|
||||
{
|
||||
editorMoveCursor(ARROW_DOWN);
|
||||
}
|
||||
|
||||
@@ -334,14 +369,16 @@ Lisp editorMoveCursorPageDown(Lisp args, LispError *e, LispContext ctx) {
|
||||
* @see editorOpen()
|
||||
* @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);
|
||||
if (filename) {
|
||||
if (filename)
|
||||
{
|
||||
// editorOpen(filename);
|
||||
EditorPane* active = splitScreenGetActivePane();
|
||||
active->buffer_id = bufferCreate(filename, READ_AND_WRITE);
|
||||
}
|
||||
free(filename);
|
||||
bFree(filename);
|
||||
|
||||
return lisp_null();
|
||||
}
|
||||
@@ -356,9 +393,10 @@ Lisp editorOpenFile(Lisp args, LispError *e, LispContext ctx) {
|
||||
* @return lisp_null()
|
||||
* @note Uses first character of the string argument
|
||||
*/
|
||||
Lisp editorPrintC(Lisp args, LispError *e, LispContext ctx) {
|
||||
Lisp editorPrintC(Lisp args, LispError* e, LispContext ctx)
|
||||
{
|
||||
const char* src = lisp_string(lisp_car(args));
|
||||
bufferInsertBytes(src, strlen(src));
|
||||
bufferInsertBytes(src, (int) strlen(src));
|
||||
return lisp_null();
|
||||
}
|
||||
|
||||
@@ -373,7 +411,8 @@ Lisp editorPrintC(Lisp args, LispError *e, LispContext ctx) {
|
||||
* @note Package files must be valid Lisp code
|
||||
* @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));
|
||||
appDebug("%s\n", package_name);
|
||||
char* package_dir = (char*)calloc(256, sizeof(char));
|
||||
@@ -387,14 +426,16 @@ Lisp addPackage(Lisp args, LispError *e, LispContext ctx) {
|
||||
lisp_eval(lisp_read_file(fd_package, &E.ctx_error, E.ctx), &E.ctx_error,
|
||||
E.ctx);
|
||||
fclose(fd_package);
|
||||
free(package_dir);
|
||||
bFree(package_dir);
|
||||
|
||||
return lisp_null();
|
||||
}
|
||||
|
||||
Lisp editorSwitchNextBuffer(Lisp args, LispError *e, LispContext ctx) {
|
||||
Lisp editorSwitchNextBuffer(Lisp args, LispError* e, LispContext ctx)
|
||||
{
|
||||
appDebug("switch buffer\n");
|
||||
if (E.number_of_buffer > 0) {
|
||||
if (E.number_of_buffer > 0)
|
||||
{
|
||||
EditorPane* active = splitScreenGetActivePane();
|
||||
int next_idx = (active->buffer_id + 1) % E.number_of_buffer;
|
||||
active->buffer_id = next_idx;
|
||||
@@ -403,13 +444,16 @@ Lisp editorSwitchNextBuffer(Lisp args, LispError *e, LispContext ctx) {
|
||||
return lisp_null();
|
||||
}
|
||||
|
||||
Lisp editorSwitchNextPane(Lisp args, LispError *e, LispContext ctx) {
|
||||
Lisp editorSwitchNextPane(Lisp args, LispError* e, LispContext ctx)
|
||||
{
|
||||
splitScreenSwitchPane();
|
||||
return lisp_null();
|
||||
}
|
||||
|
||||
Lisp editorUnifiedPanes(Lisp args, LispError *e, LispContext ctx) {
|
||||
if (E.layout.num_panes - 1) {
|
||||
Lisp editorUnifiedPanes(Lisp args, LispError* e, LispContext ctx)
|
||||
{
|
||||
if (E.layout.num_panes - 1)
|
||||
{
|
||||
splitScreenUnify();
|
||||
}
|
||||
return lisp_null();
|
||||
@@ -424,7 +468,8 @@ Lisp editorUnifiedPanes(Lisp args, LispError *e, LispContext ctx) {
|
||||
* @return lisp_null()
|
||||
* @see editorFind()
|
||||
*/
|
||||
Lisp bufferFind_L(Lisp args, LispError *e, LispContext ctx) {
|
||||
Lisp bufferFind_L(Lisp args, LispError* e, LispContext ctx)
|
||||
{
|
||||
appDebug("LispFind\n");
|
||||
EditorPane* active = splitScreenGetActivePane();
|
||||
struct buffer_t* buffer = bufferFindById(active->buffer_id);
|
||||
@@ -441,7 +486,8 @@ Lisp bufferFind_L(Lisp args, LispError *e, LispContext ctx) {
|
||||
* @return lisp_null()
|
||||
* @see editorFind()
|
||||
*/
|
||||
Lisp bufferFindReverse_L(Lisp args, LispError *e, LispContext ctx) {
|
||||
Lisp bufferFindReverse_L(Lisp args, LispError* e, LispContext ctx)
|
||||
{
|
||||
appDebug("LispFind\n");
|
||||
EditorPane* active = splitScreenGetActivePane();
|
||||
struct buffer_t* buffer = bufferFindById(active->buffer_id);
|
||||
@@ -458,13 +504,17 @@ Lisp bufferFindReverse_L(Lisp args, LispError *e, LispContext ctx) {
|
||||
* @param ctx Lisp context
|
||||
* @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;
|
||||
EditorPane* active = splitScreenGetActivePane();
|
||||
struct buffer_t* buffer = bufferFindById(active->buffer_id);
|
||||
if (buffer->row[buffer->y].chars[buffer->x] == 0) {
|
||||
if (buffer->row[buffer->y].chars[buffer->x] == 0)
|
||||
{
|
||||
returned_char = lisp_make_char('a');
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
returned_char = lisp_make_char(buffer->row[buffer->y].chars[buffer->x]);
|
||||
}
|
||||
return returned_char;
|
||||
@@ -481,7 +531,8 @@ Lisp editorReadChar_L(Lisp args, LispError *e, LispContext ctx) {
|
||||
* @note Updates global editor state E (prefix_state)
|
||||
* @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
|
||||
*/
|
||||
@@ -504,8 +555,10 @@ Lisp editorSetPrefix(Lisp args, LispError *e, LispContext ctx) {
|
||||
* @return lisp_null()
|
||||
* @note Updates global editor state E (prefix array)
|
||||
*/
|
||||
Lisp editorPrefix(Lisp args, LispError *e, LispContext ctx) {
|
||||
E.prefix = (struct prefix_t *)realloc(E.prefix, (++(E.number_of_prefix) + 1) *
|
||||
Lisp editorPrefix(Lisp args, LispError* e, LispContext ctx)
|
||||
{
|
||||
void * memory_temp;
|
||||
E.prefix = (struct prefix_t*)bRealloc(E.prefix, (++(E.number_of_prefix) + 1) *
|
||||
sizeof(struct prefix_t));
|
||||
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)),
|
||||
@@ -525,7 +578,9 @@ Lisp editorCutEndLine(Lisp args, LispError *e, LispContext ctx)
|
||||
{
|
||||
EditorPane* active = splitScreenGetActivePane();
|
||||
struct buffer_t* buffer = bufferFindById(active->buffer_id);
|
||||
int bytes_to_delete = buffer->row[buffer->y].size - buffer->x;
|
||||
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--)
|
||||
@@ -556,5 +611,37 @@ Lisp editorMoveEndBuffer(Lisp args, LispError *e, LispContext ctx)
|
||||
Lisp editorAutoComplete(Lisp args, LispError* e, LispContext ctx)
|
||||
{
|
||||
// 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,
|
||||
active->cursor_y + active->y_offset, // file line
|
||||
active->cursor_x + active->x_offset, // 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)
|
||||
{
|
||||
(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();
|
||||
}
|
||||
|
||||
+3206
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,555 @@
|
||||
//
|
||||
// Created by Giorgio on 25/05/2026.
|
||||
//
|
||||
|
||||
#include "../include/completion.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/syslimits.h>
|
||||
|
||||
#include "include/append_buffer.h"
|
||||
#include "include/cJSON.h"
|
||||
#include "include/data.h"
|
||||
#include "include/lsp_ui.h"
|
||||
#include "include/terminal.h"
|
||||
#include "include/utils.h"
|
||||
|
||||
void createContextBuffer(const int x, const int y, const char* text)
|
||||
{
|
||||
E.context_buffers = bAlloc(sizeof(ContextBuffer));
|
||||
ContextBuffer* buffer = E.context_buffers;
|
||||
buffer->editor_x = x;
|
||||
buffer->editor_y = y;
|
||||
buffer->height = 1;
|
||||
buffer->rows = bAlloc(sizeof(struct row));
|
||||
if (!buffer->rows) return;
|
||||
buffer->rows[0].chars = strdup(text);
|
||||
buffer->rows[0].size = strlen(text);
|
||||
buffer->width = strlen(text);
|
||||
}
|
||||
|
||||
static void lsp_send(int fd, 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 in two writes, no dprintf mixing
|
||||
write(fd, header, header_len);
|
||||
write(fd, json, body_len);
|
||||
|
||||
// Log to stderr for debugging
|
||||
fprintf(stderr, "[LSP →] Content-Length: %d | %s\n", body_len, json);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
static int lsp_uri_to_buffer_id(const char* uri)
|
||||
{
|
||||
const char *path = uri;
|
||||
if (strncmp(uri, "file://", 7) == 0)
|
||||
path = uri + 7;
|
||||
// path is now "/absolute/path" — realpath output matches this directly
|
||||
|
||||
for (int i = 0; i < E.number_of_buffer; i++) {
|
||||
if (E.buffers[i].filename == NULL) continue;
|
||||
char abs[PATH_MAX];
|
||||
realpath(E.buffers[i].filename, abs);
|
||||
fprintf(stderr, "[URI MATCH] comparing '%s' vs '%s'\n", abs, path);
|
||||
if (strcmp(abs, path) == 0)
|
||||
return E.buffers[i].buffer_id;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static char* lsp_recv(int fd)
|
||||
{
|
||||
char header[1024];
|
||||
int content_length = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int i = 0;
|
||||
char c;
|
||||
int n;
|
||||
// Read until \n
|
||||
while ((n = read(fd, &c, 1)) == 1 && c != '\n')
|
||||
header[i++] = c;
|
||||
if (n <= 0) return NULL;
|
||||
header[i] = '\0';
|
||||
// Strip \r explicitly
|
||||
while (i > 0 && (header[i - 1] == '\r' || header[i - 1] == ' '))
|
||||
header[--i] = '\0';
|
||||
if (i == 0) break; // blank line = end of headers
|
||||
if (strncmp(header, "Content-Length: ", 16) == 0)
|
||||
content_length = atoi(header + 16);
|
||||
}
|
||||
|
||||
if (content_length == 0) return NULL;
|
||||
|
||||
char* body = bAlloc(content_length + 1);
|
||||
int total = 0;
|
||||
while (total < content_length)
|
||||
{
|
||||
int n = read(fd, body + total, content_length - total);
|
||||
if (n <= 0)
|
||||
{
|
||||
bFree(body);
|
||||
return NULL;
|
||||
}
|
||||
total += n;
|
||||
}
|
||||
body[content_length] = '\0';
|
||||
return body;
|
||||
}
|
||||
|
||||
static void lsp_dispatch(LspClient* lsp, const char* json)
|
||||
{
|
||||
if (!json) return;
|
||||
|
||||
cJSON* root = cJSON_Parse(json);
|
||||
if (!root)
|
||||
{
|
||||
fprintf(stderr, "[LSP ←] Failed to parse JSON: %.120s\n", json);
|
||||
return;
|
||||
}
|
||||
|
||||
cJSON* method = cJSON_GetObjectItem(root, "method");
|
||||
cJSON* id = cJSON_GetObjectItem(root, "id");
|
||||
cJSON* result = cJSON_GetObjectItem(root, "result");
|
||||
cJSON* error = cJSON_GetObjectItem(root, "error");
|
||||
|
||||
// ── Error response ────────────────────────────────────────────────────────
|
||||
if (error)
|
||||
{
|
||||
cJSON* msg = cJSON_GetObjectItem(error, "message");
|
||||
fprintf(stderr, "[LSP ←] ERROR: %s\n",
|
||||
msg ? msg->valuestring : "(no message)");
|
||||
cJSON_Delete(root);
|
||||
return;
|
||||
}
|
||||
|
||||
// ── Notification (no id, has method) ─────────────────────────────────────
|
||||
if (method && !id)
|
||||
{
|
||||
const char* m = method->valuestring;
|
||||
fprintf(stderr, "[LSP ←] NOTIF: %s\n", m);
|
||||
|
||||
if (strcmp(m, "textDocument/publishDiagnostics") == 0)
|
||||
{
|
||||
// Find which buffer this diagnostic belongs to
|
||||
cJSON* params = cJSON_GetObjectItem(root, "params");
|
||||
cJSON* uri = cJSON_GetObjectItem(params, "uri");
|
||||
int buf_id = lsp_uri_to_buffer_id(
|
||||
uri ? uri->valuestring : "");
|
||||
|
||||
fprintf(stderr, "[LSP ←] Diagnostics for buffer %d\n", buf_id);
|
||||
|
||||
pthread_mutex_lock(&lsp->lock);
|
||||
lspParseDiagnostics(json, &E.lsp_diagnostics, buf_id);
|
||||
pthread_mutex_unlock(&lsp->lock);
|
||||
write(lsp->wake_pipe[1], "d", 1);
|
||||
}
|
||||
|
||||
else if (strcmp(m, "window/logMessage") == 0)
|
||||
{
|
||||
cJSON* params = cJSON_GetObjectItem(root, "params");
|
||||
cJSON* message = cJSON_GetObjectItem(params, "message");
|
||||
fprintf(stderr, "[LSP ←] LOG: %s\n",
|
||||
message ? message->valuestring : "");
|
||||
}
|
||||
E.lsp_client->completion_just_arrived = 1;
|
||||
|
||||
|
||||
cJSON_Delete(root);
|
||||
return;
|
||||
}
|
||||
|
||||
// ── Response (has id + result) ────────────────────────────────────────────
|
||||
if (id && result)
|
||||
{
|
||||
int response_id = id->valueint;
|
||||
fprintf(stderr, "[LSP ←] RESPONSE id=%d\n", response_id);
|
||||
|
||||
// initialize response → send initialized + mark ready
|
||||
if (lsp->state == LSP_INITIALIZING)
|
||||
{
|
||||
fprintf(stderr, "[LSP ←] Initialize OK, sending initialized\n");
|
||||
lsp_send(lsp->write_fd,
|
||||
"{\"jsonrpc\":\"2.0\",\"method\":\"initialized\",\"params\":{}}");
|
||||
|
||||
pthread_mutex_lock(&lsp->lock);
|
||||
lsp->state = LSP_READY;
|
||||
pthread_cond_signal(&lsp->ready_cond);
|
||||
pthread_mutex_unlock(&lsp->lock);
|
||||
E.lsp_client->completion_just_arrived = 1;
|
||||
|
||||
cJSON_Delete(root);
|
||||
return;
|
||||
}
|
||||
|
||||
// completion response → parse items and show popup
|
||||
cJSON* items = cJSON_GetObjectItem(result, "items");
|
||||
if (items && cJSON_IsArray(items))
|
||||
{
|
||||
int count = cJSON_GetArraySize(items);
|
||||
fprintf(stderr, "[LSP ←] Completion: %d items\n", count);
|
||||
|
||||
// Print each item to stderr for debugging
|
||||
cJSON* item;
|
||||
int i = 0;
|
||||
cJSON_ArrayForEach(item, items)
|
||||
{
|
||||
cJSON* label = cJSON_GetObjectItem(item, "label");
|
||||
cJSON* detail = cJSON_GetObjectItem(item, "detail");
|
||||
cJSON* kind = cJSON_GetObjectItem(item, "kind");
|
||||
fprintf(stderr, " [%d] kind=%-2d %-40s %s\n",
|
||||
i++,
|
||||
kind ? kind->valueint : 0,
|
||||
label ? label->valuestring : "(no label)",
|
||||
detail ? detail->valuestring : "");
|
||||
if (i >= 10)
|
||||
{
|
||||
fprintf(stderr, " ... (%d more)\n", count - 10);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&lsp->lock);
|
||||
lspParseCompletion(json, &E.lsp_completion,
|
||||
lsp->completion_cursor_x,
|
||||
lsp->completion_cursor_y);
|
||||
pthread_mutex_unlock(&lsp->lock);
|
||||
E.lsp_client->completion_just_arrived = 1;
|
||||
|
||||
fprintf(stderr, "[POPUP] visible=%d count=%d origin=(%d,%d)\n",
|
||||
E.lsp_completion.visible,
|
||||
E.lsp_completion.count,
|
||||
E.lsp_completion.origin_x,
|
||||
E.lsp_completion.origin_y);
|
||||
write(lsp->wake_pipe[1], "c", 1);
|
||||
|
||||
cJSON_Delete(root);
|
||||
return;
|
||||
}
|
||||
|
||||
// definition response → jump to location
|
||||
cJSON* uri_item = cJSON_GetObjectItem(result, "uri");
|
||||
if (uri_item)
|
||||
{
|
||||
cJSON* range = cJSON_GetObjectItem(result, "range");
|
||||
cJSON* start = cJSON_GetObjectItem(range, "start");
|
||||
int line = cJSON_GetObjectItem(start, "line")->valueint;
|
||||
int col = cJSON_GetObjectItem(start, "character")->valueint;
|
||||
fprintf(stderr, "[LSP ←] Definition: %s:%d:%d\n",
|
||||
uri_item->valuestring, line, col);
|
||||
E.lsp_client->completion_just_arrived = 1;
|
||||
|
||||
// TODO: jump to that location
|
||||
cJSON_Delete(root);
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr, "[LSP ←] Unhandled response id=%d: %.80s\n",
|
||||
response_id, json);
|
||||
}
|
||||
|
||||
cJSON_Delete(root);
|
||||
}
|
||||
|
||||
static void* lsp_reader(void* arg)
|
||||
{
|
||||
LspClient* lsp = (LspClient*)arg;
|
||||
while (lsp->state != LSP_SHUTDOWN)
|
||||
{
|
||||
char* msg = lsp_recv(lsp->read_fd);
|
||||
if (!msg) break; // ← pipe closed or error, exit cleanly
|
||||
lsp_dispatch(lsp, msg);
|
||||
bFree(msg);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// ─── lifecycle ───────────────────────────────────────────────────────────────
|
||||
|
||||
int lspStart(LspClient* lsp, const char* project_root)
|
||||
{
|
||||
int to_clangd[2], from_clangd[2];
|
||||
pipe(to_clangd);
|
||||
pipe(from_clangd);
|
||||
|
||||
pipe(lsp->wake_pipe);
|
||||
|
||||
lsp->pid = fork();
|
||||
if (lsp->pid == 0)
|
||||
{
|
||||
// Child: become clangd
|
||||
dup2(to_clangd[0], STDIN_FILENO);
|
||||
dup2(from_clangd[1], STDOUT_FILENO);
|
||||
close(to_clangd[1]);
|
||||
close(from_clangd[0]);
|
||||
execlp("clangd", "clangd", "--log=error", "--completion-style=detailed", NULL);
|
||||
_exit(1); // clangd not found
|
||||
}
|
||||
|
||||
close(to_clangd[0]);
|
||||
close(from_clangd[1]);
|
||||
lsp->write_fd = to_clangd[1];
|
||||
lsp->read_fd = from_clangd[0];
|
||||
lsp->next_id = 1;
|
||||
lsp->state = LSP_INITIALIZING;
|
||||
pthread_mutex_init(&lsp->lock, NULL);
|
||||
|
||||
// Send initialize
|
||||
char buf[1024];
|
||||
snprintf(buf, sizeof(buf),
|
||||
"{\"jsonrpc\":\"2.0\",\"id\":%d,\"method\":\"initialize\","
|
||||
"\"params\":{\"processId\":%d,\"rootUri\":\"file://%s\","
|
||||
"\"capabilities\":{"
|
||||
"\"textDocument\":{"
|
||||
"\"completion\":{\"completionItem\":{\"snippetSupport\":false}},"
|
||||
"\"hover\":{},"
|
||||
"\"definition\":{},"
|
||||
"\"publishDiagnostics\":{}"
|
||||
"}"
|
||||
"}}}",
|
||||
lsp->next_id++, getpid(), project_root);
|
||||
|
||||
pthread_mutex_init(&lsp->lock, NULL);
|
||||
pthread_cond_init(&lsp->ready_cond, NULL);
|
||||
pthread_create(&lsp->reader_thread, NULL, lsp_reader, lsp);
|
||||
|
||||
|
||||
lsp_send(lsp->write_fd, buf);
|
||||
|
||||
pthread_mutex_lock(&lsp->lock);
|
||||
while (lsp->state != LSP_READY)
|
||||
pthread_cond_wait(&lsp->ready_cond, &lsp->lock);
|
||||
pthread_mutex_unlock(&lsp->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ─── document sync ───────────────────────────────────────────────────────────
|
||||
|
||||
// Build the full buffer text into a bAlloc'd string
|
||||
static char* buffer_to_text(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 = bAlloc(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;
|
||||
}
|
||||
|
||||
void lspDidOpen(LspClient* lsp, struct buffer_t* buf)
|
||||
{
|
||||
if (lsp->state != LSP_READY || buf->b_lsp_open) return;
|
||||
|
||||
char abs[PATH_MAX];
|
||||
realpath(buf->filename, abs);
|
||||
char uri[PATH_MAX + 8];
|
||||
snprintf(uri, sizeof(uri), "file://%s", abs);
|
||||
|
||||
const char* lang = "c";
|
||||
if (strstr(buf->filename, ".cpp") || strstr(buf->filename, ".cc"))
|
||||
lang = "cpp";
|
||||
|
||||
char* raw = buffer_to_text(buf);
|
||||
|
||||
// Let cJSON handle ALL escaping — don't touch the text yourself
|
||||
cJSON* root = cJSON_CreateObject();
|
||||
cJSON* params = cJSON_CreateObject();
|
||||
cJSON* td = cJSON_CreateObject();
|
||||
|
||||
cJSON_AddStringToObject(root, "jsonrpc", "2.0");
|
||||
cJSON_AddStringToObject(root, "method", "textDocument/didOpen");
|
||||
|
||||
cJSON_AddStringToObject(td, "uri", uri);
|
||||
cJSON_AddStringToObject(td, "languageId", lang);
|
||||
cJSON_AddNumberToObject(td, "version", 1);
|
||||
cJSON_AddStringToObject(td, "text", raw); // cJSON escapes this
|
||||
|
||||
cJSON_AddItemToObject(params, "textDocument", td);
|
||||
cJSON_AddItemToObject(root, "params", params);
|
||||
|
||||
char* msg = cJSON_PrintUnformatted(root);
|
||||
lsp_send(lsp->write_fd, msg);
|
||||
|
||||
bFree(msg);
|
||||
bFree(raw);
|
||||
cJSON_Delete(root);
|
||||
buf->b_lsp_open = 1;
|
||||
}
|
||||
|
||||
void lspDidChange(LspClient* lsp, struct buffer_t* buf)
|
||||
{
|
||||
if (lsp->state != LSP_READY || !buf->b_lsp_open) return;
|
||||
|
||||
char abs[PATH_MAX];
|
||||
realpath(buf->filename, abs);
|
||||
char uri[PATH_MAX + 8];
|
||||
snprintf(uri, sizeof(uri), "file://%s", abs);
|
||||
|
||||
char* raw = buffer_to_text(buf);
|
||||
|
||||
cJSON* root = cJSON_CreateObject();
|
||||
cJSON* params = cJSON_CreateObject();
|
||||
cJSON* td = cJSON_CreateObject();
|
||||
cJSON* changes = cJSON_CreateArray();
|
||||
cJSON* change = cJSON_CreateObject();
|
||||
|
||||
cJSON_AddStringToObject(root, "jsonrpc", "2.0");
|
||||
cJSON_AddStringToObject(root, "method", "textDocument/didChange");
|
||||
|
||||
cJSON_AddStringToObject(td, "uri", uri);
|
||||
cJSON_AddNumberToObject(td, "version", buf->dirty);
|
||||
|
||||
cJSON_AddStringToObject(change, "text", raw); // full content sync
|
||||
cJSON_AddItemToArray(changes, change);
|
||||
|
||||
cJSON_AddItemToObject(params, "textDocument", td);
|
||||
cJSON_AddItemToObject(params, "contentChanges", changes);
|
||||
cJSON_AddItemToObject(root, "params", params);
|
||||
|
||||
char* msg = cJSON_PrintUnformatted(root);
|
||||
lsp_send(lsp->write_fd, msg);
|
||||
|
||||
bFree(msg);
|
||||
bFree(raw);
|
||||
cJSON_Delete(root);
|
||||
}
|
||||
|
||||
void lspDidClose(LspClient* lsp, struct buffer_t* buf)
|
||||
{
|
||||
char abs[PATH_MAX];
|
||||
realpath(buf->filename, abs);
|
||||
char uri[PATH_MAX + 8];
|
||||
snprintf(uri, sizeof(uri), "file://%s", abs);
|
||||
|
||||
cJSON* root = cJSON_CreateObject();
|
||||
cJSON* params = cJSON_CreateObject();
|
||||
cJSON* td = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(root, "jsonrpc", "2.0");
|
||||
cJSON_AddStringToObject(root, "method", "textDocument/didClose");
|
||||
cJSON_AddStringToObject(td, "uri", uri); // ← fixed
|
||||
cJSON_AddItemToObject(params, "textDocument", td);
|
||||
cJSON_AddItemToObject(root, "params", params);
|
||||
char* msg = cJSON_PrintUnformatted(root);
|
||||
lsp_send(lsp->write_fd, msg);
|
||||
bFree(msg);
|
||||
cJSON_Delete(root);
|
||||
buf->b_lsp_open = 0;
|
||||
}
|
||||
|
||||
// ─── requests ────────────────────────────────────────────────────────────────
|
||||
|
||||
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; // ← add
|
||||
lsp->completion_cursor_y = screen_y;
|
||||
|
||||
appDebug("LSP REQUEST COMP");
|
||||
|
||||
char* msg;
|
||||
char abs[PATH_MAX];
|
||||
realpath(buf->filename, abs);
|
||||
char uri[PATH_MAX + 8];
|
||||
snprintf(uri, sizeof(uri), "file://%s", abs);
|
||||
|
||||
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->write_fd, msg);
|
||||
E.lsp_client->completion_requested = 1;
|
||||
cJSON_Delete(req);
|
||||
bFree(msg);
|
||||
}
|
||||
|
||||
void lspRequestDefinition(LspClient* lsp, struct buffer_t* buf, int line, int col)
|
||||
{
|
||||
if (lsp->state != LSP_READY) return;
|
||||
char* msg;
|
||||
asprintf(&msg,
|
||||
"{\"jsonrpc\":\"2.0\",\"id\":%d,\"method\":\"textDocument/definition\","
|
||||
"\"params\":{\"textDocument\":{\"uri\":\"file://%s\"},"
|
||||
"\"position\":{\"line\":%d,\"character\":%d}}}",
|
||||
lsp->next_id++, buf->filename, line, col);
|
||||
lsp_send(lsp->write_fd, msg);
|
||||
bFree(msg);
|
||||
}
|
||||
|
||||
void lspShutdown(LspClient* lsp)
|
||||
{
|
||||
if (!lsp || lsp->state == LSP_SHUTDOWN) return;
|
||||
|
||||
lsp->state = LSP_SHUTDOWN;
|
||||
|
||||
// 1. Send shutdown request (clangd expects this before exit)
|
||||
cJSON* req = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(req, "jsonrpc", "2.0");
|
||||
cJSON_AddNumberToObject(req, "id", lsp->next_id++);
|
||||
cJSON_AddStringToObject(req, "method", "shutdown");
|
||||
cJSON_AddNullToObject(req, "params");
|
||||
char* msg = cJSON_PrintUnformatted(req);
|
||||
lsp_send(lsp->write_fd, msg);
|
||||
bFree(msg);
|
||||
cJSON_Delete(req);
|
||||
|
||||
// 2. Wait briefly for the shutdown response
|
||||
// (don't block forever — clangd has 2s to reply)
|
||||
struct timeval tv = {.tv_sec = 2, .tv_usec = 0};
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(lsp->read_fd, &fds);
|
||||
if (select(lsp->read_fd + 1, &fds, NULL, NULL, &tv) > 0)
|
||||
{
|
||||
char* resp = lsp_recv(lsp->read_fd);
|
||||
bFree(resp);
|
||||
}
|
||||
|
||||
// 3. Send exit notification
|
||||
lsp_send(lsp->write_fd,
|
||||
"{\"jsonrpc\":\"2.0\",\"method\":\"exit\",\"params\":null}");
|
||||
|
||||
// 4. Close pipes — this signals the reader thread to stop
|
||||
close(lsp->write_fd);
|
||||
close(lsp->read_fd);
|
||||
|
||||
// 5. Wait for reader thread to finish
|
||||
pthread_join(lsp->reader_thread, NULL);
|
||||
pthread_mutex_destroy(&lsp->lock);
|
||||
|
||||
// 6. Reap the clangd process
|
||||
waitpid(lsp->pid, NULL, 0);
|
||||
|
||||
bFree(lsp);
|
||||
}
|
||||
+3
-2
@@ -8,6 +8,7 @@
|
||||
#include "../include/split_screen.h"
|
||||
#include "../include/terminal.h"
|
||||
#include "../include/utf8.h"
|
||||
#include "include/utils.h"
|
||||
|
||||
|
||||
extern struct editorConfig E;
|
||||
@@ -83,13 +84,13 @@ char *editorGetClipboard(void) {
|
||||
|
||||
size_t cap = 4096;
|
||||
size_t len = 0;
|
||||
char *buf = malloc(cap);
|
||||
char *buf = bAlloc(cap);
|
||||
|
||||
int c;
|
||||
while ((c = fgetc(pipe)) != EOF) {
|
||||
if (len + 1 >= cap) {
|
||||
cap *= 2;
|
||||
buf = realloc(buf, cap);
|
||||
buf = bRealloc(buf, cap);
|
||||
}
|
||||
buf[len++] = (char)c;
|
||||
}
|
||||
|
||||
+14
-6
@@ -21,6 +21,8 @@
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "../include/utils.h"
|
||||
|
||||
extern struct editorConfig E;
|
||||
|
||||
/**
|
||||
@@ -37,13 +39,13 @@ void editorCloseFile(void) {
|
||||
active->x_offset = 0;
|
||||
active->y_offset = 0;
|
||||
for (int i = 0; i < buf->numrows; ++i) {
|
||||
free(buf->row[i].chars);
|
||||
bFree(buf->row[i].chars);
|
||||
}
|
||||
buf->numrows = 0;
|
||||
free(buf->row);
|
||||
bFree(buf->row);
|
||||
buf->row = NULL;
|
||||
buf->dirty = 0;
|
||||
free(buf->filename);
|
||||
bFree(buf->filename);
|
||||
buf->filename = NULL;
|
||||
E.status_msg[0] = '\0';
|
||||
E.status_msg_time = 0;
|
||||
@@ -62,8 +64,14 @@ void editorCloseFile(void) {
|
||||
*/
|
||||
void editorOpen(struct buffer_t* buffer) {
|
||||
FILE *fp;
|
||||
char full_name[1024];
|
||||
strcpy(full_name, buffer->path);
|
||||
strcat(full_name, "/");
|
||||
strcat(full_name, buffer->filename);
|
||||
strcat(full_name, "\0");
|
||||
appDebug("full name : %s", full_name);
|
||||
|
||||
fp = fopen(buffer->filename, "a+");
|
||||
fp = fopen(full_name, "a+");
|
||||
if (!fp)
|
||||
die("fopen");
|
||||
|
||||
@@ -80,10 +88,10 @@ void editorOpen(struct buffer_t* buffer) {
|
||||
}
|
||||
appDebug("line %s\n", line);
|
||||
bufferInsertRow(buffer, buffer->numrows, line, line_len);
|
||||
free(line);
|
||||
bFree(line);
|
||||
line = NULL;
|
||||
}
|
||||
free(line);
|
||||
bFree(line);
|
||||
fclose(fp);
|
||||
E.dirty = 0;
|
||||
}
|
||||
|
||||
+6
-2
@@ -11,6 +11,7 @@
|
||||
#define LISP_IMPLEMENTATION
|
||||
#include "../include/lisp.h"
|
||||
#include "../include/lisp_lib.h"
|
||||
#include "include/utils.h"
|
||||
|
||||
struct editorConfig;
|
||||
|
||||
@@ -50,6 +51,8 @@ void initBuiltins() {
|
||||
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() {
|
||||
@@ -128,13 +131,14 @@ void initEditor() {
|
||||
E.number_of_keybinds = 0;
|
||||
E.number_of_prefix = 0;
|
||||
// General prefix is 0 (no prefix)
|
||||
E.prefix = (struct prefix_t *)malloc(sizeof(struct prefix_t));
|
||||
E.prefix = (struct prefix_t *)bAlloc(sizeof(struct prefix_t));
|
||||
E.prefix[0].prefix_id = 0;
|
||||
strncpy(E.prefix[0].prefix_name, "no-prefix", 64);
|
||||
E.prefix_state = 0;
|
||||
|
||||
initConfig();
|
||||
E.lsp_client = (LspClient*)bAlloc(sizeof(LspClient));
|
||||
|
||||
initConfig();
|
||||
initTheme();
|
||||
|
||||
// To modify
|
||||
|
||||
+56
-7
@@ -6,6 +6,8 @@
|
||||
#include "include/buffer.h"
|
||||
#include "include/data.h"
|
||||
#include "include/split_screen.h"
|
||||
#include "include/completion.h"
|
||||
#include "include/lsp_ui.h"
|
||||
#include <ctype.h>
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
@@ -18,6 +20,7 @@
|
||||
|
||||
#include "include/terminal.h"
|
||||
#include "include/utf8.h"
|
||||
#include "include/utils.h"
|
||||
|
||||
extern struct editorConfig E;
|
||||
|
||||
@@ -37,7 +40,7 @@ extern struct editorConfig E;
|
||||
* - path ends with '/' (already a directory)
|
||||
* - no matching entries found
|
||||
* - directory cannot be opened
|
||||
* @note Caller is responsible for freeing the returned string
|
||||
* @note Caller is responsible for bFreeing the returned string
|
||||
* @note Uses static buffer internally; may return stale pointers across calls
|
||||
*/
|
||||
const char *fileCompletion(const char *path) {
|
||||
@@ -92,7 +95,7 @@ const char *fileCompletion(const char *path) {
|
||||
// Cleanup when no more entries
|
||||
closedir(dir);
|
||||
dir = NULL;
|
||||
free(entry);
|
||||
bFree(entry);
|
||||
appDebug("[FILE COMP] no entries\n");
|
||||
return strdup(path);
|
||||
}
|
||||
@@ -108,13 +111,13 @@ const char *fileCompletion(const char *path) {
|
||||
* @return Pointer to the user-entered text (dynamically allocated), or NULL if:
|
||||
* - User pressed ESC to cancel
|
||||
* - Input buffer is empty when Enter is pressed
|
||||
* @note Caller is responsible for freeing the returned string
|
||||
* @note Caller is responsible for bFreeing the returned string
|
||||
* @note Uses editorReadKey() for input and editorRefreshScreen() for display
|
||||
*/
|
||||
char *editorPrompt(char *prompt, char *placeHolder, char bPathMode) {
|
||||
size_t buf_size = 256;
|
||||
appDebug("[FILE COMP] %s %d\n", placeHolder, strlen(placeHolder));
|
||||
char *buf = malloc(buf_size);
|
||||
char *buf = bAlloc(buf_size);
|
||||
size_t buf_len = 0;
|
||||
int c = 0;
|
||||
buf[0] = '\0';
|
||||
@@ -131,7 +134,7 @@ char *editorPrompt(char *prompt, char *placeHolder, char bPathMode) {
|
||||
}
|
||||
} else if (c == ESCAPE) {
|
||||
editorSetStatusMessage("");
|
||||
free(buf);
|
||||
bFree(buf);
|
||||
return NULL;
|
||||
} else if (c == '\r') {
|
||||
if (buf_len != 0) {
|
||||
@@ -154,14 +157,14 @@ char *editorPrompt(char *prompt, char *placeHolder, char bPathMode) {
|
||||
buf_len = 0;
|
||||
char * buf_complete = (char *) fileCompletion(path);
|
||||
strcpy(buf, buf_complete);
|
||||
free(buf_complete);
|
||||
bFree(buf_complete);
|
||||
buf_len = strlen(buf);
|
||||
buf[buf_len] = '\0';
|
||||
|
||||
} else if (!iscntrl(c) && c < 256) {
|
||||
if (buf_len == buf_size - 1) {
|
||||
buf_size *= 2;
|
||||
buf = realloc(buf, buf_size);
|
||||
buf = bRealloc(buf, buf_size);
|
||||
}
|
||||
buf[buf_len++] = c;
|
||||
buf[buf_len] = '\0';
|
||||
@@ -213,6 +216,52 @@ void editorProcessKeypress() {
|
||||
int c = editorReadKey();
|
||||
char key_sequence[8];
|
||||
|
||||
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];
|
||||
EditorPane *active = splitScreenGetActivePane();
|
||||
struct buffer_t *buf = bufferFindById(active->buffer_id);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
+324
@@ -0,0 +1,324 @@
|
||||
//
|
||||
// Created by Giorgio on 27/05/2026.
|
||||
//
|
||||
|
||||
#include "include/lsp_ui.h"
|
||||
#include "include/data.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.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"
|
||||
|
||||
// ─── 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) {
|
||||
fprintf(stderr, "[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': // ESC — dismiss
|
||||
popup->visible = 0;
|
||||
return 1;
|
||||
|
||||
case '\r': // Enter — accept
|
||||
// Caller should insert popup->items[popup->selected].label
|
||||
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;
|
||||
popup->origin_y = screen_y + 1; // 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);
|
||||
ci->kind = kind ? kind->valueint : 0;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
strncpy(diag->message, msg ? msg->valuestring : "", 255);
|
||||
}
|
||||
|
||||
cJSON_Delete(root);
|
||||
}
|
||||
+54
-18
@@ -15,11 +15,15 @@
|
||||
#include "../include/split_screen.h"
|
||||
#include "../include/syntax_highlighter.h"
|
||||
#include "../include/terminal.h"
|
||||
#include "../include/lsp_ui.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "include/completion.h"
|
||||
#include "include/utils.h"
|
||||
|
||||
extern struct editorConfig E;
|
||||
|
||||
/**
|
||||
@@ -70,7 +74,7 @@ static void editorDrawPane(struct abuf* ab, EditorPane* pane)
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
lspUiDrawGutter(ab, &E.lsp_diagnostics, pane->buffer_id, file_row);
|
||||
|
||||
if (buf->filename[strlen(buf->filename) - 1] == 'c' || buf->filename[strlen(buf->filename) - 1] == 'h')
|
||||
{
|
||||
@@ -80,7 +84,7 @@ static void editorDrawPane(struct abuf* ab, EditorPane* pane)
|
||||
|
||||
// Print only up to pane width
|
||||
abAppend(ab, highlighted, byte_len_to_print);
|
||||
free(highlighted);
|
||||
bFree(highlighted);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -180,11 +184,13 @@ void editorScroll()
|
||||
if (active->cursor_x == 0 && active->x_offset)
|
||||
{
|
||||
active->x_offset--;
|
||||
} else
|
||||
}
|
||||
else
|
||||
{
|
||||
active->cursor_x--;
|
||||
}
|
||||
} else
|
||||
}
|
||||
else
|
||||
{
|
||||
// RIGHT
|
||||
if (rel_x > active->cursor_x + active->x_offset)
|
||||
@@ -192,7 +198,8 @@ void editorScroll()
|
||||
if (active->cursor_x == active->width - 1)
|
||||
{
|
||||
active->x_offset++;
|
||||
} else
|
||||
}
|
||||
else
|
||||
{
|
||||
active->cursor_x++;
|
||||
}
|
||||
@@ -204,7 +211,8 @@ void editorScroll()
|
||||
if (active->cursor_y == 0 && active->y_offset)
|
||||
{
|
||||
active->y_offset--;
|
||||
} else
|
||||
}
|
||||
else
|
||||
{
|
||||
active->cursor_y--;
|
||||
}
|
||||
@@ -215,12 +223,12 @@ void editorScroll()
|
||||
if (active->cursor_y == active->height - 1)
|
||||
{
|
||||
active->y_offset++;
|
||||
} else
|
||||
}
|
||||
else
|
||||
{
|
||||
active->cursor_y++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,7 +362,7 @@ void editorDrawContextBuffer(struct abuf* ab)
|
||||
abAppend(ab, "|", 1);
|
||||
}
|
||||
}
|
||||
free(E.context_buffers);
|
||||
bFree(E.context_buffers);
|
||||
E.context_buffers = NULL;
|
||||
}
|
||||
|
||||
@@ -377,26 +385,54 @@ void editorRefreshScreen()
|
||||
abAppend(&ab, HIDE_CURSOR, 6);
|
||||
abAppend(&ab, CURSOR_TOP_LEFT, 3);
|
||||
abAppend(&ab, E.theme.BACKGROUND_COLOR,
|
||||
(int) strlen(E.theme.BACKGROUND_COLOR)); // RGB background is 12 bytes
|
||||
(int)strlen(E.theme.BACKGROUND_COLOR));
|
||||
|
||||
// Draw all panes
|
||||
editorScroll();
|
||||
editorDrawAllPanes(&ab);
|
||||
|
||||
// Draw status bar and message bar
|
||||
editorDrawStatusBar(&ab);
|
||||
editorDrawMessageBar(&ab);
|
||||
// editorDrawContextBuffer(&ab);
|
||||
|
||||
// Position cursor in active pane
|
||||
EditorPane *active = splitScreenGetActivePane();
|
||||
if (active != NULL)
|
||||
struct buffer_t *buffer = bufferFindById(active->buffer_id);
|
||||
|
||||
// ── LSP: sync buffer changes to clangd ────────────────────────────────────
|
||||
if (buffer->b_has_changed) {
|
||||
if (E.lsp_client && E.lsp_client->state == LSP_READY) {
|
||||
lspDidChange(E.lsp_client, buffer);
|
||||
|
||||
E.lsp_client->completion_just_arrived = 0; // consume the flag
|
||||
}
|
||||
buffer->b_has_changed = 0;
|
||||
}
|
||||
|
||||
// ── LSP: draw completion popup every frame while visible ──────────────────
|
||||
fprintf(stderr, "[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;
|
||||
|
||||
if (E.lsp_client && E.lsp_client->state == LSP_READY)
|
||||
{
|
||||
lspUiDrawCompletion(&ab, &E.lsp_completion);
|
||||
appDebug("ready\n");
|
||||
}
|
||||
|
||||
// ── LSP: diagnostic for current line in status bar ────────────────────────
|
||||
const char *diag = lspUiDiagnosticAtCursor(
|
||||
&E.lsp_diagnostics,
|
||||
active->buffer_id,
|
||||
active->cursor_y + active->y_offset);
|
||||
if (diag)
|
||||
editorSetStatusMessage("● %s", diag);
|
||||
|
||||
// ── Position cursor (account for gutter width) ────────────────────────────
|
||||
snprintf(buf, sizeof(buf), "\x1b[%d;%dH",
|
||||
active->cursor_y + active->origin_y + 1,
|
||||
active->cursor_x + active->origin_x + 1);
|
||||
active->cursor_x + active->origin_x + 1 + GUTTER_WIDTH);
|
||||
abAppend(&ab, buf, (int)strlen(buf));
|
||||
}
|
||||
|
||||
abAppend(&ab, SHOW_CURSOR, 6);
|
||||
write(STDOUT_FILENO, ab.b, ab.len);
|
||||
|
||||
+7
-5
@@ -9,6 +9,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "include/utils.h"
|
||||
|
||||
extern struct editorConfig E;
|
||||
|
||||
/**
|
||||
@@ -19,7 +21,7 @@ void splitScreenInit(void) {
|
||||
E.layout.num_panes = 1;
|
||||
E.layout.active_pane = 0;
|
||||
|
||||
E.layout.panes = malloc(sizeof(EditorPane) * 2);
|
||||
E.layout.panes = bAlloc(sizeof(EditorPane) * 2);
|
||||
|
||||
// Initialize single fullscreen pane
|
||||
E.layout.panes[0].buffer_id = -1; // No buffer for now
|
||||
@@ -48,8 +50,8 @@ int splitScreenVertical(int buffer_id_left, int buffer_id_right) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Reallocate panes array
|
||||
E.layout.panes = realloc(E.layout.panes, sizeof(EditorPane) * 2);
|
||||
// bReallocate panes array
|
||||
E.layout.panes = bRealloc(E.layout.panes, sizeof(EditorPane) * 2);
|
||||
E.layout.mode = SPLIT_VERTICAL;
|
||||
E.layout.num_panes = 2;
|
||||
E.layout.active_pane = 0;
|
||||
@@ -99,8 +101,8 @@ int splitScreenHorizontal(int buffer_id_top, int buffer_id_bottom) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Reallocate panes array
|
||||
E.layout.panes = realloc(E.layout.panes, sizeof(EditorPane) * 2);
|
||||
// bReallocate panes array
|
||||
E.layout.panes = bRealloc(E.layout.panes, sizeof(EditorPane) * 2);
|
||||
E.layout.mode = SPLIT_HORIZONTAL;
|
||||
E.layout.num_panes = 2;
|
||||
E.layout.active_pane = 0;
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "include/utils.h"
|
||||
|
||||
extern struct editorConfig E;
|
||||
|
||||
const char *c_keywords[] = {
|
||||
@@ -112,7 +114,7 @@ char *highlight_line(const char *line, int *length) {
|
||||
// Allocate generously based on line length to avoid overflow.
|
||||
int line_len = strlen(line);
|
||||
int buf_size = line_len * 32 + 256;
|
||||
char *result = malloc(buf_size);
|
||||
char *result = bAlloc(buf_size);
|
||||
int result_pos = 0;
|
||||
int i = 0;
|
||||
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// Created by Giorgio on 28/05/2026.
|
||||
//
|
||||
|
||||
#include "../include/utils.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
int beluga_alloc_counter = 0;
|
||||
|
||||
void * bAlloc(size_t size)
|
||||
{
|
||||
void * result = malloc(size);
|
||||
if (!result)
|
||||
return NULL;
|
||||
beluga_alloc_counter++;
|
||||
return result;
|
||||
}
|
||||
|
||||
void * bRealloc(void * ptr, size_t size)
|
||||
{
|
||||
void * result = realloc(ptr, size);
|
||||
if (!result)
|
||||
return NULL;
|
||||
beluga_alloc_counter++;
|
||||
return result;
|
||||
}
|
||||
|
||||
void * bFree(void * ptr)
|
||||
{
|
||||
if (ptr)
|
||||
{
|
||||
free(ptr);
|
||||
beluga_alloc_counter--;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
int main() {
|
||||
printf("hello");
|
||||
}
|
||||
Reference in New Issue
Block a user