mirror of
https://github.com/inkarkat/vim-ingo-library.git
synced 2026-05-29 11:18:51 +02:00
175 lines
8.1 KiB
VimL
175 lines
8.1 KiB
VimL
" ingo/cmdargs/substitute.vim: Functions for parsing of :substitute arguments.
|
|
"
|
|
" DEPENDENCIES:
|
|
" - ingo/list.vim autoload script
|
|
"
|
|
" Copyright: (C) 2012-2016 Ingo Karkat
|
|
" The VIM LICENSE applies to this script; see ':help copyright'.
|
|
"
|
|
" Maintainer: Ingo Karkat <ingo@karkat.de>
|
|
"
|
|
" REVISION DATE REMARKS
|
|
" 1.021.011 20-Jun-2014 FIX: ingo#cmdargs#substitute#Parse() branch for
|
|
" special case of {flags} without /pat/string/
|
|
" must only be entered when a:arguments is not
|
|
" empty.
|
|
" 1.014.010 15-Oct-2013 Factor out s:EnsureList() to ingo/list.vim.
|
|
" 1.011.009 24-Jul-2013 FIX: Use the rules for the /pattern/ separator
|
|
" as stated in :help E146.
|
|
" 1.009.008 14-Jun-2013 Minor: Make matchlist() robust against
|
|
" 'ignorecase'.
|
|
" 1.007.007 01-Jun-2013 Move functions from ingo/cmdargs.vim to
|
|
" ingo/cmdargs/pattern.vim and
|
|
" ingo/cmdargs/substitute.vim.
|
|
" 1.006.006 29-May-2013 Again change
|
|
" ingo#cmdargs#ParseSubstituteArgument() interface
|
|
" to parse the :substitute [flags] [count] by
|
|
" default.
|
|
" 1.006.005 28-May-2013 BUG: ingo#cmdargs#ParseSubstituteArgument()
|
|
" mistakenly returns a:defaultFlags when full
|
|
" /pat/repl/ or a literal pat is passed. Only
|
|
" return a:defaultFlags when the passed
|
|
" a:arguments is really empty.
|
|
" CHG: Redesign
|
|
" ingo#cmdargs#ParseSubstituteArgument() interface
|
|
" to the existing use cases. a:defaultReplacement
|
|
" should only be used when a:arguments is really
|
|
" empty, too. Introduce an optional options
|
|
" Dictionary and preset replacement / flags
|
|
" defaults of "~" and "&" resp. for when
|
|
" a:arguments is really empty, which makes sense
|
|
" for use with :substitute. Allow submatches for
|
|
" a:flagsExpr via a:options.flagsMatchCount, to
|
|
" avoid further parsing in the client.
|
|
" ENH: Also parse lone {flags} (if a:flagsExpr is
|
|
" given) by default, and allow to turn this off
|
|
" via a:options.isAllowLoneFlags.
|
|
" ENH: Allow to pass a:options.emptyPattern, too.
|
|
" 1.001.004 21-Feb-2013 Move to ingo-library.
|
|
" 003 29-Jan-2013 Add ingocmdargs#ParseSubstituteArgument() for
|
|
" use in PatternsOnText/Except.vim and
|
|
" ExtractMatchesToReg.vim.
|
|
" Change ingocmdargs#UnescapePatternArgument() to
|
|
" take the result of
|
|
" ingocmdargs#ParsePatternArgument() instead of
|
|
" invoking that function itself. And make it
|
|
" handle an empty separator.
|
|
" 002 21-Jan-2013 Add ingocmdargs#ParsePatternArgument() and
|
|
" ingocmdargs#UnescapePatternArgument() from
|
|
" PatternsOnText.vim.
|
|
" 001 25-Nov-2012 file creation from CaptureClipboard.vim.
|
|
|
|
function! s:ApplyEmptyFlags( emptyFlags, parsedFlags)
|
|
return (empty(filter(copy(a:parsedFlags), '! empty(v:val)')) ? a:emptyFlags : a:parsedFlags)
|
|
endfunction
|
|
function! ingo#cmdargs#substitute#Parse( arguments, ... )
|
|
"******************************************************************************
|
|
"* PURPOSE:
|
|
" Parse the arguments of a custom command that works like :substitute.
|
|
"* ASSUMPTIONS / PRECONDITIONS:
|
|
" None.
|
|
"* EFFECTS / POSTCONDITIONS:
|
|
" None.
|
|
"* INPUTS:
|
|
" a:arguments The command's raw arguments; usually <q-args>.
|
|
" a:options.flagsExpr Pattern that captures any optional part
|
|
" after the replacement (usually some
|
|
" substitution flags). By default, captures
|
|
" the known :substitute |:s_flags| and
|
|
" optional [count]. Pass an empty string to
|
|
" disallow any flags.
|
|
" a:options.additionalFlags Flags that will be recognized in addition to
|
|
" the default |:s_flags|; default none. Modify
|
|
" this instead of passing a:options.flagsExpr
|
|
" if you want to recognize additional flags.
|
|
" a:options.flagsMatchCount Optional number of submatches captured by
|
|
" a:options.flagsExpr. Defaults to 2 with the
|
|
" default a:options.flagsExpr, to 1 with a
|
|
" non-standard non-empty a:options.flagsExpr,
|
|
" and 0 if a:options.flagsExpr is empty.
|
|
" a:options.defaultReplacement Replacement to use when the replacement part
|
|
" is omitted. Empty by default.
|
|
" a:options.emptyPattern Pattern to use when no arguments at all are
|
|
" given. Defaults to "", which automatically
|
|
" uses the last search pattern in a
|
|
" :substitute. You need to escape this
|
|
" yourself (to be able to pass in @/, which
|
|
" already is escaped).
|
|
" a:options.emptyReplacement Replacement to use when no arguments at all
|
|
" are given. Defaults to "~" to use the
|
|
" previous replacement in a :substitute.
|
|
" a:options.emptyFlags Flags to use when a:options.flagsExpr is not
|
|
" empty, but no arguments at all are given.
|
|
" Defaults to "&" to use the previous flags of
|
|
" a :substitute. Provide a List if
|
|
" a:options.flagsMatchCount is larger than 1.
|
|
" a:options.isAllowLoneFlags Allow to omit /pat/repl/, and parse a
|
|
" stand-alone a:options.flagsExpr (assuming
|
|
" one is passed). On by default.
|
|
"* RETURN VALUES:
|
|
" A list of [separator, pattern, replacement, flags, count] (default)
|
|
" A list of [separator, pattern, replacement] when a:options.flagsExpr is
|
|
" empty or a:options.flagsMatchCount is 0.
|
|
" A list of [separator, pattern, replacement, submatch1, ...];
|
|
" elements added depending on a:options.flagsMatchCount.
|
|
" flags and count are meant to be directly concatenated; count therefore keeps
|
|
" leading whitespace, but be aware that this is optional with :substitute,
|
|
" too!
|
|
" The replacement part is always escaped for use inside separator, also when
|
|
" the default is taken.
|
|
"******************************************************************************
|
|
let l:options = (a:0 ? a:1 : {})
|
|
let l:additionalFlags = get(l:options, 'additionalFlags', '')
|
|
let l:flagsExpr = get(l:options, 'flagsExpr', '\(&\?[cegiInp#lr' . l:additionalFlags . ']*\)\(\s*\d*\)')
|
|
let l:isParseFlags = (! empty(l:flagsExpr))
|
|
let l:flagsMatchCount = get(l:options, 'flagsMatchCount', (has_key(l:options, 'flagsExpr') ? (l:isParseFlags ? 1 : 0) : 2))
|
|
let l:defaultFlags = (l:isParseFlags ? repeat([''], l:flagsMatchCount) : [])
|
|
let l:defaultReplacement = get(l:options, 'defaultReplacement', '')
|
|
let l:emptyPattern = get(l:options, 'emptyPattern', '')
|
|
let l:emptyReplacement = get(l:options, 'emptyReplacement', '~')
|
|
let l:emptyFlags = get(l:options, 'emptyFlags', ['&'] + repeat([''], l:flagsMatchCount - 1))
|
|
let l:isAllowLoneFlags = get(l:options, 'isAllowLoneFlags', 1)
|
|
|
|
let l:matches = matchlist(a:arguments, '\C^\([[:alnum:]\\"|]\@![\x00-\xFF]\)\(.\{-}\)\%(\%(^\|[^\\]\)\%(\\\\\)*\\\)\@<!\1\(.\{-}\)\%(\%(^\|[^\\]\)\%(\\\\\)*\\\)\@<!\1' . l:flagsExpr . '$')
|
|
if ! empty(l:matches)
|
|
" Full /pat/repl/[flags].
|
|
return l:matches[1:3] + (l:isParseFlags ? l:matches[4:(4 + l:flagsMatchCount - 1)] : [])
|
|
endif
|
|
|
|
let l:matches = matchlist(a:arguments, '\C^\([[:alnum:]\\"|]\@![\x00-\xFF]\)\(.\{-}\)\%(\%(^\|[^\\]\)\%(\\\\\)*\\\)\@<!\1\(.\{-}\)$')
|
|
if ! empty(l:matches)
|
|
" Partial /pat/[repl].
|
|
return l:matches[1:2] + [(empty(l:matches[3]) ? escape(l:defaultReplacement, l:matches[1]) : l:matches[3])] + l:defaultFlags
|
|
endif
|
|
|
|
let l:matches = matchlist(a:arguments, '\C^\([[:alnum:]\\"|]\@![\x00-\xFF]\)\(.\{-}\)$')
|
|
if ! empty(l:matches)
|
|
" Minimal /[pat].
|
|
return l:matches[1:2] + [escape(l:defaultReplacement, l:matches[1])] + l:defaultFlags
|
|
endif
|
|
|
|
if ! empty(a:arguments)
|
|
if l:isParseFlags && l:isAllowLoneFlags
|
|
let l:matches = matchlist(a:arguments, '\C^' . l:flagsExpr . '$')
|
|
if ! empty(l:matches)
|
|
" Special case of {flags} without /pat/string/.
|
|
return ['/', l:emptyPattern, escape(l:emptyReplacement, '/')] + s:ApplyEmptyFlags(ingo#list#Make(l:emptyFlags), l:matches[1:(l:flagsMatchCount)])
|
|
endif
|
|
endif
|
|
|
|
" Literal pat.
|
|
if ! empty(l:defaultReplacement)
|
|
" Clients cannot concatentate the results without a separator, so
|
|
" use one.
|
|
return ['/', escape(a:arguments, '/'), escape(l:defaultReplacement, '/')] + l:defaultFlags
|
|
else
|
|
return ['', a:arguments, l:defaultReplacement] + l:defaultFlags
|
|
endif
|
|
else
|
|
" Nothing.
|
|
return ['/', l:emptyPattern, escape(l:emptyReplacement, '/')] + (l:isParseFlags ? ingo#list#Make(l:emptyFlags) : [])
|
|
endif
|
|
endfunction
|
|
|
|
" vim: set ts=8 sts=4 sw=4 noexpandtab ff=unix fdm=syntax :
|