feat manage script

This commit is contained in:
Tomas Mirchev 2025-02-24 09:51:41 +00:00
commit 1a3a374f3b
45 changed files with 2149 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
**/.DS_Store
.dotfiles_env

117
README.md Normal file
View File

@ -0,0 +1,117 @@
# dotfiles
```shell
git clone https://gitea.tomastm.com/tomas.mirchev/dotfiles.git ~/.dotfiles
cd ~/.dotfiles
./install.py
```
Do not forget to change the remote from https to ssh
```shell
git remote set-url origin git@gitea.tomastm.com:tomas.mirchev/dotfiles.git
git remote -v
```
### Todo
- [ ] Uninstall option
- [ ] Fix inconsistencies with usage fn
---
# Notes for README
- How to update dotfiles? By default, they are symlinked.
-> Directly modify `config/shared/<config>` or `config/envs/<env>/<config>`
-> If new config, add it to `config.json` too.
-> And simply run `./manage.py link <env>`. If needed, do not forget to update the `config.json#install` or `homebrew dump`.
- How to unlink?
-> Just remove the symlink and unspecify it from `config.json` to keep it updated for the future.
# Files and steps
Scripts:
- macos-save_homebrew.sh
- macos-install_homebrew.sh
- macos-change_hostname --hostname
- linux-setup_docker.sh
- linux-change_hostname --hostname
- setup_shh.sh -C $USER@$HOSTNAME -f internal|git|none
Setups:
- macos (--hostname)
- linux-vm (--hostname)
- linux-dev
# For MacOS
```
$ git clone https://gitea.tomastm.com/tomas.mirchev/dotfiles.git .dotfiles
$ cd .dotfiles
$ ./manage.py setup macos --hostname macbook-pro
-> Install homebrew
-> Install all apps from dump (also stored in ./config/envs/macos)
-> run: ./manage.py link macos (git, zsh, nvim-vanilla, tmux, karabiner, linearmouse, ghostty)
-> from shared: git, zsh, tmux, nvim-simple
-> from env macos: karabiner, linearmouse, ghostty
-> execute: ./scripts/macos-change_hostname --hostname $HOSTNAME
-> execute: ./scripts/setup_ssh.sh -C $USER@$HOSTNAME -f internal (-f: id_ed25519_internal)
-> execute: ./scripts/setup_ssh.sh -C $USER@$HOSTNAME -f git (-f: id_ed25519_git)
-> echo: "internal key generated, once vms and dns are ready: ssh-copy-id tomas@ip"
-> echo: "You ssh pub key is: <key>". do not forget to change from https to git
-> echo: "Do not forget to change git remote from https to git after adding SSH Key"
-> echo: "Next steps: setup UTM and DNS" (maybe store DNS settings in private repo)
-> todo: Add WindowTagger automatically too. In the meantime, echo it and reference to the repo.
```
# For LinuxVM
## Create VM
### MacOS host with UTM
- Download the ISO for architecture ARM64.
- Create the VM with UTM.
- It is recommended to setup local DNS with `dnsmasq`.
### Other hosts
- Download the ISO for architecture AMD64.
- Setup Proxmox and create the VM.
## Setup VM
```
$ git clone https://gitea.tomastm.com/tomas.mirchev/dotfiles.git .dotfiles
$ cd .dotfiles
$ ./manage.py setup linux-vm --hostname <personal|university|client>-<utm|workstation>
-> execute: ./scripts/linux-setup_sudoers.sh
-> execute: mkdir /home/$USER/projects
-> execute: ./scripts/linux-setup_docker.sh
-> execute: ./scripts/linux-change_hostname.sh --hostname $HOSTNAME
-> execute: ./scripts/setup_ssh.sh -C $USER@HOSTNAME -f "" (-f: id_ed25519)
-> run: ./manage.py install linux-vm
-> run: ./manage.py link linux-vm
-> from shared: git, zsh, tmux, nvim-simple
-> echo: "You ssh pub key is: ". do not forget to change from https to git
-> echo: "use `dev` for bla bla"
```
# For LinuxDev
Build with multi-stage.
## base-debian
### Stage 1 - Setup NeoVim
```
RUN apt install python3
RUN git clone https://gitea.tomastm.com/tomas.mirchev/dotfiles.git .dotfiles \
&& cd .dotfiles \
&& ./manage.py setup linux-dev
-> wget tree-sitter && move (check architecture)
-> wget nvim.deb && install (check architecture)
-> nvim !lazy restore plugins
```
### Stage 2
- copy-from: .dotfiles & `./setup.py --env-linux-c --copy`
- copy-from: tree-sitter
- copy-from: `nvim.deb` & `apt install ./nvim.deb`
- copy-from: .local/share/nvim
- Setup locales (check debian setup)
- Create dev user
- Create workspace directory
## node
## python

75
config.json Normal file
View File

@ -0,0 +1,75 @@
{
"template": {
"htop": {
"link": { "from": "shared/htop", "to": "~/.config/htop" }
},
"bin": {
"link": { "from": "shared/bin", "to": "~/bin" }
},
"vim": {
"link": { "from": "shared/vim", "to": "~/.vimrc" },
"post-link": "curl -fLo ~/.vim/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim && vim -es -u ~/.vimrc -i NONE -c 'PlugInstall' -c 'qa'"
},
"nvim": {
"link": { "from": "shared/nvim", "to": "~/.config/nvim" },
"post-link": "nvim --headless '+Lazy! restore' +qa && echo 'alias vim=nvim' >> ~/.zshrc"
},
"zsh": {
"link": { "from": "shared/zsh", "to": "~/.zshrc" },
"install": "./scripts/linux-setup_zsh.sh"
},
"tmux": {
"link": { "from": "shared/tmux", "to": "~/.tmux.conf" }
},
"git": {
"link": { "from": "shared/git", "to": "~/.gitconfig" }
},
"wezterm": {
"link": { "from": "shared/wezterm", "to": "~/.wezterm.lua" }
},
"alacritty": {
"link": { "from": "shared/alacritty", "to": "~/.alacritty.toml" }
},
"ghostty": {
"link": { "from": "shared/ghostty", "to": "~/.config/ghostty" }
}
},
"environments": {
"macos": [
"zsh",
"tmux",
"nvim",
"git",
"ghostty",
{
"package": "karabiner",
"link": { "from": "macos/karabiner", "to": "~/.config/karabiner" }
},
{
"package": "linearmouse",
"link": { "from": "macos/linearmouse", "to": "~/.config/linearmouse" }
}
],
"linux-vm": [
"zsh",
"tmux",
"nvim",
"git",
"htop",
"bin"
],
"linux-dev": [
"zsh",
"tmux",
{
"package": "nvim",
"ignore-template": true,
"link": { "from": "linux-dev/nvim", "to": "~/.config/nvim" },
"post-link": "nvim --headless '+Lazy! restore' +qa && echo 'alias vim=nvim' >> ~/.zshrc"
},
"git",
"htop",
"bin"
]
}
}

View File

@ -0,0 +1,3 @@
require('config.options') -- vim options
require('config.keymaps') -- keymaps
require('config.lazy') -- plugin manager and plugins

View File

@ -0,0 +1,25 @@
{
"cmp-nvim-lsp": { "branch": "main", "commit": "39e2eda76828d88b773cc27a3f61d2ad782c922d" },
"cmp-path": { "branch": "main", "commit": "91ff86cd9c29299a64f968ebb45846c485725f23" },
"conform.nvim": { "branch": "master", "commit": "d28ccf945374edd9f1c34a82f6c22261dbd8ab98" },
"fidget.nvim": { "branch": "main", "commit": "e2a175c2abe2d4f65357da1c98c59a5cfb2b543f" },
"harpoon": { "branch": "harpoon2", "commit": "a84ab829eaf3678b586609888ef52f7779102263" },
"indent-blankline.nvim": { "branch": "master", "commit": "e51b651ca26cba250ef3a1150c8d35045eee2a84" },
"lazy.nvim": { "branch": "main", "commit": "7e6c863bc7563efbdd757a310d17ebc95166cef3" },
"mason-lspconfig.nvim": { "branch": "main", "commit": "4d0e5b49363cac187326998b96aa6a2884e0e89b" },
"mason.nvim": { "branch": "main", "commit": "e2f7f9044ec30067bc11800a9e266664b88cda22" },
"neo-tree.nvim": { "branch": "main", "commit": "a77af2e764c5ed4038d27d1c463fa49cd4794e07" },
"nui.nvim": { "branch": "main", "commit": "b58e2bfda5cea347c9d58b7f11cf3012c7b3953f" },
"nvim-autopairs": { "branch": "master", "commit": "ee297f215e95a60b01fde33275cc3c820eddeebe" },
"nvim-cmp": { "branch": "main", "commit": "f17d9b4394027ff4442b298398dfcaab97e40c4f" },
"nvim-lspconfig": { "branch": "master", "commit": "bc6ada4b0892b7f10852c0b8ca7209fd39a6d754" },
"nvim-treesitter": { "branch": "master", "commit": "7dc8aabe86db8c2f23520e8334f7584f83e84342" },
"nvim-ts-autotag": { "branch": "main", "commit": "1cca23c9da708047922d3895a71032bc0449c52d" },
"nvim-web-devicons": { "branch": "master", "commit": "e87554285f581047b1bf236794b0eb812b444b87" },
"plenary.nvim": { "branch": "master", "commit": "2d9b06177a975543726ce5c73fca176cedbffe9d" },
"rose-pine": { "branch": "main", "commit": "91548dca53b36dbb9d36c10f114385f759731be1" },
"schemastore.nvim": { "branch": "main", "commit": "f8d6e9068861888651f68958521b1958314aac41" },
"telescope-fzf-native.nvim": { "branch": "main", "commit": "cf48d4dfce44e0b9a2e19a008d6ec6ea6f01a83b" },
"telescope-ui-select.nvim": { "branch": "master", "commit": "6e51d7da30bd139a6950adf2a47fda6df9fa06d2" },
"telescope.nvim": { "branch": "0.1.x", "commit": "a0bbec21143c7bc5f8bb02e0005fa0b982edc026" }
}

