holistic-ruby 0.1.4 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/lib/holistic/application.rb +12 -4
  4. data/lib/holistic/database/migrations.rb +20 -0
  5. data/lib/holistic/database/node.rb +29 -0
  6. data/lib/holistic/database/table.rb +53 -53
  7. data/lib/holistic/document/file/record.rb +10 -0
  8. data/lib/holistic/document/file/repository.rb +24 -0
  9. data/lib/holistic/document/file/store.rb +13 -0
  10. data/lib/holistic/document/location.rb +4 -6
  11. data/lib/holistic/document/unsaved/record.rb +0 -4
  12. data/lib/holistic/extensions/ruby/stdlib.rb +6 -6
  13. data/lib/holistic/language_server/requests/lifecycle/initialize.rb +5 -10
  14. data/lib/holistic/language_server/requests/text_document/completion.rb +5 -4
  15. data/lib/holistic/language_server/requests/text_document/did_close.rb +5 -9
  16. data/lib/holistic/language_server/requests/text_document/did_open.rb +1 -0
  17. data/lib/holistic/language_server/requests/text_document/did_save.rb +5 -9
  18. data/lib/holistic/language_server/requests/text_document/find_references.rb +7 -4
  19. data/lib/holistic/language_server/requests/text_document/go_to_definition.rb +3 -2
  20. data/lib/holistic/ruby/autocompletion/suggest.rb +40 -20
  21. data/lib/holistic/ruby/parser/constant_resolution.rb +8 -8
  22. data/lib/holistic/ruby/parser/live_editing/process_file_changed.rb +21 -21
  23. data/lib/holistic/ruby/parser/program_visitor.rb +21 -24
  24. data/lib/holistic/ruby/parser.rb +8 -11
  25. data/lib/holistic/ruby/reference/delete.rb +18 -0
  26. data/lib/holistic/ruby/reference/find_referenced_scope.rb +2 -2
  27. data/lib/holistic/ruby/reference/record.rb +7 -8
  28. data/lib/holistic/ruby/reference/repository.rb +19 -41
  29. data/lib/holistic/ruby/reference/store.rb +18 -0
  30. data/lib/holistic/ruby/scope/delete.rb +29 -0
  31. data/lib/holistic/ruby/scope/lexical.rb +11 -0
  32. data/lib/holistic/ruby/scope/list_references.rb +2 -2
  33. data/lib/holistic/ruby/scope/location.rb +9 -9
  34. data/lib/holistic/ruby/scope/outline.rb +8 -8
  35. data/lib/holistic/ruby/scope/record.rb +15 -51
  36. data/lib/holistic/ruby/scope/repository.rb +24 -25
  37. data/lib/holistic/ruby/scope/store.rb +45 -0
  38. data/lib/holistic/ruby/type_inference/processing_queue.rb +19 -0
  39. data/lib/holistic/ruby/type_inference/solve.rb +16 -25
  40. data/lib/holistic/ruby/type_inference/solve_pending_references.rb +3 -1
  41. data/lib/holistic/version.rb +1 -1
  42. metadata +13 -9
  43. data/lib/holistic/document/file.rb +0 -36
  44. data/lib/holistic/ruby/parser/table_of_contents.rb +0 -17
  45. data/lib/holistic/ruby/reference/register.rb +0 -15
  46. data/lib/holistic/ruby/reference/unregister.rb +0 -11
  47. data/lib/holistic/ruby/scope/register.rb +0 -31
  48. data/lib/holistic/ruby/scope/unregister.rb +0 -27
  49. data/lib/holistic/ruby/type_inference/conclusion.rb +0 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: abe4144d1f581c6151784152792604f799c0140d62c28f445f377f6039c40457
4
- data.tar.gz: 6593b92046a8087f75c171280ae66401ff0bb1eb0f6dba9fa496df127bb39ee8
3
+ metadata.gz: 43d41e307a4ae8b8b904fda778342ef5d175db25580697f597f606f3aa2a0173
4
+ data.tar.gz: 5286406cf4e0f5a5cb21745b3f7d49268eb934e4002e76d1ba43f9dfdda034fd
5
5
  SHA512:
