ruby_language_server 0.3.16 → 0.3.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bbab25376f7165888330fb96c5b0da91f6d0656ae6f7ceb36b1bfe7b4c036cf6
4
- data.tar.gz: 036050f7c473fd078087e5944e87a567ea0f074a3a15e2cc793892e7815376ee
3
+ metadata.gz: 0b9331721f750df7c683c22e8afb8591e002cdc779ba2b99fd94566f7d34e11e
4
+ data.tar.gz: a9865c1ca41b3860d4651f477ea4f0595c917e5730a01b135c5aee75e8d62da6
5
5
  SHA512:
6
- metadata.gz: 0be805b10e8c457ac22d2613cc2da3203953d99962e20c66883aa56cf711a868890c42833227bda771ddd81716ee36aa5b912c00808471d2e7290870ce5569ad
7
- data.tar.gz: fca402851a24ac39f23a61fef71bf179d23807cb715a2615d27d44280490e85305e0001f84d2e6f4fee99d0f674efd0438c6ef348ccce96839a99ae2d3efd720
6
+ metadata.gz: 7c48ba51bcd0f863a4df9003d482c9e7e7891a5788bb247f30d0306fd5f5171d45bc93687c4d78f429ec49a78c2b769e3ec9a20c50b3c2ed39a0315314df4001
7
+ data.tar.gz: 6035b4ab5245d9f5abbc20a5c59595b22540fef7fd3747bda92aa52f84c53affec76cb9d8d595245846a3a9e1d1a6b8b1c19b1d36b35952ba37e9862fb496d4d
data/CHANGELOG.txt CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ #### 0.3.17 Mon Aug 19 21:47:30 PDT 2024
4
+
5
+ * Update ruby to 3.3
6
+
7
+ #### 0.3.16.1 Thu Feb 17 23:27:11 PST 2022
8
+
9
+ * Update docker image to be cross-platform
3
10
 
4
11
  #### 0.3.16 Fri Jun 11 19:03:10 PDT 2021
5
12
 
data/Gemfile.lock CHANGED
@@ -1,33 +1,33 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ruby_language_server (0.3.16)
5
- activerecord (~> 6.1)
4
+ ruby_language_server (0.3.17)
5
+ activerecord (~> 7.0)
6
6
  amatch
7
7
  bundler
8
8
  etc
9
9
  fuzzy_match
10
10
  json
11
- rubocop
11
+ rubocop (> 1.38.0)
12
+ rubocop-ast (> 1.32.0)
12
13
  rubocop-performance
13
14
  rubocop-rspec
14
- sqlite3
15
+ sqlite3 (< 1.5.0)
15
16
 
16
17
  GEM
17
18
  remote: https://rubygems.org/
18
19
  specs:
19
- activemodel (6.1.3.1)
20
- activesupport (= 6.1.3.1)
21
- activerecord (6.1.3.1)
22
- activemodel (= 6.1.3.1)
23
- activesupport (= 6.1.3.1)
24
- activesupport (6.1.3.1)
20
+ activemodel (7.0.7.2)
21
+ activesupport (= 7.0.7.2)
22
+ activerecord (7.0.7.2)
23
+ activemodel (= 7.0.7.2)
24
+ activesupport (= 7.0.7.2)
25
+ activesupport (7.0.7.2)
25
26
  concurrent-ruby (~> 1.0, >= 1.0.2)
26
27
  i18n (>= 1.6, < 2)
27
28
  minitest (>= 5.1)
28
29
  tzinfo (~> 2.0)
29
- zeitwerk (~> 2.3)
30
- amatch (0.4.0)
30
+ amatch (0.4.1)
31
31
  mize
32
32
  tins (~> 1.0)
33
33
  ansi (1.5.0)
@@ -35,37 +35,37 @@ GEM
35
35
  builder (3.2.4)
36
36
  byebug (11.1.3)
37
37
  coderay (1.1.3)
38
- concurrent-ruby (1.1.8)
39
- etc (1.2.0)
40
- ffi (1.15.0)
41
- formatador (0.2.5)
38
+ concurrent-ruby (1.2.2)
39
+ etc (1.3.0)
40
+ ffi (1.15.5)
41
+ formatador (1.1.0)
42
42
  fuzzy_match (2.1.0)
43
- guard (2.16.2)
43
+ guard (2.18.0)
44
44
  formatador (>= 0.2.4)
45
45
  listen (>= 2.7, < 4.0)
46
46
  lumberjack (>= 1.0.12, < 2.0)
47
47
  nenv (~> 0.1)
48
48
  notiffany (~> 0.0)
49
- pry (>= 0.9.12)
49
+ pry (>= 0.13.0)
50
50
  shellany (~> 0.0)
51
51
  thor (>= 0.18.1)
52
52
  guard-compat (1.2.1)
53
53
  guard-minitest (2.4.6)
54
54
  guard-compat (~> 1.2)
55
55
  minitest (>= 3.0)
56
- guard-rubocop (1.4.0)
56
+ guard-rubocop (1.5.0)
57
57
  guard (~> 2.0)
58
58
  rubocop (< 2.0)
59
- i18n (1.8.10)
59
+ i18n (1.14.1)
60
60
  concurrent-ruby (~> 1.0)
61
- json (2.5.1)
62
- listen (3.5.1)
61
+ json (2.6.2)
62
+ listen (3.7.1)
63
63
  rb-fsevent (~> 0.10, >= 0.10.3)
64
64
  rb-inotify (~> 0.9, >= 0.9.10)
65
65
  lumberjack (1.2.8)
66
66
  method_source (1.0.0)
67
- minitest (5.14.4)
68
- minitest-reporters (1.4.3)
67
+ minitest (5.16.3)
68
+ minitest-reporters (1.5.0)
69
69
  ansi
70
70
  builder
71
71
  minitest (>= 5.0)
@@ -76,70 +76,74 @@ GEM
76
76
  notiffany (0.1.3)
77
77
  nenv (~> 0.1)
78
78
  shellany (~> 0.0)
