combined

Warning
Use this information at your own risk.
TL;DR
Easier way: Sideload nvim-win64.zip to your work machine by uploading and then downloading from your Github (or Gitlab or BitBucket) account. Use vscode-neovim to embed a real Neovim instance inside Visual Studio Code (vscode). This is the approach I recommend.

The Good:

  • Out of the box LSP support from vscode
  • vscode plugins are available

leap.nvim works in vscode-neovim:

vscode-neovim-leap

The Bad:

  • No treesitter (e.g., text objects and incremental selection)
  • Any plugins requiring Neovim UI like telescope, legendary, harpoon and portal won’t work

Harder way: use neovide as our Neovim client. Compile treesitter parsers and telescope-fzf-native.nvim on an unrestricted Windows machine and then copy them to your work machine. LSP configuration is easier thanks to mason.nvim. I don’t recommend this approach unless you want the full Neovim experience.

The Good:

  • Feels like home

The Bad:

  • Neovide (below pic source) and neovim-qt would freeze randomly (probably due to Windows and company’s spyware and bloatware)
  • Time-consuming to set up right due to the machine severely restricted and some Windows specifics like paths

BasicScreenCap

Introduction

I recently got a software developer job at a corporate. Little did I know that I’d be assigned a no-admin-rights Windows machine. I won’t go about how ridiculous and suffocating it is to force a developer to write code on a restricted Windows machine without admin rights. This article aims to help my fellow Neovim users who are in my shoes. It can also serve as a guide to getting the most out of Neovim on Windows without using WSL2.

Pre-requisites

On your work machine, you need to be able to:

  • Download Github repo as zip or else there’s no point continue reading:

download-as-zip

Overview

The idea is that we use a private repo (let’s call it work repo) as a central hub where we sideload configs and exes into the work machine. On my personal Linux machine, I push everything I need to the work repo, then download it to my work machine.

  1. Create a private repo from your Github (or Gitlab or BitBucket) account.

  2. Add the following if you just want to use vscode-neovim:

If you want the full Neovim experience, also git add the following:

My Workflow

I use neovide to take notes with vimwiki for the full Neovim experience.

To write code, I use vscode-neovim. It offers the best of both worlds: out of box LSP support and most Neovim plugins work. Although we don’t have telescope, we can use vscode’s counterpart to make up for it (see below). It’s detrimental that Neovide or nvim-qt would hang randomly. vscode-neovim would hang sometimes too, but less and overall, I found vscode-neovim provides a better development experience.

I have a set of unified keybindings regardless whether I’m using vscode-neovim, neovide or nvim-qt.

init.lua:

nnoremap <silent> <C-g> <Cmd>call VSCodeCall('filesExplorer.findInFolder')<CR>
nnoremap <silent> <leader>fg <Cmd>call VSCodeCall('workbench.action.findInFiles')<CR>
nnoremap <silent> <leader>ff <Cmd>call VSCodeCall('actions.find')<CR>

legendary.lua:

{ "<C-g>", "<cmd>Telescope find_files<CR>", description = "Telescope: Find Files" },
{ "<leader>fg", "<cmd>Telescope live_grep<CR>", description = "Telescope: Find Text" },
{ "<leader>ff", "<cmd>Telescope current_buffer_fuzzy_find <CR>", description = "Telescope: Find Text in Current Buffer"}

Plugin Managers?

I’ve tested that both packer.nvim and vim-plug work on Windows 10 without requiring admin rights.

Note

Don’t be fooled by packer’s Requirements:

Quote
If you are on Windows 10, you need developer mode enabled in order to use local plugins (creating symbolic links requires admin privileges on Windows)

For more please see: Installation of local plugins on Windows requires running with Admin privileges #679

Neovim configuration on Windows 10 is an excellent guide for a general overview.

Since I can’t git clone anything, I don’t use any plugin manager; I just install them manually (more on this below).

Setting up Neovim with Your Favorite Plugins

  1. Unzip nvim-win64.zip into your work machine under C:\Users\username\Documents\config\nvim-win64\ (or whatever path you prefer and change it accordingly).

  2. Run C:\Users\username\Documents\config\nvim-win64\bin\nvim-qt.exe to verify that it works. Two paths C:\Users\username\AppData\Local\nvim and C:\Users\username\AppData\Local\nvim-data should be created (if not, just create them).

  3. Download plugin sources from Github as zips and put them under C:\Users\username\AppData\Local\nvim-data\site\pack\packer\start:

start
|   leap.nvim
|   flit.nvim
|   dressing.nvim
|   legendary.nvim
|   nvim-tree.lua
|   telescope.nvim
    ...

Directory Structures

C:\Users\username\AppData\Local\nvim:

