Skip to content

Instantly share code, notes, and snippets.

@aristidebm
Created May 6, 2025 18:25
Show Gist options
  • Select an option

  • Save aristidebm/aebc2d90c5fae67fa5c000a24a35e9a0 to your computer and use it in GitHub Desktop.

Select an option

Save aristidebm/aebc2d90c5fae67fa5c000a24a35e9a0 to your computer and use it in GitHub Desktop.

Argument list: The underrated (neo)vim list

The argument list is so powerful that I wonder why we as vimmers are underusing it. Below are some useful use cases that I have found for the (neo)vim argument list

Arguments list for bookmarking

This bookmarking journey began with harpoon, a plugin built by a prolific YouTube content creator named Theprimeagen. The main idea is to store commonly used files needed to achieve the task at hand into a buffer for fast access. For more information, check Why harpoon?.

After that, I came across grapple. Grapple shares the same underlying concept as harpoon but is a step ahead. It provides a neat feature on top: Scope. I really like the git_branch one, as it lets you bookmark your files per branch. So if, for example, you are working on branch A and for some reason you have to move to branch B, with that scope activated, when you come back to branch A, all the files you have bookmarked are there waiting for you.

So why move away from all these plugins if they are working well for me? The answer is context overhead.

With these plugins, I have bound <C-j>, <C-k>, <C-l>, and <C-;> to pick the first four files in the bookmark. When I am in a hurry, I usually don't know which file is attached to which key and mess up every time. So I needed to find a solution. An acceptable solution is to be able to fuzzy find my bookmarks. Grapple has a plugin for telescope but not a plugin for fzf-lua. Since I am a huge fan of fzf-lua, I needed to find a solution that can leverage its potential, thus the solution described below.

I use <leader>a to add an item to the argument list, and have bound it as follows, so that I can keep the insertion order:

-- Arguments list management command
nnoremap("<leader>a", ":$argadd | argdedupe <CR>")

I use <C-j>, <C-k>, <C-l>, and <C-;> to pick the first four elements in the argument list in that order:

-- It is 0-based index but the first index always corresponds
-- to the first argument passed to nvim cli command and usually
-- it is the folder name (since I am used to nvim .)
nnoremap("<C-j>", ":execute 'edit' argv(1)<CR>")
nnoremap("<C-k>", ":execute 'edit' argv(2)<CR>")
nnoremap("<C-l>", ":execute 'edit' argv(3)<CR>")
nnoremap("<C-;>", ":execute 'edit' argv(4)<CR>")

When I mess up with the <C-*> keys above, I can launch a fuzzy finder on my argument list by typing <leader>pa and have bound it as follows:

vim.keymap.set("n", "<leader>pa", require("fzf-lua").args, { desc = "[P]roject [A]rgs" })

To clear my argument list, I have created a custom command that clears every entry but directories. This is useful because I usually launch (neo)vim with an argument pointing to a directory (mainly nvim .) and don't want the current directory to be deleted from the argument list. If you want to remove everything from the argument list, (neo)vim covers this use case with :argdelete ##:

vim.api.nvim_create_user_command("ClearArglist", function()
	local arglist = vim.fn.argv()
	for i = #arglist, 1, -1 do
		if vim.fn.isdirectory(arglist[i]) == 1 then
			--- We don't want to remove the directory added to args list
			--- when launching vim with nvim <directory>
		else
			vim.cmd("argdelete " .. arglist[i])
		end
	end

end, {})

Argument list for conflicts files grabbing

Previously when I had merge conflicts while merging a branch into another, I had to go through the output of git merge <branch> to know which files had conflicts in them. When you are a django user, with multiple apps each having django.po, it is not a pleasant experience to go through all these files by scanning the git merge output when you have conflicts in them. So I needed to find a better way of doing this. Here is where it began.

I first came across git mergetool. I tried it, but it did not suit my needs because the mergetool must be opened and closed for each conflicted file. I needed something that would let me go through conflicts as fast as possible, fix conflicts, and stage all the changes at the end.

Since the previous solution did not suit my needs and I knew what I was looking for experience-wise and also knew git has a hooking system, I naturally searched for a hook that could be executed when a merge fails. I went through git hooks, but unfortunately there wasn't such a hook available. So I needed another solution. I asked myself if there were a way to grab all conflicted files after a merge fails, so I could pass those files directly to (neo)vim. It turns out that git provides such a utility: git diff --name-only --diff-filter=U -z. Thus, the solution described below.

I created a bash script that can grab all conflicted files and pass them to (neo)vim as arguments with the aid of perplexity:

#!/usr/bin/env bash

function resolve() {
    # Initialize an empty array to hold conflicted filenames
    local files=()

    # Read NULL-separated filenames from git diff output safely into the array
    while IFS= read -r -d '' file; do
        files+=("$file")  # Append each filename to the array
    done < <(git diff --name-only --diff-filter=U -z)

    # Check if the array is not empty (i.e., there are conflicted files)
    if (( ${#files[@]} )); then
        # Open all conflicted files in Neovim, passing each as a separate argument
        nvim --clean "${files[@]}"
    fi
}

# Run resolve if the function is invoked as a script
if [[ "${#BASH_SOURCE[@]}" -eq 1 ]]; then
    resolve
fi

Then, I set the script above as a git alias so that after a git merge with conflicts, I can run git conflicts to open all conflicted files into (neo)vim:

[alias]
    conflicts = !git-resolve

After the above was working great, I decided to merge these two steps (git merge && git conflicts) into one, thus the git merge-fix alias defined as follows:

#!/usr/bin/env bash

function merge_fix() {
    local bsh="$HOME/.local/bin/bash-scripts/"
    source "$bsh/git-resolve"

    # Merge as usual
    git merge "$@"
    # Retrieve the process exit_code
    exit_code=$?

    if [ $exit_code -ne 0 ]; then
      # The merging process has failed,
      # resolve conflicts if any.
      resolve
      # exit $status
    fi
}

merge_fix $@
[alias]
    merge-fix = !git-merge-fix

After fixing conflicts inside one file, I just have to hit ]a to go to the next one. Here are other keybindings that I use when in a merge conflicts fixing session:

vim.keymap.set("n", "[a", ":previous<CR>")
vim.keymap.set("n", "]a", ":next<CR>")
vim.keymap.set("n", "[A", ":first<CR>")
vim.keymap.set("n", "]A", ":last<CR>")

Argument list for batch processing

This is the most widely known and used approach. It allows you to perform batch processing on files present inside the argument list. I often use it to remove all breakpoint() calls that I have set inside Python files before publishing to the upstream:

$ nvim *.py
:argdo g/breakpoint()/d | up

Conclusion

Do you have different ways of using the argument list? I would appreciate if you shared them in the comment section below. Happy coding

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment