Dotfiles for 2024
Intro
I’ve been on this kick to update my dotfiles so that I can also revamp my dotfiles that I use for work as well in an effort to optimize my workflow. With this I’m also making the switch from Vim to Neovim as my default text editor in order to really force myself to use and learn vim motions more. I’m not a software engineer, so my typical usage with Vim has just been to edit configuration files, write bash scripts or perform small edits. I’ve been using VSCode a lot for writing configuration yamls and anything remotely close to programming. So I’m going cold turkey and cutting VSCode out of my repertoire.
So let’s go over some goals I have for iterating over my existing dotfiles.
- Creating a new branch for the update before merging to master.
- Adding new configurations for neovim and tmux.
But before we go all in on what I’m adding, let’s go over a bit on how my dotfiles are structured for modularity.
The basic structure
It was a few years ago that I learned about GNU stow which allows you to create and manage symlinks. With this we can manage symlinks appearing in a given location following the structured format of another.
To illustrate this, let’s say we have the following directory structure in my home directory
drwx--x---+ 53 sinicide sinicide 4.0K Apr 14 18:37 .
drwxr-xr-x 3 root root 4.0K Sep 16 2021 ..
drwxr-xr-x 2 sinicide sinicide 4.0K Feb 17 20:41 bin
drwxr-xr-x 72 sinicide sinicide 4.0K Apr 14 15:55 .config
drwxr-xr-x 10 sinicide sinicide 4.0K Apr 5 07:41 .dotfiles
drwxr-xr--+ 30 sinicide sinicide 80K Apr 14 13:37 Downloads
lrwxrwxrwx 1 sinicide sinicide 29 Jan 23 2022 .gitconfig -> .dotfiles/personal/.gitconfig
drwxr-xr-x 3 sinicide sinicide 4.0K Aug 11 2023 Pictures
drwxr-xr-x 2 sinicide sinicide 4.0K Dec 7 16:52 .ssh
lrwxrwxrwx 1 sinicide sinicide 20 Mar 30 00:28 .tmux -> .dotfiles/tmux/.tmux
lrwxrwxrwx 1 sinicide sinicide 25 Mar 30 00:28 .tmux.conf -> .dotfiles/tmux/.tmux.conf
lrwxrwxrwx 1 sinicide sinicide 18 Mar 29 14:42 .vim -> .dotfiles/vim/.vim
-rw------- 1 sinicide sinicide 32K Mar 29 15:39 .viminfo
lrwxrwxrwx 1 sinicide sinicide 20 Mar 29 14:42 .vimrc -> .dotfiles/vim/.vimrc
-rw------- 1 sinicide sinicide 21K Oct 17 2021 .zhistory
-rw-r--r-- 1 sinicide sinicide 87 Mar 13 00:26 .zlogin
lrwxrwxrwx 1 sinicide sinicide 21 Mar 29 14:42 .zshenv -> .dotfiles/zsh/.zshenv
-rw------- 1 sinicide sinicide 349K Apr 14 18:37 .zsh_history
lrwxrwxrwx 1 sinicide sinicide 20 Mar 29 14:42 .zshrc -> .dotfiles/zsh/.zshrc
From this you can see how the symlinks are mapped to the files within my .dotfiles
directory, I’ve split up configurations into their own specific directories, such as tmux
, zsh
, and vim
So the base of these subdirectories act as the base home directory and how I structure the files and subdirectories within this maps back to the home directory.
/home/sinicide/ = /home/sinicide/.dotfiles/zsh/
This makes it very modular and if I want to add new configs that are specific to an application I can just create a new directory for it. So for example, I’ll be adding Neovim configs which will live in
/home/sinicide/.dotfiles/nvim/
Neovim configs are stored within a user’s .config
directory. So we’ll structure them the same way within our sub directory and when we run the stow command it’ll map it accordingly in our home directory.
/home/sinicide/.dotfiles/nvim/.config/nvim/
How stow
works is that it can take a target directory, which is where stow
should create the symlinks and then it takes a given directory that it maps.
cd ~/.dotfiles
stow -t ~ nvim
So the above command here will take our Home directory as a target and will map our nvim
directory. So when I navigate into the ~/.config
directory I’ll see the following symlink.
nvim -> ../.dotfiles/nvim/.config/nvim
Neovim
So one of my biggest undertaking here is switching to Neovim, with this comes a slew of possible plugins and key remaps. I had the following goals in mind to help make my choices.
- Keep key remaps to a minimum to start
- Choose plugins that are useful instead of just providing a visual cool factor
With that said, let’s take a look at my nvim config directory and how I’ve structure it.
[sinicide 4096] .
├── [sinicide 42] init.lua
├── [sinicide 2123] lazy-lock.json
├── [sinicide 4096] lua
│ ├── [sinicide 4096] core
│ │ ├── [sinicide 42] init.lua
│ │ ├── [sinicide 591] remap.lua
│ │ └── [sinicide 915] set.lua
│ ├── [sinicide 4096] packagemanager
│ │ └── [sinicide 494] init.lua
│ └── [sinicide 4096] plugins
│ ├── [sinicide 902] conform.lua
│ ├── [sinicide 4591] lsp-config.lua
│ ├── [sinicide 211] markdown-preview.lua
│ ├── [sinicide 955] mason.lua
│ ├── [sinicide 281] monokai-pro.lua
│ ├── [sinicide 1252] nvim-cmp.lua
│ ├── [sinicide 954] nvim-lint.lua
│ ├── [sinicide 891] telescope.lua
│ ├── [sinicide 906] treesitter.lua
│ ├── [sinicide 91] undotree.lua
│ ├── [sinicide 111] vim-paper.lua
│ └── [sinicide 528] vim-tmux-navigator.lua
└── [sinicide 4096] undodir
At the core of it, we have the init.lua
inside nvim, which starts off all the requirements. This is a pretty simple file that only contains 2 things for my setup
require("core")
require("packagemanager")
The required just points to non-plugin configurations, which are essentially the vim configurations and remaps. The other one just points to lazy.nvim plugin manager. Now I do want to note that the docs recommends structuring the lazy config within the top level init.lua
, plugins.lua
or plugins/init.lua
However I structured mine within a directory I called packagemanager
and called it from the init.lua
just to help me understand the structure of loading things in neovim.
nvim core configs
So the core
directory here contains 2 main things, vim default configs and key remappings, the init.lua
just points to 2 these two files. I won’t go into detail on the vim configs, these are primarily carried over from my original vim dotfiles and are less interesting I think.
As for the key remaps, I do want to touch base on them since I’m trying to use VIM motions more and more so I’ve kept my remaps to a minimum for now to ensure that I learn the remaps as muscle memory.
I’ve taken these remaps from ThePrimeagen I did a little testing here and there to determine their benefits for my workflow. So let’s go over the ones I’ve chosen.
-- page up/down, keeping cursor centered
vim.keymap.set("n", "<C-d>", "<C-d>zz")
vim.keymap.set("n", "<C-u>", "<C-u>zz")
I like the idea of keeping focus towards the center of the screen, so basically this remaps combines the default page up/down and then centers the cursor in the middle of the screen.
-- search, but keep item middle of screen
vim.keymap.set("n", "n", "nzzzv")
vim.keymap.set("n", "N", "Nzzzv")
The following keeps this same thing in mind, which is to keep the search pattern in center focus.
-- yank/paste to/from system clipboard
vim.keymap.set({"n", "v"}, "<leader>y", [["+y]])
vim.keymap.set("n", "<leader>p", [["+p]])
-- delete into blackhole register
vim.keymap.set({"n", "v"}, "<leader>d", [["_d]])
The above here should keep 2 separate copy buffers, one for vim and one for system clipboard.
-- shift highlighted line up/down
vim.keymap.set("v", "J", ":m '>+1<CR>gv=gv")
vim.keymap.set("v", "K", ":m '<-2<CR>gv=gv")
Finally the above remap seems super nice for being able to highlight a block of text and shifting it up or down without having to yank/paste.
The plugins
Let’s talk plugins, I’m keeping my approach with this to the absolute minimal for now, so you can see that I’m not installing a filemanager like NERDTree and just sticking with the default netrw
while it ain’t pretty, it does get the job done. I’m also not doing much in the way of customizing the bottom bar either, no lualine or anything as it’s really just for aesthetics.
I’m using telescope to fuzzy find my way around files, undotree for viewing previous iterations of a file, this is particularly helpful for configuration orientated files which I find myself working with often in my workflow. vim-tmux-navigator is for some nice keybindings for navigating between vim and tmux. Markdown Preview for being able to view what my markdown looks like rendered in a web browser, this is useful in particular for this blog as all content is written in Markdown, previously I’ve been using something similar in VSCode, so this is totally a nice to have.
Finally we have 2 themes, monokai-pro is my default theme, but I have a nice vim-paper one for light mode if I ever need to do screensharing/presenting that way vim in my terminal is actually readable.
The rest are for LSPs, linting and formatting, with the use of treesitter, mason for managing LSPs, and conform for managing Linters/Formatters. Mason is pretty awesome for being able to manage installing LSPs, Linters, and Formatters, however the only thing it can auto install it seems are LSPs. Hence why we have Conform which helps to automating the installation of Linters and Formatters.
With that my Neovim setup is complete.
Tmux Configs
My tmux configs are split between 2 main components, the .tmux.conf
configuration file and a custom theme.
Let’s start by talking about the configuration file as this primarily contains my key bindings. Like most people, I’ve rebind the leader prefix from the default C-b
to C-a
Most important rebinds I have are, changing the pane navigation to VIM movements ala hjkl
and the splits. I’ve opted to use |
for horiztonal splits and -
for vertical splits, simply because they look like their respective cuts as opposed to the defaults, which are %
and "
, while it wouldn’t be difficult to learn them over time, my visual splits are just easier for me to remember.
# rebind navigation to VIM keys
bind -r h select-pane -L # move left
bind -r j select-pane -D # move down
bind -r k select-pane -U # move up
bind -r l select-pane -R # move right
# rebind splits
unbind |
unbind -
unbind %
unbind '"'
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"
Like most, I also change the default numbering from starting 0 to 1. The reasoning behind this is that the 1 key is just closer on the left hand, so it’s a bit more optimized for session switching, as opposed to using 2 hands to hit the prefix keys + session number.
I also have the configurations in place for loading the Tmux Plugin Manager, however I’m not currently using any plugins at the moment. I was testing out the vim-tmux-navigator, but have decided against the keybinds that it default for moving windows, which is to not use leader key prefix and only relay on <ctrl> + <key>
. Personally I like keeping this leader key prefix style specific for tmux so that I have it separate in my mind. With the keybindings it also removes the clear line key mapping in the terminal, so that was the main motivator for me to remove this plugin. I don’t need to necessarily use tmux all the time, so I don’t need to keep 2 different key bindings in my brain for the same action.
Finally we have my custom theme, I looked at a bunch of the other themes out there for tmux and honestly I didn’t care for what they offered. So of course I had to create my own. So what makes my theme different? Mostly the removal of hostname + time as these are unnecessary give modern desktops generally show a clock somewhere.
So I thought about the type of information that would be important to me with managing multiple terminal windows. This essentially boils down to,
- What Session am I on?
- What Window am I on?
- How many panes are in a given window?
- Am I zoomed into a pane?
So I’ve customized the theme with these in mind and have removed the necessary things.
# left status bar
set-option -g status-left-style 'bg=#19181a'
set-option -g status-left ' '
# right status bar
set-option -g status-right-length 40
set-option -g status-right-style 'bg=#19181a,fg=#a9dc76'
set-option -g status-right '#{?client_prefix,#[bg=#fc9868]#[fg=#19184a],} [#{=30:session_name}] '
# windows status
set-option -g window-status-format '#{window_index}#(echo ":")#{=20:#{b:window_name}}#{window_flags}'
# window current/active status
set-option -g window-status-current-format '#{?client_prefix,#[bg=#fc9868]#[fg=#19184a],}#{window_index}:#(echo "#{b:window_name}")#{?window_zoomed_flag,[Z],[#{window_panes}]}#{?mouse,[M],}'
So you can see that this is split into 3 sections, a left side, right side and window-status, gets pushed to the left side. A nice little addition I added was the Z
indicator whenever I’m zoomed into a pane.
Conclusion
With these new changes implemented, I can git pull
and update my existing dotfiles and stow the new changes all at once or one at a time. This becomes quite flexible when I want to integrate a personal or work folder given whether I’m using my work computer or my personal computer.
All and all dotfiles are great and no two need be alike. I quite enjoy seeing how people setup their own dotfiles and take inspiration and snippets where it makes sense for my own workflow.