Class: RubyLanguageServer::ProjectManager
- Inherits:
-
Object
- Object
- RubyLanguageServer::ProjectManager
- Defined in:
- lib/ruby_language_server/project_manager.rb
Constant Summary collapse
- ROOT_PATH_MUTEX =
GoodCop wants to know where to find its config. So here we are.
Mutex.new
Instance Attribute Summary collapse
-
#uri_code_file_hash ⇒ Object
readonly
Returns the value of attribute uri_code_file_hash.
Class Method Summary collapse
Instance Method Summary collapse
- #all_scopes ⇒ Object
- #completion_at(uri, position) ⇒ Object
-
#context_at_location(uri, position) ⇒ Object
Returns the context of what is being typed in the given line.
- #diagnostics_ready? ⇒ Boolean
-
#initialize(path, uri = nil) ⇒ ProjectManager
constructor
A new instance of ProjectManager.
- #install_additional_gems(gem_names) ⇒ Object
- #possible_definitions(uri, position) ⇒ Object
- #project_definitions_for(name) ⇒ Object
- #root_scope_for(uri) ⇒ Object
-
#scan_all_project_files(mutex) ⇒ Object
interface CompletionItem { /** * The label of this completion item. By default * also the text that is inserted when selecting * this completion. */ label: string; /** * The kind of this completion item. Based of the kind * an icon is chosen by the editor. */ kind?: number; /** * A human-readable string with additional information * about this item, like type or symbol information. */ detail?: string; /** * A human-readable string that represents a doc-comment. */ documentation?: string; /** * A string that shoud be used when comparing this item * with other items. When
falsythe label is used. */ sortText?: string; /** * A string that should be used when filtering a set of * completion items. Whenfalsythe label is used. */ filterText?: string; /** * A string that should be inserted a document when selecting * this completion. Whenfalsythe label is used. */ insertText?: string; /** * The format of the insert text. The format applies to both theinsertTextproperty * and thenewTextproperty of a providedtextEdit. */ insertTextFormat?: InsertTextFormat; /** * An edit which is applied to a document when selecting this completion. When an edit is provided the value of *insertTextis ignored. * * Note: The range of the edit must be a single line range and it must contain the position at which completion * has been requested. */ textEdit?: TextEdit; /** * An optional array of additional text edits that are applied when * selecting this completion. Edits must not overlap with the main edit * nor with themselves. */ additionalTextEdits?: TextEdit[]; /** * An optional set of characters that when pressed while this completion is active will accept it first and * then type that character. Note that all commit characters should have ‘length=1` and that superfluous * characters will be ignored. */ commitCharacters?: string[]; /** * An optional command that is executed after inserting this completion. Note that * additional modifications to the current document should be described with the * additionalTextEdits-property. */ command?: Command; /** * An data entry field that is preserved on a completion item between * a completion and a completion resolve request. */ data?: any }. - #scope_definitions_for(name, scope, uri) ⇒ Object
-
#scopes_at(uri, position) ⇒ Object
Return the list of scopes [deepest, parent, …, Object].
- #tags_for_uri(uri) ⇒ Object
- #text_for_uri(uri) ⇒ Object
-
#update_document_content(uri, text) ⇒ Object
returns diagnostic info (if possible).
- #updated_diagnostics_for_codefile(code_file) ⇒ Object
- #word_at_location(uri, position) ⇒ Object
Constructor Details
#initialize(path, uri = nil) ⇒ ProjectManager
Returns a new instance of ProjectManager.
45 46 47 48 49 50 51 52 53 54 |
# File 'lib/ruby_language_server/project_manager.rb', line 45 def initialize(path, uri = nil) # Should probably lock for read, but I'm feeling crazy! self.class.root_path = path if self.class.root_path.nil? self.class.root_uri = uri if uri @root_uri = "file://#{path}" # This is {uri: code_file} where content stuff is like @additional_gems_installed = false @additional_gem_mutex = Mutex.new end |
Instance Attribute Details
#uri_code_file_hash ⇒ Object (readonly)
Returns the value of attribute uri_code_file_hash.
9 10 11 |
# File 'lib/ruby_language_server/project_manager.rb', line 9 def uri_code_file_hash @uri_code_file_hash end |
Class Method Details
.root_path ⇒ Object
22 23 24 25 26 27 28 29 |
# File 'lib/ruby_language_server/project_manager.rb', line 22 def root_path # I'm torn about this. Should this be set in the Server? Or is this right. # Rather than worry too much, I'll just do this here and change it later if it feels wrong. path = ENV['RUBY_LANGUAGE_SERVER_PROJECT_ROOT'] || @_root_path return path if path.nil? path.end_with?(File::SEPARATOR) ? path : "#{path}#{File::SEPARATOR}" end |
.root_path=(path) ⇒ Object
16 17 18 19 20 |
# File 'lib/ruby_language_server/project_manager.rb', line 16 def root_path=(path) ROOT_PATH_MUTEX.synchronize do @_root_path = path end end |
.root_uri ⇒ Object
40 41 42 |
# File 'lib/ruby_language_server/project_manager.rb', line 40 def root_uri @_root_uri || "file://#{root_path}" end |
.root_uri=(uri) ⇒ Object
31 32 33 34 35 36 37 38 |
# File 'lib/ruby_language_server/project_manager.rb', line 31 def root_uri=(uri) ROOT_PATH_MUTEX.synchronize do if uri uri = "#{uri}/" unless uri.end_with?('/') @_root_uri = uri end end end |
Instance Method Details
#all_scopes ⇒ Object
87 88 89 |
# File 'lib/ruby_language_server/project_manager.rb', line 87 def all_scopes RubyLanguageServer::ScopeData::Scope.all end |
#completion_at(uri, position) ⇒ Object
98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/ruby_language_server/project_manager.rb', line 98 def completion_at(uri, position) relative_position = position.dup relative_position.character = relative_position.character # To get before the . or :: # RubyLanguageServer.logger.debug("relative_position #{relative_position}") RubyLanguageServer.logger.debug("scopes_at(uri, position) #{scopes_at(uri, position).map(&:name)}") position_scopes = scopes_at(uri, position) || RubyLanguageServer::ScopeData::Scope.where(id: root_scope_for(uri).id) context_scope = position_scopes.first context = context_at_location(uri, relative_position) return {} if context.nil? || context == '' RubyLanguageServer::Completion.completion(context, context_scope, position_scopes) end |
#context_at_location(uri, position) ⇒ Object
Returns the context of what is being typed in the given line
226 227 228 229 |
# File 'lib/ruby_language_server/project_manager.rb', line 226 def context_at_location(uri, position) code_file = code_file_for_uri(uri) code_file&.context_at_location(position) end |
#diagnostics_ready? ⇒ Boolean
56 57 58 |
# File 'lib/ruby_language_server/project_manager.rb', line 56 def diagnostics_ready? @additional_gem_mutex.synchronize { @additional_gems_installed } end |
#install_additional_gems(gem_names) ⇒ Object
60 61 62 63 64 65 66 67 |
# File 'lib/ruby_language_server/project_manager.rb', line 60 def install_additional_gems(gem_names) Thread.new do RubyLanguageServer::GemInstaller.install_gems(gem_names) @additional_gem_mutex.synchronize { @additional_gems_installed = true } rescue StandardError => e RubyLanguageServer.logger.error("Issue installing rubocop gems: #{e}") end end |
#possible_definitions(uri, position) ⇒ Object
235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/ruby_language_server/project_manager.rb', line 235 def possible_definitions(uri, position) name = word_at_location(uri, position) return {} if name.blank? name = 'initialize' if name == 'new' scope = scopes_at(uri, position).first results = scope_definitions_for(name, scope, uri) return results unless results.empty? project_definitions_for(name) end |
#project_definitions_for(name) ⇒ Object
260 261 262 263 264 265 266 |
# File 'lib/ruby_language_server/project_manager.rb', line 260 def project_definitions_for(name) scopes = RubyLanguageServer::ScopeData::Scope.where(name: name) variables = RubyLanguageServer::ScopeData::Variable.constant_variables.where(name: name) (scopes + variables).reject { |scope| scope.code_file.nil? }.map do |scope| Location.hash(scope.code_file.uri.delete_prefix(self.class.root_uri), scope.top_line, 1) end end |
#root_scope_for(uri) ⇒ Object
81 82 83 84 85 |
# File 'lib/ruby_language_server/project_manager.rb', line 81 def root_scope_for(uri) code_file = code_file_for_uri(uri) RubyLanguageServer.logger.error('code_file.nil?!!!!!!!!!!!!!!') if code_file.nil? code_file&.root_scope end |
#scan_all_project_files(mutex) ⇒ Object
interface CompletionItem
/**
* The label of this completion item. By default
* also the text that is inserted when selecting
* this completion.
*/
label: string;
/**
* The kind of this completion item. Based of the kind
* an icon is chosen by the editor.
*/
kind?: number;
/**
* A human-readable string with additional information
* about this item, like type or symbol information.
*/
detail?: string;
/**
* A human-readable string that represents a doc-comment.
*/
documentation?: string;
/**
* A string that shoud be used when comparing this item
* with other items. When `falsy` the label is used.
*/
sortText?: string;
/**
* A string that should be used when filtering a set of
* completion items. When `falsy` the label is used.
*/
filterText?: string;
/**
* A string that should be inserted a document when selecting
* this completion. When `falsy` the label is used.
*/
insertText?: string;
/**
* The format of the insert text. The format applies to both the `insertText` property
* and the `newText` property of a provided `textEdit`.
*/
insertTextFormat?: InsertTextFormat;
/**
* An edit which is applied to a document when selecting this completion. When an edit is provided the value of
* `insertText` is ignored.
*
* *Note:* The range of the edit must be a single line range and it must contain the position at which completion
* has been requested.
*/
textEdit?: TextEdit;
/**
* An optional array of additional text edits that are applied when
* selecting this completion. Edits must not overlap with the main edit
* nor with themselves.
*/
additionalTextEdits?: TextEdit[];
/**
* An optional set of characters that when pressed while this completion is active will accept it first and
* then type that character. *Note* that all commit characters should have `length=1` and that superfluous
* characters will be ignored.
*/
commitCharacters?: string[];
/**
* An optional command that is executed *after* inserting this completion. *Note* that
* additional modifications to the current document should be described with the
* additionalTextEdits-property.
*/
command?: Command;
/**
* An data entry field that is preserved on a completion item between
* a completion and a completion resolve request.
*/
data?: any
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
# File 'lib/ruby_language_server/project_manager.rb', line 185 def scan_all_project_files(mutex) project_ruby_files = Dir.glob("#{self.class.root_path}**/*.rb") Thread.new do RubyLanguageServer.logger.error('Threading up!') project_ruby_files.each do |container_path| # Let's not preload spec/test or vendor - yet.. next if container_path.match?(/^(.?spec|test|vendor)/) text = File.read(container_path) relative_path = container_path.delete_prefix(self.class.root_path) host_uri = @root_uri + relative_path RubyLanguageServer.logger.debug "Locking scan for #{container_path}" mutex.synchronize do RubyLanguageServer.logger.debug("Threading #{host_uri}") update_document_content(host_uri, text) code_file_for_uri(host_uri).refresh_scopes_if_needed end RubyLanguageServer.logger.debug "Unlocking scan for #{container_path}" end end end |
#scope_definitions_for(name, scope, uri) ⇒ Object
247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/ruby_language_server/project_manager.rb', line 247 def scope_definitions_for(name, scope, uri) check_scope = scope return_array = [] while check_scope scope.variables.each do |variable| return_array << Location.hash(uri.delete_prefix(self.class.root_uri), variable.line, 1) if variable.name == name end check_scope = check_scope.parent end RubyLanguageServer.logger.debug("scope_definitions_for(#{name}, #{scope}, #{uri}: #{return_array.uniq})") return_array.uniq end |
#scopes_at(uri, position) ⇒ Object
Return the list of scopes [deepest, parent, …, Object]
92 93 94 95 96 |
# File 'lib/ruby_language_server/project_manager.rb', line 92 def scopes_at(uri, position) code_file = code_file_for_uri(uri) code_file.refresh_scopes_if_needed code_file.scopes.for_line(position.line).where.not(path: nil).by_path_length end |
#tags_for_uri(uri) ⇒ Object
74 75 76 77 78 79 |
# File 'lib/ruby_language_server/project_manager.rb', line 74 def (uri) code_file = code_file_for_uri(uri) return {} if code_file.nil? code_file. end |
#text_for_uri(uri) ⇒ Object
69 70 71 72 |
# File 'lib/ruby_language_server/project_manager.rb', line 69 def text_for_uri(uri) code_file = code_file_for_uri(uri) code_file&.text || '' end |
#update_document_content(uri, text) ⇒ Object
returns diagnostic info (if possible)
208 209 210 211 212 213 214 215 216 |
# File 'lib/ruby_language_server/project_manager.rb', line 208 def update_document_content(uri, text) RubyLanguageServer.logger.debug("update_document_content: #{uri}") # RubyLanguageServer.logger.error("@root_path: #{@root_path}") code_file = code_file_for_uri(uri) return code_file.diagnostics if code_file.text == text code_file.update_text(text) diagnostics_ready? ? updated_diagnostics_for_codefile(code_file) : [] end |
#updated_diagnostics_for_codefile(code_file) ⇒ Object
218 219 220 221 222 223 |
# File 'lib/ruby_language_server/project_manager.rb', line 218 def updated_diagnostics_for_codefile(code_file) # Maybe we should be sharing this GoodCop across instances @good_cop ||= GoodCop.new project_relative_filename = filename_relative_to_project(code_file.uri) code_file.diagnostics = @good_cop.diagnostics(code_file.text, project_relative_filename) end |
#word_at_location(uri, position) ⇒ Object
231 232 233 |
# File 'lib/ruby_language_server/project_manager.rb', line 231 def word_at_location(uri, position) context_at_location(uri, position)&.last end |