Configurable settings with blisp

This commit is contained in:
Arthur Barraux
2025-06-02 10:31:31 +02:00
parent 19601a0078
commit bff3f84ecd
18 changed files with 446 additions and 119 deletions
+1
View File
@@ -1,3 +1,4 @@
tmp/*
bin/*
doc/*
build/*
+60
View File
@@ -0,0 +1,60 @@
# Specify the minimum version of CMake required
cmake_minimum_required(VERSION 3.10)
# Define the project name and the programming language (C)
project(Beluga)
# Set the C standard (optional)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_COMPILER clang)
# Add the header files directory to the include path
include_directories(include)
include_directories(blisp/include)
# Add the source files for the project
set(SRCS
main.c
src/append_buffer.c
src/file_io.c
src/input.c
src/row_op.c
src/editor_op.c
src/init.c
src/output.c
src/terminal.c
src/builtins.c
blisp/src/config_tools.c
blisp/src/data.c
blisp/src/lexer.c
blisp/src/parser.c)
find_package(Doxygen)
if(DOXYGEN_FOUND)
# set input and output for doxygen
set(DOXYGEN_OUT ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile)
add_custom_target(
doc_doxygen ALL
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Generating documentation with Doxygen"
VERBATIM)
else(DOXYGEN_FOUND)
message("Doxygen not found")
endif(DOXYGEN_FOUND)
# we default to Release build type
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release")
endif()
set(CMAKE_C_FLAGS "-Wall -Wextra")
set(CMAKE_C_FLAGS_DEBUG "-g")
# Create an executable target with the specified source files
add_executable(beluga ${SRCS})
# Optionally, you can set the output directory for the executable
set_target_properties(beluga PROPERTIES RUNTIME_OUTPUT_DIRECTORY bin)
-32
View File
@@ -1,32 +0,0 @@
##
# TEST
#
# @file
# @version 0.1
BELUGA_OUTPUT=bin
BUILD_FLAGS=-Wall -Wextra -pedantic
build: main.c src/*
if [ ! -d $(BELUGA_OUTPUT) ]; then mkdir $(BELUGA_OUTPUT); fi
$(CC) main.c -o $(BELUGA_OUTPUT)/beluga src/* $(BUILD_FLAGS)
DEBUG_FLAGS=-Wall -Wextra -pedantic -Werror -fsanitize=address -g
debug: main.c src/*
if [ ! -d $(BELUGA_OUTPUT) ]; then mkdir $(BELUGA_OUTPUT); fi
$(CC) main.c -o $(BELUGA_OUTPUT)/beluga src/* $(DEBUG_FLAGS)
doc:
if [ ! -d doc/ ]; then mkdir doc; fi
doxygen
clean:
rm -r $(BELUGA_OUTPUT)
rm -rf doc/
rm -rf tmp/
all: build doc
# end
+3 -3
View File
@@ -4,17 +4,17 @@ Beluga is a project of CLI text editor that will fit perfectly with your azerty
## Requirements
You will only need **make** or **gcc** to compile the editor.
You will only need **cmake** and **clang** to compile the editor.
## Installation
Here is the installation line :
```git clone --branch V1.0 --single-branch https://github.com/le-cocotier/beluga.git ~/.beluga && cd ~/.beluga && make build```
```git clone --branch V1.0 --single-branch https://github.com/le-cocotier/beluga.git ~/.beluga && cd ~/.beluga && mkdir build && cd build && cmake ../ && make beluga```
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.
You can either run `make all` or `make doc_doxygen` if you're interested by the doxygen documentation.
## Getting start
+21
View File
@@ -0,0 +1,21 @@
// Configuration file
,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("PAGE-UP" %move-cursor-page-up)
,map-key("PAGE-DOWN" %move-cursor-page-down)
,map-key("DEL_KEY" %delete-next-char)
,map-key("BACKSPACE" %delete-previous-char)
,map-key("ENTER" %editor-insert-new-line)
,map-key("CTRL-s" %editor-save)
,map-key("CTRL-q" %editor-quit)
,map-key("CTRL-a" %move-cursor-beg-line)
,map-key("CTRL-z" %move-cursor-end-line)
,map-key("CTRL-f o" %open-file)
,map-key("CTRL-w s h" %window-split-horizontal)
,map-key("CTRL-w s v" %window-split-vertical)
,define(theme "dark")
,define(auto-save true)
+40
View File
@@ -0,0 +1,40 @@
#ifndef BUILTINS_H_
#define BUILTINS_H_
#include "../blisp/include/config_tools.h"
#include "../include/editor_op.h"
#include "../include/file_io.h"
#include "../include/output.h"
#include "../include/input.h"
#include "data.h"
#include "file_io.h"
// Function pointer type for commands
typedef void (*command_func_t)(struct editorConfig *E);
// Structure to hold function mappings
typedef struct {
const char *name;
command_func_t func;
} function_entry_t;
// Function registry
void init_function_registry(void);
int register_function(const char *name, command_func_t func);
command_func_t find_function(const char *name);
int execute_command(const char *name, struct editorConfig *E);
void moveCursorBeginLine(struct editorConfig *E);
void moveCursorEndLine(struct editorConfig *E);
void editorQuit(struct editorConfig *E);
void editorMoveCursorUp(struct editorConfig *E);
void editorMoveCursorDown(struct editorConfig *E);
void editorMoveCursorLeft(struct editorConfig *E);
void editorMoveCursorRight(struct editorConfig *E);
void deleteNextChar(struct editorConfig *E);
void editorMoveCursorPageUp(struct editorConfig *E);
void editorMoveCursorPageDown(struct editorConfig *E);
#endif
+11
View File
@@ -3,6 +3,7 @@
#include <termios.h>
#include <time.h>
#include "../blisp/include/data.h"
/**
* \struct erow
@@ -31,10 +32,12 @@ struct editorConfig {
int numrows; /**< Number of rows contained */
erow *row; /**< Store all the rows printed */
int dirty;
int quit_times;
char *filename;
char status_msg[80];
time_t status_msg_time;
struct termios orig_termios; /**< Terminal communication interface */
config_t *config;
};
/**
@@ -47,4 +50,12 @@ struct abuf {
int len; /**< Length of the text */
};
// Enhanced key sequence handling for multi-key bindings like "CTRL-f o"
typedef struct {
char sequence[64];
int sequence_len;
int last_key_time; // You might want to add timing if needed
} key_sequence_t;
#endif
+1 -1
View File
@@ -25,7 +25,7 @@ enum editorKey {
#define ABUF_INIT {NULL, 0}
#define BELUGA_VERSION "1.0"
#define TAB_LENGTH 4
#define TAB_LENGTH 2
#define QUIT_TIMES 1
#endif // DEFINE_H_
+5
View File
@@ -3,8 +3,13 @@
#include "data.h"
#include "terminal.h"
#include "builtins.h"
#include "../blisp/include/config_tools.h"
#include <stdio.h>
void getConfig();
/**
* \fn void initEditor()
* \brief Job's function is to initialize all the fields of editorConfig.
+8
View File
@@ -5,6 +5,8 @@
#include "define.h"
#include "output.h"
#include "terminal.h"
#include "builtins.h"
#include "../blisp/include/config_tools.h"
#include <unistd.h>
// KEYS keycode
@@ -19,6 +21,12 @@
// END \x1b[4~ || <esc>[8~ || <esc>[F || <esc>OF
// DELETE \x1b[3~
char *key_to_string(int key);
int execute_key_binding(config_t *config, const char *key_combo, void *context);
int handle_key_sequence(struct editorConfig *E, int key);
char *editorPrompt(struct editorConfig *E, char *prompt);
void editorMoveCursor(struct editorConfig *E, int key);
+2
View File
@@ -25,4 +25,6 @@ void editorRowAppendString(struct editorConfig *E, erow *row, char *s,
void editorRowDelchar(struct editorConfig *E, erow *row, int at);
void log_string(char * string);
#endif // ROW_OP_H_
+2 -4
View File
@@ -5,6 +5,7 @@
* interactions. \version 0.1 \date 21 septembre 2024
*/
#include <unistd.h>
#define _DEFAULT_SOURCE
#define _BSD_SOURCE
#define _GNU_SOURCE
@@ -17,15 +18,12 @@
#include "include/terminal.h"
int main(int argc, char *argv[]) {
struct editorConfig E;
static struct editorConfig E;
enableRawMode(&E);
initEditor(&E);
if (argc >= 2) {
editorOpen(&E, argv[1]);
}
editorSetStatusMessage(&E, "HELP: Ctrl-S = save | Ctrl-Q = quit");
while (1) {
+131
View File
@@ -0,0 +1,131 @@
// function_registry.c
#include "../include/builtins.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Static array to hold function mappings
function_entry_t function_registry[256];
int registry_count = 0;
// Initialize the function registry
void init_function_registry(void) {
register_function("editor-save", editorSave);
register_function("move-cursor-beg-line", moveCursorBeginLine);
register_function("move-cursor-end-line", moveCursorEndLine);
register_function("editor-quit", editorQuit);
register_function("move-cursor-up", editorMoveCursorUp);
register_function("move-cursor-down", editorMoveCursorDown);
register_function("move-cursor-right", editorMoveCursorRight);
register_function("move-cursor-left", editorMoveCursorLeft);
register_function("move-cursor-page-up", editorMoveCursorPageUp);
register_function("move-cursor-page-down", editorMoveCursorPageDown);
register_function("delete-previous-char", editorDelChar);
register_function("delete-next-char", deleteNextChar);
register_function("editor-insert-new-line", editorInsertNewLine);
}
// Register a function with a name
int register_function(const char *name, command_func_t func) {
if (registry_count >= 256) {
return -1; // Registry full
}
function_registry[registry_count].name = strdup(name);
function_registry[registry_count].func = func;
registry_count++;
return 0;
}
// Find a function by name
command_func_t find_function(const char *name) {
log_string("registry :");
char *tmp = malloc(3 * sizeof(char));
sprintf(tmp, "%d\n", registry_count);
log_string(tmp);
for (int i = 0; i < registry_count; i++) {
if (strcmp(function_registry[i].name, name) == 0) {
return function_registry[i].func;
}
}
return NULL;
}
// Execute a command by name
int execute_command(const char *name, struct editorConfig *E) {
log_string(name);
command_func_t func = find_function(name);
if (func) {
func(E);
return 0;
}
log_string("Unknown command: \n");
return -1;
}
// Builtins tools
void moveCursorBeginLine(struct editorConfig *E) { E->cursor_x = 0; }
void moveCursorEndLine(struct editorConfig *E) {
if (E->cursor_y < E->numrows) {
E->cursor_x = E->row[E->cursor_y].size;
}
}
void editorQuit(struct editorConfig *E) {
log_string("time to quit\n");
if (E->dirty && E->quit_times > 0) {
editorSetStatusMessage(E,
"WARNING! Changes hasn't been saved. Press Ctrl-Q "
"another time to quit.");
--E->quit_times;
return;
}
write(STDOUT_FILENO, "\x1b[2J", 4);
write(STDOUT_FILENO, CURSOR_TOP_LEFT, 3);
disableRawMode(E);
exit(0);
}
void editorMoveCursorUp(struct editorConfig *E) {
editorMoveCursor(E, ARROW_UP);
}
void editorMoveCursorDown(struct editorConfig *E) {
editorMoveCursor(E, ARROW_DOWN);
}
void editorMoveCursorRight(struct editorConfig *E) {
editorMoveCursor(E, ARROW_RIGHT);
}
void editorMoveCursorLeft(struct editorConfig *E) {
editorMoveCursor(E, ARROW_LEFT);
}
void deleteNextChar(struct editorConfig *E) {
editorMoveCursorRight(E);
editorDelChar(E);
}
void editorMoveCursorPageUp(struct editorConfig *E) {
E->cursor_y = E->row_offset;
int times = E->screenrows;
while (--times) {
editorMoveCursor(E, ARROW_UP);
}
}
void editorMoveCursorPageDown(struct editorConfig *E) {
E->cursor_y = E->row_offset + E->screenrows - 1;
if (E->cursor_y > E->numrows) {
E->cursor_y = E->numrows;
}
int times = E->screenrows;
while (--times) {
editorMoveCursor(E, ARROW_DOWN);
}
}
+5
View File
@@ -31,6 +31,11 @@ char *editorRowsToString(struct editorConfig *E, int *buffer_len) {
}
void editorOpen(struct editorConfig *E, char *filename) {
/**
\function void editorOpen(struct editorConfig *E, char *filename)
\brief Open filename on editor stream. Throw fopen error if file doesn't
exist.
*/
FILE *fp;
free(E->filename);
+8
View File
@@ -9,6 +9,7 @@ void initEditor(struct editorConfig *E) {
E->numrows = 0;
E->row = NULL;
E->dirty = 0;
E->quit_times = QUIT_TIMES;
E->filename = NULL;
E->status_msg[0] = '\0';
E->status_msg_time = 0;
@@ -16,4 +17,11 @@ void initEditor(struct editorConfig *E) {
die("getWindowSize");
}
E->screenrows -= 2;
E->config = config_create();
config_parse_file(E->config, "../config/init.bl");
config_print_all(E->config);
init_function_registry();
}
+135 -79
View File
@@ -1,17 +1,76 @@
#include "../include/input.h"
#include "../include/editor_op.h"
#include "../include/file_io.h"
#include "../include/output.h"
#include <ctype.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
/**
* \fn char * editorPrompt(struct editorConfig *E, char *prompt)
* \brief Return user input in a prompt when enter is hit. */
char *key_to_string(int key) {
static char key_str[32];
char * tmp = malloc(10 * sizeof(char));
sprintf(tmp, "%d\n", key);
log_string(tmp);
// 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");
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;
}
char *editorPrompt(struct editorConfig *E, char *prompt) {
size_t buf_size = 128;
char *buf = malloc(buf_size);
@@ -51,6 +110,7 @@ char *editorPrompt(struct editorConfig *E, char *prompt) {
void editorMoveCursor(struct editorConfig *E, int key) {
erow *row = (E->cursor_y >= E->numrows) ? NULL : &E->row[E->cursor_y];
int row_len;
char *sequence = key_to_string(key);
switch (key) {
case ARROW_RIGHT:
if (row && E->cursor_x < row->size) {
@@ -87,82 +147,78 @@ void editorMoveCursor(struct editorConfig *E, int key) {
}
}
key_sequence_t current_sequence = {0};
int handle_key_sequence(struct editorConfig *E, int key) {
char *key_str = key_to_string(key);
log_string(key_str);
// Add current key to sequence
if (current_sequence.sequence_len > 0) {
strcat(current_sequence.sequence, " ");
}
strcat(current_sequence.sequence, key_str);
current_sequence.sequence_len++;
// Check if this sequence matches any binding
const char *command =
config_get_key_mapping(E->config, current_sequence.sequence);
if (command) {
log_string("Command found\n");
// Found a complete binding - execute it
execute_key_binding(E->config, current_sequence.sequence, E);
// Reset sequence
memset(&current_sequence, 0, sizeof(current_sequence));
return 1; // Handled
}
// Check if this could be the start of a longer sequence
// (This is a simple check - you might want to make it more sophisticated)
int potential_match = 0;
// You'd implement a function to check for partial matches here
if (!potential_match) {
// No potential matches, reset sequence and handle as single key
memset(&current_sequence, 0, sizeof(current_sequence));
return 0; // Not handled
}
return 1; // Waiting for more keys in sequence
}
int execute_key_binding(config_t *config, const char *key_combo,
void *context) {
const char *command = config_get_key_mapping(config, key_combo);
if (!command) {
log_string("No mapping found for key combination: ");
log_string(key_combo);
log_string("\n");
return -1;
}
// Remove the '%' prefix if present
const char *func_name = command;
if (command[0] == '%') {
func_name = command + 1;
}
return execute_command(func_name, context);
}
void editorProcessKeypress(struct editorConfig *E) {
static int quit_times = QUIT_TIMES;
int c = editorReadKey();
int times;
switch (c) {
case '\r':
editorInsertNewLine(E);
break;
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, CURSOR_TOP_LEFT, 3);
disableRawMode(E);
exit(0);
break;
case CTRL_KEY('s'):
editorSave(E);
break;
case BEG_LINE:
E->cursor_x = 0;
break;
case END_LINE:
if (E->cursor_y < E->numrows) {
E->cursor_x = E->row[E->cursor_y].size;
}
break;
case BACKSPACE:
case CTRL_KEY('h'):
case DEL_KEY:
if (c == DEL_KEY) {
editorMoveCursor(E, ARROW_RIGHT);
}
editorDelChar(E);
break;
case PAGE_UP:
case PAGE_DOWN: {
if (c == PAGE_UP) {
E->cursor_y = E->row_offset;
} else if (c == PAGE_DOWN) {
E->cursor_y = E->row_offset + E->screenrows - 1;
if (E->cursor_y > E->numrows) {
E->cursor_y = E->numrows;
}
}
times = E->screenrows;
while (--times) {
editorMoveCursor(E, c == PAGE_UP ? ARROW_UP : ARROW_DOWN);
}
} break;
case ARROW_UP:
case ARROW_DOWN:
case ARROW_LEFT:
case ARROW_RIGHT:
editorMoveCursor(E, c);
break;
case CTRL_KEY('l'):
case '\x1b':
break;
default:
editorInsertChar(E, c);
break;
}
if (E->config) {
if (handle_key_sequence(E, c)) {
quit_times = QUIT_TIMES;
return; // Key was handled by config system
}
}
editorInsertChar(E, c);
// reset quit times
E->quit_times = QUIT_TIMES;
}
+8
View File
@@ -1,4 +1,5 @@
#include "../include/row_op.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
@@ -129,3 +130,10 @@ void editorRowDelchar(struct editorConfig *E, erow *row, int at) {
editorUpdateRow(row);
++E->dirty;
}
void log_string(char * string){
FILE * fd = fopen("tmp/log.txt", "a");
fprintf(fd, "%s\n", string);
fclose(fd);
}
+6 -1
View File
@@ -1,14 +1,18 @@
#include "../include/terminal.h"
#include <stdio.h>
#include <stdlib.h>
void die(const char *s) {
write(STDOUT_FILENO, "\x1b[2J", 4);
write(STDOUT_FILENO, CURSOR_TOP_LEFT, 3);
perror(s);
exit(1);
}
void disableRawMode(struct editorConfig *E) {
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &E->orig_termios) == -1) {
free(E);
die("tcsetattr");
}
}
@@ -27,7 +31,7 @@ void enableRawMode(struct editorConfig *E) {
raw.c_cc[VTIME] = 1;
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) {
die("tcgetattr");
die("tcsetattr");
}
}
@@ -41,6 +45,7 @@ int editorReadKey() {
}
}
fprintf(stderr, "%X (%c)\n", c, c);
if (c == '\x1b') {
if (read(STDIN_FILENO, &seq[0], 1) != 1 ||
read(STDIN_FILENO, &seq[1], 1) != 1) {