Class: PlatformosCheck::LanguageServer::Handler

Inherits:
Object
  • Object
show all
Includes:
URIHelper
Defined in:
lib/platformos_check/language_server/handler.rb

Constant Summary collapse

SERVER_INFO =
{
  name: $PROGRAM_NAME,
  version: PlatformosCheck::VERSION
}
FILE_OPERATION_FILTER =
{
  filters: [{
    scheme: 'file',
    pattern: {
      glob: '**/*'
    }
  }]
}
CAPABILITIES =
{
  completionProvider: {
    triggerCharacters: ['.', '{{ ', '{% ', '/'],
    context: true
  },
  codeActionProvider: {
    codeActionKinds: CodeActionProvider.all.map(&:kind),
    resolveProvider: false,
    workDoneProgress: false
  },
  documentLinkProvider: true,
  hoverProvider: true,
  executeCommandProvider: {
    workDoneProgress: false,
    commands: ExecuteCommandProvider.all.map(&:command)
  },
  textDocumentSync: {
    openClose: true,
    change: TextDocumentSyncKind::FULL,
    willSave: false,
    save: true
  },
  workspace: {
    fileOperations: {
      didCreate: FILE_OPERATION_FILTER,
      didDelete: FILE_OPERATION_FILTER,
      willRename: FILE_OPERATION_FILTER
    }
  }
}

Instance Method Summary collapse

Methods included from URIHelper

#file_path, #file_uri

Constructor Details

#initialize(bridge) ⇒ Handler

Returns a new instance of Handler.



56
57
58
# File 'lib/platformos_check/language_server/handler.rb', line 56

def initialize(bridge)
  @bridge = bridge
end

Instance Method Details

#on_exit(_id, _params) ⇒ Object



103
104
105
# File 'lib/platformos_check/language_server/handler.rb', line 103

def on_exit(_id, _params)
  close!
end

#on_initialize(id, params) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/platformos_check/language_server/handler.rb', line 60

def on_initialize(id, params)
  @root_path = root_path_from_params(params)

  # Tell the client we don't support anything if there's no rootPath
  return @bridge.send_response(id, { capabilities: {} }) if @root_path.nil?

  @client_capabilities = ClientCapabilities.new(params.dig(:capabilities) || {})
  @configuration = Configuration.new(@bridge, @client_capabilities)
  @bridge.supports_work_done_progress = @client_capabilities.supports_work_done_progress?
  @storage = in_memory_storage(@root_path)
  @diagnostics_manager = DiagnosticsManager.new
  @completion_engine = CompletionEngine.new(@storage, @bridge)
  @hover_engine = HoverEngine.new(@storage, @bridge)
  @document_link_engine = DocumentLinkEngine.new(@storage)
  @diagnostics_engine = DiagnosticsEngine.new(@storage, @bridge, @diagnostics_manager)
  @execute_command_engine = ExecuteCommandEngine.new
  @execute_command_engine << CorrectionExecuteCommandProvider.new(@storage, @bridge, @diagnostics_manager)
  @execute_command_engine << RunChecksExecuteCommandProvider.new(
    @diagnostics_engine,
    @storage,
    config_for_path(@root_path),
    @configuration
  )
  @code_action_engine = CodeActionEngine.new(@storage, @diagnostics_manager)
  @bridge.send_response(id, {
                          capabilities: CAPABILITIES,
                          serverInfo: SERVER_INFO
                        })
end

#on_initialized(_id, _params) ⇒ Object



90
91
92
93
94
95
96
97
# File 'lib/platformos_check/language_server/handler.rb', line 90

def on_initialized(_id, _params)
  return unless @configuration

  @configuration.fetch
  @configuration.register_did_change_capability

  PlatformosLiquid::SourceManager.download_or_refresh_files
end

#on_shutdown(id, _params) ⇒ Object



99
100
101
# File 'lib/platformos_check/language_server/handler.rb', line 99

def on_shutdown(id, _params)
  @bridge.send_response(id, nil)
end

#on_text_document_code_action(id, params) ⇒ Object



157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/platformos_check/language_server/handler.rb', line 157

def on_text_document_code_action(id, params)
  absolute_path = text_document_uri(params)
  start_position = range_element(params, :start)
  end_position = range_element(params, :end)
  only_code_action_kinds = params.dig(:context, :only) || []
  @bridge.send_response(id, @code_action_engine.code_actions(
                              absolute_path,
                              start_position,
                              end_position,
                              only_code_action_kinds
                            ))
end

#on_text_document_completion(id, params) ⇒ Object



150
151
152
153
154
155
# File 'lib/platformos_check/language_server/handler.rb', line 150

def on_text_document_completion(id, params)
  relative_path = relative_path_from_text_document_uri(params)
  line = params.dig(:position, :line)
  col = params.dig(:position, :character)
  @bridge.send_response(id, @completion_engine.completions(relative_path, line, col))
end

#on_text_document_did_change(_id, params) ⇒ Object



113
114
115
116
117
# File 'lib/platformos_check/language_server/handler.rb', line 113

