Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ git clone https://github.com/maedmatt/dotfiles.git ~/dotfiles && cd ~/dotfiles
./install.sh --all # everything
```

`--apps` installs both tmux and Herdr. Herdr uses its own terminal runtime, so
tmux remains available as an independent fallback rather than a dependency.
It also installs the shared Neovim toolchain: `uv`, CMake/CTest, `clangd`,
`clang-format`, and the Tree-sitter CLI.
After connecting over SSH, run `herdr` to start or attach to the multiplexer.

## Linux

The app installer supports Debian/Ubuntu on `x86_64` and `aarch64`. It uses
Expand Down
30 changes: 27 additions & 3 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ install_apps() {
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
fi
# tree-sitter-cli: nvim-treesitter main branch builds parsers with it
brew install neovim uv yazi tmux lazygit btop fzf fd ripgrep tree-sitter-cli imagemagick ghostscript mermaid-cli bun
brew install neovim uv cmake yazi tmux herdr lazygit btop fzf fd ripgrep tree-sitter-cli clang-format imagemagick ghostscript mermaid-cli bun
else
# Detect architecture
ARCH=$(uname -m) # x86_64 or aarch64
Expand Down Expand Up @@ -105,6 +105,9 @@ install_apps() {
btop \
build-essential \
ca-certificates \
clangd \
clang-format \
cmake \
curl \
git \
gzip \
Expand Down Expand Up @@ -134,8 +137,17 @@ install_apps() {
rm -f /tmp/fd.deb
fi

# neovim
if ! command -v nvim &> /dev/null; then
# Keep every workstation on the shared, tested Neovim 0.12 baseline.
# Replace older distro/manual installs too.
nvim_works() {
local version

command -v nvim &> /dev/null || return 1
version=$(nvim --version 2>/dev/null | awk 'NR == 1 { sub(/^v/, "", $2); print $2 }') || return 1
[ -n "$version" ] && dpkg --compare-versions "$version" ge 0.12.0
}

if ! nvim_works; then
download /tmp/nvim.tar.gz \
"https://github.com/neovim/neovim/releases/latest/download/nvim-linux-${NVIM_ARCH}.tar.gz"

Expand Down Expand Up @@ -186,6 +198,12 @@ install_apps() {
rm -f /tmp/tree-sitter.gz /tmp/tree-sitter
fi

# uv drives every Python classroom project and installs into
# ~/.local/bin, which linux/bashrc already adds to PATH.
if ! command -v uv &> /dev/null; then
curl -LsSf https://astral.sh/uv/install.sh | sh
fi

# lazygit
if ! command -v lazygit &> /dev/null; then
V=$(gh_latest jesseduffield/lazygit)
Expand Down Expand Up @@ -224,6 +242,12 @@ install_apps() {
--no-zsh \
--no-fish

# Herdr publishes a self-updating binary for Linux. It is independent
# of tmux; tmux remains installed as a separate fallback multiplexer.
if ! command -v herdr &> /dev/null; then
curl -fsSL https://herdr.dev/install.sh | sh
fi

fi
}

Expand Down
5 changes: 5 additions & 0 deletions macos/ghostty/config
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
# macOS-specific Ghostty configuration
config-file = ~/dotfiles/shared/ghostty/config

cursor-style-blink = false
shell-integration-features = no-cursor,ssh-terminfo,ssh-env
font-family = Ioskeley Mono
font-size = 14
window-decoration = true
macos-non-native-fullscreen = true
18 changes: 15 additions & 3 deletions macos/zshrc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ alias la='ls -la'
alias vim="nvim"
alias vi="nvim"
alias lg="lazygit"
alias tailscale="/Applications/Tailscale.app/Contents/MacOS/Tailscale"

# the code are the 256 color codes
# refrence here --> https://www.ditig.com/256-colors-cheat-sheet
Expand Down Expand Up @@ -51,9 +52,20 @@ function y() {
rm -f -- "$tmp"
}

# bun completions
[ -s "/Users/matteo/.bun/_bun" ] && source "/Users/matteo/.bun/_bun"

# bun
export BUN_INSTALL="$HOME/.bun"
export PATH="$BUN_INSTALL/bin:$PATH"
[ -s "$BUN_INSTALL/_bun" ] && source "$BUN_INSTALL/_bun"

# pnpm
export PNPM_HOME="$HOME/Library/pnpm"
case ":$PATH:" in
*":$PNPM_HOME:"*) ;;
*) export PATH="$PNPM_HOME:$PATH" ;;
esac
# pnpm end

# OpenClaw Completion
autoload -Uz compinit
compinit
[ -r "$HOME/.openclaw/completions/openclaw.zsh" ] && source "$HOME/.openclaw/completions/openclaw.zsh"
8 changes: 6 additions & 2 deletions shared/nvim/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ vim.g.loaded_netrwPlugin = 1

-- Editor behavior
vim.opt.mouse = "a"
-- Use OSC 52 for remote sessions; prefer the native provider locally.
if vim.env.SSH_CONNECTION then
-- Use OSC 52 for SSH and Herdr panes; prefer the native provider locally.
-- Herdr's long-lived server may outlive the original SSH environment.
if vim.env.SSH_CONNECTION or vim.env.HERDR_ENV == "1" then
vim.g.clipboard = "osc52"
end
vim.opt.clipboard = "unnamedplus"
Expand Down Expand Up @@ -138,6 +139,9 @@ map("n", "<leader>yp", function() yank_path(vim.fn.expand("%:p")) end, { desc =
map("n", "<leader>yr", function() yank_path(vim.fn.fnamemodify(vim.fn.expand("%"), ":.")) end, { desc = "Yank relative path" })
map("n", "<leader>yn", function() yank_path(vim.fn.expand("%:t")) end, { desc = "Yank filename" })

-- Run the nearest Python or CMake project's tests in a terminal.
require("test_runner").setup()

-- Edit
map("v", "J", ":m '>+1<CR>gv=gv")
map("v", "K", ":m '<-2<CR>gv=gv")
Expand Down
34 changes: 17 additions & 17 deletions shared/nvim/lazy-lock.json
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
{
"LuaSnip": { "branch": "master", "commit": "0abc8f390b278c3b4aabc4c004ac8a088b65cf24" },
"blink.cmp": { "branch": "main", "commit": "78336bc89ee5365633bcf754d93df01678b5c08f" },
"catppuccin": { "branch": "main", "commit": "79e2049a0fdf7ec840f8463fe1d962c94493196b" },
"conform.nvim": { "branch": "master", "commit": "619363c30309d29ffa631e67c8183f2a72caa373" },
"LuaSnip": { "branch": "master", "commit": "dae4f5aaa3574bd0c2b9dd20fb9542a02c10471c" },
"blink.cmp": { "branch": "main", "commit": "451168851e8e2466bc97ee3e026c3dcb9141ce07" },
"catppuccin": { "branch": "main", "commit": "edefef779ab08ce1a4a404713e3012b0d202bd35" },
"conform.nvim": { "branch": "master", "commit": "016802de402556da54c36bd7359b441266b01cdd" },
"fidget.nvim": { "branch": "main", "commit": "6f793b2bcd2d35e201c09520f698bb763220908a" },
"friendly-snippets": { "branch": "main", "commit": "6cd7280adead7f586db6fccbd15d2cac7e2188b9" },
"fzf-lua": { "branch": "main", "commit": "28440c741e927592877514fba3db21eb188caec8" },
"gitsigns.nvim": { "branch": "main", "commit": "31d6fb2d618bca1482b9f274751ead5f03461408" },
"lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" },
"fzf-lua": { "branch": "main", "commit": "6e41ba7505f35a6f054ca682b043aa0cea36c2ef" },
"gitsigns.nvim": { "branch": "main", "commit": "7c4faa3540d0781a28588cafbd4dd187a28ac6e3" },
"lazy.nvim": { "branch": "main", "commit": "85c7ff3711b730b4030d03144f6db6375044ae82" },
"lualine.nvim": { "branch": "master", "commit": "221ce6b2d999187044529f49da6554a92f740a96" },
"mason-lspconfig.nvim": { "branch": "main", "commit": "7adc933dabcc7c86ae6b07aff7ee68eac398491f" },
"mason-lspconfig.nvim": { "branch": "main", "commit": "a676ab7282da8d651e175118bcf54483ca11e46d" },
"mason-tool-installer.nvim": { "branch": "main", "commit": "443f1ef8b5e6bf47045cb2217b6f748a223cf7dc" },
"mason.nvim": { "branch": "main", "commit": "2a6940af80375532e5e9e7c1f2fc6319a1b7a69d" },
"mini.nvim": { "branch": "main", "commit": "946ae64e0ee807ae3c41f382f0114b4ed4915b2c" },
"nvim-lspconfig": { "branch": "master", "commit": "b7b920947f21339ee41fbb38c79d6445e12900aa" },
"nvim-tree.lua": { "branch": "master", "commit": "4213bd6eabac38b16dd6615002b6243b23cf3bf6" },
"nvim-treesitter": { "branch": "main", "commit": "61df84986b4b4ec469ee745a182e433d49f8c27e" },
"nvim-treesitter-context": { "branch": "master", "commit": "b311b30818951d01f7b4bf650521b868b3fece16" },
"mason.nvim": { "branch": "main", "commit": "44d1e90e1f66e077268191e3ee9d2ac97cc18e65" },
"mini.nvim": { "branch": "main", "commit": "9990c41f10f54f29a888d13024c9f765037bde23" },
"nvim-lspconfig": { "branch": "master", "commit": "dd261ad5266ab5bbec249d21efeceda98ff3e1a6" },
"nvim-tree.lua": { "branch": "master", "commit": "b3772adec8db61ba9098c5624a0823a77be3a23d" },
"nvim-treesitter": { "branch": "main", "commit": "2f5d4c3f3c675962242096bcc8e586d76dd72eb2" },
"nvim-treesitter-context": { "branch": "master", "commit": "f3061339b8eaf9fda873600bc425b8d2d8502533" },
"nvim-treesitter-textobjects": { "branch": "main", "commit": "898ee307df58f854d11cd7edd06472574d48014e" },
"nvim-web-devicons": { "branch": "master", "commit": "2ae6958df7ced50baac5035cec0c15799eedfbf7" },
"nvim-web-devicons": { "branch": "master", "commit": "d7462543c9e366c0d196c7f67a945eaaf5d99414" },
"outline.nvim": { "branch": "main", "commit": "2a132953b944561d45b52e4541ebfff71934a742" },
"render-markdown.nvim": { "branch": "main", "commit": "f422cb5c6855f150e2ddcfaf44e7157b98b34f6a" },
"render-markdown.nvim": { "branch": "main", "commit": "e3c18ddd27a853f85a6f513a864cf4f2982b9f26" },
"snacks.nvim": { "branch": "main", "commit": "882c996cf28183f4d63640de0b4c02ec886d01f2" },
"tiny-inline-diagnostic.nvim": { "branch": "main", "commit": "6264451f14119d63a52580e5198d6baf8518b0b2" },
"vimtex": { "branch": "master", "commit": "853eb587873a7061a083655578be56dd8027b834" },
"vimtex": { "branch": "master", "commit": "82d2305ff71dfb3bd91602534cc9bb9a195bcb38" },
"which-key.nvim": { "branch": "main", "commit": "3aab2147e74890957785941f0c1ad87d0a44c15a" }
}
10 changes: 9 additions & 1 deletion shared/nvim/lua/plugins/format.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
-- Formatting on save through conform, with the LSP as fallback.
-- :ConformDisable turns it off globally, :ConformDisable! for this buffer only.
local manual_format_only = { c = true, cpp = true, objc = true, objcpp = true, cuda = true }

vim.api.nvim_create_user_command("ConformDisable", function(args)
if args.bang then
vim.b.disable_autoformat = true
Expand Down Expand Up @@ -29,13 +31,19 @@ return {
notify_on_error = false,
default_format_opts = { timeout_ms = 1000, lsp_format = "fallback" },
format_after_save = function(bufnr)
if vim.g.disable_autoformat or vim.b[bufnr].disable_autoformat then
if
vim.g.disable_autoformat
or vim.b[bufnr].disable_autoformat
or manual_format_only[vim.bo[bufnr].filetype]
then
return
end
return { timeout_ms = 1000, lsp_format = "fallback" }
end,
formatters_by_ft = {
python = { "ruff_fix", "ruff_format" },
c = { "clang-format" },
cpp = { "clang-format" },
lua = { "stylua" },
json = { "jq" },
sh = { "shfmt" },
Expand Down
44 changes: 28 additions & 16 deletions shared/nvim/lua/plugins/lsp.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,38 @@ return {
"neovim/nvim-lspconfig",
event = { "BufReadPre", "BufNewFile" },
config = function()
local lspconfig = require("lspconfig")

-- Mason installs the managed servers, but activation stays explicit so
-- system servers such as clangd follow the same predictable path.
require("mason-lspconfig").setup({
handlers = {
function(server_name)
lspconfig[server_name].setup({})
end,
["basedpyright"] = function()
lspconfig.basedpyright.setup({
settings = {
basedpyright = { typeCheckingMode = "standard" },
},
})
end,
["ruff"] = function()
lspconfig.ruff.setup({})
end,
automatic_enable = false,
})

vim.lsp.config("basedpyright", {
settings = {
basedpyright = {
analysis = {
typeCheckingMode = "standard",
},
},
},
})

-- clangd comes from Xcode/Homebrew rather than Mason. Including
-- CMakeLists.txt lets each classroom exercise form its own LSP root.
vim.lsp.config("clangd", {
root_markers = {
".clangd",
".clang-tidy",
".clang-format",
"compile_commands.json",
"compile_flags.txt",
"CMakeLists.txt",
".git",
},
})

vim.lsp.enable({ "basedpyright", "ruff", "texlab", "clangd" })

vim.api.nvim_create_autocmd("LspAttach", {
group = vim.api.nvim_create_augroup("UserLspConfig", { clear = true }),
callback = function(args)
Expand Down
98 changes: 98 additions & 0 deletions shared/nvim/lua/test_runner.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
local M = {}

local markers = {
{ name = "pyproject.toml", kind = "python" },
{ name = "CMakeLists.txt", kind = "cmake" },
}

local function marker_at(dir, preferred_kind)
if preferred_kind then
for _, marker in ipairs(markers) do
if marker.kind == preferred_kind and vim.uv.fs_stat(vim.fs.joinpath(dir, marker.name)) then
return marker
end
end
end

for _, marker in ipairs(markers) do
if vim.uv.fs_stat(vim.fs.joinpath(dir, marker.name)) then
return marker
end
end
end

local function find_from(start, preferred_kind)
local dir = vim.fs.normalize(start)
while dir do
local marker = marker_at(dir, preferred_kind)
if marker then
return { root = dir, kind = marker.kind, marker = marker.name }
end

local parent = vim.fs.dirname(dir)
if not parent or parent == dir then
break
end
dir = parent
end
end

function M.find_project(bufnr)
bufnr = bufnr or 0
local filetype = vim.bo[bufnr].filetype
local preferred_kind = filetype == "python" and "python"
or ({ c = true, cpp = true, objc = true, objcpp = true, cuda = true })[filetype] and "cmake"
or nil
local cwd = vim.fn.getcwd()
local name = vim.api.nvim_buf_get_name(bufnr)

if name ~= "" then
local project = find_from(vim.fs.dirname(name), preferred_kind)
if project then
return project
end
end

return find_from(cwd, preferred_kind)
end

local commands = {
python = "uv run pytest",
cmake = table.concat({
"cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON",
"cmake --build build",
"ctest --test-dir build --output-on-failure",
}, " && "),
}

function M.run()
local project = M.find_project(0)
if not project then
vim.notify("No pyproject.toml or CMakeLists.txt found above the buffer or working directory", vim.log.levels.ERROR)
return
end

vim.cmd("botright new")
local job = vim.fn.jobstart({ "sh", "-c", commands[project.kind] }, {
cwd = project.root,
term = true,
})
if job <= 0 then
vim.cmd("bdelete!")
vim.notify("Could not start test command", vim.log.levels.ERROR)
return
end
vim.cmd("startinsert")
end

function M.setup()
vim.api.nvim_create_user_command("RunTests", M.run, {
desc = "Run tests for the nearest Python or CMake project",
})
vim.keymap.set("n", "<leader>tt", "<cmd>RunTests<cr>", {
desc = "Run project tests",
silent = true,
})
end

return M
Loading