From be6be22b76b183fe214cf65d7f595a23ec585efb Mon Sep 17 00:00:00 2001 From: Valdemar Lorenzen Date: Fri, 9 Oct 2026 14:48:25 +0200 Subject: [PATCH] refactor: nest Ruby LSP actions under core.lsp --- README.md | 10 +++++----- examples/config.rb | 6 +++--- src/main.rs | 2 +- src/ruby.rs | 13 +++++++++++++ src/ruby_host.rb | 13 ++++++++++++- tests/lsp.rs | 2 +- 6 files changed, 35 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 91a2331..44cc9b0 100644 --- a/README.md +++ b/README.md @@ -259,9 +259,9 @@ Vamp.configure do |config| initialization_options: {}, settings: { "my-language-server" => { "exampleOption" => true } } } - config.bind "leader d", config.core.lsp_definition - config.bind "leader D", config.core.lsp_diagnostics - config.bind "K", config.core.lsp_hover + config.bind "leader d", config.core.lsp.definition + config.bind "leader D", config.core.lsp.diagnostics + config.bind "K", config.core.lsp.hover end ``` @@ -275,7 +275,7 @@ The native module initializes each server, sends `initialization_options` as `in `:lsp_definition` or the configured Space d binding requests a definition at the primary caret, once regardless of selection count. Both Location and LocationLink results are supported; multiple results use the first. Local-file navigation reuses loaded buffers without overwriting unsaved text. LSP UTF-16 positions and percent-encoded file URIs are translated to validated grapheme coordinates. Navigation is ignored if its source buffer, revision, caret, or latest request has changed. -For programmatic bindings, use `config.core.lsp_definition`, `config.core.lsp_diagnostics`, and `config.core.lsp_hover`, immutable Action objects eligible in Normal mode. Each routes to the same native implementation as its Ex command; no LSP keys are bound by default. These actions are also available in the command palette. +For programmatic bindings, `config.core.lsp` exposes immutable Normal-mode Action objects: `definition`, `diagnostics`, and `hover`. Each routes to the same native implementation as its Ex command; no LSP keys are bound by default. These actions are also available in the command palette. The namespace is distinct from `config.lsp[language_id]` server configuration; flat Ruby LSP action members have been removed without aliases. Diagnostics appear as severity-colored gutter numbers and text underlines; the primary caret's diagnostic appears in the message row. `:lsp_diagnostics` reports the current document's server state. Older versioned diagnostics are discarded; unversioned diagnostics can only be associated with the arrival-time document revision. Editing clears previous annotations immediately. Server startup/transport failures are reported and clear that server's diagnostics; other servers and native editing remain available. There are no automatic restarts. @@ -332,7 +332,7 @@ Key notation follows [Herdr's readable style](https://herdr.dev/docs/configurati | Normal | `insert`, `append`, `open_below`, `open_above` | | Normal, Visual | `insert_first_nonblank`, `append_line_end`, `clone_above`, `clone_below` | | Normal | `join_lines`, `undo`, `redo`, `command` | -| Normal | `lsp_definition`, `lsp_diagnostics`, `lsp_hover` | +| Normal | `lsp.definition`, `lsp.diagnostics`, `lsp.hover` | | Normal | `search`, `paste_before`, `paste_after` | | Normal | `paste_system_before`, `paste_system_after` | | Normal, Visual | `select`, `search_next`, `search_previous` | diff --git a/examples/config.rb b/examples/config.rb index 63e54b4..0dc1e4f 100644 --- a/examples/config.rb +++ b/examples/config.rb @@ -19,9 +19,9 @@ Vamp.configure do |config| # command: ["my-language-server", "--stdio"], extensions: ["myl"], # initialization_options: {}, settings: {} # } - # config.bind "leader d", config.core.lsp_definition - # config.bind "leader D", config.core.lsp_diagnostics - # config.bind "K", config.core.lsp_hover + # config.bind "leader d", config.core.lsp.definition + # config.bind "leader D", config.core.lsp.diagnostics + # config.bind "K", config.core.lsp.hover config.bind "g", "greet" config.bind "leader g", "greet" config.bind "ctrl+r", config.core.redo diff --git a/src/main.rs b/src/main.rs index 7cf1bd2..ff6d82c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -39,7 +39,7 @@ Set VAMP_RUBY to choose the Ruby executable. Native LSP is disabled by default: config.lsp[\"rust\"] = true enables a preset Custom languages: config.lsp[\"my-language\"] = { command: [...], extensions: [...] } LSP commands: :lsp_definition, :lsp_diagnostics, :lsp_hover -Hover binding: config.bind \"K\", config.core.lsp_hover (no default key)"; +Hover binding: config.bind \"K\", config.core.lsp.hover (no default key)"; struct Options { file: Option, diff --git a/src/ruby.rs b/src/ruby.rs index d5f701c..bd7360e 100644 --- a/src/ruby.rs +++ b/src/ruby.rs @@ -873,6 +873,19 @@ end .unwrap_err() .contains("must be a String") ); + for target in [ + "config.core.lsp_hover", + "config.core.lsp_definition", + "config.core.lsp_diagnostics", + "config.core.lsp.hvoer", + "config.core.lsp", + ] { + let invalid = fixture.file( + "invalid_action.rb", + &format!("Vamp.configure {{ |config| config.bind 'K', {target} }}"), + ); + assert!(load_config(&invalid).is_err(), "{target}"); + } let invalid = fixture.file("core.rb", "Vamp.plugin 'evil', core: true"); assert!(RubyPlugin::start(&invalid, &serde_json::json!({})).is_err()); let invalid = fixture.file("syntax.rb", "this is not valid ruby ("); diff --git a/src/ruby_host.rb b/src/ruby_host.rb index 623145a..def0333 100644 --- a/src/ruby_host.rb +++ b/src/ruby_host.rb @@ -64,11 +64,22 @@ module Vamp copy_system paste_system_before paste_system_after select_lines clone_above clone_below select_next skip_occurrence select_all file_picker file_explorer - lsp_definition lsp_diagnostics lsp_hover ].each do |name| action = Action.new("core.#{name}".freeze).freeze define_singleton_method(name) { action } end + + module Lsp + %w[definition diagnostics hover].each do |name| + action = Action.new("core.lsp_#{name}".freeze).freeze + define_singleton_method(name) { action } + end + end + Lsp.freeze + + def self.lsp + Lsp + end end module Modes diff --git a/tests/lsp.rs b/tests/lsp.rs index 7c6a4c0..bc6effc 100644 --- a/tests/lsp.rs +++ b/tests/lsp.rs @@ -245,7 +245,7 @@ impl Fixture { "settings": {"rust-analyzer": {"cargo": {"allFeatures": true}}}, }); let config = format!( - "Vamp.configure do |config|\n config.clipboard_mode = 'separate'\n config.lsp['fixture-language'] = JSON.parse({})\n config.bind 'leader d', config.core.lsp_definition\n config.bind 'leader D', config.core.lsp_diagnostics\n config.bind 'K', config.core.lsp_hover\nend\n", + "Vamp.configure do |config|\n config.clipboard_mode = 'separate'\n config.lsp['fixture-language'] = JSON.parse({})\n config.bind 'leader d', config.core.lsp.definition\n config.bind 'leader D', config.core.lsp.diagnostics\n config.bind 'K', config.core.lsp.hover\nend\n", serde_json::to_string(&options.to_string()).unwrap(), ); let config_path = self.root.join("config.rb");