View File

@ -0,0 +1,49 @@
local remap = require("utils.remap")
remap.nmap("<leader>q", vim.diagnostic.setloclist, { desc = "Open diagnostic [Q]uickfix list" })
remap.imap("jk", "<Esc>", { desc = "Exit insert mode with jk" })
remap.nmap("<Esc>", "<cmd>nohlsearch<CR>", { desc = "Clear highlights" })
-- Prevent "x" from overriding the register
remap.nmap("x", '"_x')
-- Window Navigation
remap.nmap("<C-h>", "<C-w>h", { desc = "Move focus to the left window" })
remap.nmap("<C-l>", "<C-w>l", { desc = "Move focus to the right window" })
remap.nmap("<C-j>", "<C-w>j", { desc = "Move focus to the lower window" })
remap.nmap("<C-k>", "<C-w>k", { desc = "Move focus to the upper window" })
-- Tab management
remap.nmap("<Leader>tn", ":tabnew<CR>", { desc = "[T]ab [N]ew" })
remap.nmap("<Leader>tc", ":tabclose<CR>", { desc = "[T]ab [C]lose" })
remap.nmap("<Leader>to", ":tabonly<CR>", { desc = "[T]ab [O]nly" })
remap.nmap("<Leader>tl", ":tabnext<CR>", { desc = "[T]ab Next" })
remap.nmap("<Leader>th", ":tabprevious<CR>", { desc = "[T]ab Previous" })
remap.nmap("<Leader>tm.", ":tabmove +1<CR>", { desc = "[T]ab [M]ove Right" })
remap.nmap("<Leader>tm,", ":tabmove -1<CR>", { desc = "[T]ab [M]ove Left" })
for i = 1, 9 do
remap.nmap(string.format("<Leader>%d", i), string.format("%dgt", i), { desc = string.format("[T]ab %d", i) })
end
-- Buffer Management
remap.nmap("<Leader>bl", ":ls<CR>", { desc = "[B]uffer [L]ist" })
remap.nmap("<Leader>bd", ":bdelete<CR>", { desc = "[B]uffer [D]elete" })
remap.nmap("]b", ":bnext<CR>", { desc = "[B]uffer [N]ext" })
remap.nmap("[b", ":bprevious<CR>", { desc = "[B]uffer [P]revious" })
remap.nmap("<Leader>bb", ":b<Space>", { desc = "[B]uffer Select" })
remap.nmap("<Leader>bo", ":bufdo bd|1bd<CR>", { desc = "[B]uffer Delete Others" })
-- Terminal
remap.nmap("<Leader>tet", function()
vim.cmd("terminal")
vim.cmd("startinsert")
end, { desc = "[T]erminal" })
remap.nmap("<leader>ter", function()
local buf_dir = vim.fn.expand("%:p:h")
vim.cmd("edit term://" .. buf_dir .. "//zsh")
vim.cmd("startinsert")
end, { desc = "[T]erminal [R]elative" })
remap.tmap("<Esc>", "<C-\\><C-n>", { desc = "Terminal Normal Mode" })
remap.tmap("jk", "<C-\\><C-n>", { desc = "Terminal Normal Mode" })
remap.tmap("<C-w>", "<C-\\><C-n><C-w>", { desc = "Terminal Window Command" })

View File

@ -0,0 +1,11 @@
local lazypath = vim.fn.stdpath 'data' .. '/lazy/lazy.nvim'
if not (vim.uv or vim.loop).fs_stat(lazypath) then
local lazyrepo = 'https://github.com/folke/lazy.nvim.git'
local out = vim.fn.system { 'git', 'clone', '--filter=blob:none', '--branch=stable', lazyrepo, lazypath }
if vim.v.shell_error ~= 0 then
error('Error cloning lazy.nvim:\n' .. out)
end
end ---@diagnostic disable-next-line: undefined-field
vim.opt.rtp:prepend(lazypath)
require('lazy').setup('plugins')

View File

@ -0,0 +1,86 @@
-- Map Leader
vim.g.mapleader = " "
vim.g.maplocalleader = " "
-- Use Nerd Font
vim.g.have_nerd_font = true
-- Add vertical line
-- vim.opt.colorcolumn = "100"
-- Enable TrueColor
vim.opt.termguicolors = true
-- Disable Neovim background
vim.api.nvim_set_hl(0, "Normal", { bg = "none" })
vim.api.nvim_set_hl(0, "NormalFloat", { bg = "none" })
-- Scroll lines/columns
vim.opt.mousescroll = "hor:1,ver:1"
-- Set indentation preferences
vim.opt.expandtab = true -- Convert tabs to spaces
vim.opt.shiftwidth = 2 -- Number of spaces for auto-indent
vim.opt.tabstop = 2 -- Number of spaces a tab counts for
vim.opt.softtabstop = 2 -- Number of spaces a tab counts for when editing
vim.opt.autoindent = true -- Copy indent from current line when starting new line
vim.opt.smartindent = true -- Do smart autoindenting when starting a new line
-- Disable line wrapping
vim.opt.wrap = false
-- Enable break indent
vim.opt.breakindent = true
-- Make line numbers default
vim.opt.number = true
vim.opt.relativenumber = true
-- Enable mouse mode, can be useful for resizing splits for example
vim.opt.mouse = "a"
-- Full path on status line
vim.opt.statusline = "%F%m%r%h%w%=%l,%c %P"
-- Sync clipboard between OS and Neovim
vim.schedule(function()
vim.opt.clipboard = "unnamedplus"
end)
-- Save undo history
vim.opt.undofile = true
-- Case-insensitive searching UNLESS \C or one or more capital letters in the search term
vim.opt.ignorecase = true
vim.opt.smartcase = true
-- Decrease update time
vim.opt.updatetime = 250
-- Decrease mapped sequence wait time
-- Displays which-key popup sooner
vim.opt.timeoutlen = 300
-- Configure how new splits should be opened
vim.opt.splitright = true
vim.opt.splitbelow = true
-- Sets how neovim will display certain whitespace characters in the editor.
vim.opt.list = true
vim.opt.listchars = { tab = "» ", trail = "·", nbsp = "" }
-- Preview substitutions live, as you type
vim.opt.inccommand = "split"
-- Show which line your cursor is on
vim.opt.cursorline = true
-- Minimal number of screen lines to keep above and below the cursor
vim.opt.scrolloff = 10
-- Highlight when yanking (copying) text
vim.api.nvim_create_autocmd("TextYankPost", {
callback = function()
vim.highlight.on_yank()
end,
})

View File

@ -0,0 +1,40 @@
return { -- Autoformat
"stevearc/conform.nvim",
event = { "BufWritePre" },
cmd = { "ConformInfo" },
keys = {
{
"<leader>f",
function()
require("conform").format({ async = true, lsp_format = "fallback" })
end,
mode = "",
desc = "[F]ormat buffer",
},
},
opts = {
notify_on_error = false,
format_on_save = function(bufnr)
local disable_filetypes = { c = true, cpp = true }
local lsp_format_opt
if disable_filetypes[vim.bo[bufnr].filetype] then
lsp_format_opt = "never"
else
lsp_format_opt = "fallback"
end
return {
timeout_ms = 500,
lsp_format = lsp_format_opt,
}
end,
formatters_by_ft = {
lua = { "stylua" },
swift = { "swift_format" },
python = { "isort", "black", stop_after_first = true },
javascript = { "prettierd", "prettier", stop_after_first = true },
javascriptreact = { "prettierd", "prettier", stop_after_first = true },
typescript = { "prettierd", "prettier", stop_after_first = true },
typescriptreact = { "prettierd", "prettier", stop_after_first = true },
},
},
}

