Skip to content

Instantly share code, notes, and snippets.

@JayDoubleu
Last active July 12, 2024 22:44
Show Gist options
  • Save JayDoubleu/6a87607fa38e6f954f8426f743ae10bf to your computer and use it in GitHub Desktop.
Save JayDoubleu/6a87607fa38e6f954f8426f743ae10bf to your computer and use it in GitHub Desktop.
#!/bin/bash
set -e
echo "Setting up Neovim with a modern configuration..."
# Detect the operating system
if [ -f /etc/os-release ]; then
. /etc/os-release
OS=$NAME
elif type lsb_release >/dev/null 2>&1; then
OS=$(lsb_release -si)
elif [ -f /etc/lsb-release ]; then
. /etc/lsb-release
OS=$DISTRIB_ID
else
OS=$(uname -s)
fi
# Function to install packages
install_packages() {
if [[ "$OS" == "Ubuntu" ]]; then
sudo apt update
sudo apt install -y "$@"
elif [[ "$OS" == "Fedora"* ]]; then
sudo dnf install -y "$@"
else
echo "Unsupported operating system: $OS"
exit 1
fi
}
# Update system
echo "Updating system..."
if [[ "$OS" == "Ubuntu" ]]; then
sudo apt update && sudo apt upgrade -y
elif [[ "$OS" == "Fedora"* ]]; then
sudo dnf upgrade -y
fi
# Install dependencies
echo "Installing dependencies..."
if [[ "$OS" == "Ubuntu" ]]; then
install_packages git curl unzip nodejs npm ripgrep fd-find wget build-essential software-properties-common fontconfig
elif [[ "$OS" == "Fedora"* ]]; then
install_packages git curl unzip nodejs npm ripgrep fd-find wget gcc gcc-c++ make fontconfig
fi
# Install latest Neovim
echo "Installing latest Neovim..."
if [[ "$OS" == "Ubuntu" ]]; then
sudo add-apt-repository ppa:neovim-ppa/unstable -y
sudo apt update
sudo apt install neovim -y
elif [[ "$OS" == "Fedora"* ]]; then
sudo dnf install -y neovim python3-neovim
fi
# Check Neovim version
NVIM_VERSION=$(nvim --version | head -n 1 | cut -d ' ' -f 2)
echo "Installed Neovim version: $NVIM_VERSION"
if [ "$(printf '%s\n' "0.8.0" "$NVIM_VERSION" | sort -V | head -n1)" = "0.8.0" ]; then
echo "Neovim version is 0.8.0 or higher, proceeding with setup."
else
echo "Error: Neovim version is lower than 0.8.0. Please check your installation."
exit 1
fi
# Create Neovim config directory
echo "Creating Neovim config directory..."
mkdir -p ~/.config/nvim
# Install Nerd Font (only if not already installed)
FONT_PATH="$HOME/.local/share/fonts/Droid Sans Mono Nerd Font Complete Mono.otf"
if [ ! -f "$FONT_PATH" ]; then
echo "Installing Nerd Font..."
mkdir -p ~/.local/share/fonts
cd ~/.local/share/fonts
wget https://github.com/ryanoasis/nerd-fonts/releases/download/v2.3.3/DroidSansMono.zip
unzip -o DroidSansMono.zip
rm DroidSansMono.zip
fc-cache -f
else
echo "Nerd Font already installed, skipping..."
fi
# Install or update Lazy.nvim
echo "Installing or updating Lazy.nvim..."
LAZY_PATH="$HOME/.local/share/nvim/lazy/lazy.nvim"
if [ -d "$LAZY_PATH" ]; then
echo "Lazy.nvim directory exists. Checking its state..."
if [ -d "$LAZY_PATH/.git" ]; then
echo "Updating Lazy.nvim..."
git -C "$LAZY_PATH" fetch --tags
git -C "$LAZY_PATH" checkout stable
else
echo "Lazy.nvim directory is not a git repository. Removing and reinstalling..."
rm -rf "$LAZY_PATH"
git clone --filter=blob:none https://github.com/folke/lazy.nvim.git --branch=stable "$LAZY_PATH"
fi
else
echo "Installing Lazy.nvim..."
git clone --filter=blob:none https://github.com/folke/lazy.nvim.git --branch=stable "$LAZY_PATH"
fi
# Create init.lua
echo "Creating init.lua..."
cat > ~/.config/nvim/init.lua << EOL
-- Bootstrap lazy.nvim
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
vim.fn.system({
"git",
"clone",
"--filter=blob:none",
"https://github.com/folke/lazy.nvim.git",
"--branch=stable",
lazypath,
})
end
vim.opt.rtp:prepend(lazypath)
-- Set leader key
vim.g.mapleader = " "
-- Plugin specification
require("lazy").setup({
-- Colorscheme
{ "chriskempson/base16-vim" },
-- LSP
{
"neovim/nvim-lspconfig",
dependencies = {
"williamboman/mason.nvim",
"williamboman/mason-lspconfig.nvim",
},
},
-- Completion
{
"hrsh7th/nvim-cmp",
dependencies = {
"hrsh7th/cmp-nvim-lsp",
"L3MON4D3/LuaSnip",
"saadparwaiz1/cmp_luasnip",
},
},
-- Treesitter
{
"nvim-treesitter/nvim-treesitter",
build = ":TSUpdate",
config = function()
require("nvim-treesitter.configs").setup({
ensure_installed = { "lua", "vim", "vimdoc", "python", "terraform", "hcl", "bash", "json", "yaml", "markdown", "git_rebase", "gitcommit" },
auto_install = true,
highlight = { enable = true },
})
end,
},
-- Telescope
{
"nvim-telescope/telescope.nvim",
dependencies = { "nvim-lua/plenary.nvim" },
},
-- File Explorer
"nvim-tree/nvim-tree.lua",
-- Status Line
"nvim-lualine/lualine.nvim",
-- Terminal
"akinsho/toggleterm.nvim",
-- Git
"lewis6991/gitsigns.nvim",
-- Formatting
{
"stevearc/conform.nvim",
opts = {},
},
-- Terraform
"hashivim/vim-terraform",
-- PowerShell
"pprovost/vim-ps1",
})
EOL
cat >> ~/.config/nvim/init.lua << EOL
-- Basic settings
vim.opt.number = true
vim.opt.relativenumber = true
vim.opt.expandtab = true
vim.opt.shiftwidth = 2
vim.opt.tabstop = 2
vim.opt.smartindent = true
vim.opt.termguicolors = true
vim.opt.clipboard = "unnamedplus" -- Enable system clipboard
vim.opt.fileformats = "unix,dos" -- Automatically detect and use the correct line endings
-- Function to toggle paste mode
function toggle_paste_mode()
if vim.o.paste then
vim.o.paste = false
print("Paste mode: OFF")
else
vim.o.paste = true
print("Paste mode: ON")
end
end
-- Key mapping for F2 (Toggle paste mode)
vim.api.nvim_set_keymap('n', '<F2>', ':lua toggle_paste_mode()<CR>', {noremap = true, silent = true})
-- Function to remove ^M characters
local function remove_ctrl_m()
local view = vim.fn.winsaveview()
vim.cmd([[%s/\r$//e]])
vim.fn.winrestview(view)
end
-- Autocommand to remove ^M characters on file save
vim.api.nvim_create_autocmd("BufWritePre", {
pattern = "*",
callback = remove_ctrl_m
})
-- Function to strip trailing whitespace
local function strip_trailing_whitespace()
local view = vim.fn.winsaveview()
vim.cmd([[%s/\s\+$//e]])
vim.fn.winrestview(view)
end
-- Key mapping for F4 (Strip whitespace)
vim.api.nvim_set_keymap('n', '<F4>', ':lua strip_trailing_whitespace()<CR>', {noremap = true, silent = true})
-- Color scheme (wrapped in pcall to avoid errors on first run)
pcall(function()
vim.cmd[[colorscheme base16-gruvbox-dark-hard]]
end)
-- Setup conform.nvim for formatting
require("conform").setup({
formatters_by_ft = {
lua = { "stylua" },
python = { "isort", "black" },
terraform = { "terraform_fmt" },
json = { "jq" },
sh = { "shfmt" },
bash = { "shfmt" },
yaml = { "yamlfix" },
markdown = { "prettier" },
powershell = { "psscriptanalyzer" },
},
format_on_save = false, -- We'll use F5 instead of format on save
})
-- Key mapping for F5 (Autoformat)
vim.api.nvim_set_keymap('n', '<F5>', ':lua require("conform").format()<CR>', {noremap = true, silent = true})
EOL
cat >> ~/.config/nvim/init.lua << EOL
-- LSP and completion setup (wrapped in pcall to avoid errors if plugins are not yet installed)
pcall(function()
-- LSP setup
require("mason").setup()
require("mason-lspconfig").setup()
local lspconfig = require('lspconfig')
local capabilities = require('cmp_nvim_lsp').default_capabilities()
-- Setup language servers
local servers = {
'pyright', -- Python
'terraformls', -- Terraform
'powershell_es', -- PowerShell
'bashls', -- Bash (for Azure CLI scripts)
'jsonls', -- JSON (for JQ and Databricks notebooks)
'yamlls', -- YAML
}
for _, lsp in ipairs(servers) do
lspconfig[lsp].setup {
capabilities = capabilities,
}
end
-- Completion setup
local cmp = require('cmp')
cmp.setup({
snippet = {
expand = function(args)
require('luasnip').lsp_expand(args.body)
end,
},
mapping = cmp.mapping.preset.insert({
['<C-b>'] = cmp.mapping.scroll_docs(-4),
['<C-f>'] = cmp.mapping.scroll_docs(4),
['<C-Space>'] = cmp.mapping.complete(),
['<C-e>'] = cmp.mapping.abort(),
['<CR>'] = cmp.mapping.confirm({ select = true }),
}),
sources = cmp.config.sources({
{ name = 'nvim_lsp' },
{ name = 'luasnip' },
}, {
{ name = 'buffer' },
})
})
-- Key mappings
local opts = { noremap=true, silent=true }
vim.keymap.set('n', '<space>e', vim.diagnostic.open_float, opts)
vim.keymap.set('n', '[d', vim.diagnostic.goto_prev, opts)
vim.keymap.set('n', ']d', vim.diagnostic.goto_next, opts)
vim.keymap.set('n', '<space>q', vim.diagnostic.setloclist, opts)
-- Use LspAttach autocommand to only map the following keys
-- after the language server attaches to the current buffer
vim.api.nvim_create_autocmd('LspAttach', {
group = vim.api.nvim_create_augroup('UserLspConfig', {}),
callback = function(ev)
local bufopts = { noremap=true, silent=true, buffer=ev.buf }
vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, bufopts)
vim.keymap.set('n', 'gd', vim.lsp.buf.definition, bufopts)
vim.keymap.set('n', 'K', vim.lsp.buf.hover, bufopts)
vim.keymap.set('n', 'gi', vim.lsp.buf.implementation, bufopts)
vim.keymap.set('n', '<C-k>', vim.lsp.buf.signature_help, bufopts)
vim.keymap.set('n', '<space>wa', vim.lsp.buf.add_workspace_folder, bufopts)
vim.keymap.set('n', '<space>wr', vim.lsp.buf.remove_workspace_folder, bufopts)
vim.keymap.set('n', '<space>wl', function()
print(vim.inspect(vim.lsp.buf.list_workspace_folders()))
end, bufopts)
vim.keymap.set('n', '<space>D', vim.lsp.buf.type_definition, bufopts)
vim.keymap.set('n', '<space>rn', vim.lsp.buf.rename, bufopts)
vim.keymap.set('n', '<space>ca', vim.lsp.buf.code_action, bufopts)
vim.keymap.set('n', 'gr', vim.lsp.buf.references, bufopts)
vim.keymap.set('n', '<space>f', function() vim.lsp.buf.format { async = true } end, bufopts)
end,
})
end)
-- Filetype-specific settings
vim.api.nvim_create_autocmd({"BufNewFile", "BufRead"}, {
pattern = "*.bicepparam",
command = "set filetype=bicep",
})
vim.api.nvim_create_autocmd({"BufNewFile", "BufRead"}, {
pattern = "*.tf,*.tfvars",
command = "set filetype=terraform",
})
vim.api.nvim_create_autocmd({"BufNewFile", "BufRead"}, {
pattern = "*.az",
command = "set filetype=sh",
})
vim.api.nvim_create_autocmd({"BufNewFile", "BufRead"}, {
pattern = "*.databricks",
command = "set filetype=json",
})
print("Neovim configuration loaded successfully!")
EOL
# Install language servers and tools
echo "Installing language servers and tools..."
sudo npm install -g pyright typescript typescript-language-server
# Install Rust and rust-analyzer
if ! command -v rustc &> /dev/null; then
echo "Installing Rust..."
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source $HOME/.cargo/env
rustup component add rust-analyzer
else
echo "Rust is already installed, skipping..."
fi
# Function to install Mason packages
install_mason_package() {
nvim --headless -c "MasonInstall $1" -c qall || echo "Failed to install $1"
}
# Install plugins and language servers
echo "Installing plugins and language servers..."
nvim --headless "+Lazy! sync" +qa
echo "Installing Mason packages..."
install_mason_package "pyright"
install_mason_package "terraform-ls"
install_mason_package "powershell-editor-services"
install_mason_package "bash-language-server"
install_mason_package "json-lsp"
install_mason_package "yaml-language-server"
# Final message
echo "Setup complete! You can now start Neovim by running 'nvim'."
echo "All plugins and language servers have been installed automatically."
echo "You may need to restart your terminal or log out and back in for font changes to take effect."
echo "Enjoy your new Neovim setup!"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment