Tim Kapitein

From Neovim to Cursor

A while ago, I experimented with Neovim - a highly customizable text editor loved by many developers. While it's an incredibly powerful tool that many use successfully, I found myself spending my evenings tweaking configurations and exploring new plugins. It turned into quite the time sink - fun to set up, but many features ended up unused in my daily workflow. Dealing with updates could be tricky, and I missed the more mature AI capabilities that VS Code offered.

So I decided to move back to VS Code, and eventually to Cursor. Everyone keeps talking about Cursor's Composer feature, but I'm kind of addicted to the multiline edit feature. It's just neat how it suggests changes to multiple lines while I'm coding and it's not getting in your way. The switch was pretty straightforward - I installed the Vim plugin to keep my familiar Vim movements, and since Cursor is built on VS Code, I got access to all my favorite extensions too.

I've been using this setup for a while now, and thought it might be fun to share how I've configured everything. Maybe you'll find some useful bits for your own setup. I took inspiration from Lazar Nikolov's VS Code vim setup and added my own twist to it.

Quick Disclaimer

Hey, just a quick heads up - this is just my personal setup that evolved over time. There's no "right way" to do this stuff, and what works for me might drive you nuts. Feel free to grab any bits you like and tweak them to match your style. The best setup is the one that doesn't get in your way while coding. Also, you could use this setup in VS Code as well.

Essential Settings

Let's start with the basic settings that make this setup work. These go in your settings.json file:

{
  "workbench.sideBar.location": "right", // Moves sidebar to the right
  "workbench.activityBar.orientation": "vertical", // Ensures sideways navigation to sidebar works properly
  "vim.smartRelativeLine": true // Smart line numbers for Vim navigation
}

Keybindings Setup

I've organized my keybindings into different categories to make them easier to understand and customize. You can add these to your keybindings.json file.

