From 3d0d6ff9d4123d4297f1ced6d3073f7e16514217 Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Sat, 15 Oct 2022 11:09:26 +0200 Subject: [PATCH 1/3] Remove redundant call to ncurses set_tabsize() ncurses defines a global variable called TABSIZE which is the number of columns occupied by a tab character. We set this option to the value of the tab-size option. I believe we never rely on TABSIZE because we always expand tabs to the appropriate number of spaces before passing them to ncurses. The following patches will potentially give each screen its own tab-size, which is not possible with the global TABSIZE, so we should not be using it anyway. --- src/display.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/display.c b/src/display.c index b2fe8b9c2..eff70761f 100644 --- a/src/display.c +++ b/src/display.c @@ -678,12 +678,6 @@ init_display(void) keyok(code, false); } #endif - -#if defined(NCURSES_VERSION_PATCH) && (NCURSES_VERSION_PATCH >= 20080119) - set_tabsize(opt_tab_size); -#else - TABSIZE = opt_tab_size; -#endif } static bool From dfd3e9ed2d39003a975c4b3fa04620cdb22f6424 Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Sat, 15 Oct 2022 20:21:15 +0200 Subject: [PATCH 2/3] Pass tab-size parameter to drawing functions The next patch wants to pass in a tab-size other than the global opt_tab_size, so let's add the parameters to enable that. The next commit really only cares about places where we render diff hunks. Hence, keep passing "opt_tab_size" (or for brevity, 0) to calls that never draw diff hunks anyway. --- include/tig/draw.h | 2 +- src/draw.c | 54 ++++++++++++++++++++++++---------------------- src/help.c | 4 ++-- 3 files changed, 31 insertions(+), 29 deletions(-) diff --git a/include/tig/draw.h b/include/tig/draw.h index 50a474712..6240ba2ac 100644 --- a/include/tig/draw.h +++ b/include/tig/draw.h @@ -25,7 +25,7 @@ enum align { ALIGN_RIGHT }; -bool draw_text(struct view *view, enum line_type type, const char *string); +bool draw_text(struct view *view, enum line_type type, const char *string, int tab_size); bool PRINTF_LIKE(3, 4) draw_formatted(struct view *view, enum line_type type, const char *format, ...); bool draw_graphic(struct view *view, enum line_type type, const chtype graphic[], size_t size, bool separator); bool draw_field(struct view *view, enum line_type type, const char *text, int width, enum align align, bool trim); diff --git a/src/draw.c b/src/draw.c index b764294b6..e20995fd8 100644 --- a/src/draw.c +++ b/src/draw.c @@ -55,7 +55,7 @@ set_view_attr(struct view *view, enum line_type type) static bool draw_chars(struct view *view, enum line_type type, const char *string, int length, - int max_width, bool use_tilde) + int max_width, bool use_tilde, int tab_size) { int len = 0; int col = 0; @@ -67,6 +67,8 @@ draw_chars(struct view *view, enum line_type type, const char *string, int lengt if (length == -1) length = strlen(string); + if (!tab_size) + tab_size = opt_tab_size; if (opt_iconv_out != ICONV_NONE) { string = encoding_iconv(opt_iconv_out, string, length); @@ -75,7 +77,7 @@ draw_chars(struct view *view, enum line_type type, const char *string, int lengt length = strlen(string); } - len = utf8_length(&string, length, skip, &col, max_width, &trimmed, use_tilde, opt_tab_size); + len = utf8_length(&string, length, skip, &col, max_width, &trimmed, use_tilde, tab_size); set_view_attr(view, type); if (len > 0) @@ -101,7 +103,7 @@ draw_space(struct view *view, enum line_type type, int max, int spaces) while (spaces > 0) { int len = MIN(spaces, sizeof(space) - 1); - if (draw_chars(view, type, space, -1, len, false)) + if (draw_chars(view, type, space, -1, len, false, 0)) return true; spaces -= len; } @@ -110,7 +112,7 @@ draw_space(struct view *view, enum line_type type, int max, int spaces) } static bool -draw_text_expanded(struct view *view, enum line_type type, const char *string, int length, int max_width, bool use_tilde) +draw_text_expanded(struct view *view, enum line_type type, const char *string, int length, int max_width, bool use_tilde, int tab_size) { static char text[SIZEOF_STR]; @@ -118,10 +120,10 @@ draw_text_expanded(struct view *view, enum line_type type, const char *string, i length = strlen(string); do { - size_t pos = string_expand(text, sizeof(text), string, length, opt_tab_size); + size_t pos = string_expand(text, sizeof(text), string, length, tab_size); size_t col = view->col; - if (draw_chars(view, type, text, -1, max_width, use_tilde)) + if (draw_chars(view, type, text, -1, max_width, use_tilde, tab_size)) return true; string += pos; length -= pos; @@ -132,15 +134,15 @@ draw_text_expanded(struct view *view, enum line_type type, const char *string, i } static inline bool -draw_textn(struct view *view, enum line_type type, const char *string, int length) +draw_textn(struct view *view, enum line_type type, const char *string, int length, int tab_size) { - return draw_text_expanded(view, type, string, length, VIEW_MAX_LEN(view), false); + return draw_text_expanded(view, type, string, length, VIEW_MAX_LEN(view), false, tab_size); } bool -draw_text(struct view *view, enum line_type type, const char *string) +draw_text(struct view *view, enum line_type type, const char *string, int tab_size) { - return draw_textn(view, type, string, -1); + return draw_textn(view, type, string, -1, tab_size); } static bool @@ -157,14 +159,14 @@ draw_text_overflow(struct view *view, const char *text, enum line_type type, int trimmed = false; size_t len = utf8_length(&tmp, -1, 0, &text_width, max, &trimmed, false, 1); - if (draw_text_expanded(view, type, text, -1, text_width, max < overflow)) + if (draw_text_expanded(view, type, text, -1, text_width, max < overflow, opt_tab_size)) return true; text += len; type = LINE_OVERFLOW; } - if (*text && draw_text(view, type, text)) + if (*text && draw_text(view, type, text, opt_tab_size)) return true; return VIEW_MAX_LEN(view) <= 0; @@ -177,7 +179,7 @@ draw_formatted(struct view *view, enum line_type type, const char *format, ...) int retval; FORMAT_BUFFER(text, sizeof(text), format, retval, true); - return retval >= 0 ? draw_text(view, type, text) : VIEW_MAX_LEN(view) <= 0; + return retval >= 0 ? draw_text(view, type, text, opt_tab_size) : VIEW_MAX_LEN(view) <= 0; } bool @@ -227,7 +229,7 @@ draw_field(struct view *view, enum line_type type, const char *text, int width, } } - return draw_chars(view, type, text, -1, max - 1, trim) + return draw_chars(view, type, text, -1, max - 1, trim, 0) || draw_space(view, type, max - (view->col - col), max); } @@ -333,17 +335,17 @@ draw_lineno_custom(struct view *view, struct view_column *column, unsigned int l text = number; } if (text) - draw_chars(view, LINE_LINE_NUMBER, text, -1, max, true); + draw_chars(view, LINE_LINE_NUMBER, text, -1, max, true, 0); else draw_space(view, LINE_LINE_NUMBER, max, digits3); switch (opt_line_graphics) { case GRAPHIC_ASCII: - return draw_chars(view, LINE_DEFAULT, "| ", -1, 2, false); + return draw_chars(view, LINE_DEFAULT, "| ", -1, 2, false, 0); case GRAPHIC_DEFAULT: return draw_graphic(view, LINE_DEFAULT, &separator, 1, true); case GRAPHIC_UTF_8: - return draw_chars(view, LINE_DEFAULT, "│ ", -1, 2, false); + return draw_chars(view, LINE_DEFAULT, "│ ", -1, 2, false, 0); } return false; @@ -381,7 +383,7 @@ draw_refs(struct view *view, struct view_column *column, const struct ref *refs) if (draw_formatted(view, type, "%s%s%s", format->start, ref->name, format->end)) return true; - if (draw_text(view, LINE_DEFAULT, " ")) + if (draw_text(view, LINE_DEFAULT, " ", opt_tab_size)) return true; } @@ -415,7 +417,7 @@ draw_graph_utf8(void *view, const struct graph *graph, const struct graph_symbol { const char *chars = graph->symbol_to_utf8(symbol); - return draw_text(view, get_graph_color(color_id), chars + !!first); + return draw_text(view, get_graph_color(color_id), chars + !!first, opt_tab_size); } static bool @@ -423,7 +425,7 @@ draw_graph_ascii(void *view, const struct graph *graph, const struct graph_symbo { const char *chars = graph->symbol_to_ascii(symbol); - return draw_text(view, get_graph_color(color_id), chars + !!first); + return draw_text(view, get_graph_color(color_id), chars + !!first, opt_tab_size); } static bool @@ -444,7 +446,7 @@ draw_graph(struct view *view, const struct graph *graph, const struct graph_canv }; graph->foreach_symbol(graph, canvas, fns[opt_line_graphics], view); - return draw_text(view, LINE_DEFAULT, " "); + return draw_text(view, LINE_DEFAULT, " ", opt_tab_size); } static bool @@ -534,7 +536,7 @@ view_column_draw(struct view *view, struct line *line, unsigned int lineno) continue; case VIEW_COLUMN_SECTION: - if (draw_text(view, column->opt.section.type, column->opt.section.text)) + if (draw_text(view, column->opt.section.type, column->opt.section.text, opt_tab_size)) return true; continue; @@ -549,13 +551,13 @@ view_column_draw(struct view *view, struct line *line, unsigned int lineno) const char *text = column_data.text; size_t indent = 0; - if (line->wrapped && draw_text(view, LINE_DELIMITER, "+")) + if (line->wrapped && draw_text(view, LINE_DELIMITER, "+", opt_tab_size)) return true; if (line->graph_indent) { indent = get_graph_indent(text); - if (draw_text_expanded(view, LINE_DEFAULT, text, -1, indent, false)) + if (draw_text_expanded(view, LINE_DEFAULT, text, -1, indent, false, opt_tab_size)) return true; text += indent; } @@ -580,13 +582,13 @@ view_column_draw(struct view *view, struct line *line, unsigned int lineno) indent = 0; } - if (draw_textn(view, cell->type, text, length)) + if (draw_textn(view, cell->type, text, length, opt_tab_size)) return true; text += length; } - } else if (draw_text(view, type, text)) { + } else if (draw_text(view, type, text, opt_tab_size)) { return true; } } diff --git a/src/help.c b/src/help.c index 3dba1709a..c3fd758de 100644 --- a/src/help.c +++ b/src/help.c @@ -46,7 +46,7 @@ help_draw(struct view *view, struct line *line, unsigned int lineno) keymap->hidden ? '+' : '-', keymap->name); } else if (line->type == LINE_HELP_GROUP || !keymap) { - draw_text(view, line->type, help->data.text); + draw_text(view, line->type, help->data.text, opt_tab_size); } else if (help->request > REQ_RUN_REQUESTS) { struct run_request *req = get_run_request(help->request); @@ -73,7 +73,7 @@ help_draw(struct view *view, struct line *line, unsigned int lineno) if (draw_field(view, LINE_HELP_ACTION, enum_name(req_info->name), state->name_width, ALIGN_LEFT, false)) return true; - draw_text(view, LINE_DEFAULT, req_info->help); + draw_text(view, LINE_DEFAULT, req_info->help, opt_tab_size); } return true; From 6b69ad056eeb04a3b3357fc7d45eb2ddc4c3ad26 Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Sat, 15 Oct 2022 20:32:11 +0200 Subject: [PATCH 3/3] Use tab-size from EditorConfig For projects that use tab width != 8, users want to set the "tab-size" config option. Many projects these days already state their preference in a ".editorconfig" file. GitHub honors that file when rendering diffs and blobs. Add an optional dependency to the EditorConfig library to read such files. Prefer the tab-size from EditorConfig, over the "tab-size" config option. Not being able to override the EditorConfig tab-size seems counterintuitive but I don't see why someone would want that, so I'd mayb ewait until someone complains. If we want that we could implement a "tab-size-from-editorconfig" option that defaults to true. Implementation hiccups: Unfortunately, we currently don't always fill "repo.worktree" - only in the special cases where either of $GIT_WORK_TREE or core.worktree is defined. Hence we need to run an extra "git rev-parse --show-toplevel". We do run "git rev-parse --is-inside-worktree [...]" elsewhere but we can't add "--show-toplevel" to that call or else we'd fail when run in bare repos. The use of diff_get_pathname() is a bit wasteful, we should probably refactor this to just remember the last line of type LINE_DIFF_ADD_FILE or LINE_DIFF_HEADER. Closes #840 --- .github/workflows/linux.yml | 5 ++- .github/workflows/macos.yml | 10 ++++- INSTALL.adoc | 2 + NEWS.adoc | 7 +++ configure.ac | 10 +++++ doc/tigrc.5.adoc | 1 + include/tig/diff.h | 9 ++++ include/tig/pager.h | 4 +- include/tig/view.h | 3 ++ src/blob.c | 10 ++++- src/diff.c | 36 ++++++++++------ src/draw.c | 10 ++++- src/log.c | 5 ++- src/pager.c | 86 +++++++++++++++++++++++++++++++++++-- src/stage.c | 7 ++- src/tig.c | 12 ++++++ 16 files changed, 194 insertions(+), 23 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 6523c8944..299f6afd9 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -28,5 +28,8 @@ jobs: export LANG=en_US.utf8 sudo apt update sudo DEBIAN_FRONTEND=noninteractive apt -yq install --no-install-recommends \ - asciidoc valgrind xmlto + asciidoc \ + libeditorconfig-dev \ + valgrind \ + xmlto CC=${{ matrix.compiler }} TIG_BUILD=${{ matrix.tig_build }} tools/travis.sh diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index b0582eea8..b63bc9579 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -45,5 +45,13 @@ jobs: - name: Test Tig shell: 'script -q typescript sh {0}' # Workaround to get a TTY, see https://github.com/gfx/example-github-actions-with-tty run: | - brew install asciidoc autoconf automake coreutils gnu-sed ncurses xmlto + brew install \ + asciidoc \ + autoconf \ + automake \ + coreutils \ + editorconfig \ + gnu-sed \ + ncurses \ + xmlto TIG_BUILD=autoconf tools/travis.sh diff --git a/INSTALL.adoc b/INSTALL.adoc index 2bbfa8d40..194d888a5 100644 --- a/INSTALL.adoc +++ b/INSTALL.adoc @@ -206,6 +206,8 @@ configure script and building documentation: search and command prompts. |PCRE |Adds support for Perl Compatible Regular Expressions in searches. +|EditorConfig core library |Adds support for setting tab-size based on + an .editorconfig file. |autoconf |Contains autoreconf for generating configure from configure.ac. |asciidoc (>= 8.4) |Generates HTML and (DocBook) XML from text. diff --git a/NEWS.adoc b/NEWS.adoc index 4708e5f33..bde686287 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -1,6 +1,13 @@ Release notes ============= +master +------ + +Improvements: + +- Honor tab width from EditorConfig by linking against the EditorConfig core library. (#1239) + tig-2.5.8 --------- diff --git a/configure.ac b/configure.ac index cf55c658f..192e216ce 100644 --- a/configure.ac +++ b/configure.ac @@ -58,6 +58,16 @@ AS_IF([test "x$with_pcre" != xno], [ ]) ]) +dnl Check for EditorConfig library +AC_ARG_WITH(editorconfig, [AS_HELP_STRING([--without-editorconfig], [do not use the EditorConfig library])]) +AS_IF([test "x$with_editorconfig" != xno], [ + AC_CHECK_HEADERS([editorconfig/editorconfig.h]) + AS_IF([test "x$ac_cv_header_editorconfig_editorconfig_h" = xyes], [ + AC_DEFINE([HAVE_EDITORCONFIG], [1], [Define if you have EditorConfig]) + LIBS="$LIBS -leditorconfig" + ]) +]) + dnl OS-specific case $(uname -s 2>/dev/null || echo unknown) in "OS400") AC_CHECK_LIB(util, main, [LIBS="$LIBS -lutil"], AC_MSG_ERROR([Please install the libutil-devel package])) diff --git a/doc/tigrc.5.adoc b/doc/tigrc.5.adoc index 865d3784a..9a812dc98 100644 --- a/doc/tigrc.5.adoc +++ b/doc/tigrc.5.adoc @@ -269,6 +269,7 @@ The following variables can be set: 'tab-size' (int):: Number of spaces per tab. The default is 8 spaces. + This may be overridden by an EditorConfig file. 'diff-context' (int):: diff --git a/include/tig/diff.h b/include/tig/diff.h index 63ad75379..50a0c7e99 100644 --- a/include/tig/diff.h +++ b/include/tig/diff.h @@ -16,7 +16,16 @@ #include "tig/view.h" +#if defined HAVE_EDITORCONFIG +struct diff_common_state { + uint8_t tab_size; +}; +#endif + struct diff_state { +#if defined HAVE_EDITORCONFIG + struct diff_common_state common; +#endif bool after_commit_title; bool after_diff; bool reading_diff_chunk; diff --git a/include/tig/pager.h b/include/tig/pager.h index bf33b2ef9..270ebdccb 100644 --- a/include/tig/pager.h +++ b/include/tig/pager.h @@ -17,10 +17,12 @@ #include "tig/view.h" bool pager_get_column_data(struct view *view, const struct line *line, struct view_column_data *column_data); -bool pager_common_read(struct view *view, const char *data, enum line_type type, struct line **line); +bool pager_common_read(struct view *view, const char *data, enum line_type type, bool is_diff, struct line **line); enum request pager_request(struct view *view, enum request request, struct line *line); void pager_select(struct view *view, struct line *line); +uint8_t editorconfig_tab_size(const char file[]); + extern struct view pager_view; static inline void diff --git a/include/tig/view.h b/include/tig/view.h index 794530c1c..53d0c26fd 100644 --- a/include/tig/view.h +++ b/include/tig/view.h @@ -39,6 +39,9 @@ struct box { struct line { enum line_type type; unsigned int lineno:24; +#if defined HAVE_EDITORCONFIG + unsigned int tab_size:8; +#endif /* State flags */ unsigned int selected:1; diff --git a/src/blob.c b/src/blob.c index 05c258954..b97e4af5f 100644 --- a/src/blob.c +++ b/src/blob.c @@ -14,6 +14,7 @@ #include "tig/refdb.h" #include "tig/parse.h" #include "tig/repo.h" +#include "tig/diff.h" #include "tig/display.h" #include "tig/draw.h" #include "tig/ui.h" @@ -22,6 +23,9 @@ #include "tig/blob.h" struct blob_state { +#if defined HAVE_EDITORCONFIG + struct diff_common_state common; +#endif char commit[SIZEOF_REF]; const char *file; }; @@ -90,6 +94,10 @@ blob_open(struct view *view, enum open_flags flags) else string_copy_rev(view->ref, view->ops->id); +#if defined HAVE_EDITORCONFIG + state->common.tab_size = editorconfig_tab_size(view->env->file); +#endif + return begin_update(view, NULL, argv, flags); } @@ -104,7 +112,7 @@ blob_read(struct view *view, struct buffer *buf, bool force_stop) return true; } - return pager_common_read(view, buf->data, LINE_DEFAULT, NULL); + return pager_common_read(view, buf->data, LINE_DEFAULT, false, NULL); } static void diff --git a/src/diff.c b/src/diff.c index 2071c38ee..8e871b5db 100644 --- a/src/diff.c +++ b/src/diff.c @@ -263,7 +263,7 @@ diff_common_read_diff_wdiff_group(struct diff_stat_context *context) return true; } -static bool +static struct line * diff_common_read_diff_wdiff(struct view *view, const char *text) { struct diff_stat_context context = { text, LINE_DEFAULT }; @@ -282,7 +282,7 @@ diff_common_read_diff_wdiff(struct view *view, const char *text) return diff_common_add_line(view, text, LINE_DEFAULT, &context); } -static bool +static struct line * diff_common_highlight(struct view *view, const char *text, enum line_type type) { struct diff_stat_context context = { text, type, true }; @@ -303,6 +303,7 @@ bool diff_common_read(struct view *view, const char *data, struct diff_state *state) { enum line_type type = get_line_type(data); + struct line *line; /* ADD2 and DEL2 are only valid in combined diff hunks */ if (!state->combined_diff && (type == LINE_DIFF_ADD2 || type == LINE_DIFF_DEL2)) @@ -334,7 +335,7 @@ diff_common_read(struct view *view, const char *data, struct diff_state *state) } if (!state->after_commit_title && !prefixcmp(data, " ")) { - struct line *line = add_line_text(view, data, LINE_DEFAULT); + line = add_line_text(view, data, LINE_DEFAULT); if (line) line->commit_title = 1; @@ -345,13 +346,11 @@ diff_common_read(struct view *view, const char *data, struct diff_state *state) if (type == LINE_DIFF_HEADER) { state->after_diff = true; state->reading_diff_chunk = false; - } else if (type == LINE_DIFF_CHUNK) { const int len = chunk_header_marker_length(data); const char *context = strstr(data + len, "@@"); - struct line *line = - context ? add_line_text_at(view, view->lines, data, LINE_DIFF_CHUNK, len) - : NULL; + line = context ? add_line_text_at(view, view->lines, data, LINE_DIFF_CHUNK, len) + : NULL; struct box *box; if (!line) @@ -363,21 +362,34 @@ diff_common_read(struct view *view, const char *data, struct diff_state *state) box->cell[box->cells++].type = LINE_DIFF_STAT; state->combined_diff = (len > 2); state->reading_diff_chunk = true; - return true; + goto set_tab_width; } else if (type == LINE_COMMIT) { state->reading_diff_chunk = false; } else if (state->highlight && strchr(data, 0x1b)) { - return diff_common_highlight(view, data, type); - + if (!(line = diff_common_highlight(view, data, type))) + return false; + goto set_tab_width; } else if (opt_word_diff && state->reading_diff_chunk && /* combined diff format is not using word diff */ !state->combined_diff) { - return diff_common_read_diff_wdiff(view, data); + if (!(line = diff_common_read_diff_wdiff(view, data))) + return false; + goto set_tab_width; } - return pager_common_read(view, data, type, NULL); + return pager_common_read(view, data, type, true, &line); + +set_tab_width: +#if defined HAVE_EDITORCONFIG + if (type == LINE_DIFF_CHUNK || type == LINE_DEFAULT || + type == LINE_DIFF_ADD || type == LINE_DIFF_ADD2 || + type == LINE_DIFF_DEL || type == LINE_DIFF_DEL2) { + line->tab_size = state->common.tab_size; + } +#endif + return true; } static bool diff --git a/src/draw.c b/src/draw.c index e20995fd8..e942c1165 100644 --- a/src/draw.c +++ b/src/draw.c @@ -471,6 +471,12 @@ view_column_draw(struct view *view, struct line *line, unsigned int lineno) { struct view_column *column = view->columns; struct view_column_data column_data = {0}; + int tab_size; +#if defined HAVE_EDITORCONFIG + tab_size = line->tab_size ? line->tab_size : opt_tab_size; +#else + tab_size = opt_tab_size; +#endif if (!view->ops->get_column_data(view, line, &column_data)) return true; @@ -582,13 +588,13 @@ view_column_draw(struct view *view, struct line *line, unsigned int lineno) indent = 0; } - if (draw_textn(view, cell->type, text, length, opt_tab_size)) + if (draw_textn(view, cell->type, text, length, tab_size)) return true; text += length; } - } else if (draw_text(view, type, text, opt_tab_size)) { + } else if (draw_text(view, type, text, tab_size)) { return true; } } diff --git a/src/log.c b/src/log.c index b95c87b39..96ffb8704 100644 --- a/src/log.c +++ b/src/log.c @@ -19,6 +19,9 @@ #include "tig/pager.h" struct log_state { +#if defined HAVE_EDITORCONFIG + struct diff_common_state common; +#endif /* Used for tracking when we need to recalculate the previous * commit, for example when the user scrolls up or uses the page * up/down in the log view. */ @@ -145,7 +148,7 @@ log_read(struct view *view, struct buffer *buf, bool force_stop) state->reading_diff_stat = false; } - if (!pager_common_read(view, data, type, &line)) + if (!pager_common_read(view, data, type, true, &line)) return false; if (line && state->graph_indent) line->graph_indent = 1; diff --git a/src/pager.c b/src/pager.c index 0f81e110d..37785182f 100644 --- a/src/pager.c +++ b/src/pager.c @@ -15,12 +15,16 @@ #include "tig/pager.h" #include "tig/options.h" #include "tig/request.h" +#include "tig/repo.h" #include "tig/line.h" #include "tig/keys.h" #include "tig/display.h" #include "tig/view.h" #include "tig/draw.h" #include "tig/diff.h" +#if defined HAVE_EDITORCONFIG + #include "editorconfig/editorconfig.h" +#endif /* * Pager backend @@ -75,12 +79,19 @@ pager_wrap_line(struct view *view, const char *data, enum line_type type) bool has_first_line = false; size_t datalen = strlen(data); size_t lineno = 0; + int tab_size; +#if defined HAVE_EDITORCONFIG + struct diff_common_state *state = view->private; + tab_size = state->tab_size ? state->tab_size : opt_tab_size; +#else + tab_size = opt_tab_size; +#endif while (datalen > 0 || !has_first_line) { int width; int trimmed; bool wrapped = !!first_line; - size_t linelen = utf8_length(&data, datalen, 0, &width, view->width, &trimmed, wrapped, opt_tab_size); + size_t linelen = utf8_length(&data, datalen, 0, &width, view->width, &trimmed, wrapped, tab_size); struct line *line; line = add_line_text_at_(view, view->lines, data, linelen, type, 1, wrapped); @@ -105,7 +116,7 @@ pager_wrap_line(struct view *view, const char *data, enum line_type type) } bool -pager_common_read(struct view *view, const char *data, enum line_type type, struct line **line_ptr) +pager_common_read(struct view *view, const char *data, enum line_type type, bool is_diff, struct line **line_ptr) { struct line *line; @@ -130,7 +141,18 @@ pager_common_read(struct view *view, const char *data, enum line_type type, stru data++; add_pager_refs(view, data); } - +#if defined HAVE_EDITORCONFIG + else if (is_diff && type == LINE_DIFF_ADD_FILE) { + struct diff_common_state *state = view->private; + const char *file = diff_get_pathname(view, line, false); + state->tab_size = file ? editorconfig_tab_size(file) : 0; + } else if (type == LINE_DIFF_CHUNK || type == LINE_DEFAULT || + type == LINE_DIFF_ADD || type == LINE_DIFF_ADD2 || + type == LINE_DIFF_DEL || type == LINE_DIFF_DEL2) { + struct diff_common_state *state = view->private; + line->tab_size = state->tab_size; + } +#endif return true; } @@ -214,6 +236,64 @@ pager_open(struct view *view, enum open_flags flags) return diff_init_highlight(view, view->private); } +#if defined HAVE_EDITORCONFIG +static editorconfig_handle the_editorconfig_handle; + +static void +destroy_the_editorconfig_handle() { + editorconfig_handle_destroy(the_editorconfig_handle); +} + +uint8_t +editorconfig_tab_size(const char file[]) { + static argv_string abspath; + static int worktree_path_size; + int tab_size, i, n; + const char *indent_size_str = NULL, *tab_width_str = NULL; + const char *name, *value; + + if (!*file) + return 0; + + if (!*abspath) { + the_editorconfig_handle = editorconfig_handle_init(); + atexit(destroy_the_editorconfig_handle); + + if (!*repo.worktree) { + const char *rev_parse_argv[] = { + "git", "rev-parse", "--show-toplevel", NULL + }; + if (!io_run_buf(rev_parse_argv, repo.worktree, sizeof(repo.worktree) - strlen("/"), NULL, false)) + die("Not a git repository"); // should never happen + } + + strcpy(abspath, repo.worktree); + abspath[strlen(abspath)] = '/'; + worktree_path_size = strlen(abspath); + } + + if (worktree_path_size + strlen(file) + 1 >= sizeof(abspath)) + return 0; + strcpy(abspath + worktree_path_size, file); + if (editorconfig_parse(abspath, the_editorconfig_handle)) + return 0; + + n = editorconfig_handle_get_name_value_count(the_editorconfig_handle); + for (i = 0; i < n; i++) { + editorconfig_handle_get_name_value(the_editorconfig_handle, i, &name, &value); + if (!strcmp(name, "indent_size")) + indent_size_str = value; + if (!strcmp(name, "tab_width")) + tab_width_str = value; + } + if (!tab_width_str) + tab_width_str = indent_size_str; + if (!tab_width_str || parse_int(&tab_size, tab_width_str, 1, 255) != SUCCESS) + return 0; + return tab_size; +} +#endif + static struct view_ops pager_ops = { "line", "", diff --git a/src/stage.c b/src/stage.c index 6fbc6125c..0daec8e5b 100644 --- a/src/stage.c +++ b/src/stage.c @@ -767,6 +767,11 @@ stage_open(struct view *view, enum open_flags flags) if (stage_line_type != LINE_STAT_UNTRACKED) diff_save_line(view, &state->diff, flags); +#if defined HAVE_EDITORCONFIG + if (stage_line_type == LINE_STAT_UNTRACKED) + state->diff.common.tab_size = editorconfig_tab_size(stage_status.new.name); +#endif + view->vid[0] = 0; code = begin_update(view, repo.exec_dir, argv, flags); if (code == SUCCESS && stage_line_type != LINE_STAT_UNTRACKED) { @@ -787,7 +792,7 @@ stage_read(struct view *view, struct buffer *buf, bool force_stop) return true; if (stage_line_type == LINE_STAT_UNTRACKED) - return pager_common_read(view, buf ? buf->data : NULL, LINE_DEFAULT, NULL); + return pager_common_read(view, buf ? buf->data : NULL, LINE_DEFAULT, false, NULL); if (!buf) { if (!diff_done_highlight(&state->diff)) { diff --git a/src/tig.c b/src/tig.c index c8e6786cc..d5d381f06 100644 --- a/src/tig.c +++ b/src/tig.c @@ -60,6 +60,10 @@ #include #endif +#if defined HAVE_EDITORCONFIG +#include +#endif + /* * Option management */ @@ -543,6 +547,14 @@ parse_options(int argc, const char *argv[], bool pager_mode) printf("PCRE2 version %s\n", pcre2_version); #elif defined HAVE_PCRE printf("PCRE version %s\n", pcre_version()); +#endif +#if defined HAVE_EDITORCONFIG + { + int major, minor, patch; + const char *suffix = editorconfig_get_version_suffix(); + editorconfig_get_version(&major, &minor, &patch); + printf("EditorConfig version %d.%d.%d%s\n", major, minor, patch, suffix); + } #endif exit(EXIT_SUCCESS);