Vim: Go to the changed block under the cursor from Git diff output

2012-01-18T22:16:47+09:00 / tag:vim, tag:release, tag:git / Comments

I often read Git diff output in Vim to review patches etc. While reading a diff output, I often open files listed in the diff output to check more code around changed lines.

Vim has a great command for this situation. It is gf. gf opens the file whose name is under the cursor. Since any diff output contains paths of changed files, all I have to do is to move the cursor to a path then type gf.

Though gf is a great command, the above operation is stressful. Because I have to:

Suppose that the current buffer contains the following text (note that the most left numbers are line numbers; please ignore them):

 1 diff --git a/autoload/gf/diff.vim b/autoload/gf/diff.vim
 2 index 469fdb3..b135316 100644
 3 --- a/autoload/gf/diff.vim
 4 +++ b/autoload/gf/diff.vim
 5 @@ -21,7 +22,7 @@
 6  "     SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 7  "
 8  " Interface
 9 -function! gf#diff#go_to_hunk(type)
10 +function! gf#diff#go(type)
11    let d = gf#diff#investigate_the_hunk_under_the_cursor()
12    if d is 0
13      echomsg 'There is no diff hunk to jump.'
14 @@ -113,7 +114,7 @@ function! gf#diff#investigate_the_hunk_under_the_c
15        return 0
16      endif
17      let [d.from_path, d.to_path] = xs
18 +  call setpos('.', original_position)
19 -  call setpos(original_position)
20
21    return d
22  endfunction

What I want to do is to:

To realize the desired behavior, I wrote a plugin vim-gf-diff which extends gf, <C-w>f, etc to behave so.

Once I wrote vim-gf-diff, I noticed that there are similar situations to open a file by context. So I wrote also vim-gf-user to easily write gf extension like vim-gf-diff and to coexist multiple gf extensions.

I hope that these plugins help someone who have the same problem.

Vim: textobj-line 0.0.0

2012-01-16T22:49:25+09:00 / tag:vim, tag:release / Comments

Released vim-textobj-line 0.0.0. Sometimes I want to select the current line without the end-of-line character, leading spaces and trailing spaces. This plugin provides the text objects for that purpose.

Results in 2011, Resolutions for 2012

2012-01-16T22:16:56+09:00 / tag:diary / Comments

Results of the resolutions for 2011

Improve my skill of programming more.

Took a quick look about Erlang, F#, Ruby on Rails, Reactive Extensions, ASP.NET MVC. Also reviewed about Haskell. But did not create any product with on the languages/frameworks. Too less output.

Adore my girls more.

So so. Bought many dresses.

Don’t waste anymore.

Failed. Ordered a MSD girl with Volks' Full Choice System.

Other highlights in 2011

  1. Adopted more dolls; Luna (one-off model), MSD F-18 (full-choice), DDH-03 (custom).

  2. GitMugged.

  3. iPad 2. This is an evil device; it makes me lazy to face my MacBook for hacking.

Resolutions for 2012

Simplify asynchronous data processing with Reactive Extensions

Problem

Now it’s easy for everyone to release Web applications because of excellent hosting service such as Heroku. And it’s also easy to create Web applications rapidly because of excellent libraries/frameworks such as jQuery for both front-end and back-end. We can write complex process with simple and elegant notation.

But it’s not easy to write asynchronous data processing. Suppose that you choose jQuery to write code for front-end. jQuery provides elegant API to manipulate DOM, and it also provides API such as $.ajax for asynchronous communication. But $.ajax and others are not elegant as API to manipulate DOM.

For example, the search box in pages in Wikipedia is automatically completed. Whenever user types a key in the search box, titles of related pages will be appeared. This function can be written as follows:

var showCompletionMenu = function (words) {
  ...
};

var completeWords = function (partialWord) {
  $.ajax({
    url: 'http://en.wikipedia.org/w/api.php',
    dataType: 'jsonp',
    data: {
      action: 'opensearch',
      format: 'json',
      limit: 100,
      search: 'foo',
      success: function (data) {
        var words = data.data[1];
        showCompletionMenu(words);
      }
    }
  });
};

var $form = $('#userInput');
$form.keyup(function () {
  completeWords($form.val());
});

There are two asynchronous operations:

Traditionally, we have to split code for asynchronous operation like this. This split makes code hard to read, because the order of lines to run is not straight, while usual code can be read and is run from first line to last line. So that it’s very hard to realize code for asynchronous operation without carefully reading.

