]> Sergey Matveev's repositories - vim-lsp.git/commitdiff
Add suppport for expanding and shrinking the visual selection
authorYegappan Lakshmanan <yegappan@yahoo.com>
Sat, 5 Feb 2022 02:32:16 +0000 (18:32 -0800)
committerYegappan Lakshmanan <yegappan@yahoo.com>
Sat, 5 Feb 2022 02:32:16 +0000 (18:32 -0800)
README.md
autoload/codeaction.vim
autoload/diag.vim
autoload/handlers.vim
autoload/lsp.vim
autoload/lspserver.vim
autoload/selection.vim [new file with mode: 0644]
doc/lsp.txt
plugin/lsp.vim
test/unit_tests.vim

index 08631d583bbee82620cfe04a25a0b84874d1853f..0f1614ade02293ed698756d56644cde38809d63f 100644 (file)
--- a/README.md
+++ b/README.md
@@ -87,6 +87,10 @@ Command|Description
 :LspGotoDeclaration|Go to the declaration of the keyword under cursor
 :LspGotoTypeDef|Go to the type definition of the keyword under cursor
 :LspGotoImpl|Go to the implementation of the keyword under cursor
+:LspPeekDefinition|Open the definition of the symbol under cursor in the preview window.
+:LspPeekDeclaration|Open the declaration of the symbol under cursor in the preview window.
+:LspPeekTypeDef|Open the type definition of the symbol under cursor in the preview window.
+:LspPeekImpl|Open the implementation of the symbol under cursor in the preview window.
 :LspShowSignature|Display the signature of the keyword under cursor
 :LspDiagShow|Display the diagnostics messages from the LSP server for the current buffer in a new location list.
 :LspDiagFirst|Display the first diagnostic message for the current buffer
@@ -94,6 +98,7 @@ Command|Description
 :LspDiagPrev|Display the previous diagnostic message before the current line
 :LspDiagCurrent|Display the diagnostic message for the current line
 :LspShowReferences|Display the list of references to the keyword under cursor in a new location list.
+:LspPeekReferences|Display the list of references to the keyword under cursor in a location list associated with the preview window.
 :LspHighlight|Highlight all the matches for the keyword under cursor
 :LspHighlightClear|Clear all the matches highlighted by :LspHighlight
 :LspOutline|Show the list of symbols defined in the current file in a separate window.
@@ -104,7 +109,8 @@ Command|Description
 :LspRename|Rename the current symbol
 :LspCodeAction|Apply the code action supplied by the LSP server to the diagnostic in the current line.
 :LspSymbolSearch|Perform a workspace wide search for a symbol
-:LspSelectionRange|Visually select the current symbol range
+:LspSelectionExpand|Expand the current symbol range visual selection
+:LspSelectionShrink|Shrink the current symbol range visual selection
 :LspFold|Fold the current file
 :LspWorkspaceAddFolder `{folder}`| Add a folder to the workspace
 :LspWorkspaceRemoveFolder `{folder}`|Remove a folder from the workspace
index 51b2355377e641850100d4550ed5190658b65f58..e7e838f2c2b8ed83cf0889aff1dfcd0d36840aa5 100644 (file)
@@ -1,5 +1,7 @@
 vim9script
 
+# Functions related to handling LSP code actions to fix diagnostics.
+
 var util = {}
 var textedit = {}
 
index 9c984e0e94d0369c41351973fc6f9a357522ebad..e5a5b003d0abec12e65cbefa27a4aab6b372ca32 100644 (file)
@@ -1,5 +1,7 @@
 vim9script
 
+# Functions related to handling LSP diagnostics.
+
 var opt = {}
 var util = {}
 
index 85a067b1dcf6365b5ae19095cb8d706373496f50..cbb451297478e757c8be60c713f0b19d368f1c10 100644 (file)
@@ -12,6 +12,7 @@ var textedit = {}
 var symbol = {}
 var codeaction = {}
 var callhier = {}
+var selection = {}
 
 if has('patch-8.2.4019')
   import './lspoptions.vim' as opt_import
@@ -22,6 +23,7 @@ if has('patch-8.2.4019')
   import './symbol.vim' as symbol_import
   import './codeaction.vim' as codeaction_import
   import './callhierarchy.vim' as callhierarchy_import