79
- parallel (1.20.1)
80
- parser (3.0.1.0)
79
+ parallel (1.22.1)
80
+ parser (3.3.4.2)
81
81
  ast (~> 2.4.1)
82
+ racc
82
83
  protocol (2.0.0)
83
84
  ruby_parser (~> 3.0)
84
- pry (0.13.1)
85
+ pry (0.14.1)
85
86
  coderay (~> 1.1)
86
87
  method_source (~> 1.0)
87
- pry-byebug (3.9.0)
88
- byebug (~> 11.0)
89
- pry (~> 0.13.0)
90
- rainbow (3.0.0)
91
- rake (13.0.3)
92
- rb-fsevent (0.10.4)
88
+ racc (1.8.1)
89
+ rainbow (3.1.1)
90
+ rake (13.0.6)
91
+ rb-fsevent (0.11.2)
93
92
  rb-inotify (0.10.1)
94
93
  ffi (~> 1.0)
95
- regexp_parser (2.1.1)
94
+ regexp_parser (2.6.0)
96
95
  rexml (3.2.5)
97
- rubocop (1.13.0)
96
+ rubocop (1.42.0)
97
+ json (~> 2.3)
98
98
  parallel (~> 1.10)
99
- parser (>= 3.0.0.0)
99
+ parser (>= 3.1.2.1)
100
100
  rainbow (>= 2.2.2, < 4.0)
101
101
  regexp_parser (>= 1.8, < 3.0)
102
- rexml
103
- rubocop-ast (>= 1.2.0, < 2.0)
102
+ rexml (>= 3.2.5, < 4.0)
103
+ rubocop-ast (>= 1.24.1, < 2.0)
104
104
  ruby-progressbar (~> 1.7)
105
105
  unicode-display_width (>= 1.4.0, < 3.0)
106
- rubocop-ast (1.4.1)
107
- parser (>= 2.7.1.5)
108
- rubocop-performance (1.11.0)
106
+ rubocop-ast (1.32.1)
107
+ parser (>= 3.3.1.0)
108
+ rubocop-minitest (0.34.5)
109
+ rubocop (>= 1.39, < 2.0)
110
+ rubocop-ast (>= 1.30.0, < 2.0)
111
+ rubocop-performance (1.19.1)
109
112
  rubocop (>= 1.7.0, < 2.0)
110
113
  rubocop-ast (>= 0.4.0)
111
- rubocop-rspec (2.2.0)
114
+ rubocop-rake (0.6.0)
112
115
  rubocop (~> 1.0)
113
- rubocop-ast (>= 1.1.0)
116
+ rubocop-rspec (3.0.0)
117
+ rubocop (~> 1.40)
114
118
  ruby-progressbar (1.11.0)
115
- ruby_parser (3.15.1)
116
- sexp_processor (~> 4.9)
117
- sexp_processor (4.15.2)
119
+ ruby_parser (3.19.1)
120
+ sexp_processor (~> 4.16)
121
+ sexp_processor (4.16.1)
118
122
  shellany (0.0.1)
119
- sqlite3 (1.4.2)
123
+ sqlite3 (1.4.4)
120
124
  sync (0.5.0)
121
- thor (1.1.0)
122
- tins (1.28.0)
125
+ thor (1.2.1)
126
+ tins (1.31.1)
123
127
  sync
124
- tzinfo (2.0.4)
128
+ tzinfo (2.0.6)
125
129
  concurrent-ruby (~> 1.0)
126
- unicode-display_width (2.0.0)
127
- zeitwerk (2.4.2)
130
+ unicode-display_width (2.3.0)
128
131
 
129
132
  PLATFORMS
130
- x86_64-linux-musl
133
+ aarch64-linux-musl
134
+ x86_64-linux
131
135
 
132
136
  DEPENDENCIES
137
+ byebug
133
138
  guard
134
139
  guard-minitest
135
140
  guard-rubocop
136
141
  minitest
137
142
  minitest-reporters
138
- pry
139
- pry-byebug
140
143
  rake
144
+ rubocop-minitest
145
+ rubocop-rake
141
146
  ruby_language_server!
142
- sexp_processor
143
147
 
144
148
  BUNDLED WITH
145
- 2.2.16
149
+ 2.3.25
data/Makefile CHANGED
@@ -39,3 +39,8 @@ gem: image
39
39
  # Requires rubygems be installed on host
40
40
  gem_release: gem
41
41
  docker run -it --rm $(LOCAL_LINK) $(PROJECT_NAME) gem push $(PROJECT_NAME)*.gem
42
+
43
+ publish_cross_platform_image:
44
+ (docker buildx ls | grep mybuilder) || docker buildx create --name mybuilder
45
+ docker buildx use mybuilder
46
+ docker buildx build --push --platform linux/amd64,linux/arm64/v8 -t kwerle/$(PROJECT_NAME) .
data/README.md CHANGED
@@ -31,17 +31,17 @@ You probably want to use one of the developed integrations:
31
31
 
32
32
  # Development
33
33
 