By the way, the essential part of this example is to provide automatic completion. Neither the timing to show the completion menu nor the source of candidates to be completed are important. But it’s necessary to pass callback functions for asynchronous operations, and such callback functions make code hard to read.

Is there any way to ease the burden?

Solution

Let’s use Reactive Extensions (Rx). Asynchronous data processing can be written elegantly with Rx. It’s also easy to compose asynchronous data processing. Though Rx is a library for .NET Framework, there is also Rx for JavaScript.

For example, the example code can be simplified as follows (see also running examaple):

var showCompletionMenu = function (words) {
  ....
};

var completeWords = function (partialWord) {
  return $.ajaxAsObservable({
    url: 'http://en.wikipedia.org/w/api.php',
    dataType: 'jsonp',
    data: {
      action: 'opensearch',
      search: partialWord,
      format: 'json',
      limit: 100
    }
  })
  .Select(function (data) {return data.data[1];});
};

var $form = $('#userInput');
var observableWords = $form
                      .toObservable("keyup")
                      .Select(function (_) {return $form.val();})
                      .Select(completeWords)
                      .Switch();
observableWords.Subscribe(showCompletionMenu);

Firstly, $form.toObservable("keyup") defines an asynchronous data sequence. "keyup" event can be interpreted as a data sequence which contains the key code being released etc. Once asynchronous data sequence is defined, it’s easy to compose code to process each data in the sequence.

Pay attention to the definition observableWords. With Rx, data sequence which is asynchronously fetched can be treated as an ordinary data sequence such as array. So that the resulting code can be read from first line to last line, and there is less gap to the order of lines to run. It’s easy to write asynchronous operation with Rx, and resulting code is also easy to read and to realize. Yay!

More examples

The example is very simple and it is not useful for actual use. So that it’s should be tweaked. But it’s easy to tweak asynchronous data processing with Rx.

Complete candidates when user stops typing

In most cases, interval of key event is very little (while user types well-known words) or very large (while user forgets a spell of word etc). The first example sends request to Wikipedia for each key event. It’s very wasteful for the former case. So that it’s better to send request when user stops typing for a while.

Rx provides API for this case. It is Throttle which ignores data which is followed by another data before the specified time span. The function can be implemented as follows:

var observableWords = $form
                      .toObservable("keyup")
                      .Select(function (_) {return $form.val();})
                      .Throttle(500)
                      .Select(completeWords)
                      .Switch();

Complete candidates if the content of the input form is changed

The content of the input form is not changed when user types cursor keys, but "keyup" event will be raised. So that the first example sends request to Wikipedia even if the content of the input form is not changed. It’s also wasteful. It’s better to send request if and only if the content is changed.

Rx provides API for this case. It is DistinctUntilChanged. The function can be implemented as follows:

var observableWords = $form
                      .toObservable("keyup")
                      .Select(function (_) {return $form.val();})
                      .Throttle(500)
                      .DistinctUntilChanged()
                      .Select(completeWords)
                      .Switch();

Pitfall of Vim: :doautocmd triggers modeline processing

2011-11-08T01:48:26+09:00 / tag:vim / Comments

Problem

For a long time, I’m irritated a behavior of Vim. Whenever CursorHoldI event is happened, it causes unexpected effects. For example:

This strange behavior is not happened if all autocommands for CursorHoldI are deleted. So that there must be a problematic autocommand, but I couldn’t extract minimal example to reproduce the strange behavior.

Answer

Today I found the cause of the problem. It is :doautocmd! According to :help :doautocmd in Vim 7.3.353:

After applying the autocommands the modelines are processed, so that their settings overrule the settings from autocommands, like what happens when editing a file.

So that, if

the strange behavior I encountered will be happened. See also scripts to reproduce the problem.

But, why :doautocmd triggers modeline processing?

According to :help :doautocmd in Vim 7.3.353:

It’s possible to use this inside an autocommand too, so you can base the autocommands for one extension on another extension. Example:

:au Bufenter *.cpp so ~/.vimrc_cpp
:au Bufenter *.cpp doau BufEnter x.c

Perhaps the automatic modeline processing is useful for that purpose.

But I believe that the automatic modeline processing should be performed while opening a file and it should not be performed other timing. For example, running :doautocmd User NO-OP by hand also triggers modeline processing. This is weird behavior. I’ll ask vim_dev about this design later.

(2011-11-08T20:56:42+09:00 — I asked vim_dev about the design of :doautocmd.)