summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile5
-rw-r--r--config.c375
-rw-r--r--main.c14
-rw-r--r--ted.h3
4 files changed, 219 insertions, 178 deletions
diff --git a/Makefile b/Makefile
index 5abdf63..63af6c6 100644
--- a/Makefile
+++ b/Makefile
@@ -11,9 +11,10 @@ LOCAL_DATA_DIR=/home/`logname`/.local/share/ted
INSTALL_BIN_DIR=/usr/bin
debug-build: ted compile_commands.json
ted: debug/ted
- cp debug/ted .
+ @# note: doing cp would fail if `ted` is busy
+ cat debug/ted > ted
compile_commands.json: debug/ted
- cp debug/compile_commands.json .
+ cat debug/compile_commands.json > compile_commands.json
debug/ted: *.[ch] libpcre2-32.a CMakeLists.txt
mkdir -p debug
cd debug && cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DCMAKE_BUILD_TYPE=Debug -GNinja ..
diff --git a/config.c b/config.c
index a1b73a2..78b3e8e 100644
--- a/config.c
+++ b/config.c
@@ -119,8 +119,6 @@ static SettingFloat const settings_float[] = {
static SettingString const settings_string[] = {
{"build-default-command", settings_zero.build_default_command, sizeof settings_zero.build_default_command, true},
{"build-command", settings_zero.build_command, sizeof settings_zero.build_command, true},
- {"bg-shader", settings_zero.bg_shader_text, sizeof settings_zero.bg_shader_text, true},
- {"bg-texture", settings_zero.bg_shader_image, sizeof settings_zero.bg_shader_image, true},
{"root-identifiers", settings_zero.root_identifiers, sizeof settings_zero.root_identifiers, true},
{"lsp", settings_zero.lsp, sizeof settings_zero.lsp, true},
{"lsp-configuration", settings_zero.lsp_configuration, sizeof settings_zero.lsp_configuration, true},
@@ -497,9 +495,8 @@ void config_read(Ted *ted, ConfigPart **parts, const char *filename) {
// IMPORTANT REQUIREMENT FOR THIS FUNCTION:
// - less specific contexts compare as less
// (i.e. if context_is_parent(a.context, b.context), then we return -1, and vice versa.)
-// if total = true, this gives a total ordering
-// if total = false, parts with identical contexts will compare equal.
-static int config_part_cmp(const ConfigPart *ap, const ConfigPart *bp, bool total) {
+// - this gives a total ordering; ties are broken by order of appearance
+static int config_part_cmp(const ConfigPart *ap, const ConfigPart *bp) {
const SettingsContext *a = &ap->context, *b = &bp->context;
if (a->language == 0 && b->language != 0)
return -1;
@@ -520,18 +517,16 @@ static int config_part_cmp(const ConfigPart *ap, const ConfigPart *bp, bool tota
return +1;
int cmp = strcmp(a_path, b_path);
if (cmp != 0) return cmp;
- if (total) {
- if (ap->index < bp->index)
- return -1;
- if (ap->index > bp->index)
- return +1;
- }
+ if (ap->index < bp->index)
+ return -1;
+ if (ap->index > bp->index)
+ return +1;
return 0;
}
static int config_part_qsort_cmp(const void *av, const void *bv) {
- return config_part_cmp(av, bv, true);
+ return config_part_cmp(av, bv);
}
static i64 config_read_string(Ted *ted, ConfigReader *cfg, char **ptext) {
@@ -596,7 +591,7 @@ static i64 config_read_string(Ted *ted, ConfigReader *cfg, char **ptext) {
return str_idx;
}
-static void settings_load_bg_shader(Ted *ted, Settings *s) {
+static void settings_load_bg_shader(Ted *ted, Settings **applicable_settings, const char *bg_shader_text) {
char vshader[8192] ;
strbuf_printf(vshader, "attribute vec2 v_pos;\n\
OUT vec2 t_pos;\n\
@@ -611,46 +606,49 @@ uniform float t_save_time;\n\
uniform vec2 t_aspect;\n\
uniform sampler2D t_texture;\n\
#line 1\n\
-%s", s->bg_shader_text);
+%s", bg_shader_text);
- gl_rc_sab_decref(&s->bg_shader);
char error[512] = {0};
GLuint shader = gl_compile_and_link_shaders(error, vshader, fshader);
if (*error)
ted_error(ted, "%s", error);
- if (shader) {
- GLuint buffer = 0, array = 0;
- glGenBuffers(1, &buffer);
- if (gl_version_major >= 3) {
- glGenVertexArrays(1, &array);
- glBindVertexArray(array);
- }
-
-
- float buffer_data[][2] = {
- {0,0},
- {1,0},
- {1,1},
- {0,0},
- {1,1},
- {0,1}
- };
+ if (!shader) return;
+
+ GLuint buffer = 0, array = 0;
+ glGenBuffers(1, &buffer);
+ if (gl_version_major >= 3) {
+ glGenVertexArrays(1, &array);
+ glBindVertexArray(array);
+ }
+
+
+ float buffer_data[][2] = {
+ {0,0},
+ {1,0},
+ {1,1},
+ {0,0},
+ {1,1},
+ {0,1}
+ };
- GLuint v_pos = (GLuint)glGetAttribLocation(shader, "v_pos");
- glBindBuffer(GL_ARRAY_BUFFER, buffer);
- glBufferData(GL_ARRAY_BUFFER, sizeof buffer_data, buffer_data, GL_STATIC_DRAW);
- glVertexAttribPointer(v_pos, 2, GL_FLOAT, 0, 2 * sizeof(float), 0);
- glEnableVertexAttribArray(v_pos);
-
- s->bg_shader = gl_rc_sab_new(shader, array, buffer);
+ GLuint v_pos = (GLuint)glGetAttribLocation(shader, "v_pos");
+ glBindBuffer(GL_ARRAY_BUFFER, buffer);
+ glBufferData(GL_ARRAY_BUFFER, sizeof buffer_data, buffer_data, GL_STATIC_DRAW);
+ glVertexAttribPointer(v_pos, 2, GL_FLOAT, 0, 2 * sizeof(float), 0);
+ glEnableVertexAttribArray(v_pos);
+
+ GlRcSAB *bg_shader = gl_rc_sab_new(shader, array, buffer);
+ bg_shader->ref_count = arr_len(applicable_settings);
+ arr_foreach_ptr(applicable_settings, Settings *, psettings) {
+ Settings *settings = *psettings;
+ // decrease refcount on previous shader
+ gl_rc_sab_decref(&settings->bg_shader);
+ settings->bg_shader = bg_shader;
}
}
-static void settings_load_bg_texture(Ted *ted, Settings *s) {
- gl_rc_texture_decref(&s->bg_texture);
-
- const char *path = s->bg_shader_image;
+static void settings_load_bg_texture(Ted *ted, Settings **applicable_settings, const char *path) {
char expanded[TED_PATH_MAX];
expanded[0] = '\0';
if (path[0] == '~') {
@@ -660,16 +658,26 @@ static void settings_load_bg_texture(Ted *ted, Settings *s) {
strbuf_cat(expanded, path);
GLuint texture = gl_load_texture_from_image(expanded);
- if (texture) {
- s->bg_texture = gl_rc_texture_new(texture);
- } else {
+ if (!texture) {
ted_error(ted, "Couldn't load image %s", path);
+ return;
+ }
+
+ GlRcTexture *bg_texture = gl_rc_texture_new(texture);
+ bg_texture->ref_count = arr_len(applicable_settings);
+ arr_foreach_ptr(applicable_settings, Settings *, psettings) {
+ Settings *settings = *psettings;
+ // decrease refcount on previous texture
+ gl_rc_texture_decref(&settings->bg_texture);
+ settings->bg_texture = bg_texture;
}
+
}
// reads a single "line" of the config file, but it may include a multiline string,
// so it may read multiple lines.
-static void config_parse_line(ConfigReader *cfg, Settings *settings, const ConfigPart *part, char **pline) {
+// applicable_settings is a dynamic array of all settings objects to update
+static void config_parse_line(ConfigReader *cfg, Settings **applicable_settings, const ConfigPart *part, char **pline) {
char *line = *pline;
Ted *ted = cfg->ted;
@@ -730,7 +738,9 @@ static void config_parse_line(ConfigReader *cfg, Settings *settings, const Confi
if (setting != COLOR_UNKNOWN) {
u32 color = 0;
if (color_from_str(value, &color)) {
- settings->colors[setting] = color;
+ arr_foreach_ptr(applicable_settings, Settings *, psettings) {
+ (*psettings)->colors[setting] = color;
+ }
} else {
config_err(cfg, "'%s' is not a valid color. Colors should look like #rgb, #rgba, #rrggbb, or #rrggbbaa.", value);
}
@@ -745,18 +755,8 @@ static void config_parse_line(ConfigReader *cfg, Settings *settings, const Confi
case SECTION_KEYBOARD: {
// lines like Ctrl+Down = 10 :down
u32 key_combo = config_parse_key_combo(cfg, key);
- KeyAction *action = NULL;
- // check if we already have an action for this key combo
- arr_foreach_ptr(settings->key_actions, KeyAction, act) {
- if (act->key_combo == key_combo) {
- action = act;
- break;
- }
- }
- // if this is a new key combo, add an element to the key_actions array
- if (!action)
- action = arr_addp(settings->key_actions);
- action->key_combo = key_combo;
+ KeyAction action = {0};
+ action.key_combo = key_combo;
llong argument = 1; // default argument = 1
if (isdigit(*value)) {
// read the argument
@@ -785,34 +785,50 @@ static void config_parse_line(ConfigReader *cfg, Settings *settings, const Confi
// read the command
Command command = command_from_str(value + 1);
if (command != CMD_UNKNOWN) {
- action->command = command;
- action->argument = argument;
+ action.command = command;
+ action.argument = argument;
} else {
config_err(cfg, "Unrecognized command %s", value);
}
} else {
config_err(cfg, "Expected ':' for key action. This line should look something like: %s = :command.", key);
}
+
+ arr_foreach_ptr(applicable_settings, Settings *, psettings) {
+ Settings *settings = *psettings;
+ bool have = false;
+ // check if we already have an action for this key combo
+ arr_foreach_ptr(settings->key_actions, KeyAction, act) {
+ if (act->key_combo == key_combo) {
+ *act = action;
+ have = true;
+ break;
+ }
+ }
+ // if this is a new key combo, add an element to the key_actions array
+ if (!have)
+ arr_add(settings->key_actions, action);
+ }
} break;
case SECTION_EXTENSIONS: {
Language lang = language_from_str(key);
if (lang == LANG_NONE) {
config_err(cfg, "Invalid programming language: %s.", key);
} else {
- char *new_str = malloc(strlen(value) + 1);
- if (!new_str) {
- config_err(cfg, "Out of memory.");
- } else {
- char *dst = new_str;
- // get rid of whitespace in extension list
- for (const char *src = value; *src; ++src)
- if (!isspace(*src))
- *dst++ = *src;
- *dst = 0;
+ char *exts = calloc(1, strlen(value) + 1);
+ char *dst = exts;
+ // get rid of whitespace in extension list
+ for (const char *src = value; *src; ++src)
+ if (!isspace(*src))
+ *dst++ = *src;
+ *dst = 0;
+ arr_foreach_ptr(applicable_settings, Settings *, psettings) {
+ Settings *settings = *psettings;
if (settings->language_extensions[lang])
free(settings->language_extensions[lang]);
- settings->language_extensions[lang] = new_str;
+ settings->language_extensions[lang] = str_dup(exts);
}
+ free(exts);
}
} break;
case SECTION_CORE: {
@@ -852,78 +868,83 @@ static void config_parse_line(ConfigReader *cfg, Settings *settings, const Confi
}
}
- // go through all settings
- bool recognized = false;
- for (size_t i = 0; i < arr_count(settings_all) && !recognized; ++i) {
- SettingAny const *any = &settings_all[i];
- if (any->type == 0) break;
- if (streq(key, any->name)) {
- recognized = true;
-
- if (part->context.language != 0 && !any->per_language) {
- config_err(cfg, "Setting %s cannot be controlled for individual languages.", key);
- break;
- }
-
- switch (any->type) {
- case SETTING_BOOL: {
- const SettingBool *setting = &any->u._bool;
- if (is_bool)
- setting_bool_set(settings, setting, boolean);
- else
- config_err(cfg, "Invalid %s: %s. This should be yes, no, on, or off.", setting->name, value);
- } break;
- case SETTING_U8: {
- const SettingU8 *setting = &any->u._u8;
- if (is_integer && integer >= setting->min && integer <= setting->max)
- setting_u8_set(settings, setting, (u8)integer);
- else
- config_err(cfg, "Invalid %s: %s. This should be an integer from %u to %u.", setting->name, value, setting->min, setting->max);
- } break;
- case SETTING_U16: {
- const SettingU16 *setting = &any->u._u16;
- if (is_integer && integer >= setting->min && integer <= setting->max)
- setting_u16_set(settings, setting, (u16)integer);
- else
- config_err(cfg, "Invalid %s: %s. This should be an integer from %u to %u.", setting->name, value, setting->min, setting->max);
- } break;
- case SETTING_U32: {
- const SettingU32 *setting = &any->u._u32;
- if (is_integer && integer >= setting->min && integer <= setting->max)
- setting_u32_set(settings, setting, (u32)integer);
- else
- config_err(cfg, "Invalid %s: %s. This should be an integer from %" PRIu32 " to %" PRIu32 ".",
- setting->name, value, setting->min, setting->max);
- } break;
- case SETTING_FLOAT: {
- const SettingFloat *setting = &any->u._float;
- if (is_floating && floating >= setting->min && floating <= setting->max)
- setting_float_set(settings, setting, (float)floating);
- else
- config_err(cfg, "Invalid %s: %s. This should be a number from %g to %g.", setting->name, value, setting->min, setting->max);
- } break;
- case SETTING_STRING: {
- const SettingString *setting = &any->u._string;
- if (strlen(value) >= setting->buf_size) {
- config_err(cfg, "%s is too long (length: %zu, maximum length: %zu).", key, strlen(value), setting->buf_size - 1);
- } else {
- setting_string_set(settings, setting, value);
- }
- } break;
- }
+
+ SettingAny const *setting_any = NULL;
+ for (u32 i = 0; i < arr_count(settings_all); ++i) {
+ SettingAny const *s = &settings_all[i];
+ if (s->type == 0) break;
+ if (streq(key, s->name)) {
+ setting_any = s;
+ break;
}
}
- if (streq(key, "bg-shader"))
- settings_load_bg_shader(ted, settings);
- if (streq(key, "bg-texture"))
- settings_load_bg_texture(ted, settings);
+ if (!setting_any) {
+ if (streq(key, "bg-shader"))
+ settings_load_bg_shader(ted, applicable_settings, value);
+ else if (streq(key, "bg-texture"))
+ settings_load_bg_texture(ted, applicable_settings, value);
+ // it's probably a bad idea to error on unrecognized settings
+ // because if we ever remove a setting in the future
+ // everyone will get errors
+ break;
+ }
+
+ arr_foreach_ptr(applicable_settings, Settings *, psettings) {
+ Settings *settings = *psettings;
+ if (part->context.language != 0 && !setting_any->per_language) {
+ config_err(cfg, "Setting %s cannot be controlled for individual languages.", key);
+ break;
+ }
+
+ switch (setting_any->type) {
+ case SETTING_BOOL: {
+ const SettingBool *setting = &setting_any->u._bool;
+ if (is_bool)
+ setting_bool_set(settings, setting, boolean);
+ else
+ config_err(cfg, "Invalid %s: %s. This should be yes, no, on, or off.", setting->name, value);
+ } break;
+ case SETTING_U8: {
+ const SettingU8 *setting = &setting_any->u._u8;
+ if (is_integer && integer >= setting->min && integer <= setting->max)
+ setting_u8_set(settings, setting, (u8)integer);
+ else
+ config_err(cfg, "Invalid %s: %s. This should be an integer from %u to %u.", setting->name, value, setting->min, setting->max);
+ } break;
+ case SETTING_U16: {
+ const SettingU16 *setting = &setting_any->u._u16;
+ if (is_integer && integer >= setting->min && integer <= setting->max)
+ setting_u16_set(settings, setting, (u16)integer);
+ else
+ config_err(cfg, "Invalid %s: %s. This should be an integer from %u to %u.", setting->name, value, setting->min, setting->max);
+ } break;
+ case SETTING_U32: {
+ const SettingU32 *setting = &setting_any->u._u32;
+ if (is_integer && integer >= setting->min && integer <= setting->max)
+ setting_u32_set(settings, setting, (u32)integer);
+ else
+ config_err(cfg, "Invalid %s: %s. This should be an integer from %" PRIu32 " to %" PRIu32 ".",
+ setting->name, value, setting->min, setting->max);
+ } break;
+ case SETTING_FLOAT: {
+ const SettingFloat *setting = &setting_any->u._float;
+ if (is_floating && floating >= setting->min && floating <= setting->max)
+ setting_float_set(settings, setting, (float)floating);
+ else
+ config_err(cfg, "Invalid %s: %s. This should be a number from %g to %g.", setting->name, value, setting->min, setting->max);
+ } break;
+ case SETTING_STRING: {
+ const SettingString *setting = &setting_any->u._string;
+ if (strlen(value) >= setting->buf_size) {
+ config_err(cfg, "%s is too long (length: %zu, maximum length: %zu).", key, strlen(value), setting->buf_size - 1);
+ } else {
+ setting_string_set(settings, setting, value);
+ }
+ } break;
+ }
+ }
- // this is probably a bad idea:
- //if (!recognized)
- // config_err(cfg, "Unrecognized setting: %s", key);
- // because if we ever remove a setting qin the future
- // everyone will get errors
} break;
}
}
@@ -952,42 +973,62 @@ void config_parse(Ted *ted, ConfigPart **pparts) {
ConfigPart *const parts = *pparts;
qsort(parts, arr_len(parts), sizeof *parts, config_part_qsort_cmp);
- Settings *settings = NULL;
-
+ const char **paths = NULL;
+ Language *languages = NULL;
+ arr_add(languages, 0);
+ // find all paths and languages referenced in config files
arr_foreach_ptr(parts, ConfigPart, part) {
- cfg->filename = part->file;
- cfg->line_number = part->line;
-
- if (part == parts || config_part_cmp(part, part - 1, false) != 0) {
- // new settings
- settings = arr_addp(ted->all_settings);
-
- // go backwards to find most specific parent
- ConfigPart *parent = part;
- while (1) {
- if (parent <= parts) {
- parent = NULL;
- break;
- }
- --parent;
- if (context_is_parent(&parent->context, &part->context)) {
- // copy parent's settings
- settings_copy(settings, &ted->all_settings[parent->settings]);
+ bool already_have = false;
+ if (part->context.path) {
+ for (u32 i = 0; i < arr_len(paths); ++i) {
+ if (paths_eq(paths[i], part->context.path)) {
+ already_have = true;
break;
}
}
-
- context_free(&settings->context);
- context_copy(&settings->context, &part->context);
+ if (!already_have)
+ arr_add(paths, part->context.path);
+ }
+ already_have = false;
+ for (u32 i = 0; i < arr_len(languages); ++i) {
+ if (languages[i] == part->context.language) {
+ already_have = true;
+ break;
+ }
+ }
+ if (!already_have)
+ arr_add(languages, part->context.language);
+ }
+ arr_foreach_ptr(languages, Language, lang) {
+ // pathless settings
+ {
+ Settings *settings = arr_addp(ted->all_settings);
+ settings->context.language = *lang;
}
- part->settings = arr_len(ted->all_settings) - 1;
+ arr_foreach_ptr(paths, const char *, path) {
+ Settings *settings = arr_addp(ted->all_settings);
+ settings->context.language = *lang;
+ settings->context.path = str_dup(*path);
+ }
+ }
+ arr_free(paths);
+ arr_free(languages);
+
+ arr_foreach_ptr(parts, ConfigPart, part) {
arr_add(part->text, '\0'); // null termination
char *line = part->text;
while (*line) {
- config_parse_line(cfg, settings, part, &line);
-
+ Settings **applicable_settings = NULL;
+ arr_foreach_ptr(ted->all_settings, Settings, settings) {
+ if (context_is_parent(&part->context, &settings->context)) {
+ arr_add(applicable_settings, settings);
+ }
+ }
+ config_parse_line(cfg, applicable_settings, part, &line);
+ arr_free(applicable_settings);
+
if (cfg->error) break;
++cfg->line_number;
diff --git a/main.c b/main.c
index dde5950..b505377 100644
--- a/main.c
+++ b/main.c
@@ -1,17 +1,13 @@
/*
@TODO:
-- rust-analyzer bug reports:
- - bad json can give "Unexpected error: client exited without proper shutdown sequence"
- - containerName not always given in workspace/symbols
-- texlab bug report:
- - textDocument/definition gives LocationLink regardless of client capabilities
+- test new settings system
FUTURE FEATURES:
- doxygen documentation for ted.h
-- better interaction between language-specific and path-specific settings
- manual.md
- CSS highlighting
- option for separate colors for read/write highlights
- styles ([color] sections)
+ - for this, it would be nice to have #include in ted.cfg
- make go-to-definition/hover/highlight modifier key configurable
- return to previous location in buffer
- font setting & support for multiple fonts to cover more characters
@@ -35,6 +31,12 @@ FUTURE FEATURES:
- ctrl+9/0 to inc/dec number would be useful here
- with macros we can really test performance of buffer_insert_text_at_pos, etc. (which should ideally be fast)
- LSP request timeout
+BUG REPORTS IM TO LAZY TO FILE (RIGHT NOW)
+- rust-analyzer:
+ - bad json can give "Unexpected error: client exited without proper shutdown sequence"
+ - containerName not always given in workspace/symbols
+- texlab:
+ - textDocument/definition gives LocationLink regardless of client capabilities
*/
#include "ted.h"
diff --git a/ted.h b/ted.h
index 375049b..74d8f3d 100644
--- a/ted.h
+++ b/ted.h
@@ -143,8 +143,6 @@ typedef struct {
u8 tags_max_depth;
GlRcSAB *bg_shader;
GlRcTexture *bg_texture;
- char bg_shader_text[4096];
- char bg_shader_image[TED_PATH_MAX];
char root_identifiers[4096];
char lsp[512];
char lsp_configuration[4096];
@@ -186,7 +184,6 @@ typedef struct {
char *file;
u32 line;
char *text;
- u32 settings; // index into ted->all_settings. only used in config_parse
} ConfigPart;
// this refers to replacing prev_len characters (found in prev_text) at pos with new_len characters