6
- metadata.gz: eb1f43eaa7f78c334da3f8d8f2fc782bfa662b94f8a5a6b5a8a24ab271cae26d62703bd722860dbecf637d9aae5d4595d1424257406d9d349f0d90a95ab702d4
7
- data.tar.gz: f42ed13efc65779f8ef175c7127d03cf68bebd5df990a1744d714100efe639cad104b55592507d4737ecfa4d48e75d6b69da194a033f056740a16fde8bf3d65d
6
+ metadata.gz: 882cac6ec7c32d309898aab43f001a3fcf0e497b6c019569472db3880b792e95fe50158b8672690c80237e0a9c6ac361ede5b79a09e2784ddc944d103c54eaba
7
+ data.tar.gz: 64e1becad926aba30e7c9f25b294a2ff9b53dcdfe7affc5e59792054cf21b17b3509eb7fc80e1819fecc9f7d7e22957df3bda2c03990b9e77476c3bc1fa956e0
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- holistic-ruby (0.1.0)
4
+ holistic-ruby (0.1.4)
5
5
  activesupport (~> 7.0)
6
6
  syntax_tree (~> 6.0)
7
7
  zeitwerk (~> 2.6)
@@ -2,12 +2,12 @@
2
2
 
3
3
  module Holistic
4
4
  class Application
5
- attr_reader :name, :root_directory, :root_scope
5
+ attr_reader :name, :root_directory, :database
6
6
 
7
7
  def initialize(name:, root_directory:)
8
8
  @name = name
9
9
  @root_directory = root_directory
10
- @root_scope = Ruby::Scope::Record.new(kind: Ruby::Scope::Kind::ROOT, name: "::", parent: nil)
10
+ @database = Database::Table.new.tap(&Database::Migrations::Run)
11
11
  end
12
12
 
13
13
  def extensions
@@ -15,11 +15,19 @@ module Holistic
15
15
  end
16
16
 
17
17
  def scopes
18
- @scopes ||= Ruby::Scope::Repository.new
18
+ @scopes ||= Ruby::Scope::Repository.new(database:)
19
19
  end
20
20
 
21
21
  def references
22
- @references ||= Ruby::Reference::Repository.new
22
+ @references ||= Ruby::Reference::Repository.new(database:)
23
+ end
24
+
25
+ def files
26
+ @files ||= Document::File::Repository.new(database:)
27
+ end
28
+
29
+ def type_inference_processing_queue
30
+ @type_inference_processing_queue ||= Ruby::TypeInference::ProcessingQueue.new
23
31
  end
24
32
 
25
33
  def unsaved_documents
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Holistic::Database::Migrations
4
+ Run = ->(database) do
5
+ # scope parent-children relation
6
+ database.define_connection(name: :children, inverse_of: :parent)
7
+
8
+ # type inference conclusion
9
+ database.define_connection(name: :referenced_scope, inverse_of: :referenced_by)
10
+
11
+ # reference definition
12
+ database.define_connection(name: :located_in_scope, inverse_of: :contains_many_references)
13
+
14
+ # scope location in files
15
+ database.define_connection(name: :defines_scopes, inverse_of: :scope_defined_in_file)
16
+
17
+ # reference location in files
18
+ database.define_connection(name: :defines_references, inverse_of: :reference_defined_in_file)
19
+ end
20
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Holistic::Database
4
+ class Node
5
+ attr_accessor :attributes, :connections, :__database__
6
+
7
+ def initialize(id, attributes)
8
+ @id = id
9
+ @attributes = attributes
10
+ @connections = ::Hash.new { |hash, key| hash[key] = ::Set.new }
11
+ end
12
+
13
+ def attr(attribute_name)
14
+ @attributes[attribute_name]
15
+ end
16
+
17
+ def has_many(connection_name)
18
+ @connections[connection_name].to_a
19
+ end
20
+
21
+ def has_one(connection_name)
22
+ @connections[connection_name].first
23
+ end
24
+
25
+ def __set_database__(database)
26
+ @__database__ = database
27
+ end
28
+ end
29
+ end
@@ -1,78 +1,78 @@
1
- # frozen_string_literal: true
2
-
3
- class Holistic::Database::Table
4
- attr_reader :primary_attribute, :primary_index, :secondary_indices
5
-
6
- def initialize(primary_attribute:, indices: [])
7
- @primary_attribute = primary_attribute
8
1
 