View File

@ -0,0 +1,13 @@
return {
'windwp/nvim-autopairs',
event = 'InsertEnter',
-- Optional dependency
dependencies = { 'hrsh7th/nvim-cmp' },
config = function()
require('nvim-autopairs').setup {}
-- If you want to automatically add `(` after selecting a function or method
local cmp_autopairs = require 'nvim-autopairs.completion.cmp'
local cmp = require 'cmp'
cmp.event:on('confirm_done', cmp_autopairs.on_confirm_done())
end,
}

View File

@ -0,0 +1,11 @@
return {
"windwp/nvim-ts-autotag",
config = {
autotag = {
enable = true,
enable_close = true,
enable_rename = true,
enable_close_on_slash = true,
},
},
}

View File

@ -0,0 +1,11 @@
return {
"rose-pine/neovim",
name = "rose-pine",
config = function()
require("rose-pine").setup({
disable_background = true,
disable_float_background = true,
})
vim.cmd("colorscheme rose-pine")
end,
}

View File

@ -0,0 +1,60 @@
return {
"hrsh7th/nvim-cmp",
event = "InsertEnter",
dependencies = {
"hrsh7th/cmp-nvim-lsp",
"hrsh7th/cmp-path",
},
config = function()
local cmp = require("cmp")
cmp.setup({
window = {
completion = {
border = "single",
-- or border = true for default border
},
documentation = {
border = "single",
},
},
completion = { completeopt = "menu,menuone,noselect" }, -- This ensures nothing is auto-selected
sources = cmp.config.sources({
{ name = "nvim_lsp" }, -- from language server
{ name = "buffer" }, -- from current buffer
{ name = "path" }, -- for file paths
}),
mapping = cmp.mapping.preset.insert({
["<Tab>"] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_next_item({ behavior = cmp.SelectBehavior.Insert })
else
fallback()
end
end),
["<S-Tab>"] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_prev_item({ behavior = cmp.SelectBehavior.Insert })
else
fallback()
end
end),
["<C-e>"] = cmp.mapping.abort(), -- This closes the completion menu
["<C-u>"] = cmp.mapping(function(fallback)
if cmp.visible() and cmp.get_selected_entry() then
cmp.scroll_docs(-4)
else
fallback()
end
end),
["<C-d>"] = cmp.mapping(function(fallback)
if cmp.visible() and cmp.get_selected_entry() then
cmp.scroll_docs(4)
else
fallback()
end
end),
}),
})
end,
}

View File

@ -0,0 +1,42 @@
return {
"ThePrimeagen/harpoon",
branch = "harpoon2",
opts = {
menu = {
width = vim.api.nvim_win_get_width(0) - 4,
},
settings = {
save_on_toggle = true,
},
},
keys = function()
local keys = {
{
"<leader>H",
function()
require("harpoon"):list():add()
end,
desc = "Harpoon File",
},
{
"<leader>h",
function()
local harpoon = require("harpoon")
harpoon.ui:toggle_quick_menu(harpoon:list())
end,
desc = "Harpoon Quick Menu",
},
}
for i = 1, 5 do
table.insert(keys, {
"<C-" .. i .. ">",
function()
require("harpoon"):list():select(i)
end,
desc = "Harpoon to File " .. i,
})
end
return keys
end,
}

View File

@ -0,0 +1,9 @@
return {
{ -- Add indentation guides even on blank lines
'lukas-reineke/indent-blankline.nvim',
-- Enable `lukas-reineke/indent-blankline.nvim`
-- See `:help ibl`
main = 'ibl',
opts = {},
},
}

View File

@ -0,0 +1,98 @@
local remap = require("utils.remap")
vim.diagnostic.config({
update_in_insert = false,
virtual_text = {
source = true,
},
float = {
border = "rounded",
},
})
vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(vim.lsp.handlers.hover, {
border = "rounded",
})
vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(vim.lsp.handlers.signature_help, {
border = "rounded",
})
return {
"neovim/nvim-lspconfig",
dependencies = {
{ "williamboman/mason.nvim", config = true },
"williamboman/mason-lspconfig.nvim",
{ "j-hui/fidget.nvim", opts = {} }, -- side fidget showing status
"hrsh7th/cmp-nvim-lsp", -- completion
"b0o/schemastore.nvim",
},
config = function()
require("mason").setup()
require("mason-lspconfig").setup()
local lspconfig = require("lspconfig")
local capabilities = require("cmp_nvim_lsp").default_capabilities()
require("lspconfig.ui.windows").default_options = {
border = "rounded",
}
lspconfig.ts_ls.setup({
capabilities = capabilities,
init_options = {
preferences = {
includeCompletionsWithSnippetText = true,
jsxAttributeCompletionStyle = "auto",
},
},
on_attach = function(client, bufnr)
-- Mappings
local opts = { buffer = bufnr }
remap.nmap("gd", vim.lsp.buf.definition, opts)
remap.nmap("gr", vim.lsp.buf.references, opts)
remap.nmap("K", vim.lsp.buf.hover, opts)
remap.nmap("<leader>rn", vim.lsp.buf.rename, opts)
remap.nmap("<leader>ca", vim.lsp.buf.code_action, opts)
remap.nmap("<leader>ri", function()
local diagnostics = vim.diagnostic.get(0)
-- Filter for TypeScript's unused import diagnostics (code 6133)
local unused_imports_diagnostics = {}
for _, diagnostic in ipairs(diagnostics) do
if diagnostic.code == 6133 and diagnostic.source == "typescript" then
table.insert(unused_imports_diagnostics, diagnostic)
end
end
vim.lsp.buf.code_action({
context = {
diagnostics = unused_imports_diagnostics,
only = { "source.removeUnusedImports" },
},
})
end, { desc = "Remove unused imports" })
remap.nmap("[d", vim.diagnostic.goto_prev, opts)
remap.nmap("]d", vim.diagnostic.goto_next, opts)
remap.nmap("<leader>d", vim.diagnostic.open_float, opts)
end,
})
lspconfig.eslint.setup({})
lspconfig.html.setup({})
lspconfig.cssls.setup({})
lspconfig.jsonls.setup({
capabilities = capabilities,
settings = {
json = {
schemas = require("schemastore").json.schemas(),
validate = { enable = true },
allowComments = true,
},
},
})
end,
}

View File

@ -0,0 +1,33 @@
return {
"nvim-neo-tree/neo-tree.nvim",
version = "*",
dependencies = {
"nvim-lua/plenary.nvim",
"nvim-tree/nvim-web-devicons",
"MunifTanjim/nui.nvim",
},
cmd = "Neotree",
keys = {
{ "<Leader>et", ":Neotree position=left toggle<CR>", desc = "Explorer Toggle", silent = true },
{ "<Leader>E", ":Neotree focus<CR>", desc = "Explorer Focus", silent = true },
{ "<Leader>ef", ":Neotree float<CR>", desc = "Explorer Float", silent = true },
{ "<Leader>eb", ":Neotree buffers<CR>", desc = "Explorer Buffers", silent = true },
{ "<Leader>eg", ":Neotree git_status<CR>", desc = "Explorer Git", silent = true },
},
opts = {
filesystem = {
follow_current_file = {
enabled = true, -- Enable this feature
leave_dirs_open = true, -- Leave directories open when following
},
filtered_items = {
visible = true,
},
window = {
mappings = {
["<Leader>e"] = "close_window",
},
},
},
},
}

View File

