add utf8_char_t struct
This commit is contained in:
@@ -5,7 +5,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
void abAppend(struct abuf *ab, const char *s, int len);
|
void abAppend(struct abuf *ab, const unsigned char *s, int len);
|
||||||
|
|
||||||
void abFree(struct abuf *ab);
|
void abFree(struct abuf *ab);
|
||||||
|
|
||||||
|
|||||||
+47
-6
@@ -7,6 +7,12 @@
|
|||||||
|
|
||||||
#include "lisp.h"
|
#include "lisp.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct{
|
||||||
|
unsigned char c[4];
|
||||||
|
char len;
|
||||||
|
} utf_8_char_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \struct erow
|
* \struct erow
|
||||||
* \brief Store one editor row
|
* \brief Store one editor row
|
||||||
@@ -16,8 +22,8 @@
|
|||||||
typedef struct erow {
|
typedef struct erow {
|
||||||
int size; /**< Size of the line */
|
int size; /**< Size of the line */
|
||||||
int rsize; /**< Size of the render line */
|
int rsize; /**< Size of the render line */
|
||||||
char *chars; /**< Characters of the line */
|
utf_8_char_t *chars; /**< Characters of the line */
|
||||||
char *render; /**< The actual line we will print */
|
utf_8_char_t *render; /**< The actual line we will print */
|
||||||
} erow;
|
} erow;
|
||||||
|
|
||||||
enum editorStatus_e {
|
enum editorStatus_e {
|
||||||
@@ -31,10 +37,45 @@ struct const_t {
|
|||||||
int QUIT_TIMES;
|
int QUIT_TIMES;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct keyBind_t {
|
// Key types
|
||||||
char *key_sequence;
|
typedef enum {
|
||||||
Lisp command;
|
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 {
|
||||||
|
KeyInfo *key_sequence;
|
||||||
|
Lisp command;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -77,7 +118,7 @@ struct editorConfig {
|
|||||||
* */
|
* */
|
||||||
|
|
||||||
struct abuf {
|
struct abuf {
|
||||||
char *b; /**< Text that will be printed */
|
unsigned char *b; /**< Text that will be printed */
|
||||||
int len; /**< Length of the text */
|
int len; /**< Length of the text */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+3
-12
@@ -8,19 +8,10 @@
|
|||||||
#define HIDE_CURSOR "\x1b[?25l"
|
#define HIDE_CURSOR "\x1b[?25l"
|
||||||
#define SHOW_CURSOR "\x1b[?25h"
|
#define SHOW_CURSOR "\x1b[?25h"
|
||||||
#define ERASE_END_LINE "\x1b[K"
|
#define ERASE_END_LINE "\x1b[K"
|
||||||
|
#define TAB "\x09"
|
||||||
|
#define SPACE "\x20"
|
||||||
|
|
||||||
|
|
||||||
enum editorKey {
|
|
||||||
BACKSPACE = 127,
|
|
||||||
ARROW_LEFT = 1000,
|
|
||||||
ARROW_RIGHT,
|
|
||||||
ARROW_UP,
|
|
||||||
ARROW_DOWN,
|
|
||||||
DEL_KEY,
|
|
||||||
BEG_LINE,
|
|
||||||
END_LINE,
|
|
||||||
PAGE_UP,
|
|
||||||
PAGE_DOWN,
|
|
||||||
};
|
|
||||||
|
|
||||||
#define ABUF_INIT {NULL, 0}
|
#define ABUF_INIT {NULL, 0}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -2,9 +2,9 @@
|
|||||||
#define EDITOR_OP_H_
|
#define EDITOR_OP_H_
|
||||||
|
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
void editorInsertChar(int c);
|
void editorInsertChar(utf_8_char_t *c);
|
||||||
|
|
||||||
void editorInsertNewLine();
|
void editorInsertNewLine(void);
|
||||||
|
|
||||||
void editorDelChar();
|
void editorDelChar();
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -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(int key);
|
void editorMoveCursor(KeyInfo * key);
|
||||||
|
|
||||||
int executeKeyBind(char *key_sequence);
|
int executeKeyBind(KeyInfo *key_sequence);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \fn void editorProcessKeypress()
|
* \fn void editorProcessKeypress()
|
||||||
|
|||||||
+1
-1
@@ -20,7 +20,7 @@ void editorFreeRow(erow *row);
|
|||||||
|
|
||||||
void editorDelRow(int at);
|
void editorDelRow(int at);
|
||||||
|
|
||||||
void editorRowInsertChar(erow *row, int at, int c);
|
void editorRowInsertChar(erow *row, int at, utf_8_char_t c);
|
||||||
|
|
||||||
void editorRowAppendString(erow *row, char *s, size_t len);
|
void editorRowAppendString(erow *row, char *s, size_t len);
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -25,10 +25,12 @@ void disableRawMode();
|
|||||||
|
|
||||||
void enableRawMode();
|
void enableRawMode();
|
||||||
|
|
||||||
int editorReadKey();
|
KeyInfo * editorReadKey();
|
||||||
|
|
||||||
int getCursorPosition(int *rows, int *cols);
|
int getCursorPosition(int *rows, int *cols);
|
||||||
|
|
||||||
|
KeyInfo *stringToCodepoint(const char *string);
|
||||||
|
|
||||||
int getWindowSize(int *rows, int *cols);
|
int getWindowSize(int *rows, int *cols);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ fi
|
|||||||
echo "Create config files ..."
|
echo "Create config files ..."
|
||||||
mkdir -pv ~/.beluga/
|
mkdir -pv ~/.beluga/
|
||||||
cp -rv ./assets/ ~/.beluga/
|
cp -rv ./assets/ ~/.beluga/
|
||||||
|
mkdir -pv ~/.beluga/config/
|
||||||
mkdir -pv ~/.beluga/packages/
|
mkdir -pv ~/.beluga/packages/
|
||||||
|
|
||||||
read -p "Do you want to replace your config file or keep it (init.lisp.bak) / (init.lisp.new) ? (Y/n)" confirm
|
read -p "Do you want to replace your config file or keep it (init.lisp.bak) / (init.lisp.new) ? (Y/n)" confirm
|
||||||
|
|||||||
@@ -21,12 +21,20 @@
|
|||||||
#include "include/output.h"
|
#include "include/output.h"
|
||||||
#include "include/terminal.h"
|
#include "include/terminal.h"
|
||||||
|
|
||||||
|
#include <locale.h>
|
||||||
|
#include <wchar.h>
|
||||||
|
|
||||||
struct editorConfig E;
|
struct editorConfig E;
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
char * splash_screen = (char *) calloc(256, sizeof(char));
|
char * splash_screen = (char *) calloc(256, sizeof(char));
|
||||||
|
|
||||||
|
// Set support for utf-8
|
||||||
|
setlocale(LC_ALL, "");
|
||||||
|
|
||||||
|
// INIT
|
||||||
|
|
||||||
enableRawMode();
|
enableRawMode();
|
||||||
initEditor();
|
initEditor();
|
||||||
if (argc >= 2) {
|
if (argc >= 2) {
|
||||||
|
|||||||
+2
-2
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
extern struct editorConfig E;
|
extern struct editorConfig E;
|
||||||
|
|
||||||
void abAppend(struct abuf *ab, const char *s, int len) {
|
void abAppend(struct abuf *ab, const unsigned char *s, int len) {
|
||||||
char *new = realloc(ab->b, ab->len + len);
|
unsigned char *new = realloc(ab->b, ab->len + len);
|
||||||
|
|
||||||
if (new == NULL) {
|
if (new == NULL) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
+35
-14
@@ -5,22 +5,31 @@
|
|||||||
#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 "../include/data.h"
|
||||||
|
#include "../include/terminal.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
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) {
|
Lisp mapKey(Lisp args, LispError *e, LispContext ctx) {
|
||||||
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);
|
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(E.key_binds, ++E.number_of_keybinds * sizeof(struct keyBind_t));
|
(struct keyBind_t *)realloc(E.key_binds, ++E.number_of_keybinds * sizeof(struct keyBind_t));
|
||||||
E.key_binds[E.number_of_keybinds - 1].key_sequence = (char *) malloc(50 * sizeof(char));
|
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;
|
E.key_binds[E.number_of_keybinds - 1].command = func;
|
||||||
|
|
||||||
@@ -29,26 +38,30 @@ Lisp mapKey(Lisp args, LispError *e, LispContext ctx) {
|
|||||||
|
|
||||||
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;
|
||||||
|
key.type = KEY_ARROW;
|
||||||
switch (direction[0]) {
|
switch (direction[0]) {
|
||||||
case 'u':
|
case 'u':
|
||||||
editorMoveCursor(ARROW_UP);
|
key.data.arrow = 'A';
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
editorMoveCursor(ARROW_DOWN);
|
key.data.arrow = 'B';
|
||||||
break;
|
break;
|
||||||
case 'r':
|
case 'r':
|
||||||
editorMoveCursor(ARROW_RIGHT);
|
key.data.arrow = 'C';
|
||||||
break;
|
break;
|
||||||
case 'l':
|
case 'l':
|
||||||
editorMoveCursor(ARROW_LEFT);
|
key.data.arrow = 'D';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
editorMoveCursor(&key);
|
||||||
|
|
||||||
|
|
||||||
return lisp_null();
|
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.");
|
||||||
@@ -77,7 +90,7 @@ Lisp l_editorSave(Lisp args, LispError* e, LispContext ctx) {
|
|||||||
|
|
||||||
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();
|
||||||
|
|
||||||
@@ -105,8 +118,11 @@ 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(ARROW_UP);
|
editorMoveCursor(&key);
|
||||||
}
|
}
|
||||||
return lisp_null();
|
return lisp_null();
|
||||||
}
|
}
|
||||||
@@ -117,8 +133,11 @@ 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(ARROW_DOWN);
|
editorMoveCursor(&key);
|
||||||
}
|
}
|
||||||
|
|
||||||
return lisp_null();
|
return lisp_null();
|
||||||
@@ -134,8 +153,9 @@ Lisp editorOpenFile(Lisp args, LispError *e, LispContext ctx) {
|
|||||||
|
|
||||||
|
|
||||||
Lisp editorPrintC(Lisp args, LispError *e, LispContext ctx) {
|
Lisp editorPrintC(Lisp args, LispError *e, LispContext ctx) {
|
||||||
char c = lisp_string(lisp_car(args))[0];
|
char *c = lisp_string(lisp_car(args));
|
||||||
editorInsertChar(c);
|
utf_8_char_t ch = make_utf8_char(c, 1);
|
||||||
|
editorInsertChar(&ch);
|
||||||
return lisp_null();
|
return lisp_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,7 +188,8 @@ Lisp editorFind_L(Lisp args, LispError *e, LispContext ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Lisp editorReadChar_L(Lisp args, LispError *e, LispContext ctx) {
|
Lisp editorReadChar_L(Lisp args, LispError *e, LispContext ctx) {
|
||||||
fprintf(stderr, "char read : %c\n", E.row[E.cursor_y].render[E.cursor_x]);
|
// 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_make_char(E.row[E.cursor_y].render[E.cursor_x]);
|
||||||
|
return lisp_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+71
-24
@@ -1,47 +1,94 @@
|
|||||||
#include "../include/editor_op.h"
|
#include "../include/editor_op.h"
|
||||||
#include "../include/row_op.h"
|
#include "../include/row_op.h"
|
||||||
|
#include "include/data.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
|
||||||
extern struct editorConfig E;
|
extern struct editorConfig E;
|
||||||
|
|
||||||
void editorInsertChar(int c) {
|
void editorInsertChar(utf_8_char_t *c) {
|
||||||
if (E.cursor_y == E.numrows) {
|
if (E.state == READ_ONLY) return;
|
||||||
editorInsertRow(E.numrows, "", 0);
|
fprintf(stderr, "Insert char %s %d\n", c->c, c->len);
|
||||||
}
|
// If cursor is past end of file, add empty rows
|
||||||
editorRowInsertChar(&E.row[E.cursor_y], E.cursor_x, c);
|
if (E.cursor_y == E.numrows) {
|
||||||
E.cursor_x++;
|
editorInsertRow(E.numrows, "", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert character at cursor position
|
||||||
|
editorRowInsertChar(&E.row[E.cursor_y], E.cursor_x, *c);
|
||||||
|
E.cursor_x++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorInsertNewLine() {
|
void editorInsertNewline(void) {
|
||||||
erow *row;
|
if (E.state == READ_ONLY) return;
|
||||||
if (!E.cursor_x) {
|
|
||||||
|
if (E.cursor_x == 0) {
|
||||||
|
// Insert blank line before current line
|
||||||
editorInsertRow(E.cursor_y, "", 0);
|
editorInsertRow(E.cursor_y, "", 0);
|
||||||
} else {
|
} else {
|
||||||
row = &E.row[E.cursor_y];
|
// Split current line at cursor
|
||||||
editorInsertRow(E.cursor_y + 1, &row->chars[E.cursor_x],
|
erow *row = &E.row[E.cursor_y];
|
||||||
row->size - E.cursor_x);
|
|
||||||
row = &E.row[E.cursor_y];
|
// Calculate byte length of remaining part
|
||||||
|
int remaining_chars = row->size - E.cursor_x;
|
||||||
|
|
||||||
|
// Allocate buffer for remaining characters
|
||||||
|
char *buf = malloc(remaining_chars * 4); // Max 4 bytes per UTF-8 char
|
||||||
|
int buf_len = 0;
|
||||||
|
|
||||||
|
// Convert utf_8_char_t to bytes
|
||||||
|
for (int i = E.cursor_x; i < row->size; i++) {
|
||||||
|
for (int j = 0; j < row->chars[i].len; j++) {
|
||||||
|
buf[buf_len++] = row->chars[i].c[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert new row with remaining text
|
||||||
|
editorInsertRow(E.cursor_y + 1, buf, buf_len);
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
// Truncate current row at cursor
|
||||||
|
row = &E.row[E.cursor_y]; // Refresh pointer after realloc
|
||||||
row->size = E.cursor_x;
|
row->size = E.cursor_x;
|
||||||
row->chars[row->size] = '\0';
|
|
||||||
editorUpdateRow(row);
|
editorUpdateRow(row);
|
||||||
}
|
}
|
||||||
++E.cursor_y;
|
|
||||||
|
E.cursor_y++;
|
||||||
E.cursor_x = 0;
|
E.cursor_x = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorDelChar() {
|
void editorRowAppendRow(erow *dest, erow *src) {
|
||||||
erow *row;
|
// Allocate space for combined rows
|
||||||
if (E.cursor_y == E.numrows || !(E.cursor_x || E.cursor_y)) {
|
utf_8_char_t *new_chars = realloc(dest->chars,
|
||||||
return;
|
sizeof(utf_8_char_t) * (dest->size + src->size));
|
||||||
}
|
if (!new_chars) return;
|
||||||
row = &E.row[E.cursor_y];
|
|
||||||
|
dest->chars = new_chars;
|
||||||
|
|
||||||
|
// Copy source row characters
|
||||||
|
memcpy(&dest->chars[dest->size], src->chars, sizeof(utf_8_char_t) * src->size);
|
||||||
|
dest->size += src->size;
|
||||||
|
|
||||||
|
editorUpdateRow(dest);
|
||||||
|
++E.dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
void editorDelChar(void) {
|
||||||
|
if (E.state == READ_ONLY) return;
|
||||||
|
if (E.cursor_y == E.numrows) return;
|
||||||
|
if (E.cursor_x == 0 && E.cursor_y == 0) return;
|
||||||
|
|
||||||
|
erow *row = &E.row[E.cursor_y];
|
||||||
|
|
||||||
if (E.cursor_x > 0) {
|
if (E.cursor_x > 0) {
|
||||||
|
// Delete character before cursor
|
||||||
editorRowDelchar(row, E.cursor_x - 1);
|
editorRowDelchar(row, E.cursor_x - 1);
|
||||||
--E.cursor_x;
|
E.cursor_x--;
|
||||||
} else {
|
} else {
|
||||||
|
// At beginning of line - join with previous line
|
||||||
E.cursor_x = E.row[E.cursor_y - 1].size;
|
E.cursor_x = E.row[E.cursor_y - 1].size;
|
||||||
editorRowAppendString(&E.row[E.cursor_y - 1], row->chars, row->size);
|
editorRowAppendRow(&E.row[E.cursor_y - 1], row);
|
||||||
editorDelRow(E.cursor_y);
|
editorDelRow(E.cursor_y);
|
||||||
--E.cursor_y;
|
E.cursor_y--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+121
-34
@@ -7,6 +7,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
extern char *strdup(const char *);
|
extern char *strdup(const char *);
|
||||||
extern ssize_t getline(char **restrict lineptr, size_t *restrict n,
|
extern ssize_t getline(char **restrict lineptr, size_t *restrict n,
|
||||||
@@ -14,37 +15,56 @@ extern ssize_t getline(char **restrict lineptr, size_t *restrict n,
|
|||||||
extern int ftruncate(int fd, off_t length);
|
extern int ftruncate(int fd, off_t length);
|
||||||
extern struct editorConfig E;
|
extern struct editorConfig E;
|
||||||
|
|
||||||
|
// Convert utf_8_char_t array to byte string
|
||||||
char *editorRowsToString(int *buffer_len) {
|
char *editorRowsToString(int *buffer_len) {
|
||||||
int tot_len = 0;
|
int tot_len = 0;
|
||||||
int j;
|
int j, i;
|
||||||
char *buf;
|
char *buf;
|
||||||
char *p;
|
char *p;
|
||||||
|
|
||||||
|
// Calculate total byte length (not character count)
|
||||||
for (j = 0; j < E.numrows; ++j) {
|
for (j = 0; j < E.numrows; ++j) {
|
||||||
tot_len += E.row[j].size + 1;
|
// Count actual bytes in each character
|
||||||
|
for (i = 0; i < E.row[j].size; i++) {
|
||||||
|
tot_len += E.row[j].chars[i].len;
|
||||||
|
}
|
||||||
|
tot_len++; // For newline
|
||||||
}
|
}
|
||||||
|
|
||||||
*buffer_len = tot_len;
|
*buffer_len = tot_len;
|
||||||
buf = malloc(tot_len);
|
buf = malloc(tot_len);
|
||||||
|
if (!buf) return NULL;
|
||||||
|
|
||||||
p = buf;
|
p = buf;
|
||||||
for (j = 0; j < E.numrows; ++j) {
|
for (j = 0; j < E.numrows; ++j) {
|
||||||
memcpy(p, E.row[j].chars, E.row[j].size);
|
// Copy each character's bytes
|
||||||
p += E.row[j].size;
|
for (i = 0; i < E.row[j].size; i++) {
|
||||||
*p = '\n';
|
for (int k = 0; k < E.row[j].chars[i].len; k++) {
|
||||||
p++;
|
*p++ = E.row[j].chars[i].c[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*p++ = '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorCloseFile(void) {
|
void editorCloseFile(void) {
|
||||||
|
// Free all rows
|
||||||
|
for (int i = 0; i < E.numrows; i++) {
|
||||||
|
editorFreeRow(&E.row[i]);
|
||||||
|
}
|
||||||
|
|
||||||
E.cursor_x = 0;
|
E.cursor_x = 0;
|
||||||
E.cursor_y = 0;
|
E.cursor_y = 0;
|
||||||
E.rx = 0;
|
E.rx = 0;
|
||||||
E.row_offset = 0;
|
E.row_offset = 0;
|
||||||
E.col_offset = 0;
|
E.col_offset = 0;
|
||||||
E.numrows = 0;
|
E.numrows = 0;
|
||||||
|
free(E.row);
|
||||||
E.row = NULL;
|
E.row = NULL;
|
||||||
E.dirty = 0;
|
E.dirty = 0;
|
||||||
|
free(E.filename);
|
||||||
E.filename = NULL;
|
E.filename = NULL;
|
||||||
E.status_msg[0] = '\0';
|
E.status_msg[0] = '\0';
|
||||||
E.status_msg_time = 0;
|
E.status_msg_time = 0;
|
||||||
@@ -56,25 +76,29 @@ void editorOpen(char *filename) {
|
|||||||
// Test if a file is already open
|
// Test if a file is already open
|
||||||
if (E.filename != NULL) {
|
if (E.filename != NULL) {
|
||||||
editorCloseFile();
|
editorCloseFile();
|
||||||
E.state = READ_AND_WRITE;
|
|
||||||
}
|
}
|
||||||
|
E.state = READ_AND_WRITE;
|
||||||
|
|
||||||
free(E.filename);
|
|
||||||
E.filename = strdup(filename);
|
E.filename = strdup(filename);
|
||||||
|
|
||||||
fp = fopen(filename, "a+");
|
fp = fopen(filename, "r");
|
||||||
if (!fp)
|
if (!fp) {
|
||||||
die("fopen");
|
// File doesn't exist - that's okay, we'll create it on save
|
||||||
|
E.dirty = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
char *line = NULL;
|
char *line = NULL;
|
||||||
size_t line_cap = 0;
|
size_t line_cap = 0;
|
||||||
ssize_t line_len;
|
ssize_t line_len;
|
||||||
|
|
||||||
while ((line_len = getline(&line, &line_cap, fp)) != -1) {
|
while ((line_len = getline(&line, &line_cap, fp)) != -1) {
|
||||||
|
// Strip newline characters
|
||||||
while (line_len > 0 &&
|
while (line_len > 0 &&
|
||||||
(line[line_len - 1] == '\n' || line[line_len - 1] == '\r')) {
|
(line[line_len - 1] == '\n' || line[line_len - 1] == '\r')) {
|
||||||
--line_len;
|
--line_len;
|
||||||
}
|
}
|
||||||
|
// editorInsertRow will convert bytes to utf_8_char_t
|
||||||
editorInsertRow(E.numrows, line, line_len);
|
editorInsertRow(E.numrows, line, line_len);
|
||||||
}
|
}
|
||||||
free(line);
|
free(line);
|
||||||
@@ -86,6 +110,7 @@ void editorSave() {
|
|||||||
int len;
|
int len;
|
||||||
char *buf;
|
char *buf;
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
if (E.filename == NULL) {
|
if (E.filename == NULL) {
|
||||||
E.filename = editorPrompt("Save as: %s (ESC to cancel)", "", 1);
|
E.filename = editorPrompt("Save as: %s (ESC to cancel)", "", 1);
|
||||||
if (E.filename == NULL) {
|
if (E.filename == NULL) {
|
||||||
@@ -93,38 +118,100 @@ void editorSave() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buf = editorRowsToString(&len);
|
buf = editorRowsToString(&len);
|
||||||
fd = open(E.filename, O_RDWR | O_CREAT, 0644);
|
if (!buf) {
|
||||||
|
editorSetStatusMessage("Can't save! Memory error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = open(E.filename, O_RDWR | O_CREAT | O_TRUNC, 0644);
|
||||||
if (fd != -1) {
|
if (fd != -1) {
|
||||||
if (ftruncate(fd, len) != -1) {
|
if (write(fd, buf, len) == len) {
|
||||||
if (write(fd, buf, len) == len) {
|
close(fd);
|
||||||
close(fd);
|
free(buf);
|
||||||
free(buf);
|
E.dirty = 0;
|
||||||
E.dirty = 0;
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorFind() {
|
// Helper to convert utf_8_char_t array to byte string for searching
|
||||||
char *query = editorPrompt("Search: %s (ESC to cancel)", "", 0);
|
static char *row_to_string(erow *row) {
|
||||||
if (query == NULL) return;
|
// Calculate byte length
|
||||||
int i;
|
int byte_len = 0;
|
||||||
for (i = E.cursor_y + 1; i < E.numrows; i++) {
|
for (int i = 0; i < row->rsize; i++) {
|
||||||
erow *row = &E.row[i];
|
byte_len += row->render[i].len;
|
||||||
char *match = strstr(row->render, query);
|
|
||||||
if (match) {
|
|
||||||
E.cursor_y = i;
|
|
||||||
E.cursor_x = editorRowRxToCx(row, match - row->render);
|
|
||||||
E.row_offset = E.numrows;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
free(query);
|
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 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];
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -86,6 +86,6 @@ void initEditor() {
|
|||||||
(int)lisp_eval(lisp_read("QUIT-TIMES", &E.ctx_error, E.ctx), &E.ctx_error,
|
(int)lisp_eval(lisp_read("QUIT-TIMES", &E.ctx_error, E.ctx), &E.ctx_error,
|
||||||
E.ctx)
|
E.ctx)
|
||||||
.val.int_val;
|
.val.int_val;
|
||||||
|
fprintf(stderr, "Tab %d\n", E.constantes.QUIT_TIMES);
|
||||||
E.quit_times_buffer = E.constantes.QUIT_TIMES;
|
E.quit_times_buffer = E.constantes.QUIT_TIMES;
|
||||||
}
|
}
|
||||||
|
|||||||
+147
-155
@@ -1,200 +1,147 @@
|
|||||||
#include "../include/input.h"
|
#include "../include/input.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/define.h"
|
#include "include/data.h"
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
extern struct editorConfig E;
|
extern struct editorConfig E;
|
||||||
|
|
||||||
char * file_completion(const char *path) {
|
char *file_completion(const char *path) {
|
||||||
DIR * dir;
|
DIR *dir;
|
||||||
struct dirent *entry;
|
struct dirent *entry;
|
||||||
char directory[128];
|
char directory[128];
|
||||||
char predict[128];
|
char predict[128];
|
||||||
int predict_len = 0;
|
int predict_len = 0;
|
||||||
|
|
||||||
if (path[strlen(path) - 1] == '/') {
|
if (path[strlen(path) - 1] == '/') {
|
||||||
return path;
|
return strdup(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find dir name
|
// Find dir name
|
||||||
char * last_slash = strrchr(path, '/');
|
char *last_slash = strrchr(path, '/');
|
||||||
if (last_slash) {
|
if (last_slash) {
|
||||||
size_t dir_len = last_slash - path + 1; // length of dir_path
|
size_t dir_len = last_slash - path + 1;
|
||||||
strncpy(directory, path, dir_len);
|
strncpy(directory, path, dir_len);
|
||||||
predict_len = strlen(path) - dir_len - 1;
|
predict_len = strlen(path) - dir_len;
|
||||||
strncpy(predict, last_slash + 1, predict_len);
|
strncpy(predict, last_slash + 1, predict_len);
|
||||||
directory[dir_len] = '\0';
|
directory[dir_len] = '\0';
|
||||||
predict[predict_len] = '\0';
|
predict[predict_len] = '\0';
|
||||||
fprintf(stderr, "%s %s\n", directory, predict);
|
} else {
|
||||||
} else {
|
return NULL;
|
||||||
return NULL;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
dir = opendir(directory);
|
dir = opendir(directory);
|
||||||
if (!dir)
|
if (!dir)
|
||||||
return NULL;
|
|
||||||
|
|
||||||
while ((entry = readdir(dir)) != NULL) {
|
|
||||||
if (strncmp(entry->d_name, predict, predict_len) == 0) {
|
|
||||||
static char full_path[128];
|
|
||||||
snprintf(full_path, sizeof(full_path), "%s%s", directory, entry->d_name);
|
|
||||||
|
|
||||||
struct stat st;
|
|
||||||
if (stat(full_path, &st) == 0 && S_ISDIR(st.st_mode)) {
|
|
||||||
strcat(full_path, "/"); // add slash for directories
|
|
||||||
}
|
|
||||||
|
|
||||||
return strdup(full_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup when no more entries
|
|
||||||
closedir(dir);
|
|
||||||
dir = NULL;
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
while ((entry = readdir(dir)) != NULL) {
|
||||||
|
if (strncmp(entry->d_name, predict, predict_len) == 0) {
|
||||||
|
static char full_path[128];
|
||||||
|
snprintf(full_path, sizeof(full_path), "%s%s", directory, entry->d_name);
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
if (stat(full_path, &st) == 0 && S_ISDIR(st.st_mode)) {
|
||||||
|
strcat(full_path, "/");
|
||||||
|
}
|
||||||
|
closedir(dir);
|
||||||
|
return strdup(full_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dir);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \fn char * editorPrompt(struct editorConfig *E, char *prompt, char bPathMode)
|
* \fn char * editorPrompt(struct editorConfig *E, char *prompt, char bPathMode)
|
||||||
* \brief Return user input in a prompt when enter is hit. */
|
* \brief Return user input in a prompt when enter is hit. */
|
||||||
|
|
||||||
char *editorPrompt(char *prompt, char * placeHolder, char bPathMode) {
|
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);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
editorSetStatusMessage(prompt, buf);
|
editorSetStatusMessage(prompt, buf);
|
||||||
editorRefreshScreen();
|
editorRefreshScreen();
|
||||||
c = editorReadKey();
|
|
||||||
if (c == DEL_KEY || c == CTRL_KEY('h') || c == BACKSPACE) {
|
KeyInfo *key = editorReadKey();
|
||||||
|
|
||||||
|
// Handle backspace/delete
|
||||||
|
if (key->type == KEY_SPECIAL && (key->data.special == 127 || key->data.special == 8)) {
|
||||||
if (buf_len != 0) {
|
if (buf_len != 0) {
|
||||||
buf[--buf_len] = '\0';
|
buf[--buf_len] = '\0';
|
||||||
}
|
}
|
||||||
} else if (c == ESCAPE) {
|
}
|
||||||
|
// Handle Ctrl+H (backspace)
|
||||||
|
else if (key->type == KEY_CTRL && key->data.ctrl_char == 'H') {
|
||||||
|
if (buf_len != 0) {
|
||||||
|
buf[--buf_len] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Handle ESC
|
||||||
|
else if (key->type == KEY_SPECIAL && key->data.special == 27) {
|
||||||
editorSetStatusMessage("");
|
editorSetStatusMessage("");
|
||||||
free(buf);
|
free(buf);
|
||||||
return NULL;
|
return NULL;
|
||||||
} else if (c == '\r') {
|
}
|
||||||
|
// Handle Enter
|
||||||
|
else if (key->type == KEY_SPECIAL && (key->data.special == 13 || key->data.special == 10)) {
|
||||||
if (buf_len != 0) {
|
if (buf_len != 0) {
|
||||||
editorSetStatusMessage("");
|
editorSetStatusMessage("");
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
} else if (bPathMode && c == '\t') {
|
}
|
||||||
char path[128];
|
// Handle Tab for path completion
|
||||||
char * pwd;
|
else if (bPathMode && key->type == KEY_SPECIAL && key->data.special == 9) {
|
||||||
if (buf[0] != '/') {
|
char path[128];
|
||||||
pwd = getenv("PWD");
|
char *pwd;
|
||||||
fprintf(stderr, "%s\n", pwd);
|
if (buf[0] != '/') {
|
||||||
memcpy(path, pwd, strlen(pwd));
|
pwd = getenv("PWD");
|
||||||
path[strlen(pwd)] = '/';
|
snprintf(path, sizeof(path), "%s/%s", pwd, buf);
|
||||||
strncat(path, buf, buf_len);
|
} else {
|
||||||
} else {
|
strcpy(path, buf);
|
||||||
strcpy(path, buf);
|
}
|
||||||
}
|
|
||||||
memset(buf, 0, 128);
|
|
||||||
buf_len = 0;
|
|
||||||
strcpy(buf, file_completion(path));
|
|
||||||
buf_len = strlen(buf);
|
|
||||||
buf[buf_len] = '\0';
|
|
||||||
|
|
||||||
} else if (!iscntrl(c) && c < 128) {
|
char *completion = file_completion(path);
|
||||||
|
if (completion) {
|
||||||
|
memset(buf, 0, buf_size);
|
||||||
|
strcpy(buf, completion);
|
||||||
|
buf_len = strlen(buf);
|
||||||
|
free(completion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Handle regular characters (ASCII only for prompts)
|
||||||
|
else if (key->type == KEY_CHAR && key->data.codepoint < 128) {
|
||||||
if (buf_len == buf_size - 1) {
|
if (buf_len == buf_size - 1) {
|
||||||
buf_size *= 2;
|
buf_size *= 2;
|
||||||
buf = realloc(buf, buf_size);
|
buf = realloc(buf, buf_size);
|
||||||
}
|
}
|
||||||
buf[buf_len++] = c;
|
buf[buf_len++] = (char)key->data.codepoint;
|
||||||
buf[buf_len] = '\0';
|
buf[buf_len] = '\0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
char *key_to_string(int key) {
|
void editorMoveCursor(KeyInfo *key) {
|
||||||
static char key_str[32];
|
if (key->type != KEY_ARROW) return;
|
||||||
|
|
||||||
char tmp[10];
|
|
||||||
sprintf(tmp, "%d", key);
|
|
||||||
|
|
||||||
|
|
||||||
// First test enter key
|
|
||||||
|
|
||||||
if (key == '\r') {
|
|
||||||
strcpy(key_str, "ENTER");
|
|
||||||
} else if (key >= 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:
|
|
||||||
fprintf(stderr, "delete key\n");
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void editorMoveCursor(int key) {
|
|
||||||
erow *row = (E.cursor_y >= E.numrows) ? NULL : &E.row[E.cursor_y];
|
erow *row = (E.cursor_y >= E.numrows) ? NULL : &E.row[E.cursor_y];
|
||||||
int row_len;
|
int row_len;
|
||||||
switch (key) {
|
|
||||||
case ARROW_RIGHT:
|
switch (key->data.arrow) {
|
||||||
|
case 'C': // Right
|
||||||
if (row && E.cursor_x < row->size) {
|
if (row && E.cursor_x < row->size) {
|
||||||
++E.cursor_x;
|
++E.cursor_x;
|
||||||
} else if (row && E.cursor_x == row->size) {
|
} else if (row && E.cursor_x == row->size) {
|
||||||
@@ -202,17 +149,17 @@ void editorMoveCursor(int key) {
|
|||||||
E.cursor_x = 0;
|
E.cursor_x = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ARROW_DOWN:
|
case 'B': // Down
|
||||||
if (E.cursor_y < E.numrows) {
|
if (E.cursor_y < E.numrows) {
|
||||||
++E.cursor_y;
|
++E.cursor_y;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ARROW_UP:
|
case 'A': // Up
|
||||||
if (E.cursor_y != 0) {
|
if (E.cursor_y != 0) {
|
||||||
--E.cursor_y;
|
--E.cursor_y;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ARROW_LEFT:
|
case 'D': // Left
|
||||||
if (E.cursor_x != 0) {
|
if (E.cursor_x != 0) {
|
||||||
--E.cursor_x;
|
--E.cursor_x;
|
||||||
} else if (E.cursor_y > 0) {
|
} else if (E.cursor_y > 0) {
|
||||||
@@ -229,28 +176,73 @@ void editorMoveCursor(int key) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int executeKeyBind(char *key_sequence) {
|
KeyInfo *stringToCodepoint(const char *string) {
|
||||||
int i;
|
KeyInfo *key = (KeyInfo *)malloc(sizeof(KeyInfo));
|
||||||
for (i = 0; i < E.number_of_keybinds; ++i) {
|
// test control key
|
||||||
if (!strcmp(key_sequence, E.key_binds[i].key_sequence)) {
|
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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fprintf(stderr, "lisp function %s\n", key_sequence);
|
return key;
|
||||||
// It's a symbol, create a function call
|
}
|
||||||
lisp_eval(lisp_cons(E.key_binds[i].command, lisp_null(), E.ctx),
|
|
||||||
&E.ctx_error, E.ctx);
|
static int key_match(KeyInfo *a, KeyInfo *b) {
|
||||||
return 1;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorProcessKeypress() {
|
void editorProcessKeypress() {
|
||||||
int c = editorReadKey();
|
KeyInfo *key = editorReadKey();
|
||||||
|
if (!key)
|
||||||
|
return;
|
||||||
|
|
||||||
if (executeKeyBind(key_to_string(c))) {
|
if (executeKeyBind(key)) {
|
||||||
|
fprintf(stderr, "Keybinds found\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
editorInsertChar(c);
|
editorInsertChar(&key->c);
|
||||||
E.quit_times_buffer = E.constantes.QUIT_TIMES;
|
E.quit_times_buffer = E.constantes.QUIT_TIMES;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-1
@@ -6,6 +6,17 @@
|
|||||||
|
|
||||||
extern struct editorConfig E;
|
extern struct editorConfig E;
|
||||||
|
|
||||||
|
static void utf8_to_bytes(utf_8_char_t *chars, int count, unsigned char *output, int *output_len) {
|
||||||
|
int pos = 0;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
for (int j = 0; j < chars[i].len; j++) {
|
||||||
|
output[pos++] = chars[i].c[j];
|
||||||
|
}
|
||||||
|
fprintf(stderr, "bytes length : %s %d\n", chars[i].c, pos);
|
||||||
|
}
|
||||||
|
*output_len = pos;
|
||||||
|
}
|
||||||
|
|
||||||
void editorDrawRows(struct abuf *ab) {
|
void editorDrawRows(struct abuf *ab) {
|
||||||
int y;
|
int y;
|
||||||
char welcome[80];
|
char welcome[80];
|
||||||
@@ -41,7 +52,16 @@ void editorDrawRows(struct abuf *ab) {
|
|||||||
len = 0;
|
len = 0;
|
||||||
if (len > E.screencols)
|
if (len > E.screencols)
|
||||||
len = E.screencols;
|
len = E.screencols;
|
||||||
abAppend(ab, &E.row[file_row].render[E.col_offset], len);
|
if (len > 0) {
|
||||||
|
unsigned char *display_buf = malloc(len * 4); // Max 4 bytes per char
|
||||||
|
int byte_len;
|
||||||
|
|
||||||
|
utf8_to_bytes(&E.row[file_row].render[E.col_offset], len, display_buf,
|
||||||
|
&byte_len);
|
||||||
|
abAppend(ab, display_buf, byte_len);
|
||||||
|
fprintf(stderr, "display buffer : %s %d\n", display_buf, byte_len);
|
||||||
|
free(display_buf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
abAppend(ab, ERASE_END_LINE, 3);
|
abAppend(ab, ERASE_END_LINE, 3);
|
||||||
abAppend(ab, "\r\n", 2);
|
abAppend(ab, "\r\n", 2);
|
||||||
|
|||||||
+106
-25
@@ -1,4 +1,6 @@
|
|||||||
#include "../include/row_op.h"
|
#include "../include/row_op.h"
|
||||||
|
#include "include/data.h"
|
||||||
|
#include "include/define.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -6,11 +8,29 @@
|
|||||||
|
|
||||||
extern struct editorConfig E;
|
extern struct editorConfig E;
|
||||||
|
|
||||||
|
static int is_tab(utf_8_char_t *ch) {
|
||||||
|
return ch->len == 1 && ch->c[0] == '\t';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to check if two utf_8_char_t are equal
|
||||||
|
static int utf8_char_equal(utf_8_char_t *a, utf_8_char_t *b) {
|
||||||
|
if (a->len != b->len) return 0;
|
||||||
|
return memcmp(a->c, b->c, a->len) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to create a space character
|
||||||
|
static utf_8_char_t make_space() {
|
||||||
|
utf_8_char_t space;
|
||||||
|
space.c[0] = ' ';
|
||||||
|
space.len = 1;
|
||||||
|
return space;
|
||||||
|
}
|
||||||
|
|
||||||
int editorRowCxToRx(erow *row, int cursor_x) {
|
int editorRowCxToRx(erow *row, int cursor_x) {
|
||||||
int render_x = 0;
|
int render_x = 0;
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < cursor_x; ++i) {
|
for (i = 0; i < cursor_x; ++i) {
|
||||||
if (row->chars[i] == '\t') {
|
if (is_tab(&row->chars[i])) {
|
||||||
render_x += (E.constantes.TAB_LENGTH - 1) - (render_x % E.constantes.TAB_LENGTH);
|
render_x += (E.constantes.TAB_LENGTH - 1) - (render_x % E.constantes.TAB_LENGTH);
|
||||||
}
|
}
|
||||||
render_x++;
|
render_x++;
|
||||||
@@ -22,7 +42,7 @@ int editorRowRxToCx(erow *row, int rx) {
|
|||||||
int cur_rx = 0;
|
int cur_rx = 0;
|
||||||
int cx;
|
int cx;
|
||||||
for (cx = 0; cx < row->size; cx++) {
|
for (cx = 0; cx < row->size; cx++) {
|
||||||
if (row->chars[cx] == '\t')
|
if (is_tab(&row->chars[cx]))
|
||||||
cur_rx += (E.constantes.TAB_LENGTH - 1) - (cur_rx % E.constantes.TAB_LENGTH);
|
cur_rx += (E.constantes.TAB_LENGTH - 1) - (cur_rx % E.constantes.TAB_LENGTH);
|
||||||
cur_rx++;
|
cur_rx++;
|
||||||
if (cur_rx > rx) return cx;
|
if (cur_rx > rx) return cx;
|
||||||
@@ -39,40 +59,42 @@ void editorUpdateRow(erow *row) {
|
|||||||
int i, i_render;
|
int i, i_render;
|
||||||
int tabs = 0;
|
int tabs = 0;
|
||||||
|
|
||||||
// counting number of tabs
|
// Count number of tabs
|
||||||
|
|
||||||
for (i = 0; i < row->size; ++i) {
|
for (i = 0; i < row->size; ++i) {
|
||||||
tabs +=
|
if (is_tab(&row->chars[i])) {
|
||||||
(row->chars[i] == '\t'); /**< increment tabs of 1 if chars[i] is one. */
|
tabs++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free(row->render);
|
free(row->render);
|
||||||
row->render = malloc(row->size + tabs * (E.constantes.TAB_LENGTH - 1) +
|
// Allocate space for utf_8_char_t array
|
||||||
1); /**< Tabs needs E.constantes.TAB_LENGTH chars so E.constantes.TAB_LENGTH - 1
|
row->render = malloc(sizeof(utf_8_char_t) * (row->size + tabs * (E.constantes.TAB_LENGTH - 1)));
|
||||||
more than the first already counted. */
|
|
||||||
|
if (!row->render) {
|
||||||
|
row->rsize = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// end of counting
|
|
||||||
i_render = 0;
|
i_render = 0;
|
||||||
for (i = 0; i < row->size; ++i) {
|
for (i = 0; i < row->size; ++i) {
|
||||||
if (row->chars[i] == '\t') {
|
if (is_tab(&row->chars[i])) {
|
||||||
row->render[i_render++] = ' ';
|
// Replace tab with spaces
|
||||||
|
row->render[i_render++] = make_space();
|
||||||
while (i_render % E.constantes.TAB_LENGTH) {
|
while (i_render % E.constantes.TAB_LENGTH) {
|
||||||
row->render[i_render++] =
|
row->render[i_render++] = make_space();
|
||||||
' '; /**< Addind the right amount of spaces for tabs */
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
row->render[i_render++] = row->chars[i];
|
row->render[i_render++] = row->chars[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
row->render[i_render] = '\0'; // Don't forget the end of string character.
|
|
||||||
row->rsize = i_render;
|
row->rsize = i_render;
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorInsertRow(int at, char *s, size_t len) {
|
void editorInsertRow(int at, char *s, size_t len) {
|
||||||
if (at < 0 || at > E.numrows) {
|
if (at < 0 || at > E.numrows) {
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
erow *tmp = (erow *)realloc(E.row, sizeof(erow) * (E.numrows + 1));
|
erow *tmp = (erow *)realloc(E.row, sizeof(erow) * (E.numrows + 1));
|
||||||
if (!tmp) {
|
if (!tmp) {
|
||||||
return;
|
return;
|
||||||
@@ -80,19 +102,78 @@ void editorInsertRow(int at, char *s, size_t len) {
|
|||||||
E.row = tmp;
|
E.row = tmp;
|
||||||
memmove(&E.row[at + 1], &E.row[at], sizeof(erow) * (E.numrows - at));
|
memmove(&E.row[at + 1], &E.row[at], sizeof(erow) * (E.numrows - at));
|
||||||
|
|
||||||
E.row[at].size = len;
|
// Initialize the new row
|
||||||
E.row[at].chars = malloc(len + 1);
|
E.row[at].size = 0;
|
||||||
memcpy(E.row[at].chars, s, len);
|
E.row[at].chars = NULL;
|
||||||
E.row[at].chars[len] = '\0';
|
|
||||||
|
|
||||||
E.row[at].rsize = 0;
|
E.row[at].rsize = 0;
|
||||||
E.row[at].render = NULL;
|
E.row[at].render = NULL;
|
||||||
|
|
||||||
|
// Count UTF-8 characters first
|
||||||
|
int char_count = 0;
|
||||||
|
int i = 0;
|
||||||
|
while (i < len) {
|
||||||
|
unsigned char first = (unsigned char)s[i];
|
||||||
|
int char_len;
|
||||||
|
|
||||||
|
if ((first & 0x80) == 0) {
|
||||||
|
char_len = 1;
|
||||||
|
} else if ((first & 0xE0) == 0xC0) {
|
||||||
|
char_len = 2;
|
||||||
|
} else if ((first & 0xF0) == 0xE0) {
|
||||||
|
char_len = 3;
|
||||||
|
} else if ((first & 0xF8) == 0xF0) {
|
||||||
|
char_len = 4;
|
||||||
|
} else {
|
||||||
|
char_len = 1; // Invalid, treat as single byte
|
||||||
|
}
|
||||||
|
|
||||||
|
i += char_len;
|
||||||
|
char_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate for the actual number of characters
|
||||||
|
if (char_count > 0) {
|
||||||
|
E.row[at].chars = malloc(sizeof(utf_8_char_t) * char_count);
|
||||||
|
if (!E.row[at].chars) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now convert to utf_8_char_t array
|
||||||
|
i = 0;
|
||||||
|
E.row[at].size = 0;
|
||||||
|
while (i < len && E.row[at].size < char_count) {
|
||||||
|
utf_8_char_t ch;
|
||||||
|
|
||||||
|
unsigned char first = (unsigned char)s[i];
|
||||||
|
if ((first & 0x80) == 0) {
|
||||||
|
ch.len = 1;
|
||||||
|
} else if ((first & 0xE0) == 0xC0) {
|
||||||
|
ch.len = 2;
|
||||||
|
} else if ((first & 0xF0) == 0xE0) {
|
||||||
|
ch.len = 3;
|
||||||
|
} else if ((first & 0xF8) == 0xF0) {
|
||||||
|
ch.len = 4;
|
||||||
|
} else {
|
||||||
|
ch.len = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy bytes
|
||||||
|
for (int j = 0; j < ch.len && i < len; j++) {
|
||||||
|
ch.c[j] = s[i++];
|
||||||
|
}
|
||||||
|
|
||||||
|
E.row[at].chars[E.row[at].size++] = ch;
|
||||||
|
}
|
||||||
|
|
||||||
editorUpdateRow(&E.row[at]);
|
editorUpdateRow(&E.row[at]);
|
||||||
|
|
||||||
|
|
||||||
++E.numrows;
|
++E.numrows;
|
||||||
++E.dirty;
|
++E.dirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void editorFreeRow(erow *row) {
|
void editorFreeRow(erow *row) {
|
||||||
free(row->render);
|
free(row->render);
|
||||||
free(row->chars);
|
free(row->chars);
|
||||||
@@ -112,16 +193,17 @@ void editorDelRow(int at) {
|
|||||||
* \fn editorRowInsertChar(erow *row, int at, int c)
|
* \fn editorRowInsertChar(erow *row, int at, int c)
|
||||||
* \param at Index of where we want to insert the char */
|
* \param at Index of where we want to insert the char */
|
||||||
|
|
||||||
void editorRowInsertChar(erow *row, int at, int c) {
|
void editorRowInsertChar(erow *row, int at, utf_8_char_t c) {
|
||||||
if (E.state == READ_ONLY)
|
if (E.state == READ_ONLY)
|
||||||
return;
|
return;
|
||||||
if (at < 0 || at > row->size) {
|
if (at < 0 || at > row->size) {
|
||||||
at = row->size;
|
at = row->size;
|
||||||
}
|
}
|
||||||
row->chars = realloc(row->chars, row->size + 2);
|
row->chars = realloc(row->chars, row->size + 1);
|
||||||
memmove(&row->chars[at + 1], &row->chars[at], row->size - at + 1);
|
memmove(&row->chars[at + 1], &row->chars[at], row->size - at + 1);
|
||||||
++row->size;
|
++(row->size);
|
||||||
row->chars[at] = c;
|
row->chars[at] = c;
|
||||||
|
fprintf(stderr, "Row insert : %s %d\n", c.c, c.len);
|
||||||
editorUpdateRow(row);
|
editorUpdateRow(row);
|
||||||
++E.dirty;
|
++E.dirty;
|
||||||
}
|
}
|
||||||
@@ -130,7 +212,6 @@ void editorRowAppendString(erow *row, char *s, size_t len) {
|
|||||||
row->chars = realloc(row->chars, row->size + len + 1);
|
row->chars = realloc(row->chars, row->size + len + 1);
|
||||||
memcpy(&row->chars[row->size], s, len);
|
memcpy(&row->chars[row->size], s, len);
|
||||||
row->size += len;
|
row->size += len;
|
||||||
row->chars[row->size] = '\0';
|
|
||||||
editorUpdateRow(row);
|
editorUpdateRow(row);
|
||||||
++E.dirty;
|
++E.dirty;
|
||||||
}
|
}
|
||||||
|
|||||||
+197
-57
@@ -2,6 +2,8 @@
|
|||||||
#include "../include/data.h"
|
#include "../include/data.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
void die(const char *s) {
|
void die(const char *s) {
|
||||||
write(STDOUT_FILENO, "\x1b[2J", 4);
|
write(STDOUT_FILENO, "\x1b[2J", 4);
|
||||||
@@ -35,73 +37,211 @@ void enableRawMode() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int editorReadKey() {
|
int utf8_char_length(unsigned char first_byte) {
|
||||||
int nread;
|
if ((first_byte & 0x80) == 0)
|
||||||
char c;
|
return 1; // 0xxxxxxx - ASCII
|
||||||
char seq[3];
|
if ((first_byte & 0xE0) == 0xC0)
|
||||||
while ((nread = read(STDIN_FILENO, &c, 1)) != 1) {
|
return 2; // 110xxxxx - 2 bytes
|
||||||
if (nread == -1 && errno != EAGAIN) {
|
if ((first_byte & 0xF0) == 0xE0)
|
||||||
die("read");
|
return 3; // 1110xxxx - 3 bytes
|
||||||
|
if ((first_byte & 0xF8) == 0xF0)
|
||||||
|
return 4; // 11110xxx - 4 bytes
|
||||||
|
return 1; // Invalid, treat as single byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert UTF-8 to Unicode code point
|
||||||
|
unsigned int utf8_to_codepoint(const unsigned char *bytes, int len) {
|
||||||
|
if (len == 1)
|
||||||
|
return bytes[0];
|
||||||
|
if (len == 2)
|
||||||
|
return ((bytes[0] & 0x1F) << 6) | (bytes[1] & 0x3F);
|
||||||
|
if (len == 3)
|
||||||
|
return ((bytes[0] & 0x0F) << 12) | ((bytes[1] & 0x3F) << 6) |
|
||||||
|
(bytes[2] & 0x3F);
|
||||||
|
if (len == 4)
|
||||||
|
return ((bytes[0] & 0x07) << 18) | ((bytes[1] & 0x3F) << 12) |
|
||||||
|
((bytes[2] & 0x3F) << 6) | (bytes[3] & 0x3F);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_key(unsigned char *seq, int len, KeyInfo *key) {
|
||||||
|
memcpy(key->c.c, seq, len);
|
||||||
|
key->c.len = len;
|
||||||
|
key->modifiers = MOD_NONE;
|
||||||
|
key->type = KEY_UNKNOWN;
|
||||||
|
|
||||||
|
// Control characters (Ctrl+A to Ctrl+Z)
|
||||||
|
if (len == 1 && seq[0] < 32 && seq[0] != 27 && seq[0] != 9 && seq[0] != 10 &&
|
||||||
|
seq[0] != 13) {
|
||||||
|
key->type = KEY_CTRL;
|
||||||
|
key->data.ctrl_char = seq[0] + 64;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special single characters
|
||||||
|
if (len == 1) {
|
||||||
|
switch (seq[0]) {
|
||||||
|
case 9:
|
||||||
|
case 10:
|
||||||
|
case 13:
|
||||||
|
case 27:
|
||||||
|
case 127:
|
||||||
|
key->type = KEY_SPECIAL;
|
||||||
|
key->data.special = seq[0];
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c == '\x1b') {
|
// Escape sequences
|
||||||
if (read(STDIN_FILENO, &seq[0], 1) != 1 ||
|
if (len >= 2 && seq[0] == 27) {
|
||||||
read(STDIN_FILENO, &seq[1], 1) != 1) {
|
// Alt+key combinations
|
||||||
return '\x1b';
|
if (len == 2 && seq[1] >= 32 && seq[1] < 127) {
|
||||||
|
key->type = KEY_ALT;
|
||||||
|
key->data.alt_char = seq[1];
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (seq[0] == '[') {
|
|
||||||
if (seq[1] >= '0' && seq[1] <= '9') {
|
// CSI sequences (ESC [ ...)
|
||||||
if (read(STDIN_FILENO, &seq[2], 1) != 1) {
|
if (len >= 3 && seq[1] == '[') {
|
||||||
return '\x1b';
|
// Arrow keys
|
||||||
|
if (len == 3) {
|
||||||
|
switch (seq[2]) {
|
||||||
|
case 'A':
|
||||||
|
case 'B':
|
||||||
|
case 'C':
|
||||||
|
case 'D':
|
||||||
|
key->type = KEY_ARROW;
|
||||||
|
key->data.arrow = seq[2];
|
||||||
|
return;
|
||||||
|
case 'H':
|
||||||
|
case 'F':
|
||||||
|
key->type = KEY_NAVIGATION;
|
||||||
|
key->data.special = seq[2];
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (seq[2] == '~') {
|
}
|
||||||
switch (seq[1]) {
|
|
||||||
case '1':
|
// Modified keys (ESC [ 1 ; modifier letter)
|
||||||
return BEG_LINE;
|
if (len >= 6 && seq[2] == '1' && seq[3] == ';') {
|
||||||
case '3':
|
int modifier = seq[4] - '0';
|
||||||
return DEL_KEY;
|
char k = seq[5];
|
||||||
case '4':
|
|
||||||
return END_LINE;
|
if (modifier & 1)
|
||||||
case '5':
|
key->modifiers |= MOD_SHIFT;
|
||||||
return PAGE_UP;
|
if (modifier & 2)
|
||||||
case '6':
|
key->modifiers |= MOD_ALT;
|
||||||
return PAGE_DOWN;
|
if (modifier & 4)
|
||||||
case '7':
|
key->modifiers |= MOD_CTRL;
|
||||||
return BEG_LINE;
|
|
||||||
case '8':
|
switch (k) {
|
||||||
return END_LINE;
|
case 'A':
|
||||||
|
case 'B':
|
||||||
|
case 'C':
|
||||||
|
case 'D':
|
||||||
|
key->type = KEY_ARROW;
|
||||||
|
key->data.arrow = k;
|
||||||
|
return;
|
||||||
|
case 'H':
|
||||||
|
case 'F':
|
||||||
|
key->type = KEY_NAVIGATION;
|
||||||
|
key->data.special = k;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
|
|
||||||
switch (seq[1]) {
|
|
||||||
case 'A':
|
|
||||||
return ARROW_UP;
|
|
||||||
case 'B':
|
|
||||||
return ARROW_DOWN;
|
|
||||||
case 'C':
|
|
||||||
return ARROW_RIGHT;
|
|
||||||
case 'D':
|
|
||||||
return ARROW_LEFT;
|
|
||||||
case 'H':
|
|
||||||
return BEG_LINE;
|
|
||||||
case 'F':
|
|
||||||
return END_LINE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (seq[0] == 'O') {
|
|
||||||
switch (seq[1]) {
|
|
||||||
case 'H':
|
|
||||||
return BEG_LINE;
|
|
||||||
case 'F':
|
|
||||||
return END_LINE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return '\x1b';
|
|
||||||
} else {
|
// SS3 sequences (ESC O ...)
|
||||||
return c;
|
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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user