nvim
|   ginit.vim
|   init.lua
|
+---lua
|   +---core
|   |       autocmds.lua
|   |       colors.lua
|   |       keymaps.lua
|   |       options.lua
|   |       workkeymaps.lua
|   |
|   +---lsp
|   |       cmp.lua
|   |       lspsaga.lua
|   |       lsp_config.lua
|   |       mason.lua
|   |       mason_lspconfig.lua
|   |
|   \---plugins
|           barbar.lua
|           diffview.lua
|           dressing.lua
|           grapple.lua
|           leap.lua
|           legendary.lua
|           lualine.lua
|           nvim-surround.lua
|           nvim-tree.lua
|           telescope.lua
|           treesitter.lua
|           ...

File Structures

This section explains the file structures under C:\Users\username\AppData\Local\nvim.

ginit.vim:

Guifont! JetBrainsMono Nerd Font:h8
GuiTabline 0
GuiPopupmenu 0
set mouse=

ginit.vim is for configuring nvim-qt. I don’t use nvim-qt but it’s nice to have as a backup.

init.lua:

Please see the inline comments.

Note: this init.lua contains config for vscode-neovim, nvim-qt and neovide, Remove unnecessary parts as you see fit.

-- core are loaded regardless of using `vscode-neovim`, `nvim-qt` or `neovide`
require("core.options")
require("core.keymaps")
require("core.autocmds")
require("core.colors")

-- plugins shared by `vscode-neovim`, `nvim-qt` and `neovide`
require("plugins.leap")
require("plugins.flit")
require("plugins.nvim-surround")
require("plugins.Comment")
require("plugins.illuminate")

vim.cmd[[
" `neovide` config
if exists("g:neovide")
    set guifont=JetBrainsMono\ Nerd\ Font:h10
    let g:neovide_scale_factor=1.0
    let g:neovide_cursor_animation_length=0
endif

" `vscode-neovim` config. See: https://github.com/vscode-neovim/vscode-neovim#invoking-vscode-actions-from-neovim
if exists('g:vscode')
    nnoremap <silent> <C-g> <Cmd>call VSCodeCall('filesExplorer.findInFolder')<CR>
    nnoremap <silent> <leader>ff <Cmd>call VSCodeCall('actions.find')<CR>
    nnoremap <silent> <leader>fg <Cmd>call VSCodeCall('workbench.action.findInFiles')<CR>
    nnoremap <silent> <C-s> <Cmd>call VSCodeCall('workbench.action.files.save')<CR>
endif
]]

if vim.g.vscode then
    -- VSCode extension
else
    -- below for `neovide` and `nvim-qt`
    -- LSP
    require("lsp.mason")
    require("lsp.mason_lspconfig")
    require("lsp.lsp_config")
    require("lsp.cmp")
    require("lsp.lspsaga")
    require("lsp.null_ls")

    -- telescope
    require("plugins.telescope")
    require("plugins.project")

    -- treesitter
    require("plugins.treesitter")
    require("plugins.treesitter-context")

    -- any plugins requiring `Neovim` UI won't work for `vscode-neovim`
    -- these plugins only work for `neovide` and `nvim-qt`
    -- so we don't load them for `vscode-neovim`
    require("plugins.dressing")
    require("plugins.legendary")
    require("plugins.lualine")
    require("plugins.nvim-tree")
    require("plugins.barbar")
    require("plugins.neoscroll")
    require("plugins.portal")
    ...
end

vscode-neovim Configuration

Go to your vscode’s settings.json and add the following:

"vscode-neovim.neovimExecutablePaths.win32": "C:\\Users\\username\\Documents\\config\\nvim-win64\\bin\\nvim.exe",
"vscode-neovim.neovimInitVimPaths.win32": "C:\\Users\\username\\AppData\\Local\\nvim\\init.lua"

If you want to escape to Normal mode with jk, go to vscode’s keybindings.json and add the following:

{
    "command": "vscode-neovim.compositeEscape1",
    "key": "j",
    "when": "neovim.mode == insert && editorTextFocus",
    "args": "j"
},
{
    "command": "vscode-neovim.compositeEscape2",
    "key": "k",
    "when": "neovim.mode == insert && editorTextFocus",
    "args": "k"
}

For more please see vscode-neovim.

