Installing NeoVim on Manjaro

Note: If you want it to be considerably simpler, but still don’t want to miss the Vim or Neovim feeling, then you can also use this Rust editor: Zed - Your last next editor - https://zed.dev/

Manjaro’s official repositories usually offer a current version of Neovim.

With pacman

sudo pacman -Syu
sudo pacman -S neovim lua51

After installation, you can check the version with the following command:

nvim --version

Installing Rust

What Is Rustup - Toolchain

Advantage of Rustup

Rustup is the official toolchain manager for Rust. Without Rustup, you install Rust once via your distribution’s package management and are tied to that one version. Rustup additionally offers the following advantages:

Multiple Toolchains in Parallel

You can install the stable, beta, and nightly versions of Rust at the same time and switch between them:

rustup install nightly
rustup install beta
rustup default stable
rustup override set nightly   # only for the current project

This is particularly useful, since some crates (programs or libraries) only run on nightly, for example for experimental features.

Project-Specific Versions

With a rust-toolchain.toml file in the project folder, you define which Rust version the project uses:

[toolchain]
channel = "1.75.0"
components = ["rustfmt", "clippy"]

As soon as you switch into the folder, Rustup automatically activates the matching version. This ensures that all team members and CI systems use the same version.

Pinning to Exact Versions

You can specifically install a particular Rust version to ensure reproducible builds:

rustup install 1.75.0
rustup default 1.75.0

Installing Additional Components

Additional tools such as rustfmt, clippy, rust-analyzer, or rust-src can be added or removed at any time:

rustup component add clippy
rustup component remove rust-docs

Target Support

For cross-compilation, you can add targets for other platforms:

rustup target add aarch64-unknown-linux-gnu
rustup target add x86_64-pc-windows-gnu

Updates

Rustup updates the toolchains with one command:

rustup update

In contrast, with installation via pacman you depend on the distribution’s update cycles, which often lag months behind.

Advantage of a Toolchain

A toolchain is the actual complete Rust package and consists of several components:

  • rustc: the compiler
  • cargo: the build manager and package manager
  • rust-std: the standard library
  • rust-docs: the local documentation
  • optional components such as rustfmt and clippy

Why This Is Important

The advantage lies in the fact that all components of a toolchain are coordinated with one another. The compiler, the standard library, and the tools come from the same version. This avoids version conflicts such as can occur with manually assembled installations.

Channel Instead of Version

A toolchain is referenced via a channel (stable, beta, nightly) or via an exact version (1.75.0). This allows you to either always use the latest stable features or to rely on a fixed version when your project requires it.

Comparison: Rustup vs. Package Manager

AspectRustuppacman
VersionCurrentOften outdated
Multiple versionsYesNo
Channel switchingYesNo
Project-specific versionsYesNo
Installing additional componentsYesPartially
Cross-compilation targetsYesLimited
Update cycleIndependentDependent on distribution

When Is the Package Manager Enough?

If you only use Rust occasionally, do not need project-specific versions, and are satisfied with your distribution’s version, installation via pacman is sufficient. For professional development, however, Rustup is clearly the better choice.

Installing Rustup

Step 1: Install base-devel & Rustup from the repository

sudo pacman -S base-devel rustup

Step 2: Activate the toolchain

rustup default stable

Important: The rustup package from the Arch repositories does not install a toolchain by default. The binaries (rustc, cargo) are symbolic links to rustup, so the toolchain must be activated manually.

Step 3: Verify the installation

rustc --version
cargo --version

Development Tools

After installing Rustup, you can add additional components:

rustup component add rustfmt         # Code formatting
rustup component add clippy          # Static analysis
rustup component add rust-analyzer   # Language server for IDEs

Testing the Rust Installation

cargo new hello_world
cd hello_world
cargo run

NeoVim 4 Rust

To program Rust in Neovim, you essentially need Rust itself (done), the language server rust-analyzer, and a Neovim LSP configuration.

