123456789_123456789_123456789_123456789_123456789_

Class: TypeProf::LSP::Server

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

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 50

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)

[ GitHub ]

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

def self.start_socket(core_options)
  Socket.tcp_server_sockets("localhost", nil) 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 65

attr_reader :open_texts

#signature_enabled (rw)

[ GitHub ]

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

attr_accessor :signature_enabled

Instance Method Details

#add_workspaces(folders)

: (Array) -> void

[ GitHub ]

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

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
        conf[:analysis_unit_dirs].each do |dir|
          dir = File.expand_path(dir, path)
          core = @cores[dir] = TypeProf::Core::Service.new(@core_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 136

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 231

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 177

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 183

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 151

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 128

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 236

def exit
  @exit = true
end

#hover(path, pos)

[ GitHub ]

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

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

#path_to_uri(path)

: (String) -> String

[ GitHub ]

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

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

#publish_updated_diagnostics

[ GitHub ]

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

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 163

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 189

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 195

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 221

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 225

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 216

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 120

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 157

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 145

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 73

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