Skip to content

Commit 31dac27

Browse files
author
Itamar Shalev
committed
feat: Add interactive mode for improved user experience
Implement interactive mode in the tldr client, allowing users to browse and select commands from a list of available pages. Access this mode using the `--interactive` or `-i` flag. This enhancement improves usability and streamlines command discovery. Signed-off-by: Itamar Shalev <[email protected]>
1 parent fe1d796 commit 31dac27

File tree

2 files changed

+125
-4
lines changed

2 files changed

+125
-4
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ options:
6969
-L LANGUAGE, --language LANGUAGE
7070
Override the default language
7171
-m, --markdown Just print the plain page file.
72+
-i, --interactive Start tldr in interactive mode
7273
--short-options Display shortform options over longform
7374
--long-options Display longform options over shortform
7475
--print-completion {bash,zsh,tcsh}

tldr.py

Lines changed: 124 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import sys
55
import os
66
import re
7-
from argparse import ArgumentParser
7+
from argparse import ArgumentParser, Namespace
88
from pathlib import Path
99
from zipfile import ZipFile
1010
from datetime import datetime
@@ -13,11 +13,14 @@
1313
from urllib.parse import quote
1414
from urllib.request import urlopen, Request
1515
from urllib.error import HTTPError, URLError
16-
from termcolor import colored
1716
import ssl
18-
import shtab
1917
import shutil
2018

19+
# Third party imports
20+
from termcolor import colored
21+
import shtab
22+
23+
2124
__version__ = "3.4.1"
2225
__client_specification__ = "2.3"
2326

@@ -571,6 +574,115 @@ def clear_cache(language: Optional[List[str]] = None) -> None:
571574
print(f"No cache directory found for language {language}")
572575

573576

577+
def interactive_mode(options: Namespace) -> None:
578+
"""Interactive mode for browsing/searching tldr pages."""
579+
try:
580+
import readline
581+
except ImportError:
582+
pass
583+
584+
print(colored("tldr interactive mode. Type 'help' for commands, 'quit' to exit.", 'green', attrs=['bold']))
585+
586+
# Use the flags/options provided by the user, not just defaults
587+
# If options.platform is a list (from argparse), flatten it to a string if needed
588+
session_platform = options.platform if options.platform else get_platform_list()
589+
if isinstance(session_platform, list) and len(session_platform) == 1:
590+
session_platform = session_platform
591+
592+
session_language = options.language if options.language else get_language_list()
593+
if isinstance(session_language, list) and len(session_language) == 1:
594+
session_language = session_language
595+
596+
# If user passed --platform or --language, always use those as the starting values
597+
# and allow them to be changed in the session
598+
while True:
599+
try:
600+
user_input = input(colored("tldr> ", 'cyan', attrs=['bold'])).strip()
601+
if not user_input:
602+
continue
603+
parts = user_input.split()
604+
cmd = parts[0].lower()
605+
args = parts[1:]
606+
607+
if cmd in ('quit', 'exit', 'q'):
608+
print("Exiting interactive mode.")
609+
break
610+
elif cmd in ('help', 'h'):
611+
print(
612+
"Commands:\n"
613+
" help Show this help\n"
614+
" list List available commands\n"
615+
" search <term> Search commands\n"
616+
" show <command> Show documentation for a command\n"
617+
" platform [name] Show or set platform\n"
618+
" language [code] Show or set language\n"
619+
" update Update cache\n"
620+
" clear Clear cache\n"
621+
" quit Exit interactive mode\n"
622+
)
623+
elif cmd == 'list':
624+
cmds = get_commands(session_platform, session_language)
625+
if not cmds:
626+
print(colored("No commands found. Try 'update'.", 'red'))
627+
else:
628+
unique = sorted(set(c.split(' (')[0] for c in cmds))
629+
print("Available commands:")
630+
print('\n'.join(' ' + cmd for cmd in unique))
631+
elif cmd == 'search':
632+
if not args:
633+
print("Usage: search <term>")
634+
continue
635+
term = ' '.join(args).lower()
636+
cmds = get_commands(session_platform, session_language)
637+
found = sorted(set(c.split(' (')[0] for c in cmds if term in c.lower()))
638+
if found:
639+
print("Matches:")
640+
print('\n'.join(' ' + cmd for cmd in found))
641+
else:
642+
print(colored("No matches found.", 'yellow'))
643+
elif cmd == 'show':
644+
if not args:
645+
print("Usage: show <command>")
646+
continue
647+
command = '-'.join(args).lower()
648+
results = get_page_for_every_platform(command, None, session_platform, session_language)
649+
if not results:
650+
print(colored(f"No documentation for '{command}'.", 'red'))
651+
else:
652+
output(results[0][0], "long", plain=False)
653+
elif cmd == 'platform':
654+
if args:
655+
platform_arg = args[0].lower()
656+
if platform_arg in OS_DIRECTORIES:
657+
session_platform = [platform_arg]
658+
print(f"Platform set to: {platform_arg}")
659+
else:
660+
print(f"Invalid platform: {platform_arg}.")
661+
print(f"Valid platforms are: {', '.join(OS_DIRECTORIES)}")
662+
else:
663+
print(f"Current platform: {session_platform[0]}")
664+
elif cmd == 'language':
665+
if args:
666+
session_language = [args[0]]
667+
print(f"Language set to: {args[0]}")
668+
else:
669+
print(f"Current language: {session_language[0]}")
670+
elif cmd == 'update':
671+
update_cache(language=session_language)
672+
elif cmd == 'clear':
673+
clear_cache(language=session_language)
674+
else:
675+
command = '-'.join(user_input.split()).lower()
676+
results = get_page_for_every_platform(command, None, session_platform, session_language)
677+
if not results:
678+
print(colored(f"No documentation for '{command}'.", 'red'))
679+
else:
680+
output(results[0][0], "long", plain=False)
681+
except (KeyboardInterrupt, EOFError):
682+
print("\nExiting interactive mode.")
683+
break
684+
685+
574686
def create_parser() -> ArgumentParser:
575687
parser = ArgumentParser(
576688
prog="tldr",
@@ -645,6 +757,10 @@ def create_parser() -> ArgumentParser:
645757
action='store_true',
646758
help='Just print the plain page file.')
647759

760+
parser.add_argument('-i', '--interactive',
761+
action='store_true',
762+
help='Start tldr in interactive mode')
763+
648764
parser.add_argument('--short-options',
649765
default=False,
650766
action="store_true",
@@ -673,7 +789,6 @@ def create_parser() -> ArgumentParser:
673789

674790
def main() -> None:
675791
parser = create_parser()
676-
677792
options = parser.parse_args()
678793

679794
display_option_length = "long"
@@ -696,6 +811,11 @@ def main() -> None:
696811
if options.color is False:
697812
os.environ["FORCE_COLOR"] = "true"
698813

814+
# Call interactive_mode if requested, before any other logic
815+
if options.interactive:
816+
interactive_mode(options)
817+
return
818+
699819
if options.update:
700820
update_cache(language=options.language)
701821
return

0 commit comments

Comments
 (0)