Class: Steep::Drivers::Langserver
- Inherits:
-
Object
- Object
- Steep::Drivers::Langserver
- Includes:
- Utils::EachSignature
- Defined in:
- lib/steep/drivers/langserver.rb
Instance Attribute Summary collapse
-
#open_paths ⇒ Object
readonly
Returns the value of attribute open_paths.
-
#options ⇒ Object
readonly
Returns the value of attribute options.
-
#signature_dirs ⇒ Object
readonly
Returns the value of attribute signature_dirs.
-
#source_dirs ⇒ Object
readonly
Returns the value of attribute source_dirs.
-
#subscribers ⇒ Object
readonly
Returns the value of attribute subscribers.
Instance Method Summary collapse
- #close_path(path) ⇒ Object
-
#initialize(source_dirs:, signature_dirs:) ⇒ Langserver
constructor
A new instance of Langserver.
- #open_path(path) ⇒ Object
- #open_path?(path) ⇒ Boolean
- #project ⇒ Object
- #respond_to_hover(uri:, line:, column:, notifier:, id:) ⇒ Object
- #run ⇒ Object
- #subscribe(method, &callback) ⇒ Object
- #synchronize_project(uri:, text:, notifier:) ⇒ Object
Methods included from Utils::EachSignature
#each_file_in_dir, #each_file_in_path
Constructor Details
#initialize(source_dirs:, signature_dirs:) ⇒ Langserver
Returns a new instance of Langserver.
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/steep/drivers/langserver.rb', line 12 def initialize(source_dirs:, signature_dirs:) @source_dirs = source_dirs @signature_dirs = signature_dirs = Project::Options.new @subscribers = {} @open_paths = Set.new subscribe :initialize do |request:, notifier:| LanguageServer::Protocol::Interface::InitializeResult.new( capabilities: LanguageServer::Protocol::Interface::ServerCapabilities.new( text_document_sync: LanguageServer::Protocol::Interface::TextDocumentSyncOptions.new( open_close: true, change: LanguageServer::Protocol::Constant::TextDocumentSyncKind::FULL, ), hover_provider: true ), ) end subscribe :shutdown do |request:, notifier:| Steep.logger.warn "Shutting down the server..." exit end subscribe :"textDocument/didOpen" do |request:, notifier:| uri = URI.parse(request[:params][:textDocument][:uri]) open_path uri text = request[:params][:textDocument][:text] synchronize_project(uri: uri, text: text, notifier: notifier) end subscribe :"textDocument/didClose" do |request:, notifier:| uri = URI.parse(request[:params][:textDocument][:uri]) close_path uri end subscribe :"textDocument/didChange" do |request:, notifier:| uri = URI.parse(request[:params][:textDocument][:uri]) text = request[:params][:contentChanges][0][:text] synchronize_project(uri: uri, text: text, notifier: notifier) end subscribe :"textDocument/hover" do |request:, notifier:| Steep.logger.warn request.inspect uri = URI.parse(request[:params][:textDocument][:uri]) line = request[:params][:position][:line] column = request[:params][:position][:character] respond_to_hover(uri: uri, line: line, column: column, notifier: notifier, id: request[:id]) end end |
Instance Attribute Details
#open_paths ⇒ Object (readonly)
Returns the value of attribute open_paths.
8 9 10 |
# File 'lib/steep/drivers/langserver.rb', line 8 def open_paths @open_paths end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
6 7 8 |
# File 'lib/steep/drivers/langserver.rb', line 6 def end |
#signature_dirs ⇒ Object (readonly)
Returns the value of attribute signature_dirs.
5 6 7 |
# File 'lib/steep/drivers/langserver.rb', line 5 def signature_dirs @signature_dirs end |
#source_dirs ⇒ Object (readonly)
Returns the value of attribute source_dirs.
4 5 6 |
# File 'lib/steep/drivers/langserver.rb', line 4 def source_dirs @source_dirs end |
#subscribers ⇒ Object (readonly)
Returns the value of attribute subscribers.
7 8 9 |
# File 'lib/steep/drivers/langserver.rb', line 7 def subscribers @subscribers end |
Instance Method Details
#close_path(path) ⇒ Object
118 119 120 |
# File 'lib/steep/drivers/langserver.rb', line 118 def close_path(path) open_paths.delete path end |
#open_path(path) ⇒ Object
114 115 116 |
# File 'lib/steep/drivers/langserver.rb', line 114 def open_path(path) open_paths << path end |
#open_path?(path) ⇒ Boolean
110 111 112 |
# File 'lib/steep/drivers/langserver.rb', line 110 def open_path?(path) open_paths.member?(path) end |
#project ⇒ Object
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/steep/drivers/langserver.rb', line 90 def project @project ||= Project.new.tap do |project| source_dirs.each do |path| each_file_in_path(".rb", path) do |file_path| file = Project::SourceFile.new(path: file_path, options: ) file.content = file_path.read project.source_files[file_path] = file end end signature_dirs.each do |path| each_file_in_path(".rbi", path) do |file_path| file = Project::SignatureFile.new(path: file_path) file.content = file_path.read project.signature_files[file_path] = file end end end end |
#respond_to_hover(uri:, line:, column:, notifier:, id:) ⇒ Object
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/steep/drivers/langserver.rb', line 63 def respond_to_hover(uri:, line:, column:, notifier:, id:) path = Pathname(uri.path).relative_path_from(Pathname.pwd) if path.extname == ".rb" # line in LSP is zero-origin project.type_of(path: path, line: line + 1, column: column) do |type, node| Steep.logger.warn "type = #{type.to_s}" start_position = { line: node.location.line - 1, character: node.location.column } end_position = { line: node.location.last_line - 1, character: node.location.last_column } range = { start: start_position, end: end_position } Steep.logger.warn "node = #{node.type}" Steep.logger.warn "range = #{range.inspect}" LanguageServer::Protocol::Interface::Hover.new( contents: { kind: "markdown", value: "`#{type}`" }, range: range ) end end end |
#run ⇒ Object
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/steep/drivers/langserver.rb', line 122 def run writer = LanguageServer::Protocol::Transport::Stdio::Writer.new reader = LanguageServer::Protocol::Transport::Stdio::Reader.new notifier = Proc.new { |method:, params: {}| writer.write(method: method, params: params) } reader.read do |request| id = request[:id] method = request[:method].to_sym Steep.logger.warn "Received event: #{method}" subscriber = subscribers[method] if subscriber result = subscriber.call(request: request, notifier: notifier) if id && result writer.write(id: id, result: result) end else Steep.logger.warn "Ignored event: #{method}" end end end |
#subscribe(method, &callback) ⇒ Object
86 87 88 |
# File 'lib/steep/drivers/langserver.rb', line 86 def subscribe(method, &callback) @subscribers[method] = callback end |
#synchronize_project(uri:, text:, notifier:) ⇒ Object
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/steep/drivers/langserver.rb', line 143 def synchronize_project(uri:, text:, notifier:) path = Pathname(uri.path).relative_path_from(Pathname.pwd) case path.extname when ".rb" file = project.source_files[path] || Project::SourceFile.new(path: path, options: ) file.content = text project.source_files[path] = file when ".rbi" file = project.signature_files[path] || Project::SignatureFile.new(path: path) file.content = text project.signature_files[path] = file end project.type_check open_paths.each do |uri| Pathname(uri.path).relative_path_from(Pathname.pwd).yield_self do |path| case path.extname when ".rb" file = project.source_files[path] || Project::SourceFile.new(path: path, options: ) diags = (file.errors || []).map do |error| LanguageServer::Protocol::Interface::Diagnostic.new( message: error.to_s, severity: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR, range: LanguageServer::Protocol::Interface::Range.new( start: LanguageServer::Protocol::Interface::Position.new( line: error.node.loc.line - 1, character: error.node.loc.column, ), end: LanguageServer::Protocol::Interface::Position.new( line: error.node.loc.last_line - 1, character: error.node.loc.last_column, ), ) ) end notifier.call( method: :"textDocument/publishDiagnostics", params: LanguageServer::Protocol::Interface::PublishDiagnosticsParams.new( uri: uri, diagnostics: diags, ), ) end end end end |