LSP - Language Server rust-analyzer

  1. rust-analyzer (the LSP server): This is the heart of code completion, error checking, and navigation. The best way is to add it as a component via rustup.
  2. rustup component add rust-analyzer
    

Because there are occasional problems with invoking rust-analyzer, the following adjustment should also be made.

If the invocation does not work

rust-analyzer --version

install explicitly and then call the above command again

sudo pacman -S rust-analyzer

All of this has to do with a broken symlink/proxy. Possibly more on that later. For now, it should just work!

Neovim Configuration

# Overview of the directories

~/.config/nvim/
├── init.lua                          # Entry point, loads config.lazy
└── lua/
    ├── config/
    │   └── lazy.lua                  # Bootstrap & setup for lazy.nvim
    └── plugins/
        ├── init.lua                  # Placeholder, prevents errors
        ├── rust.lua                  # rustaceanvim
        └── completion.lua            # blink.cmp
  1. Create the configuration directory
mkdir -p ~/.config/nvim
  1. Install the plugin manager lazy.vim

Before you begin, make sure that Neovim (>= 0.8.0) and Git (>= 2.19.0) are installed on your system.

nvim --version | head -n 1 && git --version

If Git is not yet installed:

sudo pacman -S git
  1. Create configuration files

First create the required folders and files in your Neovim configuration directory.

mkdir -p ~/.config/nvim/lua/config && mkdir -p ~/.config/nvim/lua/plugins
  1. Create or adjust init.lua

Create the file ~/.config/nvim/init.lua (if it does not yet exist) and add this line:

nvim ~/.config/nvim/init.lua
require("config.lazy")
  1. Create bootstrap file for lazy.nvim

So that lazy.nvim does not return an error message, an empty init.lua must be created in ~/.config/nvim/lua/plugins/.

echo "return {}" > ~/.config/nvim/lua/plugins/init.lua
  1. Create the file ~/.config/nvim/lua/config/lazy.lua
nvim ~/.config/nvim/lua/config/lazy.lua

with the following content. This code in lazy.lua automatically downloads the plugin lazy.nvim if it is not yet present:

-- ~/.config/nvim/lua/config/lazy.lua

-- Bootstrap lazy.nvim
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
    vim.api.nvim_echo({
      { "Failed to clone lazy.nvim:\n", "ErrorMsg" },
      { out, "WarningMsg" },
      { "\nPress any key to exit..." },
    }, true, {})
    vim.fn.getchar()
    os.exit(1)
  end
end
vim.opt.rtp:prepend(lazypath)

-- Leader keys must be set before lazy.nvim
vim.g.mapleader = " "
vim.g.maplocalleader = "\\"

-- lazy.nvim setup
require("lazy").setup({
  spec = {
    { import = "plugins" },
  },

  -- LuaRocks support with hererocks
  rocks = {
    enabled = true,
    hererocks = true,
  },

  -- Colorscheme when installing
  install = { colorscheme = { "habamax" } },

  -- Automatic update check
  checker = { enabled = true },

  -- Performance
  performance = {
    rtp = {
      disabled_plugins = {
        "gzip",
        "matchit",
        "matchparen",
        "netrwPlugin",
        "tarPlugin",
        "tohtml",
        "tutor",
        "zipPlugin",
      },
    },
  },
})
  1. Verify the installation

After you have saved the configuration, restart Neovim. The plugin lazy.nvim should now install itself automatically.

Open lazy.nvim: Enter the command :Lazy to open the graphical interface.

Once lazy.nvim is running, you can proceed to the next step: setting up rustaceanvim in your plugin configuration.

  1. Install the plugin rustaceanvim

There are two common ways to configure this. The simplest and most modern way is a special plugin for Rust.

This plugin is a kind of “all-inclusive package” for Rust in Neovim. It automatically connects to rust-analyzer and brings many useful functions without you having to configure the LSP server manually.

