[TxMt] A fix for Move Selection Line Up/Down and suggestions for TextMate
Сергей Яковлев
sergei.yakovlev at gmail.com
Wed Dec 26 13:04:19 UTC 2007
Hi!
A well-known problem with Move Selection Line Up/Down commands
(Control-Command-Up/Down) is
that if you use them without selection (which means "move current
line"), the cursor ends
up in the wrong place. This has been bugging me and my friends for
quite some time, so I
wrote a fix. It's two macros overriding default key equivalents,
which you can find
attached to the bug description at http://macromates.com/ticket/show?
ticket_id=58C52785 .
Please tell if this works for you.
Also, is it possible to put this fix into some standard TextMate
bundle like Text.tmbundle,
so that all TextMate users enjoy it? (Until it is fixed in the new
engine of TextMate 2.)
What is the best way to do it?
This is my first experience in TextMate programming, and several
times I've been stuck in
various dead-ends. What follows is a somewhat long description of how
I approached the
problem along with some suggestions on making TextMate easier to
program. Most people would
like to skip this.
So, my first (rather silly) attempt was a huge Ruby program which
replicated functionality
of Move Selection Line Up/Down and depended on my assumption that the
cursor is always at
the end of selection (potentially allowing me to calculate all the
necessary offsets in the
file). Well, it turned out that my assumption was simply not true, as
the cursor can be
anywhere inside the selection, depending on how you've selected the
text.
Suggestion 1. It would be useful if TextMate had variables like
$TM_SELECTION_START and
$TM_SELECTION_END which would return the corresponding offsets in
the file (or the
cursor offset, if nothing is selected).
On a second attempt, I decided that I should instead go with the
built-in commands, simply
correcting the cursor position for the special case when nothing is
selected. So we
remember current cursor position, call Move Selection Line Up/Down,
and move the cursor
back to remembered position if nothing is selected. There are two
problems here. First, you
can only call built-in commands from macros, and it is not possible
to remember values
through the steps of a macro. Second, there is no direct way to
reposition the cursor. One
indirect way to achieve this is to insert a snippet with a $0
variable. However, to move
the cursor to arbitrary position in this fashion, we need an output
option like "Replace
Document as Snippet", which is not available. Because of these two
problems, I decided to
use TMTOOLS plugin, which has "call macro" (for calling arbitrary
macros and built-in
commands from commands) and "set caretTo" (for setting the cursor
position). I got a
working solution, but it had a delay of 0.5 to 1 s, which was too
slow to be useful.
Suggestion 2. Add a way to set "global" environment variables
programmatically (from
commands). These "global" variables should be available to all
subsequently called
commands (and other bundle items) until explicitly unset.
Suggestion 3. Replace output options "Insert as Text" and
"Insert as Snippet" with just
one option, "Insert", and add a checkbox "as Snippet" to the
right. This would add such
useful options as "Replace Selected Text as Snippet", "Replace
Document as Snippet" and
"Create New Document as Snippet" without making the list of
options too long.
So, on a third (and final) attempt, I dropped TMTOOLS and returned to
the idea of a macro.
Inspired by Duane Johnson's solution for multiple insertion points, I
decided to use a
special symbol (mark) to "remember" the initial position of the
cursor. So we insert the
mark at the cursor (if nothing is selected), call Move Selection Line
Up/Down, and replace
the mark with empty string (if nothing is selected), which moves the
cursor there as a
side-effect. The problem here is that Replace command always replaces
current selection
with replace buffer, even if it doesn't match the find buffer, which
ruins the case when
something has already been selected in the beginning. Replace All
also ruins current
selection, moving the cursor to the beginning of the file even if it
has not found any
matches. Fortunately, the Find Next/Previous command will only change
current selection if
the match has been found. So that's what we use. If there is a mark
in the text, it will
get selected, otherwise, the selection won't change. Finally, the
selection is replaced
with an empty string if what's selected is a mark. The wrong cursor
position after Move
Selection Line Up/Down is always further in text than the right
position (actually, it's on
the next line), so we use Find Previous command to perform the
search. Because of this
little detail, the solution is actually pretty fast.
Suggestion 4. Replace All should not alter current selection if
no matches are found.
The only remaining question is what symbol to use as a mark. I
decided to use à (221A,
SQUARE ROOT character, which you can type with Option-V), but I'm not
sure this is the best
choice. Maybe it's better to use some character from Private Use Area
(E000--F8FF)?
Best regards,
Sergei Yakovlev
More information about the textmate
mailing list