steep 0.29.0 → 0.30.0

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: 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