4 Commits

Author SHA1 Message Date
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
25 changed files with 1595 additions and 545 deletions
+1 -1
View File
@@ -9,7 +9,7 @@
#----------------------------------------------------------##
#=--------------------------------------------------------------##
+--------------------------+@#-%*-----------------------------------#*
+--------------------------%@@@@#-------------------------------------** BELUGA - VERSION 2.2.1
+--------------------------%@@@@#-------------------------------------** BELUGA - VERSION 2.3
*-=-------------------------#@@*---------------------------------------=%
%*#==--------------------------------------------------------------------+# ----- KEY-BINDS -----
*%%=-=--------------------------------------------------------------------=# CTRL-x CTRL-c leave
+6 -1
View File
@@ -2,6 +2,7 @@
(define TAB-LENGTH 4)
(define QUIT-TIMES 1)
(define THEME "dark")
;; PACKAGES
@@ -80,5 +81,9 @@
(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" editor-find "no-prefix")
(map-key "CTRL-s" buffer-find "no-prefix")
(map-key "CTRL-r" editor-move-to-end-of-word "no-prefix")
(map-key "\"" editor-split-screen-vertical "user")
(map-key "o" editor-switch-next-pane "user")
(map-key "*" editor-unify-panes "user")
+58
View File
@@ -0,0 +1,58 @@
/**
* @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);
#endif
+34 -26
View File
@@ -3,45 +3,53 @@
#include "lisp.h"
// Mouvement
Lisp moveCursor(Lisp args, LispError *e, LispContext ctx);
Lisp mapKey(Lisp args, LispError *e, LispContext ctx);
void registerBuiltin(char * key_sequence, LispCFunc f);
Lisp editorQuit(Lisp args, LispError *e, LispContext ctx);
Lisp l_editorSave(Lisp args, LispError *e, LispContext ctx);
Lisp l_editorInsertNewLine(Lisp args, LispError* e, LispContext ctx);
Lisp moveCursorBeginLine(Lisp args, LispError *e, LispContext ctx);
Lisp moveCursorEndLine(Lisp args, LispError *e, LispContext ctx);
Lisp l_editorInserTab(Lisp args, LispError *e, LispContext ctx);
Lisp deletePreviousChar(Lisp args, LispError *e, LispContext ctx);
Lisp editorMoveCursorPageUp(Lisp args, LispError* e, LispContext ctx);
Lisp editorMoveCursorPageDown(Lisp args, LispError *e, LispContext ctx);
Lisp editorOpenFile(Lisp args, LispError *e, LispContext ctx);
// Text editing
Lisp l_editorInsertNewLine(Lisp args, LispError* e, LispContext ctx);
Lisp l_editorInserTab(Lisp args, LispError *e, LispContext ctx);
Lisp deletePreviousChar(Lisp args, LispError *e, LispContext ctx);
Lisp editorPrintC(Lisp args, LispError *e, LispContext ctx);
Lisp addPackage(Lisp args, LispError *e, LispContext ctx);
Lisp editorDelRow_L(Lisp args, LispError *e, LispContext ctx);
Lisp editorFind_L(Lisp args, LispError *e, LispContext ctx);
Lisp editorReadChar_L(Lisp args, LispError *e, LispContext ctx);
Lisp editorSetPrefix(Lisp args, LispError *e, LispContext ctx);
// Editor
Lisp editorQuit(Lisp args, LispError *e, LispContext ctx);
Lisp l_editorSave(Lisp args, LispError *e, LispContext ctx);
Lisp editorSetPrefix(Lisp args, LispError *e, LispContext ctx);
Lisp editorPrefix(Lisp args, LispError *e, LispContext ctx);
Lisp mapKey(Lisp args, LispError *e, LispContext ctx);
void registerBuiltin(char * key_sequence, LispCFunc f);
Lisp editorOpenFile(Lisp args, LispError *e, LispContext ctx);
Lisp addPackage(Lisp args, LispError *e, LispContext ctx);
// Buffer
Lisp editorSwitchNextBuffer(Lisp args, LispError *e, LispContext ctx);
Lisp bufferFind_L(Lisp args, LispError *e, LispContext ctx);
// 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);
+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
+75 -12
View File
@@ -13,14 +13,63 @@
* \param
* */
typedef struct erow {
typedef struct frow {
int size; /**< Size of the line */
int rsize; /**< Size of the render line */
char *chars; /**< Characters of the line */
char *render; /**< The actual line we will print */
} erow;
} frow;
enum editorStatus_e {
/**
* @brief Split modes for screen layout
*/
typedef enum {
SPLIT_NONE = 0, // Single buffer fullscreen
SPLIT_VERTICAL, // Left-right split
SPLIT_HORIZONTAL // Top-bottom split
} SplitMode;
/**
* @brief Represents an editor viewport/pane
*/
typedef struct {
int buffer_id; // Which buffer this pane displays
int start_row; // Starting row on screen
int start_col; // Starting column on screen
int height; // Height of this pane
int width; // Width of this pane
int cursor_x; // Local cursor x in this pane
int cursor_y; // Local cursor y in this pane
int rx, ry;
int row_offset; // Scroll offset for rows
int col_offset; // Scroll offset for columns
int is_active; // Is this pane currently active
} EditorPane;
/**
* @brief Screen layout manager
*/
typedef struct {
SplitMode mode;
EditorPane *panes;
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,
@@ -29,6 +78,7 @@ enum editorStatus_e {
struct const_t {
int TAB_LENGTH;
int QUIT_TIMES;
char *THEME;
};
struct prefix_t {
@@ -40,7 +90,22 @@ 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 */
};
/**
@@ -48,17 +113,12 @@ struct keyBind_t {
* \brief Containing our editor state.
*/
struct editorConfig {
int cursor_x, cursor_y; /**< Cursor position */
int rx; /**< Position in the render*/
int row_offset; /**< Position scroll of lines */
int col_offset; /**< Position scroll of colomns*/
int screenrows; /**< Terminal height*/
int screencols; /**< Terminal width*/
int numrows; /**< Number of rows contained */
erow *row; /**< Store all the rows printed */
ScreenLayout layout;
int dirty;
char *filename;
enum editorStatus_e state;
int prefix_state;
char status_msg[80];
time_t status_msg_time;
@@ -80,6 +140,10 @@ struct editorConfig {
struct prefix_t *prefix;
int number_of_prefix;
struct buffer_t buffers[64];
int number_of_buffer;
theme_t theme;
};
/**
@@ -94,5 +158,4 @@ struct abuf {
extern struct editorConfig E;
#endif
+5 -3
View File
@@ -2,10 +2,12 @@
#define EDITOR_OP_H_
#include "data.h"
void editorInsertChar(int c);
void bufferInsertChar(int c);
void editorInsertNewLine();
void bufferInsertNewLine();
void editorDelChar();
void bufferDelChar();
void editorSetStatusMessage(const char *fmt, ...);
#endif // EDITOR_OP_H_
+3 -3
View File
@@ -8,15 +8,15 @@
#include <stdlib.h>
#include <sys/types.h>
char *editorRowsToString(int *buffer_len);
char *bufferRowsToString(struct buffer_t *buf,int *buffer_len);
void editorCloseFile(void);
void editorOpen(char *filename);
void editorOpen(struct buffer_t *buffer);
void editorSave();
void editorFind();
void bufferFind(struct buffer_t *buf);
#endif // FILE_IO_H_
-6
View File
@@ -1,11 +1,7 @@
#ifndef OUTPUT_H_
#define OUTPUT_H_
#include "append_buffer.h"
#include "data.h"
#include "define.h"
#include "row_op.h"
#include <stdio.h>
#include <unistd.h>
/**
@@ -23,6 +19,4 @@ void editorDrawStatusBar(struct abuf *ab);
void editorDrawMessageBar(struct abuf *ab);
void editorSetStatusMessage(const char *fmt, ...);
#endif // OUTPUT_H_
+9 -9
View File
@@ -8,22 +8,22 @@
#include <time.h>
#include <unistd.h>
int editorRowCxToRx(erow *row, int cursor_x);
int bufferRowCxToRx(frow *row, int cursor_x);
int editorRowRxToCx(erow *row, int rx);
int bufferRowRxToCx(frow *row, int rx);
void editorUpdateRow(erow *row);
void bufferUpdatfrow(frow *row);
void editorInsertRow(int at, char *s, size_t len);
void bufferInsertRow(struct buffer_t *buffer, int at, char *s, size_t len);
void editorFreeRow(erow *row);
void bufferFrefrow(frow *row);
void editorDelRow(int at);
void bufferDelRow(struct buffer_t *buffer, int at);
void editorRowInsertChar(erow *row, int at, int c);
void bufferRowInsertChar(struct buffer_t *buffer, frow *row, int at, int c);
void editorRowAppendString(erow *row, char *s, size_t len);
void bufferRowAppendString(struct buffer_t *buffer, frow *row, char *s, size_t len);
void editorRowDelchar(erow *row, int at);
void bufferRowDelchar(struct buffer_t *buffer, frow *row, int at);
#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
+10 -7
View File
@@ -1,10 +1,10 @@
#define COLOR_RESET "\033[0m"
#define COLOR_KEYWORD "\033[1;35m" // Bold magenta
#define COLOR_TYPE "\033[1;34m" // Bold blue
#define COLOR_STRING "\033[1;32m" // Bold green
#define COLOR_COMMENT "\033[0;36m" // Cyan
#define COLOR_NUMBER "\033[1;33m" // Bold yellow
#define COLOR_DEFAULT "\033[0;37m" // White
#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 {
@@ -18,3 +18,6 @@ typedef enum {
} TokenType;
char *highlight_line(const char * line, int *length);
#endif
+2 -1
View File
@@ -16,7 +16,8 @@ fi
echo "Create config files ..."
mkdir -pv ~/.beluga/
cp -rv ./assets/ ~/.beluga/
mkdir -pv ~/.beluga/assets/
cp -rv ./assets/beluga.txt ~/.beluga/assets/
mkdir -pv ~/.beluga/packages/
mkdir -pv ~/.beluga/config/
+9 -6
View File
@@ -5,6 +5,8 @@
* interactions. \version 0.1 \date 21 septembre 2024
*/
#include "include/buffer.h"
#include "include/split_screen.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -18,7 +20,7 @@
#include "include/file_io.h"
#include "include/init.h"
#include "include/input.h"
#include "include/output.h"
#include "include/editor_op.h"
#include "include/terminal.h"
struct editorConfig E;
@@ -30,17 +32,18 @@ int main(int argc, char *argv[]) {
enableRawMode();
initEditor();
if (argc >= 2) {
E.state = READ_AND_WRITE;
editorOpen(argv[1]);
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);
editorOpen(splash_screen);
EditorPane *active = splitScreenGetActivePane();
active->buffer_id = bufferCreate(splash_screen);
}
free(splash_screen);
//free(splash_screen);
editorSetStatusMessage("HELP: Ctrl-x Ctrl-s = save | Ctrl-x Ctrl-q = quit");
editorSetStatusMessage("HELP: Ctrl-x Ctrl-s = save | Ctrl-x Ctrl-c = quit");
while (1) {
+3 -1
View File
@@ -1,5 +1,5 @@
project('beluga', 'c',
version : '1.1',
version : '2.3',
default_options : [
'c_std=none',
]
@@ -21,6 +21,8 @@ src_files = files(
'src/row_op.c',
'src/terminal.c',
'src/builtins.c',
'src/buffer.c',
'src/split_screen.c'
)
# Executable
+236
View File
@@ -0,0 +1,236 @@
/**
* @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;
}
+78 -24
View File
@@ -12,7 +12,8 @@
#include "../include/file_io.h"
#include "../include/input.h"
#include "../include/row_op.h"
#include "include/output.h"
#include "../include/buffer.h"
#include "../include/split_screen.h"
#include <stdio.h>
#include <stdlib.h>
@@ -116,18 +117,25 @@ Lisp moveCursor(Lisp args, LispError *e, LispContext ctx) {
* @note Updates global editor state E
*/
void free_structs(void) {
int i;
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(E.filename);
for (i = 0; i < E.numrows; ++i) {
free(E.row[i].render);
free(E.row[i].chars);
// 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.row);
free(E.buffers[i].row);
}
free(E.init_file_path);
fclose(E.fd_init_file);
@@ -171,6 +179,16 @@ Lisp editorQuit(Lisp args, LispError *e, LispContext ctx) {
* @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();
@@ -189,7 +207,7 @@ Lisp l_editorSave(Lisp args, LispError *e, LispContext ctx) {
*/
Lisp l_editorInsertNewLine(Lisp args, LispError *e, LispContext ctx) {
editorInsertNewLine();
bufferInsertNewLine();
return lisp_null();
}
@@ -206,7 +224,7 @@ Lisp l_editorInsertNewLine(Lisp args, LispError *e, LispContext ctx) {
Lisp l_editorInserTab(Lisp args, LispError *e, LispContext ctx) {
for (int i = 0; i<E.constantes.TAB_LENGTH; ++i) {
editorInsertChar(' ');
bufferInsertChar(' ');
}
return lisp_null();
@@ -222,7 +240,7 @@ Lisp l_editorInserTab(Lisp args, LispError *e, LispContext ctx) {
* @note Updates global editor state E
*/
Lisp moveCursorBeginLine(Lisp args, LispError *e, LispContext ctx) {
E.cursor_x = 0;
E.layout.panes[E.layout.active_pane].cursor_x = 0;
return lisp_null();
}
@@ -236,8 +254,10 @@ Lisp moveCursorBeginLine(Lisp args, LispError *e, LispContext ctx) {
* @note Updates global editor state E
*/
Lisp moveCursorEndLine(Lisp args, LispError *e, LispContext ctx) {
if (E.cursor_y < E.numrows) {
E.cursor_x = E.row[E.cursor_y].size;
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();
}
@@ -252,7 +272,7 @@ Lisp moveCursorEndLine(Lisp args, LispError *e, LispContext ctx) {
* @see editorDelChar()
*/
Lisp deletePreviousChar(Lisp args, LispError *e, LispContext ctx) {
editorDelChar();
bufferDelChar();
return lisp_null();
}
@@ -267,7 +287,8 @@ Lisp deletePreviousChar(Lisp args, LispError *e, LispContext ctx) {
* @see editorMoveCursor()
*/
Lisp editorMoveCursorPageUp(Lisp args, LispError *e, LispContext ctx) {
E.cursor_y = E.row_offset;
EditorPane *active = splitScreenGetActivePane();
active->cursor_y = active->row_offset;
int times = E.screenrows;
while (--times) {
editorMoveCursor(ARROW_UP);
@@ -286,9 +307,11 @@ Lisp editorMoveCursorPageUp(Lisp args, LispError *e, LispContext ctx) {
* @see editorMoveCursor()
*/
Lisp editorMoveCursorPageDown(Lisp args, LispError *e, LispContext ctx) {
E.cursor_y = E.row_offset + E.screenrows - 1;
if (E.cursor_y > E.numrows) {
E.cursor_y = E.numrows;
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) {
@@ -312,7 +335,9 @@ Lisp editorMoveCursorPageDown(Lisp args, LispError *e, LispContext ctx) {
Lisp editorOpenFile(Lisp args, LispError *e, LispContext ctx) {
char *filename = editorPrompt("Open : %s", getenv("PWD"), 1);
if (filename){
editorOpen(filename);
// editorOpen(filename);
EditorPane *active = splitScreenGetActivePane();
active->buffer_id = bufferCreate(filename);
}
free(filename);
@@ -331,7 +356,7 @@ Lisp editorOpenFile(Lisp args, LispError *e, LispContext ctx) {
*/
Lisp editorPrintC(Lisp args, LispError *e, LispContext ctx) {
char c = lisp_string(lisp_car(args))[0];
editorInsertChar(c);
bufferInsertChar(c);
return lisp_null();
}
@@ -376,7 +401,32 @@ Lisp addPackage(Lisp args, LispError *e, LispContext ctx) {
* @see editorDelRow()
*/
Lisp editorDelRow_L(Lisp args, LispError *e, LispContext ctx) {
editorDelRow(E.cursor_y);
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();
}
@@ -389,9 +439,11 @@ Lisp editorDelRow_L(Lisp args, LispError *e, LispContext ctx) {
* @return lisp_null()
* @see editorFind()
*/
Lisp editorFind_L(Lisp args, LispError *e, LispContext ctx) {
Lisp bufferFind_L(Lisp args, LispError *e, LispContext ctx) {
fprintf(stderr, "LispFind\n");
editorFind();
EditorPane *active = splitScreenGetActivePane();
struct buffer_t *buffer = bufferFindById(active->buffer_id);
bufferFind(buffer);
return lisp_null();
}
@@ -406,11 +458,13 @@ Lisp editorFind_L(Lisp args, LispError *e, LispContext ctx) {
*/
Lisp editorReadChar_L(Lisp args, LispError *e, LispContext ctx) {
Lisp returned_char;
if (E.row[E.cursor_y].render[E.cursor_x] == 0) {
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(E.row[E.cursor_y].render[E.cursor_x]);
returned_char = lisp_make_char(buffer->row[active->cursor_y].render[active->cursor_x]);
}
return returned_char;
}
+59 -29
View File
@@ -1,52 +1,82 @@
#include <stdarg.h>
#include "../include/editor_op.h"
#include "../include/row_op.h"
#include "include/buffer.h"
#include "include/data.h"
#include "include/split_screen.h"
extern struct editorConfig E;
void editorInsertChar(int c) {
if (E.cursor_y == E.numrows) {
editorInsertRow(E.numrows, "", 0);
}
editorRowInsertChar(&E.row[E.cursor_y], E.cursor_x, c);
E.cursor_x++;
/**
* @brief Sets a temporary status message for display
* @details Formats and stores a message that will be displayed in the message
* bar for 5 seconds. Uses printf-style variable argument formatting.
* @param fmt Printf-style format string
* @param ... Variable arguments for format string
* @note Updates global editor state E (status_msg, status_msg_time)
* @see editorDrawMessageBar()
*/
void editorSetStatusMessage(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vsnprintf(E.status_msg, sizeof(E.status_msg), fmt, ap);
va_end(ap);
E.status_msg_time = time(NULL);
}
void editorInsertNewLine() {
void bufferInsertChar(int c) {
EditorPane *active = splitScreenGetActivePane();
struct buffer_t *buf = bufferFindById(active->buffer_id);
if (active->cursor_y == buf->numrows) {
bufferInsertRow(buf, buf->numrows, "", 0);
}
bufferRowInsertChar(buf, &buf->row[active->cursor_y], active->cursor_x, c);
active->cursor_x++;
}
void bufferInsertNewLine() {
/*
* Add new line and place the cursor at the beginning of it
*/
fprintf(stderr, "Inserting new line\n");
erow *row;
if (!E.cursor_x) {
editorInsertRow(E.cursor_y, "", 0);
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 = &E.row[E.cursor_y];
editorInsertRow(E.cursor_y + 1, &row->chars[E.cursor_x],
row->size - E.cursor_x);
row = &E.row[E.cursor_y];
row->size = E.cursor_x;
row = &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';
editorUpdateRow(row);
bufferUpdatfrow(row);
}
++E.cursor_y;
E.cursor_x = 0;
++active->cursor_y;
active->cursor_x = 0;
fprintf(stderr, "Insert new line done\n");
}
void editorDelChar() {
erow *row;
if (E.cursor_y == E.numrows || !(E.cursor_x || E.cursor_y)) {
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 = &E.row[E.cursor_y];
if (E.cursor_x > 0) {
editorRowDelchar(row, E.cursor_x - 1);
--E.cursor_x;
row = &buf->row[active->cursor_y];
if (active->cursor_x > 0) {
bufferRowDelchar(buf, row, active->cursor_x - 1);
--active->cursor_x;
} else {
E.cursor_x = E.row[E.cursor_y - 1].size;
editorRowAppendString(&E.row[E.cursor_y - 1], row->chars, row->size);
editorDelRow(E.cursor_y);
--E.cursor_y;
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;
}
}
+55 -53
View File
@@ -7,8 +7,11 @@
*/
#include "../include/file_io.h"
#include "../include/editor_op.h"
#include "../include/input.h"
#include "../include/output.h"
#include "include/buffer.h"
#include "include/data.h"
#include "include/split_screen.h"
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
@@ -31,77 +34,71 @@ extern struct editorConfig E;
* Rows are separated by newline characters.
* @note Caller is responsible for freeing the returned buffer
*/
char *editorRowsToString(int *buffer_len) {
char *bufferRowsToString(struct buffer_t *buf, int *buffer_len) {
int tot_len = 0;
int j;
char *buf;
char *buffer;
char *p;
for (j = 0; j < E.numrows; ++j) {
tot_len += E.row[j].size + 1;
for (j = 0; j < buf->numrows; ++j) {
tot_len += buf->row[j].size + 1;
}
*buffer_len = tot_len;
buf = malloc(tot_len);
p = buf;
for (j = 0; j < E.numrows; ++j) {
memcpy(p, E.row[j].chars, E.row[j].size);
p += E.row[j].size;
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 buf;
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.
* @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) {
E.cursor_x = 0;
E.cursor_y = 0;
E.rx = 0;
E.row_offset = 0;
E.col_offset = 0;
for (int i = 0; i < E.numrows; ++i) {
free(E.row[i].chars);
free(E.row[i].render);
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);
}
E.numrows = 0;
free(E.row);
E.row = NULL;
E.dirty = 0;
free(E.filename);
E.filename = NULL;
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.
* @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(char *filename) {
void editorOpen(struct buffer_t* buffer) {
FILE *fp;
// Test if a file is already open
if (E.filename != NULL) {
editorCloseFile();
E.state = READ_AND_WRITE;
}
E.filename = strdup(filename);
fp = fopen(filename, "a+");
fp = fopen(buffer->filename, "a+");
if (!fp)
die("fopen");
@@ -114,7 +111,7 @@ void editorOpen(char *filename) {
(line[line_len - 1] == '\n' || line[line_len - 1] == '\r')) {
--line_len;
}
editorInsertRow(E.numrows, line, line_len);
bufferInsertRow(buffer, buffer->numrows, line, line_len);
free(line);
line = NULL;
}
@@ -134,18 +131,20 @@ void editorOpen(char *filename) {
* @see editorRowsToString()
*/
void editorSave() {
EditorPane *active = splitScreenGetActivePane();
struct buffer_t *buffer = bufferFindById(active->buffer_id);
int len;
char *buf;
int fd;
if (E.filename == NULL) {
E.filename = editorPrompt("Save as: %s (ESC to cancel)", "", 1);
if (E.filename == NULL) {
if (buffer->filename == NULL) {
buffer->filename = editorPrompt("Save as: %s (ESC to cancel)", "", 1);
if (buffer->filename == NULL) {
editorSetStatusMessage("Save aborted");
return;
}
}
buf = editorRowsToString(&len);
fd = open(E.filename, O_RDWR | O_CREAT, 0644);
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) {
@@ -172,18 +171,21 @@ void editorSave() {
* @see editorPrompt()
* @see editorRowRxToCx()
*/
void editorFind() {
void bufferFind(struct buffer_t *buf) {
fprintf(stderr, "searching\n");
char *query = editorPrompt("Search: %s (ESC to cancel)", "", 0);
if (query == NULL) return;
EditorPane *active = splitScreenGetActivePane();
if (query == NULL)
return;
int i;
for (i = E.cursor_y + 1; i < E.numrows; i++) {
erow *row = &E.row[i];
for (i = active->cursor_y + 1; i < buf->numrows; i++) {
frow *row = &buf->row[i];
char *match = strstr(row->render, query);
if (match) {
E.cursor_y = i;
E.cursor_x = editorRowRxToCx(row, match - row->render);
E.row_offset = E.numrows;
active->cursor_y = i;
active->cursor_x = bufferRowRxToCx(row, match - row->render);
buf->row_offset = buf->numrows;
break;
}
}
+71 -33
View File
@@ -1,9 +1,12 @@
#include "../include/init.h"
#include "../include/builtins.h"
#include "../include/color.h"
#include "../include/data.h"
#include "../include/terminal.h"
#include "include/split_screen.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LISP_IMPLEMENTATION
#include "../include/lisp.h"
@@ -17,7 +20,7 @@ void registerBuiltin(char *key_sequence, LispCFunc f) {
}
void initBuiltins() {
// move cursor
// Registering C functions as lisp macro
registerBuiltin("move-cursor", moveCursor);
registerBuiltin("map-key", mapKey);
registerBuiltin("editor-quit", editorQuit);
@@ -32,45 +35,19 @@ void initBuiltins() {
registerBuiltin("editor-insert-char", editorPrintC);
registerBuiltin("add-package", addPackage);
registerBuiltin("editor-del-row", editorDelRow_L);
registerBuiltin("editor-find", editorFind_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);
}
void initEditor() {
E.init_file_path = (char *)calloc(256, sizeof(char));
E.cursor_x = 0;
E.cursor_y = 0;
E.rx = 0;
E.row_offset = 0;
E.col_offset = 0;
E.numrows = 0;
E.row = NULL;
E.dirty = 0;
E.filename = NULL;
E.state = READ_ONLY;
E.status_msg[0] = '\0';
E.status_msg_time = 0;
if (getWindowSize(&E.screenrows, &E.screencols) == -1) {
die("getWindowSize");
}
E.screenrows -= 2;
void initConfig() {
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;
strcat(E.init_file_path, getenv("HOME"));
strcat(E.init_file_path, "/.beluga/config/init.lisp");
// printf("%s\n", init_file_path);
E.fd_init_file = fopen(E.init_file_path, "r");
E.ctx = lisp_init();
E.env = lisp_env(E.ctx);
lisp_lib_load(E.ctx);
@@ -83,6 +60,67 @@ void initEditor() {
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
+33 -26
View File
@@ -2,6 +2,9 @@
#include "../include/define.h"
#include "../include/editor_op.h"
#include "../include/output.h"
#include "include/buffer.h"
#include "include/data.h"
#include "include/split_screen.h"
#include <ctype.h>
#include <dirent.h>
#include <stdint.h>
@@ -42,7 +45,8 @@ const char *file_completion(const char *path) {
// path is a directory
if (path[strlen(path) - 1] == '/') {
return path;
fprintf(stderr, "[FILE COMP] is dir\n");
strncpy(directory, path, 128);
}
// Find dir name
@@ -56,12 +60,13 @@ const char *file_completion(const char *path) {
predict[predict_len] = '\0';
fprintf(stderr, "%s %s\n", directory, predict);
} else {
return NULL;
fprintf(stderr, "[FILE COMP] dir not found\n");
return strdup(path);
}
dir = opendir(directory);
if (!dir)
return NULL;
return strdup(path);
while ((entry = readdir(dir)) != NULL) {
if (strncmp(entry->d_name, predict, predict_len) == 0) {
@@ -82,7 +87,8 @@ const char *file_completion(const char *path) {
closedir(dir);
dir = NULL;
free(entry);
return NULL;
fprintf(stderr, "[FILE COMP] no entries\n");
return strdup(path);
}
/**
@@ -243,43 +249,44 @@ char *key_to_string(int key) {
* @note Updates global editor state E
*/
int editorMoveCursor(int key) {
erow *row = (E.cursor_y >= E.numrows) ? NULL : &E.row[E.cursor_y];
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 && E.cursor_x < row->size) {
++E.cursor_x;
} else if (row && E.cursor_x == row->size) {
E.cursor_y++;
E.cursor_x = 0;
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 (E.cursor_y < E.numrows) {
++E.cursor_y;
if (y < buf->numrows) {
active->cursor_y++;
}
break;
case ARROW_UP:
if (E.cursor_y != 0) {
--E.cursor_y;
if (y != 0) {
--active->cursor_y;
}
break;
case ARROW_LEFT:
if (E.cursor_x != 0) {
--E.cursor_x;
} else if (E.cursor_y > 0) {
--E.cursor_y;
E.cursor_x = E.row[E.cursor_y].size;
if (x != 0) {
--active->cursor_x;
} else if (y > 0) {
--active->cursor_y;
active->cursor_x = buf->row[y].size;
}
break;
}
row = (E.cursor_y >= E.numrows) ? NULL : &E.row[E.cursor_y];
row_len = row ? row->size : 0;
if (E.cursor_x > row_len) {
E.cursor_x = row_len;
return 0;
}
return 1;
}
@@ -327,6 +334,6 @@ void editorProcessKeypress() {
if (executeKeyBind(key_to_string(c))) {
return;
}
editorInsertChar(c);
bufferInsertChar(c);
E.quit_times_buffer = E.constantes.QUIT_TIMES;
}
+188 -82
View File
@@ -1,13 +1,18 @@
/**
* @file output.c
* @brief Screen rendering and output module for the Beluga text editor
* @details Handles all screen updates, cursor positioning, status bar rendering,
* and display synchronization using ANSI escape sequences
* @details Handles all screen updates, cursor positioning, status bar
* rendering, and display synchronization using ANSI escape sequences
*/
#include "../include/output.h"
#include "../include/append_buffer.h"
#include "../include/buffer.h"
#include "../include/data.h"
#include "../include/define.h"
#include "../include/row_op.h"
#include "../include/split_screen.h"
#include "../include/syntax_highlighter.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -16,56 +21,128 @@
extern struct editorConfig E;
/**
* @brief Renders all visible rows to the screen buffer
* @details Draws file content with syntax highlighting, handles line wrapping,
* and displays tilde characters (~) for empty lines. Shows welcome message
* when no file is open.
* @param ab Pointer to append buffer structure for accumulating output
* @note Respects E.row_offset and E.col_offset for scrolling
* @note Clears to end of each line after content
* @see editorRefreshScreen()
* @brief Renders a single pane with its buffer content
*/
void editorDrawRows(struct abuf *ab) {
int y;
static void editorDrawPane(struct abuf *ab, EditorPane *pane) {
if (pane == NULL || pane->buffer_id < 0)
return;
struct buffer_t *buf = bufferFindById(pane->buffer_id);
if (buf == NULL)
return;
// Draw content for this pane
for (int y = 0; y < pane->height; y++) {
int file_row = y + pane->row_offset;
// Position cursor at start of pane row
char pos_buf[32];
int pos_len = snprintf(pos_buf, sizeof(pos_buf), "\x1b[%d;%dH",
pane->start_row + y + 1, pane->start_col + 1);
abAppend(ab, pos_buf, pos_len);
// Apply background color (6 bytes for RGB format)
abAppend(ab, E.theme.BACKGROUND_COLOR, strlen(E.theme.BACKGROUND_COLOR));
int chars_printed = 0;
if (file_row >= buf->numrows) {
// Empty line - show tilde
if (buf->numrows == 0 && y == pane->height / 3) {
char welcome[80];
int welcome_len;
int padding;
int len;
int file_row;
for (y = 0; y < E.screenrows; ++y) {
file_row = y + E.row_offset;
if (file_row >= E.numrows) {
if (E.numrows == 0 && y == E.screenrows / 3) {
welcome_len =
snprintf(welcome, sizeof(welcome),
"Beluga text editor -- version %s", BELUGA_VERSION);
if (welcome_len > E.screencols) {
welcome_len = E.screencols;
}
padding = (E.screencols - welcome_len) / 2;
int welcome_len =
snprintf(welcome, sizeof(welcome), "Buffer %d", pane->buffer_id);
if (welcome_len > pane->width)
welcome_len = pane->width;
int padding = (pane->width - welcome_len) / 2;
if (padding) {
abAppend(ab, "~", 1);
--padding;
chars_printed++;
padding--;
}
while (padding--) {
while (padding-- && chars_printed < pane->width) {
abAppend(ab, " ", 1);
chars_printed++;
}
abAppend(ab, welcome, welcome_len);
chars_printed += welcome_len;
} else {
abAppend(ab, "~", 1);
chars_printed++;
}
} else {
len = E.row[file_row].rsize - E.col_offset;
if (len < 0)
len = 0;
if (len > E.screencols)
len = E.screencols;
char * highlighted = highlight_line(&E.row[file_row].render[E.col_offset], &E.row[file_row].rsize);
abAppend(ab, highlighted, E.row[file_row].rsize);
// Render line with syntax highlighting, constrain to pane width
int start_offset = pane->col_offset;
int visible_len = buf->row[file_row].rsize - start_offset;
if (visible_len < 0)
visible_len = 0;
if (visible_len > pane->width)
visible_len = pane->width;
if (buf->filename[strlen(buf->filename) - 1] == 'c') {
int byte_len_to_print;
char *highlighted = highlight_line(
&buf->row[file_row].render[start_offset], &byte_len_to_print);
// Print only up to pane width
abAppend(ab, highlighted, byte_len_to_print);
free(highlighted);
} else {
abAppend(ab, &buf->row[file_row].render[start_offset], buf->row[file_row].rsize);
}
chars_printed = visible_len;
}
// Fill remaining space with background color to pane width
abAppend(ab, E.theme.BACKGROUND_COLOR, strlen(E.theme.BACKGROUND_COLOR));
while (chars_printed < pane->width) {
abAppend(ab, " ", 1);
chars_printed++;
}
// 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
}
}
}
abAppend(ab, ERASE_END_LINE, 3);
abAppend(ab, "\r\n", 2);
}
}
@@ -77,45 +154,80 @@ void editorDrawRows(struct abuf *ab) {
* @see editorRowCxToRx()
*/
void editorScroll() {
E.rx = E.cursor_x;
if (E.cursor_y < E.numrows) {
E.rx = editorRowCxToRx(&E.row[E.cursor_y], E.cursor_x);
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 (E.cursor_y < E.row_offset) {
E.row_offset = E.cursor_y;
if (active->cursor_y < 0) {
active->cursor_y = 0;
active->row_offset = active->row_offset == 0 ? 0 : --active->row_offset;
}
if (E.cursor_y >= E.row_offset + E.screenrows) {
E.row_offset = E.cursor_y - E.screenrows + 1;
if (active->cursor_x == active->width) {
active->cursor_x--;
active->col_offset++;
}
if (E.rx < E.col_offset) {
E.col_offset = E.rx;
}
if (E.rx >= E.col_offset + E.screencols) {
E.col_offset = E.rx - E.screencols + 1;
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.
* @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
len = snprintf(status, sizeof(status), "%.20s - %d lines%s",
E.filename ? E.filename : "[No Name]", E.numrows,
E.dirty ? "*" : "");
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",
E.cursor_y + 1, E.numrows);
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) {
@@ -126,6 +238,7 @@ void editorDrawStatusBar(struct abuf *ab) {
++len;
}
}
abAppend(ab, "\x1b[m", 3); // normal text mode
abAppend(ab, "\r\n", 2);
}
@@ -150,49 +263,42 @@ void editorDrawMessageBar(struct abuf *ab) {
/**
* @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.
* @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() {
editorScroll();
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
editorDrawRows(&ab);
// Draw all panes
editorScroll();
editorDrawAllPanes(&ab);
// Draw status bar and message bar
editorDrawStatusBar(&ab);
editorDrawMessageBar(&ab);
snprintf(buf, sizeof(buf), "\x1b[%d;%dH", (E.cursor_y - E.row_offset) + 1,
(E.rx - E.col_offset) + 1);
// 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);
}
/**
* @brief Sets a temporary status message for display
* @details Formats and stores a message that will be displayed in the message bar
* for 5 seconds. Uses printf-style variable argument formatting.
* @param fmt Printf-style format string
* @param ... Variable arguments for format string
* @note Updates global editor state E (status_msg, status_msg_time)
* @see editorDrawMessageBar()
*/
void editorSetStatusMessage(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vsnprintf(E.status_msg, sizeof(E.status_msg), fmt, ap);
va_end(ap);
E.status_msg_time = time(NULL);
}
+38 -37
View File
@@ -6,7 +6,7 @@
extern struct editorConfig E;
int editorRowCxToRx(erow *row, int cursor_x) {
int bufferRowCxToRx(frow *row, int cursor_x) {
int render_x = 0;
int i;
for (i = 0; i < cursor_x; ++i) {
@@ -18,7 +18,7 @@ int editorRowCxToRx(erow *row, int cursor_x) {
return render_x;
}
int editorRowRxToCx(erow *row, int rx) {
int bufferRowRxToCx(frow *row, int rx) {
int cur_rx = 0;
int cx;
for (cx = 0; cx < row->size; cx++) {
@@ -31,11 +31,11 @@ int editorRowRxToCx(erow *row, int rx) {
}
/**
* \fn editorUpdateRow(erow *row)
* \fn bufferUpdatfrow(frow *row)
* \brief Copy content of \p row in \p row->render.
* */
void editorUpdateRow(erow *row) {
void bufferUpdatfrow(frow *row) {
int i, i_render;
int tabs = 0;
@@ -68,52 +68,53 @@ void editorUpdateRow(erow *row) {
row->rsize = i_render;
}
void editorInsertRow(int at, char *s, size_t len) {
if (at < 0 || at > E.numrows) {
void bufferInsertRow(struct buffer_t *buffer, int at, char *s, size_t len) {
if (at < 0 || at > buffer->numrows) {
return;
}
erow *tmp = (erow *)realloc(E.row, sizeof(erow) * (E.numrows + 1));
frow *tmp = (frow *)realloc(buffer->row, sizeof(frow) * (buffer->numrows + 1));
if (!tmp) {
return;
}
E.row = tmp;
memmove(&E.row[at + 1], &E.row[at], sizeof(erow) * (E.numrows - at));
buffer->row = tmp;
memmove(&buffer->row[at + 1], &buffer->row[at], sizeof(frow) * (buffer->numrows - at));
E.row[at].size = len;
E.row[at].chars = malloc(len + 1);
memcpy(E.row[at].chars, s, len);
E.row[at].chars[len] = '\0';
buffer->row[at].size = len;
buffer->row[at].chars = malloc(len + 1);
memcpy(buffer->row[at].chars, s, len);
buffer->row[at].chars[len] = '\0';
E.row[at].rsize = 0;
E.row[at].render = NULL;
editorUpdateRow(&E.row[at]);
buffer->row[at].rsize = 0;
buffer->row[at].render = NULL;
bufferUpdatfrow(&buffer->row[at]);
++E.numrows;
++E.dirty;
++buffer->numrows;
++buffer->dirty;
}
void editorFreeRow(erow *row) {
void bufferFrefrow(frow *row) {
free(row->render);
free(row->chars);
}
void editorDelRow(int at) {
if (at < 0 || at >= E.numrows) {
void bufferDelRow(struct buffer_t *buffer, int at) {
if (at < 0 || at >= buffer->numrows) {
return;
}
editorFreeRow(&E.row[at]);
memmove(&E.row[at], &E.row[at + 1], sizeof(erow) * (E.numrows - at - 1));
--E.numrows;
++E.dirty;
bufferFrefrow(&buffer->row[at]);
memmove(&buffer->row[at], &buffer->row[at + 1], sizeof(frow) * (buffer->numrows - at - 1));
--buffer->numrows;
++buffer->dirty;
}
/**
* \fn editorRowInsertChar(erow *row, int at, int c)
* \fn bufferRowInsertChar(frow *row, int at, int c)
* \param at Index of where we want to insert the char */
void editorRowInsertChar(erow *row, int at, int c) {
if (E.state == READ_ONLY)
void bufferRowInsertChar(struct buffer_t *buffer, frow *row, int at, int c) {
if (buffer->state == READ_ONLY)
return;
if (at < 0 || at > row->size) {
at = row->size;
@@ -122,30 +123,30 @@ void editorRowInsertChar(erow *row, int at, int c) {
memmove(&row->chars[at + 1], &row->chars[at], row->size - at + 1);
++row->size;
row->chars[at] = c;
editorUpdateRow(row);
++E.dirty;
bufferUpdatfrow(row);
++buffer->dirty;
}
void editorRowAppendString(erow *row, char *s, size_t len) {
void bufferRowAppendString(struct buffer_t *buffer, frow *row, char *s, size_t len) {
row->chars = realloc(row->chars, row->size + len + 1);
memcpy(&row->chars[row->size], s, len);
row->size += len;
row->chars[row->size] = '\0';
editorUpdateRow(row);
++E.dirty;
bufferUpdatfrow(row);
++buffer->dirty;
}
/**
* \fn editorRowDelChar(struct editorConfig *E, erow *erow, int at)
* \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 editorRowDelchar(erow *row, int at) {
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;
editorUpdateRow(row);
++E.dirty;
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];
}
+65 -31
View File
@@ -1,31 +1,34 @@
#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",
"continue", "return", "goto", "struct", "union", "enum",
"typedef", "static", "extern", "const", "volatile", "sizeof",
"auto", "register", "inline", "restrict", NULL
};
"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
};
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 >= '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;
if (strcmp(word, c_keywords[i]) == 0)
return 1;
}
return 0;
}
@@ -33,7 +36,8 @@ int is_keyword(const char *word) {
// 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;
if (strcmp(word, c_types[i]) == 0)
return 1;
}
return 0;
}
@@ -41,15 +45,23 @@ int is_type(const char *word) {
// Get color code for token type
const char *get_color(TokenType type) {
switch (type) {
case TOKEN_KEYWORD: return COLOR_KEYWORD;
case TOKEN_TYPE: return COLOR_TYPE;
case TOKEN_STRING: return COLOR_STRING;
case TOKEN_COMMENT: return COLOR_COMMENT;
case TOKEN_NUMBER: return COLOR_NUMBER;
default: return COLOR_DEFAULT;
case TOKEN_KEYWORD:
return E.theme.COLOR_KEYWORD;
case TOKEN_TYPE:
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) {
@@ -64,9 +76,23 @@ char *highlight_line(const char *line, int *length) {
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", COLOR_COMMENT);
result_pos += sprintf(&result[result_pos], "%s", E.theme.COLOR_COMMENT);
while (line[i] != '\0' && line[i] != '\n') {
result[result_pos++] = line[i++];
}
@@ -75,8 +101,9 @@ char *highlight_line(const char *line, int *length) {
}
// Handle block comments
if (line[i] == '/' && line[i + 1] == '*') {
result_pos += sprintf(&result[result_pos], "%s", COLOR_COMMENT);
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') {
@@ -93,7 +120,7 @@ char *highlight_line(const char *line, int *length) {
// Handle strings
if (line[i] == '"') {
result_pos += sprintf(&result[result_pos], "%s\"", COLOR_STRING);
result_pos += sprintf(&result[result_pos], "%s\"", E.theme.COLOR_STRING);
i++;
while (line[i] != '\0' && line[i] != '"') {
if (line[i] == '\\') {
@@ -103,14 +130,15 @@ char *highlight_line(const char *line, int *length) {
result[result_pos++] = line[i++];
}
}
if (line[i] == '"') 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'", COLOR_STRING);
result_pos += sprintf(&result[result_pos], "%s'", E.theme.COLOR_STRING);
i++;
while (line[i] != '\0' && line[i] != '\'') {
if (line[i] == '\\') {
@@ -120,14 +148,15 @@ char *highlight_line(const char *line, int *length) {
result[result_pos++] = line[i++];
}
}
if (line[i] == '\'') 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", COLOR_NUMBER);
result_pos += sprintf(&result[result_pos], "%s", E.theme.COLOR_NUMBER);
while (is_word_char(line[i]) || line[i] == '.') {
result[result_pos++] = line[i++];
}
@@ -138,23 +167,28 @@ char *highlight_line(const char *line, int *length) {
// Handle identifiers and keywords
if (is_word_char(line[i])) {
int start = i;
while (is_word_char(line[i])) i++;
while (is_word_char(line[i]))
i++;
char word[256];
strncpy(word, &line[start], i - start);
word[i - start] = '\0';
TokenType type = TOKEN_DEFAULT;
if (is_keyword(word)) type = TOKEN_KEYWORD;
else if (is_type(word)) type = TOKEN_TYPE;
if (is_keyword(word))
type = TOKEN_KEYWORD;
else if (is_type(word))
type = TOKEN_TYPE;
result_pos += sprintf(&result[result_pos], "%s%s%s",
get_color(type), word, COLOR_RESET);
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';