infra | ||
sharedproducts/servicebus | ||
src | ||
tests | ||
azure.yaml | ||
FzFLuaDemo.sln | ||
nuget.config | ||
README.md |
Ciao Telescope! I don't need you anymore!
Disclaimer: being a software engineer / consultant in the enterprise myself, I am perfectly aware of how expensive software development can get and the value of open source contributions. Telescope is a wonderful piece of software and that we don't have to pay for it is a miracle. The following tutorial therefore doesn't aim to bash telescope but to show alternatives that might fit your needs even better.
Use Case
Trying to find the projects in my solution that use a certain nuget package, that goes by the name ramboe.Utils.Testing
The Problem
telescope yields way too many results due to fuzzy finding
What I actuall want and what we will achieve fairly easily with fzf-lua is an exact search string matching:
How I'd do it with telescope
-
live grep:
ramboe.Utils.Testing
-
send all to quickfix
-
:Telescope quickfix
-
filter through
.csproj
-
send to quickfix again
Telescope and the thing with the exact string matching
Apparently it is not possible to have a strict search in Telescope.
chatgpt
First gives me additional_args
that don't do anything
-- init.lua
require('telescope').setup {
defaults = {
-- sorter = ...
},
pickers = {
-- ...
find_files = {
-- doesn't work
additional_args = function()
return { "--fixed-strings" } -- disables regex & fuzzy, matches literally
end,
theme = "dropdown",
}
},
extensions = {
-- ...
}
}
and then tells me to hardcode file extensions:
doesn't look much better either.
fzf-lua: enabling exact string matching is easy
If we want to use exact string matching, but only for the files picker, we would set it up like this
-- init.lua
require("fzf-lua").setup({
-- ...
-- use exact string matching, but only for the files picker
files = {
fzf_opts = {
['--exact'] = '',
['--no-sort'] = '',
}
},
-- ...
})
Now I really only see the files I want to see
I can now send them to the quickfix window with ctrl+q
, and then live_grep over those files with lgrep_quickfix
Install and set up fzf-lua
Install
fzf-lua is a wrapper around fzf. Make sure fzf is installed in your system
As the README tells us: Inside your lazy.nvim configuration add these lines
-- lua/plugins/init.lua
-- ...
{
"ibhagwan/fzf-lua",
-- optional for icon support
dependencies = { "nvim-tree/nvim-web-devicons" },
-- or if using mini.icons/mini.nvim
-- dependencies = { "echasnovski/mini.icons" },
opts = {}
},
-- ...
Now, in order to use fzf-lua after the installation, try a keymap to the files
picker like so:
vim.keymap.set("n", "<leader>ff", require("fzf-lua").files, { noremap = true, silent = true })
Check out all the pickers with :lua require("fzf-lua").builtin()
Fixing csharp_ls
The Problem
After Install and set up fzf-lua we notice that everything works flawless with lua-language-server and omnisharp but not with csharp_ls:
csharp_ls: -32602 System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values. (Parameter 'lineNumber')
stacktrace...
csharp_ls: -32602: System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values. (
Parameter 'lineNumber')
at Microsoft.CodeAnalysis.Text.CompositeText.CompositeTextLineInfo.get_Item(Int32 lineNumber)
at Microsoft.CodeAnalysis.Text.TextLineCollection.GetLineFromPosition(Int32 position)
at Microsoft.CodeAnalysis.Text.TextLineCollection.GetLinePosition(Int32 position)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.GetLinePosition(Int32 position, CancellationToken cancellati
onToken)
at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.GetLineSpan(TextSpan span, CancellationToken cancellationTok
en)
at Microsoft.CodeAnalysis.SourceLocation.GetLineSpan()
at CSharpLanguageServer.Conversions.Diagnostic.fromRoslynDiagnostic(Diagnostic diagnostic) in /_//src/CSharpLa
nguageServer/Conversions.fs:line 237
at CSharpLanguageServer.Handlers.Diagnostic.diagnostics@71-1.Invoke(Diagnostic diagnostic)
at Microsoft.FSharp.Collections.Internal.IEnumerator.map@75.DoMoveNext(b& curr) in D:\workspace\_work\1\s\src\
fsharp\FSharp.Core\seq.fs:line 77
at Microsoft.FSharp.Collections.Internal.IEnumerator.MapEnumerator1.System.Collections.IEnumerator.MoveNext()
in D:\workspace\_work\1\s\src\fsharp\FSharp.Core\seq.fs:line 64
at System.Collections.Generic.List1..ctor(IEnumerable1 collection)
at Microsoft.FSharp.Collections.SeqModule.ToArray[T](IEnumerable1 source) in D:\workspace\_work\1\s\src\fshar
p\FSharp.Core\seq.fs:line 825
at CSharpLanguageServer.Handlers.Diagnostic.handle@67-66.Invoke(SemanticModel semanticModelMaybe) in /_//src/C
SharpLanguageServer/Handlers/Diagnostic.fs:line 70
at Microsoft.FSharp.Control.AsyncPrimitives.CallThenInvokeNoHijackCheck[a,b](AsyncActivation1 ctxt, b result1
, FSharpFunc2 userCode) in D:\workspace\_work\1\s\src\fsharp\FSharp.Core\async.fs:line 464
at Microsoft.FSharp.Control.Trampoline.Execute(FSharpFunc2 firstAction) in D:\workspace\_work\1\s\src\fsharp\
FSharp.Core\async.fs:line 104
Explanation
First we need to have a quick look under the hood:
:help 'buftype'
https://neovim.io/doc/user/options.html
fzf-lua works with preview buffers, e.g. to display search results, those are then also sent to the LSP.
csharp_ls
is receiving a position (line number) from Neovim that doesn't exist in the underlying document. It's trying to map a buffer position to a line number, but failing — often caused by an invalid buffer state.
csharp_ls
is trying to return or parse diagnostics for a file that it thinks has N lines, but the line number sent from Neovim is beyond that.
fzf-lua
can cause this in the following ways:
-
It opens preview buffers or temporary buffers in the background.
-
It may focus or jump to locations in files temporarily during fuzzy find.
These actions can trigger LSP features like diagnostics, document highlights, etc. even for unsaved or virtual buffers.
Fix
In essence we need to tell neovim to only load csharp_ls
on actual .cs
files.
-- lua/configs/lspconfig.lua
-- ..
-- Create a wrapper on_attach for csharp_ls
local function csharp_on_attach(client, bufnr)
-- disable diagnostics for non .cs files
if client.name == "csharp_ls" then
local ft = vim.bo[bufnr].filetype
local bt = vim.bo[bufnr].buftype
if ft ~= "cs" or bt ~= "" then
vim.diagnostic.disable(bufnr)
end
end
-- still call the default on_attach
on_attach(client, bufnr)
end
lspconfig.csharp_ls.setup({
on_attach = csharp_on_attach,
--...
})
You can find the changes I made to my nvim config here.
Fixing omnisharp
https://github.com/OmniSharp/omnisharp-roslyn/releases/tag/v1.39.14
Intro
I use nvchad and installed the omnisharp language server with Mason. When I then try to open a .cs file it says
/usr/share/nvim/runtime/lua/vim/lsp/rpc.lua:800: Spawning language server with cmd: `{ "omnisharp", "--languagese
rver", "--hostPID", "3425232", "-z", "--hostPID", "3425232", "DotNet:enablePackageRestore=false", "--encoding", "
utf-8", "--languageserver", "Sdk:IncludePrereleases=true", "FormattingOptions:EnableEditorConfigSupport=true" }`
failed. The language server is either not installed, missing from PATH, or not executable
Fix
~/.local/share/nvim/mason/packages/
OmniSharp vs omnisharp
lspconfig.omnisharp.setup({
cmd = { "OmniSharp", "--languageserver", "--hostPID", tostring(pid) },
on_attach = on_attach,
on_init = on_init,
capabilities = capabilities,
})
fzf-lua Customization
With :FzfLua profiles
you can choose between different profiles. Choosing a profile affects UI but also key bindings and basically everything you can configure in fzf-lua.
If you wanted to have a UI that looks like telescope and wanted the typical telescope bindings (like ctrl+q
), you would choose the telescope
profile.
I personally like to stick to the default
profile and configure just what I need. This is done by calling the require("fzf-lua").setup()
function:
-- init.lua
-- you only need this area if you want to configure fzf-lua
require("fzf-lua").setup({
-- preview window is in fullscreen and takes 70% of the space and is above the search results
winopts = {
fullscreen = true,
preview = {
layout = "vertical",
vertical = "up:70%",
},
},
-- use exact string matching, but only for the files picker
files = {
fzf_opts = {
['--exact'] = '',
['--no-sort'] = '',
}
},
-- use cltr-q to select all items and convert to quickfix list (as in telescope)
keymap = {
fzf = {
["ctrl-q"] = "select-all+accept",
},
},
})
The ways in which fzf-lua can be configured can be found here. If you don't care much about the specifics you might want to try some of the profiles that come with fzf-lua.
fzf-lua: there's more!
In fzf-lua, after live grepping through your files / the quickfix window, you can then press ctrl+g
to fuzzy search through the results
to eventually only look into the files you actually want to look into
final workflow
-
live grep:
ramboe.Utils.Testing
" -
ctrl+g
-
grep: .csproj
you can find my whole nvim config here