diff --git a/exe/ruby-lsp b/exe/ruby-lsp index 3a44d07556..574caeb39f 100755 --- a/exe/ruby-lsp +++ b/exe/ruby-lsp @@ -29,6 +29,13 @@ parser = OptionParser.new do |opts| options[:branch] = branch end + opts.on( + "--lsp-path [PATH]", + "Launch the Ruby LSP using a local PATH rather than the release version", + ) do |path| + options[:lsp_path] = path + end + opts.on("--doctor", "Run troubleshooting steps") do options[:doctor] = true end @@ -54,6 +61,16 @@ rescue OptionParser::InvalidOption => e exit(1) end +if options[:branch] && options[:lsp_path] + warn('Invalid options: --branch and --lsp-path cannot both be set.') + exit(1) +end + +if options[:lsp_path] && !File.absolute_path?(options[:lsp_path]) + warn('Invalid option: --lsp-path must be an absolute path.') + exit(1) +end + # When we're running without bundler, then we need to make sure the composed bundle is fully configured and re-execute # using `BUNDLE_GEMFILE=.ruby-lsp/Gemfile bundle exec ruby-lsp` so that we have access to the gems that are a part of # the application's bundle diff --git a/jekyll/contributing.markdown b/jekyll/contributing.markdown index 270284cdb0..f48452446a 100644 --- a/jekyll/contributing.markdown +++ b/jekyll/contributing.markdown @@ -58,6 +58,20 @@ with a debugger. Note that the debug mode applies only until the editor is close Caveat: since you are debugging the language server instance that is currently running in your own editor, features will not be available if the execution is currently suspended at a breakpoint. +#### Configuring the server version + +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. + +```jsonc +{ + "rubyLsp.serverPath": "/path/to/your/ruby-lsp-clone" +} +``` + +*Note: There are some exceptions to this. Most notably, when the ruby-lsp repository is opened in VS Code with the extension active, it will run the server contained within the repository. + #### Understanding Prism ASTs The Ruby LSP uses Prism to parse and understand Ruby code. When working on a feature, it's very common to need to diff --git a/lib/ruby_lsp/setup_bundler.rb b/lib/ruby_lsp/setup_bundler.rb index e520dd1498..c614e65bac 100644 --- a/lib/ruby_lsp/setup_bundler.rb +++ b/lib/ruby_lsp/setup_bundler.rb @@ -35,6 +35,7 @@ def stdout def initialize(project_path, **options) @project_path = project_path @branch = options[:branch] #: String? + @lsp_path = options[:lsp_path] #: String? @launcher = options[:launcher] #: bool? patch_thor_to_print_progress_to_stderr! if @launcher @@ -166,6 +167,7 @@ def write_custom_gemfile unless @dependencies["ruby-lsp"] ruby_lsp_entry = +'gem "ruby-lsp", require: false, group: :development' ruby_lsp_entry << ", github: \"Shopify/ruby-lsp\", branch: \"#{@branch}\"" if @branch + ruby_lsp_entry << ", path: \"#{@lsp_path}\"" if @lsp_path parts << ruby_lsp_entry end diff --git a/test/setup_bundler_test.rb b/test/setup_bundler_test.rb index ca504d5307..f93c3d1091 100644 --- a/test/setup_bundler_test.rb +++ b/test/setup_bundler_test.rb @@ -280,6 +280,27 @@ def test_creates_composed_bundle_with_specified_branch end end + def test_creates_composed_bundle_with_specified_path + Dir.mktmpdir do |dir| + local_path = File.join(dir, "local-ruby-lsp") + FileUtils.mkdir_p(File.join(local_path, "lib")) + + Dir.chdir(dir) do + bundle_gemfile = Pathname.new(".ruby-lsp").expand_path(Dir.pwd) + "Gemfile" + Bundler.with_unbundled_env do + stub_bundle_with_env(bundle_env(dir, bundle_gemfile.to_s)) + run_script(File.realpath(dir), lsp_path: local_path) + end + + assert_path_exists(".ruby-lsp") + assert_path_exists(".ruby-lsp/Gemfile") + assert_match(%r{ruby-lsp.*path: "#{Regexp.escape(local_path)}"}, File.read(".ruby-lsp/Gemfile")) + assert_match("debug", File.read(".ruby-lsp/Gemfile")) + end + end + end + + def test_returns_bundle_app_config_if_there_is_local_config Dir.mktmpdir do |dir| Dir.chdir(dir) do diff --git a/vscode/package.json b/vscode/package.json index fac2b8ed93..a360174179 100644 --- a/vscode/package.json +++ b/vscode/package.json @@ -467,6 +467,10 @@ "type": "string", "default": "" }, + "rubyLsp.serverPath": { + "description": "Absolute path to a local ruby-lsp repository. Only supported if not using bundleGemfile", + "type": "string" + }, "rubyLsp.pullDiagnosticsOn": { "description": "When to pull diagnostics from the server (on change, save or both). Selecting 'save' may significantly improve performance on large files", "type": "string", diff --git a/vscode/src/client.ts b/vscode/src/client.ts index d7cf40784d..4ae716eec7 100644 --- a/vscode/src/client.ts +++ b/vscode/src/client.ts @@ -85,6 +85,7 @@ function getLspExecutables(workspaceFolder: vscode.WorkspaceFolder, env: NodeJS. const customBundleGemfile: string = config.get("bundleGemfile")!; const useBundlerCompose: boolean = config.get("useBundlerCompose")!; const bypassTypechecker: boolean = config.get("bypassTypechecker")!; + const serverPath: string = config.get("serverPath")!; const executableOptions: ExecutableOptions = { cwd: workspaceFolder.uri.fsPath, @@ -119,18 +120,21 @@ function getLspExecutables(workspaceFolder: vscode.WorkspaceFolder, env: NodeJS. options: executableOptions, }; } else { - const workspacePath = workspaceFolder.uri.fsPath; + const basePath = serverPath || workspaceFolder.uri.fsPath; const command = - path.basename(workspacePath) === "ruby-lsp" && os.platform() !== "win32" - ? path.join(workspacePath, "exe", "ruby-lsp") + path.basename(basePath) === "ruby-lsp" && os.platform() !== "win32" + ? path.join(basePath, "exe", "ruby-lsp") : "ruby-lsp"; const args = []; - if (branch.length > 0) { args.push("--branch", branch); } + if (serverPath) { + args.push("--lsp-path", serverPath); + } + if (featureEnabled("launcher")) { args.push("--use-launcher"); }