@ -0,0 +1,91 @@
local remap = require("utils.remap")
return { -- Fuzzy Finder (files, lsp, etc)
"nvim-telescope/telescope.nvim",
event = "VimEnter",
branch = "0.1.x",
dependencies = {
"nvim-lua/plenary.nvim",
{ -- If encountering errors, see telescope-fzf-native README for installation instructions
"nvim-telescope/telescope-fzf-native.nvim",
-- `build` is used to run some command when the plugin is installed/updated.
-- This is only run then, not every time Neovim starts up.
build = "make",
-- `cond` is a condition used to determine whether this plugin should be
-- installed and loaded.
cond = function()
return vim.fn.executable("make") == 1
end,
},
{ "nvim-telescope/telescope-ui-select.nvim" },
-- Useful for getting pretty icons, but requires a Nerd Font.
{ "nvim-tree/nvim-web-devicons", enabled = vim.g.have_nerd_font },
},
config = function()
-- Telescope is a fuzzy finder that comes with a lot of different things that
-- it can fuzzy find! It's more than just a "file finder", it can search
-- many different aspects of Neovim, your workspace, LSP, and more!
--
-- The easiest way to use Telescope, is to start by doing something like:
-- :Telescope help_tags
--
-- After running this command, a window will open up and you're able to
-- type in the prompt window. You'll see a list of `help_tags` options and
-- a corresponding preview of the help.
--
-- Two important keymaps to use while in Telescope are:
-- - Insert mode: <c-/>
-- - Normal mode: ?
--
-- This opens a window that shows you all of the keymaps for the current
-- Telescope picker. This is really useful to discover what Telescope can
-- do as well as how to actually do it!
-- [[ Configure Telescope ]]
-- See `:help telescope` and `:help telescope.setup()`
require("telescope").setup({
-- You can put your default mappings / updates / etc. in here
-- All the info you're looking for is in `:help telescope.setup()`
--
-- defaults = {
-- mappings = {
-- i = { ['<c-enter>'] = 'to_fuzzy_refine' },
-- },
-- },
-- pickers = {}
defaults = {
layout_strategy = "vertical",
layout_config = {
-- vertical = { width = 0.5 }
-- horizontal = {
-- width = 0.9,
-- preview_width = 0.5,
-- },
},
mappings = {
n = {
["d"] = "delete_buffer",
},
},
},
})
-- Enable Telescope extensions if they are installed
pcall(require("telescope").load_extension, "fzf")
pcall(require("telescope").load_extension, "ui-select")
local builtin = require("telescope.builtin")
remap.nmap("<leader>sk", builtin.keymaps, { desc = "[S]earch [K]eymaps" })
remap.nmap("<leader>sf", builtin.find_files, { desc = "[S]earch [F]iles" })
remap.nmap("<leader>sw", builtin.grep_string, { desc = "[S]earch current [W]ord" })
remap.nmap("<leader>sg", builtin.live_grep, { desc = "[S]earch by [G]rep" })
remap.nmap("<leader>sd", builtin.diagnostics, { desc = "[S]earch [D]iagnostics" })
remap.nmap("<leader>sr", builtin.lsp_references, { desc = "[S]earch [R]references" })
remap.nmap("<leader>s.", builtin.oldfiles, { desc = '[S]earch Recent Files ("." for repeat)' })
remap.nmap("<leader>ss", builtin.git_status, { desc = "[S]earch Git [S]tatus" })
remap.nmap("<leader><leader>", builtin.buffers, { desc = "Find existing [B]uffers" })
end,
}

View File

@ -0,0 +1,21 @@
return {
'nvim-treesitter/nvim-treesitter',
build = 'TSUpdate',
main = 'nvim-treesitter.configs',
opts = {
ensure_installed = {
'diff',
'lua',
'html',
'css',
'javascript',
'typescript'
},
auto_install = true,
highlight = {
enable = true,
},
indent = { enable = true }
}
}

View File

@ -0,0 +1,35 @@
local M = {}
function M.map(mode, lhs, rhs, opts)
local options = { silent = true, noremap = true }
if opts then
options = vim.tbl_extend("force", options, opts)
end
if type(mode) == "table" then
for _, m in ipairs(mode) do
vim.keymap.set(m, lhs, rhs, options)
end
else
vim.keymap.set(mode, lhs, rhs, options)
end
end
function M.nmap(lhs, rhs, opts)
M.map("n", lhs, rhs, opts)
end
function M.imap(lhs, rhs, opts)
M.map("i", lhs, rhs, opts)
end
function M.vmap(lhs, rhs, opts)
M.map("v", lhs, rhs, opts)
end
function M.tmap(lhs, rhs, opts)
M.map("t", lhs, rhs, opts)
end
return M

View File

@ -0,0 +1,105 @@
-- Window Creation/Closing
Ctrl-w s - Split window horizontally
Ctrl-w v - Split window vertically
Ctrl-w n - Create new window horizontally with empty buffer
Ctrl-w c - Close current window
Ctrl-w o - Close all windows except current one
-- Window Navigation
Ctrl-w h - Move to window on the left
Ctrl-w j - Move to window below
Ctrl-w k - Move to window above
Ctrl-w l - Move to window on the right
-- Window Moving/Rearranging
Ctrl-w H - Move current window to far left
Ctrl-w J - Move current window to bottom
Ctrl-w K - Move current window to top
Ctrl-w L - Move current window to far right
Ctrl-w r - Rotate windows downward/rightward
Ctrl-w R - Rotate windows upward/leftward
Ctrl-w x - Exchange current window with next one
-- Window Resizing
Ctrl-w = - Make all windows equal size
Ctrl-w _ - Maximize height of current window
Ctrl-w | - Maximize width of current window
Ctrl-w > - Increase width by 1 column
Ctrl-w < - Decrease width by 1 column
Ctrl-w + - Increase height by 1 row
Ctrl-w - - Decrease height by 1 row
-- Window Special Commands
Ctrl-w T - Move current window to new tab
Ctrl-w } - Preview definition in new window
Ctrl-w z - Close preview window
Ctrl-w ] - Split window and jump to definition
Ctrl-w f - Split window and edit file under cursor
Ctrl-w i - Split window and show declaration
Ctrl-w ^ - Split window and edit alternate file
-- Tab
gt :tabnext - Go to next tab
gT :tabprevious - Go to previous tab
{n}gt :tabnext {n} - Go to tab number {n}
<Leader>tn :tabnew - Create a new tab - Suggested
<Leader>tc :tabclose - Close current tab - Suggested
<Leader>to :tabonly - Close all other tabs - Suggested
<Leader>t{n} {n}gt - Go to tab {n} - Suggested
<Leader>tm. :tabmove +1 - Move tab right - Suggested
<Leader>tm, :tabmove -1 - Move tab left - Suggested
-- Buffer
<Leader>bl :ls - List all buffers - Suggested
<Leader>bd :bdelete - Delete current buffer - Suggested
<Leader>bn :bnext - Go to next buffer - Suggested
<Leader>bp :bprevious - Go to previous buffer - Suggested
<Leader>b{n} :buffer {n} - Go to buffer {n} - Suggested
<Leader>bb :b<Space> - Start buffer selection - Suggested
<Leader>bo :bufdo bd|1bd - Delete all other buffers - Suggested
-- Telescope
<Leader>sf telescope.find_files - Search Files
<Leader>sg telescope.live_grep - Search by Grep
<Leader>sb telescope.buffers - Search Buffers
<Leader>sh telescope.help_tags - Search Help
<Leader>sp telescope.projects - Search Projects
<Leader>sm telescope.marks - Search Marks
<Leader>sc telescope.commands - Search Commands
<Leader>sk telescope.keymaps - Search Keymaps
<Leader>ss telescope.git_status - Search Git Status
<Leader>sw telescope.grep_string - Search current Word
<Leader>sd telescope.diagnostics - Search Diagnostics
<Leader>sr telescope.lsp_references - Search References
-- Neo-tree
<Leader>e :Neotree toggle - Explorer Toggle
<Leader>E :Neotree focus - Explorer Focus
<Leader>ef :Neotree float - Explorer Float
<Leader>eb :Neotree buffers - Explorer Buffers
<Leader>eg :Neotree git_status - Explorer Git
-- Harpoon
<Leader>h harpoon_ui.toggle_menu - Harpoon Menu
<Leader>m harpoon_mark.add_file - Mark File
<Leader>1 harpoon_ui.nav_file(1) - Harpoon File 1
<Leader>2 harpoon_ui.nav_file(2) - Harpoon File 2
<Leader>3 harpoon_ui.nav_file(3) - Harpoon File 3
<Leader>4 harpoon_ui.nav_file(4) - Harpoon File 4
<Leader>hn harpoon_ui.nav_next - Harpoon Next
<Leader>hp harpoon_ui.nav_prev - Harpoon Previous
-- Terminal
<Leader>tet :terminal cd %:h - Terminal in This dir
<Leader>ter :terminal - Terminal Regular
<Leader>tec :!cd %:h && - Terminal Command
<Esc> <C-\><C-n> - Terminal Normal Mode
<C-w> <C-\><C-n><C-w> - Terminal Window Command
-- LSP
gd vim.lsp.buf.definition - Goto Definition
gr vim.lsp.buf.references - Goto References
K vim.lsp.buf.hover - Hover Documentation
<Leader>rn vim.lsp.buf.rename - Rename
<Leader>ca vim.lsp.buf.code_action - Code Action
<Leader>f vim.lsp.buf.format - Format

View File

