diff --git a/NEWS.adoc b/NEWS.adoc index a368ee6ed..01be6599a 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -14,6 +14,7 @@ Improvements: - The blame view requires a working tree. - Fix use of deprecated vwprintw() function. - Update utf8proc to v2.5.0. + - Add --word-diff=plain colorizing support. (#221) Bug fixes: diff --git a/doc/tig.1.adoc b/doc/tig.1.adoc index 30b60e31b..a6c475272 100644 --- a/doc/tig.1.adoc +++ b/doc/tig.1.adoc @@ -183,6 +183,11 @@ Show references (branches, remotes and tags): $ tig refs ----------------------------------------------------------------------------- +Use word diff in the diff view: +----------------------------------------------------------------------------- +$ tig --word-diff=plain +----------------------------------------------------------------------------- + ENVIRONMENT VARIABLES --------------------- diff --git a/include/tig/options.h b/include/tig/options.h index 4636e51a6..79b212851 100644 --- a/include/tig/options.h +++ b/include/tig/options.h @@ -183,6 +183,7 @@ extern iconv_t opt_iconv_out; extern char opt_editor[SIZEOF_STR]; extern const char **opt_cmdline_args; extern bool opt_log_follow; +extern bool opt_word_diff; /* * Mapping between options and command argument mapping. diff --git a/src/diff.c b/src/diff.c index 6f1f4456d..e38162c12 100644 --- a/src/diff.c +++ b/src/diff.c @@ -46,7 +46,7 @@ diff_open(struct view *view, enum open_flags flags) enum status_code diff_init_highlight(struct view *view, struct diff_state *state) { - if (!opt_diff_highlight || !*opt_diff_highlight) + if (!opt_diff_highlight || !*opt_diff_highlight || opt_word_diff) return SUCCESS; struct app_external *app = app_diff_highlight_load(opt_diff_highlight); @@ -211,6 +211,77 @@ diff_common_add_diff_stat(struct view *view, const char *text, size_t offset) return NULL; } +static bool +diff_common_read_diff_wdiff_group(struct diff_stat_context *context) +{ + const char *sep_add = strstr(context->text, "{+"); + const char *sep_del = strstr(context->text, "[-"); + const char *sep; + enum line_type next_type; + const char *end_delimiter; + const char *end_sep; + size_t len; + + if (sep_add == NULL && sep_del == NULL) + return false; + + if (sep_del == NULL || (sep_add != NULL && sep_add < sep_del)) { + sep = sep_add; + next_type = LINE_DIFF_ADD; + end_delimiter = "+}"; + } else { + sep = sep_del; + next_type = LINE_DIFF_DEL; + end_delimiter = "-]"; + } + + diff_common_add_cell(context, sep - context->text, false); + + context->type = next_type; + context->text = sep; + + // workaround for a single }/] change + end_sep = strstr(context->text + sizeof("{+") - 1, end_delimiter); + + if (end_sep == NULL) { + // diff is not terminated + len = strlen(context->text); + } else { + // separators are included in the add/del highlight + len = end_sep - context->text + sizeof("+}") - 1; + } + + diff_common_add_cell(context, len, false); + + if (end_sep == NULL) { + context->text += len; + } else { + context->text = end_sep + sizeof("+}") - 1; + } + context->type = LINE_DEFAULT; + + return true; +} + +static bool +diff_common_read_diff_wdiff(struct view *view, const char *text) +{ + struct diff_stat_context context = { text, LINE_DEFAULT }; + + /* Detect remaining part of a word diff line: + * + * added {+new +} text + * removed[- something -] from the file + * replaced [-some-]{+same+} text + * there could be [-one-] diff part{+s+} in the {+any +} line + */ + while (diff_common_read_diff_wdiff_group(&context)) + ; + + diff_common_add_cell(&context, strlen(context.text), true); + return diff_common_add_line(view, text, LINE_DEFAULT, &context); +} + static bool diff_common_highlight(struct view *view, const char *text, enum line_type type) { @@ -300,6 +371,13 @@ diff_common_read(struct view *view, const char *data, struct diff_state *state) } else if (state->highlight && strchr(data, 0x1b)) { return diff_common_highlight(view, data, type); + } else if (opt_word_diff && state->reading_diff_chunk && + /* combined diff format is not using word diff */ + !state->combined_diff && + (type = LINE_DEFAULT || + /* ADD and DEL are only valid in regular diff hunks */ + type == LINE_DIFF_ADD || type == LINE_DIFF_DEL)) { + return diff_common_read_diff_wdiff(view, data); } return pager_common_read(view, data, type, NULL); diff --git a/src/options.c b/src/options.c index b201be4ff..8ef8024d8 100644 --- a/src/options.c +++ b/src/options.c @@ -123,6 +123,7 @@ iconv_t opt_iconv_out = ICONV_NONE; char opt_editor[SIZEOF_STR] = ""; const char **opt_cmdline_args = NULL; bool opt_log_follow = false; +bool opt_word_diff = false; /* * Mapping between options and command argument mapping. @@ -253,6 +254,11 @@ update_options_from_argv(const char *argv[]) continue; } + if (!strcmp(flag, "--word-diff") || + !strcmp(flag, "--word-diff=plain")) { + opt_word_diff = true; + } + argv[flags_pos++] = flag; } @@ -1065,6 +1071,11 @@ load_options(void) return error("Failed to format TIG_DIFF_OPTS arguments"); } + if (argv_contains(opt_diff_options, "--word-diff") || + argv_contains(opt_diff_options, "--word-diff=plain")) { + opt_word_diff = true; + } + return SUCCESS; } diff --git a/test/diff/diff-wdiff-context-test b/test/diff/diff-wdiff-context-test new file mode 100755 index 000000000..10a41477d --- /dev/null +++ b/test/diff/diff-wdiff-context-test @@ -0,0 +1,213 @@ +#!/bin/sh + +. libtest.sh +. libgit.sh + +steps ' + :save-display diff-default.screen + + :21 + ] + :save-display diff-u4.screen + + ] + :save-display diff-u5.screen + + :toggle diff-context +5 + :save-display diff-u10.screen + + [ + [ + :save-display diff-u8.screen + + :0 + :set diff-context = 3 + :view-main + :view-diff + :save-display diff-default-from-main.screen + + :21 + ] + :save-display diff-u4-from-main.screen + + ] + :save-display diff-u5-from-main.screen + + :toggle diff-context +5 + :save-display diff-u10-from-main.screen + + [ + [ + :save-display diff-u8-from-main.screen +' + +in_work_dir create_repo_from_tgz "$base_dir/files/scala-js-benchmarks.tgz" + +test_tig show master^ --word-diff + +assert_equals 'diff-default.screen' < +AuthorDate: Sat Mar 1 15:59:02 2014 -0500 +Commit: Jonas Fonseca +CommitDate: Sat Mar 1 15:59:02 2014 -0500 + + Add type parameter for js.Dynamic +--- + common/src/main/scala/org/scalajs/benchmark/Benchmark.scala | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/common/src/main/scala/org/scalajs/benchmark/Benchmark.scala b/commo +index 65f914a..3aa4320 100644 +--- a/common/src/main/scala/org/scalajs/benchmark/Benchmark.scala ++++ b/common/src/main/scala/org/scalajs/benchmark/Benchmark.scala +@@ -15,7 +15,7 @@ object Benchmark { + val benchmarks = js.Array[Benchmark]() + val benchmarkApps = js.Array[BenchmarkApp]() + + val global = [-js.Dynamic.global.asInstanceOf[js.Dictionary]-]{+js.Dynamic.glo + global("runScalaJSBenchmarks") = runBenchmarks _ + global("initScalaJSBenchmarkApps") = initBenchmarkApps _ + + + + + + +[diff] a1dcf1aaa11470978db1d5d8bcf9e16201eb70ff - line 1 of 23 100% +EOF + +assert_equals 'diff-u4.screen' < +AuthorDate: Sat Mar 1 15:59:02 2014 -0500 +Commit: Jonas Fonseca +CommitDate: Sat Mar 1 15:59:02 2014 -0500 + + Add type parameter for js.Dynamic +--- + common/src/main/scala/org/scalajs/benchmark/Benchmark.scala | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/common/src/main/scala/org/scalajs/benchmark/Benchmark.scala b/commo +index 65f914a..3aa4320 100644 +--- a/common/src/main/scala/org/scalajs/benchmark/Benchmark.scala ++++ b/common/src/main/scala/org/scalajs/benchmark/Benchmark.scala +@@ -14,9 +14,9 @@ import scala.scalajs.js +object Benchmark { + val benchmarks = js.Array[Benchmark]() + val benchmarkApps = js.Array[BenchmarkApp]() + + val global = [-js.Dynamic.global.asInstanceOf[js.Dictionary]-]{+js.Dynamic.glo + global("runScalaJSBenchmarks") = runBenchmarks _ + global("initScalaJSBenchmarkApps") = initBenchmarkApps _ + + def add(benchmark: Benchmark) { + + + + +[diff] Changes to 'common/src/main/scala/org/scalajs/benchmark/Benchmark.sca100% +EOF + +assert_equals 'diff-u5.screen' < +CommitDate: Sat Mar 1 15:59:02 2014 -0500 + + Add type parameter for js.Dynamic +--- + common/src/main/scala/org/scalajs/benchmark/Benchmark.scala | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/common/src/main/scala/org/scalajs/benchmark/Benchmark.scala b/commo +index 65f914a..3aa4320 100644 +--- a/common/src/main/scala/org/scalajs/benchmark/Benchmark.scala ++++ b/common/src/main/scala/org/scalajs/benchmark/Benchmark.scala +@@ -13,11 +13,11 @@ import scala.scalajs.js + +object Benchmark { + val benchmarks = js.Array[Benchmark]() + val benchmarkApps = js.Array[BenchmarkApp]() + + val global = [-js.Dynamic.global.asInstanceOf[js.Dictionary]-]{+js.Dynamic.glo + global("runScalaJSBenchmarks") = runBenchmarks _ + global("initScalaJSBenchmarkApps") = initBenchmarkApps _ + + def add(benchmark: Benchmark) { + benchmarks.push(benchmark) + + + +[diff] Changes to 'common/src/main/scala/org/scalajs/benchmark/Benchmark.sca100% +EOF + +assert_equals 'diff-u10.screen' <