Skip to content

Additional Language Support

Ben Jackson edited this page Dec 29, 2023 · 6 revisions

Introduction

This page details how to make Vimspector work for languages not officially mentioned in the README.

How to use these files:

  1. If there is a gadget installer file, save it to <vimspector base dir>/gadgets/custom/cust_<Language>.json. Then run ./install_gadget.py --upgrade
  2. Merge or copy the vimspector.json snippet to your local .vimspector.json or custom configurations directory and modify as appropriate.

Ruby (rdbg)

Debugger: https://github.com/ruby/debug

Instalation: gem install debug (https://github.com/ruby/debug#installation)

Simple

A very simple way is to run rdbg manually and attach vimspector to it:

  • rdbg --open --port 54321 yourfile.rb, or
  • rdbg --open --port 54321 -c -- bundle exec yourthing, or
  • rdbg --open --port 54321 -c -- rails server

See https://github.com/ruby/debug#use-rdbg-with-commands-written-in-ruby

  • Use vimspector config:
{
  "$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json",
  "configurations": {
    "run": {
      "adapter": {
        "port": 54321
      },
      "configuration": {
        "request": "launch"
      }
    }
  }
}

Advanced

More advanced: get vimspector to run rdbg for you, using "remote" debugging, for example:

{
  "$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json",
  "adapters": {
    "rdbg-script": {
      "variables": {
        "port": "${unusedLocalPort}"
      },
      "launch": {
        "remote": {
          "runCommand": [
            "rdbg",
            "--open",
            "--port", "${port}",
            "--",
            "%CMD%"
          ]
        }
      },
      "port": "${port}"
    },
    "rdbg-command": {
      "variables": {
        "port": "${unusedLocalPort}"
      },
      "launch": {
        "remote": {
          "runCommand": [
            "rdbg",
            "--open",
            "--port", "${port}",
            "-c",
            "--",
            "%CMD%"
          ]
        }
      },
      "port": "${port}",
      "configuration": {
        "request": "launch"
      }
    }
  },
  "configurations": {
    "run current script": {
      "adapter": "rdbg-script",
      "remote-cmdLine": [
        "${file}"
      ],
      "configuration": {}

    },
    "run rails": {
      "adapter": "rdbg-command",
      "remote-cmdLine": [
        "rails",
        "server"
      ]
    }
  }
}

Ruby (legacy)

Gadget installer file

cust_vscode-ruby.json

{
  "cust_vscode-ruby":  {
    "download": {
      "url": "https://github.com/rubyide/vscode-ruby/releases/download/v${version}/${file_name}"
    },
    "all": {
      "version": "0.25.0",
      "file_name": "ruby-0.25.0.vsix",
      "checksum": "fc67efbdc7261e7e1ae53707ead9bda10ec19c498369ff8a3ccd2e7fb8125a68",
      "adapters": {
        "cust_vscode-ruby": {
          "command": [
            "node",
            "${gadgetDir}/cust_vscode-ruby/dist/debugger/main.js"
          ],
          "name": "vscode-ruby-debug",
          "configuration": {
            "cwd": "${workspaceRoot}",
            "showDebugOutput": false,
            "trace": false
          }
        }
      }
    }
  }
}

Vimspector config

{
  "configurations": {
    "launch current file": {
      "adapter": "cust_vscode-ruby",
      "configuration": {
        "request": "launch",
        "program": "${file}",
        "args": [ "*${args}" ]
      }
    }
  }
}

Details and Dependencies: https://github.com/rubyide/vscode-ruby/blob/master/docs/debugger.md

Puppet

Gadget installer file

cust_puppet-debugserver.json

{
  "cust_puppet-debugserver":  {
    "download": {
      "url": "https://github.com/puppetlabs/puppet-editor-services/releases/download/${version}/${file_name}"
    },
    "all": {
      "version": "1.0.1",
      "file_name": "puppet_editor_services_1.0.1.zip",
      "checksum": "15b33bf63062f226466191d4417368a411f6a14f53c67d4898ca488a8b22454b",
      "extension_path": "",
      "adapters": {
        "cust_puppet-debugserver": {
          "command": [
            "ruby",
            "${gadgetDir}/cust_puppet-debugserver/puppet-debugserver",
            "--port",
            "${unusedLocalPort}"
          ],
          "configuration": {
            "cwd": "${workspaceRoot}"
          },
          "name": "puppet-debugserver",
          "port": "${unusedLocalPort}"
        }
      }
    }
  }
}

Vimspector config

{
  "configurations": {
    "launch current file": {
      "adapter": "cust_puppet-debugserver",
      "configuration": {
        "request": "launch",
        "manifest": "${file}",
        "noop": true,
        "args": [ 
          "--modulepath",
          "/path/to/your/modules"
        ]
      }
    }
  }
}

Some considerations:

  • about the configurations:
    • the manifest configuration parameter is required, otherwise puppet apply errors out and the puppet-debugserver adapter crashes
    • noop is convenient in order to avoid applying manifests for real on your machine
    • args are additional arguments given to puppet apply, so of course --modulepath is optional, but it's handy when debugging anything with dependencies.
  • The debugger doesn't seem to have a configuration to stop on entry. You need to set at least one breakpoint somewhere to make it stop.

Kotlin

Gadget Installer

cust_kotlin-debug-adapter.json

{
  "cust_kotlin-debug-adapter": {
    "download": {
      "url": "https://github.com/fwcd/kotlin-debug-adapter/releases/download/${version}/${file_name}"
    },
    "all": {
      "version": "0.4.2",
      "file_name": "adapter.zip",
      "checksum": "434abbcc5600fa6d2424930a7c81e4c30ae5fbf05cce1cbb0af96b58db540294",
      "make_executable": [
        "bin/kotlin-debug-adapter"
      ],
      "extension_path": "adapter",
      "adapters": {
        "cust_kotlin-debug-adapter": {
          "command": [
            "${gadgetDir}/cust_kotlin-debug-adapter/bin/kotlin-debug-adapter"
          ],
          "name": "kotlin-debug-adapter",
          "configuration": {
            "projectRoot": "${workspaceRoot}"
          }
        }
      }
    }
  }
}

Vimspector Config

.vimspector.json

{
  "configurations": {
    "kotlin-debug-adapter launch": {
      "adapter": "cust_kotlin-debug-adapter",
      "configuration": {
        "request": "launch",
        "projectRoot": "${workspaceFolder}",
        "mainClass": "vimspector/test/ApplicationKt"
      }
    },
    "kotlin-debug-adapter attach": {
      "adapter": "cust_kotlin-debug-adapter",
      "configuration": {
        "request": "attach",
        "projectRoot": "${workspaceFolder}",
        "hostName": "${hostName}",
        "port": "${port}"
      }
    }
  }
}

Details and Dependencies: https://github.com/fwcd/kotlin-debug-adapter

Dart & Flutter

Clone the Dart-Code repo onto your system.

Gadget Installer

Use "type": "flutter" to debug Flutter apps.

cust_dart_flutter-debug-adapter.json

"cust_dart-debug-adapter": {
  "command": [
    "node",
    "${root}/out/dist/debug.js",
    "dart"
  ],
  "type": "dart",
  "variables": {
    "root": "/path/to/Dart-Code"
  }
}

.vimspector.json (Place in your project root)

{
 "configurations": {
   "launch": {
     "adapter": "cust_dart-debug-adapter",
     "configuration": {
       "request": "launch",
       "type": "flutter",
       "flutterSdkPath": "/path/to/flutter", // Change this
       "dartSdkPath": "/path/to/flutter/bin/cache/dart-sdk/bin", // Change this
       "program": "${workspaceRoot}/main.dart", // Change this
       "cwd": "${workspaceRoot}" // Change this (optionally)
     }
   }
 }
}

Hot-Reload

Place this in e.g. your ~/.vimrc to enable hot-reloading on save.

function! s:DartHotReload()
    if py3eval( 'not _vimspector_session or not _vimspector_session._connection' )
      return
    endif
    py3 _vimspector_session._connection.DoRequest( None, { 'command': 'hotReload' } )
endfunction

autocmd BufWritePost *.dart call s:DartHotReload()

Probe-rs Remote Hardware Assisted Debugging

Probe-rs is software to make use of various "debug probes" (hardware interfaces - often USB adapters such as CMSIS-DAP, JLink, FTDI, STLink etc.) to program and debug many microcontrollers and microprocessors. It uses hardware features to debug "bare metal" software such as embedded control systems, RTOS, or full operating systems. Although it's most often used to debug Rust on microcontrollers, it can also be used to debug a diverse range of languages and hardware for instance it can be used to interactively debug the Linux kernel C code running on the Raspberry Pi 4 from another machine.

Follow the install instructions on the probe-rs website. You must use a version newer than Release 0.21.0 (or build from git master). Since there are a very large number of possible combinations of host OS, debug hardware and target hardware, it is strongly recommended to configure and manually test the probe-run binary with your particular hardware configuration (e.g. to program and execute code, and receive simple debug output strings etc.) before moving on to configure vimspector.

.vimspector.json (Place in your project root)

The "configuration" section is passed to probe-rs, and can be edited with reference to the probe-rs option documentation

The first RUST_LOG entry will enable verbose debug logging output from probe-rs itself, (this could be changed to warn or info).

The example configuration below is suitable for debugging a bare metal Rust executable running on an STM32F401CEU6 microcontroller (ARM Cortex-M4) with an "STLink" USB to SWD debug probe (autodetected by probe-rs):

{
  "$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json#",
  "adapters": {
    "probe-rs": {
      "command": [
        "probe-rs",
        "dap-server",
        "--port",
        "${unusedLocalPort}"
      ],
      "name": "probe-rs",
      "port": "${unusedLocalPort}",
      "env": {
        "RUST_LOG": "debug"
      }
    }
  },
  "configurations": {
    "attach to probe-rs dap": {
      "adapter": "probe-rs",
      "filetypes": [ "rust" ],
      "configuration": {
        "request": "launch",
        "cwd": "${workspaceRoot}",
        "chip": "STM32F401CEUx",
        "coreConfigs": [
          {
            "coreIndex": 0,
            "svdFile": "/home/tim/prog/rust/stm32-rs/svd/stm32f401.svd.patched",
            "programBinary": "./target/thumbv7em-none-eabihf/release/fugit-test1",
            "rttEnabled": true
          }
        ],
        "env": {
          "DEFMT_LOG": "debug",
          "RUST_LOG": "debug"
        },
        "runtimeExecutable": "probe-rs",
        "runtimeArgs": ["dap-server"],
        "flashingConfig": {
          "flashingEnabled": true,
          "haltAfterReset": false,
          "formatOptions": {
            "format": "elf"
          }
        },
        "connectUnderReset": false,
        "consoleLogLevel": "Debug"
      }
    }
  }
}

... alternatively you can change "adapter": "probe-rs" to "adapter": "multi-session" and run probe-rs manually on the command line: e.g. $ probe-rs dap-server --port 4567.

Help wanted

Help improve Vimspector by writing gadget installer files and configuration snippets for these servers:

https://github.com/puremourning/vimspector/issues?q=is%3Aissue+is%3Aopen+label%3A%22Server+support%22

Using an arbitrary server

In general, the approach is:

  1. Install the VSCode extension. You can either use VSCode to install it, or you can extract the file manually (it's usually a zip file). Vimspector also supports an experrimental configuration which will use Vimspector's own installer (see later)
  2. Work out how to launch it. Find out how the VSCode extension launches the debug adapter. This is usually a matter of opening the package.json for the extension and finding the contributes entry for debuggers. Most of the time, it's just a simple command like node some/path/main.js).
  3. Set up the adapter block in your .vimspector.json. This can be as simple as just a command, but see the full adapter specification for details.
{
  "adapters": {
    "<your-adapter-name>": {
      "command": "<command to launch adapter>"
    }
  }
}
  1. Find out what launch arguments are needed. This is often documented by the server author, but will usually be targetted at VScode users and may be incomplete. Most of the time, however, launch arguments are documented and generally work the same in Vimspector as any other DAP client (e.g. VSCode).

You can save the gadget config (adapter block) to <vimspector base dir>/gadgets/.gadgets.d/<name>.json or just put it in your local .vimspector.json. In general, I recommend the former.

Gadget Installer Files

** NOTE: This is an experimental feature and may change at any time, or be completely withdrawn **

Vimspector's installer has support for downloading and unpacking arbitrary gadgets using the same mechanisms that the 'officially' supported gadgets use. It is however somewhat limited and only works for some types of gadget.

The way this is done is by dropping a JSON file in gadgets/custom/ which contains a specification for the gadget to install. This tells Vimspector what to download and how to set up the gadget config.

Currently this is only supported with install_gadget.py (not with :VimspectorInstall or :VimspectorUpdate).

Each gadget installer file must contain a single object with keys as the names of gadgets (and conventionally start with cust_ to differentiate from those officially supported), and whose values as an object describing the gadget as follows:

  • download (object): Describes what to download
    • download.url: (string) URL to download
    • download.target (string, optional): File name to save the result to
    • download.format (string, optional, default: zip): one of zip, zip.gz, tar
  • repo (object): If supplied, clone this repo (rather than downloading a zip)
    • repo.url (string): Url to clone
    • repo.ref (string): Ref (branch/tag) to check out (e.g. master or vX.Y.Z)
  • make_executable (list of string): List of relative paths within the bundle to make executable after download/install. See next section.
  • all (object): parameters for the gadget on all platforms. These items are available as ${item} (e.g. ${version}) within the download and repo string keys.
    • all.version (string): The version. Available as ${version}.
    • all.file_name (string): The resulting downloaded file name. ${file_name}
    • all.checksum (string): The expected SHA256 checksum of downloaded file.
    • all.extension_path (string, optional, default: extension): The base path of the downloaded extension. The purpose of this property is to support generic debug adapters, vs code extensions should work with the default value.
    • all.'anything' (string): Any additional parameters
    • all.adapters (object): Adapters to create. Same format as adapter block in .vimspector.json
  • <os> (object): <os> can be windows, linux or macos and the object has the same definition as all. Any values supplied in this block override those in all for the specified OS.

This format is a JSON-equivalent of the format used in python3/vimspector/gadgets.py.

Executable files

For some reason that can only be explained by history, the vsix format used for VScode does not include 'execute' file permissions. Therefore, when unzipping such extensions, you need to manually set the execute flag on all the files in the bundle that should be executable.

Therefore when creating a gadget installer file, you need to work out which files should be made executable. Usually this is just the debug adapter itself if anything.

The process for working this out is to test it and see what doesn't work, or read the VScode extension's code. Sad, but that's the way it is.

The 'all' and OS-specific blocks

Things that are defined in these blocks are used to determine if an upgrade is required. So include the version and anything that changes on upgrade.

These values are also made available in the 'download' and 'repo' blocks to save typing. However, they are not available to each other, so you can't include ${version} in "file_name", unfortunately.