123456789_123456789_123456789_123456789_123456789_

Class: TypeProf::LSP::Server

Relationships & Source Files
Inherits: Object
Defined in: lib/typeprof/lsp/server.rb

Constant Summary

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(core_options, reader, writer, url_schema: nil) ⇒ Server

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 59

def initialize(core_options, reader, writer, url_schema: nil)
  @core_options = core_options
  @cores = {}
  @reader = reader
  @writer = writer
  @request_id = 0
  @running_requests_from_client = {}
  @running_requests_from_server = {}
  @open_texts = {}
  @exit = false
  @signature_enabled = true
  @url_schema = url_schema || (File::ALT_SEPARATOR != "\\" ? "file://" : "file:///")
  @diagnostic_severity = :error
end

Class Method Details

.start_socket(core_options, port = 0)

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 24

def self.start_socket(core_options, port = 0)
  Socket.tcp_server_sockets("localhost", port) do |servs|
    serv = servs[0].local_address
    $stdout << JSON.generate({
      host: serv.ip_address,
      port: serv.ip_port,
      pid: $$,
    })
    $stdout.flush

    $stdout = $stderr

    Socket.accept_loop(servs) do |sock|
      sock.set_encoding("UTF-8")
      begin
        reader = Reader.new(sock)
        writer = Writer.new(sock)
        new(core_options, reader, writer).run
      ensure
        sock.close
      end
      exit
    end
  end
end

.start_stdio(core_options)

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 14

def self.start_stdio(core_options)
  $stdin.binmode
  $stdout.binmode
  reader = Reader.new($stdin)
  writer = Writer.new($stdout)
  # pipe all builtin print output to stderr to avoid conflicting with lsp
  $stdout = $stderr
  new(core_options, reader, writer).run
end

Instance Attribute Details

#open_texts (readonly)

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 74

attr_reader :open_texts, :position_encoding

#position_encoding (readonly)

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 74

attr_reader :open_texts, :position_encoding

#signature_enabled (rw)

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 75

attr_accessor :signature_enabled

Instance Method Details

#add_workspaces(folders)

: (Array) -> void

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 107

def add_workspaces(folders)
  folders.each do |path|
    conf_path = [".json", ".jsonc"].map do |ext|
      File.join(path, "typeprof.conf" + ext)
    end.find do |path|
      File.readable?(path)
    end
    unless conf_path
      puts "typeprof.conf.json is not found in #{ path }"
      next
    end
    conf = TypeProf::LSP.load_json_with_comments(conf_path, symbolize_names: true)
    if conf
      if conf[:rbs_dir]
        rbs_dir = File.expand_path(conf[:rbs_dir])
      else
        rbs_dir = File.expand_path(File.expand_path("sig", path))
      end
      @rbs_dir = rbs_dir
      if conf[:typeprof_version] == "experimental"
        if conf[:diagnostic_severity]
          severity = conf[:diagnostic_severity].to_sym
          case severity
          when :error, :warning, :info, :hint
            @diagnostic_severity = severity
          else
            puts "unknown severity: #{ severity }"
          end
        end
        @core_options[:exclude_patterns] = conf[:exclude] if conf[:exclude]
        service_options = @core_options.merge(position_encoding: @position_encoding)
        conf[:analysis_unit_dirs].each do |dir|
          dir = File.expand_path(dir, path)
          core = @cores[dir] = TypeProf::Core::Service.new(service_options)
          core.add_workspace(dir, @rbs_dir)
        end
      else
        puts "Unknown typeprof_version: #{ conf[:typeprof_version] }"
      end
    end
  end
end

#aggregate_each_core(path)

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 167

def aggregate_each_core(path)
  ret = []
  each_core(path) do |core|
    r = yield(core)
    ret.concat(r) if r
  end
  ret
end

#cancel_request(id)

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 262

def cancel_request(id)
  req = @running_requests_from_client[id]
  req.cancel if req.respond_to?(:cancel)
end

#code_lens(path, &blk)

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 208

def code_lens(path, &blk)
  each_core(path) do |core|
    core.code_lens(path, &blk)
  end
end

#completion(path, trigger, pos, &blk)

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 214

def completion(path, trigger, pos, &blk)
  each_core(path) do |core|
    core.completion(path, trigger, pos, &blk)
  end