9
- @primary_index = ::Hash.new
2
+ # frozen_string_literal: true
10
3
 
11
- @secondary_indices = indices.map do |attribute_name|
12
- [attribute_name, ::Hash.new { |hash, key| hash[key] = ::Set.new }]
13
- end.to_h
14
- end
4
+ module Holistic::Database
5
+ class Table
6
+ attr_reader :records, :connections
15
7
 
16
- RecordNotUniqueError = ::Class.new(::StandardError)
8
+ def initialize
9
+ @records = ::Hash.new
10
+ @connections = ::Hash.new
11
+ end
17
12
 
18
- def insert(record)
19
- primary_key = record.fetch(primary_attribute)
13
+ def define_connection(name:, inverse_of:)
14
+ raise ::ArgumentError if @connections.key?(name) || @connections.key?(inverse_of)
20
15
 
21
- if primary_index.key?(primary_key)
22
- raise RecordNotUniqueError, "record already inserted: #{record.inspect}"
16
+ @connections[name] = { inverse_of: }
17
+ @connections[inverse_of] = { inverse_of: name }
23
18
  end
24
19
 
25
- primary_index[primary_key] = record
26
-
27
- secondary_indices.each do |attribute_name, secondary_index|
28
- Array(record[attribute_name]).each do |value|
29
- secondary_index[value].add(primary_key)
20
+ def store(id, node_or_attrs)
21
+ if @records.key?(id)
22
+ return @records[id]&.tap do |node|
23
+ node.attributes =
24
+ case node_or_attrs
25
+ in ::Hash then node_or_attrs
26
+ in Node then node_or_attrs.attributes
27
+ end
28
+ end
30
29
  end
31
- end
32
- end
33
30
 
34
- def find(identifier)
35
- primary_index[identifier]
36
- end
31
+ node =
32
+ case node_or_attrs
33
+ in ::Hash then Node.new(id, node_or_attrs)
34
+ in Node then node_or_attrs
35
+ end
37
36
 
38
- def filter(name, value)
39
- return [] unless secondary_indices[name].key?(value)
37
+ node.__set_database__(self)
40
38
 
41
- secondary_indices.dig(name, value).to_a.map { find(_1) }
42
- end
43
-
44
- def update(record)
45
- primary_key = record.fetch(primary_attribute)
39
+ @records[id] = node
40
+ end
46
41
 
47
- delete(primary_key)
42
+ def connect(source:, target:, name:, inverse_of:)
43
+ connection = @connections[name]
48
44
 
49
- insert(record)
50
- end
45
+ raise ::ArgumentError if connection.nil? || connection[:inverse_of] != inverse_of
51
46
 
52
- def delete(primary_key)
53
- record = find(primary_key)
47
+ source.connections[name].add(target)
48
+ target.connections[inverse_of].add(source)
49
+ end
54
50
 
55
- return if record.nil?
51
+ def disconnect(source:, target:, name:, inverse_of:)
52
+ connection = @connections[name]
56
53
 
57
- primary_index.delete(primary_key)
54
+ raise ::ArgumentError if connection.nil? || connection[:inverse_of] != inverse_of
58
55
 
59
- secondary_indices.each do |attribute_name, index_data|
60
- Array(record[attribute_name]).each do |value|
61
- index_data[value].delete(primary_key)
62
- index_data.delete(value) if index_data[value].empty?
63
- end
56
+ source.connections[name].delete(target)
57
+ target.connections[inverse_of].delete(source)
64
58
  end
