commit 341f6365ef49480bcef99e5f51bb2d02f7211907
parent 06a5020778af8f4b4014f8ed872b1da15d590430
Author: Samdal <samdal@protonmail.com>
Date: Fri, 21 Jan 2022 21:30:54 +0100
status bars, added some helper functions, plugins
Diffstat:
M | LICENSE | | | 5 | +++++ |
M | README | | | 33 | ++------------------------------- |
M | config.def.c | | | 367 | ++++++++++++++++--------------------------------------------------------------- |
A | plugins/color_schemes/gruvbox.h | | | 66 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | plugins/syntax/c.h | | | 101 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | plugins/syntax/handy_defines.h | | | 20 | ++++++++++++++++++++ |
M | se.c | | | 730 | +++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------- |
M | se.h | | | 44 | +++++++++++++++++++++++++++++++++----------- |
M | x.c | | | 100 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------ |
M | x.h | | | 8 | ++++++-- |
10 files changed, 858 insertions(+), 616 deletions(-)
diff --git a/LICENSE b/LICENSE
@@ -1,5 +1,10 @@
MIT/X Consortium License
+© 2021-2022 Halvard Samdal <halvardsamdal at gmail dot com>
+
+The below copyrights are from st, the terminal emulator which
+this editor is based on
+
© 2014-2020 Hiltjo Posthuma <hiltjo at codemadness dot org>
© 2018 Devin J. Pohly <djpohly at gmail dot com>
© 2014-2017 Quentin Rameau <quinq at fifth dot space>
diff --git a/README b/README
@@ -1,34 +1,5 @@
-st - simple terminal
---------------------
-st is a simple terminal emulator for X which sucks less.
-
-
-Requirements
-------------
-In order to build st you need the Xlib header files.
-
-
-Installation
-------------
-Edit config.mk to match your local setup (st is installed into
-the /usr/local namespace by default).
-
-Afterwards enter the following command to build and install st (if
-necessary as root):
-
- make clean install
-
-
-Running st
-----------
-If you did not install st with make clean install, you must compile
-the st terminfo entry with the following command:
-
- tic -sx st.info
-
-See the man page for additional details.
-
Credits
-------
-Based on Aurélien APTEL <aurelien dot aptel at gmail dot com> bt source code.
+Based on st(http://st.suckless.org/), see LICENSE file copyrights
+which is based on Aurélien APTEL <aurelien dot aptel at gmail dot com> bt source code.
diff --git a/config.def.c b/config.def.c
@@ -1,5 +1,4 @@
#include <assert.h>
-#include <time.h>
#include "x.h"
@@ -8,7 +7,8 @@
//
// font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html
-char *font = "Iosevka:pixelsize=16:antialias=true:autohint=true";
+//char *fontconfig = "Liberation Mono:pixelsize=16:antialias=true:autohint=true";
+char *fontconfig = "Iosevka:pixelsize=16:antialias=true:autohint=true";
// pixels of border around the window
int border_px = 2;
@@ -26,188 +26,31 @@ int wrap_buffer = 0;
// spaces per tab (tabs will self align)
unsigned int tabspaces = 8;
-///////////////////////////////////////////
-// colour scheme
-// this color scheme is gruvbox
-// see colors at: https://github.com/morhetz/gruvbox
-//
-
-enum colour_names {
- bg,
- bg0_h,
- fg,
- sel,
- line,
- red,
- dark_green,
- green,
- teal,
- yellow,
- orange,
- blue,
- purple,
- aqua,
- gray,
-};
-
-const char * const colors[] = {
- [bg] = "#282828",
- [bg0_h] = "#1d2021",
- [fg] = "#fbf1c7",
- [sel] = "#504945",
- [line] = "#32302f",
- [red] = "#cc251d",
- [dark_green] = "#98971a",
- [green] = "#b8bb26",
- [teal] = "#8ec07c",
- [yellow] = "#fabd2f",
- [orange] = "#d65d0e",
- [blue] = "#458588",
- [purple] = "#b16286",
- [aqua] = "#83a598",
- [gray] = "#a89984",
- NULL
-};
-
-// default colors
-Glyph default_attributes = {.fg = fg, .bg = bg};
-unsigned int mouse_fg = fg;
-unsigned int mouse_bg = bg;
-unsigned int selection_bg = sel;
-unsigned int mouse_line_bg = line;
-
// Default shape of cursor
// 2: Block ("█")
// 4: Underline ("_")
// 6: Bar ("|")
-// 7: Snowman ("☃")
unsigned int cursor_shape = 2;
// thickness of underline and bar cursors
unsigned int cursor_thickness = 2;
-//////////////////////////////////////////
-// Color scheme
+///////////////////////////////////////////
+// color scheme
+// the syntax highlighting is applied in one pass,
+// so you can't have nested syntax highlighting
//
-#define str_color gray
-#define comment_color gray
-#define type_color teal
-#define keyword_color green
-#define macro_color yellow
-#define operator_color yellow
-#define constants_color dark_green
-#define number_color gray
-#define color_macro(_str) {COLOR_WORD,{_str}, macro_color}
-#define color_keyword(_str) {COLOR_WORD,{_str}, keyword_color}
-#define color_type(_str) {COLOR_WORD,{_str}, type_color}
-#define color_number(_num) \
- {COLOR_WORD_STARTING_WITH_STR, {_num}, number_color}, \
- {COLOR_WORD_ENDING_WITH_STR, {_num".f"},number_color}
-
-// (order matters)
-const struct color_scheme_entry c_color_scheme[] = {
- // Coloring type arguments Color
-
- // strings
- {COLOR_AROUND_TO_LINE, {"\"", "\""}, str_color},
- {COLOR_STR, {"''"}, fg},
- {COLOR_AROUND_TO_LINE, {"'", "'"}, str_color},
- {COLOR_INSIDE_TO_LINE, {"#include <", ">"}, str_color},
- {COLOR_INSIDE_TO_LINE, {"#include<", ">"}, str_color},
- // comments
- {COLOR_AROUND, {"/*", "*/"}, comment_color},
- {COLOR_AROUND, {"//", "\n"}, comment_color},
- // macros
- {COLOR_UPPER_CASE_WORD, {0}, constants_color},
- {COLOR_STR_AFTER_WORD, {"#ifdef"}, constants_color},
- {COLOR_STR_AFTER_WORD, {"#ifndef"}, constants_color},
- {COLOR_STR_AFTER_WORD, {"#define"}, constants_color},
- {COLOR_STR_AFTER_WORD, {"#undef"}, constants_color},
- {COLOR_WORD_STARTING_WITH_STR, {"#"}, macro_color},
- color_macro("sizeof"),
- color_macro("alignof"),
- color_macro("offsetof"),
- color_macro("va_arg"),
- color_macro("va_start"),
- color_macro("va_end"),
- color_macro("va_copy"),
- {COLOR_STR_AFTER_WORD, {"defined"}, constants_color},
- color_macro("defined"),
- // operators
- {COLOR_STR, {"!="}, fg},
- {COLOR_STR, {"!"}, operator_color},
- {COLOR_STR, {"~"}, operator_color},
- {COLOR_STR, {"?"}, operator_color},
- // keywords
- {COLOR_WORD_STR, {"...", ")"}, keyword_color},
- {COLOR_WORD_STR, {"struct", "{"},keyword_color},
- {COLOR_WORD_STR, {"union", "{"}, keyword_color},
- {COLOR_WORD_STR, {"enum", "{"}, keyword_color},
- {COLOR_STR_AFTER_WORD, {"struct"}, type_color},
- {COLOR_STR_AFTER_WORD, {"union"}, type_color},
- {COLOR_STR_AFTER_WORD, {"enum"}, type_color},
- {COLOR_STR_AFTER_WORD, {"goto"}, constants_color},
- {COLOR_WORD_INSIDE, {"}", ":"}, constants_color},
- {COLOR_WORD_INSIDE, {"{", ":"}, constants_color},
- {COLOR_WORD_INSIDE, {";", ":"}, constants_color},
- color_keyword("struct"),
- color_keyword("enum"),
- color_keyword("union"),
- color_keyword("const"),
- color_keyword("typedef"),
- color_keyword("extern"),
- color_keyword("static"),
- color_keyword("inline"),
- color_keyword("if"),
- color_keyword("else"),
- color_keyword("for"),
- color_keyword("while"),
- color_keyword("case"),
- color_keyword("switch"),
- color_keyword("do"),
- color_keyword("return"),
- color_keyword("break"),
- color_keyword("continue"),
- color_keyword("goto"),
- color_keyword("restrict"),
- color_keyword("register"),
- // functions
- //{COLOR_WORD_BEFORE_STR, {"("}, aqua},
- // types
- color_type("int"),
- color_type("unsigned"),
- color_type("long"),
- color_type("short"),
- color_type("char"),
- color_type("void"),
- color_type("float"),
- color_type("double"),
- color_type("complex"),
- color_type("bool"),
- color_type("_Bool"),
- color_type("FILE"),
- color_type("va_list"),
- {COLOR_WORD_ENDING_WITH_STR, {"_t"}, type_color},
- {COLOR_WORD_ENDING_WITH_STR, {"_type"}, type_color},
- {COLOR_WORD_ENDING_WITH_STR, {"T"}, type_color},
- // numbers
- color_number("0"),
- color_number("1"),
- color_number("2"),
- color_number("3"),
- color_number("4"),
- color_number("5"),
- color_number("6"),
- color_number("7"),
- color_number("8"),
- color_number("9"),
-};
-#define default_word_seperators "., \n\t*+-/%!~<>=(){}[]\"^&|\\\'?:;"
+#include "plugins/color_schemes/gruvbox.h"
+
+// disable coloring functions for the syntax schemes below
+#undef function_color
+
+#include "plugins/syntax/c.h"
const struct color_scheme color_schemes[] = {
- {".c", default_word_seperators, c_color_scheme, LEN(c_color_scheme)},
- {".h", default_word_seperators, c_color_scheme, LEN(c_color_scheme)},
+ {".c", c_word_seperators, c_color_scheme, LEN(c_color_scheme)},
+ {".h", c_word_seperators, c_color_scheme, LEN(c_color_scheme)},
{0},
};
@@ -234,6 +77,7 @@ static void zoomabs(const Arg* arg);
static void zoomreset(const Arg* arg);
static void cursor_move_x_relative(const Arg* arg);
static void cursor_move_y_relative(const Arg* arg);
+static void change_to_file_buffer(const Arg* arg);
static void save_buffer(const Arg* arg);
static void toggle_selection(const Arg* arg);
static void move_cursor_to_offset(const Arg* arg);
@@ -242,17 +86,15 @@ static void clipboard_copy(const Arg* arg);
static void clipboard_paste(const Arg* arg);
static void undo(const Arg* arg);
static void redo(const Arg* arg);
+static void open_file_browser(const Arg* arg);
+static void buffer_kill(const Arg* arg);
static void cursor_callback(struct window_buffer* buf, enum cursor_reason callback_reason);
static void buffer_content_callback(struct file_buffer* buffer, int offset, enum buffer_content_reason reason);
-static int keypress_actions(KeySym keysym, int modkey);
-static void string_insert_callback(const char* buf, int buflen);
-static void xunloadfonts(void);
-static void xunloadfont(Font *);
-static void buffer_copy_ub_to_current(struct window_buffer* buffer);
static void keep_cursor_col(struct window_buffer* buf, enum cursor_reason callback_reason);
static void move_selection(struct window_buffer* buf, enum cursor_reason callback_reason);
-static void add_to_undo_buffer(struct file_buffer* buffer, int offset, enum buffer_content_reason reason);
+static int keypress_actions(KeySym keysym, int modkey);
+static void string_insert_callback(const char* buf, int buflen);
/////////////////////////////////////////
// Shortcuts
@@ -282,12 +124,15 @@ const Shortcut shortcuts[] = {
{ TERMMOD, XK_Left, window_resize, {.i = MOVE_LEFT} },
{ TERMMOD, XK_Down, window_resize, {.i = MOVE_DOWN} },
{ TERMMOD, XK_Up, window_resize, {.i = MOVE_UP} },
+ { ControlMask, XK_Tab, change_to_file_buffer, {.i = +1} },
{ ControlMask, XK_m, toggle_selection, {0} },
{ ControlMask, XK_g, move_cursor_to_offset, {0} },
{ TERMMOD, XK_G, move_cursor_to_end_of_buffer, {0} },
+ { ControlMask, XK_period, open_file_browser, {0} },
+ { TERMMOD, XK_D, buffer_kill, {0} },
{ ControlMask, XK_l, window_split, {.i = WINDOW_HORISONTAL}},
{ ControlMask, XK_k, window_split, {.i = WINDOW_VERTICAL} },
- { ControlMask, XK_d, window_delete, {0} },
+ { ControlMask, XK_d, window_delete, {0} },
{ ControlMask, XK_z, undo, {0} },
{ TERMMOD, XK_Z, redo, {0} },
{ ControlMask, XK_s, save_buffer, {0} },
@@ -345,8 +190,10 @@ numlock(const Arg *dummy)
void window_split(const Arg *arg)
{
window_node_split(focused_node, 0.5, arg->i);
- focused_node = focused_node->node2;
- focused_window = &focused_node->window;
+ if (focused_node->node2) {
+ focused_node = focused_node->node2;
+ focused_window = &focused_node->window;
+ }
}
void window_resize(const Arg *arg)
@@ -382,7 +229,7 @@ void
zoomabs(const Arg *arg)
{
xunloadfonts();
- xloadfonts(font, arg->f);
+ xloadfonts(fontconfig, arg->f);
cresize(0, 0);
xhints();
}
@@ -401,7 +248,8 @@ zoomreset(const Arg *arg)
void
cursor_move_x_relative(const Arg* arg)
{
- buffer_move_on_line(focused_window, arg->i, CURSOR_RIGHT_LEFT_MOVEMENT);
+ if (focused_window->mode != WINDOW_BUFFER_FILE_BROWSER)
+ buffer_move_on_line(focused_window, arg->i, CURSOR_RIGHT_LEFT_MOVEMENT);
}
void
@@ -412,6 +260,27 @@ cursor_move_y_relative(const Arg* arg)
}
void
+change_to_file_buffer(const Arg* arg)
+{
+ if (arg->i < 0) {
+ int prev_buffer_index = focused_window->buffer_index;
+ focused_window->buffer_index += 1;
+ get_file_buffer(focused_window);
+ if (focused_window->buffer_index == prev_buffer_index)
+ return;
+
+ for (int i = 0; ; i++) {
+ focused_window->buffer_index += prev_buffer_index - arg->i - i;
+ get_file_buffer(focused_window);
+ if (focused_window->buffer_index != prev_buffer_index)
+ return;
+ }
+ }
+
+ focused_window->buffer_index += arg->i;
+}
+
+void
save_buffer(const Arg* arg)
{
buffer_write_to_filepath(get_file_buffer(focused_window));
@@ -446,92 +315,47 @@ clipboard_copy(const Arg* arg)
{
struct file_buffer* fb = get_file_buffer(focused_window);
int len;
- char* temp = buffer_get_selection(fb, &len);
- if (!temp)
- return;
- if (copy_buffer)
- free(copy_buffer);
- copy_buffer = temp;
- copy_len = len;
+ char* buf = buffer_get_selection(fb, &len);
+ set_clipboard_copy(buf, len);
buffer_move_cursor_to_selection_start(focused_window);
fb->mode &= ~BUFFER_SELECTION_ON;
-
- Atom clipboard;
- clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
- XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime);
}
void
clipboard_paste(const Arg* arg)
{
- Atom clipboard;
-
- clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
- XConvertSelection(xw.dpy, clipboard, xtarget, clipboard,
- xw.win, CurrentTime);
-}
-
-void
-buffer_copy_ub_to_current(struct window_buffer* buffer)
-{
- struct file_buffer* fb = get_file_buffer(buffer);
- struct undo_buffer* cub = &fb->ub[fb->current_undo_buffer];
- assert(cub->contents);
-
- fb->contents = xrealloc(fb->contents, cub->capacity);
- memcpy(fb->contents, cub->contents, cub->capacity);
- fb->len = cub->len;
- fb->capacity = cub->capacity;
-
- buffer_move_to_offset(buffer, cub->cursor_offset, CURSOR_SNAPPED);
- buffer->y_scroll = cub->y_scroll;
+ insert_clipboard_at_cursor();
}
void
undo(const Arg* arg)
{
- struct file_buffer* fb = get_file_buffer(focused_window);
- if (fb->current_undo_buffer == 0)
- return;
- fb->current_undo_buffer--;
- fb->available_redo_buffers++;
-
- buffer_copy_ub_to_current(focused_window);
+ buffer_undo(get_file_buffer(focused_window));
}
void
redo(const Arg* arg)
{
- struct file_buffer* fb = get_file_buffer(focused_window);
- if (fb->available_redo_buffers == 0)
- return;
- fb->available_redo_buffers--;
- fb->current_undo_buffer++;
-
- buffer_copy_ub_to_current(focused_window);
+ buffer_redo(get_file_buffer(focused_window));
}
void
-xunloadfonts(void)
+open_file_browser(const Arg* arg)
{
- /* Free the loaded fonts in the font cache. */
- while (frclen > 0)
- XftFontClose(xw.dpy, frc[--frclen].font);
-
- xunloadfont(&dc.font);
- xunloadfont(&dc.bfont);
- xunloadfont(&dc.ifont);
- xunloadfont(&dc.ibfont);
+ int last_fb = focused_window->buffer_index;
+ struct file_buffer* fb = get_file_buffer(focused_window);
+
+ char* path = file_path_get_path(fb->file_path);
+ *focused_window = window_buffer_new(new_file_buffer_entry(path));
+ focused_window->cursor_col = last_fb;
+ free(path);
}
void
-xunloadfont(Font *f)
+buffer_kill(const Arg* arg)
{
- XftFontClose(xw.dpy, f->match);
- FcPatternDestroy(f->pattern);
- if (f->set)
- FcFontSetDestroy(f->set);
+ destroy_file_buffer_entry(focused_node, &root_node);
}
////////////////////////////////////////////////////////7
@@ -541,12 +365,9 @@ xunloadfont(Font *f)
void
keep_cursor_col(struct window_buffer* buf, enum cursor_reason callback_reason)
{
- static int last_cursor_col;
-
if (callback_reason == CURSOR_COMMAND_MOVEMENT || callback_reason == CURSOR_RIGHT_LEFT_MOVEMENT) {
int y;
buffer_offset_to_xy(buf, buf->cursor_offset, -1, &buf->cursor_col, &y);
- last_cursor_col = buf->cursor_col;
}
}
@@ -564,61 +385,13 @@ cursor_callback(struct window_buffer* buf, enum cursor_reason callback_reason)
keep_cursor_col(buf, callback_reason);
move_selection(buf, callback_reason);
- printf("mved to: %d | reason: %d\n", buf->cursor_offset, callback_reason);
-}
-
-void
-add_to_undo_buffer(struct file_buffer* buffer, int offset, enum buffer_content_reason reason)
-{
- static time_t last_normal_edit;
- static int edits;
-
- if (reason == BUFFER_CONTENT_NORMAL_EDIT) {
- time_t previous_time = last_normal_edit;
- last_normal_edit = time(NULL);
-
- if (last_normal_edit - previous_time < 2 && edits < 30) {
- edits++;
- goto copy_undo_buffer;
- } else {
- edits = 0;
- }
- } else if (reason == BUFFER_CONTENT_INIT) {
- goto copy_undo_buffer;
- }
-
- if (buffer->available_redo_buffers > 0) {
- buffer->available_redo_buffers = 0;
- buffer->current_undo_buffer++;
- goto copy_undo_buffer;
- }
-
- if (buffer->current_undo_buffer == UNDO_BUFFERS_COUNT-1) {
- char* begin_buffer = buffer->ub[0].contents;
- memmove(buffer->ub, &(buffer->ub[1]), (UNDO_BUFFERS_COUNT-1) * sizeof(struct undo_buffer));
- buffer->ub[buffer->current_undo_buffer].contents = begin_buffer;
- } else {
- buffer->current_undo_buffer++;
- }
-
-copy_undo_buffer: ;
- struct undo_buffer* cub = &buffer->ub[buffer->current_undo_buffer];
-
- cub->contents = xrealloc(cub->contents, buffer->capacity);
- memcpy(cub->contents, buffer->contents, buffer->capacity);
- cub->len = buffer->len;
- cub->capacity = buffer->capacity;
- cub->cursor_offset = offset;
- if (focused_window)
- cub->y_scroll = focused_window->y_scroll;
- else
- cub->y_scroll = 0;
+ printf("moved to: %d | reason: %d\n", buf->cursor_offset, callback_reason);
}
void
buffer_content_callback(struct file_buffer* buffer, int offset, enum buffer_content_reason reason)
{
- add_to_undo_buffer(buffer, offset, reason);
+ buffer_add_to_undo(buffer, offset, reason);
}
int
@@ -659,6 +432,8 @@ keypress_actions(KeySym keysym, int modkey)
window_move_all_cursors_on_same_buf(&root_node, focused_node, focused_window->buffer_index, offset,
buffer_move_offset_relative, -move, CURSOR_COMMAND_MOVEMENT);
return 1;
+ case XK_Escape:
+ return 1;
case XK_Return:
delete_selection(fb);
@@ -705,7 +480,7 @@ void string_insert_callback(const char* buf, int buflen)
struct file_buffer* fb = get_file_buffer(focused_window);
// TODO: allow blocking of the bufferwrite, redirecting to keybinds with multiple characther length
- if (buf[0] >= 32) {
+ if (buf[0] >= 32 || buflen > 1) {
delete_selection(fb);
buffer_insert(fb, buf, buflen, focused_window->cursor_offset, 0);
window_move_all_cursors_on_same_buf(&root_node, NULL, focused_window->buffer_index, focused_window->cursor_offset,
diff --git a/plugins/color_schemes/gruvbox.h b/plugins/color_schemes/gruvbox.h
@@ -0,0 +1,66 @@
+// see colors at: https://github.com/morhetz/gruvbox
+
+enum colour_names {
+ bg,
+ bg0_h,
+ fg,
+ sel,
+ line,
+ red,
+ dark_green,
+ green,
+ teal,
+ yellow,
+ orange,
+ blue,
+ purple,
+ aqua,
+ gray,
+};
+
+const char * const colors[] = {
+ [bg] = "#282828",
+ [bg0_h] = "#1d2021",
+ [fg] = "#fbf1c7",
+ [sel] = "#504945",
+ [line] = "#32302f",
+ [red] = "#cc251d",
+ [dark_green] = "#98971a",
+ [green] = "#b8bb26",
+ [teal] = "#8ec07c",
+ [yellow] = "#fabd2f",
+ [orange] = "#d65d0e",
+ [blue] = "#458588",
+ [purple] = "#b16286",
+ [aqua] = "#83a598",
+ [gray] = "#a89984",
+ NULL
+};
+
+// default colors
+Glyph default_attributes = {.fg = fg, .bg = bg};
+unsigned int alternate_bg_bright = sel;
+unsigned int alternate_bg_dark = bg0_h;
+
+unsigned int cursor_fg = fg;
+unsigned int cursor_bg = bg;
+unsigned int mouse_line_bg = line;
+
+unsigned int selection_bg = sel;
+unsigned int highlight_color = yellow;
+unsigned int path_color = teal;
+
+unsigned int error_color = red;
+unsigned int warning_color = yellow;
+unsigned int ok_color = green;
+
+#define normal_color {.fg = fg}
+#define string_color {.fg = gray}
+#define comment_color {.fg = gray}
+#define type_color {.fg = teal}
+#define keyword_color {.fg = green}
+#define macro_color {.fg = yellow}
+#define operator_color {.fg = yellow, .mode = ATTR_BOLD}
+#define constants_color {.fg = dark_green}
+#define number_color {.fg = gray}
+#define function_color {.fg = aqua}
diff --git a/plugins/syntax/c.h b/plugins/syntax/c.h
@@ -0,0 +1,101 @@
+#include "handy_defines.h"
+
+#ifdef macro_color
+#define color_macro(_str) {COLOR_WORD,{_str}, macro_color}
+#endif
+
+const struct color_scheme_entry c_color_scheme[] = {
+ // Coloring type arguments Color
+
+ // strings
+#ifdef string_color
+ {COLOR_AROUND_TO_LINE, {"\"", "\""}, string_color},
+ {COLOR_STR, {"''"}, normal_color},
+ {COLOR_AROUND_TO_LINE, {"'", "'"}, string_color},
+ {COLOR_INSIDE_TO_LINE, {"#include <", ">"}, string_color},
+ {COLOR_INSIDE_TO_LINE, {"#include<", ">"}, string_color},
+#endif
+ // comments
+#ifdef comment_color
+ {COLOR_AROUND, {"/*", "*/"}, comment_color},
+ {COLOR_AROUND, {"//", "\n"}, comment_color},
+#endif
+ // macros
+#ifdef macro_color
+#ifdef constants_color
+ {COLOR_STR_AFTER_WORD, {"#ifdef"}, constants_color},
+ {COLOR_STR_AFTER_WORD, {"#ifndef"}, constants_color},
+ {COLOR_STR_AFTER_WORD, {"#define"}, constants_color},
+ {COLOR_STR_AFTER_WORD, {"#undef"}, constants_color},
+#endif // constants_color
+ {COLOR_WORD_STARTING_WITH_STR, {"#"}, {.fg = yellow, .mode = ATTR_BOLD}},
+ color_macro("sizeof"), color_macro("alignof"),
+ color_macro("offsetof"), color_macro("va_arg"),
+ color_macro("va_start"), color_macro("va_end"),
+ color_macro("va_copy"),
+ {COLOR_STR_AFTER_WORD, {"defined"}, constants_color},
+ color_macro("defined"),
+#endif
+ // operators
+#ifdef operator_color
+ {COLOR_STR, {"!="}, normal_color},
+ {COLOR_STR, {"!"}, operator_color},
+ {COLOR_STR, {"~"}, operator_color},
+ {COLOR_STR, {"?"}, operator_color},
+#endif
+ // keywords
+#ifdef keyword_color
+ {COLOR_STR, {"..."}, keyword_color},
+ {COLOR_WORD_STR, {"struct", "{"},keyword_color},
+ {COLOR_WORD_STR, {"union", "{"}, keyword_color},
+ {COLOR_WORD_STR, {"enum", "{"}, keyword_color},
+ {COLOR_STR_AFTER_WORD, {"struct"}, type_color},
+ {COLOR_STR_AFTER_WORD, {"union"}, type_color},
+ {COLOR_STR_AFTER_WORD, {"enum"}, type_color},
+ {COLOR_STR_AFTER_WORD, {"goto"}, constants_color},
+ {COLOR_WORD_INSIDE, {"}", ":"}, constants_color},
+ {COLOR_WORD_INSIDE, {"{", ":"}, constants_color},
+ {COLOR_WORD_INSIDE, {";", ":"}, constants_color},
+ color_keyword("struct"), color_keyword("enum"),
+ color_keyword("union"), color_keyword("const"),
+ color_keyword("typedef"), color_keyword("extern"),
+ color_keyword("static"), color_keyword("inline"),
+ color_keyword("if"), color_keyword("else"),
+ color_keyword("for"), color_keyword("while"),
+ color_keyword("case"), color_keyword("switch"),
+ color_keyword("do"), color_keyword("return"),
+ color_keyword("break"), color_keyword("continue"),
+ color_keyword("goto"), color_keyword("restrict"),
+ color_keyword("register"),
+#endif
+ // functions
+#ifdef function_color
+ {COLOR_WORD_BEFORE_STR, {"("}, function_color},
+#endif
+#ifdef constants_color
+ {COLOR_UPPER_CASE_WORD, {0}, constants_color},
+#endif
+ // types
+#ifdef type_color
+ color_type("int"), color_type("unsigned"),
+ color_type("long"), color_type("short"),
+ color_type("char"), color_type("void"),
+ color_type("float"), color_type("double"),
+ color_type("complex"), color_type("bool"),
+ color_type("_Bool"), color_type("FILE"),
+ color_type("va_list"),
+ {COLOR_WORD_ENDING_WITH_STR, {"_t"}, type_color},
+ {COLOR_WORD_ENDING_WITH_STR, {"_type"}, type_color},
+ {COLOR_WORD_ENDING_WITH_STR, {"T"}, type_color},
+#endif
+ // numbers
+#ifdef number_color
+ color_number("0"), color_number("1"),
+ color_number("2"), color_number("3"),
+ color_number("4"), color_number("5"),
+ color_number("6"), color_number("7"),
+ color_number("8"), color_number("9"),
+#endif
+};
+
+#define c_word_seperators default_word_seperators
diff --git a/plugins/syntax/handy_defines.h b/plugins/syntax/handy_defines.h
@@ -0,0 +1,20 @@
+#ifndef HANDY_DEFINES_H_
+#define HANDY_DEFINES_H_
+
+#define default_word_seperators "., \n\t*+-/%!~<>=(){}[]\"^&|\\\'?:;"
+
+#ifdef keyword_color
+#define color_keyword(_str) {COLOR_WORD,{_str}, keyword_color}
+#endif
+
+#ifdef type_color
+#define color_type(_str) {COLOR_WORD,{_str}, type_color}
+#endif
+
+#ifdef number_color
+#define color_number(_num) \
+ {COLOR_WORD_STARTING_WITH_STR, {_num}, number_color}, \
+ {COLOR_WORD_ENDING_WITH_STR, {_num".f"},number_color}
+#endif
+
+#endif // HANDY_DEFINES_H_
diff --git a/se.c b/se.c
@@ -10,6 +10,8 @@
#include <errno.h>
#include <assert.h>
#include <ctype.h>
+#include <stdarg.h>
+#include <time.h>
#include <dirent.h>
#include "se.h"
@@ -19,24 +21,19 @@
// config.c variables and globals
//
-// default colors
extern Glyph default_attributes;
extern unsigned int alternate_bg_bright;
extern unsigned int alternate_bg_dark;
-
extern unsigned int cursor_fg;
extern unsigned int cursor_bg;
extern unsigned int mouse_line_bg;
-
extern unsigned int selection_bg;
extern unsigned int highlight_color;
extern unsigned int path_color;
-
extern unsigned int error_color;
extern unsigned int warning_color;
extern unsigned int ok_color;
-// other
extern unsigned int tabspaces;
extern int wrap_buffer;
extern const struct color_scheme color_schemes[];
@@ -61,12 +58,11 @@ extern void(*buffer_written_to_screen_callback)(struct window_buffer*, int, int,
// Internal functions
//
-// path must be freed
static void recursive_mkdir(char* path);
+static int writef_string(int y, int x1, int x2, const char* fmt, ...);
static void draw_dir(const char* path, const char* search, int* sel, int minx, int miny, int maxx, int maxy, int focused);
static void color_selection(Glyph* letter);
-static void do_color_scheme(struct file_buffer* fb, struct color_scheme cs, int offset);
-static int write_string(const char* string, int y, int minx, int maxx);
+static void do_color_scheme(struct file_buffer* fb, const struct color_scheme* cs, int offset);
static int str_contains_char(const char* string, char check);
static int t_decode_utf8_buffer(const char* buffer, const int buflen, Rune* u);
static size_t utf8decode(const char *, Rune *, size_t);
@@ -74,6 +70,7 @@ static Rune utf8decodebyte(char, size_t *);
static char utf8encodebyte(Rune, size_t);
static size_t utf8validate(Rune *, size_t);
static int is_correct_mode(enum window_split_mode mode, enum move_directons move);
+void buffer_copy_ub_to_current(struct window_buffer* buffer);
////////////////////////////////////////////
// function implementations
@@ -182,16 +179,6 @@ die(const char *errstr, ...)
exit(1);
}
-int
-tattrset(int attr)
-{
- for (int i = 0; i < term.row-1; i++)
- for (int j = 0; j < term.col-1; j++)
- if (term.line[i][j].mode & attr)
- return 1;
- return 0;
-}
-
void
tnew(int col, int row)
{
@@ -199,17 +186,28 @@ tnew(int col, int row)
term = (Term){0};
tresize(col, row);
- tsetregion(0, 0, term.col-1, term.row-1, ' ');
}
+const struct color_scheme*
+buffer_get_color_scheme(struct file_buffer* fb)
+{
+ for (int i = 0; color_schemes[i].file_ending; i++) {
+ if (is_file_type(fb->file_path, color_schemes[i].file_ending)) {
+ return &color_schemes[i];
+ }
+ }
+ return NULL;
+}
struct window_buffer
window_buffer_new(int buffer_index)
{
struct window_buffer wb = {0};
wb.buffer_index = buffer_index;
- if (path_is_folder(get_file_buffer(&wb)->file_path))
+ if (path_is_folder(get_file_buffer(&wb)->file_path)) {
wb.mode = WINDOW_BUFFER_FILE_BROWSER;
+ writef_to_status_bar("opened file browser %s", get_file_buffer(&wb)->file_path);
+ }
return wb;
}
@@ -260,6 +258,98 @@ buffer_seek_string_backwards(const struct file_buffer* buf, int offset, const ch
return -1;
}
+int
+buffer_is_on_a_word(const struct file_buffer* fb, int offset, const char* word_seperators)
+{
+ LIMIT(offset, 0, fb->len);
+ return !str_contains_char(word_seperators, fb->contents[offset]);
+}
+
+int
+buffer_is_start_of_a_word(const struct file_buffer* fb, int offset, const char* word_seperators)
+{
+ return buffer_is_on_a_word(fb, offset, word_seperators) &&
+ (offset-1 <= 0 || str_contains_char(word_seperators, fb->contents[offset-1]));
+}
+
+int
+buffer_is_on_word(const struct file_buffer* fb, int offset, const char* word_seperators, const char* word)
+{
+ LIMIT(offset, 0, fb->len);
+ int word_start = buffer_seek_word_backwards(fb, offset, word_seperators);
+ int word_len = strlen(word);
+ if (word_start < offset - (word_len-1))
+ return 0;
+ return buffer_offset_starts_with(fb, word_start, word) &&
+ !buffer_is_on_a_word(fb, word_start + word_len, word_seperators);
+}
+
+int
+buffer_offset_starts_with(const struct file_buffer* fb, int offset, const char* start)
+{
+ LIMIT(offset, 0, fb->len);
+ int len = strlen(start);
+ if (len + offset > fb->len) return 0;
+
+ return memcmp(fb->contents + offset, start, len) == 0;
+}
+
+int
+buffer_seek_word(const struct file_buffer* fb, int offset, const char* word_seperators)
+{
+ if (buffer_is_on_a_word(fb, offset, word_seperators))
+ offset = buffer_seek_word_end(fb, offset, word_seperators);
+ while (offset < fb->len && !str_contains_char(word_seperators, fb->contents[offset])) offset++;
+ return offset;
+}
+
+int
+buffer_seek_word_end(const struct file_buffer* fb, int offset, const char* word_seperators)
+{
+ while (offset < fb->len && !str_contains_char(word_seperators, fb->contents[offset])) offset++;
+ return offset;
+}
+
+int
+buffer_seek_word_backwards(const struct file_buffer* fb, int offset, const char* word_seperators)
+{
+ LIMIT(offset, 0, fb->len);
+ if (!buffer_is_on_a_word(fb, offset, word_seperators))
+ while (offset > 0 && str_contains_char(word_seperators, fb->contents[offset])) offset--;
+ while (offset-1 > 0 && !str_contains_char(word_seperators, fb->contents[offset-1])) offset--;
+ return offset;
+}
+
+int
+buffer_seek_whitespace(const struct file_buffer* fb, int offset)
+{
+ while (offset < fb->len && !isspace(fb->contents[offset])) offset++;
+ return offset;
+}
+
+int
+buffer_seek_whitespace_backwrads(const struct file_buffer* fb, int offset)
+{
+ LIMIT(offset, 0, fb->len);
+ while (offset > 0 && !isspace(fb->contents[offset])) offset--;
+ return offset;
+}
+
+int
+buffer_seek_not_whitespace(const struct file_buffer* fb, int offset)
+{
+ while (offset < fb->len && isspace(fb->contents[offset])) offset++;
+ return offset;
+}
+
+int
+buffer_seek_not_whitespace_backwrads(const struct file_buffer* fb, int offset)
+{
+ LIMIT(offset, 0, fb->len);
+ while (offset > 0 && isspace(fb->contents[offset])) offset--;
+ return offset;
+}
+
void
buffer_move_on_line(struct window_buffer* buf, int amount, enum cursor_reason callback_reason)
{
@@ -433,8 +523,10 @@ window_node_split(struct window_split_node* parent, float ratio, enum window_spl
struct window_split_node*
window_node_delete(struct window_split_node* node)
{
- if (!node->parent)
+ if (!node->parent) {
+ writef_to_status_bar("can't close root winodw");
return node;
+ }
struct window_split_node* old = node;
node = node->parent;
struct window_split_node* other = (node->node1 == old) ? node->node2 : node->node1;
@@ -468,63 +560,13 @@ window_draw_tree_to_screen(struct window_split_node* root, int minx, int miny, i
window_draw_tree_to_screen(root->node1, minx, miny, middlex, maxy);
window_draw_tree_to_screen(root->node2, middlex+2, miny, maxx, maxy);
- // print connecting borders
- if (middlex+1 >= term.col)
- return;
- if (miny-1 >= 0 && miny-1 < term.row) {
- if (term.line[miny-1][middlex+1].u == L'┬' ||
- term.line[miny-1][middlex+1].u == L'┴' ||
- term.line[miny-1][middlex+1].u == L'├' ||
- term.line[miny-1][middlex+1].u == L'┤')
- tsetchar(L'┼', middlex+1, miny-1);
- if (term.line[miny-1][middlex+1].u == L'─')
- tsetchar(L'┬', middlex+1, miny-1);
- }
- if (maxy+1 >= 0 && maxy+1 < term.row) {
- if (term.line[maxy+1][middlex+1].u == L'┬' ||
- term.line[maxy+1][middlex+1].u == L'┴' ||
- term.line[maxy+1][middlex+1].u == L'├' ||
- term.line[maxy+1][middlex+1].u == L'┤')
- tsetchar(L'┼', middlex+1, maxy+1);
- if (term.line[maxy+1][middlex+1].u == L'─')
- tsetchar(L'┴', middlex+1, maxy+1);
- }
for (int y = miny; y < maxy+1; y++)
- xdrawline(term.line[y], middlex+1, y, middlex+2);
+ xdrawline(middlex+1, y, middlex+2);
} else if (root->mode == WINDOW_VERTICAL) {
int middley = ((float)(maxy - miny) * root->ratio) + miny;
- // print seperator
- tsetregion(minx, middley+1, maxx, middley+1, L'─');
- //write_string(get_file_buffer(&root->window)->file_path, middley+1, minx, maxx);
-
window_draw_tree_to_screen(root->node1, minx, miny, maxx, middley);
- window_draw_tree_to_screen(root->node2, minx, middley+2, maxx, maxy);
-
- // print connecting borders
- if (middley+1 >= term.row)
- return;
- if (minx-1 >= 0 && minx-1 < term.col) {
- if (term.line[middley+1][minx-1].u == L'┬' ||
- term.line[middley+1][minx-1].u == L'┴' ||
- term.line[middley+1][minx-1].u == L'├' ||
- term.line[middley+1][minx-1].u == L'┤')
- tsetchar(L'┼', minx-1, middley+1);
- if (term.line[middley+1][minx-1].u == L'│')
- tsetchar(L'├', minx-1, middley+1);
- }
- if (maxx+1 >= 0 && maxx+1 < term.col) {
- if (term.line[middley+1][maxx+1].u == L'┤')
- if (term.line[middley+1][maxx+1].u == L'┬' ||
- term.line[middley+1][maxx+1].u == L'┴' ||
- term.line[middley+1][maxx+1].u == L'├' ||
- term.line[middley+1][maxx+1].u == L'┤')
- tsetchar(L'┼', maxx+1, middley+1);
- if (term.line[middley+1][maxx+1].u == L'│')
- tsetchar(L'┤', maxx+1, middley+1);
- }
- for (int y = middley+1; y < middley+2; y++)
- xdrawline(term.line[y], minx, y, maxx+1);
+ window_draw_tree_to_screen(root->node2, minx, middley, maxx, maxy);
}
}
@@ -654,9 +696,11 @@ file_path_get_path(const char* path)
}
int
-file_browser_next_item(DIR* dir, const char* path, const char* search, char* full_path, char* filename)
+file_browser_next_item(DIR* dir, const char* path, const char* search, char* full_path, char* filename, int* offset)
{
+ static char filename_search[PATH_MAX];
assert(path);
+ assert(search);
assert(dir);
assert(strlen(path) < PATH_MAX+1);
int len = strlen(search);
@@ -668,19 +712,39 @@ file_browser_next_item(DIR* dir, const char* path, const char* search, char* ful
strcat(full_path, filename);
if (path_is_folder(full_path))
strcat(filename, "/");
+ strcpy(filename_search, filename);
- if (memcmp(filename, search, len) == 0) {
- if (search[0] != '.' && folder->d_name[0] == '.')
- continue;
- if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0)
- continue;
- return 1;
+ const char* s_repl = search;
+ while(!isupper(*s_repl++)) {
+ if (*s_repl == 0) {
+ for (char* fs = filename_search; *fs; fs++)
+ *fs = tolower(*fs);
+ break;
+ }
+ }
+
+ int f_len = strlen(filename_search);
+ char* search_start = filename_search;
+ if (!*search)
+ goto search_match;
+ while((search_start = memchr(search_start, *search, f_len))) {
+ if (memcmp(search_start, search, len) == 0) {
+ search_match:
+ if (search[0] != '.' && folder->d_name[0] == '.')
+ break;
+ if (strcmp(filename_search, "./") == 0 || strcmp(filename, "../") == 0)
+ break;
+ *offset = search_start - filename_search;
+ return 1;
+ }
+ search_start++;
}
}
*filename = *full_path = 0;
return 0;
}
+//TODO: generalise and reuse for other searching applications
void
draw_dir(const char* path, const char* search, int* sel, int minx, int miny, int maxx, int maxy, int focused)
{
@@ -689,6 +753,8 @@ draw_dir(const char* path, const char* search, int* sel, int minx, int miny, int
assert(path);
assert(sel);
+
+ // change background color
global_attr.bg = alternate_bg_dark;
tsetregion(minx, miny+1, maxx, maxy, ' ');
global_attr = default_attributes;
@@ -696,36 +762,41 @@ draw_dir(const char* path, const char* search, int* sel, int minx, int miny, int
int len = strlen(search);
DIR *dir = opendir(path);
+ // count folders to get scroll
int folder_lines = maxy - miny - 1;
int folders = 0;
- while(file_browser_next_item(dir, path, search, full_path, filename))
+ int tmp_offset;
+ while(file_browser_next_item(dir, path, search, full_path, filename, &tmp_offset))
folders++;
-
rewinddir(dir);
*sel = MIN(*sel, folders-1);
int sel_local = *sel;
+
+ // print num of files
char count[256];
if (sel_local > folder_lines)
- snprintf(count, 256, "^[%2d] ", folders);
- else if (folders > folder_lines)
- snprintf(count, 256, "ˇ[%2d] ", folders);
+ snprintf(count, sizeof(count), "^[%2d] ", folders);
+ else if (folders-1 > folder_lines)
+ snprintf(count, sizeof(count), "ˇ[%2d] ", folders);
else
- snprintf(count, 256, " [%2d] ", folders);
+ snprintf(count, sizeof(count), " [%2d] ", folders);
+ // print current input, where the folder path not in the search
+ // is path_color while the search is default
global_attr.fg = path_color;
int new_x = write_string(count, miny, minx, maxx+1);
new_x = write_string(path, miny, new_x, maxx+1);
-
global_attr = default_attributes;
new_x = write_string(search, miny, new_x, maxx+1);
- global_attr = default_attributes;
- global_attr.bg = alternate_bg_dark;
-
+ // print folders
int start_miny = miny;
+ int offset;
folders--;
miny++;
- while(miny < maxy && file_browser_next_item(dir, path, search, full_path, filename)) {
+ while(miny <= maxy && file_browser_next_item(dir, path, search, full_path, filename, &offset)) {
+ global_attr = default_attributes;
+ global_attr.bg = alternate_bg_dark;
if (path_is_folder(full_path))
global_attr.fg = path_color;
else
@@ -736,19 +807,20 @@ draw_dir(const char* path, const char* search, int* sel, int minx, int miny, int
sel_local--;
continue;
}
-
write_string(filename, miny, minx, maxx+1);
- for (int i = minx; i < minx + len; i++)
- term.line[miny][i].fg = highlight_color;
+
+ // change the color to highlight search term
+ for (int i = minx + offset; i < minx + len + offset && i < maxx; i++)
+ tsetattr(i, miny)->fg = highlight_color;
+ // change the background of the selected line
if (miny - start_miny - 1 == sel_local)
for (int i = minx; i < maxx+1; i++)
- term.line[miny][i].bg = selection_bg;
+ tsetattr(i, miny)->bg = selection_bg;
miny++;
}
- miny = MIN(miny, maxy);
closedir(dir);
-
+ // print message if the file doesn't exist, error trying to make folder
if (folders < 0) {
global_attr = default_attributes;
if (search[strlen(search)-1] == '/')
@@ -758,8 +830,10 @@ draw_dir(const char* path, const char* search, int* sel, int minx, int miny, int
write_string(" [Create New File]", start_miny, new_x, maxx+1);
}
+ // draw
+
for (int y = start_miny; y < maxy+1; y++)
- xdrawline(term.line[y], minx, y, maxx+1);
+ xdrawline(minx, y, maxx+1);
xdrawcursor(new_x, start_miny, focused);
@@ -779,6 +853,143 @@ void recursive_mkdir(char *path) {
fprintf(stderr, "error while trying to create '%s'\n%s\n", path, strerror(errno));
}
+int
+writef_string(int y, int x1, int x2, const char* fmt, ...)
+{
+ char string[STATUS_BAR_MAX_LEN];
+
+ va_list args;
+ va_start(args, fmt);
+ vsnprintf(string, STATUS_BAR_MAX_LEN, fmt, args);
+ va_end(args);
+
+ return write_string(string, y, x1, x2);
+}
+
+int
+writef_to_status_bar(const char* fmt, ...)
+{
+ static char string[STATUS_BAR_MAX_LEN];
+ if (!fmt)
+ return write_string(string, term.row-1, 0, term.col);
+
+ va_list args;
+ va_start(args, fmt);
+ vsnprintf(string, STATUS_BAR_MAX_LEN, fmt, args);
+ va_end(args);
+
+ return write_string(string, term.row-1, 0, term.col);
+}
+
+void
+draw_status_bar()
+{
+ // change background color
+ global_attr = default_attributes;
+ global_attr.bg = alternate_bg_dark;
+ tsetregion(0, term.row-1, term.col-1, term.row-1, ' ');
+ writef_to_status_bar(NULL);
+ global_attr = default_attributes;
+
+ xdrawline(0, term.row-1, term.col);
+ draw_horisontal_line(term.row-2, 0, term.col-1);
+}
+
+void
+buffer_copy_ub_to_current(struct window_buffer* buffer)
+{
+ struct file_buffer* fb = get_file_buffer(buffer);
+ struct undo_buffer* cub = &fb->ub[fb->current_undo_buffer];
+ assert(cub->contents);
+
+ fb->contents = xrealloc(fb->contents, cub->capacity);
+ memcpy(fb->contents, cub->contents, cub->capacity);
+ fb->len = cub->len;
+ fb->capacity = cub->capacity;
+
+ buffer_move_to_offset(buffer, cub->cursor_offset, CURSOR_SNAPPED);
+ buffer->y_scroll = cub->y_scroll;
+}
+
+void
+buffer_undo(struct file_buffer* buf)
+{
+ struct file_buffer* fb = get_file_buffer(focused_window);
+ if (fb->current_undo_buffer == 0) {
+ writef_to_status_bar("end of undo buffer");
+ return;
+ }
+ fb->current_undo_buffer--;
+ fb->available_redo_buffers++;
+
+ buffer_copy_ub_to_current(focused_window);
+ writef_to_status_bar("undo");
+}
+
+void
+buffer_redo(struct file_buffer* buf)
+{
+ struct file_buffer* fb = get_file_buffer(focused_window);
+ if (fb->available_redo_buffers == 0) {
+ writef_to_status_bar("end of redo buffer");
+ return;
+ }
+ fb->available_redo_buffers--;
+ fb->current_undo_buffer++;
+
+ buffer_copy_ub_to_current(focused_window);
+ writef_to_status_bar("redo");
+}
+
+void
+buffer_add_to_undo(struct file_buffer* buffer, int offset, enum buffer_content_reason reason)
+{
+ static time_t last_normal_edit;
+ static int edits;
+
+ if (reason == BUFFER_CONTENT_NORMAL_EDIT) {
+ time_t previous_time = last_normal_edit;
+ last_normal_edit = time(NULL);
+
+ if (last_normal_edit - previous_time < 2 && edits < 30) {
+ edits++;
+ goto copy_undo_buffer;
+ } else {
+ edits = 0;
+ }
+ } else if (reason == BUFFER_CONTENT_INIT) {
+ goto copy_undo_buffer;
+ }
+
+ if (buffer->available_redo_buffers > 0) {
+ buffer->available_redo_buffers = 0;
+ buffer->current_undo_buffer++;
+ goto copy_undo_buffer;
+ }
+
+ if (buffer->current_undo_buffer == UNDO_BUFFERS_COUNT-1) {
+ char* begin_buffer = buffer->ub[0].contents;
+ memmove(buffer->ub, &(buffer->ub[1]), (UNDO_BUFFERS_COUNT-1) * sizeof(struct undo_buffer));
+ buffer->ub[buffer->current_undo_buffer].contents = begin_buffer;
+ } else {
+ buffer->current_undo_buffer++;
+ }
+
+copy_undo_buffer: ;
+ struct undo_buffer* cub = &buffer->ub[buffer->current_undo_buffer];
+
+ cub->contents = xrealloc(cub->contents, buffer->capacity);
+ memcpy(cub->contents, buffer->contents, buffer->capacity);
+ cub->len = buffer->len;
+ cub->capacity = buffer->capacity;
+ cub->cursor_offset = offset;
+ if (focused_window)
+ cub->y_scroll = focused_window->y_scroll;
+ else
+ cub->y_scroll = 0;
+}
+
+
struct file_buffer
buffer_new(const char* file_path)
{
@@ -795,6 +1006,7 @@ buffer_new(const char* file_path)
fclose(new_file);
realpath(file_path, buffer.file_path);
+ writef_to_status_bar("created new file %s", buffer.file_path);
}
if (path_is_folder(buffer.file_path)) {
@@ -839,6 +1051,8 @@ buffer_new(const char* file_path)
if (buffer_contents_updated)
buffer_contents_updated(&buffer, 0, BUFFER_CONTENT_INIT);
+ if (res)
+ writef_to_status_bar("new buffer %s", buffer.file_path);
return buffer;
}
@@ -988,8 +1202,8 @@ buffer_draw_to_screen(struct window_buffer* buf, int minx, int miny, int maxx, i
LIMIT(maxx, 0, term.col-1);
LIMIT(maxy, 0, term.row-1);
- LIMIT(minx, 0, maxx-1);
- LIMIT(miny, 0, maxy-1);
+ LIMIT(minx, 0, maxx);
+ LIMIT(miny, 0, maxy);
LIMIT(buf->cursor_offset, 0, fb->len);
tsetregion(minx, miny, maxx, maxy, ' ');
@@ -1005,17 +1219,9 @@ buffer_draw_to_screen(struct window_buffer* buf, int minx, int miny, int maxx, i
return;
}
- int color_scheme_available = -1;
- for (int i = 0; color_schemes[i].file_ending; i++) {
- if (is_file_type(fb->file_path, color_schemes[i].file_ending)) {
- color_scheme_available = i;
- break;
- }
- }
-
int x = minx, y = miny;
global_attr = default_attributes;
- do_color_scheme(NULL, (struct color_scheme){0}, 0);
+ do_color_scheme(NULL, &(struct color_scheme){0}, 0);
// force the screen in a place where the cursor is visable
int ox, oy;
@@ -1027,7 +1233,7 @@ buffer_draw_to_screen(struct window_buffer* buf, int minx, int miny, int maxx, i
if (oy < 0) {
buf->y_scroll += oy;
} else {
- oy += miny - maxy;
+ oy += miny - maxy+2;
if (oy > 0)
buf->y_scroll += oy;
}
@@ -1053,25 +1259,27 @@ buffer_draw_to_screen(struct window_buffer* buf, int minx, int miny, int maxx, i
int offset_start = repl - fb->contents;
int cursor_x = 0, cursor_y = 0;
+ const struct color_scheme* cs = buffer_get_color_scheme(fb);
+
// search backwards to find multi-line syntax highlighting
- if (color_scheme_available >= 0) {
- for (int i = 0; i < color_schemes[color_scheme_available].entry_count; i++) {
- const struct color_scheme_entry cs = color_schemes[color_scheme_available].entries[i];
- if (cs.mode == COLOR_AROUND || cs.mode == COLOR_INSIDE) {
+ if (cs) {
+ for (int i = 0; i < cs->entry_count; i++) {
+ const struct color_scheme_entry cse = cs->entries[i];
+ if (cse.mode == COLOR_AROUND || cse.mode == COLOR_INSIDE) {
int offset = 0;
int count = 0;
- int start_len = strlen(cs.arg.start);
- while((offset = buffer_seek_string(fb, offset, cs.arg.start)) >= 0) {
+ int start_len = strlen(cse.arg.start);
+ while((offset = buffer_seek_string(fb, offset, cse.arg.start)) >= 0) {
offset += start_len;
if (offset >= offset_start)
break;
count++;
}
- if (strcmp(cs.arg.start, cs.arg.end) != 0) {
- int end_len = strlen(cs.arg.end);
+ if (strcmp(cse.arg.start, cse.arg.end) != 0) {
+ int end_len = strlen(cse.arg.end);
offset = 0;
- while((offset = buffer_seek_string(fb, offset, cs.arg.end)) >= 0) {
+ while((offset = buffer_seek_string(fb, offset, cse.arg.end)) >= 0) {
offset += end_len;
if (offset >= offset_start)
break;
@@ -1079,8 +1287,8 @@ buffer_draw_to_screen(struct window_buffer* buf, int minx, int miny, int maxx, i
}
}
if (count > 0) {
- offset = buffer_seek_string_backwards(fb, offset_start, cs.arg.start);
- do_color_scheme(fb, color_schemes[color_scheme_available], offset);
+ offset = buffer_seek_string_backwards(fb, offset_start, cse.arg.start);
+ do_color_scheme(fb, cs, offset);
break;
}
}
@@ -1099,8 +1307,8 @@ buffer_draw_to_screen(struct window_buffer* buf, int minx, int miny, int maxx, i
LIMIT(cursor_y, miny, maxy);
}
- if (color_scheme_available >= 0)
- do_color_scheme(fb, color_schemes[color_scheme_available], repl - fb->contents);
+ if (cs)
+ do_color_scheme(fb, cs, repl - fb->contents);
if (!wrap_buffer && x - xscroll > maxx && *repl != '\n') {
charsize = 1;
@@ -1110,7 +1318,7 @@ buffer_draw_to_screen(struct window_buffer* buf, int minx, int miny, int maxx, i
if (*repl == '\n' || (wrap_buffer && x >= maxx)) {
x = minx;
- if (++y > maxy)
+ if (++y >= maxy-1)
break;
if (wrap_buffer && *repl != '\n')
continue;
@@ -1118,12 +1326,12 @@ buffer_draw_to_screen(struct window_buffer* buf, int minx, int miny, int maxx, i
continue;
} else if (*repl == '\t') {
charsize = 1;
- if (x <= 0) {
+ if ((x - minx) <= 0) {
x += tsetchar(' ', x - xscroll, y);
if (x >= maxx)
continue;
}
- while (x % tabspaces != 0 && x - xscroll <= maxx)
+ while ((x - minx) % tabspaces != 0 && x - xscroll <= maxx)
x += tsetchar(' ', x - xscroll, y);
if (x - xscroll <= maxx)
@@ -1143,30 +1351,48 @@ buffer_draw_to_screen(struct window_buffer* buf, int minx, int miny, int maxx, i
x += width;
}
int offset_end = repl - fb->contents;
+ global_attr = default_attributes;
if (buf->cursor_offset >= fb->len) {
cursor_x = x - xscroll;
- cursor_y = y;
+ cursor_y = MIN(y, maxy);
}
if(buffer_written_to_screen_callback)
buffer_written_to_screen_callback(buf, offset_start, offset_end, minx, miny, maxx, maxy);
- if (buf == focused_window)
- for (int i = minx; i < maxx+1; i++)
- term.line[cursor_y][i].bg = mouse_line_bg;
+ // TODO: let the user do this
+ int status_end = writef_string(maxy-1, minx, maxx+1, " %dk %s %d:%d %d%%",
+ fb->len/1000, fb->file_path, cursor_y + buf->y_scroll, cursor_x,
+ (int)(((float)(buf->cursor_offset+1)/(float)fb->len)*100.0f));
+ if (fb->mode & BUFFER_SELECTION_ON) {
+ int y1, y2, tmp;
+ buffer_offset_to_xy(buf, fb->s1o, 0, &tmp, &y1);
+ buffer_offset_to_xy(buf, fb->s2o, 0, &tmp, &y2);
+ writef_string(maxy-1, status_end, maxx, " %dL", abs(y1-y2));
+ }
+
+ if (buf == focused_window) {
+ for (int i = minx; i < maxx+1; i++) {
+ if (!(fb->mode & BUFFER_SELECTION_ON))
+ tsetattr(i, cursor_y)->bg = mouse_line_bg;
+ tsetattr(i, maxy-1)->bg = alternate_bg_bright;
+ }
+ }
buffer_write_selection(buf, minx, miny, maxx, maxy);
- do_color_scheme(NULL, (struct color_scheme){0}, 0);
+ do_color_scheme(NULL, &(struct color_scheme){0}, 0);
- for (int i = miny; i < maxy+1; i++)
- xdrawline(term.line[i], minx, i, maxx+1);
+ for (int i = miny; i < maxy; i++)
+ xdrawline(minx, i, maxx+1);
+ draw_horisontal_line(maxy-1, minx, maxx);
xdrawcursor(cursor_x, cursor_y, buf == focused_window);
}
+// TODO: Scope checks (with it's own system, so that it can be used for auto indent as well)
void
-do_color_scheme(struct file_buffer* fb, struct color_scheme cs, int offset)
+do_color_scheme(struct file_buffer* fb, const struct color_scheme* cs, int offset)
{
static int end_at_whitespace = 0;
static const char* end_condition;
@@ -1175,7 +1401,7 @@ do_color_scheme(struct file_buffer* fb, struct color_scheme cs, int offset)
static int color_next_word = 0;
static int around = 0;
- if (!fb) {
+ if (!fb || !cs) {
// reset
end_at_whitespace = 0;
end_condition_len = 0;
@@ -1199,14 +1425,13 @@ do_color_scheme(struct file_buffer* fb, struct color_scheme cs, int offset)
end_condition = NULL;
end_at_whitespace = 0;
global_attr = default_attributes;
- } else if (memcmp(buf + offset, end_condition, end_condition_len) == 0) {
- // end word mathces
+ } else if (buffer_offset_starts_with(fb, offset, end_condition)) {
if (isspace(end_condition[end_condition_len-1])) {
end_condition_len--;
if (end_condition_len <= 0)
global_attr = default_attributes;
}
- // if it's around not inside, don't reset color
+ // if it's around not inside, don't reset color until later
if (around)
around = 0;
else
@@ -1217,7 +1442,7 @@ do_color_scheme(struct file_buffer* fb, struct color_scheme cs, int offset)
}
return;
} else if (end_at_whitespace) {
- if (str_contains_char(cs.word_seperators, buf[offset])) {
+ if (!buffer_is_on_a_word(fb, offset, cs->word_seperators)) {
end_at_whitespace = 0;
global_attr = default_attributes;
} else {
@@ -1225,7 +1450,7 @@ do_color_scheme(struct file_buffer* fb, struct color_scheme cs, int offset)
}
} else if (color_next_word) {
// check if new word encountered
- if (str_contains_char(cs.word_seperators, buf[offset]))
+ if (!buffer_is_on_a_word(fb, offset, cs->word_seperators))
return;
global_attr = next_word_attr;
color_next_word = 0;
@@ -1240,24 +1465,24 @@ do_color_scheme(struct file_buffer* fb, struct color_scheme cs, int offset)
return;
}
- for (int i = 0; i < cs.entry_count; i++) {
- struct color_scheme_entry entry = cs.entries[i];
+ for (int i = 0; i < cs->entry_count; i++) {
+ struct color_scheme_entry entry = cs->entries[i];
enum color_scheme_mode mode = entry.mode;
if (mode == COLOR_UPPER_CASE_WORD) {
- // check if this is a new word
- if (str_contains_char(cs.word_seperators, buf[offset])) continue;
+ if (!buffer_is_start_of_a_word(fb, offset, cs->word_seperators))
+ continue;
- // check if it's upper case
int end_len = 0;
- while (offset + end_len < fb->len && !str_contains_char(cs.word_seperators, buf[offset + end_len])) {
+ while (offset + end_len < fb->len && !str_contains_char(cs->word_seperators, buf[offset + end_len])) {
if (!isupper(buf[offset + end_len]) && buf[offset + end_len] != '_'
&& (!end_len || (buf[offset + end_len] < '0' || buf[offset + end_len] > '9')))
goto not_upper_case;
end_len++;
}
- // upper case words must be longer than x chars
- if (end_len < 3) continue;
+ // upper case words must be longer than UPPER_CASE_WORD_MIN_LEN chars
+ if (end_len < UPPER_CASE_WORD_MIN_LEN)
+ continue;
global_attr = entry.attr;
end_condition_len = end_len;
@@ -1271,7 +1496,7 @@ do_color_scheme(struct file_buffer* fb, struct color_scheme cs, int offset)
if (mode == COLOR_WORD_BEFORE_STR || mode == COLOR_WORD_BEFORE_STR_STR || mode == COLOR_WORD_ENDING_WITH_STR) {
// check if this is a new word
- if (str_contains_char(cs.word_seperators, buf[offset])) continue;
+ if (str_contains_char(cs->word_seperators, buf[offset])) continue;
int offset_tmp = offset;
// find new word twice if it's BEFORE_STR_STR
@@ -1280,34 +1505,24 @@ do_color_scheme(struct file_buffer* fb, struct color_scheme cs, int offset)
int first_time = 1;
while (times--) {
// seek end of word
- int chars = 0;
- while (offset_tmp < fb->len && !str_contains_char(cs.word_seperators, buf[offset_tmp])) {
- if (buf[offset_tmp] != '*')
- chars++;
- offset_tmp++;
- }
- if (!chars && mode == COLOR_WORD_BEFORE_STR_STR)
+ offset_tmp = buffer_seek_word_end(fb, offset_tmp, cs->word_seperators);
+ if (offset_tmp == offset && mode == COLOR_WORD_BEFORE_STR_STR)
goto exit_word_before_str_str;
if (first_time)
- first_word_len = chars;
-
- // seek start of word
- if (mode != COLOR_WORD_ENDING_WITH_STR) {
- int whiespaces = 0;
- while (offset_tmp < fb->len && (isspace(buf[offset_tmp]) || buf[offset_tmp] == '*')) {
- offset_tmp++;
- whiespaces++;
- }
- }
+ first_word_len = offset_tmp - offset;
+
+ if (mode != COLOR_WORD_ENDING_WITH_STR)
+ offset_tmp = buffer_seek_not_whitespace(fb, offset_tmp);
+
first_time = 0;
}
+
if (mode == COLOR_WORD_ENDING_WITH_STR) {
offset_tmp -= len;
if (offset_tmp < 0)
continue;
}
- // check if string matches
- if (memcmp(buf + offset_tmp, entry.arg.start, len) == 0) {
+ if (buffer_offset_starts_with(fb, offset_tmp, entry.arg.start)) {
global_attr = entry.attr;
end_condition_len = first_word_len;
return;
@@ -1320,25 +1535,24 @@ do_color_scheme(struct file_buffer* fb, struct color_scheme cs, int offset)
if (offset - len < 0)
continue;
// check the if what's behind the cursor is the first string
- if (memcmp(buf + offset - len, entry.arg.start, len) == 0) {
- assert(entry.arg.end);
- int end_len = strlen(entry.arg.end);
- if (offset < fb->len && memcmp(buf + offset, entry.arg.end, end_len) == 0)
+ if (buffer_offset_starts_with(fb, offset - len, entry.arg.start)) {
+ if (offset < fb->len && buffer_offset_starts_with(fb, offset, entry.arg.end))
continue;
if (mode == COLOR_WORD_INSIDE) {
// verify that only one word exists inside
int offset_tmp = offset;
- while (offset_tmp < fb->len && isspace(buf[offset_tmp])) offset_tmp++;
- while (offset_tmp < fb->len && !str_contains_char(cs.word_seperators, buf[offset_tmp])) offset_tmp++;
- while (offset_tmp < fb->len && isspace(buf[offset_tmp])) offset_tmp++;
- if (memcmp(buf + offset_tmp, entry.arg.end, end_len) != 0
- || offset_tmp - offset <= 1)
+ offset_tmp = buffer_seek_not_whitespace(fb, offset_tmp);
+ offset_tmp = buffer_seek_word_end(fb, offset_tmp, cs->word_seperators);
+ offset_tmp = buffer_seek_not_whitespace(fb, offset_tmp);
+
+ if (!buffer_offset_starts_with(fb, offset_tmp, entry.arg.end) ||
+ offset_tmp - offset <= 1)
continue;
}
end_condition = entry.arg.end;
- end_condition_len = end_len;
+ end_condition_len = strlen(entry.arg.end);
global_attr = entry.attr;
around = 0;
if (entry.mode == COLOR_INSIDE_TO_LINE)
@@ -1348,69 +1562,63 @@ do_color_scheme(struct file_buffer* fb, struct color_scheme cs, int offset)
continue;
}
- // the rest of the conditions all check if the first string matches
- if (buflen - offset <= len)
- continue;
- if (memcmp(buf + offset, entry.arg.start, len) == 0) {
- if (mode == COLOR_AROUND || mode == COLOR_AROUND_TO_LINE) {
- assert(entry.arg.end);
- end_condition = entry.arg.end;
- end_condition_len = strlen(entry.arg.end);
- around = 1;
- if (entry.mode == COLOR_AROUND_TO_LINE)
- end_at_whitespace = 1;
- } else if (mode == COLOR_WORD || mode == COLOR_STR_AFTER_WORD ||
- mode == COLOR_WORD_STR || mode == COLOR_WORD_STARTING_WITH_STR) {
+ if ((mode == COLOR_AROUND || mode == COLOR_AROUND_TO_LINE) &&
+ buffer_offset_starts_with(fb, offset, entry.arg.start)) {
+ end_condition = entry.arg.end;
+ end_condition_len = strlen(entry.arg.end);
+ around = 1;
+ if (entry.mode == COLOR_AROUND_TO_LINE)
+ end_at_whitespace = 1;
+ global_attr = entry.attr;
+ return;
+ }
+ if (mode == COLOR_WORD || mode == COLOR_STR_AFTER_WORD ||
+ mode == COLOR_WORD_STR || mode == COLOR_WORD_STARTING_WITH_STR) {
- // check if this is the start of a new word that matches word exactly(except for WORD_STARTING_WITH_STR)
- if ((offset > 0 && !str_contains_char(cs.word_seperators, buf[offset-1])) ||
- (buflen - (offset+len) > len && !str_contains_char(cs.word_seperators, buf[offset+len])
- && mode != COLOR_WORD_STARTING_WITH_STR))
- continue;
+ // check if this is the start of a new word that matches word exactly(except for WORD_STARTING_WITH_STR)
+ if(!buffer_offset_starts_with(fb, offset, entry.arg.start) ||
+ !buffer_is_start_of_a_word(fb, offset, cs->word_seperators) ||
+ (buffer_is_on_a_word(fb, offset + len, cs->word_seperators) && mode != COLOR_WORD_STARTING_WITH_STR))
+ continue;
- if (mode == COLOR_WORD_STR) {
- assert(entry.arg.end);
- int offset_tmp = offset + len;
- // move to next string
- while (offset_tmp < fb->len && isspace(fb->contents[offset_tmp]))
- offset_tmp++;
+ if (mode == COLOR_WORD_STR) {
+ int offset_str = buffer_seek_not_whitespace(fb, offset + len);
- int end_len = strlen(entry.arg.end);
- if (offset_tmp + end_len >= fb->len ||
- memcmp(buf + offset_tmp, entry.arg.end, end_len) != 0)
- continue;
- end_condition_len = offset_tmp - offset;
- } else {
- end_at_whitespace = 1;
- }
- if (mode == COLOR_STR_AFTER_WORD) {
- next_word_attr = entry.attr;
- color_next_word = 1;
+ if (!buffer_offset_starts_with(fb, offset_str, entry.arg.end))
continue;
- }
- } else if (mode == COLOR_STR) {
- end_condition_len = len;
+ end_condition_len = strlen(entry.arg.start);
+ } else {
+ end_at_whitespace = 1;
}
-
- global_attr= entry.attr;
+ if (mode == COLOR_STR_AFTER_WORD) {
+ next_word_attr = entry.attr;
+ color_next_word = 1;
+ continue;
+ }
+ global_attr = entry.attr;
+ return;
+ }
+ if (mode == COLOR_STR) {
+ if (!buffer_offset_starts_with(fb, offset, entry.arg.start))
+ continue;
+ end_condition_len = len;
+ global_attr = entry.attr;
return;
}
}
}
-
int
write_string(const char* string, int y, int minx, int maxx)
{
LIMIT(maxx, 0, term.col);
- LIMIT(minx, 0, maxx-1);
+ LIMIT(minx, 0, maxx);
int offset = 0;
int len = strlen(string);
while(minx < maxx && offset < len) {
Rune u;
- int charsize = t_decode_utf8_buffer(string + offset, len - offset, &u);
- offset += charsize;
+ offset += t_decode_utf8_buffer(string + offset, len - offset, &u);
minx += tsetchar(u, minx, y);
}
return minx;
@@ -1457,8 +1665,8 @@ buffer_write_selection(struct window_buffer* buf, int minx, int miny, int maxx,
LIMIT(maxx, 0, term.col-1);
LIMIT(maxy, 0, term.row-1);
- LIMIT(minx, 0, maxx-1);
- LIMIT(miny, 0, maxy-1);
+ LIMIT(minx, 0, maxx);
+ LIMIT(miny, 0, maxy);
//TODO: implement alternative selection modes
if (!(fb->mode & BUFFER_SELECTION_ON))
@@ -1478,11 +1686,11 @@ buffer_write_selection(struct window_buffer* buf, int minx, int miny, int maxx,
for(; y < y2; y++) {
for(; x < maxx; x++)
- color_selection(&term.line[y][x]);
+ color_selection(tsetattr(x, y));
x = 0;
}
for(; x < x2; x++)
- color_selection(&term.line[y][x]);
+ color_selection(tsetattr(x, y));
}
char* buffer_get_selection(struct file_buffer* buffer, int* selection_len)
@@ -1540,6 +1748,7 @@ buffer_write_to_filepath(const struct file_buffer* buffer)
if (buffer->mode & BUFFER_UTF8_SIGNED)
fwrite("\xEF\xBB\xBF", 1, 3, file);
fwrite(buffer->contents, sizeof(char), buffer->len, file);
+ writef_to_status_bar("written buffer to %s", buffer->file_path);
fclose(file);
}
@@ -1568,6 +1777,8 @@ tsetchar(Rune u, int x, int y)
y < 0 || x < 0)
return 1;
+ if (u == 0)
+ u = term.line[y][x].u;
int width = wcwidth(u);
if (width == -1)
width = 1;
@@ -1576,10 +1787,10 @@ tsetchar(Rune u, int x, int y)
if (term.line[y][x].mode & ATTR_WIDE || attr.mode & ATTR_WIDE) {
if (x+1 < term.col) {
- term.line[y][x+1].u = 0;
+ term.line[y][x+1].u = ' ';
term.line[y][x+1].mode |= ATTR_WDUMMY;
}
- } else if (term.line[y][x].mode & ATTR_WDUMMY) {
+ } else if (term.line[y][x].mode & ATTR_WDUMMY && x-1 >= 0) {
term.line[y][x-1].u = ' ';
term.line[y][x-1].mode &= ~ATTR_WIDE;
}
@@ -1590,38 +1801,38 @@ tsetchar(Rune u, int x, int y)
return width;
}
-void
-tsetregion(int x1, int y1, int x2, int y2, Rune u)
+Glyph*
+tsetattr(int x, int y)
{
- int x, y, temp;
-
- if (x1 > x2)
- temp = x1, x1 = x2, x2 = temp;
- if (y1 > y2)
- temp = y1, y1 = y2, y2 = temp;
+ static Glyph dummy;
+ if (y >= term.row || x >= term.col ||
+ y < 0 || x < 0)
+ return &dummy;
- LIMIT(x1, 0, term.col-1);
- LIMIT(x2, 0, term.col-1);
- LIMIT(y1, 0, term.row-1);
- LIMIT(y2, 0, term.row-1);
+ return &term.line[y][x];
+}
- assert(term.line);
- assert(term.line[0]);
+Rune
+tgetrune(int x, int y)
+{
+ if (y >= term.row || x >= term.col ||
+ y < 0 || x < 0)
+ return 0;
+ return term.line[y][x].u;
+}
- for (y = y1; y <= y2; y++) {
- for (x = x1; x <= x2; x++) {
- term.line[y][x] = global_attr;
- term.line[y][x].u = u;
- }
- }
+void
+tsetregion(int x1, int y1, int x2, int y2, Rune u)
+{
+ for (int y = y1; y <= y2; y++)
+ for (int x = x1; x <= x2; x++)
+ tsetchar(u, x, y);
}
void
tresize(int col, int row)
{
int i;
- int minrow = MIN(row, term.row);
- int mincol = MIN(col, term.col);
if (col < 1 || row < 1) {
fprintf(stderr,
@@ -1629,10 +1840,13 @@ tresize(int col, int row)
return;
}
- /* resize to new height */
- if (row < term.row)
- for (i = row; i < term.row; i++)
+ // resize to new height
+ if (row < term.row) {
+ for (i = row; i < term.row; i++) {
free(term.line[i]);
+ term.line[i] = NULL;
+ }
+ }
term.line = xrealloc(term.line, row * sizeof(Line));
@@ -1640,19 +1854,13 @@ tresize(int col, int row)
for (i = term.row; i < row; i++)
term.line[i] = NULL;
- /* resize each row to new width, zero-pad if needed */
+ // resize each row to new width, zero-pad if needed
for (i = 0; i < row; i++)
term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
- /* update terminal size */
+ // update terminal size
term.col = col;
term.row = row;
-
- /* Clear screen */
- if (mincol < col && 0 < minrow)
- tsetregion(mincol, 0, col - 1, minrow - 1, ' ');
- if (0 < col && minrow < row)
- tsetregion(0, minrow, col - 1, row - 1, ' ');
}
int
diff --git a/se.h b/se.h
@@ -12,6 +12,9 @@
#define UTF_INVALID 0xFFFD
#define UTF_SIZ 4
#define UNDO_BUFFERS_COUNT 32
+#define UPPER_CASE_WORD_MIN_LEN 3
+#define STATUS_BAR_MAX_LEN 4096
+#define SEARCH_TERM_LEN 4096
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) < (b) ? (b) : (a))
@@ -210,16 +213,31 @@ struct file_buffer {
int s1o, s2o; // selection start offset and end offset
};
+const struct color_scheme* buffer_get_color_scheme(struct file_buffer* fb);
struct file_buffer buffer_new(const char* file_path);
void buffer_destroy(struct file_buffer* fb);
void buffer_insert(struct file_buffer* buf, const char* new_content, const int len, const int offset, int do_not_callback);
void buffer_change(struct file_buffer* buf, const char* new_content, const int len, const int offset, int do_not_callback);
int buffer_remove(struct file_buffer* buf, const int offset, int len, int do_not_calculate_charsize, int do_not_callback);
void buffer_write_to_filepath(const struct file_buffer* buffer);
-int buffer_seek_char(const struct file_buffer* buf, int offset, char byte);
-int buffer_seek_char_backwards(const struct file_buffer* buf, int offset, char byte);
-int buffer_seek_string(const struct file_buffer* buf, int offset, const char* string);
-int buffer_seek_string_backwards(const struct file_buffer* buf, int offset, const char* string);
+void buffer_undo(struct file_buffer* buf);
+void buffer_redo(struct file_buffer* buf);
+
+int buffer_is_on_a_word(const struct file_buffer* fb, int offset, const char* word_seperators);
+int buffer_is_start_of_a_word(const struct file_buffer* fb, int offset, const char* word_seperators);
+int buffer_is_on_word(const struct file_buffer* fb, int offset, const char* word_seperators, const char* word);
+int buffer_offset_starts_with(const struct file_buffer* fb, int offset, const char* start);
+int buffer_seek_char(const struct file_buffer* buf, int offset, char byte);
+int buffer_seek_char_backwards(const struct file_buffer* buf, int offset, char byte);
+int buffer_seek_string(const struct file_buffer* buf, int offset, const char* string);
+int buffer_seek_string_backwards(const struct file_buffer* buf, int offset, const char* string);
+int buffer_seek_word(const struct file_buffer* fb, int offset, const char* word_seperators);
+int buffer_seek_word_end(const struct file_buffer* fb, int offset, const char* word_seperators);
+int buffer_seek_word_backwards(const struct file_buffer* fb, int offset, const char* word_seperators);
+int buffer_seek_whitespace(const struct file_buffer* fb, int offset);
+int buffer_seek_whitespace_backwrads(const struct file_buffer* fb, int offset);
+int buffer_seek_not_whitespace(const struct file_buffer* fb, int offset);
+int buffer_seek_not_whitespace_backwrads(const struct file_buffer* fb, int offset);
///////////////////////////////////
// returns a null terminated string containing the selection
@@ -238,22 +256,24 @@ void die(const char *, ...);
int is_file_type(const char* file_path, const char* file_type);
char* file_path_get_path(const char* path);
int path_is_folder(const char* path);
-int file_browser_next_item(DIR* dir, const char* path, const char* search, char* full_path, char* filename);
+int file_browser_next_item(DIR* dir, const char* path, const char* search, char* full_path, char* filename, int* offset);
+int write_string(const char* string, int y, int minx, int maxx);
+int writef_to_status_bar(const char* fmt, ...);
+void draw_status_bar();
// Internal representation of the screen
typedef struct {
- int row; /* nb row */
- int col; /* nb col */
- Line *line; /* screen */
- int ocx, ocy; // old cursor
- Rune lastc; /* last printed char outside of sequence, 0 if control */
+ int row; // row count
+ int col; // column count
+ Line *line; // array
} Term;
-int tattrset(int);
void tnew(int, int);
void tresize(int, int);
void tsetregion(int x1, int y1, int x2, int y2, Rune u);
int tsetchar(Rune u, int x, int y);
+Glyph* tsetattr(int x, int y);
+Rune tgetrune(int x, int y);
size_t utf8encode(Rune, char *);
void *xmalloc(size_t);
@@ -267,6 +287,8 @@ enum buffer_content_reason {
BUFFER_CONTENT_INIT,
};
+void buffer_add_to_undo(struct file_buffer* buffer, int offset, enum buffer_content_reason reason);
+
enum buffer_flags {
BUFFER_SELECTION_ON = 1 << 0,
BUFFER_BLOCK_SELECT = 1 << 1,
diff --git a/x.c b/x.c
@@ -15,6 +15,7 @@
#include <locale.h>
#include <stdio.h>
#include <time.h>
+#include <stdarg.h>
#include <unistd.h>
#include <dirent.h>
@@ -128,14 +129,17 @@ static void (*handler[LASTEvent])(XEvent *) = {
//
extern Term term;
+
static struct file_buffer* file_buffers;
static int available_buffer_slots = 0;
-struct window_split_node root_node = {.mode = WINDOW_SINGULAR};
-// cursor is set when calling buffer_write_to_screen and the buffer is focused_window
+struct window_split_node root_node = {.mode = WINDOW_SINGULAR};
struct window_split_node* focused_node = &root_node;
struct window_buffer* focused_window = &root_node.window;
+char searc_term[SEARCH_TERM_LEN];
+int search_mode_on;
+
Atom xtarget;
char* copy_buffer;
int copy_len;
@@ -193,10 +197,14 @@ new_file_buffer_entry(const char* file_path)
char* res = realpath(file_path, full_path);
if (available_buffer_slots) {
if (res) {
- for(int n = 0; n < available_buffer_slots; n++)
- if (file_buffers[n].contents)
- if (strcmp(file_buffers[n].file_path, full_path) == 0)
+ for(int n = 0; n < available_buffer_slots; n++) {
+ if (file_buffers[n].contents) {
+ if (strcmp(file_buffers[n].file_path, full_path) == 0) {
+ writef_to_status_bar("buffer exits");
return n;
+ }
+ }
+ }
} else {
strcpy(full_path, file_path);
}
@@ -215,7 +223,7 @@ new_file_buffer_entry(const char* file_path)
return available_buffer_slots-1;
}
-void
+int
destroy_file_buffer_entry(struct window_split_node* node, struct window_split_node* root)
{
// do not allow deletion of the lst file buffer
@@ -223,13 +231,16 @@ destroy_file_buffer_entry(struct window_split_node* node, struct window_split_no
for(; n < available_buffer_slots; n++)
if (file_buffers[n].contents && n != node->window.buffer_index)
break;
- if (n >= available_buffer_slots)
- return;
+ if (n >= available_buffer_slots) {
+ writef_to_status_bar("can't delete last buffer");
+ return 0;
+ }
if (window_other_nodes_contain_file_buffer(node, root)) {
node->window.buffer_index++;
node->window = window_buffer_new(node->window.buffer_index);
- return;
+ writef_to_status_bar("swapped buffer");
+ return 0;
}
buffer_destroy(get_file_buffer(&node->window));
@@ -237,6 +248,8 @@ destroy_file_buffer_entry(struct window_split_node* node, struct window_split_no
node->window = window_buffer_new(node->window.cursor_col);
else
node->window = window_buffer_new(node->window.buffer_index);
+
+ return 1;
}
int
@@ -353,6 +366,31 @@ selnotify(XEvent *e)
}
void
+set_clipboard_copy(char* buffer, int len)
+{
+ if (!buffer)
+ return;
+ if (copy_buffer)
+ free(copy_buffer);
+ copy_buffer = buffer;
+ copy_len = len;
+
+ Atom clipboard;
+ clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
+ XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime);
+}
+
+void
+insert_clipboard_at_cursor()
+{
+ Atom clipboard;
+
+ clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
+ XConvertSelection(xw.dpy, clipboard, xtarget, clipboard,
+ xw.win, CurrentTime);
+}
+
+void
selrequest(XEvent *e)
{
XSelectionRequestEvent *xsre = (XSelectionRequestEvent *) e;
@@ -1178,6 +1216,21 @@ xdrawcursor(int cx, int cy, int focused)
}
void
+draw_horisontal_line(int y, int x1, int x2)
+{
+ if (y < 0 || y > term.row ||
+ x2 < x1 || x2 > term.col ||
+ x1 < 0 || x1 > x2-1)
+ return;
+
+ Color drawcol = dc.col[default_attributes.fg];
+ XftDrawRect(xw.draw, &drawcol,
+ border_px + x1 * win.cw,
+ border_px + (y + 1) * win.ch - cursor_thickness,
+ win.cw * (x2-x1+1), 1);
+}
+
+void
xseticontitle(char *p)
{
XTextProperty prop;
@@ -1206,8 +1259,12 @@ xsettitle(char *p)
}
void
-xdrawline(Line line, int x1, int y1, int x2)
+xdrawline(int x1, int y1, int x2)
{
+ LIMIT(y1, 0, term.row);
+ LIMIT(x2, 0, term.col);
+ LIMIT(x1, 0, x2);
+ Line line = term.line[y1];
int i, x, ox, numspecs;
Glyph base, new;
XftGlyphFontSpec *specs = xw.specbuf;
@@ -1336,7 +1393,6 @@ file_browser_actions(KeySym keysym, int modkey)
case XK_BackSpace:
if (offset <= 0) {
char* dest = strrchr(fb->file_path, '/');
- printf("%ld\n", dest - fb->file_path);
if (dest && dest != fb->file_path) *dest = 0;
return 1;
}
@@ -1356,7 +1412,8 @@ file_browser_actions(KeySym keysym, int modkey)
if (fb->len > 0) fb->len--;
DIR *dir = opendir(path);
- for (int y = 0; file_browser_next_item(dir, path, fb->contents, full_path, filename); y++) {
+ int tmp;
+ for (int y = 0; file_browser_next_item(dir, path, fb->contents, full_path, filename, &tmp); y++) {
if (y == focused_window->y_scroll) {
if (path_is_folder(full_path)) {
strcat(full_path, "/");
@@ -1402,7 +1459,8 @@ open_file:
focused_window->y_scroll = 0;
return 1;
case XK_Escape:
- destroy_file_buffer_entry(focused_node, &root_node);
+ if (destroy_file_buffer_entry(focused_node, &root_node))
+ writef_to_status_bar("file browser clsoed");
return 1;
case XK_Page_Down:
@@ -1529,6 +1587,7 @@ resize(XEvent *e)
return;
cresize(e->xconfigure.width, e->xconfigure.height);
+ writef_to_status_bar("window resize: %d:%d", term.col, term.row);
}
void
@@ -1574,7 +1633,9 @@ run(void)
}
tsetregion(0, 0, term.col-1, term.row-1, ' ');
- window_draw_tree_to_screen(&root_node, 0, 0, term.col-1, term.row-1);
+ if (term.row-2 >= 0)
+ window_draw_tree_to_screen(&root_node, 0, 0, term.col-1, term.row-1);
+ draw_status_bar();
if (draw_callback)
draw_callback();
@@ -1583,7 +1644,7 @@ run(void)
XFlush(xw.dpy);
}
}
-
+#include <sys/time.h>
int
main(int argc, char *argv[])
{
@@ -1591,6 +1652,7 @@ main(int argc, char *argv[])
xw.isfixed = False;
xsetcursor(cursor_shape);
+
setlocale(LC_CTYPE, "");
XSetLocaleModifiers("");
int cols = MAX(default_cols, 1);
@@ -1628,6 +1690,14 @@ main(int argc, char *argv[])
}
}
+ static const char* const welcome[] = {"Welcome to se!", "Good day", "Happy Coding", "se: the Simple Editor",
+ "Time to get some progress done!", "When someone extends se, will it be called sex?", "Ready for combat",
+ "Initialising...Done", "Fun fact: vscode has over two times as many lines describing dependencies than se has in total",
+ "You look based"};
+
+ srand(time(NULL));
+ writef_to_status_bar(welcome[rand() % LEN(welcome)]);
+
run();
return 0;
diff --git a/x.h b/x.h
@@ -104,7 +104,7 @@ typedef struct {
void xclipcopy(void);
void xdrawcursor(int, int, int focused);
-void xdrawline(Line, int, int, int);
+void xdrawline(int, int, int);
void xfinishdraw(void);
void xloadcols(void);
void xloadfonts(const char *, double);
@@ -121,7 +121,11 @@ int match(uint, uint);
struct file_buffer* get_file_buffer(struct window_buffer* buf);
int new_file_buffer_entry(const char* file_path);
-void destroy_file_buffer_entry(struct window_split_node* node, struct window_split_node* root);
+int destroy_file_buffer_entry(struct window_split_node* node, struct window_split_node* root);
int delete_selection(struct file_buffer* buf);
+void draw_horisontal_line(int y, int x1, int x2);
+// buffer MUST be malloced and NOT be freed after it is passed
+void set_clipboard_copy(char* buffer, int len);
+void insert_clipboard_at_cursor();
#endif // _X_H