Open vscode and see if Neovim inside vscode works (below showing leap.nvim works, which was just recently supported thanks to #804):

vscode-neovim-leap

If you’re satisfied with the experience, thank you for reading! If not, please continue for the full Neovim experience.

Full Neovim Experience: Neovide with Treesitter and LSP

Neovide Configuration

For all binary and executables such as neovide, ripgrep, fd, we need to add them to the user’s environment variables. To test it works, open cmd and type out neovide to see if it’s recognized.

If, for some reason, you can’t edit the user’s environment variables, try manually pointing neovide to neovim-bin like C:\Users\username\Documents\config\neovide.exe --neovim-bin C:\Users\username\Documents\config\nvim-win64\bin\nvim.exe. Reference: Neovim Binary

Treesitter Configuration

See Windows support. Sadly, pre-compiled parsers are disabled at the moment, so we’ll have to compile the parsers ourselves. I used llvm-clang. Feel free to pick any compilation method you see fit. The rub is that I can’t use chocolatey on my work machine. So on my Linux box, I have a Virtual Machine (VM) with Windows 10 installed to compile the parsers. Under the VM:

  1. Install chocolatey
  2. Open an admin powershell:
choco install git
choco install neovim
choco install llvm
  1. With the following treesitter.lua on our VM:
require("nvim-treesitter.install").compilers = { "clang" }
-- else I get error. See https://github.com/nvim-treesitter/nvim-treesitter/issues/3232
require("nvim-treesitter.install").prefer_git = true
require("nvim-treesitter.configs").setup({
  ensure_installed = "all",
  sync_install = true
})

Open Neovim and :TSUpdate all to install all parsers.

  1. The compiled parsers are located under C:\Users\usernameVM\AppData\Local\nvim-data\site\pack\packer\start\nvim-treesitter\parser. Zip them and push to your work repo. Unzip and sideload them to C:\Users\usernameWork\AppData\Local\nvim-data\site\pack\packer\start\nvim-treesitter\parser. (Reference: parser install directory)

  2. Set the following treesitter.lua on the work’s machine:

require("nvim-treesitter.configs").setup({
  highlight = {
  -- disable `treesitter` when using `vscode-neovim`
  -- see: [Invisible visual mode selections #609](https://github.com/vscode-neovim/vscode-neovim/issues/609)
    enable = (not vim.g.vscode),
  },
    })
  1. Go to any file that supports treesitter and verify if sweet sweet syntax highlighting is working:

treesitter-syntax-highlight

I tried directly copying ~/.local/share/nvim/site/pack/packer/start/nvim-treesitter/parser to C:\Users\username\AppData\Local\nvim-data\site\pack\packer\start\nvim-treesitter\parser. It doesn’t work because parsers are platform-specific. You need to compile them for the right platform.

Also, I’ve found that treesitter is vital to Neovim’s UI performance. Without treesitter installed, any popup window takes a long time to execute. Launching telescope takes literally two seconds and portal.nvim’s window five seconds. No exaggeration.

A lot of work just for treesitter parsers I know.

LSP Configuration

If LSP works in vscode, LSP in Neovim should also work, because afaik, they both connect to the same servers.

Again, I tried zipping the whole mason directory under ~/.local/share/nvim/mason to C:\Users\username\AppData\Local\nvim-data\mason. It doesn’t work because mason downloads platform-specific language servers. In other words, on Linux, you only see bash-language-server (executable binary), but on Windows, it’s bash-language-server.cmd.

I simply :Mason and install the language servers I need. Not everything in mason works, however:

lua-language-server
...acker\start\mason.nvim/lua/mason-core/installer/init.lua:121:
...start\mason.nvim/lua/mason-core/managers/github/init.lua:55:
Failed to fetch latest release from GitHub API.

Because mason tries to connect to GitHub API, which is blocked on my restricted corporate machine. We have to again, resort to downloading it from an unrestricted Windows machine and then copy it to our work machine, similar to sideloading treesitter parsers mentioned above.

Telescope Configuration

For the best experience, I recommend installing ripgrep, fd and telescope-fzf-native.nvim.

ripgrep and fd

  1. Download the Windows binary ripgrep and fd
  2. Add their paths to the user’s environment variables
  3. Modify as follows in telescope.lua:
require("telescope").setup({
  defaults = {
  -- need `rg.exe` instead of `rg`
    find_command = { "rg.exe", "--hidden", "--ignore-case" }
  },
})

telescope-fzf-native.nvim

  1. On a Windows machine: choco install cmake
  2. Compile telescope-fzf-native with use {'nvim-telescope/telescope-fzf-native.nvim', run = 'make' }
  3. Copy the whole telescope-fzf-native.nvim to your work machine
  4. Add the follows in telescope.lua:
require('telescope').load_extension('fzf')

telescope

Conclusion

The development experience on a free and unrestricted Linux machine is unparalleled. Being forced to go back to Windows and gasp — a no-admin-rights Windows machine nonetheless makes me appreciate free and open-source OS and Linux so much more.

Given LSP and treesitter support for Neovim was only available since 0.5 (July, 2021), I’m sure not a lot of developers trying to use Neovim in a corporate environment. Hopefully this guide is helpful if you’re in similar shoes. If you have other tricks and tips to enhance the Neovim experience on Windows, please don’t hesitate to leave a comment below or contact me at email kohane27 at protonmail dot com . I’d update this guide.

Thank you for reading!

:wq