Configurable settings with blisp
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
tmp/*
|
tmp/*
|
||||||
bin/*
|
bin/*
|
||||||
doc/*
|
doc/*
|
||||||
|
build/*
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -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
|
|
||||||
@@ -4,17 +4,17 @@ Beluga is a project of CLI text editor that will fit perfectly with your azerty
|
|||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
You will only need **make** or **gcc** to compile the editor.
|
You will only need **cmake** and **clang** to compile the editor.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Here is the installation line :
|
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.
|
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
|
## Getting start
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -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
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include "../blisp/include/data.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \struct erow
|
* \struct erow
|
||||||
@@ -31,10 +32,12 @@ struct editorConfig {
|
|||||||
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;
|
int dirty;
|
||||||
|
int quit_times;
|
||||||
char *filename;
|
char *filename;
|
||||||
char status_msg[80];
|
char status_msg[80];
|
||||||
time_t status_msg_time;
|
time_t status_msg_time;
|
||||||
struct termios orig_termios; /**< Terminal communication interface */
|
struct termios orig_termios; /**< Terminal communication interface */
|
||||||
|
config_t *config;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,4 +50,12 @@ struct abuf {
|
|||||||
int len; /**< Length of the text */
|
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
|
#endif
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,7 @@ enum editorKey {
|
|||||||
#define ABUF_INIT {NULL, 0}
|
#define ABUF_INIT {NULL, 0}
|
||||||
|
|
||||||
#define BELUGA_VERSION "1.0"
|
#define BELUGA_VERSION "1.0"
|
||||||
#define TAB_LENGTH 4
|
#define TAB_LENGTH 2
|
||||||
#define QUIT_TIMES 1
|
#define QUIT_TIMES 1
|
||||||
|
|
||||||
#endif // DEFINE_H_
|
#endif // DEFINE_H_
|
||||||
|
|||||||
@@ -3,8 +3,13 @@
|
|||||||
|
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
|
#include "builtins.h"
|
||||||
|
#include "../blisp/include/config_tools.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
|
||||||
|
void getConfig();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \fn void initEditor()
|
* \fn void initEditor()
|
||||||
* \brief Job's function is to initialize all the fields of editorConfig.
|
* \brief Job's function is to initialize all the fields of editorConfig.
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
#include "define.h"
|
#include "define.h"
|
||||||
#include "output.h"
|
#include "output.h"
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
|
#include "builtins.h"
|
||||||
|
#include "../blisp/include/config_tools.h"
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
// KEYS keycode
|
// KEYS keycode
|
||||||
@@ -19,6 +21,12 @@
|
|||||||
// 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 *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);
|
char *editorPrompt(struct editorConfig *E, char *prompt);
|
||||||
|
|
||||||
void editorMoveCursor(struct editorConfig *E, int key);
|
void editorMoveCursor(struct editorConfig *E, int key);
|
||||||
|
|||||||
@@ -25,4 +25,6 @@ void editorRowAppendString(struct editorConfig *E, erow *row, char *s,
|
|||||||
|
|
||||||
void editorRowDelchar(struct editorConfig *E, erow *row, int at);
|
void editorRowDelchar(struct editorConfig *E, erow *row, int at);
|
||||||
|
|
||||||
|
void log_string(char * string);
|
||||||
|
|
||||||
#endif // ROW_OP_H_
|
#endif // ROW_OP_H_
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
* interactions. \version 0.1 \date 21 septembre 2024
|
* interactions. \version 0.1 \date 21 septembre 2024
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
#define _DEFAULT_SOURCE
|
#define _DEFAULT_SOURCE
|
||||||
#define _BSD_SOURCE
|
#define _BSD_SOURCE
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
@@ -17,15 +18,12 @@
|
|||||||
#include "include/terminal.h"
|
#include "include/terminal.h"
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
static struct editorConfig E;
|
||||||
struct editorConfig E;
|
|
||||||
|
|
||||||
enableRawMode(&E);
|
enableRawMode(&E);
|
||||||
initEditor(&E);
|
initEditor(&E);
|
||||||
if (argc >= 2) {
|
if (argc >= 2) {
|
||||||
editorOpen(&E, argv[1]);
|
editorOpen(&E, argv[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
editorSetStatusMessage(&E, "HELP: Ctrl-S = save | Ctrl-Q = quit");
|
editorSetStatusMessage(&E, "HELP: Ctrl-S = save | Ctrl-Q = quit");
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|||||||
+131
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,6 +31,11 @@ char *editorRowsToString(struct editorConfig *E, int *buffer_len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void editorOpen(struct editorConfig *E, char *filename) {
|
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;
|
FILE *fp;
|
||||||
|
|
||||||
free(E->filename);
|
free(E->filename);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ void initEditor(struct editorConfig *E) {
|
|||||||
E->numrows = 0;
|
E->numrows = 0;
|
||||||
E->row = NULL;
|
E->row = NULL;
|
||||||
E->dirty = 0;
|
E->dirty = 0;
|
||||||
|
E->quit_times = QUIT_TIMES;
|
||||||
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;
|
||||||
@@ -16,4 +17,11 @@ void initEditor(struct editorConfig *E) {
|
|||||||
die("getWindowSize");
|
die("getWindowSize");
|
||||||
}
|
}
|
||||||
E->screenrows -= 2;
|
E->screenrows -= 2;
|
||||||
|
|
||||||
|
E->config = config_create();
|
||||||
|
config_parse_file(E->config, "../config/init.bl");
|
||||||
|
|
||||||
|
config_print_all(E->config);
|
||||||
|
|
||||||
|
init_function_registry();
|
||||||
}
|
}
|
||||||
|
|||||||
+134
-78
@@ -1,17 +1,76 @@
|
|||||||
#include "../include/input.h"
|
#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 <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <string.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \fn char * editorPrompt(struct editorConfig *E, char *prompt)
|
* \fn char * editorPrompt(struct editorConfig *E, char *prompt)
|
||||||
* \brief Return user input in a prompt when enter is hit. */
|
* \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) {
|
char *editorPrompt(struct editorConfig *E, char *prompt) {
|
||||||
size_t buf_size = 128;
|
size_t buf_size = 128;
|
||||||
char *buf = malloc(buf_size);
|
char *buf = malloc(buf_size);
|
||||||
@@ -51,6 +110,7 @@ char *editorPrompt(struct editorConfig *E, char *prompt) {
|
|||||||
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;
|
||||||
|
char *sequence = key_to_string(key);
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case ARROW_RIGHT:
|
case ARROW_RIGHT:
|
||||||
if (row && E->cursor_x < row->size) {
|
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(¤t_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(¤t_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) {
|
void editorProcessKeypress(struct editorConfig *E) {
|
||||||
static int quit_times = QUIT_TIMES;
|
static int quit_times = QUIT_TIMES;
|
||||||
int c = editorReadKey();
|
int c = editorReadKey();
|
||||||
int times;
|
|
||||||
|
|
||||||
switch (c) {
|
if (E->config) {
|
||||||
|
if (handle_key_sequence(E, c)) {
|
||||||
case '\r':
|
quit_times = QUIT_TIMES;
|
||||||
editorInsertNewLine(E);
|
return; // Key was handled by config system
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
quit_times = QUIT_TIMES;
|
|
||||||
|
editorInsertChar(E, c);
|
||||||
|
// reset quit times
|
||||||
|
E->quit_times = QUIT_TIMES;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "../include/row_op.h"
|
#include "../include/row_op.h"
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
@@ -129,3 +130,10 @@ void editorRowDelchar(struct editorConfig *E, erow *row, int at) {
|
|||||||
editorUpdateRow(row);
|
editorUpdateRow(row);
|
||||||
++E->dirty;
|
++E->dirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void log_string(char * string){
|
||||||
|
FILE * fd = fopen("tmp/log.txt", "a");
|
||||||
|
fprintf(fd, "%s\n", string);
|
||||||
|
fclose(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-1
@@ -1,14 +1,18 @@
|
|||||||
#include "../include/terminal.h"
|
#include "../include/terminal.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
void die(const char *s) {
|
void die(const char *s) {
|
||||||
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);
|
||||||
|
|
||||||
perror(s);
|
perror(s);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void disableRawMode(struct editorConfig *E) {
|
void disableRawMode(struct editorConfig *E) {
|
||||||
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &E->orig_termios) == -1) {
|
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &E->orig_termios) == -1) {
|
||||||
|
free(E);
|
||||||
die("tcsetattr");
|
die("tcsetattr");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,7 +31,7 @@ void enableRawMode(struct editorConfig *E) {
|
|||||||
raw.c_cc[VTIME] = 1;
|
raw.c_cc[VTIME] = 1;
|
||||||
|
|
||||||
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -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 (c == '\x1b') {
|
||||||
if (read(STDIN_FILENO, &seq[0], 1) != 1 ||
|
if (read(STDIN_FILENO, &seq[0], 1) != 1 ||
|
||||||
read(STDIN_FILENO, &seq[1], 1) != 1) {
|
read(STDIN_FILENO, &seq[1], 1) != 1) {
|
||||||
|
|||||||
Reference in New Issue
Block a user