Skip to content

Commit

Permalink
Use tab-size from EditorConfig
Browse files Browse the repository at this point in the history
For projects that use tab width != 8, we need to set the "tab-size"
config option.  Many projects these days state their preference in a
".editorconfig" file.

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 wait 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 jonas#840
  • Loading branch information
krobelus committed Oct 15, 2022
1 parent 524e62b commit 21b4bd9
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 21 deletions.
10 changes: 10 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -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]))
Expand Down
9 changes: 9 additions & 0 deletions include/tig/diff.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 3 additions & 1 deletion include/tig/pager.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@

bool pager_get_column_data(struct view *view, const struct line *line, struct view_column_data *column_data);
bool pager_read(struct view *view, struct buffer *buf, bool force_stop);
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
Expand Down
3 changes: 3 additions & 0 deletions include/tig/view.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
10 changes: 9 additions & 1 deletion src/blob.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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;
};
Expand Down Expand Up @@ -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);
}

Expand All @@ -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
Expand Down
36 changes: 24 additions & 12 deletions src/diff.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand All @@ -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 };
Expand All @@ -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))
Expand Down Expand Up @@ -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;
Expand All @@ -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)
Expand All @@ -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
Expand Down
10 changes: 8 additions & 2 deletions src/draw.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/log.c
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down Expand Up @@ -142,7 +145,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;
Expand Down
86 changes: 83 additions & 3 deletions src/pager.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -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;

Expand All @@ -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;
}

Expand Down Expand Up @@ -208,6 +230,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",
"",
Expand Down
7 changes: 6 additions & 1 deletion src/stage.c
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,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) {
Expand All @@ -777,7 +782,7 @@ stage_read(struct view *view, struct buffer *buf, bool force_stop)
struct stage_state *state = view->private;

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)) {
Expand Down

0 comments on commit 21b4bd9

Please sign in to comment.