5 Commits

Author SHA1 Message Date
arthur 39e07902f4 Update README.md
Build project / build (push) Successful in 24s
2026-01-09 15:03:08 +01:00
Arthur Barraux 815114923d Add prefix feature
Build project / build (push) Successful in 56s
2026-01-09 14:54:07 +01:00
arthur 410f382592 Add go to end of word function
Build project / build (push) Successful in 1m22s
2025-12-24 14:50:14 +01:00
arthur 80c0bf73e0 Update README.md
Build project / build (push) Successful in 1m1s
2025-12-08 00:25:05 +01:00
arthur d4ee0d86c2 Update .gitea/workflows/build.yml
Build project / build (push) Failing after 2m18s
2025-12-04 01:34:33 +01:00
24 changed files with 735 additions and 1015 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ on:
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: giorgio-runner
steps: steps:
- name: Checkout code - name: Checkout code
+5 -6
View File
@@ -1,7 +1,6 @@
# 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
@@ -11,7 +10,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://homelinuxserver.ddns.net/git/arthur/beluga.git ~/.beluga && cd ~/.beluga && meson setup build && meson compile -C build``` ```git clone https://git.giorgio-nas.fr/arthur/beluga.git && 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.
@@ -27,7 +26,7 @@ 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-Q | leave the editor | | Ctrl-x Ctrl-c | leave the editor |
| Ctrl-S | Save a file | | Ctrl-x Ctrl-s | Save a file |
| Ctrl-O | open file | | Ctrl-x f | open file |
+67 -17
View File
@@ -3,6 +3,11 @@
(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 () (
@@ -13,22 +18,67 @@
) )
;; KEY MAPPING (define (char-between ch lo hi)
(if (char>=? ch lo)
(char<=? ch hi)
#f))
(map-key "CTRL-q" editor-quit) (define (alphanumericp ch)
(map-key "CTRL-d" editor-save) (if ch
(map-key "ARROW-UP" '(move-cursor "up")) (if (char-between ch #\a #\z)
(map-key "ARROW-DOWN" '(move-cursor "down")) #t
(map-key "ARROW-RIGHT" '(move-cursor "right")) (if (char-between ch #\A #\Z)
(map-key "ARROW-LEFT" '(move-cursor "left")) #t
(map-key "ENTER" editor-insert-new-line) (if (char-between ch #\0 #\9)
(map-key "CTRL-a" move-cursor-beg-line) #t
(map-key "CTRL-e" move-cursor-end-line) #f)))
(map-key "BACKSPACE" editor-delete-previous-char) #f))
(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 unsigned char *s, int len); void abAppend(struct abuf *ab, const char *s, int len);
void abFree(struct abuf *ab); void abFree(struct abuf *ab);
+8
View File
@@ -19,6 +19,8 @@ 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);
@@ -37,4 +39,10 @@ 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
+14 -45
View File
@@ -7,12 +7,6 @@
#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
@@ -22,8 +16,8 @@ typedef struct{
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 */
utf_8_char_t *chars; /**< Characters of the line */ char *chars; /**< Characters of the line */
utf_8_char_t *render; /**< The actual line we will print */ char *render; /**< The actual line we will print */
} erow; } erow;
enum editorStatus_e { enum editorStatus_e {
@@ -37,45 +31,16 @@ struct const_t {
int QUIT_TIMES; int QUIT_TIMES;
}; };
// Key types struct prefix_t {
typedef enum { char prefix_name[64];
KEY_CHAR, // Regular character or UTF-8 int prefix_id;
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 {
KeyInfo *key_sequence; char *key_sequence;
int prefix_id;
Lisp command; Lisp command;
}; };
/** /**
@@ -94,6 +59,7 @@ 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 */
@@ -110,6 +76,9 @@ 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;
}; };
/** /**
@@ -118,7 +87,7 @@ struct editorConfig {
* */ * */
struct abuf { struct abuf {
unsigned char *b; /**< Text that will be printed */ char *b; /**< Text that will be printed */
int len; /**< Length of the text */ int len; /**< Length of the text */
}; };
+12 -3
View File
@@ -8,10 +8,19 @@
#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(utf_8_char_t *c); void editorInsertChar(int c);
void editorInsertNewLine(void); void editorInsertNewLine();
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);
void editorMoveCursor(KeyInfo * key); int editorMoveCursor(int key);
int executeKeyBind(KeyInfo *key_sequence); int executeKeyBind(char *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;
} }
+194 -197
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,20 +234,21 @@ 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) \n\ (for-each1 (lambda (var-spec) \n\
(push (car var) names) \n\ (push (car var-spec) names) \n\
(set! var (cdr var)) \n\ (push (car (cdr var-spec)) inits) \n\
(push (car var) inits) \n\ (if (pair? (cdr (cdr var-spec))) \n\
(set! var (cdr var)) \n\ (push (car (cdr (cdr var-spec))) steps) \n\
(push (car var) steps)) vars) \n\ (push (car var-spec) 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\
@@ -262,18 +263,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\
@@ -445,7 +446,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\
@@ -1926,182 +1927,178 @@ 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 https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-ref/Output-Procedures.html // Output Procedures
{ "_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 https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-ref/Universal-Time.html // Universal Time
{ "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 https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-ref/Equivalence-Predicates.html // Equivalence Predicates
{ "EQ?", sch_exact_eq }, { "eq?", sch_exact_eq },
{ "EQV?", sch_equal }, { "eqv?", sch_equal },
{ "EQUAL?", sch_equal_r }, { "equal?", sch_equal_r },
// Booleans https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-ref/Booleans.html // Booleans
{ "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 https://groups.csail.mit.edu/mac/ftpdir/scheme-7.4/doc-html/scheme_8.html // Lists
{ "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 https://groups.csail.mit.edu/mac/ftpdir/scheme-7.4/doc-html/scheme_9.html#SEC82 // Vectors
{ "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 https://groups.csail.mit.edu/mac/ftpdir/scheme-7.4/doc-html/scheme_7.html#SEC61 // Strings
{ "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 https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-ref/Characters.html#Characters // 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 },
// Association Lists https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-ref/Association-Lists.html // Numerical operations
// 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 https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-ref/Symbols.html // Symbols
{ "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 https://groups.csail.mit.edu/mac/ftpdir/scheme-7.4/doc-html/scheme_14.html // Environments
{ "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 https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-ref/Basic-Hash-Table-Operations.html#Basic-Hash-Table-Operations // Hash Tables
{ "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 https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-ref/Procedure-Operations.html#Procedure-Operations // Procedures
{ "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 },
// TOOD: Almost standard { "procedure-body", sch_lambda_body },
{ "PROCEDURE-BODY", sch_lambda_body }, { "call/cc", sch_call_cc },
{ "CALL/CC", sch_call_cc }, { "continuation?", sch_is_cont },
{ "CONTINUATION?", sch_is_cont },
// Random Numbers https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-ref/Random-Numbers.html // Random Numbers
{ "RANDOM", sch_pseudo_rand }, { "random", sch_pseudo_rand },
{ "RANDOM-SEED!", sch_pseudo_seed }, { "random-seed!", sch_pseudo_seed },
// Garbage Collection https://www.gnu.org/software/mit-scheme/documentation/mit-scheme-user/Garbage-Collection.html // Garbage Collection
{ "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)
@@ -2111,14 +2108,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, utf_8_char_t c); void editorRowInsertChar(erow *row, int at, int c);
void editorRowAppendString(erow *row, char *s, size_t len); void editorRowAppendString(erow *row, char *s, size_t len);
+1 -3
View File
@@ -25,12 +25,10 @@ void disableRawMode();
void enableRawMode(); void enableRawMode();
KeyInfo * editorReadKey(); int 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,7 +17,6 @@ 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,20 +21,12 @@
#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 unsigned char *s, int len) { void abAppend(struct abuf *ab, const char *s, int len) {
unsigned char *new = realloc(ab->b, ab->len + len); char *new = realloc(ab->b, ab->len + len);
if (new == NULL) { if (new == NULL) {
return; return;
+97 -49
View File
@@ -1,100 +1,127 @@
#include "../include/builtins.h" #include "../include/builtins.h"
#include "../include/define.h"
#include "../include/input.h"
#include "../include/file_io.h"
#include "../include/editor_op.h"
#include "../include/row_op.h"
#include "../include/data.h" #include "../include/data.h"
#include "../include/terminal.h" #include "../include/define.h"
#include "../include/editor_op.h"
#include "../include/file_io.h"
#include "../include/input.h"
#include "../include/row_op.h"
#include "include/output.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
utf_8_char_t make_utf8_char(const char *bytes, int len) { struct prefix_t find_prefix(const char prefix_name[64]) {
utf_8_char_t ch; int i = E.number_of_prefix + 1;
ch.len = len; while (i--) {
memcpy(ch.c, bytes, len); if (!strcmp(prefix_name, E.prefix[i].prefix_name)) {
return ch; return E.prefix[i];
}
}
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)); /*
KeyInfo *key = stringToCodepoint(key_string); * 3 arguments keybind command prefix
*/
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 = E.key_binds = (struct keyBind_t *)realloc(
(struct keyBind_t *)realloc(E.key_binds, ++E.number_of_keybinds * sizeof(struct keyBind_t)); E.key_binds, ++E.number_of_keybinds * sizeof(struct keyBind_t));
E.key_binds[E.number_of_keybinds - 1].key_sequence = (KeyInfo *) malloc(sizeof(KeyInfo)); E.key_binds[E.number_of_keybinds - 1].key_sequence =
(char *)malloc(50 * sizeof(char));
memcpy(E.key_binds[E.number_of_keybinds - 1].key_sequence, key, sizeof(KeyInfo)); strncpy(E.key_binds[E.number_of_keybinds - 1].key_sequence, key_sequence, 50);
E.key_binds[E.number_of_keybinds - 1].command = func; E.key_binds[E.number_of_keybinds - 1].command = func;
// Third argument
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));
KeyInfo key; int is_in = 0;
key.type = KEY_ARROW;
switch (direction[0]) { switch (direction[0]) {
case 'u': case 'u':
key.data.arrow = 'A'; is_in = editorMoveCursor(ARROW_UP);
break; break;
case 'd': case 'd':
key.data.arrow = 'B'; is_in = editorMoveCursor(ARROW_DOWN);
break; break;
case 'r': case 'r':
key.data.arrow = 'C'; is_in = editorMoveCursor(ARROW_RIGHT);
break; break;
case 'l': case 'l':
key.data.arrow = 'D'; is_in = editorMoveCursor(ARROW_LEFT);
break; break;
} }
editorMoveCursor(&key); fprintf(stderr, "move lisp %d\n", is_in);
return lisp_make_bool(is_in);
}
void free_structs(void) {
int i;
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);
return lisp_null();
} }
Lisp editorQuit(Lisp args, LispError *e, LispContext ctx) { Lisp editorQuit(Lisp args, LispError *e, LispContext ctx) {
fprintf(stderr, "quit\n");
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) {
@@ -109,7 +136,6 @@ Lisp moveCursorEndLine(Lisp args, LispError* e, LispContext ctx) {
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();
@@ -118,11 +144,8 @@ Lisp deletePreviousChar(Lisp args, LispError* e, LispContext ctx) {
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(&key); editorMoveCursor(ARROW_UP);
} }
return lisp_null(); return lisp_null();
} }
@@ -133,11 +156,8 @@ Lisp editorMoveCursorPageDown(Lisp args, LispError* e, LispContext ctx) {
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(&key); editorMoveCursor(ARROW_DOWN);
} }
return lisp_null(); return lisp_null();
@@ -145,28 +165,31 @@ 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)); char c = lisp_string(lisp_car(args))[0];
utf_8_char_t ch = make_utf8_char(c, 1); editorInsertChar(c);
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);
@@ -174,7 +197,6 @@ 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) {
@@ -188,8 +210,34 @@ 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) {
// fprintf(stderr, "char read : %c\n", E.row[E.cursor_y].render[E.cursor_x]); Lisp returned_char;
// return lisp_make_char(E.row[E.cursor_y].render[E.cursor_x]); if (E.row[E.cursor_y].render[E.cursor_x] == 0) {
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();
}
+24 -66
View File
@@ -1,94 +1,52 @@
#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(utf_8_char_t *c) { void editorInsertChar(int 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) { void editorInsertNewLine() {
if (E.state == READ_ONLY) return; /*
* Add new line and place the cursor at the beginning of it
if (E.cursor_x == 0) { */
// Insert blank line before current line fprintf(stderr, "Inserting new line\n");
erow *row;
if (!E.cursor_x) {
editorInsertRow(E.cursor_y, "", 0); editorInsertRow(E.cursor_y, "", 0);
} else { } else {
// Split current line at cursor row = &E.row[E.cursor_y];
erow *row = &E.row[E.cursor_y]; editorInsertRow(E.cursor_y + 1, &row->chars[E.cursor_x],
row->size - E.cursor_x);
// Calculate byte length of remaining part row = &E.row[E.cursor_y];
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 editorRowAppendRow(erow *dest, erow *src) { void editorDelChar() {
// Allocate space for combined rows erow *row;
utf_8_char_t *new_chars = realloc(dest->chars, if (E.cursor_y == E.numrows || !(E.cursor_x || E.cursor_y)) {
sizeof(utf_8_char_t) * (dest->size + src->size)); return;
if (!new_chars) return;
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;
} }
row = &E.row[E.cursor_y];
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;
editorRowAppendRow(&E.row[E.cursor_y - 1], row); editorRowAppendString(&E.row[E.cursor_y - 1], row->chars, row->size);
editorDelRow(E.cursor_y); editorDelRow(E.cursor_y);
E.cursor_y--; --E.cursor_y;
} }
} }
+21 -108
View File
@@ -7,7 +7,6 @@
#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,
@@ -15,56 +14,37 @@ 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, i; int j;
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) {
// Count actual bytes in each character tot_len += E.row[j].size + 1;
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) {
// Copy each character's bytes memcpy(p, E.row[j].chars, E.row[j].size);
for (i = 0; i < E.row[j].size; i++) { p += E.row[j].size;
for (int k = 0; k < E.row[j].chars[i].len; k++) { *p = '\n';
*p++ = E.row[j].chars[i].c[k]; p++;
}
}
*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;
@@ -76,29 +56,25 @@ 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, "r"); fp = fopen(filename, "a+");
if (!fp) { if (!fp)
// File doesn't exist - that's okay, we'll create it on save die("fopen");
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);
@@ -110,7 +86,6 @@ 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) {
@@ -118,15 +93,10 @@ void editorSave() {
return; return;
} }
} }
buf = editorRowsToString(&len); buf = editorRowsToString(&len);
if (!buf) { fd = open(E.filename, O_RDWR | O_CREAT, 0644);
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);
@@ -134,84 +104,27 @@ 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;
int saved_cursor_x = E.cursor_x; for (i = E.cursor_y + 1; i < E.numrows; i++) {
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);
// Find the character index from byte position E.row_offset = E.numrows;
int byte_pos = match - render_str; break;
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);
} }
+29 -20
View File
@@ -1,8 +1,9 @@
#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"
@@ -10,31 +11,32 @@
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() {
@@ -57,6 +59,13 @@ 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");
@@ -86,6 +95,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;
} }
+113 -101
View File
@@ -2,7 +2,6 @@
#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>
@@ -22,18 +21,19 @@ 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 strdup(path); return 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; size_t 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 = strlen(path) - dir_len - 1;
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,14 +49,16 @@ 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, "/"); strcat(full_path, "/"); // add slash for directories
} }
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;
} }
@@ -68,6 +70,7 @@ 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);
@@ -75,73 +78,120 @@ char *editorPrompt(char *prompt, char *placeHolder, char bPathMode) {
while (1) { while (1) {
editorSetStatusMessage(prompt, buf); editorSetStatusMessage(prompt, buf);
editorRefreshScreen(); editorRefreshScreen();
c = editorReadKey();
KeyInfo *key = editorReadKey(); if (c == DEL_KEY || c == CTRL_KEY('h') || c == BACKSPACE) {
// 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");
snprintf(path, sizeof(path), "%s/%s", pwd, buf); fprintf(stderr, "%s\n", pwd);
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);
char *completion = file_completion(path); buf_len = 0;
if (completion) { strcpy(buf, file_completion(path));
memset(buf, 0, buf_size);
strcpy(buf, completion);
buf_len = strlen(buf); buf_len = strlen(buf);
free(completion); buf[buf_len] = '\0';
}
} } else if (!iscntrl(c) && c < 128) {
// 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++] = (char)key->data.codepoint; buf[buf_len++] = c;
buf[buf_len] = '\0'; buf[buf_len] = '\0';
} }
} }
} }
void editorMoveCursor(KeyInfo *key) { char *key_to_string(int key) {
if (key->type != KEY_ARROW) return; static char key_str[32];
char tmp[10];
sprintf(tmp, "%d", key);
// First test enter key
if (key == '\r') {
strcpy(key_str, "ENTER");
} else if (key == '\t') {
strcpy(key_str, "TAB");
} else if (key >= 1 && key <= 26) { // CTRL keys
snprintf(key_str, sizeof(key_str), "CTRL-%c", 'a' + key - 1);
} else {
switch (key) {
case ARROW_UP:
strcpy(key_str, "ARROW-UP");
break;
case ARROW_DOWN:
strcpy(key_str, "ARROW-DOWN");
break;
case ARROW_LEFT:
strcpy(key_str, "ARROW-LEFT");
break;
case ARROW_RIGHT:
strcpy(key_str, "ARROW-RIGHT");
break;
case PAGE_UP:
strcpy(key_str, "PAGE-UP");
fprintf(stderr, "pagr up\n");
break;
case PAGE_DOWN:
strcpy(key_str, "PAGE-DOWN");
break;
case DEL_KEY:
strcpy(key_str, "DEL");
break;
case BACKSPACE:
strcpy(key_str, "BACKSPACE");
break;
case '\r':
strcpy(key_str, "ENTER");
break;
case '\x1b':
strcpy(key_str, "ESCAPE");
break;
case BEG_LINE:
strcpy(key_str, "HOME");
break;
case END_LINE:
strcpy(key_str, "END");
break;
default:
// For regular characters
if (isprint(key)) {
snprintf(key_str, sizeof(key_str), "%c", key);
} else {
snprintf(key_str, sizeof(key_str), "KEY-%d", key);
}
}
}
return key_str;
}
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) {
switch (key->data.arrow) { case ARROW_RIGHT:
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) {
@@ -149,17 +199,17 @@ void editorMoveCursor(KeyInfo *key) {
E.cursor_x = 0; E.cursor_x = 0;
} }
break; break;
case 'B': // Down case ARROW_DOWN:
if (E.cursor_y < E.numrows) { if (E.cursor_y < E.numrows) {
++E.cursor_y; ++E.cursor_y;
} }
break; break;
case 'A': // Up case ARROW_UP:
if (E.cursor_y != 0) { if (E.cursor_y != 0) {
--E.cursor_y; --E.cursor_y;
} }
break; break;
case 'D': // Left case ARROW_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) {
@@ -173,61 +223,26 @@ void editorMoveCursor(KeyInfo *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;
}
}
KeyInfo *stringToCodepoint(const char *string) {
KeyInfo *key = (KeyInfo *)malloc(sizeof(KeyInfo));
// test control key
if (!strncmp("CTRL", string, 4)) {
key->type = KEY_CTRL;
key->data.ctrl_char = toupper(string[6]) + 64;
} 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;
} }
return 1;
} }
int executeKeyBind(KeyInfo *key_sequence) { int executeKeyBind(char *key_sequence) {
for (int i = 0; i < E.number_of_keybinds; ++i) { int i;
fprintf(stderr, "Keybind found\n"); int previous_state = 0;
if (key_match(key_sequence, E.key_binds[i].key_sequence)) { fprintf(stderr, "pressed %s\n", key_sequence);
// Execute the lisp command for (i = 0; i < E.number_of_keybinds; ++i) {
if (!strcmp(key_sequence, E.key_binds[i].key_sequence)) {
if (E.prefix_state != E.key_binds[i].prefix_id) {
return 0;
}
previous_state = E.prefix_state;
// It's a symbol, create a function call
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;
} }
} }
@@ -235,14 +250,11 @@ int executeKeyBind(KeyInfo *key_sequence) {
} }
void editorProcessKeypress() { void editorProcessKeypress() {
KeyInfo *key = editorReadKey(); int c = editorReadKey();
if (!key)
return;
if (executeKeyBind(key)) { if (executeKeyBind(key_to_string(c))) {
fprintf(stderr, "Keybinds found\n");
return; return;
} }
editorInsertChar(&key->c); editorInsertChar(c);
E.quit_times_buffer = E.constantes.QUIT_TIMES; E.quit_times_buffer = E.constantes.QUIT_TIMES;
} }
+1 -21
View File
@@ -6,17 +6,6 @@
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];
@@ -52,16 +41,7 @@ void editorDrawRows(struct abuf *ab) {
len = 0; len = 0;
if (len > E.screencols) if (len > E.screencols)
len = E.screencols; len = E.screencols;
if (len > 0) { abAppend(ab, &E.row[file_row].render[E.col_offset], len);
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);
+25 -106
View File
@@ -1,6 +1,4 @@
#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>
@@ -8,29 +6,11 @@
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 (is_tab(&row->chars[i])) { if (row->chars[i] == '\t') {
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++;
@@ -42,7 +22,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 (is_tab(&row->chars[cx])) if (row->chars[cx] == '\t')
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;
@@ -59,42 +39,40 @@ void editorUpdateRow(erow *row) {
int i, i_render; int i, i_render;
int tabs = 0; int tabs = 0;
// Count number of tabs // counting number of tabs
for (i = 0; i < row->size; ++i) { for (i = 0; i < row->size; ++i) {
if (is_tab(&row->chars[i])) { tabs +=
tabs++; (row->chars[i] == '\t'); /**< increment tabs of 1 if chars[i] is one. */
}
} }
free(row->render); free(row->render);
// Allocate space for utf_8_char_t array row->render = malloc(row->size + tabs * (E.constantes.TAB_LENGTH - 1) +
row->render = malloc(sizeof(utf_8_char_t) * (row->size + tabs * (E.constantes.TAB_LENGTH - 1))); 1); /**< Tabs needs E.constantes.TAB_LENGTH chars so E.constantes.TAB_LENGTH - 1
more than the first already counted. */
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 (is_tab(&row->chars[i])) { if (row->chars[i] == '\t') {
// Replace tab with spaces row->render[i_render++] = ' ';
row->render[i_render++] = make_space();
while (i_render % E.constantes.TAB_LENGTH) { while (i_render % E.constantes.TAB_LENGTH) {
row->render[i_render++] = make_space(); row->render[i_render++] =
' '; /**< 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;
@@ -102,78 +80,19 @@ 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));
// Initialize the new row E.row[at].size = len;
E.row[at].size = 0; E.row[at].chars = malloc(len + 1);
E.row[at].chars = NULL; memcpy(E.row[at].chars, s, len);
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);
@@ -193,17 +112,16 @@ 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, utf_8_char_t c) { void editorRowInsertChar(erow *row, int at, int 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 + 1); row->chars = realloc(row->chars, row->size + 2);
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;
} }
@@ -212,6 +130,7 @@ 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;
} }
+49 -189
View File
@@ -2,8 +2,6 @@
#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);
@@ -37,213 +35,75 @@ void enableRawMode() {
} }
} }
int utf8_char_length(unsigned char first_byte) { int editorReadKey() {
if ((first_byte & 0x80) == 0) int nread;
return 1; // 0xxxxxxx - ASCII char c;
if ((first_byte & 0xE0) == 0xC0) char seq[3];
return 2; // 110xxxxx - 2 bytes while ((nread = read(STDIN_FILENO, &c, 1)) != 1) {
if ((first_byte & 0xF0) == 0xE0) if (nread == -1 && errno != EAGAIN) {
return 3; // 1110xxxx - 3 bytes die("read");
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;
} }
} }
// Escape sequences if (c == '\x1b') {
if (len >= 2 && seq[0] == 27) { if (read(STDIN_FILENO, &seq[0], 1) != 1 ||
// Alt+key combinations read(STDIN_FILENO, &seq[1], 1) != 1) {
if (len == 2 && seq[1] >= 32 && seq[1] < 127) { return '\x1b';
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 {
// CSI sequences (ESC [ ...) switch (seq[1]) {
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':
key->type = KEY_ARROW; return ARROW_LEFT;
key->data.arrow = seq[2];
return;
case 'H': case 'H':
return BEG_LINE;
case 'F': case 'F':
key->type = KEY_NAVIGATION; return END_LINE;
key->data.special = seq[2];
return;
} }
} }
} else if (seq[0] == 'O') {
// Modified keys (ESC [ 1 ; modifier letter) switch (seq[1]) {
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':
key->type = KEY_NAVIGATION; return END_LINE;
key->data.special = k;
return;
} }
} }
return '\x1b';
// Function keys and navigation } else {
if (len == 4 && seq[3] == '~') { return c;
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) {
char buf[32]; char buf[32];
unsigned int i = 0; unsigned int i = 0;