ruby-lsp 0.7.4 → 0.7.6

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: 5488592df860b1fd1439a1b2780a7d0da850fcb9b9164713fbf5ce07b6e67270
4
- data.tar.gz: '055789cf32db04550a7bd4f602a4e3d69a96760698c567450eceffde5a7ef152'
3
+ metadata.gz: a645362acbfb8b0f819bd89fbaf1e53d0f7d9eda81974e62bc0f114aeefc221e
4
+ data.tar.gz: 861b5c68f9355f551ebfb519507ca1c0028f0e4e2edff70ae6ad62c652bdfd2c
5
5
  SHA512:
6
- metadata.gz: a3f710de0df843d8c45499b5f818433b9d25b56547d19b2a7cd3896b73ff29c6a6e2be35d091df7d9a7058d9d0d3994dc63898328efa689a198faf864ef61ffc
7
- data.tar.gz: cc97f72b359d8125269d17cac6d0c8313ca7e2debcae54a81beb270f620b12b100ec89a2bf9c9a82990bb41411a9acf37f76909c09b561bf53eb7fcd74d407b6
6
+ metadata.gz: 4283abdb1ce9865e6dbecd0827ddd88f07a63937a9f75ba8ca78142d3536624ac374b165e7a1277adee4197553c74615ed560dad3093eb346ae3e505e174dae5
7
+ data.tar.gz: 6084fd63d0d780c169b8f8766f347cdb699e2490a0c8de542a3fa969ad37d7f49e281ffa7f92a7ee15a764dccac9832e5e9a49265ba9077f9d204185d0c00e64
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.7.4
1
+ 0.7.6
data/exe/ruby-lsp CHANGED
@@ -1,33 +1,22 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- # We should make sure that, if we're running on a bundler project, that it has a Gemfile.lock
5
- if File.exist?("Gemfile") && !File.exist?("Gemfile.lock")
6
- warn("Project contains a Gemfile, but no Gemfile.lock. Run `bundle install` to lock gems and restart the server")
7
- exit(78)
8
- end
9
-
10
4
  # When we're running without bundler, then we need to make sure the custom bundle is fully configured and re-execute
11
5
  # using `BUNDLE_GEMFILE=.ruby-lsp/Gemfile bundle exec ruby-lsp` so that we have access to the gems that are a part of
12
6
  # the application's bundle
13
- if ENV["BUNDLE_GEMFILE"].nil? && File.exist?("Gemfile.lock")
7
+ if ENV["BUNDLE_GEMFILE"].nil?
14
8
  require_relative "../lib/ruby_lsp/setup_bundler"
15
- RubyLsp::SetupBundler.new(Dir.pwd).setup!
16
-
17
- # In some cases, like when the `ruby-lsp` is already a part of the bundle, we don't generate `.ruby-lsp/Gemfile`.
18
- # However, we still want to run the server with `bundle exec`. We need to make sure we're pointing to the right
19
- # `Gemfile`
20
- bundle_gemfile = File.exist?(".ruby-lsp/Gemfile") ? ".ruby-lsp/Gemfile" : "Gemfile"
21
-
22
- # In addition to BUNDLE_GEMFILE, we also need to make sure that BUNDLE_PATH is absolute and not relative. For example,
23
- # if BUNDLE_PATH is `vendor/bundle`, we want the top level `vendor/bundle` and not `.ruby-lsp/vendor/bundle`.
24
- # Expanding to get the absolute path ensures we're pointing to the correct folder, which is the same one we use in
25
- # SetupBundler to install the gems
26
- path = Bundler.settings["path"]
27
-
28
- command = +"BUNDLE_GEMFILE=#{bundle_gemfile} bundle exec ruby-lsp #{ARGV.join(" ")}"
29
- command.prepend("BUNDLE_PATH=#{File.expand_path(path, Dir.pwd)} ") if path
30
- exit exec(command)
9
+
10
+ begin
11
+ bundle_gemfile, bundle_path = RubyLsp::SetupBundler.new(Dir.pwd).setup!
12
+ rescue RubyLsp::SetupBundler::BundleNotLocked
13
+ warn("Project contains a Gemfile, but no Gemfile.lock. Run `bundle install` to lock gems and restart the server")
14
+ exit(78)
15
+ end
16
+
17
+ env = { "BUNDLE_GEMFILE" => bundle_gemfile }
18
+ env["BUNDLE_PATH"] = bundle_path if bundle_path
19
+ exit exec(env, "bundle exec ruby-lsp #{ARGV.join(" ")}")
31
20
  end
32
21
 
33
22
  require "sorbet-runtime"
@@ -47,7 +47,7 @@ module RubyLsp
47
47
  start_char = scanner.find_char_position(range[:start])
48
48
  end_char = scanner.find_char_position(range[:end])
49
49
 
