Updates

Amolith created

Change summary

dot_Xresources.tmpl                              |   89 
dot_config/alacritty/alacritty.toml.tmpl         |    2 
dot_config/doom/config.el.tmpl                   |    5 
dot_config/doom/extra/encrypted_mu4e.el.age      |  386 ++--
dot_config/encrypted_ytsubscriptions.txt.age     |   51 
dot_config/git/config.tmpl                       |    3 
dot_config/isync/encrypted_mbsyncrc.age          |   88 
dot_config/msmtp/encrypted_config.age            |  118 
dot_config/nvim/init.lua                         |   27 
dot_config/nvim/init.vim.tmpl                    |  142 -
dot_config/nvim/lua/base/1-options.lua           |   93 +
dot_config/nvim/lua/base/2-lazy.lua              |   75 
dot_config/nvim/lua/base/3-autocmds.lua          |  281 +++
dot_config/nvim/lua/base/4-mappings.lua          | 1616 ++++++++++++++++++
dot_config/nvim/lua/base/health.lua              |  270 +++
dot_config/nvim/lua/base/icons/nerd_font.lua     |   73 
dot_config/nvim/lua/base/icons/text.lua          |   48 
dot_config/nvim/lua/base/utils/init.lua          |  307 +++
dot_config/nvim/lua/base/utils/lsp.lua           |  189 ++
dot_config/nvim/lua/base/utils/ui.lua            |  321 +++
dot_config/nvim/lua/plugins/1-base-behaviors.lua |  709 +++++++
dot_config/nvim/lua/plugins/2-ui.lua             |  709 +++++++
dot_config/nvim/lua/plugins/3-dev-core.lua       |  510 +++++
dot_config/nvim/lua/plugins/4-dev.lua            |  901 ++++++++++
dot_config/polybar/config.ini.tmpl               |    8 
dot_config/private_zathura/zathurarc.tmpl        |   78 
dot_config/zsh/private_dot_zshrc.tmpl            |   24 
dot_local/bin/executable_startup.tmpl            |    2 
dot_local/share/dark-mode.d/executable_swap.sh   |    5 
dot_local/share/light-mode.d/executable_swap.sh  |    5 
private_dot_ssh/encrypted_config.tmpl.age        |   81 
31 files changed, 6,609 insertions(+), 607 deletions(-)

Detailed changes

dot_Xresources.tmpl 🔗

@@ -6,85 +6,86 @@ Xft.hinting: 1
 Xft.antialias: 1
 Xft.rgba: rgb
 
-! Xresources has a base colour and a bright variant. The first block contains
-! the base colours and the second block contains their bright counterparts.
+! Xresources has a base colour and a bright variant. The first of each colour
+! pair is "dark" and the second is "bright". In the context of most shell
+! themes, "dark" variants seem to be used as foreground accents, while "light"
+! variants are highlights behind the default foreground colour.
 
-{{- if eq .theme_variant "dark" }}
-! Catppuccin Macchiato
-! https://github.com/catppuccin/xresources
+{{ if eq .theme_variant "dark" -}}
+! Everforest hard dark
+! https://github.com/sainnhe/everforest/blob/master/palette.md
 
-*background: #24273A
-*foreground: #CAD3F5
+*background: #272E33
+*foreground: #D3C6AA
 
 ! black
-*color0: #494D64
-*color8: #5B6078
+*color0: #1E2326
+*color8: #7A8478
 
 ! red
-*color1: #ED8796
-*color9: #ED8796
+*color1: #E67E80
+*color9: #E67E80
 
 ! green
-*color2: #A6DA95
-*color10: #A6DA95
+*color2: #A7C080
+*color10: #A7C080
 
 ! yellow
-*color3: #EED49F
-*color11: #EED49F
+*color3: #DBBC7F
+*color11: #DBBC7F
 
 ! blue
-*color4: #8AADF4
-*color12: #8AADF4
+*color4: #7FBBB3
+*color12: #7FBBB3
 
 ! magenta
-*color5: #F5BDE6
-*color13: #F5BDE6
+*color5: #D699B6
+*color13: #D699B6
 
 ! cyan
-*color6: #8BD5CA
-*color14: #8BD5CA
+*color6: #7FBBB3
+*color14: #7FBBB3
 
 ! white
-*color7: #B8C0E0
-*color15: #A5ADCB
+*color7: #D3C6AA
+*color15: #D3C6AA
 {{- end }}
-{{- if eq .theme_variant "light" }}
+{{ if eq .theme_variant "light" -}}
+! Everforest soft light
+! https://github.com/sainnhe/everforest/blob/master/palette.md
 
-! Catppuccin Latte
-! https://github.com/catppuccin/xresources
-
-*background: #EFF1F5
-*foreground: #4C4F69
+*background: #F3EAD3
+*foreground: #5C6A72
 
 ! black
 *color0: #5C5F77
-*color8: #6C6F85
+*color8: #939f91
 
 ! red
-*color1: #D20F39
-*color9: #D20F39
+*color1: #F85552
+*color9: #F85552
 
 ! green
-*color2: #40A02B
-*color10: #40A02B
+*color2: #8DA101
+*color10: #8DA101
 
 ! yellow
-*color3: #DF8E1D
-*color11: #DF8E1D
+*color3: #DFA000
+*color11: #DFA000
 
 ! blue
-*color4: #1E66F5
-*color12: #1E66F5
+*color4: #3A94C5
+*color12: #3A94C5
 
 ! magenta
-*color5: #EA76CB
-*color13: #EA76CB
+*color5: #DF69BA
+*color13: #DF69BA
 
 ! cyan
-*color6: #179299
-*color14: #179299
+*color6: #3A94C5
+*color14: #3A94C5
 
 ! white
-*color7: #ACB0BE
-*color15: #BCC0CC
+*color7: #708089
+*color15: #708089
 {{- end }}

dot_config/alacritty/alacritty.toml.tmpl 🔗

@@ -16,7 +16,7 @@ decorations = "None"
 padding = { x = 7, y = 5 }
 
 [font]
-normal = { family = "Berkeley Mono", style = "Regular" }
+normal = { family = "{{ .font }}", style = "Regular" }
 
 [cursor]
 style = { blinking = "On" }

dot_config/doom/config.el.tmpl 🔗

