summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--buffer.c26
-rw-r--r--config.c117
-rw-r--r--main.c4
-rw-r--r--ted-internal.h27
-rw-r--r--ted.c86
-rw-r--r--ted.h16
6 files changed, 167 insertions, 109 deletions
diff --git a/buffer.c b/buffer.c
index 6357ce4..039528e 100644
--- a/buffer.c
+++ b/buffer.c
@@ -85,6 +85,8 @@ struct TextBuffer {
/// capacity of \ref lines
u32 lines_capacity;
+ /// if false, need to recompute settings.
+ bool settings_computed;
Settings settings;
/// which LSP this document is open in
LSPID lsp_opened_in;
@@ -352,7 +354,6 @@ static char *buffer_strdup(TextBuffer *buffer, const char *src) {
static void buffer_set_up(Ted *ted, TextBuffer *buffer) {
buffer->store_undo_events = true;
buffer->ted = ted;
- buffer->settings_idx = -1;
}
static void line_buffer_set_up(Ted *ted, TextBuffer *buffer) {
@@ -454,7 +455,7 @@ Language buffer_language(TextBuffer *buffer) {
if (buffer->manual_language != LANG_NONE)
return (Language)buffer->manual_language;
- const Settings *settings = buffer->ted->default_settings; // important we don't use buffer_settings here since that would cause infinite recursion!
+ const Settings *settings = ted_default_settings(buffer->ted); // important we don't use buffer_settings here since that would cause infinite recursion!
const char *filename = path_filename(buffer->path);
int match_score = 0;
@@ -510,7 +511,7 @@ LSP *buffer_lsp(TextBuffer *buffer) {
return ted_get_lsp_by_id(buffer->ted, buffer->lsp_opened_in);
}
- LSP *true_lsp = ted_get_lsp(buffer->ted, buffer->path, buffer_language(buffer));
+ LSP *true_lsp = ted_get_lsp(buffer->ted, buffer_settings(buffer), buffer->path);
LSP *curr_lsp = ted_get_lsp_by_id(buffer->ted, buffer->lsp_opened_in);
if (true_lsp != curr_lsp) {
if (curr_lsp)
@@ -526,13 +527,11 @@ LSP *buffer_lsp(TextBuffer *buffer) {
Settings *buffer_settings(TextBuffer *buffer) {
Ted *ted = buffer->ted;
- if (buffer->settings_idx >= 0 && buffer->settings_idx < (i32)arr_len(ted->all_settings))
- return &ted->all_settings[buffer->settings_idx];
-
- Settings *settings = ted_get_settings(ted, buffer->path, buffer_language(buffer));
- buffer->settings_idx = (i32)(settings - ted->all_settings);
- assert(buffer->settings_idx >= 0 && buffer->settings_idx < (i32)arr_len(ted->all_settings));
- return settings;
+ if (!buffer->settings_computed) {
+ ted_compute_settings(ted, buffer->path, buffer_language(buffer), &buffer->settings);
+ buffer->settings_computed = true;
+ }
+ return &buffer->settings;
}
u8 buffer_tab_width(TextBuffer *buffer) {
@@ -1001,6 +1000,7 @@ static void buffer_free_inner(TextBuffer *buffer) {
buffer_diagnostics_clear(buffer);
arr_free(buffer->undo_history);
arr_free(buffer->redo_history);
+ settings_free(&buffer->settings);
memset(buffer, 0, sizeof *buffer);
}
@@ -2980,7 +2980,7 @@ Status buffer_load_file(TextBuffer *buffer, const char *path) {
long file_pos = ftell(fp);
size_t file_size = (size_t)file_pos;
fseek(fp, 0, SEEK_SET);
- const Settings *default_settings = buffer->ted->default_settings;
+ const Settings *default_settings = ted_default_settings(buffer->ted);
u32 max_file_size_editable = default_settings->max_file_size;
u32 max_file_size_view_only = default_settings->max_file_size_view_only;
@@ -3057,7 +3057,7 @@ Status buffer_load_file(TextBuffer *buffer, const char *path) {
if (success) {
// everything is good
buffer_clear(buffer);
- buffer->settings_idx = -1;
+ buffer->settings_computed = false;
buffer->lines = lines;
buffer->nlines = nlines;
buffer->frame_earliest_line_modified = 0;
@@ -3255,7 +3255,7 @@ bool buffer_save_as(TextBuffer *buffer, const char *new_path) {
LSP *lsp = buffer_lsp(buffer);
char *prev_path = buffer->path;
buffer->path = buffer_strdup(buffer, new_path);
- buffer->settings_idx = -1; // we might have new settings
+ buffer->settings_computed = false; // we might have new settings
if (buffer->path && buffer_save(buffer)) {
buffer->view_only = false;
diff --git a/config.c b/config.c
index 9124348..fac3c70 100644
--- a/config.c
+++ b/config.c
@@ -129,6 +129,7 @@ static const SettingU16 settings_u16[] = {
{"framerate-cap", &settings_zero.framerate_cap, 3, 1000, false},
{"lsp-port", &settings_zero.lsp_port, 0, 65535, true},
};
+const SettingU16 setting_text_size_dpi_aware = {NULL, &settings_zero.text_size, 0, U16_MAX, false};
static const SettingU32 settings_u32[] = {
{"max-file-size", &settings_zero.max_file_size, 100, 2000000000, false},
{"max-file-size-view-only", &settings_zero.max_file_size_view_only, 100, 2000000000, false},
@@ -157,6 +158,26 @@ static const SettingKeyCombo settings_key_combo[] = {
};
+bool config_applies_to(Config *cfg, const char *path, Language language) {
+ if (cfg->language && language != cfg->language)
+ return false;
+ if (cfg->path && !str_has_path_prefix(path, cfg->path))
+ return false;
+ return true;
+}
+static bool config_has_same_context(const Config *a, const Config *b) {
+ if (a->language != b->language)
+ return false;
+ if (a->path && !b->path)
+ return false;
+ if (!a->path && b->path)
+ return false;
+ if (a->path && !streq(a->path, b->path))
+ return false;
+ return true;
+}
+
+
static void config_set_setting(Config *cfg, ptrdiff_t offset, const void *value, size_t size) {
memmove((char *)&cfg->settings + offset, value, size);
memset(&cfg->settings_set[offset], 1, size);
@@ -191,7 +212,7 @@ static void config_set_string(Config *cfg, const SettingString *set, const char
RcStr **control = (RcStr **)((char *)settings + offset);
if (*control) rc_str_decref(control);
RcStr *rc = rc_str_new(value, -1);
- config_set_setting(cfg, offset, &rc, sizeof rc);
+ config_set_setting(cfg, offset, &rc, sizeof (RcStr *));
}
static void config_set_color(Config *cfg, ColorSetting setting, u32 color) {
config_set_setting(cfg, (char *)&settings_zero.colors[setting] - (char *)&settings_zero, &color, sizeof color);
@@ -236,7 +257,7 @@ static void settings_copy(Settings *dest, const Settings *src) {
dest->key_actions = arr_copy(src->key_actions);
}
-static void settings_free(Settings *settings) {
+void settings_free(Settings *settings) {
arr_free(settings->language_extensions);
gl_rc_sab_decref(&settings->bg_shader);
gl_rc_texture_decref(&settings->bg_texture);
@@ -254,8 +275,13 @@ static void config_free(Config *cfg) {
memset(cfg, 0, sizeof *cfg);
}
-static void config_merge(Config *dest_cfg, const Config *src_cfg) {
- Settings *dest = &dest_cfg->settings;
+
+i32 config_priority(const Config *cfg) {
+ size_t path_len = cfg->path ? strlen(cfg->path) : 0;
+ return (i32)path_len * 2 + (cfg->language != 0);
+}
+
+void config_merge_into(Settings *dest, const Config *src_cfg) {
const Settings *src = &src_cfg->settings;
char *destc = (char *)dest;
const char *srcc = (const char *)src;
@@ -263,6 +289,7 @@ static void config_merge(Config *dest_cfg, const Config *src_cfg) {
LanguageExtension *const src_exts = src->language_extensions;
KeyAction *const dest_keys = dest->key_actions;
KeyAction *const src_keys = src->key_actions;
+ // TODO: decrement reference counts, free language_extensions if needed
for (size_t i = 0; i < sizeof(Settings); i++) {
if (src_cfg->settings_set[i])
destc[i] = srcc[i];
@@ -431,13 +458,16 @@ static void get_config_path(Ted *ted, char *expanded, size_t expanded_sz, const
}
-static char *config_read_string(Ted *ted, ConfigReader *reader, char **ptext) {
- char *p;
+// only reads fp for multi-line strings
+static char *config_read_string(Ted *ted, ConfigReader *reader, const char *first_line, FILE *fp) {
+ const char *p;
+ char line_buf[1024];
u32 start_line = reader->line_number;
- char delimiter = **ptext;
- char *start = *ptext + 1;
+ char delimiter = *first_line;
char *str = NULL;
- for (p = start; *p != delimiter; ++p) {
+ bool increment_p = true;
+ for (p = first_line + 1; *p != delimiter; p += increment_p) {
+ increment_p = true;
switch (*p) {
case '\\':
++p;
@@ -449,31 +479,38 @@ static char *config_read_string(Ted *ted, ConfigReader *reader, char **ptext) {
case 'n':
arr_add(str, '\n');
continue;
+ case 'r':
+ arr_add(str, '\r');
+ continue;
case 't':
arr_add(str, '\t');
continue;
case '[':
arr_add(str, '[');
continue;
- case '\0':
- goto null;
default:
config_err(reader, "Unrecognized escape sequence: '\\%c'.", *p);
- *ptext += strlen(*ptext);
arr_clear(str);
return NULL;
}
break;
- case '\n':
+ case '\0':
++reader->line_number;
+ arr_add(str, '\n');
+ if (!fgets(line_buf, sizeof line_buf, fp)) {
+ reader->line_number = start_line;
+ config_err(reader, "String doesn't end.");
+ arr_clear(str);
+ return NULL;
+ }
+ line_buf[strcspn(line_buf, "\r\n")] = 0;
+ p = line_buf;
+ increment_p = false;
+ continue;
+ case '\r':
+ case '\n':
+ assert(false);
break;
- case '\0':
- null:
- reader->line_number = start_line;
- config_err(reader, "String doesn't end.");
- *ptext += strlen(*ptext);
- arr_clear(str);
- return NULL;
}
arr_add(str, *p);
}
@@ -484,7 +521,6 @@ static char *config_read_string(Ted *ted, ConfigReader *reader, char **ptext) {
ted->strings[ted->nstrings++] = s;
}
arr_clear(str);
- *ptext = p + 1;
return s;
}
@@ -796,6 +832,9 @@ static void config_parse_line(ConfigReader *reader, Config *cfg, char *line, FIL
}
} break;
}
+ if (streq(setting_any->name, "text-size")) {
+ config_set_u16(cfg, &setting_text_size_dpi_aware, (u16)roundf((float)integer * ted_get_ui_scaling(ted)));
+ }
} break;
}
@@ -865,12 +904,13 @@ static void config_read_file(Ted *ted, const char *cfg_path, const char ***inclu
#define SECTION_HEADER_HELP "Section headers should look like this: [(path//)(language.)section-name]"
char path[TED_PATH_MAX]; path[0] = '\0';
char *closing = strchr(line, ']');
+ Language language = 0;
if (!closing) {
config_err(reader, "Unmatched [. " SECTION_HEADER_HELP);
- return false;
+ break;
} else if (closing[1] != '\0') {
config_err(reader, "Text after section. " SECTION_HEADER_HELP);
- return false;
+ break;
} else {
*closing = '\0';
char *section = line + 1;
@@ -891,7 +931,6 @@ static void config_read_file(Ted *ted, const char *cfg_path, const char ***inclu
if (*p == '/')
*p = '\\';
#endif
- cfg->path = str_dup(path);
section = path_end + 2;
}
@@ -899,7 +938,7 @@ static void config_read_file(Ted *ted, const char *cfg_path, const char ***inclu
if (dot) {
*dot = '\0';
- Language language = cfg->language = language_from_str(section);
+ language = language_from_str(section);
if (!language) {
config_err(reader, "Unrecognized language: %s.", section);
}
@@ -913,15 +952,30 @@ static void config_read_file(Ted *ted, const char *cfg_path, const char ***inclu
} else if (streq(section, "core")) {
reader->section = SECTION_CORE;
} else if (streq(section, "extensions")) {
- if (cfg->language != 0 || cfg->path) {
+ if (language != 0 || *path) {
config_err(reader, "Extensions section cannot be language- or path-specific.");
- return;
+ break;
}
reader->section = SECTION_EXTENSIONS;
} else {
config_err(reader, "Unrecognized section: [%s].", section);
}
}
+ Config new_cfg = {
+ .language = language,
+ .path = *path ? path : NULL,
+ };
+ cfg = NULL;
+ // search for config with same context to update
+ arr_foreach_ptr(ted->all_configs, Config, c) {
+ if (config_has_same_context(c, &new_cfg)) {
+ cfg = c;
+ }
+ }
+ if (!cfg) {
+ // create new config
+ cfg = arr_addp(ted->all_configs);
+ }
} else if (line[0] == '%') {
if (str_has_prefix(line, "%include ")) {
char included[TED_PATH_MAX];
@@ -962,7 +1016,7 @@ void config_free_all(Ted *ted) {
ted->strings[i] = NULL;
}
ted->nstrings = 0;
- ted->default_settings = NULL;
+ settings_free(&ted->default_settings);
}
@@ -1030,6 +1084,12 @@ char *settings_get_root_dir(const Settings *settings, const char *path) {
}
}
+void config_read(Ted *ted, const char *filename) {
+ const char **include_stack = NULL;
+ config_read_file(ted, filename, &include_stack);
+ ted_compute_settings(ted, "", LANG_NONE, &ted->default_settings);
+}
+
u32 settings_color(const Settings *settings, ColorSetting color) {
if (color >= COLOR_COUNT) {
assert(0);
@@ -1062,3 +1122,4 @@ float settings_border_thickness(const Settings *settings) {
float settings_padding(const Settings *settings) {
return settings->padding;
}
+
diff --git a/main.c b/main.c
index 60c2396..a972f66 100644
--- a/main.c
+++ b/main.c
@@ -1,7 +1,7 @@
/*
TODO:
+- get rid of ted->strings; do cfg->strings instead.
- switch back to starting file after rename
-- put signature help at top if cursor is near bottom
- .editorconfig? see https://editorconfig.org/
FUTURE FEATURES:
- autodetect indentation (tabs vs spaces)
@@ -1166,7 +1166,7 @@ int main(int argc, char **argv) {
{
// annoyingly, SDL_GL_SwapWindow seems to be a busy loop on my laptop for some reason...
// this is why the framerate-cap settings exists
- const Settings *settings = ted->default_settings;
+ const Settings *settings = ted_default_settings(ted);
if (settings->framerate_cap) {
i32 ms_wait = 1000 / (i32)settings->framerate_cap - (i32)((frame_end_noswap - frame_start) * 1000);
ms_wait -= 1; // give swap an extra ms to make sure it's actually vsynced
diff --git a/ted-internal.h b/ted-internal.h
index 09ba7c9..ad7ce3a 100644
--- a/ted-internal.h
+++ b/ted-internal.h
@@ -55,16 +55,6 @@ typedef struct {
CommandArgument argument;
} KeyAction;
-
-/// A SettingsContext is a context where a specific set of settings are applied.
-/// this corresponds to `[PATH//LANGUAGE.(section)]` in config files.
-typedef struct {
- /// The settings apply to this language.
- Language language;
- /// The settings apply to all paths which start with this string, or all paths if path=NULL
- char *path;
-} SettingsContext;
-
/// Need to use reference counting for textures because of Settings:
/// We copy parent settings to children
/// e.g.
@@ -287,8 +277,10 @@ struct Ted {
TextBuffer *prev_active_buffer;
Node *active_node;
Config *all_configs;
+ /// cwd where \ref default_settings was computed
+ char default_settings_cwd[TED_PATH_MAX];
/// settings to use when no buffer is open
- Settings *default_settings;
+ Settings default_settings;
float window_width, window_height;
vec2 mouse_pos;
u32 mouse_state;
@@ -501,9 +493,11 @@ void command_execute_ex(Ted *ted, Command c, const CommandArgument *argument, co
// === config.c ===
void config_read(Ted *ted, const char *filename);
void config_free_all(Ted *ted);
-/// how well does this settings context fit the given path and language?
-/// the context with the highest score will be chosen.
-long context_score(const char *path, Language lang, const SettingsContext *context);
+void config_merge_into(Settings *dest, const Config *src_cfg);
+bool config_applies_to(Config *cfg, const char *path, Language language);
+/// higher-priority configs override lower-priority ones.
+i32 config_priority(const Config *cfg);
+void settings_free(Settings *settings);
// === find.c ===
void find_init(Ted *ted);
@@ -702,6 +696,7 @@ void ted_update_time(Ted *ted);
void ted_reset_active_buffer(Ted *ted);
/// set ted's error message to the buffer's error.
void ted_error_from_buffer(Ted *ted, TextBuffer *buffer);
+float ted_get_ui_scaling(Ted *ted);
/// Get LSP by ID. Returns NULL if there is no LSP with that ID.
LSP *ted_get_lsp_by_id(Ted *ted, LSPID id);
/// go to this LSP document position, opening a new buffer containing the file if necessary.
@@ -715,6 +710,10 @@ MessageType ted_message_type_from_lsp(LSPWindowMessageType type);
void ted_delete_buffer(Ted *ted, TextBuffer *buffer);
/// Returns a new buffer, or NULL on out of memory
TextBuffer *ted_new_buffer(Ted *ted);
+/// Compute the settings for a file at the given path in the given language.
+///
+/// NOTE: this frees the previous settings stored in `*settings`. so make sure it's either zeroed or points to valid settings.
+void ted_compute_settings(Ted *ted, const char *path, Language language, Settings *settings);
/// check for orphaned nodes and node cycles
void ted_check_for_node_problems(Ted *ted);
/// load ted configuration
diff --git a/ted.c b/ted.c
index ee1c636..bf48559 100644
--- a/ted.c
+++ b/ted.c
@@ -212,37 +212,50 @@ char *ted_get_root_dir(Ted *ted) {
}
}
-Settings *ted_active_settings(Ted *ted) {
- if (ted->active_buffer)
- return buffer_settings(ted->active_buffer);
- Settings *settings = ted->default_settings;
- int settings_score = 0;
- arr_foreach_ptr(ted->all_settings, Settings, s) {
- const SettingsContext *c = &s->context;
- if (c->language != 0) continue;
- if (!c->path || !str_has_prefix(ted->cwd, c->path)) continue;
- int score = (int)strlen(c->path);
- if (score > settings_score) {
- settings = s;
- settings_score = score;
+static int applicable_configs_cmp(void *context, const void *av, const void *bv) {
+ const Config *const all_configs = context;
+ const u32 *ai = av, *bi = bv;
+ const Config *ac = &all_configs[*ai], *bc = &all_configs[*bi];
+ const i32 a = config_priority(ac);
+ const i32 b = config_priority(bc);
+ if (a < b) return -1;
+ if (a > b) return 1;
+ return 0;
+}
+
+void ted_compute_settings(Ted *ted, const char *path, Language language, Settings *settings) {
+ settings_free(settings);
+ memset(settings, 0, sizeof *settings);
+ u32 *applicable_configs = NULL;
+ for (u32 i = 0; i < arr_len(ted->all_configs); i++) {
+ Config *cfg = &ted->all_configs[i];
+ if (config_applies_to(cfg, path, language)) {
+ arr_add(applicable_configs, i);
}
}
- return settings;
+ qsort_with_context(applicable_configs, arr_len(applicable_configs),
+ sizeof applicable_configs[0], applicable_configs_cmp, ted->all_configs);
+ arr_foreach_ptr(applicable_configs, const u32, i) {
+ config_merge_into(settings, &ted->all_configs[*i]);
+ }
+ arr_free(applicable_configs);
}
-Settings *ted_get_settings(Ted *ted, const char *path, Language language) {
- long best_score = 0;
- Settings *settings = ted->default_settings;
- arr_foreach_ptr(ted->all_settings, Settings, s) {
- long score = context_score(path, language, &s->context);
- if (score > best_score) {
- best_score = score;
- settings = s;
- }
+Settings *ted_default_settings(Ted *ted) {
+ if (!streq(ted->default_settings_cwd, ted->cwd)) {
+ // recompute default settings
+ ted_compute_settings(ted, ted->cwd, LANG_NONE, &ted->default_settings);
}
- return settings;
+ return &ted->default_settings;
}
+Settings *ted_active_settings(Ted *ted) {
+ if (ted->active_buffer)
+ return buffer_settings(ted->active_buffer);
+ return ted_default_settings(ted);
+}
+
+
LSP *ted_get_lsp_by_id(Ted *ted, LSPID id) {
if (id == 0) return NULL;
for (int i = 0; ted->lsps[i]; ++i) {
@@ -253,8 +266,7 @@ LSP *ted_get_lsp_by_id(Ted *ted, LSPID id) {
return NULL;
}
-LSP *ted_get_lsp(Ted *ted, const char *path, Language language) {
- const Settings *settings = ted_get_settings(ted, path, language);
+LSP *ted_get_lsp(Ted *ted, Settings *settings, const char *path) {
if (!settings->lsp_enabled)
return NULL;
@@ -405,7 +417,7 @@ static Font *ted_load_multifont(Ted *ted, const char *filenames) {
return first_font;
}
-static float ted_get_ui_scaling(Ted *ted) {
+float ted_get_ui_scaling(Ted *ted) {
#if _WIN32
SDL_SysWMinfo wm_info;
SDL_VERSION(&wm_info.version);
@@ -423,15 +435,6 @@ static float ted_get_ui_scaling(Ted *ted) {
}
void ted_load_fonts(Ted *ted) {
- {
- // compute text size
- float scaling = ted_get_ui_scaling(ted);
- arr_foreach_ptr(ted->all_settings, Settings, set) {
- u16 size = (u16)roundf(scaling * (float)set->text_size_no_dpi);
- set->text_size = clamp_u16(size, TEXT_SIZE_MIN, TEXT_SIZE_MAX);
- }
- }
-
ted_free_fonts(ted);
const Settings *settings = ted_active_settings(ted);
ted->font = ted_load_multifont(ted, rc_str(settings->font, ""));
@@ -724,21 +727,18 @@ void ted_load_configs(Ted *ted) {
}
- ConfigPart *parts = NULL;
- config_read(ted, &parts, global_config_filename);
- config_read(ted, &parts, local_config_filename);
+ config_read(ted, global_config_filename);
+ config_read(ted, local_config_filename);
if (ted->search_start_cwd) {
// read config in start_cwd
char start_cwd_filename[TED_PATH_MAX];
strbuf_printf(start_cwd_filename, "%s%c" TED_CFG, ted->start_cwd, PATH_SEPARATOR);
-
- config_read(ted, &parts, start_cwd_filename);
+ config_read(ted, start_cwd_filename);
}
- config_parse(ted, &parts);
}
void ted_reload_configs(Ted *ted) {
- config_free(ted);
+ config_free_all(ted);
ted_load_configs(ted);
// reset text size
ted_load_fonts(ted);
diff --git a/ted.h b/ted.h
index 43e2409..f21bf3c 100644
--- a/ted.h
+++ b/ted.h
@@ -1182,17 +1182,15 @@ char *ted_get_root_dir_of(Ted *ted, const char *path);
///
/// The returned value should be freed.
char *ted_get_root_dir(Ted *ted);
+/// settings to use when no buffer is open
+Settings *ted_default_settings(Ted *ted);
/// the settings of the active buffer, or the default settings if there is no active buffer
Settings *ted_active_settings(Ted *ted);
-/// Get the settings for a file at the given path in the given language.
-Settings *ted_get_settings(Ted *ted, const char *path, Language language);
-/// Get LSP server which should be used for the given path and language.
-///
-/// If no running server would cover the path and language, a new one is
-/// started if possible.
-/// Returns `NULL` on failure (e.g. there is no LSP server
-/// specified for the given path and language).
-struct LSP *ted_get_lsp(Ted *ted, const char *path, Language language);
+/// Get LSP server which should be used for the given settings and path.
+///
+/// The server is started if necessary.
+/// Returns `NULL` on failure (e.g. there is no LSP server configured).
+struct LSP *ted_get_lsp(Ted *ted, Settings *settings, const char *path);
/// Get the LSP server of the active buffer or directory.
///
/// Returns `NULL` if there is no such server.