The rustaceanvim entry does not go into lazy.lua, but into a separate file in the plugins folder. The lazy.lua is only responsible for the bootstrap and global configuration of lazy.nvim. Your actual plugins belong in ~/.config/nvim/lua/plugins/.

Create a new file:

nvim ~/.config/nvim/lua/plugins/rust.lua

The file must return a table. The rustaceanvim entry is in this table:

-- ~/.config/nvim/lua/plugins/rust.lua
return {
  {
    "mrcjkb/rustaceanvim",
    version = "^9", -- Version 9 for current Neovim versions
    lazy = false,   -- Important: Loads the plugin immediately
  },
}
  1. Save the file.
  2. Restart Neovim.
  3. Enter :Lazy and possibly U to update
  4. lazy.nvim recognizes the new file and installs rustaceanvim automatically.
  5. Open a .rs file. rustaceanvim then starts rust-analyzer.

Plugin blink.cmp - autocompletion

Installation

Create the following file:

nvim ~/.config/nvim/lua/plugins/completion.lua

With the following content

return {
  {
    'saghen/blink.cmp',
    -- Optional: Provides snippets for snippet completion
    dependencies = { 'rafamadriz/friendly-snippets' },

    -- Uses a release tag to download prebuilt binaries.
    -- This is the recommended way for the best performance.
    version = '1.*',

    ---@module 'blink.cmp'
    ---@type blink.cmp.Config
    opts = {
      -- 'default' uses key bindings similar to those of the built-in
      -- completion (e.g., <C-y> to accept)
      keymap = { preset = 'default' },

      appearance = {
        -- 'mono' for 'Nerd Font Mono' to align icons correctly
        nerd_font_variant = 'mono'
      },

      -- By default, suggestions from LSP, file paths,
      -- snippets, and the current buffer are used.
      sources = {
        default = { 'lsp', 'path', 'snippets', 'buffer' },
      },

      -- Uses the Rust implementation for the fuzzy matcher (faster),
      -- but automatically falls back to the Lua implementation.
      fuzzy = { implementation = "prefer_rust_with_warning" }
    },
    opts_extend = { "sources.default" }
  }
}

Shortcuts

blink.cmp uses the default preset by default, which is based on the built-in Neovim completion. The most important key bindings in insert mode are:

  • <C-Space>: Open the menu or toggle documentation.
  • <C-y>: Accept the selected suggestion.
  • <C-e>: Close the menu (and undo the preview).
  • <C-n> / <C-p> (or arrow down/up): Select the next or previous entry.
  • <C-k>: Toggle signature help (if enabled).
  • <C-b> / <C-f>: Scroll in the documentation preview.
  • <Tab> / <S-Tab>: Jump between snippet placeholders.

If you want different behavior, you can simply choose a different preset in your blink.cmp configuration, e.g., keymap = { preset = 'super-tab' } for tab-to-accept.

In addition to the shortcuts I mentioned, blink.cmp has a few more that are available depending on the configuration and context (e.g., in the command line).

Presets

In blink.cmp, presets are pre-made keyboard layouts that determine how you operate the completion menu. They are, so to speak, “packages” of key bindings for specific ways of working.

The Most Important Presets at a Glance

You choose a preset via keymap = { preset = 'name' } in your configuration.

  • default: Based on the built-in Neovim completion. You confirm a suggestion with <C-y> (like “Yes”).
  • super-tab: Based on VS Code. The Tab key accepts the selected suggestion.
  • enter: Uses the Enter key (<CR>) to accept. The Tab key remains reserved for navigating between snippet placeholders.
  • cmdline: Is specially optimized for the command line (:). The Tab key shows the menu, inserts the first entry, or selects the next one.

Common Keys in All Presets

Regardless of the chosen preset, some keys are always assigned the same way, for example:

  • <C-space>: Open the menu or toggle documentation.
  • <C-n> / <C-p> (or arrow keys): Select the next or previous entry.
  • <C-e>: Close the menu.

The Most Important Presets Compared

