diff --git a/lib/languageserver/document_symbol.rb b/lib/languageserver/document_symbol.rb index d8560bfa..455c33d7 100644 --- a/lib/languageserver/document_symbol.rb +++ b/lib/languageserver/document_symbol.rb @@ -76,4 +76,62 @@ def self.create(options) result end end + + # /** + # * Represents information about programming constructs like variables, classes, + # * interfaces etc. + # */ + # interface SymbolInformation { + # /** + # * The name of this symbol. + # */ + # name: string; + + # /** + # * The kind of this symbol. + # */ + # kind: number; + + # /** + # * Indicates if this symbol is deprecated. + # */ + # deprecated?: boolean; + + # /** + # * The location of this symbol. The location's range is used by a tool + # * to reveal the location in the editor. If the symbol is selected in the + # * tool the range's start information is used to position the cursor. So + # * the range usually spans more then the actual symbol's name and does + # * normally include things like visibility modifiers. + # * + # * The range doesn't have to denote a node range in the sense of a abstract + # * syntax tree. It can therefore not be used to re-construct a hierarchy of + # * the symbols. + # */ + # location: Location; + + # /** + # * The name of the symbol containing this symbol. This information is for + # * user interface purposes (e.g. to render a qualifier in the user interface + # * if necessary). It can't be used to re-infer a hierarchy for the document + # * symbols. + # */ + # containerName?: string; + # } + module SymbolInformation + def self.create(options) + result = {} + raise('name is a required field for SymbolInformation') if options['name'].nil? + raise('kind is a required field for SymbolInformation') if options['kind'].nil? + raise('location is a required field for DocumentSymbol') if options['location'].nil? + + result['name'] = options['name'] + result['kind'] = options['kind'] + result['deprecated'] = options['deprecated'] unless options['deprecated'].nil? + result['location'] = options['location'] + result['containerName'] = options['containerName'] unless options['containerName'].nil? + + result + end + end end diff --git a/lib/puppet-languageserver/manifest/document_symbol_provider.rb b/lib/puppet-languageserver/manifest/document_symbol_provider.rb index bd322ddb..9889b9ef 100644 --- a/lib/puppet-languageserver/manifest/document_symbol_provider.rb +++ b/lib/puppet-languageserver/manifest/document_symbol_provider.rb @@ -1,6 +1,56 @@ module PuppetLanguageServer module Manifest module DocumentSymbolProvider + def self.workspace_symbols(query) + query = '' if query.nil? + result = [] + PuppetLanguageServer::PuppetHelper.all_objects do |key, item| + key_string = key.to_s + next unless key_string.include?(query) + case item + when PuppetLanguageServer::PuppetHelper::PuppetType + result << LanguageServer::SymbolInformation.create( + 'name' => key_string, + 'kind' => LanguageServer::SYMBOLKIND_METHOD, + 'location' => LanguageServer::Location.create( + 'uri' => PuppetLanguageServer::UriHelper.build_file_uri(item.source), + 'fromline' => item.line, + 'fromchar' => 0, # Don't have char pos for types + 'toline' => item.line, + 'tochar' => 1024, # Don't have char pos for types + ) + ) + + when PuppetLanguageServer::PuppetHelper::PuppetFunction + result << LanguageServer::SymbolInformation.create( + 'name' => key_string, + 'kind' => LanguageServer::SYMBOLKIND_FUNCTION, + 'location' => LanguageServer::Location.create( + 'uri' => PuppetLanguageServer::UriHelper.build_file_uri(item.source), + 'fromline' => item.line, + 'fromchar' => 0, # Don't have char pos for functions + 'toline' => item.line, + 'tochar' => 1024, # Don't have char pos for functions + ) + ) + + when PuppetLanguageServer::PuppetHelper::PuppetClass + result << LanguageServer::SymbolInformation.create( + 'name' => key_string, + 'kind' => LanguageServer::SYMBOLKIND_CLASS, + 'location' => LanguageServer::Location.create( + 'uri' => PuppetLanguageServer::UriHelper.build_file_uri(item.source), + 'fromline' => item.line, + 'fromchar' => 0, # Don't have char pos for classes + 'toline' => item.line, + 'tochar' => 1024, # Don't have char pos for classes + ) + ) + end + end + result + end + def self.extract_document_symbols(content) parser = Puppet::Pops::Parser::Parser.new result = parser.parse_string(content, '') diff --git a/lib/puppet-languageserver/message_router.rb b/lib/puppet-languageserver/message_router.rb index a8add497..5f2dee52 100644 --- a/lib/puppet-languageserver/message_router.rb +++ b/lib/puppet-languageserver/message_router.rb @@ -164,6 +164,17 @@ def receive_request(request) PuppetLanguageServer.log_message(:error, "(textDocument/documentSymbol) #{exception}") request.reply_result(nil) end + + when 'workspace/symbol' + begin + result = [] + result.concat(PuppetLanguageServer::Manifest::DocumentSymbolProvider.workspace_symbols(request.params['query'])) + request.reply_result(result) + rescue StandardError => exception + PuppetLanguageServer.log_message(:error, "(workspace/symbol) #{exception}") + request.reply_result([]) + end + else PuppetLanguageServer.log_message(:error, "Unknown RPC method #{request.rpc_method}") end diff --git a/lib/puppet-languageserver/puppet_helper.rb b/lib/puppet-languageserver/puppet_helper.rb index 15d59404..d7f3d4f4 100644 --- a/lib/puppet-languageserver/puppet_helper.rb +++ b/lib/puppet-languageserver/puppet_helper.rb @@ -26,6 +26,14 @@ def self.initialize_helper(options = {}) sidecar_queue.cache = @inmemory_cache end + def self.all_objects(&_block) + return nil if @default_types_loaded == false + raise('Puppet Helper Cache has not been configured') if @inmemory_cache.nil? + @inmemory_cache.all_objects do |key, item| + yield key, item + end + end + # Node Graph def self.get_node_graph(content, local_workspace) with_temporary_file(content) do |filepath| diff --git a/lib/puppet-languageserver/puppet_helper/cache.rb b/lib/puppet-languageserver/puppet_helper/cache.rb index e804c0ce..6834be1f 100644 --- a/lib/puppet-languageserver/puppet_helper/cache.rb +++ b/lib/puppet-languageserver/puppet_helper/cache.rb @@ -75,6 +75,14 @@ def objects_by_section(section, &_block) end end + def all_objects(&_block) + @cache_lock.synchronize do + @inmemory_cache.each do |item| + yield item.key, item + end + end + end + private # diff --git a/lib/puppet-languageserver/server_capabilities.rb b/lib/puppet-languageserver/server_capabilities.rb index 9eb01485..53ce85a9 100644 --- a/lib/puppet-languageserver/server_capabilities.rb +++ b/lib/puppet-languageserver/server_capabilities.rb @@ -4,14 +4,15 @@ def self.capabilities # https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#initialize-request { - 'textDocumentSync' => LanguageServer::TEXTDOCUMENTSYNCKIND_FULL, - 'hoverProvider' => true, - 'completionProvider' => { + 'textDocumentSync' => LanguageServer::TEXTDOCUMENTSYNCKIND_FULL, + 'hoverProvider' => true, + 'completionProvider' => { 'resolveProvider' => true, 'triggerCharacters' => ['>', '$', '[', '='] }, - 'definitionProvider' => true, - 'documentSymbolProvider' => true + 'definitionProvider' => true, + 'documentSymbolProvider' => true, + 'workspaceSymbolProvider' => true } end end diff --git a/spec/languageserver/spec_helper.rb b/spec/languageserver/spec_helper.rb index 815bba64..679a57e3 100644 --- a/spec/languageserver/spec_helper.rb +++ b/spec/languageserver/spec_helper.rb @@ -24,7 +24,7 @@ def wait_for_puppet_loading PuppetLanguageServer::PuppetHelper.default_classes_loaded? sleep(1) interation += 1 - next if interation < 60 + next if interation < 90 raise <<-ERRORMSG Puppet has not be initialised in time: functions_loaded? = #{PuppetLanguageServer::PuppetHelper.default_functions_loaded?}