From 176f0b1f52676bbcf61e5098e5cec400d52fc306 Mon Sep 17 00:00:00 2001 From: pommicket Date: Tue, 13 Feb 2024 09:58:16 -0500 Subject: auto-detect indentation --- buffer.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- config.c | 3 ++- menu.c | 4 ++-- node.c | 7 +++++-- ted-internal.h | 1 + ted.cfg | 8 ++++++++ ui.c | 6 +++--- 7 files changed, 84 insertions(+), 10 deletions(-) diff --git a/buffer.c b/buffer.c index 9ef2467..63755d2 100644 --- a/buffer.c +++ b/buffer.c @@ -77,6 +77,14 @@ struct TextBuffer { /// If set to true, buffer will be scrolled to the cursor position next frame. /// This is to fix the problem that \ref x1, \ref y1, \ref x2, \ref y2 are not updated until the buffer is rendered. bool center_cursor_next_frame; + /// whether to indent with spaces + /// + /// this is either set according to the user's settings or according to the autodetected indentation + bool indent_with_spaces; + /// tab size + /// + /// this is either set according to the user's settings or according to the autodetected indentation + uint8_t tab_width; /// x coordinate of left side of buffer float x1; /// y coordinate of top side of buffer @@ -359,6 +367,10 @@ 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; + // will be overwritten by buffer_new_file, buffer_load_file for file buffers + const Settings *settings = buffer_settings(buffer); + buffer->indent_with_spaces = settings->indent_with_spaces; + buffer->tab_width = settings->tab_width; } static void line_buffer_set_up(Ted *ted, TextBuffer *buffer) { @@ -544,11 +556,11 @@ void buffer_recompute_settings(TextBuffer *buffer) { } u8 buffer_tab_width(TextBuffer *buffer) { - return buffer_settings(buffer)->tab_width; + return buffer->tab_width; } bool buffer_indent_with_spaces(TextBuffer *buffer) { - return buffer_settings(buffer)->indent_with_spaces; + return buffer->indent_with_spaces; } u32 buffer_line_count(TextBuffer *buffer) { @@ -2965,6 +2977,50 @@ void buffer_paste(TextBuffer *buffer) { } } +static void buffer_detect_indentation(TextBuffer *buffer) { + const Settings *settings = buffer_settings(buffer); + if (settings->autodetect_indentation && buffer->nlines > 1) { + bool use_tabs = false; + uint32_t spcs2 = 0, spcs4 = 0, spcs8 = 0; + for (uint32_t i = 0; i < buffer->nlines; i++) { + const Line *line = &buffer->lines[i]; + if (line->len == 0) continue; + if (line->str[0] == '\t') { + use_tabs = true; + break; + } + uint32_t nspc = 0; + for (nspc = 0; nspc < line->len; nspc++) { + if (line->str[nspc] != ' ') { + break; + } + } + spcs2 += nspc == 2; + spcs4 += nspc == 4; + spcs8 += nspc == 8; + } + if (use_tabs) { + buffer->indent_with_spaces = false; + buffer->tab_width = settings->tab_width; + } else if (spcs2 * 50 > spcs4) { + buffer->indent_with_spaces = true; + buffer->tab_width = 2; + } else if (spcs4 * 50 > spcs8) { + buffer->indent_with_spaces = true; + buffer->tab_width = 4; + } else if (spcs8) { + buffer->indent_with_spaces = true; + buffer->tab_width = 8; + } else { + buffer->indent_with_spaces = settings->indent_with_spaces; + buffer->tab_width = settings->tab_width; + } + } else { + buffer->indent_with_spaces = settings->indent_with_spaces; + buffer->tab_width = settings->tab_width; + } +} + // if an error occurs, buffer is left untouched (except for the error field) and the function returns false. Status buffer_load_file(TextBuffer *buffer, const char *path) { if (!unicode_is_valid_utf8(path)) { @@ -3097,6 +3153,7 @@ Status buffer_load_file(TextBuffer *buffer, const char *path) { buffer_error(buffer, "Couldn't open file %s: %s.", path, strerror(errno)); success = false; } + buffer_detect_indentation(buffer); return success; } @@ -3137,6 +3194,9 @@ void buffer_new_file(TextBuffer *buffer, const char *path) { buffer->lines_capacity = 4; buffer->lines = buffer_calloc(buffer, buffer->lines_capacity, sizeof *buffer->lines); buffer->nlines = 1; + const Settings *settings = buffer_settings(buffer); + buffer->indent_with_spaces = settings->indent_with_spaces; + buffer->tab_width = settings->tab_width; } static bool buffer_write_to_file(TextBuffer *buffer, const char *path) { @@ -3295,6 +3355,7 @@ bool buffer_save_as(TextBuffer *buffer, const char *new_path) { buffer->last_lsp_check = -INFINITY; // we'll send a didOpen the next time buffer_lsp is called. free(prev_path); + buffer_detect_indentation(buffer); return true; } else { free(buffer->path); diff --git a/config.c b/config.c index 4eb536a..7bdcc7c 100644 --- a/config.c +++ b/config.c @@ -95,6 +95,7 @@ static const SettingBool settings_bool[] = { {"auto-reload", &settings_zero.auto_reload, true}, {"auto-reload-config", &settings_zero.auto_reload_config, false}, {"syntax-highlighting", &settings_zero.syntax_highlighting, true}, + {"autodetect-indentation", &settings_zero.autodetect_indentation, 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}, @@ -115,7 +116,7 @@ static const SettingBool settings_bool[] = { #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}, + SETTING_CRLF_WINDOWS, {"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}, diff --git a/menu.c b/menu.c index 7c7f16f..dce442b 100644 --- a/menu.c +++ b/menu.c @@ -540,7 +540,7 @@ void menu_init(Ted *ted) { ted_add_edit_notify(ted, menu_edit_notify, ted); ted->command_selector = selector_new(); - for (Command c = 0; c < CMD_COUNT; ++c) { + for (Command c = 0; c < CMD_COUNT; ++c) { const char *name = command_to_str(c); if (c != CMD_UNKNOWN && *name) { SelectorEntry entry = { @@ -548,7 +548,7 @@ void menu_init(Ted *ted) { }; selector_add_entry(ted->command_selector, &entry); } - } + } MenuInfo save_as_menu = { .open = save_as_menu_open, diff --git a/node.c b/node.c index f4fe8fc..8f32087 100644 --- a/node.c +++ b/node.c @@ -418,8 +418,11 @@ void node_frame(Ted *ted, Node *node, Rect r) { // highlight active tab gl_geometry_rect(tab_rect, settings_color(settings, is_active ? COLOR_ACTIVE_TAB_HL : COLOR_SELECTED_TAB_HL)); // set window title to active tab's title - strbuf_printf(ted->window_title, "ted %s | %s", tab_title, - settings->indent_with_spaces ? "spaces" : "tabs"); + strbuf_printf(ted->window_title, "ted %s | ", tab_title); + if (buffer_indent_with_spaces(buffer)) + strbuf_catf(ted->window_title, "%u spaces", buffer_tab_width(buffer)); + else + strbuf_catf(ted->window_title, "tabs"); if (*rc_str(settings->lsp, "")) { LSP *lsp = buffer_lsp(buffer); strbuf_catf(ted->window_title, " | LSP %s", diff --git a/ted-internal.h b/ted-internal.h index c8a72c3..f5e3211 100644 --- a/ted-internal.h +++ b/ted-internal.h @@ -121,6 +121,7 @@ struct Settings { bool jump_to_build_error; bool force_monospace; bool show_diagnostics; + bool autodetect_indentation; KeyCombo hover_key; KeyCombo highlight_key; u8 tab_width; diff --git a/ted.cfg b/ted.cfg index 702284b..ea59963 100644 --- a/ted.cfg +++ b/ted.cfg @@ -3,6 +3,14 @@ [core] tab-width = 4 indent-with-spaces = off +# if enabled, indentation type will be automatically detected from file when possible +# how this currently works: +# 1. if any lines start with a tab character, tabs are used +# 2. otherwise, if # 4-spaced lines / 50 < # 2-spaced lines, 2 spaces are used +# 3. otherwise, if # 8-spaced lines / 50 < # 4-spaced lines, 4 spaces are used +# 4. otherwise, if any lines start with exactly 8 spaces, 8 spaces are used +# 5. otherwise, your default settings are used +autodetect-indentation = on # cursor width in pixels cursor-width = 1 # time to blink cursor for (i.e. it will be on for cursor-blink-time-on seconds, then off for cursor-blink-time-off seconds) diff --git a/ui.c b/ui.c index 995c631..2af72fa 100644 --- a/ui.c +++ b/ui.c @@ -501,9 +501,9 @@ static bool file_selector_cd(Ted *ted, FileSelector *fs, const char *path) { static ColorSetting color_setting_for_file_type(FsType type) { switch (type) { - case FS_FILE: return COLOR_TEXT; - case FS_DIRECTORY: return COLOR_TEXT_FOLDER; - default: return COLOR_TEXT_OTHER; + case FS_FILE: return COLOR_TEXT; + case FS_DIRECTORY: return COLOR_TEXT_FOLDER; + default: return COLOR_TEXT_OTHER; } } -- cgit v1.2.3