I like collecting GNU+Linux ISOs. I’d like to think I have a pretty serious collection of these things. I have a pretty good and stable Internet connection, so I collect them in my torrent client, and let them seed pretty much forever, so other people can enjoy them too.
For this hobby, I’m using Transmission, with
tremc as the frontend. I’ve been using it for
a while, but there’s always been a small thing bothering me. Whenever you get a
prompt to specify the name of the torrent, or the directory to which its
contents should be downloaded,
^w immediately kills the entire application.
By regular shell standards, I’m used to
^w not killing the application, but
just removing from the cursor to the start of the previous word. I don’t often
change the names of the torrents, but when I do, I often use
^w to quickly
remove one or two words in the path. With tremc, this sadly means me killing the
application, being upset for a little while as I restart it, and hold down the
backspace for a while to get the intended effect.
Until last night. I set out to read the documentation to fix this issue once and
for all. I dug into the manual, which specifies that a configuration file at
~/.config/tremc/settings.cfg is read at startup. However, the manual
doesn’t specify anything about the format of this file. There’s also no other
manual pages included in the package.
The Gentoo package specifies a homepage for this project on Github, so I open up
my least-hated browser and see if there’s anything there. Good news, the
repository contains a sample
settings.cfg, so I can read that and get an idea
on how to do keybinds. And the examples do show how to set keybinds. But it
doesn’t seem to be able to remove keybinds. This is kind of a bummer. Setting
a keybind to an empty value didn’t seem to do the trick either. This leaves only
one option, patching the defaults.
This was actually pretty straightforward, just look for the list of default keybinds, and remove a single line.
@@ -222,7 +222,6 @@ class GConfig: # First in list: 0=all 1=list 2=details 3=files 4=tracker 16=movement # +256 for RPC>=14, +512 for RPC>=16 'list_key_bindings': [0, ['F1', '?'], 'List key bindings'], - 'quit_now': [0, ['^w'], 'Quit immediately'], 'quit': [1, ['q'], 'Quit'], 'leave_details': [2, ['BACKSPACE', 'q'], 'Back to torrent list'], 'go_back_or_unfocus': [2, ['ESC', 'BREAK'], 'Unfocus or back to torrent list'],
Since I’m just testing, and this program is a single Python file, I just edit
the file in-place, and delay making a proper patch out of for later. Restarting
tremc, going to the new torrent, pressing
m (for “move”), and hitting
try and remove a single word, hoping for a quick and easy fix, I was met with
tremc just quitting again. This was not what I wanted.
So opening up the file again with everyone’s favourite editor, I search around
quit_now function. Surely that’ll bring me closer? The
string doesn’t seem to be used anywhere else, apart from the function that
defines what the keybind action should do,
action_quit_now. This seems to
simply defer to
exit_now, which leads me to a bit of code with a special case
exit_now if a certain character is detected. This character appears to be a
^w, which is exactly what I’m trying to stop. So, let’s patch that out too.
@@ -3760,10 +3759,7 @@ class Interface: self.update_torrent_list([win]) def wingetch(self, win): - c = win.getch() - if c == K.W_: - self.exit_now = True - return c + return win.getch() def win_message(self, win, height, width, message, first=0): ypos = 1
Restart tremc and test again. Still exiting immediately upon getting a
This bit of code gives me some new insights, it appears
K.W_ is related to the
keycode of a
^w. So I continue the search, this time looking for
appears to be used later on to create a list of characters which should act as
esc in certain contexts. Removing the
K.W_ from this is simple enough.
@@ -5039,7 +5047,7 @@ def parse_config_key(interface, config, gconfig, common_keys, details_keys, list else: gconfig.esc_keys = (K.ESC, K.q, curses.KEY_BREAK) gconfig.esc_keys_no_ascii = tuple(x for x in gconfig.esc_keys if x not in range(32, 127)) - gconfig.esc_keys_w = gconfig.esc_keys + (K.W_,) + gconfig.esc_keys_w = gconfig.esc_keys gconfig.esc_keys_w_enter = gconfig.esc_keys_w + (K.LF, K.CR, curses.KEY_ENTER) gconfig.esc_keys_w_no_ascii = tuple(x for x in gconfig.esc_keys_w if x not in range(32, 127))
Restart, test, and… Nothing. This is an improvement, the program isn’t exiting
immediately, it just does nothing. I know that
^u works as I expect, so
perhaps it needs some love to have
^w work properly as well. I search for
K.U_, and indeed, there is code dedicated to this keypress, in a long
construct. So I add some code to get
^w working as well. After a bit of
fiddling, and realizing I’ve spent way too much time on adding a torrent, I’ve
settled on this little bit of love.
@@ -3957,6 +3953,18 @@ class Interface: # Delete from cursor until beginning of line text = text[index:] index = 0 + elif c == K.W_: + # Delete from cursor to beginning of previous word... mostly + text_match = re.search("[\W\s]+", text[::-1]) + + if text_match.span() == 0: + # This means the match was found immediately, I can't be + # bothered to make this any nicer. + text = text[:-1] + index -= 1 + else: + text = text[:-text_match.span()] + index -= text_match.span() elif c in (curses.KEY_HOME, K.A_): index = 0 elif c in (curses.KEY_END, K.E_):
It looks for the first non-word character or a space, starting from the end of
the string (
[::-1] reverses a string in Python). The resulting
can tell me how many characters I need to delete in the
.span() value. A
small exception is created if that value is
0, otherwise the logic below
doesn’t work well.
It’s not perfect, but it gets the job done well enough, and I don’t like Python enough to spend more time on it than I’ve already done. I am open for better solutions that work better, though!