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

PR: Create widget to manage list history #622

Open
wants to merge 29 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
206b4eb
Create widget to manage list history
jsbautista Oct 1, 2024
6e20311
Create widget to manage list history
jsbautista Oct 29, 2024
7e6a582
Clean Code
jsbautista Nov 5, 2024
2d80419
Clean Code
jsbautista Nov 12, 2024
084b9e6
Merge branch 'jupyter:main' into GraphicalExecutionHistory
jsbautista Nov 12, 2024
0a899cd
Clean Code
jsbautista Nov 12, 2024
0da3fc2
Merge branch 'GraphicalExecutionHistory' of https://github.com/jsbaut…
jsbautista Nov 12, 2024
8583b2c
Update shortcut droplist history
jsbautista Nov 12, 2024
4b52e22
Clean code
jsbautista Nov 15, 2024
fbf4541
Add tests
jsbautista Nov 15, 2024
32f9f8d
Clean Code
jsbautista Nov 15, 2024
c30e461
Simplify history list widget by creating just an empty class that inh…
dalthviz Nov 25, 2024
6aaccbe
Use input_buffer to do history completions
dalthviz Nov 28, 2024
11527cb
Some code cleanup
dalthviz Dec 3, 2024
886d3a9
Add simple history complete widget test
dalthviz Dec 4, 2024
fef7498
Testing
dalthviz Dec 4, 2024
ad259b2
Testing
dalthviz Dec 9, 2024
f7877ad
Testing
dalthviz Dec 11, 2024
1920e89
Testing
dalthviz Dec 17, 2024
4d39b4a
Testing
dalthviz Dec 17, 2024
f71e316
Testing
dalthviz Dec 19, 2024
9a394fc
Testing
dalthviz Dec 19, 2024
f2346a2
Testing
dalthviz Dec 19, 2024
61fd484
Testing
dalthviz Jan 3, 2025
cd7cf0f
Merge branch 'main' into GraphicalExecutionHistory
dalthviz Jan 3, 2025
a1e9d2b
Use qtbot.waitUntil and skip test if no_display
dalthviz Jan 6, 2025
18e9b70
Merge branch 'GraphicalExecutionHistory' of https://github.com/jsbaut…
dalthviz Jan 6, 2025
6ac37b0
Mark test as xfail on Linux (lack of window manager setup)
dalthviz Jan 6, 2025
62115e5
Revert added blank line
dalthviz Jan 6, 2025
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
2 changes: 1 addition & 1 deletion .github/workflows/linux-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ jobs:
pip list
- name: Run tests
shell: bash -el {0}
run: xvfb-run --auto-servernum pytest -vv -s --full-trace --color=yes --cov=qtconsole qtconsole
run: xvfb-run --auto-servernum pytest -vv -s --color=yes --cov=qtconsole qtconsole
env:
QT_API: ${{ matrix.QT_LIB }}
PYTEST_QT_API: ${{ matrix.QT_LIB }}
Expand Down
115 changes: 108 additions & 7 deletions qtconsole/history_console_widget.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import os
import os.path

from qtpy import QtGui
from qtpy import QtCore, QtGui, QtWidgets

from traitlets import Bool
from .console_widget import ConsoleWidget
from .completion_widget import CompletionWidget


class HistoryListWidget(CompletionWidget):
""" A widget for GUI list history.
"""
complete_current = QtCore.Signal(str)

def _complete_current(self):
""" Perform the completion with the currently selected item.
"""
text = self.currentItem().data(QtCore.Qt.UserRole)
self.complete_current.emit(text)
self.hide()


class HistoryConsoleWidget(ConsoleWidget):
Expand All @@ -31,6 +47,15 @@ def __init__(self, *args, **kw):
self._history_edits = {}
self._history_index = 0
self._history_prefix = ''
self.droplist_history = QtWidgets.QAction("Show related history execution entries",
self,
shortcut="Ctrl+Shift+R",
shortcutContext=QtCore.Qt.WidgetWithChildrenShortcut,
triggered=self._show_history_droplist
)
self.addAction(self.droplist_history)
self._history_list_widget = HistoryListWidget(self, self.gui_completion_height)
self._history_list_widget.complete_current.connect(self.change_input_buffer)

#---------------------------------------------------------------------------
# 'ConsoleWidget' public interface
Expand Down Expand Up @@ -142,6 +167,70 @@ def _down_pressed(self, shift_modifier):

