sponsor Vim development Vim logo Vim Book Ad

CountJump : Create custom motions and text objects via repeated jumps.

 script karma  Rating 61/16, Downloaded by 4850  Comments, bugs, improvements  Vim wiki

created by
Ingo Karkat
 
script type
utility
 
description
DESCRIPTION
Though it is not difficult to write a custom movement (basically a :map
that executes some kind of search or jump) and a custom text-object (an
:omap that selects a range of text), this is too complex for a novice user
and often repetitive.
This plugin covers the common use case where the movement and boundaries of a
text object can be specified via start and end patterns, and offers a single
function to set up related mappings. With it, you can enhance some built-in
Vim mappings to take an optional [count], and quickly define new mappings for
help file sections, diff hunks, embedded macros, and so on...

As a generalization of the start and end patterns, the movement and boundaries
of a text object can also be specified via jump functions, i.e. Funcrefs of
functions that position the cursor on the appropriate location and return that
location. This can be used where the jump is difficult to express with a
single regular expression, the jump may need adapting depending on the
context, or other uses.
This plugin contains some support for movement and text objects consisting of
text regions that can be defined by continuous lines that match a particular
pattern, e.g. comment blocks that all start with /^\s*#/.

SEE ALSO
The following ftplugins use this plugin:

- diff_movement.vim (vimscript #3180):
  Movement over diff hunks with ]] etc.
- diffwindow_movement.vim (vimscript #3719):
  Movement over changes in a diff window.
- dosbatch_movement.vim (vimscript #4004):
  Movement over MSDOS batch file functions / labels with ]m etc.
- fortunes_movement.vim (vimscript #3181):
  Movement over email fortunes with ]] etc.
- help_movement.vim (vimscript #3179):
  Movement over Vim help sections with ]] etc.
- mail_movement.vim (vimscript #3182):
  Movement over email quotes with ]] etc.
- vbs_movement.vim (vimscript #4003):
  Movement over VBScript classes / functions / properties / subs with ]m etc.
- vim_movement.vim (vimscript #4002):
  Movement over Vim functions with ]m etc.

The following plugins offer new motions through this plugin:

