Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c06c820dfb | |||
| 5588b0a8d7 | |||
| 419e924650 | |||
| 6a201b3ebc | |||
| 1d253e51ef | |||
| 42f82e2e0d | |||
| fa32f4b177 | |||
| 65f997e964 | |||
| 7eaf6913cb | |||
| 8f7dcf3534 | |||
| d8c6b9ace3 | |||
| d0173d7308 | |||
| 40fc234eeb | |||
| 756deba83e | |||
| 85e8067e41 | |||
| 09d78f48e9 | |||
| ce94e9fb87 | |||
| 29a92ce904 | |||
| 9348ae668a | |||
| 9157b94398 | |||
| 3b6c60a49e | |||
| 53d6572c8c | |||
| d083948dfe | |||
| 09ef5c0f3b | |||
| e4691669b8 | |||
| 27ae0a684f | |||
| 3505084527 | |||
| 02d7f27ec3 | |||
| 7dded62db9 | |||
| 6cd79b5c76 | |||
| d9aab06c1c | |||
| 8844d2f064 | |||
| d8fc7d2d67 | |||
| ab482df604 | |||
| be31e83fb9 | |||
| 54db6321ad | |||
| ec0eba849a | |||
| 2ca64fae41 | |||
| 8ce621dfde | |||
| 91e247d1de | |||
| 72178adebb | |||
| 7a98f89531 | |||
| 3e562c1071 | |||
| bff3f84ecd |
@@ -0,0 +1,35 @@
|
|||||||
|
name: Build project
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ["main"]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
tokens: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y meson ninja-build gcc
|
||||||
|
|
||||||
|
- name: Configure Meson
|
||||||
|
run: meson setup build
|
||||||
|
|
||||||
|
- name: Build project
|
||||||
|
run: meson compile -C build
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: ctest --test-dir build || true
|
||||||
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: beluga
|
||||||
|
path: build/beluga
|
||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
tmp/*
|
build/*
|
||||||
bin/*
|
|
||||||
doc/*
|
doc/*
|
||||||
|
beluga.wiki/*
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
##
|
|
||||||
# TEST
|
|
||||||
#
|
|
||||||
# @file
|
|
||||||
# @version 0.1
|
|
||||||
|
|
||||||
BELUGA_OUTPUT=bin
|
|
||||||
|
|
||||||
BUILD_FLAGS=-Wall -Wextra -pedantic
|
|
||||||
|
|
||||||
build: main.c src/*
|
|
||||||
if [ ! -d $(BELUGA_OUTPUT) ]; then mkdir $(BELUGA_OUTPUT); fi
|
|
||||||
$(CC) main.c -o $(BELUGA_OUTPUT)/beluga src/* $(BUILD_FLAGS)
|
|
||||||
|
|
||||||
DEBUG_FLAGS=-Wall -Wextra -pedantic -Werror -fsanitize=address -g
|
|
||||||
|
|
||||||
debug: main.c src/*
|
|
||||||
if [ ! -d $(BELUGA_OUTPUT) ]; then mkdir $(BELUGA_OUTPUT); fi
|
|
||||||
$(CC) main.c -o $(BELUGA_OUTPUT)/beluga src/* $(DEBUG_FLAGS)
|
|
||||||
|
|
||||||
doc:
|
|
||||||
if [ ! -d doc/ ]; then mkdir doc; fi
|
|
||||||
doxygen
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -r $(BELUGA_OUTPUT)
|
|
||||||
rm -rf doc/
|
|
||||||
rm -rf tmp/
|
|
||||||
|
|
||||||
all: build doc
|
|
||||||
|
|
||||||
# end
|
|
||||||
@@ -1,26 +1,33 @@
|
|||||||
# Beluga
|
# Beluga
|
||||||
|
|
||||||
Beluga is a project of CLI text editor that will fit perfectly with your azerty keyboard.
|
Beluga is a project of CLI text editor that uses lisp as configuration language.
|
||||||
|
It's abviously only working for **Linux**.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
You will only need **make** or **gcc** to compile the editor.
|
You will only need **meson** and a **C compiler** to compile the editor.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
### From source
|
||||||
|
Here is the installation line for development version:
|
||||||
|
|
||||||
Here is the installation line :
|
```git clone https://homelinuxserver.ddns.net/git/arthur/beluga.git ~/.beluga && cd ~/.beluga && meson setup build && meson compile -C build```
|
||||||
|
|
||||||
```git clone --branch V1.0 --single-branch https://github.com/le-cocotier/beluga.git ~/.beluga && cd ~/.beluga && make build```
|
The executable file will be `build/beluga`. Feel free to add it to your path.
|
||||||
|
|
||||||
The executable file will be in `bin/beluga`. Feel free to add it to your path.
|
### From installation script ( prefered )
|
||||||
|
|
||||||
You can either run `make all` if you're interested by the doxygen documentation.
|
Just clone the repo and execute the script `install.sh`. It will automatically add beluga to your path.
|
||||||
|
|
||||||
## Getting start
|
## Getting start
|
||||||
|
|
||||||
To open an existing file just type :
|
To open an existing file just type :
|
||||||
```beluga path_to_my_file```
|
```./build/beluga path_to_my_file```
|
||||||
|
|
||||||
The only keybinds that you will need will be :
|
Here is some few command that you will need :
|
||||||
- Ctrl-Q : leave the editor
|
|
||||||
- Ctrl-S : Save a file
|
| keybind| command |
|
||||||
|
|--------|------------------|
|
||||||
|
| Ctrl-Q | leave the editor |
|
||||||
|
| Ctrl-S | Save a file |
|
||||||
|
| Ctrl-O | open file |
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
**#%#*****###%**
|
||||||
|
*##+--------------------=##*
|
||||||
|
#*=----------------------------=*#*
|
||||||
|
#*------------------------------------*#
|
||||||
|
%+----------------------------------------=#*
|
||||||
|
#+---------------------------------------------##
|
||||||
|
*#-------------------------------------------------=##
|
||||||
|
*#----------------------------------------------------:-##
|
||||||
|
#----------------------------------------------------------##
|
||||||
|
#=--------------------------------------------------------------##
|
||||||
|
+--------------------------+@#-%*-----------------------------------#*
|
||||||
|
+--------------------------%@@@@#-------------------------------------** BELUGA - VERSION 1.1
|
||||||
|
*-=-------------------------#@@*---------------------------------------=%
|
||||||
|
%*#==--------------------------------------------------------------------+# ----- KEY-BINDS -----
|
||||||
|
*%%=-=--------------------------------------------------------------------=# CTRL-q leave
|
||||||
|
%=--------------------------------------------------------------------------#* CTRL-s save
|
||||||
|
%-----------------------------------------------------------------------------** CTRL-o open-file
|
||||||
|
*+--=---===----=---------------=*-----------------------------------------------**
|
||||||
|
#--=## *#%#*+==----==+**+----------------------= ***=---------------------%
|
||||||
|
*%**=-----------==== ==---------------------------------=+#+-----------------=#
|
||||||
|
*%=----------------------------------------------------------=#*---------------#
|
||||||
|
##=----------------------------------=------------------------+%=------------+#
|
||||||
|
#%+---------------------------------=*------------------------+%------------#
|
||||||
|
*#%*=-------------=-----------------#-------------------------#+----------#
|
||||||
|
**#%#*******+=======-------------#=------------------------#----------#
|
||||||
|
#===#*=======------------------#*----=-----------=--=##*-----------#
|
||||||
|
-====##=------------------------*%+------------=*#+=====----------#
|
||||||
|
--=====+#*=----------------------=-=+*#####***+=======-----------=*
|
||||||
|
%------=====*%*=-------------------------========-----------------+*
|
||||||
|
*-=--------====%%###+=--------------------------=-----------------#
|
||||||
|
#-----------=% +*##%%%%%%@@%%%%####*==---------------------**
|
||||||
|
%=-------#* #%*=-----------------+#
|
||||||
|
*%+--=## ##=-----------------=#*
|
||||||
|
** #+----=-------------------#*
|
||||||
|
%+----------------------------#*
|
||||||
|
*%-------------==----------------+#
|
||||||
|
##--------------==------------------#
|
||||||
|
*#--------------===%-----------------=%
|
||||||
|
##---------------=-##*-----------------+#
|
||||||
|
*#---------------==#+=#%-----------------%
|
||||||
|
*%---------------+# %*---------------#*
|
||||||
|
*#------------=+#* #%*=-----------#*
|
||||||
|
#****##****** *#%%##+=----%
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
;; MACROS
|
||||||
|
|
||||||
|
(define TAB-LENGTH 4)
|
||||||
|
(define QUIT-TIMES 1)
|
||||||
|
|
||||||
|
;; FUNCTIONS
|
||||||
|
|
||||||
|
(define editor-delete-next-char (lambda () (
|
||||||
|
(move-cursor "right")
|
||||||
|
(editor-delete-previous-char)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
;; KEY MAPPING
|
||||||
|
|
||||||
|
(map-key "CTRL-q" editor-quit)
|
||||||
|
(map-key "CTRL-d" editor-save)
|
||||||
|
(map-key "ARROW-UP" '(move-cursor "up"))
|
||||||
|
(map-key "ARROW-DOWN" '(move-cursor "down"))
|
||||||
|
(map-key "ARROW-RIGHT" '(move-cursor "right"))
|
||||||
|
(map-key "ARROW-LEFT" '(move-cursor "left"))
|
||||||
|
(map-key "ENTER" editor-insert-new-line)
|
||||||
|
(map-key "CTRL-a" move-cursor-beg-line)
|
||||||
|
(map-key "CTRL-e" move-cursor-end-line)
|
||||||
|
(map-key "BACKSPACE" editor-delete-previous-char)
|
||||||
|
(map-key "DEL" editor-delete-next-char)
|
||||||
|
(map-key "PAGE-UP" move-cursor-page-up)
|
||||||
|
(map-key "PAGE-DOWN" move-cursor-page-down)
|
||||||
|
(map-key "CTRL-o" editor-open-file)
|
||||||
|
(map-key "CTRL-k" editor-del-row)
|
||||||
|
(map-key "CTRL-s" editor-find)
|
||||||
|
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#ifndef BUILTINS_H_
|
||||||
|
#define BUILTINS_H_
|
||||||
|
|
||||||
|
#include "lisp.h"
|
||||||
|
|
||||||
|
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 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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
#ifndef DATA_H_
|
#ifndef DATA_H_
|
||||||
#define DATA_H_
|
#define DATA_H_
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "lisp.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \struct erow
|
* \struct erow
|
||||||
* \brief Store one editor row
|
* \brief Store one editor row
|
||||||
@@ -17,6 +20,23 @@ typedef struct erow {
|
|||||||
char *render; /**< The actual line we will print */
|
char *render; /**< The actual line we will print */
|
||||||
} erow;
|
} erow;
|
||||||
|
|
||||||
|
enum editorStatus_e {
|
||||||
|
IDLE,
|
||||||
|
READ_ONLY,
|
||||||
|
READ_AND_WRITE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct const_t {
|
||||||
|
int TAB_LENGTH;
|
||||||
|
int QUIT_TIMES;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct keyBind_t {
|
||||||
|
char *key_sequence;
|
||||||
|
Lisp command;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \struct editorConfig
|
* \struct editorConfig
|
||||||
* \brief Containing our editor state.
|
* \brief Containing our editor state.
|
||||||
@@ -32,9 +52,23 @@ struct editorConfig {
|
|||||||
erow *row; /**< Store all the rows printed */
|
erow *row; /**< Store all the rows printed */
|
||||||
int dirty;
|
int dirty;
|
||||||
char *filename;
|
char *filename;
|
||||||
|
enum editorStatus_e state;
|
||||||
char status_msg[80];
|
char status_msg[80];
|
||||||
time_t status_msg_time;
|
time_t status_msg_time;
|
||||||
struct termios orig_termios; /**< Terminal communication interface */
|
struct termios orig_termios; /**< Terminal communication interface */
|
||||||
|
|
||||||
|
struct const_t constantes;
|
||||||
|
int quit_times_buffer;
|
||||||
|
|
||||||
|
FILE *fd_init_file;
|
||||||
|
Lisp env;
|
||||||
|
LispContext ctx; /** Lisp context */
|
||||||
|
Lisp ctx_data; /** Lisp data context */
|
||||||
|
LispError ctx_error; /** Lisp ctx error */
|
||||||
|
|
||||||
|
struct keyBind_t* key_binds;
|
||||||
|
int number_of_keybinds;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,4 +81,7 @@ struct abuf {
|
|||||||
int len; /**< Length of the text */
|
int len; /**< Length of the text */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern struct editorConfig E;
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+1
-3
@@ -24,8 +24,6 @@ enum editorKey {
|
|||||||
|
|
||||||
#define ABUF_INIT {NULL, 0}
|
#define ABUF_INIT {NULL, 0}
|
||||||
|
|
||||||
#define BELUGA_VERSION "1.0"
|
#define BELUGA_VERSION "1.1"
|
||||||
#define TAB_LENGTH 4
|
|
||||||
#define QUIT_TIMES 1
|
|
||||||
|
|
||||||
#endif // DEFINE_H_
|
#endif // DEFINE_H_
|
||||||
|
|||||||
+3
-3
@@ -2,10 +2,10 @@
|
|||||||
#define EDITOR_OP_H_
|
#define EDITOR_OP_H_
|
||||||
|
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
void editorInsertChar(struct editorConfig *E, int c);
|
void editorInsertChar(int c);
|
||||||
|
|
||||||
void editorInsertNewLine(struct editorConfig *E);
|
void editorInsertNewLine();
|
||||||
|
|
||||||
void editorDelChar(struct editorConfig *E);
|
void editorDelChar();
|
||||||
|
|
||||||
#endif // EDITOR_OP_H_
|
#endif // EDITOR_OP_H_
|
||||||
|
|||||||
+8
-3
@@ -8,10 +8,15 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
char *editorRowsToString(struct editorConfig *E, int *buffer_len);
|
char *editorRowsToString(int *buffer_len);
|
||||||
|
|
||||||
void editorOpen(struct editorConfig *E, char *filename);
|
|
||||||
|
|
||||||
void editorSave(struct editorConfig *E);
|
void editorCloseFile(void);
|
||||||
|
|
||||||
|
void editorOpen(char *filename);
|
||||||
|
|
||||||
|
void editorSave();
|
||||||
|
|
||||||
|
void editorFind();
|
||||||
|
|
||||||
#endif // FILE_IO_H_
|
#endif // FILE_IO_H_
|
||||||
|
|||||||
+4
-1
@@ -1,6 +1,7 @@
|
|||||||
#ifndef INIT_H_
|
#ifndef INIT_H_
|
||||||
#define INIT_H_
|
#define INIT_H_
|
||||||
|
|
||||||
|
#include "builtins.h"
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -10,6 +11,8 @@
|
|||||||
* \brief Job's function is to initialize all the fields of editorConfig.
|
* \brief Job's function is to initialize all the fields of editorConfig.
|
||||||
* */
|
* */
|
||||||
|
|
||||||
void initEditor(struct editorConfig *E);
|
void initBuiltins();
|
||||||
|
|
||||||
|
void initEditor();
|
||||||
|
|
||||||
#endif // INIT_H_
|
#endif // INIT_H_
|
||||||
|
|||||||
+8
-3
@@ -5,6 +5,7 @@
|
|||||||
#include "define.h"
|
#include "define.h"
|
||||||
#include "output.h"
|
#include "output.h"
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
|
#include "builtins.h"
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
// KEYS keycode
|
// KEYS keycode
|
||||||
@@ -19,15 +20,19 @@
|
|||||||
// END \x1b[4~ || <esc>[8~ || <esc>[F || <esc>OF
|
// END \x1b[4~ || <esc>[8~ || <esc>[F || <esc>OF
|
||||||
// DELETE \x1b[3~
|
// DELETE \x1b[3~
|
||||||
|
|
||||||
char *editorPrompt(struct editorConfig *E, char *prompt);
|
char *editorPrompt(char *prompt, char * PlaceHolder, char bPathMode);
|
||||||
|
|
||||||
void editorMoveCursor(struct editorConfig *E, int key);
|
char *key_to_string(int key);
|
||||||
|
|
||||||
|
void editorMoveCursor(int key);
|
||||||
|
|
||||||
|
int executeKeyBind(char *key_sequence);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \fn void editorProcessKeypress()
|
* \fn void editorProcessKeypress()
|
||||||
* \brief Get the last key input and do the proper action.
|
* \brief Get the last key input and do the proper action.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void editorProcessKeypress(struct editorConfig *E);
|
void editorProcessKeypress();
|
||||||
|
|
||||||
#endif // INPUT_H_
|
#endif // INPUT_H_
|
||||||
|
|||||||
+3215
File diff suppressed because it is too large
Load Diff
+2180
File diff suppressed because it is too large
Load Diff
+6
-6
@@ -13,16 +13,16 @@
|
|||||||
* \brief Draws left rows of the editor.
|
* \brief Draws left rows of the editor.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void editorDrawRows(struct editorConfig *E, struct abuf *ab);
|
void editorDrawRows(struct abuf *ab);
|
||||||
|
|
||||||
void editorRefreshScreen(struct editorConfig *E);
|
void editorRefreshScreen();
|
||||||
|
|
||||||
void editorScroll(struct editorConfig *E);
|
void editorScroll();
|
||||||
|
|
||||||
void editorDrawStatusBar(struct editorConfig *E, struct abuf *ab);
|
void editorDrawStatusBar(struct abuf *ab);
|
||||||
|
|
||||||
void editorDrawMessageBar(struct editorConfig *E, struct abuf *ab);
|
void editorDrawMessageBar(struct abuf *ab);
|
||||||
|
|
||||||
void editorSetStatusMessage(struct editorConfig *E, const char *fmt, ...);
|
void editorSetStatusMessage(const char *fmt, ...);
|
||||||
|
|
||||||
#endif // OUTPUT_H_
|
#endif // OUTPUT_H_
|
||||||
|
|||||||
+7
-6
@@ -10,19 +10,20 @@
|
|||||||
|
|
||||||
int editorRowCxToRx(erow *row, int cursor_x);
|
int editorRowCxToRx(erow *row, int cursor_x);
|
||||||
|
|
||||||
|
int editorRowRxToCx(erow *row, int rx);
|
||||||
|
|
||||||
void editorUpdateRow(erow *row);
|
void editorUpdateRow(erow *row);
|
||||||
|
|
||||||
void editorInsertRow(struct editorConfig *E, int at, char *s, size_t len);
|
void editorInsertRow(int at, char *s, size_t len);
|
||||||
|
|
||||||
void editorFreeRow(erow *row);
|
void editorFreeRow(erow *row);
|
||||||
|
|
||||||
void editorDelRow(struct editorConfig *E, int at);
|
void editorDelRow(int at);
|
||||||
|
|
||||||
void editorRowInsertChar(struct editorConfig *E, erow *row, int at, int c);
|
void editorRowInsertChar(erow *row, int at, int c);
|
||||||
|
|
||||||
void editorRowAppendString(struct editorConfig *E, erow *row, char *s,
|
void editorRowAppendString(erow *row, char *s, size_t len);
|
||||||
size_t len);
|
|
||||||
|
|
||||||
void editorRowDelchar(struct editorConfig *E, erow *row, int at);
|
void editorRowDelchar(erow *row, int at);
|
||||||
|
|
||||||
#endif // ROW_OP_H_
|
#endif // ROW_OP_H_
|
||||||
|
|||||||
+2
-2
@@ -21,9 +21,9 @@
|
|||||||
|
|
||||||
void die(const char *s);
|
void die(const char *s);
|
||||||
|
|
||||||
void disableRawMode(struct editorConfig *E);
|
void disableRawMode();
|
||||||
|
|
||||||
void enableRawMode(struct editorConfig *E);
|
void enableRawMode();
|
||||||
|
|
||||||
int editorReadKey();
|
int editorReadKey();
|
||||||
|
|
||||||
|
|||||||
Executable
+42
@@ -0,0 +1,42 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
|
||||||
|
echo "--- Welcome to Beluga installer ---"
|
||||||
|
read -p "Do you want to start the installation ? (Y/n)" confirm && [[ $confirm == [yY] || $confirm == [yY][eE][sS] ]] || exit 1
|
||||||
|
|
||||||
|
# Check dependencies
|
||||||
|
|
||||||
|
if ! command -v "meson" &>/dev/null; then
|
||||||
|
echo "❌ Error: meson not found. Please install it first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Create config files
|
||||||
|
|
||||||
|
echo "Create config files ..."
|
||||||
|
mkdir -pv ~/.beluga/
|
||||||
|
cp -rv ./assets/ ~/.beluga/
|
||||||
|
mkdir -pv ~/.beluga/packages/
|
||||||
|
|
||||||
|
read -p "Do you want to replace your config file or keep it (init.lisp.bak) / (init.lisp.new) ? (Y/n)" confirm
|
||||||
|
if [[ "$confirm" =~ ^[Yy]$ ]]; then
|
||||||
|
mv ~/.beluga/config/init.lisp ~/.beluga/config/init.lisp.bak
|
||||||
|
cp -rv ./config/init.lisp ~/.beluga/config/
|
||||||
|
else
|
||||||
|
cp -rv ./config/init.lisp ~/.beluga/config/init.lisp.new
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Compile the project
|
||||||
|
|
||||||
|
echo "Start compilation ..."
|
||||||
|
meson setup build/
|
||||||
|
meson compile -C build/
|
||||||
|
|
||||||
|
# Add to path
|
||||||
|
echo "Adding beluga to the path"
|
||||||
|
sudo cp -f ./build/beluga /usr/local/bin/
|
||||||
|
|
||||||
|
echo "Installation finish"
|
||||||
|
echo "Check ~/.beluga/config/init.lisp for customization"
|
||||||
@@ -5,6 +5,11 @@
|
|||||||
* interactions. \version 0.1 \date 21 septembre 2024
|
* interactions. \version 0.1 \date 21 septembre 2024
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#define _DEFAULT_SOURCE
|
#define _DEFAULT_SOURCE
|
||||||
#define _BSD_SOURCE
|
#define _BSD_SOURCE
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
@@ -16,21 +21,31 @@
|
|||||||
#include "include/output.h"
|
#include "include/output.h"
|
||||||
#include "include/terminal.h"
|
#include "include/terminal.h"
|
||||||
|
|
||||||
|
struct editorConfig E;
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
struct editorConfig E;
|
char * splash_screen = (char *) calloc(256, sizeof(char));
|
||||||
|
|
||||||
enableRawMode(&E);
|
enableRawMode();
|
||||||
initEditor(&E);
|
initEditor();
|
||||||
if (argc >= 2) {
|
if (argc >= 2) {
|
||||||
editorOpen(&E, argv[1]);
|
E.state = READ_AND_WRITE;
|
||||||
|
editorOpen(argv[1]);
|
||||||
|
} else {
|
||||||
|
strcat(splash_screen, getenv("HOME"));
|
||||||
|
strcat(splash_screen, "/.beluga/assets/beluga.txt");
|
||||||
|
fprintf(stderr, "%s\n", splash_screen);
|
||||||
|
editorOpen(splash_screen);
|
||||||
}
|
}
|
||||||
|
free(splash_screen);
|
||||||
|
|
||||||
|
editorSetStatusMessage("HELP: Ctrl-S = save | Ctrl-Q = quit");
|
||||||
|
|
||||||
editorSetStatusMessage(&E, "HELP: Ctrl-S = save | Ctrl-Q = quit");
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
editorRefreshScreen(&E);
|
editorRefreshScreen();
|
||||||
editorProcessKeypress(&E);
|
editorProcessKeypress();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
project('beluga', 'c',
|
||||||
|
version : '1.1',
|
||||||
|
default_options : [
|
||||||
|
'c_std=none',
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
cc = meson.get_compiler('c')
|
||||||
|
m = cc.find_library('m', required: false)
|
||||||
|
|
||||||
|
# Source files
|
||||||
|
src_files = files(
|
||||||
|
'main.c',
|
||||||
|
'src/append_buffer.c',
|
||||||
|
'src/editor_op.c',
|
||||||
|
'src/file_io.c',
|
||||||
|
'src/init.c',
|
||||||
|
'src/input.c',
|
||||||
|
'src/output.c',
|
||||||
|
'src/row_op.c',
|
||||||
|
'src/terminal.c',
|
||||||
|
'src/builtins.c',
|
||||||
|
)
|
||||||
|
|
||||||
|
# Executable
|
||||||
|
executable('beluga',
|
||||||
|
src_files,
|
||||||
|
dependencies: [m]
|
||||||
|
)
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "../include/append_buffer.h"
|
#include "../include/append_buffer.h"
|
||||||
|
|
||||||
|
extern struct editorConfig E;
|
||||||
|
|
||||||
void abAppend(struct abuf *ab, const char *s, int len) {
|
void abAppend(struct abuf *ab, const char *s, int len) {
|
||||||
char *new = realloc(ab->b, ab->len + len);
|
char *new = realloc(ab->b, ab->len + len);
|
||||||
|
|
||||||
|
|||||||
+174
@@ -0,0 +1,174 @@
|
|||||||
|
#include "../include/builtins.h"
|
||||||
|
#include "../include/define.h"
|
||||||
|
#include "../include/input.h"
|
||||||
|
#include "../include/file_io.h"
|
||||||
|
#include "../include/editor_op.h"
|
||||||
|
#include "../include/row_op.h"
|
||||||
|
#include "../include/data.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
Lisp mapKey(Lisp args, LispError *e, LispContext ctx) {
|
||||||
|
const char *key_sequence = lisp_string(lisp_car(args));
|
||||||
|
args = lisp_cdr(args);
|
||||||
|
// second argument
|
||||||
|
Lisp func = lisp_car(args);
|
||||||
|
|
||||||
|
E.key_binds =
|
||||||
|
(struct keyBind_t *)realloc(E.key_binds, ++E.number_of_keybinds * sizeof(struct keyBind_t));
|
||||||
|
E.key_binds[E.number_of_keybinds - 1].key_sequence = (char *) malloc(50 * sizeof(char));
|
||||||
|
|
||||||
|
strncpy(E.key_binds[E.number_of_keybinds - 1].key_sequence, key_sequence, 50);
|
||||||
|
|
||||||
|
E.key_binds[E.number_of_keybinds - 1].command = func;
|
||||||
|
|
||||||
|
return lisp_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
Lisp moveCursor(Lisp args, LispError *e, LispContext ctx) {
|
||||||
|
const char *direction = lisp_string(lisp_car(args));
|
||||||
|
switch (direction[0]) {
|
||||||
|
case 'u':
|
||||||
|
editorMoveCursor(ARROW_UP);
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
editorMoveCursor(ARROW_DOWN);
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
editorMoveCursor(ARROW_RIGHT);
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
editorMoveCursor(ARROW_LEFT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return lisp_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
Lisp editorQuit(Lisp args, LispError* e, LispContext ctx) {
|
||||||
|
if (E.dirty && E.quit_times_buffer > 0) {
|
||||||
|
editorSetStatusMessage("WARNING! Changes hasn't been saved. Press Ctrl-Q "
|
||||||
|
"another time to quit.");
|
||||||
|
--E.quit_times_buffer;
|
||||||
|
return lisp_null();
|
||||||
|
}
|
||||||
|
write(STDOUT_FILENO, "\x1b[2J", 4);
|
||||||
|
write(STDOUT_FILENO, CURSOR_TOP_LEFT, 3);
|
||||||
|
disableRawMode();
|
||||||
|
exit(0);
|
||||||
|
|
||||||
|
return lisp_null();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Lisp l_editorSave(Lisp args, LispError* e, LispContext ctx) {
|
||||||
|
|
||||||
|
editorSave();
|
||||||
|
|
||||||
|
return lisp_null();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Lisp l_editorInsertNewLine(Lisp args, LispError* e, LispContext ctx) {
|
||||||
|
|
||||||
|
editorInsertNewLine();
|
||||||
|
|
||||||
|
return lisp_null();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Lisp moveCursorBeginLine(Lisp args, LispError *e, LispContext ctx) {
|
||||||
|
E.cursor_x = 0;
|
||||||
|
return lisp_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
Lisp moveCursorEndLine(Lisp args, LispError* e, LispContext ctx) {
|
||||||
|
if (E.cursor_y < E.numrows) {
|
||||||
|
E.cursor_x = E.row[E.cursor_y].size;
|
||||||
|
}
|
||||||
|
return lisp_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Lisp deletePreviousChar(Lisp args, LispError* e, LispContext ctx) {
|
||||||
|
editorDelChar();
|
||||||
|
return lisp_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
Lisp editorMoveCursorPageUp(Lisp args, LispError* e, LispContext ctx) {
|
||||||
|
E.cursor_y = E.row_offset;
|
||||||
|
int times = E.screenrows;
|
||||||
|
while (--times) {
|
||||||
|
editorMoveCursor(ARROW_UP);
|
||||||
|
}
|
||||||
|
return lisp_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
int times = E.screenrows;
|
||||||
|
while (--times) {
|
||||||
|
editorMoveCursor(ARROW_DOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lisp_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
Lisp editorOpenFile(Lisp args, LispError *e, LispContext ctx) {
|
||||||
|
char *filename = editorPrompt("Open : %s", getenv("PWD"), 1);
|
||||||
|
if (filename)
|
||||||
|
editorOpen(filename);
|
||||||
|
|
||||||
|
return lisp_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Lisp editorPrintC(Lisp args, LispError *e, LispContext ctx) {
|
||||||
|
char c = lisp_string(lisp_car(args))[0];
|
||||||
|
editorInsertChar(c);
|
||||||
|
return lisp_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
Lisp addPackage(Lisp args, LispError *e, LispContext ctx) {
|
||||||
|
const char *package_name = lisp_string(lisp_car(args));
|
||||||
|
char *package_dir = (char *) calloc(256, sizeof(char));
|
||||||
|
FILE *fd_package = NULL;
|
||||||
|
strcat(package_dir, getenv("HOME"));
|
||||||
|
strcat(package_dir, "/.beluga/packages/");
|
||||||
|
strcat(package_dir, package_name);
|
||||||
|
strcat(package_dir, "/init.lisp");
|
||||||
|
fd_package = fopen(package_dir, "r");
|
||||||
|
lisp_eval(lisp_read_file(fd_package, &E.ctx_error, E.ctx), &E.ctx_error,
|
||||||
|
E.ctx);
|
||||||
|
fclose(fd_package);
|
||||||
|
free(package_dir);
|
||||||
|
|
||||||
|
return lisp_null();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Lisp editorDelRow_L(Lisp args, LispError *e, LispContext ctx) {
|
||||||
|
editorDelRow(E.cursor_y);
|
||||||
|
return lisp_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
Lisp editorFind_L(Lisp args, LispError *e, LispContext ctx) {
|
||||||
|
editorFind();
|
||||||
|
return lisp_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
Lisp editorReadChar_L(Lisp args, LispError *e, LispContext ctx) {
|
||||||
|
fprintf(stderr, "char read : %c\n", E.row[E.cursor_y].render[E.cursor_x]);
|
||||||
|
return lisp_make_char(E.row[E.cursor_y].render[E.cursor_x]);
|
||||||
|
}
|
||||||
|
|
||||||
+29
-26
@@ -1,44 +1,47 @@
|
|||||||
#include "../include/editor_op.h"
|
#include "../include/editor_op.h"
|
||||||
#include "../include/row_op.h"
|
#include "../include/row_op.h"
|
||||||
|
|
||||||
void editorInsertChar(struct editorConfig *E, int c) {
|
|
||||||
if (E->cursor_y == E->numrows) {
|
extern struct editorConfig E;
|
||||||
editorInsertRow(E, E->numrows, "", 0);
|
|
||||||
}
|
void editorInsertChar(int c) {
|
||||||
editorRowInsertChar(E, &E->row[E->cursor_y], E->cursor_x, c);
|
if (E.cursor_y == E.numrows) {
|
||||||
E->cursor_x++;
|
editorInsertRow(E.numrows, "", 0);
|
||||||
|
}
|
||||||
|
editorRowInsertChar(&E.row[E.cursor_y], E.cursor_x, c);
|
||||||
|
E.cursor_x++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorInsertNewLine(struct editorConfig *E) {
|
void editorInsertNewLine() {
|
||||||
erow *row;
|
erow *row;
|
||||||
if (!E->cursor_x) {
|
if (!E.cursor_x) {
|
||||||
editorInsertRow(E, E->cursor_y, "", 0);
|
editorInsertRow(E.cursor_y, "", 0);
|
||||||
} else {
|
} else {
|
||||||
row = &E->row[E->cursor_y];
|
row = &E.row[E.cursor_y];
|
||||||
editorInsertRow(E, E->cursor_y + 1, &row->chars[E->cursor_x],
|
editorInsertRow(E.cursor_y + 1, &row->chars[E.cursor_x],
|
||||||
row->size - E->cursor_x);
|
row->size - E.cursor_x);
|
||||||
row = &E->row[E->cursor_y];
|
row = &E.row[E.cursor_y];
|
||||||
row->size = E->cursor_x;
|
row->size = E.cursor_x;
|
||||||
row->chars[row->size] = '\0';
|
row->chars[row->size] = '\0';
|
||||||
editorUpdateRow(row);
|
editorUpdateRow(row);
|
||||||
}
|
}
|
||||||
++E->cursor_y;
|
++E.cursor_y;
|
||||||
E->cursor_x = 0;
|
E.cursor_x = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorDelChar(struct editorConfig *E) {
|
void editorDelChar() {
|
||||||
erow *row;
|
erow *row;
|
||||||
if (E->cursor_y == E->numrows || !(E->cursor_x || E->cursor_y)) {
|
if (E.cursor_y == E.numrows || !(E.cursor_x || E.cursor_y)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
row = &E->row[E->cursor_y];
|
row = &E.row[E.cursor_y];
|
||||||
if (E->cursor_x > 0) {
|
if (E.cursor_x > 0) {
|
||||||
editorRowDelchar(E, row, E->cursor_x - 1);
|
editorRowDelchar(row, E.cursor_x - 1);
|
||||||
--E->cursor_x;
|
--E.cursor_x;
|
||||||
} else {
|
} else {
|
||||||
E->cursor_x = E->row[E->cursor_y - 1].size;
|
E.cursor_x = E.row[E.cursor_y - 1].size;
|
||||||
editorRowAppendString(E, &E->row[E->cursor_y - 1], row->chars, row->size);
|
editorRowAppendString(&E.row[E.cursor_y - 1], row->chars, row->size);
|
||||||
editorDelRow(E, E->cursor_y);
|
editorDelRow(E.cursor_y);
|
||||||
--E->cursor_y;
|
--E.cursor_y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+110
-66
@@ -8,79 +8,123 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
char *editorRowsToString(struct editorConfig *E, int *buffer_len) {
|
extern char *strdup(const char *);
|
||||||
int tot_len = 0;
|
extern ssize_t getline(char **restrict lineptr, size_t *restrict n,
|
||||||
int j;
|
FILE *restrict stream);
|
||||||
char *buf;
|
extern int ftruncate(int fd, off_t length);
|
||||||
char *p;
|
extern struct editorConfig E;
|
||||||
|
|
||||||
for (j = 0; j < E->numrows; ++j) {
|
char *editorRowsToString(int *buffer_len) {
|
||||||
tot_len += E->row[j].size + 1;
|
int tot_len = 0;
|
||||||
}
|
int j;
|
||||||
*buffer_len = tot_len;
|
char *buf;
|
||||||
buf = malloc(tot_len);
|
char *p;
|
||||||
p = buf;
|
|
||||||
for (j = 0; j < E->numrows; ++j) {
|
|
||||||
memcpy(p, E->row[j].chars, E->row[j].size);
|
|
||||||
p += E->row[j].size;
|
|
||||||
*p = '\n';
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf;
|
for (j = 0; j < E.numrows; ++j) {
|
||||||
|
tot_len += E.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;
|
||||||
|
*p = '\n';
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorOpen(struct editorConfig *E, char *filename) {
|
void editorCloseFile(void) {
|
||||||
FILE *fp;
|
E.cursor_x = 0;
|
||||||
|
E.cursor_y = 0;
|
||||||
free(E->filename);
|
E.rx = 0;
|
||||||
E->filename = strdup(filename);
|
E.row_offset = 0;
|
||||||
|
E.col_offset = 0;
|
||||||
fp = fopen(filename, "r");
|
E.numrows = 0;
|
||||||
if (!fp)
|
E.row = NULL;
|
||||||
die("fopen");
|
E.dirty = 0;
|
||||||
|
E.filename = NULL;
|
||||||
char *line = NULL;
|
E.status_msg[0] = '\0';
|
||||||
size_t line_cap = 0;
|
E.status_msg_time = 0;
|
||||||
ssize_t line_len;
|
|
||||||
|
|
||||||
while ((line_len = getline(&line, &line_cap, fp)) != -1) {
|
|
||||||
while (line_len > 0 &&
|
|
||||||
(line[line_len - 1] == '\n' || line[line_len - 1] == '\r')) {
|
|
||||||
--line_len;
|
|
||||||
}
|
|
||||||
editorInsertRow(E, E->numrows, line, line_len);
|
|
||||||
}
|
|
||||||
free(line);
|
|
||||||
fclose(fp);
|
|
||||||
E->dirty = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorSave(struct editorConfig *E) {
|
void editorOpen(char *filename) {
|
||||||
int len;
|
FILE *fp;
|
||||||
char *buf;
|
|
||||||
int fd;
|
// Test if a file is already open
|
||||||
if (E->filename == NULL) {
|
if (E.filename != NULL) {
|
||||||
E->filename = editorPrompt(E, "Save as: %s (ESC to cancel)");
|
editorCloseFile();
|
||||||
if (E->filename == NULL) {
|
E.state = READ_AND_WRITE;
|
||||||
editorSetStatusMessage(E, "Save aborted");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
buf = editorRowsToString(E, &len);
|
free(E.filename);
|
||||||
fd = open(E->filename, O_RDWR | O_CREAT, 0644);
|
E.filename = strdup(filename);
|
||||||
if (fd != -1) {
|
|
||||||
if (ftruncate(fd, len) != -1) {
|
fp = fopen(filename, "a+");
|
||||||
if (write(fd, buf, len) == len) {
|
if (!fp)
|
||||||
|
die("fopen");
|
||||||
|
|
||||||
|
char *line = NULL;
|
||||||
|
size_t line_cap = 0;
|
||||||
|
ssize_t line_len;
|
||||||
|
|
||||||
|
while ((line_len = getline(&line, &line_cap, fp)) != -1) {
|
||||||
|
while (line_len > 0 &&
|
||||||
|
(line[line_len - 1] == '\n' || line[line_len - 1] == '\r')) {
|
||||||
|
--line_len;
|
||||||
|
}
|
||||||
|
editorInsertRow(E.numrows, line, line_len);
|
||||||
|
}
|
||||||
|
free(line);
|
||||||
|
fclose(fp);
|
||||||
|
E.dirty = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void editorSave() {
|
||||||
|
int len;
|
||||||
|
char *buf;
|
||||||
|
int fd;
|
||||||
|
if (E.filename == NULL) {
|
||||||
|
E.filename = editorPrompt("Save as: %s (ESC to cancel)", "", 1);
|
||||||
|
if (E.filename == NULL) {
|
||||||
|
editorSetStatusMessage("Save aborted");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf = editorRowsToString(&len);
|
||||||
|
fd = open(E.filename, O_RDWR | O_CREAT, 0644);
|
||||||
|
if (fd != -1) {
|
||||||
|
if (ftruncate(fd, len) != -1) {
|
||||||
|
if (write(fd, buf, len) == len) {
|
||||||
|
close(fd);
|
||||||
|
free(buf);
|
||||||
|
E.dirty = 0;
|
||||||
|
editorSetStatusMessage("%d bytes written to disk", len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
close(fd);
|
close(fd);
|
||||||
free(buf);
|
|
||||||
E->dirty = 0;
|
|
||||||
editorSetStatusMessage(E, "%d bytes written to disk", len);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
close(fd);
|
free(buf);
|
||||||
}
|
editorSetStatusMessage("Can't save! I/O error: %s", strerror(errno));
|
||||||
free(buf);
|
|
||||||
editorSetStatusMessage(E, "Can't save! I/O error: %s", strerror(errno));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void editorFind() {
|
||||||
|
char *query = editorPrompt("Search: %s (ESC to cancel)", "", 0);
|
||||||
|
if (query == NULL) return;
|
||||||
|
int i;
|
||||||
|
for (i = E.cursor_y + 1; i < E.numrows; i++) {
|
||||||
|
erow *row = &E.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;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
+86
-14
@@ -1,19 +1,91 @@
|
|||||||
#include "../include/init.h"
|
#include "../include/init.h"
|
||||||
|
#include "../include/data.h"
|
||||||
|
#include "../include/terminal.h"
|
||||||
|
#include "../include/builtins.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
void initEditor(struct editorConfig *E) {
|
#define LISP_IMPLEMENTATION
|
||||||
E->cursor_x = 0;
|
#include "../include/lisp.h"
|
||||||
E->cursor_y = 0;
|
#include "../include/lisp_lib.h"
|
||||||
E->rx = 0;
|
|
||||||
E->row_offset = 0;
|
extern struct editorConfig;
|
||||||
E->col_offset = 0;
|
|
||||||
E->numrows = 0;
|
|
||||||
E->row = NULL;
|
void registerBuiltin(char *key_sequence, LispCFunc f) {
|
||||||
E->dirty = 0;
|
lisp_env_define(E.ctx.p->env, lisp_make_symbol(key_sequence, E.ctx),
|
||||||
E->filename = NULL;
|
lisp_make_func(f), E.ctx);
|
||||||
E->status_msg[0] = '\0';
|
|
||||||
E->status_msg_time = 0;
|
}
|
||||||
if (getWindowSize(&E->screenrows, &E->screencols) == -1) {
|
|
||||||
|
void initBuiltins() {
|
||||||
|
// move cursor
|
||||||
|
registerBuiltin("MOVE-CURSOR", moveCursor);
|
||||||
|
registerBuiltin("MAP-KEY", mapKey);
|
||||||
|
registerBuiltin("EDITOR-QUIT", editorQuit);
|
||||||
|
registerBuiltin("EDITOR-SAVE", l_editorSave);
|
||||||
|
registerBuiltin("EDITOR-INSERT-NEW-LINE", l_editorInsertNewLine);
|
||||||
|
registerBuiltin("MOVE-CURSOR-BEG-LINE", moveCursorBeginLine);
|
||||||
|
registerBuiltin("MOVE-CURSOR-END-LINE", moveCursorEndLine);
|
||||||
|
registerBuiltin("EDITOR-DELETE-PREVIOUS-CHAR", deletePreviousChar);
|
||||||
|
registerBuiltin("MOVE-CURSOR-PAGE-UP", editorMoveCursorPageUp);
|
||||||
|
registerBuiltin("MOVE-CURSOR-PAGE-DOWN", editorMoveCursorPageDown);
|
||||||
|
registerBuiltin("EDITOR-OPEN-FILE", editorOpenFile);
|
||||||
|
registerBuiltin("EDITOR-INSERT-CHAR", editorPrintC);
|
||||||
|
registerBuiltin("ADD-PACKAGE", addPackage);
|
||||||
|
registerBuiltin("EDITOR-DEL-ROW", editorDelRow_L);
|
||||||
|
registerBuiltin("EDITOR-FIND", editorFind_L);
|
||||||
|
registerBuiltin("EDITOR-READ-CHAR", editorReadChar_L);
|
||||||
|
}
|
||||||
|
|
||||||
|
void initEditor() {
|
||||||
|
char * 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");
|
die("getWindowSize");
|
||||||
}
|
}
|
||||||
E->screenrows -= 2;
|
E.screenrows -= 2;
|
||||||
|
|
||||||
|
E.number_of_keybinds = 0;
|
||||||
|
|
||||||
|
strcat(init_file_path, getenv("HOME"));
|
||||||
|
strcat(init_file_path, "/.beluga/config/init.lisp");
|
||||||
|
printf("%s\n", init_file_path);
|
||||||
|
E.fd_init_file = fopen(init_file_path, "r");
|
||||||
|
E.ctx = lisp_init();
|
||||||
|
E.env = lisp_env(E.ctx);
|
||||||
|
lisp_lib_load(E.ctx);
|
||||||
|
// Init builtins lisp functions
|
||||||
|
initBuiltins();
|
||||||
|
|
||||||
|
// Read config file
|
||||||
|
E.ctx_data = lisp_read_file(E.fd_init_file, &E.ctx_error, E.ctx);
|
||||||
|
free(init_file_path);
|
||||||
|
if (E.ctx_error != LISP_ERROR_NONE) {
|
||||||
|
die("init failed");
|
||||||
|
}
|
||||||
|
lisp_eval(E.ctx_data, &E.ctx_error, E.ctx);
|
||||||
|
|
||||||
|
// To modify
|
||||||
|
|
||||||
|
E.constantes.TAB_LENGTH =
|
||||||
|
(int)lisp_eval(lisp_read("TAB-LENGTH", &E.ctx_error, E.ctx), &E.ctx_error,
|
||||||
|
E.ctx)
|
||||||
|
.val.int_val;
|
||||||
|
E.constantes.QUIT_TIMES =
|
||||||
|
(int)lisp_eval(lisp_read("QUIT-TIMES", &E.ctx_error, E.ctx), &E.ctx_error,
|
||||||
|
E.ctx)
|
||||||
|
.val.int_val;
|
||||||
|
|
||||||
|
E.quit_times_buffer = E.constantes.QUIT_TIMES;
|
||||||
}
|
}
|
||||||
|
|||||||
+190
-102
@@ -1,42 +1,117 @@
|
|||||||
#include "../include/input.h"
|
#include "../include/input.h"
|
||||||
#include "../include/editor_op.h"
|
#include "../include/editor_op.h"
|
||||||
#include "../include/file_io.h"
|
|
||||||
#include "../include/output.h"
|
#include "../include/output.h"
|
||||||
|
#include "../include/define.h"
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <dirent.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
extern struct editorConfig E;
|
||||||
|
|
||||||
|
char * file_completion(const char *path) {
|
||||||
|
DIR * dir;
|
||||||
|
struct dirent *entry;
|
||||||
|
char directory[128];
|
||||||
|
char predict[128];
|
||||||
|
int predict_len = 0;
|
||||||
|
|
||||||
|
if (path[strlen(path) - 1] == '/') {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find dir name
|
||||||
|
char * last_slash = strrchr(path, '/');
|
||||||
|
if (last_slash) {
|
||||||
|
size_t dir_len = last_slash - path + 1; // length of dir_path
|
||||||
|
strncpy(directory, path, dir_len);
|
||||||
|
predict_len = strlen(path) - dir_len - 1;
|
||||||
|
strncpy(predict, last_slash + 1, predict_len);
|
||||||
|
directory[dir_len] = '\0';
|
||||||
|
predict[predict_len] = '\0';
|
||||||
|
fprintf(stderr, "%s %s\n", directory, predict);
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
dir = opendir(directory);
|
||||||
|
if (!dir)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
while ((entry = readdir(dir)) != NULL) {
|
||||||
|
if (strncmp(entry->d_name, predict, predict_len) == 0) {
|
||||||
|
static char full_path[128];
|
||||||
|
snprintf(full_path, sizeof(full_path), "%s%s", directory, entry->d_name);
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
if (stat(full_path, &st) == 0 && S_ISDIR(st.st_mode)) {
|
||||||
|
strcat(full_path, "/"); // add slash for directories
|
||||||
|
}
|
||||||
|
|
||||||
|
return strdup(full_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup when no more entries
|
||||||
|
closedir(dir);
|
||||||
|
dir = NULL;
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \fn char * editorPrompt(struct editorConfig *E, char *prompt)
|
* \fn char * editorPrompt(struct editorConfig *E, char *prompt, char bPathMode)
|
||||||
* \brief Return user input in a prompt when enter is hit. */
|
* \brief Return user input in a prompt when enter is hit. */
|
||||||
|
|
||||||
char *editorPrompt(struct editorConfig *E, char *prompt) {
|
char *editorPrompt(char *prompt, char * placeHolder, char bPathMode) {
|
||||||
size_t buf_size = 128;
|
size_t buf_size = 128;
|
||||||
char *buf = malloc(buf_size);
|
char *buf = malloc(buf_size);
|
||||||
size_t buf_len = 0;
|
size_t buf_len = 0;
|
||||||
int c = 0;
|
int c = 0;
|
||||||
buf[0] = '\0';
|
buf[0] = '\0';
|
||||||
|
strcpy(buf, placeHolder);
|
||||||
|
buf_len = strlen(placeHolder);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
editorSetStatusMessage(E, prompt, buf);
|
editorSetStatusMessage(prompt, buf);
|
||||||
editorRefreshScreen(E);
|
editorRefreshScreen();
|
||||||
c = editorReadKey();
|
c = editorReadKey();
|
||||||
if (c == DEL_KEY || c == CTRL_KEY('h') || c == BACKSPACE) {
|
if (c == DEL_KEY || c == CTRL_KEY('h') || c == BACKSPACE) {
|
||||||
if (buf_len != 0) {
|
if (buf_len != 0) {
|
||||||
buf[--buf_len] = '\0';
|
buf[--buf_len] = '\0';
|
||||||
}
|
}
|
||||||
} else if (c == ESCAPE) {
|
} else if (c == ESCAPE) {
|
||||||
fprintf(stderr, "escape");
|
editorSetStatusMessage("");
|
||||||
editorSetStatusMessage(E, "");
|
|
||||||
free(buf);
|
free(buf);
|
||||||
return NULL;
|
return NULL;
|
||||||
} else if (c == '\r') {
|
} else if (c == '\r') {
|
||||||
if (buf_len != 0) {
|
if (buf_len != 0) {
|
||||||
editorSetStatusMessage(E, "");
|
editorSetStatusMessage("");
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
} else if (bPathMode && c == '\t') {
|
||||||
|
char path[128];
|
||||||
|
char * pwd;
|
||||||
|
if (buf[0] != '/') {
|
||||||
|
pwd = getenv("PWD");
|
||||||
|
fprintf(stderr, "%s\n", pwd);
|
||||||
|
memcpy(path, pwd, strlen(pwd));
|
||||||
|
path[strlen(pwd)] = '/';
|
||||||
|
strncat(path, buf, buf_len);
|
||||||
|
} else {
|
||||||
|
strcpy(path, buf);
|
||||||
|
}
|
||||||
|
memset(buf, 0, 128);
|
||||||
|
buf_len = 0;
|
||||||
|
strcpy(buf, file_completion(path));
|
||||||
|
buf_len = strlen(buf);
|
||||||
|
buf[buf_len] = '\0';
|
||||||
|
|
||||||
} else if (!iscntrl(c) && c < 128) {
|
} else if (!iscntrl(c) && c < 128) {
|
||||||
if (buf_len == buf_size - 1) {
|
if (buf_len == buf_size - 1) {
|
||||||
buf_size *= 2;
|
buf_size *= 2;
|
||||||
@@ -48,121 +123,134 @@ char *editorPrompt(struct editorConfig *E, char *prompt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorMoveCursor(struct editorConfig *E, int key) {
|
char *key_to_string(int key) {
|
||||||
erow *row = (E->cursor_y >= E->numrows) ? NULL : &E->row[E->cursor_y];
|
static char key_str[32];
|
||||||
|
|
||||||
|
char tmp[10];
|
||||||
|
sprintf(tmp, "%d", key);
|
||||||
|
|
||||||
|
|
||||||
|
// First test enter key
|
||||||
|
|
||||||
|
if (key == '\r') {
|
||||||
|
strcpy(key_str, "ENTER");
|
||||||
|
} else if (key >= 1 && key <= 26) { // CTRL keys
|
||||||
|
snprintf(key_str, sizeof(key_str), "CTRL-%c", 'a' + key - 1);
|
||||||
|
} else {
|
||||||
|
switch (key) {
|
||||||
|
case ARROW_UP:
|
||||||
|
strcpy(key_str, "ARROW-UP");
|
||||||
|
break;
|
||||||
|
case ARROW_DOWN:
|
||||||
|
strcpy(key_str, "ARROW-DOWN");
|
||||||
|
break;
|
||||||
|
case ARROW_LEFT:
|
||||||
|
strcpy(key_str, "ARROW-LEFT");
|
||||||
|
break;
|
||||||
|
case ARROW_RIGHT:
|
||||||
|
strcpy(key_str, "ARROW-RIGHT");
|
||||||
|
break;
|
||||||
|
case PAGE_UP:
|
||||||
|
strcpy(key_str, "PAGE-UP");
|
||||||
|
fprintf(stderr, "pagr up\n");
|
||||||
|
break;
|
||||||
|
case PAGE_DOWN:
|
||||||
|
strcpy(key_str, "PAGE-DOWN");
|
||||||
|
break;
|
||||||
|
case DEL_KEY:
|
||||||
|
fprintf(stderr, "delete key\n");
|
||||||
|
strcpy(key_str, "DEL");
|
||||||
|
|
||||||
|
break;
|
||||||
|
case BACKSPACE:
|
||||||
|
strcpy(key_str, "BACKSPACE");
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
strcpy(key_str, "ENTER");
|
||||||
|
break;
|
||||||
|
case '\x1b':
|
||||||
|
strcpy(key_str, "ESCAPE");
|
||||||
|
break;
|
||||||
|
case BEG_LINE:
|
||||||
|
strcpy(key_str, "HOME");
|
||||||
|
break;
|
||||||
|
case END_LINE:
|
||||||
|
strcpy(key_str, "END");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// For regular characters
|
||||||
|
if (isprint(key)) {
|
||||||
|
snprintf(key_str, sizeof(key_str), "%c", key);
|
||||||
|
} else {
|
||||||
|
snprintf(key_str, sizeof(key_str), "KEY-%d", key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return key_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void editorMoveCursor(int key) {
|
||||||
|
erow *row = (E.cursor_y >= E.numrows) ? NULL : &E.row[E.cursor_y];
|
||||||
int row_len;
|
int row_len;
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case ARROW_RIGHT:
|
case ARROW_RIGHT:
|
||||||
if (row && E->cursor_x < row->size) {
|
if (row && E.cursor_x < row->size) {
|
||||||
++E->cursor_x;
|
++E.cursor_x;
|
||||||
} else if (row && E->cursor_x == row->size) {
|
} else if (row && E.cursor_x == row->size) {
|
||||||
E->cursor_y++;
|
E.cursor_y++;
|
||||||
E->cursor_x = 0;
|
E.cursor_x = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ARROW_DOWN:
|
case ARROW_DOWN:
|
||||||
if (E->cursor_y < E->numrows) {
|
if (E.cursor_y < E.numrows) {
|
||||||
++E->cursor_y;
|
++E.cursor_y;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ARROW_UP:
|
case ARROW_UP:
|
||||||
if (E->cursor_y != 0) {
|
if (E.cursor_y != 0) {
|
||||||
--E->cursor_y;
|
--E.cursor_y;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ARROW_LEFT:
|
case ARROW_LEFT:
|
||||||
if (E->cursor_x != 0) {
|
if (E.cursor_x != 0) {
|
||||||
--E->cursor_x;
|
--E.cursor_x;
|
||||||
} else if (E->cursor_y > 0) {
|
} else if (E.cursor_y > 0) {
|
||||||
--E->cursor_y;
|
--E.cursor_y;
|
||||||
E->cursor_x = E->row[E->cursor_y].size;
|
E.cursor_x = E.row[E.cursor_y].size;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
row = (E->cursor_y >= E->numrows) ? NULL : &E->row[E->cursor_y];
|
row = (E.cursor_y >= E.numrows) ? NULL : &E.row[E.cursor_y];
|
||||||
row_len = row ? row->size : 0;
|
row_len = row ? row->size : 0;
|
||||||
if (E->cursor_x > row_len) {
|
if (E.cursor_x > row_len) {
|
||||||
E->cursor_x = row_len;
|
E.cursor_x = row_len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorProcessKeypress(struct editorConfig *E) {
|
int executeKeyBind(char *key_sequence) {
|
||||||
static int quit_times = QUIT_TIMES;
|
int i;
|
||||||
|
for (i = 0; i < E.number_of_keybinds; ++i) {
|
||||||
|
if (!strcmp(key_sequence, E.key_binds[i].key_sequence)) {
|
||||||
|
|
||||||
|
fprintf(stderr, "lisp function %s\n", key_sequence);
|
||||||
|
// It's a symbol, create a function call
|
||||||
|
lisp_eval(lisp_cons(E.key_binds[i].command, lisp_null(), E.ctx),
|
||||||
|
&E.ctx_error, E.ctx);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void editorProcessKeypress() {
|
||||||
int c = editorReadKey();
|
int c = editorReadKey();
|
||||||
int times;
|
|
||||||
|
|
||||||
switch (c) {
|
if (executeKeyBind(key_to_string(c))) {
|
||||||
|
return;
|
||||||
case '\r':
|
|
||||||
editorInsertNewLine(E);
|
|
||||||
break;
|
|
||||||
case CTRL_KEY('q'):
|
|
||||||
if (E->dirty && quit_times > 0) {
|
|
||||||
editorSetStatusMessage(E,
|
|
||||||
"WARNING! Changes hasn't been saved. Press Ctrl-Q "
|
|
||||||
"another time to quit.");
|
|
||||||
--quit_times;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
write(STDOUT_FILENO, "\x1b[2J", 4);
|
|
||||||
write(STDOUT_FILENO, CURSOR_TOP_LEFT, 3);
|
|
||||||
disableRawMode(E);
|
|
||||||
exit(0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CTRL_KEY('s'):
|
|
||||||
editorSave(E);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BEG_LINE:
|
|
||||||
E->cursor_x = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case END_LINE:
|
|
||||||
if (E->cursor_y < E->numrows) {
|
|
||||||
E->cursor_x = E->row[E->cursor_y].size;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BACKSPACE:
|
|
||||||
case CTRL_KEY('h'):
|
|
||||||
case DEL_KEY:
|
|
||||||
if (c == DEL_KEY) {
|
|
||||||
editorMoveCursor(E, ARROW_RIGHT);
|
|
||||||
}
|
|
||||||
editorDelChar(E);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PAGE_UP:
|
|
||||||
case PAGE_DOWN: {
|
|
||||||
if (c == PAGE_UP) {
|
|
||||||
E->cursor_y = E->row_offset;
|
|
||||||
} else if (c == PAGE_DOWN) {
|
|
||||||
E->cursor_y = E->row_offset + E->screenrows - 1;
|
|
||||||
if (E->cursor_y > E->numrows) {
|
|
||||||
E->cursor_y = E->numrows;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
times = E->screenrows;
|
|
||||||
while (--times) {
|
|
||||||
editorMoveCursor(E, c == PAGE_UP ? ARROW_UP : ARROW_DOWN);
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case ARROW_UP:
|
|
||||||
case ARROW_DOWN:
|
|
||||||
case ARROW_LEFT:
|
|
||||||
case ARROW_RIGHT:
|
|
||||||
editorMoveCursor(E, c);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CTRL_KEY('l'):
|
|
||||||
case '\x1b':
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
editorInsertChar(E, c);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
quit_times = QUIT_TIMES;
|
editorInsertChar(c);
|
||||||
|
E.quit_times_buffer = E.constantes.QUIT_TIMES;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+50
-48
@@ -4,24 +4,26 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
void editorDrawRows(struct editorConfig *E, struct abuf *ab) {
|
extern struct editorConfig E;
|
||||||
|
|
||||||
|
void editorDrawRows(struct abuf *ab) {
|
||||||
int y;
|
int y;
|
||||||
char welcome[80];
|
char welcome[80];
|
||||||
int welcome_len;
|
int welcome_len;
|
||||||
int padding;
|
int padding;
|
||||||
int len;
|
int len;
|
||||||
int file_row;
|
int file_row;
|
||||||
for (y = 0; y < E->screenrows; ++y) {
|
for (y = 0; y < E.screenrows; ++y) {
|
||||||
file_row = y + E->row_offset;
|
file_row = y + E.row_offset;
|
||||||
if (file_row >= E->numrows) {
|
if (file_row >= E.numrows) {
|
||||||
if (E->numrows == 0 && y == E->screenrows / 3) {
|
if (E.numrows == 0 && y == E.screenrows / 3) {
|
||||||
welcome_len =
|
welcome_len =
|
||||||
snprintf(welcome, sizeof(welcome),
|
snprintf(welcome, sizeof(welcome),
|
||||||
"Beluga text editor -- version %s", BELUGA_VERSION);
|
"Beluga text editor -- version %s", BELUGA_VERSION);
|
||||||
if (welcome_len > E->screencols) {
|
if (welcome_len > E.screencols) {
|
||||||
welcome_len = E->screencols;
|
welcome_len = E.screencols;
|
||||||
}
|
}
|
||||||
padding = (E->screencols - welcome_len) / 2;
|
padding = (E.screencols - welcome_len) / 2;
|
||||||
if (padding) {
|
if (padding) {
|
||||||
abAppend(ab, "~", 1);
|
abAppend(ab, "~", 1);
|
||||||
--padding;
|
--padding;
|
||||||
@@ -34,54 +36,54 @@ void editorDrawRows(struct editorConfig *E, struct abuf *ab) {
|
|||||||
abAppend(ab, "~", 1);
|
abAppend(ab, "~", 1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
len = E->row[file_row].rsize - E->col_offset;
|
len = E.row[file_row].rsize - E.col_offset;
|
||||||
if (len < 0)
|
if (len < 0)
|
||||||
len = 0;
|
len = 0;
|
||||||
if (len > E->screencols)
|
if (len > E.screencols)
|
||||||
len = E->screencols;
|
len = E.screencols;
|
||||||
abAppend(ab, &E->row[file_row].render[E->col_offset], len);
|
abAppend(ab, &E.row[file_row].render[E.col_offset], len);
|
||||||
}
|
}
|
||||||
abAppend(ab, ERASE_END_LINE, 3);
|
abAppend(ab, ERASE_END_LINE, 3);
|
||||||
abAppend(ab, "\r\n", 2);
|
abAppend(ab, "\r\n", 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorScroll(struct editorConfig *E) {
|
void editorScroll() {
|
||||||
E->rx = E->cursor_x;
|
E.rx = E.cursor_x;
|
||||||
if (E->cursor_y < E->numrows) {
|
if (E.cursor_y < E.numrows) {
|
||||||
E->rx = editorRowCxToRx(&E->row[E->cursor_y], E->cursor_x);
|
E.rx = editorRowCxToRx(&E.row[E.cursor_y], E.cursor_x);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (E->cursor_y < E->row_offset) {
|
if (E.cursor_y < E.row_offset) {
|
||||||
E->row_offset = E->cursor_y;
|
E.row_offset = E.cursor_y;
|
||||||
}
|
}
|
||||||
if (E->cursor_y >= E->row_offset + E->screenrows) {
|
if (E.cursor_y >= E.row_offset + E.screenrows) {
|
||||||
E->row_offset = E->cursor_y - E->screenrows + 1;
|
E.row_offset = E.cursor_y - E.screenrows + 1;
|
||||||
}
|
}
|
||||||
if (E->rx < E->col_offset) {
|
if (E.rx < E.col_offset) {
|
||||||
E->col_offset = E->rx;
|
E.col_offset = E.rx;
|
||||||
}
|
}
|
||||||
if (E->rx >= E->col_offset + E->screencols) {
|
if (E.rx >= E.col_offset + E.screencols) {
|
||||||
E->col_offset = E->rx - E->screencols + 1;
|
E.col_offset = E.rx - E.screencols + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorDrawStatusBar(struct editorConfig *E, 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];
|
||||||
|
|
||||||
abAppend(ab, "\x1b[7m", 4); // inverting colors
|
abAppend(ab, "\x1b[7m", 4); // inverting colors
|
||||||
len = snprintf(status, sizeof(status), "%.20s - %d lines%s",
|
len = snprintf(status, sizeof(status), "%.20s - %d lines%s",
|
||||||
E->filename ? E->filename : "[No Name]", E->numrows,
|
E.filename ? E.filename : "[No Name]", E.numrows,
|
||||||
E->dirty ? "*" : "");
|
E.dirty ? "*" : "");
|
||||||
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);
|
E.cursor_y + 1, E.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) {
|
||||||
abAppend(ab, render_status, render_len);
|
abAppend(ab, render_status, render_len);
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
@@ -93,31 +95,31 @@ void editorDrawStatusBar(struct editorConfig *E, struct abuf *ab) {
|
|||||||
abAppend(ab, "\r\n", 2);
|
abAppend(ab, "\r\n", 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorDrawMessageBar(struct editorConfig *E, struct abuf *ab) {
|
void editorDrawMessageBar(struct abuf *ab) {
|
||||||
int msg_len = strlen(E->status_msg);
|
int msg_len = strlen(E.status_msg);
|
||||||
abAppend(ab, ERASE_END_LINE, 3);
|
abAppend(ab, ERASE_END_LINE, 3);
|
||||||
if (msg_len > E->screencols) {
|
if (msg_len > E.screencols) {
|
||||||
msg_len = E->screencols;
|
msg_len = E.screencols;
|
||||||
}
|
}
|
||||||
if (msg_len && time(NULL) - E->status_msg_time < 5) {
|
if (msg_len && time(NULL) - E.status_msg_time < 5) {
|
||||||
abAppend(ab, E->status_msg, msg_len);
|
abAppend(ab, E.status_msg, msg_len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorRefreshScreen(struct editorConfig *E) {
|
void editorRefreshScreen() {
|
||||||
editorScroll(E);
|
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);
|
||||||
|
|
||||||
editorDrawRows(E, &ab);
|
editorDrawRows(&ab);
|
||||||
editorDrawStatusBar(E, &ab);
|
editorDrawStatusBar(&ab);
|
||||||
editorDrawMessageBar(E, &ab);
|
editorDrawMessageBar(&ab);
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf), "\x1b[%d;%dH", (E->cursor_y - E->row_offset) + 1,
|
snprintf(buf, sizeof(buf), "\x1b[%d;%dH", (E.cursor_y - E.row_offset) + 1,
|
||||||
(E->rx - E->col_offset) + 1);
|
(E.rx - E.col_offset) + 1);
|
||||||
abAppend(&ab, buf, strlen(buf));
|
abAppend(&ab, buf, strlen(buf));
|
||||||
|
|
||||||
abAppend(&ab, SHOW_CURSOR, 6);
|
abAppend(&ab, SHOW_CURSOR, 6);
|
||||||
@@ -126,10 +128,10 @@ void editorRefreshScreen(struct editorConfig *E) {
|
|||||||
abFree(&ab);
|
abFree(&ab);
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorSetStatusMessage(struct editorConfig *E, const char *fmt, ...) {
|
void editorSetStatusMessage(const char *fmt, ...) {
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
vsnprintf(E->status_msg, sizeof(E->status_msg), fmt, ap);
|
vsnprintf(E.status_msg, sizeof(E.status_msg), fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
E->status_msg_time = time(NULL);
|
E.status_msg_time = time(NULL);
|
||||||
}
|
}
|
||||||
|
|||||||
+51
-31
@@ -1,20 +1,35 @@
|
|||||||
#include "../include/row_op.h"
|
#include "../include/row_op.h"
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
|
extern struct editorConfig E;
|
||||||
|
|
||||||
int editorRowCxToRx(erow *row, int cursor_x) {
|
int editorRowCxToRx(erow *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) {
|
||||||
if (row->chars[i] == '\t') {
|
if (row->chars[i] == '\t') {
|
||||||
render_x += (TAB_LENGTH - 1) - (render_x % TAB_LENGTH);
|
render_x += (E.constantes.TAB_LENGTH - 1) - (render_x % E.constantes.TAB_LENGTH);
|
||||||
}
|
}
|
||||||
render_x++;
|
render_x++;
|
||||||
}
|
}
|
||||||
return render_x;
|
return render_x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int editorRowRxToCx(erow *row, int rx) {
|
||||||
|
int cur_rx = 0;
|
||||||
|
int cx;
|
||||||
|
for (cx = 0; cx < row->size; cx++) {
|
||||||
|
if (row->chars[cx] == '\t')
|
||||||
|
cur_rx += (E.constantes.TAB_LENGTH - 1) - (cur_rx % E.constantes.TAB_LENGTH);
|
||||||
|
cur_rx++;
|
||||||
|
if (cur_rx > rx) return cx;
|
||||||
|
}
|
||||||
|
return cx;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \fn editorUpdateRow(erow *row)
|
* \fn editorUpdateRow(erow *row)
|
||||||
* \brief Copy content of \p row in \p row->render.
|
* \brief Copy content of \p row in \p row->render.
|
||||||
@@ -32,8 +47,8 @@ void editorUpdateRow(erow *row) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
free(row->render);
|
free(row->render);
|
||||||
row->render = malloc(row->size + tabs * (TAB_LENGTH - 1) +
|
row->render = malloc(row->size + tabs * (E.constantes.TAB_LENGTH - 1) +
|
||||||
1); /**< Tabs needs TAB_LENGTH chars so TAB_LENGTH - 1
|
1); /**< Tabs needs E.constantes.TAB_LENGTH chars so E.constantes.TAB_LENGTH - 1
|
||||||
more than the first already counted. */
|
more than the first already counted. */
|
||||||
|
|
||||||
// end of counting
|
// end of counting
|
||||||
@@ -41,7 +56,7 @@ void editorUpdateRow(erow *row) {
|
|||||||
for (i = 0; i < row->size; ++i) {
|
for (i = 0; i < row->size; ++i) {
|
||||||
if (row->chars[i] == '\t') {
|
if (row->chars[i] == '\t') {
|
||||||
row->render[i_render++] = ' ';
|
row->render[i_render++] = ' ';
|
||||||
while (i_render % TAB_LENGTH) {
|
while (i_render % E.constantes.TAB_LENGTH) {
|
||||||
row->render[i_render++] =
|
row->render[i_render++] =
|
||||||
' '; /**< Addind the right amount of spaces for tabs */
|
' '; /**< Addind the right amount of spaces for tabs */
|
||||||
}
|
}
|
||||||
@@ -53,25 +68,29 @@ void editorUpdateRow(erow *row) {
|
|||||||
row->rsize = i_render;
|
row->rsize = i_render;
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorInsertRow(struct editorConfig *E, int at, char *s, size_t len) {
|
void editorInsertRow(int at, char *s, size_t len) {
|
||||||
if (at < 0 || at > E->numrows) {
|
if (at < 0 || at > E.numrows) {
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
erow *tmp = (erow *)realloc(E.row, sizeof(erow) * (E.numrows + 1));
|
||||||
|
if (!tmp) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
E.row = tmp;
|
||||||
|
memmove(&E.row[at + 1], &E.row[at], sizeof(erow) * (E.numrows - at));
|
||||||
|
|
||||||
E->row = realloc(E->row, sizeof(erow) * (E->numrows + 1));
|
E.row[at].size = len;
|
||||||
memmove(&E->row[at + 1], &E->row[at], sizeof(erow) * (E->numrows - at));
|
E.row[at].chars = malloc(len + 1);
|
||||||
|
memcpy(E.row[at].chars, s, len);
|
||||||
|
E.row[at].chars[len] = '\0';
|
||||||
|
|
||||||
E->row[at].size = len;
|
E.row[at].rsize = 0;
|
||||||
E->row[at].chars = malloc(len + 1);
|
E.row[at].render = NULL;
|
||||||
memcpy(E->row[at].chars, s, len);
|
editorUpdateRow(&E.row[at]);
|
||||||
E->row[at].chars[len] = '\0';
|
|
||||||
|
|
||||||
E->row[at].rsize = 0;
|
++E.numrows;
|
||||||
E->row[at].render = NULL;
|
++E.dirty;
|
||||||
editorUpdateRow(&E->row[at]);
|
|
||||||
|
|
||||||
++E->numrows;
|
|
||||||
++E->dirty;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorFreeRow(erow *row) {
|
void editorFreeRow(erow *row) {
|
||||||
@@ -79,21 +98,23 @@ void editorFreeRow(erow *row) {
|
|||||||
free(row->chars);
|
free(row->chars);
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorDelRow(struct editorConfig *E, int at) {
|
void editorDelRow(int at) {
|
||||||
if (at < 0 || at >= E->numrows) {
|
if (at < 0 || at >= E.numrows) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
editorFreeRow(&E->row[at]);
|
editorFreeRow(&E.row[at]);
|
||||||
memmove(&E->row[at], &E->row[at + 1], sizeof(erow) * (E->numrows - at - 1));
|
memmove(&E.row[at], &E.row[at + 1], sizeof(erow) * (E.numrows - at - 1));
|
||||||
--E->numrows;
|
--E.numrows;
|
||||||
++E->dirty;
|
++E.dirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \fn editorRowInsertChar(erow *row, int at, int c)
|
* \fn editorRowInsertChar(erow *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(struct editorConfig *E, erow *row, int at, int c) {
|
void editorRowInsertChar(erow *row, int at, int c) {
|
||||||
|
if (E.state == READ_ONLY)
|
||||||
|
return;
|
||||||
if (at < 0 || at > row->size) {
|
if (at < 0 || at > row->size) {
|
||||||
at = row->size;
|
at = row->size;
|
||||||
}
|
}
|
||||||
@@ -102,17 +123,16 @@ void editorRowInsertChar(struct editorConfig *E, erow *row, int at, int c) {
|
|||||||
++row->size;
|
++row->size;
|
||||||
row->chars[at] = c;
|
row->chars[at] = c;
|
||||||
editorUpdateRow(row);
|
editorUpdateRow(row);
|
||||||
++E->dirty;
|
++E.dirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
void editorRowAppendString(struct editorConfig *E, erow *row, char *s,
|
void editorRowAppendString(erow *row, char *s, size_t len) {
|
||||||
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);
|
editorUpdateRow(row);
|
||||||
++E->dirty;
|
++E.dirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -120,12 +140,12 @@ void editorRowAppendString(struct editorConfig *E, erow *row, char *s,
|
|||||||
* \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(struct editorConfig *E, erow *row, int at) {
|
void editorRowDelchar(erow *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);
|
editorUpdateRow(row);
|
||||||
++E->dirty;
|
++E.dirty;
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-5
@@ -1,24 +1,28 @@
|
|||||||
#include "../include/terminal.h"
|
#include "../include/terminal.h"
|
||||||
|
#include "../include/data.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
void die(const char *s) {
|
void die(const char *s) {
|
||||||
write(STDOUT_FILENO, "\x1b[2J", 4);
|
write(STDOUT_FILENO, "\x1b[2J", 4);
|
||||||
write(STDOUT_FILENO, CURSOR_TOP_LEFT, 3);
|
write(STDOUT_FILENO, CURSOR_TOP_LEFT, 3);
|
||||||
|
lisp_shutdown(E.ctx);
|
||||||
perror(s);
|
perror(s);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void disableRawMode(struct editorConfig *E) {
|
void disableRawMode() {
|
||||||
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &E->orig_termios) == -1) {
|
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &E.orig_termios) == -1) {
|
||||||
die("tcsetattr");
|
die("tcsetattr");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void enableRawMode(struct editorConfig *E) {
|
void enableRawMode() {
|
||||||
if (tcgetattr(STDIN_FILENO, &E->orig_termios) == -1) {
|
if (tcgetattr(STDIN_FILENO, &E.orig_termios) == -1) {
|
||||||
die("tcgetattr");
|
die("tcgetattr");
|
||||||
}
|
}
|
||||||
|
|
||||||
struct termios raw = E->orig_termios;
|
struct termios raw = E.orig_termios;
|
||||||
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
|
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
|
||||||
raw.c_oflag &= ~(OPOST);
|
raw.c_oflag &= ~(OPOST);
|
||||||
raw.c_cflag |= (CS8);
|
raw.c_cflag |= (CS8);
|
||||||
|
|||||||
Reference in New Issue
Block a user