Skip to content

Commit

Permalink
(puppetlabsGH-33) Add Code Action provider for ignoring Lint Errors
Browse files Browse the repository at this point in the history
TODO
  • Loading branch information
glennsarti committed Feb 3, 2019
1 parent 99fce1d commit a35ea3b
Show file tree
Hide file tree
Showing 7 changed files with 264 additions and 1 deletion.
122 changes: 122 additions & 0 deletions lib/languageserver/code_action.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
module LanguageServer
# /**
# * Params for the CodeActionRequest
# */
# interface CodeActionParams {
# /**
# * The document in which the command was invoked.
# */
# textDocument: TextDocumentIdentifier;

# /**
# * The range for which the command was invoked.
# */
# range: Range;

# /**
# * Context carrying additional information.
# */
# context: CodeActionContext;
# }
module CodeActionRequest
def self.create(options)
result = {}
raise('textDocument is a required field for CodeActionRequest') if options['textDocument'].nil?
raise('range is a required field for CodeActionRequest') if options['range'].nil?
raise('context is a required field for CodeActionRequest') if options['context'].nil?

result['textDocument'] = options['textDocument']
result['range'] = options['range']
result['context'] = CodeActionContext.create(options['context'])

result
end
end

# /**
# * Contains additional diagnostic information about the context in which
# * a code action is run.
# */
# interface CodeActionContext {
# /**
# * An array of diagnostics.
# */
# diagnostics: Diagnostic[];

# /**
# * Requested kind of actions to return.
# *
# * Actions not of this kind are filtered out by the client before being shown. So servers
# * can omit computing them.
# */
# only?: CodeActionKind[];
# }
module CodeActionContext
def self.create(options)
result = {}
raise('diagnostics is a required field for CodeActionContext') if options['diagnostics'].nil?

result['diagnostics'] = []

options['diagnostics'].each do |diag|
# TODO: Should really ask Diagnostic.create to create the object
result['diagnostics'] << diag
end
result['only'] = options['only'] unless options['only'].nil?

result
end
end

# /**
# * A code action represents a change that can be performed in code, e.g. to fix a problem or
# * to refactor code.
# *
# * A CodeAction must set either `edit` and/or a `command`. If both are supplied, the `edit` is applied first, then the `command` is executed.
# */
# export interface CodeAction {

# /**
# * A short, human-readable, title for this code action.
# */
# title: string;

# /**
# * The kind of the code action.
# *
# * Used to filter code actions.
# */
# kind?: CodeActionKind;

# /**
# * The diagnostics that this code action resolves.
# */
# diagnostics?: Diagnostic[];

# /**
# * The workspace edit this code action performs.
# */
# edit?: WorkspaceEdit;

# /**
# * A command this code action executes. If a code action
# * provides an edit and a command, first the edit is
# * executed and then the command.
# */
# command?: Command;
# }
module CodeAction
def self.create(options)
result = {}
raise('title is a required field for CodeAction') if options['title'].nil?

result['title'] = options['title']
result['kind'] = options['kind'] unless options['kind'].nil?
result['diagnostics'] = options['diagnostics'] unless options['diagnostics'].nil?
result['edit'] = options['edit'] unless options['edit'].nil?
result['command'] = options['command'] unless options['command'].nil?

result
end
end
end
73 changes: 73 additions & 0 deletions lib/languageserver/constants.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,77 @@ module LanguageServer
MESSAGE_TYPE_WARNING = 2
MESSAGE_TYPE_INFO = 3
MESSAGE_TYPE_LOG = 2

# /**
# * A set of predefined code action kinds
# */
# export namespace CodeActionKind {
# /**
# * Base kind for quickfix actions: 'quickfix'
# */
# export const QuickFix: CodeActionKind = 'quickfix';

# /**
# * Base kind for refactoring actions: 'refactor'
# */
# export const Refactor: CodeActionKind = 'refactor';

# /**
# * Base kind for refactoring extraction actions: 'refactor.extract'
# *
# * Example extract actions:
# *
# * - Extract method
# * - Extract function
# * - Extract variable
# * - Extract interface from class
# * - ...
# */
# export const RefactorExtract: CodeActionKind = 'refactor.extract';

# /**
# * Base kind for refactoring inline actions: 'refactor.inline'
# *
# * Example inline actions:
# *
# * - Inline function
# * - Inline variable
# * - Inline constant
# * - ...
# */
# export const RefactorInline: CodeActionKind = 'refactor.inline';

# /**
# * Base kind for refactoring rewrite actions: 'refactor.rewrite'
# *
# * Example rewrite actions:
# *
# * - Convert JavaScript function to class
# * - Add or remove parameter
# * - Encapsulate field
# * - Make method static
# * - Move method to base class
# * - ...
# */
# export const RefactorRewrite: CodeActionKind = 'refactor.rewrite';

