refined-steep-server 0.1.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 +7 -0
- data/.claude/commands/add-stub.md +7 -0
- data/.claude/commands/check.md +7 -0
- data/.github/workflows/ci.yml +55 -0
- data/.rspec +3 -0
- data/CLAUDE.md +57 -0
- data/README.md +147 -0
- data/Rakefile +12 -0
- data/Steepfile +5 -0
- data/exe/refined-steep-server +36 -0
- data/lib/refined/steep/server/base_server.rb +171 -0
- data/lib/refined/steep/server/io.rb +54 -0
- data/lib/refined/steep/server/lsp_server.rb +851 -0
- data/lib/refined/steep/server/message.rb +150 -0
- data/lib/refined/steep/server/steep_state.rb +80 -0
- data/lib/refined/steep/server/store.rb +113 -0
- data/lib/refined/steep/server/version.rb +9 -0
- data/lib/refined/steep/server.rb +22 -0
- data/rbs_collection.yaml +19 -0
- data/sig/external/steep.rbs +376 -0
- data/sig/refined/steep/server.rbs +8 -0
- data/spec/refined/steep/server/base_server_spec.rb +116 -0
- data/spec/refined/steep/server/io_spec.rb +54 -0
- data/spec/refined/steep/server/lsp_server_spec.rb +317 -0
- data/spec/refined/steep/server/message_spec.rb +80 -0
- data/spec/refined/steep/server/steep_state_spec.rb +46 -0
- data/spec/refined/steep/server/store_spec.rb +82 -0
- data/spec/spec_helper.rb +87 -0
- metadata +98 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# rbs_inline: enabled
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Refined
|
|
5
|
+
module Steep
|
|
6
|
+
module Server
|
|
7
|
+
Interface = LanguageServer::Protocol::Interface
|
|
8
|
+
Constant = LanguageServer::Protocol::Constant
|
|
9
|
+
Transport = LanguageServer::Protocol::Transport
|
|
10
|
+
|
|
11
|
+
class Message
|
|
12
|
+
attr_reader :method #: String
|
|
13
|
+
attr_reader :params #: untyped
|
|
14
|
+
|
|
15
|
+
# @rbs method: String
|
|
16
|
+
# @rbs params: untyped
|
|
17
|
+
# @rbs return: void
|
|
18
|
+
def initialize(method:, params:)
|
|
19
|
+
@method = method
|
|
20
|
+
@params = params
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
24
|
+
def to_hash
|
|
25
|
+
raise NotImplementedError
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class Notification < Message
|
|
30
|
+
class << self
|
|
31
|
+
# @rbs message: String
|
|
32
|
+
# @rbs type: Integer
|
|
33
|
+
# @rbs return: Notification
|
|
34
|
+
def window_show_message(message, type: Constant::MessageType::INFO)
|
|
35
|
+
new(
|
|
36
|
+
method: "window/showMessage",
|
|
37
|
+
params: Interface::ShowMessageParams.new(type: type, message: message),
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @rbs message: String
|
|
42
|
+
# @rbs type: Integer
|
|
43
|
+
# @rbs return: Notification
|
|
44
|
+
def window_log_message(message, type: Constant::MessageType::LOG)
|
|
45
|
+
new(
|
|
46
|
+
method: "window/logMessage",
|
|
47
|
+
params: Interface::LogMessageParams.new(type: type, message: message),
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @rbs uri: String
|
|
52
|
+
# @rbs diagnostics: Array[untyped]
|
|
53
|
+
# @rbs version: Integer?
|
|
54
|
+
# @rbs return: Notification
|
|
55
|
+
def publish_diagnostics(uri, diagnostics, version: nil)
|
|
56
|
+
new(
|
|
57
|
+
method: "textDocument/publishDiagnostics",
|
|
58
|
+
params: Interface::PublishDiagnosticsParams.new(uri: uri, diagnostics: diagnostics, version: version),
|
|
59
|
+
)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
64
|
+
def to_hash
|
|
65
|
+
hash = { method: @method } #: Hash[Symbol, untyped]
|
|
66
|
+
|
|
67
|
+
if @params
|
|
68
|
+
hash[:params] = @params.to_hash
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
hash
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class Request < Message
|
|
76
|
+
# @rbs @id: Integer | String
|
|
77
|
+
|
|
78
|
+
# @rbs id: Integer | String
|
|
79
|
+
# @rbs method: String
|
|
80
|
+
# @rbs params: untyped
|
|
81
|
+
# @rbs return: void
|
|
82
|
+
def initialize(id:, method:, params:)
|
|
83
|
+
@id = id
|
|
84
|
+
super(method: method, params: params)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
88
|
+
def to_hash
|
|
89
|
+
hash = { id: @id, method: @method } #: Hash[Symbol, untyped]
|
|
90
|
+
|
|
91
|
+
if @params
|
|
92
|
+
hash[:params] = @params.to_hash
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
hash
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
class ErrorResponse
|
|
100
|
+
# @rbs @id: Integer
|
|
101
|
+
# @rbs @data: Hash[Symbol, untyped]?
|
|
102
|
+
|
|
103
|
+
attr_reader :message #: String
|
|
104
|
+
attr_reader :code #: Integer
|
|
105
|
+
|
|
106
|
+
# @rbs id: Integer
|
|
107
|
+
# @rbs code: Integer
|
|
108
|
+
# @rbs message: String
|
|
109
|
+
# @rbs data: Hash[Symbol, untyped]?
|
|
110
|
+
# @rbs return: void
|
|
111
|
+
def initialize(id:, code:, message:, data: nil)
|
|
112
|
+
@id = id
|
|
113
|
+
@code = code
|
|
114
|
+
@message = message
|
|
115
|
+
@data = data
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
119
|
+
def to_hash
|
|
120
|
+
{
|
|
121
|
+
id: @id,
|
|
122
|
+
error: {
|
|
123
|
+
code: @code,
|
|
124
|
+
message: @message,
|
|
125
|
+
data: @data,
|
|
126
|
+
},
|
|
127
|
+
}
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
class Result
|
|
132
|
+
attr_reader :response #: untyped
|
|
133
|
+
attr_reader :id #: Integer
|
|
134
|
+
|
|
135
|
+
# @rbs id: Integer
|
|
136
|
+
# @rbs response: untyped
|
|
137
|
+
# @rbs return: void
|
|
138
|
+
def initialize(id:, response:)
|
|
139
|
+
@id = id
|
|
140
|
+
@response = response
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
144
|
+
def to_hash
|
|
145
|
+
{ id: @id, result: @response }
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# rbs_inline: enabled
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Refined
|
|
5
|
+
module Steep
|
|
6
|
+
module Server
|
|
7
|
+
class SteepState
|
|
8
|
+
# @rbs @buffered_changes: Hash[Pathname, Array[::Steep::Services::ContentChange]]
|
|
9
|
+
|
|
10
|
+
attr_reader :project #: ::Steep::Project
|
|
11
|
+
attr_reader :type_check_service #: ::Steep::Services::TypeCheckService
|
|
12
|
+
attr_reader :assignment #: ::Steep::Services::PathAssignment
|
|
13
|
+
attr_reader :mutex #: Mutex
|
|
14
|
+
|
|
15
|
+
# @rbs steepfile_path: Pathname
|
|
16
|
+
# @rbs return: void
|
|
17
|
+
def initialize(steepfile_path:)
|
|
18
|
+
@mutex = Mutex.new
|
|
19
|
+
@project = load_project(steepfile_path)
|
|
20
|
+
@type_check_service = ::Steep::Services::TypeCheckService.new(project: @project)
|
|
21
|
+
@assignment = ::Steep::Services::PathAssignment.all
|
|
22
|
+
@buffered_changes = {}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @rbs path: Pathname
|
|
26
|
+
# @rbs changes: Array[::Steep::Services::ContentChange]
|
|
27
|
+
# @rbs return: void
|
|
28
|
+
def push_changes(path, changes)
|
|
29
|
+
@mutex.synchronize do
|
|
30
|
+
existing = @buffered_changes[path]
|
|
31
|
+
unless existing
|
|
32
|
+
existing = [] #: Array[::Steep::Services::ContentChange]
|
|
33
|
+
@buffered_changes[path] = existing
|
|
34
|
+
end
|
|
35
|
+
existing.concat(changes)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @rbs return: Hash[Pathname, Array[::Steep::Services::ContentChange]]
|
|
40
|
+
def flush_changes
|
|
41
|
+
@mutex.synchronize do
|
|
42
|
+
copy = @buffered_changes.dup
|
|
43
|
+
@buffered_changes.clear
|
|
44
|
+
copy
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @rbs return: void
|
|
49
|
+
def apply_changes
|
|
50
|
+
changes = flush_changes
|
|
51
|
+
type_check_service.update(changes: changes) unless changes.empty?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @rbs path: Pathname
|
|
55
|
+
# @rbs return: ::Steep::Project::Target?
|
|
56
|
+
def target_for_path(path)
|
|
57
|
+
project.target_for_source_path(path) ||
|
|
58
|
+
project.target_for_signature_path(path)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @rbs target_name: Symbol
|
|
62
|
+
# @rbs return: ::Steep::Services::SignatureService?
|
|
63
|
+
def signature_service_for(target_name)
|
|
64
|
+
type_check_service.signature_services[target_name]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
# @rbs steepfile_path: Pathname
|
|
70
|
+
# @rbs return: ::Steep::Project
|
|
71
|
+
def load_project(steepfile_path)
|
|
72
|
+
path = steepfile_path.expand_path
|
|
73
|
+
::Steep::Project.new(steepfile_path: path).tap do |project|
|
|
74
|
+
::Steep::Project::DSL.parse(project, path.read, filename: path.to_s)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# rbs_inline: enabled
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Refined
|
|
5
|
+
module Steep
|
|
6
|
+
module Server
|
|
7
|
+
class Store
|
|
8
|
+
class DocumentEntry
|
|
9
|
+
attr_reader :uri #: String
|
|
10
|
+
attr_reader :content #: String
|
|
11
|
+
attr_reader :version #: Integer
|
|
12
|
+
|
|
13
|
+
# @rbs uri: String
|
|
14
|
+
# @rbs content: String
|
|
15
|
+
# @rbs version: Integer
|
|
16
|
+
# @rbs return: void
|
|
17
|
+
def initialize(uri:, content:, version:)
|
|
18
|
+
@uri = uri
|
|
19
|
+
@content = content
|
|
20
|
+
@version = version
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @rbs @steep_state: SteepState
|
|
25
|
+
# @rbs @documents: Hash[String, DocumentEntry]
|
|
26
|
+
|
|
27
|
+
# @rbs steep_state: SteepState
|
|
28
|
+
# @rbs return: void
|
|
29
|
+
def initialize(steep_state)
|
|
30
|
+
@steep_state = steep_state
|
|
31
|
+
@documents = {}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @rbs uri: String
|
|
35
|
+
# @rbs text: String
|
|
36
|
+
# @rbs version: Integer
|
|
37
|
+
# @rbs return: void
|
|
38
|
+
def open(uri:, text:, version:)
|
|
39
|
+
@documents[uri] = DocumentEntry.new(uri: uri, content: text, version: version)
|
|
40
|
+
|
|
41
|
+
path = uri_to_relative_path(uri)
|
|
42
|
+
return unless path
|
|
43
|
+
|
|
44
|
+
@steep_state.push_changes(path, [
|
|
45
|
+
::Steep::Services::ContentChange.new(text: text),
|
|
46
|
+
])
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @rbs uri: String
|
|
50
|
+
# @rbs content_changes: Array[Hash[Symbol, untyped]]
|
|
51
|
+
# @rbs version: Integer
|
|
52
|
+
# @rbs return: void
|
|
53
|
+
def change(uri:, content_changes:, version:)
|
|
54
|
+
entry = @documents[uri]
|
|
55
|
+
return unless entry
|
|
56
|
+
|
|
57
|
+
changes = content_changes.map do |change|
|
|
58
|
+
range = change[:range]
|
|
59
|
+
::Steep::Services::ContentChange.new(
|
|
60
|
+
range: range && [
|
|
61
|
+
::Steep::Services::ContentChange::Position.new(
|
|
62
|
+
line: range[:start][:line] + 1,
|
|
63
|
+
column: range[:start][:character],
|
|
64
|
+
),
|
|
65
|
+
::Steep::Services::ContentChange::Position.new(
|
|
66
|
+
line: range[:end][:line] + 1,
|
|
67
|
+
column: range[:end][:character],
|
|
68
|
+
),
|
|
69
|
+
],
|
|
70
|
+
text: change[:text],
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Update the stored content with the last full text
|
|
75
|
+
last_change = content_changes.last
|
|
76
|
+
if last_change && !last_change[:range]
|
|
77
|
+
@documents[uri] = DocumentEntry.new(uri: uri, content: last_change[:text], version: version)
|
|
78
|
+
else
|
|
79
|
+
@documents[uri] = DocumentEntry.new(uri: uri, content: entry.content, version: version)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
path = uri_to_relative_path(uri)
|
|
83
|
+
return unless path
|
|
84
|
+
|
|
85
|
+
@steep_state.push_changes(path, changes)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# @rbs uri: String
|
|
89
|
+
# @rbs return: void
|
|
90
|
+
def close(uri:)
|
|
91
|
+
@documents.delete(uri)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# @rbs uri: String
|
|
95
|
+
# @rbs return: DocumentEntry?
|
|
96
|
+
def get(uri)
|
|
97
|
+
@documents[uri]
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
|
|
102
|
+
# @rbs uri: String
|
|
103
|
+
# @rbs return: Pathname?
|
|
104
|
+
def uri_to_relative_path(uri)
|
|
105
|
+
path = ::Steep::PathHelper.to_pathname(uri)
|
|
106
|
+
return unless path
|
|
107
|
+
|
|
108
|
+
@steep_state.project.relative_path(path)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# rbs_inline: enabled
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "language_server-protocol"
|
|
5
|
+
require "steep"
|
|
6
|
+
require "json"
|
|
7
|
+
|
|
8
|
+
require_relative "server/version"
|
|
9
|
+
require_relative "server/message"
|
|
10
|
+
require_relative "server/io"
|
|
11
|
+
require_relative "server/base_server"
|
|
12
|
+
require_relative "server/steep_state"
|
|
13
|
+
require_relative "server/store"
|
|
14
|
+
require_relative "server/lsp_server"
|
|
15
|
+
|
|
16
|
+
module Refined
|
|
17
|
+
module Steep
|
|
18
|
+
module Server
|
|
19
|
+
class Error < StandardError; end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
data/rbs_collection.yaml
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Download sources
|
|
2
|
+
sources:
|
|
3
|
+
- type: git
|
|
4
|
+
name: ruby/gem_rbs_collection
|
|
5
|
+
remote: https://github.com/ruby/gem_rbs_collection.git
|
|
6
|
+
revision: main
|
|
7
|
+
repo_dir: gems
|
|
8
|
+
|
|
9
|
+
# You can specify local directories as sources also.
|
|
10
|
+
# - type: local
|
|
11
|
+
# path: path/to/your/local/repository
|
|
12
|
+
|
|
13
|
+
# A directory to install the downloaded RBSs
|
|
14
|
+
path: .gem_rbs_collection
|
|
15
|
+
|
|
16
|
+
# gems:
|
|
17
|
+
# # If you want to avoid installing rbs files for gems, you can specify them here.
|
|
18
|
+
# - name: GEM_NAME
|
|
19
|
+
# ignore: true
|