Fluter IDE with Neovim 0.5 ,built in LSP and lua on windows
02 Aug 2021Introduction
So I recently upgraded to NeoVim 0.5 stable release.This release has a inbuilt LSP(Language server protocol) which is a client to LSP servers.It also includes a Lua framework for building enhanced LSP tools.
I used coc-nvim earlier as LSP client to interact with Dart LSP server which it does automatically using coc-flutter extension.If you want to know how to do that read my previous blog post for more details.
The coc-nvim and coc-flutter are great tools with lot of stability and support.So I might still use it.But I plan to try out setting up Neovim Flutter IDE with its inbuilt LSP and see if its easy to setup and worth using.Below are the steps for the same
NeoVim configuration folder structure for lua.
Since we are migrating to lua based configuration.I have setup my nvim folder as below.Its not necessary to do this.You can also use old folder structure you have and use vim script.Just insert the lua script in vim script using lua tags as below
lua << EOF
-- insert lua code here
EOF
For more details on migrating and different options check out this guide
So I have create the below folder structure in my nvim folder
~\AppData\Local\nvim\..
lua
colorscheme.lua
init.lua
keymappings.lua
nvim_tree.lua
plugins.lua
settings.lua
telescope.lua
init.vim
Here all the lua files are present in lua folder.In init.lua
all the lua files
are linked as below
-- Map leader to space
vim.g.mapleader = ' '
require('plugins')
require('colorscheme')
require('nvim_tree')
require('telescope')
require('settings')
require('keymappings')
So if I add any files in lua folder I need to mention that in init.lua
, so
that it can be linked to NeoVim.Next I link my init.lua
with init.vim
using
the below command
lua << EOF
require('init')
EOF
Actually we can use init.lua
instead of init.vim
.But since the lua feature
is new so I am using init.vim
in case there are some scenarios which I cannot
do using lua.
Plugin Manger in lua
So since we are migrating to Neovim 0.5 with lua support .I will be using a package manager in lua.There are two options
I tried packer and found that it has lot of options and is little bit complicated.So I went with paq-nvim.
Note: If you are still using vim script for NeoVim configuration then you can still use your old plugin manager.
Install paq plugin manager on windows
You install the paq plugin manager on windows using the below command.
git clone https://github.com/savq/paq-nvim.git "$env:LOCALAPPDATA\nvim-data\site\pack\paqs\start\paq-nvim"
Install plugins
We can install plugins by giving the below commands in init.lua
.I have instead
given these commands in the plugins.lua file and given the require('plugins')
command in the init.lua
.
require "paq" {
"savq/paq-nvim"; -- Let Paq manage itself
-- Auto Pair
"jiangmiao/auto-pairs"
}
After adding the above lines source your configuration (using :source % or :luafile %) or quit Neovim and open again and run :PaqInstall. For more details check paq-nvim page.
Connecting Dart LSP server to Neovim LSP client
Dart Language Server
Dart has its own language server.We can check that in dart SDK github page. This is downloaded if you have installed flutter in your system.
To check if Dart language server is working we can use the below command
dart C:\Users\jithu\Documents\flutter\bin\cache\dart-sdk\bin\snapshots\analysis_server.dart.snapshot
The server gets started.
Connecting Dart Language server to nvim lspconfig
We can connect to Dart Language server in 2 ways.
- Manually
- Using flutter-tools plugin
Note: I prefer the second option as flutter-tools plugin.It has lot of flutter related features which we would need to setup manually in the first option.
Manually
To connect the Dart Language server to Neovim manually we need to install
nvim-lspconfigand give the below
lines in our init.lua
require'lspconfig'.dartls.setup{
cmd = { "dart", "C:\\Users\\jithu\\Documents\\flutter\\bin\\cache\\dart-sdk\\bin\\snapshots\\analysis_server.dart.snapshot", "--lsp" },
}
This code is based on the configurations given for dart in nvim lspconfig config page
We need to ensure two things for the above command to work
- dart command is added to the environment variable.
- path to dart lsp server in windows is given with
\\
.This is specific to windows
In order to automatically launch a language server, lspconfig searches up the
directory tree from your current buffer to find a file matching the root_dir
pattern defined in each server’s configuration. For dartls as per
nvim lspconfig config page
the pattern it checks for is pubspec.yaml
After completing the above steps we can check if the LSP server is connected to Neovim using the below command.I opened a flutter project and gave the below command.
:LspInfo
This will give you the details of clients and LSP attached.
Flutter-tools
I use the second option because of the flutter related options available in the
plugin.Install by adding the below lines in plugins.lua
and quit neovim and
open again and do :PaqInstall.
{'akinsho/flutter-tools.nvim', requires = 'nvim-lua/plenary.nvim'};
After install I created a file call flutter.lua
under the lua folder and in
that added the below line to setup the flutter tool plugin.
require("flutter-tools").setup{}
Also added the below line in my init.lua
to link the flutter.lua
require('flutter')
Flutter related features given by this plugin are,FlutterRun,FlutterDevices, FlutterEmulators,FlutterReload,FlutterRestar,FlutterQuit,FlutterOutline, FlutterDevTools etc.It has telescope integration also. For details check the site. You can create key bindings for these commands as I have done.
vim.api.nvim_set_keymap('n', '<Leader>fr',':FlutterRun<CR>'
, { noremap = true, silent = true })
vim.api.nvim_set_keymap('n', '<Leader>fc',
[[<Cmd>lua require('telescope').extensions.flutter.commands()<CR>]],
{ noremap = true, silent = true })
Note: I had a file called telescope.lua for my telescope plugin which was causing issues with flutter telescope extension.So I renamed the file telescope_plugin and its working fine now.
You can add individual bindings for Hot restart etc in similar way.Check Flutter-tools page.
Lets test the LSP is connected or not.I create a test_project for flutter.I
deleted some lines in the main.dart
to check if errors are coming.It was not
detecting the errors.I searched and tried different things.Finally adding
the dart sdk bin in the PATH variable solved it.
C:\Users\jithu\Documents\flutter\bin\cache\dart-sdk\bin
Now I can see the errors coming.So now the dart LSP is connected to NeoVim.
Things to consider.
- flutter tools does not depend on nvim-lspconfig. The two can co-exist but please ensure you do NOT configure dartls using lspconfig. It will be automatically set up by this plugin instead. So the dart LSP is automatically setup and connected to Neovim LSP client by flutter tools. 2)This plugin does not give auto-complete,code action,formatting options.These need to be configured separately which we will be doing in the below steps.
Auto Completion and Snippets
We can add any autocompletion plugin and snippets plugin.I am planning to use autocompletion plugin nvim compe and a snippets plugins vim-snip.The snippets plugin has support to create snippets similar to VS code snippets.So I can copy the common VS code snippets available for dart and flutter.
So install both the plugins using the below command
'hrsh7th/nvim-compe'
'hrsh7th/vim-vsnip'
'hrsh7th/vim-vsnip-integ'
Auto Completion
Now lets setup the auto completion plugin we have installed.I created a file
called compe_snippets.lua
under lua folder and added the below lines in it.
This last lines will allow tab completion.Also check the source which nvim-compe
uses.Since I plan to use vsnip.I have setup that as true.If you have some other
snippets you can use that.
vim.o.completeopt = "menuone,noselect"
vim.api.nvim_set_keymap("i", "<CR>", "compe#confirm({ 'keys': '<CR>', 'select': v:true })",
{ expr = true })
require'compe'.setup {
enabled = true;
autocomplete = true;
debug = false;
min_length = 1;
preselect = 'enable';
throttle_time = 80;
source_timeout = 200;
resolve_timeout = 800;
incomplete_delay = 400;
max_abbr_width = 100;
max_kind_width = 100;
max_menu_width = 100;
documentation = {
border = { '', '' ,'', ' ', '', '', '', ' ' }, -- the border option is the same as `|help nvim_open_win|`
winhighlight = "NormalFloat:CompeDocumentation,FloatBorder:CompeDocumentationBorder",
max_width = 120,
min_width = 60,
max_height = math.floor(vim.o.lines * 0.3),
min_height = 1,
};
source = {
path = true;
buffer = true;
calc = true;
nvim_lsp = true;
nvim_lua = true;
vsnip = true;
ultisnips = false;
luasnip = false;
}; }
local t = function(str)
return vim.api.nvim_replace_termcodes(str, true, true, true)
end
local check_back_space = function()
local col = vim.fn.col('.') - 1
return col == 0 or vim.fn.getline('.'):sub(col, col):match('%s') ~= nil
end
-- Use (s-)tab to:
--- move to prev/next item in completion menuone
--- jump to prev/next snippet's placeholder
_G.tab_complete = function()
if vim.fn.pumvisible() == 1 then
return t "<C-n>"
elseif vim.fn['vsnip#available'](1) == 1 then
return t "<Plug>(vsnip-expand-or-jump)"
elseif check_back_space() then
return t "<Tab>"
else
return vim.fn['compe#complete']() end
end
_G.s_tab_complete = function()
if vim.fn.pumvisible() == 1 then
return t "<C-p>"
elseif vim.fn['vsnip#jumpable'](-1) == 1 then
return t "<Plug>(vsnip-jump-prev)"
else
-- If <S-Tab> is not working in your terminal, change it to <C-h>
return t "<S-Tab>"
end
end
vim.api.nvim_set_keymap("i", "<Tab>", "v:lua.tab_complete()", {expr = true})
vim.api.nvim_set_keymap("s", "<Tab>", "v:lua.tab_complete()", {expr = true})
vim.api.nvim_set_keymap("i", "<S-Tab>", "v:lua.s_tab_complete()", {expr = true})
vim.api.nvim_set_keymap("s", "<S-Tab>", "v:lua.s_tab_complete()", {expr = true})
Also in init.lua
I added the below line to link compe.lua
require('compe')
Snippets
I am using vsnip for snippets.It allows to use VS code snippets.So I have taken the snippets from Dart-code for VS code.
To enable snippet support in LSP I modified the flutter.lua
like this
local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities.textDocument.completion.completionItem.snippetSupport = true;
require("flutter-tools").setup{
lsp = {
capabilities = capabilities, }
}
So next I have given the path where snippets will be stored as below
vim.g.vsnip_snippet_dir = 'C:\\Users\\jithu\\AppData\\Local\\nvim\\snippets\\';
You give the path where you want to store.
Then to create snippets follow the below steps
Open some file (example: main.dart)
Invoke :VsnipOpen command.
Edit snippet.
Then save
Syntax highlighting and formatting
Syntax highlighting
For syntax highlighting we will be using the dart-vim-plugin
plugin.Install by adding the below line in the plugins.lua
file then quite and
open Neovim and :PaqInstall.
'dart-lang/dart-vim-plugin';
Also some color schemes do not have LSP diagnostics highlighting or are changed
by the color schemes.So I added the below in colorscheme.lua
at the end.
vim.cmd([[ autocmd ColorScheme * :lua require('vim.lsp.diagnostic')._define_default_signs_and_highlights() ]])
Source:https://github.com/neovim/nvim-lspconfig/issues/516
Formatting
The same plugin has a formatting command :DartFmt
.So for auto formatting on
save I added the below lines in flutter.lua
-- autoformat on save
vim.cmd 'au BufWritePre *.dart :DartFmt'
Changing the LSP Diagnostic appearance
So we can use the below lines to change the LSP Diagnostic appearance.I added these line in my ‘flutter.lua’
-- LSP Enable diagnostics
vim.lsp.handlers["textDocument/publishDiagnostics"] =
vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, {
virtual_text = false,
underline = true,
signs = true,
update_in_insert = false
})
I have disabled virtual text.You can keep it if you want.
Code Action and UI changes.
For code action and some UI changes I am using the lspsaga
plugin.Add the below line in plugins.lua
quit and start Neovim and the
:PaqInstall to install
-- lsp plugin based on neovim built-in lsp
'glepnir/lspsaga.nvim';
Next to change the symbols and code action bindings using lspsaga ,added the
below lines in flutter.lua
local saga = require 'lspsaga'
saga.init_lsp_saga {
error_sign = '➤',
warn_sign = '➤',
hint_sign = '➤',
infor_sign = '➤',
code_action_icon = '',
code_action_keys = {
quit = '<ESC>',exec = '<CR>'
},
}
vim.api.nvim_set_keymap('n', '<Leader>ca',':Lspsaga code_action<CR>',
{ noremap = true, silent = true })
vim.api.nvim_set_keymap('n', '<Leader>fe',':Lspsaga diagnostic_jump_next<CR>',
{ noremap = true, silent = true })
You can add more keybindings as necessary by referring to lspsaga page.
You can find the youtube video for the above steps below