Skip to content

vimspector - A multi-language debugging system for Vim

License

Notifications You must be signed in to change notification settings

ousttrue/vimspector

 
 

Repository files navigation

vimspector - A multi language graphical debugger for Vim

Build Status

Status

The plugin is a capable Vim graphical debugger for multiple languages. It's mostly tested for c++ and python, but in theory supports any language that Visual Studio Code supports (but see caveats).

It supports:

  • breakpoints (function and line)
  • step in/out/over/up, stop, restart
  • launch and attach
  • locals and globals display
  • watches (expressions)
  • call stack and navigation
  • variable value display hover
  • interractive debug console
  • launch debugee within Vim's embedded terminal
  • logging/stdout display

The author successfully uses it for debugging Vim code and YouCompletMe's core engine ycmd (a complex python application).

It should work for any debug adapter that works in VSCode, but there are certain limitations (see FAQ). There are some bugs certainly, and configuring it is a bit of a dark art at this stage.

It is currently a work in progress, and any feedback/contributions are more than welcome.

If you are insanely curious and wish to try it out, it's probably best to shout me in the vimspector gitter channel. I'd love to hear from you.

In order to use it you have to currently:

  • Write an undocumented configuration file that contains essentially undocumented parameters.
  • Use an undocumented API via things like :call vimsepctor#Launch().
  • Accept that it isn't complete yet
  • etc.

Experimental

The plugin is currently experimental. That means that any part of it can (and probably will) change, including things like:

  • breaking changes to the configuration
  • keys, layout, functionatlity of the UI

If a large number of people start using it then I will do my best to minimise this, or at least announce on Gitter.

Background

The motivation is that debugging in Vim is a pretty horrible experience, particularly if you use multiple languages. With pyclewn no more and the built-in termdebug plugin limited to gdb, I wanted to explore options.

While Language Server Protocol is well known, the Debug Adapter Protocol is less well known, but achieves a similar goal: language agnostic API abstracting debuggers from clients.

The aim of this project is to provide a simple but effective debugging experience in Vim for multiple languages, by leveraging the debug adapters that are being built for Visual Studio Code.

The ability to do remote debugging is a must. This is key to my workflow, so baking it in to the debugging experience is a top bill goal for the project.

Demo

Please note the entire UI is placeholder. These are just proofs-of-concept.

C Debugging

C demo

Python Debugging

demo-python

Features and Usage

Mappings

By default, vimspector does not change any of your mappings. Mappings are very personal and so you should work out what you like and use vim's powerful mapping features to set your own mappings. For example, if you want <F5> to start/continue debugging, add this to some appropriate place, such as your vimrc (hint: run :e $MYVIMRC).

nnoremap <F5> :call vimspector#Continue()<CR>

That said, many people are familiar with particular debuggers, so the following mappings can be enabled by setting g:vimspector_enable_mappings to the specified value.

Visual Studio / VSCode

To use Visual Studio-like mappings, add the following to your vimrc before loading vimspector:

let g:vimspector_enable_mappings = 'VISUAL_STUDIO'
Key Function API
F5 When debugging, continue. Otherwise start debugging. vimspector#Continue()
Shift F5 Stop debugging. vimspector#Stop()
Ctrl Shift F5 Restart debugging with the same configuration. vimspector#Restart()
F6 Pause debugee. vimspector#Pause()
F9 Toggle line breakpoint on the current line. vimspector#ToggleBreakpoint()
Shift F9 Add a function breakpoint for the expression under cursor vimspector#AddFunctionBreakpoint( '<cexpr>' )
F10 Step Over vimspector#StepOver()
F11 Step Into vimspector#StepInto()
Shift F11 Step out of current function scope vimspector#StepOut()

Human Mode

If, like me, you only have 2 hands and 10 fingers, you probably don't like Ctrl-Shift-F keys. Also, if you're running in a terminal, there's a real possibility of terminfo being wrong for shifted-F-keys, particularly if your TERM is screen-256color. If these issues (number of hands, TERM variables) are unfixable, try the following mappings:

let g:vimspector_enable_mappings = 'HUMAN'
Key Function API
F5 When debugging, continue. Otherwise start debugging. vimspector#Continue()
F3 Stop debugging. vimspector#Stop()
F4 Restart debugging with the same configuration. vimspector#Restart()
F6 Pause debugee. vimspector#Pause()
F9 Toggle line breakpoint on the current line. vimspector#ToggleBreakpoint()
F8 Add a function breakpoint for the expression under cursor vimspector#AddFunctionBreakpoint( '<cexpr>' )
F10 Step Over vimspector#StepOver()
F11 Step Into vimspector#StepInto()
F12 Step out of current function scope vimspector#StepOut()

Launch and attach by PID:

  • Create vimspector.json. See below.
  • :call vimsepctor#Launch() and select a configuration.

Breakpoints

  • Use vimspector#ToggleBreakpoint() to set/disable/delete a line breakpoint.
  • Use vimspector#AddFunctionBreakpoint( '<name>' ) to add a function breakpoint.

Stepping

  • Step in/out, finish, continue, pause etc. using the WinBar.
  • If you really want to, the API is vimspector#StepInto() etc.

Variables and scopes

  • Current scope shows values of locals.
  • Use <CR> to expand/collapse (+, -).
  • When changing the stack frame the locals window updates.
  • While paused, hover to see values

Watches

