I’m writing a function that edits a certain environment in LaTeX.
The environment basically looks like this:
\begin{quicktikz}
...some stuff...
\end{quicktikz}
or like this:
\begin*{quicktikz}
...some stuff...
\end{quicktikz}
I want to write a function that toggles between the two, when called from within the environment. Since my Vim knowledge ain’t all that, I’m coming up with a simple solution:
- Get cursor position with
let save_cursor=getpos(".")
. - Backward search for
\be开发者_JS百科gin{quicktikz}
using:?\\begin{quicktikz}\|\\begin\*{quicktikz}
. - Search for the
{
and move left using:normal 0f{h
. - Check if the item under cursor equals
*
:- if it does, do
normal x
; - if it doesn’t, do
normal a*<esc>
.
- if it does, do
- Restore cursor position using
call setpos('.',save_cursor)
.
I know how to do all of this except for step 3. How can I check if the char under the cursor equals to *
or not?
If you know a better way of doing this, sharing this would be welcome.
I think the easiest way to retrieve the char under cursor is:
getline(".")[col(".")-1]
Alternatively, you can do it with strpart()
strpart(getline("."), col(".")-1, 1)
The first expression first calls the function getline()
passing "."
as
argument which means the line where the cursor is positioned will be returned.
Then we use the so called expr8 or expr-[] (see the help) to retrieve a
single character. The number passed comes from another function, col()
which returns the current cursor column. As indexes start in 0, one is subtracted.
You can use it like
if getline(".")[col(".")-1] == '*'
...
Let me propose an alternative implementation of the technique you describe.
:?\\begin\>\zs\*\=?s//\='*'[submatch(0)!='']/|norm!``
The above command consists of two separate commands chained with |
(see
:help :bar
) in one line. The first one is a substitution (see :help :s
)
performed for each line in the specified range,
?\\begin\>\zs\*\=?
According to the range syntax (see :help :range
), this range specifies the
only line, that is the previous line where the \\begin\>\zs\*\=
pattern
matches the word begin
preceded with a backslash and followed by by optional
star character.1 The \zs
atom between parts of the pattern
matching \begin
and *
, sets the start of the match there. So, the match
of the whole pattern is either empty or contains single star character. This
is not necessary for specifying a line in the range, it is useful for reusing
the same pattern later in the :substitute
command, where only that star
character or its empty place should be replaced. For details about the
pattern's syntax see :help /\>
, :help /\=
, :help /\zs
.
The substitution itself,
s//\='*'[submatch(0)!='']/
replaces the first occurrence of the last search pattern (which is set by the
backward search in the range) with a string to which the expression
'*'[submatch(0)!='']
evaluates (see :help sub-replace-\=
). As the pattern
matches only an empty string or a star character, the subexpression
submatch(0)!=''
evaluates to zero if there is no star after \begin
, or to
one otherwise. Zero subscript from the string '*'
results in a substring
containing the first character of that one-character string. Index one is
equal to the length of the string, therefore subscript results in an empty
string. Thus, when there is a star after \begin
, it gets replaced with an
empty string, when a star is not present, zero-width interval just after
\begin
is substituted with *
.
The second command,
:norm!``
takes advantage of the fact that :substitute
command stores the current
cursor position before it actually starts replacement. The ``
movement
command jumps back to the position before the latest jump (which occurs in the
aforementioned substitution command) restoring position of the
cursor.2
1 Be careful with search, since in ranges, as usual, it wraps
around the end of file, when the wrapscan
option is enabled (it is turned on
by default).
2 Do not confuse ``
with the ''
command which moves the
cursor to the first non-blank character in the line of the location before the
latest jump.
精彩评论