;;; TDM.el --- editing Taler docs.git/manpages/* -*- lexical-binding: t -*-
;; Copyright (C) 2021, 2022 Taler Systems SA
;;
;; This file is part of GNU TALER.
;;
;; TALER is free software; you can redistribute it and/or modify it
;; under the terms of the GNU Affero General Public License as
;; published by the Free Software Foundation; either version 2.1,
;; or (at your option) any later version.
;;
;; TALER 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 Affero General Public License for more details.
;;
;; You should have received a copy of the GNU Affero General Public
;; License along with TALER; see the file COPYING. If not, see
;; .
;;
;; Author: Thien-Thi Nguyen
;;; Commentary:
;; This library currently provides two commands: ‘TDM-convert-options’
;; and ‘TDM-recursive-help’.
;;
;; * ‘TDM-convert-options’
;; The intended workflow is simple:
;; - Create a new file from template.
;; - Do the substitutions / deletions as necessary.
;; - Split the window, one half for Synopsis, one for Description.
;; - Do ‘COMMAND --help’ and copy the portion of its output that
;; describes the options into the buffer in the Synopsis section.
;; + Place point at bol of an option and call ‘TDM-convert-options’.
;; (Personally, I locally bind this to ‘C-c C-o’.)
;; - Set mark at point and move point to the end of the
;; description text (for that option).
;; - Kill the region (with ‘C-w’).
;; - Switch to the other window, find a suitable place, and
;; yank (with ‘C-y’) the text there.
;; - Edit the description as necessary.
;; - Loop from "+" until finished.
;;
;; At the end, you may have to delete some extra blank lines
;; in the Synopsis section.
;;
;; ‘TDM-convert-options’ takes a prefix arg, which inhibits the
;; deletion of the original (--help output) text. This can be
;; useful if you don't trust (yet :-D) the conversion process.
;;
;; There are a couple TODO items, which point to situations that
;; have not yet arisen in practice, but that might theoretically
;; bother us in the future.
;;
;; * ‘TDM-recursive-help’
;; This command is intended for libeufin programs, specifically
;; libeufin-sandbox, libeufin-nexus, and libeufin-cli. However,
;; it should work with any Java program that uses clikt, or any
;; Python program that uses click, for its command-line handling.
;;
;; You can obtain the --help output (recursively) in a buffer
;; and write it to a file for further analysis / processing.
;;; Code:
(require 'cl-lib)
(defun TDM-parse-option-triple ()
"Return a triple formed by parsing option text at point.
The triple has the form (ARG SHORT LONG), where each element
can be either a string or ‘nil’.
Signal error if parsing fails.
Leave point after the end of the parsed text."
(cl-flet ((peer (regexp)
(re-search-forward regexp (line-end-position) t)))
(cond
;; Full form: (ARG SHORT LONG).
((peer " *-\\(.\\), --\\([^=\n]+\\)=\\(\\S +\\)")
(mapcar #'match-string-no-properties (list 3 1 2)))
;; No argument: (nil SHORT LONG).
((peer " *-\\(.\\), --\\(\\S +\\)")
(list nil
(match-string-no-properties 1)
(match-string-no-properties 2)))
;; TODO: Handle other combinations.
(t
(error "Could not parse option text")))))
(defun TDM-insert-options (arg short long)
"Insert formatted options, twice (on two lines).
The first is for the Synopsis section, the second for Description."
(unless (zerop (current-column))
(open-line 1)
(forward-line 1))
(cl-flet ((ins (short-space sep)
(cond
;; Both available.
((and short long)
(insert "**-" short "**")
(when arg
(insert short-space "*" arg "*"))
(insert sep)
(insert "**--" long (if arg "=" "") "**")
(when arg
(insert "\\ \\ *" arg "*")))
;; TODO: Handle other combinations.
(t
(error "Could not handle (%S %S %S)"
arg short long)))))
;; First line.
(insert "[")
(ins " " " | ")
(insert "]\n")
;; Second line. Leave point at its beginning.
(save-excursion
(ins " " " \\| ")
;; Add a newline only if necessary.
(unless (eolp)
(insert "\n")))))
(defun TDM-convert-options (&optional keep-orig)
"Grok the options at point and insert formatted ones.
If successful, delete the parsed options as well.
Prefix arg KEEP-ORIG means don't delete them."
(interactive "P")
(let* ((p (point))
(triple (TDM-parse-option-triple))
(q (point)))
(apply #'TDM-insert-options triple)
(unless keep-orig
(save-excursion
(delete-region p q)))))
(defun TDM-recursive-help (command)
"Call COMMAND --help and recurse on its subcommands.
Subcommands are identified by \"Commands:\" in column 0
in the output.
Collect the output in a new buffer *COMMAND --help*,
with one page per --help output."
(interactive "sCommand: ")
(let ((out (get-buffer-create (format "*%s --help*" command))))
(with-current-buffer out
(erase-buffer))
(cl-labels
;; visit command
((v (c) (with-temp-buffer
(apply #'call-process (car c) nil t nil
(append (cdr c) (list "--help")))
(goto-char (point-min))
(when (re-search-forward "^Commands:\n" nil t)
(while (looking-at "[ ][ ]\\(\\S +\\)")
(let ((sub (match-string 1)))
(v (append c (list sub))))
(forward-line 1)
(while (looking-at "[ ][ ][ ]")
(forward-line 1))))
(let ((s (buffer-string)))
(message "c: %s" c)
(with-current-buffer out
(goto-char (point-min))
(insert "\f\n")
(insert "$ " (substring (format "%s" c) 1 -1) "\n")
(insert s "\n"))))))
(v (list command)))
(switch-to-buffer out)))
(provide 'TDM)
;;; TDM.el ends here