summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
author44274505+ericricky@users.noreply.github.com <44274505+ericricky@users.noreply.github.com>2018-11-09 14:33:50 -0600
committertoonn <toonn@toonn.io>2021-01-16 21:30:14 +0100
commit95d8f9ba814f9723e2c0273202aaa2fc3e86b3c2 (patch)
treec4dbe3deb50ca53b83cffa4f60b08bcba1ed2ea9
parent9ac5cceab5863f36b4f8cdd9ff4f0769072237d8 (diff)
Add word/character transposition at the console
Inspired by emacs/readline.
-rw-r--r--ranger/config/rc.conf2
-rw-r--r--ranger/gui/widgets/console.py75
2 files changed, 77 insertions, 0 deletions
diff --git a/ranger/config/rc.conf b/ranger/config/rc.conf
index d7d79f97..dc6c7717 100644
--- a/ranger/config/rc.conf
+++ b/ranger/config/rc.conf
@@ -671,6 +671,8 @@ cmap <A-d> eval fm.ui.console.delete_word(backward=False)
cmap <C-k> eval fm.ui.console.delete_rest(1)
cmap <C-u> eval fm.ui.console.delete_rest(-1)
cmap <C-y> eval fm.ui.console.paste()
+cmap <C-t> eval fm.ui.console.transpose_chars()
+cmap <A-t> eval fm.ui.console.transpose_words()
# And of course the emacs way
copycmap <ESC> <C-g>
diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py
index 88a59136..a6d9117c 100644
--- a/ranger/gui/widgets/console.py
+++ b/ranger/gui/widgets/console.py
@@ -439,6 +439,81 @@ class Console(Widget): # pylint: disable=too-many-instance-attributes,too-many-
self.line = left_part + ''.join(uchar[upos + 1:]).encode('utf-8', 'ignore')
self.on_line_change()
+ def transpose_subr(self, line, x, y):
+ # Transpose substrings x & y of line
+ # x & y are tuples of length two containing positions of endpoints
+ if not (x[0] <= x[1] <= y[0] <= y[1] <= len(line)
+ or x[0] == y[0] <= x[1] == y[1] <= len(line)):
+ self.fm.notify("Tried to transpose invalid regions.", bad=True)
+ return line
+
+ word_x = line[x[0]:x[1]]
+ word_y = line[y[0]:y[1]]
+ diff = len(word_y) - len(word_x)
+
+ line_begin = line[0:x[0]]
+ line_end = line[x[1]:]
+
+ line = line_begin + word_y + line_end
+
+ line_begin = line[0:(y[0] + diff)]
+ line_end = line[(y[1] + diff):]
+
+ line = line_begin + word_x + line_end
+ return line
+
+ def transpose_chars(self):
+ if self.pos == len(self.line):
+ x = max(0, self.pos - 2), max(0, self.pos - 1)
+ y = max(0, self.pos - 1), self.pos
+ else:
+ x = max(0, self.pos - 1), self.pos
+ y = self.pos, min(len(self.line), self.pos + 1)
+ self.line = self.transpose_subr(self.line, x, y)
+ self.pos = y[1]
+ self.on_line_change()
+
+ def transpose_words(self):
+ # Interchange adjacent words at the console with Alt-t
+ # like in Emacs and many terminal emulators
+ if self.line:
+ # If before the first word, interchange next two words
+ if not re.search(r'[\w\d]', self.line[:self.pos], re.UNICODE):
+ self.pos = self.move_by_word(self.line, self.pos, 1)
+
+ # If in/after last word, interchange last two words
+ if re.match(r'[\w\d]*\s*$', self.line[self.pos:], re.UNICODE):
+ self.pos = self.move_by_word(self.line, self.pos, -1)
+
+ # Util function to increment position until out of word/whitespace
+ def _traverse(line, pos, regex):
+ while pos < len(line) and re.match(
+ regex, line[pos], re.UNICODE):
+ pos += 1
+ return pos
+
+ # Calculate endpoints of target words and pass them to
+ # 'self.transpose_subr'
+ x_begin = self.move_by_word(self.line, self.pos, -1)
+ x_end = _traverse(self.line, x_begin, r'[\w\d]')
+ x = x_begin, x_end
+
+ y_begin = self.pos
+
+ # If in middle of word, move to end
+ if re.match(r'[\w\d]', self.line[self.pos - 1], re.UNICODE):
+ y_begin = _traverse(self.line, y_begin, r'[\w\d]')
+
+ # Traverse whitespace to beginning of next word
+ y_begin = _traverse(self.line, y_begin, r'\s')
+
+ y_end = _traverse(self.line, y_begin, r'[\w\d]')
+ y = y_begin, y_end
+
+ self.line = self.transpose_subr(self.line, x, y)
+ self.pos = y[1]
+ self.on_line_change()
+
def execute(self, cmd=None):
if self.question_queue and cmd is None:
question = self.question_queue[0]