steep 0.29.0 → 0.30.0

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: 5d09c66d40509db4956625f6be82477c4cda1b375d4cc37ebc2b4fdfdfdd971f
4
- data.tar.gz: bfe074e9fc6e833faa4442f718e2bade7e54a5b6921fd8cf63465972ec39ced5
3
+ metadata.gz: b5293cbc87834cee36e50cb66bab84e466a5182c1e868732b7bd3e685b2bd954
4
+ data.tar.gz: 7794e42573b5ac04c74780824213cfbe1ca255f1feaa4465a1066377d2647e0b
5
5
  SHA512:
6
- metadata.gz: 05d54b1565ccf9189d335163d952d075bd6f7b3a08c26362efb815c3d19cfd2ffa2eb13e124a5dc7a4a5f1343a6fbbe5694ed3d6000a4fe37f3ace4cb2d7c1d5
7
- data.tar.gz: 2661d5a8a5273e5c4341b27fd78a5f57c296c31663f16d46bf8de39ea1af40b827bfdf48f9266e071a37b1fe395a58ec716149d88656cf983587ecbe89fa24e6
6
+ metadata.gz: e723bf77a7452e0696c38d6d1797e71f246fb97d1b1f18082ba21b8eadb8174d6bcc4deda872898b5f39d162b91173c413cacfa28175018517170f8f2568bd70
7
+ data.tar.gz: 38dbe78df43706fe33d5350ad04524c25606955bcba5397ad18690717a60c59d361485ad2d5d3077787336d23902bd91ba9fa5b31e1933a93d1fe32747837688
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.30.0 (2020-10-03)
6
+
7
+ * Let top-level defs be methods of Object ([#227](https://github.com/soutaro/steep/pull/227))
8
+ * Fix error caused by attribute definitions ([#228](https://github.com/soutaro/steep/pull/228))
9
+ * LSP worker improvements ([#222](https://github.com/soutaro/steep/pull/222), [#223](https://github.com/soutaro/steep/pull/223), [#226](https://github.com/soutaro/steep/pull/226), [#229](https://github.com/soutaro/steep/pull/229))
10
+
5
11
  ## 0.29.0 (2020-09-28)
6
12
 
7
13
  * Implement reasoning on `is_a?`, `nil?`, and `===` methods. ([#218](https://github.com/soutaro/steep/pull/218))
@@ -83,7 +83,8 @@ require "steep/server/interaction_worker"
83
83
  require "steep/server/master"
84
84
 
85
85
  require "steep/project"
86
- require "steep/project/file"
86
+ require "steep/project/signature_file"
87
+ require "steep/project/source_file"
87
88
  require "steep/project/options"
88
89
  require "steep/project/target"
89
90
  require "steep/project/dsl"
@@ -344,41 +344,43 @@ module Steep
344
344
  defined_in = method_def.defined_in
345
345
  member = method_def.member
346
346
 
347
- case
348
- when defined_in == RBS::BuiltinNames::Object.name && member.instance?
349
- case method_name
350
- when :is_a?, :kind_of?, :instance_of?
351
- return method_type.with(
352
- return_type: AST::Types::Logic::ReceiverIsArg.new(location: method_type.return_type.location)
353
- )
354
- when :nil?
355
- return method_type.with(
356
- return_type: AST::Types::Logic::ReceiverIsNil.new(location: method_type.return_type.location)
357
- )
358
- end
347
+ if member.is_a?(RBS::AST::Members::MethodDefinition)
348
+ case
349
+ when defined_in == RBS::BuiltinNames::Object.name && member.instance?
350
+ case method_name
351
+ when :is_a?, :kind_of?, :instance_of?
352
+ return method_type.with(
353
+ return_type: AST::Types::Logic::ReceiverIsArg.new(location: method_type.return_type.location)
354
+ )
355
+ when :nil?
356
+ return method_type.with(
357
+ return_type: AST::Types::Logic::ReceiverIsNil.new(location: method_type.return_type.location)
358
+ )
359
+ end
359
360
 
360
- when defined_in == AST::Builtin::NilClass.module_name && member.instance?
361
- case method_name
362
- when :nil?
363
- return method_type.with(
364
- return_type: AST::Types::Logic::ReceiverIsNil.new(location: method_type.return_type.location)
365
- )
366
- end
361
+ when defined_in == AST::Builtin::NilClass.module_name && member.instance?
362
+ case method_name
363
+ when :nil?
364
+ return method_type.with(
365
+ return_type: AST::Types::Logic::ReceiverIsNil.new(location: method_type.return_type.location)
366
+ )
367
+ end
367
368
 
368
- when defined_in == RBS::BuiltinNames::BasicObject.name && member.instance?
369
- case method_name
370
- when :!
371
- return method_type.with(
372
- return_type: AST::Types::Logic::Not.new(location: method_type.return_type.location)
373
- )
374
- end
369
+ when defined_in == RBS::BuiltinNames::BasicObject.name && member.instance?
370
+ case method_name
371
+ when :!
372
+ return method_type.with(
373
+ return_type: AST::Types::Logic::Not.new(location: method_type.return_type.location)
374
+ )
375
+ end
375
376
 
376
- when defined_in == RBS::BuiltinNames::Module.name && member.instance?
377
- case method_name
378
- when :===
379
- return method_type.with(
380
- return_type: AST::Types::Logic::ArgIsReceiver.new(location: method_type.return_type.location)
381
- )
377
+ when defined_in == RBS::BuiltinNames::Module.name && member.instance?
378
+ case method_name
379
+ when :===
380
+ return method_type.with(
381
+ return_type: AST::Types::Logic::ArgIsReceiver.new(location: method_type.return_type.location)
382
+ )
383
+ end
382
384
  end
383
385
  end
384
386
  end
@@ -24,6 +24,31 @@ module Steep
24
24
  (base_dir + path).cleanpath
25
25
  end
26
26
 
27
+ def target_for_source_path(path)
28
+ targets.find do |target|
29
+ target.possible_source_file?(path)
30
+ end
31
+ end
32
+
33
+ def targets_for_path(path)
34
+ if target = target_for_source_path(path)
35
+ [target, []]
36
+ else
37
+ [
38
+ nil,
39
+ targets.select do |target|
40
+ target.possible_signature_file?(path)
41
+ end
42
+ ]
43
+ end
44
+ end
45
+
46
+ def all_source_files
47
+ targets.each.with_object(Set[]) do |target, paths|
48
+ paths.merge(target.source_files.keys)
49
+ end
50
+ end
51
+
27
52
  def type_of_node(path:, line:, column:)
28
53
  source_file = targets.map {|target| target.source_files[path] }.compact[0]
29
54
 
@@ -28,15 +28,20 @@ module Steep
28
28
  end
29
29
 
30
30
  def load_sources(command_line_patterns)
31
+ loaded_paths = Set[]
32
+
31
33
  project.targets.each do |target|
32
34
  Steep.logger.tagged "target=#{target.name}" do
33
35
  target_patterns = command_line_patterns.empty? ? target.source_patterns : command_line_patterns
34
36
 
35
- each_path_in_patterns target_patterns, ".rb" do |path|
37
+ each_path_in_patterns(target_patterns, ".rb") do |path|
36
38
  if target.possible_source_file?(path)
37
- unless target.source_file?(path)
39
+ if loaded_paths.include?(path)
40
+ Steep.logger.warn { "Skipping #{target} while loading #{path}... (Already loaded to another target.)" }
41
+ else
38
42
  Steep.logger.info { "Adding source file: #{path}" }
39
43
  target.add_source path, project.absolute_path(path).read
44
+ loaded_paths << path
40
45
  end
41
46
  end
42
47
  end
@@ -0,0 +1,33 @@
1
+ module Steep
2
+ class Project
3
+ class SignatureFile
4
+ attr_reader :path
5
+ attr_reader :content
6
+ attr_reader :content_updated_at
7
+
8
+ attr_reader :status
9
+
10
+ ParseErrorStatus = Struct.new(:error, :timestamp, keyword_init: true)
11
+ DeclarationsStatus = Struct.new(:declarations, :timestamp, keyword_init: true)
12
+
13
+ def initialize(path:)
14
+ @path = path
15
+ self.content = ""
16
+ end
17
+
18
+ def content=(content)
19
+ @content_updated_at = Time.now
20
+ @content = content
21
+ @status = nil
22
+ end
23
+
24
+ def load!
25
+ buffer = RBS::Buffer.new(name: path, content: content)
26
+ decls = RBS::Parser.parse_signature(buffer)
27
+ @status = DeclarationsStatus.new(declarations: decls, timestamp: Time.now)
28
+ rescue RBS::Parser::SyntaxError, RBS::Parser::SemanticsError => exn
29
+ @status = ParseErrorStatus.new(error: exn, timestamp: Time.now)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -8,10 +8,10 @@ module Steep
8
8
 
9
9
  attr_accessor :status
10
10
 
11
- ParseErrorStatus = Struct.new(:error, keyword_init: true)
12
- AnnotationSyntaxErrorStatus = Struct.new(:error, :location, keyword_init: true)
11
+ ParseErrorStatus = Struct.new(:error, :timestamp, keyword_init: true)
12
+ AnnotationSyntaxErrorStatus = Struct.new(:error, :location, :timestamp, keyword_init: true)
13
13
  TypeCheckStatus = Struct.new(:typing, :source, :timestamp, keyword_init: true)
14
- TypeCheckErrorStatus = Struct.new(:error, keyword_init: true)
14
+ TypeCheckErrorStatus = Struct.new(:error, :timestamp, keyword_init: true)
15
15
 
16
16
  def initialize(path:)
17
17
  @path = path
@@ -20,11 +20,9 @@ module Steep
20
20
  end
21
21
 
22
22
  def content=(content)
23
- if @content != content
24
- @content_updated_at = Time.now
25
- @content = content
26
- @status = nil
27
- end
23
+ @content_updated_at = Time.now
24
+ @content = content
25
+ @status = nil
28
26
  end
29
27
 
30
28
  def errors
@@ -55,12 +53,14 @@ module Steep
55
53
  context = TypeInference::Context.new(
56
54
  block_context: nil,
57
55
  module_context: TypeInference::Context::ModuleContext.new(
58
- instance_type: nil,
59
- module_type: nil,
56
+ instance_type: AST::Builtin::Object.instance_type,
57
+ module_type: AST::Builtin::Object.module_type,
60
58
  implement_name: nil,
61
59
  current_namespace: RBS::Namespace.root,
62
60
  const_env: const_env,
63
- class_name: nil
61
+ class_name: AST::Builtin::Object.module_name,
62
+ instance_definition: subtyping.factory.definition_builder.build_instance(AST::Builtin::Object.module_name),
63
+ module_definition: subtyping.factory.definition_builder.build_singleton(AST::Builtin::Object.module_name)
64
64
  ),
65
65
  method_context: nil,
66
66
  break_context: nil,
@@ -86,31 +86,31 @@ module Steep
86
86
 
87
87
  def type_check(subtyping, env_updated_at)
88
88
  # skip type check
89
- return false if status.is_a?(TypeCheckStatus) && env_updated_at <= status.timestamp
89
+ return false if status && env_updated_at <= status.timestamp
90
+
91
+ now = Time.now
90
92
 
91
93
  parse(subtyping.factory) do |source|
92
94
  typing = self.class.type_check(source, subtyping: subtyping)
93
- @status = TypeCheckStatus.new(
94
- typing: typing,
95
- source: source,
96
- timestamp: Time.now
97
- )
95
+ @status = TypeCheckStatus.new(typing: typing, source: source, timestamp: now)
98
96
  rescue RBS::NoTypeFoundError,
99
97
  RBS::NoMixinFoundError,
100
98
  RBS::NoSuperclassFoundError,
101
99
  RBS::DuplicatedMethodDefinitionError,
102
100
  RBS::InvalidTypeApplicationError => exn
103
101
  # Skip logging known signature errors (they are handled with load_signatures(validate: true))
104
- @status = TypeCheckErrorStatus.new(error: exn)
102
+ @status = TypeCheckErrorStatus.new(error: exn, timestamp: now)
105
103
  rescue => exn
106
104
  Steep.log_error(exn)
107
- @status = TypeCheckErrorStatus.new(error: exn)
105
+ @status = TypeCheckErrorStatus.new(error: exn, timestamp: now)
108
106
  end
109
107
 
110
108
  true
111
109
  end
112
110
 
113
111
  def parse(factory)
112
+ now = Time.now
113
+
114
114
  if status.is_a?(TypeCheckStatus)
115
115
  yield status.source
116
116
  else
@@ -118,40 +118,10 @@ module Steep
118
118
  end
119
119
  rescue AnnotationParser::SyntaxError => exn
120
120
  Steep.logger.warn { "Annotation syntax error on #{path}: #{exn.inspect}" }
121
- @status = AnnotationSyntaxErrorStatus.new(error: exn, location: exn.location)
121
+ @status = AnnotationSyntaxErrorStatus.new(error: exn, location: exn.location, timestamp: now)
122
122
  rescue ::Parser::SyntaxError, EncodingError => exn
123
123
  Steep.logger.warn { "Source parsing error on #{path}: #{exn.inspect}" }
124
- @status = ParseErrorStatus.new(error: exn)
125
- end
126
- end
127
-
128
- class SignatureFile
129
- attr_reader :path
130
- attr_reader :content
131
- attr_reader :content_updated_at
132
-
133
- attr_reader :status
134
-
135
- ParseErrorStatus = Struct.new(:error, keyword_init: true)
136
- DeclarationsStatus = Struct.new(:declarations, keyword_init: true)
137
-
138
- def initialize(path:)
139
- @path = path
140
- self.content = ""
141
- end
142
-
143
- def content=(content)
144
- @content_updated_at = Time.now
145
- @content = content
146
- @status = nil
147
- end
148
-
149
- def load!
150
- buffer = RBS::Buffer.new(name: path, content: content)
151
- decls = RBS::Parser.parse_signature(buffer)
152
- @status = DeclarationsStatus.new(declarations: decls)
153
- rescue RBS::Parser::SyntaxError, RBS::Parser::SemanticsError => exn
154
- @status = ParseErrorStatus.new(error: exn)
124
+ @status = ParseErrorStatus.new(error: exn, timestamp: now)
155
125
  end
156
126
  end
157
127
  end
@@ -29,9 +29,15 @@ module Steep
29
29
  @signature_files = {}
30
30
  end
31
31
 
32
- def add_source(path, content)
32
+ def add_source(path, content = "")
33
33
  file = SourceFile.new(path: path)
34
- file.content = content
34
+
35
+ if block_given?
36
+ file.content = yield
37
+ else
38
+ file.content = content
39
+ end
40
+
35
41
  source_files[path] = file
36
42
  end
37
43
 
@@ -39,14 +45,22 @@ module Steep
39
45
  source_files.delete(path)
40
46
  end
41
47
 
42
- def update_source(path, content)
48
+ def update_source(path, content = nil)
43
49
  file = source_files[path]
44
- file.content = content
50
+ if block_given?
51
+ file.content = yield(file.content)
52
+ else
53
+ file.content = content || file.content
54
+ end
45
55
  end
46
56
 
47
- def add_signature(path, content)
57
+ def add_signature(path, content = "")
48
58
  file = SignatureFile.new(path: path)
49
- file.content = content
59
+ if block_given?
60
+ file.content = yield
61
+ else
62
+ file.content = content
63
+ end
50
64
  signature_files[path] = file
51
65
  end
52
66
 
@@ -54,9 +68,13 @@ module Steep
54
68
  signature_files.delete(path)
55
69
  end
56
70
 
57
- def update_signature(path, content)
71
+ def update_signature(path, content = nil)
58
72
  file = signature_files[path]
59
- file.content = content
73
+ if block_given?
74
+ file.content = yield(file.content)
75
+ else
76
+ file.content = content || file.content
77
+ end
60
78
  end
61
79
 
62
80
  def source_file?(path)
@@ -115,6 +133,7 @@ module Steep
115
133
  when TypeCheckStatus
116
134
  status.timestamp
117
135
  end
136
+ now = Time.now
118
137
 
119
138
  updated_files = []
120
139
 
@@ -151,11 +170,11 @@ module Steep
151
170
  validator.validate()
152
171
 
153
172
  if validator.no_error?
154
- yield env, check, Time.now
173
+ yield env, check, now
155
174
  else
156
175
  @status = SignatureValidationErrorStatus.new(
157
176
  errors: validator.each_error.to_a,
158
- timestamp: Time.now
177
+ timestamp: now
159
178
  )
160
179
  end
161
180
  else
@@ -169,11 +188,11 @@ module Steep
169
188
  location: exn.decls[0].location
170
189
  )
171
190
  ],
172
- timestamp: Time.now
191
+ timestamp: now
173
192
  )
174
193
  rescue => exn
175
194
  Steep.log_error exn
176
- @status = SignatureOtherErrorStatus.new(error: exn, timestamp: Time.now)
195
+ @status = SignatureOtherErrorStatus.new(error: exn, timestamp: now)
177
196
  end
178
197
  end
179
198
 
@@ -5,47 +5,32 @@ module Steep
5
5
 
6
6
  include Utils
7
7
 
8
- attr_reader :target_files
8
+ attr_reader :typecheck_paths
9
9
  attr_reader :queue
10
10
 
11
11
  def initialize(project:, reader:, writer:, queue: Queue.new)
12
12
  super(project: project, reader: reader, writer: writer)
13
13
 
14
- @target_files = {}
14
+ @typecheck_paths = Set[]
15
15
  @queue = queue
16
16
  end
17
17
 
18
- def enqueue_type_check(target:, path:, version: target_files[path])
19
- Steep.logger.info "Enqueueing type check: #{path}(#{version})@#{target.name}..."
20
- target_files[path] = version
21
- queue << [path, version, target]
22
- end
23
-
24
- def each_type_check_subject(path:, version:)
25
- case
26
- when !(updated_targets = project.targets.select {|target| target.signature_file?(path) }).empty?
27
- updated_targets.each do |target|
28
- target_files.each_key do |path|
29
- if target.source_file?(path)
30
- yield target, path, target_files[path]
31
- end
32
- end
33
- end
34
-
35
- when target = project.targets.find {|target| target.source_file?(path) }
36
- if target_files.key?(path)
37
- yield target, path, version
38
- end
39
- end
18
+ def enqueue_type_check(target:, path:)
19
+ Steep.logger.info "Enqueueing type check: #{target.name}::#{path}..."
20
+ queue << [target, path]
40
21
  end
41
22
 
42
23
  def typecheck_file(path, target)
43
- Steep.logger.info "Starting type checking: #{path}@#{target.name}..."
24
+ Steep.logger.info "Starting type checking: #{target.name}::#{path}..."
44
25
 
45
26
  source = target.source_files[path]
46
27
  target.type_check(target_sources: [source], validate_signatures: false)
47
28
 
48
- Steep.logger.info "Finished type checking: #{path}@#{target.name}"
29
+ if target.status.is_a?(Project::Target::TypeCheckStatus) && target.status.type_check_sources.empty?
30
+ Steep.logger.debug "Skipped type checking: #{target.name}::#{path}"
31
+ else
32
+ Steep.logger.info "Finished type checking: #{target.name}::#{path}"
33
+ end
49
34
 
50
35
  diagnostics = source_diagnostics(source, target.options)
51
36
 
@@ -109,8 +94,8 @@ module Steep
109
94
  # Don't respond to initialize request, but start type checking.
110
95
  project.targets.each do |target|
111
96
  target.source_files.each_key do |path|
112
- if target_files.key?(path)
113
- enqueue_type_check(target: target, path: path, version: target_files[path])
97
+ if typecheck_paths.include?(path)
98
+ enqueue_type_check(target: target, path: path)
114
99
  end
115
100
  end
116
101
  end
@@ -118,35 +103,34 @@ module Steep
118
103
  when "workspace/executeCommand"
119
104
  if request[:params][:command] == "steep/registerSourceToWorker"
120
105
  paths = request[:params][:arguments].map {|arg| source_path(URI.parse(arg)) }
121
- paths.each do |path|
122
- target_files[path] = 0
123
- end
106
+ typecheck_paths.merge(paths)
124
107
  end
125
108
 
126
109
  when "textDocument/didChange"
127
- update_source(request) do |path, version|
128
- if target_files.key?(path)
129
- target_files[path] = version
110
+ update_source(request) do |path, _|
111
+ source_target, signature_targets = project.targets_for_path(path)
112
+
113
+ if source_target
114
+ if typecheck_paths.include?(path)
115
+ enqueue_type_check(target: source_target, path: path)
116
+ end
130
117
  end
131
- end
132
118
 
133
- path = source_path(URI.parse(request[:params][:textDocument][:uri]))
134
- version = request[:params][:textDocument][:version]
135
- each_type_check_subject(path: path, version: version) do |target, path, version|
136
- enqueue_type_check(target: target, path: path, version: version)
119
+ signature_targets.each do |target|
120
+ target.source_files.each_key do |source_path|
121
+ if typecheck_paths.include?(source_path)
122
+ enqueue_type_check(target: target, path: source_path)
123
+ end
124
+ end
125
+ end
137
126
  end
138
127
  end
139
128
  end
140
129
 
141
130
  def handle_job(job)
142
- sleep 0.1
131
+ target, path = job
143
132
 
144
- path, version, target = job
145
- if !version || target_files[path] == version
146
- typecheck_file(path, target)
147
- else
148
- Steep.logger.info "Skipping type check: #{path}@#{target.name}, queued version=#{version}, latest version=#{target_files[path]}"
149
- end
133
+ typecheck_file(path, target)
150
134
  end
151
135
  end
152
136
  end
@@ -14,6 +14,8 @@ module Steep
14
14
  attr_reader :signature_worker
15
15
  attr_reader :code_workers
16
16
 
17
+ include Utils
18
+
17
19
  def initialize(project:, reader:, writer:, interaction_worker:, signature_worker:, code_workers:, queue: Queue.new)
18
20
  @project = project
19
21
  @reader = reader
@@ -27,19 +29,24 @@ module Steep
27
29
  end
28
30
 
29
31
  def start
30
- source_paths = project.targets.flat_map {|target| target.source_files.keys }
32
+ source_paths = project.all_source_files
31
33
  bin_size = (source_paths.size / code_workers.size) + 1
32
34
  source_paths.each_slice(bin_size).with_index do |paths, index|
33
35
  register_code_to_worker(paths, worker: code_workers[index])
34
36
  end
35
37
 
38
+ tags = Steep.logger.formatter.current_tags.dup
39
+ tags << "master"
40
+
36
41
  Thread.new do
42
+ Steep.logger.formatter.push_tags(*tags, "from-worker@interaction")
37
43
  interaction_worker.reader.read do |message|
38
44
  process_message_from_worker(message)
39
45
  end
40
46
  end
41
47
 
42
48
  Thread.new do
49
+ Steep.logger.formatter.push_tags(*tags, "from-worker@signature")
43
50
  signature_worker.reader.read do |message|
44
51
  process_message_from_worker(message)
45
52
  end
@@ -47,6 +54,7 @@ module Steep
47
54
 
48
55
  code_workers.each do |worker|
49
56
  Thread.new do
57
+ Steep.logger.formatter.push_tags(*tags, "from-worker@#{worker.name}")
50
58
  worker.reader.read do |message|
51
59
  process_message_from_worker(message)
52
60
  end
@@ -54,6 +62,7 @@ module Steep
54
62
  end
55
63
 
56
64
  Thread.new do
65
+ Steep.logger.formatter.push_tags(*tags, "from-client")
57
66
  reader.read do |request|
58
67
  process_message_from_client(request)
59
68
  end
@@ -97,7 +106,7 @@ module Steep
97
106
  result: LSP::Interface::InitializeResult.new(
98
107
  capabilities: LSP::Interface::ServerCapabilities.new(
99
108
  text_document_sync: LSP::Interface::TextDocumentSyncOptions.new(
100
- change: LSP::Constant::TextDocumentSyncKind::FULL
109
+ change: LSP::Constant::TextDocumentSyncKind::INCREMENTAL
101
110
  ),
102
111
  hover_provider: true,
103
112
  completion_provider: LSP::Interface::CompletionOptions.new(
@@ -112,36 +121,10 @@ module Steep
112
121
  end
113
122
 
114
123
  when "textDocument/didChange"
124
+ update_source(message)
125
+
115
126
  uri = URI.parse(message[:params][:textDocument][:uri])
116
127
  path = project.relative_path(Pathname(uri.path))
117
- text = message[:params][:contentChanges][0][:text]
118
-
119
- project.targets.each do |target|
120
- case
121
- when target.source_file?(path)
122
- if text.empty? && !path.file?
123
- Steep.logger.info { "Deleting source file: #{path}..." }
124
- target.remove_source(path)
125
- else
126
- Steep.logger.info { "Updating source file: #{path}..." }
127
- target.update_source(path, text)
128
- end
129
- when target.possible_source_file?(path)
130
- Steep.logger.info { "Adding source file: #{path}..." }
131
- target.add_source(path, text)
132
- when target.signature_file?(path)
133
- if text.empty? && !path.file?
134
- Steep.logger.info { "Deleting signature file: #{path}..." }
135
- target.remove_signature(path)
136
- else
137
- Steep.logger.info { "Updating signature file: #{path}..." }
138
- target.update_signature(path, text)
139
- end
140
- when target.possible_signature_file?(path)
141
- Steep.logger.info { "Adding signature file: #{path}..." }
142
- target.add_signature(path, text)
143
- end
144
- end
145
128
 
146
129
  unless registered_path?(path)
147
130
  register_code_to_worker [path], worker: least_busy_worker()
@@ -7,23 +7,56 @@ module Steep
7
7
  project.relative_path(Pathname(uri.path))
8
8
  end
9
9
 
10
+ def apply_change(change, text)
11
+ range = change[:range]
12
+
13
+ if range
14
+ text = text.dup
15
+
16
+ buf = AST::Buffer.new(name: :_, content: text)
17
+
18
+ start_pos = buf.loc_to_pos(range[:start].yield_self {|pos| [pos[:line]+1, pos[:character]] })
19
+ end_pos = buf.loc_to_pos(range[:end].yield_self {|pos| [pos[:line]+1, pos[:character]] })
20
+
21
+ text[start_pos...end_pos] = change[:text]
22
+ text
23
+ else
24
+ change[:text]
25
+ end
26
+ end
27
+
10
28
  def update_source(request)
11
29
  path = source_path(URI.parse(request[:params][:textDocument][:uri]))
12
- text = request[:params][:contentChanges][0][:text]
13
30
  version = request[:params][:textDocument][:version]
31
+ Steep.logger.info { "Updating source: path=#{path}, version=#{version}..." }
32
+
33
+ changes = request[:params][:contentChanges]
34
+
35
+ source_target, signature_targets = project.targets_for_path(path)
36
+
37
+ if source_target
38
+ changes.each do |change|
39
+ case
40
+ when source_target.source_file?(path)
41
+ Steep.logger.debug { "Updating source in #{source_target.name}: path=#{path}" }
42
+ source_target.update_source(path) {|text| apply_change(change, text) }
43
+ when source_target.possible_source_file?(path)
44
+ Steep.logger.debug { "Adding source to #{source_target.name}: path=#{path}" }
45
+ source_target.add_source(path, change[:text])
46
+ end
47
+ end
48
+ end
14
49
 
15
- Steep.logger.debug "Updateing source: path=#{path}, version=#{version}, size=#{text.bytesize}"
16
-
17
- project.targets.each do |target|
18
- case
19
- when target.source_file?(path)
20
- target.update_source path, text
21
- when target.possible_source_file?(path)
22
- target.add_source path, text
23
- when target.signature_file?(path)
24
- target.update_signature path, text
25
- when target.possible_signature_file?(path)
26
- target.add_signature path, text
50
+ signature_targets.each do |target|
51
+ changes.each do |change|
52
+ case
53
+ when target.signature_file?(path)
54
+ Steep.logger.debug { "Updating signature in #{target.name}: path=#{path}" }
55
+ target.update_signature(path) {|text| apply_change(change, text) }
56
+ when target.possible_signature_file?(path)
57
+ Steep.logger.debug { "Adding signature to #{target.name}: path=#{path}" }
58
+ target.add_signature(path, change[:text])
59
+ end
27
60
  end
28
61
  end
29
62
 
@@ -5,13 +5,15 @@ module Steep
5
5
  attr_reader :writer
6
6
  attr_reader :stderr
7
7
 
8
+ attr_reader :name
8
9
  attr_reader :wait_thread
9
10
 
10
- def initialize(reader:, writer:, stderr:, wait_thread:)
11
+ def initialize(reader:, writer:, stderr:, wait_thread:, name:)
11
12
  @reader = reader
12
13
  @writer = writer
13
14
  @stderr = stderr
14
15
  @wait_thread = wait_thread
16
+ @name = name
15
17
  end
16
18
 
17
19
  def self.spawn_worker(type, name:, steepfile:)
@@ -33,7 +35,7 @@ module Steep
33
35
  writer = LanguageServer::Protocol::Transport::Io::Writer.new(stdin)
34
36
  reader = LanguageServer::Protocol::Transport::Io::Reader.new(stdout)
35
37
 
36
- new(reader: reader, writer: writer, stderr: stderr, wait_thread: thread)
38
+ new(reader: reader, writer: writer, stderr: stderr, wait_thread: thread, name: name)
37
39
  end
38
40
 
39
41
  def self.spawn_code_workers(steepfile:, count: [Etc.nprocessors-3, 1].max)
@@ -1,3 +1,3 @@
1
1
  module Steep
2
- VERSION = "0.29.0"
2
+ VERSION = "0.30.0"
3
3
  end
@@ -0,0 +1,5 @@
1
+ target :test do
2
+ typing_options :strict
3
+ check "*.rb"
4
+ signature "*.rbs"
5
+ end
@@ -0,0 +1,4 @@
1
+ # !expects MethodBodyTypeMismatch: method=fizz_buzz, expected=::String, actual=nil
2
+ def fizz_buzz(n)
3
+ nil
4
+ end
@@ -0,0 +1,3 @@
1
+ class Object
2
+ def fizz_buzz: (Integer) -> String
3
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: steep
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.29.0
4
+ version: 0.30.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Soutaro Matsumoto
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-09-28 00:00:00.000000000 Z
11
+ date: 2020-10-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser
@@ -187,10 +187,11 @@ files:
187
187
  - lib/steep/project.rb
188
188
  - lib/steep/project/completion_provider.rb
189
189
  - lib/steep/project/dsl.rb
190
- - lib/steep/project/file.rb
191
190
  - lib/steep/project/file_loader.rb
192
191
  - lib/steep/project/hover_content.rb
193
192
  - lib/steep/project/options.rb
193
+ - lib/steep/project/signature_file.rb
194
+ - lib/steep/project/source_file.rb
194
195
  - lib/steep/project/target.rb
195
196
  - lib/steep/server/base_worker.rb
196
197
  - lib/steep/server/code_worker.rb
@@ -347,6 +348,9 @@ files:
347
348
  - smoke/super/Steepfile
348
349
  - smoke/super/a.rb
349
350
  - smoke/super/a.rbs
351
+ - smoke/toplevel/Steepfile
352
+ - smoke/toplevel/a.rb
353
+ - smoke/toplevel/a.rbs
350
354
  - smoke/type_case/Steepfile
351
355
  - smoke/type_case/a.rb
352
356
  - smoke/yield/Steepfile