50
- queue = T.cast(@document.tree, SyntaxTree::Program).statements.body
50
+ queue = T.cast(@document.tree, SyntaxTree::Program).statements.body.dup
51
51
  found_nodes = []
52
52
 
53
53
  until queue.empty?
@@ -26,7 +26,7 @@ module RubyLsp
26
26
  params(node: T.any(SyntaxTree::ConstPathRef, SyntaxTree::ConstRef, SyntaxTree::TopConstRef)).returns(String)
27
27
  end
28
28
  def full_constant_name(node)
29
- name = +node.constant.value
29
+ name = node.constant.value.dup
30
30
  constant = T.let(node, SyntaxTree::Node)
31
31
 
32
32
  while constant.is_a?(SyntaxTree::ConstPathRef)
@@ -38,7 +38,10 @@ module RubyLsp
38
38
 
39
39
  sig { params(gem_pattern: Regexp).returns(T::Boolean) }
40
40
  def direct_dependency?(gem_pattern)
41
- Bundler.locked_gems.dependencies.keys.grep(gem_pattern).any?
41
+ Bundler.with_original_env { Bundler.default_gemfile } &&
42
+ Bundler.locked_gems.dependencies.keys.grep(gem_pattern).any?
43
+ rescue Bundler::GemfileNotFound
44
+ false
42
45
  end
43
46
  end
44
47
  end
@@ -4,6 +4,7 @@
4
4
  require "sorbet-runtime"
5
5
  require "bundler"
6
6
  require "fileutils"
7
+ require "pathname"
7
8
 
8
9
  # This file is a script that will configure a custom bundle for the Ruby LSP. The custom bundle allows developers to use
9
10
  # the Ruby LSP without including the gem in their application's Gemfile while at the same time giving us access to the
@@ -13,123 +14,151 @@ module RubyLsp
13
14
  class SetupBundler
14
15
  extend T::Sig
15
16
 
17
+ class BundleNotLocked < StandardError; end
18
+
16
19
  sig { params(project_path: String).void }
17
20
  def initialize(project_path)
18
21
  @project_path = project_path
