1 Commits

Author SHA1 Message Date
Arthur Barraux eae85c32ca add utf8_char_t struct 2025-11-19 10:37:41 +01:00
24 changed files with 1013 additions and 733 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ on:
jobs: jobs:
build: build:
runs-on: giorgio-runner runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
+7 -6
View File
@@ -1,6 +1,7 @@
# Beluga # Beluga
Beluga is a project of CLI text editor that uses lisp as configuration language. Beluga is a project of CLI text editor that uses lisp as configuration language.
It's abviously only working for **Linux**.
## Requirements ## Requirements
@@ -10,7 +11,7 @@ You will only need **meson** and a **C compiler** to compile the editor.
### From source ### From source
Here is the installation line for development version: Here is the installation line for development version:
```git clone https://git.giorgio-nas.fr/arthur/beluga.git && cd beluga && meson setup build && meson compile -C build``` ```git clone https://homelinuxserver.ddns.net/git/arthur/beluga.git ~/.beluga && cd ~/.beluga && meson setup build && meson compile -C build```
The executable file will be `build/beluga`. Feel free to add it to your path. The executable file will be `build/beluga`. Feel free to add it to your path.
@@ -25,8 +26,8 @@ To open an existing file just type :
Here is some few command that you will need : Here is some few command that you will need :
| keybind | command | | keybind| command |
|---------------|------------------| |--------|------------------|
| Ctrl-x Ctrl-c | leave the editor | | Ctrl-Q | leave the editor |
| Ctrl-x Ctrl-s | Save a file | | Ctrl-S | Save a file |
| Ctrl-x f | open file | | Ctrl-O | open file |
+17 -67
View File
@@ -3,11 +3,6 @@
(define TAB-LENGTH 4) (define TAB-LENGTH 4)
(define QUIT-TIMES 1) (define QUIT-TIMES 1)
;; PACKAGES
;; First git clone it
;; (add-package "smart_delimiters")
;; FUNCTIONS ;; FUNCTIONS
(define editor-delete-next-char (lambda () ( (define editor-delete-next-char (lambda () (
@@ -18,67 +13,22 @@
) )
(define (char-between ch lo hi) ;; KEY MAPPING
(if (char>=? ch lo)
(char<=? ch hi)
#f))
(define (alphanumericp ch) (map-key "CTRL-q" editor-quit)
(if ch (map-key "CTRL-d" editor-save)
(if (char-between ch #\a #\z) (map-key "ARROW-UP" '(move-cursor "up"))
#t (map-key "ARROW-DOWN" '(move-cursor "down"))
(if (char-between ch #\A #\Z) (map-key "ARROW-RIGHT" '(move-cursor "right"))
#t (map-key "ARROW-LEFT" '(move-cursor "left"))
(if (char-between ch #\0 #\9) (map-key "ENTER" editor-insert-new-line)
#t (map-key "CTRL-a" move-cursor-beg-line)
#f))) (map-key "CTRL-e" move-cursor-end-line)
#f)) (map-key "BACKSPACE" editor-delete-previous-char)
(map-key "DEL" editor-delete-next-char)
(map-key "PAGE-UP" move-cursor-page-up)
(map-key "PAGE-DOWN" move-cursor-page-down)
(map-key "CTRL-o" editor-open-file)
(map-key "CTRL-k" editor-del-row)
(map-key "CTRL-s" editor-find)
(define (word-char-p ch)
(if (alphanumericp ch)
#t
#f))
(define editor-move-to-end-of-word (lambda () (
(if (word-char-p (editor-read-char))
((move-cursor "right")
(editor-move-to-end-of-word))
))
))
(define enter-and-tab
(lambda ()
(editor-insert-new-line)
(let ((is-in (move-cursor "up")))
(do ((ch (editor-read-char) (editor-read-char)))
((and (not (char=? ch #\space)) is-in) #f)
(move-cursor "down")
(editor-insert-char " ")
(set! is-in (move-cursor "up")))
(move-cursor "down"))))
(add-prefix "user")
(map-key "CTRL-x" '(editor-set-prefix "user") "no-prefix")
(map-key "CTRL-g" '(editor-set-prefix "no-prefix") "user")
(map-key "CTRL-c" editor-quit "user")
(map-key "CTRL-s" editor-save "user")
(map-key "ARROW-UP" '(move-cursor "up") "no-prefix")
(map-key "ARROW-DOWN" '(move-cursor "down") "no-prefix")
(map-key "ARROW-RIGHT" '(move-cursor "right") "no-prefix")
(map-key "ARROW-LEFT" '(move-cursor "left") "no-prefix")
(map-key "ENTER" enter-and-tab "no-prefix")
(map-key "CTRL-a" move-cursor-beg-line "no-prefix")
(map-key "CTRL-e" move-cursor-end-line "no-prefix")
(map-key "BACKSPACE" editor-delete-previous-char "no-prefix")
(map-key "DEL" editor-delete-next-char "no-prefix")
(map-key "PAGE-UP" move-cursor-page-up "no-prefix")
(map-key "PAGE-DOWN" move-cursor-page-down "no-prefix")
(map-key "f" editor-open-file "user")
(map-key "TAB" editor-insert-tab "no-prefix")
(map-key "CTRL-k" editor-del-row "no-prefix")
(map-key "CTRL-s" editor-find "no-prefix")
(map-key "CTRL-r" editor-move-to-end-of-word "no-prefix")
+1 -1
View File
@@ -5,7 +5,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
void abAppend(struct abuf *ab, const char *s, int len); void abAppend(struct abuf *ab, const unsigned char *s, int len);
void abFree(struct abuf *ab); void abFree(struct abuf *ab);
-8
View File
@@ -19,8 +19,6 @@ Lisp moveCursorBeginLine(Lisp args, LispError *e, LispContext ctx);
Lisp moveCursorEndLine(Lisp args, LispError *e, LispContext ctx); Lisp moveCursorEndLine(Lisp args, LispError *e, LispContext ctx);
Lisp l_editorInserTab(Lisp args, LispError *e, LispContext ctx);
Lisp deletePreviousChar(Lisp args, LispError *e, LispContext ctx); Lisp deletePreviousChar(Lisp args, LispError *e, LispContext ctx);
Lisp editorMoveCursorPageUp(Lisp args, LispError* e, LispContext ctx); Lisp editorMoveCursorPageUp(Lisp args, LispError* e, LispContext ctx);
@@ -39,10 +37,4 @@ Lisp editorFind_L(Lisp args, LispError *e, LispContext ctx);
Lisp editorReadChar_L(Lisp args, LispError *e, LispContext ctx); Lisp editorReadChar_L(Lisp args, LispError *e, LispContext ctx);
Lisp editorSetPrefix(Lisp args, LispError *e, LispContext ctx);
Lisp editorPrefix(Lisp args, LispError *e, LispContext ctx);
void free_structs(void);
#endif #endif
+45 -14
View File
@@ -7,6 +7,12 @@
#include "lisp.h" #include "lisp.h"
typedef struct{
unsigned char c[4];
char len;
} utf_8_char_t;
/** /**
* \struct erow * \struct erow
* \brief Store one editor row * \brief Store one editor row
@@ -16,8 +22,8 @@
typedef struct erow { typedef struct erow {
int size; /**< Size of the line */ int size; /**< Size of the line */
int rsize; /**< Size of the render line */ int rsize; /**< Size of the render line */
char *chars; /**< Characters of the line */ utf_8_char_t *chars; /**< Characters of the line */
char *render; /**< The actual line we will print */ utf_8_char_t *render; /**< The actual line we will print */
} erow; } erow;
enum editorStatus_e { enum editorStatus_e {
@@ -31,16 +37,45 @@ struct const_t {
int QUIT_TIMES; int QUIT_TIMES;
}; };
struct prefix_t { // Key types
char prefix_name[64]; typedef enum {
int prefix_id; KEY_CHAR, // Regular character or UTF-8
}; KEY_CTRL, // Ctrl+letter
KEY_ALT, // Alt+letter
KEY_ARROW, // Arrow keys
KEY_FUNCTION, // Function keys
KEY_SPECIAL, // Tab, Enter, ESC, Backspace, etc.
KEY_NAVIGATION, // Home, End, PgUp, PgDn, Insert, Delete
KEY_UNKNOWN
} KeyType;
// Modifiers
typedef enum {
MOD_NONE = 0,
MOD_SHIFT = 1,
MOD_ALT = 2,
MOD_CTRL = 4
} KeyModifier;
// Key information structure
typedef struct {
KeyType type;
int modifiers; // Bitmask of KeyModifier
union {
unsigned int codepoint; // For KEY_CHAR
char ctrl_char; // For KEY_CTRL (A-Z)
char alt_char; // For KEY_ALT
char arrow; // For KEY_ARROW (U/D/L/R)
int function_num; // For KEY_FUNCTION (1-12)
char special; // For KEY_SPECIAL and KEY_NAVIGATION
} data;
utf_8_char_t c; // Raw bytes
} KeyInfo;
struct keyBind_t { struct keyBind_t {
char *key_sequence; KeyInfo *key_sequence;
int prefix_id;
Lisp command; Lisp command;
}; };
/** /**
@@ -59,7 +94,6 @@ struct editorConfig {
int dirty; int dirty;
char *filename; char *filename;
enum editorStatus_e state; enum editorStatus_e state;
int prefix_state;
char status_msg[80]; char status_msg[80];
time_t status_msg_time; time_t status_msg_time;
struct termios orig_termios; /**< Terminal communication interface */ struct termios orig_termios; /**< Terminal communication interface */
@@ -76,9 +110,6 @@ struct editorConfig {
struct keyBind_t* key_binds; struct keyBind_t* key_binds;
int number_of_keybinds; int number_of_keybinds;
struct prefix_t* prefix;
int number_of_prefix;
}; };
/** /**
@@ -87,7 +118,7 @@ struct editorConfig {
* */ * */
struct abuf { struct abuf {
char *b; /**< Text that will be printed */ unsigned char *b; /**< Text that will be printed */
int len; /**< Length of the text */ int len; /**< Length of the text */
}; };
+3 -12
View File
@@ -8,19 +8,10 @@
#define HIDE_CURSOR "\x1b[?25l" #define HIDE_CURSOR "\x1b[?25l"
#define SHOW_CURSOR "\x1b[?25h" #define SHOW_CURSOR "\x1b[?25h"
#define ERASE_END_LINE "\x1b[K" #define ERASE_END_LINE "\x1b[K"
#define TAB "\x09"
#define SPACE "\x20"
enum editorKey {
BACKSPACE = 127,
ARROW_LEFT = 1000,
ARROW_RIGHT,
ARROW_UP,
ARROW_DOWN,
DEL_KEY,
BEG_LINE,
END_LINE,
PAGE_UP,
PAGE_DOWN,
};
#define ABUF_INIT {NULL, 0} #define ABUF_INIT {NULL, 0}
+2 -2
View File
@@ -2,9 +2,9 @@
#define EDITOR_OP_H_ #define EDITOR_OP_H_
#include "data.h" #include "data.h"
void editorInsertChar(int c); void editorInsertChar(utf_8_char_t *c);
void editorInsertNewLine(); void editorInsertNewLine(void);
void editorDelChar(); void editorDelChar();
+2 -2
View File
@@ -24,9 +24,9 @@ char *editorPrompt(char *prompt, char * PlaceHolder, char bPathMode);
char *key_to_string(int key); char *key_to_string(int key);
int editorMoveCursor(int key); void editorMoveCursor(KeyInfo * key);
int executeKeyBind(char *key_sequence); int executeKeyBind(KeyInfo *key_sequence);
/** /**
* \fn void editorProcessKeypress() * \fn void editorProcessKeypress()
+12 -12
View File
@@ -1861,8 +1861,8 @@ static Lisp parse_symbol_(Lexer* lex, LispContext ctx)
char scratch[LISP_IDENTIFIER_MAX]; char scratch[LISP_IDENTIFIER_MAX];
size_t length = lexer_copy_token(lex, 0, LISP_IDENTIFIER_MAX, scratch); size_t length = lexer_copy_token(lex, 0, LISP_IDENTIFIER_MAX, scratch);
// always convert symbols to uppercase // always convert symbols to uppercase
// for (int i = 0; i < length; ++i) for (int i = 0; i < length; ++i)
// scratch[i] = toupper(scratch[i]); scratch[i] = toupper(scratch[i]);
return symbol_intern_(ctx.p->symbols, scratch, length, ctx); return symbol_intern_(ctx.p->symbols, scratch, length, ctx);
} }
@@ -3183,17 +3183,17 @@ LispContext lisp_init(void)
ctx.p->macros = lisp_make_table(ctx); ctx.p->macros = lisp_make_table(ctx);
Lisp* c = ctx.p->symbol_cache; Lisp* c = ctx.p->symbol_cache;
c[SYM_IF] = lisp_make_symbol("if", ctx); c[SYM_IF] = lisp_make_symbol("IF", ctx);
c[SYM_BEGIN] = lisp_make_symbol("begin", ctx); c[SYM_BEGIN] = lisp_make_symbol("BEGIN", ctx);
c[SYM_QUOTE] = lisp_make_symbol("quote", ctx); c[SYM_QUOTE] = lisp_make_symbol("QUOTE", ctx);
c[SYM_QUASI_QUOTE] = lisp_make_symbol("quasiquote", ctx); c[SYM_QUASI_QUOTE] = lisp_make_symbol("QUASIQUOTE", ctx);
c[SYM_UNQUOTE] = lisp_make_symbol("unquote", ctx); c[SYM_UNQUOTE] = lisp_make_symbol("UNQUOTE", ctx);
c[SYM_UNQUOTE_SPLICE] = lisp_make_symbol("unquotesplice", ctx); c[SYM_UNQUOTE_SPLICE] = lisp_make_symbol("UNQUOTESPLICE", ctx);
c[SYM_DEFINE] = lisp_make_symbol("_def", ctx); c[SYM_DEFINE] = lisp_make_symbol("_DEF", ctx);
c[SYM_DEFINE_MACRO] = lisp_make_symbol("define-macro", ctx); c[SYM_DEFINE_MACRO] = lisp_make_symbol("DEFINE-MACRO", ctx);
c[SYM_SET] = lisp_make_symbol("_set!", ctx); c[SYM_SET] = lisp_make_symbol("_SET!", ctx);
c[SYM_LAMBDA] = lisp_make_symbol("/\\_", ctx); c[SYM_LAMBDA] = lisp_make_symbol("/\\_", ctx);
c[SYM_CONS] = lisp_make_symbol("cons", ctx); c[SYM_CONS] = lisp_make_symbol("CONS", ctx);
return ctx; return ctx;
} }
+198 -195
View File
@@ -36,7 +36,7 @@ static const char* lib_0_sequences_src_ =
(if (pair? args) \n\ (if (pair? args) \n\
(if (pair? (cdr args)) \n\ (if (pair? (cdr args)) \n\
(if (pair? (cdr (cdr args))) \n\ (if (pair? (cdr (cdr args))) \n\
`(/\\_ ,(car args) ,(cons 'begin (cdr args))) \n\ `(/\\_ ,(car args) ,(cons 'BEGIN (cdr args))) \n\
`(/\\_ ,(car args) ,(car (cdr args)))) \n\ `(/\\_ ,(car args) ,(car (cdr args)))) \n\
(syntax-error \"lambda missing body expressions: (lambda (args) body)\")) \n\ (syntax-error \"lambda missing body expressions: (lambda (args) body)\")) \n\
(syntax-error \"lambda missing argument: (lambda (args) body)\")))) \n\ (syntax-error \"lambda missing argument: (lambda (args) body)\")))) \n\
@@ -44,18 +44,18 @@ static const char* lib_0_sequences_src_ =
(define-macro set! (lambda (var x) \n\ (define-macro set! (lambda (var x) \n\
(begin \n\ (begin \n\
(if (not (symbol? var)) (syntax-error \"set! not a variable\")) \n\ (if (not (symbol? var)) (syntax-error \"set! not a variable\")) \n\
`(_set! ,var ,x)))) \n\ `(_SET! ,var ,x)))) \n\
\n\ \n\
(define-macro define \n\ (define-macro define \n\
(lambda (var . exprs) \n\ (lambda (var . exprs) \n\
(if (symbol? var) \n\ (if (symbol? var) \n\
(if (pair? (cdr exprs)) \n\ (if (pair? (cdr exprs)) \n\
(syntax-error \"define: (define var x)\") \n\ (syntax-error \"define: (define var x)\") \n\
`(_def ,var ,(car exprs))) \n\ `(_DEF ,var ,(car exprs))) \n\
(if (pair? var) \n\ (if (pair? var) \n\
`(_def ,(car var) \n\ `(_DEF ,(car var) \n\
(lambda ,(cdr var) \n\ (LAMBDA ,(cdr var) \n\
,(if (null? (cdr exprs)) (car exprs) (cons 'begin exprs)))) \n\ ,(if (null? (cdr exprs)) (car exprs) (cons 'BEGIN exprs)))) \n\
(syntax-error \"define: not a symbol\") )))) \n\ (syntax-error \"define: not a symbol\") )))) \n\
\n\ \n\
(define (first x) (car x)) \n\ (define (first x) (car x)) \n\
@@ -98,13 +98,13 @@ static const char* lib_0_sequences_src_ =
(define (_expand-shorthand-body path) \n\ (define (_expand-shorthand-body path) \n\
(if (null? path) (cons 'pair '()) \n\ (if (null? path) (cons 'pair '()) \n\
(list (if (char=? (car path) #\\A) \n\ (list (if (char=? (car path) #\\A) \n\
(cons 'car (_expand-shorthand-body (cdr path))))))) \n\ (cons 'CAR (_expand-shorthand-body (cdr path))))))) \n\
\n\ \n\
(define (_expand-shorthand text) \n\ (define (_expand-shorthand text) \n\
(cons 'define (cons (list (string->symbol (string-append \"C\" text \"R\")) 'pair) \n\ (cons 'DEFINE (cons (list (string->symbol (string-append \"C\" text \"R\")) 'pair) \n\
(_expand-shorthand-body (string->list text))))) \n\ (_expand-shorthand-body (string->list text))))) \n\
\n\ \n\
(define-macro _shorthand-accessors (lambda args (cons 'begin (map1 _expand-shorthand args)))) \n\ (define-macro _shorthand-accessors (lambda args (cons 'BEGIN (map1 _expand-shorthand args)))) \n\
\n\ \n\
(define (vector . args) (list->vector args)) \n\ (define (vector . args) (list->vector args)) \n\
\n\ \n\
@@ -124,14 +124,14 @@ static const char* lib_0_sequences_src_ =
static const char* lib_1_forms_src_ = static const char* lib_1_forms_src_ =
"(define (_make-lambda args body) \n\ "(define (_make-lambda args body) \n\
(list 'lambda args (if (null? (cdr body)) (car body) (cons 'begin body)))) \n\ (list 'LAMBDA args (if (null? (cdr body)) (car body) (cons 'BEGIN body)))) \n\
\n\ \n\
\n\ \n\
; (let <name> ((<var0> <expr0>) ... (<varN> <expr1>)) <body0> ... <bodyN>) \n\ ; (LET <name> ((<var0> <expr0>) ... (<varN> <expr1>)) <body0> ... <bodyN>) \n\
; => ((lambda (<var0> ... <varN>) (begin <body0> ... <bodyN>)) <expr0> ... <expr1>) \n\ ; => ((LAMBDA (<var0> ... <varN>) (BEGIN <body0> ... <bodyN>)) <expr0> ... <expr1>) \n\
; => named \n\ ; => named \n\
; ((lambda () \n\ ; ((lambda () \n\
; (define <name> (lambda (<var0> ... <varN>) (begin <body0> ... <bodyN>))) \n\ ; (define <name> (LAMBDA (<var0> ... <varN>) (BEGIN <body0> ... <bodyN>))) \n\
; (<name> <expr0> ... <exprN>))) \n\ ; (<name> <expr0> ... <exprN>))) \n\
\n\ \n\
(define (_check-binding-list bindings) \n\ (define (_check-binding-list bindings) \n\
@@ -145,7 +145,7 @@ static const char* lib_1_forms_src_ =
(define initial-args (map1 (lambda (entry) (second entry)) bindings)) \n\ (define initial-args (map1 (lambda (entry) (second entry)) bindings)) \n\
(if (null? var) \n\ (if (null? var) \n\
(cons body-func initial-args) \n\ (cons body-func initial-args) \n\
(list (_make-lambda '() (list (list 'define var body-func) (cons var initial-args)))))) \n\ (list (_make-lambda '() (list (list 'DEFINE var body-func) (cons var initial-args)))))) \n\
\n\ \n\
(define-macro let (lambda args \n\ (define-macro let (lambda args \n\
(if (pair? (first args)) \n\ (if (pair? (first args)) \n\
@@ -153,8 +153,8 @@ static const char* lib_1_forms_src_ =
(_let->combination (first args) (second args) (cdr (cdr args)))))) \n\ (_let->combination (first args) (second args) (cdr (cdr args)))))) \n\
\n\ \n\
(define (_let*-helper bindings body) \n\ (define (_let*-helper bindings body) \n\
(if (null? bindings) (if (null? (cdr body)) (car body) (cons 'begin body)) \n\ (if (null? bindings) (if (null? (cdr body)) (car body) (cons 'BEGIN body)) \n\
(list 'let (list (car bindings)) (_let*-helper (cdr bindings) body)))) \n\ (list 'LET (list (car bindings)) (_let*-helper (cdr bindings) body)))) \n\
\n\ \n\
(define-macro let* (lambda (bindings . body) \n\ (define-macro let* (lambda (bindings . body) \n\
(_check-binding-list bindings) \n\ (_check-binding-list bindings) \n\
@@ -163,17 +163,17 @@ static const char* lib_1_forms_src_ =
(define-macro letrec (lambda (bindings . body) \n\ (define-macro letrec (lambda (bindings . body) \n\
(_check-binding-list bindings) \n\ (_check-binding-list bindings) \n\
(cons (_make-lambda (map1 (lambda (entry) (first entry)) bindings) \n\ (cons (_make-lambda (map1 (lambda (entry) (first entry)) bindings) \n\
(append (map1 (lambda (entry) (list 'set! (first entry) (second entry))) \n\ (append (map1 (lambda (entry) (list 'SET! (first entry) (second entry))) \n\
bindings) body)) \n\ bindings) body)) \n\
(map1 (lambda (entry) '()) bindings)))) \n\ (map1 (lambda (entry) '()) bindings)))) \n\
\n\ \n\
\n\ \n\
; (cond (<pred0> <expr0>) \n\ ; (COND (<pred0> <expr0>) \n\
; (<pred1> <expr1>) \n\ ; (<pred1> <expr1>) \n\
; ... \n\ ; ... \n\
; (else <expr-1>)) \n\ ; (else <expr-1>)) \n\
; => \n\ ; => \n\
; (if <pred0> <expr0> \n\ ; (IF <pred0> <expr0> \n\
; (if <pred1> <expr1> \n\ ; (if <pred1> <expr1> \n\
; .... \n\ ; .... \n\
; (if <predN> <exprN> <expr-1>)) ... ) \n\ ; (if <predN> <exprN> <expr-1>)) ... ) \n\
@@ -187,11 +187,11 @@ static const char* lib_1_forms_src_ =
\n\ \n\
(define (_cond-helper clauses) \n\ (define (_cond-helper clauses) \n\
(if (null? clauses) '() \n\ (if (null? clauses) '() \n\
(if (eq? (car (car clauses)) 'else) \n\ (if (eq? (car (car clauses)) 'ELSE) \n\
(cons 'begin (cdr (car clauses))) \n\ (cons 'BEGIN (cdr (car clauses))) \n\
(list 'if \n\ (list 'IF \n\
(car (car clauses)) \n\ (car (car clauses)) \n\
(cons 'begin (cdr (car clauses))) \n\ (cons 'BEGIN (cdr (car clauses))) \n\
(_cond-helper (cdr clauses)))))) \n\ (_cond-helper (cdr clauses)))))) \n\
\n\ \n\
(define-macro cond (lambda clauses \n\ (define-macro cond (lambda clauses \n\
@@ -206,26 +206,26 @@ static const char* lib_2_forms_src_ =
(cond ((null? preds) #t) \n\ (cond ((null? preds) #t) \n\
((null? (cdr preds)) (car preds)) \n\ ((null? (cdr preds)) (car preds)) \n\
(else \n\ (else \n\
`(if ,(car preds) ,(_and-helper (cdr preds)) #f)))) \n\ `(IF ,(car preds) ,(_and-helper (cdr preds)) #f)))) \n\
(define-macro and (lambda preds (_and-helper preds))) \n\ (define-macro and (lambda preds (_and-helper preds))) \n\
\n\ \n\
(define (_or-helper preds var) \n\ (define (_or-helper preds var) \n\
(cond ((null? preds) #f) \n\ (cond ((null? preds) #f) \n\
((null? (cdr preds)) (car preds)) \n\ ((null? (cdr preds)) (car preds)) \n\
(else \n\ (else \n\
`(begin (set! ,var ,(car preds)) \n\ `(BEGIN (SET! ,var ,(car preds)) \n\
(if ,var ,var ,(_or-helper (cdr preds) var)))))) \n\ (IF ,var ,var ,(_or-helper (cdr preds) var)))))) \n\
\n\ \n\
(define-macro or (lambda preds \n\ (define-macro or (lambda preds \n\
(let ((var (gensym))) \n\ (let ((var (gensym))) \n\
`(let ((,var '())) ,(_or-helper preds var))))) \n\ `(LET ((,var '())) ,(_or-helper preds var))))) \n\
\n\ \n\
(define-macro case (lambda (key . clauses) \n\ (define-macro case (lambda (key . clauses) \n\
(let ((expr (gensym))) \n\ (let ((expr (gensym))) \n\
`(let ((,expr ,key)) \n\ `(LET ((,expr ,key)) \n\
,(cons 'cond (map1 (lambda (entry) \n\ ,(cons 'COND (map1 (lambda (entry) \n\
(cons (if (pair? (car entry)) \n\ (cons (if (pair? (car entry)) \n\
`(memv ,expr (quote ,(car entry))) \n\ `(MEMV ,expr (quote ,(car entry))) \n\
(car entry)) \n\ (car entry)) \n\
(cdr entry))) clauses)))))) \n\ (cdr entry))) clauses)))))) \n\
\n\ \n\
@@ -234,24 +234,23 @@ static const char* lib_2_forms_src_ =
`(begin (set! ,l (cons ,v ,l)) ,l))) \n\ `(begin (set! ,l (cons ,v ,l)) ,l))) \n\
\n\ \n\
; (DO ((<var0> <init0> <step0>) ...) (<test> <result>) <body>) \n\ ; (DO ((<var0> <init0> <step0>) ...) (<test> <result>) <body>) \n\
\n\
(define-macro do \n\ (define-macro do \n\
(lambda (vars loop-check . loops) \n\ (lambda (vars loop-check . loops) \n\
(let ( (names '()) (inits '()) (steps '()) (f (gensym)) ) \n\ (let ( (names '()) (inits '()) (steps '()) (f (gensym)) ) \n\
(for-each1 (lambda (var-spec) \n\ (for-each1 (lambda (var) \n\
(push (car var-spec) names) \n\ (push (car var) names) \n\
(push (car (cdr var-spec)) inits) \n\ (set! var (cdr var)) \n\
(if (pair? (cdr (cdr var-spec))) \n\ (push (car var) inits) \n\
(push (car (cdr (cdr var-spec))) steps) \n\ (set! var (cdr var)) \n\
(push (car var-spec) steps))) vars) \n\ (push (car var) steps)) vars) \n\
`((lambda () \n\ `((LAMBDA () \n\
(define ,f (lambda ,names \n\ (DEFINE ,f (LAMBDA ,names \n\
(if ,(car loop-check) \n\ (IF ,(car loop-check) \n\
,(if (pair? (cdr loop-check)) (car (cdr loop-check)) '()) \n\ ,(if (pair? (cdr loop-check)) (car (cdr loop-check)) '()) \n\
,(cons 'begin (append loops (list (cons f steps)))) ))) \n\ ,(cons 'BEGIN (append loops (list (cons f steps)))) ))) \n\
,(cons f inits) \n\ ,(cons f inits) \n\
)) ))) \n\ )) ))) \n\
\n\ \n\
(define-macro dotimes \n\ (define-macro dotimes \n\
(lambda (form body) \n\ (lambda (form body) \n\
(apply (lambda (i n . result) \n\ (apply (lambda (i n . result) \n\
@@ -263,18 +262,18 @@ static const char* lib_2_forms_src_ =
(define-macro swap! \n\ (define-macro swap! \n\
(lambda (x y) \n\ (lambda (x y) \n\
(let ((temp (gensym))) \n\ (let ((temp (gensym))) \n\
`(let ((,temp ,x)) \n\ `(LET ((,temp ,x)) \n\
(set! ,temp ,x) \n\ (SET! ,temp ,x) \n\
(set! ,x ,y) \n\ (SET! ,x ,y) \n\
(set! ,y ,temp))))) \n\ (SET! ,y ,temp))))) \n\
\n\ \n\
(define-macro inc! ; CL incf \n\ (define-macro inc! ; CL incf \n\
(lambda (x) \n\ (lambda (x) \n\
`(set! ,x (+ ,x 1)))) \n\ `(SET! ,x (+ ,x 1)))) \n\
\n\ \n\
(define-macro dec! ; CL decf \n\ (define-macro dec! ; CL decf \n\
(lambda (x) \n\ (lambda (x) \n\
`(set! ,x (- ,x 1))))"; `(SET! ,x (- ,x 1))))";
static const char* lib_3_math_src_ = static const char* lib_3_math_src_ =
"(define (number? x) (real? x)) \n\ "(define (number? x) (real? x)) \n\
@@ -446,7 +445,7 @@ static const char* lib_4_sequences_src_ =
static const char* lib_5_streams_src_ = static const char* lib_5_streams_src_ =
"(define-macro delay (lambda (expr) \n\ "(define-macro delay (lambda (expr) \n\
`(make-promise ,(cons 'lambda \n\ `(make-promise ,(cons 'LAMBDA \n\
(cons '() \n\ (cons '() \n\
(cons expr '())))))) \n\ (cons expr '())))))) \n\
\n\ \n\
@@ -1927,178 +1926,182 @@ static Lisp sch_is_cont(Lisp args, LispError* e, LispContext ctx)
static const LispFuncDef lib_cfunc_defs[] = { static const LispFuncDef lib_cfunc_defs[] = {
{ "error", sch_error }, { "ERROR", sch_error },
{ "syntax-error", sch_syntax_error }, { "SYNTAX-ERROR", sch_syntax_error },
// Output Procedures // Output Procedures https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-ref/Output-Procedures.html
{ "_write", sch_write }, { "_WRITE", sch_write },
{ "_display", sch_display }, { "_DISPLAY", sch_display },
{ "_write-char", sch_write_char }, { "_WRITE-CHAR", sch_write_char },
{ "_flush-output-port", sch_flush }, { "_FLUSH-OUTPUT-PORT", sch_flush },
{ "_read", sch_read }, { "_READ", sch_read },
{ "input-port?", sch_is_port_in }, { "INPUT-PORT?", sch_is_port_in },
{ "output-port?", sch_is_port_out }, { "OUTPUT-PORT?", sch_is_port_out },
{ "open-input-file", sch_open_input }, { "OPEN-INPUT-FILE", sch_open_input },
{ "open-output-file", sch_open_output }, { "OPEN-OUTPUT-FILE", sch_open_output },
{ "close-input-port", sch_port_close }, { "CLOSE-INPUT-PORT", sch_port_close },
{ "close-output-port", sch_port_close }, { "CLOSE-OUTPUT-PORT", sch_port_close },
{ "eof-object?", sch_is_eof }, { "EOF-OBJECT?", sch_is_eof },
// Universal Time // Universal Time https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-ref/Universal-Time.html
{ "get-universal-time", sch_univeral_time }, { "GET-UNIVERSAL-TIME", sch_univeral_time },
{ "print-gc-statistics", sch_print_gc_stats }, { "PRINT-GC-STATISTICS", sch_print_gc_stats },
{ "macroexpand", sch_macroexpand }, { "MACROEXPAND", sch_macroexpand },
// Equivalence Predicates // Equivalence Predicates https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-ref/Equivalence-Predicates.html
{ "eq?", sch_exact_eq }, { "EQ?", sch_exact_eq },
{ "eqv?", sch_equal }, { "EQV?", sch_equal },
{ "equal?", sch_equal_r }, { "EQUAL?", sch_equal_r },
// Booleans // Booleans https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-ref/Booleans.html
{ "boolean?", sch_is_boolean }, { "BOOLEAN?", sch_is_boolean },
{ "not", sch_not }, { "NOT", sch_not },
// PAIRS // PAIRS
{ "cons", sch_cons }, { "CONS", sch_cons },
{ "car", sch_car }, { "CAR", sch_car },
{ "cdr", sch_cdr }, { "CDR", sch_cdr },
{ "set-car!", sch_set_car }, { "SET-CAR!", sch_set_car },
{ "set-cdr!", sch_set_cdr }, { "SET-CDR!", sch_set_cdr },
{ "null?", sch_is_null }, { "NULL?", sch_is_null },
{ "pair?", sch_is_pair }, { "PAIR?", sch_is_pair },
// Lists // Lists https://groups.csail.mit.edu/mac/ftpdir/scheme-7.4/doc-html/scheme_8.html
{ "list", sch_list }, { "LIST", sch_list },
{ "list?", sch_is_list }, { "LIST?", sch_is_list },
{ "make-list", sch_make_list }, { "MAKE-LIST", sch_make_list },
{ "list-copy", sch_list_copy }, { "LIST-COPY", sch_list_copy },
{ "length", sch_length }, { "LENGTH", sch_length },
{ "append", sch_append }, { "APPEND", sch_append },
{ "append-reverse!", sch_append_reverse }, { "APPEND-REVERSE!", sch_append_reverse },
{ "list-ref", sch_list_ref }, { "LIST-REF", sch_list_ref },
{ "nthcdr", sch_list_advance }, { "NTHCDR", sch_list_advance },
// Vectors // Vectors https://groups.csail.mit.edu/mac/ftpdir/scheme-7.4/doc-html/scheme_9.html#SEC82
{ "vector?", sch_is_vector }, { "VECTOR?", sch_is_vector },
{ "make-vector", sch_make_vector }, { "MAKE-VECTOR", sch_make_vector },
{ "vector-grow", sch_vector_grow }, { "VECTOR-GROW", sch_vector_grow },
{ "vector-length", sch_vector_length }, { "VECTOR-LENGTH", sch_vector_length },
{ "vector-set!", sch_vector_set }, { "VECTOR-SET!", sch_vector_set },
{ "vector-swap!", sch_vector_swap }, { "VECTOR-SWAP!", sch_vector_swap },
{ "vector-ref", sch_vector_ref }, { "VECTOR-REF", sch_vector_ref },
{ "vector-fill!", sch_vector_fill }, { "VECTOR-FILL!", sch_vector_fill },
{ "vector-assq", sch_vector_assq }, { "VECTOR-ASSQ", sch_vector_assq },
{ "subvector", sch_subvector }, { "SUBVECTOR", sch_subvector },
{ "list->vector", sch_list_to_vector }, { "LIST->VECTOR", sch_list_to_vector },
{ "vector->list", sch_vector_to_list }, { "VECTOR->LIST", sch_vector_to_list },
// Strings // Strings https://groups.csail.mit.edu/mac/ftpdir/scheme-7.4/doc-html/scheme_7.html#SEC61
{ "string?", sch_is_string }, { "STRING?", sch_is_string },
{ "make-string", sch_make_string }, { "MAKE-STRING", sch_make_string },
{ "string=?", sch_equal_r }, { "STRING=?", sch_equal_r },
{ "string<?", sch_string_less }, { "STRING<?", sch_string_less },
{ "substring", sch_substring }, { "SUBSTRING", sch_substring },
{ "string-null?", sch_string_is_null }, { "STRING-NULL?", sch_string_is_null },
{ "string-length", sch_string_length }, { "STRING-LENGTH", sch_string_length },
{ "string-ref", sch_string_ref }, { "STRING-REF", sch_string_ref },
{ "string-set!", sch_string_set }, { "STRING-SET!", sch_string_set },
{ "string-upcase", sch_string_upcase }, { "STRING-UPCASE", sch_string_upcase },
{ "string-downcase", sch_string_downcase }, { "STRING-DOWNCASE", sch_string_downcase },
{ "string-append", sch_string_append }, { "STRING-APPEND", sch_string_append },
{ "string->list", sch_string_to_list }, { "STRING->LIST", sch_string_to_list },
{ "list->string", sch_list_to_string }, { "LIST->STRING", sch_list_to_string },
{ "string->number", sch_string_to_number }, { "STRING->NUMBER", sch_string_to_number },
{ "number->string", sch_number_to_string }, { "NUMBER->STRING", sch_number_to_string },
// Characters // Characters https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-ref/Characters.html#Characters
{ "char?", sch_is_char }, { "CHAR?", sch_is_char },
{ "char=?", sch_equals }, { "CHAR=?", sch_equals },
{ "char<?", sch_char_less }, { "CHAR<?", sch_char_less },
{ "char-upcase", sch_char_upcase }, { "CHAR-UPCASE", sch_char_upcase },
{ "char-downcase", sch_char_downcase }, { "CHAR-DOWNCASE", sch_char_downcase },
{ "char-whitespace?", sch_char_is_white }, { "CHAR-WHITESPACE?", sch_char_is_white },
{ "char-alphanumeric?", sch_char_is_alphanum }, { "CHAR-ALPHANUMERIC?", sch_char_is_alphanum },
{ "char-alphabetic?", sch_char_is_alpha }, { "CHAR-ALPHABETIC?", sch_char_is_alpha },
{ "char-numeric?", sch_char_is_number }, { "CHAR-NUMERIC?", sch_char_is_number },
{ "char->integer", sch_char_to_int }, { "CHAR->INTEGER", sch_char_to_int },
// Numerical operations // Association Lists https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-ref/Association-Lists.html
// Numerical operations https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-ref/Numerical-operations.html
{ "=", sch_equals }, { "=", sch_equals },
{ "+", sch_add }, { "+", sch_add },
{ "-", sch_sub }, { "-", sch_sub },
{ "*", sch_mult }, { "*", sch_mult },
{ "/", sch_divide }, { "/", sch_divide },
{ "<", sch_less }, { "<", sch_less },
{ "integer?", sch_is_int }, { "INTEGER?", sch_is_int },
{ "even?", sch_is_even }, { "EVEN?", sch_is_even },
{ "real?", sch_is_real }, { "REAL?", sch_is_real },
{ "exp", sch_exp }, { "EXP", sch_exp },
{ "expt", sch_power }, { "EXPT", sch_power },
{ "log", sch_log }, { "LOG", sch_log },
{ "sin", sch_sin }, { "SIN", sch_sin },
{ "cos", sch_cos }, { "COS", sch_cos },
{ "tan", sch_tan }, { "TAN", sch_tan },
{ "atan", sch_atan }, { "ATAN", sch_atan },
{ "sqrt", sch_sqrt }, { "SQRT", sch_sqrt },
{ "round", sch_round }, { "ROUND", sch_round },
{ "floor", sch_floor }, { "FLOOR", sch_floor },
{ "ceiling", sch_ceiling }, { "CEILING", sch_ceiling },
{ "quotient", sch_quotient }, { "QUOTIENT", sch_quotient },
{ "remainder", sch_remainder }, { "REMAINDER", sch_remainder },
{ "modulo", sch_modulo }, { "MODULO", sch_modulo },
{ "abs", sch_abs }, { "ABS", sch_abs },
{ "magnitude", sch_abs }, { "MAGNITUDE", sch_abs },
{ "exact?", sch_is_int }, { "EXACT?", sch_is_int },
{ "exact->inexact", sch_to_inexact }, { "EXACT->INEXACT", sch_to_inexact },
{ "inexact->exact", sch_to_exact }, { "INEXACT->EXACT", sch_to_exact },
// Symbols // Symbols https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-ref/Symbols.html
{ "symbol?", sch_is_symbol }, { "SYMBOL?", sch_is_symbol },
{ "symbol<?", sch_symbol_less }, { "SYMBOL<?", sch_symbol_less },
{ "string->symbol", sch_string_to_symbol }, { "STRING->SYMBOL", sch_string_to_symbol },
{ "symbol->string", sch_symbol_to_string }, { "SYMBOL->STRING", sch_symbol_to_string },
{ "generate-uninterned-symbol", sch_gensym }, { "GENERATE-UNINTERNED-SYMBOL", sch_gensym },
{ "gensym", sch_gensym }, { "GENSYM", sch_gensym },
// Environments // Environments https://groups.csail.mit.edu/mac/ftpdir/scheme-7.4/doc-html/scheme_14.html
{ "eval", sch_eval }, { "EVAL", sch_eval },
{ "scheme-report-environment", sch_system_env }, { "SCHEME-REPORT-ENVIRONMENT", sch_system_env },
{ "interaction-environment", sch_user_env }, { "INTERACTION-ENVIRONMENT", sch_user_env },
// { "THE-ENVIRONMENT", sch_current_env },
// Hash Tables // Hash Tables https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-ref/Basic-Hash-Table-Operations.html#Basic-Hash-Table-Operations
{ "hash-table?", sch_is_table }, { "HASH-TABLE?", sch_is_table },
{ "make-hash-table", sch_table_make }, { "MAKE-HASH-TABLE", sch_table_make },
{ "hash-table-set!", sch_table_set }, { "HASH-TABLE-SET!", sch_table_set },
{ "hash-table-ref", sch_table_get }, { "HASH-TABLE-REF", sch_table_get },
{ "hash-table-size", sch_table_size }, { "HASH-TABLE-SIZE", sch_table_size },
{ "hash-table->alist", sch_table_to_alist }, { "HASH-TABLE->ALIST", sch_table_to_alist },
{ "promise?", sch_is_promise }, { "PROMISE?", sch_is_promise },
{ "make-promise", sch_make_promise }, { "MAKE-PROMISE", sch_make_promise },
{ "_promise-procedure", sch_promise_proc }, { "_PROMISE-PROCEDURE", sch_promise_proc },
{ "_promise-store!", sch_promise_store }, { "_PROMISE-STORE!", sch_promise_store },
{ "promise-value", sch_promise_val }, { "PROMISE-VALUE", sch_promise_val },
{ "promise-forced?", sch_promise_forced }, { "PROMISE-FORCED?", sch_promise_forced },
// Procedures // Procedures https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-ref/Procedure-Operations.html#Procedure-Operations
{ "apply", sch_apply }, { "APPLY", sch_apply },
{ "compiled-procedure?", sch_is_func }, { "COMPILED-PROCEDURE?", sch_is_func },
{ "compound-procedure?", sch_is_lambda }, { "COMPOUND-PROCEDURE?", sch_is_lambda },
{ "procedure-environment", sch_lambda_env }, { "PROCEDURE-ENVIRONMENT", sch_lambda_env },
{ "procedure-body", sch_lambda_body }, // TOOD: Almost standard
{ "call/cc", sch_call_cc }, { "PROCEDURE-BODY", sch_lambda_body },
{ "continuation?", sch_is_cont }, { "CALL/CC", sch_call_cc },
{ "CONTINUATION?", sch_is_cont },
// Random Numbers // Random Numbers https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-ref/Random-Numbers.html
{ "random", sch_pseudo_rand }, { "RANDOM", sch_pseudo_rand },
{ "random-seed!", sch_pseudo_seed }, { "RANDOM-SEED!", sch_pseudo_seed },
// Garbage Collection // Garbage Collection https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-user/Garbage-Collection.html
{ "gc-flip", sch_gc_flip }, { "GC-FLIP", sch_gc_flip },
{ NULL, NULL } { NULL, NULL }
}; };
void lisp_lib_load(LispContext ctx) void lisp_lib_load(LispContext ctx)
@@ -2108,14 +2111,14 @@ void lisp_lib_load(LispContext ctx)
lisp_table_set( lisp_table_set(
table, table,
lisp_make_symbol("_current-output-port", ctx), lisp_make_symbol("_CURRENT-OUTPUT-PORT", ctx),
lisp_make_port(stdout, 0), lisp_make_port(stdout, 0),
ctx ctx
); );
lisp_table_set( lisp_table_set(
table, table,
lisp_make_symbol("_current-input-port", ctx), lisp_make_symbol("_CURRENT-INPUT-PORT", ctx),
lisp_make_port(stdin, 1), lisp_make_port(stdin, 1),
ctx ctx
); );
+1 -1
View File
@@ -20,7 +20,7 @@ void editorFreeRow(erow *row);
void editorDelRow(int at); void editorDelRow(int at);
void editorRowInsertChar(erow *row, int at, int c); void editorRowInsertChar(erow *row, int at, utf_8_char_t c);
void editorRowAppendString(erow *row, char *s, size_t len); void editorRowAppendString(erow *row, char *s, size_t len);
+3 -1
View File
@@ -25,10 +25,12 @@ void disableRawMode();
void enableRawMode(); void enableRawMode();
int editorReadKey(); KeyInfo * editorReadKey();
int getCursorPosition(int *rows, int *cols); int getCursorPosition(int *rows, int *cols);
KeyInfo *stringToCodepoint(const char *string);
int getWindowSize(int *rows, int *cols); int getWindowSize(int *rows, int *cols);
#endif #endif
+1
View File
@@ -17,6 +17,7 @@ fi
echo "Create config files ..." echo "Create config files ..."
mkdir -pv ~/.beluga/ mkdir -pv ~/.beluga/
cp -rv ./assets/ ~/.beluga/ cp -rv ./assets/ ~/.beluga/
mkdir -pv ~/.beluga/config/
mkdir -pv ~/.beluga/packages/ mkdir -pv ~/.beluga/packages/
read -p "Do you want to replace your config file or keep it (init.lisp.bak) / (init.lisp.new) ? (Y/n)" confirm read -p "Do you want to replace your config file or keep it (init.lisp.bak) / (init.lisp.new) ? (Y/n)" confirm
+8
View File
@@ -21,12 +21,20 @@
#include "include/output.h" #include "include/output.h"
#include "include/terminal.h" #include "include/terminal.h"
#include <locale.h>
#include <wchar.h>
struct editorConfig E; struct editorConfig E;
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
char * splash_screen = (char *) calloc(256, sizeof(char)); char * splash_screen = (char *) calloc(256, sizeof(char));
// Set support for utf-8
setlocale(LC_ALL, "");
// INIT
enableRawMode(); enableRawMode();
initEditor(); initEditor();
if (argc >= 2) { if (argc >= 2) {
+2 -2
View File
@@ -2,8 +2,8 @@
extern struct editorConfig E; extern struct editorConfig E;
void abAppend(struct abuf *ab, const char *s, int len) { void abAppend(struct abuf *ab, const unsigned char *s, int len) {
char *new = realloc(ab->b, ab->len + len); unsigned char *new = realloc(ab->b, ab->len + len);
if (new == NULL) { if (new == NULL) {
return; return;
+57 -105
View File
@@ -1,127 +1,100 @@
#include "../include/builtins.h" #include "../include/builtins.h"
#include "../include/data.h"
#include "../include/define.h" #include "../include/define.h"
#include "../include/editor_op.h"
#include "../include/file_io.h"
#include "../include/input.h" #include "../include/input.h"
#include "../include/file_io.h"
#include "../include/editor_op.h"
#include "../include/row_op.h" #include "../include/row_op.h"
#include "include/output.h" #include "../include/data.h"
#include "../include/terminal.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
struct prefix_t find_prefix(const char prefix_name[64]) { utf_8_char_t make_utf8_char(const char *bytes, int len) {
int i = E.number_of_prefix + 1; utf_8_char_t ch;
while (i--) { ch.len = len;
if (!strcmp(prefix_name, E.prefix[i].prefix_name)) { memcpy(ch.c, bytes, len);
return E.prefix[i]; return ch;
}
}
return E.prefix[0];
} }
Lisp mapKey(Lisp args, LispError *e, LispContext ctx) { Lisp mapKey(Lisp args, LispError *e, LispContext ctx) {
/* const char *key_string = lisp_string(lisp_car(args));
* 3 arguments keybind command prefix KeyInfo *key = stringToCodepoint(key_string);
*/
const char *key_sequence = lisp_string(lisp_car(args));
args = lisp_cdr(args); args = lisp_cdr(args);
// second argument // second argument
Lisp func = lisp_car(args); Lisp func = lisp_car(args);
E.key_binds = (struct keyBind_t *)realloc( E.key_binds =
E.key_binds, ++E.number_of_keybinds * sizeof(struct keyBind_t)); (struct keyBind_t *)realloc(E.key_binds, ++E.number_of_keybinds * sizeof(struct keyBind_t));
E.key_binds[E.number_of_keybinds - 1].key_sequence = E.key_binds[E.number_of_keybinds - 1].key_sequence = (KeyInfo *) malloc(sizeof(KeyInfo));
(char *)malloc(50 * sizeof(char));
strncpy(E.key_binds[E.number_of_keybinds - 1].key_sequence, key_sequence, 50); memcpy(E.key_binds[E.number_of_keybinds - 1].key_sequence, key, sizeof(KeyInfo));
E.key_binds[E.number_of_keybinds - 1].command = func; E.key_binds[E.number_of_keybinds - 1].command = func;
// Third argument
args = lisp_cdr(args);
const char *prefix_name = lisp_string(lisp_car(args));
struct prefix_t prefix = find_prefix(prefix_name);
E.key_binds[E.number_of_keybinds - 1].prefix_id = prefix.prefix_id;
return lisp_null(); return lisp_null();
} }
Lisp moveCursor(Lisp args, LispError *e, LispContext ctx) { Lisp moveCursor(Lisp args, LispError *e, LispContext ctx) {
const char *direction = lisp_string(lisp_car(args)); const char *direction = lisp_string(lisp_car(args));
int is_in = 0; KeyInfo key;
key.type = KEY_ARROW;
switch (direction[0]) { switch (direction[0]) {
case 'u': case 'u':
is_in = editorMoveCursor(ARROW_UP); key.data.arrow = 'A';
break; break;
case 'd': case 'd':
is_in = editorMoveCursor(ARROW_DOWN); key.data.arrow = 'B';
break; break;
case 'r': case 'r':
is_in = editorMoveCursor(ARROW_RIGHT); key.data.arrow = 'C';
break; break;
case 'l': case 'l':
is_in = editorMoveCursor(ARROW_LEFT); key.data.arrow = 'D';
break; break;
} }
fprintf(stderr, "move lisp %d\n", is_in); editorMoveCursor(&key);
return lisp_make_bool(is_in);
return lisp_null();
} }
void free_structs(void) { Lisp editorQuit(Lisp args, LispError* e, LispContext ctx) {
int i; fprintf(stderr, "quit\n");
free(E.prefix);
for (i = 0; i < E.number_of_keybinds; ++i) {
free(E.key_binds[i].key_sequence);
}
free(E.key_binds);
free(E.filename);
free(E.row->chars);
free(E.row->render);
free(E.row);
}
Lisp editorQuit(Lisp args, LispError *e, LispContext ctx) {
if (E.dirty && E.quit_times_buffer > 0) { if (E.dirty && E.quit_times_buffer > 0) {
editorSetStatusMessage("WARNING! Changes hasn't been saved. Press Ctrl-Q " editorSetStatusMessage("WARNING! Changes hasn't been saved. Press Ctrl-Q "
"another time to quit."); "another time to quit.");
--E.quit_times_buffer; --E.quit_times_buffer;
return lisp_null(); return lisp_null();
} }
free_structs();
write(STDOUT_FILENO, "\x1b[2J", 4); write(STDOUT_FILENO, "\x1b[2J", 4);
write(STDOUT_FILENO, CURSOR_TOP_LEFT, 3); write(STDOUT_FILENO, CURSOR_TOP_LEFT, 3);
disableRawMode(); disableRawMode();
lisp_shutdown(E.ctx);
exit(0); exit(0);
return lisp_null(); return lisp_null();
} }
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();
} }
Lisp l_editorInsertNewLine(Lisp args, LispError *e, LispContext ctx) { Lisp l_editorInsertNewLine(Lisp args, LispError* e, LispContext ctx) {
editorInsertNewLine(); // editorInsertNewLine();
return lisp_null(); return lisp_null();
}
Lisp l_editorInserTab(Lisp args, LispError *e, LispContext ctx) {
for (int i = 0; i<E.constantes.TAB_LENGTH; ++i) {
editorInsertChar(' ');
}
return lisp_null();
} }
Lisp moveCursorBeginLine(Lisp args, LispError *e, LispContext ctx) { Lisp moveCursorBeginLine(Lisp args, LispError *e, LispContext ctx) {
@@ -129,35 +102,42 @@ Lisp moveCursorBeginLine(Lisp args, LispError *e, LispContext ctx) {
return lisp_null(); return lisp_null();
} }
Lisp moveCursorEndLine(Lisp args, LispError *e, LispContext ctx) { Lisp moveCursorEndLine(Lisp args, LispError* e, LispContext ctx) {
if (E.cursor_y < E.numrows) { if (E.cursor_y < E.numrows) {
E.cursor_x = E.row[E.cursor_y].size; E.cursor_x = E.row[E.cursor_y].size;
} }
return lisp_null(); return lisp_null();
} }
Lisp deletePreviousChar(Lisp args, LispError *e, LispContext ctx) {
Lisp deletePreviousChar(Lisp args, LispError* e, LispContext ctx) {
editorDelChar(); editorDelChar();
return lisp_null(); return lisp_null();
} }
Lisp editorMoveCursorPageUp(Lisp args, LispError *e, LispContext ctx) { Lisp editorMoveCursorPageUp(Lisp args, LispError* e, LispContext ctx) {
E.cursor_y = E.row_offset; E.cursor_y = E.row_offset;
int times = E.screenrows; int times = E.screenrows;
KeyInfo key;
key.type = KEY_ARROW;
key.data.arrow = 'D';
while (--times) { while (--times) {
editorMoveCursor(ARROW_UP); editorMoveCursor(&key);
} }
return lisp_null(); return lisp_null();
} }
Lisp editorMoveCursorPageDown(Lisp args, LispError *e, LispContext ctx) { Lisp editorMoveCursorPageDown(Lisp args, LispError* e, LispContext ctx) {
E.cursor_y = E.row_offset + E.screenrows - 1; E.cursor_y = E.row_offset + E.screenrows - 1;
if (E.cursor_y > E.numrows) { if (E.cursor_y > E.numrows) {
E.cursor_y = E.numrows; E.cursor_y = E.numrows;
} }
int times = E.screenrows; int times = E.screenrows;
KeyInfo key;
key.type = KEY_ARROW;
key.data.arrow = 'D';
while (--times) { while (--times) {
editorMoveCursor(ARROW_DOWN); editorMoveCursor(&key);
} }
return lisp_null(); return lisp_null();
@@ -165,31 +145,28 @@ Lisp editorMoveCursorPageDown(Lisp args, LispError *e, LispContext ctx) {
Lisp editorOpenFile(Lisp args, LispError *e, LispContext ctx) { Lisp editorOpenFile(Lisp args, LispError *e, LispContext ctx) {
char *filename = editorPrompt("Open : %s", getenv("PWD"), 1); char *filename = editorPrompt("Open : %s", getenv("PWD"), 1);
if (filename){ if (filename)
editorOpen(filename); editorOpen(filename);
}
free(filename);
return lisp_null(); return lisp_null();
} }
Lisp editorPrintC(Lisp args, LispError *e, LispContext ctx) { Lisp editorPrintC(Lisp args, LispError *e, LispContext ctx) {
char c = lisp_string(lisp_car(args))[0]; char *c = lisp_string(lisp_car(args));
editorInsertChar(c); utf_8_char_t ch = make_utf8_char(c, 1);
editorInsertChar(&ch);
return lisp_null(); return lisp_null();
} }
Lisp addPackage(Lisp args, LispError *e, LispContext ctx) { Lisp addPackage(Lisp args, LispError *e, LispContext ctx) {
const char *package_name = lisp_string(lisp_car(args)); const char *package_name = lisp_string(lisp_car(args));
fprintf(stderr, "%s\n", package_name); char *package_dir = (char *) calloc(256, sizeof(char));
char *package_dir = (char *)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/");
strcat(package_dir, package_name); strcat(package_dir, package_name);
strcat(package_dir, "/init.lisp"); strcat(package_dir, "/init.lisp");
fprintf(stderr, "%s\n", package_dir);
fd_package = fopen(package_dir, "r"); fd_package = fopen(package_dir, "r");
lisp_eval(lisp_read_file(fd_package, &E.ctx_error, E.ctx), &E.ctx_error, lisp_eval(lisp_read_file(fd_package, &E.ctx_error, E.ctx), &E.ctx_error,
E.ctx); E.ctx);
@@ -197,6 +174,7 @@ Lisp addPackage(Lisp args, LispError *e, LispContext ctx) {
free(package_dir); free(package_dir);
return lisp_null(); return lisp_null();
} }
Lisp editorDelRow_L(Lisp args, LispError *e, LispContext ctx) { Lisp editorDelRow_L(Lisp args, LispError *e, LispContext ctx) {
@@ -210,34 +188,8 @@ Lisp editorFind_L(Lisp args, LispError *e, LispContext ctx) {
} }
Lisp editorReadChar_L(Lisp args, LispError *e, LispContext ctx) { Lisp editorReadChar_L(Lisp args, LispError *e, LispContext ctx) {
Lisp returned_char; // fprintf(stderr, "char read : %c\n", E.row[E.cursor_y].render[E.cursor_x]);
if (E.row[E.cursor_y].render[E.cursor_x] == 0) { // return lisp_make_char(E.row[E.cursor_y].render[E.cursor_x]);
returned_char = lisp_make_char('a');
} else {
returned_char = lisp_make_char(E.row[E.cursor_y].render[E.cursor_x]);
}
return returned_char;
}
Lisp editorSetPrefix(Lisp args, LispError *e, LispContext ctx) {
/*
* Set the prefix state of editor to the prefix in argument
*/
const char *prefix_name = lisp_string(lisp_car(args));
struct prefix_t prefix = find_prefix(prefix_name);
E.prefix_state = prefix.prefix_id;
editorSetStatusMessage("prefix %s", prefix.prefix_name);
fprintf(stderr, "%s set\n", prefix_name);
return lisp_null(); return lisp_null();
} }
Lisp editorPrefix(Lisp args, LispError *e, LispContext ctx) {
E.prefix = (struct prefix_t *)realloc(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)),
64);
return lisp_null();
}
+67 -25
View File
@@ -1,52 +1,94 @@
#include "../include/editor_op.h" #include "../include/editor_op.h"
#include "../include/row_op.h" #include "../include/row_op.h"
#include "include/data.h"
#include <stdio.h>
extern struct editorConfig E; extern struct editorConfig E;
void editorInsertChar(int c) { void editorInsertChar(utf_8_char_t *c) {
if (E.state == READ_ONLY) return;
fprintf(stderr, "Insert char %s %d\n", c->c, c->len);
// If cursor is past end of file, add empty rows
if (E.cursor_y == E.numrows) { if (E.cursor_y == E.numrows) {
editorInsertRow(E.numrows, "", 0); editorInsertRow(E.numrows, "", 0);
} }
editorRowInsertChar(&E.row[E.cursor_y], E.cursor_x, c);
// Insert character at cursor position
editorRowInsertChar(&E.row[E.cursor_y], E.cursor_x, *c);
E.cursor_x++; E.cursor_x++;
} }
void editorInsertNewLine() { void editorInsertNewline(void) {
/* if (E.state == READ_ONLY) return;
* Add new line and place the cursor at the beginning of it
*/ if (E.cursor_x == 0) {
fprintf(stderr, "Inserting new line\n"); // Insert blank line before current line
erow *row;
if (!E.cursor_x) {
editorInsertRow(E.cursor_y, "", 0); editorInsertRow(E.cursor_y, "", 0);
} else { } else {
row = &E.row[E.cursor_y]; // Split current line at cursor
editorInsertRow(E.cursor_y + 1, &row->chars[E.cursor_x], erow *row = &E.row[E.cursor_y];
row->size - E.cursor_x);
row = &E.row[E.cursor_y]; // Calculate byte length of remaining part
int remaining_chars = row->size - E.cursor_x;
// Allocate buffer for remaining characters
char *buf = malloc(remaining_chars * 4); // Max 4 bytes per UTF-8 char
int buf_len = 0;
// Convert utf_8_char_t to bytes
for (int i = E.cursor_x; i < row->size; i++) {
for (int j = 0; j < row->chars[i].len; j++) {
buf[buf_len++] = row->chars[i].c[j];
}
}
// Insert new row with remaining text
editorInsertRow(E.cursor_y + 1, buf, buf_len);
free(buf);
// Truncate current row at cursor
row = &E.row[E.cursor_y]; // Refresh pointer after realloc
row->size = E.cursor_x; row->size = E.cursor_x;
row->chars[row->size] = '\0';
editorUpdateRow(row); editorUpdateRow(row);
} }
++E.cursor_y;
E.cursor_y++;
E.cursor_x = 0; E.cursor_x = 0;
fprintf(stderr, "Insert new line done\n");
} }
void editorDelChar() { void editorRowAppendRow(erow *dest, erow *src) {
erow *row; // Allocate space for combined rows
if (E.cursor_y == E.numrows || !(E.cursor_x || E.cursor_y)) { utf_8_char_t *new_chars = realloc(dest->chars,
return; sizeof(utf_8_char_t) * (dest->size + src->size));
} if (!new_chars) return;
row = &E.row[E.cursor_y];
dest->chars = new_chars;
// Copy source row characters
memcpy(&dest->chars[dest->size], src->chars, sizeof(utf_8_char_t) * src->size);
dest->size += src->size;
editorUpdateRow(dest);
++E.dirty;
}
void editorDelChar(void) {
if (E.state == READ_ONLY) return;
if (E.cursor_y == E.numrows) return;
if (E.cursor_x == 0 && E.cursor_y == 0) return;
erow *row = &E.row[E.cursor_y];
if (E.cursor_x > 0) { if (E.cursor_x > 0) {
// Delete character before cursor
editorRowDelchar(row, E.cursor_x - 1); editorRowDelchar(row, E.cursor_x - 1);
--E.cursor_x; E.cursor_x--;
} else { } else {
// At beginning of line - join with previous line
E.cursor_x = E.row[E.cursor_y - 1].size; E.cursor_x = E.row[E.cursor_y - 1].size;
editorRowAppendString(&E.row[E.cursor_y - 1], row->chars, row->size); editorRowAppendRow(&E.row[E.cursor_y - 1], row);
editorDelRow(E.cursor_y); editorDelRow(E.cursor_y);
--E.cursor_y; E.cursor_y--;
} }
} }
+108 -21
View File
@@ -7,6 +7,7 @@
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h>
extern char *strdup(const char *); extern char *strdup(const char *);
extern ssize_t getline(char **restrict lineptr, size_t *restrict n, extern ssize_t getline(char **restrict lineptr, size_t *restrict n,
@@ -14,37 +15,56 @@ extern ssize_t getline(char **restrict lineptr, size_t *restrict n,
extern int ftruncate(int fd, off_t length); extern int ftruncate(int fd, off_t length);
extern struct editorConfig E; extern struct editorConfig E;
// Convert utf_8_char_t array to byte string
char *editorRowsToString(int *buffer_len) { char *editorRowsToString(int *buffer_len) {
int tot_len = 0; int tot_len = 0;
int j; int j, i;
char *buf; char *buf;
char *p; char *p;
// Calculate total byte length (not character count)
for (j = 0; j < E.numrows; ++j) { for (j = 0; j < E.numrows; ++j) {
tot_len += E.row[j].size + 1; // Count actual bytes in each character
for (i = 0; i < E.row[j].size; i++) {
tot_len += E.row[j].chars[i].len;
} }
tot_len++; // For newline
}
*buffer_len = tot_len; *buffer_len = tot_len;
buf = malloc(tot_len); buf = malloc(tot_len);
if (!buf) return NULL;
p = buf; p = buf;
for (j = 0; j < E.numrows; ++j) { for (j = 0; j < E.numrows; ++j) {
memcpy(p, E.row[j].chars, E.row[j].size); // Copy each character's bytes
p += E.row[j].size; for (i = 0; i < E.row[j].size; i++) {
*p = '\n'; for (int k = 0; k < E.row[j].chars[i].len; k++) {
p++; *p++ = E.row[j].chars[i].c[k];
}
}
*p++ = '\n';
} }
return buf; return buf;
} }
void editorCloseFile(void) { void editorCloseFile(void) {
// Free all rows
for (int i = 0; i < E.numrows; i++) {
editorFreeRow(&E.row[i]);
}
E.cursor_x = 0; E.cursor_x = 0;
E.cursor_y = 0; E.cursor_y = 0;
E.rx = 0; E.rx = 0;
E.row_offset = 0; E.row_offset = 0;
E.col_offset = 0; E.col_offset = 0;
E.numrows = 0; E.numrows = 0;
free(E.row);
E.row = NULL; E.row = NULL;
E.dirty = 0; E.dirty = 0;
free(E.filename);
E.filename = NULL; E.filename = NULL;
E.status_msg[0] = '\0'; E.status_msg[0] = '\0';
E.status_msg_time = 0; E.status_msg_time = 0;
@@ -56,25 +76,29 @@ void editorOpen(char *filename) {
// Test if a file is already open // Test if a file is already open
if (E.filename != NULL) { if (E.filename != NULL) {
editorCloseFile(); editorCloseFile();
E.state = READ_AND_WRITE;
} }
E.state = READ_AND_WRITE;
free(E.filename);
E.filename = strdup(filename); E.filename = strdup(filename);
fp = fopen(filename, "a+"); fp = fopen(filename, "r");
if (!fp) if (!fp) {
die("fopen"); // File doesn't exist - that's okay, we'll create it on save
E.dirty = 0;
return;
}
char *line = NULL; char *line = NULL;
size_t line_cap = 0; size_t line_cap = 0;
ssize_t line_len; ssize_t line_len;
while ((line_len = getline(&line, &line_cap, fp)) != -1) { while ((line_len = getline(&line, &line_cap, fp)) != -1) {
// Strip newline characters
while (line_len > 0 && while (line_len > 0 &&
(line[line_len - 1] == '\n' || line[line_len - 1] == '\r')) { (line[line_len - 1] == '\n' || line[line_len - 1] == '\r')) {
--line_len; --line_len;
} }
// editorInsertRow will convert bytes to utf_8_char_t
editorInsertRow(E.numrows, line, line_len); editorInsertRow(E.numrows, line, line_len);
} }
free(line); free(line);
@@ -86,6 +110,7 @@ void editorSave() {
int len; int len;
char *buf; char *buf;
int fd; int fd;
if (E.filename == NULL) { if (E.filename == NULL) {
E.filename = editorPrompt("Save as: %s (ESC to cancel)", "", 1); E.filename = editorPrompt("Save as: %s (ESC to cancel)", "", 1);
if (E.filename == NULL) { if (E.filename == NULL) {
@@ -93,10 +118,15 @@ void editorSave() {
return; return;
} }
} }
buf = editorRowsToString(&len); buf = editorRowsToString(&len);
fd = open(E.filename, O_RDWR | O_CREAT, 0644); if (!buf) {
editorSetStatusMessage("Can't save! Memory error");
return;
}
fd = open(E.filename, O_RDWR | O_CREAT | O_TRUNC, 0644);
if (fd != -1) { if (fd != -1) {
if (ftruncate(fd, len) != -1) {
if (write(fd, buf, len) == len) { if (write(fd, buf, len) == len) {
close(fd); close(fd);
free(buf); free(buf);
@@ -104,27 +134,84 @@ void editorSave() {
editorSetStatusMessage("%d bytes written to disk", len); editorSetStatusMessage("%d bytes written to disk", len);
return; return;
} }
}
close(fd); close(fd);
} }
free(buf); free(buf);
editorSetStatusMessage("Can't save! I/O error: %s", strerror(errno)); editorSetStatusMessage("Can't save! I/O error: %s", strerror(errno));
} }
// Helper to convert utf_8_char_t array to byte string for searching
static char *row_to_string(erow *row) {
// Calculate byte length
int byte_len = 0;
for (int i = 0; i < row->rsize; i++) {
byte_len += row->render[i].len;
}
char *str = malloc(byte_len + 1);
if (!str) return NULL;
// Convert to bytes
int pos = 0;
for (int i = 0; i < row->rsize; i++) {
for (int j = 0; j < row->render[i].len; j++) {
str[pos++] = row->render[i].c[j];
}
}
str[pos] = '\0';
return str;
}
void editorFind() { void editorFind() {
char *query = editorPrompt("Search: %s (ESC to cancel)", "", 0); char *query = editorPrompt("Search: %s (ESC to cancel)", "", 0);
if (query == NULL) return; if (query == NULL) return;
int i;
for (i = E.cursor_y + 1; i < E.numrows; i++) { int saved_cursor_x = E.cursor_x;
int saved_cursor_y = E.cursor_y;
int saved_row_offset = E.row_offset;
int saved_col_offset = E.col_offset;
// Search from current position forward
for (int i = E.cursor_y; i < E.numrows; i++) {
erow *row = &E.row[i]; erow *row = &E.row[i];
char *match = strstr(row->render, query);
// Convert row to byte string for searching
char *render_str = row_to_string(row);
if (!render_str) continue;
char *match = strstr(render_str, query);
if (match) { if (match) {
E.cursor_y = i; E.cursor_y = i;
E.cursor_x = editorRowRxToCx(row, match - row->render);
E.row_offset = E.numrows; // Find the character index from byte position
break; int byte_pos = match - render_str;
int char_idx = 0;
int current_byte = 0;
for (char_idx = 0; char_idx < row->rsize; char_idx++) {
if (current_byte >= byte_pos) break;
current_byte += row->render[char_idx].len;
} }
E.cursor_x = editorRowRxToCx(row, char_idx);
E.row_offset = E.numrows; // Force scroll
free(render_str);
free(query);
return;
} }
free(render_str);
}
// Not found - restore cursor position
E.cursor_x = saved_cursor_x;
E.cursor_y = saved_cursor_y;
E.row_offset = saved_row_offset;
E.col_offset = saved_col_offset;
editorSetStatusMessage("Not found: %s", query);
free(query); free(query);
} }
+21 -30
View File
@@ -1,9 +1,8 @@
#include "../include/init.h" #include "../include/init.h"
#include "../include/builtins.h"
#include "../include/data.h" #include "../include/data.h"
#include "../include/terminal.h" #include "../include/terminal.h"
#include "../include/builtins.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#define LISP_IMPLEMENTATION #define LISP_IMPLEMENTATION
#include "../include/lisp.h" #include "../include/lisp.h"
@@ -11,36 +10,35 @@
extern struct editorConfig; extern struct editorConfig;
void registerBuiltin(char *key_sequence, LispCFunc f) { void registerBuiltin(char *key_sequence, LispCFunc f) {
lisp_env_define(E.ctx.p->env, lisp_make_symbol(key_sequence, E.ctx), lisp_env_define(E.ctx.p->env, lisp_make_symbol(key_sequence, E.ctx),
lisp_make_func(f), E.ctx); lisp_make_func(f), E.ctx);
} }
void initBuiltins() { void initBuiltins() {
// move cursor // move cursor
registerBuiltin("move-cursor", moveCursor); registerBuiltin("MOVE-CURSOR", moveCursor);
registerBuiltin("map-key", mapKey); registerBuiltin("MAP-KEY", mapKey);
registerBuiltin("editor-quit", editorQuit); registerBuiltin("EDITOR-QUIT", editorQuit);
registerBuiltin("editor-save", l_editorSave); registerBuiltin("EDITOR-SAVE", l_editorSave);
registerBuiltin("editor-insert-new-line", l_editorInsertNewLine); registerBuiltin("EDITOR-INSERT-NEW-LINE", l_editorInsertNewLine);
registerBuiltin("move-cursor-beg-line", moveCursorBeginLine); registerBuiltin("MOVE-CURSOR-BEG-LINE", moveCursorBeginLine);
registerBuiltin("move-cursor-end-line", moveCursorEndLine); registerBuiltin("MOVE-CURSOR-END-LINE", moveCursorEndLine);
registerBuiltin("editor-delete-previous-char", deletePreviousChar); registerBuiltin("EDITOR-DELETE-PREVIOUS-CHAR", deletePreviousChar);
registerBuiltin("move-cursor-page-up", editorMoveCursorPageUp); registerBuiltin("MOVE-CURSOR-PAGE-UP", editorMoveCursorPageUp);
registerBuiltin("move-cursor-page-down", editorMoveCursorPageDown); registerBuiltin("MOVE-CURSOR-PAGE-DOWN", editorMoveCursorPageDown);
registerBuiltin("editor-open-file", editorOpenFile); registerBuiltin("EDITOR-OPEN-FILE", editorOpenFile);
registerBuiltin("editor-insert-char", editorPrintC); registerBuiltin("EDITOR-INSERT-CHAR", editorPrintC);
registerBuiltin("add-package", addPackage); registerBuiltin("ADD-PACKAGE", addPackage);
registerBuiltin("editor-del-row", editorDelRow_L); registerBuiltin("EDITOR-DEL-ROW", editorDelRow_L);
registerBuiltin("editor-find", editorFind_L); registerBuiltin("EDITOR-FIND", editorFind_L);
registerBuiltin("editor-read-char", editorReadChar_L); registerBuiltin("EDITOR-READ-CHAR", editorReadChar_L);
registerBuiltin("add-prefix", editorPrefix);
registerBuiltin("editor-set-prefix", editorSetPrefix);
registerBuiltin("editor-insert-tab", l_editorInserTab);
} }
void initEditor() { void initEditor() {
char *init_file_path = (char *)calloc(256, sizeof(char)); char * init_file_path = (char *) calloc(256, sizeof(char));
E.cursor_x = 0; E.cursor_x = 0;
E.cursor_y = 0; E.cursor_y = 0;
E.rx = 0; E.rx = 0;
@@ -59,13 +57,6 @@ void initEditor() {
E.screenrows -= 2; E.screenrows -= 2;
E.number_of_keybinds = 0; 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[0].prefix_id = 0;
strncpy(E.prefix[0].prefix_name, "no-prefix", 64);
E.prefix_state = 0;
strcat(init_file_path, getenv("HOME")); strcat(init_file_path, getenv("HOME"));
strcat(init_file_path, "/.beluga/config/init.lisp"); strcat(init_file_path, "/.beluga/config/init.lisp");
@@ -95,6 +86,6 @@ void initEditor() {
(int)lisp_eval(lisp_read("QUIT-TIMES", &E.ctx_error, E.ctx), &E.ctx_error, (int)lisp_eval(lisp_read("QUIT-TIMES", &E.ctx_error, E.ctx), &E.ctx_error,
E.ctx) E.ctx)
.val.int_val; .val.int_val;
fprintf(stderr, "Tab %d\n", E.constantes.QUIT_TIMES);
E.quit_times_buffer = E.constantes.QUIT_TIMES; E.quit_times_buffer = E.constantes.QUIT_TIMES;
} }
+100 -112
View File
@@ -2,6 +2,7 @@
#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 <ctype.h> #include <ctype.h>
#include <dirent.h> #include <dirent.h>
#include <stdint.h> #include <stdint.h>
@@ -21,19 +22,18 @@ char *file_completion(const char *path) {
int predict_len = 0; int predict_len = 0;
if (path[strlen(path) - 1] == '/') { if (path[strlen(path) - 1] == '/') {
return path; return strdup(path);
} }
// Find dir name // Find dir name
char *last_slash = strrchr(path, '/'); char *last_slash = strrchr(path, '/');
if (last_slash) { if (last_slash) {
size_t dir_len = last_slash - path + 1; // length of dir_path size_t dir_len = last_slash - path + 1;
strncpy(directory, path, dir_len); strncpy(directory, path, dir_len);
predict_len = strlen(path) - dir_len - 1; predict_len = strlen(path) - dir_len;
strncpy(predict, last_slash + 1, predict_len); strncpy(predict, last_slash + 1, predict_len);
directory[dir_len] = '\0'; directory[dir_len] = '\0';
predict[predict_len] = '\0'; predict[predict_len] = '\0';
fprintf(stderr, "%s %s\n", directory, predict);
} else { } else {
return NULL; return NULL;
} }
@@ -49,16 +49,14 @@ char *file_completion(const char *path) {
struct stat st; struct stat st;
if (stat(full_path, &st) == 0 && S_ISDIR(st.st_mode)) { if (stat(full_path, &st) == 0 && S_ISDIR(st.st_mode)) {
strcat(full_path, "/"); // add slash for directories strcat(full_path, "/");
} }
closedir(dir);
return strdup(full_path); return strdup(full_path);
} }
} }
// Cleanup when no more entries
closedir(dir); closedir(dir);
dir = NULL;
return NULL; return NULL;
} }
@@ -70,7 +68,6 @@ char *editorPrompt(char *prompt, char *placeHolder, char bPathMode) {
size_t buf_size = 128; size_t buf_size = 128;
char *buf = malloc(buf_size); char *buf = malloc(buf_size);
size_t buf_len = 0; size_t buf_len = 0;
int c = 0;
buf[0] = '\0'; buf[0] = '\0';
strcpy(buf, placeHolder); strcpy(buf, placeHolder);
buf_len = strlen(placeHolder); buf_len = strlen(placeHolder);
@@ -78,120 +75,73 @@ char *editorPrompt(char *prompt, char *placeHolder, char bPathMode) {
while (1) { while (1) {
editorSetStatusMessage(prompt, buf); editorSetStatusMessage(prompt, buf);
editorRefreshScreen(); editorRefreshScreen();
c = editorReadKey();
if (c == DEL_KEY || c == CTRL_KEY('h') || c == BACKSPACE) { KeyInfo *key = editorReadKey();
// Handle backspace/delete
if (key->type == KEY_SPECIAL && (key->data.special == 127 || key->data.special == 8)) {
if (buf_len != 0) { if (buf_len != 0) {
buf[--buf_len] = '\0'; buf[--buf_len] = '\0';
} }
} else if (c == ESCAPE) { }
// Handle Ctrl+H (backspace)
else if (key->type == KEY_CTRL && key->data.ctrl_char == 'H') {
if (buf_len != 0) {
buf[--buf_len] = '\0';
}
}
// Handle ESC
else if (key->type == KEY_SPECIAL && key->data.special == 27) {
editorSetStatusMessage(""); editorSetStatusMessage("");
free(buf); free(buf);
return NULL; return NULL;
} else if (c == '\r') { }
// Handle Enter
else if (key->type == KEY_SPECIAL && (key->data.special == 13 || key->data.special == 10)) {
if (buf_len != 0) { if (buf_len != 0) {
editorSetStatusMessage(""); editorSetStatusMessage("");
return buf; return buf;
} }
} else if (bPathMode && c == '\t') { }
// Handle Tab for path completion
else if (bPathMode && key->type == KEY_SPECIAL && key->data.special == 9) {
char path[128]; char path[128];
char *pwd; char *pwd;
if (buf[0] != '/') { if (buf[0] != '/') {
pwd = getenv("PWD"); pwd = getenv("PWD");
fprintf(stderr, "%s\n", pwd); snprintf(path, sizeof(path), "%s/%s", pwd, buf);
memcpy(path, pwd, strlen(pwd));
path[strlen(pwd)] = '/';
strncat(path, buf, buf_len);
} else { } else {
strcpy(path, buf); strcpy(path, buf);
} }
memset(buf, 0, 128);
buf_len = 0;
strcpy(buf, file_completion(path));
buf_len = strlen(buf);
buf[buf_len] = '\0';
} else if (!iscntrl(c) && c < 128) { char *completion = file_completion(path);
if (completion) {
memset(buf, 0, buf_size);
strcpy(buf, completion);
buf_len = strlen(buf);
free(completion);
}
}
// Handle regular characters (ASCII only for prompts)
else if (key->type == KEY_CHAR && key->data.codepoint < 128) {
if (buf_len == buf_size - 1) { if (buf_len == buf_size - 1) {
buf_size *= 2; buf_size *= 2;
buf = realloc(buf, buf_size); buf = realloc(buf, buf_size);
} }
buf[buf_len++] = c; buf[buf_len++] = (char)key->data.codepoint;
buf[buf_len] = '\0'; buf[buf_len] = '\0';
} }
} }
} }
char *key_to_string(int key) { void editorMoveCursor(KeyInfo *key) {
static char key_str[32]; if (key->type != KEY_ARROW) return;
char tmp[10];
sprintf(tmp, "%d", key);
// First test enter key
if (key == '\r') {
strcpy(key_str, "ENTER");
} else if (key == '\t') {
strcpy(key_str, "TAB");
} else if (key >= 1 && key <= 26) { // CTRL keys
snprintf(key_str, sizeof(key_str), "CTRL-%c", 'a' + key - 1);
} else {
switch (key) {
case ARROW_UP:
strcpy(key_str, "ARROW-UP");
break;
case ARROW_DOWN:
strcpy(key_str, "ARROW-DOWN");
break;
case ARROW_LEFT:
strcpy(key_str, "ARROW-LEFT");
break;
case ARROW_RIGHT:
strcpy(key_str, "ARROW-RIGHT");
break;
case PAGE_UP:
strcpy(key_str, "PAGE-UP");
fprintf(stderr, "pagr up\n");
break;
case PAGE_DOWN:
strcpy(key_str, "PAGE-DOWN");
break;
case DEL_KEY:
strcpy(key_str, "DEL");
break;
case BACKSPACE:
strcpy(key_str, "BACKSPACE");
break;
case '\r':
strcpy(key_str, "ENTER");
break;
case '\x1b':
strcpy(key_str, "ESCAPE");
break;
case BEG_LINE:
strcpy(key_str, "HOME");
break;
case END_LINE:
strcpy(key_str, "END");
break;
default:
// For regular characters
if (isprint(key)) {
snprintf(key_str, sizeof(key_str), "%c", key);
} else {
snprintf(key_str, sizeof(key_str), "KEY-%d", key);
}
}
}
return key_str;
}
int editorMoveCursor(int key) {
erow *row = (E.cursor_y >= E.numrows) ? NULL : &E.row[E.cursor_y]; erow *row = (E.cursor_y >= E.numrows) ? NULL : &E.row[E.cursor_y];
int row_len; int row_len;
switch (key) {
case ARROW_RIGHT: switch (key->data.arrow) {
case 'C': // Right
if (row && E.cursor_x < row->size) { if (row && E.cursor_x < row->size) {
++E.cursor_x; ++E.cursor_x;
} else if (row && E.cursor_x == row->size) { } else if (row && E.cursor_x == row->size) {
@@ -199,17 +149,17 @@ int editorMoveCursor(int key) {
E.cursor_x = 0; E.cursor_x = 0;
} }
break; break;
case ARROW_DOWN: case 'B': // Down
if (E.cursor_y < E.numrows) { if (E.cursor_y < E.numrows) {
++E.cursor_y; ++E.cursor_y;
} }
break; break;
case ARROW_UP: case 'A': // Up
if (E.cursor_y != 0) { if (E.cursor_y != 0) {
--E.cursor_y; --E.cursor_y;
} }
break; break;
case ARROW_LEFT: case 'D': // Left
if (E.cursor_x != 0) { if (E.cursor_x != 0) {
--E.cursor_x; --E.cursor_x;
} else if (E.cursor_y > 0) { } else if (E.cursor_y > 0) {
@@ -223,26 +173,61 @@ int editorMoveCursor(int key) {
row_len = row ? row->size : 0; row_len = row ? row->size : 0;
if (E.cursor_x > row_len) { if (E.cursor_x > row_len) {
E.cursor_x = row_len; E.cursor_x = row_len;
return 0;
} }
return 1;
} }
int executeKeyBind(char *key_sequence) { KeyInfo *stringToCodepoint(const char *string) {
int i; KeyInfo *key = (KeyInfo *)malloc(sizeof(KeyInfo));
int previous_state = 0; // test control key
fprintf(stderr, "pressed %s\n", key_sequence); if (!strncmp("CTRL", string, 4)) {
for (i = 0; i < E.number_of_keybinds; ++i) { key->type = KEY_CTRL;
if (!strcmp(key_sequence, E.key_binds[i].key_sequence)) { key->data.ctrl_char = toupper(string[6]) + 64;
if (E.prefix_state != E.key_binds[i].prefix_id) { } else if (!strncmp("ARROW", string, 5)) {
key->type = KEY_ARROW;
if (!strcmp("UP", string + 7)) {
key->data.arrow = 'A';
} else if (!strcmp("DOWN", string + 7)) {
key->data.arrow = 'B';
} else if (!strcmp("RIGHT", string + 7)) {
key->data.arrow = 'C';
} else if (!strcmp("LEFT", string + 7)) {
key->data.arrow = 'D';
}
}
return key;
}
static int key_match(KeyInfo *a, KeyInfo *b) {
if (a->type != b->type) return 0;
if (a->modifiers != b->modifiers) return 0;
switch (a->type) {
case KEY_CTRL:
return toupper(a->data.ctrl_char) == toupper(b->data.ctrl_char);
case KEY_ALT:
return a->data.alt_char == b->data.alt_char;
case KEY_ARROW:
return a->data.arrow == b->data.arrow;
case KEY_FUNCTION:
return a->data.function_num == b->data.function_num;
case KEY_CHAR:
return a->data.codepoint == b->data.codepoint;
case KEY_SPECIAL:
case KEY_NAVIGATION:
return a->data.special == b->data.special;
default:
return 0; return 0;
} }
previous_state = E.prefix_state; }
// It's a symbol, create a function call
int executeKeyBind(KeyInfo *key_sequence) {
for (int i = 0; i < E.number_of_keybinds; ++i) {
fprintf(stderr, "Keybind found\n");
if (key_match(key_sequence, E.key_binds[i].key_sequence)) {
// Execute the lisp command
lisp_eval(lisp_cons(E.key_binds[i].command, lisp_null(), E.ctx), lisp_eval(lisp_cons(E.key_binds[i].command, lisp_null(), E.ctx),
&E.ctx_error, E.ctx); &E.ctx_error, E.ctx);
if (E.prefix_state == previous_state)
E.prefix_state = 0;
return 1; return 1;
} }
} }
@@ -250,11 +235,14 @@ int executeKeyBind(char *key_sequence) {
} }
void editorProcessKeypress() { void editorProcessKeypress() {
int c = editorReadKey(); KeyInfo *key = editorReadKey();
if (!key)
return;
if (executeKeyBind(key_to_string(c))) { if (executeKeyBind(key)) {
fprintf(stderr, "Keybinds found\n");
return; return;
} }
editorInsertChar(c); editorInsertChar(&key->c);
E.quit_times_buffer = E.constantes.QUIT_TIMES; E.quit_times_buffer = E.constantes.QUIT_TIMES;
} }
+21 -1
View File
@@ -6,6 +6,17 @@
extern struct editorConfig E; extern struct editorConfig E;
static void utf8_to_bytes(utf_8_char_t *chars, int count, unsigned char *output, int *output_len) {
int pos = 0;
for (int i = 0; i < count; i++) {
for (int j = 0; j < chars[i].len; j++) {
output[pos++] = chars[i].c[j];
}
fprintf(stderr, "bytes length : %s %d\n", chars[i].c, pos);
}
*output_len = pos;
}
void editorDrawRows(struct abuf *ab) { void editorDrawRows(struct abuf *ab) {
int y; int y;
char welcome[80]; char welcome[80];
@@ -41,7 +52,16 @@ void editorDrawRows(struct abuf *ab) {
len = 0; len = 0;
if (len > E.screencols) if (len > E.screencols)
len = E.screencols; len = E.screencols;
abAppend(ab, &E.row[file_row].render[E.col_offset], len); if (len > 0) {
unsigned char *display_buf = malloc(len * 4); // Max 4 bytes per char
int byte_len;
utf8_to_bytes(&E.row[file_row].render[E.col_offset], len, display_buf,
&byte_len);
abAppend(ab, display_buf, byte_len);
fprintf(stderr, "display buffer : %s %d\n", display_buf, byte_len);
free(display_buf);
}
} }
abAppend(ab, ERASE_END_LINE, 3); abAppend(ab, ERASE_END_LINE, 3);
abAppend(ab, "\r\n", 2); abAppend(ab, "\r\n", 2);
+106 -25
View File
@@ -1,4 +1,6 @@
#include "../include/row_op.h" #include "../include/row_op.h"
#include "include/data.h"
#include "include/define.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -6,11 +8,29 @@
extern struct editorConfig E; extern struct editorConfig E;
static int is_tab(utf_8_char_t *ch) {
return ch->len == 1 && ch->c[0] == '\t';
}
// Helper function to check if two utf_8_char_t are equal
static int utf8_char_equal(utf_8_char_t *a, utf_8_char_t *b) {
if (a->len != b->len) return 0;
return memcmp(a->c, b->c, a->len) == 0;
}
// Helper function to create a space character
static utf_8_char_t make_space() {
utf_8_char_t space;
space.c[0] = ' ';
space.len = 1;
return space;
}
int editorRowCxToRx(erow *row, int cursor_x) { int editorRowCxToRx(erow *row, int cursor_x) {
int render_x = 0; int render_x = 0;
int i; int i;
for (i = 0; i < cursor_x; ++i) { for (i = 0; i < cursor_x; ++i) {
if (row->chars[i] == '\t') { if (is_tab(&row->chars[i])) {
render_x += (E.constantes.TAB_LENGTH - 1) - (render_x % E.constantes.TAB_LENGTH); render_x += (E.constantes.TAB_LENGTH - 1) - (render_x % E.constantes.TAB_LENGTH);
} }
render_x++; render_x++;
@@ -22,7 +42,7 @@ int editorRowRxToCx(erow *row, int rx) {
int cur_rx = 0; int cur_rx = 0;
int cx; int cx;
for (cx = 0; cx < row->size; cx++) { for (cx = 0; cx < row->size; cx++) {
if (row->chars[cx] == '\t') if (is_tab(&row->chars[cx]))
cur_rx += (E.constantes.TAB_LENGTH - 1) - (cur_rx % E.constantes.TAB_LENGTH); cur_rx += (E.constantes.TAB_LENGTH - 1) - (cur_rx % E.constantes.TAB_LENGTH);
cur_rx++; cur_rx++;
if (cur_rx > rx) return cx; if (cur_rx > rx) return cx;
@@ -39,40 +59,42 @@ void editorUpdateRow(erow *row) {
int i, i_render; int i, i_render;
int tabs = 0; int tabs = 0;
// counting number of tabs // Count number of tabs
for (i = 0; i < row->size; ++i) { for (i = 0; i < row->size; ++i) {
tabs += if (is_tab(&row->chars[i])) {
(row->chars[i] == '\t'); /**< increment tabs of 1 if chars[i] is one. */ tabs++;
}
} }
free(row->render); free(row->render);
row->render = malloc(row->size + tabs * (E.constantes.TAB_LENGTH - 1) + // Allocate space for utf_8_char_t array
1); /**< Tabs needs E.constantes.TAB_LENGTH chars so E.constantes.TAB_LENGTH - 1 row->render = malloc(sizeof(utf_8_char_t) * (row->size + tabs * (E.constantes.TAB_LENGTH - 1)));
more than the first already counted. */
if (!row->render) {
row->rsize = 0;
return;
}
// end of counting
i_render = 0; i_render = 0;
for (i = 0; i < row->size; ++i) { for (i = 0; i < row->size; ++i) {
if (row->chars[i] == '\t') { if (is_tab(&row->chars[i])) {
row->render[i_render++] = ' '; // Replace tab with spaces
row->render[i_render++] = make_space();
while (i_render % E.constantes.TAB_LENGTH) { while (i_render % E.constantes.TAB_LENGTH) {
row->render[i_render++] = row->render[i_render++] = make_space();
' '; /**< Addind the right amount of spaces for tabs */
} }
} else { } else {
row->render[i_render++] = row->chars[i]; row->render[i_render++] = row->chars[i];
} }
} }
row->render[i_render] = '\0'; // Don't forget the end of string character.
row->rsize = i_render; row->rsize = i_render;
} }
void editorInsertRow(int at, char *s, size_t len) { void editorInsertRow(int at, char *s, size_t len) {
if (at < 0 || at > E.numrows) { if (at < 0 || at > E.numrows) {
return; return;
} }
erow *tmp = (erow *)realloc(E.row, sizeof(erow) * (E.numrows + 1)); erow *tmp = (erow *)realloc(E.row, sizeof(erow) * (E.numrows + 1));
if (!tmp) { if (!tmp) {
return; return;
@@ -80,19 +102,78 @@ void editorInsertRow(int at, char *s, size_t len) {
E.row = tmp; E.row = tmp;
memmove(&E.row[at + 1], &E.row[at], sizeof(erow) * (E.numrows - at)); memmove(&E.row[at + 1], &E.row[at], sizeof(erow) * (E.numrows - at));
E.row[at].size = len; // Initialize the new row
E.row[at].chars = malloc(len + 1); E.row[at].size = 0;
memcpy(E.row[at].chars, s, len); E.row[at].chars = NULL;
E.row[at].chars[len] = '\0';
E.row[at].rsize = 0; E.row[at].rsize = 0;
E.row[at].render = NULL; E.row[at].render = NULL;
// Count UTF-8 characters first
int char_count = 0;
int i = 0;
while (i < len) {
unsigned char first = (unsigned char)s[i];
int char_len;
if ((first & 0x80) == 0) {
char_len = 1;
} else if ((first & 0xE0) == 0xC0) {
char_len = 2;
} else if ((first & 0xF0) == 0xE0) {
char_len = 3;
} else if ((first & 0xF8) == 0xF0) {
char_len = 4;
} else {
char_len = 1; // Invalid, treat as single byte
}
i += char_len;
char_count++;
}
// Allocate for the actual number of characters
if (char_count > 0) {
E.row[at].chars = malloc(sizeof(utf_8_char_t) * char_count);
if (!E.row[at].chars) {
return;
}
}
// Now convert to utf_8_char_t array
i = 0;
E.row[at].size = 0;
while (i < len && E.row[at].size < char_count) {
utf_8_char_t ch;
unsigned char first = (unsigned char)s[i];
if ((first & 0x80) == 0) {
ch.len = 1;
} else if ((first & 0xE0) == 0xC0) {
ch.len = 2;
} else if ((first & 0xF0) == 0xE0) {
ch.len = 3;
} else if ((first & 0xF8) == 0xF0) {
ch.len = 4;
} else {
ch.len = 1;
}
// Copy bytes
for (int j = 0; j < ch.len && i < len; j++) {
ch.c[j] = s[i++];
}
E.row[at].chars[E.row[at].size++] = ch;
}
editorUpdateRow(&E.row[at]); editorUpdateRow(&E.row[at]);
++E.numrows; ++E.numrows;
++E.dirty; ++E.dirty;
} }
void editorFreeRow(erow *row) { void editorFreeRow(erow *row) {
free(row->render); free(row->render);
free(row->chars); free(row->chars);
@@ -112,16 +193,17 @@ void editorDelRow(int at) {
* \fn editorRowInsertChar(erow *row, int at, int c) * \fn editorRowInsertChar(erow *row, int at, int c)
* \param at Index of where we want to insert the char */ * \param at Index of where we want to insert the char */
void editorRowInsertChar(erow *row, int at, int c) { void editorRowInsertChar(erow *row, int at, utf_8_char_t c) {
if (E.state == READ_ONLY) if (E.state == READ_ONLY)
return; return;
if (at < 0 || at > row->size) { if (at < 0 || at > row->size) {
at = row->size; at = row->size;
} }
row->chars = realloc(row->chars, row->size + 2); row->chars = realloc(row->chars, row->size + 1);
memmove(&row->chars[at + 1], &row->chars[at], row->size - at + 1); memmove(&row->chars[at + 1], &row->chars[at], row->size - at + 1);
++row->size; ++(row->size);
row->chars[at] = c; row->chars[at] = c;
fprintf(stderr, "Row insert : %s %d\n", c.c, c.len);
editorUpdateRow(row); editorUpdateRow(row);
++E.dirty; ++E.dirty;
} }
@@ -130,7 +212,6 @@ void editorRowAppendString(erow *row, char *s, size_t len) {
row->chars = realloc(row->chars, row->size + len + 1); row->chars = realloc(row->chars, row->size + len + 1);
memcpy(&row->chars[row->size], s, len); memcpy(&row->chars[row->size], s, len);
row->size += len; row->size += len;
row->chars[row->size] = '\0';
editorUpdateRow(row); editorUpdateRow(row);
++E.dirty; ++E.dirty;
} }
+189 -49
View File
@@ -2,6 +2,8 @@
#include "../include/data.h" #include "../include/data.h"
#include <stdio.h> #include <stdio.h>
#include <unistd.h>
#include <string.h>
void die(const char *s) { void die(const char *s) {
write(STDOUT_FILENO, "\x1b[2J", 4); write(STDOUT_FILENO, "\x1b[2J", 4);
@@ -35,73 +37,211 @@ void enableRawMode() {
} }
} }
int editorReadKey() { int utf8_char_length(unsigned char first_byte) {
int nread; if ((first_byte & 0x80) == 0)
char c; return 1; // 0xxxxxxx - ASCII
char seq[3]; if ((first_byte & 0xE0) == 0xC0)
while ((nread = read(STDIN_FILENO, &c, 1)) != 1) { return 2; // 110xxxxx - 2 bytes
if (nread == -1 && errno != EAGAIN) { if ((first_byte & 0xF0) == 0xE0)
die("read"); return 3; // 1110xxxx - 3 bytes
if ((first_byte & 0xF8) == 0xF0)
return 4; // 11110xxx - 4 bytes
return 1; // Invalid, treat as single byte
}
// Convert UTF-8 to Unicode code point
unsigned int utf8_to_codepoint(const unsigned char *bytes, int len) {
if (len == 1)
return bytes[0];
if (len == 2)
return ((bytes[0] & 0x1F) << 6) | (bytes[1] & 0x3F);
if (len == 3)
return ((bytes[0] & 0x0F) << 12) | ((bytes[1] & 0x3F) << 6) |
(bytes[2] & 0x3F);
if (len == 4)
return ((bytes[0] & 0x07) << 18) | ((bytes[1] & 0x3F) << 12) |
((bytes[2] & 0x3F) << 6) | (bytes[3] & 0x3F);
return 0;
}
void parse_key(unsigned char *seq, int len, KeyInfo *key) {
memcpy(key->c.c, seq, len);
key->c.len = len;
key->modifiers = MOD_NONE;
key->type = KEY_UNKNOWN;
// Control characters (Ctrl+A to Ctrl+Z)
if (len == 1 && seq[0] < 32 && seq[0] != 27 && seq[0] != 9 && seq[0] != 10 &&
seq[0] != 13) {
key->type = KEY_CTRL;
key->data.ctrl_char = seq[0] + 64;
return;
}
// Special single characters
if (len == 1) {
switch (seq[0]) {
case 9:
case 10:
case 13:
case 27:
case 127:
key->type = KEY_SPECIAL;
key->data.special = seq[0];
return;
} }
} }
if (c == '\x1b') { // Escape sequences
if (read(STDIN_FILENO, &seq[0], 1) != 1 || if (len >= 2 && seq[0] == 27) {
read(STDIN_FILENO, &seq[1], 1) != 1) { // Alt+key combinations
return '\x1b'; if (len == 2 && seq[1] >= 32 && seq[1] < 127) {
key->type = KEY_ALT;
key->data.alt_char = seq[1];
return;
} }
if (seq[0] == '[') {
if (seq[1] >= '0' && seq[1] <= '9') {
if (read(STDIN_FILENO, &seq[2], 1) != 1) {
return '\x1b';
}
if (seq[2] == '~') {
switch (seq[1]) {
case '1':
return BEG_LINE;
case '3':
return DEL_KEY;
case '4':
return END_LINE;
case '5':
return PAGE_UP;
case '6':
return PAGE_DOWN;
case '7':
return BEG_LINE;
case '8':
return END_LINE;
}
}
} else {
switch (seq[1]) { // CSI sequences (ESC [ ...)
if (len >= 3 && seq[1] == '[') {
// Arrow keys
if (len == 3) {
switch (seq[2]) {
case 'A': case 'A':
return ARROW_UP;
case 'B': case 'B':
return ARROW_DOWN;
case 'C': case 'C':
return ARROW_RIGHT;
case 'D': case 'D':
return ARROW_LEFT; key->type = KEY_ARROW;
key->data.arrow = seq[2];
return;
case 'H': case 'H':
return BEG_LINE;
case 'F': case 'F':
return END_LINE; key->type = KEY_NAVIGATION;
key->data.special = seq[2];
return;
} }
} }
} else if (seq[0] == 'O') {
switch (seq[1]) { // Modified keys (ESC [ 1 ; modifier letter)
if (len >= 6 && seq[2] == '1' && seq[3] == ';') {
int modifier = seq[4] - '0';
char k = seq[5];
if (modifier & 1)
key->modifiers |= MOD_SHIFT;
if (modifier & 2)
key->modifiers |= MOD_ALT;
if (modifier & 4)
key->modifiers |= MOD_CTRL;
switch (k) {
case 'A':
case 'B':
case 'C':
case 'D':
key->type = KEY_ARROW;
key->data.arrow = k;
return;
case 'H': case 'H':
return BEG_LINE;
case 'F': case 'F':
return END_LINE; key->type = KEY_NAVIGATION;
key->data.special = k;
return;
} }
} }
return '\x1b';
} else { // Function keys and navigation
return c; if (len == 4 && seq[3] == '~') {
int num = seq[2] - '0';
if (num >= 1 && num <= 6) {
key->type = KEY_NAVIGATION;
key->data.special = seq[2];
return;
} }
}
if (len == 5 && seq[4] == '~') {
int num = (seq[2] - '0') * 10 + (seq[3] - '0');
if (num >= 15 && num <= 24) {
key->type = KEY_FUNCTION;
// Map to F5-F12
int f_map[] = {15, 17, 18, 19, 20, 21, 23, 24};
for (int i = 0; i < 8; i++) {
if (f_map[i] == num) {
key->data.function_num = i + 5;
return;
}
}
}
}
}
// SS3 sequences (ESC O ...)
if (len == 3 && seq[1] == 'O') {
switch (seq[2]) {
case 'P':
case 'Q':
case 'R':
case 'S':
key->type = KEY_FUNCTION;
key->data.function_num = seq[2] - 'P' + 1;
return;
case 'H':
case 'F':
key->type = KEY_NAVIGATION;
key->data.special = seq[2];
return;
}
}
}
// UTF-8 character
if (seq[0] >= 32 || (seq[0] & 0x80)) {
int char_len = utf8_char_length(seq[0]);
fprintf(stderr, "char length : %d\n", char_len);
if (char_len <= len) {
key->type = KEY_CHAR;
memcpy(key->c.c, seq, len);
key->c.len = len;
return;
}
}
}
KeyInfo *editorReadKey() {
fd_set fds;
int timeout_ms = 10;
struct timeval tv;
int total = 0;
KeyInfo *key = (KeyInfo *)malloc(sizeof(KeyInfo));
int len;
unsigned char buffer[20];
if (read(STDIN_FILENO, &buffer[0], 1) <= 0)
return 0;
while (total < 20) {
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
tv.tv_sec = 0;
tv.tv_usec = timeout_ms * 1000;
int ret = select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv);
if (ret <= 0)
break;
if (read(STDIN_FILENO, &buffer[total], 1) <= 0)
break;
total++;
}
total++;
parse_key(buffer, total, key);
// DEBUG
fprintf(stderr, "%s %d %d %s %d\n", buffer, buffer[0], buffer[1], key->c.c, key->c.len);
return key;
} }
int getCursorPosition(int *rows, int *cols) { int getCursorPosition(int *rows, int *cols) {