@ -0,0 +1,20 @@
{
"global": { "show_in_menu_bar": false },
"profiles": [
{
"devices": [
{
"identifiers": { "is_keyboard": true },
"simple_modifications": [
{
"from": { "key_code": "non_us_backslash" },
"to": [{ "key_code": "grave_accent_and_tilde" }]
}
]
}
],
"name": "Default profile",
"selected": true
}
]
}

View File

@ -0,0 +1,44 @@
{
"global": { "show_in_menu_bar": false },
"profiles": [
{
"devices": [
{
"identifiers": { "is_keyboard": true },
"simple_modifications": [
{
"from": { "key_code": "non_us_backslash" },
"to": [{ "key_code": "grave_accent_and_tilde" }]
}
]
},
{
"identifiers": {
"is_keyboard": true,
"product_id": 49164,
"vendor_id": 7276
},
"simple_modifications": [
{
"from": { "key_code": "left_command" },
"to": [{ "key_code": "left_option" }]
},
{
"from": { "key_code": "left_option" },
"to": [{ "key_code": "left_command" }]
}
]
}
],
"name": "Default profile",
"selected": true,
"simple_modifications": [
{
"from": { "key_code": "caps_lock" },
"to": [{ "key_code": "left_control" }]
}
],
"virtual_hid_keyboard": { "keyboard_type_v2": "ansi" }
}
]
}

View File

@ -0,0 +1,44 @@
{
"$schema": "https:\/\/schema.linearmouse.app\/0.10.0",
"schemes": [
{
"if" : {
"device" : {
"vendorID" : "0x46d",
"productID" : "0xc52b",
"productName" : "USB Receiver",
"category" : "mouse"
}
},
"scrolling": {
"reverse": {
"vertical": true
},
"speed": {
"vertical": 0
},
"acceleration": {
"vertical": 1
},
"distance": {
"vertical": "100px"
},
"modifiers": {
"vertical": {
"command": {
"type": "preventDefault"
}
}
}
},
"buttons": {
"universalBackForward": true
},
"pointer": {
"acceleration": 0.3,
"speed": 0.2,
"disableAcceleration": false
}
}
]
}

40
config/shared/alacritty Normal file
View File

@ -0,0 +1,40 @@
live_config_reload = true
[env]
TERM = "xterm-256color"
[font]
normal = { family = "SF Mono", style = "Regular" }
size = 12
offset = { x = 0, y = 0 }
[window]
decorations_theme_variant = "Dark"
padding = { x = 4, y = 0 }
dynamic_padding = false
resize_increments = true
[keyboard]
bindings = [
# Create new window
{ action = "SpawnNewInstance", key = "N", mods = "Command" },
# Jump back one word
{ key = "Left", mods = "Alt", chars = "\u001bb" },
# Jump forward one word
{ key = "Right", mods = "Alt", chars = "\u001bf" },
# Move to start of line
{ key = "Left", mods = "Command", chars = "\u0001" },
# Move to end of line
{ key = "Right", mods = "Command", chars = "\u0005" },
# Delete backwards
{ key = "Back", mods = "Alt", chars = "\u001B\u007F" }, # word
{ key = "Back", mods = "Command", chars = "\u0015" }, # line
# Delete forwards
{ key = "Delete", mods = "Alt", chars = "\u001Bd" }, # word
{ key = "Delete", mods = "Command", chars = "\u000B" } # line
]
[scrolling]
multiplier = 1

97
config/shared/bin/colortest Executable file
View File

@ -0,0 +1,97 @@
#!/usr/bin/env bash
# Test black and white
echo "=== Black and White ==="
printf "Normal text\n"
printf "\e[1mBold text\e[0m\n"
printf "\e[7mReverse text\e[0m\n\n"
# Test 4-bit ANSI (16 colors)
echo "=== 4-bit ANSI Colors (16 colors) ==="
echo "Foreground colors:"
for i in {30..37}; do
printf "\e[${i}m\\e[${i}m\e[0m "
done
echo -e "\n"
echo "Background colors:"
for i in {40..47}; do
printf "\e[${i}m\\e[${i}m\e[0m "
done
echo -e "\n"
echo "Bright foreground colors:"
for i in {90..97}; do
printf "\e[${i}m\\e[${i}m\e[0m "
done
echo -e "\n"
echo "Bright background colors:"
for i in {100..107}; do
printf "\e[${i}m\\e[${i}m\e[0m "
done
echo -e "\n\n"
# Test 8-bit ANSI (256 colors)
echo "=== 8-bit ANSI Colors (256 colors) ==="
echo "16 System Colors:"
for i in {0..15}; do
printf "\e[48;5;${i}m \e[0m"
if [ $((($i + 1) % 8)) == 0 ]; then
echo
fi
done
echo
echo "216 RGB Colors:"
for i in {16..231}; do
printf "\e[48;5;${i}m \e[0m"
if [ $((($i - 15) % 36)) == 0 ]; then
echo
fi
done
echo
echo "24 Grayscale Colors:"
for i in {232..255}; do
printf "\e[48;5;${i}m \e[0m"
if [ $((($i - 231) % 12)) == 0 ]; then
echo
fi
done
echo -e "\n"
# Test 24-bit true color
echo "=== 24-bit True Color (16.7 million colors) ==="
echo "RGB Color Gradient:"
awk 'BEGIN{
s="/\\";
for (colnum = 0; colnum<77; colnum++) {
r = 255-(colnum*255/76);
g = (colnum*510/76);
b = (colnum*255/76);
if (g>255) g = 510-g;
printf "\033[48;2;%d;%d;%dm", r,g,b;
printf "\033[38;2;%d;%d;%dm", 255-r,255-g,255-b;
printf "%s\033[0m", substr(s,colnum%2+1,1);
}
printf "\n";
}'
echo "RGB Color Bars:"
for r in 0 127 255; do
for g in 0 127 255; do
for b in 0 127 255; do
printf "\e[48;2;${r};${g};${b}m \e[0m"
done
printf " "
done
echo
done
echo
# Print terminal information
echo "=== Terminal Information ==="
echo "TERM: $TERM"
echo "COLORTERM: $COLORTERM"
echo "Reported colors (tput colors): $(tput colors)"

18
config/shared/bin/detect_keys Executable file
View File

@ -0,0 +1,18 @@
#!/bin/zsh
echo "Press any key combination. Press Ctrl+C to exit."
while true; do
result=""
escape_sequence=""
read -sk 1 key
if [[ $key == $'\x1b' ]]; then
escape_sequence+="^["
while read -sk 1 -t 0.01 next_key; do
escape_sequence+="$next_key"
done
result="$escape_sequence"
else
result="$key"
fi
echo -E "Key: $result"
done

41
config/shared/bin/dev Executable file
View File