- ConflictMotions.vim (vimscript #3991):
  Motions to and inside SCM conflict markers.
- JumpToTrailingWhitespace.vim (vimscript #3968):
  Motions to locate unwanted whitespace at the end of lines.
- JumpToVerticalBlock.vim (vimscript #5657):
  Like W / E, but vertically in the same column.
- JumpToVerticalOccurrence.vim (vimscript #4841):
  Like f{char}, but searching the same screen column, not line.
- SameSyntaxMotion.vim (vimscript #4338):
  Motions to the borders of the same syntax highlighting.
- TaskMotion.vim (vimscript #3990):
  Motions to task and TODO markers.

RELATED WORKS
- motpat.vim (vimscript #3030) offers similar functions to setup motion
  mappings, but no text objects (yet).
- textobj-user (vimscript #2100) has support for user-defined text objects via
  regular expressions, but they don't support selecting multiple via [count].
- movealong.vim (vimscript #4691) provides a :Movealong command (and optional
  mappings) that repeatedly executes a motion until a condition of a syntax,
  pattern match, or arbitrary expression is met.

USAGE
The plugin defines several functions, which set up the appropriate mappings
based on the arguments that you supply. The following is an overview; you'll
find the details directly in the implementation files in the
autoload/CountJump/ directory.

CountJump#Motion#MakeBracketMotion( mapArgs, keyAfterBracket, inverseKeyAfterBracket, patternToBegin, patternToEnd, isEndPatternToEnd, ... )

This function sets up mappings starting with [ and ] for movement (with
optional [count]) relative to the current cursor position, targeting either a
text pattern at the beginning ([{keyAfterBracket} mapping) or a text pattern
at the end (]{inverseKeyAfterBracket} mapping) of whatever you want to treat
as a text block.

CountJump#Motion#MakeBracketMotionWithJumpFunctions( mapArgs, keyAfterBracket, inverseKeyAfterBracket, JumpToBeginForward, JumpToBeginBackward, JumpToEndForward, JumpToEndBackward, isEndJumpToEnd, ... )

This function sets up mappings starting with [ and ] for movement (with
optional [count]) relative to the current cursor position, but rely on four
passed jump functions instead of text patterns to do the movement.

CountJump#TextObject#MakeWithCountSearch( mapArgs, textObjectKey, types, selectionMode, patternToBegin, patternToEnd )

Defines a complete set of mappings for inner and/or outer text objects that
support an optional [count] and are driven by search patterns for the
beginning and end of a block. Outer text objects include the matched pattern
text, inner ones not. Selection can be characterwise, linewise or blockwise.

CountJump#TextObject#MakeWithJumpFunctions( mapArgs, textObjectKey, types, selectionMode, JumpToBegin, JumpToEnd )

This is a generalization of CountJump#TextObject#MakeWithCountSearch() that
invokes custom functions instead of searching for a fixed pattern. This is
useful if the check for a match is too complex for a single regular
expression, or if you need to adjust the match position depending on the
circumstances.

Often, a region can be defined as a block of continuous lines that all match a
certain pattern (or, even more generic, where a provided predicate function
returns a match position). The following functions aid in implementing
movements to the boundaries of these regions and text objects consisting of
the region:

CountJump#Region#JumpToRegionEnd( count, Expr, isMatch, step, isToEndOfLine )

Starting from the current line, search for the position where the count'th
region ends. Use this function to build Funcrefs for forward / backward jumps
that can then be passed to CountJump#TextObject#MakeWithJumpFunctions().

CountJump#Region#JumpToNextRegion( count, Expr, isMatch, step, isAcrossRegion, isToEndOfLine )

Starting from the current line, search for the position where the count'th
region begins/ends.

CountJump#Region#Motion#MakeBracketMotion( mapArgs, keyAfterBracket, inverseKeyAfterBracket, Expr, isMatch, ... )

This function sets up mappings starting with [ and ] for movement (with
optional [count]) relative to the current cursor position, targeting a text
region defined by contiguous lines that (don't) match a:Expr.

CountJump#Region#TextObject#Make( mapArgs, textObjectKey, types, selectionMode, Expr, isMatch )

Defines a complete set of mappings for inner and/or outer text objects that
support an optional [count] and select regions of lines which are defined by
contiguous lines that (don't) match a:Expr.
The inner text object comprises all lines of the region itself, while the
outer text object also includes all adjacent lines above and below which do
not themselves belong to a region.

The custom Funcrefs for jumps and predicates of lines belonging to a range may
be invoked multiple times until the CountJump function arrives at its
destination. To help the Funcrefs to determine where in this sequence they
are, an empty g:CountJump_MotionContext dictionary is initialized at the
start of a range motion (for pattern / jump functions, this isn't necessary;
there's either no custom code or just a single invocation), and an empty
g:CountJump_TextObjectContext dictionary is initialized at the start of a text
object. Funcrefs can put custom information (e.g. the particular comment
prefix on the current line) in there and evaluate this in subsequent
invocations.

EXAMPLE
Let's illustrate the usage by developing custom motions and text objects for
Pascal begin..end blocks.

We want to move around blocks, and override the default section movements for
it:
]]                      Go to [count] next start of a block.
][                      Go to [count] next end of a block.
[[                      Go to [count] previous start of a block.
[]                      Go to [count] previous end of a block.

    call CountJump#Motion#MakeBracketMotion('<buffer>', '', '', '\c^begin\n\zs', '\c^.*\nend', 0)
The begin pattern positions the cursor on the beginning of the line following
the "begin" keyword, the end pattern on the beginning of the line
preceding the "end" keyword.

We want to select a block, either including or excluding the lines with the
begin..end keywords:
ib                      "inner block" text object, select [count] contents of
                        a block.
ab                      "a block" text object, select [count] blocks.

    call CountJump#TextObject#MakeWithCountSearch('<buffer>', 'b', 'ai', 'V', '\c^begin\n', '\c^end.*$')

If there is a filetype detection for Pascal files, we can simply put the
above calls in a ~/.vim/ftplugin/pascal_movement.vim script and are done.
 
install details
INSTALLATION
The code is hosted in a Git repo at
    https://github.com/inkarkat/vim-CountJump
You can use your favorite plugin manager, or "git clone" into a directory used
for Vim packages. Releases are on the "stable" branch, the latest unstable
development snapshot on "master".

This script is also packaged as a vimball. If you have the "gunzip"
decompressor in your PATH, simply edit the *.vmb.gz package in Vim; otherwise,
decompress the archive first, e.g. using WinZip. Inside Vim, install by
sourcing the vimball or via the :UseVimball command.
    vim CountJump*.vmb.gz
    :so %
To uninstall, use the :RmVimball command.

DEPENDENCIES
- Requires Vim 7.0 or higher.
- Requires the ingo-library.vim plugin (vimscript #4433), version 1.041 or
  higher.

INTEGRATION
If you want to define motions that do not start with [ / ], and the plugin
that employs CountJump offers a configuration variable like
g:PluginName_mapping to influence the mapped key(s), you can define
intermediate <Plug>-mappings (using-<Plug>), and then define your own custom
mappings based on them:
    let g:PluginName_mapping = '<Plug>PluginName%s'
    nmap { <Plug>PluginNameBackward
    nmap } <Plug>PluginNameForward
    omap { <Plug>PluginNameBackward
    omap } <Plug>PluginNameForward
    vmap { <Plug>PluginNameBackward
    vmap } <Plug>PluginNameForward

If you want to define text objects that do not start with i / a, and the plugin
that employs CountJump offers a configuration variable like
g:PluginName_mapping to influence the mapped key(s), you can define
intermediate <Plug>-mappings (using-<Plug>), and then define your own custom
mappings based on them:
    let g:PluginName_mapping = '<Plug>PluginName%s'
    omap ,p <Plug>PluginNameInner
    omap ,P <Plug>PluginNameOuter
    vmap ,p <Plug>PluginNameInner
    vmap ,P <Plug>PluginNameOuter
 

rate this script Life Changing Helpful Unfulfilling 
script versions (upload new version)

Click on the package to download.

package script version date Vim version user release notes
CountJump-1.91.vmb.gz 1.91 2020-04-04 7.0 Ingo Karkat - ENH: Allow Funcref for a:searchArguments / first search argument / a:patternTo{Begin,End}
- ENH: Allow to influence given [count] via CountJump#CountCountJumpWithWrapMessage() variant of CountJump#CountJumpWithWrapMessage().
- FIX: Avoid "Error detected while processing :" additional line above an exception thrown (from a client-supplied Funcref).
- ENH: Allow client-supplied Funcrefs to throw a "CountJump: <error text>" exception to issue custom errors.
- Prevent a custom text object from clobbering the register passed to a custom operator (as :omaps defined by this plugin). *** You need to update to ingo-library (vimscript #4433) version 1.041! ***
CountJump-1.90.vmb.gz 1.90 2018-02-11 7.0 Ingo Karkat - Rename g:CountJump_Context to g:CountJump_MotionContext, to avoid a clash with the text object in CountJump#TextObject#TextObjectWithJumpFunctions(). Clear g:CountJump_MotionContext at the end of the function.
- Rename g:CountJump_Context to g:CountJump_TextObjectContext. When using CountJump#Region#TextObject#Make(), the generated jump functions also set that context, and there's a clash with CountJump#TextObject#TextObjectWithJumpFunctions(). In particular, one cannot store a context for the entire text object (both jumps to begin and end), as the individual jump functions clear the identical context. Clear g:CountJump_TextObjectContext at the end of the function.
- ENH: Support non-argument Funcref for a:Expr that gets evaluated once at the beginning, and should yield a regular expression that is then used in its stead.
CountJump-1.86.vmb.gz 1.86 2017-07-24 7.0 Ingo Karkat - CountJump#Motion#MakeBracketMotion(): The a:patternToBegin, a:patternToEnd, a:searchName arguments may contain special characters that need escaping in a map. Use ingo#escape#command#mapescape().
- CountJump#Region#Motion#MakeBracketMotion(): The a:Expr argument may contain special characters that need escaping in a map. Use ingo#escape#command#mapescape().
- Retire duplicated fallback for ingo#motion#helper#AdditionalMovement(); since version 1.85, the ingo-library is now a mandatory dependency.
- CountJump#Motion#MakeBracketMotionWithJumpFunctions(), CountJump#TextObject#MakeWithJumpFunctions(), CountJump#Region#Motion#MakeBracketMotion(): Catch all exceptions and report only the text. My ErrorMotion.vim plugin could be slow to find the next error if there is none. Aborting with <C-c> would print a long multi-line exception: "Error detected while processing function ErrorMotion#Forward[1]..CountJump#JumpFunc[39]..HlgroupMotion#JumpWithWrapMessage[26]..CountJump#CountJumpFuncWithWrapMessage[35]..HlgroupMotion#SearchFirstHlgroup: line   67: Interrupted". As we apparently cannot avoid the printing of "Type :quit<Enter>  to exit Vim", suppress the Vim:Interrupt exception, and :echoerr all others.
CountJump-1.85.vmb.gz 1.85 2014-12-23 7.0 Ingo Karkat - Use ingo/pos.vim.
- Use ingo#msg#WarningMsg().
- Make test for 'virtualedit' option values also account for multiple values. *** You need to update to ingo-library (vimscript #4433) version 1.019! ***
CountJump-1.84.vmb.gz 1.84 2014-04-25 7.0 Ingo Karkat - Pin down the 'virtualedit' setting (to "onemore") during CountJump#TextObject#TextObjectWithJumpFunctions() to avoid that a characterwise outer text object that ends at the end of a line includes the line's newline character when 'selection' is "exclusive".
- FIX: There are no buffer-local functions with a b: scope prefix, and Vim 7.4.264 disallows those invalid function names now. Previously, multiple buffer-local text objects with the same key would override each other. Instead, make the functions created by CountJump#TextObject#MakeWithCountSearch() and CountJump#Region#TextObject#Make() buffer-scoped by prefixing "s:B" and the buffer number.
CountJump-1.83.vmb.gz 1.83 2014-01-23 7.0 Ingo Karkat - Use more canonical way of invoking the Funcrefs in CountJump#Motion#MakeBracketMotionWithJumpFunctions(); this will then also work with passed String function names.
- FIX: Need to save v:count1 before issuing the normal mode "gv" command.
- Minor: Make substitute() robust against 'ignorecase'.
- Add optional dependency to ingo-library (vimscript #4433).
- FIX: In text objects, when the end position is before the begin position, that's not a valid selection. Test for this and abort in that case.
- For linewise selections, always position the cursor at the start of the end line to be consistent with the built-in text objects, and to avoid complicating the search patterns when attempting to do this through them.
CountJump-1.81.vmb.gz 1.81 2012-10-16 7.0 Ingo Karkat - ENH: Add optional a:searchName argument to CountJump#Motion#MakeBracketMotion() to make searches wrap around when 'wrapscan' is set. Custom jump functions can do this since version 1.70; now, this can also be utilized by motions defined via a search pattern.
- BUG: Wrong variable scope for copied a:isBackward in CountJump#CountSearchWithWrapMessage().
CountJump-1.80.vmb.gz 1.80 2012-10-15 7.0 Ingo Karkat - FIX: In CountJump#TextObject#TextObjectWithJumpFunctions(), do not beep when there's no end position. In this case, the jump function (often CountJump#CountSearch()) should have emitted a beep already, and we want to avoid a double beep.
- Also handle move to the buffer's very last character in operator-pending mode with a pattern to end "O" motion.
- Add CountJump#CountJumpFuncWithWrapMessage() / CountJump#CountJumpFunc() to help implement custom motions with only a simple function that performs a single jump.
- FIX: Visual end pattern / jump to end with 'selection' set to "exclusive" also requires the special additional treatment of moving one right, like operator-pending mode.
- BUG: Operator-pending motion with end pattern / jump to end operates on one character too few when moving to begin.
- Clear any previous wrap message when wrapping is enabled; it's confusing otherwise.
CountJump-1.70.vmb.gz 1.70 2012-09-03 7.0 Ingo Karkat - ENH: Check for searches wrapping around the buffer and issue a corresponding warning, like the built-in searches do. Though the mappings that can be made with CountJump currently do not use 'wrapscan', other plugins that define their own jump functions and use the CountJump#CountJump() function for it may use it. Create function overloads CountJump#CountJumpWithWrapMessage() and CountJump#CountSearchWithWrapMessage().
CountJump.vba.gz 1.60 2012-03-27 7.0 Ingo Karkat - ENH: Allow motions that do not start with [ / ] and text objects that do not start with i / a by passing keys that begin with <Plug>. With this, plugins using CountJump can offer the expected customizability.
CountJump.vba.gz 1.50 2011-08-31 7.0 Ingo Karkat - For regions of lines, also support a match()-like Funcref instead of a pattern to define the range. This for example enables to define a range of diff changes via a predicate function that checks diff_hlID() != 0.
- Initialize global g:CountJump_Context object for custom use by Funcrefs.
CountJump.vba.gz 1.41 2011-06-13 7.0 Ingo Karkat FIX: Directly ring the bell to avoid problems when running under :silent!.
CountJump.vba.gz 1.40 2010-12-20 7.0 Ingo Karkat - ENH: Added CountJump#Region#TextObject#Make() to easily define text objects for regions.
- Interface change: Jump functions again return position (and actual, corrected one for a:isToEndOfLine). Though the position is not used for motions, it is necessary for text objects to differentiate between "already at the begin/end position" and "no such position".  
CountJump.vba.gz 1.30 2010-12-20 7.0 Ingo Karkat - ENH: Added CountJump#Region#Motion#MakeBracketMotion() to easily define bracket motions for regions.
- Interface changes:
  - Jump functions don't necessarily return jump position any more; this special case is only required for text objects.
  - Moved CountJump#Region#Jump() to CountJump#JumpFunc().
  - Added a:isToEndOfLine argument to CountJump#Region#JumpToRegionEnd() and CountJump#Region#JumpToNextRegion(), which is useful for operator-pending and characterwise visual mode mappings; the entire last line will then be operated on / selected.
  - Added a:isMatch argument to CountJump#Region#SearchForRegionEnd(), CountJump#Region#JumpToRegionEnd(), CountJump#Region#SearchForNextRegion(), CountJump#Region#JumpToNextRegion(). This allows definition of regions via non-matches, which can be substantially simpler (and faster to match) than coming up with a "negative" regular expression.
CountJump.vba.gz 1.22 2010-08-06 7.0 Ingo Karkat - No more motion mappings and text objects for select mode; as the mappings start with a printable character, no select-mode mapping should be defined.
CountJump.vba.gz 1.21 2010-08-03 7.0 Ingo Karkat - FIX: A 2]] jump inside a region (unless last line) jumped like a 1]] jump. The search for next region must not decrease the iteration counter when _not_ searching _across_ the region.
- FIX: Must not do (characterwise) end position adaptation for linewise text object that does not exclude boundaries.
- Switched example from email fortunes to Pascal begin..end blocks, as they are conceptually easier.
CountJump.vba.gz 1.20 2010-08-03 7.0 Ingo Karkat - ENH: In CountJump#Motion#MakeBracketMotion(), a:keyAfterBracket and a:inverseKeyAfterBracket can now be empty, the resulting mappings are then omitted. Likewise, any jump function can be empty in CountJump#Motion#MakeBracketMotionWithJumpFunctions().
- With the added CountJump#Motion#MakeBracketMotionWithJumpFunctions() motions can be defined via jump functions, similar to how text objects can be defined.
- Added CountJump/Region.vim to move to borders of a region defined by lines matching a pattern.
- FIX: CountJump#CountJump() with mode "O" didn't add original position to jump list.
- The previous visual selection is kept when the text object could not be selected. (Beforehand, a new selection of the text object's selection type was created.)
- The adjustment movements after the jumps to the text object boundaries now do not cause beeps if that movement cannot be done (e.g. a 'j' at the end of the buffer).
CountJump.vba.gz 1.10 2010-07-20 7.0 Ingo Karkat - Changed behavior if there aren't [count] matches: Instead of jumping to the last available match (and ringing the bell), the cursor stays at the original position, like with the old vi-compatible motions.
- ENH: Only adding to jump list if there actually is a match. This is like the built-in Vim motions work.
- FIX: For a linewise text object, the end cursor column is not important; do not compare with the original cursor column in this case.
CountJump.vba.gz 1.00 2010-06-22 7.0 Ingo Karkat Initial upload
ip used for rating: 18.118.30.253

If you have questions or remarks about this site, visit the vimonline development pages. Please use this site responsibly.
Questions about Vim should go to the maillist. Help Bram help Uganda.
   
Vim at Github