On recent weeks I’ve had some time to scratch my own itch on matters related to tools I use daily on my computer, namely the desktop / window manager and my text editor of choice.
This post is a summary of what I tried, how it worked out and my short and medium-term plans related to them.
Desktop / WM
On the desktop / window manager front I’ve been using Cinnamon on Debian and Ubuntu systems since Gnome 3 was published (I never liked version 3, so I decided to move to something similar to Gnome 2, including the keyboard shortcuts).
In fact I’ve never been a fan of Desktop environments, before Gnome I used OpenBox and IceWM because they where a lot faster than desktop systems on my hardware at the time and I was using them only to place one or two windows on multiple workspaces using mainly the keyboard for my interactions (well, except for the web browsers and the image manipulation programs).
Although I was comfortable using Cinnamon, some years ago I tried to move to i3, a tilling window manager for X11 that looked like a good choice for me, but I didn’t have much time to play with it and never used it enough to make me productive with it (I didn’t prepare a complete configuration nor had enough time to learn the new shortcuts, so I went back to Cinnamon and never tried again).
Anyway, some weeks ago I updated my work machine OS (it was using Ubuntu 22.04 LTS and I updated it to the 24.04 LTS version) and the Cinnamon systray applet stopped working as it used to do (in fact I still have to restart Cinnamon after starting a session to make it work) and, as I had some time, I decided to try a tilling window manager again, but now I decided to go for SwayWM, as it uses Wayland instead of X11.
Sway configuration
On my ~/.config/sway/config
I tuned some things:
- Set fuzzel as the application launcher.
- Installed manually the shikane application and created a configuration to be
executed always when
sway
is started / reloaded (I adjusted my configuration withwdisplays
and usedshikanectl
to save it). - Added support for running the
xdg-desktop-portal-wlr
service. - Enabled the
swayidle
command to lock the screen after some time of inactivity. - Adjusted the keyboard to use the
es
key map - Added some keybindings to make my life easier, including the use of
grimm
andswappy
to take screenshots - Configured
waybar
as the environment bar. Added a shell script to start applications when
sway
is started (it usesswaymsg
to execute background commands and thei3toolwait
script to wait for the#!/bin/sh # VARIABLES CHROMIUM_LOCAL_STATE="$HOME/.config/google-chrome/Local State" I3_TOOLWAIT="$HOME/.config/sway/scripts/i3-toolwait" # Functions chromium_profile_dir() { jq -r ".profile.info_cache|to_entries|map({(.value.name): .key})|add|.\"$1\" // \"\"" "$CHROMIUM_LOCAL_STATE" } # MAIN IGZ_PROFILE_DIR="$(chromium_profile_dir "sergio.talens@intelygenz.com")" OURO_PROFILE_DIR="$(chromium_profile_dir "sergio.talens@nxr.global")" PERSONAL_PROFILE_DIR="$(chromium_profile_dir "stalens@gmail.com")" # Common programs swaymsg "exec nextcloud --background" swaymsg "exec nm-applet" # Run spotify on the first workspace (it is mapped to the laptop screen) swaymsg -q "workspace 1" ${I3_TOOLWAIT} "spotify" # Run tmux on the swaymsg -q "workspace 2" ${I3_TOOLWAIT} -- foot tmux a -dt sto wp_num="3" if [ "$OURO_PROFILE_DIR" ]; then swaymsg -q "workspace $wp_num" ${I3_TOOLWAIT} -m ouro-browser -- google-chrome --profile-directory="$OURO_PROFILE_DIR" wp_num="$((wp_num+1))" fi if [ "$IGZ_PROFILE_DIR" ]; then swaymsg -q "workspace $wp_num" ${I3_TOOLWAIT} -m igz-browser -- google-chrome --profile-directory="$IGZ_PROFILE_DIR" wp_num="$((wp_num+1))" fi if [ "$PERSONAL_PROFILE_DIR" ]; then swaymsg -q "workspace $wp_num" ${I3_TOOLWAIT} -m personal-browser -- google-chrome --profile-directory="$PERSONAL_PROFILE_DIR" wp_num="$((wp_num+1))" fi # Open the browser without setting the profile directory if none was found if [ "$wp_num" = "3" ]; then swaymsg -q "workspace $wp_num" ${I3_TOOLWAIT} google-chrome wp_num="$((wp_num+1))" fi swaymsg -q "workspace $wp_num" ${I3_TOOLWAIT} evolution wp_num="$((wp_num+1))" swaymsg -q "workspace $wp_num" ${I3_TOOLWAIT} slack wp_num="$((wp_num+1))" # Open a private browser and a console in the last workspace swaymsg -q "workspace $wp_num" ${I3_TOOLWAIT} -- google-chrome --incognito ${I3_TOOLWAIT} foot # Go back to the second workspace for keepassxc swaymsg "workspace 2" ${I3_TOOLWAIT} keepassxc
Conclusion
After using Sway for some days I can confirm that it is a good choice for me, but some of the components needed to make it work as I want are too new and not available on the Ubuntu 24.04 LTS repositories, so I decided to go back to Cinnamon and try Sway again in the future, although I added more workspaces to my setup (now they are only available on the main monitor, the laptop screen is fixed while there is a big monitor connected), added some additional keyboard shortcuts and installed or updated some applets.
Text editor
When I started using Linux many years ago I used vi
/vim
and emacs
as my text editors (vi
for plain text and
emacs
for programming and editing HTML/XML), but eventually I moved to vim
as my main text editor and I’ve been
using it since (well, I moved to neovim
some time ago, although I kept my old vim
configuration).
To be fair I’m not as expert as I could be with vim
, but I’m productive with it and it has many plugins that make my
life easier on my machines, while keeping my ability to edit text and configurations on any system that has a vi
compatible editor installed.
For work reasons I tried to use Visual Studio Code last year, but I’ve never really
liked it and almost everything I do with it I can do with neovim
(i. e. I even use copilot
with it). Besides, I’m a
heavy terminal user (I use tmux
locally and via ssh
) and I like to be able to use my text editor on my shell
sessions, and code
does not work like that.
The only annoying thing about vim
/neovim
is its configuration (well, the problem is that I have a very old one and
probably should spend some time fixing and updating it), but, as I said, it’s been working well for me for a long time,
so I never really had the motivation to do it.
Anyway, after finishing my desktop tests I saw that I had the Helix editor installed for
some time but I never tried it, so I decided to give it a try and see if it could be a good replacement for neovim
on
my environments (the only drawback is that as it is not vi
compatible, I would need to switch back to vi
mode when
working on remote systems, but I guess I could live with that).
I ran the helix
tutorial and I liked it, so I decided to configure and install the
Language Servers I can probably take
advantage of on my daily work on my personal and work machines and see how it works.
Language server installations
A lot of manual installations are needed to get the language servers working what I did on my machines is more or less the following:
# AWK
sudo npm i -g 'awk-language-server@>=0.5.2'
# BASH
sudo apt-get install shellcheck shfmt
sudo npm i -g bash-language-server
# C/C++
sudo apt-get install clangd
# CSS, HTML, ESLint, JSON, SCS
sudo npm i -g vscode-langservers-extracted
# Docker
sudo npm install -g dockerfile-language-server-nodejs
# Docker compose
sudo npm install -g @microsoft/compose-language-service
# Helm
app="helm_ls_linux_amd64"
url="$(
curl -s https://api.github.com/repos/mrjosh/helm-ls/releases/latest |
jq -r ".assets[] | select(.name == \"$app\") | .browser_download_url"
)"
curl -L "$url" --output /tmp/helm_ls
sudo install /tmp/helm_ls /usr/local/bin
rm /tmp/helm_ls
# Markdown
app="marksman-linux-x64"
url="$(
curl -s https://api.github.com/repos/artempyanykh/marksman/releases/latest |
jq -r ".assets[] | select(.name == \"$app\") | .browser_download_url"
)"
curl -L "$url" --output /tmp/marksman
sudo install /tmp/marksman /usr/local/bin
rm /tmp/marksman
# Python
sudo npm i -g pyright
# Rust
rustup component add rust-analyzer
# SQL
sudo npm i -g sql-language-server
# Terraform
sudo apt-get install terraform-ls
# TOML
cargo install taplo-cli --locked --features lsp
# YAML
sudo npm install --global yaml-language-server
# JavaScript, TypeScript
sudo npm install -g typescript-language-server typescript
sudo npm install -g --save-dev --save-exact @biomejs/biome
Helix configuration
The helix
configuration is done on a couple of toml
files that are placed on the ~/.config/helix
directory,
the config.toml
file I used is this one:
theme = "solarized_light"
[editor]
line-number = "relative"
mouse = false
[editor.statusline]
left = ["mode", "spinner"]
center = ["file-name"]
right = ["diagnostics", "selections", "position", "file-encoding", "file-line-ending", "file-type"]
separator = "│"
mode.normal = "NORMAL"
mode.insert = "INSERT"
mode.select = "SELECT"
[editor.cursor-shape]
insert = "bar"
normal = "block"
select = "underline"
[editor.file-picker]
hidden = false
[editor.whitespace]
render = "all"
[editor.indent-guides]
render = true
character = "╎" # Some characters that work well: "▏", "┆", "┊", "⸽"
skip-levels = 1
And to configure the language servers I used the following language-servers.toml
file:
[[language]]
name = "go"
auto-format = true
formatter = { command = "goimports" }
[[language]]
name = "javascript"
language-servers = [
"typescript-language-server", # optional
"vscode-eslint-language-server",
]
[language-server.rust-analyzer.config.check]
command = "clippy"
[language-server.sql-language-server]
command = "sql-language-server"
args = ["up", "--method", "stdio"]
[[language]]
name = "sql"
language-servers = [ "sql-language-server" ]
[[language]]
name = "hcl"
language-servers = [ "terraform-ls" ]
language-id = "terraform"
[[language]]
name = "tfvars"
language-servers = [ "terraform-ls" ]
language-id = "terraform-vars"
[language-server.terraform-ls]
command = "terraform-ls"
args = ["serve"]
[[language]]
name = "toml"
formatter = { command = "taplo", args = ["fmt", "-"] }
[[language]]
name = "typescript"
language-servers = [
"typescript-language-server",
"vscode-eslint-language-server",
]
Neovim configuration
After a little while I noticed that I was going to need some time to get used to helix
and the most interesting thing
for me was the easy configuration and the language server integrations, but as I am already comfortable with neovim
and just had installed the language server support tools on my machines I just need to
configure them for neovim and I can keep using it for a while.
As I said my configuration is old, to configure neovim
I have the following init.vim
file on my ~/.config/nvim
folder:
set runtimepath^=~/.vim runtimepath+=~/.vim/after
let &packpath=&runtimepath
source ~/.vim/vimrc
" load lua configuration
lua require('config')
With that configuration I keep my old vimrc
(it is a little bit messy, but it works) and I use a lua
configuration
file for the language servers and some additional neovim
plugins on the ~/.config/nvim/lua/config.lua
file:
-- -----------------------
-- BEG: LSP Configurations
-- -----------------------
-- AWS (awk_ls)
require'lspconfig'.awk_ls.setup{}
-- Bash (bashls)
require'lspconfig'.bashls.setup{}
-- C/C++ (clangd)
require'lspconfig'.clangd.setup{}
-- CSS (cssls)
require'lspconfig'.cssls.setup{}
-- Docker (dockerls)
require'lspconfig'.dockerls.setup{}
-- Docker Compose
require'lspconfig'.docker_compose_language_service.setup{}
-- Golang (gopls)
require'lspconfig'.gopls.setup{}
-- Helm (helm_ls)
require'lspconfig'.helm_ls.setup{}
-- Markdown
require'lspconfig'.marksman.setup{}
-- Python (pyright)
require'lspconfig'.pyright.setup{}
-- Rust (rust-analyzer)
require'lspconfig'.rust_analyzer.setup{}
-- SQL (sqlls)
require'lspconfig'.sqlls.setup{}
-- Terraform (terraformls)
require'lspconfig'.terraformls.setup{}
-- TOML (taplo)
require'lspconfig'.taplo.setup{}
-- Typescript (ts_ls)
require'lspconfig'.ts_ls.setup{}
-- YAML (yamlls)
require'lspconfig'.yamlls.setup{
settings = {
yaml = {
customTags = { "!reference sequence" }
}
}
}
-- -----------------------
-- END: LSP Configurations
-- -----------------------
-- ---------------------------------
-- BEG: Autocompletion configuration
-- ---------------------------------
-- Ref: https://github.com/neovim/nvim-lspconfig/wiki/Autocompletion
--
-- Pre requisites:
--
-- # Packer
-- git clone --depth 1 https://github.com/wbthomason/packer.nvim \
-- ~/.local/share/nvim/site/pack/packer/start/packer.nvim
--
-- # Start nvim and run :PackerSync or :PackerUpdate
-- ---------------------------------
local use = require('packer').use
require('packer').startup(function()
use 'wbthomason/packer.nvim' -- Packer, useful to avoid removing it with PackerSync / PackerUpdate
use 'neovim/nvim-lspconfig' -- Collection of configurations for built-in LSP client
use 'hrsh7th/nvim-cmp' -- Autocompletion plugin
use 'hrsh7th/cmp-nvim-lsp' -- LSP source for nvim-cmp
use 'saadparwaiz1/cmp_luasnip' -- Snippets source for nvim-cmp
use 'L3MON4D3/LuaSnip' -- Snippets plugin
end)
-- Add additional capabilities supported by nvim-cmp
local capabilities = require("cmp_nvim_lsp").default_capabilities()
local lspconfig = require('lspconfig')
-- Enable some language servers with the additional completion capabilities offered by nvim-cmp
local servers = { 'clangd', 'rust_analyzer', 'pyright', 'ts_ls' }
for _, lsp in ipairs(servers) do
lspconfig[lsp].setup {
-- on_attach = my_custom_on_attach,
capabilities = capabilities,
}
end
-- luasnip setup
local luasnip = require 'luasnip'
-- nvim-cmp setup
local cmp = require 'cmp'
cmp.setup {
snippet = {
expand = function(args)
luasnip.lsp_expand(args.body)
end,
},
mapping = cmp.mapping.preset.insert({
['<C-u>'] = cmp.mapping.scroll_docs(-4), -- Up
['<C-d>'] = cmp.mapping.scroll_docs(4), -- Down
-- C-b (back) C-f (forward) for snippet placeholder navigation.
['<C-Space>'] = cmp.mapping.complete(),
['<CR>'] = cmp.mapping.confirm {
behavior = cmp.ConfirmBehavior.Replace,
select = true,
},
['<Tab>'] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_next_item()
elseif luasnip.expand_or_jumpable() then
luasnip.expand_or_jump()
else
fallback()
end
end, { 'i', 's' }),
['<S-Tab>'] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_prev_item()
elseif luasnip.jumpable(-1) then
luasnip.jump(-1)
else
fallback()
end
end, { 'i', 's' }),
}),
sources = {
{ name = 'nvim_lsp' },
{ name = 'luasnip' },
},
}
-- ---------------------------------
-- END: Autocompletion configuration
-- ---------------------------------
Conclusion
I guess I’ll keep helix
installed and try it again on some of my personal projects to see if I can get used to it,
but for now I’ll stay with neovim
as my main text editor and learn the shortcuts to use it with the language servers.