54 Commits

Author SHA1 Message Date
Giorgio ar work cce1ffa903 mistral gagnant 2026-02-04 16:16:36 +01:00
Arthur Barraux 87746f09ce Merge remote-tracking branch 'gitea/main'
Build project / build (push) Successful in 56s
2026-01-24 19:47:59 +01:00
Arthur Barraux 5a7b073041 Adding splitting screen and buffer switching 2026-01-24 19:44:34 +01:00
Arthur Barraux 557fc8894a Adding splitting screen and buffer switching
Build project / build (push) Successful in 52s
2026-01-24 19:29:46 +01:00
Arthur Barraux b039cf3ded Forget delete file
Build project / build (push) Successful in 24s
2026-01-22 14:07:13 +01:00
Arthur Barraux af1835d75b config correct
Build project / build (push) Failing after 1m0s
2026-01-22 14:03:59 +01:00
Arthur Barraux f3bd5dd1c9 Color theming 2026-01-22 14:00:47 +01:00
Arthur Barraux fa7f8d39d8 update splash screen
Build project / build (push) Successful in 23s
2026-01-14 20:38:33 +01:00
Arthur Barraux 23036cf3d3 warranty 0 memoey leak
Build project / build (push) Successful in 58s
2026-01-14 20:24:37 +01:00
Arthur Barraux d0afd4f304 patch file_completion crash
Build project / build (push) Successful in 1m0s
2026-01-13 15:20:18 +01:00
Arthur Barraux de4fa78221 Merge remote-tracking branch 'gitea/main'
Build project / build (push) Successful in 48s
2026-01-11 19:41:57 +01:00
Arthur Barraux fc93832130 Syntax highlighting and comment 2026-01-11 19:41:30 +01:00
arthur 39e07902f4 Update README.md
Build project / build (push) Successful in 24s
2026-01-09 15:03:08 +01:00
Arthur Barraux 815114923d Add prefix feature
Build project / build (push) Successful in 56s
2026-01-09 14:54:07 +01:00
arthur 410f382592 Add go to end of word function
Build project / build (push) Successful in 1m22s
2025-12-24 14:50:14 +01:00
arthur 80c0bf73e0 Update README.md
Build project / build (push) Successful in 1m1s
2025-12-08 00:25:05 +01:00
arthur d4ee0d86c2 Update .gitea/workflows/build.yml
Build project / build (push) Failing after 2m18s
2025-12-04 01:34:33 +01:00
arthur c06c820dfb Patch path complete and read char lisp function
Build project / build (push) Has been cancelled
2025-11-07 16:23:56 +01:00
arthur 5588b0a8d7 add path autocomplete
Build project / build (push) Has been cancelled
2025-11-05 15:49:01 +01:00
arthur 419e924650 Add search function
Build project / build (push) Has been cancelled
2025-11-03 16:45:23 +01:00
arthur 6a201b3ebc delete line macro
Build project / build (push) Has been cancelled
2025-11-03 16:05:25 +01:00
arthur 1d253e51ef Update README.md
Build project / build (push) Failing after 3m27s
2025-11-01 13:22:10 +01:00
Arthur Barraux 42f82e2e0d add packages
Build project / build (push) Has been cancelled
2025-11-01 13:03:07 +01:00
arthur fa32f4b177 Update src/init.c
Build project / build (push) Has been cancelled
2025-10-31 09:10:57 +01:00
arthur 65f997e964 Update install.sh
Build project / build (push) Has been cancelled
2025-10-31 09:09:20 +01:00
Arthur Barraux 7eaf6913cb Merge remote-tracking branch 'gitea/main'
Build project / build (push) Has been cancelled
2025-10-30 18:18:12 +01:00
Arthur Barraux 8f7dcf3534 Adding installer script 2025-10-30 18:17:19 +01:00
arthur d8c6b9ace3 Update README.md
Build project / build (push) Has been cancelled
2025-10-28 09:16:36 +01:00
arthur d0173d7308 Update README.md
Build project / build (push) Has been cancelled
2025-10-28 09:02:29 +01:00
arthur 40fc234eeb Update .gitea/workflows/build.yml
Build project / build (push) Failing after 39s
2025-10-18 15:35:57 +02:00
Arthur Barraux 756deba83e remove .cache repo for cleaning
Build project / build (push) Failing after 33s
2025-10-18 15:12:06 +02:00
Arthur Barraux 85e8067e41 clean up the repo
Build project / build (push) Failing after 1m22s
2025-10-18 15:06:23 +02:00
Arthur Barraux 09d78f48e9 Merge remote-tracking branch 'gitea/lisp'
Build project / build (push) Successful in 1m3s
2025-10-06 11:37:39 +02:00
Arthur Barraux ce94e9fb87 permission files
Build and Deploy Docs / build (push) Failing after 43s
2025-10-06 11:28:49 +02:00
Arthur Barraux 29a92ce904 Merge Lisp branch
Build and Deploy Docs / build (push) Failing after 1m24s
2025-10-05 22:05:21 +02:00
Arthur Barraux 9348ae668a Add open file key-bind
Build and Deploy Docs / build (push) Failing after 40s
2025-10-05 21:44:58 +02:00
Arthur Barraux 9157b94398 Adding usefull keybinds
Build and Deploy Docs / build (push) Successful in 50s
2025-10-02 14:59:28 +02:00
Arthur Barraux 3b6c60a49e functional lisp config files
Build and Deploy Docs / build (push) Successful in 47s
2025-10-02 11:26:18 +02:00
arthur 53d6572c8c Revert doxygen deployement
Build and Deploy Docs / build (push) Successful in 37s
2025-09-25 12:10:44 +02:00
arthur d083948dfe Update .gitea/workflows/build.yml
Build and Deploy Docs / build (push) Failing after 30s
2025-09-25 11:35:41 +02:00
arthur 09ef5c0f3b Update .gitea/workflows/build.yml
Build and Deploy Docs / build (push) Failing after 1m26s
2025-09-25 11:25:52 +02:00
arthur e4691669b8 Update .gitea/workflows/build.yml
Build and Deploy Docs / build (push) Failing after 1m54s
2025-09-25 11:22:11 +02:00
arthur 27ae0a684f Update .gitea/workflows/build.yml
Build and Deploy Docs / build (push) Failing after 1m33s
2025-09-25 11:06:53 +02:00
Arthur Barraux 3505084527 second try
Meson Build and Deploy / build (push) Failing after 1m23s
2025-09-25 11:00:53 +02:00
Arthur Barraux 02d7f27ec3 Merge remote-tracking branch 'gitea/lisp' into lisp
Meson Build and Deploy / build (push) Failing after 53s
2025-09-25 10:48:09 +02:00
Arthur Barraux 7dded62db9 add doxygen deployement 2025-09-25 10:47:07 +02:00
arthur 6cd79b5c76 downgrade version of runner upload
Meson Build and Deploy / build (push) Successful in 25s
2025-09-24 11:14:22 +02:00
Arthur Barraux 8844d2f064 meson bulid changes
Meson Build and Deploy / build (push) Failing after 29s
2025-09-24 11:04:16 +02:00
Arthur Barraux d8fc7d2d67 adding lisp-interpreter
Meson Build and Deploy / build (push) Failing after 29s
2025-09-24 10:58:09 +02:00
Arthur Barraux ab482df604 get submodules
Meson Build and Deploy / build (push) Failing after 1m31s
2025-09-24 10:48:45 +02:00
Arthur Barraux be31e83fb9 update build.yml
Meson Build and Deploy / build (push) Failing after 1m21s
2025-09-24 10:45:11 +02:00
Arthur Barraux 54db6321ad add CI/CD config
Meson Build and Deploy / build (push) Failing after 1m54s
2025-09-24 10:29:31 +02:00
Arthur Barraux 8ce621dfde Made Editor Config global 2025-09-19 14:31:12 +02:00
Arthur Barraux 91e247d1de adding meson build 2025-09-19 11:02:44 +02:00
45 changed files with 12928 additions and 4309 deletions
+9 -5
View File
@@ -1,25 +1,29 @@
name: CMake Build and Deploy name: Build project
on: on:
push: push:
branches: ["main"] branches: ["main"]
pull_request:
jobs: jobs:
build: build:
runs-on: home-1 runs-on: giorgio-runner
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
tokens: ${{ secrets.GITEA_TOKEN }}
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y cmake build-essential sudo apt-get install -y meson ninja-build gcc
- name: Configure Meson
run: meson setup build
- name: Build project - name: Build project
run: cmake --build build -- -j$(nproc) run: meson compile -C build
- name: Run tests - name: Run tests
run: ctest --test-dir build || true run: ctest --test-dir build || true
+2 -3
View File
@@ -1,4 +1,3 @@
tmp/*
bin/*
doc/*
build/* build/*
doc/*
beluga.wiki/*
-3
View File
@@ -1,3 +0,0 @@
[submodule "blisp"]
path = blisp
url = https://homelinuxserver.ddns.net/git/arthur/blisp.git
-60
View File
@@ -1,60 +0,0 @@
# 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)
+2876 -2876
View File
File diff suppressed because it is too large Load Diff
+32 -26
View File
@@ -1,26 +1,32 @@
# 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 uses lisp as configuration language.
## Requirements ## Requirements
You will only need **cmake** and **clang** to compile the editor. You will only need **meson** and a **C compiler** to compile the editor.
## Installation ## Installation
### From source
Here is the installation line for development version: Here is the installation line for development version:
```git clone --recurse-submodules https://github.com/le-cocotier/beluga.git ~/.beluga && cd ~/.beluga && mkdir build && cd build && cmake ../ && make beluga``` ```git clone https://git.giorgio-nas.fr/arthur/beluga.git && cd beluga && meson setup build && meson compile -C build```
The executable file will be in `bin/beluga`. Feel free to add it to your path. The executable file will be `build/beluga`. Feel free to add it to your path.
You can either run `make all` or `make doc_doxygen` if you're interested by the doxygen documentation. ### From installation script ( prefered )
## Getting start Just clone the repo and execute the script `install.sh`. It will automatically add beluga to your path.
To open an existing file just type : ## Getting start
```beluga path_to_my_file```
To open an existing file just type :
The only keybinds that you will need will be : ```./build/beluga path_to_my_file```
- Ctrl-Q : leave the editor
- Ctrl-S : Save a file Here is some few command that you will need :
| keybind | command |
|---------------|------------------|
| Ctrl-x Ctrl-c | leave the editor |
| Ctrl-x Ctrl-s | Save a file |
| Ctrl-x f | open file |
+43
View File
@@ -0,0 +1,43 @@
**#%#*****###%**
*##+--------------------=##*
#*=----------------------------=*#*
#*------------------------------------*#
%+----------------------------------------=#*
#+---------------------------------------------##
*#-------------------------------------------------=##
*#----------------------------------------------------:-##
#----------------------------------------------------------##
#=--------------------------------------------------------------##
+--------------------------+@#-%*-----------------------------------#*
+--------------------------%@@@@#-------------------------------------** BELUGA - VERSION 2.3
*-=-------------------------#@@*---------------------------------------=%
%*#==--------------------------------------------------------------------+# ----- KEY-BINDS -----
*%%=-=--------------------------------------------------------------------=# CTRL-x CTRL-c leave
%=--------------------------------------------------------------------------#* CTRL-x CTRL-s save
%-----------------------------------------------------------------------------** CTRL-x f open-file
*+--=---===----=---------------=*-----------------------------------------------**
#--=## *#%#*+==----==+**+----------------------= ***=---------------------%
*%**=-----------==== ==---------------------------------=+#+-----------------=#
*%=----------------------------------------------------------=#*---------------#
##=----------------------------------=------------------------+%=------------+#
#%+---------------------------------=*------------------------+%------------#
*#%*=-------------=-----------------#-------------------------#+----------#
**#%#*******+=======-------------#=------------------------#----------#
#===#*=======------------------#*----=-----------=--=##*-----------#
-====##=------------------------*%+------------=*#+=====----------#
--=====+#*=----------------------=-=+*#####***+=======-----------=*
%------=====*%*=-------------------------========-----------------+*
*-=--------====%%###+=--------------------------=-----------------#
#-----------=% +*##%%%%%%@@%%%%####*==---------------------**
%=-------#* #%*=-----------------+#
*%+--=## ##=-----------------=#*
** #+----=-------------------#*
%+----------------------------#*
*%-------------==----------------+#
##--------------==------------------#
*#--------------===%-----------------=%
##---------------=-##*-----------------+#
*#---------------==#+=#%-----------------%
*%---------------+# %*---------------#*
*#------------=+#* #%*=-----------#*
#****##****** *#%%##+=----%
Submodule blisp deleted from 04464ec2e7
-21
View File
@@ -1,21 +0,0 @@
// 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)
+91
View File
@@ -0,0 +1,91 @@
;; MACROS
(define TAB-LENGTH 4)
(define QUIT-TIMES 1)
(define THEME "dark")
;; PACKAGES
;; First git clone it
;; (add-package "smart_delimiters")
;; FUNCTIONS
(define editor-delete-next-char (lambda () (
(move-cursor "right")
(editor-delete-previous-char)
)
)
)
(define (char-between ch lo hi)
(if (char>=? ch lo)
(char<=? ch hi)
#f))
(define (alphanumericp ch)
(if ch
(if (char-between ch #\a #\z)
#t
(if (char-between ch #\A #\Z)
#t
(if (char-between ch #\0 #\9)
#t
#f)))
#f))
(define (word-char-p ch)
(if (alphanumericp ch)
#t
#f))
(define editor-move-to-end-of-word (lambda () (
(if (word-char-p (editor-read-char))
((move-cursor "right")
(editor-move-to-end-of-word))
))
))
(define enter-and-tab
(lambda ()
(editor-insert-new-line)
(let ((is-in (move-cursor "up")))
(do ((ch (editor-read-char) (editor-read-char)))
((and (not (char=? ch #\space)) is-in) #f)
(move-cursor "down")
(editor-insert-char " ")
(set! is-in (move-cursor "up")))
(move-cursor "down"))))
(add-prefix "user")
(map-key "CTRL-x" '(editor-set-prefix "user") "no-prefix")
(map-key "CTRL-g" '(editor-set-prefix "no-prefix") "user")
(map-key "CTRL-c" editor-quit "user")
(map-key "CTRL-s" editor-save "user")
(map-key "ARROW-UP" '(move-cursor "up") "no-prefix")
(map-key "ARROW-DOWN" '(move-cursor "down") "no-prefix")
(map-key "ARROW-RIGHT" '(move-cursor "right") "no-prefix")
(map-key "ARROW-LEFT" '(move-cursor "left") "no-prefix")
(map-key "ENTER" enter-and-tab "no-prefix")
(map-key "CTRL-a" move-cursor-beg-line "no-prefix")
(map-key "CTRL-e" move-cursor-end-line "no-prefix")
(map-key "BACKSPACE" editor-delete-previous-char "no-prefix")
(map-key "DEL" editor-delete-next-char "no-prefix")
(map-key "PAGE-UP" move-cursor-page-up "no-prefix")
(map-key "PAGE-DOWN" move-cursor-page-down "no-prefix")
(map-key "f" editor-open-file "user")
(map-key "TAB" editor-insert-tab "no-prefix")
(map-key "CTRL-k" editor-del-row "no-prefix")
(map-key "CTRL-s" buffer-find "no-prefix")
(map-key "CTRL-r" editor-move-to-end-of-word "no-prefix")
(map-key "ARROW-RIGHT" editor-switch-next-buffer "user")
(map-key "\"" editor-split-screen-vertical "user")
(map-key "o" editor-switch-next-pane "user")
(map-key "*" editor-unify-panes "user")
(map-key "t" editor-create-terminal "user")
+12 -12
View File
@@ -1,12 +1,12 @@
#ifndef APPEND_BUFFER_H_ #ifndef APPEND_BUFFER_H_
#define APPEND_BUFFER_H_ #define APPEND_BUFFER_H_
#include "data.h" #include "data.h"
#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 char *s, int len);
void abFree(struct abuf *ab); void abFree(struct abuf *ab);
#endif // APPEND_BUFFER_H_ #endif // APPEND_BUFFER_H_
+72
View File
@@ -0,0 +1,72 @@
/**
* @file buffer.h
* @brief Buffer management for handling multiple open files
*/
#ifndef BUFFER_H_
#define BUFFER_H_
#include "data.h"
/**
* @brief Creates a new buffer for a file
* @param filename Path to the file
* @return Buffer ID on success, -1 on failure
*/
int bufferCreate(const char *filename);
/**
* @brief Switches to a specific buffer by ID
* @param buffer_id The buffer ID to switch to
* @return 0 on success, -1 on failure
*/
int bufferSwitch(int buffer_id);
struct buffer_t *bufferFindById(int buffer_id);
/**
* @brief Closes a buffer by ID
* @param buffer_id The buffer ID to close
* @return 0 on success, -1 on failure
*/
int bufferClose(int buffer_id);
/**
* @brief Gets the current active buffer
* @return Pointer to current buffer_t, NULL if none
*/
struct buffer_t *bufferGetCurrent(void);
/**
* @brief Lists all open buffers
* @return Number of open buffers
*/
int bufferListAll(void);
/**
* @brief Saves current buffer to disk
* @return 0 on success, -1 on failure
*/
int bufferSave(void);
/**
* @brief Saves all buffers to disk
* @return 0 on success, -1 on failure
*/
int bufferSaveAll(void);
/**
* @brief Creates a new terminal buffer
* @return Buffer ID on success, -1 on failure
*/
int bufferCreateTerminal(void);
/**
* @brief Executes a command in the terminal buffer
* @param buffer_id The terminal buffer ID
* @param command The command to execute
* @return 0 on success, -1 on failure
*/
int bufferExecuteTerminalCommand(int buffer_id, const char *command);
#endif
+60 -40
View File
@@ -1,40 +1,60 @@
#ifndef BUILTINS_H_ #ifndef BUILTINS_H_
#define BUILTINS_H_ #define BUILTINS_H_
#include "../blisp/include/config_tools.h" #include "lisp.h"
#include "../include/editor_op.h"
#include "../include/file_io.h" // Mouvement
#include "../include/output.h"
#include "../include/input.h" Lisp moveCursor(Lisp args, LispError *e, LispContext ctx);
#include "data.h" Lisp moveCursorBeginLine(Lisp args, LispError *e, LispContext ctx);
#include "file_io.h" Lisp moveCursorEndLine(Lisp args, LispError *e, LispContext ctx);
Lisp editorMoveCursorPageUp(Lisp args, LispError* e, LispContext ctx);
// Function pointer type for commands Lisp editorMoveCursorPageDown(Lisp args, LispError *e, LispContext ctx);
typedef void (*command_func_t)(struct editorConfig *E);
// Text editing
// Structure to hold function mappings
typedef struct { Lisp l_editorInsertNewLine(Lisp args, LispError* e, LispContext ctx);
const char *name; Lisp l_editorInserTab(Lisp args, LispError *e, LispContext ctx);
command_func_t func; Lisp deletePreviousChar(Lisp args, LispError *e, LispContext ctx);
} function_entry_t; Lisp editorPrintC(Lisp args, LispError *e, LispContext ctx);
Lisp editorDelRow_L(Lisp args, LispError *e, LispContext ctx);
// Function registry Lisp editorReadChar_L(Lisp args, LispError *e, LispContext ctx);
void init_function_registry(void); // Editor
int register_function(const char *name, command_func_t func);
command_func_t find_function(const char *name); Lisp editorQuit(Lisp args, LispError *e, LispContext ctx);
int execute_command(const char *name, struct editorConfig *E); Lisp l_editorSave(Lisp args, LispError *e, LispContext ctx);
Lisp editorSetPrefix(Lisp args, LispError *e, LispContext ctx);
void moveCursorBeginLine(struct editorConfig *E); Lisp editorPrefix(Lisp args, LispError *e, LispContext ctx);
void moveCursorEndLine(struct editorConfig *E); Lisp mapKey(Lisp args, LispError *e, LispContext ctx);
void editorQuit(struct editorConfig *E); void registerBuiltin(char * key_sequence, LispCFunc f);
Lisp editorOpenFile(Lisp args, LispError *e, LispContext ctx);
void editorMoveCursorUp(struct editorConfig *E); Lisp addPackage(Lisp args, LispError *e, LispContext ctx);
void editorMoveCursorDown(struct editorConfig *E);
void editorMoveCursorLeft(struct editorConfig *E);
void editorMoveCursorRight(struct editorConfig *E); // Buffer
void deleteNextChar(struct editorConfig *E);
void editorMoveCursorPageUp(struct editorConfig *E); Lisp editorSwitchNextBuffer(Lisp args, LispError *e, LispContext ctx);
void editorMoveCursorPageDown(struct editorConfig *E); Lisp bufferFind_L(Lisp args, LispError *e, LispContext ctx);
#endif
// Pane
Lisp l_editorSplitScreenVertical(Lisp args, LispError *e, LispContext ctx);
Lisp editorSwitchNextPane(Lisp args, LispError *e, LispContext ctx);
Lisp editorUnifiedPanes(Lisp args, LispError *e, LispContext ctx);
// Other
void free_structs(void);
// Terminal
Lisp editorCreateTerminal(Lisp args, LispError *e, LispContext ctx);
#endif
+123
View File
@@ -0,0 +1,123 @@
#ifndef COLOR_H_
#define COLOR_H_
// ============================================================================
// TEXT STYLES / ATTRIBUTES
// ============================================================================
#define ANSI_RESET "\x1b[0m" // Reset all attributes
#define ANSI_BOLD "\x1b[1m" // Bold text
#define ANSI_DIM "\x1b[2m" // Dim/faint text
#define ANSI_ITALIC "\x1b[3m" // Italic text
#define ANSI_UNDERLINE "\x1b[4m" // Underline text
#define ANSI_BLINK "\x1b[5m" // Blinking text
#define ANSI_REVERSE "\x1b[7m" // Reverse video (invert colors)
#define ANSI_HIDDEN "\x1b[8m" // Hidden/invisible text
#define ANSI_STRIKETHROUGH "\x1b[9m" // Strikethrough text
// ============================================================================
// FOREGROUND COLORS (30-37 standard, 90-97 bright)
// ============================================================================
#define ANSI_BLACK "\x1b[30m"
#define ANSI_RED "\x1b[31m"
#define ANSI_GREEN "\x1b[32m"
#define ANSI_YELLOW "\x1b[33m"
#define ANSI_BLUE "\x1b[34m"
#define ANSI_MAGENTA "\x1b[35m"
#define ANSI_CYAN "\x1b[36m"
#define ANSI_WHITE "\x1b[37m"
// Bright/Light foreground colors (90-97)
#define ANSI_BRIGHT_BLACK "\x1b[90m"
#define ANSI_BRIGHT_RED "\x1b[91m"
#define ANSI_BRIGHT_GREEN "\x1b[92m"
#define ANSI_BRIGHT_YELLOW "\x1b[93m"
#define ANSI_BRIGHT_BLUE "\x1b[94m"
#define ANSI_BRIGHT_MAGENTA "\x1b[95m"
#define ANSI_BRIGHT_CYAN "\x1b[96m"
#define ANSI_BRIGHT_WHITE "\x1b[97m"
// ============================================================================
// BACKGROUND COLORS (40-47 standard, 100-107 bright)
// ============================================================================
#define ANSI_BG_BLACK "\x1b[40m"
#define ANSI_BG_RED "\x1b[41m"
#define ANSI_BG_GREEN "\x1b[42m"
#define ANSI_BG_YELLOW "\x1b[43m"
#define ANSI_BG_BLUE "\x1b[44m"
#define ANSI_BG_MAGENTA "\x1b[45m"
#define ANSI_BG_CYAN "\x1b[46m"
#define ANSI_BG_WHITE "\x1b[47m"
// Bright/Light background colors (100-107)
#define ANSI_BG_BRIGHT_BLACK "\x1b[100m"
#define ANSI_BG_BRIGHT_RED "\x1b[101m"
#define ANSI_BG_BRIGHT_GREEN "\x1b[102m"
#define ANSI_BG_BRIGHT_YELLOW "\x1b[103m"
#define ANSI_BG_BRIGHT_BLUE "\x1b[104m"
#define ANSI_BG_BRIGHT_MAGENTA "\x1b[105m"
#define ANSI_BG_BRIGHT_CYAN "\x1b[106m"
#define ANSI_BG_BRIGHT_WHITE "\x1b[107m"
// ============================================================================
// 256-COLOR MODE (for more colors)
// ============================================================================
// Foreground: \x1b[38;5;Nm where N is 0-255
// Background: \x1b[48;5;Nm where N is 0-255
// Example macros:
#define ANSI_FG_256(N) "\x1b[38;5;" #N "m"
#define ANSI_BG_256(N) "\x1b[48;5;" #N "m"
// ============================================================================
// TRUE COLOR / 24-BIT COLOR (RGB)
// ============================================================================
// Foreground: \x1b[38;2;R;G;Bm
// Background: \x1b[48;2;R;G;Bm
// Example macros:
#define ANSI_FG_RGB(R,G,B) "\x1b[38;2;" #R ";" #G ";" #B "m"
#define ANSI_BG_RGB(R,G,B) "\x1b[48;2;" #R ";" #G ";" #B "m"
// ============================================================================
// CURSOR MOVEMENT
// ============================================================================
#define ANSI_CURSOR_UP(N) "\x1b[" #N "A" // Move up N lines
#define ANSI_CURSOR_DOWN(N) "\x1b[" #N "B" // Move down N lines
#define ANSI_CURSOR_RIGHT(N) "\x1b[" #N "C" // Move right N columns
#define ANSI_CURSOR_LEFT(N) "\x1b[" #N "D" // Move left N columns
#define ANSI_CURSOR_HOME "\x1b[H" // Move to home (0,0)
#define ANSI_CURSOR_HIDE "\x1b[?25l" // Hide cursor
#define ANSI_CURSOR_SHOW "\x1b[?25h" // Show cursor
// ============================================================================
// SCREEN CONTROL
// ============================================================================
#define ANSI_CLEAR_SCREEN "\x1b[2J" // Clear entire screen
#define ANSI_CLEAR_LINE "\x1b[K" // Clear from cursor to end of line
#define ANSI_CLEAR_UP "\x1b[1J" // Clear from cursor to start
#define ANSI_CLEAR_DOWN "\x1b[0J" // Clear from cursor to end
// ============================================================================
// EXAMPLE USAGE
// ============================================================================
/*
#include <stdio.h>
int main(void) {
// Simple color example
printf("%sHello in Red%s\n", ANSI_RED, ANSI_RESET);
// Bold and colored
printf("%s%sBold Green%s\n", ANSI_BOLD, ANSI_GREEN, ANSI_RESET);
// Background color
printf("%s%sYellow on Blue%s\n", ANSI_YELLOW, ANSI_BG_BLUE, ANSI_RESET);
// Combine multiple styles
printf("%s%s%sUnderlined Bold Cyan%s\n",
ANSI_UNDERLINE, ANSI_BOLD, ANSI_CYAN, ANSI_RESET);
return 0;
}
*/
#endif
+172 -61
View File
@@ -1,61 +1,172 @@
#ifndef DATA_H_ #ifndef DATA_H_
#define DATA_H_ #define DATA_H_
#include <termios.h> #include <stdio.h>
#include <time.h> #include <time.h>
#include "../blisp/include/data.h"
#ifdef _WIN32
/** #include "termiWin.h"
* \struct erow #else
* \brief Store one editor row #include <termios.h>
* \param #endif
* */
#include "lisp.h"
typedef struct erow {
int size; /**< Size of the line */ /**
int rsize; /**< Size of the render line */ * \struct erow
char *chars; /**< Characters of the line */ * \brief Store one editor row
char *render; /**< The actual line we will print */ * \param
} erow; * */
/** typedef struct frow {
* \struct editorConfig int size; /**< Size of the line */
* \brief Containing our editor state. int rsize; /**< Size of the render line */
*/ char *chars; /**< Characters of the line */
struct editorConfig { char *render; /**< The actual line we will print */
int cursor_x, cursor_y; /**< Cursor position */ } frow;
int rx; /**< Position in the render*/
int row_offset; /**< Position scroll of lines */
int col_offset; /**< Position scroll of colomns*/ /**
int screenrows; /**< Terminal height*/ * @brief Split modes for screen layout
int screencols; /**< Terminal width*/ */
int numrows; /**< Number of rows contained */ typedef enum {
erow *row; /**< Store all the rows printed */ SPLIT_NONE = 0, // Single buffer fullscreen
int dirty; SPLIT_VERTICAL, // Left-right split
int quit_times; SPLIT_HORIZONTAL // Top-bottom split
char *filename; } SplitMode;
char status_msg[80];
time_t status_msg_time; /**
struct termios orig_termios; /**< Terminal communication interface */ * @brief Represents an editor viewport/pane
config_t *config; */
}; typedef struct {
int buffer_id; // Which buffer this pane displays
/** int start_row; // Starting row on screen
* \struct abuf int start_col; // Starting column on screen
* \brief Contains text to add before writing to screen. int height; // Height of this pane
* */ int width; // Width of this pane
int cursor_x; // Local cursor x in this pane
struct abuf { int cursor_y; // Local cursor y in this pane
char *b; /**< Text that will be printed */ int rx, ry;
int len; /**< Length of the text */ int row_offset; // Scroll offset for rows
}; int col_offset; // Scroll offset for columns
int is_active; // Is this pane currently active
// Enhanced key sequence handling for multi-key bindings like "CTRL-f o" } EditorPane;
typedef struct {
char sequence[64]; /**
int sequence_len; * @brief Screen layout manager
int last_key_time; // You might want to add timing if needed */
} key_sequence_t; typedef struct {
SplitMode mode;
EditorPane *panes;
#endif int num_panes;
int active_pane; // Index of active pane
} ScreenLayout;
typedef struct theme {
char *BACKGROUND_COLOR;
char *COLOR_KEYWORD;
char *COLOR_TYPE;
char *COLOR_STRING;
char *COLOR_COMMENT;
char *COLOR_NUMBER;
char *COLOR_DEFAULT;
} theme_t;
enum bufferStatus_e {
IDLE,
READ_ONLY,
READ_AND_WRITE,
};
struct const_t {
int TAB_LENGTH;
int QUIT_TIMES;
char *THEME;
};
struct prefix_t {
char prefix_name[64];
int prefix_id;
};
struct keyBind_t {
char *key_sequence;
int prefix_id;
Lisp command;
};
enum buffer_type { FILE_BUFF, TERMINAL_BUFF };
struct buffer_t {
enum buffer_type type;
int buffer_id;
int width, height;
int x, y; /**< Position in the buffer (cursor) */
frow *row;
int numrows;
char *filename;
enum bufferStatus_e state;
int dirty; /**< Has this buffer been modified since last save */
int row_offset; /**< Scroll offset for rows in this buffer */
int col_offset; /**< Scroll offset for columns in this buffer */
// Terminal-specific data
char *terminal_command; /**< Current command being executed */
int terminal_pid; /**< Process ID of running terminal command */
char *terminal_output; /**< Captured terminal output */
int terminal_output_size; /**< Size of terminal output */
};
/**
* \struct editorConfig
* \brief Containing our editor state.
*/
struct editorConfig {
int screenrows; /**< Terminal height*/
int screencols; /**< Terminal width*/
ScreenLayout layout;
int dirty;
int prefix_state;
char status_msg[80];
time_t status_msg_time;
struct termios orig_termios; /**< Terminal communication interface */
struct const_t constantes;
int quit_times_buffer;
char *init_file_path;
FILE *fd_init_file;
Lisp env;
LispContext ctx; /** Lisp context */
Lisp ctx_data; /** Lisp data context */
LispError ctx_error; /** Lisp ctx error */
struct keyBind_t *key_binds;
int number_of_keybinds;
struct prefix_t *prefix;
int number_of_prefix;
struct buffer_t buffers[64];
int number_of_buffer;
theme_t theme;
};
/**
* \struct abuf
* \brief Contains text to add before writing to screen.
* */
struct abuf {
char *b; /**< Text that will be printed */
int len; /**< Length of the text */
};
extern struct editorConfig E;
#endif
+29 -31
View File
@@ -1,31 +1,29 @@
#ifndef DEFINE_H_ #ifndef DEFINE_H_
#define DEFINE_H_ #define DEFINE_H_
#define CTRL_KEY(k) ((k) & 0x1f) #define CTRL_KEY(k) ((k) & 0x1f)
#define ESCAPE '\x1b' #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"
#define ERASE_END_LINE "\x1b[K" #define ERASE_END_LINE "\x1b[K"
enum editorKey { enum editorKey {
BACKSPACE = 127, BACKSPACE = 127,
ARROW_LEFT = 1000, ARROW_LEFT = 1000,
ARROW_RIGHT, ARROW_RIGHT,
ARROW_UP, ARROW_UP,
ARROW_DOWN, ARROW_DOWN,
DEL_KEY, DEL_KEY,
BEG_LINE, BEG_LINE,
END_LINE, END_LINE,
PAGE_UP, PAGE_UP,
PAGE_DOWN, PAGE_DOWN,
}; };
#define ABUF_INIT {NULL, 0} #define ABUF_INIT {NULL, 0}
#define BELUGA_VERSION "1.0" #define BELUGA_VERSION "1.1"
#define TAB_LENGTH 2
#define QUIT_TIMES 1 #endif // DEFINE_H_
#endif // DEFINE_H_
+13 -11
View File
@@ -1,11 +1,13 @@
#ifndef EDITOR_OP_H_ #ifndef EDITOR_OP_H_
#define EDITOR_OP_H_ #define EDITOR_OP_H_
#include "data.h" #include "data.h"
void editorInsertChar(struct editorConfig *E, int c); void bufferInsertChar(int c);
void editorInsertNewLine(struct editorConfig *E); void bufferInsertNewLine();
void editorDelChar(struct editorConfig *E); void bufferDelChar();
#endif // EDITOR_OP_H_ void editorSetStatusMessage(const char *fmt, ...);
#endif // EDITOR_OP_H_
+22 -17
View File
@@ -1,17 +1,22 @@
#ifndef FILE_IO_H_ #ifndef FILE_IO_H_
#define FILE_IO_H_ #define FILE_IO_H_
#include "data.h" #include "data.h"
#include "row_op.h" #include "row_op.h"
#include "terminal.h" #include "terminal.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/types.h> #include <sys/types.h>
char *editorRowsToString(struct editorConfig *E, int *buffer_len); char *bufferRowsToString(struct buffer_t *buf,int *buffer_len);
void editorOpen(struct editorConfig *E, char *filename);
void editorCloseFile(void);
void editorSave(struct editorConfig *E);
void editorOpen(struct buffer_t *buffer);
#endif // FILE_IO_H_
void editorSave();
void bufferFind(struct buffer_t *buf);
#endif // FILE_IO_H_
+18 -20
View File
@@ -1,20 +1,18 @@
#ifndef INIT_H_ #ifndef INIT_H_
#define INIT_H_ #define INIT_H_
#include "data.h" #include "builtins.h"
#include "terminal.h" #include "data.h"
#include "builtins.h" #include "terminal.h"
#include "../blisp/include/config_tools.h" #include <stdio.h>
#include <stdio.h>
/**
* \fn void initEditor()
void getConfig(); * \brief Job's function is to initialize all the fields of editorConfig.
* */
/**
* \fn void initEditor() void initBuiltins();
* \brief Job's function is to initialize all the fields of editorConfig.
* */ void initEditor();
void initEditor(struct editorConfig *E); #endif // INIT_H_
#endif // INIT_H_
+38 -41
View File
@@ -1,41 +1,38 @@
#ifndef INPUT_H_ #ifndef INPUT_H_
#define INPUT_H_ #define INPUT_H_
#include "data.h" #include "data.h"
#include "define.h" #include "define.h"
#include "output.h" #include "output.h"
#include "terminal.h" #include "terminal.h"
#include "builtins.h" #include "builtins.h"
#include "../blisp/include/config_tools.h" #include <unistd.h>
#include <unistd.h>
// KEYS keycode
// KEYS keycode //
// // ARROW_UP \x1b[A
// ARROW_UP \x1b[A // ARROW_DOWN \x1b[B
// ARROW_DOWN \x1b[B // ARROW_RIGHT \x1b[C
// ARROW_RIGHT \x1b[C // ARROW_LEFT \x1b[D
// ARROW_LEFT \x1b[D // PAGE_UP \x1b[5~
// PAGE_UP \x1b[5~ // PAGE_DOWN \x1b[6~
// PAGE_DOWN \x1b[6~ // HOME \x1b[1~ || <esc>[7~ || <esc>[H || <esc>OH
// HOME \x1b[1~ || <esc>[7~ || <esc>[H || <esc>OH // 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(char *prompt, char * PlaceHolder, char bPathMode);
char *key_to_string(int key);
char *key_to_string(int key);
int execute_key_binding(config_t *config, const char *key_combo, void *context);
int editorMoveCursor(int key);
int handle_key_sequence(struct editorConfig *E, int key);
int executeKeyBind(char *key_sequence);
char *editorPrompt(struct editorConfig *E, char *prompt);
/**
void editorMoveCursor(struct editorConfig *E, int key); * \fn void editorProcessKeypress()
* \brief Get the last key input and do the proper action.
/** */
* \fn void editorProcessKeypress()
* \brief Get the last key input and do the proper action. void editorProcessKeypress();
*/
#endif // INPUT_H_
void editorProcessKeypress(struct editorConfig *E);
#endif // INPUT_H_
+3220
View File
File diff suppressed because it is too large Load Diff
+2177
View File
File diff suppressed because it is too large Load Diff
+22 -28
View File
@@ -1,28 +1,22 @@
#ifndef OUTPUT_H_ #ifndef OUTPUT_H_
#define OUTPUT_H_ #define OUTPUT_H_
#include "append_buffer.h" #include "data.h"
#include "data.h" #include <unistd.h>
#include "define.h"
#include "row_op.h" /**
#include <stdio.h> * \fn void editorDrawRows(struct editorConfig *E, struct abuf *ab)
#include <unistd.h> * \brief Draws left rows of the editor.
*/
/**
* \fn void editorDrawRows(struct editorConfig *E, struct abuf *ab) void editorDrawRows(struct abuf *ab);
* \brief Draws left rows of the editor.
*/ void editorRefreshScreen();
void editorDrawRows(struct editorConfig *E, struct abuf *ab); void editorScroll();
void editorRefreshScreen(struct editorConfig *E); void editorDrawStatusBar(struct abuf *ab);
void editorScroll(struct editorConfig *E); void editorDrawMessageBar(struct abuf *ab);
void editorDrawStatusBar(struct editorConfig *E, struct abuf *ab); #endif // OUTPUT_H_
void editorDrawMessageBar(struct editorConfig *E, struct abuf *ab);
void editorSetStatusMessage(struct editorConfig *E, const char *fmt, ...);
#endif // OUTPUT_H_
+29 -30
View File
@@ -1,30 +1,29 @@
#ifndef ROW_OP_H_ #ifndef ROW_OP_H_
#define ROW_OP_H_ #define ROW_OP_H_
#include "data.h" #include "data.h"
#include "define.h" #include "define.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
int editorRowCxToRx(erow *row, int cursor_x); int bufferRowCxToRx(frow *row, int cursor_x);
void editorUpdateRow(erow *row); int bufferRowRxToCx(frow *row, int rx);
void editorInsertRow(struct editorConfig *E, int at, char *s, size_t len); void bufferUpdatfrow(frow *row);
void editorFreeRow(erow *row); void bufferInsertRow(struct buffer_t *buffer, int at, char *s, size_t len);
void editorDelRow(struct editorConfig *E, int at); void bufferFrefrow(frow *row);
void editorRowInsertChar(struct editorConfig *E, erow *row, int at, int c); void bufferDelRow(struct buffer_t *buffer, int at);
void editorRowAppendString(struct editorConfig *E, erow *row, char *s, void bufferRowInsertChar(struct buffer_t *buffer, frow *row, int at, int c);
size_t len);
void bufferRowAppendString(struct buffer_t *buffer, frow *row, char *s, size_t len);
void editorRowDelchar(struct editorConfig *E, erow *row, int at);
void bufferRowDelchar(struct buffer_t *buffer, frow *row, int at);
void log_string(char * string);
#endif // ROW_OP_H_
#endif // ROW_OP_H_
+62
View File
@@ -0,0 +1,62 @@
/**
* @file split_screen.h
* @brief Split screen management for displaying multiple buffers
*/
#ifndef SPLIT_SCREEN_H_
#define SPLIT_SCREEN_H_
#include "data.h"
/**
* @brief Initializes split screen system
*/
void splitScreenInit(void);
/**
* @brief Splits screen vertically (left-right)
* @param buffer_id_left Buffer ID for left pane
* @param buffer_id_right Buffer ID for right pane
* @return 0 on success, -1 on failure
*/
int splitScreenVertical(int buffer_id_left, int buffer_id_right);
/**
* @brief Splits screen horizontally (top-bottom)
* @param buffer_id_top Buffer ID for top pane
* @param buffer_id_bottom Buffer ID for bottom pane
* @return 0 on success, -1 on failure
*/
int splitScreenHorizontal(int buffer_id_top, int buffer_id_bottom);
/**
* @brief Returns to single buffer fullscreen
*/
void splitScreenUnify(void);
/**
* @brief Switches active pane (focus moves between splits)
* @return 0 on success, -1 on failure
*/
int splitScreenSwitchPane(void);
/**
* @brief Updates the active pane's buffer
* @param buffer_id New buffer ID for active pane
* @return 0 on success, -1 on failure
*/
int splitScreenSetPaneBuffer(int buffer_id);
/**
* @brief Gets current screen layout
* @return Pointer to current ScreenLayout
*/
ScreenLayout *splitScreenGetLayout(void);
/**
* @brief Gets active pane
* @return Pointer to active EditorPane
*/
EditorPane *splitScreenGetActivePane(void);
#endif
+24
View File
@@ -0,0 +1,24 @@
#ifndef SYNTAX_HIGHLIGHTER_H_
#define SYNTAX_HIGHLIGHTER_H_
#include "color.h"
// Color codes that only affect foreground, preserving your background color
#define COLOR_RESET "\x1b[39m" // Reset all (4 bytes)
// Token types
typedef enum {
TOKEN_KEYWORD,
TOKEN_DATATYPE,
TOKEN_STRING,
TOKEN_COMMENT,
TOKEN_NUMBER,
TOKEN_OPERATOR,
TOKEN_DEFAULT
} SyntaxTokenType;
const char *get_color(SyntaxTokenType type);
char *highlight_line(const char * line, int *length);
#endif
+217
View File
@@ -0,0 +1,217 @@
/* termiWin.h
*
* Copyright (C) 2017 Christian Visintin - christian.visintin1997@gmail.com
*
* This file is part of "termiWin: a termios porting for Windows"
*
* termiWin is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* termiWin is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with termiWin. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#ifndef TERMIWIN_H_
#define TERMIWIN_H_
#define TERMIWIN_VERSION "1.2.0"
#define TERMIWIN_MAJOR_VERSION 1
#define TERMIWIN_MINOR_VERSION 2
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#ifdef _MSC_VER
#pragma comment(lib, "Ws2_32.lib")
#endif
/*Redefining functions from winsock to termiWin. This is very important since winsock2 defines functions such as close as closesocket we have to redefine it*/
#ifdef __MINGW32__
#define TERMIWIN_DONOTREDEFINE
#endif
#ifndef TERMIWIN_DONOTREDEFINE
#define read read_serial
#define write serial_write
#define open open_serial
#define close close_serial
#define select select_serial
#endif
#if !defined(SSIZE_MAX)
// ssize_t
#if SIZE_MAX == UINT_MAX
typedef int ssize_t; /* common 32 bit case */
#define SSIZE_MAX INT_MAX
#elif SIZE_MAX == ULONG_MAX
typedef long ssize_t; /* linux 64 bits */
#define SSIZE_MAX LONG_MAX
#elif SIZE_MAX == ULLONG_MAX
typedef long long ssize_t; /* windows 64 bits */
#define SSIZE_MAX LLONG_MAX
#endif
#endif
//Serial options - Linux -> Windows
/*setAttr flags - ~ in front of flags -> deny them*/
//iFlag
#define INPCK 0x00004000 /*If this bit is set, input parity checking is enabled. If it is not set, no checking at all is done for parity errors on input; the characters are simply passed through to the application.*/
#define IGNPAR 0x00001000 /*If this bit is set, any byte with a framing or parity error is ignored. This is only useful if INPCK is also set.*/
#define PARMRK 0x00040000 /*If this bit is set, input bytes with parity or framing errors are marked when passed to the program. This bit is meaningful only when INPCK is set and IGNPAR is not set.*/
#define ISTRIP 0x00008000 /*If this bit is set, valid input bytes are stripped to seven bits; otherwise, all eight bits are available for programs to read. */
#define IGNBRK 0x00000400 /*If this bit is set, break conditions are ignored. */
#define BRKINT 0x00000100 /*If this bit is set and IGNBRK is not set, a break condition clears the terminal input and output queues and raises a SIGINT signal for the foreground process group associated with the terminal. */
#define IGNCR 0x00000800 /*If this bit is set, carriage return characters ('\r') are discarded on input. Discarding carriage return may be useful on terminals that send both carriage return and linefeed when you type the RET key. */
#define ICRNL 0x00000200 /*If this bit is set and IGNCR is not set, carriage return characters ('\r') received as input are passed to the application as newline characters ('\n').*/
#define INLCR 0x00002000 /*If this bit is set, newline characters ('\n') received as input are passed to the application as carriage return characters ('\r').*/
#define IXOFF 0x00010000 /*If this bit is set, start/stop control on input is enabled. In other words, the computer sends STOP and START characters as necessary to prevent input from coming in faster than programs are reading it. The idea is that the actual terminal hardware that is generating the input data responds to a STOP character by suspending transmission, and to a START character by resuming transmission.*/
#define IXON 0x00020000 /*If this bit is set, start/stop control on output is enabled. In other words, if the computer receives a STOP character, it suspends output until a START character is received. In this case, the STOP and START characters are never passed to the application program. If this bit is not set, then START and STOP can be read as ordinary characters.*/
//lFlag
#define ICANON 0x00001000 /*This bit, if set, enables canonical input processing mode. Otherwise, input is processed in noncanonical mode. */
#define ECHO 0x00000100 /*If this bit is set, echoing of input characters back to the terminal is enabled.*/
#define ECHOE 0x00000200 /*If this bit is set, echoing indicates erasure of input with the ERASE character by erasing the last character in the current line from the screen. Otherwise, the character erased is re-echoed to show what has happened (suitable for a printing terminal). */
#define ECHOK 0x00000400 /*This bit enables special display of the KILL character by moving to a new line after echoing the KILL character normally. The behavior of ECHOKE (below) is nicer to look at.*/
#define ECHONL 0x00000800 /*If this bit is set and the ICANON bit is also set, then the newline ('\n') character is echoed even if the ECHO bit is not set. */
#define ISIG 0x00004000 /*This bit controls whether the INTR, QUIT, and SUSP characters are recognized. The functions associated with these characters are performed if and only if this bit is set. Being in canonical or noncanonical input mode has no effect on the interpretation of these characters. */
#define IEXTEN 0x00002000 /*On BSD systems and GNU/Linux and GNU/Hurd systems, it enables the LNEXT and DISCARD characters.*/
#define NOFLSH 0x00008000 /*Normally, the INTR, QUIT, and SUSP characters cause input and output queues for the terminal to be cleared. If this bit is set, the queues are not cleared. */
#define TOSTOP 0x00010000 /*If this bit is set and the system supports job control, then SIGTTOU signals are generated by background processes that attempt to write to the terminal.*/
//cFlag
#define CSTOPB 0x00001000 /*If this bit is set, two stop bits are used. Otherwise, only one stop bit is used. */
#define PARENB 0x00004000 /*If this bit is set, generation and detection of a parity bit are enabled*/
#define PARODD 0x00008000 /*This bit is only useful if PARENB is set. If PARODD is set, odd parity is used, otherwise even parity is used. */
#define CSIZE 0x00000c00 /*This is a mask for the number of bits per character. */
#define CS5 0x00000000 /*This specifies five bits per byte. */
#define CS6 0x00000400 /*This specifies six bits per byte. */
#define CS7 0x00000800 /*This specifies seven bits per byte. */
#define CS8 0x00000c00 /*This specifies eight bits per byte. */
#define CLOCAL 0x00000000 /*Ignore modem control lines -> ignore data carrier detected - not implementable in windows*/
#define CREAD 0x00000000 /*Enable receiver - if is not set no character will be received*/
//oFlag
#define OPOST 0x00000100 /*If this bit is set, output data is processed in some unspecified way so that it is displayed appropriately on the terminal device. This typically includes mapping newline characters ('\n') onto carriage return and linefeed pairs. */
//cc
#define VEOF 0
#define VEOL 1
#define VERASE 2
#define VINTR 3
#define VKILL 4
#define VMIN 5 /*If set to 0, serial communication is NOT-BLOCKING, otherwise is BLOCKING*/
#define VQUIT 6
#define VSTART 7
#define VSTOP 8
#define VSUSP 9
#define VTIME 10
//END OF setAttr flags
/*Controls*/
#define TIOMBIC DTR_CONTROL_DISABLE
#define TIOMBIS DTR_CONTROL_ENABLE
#define CRTSCTS RTS_CONTROL_ENABLE
/*Others*/
#define NCCS 11
//Baud speed
#define B110 CBR_110
#define B300 CBR_300
#define B600 CBR_600
#define B1200 CBR_2400
#define B2400 CBR_2400
#define B4800 CBR_4800
#define B9600 CBR_9600
#define B19200 CBR_19200
#define B38400 CBR_38400
#define B57600 CBR_57600
#define B115200 CBR_115200
/*Attributes optional_actions*/
#define TCSANOW 0
#define TCSADRAIN 1
#define TCSAFLUSH 2
/*TCFLUSH options*/
#define TCIFLUSH 0
#define TCOFLUSH 1
#define TCIOFLUSH 2
/*TCFLOW optons*/
#define TCOOFF 0
#define TCOON 1
#define TCIOFF 2
#define TCION 3
//typdef
typedef unsigned tcflag_t; /*This is an unsigned integer type used to represent the various bit masks for terminal flags.*/
typedef unsigned cc_t; /*This is an unsigned integer type used to represent characters associated with various terminal control functions.*/
typedef unsigned speed_t; /*used for terminal baud rates*/
typedef struct termios
{
tcflag_t c_iflag; /*input modes*/
tcflag_t c_oflag; /*output modes*/
tcflag_t c_cflag; /*control modes*/
tcflag_t c_lflag; /*local modes*/
cc_t c_cc[NCCS]; /*special character*/
} termios;
//Serial configuration functions
int tcgetattr(int fd, struct termios *termios_p);
int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);
int tcsendbreak(int fd, int duration);
int tcdrain(int fd);
int tcflush(int fd, int queue_selector);
int tcflow(int fd, int action);
void cfmakeraw(struct termios *termios_p);
speed_t cfgetispeed(const struct termios *termios_p);
speed_t cfgetospeed(const struct termios *termios_p);
int cfsetispeed(struct termios *termios_p, speed_t speed);
int cfsetospeed(struct termios *termios_p, speed_t speed);
int cfsetspeed(struct termios * termios_p, speed_t speed);
//Write/Read/Open/Close/Select Functions
ssize_t read_serial(int fd, void* buffer, size_t count);
ssize_t write_serial(int fd, const void* buffer, size_t count);
int open_serial(const char* portname, int opt);
int close_serial(int fd);
int select_serial(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
//get Handle out of the COM structure
HANDLE getHandle();
#endif
#ifndef _WIN32
#pragma message("-Warning: termiWin requires a Windows system!")
#endif
#endif
+33 -34
View File
@@ -1,34 +1,33 @@
#ifndef TERMINAL_H_ #ifndef TERMINAL_H_
#define TERMINAL_H_ #define TERMINAL_H_
/* includes */ /* includes */
#include "data.h" #include "data.h"
#include "define.h" #include "define.h"
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/ioctl.h> // #include <sys/ioctl.h>
#include <termios.h> #include <unistd.h>
#include <unistd.h>
/**
/** * \fn void die(const char *s)
* \fn void die(const char *s) * \brief Exit program and return s error message.
* \brief Exit program and return s error message. * \param *s Error string
* \param *s Error string * */
* */
void die(const char *s);
void die(const char *s);
void disableRawMode();
void disableRawMode(struct editorConfig *E);
void enableRawMode();
void enableRawMode(struct editorConfig *E);
int editorReadKey();
int editorReadKey();
int getCursorPosition(int *rows, int *cols);
int getCursorPosition(int *rows, int *cols);
int getWindowSize(int *rows, int *cols);
int getWindowSize(int *rows, int *cols);
#endif
#endif
+32
View File
@@ -0,0 +1,32 @@
/* termios.h
*
* Copyright (C) 2017 Christian Visintin - christian.visintin1997@gmail.com
*
* This file is part of "termiWin: a termios porting for Windows"
*
* termiWin is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* termiWin is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with termiWin. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#ifdef _WIN32
#ifndef TERMIOS_H
#define TERMIOS_H
#include "termiWin.h"
#endif // TERMIOS_H
#endif // _WIN32
Executable
+44
View File
@@ -0,0 +1,44 @@
#!/bin/bash
echo "--- Welcome to Beluga installer ---"
read -p "Do you want to start the installation ? (Y/n)" confirm && [[ $confirm == [yY] || $confirm == [yY][eE][sS] ]] || exit 1
# Check dependencies
if ! command -v "meson" &>/dev/null; then
echo "❌ Error: meson not found. Please install it first."
exit 1
fi
# Create config files
echo "Create config files ..."
mkdir -pv ~/.beluga/
mkdir -pv ~/.beluga/assets/
cp -rv ./assets/beluga.txt ~/.beluga/assets/
mkdir -pv ~/.beluga/packages/
mkdir -pv ~/.beluga/config/
read -p "Do you want to replace your config file or keep it (init.lisp.bak) / (init.lisp.new) ? (Y/n)" confirm
if [[ "$confirm" =~ ^[Yy]$ ]]; then
mv ~/.beluga/config/init.lisp ~/.beluga/config/init.lisp.bak
cp -rv ./config/init.lisp ~/.beluga/config/
else
cp -rv ./config/init.lisp ~/.beluga/config/init.lisp.new
fi
# Compile the project
echo "Start compilation ..."
meson setup build/
meson compile -C build/
# Add to path
echo "Adding beluga to the path"
sudo cp -f ./build/beluga /usr/local/bin/
echo "Installation finish"
echo "Check ~/.beluga/config/init.lisp for customization"
+54 -34
View File
@@ -1,34 +1,54 @@
/** /**
* \file main.c * \file main.c
* \author Arthur Barraux * \author Arthur Barraux
* \brief Base file of Beluga text editor. Contain fonctions for terminal * \brief Base file of Beluga text editor. Contain fonctions for terminal
* interactions. \version 0.1 \date 21 septembre 2024 * interactions. \version 0.1 \date 21 septembre 2024
*/ */
#include <unistd.h> #include "include/buffer.h"
#define _DEFAULT_SOURCE #include "include/split_screen.h"
#define _BSD_SOURCE #include <stdio.h>
#define _GNU_SOURCE #include <stdlib.h>
#include <string.h>
#include "include/data.h" #include <unistd.h>
#include "include/file_io.h"
#include "include/init.h" #define _DEFAULT_SOURCE
#include "include/input.h" #define _BSD_SOURCE
#include "include/output.h" #define _GNU_SOURCE
#include "include/terminal.h"
#include "include/data.h"
int main(int argc, char *argv[]) { #include "include/file_io.h"
static struct editorConfig E; #include "include/init.h"
enableRawMode(&E); #include "include/input.h"
initEditor(&E); #include "include/editor_op.h"
if (argc >= 2) { #include "include/terminal.h"
editorOpen(&E, argv[1]);
} struct editorConfig E;
editorSetStatusMessage(&E, "HELP: Ctrl-S = save | Ctrl-Q = quit");
int main(int argc, char *argv[]) {
while (1) {
editorRefreshScreen(&E); char * splash_screen = (char *) calloc(256, sizeof(char));
editorProcessKeypress(&E);
} enableRawMode();
return 0; initEditor();
} if (argc >= 2) {
EditorPane *active = splitScreenGetActivePane();
active->buffer_id = bufferCreate(argv[1]);
} else {
strcat(splash_screen, getenv("HOME"));
strcat(splash_screen, "/.beluga/assets/beluga.txt");
fprintf(stderr, "%s\n", splash_screen);
EditorPane *active = splitScreenGetActivePane();
active->buffer_id = bufferCreate(splash_screen);
}
//free(splash_screen);
editorSetStatusMessage("HELP: Ctrl-x Ctrl-s = save | Ctrl-x Ctrl-c = quit");
while (1) {
editorRefreshScreen();
editorProcessKeypress();
}
return 0;
}
+37
View File
@@ -0,0 +1,37 @@
project('beluga', 'c',
version : '2.3',
default_options : [
'c_std=none',
]
)
cc = meson.get_compiler('c')
m = cc.find_library('m', required: false)
# Source files
src_files = files(
'main.c',
'src/append_buffer.c',
'src/editor_op.c',
'src/syntax_highlighter.c',
'src/file_io.c',
'src/init.c',
'src/input.c',
'src/output.c',
'src/row_op.c',
'src/terminal.c',
'src/builtins.c',
'src/buffer.c',
'src/split_screen.c'
)
# Add termiWin.c for Windows builds
if host_machine.system() == 'windows'
src_files += files('src/termiWin.c')
endif
# Executable
executable('beluga',
src_files,
dependencies: [m]
)
+16 -14
View File
@@ -1,14 +1,16 @@
#include "../include/append_buffer.h" #include "../include/append_buffer.h"
void abAppend(struct abuf *ab, const char *s, int len) { extern struct editorConfig E;
char *new = realloc(ab->b, ab->len + len);
void abAppend(struct abuf *ab, const char *s, int len) {
if (new == NULL) { char *new = realloc(ab->b, ab->len + len);
return;
} if (new == NULL) {
memcpy(&new[ab->len], s, len); return;
ab->b = new; }
ab->len += len; memcpy(&new[ab->len], s, len);
} ab->b = new;
ab->len += len;
void abFree(struct abuf *ab) { free(ab->b); } }
void abFree(struct abuf *ab) { free(ab->b); }
+336
View File
@@ -0,0 +1,336 @@
/**
* @file buffer.c
* @brief Buffer management implementation for multiple open files
*/
#include "../include/buffer.h"
#include "../include/file_io.h"
#include "../include/editor_op.h"
#include "../include/data.h"
#include "include/split_screen.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern struct editorConfig E;
/**
* @brief Finds a buffer by filename
* @param filename The filename to search for
* @return Buffer ID if found, -1 if not found
*/
static int bufferFindByFilename(const char *filename) {
for (int i = 0; i < E.number_of_buffer; i++) {
if (E.buffers[i].filename != NULL &&
strcmp(E.buffers[i].filename, filename) == 0) {
return E.buffers[i].buffer_id;
}
}
return -1;
}
/**
* @brief Finds a buffer by ID
* @param buffer_id The buffer ID to find
* @return Pointer to buffer, NULL if not found
*/
struct buffer_t *bufferFindById(int buffer_id) {
for (int i = 0; i < E.number_of_buffer; i++) {
if (E.buffers[i].buffer_id == buffer_id) {
return &E.buffers[i];
}
}
return NULL;
}
/**
* @brief Creates a new buffer for a file
* @details Allocates buffer slot, loads file content, saves buffer metadata.
* If file is already open, switches to existing buffer instead.
* @param filename Path to the file
* @return Buffer ID on success, -1 on failure
*/
int bufferCreate(const char *filename) {
// Check if file is already open
int existing_id = bufferFindByFilename(filename);
if (existing_id != -1) {
return bufferSwitch(existing_id);
}
// Check if we have space for more buffers
if (E.number_of_buffer >= 64) {
editorSetStatusMessage("Error: maximum buffers reached (64)");
return -1;
}
// Initialize new buffer
struct buffer_t *new_buf = &E.buffers[E.number_of_buffer];
new_buf->buffer_id = E.number_of_buffer;
new_buf->filename = strdup(filename);
new_buf->type = FILE_BUFF;
new_buf->state = READ_AND_WRITE;
new_buf->x = 0;
new_buf->y = 0;
new_buf->row_offset = 0;
new_buf->col_offset = 0;
new_buf->dirty = 0; // New file starts clean
// Load file content using existing editorOpen
editorOpen(new_buf);
E.number_of_buffer++;
editorSetStatusMessage("Opened: %s (buffer %d)", filename, new_buf->buffer_id);
return new_buf->buffer_id;
}
/**
* @brief Switches to a specific buffer by ID
* @details Saves current buffer state, loads target buffer content and state.
* @param buffer_id The buffer ID to switch to
* @return 0 on success, -1 on failure
*/
int bufferSwitch(int buffer_id) {
struct buffer_t *target = bufferFindById(buffer_id);
if (target == NULL) {
E.layout.panes[E.layout.active_pane].buffer_id = buffer_id;
editorSetStatusMessage("Error: buffer not found");
return -1;
}
editorSetStatusMessage("Switched to: %s (buffer %d)",
target->filename, buffer_id);
return 0;
}
/**
* @brief Closes a buffer by ID
* @details Frees buffer resources and removes from buffer list.
* Prompts to save unsaved changes. If closing current buffer,
* switches to next available buffer.
* @param buffer_id The buffer ID to close
* @return 0 on success, -1 on failure
*/
int bufferClose(int buffer_id) {
struct buffer_t *buf = bufferFindById(buffer_id);
if (buf == NULL) {
editorSetStatusMessage("Error: buffer not found");
return -1;
}
EditorPane * active = splitScreenGetActivePane();
// If this is the current buffer, find next buffer to switch to
if (active->buffer_id == buffer_id) {
int next_id = -1;
// Try to switch to next buffer
for (int i = 0; i < E.number_of_buffer; i++) {
if (E.buffers[i].buffer_id != buffer_id) {
next_id = E.buffers[i].buffer_id;
break;
}
}
if (next_id != -1) {
bufferSwitch(next_id);
} else {
// No other buffers, clear editor
editorCloseFile();
}
}
// Free buffer resources
free(buf->filename);
buf->filename = NULL;
buf->buffer_id = -1;
// Remove from buffer list by shifting
for (int i = 0; i < E.number_of_buffer; i++) {
if (E.buffers[i].buffer_id == buffer_id) {
for (int j = i; j < E.number_of_buffer - 1; j++) {
E.buffers[j] = E.buffers[j + 1];
}
E.number_of_buffer--;
break;
}
}
editorSetStatusMessage("Closed buffer %d", buffer_id);
return 0;
}
/**
* @brief Gets the current active buffer
* @return Pointer to current buffer_t, NULL if none
*/
struct buffer_t *bufferGetCurrent(void) {
EditorPane * active = splitScreenGetActivePane();
return bufferFindById(active->buffer_id);
}
/**
* @brief Lists all open buffers
* @details Prints buffer information to status message
* @return Number of open buffers
*/
int bufferListAll(void) {
if (E.number_of_buffer == 0) {
editorSetStatusMessage("No buffers open");
return 0;
}
char buf[256] = "";
int offset = 0;
EditorPane * active = splitScreenGetActivePane();
for (int i = 0; i < E.number_of_buffer; i++) {
struct buffer_t *b = &E.buffers[i];
char marker = (b->buffer_id == active->buffer_id) ? '*' : ' ';
offset += snprintf(&buf[offset], sizeof(buf) - offset,
"%c%d:%s ", marker, b->buffer_id,
b->filename ? b->filename : "[No Name]");
}
editorSetStatusMessage("Buffers: %s", buf);
return E.number_of_buffer;
}
/**
* @brief Saves current buffer to disk
* @return 0 on success, -1 on failure
*/
int bufferSave(void) {
EditorPane * active = splitScreenGetActivePane();
if (active->buffer_id == -1) {
editorSetStatusMessage("Error: no buffer active");
return -1;
}
editorSave();
return 0;
}
/**
* @brief Saves all buffers to disk
* @details Iterates through all buffers, saves each one
* @return 0 on success, -1 on failure
*/
int bufferSaveAll(void) {
int saved = 0;
int failed = 0;
for (int i = 0; i < E.number_of_buffer; i++) {
if (bufferSwitch(E.buffers[i].buffer_id) == 0) {
if (E.dirty && bufferSave() == 0) {
saved++;
}
} else {
failed++;
}
}
editorSetStatusMessage("Saved %d buffers (%d failed)", saved, failed);
return (failed == 0) ? 0 : -1;
}
/**
* @brief Creates a new terminal buffer
* @return Buffer ID on success, -1 on failure
*/
int bufferCreateTerminal(void) {
// Check if we have space for more buffers
if (E.number_of_buffer >= 64) {
editorSetStatusMessage("Error: maximum buffers reached (64)");
return -1;
}
// Initialize new terminal buffer
struct buffer_t *new_buf = &E.buffers[E.number_of_buffer];
new_buf->buffer_id = E.number_of_buffer;
new_buf->filename = strdup("[Terminal]");
new_buf->type = TERMINAL_BUFF;
new_buf->state = READ_AND_WRITE;
new_buf->x = 0;
new_buf->y = 0;
new_buf->row_offset = 0;
new_buf->col_offset = 0;
new_buf->dirty = 0;
new_buf->numrows = 0;
new_buf->row = NULL;
// Initialize terminal-specific data
new_buf->terminal_command = NULL;
new_buf->terminal_pid = -1;
new_buf->terminal_output = NULL;
new_buf->terminal_output_size = 0;
// Initialize terminal-specific data
new_buf->terminal_command = NULL;
new_buf->terminal_pid = -1;
new_buf->terminal_output = NULL;
new_buf->terminal_output_size = 0;
E.number_of_buffer++;
editorSetStatusMessage("Created terminal buffer %d", new_buf->buffer_id);
return new_buf->buffer_id;
}
/**
* @brief Executes a command in the terminal buffer
* @param buffer_id The terminal buffer ID
* @param command The command to execute
* @return 0 on success, -1 on failure
*/
int bufferExecuteTerminalCommand(int buffer_id, const char *command) {
struct buffer_t *buf = bufferFindById(buffer_id);
if (buf == NULL || buf->type != TERMINAL_BUFF) {
editorSetStatusMessage("Error: buffer is not a terminal buffer");
return -1;
}
// Free any existing terminal data
if (buf->terminal_command) {
free(buf->terminal_command);
}
if (buf->terminal_output) {
free(buf->terminal_output);
}
// Store the command
buf->terminal_command = strdup(command);
// For now, we'll simulate terminal output
// In a real implementation, this would fork a process and capture output
char output[256];
snprintf(output, sizeof(output), "$ %s\n[Command executed: %s]\n", command, command);
buf->terminal_output = strdup(output);
buf->terminal_output_size = strlen(output);
// Update the buffer content to show terminal output
// Clear existing rows
for (int i = 0; i < buf->numrows; ++i) {
free(buf->row[i].chars);
free(buf->row[i].render);
}
free(buf->row);
buf->row = NULL;
buf->numrows = 0;
// Add terminal output as rows
// Make a copy for parsing since strtok modifies the string
char *output_copy = strdup(buf->terminal_output);
char *line = strtok(output_copy, "\n");
while (line != NULL) {
bufferInsertRow(buf, buf->numrows, line, strlen(line));
line = strtok(NULL, "\n");
}
free(output_copy);
buf->dirty = 1;
editorSetStatusMessage("Executed: %s", command);
return 0;
}
+530 -131
View File
@@ -1,131 +1,530 @@
/**
// function_registry.c * @file builtins.c
#include "../include/builtins.h" * @brief Built-in Lisp functions for editor operations
#include <stdio.h> * @details Provides Lisp bindings for core editor functionality including
#include <stdlib.h> * cursor movement, file operations, keybinding management, and text manipulation
#include <string.h> */
// Static array to hold function mappings #include "../include/builtins.h"
function_entry_t function_registry[256]; #include "../include/data.h"
int registry_count = 0; #include "../include/define.h"
#include "../include/editor_op.h"
// Initialize the function registry #include "../include/file_io.h"
void init_function_registry(void) { #include "../include/input.h"
register_function("editor-save", editorSave); #include "../include/row_op.h"
register_function("move-cursor-beg-line", moveCursorBeginLine); #include "../include/buffer.h"
register_function("move-cursor-end-line", moveCursorEndLine); #include "../include/split_screen.h"
register_function("editor-quit", editorQuit);
register_function("move-cursor-up", editorMoveCursorUp); #include <stdio.h>
register_function("move-cursor-down", editorMoveCursorDown); #include <stdlib.h>
register_function("move-cursor-right", editorMoveCursorRight); #include <string.h>
register_function("move-cursor-left", editorMoveCursorLeft);
register_function("move-cursor-page-up", editorMoveCursorPageUp); /**
register_function("move-cursor-page-down", editorMoveCursorPageDown); * @brief Finds a prefix configuration by name
register_function("delete-previous-char", editorDelChar); * @details Searches the prefix array for a prefix matching the given name.
register_function("delete-next-char", deleteNextChar); * Returns the first prefix (default) if no match is found.
register_function("editor-insert-new-line", editorInsertNewLine); * @param prefix_name Name of the prefix to search for (max 64 chars)
} * @return Matching prefix_t structure, or E.prefix[0] if not found
* @note Updates global editor state E
// Register a function with a name */
int register_function(const char *name, command_func_t func) { struct prefix_t find_prefix(const char prefix_name[64]) {
if (registry_count >= 256) { int i = E.number_of_prefix + 1;
return -1; // Registry full while (i--) {
} if (!strcmp(prefix_name, E.prefix[i].prefix_name)) {
return E.prefix[i];
function_registry[registry_count].name = strdup(name); }
function_registry[registry_count].func = func; }
registry_count++; return E.prefix[0];
return 0; }
}
/**
// Find a function by name * @brief Lisp function to bind a command to a key sequence
command_func_t find_function(const char *name) { * @details Registers a keybinding with an associated Lisp command and optional
log_string("registry :"); * prefix modifier. Dynamically extends the keybind array.
char *tmp = malloc(3 * sizeof(char)); * @param args Lisp list of 3 arguments: (key-sequence command prefix-name)
sprintf(tmp, "%d\n", registry_count); * - key_sequence (string): The key or key combination to bind
log_string(tmp); * - command (Lisp): The Lisp command/function to execute
for (int i = 0; i < registry_count; i++) { * - prefix_name (string): Prefix modifier name
if (strcmp(function_registry[i].name, name) == 0) { * @param e Error pointer for Lisp error handling
return function_registry[i].func; * @param ctx Lisp context
} * @return lisp_null()
} * @note Updates global editor state E (key_binds array)
return NULL; */
} Lisp mapKey(Lisp args, LispError *e, LispContext ctx) {
/*
// Execute a command by name * 3 arguments keybind command prefix
int execute_command(const char *name, struct editorConfig *E) { */
log_string(name); const char *key_sequence = lisp_string(lisp_car(args));
command_func_t func = find_function(name); args = lisp_cdr(args);
if (func) { // second argument
func(E); Lisp func = lisp_car(args);
return 0;
} E.key_binds = (struct keyBind_t *)realloc(
log_string("Unknown command: \n"); E.key_binds, ++E.number_of_keybinds * sizeof(struct keyBind_t));
return -1; E.key_binds[E.number_of_keybinds - 1].key_sequence =
} (char *)malloc(50 * sizeof(char));
// Builtins tools strncpy(E.key_binds[E.number_of_keybinds - 1].key_sequence, key_sequence, 50);
void moveCursorBeginLine(struct editorConfig *E) { E->cursor_x = 0; } E.key_binds[E.number_of_keybinds - 1].command = func;
void moveCursorEndLine(struct editorConfig *E) { // Third argument
if (E->cursor_y < E->numrows) { args = lisp_cdr(args);
E->cursor_x = E->row[E->cursor_y].size; const char *prefix_name = lisp_string(lisp_car(args));
} struct prefix_t prefix = find_prefix(prefix_name);
}
E.key_binds[E.number_of_keybinds - 1].prefix_id = prefix.prefix_id;
void editorQuit(struct editorConfig *E) {
log_string("time to quit\n"); return lisp_null();
if (E->dirty && E->quit_times > 0) { }
editorSetStatusMessage(E,
"WARNING! Changes hasn't been saved. Press Ctrl-Q " /**
"another time to quit."); * @brief Lisp function to create a new terminal buffer
--E->quit_times; * @details Creates a new terminal buffer and switches to it.
return; * @param args Lisp arguments (ignored)
} * @param e Error pointer for Lisp error handling
write(STDOUT_FILENO, "\x1b[2J", 4); * @param ctx Lisp context
write(STDOUT_FILENO, CURSOR_TOP_LEFT, 3); * @return lisp_null()
disableRawMode(E); * @note Updates global editor state E
exit(0); */
} Lisp editorCreateTerminal(Lisp args, LispError *e, LispContext ctx) {
int buffer_id = bufferCreateTerminal();
if (buffer_id >= 0) {
void editorMoveCursorUp(struct editorConfig *E) { bufferSwitch(buffer_id);
editorMoveCursor(E, ARROW_UP); }
} return lisp_null();
}
void editorMoveCursorDown(struct editorConfig *E) {
editorMoveCursor(E, ARROW_DOWN); /**
} * @brief Lisp function to move cursor in a specified direction
* @details Moves the editor cursor up, down, left, or right based on direction string.
void editorMoveCursorRight(struct editorConfig *E) { * @param args Lisp list with one argument: direction string
editorMoveCursor(E, ARROW_RIGHT); * - "u": up, "d": down, "r": right, "l": left
} * @param e Error pointer for Lisp error handling
* @param ctx Lisp context
void editorMoveCursorLeft(struct editorConfig *E) { * @return Lisp boolean indicating whether movement was valid
editorMoveCursor(E, ARROW_LEFT); * @note Updates global editor state E
} * @see editorMoveCursor()
*/
void deleteNextChar(struct editorConfig *E) { Lisp moveCursor(Lisp args, LispError *e, LispContext ctx) {
editorMoveCursorRight(E); const char *direction = lisp_string(lisp_car(args));
editorDelChar(E); int is_in = 0;
} switch (direction[0]) {
case 'u':
void editorMoveCursorPageUp(struct editorConfig *E) { is_in = editorMoveCursor(ARROW_UP);
E->cursor_y = E->row_offset; break;
int times = E->screenrows; case 'd':
while (--times) { is_in = editorMoveCursor(ARROW_DOWN);
editorMoveCursor(E, ARROW_UP); break;
} case 'r':
} is_in = editorMoveCursor(ARROW_RIGHT);
break;
void editorMoveCursorPageDown(struct editorConfig *E) { case 'l':
E->cursor_y = E->row_offset + E->screenrows - 1; is_in = editorMoveCursor(ARROW_LEFT);
if (E->cursor_y > E->numrows) { break;
E->cursor_y = E->numrows; }
} fprintf(stderr, "move lisp %d\n", is_in);
int times = E->screenrows; return lisp_make_bool(is_in);
while (--times) { }
editorMoveCursor(E, ARROW_DOWN);
} /**
} * @brief Frees all dynamically allocated editor structures
* @details Releases memory for prefix table, keybinds, filename, and all rows.
* Called during shutdown to prevent memory leaks.
* @note Updates global editor state E
*/
void free_structs(void) {
int i, j;
free(E.prefix);
for (i = 0; i < E.number_of_keybinds; ++i) {
free(E.key_binds[i].key_sequence);
}
free(E.key_binds);
// free layout
free(E.layout.panes);
// Free buffers
for (i = 0; i < E.number_of_buffer; ++i) {
free(E.buffers[i].filename);
for (j = 0; j < E.buffers[i].numrows; ++j) {
free(E.buffers[i].row[j].render);
free(E.buffers[i].row[j].chars);
}
free(E.buffers[i].row);
}
free(E.init_file_path);
fclose(E.fd_init_file);
}
/**
* @brief Lisp function to quit the editor
* @details Closes editor with unsaved changes protection. Prompts user to confirm
* quit after multiple attempts if file is dirty. Cleans up resources and restores terminal.
* @param args Lisp arguments (unused)
* @param e Error pointer for Lisp error handling
* @param ctx Lisp context
* @return lisp_null() (never returns on successful exit)
* @note Calls exit(0) to terminate program
* @note Updates quit_times_buffer counter
* @see free_structs()
*/
Lisp editorQuit(Lisp args, LispError *e, LispContext ctx) {
if (E.dirty && E.quit_times_buffer > 0) {
editorSetStatusMessage("WARNING! Changes hasn't been saved. Press Ctrl-Q "
"another time to quit.");
--E.quit_times_buffer;
return lisp_null();
}
free_structs();
write(STDOUT_FILENO, "\x1b[2J", 4);
write(STDOUT_FILENO, CURSOR_TOP_LEFT, 3);
disableRawMode();
lisp_shutdown(E.ctx);
exit(0);
return lisp_null();
}
/**
* @brief Lisp function to save the current file
* @details Wrapper around editorSave() for use in Lisp keybindings.
* @param args Lisp arguments (unused)
* @param e Error pointer for Lisp error handling
* @param ctx Lisp context
* @return lisp_null()
* @see editorSave()
*/
Lisp l_editorSplitScreenVertical(Lisp args, LispError *e, LispContext ctx) {
if (E.number_of_buffer >= 2) {
splitScreenVertical(E.buffers[0].buffer_id, E.buffers[1].buffer_id);
} else {
editorSetStatusMessage("Need at least 2 buffers open");
}
return lisp_null();
}
Lisp l_editorSave(Lisp args, LispError *e, LispContext ctx) {
editorSave();
return lisp_null();
}
/**
* @brief Lisp function to insert a new line at cursor
* @details Wrapper around editorInsertNewLine() for use in Lisp keybindings.
* @param args Lisp arguments (unused)
* @param e Error pointer for Lisp error handling
* @param ctx Lisp context
* @return lisp_null()
* @see editorInsertNewLine()
*/
Lisp l_editorInsertNewLine(Lisp args, LispError *e, LispContext ctx) {
bufferInsertNewLine();
return lisp_null();
}
/**
* @brief Lisp function to insert a tab (spaces) at cursor
* @details Inserts TAB_LENGTH spaces at the current cursor position.
* @param args Lisp arguments (unused)
* @param e Error pointer for Lisp error handling
* @param ctx Lisp context
* @return lisp_null()
* @note Uses E.constantes.TAB_LENGTH for indentation width
*/
Lisp l_editorInserTab(Lisp args, LispError *e, LispContext ctx) {
for (int i = 0; i<E.constantes.TAB_LENGTH; ++i) {
bufferInsertChar(' ');
}
return lisp_null();
}
/**
* @brief Lisp function to move cursor to beginning of line
* @details Moves cursor to column 0 of the current line.
* @param args Lisp arguments (unused)
* @param e Error pointer for Lisp error handling
* @param ctx Lisp context
* @return lisp_null()
* @note Updates global editor state E
*/
Lisp moveCursorBeginLine(Lisp args, LispError *e, LispContext ctx) {
E.layout.panes[E.layout.active_pane].cursor_x = 0;
return lisp_null();
}
/**
* @brief Lisp function to move cursor to end of line
* @details Moves cursor to the end of the current line content.
* @param args Lisp arguments (unused)
* @param e Error pointer for Lisp error handling
* @param ctx Lisp context
* @return lisp_null()
* @note Updates global editor state E
*/
Lisp moveCursorEndLine(Lisp args, LispError *e, LispContext ctx) {
EditorPane *active = splitScreenGetActivePane();
struct buffer_t *buf = bufferFindById(active->buffer_id);
if (active->cursor_y < buf->numrows) {
active->cursor_x = buf->row[active->cursor_y].size;
}
return lisp_null();
}
/**
* @brief Lisp function to delete character before cursor
* @details Wrapper around editorDelChar() for use in Lisp keybindings.
* @param args Lisp arguments (unused)
* @param e Error pointer for Lisp error handling
* @param ctx Lisp context
* @return lisp_null()
* @see editorDelChar()
*/
Lisp deletePreviousChar(Lisp args, LispError *e, LispContext ctx) {
bufferDelChar();
return lisp_null();
}
/**
* @brief Lisp function to move cursor up by one screen
* @details Scrolls up one full screen height, moving cursor to top of visible area.
* @param args Lisp arguments (unused)
* @param e Error pointer for Lisp error handling
* @param ctx Lisp context
* @return lisp_null()
* @note Updates global editor state E
* @see editorMoveCursor()
*/
Lisp editorMoveCursorPageUp(Lisp args, LispError *e, LispContext ctx) {
EditorPane *active = splitScreenGetActivePane();
active->cursor_y = active->row_offset;
int times = E.screenrows;
while (--times) {
editorMoveCursor(ARROW_UP);
}
return lisp_null();
}
/**
* @brief Lisp function to move cursor down by one screen
* @details Scrolls down one full screen height, moving cursor to bottom of visible area.
* @param args Lisp arguments (unused)
* @param e Error pointer for Lisp error handling
* @param ctx Lisp context
* @return lisp_null()
* @note Updates global editor state E
* @see editorMoveCursor()
*/
Lisp editorMoveCursorPageDown(Lisp args, LispError *e, LispContext ctx) {
EditorPane *active = splitScreenGetActivePane();
struct buffer_t *buffer = bufferFindById(active->buffer_id);
active->cursor_y = active->row_offset + E.screenrows - 1;
if (active->cursor_y > buffer->numrows) {
active->cursor_y = buffer->numrows;
}
int times = E.screenrows;
while (--times) {
editorMoveCursor(ARROW_DOWN);
}
return lisp_null();
}
/**
* @brief Lisp function to open a file
* @details Prompts user for filename and opens the file for editing.
* @param args Lisp arguments (unused)
* @param e Error pointer for Lisp error handling
* @param ctx Lisp context
* @return lisp_null()
* @note Updates global editor state E
* @see editorOpen()
* @see editorPrompt()
*/
Lisp editorOpenFile(Lisp args, LispError *e, LispContext ctx) {
char *filename = editorPrompt("Open : %s", getenv("PWD"), 1);
if (filename){
// editorOpen(filename);
EditorPane *active = splitScreenGetActivePane();
active->buffer_id = bufferCreate(filename);
}
free(filename);
return lisp_null();
}
/**
* @brief Lisp function to insert a character
* @details Extracts a character from Lisp string argument and inserts it at cursor.
* @param args Lisp list with one string argument
* @param e Error pointer for Lisp error handling
* @param ctx Lisp context
* @return lisp_null()
* @note Uses first character of the string argument
*/
Lisp editorPrintC(Lisp args, LispError *e, LispContext ctx) {
char c = lisp_string(lisp_car(args))[0];
bufferInsertChar(c);
return lisp_null();
}
/**
* @brief Lisp function to load and execute a package
* @details Loads a Lisp package from the user's packages directory
* (~/.beluga/packages/<package_name>/init.lisp) and evaluates it.
* @param args Lisp list with one argument: package name (string)
* @param e Error pointer for Lisp error handling
* @param ctx Lisp context
* @return lisp_null()
* @note Package files must be valid Lisp code
* @note Package directory defaults to ~/.beluga/packages/
*/
Lisp addPackage(Lisp args, LispError *e, LispContext ctx) {
const char *package_name = lisp_string(lisp_car(args));
fprintf(stderr, "%s\n", package_name);
char *package_dir = (char *)calloc(256, sizeof(char));
FILE *fd_package = NULL;
strcat(package_dir, getenv("HOME"));
strcat(package_dir, "/.beluga/packages/");
strcat(package_dir, package_name);
strcat(package_dir, "/init.lisp");
fprintf(stderr, "%s\n", package_dir);
fd_package = fopen(package_dir, "r");
lisp_eval(lisp_read_file(fd_package, &E.ctx_error, E.ctx), &E.ctx_error,
E.ctx);
fclose(fd_package);
free(package_dir);
return lisp_null();
}
/**
* @brief Lisp function to delete the current row
* @details Removes the line at the current cursor position.
* @param args Lisp arguments (unused)
* @param e Error pointer for Lisp error handling
* @param ctx Lisp context
* @return lisp_null()
* @note Updates global editor state E
* @see editorDelRow()
*/
Lisp editorDelRow_L(Lisp args, LispError *e, LispContext ctx) {
EditorPane *active = splitScreenGetActivePane();
struct buffer_t *buffer = bufferFindById(active->buffer_id);
bufferDelRow(buffer, active->cursor_y);
return lisp_null();
}
Lisp editorSwitchNextBuffer(Lisp args, LispError *e, LispContext ctx) {
fprintf(stderr, "switch buffer\n");
if (E.number_of_buffer > 0) {
EditorPane *active = splitScreenGetActivePane();
int next_idx = (active->buffer_id + 1) % E.number_of_buffer;
active->buffer_id = next_idx;
}
return lisp_null();
}
Lisp editorSwitchNextPane(Lisp args, LispError *e, LispContext ctx) {
splitScreenSwitchPane();
return lisp_null();
}
Lisp editorUnifiedPanes(Lisp args, LispError *e, LispContext ctx) {
if (E.layout.num_panes - 1) {
splitScreenUnify();
}
return lisp_null();
}
/**
* @brief Lisp function to search for text
* @details Wrapper around editorFind() for use in Lisp keybindings.
* @param args Lisp arguments (unused)
* @param e Error pointer for Lisp error handling
* @param ctx Lisp context
* @return lisp_null()
* @see editorFind()
*/
Lisp bufferFind_L(Lisp args, LispError *e, LispContext ctx) {
fprintf(stderr, "LispFind\n");
EditorPane *active = splitScreenGetActivePane();
struct buffer_t *buffer = bufferFindById(active->buffer_id);
bufferFind(buffer);
return lisp_null();
}
/**
* @brief Lisp function to read character at cursor
* @details Returns the character at the current cursor position as a Lisp character.
* Returns 'a' if at end of line.
* @param args Lisp arguments (unused)
* @param e Error pointer for Lisp error handling
* @param ctx Lisp context
* @return Lisp character object representing the character at cursor
*/
Lisp editorReadChar_L(Lisp args, LispError *e, LispContext ctx) {
Lisp returned_char;
EditorPane *active = splitScreenGetActivePane();
struct buffer_t *buffer = bufferFindById(active->buffer_id);
if (buffer->row[active->cursor_y].render[active->cursor_x] == 0) {
returned_char = lisp_make_char('a');
} else {
returned_char = lisp_make_char(buffer->row[active->cursor_y].render[active->cursor_x]);
}
return returned_char;
}
/**
* @brief Lisp function to set the current prefix mode
* @details Changes the editor's prefix state to a named prefix, affecting which
* keybindings are active. Updates status message to show active prefix.
* @param args Lisp list with one argument: prefix name (string)
* @param e Error pointer for Lisp error handling
* @param ctx Lisp context
* @return lisp_null()
* @note Updates global editor state E (prefix_state)
* @see find_prefix()
*/
Lisp editorSetPrefix(Lisp args, LispError *e, LispContext ctx) {
/*
* Set the prefix state of editor to the prefix in argument
*/
const char *prefix_name = lisp_string(lisp_car(args));
struct prefix_t prefix = find_prefix(prefix_name);
E.prefix_state = prefix.prefix_id;
editorSetStatusMessage("prefix %s", prefix.prefix_name);
fprintf(stderr, "%s set\n", prefix_name);
return lisp_null();
}
/**
* @brief Lisp function to define a new prefix modifier
* @details Registers a named prefix modifier that can be used with keybindings
* to create context-aware command sequences (e.g., Ctrl-X as a prefix).
* @param args Lisp list with one argument: prefix name (string, max 64 chars)
* @param e Error pointer for Lisp error handling
* @param ctx Lisp context
* @return lisp_null()
* @note Updates global editor state E (prefix array)
*/
Lisp editorPrefix(Lisp args, LispError *e, LispContext ctx) {
E.prefix = (struct prefix_t *)realloc(E.prefix, (++(E.number_of_prefix) + 1) *
sizeof(struct prefix_t));
E.prefix[E.number_of_prefix].prefix_id = E.number_of_prefix;
strncpy(E.prefix[E.number_of_prefix].prefix_name, lisp_string(lisp_car(args)),
64);
return lisp_null();
}
+82 -44
View File
@@ -1,44 +1,82 @@
#include "../include/editor_op.h" #include <stdarg.h>
#include "../include/row_op.h"
#include "../include/editor_op.h"
void editorInsertChar(struct editorConfig *E, int c) { #include "../include/row_op.h"
if (E->cursor_y == E->numrows) { #include "include/buffer.h"
editorInsertRow(E, E->numrows, "", 0); #include "include/data.h"
} #include "include/split_screen.h"
editorRowInsertChar(E, &E->row[E->cursor_y], E->cursor_x, c);
E->cursor_x++;
} extern struct editorConfig E;
void editorInsertNewLine(struct editorConfig *E) {
erow *row; /**
if (!E->cursor_x) { * @brief Sets a temporary status message for display
editorInsertRow(E, E->cursor_y, "", 0); * @details Formats and stores a message that will be displayed in the message
} else { * bar for 5 seconds. Uses printf-style variable argument formatting.
row = &E->row[E->cursor_y]; * @param fmt Printf-style format string
editorInsertRow(E, E->cursor_y + 1, &row->chars[E->cursor_x], * @param ... Variable arguments for format string
row->size - E->cursor_x); * @note Updates global editor state E (status_msg, status_msg_time)
row = &E->row[E->cursor_y]; * @see editorDrawMessageBar()
row->size = E->cursor_x; */
row->chars[row->size] = '\0'; void editorSetStatusMessage(const char *fmt, ...) {
editorUpdateRow(row); va_list ap;
} va_start(ap, fmt);
++E->cursor_y; vsnprintf(E.status_msg, sizeof(E.status_msg), fmt, ap);
E->cursor_x = 0; va_end(ap);
} E.status_msg_time = time(NULL);
}
void editorDelChar(struct editorConfig *E) {
erow *row;
if (E->cursor_y == E->numrows || !(E->cursor_x || E->cursor_y)) { void bufferInsertChar(int c) {
return; EditorPane *active = splitScreenGetActivePane();
} struct buffer_t *buf = bufferFindById(active->buffer_id);
row = &E->row[E->cursor_y]; if (active->cursor_y == buf->numrows) {
if (E->cursor_x > 0) { bufferInsertRow(buf, buf->numrows, "", 0);
editorRowDelchar(E, row, E->cursor_x - 1); }
--E->cursor_x; bufferRowInsertChar(buf, &buf->row[active->cursor_y], active->cursor_x, c);
} else { active->cursor_x++;
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); void bufferInsertNewLine() {
--E->cursor_y; /*
} * Add new line and place the cursor at the beginning of it
} */
fprintf(stderr, "Inserting new line\n");
EditorPane *active = splitScreenGetActivePane();
struct buffer_t *buf = bufferFindById(active->buffer_id);
frow *row;
if (!active->cursor_x) {
bufferInsertRow(buf, active->cursor_y, "", 0);
} else {
row = &buf->row[active->cursor_y];
bufferInsertRow(buf, active->cursor_y + 1, &row->chars[active->cursor_x],
row->size - active->cursor_x);
row = &buf->row[active->cursor_y];
row->size = active->cursor_x;
row->chars[row->size] = '\0';
bufferUpdatfrow(row);
}
++active->cursor_y;
active->cursor_x = 0;
fprintf(stderr, "Insert new line done\n");
}
void bufferDelChar() {
frow *row;
EditorPane *active = splitScreenGetActivePane();
struct buffer_t *buf = bufferFindById(active->buffer_id);
if (active->cursor_y == buf->numrows || !(active->cursor_x || active->cursor_y)) {
return;
}
row = &buf->row[active->cursor_y];
if (active->cursor_x > 0) {
bufferRowDelchar(buf, row, active->cursor_x - 1);
--active->cursor_x;
} else {
active->cursor_x = buf->row[active->cursor_y - 1].size;
bufferRowAppendString(buf, &buf->row[active->cursor_y - 1], row->chars, row->size);
bufferDelRow(buf, active->cursor_y);
--active->cursor_y;
}
}
+251 -91
View File
@@ -1,91 +1,251 @@
#include "../include/file_io.h" /**
#include "../include/input.h" * @file file_io.c
#include "../include/output.h" * @brief File I/O operations module for the Beluga text editor
#include <fcntl.h> * @details Handles file loading, saving, searching, and buffer management.
#include <stdio.h> * Provides functionality for opening/closing files, persisting changes to disk,
#include <stdlib.h> * and searching for text patterns within the document.
#include <string.h> */
#include <time.h>
#include <unistd.h> #include "../include/file_io.h"
#include "../include/editor_op.h"
char *editorRowsToString(struct editorConfig *E, int *buffer_len) { #include "../include/input.h"
int tot_len = 0; #include "include/buffer.h"
int j; #include "include/data.h"
char *buf; #include "include/split_screen.h"
char *p; #include <fcntl.h>
#include <stdio.h>
for (j = 0; j < E->numrows; ++j) { #include <stdlib.h>
tot_len += E->row[j].size + 1; #include <string.h>
} #include <time.h>
*buffer_len = tot_len; #include <unistd.h>
buf = malloc(tot_len);
p = buf; // Windows compatibility for getline
for (j = 0; j < E->numrows; ++j) { #ifdef _WIN32
memcpy(p, E->row[j].chars, E->row[j].size); #include <io.h>
p += E->row[j].size;
*p = '\n'; ssize_t getline(char **lineptr, size_t *n, FILE *stream) {
p++; char *bufptr = NULL;
} char *p = bufptr;
size_t size;
return buf; int c;
}
if (lineptr == NULL) {
void editorOpen(struct editorConfig *E, char *filename) { return -1;
/** }
\function void editorOpen(struct editorConfig *E, char *filename) if (stream == NULL) {
\brief Open filename on editor stream. Throw fopen error if file doesn't return -1;
exist. }
*/ if (n == NULL) {
FILE *fp; return -1;
}
free(E->filename); bufptr = *lineptr;
E->filename = strdup(filename); size = *n;
fp = fopen(filename, "r"); c = fgetc(stream);
if (!fp) if (c == EOF) {
die("fopen"); return -1;
}
char *line = NULL;
size_t line_cap = 0; if (bufptr == NULL) {
ssize_t line_len; bufptr = malloc(128);
if (bufptr == NULL) {
while ((line_len = getline(&line, &line_cap, fp)) != -1) { return -1;
while (line_len > 0 && }
(line[line_len - 1] == '\n' || line[line_len - 1] == '\r')) { size = 128;
--line_len; }
} p = bufptr;
editorInsertRow(E, E->numrows, line, line_len); while(c != EOF) {
} if ((p - bufptr) > (size - 1)) {
free(line); size = size + 128;
fclose(fp); bufptr = realloc(bufptr, size);
E->dirty = 0; if (bufptr == NULL) {
} return -1;
}
void editorSave(struct editorConfig *E) { }
int len; *p++ = c;
char *buf; if (c == '\n') {
int fd; break;
if (E->filename == NULL) { }
E->filename = editorPrompt(E, "Save as: %s (ESC to cancel)"); c = fgetc(stream);
if (E->filename == NULL) { }
editorSetStatusMessage(E, "Save aborted");
return; *p++ = '\0';
} *lineptr = bufptr;
} *n = size;
buf = editorRowsToString(E, &len);
fd = open(E->filename, O_RDWR | O_CREAT, 0644); return p - bufptr - 1;
if (fd != -1) { }
if (ftruncate(fd, len) != -1) { #endif
if (write(fd, buf, len) == len) {
close(fd); extern char *strdup(const char *);
free(buf); extern ssize_t getline(char **restrict lineptr, size_t *restrict n,
E->dirty = 0; FILE *restrict stream);
editorSetStatusMessage(E, "%d bytes written to disk", len); extern int ftruncate(int fd, off_t length);
return; extern struct editorConfig E;
}
} /**
close(fd); * @brief Converts all editor rows to a single string buffer
} * @details Concatenates all row content into a single allocated buffer with
free(buf); * newlines between rows. Useful for file saving and buffer operations.
editorSetStatusMessage(E, "Can't save! I/O error: %s", strerror(errno)); * @param buffer_len Pointer to integer where total buffer length will be stored
} * @return Pointer to dynamically allocated buffer containing all row data.
* Rows are separated by newline characters.
* @note Caller is responsible for freeing the returned buffer
*/
char *bufferRowsToString(struct buffer_t *buf, int *buffer_len) {
int tot_len = 0;
int j;
char *buffer;
char *p;
for (j = 0; j < buf->numrows; ++j) {
tot_len += buf->row[j].size + 1;
}
*buffer_len = tot_len;
buffer = malloc(tot_len);
p = buffer;
for (j = 0; j < buf->numrows; ++j) {
memcpy(p, buf->row[j].chars, buf->row[j].size);
p += buf->row[j].size;
*p = '\n';
p++;
}
return buffer;
}
/**
* @brief Closes the current file and resets editor state
* @details Clears all rows, resets cursor position, scroll offsets, and file
* metadata. Does not prompt to save unsaved changes.
* @note Updates global editor state E
*/
void editorCloseFile(void) {
EditorPane *active = splitScreenGetActivePane();
struct buffer_t *buf = bufferFindById(active->buffer_id);
active->cursor_x = 0;
active->cursor_y = 0;
active->rx = 0;
active->row_offset = 0;
active->col_offset = 0;
for (int i = 0; i < buf->numrows; ++i) {
free(buf->row[i].chars);
free(buf->row[i].render);
}
buf->numrows = 0;
free(buf->row);
buf->row = NULL;
buf->dirty = 0;
free(buf->filename);
buf->filename = NULL;
E.status_msg[0] = '\0';
E.status_msg_time = 0;
}
/**
* @brief Opens a file for editing
* @details Loads file content into editor rows, one line per row. If another
* file is already open, it is closed first (without saving). File is opened in
* a+ (read/append) mode to allow both reading and modification.
* @param filename Path to the file to open (relative or absolute)
* @note Updates global editor state E
* @note Calls die() on file open failure
* @note Newline characters are stripped from loaded lines
* @see editorInsertRow()
*/
void editorOpen(struct buffer_t* buffer) {
FILE *fp;
fp = fopen(buffer->filename, "a+");
if (!fp)
die("fopen");
char *line = NULL;
size_t line_cap = 0;
ssize_t line_len;
while ((line_len = getline(&line, &line_cap, fp)) != -1) {
while (line_len > 0 &&
(line[line_len - 1] == '\n' || line[line_len - 1] == '\r')) {
--line_len;
}
bufferInsertRow(buffer, buffer->numrows, line, line_len);
free(line);
line = NULL;
}
free(line);
fclose(fp);
E.dirty = 0;
}
/**
* @brief Saves the current file to disk
* @details Prompts for filename if not set, converts all rows to a buffer,
* writes to disk using open/ftruncate/write, and updates dirty flag.
* Displays status messages on success or failure.
* @note Updates global editor state E (dirty flag)
* @note If no filename is set, prompts user via editorPrompt()
* @note Uses O_RDWR | O_CREAT with mode 0644
* @see editorRowsToString()
*/
void editorSave() {
EditorPane *active = splitScreenGetActivePane();
struct buffer_t *buffer = bufferFindById(active->buffer_id);
int len;
char *buf;
int fd;
if (buffer->filename == NULL) {
buffer->filename = editorPrompt("Save as: %s (ESC to cancel)", "", 1);
if (buffer->filename == NULL) {
editorSetStatusMessage("Save aborted");
return;
}
}
buf = bufferRowsToString(buffer, &len);
fd = open(buffer->filename, O_RDWR | O_CREAT, 0644);
if (fd != -1) {
if (ftruncate(fd, len) != -1) {
if (write(fd, buf, len) == len) {
close(fd);
free(buf);
E.dirty = 0;
editorSetStatusMessage("%d bytes written to disk", len);
return;
}
}
close(fd);
}
free(buf);
editorSetStatusMessage("Can't save! I/O error: %s", strerror(errno));
}
/**
* @brief Searches for a string in the document
* @details Prompts user for a search query, then searches forward from current
* cursor position. Updates cursor position to the first match found.
* @note Updates global editor state E (cursor position, row_offset)
* @note Search is case-sensitive and operates on rendered line content
* @note Searches begin from the line after current cursor position
* @see editorPrompt()
* @see editorRowRxToCx()
*/
void bufferFind(struct buffer_t *buf) {
fprintf(stderr, "searching\n");
char *query = editorPrompt("Search: %s (ESC to cancel)", "", 0);
EditorPane *active = splitScreenGetActivePane();
if (query == NULL)
return;
int i;
for (i = active->cursor_y + 1; i < buf->numrows; i++) {
frow *row = &buf->row[i];
char *match = strstr(row->render, query);
if (match) {
active->cursor_y = i;
active->cursor_x = bufferRowRxToCx(row, match - row->render);
buf->row_offset = buf->numrows;
break;
}
}
free(query);
}
+138 -27
View File
@@ -1,27 +1,138 @@
#include "../include/init.h" #include "../include/init.h"
#include "../include/builtins.h"
void initEditor(struct editorConfig *E) { #include "../include/color.h"
E->cursor_x = 0; #include "../include/data.h"
E->cursor_y = 0; #include "../include/terminal.h"
E->rx = 0; #include "include/split_screen.h"
E->row_offset = 0; #include <stdio.h>
E->col_offset = 0; #include <stdlib.h>
E->numrows = 0; #include <string.h>
E->row = NULL;
E->dirty = 0; #define LISP_IMPLEMENTATION
E->quit_times = QUIT_TIMES; #include "../include/lisp.h"
E->filename = NULL; #include "../include/lisp_lib.h"
E->status_msg[0] = '\0';
E->status_msg_time = 0; struct editorConfig;
if (getWindowSize(&E->screenrows, &E->screencols) == -1) {
die("getWindowSize"); void registerBuiltin(char *key_sequence, LispCFunc f) {
} lisp_env_define(E.ctx.p->env, lisp_make_symbol(key_sequence, E.ctx),
E->screenrows -= 2; lisp_make_func(f), E.ctx);
}
E->config = config_create();
config_parse_file(E->config, "../config/init.bl"); void initBuiltins() {
// Registering C functions as lisp macro
config_print_all(E->config); registerBuiltin("move-cursor", moveCursor);
registerBuiltin("map-key", mapKey);
init_function_registry(); registerBuiltin("editor-quit", editorQuit);
} registerBuiltin("editor-save", l_editorSave);
registerBuiltin("editor-insert-new-line", l_editorInsertNewLine);
registerBuiltin("move-cursor-beg-line", moveCursorBeginLine);
registerBuiltin("move-cursor-end-line", moveCursorEndLine);
registerBuiltin("editor-delete-previous-char", deletePreviousChar);
registerBuiltin("move-cursor-page-up", editorMoveCursorPageUp);
registerBuiltin("move-cursor-page-down", editorMoveCursorPageDown);
registerBuiltin("editor-open-file", editorOpenFile);
registerBuiltin("editor-insert-char", editorPrintC);
registerBuiltin("add-package", addPackage);
registerBuiltin("editor-del-row", editorDelRow_L);
registerBuiltin("buffer-find", bufferFind_L);
registerBuiltin("editor-read-char", editorReadChar_L);
registerBuiltin("add-prefix", editorPrefix);
registerBuiltin("editor-set-prefix", editorSetPrefix);
registerBuiltin("editor-insert-tab", l_editorInserTab);
registerBuiltin("editor-switch-next-buffer", editorSwitchNextBuffer);
registerBuiltin("editor-split-screen-vertical", l_editorSplitScreenVertical);
registerBuiltin("editor-switch-next-pane", editorSwitchNextPane);
registerBuiltin("editor-unify-panes", editorUnifiedPanes);
registerBuiltin("editor-create-terminal", editorCreateTerminal);
}
void initConfig() {
E.ctx = lisp_init();
E.env = lisp_env(E.ctx);
lisp_lib_load(E.ctx);
// Init builtins lisp functions
initBuiltins();
// Read config file
E.ctx_data = lisp_read_file(E.fd_init_file, &E.ctx_error, E.ctx);
if (E.ctx_error != LISP_ERROR_NONE) {
die("init failed");
}
lisp_eval(E.ctx_data, &E.ctx_error, E.ctx);
}
void init_theme() {
E.constantes.THEME = (char *)lisp_string(
lisp_eval(lisp_read("THEME", &E.ctx_error, E.ctx), &E.ctx_error, E.ctx));
if (strcmp(E.constantes.THEME, "dark") == 0) {
E.theme.BACKGROUND_COLOR = ANSI_BG_RGB(40, 44, 52);
E.theme.COLOR_KEYWORD = ANSI_FG_RGB(198, 120, 221);
E.theme.COLOR_TYPE = ANSI_FG_RGB(97, 175, 239);
E.theme.COLOR_STRING = ANSI_FG_RGB(152, 195, 121);
E.theme.COLOR_COMMENT = ANSI_FG_RGB(92, 99, 112);
E.theme.COLOR_NUMBER = ANSI_FG_RGB(209, 154, 102);
E.theme.COLOR_DEFAULT = ANSI_FG_RGB(171, 178, 191);
} else {
E.theme.BACKGROUND_COLOR = ANSI_BG_RGB(250, 251, 252);
E.theme.COLOR_KEYWORD = ANSI_FG_RGB(166, 38, 164);
E.theme.COLOR_TYPE = ANSI_FG_RGB(4, 90, 180);
E.theme.COLOR_STRING = ANSI_FG_RGB(80, 161, 79);
E.theme.COLOR_COMMENT = ANSI_FG_RGB(152, 152, 152);
E.theme.COLOR_NUMBER = ANSI_FG_RGB(206, 102, 54);
E.theme.COLOR_DEFAULT = ANSI_FG_RGB(86, 89, 90);
}
}
void initEditor() {
if (getWindowSize(&E.screenrows, &E.screencols) == -1) {
die("getWindowSize");
}
E.screenrows -= 2;
// Init graphics variables
splitScreenInit();
EditorPane *active = splitScreenGetActivePane();
active->cursor_x = 0;
active->cursor_y = 0;
active->rx = 0;
active->row_offset = 0;
active->col_offset = 0;
E.init_file_path = (char *)calloc(256, sizeof(char));
strcat(E.init_file_path, getenv("HOME"));
strcat(E.init_file_path, "/.beluga/config/init.lisp");
E.fd_init_file = fopen(E.init_file_path, "r");
// Status bar
E.status_msg[0] = '\0';
E.status_msg_time = 0;
// Key binds
E.number_of_keybinds = 0;
E.number_of_prefix = 0;
// General prefix is 0 (no prefix)
E.prefix = (struct prefix_t *)malloc(sizeof(struct prefix_t));
E.prefix[0].prefix_id = 0;
strncpy(E.prefix[0].prefix_name, "no-prefix", 64);
E.prefix_state = 0;
initConfig();
init_theme();
// To modify
E.constantes.TAB_LENGTH =
(int)lisp_eval(lisp_read("TAB-LENGTH", &E.ctx_error, E.ctx), &E.ctx_error,
E.ctx)
.val.int_val;
E.constantes.QUIT_TIMES =
(int)lisp_eval(lisp_read("QUIT-TIMES", &E.ctx_error, E.ctx), &E.ctx_error,
E.ctx)
.val.int_val;
E.quit_times_buffer = E.constantes.QUIT_TIMES;
}
+393 -224
View File
@@ -1,224 +1,393 @@
#include "../include/input.h" #include "../include/input.h"
#include <stdio.h> #include "../include/define.h"
#include <stdlib.h> #include "../include/editor_op.h"
#include <string.h> #include "../include/output.h"
#include "include/buffer.h"
/** #include "include/data.h"
* \fn char * editorPrompt(struct editorConfig *E, char *prompt) #include "include/split_screen.h"
* \brief Return user input in a prompt when enter is hit. */ #include <ctype.h>
#include <dirent.h>
char *key_to_string(int key) { #include <stdint.h>
static char key_str[32]; #include <stdio.h>
#include <stdlib.h>
char * tmp = malloc(10 * sizeof(char)); #include <string.h>
sprintf(tmp, "%d\n", key); #include <sys/stat.h>
log_string(tmp);
extern struct editorConfig E;
// First test enter key /**
* @file input.c
if (key == '\r') { * @brief Input handling module for the Beluga text editor
strcpy(key_str, "ENTER"); * @details Manages user input processing, key bindings, cursor movement, and file path completion
} else if (key >= 1 && key <= 26) { // CTRL keys */
snprintf(key_str, sizeof(key_str), "CTRL-%c", 'a' + key - 1);
} else { /**
switch (key) { * @brief Returns the first file completion match for the given path
case ARROW_UP: * @details Searches the directory containing the given path prefix and returns
strcpy(key_str, "ARROW-UP"); * the first file or directory entry that matches the filename prefix.
break; * Appends a trailing slash for directory entries.
case ARROW_DOWN: * @param path The file path to complete (can be relative or absolute)
strcpy(key_str, "ARROW-DOWN"); * @return Pointer to the completed file path (dynamically allocated), or NULL if:
break; * - path ends with '/' (already a directory)
case ARROW_LEFT: * - no matching entries found
strcpy(key_str, "ARROW-LEFT"); * - directory cannot be opened
break; * @note Caller is responsible for freeing the returned string
case ARROW_RIGHT: * @note Uses static buffer internally; may return stale pointers across calls
strcpy(key_str, "ARROW-RIGHT"); */
break; const char *file_completion(const char *path) {
case PAGE_UP: DIR *dir;
strcpy(key_str, "PAGE-UP"); struct dirent *entry;
break; char directory[128];
case PAGE_DOWN: char predict[128];
strcpy(key_str, "PAGE-DOWN"); const char *last_slash;
break; int predict_len = 0;
case DEL_KEY: size_t dir_len;
strcpy(key_str, "DEL");
break; // path is a directory
case BACKSPACE: if (path[strlen(path) - 1] == '/') {
strcpy(key_str, "BACKSPACE"); fprintf(stderr, "[FILE COMP] is dir\n");
break; strncpy(directory, path, 128);
case '\r': }
strcpy(key_str, "ENTER");
break; // Find dir name
case '\x1b': last_slash = strrchr(path, '/');
strcpy(key_str, "ESCAPE"); if (last_slash) {
break; dir_len = last_slash - path + 1; // length of dir_path
case BEG_LINE: strncpy(directory, path, dir_len);
strcpy(key_str, "HOME"); predict_len = strlen(path) - dir_len;
break; strncpy(predict, last_slash + 1, predict_len);
case END_LINE: directory[dir_len] = '\0';
strcpy(key_str, "END"); predict[predict_len] = '\0';
break; fprintf(stderr, "%s %s\n", directory, predict);
default: } else {
// For regular characters fprintf(stderr, "[FILE COMP] dir not found\n");
if (isprint(key)) { return strdup(path);
snprintf(key_str, sizeof(key_str), "%c", key); }
} else {
snprintf(key_str, sizeof(key_str), "KEY-%d", key); dir = opendir(directory);
} if (!dir)
} return strdup(path);
}
return key_str; while ((entry = readdir(dir)) != NULL) {
} if (strncmp(entry->d_name, predict, predict_len) == 0) {
static char full_path[512];
char *editorPrompt(struct editorConfig *E, char *prompt) { snprintf(full_path, sizeof(full_path), "%s%s", directory, entry->d_name);
size_t buf_size = 128;
char *buf = malloc(buf_size); struct stat st;
size_t buf_len = 0; if (stat(full_path, &st) == 0 && S_ISDIR(st.st_mode)) {
int c = 0; strcat(full_path, "/"); // add slash for directories
buf[0] = '\0'; }
closedir(dir);
while (1) {
editorSetStatusMessage(E, prompt, buf); return strdup(full_path);
editorRefreshScreen(E); }
c = editorReadKey(); }
if (c == DEL_KEY || c == CTRL_KEY('h') || c == BACKSPACE) {
if (buf_len != 0) { // Cleanup when no more entries
buf[--buf_len] = '\0'; closedir(dir);
} dir = NULL;
} else if (c == ESCAPE) { free(entry);
fprintf(stderr, "escape"); fprintf(stderr, "[FILE COMP] no entries\n");
editorSetStatusMessage(E, ""); return strdup(path);
free(buf); }
return NULL;
} else if (c == '\r') { /**
if (buf_len != 0) { * @brief Displays an interactive prompt and returns user input
editorSetStatusMessage(E, ""); * @details Allows the user to enter text in a prompt with optional path completion
return buf; * via Tab key. Supports backspace, delete, and escape key handling. Dynamically
} * allocates memory for the input buffer.
} else if (!iscntrl(c) && c < 128) { * @param prompt The prompt message format string (printf-style)
if (buf_len == buf_size - 1) { * @param placeHolder Initial text to display in the input buffer
buf_size *= 2; * @param bPathMode If non-zero, enables Tab key file path completion
buf = realloc(buf, buf_size); * @return Pointer to the user-entered text (dynamically allocated), or NULL if:
} * - User pressed ESC to cancel
buf[buf_len++] = c; * - Input buffer is empty when Enter is pressed
buf[buf_len] = '\0'; * @note Caller is responsible for freeing the returned string
} * @note Uses editorReadKey() for input and editorRefreshScreen() for display
} */
} char *editorPrompt(char *prompt, char *placeHolder, char bPathMode) {
size_t buf_size = 128;
void editorMoveCursor(struct editorConfig *E, int key) { char *buf = malloc(buf_size);
erow *row = (E->cursor_y >= E->numrows) ? NULL : &E->row[E->cursor_y]; size_t buf_len = 0;
int row_len; int c = 0;
char *sequence = key_to_string(key); buf[0] = '\0';
switch (key) { strcpy(buf, placeHolder);
case ARROW_RIGHT: buf_len = strlen(placeHolder);
if (row && E->cursor_x < row->size) {
++E->cursor_x; while (1) {
} else if (row && E->cursor_x == row->size) { editorSetStatusMessage(prompt, buf);
E->cursor_y++; editorRefreshScreen();
E->cursor_x = 0; c = editorReadKey();
} if (c == DEL_KEY || c == CTRL_KEY('h') || c == BACKSPACE) {
break; if (buf_len != 0) {
case ARROW_DOWN: buf[--buf_len] = '\0';
if (E->cursor_y < E->numrows) { }
++E->cursor_y; } else if (c == ESCAPE) {
} editorSetStatusMessage("");
break; free(buf);
case ARROW_UP: return NULL;
if (E->cursor_y != 0) { } else if (c == '\r') {
--E->cursor_y; if (buf_len != 0) {
} editorSetStatusMessage("");
break; return buf;
case ARROW_LEFT: }
if (E->cursor_x != 0) { } else if (bPathMode && c == '\t') {
--E->cursor_x; char path[128];
} else if (E->cursor_y > 0) { char *pwd;
--E->cursor_y; if (buf[0] != '/') {
E->cursor_x = E->row[E->cursor_y].size; pwd = getenv("PWD");
} fprintf(stderr, "%s\n", pwd);
break; memcpy(path, pwd, strlen(pwd));
} path[strlen(pwd)] = '/';
strncat(path, buf, buf_len);
row = (E->cursor_y >= E->numrows) ? NULL : &E->row[E->cursor_y]; } else {
row_len = row ? row->size : 0; strcpy(path, buf);
if (E->cursor_x > row_len) { }
E->cursor_x = row_len; memset(buf, 0, 128);
} buf_len = 0;
} char * buf_complete = (char *) file_completion(path);
strcpy(buf, buf_complete);
key_sequence_t current_sequence = {0}; free(buf_complete);
buf_len = strlen(buf);
int handle_key_sequence(struct editorConfig *E, int key) { buf[buf_len] = '\0';
char *key_str = key_to_string(key);
} else if (!iscntrl(c) && c < 128) {
log_string(key_str); if (buf_len == buf_size - 1) {
buf_size *= 2;
// Add current key to sequence buf = realloc(buf, buf_size);
if (current_sequence.sequence_len > 0) { }
strcat(current_sequence.sequence, " "); buf[buf_len++] = c;
} buf[buf_len] = '\0';
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); * @brief Converts a key code to its string representation
if (command) { * @details Translates raw key codes (including special keys, control keys,
log_string("Command found\n"); * and regular characters) into human-readable string formats suitable for
// Found a complete binding - execute it * display and keybinding configuration.
execute_key_binding(E->config, current_sequence.sequence, E); * @param key The key code to convert
* @return Pointer to static buffer containing the string representation.
// Reset sequence * Examples: "ENTER", "ARROW-UP", "CTRL-a", "TAB", "DELETE", etc.
memset(&current_sequence, 0, sizeof(current_sequence)); * @note Returns pointer to static buffer; string is overwritten on next call
return 1; // Handled * @note Non-printable characters are formatted as "KEY-<number>"
} */
char *key_to_string(int key) {
// Check if this could be the start of a longer sequence static char key_str[32];
// (This is a simple check - you might want to make it more sophisticated)
int potential_match = 0; char tmp[10];
// You'd implement a function to check for partial matches here sprintf(tmp, "%d", key);
if (!potential_match) { // First test enter key
// No potential matches, reset sequence and handle as single key
memset(&current_sequence, 0, sizeof(current_sequence)); if (key == '\r') {
return 0; // Not handled strcpy(key_str, "ENTER");
} } else if (key == '\t') {
strcpy(key_str, "TAB");
return 1; // Waiting for more keys in sequence } else if (key >= 1 && key <= 26) { // CTRL keys
} snprintf(key_str, sizeof(key_str), "CTRL-%c", 'a' + key - 1);
} else {
int execute_key_binding(config_t *config, const char *key_combo, switch (key) {
void *context) { case ARROW_UP:
const char *command = config_get_key_mapping(config, key_combo); strcpy(key_str, "ARROW-UP");
if (!command) { break;
log_string("No mapping found for key combination: "); case ARROW_DOWN:
log_string(key_combo); strcpy(key_str, "ARROW-DOWN");
log_string("\n"); break;
return -1; case ARROW_LEFT:
} strcpy(key_str, "ARROW-LEFT");
break;
// Remove the '%' prefix if present case ARROW_RIGHT:
const char *func_name = command; strcpy(key_str, "ARROW-RIGHT");
if (command[0] == '%') { break;
func_name = command + 1; case PAGE_UP:
} strcpy(key_str, "PAGE-UP");
fprintf(stderr, "pagr up\n");
return execute_command(func_name, context); break;
} case PAGE_DOWN:
strcpy(key_str, "PAGE-DOWN");
void editorProcessKeypress(struct editorConfig *E) { break;
static int quit_times = QUIT_TIMES; case DEL_KEY:
int c = editorReadKey(); strcpy(key_str, "DEL");
if (E->config) { break;
if (handle_key_sequence(E, c)) { case BACKSPACE:
quit_times = QUIT_TIMES; strcpy(key_str, "BACKSPACE");
return; // Key was handled by config system break;
} case '\r':
} strcpy(key_str, "ENTER");
break;
editorInsertChar(E, c); case '\x1b':
// reset quit times strcpy(key_str, "ESCAPE");
E->quit_times = QUIT_TIMES; 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;
}
/**
* @brief Moves the cursor based on arrow key input
* @details Updates cursor position (E.cursor_x, E.cursor_y) based on the given
* key direction. Handles line wrapping and boundary conditions. Prevents cursor
* from exceeding line lengths.
* @param key The arrow key code (ARROW_UP, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT)
* @return 1 if cursor movement was valid, 0 if cursor was constrained to line boundary
* @note Updates global editor state E
*/
int editorMoveCursor(int key) {
EditorPane *active = splitScreenGetActivePane();
struct buffer_t *buf = bufferFindById(active->buffer_id);
frow *row = (active->cursor_y + active->row_offset >= buf->numrows) ? NULL : &buf->row[active->cursor_y + active->row_offset];
int row_len;
int x = active->cursor_x + active->col_offset;
int y = active->cursor_y + active->row_offset;
switch (key) {
case ARROW_RIGHT:
if (row && x < row->size) {
active->cursor_x++;
} else if (row && y == row->size) {
active->cursor_y++;
active->cursor_x = 0;
}
break;
case ARROW_DOWN:
if (y < buf->numrows) {
active->cursor_y++;
}
break;
case ARROW_UP:
if (y != 0) {
--active->cursor_y;
}
break;
case ARROW_LEFT:
if (x != 0) {
--active->cursor_x;
} else if (y > 0) {
--active->cursor_y;
active->cursor_x = buf->row[y].size;
}
break;
}
return 1;
}
/**
* @brief Executes the command bound to a key sequence
* @details Searches the keybinding table for a matching key sequence and
* prefix state, then evaluates the associated Lisp command.
* @param key_sequence The string representation of the key sequence
* @return 1 if a matching keybinding was found and executed, 0 otherwise
* @note Updates global editor state E (prefix_state)
* @note Uses Lisp interpreter to evaluate bound commands
*/
int executeKeyBind(char *key_sequence) {
int i;
int previous_state = 0;
fprintf(stderr, "pressed %s\n", key_sequence);
for (i = 0; i < E.number_of_keybinds; ++i) {
if (!strcmp(key_sequence, E.key_binds[i].key_sequence)) {
if (E.prefix_state != E.key_binds[i].prefix_id) {
continue;
}
previous_state = E.prefix_state;
// 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);
if (E.prefix_state == previous_state)
E.prefix_state = 0;
return 1;
}
}
return 0;
}
/**
* @brief Handles terminal-specific key processing
* @details Processes key presses for terminal buffers, handling command input
* and execution.
* @param c The key code
* @return 1 if key was handled, 0 otherwise
*/
int handleTerminalKeypress(int c) {
EditorPane *active = splitScreenGetActivePane();
struct buffer_t *buf = bufferFindById(active->buffer_id);
if (buf == NULL || buf->type != TERMINAL_BUFF) {
return 0;
}
// Handle terminal-specific keys
if (c == '\r') { // Enter key - execute command
if (buf->terminal_command && strlen(buf->terminal_command) > 0) {
bufferExecuteTerminalCommand(buf->buffer_id, buf->terminal_command);
return 1;
}
} else if (c == BACKSPACE || c == CTRL_KEY('h')) { // Backspace
if (buf->terminal_command && strlen(buf->terminal_command) > 0) {
buf->terminal_command[strlen(buf->terminal_command) - 1] = '\0';
return 1;
}
} else if (c == CTRL_KEY('c')) { // Ctrl+C - clear command
if (buf->terminal_command) {
free(buf->terminal_command);
buf->terminal_command = NULL;
}
return 1;
} else if (!iscntrl(c) && c < 128) { // Regular character input
if (buf->terminal_command == NULL) {
buf->terminal_command = malloc(2);
buf->terminal_command[0] = c;
buf->terminal_command[1] = '\0';
} else {
int len = strlen(buf->terminal_command);
buf->terminal_command = realloc(buf->terminal_command, len + 2);
buf->terminal_command[len] = c;
buf->terminal_command[len + 1] = '\0';
}
return 1;
}
return 0;
}
/**
* @brief Processes a single keypress from the user
* @details Reads a key, checks if it matches any registered keybinding,
* and either executes the bound command or inserts the character. Resets
* the quit buffer counter on successful key processing.
* @note Updates global editor state E
* @note Calls editorReadKey() to get input and editorInsertChar() for unbound keys
*/
void editorProcessKeypress() {
int c = editorReadKey();
// First try terminal-specific handling
if (handleTerminalKeypress(c)) {
return;
}
if (executeKeyBind(key_to_string(c))) {
return;
}
bufferInsertChar(c);
E.quit_times_buffer = E.constantes.QUIT_TIMES;
}
+333 -135
View File
@@ -1,135 +1,333 @@
#include "../include/output.h" /**
#include <stdarg.h> * @file output.c
#include <stdio.h> * @brief Screen rendering and output module for the Beluga text editor
#include <string.h> * @details Handles all screen updates, cursor positioning, status bar
#include <time.h> * rendering, and display synchronization using ANSI escape sequences
*/
void editorDrawRows(struct editorConfig *E, struct abuf *ab) {
int y; #include "../include/output.h"
char welcome[80]; #include "../include/append_buffer.h"
int welcome_len; #include "../include/buffer.h"
int padding; #include "../include/data.h"
int len; #include "../include/define.h"
int file_row; #include "../include/row_op.h"
for (y = 0; y < E->screenrows; ++y) { #include "../include/split_screen.h"
file_row = y + E->row_offset; #include "../include/syntax_highlighter.h"
if (file_row >= E->numrows) { #include <stdio.h>
if (E->numrows == 0 && y == E->screenrows / 3) { #include <stdlib.h>
welcome_len = #include <string.h>
snprintf(welcome, sizeof(welcome), #include <time.h>
"Beluga text editor -- version %s", BELUGA_VERSION);
if (welcome_len > E->screencols) { extern struct editorConfig E;
welcome_len = E->screencols;
} /**
padding = (E->screencols - welcome_len) / 2; * @brief Renders a single pane with its buffer content
if (padding) { */
abAppend(ab, "~", 1); static void editorDrawPane(struct abuf *ab, EditorPane *pane) {
--padding; if (pane == NULL || pane->buffer_id < 0)
} return;
while (padding--) {
abAppend(ab, " ", 1); struct buffer_t *buf = bufferFindById(pane->buffer_id);
} if (buf == NULL)
abAppend(ab, welcome, welcome_len); return;
} else {
abAppend(ab, "~", 1); // Draw content for this pane
} for (int y = 0; y < pane->height; y++) {
} else { int file_row = y + pane->row_offset;
len = E->row[file_row].rsize - E->col_offset;
if (len < 0) // Position cursor at start of pane row
len = 0; char pos_buf[32];
if (len > E->screencols) int pos_len = snprintf(pos_buf, sizeof(pos_buf), "\x1b[%d;%dH",
len = E->screencols; pane->start_row + y + 1, pane->start_col + 1);
abAppend(ab, &E->row[file_row].render[E->col_offset], len); abAppend(ab, pos_buf, pos_len);
}
abAppend(ab, ERASE_END_LINE, 3); // Apply background color (6 bytes for RGB format)
abAppend(ab, "\r\n", 2); abAppend(ab, E.theme.BACKGROUND_COLOR, strlen(E.theme.BACKGROUND_COLOR));
}
} int chars_printed = 0;
void editorScroll(struct editorConfig *E) { if (file_row >= buf->numrows) {
E->rx = E->cursor_x; // Empty line - show tilde
if (E->cursor_y < E->numrows) { if (buf->numrows == 0 && y == pane->height / 3) {
E->rx = editorRowCxToRx(&E->row[E->cursor_y], E->cursor_x); char welcome[80];
} int welcome_len;
if (E->cursor_y < E->row_offset) { // Different welcome message for terminal buffers
E->row_offset = E->cursor_y; if (buf->type == TERMINAL_BUFF) {
} welcome_len = snprintf(welcome, sizeof(welcome), "Terminal %d", pane->buffer_id);
if (E->cursor_y >= E->row_offset + E->screenrows) { } else {
E->row_offset = E->cursor_y - E->screenrows + 1; welcome_len = snprintf(welcome, sizeof(welcome), "Buffer %d", pane->buffer_id);
} }
if (E->rx < E->col_offset) {
E->col_offset = E->rx; if (welcome_len > pane->width)
} welcome_len = pane->width;
if (E->rx >= E->col_offset + E->screencols) {
E->col_offset = E->rx - E->screencols + 1; int padding = (pane->width - welcome_len) / 2;
} if (padding) {
} abAppend(ab, "~", 1);
chars_printed++;
void editorDrawStatusBar(struct editorConfig *E, struct abuf *ab) { padding--;
int len, render_len; }
char status[80], render_status[80]; while (padding-- && chars_printed < pane->width) {
abAppend(ab, " ", 1);
abAppend(ab, "\x1b[7m", 4); // inverting colors chars_printed++;
len = snprintf(status, sizeof(status), "%.20s - %d lines%s", }
E->filename ? E->filename : "[No Name]", E->numrows, abAppend(ab, welcome, welcome_len);
E->dirty ? "*" : ""); chars_printed += welcome_len;
render_len = snprintf(render_status, sizeof(render_status), "%d/%d", } else {
E->cursor_y + 1, E->numrows); abAppend(ab, "~", 1);
if (len > E->screencols) { chars_printed++;
len = E->screencols; }
} } else {
abAppend(ab, status, len); // Render line with syntax highlighting, constrain to pane width
while (len < E->screencols) { int start_offset = pane->col_offset;
if (E->screencols - len == render_len) { int visible_len = buf->row[file_row].rsize - start_offset;
abAppend(ab, render_status, render_len); if (visible_len < 0)
break; visible_len = 0;
} else { if (visible_len > pane->width)
abAppend(ab, " ", 1); visible_len = pane->width;
++len;
} if (buf->type == TERMINAL_BUFF) {
} // Terminal buffer - show prompt for input line
abAppend(ab, "\x1b[m", 3); // normal text mode if (file_row == buf->numrows - 1) {
abAppend(ab, "\r\n", 2); char prompt[16];
} int prompt_len = snprintf(prompt, sizeof(prompt), "$ ");
abAppend(ab, prompt, prompt_len);
void editorDrawMessageBar(struct editorConfig *E, struct abuf *ab) { chars_printed += prompt_len;
int msg_len = strlen(E->status_msg);
abAppend(ab, ERASE_END_LINE, 3); // Show the command being typed (if any)
if (msg_len > E->screencols) { if (buf->terminal_command != NULL) {
msg_len = E->screencols; int cmd_len = strlen(buf->terminal_command);
} if (cmd_len > pane->width - prompt_len) {
if (msg_len && time(NULL) - E->status_msg_time < 5) { cmd_len = pane->width - prompt_len;
abAppend(ab, E->status_msg, msg_len); }
} abAppend(ab, buf->terminal_command, cmd_len);
} chars_printed += cmd_len;
}
void editorRefreshScreen(struct editorConfig *E) { } else {
editorScroll(E); // Show terminal output
struct abuf ab = ABUF_INIT; abAppend(ab, &buf->row[file_row].render[start_offset], visible_len);
char buf[32]; chars_printed = visible_len;
}
abAppend(&ab, HIDE_CURSOR, 6); } else if (buf->filename && strlen(buf->filename) > 0 && buf->filename[strlen(buf->filename) - 1] == 'c') {
abAppend(&ab, CURSOR_TOP_LEFT, 3); int byte_len_to_print;
editorDrawRows(E, &ab); char *highlighted = highlight_line(
editorDrawStatusBar(E, &ab); &buf->row[file_row].render[start_offset], &byte_len_to_print);
editorDrawMessageBar(E, &ab);
// Print only up to pane width
snprintf(buf, sizeof(buf), "\x1b[%d;%dH", (E->cursor_y - E->row_offset) + 1,
(E->rx - E->col_offset) + 1); abAppend(ab, highlighted, byte_len_to_print);
abAppend(&ab, buf, strlen(buf)); free(highlighted);
} else {
abAppend(&ab, SHOW_CURSOR, 6); abAppend(ab, &buf->row[file_row].render[start_offset], buf->row[file_row].rsize);
}
write(STDOUT_FILENO, ab.b, ab.len);
abFree(&ab); chars_printed = visible_len;
} }
void editorSetStatusMessage(struct editorConfig *E, const char *fmt, ...) { // Fill remaining space with background color to pane width
va_list ap; abAppend(ab, E.theme.BACKGROUND_COLOR, strlen(E.theme.BACKGROUND_COLOR));
va_start(ap, fmt); while (chars_printed < pane->width) {
vsnprintf(E->status_msg, sizeof(E->status_msg), fmt, ap); abAppend(ab, " ", 1);
va_end(ap); chars_printed++;
E->status_msg_time = time(NULL); }
}
// Restore background color at end of line
}
}
/**
* @brief Renders all panes based on current split configuration
*/
static void editorDrawAllPanes(struct abuf *ab) {
ScreenLayout *layout = splitScreenGetLayout();
if (layout->num_panes == 1) {
// Single pane fullscreen
editorDrawPane(ab, &layout->panes[0]);
} else if (layout->num_panes == 2) {
// Draw both panes
for (int i = 0; i < 2; i++) {
editorDrawPane(ab, &layout->panes[i]);
// Draw pane border/divider if not the last pane
if (layout->mode == SPLIT_VERTICAL && i == 0) {
// Draw vertical divider
int divider_col = layout->panes[0].width;
for (int y = 0; y < layout->panes[0].height; y++) {
char pos_buf[32];
snprintf(pos_buf, sizeof(pos_buf), "\x1b[%d;%dH", y + 1, divider_col);
abAppend(ab, pos_buf, strlen(pos_buf));
abAppend(ab, "\x1b[1m|\x1b[0m", 9); // Bold pipe divider
}
} else if (layout->mode == SPLIT_HORIZONTAL && i == 0) {
// Draw horizontal divider
int divider_row = layout->panes[0].height;
char pos_buf[32];
snprintf(pos_buf, sizeof(pos_buf), "\x1b[%d;%dH", divider_row + 1, 1);
abAppend(ab, pos_buf, strlen(pos_buf));
for (int x = 0; x < E.screencols; x++) {
abAppend(ab, "\x1b[1m-\x1b[0m", 9); // Bold dash divider
}
}
}
}
}
/**
* @brief Updates scroll offsets to keep cursor visible on screen
* @details Adjusts E.row_offset and E.col_offset to ensure the cursor remains
* within the visible viewport. Also updates E.rx (rendered x-coordinate).
* @note Updates global editor state E
* @see editorRowCxToRx()
*/
void editorScroll() {
EditorPane *active = splitScreenGetActivePane();
struct buffer_t *buf = bufferFindById(active->buffer_id);
active->rx = active->cursor_x;
fprintf(stderr, "%d %d / %d %d\n", active->cursor_x, active->col_offset,
active->cursor_y, active->row_offset);
if (active->cursor_x < 0) {
active->cursor_x = 0;
active->col_offset = active->col_offset == 0 ? 0 : --active->col_offset;
}
if (active->cursor_y < 0) {
active->cursor_y = 0;
active->row_offset = active->row_offset == 0 ? 0 : --active->row_offset;
}
if (active->cursor_x == active->width) {
active->cursor_x--;
active->col_offset++;
}
if (active->cursor_y == active->height) {
active->cursor_y--;
active->row_offset++;
}
}
/**
* @brief Renders the status bar at the bottom of the screen
* @details Displays filename, line count, dirty flag, and current cursor
* position in an inverted color bar. Right-aligns the cursor position
* indicator.
* @param ab Pointer to append buffer structure for accumulating output
* @note Uses ANSI escape codes for color inversion
*/
void editorDrawStatusBar(struct abuf *ab) {
int len, render_len;
char status[80], render_status[80];
EditorPane *active = splitScreenGetActivePane();
struct buffer_t *buf = bufferFindById(active->buffer_id);
abAppend(ab, "\x1b[7m", 4); // inverting colors
const char *mode_str = "";
ScreenLayout *layout = splitScreenGetLayout();
if (layout->mode == SPLIT_VERTICAL)
mode_str = " [V-SPLIT]";
else if (layout->mode == SPLIT_HORIZONTAL)
mode_str = " [H-SPLIT]";
// Build buffer status showing all buffers with dirty indicators
char buf_status[200] = "";
int offset = 0;
for (int i = 0; i < E.number_of_buffer; i++) {
struct buffer_t *b = &E.buffers[i];
char marker = (b->buffer_id == active->buffer_id) ? '>' : ' ';
char dirty_marker = b->dirty ? '*' : ' ';
offset += snprintf(&buf_status[offset], sizeof(buf_status) - offset,
"%c%d:%s%c ", marker, b->buffer_id,
b->filename ? b->filename : "[No Name]", dirty_marker);
}
len = snprintf(status, sizeof(status), "%s%s", buf_status, mode_str);
render_len = snprintf(render_status, sizeof(render_status), "%d/%d",
active->cursor_y + 1, buf->numrows);
if (len > E.screencols) {
len = E.screencols;
}
abAppend(ab, status, len);
while (len < E.screencols) {
if (E.screencols - len == render_len) {
abAppend(ab, render_status, render_len);
break;
} else {
abAppend(ab, " ", 1);
++len;
}
}
abAppend(ab, "\x1b[m", 3); // normal text mode
abAppend(ab, "\r\n", 2);
}
/**
* @brief Renders the message bar below the status bar
* @details Displays temporary status messages for a limited time (5 seconds).
* Only displays message if within time window and within screen width.
* @param ab Pointer to append buffer structure for accumulating output
* @note Messages are set by editorSetStatusMessage()
*/
void editorDrawMessageBar(struct abuf *ab) {
int msg_len = strlen(E.status_msg);
abAppend(ab, ERASE_END_LINE, 3);
if (msg_len > E.screencols) {
msg_len = E.screencols;
}
if (msg_len && time(NULL) - E.status_msg_time < 5) {
abAppend(ab, E.status_msg, msg_len);
}
}
/**
* @brief Performs complete screen refresh and buffer synchronization
* @details Clears screen, redraws all visible content (rows, status bar,
* message bar), positions cursor, and writes accumulated buffer to stdout. This
* is the main rendering function called each frame.
* @note Updates global editor state E (via editorScroll())
* @see editorDrawRows()
* @see editorDrawStatusBar()
* @see editorDrawMessageBar()
*/
void editorRefreshScreen() {
struct abuf ab = ABUF_INIT;
char buf[32];
abAppend(&ab, HIDE_CURSOR, 6);
abAppend(&ab, CURSOR_TOP_LEFT, 3);
abAppend(&ab, E.theme.BACKGROUND_COLOR,
strlen(E.theme.BACKGROUND_COLOR)); // RGB background is 12 bytes
// Draw all panes
editorScroll();
editorDrawAllPanes(&ab);
// Draw status bar and message bar
editorDrawStatusBar(&ab);
editorDrawMessageBar(&ab);
// Position cursor in active pane
EditorPane *active = splitScreenGetActivePane();
if (active != NULL) {
snprintf(buf, sizeof(buf), "\x1b[%d;%dH",
active->cursor_y + active->start_row + 1,
active->cursor_x + active->start_col + 1);
abAppend(&ab, buf, strlen(buf));
}
abAppend(&ab, SHOW_CURSOR, 6);
write(STDOUT_FILENO, ab.b, ab.len);
abFree(&ab);
}
+152 -139
View File
@@ -1,139 +1,152 @@
#include "../include/row_op.h" #include "../include/row_op.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
int editorRowCxToRx(erow *row, int cursor_x) { extern struct editorConfig E;
int render_x = 0;
int i; int bufferRowCxToRx(frow *row, int cursor_x) {
for (i = 0; i < cursor_x; ++i) { int render_x = 0;
if (row->chars[i] == '\t') { int i;
render_x += (TAB_LENGTH - 1) - (render_x % TAB_LENGTH); for (i = 0; i < cursor_x; ++i) {
} if (row->chars[i] == '\t') {
render_x++; render_x += (E.constantes.TAB_LENGTH - 1) - (render_x % E.constantes.TAB_LENGTH);
} }
return render_x; render_x++;
} }
return render_x;
/** }
* \fn editorUpdateRow(erow *row)
* \brief Copy content of \p row in \p row->render. int bufferRowRxToCx(frow *row, int rx) {
* */ int cur_rx = 0;
int cx;
void editorUpdateRow(erow *row) { for (cx = 0; cx < row->size; cx++) {
int i, i_render; if (row->chars[cx] == '\t')
int tabs = 0; cur_rx += (E.constantes.TAB_LENGTH - 1) - (cur_rx % E.constantes.TAB_LENGTH);
cur_rx++;
// counting number of tabs if (cur_rx > rx) return cx;
}
for (i = 0; i < row->size; ++i) { return cx;
tabs += }
(row->chars[i] == '\t'); /**< increment tabs of 1 if chars[i] is one. */
} /**
* \fn bufferUpdatfrow(frow *row)
free(row->render); * \brief Copy content of \p row in \p row->render.
row->render = malloc(row->size + tabs * (TAB_LENGTH - 1) + * */
1); /**< Tabs needs TAB_LENGTH chars so TAB_LENGTH - 1
more than the first already counted. */ void bufferUpdatfrow(frow *row) {
int i, i_render;
// end of counting int tabs = 0;
i_render = 0;
for (i = 0; i < row->size; ++i) { // counting number of tabs
if (row->chars[i] == '\t') {
row->render[i_render++] = ' '; for (i = 0; i < row->size; ++i) {
while (i_render % TAB_LENGTH) { tabs +=
row->render[i_render++] = (row->chars[i] == '\t'); /**< increment tabs of 1 if chars[i] is one. */
' '; /**< Addind the right amount of spaces for tabs */ }
}
} else { free(row->render);
row->render[i_render++] = row->chars[i]; row->render = malloc(row->size + tabs * (E.constantes.TAB_LENGTH - 1) +
} 1); /**< Tabs needs E.constantes.TAB_LENGTH chars so E.constantes.TAB_LENGTH - 1
} more than the first already counted. */
row->render[i_render] = '\0'; // Don't forget the end of string character.
row->rsize = i_render; // end of counting
} i_render = 0;
for (i = 0; i < row->size; ++i) {
void editorInsertRow(struct editorConfig *E, int at, char *s, size_t len) { if (row->chars[i] == '\t') {
if (at < 0 || at > E->numrows) { row->render[i_render++] = ' ';
return; while (i_render % E.constantes.TAB_LENGTH) {
} row->render[i_render++] =
' '; /**< Addind the right amount of spaces for tabs */
E->row = realloc(E->row, sizeof(erow) * (E->numrows + 1)); }
memmove(&E->row[at + 1], &E->row[at], sizeof(erow) * (E->numrows - at)); } else {
row->render[i_render++] = row->chars[i];
E->row[at].size = len; }
E->row[at].chars = malloc(len + 1); }
memcpy(E->row[at].chars, s, len); row->render[i_render] = '\0'; // Don't forget the end of string character.
E->row[at].chars[len] = '\0'; row->rsize = i_render;
}
E->row[at].rsize = 0;
E->row[at].render = NULL; void bufferInsertRow(struct buffer_t *buffer, int at, char *s, size_t len) {
editorUpdateRow(&E->row[at]);
if (at < 0 || at > buffer->numrows) {
++E->numrows;
++E->dirty; return;
} }
frow *tmp = (frow *)realloc(buffer->row, sizeof(frow) * (buffer->numrows + 1));
void editorFreeRow(erow *row) { if (!tmp) {
free(row->render); return;
free(row->chars); }
} buffer->row = tmp;
memmove(&buffer->row[at + 1], &buffer->row[at], sizeof(frow) * (buffer->numrows - at));
void editorDelRow(struct editorConfig *E, int at) {
if (at < 0 || at >= E->numrows) { buffer->row[at].size = len;
return; buffer->row[at].chars = malloc(len + 1);
} memcpy(buffer->row[at].chars, s, len);
editorFreeRow(&E->row[at]); buffer->row[at].chars[len] = '\0';
memmove(&E->row[at], &E->row[at + 1], sizeof(erow) * (E->numrows - at - 1));
--E->numrows; buffer->row[at].rsize = 0;
++E->dirty; buffer->row[at].render = NULL;
} bufferUpdatfrow(&buffer->row[at]);
/** ++buffer->numrows;
* \fn editorRowInsertChar(erow *row, int at, int c) ++buffer->dirty;
* \param at Index of where we want to insert the char */ }
void editorRowInsertChar(struct editorConfig *E, erow *row, int at, int c) { void bufferFrefrow(frow *row) {
if (at < 0 || at > row->size) { free(row->render);
at = row->size; free(row->chars);
} }
row->chars = realloc(row->chars, row->size + 2);
memmove(&row->chars[at + 1], &row->chars[at], row->size - at + 1); void bufferDelRow(struct buffer_t *buffer, int at) {
++row->size; if (at < 0 || at >= buffer->numrows) {
row->chars[at] = c; return;
editorUpdateRow(row); }
++E->dirty; bufferFrefrow(&buffer->row[at]);
} memmove(&buffer->row[at], &buffer->row[at + 1], sizeof(frow) * (buffer->numrows - at - 1));
--buffer->numrows;
void editorRowAppendString(struct editorConfig *E, erow *row, char *s, ++buffer->dirty;
size_t len) { }
row->chars = realloc(row->chars, row->size + len + 1);
memcpy(&row->chars[row->size], s, len); /**
row->size += len; * \fn bufferRowInsertChar(frow *row, int at, int c)
row->chars[row->size] = '\0'; * \param at Index of where we want to insert the char */
editorUpdateRow(row);
++E->dirty; void bufferRowInsertChar(struct buffer_t *buffer, frow *row, int at, int c) {
} if (buffer->state == READ_ONLY)
return;
/** if (at < 0 || at > row->size) {
* \fn editorRowDelChar(struct editorConfig *E, erow *erow, int at) at = row->size;
* \brief Delete the a char at the chosen position on the given row }
* \param at Index of the char to delete row->chars = realloc(row->chars, row->size + 2);
* \param row Row on operation is made */ memmove(&row->chars[at + 1], &row->chars[at], row->size - at + 1);
void editorRowDelchar(struct editorConfig *E, erow *row, int at) { ++row->size;
if (at < 0 || at >= row->size) { row->chars[at] = c;
return; bufferUpdatfrow(row);
} ++buffer->dirty;
memmove(&row->chars[at], &row->chars[at + 1], row->size - at); }
--row->size;
editorUpdateRow(row); void bufferRowAppendString(struct buffer_t *buffer, frow *row, char *s, size_t len) {
++E->dirty; row->chars = realloc(row->chars, row->size + len + 1);
} memcpy(&row->chars[row->size], s, len);
row->size += len;
void log_string(char * string){ row->chars[row->size] = '\0';
FILE * fd = fopen("tmp/log.txt", "a"); bufferUpdatfrow(row);
fprintf(fd, "%s\n", string); ++buffer->dirty;
fclose(fd); }
}
/**
* \fn bufferRowDelChar(struct bufferConfig *E, frow *frow, 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 bufferRowDelchar(struct buffer_t *buffer, frow *row, int at) {
if (at < 0 || at >= row->size) {
return;
}
memmove(&row->chars[at], &row->chars[at + 1], row->size - at);
--row->size;
bufferUpdatfrow(row);
++buffer->dirty;
}
+218
View File
@@ -0,0 +1,218 @@
/**
* @file split_screen.c
* @brief Split screen implementation
*/
#include "../include/split_screen.h"
#include "../include/buffer.h"
#include "../include/editor_op.h"
#include <stdlib.h>
#include <string.h>
extern struct editorConfig E;
/**
* @brief Initializes split screen system with single fullscreen pane
*/
void splitScreenInit(void) {
E.layout.mode = SPLIT_NONE;
E.layout.num_panes = 1;
E.layout.active_pane = 0;
E.layout.panes = malloc(sizeof(EditorPane) * 2);
// Initialize single fullscreen pane
E.layout.panes[0].buffer_id = -1; // No buffer for now
E.layout.panes[0].start_row = 0;
E.layout.panes[0].start_col = 0;
E.layout.panes[0].height = E.screenrows - 2; // Leave room for status bar
E.layout.panes[0].width = E.screencols;
E.layout.panes[0].cursor_x = 0;
E.layout.panes[0].cursor_y = 0;
E.layout.panes[0].row_offset = 0;
E.layout.panes[0].col_offset = 0;
E.layout.panes[0].is_active = 1;
}
/**
* @brief Splits screen vertically (left-right)
* @param buffer_id_left Buffer ID for left pane
* @param buffer_id_right Buffer ID for right pane
* @return 0 on success, -1 on failure
*/
int splitScreenVertical(int buffer_id_left, int buffer_id_right) {
// Verify both buffers exist
if (bufferFindById(buffer_id_left) == NULL ||
bufferFindById(buffer_id_right) == NULL) {
editorSetStatusMessage("Error: invalid buffer IDs");
return -1;
}
// Reallocate panes array
E.layout.panes = realloc(E.layout.panes, sizeof(EditorPane) * 2);
E.layout.mode = SPLIT_VERTICAL;
E.layout.num_panes = 2;
E.layout.active_pane = 0;
int half_width = E.screencols / 2;
int pane_height = E.screenrows - 2; // Leave room for status bar
// Left pane
E.layout.panes[0].buffer_id = buffer_id_left;
E.layout.panes[0].start_row = 0;
E.layout.panes[0].start_col = 0;
E.layout.panes[0].height = pane_height;
E.layout.panes[0].width = half_width;
E.layout.panes[0].cursor_x = 0;
E.layout.panes[0].cursor_y = 0;
E.layout.panes[0].row_offset = 0;
E.layout.panes[0].col_offset = 0;
E.layout.panes[0].is_active = 1;
// Right pane
E.layout.panes[1].buffer_id = buffer_id_right;
E.layout.panes[1].start_row = 0;
E.layout.panes[1].start_col = half_width;
E.layout.panes[1].height = pane_height;
E.layout.panes[1].width = E.screencols - half_width;
E.layout.panes[1].cursor_x = 0;
E.layout.panes[1].cursor_y = 0;
E.layout.panes[1].row_offset = 0;
E.layout.panes[1].col_offset = 0;
E.layout.panes[1].is_active = 0;
editorSetStatusMessage("Vertical split: %d | %d", buffer_id_left, buffer_id_right);
return 0;
}
/**
* @brief Splits screen horizontally (top-bottom)
* @param buffer_id_top Buffer ID for top pane
* @param buffer_id_bottom Buffer ID for bottom pane
* @return 0 on success, -1 on failure
*/
int splitScreenHorizontal(int buffer_id_top, int buffer_id_bottom) {
// Verify both buffers exist
if (bufferFindById(buffer_id_top) == NULL ||
bufferFindById(buffer_id_bottom) == NULL) {
editorSetStatusMessage("Error: invalid buffer IDs");
return -1;
}
// Reallocate panes array
E.layout.panes = realloc(E.layout.panes, sizeof(EditorPane) * 2);
E.layout.mode = SPLIT_HORIZONTAL;
E.layout.num_panes = 2;
E.layout.active_pane = 0;
int half_height = (E.screenrows - 2) / 2; // Account for status bar
// Top pane
E.layout.panes[0].buffer_id = buffer_id_top;
E.layout.panes[0].start_row = 0;
E.layout.panes[0].start_col = 0;
E.layout.panes[0].height = half_height;
E.layout.panes[0].width = E.screencols;
E.layout.panes[0].cursor_x = 0;
E.layout.panes[0].cursor_y = 0;
E.layout.panes[0].row_offset = 0;
E.layout.panes[0].col_offset = 0;
E.layout.panes[0].is_active = 1;
// Bottom pane
E.layout.panes[1].buffer_id = buffer_id_bottom;
E.layout.panes[1].start_row = half_height;
E.layout.panes[1].start_col = 0;
E.layout.panes[1].height = E.screenrows - 2 - half_height;
E.layout.panes[1].width = E.screencols;
E.layout.panes[1].cursor_x = 0;
E.layout.panes[1].cursor_y = 0;
E.layout.panes[1].row_offset = 0;
E.layout.panes[1].col_offset = 0;
E.layout.panes[1].is_active = 0;
editorSetStatusMessage("Horizontal split: %d / %d", buffer_id_top, buffer_id_bottom);
return 0;
}
/**
* @brief Returns to single buffer fullscreen
*/
void splitScreenUnify(void) {
E.layout.mode = SPLIT_NONE;
E.layout.num_panes = 1;
E.layout.active_pane = 0;
E.layout.panes[0].start_row = 0;
E.layout.panes[0].start_col = 0;
E.layout.panes[0].height = E.screenrows - 2;
E.layout.panes[0].width = E.screencols;
E.layout.panes[0].is_active = 1;
editorSetStatusMessage("Unified view");
}
/**
* @brief Switches active pane (focus moves between splits)
* @return 0 on success, -1 on failure
*/
int splitScreenSwitchPane(void) {
if (E.layout.num_panes < 2) {
editorSetStatusMessage("No split to switch");
return -1;
}
// Deactivate current pane
E.layout.panes[E.layout.active_pane].is_active = 0;
// Move to next pane
E.layout.active_pane = (E.layout.active_pane + 1) % E.layout.num_panes;
// Activate new pane
E.layout.panes[E.layout.active_pane].is_active = 1;
editorSetStatusMessage("Switched to pane %d (buffer %d)",
E.layout.active_pane,
E.layout.panes[E.layout.active_pane].buffer_id);
return 0;
}
/**
* @brief Updates the active pane's buffer
* @param buffer_id New buffer ID for active pane
* @return 0 on success, -1 on failure
*/
int splitScreenSetPaneBuffer(int buffer_id) {
if (bufferFindById(buffer_id) == NULL) {
editorSetStatusMessage("Error: invalid buffer ID");
return -1;
}
EditorPane *active = &E.layout.panes[E.layout.active_pane];
active->buffer_id = buffer_id;
active->cursor_x = 0;
active->cursor_y = 0;
active->row_offset = 0;
active->col_offset = 0;
editorSetStatusMessage("Pane %d now showing buffer %d",
E.layout.active_pane, buffer_id);
return 0;
}
/**
* @brief Gets current screen layout
* @return Pointer to current ScreenLayout
*/
ScreenLayout *splitScreenGetLayout(void) {
return &E.layout;
}
/**
* @brief Gets active pane
* @return Pointer to active EditorPane
*/
EditorPane *splitScreenGetActivePane(void) {
if (E.layout.num_panes == 0) return NULL;
return &E.layout.panes[E.layout.active_pane];
}
+197
View File
@@ -0,0 +1,197 @@
#include "../include/syntax_highlighter.h"
#include "../include/data.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern struct editorConfig E;
const char *c_keywords[] = {
"if", "else", "while", "for", "do", "switch",
"case", "break", "#include", "#define", "continue", "return",
"goto", "struct", "union", "enum", "typedef", "static",
"extern", "const", "volatile", "sizeof", "auto", "register",
"inline", "restrict", NULL};
// C types
const char *c_types[] = {"int", "char", "float", "double",
"void", "long", "short", "unsigned",
"signed", "bool", NULL};
// Check if character is alphanumeric or underscore
int is_word_char(char c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') || c == '_' || c == '#';
}
// Check if string is a keyword
int is_keyword(const char *word) {
for (int i = 0; c_keywords[i] != NULL; i++) {
if (strcmp(word, c_keywords[i]) == 0)
return 1;
}
return 0;
}
// Check if string is a type
int is_type(const char *word) {
for (int i = 0; c_types[i] != NULL; i++) {
if (strcmp(word, c_types[i]) == 0)
return 1;
}
return 0;
}
// Get color code for token type
const char *get_color(SyntaxTokenType type) {
switch (type) {
case TOKEN_KEYWORD:
return E.theme.COLOR_KEYWORD;
case TOKEN_DATATYPE:
return E.theme.COLOR_TYPE;
case TOKEN_STRING:
return E.theme.COLOR_STRING;
case TOKEN_COMMENT:
return E.theme.COLOR_COMMENT;
case TOKEN_NUMBER:
return E.theme.COLOR_NUMBER;
default:
return E.theme.COLOR_DEFAULT;
}
}
int comment_section = 0;
// Highlight a line of C code and return the highlighted string
// Returns a newly allocated string that must be freed by the caller
char *highlight_line(const char *line, int *length) {
char *result = malloc(1024); // Allocate space for result
int result_pos = 0;
int i = 0;
while (line[i] != '\0' && line[i] != '\n') {
// Skip whitespace
if (line[i] == ' ' || line[i] == '\t') {
result[result_pos++] = line[i++];
continue;
}
if (comment_section) {
result_pos += sprintf(&result[result_pos], "%s", E.theme.COLOR_COMMENT);
while (line[i] != '\0' && line[i] != '\n') {
if (line[i] == '*' && line[i + 1] == '/') {
comment_section = 0;
}
result[result_pos++] = line[i++];
}
result_pos += sprintf(&result[result_pos], "%s", COLOR_RESET);
continue;
}
// Handle line comments
if (line[i] == '/' && line[i + 1] == '/') {
result_pos += sprintf(&result[result_pos], "%s", E.theme.COLOR_COMMENT);
while (line[i] != '\0' && line[i] != '\n') {
result[result_pos++] = line[i++];
}
result_pos += sprintf(&result[result_pos], "%s", COLOR_RESET);
continue;
}
// Handle block comments
if ((line[i] == '/' && line[i + 1] == '*')) {
comment_section = 1;
result_pos += sprintf(&result[result_pos], "%s", E.theme.COLOR_COMMENT);
result[result_pos++] = line[i++];
result[result_pos++] = line[i++];
while (line[i] != '\0') {
if (line[i] == '*' && line[i + 1] == '/') {
result[result_pos++] = line[i++];
result[result_pos++] = line[i++];
break;
}
result[result_pos++] = line[i++];
}
result_pos += sprintf(&result[result_pos], "%s", COLOR_RESET);
continue;
}
// Handle strings
if (line[i] == '"') {
result_pos += sprintf(&result[result_pos], "%s\"", E.theme.COLOR_STRING);
i++;
while (line[i] != '\0' && line[i] != '"') {
if (line[i] == '\\') {
result[result_pos++] = line[i++];
result[result_pos++] = line[i++];
} else {
result[result_pos++] = line[i++];
}
}
if (line[i] == '"')
result[result_pos++] = line[i++];
result_pos += sprintf(&result[result_pos], "%s", COLOR_RESET);
continue;
}
// Handle character literals
if (line[i] == '\'') {
result_pos += sprintf(&result[result_pos], "%s'", E.theme.COLOR_STRING);
i++;
while (line[i] != '\0' && line[i] != '\'') {
if (line[i] == '\\') {
result[result_pos++] = line[i++];
result[result_pos++] = line[i++];
} else {
result[result_pos++] = line[i++];
}
}
if (line[i] == '\'')
result[result_pos++] = line[i++];
result_pos += sprintf(&result[result_pos], "%s", COLOR_RESET);
continue;
}
// Handle numbers
if (line[i] >= '0' && line[i] <= '9') {
result_pos += sprintf(&result[result_pos], "%s", E.theme.COLOR_NUMBER);
while (is_word_char(line[i]) || line[i] == '.') {
result[result_pos++] = line[i++];
}
result_pos += sprintf(&result[result_pos], "%s", COLOR_RESET);
continue;
}
// Handle identifiers and keywords
if (is_word_char(line[i])) {
int start = i;
while (is_word_char(line[i]))
i++;
char word[256];
strncpy(word, &line[start], i - start);
word[i - start] = '\0';
SyntaxTokenType type = TOKEN_DEFAULT;
if (is_keyword(word))
type = TOKEN_KEYWORD;
else if (is_type(word))
type = TOKEN_DATATYPE;
result_pos += sprintf(&result[result_pos], "%s%s%s", get_color(type),
word, COLOR_RESET);
continue;
}
// Handle operators and other characters
result_pos += sprintf(&result[result_pos], "%s", E.theme.COLOR_DEFAULT);
result[result_pos++] = line[i++];
result_pos += sprintf(&result[result_pos], "%s", COLOR_RESET);
}
result[result_pos] = '\0';
*length = result_pos + 1;
return result;
}
+556
View File
@@ -0,0 +1,556 @@
/* termiWin.c
*
* Copyright (C) 2017 Christian Visintin - christian.visintin1997@gmail.com
*
* This file is part of "termiWin: a termios porting for Windows"
*
* termiWin is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* termiWin is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with termiWin. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../include/termiWin.h"
#ifdef _WIN32
#ifdef __cplusplus > 201711L
#define TERMIWIN_MAYBE_UNUSED [[maybe_unused]]
#else
#ifdef __GNUC__
#define TERMIWIN_MAYBE_UNUSED __attribute__((unused))
#else
#define TERMIWIN_MAYBE_UNUSED
#endif
#endif
#include <fcntl.h>
#include <stdlib.h>
typedef struct COM {
HANDLE hComm;
int fd; //Actually it's completely useless
char port[128];
} COM;
DCB SerialParams = { 0 }; //Initializing DCB structure
struct COM com;
COMMTIMEOUTS timeouts = { 0 }; //Initializing COMMTIMEOUTS structure
//LOCAL functions
//nbyte 0->7
int getByte(tcflag_t flag, int nbyte, int nibble) {
int byte;
if (nibble == 1)
byte = (flag >> (8 * (nbyte)) & 0x0f);
else
byte = (flag >> (8 * (nbyte)) & 0xf0);
return byte;
}
//INPUT FUNCTIONS
enum{
i_IXOFF = 0x01,
i_IXON = 0x02,
i_IXOFF_IXON = 0x03,
i_PARMRK = 0x04,
i_PARMRK_IXOFF = 0x05,
i_PARMRK_IXON = 0x06,
i_PARMRK_IXON_IXOFF = 0x07
};
int getIXOptions(tcflag_t flag) {
int byte = getByte(flag, 1, 1);
return byte;
}
//LOCALOPT FUNCTIONS
enum{
l_NOECHO = 0x00,
l_ECHO = 0x01,
l_ECHO_ECHOE = 0x03,
l_ECHO_ECHOK = 0x05,
l_ECHO_ECHONL = 0x09,
l_ECHO_ECHOE_ECHOK = 0x07,
l_ECHO_ECHOE_ECHONL = 0x0b,
l_ECHO_ECHOE_ECHOK_ECHONL = 0x0f,
l_ECHO_ECHOK_ECHONL = 0x0d,
l_ECHOE = 0x02,
l_ECHOE_ECHOK = 0x06,
l_ECHOE_ECHONL = 0x0a,
l_ECHOE_ECHOK_ECHONL = 0x0e,
l_ECHOK = 0x04,
l_ECHOK_ECHONL = 0x0c,
l_ECHONL = 0x08
};
int getEchoOptions(tcflag_t flag) {
int byte = getByte(flag, 1, 1);
return byte;
}
enum{
l_ICANON = 0x10,
l_ICANON_ISIG = 0x50,
l_ICANON_IEXTEN = 0x30,
l_ICANON_NOFLSH = 0x90,
l_ICANON_ISIG_IEXTEN = 0x70,
l_ICANON_ISIG_NOFLSH = 0xd0,
l_ICANON_IEXTEN_NOFLSH = 0xb0,
l_ICANON_ISIG_IEXTEN_NOFLSH = 0xf0,
l_ISIG = 0x40,
l_ISIG_IEXTEN = 0x60,
l_ISIG_NOFLSH = 0xc0,
l_ISIG_IEXTEN_NOFLSH = 0xe0,
l_IEXTEN = 0x20,
l_IEXTEN_NOFLSH = 0xa0,
l_NOFLSH = 0x80,
};
int getLocalOptions(tcflag_t flag) {
int byte = getByte(flag, 1, 0);
return byte;
}
enum{
l_TOSTOP = 0x01
};
int getToStop(tcflag_t flag) {
int byte = getByte(flag, 1, 1);
return byte;
}
//CONTROLOPT FUNCTIONS
int getCharSet(tcflag_t flag) {
//FLAG IS MADE UP OF 8 BYTES, A FLAG IS MADE UP OF A NIBBLE -> 4 BITS, WE NEED TO EXTRACT THE SECOND NIBBLE (1st) FROM THE FIFTH BYTE (6th).
int byte = getByte(flag, 1, 1);
switch (byte) {
case 0X0:
return CS5;
break;
case 0X4:
return CS6;
break;
case 0X8:
return CS7;
break;
case 0Xc:
return CS8;
break;
default:
return CS8;
break;
}
}
enum{
c_ALL_ENABLED = 0xd0,
c_PAREVEN_CSTOPB = 0x50,
c_PAREVEN_NOCSTOPB = 0x40,
c_PARODD_NOCSTOPB = 0xc0,
c_NOPARENB_CSTOPB = 0x10,
c_ALL_DISABLED = 0x00,
};
int getControlOptions(tcflag_t flag) {
int byte = getByte(flag, 1, 0);
return byte;
}
//LIBFUNCTIONS
int tcgetattr(int fd, struct termios* TERMIWIN_MAYBE_UNUSED termios_p) {
if (fd != com.fd) return -1;
int TERMIWIN_MAYBE_UNUSED ret = 0;
ret = GetCommState(com.hComm, &SerialParams);
return 0;
}
int tcsetattr(int fd, int TERMIWIN_MAYBE_UNUSED optional_actions, const struct termios* termios_p) {
if (fd != com.fd) return -1;
int ret = 0;
//Store flags into local variables
tcflag_t iflag = termios_p->c_iflag;
tcflag_t lflag = termios_p->c_lflag;
tcflag_t cflag = termios_p->c_cflag;
tcflag_t TERMIWIN_MAYBE_UNUSED oflag = termios_p->c_oflag;
//iflag
int IX = getIXOptions(iflag);
if ((IX == i_IXOFF_IXON) || (IX == i_PARMRK_IXON_IXOFF)) {
SerialParams.fOutX = TRUE;
SerialParams.fInX = TRUE;
SerialParams.fTXContinueOnXoff = TRUE;
}
//lflag
int TERMIWIN_MAYBE_UNUSED EchoOpt = getEchoOptions(lflag);
int TERMIWIN_MAYBE_UNUSED l_opt = getLocalOptions(lflag);
int TERMIWIN_MAYBE_UNUSED tostop = getToStop(lflag);
//Missing parameters...
//cflags
int CharSet = getCharSet(cflag);
int c_opt = getControlOptions(cflag);
switch (CharSet) {
case CS5:
SerialParams.ByteSize = 5;
break;
case CS6:
SerialParams.ByteSize = 6;
break;
case CS7:
SerialParams.ByteSize = 7;
break;
case CS8:
SerialParams.ByteSize = 8;
break;
}
switch (c_opt) {
case c_ALL_ENABLED:
SerialParams.Parity = ODDPARITY;
SerialParams.StopBits = TWOSTOPBITS;
break;
case c_ALL_DISABLED:
SerialParams.Parity = NOPARITY;
SerialParams.StopBits = ONESTOPBIT;
break;
case c_PAREVEN_CSTOPB:
SerialParams.Parity = EVENPARITY;
SerialParams.StopBits = TWOSTOPBITS;
break;
case c_PAREVEN_NOCSTOPB:
SerialParams.Parity = EVENPARITY;
SerialParams.StopBits = ONESTOPBIT;
break;
case c_PARODD_NOCSTOPB:
SerialParams.Parity = ODDPARITY;
SerialParams.StopBits = ONESTOPBIT;
break;
case c_NOPARENB_CSTOPB:
SerialParams.Parity = NOPARITY;
SerialParams.StopBits = TWOSTOPBITS;
break;
}
//aflags
/*
int OP;
if(oflag == OPOST)
else ...
*/
//Missing parameters...
//special characters
if (termios_p->c_cc[VEOF] != 0) SerialParams.EofChar = (char)termios_p->c_cc[VEOF];
if (termios_p->c_cc[VINTR] != 0) SerialParams.EvtChar = (char)termios_p->c_cc[VINTR];
if (termios_p->c_cc[VMIN] == 1) { //Blocking
timeouts.ReadIntervalTimeout = 0; // in milliseconds
timeouts.ReadTotalTimeoutConstant = 0; // in milliseconds
timeouts.ReadTotalTimeoutMultiplier = 0; // in milliseconds
timeouts.WriteTotalTimeoutConstant = 0; // in milliseconds
timeouts.WriteTotalTimeoutMultiplier = 0; // in milliseconds
} else { //Non blocking
timeouts.ReadIntervalTimeout = termios_p->c_cc[VTIME] * 100; // in milliseconds
timeouts.ReadTotalTimeoutConstant = termios_p->c_cc[VTIME] * 100; // in milliseconds
timeouts.ReadTotalTimeoutMultiplier = termios_p->c_cc[VTIME] * 100; // in milliseconds
timeouts.WriteTotalTimeoutConstant = termios_p->c_cc[VTIME] * 100; // in milliseconds
timeouts.WriteTotalTimeoutMultiplier = termios_p->c_cc[VTIME] * 100; // in milliseconds
}
SetCommTimeouts(com.hComm, &timeouts);
//EOF
ret = SetCommState(com.hComm, &SerialParams);
if (ret != 0)
return 0;
else
return -1;
}
int tcsendbreak(int fd, int TERMIWIN_MAYBE_UNUSED duration) {
if (fd != com.fd) return -1;
int ret = 0;
ret = TransmitCommChar(com.hComm, '\x00');
if (ret != 0)
return 0;
else
return -1;
}
int tcdrain(int fd) {
if (fd != com.fd) return -1;
return FlushFileBuffers(com.hComm);
}
int tcflush(int fd, int queue_selector) {
if (fd != com.fd) return -1;
int rc = 0;
switch (queue_selector) {
case TCIFLUSH:
rc = PurgeComm(com.hComm, PURGE_RXCLEAR);
break;
case TCOFLUSH:
rc = PurgeComm(com.hComm, PURGE_TXCLEAR);
break;
case TCIOFLUSH:
rc = PurgeComm(com.hComm, PURGE_RXCLEAR);
rc *= PurgeComm(com.hComm, PURGE_TXCLEAR);
break;
default:
rc = 0;
break;
}
if (rc != 0)
return 0;
else
return -1;
}
int tcflow(int fd, int action) {
if (fd != com.fd) return -1;
int rc = 0;
switch (action) {
case TCOOFF:
rc = PurgeComm(com.hComm, PURGE_TXABORT);
break;
case TCOON:
rc = ClearCommBreak(com.hComm);
break;
case TCIOFF:
rc = PurgeComm(com.hComm, PURGE_RXABORT);
break;
case TCION:
rc = ClearCommBreak(com.hComm);
break;
default:
rc = 0;
break;
}
if (rc != 0)
return 0;
else
return -1;
}
void cfmakeraw(struct termios* TERMIWIN_MAYBE_UNUSED termios_p) {
SerialParams.ByteSize = 8;
SerialParams.StopBits = ONESTOPBIT;
SerialParams.Parity = NOPARITY;
}
speed_t cfgetispeed(const struct termios* TERMIWIN_MAYBE_UNUSED termios_p) {
return SerialParams.BaudRate;
}
speed_t cfgetospeed(const struct termios* TERMIWIN_MAYBE_UNUSED termios_p) {
return SerialParams.BaudRate;
}
int cfsetispeed(struct termios* TERMIWIN_MAYBE_UNUSED termios_p, speed_t speed) {
SerialParams.BaudRate = speed;
return 0;
}
int cfsetospeed(struct termios* TERMIWIN_MAYBE_UNUSED termios_p, speed_t speed) {
SerialParams.BaudRate = speed;
return 0;
}
int cfsetspeed(struct termios* TERMIWIN_MAYBE_UNUSED termios_p, speed_t speed) {
SerialParams.BaudRate = speed;
return 0;
}
ssize_t read_serial(int fd, void* buffer, size_t count) {
if (fd != com.fd) return -1;
int rc = 0;
int ret;
ret = ReadFile(com.hComm, buffer, count, &rc, NULL);
if (ret == 0)
return -1;
else
return rc;
}
ssize_t write_serial(int fd, const void* buffer, size_t count) {
if (fd != com.fd) return -1;
int rc = 0;
int ret;
ret = WriteFile(com.hComm, buffer, count, &rc, NULL);
if (ret == 0)
return -1;
else
return rc;
}
int open_serial(const char* portname, int opt) {
if (strlen(portname) < 4) return -1;
// Set to zero
memset(com.port, 0x00, 128);
//COMxx
size_t portSize = 0;
if (strlen(portname) > 4) {
portSize = sizeof(char) * strlen("\\\\.\\COM10") + 1;
#ifdef _MSC_VER
strncat_s(com.port, portSize, "\\\\.\\", strlen("\\\\.\\"));
#else
strncat(com.port, "\\\\.\\", strlen("\\\\.\\"));
#endif
}
//COMx
else {
portSize = sizeof(char) * 5;
}
#ifdef _MSC_VER
strncat_s(com.port, portSize, portname, 4);
#else
strncat(com.port, portname, 4);
#endif
com.port[portSize] = 0x00;
switch (opt) {
case O_RDWR:
com.hComm = CreateFile(com.port, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
break;
case O_RDONLY:
com.hComm = CreateFile(com.port, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
break;
case O_WRONLY:
com.hComm = CreateFile(com.port, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
break;
}
if (com.hComm == INVALID_HANDLE_VALUE) {
return -1;
}
com.fd = atoi(portname + 3); // COMx and COMxx
SerialParams.DCBlength = sizeof(SerialParams);
return com.fd;
}
int close_serial(int TERMIWIN_MAYBE_UNUSED fd) {
int ret = CloseHandle(com.hComm);
if (ret != 0)
return 0;
else
return -1;
}
int select_serial(int TERMIWIN_MAYBE_UNUSED nfds, fd_set* readfds, fd_set* TERMIWIN_MAYBE_UNUSED writefds, fd_set* TERMIWIN_MAYBE_UNUSED exceptfds, struct timeval* TERMIWIN_MAYBE_UNUSED timeout) {
SetCommMask(com.hComm, EV_RXCHAR);
DWORD dwEventMask;
if (WaitCommEvent(com.hComm, &dwEventMask, NULL) == 0) {
return -1; // Return -1 if failed
}
if (dwEventMask == EV_RXCHAR) {
return com.fd;
} else {
if (readfds) {
// Clear file descriptor if event is not RXCHAR
FD_CLR(com.fd, readfds);
}
}
// NOTE: write event not detectable!
// NOTE: no timeout
return 0; // No data
}
//Returns hComm from the COM structure
HANDLE getHandle() {
return com.hComm;
}
#endif
+163 -150
View File
@@ -1,150 +1,163 @@
#include "../include/terminal.h" #include "../include/terminal.h"
#include <stdio.h> #include "../include/data.h"
#include <stdlib.h>
#include <stdio.h>
void die(const char *s) {
write(STDOUT_FILENO, "\x1b[2J", 4); #ifdef _WIN32
write(STDOUT_FILENO, CURSOR_TOP_LEFT, 3); #include <windows.h>
#endif
perror(s);
exit(1); void die(const char *s) {
} write(STDOUT_FILENO, "\x1b[2J", 4);
write(STDOUT_FILENO, CURSOR_TOP_LEFT, 3);
void disableRawMode(struct editorConfig *E) { lisp_shutdown(E.ctx);
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &E->orig_termios) == -1) { perror(s);
free(E); exit(1);
die("tcsetattr"); }
}
} void disableRawMode() {
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &E.orig_termios) == -1) {
void enableRawMode(struct editorConfig *E) { die("tcsetattr");
if (tcgetattr(STDIN_FILENO, &E->orig_termios) == -1) { }
die("tcgetattr"); }
}
void enableRawMode() {
struct termios raw = E->orig_termios; if (tcgetattr(STDIN_FILENO, &E.orig_termios) == -1) {
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); die("tcgetattr");
raw.c_oflag &= ~(OPOST); }
raw.c_cflag |= (CS8);
raw.c_lflag &= ~(ECHO | ICANON | ISIG | IEXTEN); struct termios raw = E.orig_termios;
raw.c_cc[VMIN] = 0; raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
raw.c_cc[VTIME] = 1; raw.c_oflag &= ~(OPOST);
raw.c_cflag |= (CS8);
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) { raw.c_lflag &= ~(ECHO | ICANON | ISIG | IEXTEN);
die("tcsetattr"); raw.c_cc[VMIN] = 0;
} raw.c_cc[VTIME] = 1;
}
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) {
int editorReadKey() { die("tcgetattr");
int nread; }
char c; }
char seq[3];
while ((nread = read(STDIN_FILENO, &c, 1)) != 1) { int editorReadKey() {
if (nread == -1 && errno != EAGAIN) { int nread;
die("read"); char c;
} char seq[3];
} while ((nread = read(STDIN_FILENO, &c, 1)) != 1) {
if (nread == -1 && errno != EAGAIN) {
fprintf(stderr, "%X (%c)\n", c, c); die("read");
if (c == '\x1b') { }
if (read(STDIN_FILENO, &seq[0], 1) != 1 || }
read(STDIN_FILENO, &seq[1], 1) != 1) {
return '\x1b'; if (c == '\x1b') {
} if (read(STDIN_FILENO, &seq[0], 1) != 1 ||
if (seq[0] == '[') { read(STDIN_FILENO, &seq[1], 1) != 1) {
if (seq[1] >= '0' && seq[1] <= '9') { return '\x1b';
if (read(STDIN_FILENO, &seq[2], 1) != 1) { }
return '\x1b'; if (seq[0] == '[') {
} if (seq[1] >= '0' && seq[1] <= '9') {
if (seq[2] == '~') { if (read(STDIN_FILENO, &seq[2], 1) != 1) {
switch (seq[1]) { return '\x1b';
case '1': }
return BEG_LINE; if (seq[2] == '~') {
case '3': switch (seq[1]) {
return DEL_KEY; case '1':
case '4': return BEG_LINE;
return END_LINE; case '3':
case '5': return DEL_KEY;
return PAGE_UP; case '4':
case '6': return END_LINE;
return PAGE_DOWN; case '5':
case '7': return PAGE_UP;
return BEG_LINE; case '6':
case '8': return PAGE_DOWN;
return END_LINE; case '7':
} return BEG_LINE;
} case '8':
} else { return END_LINE;
}
switch (seq[1]) { }
case 'A': } else {
return ARROW_UP;
case 'B': switch (seq[1]) {
return ARROW_DOWN; case 'A':
case 'C': return ARROW_UP;
return ARROW_RIGHT; case 'B':
case 'D': return ARROW_DOWN;
return ARROW_LEFT; case 'C':
case 'H': return ARROW_RIGHT;
return BEG_LINE; case 'D':
case 'F': return ARROW_LEFT;
return END_LINE; case 'H':
} return BEG_LINE;
} case 'F':
} else if (seq[0] == 'O') { return END_LINE;
switch (seq[1]) { }
case 'H': }
return BEG_LINE; } else if (seq[0] == 'O') {
case 'F': switch (seq[1]) {
return END_LINE; case 'H':
} return BEG_LINE;
} case 'F':
return '\x1b'; return END_LINE;
} else { }
return c; }
} return '\x1b';
} } else {
return c;
int getCursorPosition(int *rows, int *cols) { }
char buf[32]; }
unsigned int i = 0;
int getCursorPosition(int *rows, int *cols) {
if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4) { char buf[32];
return -1; unsigned int i = 0;
}
if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4) {
while (i < sizeof(buf) - 1) { return -1;
if (read(STDIN_FILENO, &buf[i], 1) != 1) { }
break;
} while (i < sizeof(buf) - 1) {
if (buf[i] == 'R') { if (read(STDIN_FILENO, &buf[i], 1) != 1) {
break; break;
} }
++i; if (buf[i] == 'R') {
} break;
buf[i] = '\0'; }
++i;
if (buf[0] != '\x1b' || buf[1] != '[') { }
return -1; buf[i] = '\0';
}
if (sscanf(&buf[2], "%d;%d", rows, cols) != 2) { if (buf[0] != '\x1b' || buf[1] != '[') {
return -1; return -1;
} }
if (sscanf(&buf[2], "%d;%d", rows, cols) != 2) {
return 0; return -1;
} }
int getWindowSize(int *rows, int *cols) { return 0;
struct winsize ws; }
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) { int getWindowSize(int *rows, int *cols) {
if (write(STDOUT_FILENO, "\x1b[999C\x1b[999B", 12) != 12) { #ifdef _WIN32
return -1; // Windows implementation
} CONSOLE_SCREEN_BUFFER_INFO csbi;
return getCursorPosition(rows, cols); GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
} else { *cols = csbi.srWindow.Right - csbi.srWindow.Left + 1;
*cols = ws.ws_col; *rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
*rows = ws.ws_row; return 0;
return 0; #else
} // Unix implementation
} struct winsize ws;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
if (write(STDOUT_FILENO, "\x1b[999C\x1b[999B", 12) != 12) {
return -1;
}
return getCursorPosition(rows, cols);
} else {
*cols = ws.ws_col;
*rows = ws.ws_row;
return 0;
}
#endif
}