ruby_language_server 0.3.16 → 0.3.17

Sign up to get free protection for your applications and to get access to all the features.
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.