Class: LspRouter

Inherits:
Object
  • Object
show all
Defined in:
lib/lsp_router.rb,
lib/lsp_router/cli.rb,
lib/lsp_router/config.rb,
lib/lsp_router/version.rb,
lib/lsp_router/client_side.rb,
lib/lsp_router/server_side.rb,
lib/lsp_router/capability_request.rb

Overview

LspRouter

Defined Under Namespace

Classes: CLI, ClientSide, Config, ServerSide

Constant Summary collapse

VERSION =
'0.2.1'
CAPABILITY_REQUEST =
{
  textDocumentSync: [
    'textDocument/willSaveWaitUntil',
  ],
  notebookDocumentSync: [
    # Notification only
  ],
  completionProvider: [
    'textDocument/completion',
    'completionItem/resolve',
  ],
  hoverProvider: [
    'textDocument/hover',
  ],
  signatureHelpProvider: [
    'textDocument/signatureHelp',
  ],
  declarationProvider: [
    'textDocument/declaration',
  ],
  definitionProvider: [
    'textDocument/definition',
  ],
  typeDefinitionProvider: [
    'textDocument/typeDefinition',
  ],
  implementationProvider: [
    'textDocument/implementation',
  ],
  referencesProvider: [
    'textDocument/references',
  ],
  documentHighlightProvider: [
    'textDocument/documentHighlight',
  ],
  documentSymbolProvider: [
    'textDocument/documentSymbol',
  ],
  codeActionProvider: [
    'textDocument/codeAction',
    'codeAction/resolve',
  ],
  codeLensProvider: [
    'textDocument/codeLens',
    'codeLens/resolve',
    'workspace/codeLens/refresh',
  ],
  documentLinkProvider: [
    'textDocument/documentLink',
    'documentLink/resolve',
  ],
  colorProvider: [
    'textDocument/documentColor',
    'textDocument/colorPresentation',
  ],
  documentFormattingProvider: [
    'textDocument/formatting',
  ],
  documentRangeFormattingProvider: [
    'textDocument/rangeFormatting',
  ],
  documentOnTypeFormattingProvider: [
    'textDocument/onTypeFormatting',
  ],
  renameProvider: [
    'textDocument/rename',
    'textDocument/prepareRename',
  ],
  foldingRangeProvider: [
    'textDocument/foldingRange',
  ],
  executeCommandProvider: [
    'textDocument/selectionRange',
    'workspace/executeCommand',
  ],
  selectionRangeProvider: [
  ],
  linkedEditingRangeProvider: [
    'textDocument/linkedEditingRange',
  ],
  callHierarchyProvider: [
    'textDocument/prepareCallHierarchy',
    'callHierarchy/incomingCalls',
    'callHierarchy/outgoingCalls',

  ],
  semanticTokensProvider: [
    'textDocument/semanticTokens',
    'textDocument/semanticTokens/full',
    'textDocument/semanticTokens/full/delta',
    'textDocument/semanticTokens/range',
    'workspace/semanticTokens/refresh',
  ],
  monikerProvider: [
    'textDocument/moniker',
],
  typeHierarchyProvider: [
    'textDocument/prepareTypeHierarchy',
    'typeHierarchy/supertypes',
    'typeHierarchy/subtypes',
  ],
  inlineValueProvider: [
    'textDocument/inlineValue',
    'workspace/inlineValue/refresh',
  ],
  inlayHintProvider: [
    'textDocument/inlayHint',
    'inlayHint/resolve',
    'workspace/inlayHint/refresh',
  ],
  diagnosticProvider: [
    'textDocument/diagnostic',
    'workspace/diagnostic',
    'workspace/diagnostic/refresh',
  ],
  workspaceSymbolProvider: [
    'workspace/symbol',
    'workspaceSymbol/resolve',
  ],
  workspace: {
    workspaceFolders: [
      'workspace/workspaceFolders',
    ],
    fileOperations: [
      'workspace/willCreateFiles',
      'workspace/willRenameFiles',
      'workspace/willDeleteFiles',
    ],
  },
}

Instance Method Summary collapse

Constructor Details

#initialize(config, port: nil, logfile: nil) ⇒ LspRouter

Returns a new instance of LspRouter.

Parameters:

  • config (LspRouter::Config)

    configuration filename

  • port (Integer, String) (defaults to: nil)
  • logfile (String) (defaults to: nil)


16
17
18
19
20
21
22
23
24
25
# File 'lib/lsp_router.rb', line 16

def initialize(config, port: nil, logfile: nil)
  @config = config
  @port = port
  logfile ||= config.logfile || $stderr
  @logger = Logger.new(logfile, level: Logger.const_get(config.loglevel.upcase))
  @logger.info "LspRouter: start"
  @config.servers.each do |server|
    @logger.info "server: #{server.info}"
  end
end

Instance Method Details

#client_loopObject



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/lsp_router.rb', line 77

def client_loop
  Thread.new do
    Thread.abort_on_exception = true
    while true
      data = @client.read
      break unless data
      if data['method'] == 'shutdown' || data['method'] == 'exit'
        @servers.each{_1.write(data)}
        break if data['method'] == 'exit'
        next
      end
      if data['id'] # request or response
        if data['method']  # request
          server = @req_server[data['method']] || @primary
        else  # response
          server_name, id = JSON.parse(data['id'])
          data['id'] = id
          server = @server_map[server_name]
        end
        server.write(data)
      else # notification
        @servers.each{_1.write(data)}
      end
    end
    @logger.info "client: end"
  end
end

#initial_packetObject



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/lsp_router.rb', line 57

def initial_packet
  req = @client.read
  req['params']['capabilities']['general'] ||= {}
  req['params']['capabilities']['general']['positionEncodings'] = [@config.encoding]
  res = nil
  @servers.each do |server|
    data = server.init(req)
    if res
      res['result']['capabilities'].update(data.dig('result', 'capabilities')){|_, v, _| v}
    else
      res = data
    end
    server.requests.each do |req|
      @req_server[req] ||= server
    end
  end
  @logger.debug "req_server: #{@req_server.transform_values(&:name).inspect}"
  @client.write(res)
end

#loopObject



45
46
47
48
49
50
51
52
53
54
55
# File 'lib/lsp_router.rb', line 45

def loop
  @servers = @config.servers.map{LspRouter::ServerSide.new(_1, logger: @logger)}
  @primary = @servers.first
  @server_map = @servers.map.to_h{[_1.name.to_s, _1]}
  @req_server = {}

  initial_packet
  threads = server_loop
  threads.push client_loop
  threads.each(&:join)
end

#runObject



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/lsp_router.rb', line 27

def run
  if @port
    @logger.info "mode: port:#{@port}"
    Socket.tcp_server_loop(@port) do |sock, _|
      @client = LspRouter::ClientSide.new(sock, sock, logger: @logger)
      loop
    end
  else
    @logger.info "mode: stdio"
    @client = LspRouter::ClientSide.new($stdin, $stdout, logger: @logger)
    loop
  end
rescue => e
  @logger.error e
ensure
  @logger.info "LspRouter: stop"
end

#server_loopObject



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/lsp_router.rb', line 105

def server_loop
  @servers.map do |server|
    Thread.new do
      Thread.abort_on_exception = true
      while true
        data = server.read
        break unless data
        if data['id'] && data['method']  # request
          data['id'] = [server.name, data['id']].to_json
        end
        @client.write(data)
      end
      @logger.info "server: [#{server.name}] end"
    end
  end
end