solargraph 0.62.0 → 0.62.1

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: 8b2496884d72c8abed7340388dc6bd09f8be728ba93724c6146366b51a12c595
4
- data.tar.gz: b8f1be6afe684fc5a50349958937a12073dab1b7f5ff4574234e3ffaa57fc164
3
+ metadata.gz: 040b9387759644d50b1d7490a5d49a58f71f7e86e96d318b0754170ab71c6efe
4
+ data.tar.gz: 8c377973800407a29e98a7273d447eb4cb06d6c1f524c2df757643c434033d11
5
5
  SHA512:
6
- metadata.gz: 3414b07c09f728a8418bf1fb97d58eeb57a8496819c4a8340595750e6f59d952f77ea0cc3ce2e44705894905f773d3073c793e987d5b0e92aeea8ed4c29e835d
7
- data.tar.gz: eb38a46cf34401299101a91be9b1c113dd977784e6c17c25aae78e2554a932bb46ac7d822dd303753e353f2b1daba6cf8186d26807b418d2804e725f60dc2a14
6
+ metadata.gz: 8447cc0f561b0a17a3612f2cc354d3b8415ef4f4066af608b9b352a45b7c9ec4120622d5a61b81a5a2cd278740185ccec478edec9a7a72321c869be40b6967ce
7
+ data.tar.gz: 85ab9d988b7e61c63c07814e05e1d0df254fe276f3ccf4ed0adf682730297c6ba52515ab674d07d5b650c08edff87b8590dc814b71728d78f59c7d4f89d21c20
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 0.62.1 - November 4, 2026
2
+ - Always report completion for workspace mapping progress (#1475)
3
+ - Close attribute directives over their namespace (#1432)
4
+
1
5
  ## 0.62.0 - October 30, 2026
2
6
  - Fix false positive: Struct.new(keyword_init: true) members are optional (#1269)
3
7
  - Strip nil from or-expression fallbacks in conditional branch values (#1309)
@@ -878,15 +878,23 @@ module Solargraph
878
878
  def sync_library_map library
879
879
  total = library.workspace.sources.length
880
880
  progress = Progress.new('Mapping workspace')
881
- progress.begin "0/#{total} files", 0
882
- progress.send self
883
- while library.next_map
884
- pct = ((library.source_map_hash.keys.length.to_f / total) * 100).to_i
885
- progress.report "#{library.source_map_hash.keys.length}/#{total} files", pct
881
+ begin
882
+ progress.begin "0/#{total} files", 0
883
+ progress.send self
884
+ while library.next_map
885
+ mapped = library.source_map_hash.length
886
+ # The workspace can change while it's being mapped, e.g., when the
887
+ # user opens a file that wasn't included in the original scan. The
888
+ # progress should never report more than 100%.
889
+ total = mapped if mapped > total
890
+ # @sg-ignore Wrong argument type for Integer#/: arg_0 expected BigDecimal, received Integer
891
+ pct = total.zero? ? 100 : ((mapped * 100) / total).clamp(0, 100)
892
+ progress.report "#{mapped}/#{total} files", pct
893
+ progress.send self
894
+ end
895
+ progress.finish 'done'
886
896
  progress.send self
887
897
  end
888
- progress.finish 'done'
889
- progress.send self
890
898
  end
891
899
  end
892
900
  end
@@ -11,6 +11,12 @@ module Solargraph
11
11
  CREATED = :created
12
12
  FINISHED = :finished
13
13
 
14
+ # The number of seconds an idle notification can go without an update
15
+ # before it's refreshed. Some hosts dismiss progress notifications that
16
+ # haven't been updated recently.
17
+ #
18
+ KEEP_ALIVE_INTERVAL = 10
19
+
14
20
  # @return [String]
15
21
  attr_reader :uuid
16
22
 
@@ -43,21 +49,23 @@ module Solargraph
43
49
  def begin message, percentage
44
50
  @kind = 'begin'
45
51
  @message = message
46
- @percentage = percentage
52
+ @percentage = clamp_percentage percentage
47
53
  end
48
54
 
49
55
  # @param message [String]
50
56
  # @param percentage [Integer]
51
57
  # @return [void]
52
58
  def report message, percentage
59
+ return if finished?
53
60
  @kind = 'report'
54
61
  @message = message
55
- @percentage = percentage
62
+ @percentage = clamp_percentage percentage
56
63
  end
57
64
 
58
65
  # @param message [String]
59
66
  # @return [void]
60
67
  def finish message
68
+ return false if finished?
61
69
  @kind = 'end'
62
70
  @message = message
63
71
  @percentage = 100
@@ -67,13 +75,16 @@ module Solargraph
67
75
  # @param host [Solargraph::LanguageServer::Host]
68
76
  # @return [void]
69
77
  def send host
70
- return unless host.client_supports_progress? && !finished?
71
-
72
- message = build
73
- create(host)
74
- host.send_notification '$/progress', message
75
- @status = FINISHED if kind == 'end'
76
- keep_alive host
78
+ return unless host.client_supports_progress?
79
+ mutex.synchronize do
80
+ return if finished?
81
+ message = build
82
+ create host
83
+ host.send_notification '$/progress', message
84
+ @last = Time.now
85
+ @status = FINISHED if kind == 'end'
86
+ start_keep_alive host unless finished?
87
+ end
77
88
  end
78
89
 
79
90
  def created?
@@ -120,20 +131,47 @@ module Solargraph
120
131
  end
121
132
  end
122
133
 
134
+ # Keep idle notifications alive. Some hosts, including VS Code, stop
135
+ # responding to progress notifications that haven't been updated for a
136
+ # while, so a long-running job has to refresh its notification until it
137
+ # either reports again or finishes.
138
+ #
139
+ # The thread is only created once and exits as soon as the progress is
140
+ # finished or the host shuts down, so a completed notification can never
141
+ # be resurrected.
142
+ #
123
143
  # @param host [Host]
124
144
  # @return [void]
125
- def keep_alive host
126
- mutex.synchronize { @last = Time.now }
127
- @keep_alive ||= Thread.new do
128
- until finished?
129
- sleep 10
130
- break if finished?
131
- next if mutex.synchronize { Time.now - @last < 10 }
132
- send host
145
+ def start_keep_alive host
146
+ return if @keep_alive
147
+ @keep_alive = Thread.new do
148
+ loop do
149
+ sleep KEEP_ALIVE_INTERVAL
150
+ break if finished? || host.stopped?
151
+ next if mutex.synchronize { elapsed < KEEP_ALIVE_INTERVAL }
152
+ begin
153
+ send host
154
+ rescue StandardError
155
+ break
156
+ end
133
157
  end
134
158
  end
135
159
  end
136
160
 
161
+ # The number of seconds since the last notification was sent.
162
+ #
163
+ # @return [Float]
164
+ def elapsed
165
+ (Time.now - (@last ||= Time.now)).to_f
166
+ end
167
+
168
+ # @param percentage [Integer, nil]
169
+ # @return [Integer]
170
+ def clamp_percentage percentage
171
+ return 0 if percentage.nil?
172
+ percentage.to_i.clamp 0, 100
173
+ end
174
+
137
175
  # @return [Thread::Mutex]
138
176
  def mutex
139
177
  @mutex ||= Mutex.new
@@ -481,15 +481,12 @@ module Solargraph
481
481
  def next_map
482
482
  return false if mapped?
483
483
  src = workspace.sources.find { |s| !source_map_hash.key?(s.filename) }
484
- if src
485
- Logging.logger.debug "Mapping #{src.filename}"
486
- # @sg-ignore OK if src.filename is nil
487
- source_map_hash[src.filename] = Solargraph::SourceMap.map(src)
488
- # @sg-ignore OK if src.filename is nil
489
- source_map_hash[src.filename]
490
- else
491
- false
492
- end
484
+ return false if src.nil?
485
+ Logging.logger.debug "Mapping #{src.filename}"
486
+ # @sg-ignore OK if src.filename is nil
487
+ mutex.synchronize { source_map_hash[src.filename] = Solargraph::SourceMap.map(src) }
488
+ # @sg-ignore OK if src.filename is nil
489
+ source_map_hash[src.filename]
493
490
  end
494
491
 
495
492
  # @return [self]
@@ -615,6 +612,14 @@ module Solargraph
615
612
  end_cache_progress
616
613
  catalog
617
614
  sync_catalog
615
+ rescue StandardError => e
616
+ logger.warn "Error caching gemspec #{spec.name} #{spec.version}"
617
+ logger.warn e
618
+ ensure
619
+ # The progress notification has to be closed even if caching failed.
620
+ # Clients have no way to dismiss a notification that never receives an
621
+ # end.
622
+ end_cache_progress
618
623
  end
619
624
  end
620
625
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- VERSION = ENV.fetch('SOLARGRAPH_FORCE_VERSION', '0.62.0')
4
+ VERSION = ENV.fetch('SOLARGRAPH_FORCE_VERSION', '0.62.1')
5
5
  end
@@ -57,7 +57,10 @@ module Solargraph
57
57
  # @param [Position] position
58
58
  # @return [Pin::Closure]
59
59
  def closure_at pins, position
60
- pins.select { |pin| pin.is_a?(Pin::Closure) and pin.location&.range&.contain?(position) }.last
60
+ # Skip the method the directive documents; attributes belong to its namespace
61
+ pins.select do |pin|
62
+ pin.is_a?(Pin::Closure) && !pin.is_a?(Pin::Method) && pin.location&.range&.contain?(position)
63
+ end.last
61
64
  end
62
65
  end
63
66
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solargraph
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.62.0
4
+ version: 0.62.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fred Snyder