Commit 0e2bbbb7 authored by Jacek Sowiński's avatar Jacek Sowiński

Implement simple completion into the Column UI

Currently it works only with single completion candidate and works for:
contexts, projects, subcommands.
parent 8d5f43ba
# Topydo - A todo.txt client written in Python.
# Copyright (C) 2017 Bram Schoenmakers <bram@topydo.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from topydo.Commands import _SUBCOMMAND_MAP
from topydo.lib.Config import config
def _get_subcmds():
subcmd_map = config().aliases()
subcmd_map.update(_SUBCOMMAND_MAP)
return sorted(subcmd_map.keys())
class CompleterBase(object):
def __init__(self, p_todolist):
self.todolist = p_todolist
self._subcmds = _get_subcmds()
def _complete_context(self, p_word):
completions = ['@' + context for context in self.todolist.contexts() if
context.startswith(p_word[1:])]
return completions
def _complete_project(self, p_word):
completions = ['+' + project for project in self.todolist.projects() if
project.startswith(p_word[1:])]
return completions
def _complete_subcmd(self, p_word):
completions = [cmd for cmd in self._subcmds if
cmd.startswith(p_word)]
return completions
def get_completions(self, p_word, p_is_first_word=False):
completions = []
if p_word.startswith('+'):
completions = self._complete_project(p_word)
elif p_word.startswith('@'):
completions = self._complete_context(p_word)
elif p_is_first_word:
completions = self._complete_subcmd(p_word)
return completions
...@@ -16,13 +16,27 @@ ...@@ -16,13 +16,27 @@
import urwid import urwid
def _get_word_before_pos(p_text, p_pos):
is_first_word = False
text = p_text
pos = p_pos
start = text.rfind(' ', 0, pos) + 1
if pos == 0 or text.lstrip().rfind(' ', 0, pos) == -1:
is_first_word = True
return (text[start:pos], is_first_word)
class CommandLineWidget(urwid.Edit): class CommandLineWidget(urwid.Edit):
def __init__(self, *args, **kwargs): def __init__(self, p_completer, *args, **kwargs):
self.history = [] self.history = []
self.history_pos = None self.history_pos = None
# temporary history storage for edits before cmd execution # temporary history storage for edits before cmd execution
self.history_tmp = [] self.history_tmp = []
self.completer = p_completer
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
urwid.register_signal(CommandLineWidget, ['blur', 'execute_command']) urwid.register_signal(CommandLineWidget, ['blur', 'execute_command'])
...@@ -71,12 +85,35 @@ class CommandLineWidget(urwid.Edit): ...@@ -71,12 +85,35 @@ class CommandLineWidget(urwid.Edit):
if self.history_pos != 0: if self.history_pos != 0:
self._history_move(-1) self._history_move(-1)
def _complete(self):
pos = self.edit_pos
text = self.edit_text
word_before_cursor, is_first = _get_word_before_pos(text, pos)
completions = self.completer.get_completions(word_before_cursor,
is_first)
if not completions:
return
elif len(completions) > 1: # TODO multiple completions
return
else:
replacement = completions[0]
if replacement == word_before_cursor:
return # Don't complete what is already completed
offset = len(replacement) - len(word_before_cursor)
final_text = text[:pos] + replacement[-offset:] + text[pos:]
self.set_edit_text(final_text)
self.set_edit_pos(pos + offset)
def keypress(self, p_size, p_key): def keypress(self, p_size, p_key):
dispatch = { dispatch = {
'enter': self._emit_command, 'enter': self._emit_command,
'esc': self._blur, 'esc': self._blur,
'up': self._history_back, 'up': self._history_back,
'down': self._history_next 'down': self._history_next,
'tab': self._complete
} }
try: try:
......
...@@ -26,6 +26,7 @@ from string import ascii_uppercase ...@@ -26,6 +26,7 @@ from string import ascii_uppercase
from topydo.Commands import get_subcommand from topydo.Commands import get_subcommand
from topydo.lib.Config import config, ConfigError from topydo.lib.Config import config, ConfigError
from topydo.lib.Completer import CompleterBase
from topydo.lib.Sorter import Sorter from topydo.lib.Sorter import Sorter
from topydo.lib.Filter import get_filter_list, RelevanceFilter, DependencyFilter from topydo.lib.Filter import get_filter_list, RelevanceFilter, DependencyFilter
from topydo.lib.Utils import get_terminal_size from topydo.lib.Utils import get_terminal_size
...@@ -123,7 +124,8 @@ class UIApplication(CLIApplicationBase): ...@@ -123,7 +124,8 @@ class UIApplication(CLIApplicationBase):
self.columns = urwid.Columns([], dividechars=0, self.columns = urwid.Columns([], dividechars=0,
min_width=config().column_width()) min_width=config().column_width())
self.commandline = CommandLineWidget('topydo> ') completer = CompleterBase(self.todolist)
self.commandline = CommandLineWidget(completer, 'topydo> ')
self.keystate_widget = KeystateWidget() self.keystate_widget = KeystateWidget()
self.status_line = urwid.Columns([ self.status_line = urwid.Columns([
('weight', 1, urwid.Filler(self.commandline)), ('weight', 1, urwid.Filler(self.commandline)),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment