Add initial filename completion
This commit is contained in:
parent
6ab51e0b7b
commit
9bdbb257ba
@ -977,6 +977,7 @@ How many steps to zoom out.
|
|||||||
|<<move-to-start-of-prev-block,move-to-start-of-prev-block>>|Move the cursor or selection to the start of previous block.
|
|<<move-to-start-of-prev-block,move-to-start-of-prev-block>>|Move the cursor or selection to the start of previous block.
|
||||||
|<<open-editor,open-editor>>|Open an external editor with the currently selected form field.
|
|<<open-editor,open-editor>>|Open an external editor with the currently selected form field.
|
||||||
|<<prompt-accept,prompt-accept>>|Accept the current prompt.
|
|<<prompt-accept,prompt-accept>>|Accept the current prompt.
|
||||||
|
|<<prompt-item-focus,prompt-item-focus>>|Shift the focus of the prompt file completion menu to another item.
|
||||||
|<<prompt-open-download,prompt-open-download>>|Immediately open a download.
|
|<<prompt-open-download,prompt-open-download>>|Immediately open a download.
|
||||||
|<<repeat-command,repeat-command>>|Repeat the last executed command.
|
|<<repeat-command,repeat-command>>|Repeat the last executed command.
|
||||||
|<<rl-backward-char,rl-backward-char>>|Move back a character.
|
|<<rl-backward-char,rl-backward-char>>|Move back a character.
|
||||||
@ -1229,6 +1230,15 @@ Accept the current prompt.
|
|||||||
* +'value'+: If given, uses this value instead of the entered one. For boolean prompts, "yes"/"no" are accepted as value.
|
* +'value'+: If given, uses this value instead of the entered one. For boolean prompts, "yes"/"no" are accepted as value.
|
||||||
|
|
||||||
|
|
||||||
|
[[prompt-item-focus]]
|
||||||
|
=== prompt-item-focus
|
||||||
|
Syntax: +:prompt-item-focus 'which'+
|
||||||
|
|
||||||
|
Shift the focus of the prompt file completion menu to another item.
|
||||||
|
|
||||||
|
==== positional arguments
|
||||||
|
* +'which'+: 'next', 'prev'
|
||||||
|
|
||||||
[[prompt-open-download]]
|
[[prompt-open-download]]
|
||||||
=== prompt-open-download
|
=== prompt-open-download
|
||||||
Syntax: +:prompt-open-download ['cmdline']+
|
Syntax: +:prompt-open-download ['cmdline']+
|
||||||
|
@ -1680,6 +1680,8 @@ KEY_DATA = collections.OrderedDict([
|
|||||||
('prompt-accept yes', ['y']),
|
('prompt-accept yes', ['y']),
|
||||||
('prompt-accept no', ['n']),
|
('prompt-accept no', ['n']),
|
||||||
('prompt-open-download', ['<Ctrl-X>']),
|
('prompt-open-download', ['<Ctrl-X>']),
|
||||||
|
('prompt-item-focus prev', ['<Shift-Tab>', '<Up>']),
|
||||||
|
('prompt-item-focus next', ['<Tab>', '<Down>']),
|
||||||
])),
|
])),
|
||||||
|
|
||||||
('command,prompt', collections.OrderedDict([
|
('command,prompt', collections.OrderedDict([
|
||||||
|
@ -24,7 +24,8 @@ import html
|
|||||||
import collections
|
import collections
|
||||||
|
|
||||||
import sip
|
import sip
|
||||||
from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QTimer, QDir, QModelIndex
|
from PyQt5.QtCore import (pyqtSlot, pyqtSignal, Qt, QTimer, QDir, QModelIndex,
|
||||||
|
QItemSelectionModel)
|
||||||
from PyQt5.QtWidgets import (QWidget, QGridLayout, QVBoxLayout, QLineEdit,
|
from PyQt5.QtWidgets import (QWidget, QGridLayout, QVBoxLayout, QLineEdit,
|
||||||
QLabel, QWidgetItem, QFileSystemModel, QTreeView,
|
QLabel, QWidgetItem, QFileSystemModel, QTreeView,
|
||||||
QSizePolicy)
|
QSizePolicy)
|
||||||
@ -264,6 +265,20 @@ class PromptContainer(QWidget):
|
|||||||
except UnsupportedOperationError:
|
except UnsupportedOperationError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@cmdutils.register(instance='prompt-container', hide=True, scope='window',
|
||||||
|
modes=[usertypes.KeyMode.prompt])
|
||||||
|
@cmdutils.argument('which', choices=['next', 'prev'])
|
||||||
|
def prompt_item_focus(self, which):
|
||||||
|
"""Shift the focus of the prompt file completion menu to another item.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
which: 'next', 'prev'
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self._prompt.item_focus(which)
|
||||||
|
except UnsupportedOperationError:
|
||||||
|
pass
|
||||||
|
|
||||||
@pyqtSlot(usertypes.Question, bool)
|
@pyqtSlot(usertypes.Question, bool)
|
||||||
def ask_question(self, question, blocking):
|
def ask_question(self, question, blocking):
|
||||||
"""Display a prompt for a given question.
|
"""Display a prompt for a given question.
|
||||||
@ -425,6 +440,10 @@ class _BasePrompt(QWidget):
|
|||||||
"""Open the download directly if this is a download prompt."""
|
"""Open the download directly if this is a download prompt."""
|
||||||
raise UnsupportedOperationError
|
raise UnsupportedOperationError
|
||||||
|
|
||||||
|
def item_focus(self, _which):
|
||||||
|
"""Switch to next file item if this is a filename prompt.."""
|
||||||
|
raise UnsupportedOperationError
|
||||||
|
|
||||||
def _allowed_commands(self):
|
def _allowed_commands(self):
|
||||||
"""Get the commands we could run as response to this message."""
|
"""Get the commands we could run as response to this message."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
@ -477,7 +496,7 @@ class FilenamePrompt(_BasePrompt):
|
|||||||
@pyqtSlot(str)
|
@pyqtSlot(str)
|
||||||
def _set_fileview_root(self, path):
|
def _set_fileview_root(self, path):
|
||||||
"""Set the root path for the file display."""
|
"""Set the root path for the file display."""
|
||||||
if not path.endswith('/'):
|
if not path.endswith('/') or path == '/':
|
||||||
return
|
return
|
||||||
path.rstrip('/')
|
path.rstrip('/')
|
||||||
|
|
||||||
@ -498,25 +517,33 @@ class FilenamePrompt(_BasePrompt):
|
|||||||
self._file_view.setRootIndex(root)
|
self._file_view.setRootIndex(root)
|
||||||
|
|
||||||
@pyqtSlot(QModelIndex)
|
@pyqtSlot(QModelIndex)
|
||||||
def _on_clicked(self, index):
|
def _insert_path(self, index, *, clicked=True):
|
||||||
"""Handle a click on an element."""
|
"""Handle an element selection.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
index: The QModelIndex of the selected element.
|
||||||
|
clicked: Whether the element was clicked.
|
||||||
|
"""
|
||||||
parts = []
|
parts = []
|
||||||
cur = index
|
cur = index
|
||||||
while cur.isValid():
|
while cur.isValid():
|
||||||
parts.append(cur.data())
|
parts.append(cur.data())
|
||||||
cur = cur.parent()
|
cur = cur.parent()
|
||||||
path = os.path.normpath(os.path.join(*reversed(parts))) + os.sep
|
path = os.path.normpath(os.path.join(*reversed(parts)))
|
||||||
|
if clicked:
|
||||||
|
path += os.sep
|
||||||
log.prompt.debug('Clicked {!r} -> {}'.format(parts, path))
|
log.prompt.debug('Clicked {!r} -> {}'.format(parts, path))
|
||||||
self._lineedit.setText(path)
|
self._lineedit.setText(path)
|
||||||
self._lineedit.setFocus()
|
self._lineedit.setFocus()
|
||||||
# Avoid having a ..-subtree highlighted
|
if clicked:
|
||||||
self._file_view.setCurrentIndex(QModelIndex())
|
# Avoid having a ..-subtree highlighted
|
||||||
|
self._file_view.setCurrentIndex(QModelIndex())
|
||||||
|
|
||||||
def _init_fileview(self):
|
def _init_fileview(self):
|
||||||
self._file_view = QTreeView(self)
|
self._file_view = QTreeView(self)
|
||||||
self._file_model = QFileSystemModel(self)
|
self._file_model = QFileSystemModel(self)
|
||||||
self._file_view.setModel(self._file_model)
|
self._file_view.setModel(self._file_model)
|
||||||
self._file_view.clicked.connect(self._on_clicked)
|
self._file_view.clicked.connect(self._insert_path)
|
||||||
self._vbox.addWidget(self._file_view)
|
self._vbox.addWidget(self._file_view)
|
||||||
# Only show name
|
# Only show name
|
||||||
self._file_view.setHeaderHidden(True)
|
self._file_view.setHeaderHidden(True)
|
||||||
@ -528,6 +555,31 @@ class FilenamePrompt(_BasePrompt):
|
|||||||
self.question.answer = text
|
self.question.answer = text
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def item_focus(self, which):
|
||||||
|
# This duplicates some completion code, but I don't see a nicer way...
|
||||||
|
assert which in ['prev', 'next'], which
|
||||||
|
selmodel = self._file_view.selectionModel()
|
||||||
|
|
||||||
|
first_index = self._file_model.index(0, 0)
|
||||||
|
last_index = self._file_model.index(self._file_model.rowCount() - 1, 0)
|
||||||
|
|
||||||
|
idx = selmodel.currentIndex()
|
||||||
|
if not idx.isValid():
|
||||||
|
# No item selected yet
|
||||||
|
idx = last_index if which == 'prev' else first_index
|
||||||
|
|
||||||
|
if which == 'prev':
|
||||||
|
idx = self._file_view.indexAbove(idx)
|
||||||
|
else:
|
||||||
|
idx = self._file_view.indexBelow(idx)
|
||||||
|
# wrap around if we arrived at beginning/end
|
||||||
|
if not idx.isValid():
|
||||||
|
idx = last_index if which == 'prev' else first_index
|
||||||
|
|
||||||
|
selmodel.setCurrentIndex(
|
||||||
|
idx, QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows)
|
||||||
|
self._insert_path(idx, clicked=False)
|
||||||
|
|
||||||
def _allowed_commands(self):
|
def _allowed_commands(self):
|
||||||
"""Get the commands we could run as response to this message."""
|
"""Get the commands we could run as response to this message."""
|
||||||
return [('prompt-accept', 'Accept'), ('leave-mode', 'Abort')]
|
return [('prompt-accept', 'Accept'), ('leave-mode', 'Abort')]
|
||||||
|
Loading…
Reference in New Issue
Block a user