Sunday, January 7, 2024

Re: Commit: patch 9.1.0009: Cannot easily get the list of matches

Hi all,

On Thu, Jan 4, 2024 at 1:45 PM Christian Brabandt <cblists@256bit.org> wrote:
patch 9.1.0009: Cannot easily get the list of matches

Commit: https://github.com/vim/vim/commit/f93b1c881a99fa847a1bafa71877d7e16f18e6ef
Author: Yegappan Lakshmanan <yegappan@yahoo.com>
Date:   Thu Jan 4 22:28:46 2024 +0100

    patch 9.1.0009: Cannot easily get the list of matches

    Problem:  Cannot easily get the list of matches
    Solution: Add the matchstrlist() and matchbufline() Vim script
              functions (Yegappan Lakshmanan)


To demonstrate the use of the new matchbufline() function, I have created
the following script.  This does search text completion from the list of words
in the current buffer.  After typing a few letters in the "/" prompt, if you press
Tab, it will complete the word from the current buffer.  If you press Tab again,
then it will go to the next match.  If you press Shift-Tab, it will go back to
the previous match.

Regards,
Yegappan

--------------------------------------------------------------------------------------------------------
vim9script

var searchMatches: list<string> = []
var prevMatchIndex: number = 0
var prevCmdline: string = ''
var prevCmdlinePrefix: string = ''
var prevPat: string = ''

def SearchComplete(forward: bool): string
  var cmdtype: string = getcmdtype()
  if cmdtype != '/' && cmdtype != '?'
    feedkeys(forward ? "\<Tab>" : "\<S-Tab>", 'nt')
    return ''
  endif

  var cmdline: string = getcmdline()
  if cmdline != '' && cmdline ==# prevCmdline
    # Jump to the next/previous match
    if (cmdtype == '/' && forward) || (cmdtype == '?' && !forward)
      prevMatchIndex += 1
    else
      prevMatchIndex -= 1
    endif
    if prevMatchIndex < 0
      prevMatchIndex = searchMatches->len() - 1
    elseif prevMatchIndex >= searchMatches->len()
      prevMatchIndex = 0
    endif
    var s = searchMatches[prevMatchIndex][strlen(prevPat) : ]

    prevCmdline = prevCmdlinePrefix .. s
    setcmdline(prevCmdlinePrefix)
    return s
  endif

  var cmdpos: number = getcmdpos()

  var start: number = cmdpos - 1
  if start >= strlen(cmdline)
    start -= 1
  endif
  while start > 0 && cmdline[start - 1] =~ '\k'
    start -= 1
  endwhile
  var pat: string = cmdline[start : cmdpos]

  # Get the List of matches
  var start_lnum = &wrapscan || cmdtype == '?' ? 1 : line('.')
  var end_lnum = &wrapscan || cmdtype == '/' ? '$' : line('.')
  var l = matchbufline('', $'\<{pat}\k\+\>', start_lnum, end_lnum)
  if l->empty()
    return ''
  endif

  var curline = line('.')
  var curcol = col('.')
  var patStartByte = curcol - 1 - strlen(pat)

  l->filter((_, v) => v.lnum == curline ? cmdtype == '/' ? v.byteidx >= patStartByte : v.byteidx <= patStartByte : true)
  if l->empty()
    return ''
  endif

  # Sort by matched string and line/byte index
  l->sort((a, b) => {
    if a.text > b.text
      return 1
    elseif a.text == b.text
      if a.lnum > b.lnum
        return 1
      elseif a.lnum == b.lnum
        return a.byteidx > b.byteidx ? 1 : 0
      endif
      return 0
    endif
    return 0
  })
  # Remove duplicates
  l->uniq((a, b) => a.text ==# b.text ? 0 : 1)
  # Sort by byte index and line number
  l->sort((a, b) => a.lnum == b.lnum ? a.byteidx - b.byteidx : a.lnum - b.lnum)

  var startIdx: number
  if cmdtype == '/'
    startIdx = l->indexof((_, v) => v.lnum == curline ? v.byteidx >= patStartByte : v.lnum > curline)
  else
    startIdx = l->copy()->reverse()->indexof((_, v) => v.lnum == curline ? v.byteidx <= patStartByte : v.lnum < curline)
    startIdx = l->len() - startIdx - 1
  endif
 
  if startIdx == -1
    startIdx = forward ? 0 : searchMatches->len() - 1
  endif

  # Save the matched strings
  searchMatches = l->map((_, v) => v.text)

  var s = searchMatches[startIdx][strlen(pat) : ]

  # Save the state for jumping to the next previous match
  prevMatchIndex = startIdx
  prevPat = pat
  prevCmdline = cmdline .. s
  prevCmdlinePrefix = cmdline

  return s
enddef

cnoremap <Tab> <C-R>=<SID>SearchComplete(v:true)<CR>
cnoremap <S-Tab> <C-R>=<SID>SearchComplete(v:false)<CR>

# vim: ts=8 sw=2 sts=2 expandtab tw=80
--------------------------------------------------------------------------------------------------------

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to vim_use+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/vim_use/CAAW7x7nDkmO_MQRy4XycR%3DhDPmAVi1pHh2DSV23cPj65%3Dfi%2BQg%40mail.gmail.com.

No comments: