Module: MilkTea::LSP::Server::ServerSelectionRange

Included in:
MilkTea::LSP::Server
Defined in:
lib/milk_tea/lsp/server/selection_range.rb

Instance Method Summary collapse

Instance Method Details

#build_selection_range(lines, lsp_line, lsp_char) ⇒ Object



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
# File 'lib/milk_tea/lsp/server/selection_range.rb', line 23

def build_selection_range(lines, lsp_line, lsp_char)
  line_str = lines[lsp_line] || ""
  return nil if line_str.empty?

  token_range = token_bounds_at(line_str, lsp_char)
  line_range = { start: { line: lsp_line, character: 0 },
                 end: { line: lsp_line, character: line_str.length } }

  current = {
    range: token_range || line_range,
  }

  current[:parent] = { range: line_range }

  statement_range = find_statement_range(lines, lsp_line)
  if statement_range
    current[:parent][:parent] = { range: statement_range }

    block_range = find_enclosing_block_range(lines, lsp_line)
    if block_range
      current[:parent][:parent][:parent] = { range: block_range }
    end
  end

  current
end

#find_enclosing_block_range(lines, lsp_line) ⇒ Object



94
95
96
97
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
# File 'lib/milk_tea/lsp/server/selection_range.rb', line 94

def find_enclosing_block_range(lines, lsp_line)
  indent = (lines[lsp_line] || "").length - (lines[lsp_line] || "").lstrip.length
  return nil if indent <= 0

  start_line = lsp_line
  start_line -= 1 while start_line > 0 &&
    ((lines[start_line - 1].strip.empty?) ||
     (lines[start_line - 1].length - lines[start_line - 1].lstrip.length) >= indent)

  while start_line > 0 &&
        (lines[start_line - 1].strip.empty? ||
         (lines[start_line - 1].length - lines[start_line - 1].lstrip.length) >= indent)
    start_line -= 1
  end

  end_line = lsp_line
  while end_line < lines.length - 1 &&
        (lines[end_line + 1].strip.empty? ||
         (lines[end_line + 1].length - lines[end_line + 1].lstrip.length) >= indent)
    end_line += 1
  end

  return nil if start_line == end_line

  {
    start: { line: start_line, character: 0 },
    end: { line: end_line, character: (lines[end_line] || "").length },
  }
end

#find_statement_range(lines, lsp_line) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/milk_tea/lsp/server/selection_range.rb', line 69

def find_statement_range(lines, lsp_line)
  indent = (lines[lsp_line] || "").length - (lines[lsp_line] || "").lstrip.length

  start_line = lsp_line
  start_line -= 1 while start_line > 0 &&
    lines[start_line - 1].strip.empty? &&
    (lines[start_line - 1].length - lines[start_line - 1].lstrip.length) >= indent

  start_line -= 1 while start_line > 0 &&
    !lines[start_line - 1].strip.empty? &&
    (lines[start_line - 1].length - lines[start_line - 1].lstrip.length) >= indent

  end_line = lsp_line
  end_line += 1 while end_line < lines.length - 1 &&
    (lines[end_line + 1].strip.empty? ||
     (lines[end_line + 1].length - lines[end_line + 1].lstrip.length) > indent)

  return nil if start_line == end_line && lines[start_line].strip.empty?

  {
    start: { line: start_line, character: 0 },
    end: { line: end_line, character: (lines[end_line] || "").length },
  }
end

#handle_selection_range(params) ⇒ Object



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# File 'lib/milk_tea/lsp/server/selection_range.rb', line 7

def handle_selection_range(params)
  positions = params["positions"] || []
  uri = params["textDocument"]["uri"]

  content = @workspace.get_content(uri)
  return [] unless content

  lines = content.split("\n", -1)

  positions.map do |pos|
    line = pos["line"]
    char = pos["character"]
    build_selection_range(lines, line, char)
  end
end

#token_bounds_at(line_str, lsp_char) ⇒ Object



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/milk_tea/lsp/server/selection_range.rb', line 50

def token_bounds_at(line_str, lsp_char)
  col = [lsp_char, line_str.length - 1].min
  col = [col, 0].max

  return nil if line_str[col] == " " || line_str[col].nil?

  left = col
  left -= 1 while left > 0 && line_str[left - 1] =~ /[A-Za-z0-9_]/
  right = col
  right += 1 while right < line_str.length - 1 && line_str[right + 1] =~ /[A-Za-z0-9_]/

  return nil if left == right && line_str[left] !~ /[A-Za-z0-9_]/

  {
    start: { line: 0, character: left },
    end: { line: 0, character: right + 1 },
  }
end