Adding splitting screen and buffer switching
This commit is contained in:
+1
-1
@@ -9,7 +9,7 @@
|
|||||||
#----------------------------------------------------------##
|
#----------------------------------------------------------##
|
||||||
#=--------------------------------------------------------------##
|
#=--------------------------------------------------------------##
|
||||||
+--------------------------+@#-%*-----------------------------------#*
|
+--------------------------+@#-%*-----------------------------------#*
|
||||||
+--------------------------%@@@@#-------------------------------------** BELUGA - VERSION 2.2.1
|
+--------------------------%@@@@#-------------------------------------** BELUGA - VERSION 2.3
|
||||||
*-=-------------------------#@@*---------------------------------------=%
|
*-=-------------------------#@@*---------------------------------------=%
|
||||||
%*#==--------------------------------------------------------------------+# ----- KEY-BINDS -----
|
%*#==--------------------------------------------------------------------+# ----- KEY-BINDS -----
|
||||||
*%%=-=--------------------------------------------------------------------=# CTRL-x CTRL-c leave
|
*%%=-=--------------------------------------------------------------------=# CTRL-x CTRL-c leave
|
||||||
|
|||||||
+6
-1
@@ -81,5 +81,10 @@
|
|||||||
(map-key "f" editor-open-file "user")
|
(map-key "f" editor-open-file "user")
|
||||||
(map-key "TAB" editor-insert-tab "no-prefix")
|
(map-key "TAB" editor-insert-tab "no-prefix")
|
||||||
(map-key "CTRL-k" editor-del-row "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 "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")
|
||||||
|
|
||||||
|
|||||||
+51
-1
@@ -1,8 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* @file buffer.h
|
||||||
|
* @brief Buffer management for handling multiple open files
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef BUFFER_H_
|
#ifndef BUFFER_H_
|
||||||
#define BUFFER_H_
|
#define BUFFER_H_
|
||||||
|
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
|
|
||||||
int new_buffer(enum buffer_type type, char * filename, int width, int height);
|
/**
|
||||||
|
* @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
|
#endif
|
||||||
|
|||||||
+34
-26
@@ -3,45 +3,53 @@
|
|||||||
|
|
||||||
#include "lisp.h"
|
#include "lisp.h"
|
||||||
|
|
||||||
|
// Mouvement
|
||||||
|
|
||||||
Lisp moveCursor(Lisp args, LispError *e, LispContext ctx);
|
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 moveCursorBeginLine(Lisp args, LispError *e, LispContext ctx);
|
||||||
|
|
||||||
Lisp moveCursorEndLine(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 editorMoveCursorPageUp(Lisp args, LispError* e, LispContext ctx);
|
||||||
|
|
||||||
Lisp editorMoveCursorPageDown(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 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 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 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 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);
|
void free_structs(void);
|
||||||
|
|
||||||
|
|||||||
+51
-13
@@ -13,12 +13,50 @@
|
|||||||
* \param
|
* \param
|
||||||
* */
|
* */
|
||||||
|
|
||||||
typedef struct erow {
|
typedef struct frow {
|
||||||
int size; /**< Size of the line */
|
int size; /**< Size of the line */
|
||||||
int rsize; /**< Size of the render line */
|
int rsize; /**< Size of the render line */
|
||||||
char *chars; /**< Characters of the line */
|
char *chars; /**< Characters of the line */
|
||||||
char *render; /**< The actual line we will print */
|
char *render; /**< The actual line we will print */
|
||||||
} erow;
|
} frow;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 {
|
typedef struct theme {
|
||||||
char *BACKGROUND_COLOR;
|
char *BACKGROUND_COLOR;
|
||||||
@@ -31,7 +69,7 @@ typedef struct theme {
|
|||||||
|
|
||||||
} theme_t;
|
} theme_t;
|
||||||
|
|
||||||
enum editorStatus_e {
|
enum bufferStatus_e {
|
||||||
IDLE,
|
IDLE,
|
||||||
READ_ONLY,
|
READ_ONLY,
|
||||||
READ_AND_WRITE,
|
READ_AND_WRITE,
|
||||||
@@ -60,8 +98,14 @@ struct buffer_t {
|
|||||||
enum buffer_type type;
|
enum buffer_type type;
|
||||||
int buffer_id;
|
int buffer_id;
|
||||||
int width, height;
|
int width, height;
|
||||||
int x, y; /**< Position of the buffer*/
|
int x, y; /**< Position in the buffer (cursor) */
|
||||||
|
frow *row;
|
||||||
|
int numrows;
|
||||||
char *filename;
|
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 */
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,17 +113,12 @@ struct buffer_t {
|
|||||||
* \brief Containing our editor state.
|
* \brief Containing our editor state.
|
||||||
*/
|
*/
|
||||||
struct editorConfig {
|
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 screenrows; /**< Terminal height*/
|
||||||
int screencols; /**< Terminal width*/
|
int screencols; /**< Terminal width*/
|
||||||
int numrows; /**< Number of rows contained */
|
|
||||||
erow *row; /**< Store all the rows printed */
|
ScreenLayout layout;
|
||||||
|
|
||||||
int dirty;
|
int dirty;
|
||||||
char *filename;
|
|
||||||
enum editorStatus_e state;
|
|
||||||
int prefix_state;
|
int prefix_state;
|
||||||
char status_msg[80];
|
char status_msg[80];
|
||||||
time_t status_msg_time;
|
time_t status_msg_time;
|
||||||
@@ -102,7 +141,6 @@ struct editorConfig {
|
|||||||
int number_of_prefix;
|
int number_of_prefix;
|
||||||
|
|
||||||
struct buffer_t buffers[64];
|
struct buffer_t buffers[64];
|
||||||
struct buffer_t ***screen_layout; /**< Which buffer is the current cell*/
|
|
||||||
int number_of_buffer;
|
int number_of_buffer;
|
||||||
|
|
||||||
theme_t theme;
|
theme_t theme;
|
||||||
|
|||||||
+3
-3
@@ -2,11 +2,11 @@
|
|||||||
#define EDITOR_OP_H_
|
#define EDITOR_OP_H_
|
||||||
|
|
||||||
#include "data.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, ...);
|
void editorSetStatusMessage(const char *fmt, ...);
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -8,15 +8,15 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
char *editorRowsToString(int *buffer_len);
|
char *bufferRowsToString(struct buffer_t *buf,int *buffer_len);
|
||||||
|
|
||||||
|
|
||||||
void editorCloseFile(void);
|
void editorCloseFile(void);
|
||||||
|
|
||||||
void editorOpen(char *filename);
|
void editorOpen(struct buffer_t *buffer);
|
||||||
|
|
||||||
void editorSave();
|
void editorSave();
|
||||||
|
|
||||||
void editorFind();
|
void bufferFind(struct buffer_t *buf);
|
||||||
|
|
||||||
#endif // FILE_IO_H_
|
#endif // FILE_IO_H_
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
#ifndef OUTPUT_H_
|
#ifndef OUTPUT_H_
|
||||||
#define OUTPUT_H_
|
#define OUTPUT_H_
|
||||||
|
|
||||||
#include "append_buffer.h"
|
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
#include "define.h"
|
|
||||||
#include "row_op.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+9
-9
@@ -8,22 +8,22 @@
|
|||||||
#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);
|
||||||
|
|
||||||
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_
|
#endif // ROW_OP_H_
|
||||||
|
|||||||
@@ -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
|
||||||
+2
-1
@@ -16,7 +16,8 @@ fi
|
|||||||
|
|
||||||
echo "Create config files ..."
|
echo "Create config files ..."
|
||||||
mkdir -pv ~/.beluga/
|
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/packages/
|
||||||
mkdir -pv ~/.beluga/config/
|
mkdir -pv ~/.beluga/config/
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
* interactions. \version 0.1 \date 21 septembre 2024
|
* interactions. \version 0.1 \date 21 septembre 2024
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "include/buffer.h"
|
||||||
|
#include "include/split_screen.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -30,17 +32,18 @@ int main(int argc, char *argv[]) {
|
|||||||
enableRawMode();
|
enableRawMode();
|
||||||
initEditor();
|
initEditor();
|
||||||
if (argc >= 2) {
|
if (argc >= 2) {
|
||||||
E.state = READ_AND_WRITE;
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
editorOpen(argv[1]);
|
active->buffer_id = bufferCreate(argv[1]);
|
||||||
} else {
|
} else {
|
||||||
strcat(splash_screen, getenv("HOME"));
|
strcat(splash_screen, getenv("HOME"));
|
||||||
strcat(splash_screen, "/.beluga/assets/beluga.txt");
|
strcat(splash_screen, "/.beluga/assets/beluga.txt");
|
||||||
fprintf(stderr, "%s\n", splash_screen);
|
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) {
|
while (1) {
|
||||||
|
|||||||
+3
-1
@@ -1,5 +1,5 @@
|
|||||||
project('beluga', 'c',
|
project('beluga', 'c',
|
||||||
version : '1.1',
|
version : '2.3',
|
||||||
default_options : [
|
default_options : [
|
||||||
'c_std=none',
|
'c_std=none',
|
||||||
]
|
]
|
||||||
@@ -21,6 +21,8 @@ src_files = files(
|
|||||||
'src/row_op.c',
|
'src/row_op.c',
|
||||||
'src/terminal.c',
|
'src/terminal.c',
|
||||||
'src/builtins.c',
|
'src/builtins.c',
|
||||||
|
'src/buffer.c',
|
||||||
|
'src/split_screen.c'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Executable
|
# Executable
|
||||||
|
|||||||
+230
-14
@@ -1,20 +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/data.h"
|
||||||
|
#include "include/split_screen.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
int new_buffer(enum buffer_type type, char *filename, int x, int y, int width,
|
extern struct editorConfig E;
|
||||||
int height) {
|
|
||||||
// Create a new buffer
|
|
||||||
|
|
||||||
struct buffer_t *buffer = &E.buffers[E.number_of_buffer];
|
/**
|
||||||
buffer->type = type;
|
* @brief Finds a buffer by filename
|
||||||
|
* @param filename The filename to search for
|
||||||
if (type == FILE_BUFF) {
|
* @return Buffer ID if found, -1 if not found
|
||||||
strcpy(buffer->filename, filename);
|
*/
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
buffer->width = width, buffer->height = height;
|
}
|
||||||
buffer->x = x, buffer->y = y;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
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
-23
@@ -12,6 +12,8 @@
|
|||||||
#include "../include/file_io.h"
|
#include "../include/file_io.h"
|
||||||
#include "../include/input.h"
|
#include "../include/input.h"
|
||||||
#include "../include/row_op.h"
|
#include "../include/row_op.h"
|
||||||
|
#include "../include/buffer.h"
|
||||||
|
#include "../include/split_screen.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -115,18 +117,25 @@ Lisp moveCursor(Lisp args, LispError *e, LispContext ctx) {
|
|||||||
* @note Updates global editor state E
|
* @note Updates global editor state E
|
||||||
*/
|
*/
|
||||||
void free_structs(void) {
|
void free_structs(void) {
|
||||||
int i;
|
int i, j;
|
||||||
free(E.prefix);
|
free(E.prefix);
|
||||||
for (i = 0; i < E.number_of_keybinds; ++i) {
|
for (i = 0; i < E.number_of_keybinds; ++i) {
|
||||||
free(E.key_binds[i].key_sequence);
|
free(E.key_binds[i].key_sequence);
|
||||||
}
|
}
|
||||||
free(E.key_binds);
|
free(E.key_binds);
|
||||||
free(E.filename);
|
// free layout
|
||||||
for (i = 0; i < E.numrows; ++i) {
|
free(E.layout.panes);
|
||||||
free(E.row[i].render);
|
|
||||||
free(E.row[i].chars);
|
// 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);
|
free(E.init_file_path);
|
||||||
fclose(E.fd_init_file);
|
fclose(E.fd_init_file);
|
||||||
|
|
||||||
@@ -170,6 +179,16 @@ Lisp editorQuit(Lisp args, LispError *e, LispContext ctx) {
|
|||||||
* @return lisp_null()
|
* @return lisp_null()
|
||||||
* @see editorSave()
|
* @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) {
|
Lisp l_editorSave(Lisp args, LispError *e, LispContext ctx) {
|
||||||
|
|
||||||
editorSave();
|
editorSave();
|
||||||
@@ -188,7 +207,7 @@ Lisp l_editorSave(Lisp args, LispError *e, LispContext ctx) {
|
|||||||
*/
|
*/
|
||||||
Lisp l_editorInsertNewLine(Lisp args, LispError *e, LispContext ctx) {
|
Lisp l_editorInsertNewLine(Lisp args, LispError *e, LispContext ctx) {
|
||||||
|
|
||||||
editorInsertNewLine();
|
bufferInsertNewLine();
|
||||||
|
|
||||||
return lisp_null();
|
return lisp_null();
|
||||||
}
|
}
|
||||||
@@ -205,7 +224,7 @@ Lisp l_editorInsertNewLine(Lisp args, LispError *e, LispContext ctx) {
|
|||||||
Lisp l_editorInserTab(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) {
|
for (int i = 0; i<E.constantes.TAB_LENGTH; ++i) {
|
||||||
editorInsertChar(' ');
|
bufferInsertChar(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
return lisp_null();
|
return lisp_null();
|
||||||
@@ -221,7 +240,7 @@ Lisp l_editorInserTab(Lisp args, LispError *e, LispContext ctx) {
|
|||||||
* @note Updates global editor state E
|
* @note Updates global editor state E
|
||||||
*/
|
*/
|
||||||
Lisp moveCursorBeginLine(Lisp args, LispError *e, LispContext ctx) {
|
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();
|
return lisp_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,8 +254,10 @@ Lisp moveCursorBeginLine(Lisp args, LispError *e, LispContext ctx) {
|
|||||||
* @note Updates global editor state E
|
* @note Updates global editor state E
|
||||||
*/
|
*/
|
||||||
Lisp moveCursorEndLine(Lisp args, LispError *e, LispContext ctx) {
|
Lisp moveCursorEndLine(Lisp args, LispError *e, LispContext ctx) {
|
||||||
if (E.cursor_y < E.numrows) {
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
E.cursor_x = E.row[E.cursor_y].size;
|
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();
|
return lisp_null();
|
||||||
}
|
}
|
||||||
@@ -251,7 +272,7 @@ Lisp moveCursorEndLine(Lisp args, LispError *e, LispContext ctx) {
|
|||||||
* @see editorDelChar()
|
* @see editorDelChar()
|
||||||
*/
|
*/
|
||||||
Lisp deletePreviousChar(Lisp args, LispError *e, LispContext ctx) {
|
Lisp deletePreviousChar(Lisp args, LispError *e, LispContext ctx) {
|
||||||
editorDelChar();
|
bufferDelChar();
|
||||||
return lisp_null();
|
return lisp_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,7 +287,8 @@ Lisp deletePreviousChar(Lisp args, LispError *e, LispContext ctx) {
|
|||||||
* @see editorMoveCursor()
|
* @see editorMoveCursor()
|
||||||
*/
|
*/
|
||||||
Lisp editorMoveCursorPageUp(Lisp args, LispError *e, LispContext ctx) {
|
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;
|
int times = E.screenrows;
|
||||||
while (--times) {
|
while (--times) {
|
||||||
editorMoveCursor(ARROW_UP);
|
editorMoveCursor(ARROW_UP);
|
||||||
@@ -285,9 +307,11 @@ Lisp editorMoveCursorPageUp(Lisp args, LispError *e, LispContext ctx) {
|
|||||||
* @see editorMoveCursor()
|
* @see editorMoveCursor()
|
||||||
*/
|
*/
|
||||||
Lisp editorMoveCursorPageDown(Lisp args, LispError *e, LispContext ctx) {
|
Lisp editorMoveCursorPageDown(Lisp args, LispError *e, LispContext ctx) {
|
||||||
E.cursor_y = E.row_offset + E.screenrows - 1;
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
if (E.cursor_y > E.numrows) {
|
struct buffer_t *buffer = bufferFindById(active->buffer_id);
|
||||||
E.cursor_y = E.numrows;
|
active->cursor_y = active->row_offset + E.screenrows - 1;
|
||||||
|
if (active->cursor_y > buffer->numrows) {
|
||||||
|
active->cursor_y = buffer->numrows;
|
||||||
}
|
}
|
||||||
int times = E.screenrows;
|
int times = E.screenrows;
|
||||||
while (--times) {
|
while (--times) {
|
||||||
@@ -311,7 +335,9 @@ Lisp editorMoveCursorPageDown(Lisp args, LispError *e, LispContext ctx) {
|
|||||||
Lisp editorOpenFile(Lisp args, LispError *e, LispContext ctx) {
|
Lisp editorOpenFile(Lisp args, LispError *e, LispContext ctx) {
|
||||||
char *filename = editorPrompt("Open : %s", getenv("PWD"), 1);
|
char *filename = editorPrompt("Open : %s", getenv("PWD"), 1);
|
||||||
if (filename){
|
if (filename){
|
||||||
editorOpen(filename);
|
// editorOpen(filename);
|
||||||
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
|
active->buffer_id = bufferCreate(filename);
|
||||||
}
|
}
|
||||||
free(filename);
|
free(filename);
|
||||||
|
|
||||||
@@ -330,7 +356,7 @@ Lisp editorOpenFile(Lisp args, LispError *e, LispContext ctx) {
|
|||||||
*/
|
*/
|
||||||
Lisp editorPrintC(Lisp args, LispError *e, LispContext ctx) {
|
Lisp editorPrintC(Lisp args, LispError *e, LispContext ctx) {
|
||||||
char c = lisp_string(lisp_car(args))[0];
|
char c = lisp_string(lisp_car(args))[0];
|
||||||
editorInsertChar(c);
|
bufferInsertChar(c);
|
||||||
return lisp_null();
|
return lisp_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,7 +401,32 @@ Lisp addPackage(Lisp args, LispError *e, LispContext ctx) {
|
|||||||
* @see editorDelRow()
|
* @see editorDelRow()
|
||||||
*/
|
*/
|
||||||
Lisp editorDelRow_L(Lisp args, LispError *e, LispContext ctx) {
|
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();
|
return lisp_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,9 +439,11 @@ Lisp editorDelRow_L(Lisp args, LispError *e, LispContext ctx) {
|
|||||||
* @return lisp_null()
|
* @return lisp_null()
|
||||||
* @see editorFind()
|
* @see editorFind()
|
||||||
*/
|
*/
|
||||||
Lisp editorFind_L(Lisp args, LispError *e, LispContext ctx) {
|
Lisp bufferFind_L(Lisp args, LispError *e, LispContext ctx) {
|
||||||
fprintf(stderr, "LispFind\n");
|
fprintf(stderr, "LispFind\n");
|
||||||
editorFind();
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
|
struct buffer_t *buffer = bufferFindById(active->buffer_id);
|
||||||
|
bufferFind(buffer);
|
||||||
return lisp_null();
|
return lisp_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,11 +458,13 @@ Lisp editorFind_L(Lisp args, LispError *e, LispContext ctx) {
|
|||||||
*/
|
*/
|
||||||
Lisp editorReadChar_L(Lisp args, LispError *e, LispContext ctx) {
|
Lisp editorReadChar_L(Lisp args, LispError *e, LispContext ctx) {
|
||||||
Lisp returned_char;
|
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');
|
returned_char = lisp_make_char('a');
|
||||||
} else {
|
} 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;
|
return returned_char;
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-28
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
#include "../include/editor_op.h"
|
#include "../include/editor_op.h"
|
||||||
#include "../include/row_op.h"
|
#include "../include/row_op.h"
|
||||||
|
#include "include/buffer.h"
|
||||||
|
#include "include/data.h"
|
||||||
|
#include "include/split_screen.h"
|
||||||
|
|
||||||
|
|
||||||
extern struct editorConfig E;
|
extern struct editorConfig E;
|
||||||
@@ -25,49 +28,55 @@ void editorSetStatusMessage(const char *fmt, ...) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void editorInsertChar(int c) {
|
void bufferInsertChar(int c) {
|
||||||
if (E.cursor_y == E.numrows) {
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
editorInsertRow(E.numrows, "", 0);
|
struct buffer_t *buf = bufferFindById(active->buffer_id);
|
||||||
|
if (active->cursor_y == buf->numrows) {
|
||||||
|
bufferInsertRow(buf, buf->numrows, "", 0);
|
||||||
}
|
}
|
||||||
editorRowInsertChar(&E.row[E.cursor_y], E.cursor_x, c);
|
bufferRowInsertChar(buf, &buf->row[active->cursor_y], active->cursor_x, c);
|
||||||
E.cursor_x++;
|
active->cursor_x++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorInsertNewLine() {
|
void bufferInsertNewLine() {
|
||||||
/*
|
/*
|
||||||
* Add new line and place the cursor at the beginning of it
|
* Add new line and place the cursor at the beginning of it
|
||||||
*/
|
*/
|
||||||
fprintf(stderr, "Inserting new line\n");
|
fprintf(stderr, "Inserting new line\n");
|
||||||
erow *row;
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
if (!E.cursor_x) {
|
struct buffer_t *buf = bufferFindById(active->buffer_id);
|
||||||
editorInsertRow(E.cursor_y, "", 0);
|
frow *row;
|
||||||
|
if (!active->cursor_x) {
|
||||||
|
bufferInsertRow(buf, active->cursor_y, "", 0);
|
||||||
} else {
|
} else {
|
||||||
row = &E.row[E.cursor_y];
|
row = &buf->row[active->cursor_y];
|
||||||
editorInsertRow(E.cursor_y + 1, &row->chars[E.cursor_x],
|
bufferInsertRow(buf, active->cursor_y + 1, &row->chars[active->cursor_x],
|
||||||
row->size - E.cursor_x);
|
row->size - active->cursor_x);
|
||||||
row = &E.row[E.cursor_y];
|
row = &buf->row[active->cursor_y];
|
||||||
row->size = E.cursor_x;
|
row->size = active->cursor_x;
|
||||||
row->chars[row->size] = '\0';
|
row->chars[row->size] = '\0';
|
||||||
editorUpdateRow(row);
|
bufferUpdatfrow(row);
|
||||||
}
|
}
|
||||||
++E.cursor_y;
|
++active->cursor_y;
|
||||||
E.cursor_x = 0;
|
active->cursor_x = 0;
|
||||||
fprintf(stderr, "Insert new line done\n");
|
fprintf(stderr, "Insert new line done\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorDelChar() {
|
void bufferDelChar() {
|
||||||
erow *row;
|
frow *row;
|
||||||
if (E.cursor_y == E.numrows || !(E.cursor_x || E.cursor_y)) {
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
|
struct buffer_t *buf = bufferFindById(active->buffer_id);
|
||||||
|
if (active->cursor_y == buf->numrows || !(active->cursor_x || active->cursor_y)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
row = &E.row[E.cursor_y];
|
row = &buf->row[active->cursor_y];
|
||||||
if (E.cursor_x > 0) {
|
if (active->cursor_x > 0) {
|
||||||
editorRowDelchar(row, E.cursor_x - 1);
|
bufferRowDelchar(buf, row, active->cursor_x - 1);
|
||||||
--E.cursor_x;
|
--active->cursor_x;
|
||||||
} else {
|
} else {
|
||||||
E.cursor_x = E.row[E.cursor_y - 1].size;
|
active->cursor_x = buf->row[active->cursor_y - 1].size;
|
||||||
editorRowAppendString(&E.row[E.cursor_y - 1], row->chars, row->size);
|
bufferRowAppendString(buf, &buf->row[active->cursor_y - 1], row->chars, row->size);
|
||||||
editorDelRow(E.cursor_y);
|
bufferDelRow(buf, active->cursor_y);
|
||||||
--E.cursor_y;
|
--active->cursor_y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+55
-53
@@ -7,8 +7,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../include/file_io.h"
|
#include "../include/file_io.h"
|
||||||
#include "../include/input.h"
|
|
||||||
#include "../include/editor_op.h"
|
#include "../include/editor_op.h"
|
||||||
|
#include "../include/input.h"
|
||||||
|
#include "include/buffer.h"
|
||||||
|
#include "include/data.h"
|
||||||
|
#include "include/split_screen.h"
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -31,77 +34,71 @@ extern struct editorConfig E;
|
|||||||
* Rows are separated by newline characters.
|
* Rows are separated by newline characters.
|
||||||
* @note Caller is responsible for freeing the returned buffer
|
* @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 tot_len = 0;
|
||||||
int j;
|
int j;
|
||||||
char *buf;
|
char *buffer;
|
||||||
char *p;
|
char *p;
|
||||||
|
|
||||||
for (j = 0; j < E.numrows; ++j) {
|
for (j = 0; j < buf->numrows; ++j) {
|
||||||
tot_len += E.row[j].size + 1;
|
tot_len += buf->row[j].size + 1;
|
||||||
}
|
}
|
||||||
*buffer_len = tot_len;
|
*buffer_len = tot_len;
|
||||||
buf = malloc(tot_len);
|
buffer = malloc(tot_len);
|
||||||
p = buf;
|
p = buffer;
|
||||||
for (j = 0; j < E.numrows; ++j) {
|
for (j = 0; j < buf->numrows; ++j) {
|
||||||
memcpy(p, E.row[j].chars, E.row[j].size);
|
memcpy(p, buf->row[j].chars, buf->row[j].size);
|
||||||
p += E.row[j].size;
|
p += buf->row[j].size;
|
||||||
*p = '\n';
|
*p = '\n';
|
||||||
p++;
|
p++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Closes the current file and resets editor state
|
* @brief Closes the current file and resets editor state
|
||||||
* @details Clears all rows, resets cursor position, scroll offsets, and file metadata.
|
* @details Clears all rows, resets cursor position, scroll offsets, and file
|
||||||
* Does not prompt to save unsaved changes.
|
* metadata. Does not prompt to save unsaved changes.
|
||||||
* @note Updates global editor state E
|
* @note Updates global editor state E
|
||||||
*/
|
*/
|
||||||
void editorCloseFile(void) {
|
void editorCloseFile(void) {
|
||||||
E.cursor_x = 0;
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
E.cursor_y = 0;
|
struct buffer_t *buf = bufferFindById(active->buffer_id);
|
||||||
E.rx = 0;
|
active->cursor_x = 0;
|
||||||
E.row_offset = 0;
|
active->cursor_y = 0;
|
||||||
E.col_offset = 0;
|
active->rx = 0;
|
||||||
for (int i = 0; i < E.numrows; ++i) {
|
active->row_offset = 0;
|
||||||
free(E.row[i].chars);
|
active->col_offset = 0;
|
||||||
free(E.row[i].render);
|
for (int i = 0; i < buf->numrows; ++i) {
|
||||||
|
free(buf->row[i].chars);
|
||||||
|
free(buf->row[i].render);
|
||||||
}
|
}
|
||||||
E.numrows = 0;
|
buf->numrows = 0;
|
||||||
free(E.row);
|
free(buf->row);
|
||||||
E.row = NULL;
|
buf->row = NULL;
|
||||||
E.dirty = 0;
|
buf->dirty = 0;
|
||||||
free(E.filename);
|
free(buf->filename);
|
||||||
E.filename = NULL;
|
buf->filename = NULL;
|
||||||
E.status_msg[0] = '\0';
|
E.status_msg[0] = '\0';
|
||||||
E.status_msg_time = 0;
|
E.status_msg_time = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Opens a file for editing
|
* @brief Opens a file for editing
|
||||||
* @details Loads file content into editor rows, one line per row. If another file
|
* @details Loads file content into editor rows, one line per row. If another
|
||||||
* is already open, it is closed first (without saving). File is opened in a+
|
* file is already open, it is closed first (without saving). File is opened in
|
||||||
* (read/append) mode to allow both reading and modification.
|
* a+ (read/append) mode to allow both reading and modification.
|
||||||
* @param filename Path to the file to open (relative or absolute)
|
* @param filename Path to the file to open (relative or absolute)
|
||||||
* @note Updates global editor state E
|
* @note Updates global editor state E
|
||||||
* @note Calls die() on file open failure
|
* @note Calls die() on file open failure
|
||||||
* @note Newline characters are stripped from loaded lines
|
* @note Newline characters are stripped from loaded lines
|
||||||
* @see editorInsertRow()
|
* @see editorInsertRow()
|
||||||
*/
|
*/
|
||||||
void editorOpen(char *filename) {
|
void editorOpen(struct buffer_t* buffer) {
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
|
|
||||||
// Test if a file is already open
|
fp = fopen(buffer->filename, "a+");
|
||||||
if (E.filename != NULL) {
|
|
||||||
editorCloseFile();
|
|
||||||
E.state = READ_AND_WRITE;
|
|
||||||
}
|
|
||||||
|
|
||||||
E.filename = strdup(filename);
|
|
||||||
|
|
||||||
fp = fopen(filename, "a+");
|
|
||||||
if (!fp)
|
if (!fp)
|
||||||
die("fopen");
|
die("fopen");
|
||||||
|
|
||||||
@@ -114,7 +111,7 @@ void editorOpen(char *filename) {
|
|||||||
(line[line_len - 1] == '\n' || line[line_len - 1] == '\r')) {
|
(line[line_len - 1] == '\n' || line[line_len - 1] == '\r')) {
|
||||||
--line_len;
|
--line_len;
|
||||||
}
|
}
|
||||||
editorInsertRow(E.numrows, line, line_len);
|
bufferInsertRow(buffer, buffer->numrows, line, line_len);
|
||||||
free(line);
|
free(line);
|
||||||
line = NULL;
|
line = NULL;
|
||||||
}
|
}
|
||||||
@@ -134,18 +131,20 @@ void editorOpen(char *filename) {
|
|||||||
* @see editorRowsToString()
|
* @see editorRowsToString()
|
||||||
*/
|
*/
|
||||||
void editorSave() {
|
void editorSave() {
|
||||||
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
|
struct buffer_t *buffer = bufferFindById(active->buffer_id);
|
||||||
int len;
|
int len;
|
||||||
char *buf;
|
char *buf;
|
||||||
int fd;
|
int fd;
|
||||||
if (E.filename == NULL) {
|
if (buffer->filename == NULL) {
|
||||||
E.filename = editorPrompt("Save as: %s (ESC to cancel)", "", 1);
|
buffer->filename = editorPrompt("Save as: %s (ESC to cancel)", "", 1);
|
||||||
if (E.filename == NULL) {
|
if (buffer->filename == NULL) {
|
||||||
editorSetStatusMessage("Save aborted");
|
editorSetStatusMessage("Save aborted");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buf = editorRowsToString(&len);
|
buf = bufferRowsToString(buffer, &len);
|
||||||
fd = open(E.filename, O_RDWR | O_CREAT, 0644);
|
fd = open(buffer->filename, O_RDWR | O_CREAT, 0644);
|
||||||
if (fd != -1) {
|
if (fd != -1) {
|
||||||
if (ftruncate(fd, len) != -1) {
|
if (ftruncate(fd, len) != -1) {
|
||||||
if (write(fd, buf, len) == len) {
|
if (write(fd, buf, len) == len) {
|
||||||
@@ -172,18 +171,21 @@ void editorSave() {
|
|||||||
* @see editorPrompt()
|
* @see editorPrompt()
|
||||||
* @see editorRowRxToCx()
|
* @see editorRowRxToCx()
|
||||||
*/
|
*/
|
||||||
void editorFind() {
|
void bufferFind(struct buffer_t *buf) {
|
||||||
fprintf(stderr, "searching\n");
|
fprintf(stderr, "searching\n");
|
||||||
char *query = editorPrompt("Search: %s (ESC to cancel)", "", 0);
|
char *query = editorPrompt("Search: %s (ESC to cancel)", "", 0);
|
||||||
if (query == NULL) return;
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
|
|
||||||
|
if (query == NULL)
|
||||||
|
return;
|
||||||
int i;
|
int i;
|
||||||
for (i = E.cursor_y + 1; i < E.numrows; i++) {
|
for (i = active->cursor_y + 1; i < buf->numrows; i++) {
|
||||||
erow *row = &E.row[i];
|
frow *row = &buf->row[i];
|
||||||
char *match = strstr(row->render, query);
|
char *match = strstr(row->render, query);
|
||||||
if (match) {
|
if (match) {
|
||||||
E.cursor_y = i;
|
active->cursor_y = i;
|
||||||
E.cursor_x = editorRowRxToCx(row, match - row->render);
|
active->cursor_x = bufferRowRxToCx(row, match - row->render);
|
||||||
E.row_offset = E.numrows;
|
buf->row_offset = buf->numrows;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-26
@@ -3,6 +3,7 @@
|
|||||||
#include "../include/color.h"
|
#include "../include/color.h"
|
||||||
#include "../include/data.h"
|
#include "../include/data.h"
|
||||||
#include "../include/terminal.h"
|
#include "../include/terminal.h"
|
||||||
|
#include "include/split_screen.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -34,11 +35,15 @@ void initBuiltins() {
|
|||||||
registerBuiltin("editor-insert-char", editorPrintC);
|
registerBuiltin("editor-insert-char", editorPrintC);
|
||||||
registerBuiltin("add-package", addPackage);
|
registerBuiltin("add-package", addPackage);
|
||||||
registerBuiltin("editor-del-row", editorDelRow_L);
|
registerBuiltin("editor-del-row", editorDelRow_L);
|
||||||
registerBuiltin("editor-find", editorFind_L);
|
registerBuiltin("buffer-find", bufferFind_L);
|
||||||
registerBuiltin("editor-read-char", editorReadChar_L);
|
registerBuiltin("editor-read-char", editorReadChar_L);
|
||||||
registerBuiltin("add-prefix", editorPrefix);
|
registerBuiltin("add-prefix", editorPrefix);
|
||||||
registerBuiltin("editor-set-prefix", editorSetPrefix);
|
registerBuiltin("editor-set-prefix", editorSetPrefix);
|
||||||
registerBuiltin("editor-insert-tab", l_editorInserTab);
|
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 initConfig() {
|
void initConfig() {
|
||||||
@@ -80,37 +85,29 @@ void init_theme() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void initEditor() {
|
void initEditor() {
|
||||||
// Init graphics variables
|
|
||||||
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;
|
|
||||||
// File relative variables
|
|
||||||
E.dirty = 0;
|
|
||||||
E.filename = NULL;
|
|
||||||
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");
|
|
||||||
E.state = READ_ONLY;
|
|
||||||
|
|
||||||
// Status bar
|
|
||||||
E.status_msg[0] = '\0';
|
|
||||||
E.status_msg_time = 0;
|
|
||||||
if (getWindowSize(&E.screenrows, &E.screencols) == -1) {
|
if (getWindowSize(&E.screenrows, &E.screencols) == -1) {
|
||||||
die("getWindowSize");
|
die("getWindowSize");
|
||||||
}
|
}
|
||||||
E.screenrows -= 2;
|
E.screenrows -= 2;
|
||||||
|
|
||||||
E.screen_layout =
|
|
||||||
(struct buffer_t ***)malloc(E.screenrows * sizeof(struct buffer_t **));
|
// Init graphics variables
|
||||||
for (int i = 0; i < E.screenrows; ++i) {
|
splitScreenInit();
|
||||||
E.screen_layout[i] =
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
(struct buffer_t **)malloc(E.screencols * sizeof(struct buffer_t *));
|
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
|
// Key binds
|
||||||
E.number_of_keybinds = 0;
|
E.number_of_keybinds = 0;
|
||||||
|
|||||||
+33
-26
@@ -2,6 +2,9 @@
|
|||||||
#include "../include/define.h"
|
#include "../include/define.h"
|
||||||
#include "../include/editor_op.h"
|
#include "../include/editor_op.h"
|
||||||
#include "../include/output.h"
|
#include "../include/output.h"
|
||||||
|
#include "include/buffer.h"
|
||||||
|
#include "include/data.h"
|
||||||
|
#include "include/split_screen.h"
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -42,7 +45,8 @@ const char *file_completion(const char *path) {
|
|||||||
|
|
||||||
// path is a directory
|
// path is a directory
|
||||||
if (path[strlen(path) - 1] == '/') {
|
if (path[strlen(path) - 1] == '/') {
|
||||||
return path;
|
fprintf(stderr, "[FILE COMP] is dir\n");
|
||||||
|
strncpy(directory, path, 128);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find dir name
|
// Find dir name
|
||||||
@@ -56,12 +60,13 @@ const char *file_completion(const char *path) {
|
|||||||
predict[predict_len] = '\0';
|
predict[predict_len] = '\0';
|
||||||
fprintf(stderr, "%s %s\n", directory, predict);
|
fprintf(stderr, "%s %s\n", directory, predict);
|
||||||
} else {
|
} else {
|
||||||
return NULL;
|
fprintf(stderr, "[FILE COMP] dir not found\n");
|
||||||
|
return strdup(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
dir = opendir(directory);
|
dir = opendir(directory);
|
||||||
if (!dir)
|
if (!dir)
|
||||||
return NULL;
|
return strdup(path);
|
||||||
|
|
||||||
while ((entry = readdir(dir)) != NULL) {
|
while ((entry = readdir(dir)) != NULL) {
|
||||||
if (strncmp(entry->d_name, predict, predict_len) == 0) {
|
if (strncmp(entry->d_name, predict, predict_len) == 0) {
|
||||||
@@ -82,7 +87,8 @@ const char *file_completion(const char *path) {
|
|||||||
closedir(dir);
|
closedir(dir);
|
||||||
dir = NULL;
|
dir = NULL;
|
||||||
free(entry);
|
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
|
* @note Updates global editor state E
|
||||||
*/
|
*/
|
||||||
int editorMoveCursor(int key) {
|
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 row_len;
|
||||||
|
int x = active->cursor_x + active->col_offset;
|
||||||
|
int y = active->cursor_y + active->row_offset;
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case ARROW_RIGHT:
|
case ARROW_RIGHT:
|
||||||
if (row && E.cursor_x < row->size) {
|
if (row && x < row->size) {
|
||||||
++E.cursor_x;
|
active->cursor_x++;
|
||||||
} else if (row && E.cursor_x == row->size) {
|
|
||||||
E.cursor_y++;
|
} else if (row && y == row->size) {
|
||||||
E.cursor_x = 0;
|
active->cursor_y++;
|
||||||
|
active->cursor_x = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ARROW_DOWN:
|
case ARROW_DOWN:
|
||||||
if (E.cursor_y < E.numrows) {
|
if (y < buf->numrows) {
|
||||||
++E.cursor_y;
|
|
||||||
|
active->cursor_y++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ARROW_UP:
|
case ARROW_UP:
|
||||||
if (E.cursor_y != 0) {
|
if (y != 0) {
|
||||||
--E.cursor_y;
|
--active->cursor_y;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ARROW_LEFT:
|
case ARROW_LEFT:
|
||||||
if (E.cursor_x != 0) {
|
if (x != 0) {
|
||||||
--E.cursor_x;
|
--active->cursor_x;
|
||||||
} else if (E.cursor_y > 0) {
|
} else if (y > 0) {
|
||||||
--E.cursor_y;
|
--active->cursor_y;
|
||||||
E.cursor_x = E.row[E.cursor_y].size;
|
active->cursor_x = buf->row[y].size;
|
||||||
}
|
}
|
||||||
break;
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,6 +334,6 @@ void editorProcessKeypress() {
|
|||||||
if (executeKeyBind(key_to_string(c))) {
|
if (executeKeyBind(key_to_string(c))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
editorInsertChar(c);
|
bufferInsertChar(c);
|
||||||
E.quit_times_buffer = E.constantes.QUIT_TIMES;
|
E.quit_times_buffer = E.constantes.QUIT_TIMES;
|
||||||
}
|
}
|
||||||
|
|||||||
+176
-59
@@ -7,6 +7,11 @@
|
|||||||
|
|
||||||
#include "../include/output.h"
|
#include "../include/output.h"
|
||||||
#include "../include/append_buffer.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 "../include/syntax_highlighter.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -16,60 +21,128 @@
|
|||||||
extern struct editorConfig E;
|
extern struct editorConfig E;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Renders all visible rows to the screen buffer
|
* @brief Renders a single pane with its buffer content
|
||||||
* @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()
|
|
||||||
*/
|
*/
|
||||||
void editorDrawRows(struct abuf *ab) {
|
static void editorDrawPane(struct abuf *ab, EditorPane *pane) {
|
||||||
int y;
|
if (pane == NULL || pane->buffer_id < 0)
|
||||||
char welcome[80];
|
return;
|
||||||
int welcome_len;
|
|
||||||
int padding;
|
struct buffer_t *buf = bufferFindById(pane->buffer_id);
|
||||||
int len;
|
if (buf == NULL)
|
||||||
int file_row;
|
return;
|
||||||
for (y = 0; y < E.screenrows; ++y) {
|
|
||||||
file_row = y + E.row_offset;
|
// 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));
|
abAppend(ab, E.theme.BACKGROUND_COLOR, strlen(E.theme.BACKGROUND_COLOR));
|
||||||
if (file_row >= E.numrows) {
|
|
||||||
if (E.numrows == 0 && y == E.screenrows / 3) {
|
int chars_printed = 0;
|
||||||
welcome_len =
|
|
||||||
snprintf(welcome, sizeof(welcome),
|
if (file_row >= buf->numrows) {
|
||||||
"Beluga text editor -- version %s", BELUGA_VERSION);
|
// Empty line - show tilde
|
||||||
if (welcome_len > E.screencols) {
|
if (buf->numrows == 0 && y == pane->height / 3) {
|
||||||
welcome_len = E.screencols;
|
char welcome[80];
|
||||||
}
|
int welcome_len =
|
||||||
padding = (E.screencols - welcome_len) / 2;
|
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) {
|
if (padding) {
|
||||||
abAppend(ab, "~", 1);
|
abAppend(ab, "~", 1);
|
||||||
--padding;
|
chars_printed++;
|
||||||
|
padding--;
|
||||||
}
|
}
|
||||||
while (padding--) {
|
while (padding-- && chars_printed < pane->width) {
|
||||||
abAppend(ab, " ", 1);
|
abAppend(ab, " ", 1);
|
||||||
|
chars_printed++;
|
||||||
}
|
}
|
||||||
abAppend(ab, welcome, welcome_len);
|
abAppend(ab, welcome, welcome_len);
|
||||||
|
chars_printed += welcome_len;
|
||||||
} else {
|
} else {
|
||||||
abAppend(ab, "~", 1);
|
abAppend(ab, "~", 1);
|
||||||
|
chars_printed++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
len = E.row[file_row].rsize - E.col_offset;
|
// Render line with syntax highlighting, constrain to pane width
|
||||||
if (len < 0)
|
int start_offset = pane->col_offset;
|
||||||
len = 0;
|
int visible_len = buf->row[file_row].rsize - start_offset;
|
||||||
if (len > E.screencols)
|
if (visible_len < 0)
|
||||||
len = E.screencols;
|
visible_len = 0;
|
||||||
char *highlighted = highlight_line(&E.row[file_row].render[E.col_offset],
|
if (visible_len > pane->width)
|
||||||
&E.row[file_row].rsize);
|
visible_len = pane->width;
|
||||||
abAppend(ab, highlighted, E.row[file_row].rsize);
|
|
||||||
|
|
||||||
|
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);
|
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));
|
abAppend(ab, E.theme.BACKGROUND_COLOR, strlen(E.theme.BACKGROUND_COLOR));
|
||||||
abAppend(ab, ERASE_END_LINE, 3);
|
while (chars_printed < pane->width) {
|
||||||
abAppend(ab, "\r\n", 2);
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,22 +154,33 @@ void editorDrawRows(struct abuf *ab) {
|
|||||||
* @see editorRowCxToRx()
|
* @see editorRowCxToRx()
|
||||||
*/
|
*/
|
||||||
void editorScroll() {
|
void editorScroll() {
|
||||||
E.rx = E.cursor_x;
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
if (E.cursor_y < E.numrows) {
|
struct buffer_t *buf = bufferFindById(active->buffer_id);
|
||||||
E.rx = editorRowCxToRx(&E.row[E.cursor_y], E.cursor_x);
|
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) {
|
if (active->cursor_y < 0) {
|
||||||
E.row_offset = E.cursor_y;
|
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 (active->cursor_y == active->height) {
|
||||||
}
|
|
||||||
if (E.rx >= E.col_offset + E.screencols) {
|
active->cursor_y--;
|
||||||
E.col_offset = E.rx - E.screencols + 1;
|
active->row_offset++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,16 +195,39 @@ void editorScroll() {
|
|||||||
void editorDrawStatusBar(struct abuf *ab) {
|
void editorDrawStatusBar(struct abuf *ab) {
|
||||||
int len, render_len;
|
int len, render_len;
|
||||||
char status[80], render_status[80];
|
char status[80], render_status[80];
|
||||||
|
EditorPane *active = splitScreenGetActivePane();
|
||||||
|
struct buffer_t *buf = bufferFindById(active->buffer_id);
|
||||||
|
|
||||||
abAppend(ab, "\x1b[7m", 4); // inverting colors
|
abAppend(ab, "\x1b[7m", 4); // inverting colors
|
||||||
len = snprintf(status, sizeof(status), "%.20s - %d lines%s",
|
|
||||||
E.filename ? E.filename : "[No Name]", E.numrows,
|
const char *mode_str = "";
|
||||||
E.dirty ? "*" : "");
|
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",
|
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) {
|
if (len > E.screencols) {
|
||||||
len = E.screencols;
|
len = E.screencols;
|
||||||
}
|
}
|
||||||
|
|
||||||
abAppend(ab, status, len);
|
abAppend(ab, status, len);
|
||||||
while (len < E.screencols) {
|
while (len < E.screencols) {
|
||||||
if (E.screencols - len == render_len) {
|
if (E.screencols - len == render_len) {
|
||||||
@@ -131,6 +238,7 @@ void editorDrawStatusBar(struct abuf *ab) {
|
|||||||
++len;
|
++len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abAppend(ab, "\x1b[m", 3); // normal text mode
|
abAppend(ab, "\x1b[m", 3); // normal text mode
|
||||||
abAppend(ab, "\r\n", 2);
|
abAppend(ab, "\r\n", 2);
|
||||||
}
|
}
|
||||||
@@ -163,23 +271,32 @@ void editorDrawMessageBar(struct abuf *ab) {
|
|||||||
* @see editorDrawStatusBar()
|
* @see editorDrawStatusBar()
|
||||||
* @see editorDrawMessageBar()
|
* @see editorDrawMessageBar()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void editorRefreshScreen() {
|
void editorRefreshScreen() {
|
||||||
editorScroll();
|
|
||||||
struct abuf ab = ABUF_INIT;
|
struct abuf ab = ABUF_INIT;
|
||||||
char buf[32];
|
char buf[32];
|
||||||
|
|
||||||
|
|
||||||
abAppend(&ab, HIDE_CURSOR, 6);
|
abAppend(&ab, HIDE_CURSOR, 6);
|
||||||
abAppend(&ab, CURSOR_TOP_LEFT, 3);
|
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);
|
||||||
|
|
||||||
editorDrawRows(&ab);
|
// Draw status bar and message bar
|
||||||
editorDrawStatusBar(&ab);
|
editorDrawStatusBar(&ab);
|
||||||
editorDrawMessageBar(&ab);
|
editorDrawMessageBar(&ab);
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf), "\x1b[%d;%dH", (E.cursor_y - E.row_offset) + 1,
|
// Position cursor in active pane
|
||||||
(E.rx - E.col_offset) + 1);
|
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, buf, strlen(buf));
|
||||||
|
}
|
||||||
|
|
||||||
abAppend(&ab, SHOW_CURSOR, 6);
|
abAppend(&ab, SHOW_CURSOR, 6);
|
||||||
write(STDOUT_FILENO, ab.b, ab.len);
|
write(STDOUT_FILENO, ab.b, ab.len);
|
||||||
|
|||||||
+38
-37
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
extern struct editorConfig E;
|
extern struct editorConfig E;
|
||||||
|
|
||||||
int editorRowCxToRx(erow *row, int cursor_x) {
|
int bufferRowCxToRx(frow *row, int cursor_x) {
|
||||||
int render_x = 0;
|
int render_x = 0;
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < cursor_x; ++i) {
|
for (i = 0; i < cursor_x; ++i) {
|
||||||
@@ -18,7 +18,7 @@ int editorRowCxToRx(erow *row, int cursor_x) {
|
|||||||
return render_x;
|
return render_x;
|
||||||
}
|
}
|
||||||
|
|
||||||
int editorRowRxToCx(erow *row, int rx) {
|
int bufferRowRxToCx(frow *row, int rx) {
|
||||||
int cur_rx = 0;
|
int cur_rx = 0;
|
||||||
int cx;
|
int cx;
|
||||||
for (cx = 0; cx < row->size; 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.
|
* \brief Copy content of \p row in \p row->render.
|
||||||
* */
|
* */
|
||||||
|
|
||||||
void editorUpdateRow(erow *row) {
|
void bufferUpdatfrow(frow *row) {
|
||||||
int i, i_render;
|
int i, i_render;
|
||||||
int tabs = 0;
|
int tabs = 0;
|
||||||
|
|
||||||
@@ -68,52 +68,53 @@ void editorUpdateRow(erow *row) {
|
|||||||
row->rsize = i_render;
|
row->rsize = i_render;
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorInsertRow(int at, char *s, size_t len) {
|
void bufferInsertRow(struct buffer_t *buffer, int at, char *s, size_t len) {
|
||||||
if (at < 0 || at > E.numrows) {
|
|
||||||
|
if (at < 0 || at > buffer->numrows) {
|
||||||
|
|
||||||
return;
|
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) {
|
if (!tmp) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
E.row = tmp;
|
buffer->row = tmp;
|
||||||
memmove(&E.row[at + 1], &E.row[at], sizeof(erow) * (E.numrows - at));
|
memmove(&buffer->row[at + 1], &buffer->row[at], sizeof(frow) * (buffer->numrows - at));
|
||||||
|
|
||||||
E.row[at].size = len;
|
buffer->row[at].size = len;
|
||||||
E.row[at].chars = malloc(len + 1);
|
buffer->row[at].chars = malloc(len + 1);
|
||||||
memcpy(E.row[at].chars, s, len);
|
memcpy(buffer->row[at].chars, s, len);
|
||||||
E.row[at].chars[len] = '\0';
|
buffer->row[at].chars[len] = '\0';
|
||||||
|
|
||||||
E.row[at].rsize = 0;
|
buffer->row[at].rsize = 0;
|
||||||
E.row[at].render = NULL;
|
buffer->row[at].render = NULL;
|
||||||
editorUpdateRow(&E.row[at]);
|
bufferUpdatfrow(&buffer->row[at]);
|
||||||
|
|
||||||
++E.numrows;
|
++buffer->numrows;
|
||||||
++E.dirty;
|
++buffer->dirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorFreeRow(erow *row) {
|
void bufferFrefrow(frow *row) {
|
||||||
free(row->render);
|
free(row->render);
|
||||||
free(row->chars);
|
free(row->chars);
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorDelRow(int at) {
|
void bufferDelRow(struct buffer_t *buffer, int at) {
|
||||||
if (at < 0 || at >= E.numrows) {
|
if (at < 0 || at >= buffer->numrows) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
editorFreeRow(&E.row[at]);
|
bufferFrefrow(&buffer->row[at]);
|
||||||
memmove(&E.row[at], &E.row[at + 1], sizeof(erow) * (E.numrows - at - 1));
|
memmove(&buffer->row[at], &buffer->row[at + 1], sizeof(frow) * (buffer->numrows - at - 1));
|
||||||
--E.numrows;
|
--buffer->numrows;
|
||||||
++E.dirty;
|
++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 */
|
* \param at Index of where we want to insert the char */
|
||||||
|
|
||||||
void editorRowInsertChar(erow *row, int at, int c) {
|
void bufferRowInsertChar(struct buffer_t *buffer, frow *row, int at, int c) {
|
||||||
if (E.state == READ_ONLY)
|
if (buffer->state == READ_ONLY)
|
||||||
return;
|
return;
|
||||||
if (at < 0 || at > row->size) {
|
if (at < 0 || at > row->size) {
|
||||||
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);
|
memmove(&row->chars[at + 1], &row->chars[at], row->size - at + 1);
|
||||||
++row->size;
|
++row->size;
|
||||||
row->chars[at] = c;
|
row->chars[at] = c;
|
||||||
editorUpdateRow(row);
|
bufferUpdatfrow(row);
|
||||||
++E.dirty;
|
++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);
|
row->chars = realloc(row->chars, row->size + len + 1);
|
||||||
memcpy(&row->chars[row->size], s, len);
|
memcpy(&row->chars[row->size], s, len);
|
||||||
row->size += len;
|
row->size += len;
|
||||||
row->chars[row->size] = '\0';
|
row->chars[row->size] = '\0';
|
||||||
editorUpdateRow(row);
|
bufferUpdatfrow(row);
|
||||||
++E.dirty;
|
++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
|
* \brief Delete the a char at the chosen position on the given row
|
||||||
* \param at Index of the char to delete
|
* \param at Index of the char to delete
|
||||||
* \param row Row on operation is made */
|
* \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) {
|
if (at < 0 || at >= row->size) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
memmove(&row->chars[at], &row->chars[at + 1], row->size - at);
|
memmove(&row->chars[at], &row->chars[at + 1], row->size - at);
|
||||||
--row->size;
|
--row->size;
|
||||||
editorUpdateRow(row);
|
bufferUpdatfrow(row);
|
||||||
++E.dirty;
|
++buffer->dirty;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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];
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user