Class: Standard::LSP::Server

Inherits:
Object
  • Object
show all
Defined in:
lib/standard/lsp/server.rb

Constant Summary collapse

Proto =
LanguageServer::Protocol
SEV =
Proto::Constant::DiagnosticSeverity

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(standardizer) ⇒ Server

Returns a new instance of Server.



16
17
18
19
20
21
22
23
24
25
26
27
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
# File 'lib/standard/lsp/server.rb', line 16

def initialize(standardizer)
  self.standardizer = standardizer
  self.writer = Proto::Transport::Io::Writer.new($stdout)
  self.reader = Proto::Transport::Io::Reader.new($stdin)
  self.logger = $stderr
  self.text_cache = {}

  self.subscribers = {
    "initialize" => ->(request) {
      init_result = Proto::Interface::InitializeResult.new(
        capabilities: Proto::Interface::ServerCapabilities.new(
          document_formatting_provider: true,
          diagnostic_provider: true,
          text_document_sync: Proto::Constant::TextDocumentSyncKind::FULL
        )
      )
      writer.write(id: request[:id], result: init_result)
    },

    "initialized" => ->(request) { logger.puts "standard v#{Standard::VERSION} initialized, pid #{Process.pid}" },

    "shutdown" => ->(request) {
      logger.puts "asked to shutdown, exiting..."
      exit
    },

    "textDocument/didChange" => ->(request) {
      params = request[:params]
      result = diagnostic(params[:textDocument][:uri], params[:contentChanges][0][:text])
      writer.write(result)
    },

    "textDocument/didOpen" => ->(request) {
      td = request[:params][:textDocument]
      result = diagnostic(td[:uri], td[:text])
      writer.write(result)
    },

    "textDocument/didClose" => ->(request) {
      text_cache.delete(request.dig(:params, :textDocument, :uri))
    },

    "textDocument/formatting" => ->(request) {
      uri = request[:params][:textDocument][:uri]
      writer.write({id: request[:id], result: format_file(uri)})
    },

    "textDocument/didSave" => ->(request) {}
  }
end

Instance Attribute Details

#loggerObject

Returns the value of attribute logger.



14
15
16
# File 'lib/standard/lsp/server.rb', line 14

def logger
  @logger
end

#readerObject

Returns the value of attribute reader.



14
15
16
# File 'lib/standard/lsp/server.rb', line 14

def reader
  @reader
end

#standardizerObject

Returns the value of attribute standardizer.



14
15
16
# File 'lib/standard/lsp/server.rb', line 14

def standardizer
  @standardizer
end

#subscribersObject

Returns the value of attribute subscribers.



14
15
16
# File 'lib/standard/lsp/server.rb', line 14

def subscribers
  @subscribers
end

#text_cacheObject

Returns the value of attribute text_cache.



14
15
16
# File 'lib/standard/lsp/server.rb', line 14

def text_cache
  @text_cache
end

#writerObject

Returns the value of attribute writer.



14
15
16
# File 'lib/standard/lsp/server.rb', line 14

def writer
  @writer
end

Class Method Details

.start(standardizer) ⇒ Object



10
11
12
# File 'lib/standard/lsp/server.rb', line 10

def self.start(standardizer)
  new(standardizer).start
end

Instance Method Details

#diagnostic(file_uri, text) ⇒ Object



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
# File 'lib/standard/lsp/server.rb', line 98

def diagnostic(file_uri, text)
  text_cache[file_uri] = text
  offenses = standardizer.offenses(text)

  lsp_diagnostics = offenses.map { |o|
    code = o[:cop_name]

    msg = o[:message].delete_prefix(code)
    loc = o[:location]

    severity = case o[:severity]
    when "error", "fatal"
      SEV::ERROR
    when "warning"
      SEV::WARNING
    when "convention"
      SEV::INFORMATION
    when "refactor", "info"
      SEV::HINT
    else # the above cases fully cover what RuboCop sends at this time
      logger.puts "unknown severity: #{severity.inspect}"
      SEV::HINT
    end

    {
      code: code,
      message: msg,
      range: {
        start: {character: loc[:start_column] - 1, line: loc[:start_line] - 1},
        end: {character: loc[:last_column] - 1, line: loc[:last_line] - 1}
      },
      severity: severity,
      source: "standard"
    }
  }

  {
    method: "textDocument/publishDiagnostics",
    params: {
      diagnostics: lsp_diagnostics,
      uri: file_uri
    }
  }
end

#format_file(file_uri) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/standard/lsp/server.rb', line 81

def format_file(file_uri)
  text = text_cache[file_uri]
  new_text = standardizer.format(text)

  if new_text == text
    []
  else
    [{
      newText: new_text,
      range: {
        start: {line: 0, character: 0},
        end: {line: text.count("\n") + 1, character: 0}
      }
    }]
  end
end

#startObject



67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/standard/lsp/server.rb', line 67

def start
  reader.read do |request|
    method = request[:method]
    if (subscriber = subscribers[method])
      subscriber.call(request)
    else
      logger.puts "unknown method: #{method}"
    end
  rescue => e
    logger.puts "error #{e.class} #{e.message[0..100]}"
    logger.puts e.backtrace.inspect
  end
end