The original blog

The installation

The Ultisnips plug-in installation consists of two parts, one is the Ultisnips plug-in itself, and the other is the repository of code snippets. Generally, you can download the default snippet repository and upload it to your Github to modify it as needed. If you, like me, use vim-Plug to manage plug-ins, add the following code to your VIMRC to save the refresh

Plug 'SirVer/ultisnips'
"Your own repository git address
Plug 'keelii/vim-snippets'
Copy the code

All the code snippets in the example above are stored in vim-Snippets /UltiSnips under the plug-in installation directory. The file is named ft. Snippets, which is the fileType in vim. There is an all.snippets that is the only snippet that works for all files

configuration

For shortcut Settings, I usually use TAB to trigger snippet completion and do not use YCM (official documentation states that TAB completion cannot be used if YCM is used)

let g:UltiSnipsExpandTrigger="<tab>"
"Use TAB to toggle the next trigger, a trigger on shit+ TAB
let g:UltiSnipsJumpForwardTrigger="<tab>"
let g:UltiSnipsJumpBackwardTrigger="<S-tab>"
"Split the screen vertically with the UltiSnipsEdit command
let g:UltiSnipsEditSplit="vertical"
Copy the code

Rely on

The ultisnips plugin requires your vim to support Python. You can check if your Vim version supports Python by using the following command in vim mode

:echo has("python")
:echo has("python3")
Copy the code

Define a code snippet

Define the format

Snippet trigger character ["Code snippet description"[Parameter]] Snippet content endsnippetCopy the code

A code snippet minimized

snippet if "if (condition) { ... }"
if (${1:true}) {
    $0
}
endsnippet
Copy the code

So when you type if in vim and you hit TAB it opens up an if statement, the first trigger is the if condition expression, the last trigger is the body of the if statement

${1:true} indicates that this is the first trigger, and the placeholder is true. If there is no default value for the placeholder, you can use $1, $2, $3…

The contents of the visual selection area are placeholders

snippet if "if (...) "
if (${1:true}) {
    ${VISUAL}
}
endsnippet
Copy the code

${VISUAL} represents text selected in VISUAL mode in VIm. This is useful when refactoring code (more advanced usage later), as illustrated in the previous figure

Parameters to the code snippet

  • bIndicates that the trigger character should be at the beginning of a line
  • iIndicates that trigger characters can be inside words (use this option for continuous presentations)
  • wIndicates that the triggering character must be preceded and followed by a letter cut-off point
  • rThe trigger character can be a regular expression
  • tIf there are tabs in the expanded snippet, output it as is, even if you have expandTab set in your vIMRC
  • mDeletes all whitespace characters to the right of the code snippet
  • eRepresents a custom context
  • AIt is automatically triggered without pressing TAB, similar to abBR in VIM

Content interpreter

Ultisnips defines code snippets that support three different language injections: shell, Vimscript, and Python, which are represented by backquotes in code snippets

Shell code

Is a snippet of code that your command line shell can execute, such as printing the current time

➜ Date Monday, August 27, 2018 18:19:38 CSTCopy the code

Just use the backquotes “‘” in the snippet

snippet today
Today is the `date`.
endsnippet
Copy the code

Enter today and press TAB to expand (the format is different from that in the shell above, presumably because of the vim language setting problem) :

Today is the Mon Aug 27 18:24:51 CST 2018.
Copy the code

Vimscript code

Use indent to print the current indent value, using prefix! V means vimscript

snippet indent
Indent is:`! vindent(".")`.
endsnippet
Copy the code

Python code

Explaining the execution of Python code in code snippets is ultisnips’ most powerful feature, with the prefix! P. The system injects variables into Python that can be manipulated directly using Python code

  • fn– Indicates the current file name
  • path– Path of the current file name
  • t– placeholder dictionary that can be usedt[1], t[2], t.vTo get placeholder content
  • snip – UltiSnips.TextObjects.SnippetUtilObject
  • match– Match elements returned when re snippets (very powerful)

The most commonly used snIP objects provide the following variables:

  • snip.rvA return value is assigned to the RV by the string processed after the python code is executed
  • snip.fnRepresents the current file name
  • snip.ftIndicates the current file type
  • snip.vRepresents the VISUAL schema variable, wheresnip.v.modeRepresents the schema type,snip.v.textRepresents the character selected in VISUAL mode

Placeholder selection

UltiSnips supports switching between placeholders using shortcut keys. I used < TAB > and

to switch between next and previous placeholders. The scope of the switch is inside the current code snippet (even if the placeholder has been changed) and it doesn’t work when the cursor is moved out