@ -0,0 +1,41 @@
# --rm: automatically remove the container when it exits
# Check if a container name was provided
if [ $# -eq 0 ]; then
echo "Usage: $0 <container_name>"
exit 1
fi
IMAGE="web-stack"
NAME="${IMAGE}-$1"
# Function to exec into the container
exec_into_container() {
docker exec --detach-keys "ctrl-q,ctrl-p" -it $NAME /bin/zsh
}
# Check if the container exists
if [ "$(docker ps -a -q -f name=$NAME)" ]; then
# Container exists, start it if it's not running
if [ ! "$(docker ps -q -f name=$NAME)" ]; then
echo "Container $NAME exists but is not running. Starting it..."
docker start $NAME
else
echo "Container $NAME is already running."
fi
else
echo "Container $NAME does not exist. Creating and running it in detached mode..."
docker run -d \
--detach-keys "ctrl-q,ctrl-p" \
--network host \
-v $HOME/.ssh:/home/dev/.ssh \
-v $PWD:/workspace \
--name $NAME \
--init \
$IMAGE \
tail -f /dev/null
fi
# Exec into the container
echo "Executing into container $NAME..."
exec_into_container

29
config/shared/bin/ssh-forward Executable file
View File

@ -0,0 +1,29 @@
#!/bin/bash
# Function to display usage information
usage() {
echo "Usage: $0 <user@host> <port1> [port2] [port3] ..."
exit 1
}
# Ensure at least two arguments are provided: host and one port
if [ "$#" -lt 2 ]; then
usage
fi
# Extract the host from the first argument
HOST="$1"
shift # Shift the arguments so that $@ contains the remaining ports
# Initialize the PORTS variable
PORTS=""
# Iterate over the remaining arguments, which are the ports
for port in "$@"; do
PORTS="$PORTS -L ${port}:localhost:${port}"
done
# Construct and run the SSH command
SSH_CMD="ssh -N -T -o ExitOnForwardFailure=yes $HOST $PORTS"
echo "Running: $SSH_CMD"
$SSH_CMD

View File

@ -0,0 +1,48 @@
# Terminal
term = "xterm-256color"
# Fonts
font-family = "SF Mono"
font-family = "SF Mono"
font-size = 12
font-thicken = true
# Cell width (affects letter spacing)
adjust-cell-width = -1
adjust-cell-height = -1
adjust-font-baseline = -1
# Cursor
cursor-style-blink = false
cursor-style = block
shell-integration-features = no-cursor
# Icon
# macos-icon = custom-style
# macos-icon-frame = plastic
# macos-icon-ghost-color = cba6f7
# macos-icon-screen-color = 181825
# Window
#background = #181818
background = #000000
window-theme = dark
window-width = 100
window-height = 26
window-padding-x = 4
window-padding-y = 2
window-colorspace = display-p3
window-decoration = true
macos-titlebar-style = native
# Resize by row
#window-step-resize = true
#window-padding-balance = true
# Background
background-opacity = 1
background-blur-radius = 0

7
config/shared/git Normal file
View File

@ -0,0 +1,7 @@
[init]
defaultBranch = main
[user]
name = Tomas Mirchev
email = contact@tomastm.com
[pull]
rebase = true

63
config/shared/htop/htoprc Normal file
View File

@ -0,0 +1,63 @@
# Beware! This file is rewritten by htop when settings are changed in the interface.
# The parser is also very primitive, and not human-friendly.
htop_version=3.2.2
config_reader_min_version=3
fields=0 48 17 18 38 39 40 2 46 47 49 1
hide_kernel_threads=1
hide_userland_threads=1
hide_running_in_container=0
shadow_other_users=0
show_thread_names=0
show_program_path=1
highlight_base_name=0
highlight_deleted_exe=1
shadow_distribution_path_prefix=0
highlight_megabytes=1
highlight_threads=1
highlight_changes=0
highlight_changes_delay_secs=5
find_comm_in_cmdline=1
strip_exe_from_cmdline=1
show_merged_command=0
header_margin=1
screen_tabs=1
detailed_cpu_time=0
cpu_count_from_one=0
show_cpu_usage=1
show_cpu_frequency=0
show_cpu_temperature=0
degree_fahrenheit=0
update_process_names=0
account_guest_in_cpu_meter=0
color_scheme=0
enable_mouse=1
delay=15
hide_function_bar=0
header_layout=two_50_50
column_meters_0=AllCPUs Memory Swap
column_meter_modes_0=1 1 1
column_meters_1=Tasks LoadAverage Uptime
column_meter_modes_1=2 2 2
tree_view=0
sort_key=47
tree_sort_key=0
sort_direction=-1
tree_sort_direction=1
tree_view_always_by_pid=0
all_branches_collapsed=0
screen:Main=PID USER PRIORITY NICE M_VIRT M_RESIDENT M_SHARE STATE PERCENT_CPU PERCENT_MEM TIME Command
.sort_key=PERCENT_MEM
.tree_sort_key=PID
.tree_view=0
.tree_view_always_by_pid=0
.sort_direction=-1
.tree_sort_direction=1
.all_branches_collapsed=0
screen:I/O=PID USER IO_PRIORITY IO_RATE IO_READ_RATE IO_WRITE_RATE PERCENT_SWAP_DELAY PERCENT_IO_DELAY Command
.sort_key=IO_RATE
.tree_sort_key=PID
.tree_view=0
.tree_view_always_by_pid=0
.sort_direction=-1
.tree_sort_direction=1
.all_branches_collapsed=0

121
config/shared/nvim Normal file
View File

@ -0,0 +1,121 @@
-- [[ Basic Keymaps ]]
-- See `:help vim.keymap.set()`
-- Map 'jk' to escape in insert mode
vim.keymap.set('i', 'jk', '<Esc>', { desc = 'Exit insert mode with jk' })
-- Clear highlights on search when pressing <Esc> in normal mode
-- See `:help hlsearch`
vim.keymap.set('n', '<Esc>', '<cmd>nohlsearch<CR>')
-- Diagnostic keymaps
vim.keymap.set('n', '<leader>q', vim.diagnostic.setloclist, { desc = 'Open diagnostic [Q]uickfix list' })
-- Exit terminal mode in the builtin terminal with a shortcut that is a bit easier
-- for people to discover. Otherwise, you normally need to press <C-\><C-n>, which
-- is not what someone will guess without a bit more experience.
vim.keymap.set('t', '<Esc><Esc>', '<C-\\><C-n>', { desc = 'Exit terminal mode' })
-- Keybinds to make split navigation easier.
-- Use CTRL+<hjkl> to switch between windows
--
-- See `:help wincmd` for a list of all window commands
vim.keymap.set('n', '<C-h>', '<C-w><C-h>', { desc = 'Move focus to the left window' })
vim.keymap.set('n', '<C-l>', '<C-w><C-l>', { desc = 'Move focus to the right window' })
vim.keymap.set('n', '<C-j>', '<C-w><C-j>', { desc = 'Move focus to the lower window' })
vim.keymap.set('n', '<C-k>', '<C-w><C-k>', { desc = 'Move focus to the upper window' })
-- [[ Basic Autocommands ]]
-- See `:help lua-guide-autocommands`
-- Highlight when yanking (copying) text
-- Try it with `yap` in normal mode
-- See `:help vim.highlight.on_yank()`
vim.api.nvim_create_autocmd('TextYankPost', {
desc = 'Highlight when yanking (copying) text',
group = vim.api.nvim_create_augroup('kickstart-highlight-yank', { clear = true }),
callback = function()
vim.highlight.on_yank()
end,
})
-- ---------------------------------------------------
-- ---------------------------------------------------
-- Set <space> as the leader key
-- See `:help mapleader`
-- NOTE: Must happen before plugins are loaded (otherwise wrong leader will be used)
vim.g.mapleader = " "
vim.g.maplocalleader = " "
-- [[ Setting options ]]
-- See `:help vim.opt`
-- NOTE: You can change these options as you wish!
-- For more options, you can see `:help option-list`
-- Disable Neovim background
vim.api.nvim_set_hl(0, "Normal", { bg = "none" })
vim.api.nvim_set_hl(0, "NormalFloat", { bg = "none" })
-- Scroll lines/columns
vim.opt.mousescroll = "hor:1,ver:1"
-- Set indentation preferences
vim.opt.expandtab = true -- Convert tabs to spaces
vim.opt.shiftwidth = 2 -- Number of spaces for auto-indent
vim.opt.tabstop = 2 -- Number of spaces a tab counts for
vim.opt.softtabstop = 2 -- Number of spaces a tab counts for when editing
vim.opt.autoindent = true -- Copy indent from current line when starting new line
vim.opt.smartindent = true -- Do smart autoindenting when starting a new line
-- Disable line wrapping
vim.opt.wrap = false
-- Make line numbers default
vim.opt.number = true
vim.opt.relativenumber = true
-- Enable mouse mode, can be useful for resizing splits for example!
vim.opt.mouse = "a"
-- Don't show the mode, since it's already in the status line
vim.opt.showmode = true
vim.opt.statusline = "%F%m%r%h%w%=%l,%c %P"
-- Sync clipboard between OS and Neovim.
-- Schedule the setting after `UiEnter` because it can increase startup-time.
-- Remove this option if you want your OS clipboard to remain independent.
-- See `:help 'clipboard'`
vim.schedule(function()
vim.opt.clipboard = "unnamedplus"
end)
-- Enable break indent
vim.opt.breakindent = true
-- Save undo history
vim.opt.undofile = true
-- Case-insensitive searching UNLESS \C or one or more capital letters in the search term
vim.opt.ignorecase = true
vim.opt.smartcase = true
-- Decrease update time
vim.opt.updatetime = 250
-- Decrease mapped sequence wait time
-- Displays which-key popup sooner
vim.opt.timeoutlen = 300
-- Configure how new splits should be opened
vim.opt.splitright = true
vim.opt.splitbelow = true
-- Preview substitutions live, as you type!
vim.opt.inccommand = "split"
-- Show which line your cursor is on
vim.opt.cursorline = true
-- Minimal number of screen lines to keep above and below the cursor.
vim.opt.scrolloff = 10

62
config/shared/tmux Normal file
View File

@ -0,0 +1,62 @@
# Change the prefix from 'C-b' to 'C-Space'
unbind C-b
set-option -g prefix C-Space
bind-key C-Space send-prefix
#set -g default-terminal "tmux-256color"
#set -as terminal-features ",*:RGB"
set-option -sg escape-time 10
set-option -g focus-events on
# Set the base index for windows and panes to 1 instead of 0
set -g base-index 1
setw -g pane-base-index 1
# Increase scrollback buffer size
set -g history-limit 10000
# Customize the status bar
set -g status-style bg=default,fg=white
set -g status-left '#[fg=cyan,bold][#S] '
set -g status-left-length 50
set -g status-right ''
# Window status format
setw -g window-status-format '#[fg=white,dim]#I#[fg=grey]:#[fg=white]#W#[fg=grey]#F'
setw -g window-status-current-format '#[fg=cyan,bold]#I#[fg=blue]:#[fg=cyan]#W#[fg=grey]#F'
# Pane border
set -g pane-border-style fg=colour240
set -g pane-active-border-style fg=cyan
# Message text
set -g message-style bg=default,fg=cyan
# Enable mouse support
setw -g mouse on
# Fix scroll. Use N3 instead of N1 to make it quicker
bind-key -T copy-mode-vi WheelUpPane send -N1 -X scroll-up
bind-key -T copy-mode-vi WheelDownPane send -N1 -X scroll-down
# Update terminal titles
set-option -g set-titles on
# Use vim keybindings in copy mode
setw -g mode-keys vi
# Setup 'v' to begin selection as in Vim
bind -T copy-mode-vi v send-keys -X begin-selection
# Pane navigation using vim-like keys
bind -r k select-pane -U
bind -r j select-pane -D
bind -r h select-pane -L
bind -r l select-pane -R
# Automatically renumber windows when one is closed
set -g renumber-windows on
# Reload tmux config
bind r source-file ~/.tmux.conf \; display "Reloaded!"

26
config/shared/vim Normal file
View File

@ -0,0 +1,26 @@
set nocompatible
set nobackup
set encoding=utf-8
set clipboard=unnamed
filetype plugin indent on
let mapleader=" "
inoremap jk <esc>
set number
set relativenumber
set ruler
set cursorline
set scrolloff=10
set nowrap
set showcmd
set wildmenu
set title
set mouse=a
set shiftwidth=2
set tabstop=2
set expandtab
set autoindent
set smartindent
syntax on

70
config/shared/wezterm Normal file
View File

@ -0,0 +1,70 @@
local wezterm = require('wezterm')
local config = wezterm.config_builder()
config.term="xterm-256color"
config.color_scheme = "Catppuccin Frappe"
config.font = wezterm.font('FiraCode Nerd Font')
-- config.window_decorations = "INTEGRATED_BUTTONS | RESIZE"
config.font_size = 14.0
config.use_resize_increments = true
config.window_padding = {
left = 4,
right = 4,
top = 4,
bottom = 0,
}
config.mouse_bindings = {
{
event = { Drag = { streak = 1, button = 'Left' } },
mods = 'CMD',
action = wezterm.action.StartWindowDrag,
},
{
event = { Drag = { streak = 1, button = 'Left' } },
mods = 'CTRL|SHIFT',
action = wezterm.action.StartWindowDrag,
},
}
local act = wezterm.action
config.keys = {
{
key = 'w',
mods = 'CMD',
action = wezterm.action.CloseCurrentTab { confirm = false }
},
{
key = 'LeftArrow',
mods = 'CMD',
action = wezterm.action { SendString = '\x1b0H' }
},
{
key = 'RightArrow',
mods = 'CMD',
action = wezterm.action { SendString = '\x1b0F' }
},
{
key = 'LeftArrow',
mods = 'OPT',
action = act.SendKey { key = 'b', mods = 'ALT' }
},
{
key = 'RightArrow',
mods = 'OPT',
action = act.SendKey { key = 'f', mods = 'ALT' }
}
---- Make Option-Left equivalent to Alt-b which many line editors interpret as backward-word
--{
-- key="LeftArrow",
-- mods="OPT",
-- action=wezterm.action{SendString="\x1bb"}
--},
---- Make Option-Right equivalent to Alt-f; forward-word
--{
-- key="RightArrow",
-- mods="OPT",
-- action=wezterm.action{SendString="\x1bf"}
--}
}
return config

71
config/shared/zsh Normal file
View File

@ -0,0 +1,71 @@
# Add /bin to path
export PATH="$PATH:$HOME/bin"
# Set locales
export LANGUAGE="en_US:en"
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
autoload -Uz compinit && compinit # Autocomplete
zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}' # Case Insensitive
setopt autocd # cd without it
setopt share_history
# Git prompt function
git_prompt_info() {
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
local branch=$(git symbolic-ref --short HEAD 2>/dev/null || git rev-parse --short HEAD)
echo " %F{green}($branch)%f"
fi
}
# Set up the prompt
setopt PROMPT_SUBST # Dyna
PROMPT='%n@%m%f %F{blue}%~%f$(git_prompt_info) $ '
# Disable the log builtin, so we don't conflict with /usr/bin/log
disable log
# Save command history
HISTFILE=${ZDOTDIR:-$HOME}/.zsh_history
HISTSIZE=2000
SAVEHIST=1000
setopt HIST_IGNORE_ALL_DUPS
# Load functions
autoload -U up-line-or-beginning-search
autoload -U down-line-or-beginning-search
zle -N up-line-or-beginning-search
zle -N down-line-or-beginning-search
# Bind both common escape sequences
bindkey '^[[A' up-line-or-beginning-search # normal mode
bindkey '^[OA' up-line-or-beginning-search # application mode
bindkey '^[[B' down-line-or-beginning-search # normal mode
bindkey '^[OB' down-line-or-beginning-search # application mode
# Aliases for ls
alias ls='ls --color=auto'
alias ll='ls -lF'
alias lla='ll -a'
alias ld='ls -ld */' # List only directories
# Aliases for git
alias g='git'
alias ga='git add'
alias gaa='git add --all'
alias gb='git branch'
alias gcm='git commit -m'
alias gam='git commit -am'
alias gco='git checkout'
alias gd='git diff'
alias gf='git fetch'
alias gl='git pull'
alias gp='git push'
alias gst='git status'
alias glg='git log --graph --oneline --decorate --all'
alias gm='git merge'
alias grb='git rebase'
alias grs='git reset'
alias grv='git remote -v'

