-
Notifications
You must be signed in to change notification settings - Fork 241
Description
Description
Ruby LSP Information
VS Code Version
1.109.5
Ruby LSP Extension Version
0.10.0
Ruby LSP Server Version
0.26.7
Ruby LSP Add-ons
Ruby Version
4.0.1
Ruby Version Manager
chruby
Installed Extensions
Click to expand
- EditorConfig (0.18.1)
- LiveServer (5.7.10)
- asciidoctor-vscode (3.4.5)
- csdevkit (2.10.3)
- csharp (2.120.3)
- elixir-ls (0.30.0)
- git-graph (1.30.0)
- html-preview-vscode (0.2.5)
- remote-explorer (0.5.0)
- remote-ssh (0.122.0)
- remote-ssh-edit (0.87.0)
- ruby-extensions-pack (0.1.13)
- ruby-lsp (0.10.0)
- vscode-dotnet-runtime (3.0.0)
- vscode-elixir (1.1.0)
- vscode-postgres (1.4.3)
Ruby LSP Settings
Click to expand
Workspace
{}User
{
"enabledFeatures": {
"codeActions": true,
"diagnostics": true,
"documentHighlights": true,
"documentLink": true,
"documentSymbols": true,
"foldingRanges": true,
"formatting": true,
"hover": true,
"inlayHint": true,
"onTypeFormatting": true,
"selectionRanges": true,
"semanticHighlighting": true,
"completion": true,
"codeLens": true,
"definition": true,
"workspaceSymbol": true,
"signatureHelp": true,
"typeHierarchy": true
},
"featuresConfiguration": {},
"addonSettings": {},
"rubyVersionManager": {
"identifier": "auto"
},
"customRubyCommand": "",
"formatter": "auto",
"linters": null,
"bundleGemfile": "",
"testTimeout": 30,
"branch": "",
"pullDiagnosticsOn": "both",
"useBundlerCompose": false,
"bypassTypechecker": false,
"rubyExecutablePath": "",
"indexing": {},
"erbSupport": true,
"featureFlags": {},
"sigOpacityLevel": "1"
}Also reproduced on Windows 11 with Ruby 3.4.8 via RubyInstaller2 (x64-mingw-ucrt).
Reproduction steps
- Create or open a project whose absolute path contains a space (e.g.
/Users/John Doe/Projects/helloorC:\Users\John Doe\Documents\hello) - Use the default generated
launch.json(viaprovideDebugConfigurations) - Launch the debugger
- Debugger exits immediately with status 1
Code snippet or error message
2026-02-28 14:28:53.852 [info] [debugger]: Command bundle exec rdbg --open --command -- ruby /Users/john.doe/Desktop/Ruby Test/hello.rb
2026-02-28 14:28:54.093 [info] [debugger]: ruby: No such file or directory -- /Users/john.doe/Desktop/Ruby (LoadError)
2026-02-28 14:28:54.094 [info] [debugger]: Debugger exited with status 1.
The extension builds the rdbg command without quoting the file path. Since the path is unquoted, ruby only sees /Users/john.doe/Desktop/Ruby as the filename and stops at the first space.
Related issue
This is the same underlying bug reported in #1709 (on Linux, closed after a user-side workaround was identified). The maintainer asked which workaround resolved it and noted: "We can do something from the extension side to improve this." That improvement was not followed up on.
Why the existing workaround is insufficient
Issue #1709 was closed after wrapping ${file} in single quotes in launch.json:
"program": "ruby '${file}'"This has two problems:
- It doesn't work on Windows —
cmd.exetreats single quotes as literal characters. - It requires manual intervention — users who use the auto-generated template (via
provideDebugConfigurations) always get the broken version. Every user with spaces in their path has to independently discover and apply this workaround.
Root cause
The program field conflates the Ruby command and the file path into a single shell command string. By the time spawnDebuggeeForLaunch sees i.program, VS Code has already substituted ${file} with the literal path — the extension can no longer distinguish where the command ends and where the path begins. The extension then passes this string to the shell unquoted.
VS Code does not add quotes during variable substitution; quoting is the responsibility of whoever constructs the shell command — in this case, the extension.
Suggested fix
Option A — Fix the template (partial fix)
Update provideDebugConfigurations to emit double-quoted paths:
{ type: "ruby_lsp", name: "Debug script", request: "launch", program: 'ruby "${file}"' },
{ type: "ruby_lsp", name: "Debug test", request: "launch", program: 'ruby -Itest "${relativeFile}"' },Double quotes work on Unix shells, cmd.exe, and PowerShell (with the caveat that PowerShell interpolates $ in double quotes, though this is unlikely to affect file paths in practice).
This only helps users who generate a fresh launch.json; existing configurations remain broken.
Option B — Separate command and file fields with program fallback
The cleanest way to eliminate the parsing ambiguity is to introduce separate command and file fields, while continuing to honour the existing program field for backwards compatibility:
const program = i.program
? i.program
: `${i.command} "${i.file}"`;The new launch.json template would emit:
{
"type": "ruby_lsp",
"name": "Debug script",
"request": "launch",
"command": "ruby",
"file": "${file}"
}With i.file as a dedicated path field, the extension can quote it correctly without any heuristic parsing. Users with existing program configurations are unaffected — their launch.json keeps working as before.
Option C — Parse the path out of program and quote it (fragile)
Another approach would be to heuristically extract the file path from the existing program string and quote it. Since program always starts with ruby optionally followed by flags, the extension could split on that known prefix and treat the remainder as the path:
const match = program.match(/^(ruby\s+(?:-\S+\s+)*)(.+)$/);
if (match) {
const cmd = match[1];
const filePath = match[2];
if (!filePath.startsWith('"') && !filePath.startsWith("'")) {
program = `${cmd}"${filePath}"`;
}
}While this would fix the common case, it is inherently fragile because the extension is guessing where the command ends and the path begins. It breaks on relative paths (ruby test/foo.rb), commands without a file path (ruby -e 'code'), or any future change to the allowed shape of program. It also adds complexity that would need to be maintained and tested across platforms. For these reasons I'd suggest Option B over this approach — separating the fields eliminates the ambiguity rather than trying to work around it.