blink.cmp comes with various predefined keyboard layouts, the so-called presets. Your currently used shortcuts (<C-Space>, <C-y>, <C-e>, etc.) belong to the default preset. Other presets change the assignment fundamentally:

Functiondefaultsuper-tabentercmdline
Accept<C-y><Tab> (smart)<CR> (Enter)<Tab>
Next<C-n>, <Down><C-n>, <Down><C-n>, <Down><Tab>, <C-n>
Previous<C-p>, <Up><C-p>, <Up><C-p>, <Up><S-Tab>, <C-p>
Snippet forward<Tab><Tab> (smart)<Tab>—

So if, for example, you want Tab to accept the suggestion (as in VS Code), you change keymap = { preset = 'super-tab' } in your configuration.

Other Useful Standard Shortcuts

Regardless of the preset, there are some commands that are found in most configurations:

  • <C-k>: Toggles signature help (if enabled).
  • <C-b> / <C-f>: Scrolls up/down in the documentation preview.
  • <C-l>: Shows the completion menu or the documentation.
  • <C-e>: Closes the menu (and undoes an automatic preview).

Shortcuts in the Command Line (cmdline)

When you type in the Neovim command line (:), different rules often apply. The cmdline preset is specially optimized for this:

  • <Tab>: Shows the menu, inserts the first element, or selects the next one.
  • <S-Tab>: Selects the previous element.
  • <Right> / <Left>: Navigates through the suggestions as in normal completion.

Defining Your Own Shortcuts

You can adjust the assignment at any time. If, for example, you want to use <CR> (Enter) to accept, you change the keymap entry in your blink.cmp configuration:

keymap = { preset = 'enter' },

Or you define completely custom combinations by setting preset = 'none' and assigning the keys yourself.

Example:

-- ~/.config/nvim/lua/plugins/completion.lua
return {
  {
    'saghen/blink.cmp',
    dependencies = { 'rafamadriz/friendly-snippets' },
    version = '1.*',
    opts = {
      keymap = { preset = 'enter' }, -- Here the preset is chosen
      -- ... remaining options such as appearance, sources, fuzzy
    },
  },
}

Installation Script for Neovim, Rust, and Plugins on Manjaro

For the impatient with a willingness to take risks: Here is a complete Bash script that automates all steps from your guide. It installs Neovim, Rust (via Rustup), the Rust components, and sets up the complete Neovim configuration with lazy.nvim, rustaceanvim, and blink.cmp.

#!/usr/bin/env bash
### ### install_neovim_rust.sh
### Installs Neovim, Rust (Rustup) and sets up the Neovim configuration
### with lazy.nvim, rustaceanvim, and blink.cmp on Manjaro.
### ### Author: hoergen (template), automated
### Date: 2026-09-22
### 
set -euo pipefail

### ------------------------------------------------------------------
### Colors for output
### ------------------------------------------------------------------
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m' # No Color

