File gazing with Telescope
Talking about one of my favourtite Neovim plugins
(Neo)Vim has some great built-in options when it comes to file navigation. The
:edit
and
:find
commands, paired with
wildmenu
and the
'path'
option, set to be
optimised with the context of the directory you’re working in, can provide
a pretty good overall experience for moving around a project. An issue I had
with this setup, however, was that wildmenu
would only complete exact matches
for substrings when searching/navigating for something. For example, when
wanting to quickly check the help docs for something, unless I knew the exact
help-tag I wanted to search for, there wasn’t any quick and easy way to get to
the result I wanted, which would lead to having to use :helpgrep
and filter
through the results. For me, this is an area where fuzzy-finders can be very
useful…🔭
“File gazing” in Lua
telescope.nvim is a highly extendable fuzzy finder over lists. Built on the latest awesome features from neovim core. Telescope is centered around modularity, allowing for easy customization.
There are a lot of fuzzy finders available for (Neo)vim, such as ctrlp.vim and fzf.vim, and they’re really good projects, too. The main reason I prefer Telescope, however, is that it feels like it could be a built-in Neovim feature. Let me explain.
Fzf.vim is an amazing fuzzy finder and I used it for a long time before migrating to Telescope. However, it isn’t really a Vim plugin, as it says itself in its README. It really just acts as a way of calling the fzf command-line tool from within Vim. The difference with Telescope is that it’s essentially just running Lua functions inside Neovim, and interacting with the current buffer. To me, this just feels a lot more natural.
Vast defaults
Telescope comes with a bunch of built-in “pickers” by default. They can be
accessed by running :Telescope <builtin_picker>
. For example
find_files
will list files in the current working directory, oldfiles
will
list recently opened files, live_grep
will give results for a string search as
you type etc. It even integrates with the native LSP with commands like
lsp_references
for search for references of the word under the cursor, as well
as diagnostics
for diagnostic search. The whole list of built-in pickers can
be found in the docs.
Extensibility
Telescope is hugely configurable, both visually and functionally. There are two
modes of configuring the default layout: layout_strategy
and layout_config
,
for example:
require("telescope").setup {
defaults = {
layout_strategy = "horizontal",
layout_config = {
height = 0.75,
widht = 0.9
}
}
}
The above shows the default horizontal
layout_strategy
. See :help
telescope.layout
for more detail on the various different layouts available.
The above setup
function sets global settings which are applied to all
built-in pickers. However, certain pickers can be better suited to other layouts
than the default horizontal
layout. Luckily, Telescope allows each picker to
be called with its own settings, and each layout can be configured individually
with some simple Lua functions.
Just as an aside to make more sense of the next section, my Telescope
configuration has a dedicated folder nvim/lua/ah/telescope
. The plug-in is set
up and required in init.lua
, and various functions are kept in utils.lua
to
be called from mappings.lua
.
telescope
├── init.lua
├── mappings.lua
└── utils.lua
One of the built-in pickers is spell_suggest
, which, when called, gives
spelling suggestions for the word under the cursor. However, having the default
large display pop-up seems a bit excessive for spelling suggestions. Instead, we
can write a function to call the spell_suggest
picker with specified settings:
local M = {}
local telescope = require("telescope.builtin")
function M.spell_check()
telescope.spell_suggest(
theme.get_cursor {
prompt_title = "",
layout_config = {
height = 0.25,
width = 0.25
}
})
end
return M
Here, we’re calling the spell_suggest
picker with the built-in get_cursor
theme, which provides a cursor relative list, which seems more appropriate for
this picker. After that we’re just getting rid of the prompt_title
by setting
it to an empty string, and then setting the size of the list in
layout_strategy
. The function can then be called with a keymap, for example
vim.keymap.set("n", "<leader>ss", require("utils").spell_check, { noremap = true, silent = true })
I used to have the following line in my .vimrc
:
command! -nargs=1 Search vimgrep <args> ~/Dropbox/notes/MyNotes/**/*.{tex,md}
This created an Ex command, Search
, which used vimgrep
to search for
a string in my notes directory, and list all the files with the occurrence of
the string in a quickfix window. This was very handy when needing to quickly
look something up in my notes. The issue with this command, however, was that
when going through the quickfix list, each file would get opened in a new
buffer, and so after eventually finding the note I was looking for, the buffer
list would be full of random .tex files. We can mirror this functionality with
Telescope, but without having to open each file to check it. By default, the
find_files
picker lists files in the current working directory. However, it can
also be given a specific directory to list files from. The same can be done with
the live_grep
picker.
function M.search_notes()
telescope.find_files {
cwd = "~/Dropbox/notes/MyNotes/",
prompt_title = "Notes",
layout_config = {
height = 0.85
}
}
end
function M.grep_notes()
telescope.live_grep {
cwd = "~/Dropbox/notes/MyNotes/",
prompt_title = "Notes",
layout_strategy = "vertical",
layout_config = {
height = 0.85,
width = 0.75
}
}
end
The first function is simply using the find_files
picker, but being pointed to
a specific directory to list from.
The second function more closely mirrors the Ex command I mentioned before,
using the live_grep
picker. Having the preview while skimming through my notes
is super convenient
And again, both of these functions are just called using a keymap:
vim.keymap.set("n", "<leader>sn", require("utils").search_notes, { noremap = true, silent = true })
vim.keymap.set("n", "<leader>gn", require("utils").grep_notes, { noremap = true, silent = true })
This post is a lot longer than I initially planned it to be, and I feel like I’ve barely scratched the surface of what Telescope is capable of. Other than the builtin pickers, there’s also a wide range of extensions available. Check out the telescope topics page to see what’s available.
NOTE: :help telescope.nvim
goes into A LOT of detail in terms of configuring
not only the layout, but also the sorting and previewing algorithms available.
I highly recommend reading through there.
I’ll end by highlighting my favourite Telescope picker:
require("telescope.builtin").planets