summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlbert Cervin <albert@acervin.com>2024-05-22 00:00:29 +0200
committerAlbert Cervin <albert@acervin.com>2024-09-12 20:17:56 +0200
commit405da5f84b072ea97b69359454899f45d92d24b6 (patch)
tree20525b4bc44a5d8cbab4d62abe8413e174731db6
parent4ab7e453e26afc6e9f4938c65f89463fbba9e267 (diff)
downloaddged-405da5f84b072ea97b69359454899f45d92d24b6.tar.gz
dged-405da5f84b072ea97b69359454899f45d92d24b6.tar.xz
dged-405da5f84b072ea97b69359454899f45d92d24b6.zip
WIP LSP client
This contains the start of an LSP client. Nothing (except starting the LSP server) works at the moment and the feature is disabled by default.
-rw-r--r--Makefile28
-rwxr-xr-xconfigure47
-rw-r--r--flake.lock12
-rw-r--r--src/dged/binding.c7
-rw-r--r--src/dged/binding.h14
-rw-r--r--src/dged/buffer.c56
-rw-r--r--src/dged/buffer.h14
-rw-r--r--src/dged/buffer_view.c5
-rw-r--r--src/dged/command.c2
-rw-r--r--src/dged/display.c60
-rw-r--r--src/dged/display.h2
-rw-r--r--src/dged/hash.c20
-rw-r--r--src/dged/hash.h14
-rw-r--r--src/dged/hashmap.h8
-rw-r--r--src/dged/json.c234
-rw-r--r--src/dged/json.h54
-rw-r--r--src/dged/lang.c34
-rw-r--r--src/dged/lsp.c163
-rw-r--r--src/dged/lsp.h75
-rw-r--r--src/dged/minibuffer.c24
-rw-r--r--src/dged/minibuffer.h20
-rw-r--r--src/dged/path.c68
-rw-r--r--src/dged/path.h69
-rw-r--r--src/dged/process-posix.c126
-rw-r--r--src/dged/process.h35
-rw-r--r--src/dged/reactor-epoll.c12
-rw-r--r--src/dged/reactor-kqueue.c2
-rw-r--r--src/dged/reactor.h2
-rw-r--r--src/dged/settings.c22
-rw-r--r--src/dged/settings.h6
-rw-r--r--src/dged/syntax.c27
-rw-r--r--src/dged/syntax.h2
-rw-r--r--src/dged/text.h4
-rw-r--r--src/dged/timers.c8
-rw-r--r--src/dged/timers.h8
-rw-r--r--src/dged/undo.c30
-rw-r--r--src/dged/undo.h6
-rw-r--r--src/dged/utf8.c3
-rw-r--r--src/dged/vec.h3
-rw-r--r--src/dged/window.c18
-rw-r--r--src/dged/window.h16
-rw-r--r--src/main/bindings.c8
-rw-r--r--src/main/bindings.h4
-rw-r--r--src/main/cmds.c182
-rw-r--r--src/main/cmds.h2
-rw-r--r--src/main/completion.c52
-rw-r--r--src/main/completion.h18
-rw-r--r--src/main/lsp.c108
-rw-r--r--src/main/lsp.h11
-rw-r--r--src/main/main.c55
-rw-r--r--src/main/search-replace.c209
-rw-r--r--src/main/search-replace.h2
-rw-r--r--test/allocator.c4
-rw-r--r--test/buffer.c9
-rw-r--r--test/command.c19
-rw-r--r--test/container.c6
-rw-r--r--test/fake-reactor.c6
-rw-r--r--test/json.c95
-rw-r--r--test/keyboard.c21
-rw-r--r--test/main.c12
-rw-r--r--test/minibuffer.c18
-rw-r--r--test/settings.c46
-rw-r--r--test/test.h21
-rw-r--r--test/text.c12
-rw-r--r--test/undo.c8
-rw-r--r--test/utf8.c5
66 files changed, 1718 insertions, 575 deletions
diff --git a/Makefile b/Makefile
index b19e128..a4cec94 100644
--- a/Makefile
+++ b/Makefile
@@ -14,6 +14,7 @@ build:
.include "config.mk"
SYNTAX_ENABLE ?= true
+LSP_ENABLE ?= true
HEADERS = src/dged/settings.h src/dged/minibuffer.h src/dged/keyboard.h src/dged/binding.h \
src/dged/buffers.h src/dged/text.h src/dged/display.h src/dged/hashmap.h src/dged/path.h \
@@ -21,22 +22,27 @@ HEADERS = src/dged/settings.h src/dged/minibuffer.h src/dged/keyboard.h src/dged
src/dged/vec.h src/dged/window.h src/dged/hash.h src/dged/undo.h src/dged/lang.h \
src/dged/settings-parse.h src/dged/utf8.h src/main/cmds.h src/main/bindings.h \
src/main/search-replace.h src/dged/location.h src/dged/buffer_view.h src/main/completion.h \
- src/dged/timers.h src/dged/s8.h src/main/version.h src/config.h
+ src/dged/timers.h src/dged/s8.h src/main/version.h src/config.h src/dged/process.h
SOURCES = src/dged/binding.c src/dged/buffer.c src/dged/command.c src/dged/display.c \
src/dged/keyboard.c src/dged/minibuffer.c src/dged/text.c \
src/dged/utf8.c src/dged/buffers.c src/dged/window.c src/dged/allocator.c src/dged/undo.c \
src/dged/settings.c src/dged/lang.c src/dged/settings-parse.c src/dged/location.c \
- src/dged/buffer_view.c src/dged/timers.c src/dged/s8.c
+ src/dged/buffer_view.c src/dged/timers.c src/dged/s8.c src/dged/path.c src/dged/hash.c
MAIN_SOURCES = src/main/main.c src/main/cmds.c src/main/bindings.c src/main/search-replace.c src/main/completion.c
+# HACK: added to MAIN_SOURCES to not be picked up in tests
+# since they have their own implementation
.if "$(HAS_EPOLL)" == true
MAIN_SOURCES += src/dged/reactor-epoll.c
.elif "$(HAS_KQUEUE)" == true
MAIN_SOURCES += src/dged/reactor-kqueue.c
.endif
+.if "$(PROCESS_MODEL)" == posix
+ SOURCES += src/dged/process-posix.c
+.endif
TEST_SOURCES = test/assert.c test/buffer.c test/text.c test/utf8.c test/main.c \
test/command.c test/keyboard.c test/fake-reactor.c test/allocator.c \
@@ -49,9 +55,9 @@ datadir = share/dged
.SUFFIXES:
.SUFFIXES: .c .o .d
-CFLAGS += -Werror -g -O2 -std=c99 \
- -I $(.CURDIR)/src \
- -I $(.CURDIR)/src/main \
+CFLAGS += -Werror -Wall -Wextra -g -O2 -std=c99\
+ -I $(.CURDIR)/src\
+ -I $(.CURDIR)/src/main\
-DDATADIR="$(prefix)/$(datadir)"
ASAN ?= false
@@ -66,12 +72,20 @@ ASAN ?= false
SOURCES += src/dged/syntax.c
treesitterflags != pkg-config tree-sitter --cflags
- CFLAGS += ${treesitterflags} -DSYNTAX_ENABLE
+ CFLAGS += ${treesitterflags}
treesitterld != pkg-config tree-sitter --libs
LDFLAGS += ${treesitterld}
.endif
+.if $(LSP_ENABLE) == true
+ HEADERS += src/dged/lsp.h src/main/lsp.h src/dged/json.h
+ SOURCES += src/dged/lsp.c src/dged/json.c
+ MAIN_SOURCES += src/main/lsp.c
+ TEST_SOURCES += test/json.c
+ CFLAGS += -DLSP_ENABLED
+.endif
+
UNAME_S != uname -s | tr '[:upper:]' '[:lower:]'
.if exists(${.CURDIR}/${UNAME_S}.mk)
. include "$(.CURDIR)/$(UNAME_S).mk"
@@ -138,7 +152,7 @@ run: dged
./dged
debug: dged
- gdb ./dged
+ gdb -ex "cd $(.CURDIR)" ./dged
debug-tests: run-tests
gdb ./run-tests
diff --git a/configure b/configure
index f95baae..ecc1949 100755
--- a/configure
+++ b/configure
@@ -3,13 +3,15 @@
_usage="./configure -- configure the DGED build.
Options:
- --[enable|disable]-syntax Enable or disable syntax highlighting support.
- --enable-asan Build DGED with address sanitizer enabled.
- --prefix=<PREFIX> Set the build prefix path to <PREFIX>, default: /usr/local.
+ --[enable|disable]-syntax Enable or disable syntax highlighting support. Default: enabled.
+ --[enable|disable]-lsp Enable or disable Language Server Protocol support. Default: disabled (experimental).
+ --enable-asan Build DGED with address sanitizer enabled. Default: disabled.
+ --prefix=<PREFIX> Set the build prefix path to <PREFIX>. Default: /usr/local.
-h/--help Show this help text.
"
enable_syntax=1
+enable_lsp=0
enable_asan=0
prefix=
while [ "$#" -gt 0 ]; do
@@ -24,6 +26,16 @@ while [ "$#" -gt 0 ]; do
shift 1
;;
+ --enable-lsp)
+ enable_lsp=1
+ shift 1
+ ;;
+
+ --disable-lsp)
+ enable_lsp=0
+ shift 1
+ ;;
+
--enable-asan)
enable_asan=1
shift 1
@@ -75,21 +87,42 @@ else
echo "none."
fi
+echo -n "detecting process model..."
+if ./scripts/has_header "unistd.h"; then
+ echo "posix."
+ echo "#define PROCESS_MODEL posix" >> src/config.h
+ echo "PROCESS_MODEL ?= posix" >> config.mk
+else
+ echo "unknown."
+fi
+
if [ "$enable_syntax" -ne 0 ]; then
- echo "enabling syntax highlighting"
+ echo "enabling syntax highlighting."
echo "SYNTAX_ENABLE = true" >> config.mk
+ echo "#define SYNTAX_ENABLE 1" >> src/config.h
else
- echo "disabling syntax highlighting"
+ echo "disabling syntax highlighting."
echo "SYNTAX_ENABLE = false" >> config.mk
+ echo "#undef SYNTAX_ENABLE" >> src/config.h
+fi
+
+if [ "$enable_lsp" -ne 0 ]; then
+ echo "enabling language server support."
+ echo "LSP_ENABLE = true" >> config.mk
+ echo "#define LSP_ENABLE 1" >> src/config.h
+else
+ echo "disabling language server support."
+ echo "LSP_ENABLE = false" >> config.mk
+ echo "#undef LSP_ENABLE" >> src/config.h
fi
if [ "$enable_asan" -ne 0 ]; then
- echo "enabling address sanitizer"
+ echo "enabling address sanitizer."
echo "ASAN = true" >> config.mk
fi
if [ -n "$prefix" ]; then
- echo "setting prefix to \"$prefix\""
+ echo "setting prefix to \"$prefix\"."
echo "prefix = $prefix" >> config.mk
fi
diff --git a/flake.lock b/flake.lock
index 89ef021..4a5def3 100644
--- a/flake.lock
+++ b/flake.lock
@@ -5,11 +5,11 @@
"systems": "systems"
},
"locked": {
- "lastModified": 1705309234,
- "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
+ "lastModified": 1710146030,
+ "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
- "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
+ "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
@@ -20,11 +20,11 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1706373441,
- "narHash": "sha256-S1hbgNbVYhuY2L05OANWqmRzj4cElcbLuIkXTb69xkk=",
+ "lastModified": 1719122173,
+ "narHash": "sha256-aEMsNUtqSPwn6l+LIZ/rX++nCgun3E9M3uSZs6Rwb7w=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "56911ef3403a9318b7621ce745f5452fb9ef6867",
+ "rev": "906320ae02f769d13a646eb3605a9821df0d6ea2",
"type": "github"
},
"original": {
diff --git a/src/dged/binding.c b/src/dged/binding.c
index 5111548..636e694 100644
--- a/src/dged/binding.c
+++ b/src/dged/binding.c
@@ -50,21 +50,22 @@ struct lookup_result lookup_key(struct keymap *keymaps, uint32_t nkeymaps,
return (struct lookup_result){
.found = true,
.type = BindingType_Command,
- .command = lookup_command_by_hash(commands, binding->command),
+ .data.command =
+ lookup_command_by_hash(commands, binding->data.command),
};
}
case BindingType_Keymap: {
return (struct lookup_result){
.found = true,
.type = BindingType_Keymap,
- .keymap = binding->keymap,
+ .data.keymap = binding->data.keymap,
};
}
case BindingType_DirectCommand:
return (struct lookup_result){
.found = true,
.type = BindingType_Command,
- .command = binding->direct_command,
+ .data.command = binding->data.direct_command,
};
}
}
diff --git a/src/dged/binding.h b/src/dged/binding.h
index 79f8c47..93de02d 100644
--- a/src/dged/binding.h
+++ b/src/dged/binding.h
@@ -37,19 +37,19 @@ enum binding_type {
#define BINDING_INNER(mod_, c_, command_) \
(struct binding) { \
.key = {.mod = mod_, .key = c_}, .type = BindingType_Command, \
- .command = hash_name(command_) \
+ .data.command = hash_name(command_) \
}
#define ANONYMOUS_BINDING_INNER(mod_, c_, command_) \
(struct binding) { \
.key = {.mod = mod_, .key = c_}, .type = BindingType_DirectCommand, \
- .direct_command = command_ \
+ .data.direct_command = command_ \
}
#define PREFIX_INNER(mod_, c_, keymap_) \
(struct binding) { \
.key = {.mod = mod_, .key = c_}, .type = BindingType_Keymap, \
- .keymap = keymap_ \
+ .data.keymap = keymap_ \
}
/**
@@ -89,14 +89,14 @@ struct binding {
/** Type of this binding, see @ref binding_type */
uint8_t type;
- union {
+ union binding_data {
/** A hash of a command name */
uint32_t command;
/** A command */
struct command *direct_command;
/** A keymap */
struct keymap *keymap;
- };
+ } data;
};
/**
@@ -109,12 +109,12 @@ struct lookup_result {
/** Type of binding in the result */
uint8_t type;
- union {
+ union lookup_data {
/** A command */
struct command *command;
/** A keymap */
struct keymap *keymap;
- };
+ } data;
};
struct commands;
diff --git a/src/dged/buffer.c b/src/dged/buffer.c
index 1062a47..c537fb3 100644
--- a/src/dged/buffer.c
+++ b/src/dged/buffer.c
@@ -31,7 +31,7 @@ static struct kill_ring {
uint32_t curr_idx;
uint32_t paste_idx;
} g_kill_ring = {.curr_idx = 0,
- .buffer = {0},
+ .buffer = {{0}},
.last_paste = {0},
.paste_idx = 0,
.paste_up_to_date = false};
@@ -58,7 +58,7 @@ static struct kill_ring {
\
static void remove_##name##_hook(vec_type *hooks, uint32_t id, \
remove_hook_cb callback) { \
- uint64_t found_at = -1; \
+ uint64_t found_at = (uint64_t)-1; \
VEC_FOR_EACH_INDEXED(hooks, struct name##_hook *h, idx) { \
if (h->id == id) { \
if (callback != NULL) { \
@@ -68,11 +68,12 @@ static struct kill_ring {
break; \
} \
} \
- if (found_at != -1) { \
+ if (found_at != (uint64_t)-1) { \
if (found_at < VEC_SIZE(hooks) - 1) { \
VEC_SWAP(hooks, found_at, VEC_SIZE(hooks) - 1); \
} \
VEC_POP(hooks, struct name##_hook removed); \
+ (void)removed; \
} \
}
@@ -84,13 +85,13 @@ typedef VEC(struct reload_hook) reload_hook_vec;
typedef VEC(struct delete_hook) delete_hook_vec;
typedef VEC(struct render_hook) render_hook_vec;
-DECLARE_HOOK(create, create_hook_cb, create_hook_vec);
-DECLARE_HOOK(destroy, destroy_hook_cb, destroy_hook_vec);
-DECLARE_HOOK(insert, insert_hook_cb, insert_hook_vec);
-DECLARE_HOOK(update, update_hook_cb, update_hook_vec);
-DECLARE_HOOK(reload, reload_hook_cb, reload_hook_vec);
-DECLARE_HOOK(render, render_hook_cb, render_hook_vec);
-DECLARE_HOOK(delete, delete_hook_cb, delete_hook_vec);
+DECLARE_HOOK(create, create_hook_cb, create_hook_vec)
+DECLARE_HOOK(destroy, destroy_hook_cb, destroy_hook_vec)
+DECLARE_HOOK(insert, insert_hook_cb, insert_hook_vec)
+DECLARE_HOOK(update, update_hook_cb, update_hook_vec)
+DECLARE_HOOK(reload, reload_hook_cb, reload_hook_vec)
+DECLARE_HOOK(render, render_hook_cb, render_hook_vec)
+DECLARE_HOOK(delete, delete_hook_cb, delete_hook_vec)
static create_hook_vec g_create_hooks;
uint32_t g_create_hook_id;
@@ -136,19 +137,19 @@ void buffer_remove_destroy_hook(struct buffer *buffer, uint32_t hook_id,
remove_destroy_hook(&buffer->hooks->destroy_hooks, hook_id, callback);
}
-void buffer_static_init() {
+void buffer_static_init(void) {
VEC_INIT(&g_create_hooks, 8);
settings_set_default(
"editor.tab-width",
- (struct setting_value){.type = Setting_Number, .number_value = 4});
+ (struct setting_value){.type = Setting_Number, .data.number_value = 4});
settings_set_default(
"editor.show-whitespace",
- (struct setting_value){.type = Setting_Bool, .bool_value = true});
+ (struct setting_value){.type = Setting_Bool, .data.bool_value = true});
}
-void buffer_static_teardown() {
+void buffer_static_teardown(void) {
VEC_DESTROY(&g_create_hooks);
for (uint32_t i = 0; i < KILL_RING_SZ; ++i) {
if (g_kill_ring.buffer[i].allocated) {
@@ -165,7 +166,7 @@ static uint32_t get_tab_width(struct buffer *buffer) {
uint32_t tab_width = 4;
if (tw != NULL && tw->value.type == Setting_Number) {
- tab_width = tw->value.number_value;
+ tab_width = tw->value.data.number_value;
}
return tab_width;
}
@@ -178,7 +179,7 @@ static bool use_tabs(struct buffer *buffer) {
bool use_tabs = false;
if (ut != NULL && ut->value.type == Setting_Bool) {
- use_tabs = ut->value.bool_value;
+ use_tabs = ut->value.data.bool_value;
}
return use_tabs;
@@ -664,14 +665,14 @@ struct location buffer_previous_word(struct buffer *buffer,
struct location buffer_previous_line(struct buffer *buffer,
struct location dot) {
- if (dot.line == 0) {
+ (void)buffer;
+
+ if (dot.line <= 0) {
+ dot.line = 0;
return dot;
}
--dot.line;
- uint32_t nchars = buffer_line_length(buffer, dot.line);
- uint32_t new_col = dot.col > nchars ? nchars : dot.col;
-
return dot;
}
@@ -842,7 +843,7 @@ struct location buffer_undo(struct buffer *buffer, struct location dot) {
switch (rec->type) {
case Undo_Boundary: {
- struct undo_boundary *b = &rec->boundary;
+ struct undo_boundary *b = &rec->data.boundary;
if (b->save_point) {
buffer->modified = false;
}
@@ -850,7 +851,7 @@ struct location buffer_undo(struct buffer *buffer, struct location dot) {
}
case Undo_Add: {
- struct undo_add *add = &rec->add;
+ struct undo_add *add = &rec->data.add;
pos = buffer_delete(buffer,
(struct region){
@@ -864,7 +865,7 @@ struct location buffer_undo(struct buffer *buffer, struct location dot) {
}
case Undo_Delete: {
- struct undo_delete *del = &rec->delete;
+ struct undo_delete *del = &rec->data.delete;
pos = buffer_add(buffer,
(struct location){
.line = del->pos.row,
@@ -934,7 +935,7 @@ void buffer_find(struct buffer *buffer, const char *pattern,
struct location buffer_copy(struct buffer *buffer, struct region region) {
if (region_has_size(region)) {
- struct text_chunk *curr = copy_region(buffer, region);
+ copy_region(buffer, region);
}
return region.begin;
@@ -1024,7 +1025,6 @@ struct location buffer_paste_older(struct buffer *buffer, struct location at) {
if (g_kill_ring.paste_up_to_date) {
// remove previous paste
- struct text_chunk *curr = &g_kill_ring.buffer[g_kill_ring.curr_idx];
buffer_delete(buffer, region_new(g_kill_ring.last_paste, at));
// paste older
@@ -1124,7 +1124,7 @@ static void apply_properties(struct command_list *cmds,
switch (prop->type) {
case TextProperty_Colors: {
- struct text_property_colors *colors = &prop->colors;
+ struct text_property_colors *colors = &prop->data.colors;
if (colors->set_bg) {
command_list_set_index_color_bg(cmds, colors->bg);
}
@@ -1221,7 +1221,7 @@ void render_line(struct text_chunk *line, void *userdata) {
}
}
-void buffer_update(struct buffer *buffer, struct buffer_update_params *params) {
+void buffer_update(struct buffer *buffer) {
VEC_FOR_EACH(&buffer->hooks->update_hooks, struct update_hook * h) {
h->callback(buffer, h->userdata);
}
@@ -1244,7 +1244,7 @@ void buffer_render(struct buffer *buffer, struct buffer_render_params *params) {
.origin = params->origin,
.width = params->width,
.height = params->height,
- .show_ws = (show_ws != NULL ? show_ws->value.bool_value : true) &&
+ .show_ws = (show_ws != NULL ? show_ws->value.data.bool_value : true) &&
!buffer->force_show_ws_off,
.buffer = buffer,
};
diff --git a/src/dged/buffer.h b/src/dged/buffer.h
index c9fe2ca..0e45b98 100644
--- a/src/dged/buffer.h
+++ b/src/dged/buffer.h
@@ -65,8 +65,8 @@ struct buffer {
bool force_show_ws_off;
};
-void buffer_static_init();
-void buffer_static_teardown();
+void buffer_static_init(void);
+void buffer_static_teardown(void);
/**
* Create a new buffer.
@@ -690,11 +690,6 @@ uint32_t buffer_add_create_hook(create_hook_cb callback, void *userdata);
void buffer_remove_create_hook(uint32_t hook_id, remove_hook_cb callback);
/**
- * Parameters for updating a buffer.
- */
-struct buffer_update_params {};
-
-/**
* Parameters for rendering a buffer.
*/
struct buffer_render_params {
@@ -716,11 +711,8 @@ struct buffer_render_params {
* Update a buffer.
*
* @param [in] buffer The buffer to update.
- * @param [inout] params The parameters for the update. The @ref commands field
- * in @p params will be modified with the rendering commands needed for this
- * buffer.
*/
-void buffer_update(struct buffer *buffer, struct buffer_update_params *params);
+void buffer_update(struct buffer *buffer);
/**
* Render a buffer.
diff --git a/src/dged/buffer_view.c b/src/dged/buffer_view.c
index f3dd2b9..0c587a6 100644
--- a/src/dged/buffer_view.c
+++ b/src/dged/buffer_view.c
@@ -386,8 +386,7 @@ void buffer_view_update(struct buffer_view *view,
struct timer *buffer_update_timer =
timer_start("update-windows.buffer-update");
- struct buffer_update_params update_params = {};
- buffer_update(view->buffer, &update_params);
+ buffer_update(view->buffer);
timer_stop(buffer_update_timer);
uint32_t height = params->height;
@@ -446,7 +445,7 @@ void buffer_view_update(struct buffer_view *view,
buffer_add_text_property(view->buffer, reg.begin, reg.end,
(struct text_property){
.type = TextProperty_Colors,
- .colors =
+ .data.colors =
(struct text_property_colors){
.set_bg = true,
.bg = 5,
diff --git a/src/dged/command.c b/src/dged/command.c
index 2775286..5f2551f 100644
--- a/src/dged/command.c
+++ b/src/dged/command.c
@@ -79,7 +79,7 @@ void command_ctx_push_arg(struct command_ctx *ctx, const char *argv) {
}
void command_ctx_free(struct command_ctx *ctx) {
- for (uint32_t i = 0; i < ctx->saved_argc; ++i) {
+ for (uint32_t i = 0; i < (uint32_t)ctx->saved_argc; ++i) {
free((char *)ctx->saved_argv[i]);
}
diff --git a/src/dged/display.c b/src/dged/display.c
index ea3f459..0c3c47c 100644
--- a/src/dged/display.c
+++ b/src/dged/display.c
@@ -35,13 +35,13 @@ enum render_cmd_type {
struct render_command {
enum render_cmd_type type;
- union {
+ union render_cmd_data {
struct draw_text_cmd *draw_txt;
struct push_fmt_cmd *push_fmt;
struct repeat_cmd *repeat;
struct show_ws_cmd *show_ws;
struct draw_list_cmd *draw_list;
- };
+ } data;
};
struct draw_text_cmd {
@@ -87,13 +87,13 @@ struct command_list {
struct command_list *next_list;
};
-struct winsize getsize() {
+struct winsize getsize(void) {
struct winsize ws;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
return ws;
}
-struct display *display_create() {
+struct display *display_create(void) {
struct winsize ws = getsize();
@@ -182,6 +182,8 @@ void put_ansiparm(int n) {
}
void display_move_cursor(struct display *display, uint32_t row, uint32_t col) {
+ (void)display;
+
putc(ESC, stdout);
putc('[', stdout);
put_ansiparm(row + 1);
@@ -192,7 +194,6 @@ void display_move_cursor(struct display *display, uint32_t row, uint32_t col) {
void display_clear(struct display *display) {
display_move_cursor(display, 0, 0);
- uint8_t bytes[] = {ESC, '[', 'J'};
putc(ESC, stdout);
putc('[', stdout);
putc('J', stdout);
@@ -239,21 +240,21 @@ struct render_command *add_command(struct command_list *list,
cmd->type = tp;
switch (tp) {
case RenderCommand_DrawText:
- cmd->draw_txt = l->allocator(sizeof(struct draw_text_cmd));
+ cmd->data.draw_txt = l->allocator(sizeof(struct draw_text_cmd));
break;
case RenderCommand_Repeat:
- cmd->repeat = l->allocator(sizeof(struct repeat_cmd));
+ cmd->data.repeat = l->allocator(sizeof(struct repeat_cmd));
break;
case RenderCommand_PushFormat:
- cmd->push_fmt = l->allocator(sizeof(struct push_fmt_cmd));
+ cmd->data.push_fmt = l->allocator(sizeof(struct push_fmt_cmd));
break;
case RenderCommand_SetShowWhitespace:
- cmd->show_ws = l->allocator(sizeof(struct show_ws_cmd));
+ cmd->data.show_ws = l->allocator(sizeof(struct show_ws_cmd));
break;
case RenderCommand_ClearFormat:
break;
case RenderCommand_DrawList:
- cmd->draw_list = l->allocator(sizeof(struct draw_list_cmd));
+ cmd->data.draw_list = l->allocator(sizeof(struct draw_list_cmd));
break;
default:
assert(false);
@@ -266,7 +267,7 @@ struct render_command *add_command(struct command_list *list,
void command_list_draw_text(struct command_list *list, uint32_t col,
uint32_t row, uint8_t *data, uint32_t len) {
struct draw_text_cmd *cmd =
- add_command(list, RenderCommand_DrawText)->draw_txt;
+ add_command(list, RenderCommand_DrawText)->data.draw_txt;
cmd->data = data;
cmd->col = col;
cmd->row = row;
@@ -283,7 +284,7 @@ void command_list_draw_text_copy(struct command_list *list, uint32_t col,
void command_list_draw_repeated(struct command_list *list, uint32_t col,
uint32_t row, uint32_t c, uint32_t nrepeat) {
- struct repeat_cmd *cmd = add_command(list, RenderCommand_Repeat)->repeat;
+ struct repeat_cmd *cmd = add_command(list, RenderCommand_Repeat)->data.repeat;
cmd->col = col;
cmd->row = row;
cmd->c = c;
@@ -293,14 +294,14 @@ void command_list_draw_repeated(struct command_list *list, uint32_t col,
void command_list_draw_command_list(struct command_list *list,
struct command_list *to_draw) {
struct draw_list_cmd *cmd =
- add_command(list, RenderCommand_DrawList)->draw_list;
+ add_command(list, RenderCommand_DrawList)->data.draw_list;
cmd->list = to_draw;
}
void command_list_set_index_color_fg(struct command_list *list,
uint8_t color_idx) {
struct push_fmt_cmd *cmd =
- add_command(list, RenderCommand_PushFormat)->push_fmt;
+ add_command(list, RenderCommand_PushFormat)->data.push_fmt;
if (color_idx < 8) {
cmd->len = snprintf((char *)cmd->fmt, 64, "%d", 30 + color_idx);
@@ -314,14 +315,14 @@ void command_list_set_index_color_fg(struct command_list *list,
void command_list_set_color_fg(struct command_list *list, uint8_t red,
uint8_t green, uint8_t blue) {
struct push_fmt_cmd *cmd =
- add_command(list, RenderCommand_PushFormat)->push_fmt;
+ add_command(list, RenderCommand_PushFormat)->data.push_fmt;
cmd->len = snprintf((char *)cmd->fmt, 64, "38;2;%d;%d;%d", red, green, blue);
}
void command_list_set_index_color_bg(struct command_list *list,
uint8_t color_idx) {
struct push_fmt_cmd *cmd =
- add_command(list, RenderCommand_PushFormat)->push_fmt;
+ add_command(list, RenderCommand_PushFormat)->data.push_fmt;
if (color_idx < 8) {
cmd->len = snprintf((char *)cmd->fmt, 64, "%d", 40 + color_idx);
} else if (color_idx < 16) {
@@ -334,13 +335,13 @@ void command_list_set_index_color_bg(struct command_list *list,
void command_list_set_color_bg(struct command_list *list, uint8_t red,
uint8_t green, uint8_t blue) {
struct push_fmt_cmd *cmd =
- add_command(list, RenderCommand_PushFormat)->push_fmt;
+ add_command(list, RenderCommand_PushFormat)->data.push_fmt;
cmd->len = snprintf((char *)cmd->fmt, 64, "48;2;%d;%d;%d", red, green, blue);
}
void command_list_set_inverted_colors(struct command_list *list) {
struct push_fmt_cmd *cmd =
- add_command(list, RenderCommand_PushFormat)->push_fmt;
+ add_command(list, RenderCommand_PushFormat)->data.push_fmt;
cmd->fmt[0] = '7';
cmd->len = 1;
}
@@ -350,7 +351,7 @@ void command_list_reset_color(struct command_list *list) {
}
void command_list_set_show_whitespace(struct command_list *list, bool show) {
- add_command(list, RenderCommand_SetShowWhitespace)->show_ws->show = show;
+ add_command(list, RenderCommand_SetShowWhitespace)->data.show_ws->show = show;
}
void display_render(struct display *display,
@@ -374,7 +375,7 @@ void display_render(struct display *display,
struct render_command *cmd = &cl->cmds[cmdi];
switch (cmd->type) {
case RenderCommand_DrawText: {
- struct draw_text_cmd *txt_cmd = cmd->draw_txt;
+ struct draw_text_cmd *txt_cmd = cmd->data.draw_txt;
display_move_cursor(display, txt_cmd->row + cl->yoffset,
txt_cmd->col + cl->xoffset);
apply_fmt(fmt_stack, fmt_stack_len);
@@ -384,7 +385,7 @@ void display_render(struct display *display,
}
case RenderCommand_Repeat: {
- struct repeat_cmd *repeat_cmd = cmd->repeat;
+ struct repeat_cmd *repeat_cmd = cmd->data.repeat;
display_move_cursor(display, repeat_cmd->row + cl->yoffset,
repeat_cmd->col + cl->xoffset);
apply_fmt(fmt_stack, fmt_stack_len);
@@ -401,7 +402,7 @@ void display_render(struct display *display,
}
case RenderCommand_PushFormat: {
- struct push_fmt_cmd *fmt_cmd = cmd->push_fmt;
+ struct push_fmt_cmd *fmt_cmd = cmd->data.push_fmt;
fmt_stack[fmt_stack_len] = ';';
++fmt_stack_len;
@@ -416,11 +417,11 @@ void display_render(struct display *display,
break;
case RenderCommand_SetShowWhitespace:
- show_whitespace_state = cmd->show_ws->show;
+ show_whitespace_state = cmd->data.show_ws->show;
break;
case RenderCommand_DrawList:
- display_render(display, cmd->draw_list->list);
+ display_render(display, cmd->data.draw_list->list);
break;
}
}
@@ -430,7 +431,7 @@ void display_render(struct display *display,
timer_stop(render_timer);
}
-void hide_cursor() {
+void hide_cursor(void) {
putc(ESC, stdout);
putc('[', stdout);
putc('?', stdout);
@@ -439,7 +440,7 @@ void hide_cursor() {
putc('l', stdout);
}
-void show_cursor() {
+void show_cursor(void) {
putc(ESC, stdout);
putc('[', stdout);
putc('?', stdout);
@@ -448,8 +449,13 @@ void show_cursor() {
putc('h', stdout);
}
-void display_begin_render(struct display *display) { hide_cursor(); }
+void display_begin_render(struct display *display) {
+ (void)display;
+ hide_cursor();
+}
void display_end_render(struct display *display) {
+ (void)display;
+
show_cursor();
fflush(stdout);
}
diff --git a/src/dged/display.h b/src/dged/display.h
index f9c7ef8..950ab3c 100644
--- a/src/dged/display.h
+++ b/src/dged/display.h
@@ -13,7 +13,7 @@ struct command_list;
* The only implementation of this is currently a termios one.
* @returns A pointer to the display.
*/
-struct display *display_create();
+struct display *display_create(void);
/**
* Resize the display
diff --git a/src/dged/hash.c b/src/dged/hash.c
new file mode 100644
index 0000000..fce61af
--- /dev/null
+++ b/src/dged/hash.c
@@ -0,0 +1,20 @@
+#include "hash.h"
+
+uint32_t hash_name(const char *s) {
+ unsigned long hash = 5381;
+ int c;
+
+ while ((c = *s++))
+ hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
+
+ return hash;
+}
+
+uint32_t hash_name_s8(struct s8 s) {
+ unsigned long hash = 5381;
+
+ for (uint64_t i = 0; i < s.l; ++i)
+ hash = ((hash << 5) + hash) + s.s[i]; /* hash * 33 + c */
+
+ return hash;
+}
diff --git a/src/dged/hash.h b/src/dged/hash.h
index 0fd689b..60b6d6a 100644
--- a/src/dged/hash.h
+++ b/src/dged/hash.h
@@ -1,11 +1,11 @@
+#ifndef _HASH_H
+#define _HASH_H
+
#include <stdint.h>
-static uint32_t hash_name(const char *s) {
- unsigned long hash = 5381;
- int c;
+#include "s8.h"
- while ((c = *s++))
- hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
+uint32_t hash_name(const char *s);
+uint32_t hash_name_s8(struct s8 s);
- return hash;
-}
+#endif
diff --git a/src/dged/hashmap.h b/src/dged/hashmap.h
index 405c193..b8475c7 100644
--- a/src/dged/hashmap.h
+++ b/src/dged/hashmap.h
@@ -66,16 +66,16 @@
} \
var = res != NULL ? &(res->value) : NULL;
-#define HASHMAP_CONTAINS_KEY(map, key) \
- uint32_t needle = (map)->hash_fn(key); \
+#define HASHMAP_CONTAINS_KEY(map, type, k, var) \
+ uint32_t needle = (map)->hash_fn(k); \
bool exists = false; \
- VEC_FOR_EACH((map)->entries, struct pair *pair) { \
+ VEC_FOR_EACH(&(map)->entries, type *pair) { \
if (needle == pair->key) { \
exists = true; \
break; \
} \
} \
- exists
+ var = exists;
#define HASHMAP_FOR_EACH(map, var) VEC_FOR_EACH_INDEXED(&(map)->entries, var, i)
diff --git a/src/dged/json.c b/src/dged/json.c
new file mode 100644
index 0000000..24d5c15
--- /dev/null
+++ b/src/dged/json.c
@@ -0,0 +1,234 @@
+#include "json.h"
+
+#include "hash.h"
+#include "hashmap.h"
+#include "utf8.h"
+#include "vec.h"
+
+#include <stddef.h>
+#include <stdio.h>
+
+HASHMAP_ENTRY_TYPE(json_object_member, struct json_value);
+
+struct json_object {
+ HASHMAP(struct json_object_member) members;
+};
+
+struct json_array {
+ VEC(struct json_value) values;
+};
+
+static void setarray(struct json_value *val) {
+ val->type = Json_Array;
+ val->value.array = calloc(1, sizeof(struct json_array));
+ VEC_INIT(&val->value.array->values, 10);
+}
+
+static void setobject(struct json_value *val) {
+ val->type = Json_Object;
+ val->value.object = calloc(1, sizeof(struct json_object));
+ HASHMAP_INIT(&val->value.object->members, 10, hash_name);
+}
+
+static void setstring(struct json_value *val, uint8_t *current) {
+ val->type = Json_String;
+ val->value.string.s = current;
+ val->value.string.l = 0;
+}
+
+static bool is_number(uint8_t byte) { return byte >= '0' && byte <= '9'; }
+
+enum object_parse_state {
+ ObjectParseState_Key,
+ ObjectParseState_Value,
+};
+
+struct json_result json_parse(uint8_t *buf, uint64_t size) {
+ struct json_result res = {
+ .ok = true,
+ .result.document.type = Json_Null,
+ };
+
+ struct json_value *parent = NULL;
+ struct json_value *current = &res.result.document;
+ struct json_value tmp_key = {0};
+ struct json_value tmp_val = {0};
+ uint32_t line = 1, col = 0;
+
+ enum object_parse_state obj_parse_state = ObjectParseState_Key;
+ for (uint64_t bufi = 0; bufi < size; ++bufi) {
+ uint8_t byte = buf[bufi];
+
+ // handle appends to the current scope
+ if (current->type == Json_Array) {
+ VEC_PUSH(&current->value.array->values, tmp_val);
+ parent = current;
+
+ // start looking for next value
+ tmp_val.type = Json_Null;
+ current = &tmp_val;
+ } else if (current->type == Json_Object &&
+ obj_parse_state == ObjectParseState_Key) {
+ // key is in tmp_key, start looking for value
+ obj_parse_state = ObjectParseState_Value;
+ parent = current;
+
+ tmp_val.type = Json_Null;
+ current = &tmp_val;
+ } else if (current->type == Json_Object &&
+ obj_parse_state == ObjectParseState_Value) {
+ // value is in tmp_val
+ // TODO: remove this alloc, should not be needed
+ char *k = s8tocstr(tmp_key.value.string);
+ uint32_t hash = 0;
+ HASHMAP_INSERT(&current->value.object->members, struct json_object_member,
+ k, tmp_val, hash);
+ (void)hash;
+ free(k);
+
+ // start looking for next key
+ obj_parse_state = ObjectParseState_Key;
+ parent = current;
+
+ tmp_key.type = Json_Null;
+ current = &tmp_key;
+ }
+
+ switch (byte) {
+ case '[':
+ setarray(current);
+ parent = current;
+
+ tmp_val.type = Json_Null;
+ current = &tmp_val;
+ break;
+ case ']':
+ current = parent;
+ break;
+ case '{':
+ setobject(current);
+ obj_parse_state = ObjectParseState_Key;
+ parent = current;
+
+ tmp_key.type = Json_Null;
+ current = &tmp_key;
+ break;
+ case '}':
+ current = parent;
+ break;
+ case '"':
+ if (current->type == Json_String) {
+ // finish off the string
+ current->value.string.l = (buf + bufi) - current->value.string.s;
+ current = parent;
+ } else {
+ setstring(current, buf + bufi + 1 /* skip " */);
+ }
+ break;
+ case '\n':
+ ++line;
+ col = 0;
+ break;
+ default:
+ if (current->type == Json_String) {
+ // append to string
+ } else if (current->type == Json_Number &&
+ !(is_number(byte) || byte == '-' || byte == '.')) {
+ // end of number
+ current->value.string.l = (buf + bufi) - current->value.string.s;
+ char *nmbr = s8tocstr(current->value.string);
+ current->value.number = atof(nmbr);
+ free(nmbr);
+
+ current = parent;
+
+ } else if (current->type == Json_Null &&
+ (is_number(byte) || byte == '-' || byte == '.')) {
+ // borrow string storage in the value for storing number
+ // as a string
+ setstring(current, buf + bufi);
+ current->type = Json_Number;
+ } else if (byte == 't') {
+ current->type = Json_Bool;
+ current->value.boolean = true;
+
+ current = parent;
+ } else if (byte == 'f') {
+ current->type = Json_Bool;
+ current->value.boolean = false;
+
+ current = parent;
+ } else if (byte == 'n') {
+ current->type = Json_Null;
+
+ current = parent;
+ }
+ break;
+ }
+
+ // TODO: not entirely correct
+ ++col;
+ }
+ return res;
+}
+
+void json_destroy(struct json_value *value) {
+ switch (value->type) {
+ case Json_Array:
+ struct json_array *arr = value->value.array;
+ VEC_FOR_EACH(&arr->values, struct json_value * val) { json_destroy(val); }
+ VEC_DESTROY(&arr->values);
+ break;
+ case Json_Object:
+ struct json_object *obj = value->value.object;
+ HASHMAP_FOR_EACH(&obj->members, struct json_object_member * memb) {
+ json_destroy(&memb->value);
+ }
+
+ HASHMAP_DESTROY(&obj->members);
+ case Json_Null:
+ case Json_Number:
+ case Json_String:
+ case Json_Bool:
+ break;
+ }
+}
+
+uint64_t json_array_len(struct json_array *arr) {
+ return VEC_SIZE(&arr->values);
+}
+
+struct json_value *json_array_get(struct json_array *arr, uint64_t idx) {
+ struct json_value *ret = NULL;
+
+ if (idx <= VEC_SIZE(&arr->values)) {
+ ret = &VEC_ENTRIES(&arr->values)[idx];
+ }
+
+ return ret;
+}
+
+uint64_t json_len(struct json_object *obj) {
+ return HASHMAP_SIZE(&obj->members);
+}
+
+bool json_contains(struct json_object *obj, struct s8 key) {
+ // TODO: get rid of alloc
+ char *k = s8tocstr(key);
+ HASHMAP_CONTAINS_KEY(&obj->members, struct json_object_member, k, bool res);
+
+ free(k);
+
+ return res;
+}
+
+struct json_value *json_get(struct json_object *obj, struct s8 key) {
+ // TODO: get rid of alloc
+ char *k = s8tocstr(key);
+ HASHMAP_GET(&obj->members, struct json_object_member, k,
+ struct json_value * result);
+
+ free(k);
+
+ return result;
+}
diff --git a/src/dged/json.h b/src/dged/json.h
new file mode 100644
index 0000000..c0428b9
--- /dev/null
+++ b/src/dged/json.h
@@ -0,0 +1,54 @@
+#ifndef _JSON_H
+#define _JSON_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "s8.h"
+
+enum json_type {
+ Json_Array,
+ Json_Object,
+ Json_Null,
+ Json_Number,
+ Json_String,
+ Json_Bool,
+};
+
+struct json_value {
+ enum json_type type;
+ union {
+ struct s8 string;
+ struct json_object *object;
+ struct json_array *array;
+ double number;
+ bool boolean;
+ } value;
+};
+
+struct json_result {
+ bool ok;
+ union {
+ const char *error;
+ struct json_value document;
+ } result;
+};
+
+struct json_writer;
+
+struct json_result json_parse(uint8_t *buf, uint64_t size);
+void json_destroy(struct json_value *value);
+
+uint64_t json_len(struct json_object *obj);
+bool json_contains(struct json_object *obj, struct s8 key);
+struct json_value *json_get(struct json_object *obj, struct s8 key);
+
+uint64_t json_array_len(struct json_array *arr);
+void json_array_foreach(struct json_array *arr,
+ void (*cb)(uint64_t, struct json_value));
+struct json_value *json_array_get(struct json_array *arr, uint64_t idx);
+
+struct json_writer *json_writer_create();
+struct s8 json_writer_done(struct json_writer *writer);
+
+#endif
diff --git a/src/dged/lang.c b/src/dged/lang.c
index 070b96e..92b4fbd 100644
--- a/src/dged/lang.c
+++ b/src/dged/lang.c
@@ -19,17 +19,19 @@ void define_lang(const char *name, const char *id, const char *pattern,
_lang_setting_set_default(
id, "name",
(struct setting_value){.type = Setting_String,
- .string_value = (char *)name});
+ .data.string_value = (char *)name});
_lang_setting_set_default(
id, "pattern",
(struct setting_value){.type = Setting_String,
- .string_value = (char *)pattern});
- _lang_setting_set_default(id, "tab-width",
- (struct setting_value){.type = Setting_Number,
- .number_value = tab_width});
+ .data.string_value = (char *)pattern});
+ _lang_setting_set_default(
+ id, "tab-width",
+ (struct setting_value){.type = Setting_Number,
+ .data.number_value = tab_width});
_lang_setting_set_default(
id, "use-tabs",
- (struct setting_value){.type = Setting_Bool, .bool_value = use_tabs});
+ (struct setting_value){.type = Setting_Bool,
+ .data.bool_value = use_tabs});
}
static struct language g_fundamental = {
@@ -65,7 +67,8 @@ bool lang_is_fundamental(const struct language *lang) {
static struct language lang_from_settings(const char *id) {
struct setting *name = _lang_setting(id, "name");
- const char *name_value = name != NULL ? name->value.string_value : "Unknown";
+ const char *name_value =
+ name != NULL ? name->value.data.string_value : "Unknown";
return (struct language){
.id = strdup(id),
@@ -73,21 +76,6 @@ static struct language lang_from_settings(const char *id) {
};
}
-static void next_ext(const char *curr, const char **nxt, const char **end) {
- if (curr == NULL) {
- *nxt = *end = NULL;
- return;
- }
-
- *nxt = curr;
- *end = curr + strlen(curr);
-
- const char *spc = strchr(curr, ' ');
- if (spc != NULL) {
- *end = spc;
- }
-}
-
void lang_settings(struct language *lang, struct setting **settings[],
uint32_t *nsettings) {
const char *key = setting_join_key("languages", lang->id);
@@ -159,7 +147,7 @@ struct language lang_from_filename(const char *filename) {
struct setting *setting = settings[i];
char *setting_name = strrchr(setting->path, '.');
if (setting_name != NULL && strncmp(setting_name + 1, "pattern", 5) == 0) {
- const char *val = setting->value.string_value;
+ const char *val = setting->value.data.string_value;
regex_t regex;
if (regcomp(&regex, val, REG_EXTENDED) == 0 &&
regexec(&regex, filename, 0, NULL, 0) == 0) {
diff --git a/src/dged/lsp.c b/src/dged/lsp.c
new file mode 100644
index 0000000..3c699f4
--- /dev/null
+++ b/src/dged/lsp.c
@@ -0,0 +1,163 @@
+#include "lsp.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "buffer.h"
+#include "process.h"
+#include "reactor.h"
+
+struct lsp {
+ const char *name;
+ char *const *command;
+ struct process *process;
+ struct reactor *reactor;
+ struct buffer *stderr_buffer;
+ struct lsp_client client_impl;
+ uint32_t stdin_event;
+ uint32_t stdout_event;
+ uint32_t stderr_event;
+};
+
+struct lsp *lsp_create(char *const command[], struct reactor *reactor,
+ struct buffer *stderr_buffer,
+ struct lsp_client client_impl, const char *name) {
+ // check length of command
+ if (command == NULL) {
+ return NULL;
+ }
+
+ uint32_t command_len = 0;
+ while (command[command_len] != NULL) {
+ ++command_len;
+ }
+
+ if (command_len == 0) {
+ return NULL;
+ }
+
+ struct lsp *lsp = calloc(1, sizeof(struct lsp));
+
+ char **cmd = calloc(command_len + 1, sizeof(const char *));
+ memcpy(cmd, command, sizeof(const char *) * command_len);
+ cmd[command_len] = NULL;
+ lsp->command = cmd;
+
+ if (name != NULL) {
+ lsp->name = strdup(name);
+ } else {
+#ifdef __unix__
+ const char *lslash = strrchr(lsp->command[0], '/');
+#elif defined(_WIN32) || defined(WIN32)
+ const char *lslash = strrchr(lsp->command[0], '\\');
+#endif
+ if (lslash == NULL) {
+ lsp->name = strdup(lsp->command[0]);
+ } else {
+ lsp->name = strdup(lslash + 1);
+ }
+ }
+ lsp->stderr_buffer = stderr_buffer;
+ lsp->client_impl = client_impl;
+ lsp->reactor = reactor;
+ lsp->stdin_event = -1;
+ lsp->stdout_event = -1;
+ lsp->stderr_event = -1;
+
+ return lsp;
+}
+
+void lsp_destroy(struct lsp *lsp) {
+ free((void *)lsp->name);
+ if (lsp->process != NULL) {
+ free(lsp->process);
+ }
+ if (lsp->command != NULL) {
+ char *command = lsp->command[0];
+ while (command != NULL) {
+ free(command);
+ ++command;
+ }
+
+ free((void *)lsp->command);
+ }
+ free(lsp);
+}
+
+uint32_t lsp_update(struct lsp *lsp, struct lsp_response **responses,
+ uint32_t responses_capacity) {
+
+ (void)responses;
+ (void)responses_capacity;
+
+ if (!lsp_server_running(lsp)) {
+ return -1;
+ }
+
+ // read stderr
+ if (lsp->stderr_event != (uint32_t)-1) {
+ uint8_t buf[1024];
+ if (reactor_poll_event(lsp->reactor, lsp->stderr_event)) {
+ ssize_t nb = 0;
+ while ((nb = read(lsp->process->stderr, buf, 1024)) > 0) {
+ buffer_set_readonly(lsp->stderr_buffer, false);
+ buffer_add(lsp->stderr_buffer, buffer_end(lsp->stderr_buffer), buf, nb);
+ buffer_set_readonly(lsp->stderr_buffer, true);
+ }
+ }
+ }
+
+ return 0;
+}
+
+int lsp_start_server(struct lsp *lsp) {
+ struct process p;
+ struct process_create_result res = process_create(lsp->command, &p);
+
+ if (!res.ok) {
+ // TODO: losing error message here
+ return -1;
+ }
+
+ lsp->process = calloc(1, sizeof(struct process));
+ memcpy(lsp->process, &p, sizeof(struct process));
+ lsp->stderr_event = reactor_register_interest(
+ lsp->reactor, lsp->process->stderr, ReadInterest);
+
+ return 0;
+}
+
+int lsp_restart_server(struct lsp *lsp) {
+ if (lsp_server_running(lsp)) {
+ lsp_stop_server(lsp);
+ }
+
+ return lsp_start_server(lsp);
+}
+
+void lsp_stop_server(struct lsp *lsp) {
+ process_kill(lsp->process);
+ process_destroy(lsp->process);
+ free(lsp->process);
+ lsp->process = NULL;
+}
+
+bool lsp_server_running(const struct lsp *lsp) {
+ if (lsp->process == NULL) {
+ return false;
+ }
+
+ return process_running(lsp->process);
+}
+
+uint64_t lsp_server_pid(const struct lsp *lsp) {
+ if (!lsp_server_running(lsp)) {
+ return -1;
+ }
+
+ return lsp->process->id;
+}
+
+const char *lsp_server_name(const struct lsp *lsp) { return lsp->name; }
diff --git a/src/dged/lsp.h b/src/dged/lsp.h
new file mode 100644
index 0000000..3fd6285
--- /dev/null
+++ b/src/dged/lsp.h
@@ -0,0 +1,75 @@
+#ifndef _LSP_H
+#define _LSP_H
+
+#include "location.h"
+#include "s8.h"
+
+struct buffer;
+struct lsp;
+struct reactor;
+
+typedef uint32_t request_id;
+
+struct lsp_response {
+ request_id id;
+ bool ok;
+ union payload_data {
+ void *result;
+ struct s8 error;
+ } payload;
+};
+
+struct lsp_notification {
+ int something;
+};
+
+struct lsp_client {
+ void (*log_message)(int type, struct s8 msg);
+};
+
+struct hover {
+ struct s8 contents;
+
+ bool has_range;
+ struct region *range;
+};
+
+struct text_doc_item {
+ struct s8 uri;
+ struct s8 language_id;
+ uint32_t version;
+ struct s8 text;
+};
+
+struct text_doc_position {
+ struct s8 uri;
+ struct location pos;
+};
+
+struct initialize_params {
+ struct s8 client_name;
+ struct s8 client_version;
+};
+
+// lifecycle functions
+struct lsp *lsp_create(char *const command[], struct reactor *reactor,
+ struct buffer *stderr_buffer,
+ struct lsp_client client_impl, const char *name);
+uint32_t lsp_update(struct lsp *lsp, struct lsp_response **responses,
+ uint32_t responses_capacity);
+void lsp_destroy(struct lsp *lsp);
+
+// process control functions
+int lsp_start_server(struct lsp *lsp);
+int lsp_restart_server(struct lsp *lsp);
+void lsp_stop_server(struct lsp *lsp);
+bool lsp_server_running(const struct lsp *lsp);
+uint64_t lsp_server_pid(const struct lsp *lsp);
+const char *lsp_server_name(const struct lsp *lsp);
+
+// protocol functions
+void lsp_initialize(struct lsp *lsp, struct initialize_params);
+void lsp_did_open_document(struct lsp *lsp, struct text_doc_item document);
+request_id lsp_hover(struct lsp *lsp, struct text_doc_position);
+
+#endif
diff --git a/src/dged/minibuffer.c b/src/dged/minibuffer.c
index d31850b..c16a9bf 100644
--- a/src/dged/minibuffer.c
+++ b/src/dged/minibuffer.c
@@ -39,7 +39,7 @@ uint32_t minibuffer_draw_prompt(struct command_list *commands) {
static void minibuffer_abort_prompt_internal(bool clear);
-int32_t minibuffer_execute() {
+int32_t minibuffer_execute(void) {
if (g_minibuffer.prompt_active) {
struct command_ctx *c = &g_minibuffer.prompt_command_ctx;
@@ -50,7 +50,7 @@ int32_t minibuffer_execute() {
// propagate any saved arguments
char *argv[64];
- for (uint32_t i = 0; i < c->saved_argc; ++i) {
+ for (uint32_t i = 0; i < (uint32_t)c->saved_argc; ++i) {
argv[i] = (char *)c->saved_argv[i];
}
argv[c->saved_argc] = l;
@@ -79,6 +79,8 @@ int32_t minibuffer_execute() {
}
void update(struct buffer *buffer, void *userdata) {
+ (void)buffer;
+
struct timespec current;
struct minibuffer *mb = (struct minibuffer *)userdata;
clock_gettime(CLOCK_MONOTONIC, &current);
@@ -143,15 +145,15 @@ void message(const char *fmt, ...) {
nbytes > 2048 ? 2048 : nbytes);
}
-void minibuffer_destroy() {
+void minibuffer_destroy(void) {
command_ctx_free(&g_minibuffer.prompt_command_ctx);
}
-struct text_chunk minibuffer_content() {
+struct text_chunk minibuffer_content(void) {
return buffer_line(g_minibuffer.buffer, 0);
}
-struct buffer *minibuffer_buffer() {
+struct buffer *minibuffer_buffer(void) {
return g_minibuffer.buffer;
}
@@ -245,22 +247,22 @@ static void minibuffer_abort_prompt_internal(bool clear) {
g_minibuffer.prompt_active = false;
}
-void minibuffer_abort_prompt() { minibuffer_abort_prompt_internal(true); }
+void minibuffer_abort_prompt(void) { minibuffer_abort_prompt_internal(true); }
-bool minibuffer_empty() { return !minibuffer_displaying(); }
+bool minibuffer_empty(void) { return !minibuffer_displaying(); }
-bool minibuffer_displaying() {
+bool minibuffer_displaying(void) {
return g_minibuffer.buffer != NULL && !buffer_is_empty(g_minibuffer.buffer);
}
-void minibuffer_clear() {
+void minibuffer_clear(void) {
g_minibuffer.expires.tv_sec = 0;
g_minibuffer.expires.tv_nsec = 0;
buffer_clear(g_minibuffer.buffer);
}
-bool minibuffer_focused() { return g_minibuffer.prompt_active; }
+bool minibuffer_focused(void) { return g_minibuffer.prompt_active; }
-struct window *minibuffer_target_window() {
+struct window *minibuffer_target_window(void) {
return g_minibuffer.prev_window;
}
diff --git a/src/dged/minibuffer.h b/src/dged/minibuffer.h
index 6ac69c1..0b98904 100644
--- a/src/dged/minibuffer.h
+++ b/src/dged/minibuffer.h
@@ -24,11 +24,11 @@ void minibuffer_init(struct buffer *buffer, struct buffers *buffers);
*
* Note that this does not release the buffer used.
*/
-void minibuffer_destroy();
+void minibuffer_destroy(void);
-struct text_chunk minibuffer_content();
+struct text_chunk minibuffer_content(void);
-struct buffer *minibuffer_buffer();
+struct buffer *minibuffer_buffer(void);
void message(const char *fmt, ...);
@@ -74,14 +74,14 @@ uint32_t minibuffer_draw_prompt(struct command_list *commands);
*
* @returns zero on success, non-zero to indicate failure
*/
-int32_t minibuffer_execute();
+int32_t minibuffer_execute(void);
/**
* Abort the current minibuffer prompt.
*
* This returns focus to the previously focused window.
*/
-void minibuffer_abort_prompt();
+void minibuffer_abort_prompt(void);
/**
* Minibuffer prompt args
@@ -94,22 +94,22 @@ struct minibuffer_prompt_args {
/**
* Clear the current text in the minibuffer.
*/
-void minibuffer_clear();
+void minibuffer_clear(void);
-bool minibuffer_empty();
+bool minibuffer_empty(void);
/**
* Is the minibuffer currently displaying something?
*
* @returns True if the minibuffer is displaying anything, false otherwise.
*/
-bool minibuffer_displaying();
+bool minibuffer_displaying(void);
/**
* Is the minibuffer currently focused?
*
* @returns True if the minibuffer is currently focused, receiving user input.
*/
-bool minibuffer_focused();
+bool minibuffer_focused(void);
-struct window *minibuffer_target_window();
+struct window *minibuffer_target_window(void);
diff --git a/src/dged/path.c b/src/dged/path.c
new file mode 100644
index 0000000..735ef0c
--- /dev/null
+++ b/src/dged/path.c
@@ -0,0 +1,68 @@
+#include "path.h"
+
+#include <limits.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+char *expanduser(const char *path) {
+ // replace tilde
+ char *res = NULL;
+ char *tilde_pos = strchr(path, '~');
+ if (tilde_pos != NULL) {
+ char *home = getenv("HOME");
+ if (home != NULL) {
+ // allocate a new string based with the new len
+ size_t home_len = strlen(home);
+ size_t path_len = strlen(path);
+ size_t total_len = path_len + home_len;
+ res = malloc(total_len);
+ size_t initial_len = tilde_pos - path;
+ strncpy(res, path, initial_len);
+
+ strncpy(res + initial_len, home, home_len + 1);
+
+ size_t rest_len = path_len - initial_len - 1;
+ strncpy(res + initial_len + home_len, path + initial_len + 1, rest_len);
+ res[total_len - 1] = '\0';
+ }
+ }
+
+ return res != NULL ? res : strdup(path);
+}
+
+char *to_abspath(const char *path) {
+ char *exp = expanduser(path);
+ char *p = realpath(path, NULL);
+ if (p != NULL) {
+ free(exp);
+ return p;
+ } else {
+ return exp;
+ }
+}
+
+const char *join_path_with_delim(const char *p1, const char *p2,
+ const char delim) {
+ size_t len1 = strlen(p1);
+ size_t len2 = strlen(p2);
+
+ char *path = malloc(len1 + len2 + 2);
+ uint32_t idx = 0;
+ memcpy(&path[idx], p1, len1);
+ idx += len1;
+ path[idx++] = delim;
+ memcpy(&path[idx], p2, len2);
+ idx += len2;
+ path[idx++] = '\0';
+
+ return path;
+}
+
+const char *join_path(const char *p1, const char *p2) {
+#ifdef __unix__
+ return join_path_with_delim(p1, p2, '/');
+#elif defined(_WIN32) || defined(WIN32)
+ return join_path_with_delim(p1, p2, '\\');
+#endif
+}
diff --git a/src/dged/path.h b/src/dged/path.h
index 8ea9977..6168e42 100644
--- a/src/dged/path.h
+++ b/src/dged/path.h
@@ -1,65 +1,10 @@
-#include <limits.h>
-#include <stdlib.h>
-#include <string.h>
+#ifndef _PATH_H
+#define _PATH_H
-static char *expanduser(const char *path) {
- // replace tilde
- char *res = NULL;
- char *tilde_pos = strchr(path, '~');
- if (tilde_pos != NULL) {
- char *home = getenv("HOME");
- if (home != NULL) {
- // allocate a new string based with the new len
- size_t home_len = strlen(home);
- size_t path_len = strlen(path);
- size_t total_len = path_len + home_len;
- res = malloc(total_len);
- size_t initial_len = tilde_pos - path;
- strncpy(res, path, initial_len);
+char *expanduser(const char *path);
+char *to_abspath(const char *path);
+const char *join_path_with_delim(const char *p1, const char *p2,
+ const char delim);
+const char *join_path(const char *p1, const char *p2);
- strncpy(res + initial_len, home, home_len);
-
- size_t rest_len = path_len - initial_len - 1;
- strncpy(res + initial_len + home_len, path + initial_len + 1, rest_len);
- res[total_len - 1] = '\0';
- }
- }
-
- return res != NULL ? res : strdup(path);
-}
-
-static char *to_abspath(const char *path) {
- char *exp = expanduser(path);
- char *p = realpath(path, NULL);
- if (p != NULL) {
- free(exp);
- return p;
- } else {
- return exp;
- }
-}
-
-static const char *join_path_with_delim(const char *p1, const char *p2,
- const char delim) {
- size_t len1 = strlen(p1);
- size_t len2 = strlen(p2);
-
- char *path = malloc(len1 + len2 + 2);
- uint32_t idx = 0;
- memcpy(&path[idx], p1, len1);
- idx += len1;
- path[idx++] = '/';
- memcpy(&path[idx], p2, len2);
- idx += len2;
- path[idx++] = '\0';
-
- return path;
-}
-
-static const char *join_path(const char *p1, const char *p2) {
-#ifdef __unix__
- return join_path_with_delim(p1, p2, '/');
-#elif defined(_WIN32) || defined(WIN32)
- return join_path_with_delim(p1, p2, '\\');
#endif
-}
diff --git a/src/dged/process-posix.c b/src/dged/process-posix.c
new file mode 100644
index 0000000..94ceb5f
--- /dev/null
+++ b/src/dged/process-posix.c
@@ -0,0 +1,126 @@
+#include "process.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+static int create_pipe(int *read_end, int *write_end, bool read_nonblock,
+ bool write_nonblock) {
+ int pipes[2] = {0};
+ if (pipe(pipes) < 0) {
+ return -1;
+ }
+
+ if (write_nonblock) {
+ int flags = fcntl(pipes[1], F_GETFL, 0);
+ if (flags < 0) {
+ return -1;
+ }
+
+ flags |= O_NONBLOCK;
+ if (fcntl(pipes[1], F_SETFL, flags) < 0) {
+ return -1;
+ }
+ }
+
+ if (read_nonblock) {
+ int flags = fcntl(pipes[0], F_GETFL, 0);
+ if (flags < 0) {
+ return -1;
+ }
+
+ flags |= O_NONBLOCK;
+ if (fcntl(pipes[0], F_SETFL, flags) < 0) {
+ return -1;
+ }
+ }
+
+ *read_end = pipes[0];
+ *write_end = pipes[1];
+
+ return 0;
+}
+
+struct process_create_result process_create(char *const command[],
+ struct process *result) {
+
+ int stdin_read, stdin_write;
+ if (create_pipe(&stdin_read, &stdin_write, false, true) < 0) {
+ return (struct process_create_result){
+ .ok = false,
+ .error_message = strerror(errno),
+ };
+ }
+
+ int stdout_read, stdout_write;
+ if (create_pipe(&stdout_read, &stdout_write, true, false) < 0) {
+ return (struct process_create_result){
+ .ok = false,
+ .error_message = strerror(errno),
+ };
+ }
+
+ int stderr_read, stderr_write;
+ if (create_pipe(&stderr_read, &stderr_write, true, false) < 0) {
+ return (struct process_create_result){
+ .ok = false,
+ .error_message = strerror(errno),
+ };
+ }
+
+ pid_t pid = fork();
+ if (pid == -1) {
+ return (struct process_create_result){
+ .ok = false,
+ .error_message = strerror(errno),
+ };
+ } else if (pid == 0) {
+ close(stdin_write);
+ close(stdout_read);
+ close(stderr_read);
+
+ if (dup2(stdin_read, STDIN_FILENO) < 0) {
+ exit(16);
+ }
+
+ if (dup2(stdout_write, STDOUT_FILENO) < 0) {
+ exit(16);
+ }
+
+ if (dup2(stderr_write, STDERR_FILENO) < 0) {
+ exit(16);
+ }
+
+ if (execvp(command[0], command) < 0) {
+ exit(16);
+ }
+ } else {
+ close(stdin_read);
+ close(stdout_write);
+ close(stderr_write);
+
+ result->stdin = stdin_write;
+ result->stdout = stdout_read;
+ result->stderr = stderr_read;
+ result->id = (fd_t)pid;
+ result->impl = NULL;
+ }
+
+ return (struct process_create_result){
+ .ok = true,
+ };
+}
+
+void process_destroy(struct process *p) { (void)p; }
+
+bool process_running(const struct process *p) {
+ return waitpid(p->id, NULL, WNOHANG) == 0;
+}
+
+bool process_kill(const struct process *p) { return kill(p->id, SIGTERM) == 0; }
diff --git a/src/dged/process.h b/src/dged/process.h
new file mode 100644
index 0000000..cefec8c
--- /dev/null
+++ b/src/dged/process.h
@@ -0,0 +1,35 @@
+#ifndef _PROCESS_H
+#define _PROCESS_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef _WIN32
+typedef HANDLE fd_t;
+#else
+typedef int fd_t;
+#endif
+
+struct platform_process;
+struct process {
+ uint64_t id;
+ fd_t stdin;
+ fd_t stdout;
+ fd_t stderr;
+ struct platform_process *impl;
+};
+
+struct process_create_result {
+ bool ok;
+ const char *error_message;
+};
+
+struct process_create_result process_create(char *const command[],
+ struct process *result);
+
+void process_destroy(struct process *p);
+
+bool process_running(const struct process *p);
+bool process_kill(const struct process *p);
+
+#endif
diff --git a/src/dged/reactor-epoll.c b/src/dged/reactor-epoll.c
index 4c83b49..beee7ec 100644
--- a/src/dged/reactor-epoll.c
+++ b/src/dged/reactor-epoll.c
@@ -22,15 +22,15 @@ struct events {
uint32_t nevents;
};
-struct reactor *reactor_create() {
+struct reactor *reactor_create(void) {
int epollfd = epoll_create1(0);
if (epollfd == -1) {
- perror("epoll_create1");
+ return NULL;
}
int inotifyfd = inotify_init1(IN_NONBLOCK);
if (inotifyfd == -1) {
- perror("inotify_init1");
+ return NULL;
}
struct reactor *r = (struct reactor *)calloc(1, sizeof(struct reactor));
@@ -55,7 +55,6 @@ uint32_t reactor_register_interest(struct reactor *reactor, int fd,
ev.events |= (interest & WriteInterest) != 0 ? EPOLLOUT : 0;
ev.data.fd = fd;
if (epoll_ctl(reactor->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
- perror("epoll_ctl");
return -1;
}
@@ -71,7 +70,7 @@ bool reactor_poll_event(struct reactor *reactor, uint32_t ev_id) {
for (uint32_t ei = 0; ei < events->nevents; ++ei) {
struct epoll_event *ev = &events->events[ei];
- if (ev->data.fd == ev_id) {
+ if ((uint32_t)ev->data.fd == ev_id) {
return true;
}
}
@@ -118,7 +117,8 @@ void reactor_update(struct reactor *reactor) {
int nfds = epoll_wait(reactor->epoll_fd, events->events, 10, -1);
if (nfds == -1) {
- // TODO: log failure
+ events->nevents = 0;
+ message("failed update epoll reactor: %s", strerror(errno));
}
events->nevents = nfds;
diff --git a/src/dged/reactor-kqueue.c b/src/dged/reactor-kqueue.c
index 58ab7af..f30cb9a 100644
--- a/src/dged/reactor-kqueue.c
+++ b/src/dged/reactor-kqueue.c
@@ -16,7 +16,7 @@ struct reactor {
uint32_t nevents;
};
-struct reactor *reactor_create() {
+struct reactor *reactor_create(void) {
int queue = kqueue();
if (queue < 0) {
return NULL;
diff --git a/src/dged/reactor.h b/src/dged/reactor.h
index 39a94a9..82ff3fe 100644
--- a/src/dged/reactor.h
+++ b/src/dged/reactor.h
@@ -18,7 +18,7 @@ struct file_event {
struct reactor;
-struct reactor *reactor_create();
+struct reactor *reactor_create(void);
void reactor_destroy(struct reactor *reactor);
void reactor_update(struct reactor *reactor);
bool reactor_poll_event(struct reactor *reactor, uint32_t ev_id);
diff --git a/src/dged/settings.c b/src/dged/settings.c
index e63862f..68256e8 100644
--- a/src/dged/settings.c
+++ b/src/dged/settings.c
@@ -20,11 +20,11 @@ void settings_init(uint32_t initial_capacity) {
HASHMAP_INIT(&g_settings.settings, initial_capacity, hash_name);
}
-void settings_destroy() {
+void settings_destroy(void) {
HASHMAP_FOR_EACH(&g_settings.settings, struct setting_entry * entry) {
struct setting *setting = &entry->value;
if (setting->value.type == Setting_String) {
- free(setting->value.string_value);
+ free(setting->value.data.string_value);
}
}
@@ -33,8 +33,9 @@ void settings_destroy() {
void setting_set_value(struct setting *setting, struct setting_value val) {
if (setting->value.type == val.type) {
- if (setting->value.type == Setting_String && val.string_value != NULL) {
- setting->value.string_value = strdup(val.string_value);
+ if (setting->value.type == Setting_String &&
+ val.data.string_value != NULL) {
+ setting->value.data.string_value = strdup(val.data.string_value);
} else {
setting->value = val;
}
@@ -99,13 +100,13 @@ void settings_set_default(const char *path, struct setting_value value) {
void setting_to_string(struct setting *setting, char *buf, size_t n) {
switch (setting->value.type) {
case Setting_Bool:
- snprintf(buf, n, "%s", setting->value.bool_value ? "true" : "false");
+ snprintf(buf, n, "%s", setting->value.data.bool_value ? "true" : "false");
break;
case Setting_Number:
- snprintf(buf, n, "%" PRId64, setting->value.number_value);
+ snprintf(buf, n, "%" PRId64, setting->value.data.number_value);
break;
case Setting_String:
- snprintf(buf, n, "%s", setting->value.string_value);
+ snprintf(buf, n, "%s", setting->value.data.string_value);
break;
}
}
@@ -113,7 +114,6 @@ void setting_to_string(struct setting *setting, char *buf, size_t n) {
static int32_t parse_toml(struct parser *state, char **errmsgs[]) {
char *curtbl = NULL;
char *curkey = NULL;
- uint32_t errcnt = 0;
VEC(char *) errs;
VEC_INIT(&errs, 16);
@@ -160,20 +160,20 @@ static int32_t parse_toml(struct parser *state, char **errmsgs[]) {
case Token_IntValue:
i = *((int64_t *)t.data);
settings_set(curkey, (struct setting_value){.type = Setting_Number,
- .number_value = i});
+ .data.number_value = i});
break;
case Token_BoolValue:
b = *((bool *)t.data);
settings_set(curkey, (struct setting_value){.type = Setting_Bool,
- .bool_value = b});
+ .data.bool_value = b});
break;
case Token_StringValue:
v = calloc(t.len + 1, 1);
strncpy(v, (char *)t.data, t.len);
settings_set(curkey, (struct setting_value){.type = Setting_String,
- .string_value = v});
+ .data.string_value = v});
free(v);
break;
diff --git a/src/dged/settings.h b/src/dged/settings.h
index b88e483..fb79c75 100644
--- a/src/dged/settings.h
+++ b/src/dged/settings.h
@@ -26,7 +26,7 @@ struct setting_value {
/** Type of setting. */
enum setting_type type;
- union {
+ union setting_data {
/** String setting. */
char *string_value;
@@ -35,7 +35,7 @@ struct setting_value {
/** Boolean setting value. */
bool bool_value;
- };
+ } data;
};
/**
@@ -73,7 +73,7 @@ void settings_init(uint32_t initial_capacity);
/**
* Destroy the global collection of settings.
*/
-void settings_destroy();
+void settings_destroy(void);
/**
* Retrieve a single setting by path.
diff --git a/src/dged/syntax.c b/src/dged/syntax.c
index 569dc70..67ab3a2 100644
--- a/src/dged/syntax.c
+++ b/src/dged/syntax.c
@@ -48,6 +48,8 @@ struct highlight {
};
static void delete_parser(struct buffer *buffer, void *userdata) {
+ (void)buffer;
+
struct highlight *highlight = (struct highlight *)userdata;
if (highlight->query != NULL) {
@@ -72,6 +74,7 @@ static void delete_parser(struct buffer *buffer, void *userdata) {
static const char *read_text(void *payload, uint32_t byte_offset,
TSPoint position, uint32_t *bytes_read) {
+ (void)byte_offset;
struct text *text = (struct text *)payload;
@@ -97,7 +100,7 @@ static const char *read_text(void *payload, uint32_t byte_offset,
static const char *grammar_name_from_buffer(struct buffer *buffer) {
struct setting *s = lang_setting(&buffer->lang, "grammar");
if (s != NULL && s->value.type == Setting_String) {
- return s->value.string_value;
+ return s->value.data.string_value;
}
return buffer->lang.name;
@@ -124,6 +127,10 @@ static const char *lang_folder(struct buffer *buffer, const char *path) {
static bool eval_eq(struct s8 capname, uint32_t argc, struct s8 argv[],
struct s8 value, void *data) {
+ (void)capname;
+ (void)argc;
+ (void)argv;
+
const struct s8 *cmp_to = (const struct s8 *)data;
if (data == NULL) {
return false;
@@ -143,6 +150,10 @@ static void cleanup_eq(void *data) {
static bool eval_match(struct s8 capname, uint32_t argc, struct s8 argv[],
struct s8 value, void *data) {
+ (void)capname;
+ (void)argc;
+ (void)argv;
+
regex_t *regex = (regex_t *)data;
if (regex == NULL) {
return false;
@@ -325,6 +336,8 @@ static void update_parser(struct buffer *buffer, void *userdata,
struct location origin, uint32_t width,
uint32_t height) {
+ (void)width;
+
struct highlight *h = (struct highlight *)userdata;
if (h->query == NULL) {
@@ -411,7 +424,7 @@ static void update_parser(struct buffer *buffer, void *userdata,
end.column > 0 ? end.column - 1 : 0,
(struct text_property){
.type = TextProperty_Colors,
- .colors =
+ .data.colors =
(struct text_property_colors){
.set_fg = true,
.fg = color,
@@ -509,8 +522,9 @@ static void text_inserted(struct buffer *buffer, struct edit_location inserted,
}
static void create_parser(struct buffer *buffer, void *userdata) {
+ (void)userdata;
- TSLanguage *(*langsym)() = NULL;
+ TSLanguage *(*langsym)(void) = NULL;
const char *lang_root = NULL, *langname = NULL;
void *h = NULL;
@@ -601,7 +615,7 @@ void syntax_init(uint32_t grammar_path_len, const char *grammar_path[]) {
lang_setting_set_default(
&l, "grammar",
(struct setting_value){.type = Setting_String,
- .string_value = "gitcommit"});
+ .data.string_value = "gitcommit"});
lang_destroy(&l);
}
@@ -609,14 +623,15 @@ void syntax_init(uint32_t grammar_path_len, const char *grammar_path[]) {
if (!lang_is_fundamental(&l)) {
lang_setting_set_default(
&l, "grammar",
- (struct setting_value){.type = Setting_String, .string_value = "cpp"});
+ (struct setting_value){.type = Setting_String,
+ .data.string_value = "cpp"});
lang_destroy(&l);
}
buffer_add_create_hook(create_parser, NULL);
}
-void syntax_teardown() {
+void syntax_teardown(void) {
for (uint32_t i = 0; i < treesitter_path_len; ++i) {
free((void *)treesitter_path[i]);
}
diff --git a/src/dged/syntax.h b/src/dged/syntax.h
index 488406b..015f5be 100644
--- a/src/dged/syntax.h
+++ b/src/dged/syntax.h
@@ -4,6 +4,6 @@
#include <stdint.h>
void syntax_init(uint32_t grammar_path_len, const char *grammar_path[]);
-void syntax_teardown();
+void syntax_teardown(void);
#endif
diff --git a/src/dged/text.h b/src/dged/text.h
index 28bd325..505c86a 100644
--- a/src/dged/text.h
+++ b/src/dged/text.h
@@ -66,10 +66,10 @@ struct text_property_colors {
struct text_property {
enum text_property_type type;
- union {
+ union property_data {
struct text_property_colors colors;
void *userdata;
- };
+ } data;
};
void text_add_property(struct text *text, uint32_t start_line,
diff --git a/src/dged/timers.c b/src/dged/timers.c
index 798c003..2fd5fc9 100644
--- a/src/dged/timers.c
+++ b/src/dged/timers.c
@@ -23,21 +23,21 @@ static struct timers {
HASHMAP(struct timer_entry) timers;
} g_timers;
-void timers_init() {
+void timers_init(void) {
HASHMAP_INIT(&g_timers.timers, 32, hash_name);
g_timers.frame_index = 0;
}
-void timers_destroy() { HASHMAP_DESTROY(&g_timers.timers); }
+void timers_destroy(void) { HASHMAP_DESTROY(&g_timers.timers); }
-void timers_start_frame() {
+void timers_start_frame(void) {
HASHMAP_FOR_EACH(&g_timers.timers, struct timer_entry * entry) {
struct timer *timer = &entry->value;
timer->samples[g_timers.frame_index] = 0;
}
}
-void timers_end_frame() {
+void timers_end_frame(void) {
g_timers.frame_index = (g_timers.frame_index + 1) % NUM_FRAME_SAMPLES;
}
diff --git a/src/dged/timers.h b/src/dged/timers.h
index ae79a8b..03ec742 100644
--- a/src/dged/timers.h
+++ b/src/dged/timers.h
@@ -5,8 +5,8 @@
struct timer;
-void timers_init();
-void timers_start_frame();
+void timers_init(void);
+void timers_start_frame(void);
struct timer *timer_start(const char *name);
uint64_t timer_stop(struct timer *timer);
@@ -19,7 +19,7 @@ const char *timer_name(const struct timer *timer);
typedef void (*timer_callback)(const struct timer *timer, void *userdata);
void timers_for_each(timer_callback callback, void *userdata);
-void timers_end_frame();
-void timers_destroy();
+void timers_end_frame(void);
+void timers_destroy(void);
#endif
diff --git a/src/dged/undo.c b/src/dged/undo.c
index 8f00f0f..ad167c3 100644
--- a/src/dged/undo.c
+++ b/src/dged/undo.c
@@ -18,9 +18,9 @@ void undo_clear(struct undo_stack *undo) {
void undo_destroy(struct undo_stack *undo) {
VEC_FOR_EACH(&undo->records, struct undo_record * rec) {
- if (rec->type == Undo_Delete && rec->delete.data != NULL &&
- rec->delete.nbytes > 0) {
- free(rec->delete.data);
+ if (rec->type == Undo_Delete && rec->data.delete.data != NULL &&
+ rec->data.delete.nbytes > 0) {
+ free(rec->data.delete.data);
}
}
@@ -35,15 +35,15 @@ uint32_t undo_push_boundary(struct undo_stack *undo,
// we can only have one save point
if (boundary.save_point) {
VEC_FOR_EACH(&undo->records, struct undo_record * rec) {
- if (rec->type == Undo_Boundary && rec->boundary.save_point) {
- rec->boundary.save_point = false;
+ if (rec->type == Undo_Boundary && rec->data.boundary.save_point) {
+ rec->data.boundary.save_point = false;
}
}
}
VEC_APPEND(&undo->records, struct undo_record * rec);
rec->type = Undo_Boundary;
- rec->boundary = boundary;
+ rec->data.boundary = boundary;
if (!undo->undo_in_progress) {
undo->top = VEC_SIZE(&undo->records) - 1;
@@ -61,12 +61,12 @@ uint32_t undo_push_add(struct undo_stack *undo, struct undo_add add) {
// "compress"
if (!VEC_EMPTY(&undo->records) &&
VEC_BACK(&undo->records)->type == Undo_Add &&
- pos_equal(&VEC_BACK(&undo->records)->add.end, &add.begin)) {
- VEC_BACK(&undo->records)->add.end = add.end;
+ pos_equal(&VEC_BACK(&undo->records)->data.add.end, &add.begin)) {
+ VEC_BACK(&undo->records)->data.add.end = add.end;
} else {
VEC_APPEND(&undo->records, struct undo_record * rec);
rec->type = Undo_Add;
- rec->add = add;
+ rec->data.add = add;
}
if (!undo->undo_in_progress) {
@@ -79,7 +79,7 @@ uint32_t undo_push_add(struct undo_stack *undo, struct undo_add add) {
uint32_t undo_push_delete(struct undo_stack *undo, struct undo_delete delete) {
VEC_APPEND(&undo->records, struct undo_record * rec);
rec->type = Undo_Delete;
- rec->delete = delete;
+ rec->data.delete = delete;
if (!undo->undo_in_progress) {
undo->top = VEC_SIZE(&undo->records) - 1;
@@ -150,15 +150,15 @@ size_t rec_to_str(struct undo_record *rec, char *buffer, size_t n) {
switch (rec->type) {
case Undo_Add:
return snprintf(buffer, n, "add { begin: (%d, %d) end: (%d, %d)}",
- rec->add.begin.row, rec->add.begin.col, rec->add.end.row,
- rec->add.end.col);
+ rec->data.add.begin.row, rec->data.add.begin.col,
+ rec->data.add.end.row, rec->data.add.end.col);
case Undo_Delete:
return snprintf(buffer, n, "delete { pos: (%d, %d), ptr: 0x%p, nbytes: %d}",
- rec->delete.pos.row, rec->delete.pos.col, rec->delete.data,
- rec->delete.nbytes);
+ rec->data.delete.pos.row, rec->data.delete.pos.col,
+ (void *)rec->data.delete.data, rec->data.delete.nbytes);
default:
return snprintf(buffer, n, "boundary { save_point: %s }",
- rec->boundary.save_point ? "yes" : "no");
+ rec->data.boundary.save_point ? "yes" : "no");
}
}
diff --git a/src/dged/undo.h b/src/dged/undo.h
index 1ce3a8a..2f308f9 100644
--- a/src/dged/undo.h
+++ b/src/dged/undo.h
@@ -31,14 +31,14 @@ struct undo_delete {
struct undo_record {
enum undo_record_type type;
- union {
+ union undo_record_data {
struct undo_boundary boundary;
struct undo_add add;
struct undo_delete delete;
- };
+ } data;
};
-#define INVALID_TOP -1
+#define INVALID_TOP (uint32_t) - 1
struct undo_stack {
VEC(struct undo_record) records;
diff --git a/src/dged/utf8.c b/src/dged/utf8.c
index ede4fb1..01dcdbd 100644
--- a/src/dged/utf8.c
+++ b/src/dged/utf8.c
@@ -112,7 +112,6 @@ uint32_t utf8_nchars(uint8_t *bytes, uint32_t nbytes) {
uint32_t utf8_nbytes(uint8_t *bytes, uint32_t nbytes, uint32_t nchars) {
uint32_t bi = 0;
uint32_t chars = 0;
- uint32_t expected = 0;
while (chars < nchars && bi < nbytes) {
struct codepoint codepoint = next_utf8_codepoint(bytes + bi, nbytes - bi);
@@ -127,7 +126,7 @@ uint32_t utf8_nbytes(uint8_t *bytes, uint32_t nbytes, uint32_t nchars) {
uint32_t unicode_visual_char_width(const struct codepoint *codepoint) {
if (codepoint->nbytes > 0) {
// TODO: use unicode classification instead
- size_t w = wcwidth(codepoint->codepoint);
+ int w = wcwidth(codepoint->codepoint);
return w >= 0 ? w : 2;
} else {
return 0;
diff --git a/src/dged/vec.h b/src/dged/vec.h
index 929c8d5..1289a08 100644
--- a/src/dged/vec.h
+++ b/src/dged/vec.h
@@ -48,8 +48,7 @@
}
#define VEC_SWAP(vec, idx1, idx2) \
- if (idx1 < (vec)->nentries && idx2 < (vec)->nentries && idx1 >= 0 && \
- idx2 >= 0) { \
+ if (idx1 < (vec)->nentries && idx2 < (vec)->nentries) { \
*((vec)->temp) = (vec)->entries[idx1]; \
(vec)->entries[idx1] = (vec)->entries[idx2]; \
(vec)->entries[idx2] = *((vec)->temp); \
diff --git a/src/dged/window.c b/src/dged/window.c
index 20cb22f..702f97a 100644
--- a/src/dged/window.c
+++ b/src/dged/window.c
@@ -124,25 +124,25 @@ static void window_tree_clear_sub(struct window_node *root_node) {
BINTREE_FREE_NODES(root_node, window_node);
}
-static void window_tree_clear() {
+static void window_tree_clear(void) {
window_tree_clear_sub(BINTREE_ROOT(&g_windows.windows));
}
-void windows_destroy() { window_tree_clear(); }
+void windows_destroy(void) { window_tree_clear(); }
-struct window *root_window() {
+struct window *root_window(void) {
return &BINTREE_VALUE(BINTREE_ROOT(&g_windows.windows));
}
-struct window *minibuffer_window() {
+struct window *minibuffer_window(void) {
return &g_minibuffer_window;
}
-struct window *popup_window() {
+struct window *popup_window(void) {
return &g_popup_window;
}
-bool popup_window_visible() { return g_popup_visible; }
+bool popup_window_visible(void) { return g_popup_visible; }
static void window_tree_resize(struct window_node *root, uint32_t height,
uint32_t width) {
@@ -412,7 +412,7 @@ struct window *window_find_by_buffer(struct buffer *b) {
return NULL;
}
-struct window *windows_get_active() {
+struct window *windows_get_active(void) {
return g_windows.active;
}
@@ -630,7 +630,7 @@ void window_split(struct window *window, struct window **new_window_a,
: window_vsplit(window, new_window_a, new_window_b);
}
-struct window *windows_focus_next() {
+struct window *windows_focus_next(void) {
struct window *active = windows_get_active();
struct window_node *n = find_window(active);
BINTREE_NEXT(n);
@@ -693,4 +693,4 @@ void windows_show_popup(uint32_t row, uint32_t col, uint32_t width,
g_popup_visible = true;
}
-void windows_close_popup() { g_popup_visible = false; }
+void windows_close_popup(void) { g_popup_visible = false; }
diff --git a/src/dged/window.h b/src/dged/window.h
index e1b1d25..7738e16 100644
--- a/src/dged/window.h
+++ b/src/dged/window.h
@@ -25,20 +25,20 @@ void windows_init(uint32_t height, uint32_t width,
struct buffer *initial_buffer, struct buffer *minibuffer,
struct buffers *buffers);
-void windows_destroy();
+void windows_destroy(void);
void windows_resize(uint32_t height, uint32_t width);
void windows_update(void *(*frame_alloc)(size_t), float frame_time);
void windows_render(struct display *display);
-struct window *root_window();
-struct window *minibuffer_window();
-struct window *popup_window();
-bool popup_window_visible();
+struct window *root_window(void);
+struct window *minibuffer_window(void);
+struct window *popup_window(void);
+bool popup_window_visible(void);
void windows_set_active(struct window *window);
struct window *windows_focus(uint32_t id);
-struct window *windows_get_active();
-struct window *windows_focus_next();
+struct window *windows_get_active(void);
+struct window *windows_focus_next(void);
struct window *window_find_by_buffer(struct buffer *b);
void window_set_buffer(struct window *window, struct buffer *buffer);
@@ -63,6 +63,6 @@ void window_vsplit(struct window *window, struct window **new_window_a,
void windows_show_popup(uint32_t row, uint32_t col, uint32_t width,
uint32_t height);
-void windows_close_popup();
+void windows_close_popup(void);
#endif
diff --git a/src/main/bindings.c b/src/main/bindings.c
index 7b25c7b..889c32b 100644
--- a/src/main/bindings.c
+++ b/src/main/bindings.c
@@ -72,6 +72,10 @@ void set_default_buffer_bindings(struct keymap *keymap) {
}
int32_t execute(struct command_ctx ctx, int argc, const char *argv[]) {
+ (void)ctx;
+ (void)argc;
+ (void)argv;
+
// TODO: this should be more lib-like
return minibuffer_execute();
}
@@ -82,7 +86,7 @@ static struct command execute_minibuffer_command = {
.userdata = NULL,
};
-void init_bindings() {
+void init_bindings(void) {
g_global_keymap = keymap_create("global", 32);
g_ctrlx_map = keymap_create("c-x", 32);
g_windows_keymap = keymap_create("c-x w", 32);
@@ -203,7 +207,7 @@ uint32_t buffer_keymaps(struct buffer *buffer, struct keymap *keymaps[],
return nkeymaps;
}
-void destroy_bindings() {
+void destroy_bindings(void) {
keymap_destroy(&g_windows_keymap);
keymap_destroy(&g_global_keymap);
keymap_destroy(&g_ctrlx_map);
diff --git a/src/main/bindings.h b/src/main/bindings.h
index 4fd760a..96f20fd 100644
--- a/src/main/bindings.h
+++ b/src/main/bindings.h
@@ -4,7 +4,7 @@ struct keymap;
struct buffer;
struct binding;
-void init_bindings();
+void init_bindings(void);
typedef uint64_t buffer_keymap_id;
buffer_keymap_id buffer_add_keymap(struct buffer *buffer, struct keymap keymap);
@@ -12,4 +12,4 @@ void buffer_remove_keymap(buffer_keymap_id id);
uint32_t buffer_keymaps(struct buffer *buffer, struct keymap *keymaps[],
uint32_t max_nkeymaps);
-void destroy_bindings();
+void destroy_bindings(void);
diff --git a/src/main/cmds.c b/src/main/cmds.c
index 18f333d..40983a2 100644
--- a/src/main/cmds.c
+++ b/src/main/cmds.c
@@ -20,7 +20,12 @@
#include "completion.h"
#include "search-replace.h"
-int32_t _abort(struct command_ctx ctx, int argc, const char *argv[]) {
+static void (*g_terminate_cb)(void) = NULL;
+
+static int32_t _abort(struct command_ctx ctx, int argc, const char *argv[]) {
+ (void)argc;
+ (void)argv;
+
abort_replace();
abort_search();
abort_completion();
@@ -33,16 +38,27 @@ int32_t _abort(struct command_ctx ctx, int argc, const char *argv[]) {
int32_t unimplemented_command(struct command_ctx ctx, int argc,
const char *argv[]) {
+ (void)argc;
+ (void)argv;
minibuffer_echo("TODO: %s is not implemented", (const char *)ctx.userdata);
return 0;
}
-int32_t exit_editor(struct command_ctx ctx, int argc, const char *argv[]) {
- ((void (*)())ctx.userdata)();
+static int32_t exit_editor(struct command_ctx ctx, int argc,
+ const char *argv[]) {
+ (void)ctx;
+ (void)argc;
+ (void)argv;
+
+ if (g_terminate_cb != NULL) {
+ g_terminate_cb();
+ }
+
return 0;
}
-int32_t write_file(struct command_ctx ctx, int argc, const char *argv[]) {
+static int32_t write_file(struct command_ctx ctx, int argc,
+ const char *argv[]) {
const char *pth = NULL;
if (argc == 0) {
return minibuffer_prompt(ctx, "write to file: ");
@@ -55,7 +71,7 @@ int32_t write_file(struct command_ctx ctx, int argc, const char *argv[]) {
return 0;
}
-static void run_interactive_comp_inserted() { minibuffer_execute(); }
+static void run_interactive_comp_inserted(void) { minibuffer_execute(); }
int32_t run_interactive(struct command_ctx ctx, int argc, const char *argv[]) {
if (argc == 0) {
@@ -63,7 +79,7 @@ int32_t run_interactive(struct command_ctx ctx, int argc, const char *argv[]) {
enable_completion(minibuffer_buffer(),
((struct completion_trigger){
.kind = CompletionTrigger_Input,
- .input =
+ .data.input =
(struct completion_trigger_input){
.nchars = 0, .trigger_initially = false}}),
providers, 1, run_interactive_comp_inserted);
@@ -106,9 +122,9 @@ int32_t do_switch_buffer(struct command_ctx ctx, int argc, const char *argv[]) {
}
}
-COMMAND_FN("do-switch-buffer", do_switch_buffer, do_switch_buffer, NULL);
+COMMAND_FN("do-switch-buffer", do_switch_buffer, do_switch_buffer, NULL)
-static void switch_buffer_comp_inserted() { minibuffer_execute(); }
+static void switch_buffer_comp_inserted(void) { minibuffer_execute(); }
int32_t switch_buffer(struct command_ctx ctx, int argc, const char *argv[]) {
if (argc == 0) {
@@ -117,7 +133,7 @@ int32_t switch_buffer(struct command_ctx ctx, int argc, const char *argv[]) {
enable_completion(minibuffer_buffer(),
((struct completion_trigger){
.kind = CompletionTrigger_Input,
- .input =
+ .data.input =
(struct completion_trigger_input){
.nchars = 0, .trigger_initially = false}}),
providers, 1, switch_buffer_comp_inserted);
@@ -156,9 +172,9 @@ int32_t do_kill_buffer(struct command_ctx ctx, int argc, const char *argv[]) {
}
}
-COMMAND_FN("do-kill-buffer", do_kill_buffer, do_kill_buffer, NULL);
+COMMAND_FN("do-kill-buffer", do_kill_buffer, do_kill_buffer, NULL)
-static void kill_buffer_comp_inserted() { minibuffer_execute(); }
+static void kill_buffer_comp_inserted(void) { minibuffer_execute(); }
int32_t kill_buffer(struct command_ctx ctx, int argc, const char *argv[]) {
if (argc == 0) {
@@ -167,7 +183,7 @@ int32_t kill_buffer(struct command_ctx ctx, int argc, const char *argv[]) {
enable_completion(minibuffer_buffer(),
((struct completion_trigger){
.kind = CompletionTrigger_Input,
- .input =
+ .data.input =
(struct completion_trigger_input){
.nchars = 0, .trigger_initially = false}}),
providers, 1, kill_buffer_comp_inserted);
@@ -188,7 +204,6 @@ void timer_to_list_line(const struct timer *timer, void *userdata) {
static char buf[128];
const char *name = timer_name(timer);
- size_t namelen = strlen(name);
size_t len =
snprintf(buf, 128, "%s - %.2f ms (min: %.2f, max: %.2f)", name,
(timer_average(timer) / 1e6), timer_min(timer) / (float)1e6,
@@ -197,6 +212,8 @@ void timer_to_list_line(const struct timer *timer, void *userdata) {
}
void timers_refresh(struct buffer *buffer, void *userdata) {
+ (void)userdata;
+
buffer_set_readonly(buffer, false);
buffer_clear(buffer);
timers_for_each(timer_to_list_line, buffer);
@@ -208,6 +225,9 @@ void timers_refresh(struct buffer *buffer, void *userdata) {
}
int32_t timers(struct command_ctx ctx, int argc, const char *argv[]) {
+ (void)argc;
+ (void)argv;
+
struct buffer *b = buffers_find(ctx.buffers, "*timers*");
if (b == NULL) {
b = buffers_add(ctx.buffers, buffer_create("*timers*"));
@@ -236,7 +256,7 @@ void buffer_to_list_line(struct buffer *buffer, void *userdata) {
listbuf, begin,
(struct location){.line = begin.line, .col = begin.col + nchars},
(struct text_property){.type = TextProperty_Colors,
- .colors = (struct text_property_colors){
+ .data.colors = (struct text_property_colors){
.set_bg = false,
.set_fg = true,
.fg = Color_Green,
@@ -249,7 +269,7 @@ void buffer_to_list_line(struct buffer *buffer, void *userdata) {
(struct location){.line = begin.line,
.col = begin.col + 24 + nchars_path},
(struct text_property){.type = TextProperty_Colors,
- .colors = (struct text_property_colors){
+ .data.colors = (struct text_property_colors){
.set_bg = false,
.set_fg = true,
.fg = Color_Blue,
@@ -259,12 +279,16 @@ void buffer_to_list_line(struct buffer *buffer, void *userdata) {
listbuf, (struct location){.line = begin.line, .col = 0},
(struct location){.line = begin.line,
.col = buffer_line_length(listbuf, begin.line)},
- (struct text_property){.type = TextProperty_Data, .userdata = buffer});
+ (struct text_property){.type = TextProperty_Data,
+ .data.userdata = buffer});
}
}
int32_t buflist_visit_cmd(struct command_ctx ctx, int argc,
const char *argv[]) {
+ (void)argc;
+ (void)argv;
+
struct window *w = ctx.active_window;
struct buffer_view *bv = window_buffer_view(w);
@@ -275,7 +299,7 @@ int32_t buflist_visit_cmd(struct command_ctx ctx, int argc,
for (uint32_t propi = 0; propi < nprops; ++propi) {
struct text_property *p = props[propi];
if (p->type == TextProperty_Data) {
- window_set_buffer(w, p->userdata);
+ window_set_buffer(w, p->data.userdata);
return 0;
}
}
@@ -300,6 +324,8 @@ void buflist_refresh(struct buffer *buffer, void *userdata) {
int32_t buflist_refresh_cmd(struct command_ctx ctx, int argc,
const char *argv[]) {
+ (void)argc;
+ (void)argv;
buflist_refresh(window_buffer(ctx.active_window), ctx.buffers);
return 0;
}
@@ -310,6 +336,9 @@ static struct command buflist_refresh_command = {
};
int32_t buflist_kill_cmd(struct command_ctx ctx, int argc, const char *argv[]) {
+ (void)argc;
+ (void)argv;
+
struct window *w = ctx.active_window;
struct buffer_view *bv = window_buffer_view(w);
@@ -333,6 +362,9 @@ int32_t buflist_kill_cmd(struct command_ctx ctx, int argc, const char *argv[]) {
}
int32_t buffer_list(struct command_ctx ctx, int argc, const char *argv[]) {
+ (void)argc;
+ (void)argv;
+
struct buffer *b = buffers_find(ctx.buffers, "*buffers*");
if (b == NULL) {
b = buffers_add(ctx.buffers, buffer_create("*buffers*"));
@@ -373,7 +405,7 @@ int32_t buffer_list(struct command_ctx ctx, int argc, const char *argv[]) {
return 0;
}
-static void find_file_comp_inserted() { minibuffer_execute(); }
+static void find_file_comp_inserted(void) { minibuffer_execute(); }
static int32_t open_file(struct buffers *buffers, struct window *active_window,
const char *pth) {
@@ -412,14 +444,13 @@ static int32_t open_file(struct buffers *buffers, struct window *active_window,
}
int32_t find_file(struct command_ctx ctx, int argc, const char *argv[]) {
- const char *pth = NULL;
if (argc == 0) {
minibuffer_clear();
struct completion_provider providers[] = {path_provider()};
enable_completion(minibuffer_buffer(),
((struct completion_trigger){
.kind = CompletionTrigger_Input,
- .input =
+ .data.input =
(struct completion_trigger_input){
.nchars = 0, .trigger_initially = true}}),
providers, 1, find_file_comp_inserted);
@@ -432,7 +463,7 @@ int32_t find_file(struct command_ctx ctx, int argc, const char *argv[]) {
return 0;
}
-COMMAND_FN("find-file-internal", find_file, find_file, NULL);
+COMMAND_FN("find-file-internal", find_file, find_file, NULL)
int32_t find_file_relative(struct command_ctx ctx, int argc,
const char *argv[]) {
struct buffer *b = window_buffer(ctx.active_window);
@@ -450,7 +481,7 @@ int32_t find_file_relative(struct command_ctx ctx, int argc,
enable_completion(minibuffer_buffer(),
((struct completion_trigger){
.kind = CompletionTrigger_Input,
- .input =
+ .data.input =
(struct completion_trigger_input){
.nchars = 0, .trigger_initially = true}}),
providers, 1, find_file_comp_inserted);
@@ -481,7 +512,8 @@ int32_t find_file_relative(struct command_ctx ctx, int argc,
}
void register_global_commands(struct commands *commands,
- void (*terminate_cb)()) {
+ void (*terminate_cb)(void)) {
+ g_terminate_cb = terminate_cb;
struct command global_commands[] = {
{.name = "find-file", .fn = find_file},
{.name = "find-file-relative", .fn = find_file_relative},
@@ -492,7 +524,7 @@ void register_global_commands(struct commands *commands,
{.name = "abort", .fn = _abort},
{.name = "timers", .fn = timers},
{.name = "buffer-list", .fn = buffer_list},
- {.name = "exit", .fn = exit_editor, .userdata = terminate_cb}};
+ {.name = "exit", .fn = exit_editor}};
register_commands(commands, global_commands,
sizeof(global_commands) / sizeof(global_commands[0]));
@@ -505,6 +537,8 @@ void teardown_global_commands(void) { cleanup_search_replace(); }
#define BUFFER_VIEW_WRAPCMD(fn) \
static int32_t fn##_cmd(struct command_ctx ctx, int argc, \
const char *argv[]) { \
+ (void)argc; \
+ (void)argv; \
buffer_view_##fn(window_buffer_view(ctx.active_window)); \
return 0; \
}
@@ -512,51 +546,59 @@ void teardown_global_commands(void) { cleanup_search_replace(); }
#define BUFFER_WRAPCMD(fn) \
static int32_t fn##_cmd(struct command_ctx ctx, int argc, \
const char *argv[]) { \
+ (void)argc; \
+ (void)argv; \
buffer_##fn(window_buffer(ctx.active_window)); \
return 0; \
}
-BUFFER_WRAPCMD(to_file);
-BUFFER_WRAPCMD(reload);
-BUFFER_VIEW_WRAPCMD(kill_line);
-BUFFER_VIEW_WRAPCMD(forward_delete_char);
-BUFFER_VIEW_WRAPCMD(backward_delete_char);
-BUFFER_VIEW_WRAPCMD(delete_word);
-BUFFER_VIEW_WRAPCMD(backward_char);
-BUFFER_VIEW_WRAPCMD(backward_word);
-BUFFER_VIEW_WRAPCMD(forward_char);
-BUFFER_VIEW_WRAPCMD(forward_word);
-BUFFER_VIEW_WRAPCMD(backward_line);
-BUFFER_VIEW_WRAPCMD(forward_line);
-BUFFER_VIEW_WRAPCMD(goto_end_of_line);
-BUFFER_VIEW_WRAPCMD(goto_beginning_of_line);
-BUFFER_VIEW_WRAPCMD(newline);
-BUFFER_VIEW_WRAPCMD(indent);
-BUFFER_VIEW_WRAPCMD(indent_alt);
-BUFFER_VIEW_WRAPCMD(set_mark);
-BUFFER_VIEW_WRAPCMD(clear_mark);
-BUFFER_VIEW_WRAPCMD(copy);
-BUFFER_VIEW_WRAPCMD(cut);
-BUFFER_VIEW_WRAPCMD(paste);
-BUFFER_VIEW_WRAPCMD(paste_older);
-BUFFER_VIEW_WRAPCMD(goto_beginning);
-BUFFER_VIEW_WRAPCMD(goto_end);
-BUFFER_VIEW_WRAPCMD(undo);
-BUFFER_VIEW_WRAPCMD(sort_lines);
+BUFFER_WRAPCMD(to_file)
+BUFFER_WRAPCMD(reload)
+BUFFER_VIEW_WRAPCMD(kill_line)
+BUFFER_VIEW_WRAPCMD(forward_delete_char)
+BUFFER_VIEW_WRAPCMD(backward_delete_char)
+BUFFER_VIEW_WRAPCMD(delete_word)
+BUFFER_VIEW_WRAPCMD(backward_char)
+BUFFER_VIEW_WRAPCMD(backward_word)
+BUFFER_VIEW_WRAPCMD(forward_char)
+BUFFER_VIEW_WRAPCMD(forward_word)
+BUFFER_VIEW_WRAPCMD(backward_line)
+BUFFER_VIEW_WRAPCMD(forward_line)
+BUFFER_VIEW_WRAPCMD(goto_end_of_line)
+BUFFER_VIEW_WRAPCMD(goto_beginning_of_line)
+BUFFER_VIEW_WRAPCMD(newline)
+BUFFER_VIEW_WRAPCMD(indent)
+BUFFER_VIEW_WRAPCMD(indent_alt)
+BUFFER_VIEW_WRAPCMD(set_mark)
+BUFFER_VIEW_WRAPCMD(clear_mark)
+BUFFER_VIEW_WRAPCMD(copy)
+BUFFER_VIEW_WRAPCMD(cut)
+BUFFER_VIEW_WRAPCMD(paste)
+BUFFER_VIEW_WRAPCMD(paste_older)
+BUFFER_VIEW_WRAPCMD(goto_beginning)
+BUFFER_VIEW_WRAPCMD(goto_end)
+BUFFER_VIEW_WRAPCMD(undo)
+BUFFER_VIEW_WRAPCMD(sort_lines)
static int32_t scroll_up_cmd(struct command_ctx ctx, int argc,
const char *argv[]) {
+ (void)argc;
+ (void)argv;
+
buffer_view_backward_nlines(window_buffer_view(ctx.active_window),
window_height(ctx.active_window) - 1);
return 0;
-};
+}
static int32_t scroll_down_cmd(struct command_ctx ctx, int argc,
const char *argv[]) {
+ (void)argc;
+ (void)argv;
+
buffer_view_forward_nlines(window_buffer_view(ctx.active_window),
window_height(ctx.active_window) - 1);
return 0;
-};
+}
static int32_t goto_line(struct command_ctx ctx, int argc, const char *argv[]) {
// don't want to goto line in minibuffer
@@ -574,7 +616,7 @@ static int32_t goto_line(struct command_ctx ctx, int argc, const char *argv[]) {
if (line < 0) {
uint32_t nlines = buffer_num_lines(v->buffer);
line = -line;
- line = line >= nlines ? 0 : nlines - line;
+ line = (uint32_t)line >= nlines ? 0 : nlines - line;
} else if (line > 0) {
line = line - 1;
}
@@ -623,12 +665,18 @@ void register_buffer_commands(struct commands *commands) {
static int32_t window_close_cmd(struct command_ctx ctx, int argc,
const char *argv[]) {
+ (void)argc;
+ (void)argv;
+
window_close(ctx.active_window);
return 0;
}
static int32_t window_split_cmd(struct command_ctx ctx, int argc,
const char *argv[]) {
+ (void)argc;
+ (void)argv;
+
struct window *resa, *resb;
window_split(ctx.active_window, &resa, &resb);
return 0;
@@ -636,6 +684,9 @@ static int32_t window_split_cmd(struct command_ctx ctx, int argc,
static int32_t window_hsplit_cmd(struct command_ctx ctx, int argc,
const char *argv[]) {
+ (void)argc;
+ (void)argv;
+
struct window *resa, *resb;
window_hsplit(ctx.active_window, &resa, &resb);
return 0;
@@ -643,6 +694,9 @@ static int32_t window_hsplit_cmd(struct command_ctx ctx, int argc,
static int32_t window_vsplit_cmd(struct command_ctx ctx, int argc,
const char *argv[]) {
+ (void)argc;
+ (void)argv;
+
struct window *resa, *resb;
window_vsplit(ctx.active_window, &resa, &resb);
return 0;
@@ -650,12 +704,19 @@ static int32_t window_vsplit_cmd(struct command_ctx ctx, int argc,
static int32_t window_close_others_cmd(struct command_ctx ctx, int argc,
const char *argv[]) {
+ (void)argc;
+ (void)argv;
+
window_close_others(ctx.active_window);
return 0;
}
static int32_t window_focus_next_cmd(struct command_ctx ctx, int argc,
const char *argv[]) {
+ (void)ctx;
+ (void)argc;
+ (void)argv;
+
windows_focus_next();
return 0;
}
@@ -676,6 +737,9 @@ static int32_t window_focus_cmd(struct command_ctx ctx, int argc,
static int32_t window_focus_n_cmd(struct command_ctx ctx, int argc,
const char *argv[]) {
+ (void)argc;
+ (void)argv;
+
char *window_id = (char *)ctx.userdata;
const char *argv_[] = {window_id};
return window_focus_cmd(ctx, 1, argv_);
@@ -730,15 +794,15 @@ int32_t settings_set_cmd(struct command_ctx ctx, int argc, const char *argv[]) {
struct setting_value new_value = {.type = setting->value.type};
switch (setting->value.type) {
case Setting_Bool:
- new_value.bool_value = strncmp("true", value, 4) == 0 ||
- strncmp("yes", value, 3) == 0 ||
- strncmp("on", value, 2) == 0;
+ new_value.data.bool_value = strncmp("true", value, 4) == 0 ||
+ strncmp("yes", value, 3) == 0 ||
+ strncmp("on", value, 2) == 0;
break;
case Setting_Number:
- new_value.number_value = atol(value);
+ new_value.data.number_value = atol(value);
break;
case Setting_String:
- new_value.string_value = (char *)value;
+ new_value.data.string_value = (char *)value;
break;
}
diff --git a/src/main/cmds.h b/src/main/cmds.h
index a258ce5..324842d 100644
--- a/src/main/cmds.h
+++ b/src/main/cmds.h
@@ -1,7 +1,7 @@
struct commands;
void register_global_commands(struct commands *commands,
- void (*terminate_cb)());
+ void (*terminate_cb)(void));
void teardown_global_commands(void);
void register_buffer_commands(struct commands *commands);
diff --git a/src/main/completion.c b/src/main/completion.c
index 4ffbc46..7a002ac 100644
--- a/src/main/completion.c
+++ b/src/main/completion.c
@@ -38,7 +38,7 @@ struct completion_state {
static struct buffer *g_target_buffer = NULL;
-static void hide_completion();
+static void hide_completion(void);
static bool is_space(const struct codepoint *c) {
// TODO: utf8 whitespace and other whitespace
@@ -67,15 +67,15 @@ static struct completion_provider g_commands_provider = {
.userdata = NULL,
};
-struct completion_provider path_provider() {
+struct completion_provider path_provider(void) {
return g_path_provider;
}
-struct completion_provider buffer_provider() {
+struct completion_provider buffer_provider(void) {
return g_buffer_provider;
}
-struct completion_provider commands_provider() {
+struct completion_provider commands_provider(void) {
return g_commands_provider;
}
@@ -89,6 +89,10 @@ VEC(struct active_completion) g_active_completions;
static int32_t goto_next_completion(struct command_ctx ctx, int argc,
const char *argv[]) {
+ (void)ctx;
+ (void)argc;
+ (void)argv;
+
if (g_state.current_completion < g_state.ncompletions - 1) {
++g_state.current_completion;
}
@@ -104,6 +108,10 @@ static int32_t goto_next_completion(struct command_ctx ctx, int argc,
static int32_t goto_prev_completion(struct command_ctx ctx, int argc,
const char *argv[]) {
+ (void)ctx;
+ (void)argc;
+ (void)argv;
+
if (g_state.current_completion > 0) {
--g_state.current_completion;
}
@@ -119,6 +127,10 @@ static int32_t goto_prev_completion(struct command_ctx ctx, int argc,
static int32_t insert_completion(struct command_ctx ctx, int argc,
const char *argv[]) {
+ (void)ctx;
+ (void)argc;
+ (void)argv;
+
// is it in the popup?
struct completion *comp = &g_state.completions[g_state.current_completion];
bool done = comp->complete;
@@ -135,7 +147,7 @@ static int32_t insert_completion(struct command_ctx ctx, int argc,
return 0;
}
-static void clear_completions() {
+static void clear_completions(void) {
for (uint32_t ci = 0; ci < g_state.ncompletions; ++ci) {
free((void *)g_state.completions[ci].display);
free((void *)g_state.completions[ci].insert);
@@ -146,9 +158,9 @@ static void clear_completions() {
g_state.ncompletions = 0;
}
-COMMAND_FN("next-completion", next_completion, goto_next_completion, NULL);
-COMMAND_FN("prev-completion", prev_completion, goto_prev_completion, NULL);
-COMMAND_FN("insert-completion", insert_completion, insert_completion, NULL);
+COMMAND_FN("next-completion", next_completion, goto_next_completion, NULL)
+COMMAND_FN("prev-completion", prev_completion, goto_prev_completion, NULL)
+COMMAND_FN("insert-completion", insert_completion, insert_completion, NULL)
static void update_completions(struct buffer *buffer,
struct active_completion_ctx *ctx,
@@ -246,7 +258,7 @@ static void on_buffer_insert(struct buffer *buffer,
ctx->trigger_current_nchars += nchars;
- if (ctx->trigger_current_nchars < ctx->trigger.input.nchars) {
+ if (ctx->trigger_current_nchars < ctx->trigger.data.input.nchars) {
return;
}
@@ -267,6 +279,9 @@ static void on_buffer_insert(struct buffer *buffer,
}
static void update_completion_buffer(struct buffer *buffer, void *userdata) {
+ (void)buffer;
+ (void)userdata;
+
buffer_add_text_property(
g_target_buffer,
(struct location){.line = g_state.current_completion, .col = 0},
@@ -274,7 +289,7 @@ static void update_completion_buffer(struct buffer *buffer, void *userdata) {
.col = buffer_line_length(g_target_buffer,
g_state.current_completion)},
(struct text_property){.type = TextProperty_Colors,
- .colors = (struct text_property_colors){
+ .data.colors = (struct text_property_colors){
.set_bg = false,
.bg = 0,
.set_fg = true,
@@ -352,7 +367,7 @@ void enable_completion(struct buffer *source, struct completion_trigger trigger,
// do we want to trigger initially?
if (ctx->trigger.kind == CompletionTrigger_Input &&
- ctx->trigger.input.trigger_initially) {
+ ctx->trigger.data.input.trigger_initially) {
struct oneshot_completion *comp =
calloc(1, sizeof(struct oneshot_completion));
comp->ctx = ctx;
@@ -361,7 +376,7 @@ void enable_completion(struct buffer *source, struct completion_trigger trigger,
}
}
-static void hide_completion() {
+static void hide_completion(void) {
windows_close_popup();
if (g_state.active) {
buffer_remove_keymap(g_state.keymap_id);
@@ -369,13 +384,13 @@ static void hide_completion() {
}
}
-void abort_completion() {
+void abort_completion(void) {
hide_completion();
g_state.active = false;
clear_completions();
}
-bool completion_active() {
+bool completion_active(void) {
return popup_window_visible() &&
window_buffer(popup_window()) == g_target_buffer && g_state.active;
}
@@ -391,7 +406,7 @@ static void cleanup_active_comp_ctx(void *userdata) {
free(ctx);
}
-static void do_nothing(void *userdata) {}
+static void do_nothing(void *userdata) { (void)userdata; }
static void cleanup_active_completion(struct active_completion *comp) {
buffer_remove_delete_hook(comp->buffer, comp->remove_hook_id, do_nothing);
@@ -410,7 +425,7 @@ void disable_completion(struct buffer *buffer) {
}
}
-void destroy_completion() {
+void destroy_completion(void) {
// clean up any active completions we might have
VEC_FOR_EACH(&g_active_completions, struct active_completion * comp) {
cleanup_active_completion(comp);
@@ -429,10 +444,10 @@ static int cmp_completions(const void *comp_a, const void *comp_b) {
}
static uint32_t complete_path(struct completion_context ctx, void *userdata) {
+ (void)userdata;
// obtain path from the buffer
struct text_chunk txt = {0};
- uint32_t start_idx = 0;
if (ctx.buffer == minibuffer_buffer()) {
txt = minibuffer_content();
} else {
@@ -455,7 +470,6 @@ static uint32_t complete_path(struct completion_context ctx, void *userdata) {
uint32_t n = 0;
char *p1 = to_abspath(path);
- size_t len = strlen(p1);
char *p2 = strdup(p1);
size_t inlen = strlen(path);
@@ -553,7 +567,6 @@ static uint32_t complete_buffers(struct completion_context ctx,
}
struct text_chunk txt = {0};
- uint32_t start_idx = 0;
if (ctx.buffer == minibuffer_buffer()) {
txt = minibuffer_content();
} else {
@@ -608,7 +621,6 @@ static uint32_t complete_commands(struct completion_context ctx,
return 0;
}
struct text_chunk txt = {0};
- uint32_t start_idx = 0;
if (ctx.buffer == minibuffer_buffer()) {
txt = minibuffer_content();
} else {
diff --git a/src/main/completion.h b/src/main/completion.h
index 28871b9..f2ce186 100644
--- a/src/main/completion.h
+++ b/src/main/completion.h
@@ -97,10 +97,10 @@ struct completion_trigger_input {
struct completion_trigger {
/** Type of trigger. */
enum completion_trigger_kind kind;
- union {
+ union completion_trigger_data {
uint32_t c;
struct completion_trigger_input input;
- };
+ } data;
};
/**
@@ -114,12 +114,12 @@ void init_completion(struct buffers *buffers, struct commands *commands);
/**
* Tear down the completion system.
*/
-void destroy_completion();
+void destroy_completion(void);
/**
* Callback for completion inserted.
*/
-typedef void (*insert_cb)();
+typedef void (*insert_cb)(void);
/**
* Enable completions in the buffer @p source.
@@ -141,7 +141,7 @@ void enable_completion(struct buffer *source, struct completion_trigger trigger,
* This provider completes filesystem paths.
* @returns A filesystem path @ref completion_provider.
*/
-struct completion_provider path_provider();
+struct completion_provider path_provider(void);
/**
* Create a new buffer completion provider.
@@ -150,7 +150,7 @@ struct completion_provider path_provider();
* buffer list.
* @returns A buffer name @ref completion_provider.
*/
-struct completion_provider buffer_provider();
+struct completion_provider buffer_provider(void);
/**
* Create a new command completion provider.
@@ -158,19 +158,19 @@ struct completion_provider buffer_provider();
* This provider completes registered command names.
* @returns A command name @ref completion_provider.
*/
-struct completion_provider commands_provider();
+struct completion_provider commands_provider(void);
/**
* Abort any active completion.
*/
-void abort_completion();
+void abort_completion(void);
/**
* Is a completion currently showing?
*
* @returns True if the completion window is showing completions.
*/
-bool completion_active();
+bool completion_active(void);
/**
* Disable completion for @ref buffer.
diff --git a/src/main/lsp.c b/src/main/lsp.c
new file mode 100644
index 0000000..d56ca07
--- /dev/null
+++ b/src/main/lsp.c
@@ -0,0 +1,108 @@
+#include "lsp.h"
+
+#include "dged/buffer.h"
+#include "dged/buffers.h"
+#include "dged/hash.h"
+#include "dged/hashmap.h"
+#include "dged/lsp.h"
+#include "dged/minibuffer.h"
+#include "dged/reactor.h"
+#include "dged/settings.h"
+
+HASHMAP_ENTRY_TYPE(lsp_entry, struct lsp *);
+
+HASHMAP(struct lsp_entry) g_lsp_clients;
+
+static struct create_data {
+ struct reactor *reactor;
+ struct buffers *buffers;
+} g_create_data;
+
+static void log_message(int type, struct s8 msg) {
+ (void)type;
+ message("%s", msg);
+}
+
+static void create_lsp_client(struct buffer *buffer, void *userdata) {
+ (void)userdata;
+
+ struct create_data *data = &g_create_data;
+ const char *id = buffer->lang.id;
+ HASHMAP_GET(&g_lsp_clients, struct lsp_entry, id, struct lsp * *lsp);
+ if (lsp == NULL) {
+ // we need to start a new server
+ struct setting *s = lang_setting(&buffer->lang, "language-server");
+ if (!s) { // no language server set
+ return;
+ }
+
+ char *const command[] = {s->value.data.string_value, NULL};
+
+ char bufname[1024] = {0};
+ snprintf(bufname, 1024, "*%s-lsp-stderr*", command[0]);
+ struct buffer *stderr_buf = buffers_find(data->buffers, bufname);
+ if (stderr_buf == NULL) {
+ struct buffer buf = buffer_create(bufname);
+ buf.lazy_row_add = false;
+ stderr_buf = buffers_add(data->buffers, buf);
+ buffer_set_readonly(stderr_buf, true);
+ }
+
+ struct lsp_client client_impl = {
+ .log_message = log_message,
+ };
+ struct lsp *new_lsp =
+ lsp_create(command, data->reactor, stderr_buf, client_impl, NULL);
+
+ if (new_lsp == NULL) {
+ minibuffer_echo("failed to create language server %s", command[0]);
+ buffers_remove(data->buffers, bufname);
+ return;
+ }
+
+ HASHMAP_APPEND(&g_lsp_clients, struct lsp_entry, id,
+ struct lsp_entry * new);
+ new->value = new_lsp;
+
+ if (lsp_start_server(new_lsp) < 0) {
+ minibuffer_echo("failed to start language server %s process.",
+ lsp_server_name(new_lsp));
+ return;
+ }
+ }
+}
+
+static void set_default_lsp(const char *lang_id, const char *server) {
+ struct language l = lang_from_id(lang_id);
+ if (!lang_is_fundamental(&l)) {
+ lang_setting_set_default(
+ &l, "language-server",
+ (struct setting_value){.type = Setting_String,
+ .data.string_value = (char *)server});
+ lang_destroy(&l);
+ }
+}
+
+void lang_servers_init(struct reactor *reactor, struct buffers *buffers) {
+ HASHMAP_INIT(&g_lsp_clients, 32, hash_name);
+
+ set_default_lsp("c", "clangd");
+ set_default_lsp("rs", "rust-analyzer");
+ set_default_lsp("python", "pylsp");
+
+ g_create_data.reactor = reactor;
+ g_create_data.buffers = buffers;
+ buffer_add_create_hook(create_lsp_client, NULL);
+}
+
+void lang_servers_update(void) {
+ HASHMAP_FOR_EACH(&g_lsp_clients, struct lsp_entry * e) {
+ lsp_update(e->value, NULL, 0);
+ }
+}
+
+void lang_servers_teardown(void) {
+ HASHMAP_FOR_EACH(&g_lsp_clients, struct lsp_entry * e) {
+ lsp_stop_server(e->value);
+ }
+}
diff --git a/src/main/lsp.h b/src/main/lsp.h
new file mode 100644
index 0000000..736282d
--- /dev/null
+++ b/src/main/lsp.h
@@ -0,0 +1,11 @@
+#ifndef _MAIN_LSP_H
+#define _MAIN_LSP_H
+
+struct reactor;
+struct buffers;
+
+void lang_servers_init(struct reactor *reactor, struct buffers *buffers);
+void lang_servers_update(void);
+void lang_servers_teardown(void);
+
+#endif
diff --git a/src/main/main.c b/src/main/main.c
index 45f72cb..fa740e8 100644
--- a/src/main/main.c
+++ b/src/main/main.c
@@ -9,6 +9,8 @@
#include <time.h>
#include <unistd.h>
+#include "config.h"
+
#include "dged/allocator.h"
#include "dged/binding.h"
#include "dged/buffer.h"
@@ -29,6 +31,10 @@
#define str(s) #s
#endif
+#ifdef LSP_ENABLE
+#include "lsp.h"
+#endif
+
#include "bindings.h"
#include "cmds.h"
#include "completion.h"
@@ -48,11 +54,16 @@ void *frame_alloc(size_t sz) {
static bool running = true;
-void terminate() { running = false; }
+void terminate(void) { running = false; }
+void terminate2(int sig) {
+ (void)sig;
+ running = false;
+}
static struct display *display = NULL;
static bool display_resized = false;
-void resized() {
+void resized(int sig) {
+ (void)sig;
if (display != NULL) {
display_resize(display);
}
@@ -61,7 +72,9 @@ void resized() {
signal(SIGWINCH, resized);
}
-void segfault() {
+void segfault(int sig) {
+ (void)sig;
+
// make an effort to restore the
// terminal to its former glory
if (display != NULL) {
@@ -73,7 +86,7 @@ void segfault() {
abort();
}
-#define INVALID_WATCH -1
+#define INVALID_WATCH (uint32_t) - 1
static void clear_buffer_props(struct buffer *buffer, void *userdata) {
(void)userdata;
@@ -139,13 +152,13 @@ void update_file_watches(struct reactor *reactor) {
}
}
-static void usage() {
+static void usage(void) {
printf("dged - a text editor for datagubbar/datagummor!\n");
printf("usage: dged [-l/--line line_number] [-e/--end] [-h/--help] "
"[filename]\n");
}
-static void version() {
+static void version(void) {
printf("dged - %s\n© Albert Cervin 2024\n", DGED_VERSION);
}
@@ -194,7 +207,7 @@ int main(int argc, char *argv[]) {
setlocale(LC_ALL, "");
- signal(SIGTERM, terminate);
+ signal(SIGTERM, terminate2);
signal(SIGSEGV, segfault);
struct commands commands = command_registry_create(32);
@@ -212,7 +225,8 @@ int main(int argc, char *argv[]) {
int32_t ret = settings_from_file(settings_file_abs, &errmsgs);
if (ret > 0) {
fprintf(stderr, "Error reading settings from %s:\n", settings_file_abs);
- for (uint32_t erri = 0; erri < ret; ++erri) {
+ uint32_t nerrors = (uint32_t)ret;
+ for (uint32_t erri = 0; erri < nerrors; ++erri) {
fprintf(stderr, " - %s", errmsgs[erri]);
free(errmsgs[erri]);
}
@@ -266,7 +280,7 @@ int main(int argc, char *argv[]) {
struct setting *path_setting = settings_get("editor.grammars-path");
char *settings_path = NULL;
if (path_setting != NULL && path_setting->value.type == Setting_String) {
- settings_path = path_setting->value.string_value;
+ settings_path = path_setting->value.data.string_value;
}
const char *builtin_path = join_path(xstr(DATADIR), "grammars");
@@ -309,6 +323,10 @@ int main(int argc, char *argv[]) {
free((void *)builtin_path);
#endif
+#ifdef LSP_ENABLE
+ lang_servers_init(reactor, &buflist);
+#endif
+
struct buffer initial_buffer = buffer_create("welcome");
if (filename != NULL) {
buffer_destroy(&initial_buffer);
@@ -394,7 +412,6 @@ int main(int argc, char *argv[]) {
struct keyboard_update kbd_upd =
keyboard_update(&kbd, reactor, frame_alloc);
- uint32_t input_data_idx = 0;
for (uint32_t ki = 0; ki < kbd_upd.nkeys; ++ki) {
struct key *k = &kbd_upd.keys[ki];
@@ -416,15 +433,15 @@ int main(int argc, char *argv[]) {
if (res.found) {
switch (res.type) {
case BindingType_Command: {
- if (res.command == NULL) {
+ if (res.data.command == NULL) {
minibuffer_echo_timeout(
4, "binding found for key %s but not command", k);
} else {
- int32_t ec = execute_command(res.command, &commands, active_window,
- &buflist, 0, NULL);
+ int32_t ec = execute_command(res.data.command, &commands,
+ active_window, &buflist, 0, NULL);
if (ec != 0 && !minibuffer_displaying()) {
minibuffer_echo_timeout(4, "command %s failed with exit code %d",
- res.command->name, ec);
+ res.data.command->name, ec);
}
}
current_keymap = NULL;
@@ -443,7 +460,7 @@ int main(int argc, char *argv[]) {
minibuffer_echo("%s", keyname);
}
- current_keymap = res.keymap;
+ current_keymap = res.data.keymap;
break;
}
}
@@ -469,6 +486,10 @@ int main(int argc, char *argv[]) {
update_file_watches(reactor);
+#if defined(LSP_ENABLE)
+ lang_servers_update();
+#endif
+
// calculate frame time
frame_time = timer_average(update_windows) +
timer_average(update_keyboard) + timer_average(update_display);
@@ -489,6 +510,10 @@ int main(int argc, char *argv[]) {
syntax_teardown();
#endif
+#ifdef LSP_ENABLE
+ lang_servers_teardown();
+#endif
+
display_clear(display);
display_destroy(display);
destroy_bindings();
diff --git a/src/main/search-replace.c b/src/main/search-replace.c
index 50beb1e..32c514c 100644
--- a/src/main/search-replace.c
+++ b/src/main/search-replace.c
@@ -45,6 +45,55 @@ static struct search {
buffer_keymap_id keymap_id;
} g_current_search = {0};
+static void highlight_match(struct buffer *buffer, struct region match,
+ bool current) {
+ if (current) {
+ buffer_add_text_property(
+ buffer, match.begin, match.end,
+ (struct text_property){.type = TextProperty_Colors,
+ .data.colors = (struct text_property_colors){
+ .set_bg = true,
+ .bg = 3,
+ .set_fg = true,
+ .fg = 0,
+ }});
+
+ } else {
+ buffer_add_text_property(
+ buffer, match.begin, match.end,
+ (struct text_property){.type = TextProperty_Colors,
+ .data.colors = (struct text_property_colors){
+ .set_bg = true,
+ .bg = 6,
+ .set_fg = true,
+ .fg = 0,
+ }});
+ }
+}
+
+static void search_highlight_hook(struct buffer *buffer, void *userdata) {
+ (void)userdata;
+
+ for (uint32_t matchi = 0; matchi < g_current_search.nmatches; ++matchi) {
+ highlight_match(buffer, g_current_search.matches[matchi],
+ matchi == g_current_search.current_match);
+ }
+}
+
+static void replace_highlight_hook(struct buffer *buffer, void *userdata) {
+ (void)userdata;
+
+ for (uint32_t matchi = 0; matchi < g_current_replace.nmatches; ++matchi) {
+ struct match *m = &g_current_replace.matches[matchi];
+ if (m->state != Todo) {
+ continue;
+ }
+
+ highlight_match(buffer, m->region,
+ matchi == g_current_replace.current_match);
+ }
+}
+
static void clear_replace(void) {
buffer_remove_keymap(g_current_replace.keymap_id);
free(g_current_replace.matches);
@@ -89,6 +138,36 @@ void abort_search(void) {
minibuffer_abort_prompt();
}
+static void start_search(struct buffer *buffer, const char *pattern) {
+ if (buffer != g_current_search.buffer) {
+ clear_search();
+ }
+
+ g_current_search.buffer = buffer;
+ g_current_search.active = true;
+
+ // if we are in a new buffer, add the update hook for it.
+ if (g_current_search.highlight_hook == (uint32_t)-1) {
+ g_current_search.highlight_hook =
+ buffer_add_update_hook(buffer, search_highlight_hook, NULL);
+ }
+
+ // replace the pattern if needed
+ if (g_current_search.pattern == NULL ||
+ !s8eq(s8(g_current_search.pattern), s8(pattern))) {
+ char *new_pattern = strdup(pattern);
+ free(g_current_search.pattern);
+ g_current_search.pattern = new_pattern;
+ }
+
+ // clear out any old search results
+ if (g_current_search.matches != NULL) {
+ free(g_current_search.matches);
+ g_current_search.matches = NULL;
+ g_current_search.nmatches = 0;
+ }
+}
+
uint64_t matchdist(struct region *match, struct location loc) {
struct location begin = match->begin;
@@ -98,64 +177,21 @@ uint64_t matchdist(struct region *match, struct location loc) {
// into the line it is, otherwise check the distance from location
int64_t coldist = begin.col;
if (linedist == 0) {
- int64_t coldist = (int64_t)begin.col - (int64_t)loc.col;
+ coldist = (int64_t)begin.col - (int64_t)loc.col;
}
// arbitrary row scaling, best effort to avoid counting line length
+ // this is not technically correct if you have lines longer than
+ // 1e6 but otoh, that seems excessive
return (linedist * linedist) * 1e6 + coldist * coldist;
}
-static void highlight_match(struct buffer *buffer, struct region match,
- bool current) {
- if (current) {
- buffer_add_text_property(
- buffer, match.begin, match.end,
- (struct text_property){.type = TextProperty_Colors,
- .colors = (struct text_property_colors){
- .set_bg = true,
- .bg = 3,
- .set_fg = true,
- .fg = 0,
- }});
-
- } else {
- buffer_add_text_property(
- buffer, match.begin, match.end,
- (struct text_property){.type = TextProperty_Colors,
- .colors = (struct text_property_colors){
- .set_bg = true,
- .bg = 6,
- .set_fg = true,
- .fg = 0,
- }});
- }
-}
-
-static void search_highlight_hook(struct buffer *buffer, void *userdata) {
- (void)userdata;
-
- for (uint32_t matchi = 0; matchi < g_current_search.nmatches; ++matchi) {
- highlight_match(buffer, g_current_search.matches[matchi],
- matchi == g_current_search.current_match);
- }
-}
-
-static void replace_highlight_hook(struct buffer *buffer, void *userdata) {
- (void)userdata;
-
- for (uint32_t matchi = 0; matchi < g_current_replace.nmatches; ++matchi) {
- struct match *m = &g_current_replace.matches[matchi];
- if (m->state != Todo) {
- continue;
- }
-
- highlight_match(buffer, m->region,
- matchi == g_current_replace.current_match);
- }
-}
-
static int32_t replace_next(struct command_ctx ctx, int argc,
const char *argv[]) {
+ (void)ctx;
+ (void)argc;
+ (void)argv;
+
struct replace *state = &g_current_replace;
struct buffer_view *buffer_view = window_buffer_view(state->window);
struct buffer *buffer = buffer_view->buffer;
@@ -207,6 +243,10 @@ static int32_t replace_next(struct command_ctx ctx, int argc,
}
static int32_t skip_next(struct command_ctx ctx, int argc, const char *argv[]) {
+ (void)ctx;
+ (void)argc;
+ (void)argv;
+
struct replace *state = &g_current_replace;
struct buffer_view *buffer_view = window_buffer_view(state->window);
@@ -230,8 +270,8 @@ static int32_t skip_next(struct command_ctx ctx, int argc, const char *argv[]) {
return 0;
}
-COMMAND_FN("replace-next", replace_next, replace_next, NULL);
-COMMAND_FN("skip-next", skip_next, skip_next, NULL);
+COMMAND_FN("replace-next", replace_next, replace_next, NULL)
+COMMAND_FN("skip-next", skip_next, skip_next, NULL)
static int cmp_matches(const void *m1, const void *m2) {
struct region *match1 = (struct region *)m1;
@@ -350,35 +390,9 @@ static struct region *find_closest(struct region *matches, uint32_t nmatches,
return closest;
}
-static void do_search(struct buffer_view *view, const char *pattern,
+static bool do_search(struct buffer_view *view, const char *pattern,
bool reverse) {
- if (view->buffer != g_current_search.buffer) {
- clear_search();
- }
-
- g_current_search.buffer = view->buffer;
- g_current_search.active = true;
-
- // if we are in a new buffer, add the update hook for it.
- if (g_current_search.highlight_hook == (uint32_t)-1) {
- g_current_search.highlight_hook =
- buffer_add_update_hook(view->buffer, search_highlight_hook, NULL);
- }
-
- // replace the pattern if needed
- if (g_current_search.pattern == NULL ||
- !s8eq(s8(g_current_search.pattern), s8(pattern))) {
- char *new_pattern = strdup(pattern);
- free(g_current_search.pattern);
- g_current_search.pattern = new_pattern;
- }
-
- // clear out any old search results first
- if (g_current_search.matches != NULL) {
- free(g_current_search.matches);
- g_current_search.matches = NULL;
- g_current_search.nmatches = 0;
- }
+ start_search(view->buffer, pattern);
buffer_find(view->buffer, g_current_search.pattern, &g_current_search.matches,
&g_current_search.nmatches);
@@ -391,10 +405,10 @@ static void do_search(struct buffer_view *view, const char *pattern,
view->dot, reverse, &closest_idx);
buffer_view_goto(view, closest->begin);
g_current_search.current_match = closest_idx;
- } else {
- abort_search();
- minibuffer_echo_timeout(4, "%s not found", pattern);
+ return true;
}
+
+ return false;
}
static const char *get_pattern() {
@@ -424,12 +438,18 @@ int32_t search_interactive(struct command_ctx ctx, int argc,
}
minibuffer_set_prompt(search_prompt(*(bool *)ctx.userdata));
+ buffer_view_goto_end(window_buffer_view(minibuffer_window()));
if (pattern != NULL) {
- do_search(window_buffer_view(minibuffer_target_window()), pattern,
- *(bool *)ctx.userdata);
+ if (!do_search(window_buffer_view(minibuffer_target_window()), pattern,
+ *(bool *)ctx.userdata)) {
+ abort_search();
+ minibuffer_echo_timeout(4, "%s not found", pattern);
+ }
+
free((char *)pattern);
}
+
return 0;
}
@@ -437,11 +457,13 @@ static bool search_dir_backward = true;
static bool search_dir_forward = false;
COMMAND_FN("search-forward", search_forward, search_interactive,
- &search_dir_forward);
+ &search_dir_forward)
COMMAND_FN("search-backward", search_backward, search_interactive,
- &search_dir_backward);
+ &search_dir_backward)
int32_t find(struct command_ctx ctx, int argc, const char *argv[]) {
+ (void)argv;
+
bool reverse = *(bool *)ctx.userdata;
if (argc == 0) {
struct binding bindings[] = {
@@ -454,17 +476,21 @@ int32_t find(struct command_ctx ctx, int argc, const char *argv[]) {
return minibuffer_prompt(ctx, search_prompt(reverse));
}
+ // allow enter to end the interactive search
if (g_current_search.active) {
abort_search();
return 0;
}
buffer_remove_keymap(g_current_search.keymap_id);
- do_search(window_buffer_view(ctx.active_window), argv[0], reverse);
+ bool found =
+ do_search(window_buffer_view(ctx.active_window), argv[0], reverse);
- if (g_current_search.active) {
- abort_search();
+ abort_search();
+ if (!found) {
+ minibuffer_echo_timeout(4, "%s not found", argv[0]);
}
+
return 0;
}
@@ -481,11 +507,10 @@ void register_search_replace_commands(struct commands *commands) {
}
void cleanup_search_replace(void) {
+ clear_replace();
clear_search();
if (g_current_search.pattern != NULL) {
free(g_current_search.pattern);
g_current_search.pattern = NULL;
}
-
- clear_replace();
}
diff --git a/src/main/search-replace.h b/src/main/search-replace.h
index 16869fc..ef5e0dc 100644
--- a/src/main/search-replace.h
+++ b/src/main/search-replace.h
@@ -3,7 +3,7 @@ struct commands;
/**
* Abort a replace currently in progress.
*/
-void abort_replace();
+void abort_replace(void);
/**
* Abort a search currently in progress.
diff --git a/test/allocator.c b/test/allocator.c
index fe01334..96ef6df 100644
--- a/test/allocator.c
+++ b/test/allocator.c
@@ -3,7 +3,7 @@
#include "assert.h"
#include "test.h"
-void test_frame_allocator() {
+void test_frame_allocator(void) {
struct frame_allocator fa = frame_allocator_create(128);
ASSERT(fa.capacity == 128,
@@ -28,4 +28,4 @@ void test_frame_allocator() {
frame_allocator_destroy(&fa);
}
-void run_allocator_tests() { run_test(test_frame_allocator); }
+void run_allocator_tests(void) { run_test(test_frame_allocator); }
diff --git a/test/buffer.c b/test/buffer.c
index 7d879b0..efc479e 100644
--- a/test/buffer.c
+++ b/test/buffer.c
@@ -52,8 +52,8 @@ static void delete_callback(struct buffer *buffer, struct edit_location removed,
static void test_delete(void) {
struct buffer b = buffer_create("test-buffer-delete");
const char *txt = "we are adding some text\ntwo lines to be exact";
- struct location loc = buffer_add(&b, (struct location){.line = 0, .col = 0},
- (uint8_t *)txt, strlen(txt));
+ buffer_add(&b, (struct location){.line = 0, .col = 0}, (uint8_t *)txt,
+ strlen(txt));
ASSERT(buffer_line_length(&b, 0) == 23,
"Expected line 1 to be 23 chars before deletion");
@@ -132,7 +132,8 @@ static void test_char_movement(void) {
"Expected a double width char to result in a 2 column move");
next = buffer_next_char(&b, (struct location){.line = 0, .col = 16});
- uint64_t tab_width = settings_get("editor.tab-width")->value.number_value;
+ uint64_t tab_width =
+ settings_get("editor.tab-width")->value.data.number_value;
ASSERT(next.col == 16 + tab_width,
"Expected a tab to result in a move the width of a tab");
@@ -225,7 +226,7 @@ void run_buffer_tests(void) {
settings_init(10);
settings_set_default(
"editor.tab-width",
- (struct setting_value){.type = Setting_Number, .number_value = 4});
+ (struct setting_value){.type = Setting_Number, .data.number_value = 4});
run_test(test_add);
run_test(test_delete);
diff --git a/test/command.c b/test/command.c
index 8db02e0..9920895 100644
--- a/test/command.c
+++ b/test/command.c
@@ -5,7 +5,7 @@
#include "dged/hash.h"
#include "dged/hashmap.h"
-void test_command_registry_create() {
+void test_command_registry_create(void) {
struct commands cmds = command_registry_create(10);
ASSERT(HASHMAP_CAPACITY(&cmds.commands) == 10,
@@ -17,6 +17,10 @@ void test_command_registry_create() {
}
int32_t fake_command(struct command_ctx ctx, int argc, const char *argv[]) {
+ (void)ctx;
+ (void)argc;
+ (void)argv;
+
return 0;
}
@@ -33,7 +37,7 @@ struct commands single_fake_command(const char *name) {
return cmds;
}
-void test_register_command() {
+void test_register_command(void) {
struct commands cmds = command_registry_create(1);
struct command cmd = {
@@ -60,7 +64,7 @@ void test_register_command() {
command_registry_destroy(&cmds);
}
-void test_lookup_command() {
+void test_lookup_command(void) {
struct commands cmds = single_fake_command("fake");
struct command *cmd = lookup_command(&cmds, "fake");
@@ -69,7 +73,6 @@ void test_lookup_command() {
ASSERT_STR_EQ(cmd->name, "fake",
"Expected the found function to have the correct name");
- struct command *also_cmd = lookup_command_by_hash(&cmds, hash_name("fake"));
ASSERT(cmd != NULL,
"Expected to be able to look up inserted command by hash");
ASSERT_STR_EQ(cmd->name, "fake",
@@ -79,10 +82,14 @@ void test_lookup_command() {
}
int32_t failing_command(struct command_ctx ctx, int argc, const char *argv[]) {
+ (void)ctx;
+ (void)argc;
+ (void)argv;
+
return 100;
}
-void test_execute_command() {
+void test_execute_command(void) {
struct commands cmds = single_fake_command("fake");
struct command *cmd = lookup_command(&cmds, "fake");
@@ -101,7 +108,7 @@ void test_execute_command() {
command_registry_destroy(&cmds);
}
-void run_command_tests() {
+void run_command_tests(void) {
run_test(test_command_registry_create);
run_test(test_register_command);
run_test(test_lookup_command);
diff --git a/test/container.c b/test/container.c
index 8be7fc9..bfdf052 100644
--- a/test/container.c
+++ b/test/container.c
@@ -5,7 +5,7 @@
#include "assert.h"
#include "test.h"
-void test_empty_bintree() {
+void test_empty_bintree(void) {
BINTREE_ENTRY_TYPE(node, int);
BINTREE(node) tree;
@@ -29,7 +29,7 @@ void test_empty_bintree() {
BINTREE_FREE_NODES(BINTREE_ROOT(&tree), node);
}
-void test_bintree_iter() {
+void test_bintree_iter(void) {
BINTREE_ENTRY_TYPE(node, char);
BINTREE(node) tree;
BINTREE_INIT(&tree);
@@ -96,7 +96,7 @@ void test_bintree_iter() {
BINTREE_FREE_NODES(root, node);
}
-void run_container_tests() {
+void run_container_tests(void) {
run_test(test_empty_bintree);
run_test(test_bintree_iter);
}
diff --git a/test/fake-reactor.c b/test/fake-reactor.c
index aafe8a3..d3497fb 100644
--- a/test/fake-reactor.c
+++ b/test/fake-reactor.c
@@ -5,13 +5,13 @@ struct reactor {
struct fake_reactor_impl *impl;
};
-struct reactor *reactor_create() {
+struct reactor *reactor_create(void) {
return (struct reactor *)calloc(1, sizeof(struct reactor));
}
void reactor_destroy(struct reactor *reactor) { free(reactor); }
-void reactor_update(struct reactor *reactor) {}
+void reactor_update(struct reactor *reactor) { (void)reactor; }
bool reactor_poll_event(struct reactor *reactor, uint32_t ev_id) {
if (reactor->impl != NULL) {
return reactor->impl->poll_event(reactor->impl->userdata, ev_id);
@@ -32,7 +32,7 @@ uint32_t reactor_register_interest(struct reactor *reactor, int fd,
void reactor_unregister_interest(struct reactor *reactor, uint32_t ev_id) {
if (reactor->impl != NULL) {
- return reactor->impl->unregister_interest(reactor->impl->userdata, ev_id);
+ reactor->impl->unregister_interest(reactor->impl->userdata, ev_id);
}
}
diff --git a/test/json.c b/test/json.c
new file mode 100644
index 0000000..c67fc75
--- /dev/null
+++ b/test/json.c
@@ -0,0 +1,95 @@
+#include "assert.h"
+#include "test.h"
+
+#include "dged/json.h"
+
+#include <string.h>
+
+void test_empty_parse(void) {
+ struct json_result res = json_parse((uint8_t *)"", 0);
+
+ ASSERT(res.ok, "Expected empty parse to work");
+ json_destroy(&res.result.document);
+}
+
+void test_empty_array(void) {
+ struct json_result res = json_parse((uint8_t *)"[]", 2);
+
+ ASSERT(res.ok, "Expected parse of empty array to work");
+ struct json_value root = res.result.document;
+ ASSERT(root.type == Json_Array, "Expected doc root to be array");
+ ASSERT(json_array_len(root.value.array) == 0, "Expected array to be empty");
+
+ json_destroy(&root);
+}
+
+void test_array(void) {
+
+ struct json_result res = json_parse((uint8_t *)"[ 1, 2, 4 ]", 11);
+ ASSERT(res.ok, "Expected parse of number array to work");
+ struct json_value root = res.result.document;
+ ASSERT(root.type == Json_Array, "Expected doc root to be array");
+ ASSERT(json_array_len(root.value.array) == 3, "Expected array len to be 3");
+
+ json_destroy(&root);
+
+ const char *jsn = "[ \"hello\", \"world\" ]";
+ res = json_parse((uint8_t *)jsn, strlen(jsn));
+ ASSERT(res.ok, "Expected parse of string array to work");
+ root = res.result.document;
+ ASSERT(root.type == Json_Array, "Expected doc root to be array");
+ struct json_value *second = json_array_get(root.value.array, 1);
+ ASSERT(second->type == Json_String, "Expected second element to be a string");
+ ASSERT(s8cmp(second->value.string, s8("world")) == 0,
+ "Expected second string to be \"world\"");
+
+ json_destroy(&root);
+}
+
+void test_object(void) {
+ struct json_result res = json_parse((uint8_t *)"{ }", 3);
+ ASSERT(res.ok, "Expected parse of empty object to work");
+ struct json_value root = res.result.document;
+ ASSERT(root.type == Json_Object, "Expected doc root to be object");
+ ASSERT(json_len(root.value.object) == 0, "Expected empty object len to be 0");
+
+ json_destroy(&root);
+
+ const char *jsn = "{ \"name\": \"Kalle Kula\", \"age\": 33, }";
+ res = json_parse((uint8_t *)jsn, strlen(jsn));
+ ASSERT(res.ok, "Expected parse of simple object to work");
+ root = res.result.document;
+ ASSERT(root.type == Json_Object, "Expected doc root to be object");
+ ASSERT(json_contains(root.value.object, s8("name")),
+ "Expected object to contain \"name\"");
+
+ struct json_value *age = json_get(root.value.object, s8("age"));
+ ASSERT(age->type == Json_Number, "Expected age to (just?) be a number");
+ ASSERT(age->value.number == 33, "Expected age to be 33");
+
+ json_destroy(&root);
+
+ jsn = "{ \"name\": \"Kalle Kula\", \"age\": 33, \"kids\": "
+ "[ "
+ "{ \"name\": \"Sune Kula\", \"age\": 10, }, "
+ "{ \"name\": \"Suna Kula\", \"age\": 7 } "
+ "] }";
+ res = json_parse((uint8_t *)jsn, strlen(jsn));
+ ASSERT(res.ok, "Expected parse of nested object to work");
+ root = res.result.document;
+ ASSERT(root.type == Json_Object, "Expected doc root to be object");
+
+ ASSERT(json_contains(root.value.object, s8("kids")),
+ "Expected json object to contain array of kids");
+ struct json_value *kids = json_get(root.value.object, s8("kids"));
+ ASSERT(kids->type == Json_Array, "Expected kids to be array");
+
+ json_destroy(&root);
+}
+
+void run_json_tests(void) {
+ run_test(test_empty_parse);
+ run_test(test_empty_array);
+ run_test(test_array);
+ run_test(test_object);
+}
diff --git a/test/keyboard.c b/test/keyboard.c
index 64419ec..fdedf20 100644
--- a/test/keyboard.c
+++ b/test/keyboard.c
@@ -16,6 +16,8 @@ struct call_count {
};
bool fake_poll(void *userdata, uint32_t ev_id) {
+ (void)ev_id;
+
if (userdata != NULL) {
struct call_count *cc = (struct call_count *)userdata;
++cc->poll;
@@ -24,6 +26,9 @@ bool fake_poll(void *userdata, uint32_t ev_id) {
}
uint32_t fake_register_interest(void *userdata, int fd,
enum interest interest) {
+ (void)fd;
+ (void)interest;
+
if (userdata != NULL) {
struct call_count *cc = (struct call_count *)userdata;
++cc->reg;
@@ -32,6 +37,8 @@ uint32_t fake_register_interest(void *userdata, int fd,
}
void fake_unregister_interest(void *userdata, uint32_t ev_id) {
+ (void)ev_id;
+
if (userdata != NULL) {
struct call_count *cc = (struct call_count *)userdata;
++cc->unreg;
@@ -75,7 +82,7 @@ void fake_keyboard_destroy(struct fake_keyboard *kbd) {
reactor_destroy(kbd->reactor);
}
-void simple_key() {
+void simple_key(void) {
struct call_count cc = {0};
struct fake_reactor_impl fake = {
.poll_event = fake_poll,
@@ -99,7 +106,7 @@ void simple_key() {
free(upd.raw);
}
-void ctrl_key() {
+void ctrl_key(void) {
struct fake_reactor_impl fake = {
.poll_event = fake_poll,
.register_interest = fake_register_interest,
@@ -122,7 +129,7 @@ void ctrl_key() {
free(upd.raw);
}
-void meta_key() {
+void meta_key(void) {
struct fake_reactor_impl fake = {
.poll_event = fake_poll,
.register_interest = fake_register_interest,
@@ -147,7 +154,7 @@ void meta_key() {
free(upd.raw);
}
-void spec_key() {
+void spec_key(void) {
struct fake_reactor_impl fake = {
.poll_event = fake_poll,
.register_interest = fake_register_interest,
@@ -170,7 +177,7 @@ void spec_key() {
free(upd.raw);
}
-void test_utf8() {
+void test_utf8(void) {
struct fake_reactor_impl fake = {
.poll_event = fake_poll,
.register_interest = fake_register_interest,
@@ -192,7 +199,7 @@ void test_utf8() {
free(upd.raw);
}
-void test_key_equal() {
+void test_key_equal(void) {
struct key k1 = {.mod = Ctrl, .key = 'A'};
ASSERT(key_equal(&k1, &k1), "Expected key to be equal to itself");
ASSERT(key_equal_char(&k1, Ctrl, 'A'), "Expected key to be c-a");
@@ -203,7 +210,7 @@ void test_key_equal() {
"Expected yet another different key to not be the same");
}
-void run_keyboard_tests() {
+void run_keyboard_tests(void) {
run_test(simple_key);
run_test(ctrl_key);
run_test(meta_key);
diff --git a/test/main.c b/test/main.c
index dc0c2dc..29e031f 100644
--- a/test/main.c
+++ b/test/main.c
@@ -6,9 +6,12 @@
#include "test.h"
-void handle_abort() { exit(1); }
+void handle_abort(int sig) {
+ (void)sig;
+ exit(1);
+}
-int main() {
+int main(void) {
// Use a hardcoded locale to get a
// predictable env.
setlocale(LC_ALL, "en_US.UTF-8");
@@ -47,6 +50,11 @@ int main() {
printf("\n 🎁 \x1b[1;36mRunning container tests...\x1b[0m\n");
run_container_tests();
+#if defined(LSP_ENABLED)
+ printf("\n 📃 \x1b[1;36mRunning JSON tests...\x1b[0m\n");
+ run_json_tests();
+#endif
+
struct timespec elapsed;
clock_gettime(CLOCK_MONOTONIC, &elapsed);
uint64_t elapsed_nanos =
diff --git a/test/minibuffer.c b/test/minibuffer.c
index 28ee277..b4f8c05 100644
--- a/test/minibuffer.c
+++ b/test/minibuffer.c
@@ -19,7 +19,7 @@ static struct frame_allocator *g_alloc = NULL;
void *alloc_fn(size_t sz) { return frame_allocator_alloc(g_alloc, sz); }
-void init() {
+void init(void) {
if (b.name == NULL) {
settings_init(10);
timers_init();
@@ -31,7 +31,7 @@ void init() {
windows_init(100, 100, &b, &b, &bufs);
}
-void destroy() {
+void destroy(void) {
if (b.name != NULL) {
buffer_destroy(&b);
buffers_destroy(&bufs);
@@ -41,7 +41,7 @@ void destroy() {
}
}
-void test_minibuffer_echo() {
+void test_minibuffer_echo(void) {
struct buffer_view view = buffer_view_create(&b, false, false);
// TODO: how to clear this?
@@ -84,9 +84,15 @@ void test_minibuffer_echo() {
destroy();
}
-int32_t fake(struct command_ctx ctx, int argc, const char *argv[]) { return 0; }
+int32_t fake(struct command_ctx ctx, int argc, const char *argv[]) {
+ (void)ctx;
+ (void)argc;
+ (void)argv;
-void test_minibuffer_prompt() {
+ return 0;
+}
+
+void test_minibuffer_prompt(void) {
init();
ASSERT(!minibuffer_focused(),
"Minibuffer should not be focused without reason");
@@ -111,7 +117,7 @@ void test_minibuffer_prompt() {
destroy();
}
-void run_minibuffer_tests() {
+void run_minibuffer_tests(void) {
run_test(test_minibuffer_echo);
run_test(test_minibuffer_prompt);
}
diff --git a/test/settings.c b/test/settings.c
index 13d0963..1d1f192 100644
--- a/test/settings.c
+++ b/test/settings.c
@@ -5,22 +5,22 @@
#include "assert.h"
#include "test.h"
-void test_get() {
+void test_get(void) {
settings_init(10);
settings_set_default(
"my.setting",
- (struct setting_value){.type = Setting_Bool, .bool_value = false});
+ (struct setting_value){.type = Setting_Bool, .data.bool_value = false});
struct setting *s = settings_get("my.setting");
ASSERT(s != NULL, "Expected setting to exist after being inserted");
ASSERT(s->value.type == Setting_Bool,
"Expected inserted setting to have the same type when retrieved");
- ASSERT(!s->value.bool_value,
+ ASSERT(!s->value.data.bool_value,
"Expected inserted setting to have the same value when retrieved");
settings_set_default(
"other.setting",
- (struct setting_value){.type = Setting_Number, .number_value = 28});
+ (struct setting_value){.type = Setting_Number, .data.number_value = 28});
struct setting **res = NULL;
uint32_t nres = 0;
@@ -29,46 +29,48 @@ void test_get() {
ASSERT(nres == 1, "Expected to get one result back");
ASSERT(s->value.type == Setting_Bool, "Expected inserted setting to have the "
"same type when retrieved by prefix");
- ASSERT(!s->value.bool_value, "Expected inserted setting to have the same "
- "value when retrieved by prefix");
+ ASSERT(!s->value.data.bool_value,
+ "Expected inserted setting to have the same "
+ "value when retrieved by prefix");
free(res);
settings_destroy();
}
-void test_set() {
+void test_set(void) {
settings_init(10);
settings_set_default(
"my.setting",
- (struct setting_value){.type = Setting_Bool, .bool_value = false});
+ (struct setting_value){.type = Setting_Bool, .data.bool_value = false});
// test that wrong type is ignored;
- settings_set("my.setting", (struct setting_value){.type = Setting_String,
- .string_value = "bonan"});
+ settings_set("my.setting",
+ (struct setting_value){.type = Setting_String,
+ .data.string_value = "bonan"});
struct setting *s = settings_get("my.setting");
ASSERT(s != NULL, "Expected setting to exist after being inserted");
ASSERT(s->value.type == Setting_Bool,
"Expected inserted setting type to not have been changed");
- ASSERT(!s->value.bool_value,
+ ASSERT(!s->value.data.bool_value,
"Expected inserted setting value to not have been changed");
// test that correct type is indeed changed
settings_set("my.setting", (struct setting_value){.type = Setting_Bool,
- .bool_value = true});
+ .data.bool_value = true});
s = settings_get("my.setting");
ASSERT(s != NULL, "Expected setting to exist");
ASSERT(s->value.type == Setting_Bool,
"Expected inserted setting type to not have been changed");
- ASSERT(s->value.bool_value,
+ ASSERT(s->value.data.bool_value,
"Expected inserted setting value to _have_ been changed");
settings_destroy();
}
-void test_from_toml_string() {
+void test_from_toml_string(void) {
char *content = "[ languages.c]\n"
"name = \"C\"";
@@ -81,13 +83,13 @@ void test_from_toml_string() {
ASSERT(setting != NULL,
"Expected to be able to retrieve setting after parsed from string");
ASSERT(setting->value.type == Setting_String, "Expected a string setting");
- ASSERT_STR_EQ(setting->value.string_value, "C",
+ ASSERT_STR_EQ(setting->value.data.string_value, "C",
"Expected setting value to be \"C\"");
content = "sune = \"wrong";
res = settings_from_string(content, &errmsgs);
ASSERT(res >= 1, "Expected (at least) one error from invalid toml");
- for (uint32_t i = 0; i < res; ++i) {
+ for (uint32_t i = 0; i < (uint32_t)res; ++i) {
free(errmsgs[i]);
}
free(errmsgs);
@@ -95,7 +97,7 @@ void test_from_toml_string() {
content = "boll = truj";
res = settings_from_string(content, &errmsgs);
ASSERT(res >= 1, "Expected (at least) one error from an invalid bool");
- for (uint32_t i = 0; i < res; ++i) {
+ for (uint32_t i = 0; i < (uint32_t)res; ++i) {
free(errmsgs[i]);
}
free(errmsgs);
@@ -109,12 +111,12 @@ void test_from_toml_string() {
setting = settings_get("editor.show-whitespace");
ASSERT(setting != NULL,
"Expected editor.show-whitespace to be set from TOML");
- ASSERT(setting->value.bool_value,
+ ASSERT(setting->value.data.bool_value,
"Expected editor.show-whitespace to be set to true from TOML");
setting = settings_get("editor.tab-width");
ASSERT(setting != NULL, "Expected editor.tab-width to be set from TOML");
- ASSERT(setting->value.number_value == 3,
+ ASSERT(setting->value.data.number_value == 3,
"Expected editor.tab-width to be set to 3 from TOML");
content = "[languages]\n"
@@ -126,7 +128,7 @@ void test_from_toml_string() {
setting = settings_get("languages.pang.name");
ASSERT(setting != NULL,
"Expected languages.pang.name to be set through inline table");
- ASSERT_STR_EQ(setting->value.string_value, "Bom",
+ ASSERT_STR_EQ(setting->value.data.string_value, "Bom",
"Expected languages.pang.name to be \"Bom\"");
content = "multi = \"\"\"This is\n"
@@ -135,13 +137,13 @@ void test_from_toml_string() {
ASSERT(res == 0, "Expected valid TOML to parse successfully");
setting = settings_get("multi");
ASSERT(setting != NULL, "Expected multi to be set");
- ASSERT_STR_EQ(setting->value.string_value, "This is\na multiline string",
+ ASSERT_STR_EQ(setting->value.data.string_value, "This is\na multiline string",
"Expected newline to have been preserved in multiline string");
settings_destroy();
}
-void run_settings_tests() {
+void run_settings_tests(void) {
run_test(test_get);
run_test(test_set);
run_test(test_from_toml_string);
diff --git a/test/test.h b/test/test.h
index b01fde4..5b9cafc 100644
--- a/test/test.h
+++ b/test/test.h
@@ -9,15 +9,16 @@
fn(); \
printf("\033[32mok!\033[0m\n");
-void run_buffer_tests();
-void run_utf8_tests();
-void run_text_tests();
-void run_undo_tests();
-void run_command_tests();
-void run_keyboard_tests();
-void run_allocator_tests();
-void run_minibuffer_tests();
-void run_settings_tests();
-void run_container_tests();
+void run_buffer_tests(void);
+void run_utf8_tests(void);
+void run_text_tests(void);
+void run_undo_tests(void);
+void run_command_tests(void);
+void run_keyboard_tests(void);
+void run_allocator_tests(void);
+void run_minibuffer_tests(void);
+void run_settings_tests(void);
+void run_container_tests(void);
+void run_json_tests(void);
#endif
diff --git a/test/text.c b/test/text.c
index f890e7b..0d1557f 100644
--- a/test/text.c
+++ b/test/text.c
@@ -8,14 +8,14 @@
#include "stdio.h"
#include "test.h"
-void assert_line_eq(struct text_chunk line, const char *txt, const char *msg) {
+static void assert_line_eq(struct text_chunk line, const char *txt,
+ const char *msg) {
ASSERT(strncmp((const char *)line.text, txt, line.nbytes) == 0, msg);
}
-void assert_line_equal(struct text_chunk *line) {}
-
-void test_add_text() {
+void test_add_text(void) {
uint32_t lines_added;
+
/* use a silly small initial capacity to test re-alloc */
struct text *t = text_create(1);
@@ -65,7 +65,7 @@ void test_add_text() {
text_destroy(t);
}
-void test_delete_text() {
+void test_delete_text(void) {
uint32_t lines_added;
struct text *t = text_create(10);
const char *txt = "This is line 1";
@@ -128,7 +128,7 @@ void test_delete_text() {
text_destroy(t4);
}
-void run_text_tests() {
+void run_text_tests(void) {
run_test(test_add_text);
run_test(test_delete_text);
}
diff --git a/test/undo.c b/test/undo.c
index a4b6ad0..05ec2e4 100644
--- a/test/undo.c
+++ b/test/undo.c
@@ -5,7 +5,7 @@
#include "assert.h"
#include "test.h"
-void test_undo_insert() {
+void test_undo_insert(void) {
struct undo_stack undo;
/* small capacity on purpose to force re-sizing */
@@ -15,7 +15,7 @@ void test_undo_insert() {
ASSERT(undo_size(&undo) == 1,
"Expected undo stack to have one item after inserting a save point");
- undo_push_boundary(&undo, (struct undo_boundary){});
+ undo_push_boundary(&undo, (struct undo_boundary){0});
ASSERT(undo_size(&undo) == 2,
"Expected undo stack to have two items after inserting a boundary");
@@ -37,7 +37,7 @@ void test_undo_insert() {
undo_destroy(&undo);
}
-void test_undo() {
+void test_undo(void) {
struct undo_stack undo;
undo_init(&undo, 10);
@@ -99,7 +99,7 @@ void test_undo() {
undo_destroy(&undo);
}
-void run_undo_tests() {
+void run_undo_tests(void) {
run_test(test_undo_insert);
run_test(test_undo);
}
diff --git a/test/utf8.c b/test/utf8.c
index c5094c7..19a18d5 100644
--- a/test/utf8.c
+++ b/test/utf8.c
@@ -6,6 +6,5 @@
#include "assert.h"
#include "test.h"
-void test_nchars_nbytes() {}
-
-void run_utf8_tests() { run_test(test_nchars_nbytes); }
+void test_nchars_nbytes(void) {}
+void run_utf8_tests(void) { run_test(test_nchars_nbytes); }