Reviewing a vim plugin with ChatGPT | The Generative Review #2
vim-walker, a plugin for easy navigation of lists
This code review is for a vim plugin called vim-walker. It is an attempt to unify stepwise navigation in vim, for listable things like git diff changes, quicklist entries and other items. It tries (and manages) to decide from context what is currently being navigated through, so that the user can have a single set of keybinds to remember for stepping through list items. It can currently go to first of list, to next and to previous, but going to last would be a simple next improvement (hint hint.)
As in the previous article from this blog, it is just an old vim plugin written by me about a decade since, and now reviewed by GPT.
I have made only slight edits to the ChatGPT output, for readability, and rearranged the text so it’s easier to follow along, but otherwise it is the raw code review that ChatGPT spat, that is, the explanations are not mine, but the AI’s.
Over the next installments I’ll finish this “vim plugin series” with vim-seek. What’s nice about vim plugins is that the code is often short and straightforward, has a C-style imperative paradigm (and thus more accessible to all programmers), and does a simple thing for its purpose, so it’s easy to ascertain whether the GPT code review is of any use. Afterwards, this blog will move on to review other code under other programming languages, so bear with me! I intend to feature code written in Rust, Python and Clojure in the coming months (and other languages.) This will include things like little UI frameworks and even roguelike terminal games.
Asking ChatGPT to review my code
The file appears to be a Vim script (says the clever bot.) Here is a review of its code:
The first few lines check if the
g:loaded_walker
variable exists or if the'compatible'
option is set. If either of these conditions is true, the script exits. Otherwise, theg:loaded_walker
variable is set to 1, indicating that the script has been loaded.The
s:diffCmpNext()
ands:diffCmpPrev()
functions are helper functions fors:diff()
. These functions are used to compare line numbers when iterating through the buffer.s:diff()
is the main function that implements logic for moving between changes in the buffer. It takes a single argument (direction
) which can be either'next'
or'prev'
. The function first gets the current line and the first character of that line. If the first character is'+'
or'-'
, it iterates through the buffer until it finds a line that has a different first character. If it finds a line with a different first character that is'+'
or'-'
, it moves the cursor to that line. Otherwise, it continues to iterate through the buffer until it finds a line with a first character of'+'
or'-'
or until it reaches the end of the buffer. If it finds a line with a first character of'+'
or'-'
, it moves the cursor to that line. Otherwise, it does nothing.
Here is the function described above for reference. I include only this function here because it is the meat of the plugin (Warning: the formatting may not render properly on mobile, if that’s the case, just skip this code and continue reading the review, it’ll do no harm), but here is the full code file on GitHub,
function! s:diff(direction)
let line = line('.')
let first = getline(l:line)[0]
let next = l:first
if a:direction == 'next'
let [lastline, step, Cmp] = [line('$'), 1 , function('s:diffCmpNext')]
else | let [lastline, step, Cmp] = [0, -1 , function('s:diffCmpPrev')]
endif
if l:first == '+' || l:first == '-'
let other = l:first == '+'? '-' : '+'
while l:next == l:first && Cmp(l:line, l:lastline, 0)
let l:line += l:step
let l:next = getline(l:line)[0]
endwhile
if l:next == l:other
execute 'normal!' l:line . 'G'
return
endif
endif
while l:next != '-' && l:next != '+' && Cmp(l:line, l:lastline, 0)
let l:line += l:step
let l:next = getline(l:line)[0]
endwhile
if Cmp(l:line, l:lastline, 1)
execute 'normal!' l:line . 'G'
endif
endfunction
vim-walker’s README on GitHub
Before moving on with the code review, I also include here the README I wrote a long time ago for this plugin.
Walker is a context-aware list stepper. Here are the heuristics used to determine what to do:
If you are in diff mode, it'll walk you through diffs (uses the vim built-ins
[c
,]c
and so forth)If you are in a buffer with git-diff-like output (i.e. has lines beginning with
+
and/or-
and has filetypegit
/diff
), a custom algorithm will help you walk through each change as if you were in diff mode.Otherwise, the commands will walk you through your quicklist (uses
:cfirst
,:cnext
,:cprevious
and so on)
The following commands let you use walker to step through lists in a unified, context-aware fashion:
Command || Example
WalkerFirst || nnoremap <Leader>f :WalkerFirst<CR>
WalkerNext || nnoremap <Leader>d :WalkerNext<CR>
WalkerPrev || nnoremap <Leader>s :WalkerPrev<CR>
vim-walker’s commands and their functions
The
WalkerFirst
,WalkerNext
, andWalkerPrev
commands are user-facing commands. They call the functions which will be explained below, on the next bullet point.s:walkerFirst()
,s:walkerNext()
ands:walkerPrev()
are the functions bound to the commands mentioned above. These functions first check if the current buffer is a diff buffer or a Git commit buffer, and if it is, they call thes:diff()
function to navigate to the next or previous diff. In other words, these functions are responsible for moving the cursor to the first, next, and previous change, respectively, using the&diff
and&ft
options to determine whether to use thes:diff()
function or the built-in Vim commands to move between changes. If the&diff
option is set, the function uses the built-in Vim commands (]c
,[c
) to move between changes. If the&ft
option is set to'git'
or'diff'
, the function uses thes:diff()
function to move between changes. Otherwise, the function uses the built-in Vim commands (cfirst
,cnext
,cprev
,clast
) to move between changes.
The WalkerKeepAlt, WalkerKeepAltOff, and WalkerKeepAltToggle commands are used to toggle the
s:keepalt
variable. This variable is used to determine whether to use thekeepalt
option when executingcfirst
,cnext
,cprev
, andclast
.Additionally, the
s:keepalt
variable is also set to the value ofkeepalt
. When theWalkerKeepAlt
command is executed,s:keepalt
is set to1
, which means thatkeepalt
is truthy. When theWalkerKeepAltOff
command is executed,s:keepalt
is set to0
, which means thatkeepalt
is falsy. When theWalkerKeepAltToggle
command is executed,s:keepalt
is toggled between0
and1
.Finally, the
s:setKeepAlt()
function sets global variabless:cfirst
,s:cnext
,s:cprev
, ands:clast
based on the value of thewalker_keepalt
variable. It returns the value of thekeepalt
argument that is passed to it. Thes:keepalt
variable is also set to the value ofkeepalt
.
Overall, the code looks well-structured and readable. The use of functions and variables with descriptive names helps to improve the code's readability and maintainability. The code also makes good use of Vim's built-in functionality, such as getline()
and the &diff
and &ft
options, to provide a useful and robust navigation tool.