Custom Context

Custom contexts can use regular matching to determine whether snippets are available, such as snippets that are valid in a given if statement, in the following format:

Snippet trigger character “description” “expression” parameter

Let’s say we define a snippet that can be expanded only if the previous line begins with if (DEVELOPMENT) {

snippet dbg "if (DEVELOPMENT) dbg" "re.match('^if \(DEVELOPMENT\) \{', snip.buffer[snip.line-1])" be
debugger;
endsnippet
Copy the code

Common use

Continuous row expansion

This is common in cases where you need to expand the snippet consecutively, such as two snippets, one to print variables and one to handle JSON serialization. You need to use the in-word option

Use the re snippet

Usually when you write code you need to use log, print, etc to print variables in context. Using a normal fragment to display console.log() by cl and then copying the variable characters into parentheses can be complicated. A good way to solve this problem is to use the re to dynamically match the preceding characters

snippet "([^\s]\w+)\.log" "console.log(postfix)" r
console.log(`! p snip.rv = match.group(1) `) $0
endsnippet
snippet "([^\s].*)\.upper" "Uppercase(postfix)"r `! p snip.rv = match.group(1).upper()`$0
endsnippet
snippet "([^\s]\w+)\.lower" "Lowercase(postfix)"r `! p snip.rv = match.group(1).lower()`$0
endsnippet
Copy the code

Dynamic graph demonstration

Note: The regular snippet is only suitable for single-line text processing, and the python + VISUAL snippet below is used for multi-line conversion

Use the Python interpreter + VISUAL mode for code annotation

Often, you need to use a bunch of plug-ins to annotate your code. However, Ultisnips provides VISUAL mode to extract the selected content from vim VISUAL mode into the snippet, so we can combine it to make a snippet with annotations

The process goes something like this:

  1. Enter vim visual mode and select what you want to annotate
  2. Press TAB to clear the selection
  3. Enter the code snippet trigger character and press TAB to complete

Because the Python code implemented is relatively complex, it is mainly divided into two methods. Single-line and multi-line comments. Note that Ultisnips allows you to write Python directly, but large sections of methods are recommended to be placed in the pythonx directory under the plug-in directory, when used in the global Python code in the corresponding snippet. I can just introduce p

Single-line comment (pythonx/javascript_snippets.py) :

def comment(snip, START="", END=""):
    lines = snip.v.text.split('\n')[:- 1]
    first_line = lines[0]
    spaces = ' '
    initial_indent = snip._initial_indent

    # Get the first non-empty line
    for idx, l in enumerate(lines):
        ifl.strip() ! =' ':
            first_line = lines[idx]
            sp = re.findall(r'^\s+', first_line)
            if len(sp):
                spaces = sp[0]
            break

    # Uncomment
    if first_line.strip().startswith(START):
        result = [line.replace(START, "".1).replace(END, "".1) if line.strip() else line for line in lines]
    else:
        result = [f'{spaces}{START}{line[len(spaces):]}{END}' if line.strip() else line for line in lines ]

    # Remove initial indent
    if result[0] and initial_indent:
        result[0] = result[0].replace(initial_indent, ' '.1)

    if result:
        return '\n'.join(result)
    else:
        return ' '
Copy the code

Multi-line comments:

def comment_inline(snip, START="/* ", END=" */"):
    text = snip.v.text
    lines = text.split('\n')[:- 1]
    first_line = lines[0]
    initial_indent = snip._initial_indent
    spaces = ' '

    # Get the first non-empty line
    for idx, l in enumerate(lines):
        ifl.strip() ! =' ':
            first_line = lines[idx]
            sp = re.findall(r'^\s+', first_line)
            if len(sp):
                spaces = sp[0]
            break

    if text.strip().startswith(START):
        result = text.replace(START, ' '.1).replace(END, ' '.1)
    else:
        result = text.replace(spaces, spaces + START, 1).rstrip('\n') + END + '\n'

    if initial_indent:
        result = result.replace(initial_indent, ' '.1)

    return result
Copy the code

Code snippet definition:

global! p from javascript_snippets import ( comment, comment_inline ) endglobal # ... snippetc "Toggle comment every single line"`! p snip.rv = comment(snip, START='// ', END=' ') ` $0
endsnippet

snippet ci "Toggle comment inline."`! p snip.rv = comment_inline(snip, START="/* ", END=" */") ` $0
endsnippet
Copy the code

Dynamic graph demonstration

Different languages can define and pass comment notation parameters in the corresponding fragment file. With this feature, you can happily delete other vim annotation plugins 😀