Upgrade to Pro — share decks privately, control downloads, hide ads and more …

2 years of Ruby LSP

2 years of Ruby LSP

This talk goes over the 2 years of development of the Ruby LSP. It starts showcasing some of its features, the current state of the internals and then goes into some of the future plans for the project.

Vinicius Stock

March 04, 2024
Tweet

More Decks by Vinicius Stock

Other Decks in Programming

Transcript

  1. Vinicius Stock Sta ff dev @ Ruby DX team Shopify

    X: @vinistock GitHub: @vinistock https://vinistock.com
  2. require "open3" stdin, stderr, stdout, wait_thr = Open3.popen3("ruby-lsp") # Uses

    stdin to send requests # Uses stdout to receive responses
  3. require "open3" stdin, stderr, stdout, wait_thr = Open3.popen3("ruby-lsp") # Uses

    stdin to send requests # Uses stdout to receive responses # Communication happens using JSON
  4. require "open3" stdin, stderr, stdout, wait_thr = Open3.popen3("ruby-lsp") # Uses

    stdin to send requests # Uses stdout to receive responses # Communication happens using JSON # Prints all stderr to the editor's output
  5. EDITOR SERVER STDOUT STDIN textDocument/didOpen class Foo end textDocument/foldingRange textDocument/foldingRange

    textDocument/didChange class Foo def bar end end textDocument/foldingRange textDocument/foldingRange
  6. Open or edit fi le De fi nition, references, rename

    Notify the server Automatic Manual Trigger automatic requests Trigger desired request
  7. Vim Ruby plugin Sublime VS Code Emacs Ruby plugin Ruby

    plugin Ruby plugin Go plugin Go plugin Go plugin Go plugin Rust plugin Rust plugin Rust plugin Rust plugin
  8. class Foo def bar end end { "textDocument": { "uri":

    "file: / / / foo.rb" } } [ { "startLine": 1, "endLine": 1, "kind": "region" } ]
  9. class Visitor def visit_child_nodes(node) node.child_nodes.each do |child| visit(child) end end

    alias_method :visit_class_node, :visit_child_nodes alias_method :visit_module_node, :visit_child_nodes end
  10. class MyVisitor < Prism : : Visitor def visit_class_node(node) puts

    "Hi, # { node.constant_path.full_name}" super end end
  11. The dispatcher is a visitor that fi res events for

    each node encountered in the AST
  12. class Dispatcher < Prism : : Visitor def initialize @listeners

    = Hash.new do |h, k| h[k] = [] end end end
  13. class Dispatcher < Prism : : Visitor def register(listener, *events)

    events.each do |e| @listeners[e] < < listener end end end
  14. CLASS CONST (Foo) DEF (bar) STRING (Hey!) Listener 1 classes

    Dispatcher Listener 2 classes and methods Event Event Event
  15. class FoldingRange def on_class_node(node) loc = node.location @ranges < <

    { startLine: loc.start_line, endLine: loc.end_line, kind: "region" } end end
  16. ast = Prism.parse_file("foo.rb").value dispatcher = Prism : : Dispatcher.new listener

    = FoldingRange.new(dispatcher) dispatcher.dispatch(ast)
  17. ast = Prism.parse_file("foo.rb").value dispatcher = Prism : : Dispatcher.new listener

    = FoldingRange.new(dispatcher) dispatcher.dispatch(ast) # The listener's response will be populated # with all ranges
  18. ast = Prism.parse_file("foo.rb").value dispatcher = Prism : : Dispatcher.new folding_range

    = FoldingRange.new(dispatcher) document_symbol = DocumentSymbol.new(dispatcher) semantic_highlighting = SemanticHighlighting.new(dispatcher) dispatcher.dispatch(ast)
  19. What's next for the Ruby LSP? • Making Ruby activation

    more robust and improving error handling • Full method support for de fi nition, hover and completion and signature help • Refactors
  20. • Rename • Occurrences • Show type hierarchy • More

    debugging features What's next for the Ruby LSP?
  21. You get features based on the tools your application uses

    without having to con fi gure anything Future bets
  22. Addons • A way for other gems to enhance Ruby

    LSP features • The Ruby LSP should automatically detect which addon to load based on your project's dependencies • Rails addon: https://github.com/Shopify/ruby-lsp-rails
  23. Challenges with addons • How to detect which addons to

    install based on the dependency? • Should the addons be embedded in the gems they are related to? Or should they be separate gems? • How do we allow for con fi guration that is editor agnostic? A `~/.ruby-lsprc` fi le?
  24. • https://github.com/Shopify/vscode-ruby-lsp • https://github.com/Shopify/ruby-lsp • https://github.com/Shopify/ruby-lsp-rails • Font: https://github.com/microsoft/cascadia-code •

    https://microsoft.github.io/language-server-protocol/speci fi cations/lsp/ 3.17/speci fi cation/ • All videos/screenshots made with VS Code References