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:
Move the cursor to a path before gf, and
Move the cursor to the position which I want to review after opening a file.
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 endfunctionWhat I want to do is to:
Open autoload/gf/diff.vim and move the cursor to the 25th line in
the file by gf if the cursor is located at the 12th line of the diff
output.
Likewise, move the cursor to the 117th line in the file if the cursor is located at the 16th line of the diff output.
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.
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.
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.
So so. Bought many dresses.
Adopted more dolls; Luna (one-off model), MSD F-18 (full-choice), DDH-03 (custom).
iPad 2. This is an evil device; it makes me lazy to face my MacBook for hacking.
More output.
Less loots.
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:
To start watching keyboard event ($form.keyup)
and to handle raised event (completeWords).
To send a request ($.ajax)
and to process received data (showCompletionMenu).
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?
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.
.Select(function (_) {return $form.val();})
is like a $.map in jQuery.
It converts event data into the content of the input form at that timing.
In this example, event data such as released key code is not important,
so that it is replaced with the content of the input form.
.Select(completeWords)
converts the content of the input form into
(an opaque data sequence of) candidate words for completion menu.
Since completeWords is an asynchronous operation,
its result must be expressed as an opaque data sequence,
which is called "observable" in terms of Rx.
.Switch()
is like a concatenation of a list of lists.
.Select(completeWords) returns
an "observable" sequence of "observable" sequences,
but what we want to process is a sequence of a set of candidate words,
so that it’s necessary to "concatenate" the nested "observable" sequences.
The most important feature of Switch is
to produce values only from the most recent "observable" sequence.
Suppose that "r" is typed into the input form,
then "eactive" is typed into the input form.
completeWords is invoked twice.
The first one sends a request with "r",
and the second one sends a request with "reactive".
Since completeWords is asynchronous,
it is not predictable which response is arrived at first.
If a response to "reactive" is arrived before a response to "r",
the latter should be ignored,
because the input form contains "reactive"
and the completion menu should show candidate words for "reactive"
instead of ones for "r".
.Subscribe(showCompletionMenu)
is like a $.each in jQuery.
It registers a "consumer" function
to process each data in the asynchronous data 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!
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.
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();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();For a long time, I’m irritated a behavior of Vim.
Whenever CursorHoldI event is happened, it causes unexpected effects.
For example:
The opened fold under the cursor is closed.
Some buffer-local options are set to different values.
Screen is redrawn, and the cursor line is lined at top of window.
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.
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
One of CursorHoldI autocommands uses :doautocmd, and
The currently edited file contains modelines,
especially setting 'filetype',
the strange behavior I encountered will be happened. See also scripts to reproduce the problem.
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.)