summaryrefslogtreecommitdiff
path: root/vim/pack/downloads/opt/lsp/autoload/lsp/selection.vim
blob: 1ef289ba9aaaffbee38b5c5bf11ac288bbd308d3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
66
67
68
69
70
71
72
73
74
75
76
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
104
105
106
107
vim9script

# Functions related to handling LSP range selection.

import './util.vim'

# Visually (character-wise) select the text in a range
def SelectText(bnr: number, range: dict<dict<number>>)
  var rstart = range.start
  var rend = range.end
  var start_col: number = util.GetLineByteFromPos(bnr, rstart) + 1
  var end_col: number = util.GetLineByteFromPos(bnr, rend)

  :normal! v"_y
  setcharpos("'<", [0, rstart.line + 1, start_col, 0])
  setcharpos("'>", [0, rend.line + 1, end_col, 0])
  :normal! gv
enddef

# Process the range selection reply from LSP server and start a new selection
export def SelectionStart(lspserver: dict<any>, sel: list<dict<any>>)
  if sel->empty()
    return
  endif

  var bnr: number = bufnr()

  # save the reply for expanding or shrinking the selected text.
  lspserver.selection = {bnr: bnr, selRange: sel[0], index: 0}

  SelectText(bnr, sel[0].range)
enddef

# Locate the range in the LSP reply at a specified level
def GetSelRangeAtLevel(selRange: dict<any>, level: number): dict<any>
  var r: dict<any> = selRange
  var idx: number = 0

  while idx != level
    if !r->has_key('parent')
      break
    endif
    r = r.parent
    idx += 1
  endwhile

  return r
enddef

# Returns true if the current visual selection matches a range in the
# selection reply from LSP.
def SelectionFromLSP(range: dict<any>, startpos: list<number>, endpos: list<number>): bool
  var rstart = range.start
  var rend = range.end
  return startpos[1] == rstart.line + 1
			&& endpos[1] == rend.line + 1
			&& startpos[2] == rstart.character + 1
			&& endpos[2] == rend.character
enddef

# Expand or Shrink the current selection or start a new one.
export def SelectionModify(lspserver: dict<any>, expand: bool)
  var fname: string = @%
  var bnr: number = bufnr()

  if mode() == 'v' && !lspserver.selection->empty()
					&& lspserver.selection.bnr == bnr
					&& !lspserver.selection->empty()
    # Already in characterwise visual mode and the previous LSP selection
    # reply for this buffer is available. Modify the current selection.

    var selRange: dict<any> = lspserver.selection.selRange
    var startpos: list<number> = getcharpos('v')
    var endpos: list<number> = getcharpos('.')
    var idx: number = lspserver.selection.index

    # Locate the range in the LSP reply for the current selection
    selRange = GetSelRangeAtLevel(selRange, lspserver.selection.index)

    # If the current selection is present in the LSP reply, then modify the
    # selection
    if SelectionFromLSP(selRange.range, startpos, endpos)
      if expand
	# expand the selection
        if selRange->has_key('parent')
          selRange = selRange.parent
          lspserver.selection.index = idx + 1
        endif
      else
	# shrink the selection
	if idx > 0
	  idx -= 1
          selRange = GetSelRangeAtLevel(lspserver.selection.selRange, idx)
	  lspserver.selection.index = idx
	endif
      endif

      SelectText(bnr, selRange.range)
      return
    endif
  endif

  # Start a new selection
  lspserver.selectionRange(fname)
enddef

# vim: tabstop=8 shiftwidth=2 softtabstop=2