Skip to content

Conversation

jconley88
Copy link

Motivation

Closes #3709

Implementation

VS Code Extension

  • Added a serverPath configuration setting to package.json
  • when serverPath configuration is present in getLspExecutables, set --path as an arg to the ruby-lsp executable
  • The serverPath option supports relative and absolute paths to both the ruby-lsp repo root directory as well as the executable file

Ruby LSP Server

  • Capture --path arg as an option on ruby-lsp execution
  • When setting up the Gemfile in setup_bundler.rb, add the configured absolute path to gem 'ruby-lsp', path: "..."

In every location where the path is considered an error is raised if the branch is also set.

Automated Tests

I followed the test pattern for the --branch option when determining what to test. I have added tests for Bundler setup, but not for VS Code nor for option parsing in the ruby-lsp executable. I would be happy to add additional testing if I missed anything.

Manual Tests

  1. Modify the VS Code "Run Extension" launch configuration to set the target workspace to a ruby project of your choice.
    {
        "name": "Run Extension",
        "type": "extensionHost",
        "request": "launch",
        "args": [
                    "/path/to/your/target/workspace",
    	    "--extensionDevelopmentPath=${workspaceFolder}"
        ],
        "outFiles": [
    	    "${workspaceFolder}/out/**/*.js"
        ],
        "preLaunchTask": "${defaultBuildTask}"
    }
  2. From the "Run and Debug" side bar, run the extension
  3. In the newly opened debug VS Code instance
    1. In the settings you should be able to see the new option: rubyLsp.serverPath.
    2. Set theserverPath to your local ruby-lsp repo
    3. *Restart the entire debug instance to properly pick up on the changes
    4. Inspect the [path/to/target/workspace]/.ruby-lsp/Gemfile
      1. You should see the path: designation
    5. It's a little more challenging to fully verify that your code is actually running. You may need to modify some code, add some output or change the version number so that you can inspect it in the status bar
    6. Repeat the above with relative vs absolute paths, the ruby-lsp root path vs executable path, and simultaneously setting the branch.

*There are multiple existing issues with the config file watcher and auto-reload when a configuration setting is changed

  1. In current production (v0.26.1), the server restarts but does not respect rubyLsp.branch nor rubyLsp.serverPath
  2. In current development (v0.26.2), the server either does not restart at all or at least, the output is not shown for a restart
  3. Changing back and forth from a server that supports serverPath to a server that does not while running a vscode instance that does support serverPath likely would not work well anyhow due to the feature mismatch

…ment...

Also add --path option to the ruby-lsp executable itself
@jconley88 jconley88 requested a review from a team as a code owner August 13, 2025 18:47
Copy link

graphite-app bot commented Aug 13, 2025

How to use the Graphite Merge Queue

Add the label graphite-merge to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

Copy link
Member

@vinistock vinistock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great. I left some comments, but the implementation is in the right direction

exe/ruby-lsp Outdated
end

