summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2023-10-19 09:47:57 -0400
committerpommicket <pommicket@gmail.com>2023-10-19 09:47:57 -0400
commit30f37caf7d9facf7698d01ef77d7da93e7316103 (patch)
treeac49bf6d922766a3613651dec9783099c87930bc
parent0dc4d4db3a91faa799187fd321fcae82b12f9b66 (diff)
basic editorconfig implementation
-rw-r--r--buffer.c4
-rw-r--r--config.c139
-rw-r--r--main.c4
-rw-r--r--ted-internal.h7
-rw-r--r--ted.cfg3
-rw-r--r--ted.h2
6 files changed, 108 insertions, 51 deletions
diff --git a/buffer.c b/buffer.c
index 6766a5a..44992bc 100644
--- a/buffer.c
+++ b/buffer.c
@@ -3185,10 +3185,8 @@ static bool buffer_write_to_file(TextBuffer *buffer, const char *path) {
}
if (i != buffer->nlines-1) {
- #if _WIN32
- if (settings->crlf_windows)
+ if (settings->crlf)
putc('\r', out);
- #endif
putc('\n', out);
}
}
diff --git a/config.c b/config.c
index aa7b705..d6b3eca 100644
--- a/config.c
+++ b/config.c
@@ -88,15 +88,18 @@ typedef struct {
static const Settings settings_zero = {0};
static const SettingBool settings_bool[] = {
{"auto-indent", &settings_zero.auto_indent, true},
- {"auto-add-newline", &settings_zero.auto_add_newline, true},
- {"remove-trailing-whitespace", &settings_zero.remove_trailing_whitespace, true},
+#define SETTING_AUTO_ADD_NEWLINE {"auto-add-newline", &settings_zero.auto_add_newline, true}
+ SETTING_AUTO_ADD_NEWLINE,
+#define SETTING_REMOVE_TRAILING_WHITESPACE {"remove-trailing-whitespace", &settings_zero.remove_trailing_whitespace, true}
+ SETTING_REMOVE_TRAILING_WHITESPACE,
{"auto-reload", &settings_zero.auto_reload, true},
{"auto-reload-config", &settings_zero.auto_reload_config, false},
{"syntax-highlighting", &settings_zero.syntax_highlighting, true},
{"line-numbers", &settings_zero.line_numbers, true},
{"restore-session", &settings_zero.restore_session, false},
{"regenerate-tags-if-not-found", &settings_zero.regenerate_tags_if_not_found, true},
- {"indent-with-spaces", &settings_zero.indent_with_spaces, true},
+#define SETTING_INDENT_WITH_SPACES {"indent-with-spaces", &settings_zero.indent_with_spaces, true}
+ SETTING_INDENT_WITH_SPACES,
{"trigger-characters", &settings_zero.trigger_characters, true},
{"identifier-trigger-characters", &settings_zero.identifier_trigger_characters, true},
{"phantom-completions", &settings_zero.phantom_completions, true},
@@ -109,13 +112,22 @@ static const SettingBool settings_bool[] = {
{"highlight-enabled", &settings_zero.highlight_enabled, true},
{"highlight-auto", &settings_zero.highlight_auto, true},
{"save-backup", &settings_zero.save_backup, true},
+#define SETTING_CRLF {"crlf", &settings_zero.crlf, true}
+ SETTING_CRLF,
+#define SETTING_CRLF_WINDOWS {"crlf-windows", &settings_zero.crlf_windows, true}
{"crlf-windows", &settings_zero.crlf_windows, true},
{"jump-to-build-error", &settings_zero.jump_to_build_error, true},
{"force-monospace", &settings_zero.force_monospace, true},
{"show-diagnostics", &settings_zero.show_diagnostics, true},
};
+static const SettingBool setting_auto_add_newline = SETTING_AUTO_ADD_NEWLINE;
+static const SettingBool setting_indent_with_spaces = SETTING_INDENT_WITH_SPACES;
+static const SettingBool setting_remove_trailing_whitespace = SETTING_REMOVE_TRAILING_WHITESPACE;
+static const SettingBool setting_crlf = SETTING_CRLF;
+static const SettingBool setting_crlf_windows = SETTING_CRLF_WINDOWS;
static const SettingU8 settings_u8[] = {
- {"tab-width", &settings_zero.tab_width, 1, 100, true},
+#define SETTING_TAB_WIDTH {"tab-width", &settings_zero.tab_width, 1, 100, true}
+ SETTING_TAB_WIDTH,
{"cursor-width", &settings_zero.cursor_width, 1, 100, true},
{"undo-save-time", &settings_zero.undo_save_time, 1, 200, true},
{"border-thickness", &settings_zero.border_thickness, 1, 30, false},
@@ -123,6 +135,7 @@ static const SettingU8 settings_u8[] = {
{"scrolloff", &settings_zero.scrolloff, 1, 100, true},
{"tags-max-depth", &settings_zero.tags_max_depth, 1, 100, false},
};
+static const SettingU8 setting_tab_width = SETTING_TAB_WIDTH;
static const SettingU16 settings_u16[] = {
{"text-size", &settings_zero.text_size_no_dpi, TEXT_SIZE_MIN, TEXT_SIZE_MAX, false},
{"max-menu-width", &settings_zero.max_menu_width, 10, U16_MAX, false},
@@ -936,6 +949,9 @@ static int key_action_qsort_cmp_combo(const void *av, const void *bv) {
void settings_finalize(Ted *ted, Settings *settings) {
arr_qsort(settings->key_actions, key_action_qsort_cmp_combo);
settings->text_size = clamp_u16((u16)roundf((float)settings->text_size_no_dpi * ted_get_ui_scaling(ted)), TEXT_SIZE_MIN, TEXT_SIZE_MAX);
+#if _WIN32
+ settings->crlf |= settings->crlf_windows;
+#endif
}
static void config_compile_regex(Config *cfg, ConfigReader *reader) {
@@ -951,7 +967,8 @@ static void config_compile_regex(Config *cfg, ConfigReader *reader) {
}
}
-static bool config_read_ted_cfg(Ted *ted, const char *cfg_path, const char ***include_stack) {
+static void config_read_ted_cfg(Ted *ted, RcStr *cfg_path_rc, const char ***include_stack) {
+ const char *const cfg_path = rc_str(cfg_path_rc, "");
// check for, e.g. %include ted.cfg inside ted.cfg
arr_foreach_ptr(*include_stack, const char *, p_include) {
if (streq(cfg_path, *p_include)) {
@@ -967,17 +984,16 @@ static bool config_read_ted_cfg(Ted *ted, const char *cfg_path, const char ***in
strbuf_cat(text, ", which");
strbuf_catf(text, " includes %s", cfg_path);
ted_error(ted, "%s", text);
- return false;
+ return;
}
}
arr_add(*include_stack, cfg_path);
FILE *fp = fopen(cfg_path, "rb");
if (!fp) {
- return false;
+ return;
}
- RcStr *cfg_path_rc = rc_str_new(cfg_path, -1);
ConfigReader reader_data = {
.ted = ted,
.filename = cfg_path,
@@ -1080,7 +1096,9 @@ static bool config_read_ted_cfg(Ted *ted, const char *cfg_path, const char ***in
strbuf_cpy(included, line + strlen("include "));
str_trim(included);
get_config_path(ted, expanded, sizeof expanded, included);
- config_read_ted_cfg(ted, expanded, include_stack);
+ RcStr *expanded_rc = rc_str_new(expanded, -1);
+ config_read_ted_cfg(ted, expanded_rc, include_stack);
+ rc_str_decref(&expanded_rc);
} else {
config_err(reader, "Unrecognized directive: %s", line);
}
@@ -1097,12 +1115,10 @@ static bool config_read_ted_cfg(Ted *ted, const char *cfg_path, const char ***in
config_err(reader, "Config has text before first section header.");
}
}
- rc_str_decref(&cfg_path_rc);
if (ferror(fp))
ted_error(ted, "Error reading %s.", cfg_path);
fclose(fp);
arr_remove_last(*include_stack);
- return true;
}
static void regex_append_literal_char(StrBuilder *b, char c) {
@@ -1365,9 +1381,10 @@ error:
return NULL;
}
-static bool config_read_editorconfig(Ted *ted, const char *path) {
+static void config_read_editorconfig(Ted *ted, RcStr *path_rc) {
+ const char *const path = rc_str(path_rc, "");
FILE *fp = fopen(path, "r");
- if (!fp) return false;
+ if (!fp) return;
ConfigReader reader_data = {
.ted = ted,
.filename = path,
@@ -1378,7 +1395,6 @@ static bool config_read_editorconfig(Ted *ted, const char *path) {
Config *cfg = NULL;
char line[4096];
bool is_root = false;
- RcStr *path_rc = rc_str_new(path, -1);
while (true) {
char c = config_getc_no_err_on_eof(reader);
if (!c) break;
@@ -1393,7 +1409,7 @@ static bool config_read_editorconfig(Ted *ted, const char *path) {
config_read_to_eol(reader, line, sizeof line);
str_trim(line);
if (strlen(line) == 0 || line[strlen(line) - 1] != ']') {
- config_err(reader, "Unmatched [");
+ config_debug_err(reader, "Unmatched [");
break;
}
line[strlen(line) - 1] = 0;
@@ -1432,20 +1448,49 @@ static bool config_read_editorconfig(Ted *ted, const char *path) {
if (streq(key, "root")) {
str_ascii_to_lowercase(value);
if (cfg) {
- config_err(reader, "root cannot be set outside of preamble");
+ config_debug_err(reader, "root cannot be set outside of preamble");
+ break;
}
if (streq(value, "true"))
is_root = true;
else
is_root = false;
+ break;
+ }
+ const int value_int = atoi(value);
+ if (cfg && streq(key, "indent_style")) {
+ if (streq(value, "tab"))
+ config_set_bool(cfg, &setting_indent_with_spaces, false);
+ else if (streq(value, "space"))
+ config_set_bool(cfg, &setting_indent_with_spaces, true);
+ } else if (cfg && (streq(key, "indent_size") || streq(key, "tab_width")) && value_int > 0 && value_int < 255) {
+ config_set_u8(cfg, &setting_tab_width, (u8)value_int);
+ } else if (cfg && streq(key, "end_of_line")) {
+ if (streq(value, "lf")) {
+ config_set_bool(cfg, &setting_crlf, false);
+ config_set_bool(cfg, &setting_crlf_windows, false);
+ } else if (streq(value, "crlf")) {
+ config_set_bool(cfg, &setting_crlf, true);
+ }
+ // who the fuck uses CR line endings in the 21st century??
+ } else if (cfg && streq(key, "insert_final_newline")) {
+ if (streq(value, "true")) {
+ config_set_bool(cfg, &setting_auto_add_newline, true);
+ } else if (streq(value, "false")) {
+ config_set_bool(cfg, &setting_auto_add_newline, false);
+ }
+ } else if (cfg && streq(key, "trim_trailing_whitespace")) {
+ if (streq(value, "true")) {
+ config_set_bool(cfg, &setting_remove_trailing_whitespace, true);
+ } else if (streq(value, "false")) {
+ config_set_bool(cfg, &setting_remove_trailing_whitespace, false);
+ }
}
}
break;
}
}
- rc_str_decref(&path_rc);
fclose(fp);
- return true;
}
void config_free_all(Ted *ted) {
@@ -1456,6 +1501,35 @@ void config_free_all(Ted *ted) {
settings_free(&ted->default_settings);
}
+void config_read(Ted *ted, const char *path, ConfigFormat format) {
+ const char **include_stack = NULL;
+ config_init_settings();
+ // check if we've already read this
+ arr_foreach_ptr(ted->all_configs, Config, c) {
+ if (streq(rc_str(c->source, ""), path))
+ return;
+ }
+ RcStr *source_rc = rc_str_new(path, -1);
+ // add dummy config so even if `path` doesn't exist we don't try to read it again
+ Config *dummy = arr_addp(ted->all_configs);
+ dummy->source = rc_str_copy(source_rc);
+ dummy->language = LANG_INVALID;
+ switch (format) {
+ case CONFIG_NONE:
+ assert(0);
+ break;
+ case CONFIG_TED_CFG:
+ config_read_ted_cfg(ted, source_rc, &include_stack);
+ // recompute default settings
+ ted_compute_settings(ted, "", LANG_NONE, &ted->default_settings);
+ break;
+ case CONFIG_EDITORCONFIG:
+ config_read_editorconfig(ted, source_rc);
+ break;
+ }
+ rc_str_decref(&source_rc);
+}
+
static char *last_separator(char *path) {
for (int i = (int)strlen(path) - 1; i >= 0; --i)
@@ -1521,29 +1595,6 @@ char *settings_get_root_dir(const Settings *settings, const char *path) {
}
}
-bool config_read(Ted *ted, const char *path, ConfigFormat format) {
- const char **include_stack = NULL;
- config_init_settings();
- // check if we've already read this
- arr_foreach_ptr(ted->all_configs, Config, c) {
- if (streq(rc_str(c->source, ""), path))
- return true;
- }
- switch (format) {
- case CONFIG_TED_CFG:
- if (config_read_ted_cfg(ted, path, &include_stack)) {
- // recompute default settings
- ted_compute_settings(ted, "", LANG_NONE, &ted->default_settings);
- return true;
- }
- return false;
- case CONFIG_EDITORCONFIG:
- return config_read_editorconfig(ted, path);
- }
- assert(0);
- return false;
-}
-
u32 settings_color(const Settings *settings, ColorSetting color) {
if (color >= COLOR_COUNT) {
assert(0);
@@ -1669,9 +1720,11 @@ static void config_test_editorconfig_glob_to_regex(Ted *ted) {
{"{0..99999999}", "/balls", 0},
{"{0..99999999}", "/99999999999", 0},
{"{0..0}", "/0", 1},
- {"{625..629}", "/627", 1},
- {"{625..629}", "/637", 0},
+ {"x{625..629}", "/x627", 1},
+ {"x{625..629}", "/x637", 0},
{"{..}", "/{..}", 1},
+ {"{a\\,b}", "/{a,b}", 1},
+ {"\\{a,b}", "/{a,b}", 1},
};
for (size_t i = 0; i < arr_count(tests); i++) {
editorconfig_glob_test_expect(ted, tests[i].pattern, tests[i].path, tests[i].result);
diff --git a/main.c b/main.c
index d9f206a..9ee97e8 100644
--- a/main.c
+++ b/main.c
@@ -1,7 +1,9 @@
/*
TODO:
- .editorconfig (see https://editorconfig.org/)
- - test number range
+ - handle root = true
+ - reload configs when .editorconfig is saved
+- local .ted.cfg
FUTURE FEATURES:
- more tests
- prepare rename support
diff --git a/ted-internal.h b/ted-internal.h
index 3a9c578..c8a72c3 100644
--- a/ted-internal.h
+++ b/ted-internal.h
@@ -115,6 +115,8 @@ struct Settings {
bool document_links;
bool vsync;
bool save_backup;
+ bool crlf;
+ // this is automatically copied to crlf if compiling for windows
bool crlf_windows;
bool jump_to_build_error;
bool force_monospace;
@@ -154,6 +156,7 @@ struct Settings {
};
typedef enum {
+ CONFIG_NONE = 0,
CONFIG_TED_CFG = 1,
CONFIG_EDITORCONFIG = 2,
} ConfigFormat;
@@ -499,10 +502,8 @@ void command_execute_ex(Ted *ted, Command c, const CommandArgument *argument, co
// === config.c ===
/// read a config file.
///
-/// returns true on success. if the file at `path` does not exist, this returns false but doesn't show an error message.
-///
/// if the config with this path has already been read, this does nothing.
-bool config_read(Ted *ted, const char *path, ConfigFormat format);
+void config_read(Ted *ted, const char *path, ConfigFormat format);
void config_free_all(Ted *ted);
void config_merge_into(Settings *dest, const Config *src_cfg);
/// call this after all your calls to \ref config_merge_into
diff --git a/ted.cfg b/ted.cfg
index 6c86fd9..9d3b864 100644
--- a/ted.cfg
+++ b/ted.cfg
@@ -126,7 +126,8 @@ root-identifiers = .ted-root, .ted-root.out, Cargo.toml, make.bat, CMakeLists.tx
# the backups are deleted immediately after writing.
save-backup = yes
# whether to default to \r\n line endings.
-# this setting is ignored on non-Windows operating system.
+crlf = no
+# same as crlf setting, but ignored on non-Windows operating system.
crlf-windows = no
# you can make your own custom background for ted using a shader.
diff --git a/ted.h b/ted.h
index 6331d5c..1dbacb6 100644
--- a/ted.h
+++ b/ted.h
@@ -70,6 +70,8 @@ enum {
/// GDScript
LANG_GDSCRIPT = 19,
+ /// this will never be a valid language ID
+ LANG_INVALID = 9999,
/// all user-defined languages are greater than this.
LANG_USER_MIN = 100000,
/// all user-defined languages are less than this.