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