65
59
 
66
- record
67
- end
60
+ def find(id)
61
+ @records[id]
62
+ end
68
63
 
69
- concerning :TestHelpers do
70
- def all
71
- primary_index.values
64
+ def delete(id)
65
+ records.delete(id)
72
66
  end
73
67
 
74
- def size
75
- primary_index.size
68
+ concerning :TestHelpers do
69
+ def all
70
+ records.values
71
+ end
72
+
73
+ def size
74
+ records.size
75
+ end
76
76
  end
77
77
  end
78
78
  end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Holistic::Document::File
4
+ class Record < ::Holistic::Database::Node
5
+ def path = attr(:path)
6
+
7
+ def defines_scopes = has_many(:defines_scopes)
8
+ def defines_references = has_many(:defines_references)
9
+ end
10
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ::Holistic::Document::File
4
+ class Repository
5
+ attr_reader :database
6
+
7
+ def initialize(database:)
8
+ @database = database
9
+ end
10
+
11
+ # rename to `find_file`
12
+ def find(file_path)
13
+ @database.find(file_path)
14
+ end
15
+
16
+ concerning :TestHelpers do
17
+ def build_fake_location(file_path)
18
+ file = Store.call(database:, file_path:)
19
+
20
+ ::Holistic::Document::Location.new(file, 0, 0, 0, 0)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Holistic::Document::File
4
+ module Store
5
+ extend self
6
+
7
+ def call(database:, file_path:)
8
+ record = Record.new(file_path, { path: file_path })
9
+
10
+ database.store(file_path, record)
11
+ end
12
+ end
13
+ end
@@ -2,20 +2,18 @@
2
2
 
3
3
  module Holistic::Document
4
4
  Location = ::Data.define(
5
- :file_path,
5
+ :file,
6
6
  :start_line,
7
7
  :start_column,
8
8
  :end_line,
9
9
  :end_column
10
10
  ) do
11
- def self.beginning_of_file(file_path)
12
- new(file_path, 0, 0, 0, 0)
11
+ def identifier
12
+ "#{file.path}[#{start_line},#{start_column},#{end_line},#{end_column}]"
13
13
  end
14
14
 
15
- def identifier = "#{file_path}[#{start_line},#{start_column},#{end_line},#{end_column}]"
16
-
17
15
  def contains?(cursor)
18
- same_file = cursor.file_path == file_path
16
+ same_file = cursor.file_path == file.path
19
17
  contains_line = cursor.line >= start_line && cursor.line <= end_line
20
18
 
21
19
  contains_column =
@@ -75,9 +75,5 @@ module Holistic::Document
75
75
  end
76
76
  end
77
77
  end
78
-
79
- def to_file
80
- File::Fake.new(path:, content:)
81
- end
82
78
  end
83
79
  end
@@ -10,7 +10,7 @@ module Holistic::Extensions::Ruby
10
10
  if method_call_clue.method_name == "new" && referenced_scope.class?
11
11
  initialize_method = "#{referenced_scope.fully_qualified_name}#initialize"
12
12
 
13
- return application.scopes.find_by_fully_qualified_name(initialize_method)
13
+ return application.scopes.find(initialize_method)
14
14
  end
15
15
 
16
16
  nil
@@ -19,11 +19,11 @@ module Holistic::Extensions::Ruby
19
19
  RegisterClassConstructor = ->(application, params) do
20
20
  class_scope, location = params[:class_scope], params[:location]
21
21
 
22
- has_overridden_new_method = class_scope.children.find { _1.class_method? && _1.name == "new" }
22
+ has_overridden_new_method = class_scope.children.find { _1.instance_method? && _1.name == "initialize" }
23
23
 
24
24
  unless has_overridden_new_method
