Fully fonctional editing

This commit is contained in:
Arthur Barraux
2024-10-10 02:09:48 +02:00
parent 1c22c3beca
commit a6f32d4c49
15 changed files with 249 additions and 47 deletions
+9 -6
View File
@@ -6,15 +6,11 @@
BELUGA_OUTPUT=bin BELUGA_OUTPUT=bin
BUILD_DIR=build
BUILD_FLAGS=-Wall -Wextra -pedantic BUILD_FLAGS=-Wall -Wextra -pedantic
build: main.c src/* build: main.c src/*
if [ ! -d $(BELUGA_OUTPUT) ]; then mkdir $(BELUGA_OUTPUT); fi if [ ! -d $(BELUGA_OUTPUT) ]; then mkdir $(BELUGA_OUTPUT); fi
if [ ! -d doc/ ]; then mkdir doc; fi $(CC) main.c -o $(BELUGA_OUTPUT)/beluga src/* $(BUILD_FLAGS)
$(CC) main.c -o $(BELUGA_OUTPUT)/beluga src/*
doxygen
DEBUG_FLAGS=-Wall -Wextra -pedantic -Werror -fsanitize=address -g DEBUG_FLAGS=-Wall -Wextra -pedantic -Werror -fsanitize=address -g
@@ -22,8 +18,15 @@ debug: main.c src/*
if [ ! -d $(BELUGA_OUTPUT) ]; then mkdir $(BELUGA_OUTPUT); fi if [ ! -d $(BELUGA_OUTPUT) ]; then mkdir $(BELUGA_OUTPUT); fi
$(CC) main.c -o $(BELUGA_OUTPUT)/beluga src/* $(DEBUG_FLAGS) $(CC) main.c -o $(BELUGA_OUTPUT)/beluga src/* $(DEBUG_FLAGS)
doc:
if [ ! -d doc/ ]; then mkdir doc; fi
doxygen
clean: clean:
rm -r $(BELUGA_OUTPUT)/* rm -r $(BELUGA_OUTPUT)
rm -rf doc/ rm -rf doc/
rm -rf tmp/
all: build doc
# end # end
+22
View File
@@ -1,3 +1,25 @@
# Beluga # Beluga
Beluga is a project of CLI text editor that will fit perfectly with your azerty keyboard. Beluga is a project of CLI text editor that will fit perfectly with your azerty keyboard.
## Requirements
You will only need **make** or **gcc** to compile the editor.
## Installation
Here is the installation line :
```git clone https://github.com/le-cocotier/beluga.git ~/.beluga && cd ~/.beluga && make build```
The executable file will be in `bin/beluga`. Feel free to add it to your path.
You can either run `make all` if you're interested by the doxygen documentation.
## Getting start
To open an existing file just type :
```beluga path_to_my_file```
The only keybinds that you will need will be :
- Ctrl-Q : leave the editor
- Ctrl-S : Save a file
+1
View File
@@ -30,6 +30,7 @@ struct editorConfig {
int screencols; /**< Terminal width*/ int screencols; /**< Terminal width*/
int numrows; /**< Number of rows contained */ int numrows; /**< Number of rows contained */
erow *row; /**< Store all the rows printed */ erow *row; /**< Store all the rows printed */
int dirty;
char *filename; char *filename;
char status_msg[80]; char status_msg[80];
time_t status_msg_time; time_t status_msg_time;
+9 -7
View File
@@ -3,6 +3,7 @@
#define CTRL_KEY(k) ((k) & 0x1f) #define CTRL_KEY(k) ((k) & 0x1f)
#define ESCAPE '\x1b'
#define CURSOR_TOP_LEFT "\x1b[H" #define CURSOR_TOP_LEFT "\x1b[H"
#define HIDE_CURSOR "\x1b[?25l" #define HIDE_CURSOR "\x1b[?25l"
#define SHOW_CURSOR "\x1b[?25h" #define SHOW_CURSOR "\x1b[?25h"
@@ -10,12 +11,12 @@
enum editorKey { enum editorKey {
BACKSPACE = 127, BACKSPACE = 127,
CURSOR_LEFT = 1000, ARROW_LEFT = 1000,
CURSOR_RIGHT, ARROW_RIGHT,
CURSOR_UP, ARROW_UP,
CURSOR_DOWN, ARROW_DOWN,
DEL_KEY,
BEG_LINE, BEG_LINE,
DELETE,
END_LINE, END_LINE,
PAGE_UP, PAGE_UP,
PAGE_DOWN, PAGE_DOWN,
@@ -23,7 +24,8 @@ enum editorKey {
#define ABUF_INIT {NULL, 0} #define ABUF_INIT {NULL, 0}
#define BELUGA_VERSION "0.1" #define BELUGA_VERSION "1.0"
#define TAB_LENGTH 8 #define TAB_LENGTH 4
#define QUIT_TIMES 1
#endif // DEFINE_H_ #endif // DEFINE_H_
+4
View File
@@ -4,4 +4,8 @@
#include "data.h" #include "data.h"
void editorInsertChar(struct editorConfig *E, int c); void editorInsertChar(struct editorConfig *E, int c);
void editorInsertNewLine(struct editorConfig *E);
void editorDelChar(struct editorConfig *E);
#endif // EDITOR_OP_H_ #endif // EDITOR_OP_H_
+3
View File
@@ -3,6 +3,7 @@
#include "data.h" #include "data.h"
#include "define.h" #include "define.h"
#include "output.h"
#include "terminal.h" #include "terminal.h"
#include <unistd.h> #include <unistd.h>
@@ -18,6 +19,8 @@
// END \x1b[4~ || <esc>[8~ || <esc>[F || <esc>OF // END \x1b[4~ || <esc>[8~ || <esc>[F || <esc>OF
// DELETE \x1b[3~ // DELETE \x1b[3~
char *editorPrompt(struct editorConfig *E, char *prompt);
void editorMoveCursor(struct editorConfig *E, int key); void editorMoveCursor(struct editorConfig *E, int key);
/** /**
+12 -2
View File
@@ -5,14 +5,24 @@
#include "define.h" #include "define.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h>
#include <unistd.h> #include <unistd.h>
int editorRowCxToRx(erow *row, int cursor_x); int editorRowCxToRx(erow *row, int cursor_x);
void editorUpdateRow(erow *row); void editorUpdateRow(erow *row);
void editorAppendRow(struct editorConfig *E, char *s, size_t len); void editorInsertRow(struct editorConfig *E, int at, char *s, size_t len);
void editorRowInsertChar(erow *row, int at, int c); void editorFreeRow(erow *row);
void editorDelRow(struct editorConfig *E, int at);
void editorRowInsertChar(struct editorConfig *E, erow *row, int at, int c);
void editorRowAppendString(struct editorConfig *E, erow *row, char *s,
size_t len);
void editorRowDelchar(struct editorConfig *E, erow *row, int at);
#endif // ROW_OP_H_ #endif // ROW_OP_H_
+1 -1
View File
@@ -26,7 +26,7 @@ int main(int argc, char *argv[]) {
editorOpen(&E, argv[1]); editorOpen(&E, argv[1]);
} }
editorSetStatusMessage(&E, "HELP: Ctrl-Q = quit"); editorSetStatusMessage(&E, "HELP: Ctrl-S = save | Ctrl-Q = quit");
while (1) { while (1) {
editorRefreshScreen(&E); editorRefreshScreen(&E);
+36 -2
View File
@@ -3,8 +3,42 @@
void editorInsertChar(struct editorConfig *E, int c) { void editorInsertChar(struct editorConfig *E, int c) {
if (E->cursor_y == E->numrows) { if (E->cursor_y == E->numrows) {
editorAppendRow(E, "", 0); editorInsertRow(E, E->numrows, "", 0);
} }
editorRowInsertChar(&E->row[E->cursor_y], E->cursor_x, c); editorRowInsertChar(E, &E->row[E->cursor_y], E->cursor_x, c);
E->cursor_x++; E->cursor_x++;
} }
void editorInsertNewLine(struct editorConfig *E) {
erow *row;
if (!E->cursor_x) {
editorInsertRow(E, E->cursor_y, "", 0);
} else {
row = &E->row[E->cursor_y];
editorInsertRow(E, 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);
}
++E->cursor_y;
E->cursor_x = 0;
}
void editorDelChar(struct editorConfig *E) {
erow *row;
if (E->cursor_y == E->numrows || !(E->cursor_x || E->cursor_y)) {
return;
}
row = &E->row[E->cursor_y];
if (E->cursor_x > 0) {
editorRowDelchar(E, row, E->cursor_x - 1);
--E->cursor_x;
} else {
E->cursor_x = E->row[E->cursor_y - 1].size;
editorRowAppendString(E, &E->row[E->cursor_y - 1], row->chars, row->size);
editorDelRow(E, E->cursor_y);
--E->cursor_y;
}
}
+22 -5
View File
@@ -1,4 +1,6 @@
#include "../include/file_io.h" #include "../include/file_io.h"
#include "../include/input.h"
#include "../include/output.h"
#include <fcntl.h> #include <fcntl.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -47,10 +49,11 @@ void editorOpen(struct editorConfig *E, char *filename) {
(line[line_len - 1] == '\n' || line[line_len - 1] == '\r')) { (line[line_len - 1] == '\n' || line[line_len - 1] == '\r')) {
--line_len; --line_len;
} }
editorAppendRow(E, line, line_len); editorInsertRow(E, E->numrows, line, line_len);
} }
free(line); free(line);
fclose(fp); fclose(fp);
E->dirty = 0;
} }
void editorSave(struct editorConfig *E) { void editorSave(struct editorConfig *E) {
@@ -58,12 +61,26 @@ void editorSave(struct editorConfig *E) {
char *buf; char *buf;
int fd; int fd;
if (E->filename == NULL) { if (E->filename == NULL) {
return; E->filename = editorPrompt(E, "Save as: %s (ESC to cancel)");
if (E->filename == NULL) {
editorSetStatusMessage(E, "Save aborted");
return;
}
} }
buf = editorRowsToString(E, &len); buf = editorRowsToString(E, &len);
fd = open(E->filename, O_RDWR | O_CREAT, 0644); fd = open(E->filename, O_RDWR | O_CREAT, 0644);
ftruncate(fd, len); if (fd != -1) {
write(fd, buf, len); if (ftruncate(fd, len) != -1) {
close(fd); if (write(fd, buf, len) == len) {
close(fd);
free(buf);
E->dirty = 0;
editorSetStatusMessage(E, "%d bytes written to disk", len);
return;
}
}
close(fd);
}
free(buf); free(buf);
editorSetStatusMessage(E, "Can't save! I/O error: %s", strerror(errno));
} }
+1
View File
@@ -8,6 +8,7 @@ void initEditor(struct editorConfig *E) {
E->col_offset = 0; E->col_offset = 0;
E->numrows = 0; E->numrows = 0;
E->row = NULL; E->row = NULL;
E->dirty = 0;
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;
+70 -12
View File
@@ -1,13 +1,58 @@
#include "../include/input.h" #include "../include/input.h"
#include "../include/editor_op.h" #include "../include/editor_op.h"
#include "../include/file_io.h" #include "../include/file_io.h"
#include "../include/output.h"
#include <ctype.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
/**
* \fn char * editorPrompt(struct editorConfig *E, char *prompt)
* \brief Return user input in a prompt when enter is hit. */
char *editorPrompt(struct editorConfig *E, char *prompt) {
size_t buf_size = 128;
char *buf = malloc(buf_size);
size_t buf_len = 0;
int c = 0;
buf[0] = '\0';
while (1) {
editorSetStatusMessage(E, prompt, buf);
editorRefreshScreen(E);
c = editorReadKey();
if (c == DEL_KEY || c == CTRL_KEY('h') || c == BACKSPACE) {
if (buf_len != 0) {
buf[--buf_len] = '\0';
}
} else if (c == ESCAPE) {
fprintf(stderr, "escape");
editorSetStatusMessage(E, "");
free(buf);
return NULL;
} else if (c == '\r') {
if (buf_len != 0) {
editorSetStatusMessage(E, "");
return buf;
}
} else if (!iscntrl(c) && c < 128) {
if (buf_len == buf_size - 1) {
buf_size *= 2;
buf = realloc(buf, buf_size);
}
buf[buf_len++] = c;
buf[buf_len] = '\0';
}
}
}
void editorMoveCursor(struct editorConfig *E, int key) { void editorMoveCursor(struct editorConfig *E, int key) {
erow *row = (E->cursor_y >= E->numrows) ? NULL : &E->row[E->cursor_y]; erow *row = (E->cursor_y >= E->numrows) ? NULL : &E->row[E->cursor_y];
int row_len; int row_len;
switch (key) { switch (key) {
case CURSOR_RIGHT: case ARROW_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) {
@@ -15,17 +60,17 @@ void editorMoveCursor(struct editorConfig *E, int key) {
E->cursor_x = 0; E->cursor_x = 0;
} }
break; break;
case CURSOR_DOWN: case ARROW_DOWN:
if (E->cursor_y < E->numrows) { if (E->cursor_y < E->numrows) {
++E->cursor_y; ++E->cursor_y;
} }
break; break;
case CURSOR_UP: case ARROW_UP:
if (E->cursor_y != 0) { if (E->cursor_y != 0) {
--E->cursor_y; --E->cursor_y;
} }
break; break;
case CURSOR_LEFT: case ARROW_LEFT:
if (E->cursor_x != 0) { if (E->cursor_x != 0) {
--E->cursor_x; --E->cursor_x;
} else if (E->cursor_y > 0) { } else if (E->cursor_y > 0) {
@@ -43,14 +88,23 @@ void editorMoveCursor(struct editorConfig *E, int key) {
} }
void editorProcessKeypress(struct editorConfig *E) { void editorProcessKeypress(struct editorConfig *E) {
static int quit_times = QUIT_TIMES;
int c = editorReadKey(); int c = editorReadKey();
int times; int times;
switch (c) { switch (c) {
case '\r': case '\r':
/* TODO */ editorInsertNewLine(E);
break; break;
case CTRL_KEY('q'): case CTRL_KEY('q'):
if (E->dirty && quit_times > 0) {
editorSetStatusMessage(E,
"WARNING! Changes hasn't been saved. Press Ctrl-Q "
"another time to quit.");
--quit_times;
return;
}
write(STDOUT_FILENO, "\x1b[2J", 4); write(STDOUT_FILENO, "\x1b[2J", 4);
write(STDOUT_FILENO, CURSOR_TOP_LEFT, 3); write(STDOUT_FILENO, CURSOR_TOP_LEFT, 3);
disableRawMode(E); disableRawMode(E);
@@ -73,8 +127,11 @@ void editorProcessKeypress(struct editorConfig *E) {
case BACKSPACE: case BACKSPACE:
case CTRL_KEY('h'): case CTRL_KEY('h'):
// case DEL_KEY: case DEL_KEY:
/* TODO */ if (c == DEL_KEY) {
editorMoveCursor(E, ARROW_RIGHT);
}
editorDelChar(E);
break; break;
case PAGE_UP: case PAGE_UP:
@@ -89,14 +146,14 @@ void editorProcessKeypress(struct editorConfig *E) {
} }
times = E->screenrows; times = E->screenrows;
while (--times) { while (--times) {
editorMoveCursor(E, c == PAGE_UP ? CURSOR_UP : CURSOR_DOWN); editorMoveCursor(E, c == PAGE_UP ? ARROW_UP : ARROW_DOWN);
} }
} break; } break;
case CURSOR_UP: case ARROW_UP:
case CURSOR_DOWN: case ARROW_DOWN:
case CURSOR_LEFT: case ARROW_LEFT:
case CURSOR_RIGHT: case ARROW_RIGHT:
editorMoveCursor(E, c); editorMoveCursor(E, c);
break; break;
@@ -107,4 +164,5 @@ void editorProcessKeypress(struct editorConfig *E) {
editorInsertChar(E, c); editorInsertChar(E, c);
break; break;
} }
quit_times = QUIT_TIMES;
} }
+3 -2
View File
@@ -71,8 +71,9 @@ void editorDrawStatusBar(struct editorConfig *E, struct abuf *ab) {
char status[80], render_status[80]; char status[80], render_status[80];
abAppend(ab, "\x1b[7m", 4); // inverting colors abAppend(ab, "\x1b[7m", 4); // inverting colors
len = snprintf(status, sizeof(status), "%.20s - %d lines", len = snprintf(status, sizeof(status), "%.20s - %d lines%s",
E->filename ? E->filename : "[No Name]", E->numrows); E->filename ? E->filename : "[No Name]", E->numrows,
E->dirty ? "*" : "");
render_len = snprintf(render_status, sizeof(render_status), "%d/%d", render_len = snprintf(render_status, sizeof(render_status), "%d/%d",
E->cursor_y + 1, E->numrows); E->cursor_y + 1, E->numrows);
if (len > E->screencols) { if (len > E->screencols) {
+51 -5
View File
@@ -1,6 +1,7 @@
#include "../include/row_op.h" #include "../include/row_op.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h>
int editorRowCxToRx(erow *row, int cursor_x) { int editorRowCxToRx(erow *row, int cursor_x) {
int render_x = 0; int render_x = 0;
@@ -52,11 +53,14 @@ void editorUpdateRow(erow *row) {
row->rsize = i_render; row->rsize = i_render;
} }
void editorAppendRow(struct editorConfig *E, char *s, size_t len) { void editorInsertRow(struct editorConfig *E, int at, char *s, size_t len) {
int at; if (at < 0 || at > E->numrows) {
E->row = realloc(E->row, sizeof(erow) * (E->numrows + 1)); return;
}
E->row = realloc(E->row, sizeof(erow) * (E->numrows + 1));
memmove(&E->row[at + 1], &E->row[at], sizeof(erow) * (E->numrows - at));
at = E->numrows;
E->row[at].size = len; E->row[at].size = len;
E->row[at].chars = malloc(len + 1); E->row[at].chars = malloc(len + 1);
memcpy(E->row[at].chars, s, len); memcpy(E->row[at].chars, s, len);
@@ -67,13 +71,29 @@ void editorAppendRow(struct editorConfig *E, char *s, size_t len) {
editorUpdateRow(&E->row[at]); editorUpdateRow(&E->row[at]);
++E->numrows; ++E->numrows;
++E->dirty;
}
void editorFreeRow(erow *row) {
free(row->render);
free(row->chars);
}
void editorDelRow(struct editorConfig *E, int at) {
if (at < 0 || at >= E->numrows) {
return;
}
editorFreeRow(&E->row[at]);
memmove(&E->row[at], &E->row[at + 1], sizeof(erow) * (E->numrows - at - 1));
--E->numrows;
++E->dirty;
} }
/** /**
* \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(struct editorConfig *E, erow *row, int at, int c) {
if (at < 0 || at > row->size) { if (at < 0 || at > row->size) {
at = row->size; at = row->size;
} }
@@ -82,4 +102,30 @@ void editorRowInsertChar(erow *row, int at, int c) {
++row->size; ++row->size;
row->chars[at] = c; row->chars[at] = c;
editorUpdateRow(row); editorUpdateRow(row);
++E->dirty;
}
void editorRowAppendString(struct editorConfig *E, 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;
}
/**
* \fn editorRowDelChar(struct editorConfig *E, erow *erow, int at)
* \brief Delete the a char at the chosen position on the given row
* \param at Index of the char to delete
* \param row Row on operation is made */
void editorRowDelchar(struct editorConfig *E, erow *row, int at) {
if (at < 0 || at >= row->size) {
return;
}
memmove(&row->chars[at], &row->chars[at + 1], row->size - at);
--row->size;
editorUpdateRow(row);
++E->dirty;
} }
+5 -5
View File
@@ -56,7 +56,7 @@ int editorReadKey() {
case '1': case '1':
return BEG_LINE; return BEG_LINE;
case '3': case '3':
return DELETE; return DEL_KEY;
case '4': case '4':
return END_LINE; return END_LINE;
case '5': case '5':
@@ -73,13 +73,13 @@ int editorReadKey() {
switch (seq[1]) { switch (seq[1]) {
case 'A': case 'A':
return CURSOR_UP; return ARROW_UP;
case 'B': case 'B':
return CURSOR_DOWN; return ARROW_DOWN;
case 'C': case 'C':
return CURSOR_RIGHT; return ARROW_RIGHT;
case 'D': case 'D':
return CURSOR_LEFT; return ARROW_LEFT;
case 'H': case 'H':
return BEG_LINE; return BEG_LINE;
case 'F': case 'F':