34
+ Master branch is for releases. Develop branch is for ongoing development. Fork off develop;
35
+ I'll merge to master for releases.
36
+
34
37
  Clone. I love git [HubFlow](https://datasift.github.io/gitflow/).
35
38
 
36
39
  Check out the [Makefile](Makefile). You are going to want to do
37
40
  `make guard` in one window and `make continuous_development` in another.
38
41
 
39
- * In Atom: install the ide-ruby.
40
- * Settings > Packages > ide-ruby > Image Name > local_ruby_language_server
41
- * CMD-ALT-CTRL-l (that's an L) will reload the window
42
- * CMD-ALT-i will show debugging info
43
-
44
- Write tests and guard will run them. Make changes and reload the window. Test them out.
42
+ I use vscode with the "Ruby Language Server" extension install. I edit the settings to use
43
+ the docker image local_ruby_language_server. Quitting and restarting vscode to load the next
44
+ iteration.
45
45
 
46
46
  # Similar
47
47
 
@@ -50,12 +50,15 @@ Write tests and guard will run them. Make changes and reload the window. Test
50
50
 
51
51
  # Release instructions to self
52
52
 
53
- * For for release
53
+ For gem release
54
54
  * bump version in [version.rb](lib/ruby_language_server/version.rb) file and [Gemfile.lock](Gemfile.lock)
55
55
  * [CHANGELOG.txt](CHANGELOG.txt)
56
56
  * merge to master, etc
57
57
  * `make gem_release`
58
58
 
59
+ For docker release
60
+ * `make publish_cross_platform_image`
61
+
59
62
  # Authors
60
63
 
61
64
  * [Kurt Werle](kurt@CircleW.org)
@@ -2,10 +2,9 @@
2
2
 
3
3
  ActiveRecord::Base.establish_connection(
4
4
  adapter: 'sqlite3',
5
- database: 'file:memory?mode=memory&cache=shared',
6
- # database: '/database',
7
- pool: 5, # does not seem to help
8
- checkout_timeout: 30.seconds # does not seem to help
5
+ database: '/database',
6
+ pool: 5,
7
+ timeout: 30.seconds # does not seem to help
9
8
  )
10
9
 
11
10
  database = ActiveRecord::Base.connection.instance_variable_get :@connection
@@ -21,7 +20,7 @@ database.enable_load_extension(0)
21
20
  if ENV['LOG_LEVEL'] == 'DEBUG'
22
21
  begin
23
22
  warn('Turning on active record logging to active_record.log')
24
- ActiveRecord::Base.logger = Logger.new(File.open('active_record.log', 'w'))
23
+ ActiveRecord::Base.logger = Logger.new(File.open('/active_record.log', 'w'))
25
24
  rescue Exception => e
26
25
  ActiveRecord::Base.logger = Logger.new($stderr)
27
26
  ActiveRecord::Base.logger.error(e)
data/lib/db/schema.rb CHANGED
@@ -1,44 +1,66 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- ActiveRecord::Schema.define do
4
- def write(*args)
5
- RubyLanguageServer.logger.debug(args)
6
- end
3
+ module Schema
4
+ class << self
5
+ def load
6
+ ActiveRecord::Schema.define do
7
+ def write(*args)
8
+ RubyLanguageServer.logger.debug(args)
9
+ end
7
10
 
8
- create_table :scopes, force: true do |t|
9
- t.references :code_file
10
- t.integer :parent_id
11
- t.integer :top_line # first line
12
- t.integer :bottom_line # last line
13
- t.integer :column
14
- t.string :name, default: ''
15
- t.string :superclass_name
16
- t.string :path
17
- t.string :class_type, null: false
18
- end
11
+ create_table :scopes, force: true do |t|
12
+ t.references :code_file
13
+ t.integer :parent_id
14
+ t.integer :top_line # first line
15
+ t.integer :bottom_line # last line
16
+ t.integer :column
17
+ t.string :name, default: ''
18
+ t.string :superclass_name
19
+ t.string :path
20
+ t.string :class_type, null: false
21
+ end
19
22
 
20
- add_index :scopes, :name
21
- add_index :scopes, :path
22
- add_index :scopes, :code_file
23
-
24
- create_table :variables, force: true do |t|
25
- t.references :code_file
26
- t.references :scope
27
- t.integer :line
28
- t.integer :column
29
- t.string :name
30
- t.string :path
31
- t.string :variable_type
32
- end
23
+ add_index :scopes, :name
24
+ add_index :scopes, :path
25
+ add_index :scopes, :code_file
33
26
 
34
- add_index :variables, :name
35
- add_index :variables, :code_file
27
+ create_table :variables, force: true do |t|
28
+ t.references :code_file
29
+ t.references :scope
30
+ t.integer :line
31
+ t.integer :column
32
+ t.string :name
33
+ t.string :path
34
+ t.string :variable_type
35
+ end
36
36
 
37
- create_table :code_files, force: true do |t|
38
- t.string :uri
39
- t.boolean :refresh_root_scope, default: true
40
- t.text :text
41
- end
37
+ add_index :variables, :name
38
+ add_index :variables, :code_file
39
+
40
+ create_table :code_files, force: true do |t|
41
+ t.string :uri
42
+ t.boolean :refresh_root_scope, default: true
43
+ t.text :text
44
+ end
42
45
 
43
- add_index :code_files, :uri
46
+ add_index :code_files, :uri
47
+ end
48
+ end
49
+
50
+ # If we dive in using a console attached to a running container we do NOT want to reset all the table data!
51
+ # Which it seems like sqlite is happy to do?
52
+ # So we utter some arcane sqlite code to find the existing tables if there are any.
53
+ def already_initialized
54
+ response = ActiveRecord::Base.connection_pool.with_connection { |con| con.exec_query "SELECT name FROM sqlite_master WHERE type='table'" }
55
+ response.rows.flatten.member?('code_files')
56
+ rescue ExceptionName
57
+ false
58
+ end
59
+
60
+ def initialize_if_needed
61
+ load unless already_initialized
62
+ end
63
+ end
44
64
  end
65
+
66
+ Schema.initialize_if_needed
@@ -20,7 +20,7 @@ module RubyLanguageServer
20
20
  def self.build(uri, text)
21
21
  RubyLanguageServer.logger.debug("CodeFile initialize #{uri}")
22
22
 
23
- create!(uri: uri, text: text)
23
+ create!(uri:, text:)
24
24
  end
25
25
 
26
26
  SYMBOL_KIND = {
@@ -70,7 +70,7 @@ module RubyLanguageServer
70
70
  kind = 9 if scope.name == 'initialize' # Magical special case
71
71
  scope_hash = {
72
72
  name: scope.name,
73
- kind: kind,
73
+ kind:,
74
74
  location: Location.hash(uri, scope.top_line)
75
75
  }
76
76
  container_name = ancestor_scope_name(scope)
@@ -80,7 +80,7 @@ module RubyLanguageServer
80
80
  tags += variables.constant_variables.reload.map do |variable|
81
81
  name = variable.name
82
82
  {
83
- name: name,
83
+ name:,
84
84
  kind: SYMBOL_KIND[:constant],
85
85
  location: Location.hash(uri, variable.line - 1),
86
86
  containerName: variable.scope.name
@@ -108,7 +108,7 @@ module RubyLanguageServer
108
108
  update(text: new_text, refresh_root_scope: true)
109
109
  end
110
110
 
111
- def refresh_scopes_if_needed
111
+ def refresh_scopes_if_needed(shallow: false)
112
112
  return unless refresh_root_scope
113
113
 
114
114
  RubyLanguageServer.logger.debug("Asking about root_scope for #{uri}")
@@ -117,7 +117,7 @@ module RubyLanguageServer
117
117
  self.class.transaction do
118
118
  scopes.clear
119
119
  variables.clear
120
- new_root = ScopeParser.new(text).root_scope
120
+ new_root = ScopeParser.new(text, shallow).root_scope
121
121
  RubyLanguageServer.logger.debug("new_root.children #{new_root.children.as_json}") if new_root&.children
122
122
  raise ActiveRecord::Rollback if new_root.nil? || new_root.children.blank?
123
123
 
@@ -132,7 +132,7 @@ module RubyLanguageServer
132
132
  def context_at_location(position)
133
133
  lines = text.split("\n")
134
134
  line = lines[position.line]
135
- return [] if line.nil? || line.strip.length.zero?
135
+ return [] if line.nil? || line.strip.empty?
136
136
 
137
137
  LineContext.for(line, position.character)
138
138
  end
@@ -47,7 +47,7 @@ module RubyLanguageServer
47
47
  private
48
48
 
49
49
  def scopes_with_name(name, scopes)
50
- return scopes.where(name: name) if scopes.respond_to?(:where)
50
+ return scopes.where(name:) if scopes.respond_to?(:where)
51
51
 
52
52
  scopes.select { |scope| scope.name == name }
53
53
  end
@@ -80,14 +80,6 @@ module RubyLanguageServer
80
80
  def scope_completions(word, scopes)
81
81
  return module_completions(word) if word.match?(/\A[A-Z][a-z]/)
82
82
 
83
- # scope_ids = scopes.map(&:id)
84
- # word_scopes = scopes.to_a + RubyLanguageServer::ScopeData::Scope.where(parent_id: scope_ids)
85
- # scope_words = word_scopes.select(&:named_scope?).sort_by(&:depth).map { |scope| [scope.name, scope] }
86
- # variable_words = RubyLanguageServer::ScopeData::Variable.where(scope_id: scope_ids).map { |variable| [variable.name, variable.scope] }
87
- # words = (scope_words + variable_words).to_h
88
- # good_words = FuzzyMatch.new(words.keys, threshold: 0.01).find_all(word).slice(0..10) || []
89
- # words = good_words.each_with_object({}) { |w, hash| hash[w] = {depth: words[w].depth, type: words[w].class_type} }.to_h
90
-
91
83
  scope_ids = scopes.map(&:id)
92
84
  word_scopes = scopes.to_a + RubyLanguageServer::ScopeData::Scope.where(parent_id: scope_ids).closest_to(word).limit(5)
93
85
  scope_words = word_scopes.select(&:named_scope?).sort_by(&:depth).map { |scope| [scope.name, scope] }
@@ -102,7 +102,7 @@ module RubyLanguageServer
102
102
  if excluded_file?(filename)
103
103
  []
104
104
  else
105
- ruby_version = 2.7
105
+ ruby_version = 3.1
106
106
  processed_source = RuboCop::ProcessedSource.new(text, ruby_version, filename)
107
107
  offenses = inspect_file(processed_source)
108
108
  offenses.compact.flatten.reject(&:blank?) # reject blank because some are `false`
@@ -136,13 +136,15 @@ module RubyLanguageServer
136
136
 
137
137
  class << self
138
138
  def instance
139
+ return @instance if @instance
140
+
139
141
  @config_path ||= config_path
140
142
  config_path_timestamp = File.mtime(@config_path)
141
143
  if @cached_config_path_timestamp.nil? || @cached_config_path_timestamp < config_path_timestamp
142
144
  @cached_config_path_timestamp = config_path_timestamp
143
145
  @instance = new(@config_path)
144
146
  else
145
- @instance
147
+ @instance = new
146
148
  end
147
149
  rescue StandardError => e
148
150
  @instance = new(@config_path, e.to_s)
@@ -24,7 +24,7 @@ module RubyLanguageServer
24
24
  def return_response(id, response, io = $stdout)
25
25
  full_response = {
26
26
  jsonrpc: '2.0',
27
- id: id,
27
+ id:,
28
28
  result: response
29
29
  }
30
30
  response_body = JSON.unparse(full_response)
@@ -39,7 +39,7 @@ module RubyLanguageServer
39
39
  full_response = {
40
40
  jsonrpc: '2.0',
41
41
  method: message,
42
- params: params
42
+ params:
43
43
  }
44
44
  body = JSON.unparse(full_response)
45
45
  RubyLanguageServer.logger.info "send_notification body: #{body}"
@@ -58,11 +58,17 @@ module RubyLanguageServer
58
58
  params = request_json['params']
59
59
  method_name = "on_#{method_name.gsub(/[^\w]/, '_')}"
60
60
  if @server.respond_to? method_name
61
- RubyLanguageServer.logger.debug 'Locking io'
62
- response = @mutex.synchronize do
63
- @server.send(method_name, params)
61
+ response = ActiveRecord::Base.connection_pool.with_connection do
62
+ retries = 3
63
+ begin
64
+ @server.send(method_name, params)
65
+ rescue StandardError => e
66
+ RubyLanguageServer.logger.warn("Error updating: #{e}\n#{e.backtrace * "\n"}")
67
+ sleep 5
68
+ retries -= 1
69
+ retry unless retries <= 0
70
+ end
64
71
  end
65
- RubyLanguageServer.logger.debug 'UNLocking io'
66
72
  exit(true) if response == 'EXIT'
67
73
  [id, response]
68
74
  else
@@ -26,6 +26,6 @@ module RubyLanguageServer
26
26
  }
27
27
  }
28
28
  end
29
- end
29
+ end
30
30
  end
31
31
  end
@@ -3,12 +3,11 @@
3
3
  require 'logger'
4
4
 
5
5
  module RubyLanguageServer
6
- level_name = ENV.fetch('LOG_LEVEL', 'error').upcase
7
- # level_name = 'DEBUG'
6
+ level_name = ENV.fetch('LOG_LEVEL', 'error').upcase || 'DEBUG'
8
7
  level = Logger::Severity.const_get(level_name)
9
8
  class << self
10
9
  attr_accessor :logger
11
10
  end
12
- @logger = ::Logger.new($stderr, level: level)
11
+ @logger = ::Logger.new($stderr, level:)
13
12
  @logger.log(level, "Logger started at level #{level_name} -> #{level}")
14
13
  end
@@ -22,7 +22,7 @@ module RubyLanguageServer
22
22
  def root_path
23
23
  # I'm torn about this. Should this be set in the Server? Or is this right.
24
24
  # Rather than worry too much, I'll just do this here and change it later if it feels wrong.
25
- path = ENV['RUBY_LANGUAGE_SERVER_PROJECT_ROOT'] || @_root_path
25
+ path = ENV.fetch('RUBY_LANGUAGE_SERVER_PROJECT_ROOT') { @_root_path }
26
26
  return path if path.nil?
27
27
 
28
28
  path.end_with?(File::SEPARATOR) ? path : "#{path}#{File::SEPARATOR}"
@@ -179,28 +179,36 @@ module RubyLanguageServer
179
179
  # data?: any
180
180
  # }
181
181
 
182
- def scan_all_project_files(mutex)
182
+ def scan_all_project_files
183
183
  project_ruby_files = Dir.glob("#{self.class.root_path}**/*.rb")
184
- Thread.new do
185
- RubyLanguageServer.logger.debug('Threading up!')
186
- root_uri = @root_uri
187
- root_uri += '/' unless root_uri.end_with? '/'
184
+ RubyLanguageServer.logger.debug('Threading up!')
185
+ root_uri = @root_uri
186
+ root_uri += '/' unless root_uri.end_with? '/'
187
+ # Using fork because this is run in a docker container that has fork.
188
+ # If you want to run this on some platform without fork, fork the code and PR it :-)
189
+ fork_id = fork do
188
190
  project_ruby_files.each do |container_path|
189
- # Let's not preload spec/test or vendor - yet..
190
- next if container_path.match?(/^(.?spec|test|vendor)/)
191
+ # Let's not preload spec/test files or vendor - yet..
192
+ next if container_path.match?(/(spec\.rb|test\.rb|vendor)/)
191
193
 
192
194
  text = File.read(container_path)
193
195
  relative_path = container_path.delete_prefix(self.class.root_path)
194
196
  host_uri = root_uri + relative_path
195
- RubyLanguageServer.logger.debug "Locking scan for #{container_path}"
196
- mutex.synchronize do
197
- RubyLanguageServer.logger.debug("Threading #{host_uri}")
198
- update_document_content(host_uri, text)
199
- code_file_for_uri(host_uri).refresh_scopes_if_needed
197
+ RubyLanguageServer.logger.debug("Threading #{host_uri}")
198
+ begin
199
+ ActiveRecord::Base.connection_pool.with_connection do |_connection|
200
+ update_document_content(host_uri, text)
201
+ code_file_for_uri(host_uri).refresh_scopes_if_needed(shallow: true)
202
+ end
203
+ rescue StandardError => e
204
+ RubyLanguageServer.logger.warn("Error updating: #{e}\n#{e.backtrace * "\n"}")
205
+ sleep 5
206
+ retry
200
207
  end
201
- RubyLanguageServer.logger.debug "Unlocking scan for #{container_path}"
202
208
  end
203
209
  end
210
+ RubyLanguageServer.logger.debug("Forked process id to look at other files: #{fork_id}")
211
+ Process.detach(fork_id)
204
212
  end
205
213
 
206
214
  # returns diagnostic info (if possible)
@@ -218,7 +226,7 @@ module RubyLanguageServer
218
226
  # Maybe we should be sharing this GoodCop across instances
219
227
  RubyLanguageServer.logger.debug("updated_diagnostics_for_codefile: #{code_file.uri}")
220
228
  project_relative_filename = filename_relative_to_project(code_file.uri)
221
- code_file.diagnostics = GoodCop.instance.diagnostics(code_file.text, project_relative_filename)
229
+ code_file.diagnostics = GoodCop.instance&.diagnostics(code_file.text, project_relative_filename)
222
230
  RubyLanguageServer.logger.debug("code_file.diagnostics: #{code_file.diagnostics}")
223
231
  code_file.diagnostics
224
232
  end
@@ -261,8 +269,8 @@ module RubyLanguageServer
261
269
  end
262
270
 
263
271
  def project_definitions_for(name)
264
- scopes = RubyLanguageServer::ScopeData::Scope.where(name: name)
265
- variables = RubyLanguageServer::ScopeData::Variable.constant_variables.where(name: name)
272
+ scopes = RubyLanguageServer::ScopeData::Scope.where(name:)
273
+ variables = RubyLanguageServer::ScopeData::Variable.constant_variables.where(name:)
266
274
  (scopes + variables).reject { |scope| scope.code_file.nil? }.map do |scope|
267
275
  Location.hash(scope.code_file.uri, scope.top_line, 1)
268
276
  end
@@ -12,6 +12,8 @@ module RubyLanguageServer
12
12
  belongs_to :parent, class_name: 'Scope', optional: true
13
13
  has_many :children, class_name: 'Scope', foreign_key: :parent_id
14
14
 
15
+ validate :bottom_line_ge_top_line, unless: :root_scope?
16
+
15
17
  scope :method_scopes, -> { where(class_type: TYPE_METHOD) }
16
18
  scope :for_line, ->(line) { where('top_line <= ? AND bottom_line >= ?', line, line).or(where(parent_id: nil)) }
17
19
  scope :by_path_length, -> { order(Arel.sql('length(path) DESC')) }
@@ -25,10 +27,10 @@ module RubyLanguageServer
25
27
  def self.build(parent = nil, type = TYPE_ROOT, name = '', top_line = 1, column = 1)
26
28
  full_name = [parent ? parent.full_name : nil, name].compact.join(JoinHash[type])
27
29
  create!(
28
- parent: parent,
29
- top_line: top_line,
30
- column: column,
31
- name: name,
30
+ parent:,
31
+ top_line:,
32
+ column:,
33
+ name:,
32
34
  path: full_name,
33
35
  class_type: type
34
36
  )
@@ -77,11 +79,25 @@ module RubyLanguageServer
77
79
  [TYPE_MODULE, TYPE_CLASS, TYPE_METHOD, TYPE_VARIABLE].include?(class_type)
78
80
  end
79
81
 
82
+ # Called from ScopeParser when a peer of this block starts - because we don't have an end notifier.
83
+ # So we do some reasonable cleanup here.
84
+ def close(line)
85
+ return destroy! if block_scope? && variables.none?
86
+
87
+ self.top_line ||= variables.map(&:top_line).min
88
+ self.bottom_line = [bottom_line, line].compact.min
89
+ save!
90
+ end
91
+
80
92
  private
81
93
 
82
94
  def scope_parts
83
95
  path&.split(/#{JoinHash.values.reject(&:blank?).uniq.join('|')}/)
84
96
  end
97
+
98
+ def bottom_line_ge_top_line
99
+ errors.add(:bottom_line, 'must be greater than or equal to top line') if bottom_line && top_line && bottom_line < top_line
100
+ end
85
101
  end
86
102
  end
87
103
  end
@@ -15,10 +15,10 @@ module RubyLanguageServer
15
15
  def self.build(scope, name, line = 1, column = 1, type = TYPE_VARIABLE)
16
16
  path = [scope.full_name, name].join(JoinHash[TYPE_VARIABLE])
17
17
  create!(
18
- line: line,
19
- column: column,
20
- name: name,
21
- path: path,
18
+ line:,
19
+ column:,
20
+ name:,
21
+ path:,
22
22
  variable_type: type
23
23
  )
24
24
  end
@@ -17,9 +17,10 @@ module RubyLanguageServer
17
17
  include ScopeParserCommands::RubyCommands
18
18
  attr_reader :sexp, :lines, :current_scope
19
19
 
20
- def initialize(sexp, lines = 1)
20
+ def initialize(sexp, lines = 1, shallow = false)
21
21
  @sexp = sexp
22
22
  @lines = lines
23
+ @shallow = shallow
23
24
  @root_scope = nil
24
25
  end
25
26
 
@@ -226,38 +227,6 @@ module RubyLanguageServer
226
227
  end
227
228
 
228
229
  [:def_with_access, klass, method_name, access, line]
229
- # when 'scope', 'named_scope'
230
- # [:rails_def, :scope, args[1][0], line]
231
- # when /^attr_(accessor|reader|writer)$/
232
- # gen_reader = Regexp.last_match(1) != 'writer'
233
- # gen_writer = Regexp.last_match(1) != 'reader'
234
- # args[1..-1].each_with_object([]) do |arg, gen|
235
- # gen << [:def, arg[0], line] if gen_reader
236
- # gen << [:def, "#{arg[0]}=", line] if gen_writer
237
- # end
238
- # when 'has_many', 'has_and_belongs_to_many'
239
- # a = args[1][0]
240
- # kind = name.to_sym
241
- # gen = []
242
- # unless a.is_a?(Enumerable) && !a.is_a?(String)
243
- # a = a.to_s
244
- # gen << [:rails_def, kind, a, line]
245
- # gen << [:rails_def, kind, "#{a}=", line]
246
- # if (sing = a.chomp('s')) != a
247
- # # poor man's singularize
248
- # gen << [:rails_def, kind, "#{sing}_ids", line]
249
- # gen << [:rails_def, kind, "#{sing}_ids=", line]
250
- # end
251
- # end
252
- # gen
253
- # when 'belongs_to', 'has_one'
254
- # a = args[1][0]
255
- # unless a.is_a?(Enumerable) && !a.is_a?(String)
256
- # kind = name.to_sym
257
- # %W[#{a} #{a}= build_#{a} create_#{a} create_#{a}!].inject([]) do |all, ident|
258
- # all << [:rails_def, kind, ident, line]
259
- # end
260
- # end
261
230
  end
262
231
  end
263
232
  end
@@ -265,19 +234,17 @@ module RubyLanguageServer
265
234
  private
266
235
 
267
236
  def add_variable(name, line, column, scope = @current_scope)
268
- newvar = scope.variables.where(name: name).first_or_create!(
269
- line: line,
270
- column: column,
237
+ return if @shallow
238
+
239
+ newvar = scope.variables.where(name:).first_or_create!(
240
+ line:,
241
+ column:,
271
242
  code_file: scope.code_file
272
243
  )
273
244
  if scope.top_line.blank?
274
245
  scope.top_line = line
275
246
  scope.save!
276
247
  end
277
- # new_variable = ScopeData::Variable.build(scope, name, line, column)
278
- # # blocks don't declare their first line in the parser
279
- # scope.top_line ||= line
280
- # scope.variables << new_variable unless scope.has_variable_or_constant?(new_variable)
281
248
  newvar
282
249
  end
283
250
 
@@ -295,7 +262,11 @@ module RubyLanguageServer
295
262
  def add_scope(args, rest, type)
296
263
  (_, name, (line, column)) = args
297
264
  scope = push_scope(type, name, line, column)
298
- process(rest)
265
+ if type == ScopeData::Scope::TYPE_METHOD && @shallow
266
+ process([])
267
+ else
268
+ process(rest)
269
+ end
299
270
  pop_scope
300
271
  scope
301
272
  end
@@ -316,10 +287,7 @@ module RubyLanguageServer
316
287
  # The notion is that when you start the next scope, all the previous peers and unclosed descendents of the previous peer should be closed.
317
288
  def close_sibling_scopes(line)
318
289
  parent_scope = @current_scope
319
- parent_scope&.descendants&.each do |scope|
320
- scope.bottom_line = [scope.bottom_line, line - 1].compact.min
321
- scope.save!
322
- end
290
+ parent_scope&.descendants&.each { |scope| scope.close(line) }
323
291
  end
324
292
 
325
293
  def pop_scope
@@ -333,14 +301,14 @@ module RubyLanguageServer
333
301
  class ScopeParser < Ripper
334
302
  attr_reader :root_scope
335
303
 
336
- def initialize(text)
304
+ def initialize(text, shallow = false)
337
305
  text ||= '' # empty is the same as nil - but it doesn't crash
338
306
  begin
339
307
  sexp = self.class.sexp(text)
340
308
  rescue TypeError => e
341
309
  RubyLanguageServer.logger.error("Exception in sexp: #{e} for text: #{text}")
342
310
  end
343
- processor = SEXPProcessor.new(sexp, text.split("\n").length)
311
+ processor = SEXPProcessor.new(sexp, text.split("\n").length, shallow)
344
312
  @root_scope = processor.root_scope
345
313
  end
346
314
  end
@@ -17,7 +17,7 @@ module RubyLanguageServer
17
17
  root_path = params['rootPath']
18
18
  root_uri = params['rootUri']
19
19
  @project_manager = ProjectManager.new(root_path, root_uri)
20
- @project_manager.scan_all_project_files(@mutex)
20
+ @project_manager.scan_all_project_files
21
21
  gem_string = ENV.fetch('ADDITIONAL_GEMS', nil)
22
22
  gem_array = (gem_string.split(',').compact.map(&:strip).reject { |string| string == '' } if gem_string && !gem_string.empty?)
23
23
  @project_manager.install_additional_gems(gem_array)
@@ -85,7 +85,7 @@ module RubyLanguageServer
85
85
 
86
86
  def send_diagnostics(uri, text)
87
87
  hash = @project_manager.update_document_content(uri, text)
88
- io.send_notification('textDocument/publishDiagnostics', uri: uri, diagnostics: hash)
88
+ io.send_notification('textDocument/publishDiagnostics', uri:, diagnostics: hash)
89
89
  end
90
90
 
91
91
  def on_textDocument_didOpen(params)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RubyLanguageServer
4
- VERSION = '0.3.16'
4
+ VERSION = '0.3.17'
5
5
  end
@@ -11,10 +11,10 @@ Gem::Specification.new do |spec|
11
11
  spec.email = ['kurt@CircleW.org']
12
12
 
13
13
  spec.summary = 'Provide a language server implementation for ruby in ruby.'
14
- spec.description = 'See https://microsoft.github.io/language-server-protocol/ "A Language Server is meant to provide the language-specific smarts and communicate with development tools over a protocol that enables inter-process communication."'
14
+ spec.description = 'Provide a language server implementation for ruby in ruby. See https://microsoft.github.io/language-server-protocol/ "A Language Server is meant to provide the language-specific smarts and communicate with development tools over a protocol that enables inter-process communication."'
15
15
  spec.homepage = 'https://github.com/kwerle/ruby_language_server'
16
16
  spec.license = 'MIT'
17
- spec.required_ruby_version = '>=2.7.0'
17
+ spec.required_ruby_version = '>=3.1.0'
18
18
 
19
19
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
20
20
  # to allow pushing to a single host or delete this section to allow pushing to any host.
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
24
24
  spec.metadata['changelog_uri'] = 'https://github.com/kwerle/ruby_language_server/blob/develop/CHANGELOG.txt'
25
25
  else
26
26
  raise 'RubyGems 2.0 or newer is required to protect against ' \
27
- 'public gem pushes.'
27
+ 'public gem pushes.'
28
28
  end
29
29
 
30
30
  # Specify which files should be added to the gem when it is released.
@@ -40,23 +40,25 @@ Gem::Specification.new do |spec|
40
40
  spec.add_dependency 'json'
41
41
 
42
42
  # No - do not put these in dev - they are needed for the app
43
- spec.add_dependency 'rubocop'
43
+ spec.add_dependency 'rubocop', '>1.38.0' # Something broke in 1.38.0. Move to rubocop --server?
44
+ spec.add_dependency 'rubocop-ast', '>1.32.0' # Something broke in 1.38.0. Move to rubocop --server?
44
45
  spec.add_dependency 'rubocop-performance' # Linter - no longer needed - use additional gems?
45
46
  spec.add_dependency 'rubocop-rspec' # Linter - no longer needed - use additional gems?
46
47
 
47
48
  spec.add_dependency 'amatch' # in c
48
49
  spec.add_dependency 'fuzzy_match' # completion matching
49
50
 
50
- spec.add_dependency 'activerecord', '~>6.1'
51
- spec.add_dependency 'sqlite3'
51
+ spec.add_dependency 'activerecord', '~>7.0'
52
+ spec.add_dependency 'sqlite3', '<1.5.0' # 1.5.0 breaks in docker M1 at least
52
53
 
54
+ spec.add_development_dependency 'byebug'
53
55
  spec.add_development_dependency 'guard'
54
56
  spec.add_development_dependency 'guard-minitest'
55
57
  spec.add_development_dependency 'guard-rubocop'
56
58
  spec.add_development_dependency 'minitest'
57
59
  spec.add_development_dependency 'minitest-reporters'
58
- spec.add_development_dependency 'pry'
59
- spec.add_development_dependency 'pry-byebug'
60
60
  spec.add_development_dependency 'rake' # required by guard :-(
61
- spec.add_development_dependency 'sexp_processor'
61
+ spec.add_development_dependency 'rubocop-minitest'
62
+ spec.add_development_dependency 'rubocop-rake'
63
+ spec.metadata['rubygems_mfa_required'] = 'true'
62
64
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_language_server
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.16
4
+ version: 0.3.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kurt Werle
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-06-12 00:00:00.000000000 Z
11
+ date: 2024-08-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -56,16 +56,30 @@ dependencies:
56
56
  name: rubocop
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - ">"
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: 1.38.0
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - ">"
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: 1.38.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop-ast
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">"
74
+ - !ruby/object:Gem::Version
75
+ version: 1.32.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">"
81
+ - !ruby/object:Gem::Version
82
+ version: 1.32.0
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: rubocop-performance
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -128,30 +142,30 @@ dependencies:
128
142
  requirements:
129
143
  - - "~>"
130
144
  - !ruby/object:Gem::Version
131
- version: '6.1'
145
+ version: '7.0'
132
146
  type: :runtime
133
147
  prerelease: false
134
148
  version_requirements: !ruby/object:Gem::Requirement
135
149
  requirements:
136
150
  - - "~>"
137
151
  - !ruby/object:Gem::Version
138
- version: '6.1'
152
+ version: '7.0'
139
153
  - !ruby/object:Gem::Dependency
140
154
  name: sqlite3
141
155
  requirement: !ruby/object:Gem::Requirement
142
156
  requirements:
143
- - - ">="
157
+ - - "<"
144
158
  - !ruby/object:Gem::Version
145
- version: '0'
159
+ version: 1.5.0
146
160
  type: :runtime
147
161
  prerelease: false
148
162
  version_requirements: !ruby/object:Gem::Requirement
149
163
  requirements:
150
- - - ">="
164
+ - - "<"
151
165
  - !ruby/object:Gem::Version
152
- version: '0'
166
+ version: 1.5.0
153
167
  - !ruby/object:Gem::Dependency
154
- name: guard
168
+ name: byebug
155
169
  requirement: !ruby/object:Gem::Requirement
156
170
  requirements:
157
171
  - - ">="
@@ -165,7 +179,7 @@ dependencies:
165
179
  - !ruby/object:Gem::Version
166
180
  version: '0'
167
181
  - !ruby/object:Gem::Dependency
168
- name: guard-minitest
182
+ name: guard
169
183
  requirement: !ruby/object:Gem::Requirement
170
184
  requirements:
171
185
  - - ">="
@@ -179,7 +193,7 @@ dependencies:
179
193
  - !ruby/object:Gem::Version
180
194
  version: '0'
181
195
  - !ruby/object:Gem::Dependency
182
- name: guard-rubocop
196
+ name: guard-minitest
183
197
  requirement: !ruby/object:Gem::Requirement
184
198
  requirements:
185
199
  - - ">="
@@ -193,7 +207,7 @@ dependencies:
193
207
  - !ruby/object:Gem::Version
194
208
  version: '0'
195
209
  - !ruby/object:Gem::Dependency
196
- name: minitest
210
+ name: guard-rubocop
197
211
  requirement: !ruby/object:Gem::Requirement
198
212
  requirements:
199
213
  - - ">="
@@ -207,7 +221,7 @@ dependencies:
207
221
  - !ruby/object:Gem::Version
208
222
  version: '0'
209
223
  - !ruby/object:Gem::Dependency
210
- name: minitest-reporters
224
+ name: minitest
211
225
  requirement: !ruby/object:Gem::Requirement
212
226
  requirements:
213
227
  - - ">="
@@ -221,7 +235,7 @@ dependencies:
221
235
  - !ruby/object:Gem::Version
222
236
  version: '0'
223
237
  - !ruby/object:Gem::Dependency
224
- name: pry
238
+ name: minitest-reporters
225
239
  requirement: !ruby/object:Gem::Requirement
226
240
  requirements:
227
241
  - - ">="
@@ -235,7 +249,7 @@ dependencies:
235
249
  - !ruby/object:Gem::Version
236
250
  version: '0'
237
251
  - !ruby/object:Gem::Dependency
238
- name: pry-byebug
252
+ name: rake
239
253
  requirement: !ruby/object:Gem::Requirement
240
254
  requirements:
241
255
  - - ">="
@@ -249,7 +263,7 @@ dependencies:
249
263
  - !ruby/object:Gem::Version
250
264
  version: '0'
251
265
  - !ruby/object:Gem::Dependency
252
- name: rake
266
+ name: rubocop-minitest
253
267
  requirement: !ruby/object:Gem::Requirement
254
268
  requirements:
255
269
  - - ">="
@@ -263,7 +277,7 @@ dependencies:
263
277
  - !ruby/object:Gem::Version
264
278
  version: '0'
265
279
  - !ruby/object:Gem::Dependency
266
- name: sexp_processor
280
+ name: rubocop-rake
267
281
  requirement: !ruby/object:Gem::Requirement
268
282
  requirements:
269
283
  - - ">="
@@ -276,9 +290,9 @@ dependencies:
276
290
  - - ">="
277
291
  - !ruby/object:Gem::Version
278
292
  version: '0'
279
- description: See https://microsoft.github.io/language-server-protocol/ "A Language
280
- Server is meant to provide the language-specific smarts and communicate with development
281
- tools over a protocol that enables inter-process communication."
293
+ description: Provide a language server implementation for ruby in ruby. See https://microsoft.github.io/language-server-protocol/
294
+ "A Language Server is meant to provide the language-specific smarts and communicate
295
+ with development tools over a protocol that enables inter-process communication."
282
296
  email:
283
297
  - kurt@CircleW.org
284
298
  executables:
@@ -332,6 +346,7 @@ metadata:
332
346
  homepage_uri: https://github.com/kwerle/ruby_language_server
333
347
  source_code_uri: https://github.com/kwerle/ruby_language_server
334
348
  changelog_uri: https://github.com/kwerle/ruby_language_server/blob/develop/CHANGELOG.txt
349
+ rubygems_mfa_required: 'true'
335
350
  post_install_message:
336
351
  rdoc_options: []
337
352
  require_paths:
@@ -340,14 +355,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
340
355
  requirements:
341
356
  - - ">="
342
357
  - !ruby/object:Gem::Version
343
- version: 2.7.0
358
+ version: 3.1.0
344
359
  required_rubygems_version: !ruby/object:Gem::Requirement
345
360
  requirements:
346
361
  - - ">="
347
362
  - !ruby/object:Gem::Version
348
363
  version: '0'
349
364
  requirements: []
350
- rubygems_version: 3.2.15
365
+ rubygems_version: 3.5.11
351
366
  signing_key:
352
367
  specification_version: 4
353
368
  summary: Provide a language server implementation for ruby in ruby.