-
Notifications
You must be signed in to change notification settings - Fork 6
Useful Macros
Here you can put SciTECO macros that are neither suitable for the macro library that ships with SciTECO, nor for inclusion into the SciTECO language itself. These are typically macros you would add to your SciTECO profile (~/.teco_ini
).
Nevertheless you will find yourself writing many small macros for everyday use.
If you catch yourself copying these macros over and over again, why not share them here?
Some of the macros here might one day grow into a part of the standard library.
When adding a macro to this page, please
- add a subheading per macro, explaining its parameters and semantics
- insert the macro as a code block
- avoid using unprintable characters, i.e. write
^A
instead of ASCII code 1, avoid using the default escape string termination, etc. - make sure the macro is valid SciTECO code, so it can be pasted into a SciTECO script file directly
- try to avoid platform-dependencies and undesired side-effects, e.g. the search/replace registers should be backed up if you modify them, etc.
Warning: The macros on this page might require a recent Git revision of SciTECO. The existing SciTECO release is very outdated. In other words, you will have to build SciTECO yourself to use them.
The following macro implements indentation of code blocks.
- The first argument it pops from the stack is the number of lines to indent.
indentation is done according to Scintilla's indentation policy, i.e. by using
SciTECO's
^I
command. Empty lines are left empty. Negative values remove one level of indentation, handling hard as well as soft tabs. One is implied if the argument is missing. - The second argument is a repeat count (default is 1).
For instance, 10M#it
will indent the next 10 lines.
@^U#it{[:
"~1'U.i "~1'U.c
Q.i">
Q.i<
0A-10"N 0A-13"N Q.c<@^I//> ''
:L;>
|
-Q.i< Q.c<
0A-9"=D | @ES/GETTABWIDTH//<0A-^^ "N1;'D> '
> :L;>
'
]:}
Suppose you have an APL keyboard layout, you might want to define macros M→
and M←
for right and
left indentation respectively:
@^U→{M#it}
@^U←{"~1'*-1M#it}
To further simplify calling these macros, you might want to define the following key macros in the "start" state:
@^U[^K→]{m→} 1^_U[^K→]
@^U[^K←]{m←} 1^_U[^K←]
This exposes the indentation as a single keypress of "→" or "←".
The following macro M{
takes a line or character range (analogous to Xq
) and stashes the given part of the buffer into a temporary Q-register.
Dot is left at the beginning of this Q-register.
When called without arguments, M{
takes the entire last matched pattern (as by ^Y
).
0U[structs.len]
@^U[{]{
"~ ^YU.tU.f |
U.t "~ .U.f ^E@ES/LINEFROMPOSITION//+Q.t@ES/POSITIONFROMLINE//^EU.t | U.f ' '
Q.fU[structs.^E\[structs.len].from] Q.tU[structs.^E\[structs.len].to]
Q.f,Q.tX[structs.^E\[structs.len]] @EQ[structs.^E\[structs.len]]//
%[structs.len]$
}
The user is supposed to perform edit operations on this temporary buffer and eventually call the macro M}
,
which takes no arguments:
@^U[}]{
-%[structs.len]"=
Q*U*
|
-%[structs.len]$ @EQ[structs.^E\[structs.len]]// %[structs.len]$
'
.U..
Q[structs.^E\[structs.len].from]J
.-Q.."< :Q[structs.^E\[structs.len]]-Q[structs.^E\[structs.len].to]+Q[structs.^E\[structs.len].from]%..$ '
Q[structs.^E\[structs.len].from],Q[structs.^E\[structs.len].to]D
G[structs.^E\[structs.len]]
Q..J
!:FQ[structs.^E\[structs.len].] FQ[structs.^E\[structs.len]]!
}
M}
replaces the buffer range - specified by M{
- with the contents of the temporary Q-register buffer.
Calls to M{
and M}
can be nested.
Code between M{
and M}
can be read as "to be applied on the given buffer range".
This construct is particularly useful in situations where operations cannot easily be bound to a given range, especially search-replace operations.
For instance 3M{ <@FR/^EM^ED/0/;> M}
will replace all integers with "0", but only in the next 3 lines, regardless of the fact that the search-replace operation can delete characters. This would be very cumbersome to script with a per-line loop.
These macros are especially powerful after plain search commands (S
or N
) as the range to be extracted and edited defaults to the last search result.
This effectively brings the power of structural regular expressions, as pioneered by the editor Sam, to SciTECO.
For instance, <@S/{^EM^N}}/; M{ <@FR/^EM^ED/0/;> M}>
will replace all integers with "0", but only within all braces.
This could easily be adapted to perform operations on all balanced braces, etc.
You can also add reverse conditionals to restrict which matches are processed thanks to the power of the ^Y
command.
For instance, <@S/{^EM^N}}/; ^Y:@S/foo/"F M{ <@FR/^EM^ED/0/;> M} '>
will replace integers with "0" in all braces, that don't contain the string "foo". Note that a failing search will not overwrite the buffer ranges returned by ^Y
.
-
FIXME: This does not currently work when starting with the first
M{
while editing a Q-Register. The correspondingM}
will return to the last edited file buffer instead. We cannot currently query and store which Q-Register is edited and not even whether any Q-Register is edited at all. -
FIXME: This may leak global Q-registers until we implement the
FQ
(forget Q-Register) command. But I don't expect this to be a problem in practice.
This is a wrapper for SciTECO's built-in clipboard support including a fallback for platforms where SciTECO does not support clipboards natively. This is especially useful on ncurses, as SciTECO only supports clipboard operations for the XTerm terminal emulator. It allows you to copy buffer contents into the main/default system clipboard or paste the clipboard at the current position.
It requires the xclip tool to be installed for the fallback case. Your Linux distribution probably has a package for xclip
.
- Without arguments, the current clipboard is pasted
- With one argument, a range of lines is copied to the clipboard.
Two arguments select a character range to be copied into the clipboard.
The arguments are passed through to the
X
orEG
commands in case you wonder. This form of the macro does not change the current document.
If clipboards are supported natively, rubbing out the macro call will indeed restore the original clipboard contents. This does not happen when the xclip
fallback has to be used.
@^U#xc{
:Q~">
"~ G~ | X~ '
|
"~
@EC'xclip -selection clipboard -out'
|
! EG will consume arguments on stack !
@EG.n'xclip -selection clipboard -in >/dev/null'
'
'
}
The following macro - invoked m#cf
- wraps ClangFormat to format a block of code according to various coding styles. Dot is always left at the equivalent position in the code after the formatting procedure. The binary is expected to be called clang-format17
, although this can be changed easily in the macro's definition. The macro also expects jq to be in the executable PATH - it is used for parsing JSON emitted by ClangFormat. There should be packages for both tools for your favorite Linux distribution and the macro should work on Windows and obscure systems as well.
The coding style to use is specified by the global register cf.style
(corresponding with ClangFormat's -style
option). By default, a file .clang-format
is looked up in all the parent directories to determine the settings, allowing you to add .clang-format
files on a per-project basis. Using this scheme, default settings could for instance be placed in ~/.clang-format
.
- With one argument, a block of code beginning at the current buffer position spanning a number of lines is reformatted. So
1M#cf
formats the next line,0M#cf
formats the current line etc. - With two arguments a block of code specified by a character range is formatted.
In other words, the semantics are compatible e.g. with the K
or X
commands.
@^U[cf.style]'file'
@^U#cf{
U.l "~
!* line range *!
^EU.[offset] ^E@ES/LINEFROMPOSITION//+Q.l@ES/POSITIONFROMLINE//-^EU.[length]
Q.[length]"< Q.[length]%.[offset]$ -1*Q.[length]U.[length] '
|
!* char range *!
^EU.[offset] Q.l^E-Q.[offset]U.[length]
'
^EU.[cursor]
:Q*"> @EU.[filename]'-assume-filename=^E@*' | @^U.[filename]'' '
!* Disable EOL-conversions and enable UNIX98 shell emulation *!
EDU.e 16,128ED
H@EC'clang-format17 -offset=^E\.[offset] -length=^E\.[length] -cursor=^E\.[cursor] ^EQ.[filename] -style=^E@[cf.style]'
!* first line contains new cursor (JSON-encoded) *!
J 1@EC'jq -r .Cursor' J\U.[cursor] K
Q.[cursor]:^EJ
Q.eED
}
This macro can be used to annotate the current buffer with commit information akin to git-blame. The shortened commit that changed a line will be shown in the margin next to the line number.
Since SciTECO currently does not support margin setups via built-in commands, but only via ES
,
this will not undo the margins when being rubbed out.
You can call -M[git.blame]
to hide the corresponding margins.
@^U[git.blame]{
"~1'"S
!* Disable blame margins *!
3@ES/SETMARGINWIDTHN//$
4@ES/SETMARGINWIDTHN//$
$$
'
HX.b
!* Disable EOL-conversions and enable UNIX98 shell emulation *!
EDU.e 16,128ED
[*[_[$
@EQ*'' ZJ -@S'/' .,ZX.#bn 0,.X$
@EQ.b'' H@EC'git blame --incremental --contents - ^E@.#bn'
!* Shorten _some_ hash to get a minimal display size, taking Git settings into account.
This size should be sufficient for all other hashes as well. *!
@EG.[HEAD]'git rev-parse --short HEAD'
J< .-Z;
.U.h 2WC\U.s WC\U.l
Q.l< Q.h,Q.h+:Q.[HEAD]-1X.[margin.^E\.s] %.s>
@S'^Jfilename ' L
>
]$]_]*
Q.eED
4,3@ES/SETMARGINTYPEN//$
33@ES/TEXTWIDTH/9/U.w
(:Q.[HEAD]-1)*Q.w,3@ES/SETMARGINWIDTHN//$
Q.w,4@ES/SETMARGINWIDTHN//$
1U.s <
:Q.[margin.^E\.s]:;
Q.s-1@ES/MARGINSETTEXT/^EQ.[margin.^E\.s]/$
%.s>
}
TODO: If we do something analogous for Subversion and Mercurial, it might be a prime candidate for adoption
into the standard library.
It already contains some VCS integration in sessions.tes
.
The following macro allows spell checking via aspell.
Unknown words, which are not in the aspell.dicts
dictionaries, are underlined with squiggles on Gtk or highlighted on Curses.
The aspell.styles
registers restrict spell checking to individual lexer states
based on the numeric lexer id.
Currently, it must always be invoked manually, ie. by typing M#sp
.
Use -M#sp
to clear all spell checking indicators.
@^U[aspell.styles.2]"1,12" !* python: all comments *!
@^U[aspell.styles.3]"1,2" !* cpp: all non-doc comments *!
@^U[aspell.styles.15]"1,2,3" !* lua: all comments *!
@^U[aspell.styles.137]"0,5" !* troff: text and string arguments *!
@^U[aspell.dicts]"en"
@^U#sp{
"~1'U.s
2@ES/SETINDICATORCURRENT//$
0EJ-1"=8|13',2@ES/INDICSETSTYLE//$
:^E,0@ES/INDICATORCLEARRANGE//$
Q.s"S $$ '
@ES/GETLEXER//U.#lx
:Q[aspell.styles.^E\.#lx]-1"<
!* spellcheck everything *!
HX.b [* @EQ.b// J<@EI"^" :L;> ]*
|
!* spell check per style *!
:M[aspell.styles.^E\.#lx],-1:<"~1;'U.s (-U.[styles.^E\.s])>
!* Make sure that everything is already styled as we spell check per style: *!
:^E,0@ES/COLOURISE//$
[: J @^U.b"^"
< .-Z"=1;' 0A-10"=
:@EU.b"^J^^"
|
^E@ES/GETSTYLEINDEXAT//U.s
:Q.[styles.^E\.s]"F 0A | 32 ':@^U.b//
' :C;>
]:
'
[* @EQ.b//
!* Disable EOL-conversions and enable UNIX98 shell emulation *!
EDU.e 16,128ED
H@EC"aspell -d ^E@[aspell.dicts] --dont-guess --dont-suggest --byte-offsets -a"
Q.eED
J < .-Z"=1;'
0A-10"=
%.l$
| 0A-^^#"= !* word not found *!
2C ^EU.w W ^E-Q.wU.w C\U.o
]*
Q.w,(Q.l@ES/POSITIONFROMLINE//+Q.o-1)@ES/INDICATORFILLRANGE//$
[* @EQ.b//
' '
:L;>
]*
}
TODO: It should be possible to invoke the spell checking macro automatically, at least after buffers are dirtified. Still, even then, it would not be undoable unless we add proper SciTECO commands for manipulating the indicators. This macro can also be significantly optimized.
The following macro - invoked m#mk
- simply runs make
and saves its output (both stdout and stderr) in the global Q-Register "make.output". If make
fails (usually caused by a compilation error), "make.output" is made the currently edited Q-Register so the errors are brought to your attention.
Since SciTECO's current working directory is seldom the directory you want make
to run in (and look for Makefiles), this macro runs make
in the directory specified by the global Q-Register "make.dir". This is useful when building out-of-tree since there is no way to automatically determine the directory you want make
to use. If "make.dir" is empty or undefined (the default), this macro will run make
in the directory of the current buffer instead, which should be fine for in-tree builds most of the time. If not set @EU[make.dir]'^EQ$'
or hack the macro :-). You could even make "make.dir" persistent per buffer session by saving it in a file alongside .teco_session
(see sessions.tes
)...
@^U#mk{ [$
:Q[make.dir]">
@FG'^EQ[make.dir]'
|
[_ @EQ*'' ZJ -:@S'/'"S 0,.X$ ' ]_
'
@EQ[make.output]'' HK
:@EC'make 2>&1'"S Q*U* '
]$ }
The following macro closes the current XML tag "automatically" taking XML comments and CDATA-sections into account. I doubt however that it implements all the lexical rules of XML.
The macro takes no arguments and can be invoked by calling M#</
.
@^U#</{
[_ .U.i
<
-@S'^N/>';
!* skip CDATA sections *!
3R ::@S']]>'"S -@S'<![CDATA[' F< '
-@S'<'
!* skip comments *!
0A-^^!"= F< '
0A-^^/"N-'%.l"<
.(@S'^E[ ,^I,>]'R.)X.n 1;
'
>
Q.iJ :Q.n"> @I'</^EQ.n>' '
]_
}
Here's a variant of the same functionality as a command line editing macro.
Whenever you type M#</
, the macro call at the end of the command line will be replaced with the insertion of the closing XML tag. If no appropriate tag could be found, it just rubs out the macro call.
@^U#</{
<
-@S'^N/>';
!* skip CDATA sections *!
3R ::@S']]>'"S -@S'<![CDATA[' F< '
-@S'<'
!* skip comments *!
0A-^^!"= F< '
0A-^^/"N-'%.l"<
.(@S'^E[ ,^I,>]'R.)X.n 1;
'
>
{ -3D
:Q.n"> @I'I</^EQ.n>^[' '
}
}
Jumps to the end of the next "function" - actually just to the end of the next pair of balanced curly braces. This is more or less sufficient in many languages that use curly braces for code blocks.
@^U#sf/ [_ (
<@S"^E[{,}]" -A-^^{"=1|-'%.n"=1;'>
) ]_ /
This macro is useful for finding the next line that exceeds 80 (display) characters - in case you want to break it into smaller lines.
It leaves dot at the next best 81st column it finds.
Note that Scintilla also allows you to set up visual hints to highlight long lines using SCI_SETEDGEMODE
and related messages.
@^U#lc{
<LR ^E@ES/SCI_GETCOLUMN//-80">
81,(^E@ES/SCI_LINEFROMPOSITION//)@ES/SCI_FINDCOLUMN//:^EJ 1;
' L>
}
Deletes the next camel-cased word beginning at the current buffer position.
E.g. if dot points to fooBar
, it will delete foo
.
@^U#nv{
(<:D; 0A"W1;'>)
}
A command-line editing macro that when executed, compresses a sequence of repeating characters at the end of the command-line into a number followed by that character.
It rubs out itself in the process. For instance if you have typed cccccccccc
and then type m#cm
, the c's are compressed to 10c
.
@^U#cm{{
-3D -AU.c <."=1;' -A-Q.c"N1;' -D %.n> Q.n\ Q.c@I//
}}