The watches window is a prompt buffer. Enter insert mode to add a new watch expression.

  • Add watches to the variables window by entering insert mode and typing the expression. Commit with <CR>.
  • Expand result with <CR>.
  • Delete with <DEL>.

Stack Traces

  • In the threads window, use <CR> to expand/collapse.
  • Use <CR> on a stack frame to jump to it.

Program Output:

  • In the outputs window use the WinBar to select the output channel.
  • The debugee prints to the stdout channel.
  • Other channels may be useful for debugging.

Console

The console window is a prompt buffer and can be used as an interactive CLI for the debug adapter. Support for this varies amongt adapters.

  • Enter insert mode to enter a command to evaluate
  • Commit the request with <CR>
  • The request and subsequent result are printed.

NOTE: See also [Watches][#watches] above.

Supported Languages

Current tested with the following debug adapters.

Note, there is no support for installing the extension. Use VSCode to do that by installing it in the UI. The default extension directory is something like $HOME/.vscode/extensions.

Note, the launch configurations below are reverse-engineered from the extensions. Typically they are documented in the extension's package.json, but not always (or not completely).

{
  "adapters": {
    "cppdbg": {
      "name": "cppdbg",
      "command": [ "<path to extension>/debugAdapters/OpenDebugAD7" ],
      "attach": {
        "pidProperty": "processId",
        "pidSelect": "ask"
      }
    },
    ....
  },
  "configurations": {
    "<name>: Launch": {
      "adapter": "cppdbg",
      "configuration": {
        "name": "<name>",
        "type": "cppdbg",
        "request": "launch",
        "program": "<path to binary>",
        "args": [ ... ],
        "cwd": "<working directory>",
        "environment": [ ... ],
        "externalConsole": true,
        "MIMode": "lldb"
      }
    },
    "<name>: Attach": {
      "adapter": "cppdbg",
      "configuration": {
        "name": "<name>: Attach",
        "type": "cppdbg",
        "request": "attach",
        "program": "<path to binary>",
        "MIMode": "lldb"
      }
    }
    ...
  }
}
{
  "adapters": {
    "lldb-mi": {
      "name": "lldb-mi",
      "command": [
        "node",
        "<path to extension>/out/src/lldb.js"
      ],
      "attach": {
        "pidProperty": "target",
        "pidSelect": "ask"
      }
    }
    ...
  },
  "configurations": {
    "<name>: Launch": {
      "adapter": "lldb-mi",
      "configuration": {
        "request": "attach",
        "cwd": "<working directory>",
        "program": "<path to binary>",
        "args": [ ... ],
        "environment": [ ... ],
        "lldbmipath": "<path to a working lldb-mi>"
      }
    },
    "<name>: Attach": {
      "adapter": "lldb-mi",
      "configuration": {
        "request": "attach",
        "cwd": "<working directory>",
        "executable": "<path to binary>",
        "lldbmipath": "<path to a working lldb-mi>"
      }
    }
    ...
  }
}

{
  "adapters": {
    "lldb": {
      "name": "lldb",
      "command": [
        "lldb",
        "-b",
        "-O",
        "command script import '<extension path>/adapter'",
        "-O",
        "script adapter.main.run_stdio_session()"
      ]
    }
    ...
  },
  "configurations": {
    "<name>: Launch": {
      "adapter": "lldb",
      "configuration": {
        "type": "lldb",
        "request": "launch",
        "name": "<name>: Launch",
        "program": "<path to binary>",
        "args": [ .. ],
        "cwd": "<working directory>"
      }
    }
  }
}
{
  "adapters": {
    "python": {
      "name": "python",
      "command": [
        "node",
        "<path to extension>/out/client/debugger/debugAdapter/main.js"
      ]
    }
    ...
  },
  "configurations": {
    "<name>: Launch": {
      "adapter": "python",
      "configuration": {
        "name": "<name>: Launch",
        "type": "python",
        "request": "launch",
        "cwd": "<working directory>",
        "stopOnEntry": true,
        "console": "externalTerminal",
        "debugOptions": [],
        "program": "<path to main python file>"
      }
    }
    ...
  }
}
  • TCL (TclProDebug)

See my fork of TclProDebug for instructions.

Also the mock debugger, but that isn't actually useful.

Unsupported

Known not to work:

  • Java Debug Server. The java debug server runs as a jdt.ls plugin, rather than a standalone debug adapter. This makes a lot of sense if you already happen to be running the language server. Vimspector is not in the business of running language servers. So, rather than doing so, vimspector simply allows you to start the java debug server manually (however you might do so) and you can tell vimspector the port on which it is listening. See this issue for more background.
  • C-sharp. The license appears to require that it is only used with Visual Studio Code.

Supported Platforms

Currently on the author's environment which is macOS.

The plugin might work on other UNIX-like environments but it hasn't been tested. It will almost certainly not work on Windows.

Requires:

  • Vim 8.1 compiled with python 3 support.

Note the plugin uses a lot of very new Vim features (like prompt buffers), so I would strongly recommend a very new build of Vim.

FAQ

  1. Q: Does it work? A: Yeah, sort of. It's incredibly buggy and unpolished.
  2. Q: Does it work with ? A: Probably, but it won't necessarily be easy to work out what to put in the .vimspector.json. As you can see above, some of the servers aren't really editor agnostic, and require very-specific unique handling.

License

Apache 2.0

Copyright © 2018 Ben Jackson

About

vimspector - A multi-language debugging system for Vim

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Python 76.7%
  • Vim Script 20.8%
  • Other 2.5%