diff --git a/README.md b/README.md index 53a87ba..fbf18b5 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ options: -L LANGUAGE, --language LANGUAGE Override the default language -m, --markdown Just print the plain page file. + -i, --interactive Start tldr in interactive mode --short-options Display shortform options over longform --long-options Display longform options over shortform --print-completion {bash,zsh,tcsh} @@ -91,8 +92,25 @@ export TLDR_CACHE_MAX_AGE=720 export TLDR_PAGES_SOURCE_LOCATION="https://raw.githubusercontent.com/tldr-pages/tldr/main/pages" export TLDR_DOWNLOAD_CACHE_LOCATION="https://github.com/tldr-pages/tldr/releases/latest/download/tldr.zip" export TLDR_OPTIONS=short +export TLDR_PLATFORM=linux ``` +### Platform +The platform that tldr will use is determined by the `TLDR_PLATFORM` environment variable. +If it is not set, the client will try to determine the platform automatically based on the system it is running on. +The following values are supported: +- `linux` +- `windows` +- `win32` +- `android` +- `darwin` +- `freebsd` +- `macos` +- `netbsd` +- `openbsd` +- `osx` +- `sunos` + ### Cache Cache is downloaded from `TLDR_DOWNLOAD_CACHE_LOCATION` (defaults to the one described in [the client specification](https://github.com/tldr-pages/tldr/blob/main/CLIENT-SPECIFICATION.md#caching)), unzipped and extracted into the [local cache directory](#cache-location). Pages are loaded directly from `TLDR_PAGES_SOURCE_LOCATION` if `tldr ` is used. diff --git a/tldr.py b/tldr.py index 161e960..91e3e91 100755 --- a/tldr.py +++ b/tldr.py @@ -4,7 +4,7 @@ import sys import os import re -from argparse import ArgumentParser +from argparse import ArgumentParser, Namespace from pathlib import Path from zipfile import ZipFile from datetime import datetime @@ -13,11 +13,14 @@ from urllib.parse import quote from urllib.request import urlopen, Request from urllib.error import HTTPError, URLError -from termcolor import colored import ssl -import shtab import shutil +# Third party imports +from termcolor import colored +import shtab + + __version__ = "3.4.1" __client_specification__ = "2.3" @@ -202,6 +205,13 @@ def update_page_for_platform( def get_platform() -> str: + if platform := os.environ.get('TLDR_PLATFORM'): + platform = platform.lower() + if platform in OS_DIRECTORIES: + return OS_DIRECTORIES[platform] + else: + print("Warning: Invalid platform specified in environment variable TLDR_PLATFORM.") + for key in OS_DIRECTORIES: if sys.platform.startswith(key): return OS_DIRECTORIES[key] @@ -564,6 +574,115 @@ def clear_cache(language: Optional[List[str]] = None) -> None: print(f"No cache directory found for language {language}") +def interactive_mode(options: Namespace) -> None: + """Interactive mode for browsing/searching tldr pages.""" + try: + import readline + except ImportError: + pass + + print(colored("tldr interactive mode. Type 'help' for commands, 'quit' to exit.", 'green', attrs=['bold'])) + + # Use the flags/options provided by the user, not just defaults + # If options.platform is a list (from argparse), flatten it to a string if needed + session_platform = options.platform if options.platform else get_platform_list() + if isinstance(session_platform, list) and len(session_platform) == 1: + session_platform = session_platform + + session_language = options.language if options.language else get_language_list() + if isinstance(session_language, list) and len(session_language) == 1: + session_language = session_language + + # If user passed --platform or --language, always use those as the starting values + # and allow them to be changed in the session + while True: + try: + user_input = input(colored("tldr> ", 'cyan', attrs=['bold'])).strip() + if not user_input: + continue + parts = user_input.split() + cmd = parts[0].lower() + args = parts[1:] + + if cmd in ('quit', 'exit', 'q'): + print("Exiting interactive mode.") + break + elif cmd in ('help', 'h'): + print( + "Commands:\n" + " help Show this help\n" + " list List available commands\n" + " search Search commands\n" + " show Show documentation for a command\n" + " platform [name] Show or set platform\n" + " language [code] Show or set language\n" + " update Update cache\n" + " clear Clear cache\n" + " quit Exit interactive mode\n" + ) + elif cmd == 'list': + cmds = get_commands(session_platform, session_language) + if not cmds: + print(colored("No commands found. Try 'update'.", 'red')) + else: + unique = sorted(set(c.split(' (')[0] for c in cmds)) + print("Available commands:") + print('\n'.join(' ' + cmd for cmd in unique)) + elif cmd == 'search': + if not args: + print("Usage: search ") + continue + term = ' '.join(args).lower() + cmds = get_commands(session_platform, session_language) + found = sorted(set(c.split(' (')[0] for c in cmds if term in c.lower())) + if found: + print("Matches:") + print('\n'.join(' ' + cmd for cmd in found)) + else: + print(colored("No matches found.", 'yellow')) + elif cmd == 'show': + if not args: + print("Usage: show ") + continue + command = '-'.join(args).lower() + results = get_page_for_every_platform(command, None, session_platform, session_language) + if not results: + print(colored(f"No documentation for '{command}'.", 'red')) + else: + output(results[0][0], "long", plain=False) + elif cmd == 'platform': + if args: + platform_arg = args[0].lower() + if platform_arg in OS_DIRECTORIES: + session_platform = [platform_arg] + print(f"Platform set to: {platform_arg}") + else: + print(f"Invalid platform: {platform_arg}.") + print(f"Valid platforms are: {', '.join(OS_DIRECTORIES)}") + else: + print(f"Current platform: {session_platform[0]}") + elif cmd == 'language': + if args: + session_language = [args[0]] + print(f"Language set to: {args[0]}") + else: + print(f"Current language: {session_language[0]}") + elif cmd == 'update': + update_cache(language=session_language) + elif cmd == 'clear': + clear_cache(language=session_language) + else: + command = '-'.join(user_input.split()).lower() + results = get_page_for_every_platform(command, None, session_platform, session_language) + if not results: + print(colored(f"No documentation for '{command}'.", 'red')) + else: + output(results[0][0], "long", plain=False) + except (KeyboardInterrupt, EOFError): + print("\nExiting interactive mode.") + break + + def create_parser() -> ArgumentParser: parser = ArgumentParser( prog="tldr", @@ -638,6 +757,10 @@ def create_parser() -> ArgumentParser: action='store_true', help='Just print the plain page file.') + parser.add_argument('-i', '--interactive', + action='store_true', + help='Start tldr in interactive mode') + parser.add_argument('--short-options', default=False, action="store_true", @@ -666,7 +789,6 @@ def create_parser() -> ArgumentParser: def main() -> None: parser = create_parser() - options = parser.parse_args() display_option_length = "long" @@ -689,6 +811,11 @@ def main() -> None: if options.color is False: os.environ["FORCE_COLOR"] = "true" + # Call interactive_mode if requested, before any other logic + if options.interactive: + interactive_mode(options) + return + if options.update: update_cache(language=options.language) return