listpick 0.1.14.8__tar.gz → 0.1.14.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of listpick might be problematic. Click here for more details.
- {listpick-0.1.14.8 → listpick-0.1.14.9}/CHANGELOG.md +1 -0
- {listpick-0.1.14.8/src/listpick.egg-info → listpick-0.1.14.9}/PKG-INFO +1 -1
- {listpick-0.1.14.8 → listpick-0.1.14.9}/setup.py +1 -1
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick/listpick_app.py +19 -44
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick/ui/input_field.py +101 -105
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick/ui/keys.py +4 -2
- listpick-0.1.14.9/src/listpick/utils/keycodes.py +88 -0
- listpick-0.1.14.9/src/listpick/utils/user_input.py +104 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9/src/listpick.egg-info}/PKG-INFO +1 -1
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick.egg-info/SOURCES.txt +2 -1
- listpick-0.1.14.8/src/listpick/ui/keycodes.py +0 -70
- {listpick-0.1.14.8 → listpick-0.1.14.9}/.gitignore +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/LICENSE.txt +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/README.md +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/TODO.md +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/assets/aria2tui_screenshot.png +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/assets/file_compare.png +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/assets/lpfman.png +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/examples/data_generation/list_files.toml +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/examples/data_generation/list_files_empty.toml +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/examples/data_generation/video_duplicates.toml +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/examples/data_generation/video_mediainfo.toml +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/examples/input_files/polynomials.tsv +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/examples/input_guides/gnuplot_graph.md +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/examples/picker/auxiallary_files/2024-25_Premier_League.pkl +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/examples/picker/footer_string_example.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/examples/picker/picker_example.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/examples/picker/template.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/examples/picker/wikipedia_table.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/listpick.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/requirements.txt +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/setup.cfg +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick/__init__.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick/__main__.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick/ui/__init__.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick/ui/build_help.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick/ui/footer.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick/ui/git-bugreport-2025-08-16-1438.txt +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick/ui/help_screen.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick/ui/pane_stuff.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick/ui/picker_colours.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick/utils/__init__.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick/utils/clipboard_operations.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick/utils/config.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick/utils/dump.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick/utils/filtering.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick/utils/generate_data.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick/utils/options_selectors.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick/utils/paste_operations.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick/utils/picker_log.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick/utils/search_and_filter_utils.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick/utils/searching.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick/utils/sorting.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick/utils/table_to_list_of_lists.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick/utils/utils.py +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick.egg-info/dependency_links.txt +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick.egg-info/entry_points.txt +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick.egg-info/requires.txt +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/src/listpick.egg-info/top_level.txt +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/tests/kitty_control.sh +0 -0
- {listpick-0.1.14.8 → listpick-0.1.14.9}/tests/sorting_dates.csv +0 -0
|
@@ -24,6 +24,7 @@ Note that the changes between 0.1.11.0 and 1.1.12.0 are listed under 0.1.11
|
|
|
24
24
|
- meta+key (input_field)
|
|
25
25
|
- Added column number to footer
|
|
26
26
|
- Improved splitting of whitespace separated data passed on stdin.
|
|
27
|
+
- Main picker is now refreshed after a resize when there is a notification.
|
|
27
28
|
|
|
28
29
|
## [0.1.14] 2025-08-20
|
|
29
30
|
- Fixed bug when cells are centred vertically.
|
|
@@ -16,7 +16,7 @@ with open("README.md", "r", encoding = "utf-8") as fh:
|
|
|
16
16
|
|
|
17
17
|
setuptools.setup(
|
|
18
18
|
name = "listpick",
|
|
19
|
-
version = "0.1.14.
|
|
19
|
+
version = "0.1.14.9",
|
|
20
20
|
author = "Grim",
|
|
21
21
|
author_email = "grimandgreedy@protonmail.com",
|
|
22
22
|
description = "Listpick is a powerful TUI data tool for creating TUI apps or viewing/comparing tabulated data.",
|
|
@@ -40,6 +40,7 @@ from listpick.utils.dump import dump_state, load_state, dump_data
|
|
|
40
40
|
from listpick.ui.build_help import build_help_rows
|
|
41
41
|
from listpick.ui.footer import StandardFooter, CompactFooter, NoFooter
|
|
42
42
|
from listpick.utils.picker_log import setup_logger
|
|
43
|
+
from listpick.utils.user_input import get_char, open_tty
|
|
43
44
|
|
|
44
45
|
|
|
45
46
|
try:
|
|
@@ -187,6 +188,8 @@ class Picker:
|
|
|
187
188
|
sheet_index = 0,
|
|
188
189
|
sheet_states = [{}],
|
|
189
190
|
|
|
191
|
+
redraw_screen_accessory: Callable = lambda : None,
|
|
192
|
+
|
|
190
193
|
):
|
|
191
194
|
self.stdscr = stdscr
|
|
192
195
|
self.items = items
|
|
@@ -335,6 +338,7 @@ class Picker:
|
|
|
335
338
|
self.sheet_states = sheet_states
|
|
336
339
|
self.sheets = sheets
|
|
337
340
|
|
|
341
|
+
self.redraw_screen_accessory = redraw_screen_accessory
|
|
338
342
|
self.initialise_picker_state(reset_colours=self.reset_colours)
|
|
339
343
|
|
|
340
344
|
# Note: We have to set the footer after initialising the picker state so that the footer can use the get_function_data method
|
|
@@ -747,6 +751,8 @@ class Picker:
|
|
|
747
751
|
|
|
748
752
|
def draw_screen_(self, indexed_items: list[Tuple[int, list[str]]], highlights: list[dict] = [{}], clear: bool = True) -> None:
|
|
749
753
|
""" Draw Picker screen. """
|
|
754
|
+
|
|
755
|
+
self.redraw_screen_accessory()
|
|
750
756
|
self.logger.debug("Draw screen.")
|
|
751
757
|
|
|
752
758
|
if clear:
|
|
@@ -1236,6 +1242,7 @@ class Picker:
|
|
|
1236
1242
|
"sheets": self.sheets,
|
|
1237
1243
|
"sheet_name": self.sheet_name,
|
|
1238
1244
|
"sheet_states": self.sheet_states,
|
|
1245
|
+
"redraw_screen_accessory": self.redraw_screen_accessory,
|
|
1239
1246
|
}
|
|
1240
1247
|
return function_data
|
|
1241
1248
|
|
|
@@ -1270,6 +1277,7 @@ class Picker:
|
|
|
1270
1277
|
"centre_in_terminal_vertical",
|
|
1271
1278
|
"centre_in_cols",
|
|
1272
1279
|
"centre_in_terminal",
|
|
1280
|
+
"redraw_screen_accessory",
|
|
1273
1281
|
]
|
|
1274
1282
|
|
|
1275
1283
|
for var in variables:
|
|
@@ -1409,6 +1417,11 @@ class Picker:
|
|
|
1409
1417
|
|
|
1410
1418
|
submenu_win = curses.newwin(notification_height, notification_width, 3, w - (notification_width+4))
|
|
1411
1419
|
# submenu_win = self.stdscr.subwin(notification_height, notification_width, 3, w - (notification_width+4))
|
|
1420
|
+
def update_after_resize():
|
|
1421
|
+
h, w = self.stdscr.getmaxyx()
|
|
1422
|
+
submenu_win.mvwin(3, w - (notification_width+4))
|
|
1423
|
+
self.draw_screen(self.indexed_items, self.highlights)
|
|
1424
|
+
|
|
1412
1425
|
notification_data = {
|
|
1413
1426
|
"items": submenu_items,
|
|
1414
1427
|
"title": title,
|
|
@@ -1430,9 +1443,14 @@ class Picker:
|
|
|
1430
1443
|
"loaded_file": "",
|
|
1431
1444
|
"loaded_file_index": 0,
|
|
1432
1445
|
"cell_cursor": False,
|
|
1446
|
+
# "redraw_screen_accessory": lambda : self.draw_screen(self.indexed_items, self.highlights),
|
|
1447
|
+
"redraw_screen_accessory": update_after_resize,
|
|
1448
|
+
"get_new_data": False,
|
|
1449
|
+
# "key_remappings": notification_remap_keys,
|
|
1433
1450
|
}
|
|
1434
1451
|
OptionPicker = Picker(submenu_win, **notification_data)
|
|
1435
1452
|
s, o, f = OptionPicker.run()
|
|
1453
|
+
os.system(f"notify-send resizing")
|
|
1436
1454
|
|
|
1437
1455
|
if o != "refresh": break
|
|
1438
1456
|
submenu_win.clear()
|
|
@@ -1441,6 +1459,7 @@ class Picker:
|
|
|
1441
1459
|
stdscr.clear()
|
|
1442
1460
|
stdscr.refresh()
|
|
1443
1461
|
self.draw_screen(self.indexed_items, self.highlights)
|
|
1462
|
+
|
|
1444
1463
|
# set_colours(colours=get_colours(0))
|
|
1445
1464
|
|
|
1446
1465
|
def toggle_column_visibility(self, col_index:int) -> None:
|
|
@@ -3580,50 +3599,6 @@ def unrestrict_curses(stdscr: curses.window) -> None:
|
|
|
3580
3599
|
curses.curs_set(False)
|
|
3581
3600
|
|
|
3582
3601
|
|
|
3583
|
-
def open_tty():
|
|
3584
|
-
""" Return a file descriptor for the tty that we are opening"""
|
|
3585
|
-
tty_fd = os.open('/dev/tty', os.O_RDONLY)
|
|
3586
|
-
tty.setraw(tty_fd)
|
|
3587
|
-
return tty_fd
|
|
3588
|
-
|
|
3589
|
-
def get_char(tty_fd, timeout: float = 0.2, secondary: bool = False) -> int:
|
|
3590
|
-
""" Get character from a tty_fd with a timeout. """
|
|
3591
|
-
rlist, _, _ = select.select([tty_fd], [], [], timeout)
|
|
3592
|
-
if rlist:
|
|
3593
|
-
# key = ord(tty_fd.read(1))
|
|
3594
|
-
key = ord(os.read(tty_fd, 1))
|
|
3595
|
-
if not secondary:
|
|
3596
|
-
if key == 27:
|
|
3597
|
-
key2 = get_char(tty_fd, timeout=0.01, secondary=True)
|
|
3598
|
-
key3 = get_char(tty_fd, timeout=0.01, secondary=True)
|
|
3599
|
-
key4 = get_char(tty_fd, timeout=0.01, secondary=True)
|
|
3600
|
-
key5 = get_char(tty_fd, timeout=0.01, secondary=True)
|
|
3601
|
-
if key2 == ord('O') and key3 == ord('B'):
|
|
3602
|
-
key = curses.KEY_DOWN
|
|
3603
|
-
elif key2 == ord('O') and key3 == ord('A'):
|
|
3604
|
-
key = curses.KEY_UP
|
|
3605
|
-
elif key2 == ord('O') and key3 == ord('D'):
|
|
3606
|
-
key = curses.KEY_LEFT
|
|
3607
|
-
elif key2 == ord('O') and key3 == ord('C'):
|
|
3608
|
-
key = curses.KEY_RIGHT
|
|
3609
|
-
elif key2 == ord('[') and key3 == ord('Z'):
|
|
3610
|
-
key = 353
|
|
3611
|
-
elif key2 == ord('O') and key3 == ord('F'):
|
|
3612
|
-
key = curses.KEY_END
|
|
3613
|
-
elif key2 == ord('O') and key3 == ord('H'):
|
|
3614
|
-
key = curses.KEY_HOME
|
|
3615
|
-
elif key2 == ord('[') and key3 == ord('3') and key4 == ord('~'):
|
|
3616
|
-
key = curses.KEY_DC
|
|
3617
|
-
elif key2 == ord('[') and key3 == ord('3') and key4 == ord('~'):
|
|
3618
|
-
key = curses.KEY_DC
|
|
3619
|
-
elif key2 == ord('O') and key3 == ord('P'):
|
|
3620
|
-
key = curses.KEY_F1
|
|
3621
|
-
elif key2 == ord('[') and key3 == ord('1') and key4 == ord('5') and key5 == ord('~'):
|
|
3622
|
-
key = curses.KEY_F5
|
|
3623
|
-
|
|
3624
|
-
else:
|
|
3625
|
-
key = -1
|
|
3626
|
-
return key
|
|
3627
3602
|
|
|
3628
3603
|
def main() -> None:
|
|
3629
3604
|
""" Main function when listpick is executed. Deals with command line arguments and starts a Picker. """
|
|
@@ -18,22 +18,24 @@ import logging
|
|
|
18
18
|
logger = logging.getLogger('picker_log')
|
|
19
19
|
import select
|
|
20
20
|
import tty
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
21
|
+
from listpick.utils.user_input import get_char, open_tty
|
|
22
|
+
from listpick.utils import keycodes
|
|
23
|
+
|
|
24
|
+
# def open_tty():
|
|
25
|
+
# """ Return a file descriptor for the tty that we are opening"""
|
|
26
|
+
# tty_fd = os.open('/dev/tty', os.O_RDONLY)
|
|
27
|
+
# tty.setraw(tty_fd)
|
|
28
|
+
# return tty_fd
|
|
29
|
+
#
|
|
30
|
+
# def get_char(tty_fd, timeout: float = 0.2) -> int:
|
|
31
|
+
# """ Get character from a tty_fd with a timeout. """
|
|
32
|
+
# rlist, _, _ = select.select([tty_fd], [], [], timeout)
|
|
33
|
+
# if rlist:
|
|
34
|
+
# # key = ord(tty_fd.read(1))
|
|
35
|
+
# key = ord(os.read(tty_fd, 1))
|
|
36
|
+
# else:
|
|
37
|
+
# key = -1
|
|
38
|
+
# return key
|
|
37
39
|
|
|
38
40
|
def input_field(
|
|
39
41
|
stdscr: curses.window,
|
|
@@ -230,100 +232,94 @@ def input_field(
|
|
|
230
232
|
key = get_char(tty_fd, timeout=0.5)
|
|
231
233
|
# key = stdscr.getch()
|
|
232
234
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
for word_separator_char in word_separator_chars:
|
|
247
|
-
tmp_index = search_txt[::-1].find(word_separator_char)
|
|
248
|
-
if tmp_index > -1:
|
|
249
|
-
if index == -1:
|
|
250
|
-
index = tmp_index
|
|
251
|
-
else:
|
|
252
|
-
index = min(index, tmp_index)
|
|
253
|
-
|
|
254
|
-
if index == -1:
|
|
255
|
-
if cursor == 0:
|
|
256
|
-
kill_ring.append(usrtxt)
|
|
257
|
-
usrtxt = ""
|
|
258
|
-
else:
|
|
259
|
-
kill_ring.append(usrtxt[:-(cursor+1)])
|
|
260
|
-
usrtxt = usrtxt[-(cursor+1):]
|
|
261
|
-
cursor = len(usrtxt)
|
|
262
|
-
else:
|
|
263
|
-
if index == 0:
|
|
264
|
-
kill_ring.append(search_txt[-1:])
|
|
265
|
-
usrtxt = search_txt[:-1] + usrtxt[len(search_txt):]
|
|
235
|
+
|
|
236
|
+
if key in [27, 7]: # ESC/ALT key or Ctrl+g
|
|
237
|
+
return "", False
|
|
238
|
+
|
|
239
|
+
elif key == keycodes.META_BS:
|
|
240
|
+
# Delete to backslash or space (word_separator_chars)
|
|
241
|
+
search_txt = usrtxt[:-cursor] if cursor > 0 else usrtxt
|
|
242
|
+
index = -1
|
|
243
|
+
for word_separator_char in word_separator_chars:
|
|
244
|
+
tmp_index = search_txt[::-1].find(word_separator_char)
|
|
245
|
+
if tmp_index > -1:
|
|
246
|
+
if index == -1:
|
|
247
|
+
index = tmp_index
|
|
266
248
|
else:
|
|
267
|
-
|
|
268
|
-
usrtxt = search_txt[:-index] + usrtxt[len(search_txt):]
|
|
249
|
+
index = min(index, tmp_index)
|
|
269
250
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
# Forward word
|
|
275
|
-
search_txt = usrtxt[-cursor:]
|
|
276
|
-
index = -1
|
|
277
|
-
for word_separator_char in word_separator_chars:
|
|
278
|
-
tmp_index = search_txt.find(word_separator_char)
|
|
279
|
-
|
|
280
|
-
if tmp_index > -1:
|
|
281
|
-
if index == -1:
|
|
282
|
-
index = tmp_index
|
|
283
|
-
else:
|
|
284
|
-
index = min(index, tmp_index)
|
|
285
|
-
|
|
286
|
-
if index == -1:
|
|
287
|
-
cursor = 0
|
|
251
|
+
if index == -1:
|
|
252
|
+
if cursor == 0:
|
|
253
|
+
kill_ring.append(usrtxt)
|
|
254
|
+
usrtxt = ""
|
|
288
255
|
else:
|
|
289
|
-
cursor
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
index = -1
|
|
297
|
-
for word_separator_char in word_separator_chars:
|
|
298
|
-
tmp_index = search_txt[::-1].find(word_separator_char)
|
|
299
|
-
|
|
300
|
-
if tmp_index == 0:
|
|
301
|
-
tmp_index = search_txt[:-1][::-1].find(word_separator_char)
|
|
302
|
-
|
|
303
|
-
if tmp_index > -1:
|
|
304
|
-
if index == -1:
|
|
305
|
-
index = tmp_index
|
|
306
|
-
else:
|
|
307
|
-
index = min(index, tmp_index)
|
|
308
|
-
|
|
309
|
-
if index == -1:
|
|
310
|
-
cursor = len(usrtxt)
|
|
256
|
+
kill_ring.append(usrtxt[:-(cursor+1)])
|
|
257
|
+
usrtxt = usrtxt[-(cursor+1):]
|
|
258
|
+
cursor = len(usrtxt)
|
|
259
|
+
else:
|
|
260
|
+
if index == 0:
|
|
261
|
+
kill_ring.append(search_txt[-1:])
|
|
262
|
+
usrtxt = search_txt[:-1] + usrtxt[len(search_txt):]
|
|
311
263
|
else:
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
264
|
+
kill_ring.append(search_txt[-index:])
|
|
265
|
+
usrtxt = search_txt[:-index] + usrtxt[len(search_txt):]
|
|
266
|
+
|
|
267
|
+
potential_path = usrtxt
|
|
268
|
+
kill_ring_active = False
|
|
269
|
+
|
|
270
|
+
elif key == keycodes.META_f:
|
|
271
|
+
# Forward word
|
|
272
|
+
search_txt = usrtxt[-cursor:]
|
|
273
|
+
index = -1
|
|
274
|
+
for word_separator_char in word_separator_chars:
|
|
275
|
+
tmp_index = search_txt.find(word_separator_char)
|
|
276
|
+
|
|
277
|
+
if tmp_index > -1:
|
|
278
|
+
if index == -1:
|
|
279
|
+
index = tmp_index
|
|
280
|
+
else:
|
|
281
|
+
index = min(index, tmp_index)
|
|
282
|
+
|
|
283
|
+
if index == -1:
|
|
284
|
+
cursor = 0
|
|
285
|
+
else:
|
|
286
|
+
cursor -= index + 1
|
|
287
|
+
cursor = max(cursor, 0)
|
|
288
|
+
kill_ring_active = False
|
|
289
|
+
|
|
290
|
+
elif key == keycodes.META_b:
|
|
291
|
+
# Backwards word
|
|
292
|
+
search_txt = usrtxt[:-cursor] if cursor > 0 else usrtxt
|
|
293
|
+
index = -1
|
|
294
|
+
for word_separator_char in word_separator_chars:
|
|
295
|
+
tmp_index = search_txt[::-1].find(word_separator_char)
|
|
296
|
+
|
|
297
|
+
if tmp_index == 0:
|
|
298
|
+
tmp_index = search_txt[:-1][::-1].find(word_separator_char)
|
|
299
|
+
|
|
300
|
+
if tmp_index > -1:
|
|
301
|
+
if index == -1:
|
|
302
|
+
index = tmp_index
|
|
322
303
|
else:
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
304
|
+
index = min(index, tmp_index)
|
|
305
|
+
|
|
306
|
+
if index == -1:
|
|
307
|
+
cursor = len(usrtxt)
|
|
308
|
+
else:
|
|
309
|
+
cursor += index + 1
|
|
310
|
+
kill_ring_active = False
|
|
311
|
+
|
|
312
|
+
elif key == keycodes.META_y:
|
|
313
|
+
# Kill ring
|
|
314
|
+
prev_kill_ring_index = kill_ring_index
|
|
315
|
+
kill_ring_index = (kill_ring_index + 1)%len(kill_ring)
|
|
316
|
+
if kill_ring_active and len(kill_ring):
|
|
317
|
+
if cursor == 0:
|
|
318
|
+
usrtxt = usrtxt[:-len(kill_ring[prev_kill_ring_index])]
|
|
319
|
+
usrtxt += kill_ring[kill_ring_index]
|
|
320
|
+
else:
|
|
321
|
+
usrtxt = usrtxt[-cursor:-(cursor+len(kill_ring[prev_kill_ring_index]))]
|
|
322
|
+
usrtxt = usrtxt[:-cursor] + kill_ring[kill_ring_index] + usrtxt[-cursor:]
|
|
327
323
|
|
|
328
324
|
elif key == 3: # ctrl+c
|
|
329
325
|
# Immediate exit
|
|
@@ -9,6 +9,7 @@ License: MIT
|
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
11
|
import curses
|
|
12
|
+
from listpick.utils import keycodes
|
|
12
13
|
|
|
13
14
|
picker_keys = {
|
|
14
15
|
"refresh": [curses.KEY_F5],
|
|
@@ -25,8 +26,8 @@ picker_keys = {
|
|
|
25
26
|
"page_down": [curses.KEY_NPAGE, 6], # Ctrl+f
|
|
26
27
|
"cursor_bottom": [ord('G'), curses.KEY_END],
|
|
27
28
|
"cursor_top": [ord('g'), curses.KEY_HOME],
|
|
28
|
-
"five_up": [ord('K')],
|
|
29
|
-
"five_down": [ord('J')],
|
|
29
|
+
"five_up": [ord('K'), keycodes.META_k],
|
|
30
|
+
"five_down": [ord('J'), keycodes.META_j],
|
|
30
31
|
"toggle_select": [ord(' ')],
|
|
31
32
|
"select_all": [ord('m'), 1], # Ctrl-a
|
|
32
33
|
"select_none": [ord('M'), 18], # Ctrl-r
|
|
@@ -135,6 +136,7 @@ notification_keys = {
|
|
|
135
136
|
"five_up": [ord('K')],
|
|
136
137
|
"five_down": [ord('J')],
|
|
137
138
|
"redraw_screen": [12], # Ctrl-l
|
|
139
|
+
"refresh": [curses.KEY_F5, curses.KEY_RESIZE],
|
|
138
140
|
}
|
|
139
141
|
|
|
140
142
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Set a base value to ensure that we have no collisions with any unicode characters
|
|
2
|
+
BASE_VALUE = 2**32
|
|
3
|
+
|
|
4
|
+
# Define constants for alt+a to alt+z
|
|
5
|
+
META_A = BASE_VALUE + 1000
|
|
6
|
+
META_B = BASE_VALUE + 1001
|
|
7
|
+
META_C = BASE_VALUE + 1002
|
|
8
|
+
META_D = BASE_VALUE + 1003
|
|
9
|
+
META_E = BASE_VALUE + 1004
|
|
10
|
+
META_F = BASE_VALUE + 1005
|
|
11
|
+
META_G = BASE_VALUE + 1006
|
|
12
|
+
META_H = BASE_VALUE + 1007
|
|
13
|
+
META_I = BASE_VALUE + 1008
|
|
14
|
+
META_J = BASE_VALUE + 1009
|
|
15
|
+
META_K = BASE_VALUE + 1010
|
|
16
|
+
META_L = BASE_VALUE + 1011
|
|
17
|
+
META_M = BASE_VALUE + 1012
|
|
18
|
+
META_N = BASE_VALUE + 1013
|
|
19
|
+
META_O = BASE_VALUE + 1014
|
|
20
|
+
META_P = BASE_VALUE + 1015
|
|
21
|
+
META_Q = BASE_VALUE + 1016
|
|
22
|
+
META_R = BASE_VALUE + 1017
|
|
23
|
+
META_S = BASE_VALUE + 1018
|
|
24
|
+
META_T = BASE_VALUE + 1019
|
|
25
|
+
META_U = BASE_VALUE + 1020
|
|
26
|
+
META_V = BASE_VALUE + 1021
|
|
27
|
+
META_W = BASE_VALUE + 1022
|
|
28
|
+
META_X = BASE_VALUE + 1023
|
|
29
|
+
META_Y = BASE_VALUE + 1024
|
|
30
|
+
META_Z = BASE_VALUE + 1025
|
|
31
|
+
|
|
32
|
+
# Define constants for alt+A to alt+Z (using uppercase)
|
|
33
|
+
META_a = BASE_VALUE + 1050
|
|
34
|
+
META_b = BASE_VALUE + 1051
|
|
35
|
+
META_c = BASE_VALUE + 1052
|
|
36
|
+
META_d = BASE_VALUE + 1053
|
|
37
|
+
META_e = BASE_VALUE + 1054
|
|
38
|
+
META_f = BASE_VALUE + 1055
|
|
39
|
+
META_g = BASE_VALUE + 1056
|
|
40
|
+
META_h = BASE_VALUE + 1057
|
|
41
|
+
META_i = BASE_VALUE + 1058
|
|
42
|
+
META_j = BASE_VALUE + 1059
|
|
43
|
+
META_k = BASE_VALUE + 1060
|
|
44
|
+
META_l = BASE_VALUE + 1061
|
|
45
|
+
META_m = BASE_VALUE + 1062
|
|
46
|
+
META_n = BASE_VALUE + 1063
|
|
47
|
+
META_o = BASE_VALUE + 1064
|
|
48
|
+
META_p = BASE_VALUE + 1065
|
|
49
|
+
META_q = BASE_VALUE + 1066
|
|
50
|
+
META_r = BASE_VALUE + 1067
|
|
51
|
+
META_s = BASE_VALUE + 1068
|
|
52
|
+
META_t = BASE_VALUE + 1069
|
|
53
|
+
META_u = BASE_VALUE + 1070
|
|
54
|
+
META_v = BASE_VALUE + 1071
|
|
55
|
+
META_w = BASE_VALUE + 1072
|
|
56
|
+
META_x = BASE_VALUE + 1073
|
|
57
|
+
META_y = BASE_VALUE + 1074
|
|
58
|
+
META_z = BASE_VALUE + 1075
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
META_0 = BASE_VALUE + 1090
|
|
62
|
+
META_1 = BASE_VALUE + 1091
|
|
63
|
+
META_2 = BASE_VALUE + 1092
|
|
64
|
+
META_3 = BASE_VALUE + 1093
|
|
65
|
+
META_4 = BASE_VALUE + 1094
|
|
66
|
+
META_5 = BASE_VALUE + 1095
|
|
67
|
+
META_6 = BASE_VALUE + 1096
|
|
68
|
+
META_7 = BASE_VALUE + 1097
|
|
69
|
+
META_8 = BASE_VALUE + 1098
|
|
70
|
+
META_9 = BASE_VALUE + 1099
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
META_BS = BASE_VALUE + 1100
|
|
74
|
+
|
|
75
|
+
# Dictionary to map character to its constant value
|
|
76
|
+
META_KEY_MAP = {
|
|
77
|
+
'a': META_a, 'b': META_b, 'c': META_c, 'd': META_d, 'e': META_e,
|
|
78
|
+
'f': META_f, 'g': META_g, 'h': META_h, 'i': META_i, 'j': META_j,
|
|
79
|
+
'k': META_k, 'l': META_l, 'm': META_m, 'n': META_n, 'o': META_o,
|
|
80
|
+
'p': META_p, 'q': META_q, 'r': META_r, 's': META_s, 't': META_t,
|
|
81
|
+
'u': META_u, 'v': META_v, 'w': META_w, 'x': META_x, 'y': META_y,
|
|
82
|
+
'z': META_z, 'A': META_A, 'B': META_B, 'C': META_C, 'D': META_D,
|
|
83
|
+
'E': META_E, 'F': META_F, 'G': META_G, 'H': META_H, 'I': META_I,
|
|
84
|
+
'J': META_J, 'K': META_K, 'L': META_L, 'M': META_M, 'N': META_N,
|
|
85
|
+
'O': META_O, 'P': META_P, 'Q': META_Q, 'R': META_R, 'S': META_S,
|
|
86
|
+
'T': META_T, 'U': META_U, 'V': META_V, 'W': META_W, 'X': META_X,
|
|
87
|
+
'Y': META_Y, 'Z': META_Z
|
|
88
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
from listpick.utils import keycodes
|
|
2
|
+
import os, tty, select, curses
|
|
3
|
+
|
|
4
|
+
def open_tty():
|
|
5
|
+
""" Return a file descriptor for the tty that we are opening"""
|
|
6
|
+
tty_fd = os.open('/dev/tty', os.O_RDONLY)
|
|
7
|
+
tty.setraw(tty_fd)
|
|
8
|
+
return tty_fd
|
|
9
|
+
|
|
10
|
+
def get_char(tty_fd, timeout: float = 0.2, secondary: bool = False) -> int:
|
|
11
|
+
""" Get character from a tty_fd with a timeout. """
|
|
12
|
+
rlist, _, _ = select.select([tty_fd], [], [], timeout)
|
|
13
|
+
if rlist:
|
|
14
|
+
# key = ord(tty_fd.read(1))
|
|
15
|
+
key = ord(os.read(tty_fd, 1))
|
|
16
|
+
if not secondary:
|
|
17
|
+
if key == 27:
|
|
18
|
+
key2 = get_char(tty_fd, timeout=0.01, secondary=True)
|
|
19
|
+
key3 = get_char(tty_fd, timeout=0.01, secondary=True)
|
|
20
|
+
key4 = get_char(tty_fd, timeout=0.01, secondary=True)
|
|
21
|
+
key5 = get_char(tty_fd, timeout=0.01, secondary=True)
|
|
22
|
+
key6 = get_char(tty_fd, timeout=0.01, secondary=True)
|
|
23
|
+
|
|
24
|
+
keys = [key2, key3, key4, key5, key6]
|
|
25
|
+
|
|
26
|
+
key_str = "".join([chr(k) for k in keys if k != -1])
|
|
27
|
+
|
|
28
|
+
## Arrow Keys
|
|
29
|
+
if key2 == ord('O') and key3 == ord('B'):
|
|
30
|
+
key = curses.KEY_DOWN
|
|
31
|
+
elif key2 == ord('O') and key3 == ord('A'):
|
|
32
|
+
key = curses.KEY_UP
|
|
33
|
+
elif key2 == ord('O') and key3 == ord('D'):
|
|
34
|
+
key = curses.KEY_LEFT
|
|
35
|
+
elif key2 == ord('O') and key3 == ord('C'):
|
|
36
|
+
key = curses.KEY_RIGHT
|
|
37
|
+
|
|
38
|
+
## Shift+ Tab
|
|
39
|
+
elif key2 == ord('[') and key3 == ord('Z'):
|
|
40
|
+
key = 353
|
|
41
|
+
|
|
42
|
+
## Home, End, Pgup, Pgdn
|
|
43
|
+
elif key2 == ord('O') and key3 == ord('F'):
|
|
44
|
+
key = curses.KEY_END
|
|
45
|
+
elif key2 == ord('O') and key3 == ord('H'):
|
|
46
|
+
key = curses.KEY_HOME
|
|
47
|
+
elif key2 == ord('[') and key3 == ord('6') and key4 == ord("~"):
|
|
48
|
+
key = curses.KEY_NPAGE
|
|
49
|
+
elif key2 == ord('[') and key3 == ord('5') and key4 == ord("~"):
|
|
50
|
+
key = curses.KEY_PPAGE
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# Delete key
|
|
54
|
+
elif key_str == "[3~": ## Delete
|
|
55
|
+
key = curses.KEY_DC
|
|
56
|
+
elif key_str == "[3;2~": ## Shift+Delete
|
|
57
|
+
key = 383
|
|
58
|
+
|
|
59
|
+
## Function Keys
|
|
60
|
+
elif key2 == ord('O') and key3 == ord('P'):
|
|
61
|
+
key = curses.KEY_F1
|
|
62
|
+
elif key_str == "OQ":
|
|
63
|
+
key = curses.KEY_F2
|
|
64
|
+
elif key_str == "OR":
|
|
65
|
+
key = curses.KEY_F3
|
|
66
|
+
elif key_str == "OS":
|
|
67
|
+
key = curses.KEY_F4
|
|
68
|
+
elif key_str == "[15~":
|
|
69
|
+
key = curses.KEY_F5
|
|
70
|
+
elif key_str == "[17~":
|
|
71
|
+
key = curses.KEY_F6
|
|
72
|
+
elif key_str == "[17~":
|
|
73
|
+
key = curses.KEY_F7
|
|
74
|
+
elif key_str == "[19~":
|
|
75
|
+
key = curses.KEY_F8
|
|
76
|
+
elif key_str == "[20~":
|
|
77
|
+
key = curses.KEY_F9
|
|
78
|
+
elif key_str == "[21~":
|
|
79
|
+
key = curses.KEY_F10
|
|
80
|
+
elif key_str == "[23~":
|
|
81
|
+
key = curses.KEY_F11
|
|
82
|
+
elif key_str == "[24~":
|
|
83
|
+
key = curses.KEY_F12
|
|
84
|
+
|
|
85
|
+
## Alt+KEY
|
|
86
|
+
elif key2 >= ord('a') and key2 <= ord('z') and key3 == -1: ## Alt+[a-zA-Z]
|
|
87
|
+
key = keycodes.META_a + (key2 - ord('a'))
|
|
88
|
+
elif key2 >= ord('A') and key2 <= ord('Z') and key3 == -1: ## Alt+[a-zA-Z]
|
|
89
|
+
key = keycodes.META_A + (key2 - ord('A'))
|
|
90
|
+
elif key2 == ord('0') and key3 == -1: ## Alt+0
|
|
91
|
+
key = keycodes.META_0
|
|
92
|
+
elif key2 >= ord('1') and key2 <= ord('9') and key3 == -1: ## Alt+1-9
|
|
93
|
+
key = keycodes.META_1 + (key2 - ord('1'))
|
|
94
|
+
elif key2 == 127: ## Alt+BS
|
|
95
|
+
key = keycodes.META_BS
|
|
96
|
+
|
|
97
|
+
# If it is an unknown key with an escape sequence then return -1.
|
|
98
|
+
elif key2 != -1:
|
|
99
|
+
key = -1
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
else:
|
|
103
|
+
key = -1
|
|
104
|
+
return key
|
|
@@ -35,7 +35,6 @@ src/listpick/ui/footer.py
|
|
|
35
35
|
src/listpick/ui/git-bugreport-2025-08-16-1438.txt
|
|
36
36
|
src/listpick/ui/help_screen.py
|
|
37
37
|
src/listpick/ui/input_field.py
|
|
38
|
-
src/listpick/ui/keycodes.py
|
|
39
38
|
src/listpick/ui/keys.py
|
|
40
39
|
src/listpick/ui/pane_stuff.py
|
|
41
40
|
src/listpick/ui/picker_colours.py
|
|
@@ -45,6 +44,7 @@ src/listpick/utils/config.py
|
|
|
45
44
|
src/listpick/utils/dump.py
|
|
46
45
|
src/listpick/utils/filtering.py
|
|
47
46
|
src/listpick/utils/generate_data.py
|
|
47
|
+
src/listpick/utils/keycodes.py
|
|
48
48
|
src/listpick/utils/options_selectors.py
|
|
49
49
|
src/listpick/utils/paste_operations.py
|
|
50
50
|
src/listpick/utils/picker_log.py
|
|
@@ -52,6 +52,7 @@ src/listpick/utils/search_and_filter_utils.py
|
|
|
52
52
|
src/listpick/utils/searching.py
|
|
53
53
|
src/listpick/utils/sorting.py
|
|
54
54
|
src/listpick/utils/table_to_list_of_lists.py
|
|
55
|
+
src/listpick/utils/user_input.py
|
|
55
56
|
src/listpick/utils/utils.py
|
|
56
57
|
tests/kitty_control.sh
|
|
57
58
|
tests/sorting_dates.csv
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
# Define constants for alt+a to alt+z
|
|
2
|
-
META_A = 1000
|
|
3
|
-
META_B = 1001
|
|
4
|
-
META_C = 1002
|
|
5
|
-
META_D = 1003
|
|
6
|
-
META_E = 1004
|
|
7
|
-
META_F = 1005
|
|
8
|
-
META_G = 1006
|
|
9
|
-
META_H = 1007
|
|
10
|
-
META_I = 1008
|
|
11
|
-
META_J = 1009
|
|
12
|
-
META_K = 1010
|
|
13
|
-
META_L = 1011
|
|
14
|
-
META_M = 1012
|
|
15
|
-
META_N = 1013
|
|
16
|
-
META_O = 1014
|
|
17
|
-
META_P = 1015
|
|
18
|
-
META_Q = 1016
|
|
19
|
-
META_R = 1017
|
|
20
|
-
META_S = 1018
|
|
21
|
-
META_T = 1019
|
|
22
|
-
META_U = 1020
|
|
23
|
-
META_V = 1021
|
|
24
|
-
META_W = 1022
|
|
25
|
-
META_X = 1023
|
|
26
|
-
META_Y = 1024
|
|
27
|
-
META_Z = 1025
|
|
28
|
-
|
|
29
|
-
# Define constants for alt+A to alt+Z (using uppercase)
|
|
30
|
-
META_a = 1050
|
|
31
|
-
META_b = 1051
|
|
32
|
-
META_c = 1052
|
|
33
|
-
META_d = 1053
|
|
34
|
-
META_e = 1054
|
|
35
|
-
META_f = 1055
|
|
36
|
-
META_g = 1056
|
|
37
|
-
META_h = 1057
|
|
38
|
-
META_i = 1058
|
|
39
|
-
META_j = 1059
|
|
40
|
-
META_k = 1060
|
|
41
|
-
META_l = 1061
|
|
42
|
-
META_m = 1062
|
|
43
|
-
META_n = 1063
|
|
44
|
-
META_o = 1064
|
|
45
|
-
META_p = 1065
|
|
46
|
-
META_q = 1066
|
|
47
|
-
META_r = 1067
|
|
48
|
-
META_s = 1068
|
|
49
|
-
META_t = 1069
|
|
50
|
-
META_u = 1070
|
|
51
|
-
META_v = 1071
|
|
52
|
-
META_w = 1072
|
|
53
|
-
META_x = 1073
|
|
54
|
-
META_y = 1074
|
|
55
|
-
META_z = 1075
|
|
56
|
-
|
|
57
|
-
# Dictionary to map character to its constant value
|
|
58
|
-
META_KEY_MAP = {
|
|
59
|
-
'a': META_a, 'b': META_b, 'c': META_c, 'd': META_d, 'e': META_e,
|
|
60
|
-
'f': META_f, 'g': META_g, 'h': META_h, 'i': META_i, 'j': META_j,
|
|
61
|
-
'k': META_k, 'l': META_l, 'm': META_m, 'n': META_n, 'o': META_o,
|
|
62
|
-
'p': META_p, 'q': META_q, 'r': META_r, 's': META_s, 't': META_t,
|
|
63
|
-
'u': META_u, 'v': META_v, 'w': META_w, 'x': META_x, 'y': META_y,
|
|
64
|
-
'z': META_z, 'A': META_A, 'B': META_B, 'C': META_C, 'D': META_D,
|
|
65
|
-
'E': META_E, 'F': META_F, 'G': META_G, 'H': META_H, 'I': META_I,
|
|
66
|
-
'J': META_J, 'K': META_K, 'L': META_L, 'M': META_M, 'N': META_N,
|
|
67
|
-
'O': META_O, 'P': META_P, 'Q': META_Q, 'R': META_R, 'S': META_S,
|
|
68
|
-
'T': META_T, 'U': META_U, 'V': META_V, 'W': META_W, 'X': META_X,
|
|
69
|
-
'Y': META_Y, 'Z': META_Z
|
|
70
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{listpick-0.1.14.8 → listpick-0.1.14.9}/examples/picker/auxiallary_files/2024-25_Premier_League.pkl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|