196
manage.py Normal file
View File

@ -0,0 +1,196 @@
#!/usr/bin/env python3
import argparse
import subprocess
import json
from pathlib import Path
import shlex
import shutil
DOTFILES_DIR = Path(__file__).parent
SETUPS_DIR = DOTFILES_DIR / "setups"
CONFIG_DIR = DOTFILES_DIR / "config"
CONFIG_PATH = DOTFILES_DIR / "config.json"
def load_config():
if not CONFIG_PATH.exists():
raise FileNotFoundError(f"Configuration file not found: {CONFIG_PATH}")
with open(CONFIG_PATH, "r") as f:
data = json.load(f)
if not isinstance(data, dict):
raise ValueError("JSON must be an object")
if "environments" not in data:
raise ValueError("Missing required field: 'environments'")
if not isinstance(data["environments"], dict):
raise ValueError("'environments' must be an object")
if "template" in data and not isinstance(data["template"], dict):
raise ValueError("'template' must be an object if present")
return data
def get_environment_packages(config, env, search_package=None):
env_entries = config["environments"].get(env, [])
if not env_entries:
raise TypeError(f"Environment {env} was not found or it is empty")
template_config = config.get("template", {})
packages = []
for entry in env_entries:
if isinstance(entry, str):
entry = { "package": entry }
package_name = entry.pop("package", None)
if package_name is None:
raise TypeError(f"The following entry is missing `package` field: {entry}")
package = { "name": package_name }
if package_name in template_config and not entry.get("ignore-template", False):
template_entry = template_config[package_name]
package = { **package, **template_entry, **entry }
else:
package.update(entry)
package.pop("ignore-template", None)
if "link" in package:
link_from = package["link"].get("from")
link_to = package["link"].get("to")
if not isinstance(link_from, str) or not isinstance(link_to, str):
raise ValueError("`link` should follow the structure: `{ from: str, to: str }`")
if len(link_from.split("/")) != 2:
raise ValueError("`link.from` should be '<env>/<package>'")
package["link"] = {
"from": Path(CONFIG_DIR / link_from).expanduser(),
"to": Path(link_to).expanduser()
}
if search_package == None:
packages.append(package)
else:
if package["name"] == search_package:
packages.append(package)
break
return packages
def force_delete(path):
if path.is_file() or path.is_symlink():
path.unlink()
elif path.is_dir():
shutil.rmtree(path)
def link_environment(config, env, **kwargs):
options = {
"package": kwargs.get("package"),
"copy": kwargs.get("copy", False),
"force": kwargs.get("force", False)
}
packages = get_environment_packages(config, env, search_package=options["package"])
for package in packages:
print(f"[{package['name']}]")
if "link" not in package:
print("\t> Skipped: No link entry")
continue
src = package["link"]["from"]
dest = package["link"]["to"]
if dest.exists():
if options["force"]:
force_delete(dest)
print(f"\t> Deleted: {dest}")
else:
print(f"\t> Skipped: Already exists {dest}")
continue
if options["copy"]:
dest.parent.mkdir(parents=True, exist_ok=True)
shutil.copy(src, dest)
print(f"\t> Copied: {src} -> {dest}")
else:
dest.symlink_to(src)
print(f"\t> Symlinked: {src} -> {dest}")
if "post-link" in package:
command = package["post-link"]
subprocess.run(shlex.split(command), check=True)
print(f"\t> Post-link executed: `{command}`")
def install_environment(config, env, **kwargs):
options = {
"package": kwargs.get("package"),
}
packages = get_environment_packages(config, env, search_package=options["package"])
for package in packages:
print(f"[{package['name']}]")
if "install" not in package:
print("\t> Skipped: No install entry")
continue
install_command = package.get("install")
if install_command:
subprocess.run(shlex.split(install_command), check=True)
print(f"\t> Installed: `{install_command}`")
if "post-install" in package:
postinstall_command = package["post-install"]
subprocess.run(shlex.split(postinstall_command), check=True)
print(f"\t> Post-install executed: `{postinstall_command}`")
def setup_environment(config, env, **kwargs):
print(f"[{env}]")
options = {
"extra_args": kwargs.get("extra"),
}
setup_script = SETUPS_DIR / f"{env}.sh"
if setup_script.exists():
cmd = ["bash", str(setup_script)]
if options["extra_args"]:
cmd.extend(shlex.split(options["extra_args"])) # Split extra args safely
subprocess.run(cmd, check=True)
print(f"\t> Setup script executed: {setup_script} {options['extra_args'] or ''}")
else:
print(f"\t> No setup script found for {env}")
def main():
config = load_config()
config_envs = list(config["environments"].keys())
setup_envs = [script.stem for script in SETUPS_DIR.glob("*.sh")]
parser = argparse.ArgumentParser(description="Dotfile & System Setup Manager")
subparsers = parser.add_subparsers(dest="command", required=True)
subparser = subparsers.add_parser("link", help="Link configs")
subparser.add_argument("env", choices=config_envs)
subparser.add_argument("-p", "--package")
subparser.add_argument("-f", "--force", action="store_true")
subparser.add_argument("--copy", action="store_true")
subparser = subparsers.add_parser("install", help="Install packages")
subparser.add_argument("env", choices=config_envs)
subparser.add_argument("-p", "--package")
setup_parser = subparsers.add_parser("setup", help="Run setup script")
setup_parser.add_argument("env", choices=setup_envs)
setup_parser.add_argument("--extra")
args = parser.parse_args()
command_actions = {"link": link_environment, "install": install_environment, "setup": setup_environment}
command_actions[args.command](config, **vars(args))
if __name__ == "__main__":
main()