[
  // ===== Editor Navigation =====
  // Move cursor 10 lines up/down with Alt+k/j
  {
    "key": "alt+k",
    "command": "cursorMove",
    "args": {
      "to": "up",
      "by": "line",
      "value": 10
    },
    "when": "(vim.mode == 'Normal' || vim.mode == 'Visual') && (editorTextFocus || !inputFocus)"
  },
  {
    "key": "alt+j",
    "command": "cursorMove",
    "args": {
      "to": "down",
      "by": "line",
      "value": 10
    },
    "when": "(vim.mode == 'Normal' || vim.mode == 'Visual') && (editorTextFocus || !inputFocus)"
  },
 
  // =====  Navigation =====
  {
    "key": "ctrl+l",
    "command": "workbench.action.navigateRight"
  },
  {
    "key": "ctrl+h",
    "command": "workbench.action.navigateLeft"
  },
 
  // ===== Tab Management =====
  // Navigate between editor tabs with Ctrl+[/]
  {
    "key": "ctrl+]",
    "command": "workbench.action.nextEditorInGroup",
    "when": "(vim.mode == 'Normal' || vim.mode == 'Visual') && (editorTextFocus || !inputFocus)"
  },
  {
    "key": "ctrl+[",
    "command": "workbench.action.previousEditorInGroup",
    "when": "(vim.mode == 'Normal' || vim.mode == 'Visual') && (editorTextFocus || !inputFocus)"
  },
 
  // ===== Quick Actions =====
  // Show all open editors with Space+,
  {
    "key": "space ,",
    "command": "workbench.action.showAllEditors",
    "when": "vim.mode == 'Normal' && (editorTextFocus || !inputFocus)"
  },
 
  // ===== Line Operations =====
  // Move lines up/down in Visual Line mode
  {
    "key": "shift+j",
    "command": "editor.action.moveLinesDownAction",
    "when": "vim.mode == 'VisualLine' && editorTextFocus"
  },
  {
    "key": "shift+k",
    "command": "editor.action.moveLinesUpAction",
    "when": "vim.mode == 'VisualLine' && editorTextFocus"
  },
 
  // ===== Code Intelligence =====
  // Show hover information in Normal mode
  {
    "key": "shift+k",
    "command": "editor.action.showHover",
    "when": "vim.mode == 'Normal' && editorTextFocus"
  },
  // Code actions and refactoring shortcuts
  {
    "key": "space c a",
    "command": "editor.action.codeAction",
    "when": "vim.mode == 'Normal' && editorTextFocus"
  },
  {
    "key": "space c r",
    "command": "editor.action.rename",
    "when": "vim.mode == 'Normal' && editorTextFocus"
  },
  {
    "key": "space c s",
    "command": "workbench.action.gotoSymbol",
    "when": "vim.mode == 'Normal' && editorTextFocus"
  },
 
  // ===== Buffer/Window Management =====
  // Close current editor
  {
    "key": "space b d",
    "command": "workbench.action.closeActiveEditor",
    "when": "vim.mode == 'Normal' && editorTextFocus"
  },
  // Quick file opener
  {
    "key": "space space",
    "command": "workbench.action.quickOpen",
    "when": "vim.mode == 'Normal' && (editorTextFocus || !inputFocus)"
  },
 
  // ===== File Operations =====
  // Save file
  {
    "key": "ctrl+s",
    "command": "workbench.action.files.save",
    "when": "vim.mode == 'Normal' && (editorTextFocus || !inputFocus)"
  },
 
  // ===== Git Integration =====
  // Stage current file
  {
    "key": "ctrl+g",
    "command": "git.stage",
    "when": "editorTextFocus || focusedView === workbench.scm"
  },
  // Focus next SCM group
  {
    "key": "ctrl+t",
    "command": "workbench.scm.action.focusNextResourceGroup",
    "when": "editorTextFocus || focusedView === workbench.scm"
  },
 
  // ===== Search and Navigation =====
  // Add selection to next find match (multi-cursor)
  {
    "key": "ctrl+d",
    "command": "editor.action.addSelectionToNextFindMatch",
    "when": "(vim.mode == 'Normal' || vim.mode == 'Visual') && (editorTextFocus || !inputFocus)"
  },
  {
    "key": "space f a",
    "command": "editor.action.selectHighlights",
    "when": "(vim.mode == 'Normal' || vim.mode == 'Visual') && (editorTextFocus || !inputFocus)"
  },
  // Navigate between problems/errors
  {
    "key": "ctrl+n",
    "command": "editor.action.marker.next",
    "when": "editorTextFocus"
  },
  {
    "key": "ctrl+b",
    "command": "editor.action.marker.prev",
    "when": "editorTextFocus"
  },
 
  // ===== File Explorer Operations =====
  // Vim-like file operations in explorer
  {
    "key": "r",
    "command": "renameFile",
    "when": "filesExplorerFocus && foldersViewVisible && !explorerResourceIsRoot && !explorerResourceReadonly && !inputFocus"
  },
  {
    "key": "c",
    "command": "filesExplorer.copy",
    "when": "filesExplorerFocus && foldersViewVisible && !explorerResourceIsRoot && !explorerResourceReadonly && !inputFocus"
  },
  {
    "key": "p",
    "command": "filesExplorer.paste",
    "when": "filesExplorerFocus && foldersViewVisible && !explorerResourceIsRoot && !explorerResourceReadonly && !inputFocus"
  },
  {
    "key": "x",
    "command": "filesExplorer.cut",
    "when": "filesExplorerFocus && foldersViewVisible && !explorerResourceIsRoot && !explorerResourceReadonly && !inputFocus"
  },
  {
    "key": "d",
    "command": "deleteFile",
    "when": "filesExplorerFocus && foldersViewVisible && !explorerResourceIsRoot && !explorerResourceReadonly && !inputFocus"
  },
 
  // ===== File Creation =====
  // Create new file/folder
  {
    "key": "a",
    "command": "explorer.newFile",
    "when": "filesExplorerFocus && foldersViewVisible && !explorerResourceIsRoot && !explorerResourceReadonly && !inputFocus"
  },
  {
    "key": "shift+a",
    "command": "explorer.newFolder",
    "when": "filesExplorerFocus && foldersViewVisible && !explorerResourceIsRoot && !explorerResourceReadonly && !inputFocus"
  },
 
  // ===== Split Editor Management =====
  // Open file in split view
  {
    "key": "s",
    "command": "explorer.openToSide",
    "when": "filesExplorerFocus && foldersViewVisible && !explorerResourceIsRoot && !explorerResourceReadonly && !inputFocus"
  },
  // Open file in split view vertically
  // need to close the existing editor
  {
    "key": "shift+s",
    "command": "runCommands",
    "when": "filesExplorerFocus && foldersViewVisible && !explorerResourceIsRoot && !explorerResourceReadonly && !inputFocus",
    "args": {
      "commands": [
        "workbench.action.splitEditorDown",
        "explorer.openAndPassFocus",
        "workbench.action.closeOtherEditors"
      ]
    }
  },
 
  // ===== Explorer Navigation =====
  // Open file/folder with enter
  {
    "key": "enter",
    "command": "explorer.openAndPassFocus",
    "when": "filesExplorerFocus && foldersViewVisible && !explorerResourceIsRoot && !explorerResourceIsFolder && !inputFocus"
  },
  {
    "key": "enter",
    "command": "list.toggleExpand",
    "when": "filesExplorerFocus && foldersViewVisible && !explorerResourceIsRoot && explorerResourceIsFolder && !inputFocus"
  },
 
  // ===== Sidebar Resizing =====
  // Decrease sidebar width
  {
    "command": "runCommands",
    "key": "alt+;",
    "args": {
      "commands": [
        "workbench.action.focusSideBar",
        "workbench.action.decreaseViewSize"
      ]
    }
  },
  // Increase sidebar width
  {
    "command": "runCommands",
    "key": "alt+l",
    "args": {
      "commands": [
        "workbench.action.focusSideBar",
        "workbench.action.increaseViewSize"
      ]
    }
  }
]

It certainly does not cover everything that I used within Neovim, but it covers the most important ones for me. I still miss the Telescope plugin for Neovim, but for now I'm fine with just using the search bar in Cursor. I think this will not be the last iteration of this setup, but I'm pretty happy with it for now.