Class: Steep::Server::Master

Inherits:
Object
  • Object
show all
Defined in:
lib/steep/server/master.rb

Constant Summary collapse

LSP =
LanguageServer::Protocol

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(project:, reader:, writer:, interaction_worker:, signature_worker:, code_workers:, queue: Queue.new) ⇒ Master

Returns a new instance of Master.



17
18
19
20
21
22
23
24
25
26
# File 'lib/steep/server/master.rb', line 17

def initialize(project:, reader:, writer:, interaction_worker:, signature_worker:, code_workers:, queue: Queue.new)
  @project = project
  @reader = reader
  @writer = writer
  @queue = queue
  @interaction_worker = interaction_worker
  @signature_worker = signature_worker
  @code_workers = code_workers
  @worker_to_paths = {}
end

Instance Attribute Details

#code_workersObject (readonly)

Returns the value of attribute code_workers.



15
16
17
# File 'lib/steep/server/master.rb', line 15

def code_workers
  @code_workers
end

#interaction_workerObject (readonly)

Returns the value of attribute interaction_worker.



13
14
15
# File 'lib/steep/server/master.rb', line 13

def interaction_worker
  @interaction_worker
end

#projectObject (readonly)

Returns the value of attribute project.



7
8
9
# File 'lib/steep/server/master.rb', line 7

def project
  @project
end

#queueObject (readonly)

Returns the value of attribute queue.



9
10
11
# File 'lib/steep/server/master.rb', line 9

def queue
  @queue
end

#readerObject (readonly)

Returns the value of attribute reader.



8
9
10
# File 'lib/steep/server/master.rb', line 8

def reader
  @reader
end

#signature_workerObject (readonly)

Returns the value of attribute signature_worker.



14
15
16
# File 'lib/steep/server/master.rb', line 14

def signature_worker
  @signature_worker
end

#steepfileObject (readonly)

Returns the value of attribute steepfile.



6
7
8
# File 'lib/steep/server/master.rb', line 6

def steepfile
  @steepfile
end

#worker_countObject (readonly)

Returns the value of attribute worker_count.



10
11
12
# File 'lib/steep/server/master.rb', line 10

def worker_count
  @worker_count
end

#worker_to_pathsObject (readonly)

Returns the value of attribute worker_to_paths.



11
12
13
# File 'lib/steep/server/master.rb', line 11

def worker_to_paths
  @worker_to_paths
end

#writerObject (readonly)

Returns the value of attribute writer.



8
9
10
# File 'lib/steep/server/master.rb', line 8

def writer
  @writer
end

Instance Method Details

#each_worker(&block) ⇒ Object



72
73
74
75
76
77
78
79
80
# File 'lib/steep/server/master.rb', line 72

def each_worker(&block)
  if block_given?
    yield interaction_worker
    yield signature_worker
    code_workers.each &block
  else
    enum_for :each_worker
  end
end

#least_busy_workerObject



171
172
173
174
175
# File 'lib/steep/server/master.rb', line 171

def least_busy_worker
  code_workers.min_by do |w|
    paths_for(w).size
  end
end

#paths_for(worker) ⇒ Object



167
168
169
# File 'lib/steep/server/master.rb', line 167

def paths_for(worker)
  worker_to_paths[worker] ||= Set[]
end

#process_message_from_client(message) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/steep/server/master.rb', line 82

def process_message_from_client(message)
  id = message[:id]

  case message[:method]
  when "initialize"
    queue << {
      id: id,
      result: LSP::Interface::InitializeResult.new(
        capabilities: LSP::Interface::ServerCapabilities.new(
          text_document_sync: LSP::Interface::TextDocumentSyncOptions.new(
            change: LSP::Constant::TextDocumentSyncKind::FULL
          ),
          hover_provider: true,
          completion_provider: LSP::Interface::CompletionOptions.new(
            trigger_characters: [".", "@"]
          )
        )
      )
    }

    each_worker do |worker|
      worker << message
    end

  when "textDocument/didChange"
    uri = URI.parse(message[:params][:textDocument][:uri])
    path = project.relative_path(Pathname(uri.path))
    text = message[:params][:contentChanges][0][:text]

    project.targets.each do |target|
      case
      when target.source_file?(path)
        if text.empty? && !path.file?
          Steep.logger.info { "Deleting source file: #{path}..." }
          target.remove_source(path)
        else
          Steep.logger.info { "Updating source file: #{path}..." }
          target.update_source(path, text)
        end
      when target.possible_source_file?(path)
        Steep.logger.info { "Adding source file: #{path}..." }
        target.add_source(path, text)
      when target.signature_file?(path)
        if text.empty? && !path.file?
          Steep.logger.info { "Deleting signature file: #{path}..." }
          target.remove_signature(path)
        else
          Steep.logger.info { "Updating signature file: #{path}..." }
          target.update_signature(path, text)
        end
      when target.possible_signature_file?(path)
        Steep.logger.info { "Adding signature file: #{path}..." }
        target.add_signature(path, text)
      end
    end

    unless registered_path?(path)
      register_code_to_worker [path], worker: least_busy_worker()
    end

    each_worker do |worker|
      worker << message
    end

  when "textDocument/hover"
    interaction_worker << message

  when "textDocument/completion"
    interaction_worker << message

  when "textDocument/open"
    # Ignores open notification

  when "shutdown"
    queue << { id: id, result: nil }

  when "exit"
    queue << nil
  end
end

#process_message_from_worker(message) ⇒ Object



163
164
165
# File 'lib/steep/server/master.rb', line 163

def process_message_from_worker(message)
  queue << message
end

#register_code_to_worker(paths, worker:) ⇒ Object



181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/steep/server/master.rb', line 181

def register_code_to_worker(paths, worker:)
  paths_for(worker).merge(paths)

  worker << {
    method: "workspace/executeCommand",
    params: LSP::Interface::ExecuteCommandParams.new(
      command: "steep/registerSourceToWorker",
      arguments: paths.map do |path|
        "file://#{project.absolute_path(path)}"
      end
    )
  }
end

#registered_path?(path) ⇒ Boolean

Returns:

  • (Boolean)


177
178
179
# File 'lib/steep/server/master.rb', line 177

def registered_path?(path)
  worker_to_paths.each_value.any? {|set| set.include?(path) }
end

#startObject



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/steep/server/master.rb', line 28

def start
  source_paths = project.targets.flat_map {|target| target.source_files.keys }
  bin_size = (source_paths.size / code_workers.size) + 1
  source_paths.each_slice(bin_size).with_index do |paths, index|
    register_code_to_worker(paths, worker: code_workers[index])
  end

  Thread.new do
    interaction_worker.reader.read do |message|
      process_message_from_worker(message)
    end
  end

  Thread.new do
    signature_worker.reader.read do |message|
      process_message_from_worker(message)
    end
  end

  code_workers.each do |worker|
    Thread.new do
      worker.reader.read do |message|
        process_message_from_worker(message)
      end
    end
  end

  Thread.new do
    reader.read do |request|
      process_message_from_client(request)
    end
  end

  while job = queue.pop
    writer.write(job)
  end

  writer.io.close

  each_worker do |w|
    w.shutdown()
  end
end