popup and diagnose ui
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
(define TAB-LENGTH 4)
|
(define TAB-LENGTH 4)
|
||||||
(define QUIT-TIMES 1)
|
(define QUIT-TIMES 1)
|
||||||
(define THEME "dark")
|
(define THEME "dark")
|
||||||
|
(define LSP #t)
|
||||||
|
|
||||||
;; PACKAGES
|
;; PACKAGES
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,6 @@
|
|||||||
|
|
||||||
void abAppend(struct abuf *ab, const char *s, int len);
|
void abAppend(struct abuf *ab, const char *s, int len);
|
||||||
|
|
||||||
void abFree(struct abuf *ab);
|
void abFree(const struct abuf *ab);
|
||||||
|
|
||||||
#endif // APPEND_BUFFER_H_
|
#endif // APPEND_BUFFER_H_
|
||||||
|
|||||||
+8
-11
@@ -7,23 +7,20 @@
|
|||||||
#include "data.h"
|
#include "data.h"
|
||||||
|
|
||||||
|
|
||||||
|
int lspStart(LspClient* lsp, const char* project_root);
|
||||||
void createContextBuffer(const int x, const int y, const char * text);
|
void lspShutdown(LspClient* lsp);
|
||||||
|
|
||||||
int lspStart(LspClient *lsp, const char *project_root);
|
|
||||||
void lspShutdown(LspClient *lsp);
|
|
||||||
|
|
||||||
// Document sync — call these from your buffer open/save/edit hooks
|
// Document sync — call these from your buffer open/save/edit hooks
|
||||||
void lspDidOpen(LspClient *lsp, struct buffer_t *buf);
|
void lspDidOpen(LspClient* lsp, struct buffer_t* buf);
|
||||||
void lspDidChange(LspClient *lsp, struct buffer_t *buf);
|
void lspDidChange(LspClient* lsp, struct buffer_t* buf);
|
||||||
void lspDidClose(LspClient *lsp, struct buffer_t *buf);
|
void lspDidClose(LspClient* lsp, struct buffer_t* buf);
|
||||||
|
|
||||||
// Requests
|
// Requests
|
||||||
void lspRequestCompletion(LspClient *lsp, struct buffer_t *buf,
|
void lspRequestCompletion(LspClient* lsp, struct buffer_t* buf,
|
||||||
int line, int col,
|
int line, int col,
|
||||||
int screen_x, int screen_y);
|
int screen_x, int screen_y);
|
||||||
void lspRequestHover(LspClient *lsp, struct buffer_t *buf, int line, int col);
|
void lspRequestHover(LspClient* lsp, struct buffer_t* buf, int line, int col);
|
||||||
void lspRequestDefinition(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
|
#endif //BELUGA_COMPLETION_H
|
||||||
|
|||||||
+158
-146
@@ -17,85 +17,94 @@ typedef struct lsp_client_t LspClient;
|
|||||||
* \param
|
* \param
|
||||||
* */
|
* */
|
||||||
|
|
||||||
typedef struct row {
|
typedef struct row
|
||||||
int size; /**< Size of the line */
|
{
|
||||||
int cap; /**< Size of the render line */
|
int size; /**< Size of the line */
|
||||||
char *chars; /**< Characters of the line */
|
int cap; /**< Size of the render line */
|
||||||
|
char* chars; /**< Characters of the line */
|
||||||
} row_t;
|
} row_t;
|
||||||
|
|
||||||
typedef struct context_buffer_t
|
typedef struct context_buffer_t
|
||||||
{
|
{
|
||||||
int editor_x, editor_y;
|
int editor_x, editor_y;
|
||||||
int width, height;
|
int width, height;
|
||||||
row_t *rows;
|
row_t* rows;
|
||||||
} ContextBuffer;
|
} ContextBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Split modes for screen layout
|
* @brief Split modes for screen layout
|
||||||
*/
|
*/
|
||||||
typedef enum {
|
typedef enum
|
||||||
SPLIT_NONE = 0, // Single buffer fullscreen
|
{
|
||||||
SPLIT_VERTICAL, // Left-right split
|
SPLIT_NONE = 0, // Single buffer fullscreen
|
||||||
SPLIT_HORIZONTAL // Top-bottom split
|
SPLIT_VERTICAL, // Left-right split
|
||||||
|
SPLIT_HORIZONTAL // Top-bottom split
|
||||||
} SplitMode;
|
} SplitMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Represents an editor viewport/pane
|
* @brief Represents an editor viewport/pane
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct
|
||||||
int buffer_id; // Which buffer this pane displays
|
{
|
||||||
int height; // Height of this pane
|
int buffer_id; // Which buffer this pane displays
|
||||||
int width; // Width of this pane
|
int height; // Height of this pane
|
||||||
|
int width; // Width of this pane
|
||||||
int origin_x, origin_y;
|
int origin_x, origin_y;
|
||||||
int cursor_x; // Local cursor x in this pane
|
int cursor_x; // Local cursor x in this pane
|
||||||
int cursor_y; // Local cursor y in this pane
|
int cursor_y; // Local cursor y in this pane
|
||||||
int x_offset, y_offset;
|
int x_offset, y_offset;
|
||||||
int is_active; // Is this pane currently active
|
int is_active; // Is this pane currently active
|
||||||
} EditorPane;
|
} EditorPane;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Screen layout manager
|
* @brief Screen layout manager
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct
|
||||||
|
{
|
||||||
SplitMode mode;
|
SplitMode mode;
|
||||||
EditorPane *panes;
|
EditorPane* panes;
|
||||||
int num_panes;
|
int num_panes;
|
||||||
int active_pane; // Index of active pane
|
int active_pane; // Index of active pane
|
||||||
} ScreenLayout;
|
} ScreenLayout;
|
||||||
|
|
||||||
|
|
||||||
typedef struct theme {
|
typedef struct theme
|
||||||
char *BACKGROUND_COLOR;
|
{
|
||||||
char *COLOR_KEYWORD;
|
char* BACKGROUND_COLOR;
|
||||||
char *COLOR_TYPE;
|
char* COLOR_KEYWORD;
|
||||||
char *COLOR_STRING;
|
char* COLOR_TYPE;
|
||||||
char *COLOR_COMMENT;
|
char* COLOR_STRING;
|
||||||
char *COLOR_NUMBER;
|
char* COLOR_COMMENT;
|
||||||
char *COLOR_DEFAULT;
|
char* COLOR_NUMBER;
|
||||||
|
char* COLOR_DEFAULT;
|
||||||
} theme_t;
|
} theme_t;
|
||||||
|
|
||||||
enum bufferStatus_e {
|
enum bufferStatus_e
|
||||||
IDLE,
|
{
|
||||||
READ_ONLY,
|
IDLE,
|
||||||
READ_AND_WRITE,
|
READ_ONLY,
|
||||||
|
READ_AND_WRITE,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct const_t {
|
struct const_t
|
||||||
int TAB_LENGTH;
|
{
|
||||||
int QUIT_TIMES;
|
int TAB_LENGTH;
|
||||||
char *THEME;
|
int QUIT_TIMES;
|
||||||
|
char* THEME;
|
||||||
|
int LSP;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct prefix_t {
|
struct prefix_t
|
||||||
char prefix_name[64];
|
{
|
||||||
int prefix_id;
|
char prefix_name[64];
|
||||||
|
int prefix_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct keyBind_t {
|
struct keyBind_t
|
||||||
char *key_sequence;
|
{
|
||||||
int prefix_id;
|
char* key_sequence;
|
||||||
Lisp command;
|
int prefix_id;
|
||||||
|
Lisp command;
|
||||||
};
|
};
|
||||||
|
|
||||||
// In data.h — add these
|
// In data.h — add these
|
||||||
@@ -104,142 +113,144 @@ struct keyBind_t {
|
|||||||
|
|
||||||
#define LSP_MAX_PENDING 64
|
#define LSP_MAX_PENDING 64
|
||||||
|
|
||||||
typedef enum {
|
typedef enum
|
||||||
LSP_NOT_STARTED = 0,
|
{
|
||||||
LSP_INITIALIZING,
|
LSP_NOT_STARTED = 0,
|
||||||
LSP_READY,
|
LSP_INITIALIZING,
|
||||||
LSP_SHUTDOWN,
|
LSP_READY,
|
||||||
|
LSP_SHUTDOWN,
|
||||||
} LspState;
|
} LspState;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
int id;
|
{
|
||||||
void (*callback)(struct lsp_client_t *lsp, const char *json);
|
int id;
|
||||||
|
void (*callback)(struct lsp_client_t* lsp, const char* json);
|
||||||
} LspPending;
|
} LspPending;
|
||||||
|
|
||||||
typedef struct lsp_client_t {
|
typedef struct lsp_client_t
|
||||||
// ── Process ───────────────────────────────────────────────────────────────
|
{
|
||||||
pid_t pid;
|
// ── Process ───────────────────────────────────────────────────────────────
|
||||||
int write_fd;
|
pid_t pid;
|
||||||
int read_fd;
|
int write_fd;
|
||||||
int completion_just_arrived;
|
int read_fd;
|
||||||
int completion_requested;
|
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;
|
||||||
|
|
||||||
int wake_pipe[2]; // [0] = read end (main loop), [1] = write end (reader thread)
|
// ── Threading ─────────────────────────────────────────────────────────────
|
||||||
|
pthread_t reader_thread;
|
||||||
|
pthread_mutex_t lock;
|
||||||
|
pthread_cond_t ready_cond; // signaled when state → LSP_READY
|
||||||
|
|
||||||
|
// ── Completion context ────────────────────────────────────────────────────
|
||||||
// ── State ─────────────────────────────────────────────────────────────────
|
int completion_cursor_x; // screen position when
|
||||||
LspState state;
|
int completion_cursor_y; // completion was requested
|
||||||
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;
|
} LspClient;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
char label[128]; // display text e.g. "printf"
|
{
|
||||||
char detail[64]; // type/sig hint e.g. "int (const char *, ...)"
|
char label[128]; // display text e.g. "printf"
|
||||||
int kind; // LSP CompletionItemKind (1=Text,2=Method,3=Function…)
|
char detail[64]; // type/sig hint e.g. "int (const char *, ...)"
|
||||||
|
int kind; // LSP CompletionItemKind (1=Text,2=Method,3=Function…)
|
||||||
} CompletionItem;
|
} CompletionItem;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
CompletionItem items[COMPLETION_MAX_ITEMS];
|
{
|
||||||
int count;
|
CompletionItem items[COMPLETION_MAX_ITEMS];
|
||||||
int selected; // currently highlighted row
|
int count;
|
||||||
int visible; // is the popup shown?
|
int selected; // currently highlighted row
|
||||||
int origin_x; // screen col where popup appears
|
int visible; // is the popup shown?
|
||||||
int origin_y; // screen row where popup appears
|
int origin_x; // screen col where popup appears
|
||||||
|
int origin_y; // screen row where popup appears
|
||||||
} CompletionPopup;
|
} CompletionPopup;
|
||||||
|
|
||||||
typedef enum { DIAG_ERROR = 1, DIAG_WARNING, DIAG_HINT } DiagSeverity;
|
typedef enum { DIAG_ERROR = 1, DIAG_WARNING, DIAG_HINT } DiagSeverity;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
int buffer_id;
|
{
|
||||||
int line; // 0-based
|
int buffer_id;
|
||||||
int col_start; // 0-based
|
int line; // 0-based
|
||||||
int col_end;
|
int col_start; // 0-based
|
||||||
DiagSeverity severity;
|
int col_end;
|
||||||
char message[256];
|
DiagSeverity severity;
|
||||||
|
char message[256];
|
||||||
} Diagnostic;
|
} Diagnostic;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
Diagnostic entries[DIAG_MAX];
|
{
|
||||||
int count;
|
Diagnostic entries[DIAG_MAX];
|
||||||
|
int count;
|
||||||
} DiagnosticList;
|
} DiagnosticList;
|
||||||
|
|
||||||
enum buffer_type { FILE_BUFF, TERMINAL_BUFF };
|
enum buffer_type { FILE_BUFF, TERMINAL_BUFF };
|
||||||
|
|
||||||
struct buffer_t {
|
struct buffer_t
|
||||||
enum buffer_type type;
|
{
|
||||||
int buffer_id;
|
enum buffer_type type;
|
||||||
int b_lsp_open;
|
int buffer_id;
|
||||||
int x, y; /**< Position in the file */
|
int b_lsp_open;
|
||||||
row_t *row;
|
int x, y; /**< Position in the file */
|
||||||
int numrows;
|
row_t* row;
|
||||||
int b_has_changed;
|
int numrows;
|
||||||
char *filename;
|
int b_has_changed;
|
||||||
char *path;
|
char* filename;
|
||||||
enum bufferStatus_e state;
|
char* path;
|
||||||
int dirty; /**< Has this buffer been modified since last save */
|
char * fullname;
|
||||||
|
enum bufferStatus_e state;
|
||||||
|
int dirty; /**< Has this buffer been modified since last save */
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \struct editorConfig
|
* \struct editorConfig
|
||||||
* \brief Containing our editor state.
|
* \brief Containing our editor state.
|
||||||
*/
|
*/
|
||||||
struct editorConfig {
|
struct editorConfig
|
||||||
int cursor_x, cursor_y; /**< Cursor position */
|
{
|
||||||
int screenrows; /**< Terminal height*/
|
int cursor_x, cursor_y; /**< Cursor position */
|
||||||
int screencols; /**< Terminal width*/
|
int screenrows; /**< Terminal height*/
|
||||||
|
int screencols; /**< Terminal width*/
|
||||||
|
|
||||||
ScreenLayout layout;
|
ScreenLayout layout;
|
||||||
|
|
||||||
row_t *rows; /**< Store all the rows printed */
|
LspClient* lsp_client;
|
||||||
|
CompletionPopup lsp_completion;
|
||||||
|
DiagnosticList lsp_diagnostics;
|
||||||
|
|
||||||
ContextBuffer* context_buffers;
|
int dirty;
|
||||||
|
|
||||||
LspClient *lsp_client;
|
char* status_msg;
|
||||||
CompletionPopup lsp_completion;
|
time_t status_msg_time;
|
||||||
DiagnosticList lsp_diagnostics;
|
struct termios orig_termios; /**< Terminal communication interface */
|
||||||
|
|
||||||
int dirty;
|
struct const_t constantes;
|
||||||
|
int quit_times_buffer;
|
||||||
|
|
||||||
char *status_msg;
|
char* init_file_path;
|
||||||
time_t status_msg_time;
|
FILE* fd_init_file;
|
||||||
struct termios orig_termios; /**< Terminal communication interface */
|
Lisp env;
|
||||||
|
LispContext ctx; /** Lisp context */
|
||||||
|
Lisp ctx_data; /** Lisp data context */
|
||||||
|
LispError ctx_error; /** Lisp ctx error */
|
||||||
|
|
||||||
struct const_t constantes;
|
struct keyBind_t* key_binds;
|
||||||
int quit_times_buffer;
|
int number_of_keybinds;
|
||||||
|
|
||||||
char *init_file_path;
|
struct prefix_t* prefix;
|
||||||
FILE *fd_init_file;
|
int number_of_prefix;
|
||||||
Lisp env;
|
int prefix_state;
|
||||||
LispContext ctx; /** Lisp context */
|
|
||||||
Lisp ctx_data; /** Lisp data context */
|
|
||||||
LispError ctx_error; /** Lisp ctx error */
|
|
||||||
|
|
||||||
struct keyBind_t *key_binds;
|
struct buffer_t buffers[64];
|
||||||
int number_of_keybinds;
|
int number_of_buffer;
|
||||||
|
|
||||||
struct prefix_t *prefix;
|
theme_t theme;
|
||||||
int number_of_prefix;
|
|
||||||
int prefix_state;
|
|
||||||
|
|
||||||
struct buffer_t buffers[64];
|
|
||||||
int number_of_buffer;
|
|
||||||
|
|
||||||
theme_t theme;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -247,9 +258,10 @@ struct editorConfig {
|
|||||||
* \brief Contains text to add before writing to screen.
|
* \brief Contains text to add before writing to screen.
|
||||||
* */
|
* */
|
||||||
|
|
||||||
struct abuf {
|
struct abuf
|
||||||
char *b; /**< Text that will be printed */
|
{
|
||||||
int len; /**< Length of the text */
|
char* b; /**< Text that will be printed */
|
||||||
|
int len; /**< Length of the text */
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct editorConfig E;
|
extern struct editorConfig E;
|
||||||
|
|||||||
+4
-1
@@ -12,7 +12,9 @@
|
|||||||
#define ERASE_END_LINE "\x1b[K"
|
#define ERASE_END_LINE "\x1b[K"
|
||||||
#define TAB "\t"
|
#define TAB "\t"
|
||||||
#define SPACE "\x20"
|
#define SPACE "\x20"
|
||||||
#define APP_DEBUG
|
|
||||||
|
/* Uncomment to see debug logs on stderr */
|
||||||
|
// #define APP_DEBUG
|
||||||
|
|
||||||
#define COMPLETION_MAX_ITEMS 16
|
#define COMPLETION_MAX_ITEMS 16
|
||||||
#define COMPLETION_MAX_WIDTH 40
|
#define COMPLETION_MAX_WIDTH 40
|
||||||
@@ -32,6 +34,7 @@ enum editorKey_e {
|
|||||||
END_LINE,
|
END_LINE,
|
||||||
PAGE_UP,
|
PAGE_UP,
|
||||||
PAGE_DOWN,
|
PAGE_DOWN,
|
||||||
|
LSP_WAKE_KEY = 2000
|
||||||
};
|
};
|
||||||
|
|
||||||
#define ABUF_INIT {NULL, 0}
|
#define ABUF_INIT {NULL, 0}
|
||||||
|
|||||||
@@ -11,4 +11,7 @@ void initBuiltins();
|
|||||||
|
|
||||||
void initEditor();
|
void initEditor();
|
||||||
|
|
||||||
|
void deInitEditor();
|
||||||
|
|
||||||
|
|
||||||
#endif // INIT_H_
|
#endif // INIT_H_
|
||||||
|
|||||||
+2
-2
@@ -1163,7 +1163,7 @@ static Lisp sch_string_ref(Lisp args, LispError* e, LispContext ctx)
|
|||||||
return lisp_null();
|
return lisp_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
return lisp_make_char((int)lisp_string_ref(str, lisp_int(index)));
|
return lisp_make_char(lisp_string_ref(str, lisp_int(index)));
|
||||||
}
|
}
|
||||||
|
|
||||||
static Lisp sch_string_set(Lisp args, LispError* e, LispContext ctx)
|
static Lisp sch_string_set(Lisp args, LispError* e, LispContext ctx)
|
||||||
@@ -1737,7 +1737,7 @@ static Lisp sch_pseudo_rand(Lisp args, LispError* e, LispContext ctx)
|
|||||||
|
|
||||||
static Lisp sch_univeral_time(Lisp args, LispError* e, LispContext ctx)
|
static Lisp sch_univeral_time(Lisp args, LispError* e, LispContext ctx)
|
||||||
{
|
{
|
||||||
return lisp_make_int((LispInt)time(NULL));
|
return lisp_make_int(time(NULL));
|
||||||
}
|
}
|
||||||
|
|
||||||
static Lisp sch_is_table(Lisp args, LispError* e, LispContext ctx)
|
static Lisp sch_is_table(Lisp args, LispError* e, LispContext ctx)
|
||||||
|
|||||||
@@ -59,4 +59,8 @@ ScreenLayout *splitScreenGetLayout(void);
|
|||||||
*/
|
*/
|
||||||
EditorPane *splitScreenGetActivePane(void);
|
EditorPane *splitScreenGetActivePane(void);
|
||||||
|
|
||||||
|
void freePane(EditorPane *pane);
|
||||||
|
void freeScreenLayout(ScreenLayout *layout);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ int utf8Encode(uint32_t cp, char *buf);
|
|||||||
int utf8Seqlen(unsigned char c);
|
int utf8Seqlen(unsigned char c);
|
||||||
int codepointWidth(uint32_t codepoint);
|
int codepointWidth(uint32_t codepoint);
|
||||||
uint32_t utf8Decode(const char** s);
|
uint32_t utf8Decode(const char** s);
|
||||||
|
int is_word_char(const char *s);
|
||||||
|
|
||||||
|
|
||||||
#endif //BELUGA_UTF8_H
|
#endif //BELUGA_UTF8_H
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
#define _BSD_SOURCE
|
#define _BSD_SOURCE
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
#include <libgen.h>
|
||||||
|
|
||||||
#include "include/utils.h"
|
#include "include/utils.h"
|
||||||
|
|
||||||
#include "include/buffer.h"
|
#include "include/buffer.h"
|
||||||
@@ -36,34 +38,27 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
enableRawMode();
|
enableRawMode();
|
||||||
initEditor();
|
initEditor();
|
||||||
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
|
struct buffer_t *buf;
|
||||||
|
|
||||||
|
strcat(splash_screen, getenv("HOME"));
|
||||||
|
strcat(splash_screen, "/.beluga/assets/beluga.txt");
|
||||||
|
|
||||||
|
appDebug("splash : %s\n", splash_screen);
|
||||||
|
active->buffer_id = bufferCreate(splash_screen, READ_ONLY);
|
||||||
|
|
||||||
if (argc >= 2) {
|
if (argc >= 2) {
|
||||||
EditorPane *active = splitScreenGetActivePane();
|
|
||||||
active->buffer_id = bufferCreate(argv[1], READ_AND_WRITE);
|
active->buffer_id = bufferCreate(argv[1], READ_AND_WRITE);
|
||||||
|
|
||||||
char project_root[512];
|
buf = &E.buffers[active->buffer_id];
|
||||||
realpath(argv[1], project_root);
|
|
||||||
char *slash = strrchr(project_root, '/');
|
|
||||||
if (slash) *slash = '\0';
|
|
||||||
|
|
||||||
appDebug("peoject root : %s\n", project_root);
|
appDebug("peoject root : %s\n", dirname(buf->fullname));
|
||||||
|
if (E.constantes.LSP) {
|
||||||
lspStart(E.lsp_client, project_root);
|
lspDidOpen(E.lsp_client, buf);
|
||||||
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");
|
|
||||||
|
|
||||||
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);
|
bFree(splash_screen);
|
||||||
|
|
||||||
editorSetStatusMessage("HELP: Ctrl-x Ctrl-s = save | Ctrl-x Ctrl-c = quit");
|
editorSetStatusMessage("HELP: Ctrl-x Ctrl-s = save | Ctrl-x Ctrl-c = quit");
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -12,4 +12,4 @@ void abAppend(struct abuf *ab, const char *s, int len) {
|
|||||||
ab->len += len;
|
ab->len += len;
|
||||||
}
|
}
|
||||||
|
|
||||||
void abFree(struct abuf *ab) { bFree(ab->b); }
|
void abFree(const struct abuf *ab) { bFree(ab->b); }
|
||||||
|
|||||||
+10
-4
@@ -8,12 +8,14 @@
|
|||||||
#include "../include/editor_op.h"
|
#include "../include/editor_op.h"
|
||||||
#include "../include/data.h"
|
#include "../include/data.h"
|
||||||
#include "include/split_screen.h"
|
#include "include/split_screen.h"
|
||||||
|
#include <_string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include "include/completion.h"
|
||||||
#include "include/input.h"
|
#include "include/input.h"
|
||||||
#include "include/utils.h"
|
#include "include/utils.h"
|
||||||
|
|
||||||
@@ -64,7 +66,6 @@ int bufferCreate(const char* path, enum bufferStatus_e state)
|
|||||||
char *filename = basename((char *) path);
|
char *filename = basename((char *) path);
|
||||||
// Check if file is already open
|
// Check if file is already open
|
||||||
const int existing_id = bufferFindByFilename(path);
|
const int existing_id = bufferFindByFilename(path);
|
||||||
path = dirname((char *) path);
|
|
||||||
if (existing_id != -1)
|
if (existing_id != -1)
|
||||||
{
|
{
|
||||||
return bufferSwitch(existing_id);
|
return bufferSwitch(existing_id);
|
||||||
@@ -80,17 +81,22 @@ int bufferCreate(const char* path, enum bufferStatus_e state)
|
|||||||
struct buffer_t* new_buf = &E.buffers[E.number_of_buffer];
|
struct buffer_t* new_buf = &E.buffers[E.number_of_buffer];
|
||||||
new_buf->buffer_id = E.number_of_buffer;
|
new_buf->buffer_id = E.number_of_buffer;
|
||||||
new_buf->filename = strdup(filename);
|
new_buf->filename = strdup(filename);
|
||||||
|
new_buf->fullname = bAlloc(1024 * sizeof(char));
|
||||||
|
realpath(path, new_buf->fullname);
|
||||||
|
new_buf->path = dirname(new_buf->fullname);
|
||||||
new_buf->type = FILE_BUFF;
|
new_buf->type = FILE_BUFF;
|
||||||
new_buf->state = state;
|
new_buf->state = state;
|
||||||
new_buf->x = 0;
|
new_buf->x = 0;
|
||||||
new_buf->y = 0;
|
new_buf->y = 0;
|
||||||
new_buf->dirty = 0; // New file starts clean
|
new_buf->dirty = 0; // New file starts clean
|
||||||
new_buf->path = strdup(path);
|
|
||||||
|
|
||||||
// Load file content using existing editorOpen
|
// Load file content using existing editorOpen
|
||||||
editorOpen(new_buf);
|
editorOpen(new_buf);
|
||||||
|
|
||||||
E.number_of_buffer++;
|
E.number_of_buffer++;
|
||||||
|
if (new_buf->filename[strlen(new_buf->filename) - 1] == 'c')
|
||||||
|
{
|
||||||
|
lspStart(E.lsp_client, new_buf->path);
|
||||||
|
}
|
||||||
|
|
||||||
editorSetStatusMessage("Opened: %s (buffer %d)", filename, new_buf->buffer_id);
|
editorSetStatusMessage("Opened: %s (buffer %d)", filename, new_buf->buffer_id);
|
||||||
return new_buf->buffer_id;
|
return new_buf->buffer_id;
|
||||||
@@ -478,4 +484,4 @@ void bufferInsertNewLine(void) {
|
|||||||
buf->y++;
|
buf->y++;
|
||||||
buf->x = 0;
|
buf->x = 0;
|
||||||
appDebug("Insert new line done\n");
|
appDebug("Insert new line done\n");
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-6
@@ -21,6 +21,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "include/completion.h"
|
#include "include/completion.h"
|
||||||
|
#include "include/init.h"
|
||||||
#include "include/utils.h"
|
#include "include/utils.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -68,7 +69,7 @@ Lisp mapKey(Lisp args, LispError* e, LispContext ctx)
|
|||||||
// second argument
|
// second argument
|
||||||
const Lisp func = lisp_car(args);
|
const Lisp func = lisp_car(args);
|
||||||
|
|
||||||
memory_temp = (void*)bRealloc(
|
memory_temp = bRealloc(
|
||||||
E.key_binds, ++E.number_of_keybinds * sizeof(struct keyBind_t));
|
E.key_binds, ++E.number_of_keybinds * sizeof(struct keyBind_t));
|
||||||
E.key_binds = (struct keyBind_t*)memory_temp;
|
E.key_binds = (struct keyBind_t*)memory_temp;
|
||||||
if (!E.key_binds)
|
if (!E.key_binds)
|
||||||
@@ -188,6 +189,7 @@ Lisp editorQuit(Lisp args, LispError* e, LispContext ctx)
|
|||||||
disableRawMode();
|
disableRawMode();
|
||||||
lspShutdown(E.lsp_client);
|
lspShutdown(E.lsp_client);
|
||||||
lisp_shutdown(E.ctx);
|
lisp_shutdown(E.ctx);
|
||||||
|
deInitEditor();
|
||||||
appDebug("Rest alloc %d\n", beluga_alloc_counter);
|
appDebug("Rest alloc %d\n", beluga_alloc_counter);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
@@ -218,7 +220,6 @@ Lisp l_editorSplitScreenVertical(Lisp args, LispError* e, LispContext ctx)
|
|||||||
Lisp l_editorSave(Lisp args, LispError* e, LispContext ctx)
|
Lisp l_editorSave(Lisp args, LispError* e, LispContext ctx)
|
||||||
{
|
{
|
||||||
editorSave();
|
editorSave();
|
||||||
|
|
||||||
return lisp_null();
|
return lisp_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -415,7 +416,7 @@ Lisp addPackage(Lisp args, LispError* e, LispContext ctx)
|
|||||||
{
|
{
|
||||||
const char* package_name = lisp_string(lisp_car(args));
|
const char* package_name = lisp_string(lisp_car(args));
|
||||||
appDebug("%s\n", package_name);
|
appDebug("%s\n", package_name);
|
||||||
char* package_dir = (char*)calloc(256, sizeof(char));
|
char* package_dir = calloc(256, sizeof(char));
|
||||||
FILE* fd_package = NULL;
|
FILE* fd_package = NULL;
|
||||||
strcat(package_dir, getenv("HOME"));
|
strcat(package_dir, getenv("HOME"));
|
||||||
strcat(package_dir, "/.beluga/packages/");
|
strcat(package_dir, "/.beluga/packages/");
|
||||||
@@ -557,7 +558,6 @@ Lisp editorSetPrefix(Lisp args, LispError* e, LispContext ctx)
|
|||||||
*/
|
*/
|
||||||
Lisp editorPrefix(Lisp args, LispError* e, LispContext ctx)
|
Lisp editorPrefix(Lisp args, LispError* e, LispContext ctx)
|
||||||
{
|
{
|
||||||
void * memory_temp;
|
|
||||||
E.prefix = (struct prefix_t*)bRealloc(E.prefix, (++(E.number_of_prefix) + 1) *
|
E.prefix = (struct prefix_t*)bRealloc(E.prefix, (++(E.number_of_prefix) + 1) *
|
||||||
sizeof(struct prefix_t));
|
sizeof(struct prefix_t));
|
||||||
E.prefix[E.number_of_prefix].prefix_id = E.number_of_prefix;
|
E.prefix[E.number_of_prefix].prefix_id = E.number_of_prefix;
|
||||||
@@ -610,6 +610,9 @@ Lisp editorMoveEndBuffer(Lisp args, LispError* e, LispContext ctx)
|
|||||||
|
|
||||||
Lisp editorAutoComplete(Lisp args, LispError* e, LispContext ctx)
|
Lisp editorAutoComplete(Lisp args, LispError* e, LispContext ctx)
|
||||||
{
|
{
|
||||||
|
if (!E.constantes.LSP) {
|
||||||
|
return lisp_null();
|
||||||
|
}
|
||||||
// createContextBuffer(E.cursor_x - 2, E.cursor_y + 1, "hello");
|
// createContextBuffer(E.cursor_x - 2, E.cursor_y + 1, "hello");
|
||||||
appDebug("editor-auto-complete\n");
|
appDebug("editor-auto-complete\n");
|
||||||
EditorPane* active = splitScreenGetActivePane();
|
EditorPane* active = splitScreenGetActivePane();
|
||||||
@@ -618,8 +621,8 @@ Lisp editorAutoComplete(Lisp args, LispError* e, LispContext ctx)
|
|||||||
lspRequestCompletion(
|
lspRequestCompletion(
|
||||||
E.lsp_client,
|
E.lsp_client,
|
||||||
buffer,
|
buffer,
|
||||||
active->cursor_y + active->y_offset, // file line
|
buffer->y, // file line
|
||||||
active->cursor_x + active->x_offset, // file col
|
buffer->x, // file col
|
||||||
active->cursor_x + active->origin_x + GUTTER_WIDTH, // screen x
|
active->cursor_x + active->origin_x + GUTTER_WIDTH, // screen x
|
||||||
active->cursor_y + active->origin_y // screen y
|
active->cursor_y + active->origin_y // screen y
|
||||||
);
|
);
|
||||||
@@ -628,6 +631,9 @@ Lisp editorAutoComplete(Lisp args, LispError* e, LispContext ctx)
|
|||||||
|
|
||||||
Lisp lspDefinition(Lisp args, LispError* e, LispContext ctx)
|
Lisp lspDefinition(Lisp args, LispError* e, LispContext ctx)
|
||||||
{
|
{
|
||||||
|
if (!E.constantes.LSP) {
|
||||||
|
return lisp_null();
|
||||||
|
}
|
||||||
(void)args;
|
(void)args;
|
||||||
(void)e;
|
(void)e;
|
||||||
(void)ctx;
|
(void)ctx;
|
||||||
|
|||||||
@@ -1906,6 +1906,7 @@ CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
child = array->child;
|
child = array->child;
|
||||||
|
|
||||||
while(child != NULL)
|
while(child != NULL)
|
||||||
|
|||||||
+121
-82
@@ -13,23 +13,10 @@
|
|||||||
#include "include/cJSON.h"
|
#include "include/cJSON.h"
|
||||||
#include "include/data.h"
|
#include "include/data.h"
|
||||||
#include "include/lsp_ui.h"
|
#include "include/lsp_ui.h"
|
||||||
|
#include "include/split_screen.h"
|
||||||
#include "include/terminal.h"
|
#include "include/terminal.h"
|
||||||
#include "include/utils.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)
|
static void lsp_send(int fd, const char* json)
|
||||||
{
|
{
|
||||||
int body_len = strlen(json);
|
int body_len = strlen(json);
|
||||||
@@ -42,7 +29,7 @@ static void lsp_send(int fd, const char* json)
|
|||||||
write(fd, json, body_len);
|
write(fd, json, body_len);
|
||||||
|
|
||||||
// Log to stderr for debugging
|
// Log to stderr for debugging
|
||||||
fprintf(stderr, "[LSP →] Content-Length: %d | %s\n", body_len, json);
|
appDebug("[LSP →] Content-Length: %d | %s\n", body_len, json);
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,10 +42,8 @@ static int lsp_uri_to_buffer_id(const char* uri)
|
|||||||
|
|
||||||
for (int i = 0; i < E.number_of_buffer; i++) {
|
for (int i = 0; i < E.number_of_buffer; i++) {
|
||||||
if (E.buffers[i].filename == NULL) continue;
|
if (E.buffers[i].filename == NULL) continue;
|
||||||
char abs[PATH_MAX];
|
appDebug("[URI MATCH] comparing '%s' vs '%s'\n", E.buffers[i].fullname, path);
|
||||||
realpath(E.buffers[i].filename, abs);
|
if (strcmp(E.buffers[i].fullname, path) == 0)
|
||||||
fprintf(stderr, "[URI MATCH] comparing '%s' vs '%s'\n", abs, path);
|
|
||||||
if (strcmp(abs, path) == 0)
|
|
||||||
return E.buffers[i].buffer_id;
|
return E.buffers[i].buffer_id;
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
@@ -112,7 +97,7 @@ static void lsp_dispatch(LspClient* lsp, const char* json)
|
|||||||
cJSON* root = cJSON_Parse(json);
|
cJSON* root = cJSON_Parse(json);
|
||||||
if (!root)
|
if (!root)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "[LSP ←] Failed to parse JSON: %.120s\n", json);
|
appDebug("[LSP ←] Failed to parse JSON: %.120s\n", json);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +110,7 @@ static void lsp_dispatch(LspClient* lsp, const char* json)
|
|||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
cJSON* msg = cJSON_GetObjectItem(error, "message");
|
cJSON* msg = cJSON_GetObjectItem(error, "message");
|
||||||
fprintf(stderr, "[LSP ←] ERROR: %s\n",
|
appDebug("[LSP ←] ERROR: %s\n",
|
||||||
msg ? msg->valuestring : "(no message)");
|
msg ? msg->valuestring : "(no message)");
|
||||||
cJSON_Delete(root);
|
cJSON_Delete(root);
|
||||||
return;
|
return;
|
||||||
@@ -135,17 +120,16 @@ static void lsp_dispatch(LspClient* lsp, const char* json)
|
|||||||
if (method && !id)
|
if (method && !id)
|
||||||
{
|
{
|
||||||
const char* m = method->valuestring;
|
const char* m = method->valuestring;
|
||||||
fprintf(stderr, "[LSP ←] NOTIF: %s\n", m);
|
appDebug("[LSP ←] NOTIF: %s\n", m);
|
||||||
|
|
||||||
if (strcmp(m, "textDocument/publishDiagnostics") == 0)
|
if (strcmp(m, "textDocument/publishDiagnostics") == 0)
|
||||||
{
|
{
|
||||||
// Find which buffer this diagnostic belongs to
|
// Find which buffer this diagnostic belongs to
|
||||||
cJSON* params = cJSON_GetObjectItem(root, "params");
|
cJSON* params = cJSON_GetObjectItem(root, "params");
|
||||||
cJSON* uri = cJSON_GetObjectItem(params, "uri");
|
cJSON* uri = cJSON_GetObjectItem(params, "uri");
|
||||||
int buf_id = lsp_uri_to_buffer_id(
|
int buf_id = splitScreenGetActivePane()->buffer_id;
|
||||||
uri ? uri->valuestring : "");
|
|
||||||
|
|
||||||
fprintf(stderr, "[LSP ←] Diagnostics for buffer %d\n", buf_id);
|
appDebug("[LSP ←] Diagnostics for buffer %d\n", buf_id);
|
||||||
|
|
||||||
pthread_mutex_lock(&lsp->lock);
|
pthread_mutex_lock(&lsp->lock);
|
||||||
lspParseDiagnostics(json, &E.lsp_diagnostics, buf_id);
|
lspParseDiagnostics(json, &E.lsp_diagnostics, buf_id);
|
||||||
@@ -157,7 +141,7 @@ static void lsp_dispatch(LspClient* lsp, const char* json)
|
|||||||
{
|
{
|
||||||
cJSON* params = cJSON_GetObjectItem(root, "params");
|
cJSON* params = cJSON_GetObjectItem(root, "params");
|
||||||
cJSON* message = cJSON_GetObjectItem(params, "message");
|
cJSON* message = cJSON_GetObjectItem(params, "message");
|
||||||
fprintf(stderr, "[LSP ←] LOG: %s\n",
|
appDebug("[LSP ←] LOG: %s\n",
|
||||||
message ? message->valuestring : "");
|
message ? message->valuestring : "");
|
||||||
}
|
}
|
||||||
E.lsp_client->completion_just_arrived = 1;
|
E.lsp_client->completion_just_arrived = 1;
|
||||||
@@ -171,12 +155,12 @@ static void lsp_dispatch(LspClient* lsp, const char* json)
|
|||||||
if (id && result)
|
if (id && result)
|
||||||
{
|
{
|
||||||
int response_id = id->valueint;
|
int response_id = id->valueint;
|
||||||
fprintf(stderr, "[LSP ←] RESPONSE id=%d\n", response_id);
|
appDebug("[LSP ←] RESPONSE id=%d\n", response_id);
|
||||||
|
|
||||||
// initialize response → send initialized + mark ready
|
// initialize response → send initialized + mark ready
|
||||||
if (lsp->state == LSP_INITIALIZING)
|
if (lsp->state == LSP_INITIALIZING)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "[LSP ←] Initialize OK, sending initialized\n");
|
appDebug("[LSP ←] Initialize OK, sending initialized\n");
|
||||||
lsp_send(lsp->write_fd,
|
lsp_send(lsp->write_fd,
|
||||||
"{\"jsonrpc\":\"2.0\",\"method\":\"initialized\",\"params\":{}}");
|
"{\"jsonrpc\":\"2.0\",\"method\":\"initialized\",\"params\":{}}");
|
||||||
|
|
||||||
@@ -195,7 +179,7 @@ static void lsp_dispatch(LspClient* lsp, const char* json)
|
|||||||
if (items && cJSON_IsArray(items))
|
if (items && cJSON_IsArray(items))
|
||||||
{
|
{
|
||||||
int count = cJSON_GetArraySize(items);
|
int count = cJSON_GetArraySize(items);
|
||||||
fprintf(stderr, "[LSP ←] Completion: %d items\n", count);
|
appDebug("[LSP ←] Completion: %d items\n", count);
|
||||||
|
|
||||||
// Print each item to stderr for debugging
|
// Print each item to stderr for debugging
|
||||||
cJSON* item;
|
cJSON* item;
|
||||||
@@ -205,14 +189,14 @@ static void lsp_dispatch(LspClient* lsp, const char* json)
|
|||||||
cJSON* label = cJSON_GetObjectItem(item, "label");
|
cJSON* label = cJSON_GetObjectItem(item, "label");
|
||||||
cJSON* detail = cJSON_GetObjectItem(item, "detail");
|
cJSON* detail = cJSON_GetObjectItem(item, "detail");
|
||||||
cJSON* kind = cJSON_GetObjectItem(item, "kind");
|
cJSON* kind = cJSON_GetObjectItem(item, "kind");
|
||||||
fprintf(stderr, " [%d] kind=%-2d %-40s %s\n",
|
appDebug(" [%d] kind=%-2d %-40s %s\n",
|
||||||
i++,
|
i++,
|
||||||
kind ? kind->valueint : 0,
|
kind ? kind->valueint : 0,
|
||||||
label ? label->valuestring : "(no label)",
|
label ? label->valuestring : "(no label)",
|
||||||
detail ? detail->valuestring : "");
|
detail ? detail->valuestring : "");
|
||||||
if (i >= 10)
|
if (i >= 10)
|
||||||
{
|
{
|
||||||
fprintf(stderr, " ... (%d more)\n", count - 10);
|
appDebug(" ... (%d more)\n", count - 10);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -224,7 +208,7 @@ static void lsp_dispatch(LspClient* lsp, const char* json)
|
|||||||
pthread_mutex_unlock(&lsp->lock);
|
pthread_mutex_unlock(&lsp->lock);
|
||||||
E.lsp_client->completion_just_arrived = 1;
|
E.lsp_client->completion_just_arrived = 1;
|
||||||
|
|
||||||
fprintf(stderr, "[POPUP] visible=%d count=%d origin=(%d,%d)\n",
|
appDebug("[POPUP] visible=%d count=%d origin=(%d,%d)\n",
|
||||||
E.lsp_completion.visible,
|
E.lsp_completion.visible,
|
||||||
E.lsp_completion.count,
|
E.lsp_completion.count,
|
||||||
E.lsp_completion.origin_x,
|
E.lsp_completion.origin_x,
|
||||||
@@ -243,7 +227,7 @@ static void lsp_dispatch(LspClient* lsp, const char* json)
|
|||||||
cJSON* start = cJSON_GetObjectItem(range, "start");
|
cJSON* start = cJSON_GetObjectItem(range, "start");
|
||||||
int line = cJSON_GetObjectItem(start, "line")->valueint;
|
int line = cJSON_GetObjectItem(start, "line")->valueint;
|
||||||
int col = cJSON_GetObjectItem(start, "character")->valueint;
|
int col = cJSON_GetObjectItem(start, "character")->valueint;
|
||||||
fprintf(stderr, "[LSP ←] Definition: %s:%d:%d\n",
|
appDebug("[LSP ←] Definition: %s:%d:%d\n",
|
||||||
uri_item->valuestring, line, col);
|
uri_item->valuestring, line, col);
|
||||||
E.lsp_client->completion_just_arrived = 1;
|
E.lsp_client->completion_just_arrived = 1;
|
||||||
|
|
||||||
@@ -252,7 +236,7 @@ static void lsp_dispatch(LspClient* lsp, const char* json)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "[LSP ←] Unhandled response id=%d: %.80s\n",
|
appDebug("[LSP ←] Unhandled response id=%d: %.80s\n",
|
||||||
response_id, json);
|
response_id, json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,63 +258,122 @@ static void* lsp_reader(void* arg)
|
|||||||
|
|
||||||
// ─── lifecycle ───────────────────────────────────────────────────────────────
|
// ─── lifecycle ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
int lspStart(LspClient* lsp, const char* project_root)
|
int lspStart(LspClient *lsp, const char *project_root)
|
||||||
{
|
{
|
||||||
|
// ── Pipes ─────────────────────────────────────────────────────────────────
|
||||||
int to_clangd[2], from_clangd[2];
|
int to_clangd[2], from_clangd[2];
|
||||||
pipe(to_clangd);
|
|
||||||
pipe(from_clangd);
|
|
||||||
|
|
||||||
pipe(lsp->wake_pipe);
|
if (pipe(to_clangd) < 0 || pipe(from_clangd) < 0) {
|
||||||
|
fprintf(stderr, "[LSP] pipe() failed\n");
|
||||||
|
free(lsp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (pipe(lsp->wake_pipe) < 0) {
|
||||||
|
fprintf(stderr, "[LSP] wake pipe() failed\n");
|
||||||
|
free(lsp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Fork clangd ───────────────────────────────────────────────────────────
|
||||||
lsp->pid = fork();
|
lsp->pid = fork();
|
||||||
if (lsp->pid == 0)
|
if (lsp->pid < 0) {
|
||||||
{
|
fprintf(stderr, "[LSP] fork() failed\n");
|
||||||
// Child: become clangd
|
free(lsp);
|
||||||
dup2(to_clangd[0], STDIN_FILENO);
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lsp->pid == 0) {
|
||||||
|
// Child — become clangd
|
||||||
|
dup2(to_clangd[0], STDIN_FILENO);
|
||||||
dup2(from_clangd[1], STDOUT_FILENO);
|
dup2(from_clangd[1], STDOUT_FILENO);
|
||||||
close(to_clangd[1]);
|
close(to_clangd[1]);
|
||||||
close(from_clangd[0]);
|
close(from_clangd[0]);
|
||||||
execlp("clangd", "clangd", "--log=error", "--completion-style=detailed", NULL);
|
close(lsp->wake_pipe[0]);
|
||||||
_exit(1); // clangd not found
|
close(lsp->wake_pipe[1]);
|
||||||
|
execlp("clangd", "clangd",
|
||||||
|
"--log=error",
|
||||||
|
"--completion-style=detailed",
|
||||||
|
NULL);
|
||||||
|
fprintf(stderr, "[LSP] execlp failed — is clangd installed?\n");
|
||||||
|
_exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parent — keep write end of to_clangd, read end of from_clangd
|
||||||
close(to_clangd[0]);
|
close(to_clangd[0]);
|
||||||
close(from_clangd[1]);
|
close(from_clangd[1]);
|
||||||
lsp->write_fd = to_clangd[1];
|
lsp->write_fd = to_clangd[1];
|
||||||
lsp->read_fd = from_clangd[0];
|
lsp->read_fd = from_clangd[0];
|
||||||
lsp->next_id = 1;
|
lsp->next_id = 1;
|
||||||
lsp->state = LSP_INITIALIZING;
|
lsp->state = LSP_INITIALIZING;
|
||||||
pthread_mutex_init(&lsp->lock, NULL);
|
|
||||||
|
|
||||||
// Send initialize
|
// ── Threading ─────────────────────────────────────────────────────────────
|
||||||
char buf[1024];
|
pthread_mutex_init(&lsp->lock, NULL);
|
||||||
snprintf(buf, sizeof(buf),
|
pthread_cond_init (&lsp->ready_cond, NULL);
|
||||||
"{\"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);
|
// Start reader thread BEFORE sending initialize
|
||||||
pthread_cond_init(&lsp->ready_cond, NULL);
|
// so it can handle the response
|
||||||
pthread_create(&lsp->reader_thread, NULL, lsp_reader, lsp);
|
pthread_create(&lsp->reader_thread, NULL, lsp_reader, lsp);
|
||||||
|
|
||||||
|
// ── Send initialize ───────────────────────────────────────────────────────
|
||||||
|
char abs_root[PATH_MAX];
|
||||||
|
if (realpath(project_root, abs_root) == NULL)
|
||||||
|
strncpy(abs_root, project_root, PATH_MAX - 1);
|
||||||
|
|
||||||
lsp_send(lsp->write_fd, buf);
|
cJSON *req = cJSON_CreateObject();
|
||||||
|
cJSON *params = cJSON_CreateObject();
|
||||||
|
cJSON *caps = cJSON_CreateObject();
|
||||||
|
cJSON *td_caps = cJSON_CreateObject();
|
||||||
|
cJSON *comp_caps = cJSON_CreateObject();
|
||||||
|
cJSON *comp_item = cJSON_CreateObject();
|
||||||
|
|
||||||
|
cJSON_AddStringToObject(req, "jsonrpc", "2.0");
|
||||||
|
cJSON_AddNumberToObject(req, "id", lsp->next_id++);
|
||||||
|
cJSON_AddStringToObject(req, "method", "initialize");
|
||||||
|
|
||||||
|
// rootUri
|
||||||
|
char root_uri[PATH_MAX + 8];
|
||||||
|
snprintf(root_uri, sizeof(root_uri), "file://%s", abs_root);
|
||||||
|
cJSON_AddNumberToObject(params, "processId", getpid());
|
||||||
|
cJSON_AddStringToObject(params, "rootUri", root_uri);
|
||||||
|
|
||||||
|
// Capabilities — tell clangd what we support
|
||||||
|
cJSON_AddBoolToObject (comp_item, "snippetSupport", 0);
|
||||||
|
cJSON_AddBoolToObject (comp_item, "commitCharactersSupport", 0);
|
||||||
|
cJSON_AddItemToObject (comp_caps, "completionItem", comp_item);
|
||||||
|
cJSON_AddItemToObject (td_caps, "completion", comp_caps);
|
||||||
|
cJSON_AddItemToObject (td_caps, "hover", cJSON_CreateObject());
|
||||||
|
cJSON_AddItemToObject (td_caps, "definition", cJSON_CreateObject());
|
||||||
|
cJSON_AddItemToObject (td_caps, "publishDiagnostics", cJSON_CreateObject());
|
||||||
|
cJSON_AddItemToObject (caps, "textDocument", td_caps);
|
||||||
|
cJSON_AddItemToObject (params, "capabilities", caps);
|
||||||
|
cJSON_AddItemToObject (req, "params", params);
|
||||||
|
|
||||||
|
char *msg = cJSON_PrintUnformatted(req);
|
||||||
|
lsp_send(lsp->write_fd, msg);
|
||||||
|
free(msg);
|
||||||
|
cJSON_Delete(req);
|
||||||
|
|
||||||
|
// ── Wait for LSP_READY ────────────────────────────────────────────────────
|
||||||
|
// Reader thread will handle the initialize response,
|
||||||
|
// send "initialized", and signal ready_cond
|
||||||
pthread_mutex_lock(&lsp->lock);
|
pthread_mutex_lock(&lsp->lock);
|
||||||
while (lsp->state != LSP_READY)
|
while (lsp->state != LSP_READY) {
|
||||||
pthread_cond_wait(&lsp->ready_cond, &lsp->lock);
|
struct timespec ts;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &ts);
|
||||||
|
ts.tv_sec += 5; // 5 second timeout — clangd should respond fast
|
||||||
|
int rc = pthread_cond_timedwait(&lsp->ready_cond, &lsp->lock, &ts);
|
||||||
|
if (rc == ETIMEDOUT) {
|
||||||
|
fprintf(stderr, "[LSP] timeout waiting for initialize response\n");
|
||||||
|
pthread_mutex_unlock(&lsp->lock);
|
||||||
|
// Don't kill clangd — it might still come up, just return what we have
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
pthread_mutex_unlock(&lsp->lock);
|
pthread_mutex_unlock(&lsp->lock);
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
fprintf(stderr, "[LSP] ready — clangd initialized at %s\n", abs_root);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
// ─── document sync ───────────────────────────────────────────────────────────
|
// ─── document sync ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
// Build the full buffer text into a bAlloc'd string
|
// Build the full buffer text into a bAlloc'd string
|
||||||
@@ -355,10 +398,9 @@ void lspDidOpen(LspClient* lsp, struct buffer_t* buf)
|
|||||||
{
|
{
|
||||||
if (lsp->state != LSP_READY || buf->b_lsp_open) return;
|
if (lsp->state != LSP_READY || buf->b_lsp_open) return;
|
||||||
|
|
||||||
char abs[PATH_MAX];
|
|
||||||
realpath(buf->filename, abs);
|
|
||||||
char uri[PATH_MAX + 8];
|
char uri[PATH_MAX + 8];
|
||||||
snprintf(uri, sizeof(uri), "file://%s", abs);
|
snprintf(uri, sizeof(uri), "file://%s", buf->fullname);
|
||||||
|
|
||||||
const char* lang = "c";
|
const char* lang = "c";
|
||||||
if (strstr(buf->filename, ".cpp") || strstr(buf->filename, ".cc"))
|
if (strstr(buf->filename, ".cpp") || strstr(buf->filename, ".cc"))
|
||||||
@@ -395,10 +437,9 @@ void lspDidChange(LspClient* lsp, struct buffer_t* buf)
|
|||||||
{
|
{
|
||||||
if (lsp->state != LSP_READY || !buf->b_lsp_open) return;
|
if (lsp->state != LSP_READY || !buf->b_lsp_open) return;
|
||||||
|
|
||||||
char abs[PATH_MAX];
|
|
||||||
realpath(buf->filename, abs);
|
|
||||||
char uri[PATH_MAX + 8];
|
char uri[PATH_MAX + 8];
|
||||||
snprintf(uri, sizeof(uri), "file://%s", abs);
|
snprintf(uri, sizeof(uri), "file://%s", buf->fullname);
|
||||||
|
|
||||||
char* raw = buffer_to_text(buf);
|
char* raw = buffer_to_text(buf);
|
||||||
|
|
||||||
@@ -431,10 +472,9 @@ void lspDidChange(LspClient* lsp, struct buffer_t* buf)
|
|||||||
|
|
||||||
void lspDidClose(LspClient* lsp, struct buffer_t* buf)
|
void lspDidClose(LspClient* lsp, struct buffer_t* buf)
|
||||||
{
|
{
|
||||||
char abs[PATH_MAX];
|
|
||||||
realpath(buf->filename, abs);
|
|
||||||
char uri[PATH_MAX + 8];
|
char uri[PATH_MAX + 8];
|
||||||
snprintf(uri, sizeof(uri), "file://%s", abs);
|
snprintf(uri, sizeof(uri), "file://%s", buf->fullname);
|
||||||
|
|
||||||
cJSON* root = cJSON_CreateObject();
|
cJSON* root = cJSON_CreateObject();
|
||||||
cJSON* params = cJSON_CreateObject();
|
cJSON* params = cJSON_CreateObject();
|
||||||
@@ -458,16 +498,15 @@ void lspRequestCompletion(LspClient* lsp, struct buffer_t* buf,
|
|||||||
int screen_x, int screen_y)
|
int screen_x, int screen_y)
|
||||||
{
|
{
|
||||||
if (lsp->state != LSP_READY) return;
|
if (lsp->state != LSP_READY) return;
|
||||||
lsp->completion_cursor_x = screen_x; // ← add
|
lsp->completion_cursor_x = screen_x;
|
||||||
lsp->completion_cursor_y = screen_y;
|
lsp->completion_cursor_y = screen_y;
|
||||||
|
|
||||||
appDebug("LSP REQUEST COMP");
|
appDebug("LSP REQUEST COMP");
|
||||||
|
|
||||||
char* msg;
|
char* msg;
|
||||||
char abs[PATH_MAX];
|
|
||||||
realpath(buf->filename, abs);
|
|
||||||
char uri[PATH_MAX + 8];
|
char uri[PATH_MAX + 8];
|
||||||
snprintf(uri, sizeof(uri), "file://%s", abs);
|
snprintf(uri, sizeof(uri), "file://%s", buf->fullname);
|
||||||
|
appDebug("FULLNAME : %s\n", buf->fullname);
|
||||||
|
|
||||||
cJSON* req = cJSON_CreateObject();
|
cJSON* req = cJSON_CreateObject();
|
||||||
cJSON* params = cJSON_CreateObject();
|
cJSON* params = cJSON_CreateObject();
|
||||||
|
|||||||
@@ -74,6 +74,8 @@ int editorMoveCursor(int key) {
|
|||||||
buf->x = buf->row[buf->y].size;
|
buf->x = buf->row[buf->y].size;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-6
@@ -13,18 +13,16 @@
|
|||||||
#include "../include/data.h"
|
#include "../include/data.h"
|
||||||
#include "../include/split_screen.h"
|
#include "../include/split_screen.h"
|
||||||
#include "../include/row_op.h"
|
#include "../include/row_op.h"
|
||||||
|
#include <_string.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include "../include/utils.h"
|
#include "../include/utils.h"
|
||||||
|
|
||||||
extern struct editorConfig E;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Closes the current file and resets editor state
|
* @brief Closes the current file and resets editor state
|
||||||
* @details Clears all rows, resets cursor position, scroll offsets, and file
|
* @details Clears all rows, resets cursor position, scroll offsets, and file
|
||||||
@@ -56,7 +54,7 @@ void editorCloseFile(void) {
|
|||||||
* @details Loads file content into editor rows, one line per row. If another
|
* @details Loads file content into editor rows, one line per row. If another
|
||||||
* file is already open, it is closed first (without saving). File is opened in
|
* file is already open, it is closed first (without saving). File is opened in
|
||||||
* a+ (read/append) mode to allow both reading and modification.
|
* a+ (read/append) mode to allow both reading and modification.
|
||||||
* @param filename Path to the file to open (relative or absolute)
|
* @param buffer Path to the file to open (relative or absolute)
|
||||||
* @note Updates global editor state E
|
* @note Updates global editor state E
|
||||||
* @note Calls die() on file open failure
|
* @note Calls die() on file open failure
|
||||||
* @note Newline characters are stripped from loaded lines
|
* @note Newline characters are stripped from loaded lines
|
||||||
@@ -118,11 +116,11 @@ void editorSave() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fd = open(buffer->filename, O_RDWR | O_CREAT, 0644);
|
fd = open(buffer->fullname, O_RDWR | O_CREAT, 0644);
|
||||||
if (fd != -1) {
|
if (fd != -1) {
|
||||||
for (int i = 0; i < buffer->numrows; ++i)
|
for (int i = 0; i < buffer->numrows; ++i)
|
||||||
{
|
{
|
||||||
len = strlen(buffer->row[i].chars);
|
len = (int) strlen(buffer->row[i].chars);
|
||||||
if (write(fd, buffer->row[i].chars, len) != len) {
|
if (write(fd, buffer->row[i].chars, len) != len) {
|
||||||
close(fd);
|
close(fd);
|
||||||
editorSetStatusMessage("Can't save! I/O error: %s", strerror(errno));
|
editorSetStatusMessage("Can't save! I/O error: %s", strerror(errno));
|
||||||
|
|||||||
+18
-1
@@ -75,6 +75,8 @@ void initConfig() {
|
|||||||
void initTheme() {
|
void initTheme() {
|
||||||
E.constantes.THEME = (char *)lisp_string(
|
E.constantes.THEME = (char *)lisp_string(
|
||||||
lisp_eval(lisp_read("THEME", &E.ctx_error, E.ctx), &E.ctx_error, E.ctx));
|
lisp_eval(lisp_read("THEME", &E.ctx_error, E.ctx), &E.ctx_error, E.ctx));
|
||||||
|
E.constantes.LSP = lisp_bool(lisp_eval(lisp_read("LSP", &E.ctx_error, E.ctx), &E.ctx_error, E.ctx));
|
||||||
|
appDebug("LSP ON : %d", E.constantes.LSP);
|
||||||
if (strcmp(E.constantes.THEME, "dark") == 0) {
|
if (strcmp(E.constantes.THEME, "dark") == 0) {
|
||||||
E.theme.BACKGROUND_COLOR = ANSI_BG_RGB(40, 44, 52);
|
E.theme.BACKGROUND_COLOR = ANSI_BG_RGB(40, 44, 52);
|
||||||
E.theme.COLOR_KEYWORD = ANSI_FG_RGB(198, 120, 221);
|
E.theme.COLOR_KEYWORD = ANSI_FG_RGB(198, 120, 221);
|
||||||
@@ -99,7 +101,7 @@ void initEditor() {
|
|||||||
if (getWindowSize(&E.screenrows, &E.screencols) == -1) {
|
if (getWindowSize(&E.screenrows, &E.screencols) == -1) {
|
||||||
die("getWindowSize");
|
die("getWindowSize");
|
||||||
}
|
}
|
||||||
appDebug("%d %d\n", E.screenrows, E.screencols);
|
appDebug("%d %d", E.screenrows, E.screencols);
|
||||||
E.screenrows -= 2;
|
E.screenrows -= 2;
|
||||||
|
|
||||||
|
|
||||||
@@ -137,6 +139,7 @@ void initEditor() {
|
|||||||
E.prefix_state = 0;
|
E.prefix_state = 0;
|
||||||
|
|
||||||
E.lsp_client = (LspClient*)bAlloc(sizeof(LspClient));
|
E.lsp_client = (LspClient*)bAlloc(sizeof(LspClient));
|
||||||
|
E.lsp_client->state = LSP_SHUTDOWN;
|
||||||
|
|
||||||
initConfig();
|
initConfig();
|
||||||
initTheme();
|
initTheme();
|
||||||
@@ -154,3 +157,17 @@ void initEditor() {
|
|||||||
|
|
||||||
E.quit_times_buffer = E.constantes.QUIT_TIMES;
|
E.quit_times_buffer = E.constantes.QUIT_TIMES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void deInitEditor()
|
||||||
|
{
|
||||||
|
freeScreenLayout(&E.layout);
|
||||||
|
bFree(E.lsp_client);
|
||||||
|
bFree(E.status_msg);
|
||||||
|
bFree(E.init_file_path);
|
||||||
|
bFree(E.key_binds);
|
||||||
|
for (int i = 0; i < E.number_of_keybinds; i++)
|
||||||
|
{
|
||||||
|
bFree(E.key_binds[i].key_sequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
+67
-56
@@ -2,32 +2,27 @@
|
|||||||
#include "../include/define.h"
|
#include "../include/define.h"
|
||||||
#include "../include/editor_op.h"
|
#include "../include/editor_op.h"
|
||||||
#include "../include/output.h"
|
#include "../include/output.h"
|
||||||
#include "include/data.h"
|
|
||||||
#include "include/buffer.h"
|
#include "include/buffer.h"
|
||||||
|
#include "include/builtins.h"
|
||||||
|
#include "../include/completion.h"
|
||||||
#include "include/data.h"
|
#include "include/data.h"
|
||||||
#include "include/split_screen.h"
|
#include "include/split_screen.h"
|
||||||
#include "include/completion.h"
|
|
||||||
#include "include/lsp_ui.h"
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "include/terminal.h"
|
#include "include/terminal.h"
|
||||||
#include "include/utf8.h"
|
#include "include/utf8.h"
|
||||||
#include "include/utils.h"
|
#include "include/utils.h"
|
||||||
|
|
||||||
extern struct editorConfig E;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file input.c
|
* @file input.c
|
||||||
* @brief Input handling module for the Beluga text editor
|
* @brief Input handling module for the Beluga text editor
|
||||||
* @details Manages user input processing, key bindings, cursor movement, and file path completion
|
* @details Manages user input processing, key bindings, cursor movement, and
|
||||||
|
* file path completion
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,7 +31,8 @@ extern struct editorConfig E;
|
|||||||
* the first file or directory entry that matches the filename prefix.
|
* the first file or directory entry that matches the filename prefix.
|
||||||
* Appends a trailing slash for directory entries.
|
* Appends a trailing slash for directory entries.
|
||||||
* @param path The file path to complete (can be relative or absolute)
|
* @param path The file path to complete (can be relative or absolute)
|
||||||
* @return Pointer to the completed file path (dynamically allocated), or NULL if:
|
* @return Pointer to the completed file path (dynamically allocated), or NULL
|
||||||
|
* if:
|
||||||
* - path ends with '/' (already a directory)
|
* - path ends with '/' (already a directory)
|
||||||
* - no matching entries found
|
* - no matching entries found
|
||||||
* - directory cannot be opened
|
* - directory cannot be opened
|
||||||
@@ -63,7 +59,7 @@ const char *fileCompletion(const char *path) {
|
|||||||
if (last_slash) {
|
if (last_slash) {
|
||||||
dir_len = last_slash - path + 1; // length of dir_path
|
dir_len = last_slash - path + 1; // length of dir_path
|
||||||
strncpy(directory, path, dir_len);
|
strncpy(directory, path, dir_len);
|
||||||
predict_len = strlen(path) - dir_len;
|
predict_len = (int)(strlen(path) - dir_len);
|
||||||
strncpy(predict, last_slash + 1, predict_len);
|
strncpy(predict, last_slash + 1, predict_len);
|
||||||
directory[dir_len] = '\0';
|
directory[dir_len] = '\0';
|
||||||
predict[predict_len] = '\0';
|
predict[predict_len] = '\0';
|
||||||
@@ -102,9 +98,9 @@ const char *fileCompletion(const char *path) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Displays an interactive prompt and returns user input
|
* @brief Displays an interactive prompt and returns user input
|
||||||
* @details Allows the user to enter text in a prompt with optional path completion
|
* @details Allows the user to enter text in a prompt with optional path
|
||||||
* via Tab key. Supports backspace, delete, and escape key handling. Dynamically
|
* completion via Tab key. Supports backspace, delete, and escape key handling.
|
||||||
* allocates memory for the input buffer.
|
* Dynamically allocates memory for the input buffer.
|
||||||
* @param prompt The prompt message format string (printf-style)
|
* @param prompt The prompt message format string (printf-style)
|
||||||
* @param placeHolder Initial text to display in the input buffer
|
* @param placeHolder Initial text to display in the input buffer
|
||||||
* @param bPathMode If non-zero, enables Tab key file path completion
|
* @param bPathMode If non-zero, enables Tab key file path completion
|
||||||
@@ -154,8 +150,7 @@ char *editorPrompt(char *prompt, char *placeHolder, char bPathMode) {
|
|||||||
strcpy(path, buf);
|
strcpy(path, buf);
|
||||||
}
|
}
|
||||||
memset(buf, 0, 256);
|
memset(buf, 0, 256);
|
||||||
buf_len = 0;
|
char *buf_complete = (char *)fileCompletion(path);
|
||||||
char * buf_complete = (char *) fileCompletion(path);
|
|
||||||
strcpy(buf, buf_complete);
|
strcpy(buf, buf_complete);
|
||||||
bFree(buf_complete);
|
bFree(buf_complete);
|
||||||
buf_len = strlen(buf);
|
buf_len = strlen(buf);
|
||||||
@@ -166,14 +161,12 @@ char *editorPrompt(char *prompt, char *placeHolder, char bPathMode) {
|
|||||||
buf_size *= 2;
|
buf_size *= 2;
|
||||||
buf = bRealloc(buf, buf_size);
|
buf = bRealloc(buf, buf_size);
|
||||||
}
|
}
|
||||||
buf[buf_len++] = c;
|
buf[buf_len++] = (char)c;
|
||||||
buf[buf_len] = '\0';
|
buf[buf_len] = '\0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Executes the command bound to a key sequence
|
* @brief Executes the command bound to a key sequence
|
||||||
* @details Searches the keybinding table for a matching key sequence and
|
* @details Searches the keybinding table for a matching key sequence and
|
||||||
@@ -210,56 +203,62 @@ int executeKeyBind(char *key_sequence) {
|
|||||||
* and either executes the bound command or inserts the character. Resets
|
* and either executes the bound command or inserts the character. Resets
|
||||||
* the quit buffer counter on successful key processing.
|
* the quit buffer counter on successful key processing.
|
||||||
* @note Updates global editor state E
|
* @note Updates global editor state E
|
||||||
* @note Calls editorReadKey() to get input and editorInsertChar() for unbound keys
|
* @note Calls editorReadKey() to get input and editorInsertChar() for unbound
|
||||||
|
* keys
|
||||||
*/
|
*/
|
||||||
void editorProcessKeypress() {
|
void editorProcessKeypress() {
|
||||||
int c = editorReadKey();
|
int c = editorReadKey();
|
||||||
char key_sequence[8];
|
char key_sequence[8];
|
||||||
|
|
||||||
if (E.lsp_client && E.lsp_completion.visible) {
|
if (E.constantes.LSP) {
|
||||||
if (c == ARROW_UP || c == CTRL_KEY('p')) {
|
if (c == LSP_WAKE_KEY)
|
||||||
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;
|
return;
|
||||||
}
|
if (E.lsp_client && E.lsp_completion.visible) {
|
||||||
if (c == '\r') {
|
if (c == ARROW_UP || c == CTRL_KEY('p')) {
|
||||||
CompletionItem *item = &E.lsp_completion.items[E.lsp_completion.selected];
|
if (E.lsp_completion.selected > 0)
|
||||||
EditorPane *active = splitScreenGetActivePane();
|
E.lsp_completion.selected--;
|
||||||
struct buffer_t *buf = bufferFindById(active->buffer_id);
|
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
|
// Find how many chars the user already typed by looking at the
|
||||||
// current word (chars before cursor on the same line)
|
// current word (chars before cursor on the same line)
|
||||||
int file_col = active->cursor_x + active->x_offset;
|
int file_col = active->cursor_x + active->x_offset;
|
||||||
row_t *row = &buf->row[active->cursor_y + active->y_offset];
|
row_t *row = &buf->row[active->cursor_y + active->y_offset];
|
||||||
|
|
||||||
// Walk backwards from cursor to find start of current word
|
// Walk backwards from cursor to find start of current word
|
||||||
int word_start = file_col;
|
int word_start = file_col;
|
||||||
while (word_start > 0 &&
|
while (word_start > 0 &&
|
||||||
(isalnum((unsigned char)row->chars[word_start - 1]) ||
|
(isalnum((unsigned char)row->chars[word_start - 1]) ||
|
||||||
row->chars[word_start - 1] == '_'))
|
row->chars[word_start - 1] == '_'))
|
||||||
word_start--;
|
word_start--;
|
||||||
|
|
||||||
int already_typed = file_col - word_start; // chars user already typed
|
int already_typed = file_col - word_start; // chars user already typed
|
||||||
|
|
||||||
// Insert only the suffix — what comes after what's already typed
|
// Insert only the suffix — what comes after what's already typed
|
||||||
const char *suffix = item->label + already_typed;
|
const char *suffix = item->label + already_typed;
|
||||||
for (int i = 0; suffix[i]; i++)
|
for (int i = 0; suffix[i]; i++)
|
||||||
bufferInsertBytes(&suffix[i], 1);
|
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;
|
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))) {
|
if (executeKeyBind(keyToString(c))) {
|
||||||
@@ -268,5 +267,17 @@ void editorProcessKeypress() {
|
|||||||
int seq_len = utf8Encode(c, key_sequence);
|
int seq_len = utf8Encode(c, key_sequence);
|
||||||
appDebug("key seq : %s\n", key_sequence);
|
appDebug("key seq : %s\n", key_sequence);
|
||||||
bufferInsertBytes(key_sequence, seq_len);
|
bufferInsertBytes(key_sequence, seq_len);
|
||||||
|
if (E.constantes.LSP && is_word_char(key_sequence)) {
|
||||||
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
|
struct buffer_t *buffer = bufferFindById(active->buffer_id);
|
||||||
|
|
||||||
|
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;
|
||||||
|
editorAutoComplete(lisp_null(), &E.ctx_error, E.ctx);
|
||||||
|
}
|
||||||
E.quit_times_buffer = E.constantes.QUIT_TIMES;
|
E.quit_times_buffer = E.constantes.QUIT_TIMES;
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-16
@@ -5,12 +5,12 @@
|
|||||||
#include "include/lsp_ui.h"
|
#include "include/lsp_ui.h"
|
||||||
#include "include/data.h"
|
#include "include/data.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
// We use cJSON — drop cJSON.c + cJSON.h into src/ and include/
|
// We use cJSON — drop cJSON.c + cJSON.h into src/ and include/
|
||||||
#include "include/cJSON.h"
|
#include "include/cJSON.h"
|
||||||
#include "include/append_buffer.h"
|
#include "include/append_buffer.h"
|
||||||
|
#include "include/terminal.h"
|
||||||
|
|
||||||
// ─── ANSI helpers ─────────────────────────────────────────────────────────────
|
// ─── ANSI helpers ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ static const char *kind_color(int k) {
|
|||||||
// editorRefreshScreen() before the final write().
|
// editorRefreshScreen() before the final write().
|
||||||
|
|
||||||
void lspUiDrawCompletion(struct abuf *ab, CompletionPopup *popup) {
|
void lspUiDrawCompletion(struct abuf *ab, CompletionPopup *popup) {
|
||||||
fprintf(stderr, "[DRAW] visible=%d count=%d origin=(%d,%d)\n",
|
appDebug("[DRAW] visible=%d count=%d origin=(%d,%d)\n",
|
||||||
popup->visible, popup->count,
|
popup->visible, popup->count,
|
||||||
popup->origin_x, popup->origin_y);
|
popup->origin_x, popup->origin_y);
|
||||||
if (!popup->visible || popup->count == 0) return;
|
if (!popup->visible || popup->count == 0) return;
|
||||||
@@ -104,7 +104,7 @@ void lspUiDrawCompletion(struct abuf *ab, CompletionPopup *popup) {
|
|||||||
if (is_sel) ab_sgr(ab, "7"); // reverse video (highlight)
|
if (is_sel) ab_sgr(ab, "7"); // reverse video (highlight)
|
||||||
else ab_sgr(ab, "0;37;40");
|
else ab_sgr(ab, "0;37;40");
|
||||||
|
|
||||||
abAppend(ab, "│ ", 2);
|
abAppend(ab, "| ", 2);
|
||||||
|
|
||||||
// kind tag in color
|
// kind tag in color
|
||||||
if (!is_sel) ab_sgr(ab, kind_color(it->kind));
|
if (!is_sel) ab_sgr(ab, kind_color(it->kind));
|
||||||
@@ -131,7 +131,7 @@ void lspUiDrawCompletion(struct abuf *ab, CompletionPopup *popup) {
|
|||||||
AB_RESET(ab);
|
AB_RESET(ab);
|
||||||
if (is_sel) ab_sgr(ab, "7");
|
if (is_sel) ab_sgr(ab, "7");
|
||||||
else ab_sgr(ab, "37;40");
|
else ab_sgr(ab, "37;40");
|
||||||
abAppend(ab, " │", 2);
|
abAppend(ab, " |", 2);
|
||||||
AB_RESET(ab);
|
AB_RESET(ab);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,12 +162,8 @@ int lspUiHandleKey(CompletionPopup *popup, int key) {
|
|||||||
if (!popup->visible) return 0;
|
if (!popup->visible) return 0;
|
||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case '\x1b': // ESC — dismiss
|
case '\x1b':
|
||||||
popup->visible = 0;
|
case '\r': // ESC — dismiss
|
||||||
return 1;
|
|
||||||
|
|
||||||
case '\r': // Enter — accept
|
|
||||||
// Caller should insert popup->items[popup->selected].label
|
|
||||||
popup->visible = 0;
|
popup->visible = 0;
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
@@ -248,14 +244,13 @@ void lspParseCompletion(const char *json, CompletionPopup *popup,
|
|||||||
int screen_x, int screen_y) {
|
int screen_x, int screen_y) {
|
||||||
popup->count = 0;
|
popup->count = 0;
|
||||||
popup->selected = 0;
|
popup->selected = 0;
|
||||||
popup->origin_x = screen_x;
|
popup->origin_x = screen_x + 1;
|
||||||
popup->origin_y = screen_y + 1; // one row below cursor
|
popup->origin_y = screen_y + 2; // one row below cursor
|
||||||
|
|
||||||
cJSON *root = cJSON_Parse(json);
|
cJSON *root = cJSON_Parse(json);
|
||||||
if (!root) return;
|
if (!root) return;
|
||||||
cJSON *result = cJSON_GetObjectItem(root, "result");
|
cJSON *result = cJSON_GetObjectItem(root, "result");
|
||||||
if (!result) { cJSON_Delete(root); return; }
|
if (!result) { cJSON_Delete(root); return; }
|
||||||
|
|
||||||
// result can be a list or {isIncomplete, items:[…]}
|
// result can be a list or {isIncomplete, items:[…]}
|
||||||
cJSON *items = cJSON_IsArray(result)
|
cJSON *items = cJSON_IsArray(result)
|
||||||
? result
|
? result
|
||||||
@@ -276,7 +271,11 @@ void lspParseCompletion(const char *json, CompletionPopup *popup,
|
|||||||
|
|
||||||
strncpy(ci->label, raw_label, 127);
|
strncpy(ci->label, raw_label, 127);
|
||||||
strncpy(ci->detail, detail ? detail->valuestring : "", 63);
|
strncpy(ci->detail, detail ? detail->valuestring : "", 63);
|
||||||
ci->kind = kind ? kind->valueint : 0;
|
|
||||||
|
if (!kind)
|
||||||
|
ci->kind = 0;
|
||||||
|
else
|
||||||
|
ci->kind = kind->valueint;
|
||||||
}
|
}
|
||||||
|
|
||||||
popup->visible = (popup->count > 0);
|
popup->visible = (popup->count > 0);
|
||||||
@@ -317,8 +316,15 @@ void lspParseDiagnostics(const char *json, DiagnosticList *diags,
|
|||||||
diag->col_end = cJSON_GetObjectItem(end_pos, "character")->valueint;
|
diag->col_end = cJSON_GetObjectItem(end_pos, "character")->valueint;
|
||||||
diag->severity = sev ? (DiagSeverity)sev->valueint : DIAG_ERROR;
|
diag->severity = sev ? (DiagSeverity)sev->valueint : DIAG_ERROR;
|
||||||
|
|
||||||
strncpy(diag->message, msg ? msg->valuestring : "", 255);
|
const char *raw = msg ? msg->valuestring : "";
|
||||||
|
int i = 0;
|
||||||
|
while (raw[i] && raw[i] != '\n' && i < 255)
|
||||||
|
{
|
||||||
|
diag->message[i] = raw[i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
diag->message[i] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
cJSON_Delete(root);
|
cJSON_Delete(root);
|
||||||
}
|
}
|
||||||
|
|||||||
+51
-71
@@ -24,8 +24,6 @@
|
|||||||
#include "include/completion.h"
|
#include "include/completion.h"
|
||||||
#include "include/utils.h"
|
#include "include/utils.h"
|
||||||
|
|
||||||
extern struct editorConfig E;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Renders a single pane with its buffer content
|
* @brief Renders a single pane with its buffer content
|
||||||
*/
|
*/
|
||||||
@@ -74,7 +72,10 @@ static void editorDrawPane(struct abuf* ab, EditorPane* pane)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
lspUiDrawGutter(ab, &E.lsp_diagnostics, pane->buffer_id, file_row);
|
if (E.constantes.LSP) {
|
||||||
|
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')
|
if (buf->filename[strlen(buf->filename) - 1] == 'c' || buf->filename[strlen(buf->filename) - 1] == 'h')
|
||||||
{
|
{
|
||||||
@@ -304,11 +305,8 @@ void editorDrawStatusBar(struct abuf* ab)
|
|||||||
abAppend(ab, render_status, render_len);
|
abAppend(ab, render_status, render_len);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
abAppend(ab, " ", 1);
|
||||||
{
|
++len;
|
||||||
abAppend(ab, " ", 1);
|
|
||||||
++len;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abAppend(ab, "\x1b[m", 3); // normal text mode
|
abAppend(ab, "\x1b[m", 3); // normal text mode
|
||||||
@@ -336,36 +334,6 @@ void editorDrawMessageBar(struct abuf* ab)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorDrawContextBuffer(struct abuf* ab)
|
|
||||||
{
|
|
||||||
int pos_len;
|
|
||||||
char pos_buf[1024];
|
|
||||||
int i, j;
|
|
||||||
|
|
||||||
if (!E.context_buffers)
|
|
||||||
return;
|
|
||||||
|
|
||||||
appDebug("Printing context");
|
|
||||||
|
|
||||||
for (i = 0; i < E.context_buffers->height; ++i)
|
|
||||||
{
|
|
||||||
if (E.context_buffers->editor_y + i + 1 > 0)
|
|
||||||
{
|
|
||||||
pos_len = snprintf(pos_buf, sizeof(pos_buf), "\x1b[%d;%dH",
|
|
||||||
E.context_buffers->editor_y + i + 1, E.context_buffers->editor_x - 2);
|
|
||||||
abAppend(ab, pos_buf, pos_len);
|
|
||||||
|
|
||||||
// Apply background color (6 bytes for RGB format)
|
|
||||||
abAppend(ab, E.theme.BACKGROUND_COLOR, (int)strlen(E.theme.BACKGROUND_COLOR));
|
|
||||||
abAppend(ab, "|", 1);
|
|
||||||
abAppend(ab, E.context_buffers->rows->chars, E.context_buffers->rows->size);
|
|
||||||
abAppend(ab, "|", 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bFree(E.context_buffers);
|
|
||||||
E.context_buffers = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Performs complete screen refresh and buffer synchronization
|
* @brief Performs complete screen refresh and buffer synchronization
|
||||||
* @details Clears screen, redraws all visible content (rows, status bar,
|
* @details Clears screen, redraws all visible content (rows, status bar,
|
||||||
@@ -388,50 +356,62 @@ void editorRefreshScreen()
|
|||||||
(int)strlen(E.theme.BACKGROUND_COLOR));
|
(int)strlen(E.theme.BACKGROUND_COLOR));
|
||||||
|
|
||||||
editorScroll();
|
editorScroll();
|
||||||
|
|
||||||
|
|
||||||
|
EditorPane* active = splitScreenGetActivePane();
|
||||||
|
struct buffer_t* buffer = bufferFindById(active->buffer_id);
|
||||||
|
|
||||||
editorDrawAllPanes(&ab);
|
editorDrawAllPanes(&ab);
|
||||||
|
if (E.constantes.LSP) {
|
||||||
|
|
||||||
|
// ── LSP: draw completion popup every frame while visible ──────────────────
|
||||||
|
appDebug("[REFRESH] lsp_completion.visible=%d\n",
|
||||||
|
E.lsp_completion.visible);
|
||||||
|
while (E.lsp_client->completion_requested && !E.lsp_client->completion_just_arrived);
|
||||||
|
// reset flags
|
||||||
|
E.lsp_client->completion_just_arrived = 0;
|
||||||
|
E.lsp_client->completion_requested = 0;
|
||||||
|
|
||||||
|
// ── LSP: diagnostic for current line in status bar ────────────────────────
|
||||||
|
const char* diag = lspUiDiagnosticAtCursor(
|
||||||
|
&E.lsp_diagnostics,
|
||||||
|
active->buffer_id,
|
||||||
|
buffer->y);
|
||||||
|
if (diag) {
|
||||||
|
char single_line[512];
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
// Copy until newline, \0, or screen width
|
||||||
|
while (diag[i] && diag[i] != '\n' && i < E.screencols - 4)
|
||||||
|
{
|
||||||
|
single_line[i] = diag[i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If message was truncated, add ellipsis
|
||||||
|
if (diag[i] != '\0' && diag[i] != '\n')
|
||||||
|
{
|
||||||
|
single_line[i++] = '.';
|
||||||
|
single_line[i++] = '.';
|
||||||
|
single_line[i++] = '.';
|
||||||
|
}
|
||||||
|
single_line[i] = '\0';
|
||||||
|
editorSetStatusMessage("● %s", single_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
editorDrawStatusBar(&ab);
|
editorDrawStatusBar(&ab);
|
||||||
editorDrawMessageBar(&ab);
|
editorDrawMessageBar(&ab);
|
||||||
|
if (E.constantes.LSP && (E.lsp_client && E.lsp_client->state == LSP_READY))
|
||||||
EditorPane *active = splitScreenGetActivePane();
|
|
||||||
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);
|
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) ────────────────────────────
|
// ── Position cursor (account for gutter width) ────────────────────────────
|
||||||
snprintf(buf, sizeof(buf), "\x1b[%d;%dH",
|
snprintf(buf, sizeof(buf), "\x1b[%d;%dH",
|
||||||
active->cursor_y + active->origin_y + 1,
|
active->cursor_y + active->origin_y + 1,
|
||||||
active->cursor_x + active->origin_x + 1 + GUTTER_WIDTH);
|
active->cursor_x + active->origin_x + 1 + (E.constantes.LSP ? GUTTER_WIDTH : 0));
|
||||||
abAppend(&ab, buf, (int)strlen(buf));
|
abAppend(&ab, buf, (int)strlen(buf));
|
||||||
|
|
||||||
abAppend(&ab, SHOW_CURSOR, 6);
|
abAppend(&ab, SHOW_CURSOR, 6);
|
||||||
|
|||||||
@@ -218,3 +218,18 @@ EditorPane *splitScreenGetActivePane(void) {
|
|||||||
if (E.layout.num_panes == 0) return NULL;
|
if (E.layout.num_panes == 0) return NULL;
|
||||||
return &E.layout.panes[E.layout.active_pane];
|
return &E.layout.panes[E.layout.active_pane];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void freeScreenLayout(ScreenLayout *layout)
|
||||||
|
{
|
||||||
|
while (--E.layout.num_panes >= 0)
|
||||||
|
{
|
||||||
|
bFree(&E.layout.panes);
|
||||||
|
}
|
||||||
|
|
||||||
|
bFree(&E.layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void freePane(EditorPane *pane)
|
||||||
|
{
|
||||||
|
bFree(pane);
|
||||||
|
}
|
||||||
|
|||||||
+24
-25
@@ -34,6 +34,30 @@ static int utf8_char_len(const char *s)
|
|||||||
return 1; // continuation byte or invalid — advance 1 to avoid infinite loop
|
return 1; // continuation byte or invalid — advance 1 to avoid infinite loop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int is_word_char(const char *s)
|
||||||
|
{
|
||||||
|
uint32_t cp = utf8Decode(&s);
|
||||||
|
|
||||||
|
if ((cp >= 'a' && cp <= 'z') || (cp >= 'A' && cp <= 'Z') ||
|
||||||
|
(cp >= '0' && cp <= '9') || cp == '_' || cp == '#')
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (cp == 0xFFFD) return 0;
|
||||||
|
|
||||||
|
if (cp >= 0x00C0 && cp <= 0x017F) return 1;
|
||||||
|
if (cp >= 0x0370 && cp <= 0x03FF) return 1;
|
||||||
|
if (cp >= 0x0400 && cp <= 0x04FF) return 1;
|
||||||
|
if (cp >= 0x0600 && cp <= 0x06FF) return 1;
|
||||||
|
if (cp >= 0x05D0 && cp <= 0x05EA) return 1;
|
||||||
|
if (cp >= 0x0900 && cp <= 0x097F) return 1;
|
||||||
|
if (cp >= 0x4E00 && cp <= 0x9FFF) return 1;
|
||||||
|
if ((cp >= 0x3040 && cp <= 0x309F) ||
|
||||||
|
(cp >= 0x30A0 && cp <= 0x30FF)) return 1;
|
||||||
|
if (cp >= 0xAC00 && cp <= 0xD7A3) return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Copy one full UTF-8 character from src+i into dst+pos, advance both indices.
|
// Copy one full UTF-8 character from src+i into dst+pos, advance both indices.
|
||||||
static void copy_utf8_char(char *dst, int *dst_pos, const char *src, int *src_pos)
|
static void copy_utf8_char(char *dst, int *dst_pos, const char *src, int *src_pos)
|
||||||
{
|
{
|
||||||
@@ -43,31 +67,6 @@ static void copy_utf8_char(char *dst, int *dst_pos, const char *src, int *src_po
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if character is alphanumeric or underscore
|
// Check if character is alphanumeric or underscore
|
||||||
int is_word_char(const char *s)
|
|
||||||
{
|
|
||||||
uint32_t cp = utf8Decode(&s);
|
|
||||||
|
|
||||||
if ((cp >= 'a' && cp <= 'z') || (cp >= 'A' && cp <= 'Z') ||
|
|
||||||
(cp >= '0' && cp <= '9') || cp == '_' || cp == '#')
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
if (cp == 0xFFFD) return 0;
|
|
||||||
|
|
||||||
if (cp >= 0x00C0 && cp <= 0x017F) return 1;
|
|
||||||
if (cp >= 0x0370 && cp <= 0x03FF) return 1;
|
|
||||||
if (cp >= 0x0400 && cp <= 0x04FF) return 1;
|
|
||||||
if (cp >= 0x0600 && cp <= 0x06FF) return 1;
|
|
||||||
if (cp >= 0x05D0 && cp <= 0x05EA) return 1;
|
|
||||||
if (cp >= 0x0900 && cp <= 0x097F) return 1;
|
|
||||||
if (cp >= 0x4E00 && cp <= 0x9FFF) return 1;
|
|
||||||
if ((cp >= 0x3040 && cp <= 0x309F) ||
|
|
||||||
(cp >= 0x30A0 && cp <= 0x30FF)) return 1;
|
|
||||||
if (cp >= 0xAC00 && cp <= 0xD7A3) return 1;
|
|
||||||
if ((cp >= 0x0660 && cp <= 0x0669) ||
|
|
||||||
(cp >= 0x06F0 && cp <= 0x06F9)) return 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if string is a keyword
|
// Check if string is a keyword
|
||||||
int is_keyword(const char *word) {
|
int is_keyword(const char *word) {
|
||||||
|
|||||||
+40
-9
@@ -108,9 +108,41 @@ char *keyToString(int key) {
|
|||||||
|
|
||||||
int editorReadKey() {
|
int editorReadKey() {
|
||||||
char c;
|
char c;
|
||||||
/* read first byte — may be start of UTF-8 or escape */
|
int nread;
|
||||||
while (read(STDIN_FILENO, &c, 1) != 1)
|
|
||||||
;
|
while (1) {
|
||||||
|
fd_set fds;
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
FD_SET(STDIN_FILENO, &fds);
|
||||||
|
|
||||||
|
// Also watch the LSP wake pipe if available
|
||||||
|
int max_fd = STDIN_FILENO;
|
||||||
|
if (E.lsp_client && E.lsp_client->wake_pipe[0] > 0) {
|
||||||
|
FD_SET(E.lsp_client->wake_pipe[0], &fds);
|
||||||
|
if (E.lsp_client->wake_pipe[0] > max_fd)
|
||||||
|
max_fd = E.lsp_client->wake_pipe[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
int ready = select(max_fd + 1, &fds, NULL, NULL, NULL);
|
||||||
|
if (ready <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// LSP event arrived — drain pipe and trigger a redraw
|
||||||
|
// by returning a special no-op key
|
||||||
|
if (E.lsp_client && FD_ISSET(E.lsp_client->wake_pipe[0], &fds)) {
|
||||||
|
char tmp[16];
|
||||||
|
read(E.lsp_client->wake_pipe[0], tmp, sizeof(tmp));
|
||||||
|
return LSP_WAKE_KEY; // ← special value, see below
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normal keypress
|
||||||
|
if (FD_ISSET(STDIN_FILENO, &fds)) {
|
||||||
|
nread = read(STDIN_FILENO, &c, 1);
|
||||||
|
if (nread == 1)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
appDebug("f : %hhu %ld\r\n", c, 0x200);
|
appDebug("f : %hhu %ld\r\n", c, 0x200);
|
||||||
|
|
||||||
if (c == '\x1b') {
|
if (c == '\x1b') {
|
||||||
@@ -219,19 +251,18 @@ int getWindowSize(int *rows, int *cols) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return getCursorPosition(rows, cols);
|
return getCursorPosition(rows, cols);
|
||||||
} else {
|
|
||||||
*cols = ws.ws_col;
|
|
||||||
*rows = ws.ws_row;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
*cols = ws.ws_col;
|
||||||
|
*rows = ws.ws_row;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void appDebug(const char *fmt, ...) {
|
void appDebug(const char *fmt, ...) {
|
||||||
#ifdef APP_DEBUG
|
#ifdef APP_DEBUG
|
||||||
va_list ap;
|
va_list ap;
|
||||||
char message[256];
|
char message[1024];
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
vsnprintf(message, 256, fmt, ap);
|
vsnprintf(message, 1024, fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
fprintf(stderr, "%s\n", message);
|
fprintf(stderr, "%s\n", message);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
int beluga_alloc_counter = 0;
|
int beluga_alloc_counter = 0;
|
||||||
|
|
||||||
void * bAlloc(size_t size)
|
void * bAlloc(size_t size)
|
||||||
|
|||||||
Reference in New Issue
Block a user