Class: FlogLsp::Server

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

Constant Summary collapse

HANDLERS =
{
  "initialize" => :handle_initialize,
  "initialized" => :handle_initialized,
  "textDocument/diagnostic" => :handle_diagnostic,
  "textDocument/didOpen" => :handle_open,
  "textDocument/didChange" => :handle_change,
  "textDocument/didClose" => :handle_close,
  "shutdown" => :handle_shutdown,
  "exit" => :handle_exit,
}

Instance Method Summary collapse

Constructor Details

#initializeServer

Returns a new instance of Server.



36
37
38
39
# File 'lib/flog_lsp/lsp.rb', line 36

def initialize
  @file_data = {}
  @options = {}
end

Instance Method Details

#file_data(uri) ⇒ Object



45
46
47
48
49
50
51
# File 'lib/flog_lsp/lsp.rb', line 45

def file_data(uri)
  @file_data[uri] ||= begin
    path = CGI.unescape(URI.parse(uri).path)
    logger.debug("Loading file #{path}")
    File.read(path)
  end
end

#get_diagnostics(contents) ⇒ Object



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/flog_lsp/lsp.rb', line 157

def get_diagnostics(contents)
  flog = Flog.new
  flog.flog_ruby(contents)
  flog.calculate_total_scores
  flog.totals.filter_map do |method_name, score|
    next if score <= score_threshold

    location = flog.method_locations[method_name]
    next unless location

    _filename, range = location.split(":")
    start_line, _end_line = range.split("-")
    [method_name, start_line, score]
  end
end

#get_prefix(lines, line, character) ⇒ Object



126
127
128
# File 'lib/flog_lsp/lsp.rb', line 126

def get_prefix(lines, line, character)
  lines[0...line].join + lines[line][0...character]
end

#get_suffix(lines, line, character) ⇒ Object



130
131
132
133
134
135
136
# File 'lib/flog_lsp/lsp.rb', line 130

def get_suffix(lines, line, character)
  if line >= lines.length
    ""
  else
    lines[line][character..] + lines[line + 1..].join
  end
end

#handle(request) ⇒ Object



68
69
70
71
72
73
74
75
76
# File 'lib/flog_lsp/lsp.rb', line 68

def handle(request)
  logger.debug("Received request: #{request[:method]}")
  if HANDLERS.key?(request[:method])
    send(HANDLERS[request[:method]], request)
  else
    logger.warn("Unknown request: #{request[:method]}")
    nil
  end
end

#handle_change(request) ⇒ Object



145
146
147
148
149
150
151
152
153
154
155
# File 'lib/flog_lsp/lsp.rb', line 145

def handle_change(request)
  uri = request[:params][:textDocument][:uri]
  request[:params][:contentChanges].each do |change|
    @file_data[uri] = if change.key?(:range)
      patch_contents(file_data(uri), change[:range], change[:text])
    else
      change[:text]
    end
  end
  nil
end

#handle_close(request) ⇒ Object



120
121
122
123
124
# File 'lib/flog_lsp/lsp.rb', line 120

def handle_close(request)
  uri = request[:params][:textDocument][:uri]
  @file_data.delete(uri)
  nil
end

#handle_diagnostic(request) ⇒ Object



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/flog_lsp/lsp.rb', line 173

def handle_diagnostic(request)
  uri = request[:params][:textDocument][:uri]
  contents = file_data(uri)
  diagnostics = get_diagnostics(contents).map do |method_name, start_line, score|
    LSP::Interface::Diagnostic.new(
      range: LSP::Interface::Range.new(
        start: LSP::Interface::Position.new(line: start_line.to_i - 1, character: 0),
        end: LSP::Interface::Position.new(line: start_line.to_i - 1, character: 0),
        # end: LSP::Interface::Position.new(line: end_line.to_i - 1, character: 0),
      ),
      severity: LSP::Constant::DiagnosticSeverity::WARNING,
      source: "flog",
      message: "#{method_name} has a flog score of #{score.round(2)}",
      data: { correctable: false },
    )
  end
  LSP::Interface::FullDocumentDiagnosticReport.new(kind: "full", items: diagnostics)
rescue StandardError => e
  logger.error("Error calculating flog: #{e}")
  LSP::Interface::FullDocumentDiagnosticReport.new(kind: "full", items: [])
end

#handle_exit(request) ⇒ Object



109
110
111
# File 'lib/flog_lsp/lsp.rb', line 109

def handle_exit(request)
  Kernel.exit(0)
end

#handle_initialize(request) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/flog_lsp/lsp.rb', line 78

def handle_initialize(request)
  logger.debug("Initializing with request: #{request}")
  @options = request[:params][:initializationOptions] || {}

  LSP::Interface::InitializeResult.new(
    capabilities: LSP::Interface::ServerCapabilities.new(
      text_document_sync: LSP::Interface::TextDocumentSyncOptions.new(
        change: LSP::Constant::TextDocumentSyncKind::INCREMENTAL,
        open_close: true,
      ),
      diagnostic_provider: LSP::Interface::DiagnosticOptions.new(
        identifier: "flog",
        inter_file_dependencies: false,
        workspace_diagnostics: false,
      ),
    ),
    server_info: {
      name: "Flog LSP",
      version: FlogLsp::VERSION,
    },
  )
end

#handle_initialized(request) ⇒ Object



101
102
103
# File 'lib/flog_lsp/lsp.rb', line 101

def handle_initialized(request)
  nil
end

#handle_open(request) ⇒ Object



113
114
115
116
117
118
# File 'lib/flog_lsp/lsp.rb', line 113

def handle_open(request)
  # Load the file
  uri = request[:params][:textDocument][:uri]
  file_data(uri)
  nil
end

#handle_shutdown(request) ⇒ Object



105
106
107
# File 'lib/flog_lsp/lsp.rb', line 105

def handle_shutdown(request)
  nil
end

#loggerObject



53
54
55
# File 'lib/flog_lsp/lsp.rb', line 53

def logger
  FlogLsp.logger
end

#patch_contents(contents, range, text) ⇒ Object



138
139
140
141
142
143
# File 'lib/flog_lsp/lsp.rb', line 138

def patch_contents(contents, range, text)
  lines = contents.lines
  prefix = get_prefix(lines, range[:start][:line], range[:start][:character])
  suffix = get_suffix(lines, range[:end][:line], range[:end][:character])
  prefix + text + suffix
end

#score_thresholdObject



41
42
43
# File 'lib/flog_lsp/lsp.rb', line 41

def score_threshold
  @options.fetch(:score_threshold, 10)
end

#startObject



57
58
59
60
61
62
63
64
65
66
# File 'lib/flog_lsp/lsp.rb', line 57

def start
  writer = LSP::Transport::Stdio::Writer.new
  reader = LSP::Transport::Stdio::Reader.new
  reader.read do |request|
    response = handle(request)
    unless response.nil?
      writer.write(id: request[:id], result: response)
    end
  end
end