def on_text_document_did_change(_id, params)
  relative_path = relative_path_from_text_document_uri(params)
  @storage.write(relative_path, content_changes_text(params), text_document_version(params))
  analyze_and_send_offenses(text_document_uri(params), only_single_file: true) if @configuration.check_on_change?
end

#on_text_document_did_close(_id, params) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/platformos_check/language_server/handler.rb', line 119

def on_text_document_did_close(_id, params)
  relative_path = relative_path_from_text_document_uri(params)
  begin
    file_system_content = Pathname.new(text_document_uri(params)).read(mode: 'rb', encoding: 'UTF-8')
    # On close, the file system becomes the source of truth
    @storage.write(relative_path, file_system_content, nil)

  # the file no longer exists because either the user deleted it, or the user renamed it.
  rescue Errno::ENOENT
    @storage.remove(relative_path)
  ensure
    @diagnostics_engine.clear_diagnostics(relative_path) if @configuration.only_single_file?
  end
end

#on_text_document_did_open(_id, params) ⇒ Object



107
108
109
110
111
# File 'lib/platformos_check/language_server/handler.rb', line 107

def on_text_document_did_open(_id, params)
  relative_path = relative_path_from_text_document_uri(params)
  @storage.write(relative_path, text_document_text(params), text_document_version(params))
  analyze_and_send_offenses(text_document_uri(params)) if @configuration.check_on_open?
end

#on_text_document_did_save(_id, params) ⇒ Object



134
135
136
# File 'lib/platformos_check/language_server/handler.rb', line 134

def on_text_document_did_save(_id, params)
  analyze_and_send_offenses(text_document_uri(params)) if @configuration.check_on_save?
end


138
139
140
141
# File 'lib/platformos_check/language_server/handler.rb', line 138

def on_text_document_document_link(id, params)
  relative_path = relative_path_from_text_document_uri(params)
  @bridge.send_response(id, @document_link_engine.document_links(relative_path))
end

#on_text_document_hover(id, params) ⇒ Object



143
144
145
146
147
148
# File 'lib/platformos_check/language_server/handler.rb', line 143

def on_text_document_hover(id, params)
  relative_path = relative_path_from_text_document_uri(params)
  line = params.dig(:position, :line)
  col = params.dig(:position, :character)
  @bridge.send_response(id, @hover_engine.completions(relative_path, line, col)[0])
end

#on_workspace_did_change_configuration(_id, _params) ⇒ Object



226
227
228
# File 'lib/platformos_check/language_server/handler.rb', line 226

def on_workspace_did_change_configuration(_id, _params)
  @configuration.fetch(force: true)
end

#on_workspace_did_create_files(_id, params) ⇒ Object



170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/platformos_check/language_server/handler.rb', line 170

def on_workspace_did_create_files(_id, params)
  paths = params[:files]
          &.map { |file| file[:uri] }
          &.map { |uri| file_path(uri) }
  return unless paths

  paths.each do |path|
    next if File.directory?(path)

    relative_path = @storage.relative_path(path)
    file_system_content = Pathname.new(path).read(mode: 'rb', encoding: 'UTF-8')
    @storage.write(relative_path, file_system_content, nil)
  end
end

#on_workspace_did_delete_files(_id, params) ⇒ Object



185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/platformos_check/language_server/handler.rb', line 185

def on_workspace_did_delete_files(_id, params)
  absolute_paths = params[:files]
                   &.map { |file| file[:uri] }
                   &.map { |uri| file_path(uri) }

  return unless absolute_paths

  absolute_paths.each do |path|
    relative_path = @storage.relative_path(path)
    @storage.remove(relative_path)
  end

  analyze_and_send_offenses(absolute_paths)
end

#on_workspace_execute_command(id, params) ⇒ Object



219
220
221
222
223
224
# File 'lib/platformos_check/language_server/handler.rb', line 219

def on_workspace_execute_command(id, params)
  @bridge.send_response(id, @execute_command_engine.execute(
                              params[:command],
                              params[:arguments]
                            ))
end

#on_workspace_will_rename_files(id, params) ⇒ Object

We’re using workspace/willRenameFiles here because we want this to run before textDocument/didOpen and textDocumetn/didClose of the files (which might trigger another platformos_app analysis).



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/platformos_check/language_server/handler.rb', line 203

def on_workspace_will_rename_files(id, params)
  relative_paths = params[:files]
                   &.map { |file| [file[:oldUri], file[:newUri]] }
                   &.map { |(old_uri, new_uri)| [relative_path_from_uri(old_uri), relative_path_from_uri(new_uri)] }
  return @bridge.send_response(id, nil) unless relative_paths

  relative_paths.each do |(old_path, new_path)|
    @storage.write(new_path, @storage.read(old_path), nil)
    @storage.remove(old_path)
  end
  @bridge.send_response(id, nil)

  absolute_paths = relative_paths.flatten(2).map { |p| @storage.path(p) }
  analyze_and_send_offenses(absolute_paths)
end