Module: Mbeditor::LspDiagnosticsTranslator

Defined in:
app/services/mbeditor/lsp_diagnostics_translator.rb

Overview

Translates an LSP DocumentDiagnosticReport into the marker shape the editor already uses for RuboCop offenses, so ruby-lsp diagnostics flow through the existing pipeline (markers state -> setModelMarkers -> quick-fix lightbulbs) with no frontend changes beyond honouring source.

LSP ranges are 0-based; editor markers are 1-based.

Constant Summary collapse

SEVERITIES =

LSP DiagnosticSeverity -> the severity strings cop_severity also emits.

{ 1 => "error", 2 => "warning", 3 => "info", 4 => "info" }.freeze
UNCORRECTABLE_SUFFIX =

ruby-lsp appends this to non-correctable RuboCop messages; it's noise in a Monaco hover.

"This offense is not auto-correctable."

Class Method Summary collapse

Class Method Details

.call(result) ⇒ Object



20
21
22
23
24
25
26
27
28
29
# File 'app/services/mbeditor/lsp_diagnostics_translator.rb', line 20

def call(result)
  items = case result
          when Hash  then Array(result["items"])
          when Array then result
          else []
          end

  markers = items.filter_map { |diag| translate_one(diag) }
  { markers: markers, summary: { "offense_count" => markers.length } }
end

.clean_message(message) ⇒ Object



72
73
74
# File 'app/services/mbeditor/lsp_diagnostics_translator.rb', line 72

def clean_message(message)
  message.to_s.sub(UNCORRECTABLE_SUFFIX, "").strip
end

.extract_code(code) ⇒ Object



64
65
66
67
68
69
70
# File 'app/services/mbeditor/lsp_diagnostics_translator.rb', line 64

def extract_code(code)
  case code
  when Hash then code["value"].to_s
  when nil  then ""
  else code.to_s
  end
end

.rubocop_source?(source) ⇒ Boolean

Returns:

  • (Boolean)


60
61
62
# File 'app/services/mbeditor/lsp_diagnostics_translator.rb', line 60

def rubocop_source?(source)
  source.to_s.match?(/rubocop/i)
end

.translate_one(diag) ⇒ Object



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
# File 'app/services/mbeditor/lsp_diagnostics_translator.rb', line 31

def translate_one(diag)
  return nil unless diag.is_a?(Hash)

  range      = diag["range"] || {}
  start_line = (range.dig("start", "line") || 0) + 1
  start_col  = (range.dig("start", "character") || 0) + 1
  end_line   = (range.dig("end", "line") || range.dig("start", "line") || 0) + 1
  end_col    = (range.dig("end", "character") || range.dig("start", "character") || 0) + 1
  end_line   = start_line if end_line < start_line
  end_col    = start_col + 1 if end_line == start_line && end_col <= start_col

  cop_name = extract_code(diag["code"])
  message  = clean_message(diag["message"])

  {
    severity: SEVERITIES.fetch(diag["severity"], "info"),
    copName: cop_name,
    correctable: diag.dig("data", "correctable") == true,
    # Only RuboCop-sourced markers may claim the 'rubocop' source: the
    # quick-fix provider filters on it and sends copName to /quick_fix,
    # which runs `rubocop -A`. A Prism syntax error must not get a
    # lightbulb that can't fix anything.
    source: rubocop_source?(diag["source"]) ? "rubocop" : diag["source"].to_s.downcase,
    message: cop_name.empty? ? message : "[#{cop_name}] #{message}",
    startLine: start_line, startCol: start_col,
    endLine: end_line, endCol: end_col
  }
end