info()  { echo -e "${GREEN}[INFO]${NC}  $*"; }
warn()  { echo -e "${YELLOW}[WARN]${NC}  $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }

### ------------------------------------------------------------------
### 0. Check prerequisites
### ------------------------------------------------------------------
if [[ $EUID -eq 0 ]]; then
  error "Please do NOT run as root. The script uses sudo where necessary."
  exit 1
fi

if ! command -v pacman &>/dev/null; then
  error "pacman not found. This script is intended for Manjaro/Arch."
  exit 1
fi

### ------------------------------------------------------------------
### 1. Update system and install Neovim + Lua
### ------------------------------------------------------------------
info "Updating package database and system ..."
sudo pacman -Syu --noconfirm

info "Installing Neovim, Lua 5.1, git, base-devel, and rustup ..."
sudo pacman -S --noconfirm --needed \
  neovim \
  lua51 \
  git \
  base-devel \
  rustup

### ------------------------------------------------------------------
### 2. Activate Rust toolchain
### ------------------------------------------------------------------
info "Activating Rust stable toolchain ..."
if ! rustup toolchain list | grep -q '^stable'; then
  rustup default stable
else
  info "stable toolchain is already active."
fi

### ------------------------------------------------------------------
### 3. Install Rust components
### ------------------------------------------------------------------
info "Installing Rust components: rustfmt, clippy, rust-analyzer ..."
rustup component add rustfmt   || warn "rustfmt could not be installed."
rustup component add clippy    || warn "clippy could not be installed."
rustup component add rust-analyzer || warn "rust-analyzer (rustup) could not be installed."

### Fallback: rust-analyzer from the repos if the rustup call does not work.
if ! command -v rust-analyzer &>/dev/null; then
  warn "rust-analyzer not found in PATH. Installing from the repos ..."
  sudo pacman -S --noconfirm --needed rust-analyzer
fi

### ------------------------------------------------------------------
### 4. Check versions
### ------------------------------------------------------------------
info "Checking installations ..."
nvim --version | head -n 1
rustc --version
cargo --version
rust-analyzer --version 2>/dev/null || warn "rust-analyzer --version not available."

### ------------------------------------------------------------------
### 5. Create Neovim configuration directory
### ------------------------------------------------------------------
NVIM_CONFIG="$HOME/.config/nvim"
NVIM_LUA="$NVIM_CONFIG/lua"
NVIM_PLUGINS="$NVIM_LUA/plugins"

info "Creating Neovim configuration directories ..."
mkdir -p "$NVIM_CONFIG"
mkdir -p "$NVIM_LUA/config"
mkdir -p "$NVIM_PLUGINS"

### ------------------------------------------------------------------
### 6. Create init.lua
### ------------------------------------------------------------------
info "Creating $NVIM_CONFIG/init.lua ..."
cat > "$NVIM_CONFIG/init.lua" <<'EOF'
require("config.lazy")
EOF

### ------------------------------------------------------------------
### 7. Plugin init (empty so that lazy.nvim does not throw errors)
### ------------------------------------------------------------------
info "Creating $NVIM_PLUGINS/init.lua ..."
cat > "$NVIM_PLUGINS/init.lua" <<'EOF'
return {}
EOF

### ------------------------------------------------------------------
### 8. Create lazy.lua bootstrap file
### ------------------------------------------------------------------
info "Creating $NVIM_LUA/config/lazy.lua ..."
cat > "$NVIM_LUA/config/lazy.lua" <<'EOF'
-- ~/.config/nvim/lua/config/lazy.lua

-- Bootstrap lazy.nvim
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
    vim.api.nvim_echo({
      { "Failed to clone lazy.nvim:\n", "ErrorMsg" },
      { out, "WarningMsg" },
      { "\nPress any key to exit..." },
    }, true, {})
    vim.fn.getchar()
    os.exit(1)
  end
end
vim.opt.rtp:prepend(lazypath)

-- Leader keys must be set before lazy.nvim
vim.g.mapleader = " "
vim.g.maplocalleader = "\\"

-- lazy.nvim setup
require("lazy").setup({
  spec = {
    { import = "plugins" },
  },

  -- LuaRocks support with hererocks
  rocks = {
    enabled = true,
    hererocks = true,
  },

  -- Colorscheme when installing
  install = { colorscheme = { "habamax" } },

  -- Automatic update check
  checker = { enabled = true },

  -- Performance
  performance = {
    rtp = {
      disabled_plugins = {
        "gzip",
        "matchit",
        "matchparen",
        "netrwPlugin",
        "tarPlugin",
        "tohtml",
        "tutor",
        "zipPlugin",
      },
    },
  },
})
EOF

### ------------------------------------------------------------------
### 9. rustaceanvim plugin configuration
### ------------------------------------------------------------------
info "Creating $NVIM_PLUGINS/rust.lua ..."
cat > "$NVIM_PLUGINS/rust.lua" <<'EOF'
-- ~/.config/nvim/lua/plugins/rust.lua
return {
  {
    "mrcjkb/rustaceanvim",
    version = "^9", -- Version 9 for current Neovim versions
    lazy = false,   -- Important: Loads the plugin immediately
  },
}
EOF

### ------------------------------------------------------------------
### 10. blink.cmp plugin configuration
### ------------------------------------------------------------------
info "Creating $NVIM_PLUGINS/completion.lua ..."
cat > "$NVIM_PLUGINS/completion.lua" <<'EOF'
-- ~/.config/nvim/lua/plugins/completion.lua
return {
  {
    'saghen/blink.cmp',
    -- Optional: Provides snippets for snippet completion
    dependencies = { 'rafamadriz/friendly-snippets' },

    -- Uses a release tag to download prebuilt binaries.
    -- This is the recommended way for the best performance.
    version = '1.*',

    ---@module 'blink.cmp'
    ---@type blink.cmp.Config
    opts = {
      -- 'default' uses key bindings similar to those of the built-in
      -- completion (e.g., <C-y> to accept)
      keymap = { preset = 'default' },

      appearance = {
        -- 'mono' for 'Nerd Font Mono' to align icons correctly
        nerd_font_variant = 'mono'
      },

      -- By default, suggestions from LSP, file paths,
      -- snippets, and the current buffer are used.
      sources = {
        default = { 'lsp', 'path', 'snippets', 'buffer' },
      },

      -- Uses the Rust implementation for the fuzzy matcher (faster),
      -- but automatically falls back to the Lua implementation.
      fuzzy = { implementation = "prefer_rust_with_warning" }
    },
    opts_extend = { "sources.default" }
  }
}
EOF

### ------------------------------------------------------------------
### 11. Install plugins headless (lazy.nvim sync)
### ------------------------------------------------------------------
info "Installing plugins via lazy.nvim (headless) ..."
if nvim --headless "+Lazy! sync" +qa 2>/dev/null; then
  info "Plugins successfully synchronized."
else
  warn "Headless sync failed. Please start Neovim manually and run ':Lazy sync'."
fi

### ------------------------------------------------------------------
### 12. Done
### ------------------------------------------------------------------
info "Done! Summary:"
echo "  - Neovim version:       $(nvim --version | head -n 1)"
echo "  - Rust version:         $(rustc --version)"
echo "  - Cargo version:        $(cargo --version)"
echo "  - rust-analyzer:        $(rust-analyzer --version 2>/dev/null || echo 'not available')"
echo "  - Neovim config:        $NVIM_CONFIG"
echo
info "Start Neovim with 'nvim' and check with ':Lazy' whether all plugins are installed."
info "Open a .rs file to test rustaceanvim and rust-analyzer."

Usage

  1. Save the file as e.g., install_neovim_rust.sh.

  2. Make it executable:

    chmod +x install_neovim_rust.sh
    
  3. Start it (not as root):

    ./install_neovim_rust.sh
    

What the Script Does

StepAction
1System update via pacman -Syu
2Installation of neovim, lua51, git, base-devel, rustup
3Activation of the Rust stable toolchain
4Adding rustfmt, clippy, rust-analyzer
5Fallback: rust-analyzer from the repos if the rustup version is not in PATH
6Creating the directories ~/.config/nvim/lua/{config,plugins}
7Creating init.lua with require("config.lazy")
8Creating the empty plugins/init.lua
9Creating config/lazy.lua (bootstrap + setup)
10Creating plugins/rust.lua (rustaceanvim)
11Creating plugins/completion.lua (blink.cmp, preset default)
12Headless installation of the plugins via nvim --headless "+Lazy! sync" +qa

Notes

  • The script deliberately uses the default preset for blink.cmp. The custom shortcuts (e.g., super-tab or enter) are not included – you can adjust them later in ~/.config/nvim/lua/plugins/completion.lua.
  • rust-analyzer is first installed via rustup. If that fails or the command does not end up in PATH, the package from the Manjaro repos is automatically installed afterwards.
  • The plugin installation runs headless. If that fails, you can simply start Neovim normally and run :Lazy sync.