opts.on(
"--path [PATH]",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about we call this --lsp-path to prevent any confusion with a potential ability to configure the workspace path or some other path?


When developing the Ruby LSP server, you may want to test your changes in your own Ruby projects to ensure they work correctly in real-world scenarios.

The running server, even in debug mode, will default to the installed release version*. You can use the `rubyLsp.serverPath` configuration setting in the target workspace to start your local copy instead. Make sure to restart the language server after making changes to pick up your updates.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We automatically restart the server when any rubyLsp. settings change, so you actually don't need a manual restart.

Suggested change
The running server, even in debug mode, will default to the installed release version*. You can use the `rubyLsp.serverPath` configuration setting in the target workspace to start your local copy instead. Make sure to restart the language server after making changes to pick up your updates.
The running server, even in debug mode, will default to the installed release version*. You can use the `rubyLsp.serverPath` configuration setting in the target workspace to start your local copy instead.

@path = options[:path] #: String?
@launcher = options[:launcher] #: bool?

if @branch && !@branch.empty? && @path && !@path.empty?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're currently handling this both in the executable and here. I think we can skip this validation.

ruby_lsp_entry << ", github: \"Shopify/ruby-lsp\", branch: \"#{@branch}\""
end
if @path && !@path.empty?
absolute_path = File.expand_path(@path, @project_path)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this path expansion correct? The @project_path is the workspace currently being worked on and @path is the path to the LSP, which are potentially unrelated.

I think we can set the expectation that the path to the LSP clone must always be an absolute path and if it's not we error early in the executable.

let command: string;

const args = [];
if (serverPath.length > 0 && branch.length > 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly here, I think we can do without this validation since the executable already errors in this case. These specific settings are for the development of the LSP only and don't impact end user experience, so we don't need to worry too much about the failure scenarios.

"rubyLsp.serverPath": {
"description": "Absolute or workspace-relative path to a local ruby-lsp repository or its ruby-lsp executable. Only supported if not using bundleGemfile",
"type": "string",
"default": ""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can avoid setting a default here, so that when nothing is configured the value is undefined and we can simply check if (value) instead of checking for the string's length.

Comment on lines 135 to 151
const absoluteServerPath = path.isAbsolute(serverPath) ? serverPath : path.resolve(workspacePath, serverPath);
const exists = fs.existsSync(absoluteServerPath);

if (exists) {
args.push("--path", absoluteServerPath);
const stat = fs.statSync(absoluteServerPath);

if (stat.isDirectory()) {
command = os.platform() !== "win32" ? path.join(absoluteServerPath, "exe", "ruby-lsp") : "ruby-lsp";
} else {
command = absoluteServerPath;
}
} else {
throw new Error(
`The configured rubyLsp.serverPath "${serverPath}" does not exist at "${absoluteServerPath}". `,
);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few things about this section:

  • We don't need to change the command, it will still be ruby-lsp, we just need to append the new CLI flag
  • In VS Code extensions, we cannot ever use fs because that bypasses the VS Code file system API. Everything has to happen through vscode.workspace.fs

That said, since these options are for the development of the LSP itself only, I think we can skip checking these arguments here and move everything to the server (that also ensures that the checks work for any editor connecting to the LSP).

Let's check if the path is absolute and if it exists there and simplify this part.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't test it without changing the executable path. The extension is running the code with the serverPath configuration but without changing the executable directory, the server won't recognize the new --lsp-path option. I wasn't sure if you meant this, but I also removed the ability to specify the executable directly. I was already on the fence about it and i think it's just unnecessary complexity.

"default": ""
},
"rubyLsp.serverPath": {
"description": "Absolute or workspace-relative path to a local ruby-lsp repository or its ruby-lsp executable. Only supported if not using bundleGemfile",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make this only absolute.

@vinistock vinistock added enhancement New feature or request vscode This pull request should be included in the VS Code extension's release notes server This pull request should be included in the server gem's release notes labels Aug 18, 2025
  - Rename CLI flag from --path to --lsp-path to prevent confusion
  with other path configurations
  - Add validation in ruby-lsp executable to require absolute paths
   to repo root for --lsp-path option
  - Do not allow serverPath to directly be an executable
  - Remove incorrect path expansion in SetupBundler (LSP path and
  project path are unrelated)
  - Simplify VS Code extension to pass serverPath directly without
  validation or fs usage
  - Remove duplicate validation between executable and SetupBundler
  - Update package.json to specify absolute path requirement and
  remove default value
  - Remove manual restart instruction from documentation (server
  auto-restarts on config changes)
  - Fix potential runtime error when serverPath is undefined by
  using truthy check
@jconley88
Copy link
Author

Thanks for all of the feedback and good point about the unnecessary validation! I think I addressed everything. I left the validation that makes sure the configured executable path in getLspExectuables is named ruby-lsp. This shouldn't technically still be validated when serverPath is specified, but it kept the logic simpler. Let me know if you disagree, I missed any feedback or you've thought of anything else. I'm happy to continue iterating.

@jconley88
Copy link
Author

Hey, I'm just checking in on this in case it got lost. Is there anything else that I can do to get this in a good state for review and/or merge?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request server This pull request should be included in the server gem's release notes vscode This pull request should be included in the VS Code extension's release notes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow running a server from a local repo in standalone mode
2 participants