25
- ::Holistic::Ruby::Scope::Register.call(
26
- repository: application.scopes,
25
+ ::Holistic::Ruby::Scope::Store.call(
26
+ database: application.database,
27
27
  parent: class_scope,
28
28
  kind: ::Holistic::Ruby::Scope::Kind::CLASS_METHOD,
29
29
  name: "new",
@@ -38,8 +38,8 @@ module Holistic::Extensions::Ruby
38
38
  lambda_scope, location = params[:lambda_scope], params[:location]
39
39
 
40
40
  LAMBDA_METHODS.each do |method_name|
41
- ::Holistic::Ruby::Scope::Register.call(
42
- repository: application.scopes,
41
+ ::Holistic::Ruby::Scope::Store.call(
42
+ database: application.database,
43
43
  parent: lambda_scope,
44
44
  kind: ::Holistic::Ruby::Scope::Kind::CLASS_METHOD,
45
45
  name: method_name,
@@ -14,23 +14,18 @@ module Holistic::LanguageServer
14
14
 
15
15
  parse_application_in_background(application)
16
16
 
17
- respond_with_holistic_capabilities(request)
17
+ respond_with_language_server_capabilities(request)
18
18
  end
19
19
 
20
20
  private
21
21
 
22
22
  def create_application(request)
23
- ::Holistic.logger.info("===========")
24
- ::Holistic.logger.info(request.message.inspect)
25
-
26
23
  root_directory = request.param("rootPath")
27
24
  name = ::File.basename(root_directory)
28
25
 
29
- Current.application = ::Holistic::Application.new(name:, root_directory:)
30
-
31
- ::Holistic::Extensions::Ruby::Stdlib.register(Current.application)
32
-
33
- Current.application
26
+ Current.application = ::Holistic::Application.new(name:, root_directory:).tap do |application|
27
+ ::Holistic::Extensions::Ruby::Stdlib.register(application)
28
+ end
34
29
  end
35
30
 
36
31
  def advance_lifecycle_state
@@ -45,7 +40,7 @@ module Holistic::LanguageServer
45
40
  end
46
41
  end
47
42
 
48
- def respond_with_holistic_capabilities(request)
43
+ def respond_with_language_server_capabilities(request)
49
44
  request.respond_with({
50
45
  capabilities: {
51
46
  # Defines how the host (editor) should sync document changes to the language server.
@@ -8,21 +8,22 @@ module Holistic::LanguageServer
8
8
  cursor = build_cursor_from_request_params(request)
9
9
 
10
10
  document = request.application.unsaved_documents.find(cursor.file_path)
11
-
11
+
12
12
  return request.respond_with(nil) if document.nil?
13
13
 
14
14
  if document.has_unsaved_changes?
15
15
  ::Holistic::Ruby::Parser::LiveEditing::ProcessFileChanged.call(
16
16
  application: request.application,
17
- file: document.to_file
17
+ file_path: document.path,
18
+ content: document.content
18
19
  )
19
20
  end
20
21
 
21
22
  code = document.expand_code(cursor)
22
-
23
+
23
24
  return request.respond_with(nil) if code.blank?
24
25
 
25
- scope = request.application.scopes.find_inner_most_scope_by_cursor(cursor) || request.application.root_scope
26
+ scope = request.application.scopes.find_inner_most_scope_by_cursor(cursor) || request.application.scopes.root
26
27
 
27
28
  suggestions = ::Holistic::Ruby::Autocompletion::Suggest.call(code:, scope:)
28
29
 
@@ -13,19 +13,15 @@ module Holistic::LanguageServer
13
13
  if unsaved_document.has_unsaved_changes?
14
14
  unsaved_document.restore_original_content!
15
15
 
16
- process_in_background(application: request.application, file: unsaved_document.to_file)
16
+ ::Holistic::Ruby::Parser::LiveEditing::ProcessFileChanged.call(
17
+ application: request.application,
18
+ file_path: unsaved_document.path,
19
+ content: unsaved_document.content
20
+ )
17
21
  end
18
22
  end
19
23
 
20
24
  request.respond_with(nil)
21
25
  end
22
-
23
- private
24
-
25
- def process_in_background(application:, file:)
26
- ::Holistic::BackgroundProcess.run do
27
- ::Holistic::Ruby::Parser::LiveEditing::ProcessFileChanged.call(application:, file:)
28
- end
29
- end
30
26
  end
31
27
  end
@@ -9,6 +9,7 @@ module Holistic::LanguageServer
9
9
  content = request.message.param("textDocument", "text")
10
10
 
11
11
  request.application.unsaved_documents.add(path:, content:)
12
+ ::Holistic::Document::File::Store.call(database: request.application.database, file_path: path)
12
13
 
13
14
  request.respond_with(nil)
14
15
  end
@@ -17,17 +17,13 @@ module Holistic::LanguageServer
17
17
 
18
18
  unsaved_document.mark_as_saved!
19
19
 
20
- process_in_background(application: request.application, file: unsaved_document.to_file)
20
+ ::Holistic::Ruby::Parser::LiveEditing::ProcessFileChanged.call(
21
+ application: request.application,
22
+ file_path: unsaved_document.path,
23
+ content: unsaved_document.content
24
+ )
21
25
 
22
26
  request.respond_with(nil)
23
27
  end
24
-
25
- private
26
-
27
- def process_in_background(application:, file:)
28
- ::Holistic::BackgroundProcess.run do
29
- ::Holistic::Ruby::Parser::LiveEditing::ProcessFileChanged.call(application:, file:)
30
- end
31
- end
32
28
  end
33
29
  end
@@ -12,7 +12,8 @@ module Holistic::LanguageServer
12
12
  if unsaved_document.has_unsaved_changes?
13
13
  ::Holistic::Ruby::Parser::LiveEditing::ProcessFileChanged.call(
14
14
  application: request.application,
15
- file: unsaved_document.to_file
15
+ file_path: unsaved_document.path,
16
+ content: unsaved_document.content
16
17
  )
17
18
  end
18
19
  end
@@ -37,11 +38,13 @@ module Holistic::LanguageServer
37
38
 
38
39
  def respond_with_locations(request, references)
39
40
  locations = references.map do |reference|
41
+ location = reference.location
42
+
40
43
  {
41
- "uri" => Format::FileUri.from_path(reference.location.file_path),
44
+ "uri" => Format::FileUri.from_path(location.file.path),
42
45
  "range" => {
43
- "start" => { "line" => reference.location.start_line, "character" => reference.location.start_column },
44
- "end" => { "line" => reference.location.end_line, "character" => reference.location.end_column }
46
+ "start" => { "line" => location.start_line, "character" => location.start_column },
47
+ "end" => { "line" => location.end_line, "character" => location.end_column }
45
48
  }
46
49
  }
47
50
  end
@@ -12,7 +12,8 @@ module Holistic::LanguageServer
12
12
  if unsaved_document.has_unsaved_changes?
13
13
  ::Holistic::Ruby::Parser::LiveEditing::ProcessFileChanged.call(
14
14
  application: request.application,
15
- file: unsaved_document.to_file
15
+ file_path: unsaved_document.path,
16
+ content: unsaved_document.content
16
17
  )
17
18
  end
18
19
  end
@@ -46,7 +47,7 @@ module Holistic::LanguageServer
46
47
  "start" => { "line" => origin_location.start_line, "character" => origin_location.start_column },
47
48
  "end" => { "line" => origin_location.end_line, "character" => origin_location.end_column }
48
49
  },
49
- "targetUri" => Format::FileUri.from_path(target_declaration_location.file_path),
50
+ "targetUri" => Format::FileUri.from_path(target_declaration_location.file.path),
50
51
  "targetRange" => {
51
52
  "start" => { "line" => target_declaration_location.start_line, "character" => target_declaration_location.start_column },
52
53
  "end" => { "line" => target_declaration_location.end_line, "character" => target_declaration_location.end_column }
@@ -6,6 +6,12 @@ module Holistic::Ruby::Autocompletion
6
6
 
7
7
  Suggestion = ::Data.define(:code, :kind)
8
8
 
9
+ StartsWithLowerCaseLetter = ->(code) do
10
+ return false if [".", ":", "@"].include?(code[0])
11
+
12
+ code[0] == code[0].downcase
13
+ end
14
+
9
15
  def call(code:, scope:)
10
16
  lookup_scope = scope
11
17
 
@@ -13,7 +19,9 @@ module Holistic::Ruby::Autocompletion
13
19
  lookup_scope = lookup_scope.parent until lookup_scope.root?
14
20
  end
15
21
 
16
- if code.include?(".")
22
+ if StartsWithLowerCaseLetter[code]
23
+ suggest_local_methods_from_current_scope(code:, scope: lookup_scope)
24
+ elsif code.include?(".")
17
25
  suggest_methods_from_scope(code:, scope: lookup_scope)
18
26
  else
19
27
  suggest_namespaces_from_scope(code:, scope: lookup_scope)
@@ -22,12 +30,32 @@ module Holistic::Ruby::Autocompletion
22
30
 
23
31
  private
24
32
 
25
- NonMethods = ->(scope) { !Methods[scope] }
26
- Methods = ->(scope) { scope.instance_method? || scope.class_method? }
33
+ def suggest_local_methods_from_current_scope(code:, scope:)
34
+ suggestions = []
35
+
36
+ method_to_autocomplete = code
37
+
38
+ if scope.instance_method?
39
+ sibling_methods = scope.parent.children.filter { _1.instance_method? }
40
+
41
+ sibling_methods.each do |method_scope|
42
+ if method_scope.name.start_with?(method_to_autocomplete)
43
+ suggestions << Suggestion.new(code: method_scope.name, kind: method_scope.kind)
44
+ end
45
+ end
46
+ elsif scope.class_method?
47
+ sibling_methods = scope.parent.children.filter { _1.class_method? }
48
+
49
+ sibling_methods.each do |method_scope|
50
+ if method_scope.name.start_with?(method_to_autocomplete)
51
+ suggestions << Suggestion.new(code: method_scope.name, kind: method_scope.kind)
52
+ end
53
+ end
54
+ end
55
+
56
+ suggestions
57
+ end
27
58
 
28
- # Payment. <--
29
- # Payment::Notifications. <--
30
- # current_user.a
31
59
  def suggest_methods_from_scope(code:, scope:)
32
60
  suggestions = []
33
61
 
@@ -41,7 +69,9 @@ module Holistic::Ruby::Autocompletion
41
69
  return suggestions if scope.nil?
42
70
  end
43
71
 
44
- scope.children.filter(&:class_method?).each do |method_scope|
72
+ class_methods = scope.children.filter { _1.class_method? }
73
+
74
+ class_methods.each do |method_scope|
45
75
  if method_scope.name.start_with?(method_to_autocomplete)
46
76
  suggestions << Suggestion.new(code: method_scope.name, kind: method_scope.kind)
47
77
  end
@@ -63,22 +93,12 @@ module Holistic::Ruby::Autocompletion
63
93
  return suggestions if scope.nil?
64
94
  end
65
95
 
66
- # special case when user did not type :: at the end but the current word
67
- # is matches an existing namespace. In this case, suggestions will start with ::.
68
- # For example:
69
- #
70
- # \/ cursor here
71
- # typing: "::MyApp::Payments"
72
- # suggestions: ["::Record", "::SendReminder"]
73
- resolve_scope(name: namespace_to_autocomplete, from_scope: scope)&.then do |fully_typed_scope|
74
- scope = fully_typed_scope
75
- namespace_to_autocomplete = ""
76
- end
77
-
78
96
  should_search_upwards = namespaces_to_resolve.empty?
79
97
 
80
98
  search = ->(scope) do
81
- scope.children.filter(&NonMethods).each do |child_scope|
99
+ scope.children.each do |child_scope|
100
+ next if child_scope.method?
101
+
82
102
  if child_scope.name.start_with?(namespace_to_autocomplete)
83
103
  suggestions << Suggestion.new(code: child_scope.name, kind: child_scope.kind)
84
104
  end