Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add logic to handle ANSI escape sequences to move cursor #616

Open
wants to merge 33 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
83f6780
First commit to fix TQDM
jsbautista Jul 29, 2024
95f2498
Fix command F
jsbautista Jul 30, 2024
a711809
add Test to moveCursor
jsbautista Aug 5, 2024
be2f619
Fix Test to moveCursor
jsbautista Aug 5, 2024
548c65f
Fix Test to moveCursor
jsbautista Aug 5, 2024
530ba46
Merge branch 'jupyter:main' into TQDMQtConsole
jsbautista Aug 12, 2024
dea62c7
Add down line action
jsbautista Aug 20, 2024
f8cb1a8
Merge branch 'TQDMQtConsole' of https://github.com/jsbautista/qtconso…
jsbautista Aug 20, 2024
6f82ea8
Merge branch 'jupyter:main' into TQDMQtConsole
jsbautista Aug 20, 2024
d54f69b
Add action move down AnsiCodeProcessor
jsbautista Aug 27, 2024
b492920
Add new behavior to \n
jsbautista Aug 27, 2024
6044edc
Add new behavior to \n
jsbautista Aug 27, 2024
461a8d4
fixTests
jsbautista Aug 27, 2024
83e2f1d
fixTests
jsbautista Aug 27, 2024
3b23c09
Add new behavior to \n
jsbautista Aug 27, 2024
152740a
fixTests
jsbautista Aug 27, 2024
75a53bf
fix behavior to \n
jsbautista Aug 27, 2024
c198d20
fix behavior to \n
jsbautista Aug 27, 2024
380e56c
fixTests
jsbautista Aug 27, 2024
03e83dc
fix behavior to \n
jsbautista Sep 2, 2024
6aa21d6
fixTests
jsbautista Sep 2, 2024
46aa854
fix behavior to \n
jsbautista Sep 2, 2024
7aff3b0
fixTests
jsbautista Sep 2, 2024
9189023
fixTests
jsbautista Sep 2, 2024
7591edd
fixTests
jsbautista Sep 2, 2024
41ea37e
Update qtconsole/console_widget.py
jsbautista Sep 9, 2024
cb7bcaa
Clean code
jsbautista Sep 16, 2024
ebdd625
Merge branch 'jupyter:main' into TQDMQtConsole
jsbautista Sep 16, 2024
7aaff63
Update qtconsole/tests/test_ansi_code_processor.py
jsbautista Sep 24, 2024
42cc431
Clean Code
jsbautista Oct 8, 2024
ddf5a1d
Apply suggestions from code review
jsbautista Oct 17, 2024
a7b4c10
add coment to test
jsbautista Oct 17, 2024
c7b4589
Merge branch 'TQDMQtConsole' of https://github.com/jsbautista/qtconso…
jsbautista Oct 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions qtconsole/ansi_code_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def split_string(self, string):
# strings ending with \r are assumed to be ending in \r\n since
# \n is appended to output strings automatically. Accounting
# for that, here.
last_char = '\n' if len(string) > 0 and string[-1] == '\n' else None
last_char = None#'\n' if len(string) > 0 and string[-1] == '\n' else None
string = string[:-1] if last_char is not None else string

for match in ANSI_OR_SPECIAL_PATTERN.finditer(string):
Expand All @@ -122,7 +122,7 @@ def split_string(self, string):
self.actions = []
elif g0 == '\n' or g0 == '\r\n':
self.actions.append(NewLineAction('newline'))
yield g0
yield None
self.actions = []
else:
params = [ param for param in groups[1].split(';') if param ]
Expand All @@ -147,7 +147,7 @@ def split_string(self, string):

if last_char is not None:
self.actions.append(NewLineAction('newline'))
yield last_char
yield None

def set_csi_code(self, command, params=[]):
""" Set attributes based on CSI (Control Sequence Introducer) code.
Expand Down Expand Up @@ -185,6 +185,20 @@ def set_csi_code(self, command, params=[]):
count = params[0] if params else 1
self.actions.append(ScrollAction('scroll', dir, 'line', count))

elif (command == 'A'): # Move N lines Up
jsbautista marked this conversation as resolved.
Show resolved Hide resolved
dir = 'up'
count = params[0] if params else 1
self.actions.append(MoveAction('move', dir, 'line', count))
elif (command == 'B'): # Move N lines Down
jsbautista marked this conversation as resolved.
Show resolved Hide resolved
dir = 'down'
count = params[0] if params else 1
self.actions.append(MoveAction('move', dir, 'line', count))
elif (command == 'F'): # Goes back to the begining of the n-th previous line
jsbautista marked this conversation as resolved.
Show resolved Hide resolved
dir = 'leftup'
count = params[0] if params else 1
self.actions.append(MoveAction('move', dir, 'line', count))


def set_osc_code(self, params):
""" Set attributes based on OSC (Operating System Command) parameters.

