Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
T
topydo
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
topydo
Commits
7ca36b81
Commit
7ca36b81
authored
Jul 13, 2017
by
Bram Schoenmakers
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'completion'
parents
9acb8f44
6f77ce7e
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
485 additions
and
27 deletions
+485
-27
topydo/Commands.py
topydo/Commands.py
+7
-7
topydo/ui/CompleterBase.py
topydo/ui/CompleterBase.py
+95
-0
topydo/ui/columns/ColumnCompleter.py
topydo/ui/columns/ColumnCompleter.py
+49
-0
topydo/ui/columns/CommandLineWidget.py
topydo/ui/columns/CommandLineWidget.py
+151
-3
topydo/ui/columns/CompletionBoxWidget.py
topydo/ui/columns/CompletionBoxWidget.py
+65
-0
topydo/ui/columns/ConsoleWidget.py
topydo/ui/columns/ConsoleWidget.py
+0
-12
topydo/ui/columns/Main.py
topydo/ui/columns/Main.py
+50
-3
topydo/ui/prompt/Prompt.py
topydo/ui/prompt/Prompt.py
+2
-2
topydo/ui/prompt/PromptCompleter.py
topydo/ui/prompt/PromptCompleter.py
+66
-0
No files found.
topydo/Commands.py
View file @
7ca36b81
...
...
@@ -23,7 +23,7 @@ import sys
from
topydo.lib.Config
import
config
,
ConfigError
_
SUBCOMMAND_MAP
=
{
SUBCOMMAND_MAP
=
{
'add'
:
'AddCommand'
,
'app'
:
'AppendCommand'
,
'append'
:
'AppendCommand'
,
...
...
@@ -63,9 +63,9 @@ def get_subcommand(p_args):
"""
Returns the class of the requested subcommand. An invalid p_subcommand
will result in an ImportError, since this is a programming mistake
(most likely an error in the
_
SUBCOMMAND_MAP).
(most likely an error in the SUBCOMMAND_MAP).
"""
classname
=
_
SUBCOMMAND_MAP
[
p_subcommand
]
classname
=
SUBCOMMAND_MAP
[
p_subcommand
]
modulename
=
'topydo.commands.{}'
.
format
(
classname
)
__import__
(
modulename
,
globals
(),
locals
(),
[
classname
],
0
)
...
...
@@ -111,14 +111,14 @@ def get_subcommand(p_args):
if
subcommand
in
alias_map
:
result
,
args
=
resolve_alias
(
subcommand
,
args
[
1
:])
elif
subcommand
in
_
SUBCOMMAND_MAP
:
elif
subcommand
in
SUBCOMMAND_MAP
:
result
=
import_subcommand
(
subcommand
)
args
=
args
[
1
:]
elif
subcommand
==
'help'
:
try
:
subcommand
=
args
[
1
]
if
subcommand
in
_
SUBCOMMAND_MAP
:
if
subcommand
in
SUBCOMMAND_MAP
:
args
=
[
subcommand
,
'help'
]
return
get_subcommand
(
args
)
except
IndexError
:
...
...
@@ -128,14 +128,14 @@ def get_subcommand(p_args):
p_command
=
config
().
default_command
()
if
p_command
in
alias_map
:
result
,
args
=
resolve_alias
(
p_command
,
args
)
elif
p_command
in
_
SUBCOMMAND_MAP
:
elif
p_command
in
SUBCOMMAND_MAP
:
result
=
import_subcommand
(
p_command
)
# leave args unchanged
except
IndexError
:
p_command
=
config
().
default_command
()
if
p_command
in
alias_map
:
result
,
args
=
resolve_alias
(
p_command
,
args
)
elif
p_command
in
_
SUBCOMMAND_MAP
:
elif
p_command
in
SUBCOMMAND_MAP
:
result
=
import_subcommand
(
p_command
)
return
(
result
,
args
)
topydo/ui/CompleterBase.py
0 → 100644
View file @
7ca36b81
# 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/>.
import
datetime
from
functools
import
lru_cache
from
topydo.Commands
import
SUBCOMMAND_MAP
from
topydo.lib.Config
import
config
@
lru_cache
(
maxsize
=
1
)
def
_get_subcmds
():
subcmd_map
=
config
().
aliases
().
copy
()
subcmd_map
.
update
(
SUBCOMMAND_MAP
)
return
sorted
(
subcmd_map
.
keys
())
def
date_suggestions
():
"""
Returns a list of relative date that is presented to the user as auto
complete suggestions.
"""
# don't use strftime, prevent locales to kick in
days_of_week
=
{
0
:
"Monday"
,
1
:
"Tuesday"
,
2
:
"Wednesday"
,
3
:
"Thursday"
,
4
:
"Friday"
,
5
:
"Saturday"
,
6
:
"Sunday"
}
dates
=
[
'today'
,
'tomorrow'
,
]
# show days of week up to next week
dow
=
datetime
.
date
.
today
().
weekday
()
for
i
in
range
(
dow
+
2
%
7
,
dow
+
7
):
dates
.
append
(
days_of_week
[
i
%
7
])
# and some more relative days starting from next week
dates
+=
[
"1w"
,
"2w"
,
"1m"
,
"2m"
,
"3m"
,
"1y"
]
return
dates
class
CompleterBase
(
object
):
def
__init__
(
self
,
p_todolist
):
self
.
todolist
=
p_todolist
self
.
_all_subcmds
=
_get_subcmds
()
def
_contexts
(
self
,
p_word
):
completions
=
[
'@'
+
context
for
context
in
self
.
todolist
.
contexts
()
if
context
.
startswith
(
p_word
[
1
:])]
return
sorted
(
completions
)
def
_projects
(
self
,
p_word
):
completions
=
[
'+'
+
project
for
project
in
self
.
todolist
.
projects
()
if
project
.
startswith
(
p_word
[
1
:])]
return
sorted
(
completions
)
def
_subcmds
(
self
,
p_word
):
completions
=
[
cmd
for
cmd
in
self
.
_all_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
.
_projects
(
p_word
)
elif
p_word
.
startswith
(
'@'
):
completions
=
self
.
_contexts
(
p_word
)
elif
p_is_first_word
:
completions
=
self
.
_subcmds
(
p_word
)
return
completions
topydo/ui/columns/ColumnCompleter.py
0 → 100644
View file @
7ca36b81
# 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/>.
"""
This module provides a completer class that can be used by the Column UI
CommmandLineWidget.
"""
from
topydo.lib.Config
import
config
from
topydo.ui.CompleterBase
import
CompleterBase
,
date_suggestions
class
ColumnCompleter
(
CompleterBase
):
"""
Completer class that completes projects, contexts, dates and
subcommands designed to work with CommandLineWidget for column UI.
"""
def
get_completions
(
self
,
p_word
,
p_is_first_word
=
False
):
def
dates
(
p_word
,
p_tag
):
dates
=
[]
for
date
in
date_suggestions
():
candidate
=
p_tag
+
':'
+
date
if
candidate
.
startswith
(
p_word
):
dates
.
append
(
candidate
)
return
dates
due_tag
=
config
().
tag_due
()
start_tag
=
config
().
tag_start
()
if
p_word
.
startswith
(
due_tag
+
':'
):
return
dates
(
p_word
,
due_tag
)
elif
p_word
.
startswith
(
start_tag
+
':'
):
return
dates
(
p_word
,
start_tag
)
else
:
return
super
().
get_completions
(
p_word
,
p_is_first_word
)
topydo/ui/columns/CommandLineWidget.py
View file @
7ca36b81
...
...
@@ -16,16 +16,28 @@
import
urwid
from
os.path
import
commonprefix
from
topydo.ui.columns.CompletionBoxWidget
import
CompletionBoxWidget
class
CommandLineWidget
(
urwid
.
Edit
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
def
__init__
(
self
,
p_completer
,
*
args
,
**
kwargs
):
self
.
history
=
[]
self
.
history_pos
=
None
# temporary history storage for edits before cmd execution
self
.
history_tmp
=
[]
self
.
completer
=
p_completer
self
.
completion_box
=
CompletionBoxWidget
()
self
.
_surrounding_text
=
None
# text before insertion of completion
super
().
__init__
(
*
args
,
**
kwargs
)
urwid
.
register_signal
(
CommandLineWidget
,
[
'blur'
,
'execute_command'
])
urwid
.
register_signal
(
CommandLineWidget
,
[
'blur'
,
'execute_command'
,
'show_completions'
,
'hide_completions'
])
def
clear
(
self
):
self
.
set_edit_text
(
""
)
...
...
@@ -71,12 +83,148 @@ class CommandLineWidget(urwid.Edit):
if
self
.
history_pos
!=
0
:
self
.
_history_move
(
-
1
)
def
insert_completion
(
self
,
p_insert
):
"""
Inserts currently chosen completion (p_insert parameter) into proper
place in edit_text and adjusts cursor position accordingly.
"""
start
,
end
=
self
.
_surrounding_text
final_text
=
start
+
p_insert
+
end
self
.
set_edit_text
(
final_text
)
self
.
set_edit_pos
(
len
(
start
)
+
len
(
p_insert
))
@
property
def
completion_mode
(
self
):
return
len
(
self
.
completion_box
)
>
1
@
completion_mode
.
setter
def
completion_mode
(
self
,
p_enable
):
if
p_enable
is
True
:
urwid
.
emit_signal
(
self
,
'show_completions'
)
elif
p_enable
is
False
:
self
.
_surrounding_text
=
None
if
self
.
completion_mode
:
self
.
completion_box
.
clear
()
urwid
.
emit_signal
(
self
,
'hide_completions'
)
def
_complete
(
self
):
"""
Main completion function.
Gets list of potential completion candidates for currently edited word,
completes it to the longest common part, and shows convenient completion
widget (if multiple completions are returned) with currently selected
candidate highlighted.
"""
def
find_word_start
(
p_text
,
p_pos
):
""" Returns position of the beginning of a word ending in p_pos. """
return
p_text
.
lstrip
().
rfind
(
' '
,
0
,
p_pos
)
+
1
def
get_word_before_pos
(
p_text
,
p_pos
):
start
=
find_word_start
(
p_text
,
p_pos
)
return
(
p_text
[
start
:
p_pos
],
start
)
pos
=
self
.
edit_pos
text
=
self
.
edit_text
completer
=
self
.
completer
word_before_cursor
,
start
=
get_word_before_pos
(
text
,
pos
)
completions
=
completer
.
get_completions
(
word_before_cursor
,
start
==
0
)
# store slices before and after place for completion
self
.
_surrounding_text
=
(
text
[:
start
],
text
[
pos
:])
single_completion
=
len
(
completions
)
==
1
completion_done
=
single_completion
and
completions
[
0
]
==
word_before_cursor
if
completion_done
or
not
completions
:
self
.
completion_mode
=
False
return
elif
single_completion
:
replacement
=
completions
[
0
]
else
:
replacement
=
commonprefix
(
completions
)
zero_candidate
=
replacement
if
replacement
else
word_before_cursor
if
zero_candidate
!=
completions
[
0
]:
completions
.
insert
(
0
,
zero_candidate
)
self
.
completion_box
.
add_completions
(
completions
)
self
.
insert_completion
(
replacement
)
self
.
completion_mode
=
not
single_completion
def
_completion_move
(
self
,
p_step
,
p_size
):
"""
Visually selects completion specified by p_step (positive numbers
forwards, negative numbers backwards) and inserts it into edit_text.
If p_step results in value out of range of currently evaluated
completion candidates, list is rewinded to the start (if cycling
forwards) or to the end (if cycling backwards).
"""
current_position
=
self
.
completion_box
.
focus_position
try
:
self
.
completion_box
.
set_focus
(
current_position
+
p_step
)
except
IndexError
:
position
=
0
if
p_step
>
0
else
len
(
self
.
completion_box
)
-
1
self
.
completion_box
.
set_focus
(
position
)
maxcols
,
=
p_size
size
=
(
maxcols
,
self
.
completion_box
.
height
)
self
.
completion_box
.
calculate_visible
(
size
)
candidate
=
self
.
completion_box
.
focus
.
original_widget
.
text
self
.
insert_completion
(
candidate
)
def
_prev_completion
(
self
,
p_size
):
if
self
.
completion_mode
:
self
.
_completion_move
(
-
1
,
p_size
)
def
_next_completion
(
self
,
p_size
):
self
.
_completion_move
(
1
,
p_size
)
def
_home
(
self
):
""" Moves cursor to the beginning of the line. """
self
.
set_edit_pos
(
0
)
def
_end
(
self
):
""" Moves cursor to the end of the line. """
end
=
len
(
self
.
edit_text
)
self
.
set_edit_pos
(
end
)
def
_home_del
(
self
):
""" Deletes the line content before the cursor """
text
=
self
.
edit_text
[
self
.
edit_pos
:]
self
.
set_edit_text
(
text
)
self
.
_home
()
def
_end_del
(
self
):
""" Deletes the line content after the cursor """
text
=
self
.
edit_text
[:
self
.
edit_pos
]
self
.
set_edit_text
(
text
)
def
keypress
(
self
,
p_size
,
p_key
):
tab_handler
=
self
.
_complete
if
self
.
completion_mode
:
tab_handler
=
lambda
:
self
.
_next_completion
(
p_size
)
if
p_key
not
in
{
'tab'
,
'shift tab'
}:
self
.
completion_mode
=
False
dispatch
=
{
'enter'
:
self
.
_emit_command
,
'esc'
:
self
.
_blur
,
'up'
:
self
.
_history_back
,
'down'
:
self
.
_history_next
'down'
:
self
.
_history_next
,
'ctrl a'
:
self
.
_home
,
'ctrl e'
:
self
.
_end
,
'ctrl u'
:
self
.
_home_del
,
'ctrl k'
:
self
.
_end_del
,
'tab'
:
tab_handler
,
'shift tab'
:
lambda
:
self
.
_prev_completion
(
p_size
)
}
try
:
...
...
topydo/ui/columns/CompletionBoxWidget.py
0 → 100644
View file @
7ca36b81
# 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/>.
import
urwid
from
topydo.ui.columns.Utils
import
PaletteItem
class
CompletionBoxWidget
(
urwid
.
ListBox
):
def
__init__
(
self
):
self
.
items
=
urwid
.
SimpleFocusListWalker
([])
self
.
min_width
=
0
super
().
__init__
(
self
.
items
)
def
__len__
(
self
):
return
len
(
self
.
items
)
@
property
def
height
(
self
):
""" Returns height of the widget, with maximum set to 4 lines. """
return
min
(
len
(
self
),
4
)
@
property
def
margin
(
self
):
"""
Returns margin for rendering the widget always glued to the cursor.
"""
return
len
(
self
.
items
[
0
].
original_widget
.
text
)
def
add_completions
(
self
,
p_completions
):
"""
Creates proper urwid.Text widgets for all completion candidates from
p_completions list, and populates them into the items attribute.
"""
palette
=
PaletteItem
.
MARKED
for
completion
in
p_completions
:
width
=
len
(
completion
)
if
width
>
self
.
min_width
:
self
.
min_width
=
width
w
=
urwid
.
Text
(
completion
)
self
.
items
.
append
(
urwid
.
AttrMap
(
w
,
None
,
focus_map
=
palette
))
self
.
items
.
set_focus
(
0
)
def
clear
(
self
):
self
.
items
.
clear
()
self
.
min_width
=
0
def
set_focus
(
self
,
p_position
,
p_coming_from
=
None
):
self
.
focus
.
set_attr_map
({
PaletteItem
.
MARKED
:
None
})
super
().
set_focus
(
p_position
,
p_coming_from
)
self
.
focus
.
set_attr_map
({
None
:
PaletteItem
.
MARKED
})
topydo/ui/columns/ConsoleWidget.py
View file @
7ca36b81
...
...
@@ -90,14 +90,6 @@ class ConsoleWidget(urwid.LineBox):
elif
p_key
==
':'
:
urwid
.
emit_signal
(
self
,
'close'
,
True
)
def
render
(
self
,
p_size
,
focus
):
"""
This override intercepts the width of the widget such that it can be
stored. The width is used for rendering `ls` output.
"""
self
.
width
=
p_size
[
0
]
return
super
().
render
(
p_size
,
focus
)
def
selectable
(
self
):
return
True
...
...
@@ -116,7 +108,3 @@ class ConsoleWidget(urwid.LineBox):
def
clear
(
self
):
self
.
pile
.
contents
=
[]
def
console_width
(
self
):
# return the last known width (last render)
return
self
.
width
topydo/ui/columns/Main.py
View file @
7ca36b81
...
...
@@ -26,6 +26,7 @@ from string import ascii_uppercase
from
topydo.Commands
import
get_subcommand
from
topydo.lib.Config
import
config
,
ConfigError
from
topydo.ui.columns.ColumnCompleter
import
ColumnCompleter
from
topydo.lib.Sorter
import
Sorter
from
topydo.lib.Filter
import
get_filter_list
,
RelevanceFilter
,
DependencyFilter
from
topydo.lib.Utils
import
get_terminal_size
...
...
@@ -59,6 +60,21 @@ _COPY_COLUMN = 3
_INSERT_COLUMN
=
4
class
CliWrapper
(
urwid
.
Pile
):
"""
Simple wrapper widget for CommandLineWidget with KeystateWidget and
CompletionBoxWidget rendered dynamically.
"""
def
__init__
(
self
,
*
args
,
**
kwargs
):
self
.
width
=
0
super
().
__init__
(
*
args
,
**
kwargs
)
def
render
(
self
,
p_size
,
focus
):
self
.
width
=
p_size
[
0
]
return
super
().
render
(
p_size
,
focus
)
class
MainPile
(
urwid
.
Pile
):
"""
This subclass of Pile doesn't change focus on cursor up/down / mouse press
...
...
@@ -123,11 +139,13 @@ class UIApplication(CLIApplicationBase):
self
.
columns
=
urwid
.
Columns
([],
dividechars
=
0
,
min_width
=
config
().
column_width
())
self
.
commandline
=
CommandLineWidget
(
'topydo> '
)
completer
=
ColumnCompleter
(
self
.
todolist
)
self
.
commandline
=
CommandLineWidget
(
completer
,
'topydo> '
)
self
.
keystate_widget
=
KeystateWidget
()
self
.
status_line
=
urwid
.
Columns
([
(
'weight'
,
1
,
urwid
.
Filler
(
self
.
commandline
)),
])
self
.
cli_wrapper
=
CliWrapper
([(
1
,
self
.
status_line
)])
self
.
keymap
=
config
().
column_keymap
()
self
.
_alarm
=
None
...
...
@@ -141,6 +159,8 @@ class UIApplication(CLIApplicationBase):
urwid
.
connect_signal
(
self
.
commandline
,
'blur'
,
self
.
_blur_commandline
)
urwid
.
connect_signal
(
self
.
commandline
,
'execute_command'
,
self
.
_execute_handler
)
urwid
.
connect_signal
(
self
.
commandline
,
'show_completions'
,
self
.
_show_completion_box
)
urwid
.
connect_signal
(
self
.
commandline
,
'hide_completions'
,
self
.
_hide_completion_box
)
def
hide_console
(
p_focus_commandline
=
False
):
if
p_focus_commandline
:
...
...
@@ -166,7 +186,7 @@ class UIApplication(CLIApplicationBase):
self
.
mainwindow
=
MainPile
([
(
'weight'
,
1
,
self
.
columns
),
(
1
,
self
.
status_line
),
(
'pack'
,
self
.
cli_wrapper
),
])
urwid
.
connect_signal
(
self
.
mainwindow
,
'blur_console'
,
hide_console
)
...
...
@@ -529,6 +549,33 @@ class UIApplication(CLIApplicationBase):
self
.
keystate_widget
.
set_text
(
p_keystate
)
self
.
_keystate_visible
=
len
(
p_keystate
)
>
0
def
_show_completion_box
(
self
):
contents
=
self
.
cli_wrapper
.
contents
if
len
(
contents
)
==
1
:
completion_box
=
self
.
commandline
.
completion_box
opts
=
(
'given'
,
completion_box
.
height
)
max_width
=
self
.
cli_wrapper
.
width
pos
=
self
.
commandline
.
get_cursor_coords
((
max_width
,))[
0
]
l_margin
=
pos
-
completion_box
.
margin
r_margin
=
max_width
-
pos
-
completion_box
.
min_width
+
completion_box
.
margin
padding
=
urwid
.
Padding
(
completion_box
,
min_width
=
completion_box
.
min_width
,
left
=
l_margin
,
right
=
r_margin
)
contents
.
insert
(
0
,
(
padding
,
opts
))
completion_box
.
focus
.
set_attr_map
({
None
:
PaletteItem
.
MARKED
})
self
.
cli_wrapper
.
focus_position
=
1
def
_hide_completion_box
(
self
):
contents
=
self
.
cli_wrapper
.
contents
if
len
(
contents
)
==
2
:
del
contents
[
0
]
self
.
cli_wrapper
.
focus_position
=
0
def
_set_alarm
(
self
,
p_callback
):
""" Sets alarm to execute p_action specified in 0.5 sec. """
self
.
_alarm
=
self
.
mainloop
.
set_alarm_in
(
0.5
,
p_callback
)
...
...
@@ -622,7 +669,7 @@ class UIApplication(CLIApplicationBase):
def
_console_width
(
self
):
terminal_size
=
namedtuple
(
'Terminal_Size'
,
'columns lines'
)
width
=
self
.
c
onsole
.
console_width
()
-
2
width
=
self
.
c
li_wrapper
.
width
-
2
sz
=
terminal_size
(
width
,
1
)
return
sz
...
...
topydo/ui/prompt/Prompt.py
View file @
7ca36b81
...
...
@@ -21,7 +21,7 @@ import shlex
import
sys
from
topydo.ui.CLIApplicationBase
import
CLIApplicationBase
,
error
,
GENERIC_HELP
from
topydo.ui.prompt.
TopydoCompleter
import
Topydo
Completer
from
topydo.ui.prompt.
PromptCompleter
import
Prompt
Completer
from
prompt_toolkit.shortcuts
import
prompt
from
prompt_toolkit.history
import
InMemoryHistory
...
...
@@ -61,7 +61,7 @@ class PromptApplication(CLIApplicationBase):
"""
self
.
todolist
.
erase
()
self
.
todolist
.
add_list
(
self
.
todofile
.
read
())
self
.
completer
=
Topydo
Completer
(
self
.
todolist
)
self
.
completer
=
Prompt
Completer
(
self
.
todolist
)
def
run
(
self
):
""" Main entry function. """
...
...
topydo/ui/prompt/
Topydo
Completer.py
→
topydo/ui/prompt/
Prompt
Completer.py
View file @
7ca36b81
...
...
@@ -19,94 +19,38 @@ This module provides a completer class that can be used by the prompt provided
by the prompt toolkit.
"""
import
datetime
import
re
from
prompt_toolkit.completion
import
Completer
,
Completion
from
topydo.
Commands
import
_SUBCOMMAND_MAP
from
topydo.
ui.CompleterBase
import
CompleterBase
,
date_suggestions
from
topydo.lib.Config
import
config
from
topydo.lib.RelativeDate
import
relative_date_to_date
def
_subcommands
(
p_word_before_cursor
):
""" Generator for subcommand name completion. """
sc_map
=
config
().
aliases
()
sc_map
.
update
(
_SUBCOMMAND_MAP
)
subcommands
=
[
sc
for
sc
in
sorted
(
sc_map
.
keys
())
if
sc
.
startswith
(
p_word_before_cursor
)]
for
command
in
subcommands
:
yield
Completion
(
command
,
-
len
(
p_word_before_cursor
))
def
_dates
(
p_word_before_cursor
):
""" Generator for date completion. """
def
_date_suggestions
():
"""
Returns a list of relative date that is presented to the user as auto
complete suggestions.
"""
# don't use strftime, prevent locales to kick in
days_of_week
=
{
0
:
"Monday"
,
1
:
"Tuesday"
,
2
:
"Wednesday"
,
3
:
"Thursday"
,
4
:
"Friday"
,
5
:
"Saturday"
,
6
:
"Sunday"
}
dates
=
[
'today'
,
'tomorrow'
,
]
# show days of week up to next week
dow
=
datetime
.
date
.
today
().
weekday
()
for
i
in
range
(
dow
+
2
%
7
,
dow
+
7
):
dates
.
append
(
days_of_week
[
i
%
7
])
# and some more relative days starting from next week
dates
+=
[
"1w"
,
"2w"
,
"1m"
,
"2m"
,
"3m"
,
"1y"
]
return
dates
to_absolute
=
lambda
s
:
relative_date_to_date
(
s
).
isoformat
()
start_value_pos
=
p_word_before_cursor
.
find
(
':'
)
+
1
value
=
p_word_before_cursor
[
start_value_pos
:]
for
reldate
in
_
date_suggestions
():
for
reldate
in
date_suggestions
():
if
not
reldate
.
startswith
(
value
):
continue
yield
Completion
(
reldate
,
-
len
(
value
),
display_meta
=
to_absolute
(
reldate
))
class
TopydoCompleter
(
Completer
):
class
PromptCompleter
(
CompleterBase
,
Completer
):
"""
Completer class that completes projects, contexts, dates and
subcommands.
subcommands
and is compatible with prompt toolkit
.
"""
def
__init__
(
self
,
p_todolist
):
self
.
todolist
=
p_todolist
def
_projects
(
self
,
p_word_before_cursor
):
""" Generator for project completion. """
projects
=
[
p
for
p
in
self
.
todolist
.
projects
()
if
p
.
startswith
(
p_word_before_cursor
[
1
:])]
for
project
in
projects
:
yield
Completion
(
"+"
+
project
,
-
len
(
p_word_before_cursor
))
def
_contexts
(
self
,
p_word_before_cursor
):
""" Generator for context completion. """
contexts
=
[
c
for
c
in
self
.
todolist
.
contexts
()
if
c
.
startswith
(
p_word_before_cursor
[
1
:])]
for
context
in
contexts
:
yield
Completion
(
"@"
+
context
,
-
len
(
p_word_before_cursor
))
def
_completion_generator
(
self
,
p_word
,
is_first_word
):
candidates
=
super
().
get_completions
(
p_word
,
is_first_word
)
for
candidate
in
candidates
:
yield
Completion
(
candidate
,
-
len
(
p_word
))
def
get_completions
(
self
,
p_document
,
_
):
# include all characters except whitespaces (for + and @)
...
...
@@ -114,15 +58,9 @@ class TopydoCompleter(Completer):
is_first_word
=
not
re
.
match
(
r'\
s*
\S+\
s
',
p_document.current_line_before_cursor)
if is_first_word:
return _subcommands(word_before_cursor)
elif word_before_cursor.startswith('
+
'):
return self._projects(word_before_cursor)
elif word_before_cursor.startswith('
@
'):
return self._contexts(word_before_cursor)
elif word_before_cursor.startswith(config().tag_due() + '
:
'):
if word_before_cursor.startswith(config().tag_due() + '
:
'):
return _dates(word_before_cursor)
elif word_before_cursor.startswith(config().tag_start() + '
:
'):
return _dates(word_before_cursor)
return []
else:
return self._completion_generator(word_before_cursor, is_first_word)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment