is Language Server? - A server which provides information to editors - ex. auto completion candidates, method definitions - How to communicate is defined as Language Server Protocol (LSP) - JSON-RPC based - Originally created by Microsoft and made open standard at 2016-06 22 / 116
do we need Language Server? - We have built language plugins for each editor - ex. a Ruby plugin for Vim, a Ruby plugin for Emacs, a Ruby plugin for VS code, … - We need to re-implement if we once implement core logic in editor specific language - Now we need to build just one Language Server 27 / 116
How can we implement Language Server in Ruby? 1. What is Language Server? 2. How can we implement Language Server in Ruby? 3. Introduction of language_server gem 36 / 116
Server Editor - VS Code extension - Node.js - vscode-languageclient (npm) - Node.js - vscode- languageserver (npm) Implementation in the example 39 / 116
of LSP Client - Editor specific library - Plugin author builds plugins depend on the library for each editor - https://github.com/Microsoft/vscode-languageserver-node - https://github.com/atom/atom-languageclient - Universal LSP Client plugin - Users install one plugin for all language - https://github.com/tomv564/LSP - https://github.com/autozimu/LanguageClient-neovim 45 / 116
and Cons - Editor specific library Author can provide editor specific configuration User don’t have to configure Author needs to build plugins for every editor User needs plugins for every language - Universal LSP Client plugin Author has to do “nothing” User needs one plugin for all language Author can’t provide editor specific configuration User must configure how to boot Language Server 48 / 116
Code Language Server extension example let disposable = new LanguageClient( 'languageServerExample', 'Language Server Example', { module: context.asAbsolutePath(path.join('server', 'server.js')), transport: TransportKind.ipc }, { documentSelector: ['plaintext'], } ).start(); context.subscriptions.push(disposable); 49 / 116
Server Editor - VS Code extension - Node.js - vscode-languageclient (npm) - Node.js - vscode- languageserver (npm) Implementation in the example 51 / 116
- A simple remote procedure call protocol - It uses JSON as data format - Transport agnostic - We can use socket, STDIO, HTTP, or other one as a transport layer 54 / 116
Code Language Server extension example let disposable = new LanguageClient( 'languageServerExample', 'Language Server Example', { module: context.asAbsolutePath(path.join('server', 'server.js')), transport: TransportKind.ipc }, { documentSelector: ['plaintext'], } ).start(); context.subscriptions.push(disposable); 58 / 116
Server requirements - Communicate with client using JSON-RPC via client supported transport layer - vscode-languageclient supports: - STDIO - Node ipc - Named pipe - Socket file 59 / 116
Server requirements - Communicate with client using JSON-RPC via client supported transport layer - vscode-languageclient supports: - STDIO - Node ipc - Named pipe - Socket file 63 / 116
Introduction of language_server gem 1. What is Language Server? 2. How can we implement Language Server in Ruby? 3. Introduction of language_server gem 75 / 116
gem - A Language Server implementation for Ruby - In alpha stage - Pure Ruby - Syntax check - [WIP] Auto completion - [WIP] Jump to definition 76 / 116
- Collection of Ruby code manipulation tools - https://github.com/rcodetools/rcodetools - Written in pure Ruby (other than editor specific codes) - Dynamic analysis 89 / 116
Analyze files under $BUNDLE_PATH statically on boot (Project) 1. Get current context for the position 2. Find available constants for the context FileStore 95 / 116 textDocument/completion Completion items Position Completion Provider AdHoc Project Reader Writer
#=> [<Module Foo#L1-10>, <Module Foo::Bar::Baz#L3-4>] result.classes #=> [<Class Foo::Bar#L2-5>] 1 module Foo 2 class Bar 3 module Baz 4 end 5 end 6 7 def foo 8 Ba_ 9 end 10 end Position (8, 7) 1. Get current context for the position 2. Find available constants for the context 101 / 116
#=> [<Module Foo#L1-10>, <Module Foo::Bar::Baz#L3-4>] result.classes #=> [<Class Foo::Bar#L2-5>] 1 module Foo 2 class Bar 3 module Baz 4 end 5 end 6 7 def foo 8 Ba_ 9 end 10 end Position (8, 7) 1. Get current context for the position 2. Find available constants for the context 102 / 116
#=> [<Module Foo#L1-10>, <Module Foo::Bar::Baz#L3-4>] result.classes #=> [<Class Foo::Bar#L2-5>] 1 module Foo 2 class Bar 3 module Baz 4 end 5 end 6 7 def foo 8 Ba_ 9 end 10 end Position (8, 7) 1. Get current context for the position 2. Find available constants for the context 103 / 116
Analyze files under $BUNDLE_PATH statically on boot (Project) 1. Get current node for the position 2. Find module/class for the node FileStore 106 / 116 textDocument/definition Location(s) Position Definition Provider AdHoc Project Reader Writer
#=> [<Class Foo#L1-6>, <Class Foo#L8-10>] result.classes #=> [<Class Foo::Bar#L2-5>] result.refs #=> [<VarRef Foo::Bar#L9(3..5)>] 1 class Foo 2 module Bar 3 def hi 4 end 5 end 6 end 7 8 class Foo 9 Bar 10 end Position (9, 3) 1. Get current context for the position 2. Find module/class for the node 107 / 116
#=> [<Class Foo#L1-6>, <Class Foo#L8-10>] result.classes #=> [<Class Foo::Bar#L2-5>] result.refs #=> [<VarRef Foo::Bar#L9(3..5)>] 1 class Foo 2 module Bar 3 def hi 4 end 5 end 6 end 7 8 class Foo 9 Bar 10 end Position (9, 3) 1. Get current context for the position 2. Find module/class for the node 108 / 116
#=> [<Class Foo#L1-6>, <Class Foo#L8-10>] result.classes #=> [<Class Foo::Bar#L2-5>] result.refs #=> [<VarRef Foo::Bar#L9(3..5)>] 1 class Foo 2 module Bar 3 def hi 4 end 5 end 6 end 7 8 class Foo 9 Bar 10 end Position (9, 3) 1. Get current context for the position 2. Find module/class for the node 109 / 116
- Building Language Server is a common way to provide useful information to editor - I created language_server gem, an implementation of Language Server for Ruby - You can build your Language Server using language_server-protocol gem 114 / 116