commit 9d120485062d6cf0ce9075543f46000b984f7f23
parent 0f1494bc0a8b07c352df0b20586b91f93efa2b71
Author: Samdal <samdal@protonmail.com>
Date: Wed, 14 Sep 2022 23:06:48 +0200
vim keybinds, so much more it's insane
Diffstat:
37 files changed, 5028 insertions(+), 3539 deletions(-)
diff --git a/.seproj b/.seproj
@@ -3,4 +3,3 @@ se.h
x.c
x.h
config.c
-config.mk
diff --git a/Makefile b/Makefile
@@ -4,7 +4,7 @@
include config.mk
-SRC = se.c x.c config.c
+SRC = se.c x.c config.c buffer.c seek.c utf8.c
OBJ = $(SRC:.c=.o)
all: options se
@@ -54,4 +54,7 @@ uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/se
# rm -f $(DESTDIR)$(MANPREFIX)/man1/st.1
-.PHONY: all options clean dist install uninstall
+run: all
+ ./se
+
+.PHONY: all options clean dist install uninstall run
diff --git a/buffer.c b/buffer.c
@@ -0,0 +1,998 @@
+#include "buffer.h"
+
+#include "config.h"
+#include "se.h"
+#include "extension.h"
+
+#include <unistd.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <time.h>
+
+// TODO: mark buffers as dirty and only redraw windows that have been changed
+
+////////////////////////////////////////////////
+// Globals
+//
+
+static char root_node_search[SEARCH_TERM_MAX_LEN];
+struct window_split_node root_node = {.mode = WINDOW_SINGULAR, .search = root_node_search};
+struct window_split_node* focused_node = &root_node;
+struct window_buffer* focused_window = &root_node.wb;
+
+static struct file_buffer* file_buffers;
+static int available_buffer_slots = 0;
+
+/////////////////////////////////////////////////
+// Function implementations
+//
+
+////////////////////////////////////////////////
+// File buffer
+//
+
+static void
+recursive_mkdir(char *path) {
+ if (!path || !strlen(path))
+ return;
+ char *sep = strrchr(path, '/');
+ if(sep) {
+ *sep = '\0';
+ recursive_mkdir(path);
+ *sep = '/';
+ }
+ if(mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) && errno != EEXIST)
+ fprintf(stderr, "error while trying to create '%s'\n%s\n", path, strerror(errno));
+}
+
+
+// TODO: file open callback, implement as plugin
+static int open_seproj(struct file_buffer fb);
+int
+open_seproj(struct file_buffer fb)
+{
+ int first = -1;
+
+ char* path = file_path_get_path(fb.file_path);
+ chdir(path);
+ int offset = -1;
+
+ while((offset = fb_seek_char(&fb, offset+1, ' ')) >= 0)
+ fb_change(&fb, "\n", 1, offset, 0);
+
+ offset = -1;
+ while((offset = fb_seek_char(&fb, offset+1, '\n')) >= 0) {
+ char* line = fb_get_line_at_offset(&fb, offset);
+ if (strlen(line) && !is_file_type(line, ".seproj")) {
+ if (first < 0)
+ first = fb_new_entry(line);
+ else
+ fb_new_entry(line);
+ }
+ free(line);
+ }
+
+ if (first < 0)
+ first = fb_new_entry(NULL);
+ writef_to_status_bar("opened project %s", path);
+ free(path);
+
+ fb_destroy(&fb);
+ return first;
+}
+
+void
+fb_write_to_filepath(struct file_buffer* fb)
+{
+ if (!fb->file_path)
+ return;
+ soft_assert(fb->contents, return;);
+ FILE* file = fopen(fb->file_path, "w");
+ soft_assert(file, return;);
+
+ if (fb->mode & FB_UTF8_SIGNED)
+ fwrite("\xEF\xBB\xBF", 1, 3, file);
+ fwrite(fb->contents, sizeof(char), fb->len, file);
+ writef_to_status_bar("saved buffer to %s", fb->file_path);
+
+ fclose(file);
+ call_extension(fb_written_to_file, fb);
+}
+
+
+int
+destroy_fb_entry(struct window_split_node* node, struct window_split_node* root)
+{
+ // do not allow deletion of the lst file buffer
+ int n = 0;
+ for(; n < available_buffer_slots; n++)
+ if (file_buffers[n].contents && n != node->wb.fb_index)
+ break;
+ if (n >= available_buffer_slots) {
+ writef_to_status_bar("can't delete last buffer");
+ return 0;
+ }
+
+ if (window_other_nodes_contain_fb(node, root)) {
+ node->wb.fb_index++;
+ node->wb = wb_new(node->wb.fb_index);
+ writef_to_status_bar("swapped buffer");
+ return 0;
+ }
+ fb_destroy(get_fb(&node->wb));
+
+ node->wb = wb_new(node->wb.fb_index);
+
+ return 1;
+}
+
+
+
+struct file_buffer*
+get_fb(struct window_buffer* wb)
+{
+ soft_assert(wb, wb = focused_window;);
+ soft_assert(file_buffers, fb_new_entry(NULL););
+
+ if (wb->fb_index < 0)
+ wb->fb_index = available_buffer_slots-1;
+ else if (wb->fb_index >= available_buffer_slots)
+ wb->fb_index = 0;
+
+ if (!file_buffers[wb->fb_index].contents) {
+ for(int n = wb->fb_index; n < available_buffer_slots; n++) {
+ if (file_buffers[n].contents) {
+ wb->fb_index = n;
+ return &file_buffers[n];
+ }
+ }
+ for(int n = 0; n < available_buffer_slots; n++) {
+ if (file_buffers[n].contents) {
+ wb->fb_index = n;
+ return &file_buffers[n];
+ }
+ }
+ } else {
+ soft_assert(file_buffers[wb->fb_index].contents, );
+ return &file_buffers[wb->fb_index];
+ }
+
+ wb->fb_index = fb_new_entry(NULL);
+ writef_to_status_bar("all buffers were somehow deleted, creating new one");
+ status_bar_bg = warning_color;
+ return get_fb(wb);
+}
+
+int
+fb_delete_selection(struct file_buffer* fb)
+{
+ if (fb->mode & FB_SELECTION_ON) {
+ fb_remove_selection(fb);
+ wb_move_cursor_to_selection_start(focused_window);
+ fb->mode &= ~(FB_SELECTION_ON);
+ return 1;
+ }
+ return 0;
+}
+
+struct file_buffer
+fb_new(const char* file_path)
+{
+ struct file_buffer fb = {0};
+ fb.file_path = xmalloc(PATH_MAX);
+
+ char* res = realpath(file_path, fb.file_path);
+ if (!res) {
+ char* path = file_path_get_path(file_path);
+ recursive_mkdir(path);
+ free(path);
+
+ FILE *new_file = fopen(file_path, "wb");
+ fclose(new_file);
+ realpath(file_path, fb.file_path);
+ remove(file_path);
+
+ writef_to_status_bar("opened new file %s", fb.file_path);
+ } else if (path_is_folder(fb.file_path)) {
+ int len = strlen(fb.file_path);
+ if (fb.file_path[len-1] != '/' && len < PATH_MAX-1) {
+ fb.file_path[len] = '/';
+ fb.file_path[len+1] = '\0';
+ }
+ } else {
+ FILE *file = fopen(fb.file_path, "rb");
+ if (file) {
+ fseek(file, 0L, SEEK_END);
+ long readsize = ftell(file);
+ rewind(file);
+
+ if (readsize > (long)1.048576e+7) {
+ fclose(file);
+ die("you are opening a huge file(>10MiB), not allowed");
+ return fb;
+ // TODO: don't crash
+ }
+
+ fb.len = readsize;
+ fb.capacity = readsize + 100;
+
+ fb.contents = xmalloc(fb.capacity);
+ fb.contents[0] = 0;
+
+ char bom[4] = {0};
+ fread(bom, 1, 3, file);
+ if (strcmp(bom, "\xEF\xBB\xBF"))
+ rewind(file);
+ else
+ fb.mode |= FB_UTF8_SIGNED;
+ fread(fb.contents, 1, readsize, file);
+ fclose(file);
+
+ fb.syntax_index = -1;
+ }
+ }
+
+ if (!fb.capacity)
+ fb.capacity = 100;
+ if (!fb.contents) {
+ fb.contents = xmalloc(fb.capacity);
+ memset(fb.contents, 0, fb.capacity);
+ }
+ fb.ub = xmalloc(sizeof(struct undo_buffer) * UNDO_BUFFERS_COUNT);
+ fb.search_term = xmalloc(SEARCH_TERM_MAX_LEN);
+ fb.non_blocking_search_term = xmalloc(SEARCH_TERM_MAX_LEN);
+ memset(fb.ub, 0, sizeof(struct undo_buffer) * UNDO_BUFFERS_COUNT);
+ memset(fb.search_term, 0, SEARCH_TERM_MAX_LEN);
+ memset(fb.non_blocking_search_term, 0, SEARCH_TERM_MAX_LEN);
+ fb.indent_len = default_indent_len;
+
+ // change line endings
+ int offset = 0;
+ while((offset = fb_seek_string(&fb, offset, "\r\n")) >= 0)
+ fb_remove(&fb, offset, 1, 1, 1);
+ offset = 0;
+ while((offset = fb_seek_char(&fb, offset, '\r')) >= 0)
+ fb_change(&fb, "\n", 1, offset, 1);
+
+ call_extension(fb_new_file_opened, &fb);
+
+ call_extension(fb_contents_updated, &fb, 0, FB_CONTENT_INIT);
+
+ if (res)
+ writef_to_status_bar("new fb %s", fb.file_path);
+ return fb;
+}
+
+int
+fb_new_entry(const char* file_path)
+{
+ static char full_path[PATH_MAX];
+ if (!file_path)
+ file_path = "./";
+ soft_assert(strlen(file_path) < PATH_MAX, 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) {
+ writef_to_status_bar("buffer exits");
+ return n;
+ }
+ }
+ }
+ } else {
+ strcpy(full_path, file_path);
+ }
+
+ for(int n = 0; n < available_buffer_slots; n++) {
+ if (!file_buffers[n].contents) {
+ if (is_file_type(full_path, ".seproj"))
+ return open_seproj(fb_new(full_path));
+ file_buffers[n] = fb_new(full_path);
+ return n;
+ }
+ }
+ }
+
+ if (is_file_type(full_path, ".seproj"))
+ return open_seproj(fb_new(full_path));
+
+ available_buffer_slots++;
+ file_buffers = xrealloc(file_buffers, sizeof(struct file_buffer) * available_buffer_slots);
+ file_buffers[available_buffer_slots-1] = fb_new(full_path);
+
+ return available_buffer_slots-1;
+}
+
+void
+fb_destroy(struct file_buffer* fb)
+{
+ free(fb->ub);
+ free(fb->contents);
+ free(fb->file_path);
+ free(fb->search_term);
+ free(fb->non_blocking_search_term);
+ *fb = (struct file_buffer){0};
+}
+
+void
+fb_insert(struct file_buffer* fb, const char* new_content, const int len, const int offset, int do_not_callback)
+{
+ soft_assert(fb, return;);
+ soft_assert(fb->contents, fb->capacity = 0;);
+ soft_assert(offset <= fb->len && offset >= 0,
+ fprintf(stderr, "writing past fb%s\n", fb->file_path);
+ return;
+ );
+
+ if (fb->len + len >= fb->capacity) {
+ fb->capacity = fb->len + len + 256;
+ fb->contents = xrealloc(fb->contents, fb->capacity);
+ }
+ if (offset < fb->len)
+ memmove(fb->contents+offset+len, fb->contents+offset, fb->len-offset);
+ fb->len += len;
+
+ memcpy(fb->contents+offset, new_content, len);
+ if (!do_not_callback)
+ call_extension(fb_contents_updated, fb, offset, FB_CONTENT_NORMAL_EDIT);
+}
+
+void
+fb_change(struct file_buffer* fb, const char* new_content, const int len, const int offset, int do_not_callback)
+{
+ soft_assert(offset <= fb->len && offset >= 0, return;);
+
+ if (offset + len > fb->len) {
+ fb->len = offset + len;
+ if (fb->len >= fb->capacity) {
+ fb->capacity = fb->len + len + 256;
+ fb->contents = xrealloc(fb->contents, fb->capacity);
+ }
+ }
+
+ memcpy(fb->contents+offset, new_content, len);
+ if (!do_not_callback)
+ call_extension(fb_contents_updated, fb, offset, FB_CONTENT_NORMAL_EDIT);
+}
+
+int
+fb_remove(struct file_buffer* fb, int offset, int len, int do_not_calculate_charsize, int do_not_callback)
+{
+ LIMIT(offset, 0, fb->len-1);
+ if (len == 0) return 0;
+ soft_assert(fb->contents, return 0;);
+ soft_assert(offset + len <= fb->len, return 0;);
+
+ int removed_len = 0;
+ if (do_not_calculate_charsize) {
+ removed_len = len;
+ } else {
+ while (len--) {
+ int charsize = utf8_decode_buffer(fb->contents + offset, fb->len - offset, NULL);
+ if (fb->len - charsize < 0)
+ return 0;
+ removed_len += charsize;
+ }
+ }
+ fb->len -= removed_len;
+ memmove(fb->contents+offset, fb->contents+offset+removed_len, fb->len-offset);
+ if (!do_not_callback)
+ call_extension(fb_contents_updated, fb, offset, FB_CONTENT_NORMAL_EDIT);
+ return removed_len;
+}
+
+void
+wb_copy_ub_to_current(struct window_buffer* wb)
+{
+ struct file_buffer* fb = get_fb(wb);
+ struct undo_buffer* cub = &fb->ub[fb->current_undo_buffer];
+
+ fb->contents = xrealloc(fb->contents, cub->capacity);
+ memcpy(fb->contents, cub->contents, cub->capacity);
+ fb->len = cub->len;
+ fb->capacity = cub->capacity;
+
+ wb_move_to_offset(wb, cub->cursor_offset, CURSOR_SNAPPED);
+ //TODO: remove y_scroll from undo buffer
+ wb->y_scroll = cub->y_scroll;
+}
+
+
+void
+fb_undo(struct file_buffer* fb)
+{
+ if (fb->current_undo_buffer == 0) {
+ writef_to_status_bar("end of undo buffer");
+ return;
+ }
+ fb->current_undo_buffer--;
+ fb->available_redo_buffers++;
+
+ wb_copy_ub_to_current(focused_window);
+ writef_to_status_bar("undo");
+}
+
+void
+fb_redo(struct file_buffer* fb)
+{
+ if (fb->available_redo_buffers == 0) {
+ writef_to_status_bar("end of redo buffer");
+ return;
+ }
+ fb->available_redo_buffers--;
+ fb->current_undo_buffer++;
+
+ wb_copy_ub_to_current(focused_window);
+ writef_to_status_bar("redo");
+}
+
+void
+fb_add_to_undo(struct file_buffer* fb, int offset, enum buffer_content_reason reason)
+{
+ static time_t last_normal_edit;
+ static int edits;
+
+ if (reason == FB_CONTENT_CURSOR_MOVE) {
+ struct undo_buffer* cub = &fb->ub[fb->current_undo_buffer];
+ cub->cursor_offset = offset;
+ if (focused_window)
+ cub->y_scroll = focused_window->y_scroll;
+ else
+ cub->y_scroll = 0;
+ return;
+ }
+
+ if (reason == FB_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 == FB_CONTENT_INIT) {
+ goto copy_undo_buffer;
+ }
+
+ fb->available_redo_buffers = 0;
+ if (fb->current_undo_buffer == UNDO_BUFFERS_COUNT-1) {
+ char* begin_buffer = fb->ub[0].contents;
+ memmove(fb->ub, &(fb->ub[1]), (UNDO_BUFFERS_COUNT-1) * sizeof(struct undo_buffer));
+ fb->ub[fb->current_undo_buffer].contents = begin_buffer;
+ } else {
+ fb->current_undo_buffer++;
+ }
+
+copy_undo_buffer: ;
+ struct undo_buffer* cub = fb->ub + fb->current_undo_buffer;
+
+ cub->contents = xrealloc(cub->contents, fb->capacity);
+ memcpy(cub->contents, fb->contents, fb->capacity);
+ cub->len = fb->len;
+ cub->capacity = fb->capacity;
+ cub->cursor_offset = offset;
+ if (focused_window)
+ cub->y_scroll = focused_window->y_scroll;
+ else
+ cub->y_scroll = 0;
+}
+
+
+char*
+fb_get_string_between_offsets(struct file_buffer* fb, int start, int end)
+{
+ int len = end - start;
+
+ char* string = xmalloc(len + 1);
+ memcpy(string, fb->contents+start, len);
+ string[len] = 0;
+ return string;
+}
+
+char*
+fb_get_selection(struct file_buffer* fb, int* selection_len)
+{
+ if (!(fb->mode & FB_SELECTION_ON))
+ return NULL;
+
+ int start, end;
+ if (fb_is_selection_start_top_left(fb)) {
+ start = fb->s1o;
+ end = fb->s2o+1;
+ } else {
+ start = fb->s2o;
+ end = fb->s1o+1;
+ }
+ if (selection_len)
+ *selection_len = end - start;
+ return fb_get_string_between_offsets(fb, start, end);
+}
+
+
+int
+fb_is_selection_start_top_left(const struct file_buffer* fb)
+{
+ return (fb->s1o <= fb->s2o) ? 1 : 0;
+}
+
+void
+fb_remove_selection(struct file_buffer* buffer)
+{
+ if (!(buffer->mode & FB_SELECTION_ON))
+ return;
+
+ int start, end, len;
+ if (fb_is_selection_start_top_left(buffer)) {
+ start = buffer->s1o;
+ end = buffer->s2o+1;
+ } else {
+ start = buffer->s2o;
+ end = buffer->s1o+1;
+ }
+ len = end - start;
+ fb_remove(buffer, start, len, 1, 1);
+ call_extension(fb_contents_updated, buffer, start, FB_CONTENT_BIG_CHANGE);
+}
+
+char*
+fb_get_line_at_offset(const struct file_buffer* fb, int offset)
+{
+ int start = fb_seek_char_backwards(fb, offset, '\n');
+ if (start < 0) start = 0;
+ int end = fb_seek_char(fb, offset, '\n');
+ if (end < 0) end = fb->len-1;
+
+ int len = end - start;
+
+ char* res = xmalloc(len + 1);
+ if (len > 0)
+ memcpy(res, fb->contents+start, len);
+ res[len] = 0;
+ return res;
+}
+
+void
+fb_offset_to_xy(struct file_buffer* fb, int offset, int maxx, int y_scroll, int* cx, int* cy, int* xscroll)
+{
+ *cx = *cy = *xscroll = 0;
+ soft_assert(fb, return;);
+
+ if (fb->len <= 0)
+ return;
+ LIMIT(offset, 0, fb->len);
+
+ char* repl = fb->contents;
+ char* last = repl + offset;
+
+ char* new_repl;
+ if (wrap_buffer && maxx > 0) {
+ int yscroll = 0;
+ while ((new_repl = memchr(repl, '\n', last - repl))) {
+ if (++yscroll >= y_scroll)
+ break;
+ repl = new_repl+1;
+ }
+ *cy = yscroll - y_scroll;
+ } else {
+ while ((new_repl = memchr(repl, '\n', last - repl))) {
+ repl = new_repl+1;
+ *cy += 1;
+ }
+ *cy -= y_scroll;
+ }
+
+ while (repl < last) {
+ if (wrap_buffer && maxx > 0 && (*repl == '\n' || *cx >= maxx)) {
+ *cy += 1;
+ *cx = 0;
+ repl++;
+ continue;
+ }
+ if (*repl == '\t') {
+ repl++;
+ if (*cx <= 0) *cx += 1;
+ while (*cx % tabspaces != 0) *cx += 1;
+ *cx += 1;
+ continue;
+ }
+ rune_t u;
+ repl += utf8_decode_buffer(repl, last - repl, &u);
+ *cx += wcwidth(u);
+ }
+
+ // TODO: make customizable
+ // -1 = wrap, >= 0 is padding or something like that
+ const int padding = 3;
+
+ if ((*cx - maxx) + padding > 0)
+ *xscroll = (*cx - maxx) + padding;
+}
+
+////////////////////////////////////////////////
+// Window buffer
+//
+
+struct window_buffer
+wb_new(int fb_index)
+{
+ struct window_buffer wb = {0};
+ wb.fb_index = fb_index;
+ if (path_is_folder(get_fb(&wb)->file_path)) {
+ wb.mode = WB_FILE_BROWSER;
+ writef_to_status_bar("opened file browser %s", get_fb(&wb)->file_path);
+ }
+
+ return wb;
+}
+
+void
+wb_move_on_line(struct window_buffer* wb, int amount, enum cursor_reason callback_reason)
+{
+ const struct file_buffer* fb = get_fb(wb);
+ if (fb->len <= 0)
+ return;
+
+ if (amount < 0) {
+ while (wb->cursor_offset > 0 && fb->contents[wb->cursor_offset - 1] != '\n' && amount < 0) {
+ wb->cursor_offset--;
+ if ((fb->contents[wb->cursor_offset] & 0xC0) == 0x80) // if byte starts with 0b10
+ continue; // byte is UTF-8 extender
+ amount++;
+ }
+ LIMIT(wb->cursor_offset, 0, fb->len);
+ } else if (amount > 0) {
+ for (int charsize = 0;
+ wb->cursor_offset < fb->len && amount > 0 && fb->contents[wb->cursor_offset + charsize] != '\n';
+ wb->cursor_offset += charsize, amount--) {
+ rune_t u;
+ charsize = utf8_decode_buffer(fb->contents + wb->cursor_offset, fb->len - wb->cursor_offset, &u);
+ if (u != '\n' && u != '\t')
+ if (wcwidth(u) <= 0)
+ amount++;
+ if (wb->cursor_offset + charsize > fb->len)
+ break;
+ }
+ }
+
+ if (callback_reason)
+ call_extension(wb_cursor_movement, wb, callback_reason);
+}
+
+void
+wb_move_offset_relative(struct window_buffer* wb, int amount, enum cursor_reason callback_reason)
+{
+ //NOTE: this does not check if the character on this offset is the start of a valid utf8 char
+ const struct file_buffer* fb = get_fb((wb));
+ if (fb->len <= 0)
+ return;
+ wb->cursor_offset += amount;
+ LIMIT(wb->cursor_offset, 0, fb->len);
+
+ if (callback_reason)
+ call_extension(wb_cursor_movement, wb, callback_reason);
+}
+
+void
+wb_move_lines(struct window_buffer* wb, int amount, enum cursor_reason callback_reason)
+{
+ const struct file_buffer* fb = get_fb((wb));
+ if (fb->len <= 0)
+ return;
+ int offset = wb->cursor_offset;
+ if (amount > 0) {
+ while (amount-- && offset >= 0) {
+ int new_offset = fb_seek_char(fb, offset, '\n');
+ if (new_offset < 0) {
+ offset = fb->len;
+ break;
+ }
+ offset = new_offset+1;
+ }
+ } else if (amount < 0) {
+ while (amount++ && offset >= 0)
+ offset = fb_seek_char_backwards(fb, offset, '\n')-1;
+ }
+ wb_move_to_offset(wb, offset, callback_reason);
+}
+
+void
+wb_move_to_offset(struct window_buffer* wb, int offset, enum cursor_reason callback_reason)
+{
+ //NOTE: this does not check if the character on this offset is the start of a valid utf8 char
+ const struct file_buffer* fb = get_fb((wb));
+ if (fb->len <= 0)
+ return;
+ LIMIT(offset, 0, fb->len);
+ wb->cursor_offset = offset;
+
+ if (callback_reason)
+ call_extension(wb_cursor_movement, wb, callback_reason);
+}
+
+void
+wb_move_to_x(struct window_buffer* wb, int x, enum cursor_reason callback_reason)
+{
+ soft_assert(wb, return;);
+ struct file_buffer* fb = get_fb(wb);
+
+ int offset = fb_seek_char_backwards(fb, wb->cursor_offset, '\n');
+ if (offset < 0)
+ offset = 0;
+ wb_move_to_offset(wb, offset, 0);
+
+ int x_counter = 0;
+
+ while (offset < fb->len) {
+ if (fb->contents[offset] == '\t') {
+ offset++;
+ if (x_counter <= 0) x_counter += 1;
+ while (x_counter % tabspaces != 0) x_counter += 1;
+ x_counter += 1;
+ continue;
+ } else if (fb->contents[offset] == '\n') {
+ break;
+ }
+ rune_t u = 0;
+ int charsize = utf8_decode_buffer(fb->contents + offset, fb->len - offset, &u);
+ x_counter += wcwidth(u);
+ if (x_counter <= x) {
+ offset += charsize;
+ if (x_counter == x)
+ break;
+ } else {
+ break;
+ }
+ }
+ wb_move_to_offset(wb, offset, callback_reason);
+}
+
+////////////////////////////////////////////////
+// Window split node
+//
+
+static int
+is_correct_mode(enum window_split_mode mode, enum move_directons move)
+{
+ if (move == MOVE_RIGHT || move == MOVE_LEFT)
+ return (mode == WINDOW_HORISONTAL);
+ if (move == MOVE_UP || move == MOVE_DOWN)
+ return (mode == WINDOW_VERTICAL);
+ return 0;
+}
+
+void
+window_node_split(struct window_split_node* parent, float ratio, enum window_split_mode mode)
+{
+ soft_assert(parent, return;);
+ soft_assert(parent->mode == WINDOW_SINGULAR, return;);
+ soft_assert(mode != WINDOW_SINGULAR, return;);
+
+ if ((parent->maxx - parent->minx < MIN_WINDOW_SPLIT_SIZE_HORISONTAL && mode == WINDOW_HORISONTAL)
+ || (parent->maxy - parent->miny < MIN_WINDOW_SPLIT_SIZE_VERTICAL && mode == WINDOW_VERTICAL))
+ return;
+
+ parent->node1 = xmalloc(sizeof(struct window_split_node));
+ *parent->node1 = *parent;
+ parent->node1->search = xmalloc(SEARCH_TERM_MAX_LEN);
+ parent->node1->parent = parent;
+ parent->node1->node1 = NULL;
+ parent->node1->node2 = NULL;
+
+
+ parent->node2 = xmalloc(sizeof(struct window_split_node));
+ *parent->node2 = *parent;
+ parent->node2->search = xmalloc(SEARCH_TERM_MAX_LEN);
+ parent->node2->parent = parent;
+ parent->node2->node1 = NULL;
+ parent->node2->node2 = NULL;
+
+ if (parent->mode == WINDOW_HORISONTAL) {
+ // NOTE: if the window resizing is changed, change in draw tree function as well
+ int middlex = ((float)(parent->maxx - parent->minx) * parent->ratio) + parent->minx;
+ parent->node1->minx = parent->minx;
+ parent->node1->miny = parent->miny;
+ parent->node1->maxx = middlex;
+ parent->node1->maxy = parent->maxy;
+ parent->node2->minx = middlex+2;
+ parent->node2->miny = parent->miny;
+ parent->node2->maxx = parent->maxx;
+ parent->node2->maxy = parent->maxy;
+ } else if (parent->mode == WINDOW_VERTICAL) {
+ // NOTE: if the window resizing is changed, change in draw tree function as well
+ int middley = ((float)(parent->maxy - parent->miny) * parent->ratio) + parent->miny;
+ parent->node1->minx = parent->minx;
+ parent->node1->miny = parent->miny;
+ parent->node1->maxx = parent->maxx;
+ parent->node1->maxy = middley;
+ parent->node2->minx = parent->miny;
+ parent->node2->miny = middley;
+ parent->node2->maxx = parent->maxx;
+ parent->node2->maxy = parent->maxy;
+ }
+
+ parent->mode = mode;
+ parent->ratio = ratio;
+ parent->wb = (struct window_buffer){0};
+}
+
+struct window_split_node*
+window_node_delete(struct window_split_node* node)
+{
+ 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;
+ free(old->search);
+ free(old);
+
+ struct window_split_node* parent = node->parent;
+ *node = *other;
+ if (other->mode != WINDOW_SINGULAR) {
+ other->node1->parent = node;
+ other->node2->parent = node;
+ }
+ free(other);
+ node->parent = parent;
+
+ return node;
+}
+
+void
+window_node_draw_tree_to_screen(struct window_split_node* root, int minx, int miny, int maxx, int maxy)
+{
+ soft_assert(root, return;);
+
+ if (root->mode == WINDOW_SINGULAR) {
+ LIMIT(maxx, 0, screen.col-1);
+ LIMIT(maxy, 0, screen.row-1);
+ LIMIT(minx, 0, maxx);
+ LIMIT(miny, 0, maxy);
+ root->minx = minx;
+ root->miny = miny;
+ root->maxx = maxx;
+ root->maxy = maxy;
+ if (root->wb.mode != 0) {
+ int wn_custom_window_draw_callback_exists = 0;
+ extension_callback_exists(wn_custom_window_draw, wn_custom_window_draw_callback_exists = 1;);
+ soft_assert(wn_custom_window_draw_callback_exists, return;);
+
+ call_extension(wn_custom_window_draw, root);
+
+ return;
+ } else {
+ window_node_draw_to_screen(root);
+ }
+ } else if (root->mode == WINDOW_HORISONTAL) {
+ // NOTE: if the window resizing is changed, change in split function as well
+ int middlex = ((float)(maxx - minx) * root->ratio) + minx;
+
+ // print seperator
+ screen_set_region(middlex+1, miny, middlex+1, maxy, L'│');
+
+ window_node_draw_tree_to_screen(root->node1, minx, miny, middlex, maxy);
+ window_node_draw_tree_to_screen(root->node2, middlex+2, miny, maxx, maxy);
+
+ for (int y = miny; y < maxy+1; y++)
+ xdrawline(middlex+1, y, middlex+2);
+ } else if (root->mode == WINDOW_VERTICAL) {
+ // NOTE: if the window resizing is changed, change in split function as well
+ int middley = ((float)(maxy - miny) * root->ratio) + miny;
+
+ window_node_draw_tree_to_screen(root->node1, minx, miny, maxx, middley);
+ window_node_draw_tree_to_screen(root->node2, minx, middley, maxx, maxy);
+ }
+}
+
+void
+window_node_move_all_cursors_on_same_fb(struct window_split_node* root, struct window_split_node* excluded, int fb_index, int offset,
+ void(movement)(struct window_buffer*, int, enum cursor_reason),
+ int move, enum cursor_reason reason)
+{
+ if (root->mode == WINDOW_SINGULAR) {
+ if (root->wb.fb_index == fb_index && root->wb.cursor_offset >= offset && root != excluded)
+ movement(&root->wb, move, reason);
+ } else {
+ window_node_move_all_cursors_on_same_fb(root->node1, excluded, fb_index, offset, movement, move, reason);
+ window_node_move_all_cursors_on_same_fb(root->node2, excluded, fb_index, offset, movement, move, reason);
+ }
+}
+
+void
+window_node_move_all_yscrolls(struct window_split_node* root, struct window_split_node* excluded, int fb_index, int offset, int move)
+{
+ if (root->mode == WINDOW_SINGULAR) {
+ if (root->wb.fb_index == fb_index && root->wb.cursor_offset >= offset && root != excluded)
+ root->wb.y_scroll += move;
+ } else {
+ window_node_move_all_yscrolls(root->node1, excluded, fb_index, offset, move);
+ window_node_move_all_yscrolls(root->node2, excluded, fb_index, offset, move);
+ }
+}
+
+int
+window_other_nodes_contain_fb(struct window_split_node* node, struct window_split_node* root)
+{
+ if (root->mode == WINDOW_SINGULAR)
+ return (root->wb.fb_index == node->wb.fb_index && root != node);
+
+ return (window_other_nodes_contain_fb(node, root->node1) ||
+ window_other_nodes_contain_fb(node, root->node2));
+}
+
+// TODO: create a distance type function and use that instead (from the current cursor position)?
+// struct window_split_node* wincdow_closest(root, node ignore(get maxx/minx etc from this and make sure the it's outside the ignored node), enum move_directions move, int cx, int cy)
+struct window_split_node*
+window_switch_to_window(struct window_split_node* node, enum move_directons move)
+{
+ soft_assert(node, return &root_node;);
+ if (!node->parent) return node;
+ soft_assert(node->mode == WINDOW_SINGULAR,
+ while(node->mode != WINDOW_SINGULAR)
+ node = node->node1;
+ return node;
+ );
+ struct window_split_node* old_node = node;
+
+ if (move == MOVE_RIGHT || move == MOVE_DOWN) {
+ // traverse up the tree to the right
+ for (; node->parent; node = node->parent) {
+ if (is_correct_mode(node->parent->mode, move) && node->parent->node1 == node) {
+ // traverse down until a screen is found
+ node = node->parent->node2;
+ while(node->mode != WINDOW_SINGULAR)
+ node = node->node1;
+
+ return node;
+ }
+ }
+ } else if (move == MOVE_LEFT || move == MOVE_UP) {
+ // traverse up the tree to the left
+ for (; node->parent; node = node->parent) {
+ if (is_correct_mode(node->parent->mode, move) && node->parent->node2 == node) {
+ // traverse down until a screen is found
+ node = node->parent->node1;
+ while(node->mode != WINDOW_SINGULAR)
+ node = node->node2;
+
+ return node;
+ }
+ }
+ }
+
+ return old_node;
+}
+
+void
+window_node_resize(struct window_split_node* node, enum move_directons move, float amount)
+{
+ for (; node; node = node->parent) {
+ if (is_correct_mode(node->mode, move)) {
+ float amount = (move == MOVE_RIGHT || move == MOVE_LEFT) ? 0.1f : 0.05f;
+ if (move == MOVE_RIGHT || move == MOVE_DOWN) amount = -amount;
+ node->ratio -= amount;
+ LIMIT(node->ratio, 0.001f, 0.95f);
+ return;
+ }
+ }
+}
+
+void
+window_node_resize_absolute(struct window_split_node* node, enum move_directons move, float amount)
+{
+ for (; node; node = node->parent) {
+ if (is_correct_mode(node->mode, move)) {
+ node->ratio = amount;
+ LIMIT(node->ratio, 0.001f, 0.95f);
+ return;
+ }
+ }
+}
diff --git a/buffer.h b/buffer.h
@@ -0,0 +1,204 @@
+#ifndef BUFFER_H_
+#define BUFFER_H_
+
+/* fb: file_buffer
+** wb: window_buffer
+** wn: window_split_node
+**
+*/
+
+// Arbitrary sizes
+#define SEARCH_TERM_MAX_LEN PATH_MAX
+#define LINE_MAX_LEN 2048
+#define MIN_WINDOW_SPLIT_SIZE_VERTICAL 10
+#define MIN_WINDOW_SPLIT_SIZE_HORISONTAL 20
+
+// external globals
+extern struct window_split_node root_node;
+extern struct window_split_node* focused_node;
+extern struct window_buffer* focused_window;
+
+////////////////////////////////////////////////
+// File buffer
+//
+
+#define UNDO_BUFFERS_COUNT 128
+struct undo_buffer {
+ char* contents; // not null terminated
+ int len, capacity;
+ int cursor_offset;
+ int y_scroll;
+};
+
+enum buffer_flags {
+ FB_SELECTION_ON = 1 << 0,
+ FB_BLOCK_SELECT = 1 << 1,
+ FB_LINE_SELECT = 1 << 2,
+ FB_SELECT_MASK = (FB_SELECTION_ON | FB_BLOCK_SELECT | FB_LINE_SELECT),
+ FB_SELECT_MODE_MASK = (FB_BLOCK_SELECT | FB_LINE_SELECT),
+ FB_READ_ONLY = 1 << 3,
+ FB_UTF8_SIGNED = 1 << 4,
+ FB_SEARCH_BLOCKING = 1 << 5,
+ FB_SEARCH_BLOCKING_IDLE = 1 << 6,
+ FB_SEARCH_BLOCKING_MASK = (FB_SEARCH_BLOCKING | FB_SEARCH_BLOCKING_IDLE),
+ FB_SEARCH_NON_BLOCKING = 1 << 7,
+ FB_SEARCH_BLOCKING_BACKWARDS = 1 << 8,
+ FB_SEARCH_NON_BLOCKING_BACKWARDS = 1 << 9,
+};
+
+struct file_buffer {
+ char* file_path;
+ char* contents; // !! NOT NULL TERMINATED !!
+ int len;
+ int capacity;
+ int mode; // buffer_flags
+ struct undo_buffer* ub;
+ // TODO: int file_buffer_len;
+ int current_undo_buffer;
+ int available_redo_buffers;
+ int s1o, s2o; // selection start offset and end offset
+ char* search_term;
+ char* non_blocking_search_term;
+ int syntax_index;
+ unsigned int indent_len; // amount of spaces, if 0 tab is used
+};
+
+enum buffer_content_reason {
+ FB_CONTENT_DO_NOT_CALLBACK = 0,
+ FB_CONTENT_OPERATION_ENDED,
+ FB_CONTENT_NORMAL_EDIT,
+ FB_CONTENT_BIG_CHANGE,
+ FB_CONTENT_INIT,
+ FB_CONTENT_CURSOR_MOVE,
+};
+
+struct file_buffer* get_fb(struct window_buffer* wb);
+int fb_new_entry(const char* file_path);
+int destroy_fb_entry(struct window_split_node* node, struct window_split_node* root);
+int fb_delete_selection(struct file_buffer* fb);
+
+struct file_buffer fb_new(const char* file_path);
+void fb_write_to_filepath(struct file_buffer* fb);
+void fb_destroy(struct file_buffer* fb);
+
+void fb_insert(struct file_buffer* fb, const char* new_content, const int len, const int offset, int do_not_callback);
+void fb_change(struct file_buffer* fb, const char* new_content, const int len, const int offset, int do_not_callback);
+int fb_remove(struct file_buffer* fb, const int offset, int len, int do_not_calculate_charsize, int do_not_callback);
+
+void fb_undo(struct file_buffer* fb);
+void fb_redo(struct file_buffer* fb);
+void fb_add_to_undo(struct file_buffer* fb, int offset, enum buffer_content_reason reason);
+
+///////////////////////////////////
+// returns a null terminated string containing the selection
+// the returned value must be freed by the reciever
+// for conveniance the length of the string may be taken with the pointer
+// a selection_len of NULL wil be ignored
+char* fb_get_string_between_offsets(struct file_buffer* fb, int start, int end);
+
+char* fb_get_selection(struct file_buffer* fb, int* selection_len);
+int fb_is_selection_start_top_left(const struct file_buffer* fb);
+void fb_remove_selection(struct file_buffer* fb);
+
+///////////////////////////////////
+// returns a null terminated string containing the current line
+// the returned value must be freed by the reciever
+// TODO: make this take any string/char instead of hardcoded \n
+char* fb_get_line_at_offset(const struct file_buffer* fb, int offset);
+
+void fb_offset_to_xy(struct file_buffer* fb, int offset, int maxx, int y_scroll, int* cx, int* cy, int* xscroll);
+
+
+////////////////////////////////////////////////
+// Window buffer
+//
+
+#define WB_NORMAL 0
+#define WB_FILE_BROWSER 1
+#define WB_MODES_DEFAULT_END 1
+
+struct window_buffer {
+ int y_scroll;
+ int cursor_offset;
+ int cursor_col;
+
+ int fb_index; // index into an array storing file buffers
+
+ ///////////////////////////////////
+ // you may implement your own "modes"
+ // it will run a callback where you can render your window
+ // a callback allowing you to override the default input callback
+ // is also provided
+ // TODO:↑
+ // see extensions/window_modes for other modes
+ unsigned int mode; // WB_NORMAL = 0
+};
+
+enum cursor_reason {
+ CURSOR_DO_NOT_CALLBACK = 0,
+ CURSOR_COMMAND_MOVEMENT = 1,
+ CURSOR_UP_DOWN_MOVEMENT,
+ CURSOR_RIGHT_LEFT_MOVEMENT,
+ CURSOR_SNAPPED,
+};
+
+struct window_buffer wb_new(int buffer_index);
+
+////////////////////////////////////////////////
+// Window split node
+//
+
+enum window_split_mode {
+ WINDOW_SINGULAR,
+ WINDOW_HORISONTAL,
+ WINDOW_VERTICAL,
+ WINDOW_FILE_BROWSER,
+};
+
+struct window_split_node {
+ struct window_buffer wb;
+ enum window_split_mode mode;
+ float ratio;
+ struct window_split_node *node1, *node2, *parent;
+ int minx, miny, maxx, maxy; // position informatin from the last frame
+ char* search;
+ int selected;
+};
+
+enum move_directons {
+ MOVE_RIGHT,
+ MOVE_LEFT,
+ MOVE_UP,
+ MOVE_DOWN,
+};
+
+////////////////////////////////////////////////
+// Window buffer
+//
+
+void wb_write_selection(struct window_buffer* wb, int minx, int miny, int maxx, int maxy);
+void wb_move_cursor_to_selection_start(struct window_buffer* wb);
+
+void wb_move_on_line(struct window_buffer* wb, int amount, enum cursor_reason callback_reason);
+void wb_move_lines(struct window_buffer* wb, int amount, enum cursor_reason callback_reason);
+void wb_move_to_offset(struct window_buffer* wb, int offset, enum cursor_reason callback_reason);
+void wb_move_offset_relative(struct window_buffer* wb, int amount, enum cursor_reason callback_reason);
+void wb_move_to_x(struct window_buffer* wb, int x, enum cursor_reason callback_reason);
+
+// window split node
+
+void window_node_split(struct window_split_node* parent, float ratio, enum window_split_mode mode);
+struct window_split_node* window_node_delete(struct window_split_node* node);
+// uses focused_window to draw the cursor
+void window_node_draw_tree_to_screen(struct window_split_node* root, int minx, int miny, int maxx, int maxy);
+void window_node_move_all_cursors_on_same_fb(struct window_split_node* root, struct window_split_node* excluded, int buf_index, int offset, void(movement)(struct window_buffer*, int, enum cursor_reason), int move, enum cursor_reason reason);
+void window_node_move_all_yscrolls(struct window_split_node* root, struct window_split_node* excluded, int buf_index, int offset, int move);
+int window_other_nodes_contain_fb(struct window_split_node* node, struct window_split_node* root);
+
+struct window_split_node* window_switch_to_window(struct window_split_node* node, enum move_directons move);
+// NOTE: if you have two splits both having two splits of the split same type, you can't resize the upper split
+void window_node_resize(struct window_split_node* node, enum move_directons move, float amount);
+void window_node_resize_absolute(struct window_split_node* node, enum move_directons move, float amount);
+
+
+#endif // BUFFER_H_
diff --git a/config.def.c b/config.def.c
@@ -1,6 +1,4 @@
-#include <assert.h>
-
-#include "x.h"
+#include "config.h"
////////////////////////////////////////
// apperance
@@ -48,74 +46,23 @@ unsigned int cursor_thickness = 2;
#include "plugins/syntax/c.h"
-const struct color_scheme color_schemes[] = {
- {".c", c_word_seperators, c_color_scheme, LEN(c_color_scheme)},
- {".h", c_word_seperators, c_color_scheme, LEN(c_color_scheme)},
+const struct syntax_scheme syntax_schemes[] = {
+ {".c", c_word_seperators, c_syntax, LEN(c_syntax)},
+ {".h", c_word_seperators, c_syntax, LEN(c_syntax)},
{0},
};
-///////////////////////////////////////////////////
-// Declarations
-//
-
-typedef union {
- int i;
- uint ui;
- float f;
- int vec2i[2];
- const void *v;
- const char *s;
-} Arg;
-
-static void numlock(const Arg* arg);
-static void window_split(const Arg* arg);
-static void window_resize(const Arg *arg);
-static void window_delete(const Arg *arg);
-static void window_change(const Arg* arg);
-static void zoom(const Arg* arg);
-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 swap_to_next_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);
-static void move_cursor_to_end_of_buffer(const Arg* arg);
-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 search(const Arg* arg);
-static void search_next(const Arg* arg);
-static void search_previous(const Arg* arg);
-static void search_for_buffer(const Arg* arg);
-static void search_keyword_in_buffers(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 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 int keypress_actions(KeySym keysym, int modkey);
-static void string_insert_callback(const char* buf, int buflen);
-
/////////////////////////////////////////
// Shortcuts
//
-typedef struct {
- uint mod;
- KeySym keysym;
- void (*func)(const Arg* arg);
- const Arg arg;
-} Shortcut;
+#include "plugins/shortcuts.h"
+#include "plugins/default_shortcuts.h"
-#define MODKEY Mod1Mask
-#define TERMMOD (ControlMask|ShiftMask)
+#define MODKEY Mod4Mask
+#define CtrlShift (ControlMask|ShiftMask)
-const Shortcut shortcuts[] = {
+shortcuts = {
// mask keysym function argument
{ 0, XK_Right, cursor_move_x_relative, {.i = +1} },
{ 0, XK_Left, cursor_move_x_relative, {.i = -1} },
@@ -125,296 +72,56 @@ const Shortcut shortcuts[] = {
{ ControlMask, XK_Left, window_change, {.i = MOVE_LEFT} },
{ ControlMask, XK_Down, window_change, {.i = MOVE_DOWN} },
{ ControlMask, XK_Up, window_change, {.i = MOVE_UP} },
- { TERMMOD, XK_Right, window_resize, {.i = MOVE_RIGHT} },
- { TERMMOD, XK_Left, window_resize, {.i = MOVE_LEFT} },
- { TERMMOD, XK_Down, window_resize, {.i = MOVE_DOWN} },
- { TERMMOD, XK_Up, window_resize, {.i = MOVE_UP} },
+ { CtrlShift, XK_Right, window_resize, {.i = MOVE_RIGHT} },
+ { CtrlShift, XK_Left, window_resize, {.i = MOVE_LEFT} },
+ { CtrlShift, XK_Down, window_resize, {.i = MOVE_DOWN} },
+ { CtrlShift, XK_Up, window_resize, {.i = MOVE_UP} },
{ ControlMask, XK_Tab, swap_to_next_file_buffer, {0} },
{ ControlMask, XK_m, toggle_selection, {0} },
{ ControlMask, XK_g, move_cursor_to_offset, {0} },
- { TERMMOD, XK_G, move_cursor_to_end_of_buffer, {0} },
+ { CtrlShift, XK_G, move_cursor_to_end_of_buffer, {0} },
{ ControlMask, XK_period, open_file_browser, {0} },
- { TERMMOD, XK_D, buffer_kill, {0} },
+ { CtrlShift, 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_z, undo, {0} },
- { TERMMOD, XK_Z, redo, {0} },
+ { CtrlShift, XK_Z, redo, {0} },
{ ControlMask, XK_s, save_buffer, {0} },
{ ControlMask, XK_f, search, {0} },
- { TERMMOD, XK_F, search_keyword_in_buffers,{0},},
+ { CtrlShift, XK_F, search_keyword_in_buffers,{0},},
{ ControlMask, XK_space, search_for_buffer,{0}, },
{ ControlMask, XK_n, search_next, {0} },
- { TERMMOD, XK_N, search_previous,{0} },
+ { CtrlShift, XK_N, search_previous,{0} },
{ ControlMask, XK_c, clipboard_copy, {0} },
{ ControlMask, XK_v, clipboard_paste,{0} },
- { TERMMOD, XK_Prior, zoom, {.f = +1} },
- { TERMMOD, XK_Next, zoom, {.f = -1} },
- { TERMMOD, XK_Home, zoomreset, {.f = 0} },
- { TERMMOD, XK_Num_Lock, numlock, {.i = 0} },
+ { CtrlShift, XK_Prior, zoom, {.f = +1} },
+ { CtrlShift, XK_Next, zoom, {.f = -1} },
+ { CtrlShift, XK_Home, zoomreset, {.f = 0} },
+ { CtrlShift, XK_Num_Lock, numlock, {.i = 0} },
};
/////////////////////////////////////////////////
// callbacks
//
+static void cursor_callback(struct window_buffer* buf, enum cursor_reason callback_reason);
+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 int keypress_actions(KeySym keysym, int modkey);
+static void string_insert_callback(const char* buf, int buflen);
+#include "plugins/default_status_bar.h"
+
void(*cursor_movement_callback)(struct window_buffer*, enum cursor_reason) = cursor_callback;
-void(*buffer_contents_updated)(struct file_buffer*, int, enum buffer_content_reason) = buffer_content_callback;
+void(*buffer_contents_updated)(struct file_buffer*, int, enum buffer_content_reason) = buffer_add_to_undo;
int(*keypress_callback)(KeySym, int) = keypress_actions;
void(*string_input_callback)(const char*, int) = string_insert_callback;
void(*draw_callback)(void) = NULL;
void(*startup_callback)(void) = NULL;
-void(*buffer_written_to_screen_callback)(struct file_buffer*, int, int, int, int, int, int) = NULL;
-
-
-////////////////////////////////////////////////
-// external globals
-//
-
-extern struct window_split_node root_node;
-extern struct window_buffer* focused_window;
-extern struct window_split_node* focused_node;
-extern TermWindow win;
-extern XWindow xw;
-extern DC dc;
-
-extern Term term;
-
-extern Fontcache *frc;
-extern int frclen;
-extern Atom xtarget;
-extern double defaultfontsize;
-extern double usedfontsize;
-extern char* copy_buffer;
-extern int copy_len;
-
-/////////////////////////////////////////////////
-// function implementations
-//
-
-void
-numlock(const Arg *dummy)
-{
- win.mode ^= MODE_NUMLOCK;
-}
-
-void window_split(const Arg *arg)
-{
- window_node_split(focused_node, 0.5, arg->i);
- if (focused_node->node2) {
- focused_node = focused_node->node2;
- focused_window = &focused_node->window;
- }
-}
-
-void window_resize(const Arg *arg)
-{
- float amount = (arg->i == MOVE_RIGHT || arg->i == MOVE_LEFT) ? 0.1f : 0.05f;
- window_node_resize(focused_node, arg->i, amount);
-}
-
-void window_delete(const Arg *arg)
-{
- struct window_split_node* new_node = window_node_delete(focused_node);
- while (new_node->mode != WINDOW_SINGULAR)
- new_node = new_node->node1;
- focused_node = new_node;
- focused_window = &focused_node->window;
-}
-
-void window_change(const Arg *arg)
-{
- focused_node = window_switch_to_window(focused_node, arg->i);
- focused_window = &focused_node->window;
-}
-
-void
-zoom(const Arg *arg)
-{
- Arg larg;
-
- larg.f = usedfontsize + arg->f;
- zoomabs(&larg);
-}
-
-void
-zoomabs(const Arg *arg)
-{
- xunloadfonts();
- xloadfonts(fontconfig, arg->f);
- cresize(0, 0);
- xhints();
-}
-
-void
-zoomreset(const Arg *arg)
-{
- Arg larg;
-
- if (defaultfontsize > 0) {
- larg.f = defaultfontsize;
- zoomabs(&larg);
- }
-}
-
-void
-cursor_move_x_relative(const Arg* arg)
-{
- if (focused_window->mode != WINDOW_BUFFER_FILE_BROWSER)
- buffer_move_on_line(focused_window, arg->i, CURSOR_RIGHT_LEFT_MOVEMENT);
-}
-
-void
-cursor_move_y_relative(const Arg* arg)
-{
- buffer_move_lines(focused_window, arg->i, 0);
- buffer_move_to_x(focused_window, focused_window->cursor_col, CURSOR_UP_DOWN_MOVEMENT);
-}
-
-void
-swap_to_next_file_buffer(const Arg* arg)
-{
- focused_window->buffer_index++;
-}
-
-void
-save_buffer(const Arg* arg)
-{
- buffer_write_to_filepath(get_file_buffer(focused_window));
-}
-
-void
-toggle_selection(const Arg* arg)
-{
- struct file_buffer* fb = get_file_buffer(focused_window);
- if (fb->mode & BUFFER_SELECTION_ON) {
- fb->mode &= ~(BUFFER_SELECTION_ON);
- } else {
- fb->mode |= BUFFER_SELECTION_ON;
- fb->s1o = fb->s2o = focused_window->cursor_offset;
- }
-}
-
-void
-move_cursor_to_offset(const Arg* arg)
-{
- focused_window->cursor_offset = arg->i;
-}
-
-void
-move_cursor_to_end_of_buffer(const Arg* arg)
-{
- focused_window->cursor_offset = get_file_buffer(focused_window)->len-1;
-}
-
-void
-clipboard_copy(const Arg* arg)
-{
- struct file_buffer* fb = get_file_buffer(focused_window);
- int 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;
-}
-
-void
-clipboard_paste(const Arg* arg)
-{
- insert_clipboard_at_cursor();
-}
-
-void
-undo(const Arg* arg)
-{
- buffer_undo(get_file_buffer(focused_window));
-}
-
-void
-redo(const Arg* arg)
-{
- buffer_redo(get_file_buffer(focused_window));
-}
-
-void
-search(const Arg* arg)
-{
- get_file_buffer(focused_window)->mode &= ~BUFFER_SEARCH_BLOCKING_IDLE;
- get_file_buffer(focused_window)->mode |= BUFFER_SEARCH_BLOCKING;
- writef_to_status_bar("search: %s", get_file_buffer(focused_window)->search_term);
-}
-
-void
-search_next(const Arg* arg)
-{
- int new_offset = buffer_seek_string_wrap(focused_window, focused_window->cursor_offset+1,
- get_file_buffer(focused_window)->search_term);
- if (new_offset < 0) {
- writef_to_status_bar("no results for \"%s\"", get_file_buffer(focused_window)->search_term);
- return;
- } else if (focused_window->cursor_offset > new_offset) {
- writef_to_status_bar("search wrapped");
- }
- focused_window->cursor_offset = new_offset;
-}
-
-void
-search_previous(const Arg* arg)
-{
- int new_offset = buffer_seek_string_wrap_backwards(focused_window, focused_window->cursor_offset-1,
- get_file_buffer(focused_window)->search_term);
- if (new_offset < 0) {
- writef_to_status_bar("no results for \"%s\"", get_file_buffer(focused_window)->search_term);
- return;
- } else if (focused_window->cursor_offset < new_offset) {
- writef_to_status_bar("search wrapped");
- }
- focused_window->cursor_offset = new_offset;
-}
-
-void
-search_for_buffer(const Arg* arg)
-{
- if (focused_window->mode != WINDOW_BUFFER_NORMAL)
- return;
- *focused_node->search = 0;
- focused_node->selected = 0;
- focused_window->mode = WINDOW_BUFFER_SEARCH_BUFFERS;
-}
-
-void
-search_keyword_in_buffers(const Arg* arg)
-{
- if (focused_window->mode != WINDOW_BUFFER_NORMAL)
- return;
- *focused_node->search = 0;
- focused_node->selected = 0;
- focused_window->mode = WINDOW_BUFFER_KEYWORD_ALL_BUFFERS;
-}
-
-
-void
-open_file_browser(const Arg* arg)
-{
- 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
-buffer_kill(const Arg* arg)
-{
- destroy_file_buffer_entry(focused_node, &root_node);
-}
-
-////////////////////////////////////////////////////////7
-// Callbacks
-//
+void(*buffer_written_to_screen_callback)(struct window_buffer* buf, int offset_start, int offset_end, int minx, int miny, int maxx, int maxy) = NULL;
+char*(*new_line_draw)(struct window_buffer* buf, int y, int lines_left, int minx, int maxx, Glyph* attr) = NULL;
+void(*buffer_written_to_file_callback)(struct file_buffer* fb) = NULL;
+int(*write_status_bar)(struct window_buffer* buf, int minx, int maxx, int cx, int cy, char line[LINE_MAX_LEN], Glyph* g) = default_status_bar;
void
keep_cursor_col(struct window_buffer* buf, enum cursor_reason callback_reason)
@@ -439,25 +146,13 @@ cursor_callback(struct window_buffer* buf, enum cursor_reason callback_reason)
keep_cursor_col(buf, callback_reason);
move_selection(buf, callback_reason);
- //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)
-{
- buffer_add_to_undo(buffer, offset, reason);
+ //writef_to_status_bar("moved to: %d | reason: %d\n", buf->cursor_offset, callback_reason);
}
int
keypress_actions(KeySym keysym, int modkey)
{
- // check shortcuts
- for (int i = 0; i < LEN(shortcuts); i++) {
- if (keysym == shortcuts[i].keysym && match(shortcuts[i].mod, modkey)) {
- shortcuts[i].func(&(shortcuts[i].arg));
- return 1;
- }
- }
+ check_shortcuts(keysym, modkey);
// default actions
@@ -477,10 +172,11 @@ keypress_actions(KeySym keysym, int modkey)
offset = focused_window->cursor_offset;
- // FALLTHROUGH
+ goto skip_delete_remove_selection;
case XK_Delete:
if (delete_selection(fb)) return 1;
+skip_delete_remove_selection:
move = buffer_remove(fb, offset, 1, 0, 0);
window_move_all_cursors_on_same_buf(&root_node, focused_node, focused_window->buffer_index, offset,
@@ -516,14 +212,14 @@ keypress_actions(KeySym keysym, int modkey)
return 1;
}
case XK_Page_Down:
- buffer_move_lines(focused_window, (term.row-1) / 2, 0);
+ buffer_move_lines(focused_window, (focused_node->maxy - focused_node->miny) / 2, 0);
buffer_move_to_x(focused_window, focused_window->cursor_col, CURSOR_UP_DOWN_MOVEMENT);
- focused_window->y_scroll += (term.row-1) / 2;
+ focused_window->y_scroll += (focused_node->maxy - focused_node->miny) / 2;
return 1;
case XK_Page_Up:
- buffer_move_lines(focused_window, -((term.row-1) / 2), 0);
+ buffer_move_lines(focused_window, -((focused_node->maxy - focused_node->miny) / 2), 0);
buffer_move_to_x(focused_window, focused_window->cursor_col, CURSOR_UP_DOWN_MOVEMENT);
- focused_window->y_scroll -= (term.row-1) / 2;
+ focused_window->y_scroll -= (focused_node->maxy - focused_node->miny) / 2;
return 1;
case XK_Tab:
buffer_insert(fb, "\t", 1, offset, 0);
diff --git a/config.h b/config.h
@@ -0,0 +1,52 @@
+/*
+ * ***THIS FILE IS NOT MADE FOR CUSTOMISATION, FOR CUSTOMISATION USE config.c***
+ *
+ * Unless you are adding new shared globals or heavily modifying the program,
+ * this file just expands global symbols that the rest of the files
+ * rely on to configure themselves.
+ *
+ * this file can also be used for reference if you are unsure what
+ * variables you might be missing from your own config.
+ */
+
+#ifndef CONFIG_H_
+#define CONFIG_H_
+
+#include "se.h"
+#include "x.h"
+#include "extension.h"
+
+extern struct 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;
+
+extern int border_px;
+extern float cw_scale;
+extern float ch_scale;
+extern char* fontconfig;
+extern const char* const colors[];
+extern struct glyph default_attributes;
+extern unsigned int cursor_fg;
+extern unsigned int cursor_bg;
+extern unsigned int cursor_thickness;
+extern unsigned int cursor_shape;
+extern unsigned int default_cols;
+extern unsigned int default_rows;
+
+extern unsigned int tabspaces;
+extern unsigned int default_indent_len; // 0 means tab
+extern int wrap_buffer;
+
+// see extension.h and extension.c
+extern struct extension_meta* extensions;
+
+#endif // CONFIG_H_
diff --git a/config.mk b/config.mk
@@ -21,8 +21,8 @@ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \
`$(PKG_CONFIG) --libs freetype2`
# flags
-SECPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
-SECFLAGS = $(INCS) $(SECPPFLAGS) $(CPPFLAGS) -g -Wall -Wpedantic -Os
+SECPPFLAGS = -D_XOPEN_SOURCE=600
+SECFLAGS = $(INCS) $(SECPPFLAGS) $(CPPFLAGS) -Wall -Wpedantic -O0 -g
SELDFLAGS = $(LIBS) $(LDFLAGS)
# OpenBSD:
diff --git a/extension.h b/extension.h
@@ -0,0 +1,89 @@
+#ifndef EXTENSION_H_
+#define EXTENSION_H_
+
+#include "se.h"
+
+struct extension {
+ char* name;
+ char* description;
+
+// standard functions
+ void(*init)(struct extension* self);
+ void(*frame)(void);
+ void(*enable)(void);
+ void(*disable)(void);
+
+/////////////////////////////////////////
+// callbacks
+//
+// A non-zero return value from an extension callback
+// will not call any further extensions that have the same callback.
+// This is necearry for any of the callbacks that have "return" values,
+// namely wb_new_line_draw, wb_write_status_bar
+// and wn_custom_window_keypress_override
+//
+
+
+// window buffer
+ int(*wb_cursor_movement)(struct window_buffer* wb, enum cursor_reason reason);
+
+ ///////////////////////////////////
+ // string will be drawn at the start of a new line
+ int(*wb_new_line_draw)(char** string, struct window_buffer* wb, int y, int lines_left, int minx, int maxx, struct glyph* attr);
+
+ ///////////////////////////////////
+ // write_again means the line will be written and the callback will
+ // be called again with minx at the end line
+ int(*wb_write_status_bar)(int* write_again, struct window_buffer* wb, int minx, int maxx, int cx, int cy, char line[LINE_MAX_LEN], struct glyph* g);
+
+
+// file buffer
+ int(*fb_new_file_opened)(struct file_buffer* fb);
+
+ int(*fb_paste)(struct file_buffer* fb, char* data, int len);
+ int(*keypress)(KeySym keycode, int modkey, const char* buf, int len);
+ int(*fb_written_to_file)(struct file_buffer* fb);
+
+ ///////////////////////////////////
+ // For undo functionality you can use this function to update the undo buffers
+ // there is provided the fb_add_to_undo function, but you may implement your own
+ // see buffer.h/buffer.c fb_add_to_undo()
+ int(*fb_contents_updated)(struct file_buffer* fb, int offset, enum buffer_content_reason reason);
+
+
+// window node
+ int(*wn_custom_window_draw)(struct window_split_node* wn);
+ int(*window_written_to_screen)(struct window_split_node* wn, const int offset_start, const int offset_end, uint8_t* move_buffer, const int move_buffer_len);
+ int(*wn_custom_window_keypress_override)(int* skip_keypress_callback, struct window_split_node* wn, KeySym keycode, int modkey, const char* buf, int len);
+};
+
+struct extension_meta {
+ struct extension e;
+ size_t enabled;
+ size_t end;
+};
+
+#define extension_callback_exists(_callback, ...) \
+ do { \
+ if (!extensions) \
+ break; \
+ for (int _iterator = 0; !extensions[_iterator].end; _iterator++) { \
+ if (extensions[_iterator].e._callback && extensions[_iterator].enabled) { \
+ __VA_ARGS__ \
+ break; \
+ } \
+ } \
+ } while (0)
+
+#define call_extension(_callback, ...) \
+ do { \
+ if (!extensions) \
+ break; \
+ for (int _iterator = 0; !extensions[_iterator].end; _iterator++) \
+ if (extensions[_iterator].e._callback && extensions[_iterator].enabled) \
+ if (extensions[_iterator].e._callback(__VA_ARGS__)) \
+ break; \
+ } while (0)
+
+
+#endif // EXTENSION_H_
diff --git a/extensions/README.txt b/extensions/README.txt
@@ -0,0 +1,3 @@
+TODO: create readme in the different folders
+
+TODO: make new default configs
diff --git a/extensions/default_shortcuts.h b/extensions/default_shortcuts.h
@@ -0,0 +1,262 @@
+static void numlock(const shortcut_arg* arg);
+static void zoom(const shortcut_arg* arg);
+static void zoomabs(const shortcut_arg* arg);
+static void zoomreset(const shortcut_arg* arg);
+
+static void window_split(const shortcut_arg* arg);
+static void window_resize(const shortcut_arg* arg);
+static void window_delete(const shortcut_arg* arg);
+static void window_change(const shortcut_arg* arg);
+
+static void cursor_move_x_relative(const shortcut_arg* arg);
+static void cursor_move_y_relative(const shortcut_arg* arg);
+static void move_cursor_to_offset(const shortcut_arg* arg);
+static void move_cursor_to_end_of_buffer(const shortcut_arg* arg);
+
+static void swap_to_next_file_buffer(const shortcut_arg* arg);
+static void save_buffer(const shortcut_arg* arg);
+static void buffer_kill(const shortcut_arg* arg);
+
+static void clipboard_copy(const shortcut_arg* arg);
+static void clipboard_paste(const shortcut_arg* arg);
+static void toggle_selection(const shortcut_arg* arg);
+static void undo(const shortcut_arg* arg);
+static void redo(const shortcut_arg* arg);
+
+static void search(const shortcut_arg* arg);
+static void search_next(const shortcut_arg* arg);
+static void search_previous(const shortcut_arg* arg);
+static void search_for_buffer(const shortcut_arg* arg);
+static void search_keyword_in_buffers(const shortcut_arg* arg);
+static void open_file_browser(const shortcut_arg* arg);
+
+/////////////////////////////////////////////////
+// function implementations
+//
+
+void
+numlock(const shortcut_arg* arg)
+{
+ win.mode ^= MODE_NUMLOCK;
+}
+
+void window_split(const shortcut_arg* arg)
+{
+ window_node_split(focused_node, 0.5, arg->i);
+#if 1
+ if (focused_node->node2) {
+ focused_node = focused_node->node2;
+ focused_window = &focused_node->window;
+ }
+#else
+ if (focused_node->node1) {
+ focused_node = focused_node->node1;
+ focused_window = &focused_node->window;
+ }
+#endif
+}
+
+void window_resize(const shortcut_arg* arg)
+{
+ float amount = (arg->i == MOVE_RIGHT || arg->i == MOVE_LEFT) ? 0.1f : 0.05f;
+ window_node_resize(focused_node, arg->i, amount);
+}
+
+void window_delete(const shortcut_arg* arg)
+{
+ struct window_split_node* new_node = window_node_delete(focused_node);
+ while (new_node->mode != WINDOW_SINGULAR)
+ new_node = new_node->node1;
+ focused_node = new_node;
+ focused_window = &focused_node->window;
+}
+
+void window_change(const shortcut_arg* arg)
+{
+ focused_node = window_switch_to_window(focused_node, arg->i);
+ focused_window = &focused_node->window;
+}
+
+void
+zoom(const shortcut_arg* arg)
+{
+ shortcut_arg larg;
+
+ larg.f = usedfontsize + arg->f;
+ zoomabs(&larg);
+}
+
+void
+zoomabs(const shortcut_arg* arg)
+{
+ xunloadfonts();
+ xloadfonts(fontconfig, arg->f);
+ cresize(0, 0);
+ xhints();
+}
+
+void
+zoomreset(const shortcut_arg* arg)
+{
+ shortcut_arg larg;
+
+ if (defaultfontsize > 0) {
+ larg.f = defaultfontsize;
+ zoomabs(&larg);
+ }
+}
+
+void
+cursor_move_x_relative(const shortcut_arg* arg)
+{
+ if (focused_window->mode != WINDOW_BUFFER_FILE_BROWSER)
+ buffer_move_on_line(focused_window, arg->i, CURSOR_RIGHT_LEFT_MOVEMENT);
+}
+
+void
+cursor_move_y_relative(const shortcut_arg* arg)
+{
+ buffer_move_lines(focused_window, arg->i, 0);
+ buffer_move_to_x(focused_window, focused_window->cursor_col, CURSOR_UP_DOWN_MOVEMENT);
+}
+
+void
+swap_to_next_file_buffer(const shortcut_arg* arg)
+{
+ focused_window->buffer_index++;
+}
+
+void
+save_buffer(const shortcut_arg* arg)
+{
+ buffer_write_to_filepath(get_file_buffer(focused_window));
+}
+
+void
+toggle_selection(const shortcut_arg* arg)
+{
+ struct file_buffer* fb = get_file_buffer(focused_window);
+ if (fb->mode & BUFFER_SELECTION_ON) {
+ fb->mode &= ~(BUFFER_SELECTION_ON);
+ } else {
+ fb->mode |= BUFFER_SELECTION_ON;
+ fb->s1o = fb->s2o = focused_window->cursor_offset;
+ }
+}
+
+void
+move_cursor_to_offset(const shortcut_arg* arg)
+{
+ focused_window->cursor_offset = arg->i;
+}
+
+void
+move_cursor_to_end_of_buffer(const shortcut_arg* arg)
+{
+ focused_window->cursor_offset = get_file_buffer(focused_window)->len-1;
+}
+
+void
+clipboard_copy(const shortcut_arg* arg)
+{
+ struct file_buffer* fb = get_file_buffer(focused_window);
+ int 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;
+}
+
+void
+clipboard_paste(const shortcut_arg* arg)
+{
+ insert_clipboard_at_cursor();
+}
+
+void
+undo(const shortcut_arg* arg)
+{
+ buffer_undo(get_file_buffer(focused_window));
+}
+
+void
+redo(const shortcut_arg* arg)
+{
+ buffer_redo(get_file_buffer(focused_window));
+}
+
+void
+search(const shortcut_arg* arg)
+{
+ get_file_buffer(focused_window)->mode &= ~BUFFER_SEARCH_BLOCKING_IDLE;
+ get_file_buffer(focused_window)->mode |= BUFFER_SEARCH_BLOCKING;
+ writef_to_status_bar("search: %s", get_file_buffer(focused_window)->search_term);
+}
+
+void
+search_next(const shortcut_arg* arg)
+{
+ int new_offset = buffer_seek_string_wrap(focused_window, focused_window->cursor_offset+1,
+ get_file_buffer(focused_window)->search_term);
+ if (new_offset < 0) {
+ writef_to_status_bar("no results for \"%s\"", get_file_buffer(focused_window)->search_term);
+ return;
+ } else if (focused_window->cursor_offset > new_offset) {
+ writef_to_status_bar("search wrapped");
+ }
+ focused_window->cursor_offset = new_offset;
+}
+
+void
+search_previous(const shortcut_arg* arg)
+{
+ int new_offset = buffer_seek_string_wrap_backwards(focused_window, focused_window->cursor_offset-1,
+ get_file_buffer(focused_window)->search_term);
+ if (new_offset < 0) {
+ writef_to_status_bar("no results for \"%s\"", get_file_buffer(focused_window)->search_term);
+ return;
+ } else if (focused_window->cursor_offset < new_offset) {
+ writef_to_status_bar("search wrapped");
+ }
+ focused_window->cursor_offset = new_offset;
+}
+
+void
+search_for_buffer(const shortcut_arg* arg)
+{
+ if (focused_window->mode != WINDOW_BUFFER_NORMAL)
+ return;
+ *focused_node->search = 0;
+ focused_node->selected = 0;
+ focused_window->mode = WINDOW_BUFFER_SEARCH_BUFFERS;
+}
+
+void
+search_keyword_in_buffers(const shortcut_arg* arg)
+{
+ if (focused_window->mode != WINDOW_BUFFER_NORMAL)
+ return;
+ *focused_node->search = 0;
+ focused_node->selected = 0;
+ focused_window->mode = WINDOW_BUFFER_KEYWORD_ALL_BUFFERS;
+}
+
+
+void
+open_file_browser(const shortcut_arg* arg)
+{
+ 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
+buffer_kill(const shortcut_arg* arg)
+{
+ destroy_file_buffer_entry(focused_node, &root_node);
+}
diff --git a/extensions/default_status_bar.h b/extensions/default_status_bar.h
@@ -0,0 +1,58 @@
+#include <math.h>
+
+extern struct window_buffer* focused_window;
+
+static int default_status_bar_callback(int* write_again, struct window_buffer* buf, int minx, int maxx, int cx, int cy, char line[LINE_MAX_LEN], struct glyph* g);
+
+static const struct extension default_status_bar = {
+ .wb_write_status_bar = default_status_bar_callback
+};
+
+int
+default_status_bar_callback(int* write_again, struct window_buffer* buf, int minx, int maxx, int cx, int cy, char line[LINE_MAX_LEN], struct glyph* g)
+{
+ static int count = 0;
+ if (!buf) {
+ count = 0;
+ return 0;
+ }
+
+ struct file_buffer* fb = get_fb(buf);
+ switch (count) {
+ const char* name;
+ int percent;
+ case 0:
+ if (fb->mode & FB_SEARCH_BLOCKING_IDLE) {
+ int before;
+ int search_count = fb_count_string_instances(fb, fb->search_term, focused_window->cursor_offset, &before);
+ snprintf(line, LINE_MAX_LEN, " %d/%d", before, search_count);
+ }
+ break;
+ case 1:
+ snprintf(line, LINE_MAX_LEN, " %dk ", fb->len/1000);
+ break;
+ case 2:
+ g->fg = path_color;
+ char* path = file_path_get_path(fb->file_path);
+ snprintf(line, LINE_MAX_LEN, "%s", path);
+ free(path);
+ break;
+ case 3:
+ name = strrchr(fb->file_path, '/')+1;
+ if (name)
+ snprintf(line, LINE_MAX_LEN, "%s", name);
+ break;
+ case 4:
+ percent = ceilf(((float)(buf->cursor_offset)/(float)fb->len)*100.0f);
+ LIMIT(percent, 0, 100);
+ snprintf(line, LINE_MAX_LEN, " %d:%d %d%%" , cy+1, cx, percent);
+ break;
+ case 5:
+ count = 0;
+ *write_again = 0;
+ return 0;
+ }
+ count++;
+ *write_again = 1;
+ return 0;
+}
diff --git a/extensions/keep_cursor_col.h b/extensions/keep_cursor_col.h
@@ -0,0 +1,18 @@
+#ifndef KEEP_CURSOR_COL_H_
+#define KEEP_CURSOR_COL_H_
+
+static int
+keep_cursor_col_callback(struct window_buffer* buf, enum cursor_reason callback_reason)
+{
+ if (callback_reason == CURSOR_COMMAND_MOVEMENT || callback_reason == CURSOR_RIGHT_LEFT_MOVEMENT) {
+ int y, tmp;
+ fb_offset_to_xy(get_fb(buf), buf->cursor_offset, 0, buf->y_scroll, &buf->cursor_col, &y, &tmp);
+ }
+ return 0;
+}
+
+static const struct extension keep_cursor_col = {
+ .wb_cursor_movement = keep_cursor_col_callback
+};
+
+#endif // KEEP_CURSOR_COL_H_
diff --git a/extensions/line_count.h b/extensions/line_count.h
@@ -0,0 +1,21 @@
+static char* line_count(struct window_buffer* wb, int y, int lines_left, int minx, int maxx, struct glyph* attr)
+{
+ static char line[LINE_MAX_LEN];
+ int tmp, tmp2, cy;
+ fb_offset_to_xy(get_fb(wb), wb->cursor_offset, 0, wb->y_scroll, &tmp, &cy, &tmp2);
+
+ y += wb->y_scroll + 1;
+ cy += wb->y_scroll + 1;
+
+ snprintf(line, LINE_MAX_LEN, "%3d ", y);
+
+ if (y == cy) {
+ attr->fg = yellow;
+ attr->bg = alternate_bg_bright;
+ }
+
+ return line;
+}
+
+// add with this:
+// char*(*wb_new_line_draw)(struct window_buffer* wb, int y, int lines_left, int minx, int maxx, Glyph* attr) = line_count;
diff --git a/extensions/line_count_relative.h b/extensions/line_count_relative.h
@@ -0,0 +1,28 @@
+static char* line_count_relative(struct window_buffer* wb, int y, int lines_left, int minx, int maxx, struct glyph* attr)
+{
+ static char line[LINE_MAX_LEN];
+ int tmp, tmp2, cy;
+ fb_offset_to_xy(get_fb(wb), wb->cursor_offset, 0, wb->y_scroll, &tmp, &cy, &tmp2);
+
+ cy += wb->y_scroll + 1;
+ y += wb->y_scroll + 1;
+
+ if (y == cy) {
+ attr->fg = yellow;
+ attr->bg = alternate_bg_bright;
+ snprintf(line, LINE_MAX_LEN, "%3d ", y);
+ } else {
+ int tmp_y = y;
+ char* offset = line;
+ while(tmp_y >= 1000 && offset - line < LINE_MAX_LEN) {
+ *offset++ = ' ';
+ tmp_y /= 10;
+ }
+ snprintf(offset, LINE_MAX_LEN - (offset - line), "%3d ", abs(cy - y));
+ }
+
+ return line;
+}
+
+// add with this
+// char*(*wb_new_line_draw)(struct window_buffer* wb, int y, int lines_left, int minx, int maxx, struct glyph* attr) = line_count_relative;
diff --git a/extensions/move_selection_with_cursor.h b/extensions/move_selection_with_cursor.h
@@ -0,0 +1,32 @@
+#ifndef MOVE_SELECTION_WITH_CURSOR_H_
+#define MOVE_SELECTION_WITH_CURSOR_H_
+
+static int
+move_selection(struct window_buffer* wb, enum cursor_reason callback_reason)
+{
+ struct file_buffer* fb = get_fb(wb);
+ if (fb->mode & FB_SELECTION_ON) {
+ if (fb->mode & FB_LINE_SELECT) {
+ int twice = 2;
+ while (twice--) {
+ if (fb_is_selection_start_top_left(fb)) {
+ fb->s2o = fb_seek_char(fb, wb->cursor_offset, '\n');
+ fb->s1o = fb_seek_char_backwards(fb, fb->s1o, '\n');
+ } else {
+ fb->s2o = fb_seek_char_backwards(fb, wb->cursor_offset, '\n');
+ fb->s1o = fb_seek_char(fb, fb->s1o, '\n');
+ }
+ }
+ } else {
+ fb->s2o = wb->cursor_offset;
+ }
+
+ }
+ return 0;
+}
+
+static const struct extension move_selection_with_cursor = {
+ .wb_cursor_movement = move_selection,
+};
+
+#endif // MOVE_SELECTION_WITH_CURSOR_H_
diff --git a/extensions/shortcuts.h b/extensions/shortcuts.h
@@ -0,0 +1,47 @@
+typedef union {
+ int i;
+ unsigned int ui;
+ float f;
+ int vec2i[2];
+ const void *v;
+ const char *s;
+} shortcut_arg;
+
+
+typedef struct {
+ unsigned int mod;
+ KeySym keysym;
+ void (*func)(const shortcut_arg* arg);
+ const shortcut_arg arg;
+} Shortcut;
+
+#define shortcuts const Shortcut shortcut_array[]
+
+#define check_shortcuts(_ksym, _modkey) \
+ do { \
+ for (int i = 0; i < LEN(shortcut_array); i++) { \
+ if (_ksym == shortcut_array[i].keysym && match(shortcut_array[i].mod, _modkey)) { \
+ shortcut_array[i].func(&(shortcut_array[i].arg)); \
+ return 1; \
+ } \
+ } \
+ }while(0)
+
+/*
+implement with:
+
+SHORTCUTS() = {
+// mask keysym function argument
+ { 0, XK_Right, cursor_move_x_relative, {.i = +1} },
+ { 0, XK_Left, cursor_move_x_relative, {.i = -1} },
+ { 0, XK_Down, cursor_move_y_relative, {.i = +1} },
+};
+
+...
+
+int
+keypress_actions(KeySym keysym, int modkey)
+{
+ check_shortcuts(keysym, modkey);
+ ...
+*/
diff --git a/extensions/startup_message.h b/extensions/startup_message.h
@@ -0,0 +1,21 @@
+#ifndef STARTUP_MESSAGE_H_
+#define STARTUP_MESSAGE_H_
+
+static const char* const welcome[] = {"Welcome to se!", "Good day, Human", "Happy Coding", "se: the Simple Editor",
+ "Time to get some progress done!", "Ready for combat", "Initialising...Done", "loaded in %%d seconds",
+ "Fun fact: vscode has over two times as many lines describing dependencies than se has in total",
+ "You look based", "Another day, another bug to fix", "Who needs a mouse ¯\\_(ツ)_/¯", "grrgrrggghhaaaaaa (╯°□ °)╯︵ ┻━┻",
+ "┬┴┬┤(・_├┬┴┬┴┬┴┬┤ʖ ͡°) ├┬┴┬┴┬┴┬┴┬┴┬┴┬┴┬┴┬┴┬┴", "ʰᵉˡˡᵒ"};
+
+static void
+choose_random_message()
+{
+ writef_to_status_bar(welcome[rand() % LEN(welcome)]);
+}
+
+static const struct extension startup_message = {
+ .enable = choose_random_message
+};
+
+
+#endif // STARTUP_MESSAGE_H_
diff --git a/extensions/syntax/c.h b/extensions/syntax/c.h
@@ -0,0 +1,125 @@
+#include "handy_defines.h"
+
+#ifdef macro_color
+#define color_macro(_str) {COLOR_WORD,{_str}, macro_color}
+#endif
+
+const struct syntax_scheme_entry c_syntax[] = {
+ // 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
+#ifdef string_color
+ {COLOR_INSIDE_TO_LINE, {"#error ", "\n"}, string_color},
+#endif // string_color
+ {COLOR_WORD_STARTING_WITH_STR, {"#"}, {.fg = yellow, .mode = ATTR_BOLD}},
+ color_macro("sizeof"), color_macro("alignof"),
+ color_macro("offsetof"),
+ {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_numbers(),
+#endif
+};
+
+#define c_word_seperators default_word_seperators
+
+const struct indent_scheme_entry c_indent[] = {
+ {INDENT_REMOVE, INDENT_LINE_CONTAINS_STR_MORE_THAN_STR, 0, {"}", "{"} },
+ {INDENT_NEW, INDENT_LINE_CONTAINS_STR_MORE_THAN_STR, -1, {"{", "}"} },
+
+ {INDENT_NEW, INDENT_LINE_ONLY_CONTAINS_STR, -1, {"("} },
+ {INDENT_KEEP_OPENER, INDENT_LINE_CONTAINS_STR_MORE_THAN_STR, -1, {"(", ")"} },
+ {INDENT_REMOVE, INDENT_LINE_ONLY_CONTAINS_STR, 0, {")"} },
+ {INDENT_REMOVE, INDENT_LINE_ONLY_CONTAINS_STR, 0, {");"} },
+ {INDENT_RETURN_TO_OPENER_BASE_INDENT, INDENT_LINE_CONTAINS_STR_MORE_THAN_STR, -1, {")", "("} },
+
+ {INDENT_NEW, INDENT_LINE_ONLY_CONTAINS_STR, -1, {"["} },
+ {INDENT_REMOVE, INDENT_LINE_ONLY_CONTAINS_STR, 0, {"]"} },
+ {INDENT_REMOVE, INDENT_LINE_ONLY_CONTAINS_STR, 0, {"];"} },
+ {INDENT_KEEP_OPENER, INDENT_LINE_CONTAINS_STR_MORE_THAN_STR, -1, {"[", "]"} },
+ {INDENT_RETURN_TO_OPENER_BASE_INDENT, INDENT_LINE_CONTAINS_STR_MORE_THAN_STR, -1, {"]", "["} },
+
+ {INDENT_REMOVE, INDENT_LINE_ENDS_WITH_STR, 0, {":"} },
+ {INDENT_NEW, INDENT_LINE_ENDS_WITH_STR, -1, {":"} },
+ {INDENT_KEEP, INDENT_LINE_ENDS_WITH_STR, -1, {";"} },
+ {INDENT_KEEP, INDENT_LINE_CONTAINS_STR_MORE_THAN_STR, 0, {"{", "}"} },
+ {INDENT_NEW, INDENT_LINE_CONTAINS_WORD, -1, {"else"} },
+ {INDENT_NEW, INDENT_LINE_CONTAINS_WORD, -1, {"if"} },
+ {INDENT_NEW, INDENT_LINE_CONTAINS_WORD, -1, {"for"} },
+ {INDENT_NEW, INDENT_LINE_CONTAINS_WORD, -1, {"while"} },
+
+};
diff --git a/extensions/syntax/gd.h b/extensions/syntax/gd.h
@@ -0,0 +1,99 @@
+#include "handy_defines.h"
+
+const struct syntax_scheme_entry gd_syntax[] = {
+ // Coloring type arguments Color
+
+ // strings
+#ifdef string_color
+ {COLOR_AROUND_TO_LINE, {"\"", "\""}, string_color},
+ {COLOR_AROUND, {"\"\"\"", "\"\"\""}, string_color},
+ {COLOR_AROUND_TO_LINE, {"'", "'"}, string_color},
+ {COLOR_INSIDE_TO_LINE, {"$", "."}, string_color},
+ {COLOR_INSIDE_TO_LINE, {"$", " "}, string_color},
+ {COLOR_INSIDE_TO_LINE, {"$", "("}, string_color},
+ {COLOR_INSIDE_TO_LINE, {"$", "["}, string_color},
+ {COLOR_INSIDE_TO_LINE, {"$", "\n"}, string_color},
+ {COLOR_STR, {"$"}, string_color},
+#endif
+ // comments
+#ifdef comment_color
+ {COLOR_AROUND, {"#", "\n"}, comment_color},
+#endif
+ // operators
+#ifdef operator_color
+ {COLOR_STR, {"!="}, normal_color},
+ {COLOR_STR, {"!"}, operator_color},
+#endif
+ // constants
+#ifdef constants_color
+ {COLOR_UPPER_CASE_WORD, {0}, constants_color},
+ {COLOR_WORD, {"PI"}, constants_color},
+ {COLOR_WORD, {"TAU"}, constants_color},
+ {COLOR_WORD, {"INF"}, constants_color},
+ {COLOR_WORD, {"NAN"}, constants_color},
+ {COLOR_WORD, {"null"}, constants_color},
+ {COLOR_WORD, {"true"}, constants_color},
+ {COLOR_WORD, {"false"}, constants_color},
+ {COLOR_STR_AFTER_WORD, {"const"}, constants_color},
+#endif
+ // keywords
+#ifdef keyword_color
+ color_keyword("extends"), color_keyword("class_name"),
+ color_keyword("var"), color_keyword("const"),
+ color_keyword("enum"), color_keyword("func"),
+ color_keyword("if"), color_keyword("else"),
+ color_keyword("elif"), color_keyword("for"),
+ color_keyword("while"), color_keyword("in"),
+ color_keyword("return"), color_keyword("class"),
+ color_keyword("pass"), color_keyword("continue"),
+ color_keyword("break"), color_keyword("is"),
+ color_keyword("as"), color_keyword("self"),
+ color_keyword("tool"), color_keyword("signal"),
+ color_keyword("static"), color_keyword("onready"),
+ color_keyword("export"), color_keyword("setget"),
+ color_keyword("breakpoint"), color_keyword("yield"),
+ color_keyword("assert"), color_keyword("preload"),
+ color_keyword("remote"), color_keyword("master"),
+ color_keyword("puppet"), color_keyword("remotesync"),
+ color_keyword("mastersync"), color_keyword("puppetsync"),
+ color_keyword("and"), color_keyword("or"),
+#endif
+ // functions
+#ifdef function_color
+ {COLOR_WORD_INSIDE, {"func ", "("}, macro_color},
+ {COLOR_WORD_INSIDE, {"class ", ":"}, macro_color},
+ {COLOR_WORD_BEFORE_STR, {"("}, function_color},
+#endif
+ // types
+#ifdef type_color
+ color_type("bool"), color_type("int"),
+ color_type("float"), color_type("String"),
+ color_type("Vector2"), color_type("Rect2"),
+ color_type("Vector3"), color_type("Transform2D"),
+ color_type("Plane"), color_type("Quat"),
+ color_type("AABB"), color_type("Basis"),
+ color_type("Transform"), color_type("Color"),
+ color_type("NodePath"), color_type("RID"),
+ color_type("Object"), color_type("Array"),
+ color_type("PoolByteArray"), color_type("PoolIntArray"),
+ color_type("PoolRealArray"), color_type("PoolStringArray"),
+ color_type("PoolVector2Array"), color_type("PoolVector3Array"),
+ color_type("PoolColorArray"), color_type("Dictionary"),
+ color_type("Dictionary"), color_type("Node"),
+ color_type("void"),
+#endif
+ // numbers
+#ifdef number_color
+ color_numbers(),
+#endif
+};
+
+#define gd_word_seperators default_word_seperators
+
+const struct indent_scheme_entry gd_indent[] = {
+ {INDENT_REMOVE, INDENT_LINE_CONTAINS_WORD, -1, {"return"}},
+ {INDENT_REMOVE, INDENT_LINE_CONTAINS_WORD, -1, {"break"}},
+ {INDENT_REMOVE, INDENT_LINE_CONTAINS_WORD, -1, {"continue"}},
+ {INDENT_REMOVE, INDENT_LINE_CONTAINS_WORD, -1, {"pass"}},
+ {INDENT_NEW, INDENT_LINE_ENDS_WITH_STR, -1, {":"}},
+};
diff --git a/extensions/syntax/handy_defines.h b/extensions/syntax/handy_defines.h
@@ -0,0 +1,32 @@
+#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
+
+#define 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")
+
+#endif // HANDY_DEFINES_H_
diff --git a/extensions/syntax/schemes/gruvbox.h b/extensions/syntax/schemes/gruvbox.h
@@ -0,0 +1,71 @@
+#ifndef _color_scheme_
+#define _color_scheme_
+
+// 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
+struct 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}
+
+#endif // _color_scheme_
diff --git a/extensions/syntax/syntax.h b/extensions/syntax/syntax.h
@@ -0,0 +1,646 @@
+#ifndef SYNTAX_H_
+#define SYNTAX_H_
+
+#include "../../se.h"
+#include "../../config.h"
+#include "../../extension.h"
+#include <ctype.h>
+
+
+///////////////////////////////////
+// the user must fill this inn
+// null terminated
+extern const struct syntax_scheme* syntax_schemes;
+
+static int fb_set_syntax_scheme(struct file_buffer* fb);
+static int apply_syntax(struct window_split_node* wn, const int offset_start, const int offset_end, uint8_t* move_buffer, const int move_buffer_len);
+
+static const struct extension syntax_e = {
+ .fb_new_file_opened = fb_set_syntax_scheme,
+ .window_written_to_screen = apply_syntax
+};
+
+#define UPPER_CASE_WORD_MIN_LEN 3
+
+enum syntax_scheme_mode {
+ // needs two strings
+ COLOR_AROUND,
+ // needs two strings
+ COLOR_AROUND_TO_LINE,
+ // needs two strings
+ COLOR_INSIDE,
+ // needs two strings
+ COLOR_INSIDE_TO_LINE,
+ // needs two strings
+ COLOR_WORD_INSIDE,
+ // needs one string
+ COLOR_WORD,
+ // needs one string
+ COLOR_WORD_ENDING_WITH_STR,
+ // needs one string
+ COLOR_WORD_STARTING_WITH_STR,
+ // needs one string
+ COLOR_STR,
+ // needs two strings
+ // colors word if string is found after it
+ COLOR_WORD_STR,
+ // needs one string
+ // can be combined with others if this is first
+ COLOR_STR_AFTER_WORD,
+ // needs one string
+ // "(" would color like this "not_colored colored("
+ // "[" would color like this "not_colored colored ["
+ COLOR_WORD_BEFORE_STR,
+ // needs one string
+ // "(" would color like this "colored not_colored("
+ // "=" would color like this "colored not_colored ="
+ COLOR_WORD_BEFORE_STR_STR,
+ // no arguments needed
+ COLOR_UPPER_CASE_WORD,
+};
+
+struct syntax_scheme_entry {
+ const enum syntax_scheme_mode mode;
+ const struct delimiter arg;
+ const struct glyph attr;
+};
+
+// TODO: INDENT_LINE_CONTAINS_STR_AND_STR
+enum indent_scheme_mode {
+ INDENT_LINE_ENDS_WITH_STR,
+ INDENT_LINE_DOES_NOT_END_WITH_STR,
+
+ INDENT_LINE_CONTAINS_WORD,
+ INDENT_LINE_ONLY_CONTAINS_STR,
+ // neds two strings
+ INDENT_LINE_CONTAINS_STR_MORE_THAN_STR,
+};
+
+enum indent_scheme_type {
+ INDENT_REMOVE = -1,
+ INDENT_KEEP_OPENER = 0,
+ INDENT_NEW = 1,
+
+ INDENT_KEEP,
+ // needs two strings, requires closer to be string 1 and opener to be string two
+ INDENT_RETURN_TO_OPENER_BASE_INDENT,
+};
+
+struct indent_scheme_entry {
+ const enum indent_scheme_type type;
+ const enum indent_scheme_mode mode;
+ const unsigned int line_offset;
+ const struct delimiter arg;
+};
+
+struct syntax_scheme {
+ const char* file_ending;
+ const char* word_seperators;
+
+ const struct syntax_scheme_entry* entries;
+ const int entry_count;
+
+ const struct indent_scheme_entry* indents;
+ const int indent_count;
+};
+
+
+static int fb_auto_indent(struct file_buffer* fb, int offset);
+
+static void do_syntax_scheme(struct file_buffer* fb, const struct syntax_scheme* cs, int offset);
+
+static void whitespace_count_to_indent_amount(int indent_len, int count, int* indent_count, int* extra_spaces);
+static int get_line_leading_whitespace_count(const char* line);
+static int get_line_relative_offset(struct file_buffer* fb, int offset, int count);
+
+static const struct syntax_scheme*
+fb_get_syntax_scheme(struct file_buffer* fb)
+{
+ return fb->syntax_index < 0 ? NULL : &syntax_schemes[fb->syntax_index];
+}
+
+static int
+fb_set_syntax_scheme(struct file_buffer* fb)
+{
+ for (int i = 0; syntax_schemes[i].file_ending; i++)
+ if (is_file_type(fb->file_path, syntax_schemes[i].file_ending))
+ fb->syntax_index = i;
+ return 0;
+}
+
+static int
+apply_syntax(struct window_split_node* wn, const int offset_start, const int offset_end, uint8_t* move_buffer, const int move_buffer_len)
+{
+ global_attr = default_attributes;
+ struct window_buffer* wb = &wn->wb;
+ struct file_buffer* fb = get_fb(wb);
+ const struct syntax_scheme* cs = fb_get_syntax_scheme(fb);
+ if (!cs)
+ return 0;
+
+ // clear state
+ do_syntax_scheme(NULL, &(struct syntax_scheme){0}, 0);
+
+ // search backwards to find multi-line syntax highlighting
+ for (int i = 0; i < cs->entry_count; i++) {
+ const struct syntax_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(cse.arg.start);
+ while((offset = fb_seek_string(fb, offset, cse.arg.start)) >= 0) {
+ offset += start_len;
+ if (offset >= offset_start)
+ break;
+ count++;
+ }
+
+ if (strcmp(cse.arg.start, cse.arg.end) != 0) {
+ int end_len = strlen(cse.arg.end);
+ offset = 0;
+ while((offset = fb_seek_string(fb, offset, cse.arg.end)) >= 0) {
+ offset += end_len;
+ if (offset >= offset_start)
+ break;
+ count--;
+ }
+ }
+ if (count > 0) {
+ offset = fb_seek_string_backwards(fb, offset_start, cse.arg.start);
+ do_syntax_scheme(fb, cs, offset);
+ break;
+ }
+ }
+ }
+
+ int x = wn->minx + move_buffer[0], y = wn->miny;
+ int move_buffer_index = 0;
+ int charsize = 1;
+ for(int i = offset_start; i < offset_end && y < wn->maxy
+ && move_buffer_index < move_buffer_len; i += charsize) {
+ do_syntax_scheme(fb, cs, i);
+ screen_set_attr(x, y)->fg = global_attr.fg;
+ screen_set_attr(x, y)->bg = global_attr.bg;
+
+ uint8_t amount = move_buffer[move_buffer_index];
+ if (amount & (1<<7)) {
+ x = wn->minx;
+ y++;
+ amount &= ~(1<<7);
+ }
+ x += amount;
+
+ rune_t u;
+ charsize = utf8_decode_buffer(fb->contents + i, i - offset_start, &u);
+ if (charsize == 0)
+ charsize = 1;
+ move_buffer_index++;
+ }
+
+ do_syntax_scheme(NULL, &(struct syntax_scheme){0}, 0);
+ global_attr = default_attributes;
+ return 0;
+}
+
+void
+do_syntax_scheme(struct file_buffer* fb, const struct syntax_scheme* cs, int offset)
+{
+ static int end_at_whitespace = 0;
+ static const char* end_condition;
+ static int end_condition_len;
+ static struct glyph next_word_attr;
+ static int color_next_word = 0;
+ static int around = 0;
+
+ if (!fb || !cs) {
+ // reset
+ end_at_whitespace = 0;
+ end_condition_len = 0;
+ around = 0;
+ color_next_word = 0;
+ end_condition = NULL;
+ global_attr = default_attributes;
+ return;
+ }
+
+ char* buf = fb->contents;
+ int buflen = fb->len;
+
+ if (end_condition && !color_next_word) {
+ if (buflen - offset <= end_condition_len)
+ return;
+ if (end_at_whitespace && buf[offset] == '\n') {
+ // *_TO_LINE reached end of line
+ end_condition_len = 0;
+ end_condition = NULL;
+ end_at_whitespace = 0;
+ global_attr = default_attributes;
+ } else if (fb_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 until later
+ if (around)
+ around = 0;
+ else
+ global_attr = default_attributes;
+
+ end_condition = NULL;
+ end_at_whitespace = 0;
+ }
+ return;
+ } else if (end_at_whitespace) {
+ if (!fb_is_on_a_word(fb, offset, cs->word_seperators)) {
+ end_at_whitespace = 0;
+ global_attr = default_attributes;
+ } else {
+ return;
+ }
+ } else if (color_next_word) {
+ // check if new word encountered
+ if (!fb_is_on_a_word(fb, offset, cs->word_seperators))
+ return;
+ global_attr = next_word_attr;
+ color_next_word = 0;
+ end_at_whitespace = 1;
+ return;
+ } else if (end_condition_len > 0) {
+ // wait for the word/sequence to finish
+ // NOTE: does not work with utf8 chars
+ // TODO: ???
+ if (--end_condition_len <= 0)
+ global_attr = default_attributes;
+ else
+ return;
+ }
+
+ for (int i = 0; i < cs->entry_count; i++) {
+ struct syntax_scheme_entry entry = cs->entries[i];
+ enum syntax_scheme_mode mode = entry.mode;
+
+ if (mode == COLOR_UPPER_CASE_WORD) {
+ if (!fb_is_start_of_a_word(fb, offset, cs->word_seperators))
+ continue;
+
+ int end_len = 0;
+ 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 UPPER_CASE_WORD_MIN_LEN chars
+ if (end_len < UPPER_CASE_WORD_MIN_LEN)
+ continue;
+
+ global_attr = entry.attr;
+ end_condition_len = end_len;
+ return;
+
+ not_upper_case:
+ continue;
+ }
+
+ int len = strlen(entry.arg.start);
+
+ 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;
+
+ int offset_tmp = offset;
+ // find new word twice if it's BEFORE_STR_STR
+ int times = mode == COLOR_WORD_BEFORE_STR_STR ? 2 : 1;
+ int first_word_len = 0;
+ int first_time = 1;
+ while (times--) {
+ // seek end of word
+ offset_tmp = fb_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 = offset_tmp - offset;
+
+ if (mode != COLOR_WORD_ENDING_WITH_STR)
+ offset_tmp = fb_seek_not_whitespace(fb, offset_tmp);
+
+ first_time = 0;
+ }
+
+ if (mode == COLOR_WORD_ENDING_WITH_STR) {
+ offset_tmp -= len;
+ if (offset_tmp < 0)
+ continue;
+ }
+ if (fb_offset_starts_with(fb, offset_tmp, entry.arg.start)) {
+ global_attr = entry.attr;
+ end_condition_len = first_word_len;
+ return;
+ }
+ exit_word_before_str_str:
+ continue;
+ }
+
+ if (mode == COLOR_INSIDE || mode == COLOR_INSIDE_TO_LINE || mode == COLOR_WORD_INSIDE) {
+ if (offset - len < 0)
+ continue;
+ // check the if what's behind the cursor is the first string
+ if (fb_offset_starts_with(fb, offset - len, entry.arg.start)) {
+ if (offset < fb->len && fb_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;
+ offset_tmp = fb_seek_not_whitespace(fb, offset_tmp);
+ offset_tmp = fb_seek_whitespace(fb, offset_tmp);
+ int offset_tmp1 = offset_tmp - strlen(entry.arg.end);
+ offset_tmp = fb_seek_not_whitespace(fb, offset_tmp);
+
+ if ((!fb_offset_starts_with(fb, offset_tmp, entry.arg.end)
+ && !fb_offset_starts_with(fb, offset_tmp1, entry.arg.end))
+ || offset_tmp1 - offset <= 1 || offset_tmp - offset <= 1)
+ continue;
+ } else if (mode == COLOR_INSIDE_TO_LINE) {
+ if (fb_seek_char(fb, offset, '\n') < fb_seek_string(fb, offset, entry.arg.end))
+ continue;
+ }
+
+
+ end_condition = entry.arg.end;
+ end_condition_len = strlen(entry.arg.end);
+ global_attr = entry.attr;
+ around = 0;
+ return;
+ }
+ continue;
+ }
+
+ if ((mode == COLOR_AROUND || mode == COLOR_AROUND_TO_LINE) &&
+ fb_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(!fb_offset_starts_with(fb, offset, entry.arg.start) ||
+ !fb_is_start_of_a_word(fb, offset, cs->word_seperators) ||
+ (fb_is_on_a_word(fb, offset + len, cs->word_seperators) && mode != COLOR_WORD_STARTING_WITH_STR))
+ continue;
+
+ if (mode == COLOR_WORD_STR) {
+ int offset_str = fb_seek_not_whitespace(fb, offset + len);
+
+ if (!fb_offset_starts_with(fb, offset_str, entry.arg.end))
+ continue;
+ end_condition_len = strlen(entry.arg.start);
+ } else {
+ end_at_whitespace = 1;
+ }
+ 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 (!fb_offset_starts_with(fb, offset, entry.arg.start))
+ continue;
+ end_condition_len = len;
+ global_attr = entry.attr;
+ return;
+ }
+ }
+}
+
+// TODO: make seperate plugin
+// also make the "syntax" member of file buffer not a thing
+// needs to be more dynamic
+int
+fb_auto_indent(struct file_buffer* fb, int offset)
+{
+ const struct syntax_scheme* cs = fb_get_syntax_scheme(fb);
+ LIMIT(offset, 0, fb->len-1);
+
+ int indent_diff = 0;
+ int indent_keep_x = -1;
+ int keep_pos = 0;
+
+ int get_line_offset;
+ char* get_line = NULL;
+
+ for (int i = 0; i < cs->indent_count; i++) {
+ const struct indent_scheme_entry indent = cs->indents[i];
+
+ get_line_offset = get_line_relative_offset(fb, offset, indent.line_offset);
+ if (get_line)
+ free(get_line);
+ get_line = fb_get_line_at_offset(fb, get_line_offset);
+
+ switch(indent.mode) {
+ int temp_offset, len;
+ char* res;
+ case INDENT_LINE_CONTAINS_WORD:
+ case INDENT_LINE_ONLY_CONTAINS_STR:
+ case INDENT_LINE_CONTAINS_STR_MORE_THAN_STR:
+ res = strstr(get_line, indent.arg.start);
+ if (!res)
+ continue;
+ if (INDENT_LINE_CONTAINS_WORD) {
+ if (res > get_line && !str_contains_char(cs->word_seperators, *(res-1)))
+ continue;
+ res += strlen(indent.arg.start);
+ if (*res && !str_contains_char(cs->word_seperators, *res))
+ continue;
+ } else if (INDENT_LINE_CONTAINS_STR_MORE_THAN_STR == indent.mode) {
+ char* start_last = get_line;
+ char* end_last = get_line;
+ for (int count = 0; count >= 0; ) {
+ if (start_last && (start_last = strstr(start_last, indent.arg.start))) {
+ start_last++;
+ count--;
+ } else {
+ goto indent_for_loop_continue;
+ }
+ if (end_last && (end_last = strstr(end_last, indent.arg.end))) {
+ end_last++;
+ count++;
+ }
+ }
+ } else if (indent.mode == INDENT_LINE_ONLY_CONTAINS_STR) {
+ int str_start = fb_seek_string_backwards(fb, get_line_offset, indent.arg.start);
+ int str_end = str_start + strlen(indent.arg.start);
+ int line_end = MIN(get_line_offset + 1, fb->len);
+ int line_start = MAX(fb_seek_char_backwards(fb, get_line_offset, '\n'), 0);
+ if (fb_seek_not_whitespace_backwards(fb, str_start-1) >= line_start ||
+ fb_seek_not_whitespace(fb, str_end+1) <= line_end)
+ continue;
+ }
+ if (indent.type == INDENT_KEEP_OPENER || indent.type == INDENT_RETURN_TO_OPENER_BASE_INDENT)
+ keep_pos = fb_seek_string_backwards(fb, get_line_offset, indent.arg.start);
+ goto set_indent_type;
+
+ case INDENT_LINE_ENDS_WITH_STR:
+ case INDENT_LINE_DOES_NOT_END_WITH_STR:
+ len = strlen(indent.arg.start);
+ temp_offset = fb_seek_not_whitespace_backwards(fb, get_line_offset) - len;
+ if (temp_offset < 0 ||
+ temp_offset < fb_seek_char_backwards(fb, get_line_offset, '\n'))
+ continue;
+ if (memcmp(fb->contents + get_line_offset, indent.arg.start, strlen(indent.arg.start)) == 0) {
+ if (indent.mode == INDENT_LINE_DOES_NOT_END_WITH_STR)
+ continue;
+ } else {
+ if (indent.mode == INDENT_LINE_ENDS_WITH_STR)
+ continue;
+ }
+ keep_pos = temp_offset;
+ goto set_indent_type;
+
+ set_indent_type:
+ if (indent.type == INDENT_KEEP_OPENER) {
+ if (indent_keep_x >= 0)
+ continue;
+ int tmp;
+ fb_offset_to_xy(fb, keep_pos, 0, 0, &indent_keep_x, &tmp, &tmp);
+ } else if (indent.type == INDENT_RETURN_TO_OPENER_BASE_INDENT) {
+ if (indent_keep_x >= 0)
+ continue;
+ int opener, closer;
+ if (!fb_get_delimiter(fb, keep_pos, indent.arg, NULL, &opener, &closer)) {
+ indent_keep_x = -1;
+ goto indent_for_loop_continue;
+ }
+ keep_pos = fb_seek_not_whitespace(fb, fb_seek_char_backwards(fb, opener, '\n'));
+ int tmp;
+ fb_offset_to_xy(fb, keep_pos, 0, 0, &indent_keep_x, &tmp, &tmp);
+ //TODO: why does this miss by one?
+ if (indent_keep_x > 0)
+ indent_keep_x--;
+ } else {
+ if (indent_diff)
+ continue;
+ indent_diff += indent.type;
+ }
+ }
+ indent_for_loop_continue:
+ continue;
+ }
+
+ if (get_line)
+ free(get_line);
+
+ int indents = 0, extra_spaces = 0;
+
+ int prev_line_offset = fb_seek_char_backwards(fb, offset, '\n') - 1;
+ if (prev_line_offset < 0)
+ return 0;
+ char* prev_line = fb_get_line_at_offset(fb, prev_line_offset);
+
+ if (indent_keep_x >= 0) {
+ whitespace_count_to_indent_amount(fb->indent_len, indent_keep_x, &indents, &extra_spaces);
+ } else {
+ whitespace_count_to_indent_amount(fb->indent_len, get_line_leading_whitespace_count(prev_line), &indents, &extra_spaces);
+ }
+ if (indent_diff != INDENT_KEEP) {
+ indents += indent_diff;
+ indents = MAX(indents, 0);
+ }
+
+ // remove the lines existing indent
+ int removed = 0;
+ int line_code_start = MIN(fb_seek_not_whitespace(fb, prev_line_offset + 1), fb_seek_char(fb, prev_line_offset + 1, '\n'));
+ if (line_code_start - (prev_line_offset) >= 1) {
+ removed = line_code_start - (prev_line_offset+1);
+ fb_remove(fb, prev_line_offset + 1, removed, 1, 1);
+ }
+
+ if (indents + extra_spaces <= 0) {
+ free(prev_line);
+ return -removed;
+ }
+
+ unsigned int indent_str_len = 0;
+ while(indents--)
+ indent_str_len += fb->indent_len ? fb->indent_len : 1;
+ char indent_str[indent_str_len + extra_spaces];
+
+ char space_tab = fb->indent_len > 0 ? ' ' : '\t';
+ int i = 0;
+ if (!fb->indent_len)
+ for (/* i = 0 */; i < indent_str_len; i++)
+ indent_str[i] = space_tab;
+ for (/* i = 0 or indent_str_len */ ; i < indent_str_len + extra_spaces; i++)
+ indent_str[i] = ' ';
+
+ fb_insert(fb, indent_str, indent_str_len + extra_spaces, prev_line_offset + 1, 0);
+
+ free(prev_line);
+ return indent_str_len + extra_spaces - removed;
+}
+
+int get_line_leading_whitespace_count(const char* line)
+{
+ int count = 0;
+ while(*line) {
+ if (*line == ' ')
+ count++;
+ else if (*line == '\t')
+ count += tabspaces - (count % tabspaces);
+ else
+ break;
+ line++;
+ }
+ return count;
+}
+
+void
+whitespace_count_to_indent_amount(int indent_len, int count, int* indent_count, int* extra_spaces)
+{
+ *indent_count = 0, *extra_spaces = 0;
+ int space_count = indent_len > 0 ? indent_len : tabspaces;
+ while(count >= space_count) {
+ *indent_count += 1;
+ count -= space_count;
+ }
+ *extra_spaces = count;
+}
+
+int
+get_line_relative_offset(struct file_buffer* fb, int offset, int count)
+{
+ offset = fb_seek_char(fb, offset, '\n');
+ if (offset < 0)
+ offset = fb->len;
+ if (count > 0) {
+ while(count-- && offset >= 0)
+ offset = fb_seek_char(fb, offset+1, '\n');
+ if (offset < 0)
+ offset = fb->len;
+ } else if (count < 0) {
+ offset = fb_seek_char_backwards(fb, offset, '\n');
+ while(count++ && offset >= 0)
+ offset = fb_seek_char_backwards(fb, offset-1, '\n');
+ if (offset < 0)
+ offset = 0;
+ }
+ offset = fb_seek_char(fb, offset, '\n');
+ if (offset > 0 && fb->contents[offset-1] != '\n')
+ offset--;
+ if (offset < 0)
+ offset = fb->len;
+ return offset;
+}
+
+
+#endif // SYNTAX_H_
diff --git a/extensions/undo.h b/extensions/undo.h
@@ -0,0 +1,24 @@
+#ifndef UNDO_H_
+#define UNDO_H_
+
+static int
+fb_add_to_undo_callback(struct file_buffer* fb, int offset, enum buffer_content_reason reason)
+{
+ fb_add_to_undo(fb, offset, reason);
+ return 0;
+}
+
+static int
+cursor_undo(struct window_buffer* wb, enum cursor_reason callback_reason)
+{
+ fb_add_to_undo(get_fb(wb), wb->cursor_offset, FB_CONTENT_CURSOR_MOVE);
+ //writef_to_status_bar("moved to: %d | reason: %d\n", wb->cursor_offset, callback_reason);
+ return 0;
+}
+
+static const struct extension undo = {
+ .fb_contents_updated = fb_add_to_undo_callback,
+ .wb_cursor_movement = cursor_undo
+};
+
+#endif // UNDO_H_
diff --git a/extensions/window_modes/choose_one_of_selection.enums b/extensions/window_modes/choose_one_of_selection.enums
@@ -0,0 +1,2 @@
+WB_SEARCH_FOR_BUFFERS,
+WB_SEARCH_KEYWORD_ALL_BUFFERS,
diff --git a/extensions/window_modes/choose_one_of_selection.h b/extensions/window_modes/choose_one_of_selection.h
@@ -0,0 +1,594 @@
+#ifndef CHOOSE_ONE_OF_SELECTION_H_
+#define CHOOSE_ONE_OF_SELECTION_H_
+
+#include "../../config.h"
+#include "../../extension.h"
+#include <ctype.h>
+#include <dirent.h>
+
+static int draw_dir(struct window_split_node* win);
+static int draw_search_buffers(struct window_split_node* wn);
+static int draw_search_keyword_in_all_buffers(struct window_split_node* wn);
+
+static int file_buffer_keypress_override_callback(int* skip_keypress_callback, struct window_split_node* wn, KeySym ksym, int modkey, const char* buf, int len);
+static int choose_one_of_selection_keypress_override_callback(int* skip_keypress_callback, struct window_split_node* wn, KeySym ksym, int modkey, const char* buf, int len);
+
+static const struct extension file_browser = {
+ .wn_custom_window_draw = draw_dir,
+ .wn_custom_window_keypress_override = file_buffer_keypress_override_callback,
+};
+
+static const struct extension search_open_fb = {
+ .wn_custom_window_draw = draw_search_buffers,
+ .wn_custom_window_keypress_override = choose_one_of_selection_keypress_override_callback
+};
+
+static const struct extension search_keywords_open_fb = {
+ .wn_custom_window_draw = draw_search_keyword_in_all_buffers,
+ .wn_custom_window_keypress_override = choose_one_of_selection_keypress_override_callback
+};
+
+struct keyword_pos {
+ int offset, fb_index;
+};
+
+static void choose_one_of_selection(const char* prefix, const char* search, const char* err,
+ const char*(*get_next_element)(const char*, const char*, int* offset, struct glyph* attr, void* data),
+ int* selected_line, int minx, int miny, int maxx, int maxy, int focused);
+
+// TODO: system for custom buffers
+// and allow for input overrides
+// have theese in their own file
+static const char* file_browser_next_item(const char* path, const char* search, int* offset, struct glyph* attr, void* data);
+// data pointer will give the file buffer of the current item
+static const char* buffer_search_next_item(const char* tmp, const char* search, int* offset, struct glyph* attr, void* data);
+// data pointer will give the keyword_pos of the current item
+static const char* buffers_search_keyword_next_item(const char* tmp, const char* search, int* offset, struct glyph* attr, void* data);
+
+const char*
+file_browser_next_item(const char* path, const char* search, int* offset, struct glyph* attr, void* data)
+{
+ static char filename_search[PATH_MAX];
+ static char filename[PATH_MAX];
+ static char full_path[PATH_MAX];
+ static DIR* dir;
+ if (!path || !search) {
+ if (dir) {
+ closedir(dir);
+ dir = NULL;
+ }
+ return NULL;
+ }
+ if (!dir)
+ dir = opendir(path);
+ int len = strlen(search);
+
+ struct dirent *folder;
+ while((folder = readdir(dir))) {
+ strcpy(filename, folder->d_name);
+ strcpy(full_path, path);
+ strcat(full_path, filename);
+ if (path_is_folder(full_path)) {
+ strcat(filename, "/");
+ if (attr)
+ attr->fg = path_color;
+ } else {
+ if (attr)
+ attr->fg = default_attributes.fg;
+ }
+
+ strcpy(filename_search, filename);
+ 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, "./") == 0 || strcmp(filename, "../") == 0)
+ break;
+ if (offset)
+ *offset = search_start - filename_search;
+ return filename;
+ }
+ search_start++;
+ }
+ }
+ *filename = *full_path = 0;
+ closedir(dir);
+ dir = NULL;
+ return NULL;
+}
+
+const char*
+buffer_search_next_item(const char* tmp, const char* search, int* offset, struct glyph* attr, void* data)
+{
+ static struct window_buffer wb;
+ static char file_path[PATH_MAX];
+ static int quit_next = 0;
+ if (!tmp || !search) {
+ wb.fb_index = 0;
+ quit_next = 0;
+ return NULL;
+ }
+
+ int len = strlen(search);
+ for(;;) {
+ if (quit_next) {
+ wb.fb_index = 0;
+ quit_next = 0;
+ return NULL;
+ }
+ struct file_buffer* fb = get_fb(&wb);
+ int last_fb_index = wb.fb_index;
+ wb.fb_index++;
+ get_fb(&wb);
+ if (wb.fb_index <= last_fb_index)
+ quit_next = 1;
+
+ strcpy(file_path, fb->file_path);
+
+ const char* s_repl = search;
+ while(!isupper(*s_repl++)) {
+ if (*s_repl == 0) {
+ for (char* fs = file_path; *fs; fs++)
+ *fs = tolower(*fs);
+ break;
+ }
+ }
+ char* search_start = file_path;
+
+ if (!len)
+ goto search_match;
+ while ((search_start = strchr(search_start+1, *search))) {
+ if (memcmp(search_start, search, len) == 0) {
+ search_match:
+ if (offset)
+ *offset = search_start - file_path;
+ if (data)
+ *(int*)data = last_fb_index;
+ return fb->file_path;
+ }
+ }
+ }
+}
+
+const char*
+buffers_search_keyword_next_item(const char* tmp, const char* search, int* offset, struct glyph* attr, void* data)
+{
+ static char item[LINE_MAX_LEN];
+ static struct window_buffer wb;
+ static int pos = -1;
+ if (!tmp || !search) {
+ wb.fb_index = 0;
+ pos = -1;
+ return NULL;
+ }
+ int len = strlen(search);
+ if (!len)
+ return NULL;
+
+ for (;;) {
+ struct file_buffer* fb = get_fb(&wb);
+
+ if ((pos = fb_seek_string(fb, pos+1, search)) >= 0) {
+ const char* filename = strrchr(fb->file_path, '/')+1;
+ if (!filename)
+ filename = fb->file_path;
+
+ int tmp, y;
+ fb_offset_to_xy(fb, pos, 0, wb.y_scroll, &tmp, &y, &tmp);
+
+ snprintf(item, LINE_MAX_LEN, "%s:%d: ", filename, y);
+ int itemlen = strlen(item);
+
+ char* line = fb_get_line_at_offset(fb, pos);
+ char* line_no_whitespace = line;
+ if (!isspace(*search))
+ while(isspace(*line_no_whitespace)) line_no_whitespace++;
+
+ snprintf(item + itemlen, LINE_MAX_LEN - itemlen, "%s", line_no_whitespace);
+
+ int line_start = fb_seek_char_backwards(fb, pos, '\n') + (line_no_whitespace - line);
+ free(line);
+
+ if (line_start < 0)
+ line_start = 0;
+ if (offset)
+ *offset = (pos - line_start) + itemlen;
+ if (data)
+ *(struct keyword_pos*)data = (struct keyword_pos){.offset = pos, .fb_index = wb.fb_index};
+ pos = fb_seek_char(fb, pos+1, '\n');
+ return item;
+ } else {
+ int last_fb_index = wb.fb_index;
+ wb.fb_index++;
+ get_fb(&wb);
+ if (wb.fb_index <= last_fb_index)
+ break;
+ pos = -1;
+ }
+ }
+ wb.fb_index = 0;
+ pos = -1;
+ return NULL;
+}
+
+static void
+choose_one_of_selection(const char* prefix, const char* search, const char* err,
+ const char*(*get_next_element)(const char*, const char*, int* offset, struct glyph* attr, void* data),
+ int* selected_line, int minx, int miny, int maxx, int maxy, int focused)
+{
+ soft_assert(prefix, prefix = "ERROR";);
+ soft_assert(search, search = "ERROR";);
+ soft_assert(err, search = "ERROR";);
+ soft_assert(selected_line, static int tmp; selected_line = &tmp;);
+ soft_assert(get_next_element, die("function choose_one_of_selection REQUIRES get_next_element to be a valid pointer"););
+
+ // change background color
+ global_attr.bg = alternate_bg_dark;
+ screen_set_region(minx, miny+1, maxx, maxy, ' ');
+ global_attr = default_attributes;
+ get_next_element(NULL, NULL, NULL, NULL, NULL);
+
+ int len = strlen(search);
+
+ // count folders to get scroll
+ int folder_lines = maxy - miny - 2;
+ int elements = 0;
+ int tmp_offset;
+ int limit = MAX(*selected_line, 999);
+ while(get_next_element(prefix, search, &tmp_offset, NULL, NULL) && elements <= limit)
+ elements++;
+ get_next_element(NULL, NULL, NULL, NULL, NULL);
+ *selected_line = MIN(*selected_line, elements-1);
+ int sel_local = *selected_line;
+
+ // print num of files
+ char count[256];
+ if (elements >= 1000)
+ snprintf(count, sizeof(count), "[>999:%2d] ", *selected_line+1);
+ else if (*selected_line > folder_lines)
+ snprintf(count, sizeof(count), "^[%3d:%2d] ", elements, *selected_line+1);
+ else if (elements-1 > folder_lines)
+ snprintf(count, sizeof(count), "ˇ[%3d:%2d] ", elements, *selected_line+1);
+ else
+ snprintf(count, sizeof(count), " [%3d:%2d] ", elements, *selected_line+1);
+
+ // print search term with prefix in front of it
+ // prefix is in path_color
+ global_attr.fg = path_color;
+ int new_x = write_string(count, miny, minx, maxx+1);
+ new_x = write_string(prefix, miny, new_x, maxx+1);
+ global_attr = default_attributes;
+ new_x = write_string(search, miny, new_x, maxx+1);
+
+ // print elements
+ int start_miny = miny;
+ int offset;
+ elements--;
+ miny++;
+ global_attr = default_attributes;
+ global_attr.bg = alternate_bg_dark;
+ const char* element;
+ while(miny <= maxy && (element = get_next_element(prefix, search, &offset, &global_attr, NULL))) {
+ if (elements > folder_lines && sel_local > folder_lines) {
+ elements--;
+ sel_local--;
+ continue;
+ }
+ write_string(element, miny, minx, maxx+1);
+
+ // change the color to highlight search term
+ for (int i = minx + offset; i < minx + len + offset && i < maxx+1; i++)
+ screen_set_attr(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++)
+ screen_set_attr(i, miny)->bg = selection_bg;
+ miny++;
+ }
+
+ if (elements < 0) {
+ global_attr = default_attributes;
+ global_attr.fg = warning_color;
+ write_string(err, start_miny, new_x, maxx+1);
+ }
+
+ // draw
+
+ for (int y = start_miny; y < maxy+1; y++)
+ xdrawline(minx, y, maxx+1);
+
+ draw_horisontal_line(maxy-1, minx, maxx);
+ xdrawcursor(new_x, start_miny, focused);
+
+ global_attr = default_attributes;
+}
+
+static int
+draw_dir(struct window_split_node* wn)
+{
+ soft_assert(wn->wb.mode < WB_MODES_END, return 1;);
+ if (wn->wb.mode != WB_FILE_BROWSER) return 0;
+
+ int focused = &wn->wb == focused_window;
+ struct file_buffer* fb = get_fb(&wn->wb);
+ char* folder = file_path_get_path(fb->file_path);
+
+ fb_change(fb, "\0", 1, fb->len, 1);
+ if (fb->len > 0) fb->len--;
+
+ choose_one_of_selection(folder, fb->contents, " [Create New File]", file_browser_next_item,
+ &wn->selected, wn->minx, wn->miny, wn->maxx, wn->maxy, focused);
+
+ free(folder);
+ return 1;
+}
+
+static int
+draw_search_buffers(struct window_split_node* wn)
+{
+ soft_assert(wn->wb.mode < WB_MODES_END, return 1;);
+ if (wn->wb.mode != WB_SEARCH_FOR_BUFFERS) return 0;
+
+ int focused = &wn->wb == focused_window;
+ choose_one_of_selection("Find loaded buffer: ", wn->search, " [No resuts]", buffer_search_next_item,
+ &wn->selected, wn->minx, wn->miny, wn->maxx, wn->maxy, focused);
+ return 1;
+}
+
+static int
+draw_search_keyword_in_all_buffers(struct window_split_node* wn)
+{
+ soft_assert(wn->wb.mode < WB_MODES_END, return 1;);
+ if (wn->wb.mode != WB_SEARCH_KEYWORD_ALL_BUFFERS) return 0;
+
+ int focused = &wn->wb == focused_window;
+ choose_one_of_selection("Find in all buffers: ", wn->search, " [No resuts]", buffers_search_keyword_next_item,
+ &wn->selected, wn->minx, wn->miny, wn->maxx, wn->maxy, focused);
+ return 1;
+}
+
+static int
+file_browser_actions(KeySym keysym, int modkey)
+{
+ static char full_path[PATH_MAX];
+ struct file_buffer* fb = get_fb(focused_window);
+ int offset = fb->len;
+
+ switch (keysym) {
+ int new_fb;
+ case XK_BackSpace:
+ if (offset <= 0) {
+ char* dest = strrchr(fb->file_path, '/');
+ if (dest && dest != fb->file_path) *dest = 0;
+ return 1;
+ }
+
+ focused_window->cursor_offset = offset;
+ wb_move_on_line(focused_window, -1, 0);
+ offset = focused_window->cursor_offset;
+
+ fb_remove(fb, offset, 1, 0, 0);
+ focused_node->selected = 0;
+ return 1;
+ case XK_Tab:
+ case XK_Return:
+ {
+ char* path = file_path_get_path(fb->file_path);
+ fb_change(fb, "\0", 1, fb->len, 1);
+ if (fb->len > 0) fb->len--;
+
+ file_browser_next_item(NULL, NULL, NULL, NULL, NULL);
+ const char* filename;
+ for (int y = 0; (filename = file_browser_next_item(path, fb->contents, NULL, NULL, NULL)); y++) {
+ strcpy(full_path, path);
+ strcat(full_path, filename);
+ if (y == focused_node->selected) {
+ if (path_is_folder(full_path)) {
+ strcpy(fb->file_path, full_path);
+
+ fb->len = 0;
+ fb->contents[0] = '\0';
+ focused_node->selected = 0;
+
+ free(path);
+ file_browser_next_item(NULL, NULL, NULL, NULL, NULL);
+ *full_path = 0;
+ return 1;
+ }
+ goto open_file;
+ }
+ }
+
+ if (fb->contents[fb->len-1] == '/') {
+ free(path);
+ *full_path = 0;
+ return 1;
+ }
+
+ strcpy(full_path, path);
+ strcat(full_path, fb->contents);
+open_file:
+ new_fb = fb_new_entry(full_path);
+ destroy_fb_entry(focused_node, &root_node);
+ focused_node->wb = wb_new(new_fb);
+ free(path);
+ *full_path = 0;
+ return 1;
+ }
+ case XK_Down:
+ focused_node->selected++;
+ return 1;
+ case XK_Up:
+ focused_node->selected--;
+ if (focused_node->selected < 0)
+ focused_node->selected = 0;
+ return 1;
+ case XK_Escape:
+ if (destroy_fb_entry(focused_node, &root_node))
+ writef_to_status_bar("Quit");
+ return 1;
+ }
+ return 0;
+}
+
+static void
+file_browser_string_insert(const char* buf, int buflen)
+{
+ static char full_path[PATH_MAX];
+ struct file_buffer* fb = get_fb(focused_window);
+
+ if (fb->len + buflen + strlen(fb->file_path) > PATH_MAX)
+ return;
+
+ if (buf[0] >= 32 || buflen > 1) {
+ fb_insert(fb, buf, buflen, fb->len, 0);
+ focused_node->selected = 0;
+
+ if (*buf == '/') {
+ fb_change(fb, "\0", 1, fb->len, 0);
+ if (fb->len > 0) fb->len--;
+ char* path = file_path_get_path(fb->file_path);
+ strcpy(full_path, path);
+ strcat(full_path, fb->contents);
+
+ free(path);
+
+ if (path_is_folder(full_path)) {
+ file_browser_actions(XK_Return, 0);
+ return;
+ }
+ }
+ } else {
+ writef_to_status_bar("unhandled control character 0x%x\n", buf[0]);
+ }
+}
+
+static int
+search_for_buffer_actions(KeySym keysym, int modkey)
+{
+ switch (keysym) {
+ case XK_Return:
+ case XK_Tab:
+ if (focused_window->mode == WB_SEARCH_KEYWORD_ALL_BUFFERS) {
+ struct keyword_pos kw;
+ int n = 0;
+ buffers_search_keyword_next_item(NULL, NULL, NULL, NULL, NULL);
+ while (buffers_search_keyword_next_item("", focused_node->search, NULL, NULL, &kw)) {
+ if (n == focused_node->selected) {
+ *focused_window = wb_new(kw.fb_index);
+ focused_window->cursor_offset = kw.offset;
+ return 1;
+ }
+ n++;
+ }
+ buffers_search_keyword_next_item(NULL, NULL, NULL, NULL, NULL);
+ } else if (focused_window->mode == WB_SEARCH_FOR_BUFFERS) {
+ int fb_index;
+ int n = 0;
+ buffer_search_next_item(NULL, NULL, NULL, NULL, NULL);
+ while (buffer_search_next_item("", focused_node->search, NULL, NULL, &fb_index)) {
+ if (n == focused_node->selected) {
+ *focused_window = wb_new(fb_index);
+ return 1;
+ }
+ n++;
+ }
+ buffer_search_next_item(NULL, NULL, NULL, NULL, NULL);
+ }
+ writef_to_status_bar("no results for \"%s\"", focused_node->search);
+ return 1;
+ case XK_BackSpace:
+ utf8_remove_string_end(focused_node->search);
+ focused_node->selected = 0;
+ return 1;
+ case XK_Down:
+ focused_node->selected++;
+ return 1;
+ case XK_Up:
+ focused_node->selected--;
+ if (focused_node->selected < 0)
+ focused_node->selected = 0;
+ return 1;
+ case XK_Page_Down:
+ focused_node->selected += 10;
+ return 1;
+ case XK_Page_Up:
+ focused_node->selected -= 10;
+ if (focused_node->selected < 0)
+ focused_node->selected = 0;
+ return 1;
+ case XK_Escape:
+ if (path_is_folder(get_fb(focused_window)->file_path))
+ focused_window->mode = WB_FILE_BROWSER;
+ else
+ focused_window->mode = WB_NORMAL;
+
+ writef_to_status_bar("Quit");
+ return 1;
+ }
+ return 0;
+}
+
+static void
+search_for_buffer_string_insert(const char* buf, int buflen)
+{
+ int len = strlen(focused_node->search);
+ if (buflen + len + 1 > SEARCH_TERM_MAX_LEN)
+ return;
+
+ if (buf[0] >= 32 || buflen > 1) {
+ memcpy(focused_node->search + len, buf, buflen);
+ focused_node->search[len + buflen] = 0;
+ focused_node->selected = 0;
+ } else {
+ writef_to_status_bar("unhandled control character 0x%x\n", buf[0]);
+ }
+}
+
+int
+file_buffer_keypress_override_callback(int* skip_keypress_callback, struct window_split_node* wn, KeySym ksym, int modkey, const char* buf, int len)
+{
+ soft_assert(wn->wb.mode < WB_MODES_END, return 1;);
+ if (wn->wb.mode != WB_FILE_BROWSER) return 0;
+
+ if (file_browser_actions(ksym, modkey)) {
+ *skip_keypress_callback = 1;
+ return 1;
+ }
+ file_browser_string_insert(buf, len);
+ *skip_keypress_callback = 1;
+ return 1;
+}
+
+int
+choose_one_of_selection_keypress_override_callback(int* skip_keypress_callback, struct window_split_node* wn, KeySym ksym, int modkey, const char* buf, int len)
+{
+ soft_assert(wn->wb.mode < WB_MODES_END, return 1;);
+ if (wn->wb.mode != WB_SEARCH_FOR_BUFFERS &&
+ wn->wb.mode != WB_SEARCH_KEYWORD_ALL_BUFFERS)
+ return 0;
+
+ if (search_for_buffer_actions(ksym, modkey)) {
+ *skip_keypress_callback = 1;
+ return 1;
+ }
+ search_for_buffer_string_insert(buf, len);
+ *skip_keypress_callback = 1;
+ return 1;
+}
+
+#endif // CHOOSE_ONE_OF_SELECTION_H_
diff --git a/normal_config.def.c b/normal_config.def.c
@@ -0,0 +1,245 @@
+#include "config.h"
+
+////////////////////////////////////////
+// apperance
+//
+
+// font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html
+//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;
+
+// default size of the editor
+unsigned int default_cols = 80;
+unsigned int default_rows = 24;
+
+// Kerning / character bounding-box multipliers
+float cw_scale = 1.0;
+float ch_scale = 1.0;
+
+int wrap_buffer = 0;
+
+// spaces per tab (tabs will self align)
+unsigned int tabspaces = 8;
+
+// Default shape of cursor
+// 2: Block ("█")
+// 4: Underline ("_")
+// 6: Bar ("|")
+unsigned int cursor_shape = 2;
+
+// thickness of underline and bar cursors
+unsigned int cursor_thickness = 2;
+
+///////////////////////////////////////////
+// color scheme
+// the syntax highlighting is applied in one pass,
+// so you can't have nested syntax highlighting
+//
+
+#include "plugins/color_schemes/gruvbox.h"
+
+// disable coloring functions for the syntax schemes below
+#undef function_color
+
+#include "plugins/syntax/c.h"
+
+const struct syntax_scheme syntax_schemes[] = {
+ {".c", c_word_seperators, c_syntax, LEN(c_syntax)},
+ {".h", c_word_seperators, c_syntax, LEN(c_syntax)},
+ {0},
+};
+
+/////////////////////////////////////////
+// Shortcuts
+//
+
+#include "plugins/shortcuts.h"
+#include "plugins/default_shortcuts.h"
+
+#define MODKEY Mod1Mask
+#define CtrlShift (ControlMask|ShiftMask)
+
+shortcuts = {
+// mask keysym function argument
+ { 0, XK_Right, cursor_move_x_relative, {.i = +1} },
+ { 0, XK_Left, cursor_move_x_relative, {.i = -1} },
+ { 0, XK_Down, cursor_move_y_relative, {.i = +1} },
+ { 0, XK_Up, cursor_move_y_relative, {.i = -1} },
+ { ControlMask, XK_Right, window_change, {.i = MOVE_RIGHT} },
+ { ControlMask, XK_Left, window_change, {.i = MOVE_LEFT} },
+ { ControlMask, XK_Down, window_change, {.i = MOVE_DOWN} },
+ { ControlMask, XK_Up, window_change, {.i = MOVE_UP} },
+ { CtrlShift, XK_Right, window_resize, {.i = MOVE_RIGHT} },
+ { CtrlShift, XK_Left, window_resize, {.i = MOVE_LEFT} },
+ { CtrlShift, XK_Down, window_resize, {.i = MOVE_DOWN} },
+ { CtrlShift, XK_Up, window_resize, {.i = MOVE_UP} },
+ { ControlMask, XK_Tab, swap_to_next_file_buffer, {0} },
+ { ControlMask, XK_m, toggle_selection, {0} },
+ { ControlMask, XK_g, move_cursor_to_offset, {0} },
+ { CtrlShift, XK_G, move_cursor_to_end_of_buffer, {0} },
+ { ControlMask, XK_period, open_file_browser, {0} },
+ { CtrlShift, 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_z, undo, {0} },
+ { CtrlShift, XK_Z, redo, {0} },
+ { ControlMask, XK_s, save_buffer, {0} },
+ { ControlMask, XK_f, search, {0} },
+ { CtrlShift, XK_F, search_keyword_in_buffers,{0},},
+ { ControlMask, XK_space, search_for_buffer,{0}, },
+ { ControlMask, XK_n, search_next, {0} },
+ { CtrlShift, XK_N, search_previous,{0} },
+ { ControlMask, XK_c, clipboard_copy, {0} },
+ { ControlMask, XK_v, clipboard_paste,{0} },
+ { CtrlShift, XK_Prior, zoom, {.f = +1} },
+ { CtrlShift, XK_Next, zoom, {.f = -1} },
+ { CtrlShift, XK_Home, zoomreset, {.f = 0} },
+ { CtrlShift, XK_Num_Lock, numlock, {.i = 0} },
+};
+
+/////////////////////////////////////////////////
+// callbacks
+//
+
+static void cursor_callback(struct window_buffer* buf, enum cursor_reason callback_reason);
+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 int keypress_actions(KeySym keysym, int modkey);
+static void string_insert_callback(const char* buf, int buflen);
+#include "plugins/default_status_bar.h"
+
+void(*cursor_movement_callback)(struct window_buffer*, enum cursor_reason) = cursor_callback;
+void(*buffer_contents_updated)(struct file_buffer*, int, enum buffer_content_reason) = buffer_add_to_undo;
+int(*keypress_callback)(KeySym, int) = keypress_actions;
+void(*string_input_callback)(const char*, int) = string_insert_callback;
+void(*draw_callback)(void) = NULL;
+void(*startup_callback)(void) = NULL;
+void(*buffer_written_to_screen_callback)(struct window_buffer* buf, int offset_start, int offset_end, int minx, int miny, int maxx, int maxy) = NULL;
+char*(*new_line_draw)(struct window_buffer* buf, int y, int lines_left, int minx, int maxx, Glyph* attr) = NULL;
+void(*buffer_written_to_file_callback)(struct file_buffer* fb) = NULL;
+int(*write_status_bar)(struct window_buffer* buf, int minx, int maxx, int cx, int cy, char line[LINE_MAX_LEN], Glyph* g) = default_status_bar;
+
+void
+keep_cursor_col(struct window_buffer* buf, enum cursor_reason callback_reason)
+{
+ 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);
+ }
+}
+
+void move_selection(struct window_buffer* buf, enum cursor_reason callback_reason)
+{
+ struct file_buffer* fb = get_file_buffer(buf);
+ if (fb->mode & BUFFER_SELECTION_ON) {
+ fb->s2o = buf->cursor_offset;
+ }
+}
+
+void
+cursor_callback(struct window_buffer* buf, enum cursor_reason callback_reason)
+{
+ keep_cursor_col(buf, callback_reason);
+ move_selection(buf, callback_reason);
+
+ //writef_to_status_bar("moved to: %d | reason: %d\n", buf->cursor_offset, callback_reason);
+}
+
+int
+keypress_actions(KeySym keysym, int modkey)
+{
+ check_shortcuts(keysym, modkey);
+
+ // default actions
+
+ int offset = focused_window->cursor_offset;
+ struct file_buffer* fb = get_file_buffer(focused_window);
+
+ switch (keysym) {
+ int move;
+ case XK_BackSpace:
+ if (delete_selection(fb)) return 1;
+ if (offset <= 0) return 1;
+
+ if (fb->contents[offset-1] == '\n')
+ buffer_move_lines(focused_window, -1, CURSOR_COMMAND_MOVEMENT);
+ else
+ buffer_move_on_line(focused_window, -1, CURSOR_COMMAND_MOVEMENT);
+
+ offset = focused_window->cursor_offset;
+
+ goto skip_delete_remove_selection;
+ case XK_Delete:
+
+ if (delete_selection(fb)) return 1;
+skip_delete_remove_selection:
+
+ move = buffer_remove(fb, offset, 1, 0, 0);
+ 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:
+ fb->mode &= ~BUFFER_SEARCH_BLOCKING_MASK;
+ fb->mode &= ~BUFFER_SEARCH_NON_BLOCKING;
+ fb->mode &= ~BUFFER_SEARCH_NON_BLOCKING_BACKWARDS;
+ fb->mode &= ~BUFFER_SEARCH_BLOCKING_BACKWARDS;
+ writef_to_status_bar("");
+ return 1;
+ case XK_Return:
+ delete_selection(fb);
+
+ buffer_insert(fb, "\n", 1, offset, 0);
+ window_move_all_cursors_on_same_buf(&root_node, NULL, focused_window->buffer_index, offset,
+ buffer_move_offset_relative, 1, CURSOR_COMMAND_MOVEMENT);
+ window_move_all_yscrolls(&root_node, focused_node, focused_window->buffer_index, offset, 1);
+ return 1;
+ case XK_Home: {
+ int new_offset = buffer_seek_char_backwards(fb, offset, '\n');
+ if (new_offset < 0)
+ new_offset = 0;
+ buffer_move_to_offset(focused_window, new_offset, CURSOR_COMMAND_MOVEMENT);
+ return 1;
+ }
+ case XK_End: {
+ int new_offset = buffer_seek_char(fb, offset, '\n');
+ if (new_offset < 0)
+ new_offset = fb->len-1;
+ buffer_move_to_offset(focused_window, new_offset, CURSOR_COMMAND_MOVEMENT);
+ return 1;
+ }
+ case XK_Page_Down:
+ buffer_move_lines(focused_window, (focused_node->maxy - focused_node->miny) / 2, 0);
+ buffer_move_to_x(focused_window, focused_window->cursor_col, CURSOR_UP_DOWN_MOVEMENT);
+ focused_window->y_scroll += (focused_node->maxy - focused_node->miny) / 2;
+ return 1;
+ case XK_Page_Up:
+ buffer_move_lines(focused_window, -((focused_node->maxy - focused_node->miny) / 2), 0);
+ buffer_move_to_x(focused_window, focused_window->cursor_col, CURSOR_UP_DOWN_MOVEMENT);
+ focused_window->y_scroll -= (focused_node->maxy - focused_node->miny) / 2;
+ return 1;
+ case XK_Tab:
+ buffer_insert(fb, "\t", 1, offset, 0);
+ window_move_all_cursors_on_same_buf(&root_node, NULL, focused_window->buffer_index, offset,
+ buffer_move_on_line, 1, CURSOR_COMMAND_MOVEMENT);
+ return 1;
+ }
+ return 0;
+}
+
+void string_insert_callback(const char* buf, int buflen)
+{
+ struct file_buffer* fb = get_file_buffer(focused_window);
+
+ 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,
+ buffer_move_offset_relative, buflen, CURSOR_COMMAND_MOVEMENT);
+ } else {
+ writef_to_status_bar("unhandled control character 0x%x\n", buf[0]);
+ }
+}
diff --git a/plugins/color_schemes/gruvbox.h b/plugins/color_schemes/gruvbox.h
@@ -1,66 +0,0 @@
-// 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
@@ -1,101 +0,0 @@
-#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
@@ -1,20 +0,0 @@
-#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
@@ -3,12 +3,11 @@
/*
** This file mainly contains the functionality of handling the
** "buffer". There should a good amount of customisation you
-** cand do wihtout tuching this file, but adding or changing
+** can do wihtout tuching this file, but adding or changing
** functionality to fit your needs shouldn't be too hard.
*/
#include <errno.h>
-#include <assert.h>
#include <ctype.h>
#include <stdarg.h>
#include <time.h>
@@ -16,1513 +15,103 @@
#include "se.h"
#include "x.h"
+#include "config.h"
+#include "extension.h"
-///////////////////////////////////////////
-// config.c variables and globals
-//
-
-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;
-
-extern unsigned int tabspaces;
-extern int wrap_buffer;
-extern const struct color_scheme color_schemes[];
-
-// x.c globals
-extern struct window_buffer* focused_window;
-
-// se.c globals
-Term term;
-static Glyph global_attr;
-static const uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
-static const uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
-
-// callbacks
-extern void(*cursor_movement_callback)(struct window_buffer*, enum cursor_reason); // buffer, current_pos, reason
-extern void(*buffer_contents_updated)(struct file_buffer*, int, enum buffer_content_reason); // modified buffer, current_pos
-extern void(*buffer_written_to_screen_callback)(struct window_buffer*, int, int, int, int, int, int); // drawn buffer, offset start & end, min x & y, max x & y
-// TODO: planned callbacks:
-// buffer written
-
-/////////////////////////////////////////////
-// Internal functions
-//
-
-static void buffer_draw_to_screen(struct window_split_node* wn, int minx, int miny, int maxx, int maxy);
-static void choose_one_of_selection(const char* prefix, const char* search, const char* err,
- const char*(*get_next_element)(const char*, const char*, int* offset, Glyph* attr, void* data),
- int* selected_line, int minx, int miny, int maxx, int maxy, int focused);
-static void draw_dir(struct window_split_node* win, int minx, int miny, int maxx, int maxy, int focused);
-static void draw_search_buffers(struct window_split_node* wn, int minx, int miny, int maxx, int maxy, int focused);
-static void draw_search_keyword_in_all_buffers(struct window_split_node* wn, int minx, int miny, int maxx, int maxy, int focused);
-void(*alternate_buffer_modes[WINDOW_BUFFER_MODE_LEN])
-(struct window_split_node* wn, int minx, int miny, int maxx, int maxy, int focused) = {
- [WINDOW_BUFFER_NORMAL] = NULL,
- [WINDOW_BUFFER_FILE_BROWSER] = draw_dir,
- [WINDOW_BUFFER_SEARCH_BUFFERS] = draw_search_buffers,
- [WINDOW_BUFFER_KEYWORD_ALL_BUFFERS] = draw_search_keyword_in_all_buffers
-};
-
-static void recursive_mkdir(char* path);
-static int writef_string(int y, int x1, int x2, const char* fmt, ...);
-static void color_selection(Glyph* letter);
-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);
-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
-//
-
-void *
-xmalloc(size_t len)
-{
- void *p;
-
- if (!(p = malloc(len)))
- die("malloc: %s\n", strerror(errno));
-
- return p;
-}
-
-void *
-xrealloc(void *p, size_t len)
-{
- if ((p = realloc(p, len)) == NULL)
- die("realloc: %s\n", strerror(errno));
-
- return p;
-}
-
-size_t
-utf8decode(const char *c, Rune *u, size_t clen)
-{
- size_t i, j, len, type;
- Rune udecoded;
-
- *u = UTF_INVALID;
- if (!clen)
- return 0;
- udecoded = utf8decodebyte(c[0], &len);
- if (!BETWEEN(len, 1, UTF_SIZ))
- return 1;
- for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
- udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);
- if (type != 0)
- return j;
- }
- if (j < len)
- return 0;
- *u = udecoded;
- utf8validate(u, len);
-
- return len;
-}
-
-Rune
-utf8decodebyte(char c, size_t *i)
-{
- for (*i = 0; *i < LEN(utfmask); ++(*i))
- if (((uchar)c & utfmask[*i]) == utfbyte[*i])
- return (uchar)c & ~utfmask[*i];
- return 0;
-}
-
-size_t
-utf8encode(Rune u, char *c)
-{
- size_t len, i;
-
- len = utf8validate(&u, 0);
- if (len > UTF_SIZ)
- return 0;
-
- for (i = len - 1; i != 0; --i) {
- c[i] = utf8encodebyte(u, 0);
- u >>= 6;
- }
- c[0] = utf8encodebyte(u, len);
-
- return len;
-}
-
-char
-utf8encodebyte(Rune u, size_t i)
-{
- return utfbyte[i] | (u & ~utfmask[i]);
-}
-
-size_t
-utf8validate(Rune *u, size_t i)
-{
- const Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000};
- const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
-
- if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
- *u = UTF_INVALID;
- for (i = 1; *u > utfmax[i]; ++i)
- ;
-
- return i;
-}
-
-void
-die(const char *errstr, ...)
-{
- va_list ap;
-
- va_start(ap, errstr);
- vfprintf(stderr, errstr, ap);
- va_end(ap);
- exit(1);
-}
-
-void
-tnew(int col, int row)
-{
- global_attr = default_attributes;
-
- term = (Term){0};
- tresize(col, row);
-}
-
-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)) {
- wb.mode = WINDOW_BUFFER_FILE_BROWSER;
- writef_to_status_bar("opened file browser %s", get_file_buffer(&wb)->file_path);
- }
-
- return wb;
-}
-
-int
-buffer_seek_char(const struct file_buffer* buf, int offset, char byte)
-{
- if (offset > buf->len) return -1;
- char* new_buf = memchr(buf->contents + offset, byte, buf->len - offset);
- if (!new_buf) return -1;
- return new_buf - buf->contents;
-}
-
-int
-buffer_seek_char_backwards(const struct file_buffer* buf, int offset, char byte)
-{
- LIMIT(offset, 0, buf->len-1);
- for (int n = offset-1; n >= 0; n--) {
- if (buf->contents[n] == byte) {
- return n+1;
- }
- }
- return -1;
-}
-
-int
-buffer_seek_string(const struct file_buffer* buf, int offset, const char* string)
-{
- LIMIT(offset, 0, buf->len-1);
- int str_len = strlen(string);
-
- for (int n = offset; n < buf->len - str_len; n++)
- if (!memcmp(buf->contents + n, string, str_len))
- return n;
- return -1;
-}
-
-int
-buffer_seek_string_backwards(const struct file_buffer* buf, int offset, const char* string)
-{
- int str_len = strlen(string);
- offset += str_len;
- LIMIT(offset, 0, buf->len-1);
-
- for (int n = offset - str_len; n >= 0; n--)
- if (!memcmp(buf->contents + n, string, str_len))
- return n;
- return -1;
-}
-
-int
-buffer_seek_string_wrap(const struct window_buffer* wb, int offset, const char* search)
-{
- struct file_buffer* fb = get_file_buffer(focused_window);
- if (*search == 0 || !buffer_count_string_instances(fb, search, 0, NULL))
- return -1;
-
- int new_offset = buffer_seek_string(fb, offset, search);
- if (new_offset < 0)
- new_offset = buffer_seek_string(fb, 0, search);
-
- if (!(fb->mode & BUFFER_SEARCH_BLOCKING))
- fb->mode |= BUFFER_SEARCH_BLOCKING_IDLE;
- return new_offset;
-}
-
-int
-buffer_seek_string_wrap_backwards(const struct window_buffer* wb, int offset, const char* search)
-{
- struct file_buffer* fb = get_file_buffer(focused_window);
- if (*search == 0 || !buffer_count_string_instances(fb, search, 0, NULL))
- return -1;
-
- int new_offset = buffer_seek_string_backwards(fb, offset, search);
- if (new_offset < 0)
- new_offset = buffer_seek_string_backwards(fb, fb->len, search);
-
- if (!(fb->mode & BUFFER_SEARCH_BLOCKING))
- fb->mode |= BUFFER_SEARCH_BLOCKING_IDLE;
- return new_offset;
-}
-
-int
-buffer_seek_string_wrap_backwards_ignore_case(const struct window_buffer* wb, int offset, const char* search);
-
-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;
-}
-
-int
-buffer_count_string_instances(const struct file_buffer* fb, const char* string, int offset, int* before_offset)
-{
- int tmp;
- if (!before_offset)
- before_offset = &tmp;
- if (!string || *string == 0) {
- *before_offset = 0;
- return 0;
- }
-
- int pos = -1;
- int count = 0;
- int once = 1;
- while((pos = buffer_seek_string(fb, pos+1, string)) >= 0) {
- if (once && pos > offset) {
- *before_offset = count;
- once = 0;
- }
- count++;
- }
- if (once)
- *before_offset = count;
- return count;
-}
-
-void
-buffer_move_on_line(struct window_buffer* buf, int amount, enum cursor_reason callback_reason)
-{
- const struct file_buffer* fb = get_file_buffer((buf));
- if (fb->len <= 0)
- return;
-
- if (amount < 0) {
- /*
- ** we cant get the size of a utf8 char backwards
- ** therefore we move all the way to the start of the line,
- ** then a seeker will try to find the cursor pos
- ** the follower will then be *amount* steps behind,
- ** when the seeker reaches the cursor
- ** the follower will be the new cursor position
- */
-
- int line_start = buffer_seek_char_backwards(fb, buf->cursor_offset, '\n');
- amount = abs(MAX(line_start - buf->cursor_offset, amount));
- assert(amount < 2048);
-
- char moves[amount];
- int seek_pos = line_start, follower_pos = line_start;
- int n = 0;
- while (seek_pos < buf->cursor_offset) {
- Rune u;
- int charsize = t_decode_utf8_buffer(fb->contents + seek_pos, fb->len - seek_pos, &u);
- seek_pos += charsize;
- if (n < amount) {
- moves[n++] = charsize;
- } else {
- follower_pos += moves[0];
- memmove(moves, moves + 1, amount - 1);
- moves[amount - 1] = charsize;
- }
- }
- buf->cursor_offset = follower_pos;
-
- LIMIT(buf->cursor_offset, 0, fb->len);
- } else if (amount > 0) {
- for (int charsize = 0;
- buf->cursor_offset < fb->len && amount > 0 && fb->contents[buf->cursor_offset + charsize] != '\n';
- buf->cursor_offset += charsize, amount--) {
- Rune u;
- charsize = t_decode_utf8_buffer(fb->contents + buf->cursor_offset, fb->len - buf->cursor_offset, &u);
- if (u != '\n' && u != '\t')
- if (wcwidth(u) <= 0)
- amount++;
- if (buf->cursor_offset + charsize > fb->len)
- break;
- }
- }
-
- if (callback_reason && cursor_movement_callback)
- cursor_movement_callback(buf, callback_reason);
-}
-
-void
-buffer_move_offset_relative(struct window_buffer* buf, int amount, enum cursor_reason callback_reason)
-{
- //NOTE: this does not check if the character on this offset is the start of a valid utf8 char
- const struct file_buffer* fb = get_file_buffer((buf));
- if (fb->len <= 0)
- return;
- buf->cursor_offset += amount;
- LIMIT(buf->cursor_offset, 0, fb->len);
-
- if (callback_reason && cursor_movement_callback)
- cursor_movement_callback(buf, callback_reason);
-}
-
-void
-buffer_move_lines(struct window_buffer* buf, int amount, enum cursor_reason callback_reason)
-{
- const struct file_buffer* fb = get_file_buffer((buf));
- if (fb->len <= 0)
- return;
- int offset = buf->cursor_offset;
- if (amount > 0) {
- while (amount-- && offset >= 0) {
- int new_offset = buffer_seek_char(fb, offset, '\n');
- if (new_offset < 0) {
- offset = fb->len;
- break;
- }
- offset = new_offset+1;
- }
- } else if (amount < 0) {
- while (amount++ && offset >= 0)
- offset = buffer_seek_char_backwards(fb, offset, '\n')-1;
- }
- buffer_move_to_offset(buf, offset, callback_reason);
-}
-
-void
-buffer_move_to_offset(struct window_buffer* buf, int offset, enum cursor_reason callback_reason)
-{
- //NOTE: this does not check if the character on this offset is the start of a valid utf8 char
- const struct file_buffer* fb = get_file_buffer((buf));
- if (fb->len <= 0)
- return;
- LIMIT(offset, 0, fb->len);
- buf->cursor_offset = offset;
-
- if (callback_reason && cursor_movement_callback)
- cursor_movement_callback(buf, callback_reason);
-}
-
-void
-buffer_move_to_x(struct window_buffer* buf, int x, enum cursor_reason callback_reason)
-{
- assert(buf);
- struct file_buffer* fb = get_file_buffer(buf);
-
- int offset = buffer_seek_char_backwards(fb, buf->cursor_offset, '\n');
- if (offset < 0)
- offset = 0;
- buffer_move_to_offset(buf, offset, 0);
-
- int x_counter = 0;
-
- while (offset < fb->len) {
- if (fb->contents[offset] == '\t') {
- offset++;
- if (x_counter <= 0) x_counter += 1;
- while (x_counter % tabspaces != 0) x_counter += 1;
- x_counter += 1;
- continue;
- } else if (fb->contents[offset] == '\n') {
- break;
- }
- Rune u = 0;
- int charsize = t_decode_utf8_buffer(fb->contents + offset, fb->len - offset, &u);
- x_counter += wcwidth(u);
- if (x_counter <= x) {
- offset += charsize;
- if (x_counter == x)
- break;
- } else {
- break;
- }
- }
- buffer_move_to_offset(buf, offset, callback_reason);
-}
-
-void
-window_node_split(struct window_split_node* parent, float ratio, enum window_split_mode mode)
-{
- assert(parent);
- assert(parent->mode == WINDOW_SINGULAR);
- assert(mode != WINDOW_SINGULAR);
-
- parent->node1 = xmalloc(sizeof(struct window_split_node));
- parent->node2 = xmalloc(sizeof(struct window_split_node));
- parent->node1->search = xmalloc(SEARCH_TERM_MAX_LEN);
- parent->node2->search = xmalloc(SEARCH_TERM_MAX_LEN);
-
- *parent->node1 = *parent;
- *parent->node2 = *parent;
- parent->node1->parent = parent;
- parent->node2->parent = parent;
-
- parent->node1->node1 = NULL;
- parent->node1->node2 = NULL;
- parent->node2->node1 = NULL;
- parent->node2->node2 = NULL;
-
- parent->mode = mode;
- parent->ratio = ratio;
- parent->window = (struct window_buffer){0};
-}
-
-struct window_split_node*
-window_node_delete(struct window_split_node* node)
-{
- 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;
- free(old->search);
- free(old);
-
- struct window_split_node* parent = node->parent;
- *node = *other;
- if (other->mode != WINDOW_SINGULAR) {
- other->node1->parent = node;
- other->node2->parent = node;
- }
- free(other);
- node->parent = parent;
-
- return node;
-}
-
-void
-window_draw_tree_to_screen(struct window_split_node* root, int minx, int miny, int maxx, int maxy)
-{
- assert(root);
-
- if (root->mode == WINDOW_SINGULAR) {
- buffer_draw_to_screen(root, minx, miny, maxx, maxy);
- } else if (root->mode == WINDOW_HORISONTAL) {
- int middlex = ((float)(maxx - minx) * root->ratio) + minx;
-
- // print seperator
- tsetregion(middlex+1, miny, middlex+1, maxy, L'│');
-
- window_draw_tree_to_screen(root->node1, minx, miny, middlex, maxy);
- window_draw_tree_to_screen(root->node2, middlex+2, miny, maxx, maxy);
-
- for (int y = miny; y < maxy+1; y++)
- xdrawline(middlex+1, y, middlex+2);
- } else if (root->mode == WINDOW_VERTICAL) {
- int middley = ((float)(maxy - miny) * root->ratio) + miny;
-
- window_draw_tree_to_screen(root->node1, minx, miny, maxx, middley);
- window_draw_tree_to_screen(root->node2, minx, middley, maxx, maxy);
- }
-}
-
-void
-window_move_all_cursors_on_same_buf(struct window_split_node* root, struct window_split_node* excluded, int buf_index, int offset,
- void(movement)(struct window_buffer*, int, enum cursor_reason),
- int move, enum cursor_reason reason)
-{
- if (root->mode == WINDOW_SINGULAR) {
- if (root->window.buffer_index == buf_index && root->window.cursor_offset >= offset && root != excluded)
- movement(&root->window, move, reason);
- } else {
- window_move_all_cursors_on_same_buf(root->node1, excluded, buf_index, offset, movement, move, reason);
- window_move_all_cursors_on_same_buf(root->node2, excluded, buf_index, offset, movement, move, reason);
- }
-}
-
-void
-window_move_all_yscrolls(struct window_split_node* root, struct window_split_node* excluded, int buf_index, int offset, int move)
-{
- if (root->mode == WINDOW_SINGULAR) {
- if (root->window.buffer_index == buf_index && root->window.cursor_offset >= offset && root != excluded)
- root->window.y_scroll += move;
- } else {
- window_move_all_yscrolls(root->node1, excluded, buf_index, offset, move);
- window_move_all_yscrolls(root->node2, excluded, buf_index, offset, move);
- }
-}
-
-int
-window_other_nodes_contain_file_buffer(struct window_split_node* node, struct window_split_node* root)
-{
- if (root->mode == WINDOW_SINGULAR)
- return (root->window.buffer_index == node->window.buffer_index && root != node);
-
- return (window_other_nodes_contain_file_buffer(node, root->node1) ||
- window_other_nodes_contain_file_buffer(node, root->node2));
-}
-
-int
-is_correct_mode(enum window_split_mode mode, enum move_directons move)
-{
- if (move == MOVE_RIGHT || move == MOVE_LEFT)
- return (mode == WINDOW_HORISONTAL);
- if (move == MOVE_UP || move == MOVE_DOWN)
- return (mode == WINDOW_VERTICAL);
- return 0;
-}
-
-struct window_split_node*
-window_switch_to_window(struct window_split_node* node, enum move_directons move)
-{
- assert(node);
- if (!node->parent) return node;
- assert(node->mode == WINDOW_SINGULAR);
- struct window_split_node* old_node = node;
-
- if (move == MOVE_RIGHT || move == MOVE_DOWN) {
- // traverse up the tree to the right
- for (; node->parent; node = node->parent) {
- if (is_correct_mode(node->parent->mode, move) && node->parent->node1 == node) {
- // traverse down until a screen is found
- node = node->parent->node2;
- while(node->mode != WINDOW_SINGULAR)
- node = node->node1;
-
- return node;
- }
- }
- } else if (move == MOVE_LEFT || move == MOVE_UP) {
- // traverse up the tree to the left
- for (; node->parent; node = node->parent) {
- if (is_correct_mode(node->parent->mode, move) && node->parent->node2 == node) {
- // traverse down until a screen is found
- node = node->parent->node1;
- while(node->mode != WINDOW_SINGULAR)
- node = node->node2;
-
- return node;
- }
- }
- }
-
- return old_node;
-}
-
-void
-window_node_resize(struct window_split_node* node, enum move_directons move, float amount)
-{
- for (; node; node = node->parent) {
- if (is_correct_mode(node->mode, move)) {
- float amount = (move == MOVE_RIGHT || move == MOVE_LEFT) ? 0.1f : 0.05f;
- if (move == MOVE_RIGHT || move == MOVE_DOWN) amount = -amount;
- node->ratio -= amount;
- LIMIT(node->ratio, 0.001f, 0.95f);
- return;
- }
- }
-}
-
-void
-window_node_resize_absolute(struct window_split_node* node, enum move_directons move, float amount)
-{
- for (; node; node = node->parent) {
- if (is_correct_mode(node->mode, move)) {
- node->ratio = amount;
- LIMIT(node->ratio, 0.001f, 0.95f);
- return;
- }
- }
-}
-
-int
-path_is_folder(const char* path) {
- struct stat statbuf;
- if (stat(path, &statbuf) != 0)
- return 0;
- return S_ISDIR(statbuf.st_mode);
-}
-
-// result must be freed
-char*
-file_path_get_path(const char* path)
-{
- assert(path);
-
- const char* folder_start = strrchr(path, '/');
- if (!folder_start)
- folder_start = path;
- else
- folder_start++;
- int folder_len = folder_start - path;
- char* folder = xmalloc(folder_len + 1);
-
- memcpy(folder, path, folder_len);
- folder[folder_len] = '\0';
-
- return folder;
-}
-
-const char*
-file_browser_next_item(const char* path, const char* search, int* offset, Glyph* attr, void* data)
-{
- static char filename_search[PATH_MAX];
- static char filename[PATH_MAX];
- static char full_path[PATH_MAX];
- static DIR* dir;
- if (!path || !search) {
- if (dir) {
- closedir(dir);
- dir = NULL;
- }
- return NULL;
- }
- if (!dir)
- dir = opendir(path);
- int len = strlen(search);
-
- struct dirent *folder;
- while((folder = readdir(dir))) {
- strcpy(filename, folder->d_name);
- strcpy(full_path, path);
- strcat(full_path, filename);
- if (path_is_folder(full_path)) {
- strcat(filename, "/");
- if (attr)
- attr->fg = path_color;
- } else {
- if (attr)
- attr->fg = default_attributes.fg;
- }
-
- strcpy(filename_search, filename);
- 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, "./") == 0 || strcmp(filename, "../") == 0)
- break;
- if (offset)
- *offset = search_start - filename_search;
- return filename;
- }
- search_start++;
- }
- }
- *filename = *full_path = 0;
- closedir(dir);
- dir = NULL;
- return NULL;
-}
-
-const char*
-buffer_search_next_item(const char* tmp, const char* search, int* offset, Glyph* attr, void* data)
-{
- static struct window_buffer wb;
- static char file_path[PATH_MAX];
- static int quit_next = 0;
- if (!tmp || !search) {
- wb.buffer_index = 0;
- quit_next = 0;
- return NULL;
- }
-
- int len = strlen(search);
- for(;;) {
- if (quit_next) {
- wb.buffer_index = 0;
- quit_next = 0;
- return NULL;
- }
- struct file_buffer* fb = get_file_buffer(&wb);
- int last_buffer_index = wb.buffer_index;
- wb.buffer_index++;
- get_file_buffer(&wb);
- if (wb.buffer_index <= last_buffer_index)
- quit_next = 1;
-
- strcpy(file_path, fb->file_path);
-
- const char* s_repl = search;
- while(!isupper(*s_repl++)) {
- if (*s_repl == 0) {
- for (char* fs = file_path; *fs; fs++)
- *fs = tolower(*fs);
- break;
- }
- }
- char* search_start = file_path;
-
- if (!len)
- goto search_match;
- while ((search_start = strchr(search_start+1, *search))) {
- if (memcmp(search_start, search, len) == 0) {
- search_match:
- if (offset)
- *offset = search_start - file_path;
- if (data)
- *(int*)data = last_buffer_index;
- return fb->file_path;
- }
- }
- }
-}
-
-const char*
-buffers_search_keyword_next_item(const char* tmp, const char* search, int* offset, Glyph* attr, void* data)
-{
- static char item[2048];
- static struct window_buffer wb;
- static int pos = -1;
- if (!tmp || !search) {
- wb.buffer_index = 0;
- pos = -1;
- return NULL;
- }
- int len = strlen(search);
- if (!len)
- return NULL;
-
- for (;;) {
- struct file_buffer* fb = get_file_buffer(&wb);
-
- if ((pos = buffer_seek_string(fb, pos+1, search)) >= 0) {
- const char* filename = strrchr(fb->file_path, '/')+1;
- if (!filename)
- filename = fb->file_path;
-
- int tmp, y;
- buffer_offset_to_xy(&wb, pos, 0, &tmp, &y);
-
- snprintf(item, 2048, "%s:%d: ", filename, y);
- int itemlen = strlen(item);
-
- char* line = buffer_get_line_at_offset(fb, pos);
- char* line_no_whitespace = line;
- if (!isspace(*search))
- while(isspace(*line_no_whitespace)) line_no_whitespace++;
-
- snprintf(item + itemlen, 2048 - itemlen, "%s", line_no_whitespace);
- free(line);
-
- int line_start = buffer_seek_char_backwards(fb, pos, '\n') + (line_no_whitespace - line);
- if (line_start < 0)
- line_start = 0;
- if (offset)
- *offset = (pos - line_start) + itemlen;
- if (data)
- *(struct keyword_pos*)data = (struct keyword_pos){.offset = pos, .buffer_index = wb.buffer_index};
- pos = buffer_seek_char(fb, pos+1, '\n');
- return item;
- } else {
- int last_buffer_index = wb.buffer_index;
- wb.buffer_index++;
- get_file_buffer(&wb);
- if (wb.buffer_index <= last_buffer_index)
- break;
- pos = -1;
- }
- }
- wb.buffer_index = 0;
- pos = -1;
- return NULL;
-}
-
-void
-choose_one_of_selection(const char* prefix, const char* search, const char* err,
- const char*(*get_next_element)(const char*, const char*, int* offset, Glyph* attr, void* data),
- int* selected_line, int minx, int miny, int maxx, int maxy, int focused)
-{
- assert(prefix);
- assert(search);
- assert(err);
- assert(selected_line);
- assert(get_next_element);
-
- // change background color
- global_attr.bg = alternate_bg_dark;
- tsetregion(minx, miny+1, maxx, maxy, ' ');
- global_attr = default_attributes;
- get_next_element(NULL, NULL, NULL, NULL, NULL);
-
- int len = strlen(search);
-
- // count folders to get scroll
- int folder_lines = maxy - miny - 2;
- int elements = 0;
- int tmp_offset;
- int limit = MAX(*selected_line, 999);
- while(get_next_element(prefix, search, &tmp_offset, NULL, NULL) && elements <= limit)
- elements++;
- get_next_element(NULL, NULL, NULL, NULL, NULL);
- *selected_line = MIN(*selected_line, elements-1);
- int sel_local = *selected_line;
-
- // print num of files
- char count[256];
- if (elements >= 1000)
- snprintf(count, sizeof(count), "[>999:%2d] ", *selected_line+1);
- else if (*selected_line > folder_lines)
- snprintf(count, sizeof(count), "^[%3d:%2d] ", elements, *selected_line+1);
- else if (elements-1 > folder_lines)
- snprintf(count, sizeof(count), "ˇ[%3d:%2d] ", elements, *selected_line+1);
- else
- snprintf(count, sizeof(count), " [%3d:%2d] ", elements, *selected_line+1);
-
- // print search term with prefix in front of it
- // prefix is in path_color
- global_attr.fg = path_color;
- int new_x = write_string(count, miny, minx, maxx+1);
- new_x = write_string(prefix, miny, new_x, maxx+1);
- global_attr = default_attributes;
- new_x = write_string(search, miny, new_x, maxx+1);
-
- // print elements
- int start_miny = miny;
- int offset;
- elements--;
- miny++;
- global_attr = default_attributes;
- global_attr.bg = alternate_bg_dark;
- const char* element;
- while(miny <= maxy && (element = get_next_element(prefix, search, &offset, &global_attr, NULL))) {
- if (elements > folder_lines && sel_local > folder_lines) {
- elements--;
- sel_local--;
- continue;
- }
- write_string(element, miny, minx, maxx+1);
-
- // change the color to highlight search term
- for (int i = minx + offset; i < minx + len + offset && i < maxx+1; 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++)
- tsetattr(i, miny)->bg = selection_bg;
- miny++;
- }
-
- if (elements < 0) {
- global_attr = default_attributes;
- global_attr.fg = warning_color;
- write_string(err, start_miny, new_x, maxx+1);
- }
-
- // draw
-
- for (int y = start_miny; y < maxy+1; y++)
- xdrawline(minx, y, maxx+1);
-
- draw_horisontal_line(maxy-1, minx, maxx);
- xdrawcursor(new_x, start_miny, focused);
-
- global_attr = default_attributes;
-}
-
-void
-draw_dir(struct window_split_node* wn, int minx, int miny, int maxx, int maxy, int focused)
-{
- struct file_buffer* fb = get_file_buffer(&wn->window);
- char* folder = file_path_get_path(fb->file_path);
-
- buffer_change(fb, "\0", 1, fb->len, 1);
- if (fb->len > 0) fb->len--;
-
- choose_one_of_selection(folder, fb->contents, " [Create New File]",
- file_browser_next_item, &wn->selected, minx, miny, maxx, maxy, focused);
-
- free(folder);
-}
-
-void
-draw_search_buffers(struct window_split_node* wn, int minx, int miny, int maxx, int maxy, int focused)
-{
- choose_one_of_selection("Find loaded buffer: ", wn->search, " [No resuts]",
- buffer_search_next_item, &wn->selected, minx, miny, maxx, maxy, focused);
-}
-
-void
-draw_search_keyword_in_all_buffers(struct window_split_node* wn, int minx, int miny, int maxx, int maxy, int focused)
-{
- choose_one_of_selection("Find in all buffers: ", wn->search, " [No resuts]",
- buffers_search_keyword_next_item, &wn->selected, minx, miny, maxx, maxy, focused);
-}
-
-void recursive_mkdir(char *path) {
- if (!path || !strlen(path))
- return;
- char *sep = strrchr(path, '/');
- if(sep) {
- *sep = '\0';
- recursive_mkdir(path);
- *sep = '/';
- }
- if(mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) && errno != EEXIST)
- 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, ' ');
- int x_end = 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);
- if (get_file_buffer(focused_window)->mode & BUFFER_SEARCH_BLOCKING)
- xdrawcursor(x_end, term.row-1, 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)
-{
- struct file_buffer buffer = {0};
- buffer.file_path = xmalloc(PATH_MAX);
-
- char* res = realpath(file_path, buffer.file_path);
- if (!res) {
- char* path = file_path_get_path(file_path);
- recursive_mkdir(path);
- free(path);
-
- FILE *new_file = fopen(file_path, "wb");
- 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)) {
- int len = strlen(buffer.file_path);
- if (buffer.file_path[len-1] != '/' && len < PATH_MAX-1) {
- buffer.file_path[len] = '/';
- buffer.file_path[len+1] = '\0';
- }
- buffer.len = 0;
- buffer.capacity = 100;
- buffer.contents = xmalloc(buffer.capacity);
- } else {
- FILE *file = fopen(buffer.file_path, "rb");
- fseek(file, 0L, SEEK_END);
- long readsize = ftell(file);
- rewind(file);
-
- if (readsize > (long)1.048576e+7) {
- fclose(file);
- die("you are opening a huge file(>10MiB), not allowed");
- }
-
- buffer.len = readsize;
- buffer.capacity = readsize + 100;
-
- buffer.contents = xmalloc(buffer.capacity);
- buffer.contents[0] = 0;
-
- char bom[4] = {0};
- fread(bom, 1, 3, file);
- if (strcmp(bom, "\xEF\xBB\xBF"))
- rewind(file);
- else
- buffer.mode |= BUFFER_UTF8_SIGNED;
- fread(buffer.contents, 1, readsize, file);
- fclose(file);
- }
-
- buffer.ub = xmalloc(sizeof(struct undo_buffer) * UNDO_BUFFERS_COUNT);
- buffer.search_term = xmalloc(SEARCH_TERM_MAX_LEN);
- buffer.non_blocking_search_term = xmalloc(SEARCH_TERM_MAX_LEN);
- memset(buffer.ub, 0, sizeof(struct undo_buffer) * UNDO_BUFFERS_COUNT);
- memset(buffer.search_term, 0, SEARCH_TERM_MAX_LEN);
- memset(buffer.non_blocking_search_term, 0, SEARCH_TERM_MAX_LEN);
-
- // change line endings
- int offset = 0;
- while((offset = buffer_seek_string(&buffer, offset, "\r\n")) >= 0)
- buffer_remove(&buffer, offset, 1, 1, 1);
- offset = 0;
- while((offset = buffer_seek_char(&buffer, offset, '\r')) >= 0)
- buffer_change(&buffer, "\n", 1, offset, 1);
+// se.c globals
- if (buffer_contents_updated)
- buffer_contents_updated(&buffer, 0, BUFFER_CONTENT_INIT);
+/////////////////////////////////////////////
+// Internal functions
+//
- if (res)
- writef_to_status_bar("new buffer %s", buffer.file_path);
- return buffer;
-}
+static int writef_string(int y, int x1, int x2, const char* fmt, ...);
+static void color_selection(struct glyph* letter);
-void
-buffer_destroy(struct file_buffer* fb)
-{
- free(fb->ub);
- free(fb->contents);
- free(fb->file_path);
- free(fb->search_term);
- free(fb->non_blocking_search_term);
- *fb = (struct file_buffer){0};
-}
+////////////////////////////////////////////
+// function implementations
+//
-void
-buffer_insert(struct file_buffer* buf, const char* new_content, const int len, const int offset, int do_not_callback)
+int
+writef_string(int y, int x1, int x2, const char* fmt, ...)
{
- assert(buf->contents);
- if (offset > buf->len || offset < 0) {
- fprintf(stderr, "writing past buf %s\n", buf->file_path);
- return;
- }
+ char string[STATUS_BAR_MAX_LEN];
- if (buf->len + len >= buf->capacity) {
- buf->capacity = buf->len + len + 256;
- buf->contents = xrealloc(buf->contents, buf->capacity);
- }
- if (offset < buf->len)
- memmove(buf->contents+offset+len, buf->contents+offset, buf->len-offset);
- buf->len += len;
+ va_list args;
+ va_start(args, fmt);
+ vsnprintf(string, STATUS_BAR_MAX_LEN, fmt, args);
+ va_end(args);
- memcpy(buf->contents+offset, new_content, len);
- if (buffer_contents_updated && !do_not_callback)
- buffer_contents_updated(buf, offset, BUFFER_CONTENT_NORMAL_EDIT);
+ return write_string(string, y, x1, x2);
}
+char status_bar_contents[STATUS_BAR_MAX_LEN] = {0};
+static int status_bar_end;
+uint32_t status_bar_bg;
void
-buffer_change(struct file_buffer* buf, const char* new_content, const int len, const int offset, int do_not_callback)
-{
- assert(buf->contents);
- if (offset > buf->len)
- die("writing past buf %s\n", buf->file_path);
-
- if (offset + len > buf->len) {
- buf->len = offset + len;
- if (buf->len >= buf->capacity) {
- buf->capacity = buf->len + len + 256;
- buf->contents = xrealloc(buf->contents, buf->capacity);
- }
- }
-
- memcpy(buf->contents+offset, new_content, len);
- if (buffer_contents_updated && !do_not_callback)
- buffer_contents_updated(buf, offset, BUFFER_CONTENT_NORMAL_EDIT);
-}
-
-int
-buffer_remove(struct file_buffer* buf, const int offset, int len, int do_not_calculate_charsize, int do_not_callback)
+writef_to_status_bar(const char* fmt, ...)
{
- assert(buf->contents);
- if (offset > buf->len)
- die("deleting past buffer (offset is %d len is %d)\n", offset, buf->len);
-
- int removed_len = 0;
- if (do_not_calculate_charsize) {
- removed_len = len;
- } else {
- while (len--) {
- int charsize = t_decode_utf8_buffer(buf->contents + offset, buf->len - offset, NULL);
- if (buf->len - charsize < 0)
- return 0;
- removed_len += charsize;
- }
- }
- buf->len -= removed_len;
- memmove(buf->contents+offset, buf->contents+offset+removed_len, buf->len-offset);
- if (buffer_contents_updated && !do_not_callback)
- buffer_contents_updated(buf, offset, BUFFER_CONTENT_NORMAL_EDIT);
- return removed_len;
-}
+ if (fmt) {
+ if (status_bar_bg == error_color ||
+ status_bar_bg == warning_color ||
+ status_bar_bg == ok_color)
+ return;
-void
-remove_utf8_string_end(char* string)
-{
- int line_start = 0;
+ va_list args;
+ va_start(args, fmt);
+ vsnprintf(status_bar_contents, STATUS_BAR_MAX_LEN, fmt, args);
+ va_end(args);
- int len = strlen(string);
- if (len <= 0)
+ status_bar_bg = alternate_bg_dark;
return;
- int move;
- int seek_pos = line_start, follower_pos = line_start;
- int n = 0;
- while (seek_pos < len) {
- Rune u;
- int charsize = t_decode_utf8_buffer(string + seek_pos, len - seek_pos, &u);
- seek_pos += charsize;
- if (!n) {
- move = charsize;
- n = 1;
- } else {
- follower_pos += move;
- move = charsize;
- }
}
- string[follower_pos] = 0;
-}
-int
-buffer_get_charsize(Rune u, int cur_x_pos)
-{
- if (u == '\t')
- return 8 - (cur_x_pos % tabspaces);
- if (u == '\n')
- return 0;
- return wcwidth(u);
+ global_attr = default_attributes;
+ global_attr.bg = status_bar_bg;
+ status_bar_end = write_string(status_bar_contents, screen.row-1, 0, screen.col);
+ screen_set_region(status_bar_end, screen.row-1, screen.col-1, screen.row-1, ' ');
+
+ global_attr = default_attributes;
}
void
-buffer_offset_to_xy(struct window_buffer* buf, int offset, int maxx, int* cx, int* cy)
+draw_status_bar()
{
- assert(buf);
- struct file_buffer* fb = get_file_buffer(buf);
-
- *cx = *cy = 0;
- if (fb->len <= 0)
- return;
- LIMIT(offset, 0, fb->len);
-
- char* repl = fb->contents;
- char* last = repl + offset;
-
- char* new_repl;
- if (wrap_buffer && maxx > 0) {
- int yscroll = 0;
- while ((new_repl = memchr(repl, '\n', last - repl))) {
- if (++yscroll >= buf->y_scroll)
- break;
- repl = new_repl+1;
- }
- *cy = yscroll - buf->y_scroll;
- } else {
- while ((new_repl = memchr(repl, '\n', last - repl))) {
- repl = new_repl+1;
- *cy += 1;
- }
- *cy -= buf->y_scroll;
- }
-
- while (repl < last) {
- if (wrap_buffer && maxx > 0 && (*repl == '\n' || *cx >= maxx)) {
- *cy += 1;
- *cx = 0;
- repl++;
- continue;
- }
- if (*repl == '\t') {
- repl++;
- if (*cx <= 0) *cx += 1;
- while (*cx % tabspaces != 0) *cx += 1;
- *cx += 1;
- continue;
- }
- Rune u;
- repl += t_decode_utf8_buffer(repl, last - repl, &u);
- *cx += wcwidth(u);
- }
+ writef_to_status_bar(NULL);
+ xdrawline(0, screen.row-1, screen.col);
+ draw_horisontal_line(screen.row-2, 0, screen.col-1);
+ if (get_fb(focused_window)->mode & FB_SEARCH_BLOCKING)
+ xdrawcursor(status_bar_end, screen.row-1, 1);
+ status_bar_bg = alternate_bg_dark;
}
void
-buffer_draw_to_screen(struct window_split_node* wn, int minx, int miny, int maxx, int maxy)
+window_node_draw_to_screen(struct window_split_node* wn)
{
- struct window_buffer* buf = &wn->window;
- assert(buf);
- struct file_buffer* fb = get_file_buffer(buf);
-
- LIMIT(maxx, 0, term.col-1);
- LIMIT(maxy, 0, term.row-1);
- LIMIT(minx, 0, maxx);
- LIMIT(miny, 0, maxy);
- LIMIT(buf->cursor_offset, 0, fb->len);
- tsetregion(minx, miny, maxx, maxy, ' ');
- int focused = buf == focused_window && !(fb->mode & BUFFER_SEARCH_BLOCKING);
+ struct window_buffer* wb = &wn->wb;
+ struct file_buffer* fb = get_fb(wb);
+ int minx = wn->minx, miny = wn->miny,
+ maxx = wn->maxx, maxy = wn->maxy;
- if (buf->mode > WINDOW_BUFFER_NORMAL) {
- // alternate buffer modes
- assert(buf->mode < WINDOW_BUFFER_MODE_LEN);
- alternate_buffer_modes[buf->mode](wn, minx, miny, maxx, maxy, focused);
- return;
- }
+ LIMIT(wb->cursor_offset, 0, fb->len);
+ screen_set_region(minx, miny, maxx, maxy, ' ');
+ int focused = wb == focused_window && !(fb->mode & FB_SEARCH_BLOCKING);
int x = minx, y = miny;
global_attr = default_attributes;
- do_color_scheme(NULL, &(struct color_scheme){0}, 0);
// force the screen in a place where the cursor is visable
- int ox, oy;
- buffer_offset_to_xy(buf, buf->cursor_offset, maxx - minx, &ox, &oy);
- ox += minx - (maxx-3);
- int xscroll = 0;
- if (ox > 0)
- xscroll = ox;
+ int ox, oy, xscroll;
+ fb_offset_to_xy(fb, wb->cursor_offset, maxx - minx, wb->y_scroll, &ox, &oy, &xscroll);
if (oy < 0) {
- buf->y_scroll += oy;
+ wb->y_scroll += oy;
} else {
oy += miny - maxy+2;
if (oy > 0)
- buf->y_scroll += oy;
+ wb->y_scroll += oy;
}
- if (buf->y_scroll < 0)
- buf->y_scroll = 0;
+ if (wb->y_scroll < 0)
+ wb->y_scroll = 0;
if (wrap_buffer)
xscroll = 0;
@@ -1531,7 +120,7 @@ buffer_draw_to_screen(struct window_split_node* wn, int minx, int miny, int maxx
char* repl = fb->contents;
char* last = repl + fb->len;
char* new_repl;
- int line = buf->y_scroll;
+ int line = wb->y_scroll;
while ((new_repl = memchr(repl, '\n', last - repl))) {
if (--line < 0)
break;
@@ -1540,51 +129,47 @@ buffer_draw_to_screen(struct window_split_node* wn, int minx, int miny, int maxx
else
return;
}
- int offset_start = repl - fb->contents;
+ int offset_start = repl - fb->contents - 1;
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 (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(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(cse.arg.start, cse.arg.end) != 0) {
- int end_len = strlen(cse.arg.end);
- offset = 0;
- while((offset = buffer_seek_string(fb, offset, cse.arg.end)) >= 0) {
- offset += end_len;
- if (offset >= offset_start)
- break;
- count--;
- }
- }
- if (count > 0) {
- offset = buffer_seek_string_backwards(fb, offset_start, cse.arg.start);
- do_color_scheme(fb, cs, offset);
- break;
- }
- }
- }
- }
-
// actually write to the screen
int once = 0;
int search_found = 0;
int non_blocking_search_found = 0;
+
+ // TODO: verify that last - repl is the same as offset_last - offset_start
+ int move_buffer_len = last - repl + 2;
+ uint8_t* move_buffer = xmalloc(move_buffer_len);
+ memset(move_buffer, 0, move_buffer_len);
+ move_buffer[0] = 0;
+ int lastx = x, lasty = y;
+ int move_buffer_index = 0;
+
+ // TODO: write max string len of 127
+ // TODO: make the write thing similar to syntax?
+ char* new_line_start = NULL;
+ call_extension(wb_new_line_draw, &new_line_start, wb, y - miny, maxy - y, minx, maxx, &global_attr);
+ if (new_line_start) {
+ struct glyph old_attr = global_attr;
+ global_attr = default_attributes;
+ x = write_string(new_line_start, y, minx, maxx+1);
+ global_attr = old_attr;
+ }
+
+ int tmp = 0;
+ call_extension(wb_write_status_bar, &tmp, NULL, 0, 0, 0, 0, NULL, NULL);
+
for (int charsize = 1; repl < last && charsize; repl += charsize) {
- if (!once && repl - fb->contents >= buf->cursor_offset) {
+ if (y > lasty) {
+ move_buffer[move_buffer_index] = x - minx;
+ move_buffer[move_buffer_index] |= 1<<7;
+ } else {
+ move_buffer[move_buffer_index] = x - lastx;
+ }
+ move_buffer_index++;
+ lastx = x, lasty = y;
+
+ if (!once && repl - fb->contents >= wb->cursor_offset) {
// if the buffer being drawn is focused, set the cursor position global
once = 1;
cursor_x = x - xscroll;
@@ -1593,9 +178,6 @@ buffer_draw_to_screen(struct window_split_node* wn, int minx, int miny, int maxx
LIMIT(cursor_y, miny, maxy);
}
- if (cs)
- do_color_scheme(fb, cs, repl - fb->contents);
-
if (!wrap_buffer && x - xscroll > maxx && *repl != '\n') {
charsize = 1;
x++;
@@ -1609,47 +191,55 @@ buffer_draw_to_screen(struct window_split_node* wn, int minx, int miny, int maxx
if (wrap_buffer && *repl != '\n')
continue;
charsize = 1;
+ char* new_line_start = NULL;
+ call_extension(wb_new_line_draw, &new_line_start, wb, y - miny, maxy - y, minx, maxx, &global_attr);
+ if (new_line_start) {
+ struct glyph old_attr = global_attr;
+ global_attr = default_attributes;
+ x = write_string(new_line_start, y, minx, maxx+1);
+ global_attr = old_attr;
+ }
continue;
} else if (*repl == '\t') {
charsize = 1;
if ((x - minx) <= 0) {
- x += tsetchar(' ', x - xscroll, y);
+ x += screen_set_char(' ', x - xscroll, y);
if (x >= maxx)
continue;
}
while ((x - minx) % tabspaces != 0 && x - xscroll <= maxx)
- x += tsetchar(' ', x - xscroll, y);
+ x += screen_set_char(' ', x - xscroll, y);
if (x - xscroll <= maxx)
- x += tsetchar(' ', x, y);
+ x += screen_set_char(' ', x, y);
continue;
}
- Rune u;
- charsize = t_decode_utf8_buffer(repl, last - repl, &u);
+ rune_t u;
+ charsize = utf8_decode_buffer(repl, last - repl, &u);
int width;
if (x - xscroll >= minx)
- width = tsetchar(u, x - xscroll, y);
+ width = screen_set_char(u, x - xscroll, y);
else
width = wcwidth(u);
// drawing search highlight
- if (fb->mode & BUFFER_SEARCH_BLOCKING_MASK) {
- if (!search_found && buffer_offset_starts_with(fb, repl - fb->contents, fb->search_term))
+ if (fb->mode & FB_SEARCH_BLOCKING_MASK) {
+ if (!search_found && fb_offset_starts_with(fb, repl - fb->contents, fb->search_term))
search_found = strlen(fb->search_term);
if (search_found) {
- tsetattr(x - xscroll, y)->bg = highlight_color;
- tsetattr(x - xscroll, y)->fg = default_attributes.bg;
+ screen_set_attr(x - xscroll, y)->bg = highlight_color;
+ screen_set_attr(x - xscroll, y)->fg = default_attributes.bg;
search_found--;
}
}
- if (fb->mode & BUFFER_SEARCH_NON_BLOCKING) {
- if (!non_blocking_search_found && buffer_offset_starts_with(fb, repl - fb->contents, fb->non_blocking_search_term))
+ if (fb->mode & FB_SEARCH_NON_BLOCKING) {
+ if (!non_blocking_search_found && fb_offset_starts_with(fb, repl - fb->contents, fb->non_blocking_search_term))
non_blocking_search_found = strlen(fb->search_term);
if (non_blocking_search_found) {
- tsetattr(x - xscroll, y)->fg = highlight_color;
- tsetattr(x - xscroll, y)->mode |= ATTR_UNDERLINE;
+ screen_set_attr(x - xscroll, y)->fg = highlight_color;
+ screen_set_attr(x - xscroll, y)->mode |= ATTR_UNDERLINE;
non_blocking_search_found--;
}
}
@@ -1659,52 +249,45 @@ buffer_draw_to_screen(struct window_split_node* wn, int minx, int miny, int maxx
int offset_end = repl - fb->contents;
global_attr = default_attributes;
- if (buf->cursor_offset >= fb->len) {
+ if (wb->cursor_offset >= fb->len) {
cursor_x = x - xscroll;
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);
+ call_extension(window_written_to_screen, wn, offset_start, offset_end, move_buffer, move_buffer_len);
- // TODO: let the user do this
int status_end = minx;
- if (fb->mode & BUFFER_SEARCH_BLOCKING_IDLE) {
- int before;
- int search_count = buffer_count_string_instances(fb, fb->search_term, focused_window->cursor_offset, &before);
- status_end = writef_string(maxy-1, status_end, maxx+1, " %d/%d", before, search_count);
- }
- status_end = writef_string(maxy-1, status_end, maxx+1, " %dk ", fb->len/1000);
+ int write_again;
+ do {
+ write_again = 0;
+ char bar[LINE_MAX_LEN];
+ *bar = 0;
- char* path = file_path_get_path(fb->file_path);
- global_attr.fg = path_color;
- status_end = writef_string(maxy-1, status_end, maxx+1, "%s", path);
- global_attr = default_attributes;
- free(path);
- const char* name = strrchr(fb->file_path, '/');
- if (name)
- status_end = writef_string(maxy-1, status_end, maxx+1, "%s", name+1);
- status_end = writef_string(maxy-1, status_end, maxx+1, " %d:%d %d%%" , cursor_y + buf->y_scroll, cursor_x - minx + xscroll,
- (int)(((float)(buf->cursor_offset+1)/(float)fb->len)*100.0f));
- if (fb->mode & BUFFER_SELECTION_ON) {
+ call_extension(wb_write_status_bar, &write_again, wb, status_end, maxx+1, cursor_x - minx + xscroll, cursor_y - miny + wb->y_scroll, bar, &global_attr);
+ status_end = write_string(bar, maxy-1, status_end, maxx+1);
+
+ global_attr = default_attributes;
+ } while (write_again);
+
+ if (fb->mode & FB_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);
+ fb_offset_to_xy(fb, fb->s1o, 0, wb->y_scroll, &tmp, &y1, &tmp);
+ fb_offset_to_xy(fb, fb->s2o, 0, wb->y_scroll, &tmp, &y2, &tmp);
writef_string(maxy-1, status_end, maxx, " %dL", abs(y1-y2));
}
if (focused) {
for (int i = minx; i < maxx+1; i++) {
- if (!(fb->mode & BUFFER_SELECTION_ON)) {
- if (tsetattr(i, cursor_y)->bg == default_attributes.bg)
- tsetattr(i, cursor_y)->bg = mouse_line_bg;
+ if (!(fb->mode & FB_SELECTION_ON)) {
+ if (screen_set_attr(i, cursor_y)->bg == default_attributes.bg)
+ screen_set_attr(i, cursor_y)->bg = mouse_line_bg;
}
- tsetattr(i, maxy-1)->bg = alternate_bg_bright;
+ screen_set_attr(i, maxy-1)->bg = alternate_bg_bright;
}
}
- buffer_write_selection(buf, minx, miny, maxx, maxy);
- do_color_scheme(NULL, &(struct color_scheme){0}, 0);
+ wb_write_selection(wb, minx, miny, maxx, maxy);
+ //do_syntax_scheme(NULL, &(struct syntax_scheme){0}, 0);
for (int i = miny; i < maxy; i++)
xdrawline(minx, i, maxx+1);
@@ -1712,508 +295,82 @@ buffer_draw_to_screen(struct window_split_node* wn, int minx, int miny, int maxx
draw_horisontal_line(maxy-1, minx, maxx);
xdrawcursor(cursor_x, cursor_y, focused);
-}
-
-// 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, const struct color_scheme* cs, int offset)
-{
- static int end_at_whitespace = 0;
- static const char* end_condition;
- static int end_condition_len;
- static Glyph next_word_attr;
- static int color_next_word = 0;
- static int around = 0;
-
- if (!fb || !cs) {
- // reset
- end_at_whitespace = 0;
- end_condition_len = 0;
- around = 0;
- color_next_word = 0;
- end_condition = NULL;
- global_attr = default_attributes;
- return;
- }
-
- char* buf = fb->contents;
- int buflen = fb->len;
-
- if (end_condition && !color_next_word) {
- if (buflen - offset <= end_condition_len
- || (offset-1 >= 0 && buf[offset-1] == '\\'))
- return;
- if (end_at_whitespace && buf[offset] == '\n') {
- // *_TO_LINE reached end of line
- end_condition_len = 0;
- end_condition = NULL;
- end_at_whitespace = 0;
- global_attr = default_attributes;
- } 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 until later
- if (around)
- around = 0;
- else
- global_attr = default_attributes;
-
- end_condition = NULL;
- end_at_whitespace = 0;
- }
- return;
- } else if (end_at_whitespace) {
- if (!buffer_is_on_a_word(fb, offset, cs->word_seperators)) {
- end_at_whitespace = 0;
- global_attr = default_attributes;
- } else {
- return;
- }
- } else if (color_next_word) {
- // check if new word encountered
- if (!buffer_is_on_a_word(fb, offset, cs->word_seperators))
- return;
- global_attr = next_word_attr;
- color_next_word = 0;
- end_at_whitespace = 1;
- return;
- } else if (end_condition_len > 0) {
- // wait for the word/sequence to finish
- // NOTE: does not work with utf8 chars
- if (--end_condition_len <= 0)
- global_attr = default_attributes;
- else
- return;
- }
-
- 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) {
- if (!buffer_is_start_of_a_word(fb, offset, cs->word_seperators))
- continue;
-
- int end_len = 0;
- 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 UPPER_CASE_WORD_MIN_LEN chars
- if (end_len < UPPER_CASE_WORD_MIN_LEN)
- continue;
-
- global_attr = entry.attr;
- end_condition_len = end_len;
- return;
-
- not_upper_case:
- continue;
- }
-
- int len = strlen(entry.arg.start);
-
- 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;
-
- int offset_tmp = offset;
- // find new word twice if it's BEFORE_STR_STR
- int times = mode == COLOR_WORD_BEFORE_STR_STR ? 2 : 1;
- int first_word_len = 0;
- int first_time = 1;
- while (times--) {
- // seek end of word
- 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 = 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;
- }
- if (buffer_offset_starts_with(fb, offset_tmp, entry.arg.start)) {
- global_attr = entry.attr;
- end_condition_len = first_word_len;
- return;
- }
- exit_word_before_str_str:
- continue;
- }
-
- if (mode == COLOR_INSIDE || mode == COLOR_INSIDE_TO_LINE || mode == COLOR_WORD_INSIDE) {
- if (offset - len < 0)
- continue;
- // check the if what's behind the cursor is the first string
- 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;
- 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 = strlen(entry.arg.end);
- global_attr = entry.attr;
- around = 0;
- if (entry.mode == COLOR_INSIDE_TO_LINE)
- end_at_whitespace = 1;
- return;
- }
- continue;
- }
-
- 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(!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) {
- int offset_str = buffer_seek_not_whitespace(fb, offset + len);
-
- if (!buffer_offset_starts_with(fb, offset_str, entry.arg.end))
- continue;
- end_condition_len = strlen(entry.arg.start);
- } else {
- end_at_whitespace = 1;
- }
- 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;
- }
- }
+ free(move_buffer);
}
int
write_string(const char* string, int y, int minx, int maxx)
{
- LIMIT(maxx, 0, term.col);
+ if (!string)
+ return 0;
+ LIMIT(maxx, 0, screen.col);
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);
+ rune_t u;
+ int charsize = utf8_decode_buffer(string + offset, len - offset, &u);
offset += charsize;
if (charsize == 1 && u <= 32)
u = ' ';
- minx += tsetchar(u, minx, y);
+ minx += screen_set_char(u, minx, y);
}
return minx;
}
-int
-str_contains_char(const char* string, char check)
-{
- int len = strlen(string);
- for (int i = 0; i < len; i++)
- if (string[i] == check)
- return 1;
- return 0;
-}
-
void
-color_selection(Glyph* letter)
+color_selection(struct glyph* letter)
{
if (letter->bg == default_attributes.bg)
letter->bg = selection_bg;
}
-
-int
-buffer_is_selection_start_top_left(const struct file_buffer* buf)
-{
- return (buf->s1o <= buf->s2o) ? 1 : 0;
-}
-
void
-buffer_move_cursor_to_selection_start(struct window_buffer* buf)
+wb_move_cursor_to_selection_start(struct window_buffer* wb)
{
- const struct file_buffer* fb = get_file_buffer(buf);
- if (buffer_is_selection_start_top_left(fb))
- buffer_move_to_offset(buf, fb->s1o, CURSOR_SNAPPED);
+ const struct file_buffer* fb = get_fb(wb);
+ if (fb_is_selection_start_top_left(fb))
+ wb_move_to_offset(wb, fb->s1o, CURSOR_SNAPPED);
else
- buffer_move_to_offset(buf, fb->s2o, CURSOR_SNAPPED);
+ wb_move_to_offset(wb, fb->s2o, CURSOR_SNAPPED);
}
void
-buffer_write_selection(struct window_buffer* buf, int minx, int miny, int maxx, int maxy)
+wb_write_selection(struct window_buffer* wb, int minx, int miny, int maxx, int maxy)
{
- assert(buf);
- struct file_buffer* fb = get_file_buffer(buf);
+ soft_assert(wb, return;);
+ struct file_buffer* fb = get_fb(wb);
- LIMIT(maxx, 0, term.col-1);
- LIMIT(maxy, 0, term.row-1);
+ LIMIT(maxx, 0, screen.col-1);
+ LIMIT(maxy, 0, screen.row-1);
LIMIT(minx, 0, maxx);
LIMIT(miny, 0, maxy);
- //TODO: implement alternative selection modes
- if (!(fb->mode & BUFFER_SELECTION_ON))
+ if (!(fb->mode & FB_SELECTION_ON))
return;
- int x, y, x2, y2;
- if (buffer_is_selection_start_top_left(fb)) {
- buffer_offset_to_xy(buf, fb->s1o, maxx - minx, &x, &y);
- buffer_offset_to_xy(buf, fb->s2o, maxx - minx, &x2, &y2);
+ int x, y, x2, y2, tmp, xscroll;
+ if (fb_is_selection_start_top_left(fb)) {
+ fb_offset_to_xy(fb, fb->s1o, maxx - minx, wb->y_scroll, &x, &y, &tmp);
+ fb_offset_to_xy(fb, fb->s2o, maxx - minx, wb->y_scroll, &x2, &y2, &xscroll);
} else {
- buffer_offset_to_xy(buf, fb->s2o, maxx - minx, &x, &y);
- buffer_offset_to_xy(buf, fb->s1o, maxx - minx, &x2, &y2);
+ fb_offset_to_xy(fb, fb->s2o, maxx - minx, wb->y_scroll, &x, &y, &xscroll);
+ fb_offset_to_xy(fb, fb->s1o, maxx - minx, wb->y_scroll, &x2, &y2, &tmp);
}
x += minx, x2 += minx + 1;
y += miny, y2 += miny;
+ if (!wrap_buffer) {
+ x -= xscroll;
+ x2 -= xscroll;
+ }
for(; y < y2; y++) {
- for(; x < maxx; x++)
- color_selection(tsetattr(x, y));
+ for(; x <= maxx; x++)
+ color_selection(screen_set_attr(x, y));
x = 0;
}
for(; x < x2; x++)
- color_selection(tsetattr(x, y));
-}
-
-char* buffer_get_selection(struct file_buffer* buffer, int* selection_len)
-{
- if (!(buffer->mode & BUFFER_SELECTION_ON))
- return NULL;
-
- int start, end, len;
- if (buffer_is_selection_start_top_left(buffer)) {
- start = buffer->s1o;
- end = buffer->s2o+1;
- } else {
- start = buffer->s2o;
- end = buffer->s1o+1;
- }
- len = end - start;
- if (selection_len)
- *selection_len = len;
-
- char* returned_selction = xmalloc(len + 1);
- memcpy(returned_selction, buffer->contents+start, len);
- returned_selction[len] = 0;
- return returned_selction;
-}
-
-void
-buffer_remove_selection(struct file_buffer* buffer)
-{
- if (!(buffer->mode & BUFFER_SELECTION_ON))
- return;
-
- int start, end, len;
- if (buffer_is_selection_start_top_left(buffer)) {
- start = buffer->s1o;
- end = buffer->s2o+1;
- } else {
- start = buffer->s2o;
- end = buffer->s1o+1;
- }
- len = end - start;
- buffer_remove(buffer, start, len, 1, 1);
- if (buffer_contents_updated)
- buffer_contents_updated(buffer, start, BUFFER_CONTENT_BIG_CHANGE);
-}
-
-char*
-buffer_get_line_at_offset(const struct file_buffer* fb, int offset)
-{
- int start = buffer_seek_char_backwards(fb, offset, '\n');
- if (start < 0) start = 0;
- int end = buffer_seek_char(fb, offset, '\n');
- if (end < 0) end = fb->len-1;
-
- int len = end - start;
-
- char* res = xmalloc(len + 1);
- if (len > 0)
- memcpy(res, fb->contents+start, len);
- res[len] = 0;
- return res;
-}
-
-void
-buffer_write_to_filepath(const struct file_buffer* buffer)
-{
- if (!buffer->file_path)
- return;
- assert(buffer->contents);
- FILE* file = fopen(buffer->file_path, "w");
- assert(file);
-
- 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);
-}
-
-int
-t_decode_utf8_buffer(const char* buffer, const int buflen, Rune* u)
-{
- if (!buflen) return 0;
-
- Rune u_tmp;
- int charsize;
- if (!u)
- u = &u_tmp;
-
- /* process a complete utf8 char */
- charsize = utf8decode(buffer, u, buflen);
-
- return charsize;
-}
-
-int
-tsetchar(Rune u, int x, int y)
-{
- Glyph attr = global_attr;
- if (y >= term.row || x >= term.col ||
- y < 0 || x < 0)
- return 1;
-
- if (u == 0)
- u = term.line[y][x].u;
- int width = wcwidth(u);
- if (width == -1)
- width = 1;
- else if (width > 1)
- attr.mode |= ATTR_WIDE;
-
- if (term.line[y][x].mode & ATTR_WIDE || attr.mode & ATTR_WIDE) {
- if (x+1 < term.col) {
- term.line[y][x+1].u = ' ';
- term.line[y][x+1].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;
- }
-
- term.line[y][x] = attr;
- term.line[y][x].u = u;
-
- return width;
-}
-
-Glyph*
-tsetattr(int x, int y)
-{
- static Glyph dummy;
- if (y >= term.row || x >= term.col ||
- y < 0 || x < 0)
- return &dummy;
-
- return &term.line[y][x];
-}
-
-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;
-}
-
-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;
-
- if (col < 1 || row < 1) {
- fprintf(stderr,
- "tresize: error resizing to %dx%d\n", col, row);
- return;
- }
-
- // 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));
-
- if (row > term.row)
- for (i = term.row; i < row; i++)
- term.line[i] = NULL;
-
- // 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
- term.col = col;
- term.row = row;
-}
-
-int
-is_file_type(const char* file_path, const char* file_type)
-{
- int ftlen = strlen(file_type);
- int offset = strlen(file_path) - ftlen;
- if(offset >= 0 && memcmp(file_path + offset, file_type, ftlen) == 0)
- return 1;
- return 0;
+ color_selection(screen_set_attr(x, y));
}
diff --git a/se.h b/se.h
@@ -2,335 +2,22 @@
#ifndef _ST_H
#define _ST_H
-#include <stdint.h>
-#include <sys/types.h>
-#include <wchar.h>
-#include <limits.h>
-#include <dirent.h>
+#include "utf8.h"
+#include "buffer.h"
+#include "seek.h"
+#include "x.h"
-// Arbitrary sizes
-#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_MAX_LEN PATH_MAX
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
-#define MAX(a, b) ((a) < (b) ? (b) : (a))
-#define LEN(a) (sizeof(a) / sizeof(a)[0])
-#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
-#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
-
-////////////////////////////////////////////////
-// Glyphs
-//
-
-enum glyph_attribute {
- ATTR_NULL = 0,
- ATTR_BOLD = 1 << 0,
- ATTR_FAINT = 1 << 1,
- ATTR_ITALIC = 1 << 2,
- ATTR_UNDERLINE = 1 << 3,
- ATTR_REVERSE = 1 << 5,
- ATTR_INVISIBLE = 1 << 6,
- ATTR_STRUCK = 1 << 7,
- ATTR_WIDE = 1 << 9,
- ATTR_WDUMMY = 1 << 10,
- ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
-};
-
-typedef unsigned char uchar;
-typedef unsigned int uint;
-typedef unsigned long ulong;
-typedef unsigned short ushort;
-
-typedef uint_least32_t Rune;
-
-typedef struct {
- Rune u; // character code
- ushort mode; // attribute flags
- uint32_t fg; // foreground
- uint32_t bg; // background
-} Glyph_;
-#define Glyph Glyph_
-
-typedef Glyph *Line;
-
-////////////////////////////////////////////////
-// Window buffer
-//
-
-// NOTE:
-// refrain from entering the buffers or buffer keyword searching
-// mode if you already are in file browser mode
-enum window_buffer_mode {
- WINDOW_BUFFER_NORMAL = 0,
- WINDOW_BUFFER_FILE_BROWSER,
- WINDOW_BUFFER_SEARCH_BUFFERS,
- WINDOW_BUFFER_KEYWORD_ALL_BUFFERS,
- WINDOW_BUFFER_MODE_LEN,
-};
-
-struct window_buffer {
- int y_scroll;
- int cursor_offset;
- int cursor_col;
-
- int buffer_index; // index into an array storing file buffers
- enum window_buffer_mode mode;
-};
-
-struct window_buffer window_buffer_new(int buffer_index);
-
-void buffer_write_selection(struct window_buffer* buf, int minx, int miny, int maxx, int maxy);
-void buffer_move_cursor_to_selection_start(struct window_buffer* buffer);
-
-void buffer_offset_to_xy(struct window_buffer* buf, int offset, int maxx, int* cx, int* cy);
-
-enum cursor_reason {
- CURSOR_DO_NOT_CALLBACK = 0,
- CURSOR_COMMAND_MOVEMENT = 1,
- CURSOR_UP_DOWN_MOVEMENT,
- CURSOR_RIGHT_LEFT_MOVEMENT,
- CURSOR_SNAPPED,
-};
-
-void buffer_move_on_line(struct window_buffer* buf, int amount, enum cursor_reason callback_reason);
-void buffer_move_lines(struct window_buffer* buf, int amount, enum cursor_reason callback_reason);
-void buffer_move_to_offset(struct window_buffer* buf, int offset, enum cursor_reason callback_reason);
-void buffer_move_offset_relative(struct window_buffer* buf, int amount, enum cursor_reason callback_reason);
-void buffer_move_to_x(struct window_buffer* buf, int x, enum cursor_reason callback_reason);
-
-enum window_split_mode {
- WINDOW_SINGULAR,
- WINDOW_HORISONTAL,
- WINDOW_VERTICAL,
- WINDOW_FILE_BROWSER,
-};
-
-struct window_split_node {
- struct window_buffer window;
- enum window_split_mode mode;
- float ratio;
- struct window_split_node *node1, *node2, *parent;
- char* search;
- int selected;
-};
-
-void window_node_split(struct window_split_node* parent, float ratio, enum window_split_mode mode);
-struct window_split_node* window_node_delete(struct window_split_node* node);
-// uses focused_window to draw the cursor
-void window_draw_tree_to_screen(struct window_split_node* root, int minx, int miny, int maxx, int maxy);
-void window_move_all_cursors_on_same_buf(struct window_split_node* root, struct window_split_node* excluded, int buf_index, int offset, void(movement)(struct window_buffer*, int, enum cursor_reason), int move, enum cursor_reason reason);
-void window_move_all_yscrolls(struct window_split_node* root, struct window_split_node* excluded, int buf_index, int offset, int move);
-int window_other_nodes_contain_file_buffer(struct window_split_node* node, struct window_split_node* root);
-
-enum move_directons {
- MOVE_RIGHT,
- MOVE_LEFT,
- MOVE_UP,
- MOVE_DOWN,
-};
-
-struct window_split_node* window_switch_to_window(struct window_split_node* node, enum move_directons move);
-// NOTE: if you have two splits both having two splits of the same type, you can't resize the upper split
-void window_node_resize(struct window_split_node* node, enum move_directons move, float amount);
-void window_node_resize_absolute(struct window_split_node* node, enum move_directons move, float amount);
-
-////////////////////////////////////////////////
-// Color Scheme
-//
-
-struct color_scheme_arg {
- const char* start;
- const char* end;
-};
-
-enum color_scheme_mode {
- // needs two strings
- COLOR_AROUND,
- // needs two strings
- COLOR_AROUND_TO_LINE,
- // needs two strings
- COLOR_INSIDE,
- // needs two strings
- COLOR_INSIDE_TO_LINE,
- // needs two strings
- COLOR_WORD_INSIDE,
- // needs one string
- COLOR_WORD,
- // needs one string
- COLOR_WORD_ENDING_WITH_STR,
- // needs one string
- COLOR_WORD_STARTING_WITH_STR,
- // needs one string
- COLOR_STR,
- // needs two strings
- // colors word if string is found after it
- COLOR_WORD_STR,
- // needs one string
- // can be combined with others if this is first
- COLOR_STR_AFTER_WORD,
- // needs one string
- // "(" would color like this "not_colored colored("
- // "[" would color like this "not_colored colored ["
- COLOR_WORD_BEFORE_STR,
- // needs one string
- // "(" would color like this "colored not_colored("
- // "=" would color like this "colored not_colored ="
- COLOR_WORD_BEFORE_STR_STR,
- // no arguments needed
- COLOR_UPPER_CASE_WORD,
-};
-
-struct color_scheme_entry {
- const enum color_scheme_mode mode;
- const struct color_scheme_arg arg;
- const Glyph attr;
-};
-
-struct color_scheme {
- const char* file_ending;
- const char* word_seperators;
- const struct color_scheme_entry* entries;
- const int entry_count;
-};
-
-////////////////////////////////////////////////
-// File buffer
-//
-
-struct undo_buffer {
- char* contents; // not null terminated
- int len, capacity;
- int cursor_offset;
- int y_scroll;
-};
-
-enum buffer_flags {
- BUFFER_SELECTION_ON = 1 << 0,
- BUFFER_BLOCK_SELECT = 1 << 1,
- BUFFER_LINE_SELECT = 1 << 2,
- BUFFER_READ_ONLY = 1 << 3,
- BUFFER_UTF8_SIGNED = 1 << 4,
- BUFFER_SEARCH_BLOCKING = 1 << 5,
- BUFFER_SEARCH_BLOCKING_IDLE = 1 << 6,
- BUFFER_SEARCH_BLOCKING_MASK = (BUFFER_SEARCH_BLOCKING | BUFFER_SEARCH_BLOCKING_IDLE),
- BUFFER_SEARCH_NON_BLOCKING = 1 << 7,
- BUFFER_SEARCH_BLOCKING_BACKWARDS = 1 << 8,
- BUFFER_SEARCH_NON_BLOCKING_BACKWARDS = 1 << 9,
-};
-
-// Contents of a file buffer
-struct file_buffer {
- char* file_path;
- char* contents; // !! NOT NULL TERMINATED !!
- int len;
- int capacity;
- enum buffer_flags mode;
- struct undo_buffer* ub;
- int current_undo_buffer;
- int available_redo_buffers;
- int s1o, s2o; // selection start offset and end offset
- char* search_term;
- char* non_blocking_search_term;
-};
-
-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);
-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_string_wrap(const struct window_buffer* wb, int offset, const char* search);
-int buffer_seek_string_wrap_backwards(const struct window_buffer* wb, int offset, const char* search);
-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);
-
-int buffer_count_string_instances(const struct file_buffer* fb, const char* string, int offset, int* before_offset);
-
-///////////////////////////////////
-// returns a null terminated string containing the selection
-// the returned value must be freed by the reciever
-// for conveniance the length of the string may be taken with the pointer
-// a selection_len of NULL wil be ignored
-char* buffer_get_selection(struct file_buffer* buf, int* selection_len);
-int buffer_is_selection_start_top_left(const struct file_buffer* buffer);
-void buffer_remove_selection(struct file_buffer* buffer);
-
-///////////////////////////////////
-// returns a null terminated string containing the current line
-// the returned value must be freed by the reciever
-char* buffer_get_line_at_offset(const struct file_buffer* fb, int offset);
-// result must be freed
-char* file_path_get_path(const char* path);
-
-////////////////////////////////////////////////
-// Other
-//
-
-struct keyword_pos {
- int offset, buffer_index;
-};
-
-const char* file_browser_next_item(const char* path, const char* search, int* offset, Glyph* attr, void* data);
-// data pointer will give the file buffer of the current item
-const char* buffer_search_next_item(const char* tmp, const char* search, int* offset, Glyph* attr, void* data);
-// data pointer will give the keyword_pos of the current item
-const char* buffers_search_keyword_next_item(const char* tmp, const char* search, int* offset, Glyph* attr, void* data);
-
-void die(const char *, ...);
-int is_file_type(const char* file_path, const char* file_type);
-int path_is_folder(const char* path);
int write_string(const char* string, int y, int minx, int maxx);
-int writef_to_status_bar(const char* fmt, ...);
-void draw_status_bar();
-void remove_utf8_string_end(char* string);
-
-// Internal representation of the screen
-typedef struct {
- int row; // row count
- int col; // column count
- Line *line; // array
-} Term;
-
-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);
-void *xrealloc(void *, size_t);
-
-enum buffer_content_reason {
- BUFFER_CONTENT_DO_NOT_CALLBACK,
- BUFFER_CONTENT_OPERATION_ENDED,
- BUFFER_CONTENT_NORMAL_EDIT,
- BUFFER_CONTENT_BIG_CHANGE,
- BUFFER_CONTENT_INIT,
-};
+// TODO: make this user addable
+// so lessen the size of the root window and put this at the bottom (each frame)
+extern char status_bar_contents[STATUS_BAR_MAX_LEN];
+extern uint32_t status_bar_bg;
+void writef_to_status_bar(const char* fmt, ...);
+void draw_status_bar();
-void buffer_add_to_undo(struct file_buffer* buffer, int offset, enum buffer_content_reason reason);
+void window_node_draw_to_screen(struct window_split_node* wn);
#endif // _ST_H
diff --git a/seek.c b/seek.c
@@ -0,0 +1,484 @@
+
+
+#if 1
+#define _GNU_SOURCE
+#endif
+
+#include "seek.h"
+
+#include <string.h>
+#include <ctype.h>
+
+#include "config.h"
+#include <sys/stat.h>
+
+#if 0
+#define BOUNDS_CHECK(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
+#else
+#define BOUNDS_CHECK(x, a, b)
+#endif
+
+int
+str_contains_char(const char* string, char check)
+{
+ return strchr(string, check) != NULL;
+}
+
+inline int
+is_file_type(const char* file_path, const char* file_type)
+{
+ int ftlen = strlen(file_type);
+ int offset = strlen(file_path) - ftlen;
+ if(offset >= 0 && memcmp(file_path + offset, file_type, ftlen) == 0)
+ return 1;
+ return 0;
+}
+
+char*
+file_path_get_path(const char* path)
+{
+ soft_assert(path, path = "/";);
+
+ const char* folder_start = strrchr(path, '/');
+ if (!folder_start)
+ folder_start = path;
+ else
+ folder_start++;
+ int folder_len = folder_start - path;
+ char* folder = xmalloc(folder_len + 1);
+
+ memcpy(folder, path, folder_len);
+ folder[folder_len] = '\0';
+
+ return folder;
+}
+
+inline int
+path_is_folder(const char* path)
+{
+ struct stat statbuf;
+ if (stat(path, &statbuf) != 0)
+ return 0;
+ return S_ISDIR(statbuf.st_mode);
+}
+
+inline int
+fb_seek_char(const struct file_buffer* fb, int offset, char byte)
+{
+ if (offset > fb->len) return -1;
+ char* new_buf = memchr(fb->contents + offset, byte, fb->len - offset);
+ if (!new_buf) return -1;
+ return new_buf - fb->contents;
+}
+
+inline int
+fb_seek_char_backwards(const struct file_buffer* fb, int offset, char byte)
+{
+ BOUNDS_CHECK(offset, 0, fb->len-1);
+ for (int n = offset-1; n >= 0; n--) {
+ if (fb->contents[n] == byte) {
+ return n+1;
+ }
+ }
+ return -1;
+}
+
+inline int
+fb_seek_string(const struct file_buffer* fb, int offset, const char* string)
+{
+ BOUNDS_CHECK(offset, 0, fb->len-1);
+ int str_len = strlen(string);
+
+#if 0
+ for (int n = offset; n < fb->len - str_len; n++)
+ if (!memcmp(fb->contents + n, string, str_len))
+ return n;
+#else
+ char* res = memmem(fb->contents + offset, fb->len - offset,
+ string, str_len);
+ if (res)
+ return res - fb->contents;
+#endif
+ return -1;
+}
+
+inline int
+fb_seek_string_backwards(const struct file_buffer* fb, int offset, const char* string)
+{
+ int str_len = strlen(string);
+ offset += str_len;
+ BOUNDS_CHECK(offset, 0, fb->len-1);
+
+ for (int n = offset - str_len; n >= 0; n--)
+ if (!memcmp(fb->contents + n, string, str_len))
+ return n;
+ return -1;
+}
+
+inline int
+wb_seek_string_wrap(const struct window_buffer* wb, int offset, const char* search)
+{
+ struct file_buffer* fb = get_fb(focused_window);
+ if (*search == 0 || !fb_count_string_instances(fb, search, 0, NULL))
+ return -1;
+
+ int new_offset = fb_seek_string(fb, offset, search);
+ if (new_offset < 0)
+ new_offset = fb_seek_string(fb, 0, search);
+
+ if (!(fb->mode & FB_SEARCH_BLOCKING))
+ fb->mode |= FB_SEARCH_BLOCKING_IDLE;
+ return new_offset;
+}
+
+inline int
+wb_seek_string_wrap_backwards(const struct window_buffer* wb, int offset, const char* search)
+{
+ struct file_buffer* fb = get_fb(focused_window);
+ if (*search == 0 || !fb_count_string_instances(fb, search, 0, NULL))
+ return -1;
+
+ int new_offset = fb_seek_string_backwards(fb, offset, search);
+ if (new_offset < 0)
+ new_offset = fb_seek_string_backwards(fb, fb->len, search);
+
+ if (!(fb->mode & FB_SEARCH_BLOCKING))
+ fb->mode |= FB_SEARCH_BLOCKING_IDLE;
+ return new_offset;
+}
+
+int
+fb_is_on_a_word(const struct file_buffer* fb, int offset, const char* word_seperators)
+{
+ BOUNDS_CHECK(offset, 0, fb->len);
+ return !str_contains_char(word_seperators, fb->contents[offset]);
+}
+
+inline int
+fb_is_start_of_a_word(const struct file_buffer* fb, int offset, const char* word_seperators)
+{
+ BOUNDS_CHECK(offset, 0, fb->len);
+ return fb_is_on_a_word(fb, offset, word_seperators) &&
+ (offset-1 <= 0 || str_contains_char(word_seperators, fb->contents[offset-1]));
+}
+
+inline int
+fb_is_on_word(const struct file_buffer* fb, int offset, const char* word_seperators, const char* word)
+{
+ BOUNDS_CHECK(offset, 0, fb->len);
+ int word_start = fb_seek_start_of_word_backwards(fb, offset, word_seperators);
+ int word_len = strlen(word);
+ if (word_start < offset - (word_len-1))
+ return 0;
+ return fb_offset_starts_with(fb, word_start, word) &&
+ !fb_is_on_a_word(fb, word_start + word_len, word_seperators);
+}
+
+inline int
+fb_offset_starts_with(const struct file_buffer* fb, int offset, const char* start)
+{
+ BOUNDS_CHECK(offset, 0, fb->len);
+ if (offset > 0 && fb->contents[offset-1] == '\\') return 0;
+
+ int len = strlen(start);
+ int mlen = MIN(len, fb->len - offset);
+ return memcmp(fb->contents + offset, start, mlen) == 0;
+}
+
+inline int
+fb_seek_word(const struct file_buffer* fb, int offset, const char* word_seperators)
+{
+ if (fb_is_on_a_word(fb, offset, word_seperators))
+ offset = fb_seek_word_end(fb, offset, word_seperators);
+ while (offset < fb->len && str_contains_char(word_seperators, fb->contents[offset])) offset++;
+ return offset;
+}
+
+inline int
+fb_seek_word_end(const struct file_buffer* fb, int offset, const char* word_seperators)
+{
+ BOUNDS_CHECK(offset, 0, fb->len);
+ if (!fb_is_on_a_word(fb, offset, word_seperators))
+ offset = fb_seek_word(fb, offset, word_seperators);
+ while (offset < fb->len && !str_contains_char(word_seperators, fb->contents[offset])) offset++;
+ return offset;
+}
+
+inline int
+fb_seek_word_backwards(const struct file_buffer* fb, int offset, const char* word_seperators)
+{
+ BOUNDS_CHECK(offset, 0, fb->len);
+ if (!fb_is_on_a_word(fb, offset, word_seperators))
+ while (offset > 0 && str_contains_char(word_seperators, fb->contents[offset])) offset--;
+ return offset;
+}
+
+inline int
+fb_seek_start_of_word_backwards(const struct file_buffer* fb, int offset, const char* word_seperators)
+{
+ BOUNDS_CHECK(offset, 0, fb->len);
+ if (!fb_is_on_a_word(fb, offset, word_seperators))
+ while (offset > 0 && str_contains_char(word_seperators, fb->contents[offset])) offset--;
+ while (offset > 0 && !str_contains_char(word_seperators, fb->contents[offset])) offset--;
+ return offset+1;
+}
+
+inline int
+fb_seek_whitespace(const struct file_buffer* fb, int offset)
+{
+ BOUNDS_CHECK(offset, 0, fb->len);
+ while (offset < fb->len && !isspace(fb->contents[offset])) offset++;
+ return offset;
+}
+
+inline int
+fb_seek_whitespace_backwards(const struct file_buffer* fb, int offset)
+{
+ BOUNDS_CHECK(offset, 0, fb->len);
+ while (offset > 0 && !isspace(fb->contents[offset])) offset--;
+ return offset;
+}
+
+inline int
+fb_seek_not_whitespace(const struct file_buffer* fb, int offset)
+{
+ BOUNDS_CHECK(offset, 0, fb->len);
+ while (offset < fb->len && isspace(fb->contents[offset])) offset++;
+ return offset;
+}
+
+inline int
+fb_seek_not_whitespace_backwards(const struct file_buffer* fb, int offset)
+{
+ BOUNDS_CHECK(offset, 0, fb->len);
+ while (offset > 0 && isspace(fb->contents[offset])) offset--;
+ return offset;
+}
+
+inline int
+fb_count_string_instances(const struct file_buffer* fb, const char* string, int offset, int* before_offset)
+{
+ int tmp;
+ if (!before_offset)
+ before_offset = &tmp;
+ if (!string || *string == 0) {
+ *before_offset = 0;
+ return 0;
+ }
+
+ int pos = -1;
+ int count = 0;
+ int once = 1;
+ while((pos = fb_seek_string(fb, pos+1, string)) >= 0) {
+ if (pos > 0 && fb->contents[pos-2] == '\\')
+ continue;
+ if (once && pos > offset) {
+ *before_offset = count;
+ once = 0;
+ }
+ count++;
+ }
+ if (once)
+ *before_offset = count;
+ return count;
+}
+
+/*
+** Search from the start of the file
+** Once any patterns are found, other patterns are disabled.
+** If the pattern we are searching for extends beyond *offset* then we will use that as our delimiter
+*/
+
+///////////////
+// " \" // we're here "
+// ↑ ↑
+// start seeked end
+int
+fb_seek_string_not_escaped(const struct file_buffer* fb, int offset, const char* string)
+{
+ for (;;) {
+ offset = fb_seek_string(fb, offset, string);
+ if (offset >= 0 && fb->contents[offset-1] == '\\')
+ offset++;
+ else
+ break;
+ }
+ return offset;
+}
+
+inline int
+fb_seek_string_backwards_not_escaped(const struct file_buffer* fb, int offset, const char* string)
+{
+ for (;;) {
+ offset = fb_seek_string_backwards(fb, offset, string);
+ if (offset >= 0 && fb->contents[offset-1] == '\\')
+ offset--;
+ else
+ break;
+ }
+ return offset;
+}
+
+inline static int
+fb_get_delimiter_equal_start_end(const struct file_buffer* fb, int offset, const char* string, struct delimiter* ignore, int* start, int* end)
+{
+ int ignore_index = 0;
+ int last_distance = 0;
+
+ while (last_distance >= 0) {
+ int d_res = INT32_MAX;
+ int i = 0;
+ if (ignore) {
+ for (struct delimiter* ign = ignore; ign->start; ign++) {
+ int d = fb_seek_string_not_escaped(fb, last_distance, ign->start);
+ if (d < 0 || d > offset) {
+ i++;
+ continue;
+ }
+
+ if (d < d_res) {
+ d_res = d;
+ ignore_index = i;
+ }
+ i++;
+ }
+ }
+ int str_d = fb_seek_string_not_escaped(fb, last_distance, string);
+
+ if ((str_d <= 0 || str_d > offset) && d_res == INT32_MAX)
+ return 0;
+
+ const char* end_str;
+ if (str_d >= 0 && str_d <= d_res) {
+ last_distance = str_d;
+ end_str = string;
+ *start = last_distance;
+ } else {
+ last_distance = d_res;
+ end_str = ignore[ignore_index].end;
+ }
+
+ last_distance = fb_seek_string_not_escaped(fb, last_distance+1, end_str);
+
+ if (end_str == string && last_distance > offset) {
+ *end = last_distance;
+ return 1;
+ }
+ if (last_distance >= 0)
+ last_distance++;
+ }
+ return 0;
+}
+
+inline static int
+fb_get_matched_delimiter_end(const struct file_buffer* fb, int offset, struct delimiter d)
+{
+ int closers_left = 1;
+ while (closers_left) {
+ int next_open = fb_seek_string_not_escaped(fb, offset, d.start);
+ int next_close = fb_seek_string_not_escaped(fb, offset, d.end);
+
+ if (next_close < 0 || next_open < 0)
+ return next_close;
+
+ if (next_open < next_close) {
+ closers_left++;
+ offset = next_open+1;
+ } else {
+ closers_left--;
+ offset = next_close;
+ if (closers_left)
+ offset++;
+ }
+ }
+ return offset;
+}
+
+inline static int
+fb_get_matched_delimiter_start(const struct file_buffer* fb, int offset, struct delimiter d)
+{
+ int openers_left = 1;
+ while (openers_left) {
+ int next_open = fb_seek_string_backwards_not_escaped(fb, offset, d.start);
+ int next_close = fb_seek_string_backwards_not_escaped(fb, offset, d.end);
+
+ if (next_close < 0 || next_open < 0)
+ return next_open;
+
+ if (next_open < next_close) {
+ openers_left++;
+ offset = next_open-1;
+ } else {
+ openers_left--;
+ offset = next_open;
+ if (openers_left)
+ offset--;
+ }
+ }
+ return offset;
+}
+
+inline int
+fb_get_delimiter(const struct file_buffer* fb, int offset, struct delimiter d, struct delimiter* ignore, int* start, int* end)
+{
+ const char* start_word = d.start;
+ const char* end_word = d.end;
+
+ soft_assert(start, return 0;);
+ soft_assert(end, return 0;);
+ soft_assert(start_word, return 0;);
+ soft_assert(end_word, return 0;);
+
+ if (strcmp(start_word, end_word) == 0)
+ return fb_get_delimiter_equal_start_end(fb, offset, start_word, ignore, start, end);
+ int wanted_opener = fb_get_matched_delimiter_start(fb, offset, d);
+
+ int ignore_index = 0;
+ int last_distance = 0;
+
+ while (last_distance >= 0) {
+ int d_res = INT32_MAX;
+ int i = 0;
+ if (ignore) {
+ for (struct delimiter* ign = ignore; ign->start; ign++) {
+ int d = fb_seek_string_not_escaped(fb, last_distance, ign->start);
+ if (d < 0 || d > offset) {
+ i++;
+ continue;
+ }
+
+ if (d < d_res) {
+ d_res = d;
+ ignore_index = i;
+ }
+ i++;
+ }
+ }
+ int str_d = fb_seek_string_not_escaped(fb, last_distance, start_word);
+
+ if ((str_d <= 0 || str_d > offset) && d_res == INT32_MAX)
+ return 0;
+
+ if (str_d >= 0 && str_d <= d_res) {
+ if (str_d == wanted_opener) {
+ last_distance = str_d;
+ *start = last_distance;
+
+ last_distance = fb_get_matched_delimiter_end(fb, last_distance+1, d);
+
+ if (last_distance > offset) {
+ *end = last_distance;
+ return 1;
+ }
+ }
+ } else {
+ last_distance = d_res;
+
+ last_distance = fb_seek_string_not_escaped(fb, last_distance+1, ignore[ignore_index].end);
+ }
+
+ if (last_distance >= 0)
+ last_distance++;
+ }
+ return 0;
+}
diff --git a/seek.h b/seek.h
@@ -0,0 +1,57 @@
+#ifndef SEEK_H_
+#define SEEK_H_
+
+// TODO: check that all functions are in the right place
+// complete rework / file renaming first
+
+#include "buffer.h"
+#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
+
+struct delimiter {
+ char* start;
+ char* end;
+};
+
+int str_contains_char(const char* string, char check);
+/////////////
+// result must be freed
+char* file_path_get_path(const char* path);
+int is_file_type(const char* file_path, const char* file_type);
+int path_is_folder(const char* path);
+
+int fb_is_on_a_word(const struct file_buffer* fb, int offset, const char* word_seperators);
+int fb_is_start_of_a_word(const struct file_buffer* fb, int offset, const char* word_seperators);
+int fb_is_on_word(const struct file_buffer* fb, int offset, const char* word_seperators, const char* word);
+
+int fb_offset_starts_with(const struct file_buffer* fb, int offset, const char* start);
+
+int fb_seek_char(const struct file_buffer* fb, int offset, char byte);
+int fb_seek_char_backwards(const struct file_buffer* fb, int offset, char byte);
+
+int fb_seek_string(const struct file_buffer* fb, int offset, const char* string);
+int fb_seek_string_not_escaped(const struct file_buffer* fb, int offset, const char* string);
+int fb_seek_string_backwards(const struct file_buffer* fb, int offset, const char* string);
+int fb_seek_string_backwards_not_escaped(const struct file_buffer* fb, int offset, const char* string);
+int wb_seek_string_wrap(const struct window_buffer* wb, int offset, const char* search);
+int wb_seek_string_wrap_backwards(const struct window_buffer* wb, int offset, const char* search);
+
+int fb_seek_word(const struct file_buffer* fb, int offset, const char* word_seperators);
+int fb_seek_word_end(const struct file_buffer* fb, int offset, const char* word_seperators);
+int fb_seek_word_backwards(const struct file_buffer* fb, int offset, const char* word_seperators);
+int fb_seek_start_of_word_backwards(const struct file_buffer* fb, int offset, const char* word_seperators);
+
+int fb_seek_whitespace(const struct file_buffer* fb, int offset);
+int fb_seek_whitespace_backwards(const struct file_buffer* fb, int offset);
+int fb_seek_not_whitespace(const struct file_buffer* fb, int offset);
+int fb_seek_not_whitespace_backwards(const struct file_buffer* fb, int offset);
+// TODO:
+//int fb_has_whitespace_between(const struct file_buffer* fb, int start, int end);
+
+int fb_count_string_instances(const struct file_buffer* fb, const char* string, int offset, int* before_offset);
+
+////////////////////////
+// struct delimiter* ignore is a null terminated pointer array
+int fb_get_delimiter(const struct file_buffer* fb, int offset, struct delimiter d, struct delimiter* ignore, int* start, int* end);
+
+
+#endif // SEEK_H_
diff --git a/utf8.c b/utf8.c
@@ -0,0 +1,117 @@
+#include "utf8.h"
+
+// TODO(not important):
+// optimize charsize algorthim
+
+#define LEN(a) (sizeof(a) / sizeof(a)[0])
+#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
+
+static const uint8_t utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
+static const uint8_t utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
+
+static size_t utf8_decode(const char *, rune_t *, size_t);
+static rune_t utf8_decodebyte(char, size_t *);
+static char utf8_encodebyte(rune_t, size_t);
+static size_t utf8_validate(rune_t *, size_t);
+
+rune_t
+utf8_decodebyte(char c, size_t *i)
+{
+ for (*i = 0; *i < LEN(utfmask); ++(*i))
+ if (((uint8_t)c & utfmask[*i]) == utfbyte[*i])
+ return (uint8_t)c & ~utfmask[*i];
+ return 0;
+}
+
+size_t
+utf8_encode(rune_t u, char *c)
+{
+ size_t len, i;
+
+ len = utf8_validate(&u, 0);
+ if (len > UTF_SIZ)
+ return 0;
+
+ for (i = len - 1; i != 0; --i) {
+ c[i] = utf8_encodebyte(u, 0);
+ u >>= 6;
+ }
+ c[0] = utf8_encodebyte(u, len);
+
+ return len;
+}
+
+char
+utf8_encodebyte(rune_t u, size_t i)
+{
+ return utfbyte[i] | (u & ~utfmask[i]);
+}
+
+size_t
+utf8_validate(rune_t *u, size_t i)
+{
+ const rune_t utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000};
+ const rune_t utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
+
+ if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
+ *u = UTF_INVALID;
+ for (i = 1; *u > utfmax[i]; ++i)
+ ;
+
+ return i;
+}
+
+size_t
+utf8_decode(const char *c, rune_t *u, size_t clen)
+{
+ size_t i, j, len, type;
+ rune_t udecoded;
+
+ *u = UTF_INVALID;
+ if (!clen)
+ return 0;
+ udecoded = utf8_decodebyte(c[0], &len);
+ if (!BETWEEN(len, 1, UTF_SIZ))
+ return 1;
+ for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
+ udecoded = (udecoded << 6) | utf8_decodebyte(c[i], &type);
+ if (type != 0)
+ return j;
+ }
+ if (j < len)
+ return 0;
+ *u = udecoded;
+ utf8_validate(u, len);
+
+ return len;
+}
+
+int
+utf8_decode_buffer(const char* buffer, const int buflen, rune_t* u)
+{
+ if (!buflen) return 0;
+
+ rune_t u_tmp;
+ int charsize;
+ if (!u)
+ u = &u_tmp;
+
+ // process a complete utf8 char
+ charsize = utf8_decode(buffer, u, buflen);
+
+ return charsize;
+}
+
+void
+utf8_remove_string_end(char* string)
+{
+ char* end = string + strlen(string);
+ if (end == string)
+ return;
+
+ do {
+ end--;
+ // if byte starts with 0b10, byte is an UTF-8 extender
+ } while (end > string && (*end & 0xC0) == 0x80);
+ *end = 0;
+}
diff --git a/utf8.h b/utf8.h
@@ -0,0 +1,19 @@
+#ifndef UTF8_H_
+#define UTF8_H_
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <wchar.h>
+
+#define UTF_INVALID 0xFFFD
+#define UTF_SIZ 4
+
+typedef uint_least32_t rune_t;
+
+size_t utf8_encode(rune_t, char *);
+int utf8_decode_buffer(const char* buffer, const int buflen, rune_t* u);
+void utf8_remove_string_end(char* string);
+
+
+#endif // UTF8_H_
diff --git a/x.c b/x.c
@@ -5,11 +5,9 @@
** Most of that part is unchanged from ST (https://st.suckless.org/)
** the main() function and the main loop are found at the very bottom of this file
** there are a very few functions here that are interresting for configuratinos.
-** I would suggest looking into x.h and seeing if any of the callbacks fit
-** your configuration needs.
*/
-#include <assert.h>
+
#include <errno.h>
#include <math.h>
#include <locale.h>
@@ -18,46 +16,17 @@
#include <stdarg.h>
#include <unistd.h>
#include <dirent.h>
+#include <assert.h>
+#include "se.h"
#include "x.h"
-
-/////////////////////////////////////////////////////
-// config.c variables that must be defined
-//
-
-extern int border_px;
-extern float cw_scale;
-extern float ch_scale;
-extern char* fontconfig;
-extern const char* const colors[];
-extern Glyph default_attributes;
-extern unsigned int cursor_fg;
-extern unsigned int cursor_bg;
-extern unsigned int cursor_thickness;
-extern unsigned int cursor_shape;
-extern unsigned int default_cols;
-extern unsigned int default_rows;
-
-// callbacks
-extern void(*draw_callback)(void);
-extern void(*buffer_contents_updated)(struct file_buffer* modified_fb, int current_pos, enum buffer_content_reason reason);
-// non-zero return value means the kpress function will not proceed further
-extern int(*keypress_callback)(KeySym keycode, int modkey);
-extern void(*string_input_callback)(const char* buf, int len);
-extern void(*startup_callback)(void);
-// TODO: planned callbacks:
-// buffer focused
-// window focused
+#include "config.h"
+#include "extension.h"
//////////////////////////////////
// macros
//
-// X modifiers
-#define XK_ANY_MOD UINT_MAX
-#define XK_NO_MOD 0
-#define XK_SWITCH_MOD (1<<13|1<<14)
-
// XEMBED messages
#define XEMBED_FOCUS_IN 4
#define XEMBED_FOCUS_OUT 5
@@ -72,21 +41,98 @@ extern void(*startup_callback)(void);
#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || (a).bg != (b).bg)
#define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
+#include <X11/Xatom.h>
+#include <X11/cursorfont.h>
+#include <X11/Xft/Xft.h>
+#include <X11/XKBlib.h>
+
+// Purely graphic info
+typedef struct {
+ int tw, th; // tty width and height
+ int w, h; // window width and height
+ int ch; // char height
+ int cw; // char width
+ int mode; // window state/mode flags
+} TermWindow;
+extern TermWindow win;
+
+typedef XftDraw *Draw;
+typedef XftColor Color;
+typedef XftGlyphFontSpec GlyphFontSpec;
+
+typedef struct {
+ Display *dpy;
+ Colormap cmap;
+ Window win;
+ Drawable buf;
+ GlyphFontSpec *specbuf; // font spec buffer used for rendering
+ Atom xembed, wmdeletewin, netwmname, netwmiconname, netwmpid;
+ struct {
+ XIM xim;
+ XIC xic;
+ XPoint spot;
+ XVaNestedList spotlist;
+ } ime;
+ Draw draw;
+ Visual *vis;
+ XSetWindowAttributes attrs;
+ int scr;
+ int isfixed; // is fixed geometry?
+ int l, t; // left and top offset
+ int gm; // geometry mask
+} XWindow;
+extern XWindow xw;
+
+// Font structure
+#define Font Font_
+typedef struct {
+ int height;
+ int width;
+ int ascent;
+ int descent;
+ int badslant;
+ int badweight;
+ short lbearing;
+ short rbearing;
+ XftFont *match;
+ FcFontSet *set;
+ FcPattern *pattern;
+} Font;
+
+// Font Ring Cache
+enum {
+ FRC_NORMAL,
+ FRC_ITALIC,
+ FRC_BOLD,
+ FRC_ITALICBOLD
+};
+
+typedef struct {
+ XftFont *font;
+ int flags;
+ rune_t unicodep;
+} Fontcache;
+
+extern Fontcache *frc;
+extern int frclen;
+
+// Drawing Context
+typedef struct {
+ Color *col;
+ size_t collen;
+ Font font, bfont, ifont, ibfont;
+ GC gc;
+} DC;
+extern DC dc;
+
////////////////////////////////////////
// Internal Functions
//
-static int file_browser_actions(KeySym keysym, int modkey);
-static void file_browser_string_insert(const char* buf, int buflen);
-static int buffer_search_actions(KeySym keysym, int modkey);
-static void buffer_search_string_insert(const char* buf, int buflen);
-
-static int search_term_actions(KeySym keysym, int modkey);
-static void search_term_string_insert(const char* buf, int buflen);
-static int open_seproj(struct file_buffer fb);
-static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int);
-static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int);
-static void xdrawglyph(Glyph, int, int);
+static void xunloadfont(Font *);
+static int xmakeglyphfontspecs(XftGlyphFontSpec *, const struct glyph *, int, int, int);
+static void xdrawglyphfontspecs(const XftGlyphFontSpec *, struct glyph, int, int, int);
+static void xdrawglyph(struct glyph, int, int);
static void xclear(int, int, int, int);
static int xgeommasktogravity(int);
static int ximopen(Display *);
@@ -135,26 +181,20 @@ static void (*handler[LASTEvent])(XEvent *) = {
// Globals
//
-extern Term term;
+struct screen screen;
+struct glyph global_attr;
-static struct file_buffer* file_buffers;
-static int available_buffer_slots = 0;
+static Atom xtarget;
+static char* copy_buffer;
+static int copy_len;
-static char root_node_search[SEARCH_TERM_MAX_LEN];
-struct window_split_node root_node = {.mode = WINDOW_SINGULAR, .search = root_node_search};
-struct window_split_node* focused_node = &root_node;
-struct window_buffer* focused_window = &root_node.window;
-
-Atom xtarget;
-char* copy_buffer;
-int copy_len;
TermWindow win;
-DC dc;
XWindow xw;
+DC dc;
// Fontcache is an array. A new font will be appended to the array.
-int frccap = 0;
Fontcache *frc = NULL;
+int frccap = 0;
int frclen = 0;
double defaultfontsize = 0;
double usedfontsize = 0;
@@ -163,157 +203,171 @@ double usedfontsize = 0;
// function implementations
//
-struct file_buffer*
-get_file_buffer(struct window_buffer* buf)
+void
+screen_init(int col, int row)
{
- assert(buf);
- assert(file_buffers);
-
- if (buf->buffer_index < 0)
- buf->buffer_index = available_buffer_slots-1;
- else if (buf->buffer_index >= available_buffer_slots)
- buf->buffer_index = 0;
-
- if (!file_buffers[buf->buffer_index].contents) {
- for(int n = buf->buffer_index; n < available_buffer_slots; n++) {
- if (file_buffers[n].contents) {
- buf->buffer_index = n;
- assert(file_buffers[n].contents);
- return &file_buffers[n];
- }
- }
- for(int n = 0; n < available_buffer_slots; n++) {
- if (file_buffers[n].contents) {
- buf->buffer_index = n;
- assert(file_buffers[n].contents);
- return &file_buffers[n];
- }
- }
- } else {
- assert(file_buffers[buf->buffer_index].contents);
- return &file_buffers[buf->buffer_index];
- }
+ global_attr = default_attributes;
- buf->buffer_index = new_file_buffer_entry(NULL);
- writef_to_status_bar("all buffers were somehow deleted, creating new one");
- return get_file_buffer(buf);
+ screen.col = 0;
+ screen.row = 0;
+ screen.lines = NULL;
+ screen_resize(col, row);
}
-
-int
-open_seproj(struct file_buffer fb)
+void
+draw_horisontal_line(int y, int x1, int x2)
{
- int first = -1;
+ if (y < 0 || y > screen.row ||
+ x2 < x1 || x2 > screen.col ||
+ x1 < 0 || x1 > x2-1)
+ return;
- char* path = file_path_get_path(fb.file_path);
- chdir(path);
+ 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);
+}
- int offset = -1;
- while((offset = buffer_seek_char(&fb, offset+1, '\n')) >= 0) {
- char* line = buffer_get_line_at_offset(&fb, offset);
- if (strlen(line) && !is_file_type(line, ".seproj")) {
- if (first < 0)
- first = new_file_buffer_entry(line);
- else
- new_file_buffer_entry(line);
- }
- free(line);
- }
+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);
+}
- if (first < 0)
- first = new_file_buffer_entry(NULL);
- writef_to_status_bar("opened project %s", path);
- free(path);
+void
+execute_clipbaord_event()
+{
+ Atom clipboard;
- buffer_destroy(&fb);
- return first;
+ clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
+ XConvertSelection(xw.dpy, clipboard, xtarget, clipboard,
+ xw.win, CurrentTime);
}
int
-new_file_buffer_entry(const char* file_path)
+screen_set_char(rune_t u, int x, int y)
{
- static char full_path[PATH_MAX];
- if (!file_path)
- file_path = "./";
- assert(strlen(file_path) < PATH_MAX);
-
- 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) {
- writef_to_status_bar("buffer exits");
- return n;
- }
- }
- }
- } else {
- strcpy(full_path, file_path);
- }
+ struct glyph attr = global_attr;
+ if (y >= screen.row || x >= screen.col ||
+ y < 0 || x < 0)
+ return 1;
- for(int n = 0; n < available_buffer_slots; n++) {
- if (!file_buffers[n].contents) {
- if (is_file_type(full_path, ".seproj"))
- return open_seproj(buffer_new(full_path));
- file_buffers[n] = buffer_new(full_path);
- return n;
- }
+ if (u == 0)
+ u = screen.lines[y][x].u;
+ int width = wcwidth(u);
+ if (width == -1)
+ width = 1;
+ else if (width > 1)
+ attr.mode |= ATTR_WIDE;
+
+ if (screen.lines[y][x].mode & ATTR_WIDE || attr.mode & ATTR_WIDE) {
+ if (x+1 < screen.col) {
+ screen.lines[y][x+1].u = ' ';
+ screen.lines[y][x+1].mode |= ATTR_WDUMMY;
}
+ } else if (screen.lines[y][x].mode & ATTR_WDUMMY && x-1 >= 0) {
+ screen.lines[y][x-1].u = ' ';
+ screen.lines[y][x-1].mode &= ~ATTR_WIDE;
}
- if (is_file_type(full_path, ".seproj"))
- return open_seproj(buffer_new(full_path));
+ screen.lines[y][x] = attr;
+ screen.lines[y][x].u = u;
- available_buffer_slots++;
- file_buffers = xrealloc(file_buffers, sizeof(struct file_buffer) * available_buffer_slots);
- file_buffers[available_buffer_slots-1] = buffer_new(full_path);
+ return width;
+}
- return available_buffer_slots-1;
+void*
+xmalloc(size_t len)
+{
+ void *p;
+ if (!(p = malloc(len)))
+ die("malloc: error, reutrned NULL | errno: %s\n", strerror(errno));
+ return p;
}
-int
-destroy_file_buffer_entry(struct window_split_node* node, struct window_split_node* root)
+void*
+xrealloc(void *p, size_t len)
{
- // do not allow deletion of the lst file buffer
- int n = 0;
- for(; n < available_buffer_slots; n++)
- if (file_buffers[n].contents && n != node->window.buffer_index)
- break;
- if (n >= available_buffer_slots) {
- writef_to_status_bar("can't delete last buffer");
- return 0;
- }
+ if ((p = realloc(p, len)) == NULL)
+ die("realloc: error, returned NULL | errno: %s\n", strerror(errno));
+ return p;
+}
- if (window_other_nodes_contain_file_buffer(node, root)) {
- node->window.buffer_index++;
- node->window = window_buffer_new(node->window.buffer_index);
- writef_to_status_bar("swapped buffer");
- return 0;
- }
- buffer_destroy(get_file_buffer(&node->window));
+void
+die(const char *errstr, ...)
+{
+ va_list ap;
- if (node->window.mode == WINDOW_BUFFER_FILE_BROWSER)
- node->window = window_buffer_new(node->window.cursor_col);
- else
- node->window = window_buffer_new(node->window.buffer_index);
+ va_start(ap, errstr);
+ vfprintf(stderr, errstr, ap);
+ va_end(ap);
+ assert(0);
+}
- return 1;
+////////////////////////////////////////////////
+// X11 and drawing
+//
+
+struct glyph*
+screen_set_attr(int x, int y)
+{
+ static struct glyph dummy;
+ if (y >= screen.row || x >= screen.col ||
+ y < 0 || x < 0)
+ return &dummy;
+
+ return &screen.lines[y][x];
}
-int
-delete_selection(struct file_buffer* buf)
+void
+screen_set_region(int x1, int y1, int x2, int y2, rune_t u)
{
- if (buf->mode & BUFFER_SELECTION_ON) {
- buffer_remove_selection(buf);
- buffer_move_cursor_to_selection_start(focused_window);
- buf->mode &= ~(BUFFER_SELECTION_ON);
- return 1;
+ for (int y = y1; y <= y2; y++)
+ for (int x = x1; x <= x2; x++)
+ screen_set_char(u, x, y);
+}
+
+void
+screen_resize(int col, int row)
+{
+ if (col < 1 || row < 1) {
+ fprintf(stderr,
+ "tresize: error resizing to %dx%d\n", col, row);
+ return;
}
- return 0;
+
+ // resize to new height
+ for (int i = row; i < screen.row; i++)
+ free(screen.lines[i]);
+
+ screen.lines = xrealloc(screen.lines, row * sizeof(*screen.lines));
+
+ for (int i = screen.row; i < row; i++)
+ screen.lines[i] = NULL;
+
+ // resize each row to new width, zero-pad if needed
+ for (int i = 0; i < row; i++) {
+ screen.lines[i] = xrealloc(screen.lines[i], col * sizeof(struct glyph));
+ memset(screen.lines[i], 0, col * sizeof(struct glyph));
+ }
+
+ // update terminal size
+ screen.col = col;
+ screen.row = row;
}
+
+
+
void
propnotify(XEvent *e)
{
@@ -331,9 +385,9 @@ propnotify(XEvent *e)
void
selnotify(XEvent *e)
{
- ulong nitems, ofs, rem;
+ unsigned long nitems, ofs, rem;
int format;
- uchar *data, *last, *repl;
+ uint8_t *data, *last, *repl;
Atom type, incratom, property = None;
incratom = XInternAtom(xw.dpy, "INCR", 0);
@@ -390,18 +444,15 @@ selnotify(XEvent *e)
*repl++ = '\n';
}
- struct file_buffer* fb = get_file_buffer(focused_window);
+ struct file_buffer* fb = get_fb(focused_window);
if (fb->contents) {
- if (fb->mode & BUFFER_SELECTION_ON) {
- buffer_remove_selection(fb);
- buffer_move_cursor_to_selection_start(focused_window);
- fb->mode &= ~(BUFFER_SELECTION_ON);
+ if (fb->mode & FB_SELECTION_ON) {
+ fb_remove_selection(fb);
+ wb_move_cursor_to_selection_start(focused_window);
+ fb->mode &= ~(FB_SELECTION_ON);
}
- int offset = focused_window->cursor_offset;
- buffer_insert(fb, (char*)data, nitems * format / 8, offset, 1);
- buffer_move_to_offset(focused_window, offset + (nitems * format / 8), 0);
- if (buffer_contents_updated)
- buffer_contents_updated(fb, offset, BUFFER_CONTENT_BIG_CHANGE);
+ call_extension(fb_paste, fb, (char*)data, nitems * format / 8);
+ call_extension(fb_contents_updated, fb, focused_window->cursor_offset, FB_CONTENT_BIG_CHANGE);
}
XFree(data);
/* number of 32-bit chunks returned */
@@ -416,31 +467,6 @@ 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;
@@ -465,7 +491,7 @@ selrequest(XEvent *e)
string = xtarget;
XChangeProperty(xsre->display, xsre->requestor, xsre->property,
XA_ATOM, 32, PropModeReplace,
- (uchar *) &string, 1);
+ (uint8_t *) &string, 1);
xev.property = xsre->property;
} else if (xsre->target == xtarget || xsre->target == XA_STRING) {
/*
@@ -475,7 +501,7 @@ selrequest(XEvent *e)
clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
int sel_len;
if (xsre->selection == XA_PRIMARY) {
- seltext = buffer_get_selection(get_file_buffer(focused_window), &sel_len);
+ seltext = fb_get_selection(get_fb(focused_window), &sel_len);
} else if (xsre->selection == clipboard) {
seltext = copy_buffer;
sel_len = copy_len;
@@ -489,7 +515,7 @@ selrequest(XEvent *e)
XChangeProperty(xsre->display, xsre->requestor,
xsre->property, xsre->target,
8, PropModeReplace,
- (uchar *)seltext, sel_len);
+ (uint8_t *)seltext, sel_len);
xev.property = xsre->property;
if (seltext != copy_buffer)
free(seltext);
@@ -506,9 +532,9 @@ cresize(int width, int height)
{
int col, row;
- if (width != 0)
+ if (width > 0)
win.w = width;
- if (height != 0)
+ if (height > 0)
win.h = height;
col = (win.w - 2 * border_px) / win.cw;
@@ -516,7 +542,7 @@ cresize(int width, int height)
col = MAX(1, col);
row = MAX(1, row);
- tresize(col, row);
+ screen_resize(col, row);
xresize(col, row);
}
@@ -662,7 +688,7 @@ xloadfont(Font *f, FcPattern *pattern)
/*
* Manually configure instead of calling XftMatchFont
* so that we can use the configured pattern for
- * "missing glyph" lookups.
+ * "missing struct glyph" lookups.
*/
configured = FcPatternDuplicate(pattern);
if (!configured)
@@ -947,7 +973,7 @@ xinit(int cols, int rows)
xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False);
XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32,
- PropModeReplace, (uchar *)&thispid, 1);
+ PropModeReplace, (uint8_t *)&thispid, 1);
win.mode = MODE_NUMLOCK;
xsettitle(NULL);
@@ -961,14 +987,14 @@ xinit(int cols, int rows)
}
int
-xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
+xmakeglyphfontspecs(XftGlyphFontSpec *specs, const struct glyph *glyphs, int len, int x, int y)
{
float winx = border_px + x * win.cw, winy = border_px + y * win.ch, xp, yp;
- ushort mode, prevmode = USHRT_MAX;
+ unsigned short mode, prevmode = USHRT_MAX;
Font *font = &dc.font;
int frcflags = FRC_NORMAL;
float runewidth = win.cw;
- Rune rune;
+ rune_t rune;
FT_UInt glyphidx;
FcResult fcres;
FcPattern *fcpattern, *fontpattern;
@@ -977,7 +1003,7 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
int i, f, numspecs = 0;
for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
- /* Fetch rune and mode for current glyph. */
+ /* Fetch rune and mode for current struct glyph. */
rune = glyphs[i].u;
mode = glyphs[i].mode;
@@ -985,7 +1011,7 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
if (mode & ATTR_WDUMMY)
continue;
- /* Determine font for glyph if different from previous glyph. */
+ /* Determine font for struct glyph if different from previous struct glyph. */
if (prevmode != mode) {
prevmode = mode;
font = &dc.font;
@@ -1022,7 +1048,7 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
/* Everything correct. */
if (glyphidx && frc[f].flags == frcflags)
break;
- /* We got a default font for a not found glyph. */
+ /* We got a default font for a not found struct glyph. */
if (!glyphidx && frc[f].flags == frcflags
&& frc[f].unicodep == rune) {
break;
@@ -1093,7 +1119,7 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
}
void
-xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
+xdrawglyphfontspecs(const XftGlyphFontSpec *specs, struct glyph base, int len, int x, int y)
{
int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
int winx = border_px + x * win.cw, winy = border_px + y * win.ch,
@@ -1199,7 +1225,7 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
}
void
-xdrawglyph(Glyph g, int x, int y)
+xdrawglyph(struct glyph g, int x, int y)
{
int numspecs;
XftGlyphFontSpec spec;
@@ -1211,9 +1237,9 @@ xdrawglyph(Glyph g, int x, int y)
void
xdrawcursor(int cx, int cy, int focused)
{
- LIMIT(cx, 0, term.col-1);
- LIMIT(cy, 0, term.row-1);
- Glyph g = term.line[cy][cx];
+ LIMIT(cx, 0, screen.col-1);
+ LIMIT(cy, 0, screen.row-1);
+ struct glyph g = screen.lines[cy][cx];
if (IS_SET(MODE_HIDE)) return;
g.mode &= ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE;
@@ -1222,8 +1248,8 @@ xdrawcursor(int cx, int cy, int focused)
Color drawcol = dc.col[g.bg];
/* draw the new one */
- if (IS_SET(MODE_FOCUSED) && !(get_file_buffer(focused_window)->mode & BUFFER_SELECTION_ON) && focused) {
- switch (win.cursor) {
+ if (IS_SET(MODE_FOCUSED) && !(get_fb(focused_window)->mode & FB_SELECTION_ON) && focused) {
+ switch (cursor_shape) {
case 0: // Blinking Block
case 1: // Blinking Block (Default)
case 2: // Steady Block
@@ -1266,20 +1292,6 @@ 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;
@@ -1310,12 +1322,12 @@ xsettitle(char *p)
void
xdrawline(int x1, int y1, int x2)
{
- LIMIT(y1, 0, term.row);
- LIMIT(x2, 0, term.col);
+ LIMIT(y1, 0, screen.row);
+ LIMIT(x2, 0, screen.col);
LIMIT(x1, 0, x2);
- Line line = term.line[y1];
+ struct glyph* line = screen.lines[y1];
int i, x, ox, numspecs;
- Glyph base, new;
+ struct glyph base, new;
XftGlyphFontSpec *specs = xw.specbuf;
numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
@@ -1369,13 +1381,6 @@ xsetpointermotion(int set)
XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs);
}
-int xsetcursor(int cursor) {
- if (!BETWEEN(cursor, 0, 7)) /* 7: st extension */
- return 1;
- win.cursor = cursor;
- return 0;
-}
-
void xseturgency(int add) {
XWMHints *h = XGetWMHints(xw.dpy, xw.win);
MODBIT(h->flags, add, XUrgencyHint);
@@ -1399,9 +1404,9 @@ xunloadfonts(void)
void
xunloadfont(Font *f)
{
- assert(f);
- assert(f->match);
- assert(f->pattern);
+ soft_assert(f, return;);
+ soft_assert(f->match, return;);
+ soft_assert(f->pattern, return;);
XftFontClose(xw.dpy, f->match);
FcPatternDestroy(f->pattern);
if (f->set)
@@ -1429,237 +1434,60 @@ focus(XEvent *ev)
}
int
-file_browser_actions(KeySym keysym, int modkey)
-{
- static char full_path[PATH_MAX];
- struct file_buffer* fb = get_file_buffer(focused_window);
- int offset = fb->len;
-
- switch (keysym) {
- float new_font_size;
- int new_fb;
- case XK_BackSpace:
- if (offset <= 0) {
- char* dest = strrchr(fb->file_path, '/');
- if (dest && dest != fb->file_path) *dest = 0;
- return 1;
- }
-
- focused_window->cursor_offset = offset;
- buffer_move_on_line(focused_window, -1, 0);
- offset = focused_window->cursor_offset;
-
- buffer_remove(fb, offset, 1, 0, 0);
- focused_node->selected = 0;
- return 1;
- case XK_Tab:
- case XK_Return:
- {
- char* path = file_path_get_path(fb->file_path);
- buffer_change(fb, "\0", 1, fb->len, 1);
- if (fb->len > 0) fb->len--;
-
- file_browser_next_item(NULL, NULL, NULL, NULL, NULL);
- const char* filename;
- for (int y = 0; (filename = file_browser_next_item(path, fb->contents, NULL, NULL, NULL)); y++) {
- strcpy(full_path, path);
- strcat(full_path, filename);
- if (y == focused_node->selected) {
- if (path_is_folder(full_path)) {
- strcpy(fb->file_path, full_path);
-
- fb->len = 0;
- fb->contents[0] = '\0';
- focused_node->selected = 0;
-
- free(path);
- file_browser_next_item(NULL, NULL, NULL, NULL, NULL);
- *full_path = 0;
- return 1;
- }
- goto open_file;
- }
- }
-
- if (fb->contents[fb->len-1] == '/') {
- free(path);
- *full_path = 0;
- return 1;
- }
-
- strcpy(full_path, path);
- strcat(full_path, fb->contents);
-open_file:
- new_fb = new_file_buffer_entry(full_path);
- destroy_file_buffer_entry(focused_node, &root_node);
- focused_node->window = window_buffer_new(new_fb);
- free(path);
- *full_path = 0;
- return 1;
- }
- case XK_Down:
- focused_node->selected++;
- return 1;
- case XK_Up:
- focused_node->selected--;
- if (focused_node->selected < 0)
- focused_node->selected = 0;
- return 1;
- case XK_Escape:
- if (destroy_file_buffer_entry(focused_node, &root_node))
- writef_to_status_bar("file browser clsoed");
- return 1;
-
- case XK_Page_Down:
- new_font_size = usedfontsize-1.0;
- goto set_fontsize;
- case XK_Page_Up:
- new_font_size = usedfontsize+1.0;
- goto set_fontsize;
- case XK_Home:
- new_font_size = defaultfontsize;
- set_fontsize:
- xunloadfonts();
- xloadfonts(fontconfig, new_font_size);
- cresize(0, 0);
- xhints();
- return 1;
- }
- return 0;
+match(unsigned int mask, unsigned int state) {
+ const unsigned int ignoremod = Mod2Mask|XK_SWITCH_MOD;
+ return mask == XK_ANY_MOD || mask == (state & ~ignoremod);
}
-void
-file_browser_string_insert(const char* buf, int buflen)
+static void
+search_term_string_insert(const char* buf, int buflen)
{
- static char full_path[PATH_MAX];
- struct file_buffer* fb = get_file_buffer(focused_window);
+ static int first = 0;
- if (fb->len + buflen + strlen(fb->file_path) > PATH_MAX)
+ struct file_buffer* fb = get_fb(focused_window);
+ if (!buf) {
+ first = 1;
return;
-
- if (buf[0] >= 32 || buflen > 1) {
- buffer_insert(fb, buf, buflen, fb->len, 0);
- focused_node->selected = 0;
-
- if (*buf == '/') {
- buffer_change(fb, "\0", 1, fb->len, 0);
- if (fb->len > 0) fb->len--;
- char* path = file_path_get_path(fb->file_path);
- strcpy(full_path, path);
- strcat(full_path, fb->contents);
-
- free(path);
-
- if (path_is_folder(full_path)) {
- file_browser_actions(XK_Return, 0);
- return;
- }
- }
- } else {
- writef_to_status_bar("unhandled control character 0x%x\n", buf[0]);
}
-}
-
-int
-buffer_search_actions(KeySym keysym, int modkey)
-{
- switch (keysym) {
- case XK_Return:
- case XK_Tab:
- if (focused_window->mode == WINDOW_BUFFER_KEYWORD_ALL_BUFFERS) {
- struct keyword_pos kw;
- int n = 0;
- buffers_search_keyword_next_item(NULL, NULL, NULL, NULL, NULL);
- while (buffers_search_keyword_next_item("", focused_node->search, NULL, NULL, &kw)) {
- if (n == focused_node->selected) {
- *focused_window = window_buffer_new(kw.buffer_index);
- focused_window->cursor_offset = kw.offset;
- return 1;
- }
- n++;
- }
- buffers_search_keyword_next_item(NULL, NULL, NULL, NULL, NULL);
- } else if (focused_window->mode == WINDOW_BUFFER_SEARCH_BUFFERS) {
- int buffer_index;
- int n = 0;
- buffer_search_next_item(NULL, NULL, NULL, NULL, NULL);
- while (buffer_search_next_item("", focused_node->search, NULL, NULL, &buffer_index)) {
- if (n == focused_node->selected) {
- *focused_window = window_buffer_new(buffer_index);
- return 1;
- }
- n++;
- }
- buffer_search_next_item(NULL, NULL, NULL, NULL, NULL);
- }
- puts("n");
- return 1;
- case XK_BackSpace:
- remove_utf8_string_end(focused_node->search);
- focused_node->selected = 0;
- return 1;
- case XK_Down:
- focused_node->selected++;
- return 1;
- case XK_Up:
- focused_node->selected--;
- if (focused_node->selected < 0)
- focused_node->selected = 0;
- return 1;
- case XK_Page_Down:
- focused_node->selected += 10;
- return 1;
- case XK_Page_Up:
- focused_node->selected -= 10;
- if (focused_node->selected < 0)
- focused_node->selected = 0;
- return 1;
- case XK_Escape:
- if (path_is_folder(get_file_buffer(focused_window)->file_path))
- focused_window->mode = WINDOW_BUFFER_FILE_BROWSER;
- else
- focused_window->mode = WINDOW_BUFFER_NORMAL;
- return 1;
+ if (first) {
+ *fb->search_term = 0;
+ first = 0;
}
- return 0;
-}
-
-void
-buffer_search_string_insert(const char* buf, int buflen)
-{
- int len = strlen(focused_node->search);
- if (buflen + len + 1 > SEARCH_TERM_MAX_LEN)
- return;
-
if (buf[0] >= 32 || buflen > 1) {
- memcpy(focused_node->search + len, buf, buflen);
- focused_node->search[len + buflen] = 0;
- focused_node->selected = 0;
- } else {
- writef_to_status_bar("unhandled control character 0x%x\n", buf[0]);
+ int len = strlen(fb->search_term);
+ if (len + buflen + 1 > SEARCH_TERM_MAX_LEN)
+ return;
+ memcpy(fb->search_term + len, buf, buflen);
+ fb->search_term[len + buflen] = 0;
+ if (fb->mode & FB_SEARCH_BLOCKING_BACKWARDS)
+ focused_window->cursor_offset = fb_seek_string_backwards(fb, focused_window->cursor_offset, fb->search_term);
+ else
+ focused_window->cursor_offset = fb_seek_string(fb, focused_window->cursor_offset, fb->search_term);
+ writef_to_status_bar("search: %s", fb->search_term);
}
}
-int
+
+static int
search_term_actions(KeySym keysym, int modkey)
{
static int first = 0;
- struct file_buffer* fb = get_file_buffer(focused_window);
+ struct file_buffer* fb = get_fb(focused_window);
if (keysym == XK_Return || keysym == XK_Escape) {
- int count = buffer_count_string_instances(fb, fb->search_term, 0, NULL);
+ int count = fb_count_string_instances(fb, fb->search_term, 0, NULL);
if (!count) {
- fb->mode &= ~BUFFER_SEARCH_BLOCKING_MASK;
+ fb->mode &= ~FB_SEARCH_BLOCKING_MASK;
writef_to_status_bar("no resulrs for \"%s\"", fb->search_term);
} else {
- fb->mode &= ~BUFFER_SEARCH_BLOCKING;
- fb->mode &= ~BUFFER_SEARCH_BLOCKING_BACKWARDS;
- fb->mode |= BUFFER_SEARCH_BLOCKING_IDLE;
+ fb->mode &= ~FB_SEARCH_BLOCKING;
+ fb->mode &= ~FB_SEARCH_BLOCKING_BACKWARDS;
+ fb->mode |= FB_SEARCH_BLOCKING_IDLE;
writef_to_status_bar("%d results for \"%s\"", count, fb->search_term);
- if (fb->mode & BUFFER_SEARCH_BLOCKING_BACKWARDS)
- focused_window->cursor_offset = buffer_seek_string_backwards(fb, focused_window->cursor_offset, fb->search_term);
+ if (fb->mode & FB_SEARCH_BLOCKING_BACKWARDS)
+ focused_window->cursor_offset = fb_seek_string_backwards(fb, focused_window->cursor_offset, fb->search_term);
else
- focused_window->cursor_offset = buffer_seek_string(fb, focused_window->cursor_offset, fb->search_term);
+ focused_window->cursor_offset = fb_seek_string(fb, focused_window->cursor_offset, fb->search_term);
}
first = 1;
search_term_string_insert(NULL, 0);
@@ -1670,8 +1498,8 @@ search_term_actions(KeySym keysym, int modkey)
first = 0;
*fb->search_term = 0;
} else {
- remove_utf8_string_end(fb->search_term);
- focused_window->cursor_offset = buffer_seek_string_wrap(focused_window, focused_window->cursor_offset, fb->search_term);
+ utf8_remove_string_end(fb->search_term);
+ focused_window->cursor_offset = wb_seek_string_wrap(focused_window, focused_window->cursor_offset, fb->search_term);
}
writef_to_status_bar("search: %s", fb->search_term);
return 1;
@@ -1680,38 +1508,6 @@ search_term_actions(KeySym keysym, int modkey)
return 0;
}
-void
-search_term_string_insert(const char* buf, int buflen)
-{
- static int first = 0;
-
- struct file_buffer* fb = get_file_buffer(focused_window);
- if (!buf) {
- first = 1;
- return;
- }
- if (first) {
- *fb->search_term = 0;
- first = 0;
- }
- if (buf[0] >= 32 || buflen > 1) {
- int len = strlen(fb->search_term);
- if (len + buflen + 1 > SEARCH_TERM_MAX_LEN)
- return;
- memcpy(fb->search_term + len, buf, buflen);
- fb->search_term[len + buflen] = 0;
- if (fb->mode & BUFFER_SEARCH_BLOCKING_BACKWARDS)
- focused_window->cursor_offset = buffer_seek_string_backwards(fb, focused_window->cursor_offset, fb->search_term);
- else
- focused_window->cursor_offset = buffer_seek_string(fb, focused_window->cursor_offset, fb->search_term);
- writef_to_status_bar("search: %s", fb->search_term);
- }
-}
-
-int match(uint mask, uint state) {
- const uint ignoremod = Mod2Mask|XK_SWITCH_MOD;
- return mask == XK_ANY_MOD || mask == (state & ~ignoremod);
-}
void
kpress(XEvent *ev)
@@ -1720,54 +1516,46 @@ kpress(XEvent *ev)
KeySym ksym;
char buf[64];
int len;
- Rune c;
+ rune_t c;
Status status;
if (IS_SET(MODE_KBDLOCK))
return;
if (xw.ime.xic)
- len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status);
+ len = XmbLookupString(xw.ime.xic, e, buf, sizeof(buf), &ksym, &status);
else
- len = XLookupString(e, buf, sizeof buf, &ksym, NULL);
- const struct file_buffer* fb = get_file_buffer(focused_window);
+ len = XLookupString(e, buf, sizeof(buf), &ksym, NULL);
+ if (len == 1 && e->state & Mod1Mask) {
+ if (*buf < 0177) {
+ c = *buf | 0x80;
+ len = utf8_encode(c, buf);
+ }
+ }
+ const struct file_buffer* fb = get_fb(focused_window);
// keysym callback
- if (focused_window->mode == WINDOW_BUFFER_FILE_BROWSER) {
- if (file_browser_actions(ksym, e->state))
- return;
- } else if (focused_window->mode & WINDOW_BUFFER_SEARCH_BUFFERS ||
- focused_window->mode & WINDOW_BUFFER_KEYWORD_ALL_BUFFERS) {
- if (buffer_search_actions(ksym, e->state))
- return;
- } else if (fb->mode & BUFFER_SEARCH_BLOCKING) {
+ if (fb->mode & FB_SEARCH_BLOCKING) {
if (search_term_actions(ksym, e->state))
return;
- } else if (keypress_callback) {
- if (keypress_callback(ksym, e->state))
- return;
+ }
+ if (fb->mode & FB_SEARCH_BLOCKING) {
+ search_term_string_insert(buf, len);
+ return;
}
- // composed string from input method and send to callback
- if (string_input_callback) {
- if (len == 0)
+ if (focused_window->mode != WB_NORMAL) {
+ int override = 0;
+ call_extension(wn_custom_window_keypress_override, &override, focused_node, ksym, e->state, buf, len);
+ if (override)
return;
- if (len == 1 && e->state & Mod1Mask) {
- if (*buf < 0177) {
- c = *buf | 0x80;
- len = utf8encode(c, buf);
- }
- }
- if (focused_window->mode == WINDOW_BUFFER_FILE_BROWSER)
- file_browser_string_insert(buf, len);
- else if (focused_window->mode & WINDOW_BUFFER_SEARCH_BUFFERS ||
- focused_window->mode & WINDOW_BUFFER_KEYWORD_ALL_BUFFERS)
- buffer_search_string_insert(buf, len);
- else if (fb->mode & BUFFER_SEARCH_BLOCKING)
- search_term_string_insert(buf, len);
- else
- string_input_callback(buf, len);
+
+ int wn_custom_window_keypress_override_callback_exists = 0;
+ extension_callback_exists(wn_custom_window_keypress_override, wn_custom_window_keypress_override_callback_exists = 1;);
+ soft_assert(wn_custom_window_keypress_override_callback_exists, );
}
+
+ call_extension(keypress, ksym, e->state, buf, len);
}
void
@@ -1794,7 +1582,7 @@ resize(XEvent *e)
return;
cresize(e->xconfigure.width, e->xconfigure.height);
- writef_to_status_bar("window resize: %d:%d", term.col, term.row);
+ writef_to_status_bar("window resize: %d:%d", screen.col, screen.row);
}
void
@@ -1822,6 +1610,7 @@ run(void)
cresize(w, h);
for (;;) {
+ struct timespec ts_start;
int xev = 0;
while (XPending(xw.dpy)) {
@@ -1829,6 +1618,7 @@ run(void)
if (XFilterEvent(&ev, None))
continue;
if (handler[ev.type]) {
+ clock_gettime(CLOCK_MONOTONIC, &ts_start);
(handler[ev.type])(&ev);
xev = 1;
}
@@ -1839,42 +1629,39 @@ run(void)
continue;
}
- tsetregion(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);
+ screen_set_region(0, 0, screen.col-1, screen.row-1, ' ');
+ if (screen.row-2 >= 0)
+ window_node_draw_tree_to_screen(&root_node, 0, 0, screen.col-1, screen.row-1);
draw_status_bar();
- if (draw_callback)
- draw_callback();
-
xfinishdraw();
XFlush(xw.dpy);
+
+ struct timespec ts_end;
+ clock_gettime(CLOCK_MONOTONIC, &ts_end);
+ double dif = 0;
+ dif = (ts_end.tv_sec - ts_start.tv_sec) * 1e6;
+ dif += (ts_end.tv_nsec - ts_start.tv_nsec) * 1e-3;
+ printf("%lf\n", dif);
}
}
int
main(int argc, char *argv[])
{
- if (startup_callback) {
- startup_callback();
- }
xw.l = xw.t = 0;
xw.isfixed = False;
- xsetcursor(cursor_shape);
setlocale(LC_CTYPE, "");
XSetLocaleModifiers("");
int cols = MAX(default_cols, 1);
int rows = MAX(default_rows, 1);
- tnew(cols, rows);
+ screen_init(cols, rows);
xinit(cols, rows);
xsetenv();
- // TODO: fix things when the file buffer is empty
- // for example on a new file
-
if (argc <= 1) {
- *focused_window = window_buffer_new(new_file_buffer_entry(NULL));
+ *focused_window = wb_new(fb_new_entry(NULL));
} else {
int master_stack = 1;
for (int i = 1; i < argc; i++) {
@@ -1885,7 +1672,7 @@ main(int argc, char *argv[])
window_node_split(focused_node, 0.5, WINDOW_HORISONTAL);
master_stack = 0;
} else if (master_stack > 0) {
- *focused_window = window_buffer_new(new_file_buffer_entry(argv[i]));
+ *focused_window = wb_new(fb_new_entry(argv[i]));
master_stack = -1;
continue;
} else {
@@ -1894,23 +1681,27 @@ main(int argc, char *argv[])
if (focused_node->node2) {
focused_node = focused_node->node2;
- focused_window = &focused_node->window;
+ focused_window = &focused_node->wb;
if (!master_stack)
- *focused_window = window_buffer_new(new_file_buffer_entry(argv[i]));
+ *focused_window = wb_new(fb_new_entry(argv[i]));
}
master_stack = 0;
}
}
}
- static const char* const welcome[] = {"Welcome to se!", "Good day, Human", "Happy Coding", "se: the Simple Editor",
- "Time to get some progress done!", "Ready for combat", "Initialising...Done", "loaded in %%d seconds",
- "Fun fact: vscode has over two times as many lines describing dependencies than se has in total",
- "You look based", "Another day, another bug to fix", "Who needs a mouse ¯\\_(ツ)_/¯", "grrgrrggghhaaaaaa (╯°□ °)╯︵ ┻━┻",
- "┬┴┬┤(・_├┬┴┬┤├┬┴┬┴┬┤ʖ ͡°) ├┬┴┬┴┬┴┬┴┬┴┬┴┬┴┬┴┬┴┬┴", "ʰᵉˡˡᵒ", "(=^ ◡ ^=)"};
-
srand(time(NULL));
- writef_to_status_bar(welcome[rand() % LEN(welcome)]);
+
+ // TODO: start screen extension
+
+ if (extensions) {
+ for (int i = 0; !extensions[i].end; i++) {
+ if (extensions[i].e.init)
+ extensions[i].e.init(&extensions[i].e);
+ if (extensions[i].e.enable)
+ extensions[i].e.enable();
+ }
+ }
run();
diff --git a/x.h b/x.h
@@ -1,18 +1,81 @@
/* See LICENSE for license details. */
+
+/*
+** This file mainly contains X11 stuff (drawing to the screen, window hints, etc)
+** Most of that part is unchanged from ST (https://st.suckless.org/)
+** the main() function and the main loop are found at the very bottom of this file
+** there are a very few functions here that are interresting for configuratinos.
+*/
+
#ifndef _X_H
#define _X_H
-#include "se.h"
-#undef Glyph
+#include "utf8.h"
-#include <X11/Xatom.h>
#include <X11/Xlib.h>
-#include <X11/cursorfont.h>
#include <X11/keysym.h>
-#include <X11/Xft/Xft.h>
-#include <X11/XKBlib.h>
+#include <stdio.h>
+#include <limits.h>
+
+void draw_horisontal_line(int y, int x1, int x2);
+// TODO: vertical line
+// !!! NOTE:
+// !!! buffer MUST be malloced and NOT be freed after it is passed
+void set_clipboard_copy(char* buffer, int len);
+void execute_clipbaord_event();
+
+enum glyph_attribute {
+ ATTR_NULL = 0,
+ ATTR_BOLD = 1 << 0,
+ ATTR_FAINT = 1 << 1,
+ ATTR_ITALIC = 1 << 2,
+ ATTR_UNDERLINE = 1 << 3,
+ ATTR_REVERSE = 1 << 5,
+ ATTR_INVISIBLE = 1 << 6,
+ ATTR_STRUCK = 1 << 7,
+ ATTR_WIDE = 1 << 9,
+ ATTR_WDUMMY = 1 << 10,
+ ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
+};
+
+struct glyph {
+ rune_t u; // character code
+ uint16_t mode; // attribute flags
+ uint32_t fg; // foreground
+ uint32_t bg; // background
+};
+
+// Internal representation of the screen
+struct screen {
+ int row; // row count
+ int col; // column count
+ struct glyph** lines; // screen letters 2d array
+};
-#define Glyph Glyph_
+extern struct screen screen;
+extern struct glyph global_attr;
+
+void screen_init(int col, int row);
+void screen_resize(int col, int row);
+void screen_set_region(int x1, int y1, int x2, int y2, rune_t u);
+int screen_set_char(rune_t u, int x, int y);
+struct glyph* screen_set_attr(int x, int y);
+
+void* xmalloc(size_t len);
+void* xrealloc(void *p, size_t len);
+void die(const char *, ...);
+
+// the va_args can be used to return; or any other stuff like that
+// TODO: optionally crash the program for debugging
+#define soft_assert(condition, ...) \
+ do { \
+ if(!(condition)) { \
+ fprintf(stderr, "SOFT ASSERT ERROR: (%s) failed at %s %s():%d\n", #condition, __FILE__, __func__, __LINE__); \
+ writef_to_status_bar("SOFT ASSERT ERROR: (%s) failed at %s %s():%d", #condition, __FILE__, __func__, __LINE__); \
+ status_bar_bg = error_color; \
+ __VA_ARGS__ \
+ } \
+ } while(0) \
enum win_mode {
MODE_VISIBLE = 1 << 0,
@@ -28,81 +91,24 @@ enum win_mode {
MODE_NUMLOCK = 1 << 17,
};
-// Purely graphic info
-typedef struct {
- int tw, th; // tty width and height
- int w, h; // window width and height
- int ch; // char height
- int cw; // char width
- int mode; // window state/mode flags
- int cursor; // cursor style
-} TermWindow;
-
-typedef XftDraw *Draw;
-typedef XftColor Color;
-typedef XftGlyphFontSpec GlyphFontSpec;
-
-typedef struct {
- Display *dpy;
- Colormap cmap;
- Window win;
- Drawable buf;
- GlyphFontSpec *specbuf; // font spec buffer used for rendering
- Atom xembed, wmdeletewin, netwmname, netwmiconname, netwmpid;
- struct {
- XIM xim;
- XIC xic;
- XPoint spot;
- XVaNestedList spotlist;
- } ime;
- Draw draw;
- Visual *vis;
- XSetWindowAttributes attrs;
- int scr;
- int isfixed; // is fixed geometry?
- int l, t; // left and top offset
- int gm; // geometry mask
-} XWindow;
-
-// Font structure
-#define Font Font_
-typedef struct {
- int height;
- int width;
- int ascent;
- int descent;
- int badslant;
- int badweight;
- short lbearing;
- short rbearing;
- XftFont *match;
- FcFontSet *set;
- FcPattern *pattern;
-} Font;
-
-// Font Ring Cache
-enum {
- FRC_NORMAL,
- FRC_ITALIC,
- FRC_BOLD,
- FRC_ITALICBOLD
-};
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) < (b) ? (b) : (a))
+#define LEN(a) (sizeof(a) / sizeof(a)[0])
+#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
+#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
+
+////////////////////////////////////////////////
+// X11 and drawing
+//
+
+// X modifiers
+#define XK_ANY_MOD UINT_MAX
+#define XK_NO_MOD 0
+#define XK_SWITCH_MOD (1<<13|1<<14)
+
+extern double defaultfontsize;
+extern double usedfontsize;
-typedef struct {
- XftFont *font;
- int flags;
- Rune unicodep;
-} Fontcache;
-
-// Drawing Context
-typedef struct {
- Color *col;
- size_t collen;
- Font font, bfont, ifont, ibfont;
- GC gc;
-} DC;
-
-void xclipcopy(void);
void xdrawcursor(int, int, int focused);
void xdrawline(int, int, int);
void xfinishdraw(void);
@@ -110,22 +116,11 @@ void xloadcols(void);
void xloadfonts(const char *, double);
int xsetcolorname(int, const char *);
void xseticontitle(char *);
-int xsetcursor(int);
void xsetpointermotion(int);
int xstartdraw(void);
void xunloadfonts(void);
-void xunloadfont(Font *);
void cresize(int, int);
void xhints(void);
-int match(uint, uint);
-
-struct file_buffer* get_file_buffer(struct window_buffer* buf);
-int new_file_buffer_entry(const char* file_path);
-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();
+int match(unsigned int, unsigned int);
#endif // _X_H