@@ -13,7 +13,8 @@
 (load-theme 'everforest-hard-dark t)
 {{- end }}
 {{- if eq .theme_variant "light"}}
-(load-theme 'everforest-soft-light t)
+;;(load-theme 'everforest-soft-light t)
+(load-theme 'doom-flatwhite t)
 {{- end }}
 
 (setq display-line-numbers-type t)
@@ -145,7 +146,7 @@
 
 (setq tidal-boot-script-path "/usr/share/x86_64-linux-ghc-9.0.2/tidal-1.7.10/BootTidal.hs")
 
-(global-tree-sitter-mode)
+(setq +tree-sitter-hl-enabled-modes '(not fundamental-mode))
 (add-hook 'tree-sitter-after-on-hook #'tree-sitter-hl-mode)
 
 (global-wakatime-mode)

dot_config/doom/extra/encrypted_mu4e.el.age 🔗

@@ -1,196 +1,194 @@
 -----BEGIN AGE ENCRYPTED FILE-----
-YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IEhBV2NqUSBBbkdSeDhT
-V3U5R1NiRlFHOERTKzJUODZ5WXQ1ZnFnWGhMeUZzTDV4R2JzeQpZS2I4UzhxOGxh
-TzF0STZoNVpFVkdnOGNGMldHQkZkY3ZBaE14SFhQQkI0Ci0+IHBpdi1wMjU2IHBE
-YXpSZyBBMG91U0RjeC9iRE9qZjQ5eWg2cnp3bFZ1SHUwbDhHL0NLa0NrL0xtNzZ1
-MwpWM2J4cU01ZlZnUldkajdwQjBUWlFDMHk4RzZYcXJ3R0lQd1IyWHRxZEVRCi0+
-IHJOYWN3Jy1ncmVhc2UgdVs6ID51UgprcVZoVlgrYlgyRWVSN1ppWmZsMDBaMDFJ
-bWdqTm16SGNvNHFRcDBFOE0yQmdORGVSNFVGUlV6ejc2SHRaSEhJCmxkcXdvaDly
-VFc1RkxaUWZ3bkRoV2V3YmtOcXhhNTRYV2tZawotLS0gblZqK1RBOUEySmp4ci9B
-SWxyZFlZNnUzLzZDWHdNM2ZzY0lZU09pZENEcwpIYQ5Hy0EvpPoEymKQdwB3Kzwo
-lvE3EYzgWpxHkpiemxwngKE0wB32swTQpiWM/7XRCqZ2lNqY38lzuthz0SyQtGOE
-elCIMfi6uB5MUQICai9MnDYx//wOoODJZDRKqhLfjdDb4oRRdZWxbevcGCqu1tr5
-x80fEzwTDAM68Rui+BGii72AAYmVBYZJBSBxAQfH/XjhKP2wYF6OZcVQ3tABosvz
-Ro6SkXZnhy10/PzNCIC+yYOHZsAGEMTD5mOdGTT6FyIumAOfTIo1LnC4OTOcrqpz
-5TiYvp78admTAQniIGVTk+p5yGH0LBQ7R6LA5RTVHQcOD17fKizOb0VqYQAhfXWI
-WT2ayCfyhY6YpY7o56/R89tSUW+i/QO9d6DuQjEC1t7qZ0+XGlF+139trGFXj83R
-wP0r++7SMh1DwwAlfRfWk51/SgGrtueSdVPd0M1OgLGL7M+PIMVqV4Iasunwry8X
-cDGJSzxPENbe3SUZbmfAXpERZcnm5DB8U8ZW1Tf8gbLCz3q/zHTenf05VHIc8e9Z
-82UxWKdC9uw9/ESe3nmoX2MeeI8OCXyNNe1TxohGlag/3QF5Auto4rxm4Q3Kt8un
-xZfEy2ujtnpuF1gdhhdPNGmJ0U/lhrUwh8huj7SuunYQDZ1dkH1vd2R5jKQRBzoo
-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
-ufHjZUKZaDwFEBf+UPmEcYZxUetDdYUla4OE2W9lJWiuZAkIYudUHLsWrCCnVSVM
-+zWl4SDbXAQcaBRE6pa5q9v0qKNpwezcnu5X4+sTXPN76J5C2NnmtKYHZdRk1b+T
-RteTlC1nC4CoXin47n/99hVORWpMYB6wlZFeNLqUJrH1KPLI7haCWU6z4CMR0ZGX
-X2V/mz1FZkIFCaqieJuAw3CurpLbkB/4AtUVv8HabHc/rR+Tog==
+YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IEhBV2NqUSBBLy84L0s1
+YlJKMm5YMGM4WFBJYUtOUFpCV0VOUitHbnRwL2JnaTNsRXRzNwpTbjRFU0lhRkxz
+REMwUzQyQWlpbFlDbXh4YmQ2QVdUQVRLYzB0UTlVT25ZCi0+IHBpdi1wMjU2IHBE
+YXpSZyBBekVjeDVqSDdtVTVZb29JYkpxTnVWaGdLdGYyenB2OTJGQjVUNGxXc3hq
+cApyejR1bUpocVBQOGF5N3cyOEtycGkwdjIxM0tHUVpnWXJObWxXVjR5bVlRCi0+
+IFB0bkAqMS1ncmVhc2UKZk9ST3RnTHlaRmlVWEEKLS0tIEZLaFAyendSeWI5dVQ4
+ZXE5dEdnbzdkZ3FiTyt1QXdEaGxOaWJRQUZNV2sKfeYlywwJPzsS2U8Y6IPh+/k+
+SPOzJiGHn0cXF1iyCM+r//bU0Pw0hE44I1H9IZBrdYwiN9UuYAJLZKaq8PervLl9
+va2wYQGHtTTsSrRA0pprQ+4NbHZI+Bx7Y70PZCLk3nK5Z+yZTVdXVXTKmJTCfiX6
+RZkc6kJJaX14aKsdDdIrjTmavZX1qTdTFYTJGXq3hVOwTdvMELimTLVPZkwAE9Ng
+Wt4ZzFqsFEEE1Cfau2wQgtYwHXqtywyMs68MyzZzTP5EWjetkX8SoSLPByTFiya6
+wIcHchM6Er7Dq3zAuCSQeGro+BcxTtsLqpn+UpBYc9Y4e4Q156DKdTtyB1jmBkLe
+izDXkt6St7mKdGH3odvpyi1wNuG8yroM7RqVeyF0tA/4s46Z1OPYbL6YbKkX8LM/
+A438QSXY+RtFt1dYvjzIHwh1lfnzE8gCNTWZjMQfiXYGZA/zdLiEus9PFuAsoZtf
+X55fGeNP8vPdV95cPt04vO+dXK1cNge8Nq5BUuZVY9ASOAfSVPCicdoE7lvk6bxX
+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
 -----END AGE ENCRYPTED FILE-----

dot_config/encrypted_ytsubscriptions.txt.age 🔗

@@ -1,29 +1,26 @@
 -----BEGIN AGE ENCRYPTED FILE-----
-YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IEhBV2NqUSBBbThZUVRt
-bmIzbk1KS0c5K3ZJZlNkcW16dkc4RjlrZG80QkVndlVydk5oOQpYQjZsd0ttMXB5
-WVVGQ1NSTCtyeE5US3ZtVGpzYnQ5MXZyaWxQTGFWS3VVCi0+IHBpdi1wMjU2IHBE
-YXpSZyBBbDRJQWlZTm1pRlJLN1N2SEVYc0prYkZUN0RSWkVjbnB0VGxBYU9mK2JX
-eApaeS9yWmpoQnFKaFhtQ2dPTjR1S0trQVJjZlhFanRBNUZadWJBUUpMSDUwCi0+
-IFNPMW4tZ3JlYXNlIDEqOickSiEgXCRGXyBVMkZXIH41ZUAkTApOZzlsRk02ZjdK
-QitVMXR2SXg2ZWtrcjVxSE5WSHd3M2xyaTdKT0M3dVUvR1NaOEFBN2YwOHpnUFBr
-VmFUVG5ECm1IVGpPMWFPd0gweWRWa0xNc3E5K0dxQnRnRUIvU3AwUTVpd0J2dE40
-SzhaRXNWeTRPMWdzby9YSVhRVgotLS0gOUZxb3A5c3ZtWXczbXVkTXRwNGlLYVUv
-dCtzMzUycHlRV2lyU2hpcVg5WQogH7jv9knJvy/XUG3/78RGg0DjBAwDhdha5Opl
-Wwjs/9qLPFYgKW4kUVW7HqlJ10YrYOaJvquK77a1o1mA1aYE4Qw6vD8NbxATuNNk
-Ykrthjhu8wioOrRZtMB1rKAcl1GaBH6S/jsZrp7K6wNXYH3fYx/1AW+QN0INTS4f
-CSX0VQwskQunNcUKcmNmyzaeitFkyKeBmPkUddNAleE4ulQ5NLeHsYzNPR3eE9JD
-fvWa3KvzMLzxbrvS1pBqFL+cHBPWDiKjFuXMGAxnbSIWd08JAg3vRZ/lWI55/+3z
-frRieXSYOvlAw1/UoT1jbMoUYSwhunDDh9OWOT16YM72tlpXRTFfVbh4mxKYWTk0
-MugKGP/QuuaUH56h2hXrBL5+PzAaMigI6G5o9dIHhgPufHnIHLzbmSWSH0V1BGtI
-RSHExePHk/VrQkSO9Hft8iNdvJtEJ4eI4qYYwjYOpjkgri8N/daO6dmgY2S3NngH
-9eZsTo2dS8jt1HY9B7+jkVpoCYFaX3Eipet0MtnZPqCCi4EzGR+/79FIspJXPvWb
-ealEu91VFeqb9WGiPcSk4OZmKicrLx3Dw90Ts4VbR3AW5kxKWyOrFHTwSpZLs6+i
-4G3xrqZ85T5ZTvT4m7rZQDeoUq7k8WmBcUUlntWsmjBSvUKq2awTyI/+95rKeKwK
-cjHxUCXH1YKDcCkuqaS1NdSjrUXuhvKn8ESxpE/5QIqABr8AKvR3j//rXGqtqC7D
-rv7kMHviP0uJqzM15cYZ+BT6fwM04a2nF2wT/WPhk1DLIviYRQzZb5P2W/2nCOP4
-+bmB9PINM0yGc+OqHzelKGRKN/yh390eZiQygb/U43ceTAwXfVdIW0svb1+4dlQm
-mjPgQnTRSasOo4hFUK+IbeELB4gTswVpu02mdM4I0KlkK7mvYDxsZzY+OGxxb8Re
-k+MbwZ6DbMsfGt2VGGM9LWeaWrnPBhbR5XDhuNj4w/ca+GdiLdloRuD72uCGYed6
-UDP0ANRt6C8MGJqINmCOQceYgkhP8ColktTLk6b0DspmY9mTCBED5EXgD5o/qnlM
-ui9VFT75Rr9eELebT2s2IU+s69ESaTPrZ3bNDqSDMkeYbdzfcCsPwI9sz38fow==
+YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IEhBV2NqUSBBMVJ3aGtZ
+Z3dVbmYvZUJudXhja1B1bUlHRGJzdi94N0FtSlNUenJvV3VadQpaOHR5bEpkYjJy
+UnI2OGoxWTZEN2FQaVRzU2tpd0g2TlBmRUhIcCtteDZBCi0+IHBpdi1wMjU2IHBE
+YXpSZyBBMVl3a25ZRUs4YU9seE5PQW1OWDZyOEh4RXRBZ0t1azZFSEt3T0lLcTQ3
+cAp1WFh4aFV0VG52VTFhM3VXUW41UG9XQnZ5bGUzajdqbGptUVRLdkdlemFnCi0+
+IDxVaW8nLWdyZWFzZSA8fSBAY0EuIDB+ClpEeFlvY1JSWFFoRFhsVGRXTXp6YjFF
+SXVlU0xwMzY0N05BCi0tLSBzNHFLbEZXRENpUkRqWTRLQ0N1QlZObFZtZFpmQkcr
+TWN1WFRobzZZN0lFCh1kjTRNXk8Xm3hqf5IjFjZ/ThsnLtLZ2U9wR+AQzVggENXI
+vkXAbCMAVH3Q+DUjCM0kwhp2+GJOW/U0KjEa/UGrwokZwzhs0M5yAjw4RiwyoOoR
+CxewEV36tvW/9ruIfQvqWxPNewknp4dmDo4YqJ5JM6R4EJgywHRqPDgMk+WZ6jXi
+ndYdmfo3K0i34DzmYMw8wIlc646V7UIIJYp0n8Ff0AiS78pzFvjlmtE3TdWqeeRX
+xNI3iQ6DRXirkiumoVItqU1ykWK3CbXEDQAHeKpGKNCJ/yT2v2Y27TZTJ3yGACNW
+dxedoJi1gh/kEUEL+adifIjbZiCFH+my8jVK7plWvav+0+G0cuK1euFNp/ZCPujA
+pFevpbwUkPWcNmbxFvAxd+8vO9e+2Msk6BRVNtOWvKoOQa2BXYDqM1qdOv2iV0Me
+V0VTDBRbNoyz/CrTyNEi3aYEFUvsHgnPD3XXB9uJFL0+wbLprVn2F0jVaBPublnl
+8XHKRL8aJS3q2u1CADXNdOMnYpoHHNFr77qBi2L4wIBWoqfWU5vNYUu8C49NwuxD
+dlIqw6yA/OcnQgFs+xKkOq+of9aRC65zHh2rxttsHo7RvjnQALnb3zLOOR56NhQ2
+ZQCkgrAQYGg6G0NtmK7H3N23yM12IqHmlBn8QRZCdb7hMMw/8gLf5RpreJviSqzo
+tOtZAxBC4UkZhAhQDBZ7GMzvHHwFQNd4ntXH6C7qiRJaIw+hlUI25yuGE/1k1s0o
+PHtEcXeuHqs2Sgdwe9nM9MdvZqpXGcsnuE7Z0xantXlOLWpx/t61IOPoPnhj0vOm
+q6ARC6jpFAUJ5v4UEf3Ss2bzbwfWFAekaRX1t4GVhhUHRK+OxO7xs3OsOya2sLzv
+kOCMjC6nsOlvkRE4xtMtM9Kdh2foCvPkxecChIVYkRWASDFmjAd+Sqo2rYTEOlF1
+zZttUMEFcLN0PQMfIAnLpn0LtaxwiodmdGtMEHWE3yMVQ98xYmfuoJuKrySDqfq0
+W5lPZjjx0mTUXZH5poRpg6JAr5I46Ag=
 -----END AGE ENCRYPTED FILE-----

dot_config/git/config.tmpl 🔗

@@ -2,6 +2,7 @@
 	defaultBranch = main
 [user]
 	{{- if eq .chezmoi.hostname "angmar" }}
+	# {{ .chezmoi.hostname }}
 	signingkey = ~/.ssh/yk-stationary.pub
 	{{- else }}
 	signingkey = ~/.ssh/yk-mobile.pub
@@ -49,5 +50,3 @@
 	smudge = git-lfs smudge -- %f
 	process = git-lfs filter-process
 	required = true
-[url "git@git.sr.ht:"]
-	insteadOf = https://git.sr.ht/

dot_config/isync/encrypted_mbsyncrc.age 🔗

@@ -1,45 +1,47 @@
 -----BEGIN AGE ENCRYPTED FILE-----
-YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IEhBV2NqUSBBeXlITDNv
-RzFWZ1JiOVdKdXhleGFrbWU0ek9QaGVWdmptWDNTckJVTDNoTwpiUzcvSVpXYUg5
-OXJOdGVybnp4TXdzQTNVL1NnT3ZTSXBVektUOHdFbnBVCi0+IHBpdi1wMjU2IHBE
-YXpSZyBBc0o3TnpKajM2M1d4NEhBUGtYV2NHZlVUdDhZWDUzL05TR2p5c3JDWWMz
-LwpiOXBaWnV0a2Jmb0R2RSswSU1odHBmTUlTMlB2Q05oOHJ1dTQ1S0xUVTJZCi0t
-LSBYNGx0cnBJTmk2WGdTdERORVBoRDdxQ2RyT0VjZGVuck9KdGoyNU40OThvCutv
-MD+I/sNKpbSV4mNu8Q/rCRIrbYaibtdkaBk6uUo69dSChRzSaYnUbg9PB5VSw144
-nFRRydOKpiQC9PD/mL7UV9E0XIpgLEehlrYYKftWBUv7DuLFJgF74UOgzhy9xDAK
-OsGsAnU4itfuRRXGMnNv1reL6oGZLtxFb5uNCLuO6PoBjf2HrMMUQy2CccyUWEaA
-iLcnOpwELYRczHlFIgmu9ywOU83VV/h/XP0tcwHWkp+tCBMkBUBn2YccpH/y7FrI
-1pBb5oqGLGK0lBrEcaf49ylCTRHTKSXvHSWRdXjj/9ZLTayAUzXa4KZXEFf+YAFZ
-Jx9Fc5cDAp1nl9qOnwaqVLIdGHVtCIAdDZ9q/BaOMzmITfXzwWOF+m90dN1N2Dtm
-fWFWgun+RTZ0NzHM9vx5YhsatmPZ+zqOxTwwhbPcTdztvoeDk9O/U8vom3HlJcV3
-2H62LVkUmkIsyIdbNXOP9agy7tZNsJ/6tTvsgMqfbV0MiTChOTRWDMZ+AkR5r08v
-hF+ZvjlRJHaB7yYsQLQmXdSFE9R7eg3DekkLMdGfbxt53UZWe14QxrhEqRR+x1J3
-KupS1/+DNC9Gy2ISC9sSwK3/iNfkNCD8bRcOJX6+pZOJrX5rnGBa/ab3TCMw7waZ
-4WDefTgOl4bz5kwtfj2qLFhWvXjCY3xJI0AmF6/6qwmIdQtHDWp5gtHTtuuJaIuO
-oWoJLRHRULCVsaKvxyh3eO7yyBy/VBzCWBjpyc/ldxKzlsDPjqyJn3xRfd+vFrC8
-pn094vDoAa0LXHBr5z689NE1iNR6Q6X+6QXdZfA9r1L4OmZXZ7jX7w54UkcPVy0P
-9TPACvYZ3LCECHcPmCnivzZ0JrPlgsbxNLdo1tV1VwdyyllFQdOIoI7a/f8FOAYY
-ZdlGgLiw1iUqgPC4RwU5iRXIq0ezQLg22TFr3XUJx0JNp2mbhxCACHNKfHPJIENc
-TzDCPFfY5B4fRqh/nt5Jxz8O7M1GMstyJvG6rJlEO9/+AvL63bKAZdeM7rUlHcME
-q57vIatTKQfPopzgGFMZweIuZKHd++qfayPmGZ+T/ffxohqunFc5tQ4qhkG55TTd
-63/v7jUruPWS/E8TjMre4y0qi+lOKEGjEtdUjdOFlIhNw3JwJp0rKeZDC7zwQ6Mk
-z1TRnJPIqTwhJY3TIGmGVFVVT4iCCTLO3svFsZCJAbb30GsHudQwcYKDx1oUDi+A
-uDj8+199MoAVG7KmlbY8F5LtEoQPNXYmhUkwKWe4HBpM43nE2RDZ50mPLmNFJgoI
-9VPhprN75hRiOzPoCYHBnJlIpKWmoX6mx9lpg0GMhdIcs24vGTwy2DngW4IyEuuW
-Hp0RRvc7ZsZotAjz4dHmOo2O95lwPilmA5sgGj1ESEFzbDR9GibzKJzHD+vq4Jw3
-1ImoV3rASY1YfKXmGvpCPQt69HPMQlTErMEdmxGZIZ6577Bsva7oFQxt7Y0Kd9to
-M4N3S3ITPCxMtSYyY5n/80YtT/lcb1ltS2k7tNRAUaaDmQkbpqQlWPL7TjQZlCXz
-ti1IhtJavCG9ER69G3pKkgAY1gO2lyntnmHMooj0SbHza/5g8WTqL+rbQNyxOBok
-OX4PMvnT9E6/k2gpTkeRuCI3E1DWywKEB/RA0jHIZcU3WC05Qh2Yx8F3DLTW/hBn
-ooChNyagg8JgLBisK14TQy6qCL54DaEHM3ZPzo2bnibfXV3MABZ36hfm5j2q3g90
-UFvS4scOxMdVTob+DjFXQand88jR9ldmDZP9kXr2P/PEG96ghVmQ06HOy7e4CCrk
-xAMIjfGb4BjzbvpIODrBRW6JfK1YgSWZw3+zEZqYtxZvBQpZ4Q2Mif/nmpVVVovZ
-XmEgBVQIYWDzINbfDvdgTBquxRh43VclBZ7V6AbJsd5VWPMMLADHJj/s+ynsSsT1
-7i7tLJ2w0jlo3g6iFDs095z1qY+Ek0qCOc/pRIsQLG5sNs/gaT9NQ/hbIUV0hNlE
-Hb9yQeorEEGf9NwOmPLEtBQrKXqLteV2OpUQdGg22uyjv+adBNUQuVCG99cxBexO
-ucgvjdNtuahWYbzLOA6d6h3hKi0Nj9QRxJ88wp9bJFXsUGg8TCbGDsL8DMmtijL6
-ooRHl1qZN9TZhxFzKpx3nE7AAeyyiV5V0pRpMfNbNf9JMCVZTMcVW3yFK1pQCIY3
-QovAj/0gdb7KEJK8blub8EopvY8goSsFon0kxmmsssuml+vbypBkWlc+2eYqYZ6W
-Es7jCTwSmmdxIld1naR2Ti3115namC6SOBfqUPHrJcsSPtyWHYg8R8IOpioWnn5S
-h14l05vIg5zE8m3zl8Yzlp0ARJA1FHum+/l+Q1b8W9IG8Z9mgyjUjsJONVTzlB2X
+YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IEhBV2NqUSBBdk5Za1JU
+VTJnUzE2Mk1QZW5ZdkZUU0QvVTFDTXRSM1pWNjNnWWlCY3ozMQo0a1lFa0xFVHBk
+MHp2QnByVUphalNCc0M2emtLSldHZUFwVFRFRGZ6dHpZCi0+IHBpdi1wMjU2IHBE
+YXpSZyBBeUNXcVlwTm56blFPc2hETy9SdnB4WHptcFFvSTlvc01rUmJGZ3dGQVZZ
+NAorVzRKcTZSdmtXR3kvRHBuSnlSUE8xcUsrR0lsKy9KV1ptNmZETUdVaUtVCi0+
+ID00Pi1ncmVhc2Ugay5fRj8nIElWN3dkRyA9TS1rVwpzYzQxKzkrdTZ2UXIrVDIw
+ZmdIbnU2Y1ZxaTdwUlIwZXRkYUw1bUJVdkovUkUyYwotLS0gWGZYd29qb3Q3SGFE
+WDJWNjNsb2Y1cXlZemsxMkhCcXQxc1JkaUNqcVdiSQohDWKEZ7K2JBAC8g9ekSlN
+DeNIbSnEGKEAfQpIi9sh23MuKbKM26zzK9KXqPm7uPwJ7RrstFiyybRYlLvBMgL/
+VOLV7pu3Poyt+UTpg2plmOFiaHSe96yZBauDzZDFTsuqJUysHyXzOyk6hC34k8zT
+luqBuZYlkQ0LJK/pF63nxU58ZaJAE6F3gWMjhdtwUKN7Uyt//wEfoZR/c2Dh9gYn
+hNc8EIIQxtdoCvLTobJ9uBY5AinDLLElbi9E+16HwitumhvC6DHatOJdFNFlCo3P
+hpexmj2iyRzx0v2WRo5rn4/IBljGoWL/8MZSz4g/SSfuBqb6uqy6lh99gSKD8Xnn
+4BX8Ne9lvMsHjHDCh5a9d6SZJR1G5XseK+S28DPtegZCG/AdoN36eNKyo13/6kFf
+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
 -----END AGE ENCRYPTED FILE-----

dot_config/msmtp/encrypted_config.age 🔗

@@ -1,58 +1,64 @@
 -----BEGIN AGE ENCRYPTED FILE-----
-YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IEhBV2NqUSBBdjVETGd1
-czA0TE1ISFdXRjRQY05uVVJTUVJhSzRSaHFqMWo0SUxwOUtTcwoyVFlvUGdkc2Ja
-YmRPcWtPb2JrRXRXRTVySThPaE9aSUpZOEMxTGVyNEdrCi0+IHBpdi1wMjU2IHBE
-YXpSZyBBc1pQQ1duek5RSHhZNTdxNWJVVTVMWTJSMGhpTGs3U3VUR04zR01MMWl4
-WApDa2o0aDlLYUQ3UTNiSlpqKzZPckJHUkkwYS9EcEw4a2hMTXZQTnNEVXpvCi0t
-LSBLRy9TbWdVd2ZyZnluSE8wRXFjaTE0TTdaeFJhRUIwRjMzR3RLRHRxNHdJCosk
-DjcVFTCpdzOmwL2vFZlBCplLt6YBVI8ELxMKydRCIhoM9aHbqjZ02ppIN2dYu1Fv
-vi3JYsrp9rSkesd+RSdIdxlLaDF6OKwcl+v/K7BisxUuNtdDDBIRB7ZCJxyDOd4z
-SEwh8PWKxu4mLOVULiY9CLEyzp3/hVPCQKcqFfswHjeNyx3LYf3ubGyioRmFhTKF
-s4+ccznvqiNrHlQrBxq6y2P/rhBjOVng2vq38odkSUi5sBZnlVQglEAoRSrokDrs
-T85voWFS44xQ+vG4VABVYJXLyNqsQ2PuH0U52ctcN6iJTkMBdz+YGvRagWKQTNCz
-P7kAj1xDRIejUtyKUD5Wps0R8j9mgbnLRvhUk8uox461EYJ/MVyF/26BOlZYXeSW
-w5U8dGgOscGl+DkHRPOHike+qkuer7JJRXp/ADurvRZhXJJ7AtLtE98vQpEwVGtv
-uthTMy6du1JWffl7UMxCQ7FLwgojMGLoFqN/1H74/DKuPTMESvV1D8XTUYoyxy4G
-vaC8ZXAAd8nshxLZweMRltiQj67wgEL3RYPHB/TL5DDtP2rRjiCZbdaHiYdVjJgH
-bzRPXlCkXPBgqW6Ehass/4I7lW/7aGwcMTYBNJaI8wDKnxPOiZ+40eV9Z35AVIIo
-MT7AMT6AaQ0zQTBZCOT9L1exzEwLMMsEmZ9GisUkHsOsqghSmQ57eRsW+deRPbHD
-PDCR/p2R/37tp0CLobnpyjiIR6ooJU+7nSI/T+vwInHUObqjjbMaNnWbQexgRv6z
-K3VQoznGIXNEnc1pQybTgDtQ61UH6jE6AlelO9/VVoK7dFAlHfyYD5FOpFcgAn2G
-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
+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
+dXpYSUJla3NId3hjQmtPTG5GcHE2dzU3aGNpY2RobW9IK0Q3cwo4bHNEd1loUkc3
+QUZ0ejd4bW9CLzFCRmpSY0Q5Tk5MN1lWS3NNVXB1TUY4Ci0+IHBpdi1wMjU2IHBE
+YXpSZyBBaG1wSE1rMEZtTEQ2WXFHMFNQL2JSZFhtelV5WHU4UWgzTGZyR3E4SEor
+SQpCU3pwUWVkRGs5SXZUbFU5eUM4cS9aSDhzV2JncUJsd25VY1dXZTNlR29rCi0+
+IG0tZ3JlYXNlICJcIGhBM1xfICpWLDM2R0JKICkKQTNWd2R6ODdLRFM1QmE0WEEw
+eUVYSDJTSi9oWVd4Umo0OERFbDRObkUzbHd4cGtxQkxsZVAzdFdEazBQbUlMOQo2
+dWUrcm1PcGl3Yy9HRGdybFI2SXBVc1QKLS0tIDNGM3I4VlNyR1J5MnhlenM5Y2I3
+WUNHSGZxOWZMcWtBUFhYZTJWUTJTUjAKuyf4O6+aRarG1AqEO2H+lVnAv1p5YXfj
+wL1ROgwqY/0pl0JONI/5bZKqx0odo/seDMpKENVBDZjU69riCVPRE4WJxRK77yk7
+zvBiR1A4i934JV231icsW8WzCvYs3n1e3kKWkF75ictXiV0rLYE27+HQJHwGf0ns
+rcyofuBVrkkhcscw77QYYJJkxvRuf7Xbcr24oacG7WIYgYvo+TDqqxxm6WmxVaib
++9YR/box73wM5H9Niof9tmjZE6RQEOSoUNs50wJL0hHxGNlK/7pm4KsrQdDcZGw6
+CjRsTyYxHwi3tlPltlyvsEQPQZRpsbLvCoDCioJl1lU8FvcozwMwVB0uJLTxJx41
+Q8I2RHL32y/Y45jevJURJlf1SKN5gIpljUZUPK6KN6Ta9Jvc+wl4DGbJOYiDqMzL
+9u/S483LyFWNrX/etAqlwr5riztuZAITOKUH4P/BYrGN8DA8sfBenDPuUVjlY1pd
+9N2+VA/4uBJjDirRqy0988lHUog480P9ZZsAq0E9J3wViWWNnO4yv0FceC8dFZN9
+2BkExlcLwx/JlqdrX1JwZVzeURsIlsCBmOV6p3Dga1+hwPnMDVnuo8lOg+HqpfqQ
+AIctVHdXsCyZ/91C/+pSQ3dLK6PW2FX9zIgs1z91+Mc6QpJX6jqao5NVZvNttB5v
+Y+VqO4aFTElMi7RC0gxzcVmuHq+2ZJQsiSzj6WTH1e9Zz6JJhbQ1iMPP21O14quq
+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
 -----END AGE ENCRYPTED FILE-----

dot_config/nvim/init.lua 🔗

@@ -0,0 +1,27 @@
+-- HELLO, welcome to NormalNvim!
+-- ---------------------------------------
+-- This is the entry point of your config.
+-- ---------------------------------------
+
+-- LOAD SOURCE FILES BY ORDER:
+vim.loader.enable()
+for _, source in ipairs {
+  "base.1-options",
+  "base.2-lazy",
+  "base.3-autocmds",
+  "base.4-mappings",
+} do
+  local status_ok, error = pcall(require, source)
+  if not status_ok then vim.api.nvim_err_writeln("Failed to load " .. source .. "\n\n" .. error) end
+end
+
+-- ONCE ALL SOURCE FILES HAVE LOADED:
+-- Load the color scheme defined in ./lua/1-options.lua
+if base.default_colorscheme then
+  if not pcall(vim.cmd.colorscheme, base.default_colorscheme) then
+    require("base.utils").notify(
+      "Error setting up colorscheme: " .. base.default_colorscheme,
+      vim.log.levels.ERROR
+    )
+  end
+end

dot_config/nvim/init.vim.tmpl 🔗

@@ -1,142 +0,0 @@
-let mapleader =","
-
-if ! filereadable(expand('~/.config/nvim/autoload/plug.vim'))
-	echo "Downloading junegunn/vim-plug to manage plugins..."
-	silent !mkdir -p ~/.config/nvim/autoload/
-	silent !curl "https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim" > ~/.config/nvim/autoload/plug.vim
-endif
-
-call plug#begin()
-Plug 'ap/vim-css-color'                 " highlight css colour codes with that color
-Plug 'tpope/vim-surround'               " highlight open/close characters like [], {}, ()
-Plug 'wakatime/vim-wakatime'            " Aggregate editor stats and metrics
-Plug 'catppuccin/vim', { 'as': 'catppuccin' }
-Plug 'nvim-treesitter/nvim-treesitter', {'do': ':TSUpdate'}
-Plug 'github/copilot.vim'
-call plug#end()
-
-{{- if eq .theme_variant "dark"}}
-colorscheme catppuccin_macchiato
-{{- end }}
-{{- if eq .theme_variant "light"}}
-colorscheme catppuccin_latte
-{{- end }}
-
-" LaTeX config
-let g:livepreview_previewer = 'zathura'
-let g:livepreview_use_biber = 1
-
-" Grammorous config
-let g:grammarous#languagetool_cmd = 'java -cp ~/languagetool/languagetool-server.jar org.languagetool.server.HTTPServer --port 8081 --allow-origin "*"'
-syntax enable
-
-""
-" Some basics
-""
-" Make ctags for supported languages
-command! MakeTags !ctags -R .
-" Add all subdirs to working path so :find works well
-set path+=**
-" Status line completion menu
-set wildmenu
-" Set above menu to a vertical instead of horizontal list
-set wildmode=full,list,full
-" Wrap long lines that continue past the viewport
-set linebreak
-" Highlight the column the cursor is on
-set cursorcolumn
-" Highlight the line the cursor is on
-set cursorline
-" Enables more colors
-set termguicolors
-" If there are folds, close some of them on startup
-set foldlevelstart=99
-" Use system clipboard for all yanking/pasting
-set clipboard+=unnamedplus
-" Use tabs but display them as 4 spaces instead of 8
-set ts=4 sts=4 sw=4 expandtab
-" Round the indent to a multiple of sw
-set shiftround
-" Set hidden character characters
-set listchars=tab:▸\ ,eol:¬,nbsp:␣,trail:•,space:.,extends:→,precedes:←
-set backspace=indent,eol,start
-" Keep 12 lines above and below the cursor when scrolling
-set scrolloff=12
-" Disable mouse support
-set mouse=
-" Use indents to determine folds
-set foldmethod=indent
-" Set line numbers relative to current line
-set number nu
-" Show partial commands in the bottom right
-set showcmd
-" Show which mode you're in
-set showmode
-" Display partial matches when searching
-set incsearch
-" Highlight search results—hide with :noh
-set hlsearch
-" Wrap searches around the end of the file
-set wrapscan
-" Tell Vim to be more liberal with creating hidden buffers
-set hidden
-" Case-insensitive search unless term contains uppercase chars
-set smartcase
-
-" Statusbar theme
-let g:airline_powerline_fonts = 1
-let g:airline_theme='bubblegum'
-if !exists('g:airline_symbols')
-	let g:airline_symbols = {}
-endif
-
-""
-" Keybindings
-""
-" Insert timestamp at the end of the line in this format: 20200527T113245
-nnoremap <C-t><C-s> m'A<C-R>=strftime('%Y%m%dT%H%M%S')<CR>
-" Open a new tab
-nnoremap <C-t>t :tabnew<CR>
-" Toggle hidden characters
-nnoremap <C-l> :set list!<CR>
-
-" Window management
-map <C-h> <C-w>h
-map <C-j> <C-w>j
-map <C-k> <C-w>k
-map <C-l> <C-w>l
-
-" only enable persistent undo if vim supports
-if has('persistent_undo')
-	set undodir=$HOME/.config/nvim/undofile
-	set undofile
-endif
-
-" Only enable autocommands when Vim supports them
-if has("autocmd")
-	" Disables automatic commenting on newline:
-	autocmd FileType * setlocal formatoptions-=c formatoptions-=r formatoptions-=o
-
-	""
-	" File-specific indentation settings
-	""
-	autocmd FileType make setlocal ts=8 sts=8 sw=8 noexpandtab
-	autocmd FileType yaml setlocal ts=2 sts=2 sw=2 expandtab
-
-	""
-	" Markdown Configuration
-	""
-	" Treat all .md files as markdown
-	autocmd BufNewFile,BufRead *.md set filetype=markdown
-	" Spellcheck in British English
-	autocmd FileType markdown setlocal spell spelllang=en_gb
-
-	""
-	" Working with LaTeX
-	""
-	" Reduce udpatetime for faster previews
-	autocmd BufNewFile,BufRead *.tex setfiletype tex
-	autocmd FileType tex set ut=250
-	autocmd FileType tex set colorcolumn=72
-
-endif

dot_config/nvim/lua/base/1-options.lua 🔗

@@ -0,0 +1,93 @@
+-- HELLO, welcome to NormalNvim!
+-- ----------------------------------------
+-- Here you can define your nvim variables.
+-- ----------------------------------------
+
+-- NormalNvin lua globals
+_G.base = {}
+
+-- Theme
+base.default_colorscheme = "tokyonight-night"
+
+-- Options --------------------------------------------------------------------
+vim.opt.breakindent = true -- Wrap indent to match  line start.
+vim.opt.clipboard = "unnamedplus" -- Connection to the system clipboard.
+vim.opt.cmdheight = 0 -- Hide command line unless needed.
+vim.opt.completeopt = { "menu", "menuone", "noselect" } -- Options for insert mode completion.
+vim.opt.copyindent = true -- Copy the previous indentation on autoindenting.
+vim.opt.cursorline = true -- Highlight the text line of the cursor.
+vim.opt.expandtab = true -- Enable the use of space in tab.
+vim.opt.fileencoding = "utf-8" -- File content encoding for the buffer.
+vim.opt.fillchars = { eob = " " } -- Disable `~` on nonexistent lines.
+vim.opt.foldenable = true -- Enable fold for nvim-ufo.
+vim.opt.foldlevel = 99 -- set highest foldlevel for nvim-ufo.
+vim.opt.foldlevelstart = 99 -- Start with all code unfolded.
+vim.opt.foldcolumn = "1" -- Show foldcolumn in nvim 0.9+.
+vim.opt.ignorecase = true -- Case insensitive searching.
+vim.opt.infercase = true -- Infer cases in keyword completion.
+
+vim.opt.laststatus = 3 -- Global statusline.
+vim.opt.linebreak = true -- Wrap lines at 'breakat'.
+vim.opt.number = true -- Show numberline.
+vim.opt.preserveindent = true -- Preserve indent structure as much as possible.
+vim.opt.pumheight = 10 -- Height of the pop up menu.
+vim.opt.relativenumber = false -- Show relative numberline.
+vim.opt.shiftwidth = 2 -- Number of space inserted for indentation.
+vim.opt.showmode = false -- Disable showing modes in command line.
+vim.opt.showtabline = 2 -- always display tabline.
+vim.opt.signcolumn = "yes" -- Always show the sign column.
+vim.opt.smartcase = true -- Case sensitivie searching.
+vim.opt.smartindent = false -- Smarter autoindentation.
+vim.opt.splitbelow = true -- Splitting a new window below the current one.
+vim.opt.splitright = true -- Splitting a new window at the right of the current one.
+vim.opt.tabstop = 2 -- Number of space in a tab.
+
+vim.opt.termguicolors = true -- Enable 24-bit RGB color in the TUI.
+vim.opt.timeoutlen = 500 -- Shorten key timeout length a little bit for which-key.
+vim.opt.undofile = true -- Enable persistent undo between session and reboots.
+vim.opt.updatetime = 300 -- Length of time to wait before triggering the plugin.
+vim.opt.virtualedit = "block" -- Allow going past end of line in visual block mode.
+vim.opt.writebackup = false -- Disable making a backup before overwriting a file.
+vim.opt.shada = "!,'1000,<50,s10,h" -- Remember the last 1000 opened files
+vim.opt.history = 1000 -- Number of commands to remember in a history table (per buffer).
+vim.opt.swapfile = false -- Ask what state to recover when opening a file that was not saved.
+vim.opt.wrap = true -- Disable wrapping of lines longer than the width of window.
+vim.opt.colorcolumn = "80" -- PEP8 like character limit vertical bar.
+vim.opt.mousescroll = "ver:1,hor:0" -- Disables hozirontal scroll in neovim.
+vim.opt.guicursor = "n:blinkon200,i-ci-ve:ver25" -- Enable cursor blink.
+vim.opt.autochdir = true -- Use current file dir as working dir (See project.nvim).
+vim.opt.scrolloff = 1000 -- Number of lines to leave before/after the cursor when scrolling. Setting a high value keep the cursor centered.
+vim.opt.sidescrolloff = 8 -- Same but for side scrolling.
+vim.opt.selection = "old" -- Don't select the newline symbol when using <End> on visual mode.
+
+vim.opt.viewoptions:remove "curdir" -- Disable saving current directory with views.
+vim.opt.shortmess:append { s = true, I = true } -- Disable startup message.
+vim.opt.backspace:append { "nostop" } -- Don't stop backspace at insert.
+vim.opt.diffopt:append { "algorithm:histogram", "linematch:60" } -- Enable linematch diff algorithm
+
+local is_android = vim.fn.isdirectory('/data') == 1
+if is_android then vim.opt.mouse = "v" else vim.opt.mouse = "a" end -- Enable scroll for android
+
+-- Globals --------------------------------------------------------------------
+vim.g.mapleader = " " -- Set leader key.
+vim.g.maplocalleader = "," -- Set default local leader key.
+vim.g.big_file = { size = 1024 * 100, lines = 10000 } -- For files bigger than this, disable 'treesitter' (+100kb).
+
+-- The next globals are toggleable with <space + l + u>
+vim.g.autoformat_enabled = false -- Enable auto formatting at start.
+vim.g.autopairs_enabled = false -- Enable autopairs at start.
+vim.g.cmp_enabled = true -- Enable completion at start.
+vim.g.codeactions_enabled = true -- Enable displaying 💡 where code actions can be used.
+vim.g.codelens_enabled = true -- Enable automatic codelens refreshing for lsp that support it.
+vim.g.diagnostics_mode = 3 -- Set code linting (0=off, 1=only show in status line, 2=virtual text off, 3=all on).
+vim.g.icons_enabled = true -- Enable icons in the UI (disable if no nerd font is available).
+vim.g.inlay_hints_enabled = false -- Enable always show function parameter names.
+vim.g.lsp_round_borders_enabled = true -- Enable round borders for lsp hover and signatureHelp.
+vim.g.lsp_signature_enabled = true -- Enable automatically showing lsp help as you write function parameters.
+vim.g.notifications_enabled = true -- Enable notifications.
+vim.g.semantic_tokens_enabled = true -- Enable lsp semantic tokens at start.
+vim.g.url_effect_enabled = true -- Highlight URLs with an underline effect.
+
+
+
+

dot_config/nvim/lua/base/2-lazy.lua 🔗

@@ -0,0 +1,75 @@
+-- Lazy.nvim config file.
+
+-- DESCRIPTION:
+-- Use this file to configure the way you get updates.
+
+--    Sections:
+--      -> lazy updater options  → choose your lazy updates channel here.
+--      -> extra behaviors       → extra stuff we add to lazy for better UX.
+--      -> assign spec           → if channel==stable, uses lazy_snatshot.lua
+--      -> setup using spec      → actual setup.
+
+
+-- lazy updater options
+-- Use the same values you have in the plugin `distroupdate.nvim`
+local updater = {
+  channel = "stable",               -- 'nightly', or 'stable'
+  snapshot_module = "lazy_snapshot" -- snapshot file name without extension.
+}
+
+-- lazyload extra behavior
+--  * If plugins need to be installed         → auto launch lazy at startup.
+--  * When lazy finishes installing plugins   → check for mason updates too.
+--                                              (but not when updating them)
+--  * Then show notifications and stuff.
+local lazypath = vim.fn.stdpath "data" .. "/lazy/lazy.nvim"
+if not vim.uv.fs_stat(lazypath) then
+  local output = vim.fn.system {
+    "git",
+    "clone",
+    "--filter=blob:none",
+    "--branch=stable",
+    "https://github.com/folke/lazy.nvim.git",
+    lazypath,
+  }
+  if vim.api.nvim_get_vvar "shell_error" ~= 0 then
+    vim.api.nvim_err_writeln("Error cloning lazy.nvim repository...\n\n" .. output)
+  end
+  local oldcmdheight = vim.opt.cmdheight:get()
+  vim.opt.cmdheight = 1
+  vim.notify "Please wait while plugins are installed..."
+  vim.api.nvim_create_autocmd("User", {
+    desc = "Load Mason and Treesitter after Lazy installs plugins",
+    once = true,
+    pattern = "LazyInstall",
+    callback = function()
+      vim.cmd.bw()
+      vim.opt.cmdheight = oldcmdheight
+      vim.tbl_map(function(module) pcall(require, module) end, { "nvim-treesitter", "mason" })
+      -- Note: This event will also trigger a Mason update in distroupdate.nvim
+    end,
+  })
+end
+vim.opt.rtp:prepend(lazypath)
+
+-- assign spec (if pin_plugins is true, load ./lua/lazy_snapshot.lua).
+local pin_plugins = updater.channel == "stable"
+local spec = pin_plugins and { { import = updater.snapshot_module } } or {}
+vim.list_extend(spec, { { import = "plugins" } })
+
+-- Require lazy and pass the spec.
+require("lazy").setup({
+  spec = spec,
+  defaults = { lazy = true },
+  performance = {
+    rtp = { -- Use deflate to download faster from the plugin repos.
+      disabled_plugins = {
+        "tohtml", "gzip", "zipPlugin", "netrwPlugin", "tarPlugin"
+      },
+    },
+  },
+  -- Enable luarocks if installed.
+  rocks = { enabled = vim.fn.executable("luarocks") == 1 },
+  -- We don't use this, so create it in a disposable place.
+  lockfile = vim.fn.stdpath("cache") .. "/lazy-lock.json",
+})

dot_config/nvim/lua/base/3-autocmds.lua 🔗

@@ -0,0 +1,281 @@
+-- General usage autocmds.
+
+-- DESCRIPTION:
+-- All autocmds are defined here.
+
+--    Sections:
+--       ## EXTRA LOGIC
+--       -> 1. Events to load plugins faster.
+--       -> 2. Save/restore window layout when possible.
+--       -> 3. Launch alpha greeter on startup.
+--       -> 4. Update neotree when closing the git client.
+--       -> 5. Create parent directories when saving a file.
+--
+--       ## COOL HACKS
+--       -> 6. Effect: URL underline.
+--       -> 7. Customize right click contextual menu.
+--       -> 8. Unlist quickfix buffers if the filetype changes.
+--       -> 9. Close all notifications on BufWritePre.
+--
+--       ## COMMANDS
+--       -> 10. Neotest commands.
+--       ->     Extra commands.
+
+local autocmd = vim.api.nvim_create_autocmd
+local cmd = vim.api.nvim_create_user_command
+local utils = require("base.utils")
+local is_available = utils.is_available
+
+-- ## EXTRA LOGIC -----------------------------------------------------------
+-- 1. Events to load plugins faster → 'BaseFile'/'BaseGitFile'/'BaseDefered':
+--    this is pretty much the same thing as the event 'BufEnter',
+--    but without increasing the startup time displayed in the greeter.
+autocmd({ "BufReadPost", "BufNewFile", "BufWritePost" }, {
+  desc = "Nvim user events for file detection (BaseFile and BaseGitFile)",
+  callback = function(args)
+    local empty_buffer = vim.fn.resolve(vim.fn.expand "%") == ""
+    local greeter = vim.api.nvim_get_option_value("filetype", { buf = args.buf }) == "alpha"
+    local git_repo = utils.run_cmd(
+    { "git", "-C", vim.fn.fnamemodify(vim.fn.resolve(vim.fn.expand "%"), ":p:h"), "rev-parse" }, false)
+
+    -- For any file exept empty buffer, or the greeter (alpha)
+    if not (empty_buffer or greeter) then
+      utils.trigger_event("User BaseFile")
+
+      -- Is the buffer part of a git repo?
+      if git_repo then
+        utils.trigger_event("User BaseGitFile")
+      end
+    end
+  end,
+})
+autocmd({ "VimEnter" }, {
+  desc = "Nvim user event that trigger a few ms after nvim starts",
+  callback = function()
+    -- If nvim is opened passing a filename, trigger the event inmediatelly.
+    if #vim.fn.argv() >= 1 then
+      -- In order to avoid visual glitches.
+      utils.trigger_event("User BaseDefered", true)
+      utils.trigger_event("BufEnter", true) -- also, initialize tabline_buffers.
+    else                                    -- Wait some ms before triggering the event.
+      vim.defer_fn(function()
+        utils.trigger_event("User BaseDefered")
+      end, 70)
+    end
+  end,
+})
+
+-- 2. Save/restore window layout when possible.
+autocmd({ "BufWinLeave", "BufWritePost", "WinLeave" }, {
+  desc = "Save view with mkview for real files",
+  callback = function(args)
+    if vim.b[args.buf].view_activated then
+      vim.cmd.mkview { mods = { emsg_silent = true } }
+    end
+  end,
+})
+autocmd("BufWinEnter", {
+  desc = "Try to load file view if available and enable view saving for real files",
+  callback = function(args)
+    if not vim.b[args.buf].view_activated then
+      local filetype =
+          vim.api.nvim_get_option_value("filetype", { buf = args.buf })
+      local buftype =
+          vim.api.nvim_get_option_value("buftype", { buf = args.buf })
+      local ignore_filetypes = { "gitcommit", "gitrebase", "svg", "hgcommit" }
+      if
+          buftype == ""
+          and filetype
+          and filetype ~= ""
+          and not vim.tbl_contains(ignore_filetypes, filetype)
+      then
+        vim.b[args.buf].view_activated = true
+        vim.cmd.loadview { mods = { emsg_silent = true } }
+      end
+    end
+  end,
+})
+
+-- 3. Launch alpha greeter on startup
+if is_available "alpha-nvim" then
+  autocmd({ "User", "BufEnter" }, {
+    desc = "Disable status and tablines for alpha",
+    callback = function(args)
+      local is_filetype_alpha = vim.api.nvim_get_option_value(
+        "filetype", { buf = 0 }) == "alpha"
+      local is_empty_file = vim.api.nvim_get_option_value(
+        "buftype", { buf = 0 }) == "nofile"
+      if ((args.event == "User" and args.file == "AlphaReady") or
+            (args.event == "BufEnter" and is_filetype_alpha)) and
+          not vim.g.before_alpha
+      then
+        vim.g.before_alpha = {
+          showtabline = vim.opt.showtabline:get(),
+          laststatus = vim.opt.laststatus:get()
+        }
+        vim.opt.showtabline, vim.opt.laststatus = 0, 0
+      elseif
+          vim.g.before_alpha
+          and args.event == "BufEnter"
+          and not is_empty_file
+      then
+        vim.opt.laststatus = vim.g.before_alpha.laststatus
+        vim.opt.showtabline = vim.g.before_alpha.showtabline
+        vim.g.before_alpha = nil
+      end
+    end,
+  })
+  autocmd("VimEnter", {
+    desc = "Start Alpha only when nvim is opened with no arguments",
+    callback = function()
+      -- Precalculate conditions.
+      local lines = vim.api.nvim_buf_get_lines(0, 0, 2, false)
+      local buf_not_empty = vim.fn.argc() > 0
+          or #lines > 1
+          or (#lines == 1 and lines[1]:len() > 0)
+      local buflist_not_empty = #vim.tbl_filter(
+        function(bufnr) return vim.bo[bufnr].buflisted end,
+        vim.api.nvim_list_bufs()
+      ) > 1
+      local buf_not_modifiable = not vim.o.modifiable
+
+      -- Return instead of opening alpha if any of these conditions occur.
+      if buf_not_modifiable or buf_not_empty or buflist_not_empty then
+        return
+      end
+      for _, arg in pairs(vim.v.argv) do
+        if arg == "-b"
+            or arg == "-c"
+            or vim.startswith(arg, "+")
+            or arg == "-S"
+        then
+          return
+        end
+      end
+
+      -- All good? Show alpha.
+      require("alpha").start(true, require("alpha").default_config)
+      vim.schedule(function() vim.cmd.doautocmd "FileType" end)
+    end,
+  })
+end
+
+-- 4. Update neotree when closin the git client.
+if is_available "neo-tree.nvim" then
+  autocmd("TermClose", {
+    pattern = { "*lazygit", "*gitui" },
+    desc = "Refresh Neo-Tree git when closing lazygit/gitui",
+    callback = function()
+      local manager_avail, manager = pcall(require, "neo-tree.sources.manager")
+      if manager_avail then
+        for _, source in ipairs {
+          "filesystem",
+          "git_status",
+          "document_symbols",
+        } do
+          local module = "neo-tree.sources." .. source
+          if package.loaded[module] then
+            manager.refresh(require(module).name)
+          end
+        end
+      end
+    end,
+  })
+end
+
+-- 5. Create parent directories when saving a file.
+autocmd("BufWritePre", {
+  desc = "Automatically create parent directories if they don't exist when saving a file",
+  callback = function(args)
+    local buf_is_valid_and_listed = vim.api.nvim_buf_is_valid(args.buf)
+        and vim.bo[args.buf].buflisted
+
+    if buf_is_valid_and_listed then
+      vim.fn.mkdir(vim.fn.fnamemodify(
+        vim.loop.fs_realpath(args.match) or args.match, ":p:h"), "p")
+    end
+  end,
+})
+
+-- ## COOL HACKS ------------------------------------------------------------
+-- 6. Effect: URL underline.
+vim.api.nvim_set_hl(0, 'HighlightURL', { underline = true })
+autocmd({ "VimEnter", "FileType", "BufEnter", "WinEnter" }, {
+  desc = "URL Highlighting",
+  callback = function() utils.set_url_effect() end,
+})
+
+-- 7. Customize right click contextual menu.
+autocmd("VimEnter", {
+  desc = "Disable right contextual menu warning message",
+  callback = function()
+    -- Disable right click message
+    vim.api.nvim_command [[aunmenu PopUp.How-to\ disable\ mouse]]
+    -- vim.api.nvim_command [[aunmenu PopUp.-1-]] -- You can remode a separator like this.
+    vim.api.nvim_command [[menu PopUp.Toggle\ \Breakpoint <cmd>:lua require('dap').toggle_breakpoint()<CR>]]
+    vim.api.nvim_command [[menu PopUp.-2- <Nop>]]
+    vim.api.nvim_command [[menu PopUp.Start\ \Compiler <cmd>:CompilerOpen<CR>]]
+    vim.api.nvim_command [[menu PopUp.Start\ \Debugger <cmd>:DapContinue<CR>]]
+    vim.api.nvim_command [[menu PopUp.Run\ \Test <cmd>:Neotest run<CR>]]
+  end,
+})
+
+-- 8. Unlist quickfix buffers if the filetype changes.
+autocmd("FileType", {
+  desc = "Unlist quickfist buffers",
+  pattern = "qf",
+  callback = function() vim.opt_local.buflisted = false end,
+})
+
+-- 9. Close all notifications on BufWritePre.
+autocmd("BufWritePre", {
+  desc = "Close all notifications on BufWritePre",
+  callback = function()
+    require("notify").dismiss({ pending = true, silent = true })
+  end,
+})
+
+-- ## COMMANDS --------------------------------------------------------------
+
+-- 10. Testing commands
+-- Aditional commands to the ones implemented in neotest.
+-------------------------------------------------------------------
+
+-- Customize this command to work as you like
+cmd("TestNodejs", function()
+  vim.cmd ":ProjectRoot"                 -- cd the project root (requires project.nvim)
+  vim.cmd ":TermExec cmd='npm run test'" -- convention to run tests on nodejs
+  -- You can generate code coverage by add this to your project's packages.json
+  -- "tests": "jest --coverage"
+end, { desc = "Run all unit tests for the current nodejs project" })
+
+-- Customize this command to work as you like
+cmd("TestNodejsE2e", function()
+  vim.cmd ":ProjectRoot"                -- cd the project root (requires project.nvim)
+  vim.cmd ":TermExec cmd='npm run e2e'" -- Conventional way to call e2e in nodejs (requires ToggleTerm)
+end, { desc = "Run e2e tests for the current nodejs project" })
+
+-- Extra commands
+----------------------------------------------
+
+-- Change working directory
+cmd("Cwd", function()
+  vim.cmd ":cd %:p:h"
+  vim.cmd ":pwd"
+end, { desc = "cd current file's directory" })
+
+-- Set working directory (alias)
+cmd("Swd", function()
+  vim.cmd ":cd %:p:h"
+  vim.cmd ":pwd"
+end, { desc = "cd current file's directory" })
+
+-- Write all buffers
+cmd("WriteAllBuffers", function()
+  vim.cmd "wa"
+end, { desc = "Write all changed buffers" })
+
+-- Close all notifications
+cmd("CloseNotifications", function()
+  require("notify").dismiss({ pending = true, silent = true })
+end, { desc = "Dismiss all notifications" })

dot_config/nvim/lua/base/4-mappings.lua 🔗

@@ -0,0 +1,1616 @@
+-- Keybindings (qwerty).
+
+-- DESCRIPTION:
+-- All mappings are defined here.
+
+--    Sections:
+--       ## Base bindings
+--       -> icons displayed on which-key.nvim
+--       -> standard operations
+--       -> clipboard
+--       -> search highlighting
+--       -> improved tabulation
+--       -> improved gg
+--       -> packages
+--       -> buffers/tabs                       [buffers]
+--       -> ui toggles                         [ui]
+--       -> shifted movement keys
+--       -> cmdline autocompletion
+--       -> special cases
+
+--       ## Plugin bindings
+--       -> alpha-nvim
+--       -> git                                [git]
+--       -> file browsers
+--       -> session manager
+--       -> smart-splits.nvim
+--       -> aerial.nvim
+--       -> litee-calltree.nvim
+--       -> telescope.nivm                     [find]
+--       -> toggleterm.nvim
+--       -> dap.nvim                           [debugger]
+--       -> tests                              [tests]
+--       -> nvim-ufo
+--       -> code documentation                 [docs]
+--       -> ask chatgpt                        [neural]
+--       -> hop.nvim
+--       -> mason-lspconfig.nvim               [lsp]
+
+--
+--   KEYBINDINGS REFERENCE
+--   -------------------------------------------------------------------
+--   |        Mode  | Norm | Ins | Cmd | Vis | Sel | Opr | Term | Lang |
+--   Command        +------+-----+-----+-----+-----+-----+------+------+
+--   [nore]map      | yes  |  -  |  -  | yes | yes | yes |  -   |  -   |
+--   n[nore]map     | yes  |  -  |  -  |  -  |  -  |  -  |  -   |  -   |
+--   [nore]map!     |  -   | yes | yes |  -  |  -  |  -  |  -   |  -   |
+--   i[nore]map     |  -   | yes |  -  |  -  |  -  |  -  |  -   |  -   |
+--   c[nore]map     |  -   |  -  | yes |  -  |  -  |  -  |  -   |  -   |
+--   v[nore]map     |  -   |  -  |  -  | yes | yes |  -  |  -   |  -   |
+--   x[nore]map     |  -   |  -  |  -  | yes |  -  |  -  |  -   |  -   |
+--   s[nore]map     |  -   |  -  |  -  |  -  | yes |  -  |  -   |  -   |
+--   o[nore]map     |  -   |  -  |  -  |  -  |  -  | yes |  -   |  -   |
+--   t[nore]map     |  -   |  -  |  -  |  -  |  -  |  -  | yes  |  -   |
+--   l[nore]map     |  -   | yes | yes |  -  |  -  |  -  |  -   | yes  |
+--   -------------------------------------------------------------------
+
+local M = {}
+local utils = require "base.utils"
+local get_icon = utils.get_icon
+local is_available = utils.is_available
+local ui = require "base.utils.ui"
+local maps = require("base.utils").get_mappings_template()
+local is_android = vim.fn.isdirectory('/data') == 1 -- true if on android
+
+-- -------------------------------------------------------------------------
+--
+-- ## Base bindings ########################################################
+--
+-- -------------------------------------------------------------------------
+
+-- icons displayed on which-key.nvim ---------------------------------------
+local icons = {
+  f = { desc = get_icon("Search", 1, true) .. "Find" },
+  p = { desc = get_icon("Package", 1, true) .. "Packages" },
+  l = { desc = get_icon("ActiveLSP", 1, true) .. "LSP" },
+  u = { desc = get_icon("Window", 1, true) .. "UI" },
+  b = { desc = get_icon("Tab", 1, true) .. "Buffers" },
+  bs = { desc = get_icon("Sort", 1, true) .. "Sort Buffers" },
+  c = { desc = get_icon("Run", 1, true) .. "Compiler" },
+  d = { desc = get_icon("Debugger", 1, true) .. "Debugger" },
+  tt = { desc = get_icon("Test", 1, true) .. "Test" },
+  dc = { desc = get_icon("Docs", 1, true) .. "Docs" },
+  g = { desc = get_icon("Git", 1, true) .. "Git" },
+  S = { desc = get_icon("Session", 1, true) .. "Session" },
+  t = { desc = get_icon("Terminal", 1, true) .. "Terminal" },
+}
+
+-- standard Operations -----------------------------------------------------
+maps.n["j"] =
+{ "v:count == 0 ? 'gj' : 'j'", expr = true, desc = "Move cursor down" }
+maps.n["k"] =
+{ "v:count == 0 ? 'gk' : 'k'", expr = true, desc = "Move cursor up" }
+maps.n["<leader>w"] = { "<cmd>w<cr>", desc = "Save" }
+maps.n["<leader>W"] =
+{ function() vim.cmd "SudaWrite" end, desc = "Save as sudo" }
+maps.n["<leader>n"] = { "<cmd>enew<cr>", desc = "New file" }
+maps.n["<Leader>/"] = { "gcc", remap = true, desc = "Toggle comment line" }
+maps.x["<Leader>/"] = { "gc", remap = true, desc = "Toggle comment" }
+maps.n["gx"] =
+{ utils.open_with_program, desc = "Open the file under cursor with a program" }
+maps.n["<C-s>"] = { "<cmd>w!<cr>", desc = "Force write" }
+maps.n["|"] = { "<cmd>vsplit<cr>", desc = "Vertical Split" }
+maps.n["\\"] = { "<cmd>split<cr>", desc = "Horizontal Split" }
+maps.i["<C-BS>"] = { "<C-W>", desc = "Enable CTRL+backsace to delete." }
+maps.n["0"] =
+{ "^", desc = "Go to the fist character of the line (aliases 0 to ^)" }
+maps.n["<leader>q"] = { "<cmd>confirm q<cr>", desc = "Quit" }
+maps.n["<leader>q"] = {
+  function()
+    -- Ask user for confirmation
+    local choice = vim.fn.confirm("Do you really want to exit nvim?", "&Yes\n&No", 2)
+    if choice == 1 then
+      -- If user confirms, but there are still files to be saved: Ask
+      vim.cmd('confirm quit')
+    end
+  end,
+  desc = "Quit",
+}
+maps.n["<Tab>"] = {
+  "<Tab>",
+  noremap = true,
+  silent = true,
+  expr = false,
+  desc = "FIX: Prevent TAB from behaving like <C-i>, as they share the same internal code",
+}
+
+-- clipboard ---------------------------------------------------------------
+
+-- BUG: We disable these mappings on termux by default because <C-y>
+--      is the keycode for scrolling, and remapping it would break it.
+if not is_android then
+  -- only useful when the option clipboard is commented on ./1-options.lua
+  maps.n["<C-y>"] = { '"+y<esc>', desc = "Copy to cliboard" }
+  maps.x["<C-y>"] = { '"+y<esc>', desc = "Copy to cliboard" }
+  maps.n["<C-d>"] = { '"+y<esc>dd', desc = "Copy to clipboard and delete line" }
+  maps.x["<C-d>"] = { '"+y<esc>dd', desc = "Copy to clipboard and delete line" }
+  maps.n["<C-p>"] = { '"+p<esc>', desc = "Paste from clipboard" }
+end
+
+-- Make 'c' key not copy to clipboard when changing a character.
+maps.n["c"] = { '"_c', desc = "Change without yanking" }
+maps.n["C"] = { '"_C', desc = "Change without yanking" }
+maps.x["c"] = { '"_c', desc = "Change without yanking" }
+maps.x["C"] = { '"_C', desc = "Change without yanking" }
+
+-- Make 'x' key not copy to clipboard when deleting a character.
+maps.n["x"] = {
+  -- Also let's allow 'x' key to delete blank lines in normal mode.
+  function()
+    if vim.fn.col "." == 1 then
+      local line = vim.fn.getline "."
+      if line:match "^%s*$" then
+        vim.api.nvim_feedkeys('"_dd', "n", false)
+        vim.api.nvim_feedkeys("$", "n", false)
+      else
+        vim.api.nvim_feedkeys('"_x', "n", false)
+      end
+    else
+      vim.api.nvim_feedkeys('"_x', "n", false)
+    end
+  end,
+  desc = "Delete character without yanking it",
+}
+maps.x["x"] = { '"_x', desc = "Delete all characters in line" }
+
+-- Same for shifted X
+maps.n["X"] = {
+  -- Also let's allow 'x' key to delete blank lines in normal mode.
+  function()
+    if vim.fn.col "." == 1 then
+      local line = vim.fn.getline "."
+      if line:match "^%s*$" then
+        vim.api.nvim_feedkeys('"_dd', "n", false)
+        vim.api.nvim_feedkeys("$", "n", false)
+      else
+        vim.api.nvim_feedkeys('"_X', "n", false)
+      end
+    else
+      vim.api.nvim_feedkeys('"_X', "n", false)
+    end
+  end,
+  desc = "Delete before character without yanking it",
+}
+maps.x["X"] = { '"_X', desc = "Delete all characters in line" }
+
+-- Override nvim default behavior so it doesn't auto-yank when pasting on visual mode.
+maps.x["p"] = { "P", desc = "Paste content you've previourly yanked" }
+maps.x["P"] = { "p", desc = "Yank what you are going to override, then paste" }
+
+-- search highlighting ------------------------------------------------------
+-- use ESC to clear hlsearch, while preserving its original functionality.
+--
+-- TIP: If you prefer,  use <leader>ENTER instead of <ESC>
+--      to avoid triggering it by accident.
+maps.n["<ESC>"] = {
+  function()
+    if vim.fn.hlexists "Search" then
+      vim.cmd "nohlsearch"
+    else
+      vim.api.nvim_feedkeys(
+        vim.api.nvim_replace_termcodes("<ESC>", true, true, true),
+        "n",
+        true
+      )
+    end
+  end,
+}
+
+-- Improved tabulation ------------------------------------------------------
+maps.x["<S-Tab>"] = { "<gv", desc = "unindent line" }
+maps.x["<Tab>"] = { ">gv", desc = "indent line" }
+maps.x["<"] = { "<gv", desc = "unindent line" }
+maps.x[">"] = { ">gv", desc = "indent line" }
+
+-- improved gg --------------------------------------------------------------
+maps.n["gg"] = {
+  function()
+    vim.g.minianimate_disable = true
+    if vim.v.count > 0 then
+      vim.cmd("normal! " .. vim.v.count .. "gg")
+    else
+      vim.cmd "normal! gg0"
+    end
+    vim.g.minianimate_disable = false
+  end,
+  desc = "gg and go to the first position",
+}
+maps.n["G"] = {
+  function()
+    vim.g.minianimate_disable = true
+    vim.cmd "normal! G$"
+    vim.g.minianimate_disable = false
+  end,
+  desc = "G and go to the last position",
+}
+maps.x["gg"] = {
+  function()
+    vim.g.minianimate_disable = true
+    if vim.v.count > 0 then
+      vim.cmd("normal! " .. vim.v.count .. "gg")
+    else
+      vim.cmd "normal! gg0"
+    end
+    vim.g.minianimate_disable = false
+  end,
+  desc = "gg and go to the first position (visual)",
+}
+maps.x["G"] = {
+  function()
+    vim.g.minianimate_disable = true
+    vim.cmd "normal! G$"
+    vim.g.minianimate_disable = false
+  end,
+  desc = "G and go to the last position (visual)",
+}
+maps.n["<C-a>"] = { -- to move to the previous position press ctrl + oo
+  function()
+    vim.g.minianimate_disable = true
+    vim.cmd "normal! gg0vG$"
+    vim.g.minianimate_disable = false
+  end,
+  desc = "Visually select all",
+}
+
+-- packages -----------------------------------------------------------------
+-- lazy
+maps.n["<leader>p"] = icons.p
+maps.n["<leader>pu"] =
+{ function() require("lazy").check() end, desc = "Lazy open" }
+maps.n["<leader>pU"] =
+{ function() require("lazy").update() end, desc = "Lazy update" }
+
+-- mason
+if is_available "mason.nvim" then
+  maps.n["<leader>pm"] = { "<cmd>Mason<cr>", desc = "Mason open" }
+  maps.n["<leader>pM"] = { "<cmd>MasonUpdateAll<cr>", desc = "Mason update" }
+end
+
+-- treesitter
+if is_available "nvim-treesitter" then
+  maps.n["<leader>pT"] = { "<cmd>TSUpdate<cr>", desc = "Treesitter update" }
+  maps.n["<leader>pt"] = { "<cmd>TSInstallInfo<cr>", desc = "Treesitter open" }
+end
+
+-- nvim updater
+maps.n["<leader>pD"] = { "<cmd>DistroUpdate<cr>", desc = "Distro update" }
+maps.n["<leader>pv"] = { "<cmd>DistroReadVersion<cr>", desc = "Distro version" }
+maps.n["<leader>pc"] = { "<cmd>DistroReadChangelog<cr>", desc = "Distro changelog" }
+
+-- buffers/tabs [buffers ]--------------------------------------------------
+maps.n["<leader>c"] = { -- Close window and buffer at the same time.
+  function() require("heirline-components.buffer").wipe() end,
+  desc = "Wipe buffer",
+}
+maps.n["<leader>C"] = { -- Close buffer keeping the window.
+  function() require("heirline-components.buffer").close() end,
+  desc = "Close buffer",
+}
+-- Close buffer keeping the window → Without confirmation.
+-- maps.n["<leader>X"] = {
+--   function() require("heirline-components.buffer").close(0, true) end,
+--   desc = "Force close buffer",
+--
+maps.n["<leader>ba"] = {
+  function() vim.cmd "wa" end,
+  desc = "Write all changed buffers",
+}
+maps.n["]b"] = {
+  function()
+    require("heirline-components.buffer").nav(vim.v.count > 0 and vim.v.count or 1)
+  end,
+  desc = "Next buffer",
+}
+maps.n["[b"] = {
+  function()
+    require("heirline-components.buffer").nav(-(vim.v.count > 0 and vim.v.count or 1))
+  end,
+  desc = "Previous buffer",
+}
+maps.n[">b"] = {
+  function()
+    require("heirline-components.buffer").move(vim.v.count > 0 and vim.v.count or 1)
+  end,
+  desc = "Move buffer tab right",
+}
+maps.n["<b"] = {
+  function()
+    require("heirline-components.buffer").move(-(vim.v.count > 0 and vim.v.count or 1))
+  end,
+  desc = "Move buffer tab left",
+}
+
+maps.n["<leader>b"] = icons.b
+maps.n["<leader>bc"] = {
+  function() require("heirline-components.buffer").close_all(true) end,
+  desc = "Close all buffers except current",
+}
+maps.n["<leader>bC"] = {
+  function() require("heirline-components.buffer").close_all() end,
+  desc = "Close all buffers",
+}
+maps.n["<leader>bb"] = {
+  function()
+    require("heirline-components.all").heirline.buffer_picker(
+      function(bufnr) vim.api.nvim_win_set_buf(0, bufnr) end
+    )
+  end,
+  desc = "Select buffer from tabline",
+}
+maps.n["<leader>bd"] = {
+  function()
+    require("heirline-components.all").heirline.buffer_picker(
+      function(bufnr) require("heirline-components.buffer").close(bufnr) end
+    )
+  end,
+  desc = "Delete buffer from tabline",
+}
+maps.n["<leader>bl"] = {
+  function() require("heirline-components.buffer").close_left() end,
+  desc = "Close all buffers to the left",
+}
+maps.n["<leader>br"] = {
+  function() require("heirline-components.buffer").close_right() end,
+  desc = "Close all buffers to the right",
+}
+maps.n["<leader>bs"] = icons.bs
+maps.n["<leader>bse"] = {
+  function() require("heirline-components.buffer").sort "extension" end,
+  desc = "Sort by extension (buffers)",
+}
+maps.n["<leader>bsr"] = {
+  function() require("heirline-components.buffer").sort "unique_path" end,
+  desc = "Sort by relative path (buffers)",
+}
+maps.n["<leader>bsp"] = {
+  function() require("heirline-components.buffer").sort "full_path" end,
+  desc = "Sort by full path (buffers)",
+}
+maps.n["<leader>bsi"] = {
+  function() require("heirline-components.buffer").sort "bufnr" end,
+  desc = "Sort by buffer number (buffers)",
+}
+maps.n["<leader>bsm"] = {
+  function() require("heirline-components.buffer").sort "modified" end,
+  desc = "Sort by modification (buffers)",
+}
+maps.n["<leader>b\\"] = {
+  function()
+    require("heirline-components.all").heirline.buffer_picker(function(bufnr)
+      vim.cmd.split()
+      vim.api.nvim_win_set_buf(0, bufnr)
+    end)
+  end,
+  desc = "Horizontal split buffer from tabline",
+}
+maps.n["<leader>b|"] = {
+  function()
+    require("heirline-components.all").heirline.buffer_picker(function(bufnr)
+      vim.cmd.vsplit()
+      vim.api.nvim_win_set_buf(0, bufnr)
+    end)
+  end,
+  desc = "Vertical split buffer from tabline",
+}
+
+-- quick movement aliases
+maps.n["<C-k>"] = {
+  function()
+    require("heirline-components.buffer").nav(vim.v.count > 0 and vim.v.count or 1)
+  end,
+  desc = "Next buffer",
+}
+maps.n["<C-j>"] = {
+  function()
+    require("heirline-components.buffer").nav(-(vim.v.count > 0 and vim.v.count or 1))
+  end,
+  desc = "Previous buffer",
+}
+maps.n["<S-Down>"] = {
+  function() vim.api.nvim_feedkeys("5j", "n", true) end,
+  desc = "Fast move down",
+}
+maps.n["<S-Up>"] = {
+  function() vim.api.nvim_feedkeys("5k", "n", true) end,
+  desc = "Fast move up",
+}
+
+-- tabs
+maps.n["]t"] = { function() vim.cmd.tabnext() end, desc = "Next tab" }
+maps.n["[t"] = { function() vim.cmd.tabprevious() end, desc = "Previous tab" }
+
+-- zen mode
+if is_available "zen-mode.nvim" then
+  maps.n["<leader>uz"] =
+  { function() ui.toggle_zen_mode() end, desc = "Zen mode" }
+end
+
+-- ui toggles [ui] ---------------------------------------------------------
+maps.n["<leader>u"] = icons.u
+if is_available "nvim-autopairs" then
+  maps.n["<leader>ua"] = { ui.toggle_autopairs, desc = "Autopairs" }
+end
+maps.n["<leader>ub"] = { ui.toggle_background, desc = "Background" }
+if is_available "nvim-cmp" then
+  maps.n["<leader>uc"] = { ui.toggle_cmp, desc = "Autocompletion" }
+end
+if is_available "nvim-colorizer.lua" then
+  maps.n["<leader>uC"] =
+  { "<cmd>ColorizerToggle<cr>", desc = "color highlight" }
+end
+maps.n["<leader>ud"] = { ui.toggle_diagnostics, desc = "Diagnostics" }
+maps.n["<leader>uD"] = { ui.set_indent, desc = "Change indent setting" }
+maps.n["<leader>ug"] = { ui.toggle_signcolumn, desc = "Signcolumn" }
+maps.n["<leader>ul"] = { ui.toggle_statusline, desc = "Statusline" }
+maps.n["<leader>un"] = { ui.change_number, desc = "Change line numbering" }
+maps.n["<leader>uP"] = { ui.toggle_paste, desc = "Paste mode" }
+maps.n["<leader>us"] = { ui.toggle_spell, desc = "Spellcheck" }
+maps.n["<leader>uS"] = { ui.toggle_conceal, desc = "Conceal" }
+maps.n["<leader>ut"] = { ui.toggle_tabline, desc = "Tabline" }
+maps.n["<leader>uu"] = { ui.toggle_url_effect, desc = "URL highlight" }
+maps.n["<leader>uw"] = { ui.toggle_wrap, desc = "Wrap" }
+maps.n["<leader>uy"] = { ui.toggle_buffer_syntax, desc = "Syntax highlight (buffer)" }
+maps.n["<leader>uh"] = { ui.toggle_foldcolumn, desc = "Foldcolumn" }
+maps.n["<leader>uN"] =
+{ ui.toggle_ui_notifications, desc = "UI notifications" }
+if is_available "lsp_signature.nvim" then
+  maps.n["<leader>up"] = { ui.toggle_lsp_signature, desc = "LSP signature" }
+end
+if is_available "mini.animate" then
+  maps.n["<leader>uA"] = { ui.toggle_animations, desc = "Animations" }
+end
+
+-- shifted movement keys ----------------------------------------------------
+maps.n["<S-Down>"] = {
+  function() vim.api.nvim_feedkeys("7j", "n", true) end,
+  desc = "Fast move down",
+}
+maps.n["<S-Up>"] = {
+  function() vim.api.nvim_feedkeys("7k", "n", true) end,
+  desc = "Fast move up",
+}
+maps.n["<S-PageDown>"] = {
+  function()
+    local current_line = vim.fn.line "."
+    local total_lines = vim.fn.line "$"
+    local target_line = current_line + 1 + math.floor(total_lines * 0.20)
+    if target_line > total_lines then target_line = total_lines end
+    vim.api.nvim_win_set_cursor(0, { target_line, 0 })
+    vim.cmd "normal! zz"
+  end,
+  desc = "Page down exactly a 20% of the total size of the buffer",
+}
+maps.n["<S-PageUp>"] = {
+  function()
+    local current_line = vim.fn.line "."
+    local target_line = current_line - 1 - math.floor(vim.fn.line "$" * 0.20)
+    if target_line < 1 then target_line = 1 end
+    vim.api.nvim_win_set_cursor(0, { target_line, 0 })
+    vim.cmd "normal! zz"
+  end,
+  desc = "Page up exactly 20% of the total size of the buffer",
+}
+
+-- cmdline autocompletion ---------------------------------------------------
+maps.c["<Up>"] = {
+  function() return vim.fn.wildmenumode() == 1 and "<Left>" or "<Up>" end,
+  noremap = true,
+  expr = true,
+  desc = "Wildmenu fix for neovim bug #9953",
+}
+maps.c["<Down>"] = {
+  function() return vim.fn.wildmenumode() == 1 and "<Right>" or "<Down>" end,
+  noremap = true,
+  expr = true,
+  desc = "Wildmenu fix for neovim bug #9953",
+}
+maps.c["<Left>"] = {
+  function() return vim.fn.wildmenumode() == 1 and "<Up>" or "<Left>" end,
+  noremap = true,
+  expr = true,
+  desc = "Wildmenu fix for neovim bug #9953",
+}
+maps.c["<Right>"] = {
+  function() return vim.fn.wildmenumode() == 1 and "<Down>" or "<Right>" end,
+  noremap = true,
+  expr = true,
+  desc = "Wildmenu fix for neovim bug #9953",
+}
+
+-- special cases ------------------------------------------------------------
+vim.api.nvim_create_autocmd("BufWinEnter", {
+  desc = "Make q close help, man, quickfix, dap floats",
+  callback = function(args)
+    local buftype =
+        vim.api.nvim_get_option_value("buftype", { buf = args.buf })
+    if vim.tbl_contains({ "help", "nofile", "quickfix" }, buftype) then
+      vim.keymap.set(
+        "n", "q", "<cmd>close<cr>",
+        { buffer = args.buf, silent = true, nowait = true }
+      )
+    end
+  end,
+})
+vim.api.nvim_create_autocmd("CmdwinEnter", {
+  desc = "Make q close command history (q: and q?)",
+  callback = function(args)
+    vim.keymap.set(
+      "n", "q", "<cmd>close<cr>",
+      { buffer = args.buf, silent = true, nowait = true }
+    )
+  end,
+})
+
+-- -------------------------------------------------------------------------
+--
+-- ## Plugin bindings
+--
+-- -------------------------------------------------------------------------
+
+-- alpha-nvim --------------------------------------------------------------
+if is_available "alpha-nvim" then
+  maps.n["<leader>h"] = {
+    function()
+      local wins = vim.api.nvim_tabpage_list_wins(0)
+      if #wins > 1
+          and vim.api.nvim_get_option_value("filetype", { win = wins[1] })
+          == "neo-tree"
+      then
+        vim.fn.win_gotoid(wins[2]) -- go to non-neo-tree window to toggle alpha
+      end
+      require("alpha").start(false, require("alpha").default_config)
+      vim.b.miniindentscope_disable = true
+    end,
+    desc = "Home screen",
+  }
+end
+
+-- [git] -----------------------------------------------------------
+-- gitsigns.nvim
+maps.n["<leader>g"] = icons.g
+if is_available "gitsigns.nvim" then
+  maps.n["<leader>g"] = icons.g
+  maps.n["]g"] =
+  { function() require("gitsigns").next_hunk() end, desc = "Next Git hunk" }
+  maps.n["[g"] = {
+    function() require("gitsigns").prev_hunk() end,
+    desc = "Previous Git hunk",
+  }
+  maps.n["<leader>gl"] = {
+    function() require("gitsigns").blame_line() end,
+    desc = "View Git blame",
+  }
+  maps.n["<leader>gL"] = {
+    function() require("gitsigns").blame_line { full = true } end,
+    desc = "View full Git blame",
+  }
+  maps.n["<leader>gp"] = {
+    function() require("gitsigns").preview_hunk() end,
+    desc = "Preview Git hunk",
+  }
+  maps.n["<leader>gh"] = {
+    function() require("gitsigns").reset_hunk() end,
+    desc = "Reset Git hunk",
+  }
+  maps.n["<leader>gr"] = {
+    function() require("gitsigns").reset_buffer() end,
+    desc = "Reset Git buffer",
+  }
+  maps.n["<leader>gs"] = {
+    function() require("gitsigns").stage_hunk() end,
+    desc = "Stage Git hunk",
+  }
+  maps.n["<leader>gS"] = {
+    function() require("gitsigns").stage_buffer() end,
+    desc = "Stage Git buffer",
+  }
+  maps.n["<leader>gu"] = {
+    function() require("gitsigns").undo_stage_hunk() end,
+    desc = "Unstage Git hunk",
+  }
+  maps.n["<leader>gd"] = {
+    function() require("gitsigns").diffthis() end,
+    desc = "View Git diff",
+  }
+end
+-- git fugitive
+if is_available "vim-fugitive" then
+  maps.n["<leader>gP"] = {
+    function() vim.cmd ":GBrowse" end,
+    desc = "Open in github ",
+  }
+end
+-- git client
+if vim.fn.executable "lazygit" == 1 then -- if lazygit exists, show it
+  maps.n["<leader>gg"] = {
+    function()
+      local git_dir = vim.fn.finddir(".git", vim.fn.getcwd() .. ";")
+      if git_dir ~= "" then
+        vim.cmd "TermExec cmd='lazygit && exit'"
+      else
+        utils.notify("Not a git repository", vim.log.levels.WARN)
+      end
+    end,
+    desc = "ToggleTerm lazygit",
+  }
+end
+if vim.fn.executable "gitui" == 1 then -- if gitui exists, show it
+  maps.n["<leader>gg"] = {
+    function()
+      local git_dir = vim.fn.finddir(".git", vim.fn.getcwd() .. ";")
+      if git_dir ~= "" then
+        if vim.fn.executable "keychain" == 1 then
+          vim.cmd 'TermExec cmd="eval `keychain --eval ~/.ssh/github.key` && gitui && exit"'
+        else
+          vim.cmd "TermExec cmd='gitui && exit'"
+        end
+      else
+        utils.notify("Not a git repository", vim.log.levels.WARN)
+      end
+    end,
+    desc = "ToggleTerm gitui",
+  }
+end
+
+-- file browsers ------------------------------------
+-- ranger
+if is_available "rnvimr" then
+  maps.n["<leader>r"] = { "<cmd>RnvimrToggle<cr>", desc = "Ranger" }
+end
+
+-- neotree
+if is_available "neo-tree.nvim" then
+  maps.n["<leader>e"] = { "<cmd>Neotree toggle<cr>", desc = "Neotree" }
+end
+
+-- session manager ---------------------------------------------------------
+if is_available "neovim-session-manager" then
+  maps.n["<leader>S"] = icons.S
+  maps.n["<leader>Sl"] = {
+    "<cmd>SessionManager! load_last_session<cr>",
+    desc = "Load last session",
+  }
+  maps.n["<leader>Ss"] = {
+    "<cmd>SessionManager! save_current_session<cr>",
+    desc = "Save this session",
+  }
+  maps.n["<leader>Sd"] =
+  { "<cmd>SessionManager! delete_session<cr>", desc = "Delete session" }
+  maps.n["<leader>Sf"] =
+  { "<cmd>SessionManager! load_session<cr>", desc = "Search sessions" }
+  maps.n["<leader>S."] = {
+    "<cmd>SessionManager! load_current_dir_session<cr>",
+    desc = "Load current directory session",
+  }
+end
+if is_available "resession.nvim" then
+  maps.n["<leader>S"] = icons.S
+  maps.n["<leader>Sl"] = {
+    function() require("resession").load "Last Session" end,
+    desc = "Load last session",
+  }
+  maps.n["<leader>Ss"] =
+  { function() require("resession").save() end, desc = "Save this session" }
+  maps.n["<leader>St"] = {
+    function() require("resession").save_tab() end,
+    desc = "Save this tab's session",
+  }
+  maps.n["<leader>Sd"] =
+  { function() require("resession").delete() end, desc = "Delete a session" }
+  maps.n["<leader>Sf"] =
+  { function() require("resession").load() end, desc = "Load a session" }
+  maps.n["<leader>S."] = {
+    function()
+      require("resession").load(vim.fn.getcwd(), { dir = "dirsession" })
+    end,
+    desc = "Load current directory session",
+  }
+end
+
+-- smart-splits.nivm
+if is_available "smart-splits.nvim" then
+  maps.n["<C-h>"] = {
+    function() require("smart-splits").move_cursor_left() end,
+    desc = "Move to left split",
+  }
+  maps.n["<C-j>"] = {
+    function() require("smart-splits").move_cursor_down() end,
+    desc = "Move to below split",
+  }
+  maps.n["<C-k>"] = {
+    function() require("smart-splits").move_cursor_up() end,
+    desc = "Move to above split",
+  }
+  maps.n["<C-l>"] = {
+    function() require("smart-splits").move_cursor_right() end,
+    desc = "Move to right split",
+  }
+  maps.n["<C-Up>"] = {
+    function() require("smart-splits").resize_up() end,
+    desc = "Resize split up",
+  }
+  maps.n["<C-Down>"] = {
+    function() require("smart-splits").resize_down() end,
+    desc = "Resize split down",
+  }
+  maps.n["<C-Left>"] = {
+    function() require("smart-splits").resize_left() end,
+    desc = "Resize split left",
+  }
+  maps.n["<C-Right>"] = {
+    function() require("smart-splits").resize_right() end,
+    desc = "Resize split right",
+  }
+else
+  maps.n["<C-h>"] = { "<C-w>h", desc = "Move to left split" }
+  maps.n["<C-j>"] = { "<C-w>j", desc = "Move to below split" }
+  maps.n["<C-k>"] = { "<C-w>k", desc = "Move to above split" }
+  maps.n["<C-l>"] = { "<C-w>l", desc = "Move to right split" }
+  maps.n["<C-Up>"] = { "<cmd>resize -2<CR>", desc = "Resize split up" }
+  maps.n["<C-Down>"] = { "<cmd>resize +2<CR>", desc = "Resize split down" }
+  maps.n["<C-Left>"] =
+  { "<cmd>vertical resize -2<CR>", desc = "Resize split left" }
+  maps.n["<C-Right>"] =
+  { "<cmd>vertical resize +2<CR>", desc = "Resize split right" }
+end
+
+-- aerial.nvimm ------------------------------------------------------------
+if is_available "aerial.nvim" then
+  maps.n["<leader>i"] =
+  { function() require("aerial").toggle() end, desc = "Aerial" }
+end
+
+-- letee-calltree.nvimm ------------------------------------------------------------
+if is_available "litee-calltree.nvim" then
+  -- For every buffer, look for the one with filetype "calltree" and focus it.
+  local calltree_delay = 1500 -- first run? wait a bit longer.
+  local function focus_calltree()
+    -- Note: No go to the previous cursor position, press ctrl+i / ctrl+o
+    vim.defer_fn(function()
+      for _, win in ipairs(vim.api.nvim_list_wins()) do
+        local buf = vim.api.nvim_win_get_buf(win)
+        local ft = vim.api.nvim_get_option_value('filetype', { buf = buf })
+
+        if ft == "calltree" then
+          vim.api.nvim_set_current_win(win)
+          return true
+        end
+      end
+    end, calltree_delay)
+    calltree_delay = 100
+  end
+  maps.n["gj"] = {
+    function()
+      vim.lsp.buf.incoming_calls()
+      focus_calltree()
+    end,
+    desc = "Call tree (incoming)"
+  }
+  maps.n["gJ"] =
+  {
+    function()
+      vim.lsp.buf.outgoing_calls()
+      focus_calltree()
+    end,
+    desc = "Call tree (outgoing)"
+  }
+end
+
+-- telescope.nvim [find] ----------------------------------------------------
+if is_available "telescope.nvim" then
+  maps.n["<leader>f"] = icons.f
+  maps.n["<leader>gb"] = {
+    function() require("telescope.builtin").git_branches() end,
+    desc = "Git branches",
+  }
+  maps.n["<leader>gc"] = {
+    function()
+      require("telescope.builtin").git_commits()
+    end,
+    desc = "Git commits (repository)"
+  }
+  maps.n["<leader>gC"] = {
+    function()
+      require("telescope.builtin").git_bcommits()
+    end,
+    desc = "Git commits (current file)"
+  }
+  maps.n["<leader>gt"] = {
+    function() require("telescope.builtin").git_status() end,
+    desc = "Git status",
+  }
+  maps.n["<leader>f<CR>"] = {
+    function() require("telescope.builtin").resume() end,
+    desc = "Resume previous search",
+  }
+  maps.n["<leader>f'"] = {
+    function() require("telescope.builtin").marks() end,
+    desc = "Find marks",
+  }
+  maps.n["<leader>fa"] = {
+    function()
+      local cwd = vim.fn.stdpath "config" .. "/.."
+      local search_dirs = { vim.fn.stdpath "config" }
+      if #search_dirs == 1 then cwd = search_dirs[1] end -- if only one directory, focus cwd
+      require("telescope.builtin").find_files {
+        prompt_title = "Config Files",
+        search_dirs = search_dirs,
+        cwd = cwd,
+        follow = true,
+      } -- call telescope
+    end,
+    desc = "Find nvim config files",
+  }
+  maps.n["<leader>fB"] = {
+    function() require("telescope.builtin").buffers() end,
+    desc = "Find buffers",
+  }
+  maps.n["<leader>fw"] = {
+    function() require("telescope.builtin").grep_string() end,
+    desc = "Find word under cursor in project",
+  }
+  maps.n["<leader>fC"] = {
+    function() require("telescope.builtin").commands() end,
+    desc = "Find commands",
+  }
+  -- Let's disable this. It is way too imprecise. Use rnvimr instead.
+  -- maps.n["<leader>ff"] = {
+  --   function()
+  --     require("telescope.builtin").find_files { hidden = true, no_ignore = true }
+  --   end,
+  --   desc = "Find all files",
+  -- }
+  -- maps.n["<leader>fF"] = {
+  --   function() require("telescope.builtin").find_files() end,
+  --   desc = "Find files (no hidden)",
+  -- }
+  maps.n["<leader>fh"] = {
+    function() require("telescope.builtin").help_tags() end,
+    desc = "Find help",
+  }
+  maps.n["<leader>fk"] = {
+    function() require("telescope.builtin").keymaps() end,
+    desc = "Find keymaps",
+  }
+  maps.n["<leader>fm"] = {
+    function() require("telescope.builtin").man_pages() end,
+    desc = "Find man",
+  }
+  if is_available "nvim-notify" then
+    maps.n["<leader>fn"] = {
+      function() require("telescope").extensions.notify.notify() end,
+      desc = "Find notifications",
+    }
+  end
+  maps.n["<leader>fo"] = {
+    function() require("telescope.builtin").oldfiles() end,
+    desc = "Find recent",
+  }
+  maps.n["<leader>fv"] = {
+    function() require("telescope.builtin").registers() end,
+    desc = "Find vim registers",
+  }
+  maps.n["<leader>ft"] = {
+    function()
+      -- load color schemes before listing them
+      pcall(vim.api.nvim_command, "doautocmd User LoadColorSchemes")
+
+      -- Open telescope
+      pcall(require("telescope.builtin").colorscheme, {
+        enable_preview = true,
+        ignore_builtins = true
+      })
+    end,
+    desc = "Find themes",
+  }
+  maps.n["<leader>ff"] = {
+    function()
+      require("telescope.builtin").live_grep {
+        additional_args = function(args)
+          return vim.list_extend(args, { "--hidden", "--no-ignore" })
+        end,
+      }
+    end,
+    desc = "Find words in project",
+  }
+  maps.n["<leader>fF"] = {
+    function() require("telescope.builtin").live_grep() end,
+    desc = "Find words in project (no hidden)",
+  }
+  maps.n["<leader>f/"] = {
+    function() require("telescope.builtin").current_buffer_fuzzy_find() end,
+    desc = "Find words in current buffer"
+  }
+
+  -- Some lsp keymappings are here because they depend on telescope
+  maps.n["<leader>l"] = icons.l
+  maps.n["<leader>ls"] = {
+    function()
+      local aerial_avail, _ = pcall(require, "aerial")
+      if aerial_avail then
+        require("telescope").extensions.aerial.aerial()
+      else
+        require("telescope.builtin").lsp_document_symbols()
+      end
+    end,
+    desc = "Search symbol in buffer", -- Useful to find every time a variable is assigned.
+  }
+  maps.n["gs"] = {
+    function()
+      local aerial_avail, _ = pcall(require, "aerial")
+      if aerial_avail then
+        require("telescope").extensions.aerial.aerial()
+      else
+        require("telescope.builtin").lsp_document_symbols()
+      end
+    end,
+    desc = "Search symbol in buffer", -- Useful to find every time a variable is assigned.
+  }
+
+  -- extra - project.nvim
+  if is_available "project.nvim" then
+    maps.n["<leader>fp"] = {
+      function() vim.cmd "Telescope projects" end,
+      desc = "Find project",
+    }
+  end
+
+  -- extra - spectre.nvim (search and replace in project)
+  if is_available "nvim-spectre" then
+    maps.n["<leader>fr"] = {
+      function() require("spectre").toggle() end,
+      desc = "Find and replace word in project",
+    }
+    maps.n["<leader>fb"] = {
+      function() require("spectre").toggle { path = vim.fn.expand "%:t:p" } end,
+      desc = "Find and replace word in buffer",
+    }
+  end
+
+  -- extra - luasnip
+  if is_available "LuaSnip" and is_available "telescope-luasnip.nvim" then
+    maps.n["<leader>fs"] = {
+      function() require("telescope").extensions.luasnip.luasnip {} end,
+      desc = "Find snippets",
+    }
+  end
+
+  -- extra - nvim-neoclip (neovim internal clipboard)
+  --         Specially useful if you disable the shared clipboard in options.
+  if is_available "nvim-neoclip.lua" then
+    maps.n["<leader>fy"] = {
+      function() require("telescope").extensions.neoclip.default() end,
+      desc = "Find yank history",
+    }
+    maps.n["<leader>fq"] = {
+      function() require("telescope").extensions.macroscope.default() end,
+      desc = "Find macro history",
+    }
+  end
+
+  -- extra - undotree
+  if is_available "telescope-undo.nvim" then
+    maps.n["<leader>fu"] = {
+      function() require("telescope").extensions.undo.undo() end,
+      desc = "Find in undo tree",
+    }
+  end
+
+  -- extra - compiler
+  if is_available "compiler.nvim" and is_available "overseer.nvim" then
+    maps.n["<leader>m"] = icons.c
+    maps.n["<leader>mm"] = {
+      function() vim.cmd "CompilerOpen" end,
+      desc = "Open compiler",
+    }
+    maps.n["<leader>mr"] = {
+      function() vim.cmd "CompilerRedo" end,
+      desc = "Compiler redo",
+    }
+    maps.n["<leader>mt"] = {
+      function() vim.cmd "CompilerToggleResults" end,
+      desc = "compiler results",
+    }
+    maps.n["<F6>"] = {
+      function() vim.cmd "CompilerOpen" end,
+      desc = "Open compiler",
+    }
+    maps.n["<S-F6>"] = {
+      function() vim.cmd "CompilerRedo" end,
+      desc = "Compiler redo",
+    }
+    maps.n["<S-F7>"] = {
+      function() vim.cmd "CompilerToggleResults" end,
+      desc = "compiler resume",
+    }
+  end
+end
+
+-- toggleterm.nvim ----------------------------------------------------------
+if is_available "toggleterm.nvim" then
+  maps.n["<leader>t"] = icons.t
+  maps.n["<leader>tt"] =
+  { "<cmd>ToggleTerm direction=float<cr>", desc = "ToggleTerm float" }
+  maps.n["<leader>th"] = {
+    "<cmd>ToggleTerm size=10 direction=horizontal<cr>",
+    desc = "Toggleterm horizontal split",
+  }
+  maps.n["<leader>tv"] = {
+    "<cmd>ToggleTerm size=80 direction=vertical<cr>",
+    desc = "Toggleterm vertical split",
+  }
+  maps.n["<F7>"] = { "<cmd>ToggleTerm<cr>", desc = "terminal" }
+  maps.t["<F7>"] = maps.n["<F7>"]
+  maps.n["<C-'>"] = maps.n["<F7>"] -- requires terminal that supports binding <C-'>
+  maps.t["<C-'>"] = maps.n["<F7>"] -- requires terminal that supports binding <C-'>
+end
+
+-- extra - improved terminal navigation
+maps.t["<C-h>"] =
+{ "<cmd>wincmd h<cr>", desc = "Terminal left window navigation" }
+maps.t["<C-j>"] =
+{ "<cmd>wincmd j<cr>", desc = "Terminal down window navigation" }
+maps.t["<C-k>"] =
+{ "<cmd>wincmd k<cr>", desc = "Terminal up window navigation" }
+maps.t["<C-l>"] =
+{ "<cmd>wincmd l<cr>", desc = "Terminal right window navigation" }
+
+-- dap.nvim [debugger] -----------------------------------------------------
+-- Depending your terminal some F keys may not work. To fix it:
+-- modified function keys found with `showkey -a` in the terminal to get key code
+-- run `nvim -V3log +quit` and search through the "Terminal info" in the `log` file for the correct keyname
+if is_available "nvim-dap" then
+  maps.n["<leader>d"] = icons.d
+  maps.x["<leader>d"] = icons.d
+
+  -- F keys
+  maps.n["<F5>"] = {
+    function()
+      require("dap").continue()
+    end,
+    desc = "Debugger: Start"
+  }
+  maps.n["<S-F5>"] =
+  { function() require("dap").terminate() end, desc = "Debugger: Stop" }
+  maps.n["<C-F5>"] = {
+    function() require("dap").restart_frame() end, desc = "Debugger: Restart" }
+  maps.n["<F9>"] = {
+    function() require("dap").toggle_breakpoint() end, desc = "Debugger: Toggle Breakpoint" }
+  maps.n["<S-F9>"] = {
+    function()
+      vim.ui.input({ prompt = "Condition: " }, function(condition)
+        if condition then require("dap").set_breakpoint(condition) end
+      end)
+    end,
+    desc = "Debugger: Conditional Breakpoint",
+  }
+  maps.n["<F10>"] =
+  { function() require("dap").step_over() end, desc = "Debugger: Step Over" }
+  maps.n["<S-F10>"] =
+  { function() require("dap").step_back() end, desc = "Debugger: Step Back" }
+  maps.n["<F11>"] =
+  { function() require("dap").step_into() end, desc = "Debugger: Step Into" }
+  maps.n["<S-11>"] =
+  { function() require("dap").step_out() end, desc = "Debugger: Step Out" }
+
+  -- Space + d
+  maps.n["<leader>db"] = {
+    function() require("dap").toggle_breakpoint() end,
+    desc = "Breakpoint (F9)",
+  }
+  maps.n["<leader>dB"] = {
+    function() require("dap").clear_breakpoints() end,
+    desc = "Clear Breakpoints",
+  }
+  maps.n["<leader>dc"] =
+  { function() require("dap").continue() end, desc = "Start/Continue (F5)" }
+  maps.n["<leader>dC"] = {
+    function()
+      vim.ui.input({ prompt = "Condition: " }, function(condition)
+        if condition then require("dap").set_breakpoint(condition) end
+      end)
+    end,
+    desc = "Conditional Breakpoint (S-F9)",
+  }
+  maps.n["<leader>do"] =
+  { function() require("dap").step_over() end, desc = "Step Over (F10)" }
+  maps.n["<leader>do"] =
+  { function() require("dap").step_back() end, desc = "Step Back (S-F10)" }
+  maps.n["<leader>db"] =
+  { function() require("dap").step_into() end, desc = "Step Into (F11)" }
+  maps.n["<leader>dO"] =
+  { function() require("dap").step_out() end, desc = "Step Out (S-F11)" }
+  maps.n["<leader>dq"] =
+  { function() require("dap").close() end, desc = "Close Session" }
+  maps.n["<leader>dQ"] = {
+    function() require("dap").terminate() end,
+    desc = "Terminate Session (S-F5)",
+  }
+  maps.n["<leader>dp"] =
+  { function() require("dap").pause() end, desc = "Pause" }
+  maps.n["<leader>dr"] =
+  { function() require("dap").restart_frame() end, desc = "Restart (C-F5)" }
+  maps.n["<leader>dR"] =
+  { function() require("dap").repl.toggle() end, desc = "REPL" }
+  maps.n["<leader>ds"] =
+  { function() require("dap").run_to_cursor() end, desc = "Run To Cursor" }
+
+  if is_available "nvim-dap-ui" then
+    maps.n["<leader>dE"] = {
+      function()
+        vim.ui.input({ prompt = "Expression: " }, function(expr)
+          if expr then require("dapui").eval(expr, { enter = true }) end
+        end)
+      end,
+      desc = "Evaluate Input",
+    }
+    maps.x["<leader>dE"] =
+    { function() require("dapui").eval() end, desc = "Evaluate Input" }
+    maps.n["<leader>du"] =
+    { function() require("dapui").toggle() end, desc = "Debugger UI" }
+    maps.n["<leader>dh"] = {
+      function() require("dap.ui.widgets").hover() end,
+      desc = "Debugger Hover",
+    }
+  end
+end
+
+-- testing [tests] -------------------------------------------------
+-- neotest
+maps.n["<leader>T"] = icons.tt
+maps.x["<leader>T"] = icons.tt
+if is_available "neotest" then
+  maps.n["<leader>Tu"] = {
+    function() require("neotest").run.run() end,
+    desc = "Unit",
+  }
+  maps.n["<leader>Ts"] = {
+    function() require("neotest").run.stop() end,
+    desc = "Stop unit",
+  }
+  maps.n["<leader>Tf"] = {
+    function() require("neotest").run.run(vim.fn.expand "%") end,
+    desc = "File",
+  }
+  maps.n["<leader>Td"] = {
+    function() require("neotest").run.run { strategy = "dap" } end,
+    desc = "Unit in debugger",
+  }
+  maps.n["<leader>Tt"] = {
+    function() require("neotest").summary.toggle() end,
+    desc = "Neotest summary",
+  }
+  maps.n["<leader>TT"] = {
+    function() require("neotest").output_panel.toggle() end,
+    desc = "Output panel",
+  }
+end
+
+-- Extra - nvim-coverage
+--         Your project must generate coverage/lcov.info for this to work.
+--
+--         On jest, make sure your packages.json file has this:
+--         "test": "jest --coverage"
+--
+--         If you use other framework or language, refer to nvim-coverage docs:
+--         https://github.com/andythigpen/nvim-coverage/blob/main/doc/nvim-coverage.txt
+if is_available "nvim-coverage" then
+  maps.n["<leader>Tc"] = {
+    function()
+      require("coverage").load(false)
+      require("coverage").summary()
+    end,
+    desc = "Coverage",
+  }
+  maps.n["<leader>TC"] = {
+    function()
+      ui.toggle_coverage_signs()
+    end,
+    desc = "Coverage signs (toggle)",
+  }
+end
+
+-- Extra - nodejs testing commands
+maps.n["<leader>Ta"] = {
+  function() vim.cmd "TestNodejs" end,
+  desc = "All",
+}
+maps.n["<leader>Te"] = {
+  function() vim.cmd "TestNodejsE2e" end,
+  desc = "E2e",
+}
+
+-- nvim-ufo [code folding] --------------------------------------------------
+if is_available "nvim-ufo" then
+  maps.n["zR"] =
+  { function() require("ufo").openAllFolds() end, desc = "Open all folds" }
+  maps.n["zM"] =
+  { function() require("ufo").closeAllFolds() end, desc = "Close all folds" }
+  maps.n["zr"] = {
+    function() require("ufo").openFoldsExceptKinds() end,
+    desc = "Fold less",
+  }
+  maps.n["zm"] =
+  { function() require("ufo").closeFoldsWith() end, desc = "Fold more" }
+  maps.n["zp"] = {
+    function() require("ufo").peekFoldedLinesUnderCursor() end,
+    desc = "Peek fold",
+  }
+  maps.n["zn"] =
+  {
+    function() require("ufo").openFoldsExceptKinds({ 'comment' }) end,
+    desc = "Fold comments"
+  }
+  maps.n["zN"] =
+  {
+    function() require("ufo").openFoldsExceptKinds({ 'region' }) end,
+    desc = "Fold region"
+  }
+end
+
+-- code docmentation [docs] -------------------------------------------------
+
+if is_available "markdown-preview.nivm" or is_available "markmap.nvim" or is_available "dooku.nvim" then
+  maps.n["<leader>D"] = icons.dc
+
+  -- Markdown preview
+  if is_available "markdown-preview.nvim" then
+    maps.n["<leader>Dp"] = {
+      function() vim.cmd "MarkdownPreview" end,
+      desc = "Markdown preview",
+    }
+  end
+
+  -- Markdown Mindmap
+  if is_available "markmap.nvim" then
+    maps.n["<leader>Dm"] = {
+      function()
+        if is_android then
+          vim.cmd "MarkmapWatch"
+        else
+          vim.cmd "MarkmapOpen"
+        end
+      end,
+      desc = "Markmap",
+    }
+  end
+
+  if is_available "dooku.nvim" then
+    maps.n["<leader>Dd"] = {
+      function() vim.cmd ":DookuGenerate" end,
+      desc = "Open documentation",
+    }
+  end
+end
+
+-- [neural] -----------------------------------------------------------------
+if is_available "neural" or is_available "copilot" then
+  maps.n["<leader>a"] = {
+    function() require("neural").prompt() end,
+    desc = "Ask chatgpt",
+  }
+end
+
+-- hop.nivm ----------------------------------------------------------------
+if is_available "hop.nvim" then
+  -- Note that Even though we are using ENTER for hop, you can still select items
+  -- from special menus like 'quickfix', 'q?' and 'q:' with <C+ENTER>.
+
+  maps.n["<C-m>"] = { -- The terminal undersand C-m and ENTER as the same key.
+    function()
+      require("hop")
+      vim.cmd("silent! HopWord")
+    end,
+    desc = "Hop to word",
+  }
+  maps.x["<C-m>"] = { -- The terminal undersand C-m and ENTER as the same key.
+    function()
+      require("hop")
+      vim.cmd("silent! HopWord")
+    end,
+    desc = "Hop to word",
+  }
+end
+
+-- mason-lspconfig.nvim [lsp] -------------------------------------------------
+-- WARNING: Don't delete this section, or you won't have LSP keymappings.
+
+--A function we call from the script to start lsp.
+--@return table lsp_mappings
+function M.lsp_mappings(client, bufnr)
+  -- Helper function to check if any active LSP clients
+  -- given a filter provide a specific capability.
+  -- @param capability string The server capability to check for (example: "documentFormattingProvider").
+  -- @param filter vim.lsp.get_clients.filter|nil A valid get_clients filter (see function docs).
+  -- @return boolean # `true` if any of the clients provide the capability.
+  local function has_capability(capability, filter)
+    for _, lsp_client in ipairs(vim.lsp.get_clients(filter)) do
+      if lsp_client.supports_method(capability) then return true end
+    end
+    return false
+  end
+
+  local lsp_mappings = require("base.utils").get_mappings_template()
+
+  -- Diagnostics
+  lsp_mappings.n["<leader>ld"] = { function() vim.diagnostic.open_float() end, desc = "Hover diagnostics" }
+  lsp_mappings.n["[d"] = { function()
+      -- TODO: Delete after dropping nvim 0.10 support.
+      if vim.fn.has('nvim-0.11') == 1 then vim.diagnostic.jump({ count = -1 })
+      else vim.diagnostic.goto_prev() end end, desc = "Previous diagnostic"
+  }
+  lsp_mappings.n["]d"] = { function()
+    -- TODO: Delete after dropping nvim 0.10 support.
+    if vim.fn.has('nvim-0.11') == 1 then vim.diagnostic.jump({ count = 1 })
+    else vim.diagnostic.goto_next() end end, desc = "Next diagnostic" }
+
+  -- Diagnostics
+  lsp_mappings.n["gl"] = { function() vim.diagnostic.open_float() end, desc = "Hover diagnostics" }
+  if is_available "telescope.nvim" then
+    lsp_mappings.n["<leader>lD"] =
+      { function() require("telescope.builtin").diagnostics() end, desc = "Diagnostics" }
+  end
+
+  -- LSP info
+  if is_available "mason-lspconfig.nvim" then
+    lsp_mappings.n["<leader>li"] = { "<cmd>LspInfo<cr>", desc = "LSP information" }
+  end
+
+  if is_available "none-ls.nvim" then
+    lsp_mappings.n["<leader>lI"] = { "<cmd>NullLsInfo<cr>", desc = "Null-ls information" }
+  end
+
+  -- Code actions
+  lsp_mappings.n["<leader>la"] = {
+    function() vim.lsp.buf.code_action() end,
+    desc = "LSP code action",
+  }
+  lsp_mappings.v["<leader>la"] = lsp_mappings.n["<leader>la"]
+
+  -- Codelens
+  utils.add_autocmds_to_buffer("lsp_codelens_refresh", bufnr, {
+    events = { "InsertLeave" },
+    desc = "Refresh codelens",
+    callback = function(args)
+      if client.supports_method "textDocument/codeLens" then
+        if vim.g.codelens_enabled then vim.lsp.codelens.refresh({ bufnr = args.buf }) end
+      end
+    end,
+  })
+  if client.supports_method "textDocument/codeLens" then -- on LspAttach
+    if vim.g.codelens_enabled then vim.lsp.codelens.refresh({ bufnr = 0 }) end
+  end
+
+  lsp_mappings.n["<leader>ll"] = {
+    function()
+      vim.lsp.codelens.run()
+      vim.lsp.codelens.refresh({ bufnr = 0 })
+    end,
+    desc = "LSP CodeLens run",
+  }
+  lsp_mappings.n["<leader>uL"] = {
+    function() ui.toggle_codelens() end,
+    desc = "CodeLens",
+  }
+
+  -- Formatting
+  local formatting = require("base.utils.lsp").formatting
+  lsp_mappings.n["<leader>lf"] = {
+    function()
+      vim.lsp.buf.format(M.format_opts)
+      vim.cmd('checktime') -- update buffer to reflect changes.
+    end,
+    desc = "Format buffer",
+  }
+  lsp_mappings.v["<leader>lf"] = lsp_mappings.n["<leader>lf"]
+
+  vim.api.nvim_buf_create_user_command(
+    bufnr,
+    "Format",
+    function() vim.lsp.buf.format(M.format_opts) end,
+    { desc = "Format file with LSP" }
+  )
+  local autoformat = formatting.format_on_save
+  local filetype = vim.api.nvim_get_option_value("filetype", { buf = bufnr })
+  if
+    autoformat.enabled
+    and (vim.tbl_isempty(autoformat.allow_filetypes or {}) or vim.tbl_contains(autoformat.allow_filetypes, filetype))
+    and (vim.tbl_isempty(autoformat.ignore_filetypes or {}) or not vim.tbl_contains(autoformat.ignore_filetypes, filetype))
+  then
+    utils.add_autocmds_to_buffer("lsp_auto_format", bufnr, {
+      events = "BufWritePre",
+      desc = "Autoformat on save",
+      callback = function()
+        if not has_capability("textDocument/formatting", { bufnr = bufnr }) then
+          utils.del_autocmds_from_buffer("lsp_auto_format", bufnr)
+          return
+        end
+        local autoformat_enabled = vim.b.autoformat_enabled
+        if autoformat_enabled == nil then autoformat_enabled = vim.g.autoformat_enabled end
+        if autoformat_enabled and ((not autoformat.filter) or autoformat.filter(bufnr)) then
+          vim.lsp.buf.format(vim.tbl_deep_extend("force", M.format_opts, { bufnr = bufnr }))
+        end
+      end,
+    })
+    lsp_mappings.n["<leader>uf"] = {
+      function() require("base.utils.ui").toggle_buffer_autoformat() end,
+      desc = "Autoformatting (buffer)",
+    }
+    lsp_mappings.n["<leader>uF"] = {
+      function() require("base.utils.ui").toggle_autoformat() end,
+      desc = "Autoformatting (global)",
+    }
+  end
+
+  -- Highlight references when cursor holds
+  utils.add_autocmds_to_buffer("lsp_document_highlight", bufnr, {
+    {
+      events = { "CursorHold", "CursorHoldI" },
+      desc = "highlight references when cursor holds",
+      callback = function()
+        if has_capability("textDocument/documentHighlight", { bufnr = bufnr }) then
+          vim.lsp.buf.document_highlight()
+        end
+      end,
+    },
+    {
+      events = { "CursorMoved", "CursorMovedI", "BufLeave" },
+      desc = "clear references when cursor moves",
+      callback = function() vim.lsp.buf.clear_references() end,
+    },
+  })
+
+  -- Other LSP mappings
+  lsp_mappings.n["<leader>lL"] = {
+    function() vim.api.nvim_command(':LspRestart') end,
+    desc = "LSP refresh",
+  }
+
+  -- Goto definition / declaration
+  lsp_mappings.n["gd"] = {
+    function() vim.lsp.buf.definition() end,
+    desc = "Goto definition of current symbol",
+  }
+  lsp_mappings.n["gD"] = {
+    function() vim.lsp.buf.declaration() end,
+    desc = "Goto declaration of current symbol",
+  }
+
+  -- Goto implementation
+  lsp_mappings.n["gI"] = {
+    function() vim.lsp.buf.implementation() end,
+    desc = "Goto implementation of current symbol",
+  }
+
+  -- Goto type definition
+  lsp_mappings.n["gT"] = {
+    function() vim.lsp.buf.type_definition() end,
+    desc = "Goto definition of current type",
+  }
+
+  -- Goto references
+  lsp_mappings.n["<leader>lR"] = {
+    function() vim.lsp.buf.references() end,
+    desc = "Hover references",
+  }
+  lsp_mappings.n["gr"] = {
+    function() vim.lsp.buf.references() end,
+    desc = "References of current symbol",
+  }
+
+  -- Goto help
+  lsp_mappings.n["gh"] = {
+    function() vim.lsp.buf.hover() end,
+    desc = "Hover help",
+  }
+  lsp_mappings.n["gH"] = {
+    function() vim.lsp.buf.signature_help() end,
+    desc = "Signature help",
+  }
+
+  lsp_mappings.n["<leader>lh"] = {
+    function() vim.lsp.buf.hover() end,
+    desc = "Hover help",
+  }
+  lsp_mappings.n["<leader>lH"] = {
+    function() vim.lsp.buf.signature_help() end,
+    desc = "Signature help",
+  }
+
+  -- Goto man
+  lsp_mappings.n["gm"] = {
+    function() vim.api.nvim_feedkeys("K", "n", false) end,
+    desc = "Hover man",
+  }
+  lsp_mappings.n["<leader>lm"] = {
+    function() vim.api.nvim_feedkeys("K", "n", false) end,
+    desc = "Hover man",
+  }
+
+  -- Rename symbol
+  lsp_mappings.n["<leader>lr"] = {
+    function() vim.lsp.buf.rename() end,
+    desc = "Rename current symbol",
+  }
+
+  -- Toggle inlay hints
+  if vim.b.inlay_hints_enabled == nil then vim.b.inlay_hints_enabled = vim.g.inlay_hints_enabled end
+  if vim.b.inlay_hints_enabled then vim.lsp.inlay_hint.enable(true, { bufnr = bufnr }) end
+  lsp_mappings.n["<leader>uH"] = {
+    function() require("base.utils.ui").toggle_buffer_inlay_hints(bufnr) end,
+    desc = "LSP inlay hints (buffer)",
+  }
+
+  -- Toggle semantic tokens
+  if vim.g.semantic_tokens_enabled then
+    vim.b[bufnr].semantic_tokens_enabled = true
+    lsp_mappings.n["<leader>uY"] = {
+      function() require("base.utils.ui").toggle_buffer_semantic_tokens(bufnr) end,
+      desc = "LSP semantic highlight (buffer)",
+    }
+  else
+    client.server_capabilities.semanticTokensProvider = nil
+  end
+
+  -- LSP based search
+  lsp_mappings.n["<leader>lS"] = { function() vim.lsp.buf.workspace_symbol() end, desc = "Search symbol in workspace" }
+  lsp_mappings.n["gS"] = { function() vim.lsp.buf.workspace_symbol() end, desc = "Search symbol in workspace" }
+
+  -- LSP telescope
+  if is_available "telescope.nvim" then -- setup telescope mappings if available
+    if lsp_mappings.n.gd then lsp_mappings.n.gd[1] = function() require("telescope.builtin").lsp_definitions() end end
+    if lsp_mappings.n.gI then
+      lsp_mappings.n.gI[1] = function() require("telescope.builtin").lsp_implementations() end
+    end
+    if lsp_mappings.n.gr then lsp_mappings.n.gr[1] = function() require("telescope.builtin").lsp_references() end end
+    if lsp_mappings.n["<leader>lR"] then
+      lsp_mappings.n["<leader>lR"][1] = function() require("telescope.builtin").lsp_references() end
+    end
+    if lsp_mappings.n.gy then
+      lsp_mappings.n.gy[1] = function() require("telescope.builtin").lsp_type_definitions() end
+    end
+    if lsp_mappings.n["<leader>lS"] then
+      lsp_mappings.n["<leader>lS"][1] = function()
+        vim.ui.input({ prompt = "Symbol Query: (leave empty for word under cursor)" }, function(query)
+          if query then
+            -- word under cursor if given query is empty
+            if query == "" then query = vim.fn.expand "<cword>" end
+            require("telescope.builtin").lsp_workspace_symbols {
+              query = query,
+              prompt_title = ("Find word (%s)"):format(query),
+            }
+          end
+        end)
+      end
+    end
+    if lsp_mappings.n["gS"] then
+      lsp_mappings.n["gS"][1] = function()
+        vim.ui.input({ prompt = "Symbol Query: (leave empty for word under cursor)" }, function(query)
+          if query then
+            -- word under cursor if given query is empty
+            if query == "" then query = vim.fn.expand "<cword>" end
+            require("telescope.builtin").lsp_workspace_symbols {
+              query = query,
+              prompt_title = ("Find word (%s)"):format(query),
+            }
+          end
+        end)
+      end
+    end
+  end
+
+  return lsp_mappings
+end
+
+utils.set_mappings(maps)
+return M

dot_config/nvim/lua/base/health.lua 🔗

@@ -0,0 +1,270 @@
+-- Command to check if you have the required dependencies to use NormalNvim.
+--
+-- DESCRIPTION:
+-- To use it run the command :healthcheck base
+
+local M = {}
+
+local health = {
+  start = vim.health.start or vim.health.report_start,
+  ok = vim.health.ok or vim.health.report_ok,
+  warn = vim.health.warn or vim.health.report_warn,
+  error = vim.health.error or vim.health.report_error,
+  info = vim.health.info or vim.health.report_info,
+}
+
+function M.check()
+  health.start "NormalNvim"
+
+  health.info(
+    "NormalNvim Version: " .. require("distroupdate.utils.updater").version(true)
+  )
+  health.info(
+    "Neovim Version: v"
+    .. vim.fn.matchstr(vim.fn.execute "version", "NVIM v\\zs[^\n]*")
+  )
+
+  if vim.version().prerelease then
+    health.warn "Neovim nightly is not officially supported and may have breaking changes"
+  elseif vim.fn.has "nvim-0.9" == 1 then
+    health.ok "Using stable Neovim >= 0.9.0"
+  else
+    health.error "Neovim >= 0.9.0 is required"
+  end
+
+  -- Checks to perform.
+  local programs = {
+    {
+      cmd = { "git" },
+      type = "error",
+      msg = "Used for core functionality such as updater and plugin management.",
+    },
+    {
+      cmd = { "luarocks" },
+      type = "error",
+      msg = "Used for core functionality such as updater and plugin management.",
+    },
+    {
+      cmd = { "node" },
+      type = "error",
+      msg = "Used for core functionality such as updater and plugin management.",
+    },
+    {
+      cmd = { "yarn" },
+      type = "error",
+      msg = "Used for core functionality such as updater and plugin management.",
+    },
+    {
+      cmd = { "cargo" },
+      type = "error",
+      msg = "Used by nvim-spectre to install oxi. Also by dooku.nvim to generate rust html docs.",
+    },
+    {
+      cmd = { "markmap" },
+      type = "warn",
+      msg = "Used by markmap.nvim. Make sure yarn is in your PATH. To learn how check markmap.nvim github page.",
+    },
+    {
+      cmd = { "fd" },
+      type = "error",
+      msg = "Used for nvim-spectre to find using fd.",
+    },
+    {
+      cmd = { "lazygit" },
+      type = "warn",
+      msg = "Used for mappings to pull up git TUI (Optional)",
+    },
+    {
+      cmd = { "gitui" },
+      type = "warn",
+      msg = "Used for mappings to pull up git TUI (Optional)",
+    },
+    {
+      cmd = { "pynvim" },
+      type = "warn",
+      msg =
+      "Used to enable ranger file browser (optional)\nNOTE: checkhealth won't detect this correctly, but you can ensure it is installed with 'pip list | grep pynvim'.",
+    },
+    {
+      cmd = { "ranger" },
+      type = "warn",
+      msg = "Used to enable ranger file browser (Optional)",
+    },
+    {
+      cmd = { "delta" },
+      type = "warn",
+      msg = "Used by undotree to show a diff (Optional)",
+    },
+    {
+      cmd = { "grcov" },
+      type = "warn",
+      msg = "Used to show code coverage (Optional)",
+    },
+    {
+      cmd = { "grcov" },
+      type = "warn",
+      msg = "Used to show code coverage (Optional)",
+    },
+    {
+      cmd = { "jest" },
+      type = "warn",
+      msg = "Used to run typescript and javascript tests (Optional)",
+    },
+    {
+      cmd = { "pytest" },
+      type = "warn",
+      msg = "Used to run python tests (Optional)",
+    },
+    {
+      cmd = { "cargo nextest" },
+      type = "warn",
+      msg =
+      "Used to run rust tests (optional)\nNOTE: checkhealth won't detect this correctly, but you can confirm it works correctly with 'cargo nextest'.",
+    },
+    {
+      cmd = { "nunit" },
+      type = "warn",
+      msg =
+      "Used to run C# tests (optional)\nNOTE: There is no way to install this system wide. To use it you must add it to your dotnet C# project: 'dotnet add package NUnit NUnit3TestAdapter'.",
+    },
+    {
+      cmd = { "csc" },
+      type = "warn",
+      msg = "Used by compiler.nvim to compile non dotnet C# files (Optional)",
+    },
+    {
+      cmd = { "mono" },
+      type = "warn",
+      msg = "Used by compiler.nvim to run non dotnet C# files. (Optional)",
+    },
+    {
+      cmd = { "dotnet" },
+      type = "warn",
+      msg =
+      "Used by compiler.nvim and DAP to operate with dotnet projects (optional)\nNOTE: Make sure you also have the system package dotnet-sdk installed.",
+    },
+    {
+      cmd = { "java" },
+      type = "warn",
+      msg = "Used by compiler.nvim and dap to operate with java (Optional)",
+    },
+    {
+      cmd = { "javac" },
+      type = "warn",
+      msg = "Used by compiler.nvim to compile java (Optional)",
+    },
+    {
+      cmd = { "nasm" },
+      type = "warn",
+      msg = "Used by compiler.nvim to compile assembly x86_64 (Optional)",
+    },
+
+    {
+      cmd = { "gcc" },
+      type = "warn",
+      msg = "Used by compiler.nvim to compile C (Optional)",
+    },
+    {
+      cmd = { "g++" },
+      type = "warn",
+      msg = "Used by compiler.nvim to compile C++ (Optional)",
+    },
+    {
+      cmd = { "elixir" },
+      type = "warn",
+      msg = "Used by compiler.nvim to compile elixir (optional)",
+    },
+    {
+      cmd = { "Rscript" },
+      type = "warn",
+      msg = "Used by compiler.nvim to interpretate R (Optional)",
+    },
+    {
+      cmd = { "python" },
+      type = "warn",
+      msg = "Used by compiler.nvim to interpretate python (Optional)",
+    },
+    {
+      cmd = { "nuitka3" },
+      type = "warn",
+      msg = "Used by compiler.nvim to compile python to machine code (Optional)",
+    },
+    {
+      cmd = { "pyinstaller" },
+      type = "warn",
+      msg = "Used by compiler.nvim to compile python to bytecode (Optional)",
+    },
+    {
+      cmd = { "ruby" },
+      type = "warn",
+      msg = "Used by compiler.nvim to interpretate ruby (optional)",
+    },
+    {
+      cmd = { "perl" },
+      type = "warn",
+      msg = "Used by compiler.nvim to interpretate perl (optional)",
+    },
+    {
+      cmd = { "swiftc" },
+      type = "warn",
+      msg = "Used by compiler.nvim to compile swift (optional)",
+    },
+    {
+      cmd = { "swift" },
+      type = "warn",
+      msg = "Used by compiler.nvim to compile swift (optional)",
+    },
+    {
+      cmd = { "gfortran" },
+      type = "warn",
+      msg = "Used by compiler.nvim to compile fortran (optional)"
+    },
+    {
+      cmd = { "fpm" },
+      type = "warn",
+      msg = "Used by compiler.nvim to compile fortran (optional)"
+    },
+    {
+      cmd = { "go" },
+      type = "warn",
+      msg = "Used by compiler.nvim to compile go (optional)",
+    },
+    {
+      cmd = { "godoc" },
+      type = "warn",
+      msg =
+      "Used by dooku.nvim to generate go html docs\nNOTE: If you have it installed but you can't run it on the terminal, ensure you have added 'go' to your OS path (optional)",
+    },
+    {
+      cmd = { "doxygen" },
+      type = "warn",
+      msg = "Used by dooku.nvim to generate c/c++/python/java html docs (optional)",
+    },
+  }
+
+  -- Actually perform the checks we defined above.
+  for _, program in ipairs(programs) do
+    if type(program.cmd) == "string" then program.cmd = { program.cmd } end
+    local name = table.concat(program.cmd, "/")
+    local found = false
+    for _, cmd in ipairs(program.cmd) do
+      if vim.fn.executable(cmd) == 1 then
+        name = cmd
+        found = true
+        break
+      end
+    end
+
+    if found then
+      health.ok(("`%s` is installed: %s"):format(name, program.msg))
+    else
+      health[program.type](
+        ("`%s` is not installed: %s"):format(name, program.msg)
+      )
+    end
+  end
+  health.info("")
+  health.info("Write `:bw` to close `:checkhealth` gracefully.")
+end
+
+return M

dot_config/nvim/lua/base/icons/nerd_font.lua 🔗

@@ -0,0 +1,73 @@
+-- ### Nerd fonts
+
+-- DESCRIPTION:
+-- Here you can edit the icons displayed in NormalNvim.
+
+-- If you can't see the default icons:
+-- Install nerd fonts and set it as your terminal font:
+-- https://www.nerdfonts.com/
+
+return {
+  ActiveLSP = "",
+  ActiveTS = "",
+  ArrowLeft = "",
+  ArrowRight = "",
+  Bookmarks = "",
+  BufferClose = "󰅖",
+  DapBreakpoint = "",
+  DapBreakpointCondition = "",
+  DapBreakpointRejected = "",
+  DapLogPoint = ".>",
+  DapStopped = "󰁕",
+  Debugger = "",
+  DefaultFile = "󰈙",
+  Diagnostic = "󰒡",
+  DiagnosticError = "",
+  DiagnosticHint = "󰌵",
+  DiagnosticInfo = "󰋼",
+  DiagnosticWarn = "",
+  Ellipsis = "…",
+  Environment = "",
+  FileNew = "",
+  FileModified = "",
+  FileReadOnly = "",
+  FoldClosed = "",
+  FoldOpened = "",
+  FoldSeparator = " ",
+  FolderClosed = "",
+  FolderEmpty = "",
+  FolderOpen = "",
+  Git = "󰊢",
+  GitAdd = "",
+  GitBranch = "",
+  GitChange = "",
+  GitConflict = "",
+  GitDelete = "",
+  GitIgnored = "◌",
+  GitRenamed = "➜",
+  GitSign = "▎",
+  GitStaged = "✓",
+  GitUnstaged = "✗",
+  GitUntracked = "★",
+  LSPLoaded = "",
+  LSPLoading1 = "",
+  LSPLoading2 = "󰀚",
+  LSPLoading3 = "",
+  MacroRecording = "",
+  Package = "󰏖",
+  Paste = "󰅌",
+  Refresh = "",
+  Run = "󰑮",
+  Search = "",
+  Selected = "❯",
+  Session = "󱂬",
+  Sort = "󰒺",
+  Spellcheck = "󰓆",
+  Tab = "󰓩",
+  TabClose = "󰅙",
+  Terminal = "",
+  Window = "",
+  WordFile = "󰈭",
+  Test = "󰙨",
+  Docs = "",
+}

dot_config/nvim/lua/base/icons/text.lua 🔗

@@ -0,0 +1,48 @@
+-- ### Text fonts
+
+-- DESCRIPTION:
+-- Fallback icons that will be displayed
+-- if you don't have nerd fonts installed,
+-- or if you set `vim.g.icons_enabled = false` on `../1-options.lua`.
+
+return {
+  ActiveLSP = "LSP:",
+  ArrowLeft = "<",
+  ArrowRight = ">",
+  BufferClose = "x",
+  DapBreakpoint = "B",
+  DapBreakpointCondition = "C",
+  DapBreakpointRejected = "R",
+  DapLogPoint = "L",
+  DapStopped = ">",
+  DefaultFile = "[F]",
+  DiagnosticError = "X",
+  DiagnosticHint = "?",
+  DiagnosticInfo = "i",
+  DiagnosticWarn = "!",
+  Ellipsis = "...",
+  Environment = "Env:",
+  FileModified = "*",
+  FileReadOnly = "[lock]",
+  FoldClosed = "+",
+  FoldOpened = "-",
+  FoldSeparator = " ",
+  FolderClosed = "[D]",
+  FolderEmpty = "[E]",
+  FolderOpen = "[O]",
+  GitAdd = "[+]",
+  GitChange = "[/]",
+  GitConflict = "[!]",
+  GitDelete = "[-]",
+  GitIgnored = "[I]",
+  GitRenamed = "[R]",
+  GitStaged = "[S]",
+  GitUnstaged = "[U]",
+  GitUntracked = "[?]",
+  MacroRecording = "Recording:",
+  Paste = "[PASTE]",
+  Search = "?",
+  Selected = "*",
+  Spellcheck = "[SPELL]",
+  TabClose = "X",
+}

dot_config/nvim/lua/base/utils/init.lua 🔗

@@ -0,0 +1,307 @@
+--- ### General utils.
+--
+--  DESCRIPTION:
+--  General utility functions to use within Nvim.
+
+--    Functions:
+--      -> run_cmd                  → Run a shell command and return true/false.
+--      -> add_autocmds_to_buffer   → Add autocmds to a bufnr.
+--      -> del_autocmds_from_buffer → Delete autocmds from a bufnr.
+--      -> get_icon                 → Return an icon from the icons directory.
+--      -> get_mappings_template    → Return a empty mappings table.
+--      -> is_available             → Return true if the plugin exist.
+--      -> is_big_file              → Return true if the file is too big.
+--      -> notify                   → Send a notification with a default title.
+--      -> os_path                  → Converts a path to the current OS.
+--      -> get_plugin_opts          → Return a plugin opts table.
+--      -> set_mappings             → Set a list of mappings in a clean way.
+--      -> set_url_effect           → Show an effect for urls.
+--      -> open_with_program        → Open the file or URL under the cursor.
+--      -> trigger_event            → Manually trigger a event.
+--      -> which_key_register       → When setting a mapping, add it to whichkey.
+
+
+local M = {}
+
+--- Run a shell command and capture the output and if the command
+--- succeeded or failed.
+---@param cmd string|string[] The terminal command to execute
+---@param show_error? boolean If true, print errors if the command fail.
+---@return string|nil # The result of a successfully executed command or nil
+function M.run_cmd(cmd, show_error)
+  if type(cmd) == "string" then cmd = vim.split(cmd, " ") end
+  if vim.fn.has "win32" == 1 then cmd = vim.list_extend({ "cmd.exe", "/C" }, cmd) end
+  local result = vim.fn.system(cmd)
+  local success = vim.api.nvim_get_vvar "shell_error" == 0
+  if not success and (show_error == nil or show_error) then
+    vim.api.nvim_err_writeln(("Error running command %s\nError message:\n%s"):format(table.concat(cmd, " "), result))
+  end
+  return success and result:gsub("[\27\155][][()#;?%d]*[A-PRZcf-ntqry=><~]", "") or nil
+end
+
+--- Adds autocmds to a specific buffer if they don't already exist.
+---
+--- @param augroup string       The name of the autocmd group to which the autocmds belong.
+--- @param bufnr number         The buffer number to which the autocmds should be applied.
+--- @param autocmds table|any  A table or a single autocmd definition containing the autocmds to add.
+function M.add_autocmds_to_buffer(augroup, bufnr, autocmds)
+  -- Check if autocmds is a list, if not convert it to a list
+  if not vim.islist(autocmds) then autocmds = { autocmds } end
+
+  -- Attempt to retrieve existing autocmds associated with the specified augroup and bufnr
+  local cmds_found, cmds = pcall(vim.api.nvim_get_autocmds, { group = augroup, buffer = bufnr })
+
+  -- If no existing autocmds are found or the cmds_found call fails
+  if not cmds_found or vim.tbl_isempty(cmds) then
+    -- Create a new augroup if it doesn't already exist
+    vim.api.nvim_create_augroup(augroup, { clear = false })
+
+    -- Iterate over each autocmd provided
+    for _, autocmd in ipairs(autocmds) do
+      -- Extract the events from the autocmd and remove the events key
+      local events = autocmd.events
+      autocmd.events = nil
+
+      -- Set the group and buffer keys for the autocmd
+      autocmd.group = augroup
+      autocmd.buffer = bufnr
+
+      -- Create the autocmd
+      vim.api.nvim_create_autocmd(events, autocmd)
+    end
+  end
+end
+
+--- Deletes autocmds associated with a specific buffer and autocmd group.
+---
+--- @param augroup string  The name of the autocmd group from which the autocmds should be removed.
+--- @param bufnr number    The buffer number from which the autocmds should be removed.
+function M.del_autocmds_from_buffer(augroup, bufnr)
+  -- Attempt to retrieve existing autocmds associated with the specified augroup and bufnr
+  local cmds_found, cmds = pcall(vim.api.nvim_get_autocmds, { group = augroup, buffer = bufnr })
+
+  -- If retrieval was successful
+  if cmds_found then
+    -- Map over each retrieved autocmd and delete it
+    vim.tbl_map(function(cmd) vim.api.nvim_del_autocmd(cmd.id) end, cmds)
+  end
+end
+
+--- Get an icon from `lspkind` if it is available and return it.
+---@param kind string The kind of icon in `lspkind` to retrieve.
+---@return string icon.
+function M.get_icon(kind, padding, no_fallback)
+  if not vim.g.icons_enabled and no_fallback then return "" end
+  local icon_pack = vim.g.icons_enabled and "icons" or "text_icons"
+  if not M[icon_pack] then
+    M.icons = require("base.icons.nerd_font")
+    M.text_icons = require("base.icons.text")
+  end
+  local icon = M[icon_pack] and M[icon_pack][kind]
+  return icon and icon .. string.rep(" ", padding or 0) or ""
+end
+
+--- Get an empty table of mappings with a key for each map mode.
+---@return table<string,table> # a table with entries for each map mode.
+function M.get_mappings_template()
+  local maps = {}
+  for _, mode in ipairs { "", "n", "v", "x", "s", "o", "!", "i", "l", "c", "t" } do
+    maps[mode] = {}
+  end
+  if vim.fn.has "nvim-0.10.0" == 1 then
+    for _, abbr_mode in ipairs { "ia", "ca", "!a" } do
+      maps[abbr_mode] = {}
+    end
+  end
+  return maps
+end
+
+--- Check if a plugin is defined in lazy. Useful with lazy loading
+--- when a plugin is not necessarily loaded yet.
+---@param plugin string The plugin to search for.
+---@return boolean available # Whether the plugin is available.
+function M.is_available(plugin)
+  local lazy_config_avail, lazy_config = pcall(require, "lazy.core.config")
+  return lazy_config_avail and lazy_config.spec.plugins[plugin] ~= nil
+end
+
+--- Returns true if the file is considered a big file,
+--- according to the criteria defined in `vim.g.big_file`.
+---@param bufnr number|nil buffer number. 0 by default, which means current buf.
+---@return boolean is_big_file true or false.
+function M.is_big_file(bufnr)
+  if bufnr == nil then bufnr = 0 end
+  local filesize = vim.fn.getfsize(vim.api.nvim_buf_get_name(bufnr))
+  local nlines = vim.api.nvim_buf_line_count(bufnr)
+  local is_big_file = (filesize > vim.g.big_file.size)
+      or (nlines > vim.g.big_file.lines)
+  return is_big_file
+end
+
+--- Sends a notification with 'Neovim' as default title.
+--- Same as using vim.notify, but it saves us typing the title every time.
+---@param msg string The notification body.
+---@param type number|nil The type of the notification (:help vim.log.levels).
+---@param opts? table The nvim-notify options to use (:help notify-options).
+function M.notify(msg, type, opts)
+  vim.schedule(function()
+    vim.notify(
+      msg, type, vim.tbl_deep_extend("force", { title = "Neovim" }, opts or {}))
+  end)
+end
+
+--- Convert a path to the path format of the current operative system.
+--- It converts 'slash' to 'inverted slash' if on windows, and vice versa on UNIX.
+---@param path string A path string.
+---@return string|nil,nil path A path string formatted for the current OS.
+function M.os_path(path)
+  if path == nil then return nil end
+  -- Get the platform-specific path separator
+  local separator = string.sub(package.config, 1, 1)
+  return string.gsub(path, '[/\\]', separator)
+end
+
+--- Get the options of a plugin managed by lazy.
+---@param plugin string The plugin to get options from
+---@return table opts # The plugin options, or empty table if no plugin.
+function M.get_plugin_opts(plugin)
+  local lazy_config_avail, lazy_config = pcall(require, "lazy.core.config")
+  local lazy_plugin_avail, lazy_plugin = pcall(require, "lazy.core.plugin")
+  local opts = {}
+  if lazy_config_avail and lazy_plugin_avail then
+    local spec = lazy_config.spec.plugins[plugin]
+    if spec then opts = lazy_plugin.values(spec, "opts") end
+  end
+  return opts
+end
+
+--- Set a table of mappings.
+---
+--- This wrapper prevents a  boilerplate code, and takes care of `whichkey.nvim`.
+---@param map_table table A nested table where the first key is the vim mode,
+---                       the second key is the key to map, and the value is
+---                       the function to set the mapping to.
+---@param base? table A base set of options to set on every keybinding.
+function M.set_mappings(map_table, base)
+  -- iterate over the first keys for each mode
+  base = base or {}
+  for mode, maps in pairs(map_table) do
+    -- iterate over each keybinding set in the current mode
+    for keymap, options in pairs(maps) do
+      -- build the options for the command accordingly
+      if options then
+        local cmd = options
+        local keymap_opts = base
+        if type(options) == "table" then
+          cmd = options[1]
+          keymap_opts = vim.tbl_deep_extend("force", keymap_opts, options)
+          keymap_opts[1] = nil
+        end
+        if not cmd or keymap_opts.name then -- if which-key mapping, queue it
+          if not keymap_opts.name then keymap_opts.name = keymap_opts.desc end
+          if not M.which_key_queue then M.which_key_queue = {} end
+          if not M.which_key_queue[mode] then M.which_key_queue[mode] = {} end
+          M.which_key_queue[mode][keymap] = keymap_opts
+        else -- if not which-key mapping, set it
+          vim.keymap.set(mode, keymap, cmd, keymap_opts)
+        end
+      end
+    end
+  end
+  -- if which-key is loaded already, register
+  if package.loaded["which-key"] then M.which_key_register() end
+end
+
+--- Add syntax matching rules for highlighting URLs/URIs.
+function M.set_url_effect()
+  --- regex used for matching a valid URL/URI string
+  local url_matcher =
+      "\\v\\c%(%(h?ttps?|ftp|file|ssh|git)://|[a-z]+[@][a-z]+[.][a-z]+:)" ..
+      "%([&:#*@~%_\\-=?!+;/0-9a-z]+%(%([.;/?]|[.][.]+)" ..
+      "[&:#*@~%_\\-=?!+/0-9a-z]+|:\\d+|,%(%(%(h?ttps?|ftp|file|ssh|git)://|" ..
+      "[a-z]+[@][a-z]+[.][a-z]+:)@![0-9a-z]+))*|\\([&:#*@~%_\\-=?!+;/.0-9a-z]*\\)" ..
+      "|\\[[&:#*@~%_\\-=?!+;/.0-9a-z]*\\]|\\{%([&:#*@~%_\\-=?!+;/.0-9a-z]*" ..
+      "|\\{[&:#*@~%_\\-=?!+;/.0-9a-z]*})\\})+"
+
+  M.delete_url_effect()
+  if vim.g.url_effect_enabled then
+    vim.fn.matchadd("HighlightURL", url_matcher, 15)
+  end
+end
+
+--- Delete the syntax matching rules for URLs/URIs if set.
+function M.delete_url_effect()
+  for _, match in ipairs(vim.fn.getmatches()) do
+    if match.group == "HighlightURL" then vim.fn.matchdelete(match.id) end
+  end
+end
+
+--- Open the file or url under the cursor.
+---@param path string The path of the file to open with the system opener.
+function M.open_with_program(path)
+  if vim.ui.open then return vim.ui.open(path) end
+  local cmd
+  if vim.fn.has "mac" == 1 then
+    cmd = { "open" }
+  elseif vim.fn.has "win32" == 1 then
+    if vim.fn.executable "rundll32" then
+      cmd = { "rundll32", "url.dll,FileProtocolHandler" }
+    else
+      cmd = { "cmd.exe", "/K", "explorer" }
+    end
+  elseif vim.fn.has "unix" == 1 then
+    if vim.fn.executable "explorer.exe" == 1 then -- available in WSL
+      cmd = { "explorer.exe" }
+    elseif vim.fn.executable "xdg-open" == 1 then
+      cmd = { "xdg-open" }
+    end
+  end
+  if not cmd then M.notify("Available system opening tool not found!", vim.log.levels.ERROR) end
+  if not path then
+    path = vim.fn.expand "<cfile>"
+  elseif not path:match "%w+:" then
+    path = vim.fn.expand(path)
+  end
+  vim.fn.jobstart(vim.list_extend(cmd, { path }), { detach = true })
+end
+
+--- Convenient wapper to save code when we Trigger events.
+--- To listen for a event triggered by this function you can use `autocmd`.
+---@param event string Name of the event.
+---@param is_urgent boolean|nil If true, trigger directly instead of scheduling. Useful for startup events.
+-- @usage To run a User event:   `trigger_event("User MyUserEvent")`
+-- @usage To run a Neovim event: `trigger_event("BufEnter")
+function M.trigger_event(event, is_urgent)
+  -- define behavior
+  local function trigger()
+    local is_user_event = string.match(event, "^User ") ~= nil
+    if is_user_event then
+      event = event:gsub("^User ", "")
+      vim.api.nvim_exec_autocmds("User", { pattern = event, modeline = false })
+    else
+      vim.api.nvim_exec_autocmds(event, { modeline = false })
+    end
+  end
+
+  -- execute
+  if is_urgent then
+    trigger()
+  else
+    vim.schedule(trigger)
+  end
+end
+
+--- Register queued which-key mappings.
+function M.which_key_register()
+  if M.which_key_queue then
+    local wk_avail, wk = pcall(require, "which-key")
+    if wk_avail then
+      for mode, registration in pairs(M.which_key_queue) do
+        wk.register(registration, { mode = mode })
+      end
+      M.which_key_queue = nil
+    end
+  end
+end
+
+return M

dot_config/nvim/lua/base/utils/lsp.lua 🔗

@@ -0,0 +1,189 @@
+--- ### LSP utils.
+--
+--  DESCRIPTION:
+--  Functions we use to configure the plugin `mason-lspconfig.nvim`.
+--  You can specify your own lsp settings inside `M.apply_user_lsp_settings()`.
+--
+--  Most options we use in `M.apply_default_lsp_settings()`
+--  can be tweaked on the file `../1-options.lua`.
+--  Take this into consideration to minimize the risk of breaking stuff.
+
+--    Functions:
+--      -> M.apply_default_lsp_settings  → Apply our default lsp settings.
+--      -> M.apply_user_lsp_mappings     → Apply the user lsp keymappings.
+--      -> M.apply_user_lsp_settings     → Apply the user lsp settings.
+--      -> M.setup                       → It passes the user lsp settings to lspconfig.
+
+local M = {}
+local utils = require "base.utils"
+local stored_handlers = {}
+
+--- Apply default settings for diagnostics, formatting, and lsp capabilities.
+--- It only need to be executed once, normally on mason-lspconfig.
+---@return nil
+M.apply_default_lsp_settings = function()
+  -- Icons
+  -- Apply the icons defined in ../icons/nerd_font.lua
+  local get_icon = utils.get_icon
+  local signs = {
+    { name = "DiagnosticSignError",    text = get_icon("DiagnosticError"),        texthl = "DiagnosticSignError" },
+    { name = "DiagnosticSignWarn",     text = get_icon("DiagnosticWarn"),         texthl = "DiagnosticSignWarn" },
+    { name = "DiagnosticSignHint",     text = get_icon("DiagnosticHint"),         texthl = "DiagnosticSignHint" },
+    { name = "DiagnosticSignInfo",     text = get_icon("DiagnosticInfo"),         texthl = "DiagnosticSignInfo" },
+    { name = "DapStopped",             text = get_icon("DapStopped"),             texthl = "DiagnosticWarn" },
+    { name = "DapBreakpoint",          text = get_icon("DapBreakpoint"),          texthl = "DiagnosticInfo" },
+    { name = "DapBreakpointRejected",  text = get_icon("DapBreakpointRejected"),  texthl = "DiagnosticError" },
+    { name = "DapBreakpointCondition", text = get_icon("DapBreakpointCondition"), texthl = "DiagnosticInfo" },
+    { name = "DapLogPoint",            text = get_icon("DapLogPoint"),            texthl = "DiagnosticInfo" }
+  }
+  for _, sign in ipairs(signs) do
+    vim.fn.sign_define(sign.name, sign)
+  end
+
+  -- Borders
+  -- Apply the option lsp_round_borders_enabled from ../1-options.lua
+  if vim.g.lsp_round_borders_enabled then
+    vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(vim.lsp.handlers.hover, { border = "rounded", silent = true })
+    vim.lsp.handlers["textDocument/signatureHelp"] =
+        vim.lsp.with(vim.lsp.handlers.signature_help, { border = "rounded", silent = true })
+  end
+
+  -- Set default diagnostics
+  local default_diagnostics = {
+    virtual_text = true,
+    signs = {
+      text = {
+        [vim.diagnostic.severity.ERROR] = utils.get_icon("DiagnosticError"),
+        [vim.diagnostic.severity.HINT] = utils.get_icon("DiagnosticHint"),
+        [vim.diagnostic.severity.WARN] = utils.get_icon("DiagnosticWarn"),
+        [vim.diagnostic.severity.INFO] = utils.get_icon("DiagnosticInfo"),
+      },
+      active = signs,
+    },
+    update_in_insert = true,
+    underline = true,
+    severity_sort = true,
+    float = {
+      focused = false,
+      style = "minimal",
+      border = "rounded",
+      source = "always",
+      header = "",
+      prefix = "",
+    },
+  }
+
+  -- Apply default diagnostics
+  -- Applies the option diagnostics_mode from ../1-options.lua
+  M.diagnostics = {
+    -- diagnostics off
+    [0] = vim.tbl_deep_extend(
+      "force",
+      default_diagnostics,
+      { underline = false, virtual_text = false, signs = false, update_in_insert = false }
+    ),
+    -- status only
+    vim.tbl_deep_extend("force", default_diagnostics, { virtual_text = false, signs = false }),
+    -- virtual text off, signs on
+    vim.tbl_deep_extend("force", default_diagnostics, { virtual_text = false }),
+    -- all diagnostics on
+    default_diagnostics,
+  }
+  vim.diagnostic.config(M.diagnostics[vim.g.diagnostics_mode])
+
+  -- Apply formatting settings
+  M.formatting = { format_on_save = { enabled = true }, disabled = {} }
+  if type(M.formatting.format_on_save) == "boolean" then
+    M.formatting.format_on_save = { enabled = M.formatting.format_on_save }
+  end
+  M.format_opts = vim.deepcopy(M.formatting)
+  M.format_opts.disabled = nil
+  M.format_opts.format_on_save = nil
+  M.format_opts.filter = function(client)
+    local filter = M.formatting.filter
+    local disabled = M.formatting.disabled or {}
+    -- check if client is fully disabled or filtered by function
+    return not (vim.tbl_contains(disabled, client.name) or (type(filter) == "function" and not filter(client)))
+  end
+end
+
+--- This function has the sole purpose of passing the lsp keymappings to lsp.
+--- We have this function, because we use it on none-ls.
+---@param client string The client where the lsp mappings will load.
+---@param bufnr string The bufnr where the lsp mappings will load.
+function M.apply_user_lsp_mappings(client, bufnr)
+  local lsp_mappings = require("base.4-mappings").lsp_mappings(client, bufnr)
+  if not vim.tbl_isempty(lsp_mappings.v) then
+    lsp_mappings.v["<leader>l"] = { desc = utils.get_icon("ActiveLSP", 1, true) .. "LSP" }
+  end
+  utils.set_mappings(lsp_mappings, { buffer = bufnr })
+end
+
+--- Here you can specify custom settings for the lsp servers you install.
+--- This is not normally necessary. But you can.
+---@param server_name string The name of the server
+---@return table # The table of LSP options used when setting up the given language server
+function M.apply_user_lsp_settings(server_name)
+  local server = require("lspconfig")[server_name]
+
+  -- Define user server capabilities.
+  M.capabilities = vim.lsp.protocol.make_client_capabilities()
+  M.capabilities.textDocument.completion.completionItem.documentationFormat = { "markdown", "plaintext" }
+  M.capabilities.textDocument.completion.completionItem.snippetSupport = true
+  M.capabilities.textDocument.completion.completionItem.preselectSupport = true
+  M.capabilities.textDocument.completion.completionItem.insertReplaceSupport = true
+  M.capabilities.textDocument.completion.completionItem.labelDetailsSupport = true
+  M.capabilities.textDocument.completion.completionItem.deprecatedSupport = true
+  M.capabilities.textDocument.completion.completionItem.commitCharactersSupport = true
+  M.capabilities.textDocument.completion.completionItem.tagSupport = { valueSet = { 1 } }
+  M.capabilities.textDocument.completion.completionItem.resolveSupport =
+  { properties = { "documentation", "detail", "additionalTextEdits" } }
+  M.capabilities.textDocument.foldingRange = { dynamicRegistration = false, lineFoldingOnly = true }
+  M.flags = {}
+  local opts = vim.tbl_deep_extend("force", server, { capabilities = M.capabilities, flags = M.flags })
+
+  -- Define user server rules.
+  if server_name == "jsonls" then -- Add schemastore schemas
+    local is_schemastore_loaded, schemastore = pcall(require, "schemastore")
+    if is_schemastore_loaded then
+      opts.settings = { json = { schemas = schemastore.json.schemas(), validate = { enable = true } } }
+    end
+  end
+  if server_name == "yamlls" then -- Add schemastore schemas
+    local is_schemastore_loaded, schemastore = pcall(require, "schemastore")
+    if is_schemastore_loaded then opts.settings = { yaml = { schemas = schemastore.yaml.schemas() } } end
+  end
+  if server_name == "lua_ls" then -- Disable third party checking
+    pcall(require, "neodev")
+    opts.settings = { Lua = { workspace = { checkThirdParty = false } } }
+  end
+
+  -- Apply them
+  local old_on_attach = server.on_attach
+  opts.on_attach = function(client, bufnr)
+    -- If the server on_attach function exist → server.on_attach(client, bufnr)
+    if type(old_on_attach) == "function" then old_on_attach(client, bufnr) end
+    -- Also, apply mappings to the buffer.
+    M.apply_user_lsp_mappings(client, bufnr)
+  end
+  return opts
+end
+
+--- This function passes the `user lsp settings` to lspconfig,
+--- which is the responsible of configuring everything for us.
+---
+--- You are meant to call this function from the plugin `mason-lspconfig.nvim`.
+---@param server string A lsp server name.
+---@return nil
+M.setup = function(server)
+  -- Get the user settings.
+  local opts = M.apply_user_lsp_settings(server)
+
+  -- Get a handler from lspconfig.
+  local setup_handler = stored_handlers[server] or require("lspconfig")[server].setup(opts)
+
+  -- Apply our user settings to the lspconfig handler.
+  if setup_handler then setup_handler(server, opts) end
+end
+
+return M

dot_config/nvim/lua/base/utils/ui.lua 🔗

@@ -0,0 +1,321 @@
+--- ### UI toggle functions.
+--
+--  DESCRIPTION:
+--  While you could technically delete this file, we encourage you
+--  to keep it as it takes a lot of complexity out of `../4-mappings.lua`.
+
+--    Functions:
+--      -> change_number
+--      -> set_indent
+--      -> toggle_animations
+--      -> toggle_autoformat
+--      -> toggle_autopairs
+--      -> toggle_background
+--      -> toggle_buffer_autoformat
+--      -> toggle_buffer_inlay_hints
+--      -> toggle_buffer_semantic_tokens
+--      -> toggle_buffer_syntax
+--      -> toggle_codelens
+--      -> toggle_coverage_signs
+--      -> toggle_cmp
+--      -> toggle_conceal
+--      -> toggle_diagnostics
+--      -> toggle_foldcolumn
+--      -> toggle_inlay_hints
+--      -> toggle_lsp_signature
+--      -> toggle_paste
+--      -> toggle_signcolumn
+--      -> toggle_spell
+--      -> toggle_statusline
+--      -> toggle_tabline
+--      -> toggle_ui_notifications
+--      -> toggle_url_effect
+--      -> toggle_wrap
+--      -> toggle_zen_mode
+
+
+local M = {}
+local utils = require("base.utils")
+local function bool2str(bool) return bool and "on" or "off" end
+
+--- Change the number display modes
+function M.change_number()
+  local number = vim.wo.number                 -- local to window
+  local relativenumber = vim.wo.relativenumber -- local to window
+  if not number and not relativenumber then
+    vim.wo.number = true
+  elseif number and not relativenumber then
+    vim.wo.relativenumber = true
+  elseif number and relativenumber then
+    vim.wo.number = false
+  else -- not number and relativenumber
+    vim.wo.relativenumber = false
+  end
+  utils.notify(string.format("number %s, relativenumber %s", bool2str(vim.wo.number), bool2str(vim.wo.relativenumber)))
+end
+
+--- Set the indent and tab related numbers
+function M.set_indent()
+  local input_avail, input = pcall(vim.fn.input, "Set indent value (>0 expandtab, <=0 noexpandtab): ")
+  if input_avail then
+    local indent = tonumber(input)
+    if not indent or indent == 0 then return end
+    vim.bo.expandtab = (indent > 0) -- local to buffer
+    indent = math.abs(indent)
+    vim.bo.tabstop = indent         -- local to buffer
+    vim.bo.softtabstop = indent     -- local to buffer
+    vim.bo.shiftwidth = indent      -- local to buffer
+    utils.notify(string.format("indent=%d %s", indent, vim.bo.expandtab and "expandtab" or "noexpandtab"))
+  end
+end
+
+--- Toggle animations
+function M.toggle_animations()
+  if vim.g.minianimate_disable then
+    vim.g.minianimate_disable = false
+  else
+    vim.g.minianimate_disable = true
+  end
+
+  local state = vim.g.minianimate_disable
+  utils.notify(string.format("animations %s", bool2str(not state)))
+end
+
+--- Toggle auto format
+function M.toggle_autoformat()
+  vim.g.autoformat_enabled = not vim.g.autoformat_enabled
+  utils.notify(string.format("Global autoformatting %s", bool2str(vim.g.autoformat_enabled)))
+end
+
+--- Toggle autopairs
+function M.toggle_autopairs()
+  local ok, autopairs = pcall(require, "nvim-autopairs")
+  if ok then
+    if autopairs.state.disabled then
+      autopairs.enable()
+    else
+      autopairs.disable()
+    end
+    vim.g.autopairs_enabled = autopairs.state.disabled
+    utils.notify(string.format("autopairs %s", bool2str(not autopairs.state.disabled)))
+  else
+    utils.notify "autopairs not available"
+  end
+end
+
+--- Toggle background="dark"|"light"
+function M.toggle_background()
+  vim.go.background = vim.go.background == "light" and "dark" or "light"
+  utils.notify(string.format("background=%s", vim.go.background))
+end
+
+--- Toggle buffer local auto format
+function M.toggle_buffer_autoformat(bufnr)
+  bufnr = bufnr or 0
+  local old_val = vim.b[bufnr].autoformat_enabled
+  if old_val == nil then old_val = vim.g.autoformat_enabled end
+  vim.b[bufnr].autoformat_enabled = not old_val
+  utils.notify(string.format("Buffer autoformatting %s", bool2str(vim.b[bufnr].autoformat_enabled)))
+end
+
+--- Toggle LSP inlay hints (buffer)
+-- @param bufnr? number the buffer to toggle the clients on
+function M.toggle_buffer_inlay_hints(bufnr)
+  bufnr = bufnr or 0
+  vim.b[bufnr].inlay_hints_enabled = not vim.b[bufnr].inlay_hints_enabled
+  vim.lsp.inlay_hint.enable(vim.b[bufnr].inlay_hints_enabled, { bufnr = bufnr })
+  utils.notify(string.format("Buffer inlay hints %s", bool2str(vim.b[bufnr].inlay_hints_enabled)))
+end
+
+--- Toggle buffer semantic token highlighting for all language servers that support it
+--@param bufnr? number the buffer to toggle the clients on
+function M.toggle_buffer_semantic_tokens(bufnr)
+  bufnr = bufnr or 0
+  vim.b[bufnr].semantic_tokens_enabled = not vim.b[bufnr].semantic_tokens_enabled
+  for _, client in ipairs(vim.lsp.get_clients({ bufnr = bufnr })) do
+    if client.server_capabilities.semanticTokensProvider then
+      vim.lsp.semantic_tokens[vim.b[bufnr].semantic_tokens_enabled and "start" or "stop"](bufnr, client.id)
+      utils.notify(string.format("Buffer lsp semantic highlighting %s", bool2str(vim.b[bufnr].semantic_tokens_enabled)))
+    end
+  end
+end
+
+--- Toggle syntax highlighting and treesitter
+function M.toggle_buffer_syntax(bufnr)
+  -- HACK: this should just be `bufnr = bufnr or 0` but it looks like
+  --       `vim.treesitter.stop` has a bug with `0` being current.
+  bufnr = (bufnr and bufnr ~= 0) and bufnr or vim.api.nvim_win_get_buf(0)
+  local ts_avail, parsers = pcall(require, "nvim-treesitter.parsers")
+  if vim.bo[bufnr].syntax == "off" then
+    if ts_avail and parsers.has_parser() then vim.treesitter.start(bufnr) end
+    vim.bo[bufnr].syntax = "on"
+    if not vim.b.semantic_tokens_enabled then M.toggle_buffer_semantic_tokens(bufnr, true) end
+  else
+    if ts_avail and parsers.has_parser() then vim.treesitter.stop(bufnr) end
+    vim.bo[bufnr].syntax = "off"
+    if vim.b.semantic_tokens_enabled then M.toggle_buffer_semantic_tokens(bufnr, true) end
+  end
+  utils.notify(string.format("syntax %s", bool2str(vim.bo[bufnr].syntax)))
+end
+
+--- Toggle codelens
+function M.toggle_codelens(bufnr)
+  bufnr = bufnr or 0
+  vim.g.codelens_enabled = not vim.g.codelens_enabled
+  if vim.g.codelens_enabled then
+    vim.lsp.codelens.refresh({ bufnr = bufnr })
+  else
+    vim.lsp.codelens.clear()
+  end
+  utils.notify(string.format("CodeLens %s", bool2str(vim.g.codelens_enabled)))
+end
+
+--- Toggle coverage signs
+function M.toggle_coverage_signs(bufnr)
+  bufnr = bufnr or 0
+  vim.b[bufnr].coverage_signs_enabled = not vim.b[bufnr].coverage_signs_enabled
+  if vim.b[bufnr].coverage_signs_enabled then
+    utils.notify("Coverage signs on:" ..
+                 "\n\n- Git signs will be temporary disabled." ..
+                 "\n- Diagnostic signs won't be automatically disabled.")
+    vim.cmd("Gitsigns toggle_signs")
+    require("coverage").load(true)
+  else
+    utils.notify("Coverage signs off:\n\n- Git signs re-enabled.")
+    require("coverage").hide()
+    vim.cmd("Gitsigns toggle_signs")
+  end
+end
+
+--- Toggle cmp entrirely
+function M.toggle_cmp()
+  vim.g.cmp_enabled = not vim.g.cmp_enabled
+  local ok, _ = pcall(require, "cmp")
+  utils.notify(ok and string.format("completion %s", bool2str(vim.g.cmp_enabled)) or "completion not available")
+end
+
+--- Toggle conceal=2|0
+function M.toggle_conceal()
+  vim.opt.conceallevel = vim.opt.conceallevel:get() == 0 and 2 or 0
+  utils.notify(string.format("conceal %s", bool2str(vim.opt.conceallevel:get() == 2)))
+end
+
+--- Toggle diagnostics
+function M.toggle_diagnostics()
+  vim.g.diagnostics_mode = (vim.g.diagnostics_mode - 1) % 4
+  vim.diagnostic.config(require("base.utils.lsp").diagnostics[vim.g.diagnostics_mode])
+  if vim.g.diagnostics_mode == 0 then
+    utils.notify "diagnostics off"
+  elseif vim.g.diagnostics_mode == 1 then
+    utils.notify "only status diagnostics"
+  elseif vim.g.diagnostics_mode == 2 then
+    utils.notify "virtual text off"
+  else
+    utils.notify "all diagnostics on"
+  end
+end
+
+local last_active_foldcolumn
+--- Toggle foldcolumn=0|1
+function M.toggle_foldcolumn()
+  local curr_foldcolumn = vim.wo.foldcolumn
+  if curr_foldcolumn ~= "0" then last_active_foldcolumn = curr_foldcolumn end
+  vim.wo.foldcolumn = curr_foldcolumn == "0" and (last_active_foldcolumn or "1") or "0"
+  utils.notify(string.format("foldcolumn=%s", vim.wo.foldcolumn))
+end
+
+--- Toggle LSP inlay hints (global)
+-- @param bufnr? number the buffer to toggle the clients on
+function M.toggle_inlay_hints(bufnr)
+  bufnr = bufnr or 0
+  vim.g.inlay_hints_enabled = not vim.g.inlay_hints_enabled -- flip global state
+  vim.b.inlay_hints_enabled = not vim.g.inlay_hints_enabled -- sync buffer state
+  vim.lsp.buf.inlay_hint.enable(vim.g.inlay_hints_enabled, { bufnr = bufnr }) -- apply state
+  utils.notify(string.format("Global inlay hints %s", bool2str(vim.g.inlay_hints_enabled)))
+end
+
+--- Toggle lsp signature
+function M.toggle_lsp_signature()
+  local state = require('lsp_signature').toggle_float_win()
+  utils.notify(string.format("lsp signature %s", bool2str(state)))
+end
+
+--- Toggle paste
+function M.toggle_paste()
+  vim.opt.paste = not vim.opt.paste:get() -- local to window
+  utils.notify(string.format("paste %s", bool2str(vim.opt.paste:get())))
+end
+
+--- Toggle signcolumn="auto"|"no"
+function M.toggle_signcolumn()
+  if vim.wo.signcolumn == "no" then
+    vim.wo.signcolumn = "yes"
+  elseif vim.wo.signcolumn == "yes" then
+    vim.wo.signcolumn = "auto"
+  else
+    vim.wo.signcolumn = "no"
+  end
+  utils.notify(string.format("signcolumn=%s", vim.wo.signcolumn))
+end
+
+--- Toggle spell
+function M.toggle_spell()
+  vim.wo.spell = not vim.wo.spell -- local to window
+  utils.notify(string.format("spell %s", bool2str(vim.wo.spell)))
+end
+
+--- Toggle laststatus=3|2|0
+function M.toggle_statusline()
+  local laststatus = vim.opt.laststatus:get()
+  local status
+  if laststatus == 0 then
+    vim.opt.laststatus = 2
+    status = "local"
+  elseif laststatus == 2 then
+    vim.opt.laststatus = 3
+    status = "global"
+  elseif laststatus == 3 then
+    vim.opt.laststatus = 0
+    status = "off"
+  end
+  utils.notify(string.format("statusline %s", status))
+end
+
+--- Toggle showtabline=2|0
+function M.toggle_tabline()
+  vim.opt.showtabline = vim.opt.showtabline:get() == 0 and 2 or 0
+  utils.notify(string.format("tabline %s", bool2str(vim.opt.showtabline:get() == 2)))
+end
+
+--- Toggle notifications for UI toggles
+function M.toggle_ui_notifications()
+  vim.g.notifications_enabled = not vim.g.notifications_enabled
+  utils.notify(string.format("Notifications %s", bool2str(vim.g.notifications_enabled)))
+end
+
+--- Toggle URL/URI syntax highlighting rules
+function M.toggle_url_effect()
+  vim.g.url_effect_enabled = not vim.g.url_effect_enabled
+  require("base.utils").set_url_effect()
+  utils.notify(string.format("URL effect %s", bool2str(vim.g.url_effect_enabled)))
+end
+
+--- Toggle wrap
+function M.toggle_wrap()
+  vim.wo.wrap = not vim.wo.wrap -- local to window
+  utils.notify(string.format("wrap %s", bool2str(vim.wo.wrap)))
+end
+
+--- Toggle zen mode
+function M.toggle_zen_mode(bufnr)
+  bufnr = bufnr or 0
+  if not vim.b[bufnr].zen_mode then
+    vim.b[bufnr].zen_mode = true
+  else
+    vim.b[bufnr].zen_mode = false
+  end
+  utils.notify(string.format("zen mode %s", bool2str(vim.b[bufnr].zen_mode)))
+  vim.cmd "ZenMode"
+end
+
+return M

dot_config/nvim/lua/plugins/1-base-behaviors.lua 🔗

@@ -0,0 +1,709 @@
+-- Core behaviors
+-- Things that add new behaviors.
+
+--    Sections:
+--       -> ranger file browser    [ranger]
+--       -> project.nvim           [project search + auto cd]
+--       -> trim.nvim              [auto trim spaces]
+--       -> stickybuf.nvim         [lock special buffers]
+--       -> mini.bufremove         [smart bufdelete]
+--       -> smart-splits           [move and resize buffers]
+--       -> better-scape.nvim      [esc]
+--       -> toggleterm.nvim        [term]
+--       -> session-manager        [session]
+--       -> spectre.nvim           [search and replace in project]
+--       -> neotree file browser   [neotree]
+--       -> nvim-ufo               [folding mod]
+--       -> nvim-neoclip           [nvim clipboard]
+--       -> zen-mode.nvim          [distraction free mode]
+--       -> suda.vim               [write as sudo]
+--       -> vim-matchup            [Improved % motion]
+--       -> hop.nvim               [go to word visually]
+--       -> nvim-autopairs         [auto close brackets]
+--       -> lsp_signature.nvim     [auto params help]
+--       -> nvim-lightbulb         [lightbulb for code actions]
+--       -> distroupdate.nvim      [distro update]
+
+local is_windows = vim.fn.has('win32') == 1         -- true if on windows
+local is_android = vim.fn.isdirectory('/data') == 1 -- true if on android
+
+return {
+  -- [ranger] file browser
+  -- https://github.com/kevinhwang91/rnvimr
+  -- This is NormalNvim file browser, which is only for Linux.
+  --
+  -- If you are on Windows, you have 3 options:
+  -- * Use neotree instead (<space>+e).
+  -- * Delete rnvimr and install some other file browser you like.
+  -- * Or enable WLS on Windows and launch neovim from there.
+  --   This way you can install and use 'ranger' and its dependency 'pynvim'.
+  {
+    "kevinhwang91/rnvimr",
+    event = "User BaseDefered",
+    cmd = { "RnvimrToggle" },
+    enabled = not is_windows,
+    config = function()
+      -- vim.g.rnvimr_vanilla = 1            -- Often solves issues in your ranger config.
+      vim.g.rnvimr_enable_picker = 1         -- Close rnvimr after choosing a file.
+      vim.g.rnvimr_ranger_cmd = { "ranger" } -- By passing a script like TERM=foot ranger "$@" you can open terminals inside ranger.
+      if is_android then                     -- Open on full screenn
+        vim.g.rnvimr_layout = {
+          relative = "editor",
+          width = 200,
+          height = 100,
+          col = 0,
+          row = 0,
+          style = "minimal",
+        }
+      end
+    end,
+  },
+
+  -- project.nvim [project search + auto cd]
+  -- https://github.com/ahmedkhalf/project.nvim
+  {
+    "zeioth/project.nvim",
+    event = "User BaseDefered",
+    cmd = "ProjectRoot",
+    opts = {
+      -- How to find root directory
+      patterns = {
+        ".git",
+        "_darcs",
+        ".hg",
+        ".bzr",
+        ".svn",
+        "Makefile",
+        "package.json",
+        ".solution",
+        ".solution.toml"
+      },
+      -- Don't list the next projects
+      exclude_dirs = {
+        "~/"
+      },
+      silent_chdir = true,
+      manual_mode = false,
+
+      -- Don't chdir for certain buffers
+      exclude_chdir = {
+        filetype = {"", "OverseerList", "alpha"},
+        buftype = {"nofile", "terminal"},
+      },
+
+      --ignore_lsp = { "lua_ls" },
+    },
+    config = function(_, opts) require("project_nvim").setup(opts) end,
+  },
+
+  -- trim.nvim [auto trim spaces]
+  -- https://github.com/cappyzawa/trim.nvim
+  {
+    "cappyzawa/trim.nvim",
+    event = "BufWrite",
+    opts = {
+      trim_on_write = true,
+      trim_trailing = true,
+      trim_last_line = false,
+      trim_first_line = false,
+      -- ft_blocklist = { "markdown", "text", "org", "tex", "asciidoc", "rst" },
+      -- patterns = {[[%s/\(\n\n\)\n\+/\1/]]}, -- Only one consecutive bl
+    },
+  },
+
+  -- stickybuf.nvim [lock special buffers]
+  -- https://github.com/arnamak/stay-centered.nvim
+  -- By default it support neovim/aerial and others.
+  {
+    "stevearc/stickybuf.nvim",
+    event = "User BaseDefered",
+    config = function() require("stickybuf").setup() end
+  },
+
+  -- mini.bufremove [smart bufdelete]
+  -- https://github.com/echasnovski/mini.bufremove
+  -- Defines what tab to go on :bufdelete
+  {
+    "echasnovski/mini.bufremove",
+    event = "User BaseFile"
+  },
+
+  --  smart-splits [move and resize buffers]
+  --  https://github.com/mrjones2014/smart-splits.nvim
+  {
+    "mrjones2014/smart-splits.nvim",
+    event = "User BaseFile",
+    opts = {
+      ignored_filetypes = { "nofile", "quickfix", "qf", "prompt" },
+      ignored_buftypes = { "nofile" },
+    },
+  },
+
+  -- better-scape.nvim [esc]
+  -- https://github.com/max397574/better-escape.nvim
+  {
+    "max397574/better-escape.nvim",
+    event = "InsertCharPre",
+    opts = {
+      mapping = {},
+      timeout = 300,
+    },
+  },
+
+  -- Toggle floating terminal on <F7> [term]
+  -- https://github.com/akinsho/toggleterm.nvim
+  -- neovim bug → https://github.com/neovim/neovim/issues/21106
+  -- workarounds → https://github.com/akinsho/toggleterm.nvim/wiki/Mouse-support
+  {
+    "akinsho/toggleterm.nvim",
+    cmd = { "ToggleTerm", "TermExec" },
+    opts = {
+      highlights = {
+        Normal = { link = "Normal" },
+        NormalNC = { link = "NormalNC" },
+        NormalFloat = { link = "Normal" },
+        FloatBorder = { link = "FloatBorder" },
+        StatusLine = { link = "StatusLine" },
+        StatusLineNC = { link = "StatusLineNC" },
+        WinBar = { link = "WinBar" },
+        WinBarNC = { link = "WinBarNC" },
+      },
+      size = 10,
+      open_mapping = [[<F7>]],
+      shading_factor = 2,
+      direction = "float",
+      float_opts = {
+        border = "rounded",
+        highlights = { border = "Normal", background = "Normal" },
+      },
+    },
+  },
+
+  -- session-manager [session]
+  -- https://github.com/Shatur/neovim-session-manager
+  {
+    "Shatur/neovim-session-manager",
+    event = "User BaseDefered",
+    cmd = "SessionManager",
+    opts = function()
+      local config = require('session_manager.config')
+      return {
+        autoload_mode = config.AutoloadMode.Disabled,
+        autosave_last_session = false,
+        autosave_only_in_session = false,
+      }
+    end,
+    config = function(_, opts)
+      local session_manager = require('session_manager')
+      session_manager.setup(opts)
+
+      -- Auto save session
+      -- BUG: This feature will auto-close anything nofile before saving.
+      --      This include neotree, aerial, mergetool, among others.
+      --      Consider commenting the next block if this is important for you.
+      --
+      --      This won't be necessary once neovim fixes:
+      --      https://github.com/neovim/neovim/issues/12242
+      -- vim.api.nvim_create_autocmd({ 'BufWritePre' }, {
+      --   callback = function ()
+      --     session_manager.save_current_session()
+      --   end
+      -- })
+    end
+  },
+
+  -- spectre.nvim [search and replace in project]
+  -- https://github.com/nvim-pack/nvim-spectre
+  -- INSTRUCTIONS:
+  -- To see the instructions press '?'
+  -- To start the search press <ESC>.
+  -- It doesn't have ctrl-z so please always commit before using it.
+  {
+    "nvim-pack/nvim-spectre",
+    cmd = "Spectre",
+    opts = {
+      default = {
+        find = {
+          -- pick one of item in find_engine [ fd, rg ]
+          cmd = "fd",
+          options = {}
+        },
+        replace = {
+          -- pick one of item in [ sed, oxi ]
+          cmd = "sed"
+        },
+      },
+      is_insert_mode = true,    -- start open panel on is_insert_mode
+      is_block_ui_break = true, -- prevent the UI from breaking
+      mapping = {
+        ["toggle_line"] = {
+          map = "d",
+          cmd = "<cmd>lua require('spectre').toggle_line()<CR>",
+          desc = "toggle item.",
+        },
+        ["enter_file"] = {
+          map = "<cr>",
+          cmd = "<cmd>lua require('spectre.actions').select_entry()<CR>",
+          desc = "open file.",
+        },
+        ["send_to_qf"] = {
+          map = "sqf",
+          cmd = "<cmd>lua require('spectre.actions').send_to_qf()<CR>",
+          desc = "send all items to quickfix.",
+        },
+        ["replace_cmd"] = {
+          map = "src",
+          cmd = "<cmd>lua require('spectre.actions').replace_cmd()<CR>",
+          desc = "replace command.",
+        },
+        ["show_option_menu"] = {
+          map = "so",
+          cmd = "<cmd>lua require('spectre').show_options()<CR>",
+          desc = "show options.",
+        },
+        ["run_current_replace"] = {
+          map = "c",
+          cmd = "<cmd>lua require('spectre.actions').run_current_replace()<CR>",
+          desc = "confirm item.",
+        },
+        ["run_replace"] = {
+          map = "R",
+          cmd = "<cmd>lua require('spectre.actions').run_replace()<CR>",
+          desc = "replace all.",
+        },
+        ["change_view_mode"] = {
+          map = "sv",
+          cmd = "<cmd>lua require('spectre').change_view()<CR>",
+          desc = "results view mode.",
+        },
+        ["change_replace_sed"] = {
+          map = "srs",
+          cmd = "<cmd>lua require('spectre').change_engine_replace('sed')<CR>",
+          desc = "use sed to replace.",
+        },
+        ["change_replace_oxi"] = {
+          map = "sro",
+          cmd = "<cmd>lua require('spectre').change_engine_replace('oxi')<CR>",
+          desc = "use oxi to replace.",
+        },
+        ["toggle_live_update"] = {
+          map = "sar",
+          cmd = "<cmd>lua require('spectre').toggle_live_update()<CR>",
+          desc = "auto refresh changes when nvim writes a file.",
+        },
+        ["resume_last_search"] = {
+          map = "sl",
+          cmd = "<cmd>lua require('spectre').resume_last_search()<CR>",
+          desc = "repeat last search.",
+        },
+        ["insert_qwerty"] = {
+          map = "i",
+          cmd = "<cmd>startinsert<CR>",
+          desc = "insert (qwerty).",
+        },
+        ["insert_colemak"] = {
+          map = "o",
+          cmd = "<cmd>startinsert<CR>",
+          desc = "insert (colemak).",
+        },
+        ["quit"] = {
+          map = "q",
+          cmd = "<cmd>lua require('spectre').close()<CR>",
+          desc = "quit.",
+        },
+      },
+    },
+  },
+
+  -- [neotree]
+  -- https://github.com/nvim-neo-tree/neo-tree.nvim
+  {
+    "nvim-neo-tree/neo-tree.nvim",
+    dependencies = "MunifTanjim/nui.nvim",
+    cmd = "Neotree",
+    opts = function()
+      vim.g.neo_tree_remove_legacy_commands = true
+      local utils = require("base.utils")
+      local get_icon = utils.get_icon
+      return {
+        auto_clean_after_session_restore = true,
+        close_if_last_window = true,
+        buffers = {
+          show_unloaded = true
+        },
+        sources = { "filesystem", "buffers", "git_status" },
+        source_selector = {
+          winbar = true,
+          content_layout = "center",
+          sources = {
+            {
+              source = "filesystem",
+              display_name = get_icon("FolderClosed", 1, true) .. "File",
+            },
+            {
+              source = "buffers",
+              display_name = get_icon("DefaultFile", 1, true) .. "Bufs",
+            },
+            {
+              source = "git_status",
+              display_name = get_icon("Git", 1, true) .. "Git",
+            },
+            {
+              source = "diagnostics",
+              display_name = get_icon("Diagnostic", 1, true) .. "Diagnostic",
+            },
+          },
+        },
+        default_component_configs = {
+          indent = { padding = 0 },
+          icon = {
+            folder_closed = get_icon("FolderClosed"),
+            folder_open = get_icon("FolderOpen"),
+            folder_empty = get_icon("FolderEmpty"),
+            folder_empty_open = get_icon("FolderEmpty"),
+            default = get_icon "DefaultFile",
+          },
+          modified = { symbol = get_icon "FileModified" },
+          git_status = {
+            symbols = {
+              added = get_icon("GitAdd"),
+              deleted = get_icon("GitDelete"),
+              modified = get_icon("GitChange"),
+              renamed = get_icon("GitRenamed"),
+              untracked = get_icon("GitUntracked"),
+              ignored = get_icon("GitIgnored"),
+              unstaged = get_icon("GitUnstaged"),
+              staged = get_icon("GitStaged"),
+              conflict = get_icon("GitConflict"),
+            },
+          },
+        },
+        -- A command is a function that we can assign to a mapping (below)
+        commands = {
+          system_open = function(state)
+            require("base.utils").open_with_program(state.tree:get_node():get_id())
+          end,
+          parent_or_close = function(state)
+            local node = state.tree:get_node()
+            if
+                (node.type == "directory" or node:has_children())
+                and node:is_expanded()
+            then
+              state.commands.toggle_node(state)
+            else
+              require("neo-tree.ui.renderer").focus_node(
+                state,
+                node:get_parent_id()
+              )
+            end
+          end,
+          child_or_open = function(state)
+            local node = state.tree:get_node()
+            if node.type == "directory" or node:has_children() then
+              if not node:is_expanded() then -- if unexpanded, expand
+                state.commands.toggle_node(state)
+              else                           -- if expanded and has children, seleect the next child
+                require("neo-tree.ui.renderer").focus_node(
+                  state,
+                  node:get_child_ids()[1]
+                )
+              end
+            else -- if not a directory just open it
+              state.commands.open(state)
+            end
+          end,
+          copy_selector = function(state)
+            local node = state.tree:get_node()
+            local filepath = node:get_id()
+            local filename = node.name
+            local modify = vim.fn.fnamemodify
+
+            local results = {
+              e = { val = modify(filename, ":e"), msg = "Extension only" },
+              f = { val = filename, msg = "Filename" },
+              F = {
+                val = modify(filename, ":r"),
+                msg = "Filename w/o extension",
+              },
+              h = {
+                val = modify(filepath, ":~"),
+                msg = "Path relative to Home",
+              },
+              p = {
+                val = modify(filepath, ":."),
+                msg = "Path relative to CWD",
+              },
+              P = { val = filepath, msg = "Absolute path" },
+            }
+
+            local messages = {
+              { "\nChoose to copy to clipboard:\n", "Normal" },
+            }
+            for i, result in pairs(results) do
+              if result.val and result.val ~= "" then
+                vim.list_extend(messages, {
+                  { ("%s."):format(i),           "Identifier" },
+                  { (" %s: "):format(result.msg) },
+                  { result.val,                  "String" },
+                  { "\n" },
+                })
+              end
+            end
+            vim.api.nvim_echo(messages, false, {})
+            local result = results[vim.fn.getcharstr()]
+            if result and result.val and result.val ~= "" then
+              vim.notify("Copied: " .. result.val)
+              vim.fn.setreg("+", result.val)
+            end
+          end,
+          find_in_dir = function(state)
+            local node = state.tree:get_node()
+            local path = node:get_id()
+            require("telescope.builtin").find_files {
+              cwd = node.type == "directory" and path
+                  or vim.fn.fnamemodify(path, ":h"),
+            }
+          end,
+        },
+        window = {
+          width = 30,
+          mappings = {
+            ["<space>"] = false, -- disable space until we figure out which-key disabling
+            ["<S-CR>"] = "system_open",
+            ["[b"] = "prev_source",
+            ["]b"] = "next_source",
+            F = utils.is_available "telescope.nvim" and "find_in_dir" or nil,
+            O = "system_open",
+            Y = "copy_selector",
+            h = "parent_or_close",
+            l = "child_or_open",
+          },
+        },
+        filesystem = {
+          follow_current_file = {
+            enabled = true,
+          },
+          hijack_netrw_behavior = "open_current",
+          use_libuv_file_watcher = true,
+        },
+        event_handlers = {
+          {
+            event = "neo_tree_buffer_enter",
+            handler = function(_) vim.opt_local.signcolumn = "auto" end,
+          },
+        },
+      }
+    end,
+  },
+
+  --  code [folding mod] + [promise-asyn] dependency
+  --  https://github.com/kevinhwang91/nvim-ufo
+  --  https://github.com/kevinhwang91/promise-async
+  {
+    "kevinhwang91/nvim-ufo",
+    event = { "User BaseFile" },
+    dependencies = { "kevinhwang91/promise-async" },
+    opts = {
+      preview = {
+        mappings = {
+          scrollB = "<C-b>",
+          scrollF = "<C-f>",
+          scrollU = "<C-u>",
+          scrollD = "<C-d>",
+        },
+      },
+      provider_selector = function(_, filetype, buftype)
+        local function handleFallbackException(bufnr, err, providerName)
+          if type(err) == "string" and err:match "UfoFallbackException" then
+            return require("ufo").getFolds(bufnr, providerName)
+          else
+            return require("promise").reject(err)
+          end
+        end
+
+        -- only use indent until a file is opened
+        return (filetype == "" or buftype == "nofile") and "indent"
+            or function(bufnr)
+              return require("ufo")
+                  .getFolds(bufnr, "lsp")
+                  :catch(
+                    function(err)
+                      return handleFallbackException(bufnr, err, "treesitter")
+                    end
+                  )
+                  :catch(
+                    function(err)
+                      return handleFallbackException(bufnr, err, "indent")
+                    end
+                  )
+            end
+      end,
+    },
+  },
+
+  --  nvim-neoclip [nvim clipboard]
+  --  https://github.com/AckslD/nvim-neoclip.lua
+  --  Read their docs to enable cross-session history.
+  {
+    "AckslD/nvim-neoclip.lua",
+    requires = 'nvim-telescope/telescope.nvim',
+    event = "User BaseFile",
+    opts = {}
+  },
+
+  --  zen-mode.nvim [distraction free mode]
+  --  https://github.com/folke/zen-mode.nvim
+  {
+    "folke/zen-mode.nvim",
+    cmd = "ZenMode",
+  },
+
+  --  suda.nvim [write as sudo]
+  --  https://github.com/lambdalisue/suda.vim
+  {
+    "lambdalisue/suda.vim",
+    cmd = { "SudaRead", "SudaWrite" },
+  },
+
+  --  vim-matchup [improved % motion]
+  --  https://github.com/andymass/vim-matchup
+  {
+    "andymass/vim-matchup",
+    event = "User BaseFile",
+    config = function()
+      vim.g.matchup_matchparen_deferred = 1   -- work async
+      vim.g.matchup_matchparen_offscreen = {} -- disable status bar icon
+    end,
+  },
+
+  --  hop.nvim [go to word visually]
+  --  https://github.com/smoka7/hop.nvim
+  {
+    "smoka7/hop.nvim",
+    cmd = { "HopWord" },
+    opts = { keys = "etovxqpdygfblzhckisuran" }
+  },
+
+  --  nvim-autopairs [auto close brackets]
+  --  https://github.com/windwp/nvim-autopairs
+  --  It's disabled by default, you can enable it with <space>ua
+  {
+    "windwp/nvim-autopairs",
+    event = "InsertEnter",
+    opts = {
+      check_ts = true,
+      ts_config = { java = false },
+      fast_wrap = {
+        map = "<M-e>",
+        chars = { "{", "[", "(", '"', "'" },
+        pattern = string.gsub([[ [%'%"%)%>%]%)%}%,] ]], "%s+", ""),
+        offset = 0,
+        end_key = "$",
+        keys = "qwertyuiopzxcvbnmasdfghjkl",
+        check_comma = true,
+        highlight = "PmenuSel",
+        highlight_grey = "LineNr",
+      },
+    },
+    config = function(_, opts)
+      local npairs = require("nvim-autopairs")
+      npairs.setup(opts)
+      if not vim.g.autopairs_enabled then npairs.disable() end
+
+      local is_cmp_loaded, cmp = pcall(require, "cmp")
+      if is_cmp_loaded then
+        cmp.event:on(
+          "confirm_done",
+          require("nvim-autopairs.completion.cmp").on_confirm_done {
+            tex = false }
+        )
+      end
+    end
+  },
+
+  -- lsp_signature.nvim [auto params help]
+  -- https://github.com/ray-x/lsp_signature.nvim
+  {
+    "ray-x/lsp_signature.nvim",
+    event = "User BaseFile",
+    opts = function()
+      -- Apply globals from 1-options.lua
+      local is_enabled = vim.g.lsp_signature_enabled
+      local round_borders = {}
+      if vim.g.lsp_round_borders_enabled then
+        round_borders = { border = 'rounded' }
+      end
+      return {
+        -- Window mode
+        floating_window = is_enabled, -- Display it as floating window.
+        hi_parameter = "IncSearch",   -- Color to highlight floating window.
+        handler_opts = round_borders, -- Window style
+
+        -- Hint mode
+        hint_enable = false, -- Display it as hint.
+        hint_prefix = "👈 ",
+
+        -- Additionally, you can use <space>uH to toggle inlay hints.
+        toggle_key_flip_floatwin_setting = is_enabled
+      }
+    end,
+    config = function(_, opts) require('lsp_signature').setup(opts) end
+  },
+
+  -- nvim-lightbulb [lightbulb for code actions]
+  -- https://github.com/kosayoda/nvim-lightbulb
+  -- Show a lightbulb where a code action is available
+  {
+    'kosayoda/nvim-lightbulb',
+    enabled = vim.g.codeactions_enabled,
+    event = "User BaseFile",
+    opts = {
+      action_kinds = { -- show only for relevant code actions.
+        "quickfix",
+      },
+      ignore = {
+        ft = { "lua" }, -- ignore filetypes with bad code actions.
+      },
+      autocmd = {
+        enabled = true,
+        updatetime = 100,
+      },
+      sign = { enabled = false },
+      virtual_text = {
+        enabled = true,
+        text = "💡"
+      }
+    },
+    config = function(_, opts) require("nvim-lightbulb").setup(opts) end
+  },
+
+  -- distroupdate.nvim [distro update]
+  -- https://github.com/zeioth/distroupdate.nvim
+  {
+    "zeioth/distroupdate.nvim",
+    dependencies = { "nvim-lua/plenary.nvim" },
+    cmd = {
+      "DistroFreezePluginVersions",
+      "DistroReadChangelog",
+      "DistroReadVersion",
+      "DistroUpdate",
+      "DistroUpdateRevert"
+    },
+    opts = function()
+      local utils = require("base.utils")
+      local config_dir = utils.os_path(vim.fn.stdpath "config" .. "/lua/base/")
+      return {
+        channel = "stable", -- stable/nightly
+        hot_reload_files = {
+          config_dir .. "1-options.lua",
+          config_dir .. "4-mappings.lua"
+        },
+        hot_reload_callback = function()
+          vim.cmd(":silent! colorscheme " .. base.default_colorscheme) -- nvim     colorscheme reload command
+          vim.cmd(":silent! doautocmd ColorScheme")                    -- heirline colorscheme reload event
+        end
+      }
+    end
+  },
+
+} -- end of return

dot_config/nvim/lua/plugins/2-ui.lua 🔗

@@ -0,0 +1,709 @@
+-- User interface
+-- Things that make the GUI better.
+
+--    Sections:
+--       -> tokyonight                  [theme]
+--       -> astrotheme                  [theme]
+--       -> alpha-nvim                  [greeter]
+--       -> nvim-notify                 [notifications]
+--       -> mini.indentscope            [guides]
+--       -> heirline-components.nvim    [ui components]
+--       -> heirline                    [ui components]
+--       -> telescope                   [search]
+--       -> telescope-fzf-native.nvim   [search backend]
+--       -> smart-splits                [window-dimming]
+--       -> dressing.nvim               [better ui elements]
+--       -> noice.nvim                  [better cmd/search line]
+--       -> nvim-web-devicons           [icons | ui]
+--       -> lspkind.nvim                [icons | lsp]
+--       -> nvim-scrollbar              [scrollbar]
+--       -> mini.animate                [animations]
+--       -> highlight-undo              [highlights]
+--       -> which-key                   [on-screen keybinding]
+
+local utils = require "base.utils"
+local is_windows = vim.fn.has('win32') == 1         -- true if on windows
+local is_android = vim.fn.isdirectory('/data') == 1 -- true if on android
+
+return {
+
+  -- tokyonight [theme]
+  -- https://github.com/folke/tokyonight.nvim
+  {
+    "zeioth/tokyonight.nvim",
+    event = "User LoadColorSchemes",
+    opts = {
+      dim_inactive = false,
+      styles = {
+        comments = { italic = true },
+        keywords = { italic = true },
+      },
+    }
+  },
+
+  --  astrotheme [theme]
+  --  https://github.com/AstroNvim/astrotheme
+  {
+    "AstroNvim/astrotheme",
+    event = "User LoadColorSchemes",
+    opts = {
+      palette = "astrodark",
+      plugins = { ["dashboard-nvim"] = true },
+    },
+  },
+
+  --  alpha-nvim [greeter]
+  --  https://github.com/goolord/alpha-nvim
+  {
+    "goolord/alpha-nvim",
+    cmd = "Alpha",
+    -- setup header and buttonts
+    opts = function()
+      local dashboard = require("alpha.themes.dashboard")
+
+      -- Header
+      -- dashboard.section.header.val = {
+      --   "                                                                     ",
+      --   "       ████ ██████           █████      ██                     ",
+      --   "      ███████████             █████                             ",
+      --   "      █████████ ███████████████████ ███   ███████████   ",
+      --   "     █████████  ███    █████████████ █████ ██████████████   ",
+      --   "    █████████ ██████████ █████████ █████ █████ ████ █████   ",
+      --   "  ███████████ ███    ███ █████████ █████ █████ ████ █████  ",
+      --   " ██████  █████████████████████ ████ █████ █████ ████ ██████ ",
+      -- }
+      -- dashboard.section.header.val = {
+      --   '                                        ▟▙            ',
+      --   '                                        ▝▘            ',
+      --   '██▃▅▇█▆▖  ▗▟████▙▖   ▄████▄   ██▄  ▄██  ██  ▗▟█▆▄▄▆█▙▖',
+      --   '██▛▔ ▝██  ██▄▄▄▄██  ██▛▔▔▜██  ▝██  ██▘  ██  ██▛▜██▛▜██',
+      --   '██    ██  ██▀▀▀▀▀▘  ██▖  ▗██   ▜█▙▟█▛   ██  ██  ██  ██',
+      --   '██    ██  ▜█▙▄▄▄▟▊  ▀██▙▟██▀   ▝████▘   ██  ██  ██  ██',
+      --   '▀▀    ▀▀   ▝▀▀▀▀▀     ▀▀▀▀       ▀▀     ▀▀  ▀▀  ▀▀  ▀▀',
+      -- }
+      -- dashboard.section.header.val = {
+      --   '                    ▟▙            ',
+      --   '                    ▝▘            ',
+      --   '██▃▅▇█▆▖  ██▄  ▄██  ██  ▗▟█▆▄▄▆█▙▖',
+      --   '██▛▔ ▝██  ▝██  ██▘  ██  ██▛▜██▛▜██',
+      --   '██    ██   ▜█▙▟█▛   ██  ██  ██  ██',
+      --   '██    ██   ▝████▘   ██  ██  ██  ██',
+      --   '▀▀    ▀▀     ▀▀     ▀▀  ▀▀  ▀▀  ▀▀',
+      -- }
+      -- Generated with https://www.fancytextpro.com/BigTextGenerator/Larry3D
+      -- dashboard.section.header.val = {
+      --   [[ __  __                  __  __                     ]],
+      --   [[/\ \/\ \                /\ \/\ \  __                ]],
+      --   [[\ \ `\\ \     __    ___ \ \ \ \ \/\_\    ___ ___    ]],
+      --   [[ \ \ , ` \  /'__`\ / __`\\ \ \ \ \/\ \ /' __` __`\  ]],
+      --   [[  \ \ \`\ \/\  __//\ \L\ \\ \ \_/ \ \ \/\ \/\ \/\ \ ]],
+      --   [[   \ \_\ \_\ \____\ \____/ \ `\___/\ \_\ \_\ \_\ \_\]],
+      --   [[    \/_/\/_/\/____/\/___/   `\/__/  \/_/\/_/\/_/\/_/]],
+      -- }
+      --  dashboard.section.header.val = {
+      --   '                                                     ',
+      --   '  ███╗   ██╗███████╗ ██████╗ ██╗   ██╗██╗███╗   ███╗ ',
+      --   '  ████╗  ██║██╔════╝██╔═══██╗██║   ██║██║████╗ ████║ ',
+      --   '  ██╔██╗ ██║█████╗  ██║   ██║██║   ██║██║██╔████╔██║ ',
+      --   '  ██║╚██╗██║██╔══╝  ██║   ██║╚██╗ ██╔╝██║██║╚██╔╝██║ ',
+      --   '  ██║ ╚████║███████╗╚██████╔╝ ╚████╔╝ ██║██║ ╚═╝ ██║ ',
+      --   '  ╚═╝  ╚═══╝╚══════╝ ╚═════╝   ╚═══╝  ╚═╝╚═╝     ╚═╝ ',
+      --   '                                                     ',
+      -- }
+      -- dashboard.section.header.val = {
+      --   [[                __                ]],
+      --   [[  ___   __  __ /\_\    ___ ___    ]],
+      --   [[/' _ `\/\ \/\ \\/\ \ /' __` __`\  ]],
+      --   [[/\ \/\ \ \ \_/ |\ \ \/\ \/\ \/\ \ ]],
+      --   [[\ \_\ \_\ \___/  \ \_\ \_\ \_\ \_\]],
+      --   [[ \/_/\/_/\/__/    \/_/\/_/\/_/\/_/]],
+      -- }
+
+      if is_android then
+        dashboard.section.header.val = {
+          [[         __                ]],
+          [[ __  __ /\_\    ___ ___    ]],
+          [[/\ \/\ \\/\ \ /' __` __`\  ]],
+          [[\ \ \_/ |\ \ \/\ \/\ \/\ \ ]],
+          [[ \ \___/  \ \_\ \_\ \_\ \_\]],
+          [[  \/__/    \/_/\/_/\/_/\/_/]],
+        }
+      else
+        dashboard.section.header.val = {
+          [[888b      88                                                           88]],
+          [[8888b     88                                                           88]],
+          [[88 `8b    88                                                           88]],
+          [[88  `8b   88   ,adPPYba,   8b,dPPYba,  88,dPYba,,adPYba,   ,adPPYYba,  88]],
+          [[88   `8b  88  a8"     "8a  88P'   "Y8  88P'   "88"    "8a  ""     `Y8  88]],
+          [[88    `8b 88  8b       d8  88          88      88      88  ,adPPPPP88  88]],
+          [[88     `8888  "8a,   ,a8"  88          88      88      88  88,    ,88  88]],
+          [[88      `888   `"YbbdP"'   88          88      88      88  `"8bbdP"Y8  88]],
+          [[                                    __                ]],
+          [[                      ___   __  __ /\_\    ___ ___    ]],
+          [[                    /' _ `\/\ \/\ \\/\ \ /' __` __`\  ]],
+          [[                    /\ \/\ \ \ \_/ |\ \ \/\ \/\ \/\ \ ]],
+          [[                    \ \_\ \_\ \___/  \ \_\ \_\ \_\ \_\]],
+          [[                     \/_/\/_/\/__/    \/_/\/_/\/_/\/_/]],
+        }
+      end
+
+      dashboard.section.header.opts.hl = "DashboardHeader"
+      vim.cmd "highlight DashboardHeader guifg=#F7778F"
+
+      -- If on windows, don't show the 'ranger' button
+      local ranger_button = dashboard.button("r", "🐍 Ranger  ", "<cmd>RnvimrToggle<CR>")
+      if is_windows then ranger_button = nil end
+
+      -- Buttons
+      dashboard.section.buttons.val = {
+        dashboard.button("n", "📄 New     ", "<cmd>ene<CR>"),
+        dashboard.button("e", "🌺 Recent  ", "<cmd>Telescope oldfiles<CR>"),
+        ranger_button,
+        dashboard.button(
+          "s",
+          "🔎 Sessions",
+          "<cmd>SessionManager! load_session<CR>"
+        ),
+        dashboard.button("p", "💼 Projects", "<cmd>Telescope projects<CR>"),
+        dashboard.button("", ""),
+        dashboard.button("q", "   Quit", "<cmd>exit<CR>"),
+        --  --button("LDR f '", "  Bookmarks  "),
+      }
+
+      -- Vertical margins
+      dashboard.config.layout[1].val =
+          vim.fn.max { 2, vim.fn.floor(vim.fn.winheight(0) * 0.10) } -- Above header
+      dashboard.config.layout[3].val =
+          vim.fn.max { 2, vim.fn.floor(vim.fn.winheight(0) * 0.10) } -- Above buttons
+
+      -- Disable autocmd and return
+      dashboard.config.opts.noautocmd = true
+      return dashboard
+    end,
+    config = function(_, opts)
+      -- Footer
+      require("alpha").setup(opts.config)
+      vim.api.nvim_create_autocmd("User", {
+        pattern = "LazyVimStarted",
+        desc = "Add Alpha dashboard footer",
+        once = true,
+        callback = function()
+          local stats = require("lazy").stats()
+          stats.real_cputime = not is_windows
+          local ms = math.floor(stats.startuptime * 100 + 0.5) / 100
+          opts.section.footer.val = {
+            " ",
+            " ",
+            " ",
+            "Loaded " .. stats.loaded .. " plugins  in " .. ms .. "ms",
+            ".............................",
+          }
+          opts.section.footer.opts.hl = "DashboardFooter"
+          vim.cmd "highlight DashboardFooter guifg=#D29B68"
+          pcall(vim.cmd.AlphaRedraw)
+        end,
+      })
+    end,
+  },
+
+  --  [notifications]
+  --  https://github.com/rcarriga/nvim-notify
+  {
+    "rcarriga/nvim-notify",
+    event = "User BaseDefered",
+    opts = function()
+      local fps
+      if is_android then fps = 30 else fps = 144 end
+
+      return {
+        timeout = 2500,
+        fps = fps,
+        max_height = function() return math.floor(vim.o.lines * 0.75) end,
+        max_width = function() return math.floor(vim.o.columns * 0.75) end,
+        on_open = function(win)
+          -- enable markdown support on notifications
+          vim.api.nvim_win_set_config(win, { zindex = 175 })
+          if not vim.g.notifications_enabled then
+            vim.api.nvim_win_close(win, true)
+          end
+          if not package.loaded["nvim-treesitter"] then
+            pcall(require, "nvim-treesitter")
+          end
+          vim.wo[win].conceallevel = 3
+          local buf = vim.api.nvim_win_get_buf(win)
+          if not pcall(vim.treesitter.start, buf, "markdown") then
+            vim.bo[buf].syntax = "markdown"
+          end
+          vim.wo[win].spell = false
+        end,
+      }
+    end,
+    config = function(_, opts)
+      local notify = require("notify")
+      notify.setup(opts)
+      vim.notify = notify
+    end,
+  },
+
+  --  mini.indentscope [guides]
+  --  https://github.com/echasnovski/mini.indentscope
+  {
+    "echasnovski/mini.indentscope",
+    event = { "BufReadPre", "BufNewFile" },
+    opts = {
+      draw = { delay = 0, animation = function() return 0 end },
+      options = { border = "top", try_as_border = true },
+      symbol = "▏",
+    },
+    config = function(_, opts)
+      require("mini.indentscope").setup(opts)
+
+      -- Disable for certain filetypes
+      vim.api.nvim_create_autocmd({ "FileType" }, {
+        desc = "Disable indentscope for certain filetypes",
+        callback = function()
+          local ignored_filetypes = {
+            "aerial",
+            "dashboard",
+            "help",
+            "lazy",
+            "leetcode.nvim",
+            "mason",
+            "neo-tree",
+            "NvimTree",
+            "neogitstatus",
+            "notify",
+            "startify",
+            "toggleterm",
+            "Trouble",
+            "calltree",
+            "coverage"
+          }
+          if vim.tbl_contains(ignored_filetypes, vim.bo.filetype) then
+            vim.b.miniindentscope_disable = true
+          end
+        end,
+      })
+    end
+  },
+
+  -- heirline-components.nvim [ui components]
+  -- https://github.com/zeioth/heirline-components.nvim
+  -- Collection of components to use on your heirline config.
+  {
+    "zeioth/heirline-components.nvim",
+    opts = {
+      icons = require("base.icons.nerd_font")
+    }
+  },
+
+  --  heirline [ui components]
+  --  https://github.com/rebelot/heirline.nvim
+  --  Use it to customize the components of your user interface,
+  --  Including tabline, winbar, statuscolumn, statusline.
+  --  Be aware some components are positional. Read heirline documentation.
+  {
+    "rebelot/heirline.nvim",
+    dependencies = { "zeioth/heirline-components.nvim" },
+    event = "User BaseDefered",
+    opts = function()
+      local lib = require "heirline-components.all"
+      return {
+        opts = {
+          disable_winbar_cb = function(args) -- We do this to avoid showing it on the greeter.
+            local is_disabled = not require("heirline-components.buffer").is_valid(args.buf) or
+                lib.condition.buffer_matches({
+                  buftype = { "terminal", "prompt", "nofile", "help", "quickfix" },
+                  filetype = { "NvimTree", "neo%-tree", "dashboard", "Outline", "aerial" },
+                }, args.buf)
+            return is_disabled
+          end,
+        },
+        tabline = { -- UI upper bar
+          lib.component.tabline_conditional_padding(),
+          lib.component.tabline_buffers(),
+          lib.component.fill { hl = { bg = "tabline_bg" } },
+          lib.component.tabline_tabpages()
+        },
+        winbar = { -- UI breadcrumbs bar
+          init = function(self) self.bufnr = vim.api.nvim_get_current_buf() end,
+          fallthrough = false,
+          -- Winbar for terminal, neotree, and aerial.
+          {
+            condition = function() return not lib.condition.is_active() end,
+            {
+              lib.component.neotree(),
+              lib.component.compiler_play(),
+              lib.component.fill(),
+              lib.component.compiler_redo(),
+              lib.component.aerial(),
+            },
+          },
+          -- Regular winbar
+          {
+            lib.component.neotree(),
+            lib.component.compiler_play(),
+            lib.component.fill(),
+            lib.component.breadcrumbs(),
+            lib.component.fill(),
+            lib.component.compiler_redo(),
+            lib.component.aerial(),
+          }
+        },
+        statuscolumn = { -- UI left column
+          init = function(self) self.bufnr = vim.api.nvim_get_current_buf() end,
+          lib.component.foldcolumn(),
+          lib.component.numbercolumn(),
+          lib.component.signcolumn(),
+        } or nil,
+        statusline = { -- UI statusbar
+          hl = { fg = "fg", bg = "bg" },
+          lib.component.mode(),
+          lib.component.git_branch(),
+          lib.component.file_info(),
+          lib.component.git_diff(),
+          lib.component.diagnostics(),
+          lib.component.fill(),
+          lib.component.cmd_info(),
+          lib.component.fill(),
+          lib.component.lsp(),
+          lib.component.compiler_state(),
+          lib.component.virtual_env(),
+          lib.component.nav(),
+          lib.component.mode { surround = { separator = "right" } },
+        },
+      }
+    end,
+    config = function(_, opts)
+      local heirline = require("heirline")
+      local heirline_components = require "heirline-components.all"
+
+      -- Setup
+      heirline_components.init.subscribe_to_events()
+      heirline.load_colors(heirline_components.hl.get_colors())
+      heirline.setup(opts)
+    end,
+  },
+
+  --  Telescope [search] + [search backend] dependency
+  --  https://github.com/nvim-telescope/telescope.nvim
+  --  https://github.com/nvim-telescope/telescope-fzf-native.nvim
+  --  https://github.com/debugloop/telescope-undo.nvim
+  --  NOTE: Normally, plugins that depend on Telescope are defined separately.
+  --  But its Telescope extension is added in the Telescope 'config' section.
+  {
+    "nvim-telescope/telescope.nvim",
+    dependencies = {
+      {
+        "debugloop/telescope-undo.nvim",
+        cmd = "Telescope",
+      },
+      {
+        "nvim-telescope/telescope-fzf-native.nvim",
+        enabled = vim.fn.executable "make" == 1,
+        build = "make",
+      },
+    },
+    cmd = "Telescope",
+    opts = function()
+      local get_icon = require("base.utils").get_icon
+      local actions = require("telescope.actions")
+      local mappings = {
+        i = {
+          ["<C-n>"] = actions.cycle_history_next,
+          ["<C-p>"] = actions.cycle_history_prev,
+          ["<C-j>"] = actions.move_selection_next,
+          ["<C-k>"] = actions.move_selection_previous,
+          ["<ESC>"] = actions.close,
+          ["<C-c>"] = false,
+        },
+        n = { ["q"] = actions.close },
+      }
+      return {
+        defaults = {
+          prompt_prefix = get_icon("Selected", 1),
+          selection_caret = get_icon("Selected", 1),
+          multi_icon = get_icon("selected", 1),
+          path_display = { "truncate" },
+          sorting_strategy = "ascending",
+          layout_config = {
+            horizontal = {
+              prompt_position = "top",
+              preview_width = 0.50,
+            },
+            vertical = {
+              mirror = false,
+            },
+            width = 0.87,
+            height = 0.80,
+            preview_cutoff = 120,
+          },
+          mappings = mappings,
+        },
+        extensions = {
+          undo = {
+            use_delta = true,
+            side_by_side = true,
+            diff_context_lines = 0,
+            entry_format = "󰣜 #$ID, $STAT, $TIME",
+            layout_strategy = "horizontal",
+            layout_config = {
+              preview_width = 0.65,
+            },
+            mappings = {
+              i = {
+                ["<cr>"] = require("telescope-undo.actions").yank_additions,
+                ["<S-cr>"] = require("telescope-undo.actions").yank_deletions,
+                ["<C-cr>"] = require("telescope-undo.actions").restore,
+              },
+            },
+          },
+        },
+      }
+    end,
+    config = function(_, opts)
+      local telescope = require("telescope")
+      telescope.setup(opts)
+      -- Here we define the Telescope extension for all plugins.
+      -- If you delete a plugin, you can also delete its Telescope extension.
+      if utils.is_available("nvim-notify") then telescope.load_extension("notify") end
+      if utils.is_available("telescope-fzf-native.nvim") then telescope.load_extension("fzf") end
+      if utils.is_available("telescope-undo.nvim") then telescope.load_extension("undo") end
+      if utils.is_available("project.nvim") then telescope.load_extension("projects") end
+      if utils.is_available("LuaSnip") then telescope.load_extension("luasnip") end
+      if utils.is_available("aerial.nvim") then telescope.load_extension("aerial") end
+      if utils.is_available("nvim-neoclip.lua") then
+        telescope.load_extension("neoclip")
+        telescope.load_extension("macroscope")
+      end
+    end,
+  },
+
+  --  [better ui elements]
+  --  https://github.com/stevearc/dressing.nvim
+  {
+    "stevearc/dressing.nvim",
+    event = "User BaseDefered",
+    opts = {
+      input = { default_prompt = "➤ " },
+      select = { backend = { "telescope", "builtin" } },
+    }
+  },
+
+  --  Noice.nvim [better cmd/search line]
+  --  https://github.com/folke/noice.nvim
+  --  We use it for:
+  --  * cmdline: Display treesitter for :
+  --  * search: Display a magnifier instead of /
+  --
+  --  We don't use it for:
+  --  * LSP status: We use a heirline component for this.
+  --  * Search results: We use a heirline component for this.
+  {
+    "folke/noice.nvim",
+    event = "User BaseDefered",
+    opts = function()
+      local enable_conceal = false          -- Hide command text if true
+      return {
+        presets = { bottom_search = true }, -- The kind of popup used for /
+        cmdline = {
+          view = "cmdline",                 -- The kind of popup used for :
+          format = {
+            cmdline = { conceal = enable_conceal },
+            search_down = { conceal = enable_conceal },
+            search_up = { conceal = enable_conceal },
+            filter = { conceal = enable_conceal },
+            lua = { conceal = enable_conceal },
+            help = { conceal = enable_conceal },
+            input = { conceal = enable_conceal },
+          }
+        },
+
+        -- Disable every other noice feature
+        messages = { enabled = false },
+        lsp = {
+          hover = { enabled = false },
+          signature = { enabled = false },
+          progress = { enabled = false },
+          message = { enabled = false },
+          smart_move = { enabled = false },
+        },
+      }
+    end
+  },
+
+  --  UI icons [icons]
+  --  https://github.com/nvim-tree/nvim-web-devicons
+  {
+    "nvim-tree/nvim-web-devicons",
+    enabled = vim.g.icons_enabled,
+    event = "User BaseDefered",
+    opts = {
+      override = {
+        default_icon = {
+          icon = require("base.utils").get_icon("DefaultFile"),
+          name = "default"
+        },
+        deb = { icon = "", name = "Deb" },
+        lock = { icon = "󰌾", name = "Lock" },
+        mp3 = { icon = "󰎆", name = "Mp3" },
+        mp4 = { icon = "", name = "Mp4" },
+        out = { icon = "", name = "Out" },
+        ["robots.txt"] = { icon = "󰚩", name = "Robots" },
+        ttf = { icon = "", name = "TrueTypeFont" },
+        rpm = { icon = "", name = "Rpm" },
+        woff = { icon = "", name = "WebOpenFontFormat" },
+        woff2 = { icon = "", name = "WebOpenFontFormat2" },
+        xz = { icon = "", name = "Xz" },
+        zip = { icon = "", name = "Zip" },
+      },
+    },
+    config = function(_, opts)
+      require("nvim-web-devicons").setup(opts)
+      pcall(vim.api.nvim_del_user_command, "NvimWebDeviconsHiTest")
+    end
+  },
+
+  --  LSP icons [icons]
+  --  https://github.com/onsails/lspkind.nvim
+  {
+    "onsails/lspkind.nvim",
+    opts = {
+      mode = "symbol",
+      symbol_map = {
+        Array = "󰅪",
+        Boolean = "⊨",
+        Class = "󰌗",
+        Constructor = "",
+        Key = "󰌆",
+        Namespace = "󰅪",
+        Null = "NULL",
+        Number = "#",
+        Object = "󰀚",
+        Package = "󰏗",
+        Property = "",
+        Reference = "",
+        Snippet = "",
+        String = "󰀬",
+        TypeParameter = "󰊄",
+        Unit = "",
+      },
+      menu = {},
+    },
+    enabled = vim.g.icons_enabled,
+    config = function(_, opts)
+      require("lspkind").init(opts)
+    end,
+  },
+
+  --  nvim-scrollbar [scrollbar]
+  --  https://github.com/petertriho/nvim-scrollbar
+  {
+    "petertriho/nvim-scrollbar",
+    event = "User BaseFile",
+    opts = {
+      handlers = {
+        gitsigns = true, -- gitsigns integration (display hunks)
+        ale = true,      -- lsp integration (display errors/warnings)
+        search = false,  -- hlslens integration (display search result)
+      },
+      excluded_filetypes = {
+        "cmp_docs",
+        "cmp_menu",
+        "noice",
+        "prompt",
+        "TelescopePrompt",
+        "alpha",
+      },
+    },
+  },
+
+  --  mini.animate [animations]
+  --  https://github.com/echasnovski/mini.animate
+  --  HINT: if one of your personal keymappings fail due to mini.animate, try to
+  --        disable it during the keybinding using vim.g.minianimate_disable = true
+  {
+    "echasnovski/mini.animate",
+    event = "User BaseFile",
+    enabled = not is_android,
+    opts = function()
+      -- don't use animate when scrolling with the mouse
+      local mouse_scrolled = false
+      for _, scroll in ipairs { "Up", "Down" } do
+        local key = "<ScrollWheel" .. scroll .. ">"
+        vim.keymap.set({ "", "i" }, key, function()
+          mouse_scrolled = true
+          return key
+        end, { expr = true })
+      end
+
+      local animate = require("mini.animate")
+      return {
+        open = { enable = false }, -- true causes issues on nvim-spectre
+        resize = {
+          timing = animate.gen_timing.linear { duration = 33, unit = "total" },
+        },
+        scroll = {
+          timing = animate.gen_timing.linear { duration = 50, unit = "total" },
+          subscroll = animate.gen_subscroll.equal {
+            predicate = function(total_scroll)
+              if mouse_scrolled then
+                mouse_scrolled = false
+                return false
+              end
+              return total_scroll > 1
+            end,
+          },
+        },
+        cursor = {
+          enable = false, -- We don't want cursor ghosting
+          timing = animate.gen_timing.linear { duration = 26, unit = "total" },
+        },
+      }
+    end,
+  },
+
+  --  highlight-undo
+  --  https://github.com/tzachar/highlight-undo.nvim
+  --  This plugin only flases on redo.
+  --  But we also have a autocmd to flash on yank.
+  {
+    "tzachar/highlight-undo.nvim",
+    event = "User BaseDefered",
+    opts = {
+      hlgroup = "CurSearch",
+      duration = 150,
+      keymaps = {
+        { "n", "u",     "undo", {} }, -- If you remap undo/redo, change this
+        { "n", "<C-r>", "redo", {} },
+      },
+    },
+    config = function(_, opts)
+      require("highlight-undo").setup(opts)
+
+      -- Also flash on yank.
+      vim.api.nvim_create_autocmd("TextYankPost", {
+        desc = "Highlight yanked text",
+        pattern = "*",
+        callback = function() vim.highlight.on_yank() end,
+      })
+    end,
+  },
+
+  --  which-key.nvim [on-screen keybindings]
+  --  https://github.com/folke/which-key.nvim
+  {
+    "folke/which-key.nvim",
+    event = "User BaseDefered",
+    opts = {
+      icons = { group = vim.g.icons_enabled and "" or "+", separator = "" },
+      disable = { filetypes = { "TelescopePrompt" } },
+    },
+    config = function(_, opts)
+      require("which-key").setup(opts)
+      require("base.utils").which_key_register()
+    end,
+  },
+
+
+} -- end of return

dot_config/nvim/lua/plugins/3-dev-core.lua 🔗

@@ -0,0 +1,510 @@
+-- Dev core
+-- Things that are just there.
+
+--    Sections:
+--       ## TREE SITTER
+--       -> nvim-treesitter                [syntax highlight]
+--       -> nvim-ts-autotag                [treesitter understand html tags]
+--       -> ts-comments.nvim               [treesitter comments]
+--       -> nvim-colorizer                 [hex colors]
+
+--       ## LSP
+--       -> nvim-java                      [java support]
+--       -> mason-lspconfig                [auto start lsp]
+--       -> nvim-lspconfig                 [lsp configs]
+--       -> mason.nvim                     [lsp package manager]
+--       -> SchemaStore.nvim               [mason extra schemas]
+--       -> none-ls-autoload.nvim          [mason package loader]
+--       -> none-ls                        [lsp code formatting]
+--       -> neodev                         [lsp for nvim lua api]
+--       -> garbage-day                    [lsp garbage collector]
+
+--       ## AUTO COMPLETION
+--       -> nvim-cmp                       [auto completion engine]
+--       -> cmp-nvim-buffer                [auto completion buffer]
+--       -> cmp-nvim-path                  [auto completion path]
+--       -> cmp-nvim-lsp                   [auto completion lsp]
+--       -> cmp-luasnip                    [auto completion snippets]
+
+local utils = require("base.utils")
+local utils_lsp = require("base.utils.lsp")
+
+return {
+  --  TREE SITTER ---------------------------------------------------------
+  --  [syntax highlight] + [treesitter understand html tags] + [comments]
+  --  https://github.com/nvim-treesitter/nvim-treesitter
+  --  https://github.com/windwp/nvim-ts-autotag
+  --  https://github.com/windwp/nvim-treesitter-textobjects
+  {
+    "nvim-treesitter/nvim-treesitter",
+    dependencies = {
+      "windwp/nvim-ts-autotag",
+      "nvim-treesitter/nvim-treesitter-textobjects",
+    },
+    event = "User BaseDefered",
+    cmd = {
+      "TSBufDisable",
+      "TSBufEnable",
+      "TSBufToggle",
+      "TSDisable",
+      "TSEnable",
+      "TSToggle",
+      "TSInstall",
+      "TSInstallInfo",
+      "TSInstallSync",
+      "TSModuleInfo",
+      "TSUninstall",
+      "TSUpdate",
+      "TSUpdateSync",
+    },
+    build = ":TSUpdate",
+    init = function(plugin)
+      -- perf: make treesitter queries available at startup.
+      require("lazy.core.loader").add_to_rtp(plugin)
+      require("nvim-treesitter.query_predicates")
+    end,
+    opts = {
+      auto_install = false, -- Currently bugged. Use [:TSInstall all] and [:TSUpdate all]
+      autotag = { enable = true },
+      highlight = {
+        enable = true,
+        disable = function(_, bufnr)
+          local excluded_filetypes = {} -- disabled for
+          local is_disabled = vim.tbl_contains(
+            excluded_filetypes, vim.bo.filetype) or utils.is_big_file(bufnr)
+          return is_disabled
+        end,
+      },
+      matchup = {
+        enable = true,
+        enable_quotes = true,
+        disable = function(_, bufnr)
+          local excluded_filetypes = {} -- disabled for
+          local is_disabled = vim.tbl_contains(
+            excluded_filetypes, vim.bo.filetype) or utils.is_big_file(bufnr)
+          return is_disabled
+        end,
+      },
+      incremental_selection = { enable = true },
+      indent = { enable = true },
+      textobjects = {
+        select = {
+          enable = true,
+          lookahead = true,
+          keymaps = {
+            ["ak"] = { query = "@block.outer", desc = "around block" },
+            ["ik"] = { query = "@block.inner", desc = "inside block" },
+            ["ac"] = { query = "@class.outer", desc = "around class" },
+            ["ic"] = { query = "@class.inner", desc = "inside class" },
+            ["a?"] = { query = "@conditional.outer", desc = "around conditional" },
+            ["i?"] = { query = "@conditional.inner", desc = "inside conditional" },
+            ["af"] = { query = "@function.outer", desc = "around function " },
+            ["if"] = { query = "@function.inner", desc = "inside function " },
+            ["al"] = { query = "@loop.outer", desc = "around loop" },
+            ["il"] = { query = "@loop.inner", desc = "inside loop" },
+            ["aa"] = { query = "@parameter.outer", desc = "around argument" },
+            ["ia"] = { query = "@parameter.inner", desc = "inside argument" },
+          },
+        },
+        move = {
+          enable = true,
+          set_jumps = true,
+          goto_next_start = {
+            ["]k"] = { query = "@block.outer", desc = "Next block start" },
+            ["]f"] = { query = "@function.outer", desc = "Next function start" },
+            ["]a"] = { query = "@parameter.inner", desc = "Next parameter start" },
+          },
+          goto_next_end = {
+            ["]K"] = { query = "@block.outer", desc = "Next block end" },
+            ["]F"] = { query = "@function.outer", desc = "Next function end" },
+            ["]A"] = { query = "@parameter.inner", desc = "Next parameter end" },
+          },
+          goto_previous_start = {
+            ["[k"] = { query = "@block.outer", desc = "Previous block start" },
+            ["[f"] = { query = "@function.outer", desc = "Previous function start" },
+            ["[a"] = { query = "@parameter.inner", desc = "Previous parameter start" },
+          },
+          goto_previous_end = {
+            ["[K"] = { query = "@block.outer", desc = "Previous block end" },
+            ["[F"] = { query = "@function.outer", desc = "Previous function end" },
+            ["[A"] = { query = "@parameter.inner", desc = "Previous parameter end" },
+          },
+        },
+        swap = {
+          enable = true,
+          swap_next = {
+            [">K"] = { query = "@block.outer", desc = "Swap next block" },
+            [">F"] = { query = "@function.outer", desc = "Swap next function" },
+            [">A"] = { query = "@parameter.inner", desc = "Swap next parameter" },
+          },
+          swap_previous = {
+            ["<K"] = { query = "@block.outer", desc = "Swap previous block" },
+            ["<F"] = { query = "@function.outer", desc = "Swap previous function" },
+            ["<A"] = { query = "@parameter.inner", desc = "Swap previous parameter" },
+          },
+        },
+      },
+    },
+  },
+
+  -- ts-comments.nvim [treesitter comments]
+  -- https://github.com/folke/ts-comments.nvim
+  -- This plugin can be safely removed after nvim 0.11 is released.
+  {
+   "folke/ts-comments.nvim",
+    event = "User BaseFile",
+    enabled = vim.fn.has("nvim-0.10.0") == 1,
+    opts = {},
+  },
+
+  --  [hex colors]
+  --  https://github.com/NvChad/nvim-colorizer.lua
+  {
+    "NvChad/nvim-colorizer.lua",
+    event = "User BaseFile",
+    cmd = {
+      "ColorizerToggle",
+      "ColorizerAttachToBuffer",
+      "ColorizerDetachFromBuffer",
+      "ColorizerReloadAllBuffers",
+    },
+    opts = { user_default_options = { names = false } },
+  },
+
+  --  LSP -------------------------------------------------------------------
+
+  -- nvim-java [java support]
+  -- https://github.com/nvim-java/nvim-java
+  -- Reliable jdtls support. Must go before mason-lspconfig and lsp-config.
+  {
+    "nvim-java/nvim-java",
+    ft = { "java" },
+    dependencies = {
+      "nvim-java/lua-async-await",
+      'nvim-java/nvim-java-refactor',
+      "nvim-java/nvim-java-core",
+      "nvim-java/nvim-java-test",
+      "nvim-java/nvim-java-dap",
+      "MunifTanjim/nui.nvim",
+      "neovim/nvim-lspconfig",
+      "mfussenegger/nvim-dap",
+      "williamboman/mason.nvim",
+    },
+    opts = {
+      notifications = {
+        dap = false,
+      },
+      -- NOTE: One of these files must be in your project root directory.
+      --       Otherwise the debugger will end in the wrong directory and fail.
+      root_markers = {
+        'settings.gradle',
+        'settings.gradle.kts',
+        'pom.xml',
+        'build.gradle',
+        'mvnw',
+        'gradlew',
+        'build.gradle',
+        'build.gradle.kts',
+        '.git',
+      },
+    },
+  },
+
+  --  nvim-lspconfig [lsp configs]
+  --  https://github.com/neovim/nvim-lspconfig
+  --  This plugin provide default configs for the lsp servers available on mason.
+  {
+    "neovim/nvim-lspconfig",
+    event = "User BaseFile",
+    dependencies = "nvim-java/nvim-java",
+  },
+
+  -- mason-lspconfig [auto start lsp]
+  -- https://github.com/williamboman/mason-lspconfig.nvim
+  -- This plugin auto starts the lsp servers installed by Mason
+  -- every time Neovim trigger the event FileType.
+  {
+    "williamboman/mason-lspconfig.nvim",
+    dependencies = { "neovim/nvim-lspconfig" },
+    event = "User BaseFile",
+    opts = function(_, opts)
+      if not opts.handlers then opts.handlers = {} end
+      opts.handlers[1] = function(server) utils_lsp.setup(server) end
+    end,
+    config = function(_, opts)
+      require("mason-lspconfig").setup(opts)
+      utils_lsp.apply_default_lsp_settings() -- Apply our default lsp settings.
+      utils.trigger_event("FileType")        -- This line starts this plugin.
+    end,
+  },
+
+  --  mason [lsp package manager]
+  --  https://github.com/williamboman/mason.nvim
+  --  https://github.com/zeioth/mason-extra-cmds
+  {
+    "williamboman/mason.nvim",
+    dependencies = { "zeioth/mason-extra-cmds", opts = {} },
+    cmd = {
+      "Mason",
+      "MasonInstall",
+      "MasonUninstall",
+      "MasonUninstallAll",
+      "MasonLog",
+      "MasonUpdate",
+      "MasonUpdateAll", -- this cmd is provided by mason-extra-cmds
+    },
+    opts = {
+      registries = {
+        "github:nvim-java/mason-registry",
+        "github:mason-org/mason-registry",
+      },
+      ui = {
+        icons = {
+          package_installed = "✓",
+          package_uninstalled = "✗",
+          package_pending = "⟳",
+        },
+      },
+    }
+  },
+
+  --  Schema Store [mason extra schemas]
+  --  https://github.com/b0o/SchemaStore.nvim
+  "b0o/SchemaStore.nvim",
+
+  -- none-ls-autoload.nvim [mason package loader]
+  -- https://github.com/zeioth/mason-none-ls.nvim
+  -- This plugin auto starts the packages installed by Mason
+  -- every time Neovim trigger the event FileType ().
+  -- By default it will use none-ls builtin sources.
+  -- But you can add external sources if a mason package has no builtin support.
+  {
+    "zeioth/none-ls-autoload.nvim",
+    event = "User BaseFile",
+    dependencies = {
+      "williamboman/mason.nvim",
+      "zeioth/none-ls-external-sources.nvim"
+    },
+    opts = {
+      -- Here you can add support for sources not oficially suppored by none-ls.
+      external_sources = {
+        -- diagnostics
+        'none-ls-external-sources.diagnostics.cpplint',
+        'none-ls-external-sources.diagnostics.eslint',
+        'none-ls-external-sources.diagnostics.eslint_d',
+        'none-ls-external-sources.diagnostics.flake8',
+        'none-ls-external-sources.diagnostics.luacheck',
+        'none-ls-external-sources.diagnostics.psalm',
+        'none-ls-external-sources.diagnostics.shellcheck',
+        'none-ls-external-sources.diagnostics.yamllint',
+
+        -- formatting
+        'none-ls-external-sources.formatting.autopep8',
+        'none-ls-external-sources.formatting.beautysh',
+        'none-ls-external-sources.formatting.easy-coding-standard',
+        'none-ls-external-sources.formatting.eslint',
+        'none-ls-external-sources.formatting.eslint_d',
+        'none-ls-external-sources.formatting.jq',
+        'none-ls-external-sources.formatting.latexindent',
+        'none-ls-external-sources.formatting.reformat_gherkin',
+        'none-ls-external-sources.formatting.rustfmt',
+        'none-ls-external-sources.formatting.standardrb',
+        'none-ls-external-sources.formatting.yq',
+      },
+    },
+  },
+
+  -- none-ls [lsp code formatting]
+  -- https://github.com/nvimtools/none-ls.nvim
+  {
+    "nvimtools/none-ls.nvim",
+    event = "User BaseFile",
+    opts = function()
+      local builtin_sources = require("null-ls").builtins
+
+      -- You can customize your 'builtin sources' and 'external sources' here.
+      builtin_sources.formatting.shfmt.with({
+        command = "shfmt",
+        args = { "-i", "2", "-filename", "$FILENAME" },
+      })
+
+      -- Attach the user lsp mappings to every none-ls client.
+      return { on_attach = utils_lsp.apply_user_lsp_mappings }
+    end
+  },
+
+  --  neodev.nvim [lsp for nvim lua api]
+  --  https://github.com/folke/neodev.nvim
+  {
+    "folke/neodev.nvim",
+    ft = { "lua" },
+    opts = {}
+  },
+
+  --  garbage-day.nvim [lsp garbage collector]
+  --  https://github.com/zeioth/garbage-day.nvim
+  {
+    "zeioth/garbage-day.nvim",
+    event = "User BaseFile",
+    opts = {
+      aggressive_mode = false,
+      excluded_lsp_clients = {
+        "null-ls", "jdtls", "marksman"
+      },
+      grace_period = (60 * 15),
+      wakeup_delay = 3000,
+      notifications = false,
+      retries = 3,
+      timeout = 1000,
+    }
+  },
+
+  --  AUTO COMPLETION --------------------------------------------------------
+  --  Auto completion engine [autocompletion engine]
+  --  https://github.com/hrsh7th/nvim-cmp
+  {
+    "hrsh7th/nvim-cmp",
+    dependencies = {
+      "saadparwaiz1/cmp_luasnip",
+      "hrsh7th/cmp-buffer",
+      "hrsh7th/cmp-path",
+      "hrsh7th/cmp-nvim-lsp"
+    },
+    event = "InsertEnter",
+    opts = function()
+      -- ensure dependencies exist
+      local cmp = require("cmp")
+      local luasnip = require("luasnip")
+      local lspkind = require("lspkind")
+
+      -- border opts
+      local border_opts = {
+        border = "rounded",
+        winhighlight = "Normal:NormalFloat,FloatBorder:FloatBorder,CursorLine:PmenuSel,Search:None",
+      }
+
+      -- helper
+      local function has_words_before()
+        local line, col = unpack(vim.api.nvim_win_get_cursor(0))
+        return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match "%s" == nil
+      end
+
+      return {
+        enabled = function() -- disable in certain cases on dap.
+          local is_prompt = vim.bo.buftype == "prompt"
+          local is_dap_prompt = utils.is_available("cmp-dap")
+              and vim.tbl_contains(
+                { "dap-repl", "dapui_watches", "dapui_hover" }, vim.bo.filetype)
+          if is_prompt and not is_dap_prompt then
+            return false
+          else
+            return vim.g.cmp_enabled
+          end
+        end,
+        preselect = cmp.PreselectMode.None,
+        formatting = {
+          fields = { "kind", "abbr", "menu" },
+          format = lspkind.cmp_format(utils.get_plugin_opts("lspkind.nvim")),
+        },
+        snippet = {
+          expand = function(args) luasnip.lsp_expand(args.body) end,
+        },
+        duplicates = {
+          nvim_lsp = 1,
+          luasnip = 1,
+          cmp_tabnine = 1,
+          buffer = 1,
+          path = 1,
+        },
+        confirm_opts = {
+          behavior = cmp.ConfirmBehavior.Replace,
+          select = false,
+        },
+        window = {
+          completion = cmp.config.window.bordered(border_opts),
+          documentation = cmp.config.window.bordered(border_opts),
+        },
+        mapping = {
+          ["<PageUp>"] = cmp.mapping.select_prev_item {
+            behavior = cmp.SelectBehavior.Select,
+            count = 8,
+          },
+          ["<PageDown>"] = cmp.mapping.select_next_item {
+            behavior = cmp.SelectBehavior.Select,
+            count = 8,
+          },
+          ["<C-PageUp>"] = cmp.mapping.select_prev_item {
+            behavior = cmp.SelectBehavior.Select,
+            count = 16,
+          },
+          ["<C-PageDown>"] = cmp.mapping.select_next_item {
+            behavior = cmp.SelectBehavior.Select,
+            count = 16,
+          },
+          ["<S-PageUp>"] = cmp.mapping.select_prev_item {
+            behavior = cmp.SelectBehavior.Select,
+            count = 16,
+          },
+          ["<S-PageDown>"] = cmp.mapping.select_next_item {
+            behavior = cmp.SelectBehavior.Select,
+            count = 16,
+          },
+          ["<Up>"] = cmp.mapping.select_prev_item {
+            behavior = cmp.SelectBehavior.Select,
+          },
+          ["<Down>"] = cmp.mapping.select_next_item {
+            behavior = cmp.SelectBehavior.Select,
+          },
+          ["<C-p>"] = cmp.mapping.select_prev_item {
+            behavior = cmp.SelectBehavior.Insert,
+          },
+          ["<C-n>"] = cmp.mapping.select_next_item {
+            behavior = cmp.SelectBehavior.Insert,
+          },
+          ["<C-k>"] = cmp.mapping.select_prev_item {
+            behavior = cmp.SelectBehavior.Insert,
+          },
+          ["<C-j>"] = cmp.mapping.select_next_item {
+            behavior = cmp.SelectBehavior.Insert,
+          },
+          ["<C-u>"] = cmp.mapping(cmp.mapping.scroll_docs(-4), { "i", "c" }),
+          ["<C-d>"] = cmp.mapping(cmp.mapping.scroll_docs(4), { "i", "c" }),
+          ["<C-Space>"] = cmp.mapping(cmp.mapping.complete(), { "i", "c" }),
+          ["<C-y>"] = cmp.config.disable,
+          ["<C-e>"] = cmp.mapping {
+            i = cmp.mapping.abort(),
+            c = cmp.mapping.close(),
+          },
+          ["<CR>"] = cmp.mapping.confirm { select = false },
+          ["<Tab>"] = cmp.mapping(function(fallback)
+            if cmp.visible() then
+              cmp.select_next_item()
+            elseif luasnip.expand_or_jumpable() then
+              luasnip.expand_or_jump()
+            elseif has_words_before() then
+              cmp.complete()
+            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 = cmp.config.sources {
+          { name = "nvim_lsp", priority = 1000 },
+          { name = "luasnip",  priority = 750 },
+          { name = "buffer",   priority = 500 },
+          { name = "path",     priority = 250 },
+        },
+      }
+    end,
+  },
+
+}

dot_config/nvim/lua/plugins/4-dev.lua 🔗

@@ -0,0 +1,901 @@
+-- Dev
+-- Things you actively use for coding.
+
+--    Sections:
+--       ## SNIPPETS
+--       -> luasnip                        [snippet engine]
+--       -> friendly-snippets              [snippet templates]
+
+--       ## GIT
+--       -> gitsigns.nvim                  [git hunks]
+--       -> fugitive.vim                   [git commands]
+
+--       ## ANALYZER
+--       -> aerial.nvim                    [symbols tree]
+--       -> litee-calltree.nvim            [calltree]
+
+--       ## CODE DOCUMENTATION
+--       -> dooku.nvim                     [html doc generator]
+--       -> markdown-preview.nvim          [markdown previewer]
+--       -> markmap.nvim                   [markdown mindmap]
+
+--       ## ARTIFICIAL INTELLIGENCE
+--       -> neural                         [chatgpt code generator]
+--       -> copilot                        [github code suggestions]
+--       -> guess-indent                   [guess-indent]
+
+--       ## COMPILER
+--       -> compiler.nvim                  [compiler]
+--       -> overseer.nvim                  [task runner]
+
+--       ## DEBUGGER
+--       -> nvim-dap                       [debugger]
+
+--       ## TESTING
+--       -> neotest.nvim                   [unit testing]
+--       -> nvim-coverage                  [code coverage]
+
+--       ## LANGUAGE IMPROVEMENTS
+--       -> guttentags_plus                [auto generate C/C++ tags]
+
+local is_windows = vim.fn.has('win32') == 1 -- true if on windows
+
+return {
+  --  SNIPPETS ----------------------------------------------------------------
+  --  Vim Snippets engine  [snippet engine] + [snippet templates]
+  --  https://github.com/L3MON4D3/LuaSnip
+  --  https://github.com/rafamadriz/friendly-snippets
+  {
+    "L3MON4D3/LuaSnip",
+    build = not is_windows and "make install_jsregexp" or nil,
+    dependencies = {
+      "rafamadriz/friendly-snippets",
+      "zeioth/NormalSnippets",
+      "benfowler/telescope-luasnip.nvim",
+    },
+    event = "User BaseFile",
+    opts = {
+      history = true,
+      delete_check_events = "TextChanged",
+      region_check_events = "CursorMoved",
+    },
+    config = function(_, opts)
+      if opts then require("luasnip").config.setup(opts) end
+      vim.tbl_map(
+        function(type) require("luasnip.loaders.from_" .. type).lazy_load() end,
+        { "vscode", "snipmate", "lua" }
+      )
+      -- friendly-snippets - enable standardized comments snippets
+      require("luasnip").filetype_extend("typescript", { "tsdoc" })
+      require("luasnip").filetype_extend("javascript", { "jsdoc" })
+      require("luasnip").filetype_extend("lua", { "luadoc" })
+      require("luasnip").filetype_extend("python", { "pydoc" })
+      require("luasnip").filetype_extend("rust", { "rustdoc" })
+      require("luasnip").filetype_extend("cs", { "csharpdoc" })
+      require("luasnip").filetype_extend("java", { "javadoc" })
+      require("luasnip").filetype_extend("c", { "cdoc" })
+      require("luasnip").filetype_extend("cpp", { "cppdoc" })
+      require("luasnip").filetype_extend("php", { "phpdoc" })
+      require("luasnip").filetype_extend("kotlin", { "kdoc" })
+      require("luasnip").filetype_extend("ruby", { "rdoc" })
+      require("luasnip").filetype_extend("sh", { "shelldoc" })
+    end,
+  },
+
+  --  GIT ---------------------------------------------------------------------
+  --  Git signs [git hunks]
+  --  https://github.com/lewis6991/gitsigns.nvim
+  {
+    "lewis6991/gitsigns.nvim",
+    enabled = vim.fn.executable "git" == 1,
+    event = "User BaseGitFile",
+    opts = function()
+      local get_icon = require("base.utils").get_icon
+      return {
+        max_file_length = vim.g.big_file.lines,
+        signs = {
+          add = { text = get_icon("GitSign") },
+          change = { text = get_icon("GitSign") },
+          delete = { text = get_icon("GitSign") },
+          topdelete = { text = get_icon("GitSign") },
+          changedelete = { text = get_icon("GitSign") },
+          untracked = { text = get_icon("GitSign") },
+        },
+      }
+    end
+  },
+
+  --  Git fugitive mergetool + [git commands]
+  --  https://github.com/lewis6991/gitsigns.nvim
+  --  PR needed: Setup keymappings to move quickly when using this feature.
+  --
+  --  We only want this plugin to use it as mergetool like "git mergetool".
+  --  To enable this feature, add this  to your global .gitconfig:
+  --
+  --  [mergetool "fugitive"]
+  --  	cmd = nvim -c \"Gvdiffsplit!\" \"$MERGED\"
+  --  [merge]
+  --  	tool = fugitive
+  --  [mergetool]
+  --  	keepBackup = false
+  {
+    "tpope/vim-fugitive",
+    enabled = vim.fn.executable "git" == 1,
+    dependencies = { "tpope/vim-rhubarb" },
+    cmd = {
+      "Gvdiffsplit",
+      "Gdiffsplit",
+      "Gedit",
+      "Gsplit",
+      "Gread",
+      "Gwrite",
+      "Ggrep",
+      "GMove",
+      "GRename",
+      "GDelete",
+      "GRemove",
+      "GBrowse",
+      "Git",
+      "Gstatus",
+    },
+    config = function()
+      -- NOTE: On vimplugins we use config instead of opts.
+      vim.g.fugitive_no_maps = 1
+    end,
+  },
+
+  --  ANALYZER ----------------------------------------------------------------
+  --  [symbols tree]
+  --  https://github.com/stevearc/aerial.nvim
+  {
+    "stevearc/aerial.nvim",
+    event = "User BaseFile",
+    opts = {
+      filter_kind = { -- Symbols that will appear on the tree
+        -- "Class",
+        "Constructor",
+        "Enum",
+        "Function",
+        "Interface",
+        -- "Module",
+        "Method",
+        -- "Struct",
+      },
+      open_automatic = false, -- Open if the buffer is compatible
+      autojump = true,
+      link_folds_to_tree = false,
+      link_tree_to_folds = false,
+      attach_mode = "global",
+      backends = { "lsp", "treesitter", "markdown", "man" },
+      disable_max_lines = vim.g.big_file.lines,
+      disable_max_size = vim.g.big_file.size,
+      layout = {
+        min_width = 28,
+        default_direction = "right",
+        placement = "edge",
+      },
+      show_guides = true,
+      guides = {
+        mid_item = "├ ",
+        last_item = "└ ",
+        nested_top = "│ ",
+        whitespace = "  ",
+      },
+      keymaps = {
+        ["[y"] = "actions.prev",
+        ["]y"] = "actions.next",
+        ["[Y"] = "actions.prev_up",
+        ["]Y"] = "actions.next_up",
+        ["{"] = false,
+        ["}"] = false,
+        ["[["] = false,
+        ["]]"] = false,
+      },
+    },
+    config = function(_, opts)
+      require("aerial").setup(opts)
+      -- HACK: The first time you open aerial on a session, close all folds.
+      vim.api.nvim_create_autocmd({"FileType", "BufEnter"}, {
+        desc = "Aerial: When aerial is opened, close all its folds.",
+        callback = function()
+          local is_aerial = vim.bo.filetype == "aerial"
+          local is_ufo_available = require("base.utils").is_available("nvim-ufo")
+          if is_ufo_available and is_aerial and vim.b.new_aerial_session == nil then
+            vim.b.new_aerial_session = false
+            require("aerial").tree_set_collapse_level(0, 0)
+          end
+        end,
+      })
+    end
+  },
+
+  -- Litee calltree [calltree]
+  -- https://github.com/ldelossa/litee.nvim
+  -- https://github.com/ldelossa/litee-calltree.nvim
+  -- press ? inside the panel to show help.
+  {
+    'ldelossa/litee.nvim',
+    event = "User BaseFile",
+    opts = {
+      notify = { enabled = false },
+      panel = {
+          orientation = "bottom",
+          panel_size = 10,
+      },
+    },
+    config = function(_, opts)
+      require('litee.lib').setup(opts)
+    end
+  },
+  {
+    'ldelossa/litee-calltree.nvim',
+    dependencies = 'ldelossa/litee.nvim',
+    event = "User BaseFile",
+    opts = {
+      on_open = "panel", -- or popout
+      map_resize_keys = false,
+      keymaps = {
+        expand = "<CR>",
+        collapse = "c",
+        collapse_all = "C",
+        jump = "<C-CR>"
+      },
+    },
+    config = function(_, opts)
+      require('litee.calltree').setup(opts)
+
+      -- Highlight only while on calltree
+      vim.api.nvim_create_autocmd({ "WinEnter" }, {
+        desc = "Clear highlights when leaving calltree + UX improvements.",
+        callback = function()
+          vim.defer_fn(function()
+            if vim.bo.filetype == "calltree" then
+              vim.wo.colorcolumn = "0"
+              vim.wo.foldcolumn = "0"
+              vim.cmd("silent! PinBuffer") -- stickybuf.nvim
+              vim.cmd("silent! hi LTSymbolJump ctermfg=015 ctermbg=110 cterm=italic,bold,underline guifg=#464646 guibg=#87afd7 gui=italic,bold")
+              vim.cmd("silent! hi LTSymbolJumpRefs ctermfg=015 ctermbg=110 cterm=italic,bold,underline guifg=#464646 guibg=#87afd7 gui=italic,bold")
+            else
+              vim.cmd("silent! highlight clear LTSymbolJump")
+              vim.cmd("silent! highlight clear LTSymbolJumpRefs")
+            end
+          end, 100)
+        end
+      })
+    end
+  },
+
+  --  CODE DOCUMENTATION ------------------------------------------------------
+  --  dooku.nvim [html doc generator]
+  --  https://github.com/zeioth/dooku.nvim
+  {
+    "zeioth/dooku.nvim",
+    cmd = {
+      "DookuGenerate",
+      "DookuOpen",
+      "DookuAutoSetup"
+    },
+    opts = {},
+  },
+
+  --  [markdown previewer]
+  --  https://github.com/iamcco/markdown-preview.nvim
+  --  Note: If you change the build command, wipe ~/.local/data/nvim/lazy
+  {
+    "iamcco/markdown-preview.nvim",
+    build = function() vim.fn["mkdp#util#install"]() end,
+    ft = { "markdown" },
+    cmd = {
+      "MarkdownPreview",
+      "MarkdownPreviewStop",
+      "MarkdownPreviewToggle",
+    },
+  },
+
+  --  [markdown markmap]
+  --  https://github.com/zeioth/markmap.nvim
+  --  Important: Make sure you have yarn in your PATH before running markmap.
+  {
+    "zeioth/markmap.nvim",
+    build = "yarn global add markmap-cli",
+    cmd = { "MarkmapOpen", "MarkmapSave", "MarkmapWatch", "MarkmapWatchStop" },
+    config = function(_, opts) require("markmap").setup(opts) end,
+  },
+
+  --  ARTIFICIAL INTELLIGENCE  -------------------------------------------------
+  --  neural [chatgpt code generator]
+  --  https://github.com/dense-analysis/neural
+  --
+  --  NOTE: In order for this plugin to work, you will have to set
+  --        the next env var in your OS:
+  --        OPENAI_API_KEY="my_key_here"
+  {
+    "dense-analysis/neural",
+    cmd = { "Neural" },
+    config = function()
+      require("neural").setup {
+        source = {
+          openai = {
+            api_key = vim.env.OPENAI_API_KEY,
+          },
+        },
+        ui = {
+          prompt_icon = ">",
+        },
+      }
+    end,
+  },
+
+  --  copilot [github code suggestions]
+  --  https://github.com/github/copilot.vim
+  --  As alternative to chatgpt, you can use copilot uncommenting this.
+  --  Then you must run :Copilot setup
+  -- {
+  --   "github/copilot.vim",
+  --   event = "User BaseFile"
+  -- },
+  -- copilot-cmp
+  -- https://github.com/zbirenbaum/copilot-cmp
+  -- {
+  --   "zbirenbaum/copilot-cmp",
+  --   opts = { suggesion = { enabled = false }, panel = { enabled = false } },
+  --   config = function (_, opts) require("copilot_cmp").setup(opts) end
+  -- },
+
+  -- [guess-indent]
+  -- https://github.com/NMAC427/guess-indent.nvim
+  -- Note that this plugin won't autoformat the code.
+  -- It just set the buffer options to tabluate in a certain way.
+  {
+    "NMAC427/guess-indent.nvim",
+    event = "User BaseFile",
+    opts = {}
+  },
+
+  --  COMPILER ----------------------------------------------------------------
+  --  compiler.nvim [compiler]
+  --  https://github.com/zeioth/compiler.nvim
+  {
+    "zeioth/compiler.nvim",
+    cmd = {
+      "CompilerOpen",
+      "CompilerToggleResults",
+      "CompilerRedo",
+      "CompilerStop"
+    },
+    dependencies = { "stevearc/overseer.nvim" },
+    opts = {},
+  },
+
+  --  overseer [task runner]
+  --  https://github.com/stevearc/overseer.nvim
+  --  If you need to close a task immediately:
+  --  press ENTER in the output menu on the task you wanna close.
+  {
+    "stevearc/overseer.nvim",
+    cmd = {
+      "OverseerOpen",
+      "OverseerClose",
+      "OverseerToggle",
+      "OverseerSaveBundle",
+      "OverseerLoadBundle",
+      "OverseerDeleteBundle",
+      "OverseerRunCmd",
+      "OverseerRun",
+      "OverseerInfo",
+      "OverseerBuild",
+      "OverseerQuickAction",
+      "OverseerTaskAction",
+      "OverseerClearCache"
+    },
+    opts = {
+     task_list = { -- the window that shows the results.
+        direction = "bottom",
+        min_height = 25,
+        max_height = 25,
+        default_detail = 1,
+      },
+      -- component_aliases = {
+      --   default = {
+      --     -- Behaviors that will apply to all tasks.
+      --     "on_exit_set_status",                   -- don't delete this one.
+      --     "on_output_summarize",                  -- show last line on the list.
+      --     "display_duration",                     -- display duration.
+      --     "on_complete_notify",                   -- notify on task start.
+      --     "open_output",                          -- focus last executed task.
+      --     { "on_complete_dispose", timeout=300 }, -- dispose old tasks.
+      --   },
+      -- },
+    },
+  },
+
+  --  DEBUGGER ----------------------------------------------------------------
+  --  Debugger alternative to vim-inspector [debugger]
+  --  https://github.com/mfussenegger/nvim-dap
+  --  Here we configure the adapter+config of every debugger.
+  --  Debuggers don't have system dependencies, you just install them with mason.
+  --  We currently ship most of them with nvim.
+  {
+    "mfussenegger/nvim-dap",
+    enabled = vim.fn.has "win32" == 0,
+    event = "User BaseFile",
+    config = function()
+      local dap = require("dap")
+
+      -- C#
+      dap.adapters.coreclr = {
+        type = 'executable',
+        command = vim.fn.stdpath('data') .. '/mason/bin/netcoredbg',
+        args = { '--interpreter=vscode' }
+      }
+      dap.configurations.cs = {
+        {
+          type = "coreclr",
+          name = "launch - netcoredbg",
+          request = "launch",
+          program = function() -- Ask the user what executable wants to debug
+            return vim.fn.input('Path to dll: ', vim.fn.getcwd() .. '/bin/Program.exe', 'file')
+          end,
+        },
+      }
+
+      -- F#
+      dap.configurations.fsharp = dap.configurations.cs
+
+      -- Visual basic dotnet
+      dap.configurations.vb = dap.configurations.cs
+
+      -- Java
+      -- Note: The java debugger jdtls is automatically spawned and configured
+      -- by the plugin 'nvim-java' in './3-dev-core.lua'.
+
+      -- Python
+      dap.adapters.python = {
+        type = 'executable',
+        command = vim.fn.stdpath('data') .. '/mason/packages/debugpy/venv/bin/python',
+        args = { '-m', 'debugpy.adapter' },
+      }
+      dap.configurations.python = {
+        {
+          type = "python",
+          request = "launch",
+          name = "Launch file",
+          program = "${file}", -- This configuration will launch the current file if used.
+        },
+      }
+
+      -- Lua
+      dap.adapters.nlua = function(callback, config)
+        callback({ type = 'server', host = config.host or "127.0.0.1", port = config.port or 8086 })
+      end
+      dap.configurations.lua = {
+        {
+          type = 'nlua',
+          request = 'attach',
+          name = "Attach to running Neovim instance",
+          program = function() pcall(require "osv".launch({ port = 8086 })) end,
+        }
+      }
+
+      -- C
+      dap.adapters.codelldb = {
+        type = 'server',
+        port = "${port}",
+        executable = {
+          command = vim.fn.stdpath('data') .. '/mason/bin/codelldb',
+          args = { "--port", "${port}" },
+          detached = function() if is_windows then return false else return true end end,
+        }
+      }
+      dap.configurations.c = {
+        {
+          name = 'Launch',
+          type = 'codelldb',
+          request = 'launch',
+          program = function() -- Ask the user what executable wants to debug
+            return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/bin/program', 'file')
+          end,
+          cwd = '${workspaceFolder}',
+          stopOnEntry = false,
+          args = {},
+        },
+      }
+
+      -- C++
+      dap.configurations.cpp = dap.configurations.c
+
+      -- Rust
+      dap.configurations.rust = {
+        {
+          name = 'Launch',
+          type = 'codelldb',
+          request = 'launch',
+          program = function() -- Ask the user what executable wants to debug
+            return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/bin/program', 'file')
+          end,
+          cwd = '${workspaceFolder}',
+          stopOnEntry = false,
+          args = {},
+          initCommands = function() -- add rust types support (optional)
+            -- Find out where to look for the pretty printer Python module
+            local rustc_sysroot = vim.fn.trim(vim.fn.system('rustc --print sysroot'))
+
+            local script_import = 'command script import "' .. rustc_sysroot .. '/lib/rustlib/etc/lldb_lookup.py"'
+            local commands_file = rustc_sysroot .. '/lib/rustlib/etc/lldb_commands'
+
+            local commands = {}
+            local file = io.open(commands_file, 'r')
+            if file then
+              for line in file:lines() do
+                table.insert(commands, line)
+              end
+              file:close()
+            end
+            table.insert(commands, 1, script_import)
+
+            return commands
+          end,
+        }
+      }
+
+      -- Go
+      -- Requires:
+      -- * You have initialized your module with 'go mod init module_name'.
+      -- * You :cd your project before running DAP.
+      dap.adapters.delve = {
+        type = 'server',
+        port = '${port}',
+        executable = {
+          command = vim.fn.stdpath('data') .. '/mason/packages/delve/dlv',
+          args = { 'dap', '-l', '127.0.0.1:${port}' },
+        }
+      }
+      dap.configurations.go = {
+        {
+          type = "delve",
+          name = "Compile module and debug this file",
+          request = "launch",
+          program = "./${relativeFileDirname}",
+        },
+        {
+          type = "delve",
+          name = "Compile module and debug this file (test)",
+          request = "launch",
+          mode = "test",
+          program = "./${relativeFileDirname}"
+        },
+      }
+
+      -- Dart / Flutter
+      dap.adapters.dart = {
+        type = 'executable',
+        command = vim.fn.stdpath('data') .. '/mason/bin/dart-debug-adapter',
+        args = { 'dart' }
+      }
+      dap.adapters.flutter = {
+        type = 'executable',
+        command = vim.fn.stdpath('data') .. '/mason/bin/dart-debug-adapter',
+        args = { 'flutter' }
+      }
+      dap.configurations.dart = {
+        {
+          type = "dart",
+          request = "launch",
+          name = "Launch dart",
+          dartSdkPath = "/opt/flutter/bin/cache/dart-sdk/", -- ensure this is correct
+          flutterSdkPath = "/opt/flutter",                  -- ensure this is correct
+          program = "${workspaceFolder}/lib/main.dart",     -- ensure this is correct
+          cwd = "${workspaceFolder}",
+        },
+        {
+          type = "flutter",
+          request = "launch",
+          name = "Launch flutter",
+          dartSdkPath = "/opt/flutter/bin/cache/dart-sdk/", -- ensure this is correct
+          flutterSdkPath = "/opt/flutter",                  -- ensure this is correct
+          program = "${workspaceFolder}/lib/main.dart",     -- ensure this is correct
+          cwd = "${workspaceFolder}",
+        }
+      }
+
+      -- Kotlin
+      -- Kotlin projects have very weak project structure conventions.
+      -- You must manually specify what the project root and main class are.
+      dap.adapters.kotlin = {
+        type = 'executable',
+        command = vim.fn.stdpath('data') .. '/mason/bin/kotlin-debug-adapter',
+      }
+      dap.configurations.kotlin = {
+        {
+          type = 'kotlin',
+          request = 'launch',
+          name = 'Launch kotlin program',
+          projectRoot = "${workspaceFolder}/app",     -- ensure this is correct
+          mainClass = "AppKt",                        -- ensure this is correct
+        },
+      }
+
+      -- Javascript / Typescript (firefox)
+      dap.adapters.firefox = {
+        type = 'executable',
+        command = vim.fn.stdpath('data') .. '/mason/bin/firefox-debug-adapter',
+      }
+      dap.configurations.typescript = {
+        {
+          name = 'Debug with Firefox',
+          type = 'firefox',
+          request = 'launch',
+          reAttach = true,
+          url = 'http://localhost:4200', -- Write the actual URL of your project.
+          webRoot = '${workspaceFolder}',
+          firefoxExecutable = '/usr/bin/firefox'
+        }
+      }
+      dap.configurations.javascript = dap.configurations.typescript
+      dap.configurations.javascriptreact = dap.configurations.typescript
+      dap.configurations.typescriptreact = dap.configurations.typescript
+
+      -- Javascript / Typescript (chromium)
+      -- If you prefer to use this adapter, comment the firefox one.
+      -- But to use this adapter, you must manually run one of these two, first:
+      -- * chromium --remote-debugging-port=9222 --user-data-dir=remote-profile
+      -- * google-chrome-stable --remote-debugging-port=9222 --user-data-dir=remote-profile
+      -- After starting the debugger, you must manually reload page to get all features.
+      -- dap.adapters.chrome = {
+      --  type = 'executable',
+      --  command = vim.fn.stdpath('data')..'/mason/bin/chrome-debug-adapter',
+      -- }
+      -- dap.configurations.typescript = {
+      --  {
+      --   name = 'Debug with Chromium',
+      --   type = "chrome",
+      --   request = "attach",
+      --   program = "${file}",
+      --   cwd = vim.fn.getcwd(),
+      --   sourceMaps = true,
+      --   protocol = "inspector",
+      --   port = 9222,
+      --   webRoot = "${workspaceFolder}"
+      --  }
+      -- }
+      -- dap.configurations.javascript = dap.configurations.typescript
+      -- dap.configurations.javascriptreact = dap.configurations.typescript
+      -- dap.configurations.typescriptreact = dap.configurations.typescript
+
+      -- PHP
+      dap.adapters.php = {
+        type = 'executable',
+        command = vim.fn.stdpath("data") .. '/mason/bin/php-debug-adapter',
+      }
+      dap.configurations.php = {
+        {
+          type = 'php',
+          request = 'launch',
+          name = 'Listen for Xdebug',
+          port = 9000
+        }
+      }
+
+      -- Shell
+      dap.adapters.bashdb = {
+        type = 'executable',
+        command = vim.fn.stdpath("data") .. '/mason/packages/bash-debug-adapter/bash-debug-adapter',
+        name = 'bashdb',
+      }
+      dap.configurations.sh = {
+        {
+          type = 'bashdb',
+          request = 'launch',
+          name = "Launch file",
+          showDebugOutput = true,
+          pathBashdb = vim.fn.stdpath("data") .. '/mason/packages/bash-debug-adapter/extension/bashdb_dir/bashdb',
+          pathBashdbLib = vim.fn.stdpath("data") .. '/mason/packages/bash-debug-adapter/extension/bashdb_dir',
+          trace = true,
+          file = "${file}",
+          program = "${file}",
+          cwd = '${workspaceFolder}',
+          pathCat = "cat",
+          pathBash = "/bin/bash",
+          pathMkfifo = "mkfifo",
+          pathPkill = "pkill",
+          args = {},
+          env = {},
+          terminalKind = "integrated",
+        }
+      }
+
+      -- Elixir
+      dap.adapters.mix_task = {
+        type = 'executable',
+        command = vim.fn.stdpath("data") .. '/mason/bin/elixir-ls-debugger',
+        args = {}
+      }
+      dap.configurations.elixir = {
+        {
+          type = "mix_task",
+          name = "mix test",
+          task = 'test',
+          taskArgs = { "--trace" },
+          request = "launch",
+          startApps = true, -- for Phoenix projects
+          projectDir = "${workspaceFolder}",
+          requireFiles = {
+            "test/**/test_helper.exs",
+            "test/**/*_test.exs"
+          }
+        },
+      }
+    end, -- of dap config
+    dependencies = {
+      "rcarriga/nvim-dap-ui",
+      "rcarriga/cmp-dap",
+      "jay-babu/mason-nvim-dap.nvim",
+      "jbyuki/one-small-step-for-vimkind",
+      "nvim-java/nvim-java",
+    },
+  },
+
+  -- nvim-dap-ui [dap ui]
+  -- https://github.com/mfussenegger/nvim-dap-ui
+  -- user interface for the debugger dap
+  {
+    "rcarriga/nvim-dap-ui",
+    dependencies = { "nvim-neotest/nvim-nio" },
+    opts = { floating = { border = "rounded" } },
+    config = function(_, opts)
+      local dap, dapui = require("dap"), require("dapui")
+      dap.listeners.after.event_initialized["dapui_config"] = function(
+      )
+        dapui.open()
+      end
+      dap.listeners.before.event_terminated["dapui_config"] = function(
+      )
+        dapui.close()
+      end
+      dap.listeners.before.event_exited["dapui_config"] = function()
+        dapui.close()
+      end
+      dapui.setup(opts)
+    end,
+  },
+
+  -- cmp-dap [dap autocomplete]
+  -- https://github.com/mfussenegger/cmp-dap
+  -- Enables autocomplete for the debugger dap.
+  {
+    "rcarriga/cmp-dap",
+    dependencies = { "nvim-cmp" },
+    config = function()
+      require("cmp").setup.filetype(
+        { "dap-repl", "dapui_watches", "dapui_hover" },
+        {
+          sources = {
+            { name = "dap" },
+          },
+        }
+      )
+    end,
+  },
+
+  --  TESTING -----------------------------------------------------------------
+  --  Run tests inside of nvim [unit testing]
+  --  https://github.com/nvim-neotest/neotest
+  --
+  --
+  --  MANUAL:
+  --  -- Unit testing:
+  --  To tun an unit test you can run any of these commands:
+  --
+  --    :Neotest run      -- Runs the nearest test to the cursor.
+  --    :Neotest stop     -- Stop the nearest test to the cursor.
+  --    :Neotest run file -- Run all tests in the file.
+  --
+  --  -- E2e and Test Suite
+  --  Normally you will prefer to open your e2e framework GUI outside of nvim.
+  --  But you have the next commands in ../base/3-autocmds.lua:
+  --
+  --    :TestNodejs    -- Run all tests for this nodejs project.
+  --    :TestNodejsE2e -- Run the e2e tests/suite for this nodejs project.
+  {
+    "nvim-neotest/neotest",
+    cmd = { "Neotest" },
+    dependencies = {
+      "sidlatau/neotest-dart",
+      "Issafalcon/neotest-dotnet",
+      "jfpedroza/neotest-elixir",
+      "nvim-neotest/neotest-go",
+      "rcasia/neotest-java",
+      "nvim-neotest/neotest-jest",
+      "olimorris/neotest-phpunit",
+      "nvim-neotest/neotest-python",
+      "rouge8/neotest-rust",
+      "lawrence-laz/neotest-zig",
+    },
+    opts = function()
+      return {
+        -- your neotest config here
+        adapters = {
+          require("neotest-dart"),
+          require("neotest-dotnet"),
+          require("neotest-elixir"),
+          require("neotest-go"),
+          require("neotest-java"),
+          require("neotest-jest"),
+          require("neotest-phpunit"),
+          require("neotest-python"),
+          require("neotest-rust"),
+          require("neotest-zig"),
+        },
+      }
+    end,
+    config = function(_, opts)
+      -- get neotest namespace (api call creates or returns namespace)
+      local neotest_ns = vim.api.nvim_create_namespace "neotest"
+      vim.diagnostic.config({
+        virtual_text = {
+          format = function(diagnostic)
+            local message = diagnostic.message:gsub("\n", " "):gsub("\t", " "):gsub("%s+", " "):gsub("^%s+", "")
+            return message
+          end,
+        },
+      }, neotest_ns)
+      require("neotest").setup(opts)
+    end,
+  },
+
+  --  Shows a float panel with the [code coverage]
+  --  https://github.com/andythigpen/nvim-coverage
+  --
+  --  Your project must generate coverage/lcov.info for this to work.
+  --
+  --  On jest, make sure your packages.json file has this:
+  --  "tests": "jest --coverage"
+  --
+  --  If you use other framework or language, refer to nvim-coverage docs:
+  --  https://github.com/andythigpen/nvim-coverage/blob/main/doc/nvim-coverage.txt
+  {
+    "zeioth/nvim-coverage", -- Our fork until all our PRs are merged.
+    cmd = {
+      "Coverage",
+      "CoverageLoad",
+      "CoverageLoadLcov",
+      "CoverageShow",
+      "CoverageHide",
+      "CoverageToggle",
+      "CoverageClear",
+      "CoverageSummary",
+    },
+    dependencies = { "nvim-lua/plenary.nvim" },
+    opts = {
+      summary = {
+        min_coverage = 80.0, -- passes if higher than
+      },
+    },
+    config = function(_, opts) require("coverage").setup(opts) end,
+  },
+
+  -- LANGUAGE IMPROVEMENTS ----------------------------------------------------
+  -- guttentags_plus [auto generate C/C++ tags]
+  -- https://github.com/skywind3000/gutentags_plus
+  -- This plugin is necessary for using <C-]> (go to ctag).
+  {
+    "skywind3000/gutentags_plus",
+    ft = { "c", "cpp" },
+    dependencies = { "ludovicchabant/vim-gutentags" },
+    config = function()
+      -- NOTE: On vimplugins we use config instead of opts.
+      vim.g.gutentags_plus_nomap = 1
+      vim.g.gutentags_resolve_symlinks = 1
+      vim.g.gutentags_cache_dir = vim.fn.stdpath "cache" .. "/tags"
+      vim.api.nvim_create_autocmd("FileType", {
+        desc = "Auto generate C/C++ tags",
+        callback = function()
+          local is_c = vim.bo.filetype == "c" or vim.bo.filetype == "cpp"
+          if is_c then vim.g.gutentags_enabled = 1
+          else vim.g.gutentags_enabled = 0 end
+        end,
+      })
+    end,
+  },
+
+} -- end of return

dot_config/polybar/config.ini.tmpl 🔗

@@ -1,11 +1,11 @@
 [colors]
 background = ${xrdb:background}
-background-alt = ${xrdb:color7}
+background-alt = ${xrdb:color8}
 foreground = ${xrdb:foreground}
 foreground-alt = ${xrdb:color0}
 primary = ${xrdb:color1}
 secondary = ${xrdb:color11}
-alert = ${xrdb:color3}
+alert = ${xrdb:color11}
 
 [bar/top]
 width = 100%
@@ -104,8 +104,8 @@ label-visible-padding = ${self.label-focused-padding}
 ; urgent = Workspace with urgency hint set
 label-urgent = %index%
 label-urgent-background = ${colors.alert}
-label-urgent-foreground = ${colors.foreground-alt}
-label-urgent-underline = ${colors.foreground}
+label-urgent-foreground = ${colors.background}
+label-urgent-underline = ${colors.alert}
 label-urgent-padding = 1
 
 [module/mail]

dot_config/private_zathura/zathurarc.tmpl 🔗

@@ -6,43 +6,43 @@ set selection-clipboard "clipboard"
 
 
 {{- if eq .theme_variant "dark" }}
-set default-fg               "#CAD3F5"
-set default-bg               "#24273A"
-
-set completion-bg            "#363A4F"
-set completion-fg            "#CAD3F5"
-set completion-highlight-bg  "#575268"
-set completion-highlight-fg  "#CAD3F5"
-set completion-group-bg      "#363A4F"
-set completion-group-fg      "#8AADF4"
-
-set statusbar-fg             "#CAD3F5"
-set statusbar-bg             "#363A4F"
-
-set notification-bg          "#363A4F"
-set notification-fg          "#CAD3F5"
-set notification-error-bg    "#363A4F"
-set notification-error-fg    "#ED8796"
-set notification-warning-bg  "#363A4F"
-set notification-warning-fg  "#FAE3B0"
-
-set inputbar-fg              "#CAD3F5"
-set inputbar-bg              "#363A4F"
-
-set recolor-lightcolor       "#24273A"
-set recolor-darkcolor        "#CAD3F5"
-
-set index-fg                 "#CAD3F5"
-set index-bg                 "#24273A"
-set index-active-fg          "#CAD3F5"
-set index-active-bg          "#363A4F"
-
-set render-loading-bg        "#24273A"
-set render-loading-fg        "#CAD3F5"
-
-set highlight-color          "#575268"
-set highlight-fg             "#F5BDE6"
-set highlight-active-color   "#F5BDE6"
+set default-fg               "#d3c6aa"
+set default-bg               "#272e33"
+
+set completion-bg            "#2e383c"
+set completion-fg            "#d3c6aa"
+set completion-highlight-bg  "#dbbc7f"
+set completion-highlight-fg  "#d3c6aa"
+set completion-group-bg      "#2e383c"
+set completion-group-fg      "#7fbbb3"
+
+set statusbar-fg             "#d3c6aa"
+set statusbar-bg             "#2e383c"
+
+set notification-bg          "#2e383c"
+set notification-fg          "#d3c6aa"
+set notification-error-bg    "#2e383c"
+set notification-error-fg    "#e67e80"
+set notification-warning-bg  "#2e383c"
+set notification-warning-fg  "#dbbc7f"
+
+set inputbar-fg              "#d3c6aa"
+set inputbar-bg              "#2e383c"
+
+set recolor-lightcolor       "#272e33"
+set recolor-darkcolor        "#d3c6aa"
+
+set index-fg                 "#d3c6aa"
+set index-bg                 "#272e33"
+set index-active-fg          "#d3c6aa"
+set index-active-bg          "#2e383c"
+
+set render-loading-bg        "#272e33"
+set render-loading-fg        "#d3c6aa"
+
+set highlight-color          "#dbbc7f"
+set highlight-fg             "#d699b6"
+set highlight-active-color   "#d699b6"
 {{- end }}
 {{- if eq .theme_variant "light" }}
 set default-fg               "#4C4F69"
@@ -50,7 +50,7 @@ set default-bg               "#EFF1F5"
 
 set completion-bg            "#CCD0DA"
 set completion-fg            "#4C4F69"
-set completion-highlight-bg  "#575268"
+set completion-highlight-bg  "#dbbc7f"
 set completion-highlight-fg  "#4C4F69"
 set completion-group-bg      "#CCD0DA"
 set completion-group-fg      "#1E66F5"
@@ -79,7 +79,7 @@ set index-active-bg          "#CCD0DA"
 set render-loading-bg        "#EFF1F5"
 set render-loading-fg        "#4C4F69"
 
-set highlight-color          "#575268"
+set highlight-color          "#dbbc7f"
 set highlight-fg             "#EA76CB"
 set highlight-active-color   "#EA76CB"
 {{- end }}

dot_config/zsh/private_dot_zshrc.tmpl 🔗

@@ -1,13 +1,13 @@
 # Theming
 {{- if eq .theme_variant "dark" }}
 # https://github.com/catppuccin/zsh-syntax-highlighting
-source ~/.config/zsh/themes/catppuccin-macchiato.zsh
-export BAT_THEME="Catppuccin-macchiato"
+source ~/.config/zsh/themes/everforest-hard-dark.zsh
+export BAT_THEME="ansi"
 {{- end }}
 {{- if eq .theme_variant "light" }}
 # https://github.com/catppuccin/zsh-syntax-highlighting
-source ~/.config/zsh/themes/catppuccin-latte.zsh
-export BAT_THEME="Catppuccin-latte"
+source ~/.config/zsh/themes/everforest-soft-light.zsh
+export BAT_THEME="ansi"
 {{- end }}
 
 # Remove older command from the history if a duplicate is to be added.
@@ -33,11 +33,10 @@ zstyle ':zim:termtitle:precmd'  format '%1~'
 # Customize the style that the suggestions are shown with.
 # Set what highlighters will be used.
 # See https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/docs/highlighters.md
-ZSH_HIGHLIGHT_HIGHLIGHTERS=(main brackets)
+ZSH_HIGHLIGHT_HIGHLIGHTERS=(main cursor)
 # Customize the main highlighter styles.
 # See https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/docs/highlighters/main.md#how-to-tweak-it
-#typeset -A ZSH_HIGHLIGHT_STYLES
-#ZSH_HIGHLIGHT_STYLES[comment]='fg=242'
+typeset -gA ZSH_HIGHLIGHT_STYLES
 
 # XDG variables
 export XDG_DATA_HOME="$HOME/.local/share"
@@ -122,11 +121,13 @@ export PATH="$PATH:$CACHE/yay/distrobox/pkg/distrobox/usr/bin"
 export PATH="$PATH:$XDG_DATA_HOME/cargo/bin"
 export PATH="$PATH:$HOME/.radicle/bin"
 export PATH="$HOME/.local/bin:$PATH"
+export PATH="$PATH:/usr/lib/kf6"
+export PATH="$PATH:/usr/lib/kf5"
 
 # Additional preferences
 export LANG=en_GB.UTF-8
-export EDITOR='emacsclient -c'
-export VISUAL='emacsclient -c'
+export EDITOR='nvim'
+export VISUAL='nvim'
 export _JAVA_AWT_WM_NONREPARENTING=1
 export MANPAGER="nvim +Man!"
 export COLUMNS=80
@@ -163,6 +164,9 @@ source /usr/share/z/z.sh
 source /usr/share/fzf/key-bindings.zsh
 source /usr/share/fzf/completion.zsh
 
+# Completions for git extras
+source /usr/share/doc/git-extras/git-extras-completion.zsh
+
 # Ranger conf
 export RANGER_LOAD_DEFAULT_RC=false
 
@@ -177,7 +181,6 @@ alias u="linx-client"
 alias clip="xclip -selection clipboard"
 alias tmp="cd $(mktemp -d) && export TEMP=$(pwd)"
 alias send="rsync -amzzP"
-alias tree="eza --tree"
 alias bat="bat -n --tabs 2"
 alias mov="joshuto ~/Bulk/Media/Movies"
 alias tv="joshuto ~/Bulk/Media/TV\ Shows"
@@ -194,6 +197,7 @@ alias ac="aicommits"
 alias tc="turbocommit"
 alias ai="sgpt"
 alias k="klog"
+alias ls="lsd"
 
 # Blog-related aliases
 alias secluded="cd ~/repos/personal/secluded && ./gen"

dot_local/bin/executable_startup.tmpl 🔗

@@ -13,6 +13,7 @@ if [[ -n $WAYLAND_DISPLAY ]]; then
 	hyprctl -i 0 dispatch exec -- "xfsettingsd"
 	hyprctl -i 0 dispatch exec -- "dbus-update-activation-environment --all"
 	hyprctl -i 0 dispatch exec -- "telegram-desktop"
+	hyprctl -i 0 dispatch exec -- "kdeconnect-indicator"
 	{{- if eq .chezmoi.hostname "angmar" }}
 	hyprctl -i 0 dispatch exec -- "caddy start --adapter caddyfile --config ~/Bulk/Media/Caddyfile"
 	hyprctl -i 0 dispatch exec -- "firefox-developer-edition"
@@ -37,6 +38,7 @@ else
 	i3-msg exec "xrdb -load ~/.Xresources"
 	i3-msg exec "xfsettingsd"
 	i3-msg exec "dbus-update-activation-environment --all"
+	i3-msg exec "kdeconnect-indicator"
 	{{- if eq .chezmoi.hostname "angmar" }}
 	i3-msg exec "caddy start --adapter caddyfile --config ~/Bulk/Media/Caddyfile"
 	i3-msg workspace 7

dot_local/share/dark-mode.d/executable_swap.sh 🔗

@@ -3,6 +3,9 @@
 sed -i 's/light/dark/g' ~/.config/chezmoi/chezmoi.toml
 chezmoi apply
 xfconf-query -c xsettings -p /Net/ThemeName -s "Everforest-Dark"
+xfconf-query -c xsettings -p /Net/IconThemeName -s "Everforest-Dark"
+gsettings set org.gnome.desktop.interface gtk-theme "Everforest-Dark"
+gsettings set org.gnome.desktop.interface icon-theme "Everforest-Dark"
 gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark'
 xrdb -load ~/.Xresources
 "$XDG_CONFIG_HOME/emacs/bin/doom" sync
@@ -23,6 +26,6 @@ else
 fi
 
 unlink ~/.wallpaper.png
-ln -s ~/Pictures/walls/everforest-dark.png ~/.wallpaper.png
+ln -s ~/Pictures/walls/everforest-hard-dark.png ~/.wallpaper.png
 feh --bg-scale ~/.wallpaper.png
 notify-send "Theme switch complete"

dot_local/share/light-mode.d/executable_swap.sh 🔗

@@ -3,6 +3,9 @@
 sed -i 's/dark/light/g' ~/.config/chezmoi/chezmoi.toml
 chezmoi apply
 xfconf-query -c xsettings -p /Net/ThemeName -s "Everforest-Light-Soft"
+xfconf-query -c xsettings -p /Net/IconThemeName -s "Everforest-Light"
+gsettings set org.gnome.desktop.interface gtk-theme "Everforest-Light-Soft"
+gsettings set org.gnome.desktop.interface icon-theme "Everforest-Light"
 gsettings set org.gnome.desktop.interface color-scheme 'prefer-light'
 xrdb -load ~/.Xresources
 "$XDG_CONFIG_HOME/emacs/bin/doom" sync
@@ -23,6 +26,6 @@ else
 fi
 
 unlink ~/.wallpaper.png
-ln -s ~/Pictures/walls/everforest-light.png ~/.wallpaper.png
+ln -s ~/Pictures/walls/everforest-soft-light.png ~/.wallpaper.png
 feh --bg-scale ~/.wallpaper.png
 notify-send "Theme switch complete"

private_dot_ssh/encrypted_config.tmpl.age 🔗

@@ -1,43 +1,42 @@
 -----BEGIN AGE ENCRYPTED FILE-----
-YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IEhBV2NqUSBBM0FHalZP
-VTZOR1Q0THYrZmZwaER2ZkpCK0RNY0thNmtwVXdzb1ppSWFpMgpJRkdYVVVCSEN0
-Y1dsNkRDWVAvUlc1R3M1dmJodFRrNk9SV3B1azhGTFZRCi0+IHBpdi1wMjU2IHBE
-YXpSZyBBcFZLdFJpM2pmaGQrVWZ1MkdNZWlqeVQ2RU90Yk9IRW4vK2V5RnBrWTJB
-RQp3QUR0aFBvWERzdHJlTEIvTHVEb2wzOERlS0Izdy9UcU81SnVmVlVsQXZJCi0+
-IEtUdVkyLWdyZWFzZSBFJ2kgPSJ+ZmYxIEFyfEY1CjZaN29VelNjN0Mzem1lRVAv
-NTVBVFJLTFd5V1hqVGZtQmovbUJSUXFrWXpUdXZ6WFZGa2JrN1kya1ZrTnhhSTcK
-djdwR3NtUmdiamw2SE1nZEFrWG5XU3dtUGFPWnVBCi0tLSBGNUI4U094TmxMTFlW
-OXVac3FmcGFCQ0JnTTc0V3lvcE9mcFFxZkI1SWI0CvCj9INWMTXsV7Qjkwx3qDi5
-TYMUxhSt4tLIxnqrlCiMu/u7yYkXh+Xb/GRng3Nlptp357mRkXn/sM7Rtm2qeki+
-zX9MUiFOUzX+FsqKQ3rOfU9AIBQjkC8Y0ZbTjn17c+/itoh4gV+boCQsdVcTzLLM
-nQeByWkLnxjq0B97brgCCXUuPP7Lzu88kOzCp+I/KmVgSNj1o2LSWFM6B7Ptofgb
-o/L25xjXSM0XYqnZeAeNZAWfkvpJ/arXUtzMEVSqGMWSvBZQ51On3zrKDkQThUc1
-2bIVs+FVfYJQCyhuUweMbWqyyU+Ru4HbeH4WA9pTqhHsUJi1ZU5wAlyX7ezUyLZU
-y39CEU1wLamNzVeZwbhQv9DbrmHMnOl9tHpzX518hA50FGt9294TSzB2CcPSAjYA
-SKfqZgOxdF8DdJuqBj0O5rJ9tEXPHkUI+TkyOcnpln8yVyUHM/wpOMpgsS8bfDPL
-8o/J7wg56FfHsy1F72QyhMQsyquPWCHtmCXncHVWwlXE5pxeCazdcn4TVSPXs+Fq
-yLYepyYhodQNNBJvfUvRN7BUkf2OTAZQGI8VMo9nibdpnfRxZrcp7wJcxJ06va/C
-JYy2gkKbKa98pUr7ToCqaxDiPe6r8MIuYJ+MaW3imvEF/4b+DO3BrYBdPEaA/uH4
-EB8X20Eq5e3h37iDZAh6l9I++sJlyTRD+rjd9z3wc8bP7mhsbGsI+OzVK82J8ha2
-yBLo9XLpt3m691RDxEeUtugDpunEPhf6Woak8ErwUZcJ0rMQ9khLstzCFoehKeqV
-Ffh8yXz8D/ZPWxY1OdxzVQ8Bd3JxYZlmjRTAbQL/yLZg0PBqFr0Ny2aBxfoGYLMB
-NpzwX4uUCPbyK2g3tNHafL1YDIitEEiobYVnnhVIbJQqMhS/gsVW2daMNvJ/3KZu
-1K3azW4t305/ryddRUBZ/ezJx/mf1Tr9xxnb9aj3MsvyhjbExFjUkx8/vEtnDZuA
-9IPlsD0+ydoEA+IGJqepox3Sabl6x9e3ya2maa+1TF2K3h4mVQNp09yOrBhl1Eyr
-V3eMWZe58nbFOdn8Aq99Ru0RkMesvvFAsAsKcwwuemLFNzdrl89Juuhr0dzXtMZu
-GIX09i5bL60rZXl6RQx6rWoT28ir9yaYHZYqoEXlm4IZOEo0Bc3yKWATw0chpdvX
-qlmz28gudJOBfvgfybC4SO7rD2g5vD+YRrhRQ7YOqQAuq6BcnSfUYilLC2PoBSCx
-HipZ3cZvQSoqZ1D3R7mSZI1Rh9O6GZlU6UgXxrK0RBAqPL62APN8nlMyisXlt7KM
-zwwDozt35qXSVf0ZgiDUTRwlfsUn6W+aJOsZasnLdIJDN0Y6nz4092brzMoz1ZIy
-xAM9Gt772DDmOsgb2pDZ8YAQzO+3/U1/v/ikF/g9DZxLN+xQbT9zGJ1yrScvKS04
-7kny+e2cB6ye7iUafCsM0HzKg0b+NFyHqADeWO1MxJQYO8VjK3wbedbJ6pJ+9MRj
-rjRt1ZwD2dCYphynYhpcy2CtImR4ngOMqoRtQEFTXcWafig1itkOnqYmLsy5dEH9
-YuOFeIyme9vwVvrh4WcOeSVnZOVExvEiKAkRageYCmL51BR+w92nuXBwzvhP6RM5
-KILstftZh8auNuEmDw9hK+FbOOSjJNSkkI8DKw5rTyvlVpGXxf7NYHMeYRkDFQfr
-C3be+qzGocb9GOcFGiMp4W48xkUMwrn2YTaWACFawOEdtlyfUPIjFhEQCdu4CZ0p
-lERNyo91DpcFLA6YqYUusJdrDd1988wzFURBMkas6tPCTZuuzj1lcvqhW/g2kKOT
-PrAx4ftx7qbdWzoZ+VMY9U/R9knnO+xnQ8kmp+zAcc08N6ycNcrGGsJF/LC49cpA
-m/282hpCQbUCgRxch+2uNH7IK0uos/b4Ee6ubrxNYXCSZKqtIYr4Vq8PjNumdQq7
-bSTVWNXGT54FmKhmSglMje8ANDGQM+aAiNbbip+GOiQAg6h7elFhY0uuUbb8k0ZV
-XRlXO2pKlUaTY7XCdh28ZimCnfsinjRepNkgnR8YR2ETsPTXVkJ/SU8+KPRe
+YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHBpdi1wMjU2IEhBV2NqUSBBOSs0ZzhD
+d3RyKzZYekU5ckRNUDVzVVNtc2FpeEx5a0FsNEJGaXBvakVzUApndWMwSEFkSTYz
+SUdRL3JMZnFwbGR6dFczT2tVd050VitUWkpZWlh6cVlBCi0+IHBpdi1wMjU2IHBE
+YXpSZyBBa0RkZi9uZ1ZxUmdrZEpKWEhLZGQ4SkdGVTBteDY4SzRRRUY2aStIZllP
+Vgp4cDZjenZReG1ubHNRdVdPWWhxM1NoUlBwQW5EZmhmeFdqdTdUWFBNKzNvCi0+
+IDd6aTBCPUEtZ3JlYXNlIHIqKW5vSicgS1pITGBSTSBZPyA/Ci9JMEw5YXhzQVRE
+ci9HZUpQUG0xSUdTQkJCQmd2M2ppeWFpTVVTc3JnSkVYd1h1NWhVaTcvcXRTQWlo
+eUJXN1MKZU9aL2IwUmNGR2I2NmRyd1hSNjhKdWJxY0EKLS0tIDJSUUx3MTRhL1Bo
+bVJaUUs0UTNaYkd6RE5GVTR4MUcwcS9GVERZVnllUXMKjy5S2mub3DsSJyQZJ3nG
+uqjR0xoW7xn/gd6tuwNunc6cCtpspp4QU//lNnARl3urpzTlLPbisadhNBKG7+i4
+Y65e6J7Zh/UyBf09VNCVnA4cV2jCB/jPARwXZyxAjyFzCeP6ze7Qsca9dzx6EazA
+I1ocbPPDdNOHzcwq0nGec4N5H9DBTOK9sAveNsXw1e3KMy2BO/WO27iEIFthDkPR
+ve4zQZogbhNfDH3oP98roYVBOufn7u+iV+Kye60MyAjorF3BG+BqViLm9KPJQjO+
+a4yCcxNitdBsN/SJ+e7pPNtfXbdzNgmjRyHgjOQMa4D/dCwbNSawFbQtWOTXh5KG
+kABrOEBsiSYH4pGSkpRCUUXmDwbtVpRRs75CoGb2b3mpmYmC3lDRweQ9iv7s0S18
+hi5piJO2PFx8xDUGqhJ1/WqCATs60Lsv99hgn6WolmJISeSqmUa1Y7GAgmVwCs3K
+GsiYZ1olzXgDAaAv3N//no9kGOnoJ3BnJl3fezuzljmKYLFtOfGNxwmOamrOeZfo
+wMKKZrDM2daoy2HkhbFCeXzqDNbZ0HQk8IFo+tvFbQqx3rijD0/YKiTgCEgIscyh
+a9z7DQ8AhpyaD3QX4FShi3mfnB89Apa87n7GdTWRhSqtH1GGd4l2n/kNm02E9B5q
+o0Q43y3xo8lTm3xbN4DX4J9kFQC0H8zzcvqkmZT7OJMb2YypnKMnm1Dl/0OImQVG
+jIhx7Dc2EpDlf2MBuQ+AUYseUAp18MdT1HlpOMzjrd15+k4vyBkXrWiFUudNf3+Q
+YGN3aHGA/TYtfbjKAGFhaMR87RlC4jKa9aL2FN8iiys0MAkl+WoWh/5P7hnLkZln
+WTAaayUxTqm7GhDjxR8uK+kqkbJMvyOvXhEL79PnkkO58lZDM/CiNSMF+Z5YPT1m
+R6K+p0mZnszn2zf+dgxVnmlYl09nEGW0PX8sdDQ9BG83jdQl27/q0pb6T8lPdbBB
+UbNarEaj2QGLrE/v0ZAPXf4lnMoOieKDvN8kCr7+gj7Agtn0pIZJydNQuokP4UMj
+fFTqzZgpec3w2Tmne63wcOI8ZhpP2itfQlU8Bee/qrQrZd4mJgs89qF2zRdWiw9h
+n60mOZIi+1j5QG2HdZzU9fzOgi7EulRj4VRf1Z3hNUehqUQYtRSEgGLyoSExO9TB
+X+2LBJOqs0tTUTT8bdzPXJ+XlmmAB951g9nKPFWa4VaZQfGk5iYrlYT20Ontl09a
+yiTbYTWLdl+AB6yJBM5P6guYpjzg3wvtcvGkuW+UsDI70GsyauwTo+OHN5UeTloZ
+ZYVdyOHnT7WcrzanrXylb+wLP9Y5wDj+0JL2qdZpAzNUA/PNBVJzmY8qDltq/f7D
+iUHUYEzSNJArwGrYQUd8Mubx8Rt1PUmWLJBcv6NV9hyClq6WiuXUmpaOoMjNpTiA
+2pfzb2svjrdC9anFah1k7KDhrPkzhB5ld/o4b5mnqWGtRSVIETSZPr0wXsD2N05N
+fe5BeE3+5YOd6xFj3Nt7Xa1ZHfgR1Gt4Nqhlx9MpNpmK7EqLIDPeyfd5QdK1kHD2
+RXGe44aRs3qI7JqrDjbHq3lJCfhjm4PR1uDZa7tGfQ23R/MUHhIumpAEhJWJ3BKB
+se7hb+2EGTaHNII3BQogpVPg8X/qzZ1sHMiy0mLcg/Mv64+1FELczmjSAUKnDPj0
+cRuJEkr1/CBMiQc28/PIj6PVeNB2J8d+wWa5Yoy0mgo2lAm+MznKdTP4OSKQtixt
+sedAX/RR9tHgZSYvNDIbt6XqKkV3ROFxuaaRpvyXKOG45Rcg2D1DjijOLrBHmIdu
+FUCsh1VY10D8D0h8sEib0/wr2BoEpNv7eEse4QVI78GABrDUG3awBHa3XewY92mg
+x15Q731cNt88PmOxLIl56hfXr+VJuMAffOYvyQ8ZqsdmmOzpJmojc1uFuavUBiV/
+WUSwjlRyPb27vpr5eATwY6ZjJwpJ5tfbid8KFWubuQ==
 -----END AGE ENCRYPTED FILE-----