#!/bin/bash # Resolve this script's own directory so config/ can sit right next to # it and be found regardless of where/how this is invoked (./setup_neovim.sh, # bash /some/path/setup_neovim.sh, a symlink, etc.) -- config and other # files are real, standalone files under config/, not embedded in this # script, so this script's only job is installing dependencies and # copying them into place. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" CONFIG_DIR="$SCRIPT_DIR/config" if [ ! -d "$CONFIG_DIR" ]; then echo "ERROR: $CONFIG_DIR not found." echo "This script expects a config/ directory next to it (keep the two together" echo "however you copy/clone this -- e.g. don't move setup_neovim.sh on its own)." exit 1 fi echo echo "=========================================" echo " Starting Neovim Setup" echo "=========================================" # 1. Detect distro and install system dependencies echo "[1/4] Detecting distro and installing system dependencies..." if [ ! -f /etc/os-release ]; then echo "ERROR: Cannot detect your Linux distribution (/etc/os-release not found)." echo "This script supports Fedora, Ubuntu, Debian, and Arch Linux." exit 1 fi . /etc/os-release DISTRO_ID="$ID" DISTRO_ID_LIKE="${ID_LIKE:-}" # Normalize into one of: fedora, debian, arch. Checking ID_LIKE too so # common derivatives (Pop!_OS, Linux Mint, Manjaro, EndeavourOS, etc.) # land in the right family even though only the four distros above # were specifically asked for. case "$DISTRO_ID $DISTRO_ID_LIKE" in *fedora*|*rhel*) DISTRO_FAMILY="fedora" ;; *arch*) DISTRO_FAMILY="arch" ;; *debian*|*ubuntu*) DISTRO_FAMILY="debian" ;; *) echo "ERROR: Unsupported or undetected distro (ID=$DISTRO_ID, ID_LIKE=$DISTRO_ID_LIKE)." echo "This script supports Fedora, Ubuntu, Debian, and Arch Linux." exit 1 ;; esac echo "Detected: $DISTRO_ID (family: $DISTRO_FAMILY)" # Which Neovim frontend(s) to set up. Neovide (a separate GUI # application) always runs the real `nvim` binary as its backend -- # the terminal-based install right below happens either way and is # what Neovide itself depends on -- this choice only controls whether # Neovide is ALSO installed on top of it. Both frontends share the # exact same config (see config/nvim/init.lua's `if vim.g.neovide` # block), so there's nothing else to pick between; this is purely an # installation question. NVIM_FRONTEND="terminal" if [ -t 0 ]; then echo echo "Which Neovim frontend(s) do you want installed?" echo " 1) Terminal-based only (nvim in your terminal)" echo " 2) Desktop application only (Neovide GUI -- still needs the nvim" echo " binary below as its backend, so that's installed either way)" echo " 3) Both" read -rp "Enter 1, 2, or 3 [1]: " FRONTEND_CHOICE case "$FRONTEND_CHOICE" in 2) NVIM_FRONTEND="desktop" ;; 3) NVIM_FRONTEND="both" ;; *) NVIM_FRONTEND="terminal" ;; esac else echo "Non-interactive shell detected -- defaulting to terminal-based Neovim only." echo "Run this script directly (not piped) and choose option 2 or 3 to also install Neovide." fi # The MariaDB *client* (not the server) is installed too: vim-dadbod runs # queries by shelling out to the `mariadb` binary. The package name differs # per distro -- Fedora: mariadb, Debian/Ubuntu: mariadb-client, Arch: # mariadb-clients. To actually query something you also need a server # running somewhere; this script neither installs nor starts one. case "$DISTRO_FAMILY" in fedora) sudo dnf install -y neovim git curl wget gcc gcc-c++ make cmake \ nodejs npm python3-pip ripgrep fd-find \ golang clang-tools-extra dotnet-sdk-8.0 unzip fontconfig \ lldb gdb fish mariadb ;; debian) export DEBIAN_FRONTEND=noninteractive sudo apt-get update # Package name differences from Fedora: g++ (not gcc-c++), # golang-go (not golang), clang-tools (not clang-tools-extra -- # Debian/Ubuntu split clang-format/clang-tidy differently, this # is the closest equivalent bundle). sudo apt-get install -y neovim git curl wget gcc g++ make cmake \ nodejs npm python3-pip ripgrep fd-find \ golang-go clang-tools unzip fontconfig \ lldb gdb fish mariadb-client # fd-find installs its binary as `fdfind` on Debian/Ubuntu (a # name clash with an unrelated existing package called `fd`), # unlike Fedora/Arch where it's just `fd`. Symlink it so tools # that look for a plain `fd` on PATH (e.g. Telescope's # find_files) work the same way here as everywhere else. if command -v fdfind >/dev/null 2>&1 && ! command -v fd >/dev/null 2>&1; then mkdir -p "$HOME/.local/bin" ln -sf "$(command -v fdfind)" "$HOME/.local/bin/fd" echo "Symlinked fdfind -> ~/.local/bin/fd (make sure ~/.local/bin is on your PATH)." fi # .NET SDK: Ubuntu ships dotnet-sdk-8.0 directly in its own # repos. Debian does not (long-standing packaging/licensing # reasons) and needs Microsoft's own apt feed added first. # Best-effort either way -- a failure here doesn't stop the # rest of the script, since this only matters for C#/omnisharp. if [ "$DISTRO_ID" = "ubuntu" ] || echo "$DISTRO_ID_LIKE" | grep -qi ubuntu; then sudo apt-get install -y dotnet-sdk-8.0 || \ echo "WARNING: dotnet-sdk-8.0 install failed -- see https://learn.microsoft.com/en-us/dotnet/core/install/linux-ubuntu-install" else ( set -e DEBIAN_MAJOR="${VERSION_ID%%.*}" wget -q "https://packages.microsoft.com/config/debian/${DEBIAN_MAJOR}/packages-microsoft-prod.deb" -O /tmp/packages-microsoft-prod.deb sudo dpkg -i /tmp/packages-microsoft-prod.deb rm -f /tmp/packages-microsoft-prod.deb sudo apt-get update sudo apt-get install -y dotnet-sdk-8.0 ) || echo "WARNING: dotnet-sdk-8.0 install failed -- see https://learn.microsoft.com/en-us/dotnet/core/install/linux-debian for manual steps" fi ;; arch) # Arch's own wiki explicitly warns against `pacman -Sy # ` (sync without upgrading first) -- it risks # partial-upgrade dependency issues. -Syu first is the # recommended safe order; this does mean the script also # upgrades your existing packages, not just installs new ones, # which is expected/intentional here, not a side effect to # work around. sudo pacman -Syu --noconfirm # Package name differences from Fedora: gcc includes g++ # already (no separate gcc-c++ package), python-pip (not # python3-pip -- Arch's default python already is python3), # fd (not fd-find), go (not golang), clang bundles clang-format/ # clang-tidy directly (no separate clang-tools-extra). sudo pacman -S --noconfirm neovim git curl wget gcc make cmake \ nodejs npm python-pip ripgrep fd \ go clang dotnet-sdk-8.0 unzip fontconfig \ lldb gdb fish mariadb-clients ;; esac # Neovide install, if chosen above. Best-effort, same philosophy as # the .NET SDK install below: a failure here is a WARNING, not a # reason to stop the rest of the setup. if [ "$NVIM_FRONTEND" = "desktop" ] || [ "$NVIM_FRONTEND" = "both" ]; then echo "Installing Neovide (desktop Neovim GUI)..." case "$DISTRO_FAMILY" in arch) # Packaged directly upstream -- see # https://neovide.dev/installation.html#arch-linux sudo pacman -S --noconfirm neovide libxkbcommon-x11 || \ echo "WARNING: Neovide install failed -- see https://neovide.dev/installation.html" ;; fedora|debian) # Neither Fedora nor Debian/Ubuntu package Neovide -- # upstream's own install docs point at building it from # source via cargo instead. This pulls in a Rust # toolchain (via rustup, only if cargo isn't already on # PATH) and compiles Neovide and its dependency tree, # which can take several minutes. echo "Neovide has no Fedora/Debian/Ubuntu package -- building it from source" echo "(via cargo -- this can take several minutes)..." ( set -e case "$DISTRO_FAMILY" in fedora) sudo dnf install -y clang ninja-build fontconfig-devel freetype-devel \ @development-tools libstdc++-static libstdc++-devel ;; debian) sudo apt-get install -y curl gnupg ca-certificates git clang ninja-build \ gcc-multilib g++-multilib cmake libssl-dev pkg-config libfreetype6-dev \ libasound2-dev libexpat1-dev libxcb-composite0-dev libbz2-dev libsndio-dev \ freeglut3-dev libxmu-dev libxi-dev libfontconfig1-dev libxcursor-dev ;; esac if ! command -v cargo >/dev/null 2>&1; then curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y fi # shellcheck disable=SC1091 . "$HOME/.cargo/env" cargo install --git https://github.com/neovide/neovide --locked ) || echo "WARNING: Neovide build failed -- see https://neovide.dev/installation.html#linux-source for manual steps" ;; esac fi # This config relies on fairly recent Neovim APIs (vim.lsp.config/ # enable, virtual_lines diagnostics, vim.o.winborder -- all 0.11+). # Distro-packaged Neovim can lag well behind that, especially on # Debian stable and older Ubuntu LTS releases. Warn (don't fail) if # what actually got installed is too old. NVIM_VER_LINE=$(nvim --version 2>/dev/null | head -n1) NVIM_MAJOR=$(echo "$NVIM_VER_LINE" | sed -n 's/.*v\([0-9]*\)\.\([0-9]*\).*/\1/p') NVIM_MINOR=$(echo "$NVIM_VER_LINE" | sed -n 's/.*v\([0-9]*\)\.\([0-9]*\).*/\2/p') if [ -n "$NVIM_MAJOR" ] && [ -n "$NVIM_MINOR" ] && [ "$NVIM_MAJOR" -eq 0 ] && [ "$NVIM_MINOR" -lt 11 ]; then echo "WARNING: Installed Neovim is v${NVIM_MAJOR}.${NVIM_MINOR}, but this config needs 0.11+." echo " Debian stable and older Ubuntu LTS releases often ship an older" echo " version. See https://github.com/neovim/neovim/blob/master/INSTALL.md" echo " for official AppImage/PPA/prebuilt options if so." fi # Install global NPM packages for JS/TS, HTML, and CSS LSPs echo "Installing NPM-based LSPs..." sudo npm install -g typescript typescript-language-server vscode-langservers-extracted # 2. Install Nerd Fonts (JetBrainsMono and Cascadia Code) echo "[2/4] Installing JetBrainsMono and Cascadia Code Nerd Fonts..." FONT_DIR="$HOME/.local/share/fonts" mkdir -p "$FONT_DIR" # JetBrainsMono if [ ! -f "$FONT_DIR/JetBrainsMonoNerdFont-Regular.ttf" ]; then wget -q --show-progress https://github.com/ryanoasis/nerd-fonts/releases/latest/download/JetBrainsMono.zip -O /tmp/JetBrainsMono.zip unzip -q -o /tmp/JetBrainsMono.zip -d "$FONT_DIR" rm /tmp/JetBrainsMono.zip echo "JetBrainsMono Nerd Font downloaded." else echo "JetBrainsMono Nerd Font already installed." fi # Cascadia Code (CaskaydiaCove) if [ ! -f "$FONT_DIR/CaskaydiaCoveNerdFont-Regular.ttf" ]; then wget -q --show-progress https://github.com/ryanoasis/nerd-fonts/releases/latest/download/CascadiaCode.zip -O /tmp/CascadiaCode.zip unzip -q -o /tmp/CascadiaCode.zip -d "$FONT_DIR" rm /tmp/CascadiaCode.zip echo "Cascadia Code Nerd Font downloaded." else echo "Cascadia Code Nerd Font already installed." fi # Update font cache fc-cache -fv echo "Fonts installed and cache updated successfully!" # 3. Delete existing Neovim config echo "[3/4] Deleting existing Neovim configurations..." if [ -d "$HOME/.config/nvim" ]; then rm -fr "$HOME/.config/nvim" fi if [ -d "$HOME/.local/share/nvim" ]; then rm -fr "$HOME/.local/share/nvim" fi # 4. Install Neovim configuration (copied from config/nvim, not # embedded in this script -- edit those files directly to customize). echo "[4/4] Installing new Neovim configuration..." NVIM_DIR="$HOME/.config/nvim" mkdir -p "$NVIM_DIR" # Copy the whole config/nvim tree (the trailing "/." copies the directory's # contents, dotfiles included, into the existing target) rather than naming # files one by one. The config is split into many small modules -- one file # per plugin in lua/plugins/, helpers in lua/config/ -- and a new module # dropped anywhere under config/nvim/ gets installed without touching this # script. The target was wiped just above, so nothing stale can linger from # an older layout (such as the old monolithic lua/plugins/init.lua). cp -r "$CONFIG_DIR/nvim/." "$NVIM_DIR/" # --- ~/.clang-format, ~/.clang-tidy, and ~/cpp-style-guide.md --- # Personal fallback style, implementing a specific C/C++ coding style # guide (naming conventions, brace style, include ordering, etc. -- # see the comments in each file for exactly which guide section a # given setting maps to, and cpp-style-guide.md itself for the guide # those comments point into). Both tools walk UP from the file being # checked/formatted looking for their respective config file; $HOME # sits above every project you'll open, so these become your default # for any project that doesn't ship its own .clang-format/.clang-tidy # (a project's own file, being closer to the file, always takes # precedence over these). # # lf (LSP format) and lt (clang-tidy --fix) each only # consume .clang-format / .clang-tidy respectively. clangd's live # diagnostics pick up .clang-tidy automatically too (clang-tidy # integration is on by default in clangd). # # Copied unconditionally (not "if missing") every run, same as the # rest of this config -- so a stray pre-existing file from some other # tool can't silently block these from taking effect. cp "$CONFIG_DIR/clang-format" "$HOME/.clang-format" cp "$CONFIG_DIR/clang-tidy" "$HOME/.clang-tidy" cp "$CONFIG_DIR/cpp-style-guide.md" "$HOME/cpp-style-guide.md" echo "Wrote ~/.clang-format and ~/.clang-tidy (Allman braces, real tabs), and ~/cpp-style-guide.md." echo "=========================================" echo " Setup Complete!" echo " Run 'nvim'. p and P are restored, use p to force inline paste!" echo " Debugger: db to set a breakpoint, F5 / dc to start/continue," echo " du to toggle the debug UI. First debug session will auto-install codelldb via Mason." echo " Project-specific CMake debug targets loaded from ~/.config/nvim/lua/dap/configurations/cmake.lua" echo " Database (MariaDB): Du opens the DB UI; press A there to add a connection" echo " (URL form: mariadb://user:password@127.0.0.1:3306/dbname). The sqls language server is" echo " installed on first launch; see README.md ('Database (MariaDB)') for its optional config." if [ "$NVIM_FRONTEND" = "desktop" ] || [ "$NVIM_FRONTEND" = "both" ]; then echo " Neovide: run 'neovide' to launch the desktop app (same config, same keymaps)." echo " If the command isn't found yet, open a new shell first (rustup updates PATH" echo " in shell startup files, which this script's own shell won't have re-read)." fi echo "========================================="