View File

@ -0,0 +1,20 @@
#!/bin/bash
if [ "$EUID" -ne 0 ]; then
echo "Please run this script as root using sudo."
exit 1
fi
if [ -z "$1" ]; then
echo "Usage: $0 <new-hostname>"
exit 1
fi
NEW_HOSTNAME=$1
OLD_HOSTNAME=$(hostname)
hostnamectl set-hostname "$NEW_HOSTNAME"
sed -i "s/$OLD_HOSTNAME/$NEW_HOSTNAME/g" /etc/hosts
echo "Hostname has been changed to: $(hostname)"

63
scripts/linux-setup_docker.sh Executable file
View File

@ -0,0 +1,63 @@
#!/bin/bash
set -e
if [ "$EUID" -ne 0 ]; then
echo "Please run this script as root: sudo $0"
exit 1
fi
echo "Removing old Docker versions..."
for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do
apt-get remove -y $pkg || true
done
# Detect OS
if [ -f /etc/debian_version ]; then
DOCKER_OS="debian"
elif [ -f /etc/lsb-release ]; then
DOCKER_OS="ubuntu"
else
echo "Error: Unsupported OS"
exit 1
fi
echo "Updating package list and installing dependencies..."
apt-get update
apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release
echo "Setting up Docker repository..."
# Add Docker's official GPG key
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/${DOCKER_OS}/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/${DOCKER_OS} \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
tee /etc/apt/sources.list.d/docker.list > /dev/null
echo "Installing Docker..."
apt-get update
apt-get install -y \
docker-ce \
docker-ce-cli \
containerd.io \
docker-buildx-plugin \
docker-compose-plugin
# Verify installation
echo "Verifying Docker installation..."
docker --version
containerd --version
echo "Configuring Docker permissions..."
groupadd docker 2>/dev/null || true
usermod -aG docker ${SUDO_USER:-$USER}
echo "Enabling and starting Docker services..."
systemctl enable --now docker.service
systemctl enable --now containerd.service
echo "Docker setup completed. Please log out and log back in for group changes to take effect."

30
scripts/linux-setup_zsh.sh Executable file
View File

@ -0,0 +1,30 @@
#!/bin/bash
set -e
if [ "$(id -u)" -ne 0 ]; then
echo "Please run this script as root: sudo $0"
exit 1
fi
# Check if Zsh is already installed
if command -v zsh &> /dev/null; then
echo "Zsh is already installed. Skipping installation."
else
echo "Updating package list..."
apt-get update
echo "Installing Zsh..."
apt-get install -y zsh
if ! command -v zsh &> /dev/null; then
echo "Error: Zsh installation failed."
exit 1
fi
fi
echo "Changing default shell to Zsh for user ${SUDO_USER:-$USER}..."
zsh_path=$(command -v zsh)
chsh -s "$zsh_path" "${SUDO_USER:-$USER}"
echo "Zsh installation and setup complete. Please log out and log back in for changes to take effect."

10
scripts/macos-brew_backup.sh Executable file
View File

@ -0,0 +1,10 @@
#!/bin/bash
BACKUP_FILE="$HOME/.dotfiles/config/envs/macos/homebrew/Brewfile"
echo "Backing up Homebrew installations..."
mkdir -p "$(dirname "$BACKUP_FILE")"
brew bundle dump --file="$BACKUP_FILE" --force
echo "Backup saved to $BACKUP_FILE"

13
scripts/macos-brew_restore.sh Executable file
View File

@ -0,0 +1,13 @@
#!/bin/bash
BACKUP_FILE="$HOME/.dotfiles/config/envs/macos/homebrew/Brewfile"
if [[ ! -f "$BACKUP_FILE" ]]; then
echo "Backup file not found: $BACKUP_FILE"
exit 1
fi
echo "Restoring Homebrew installations..."
brew bundle --file="$BACKUP_FILE"
echo "Homebrew restoration complete."

View File

@ -0,0 +1,22 @@
#!/bin/bash
if [ "$EUID" -ne 0 ]; then
echo "Please run this script as root using sudo."
exit 1
fi
if [ -z "$1" ]; then
echo "Usage: $0 <new-hostname>"
exit 1
fi
NEW_HOSTNAME=$1
scutil --set HostName "$NEW_HOSTNAME"
scutil --set ComputerName "$NEW_HOSTNAME"
scutil --set LocalHostName "$NEW_HOSTNAME"
echo "Hostname has been changed to:"
echo "HostName: $(scutil --get HostName)"
echo "ComputerName: $(scutil --get ComputerName)"
echo "LocalHostName: $(scutil --get LocalHostName)"

57
scripts/setup_ssh_keys.sh Executable file
View File

@ -0,0 +1,57 @@
#!/bin/bash
set -e
usage() {
echo "Usage: $0 --comment <comment> [--filename <filename>]"
echo " --comment <comment> The comment for the SSH key."
echo " --filename <filename> (optional) The filename suffix for the SSH key. Defaults to 'id_ed25519'."
exit 1
}
# Default values
COMMENT=""
FILENAME="id_ed25519"
# Parse named arguments
while [[ $# -gt 0 ]]; do
case $1 in
--comment)
COMMENT="$2"
shift 2
;;
--filename)
FILENAME="id_ed25519_$2"
shift 2
;;
*)
echo "Unknown argument: $1"
usage
;;
esac
done
# Validate required arguments
if [ -z "$COMMENT" ]; then
echo "Error: --comment is required."
usage
fi
SSH_DIR="$HOME/.ssh"
KEY_PATH="$SSH_DIR/$FILENAME"
# Ensure SSH directory exists
mkdir -p "$SSH_DIR"
chmod 700 "$SSH_DIR"
# Generate SSH key
if [ -f "$KEY_PATH" ]; then
echo "Error: Key file $KEY_PATH already exists."
exit 1
fi
ssh-keygen -t ed25519 -C "$COMMENT" -f "$KEY_PATH" -N ""
echo "SSH key created: $KEY_PATH"
echo "Public key:"
cat "$KEY_PATH.pub"