+  import './selection.vim' as selection_import
 
   opt.lspOptions = opt_import.lspOptions
   util.WarnMsg = util_import.WarnMsg
@@ -38,6 +40,7 @@ if has('patch-8.2.4019')
   codeaction.ApplyCodeAction = codeaction_import.ApplyCodeAction
   callhier.IncomingCalls = callhierarchy_import.IncomingCalls
   callhier.OutgoingCalls = callhierarchy_import.OutgoingCalls
+  selection.SelectionStart = selection_import.SelectionStart
 else
   import lspOptions from './lspoptions.vim'
   import {WarnMsg,
@@ -51,6 +54,7 @@ else
   import {ShowReferences, GotoSymbol} from './symbol.vim'
   import ApplyCodeAction from './codeaction.vim'
   import {IncomingCalls, OutgoingCalls} from './callhierarchy.vim'
+  import {SelectionStart} from './selection.vim'
 
   opt.lspOptions = lspOptions
   util.WarnMsg = WarnMsg
@@ -67,6 +71,7 @@ else
   codeaction.ApplyCodeAction = ApplyCodeAction
   callhier.IncomingCalls = IncomingCalls
   callhier.OutgoingCalls = OutgoingCalls
+  selection.SelectionStart = SelectionStart
 endif
 
 # process the 'initialize' method reply from the LSP server
@@ -567,19 +572,7 @@ enddef
 # Reply: 'textDocument/selectionRange'
 # Result: SelectionRange[] | null
 def s:processSelectionRangeReply(lspserver: dict<any>, req: dict<any>, reply: dict<any>)
-  if reply.result->empty()
-    return
-  endif
-
-  var r: dict<dict<number>> = reply.result[0].range
-  var bnr: number = bufnr()
-  var start_col: number = util.GetLineByteFromPos(bnr, r.start) + 1
-  var end_col: number = util.GetLineByteFromPos(bnr, r.end)
-
-  :normal! v"_y
-  setcharpos("'<", [0, r.start.line + 1, start_col, 0])
-  setcharpos("'>", [0, r.end.line + 1, end_col, 0])
-  :normal! gv
+  selection.SelectionStart(lspserver, reply.result)
 enddef
 
 # Reply: 'textDocument/foldingRange'
index 822c465da6f487e9aa411164018db5edd35aab0c..e7411ac59fca56be0455a8013923b7b4afa8b9b7 100644 (file)
@@ -879,16 +879,24 @@ export def RemoveWorkspaceFolder(dirArg: string)
   lspserver.removeWorkspaceFolder(dirName)
 enddef
 
-# visually select a range of positions around the current cursor.
-export def SelectionRange()
+# expand the previous selection or start a new selection
+export def SelectionExpand()
   var lspserver: dict<any> = s:curbufGetServerChecked()
   if lspserver->empty()
     return
   endif
 
-  var fname: string = @%
-  # TODO: Also support passing a range
-  lspserver.selectionRange(fname)
+  lspserver.selectionExpand()
+enddef
+
+# shrink the previous selection or start a new selection
+export def SelectionShrink()
+  var lspserver: dict<any> = s:curbufGetServerChecked()
+  if lspserver->empty()
+    return
+  endif
+
+  lspserver.selectionShrink()
 enddef
 
 # fold the entire document
index c0ee844b179a1e8be9b8ba0285cfdb606a8131c4..e59b40a334c061e0c0f34c5489f1f769e819dbe7 100644 (file)
@@ -7,11 +7,14 @@ vim9script
 var handlers = {}
 var diag = {}
 var util = {}
+var selection = {}
 
 if has('patch-8.2.4019')
   import './handlers.vim' as handlers_import
   import './util.vim' as util_import
   import './diag.vim' as diag_import
+  import './selection.vim' as selection_import
+
   handlers.ProcessReply = handlers_import.ProcessReply
   handlers.ProcessNotif = handlers_import.ProcessNotif
   handlers.ProcessRequest = handlers_import.ProcessRequest
@@ -23,6 +26,7 @@ if has('patch-8.2.4019')
   util.LspFileToUri = util_import.LspFileToUri
   util.PushCursorToTagStack = util_import.PushCursorToTagStack
   diag.GetDiagByLine = diag_import.GetDiagByLine
+  selection.SelectionModify = selection_import.SelectionModify
 else
   import {ProcessReply,
        ProcessNotif,
@@ -35,6 +39,8 @@ else
        LspBufnrToUri,
        LspFileToUri,
        PushCursorToTagStack} from './util.vim'
+  import {SelectionModify} from './selection.vim'
+
   handlers.ProcessReply = ProcessReply
   handlers.ProcessNotif = ProcessNotif
   handlers.ProcessRequest = ProcessRequest
@@ -46,6 +52,7 @@ else
   util.LspFileToUri = LspFileToUri
   util.PushCursorToTagStack = PushCursorToTagStack
   diag.GetDiagByLine = GetDiagByLine
+  selection.SelectionModify = SelectionModify
 endif
 
 # LSP server standard output handler
@@ -63,7 +70,7 @@ enddef
 # LSP server exit callback
 def s:exit_cb(lspserver: dict<any>, job: job, status: number): void
   util.WarnMsg("LSP server exited with status " .. status)
-  lspserver.job = v:none
+  lspserver.job = v:null
   lspserver.running = false
   lspserver.ready = false
   lspserver.requests = {}
@@ -201,7 +208,7 @@ def s:stopServer(lspserver: dict<any>): number
   lspserver.exitServer()
 
   lspserver.job->job_stop()
-  lspserver.job = v:none
+  lspserver.job = v:null
   lspserver.running = false
   lspserver.ready = false
   lspserver.requests = {}
@@ -862,11 +869,40 @@ def s:selectionRange(lspserver: dict<any>, fname: string)
     return
   endif
 
+  # clear the previous selection reply
+  lspserver.selection = {}
+
   var req = lspserver.createRequest('textDocument/selectionRange')
   # interface SelectionRangeParams
   # interface TextDocumentIdentifier
   req.params->extend({textDocument: {uri: util.LspFileToUri(fname)}, positions: [s:getLspPosition()]})
   lspserver.sendMessage(req)
+
+  s:waitForReponse(lspserver, req)
+enddef
+
+# Expand the previous selection or start a new one
+def s:selectionExpand(lspserver: dict<any>)
+  # Check whether LSP server supports selection ranges
+  if !lspserver.caps->has_key('selectionRangeProvider')
+                       || !lspserver.caps.selectionRangeProvider
+    util.ErrMsg("Error: LSP server does not support selection ranges")
+    return
+  endif
+
+  selection.SelectionModify(lspserver, true)
+enddef
+
+# Shrink the previous selection or start a new one
+def s:selectionShrink(lspserver: dict<any>)
+  # Check whether LSP server supports selection ranges
+  if !lspserver.caps->has_key('selectionRangeProvider')
+                       || !lspserver.caps.selectionRangeProvider
+    util.ErrMsg("Error: LSP server does not support selection ranges")
+    return
+  endif
+
+  selection.SelectionModify(lspserver, false)
 enddef
 
 # fold the entire document
@@ -922,7 +958,8 @@ export def NewLspServer(path: string, args: list<string>): dict<any>
     workspaceSymbolPopup: 0,
     workspaceSymbolQuery: '',
     peekSymbol: false,
-    callHierarchyType: ''
+    callHierarchyType: '',
+    selection: {}
   }
   # Add the LSP server functions
   lspserver->extend({
@@ -968,6 +1005,8 @@ export def NewLspServer(path: string, args: list<string>): dict<any>
     addWorkspaceFolder: function('s:addWorkspaceFolder', [lspserver]),
     removeWorkspaceFolder: function('s:removeWorkspaceFolder', [lspserver]),
     selectionRange: function('s:selectionRange', [lspserver]),
+    selectionExpand: function('s:selectionExpand', [lspserver]),
+    selectionShrink: function('s:selectionShrink', [lspserver]),
     foldRange: function('s:foldRange', [lspserver]),
     executeCommand: function('s:executeCommand', [lspserver]),
     showCapabilities: function('s:showCapabilities', [lspserver])
diff --git a/autoload/selection.vim b/autoload/selection.vim
new file mode 100644 (file)
index 0000000..1d4f090
--- /dev/null
@@ -0,0 +1,116 @@
+vim9script
+
+# Functions related to handling LSP range selection.
+
+var util = {}
+if has('patch-8.2.4019')
+  import './util.vim' as util_import
+
+  util.GetLineByteFromPos = util_import.GetLineByteFromPos
+else
+  import {GetLineByteFromPos} from './util.vim'
+
+  util.GetLineByteFromPos = GetLineByteFromPos
+endif
+
+# Visually (character-wise) select the text in a range
+def s:selectText(bnr: number, range: dict<dict<number>>)
+  var start_col: number = util.GetLineByteFromPos(bnr, range.start) + 1
+  var end_col: number = util.GetLineByteFromPos(bnr, range.end)
+
+  :normal! v"_y
+  setcharpos("'<", [0, range.start.line + 1, start_col, 0])
+  setcharpos("'>", [0, range.end.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}
+
+  s:selectText(bnr, sel[0].range)
+enddef
+
+# Locate the range in the LSP reply at a specified level
+def s: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 s:selectionFromLSP(range: dict<any>, startpos: list<number>, endpos: list<number>): bool
+  return startpos[1] == range.start.line + 1
+                       && endpos[1] == range.end.line + 1
+                       && startpos[2] == range.start.character + 1
+                       && endpos[2] == range.end.character
+enddef
+
+g:Logs = []
+
+# 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()
+
+  add(g:Logs, 'SelectionModify: expand = ' .. expand->string())
+
+  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 = s:getSelRangeAtLevel(selRange, lspserver.selection.index)
+
+    # If the current selection is present in the LSP reply, then modify the
+    # selection
+    if s: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 = s:getSelRangeAtLevel(lspserver.selection.selRange, idx)
+         lspserver.selection.index = idx
+       endif
+      endif
+
+      s:selectText(bnr, selRange.range)
+      return
+    endif
+  endif
+
+  # Start a new selection
+  lspserver.selectionRange(fname)
+enddef
+
+# vim: shiftwidth=2 softtabstop=2
index 63e6e9950b18c58467ee14aa8bf6fea9173ae6f4..411ee85a8f7a0be18a4d2ba08cb5aa75d4b93eff 100644 (file)
@@ -2,7 +2,7 @@
 
 Author: Yegappan Lakshmanan  (yegappan AT yahoo DOT com)
 For Vim version 8.2.2342 and above
-Last change: Feb 2, 2022
+Last change: Feb 4, 2022
 
 ==============================================================================
                                                *lsp-license*
@@ -115,7 +115,8 @@ The following commands are provided:
 :LspCodeAction         Apply the code action supplied by the LSP server to
                        the diagnostic in the current line.
 :LspSymbolSearch       Perform a workspace wide search for a symbol
-:LspSelectionRange     Visually select the current symbol range
+:LspSelectionExpand    Expand the current symbol range visual selection
+:LspSelectionShrink    Shrink the current symbol range visual selection
 :LspFold               Fold the current file
 :LspWorkspaceAddFolder {folder}
                        Add a folder to the workspace
@@ -453,8 +454,25 @@ diagnostic messages, you can add the following line to your .vimrc file:
 <
                        Default is false
 
-                                               *:LspSelectionRange*
-:LspSelectionRange     Visually select the current symbol range.
+                                               *:LspSelectionExpand*
+:LspSelectionExpand    Expand the current symbol range visual selection. It
+                       is useful to create a visual map to use this command.
+                       Example: >
+
+                           xnoremap <silent> le <Cmd>LspSelectionExpand<CR>
+<
+                       With the above map, you can press "le" in visual mode
+                       successively to expand the current visual region.
+
+                                               *:LspSelectionShrink*
+:LspSelectionShrink    Shrink the current symbol range visual selection. It
+                       is useful to create a visual map to use this command.
+                       Example: >
+
+                           xnoremap <silent> ls <Cmd>LspSelectionShrink<CR>
+<
+                       With the above map, you can press "ls" in visual mode
+                       successively to shrink the current visual region.
 
                                                *:LspFold*
 :LspFold               Fold the current file.
index c9ef725bff2ab9ee99bc52a57bd8b141bb42c032..4da8915a9c0debe71df42978575f1f4bff17cfef 100644 (file)
@@ -48,7 +48,8 @@ if has('patch-8.2.4257')
   lspf.codeAction = lsp.CodeAction
   lspf.symbolSearch = lsp.SymbolSearch
   lspf.hover = lsp.Hover
-  lspf.selectionRange = lsp.SelectionRange
+  lspf.selectionExpand = lsp.SelectionExpand
+  lspf.selectionShrink = lsp.SelectionShrink
   lspf.foldDocument = lsp.FoldDocument
   lspf.listWorkspaceFolders = lsp.ListWorkspaceFolders
   lspf.addWorkspaceFolder = lsp.AddWorkspaceFolder
@@ -93,7 +94,8 @@ elseif has('patch-8.2.4019')
   lspf.codeAction = lsp_import.CodeAction
   lspf.symbolSearch = lsp_import.SymbolSearch
   lspf.hover = lsp_import.Hover
-  lspf.selectionRange = lsp_import.SelectionRange
+  lspf.selectionExpand = lsp_import.SelectionExpand
+  lspf.selectionShrink = lsp_import.SelectionShrink
   lspf.foldDocument = lsp_import.FoldDocument
   lspf.listWorkspaceFolders = lsp_import.ListWorkspaceFolders
   lspf.addWorkspaceFolder = lsp_import.AddWorkspaceFolder
@@ -127,7 +129,8 @@ else
          CodeAction,
          SymbolSearch,
          Hover,
-         SelectionRange,
+         SelectionExpand,
+         SelectionShrink,
          FoldDocument,
          ListWorkspaceFolders,
          AddWorkspaceFolder,
@@ -169,7 +172,8 @@ else
   lspf.codeAction = CodeAction
   lspf.symbolSearch = SymbolSearch
   lspf.hover = Hover
-  lspf.selectionRange = SelectionRange
+  lspf.selectionExpand = SelectionExpand
+  lspf.selectionShrink = SelectionShrink
   lspf.foldDocument = FoldDocument
   lspf.listWorkspaceFolders = ListWorkspaceFolders
   lspf.addWorkspaceFolder = AddWorkspaceFolder
@@ -216,7 +220,8 @@ var Trename = s:lspf.rename
 var TcodeAction = s:lspf.codeAction
 var TsymbolSearch = s:lspf.symbolSearch
 var Thover = s:lspf.hover
-var TselectionRange = s:lspf.selectionRange
+var TselectionExpand = s:lspf.selectionExpand
+var TselectionShrink = s:lspf.selectionShrink
 var TfoldDocument = s:lspf.foldDocument
 var TlistWorkspaceFolders = s:lspf.listWorkspaceFolders
 var TaddWorkspaceFolder = s:lspf.addWorkspaceFolder
@@ -269,7 +274,8 @@ command! -nargs=0 -bar LspRename call Trename()
 command! -nargs=0 -bar LspCodeAction call TcodeAction()
 command! -nargs=? -bar LspSymbolSearch call TsymbolSearch(<q-args>)
 command! -nargs=0 -bar LspHover call Thover()
-command! -nargs=0 -bar LspSelectionRange call TselectionRange()
+command! -nargs=0 -bar LspSelectionExpand call TselectionExpand()
+command! -nargs=0 -bar LspSelectionShrink call TselectionShrink()
 command! -nargs=0 -bar LspFold call TfoldDocument()
 command! -nargs=0 -bar LspWorkspaceListFolders call TlistWorkspaceFolders()
 command! -nargs=1 -bar -complete=dir LspWorkspaceAddFolder call TaddWorkspaceFolder(<q-args>)
index dc9270ce37e9f17e8cd0f0353f3172c3c0b8fdb7..5d4dc9f7025799c3de6a75054f37c4611e92bab2 100644 (file)
@@ -397,8 +397,8 @@ def Test_LspRename()
   :%bw!
 enddef
 
-# Test for :LspSelectionRange
-def Test_LspSelectionRange()
+# Test for :LspSelectionExpand and :LspSelectionShrink
+def Test_LspSelection()
   silent! edit Xtest.c
   sleep 500m
   var lines: list<string> =<< trim END
@@ -413,37 +413,86 @@ def Test_LspSelectionRange()
   END
   setline(1, lines)
   sleep 1
-  # start a block-wise visual mode, LspSelectionRange should change this to
+  # start a block-wise visual mode, LspSelectionExpand should change this to
   # a characterwise visual mode.
   exe "normal! 1G\<C-V>G\"_y"
   cursor(2, 1)
   redraw!
-  :LspSelectionRange
-  sleep 1
+  :LspSelectionExpand
   redraw!
   normal! y
   assert_equal('v', visualmode())
   assert_equal([2, 8], [line("'<"), line("'>")])
-  # start a linewise visual mode, LspSelectionRange should change this to
+  # start a linewise visual mode, LspSelectionExpand should change this to
   # a characterwise visual mode.
   exe "normal! 3GViB\"_y"
   cursor(4, 29)
   redraw!
-  :LspSelectionRange
-  sleep 1
+  :LspSelectionExpand
   redraw!
   normal! y
   assert_equal('v', visualmode())
   assert_equal([4, 5, 6, 5], [line("'<"), col("'<"), line("'>"), col("'>")])
+
+  # Expand the visual selection
+  xnoremap <silent> le <Cmd>LspSelectionExpand<CR>
+  xnoremap <silent> ls <Cmd>LspSelectionShrink<CR>
+  cursor(5, 8)
+  normal vley
+  assert_equal([5, 8, 5, 12], [line("'<"), col("'<"), line("'>"), col("'>")])
+  cursor(5, 8)
+  normal vleley
+  assert_equal([5, 8, 5, 14], [line("'<"), col("'<"), line("'>"), col("'>")])
+  cursor(5, 8)
+  normal vleleley
+  assert_equal([4, 30, 6, 5], [line("'<"), col("'<"), line("'>"), col("'>")])
+  cursor(5, 8)
+  normal vleleleley
+  assert_equal([4, 5, 6, 5], [line("'<"), col("'<"), line("'>"), col("'>")])
+  cursor(5, 8)
+  normal vleleleleley
+  assert_equal([2, 1, 8, 1], [line("'<"), col("'<"), line("'>"), col("'>")])
+  cursor(5, 8)
+  normal vleleleleleley
+  assert_equal([1, 1, 8, 1], [line("'<"), col("'<"), line("'>"), col("'>")])
+  cursor(5, 8)
+  normal vleleleleleleley
+  assert_equal([1, 1, 8, 1], [line("'<"), col("'<"), line("'>"), col("'>")])
+
+  # Shrink the visual selection
+  cursor(5, 8)
+  normal vlsy
+  assert_equal([5, 8, 5, 12], [line("'<"), col("'<"), line("'>"), col("'>")])
+  cursor(5, 8)
+  normal vlelsy
+  assert_equal([5, 8, 5, 12], [line("'<"), col("'<"), line("'>"), col("'>")])
+  cursor(5, 8)
+  normal vlelelsy
+  assert_equal([5, 8, 5, 12], [line("'<"), col("'<"), line("'>"), col("'>")])
+  cursor(5, 8)
+  normal vlelelelsy
+  assert_equal([5, 8, 5, 14], [line("'<"), col("'<"), line("'>"), col("'>")])
+  cursor(5, 8)
+  normal vlelelelelsy
+  assert_equal([4, 30, 6, 5], [line("'<"), col("'<"), line("'>"), col("'>")])
+  cursor(5, 8)
+  normal vlelelelelelsy
+  assert_equal([4, 5, 6, 5], [line("'<"), col("'<"), line("'>"), col("'>")])
+  cursor(5, 8)
+  normal vlelelelelelelsy
+  assert_equal([2, 1, 8, 1], [line("'<"), col("'<"), line("'>"), col("'>")])
+
+  xunmap le
+  xunmap ls
   bw!
 
   # empty file
-  assert_equal('', execute('LspSelectionRange'))
+  assert_equal('', execute('LspSelectionExpand'))
 
   # file without an LSP server
   edit a.b
   assert_equal(['Error: LSP server for "a.b" is not found'],
-              execute('LspSelectionRange')->split("\n"))
+              execute('LspSelectionExpand')->split("\n"))
 
   :%bw!
 enddef