19
- @dependencies = T.let(load_dependencies, T::Hash[String, T.untyped])
20
- @custom_bundle_dependencies = T.let(
21
- if File.exist?(".ruby-lsp/Gemfile.lock")
22
- Bundler::LockfileParser.new(Bundler.read_file(".ruby-lsp/Gemfile.lock")).dependencies
23
- else
24
- {}
22
+
23
+ # Custom bundle paths
24
+ @custom_dir = T.let(Pathname.new(".ruby-lsp").expand_path(Dir.pwd), Pathname)
25
+ @custom_gemfile = T.let(@custom_dir + "Gemfile", Pathname)
26
+ @custom_lockfile = T.let(@custom_dir + "Gemfile.lock", Pathname)
27
+
28
+ # Regular bundle paths
29
+ @gemfile = T.let(
30
+ begin
31
+ Bundler.default_gemfile
32
+ rescue Bundler::GemfileNotFound
33
+ nil
25
34
  end,
26
- T::Hash[String, T.untyped],
35
+ T.nilable(Pathname),
27
36
  )
37
+ @lockfile = T.let(@gemfile ? Bundler.default_lockfile : nil, T.nilable(Pathname))
38
+
39
+ @dependencies = T.let(load_dependencies, T::Hash[String, T.untyped])
40
+ @custom_bundle_dependencies = T.let(custom_bundle_dependencies, T::Hash[String, T.untyped])
28
41
  end
29
42
 
30
- sig { void }
43
+ # Setups up the custom bundle and returns the `BUNDLE_GEMFILE` and `BUNDLE_PATH` that should be used for running the
44
+ # server
45
+ sig { returns([String, T.nilable(String)]) }
31
46
  def setup!
32
- # Do not setup a custom bundle if we're working on the Ruby LSP, since it's already included by default
33
- if File.basename(@project_path) == "ruby-lsp"
34
- warn("Ruby LSP> Skipping custom bundle setup since we're working on the Ruby LSP itself")
35
- run_bundle_install
36
- return
37
- end
47
+ raise BundleNotLocked if @gemfile&.exist? && !@lockfile&.exist?
38
48
 
39
49
  # Do not setup a custom bundle if both `ruby-lsp` and `debug` are already in the Gemfile
40
50
  if @dependencies["ruby-lsp"] && @dependencies["debug"]
41
- warn("Ruby LSP> Skipping custom bundle setup since both `ruby-lsp` and `debug` are already in the Gemfile")
51
+ warn("Ruby LSP> Skipping custom bundle setup since both `ruby-lsp` and `debug` are already in #{@gemfile}")
42
52
 
43
53
  # If the user decided to add the `ruby-lsp` and `debug` to their Gemfile after having already run the Ruby LSP,
44
54
  # then we need to remove the `.ruby-lsp` folder, otherwise we will run `bundle install` for the top level and
45
55
  # try to execute the Ruby LSP using the custom bundle, which will fail since the gems are not installed there
46
- FileUtils.rm_r(".ruby-lsp") if Dir.exist?(".ruby-lsp")
47
- run_bundle_install
48
- return
56
+ @custom_dir.rmtree if @custom_dir.exist?
57
+ return run_bundle_install
49
58
  end
50
59
 
51
60
  # Automatically create and ignore the .ruby-lsp folder for users
52
- FileUtils.mkdir(".ruby-lsp") unless Dir.exist?(".ruby-lsp")
53
- File.write(".ruby-lsp/.gitignore", "*") unless File.exist?(".ruby-lsp/.gitignore")
61
+ @custom_dir.mkpath unless @custom_dir.exist?
62
+ ignore_file = @custom_dir + ".gitignore"
63
+ ignore_file.write("*") unless ignore_file.exist?
54
64
 
55
- # Write the custom `.ruby-lsp/Gemfile` if it doesn't exist or if the content doesn't match
56
- content = custom_gemfile_content
65
+ write_custom_gemfile
57
66
 
58
- unless File.exist?(".ruby-lsp/Gemfile") && File.read(".ruby-lsp/Gemfile") == content
59
- File.write(".ruby-lsp/Gemfile", content)
67
+ unless @gemfile&.exist? && @lockfile&.exist?
68
+ warn("Ruby LSP> Skipping lockfile copies because there's no top level bundle")
69
+ return run_bundle_install(@custom_gemfile)
60
70
  end
61
71
 
62
72
  # If .ruby-lsp/Gemfile.lock already exists and the top level Gemfile.lock hasn't been modified since it was last
63
73
  # updated, then we're ready to boot the server
64
- if File.exist?(".ruby-lsp/Gemfile.lock") &&
65
- File.stat(".ruby-lsp/Gemfile.lock").mtime > File.stat("Gemfile.lock").mtime
66
- warn("Ruby LSP> Skipping custom bundle setup since .ruby-lsp/Gemfile.lock already exists and is up to date")
67
- run_bundle_install(".ruby-lsp/Gemfile")
68
- return
74
+ if @custom_lockfile.exist? && @custom_lockfile.stat.mtime > @lockfile.stat.mtime
75
+ warn("Ruby LSP> Skipping custom bundle setup since #{@custom_lockfile} already exists and is up to date")
76
+ return run_bundle_install(@custom_gemfile)
69
77
  end
70
78
 
71
- FileUtils.cp("Gemfile.lock", ".ruby-lsp/Gemfile.lock")
72
- run_bundle_install(".ruby-lsp/Gemfile")
79
+ FileUtils.cp(@lockfile.to_s, @custom_lockfile.to_s)
80
+ run_bundle_install(@custom_gemfile)
73
81
  end
74
82
 
75
83
  private
76
84
 
77
- sig { returns(String) }
78
- def custom_gemfile_content
85
+ sig { returns(T::Hash[String, T.untyped]) }
86
+ def custom_bundle_dependencies
87
+ return {} unless @custom_lockfile.exist?
88
+
89
+ ENV["BUNDLE_GEMFILE"] = @custom_gemfile.to_s
90
+ Bundler::LockfileParser.new(@custom_lockfile.read).dependencies
91
+ ensure
92
+ ENV.delete("BUNDLE_GEMFILE")
93
+ end
94
+
95
+ sig { void }
96
+ def write_custom_gemfile
79
97
  parts = [
80
98
  "# This custom gemfile is automatically generated by the Ruby LSP.",
81
99
  "# It should be automatically git ignored, but in any case: do not commit it to your repository.",
82
100
  "",
83
- "eval_gemfile(File.expand_path(\"../Gemfile\", __dir__))",
84
101
  ]
85
102
 
103
+ # If there's a top level Gemfile, we want to evaluate from the custom bundle. We get the source from the top level
104
+ # Gemfile, so if there isn't one we need to add a default source
105
+ if @gemfile&.exist?
106
+ parts << "eval_gemfile(File.expand_path(\"../Gemfile\", __dir__))"
107
+ else
108
+ parts.unshift('source "https://rubygems.org"')
109
+ end
110
+
86
111
  unless @dependencies["ruby-lsp"]
87
- parts << 'gem "ruby-lsp", require: false, group: :development, source: "https://rubygems.org"'
112
+ parts << 'gem "ruby-lsp", require: false, group: :development'
88
113
  end
89
114
 
90
115
  unless @dependencies["debug"]
91
- parts << 'gem "debug", require: false, group: :development, platforms: :mri, source: "https://rubygems.org"'
116
+ parts << 'gem "debug", require: false, group: :development, platforms: :mri'
92
117
  end
93
118
 
94
- parts.join("\n")
119
+ content = parts.join("\n")
120
+ @custom_gemfile.write(content) unless @custom_gemfile.exist? && @custom_gemfile.read == content
95
121
  end
96
122
 
97
123
  sig { returns(T::Hash[String, T.untyped]) }
98
124
  def load_dependencies
125
+ return {} unless @lockfile&.exist?
126
+
99
127
  # We need to parse the Gemfile.lock manually here. If we try to do `bundler/setup` to use something more
100
128
  # convenient, we may end up with issues when the globally installed `ruby-lsp` version mismatches the one included
101
129
  # in the `Gemfile`
102
- dependencies = Bundler::LockfileParser.new(Bundler.read_file("Gemfile.lock")).dependencies
130
+ dependencies = Bundler::LockfileParser.new(@lockfile.read).dependencies
103
131
 
104
132
  # When working on a gem, the `ruby-lsp` might be listed as a dependency in the gemspec. We need to make sure we
105
- # check those as well or else we may get version mismatch errors
106
- gemspec_path = Dir.glob("*.gemspec").first
107
- if gemspec_path
108
- gemspec_dependencies = Bundler.load_gemspec(gemspec_path).dependencies.to_h { |dep| [dep.name, dep] }
109
- dependencies.merge!(gemspec_dependencies)
133
+ # check those as well or else we may get version mismatch errors. Notice that bundler allows more than one
134
+ # gemspec, so we need to make sure we go through all of them
135
+ Dir.glob("{,*}.gemspec").each do |path|
136
+ dependencies.merge!(Bundler.load_gemspec(path).dependencies.to_h { |dep| [dep.name, dep] })
110
137
  end
111
138
 
112
139
  dependencies
113
140
  end
114
141
 
115
- sig { params(bundle_gemfile: T.untyped).void }
116
- def run_bundle_install(bundle_gemfile = nil)
142
+ sig { params(bundle_gemfile: T.nilable(Pathname)).returns([String, T.nilable(String)]) }
143
+ def run_bundle_install(bundle_gemfile = @gemfile)
117
144
  # If the user has a custom bundle path configured, we need to ensure that we will use the absolute and not
118
145
  # relative version of it when running `bundle install`. This is necessary to avoid installing the gems under the
119
146
  # `.ruby-lsp` folder, which is not the user's intention. For example, if the path is configured as `vendor`, we
120
147
  # want to install it in the top level `vendor` and not `.ruby-lsp/vendor`
121
148
  path = Bundler.settings["path"]
122
149
 
123
- command = +""
124
150
  # Use the absolute `BUNDLE_PATH` to prevent accidentally creating unwanted folders under `.ruby-lsp`
125
- command << "BUNDLE_PATH=#{File.expand_path(path, Dir.pwd)} " if path
126
- command << "BUNDLE_GEMFILE=#{bundle_gemfile} " if bundle_gemfile
151
+ env = {}
152
+ env["BUNDLE_GEMFILE"] = bundle_gemfile.to_s
153
+ env["BUNDLE_PATH"] = File.expand_path(path, Dir.pwd) if path
127
154
 
128
155
  # If both `ruby-lsp` and `debug` are already in the Gemfile, then we shouldn't try to upgrade them or else we'll
129
156
  # produce undesired source control changes. If the custom bundle was just created and either `ruby-lsp` or `debug`
130
157
  # weren't a part of the Gemfile, then we need to run `bundle install` for the first time to generate the
131
158
  # Gemfile.lock with them included or else Bundler will complain that they're missing. We can only update if the
132
159
  # custom `.ruby-lsp/Gemfile.lock` already exists and includes both gems
160
+ command = +""
161
+
133
162
  if (@dependencies["ruby-lsp"] && @dependencies["debug"]) ||
134
163
  @custom_bundle_dependencies["ruby-lsp"].nil? || @custom_bundle_dependencies["debug"].nil?
135
164
  # Install gems using the custom bundle
@@ -147,7 +176,8 @@ module RubyLsp
147
176
 
148
177
  # Add bundle update
149
178
  warn("Ruby LSP> Running bundle install for the custom bundle. This may take a while...")
150
- system(command)
179
+ system(env, command)
180
+ [bundle_gemfile.to_s, path]
151
181
  end
152
182
  end
153
183
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-lsp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.4
4
+ version: 0.7.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-07-25 00:00:00.000000000 Z
11
+ date: 2023-08-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: language_server-protocol
@@ -140,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
140
140
  - !ruby/object:Gem::Version
141
141
  version: '0'
142
142
  requirements: []
143
- rubygems_version: 3.4.16
143
+ rubygems_version: 3.4.17
144
144
  signing_key:
145
145
  specification_version: 4
146
146
  summary: An opinionated language server for Ruby