From 95d8f9ba814f9723e2c0273202aaa2fc3e86b3c2 Mon Sep 17 00:00:00 2001 From: "44274505+ericricky@users.noreply.github.com" <44274505+ericricky@users.noreply.github.com> Date: Fri, 9 Nov 2018 14:33:50 -0600 Subject: Add word/character transposition at the console Inspired by emacs/readline. --- ranger/config/rc.conf | 2 ++ ranger/gui/widgets/console.py | 75 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) 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 eval fm.ui.console.delete_word(backward=False) cmap eval fm.ui.console.delete_rest(1) cmap eval fm.ui.console.delete_rest(-1) cmap eval fm.ui.console.paste() +cmap eval fm.ui.console.transpose_chars() +cmap eval fm.ui.console.transpose_words() # And of course the emacs way copycmap 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] -- cgit v1.2.3 From dd41a90d63d9ad8268f231ad8bf6be9a24387aa9 Mon Sep 17 00:00:00 2001 From: toonn Date: Sat, 16 Jan 2021 20:46:59 +0100 Subject: transpose_subr: Check lower bound Allowing negative regions makes it harder to verify there's no overlap between regions. --- ranger/gui/widgets/console.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py index a6d9117c..6c184395 100644 --- a/ranger/gui/widgets/console.py +++ b/ranger/gui/widgets/console.py @@ -442,8 +442,8 @@ class Console(Widget): # pylint: disable=too-many-instance-attributes,too-many- 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)): + if not (0 <= x[0] <= x[1] <= y[0] <= y[1] <= len(line) + or 0<= x[0] == y[0] <= x[1] == y[1] <= len(line)): self.fm.notify("Tried to transpose invalid regions.", bad=True) return line -- cgit v1.2.3 From 4ecb0fc04409848cd1385ce8932000e1c67a3dd1 Mon Sep 17 00:00:00 2001 From: toonn Date: Sat, 16 Jan 2021 20:55:21 +0100 Subject: transpose_subr: Remove check for equal regions There's no point transposing equal regions, the result would be the same. --- ranger/gui/widgets/console.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py index 6c184395..816f341a 100644 --- a/ranger/gui/widgets/console.py +++ b/ranger/gui/widgets/console.py @@ -442,8 +442,7 @@ class Console(Widget): # pylint: disable=too-many-instance-attributes,too-many- 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 (0 <= x[0] <= x[1] <= y[0] <= y[1] <= len(line) - or 0<= x[0] == y[0] <= x[1] == y[1] <= len(line)): + if not (0 <= x[0] < x[1] <= y[0] < y[1] <= len(line)): self.fm.notify("Tried to transpose invalid regions.", bad=True) return line -- cgit v1.2.3 From 421e2e919bce612c222e6a7d8f62fe98dcded938 Mon Sep 17 00:00:00 2001 From: toonn Date: Sat, 16 Jan 2021 20:58:36 +0100 Subject: transpose_subr: Refactor to swap both regions in one concatenation The calculations for cutting out and repositioning the second now shifted region was complicated. Simply cutting the two regions out of the string and swapping them is conceptually simpler. --- ranger/gui/widgets/console.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py index 816f341a..2ff1943d 100644 --- a/ranger/gui/widgets/console.py +++ b/ranger/gui/widgets/console.py @@ -446,19 +446,13 @@ class Console(Widget): # pylint: disable=too-many-instance-attributes,too-many- self.fm.notify("Tried to transpose invalid regions.", bad=True) return line + line_begin = line[:x[0]] word_x = line[x[0]:x[1]] + line_middle = line[x[1]:y[0]] word_y = line[y[0]:y[1]] - diff = len(word_y) - len(word_x) + line_end = line[y[1]:] - 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 + line = line_begin + word_y + line_middle + word_x + line_end return line def transpose_chars(self): -- cgit v1.2.3 From b897f845848fa84a0b2d5b76949d18bff312af4d Mon Sep 17 00:00:00 2001 From: toonn Date: Sat, 16 Jan 2021 21:04:48 +0100 Subject: transpose_chars: Add explicit check for start of line There's no sensible action at the start of the line but we don't want to issue the warning about invalid regions either. --- ranger/gui/widgets/console.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py index 2ff1943d..876081f8 100644 --- a/ranger/gui/widgets/console.py +++ b/ranger/gui/widgets/console.py @@ -456,7 +456,9 @@ class Console(Widget): # pylint: disable=too-many-instance-attributes,too-many- return line def transpose_chars(self): - if self.pos == len(self.line): + if self.pos == 0: + return + elif 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: -- cgit v1.2.3 From f130f7c7e4f3e5eeed90bf1a01d69882cccb0321 Mon Sep 17 00:00:00 2001 From: toonn Date: Sat, 16 Jan 2021 21:29:40 +0100 Subject: transpose_words: Fix transposing last two words Transpose last two words even if on the first letter of last word. This behavior is consistent with readline and emacs. The previous code move one word back if it was anywhere in the last word but that meant the third and second to last words were transposed if the cursor was on the first character of the last word. --- ranger/gui/widgets/console.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py index 876081f8..64537062 100644 --- a/ranger/gui/widgets/console.py +++ b/ranger/gui/widgets/console.py @@ -477,7 +477,10 @@ class Console(Widget): # pylint: disable=too-many-instance-attributes,too-many- 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): + if (re.match(r'[\w\d]*\s*$', self.line[self.pos:], re.UNICODE) + and (re.match(r'[\w\d]', self.line[self.pos - 1], re.UNICODE) + if self.pos -1 >= 0 else True) + ): self.pos = self.move_by_word(self.line, self.pos, -1) # Util function to increment position until out of word/whitespace -- cgit v1.2.3 From 278c655b55a18e63848cda9c945b5a6f7a110636 Mon Sep 17 00:00:00 2001 From: toonn Date: Sat, 16 Jan 2021 21:53:08 +0100 Subject: transpose_subr: Appease the almighty pylint --- ranger/gui/widgets/console.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py index 64537062..eb8cbac6 100644 --- a/ranger/gui/widgets/console.py +++ b/ranger/gui/widgets/console.py @@ -442,7 +442,7 @@ class Console(Widget): # pylint: disable=too-many-instance-attributes,too-many- 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 (0 <= x[0] < x[1] <= y[0] < y[1] <= len(line)): + if not 0 <= x[0] < x[1] <= y[0] < y[1] <= len(line): self.fm.notify("Tried to transpose invalid regions.", bad=True) return line -- cgit v1.2.3 From b3279652597dcf9c3b2db8699f4a311d24730267 Mon Sep 17 00:00:00 2001 From: toonn Date: Sat, 16 Jan 2021 22:20:11 +0100 Subject: transpose_subr/words: Format in accordance to PEP8 --- ranger/gui/widgets/console.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py index eb8cbac6..71c8b6ed 100644 --- a/ranger/gui/widgets/console.py +++ b/ranger/gui/widgets/console.py @@ -479,8 +479,7 @@ class Console(Widget): # pylint: disable=too-many-instance-attributes,too-many- # If in/after last word, interchange last two words if (re.match(r'[\w\d]*\s*$', self.line[self.pos:], re.UNICODE) and (re.match(r'[\w\d]', self.line[self.pos - 1], re.UNICODE) - if self.pos -1 >= 0 else True) - ): + if self.pos - 1 >= 0 else True)): self.pos = self.move_by_word(self.line, self.pos, -1) # Util function to increment position until out of word/whitespace -- cgit v1.2.3