# /**
# * Base kind for source actions: `source`
# *
# * Source code actions apply to the entire file.
# */
# export const Source: CodeActionKind = 'source';

# /**
# * Base kind for an organize imports source action: `source.organizeImports`
# */
# export const SourceOrganizeImports: CodeActionKind = 'source.organizeImports';
# }
CODEACTIONKIND_QUICKFIX = 'quickfix'.freeze
CODEACTIONKIND_REFACTOR = 'refactor'.freeze
CODEACTIONKIND_REFACTOREXTRACT = 'refactor.extract'.freeze
CODEACTIONKIND_REFACTORINLINE = 'refactor.inline'.freeze
CODEACTIONKIND_REFACTORREWRITE = 'refactor.rewrite'.freeze
CODEACTIONKIND_SOURCE = 'source'.freeze
CODEACTIONKIND_SOURCEORGANIZEIMPORS = 'source.organizeImports'.freeze
end
2 changes: 1 addition & 1 deletion lib/languageserver/languageserver.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
%w[constants diagnostic completion_list completion_item document_symbol hover location puppet_version puppet_compilation puppet_fix_diagnostic_errors].each do |lib|
%w[constants diagnostic code_action completion_list completion_item document_symbol hover location puppet_version puppet_compilation puppet_fix_diagnostic_errors].each do |lib|
begin
require "languageserver/#{lib}"
rescue LoadError
Expand Down
51 changes: 51 additions & 0 deletions lib/puppet-languageserver/manifest/code_action_provider.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
module PuppetLanguageServer
module Manifest
module CodeActionProvider
def self.provide_actions(code_action_request)
# We only provide actions on diagnostics
return nil if code_action_request['context']['diagnostics'].empty?
file_uri = code_action_request['textDocument']['uri']
content = PuppetLanguageServer::DocumentStore.document(file_uri)
# We only providea actions on files that are being edited
return nil if content.nil?
content_version = PuppetLanguageServer::DocumentStore.document_version(file_uri)
# Extract the per line information
content_lines = content.lines

result = []
code_action_request['context']['diagnostics'].each do |diag|
text_change = " # lint:ignore:#{diag['code']}"
diag_start_line = diag['range']['start']['line']
result << LanguageServer::CodeAction.create(
'title' => " Ignore '#{diag['message']}'",
'edit' => {
'documentChanges' => [{
'textDocument' => {
'uri' => file_uri,
'version' => content_version
},
'edits' => [
{
'range' => {
'start' => { 'line' => diag_start_line, 'character' => line_length(content_lines, diag_start_line) },
'end' => { 'line' => diag_start_line, 'character' => line_length(content_lines, diag_start_line) + 1 }
},
'newText' => text_change
}
]
}]
},
'kind' => LanguageServer::CODEACTIONKIND_REFACTORINLINE,
'diagnostics' => [diag]
)
end

result
end

def self.line_length(content_lines, line_number)
content_lines[line_number].chomp.length
end
end
end
end
15 changes: 15 additions & 0 deletions lib/puppet-languageserver/message_router.rb
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,21 @@ def receive_request(request)
request.reply_result(LanguageServer::Hover.create_nil_response)
end

when 'textDocument/codeAction'
begin
request_object = LanguageServer::CodeActionRequest.create(request.params)
file_uri = request_object['textDocument']['uri']
case documents.document_type(file_uri)
when :manifest
request.reply_result(PuppetLanguageServer::Manifest::CodeActionProvider.provide_actions(request_object))
else
raise "Unable to provide code actions on #{file_uri}"
end
rescue StandardError => exception
PuppetLanguageServer.log_message(:error, "(textDocument/codeAction) #{exception}")
request.reply_result(nil)
end

when 'textDocument/definition'
file_uri = request.params['textDocument']['uri']
line_num = request.params['position']['line']
Expand Down
1 change: 1 addition & 0 deletions lib/puppet-languageserver/providers.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
%w[
epp/validation_provider
manifest/code_action_provider
manifest/completion_provider
manifest/definition_provider
manifest/document_symbol_provider
Expand Down
1 change: 1 addition & 0 deletions lib/puppet-languageserver/server_capabilities.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ def self.capabilities
{
'textDocumentSync' => LanguageServer::TEXTDOCUMENTSYNCKIND_FULL,
'hoverProvider' => true,
'codeActionProvider' => true,
'completionProvider' => {
'resolveProvider' => true,
'triggerCharacters' => ['>', '$', '[', '=']
Expand Down

0 comments on commit a35ea3b

Please sign in to comment.