return True

def _show_history_droplist(self):
# Perform the search.
prompt_cursor = self._get_prompt_cursor()
if self._get_cursor().blockNumber() == prompt_cursor.blockNumber():

# Set a search prefix based on the cursor position.
pos = self._get_input_buffer_cursor_pos()
input_buffer = self.input_buffer
# use the *shortest* of the cursor column and the history prefix
# to determine if the prefix has changed
n = min(pos, len(self._history_prefix))

# prefix changed, restart search from the beginning
if self._history_prefix[:n] != input_buffer[:n]:
self._history_index = len(self._history)

# the only time we shouldn't set the history prefix
# to the line up to the cursor is if we are already
# in a simple scroll (no prefix),
# and the cursor is at the end of the first line

# check if we are at the end of the first line
c = self._get_cursor()
current_pos = c.position()
c.movePosition(QtGui.QTextCursor.EndOfBlock)
at_eol = c.position() == current_pos

if (
self._history_index == len(self._history)
or not (self._history_prefix == "" and at_eol)
or not (
self._get_edited_history(self._history_index)[:pos]
== input_buffer[:pos]
)
):
self._history_prefix = input_buffer[:pos]
items = self._history
items.reverse()
if self._history_prefix:
items = [
item
for item in items
if item.startswith(self._history_prefix)
]

cursor = self._get_cursor()
pos = len(self._history_prefix)
cursor_pos = self._get_input_buffer_cursor_pos()
cursor.movePosition(QtGui.QTextCursor.Left, n=(cursor_pos - pos))
# This line actually applies the move to control's cursor
self._control.setTextCursor(cursor)

self._history_list_widget.cancel_completion()
if len(items) == 1:
self._history_list_widget.show_items(
cursor, items
)
elif len(items) > 1:
current_pos = self._control.textCursor().position()
prefix = os.path.commonprefix(items)
self._history_list_widget.show_items(
cursor, items, prefix_length=len(prefix)
)

#---------------------------------------------------------------------------
# 'HistoryConsoleWidget' public interface
#---------------------------------------------------------------------------
Expand Down Expand Up @@ -173,9 +262,7 @@ def history_previous(self, substring='', as_prefix=True):
break

if replace:
self._store_edits()
self._history_index = index
self.input_buffer = history
self.change_input_buffer(history, index=index)

return replace

Expand Down Expand Up @@ -206,9 +293,7 @@ def history_next(self, substring='', as_prefix=True):
break

if replace:
self._store_edits()
self._history_index = index
self.input_buffer = history
self.change_input_buffer(history, index=index)

return replace

Expand All @@ -222,6 +307,22 @@ def history_tail(self, n=10):
"""
return self._history[-n:]

@QtCore.Slot(str)
def change_input_buffer(self, buffer, index=None):
"""Change input_buffer value while storing edits and updating history index.

Parameters
----------
buffer : str
New value for the inpur buffer.
index : int, optional
History index to set. The default is 0.
"""
if index:
self._store_edits()
self._history_index = index
self.input_buffer = buffer

#---------------------------------------------------------------------------
# 'HistoryConsoleWidget' protected interface
#---------------------------------------------------------------------------
Expand Down
35 changes: 35 additions & 0 deletions qtconsole/tests/test_00_console_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,41 @@ def qtconsole(qtbot):
console.window.close()


@pytest.mark.xfail(
sys.platform.startswith("linux"),
reason="Doesn't work without a window manager on Linux"
)
def test_history_complete(qtconsole, qtbot):
"""
Test history complete widget
"""
window = qtconsole.window
shell = window.active_frontend
control = shell._control

# Wait until the console is fully up
qtbot.waitUntil(
lambda: shell._prompt_html is not None, timeout=SHELL_TIMEOUT
)

with qtbot.waitSignal(shell.executed):
shell.execute("import time")

qtbot.keyClicks(control, "imp")

qtbot.keyClick(
control,
QtCore.Qt.Key_R,
modifier=QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier,
)
qtbot.waitUntil(lambda: shell._history_list_widget.isVisible())

qtbot.keyClick(shell._history_list_widget, QtCore.Qt.Key_Enter)
qtbot.waitUntil(lambda: not shell._history_list_widget.isVisible())

assert shell.input_buffer == "import time"


@flaky(max_runs=3)
@pytest.mark.parametrize(
"debug", [True, False])
Expand Down
Loading