dfdfdf
This commit is contained in:
185
cmdnat/help.py
Normal file
185
cmdnat/help.py
Normal file
@@ -0,0 +1,185 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, Sequence, List, Optional
|
||||
import shlex
|
||||
import sys
|
||||
|
||||
from cmdlet._shared import Cmdlet, CmdletArg, parse_cmdlet_args
|
||||
from SYS.logger import log
|
||||
from result_table import ResultTable
|
||||
import pipeline as ctx
|
||||
|
||||
|
||||
def _normalize_choice_list(arg_names: Optional[List[str]]) -> List[str]:
|
||||
return sorted(set(arg_names or []))
|
||||
|
||||
|
||||
def _examples_for_cmd(name: str) -> List[str]:
|
||||
"""Return example invocations for a given command (best-effort)."""
|
||||
lookup = {
|
||||
".adjective": [
|
||||
'.adjective -add "example"',
|
||||
'.adjective -delete "example"',
|
||||
],
|
||||
}
|
||||
|
||||
key = name.replace("_", "-").lower()
|
||||
return lookup.get(key, [])
|
||||
|
||||
|
||||
def _find_cmd_metadata(name: str, metadata: Dict[str, Dict[str, Any]]) -> Optional[Dict[str, Any]]:
|
||||
target = name.replace("_", "-").lower()
|
||||
for cmd_name, meta in metadata.items():
|
||||
if target == cmd_name:
|
||||
return meta
|
||||
aliases = meta.get("aliases", []) or []
|
||||
if target in aliases:
|
||||
return meta
|
||||
return None
|
||||
|
||||
|
||||
def _render_list(metadata: Dict[str, Dict[str, Any]], filter_text: Optional[str], args: Sequence[str]) -> None:
|
||||
table = ResultTable("Help")
|
||||
table.set_source_command(".help", list(args))
|
||||
|
||||
items: List[Dict[str, Any]] = []
|
||||
needle = (filter_text or "").lower().strip()
|
||||
|
||||
for name in sorted(metadata.keys()):
|
||||
meta = metadata[name]
|
||||
summary = meta.get("summary", "") or ""
|
||||
if needle and needle not in name.lower() and needle not in summary.lower():
|
||||
continue
|
||||
|
||||
row = table.add_row()
|
||||
row.add_column("Cmd", name)
|
||||
aliases = ", ".join(meta.get("aliases", []) or [])
|
||||
row.add_column("Aliases", aliases)
|
||||
arg_names = [a.get("name") for a in meta.get("args", []) if a.get("name")]
|
||||
row.add_column("Args", ", ".join(f"-{a}" for a in arg_names))
|
||||
table.set_row_selection_args(len(table.rows) - 1, ["-cmd", name])
|
||||
items.append(meta)
|
||||
|
||||
ctx.set_last_result_table(table, items)
|
||||
ctx.set_current_stage_table(table)
|
||||
print(table)
|
||||
|
||||
|
||||
def _render_detail(meta: Dict[str, Any], args: Sequence[str]) -> None:
|
||||
title = f"Help: {meta.get('name', '') or 'cmd'}"
|
||||
table = ResultTable(title)
|
||||
table.set_source_command(".help", list(args))
|
||||
|
||||
header_lines: List[str] = []
|
||||
summary = meta.get("summary", "")
|
||||
usage = meta.get("usage", "")
|
||||
aliases = meta.get("aliases", []) or []
|
||||
examples = _examples_for_cmd(meta.get("name", ""))
|
||||
first_example_tokens: List[str] = []
|
||||
first_example_cmd: Optional[str] = None
|
||||
if examples:
|
||||
try:
|
||||
split_tokens = shlex.split(examples[0])
|
||||
if split_tokens:
|
||||
first_example_cmd = split_tokens[0]
|
||||
first_example_tokens = split_tokens[1:]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if summary:
|
||||
header_lines.append(summary)
|
||||
if usage:
|
||||
header_lines.append(f"Usage: {usage}")
|
||||
if aliases:
|
||||
header_lines.append("Aliases: " + ", ".join(aliases))
|
||||
if examples:
|
||||
header_lines.append("Examples: " + " | ".join(examples))
|
||||
if header_lines:
|
||||
table.set_header_lines(header_lines)
|
||||
|
||||
args_meta = meta.get("args", []) or []
|
||||
example_text = " | ".join(examples)
|
||||
# If we have an example, use it as the source command so @N runs that example
|
||||
if first_example_cmd:
|
||||
table.set_source_command(first_example_cmd, [])
|
||||
if not args_meta:
|
||||
row = table.add_row()
|
||||
row.add_column("Arg", "(none)")
|
||||
row.add_column("Type", "")
|
||||
row.add_column("Req", "")
|
||||
row.add_column("Description", "")
|
||||
row.add_column("Example", example_text)
|
||||
if first_example_tokens:
|
||||
table.set_row_selection_args(len(table.rows) - 1, first_example_tokens)
|
||||
else:
|
||||
for arg in args_meta:
|
||||
row = table.add_row()
|
||||
name = arg.get("name") or ""
|
||||
row.add_column("Arg", f"-{name}" if name else "")
|
||||
row.add_column("Type", arg.get("type", ""))
|
||||
row.add_column("Req", "yes" if arg.get("required") else "")
|
||||
desc = arg.get("description", "") or ""
|
||||
choices = arg.get("choices", []) or []
|
||||
if choices:
|
||||
choice_text = f"choices: {', '.join(choices)}"
|
||||
desc = f"{desc} ({choice_text})" if desc else choice_text
|
||||
row.add_column("Description", desc)
|
||||
row.add_column("Example", example_text)
|
||||
if first_example_tokens:
|
||||
table.set_row_selection_args(len(table.rows) - 1, first_example_tokens)
|
||||
|
||||
ctx.set_last_result_table_overlay(table, [meta])
|
||||
ctx.set_current_stage_table(table)
|
||||
print(table)
|
||||
|
||||
|
||||
def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
||||
try:
|
||||
from cmdlet import catalog as _catalog
|
||||
|
||||
CMDLET.arg[0].choices = _normalize_choice_list(_catalog.list_cmdlet_names())
|
||||
metadata = _catalog.list_cmdlet_metadata()
|
||||
except Exception:
|
||||
CMDLET.arg[0].choices = []
|
||||
metadata = {}
|
||||
|
||||
parsed = parse_cmdlet_args(args, CMDLET)
|
||||
|
||||
filter_text = parsed.get("filter")
|
||||
cmd_arg = parsed.get("cmd")
|
||||
|
||||
if cmd_arg:
|
||||
target_meta = _find_cmd_metadata(str(cmd_arg), metadata)
|
||||
if not target_meta:
|
||||
log(f"Unknown command: {cmd_arg}", file=sys.stderr)
|
||||
return 1
|
||||
_render_detail(target_meta, args)
|
||||
return 0
|
||||
|
||||
_render_list(metadata, filter_text, args)
|
||||
return 0
|
||||
|
||||
|
||||
CMDLET = Cmdlet(
|
||||
name=".help",
|
||||
alias=["help", "?"],
|
||||
summary="Show cmdlet or detailed help",
|
||||
usage=".help [cmd] [-filter text]",
|
||||
arg=[
|
||||
CmdletArg(
|
||||
name="cmd",
|
||||
type="string",
|
||||
description="Cmdlet name to show detailed help",
|
||||
required=False,
|
||||
choices=[],
|
||||
),
|
||||
CmdletArg(
|
||||
name="-filter",
|
||||
type="string",
|
||||
description="Filter cmdlet by substring",
|
||||
required=False,
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
CMDLET.exec = _run
|
||||
Reference in New Issue
Block a user