end

#definitions(path, pos)

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 182

def definitions(path, pos)
  aggregate_each_core(path) do |core|
    core.definitions(path, pos)
  end
end

#each_core(path)

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 159

def each_core(path)
  @cores.each do |folder, core|
    if path.start_with?(folder) || @rbs_dir && path.start_with?(@rbs_dir)
      yield core
    end
  end
end

#exit

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 267

def exit
  @exit = true
end

#hover(path, pos)

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 200

def hover(path, pos)
  ret = []
  each_core(path) do |core|
    ret << core.hover(path, pos)
  end
  ret.compact.first # TODO
end

#lsp_position_encoding

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 83

def lsp_position_encoding
  LSP_POSITION_ENCODINGS.fetch(@position_encoding)
end

#negotiate_position_encoding(client_encodings)

Pick the first mutually-supported encoding from the client's preference-ordered list and store it. Falls back to UTF-16LE (mandatory per ::TypeProf::LSP 3.17 spec).

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 79

def negotiate_position_encoding(client_encodings)
  @position_encoding = pick_position_encoding(client_encodings)
end

#path_to_uri(path)

: (String) -> String

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 98

def path_to_uri(path)
  @url_schema + File.expand_path(path).split("/").map {|s| CGI.escapeURIComponent(s) }.join("/")
end

#pick_position_encoding(client_encodings) (private)

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 87

def pick_position_encoding(client_encodings)
  return Encoding::UTF_16LE unless client_encodings.is_a?(Array)
  client_encodings.each do |enc|
    encoding = LSP_POSITION_ENCODINGS.key(enc)
    return encoding if encoding
  end
  Encoding::UTF_16LE
end

#publish_updated_diagnostics

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 271

def publish_updated_diagnostics
  @cores.each do |_, core|
    diags = []
    core.process_diagnostic_paths do |path|
      uri = path_to_uri(path)
      next false unless @open_texts[uri]
      core.diagnostics(path) do |diag|
        diags << diag.to_lsp(severity: @diagnostic_severity)
      end
      send_notification(
        "textDocument/publishDiagnostics",
        uri: uri,
        diagnostics: diags
      )
      true
    end
  end
end

#references(path, pos)

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 194

def references(path, pos)
  aggregate_each_core(path) do |core|
    core.references(path, pos)
  end
end

#rename(path, pos)

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 220

def rename(path, pos)
  aggregate_each_core(path) do |core|
    core.rename(path, pos)
  end
end

#run

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 226

def run
  @reader.read do |json|
    if json[:method]
      # request or notification
      msg_class = Message.find(json[:method])
      if msg_class
        msg = msg_class.new(self, json)
        @running_requests_from_client[json[:id]] = msg if json[:id]
        msg.run
      else

      end
    else
      # response
      callback = @running_requests_from_server.delete(json[:id])
      callback&.call(json[:params], json[:error])
    end
    break if @exit
  end
end

#send_notification(method, **params)

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 252

def send_notification(method, **params)
  @writer.write(method: method, params: params)
end

#send_request(method, **params, &blk)

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 256

def send_request(method, **params, &blk)
  id = @request_id += 1
  @running_requests_from_server[id] = blk
  @writer.write(id: id, method: method, params: params)
end

#send_response(**msg)

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 247

def send_response(**msg)
  @running_requests_from_client.delete(msg[:id])
  @writer.write(**msg)
end

#target_path?(path) ⇒ Boolean

: (String) -> bool

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 151

def target_path?(path)
  return true if @rbs_dir && path.start_with?(@rbs_dir)
  @cores.each do |folder, _|
    return true if path.start_with?(folder)
  end
  return false
end

#type_definitions(path, pos)

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 188

def type_definitions(path, pos)
  aggregate_each_core(path) do |core|
    core.type_definitions(path, pos)
  end
end

#update_file(path, text)

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 176

def update_file(path, text)
  each_core(path) do |core|
    core.update_file(path, text)
  end
end

#uri_to_path(uri)

[ GitHub ]

  
# File 'lib/typeprof/lsp/server.rb', line 102

def uri_to_path(uri)
  uri.delete_prefix(@url_schema).split("/").map {|s| CGI.unescapeURIComponent(s) }.join("/")
end