Expand Down
25 changes: 24 additions & 1 deletion qtconsole/console_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -2188,6 +2188,23 @@ def _insert_plain_text(self, cursor, text, flush=False):
cursor.select(QtGui.QTextCursor.Document)
cursor.removeSelectedText()

elif act.action == 'move' and act.unit == 'line':
if act.dir == 'up':
for i in range(act.count):
cursor.movePosition(
QtGui.QTextCursor.Up)
jsbautista marked this conversation as resolved.
Show resolved Hide resolved
elif act.dir == 'down':
for i in range(act.count):
cursor.movePosition(
QtGui.QTextCursor.Down)
jsbautista marked this conversation as resolved.
Show resolved Hide resolved
elif act.dir == 'leftup':
for i in range(act.count):
cursor.movePosition(
QtGui.QTextCursor.Up)
cursor.movePosition(
QtGui.QTextCursor.StartOfLine,
QtGui.QTextCursor.MoveAnchor)
jsbautista marked this conversation as resolved.
Show resolved Hide resolved

elif act.action == 'carriage-return':
cursor.movePosition(
QtGui.QTextCursor.StartOfLine,
Expand All @@ -2203,7 +2220,13 @@ def _insert_plain_text(self, cursor, text, flush=False):
QtGui.QTextCursor.MoveAnchor)

elif act.action == 'newline':
cursor.movePosition(QtGui.QTextCursor.EndOfLine)
self.log.debug(self._prompt)
if cursor.block() != cursor.document().lastBlock():
cursor.movePosition(QtGui.QTextCursor.NextBlock)
else:
cursor.movePosition(QtGui.QTextCursor.EndOfLine,
QtGui.QTextCursor.MoveAnchor)
cursor.insertText('\n')
jsbautista marked this conversation as resolved.
Show resolved Hide resolved

# simulate replacement mode
if substring is not None:
Expand Down
38 changes: 37 additions & 1 deletion qtconsole/tests/test_ansi_code_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def test_carriage_return_newline(self):
for split in self.processor.split_string(string):
splits.append(split)
actions.append([action.action for action in self.processor.actions])
self.assertEqual(splits, ['foo', None, 'bar', '\r\n', 'cat', '\r\n', '\n'])
self.assertEqual(splits, ['foo', None, 'bar',None, 'cat', None, None])
jsbautista marked this conversation as resolved.
Show resolved Hide resolved
self.assertEqual(actions, [[], ['carriage-return'], [], ['newline'], [], ['newline'], ['newline']])

def test_beep(self):
Expand Down Expand Up @@ -182,6 +182,42 @@ def test_combined(self):
self.assertEqual(splits, ['abc', None, 'def', None])
self.assertEqual(actions, [[], ['carriage-return'], [], ['backspace']])

def test_move_cursor_up(self):
string = '\x1b[A\x1b[5A\x1b[F\x1b[5F'
Copy link
Collaborator

@ccordoba12 ccordoba12 Oct 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment above this line to explain what it does.

i = -1
for i, substring in enumerate(self.processor.split_string(string)):
if i == 0:
self.assertEqual(len(self.processor.actions), 1)
action = self.processor.actions[0]
self.assertEqual(action.action, 'move')
self.assertEqual(action.dir, 'up')
self.assertEqual(action.unit, 'line')
self.assertEqual(action.count, 1)
elif i == 1:
self.assertEqual(len(self.processor.actions), 1)
action = self.processor.actions[0]
self.assertEqual(action.action, 'move')
self.assertEqual(action.dir, 'up')
self.assertEqual(action.unit, 'line')
self.assertEqual(action.count, 5)
elif i == 2:
self.assertEqual(len(self.processor.actions), 1)
action = self.processor.actions[0]
self.assertEqual(action.action, 'move')
self.assertEqual(action.dir, 'leftup')
self.assertEqual(action.unit, 'line')
self.assertEqual(action.count, 1)
elif i == 3:
self.assertEqual(len(self.processor.actions), 1)
action = self.processor.actions[0]
self.assertEqual(action.action, 'move')
self.assertEqual(action.dir, 'leftup')
self.assertEqual(action.unit, 'line')
self.assertEqual(action.count, 5)
else:
self.fail('Too many substrings.')
self.assertEqual(i, 3, 'Too few substrings.')


if __name__ == '__main__':
unittest.main()
Loading