I have used quite a few environments for programming. Starting with Windows and CodeBlocks few years back, then upgrading it a little bit by switching to Visual Studio Code, to which I stuck for a while. At some point, my laptop could not handle all the programs I want to use, so I switched to Arch Linux on some random night, which was not a pleasant night, as I did not use any linux before. Xfce desktop environment took some weight of my laptop’s shoulders as it decreased ram usage to < 300MB. I am not gonna lie, it was quite a challenge to use it at the beggining, but it became - after few tears and sleepless nights - awesome, and I never even considered going back to Windows.
Finally, when things with Flutter got more serious, I had to buy Macbook (Air M1 2020), so I can develop mobile apps for iOS too. I am using it till this day! Despite the fact, that I have tried to use Neovim on Arch Linux, when I switched to MacOS, I’ve started using JetBrains IDEs. I had stronger hardware, so I finally could run it. I can’t say bad word about these IDEs, I really enjoyed using them - everything was there out of the box. (un)Fortunately, when I started my full time job, I needed to change it, as I did not want to spend money for a license. I really hate VSCode, so it was a simple choice - Neovim. That’s how my journey with environment started. In this post I will try to showcase its current state, and I really hope you can find something here to boost your day-to-day programming experience!
How does it look?
Terminal (iTerm2)
Nothing fancy to say about this, it is just fun, simple and fast. I once tried a terminal written in Electron, never again. I think I have only opacity configured, to see my call wallpaper while coding.
Zsh (Shell)
I use oh-my-zsh
plugin, for a better experience with auto-completion & syntax highlighting.
Also git plugin and vi mode come in handy.
# dotfiles/.zshrc
export ZSH="$HOME/.oh-my-zsh"
ZSH_THEME="robbyrussell"
plugins=(git)
plugins+=(zsh-vi-mode)
source $ZSH/oh-my-zsh.sh
source /opt/homebrew/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
Include Zellij config and start it by default
bashexport ZELLIJ_CONFIG_DIR="$DOTFILES/zellij/"
source ~/dotfiles/zellij/.zellij.conf
if [[ -z "$ZELLIJ" ]]; then
zellij attach default
fi
Besides that, I’ve got quite a few aliases and exports, but I am not going to bore yall with that here.
Okay, maybe just these three as these are stupid but cool.
bashalias b="cd .."
alias bb="cd ../.."
alias bbb="cd ../../.."
I really recommend to setup some aliases, they save so much time in a long run. For example, instead of running
flutter run --flavor=development --target=lib/main_development.dart"
every single time,
you can just alias it to rundev
Zellij (Terminal Workspace)
Zellij is a extremely cool terminal workspace (terminal multiplexer). I used tmux
for a short
while but it did not really clicked with me. On the other hand, I can’t live without Zellij.
It allows you to have different sessions - between which you can switch easily in runtime -, retains them and makes it really convinient to work with tabs and panes.
The default keybinds were enough for me - I needed to change only one, but I can not recall which one was it to be honest.
Neovim (Code Editor)
Now, fun begins.
I won’t go through my entire nvim config, but I will share the essentials.
Plugins:
bash# full list available in file
# dotfiles/nvim/lua/plugins/plugins_list.lua
# Basically everything you need for flutter
'akinsho/flutter-tools.nvim',
# Language Server Protocol configurator
'neovim/nvim-lspconfig'
# Powerful Fuzzy Finder
'nvim-telescope/telescope.nvim',
# Comment out blocks of code
'numToStr/Comment.nvim',
# Displays float with info who commited given line
'rhysd/git-messenger.vim'
# Completion
'hrsh7th/nvim-cmp'
'hrsh7th/cmp-nvim-lsp'
# Display errors/warnings/info
"folke/trouble.nvim"
# Snippets
"L3MON4D3/LuaSnip"
# Togglable file tree
'nvim-tree/nvim-tree.lua'
# Togglable terminal
'akinsho/toggleterm.nvim'
# Opens floating window with curent buffer, to investigate some
# code without losing contest
"carbon-steel/detour.nvim"
# Debugger and its UI
'mfussenegger/nvim-dap'
'rcarriga/nvim-dap-ui'
Setting up support for flutter is as easy as this:
crequire "flutter-tools".setup {
lsp = {
color = {
enabled = true,
},
settings = {
showTodos = true,
completeFunctionCalls = true,
// enable analysis in flutter repo
analysisExcludedFolders = {},
}
}
}
Flutter codebase I work on daily is not that small - over 50k lines of code and constantly increasing. At the beginning, it was not easy to navigate through it quickly in Neovim but once I got more fluent in Telescope it is great. Nvim-tree comes in handy quite often too. It is extremely cool, that I can find a file/directory and its place in the file tree using vim shortcuts.
Debugging was also a challenge, a big one actually. At first, I was just opening VSCode for a second,
to debug something, as it have better support out of the box. After some time, I properly configured
nvim-dap-ui
, which is a UI for the debugger, and it was a game-changer. Finally I could uninstall VSCode, as its debugger is not even close as good.
First required thing is to configure flutter adapter and setup its configurations:
c// dotfiles/nvim/lua/plugins/nvim-dap.lua
-- Exported in shell config file
local dart_path = os.getenv("DART_PATH");
local flutter_path = os.getenv("FLUTTER_PATH");
dap.configurations.dart = {
{
type = "flutter",
request = "launch",
name = "Launch Flutter | Development",
dartSdkPath = dart_path,
flutterSdkPath = flutter_path,
program = "${workspaceFolder}/lib/main_development.dart",
cwd = "${workspaceFolder}",
toolArgs = function()
local default_flutter_device = os.getenv("DEFAULT_FLUTTER_DEVICE");
local selected_device = default_flutter_device;
if not selected_device then
selected_device = get_device();
end
return { "-d", selected_device, "--flavor", "development" };
end
},
-- Add other configs here, like production, staging etc.
get_device()
is my helper function, that allow me to choose on which device to run.
It runs flutter devices
underneath, analyzes the output and displays a floating window
with possible options. Unfortunately - as we know -, running flutter devices is quite
slow, and it blocks nvim for that time. Setting DEFAULT_FLUTTER_DEVICE env in
shell config may be helpful, unless you are changing yours simulators very often.
// dotfiles/nvim/lua/plugins/nvim-dap.lua
local get_device = function()
local co = coroutine.running()
local output = ""
local exit_code = 0
local on_stdout = function(_, data)
if data then
for _, line in ipairs(data) do
if string.match(line, '•') then
output = output .. line .. '\n'
end
end
end
end
vim.print("Running flutter devices...")
local job_id = vim.fn.jobstart('flutter devices', {
stdout_buffered = true,
on_stdout = on_stdout,
on_stderr = on_stdout,
on_exit = function(_, code, _)
exit_code = code
end,
})
vim.fn.jobwait({ job_id })
if exit_code ~= 0 then
return vim.print("Failed to retrieve device")
end
vim.ui.select(vim.fn.split(output, '\n'), {
prompt = "Select Device",
telescope = require 'telescope.themes'.get_cursor(),
}, function(selected)
coroutine.resume(co, selected)
end
);
local selected_device = coroutine.yield()
return vim.fn.trim(vim.fn.split(selected_device, '•')[2])
end
I have a keymap for starting debugger set to <leader>dc
(where leader is space for me),
so if I want to start debugging, I just type space dc, select which configuration (development/mock/…), then I select (unless default specified, which is usually the case) which one of available devices to use, and Voila!
Debugging starts, I can toggle debugging ui with <leader>dut
and start to play with it!
Keymaps are not restricted to plugins functions only, thus you can add some cool helpers, such as:
cmap('n', '<leader>df', ':!dart format -l 120 %<CR>')
The line above, allows you to write space df
to run the customized formatting on current file
I’ve got quite a few keymaps, but the most essential are those:
bash# dotfiles/nvim/lua/keymappings.lua
map('n', '<space>gl', vim.diagnostic.open_float)
map('n', 'gd', vim.lsp.buf.definition, opts)
map('n', 'K', vim.lsp.buf.hover, opts)
map('n', '<space>rn', vim.lsp.buf.rename, opts)
map({ 'n', 'v' }, '<space>a', vim.lsp.buf.code_action, opts)
map('n', '<leader>e', ':NvimTreeToggle<CR>')
map("n", "<leader>xw", function() trouble.open("workspace_diagnostics") end)
map("n", "gr", function() trouble.open("lsp_references") end)
map('n', '<leader>tr', ':ToggleTerm<CR>')
map('n', '<leader>dd', ":Detour<cr>")
map('n', '<leader>dbp', ':DapToggleBreakpoint<CR>')
map('n', '<leader>dtr', ':DapToggleRepl<CR>')
map('n', '<leader>dc', ':DapContinue<CR>')
map('n', '<leader>ff', function() telescope.find_files() end)
map('n', '<leader>fg', function() telescope.live_grep() end)
map('n', '<leader>fb', function() telescope.buffers() end)
map('n', '<leader>dut', function() dapui.toggle() end)
map('n', '<leader>duf', function() dapui.float_element() end)
That is basically all! Of course, there are much more things in my config, but I believe it is a cool sneak peak. I hope you found something interesting to add to your config, if you aready use neovim, if not, I hope you will consider it!
The entire config of my dotfiles can be found on my Github - here