diff --git a/ChangeLog b/ChangeLog index b511c5fa..a2c94283 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,201 @@ +2026-09-29 Bob Weiner + +* hibtypes.el (hbut-identifier, hbut-identifier:help): Move smart + tag handling of Hyperbole button variables from 'hkey-alist' + where it was improperly shadowed by the hbut handling clause + to be the lowest priority implicit button type. + hui-mouse.el (hkey-alist): Remove code replaced above. + +* test/hui-mouse-tests.el (hui-mouse-tests--hkey-alist): Fix some + failures and update to match latest `hkey-alist' expressions. + hui-mouse.el (hkey-alist): Change `smart-tags-find-p' assist-key + clause to run 'hbut:report' instead of 'smart-prog-tag' to match + the 'hbut:is-p' programming tag clause above it. + Also, in Hyperbole button recognition clause, add a call + (not (smart-prog-at-tag-p t)) to prevent that from firing when + on a programming id since those are handled further down + +* test/hui-tests.el (hui-gibut-create-link-to-file): Fix to compare + :args with second numeric argument. + +* test/kotl-mode-tests.el (kotl-mode-indent-cell-changes-level): Fix + to ensure test is run in demote/promote mode. + +* test/hbut-tests.el (hbut-tests--ibut-operate--add-name, + hbut-tests--ibut-operate--add-new-name, + hbut-tests--ibut-operate--remove-name, + hbut-tests--ibut-operate--fail-new-name-ignore-region, + hbut-tests--ibut-operate--fail-rename-from-name, + hbut-tests--ibut-operate--fail-rename-from-name-ignore-region, + hbut-tests--ibut-operate--name-ignore-region, + hbut-tests--ibut-operate--none, + hbut-tests--ibut-operate--rename-from-name: + test/hibtypes-tests.el (ibtypes::pathname-dot-slash-in-same-folder-test): + Match to updated action type, 'hpath:find-koutline-reference', not + the previous 'actypes::link-to-file'. + (hbut-tests:should-match-tmp-folder): Remove match + of path to end of string as string may contain arguments after the + pathname. + +* hpath.el (hpath:find): Fix that 'pathname' was not set after + resolving variables in a path with a modifier prefix. This + caused the test, 'hbut-pathname-with-dash-loads-file-test' + to fail. + +* test/demo-tests.el (fast-demo-display-kotl-starting-from-cell): Fix + cell ref number. + +* kotl/EXAMPLE.kotl: Add summary tables of {TAB} and {S-TAB} behavior. + +* kotl/kotl-mode.el (kotl-mode:untab-command): + man/hyperbole.texi (Org-style Visibility Cycling): Fix to better + match Org mode and have {S-TAB} cycle globally instead of locally + without a prefix argument or with a single C-u. Also match Org + behavior by making numeric prefix arguments show a single line + for each cell down to the prefix argument level. Zero means show + one line of all levels. + +2026-09-28 Bob Weiner + +* kotl/kotl-mode.el (kotl-mode:action-key): + man/hyperbole.texi (Smart Key - The Koutliner): Add doc for wrapping + text when inside an Org-style table. + +* hui-mouse.el (smart-outline, smart-outline-assist): Fix eolp check + to ensure not at an invisible char (prior code checked that was + invisible). + +* kotl/kotl-mode.el (kotl-mode:action-key): Replace call to + 'smart-outline-subtree-hidden-p' with 'kcell-view:collapsed-p' + which properly determines the collapsed state of a root cell. + +* man/hyperbole.texi (Promoting and Demoting): Expand this Koutliner + section with latest key bindings and Org table support. + (Inserting Literal Tabs): Add this Koutliner section. + +* hui-mouse.el (hkey-alist): In 'smart-prog-at-tag-p' clause, change + to flash tag only if the second call to 'smart-tags-find-p' is true. + This prevents flashing words in text modes that aren't actually + programming symbols. + hmouse-tag.el (smart-flag-tag-at-point): Add to do the flashing. + +* kotl/kotl-orgtbl.el (orgtbl-tab): Fix to allow prefix arg of 1 to pass + through rather than converting to nil. + (org-table-previous-field): Override this function + on {S-TAB} from 'orgtbl-mode' to make it compatible with Koutlines. + +* hsys-org.el (hsys-org-link-at-p): Wrap 'org-element' and 'org-in-regexp' + calls in 'ignore-errors' macro as it is possible `syntax-ppss-flush-cache' + can trigger an error, e.g. using orgtbl minor mode in .texi files. + +2026-09-27 Bob Weiner + +* kotl/kotl-mode.el (kotl-mode:setup-keymap): Remove errant binding of {M-TAB} + since this is typically used to switch applications by many users. + (kotl-mode:tab-command): Since {C-TAB} is not a valid + key binding on many terminals, move 'kotl-mode:toggle-tab-flag' back to + {M-1 TAB}, which is where it used to be. + +* hui.el (hui:link-possible-types): + hbut.el (ibut:insert-text): For a klink, if source and referent are in + the same buffer, use the simplified link format: <@ relative-id=idstamp>. + +2026-09-26 Bob Weiner + +* kotl/kotl-mode.el (kotl-mode:action-key): + man/hyperbole.texi (Smart Key - The Koutliner): Fix doc to match code. + +* hargs.el (hargs:at-p): Move (eolp) test to the end of the 'cond' + expression so it doesn't interfere with reading any of the specified + types. + +* kotl/kotl-mode.el (kotl-mode:emulate-tab): Add this customization. + (kotl-mode:before-tab-emulation-hook, + kotl-mode:tab-pre-hook, kotl-mode:tab-post-hook): Add. + (kotl-mode:toggle-tab-flag, kotl-mode:tab-command, + kotl-mode:untab-command): Non-backward compatible + change: toggle {TAB}/{M-TAB} between promote/demote and Org-style cycling. + (kotl-mode:demote-siblings, kotl-mode:promote-siblings): + Add to move following siblings. + (kotl-mode:current-level, kotl-mode:previous-cell-level, + kotl-mode:at-cell-empty-first-line-p, + kotl-mode:cycle-globally, kotl-mode:cycle-locally, + kotl-mode:all-cells-all-lines-visible-p, + kotl-mode:all-cells-one-line-visible-p, + kotl-mode:top-cells-one-line-visible-p, + kotl-mode:root-first-line-only-shown-p, + kotl-mode:show-root-first-line, + kotl-mode:show-tree-children-first-lines, + kotl-mode:tree-collapsed-p): Add. + man/hyperbole.texi (Promoting and Demoting): + kotl/EXAMPLE.kotl (Tree Demotion and Promotion): Update this section + to reflect key changes. Remove {M-TAB} as a binding for promoting trees + outside of Org tables. + (kotl-mode:mark-whole-buffer):Allow selection of entire + buffer including first relative id, so can copy it. + +* hypb.el (hypb:count-visible-lines): Add. + +2026-09-24 Bob Weiner + +* kotl/kotl-mode.el (kotl-mode:demote-tree, kotl-mode:promote-tree): + If given a null prefix arg, set it to 1. + +* kotl/kview.el (kcell-view:sibling-p): Return sibling idstamp rather + than t, so can jump back to it. + (kcell-view:child-p): Return child idstamp rather + than t, so can jump back to it. + 2026-09-23 Bob Weiner +* kotl/kotl-mode.el (kotl-mode:cycle-locally): Add. + +* kotl/kvspec.el (kvspec:default): Add this so view spec can be + reset to its default value. + kotl/kfile.el (kfile:read-v4-or-v3, kvspec:update): ): Set 'kvspec:default'. + (kvspec:reset): Add to reset view spec to default. + * man/hyperbole.texi: Remove trailing whitespace from lines. +2026-09-21 Bob Weiner + +* kotl/klink.el (klink:replace-label): Improve read-only message + and include source buffer name. + +2026-09-20 Bob Weiner + +* hpath.el (hpath:find-koutline-reference): Add and use in + pathname ibtype so that koutline reference links have + their relative labels updated on activation just like + klinks do. + +* kotl/klink.el (klink:update-label): Improve doc string. + +* hibtypes.el (pathname): Update first section to handle kotl + path's with an =idstamp referent where the relative label + needs updating upon activation. + +* kotl/kfile.el (kfile:read): Rewrite 'cond using pcase' + +* kotl/kvspec.el (kvspec:initialize): Remove this which just made + 2 vars, kvspec:current and kvspec:string, buffer local. Define + them with 'defvar-local' instead. + +2026-09-19 Bob Weiner + +* kotl/kotl-mode.el (kotl-mode:tab-flag): Update values to allow for + Org-like cycling when {tab} and {S-tab} are pressed in a context + other than the one specified by 'org-cycle-emulate-tab'. + +2026-09-18 Bob Weiner + +* kotl/kotl-mode.el (kotl-mode:show-to-level, + kotl-mode:show-lines-per-cell): Add. + +============================================================================== +V9.2.0 changes ^^^^: +============================================================================== + 2026-09-16 Bob Weiner * hsys-org.el (hsys-org-meta-return): Fix bug reported by James S where diff --git a/HY-ABOUT b/HY-ABOUT index 1d46128d..6173017e 100644 --- a/HY-ABOUT +++ b/HY-ABOUT @@ -3,7 +3,7 @@ Designed and Written by Bob Weiner Maintained by Mats Lidell and Bob Weiner https://www.gnu.org/software/hyperbole/ - Version 9.1.0 + Version 9.2.0 Say thanks or send a testimonial if you like Hyperbole: Email: diff --git a/HY-ANNOUNCE b/HY-ANNOUNCE index fec9bb59..a3c3c106 100644 --- a/HY-ANNOUNCE +++ b/HY-ANNOUNCE @@ -1,6 +1,6 @@ From: Bob Weiner To: gnu-emacs-sources@gnu.org, emacs-tangents@gnu.org, hyperbole-users@gnu.org, hyperbole-announce@gnu.org -Subject: GNU Hyperbole Major Release 9 (V9.1.0) +Subject: GNU Hyperbole Major Release 9 (V9.2.0) --text follows this line-- ======================================================================== * Overview diff --git a/HY-NEWS b/HY-NEWS index b441d2f6..d90907b9 100644 --- a/HY-NEWS +++ b/HY-NEWS @@ -1,6 +1,10 @@ * What's New in GNU Hyperbole by Bob Weiner +=========================================================================== +* V9.2.0 +=========================================================================== + =========================================================================== * V9.1.0 =========================================================================== diff --git a/Makefile b/Makefile index d6f2a28d..f587506c 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ # Author: Bob Weiner # # Orig-Date: 15-Jun-94 at 03:42:38 -# Last-Mod: 13-Sep-26 at 22:38:29 by Mats Lidell +# Last-Mod: 27-Sep-26 at 02:26:14 by Bob Weiner # # Copyright (C) 1994-2026 Free Software Foundation, Inc. # See the file HY-COPY for license information. @@ -117,7 +117,7 @@ # This ver setup won't work under any make except GNU make, so set it manually. #HYPB_VERSION = "`head -3 hversion.el | tail -1 | sed -e 's/.*|\(.*\)|.*/\1/'`" -HYPB_VERSION = 9.1.0 +HYPB_VERSION = 9.2.0 # Emacs executable used to byte-compile .el files into .elc's. # To override which executable is used from the commandline, do something like this: diff --git a/README.md b/README.md index 5a387fb0..ddb037c5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# GNU Hyperbole 9.1.0 - The Everyday Hypertextual Information Manager +# GNU Hyperbole 9.2.0 - The Everyday Hypertextual Information Manager [ If you are looking for the documentation for the stable release look [here](stable/hyperbole.html).] diff --git a/README.md.html b/README.md.html index 652d3c2a..b6106450 100644 --- a/README.md.html +++ b/README.md.html @@ -1,6 +1,6 @@

GNU -Hyperbole 9.1.0 - The Everyday Hypertextual Information Manager

+id="gnu-hyperbole-920---the-everyday-hypertextual-information-manager">GNU +Hyperbole 9.2.0 - The Everyday Hypertextual Information Manager

[ If you are looking for the documentation for the stable release look here.]

[We work on Hyperbole as a gift to the Emacs community and request @@ -10,7 +10,7 @@

  • GNU -Hyperbole 9.1.0 - The Everyday Hypertextual Information Manager +Hyperbole 9.2.0 - The Everyday Hypertextual Information Manager
    • Reference Manual
    • Videos
    • diff --git a/README.toc.md b/README.toc.md index 06944880..cf57ad1d 100644 --- a/README.toc.md +++ b/README.toc.md @@ -1,4 +1,4 @@ -# GNU Hyperbole 9.1.0 - The Everyday Hypertextual Information Manager +# GNU Hyperbole 9.2.0 - The Everyday Hypertextual Information Manager [ If you are looking for the documentation for the stable release look [here](stable/hyperbole.html).] @@ -9,7 +9,7 @@ -- [GNU Hyperbole 9.1.0 - The Everyday Hypertextual Information Manager](#gnu-hyperbole-902pre---the-everyday-hypertextual-information-manager) +- [GNU Hyperbole 9.2.0 - The Everyday Hypertextual Information Manager](#gnu-hyperbole-902pre---the-everyday-hypertextual-information-manager) - [Reference Manual](#reference-manual) - [Videos](#videos) - [Articles](#articles) diff --git a/hargs.el b/hargs.el index 98ca2383..91a05a9e 100644 --- a/hargs.el +++ b/hargs.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 31-Oct-91 at 23:17:35 -;; Last-Mod: 28-Jun-26 at 13:57:12 by Bob Weiner +;; Last-Mod: 26-Sep-26 at 17:44:06 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -672,7 +672,6 @@ Handles all of the interactive argument types that `hargs:iform-read' does." ((eq hargs:reading-type 'kvspec) (read-string "Koutline view spec: " (when (boundp 'kvspec:current) kvspec:current))) - ((eolp) nil) ((and (eq hargs:reading-type 'hmenu) (eq (selected-window) (minibuffer-window))) (char-to-string @@ -767,7 +766,8 @@ Handles all of the interactive argument types that `hargs:iform-read' does." ((eq hargs:reading-type 'integer) (save-excursion (skip-chars-backward "-0-9") (when (looking-at "-?[0-9]+") - (read (current-buffer))))))) + (read (current-buffer))))) + ((eolp) nil))) (defun hargs:completion (&optional no-insert) "If in the completions buffer, return completion at point. diff --git a/hbut.el b/hbut.el index 3f6343da..d1e427c4 100644 --- a/hbut.el +++ b/hbut.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 18-Sep-91 at 02:57:09 -;; Last-Mod: 12-Sep-26 at 13:33:57 by Mats Lidell +;; Last-Mod: 27-Sep-26 at 01:27:28 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -2761,8 +2761,10 @@ Summary of operations based on inputs (name arg from \\='hbut:current attrs): ('link-to-kcell (if arg2 (progn (insert "<") - (when arg1 (insert arg1)) - (insert ", " arg2 ">")) + (if arg1 + (insert arg1 ", ") + (insert "@ ")) + (insert arg2 ">")) (insert "<@ ") (when arg1 (insert arg1)) (insert ">"))) diff --git a/hibtypes.el b/hibtypes.el index d5f175e7..1502504e 100644 --- a/hibtypes.el +++ b/hibtypes.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 19-Sep-91 at 20:45:31 -;; Last-Mod: 30-Jul-26 at 11:50:17 by Bob Weiner +;; Last-Mod: 29-Sep-26 at 16:11:57 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -97,6 +97,27 @@ (run-hooks 'hibtypes-begin-load-hook) +;;; ======================================================================== +;;; Jumps to and displays help for variables that store Hyperbole buttons +;;; ======================================================================== + +(defib hbut-identifier () + "When on the name of a variable that holds a Hyperbole button, e.g. hbut:current: + Action Key - Jump to variable definition." + (let ((identifier (smart-prog-at-tag-p t)) + start-end) + (when (and identifier + (hbut:is-p (intern-soft identifier)) + (setq start-end (bounds-of-thing-at-point 'symbol))) + (ibut:label-set identifier (car start-end) (cdr start-end)) + (ignore-errors + (hact #'smart-prog-tag identifier))))) + +(defun hbut-identifier:help (ibut) + "When on the name of a variable that holds a Hyperbole button, e.g. hbut:current: + Assist Key - Display button attributes of any Hyperbole button symbol at point." + (hbut:report ibut)) + ;; Don't use require below here for any libraries with ibtypes in ;; them. Use load instead to ensure are reloaded when resetting ;; ibtype priorities. @@ -385,13 +406,20 @@ display options." (t nil))) (t (when (string-match "\\`file://" path) (setq path (substring path (match-end 0)))) - (if (or (> (cl-count ?: orig-path) 2) - (> (cl-count ?\; orig-path) 2)) - ;; PATH-like set of values; select just the one point is on - (apply #'ibut:label-set path (hpath:start-end path)) - ;; Otherwise, use the unchanged orig-path - (apply #'ibut:label-set orig-path (hpath:start-end orig-path))) - (hact 'link-to-file path))) + (let (start-end + start) + (if (or (> (cl-count ?: orig-path) 2) + (> (cl-count ?\; orig-path) 2)) + ;; PATH-like set of values; select just the one + ;; point is on + (apply #'ibut:label-set + path (setq start-end (hpath:start-end path))) + ;; Otherwise, use the unchanged orig-path + (apply #'ibut:label-set + orig-path (setq start-end + (hpath:start-end orig-path)))) + (setq start (car start-end)) + (hact 'hpath:find-koutline-reference path start)))) ;; ;; Match PATH-related Environment and Lisp variable names and ;; Emacs Lisp and Info files without any directory component. diff --git a/hmouse-tag.el b/hmouse-tag.el index dc077a82..eb32a2f6 100644 --- a/hmouse-tag.el +++ b/hmouse-tag.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 24-Aug-91 -;; Last-Mod: 7-Jun-26 at 10:53:31 by Bob Weiner +;; Last-Mod: 28-Sep-26 at 10:50:55 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -1243,6 +1243,17 @@ Look for include file in `smart-c-cpp-include-path' and in directory list (goto-char opoint) nil))) +(defun smart-flash-tag-at-point (tag) + "Try to flash and return any previously recognized TAG at point. +This indicates that TAG is serving as a hyperlink button. TAG may be a +string or a symbol but is always returned as a string." + (let ((start-end (bounds-of-thing-at-point 'symbol))) + (when start-end + (when (symbolp tag) + (setq tag (symbol-name hkey-value))) + (smart-flash-tag tag (car start-end) (cdr start-end)) + tag))) + (defun smart-flash-tag (tag start end) "Try to flash TAG at START to END in current buffer and return TAG. This indicates that TAG is serving as a hyperlink button." diff --git a/hpath.el b/hpath.el index 915037f8..7ceb8c15 100644 --- a/hpath.el +++ b/hpath.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 1-Nov-91 at 00:44:23 -;; Last-Mod: 28-Aug-26 at 09:41:18 by Mats Lidell +;; Last-Mod: 29-Sep-26 at 02:16:49 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -1518,6 +1518,18 @@ result is abbreviated relative to the caller's buffer directory." (line-number-at-pos (point) t) (current-column))))))) +(defun hpath:find-koutline-reference (path start) + "Find a potential koutline reference, PATH, with ref at START position." + ;; If is a koutline reference with an =idstamp, then update reference + ;; relative label if need be; this may be in a different buffer than the + ;; current one. Otherwise, just find the path. + (let ((kotl-flag (string-match-p "\\.kotl\\([:#]\\|\\'\\)" path)) + (obuf (current-buffer))) + (funcall (actype:elisp-symbol 'link-to-file) path) + (when kotl-flag + (save-excursion + (klink:update-label path start obuf))))) + (defun hpath:find-noselect (filename) "Find but don't display FILENAME. Use user customizable settings of display program and location. @@ -1635,7 +1647,8 @@ but locational suffixes within the file are utilized." ;; down below. (setq path (cdr referent)))) (cond (modifier - (setq path (hpath:resolve path))) + (setq path (hpath:resolve path) + pathname path)) (t (setq path (hpath:expand path) pathname (hpath:absolute-to path default-directory)) ;; Remove http file:// url prefix that`hpath:absolute-to' may have @@ -1690,7 +1703,7 @@ but locational suffixes within the file are utilized." (error "(hpath:find): No available executable from: %s" display-executables))) (t (setq path (hpath:validate path) ;; signals error when invalid - kotl-flag (string-match "\\.kotl?\\'" path)) + kotl-flag (string-match-p "\\.kotl?\\'" path)) (let ((buf (cond ;; If no path, e.g. just an anchor link in a non-file buffer, ;; then must display within Emacs, ignoring any external programs. diff --git a/hsys-org.el b/hsys-org.el index bc3853f1..f0693fc4 100644 --- a/hsys-org.el +++ b/hsys-org.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 2-Jul-16 at 14:54:14 -;; Last-Mod: 16-Sep-26 at 12:54:13 by Bob Weiner +;; Last-Mod: 28-Sep-26 at 08:09:55 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -565,31 +565,33 @@ Assume caller has already checked that the current buffer is in (thing-at-point 'email)) (let (start-end) (and - ;; Org will throw a warning that `org-element-property' must be - ;; used only within an `org-mode' buffer, but it works in buffers - ;; outside of `org-mode' when on an Org link, so just suppress any - ;; warning. Using this allows recognition of Org links that are - ;; not surrounded by double sqare brackets, + ;; Org will throw a warning that `org-element-property' must be used + ;; only within an `org-mode' buffer, but it works in buffers outside + ;; of `org-mode' when on an Org link, so just suppress any warning. + ;; `syntax-ppss-flush-cache' is also called and can trigger an error, + ;; so ignore that as well. Using this allows recognition of Org + ;; links that are not surrounded by double sqare brackets, ;; e.g. file:my-file::my-text. (setq start-end - (with-suppressed-warnings - ((org-element)) - (or - (org-in-regexp - org-link-any-re - (let ((origin (point))) - (max - (save-excursion - (backward-paragraph) - (count-lines - (point) - origin)) - (save-excursion - (forward-paragraph) - (count-lines origin - (point)))))) - (org-in-regexp org-ts-regexp-both nil t) - (org-in-regexp org-tsr-regexp-both nil t)))) + (ignore-errors + (with-suppressed-warnings + ((org-element)) + (or + (org-in-regexp + org-link-any-re + (let ((origin (point))) + (max + (save-excursion + (backward-paragraph) + (count-lines + (point) + origin)) + (save-excursion + (forward-paragraph) + (count-lines origin + (point)))))) + (org-in-regexp org-ts-regexp-both nil t) + (org-in-regexp org-tsr-regexp-both nil t))))) ;; Don't treat this as an Org link if its entire description is a ;; HyWikiWord, e.g. [[hy:WikiWord]], [[WikiWord]] or ;; [[link][WikiWord]], as these are handled as implicit buttons. diff --git a/hui-mouse.el b/hui-mouse.el index c245e10f..4465ca0a 100644 --- a/hui-mouse.el +++ b/hui-mouse.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 04-Feb-89 -;; Last-Mod: 10-Sep-26 at 13:59:46 by Bob Weiner +;; Last-Mod: 29-Sep-26 at 17:08:43 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -470,21 +470,14 @@ The button's attributes are stored in the symbol, `hbut:current'.") ((eq major-mode 'pages-directory-mode) . ((pages-directory-goto) . (pages-directory-goto))) ;; - ;; If a variable holding an in-memory Hyperbole button object, e.g. hbut:current: - ;; Action Key - Jump to variable definition - ;; Assist Key - Display button attributes of any Hyperbole button symbol at point - ((and (setq hkey-value (smart-prog-at-tag-p)) - (hbut:is-p (intern-soft hkey-value))) - . ((ignore-errors (smart-prog-tag hkey-value)) . - (hbut:report (intern-soft hkey-value)))) - ;; ;; Handle programming language tag definition finding via xref. ;; For most programming languages use xref which supports various ;; Language Servers - ((and (setq hkey-value (smart-prog-at-tag-p)) - (smart-tags-find-p hkey-value)) + ((and (setq hkey-value (smart-prog-at-tag-p t)) + (smart-tags-find-p hkey-value) + (setq hkey-value (smart-flash-tag-at-point hkey-value))) . ((ignore-errors (smart-prog-tag hkey-value)) . - (ignore-errors (smart-prog-tag hkey-value)))) + (hbut:report (intern-soft hkey-value)))) ;; ;; Python files - ensure this comes before Imenu for more advanced ;; definition lookups @@ -510,16 +503,16 @@ The button's attributes are stored in the symbol, `hbut:current'.") (hypb:buffer-file-name) (smart-asm-at-tag-p)) . ((smart-asm) . (smart-asm nil 'next-tag))) ;; - ((setq hkey-value nil - hkey-value - (or (when (smart-lisp-mode-p) - (or (setq hkey-value (smart-lisp-at-load-expression-p)) - (smart-lisp-at-tag-p))) - ;; Tightly limit Lisp matches in change-log-mode but - ;; call this only if hkey-value is true since - ;; otherwise, already know there is no tag at point. - (when hkey-value - (smart-lisp-at-change-log-tag-p)))) + ((progn (setq hkey-value nil) + (and (setq hkey-value + (when (smart-lisp-mode-p) + (or (setq hkey-value (smart-lisp-at-load-expression-p)) + (smart-lisp-at-tag-p)))) + ;; Tightly limit Lisp matches in change-log-mode but + ;; call this only if hkey-value is true since + ;; otherwise, already know there is no tag at point. + (setq hkey-value (or (smart-lisp-at-change-log-tag-p) + hkey-value)))) . ((smart-lisp) . (smart-lisp 'show-doc))) ;; ;; @@ -2108,10 +2101,10 @@ If key is pressed: (2) at the end of buffer, show all buffer text (3) at the beginning of a heading line, cut the headings subtree from the buffer; - (4) on a heading line but not at the beginning or end, if headings subtree is - hidden then show it, otherwise hide it; - (5) at the end of a line, invoke `action-key-eol-function', typically to - scroll up a windowful." + (4) at the visible end of a line, invoke `action-key-eol-function', + typically to scroll up a windowful; + (5) on a heading line but not at the beginning or end, if heading's subtree + is hidden then show it, otherwise hide it." (interactive) (cond (smart-outline-cut @@ -2125,7 +2118,7 @@ If key is pressed: ;; Skip past start of current entry (progn (re-search-forward outline-regexp nil t) (smart-outline-to-entry-end t))))) - ((eolp) + ((and (eolp) (not (smart-outline-char-invisible-p))) (funcall action-key-eol-function)) ((zerop (smart-outline-level)) nil) @@ -2134,7 +2127,6 @@ If key is pressed: (outline-show-subtree)) (t (outline-hide-subtree)))) - (defun smart-outline-assist () "Collapse, expand, and move outline entries. Invoked via an assist key press when in `outline-mode'. It assumes that @@ -2148,10 +2140,10 @@ If assist key is pressed: (2) at the end of buffer, hide all bodies in buffer; (3) at the beginning of a heading line, cut the current heading (sans subtree) from the buffer; - (4) on a heading line but not at the beginning or end, if heading body is - hidden then show it, otherwise hide it; - (5) at the end of a line, invoke `assist-key-eol-function', typically to - scroll down a windowful." + (4) on a heading line but not at the beginning or end of the line, if + the heading's body is hidden, then show it, otherwise hide it; + (5) at the visible end of a line, invoke `assist-key-eol-function', + typically to scroll down a windowful." (interactive) (cond (smart-outline-cut (yank)) @@ -2162,7 +2154,7 @@ If assist key is pressed: ;; Skip past start of current entry (progn (re-search-forward outline-regexp nil t) (smart-outline-to-entry-end)))) - ((eolp) + ((and (eolp) (not (smart-outline-char-invisible-p))) (funcall assist-key-eol-function)) ((zerop (smart-outline-level)) nil) diff --git a/hui.el b/hui.el index c43009eb..882d33ee 100644 --- a/hui.el +++ b/hui.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 19-Sep-91 at 21:42:03 -;; Last-Mod: 15-Aug-26 at 14:19:21 by Bob Weiner +;; Last-Mod: 27-Sep-26 at 01:27:23 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -2224,12 +2224,16 @@ Buffer without File link-to-buffer-tmp" (setq val (hargs:at-p t))) (list 'link-to-file val)) ((derived-mode-p #'kotl-mode) - (list 'link-to-kcell (hypb:buffer-file-name) + (list 'link-to-kcell + (unless (equal (hbut:get-key-src t) buffer-file-name) + (hypb:buffer-file-name)) (concat (kcell-view:label) "=" (kcell-view:idstamp)))) ;; - ;; If region is active in the target buffer and it is one - ;; line or less, then do a link-to-string-match to the region string. + ;; If region is active in the target + ;; buffer and it is one line or less, + ;; then do a link-to-string-match to + ;; the region string. ((let ((region (and (use-region-p) (string-trim (buffer-substring-no-properties (region-beginning) (region-end))))) @@ -2303,8 +2307,10 @@ Buffer without File link-to-buffer-tmp" (hpath:shorten buffer-file-name src-dir) (line-number-at-pos)))) ;; - ;; If current line starts with an outline-regexp prefix and - ;; has a non-empty heading, use a link-to-string-match. + ;; If current line starts with an + ;; `outline-regexp' prefix and has a + ;; non-empty heading, use a + ;; link-to-string-match. ((and (hypb:buffer-file-name) (derived-mode-p 'outline-mode 'kotl-mode) (stringp outline-regexp) diff --git a/hversion.el b/hversion.el index b1e59022..a962b046 100644 --- a/hversion.el +++ b/hversion.el @@ -4,7 +4,7 @@ ;; Maintainer: Bob Weiner, Mats Lidell ;; ;; Orig-Date: 1-Jan-94 -;; Last-Mod: 22-Mar-24 at 08:51:59 by Bob Weiner +;; Last-Mod: 27-Sep-26 at 02:27:07 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -36,7 +36,7 @@ ;;; Public variables ;;; ************************************************************************ -(defconst hyperb:version "9.1.0" "GNU Hyperbole revision number.") +(defconst hyperb:version "9.2.0" "GNU Hyperbole revision number.") (defvar hyperb:mouse-buttons (if (or (and hyperb:microsoft-os-p (not (memq window-system '(w32 w64 x)))) diff --git a/hypb.el b/hypb.el index d0e521f4..5e5454ae 100644 --- a/hypb.el +++ b/hypb.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 6-Oct-91 at 03:42:38 -;; Last-Mod: 30-Jul-26 at 13:16:39 by Bob Weiner +;; Last-Mod: 26-Sep-26 at 10:36:35 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -376,6 +376,25 @@ The returned value may be compared with `equal' to `this-single-command-keys'. Use `key-description' to make it human readable." (where-is-internal cmd-sym keymap t)) +;;;###autoload +(defun hypb:count-visible-lines (start end) + "Count visible lines between START and END by scanning property boundaries. +`count-lines' returns 1 rather than 0 when all characters in the region are +invisible, hence this function." + (save-excursion + (goto-char start) + (let ((count 0)) + (while (< (point) end) + (if (invisible-p (point)) + ;; Fast-forward past the invisible block + (goto-char (next-single-char-property-change (point) 'invisible nil end)) + ;; Step line by line through visible text + (let ((line-end (min end (line-end-position)))) + (unless (= (point) line-end) + (setq count (1+ count))) + (goto-char (min end (1+ line-end)))))) + count))) + ;;;###autoload (defun hypb:count-visible-windows () "Return the number of visible, non-minibuffer windows across all frames." diff --git a/hyperbole.el b/hyperbole.el index 04098572..72bcb942 100644 --- a/hyperbole.el +++ b/hyperbole.el @@ -8,9 +8,9 @@ ;; Maintainer: Robert Weiner ;; Mats Lidell ;; Created: 06-Oct-92 at 11:52:51 -;; Last-Mod: 27-Jul-26 at 19:22:04 by Bob Weiner +;; Last-Mod: 27-Sep-26 at 02:24:35 by Bob Weiner ;; Released: 27-Jul-26 -;; Version: 9.1.0 +;; Version: 9.2.0 ;; Keywords: comm, convenience, files, frames, hypermedia, languages, mail, matching, mouse, multimedia, outlines, tools, wp ;; Package: hyperbole ;; Package-Requires: ((emacs "28")) @@ -313,8 +313,9 @@ of the commands." ;; or regions to the kill ring. (hkey-set-key [remap kill-ring-save] #'hui:kill-ring-save) ;; - ;; Override the {C-x r s} command from "register.el" when hyperbole-mode is active - ;; to allow copying delimited things, kcell references or regions to a register. + ;; Override the {C-x r s} command from "register.el" when hyperbole-mode + ;; is active to allow copying delimited things, kcell references or + ;; regions to a register. (hkey-set-key "\C-xrs" #'hui:copy-to-register) ;; ;; Bind {C-c @} to create a user-specified sized grid of windows diff --git a/kotl/EXAMPLE.kotl b/kotl/EXAMPLE.kotl index 687a74f2..54391c36 100644 --- a/kotl/EXAMPLE.kotl +++ b/kotl/EXAMPLE.kotl @@ -6,29 +6,33 @@ 2. The Koutliner produces hierarchically structured files consisting of trees of cells. - 2a. A cell is an element of the outline which has its own display label - and unique, permanent identifier (idstamp). + 2a. A cell is an element of the outline which has its own relative + label based on its outline location; this is typically displayed to + the left of the cell. - 2b. Idstamps support the creation of hyperlinks to cells which are - maintained as the structure of an outline changes. + 2b. Additionally, each cell has a unique, permanent identifier + (idstamp) used for permanent hyperlinks and are typically hidden. + These always start with a 0; the invisible root cell always has + both a relative label and idstamp of "0". Idstamps support quick, + permanent hyperlinks to cells regardless of where they move within + an outline. 3. Features implemented include: - 3a. Full on-screen editing without the need for any markup. - Click to type in a node (we call them cells) and just enter - text. Structure is automatically maintained for you. All of - the standard Emacs editor command set is supported both - through keyboard, menu and mouse interaction. Menu usage is - documented in the Hyperbole Manual; this document discusses + 3a. Full on-screen editing without the need for any markup. Click to + type in a node (we call them cells) and just enter text. Structure + is automatically maintained for you. All of the standard Emacs + editor command set is supported both through keyboard, menu and + mouse interaction. Menu usage is documented in the Hyperbole + Manual, see "(hyperbole)Menu Commands"; this document discusses features and their associated keyboard commands only. 3b. Advanced outline processing - 3b1. Autonumbering: Full auto-numbering in Augment (1a2) or - legal (1.1.2) formats. Augment style is the default - because it is shorter and quicker to read. Use {C-c C-l - ?} to see the full set of label types and to select a - different label type. + 3b1. Autonumbering: Full auto-numbering in Augment (1a2) or legal + (1.1.2) formats. Augment style is the default because it is + shorter and quicker to read. Use {C-c C-l ?} to see the full + set of label types and to select a different label type. 3b2. Label Separators: By default, the Koutliner separates labels from cell contents by two spaces. If you want to change the separator @@ -60,28 +64,86 @@ contents of the cell regardless of the location of point. You may then yank the contents into another cell with {C-y}. - 3b5. Tree Demotion and Promotion: Trees may be demoted pressing {TAB} - or promoted by pressing {M-TAB}. {M-0 TAB} and {M-0 M-TAB} will - demote and promote trees and will additionally refill each cell - that has not been specially marked to prevent refilling. + 3b5. Tree Demotion and Promotion: Demote or promote the tree at point + with {TAB} and {S-TAB}, respectively. These also refill the text + of each cell not marked with a no-fill property. To override + the no-fill property and refill all demoted/promoted cells, use + {M-0 TAB} and {M-0 S-TAB}. - {M-1 TAB} behaves specially. It toggles the function of {TAB} and - {M-TAB} so that they insert a tab character and remove the - previous character, respectively. Use {M-1 TAB} to toggle them - back to promoting and demoting trees. + Any other positive or negative prefix argument to these commands + promotes or demotes the tree up to a maximum of that number of + levels, depending on what the outline allows at any given point. - To insert a literal tab character, use {C-q TAB}. + See also <@ 3d=059>, for how tab key bindings differ when Org + Table minor mode is enabled. + + To insert a literal tab character, use {C-q TAB} or adjust the + value of `kotl-mode:emulate-tab' to suit your needs. + + 3b6. Org-style Visibility Cycling + + 3b6a. {M-1 TAB} toggles the function of {TAB} and {S-TAB} between + demotion/promotion and Org-like tree visibility cycling. + + 3b6b. When cycling is toggled on, {TAB} rotates the tree at point + through three different states (local cycling): + 1. FOLDED: Hide all but the first line of the tree at point + 2. CHILDREN: Hide all but the first line of the root cell at + point and its children + 3. SUBTREE: Show the entire tree at point; if there is no + subtree, switch directly from CHILDREN to FOLDED. + + 3b6c. When cycling is toggled on, {C-u TAB} or {S-TAB} rotates the the + whole outline through these three different states (global + cycling): + 1. OVERVIEW: Show only the first line of level 1 cells + 2. CONTENTS: Show the first line of all cells at all levels + 3. SHOW ALL: Show the entire outline. + + 3b6d. When cycling is toggled on, {S-TAB} With a numeric prefix + argument shows cells across the whole outline down to prefix + argument level, one line per cell. Zero means show all cell + levels. + + 3b6e. In summary, with kotl-mode:tab-flag = nil (default): + |-------------+------------------------+-------------------------| + | Prefix Arg | TAB Function | Shift-TAB Function | + |-------------+------------------------+-------------------------| + | nil | Demote Tree | Promote Tree | + | C-u | Demote Siblings | Promote Siblings | + | C-u C-u | Reset View Spec | Reset View Spec | + | C-u C-u C-u | Show Whole Outline | Show Whole Outlin | + | 0 | Demote and Refill Tree | Promote and Refill Tree | + | 1 | Toggle tab-flag | | + | Other ARG | Demote Tree ARG Levels | Promote Tree ARG Levels | + |-------------+------------------------+-------------------------| + + 3b6f. With kotl-mode:tab-flag = 'cycle: + |---------------+-------------------------+----------------------| + | Prefix Arg | TAB Function | Shift-TAB Function | + |---------------+-------------------------+----------------------| + | nil | Cycle Tree | Cycle Outline | + | C-u | Cycle Outline | Cycle Outline | + | C-u C-u | Reset View Spec | Reset View Spec | + | C-u C-u C-u | Show Whole Outline | Show Whole Outline | + | 1 | Toggle tab-flag | | + | Other Num ARG | Show tree ARG levels up | Show 1 line per cell | + | | | down to ARGth level | + |---------------+-------------------------+----------------------| + + See {C-h f kotl-mode:tab-command RET} and {C-h f + kotl-mode:untab-command RET} for full details. - 3b6. Cell and Tree Filling: {M-q} or {M-j} refills a paragraph within a - cell so that its lines wrap within the current margin settings. - {C-c M-q} or {C-c M-j} refills all paragraphs within a cell. - {C-M-q} or {C-M-j} refills all cells within a tree. + 3b7. Cell and Tree Filling: {M-q} or {M-j} refills a paragraph within + a cell so that its lines wrap within the current margin + settings. {C-c M-q} or {C-c M-j} refills all paragraphs within + a cell. {C-M-q} or {C-M-j} refills all cells within a tree. - Set the variable, kotl-mode:refill-flag, to t if you want moving, - promoting, demoting, exchanging, splitting and appending cells to - also automatically refill each cell. + Set the variable, `kotl-mode:refill-flag', to t if you want + moving, promoting, demoting, exchanging, splitting and appending + cells to also automatically refill each cell. - 3b7. Cursor Movement: In addition to normal Emacs movement commands, + 3b8. Cursor Movement: In addition to normal Emacs movement commands, you can move within a cell or from one cell or tree to another. {C-c ,} - Move to the beginning of the current cell. @@ -103,7 +165,7 @@ {C-c ^} - Move to the level 1 root of the current tree. {C-c $} - Move to the last cell in the tree rooted at point. - 3b8. Tree Movement and Copying: Entire outline trees can be moved or + 3b9. Tree Movement and Copying: Entire outline trees can be moved or copied within an outline with single commands. Simply invoke the desired command and then double click/press with the Action Key on the desired root cell for each argument for which you are @@ -134,7 +196,7 @@ of the root cell of the tree to move and then double click within the body of the root cell of the tree you want it to follow. - 3b9. Tree Text or Region Copying to a Non-Koutline Buffer: + 3b10. Tree Text or Region Copying to a Non-Koutline Buffer: {C-c M-c} - Copy text or active region to a non-Koutline buffer. {C-c C-@} - Copy text to an outgoing mail message. @@ -142,13 +204,13 @@ hidden/invisible text when copying. If yes, all invisible text is expanded before copying. - 3b10. Cell Transposition: The move and copy commands rearrange entire + 3b11. Cell Transposition: The move and copy commands rearrange entire trees. The following two commands exchange the locations of two cells. - 3b10a. {C-c e} prompts for two cell addresses and exchanges the cells. + 3b11a. {C-c e} prompts for two cell addresses and exchanges the cells. - 3b10b. {C-c t} does not prompt. It exchanges the current and + 3b11b. {C-c t} does not prompt. It exchanges the current and preceding cells. If there is no preceding cell it exchanges the current and next cell. @@ -160,7 +222,7 @@ there are fewer visible, it makes the current cell the last cell in the outline. - 3b11. Cell Splitting: You can split one cell into two adjacent cells + 3b12. Cell Splitting: You can split one cell into two adjacent cells with {C-c s}. This leaves the cell contents preceding point in the current cell, minus any trailing whitespace, and moves the contents following point to a new sibling cell which is inserted @@ -170,15 +232,15 @@ All cell attributes in the original cell are propagated to the new one. - 3b12. Cell Appending: {C-c +} appends the contents of a specified cell + 3b13. Cell Appending: {C-c +} appends the contents of a specified cell to the end of another cell. - 3b13. Hiding and Showing: Individual cells, branches, or particular + 3b14. Hiding and Showing: Individual cells, branches, or particular levels in the outline may be hidden or shown. These commands work even when an outline buffer is read-only, e.g. when its file is not checked out of a version control system yet. - 3b13a. {C-c C-h} - Hide (collapse) tree rooted at point. + 3b14a. {C-c C-h} - Hide (collapse) tree rooted at point. {C-c C-s} - Show (expand) tree rooted at point. {C-c C-a} - Show (expand) all cells in the outline. @@ -197,11 +259,11 @@ toggle display of blank lines between cells as well, e.g. {C-u C-c C-o}. Try it now. - 3b13b. A click or press of the Action Key within a cell's body, but + 3b14b. A click or press of the Action Key within a cell's body, but not on a button, toggles between hiding and showing the tree rooted at point. - 3b14. Cell Identifiers: Permanent ids are associated with each cell + 3b15. Cell Identifiers: Permanent ids are associated with each cell and can be used in hyperlinks that are maintained as cells are reordered in a file. (These ids may also be displayed in place of the outline level relative ids. Use {C-c C-l id RET}). @@ -210,7 +272,7 @@ outline. Permanent ids always begin with a 0, as in 012, to distinguish them from relative ids. - 3b15. Cell Attributes: Each cell has its own attribute list. The + 3b16. Cell Attributes: Each cell has its own attribute list. The attribute lists for the cells in the tree rooted at point can be displayed by pressing the Assist Key. {C-c h} prompts for a kcell id and displays its attributes. {C-u C-c h} prompts for a kcell @@ -224,29 +286,29 @@ {C-0 C-c C-i} sets an attribute of the invisible 0 root cell. {C--1 C-c C-i} removes an attribute of the invisible 0 root cell. - 3b16. File Insertion: The elements of another buffer or file may be + 3b17. File Insertion: The elements of another buffer or file may be inserted into a koutline as a set of cells by using the {C-x i} command. When prompted, you may use a buffer name or file name from which to insert. - 3b16a. The cells will be inserted as the successors of the current + 3b17a. The cells will be inserted as the successors of the current cell unless {C-u C-x i} is used and then they are inserted as the initial children of the current cell. - 3b16b. See the documentation for the variables, kimport:mode-alist and - kimport:suffix-alist, for information on mode and - suffix-specific conversions performed on files before they are - inserted. This same conversion process applies if you invoke - {M-x kotl-mode RET} in a non-koutline buffer or if you perform - a generic file import as described in <@ 3b16=063>. + 3b17b. See {C-h v kimport:mode-alist RET} and {C-h v + kimport:suffix-alist RET}, for information on mode and + suffix-specific conversions performed on files before they + are inserted. This same conversion process applies if you + invoke {M-x kotl-mode RET} in a non-koutline buffer or if you + perform a generic file import as described in <@ 4=063>. - 3b16c. Use {M-x kimport:insert-file-contents RET} to insert the + 3b17c. Use {M-x kimport:insert-file-contents RET} to insert the entire contents of a file into the current cell at the location of point. {M-x kimport:insert-buffer RET} does the same for an existing buffer. {C-x r i} does the same with the contents of a text register that you specify. - 3b17. File Importing: The Koutliner supports conversion of three + 3b18. File Importing: The Koutliner supports conversion of three types of files into koutline files. You can choose to import a file into an existing koutline, following the tree at point, or to create a new koutline of the imported file @@ -255,13 +317,13 @@ the file to import. Use one of the following commands if you want to control the type of importation explicitly. - 3b17a. Text Files + 3b18a. Text Files - 3b17a1. In a text buffer, use {M-x kotl-mode RET} to convert the + 3b18a1. In a text buffer, use {M-x kotl-mode RET} to convert the buffer into a koutline in-place. You will lose the old format of the buffer when you do this. - 3b17a2. Use {M-x kimport:text RET} and you will be prompted for a + 3b18a2. Use {M-x kimport:text RET} and you will be prompted for a text buffer or file to import and the new koutline buffer or file to create from its text. It will also import the contents, attributes and level structure of cells from a @@ -269,13 +331,13 @@ With this command, your original file remains intact. - 3b17b. Star Outline Files: Star outlines are standard Emacs outlines - where each entry begins with one or more asterisk characters. - Use {M-x kimport:star-outline RET} and you will be prompted - for the star outline buffer or file to import and the new - koutline buffer or file to create. + 3b18b. Star Outline Files: Star outlines are standard Emacs outlines + or Org outlines, where each entry begins with one or more + asterisk characters. Use {M-x kimport:star-outline RET} and + you will be prompted for the star outline buffer or file to + import and the new koutline buffer or file to create. - 3b17c. Augment Files Numbered on the Right-Side: (Skip this if you are + 3b18c. Augment Files Numbered on the Right-Side: (Skip this if you are unfamiliar with this sort of file). Files exported from the Augment system as text often have alphanumeric statement identifiers on the right side. @@ -284,48 +346,48 @@ for the augment buffer or file to import and the koutline to create. - 3b18. File Exporting: The Koutliner can produce a nicely indented HTML + 3b19. File Exporting: The Koutliner can produce a nicely indented HTML version of an outline with hyperlinks and hyperlink anchors intact. Use {M-x kexport:html RET} to export a koutline file or existing buffer. - 3b19. View Specification: Flexible views of outlines may be specified + 3b20. View Specification: Flexible views of outlines may be specified with succinct single character codes. - 3b19a. The current view spec is shown in the modeline after the buffer + 3b20a. The current view spec is shown in the modeline after the buffer name, delimited by <|>. - 3b19b. The current view spec is saved whenever the outline is saved. + 3b20b. The current view spec is saved whenever the outline is saved. The next time the outline is read in, this will be the initial view. The standard initial viewspec is <|ben>. - 3b19c. {C-c C-v} prompts for a view spec string in which the following + 3b20c. {C-c C-v} prompts for a view spec string in which the following codes are valid. Any invalid characters in a view spec are ignored. - 3b19c1. a: Show all cell levels and all lines in cells. + 3b20c1. a: Show all cell levels and all lines in cells. - 3b19c2. b: Turn on blank lines between cells. Without this code, + 3b20c2. b: Turn on blank lines between cells. Without this code, blank lines will be turned off. You can also use the {C-c b} key binding to toggle line numbers on and off independent of any other view settings. - 3b19c3. cN: Hide any lines greater than N in each cell. 0 means + 3b20c3. cN: Hide any lines greater than N in each cell. 0 means don't cutoff any lines. - 3b19c4. e: Show ellipses when some content is hidden (This is + 3b20c4. e: Show ellipses when some content is hidden (This is always on in modern versions of Emacs; it cannot be turned off). - 3b19c5. lN: Hide cells at levels deeper than N. 0 means don't hide + 3b20c5. lN: Hide cells at levels deeper than N. 0 means don't hide any cells. - 3b19c6. n: Turn on the default label type. + 3b20c6. n: Turn on the default label type. n0: Display idstamps. n1: Display alpha labels. n.: Display legal labels. - 3b19d. Press the Action Key on this view spec of <|c2l1> to turn off + 3b20d. Press the Action Key on this view spec of <|c2l1> to turn off blank lines, to clip each cell after its second line, and to hide all cells below level one. Use {C-u C-c C-a} to restore a view of all cells. @@ -382,30 +444,29 @@ 3d2. Here are two table examples: - |----------------------------+---------------------------| - | Promotion Inside Org Table | Demotion Inside Org Table | - |----------------------------+---------------------------| - | {M-0 M-TAB} | {M-0 TAB} | - | {C-c C-,} | {C-c C-.} | - | {C-c C-<} | {C-c C->} | - |----------------------------+---------------------------| + |---------------------------------------+----------------------------| + | Demotion Inside Org Table | Promotion Inside Org Table | + |---------------------------------------+----------------------------| + | {M-0 TAB} (kotl-mode:tab-flag = nil) | {M-0 S-TAB} | + | {C-c C-.} | {C-c C-,} | + | {C-c C->} | {C-c C-<} | + |---------------------------------------+----------------------------| - |-----------------------------+----------------------------| - | Promotion Outside Org Table | Demotion Outside Org Table | - |-----------------------------+----------------------------| - | {M-TAB} or {Shift-TAB} | {TAB} | - | {M-} | {M-} | - | {C-c C-,} | {C-c C-.} | - | {C-c C-<} | {C-c C->} | - |-----------------------------+----------------------------| + |------------------------------------+-----------------------------| + | Demotion Outside Org Table | Promotion Outside Org Table | + |------------------------------------+-----------------------------| + | {TAB} (kotl-mode:tab-flag = nil) | {S-TAB} | + | {M-} | {M-} | + | {M-S-} | {M-S-} | + | {C-c C-.} | {C-c C-,} | + | {C-c C->} | {C-c C-<} | + |------------------------------------+-----------------------------| - To toggle table editing mode on or off, press the Action - Key on any of the table | symbols and then edit as you - would any Org table. Note how the above tables show how - TAB and Shift-TAB behave differently in and outside of - tables; use M-TAB rather than Shift-TAB for tree promotion - consistency anywhere. See "(Org)Tables" for help on utilizing - tables. + To toggle table editing mode on or off, press the Action Key on + any of the table | delimiters and then edit as you would any Org + table. Note how the above tables show how {TAB} and {S-TAB} + behave differently in and outside of tables. See "(Org)Tables" + for how to work with tables. 3e. The Koutliner code runs on any system that supports Emacs, whether under a window system or from a dumb terminal display. @@ -428,7 +489,7 @@  "ben" ;; kvspec:current -69 ;; id-counter +78 ;; id-counter alpha ;; label-type 4 ;; label-min-width ". " ;; label-separator @@ -436,15 +497,15 @@ alpha ;; label-type  ;; depth-first kcell attributes [[0 - (creator "rsw@gnu.org" create-time "20231105:21:29:07" id-counter 69)] + (creator "rsw@gnu.org" create-time "20231105:21:29:07" id-counter 78)] [2 (creator "rsw@gnu.org" create-time "20231105:21:29:07")] [3 (creator "rsw@gnu.org" create-time "20231105:21:29:07")] [4 (creator "rsw@gnu.org" create-time "20231105:21:29:07")] - [5 - (creator "rsw@gnu.org" create-time "20231105:21:29:07")] + [70 + (creator "rsw@gnu.org" create-time "20260926:21:49:08")] [6 (creator "rsw@gnu.org" create-time "20231105:21:29:07")] [7 @@ -461,6 +522,20 @@ alpha ;; label-type (creator "rsw@gnu.org" create-time "20231105:21:29:07")] [13 (creator "rsw@gnu.org" create-time "20231105:21:29:07")] + [71 + (creator "rsw@gnu.org" create-time "20260928:21:33:05")] + [73 + (creator "rsw@gnu.org" create-time "20260929:05:48:56")] + [74 + (creator "rsw@gnu.org" create-time "20260929:05:49:02")] + [75 + (creator "rsw@gnu.org" create-time "20260929:05:49:09")] + [76 + (creator "rsw@gnu.org" create-time "20260929:05:49:14")] + [77 + (creator "rsw@gnu.org" create-time "20260929:05:49:25")] + [78 + (creator "rsw@gnu.org" create-time "20260929:05:50:44")] [14 (creator "rsw@gnu.org" create-time "20231105:21:29:07")] [15 @@ -573,4 +648,4 @@ alpha ;; label-type (creator "rsw@gnu.org" create-time "20231105:21:29:07")] [69 (creator "rsw@gnu.org" create-time "20231105:21:29:07")] - nil] + nil nil nil] diff --git a/kotl/kfile.el b/kotl/kfile.el index aab5d84f..69aa4399 100644 --- a/kotl/kfile.el +++ b/kotl/kfile.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 10/31/93 -;; Last-Mod: 30-Apr-26 at 13:24:40 by Bob Weiner +;; Last-Mod: 23-Sep-26 at 11:27:18 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -179,16 +179,13 @@ Return the new view." (set-buffer buffer) (not (or (stringp ver-string) (setq ver-string (kfile:is-p))))) (error "(kfile:read): `%s' is not a koutline file" buffer)) - ((equal ver-string "Kotl-4.0") - (kfile:read-v4-or-v3 buffer nil)) - ((equal ver-string "Kotl-3.0") - (kfile:read-v4-or-v3 buffer t)) - ((equal ver-string "Kotl-2.0") - (kfile:read-v2 buffer)) - ((equal ver-string "Kotl-1.0") - (error "(kfile:read): V1 koutlines are no longer supported")) - (t (error "(kfile:read): `%s' has unknown kotl version, %s" - buffer ver-string)))) + ((pcase ver-string + ("Kotl-4.0" (kfile:read-v4-or-v3 buffer nil)) + ("Kotl-3.0" (kfile:read-v4-or-v3 buffer t)) + ("Kotl-2.0" (kfile:read-v2 buffer)) + ("Kotl-1.0" (error "(kfile:read): V1 koutlines are no longer supported")) + (_ (error "(kfile:read): `%s' has unknown kotl version, %s" + buffer ver-string)))))) (defun kfile:read-v2 (buffer) "Create a kotl view by reading kotl version-2 BUFFER. Return the new view." @@ -238,8 +235,8 @@ If V3-FLAG is true, read as a version-3 buffer." (search-forward "\n\^_" nil t 2) ;; Read rest of file data. (unless v3-flag ;; V3 files did not store viewspecs. - (kvspec:initialize) - (setq kvspec:current (read))) + (setq kvspec:current (read) + kvspec:default kvspec:current)) (setq cell-count (read) label-type (read) label-min-width (read) diff --git a/kotl/klink.el b/kotl/klink.el index 70cf0c42..8af75ba5 100644 --- a/kotl/klink.el +++ b/kotl/klink.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 15-Nov-93 at 12:15:16 -;; Last-Mod: 4-Jun-26 at 11:19:52 by Bob Weiner +;; Last-Mod: 21-Sep-26 at 09:17:09 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -329,8 +329,8 @@ See `actypes::link-to-kotl' for valid KLINK formats." (prog1 (hact 'link-to-kotl klink) (when (derived-mode-p 'kotl-mode) (save-excursion - ;; Update klink label if need be, which might be in a different buffer - ;; than the current one. + ;; Update klink label if need be, which might be in a different + ;; buffer than the current one. (klink:update-label klink start-pos obuf)))))) (defun klink:parse (reference) @@ -368,8 +368,8 @@ See documentation for `kcell:ref-to-id' for valid cell-ref formats." "Replace out of date relative id in a link reference of the form, relid=idstamp." (with-current-buffer link-buf (if buffer-read-only - (message "Relative label should be `%s' in klink <%s>." - new-label klink) + (message "Buffer <%s> is read-only; update relative koutline label to `%s' in ref \"%s\"" + (buffer-name) new-label klink) (goto-char start) (cond ((or (looking-at "<\\s-*[#@]\\s-*") (looking-at "[^,]+?[#,]\\s-*")) @@ -380,7 +380,8 @@ See documentation for `kcell:ref-to-id' for valid cell-ref formats." (defun klink:update-label (klink start link-buf) "Update label of KLINK if its relative cell id has changed. -Assume point is in klink referent buffer, where the klink points." +Assume point is in klink referent buffer, where the klink points. +START is the start of the KLINK reference in the source LINK-BUF." (and (stringp klink) (string-match "[#@,]\\s-*\\([*0-9][*.0-9a-zA-Z]*\\)\\s-*=\\s-*0[0-9]*" klink) diff --git a/kotl/kotl-mode.el b/kotl/kotl-mode.el index f29c1636..254d6202 100644 --- a/kotl/kotl-mode.el +++ b/kotl/kotl-mode.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 6/30/93 -;; Last-Mod: 16-May-26 at 17:18:12 by Bob Weiner +;; Last-Mod: 29-Sep-26 at 01:35:06 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -21,7 +21,8 @@ (eval-when-compile '(require 'klink)) ;; hibtypes.el loads this at run-time (eval-and-compile (mapc #'require '(cl-lib delsel hsettings hmail hypb hyrolo - kfile klabel outline org org-table kotl-orgtbl))) + kfile klabel outline org org-table + kotl-orgtbl))) ;;; ************************************************************************ ;;; Public declarations @@ -40,6 +41,25 @@ ;;; Public variables ;;; ************************************************************************ + +;; Derived from `org-cycle-emulate-tab' +(defcustom kotl-mode:emulate-tab nil + "Where `kotl-mode:tab-command' should run `kotl-mode:indent-line'. +Values are: +nil - Never +white - Only in completely white lines +whitestart - Only at the beginning of lines, before the first non-white char +t - Everywhere except in headlines +exc-hl-bol - Everywhere except at the start of a headline +If is used in a place where it does not emulate global , the +value of `kotl-mode:tab-flag' determines what it does." + :group 'hyperbole-koutliner + :type '(choice (const :tag "Never" nil) + (const :tag "Only in completely white lines" white) + (const :tag "Before first char in a line" whitestart) + (const :tag "Everywhere except in headlines" t) + (const :tag "Everywhere except at bol in headlines" exc-hl-bol))) + (defcustom kotl-mode:indent-tabs-mode t "Non-nil means {\\[kotl-mode:tab-command]} may insert literal tab characters. Tab characters are inserted rather than space characters when @@ -66,12 +86,33 @@ Default value is nil." :group 'hyperbole-koutliner) (defcustom kotl-mode:tab-flag nil - "Non-nil means {\\[kotl-mode:tab-command]} inserts a literal tab character and {\\[kotl-mode:untab-command]} deletes backward. -Nil means {\\[kotl-mode:tab-command]} demotes the current tree and -{\\[kotl-mode:untab-command]} promotes the tree. The default is nil." + "Determines the behavior of the and S- keys. + +The 3 allowed values are: + +1. Nil, the default, means {\\[kotl-mode:tab-command]} demotes the current +tree and {\\[kotl-mode:untab-command]} promotes the tree. See their +associated documentation strings for deeper details. + +2. The symbol, cycle, enables org-like outline view cycling when point is in +a context other than that specified by `org-cycle-emulate-tab'. Then +{\\[kotl-mode:tab-command]} cycles the current tree through three views. +With a universal prefix arg, C-u, or via {\\[kotl-mode:untab-command]}, it +cycles the whole outline through three views as defined in +`kotl-mode:cycle'. + +3. Any other non-nil value, makes {\\[kotl-mode:tab-command]} indent lines +and insert literal characters according to the context setting of +`org-cycle-emulate-tab'. It also determines where +{\\[kotl-mode:untab-command]} deletes backward." :type 'boolean :group 'hyperbole-koutliner) +(defvar kotl-mode:before-tab-emulation-hook nil + "Hook for functions to attach themselves to . +This hook runs after every other option for been exhausted, but +before indentation and \t insertion take place.") + (defvar kotl-mode:to-valid-position-commands '(kotl-mode:orgtbl-self-insert-command kotl-mode:kill-or-copy-region @@ -86,6 +127,18 @@ Nil means {\\[kotl-mode:tab-command]} demotes the current tree and yank-rectangle) "List of command symbols to move to a valid Koutline position before executing.") +(defvar kotl-mode:tab-post-hook nil + "Hook for Koutline functions run after invoking . +This hook runs as the last action when TAB is pressed. Each function +is called with no arguments. If any function in this hook returns t, +none of the following hook functions are run.") + +(defvar kotl-mode:tab-pre-hook nil + "Hook for Koutline functions run prior to invoking . +This hook runs as the first action when is pressed. Each function +is called with no arguments. If any function in this hook returns t, +any other actions that would have been run by will not occur.") + (defvar kotl-mode-map nil "Keymap containing koutliner editing and viewing commands.") @@ -240,8 +293,8 @@ It provides the following keys: (if (setq version (kfile:is-p)) ;; Koutline file that has been loaded and formatted for editing. (if (kview:is-p kotl-kview) - ;; The buffer might have been widened for inspection, so narrow to cells - ;; only. + ;; The buffer might have been widened for inspection, so + ;; narrow to cells only. (kfile:narrow-to-kcells) (kfile:read (current-buffer) @@ -787,7 +840,7 @@ Does not remove any newlines as `just-one-space' does when given a negative N." (defun kotl-mode:kill-region (start end &optional copy-flag) "Kill region between START and END within a single kcell. -With optional COPY-FLAG equal to t, copy region to kill ring but does not +With optional COPY-FLAG equal to t, copy region to kill ring but do not kill it. With COPY-FLAG any other non-nil value, return region as a string without affecting kill ring. @@ -796,11 +849,10 @@ active region, and point is not on a whitespace character, then kill/copy the selectable thing at point including any delimiters; see `hui:selectable-thing-and-bounds'. -If the buffer is read-only and COPY-FLAG is nil, the region will not be -deleted but it will be copied to the kill ring and then an error will be -signaled. +If the buffer is read-only and COPY-FLAG is nil, don't delete the region +but copy it to the kill ring and then signal an error. -If a completion is active, this aborts the completion only." +If a completion is active, abort only the completion." (interactive (progn (barf-if-buffer-read-only) (list (when mark-active (region-beginning)) @@ -1013,20 +1065,126 @@ With prefix arg TURN-OFF or at begin of line, turns fill prefix off." (interactive "P") (set-fill-prefix (or turn-off (kotl-mode:bolp)))) -(defun kotl-mode:tab-command (arg) - "Tab over by ARG tab stops or demote the current tree a maximum of ARG levels. -Which command is run depends on the value of `kotl-mode:tab-flag'. Toggle -its value by sending this command an explicit ARG of 1. Use nil for ARG to -run the tab command once. - -See also the documentation strings for `kotl-mode:indent-line' and -`kotl-mode:demote-tree'." - (interactive "*P") - (if (eq arg 1) - (call-interactively 'kotl-mode:toggle-tab-flag) - (setq arg (prefix-numeric-value arg)) - (cond (kotl-mode:tab-flag (kotl-mode:indent-line arg)) - (t (kotl-mode:demote-tree arg))))) +;; Derived from and meant to emulate `org-cycle' +(defun kotl-mode:tab-command (&optional arg) + "Tab key demote, cycle visibility or insert tabs for Koutlines. +Optional prefix ARG is used as a flag or a numeric argument depending on its +value. + +When `kotl-mode:tab-flag' is: + \\='cycle: cycle views of parts of the outline; + nil: demote trees a maximum of ARG levels; + t: indent or tab over by ARG tab stops. +Use {M-1 TAB} to toggle its value between `cycle' and nil. + +See also the documentation strings for `kotl-mode:indent-line', +`kotl-mode:demote-tree', and `kotl-mode:demote-siblings'. + +If in `cycle' mode without a prefix argument, call `kotl-mode:cycle-locally' +to rotate the tree at point through three different states (local cycling): + 1. FOLDED: Hide all but the first line of the tree at point + 2. CHILDREN: Hide all but the first line of the root cell at point and its + children + 3. SUBTREE: Show the entire tree at point; if there is no subtree, switch + directly from CHILDREN to FOLDED. + +If in `cycle' mode with a single universal argument, C-u, call +`kotl-mode:cycle-globally' to cycle the whole outline through +these three different states: + 1. OVERVIEW: Show only the first line of level 1 cells + 2. CONTENTS: Show the first line of all cells at all levels + 3. SHOW ALL: Show the entire outline. + +With a prefix argument of 0 when not in `cycle' mode, demote the tree at +point and refill all its cells regardless of any cell `no-fill' property. + +Any numeric prefix argument other than 0 or 1 is available for use by any +function this calls. + +The variable, `kotl-mode:emulate-tab', works just like +`org-cycle-emulate-tab' does in Org mode, except that it is disabled, set to +nil by default. Use it if you haven't set `kotl-mode:tab-flag' to t. It +determines where the TAB key indents or inserts TAB characters rather than +performing the functions detailed above. See its documentation string for +valid settings." + (interactive "P") + (unless (run-hook-with-args-until-success 'kotl-mode:tab-pre-hook) + ;; Skip additional org-like code originally in the above `unless' clause + ;; that promotes/demotes current tree through outline levels as it would + ;; be visually unappealing moving entire koutline trees since we must + ;; maintain proper outline structure whereas Org does not. -- RSW, + ;; 2026-09-23 + (cond + ((eq kotl-mode:tab-flag t) + ;; Indent or insert s anywhere + (kotl-mode:indent-line arg)) + ((equal arg '(4)) + ;; Single universal `arg' + (if (eq kotl-mode:tab-flag 'cycle) + ;; Cycle through whole outline views + (kotl-mode:cycle-globally) + ;; Demote siblings starting and below point up to 1 level + (kotl-mode:demote-siblings 1))) + ((equal arg '(16)) + ;; Double universal `arg'; + ;; Reset viewspec to the default + (setq last-command 'dummy) + (kvspec:reset) + (let ((message-log-max nil)) + (message "View spec reset to default: %s" kvspec:default))) + ((equal arg '(64)) + ;; Triple universal `arg' + ;; Show all trees + (kotl-mode:show-all) + (let ((message-log-max nil)) + (message "Entire outline expanded"))) + ((eq arg 1) + (if (called-interactively-p 'interactive) + (call-interactively 'kotl-mode:toggle-tab-flag) + (kotl-mode:toggle-tab-flag))) + ((and (eq arg 0) (not (eq kotl-mode:tab-flag 'cycle))) + (kotl-mode:demote-tree 0)) + ((and (integerp arg) (eq kotl-mode:tab-flag 'cycle)) + ;; Expand the outline tree up to ARG levels above point; if negative, + ;; means up to ARG levels below point + (save-excursion + (kotl-mode:up-level arg) + (kotl-mode:show-tree))) + (t + ;; Null or any other prefix arg; + (let ((pos (point))) + (cond ((or (kotl-mode:bocp) + (and (save-excursion + (kotl-mode:beginning-of-line) + (kotl-mode:bocp)) + (not (eq kotl-mode:emulate-tab 'exc-hl-bol)))) + (if (eq kotl-mode:tab-flag 'cycle) + ;; Cycle through views of the tree rooted at point + (kotl-mode:cycle-locally) + ;; Demote tree rooted at point up to ARG levels + (kotl-mode:demote-tree arg))) + ;; emulation and template completion + (buffer-read-only + (goto-char (kcell-view:start))) + ((run-hook-with-args-until-success + 'kotl-mode:before-tab-emulation-hook)) + ((and (eq kotl-mode:emulate-tab 'exc-hl-bol) + (not (kotl-mode:bocp))) + (kotl-mode:indent-line arg)) + ((or (eq kotl-mode:emulate-tab t) + (and (memq kotl-mode:emulate-tab '(white whitestart)) + (save-excursion + (forward-line 0) (looking-at "[ \t]*")) + (or (and (eq kotl-mode:emulate-tab 'white) + (= (match-end 0) (line-end-position))) + (and (eq kotl-mode:emulate-tab 'whitestart) + (>= (match-end 0) pos))))) + (kotl-mode:indent-line arg)) + (t + (save-excursion + (goto-char (kcell-view:start)) + (kotl-mode:tab-command arg))))))) + (run-hook-with-args-until-success 'kotl-mode:tab-post-hook))) (defun kotl-mode:toggle-indent-tabs-mode () "Toggle the value of `kotl-mode:indent-tabs-mode' and explain its current usage." @@ -1038,15 +1196,29 @@ See also the documentation strings for `kotl-mode:indent-line' and (message "Tab insertion now uses spaces to form tabs.")))) (defun kotl-mode:toggle-tab-flag () - "Toggle the value of `kotl-mode:tab-flag' and explain its current usage." (interactive) - (setq kotl-mode:tab-flag (not kotl-mode:tab-flag)) - (if (called-interactively-p 'interactive) - (if kotl-mode:tab-flag - (message (substitute-command-keys - "{\\[kotl-mode:tab-command]} now inserts literal tabs; {\\[kotl-mode:untab-command]} removes tabs.")) - (message (substitute-command-keys - "{\\[kotl-mode:tab-command]} now demotes trees; {\\[kotl-mode:untab-command]} promotes trees."))))) + "Toggle the value of `kotl-mode:tab-flag' and explain its current usage. +This now toggles TAB/M-TAB between the traditional Koutliner demote/promote +commands and the Org-compatible cycling commands." + (if (eq kotl-mode:tab-flag 'cycle) + (progn + (setq kotl-mode:tab-flag + ;; custom default value + (eval (car (get 'kotl-mode:tab-flag 'standard-value)))) + (when (eq kotl-mode:tab-flag 'cycle) + (setq kotl-mode:tab-flag nil)) + (when (called-interactively-p 'interactive) + (message (substitute-command-keys + (format "Disabled Org-style TAB cycling; use {\\[%s]} to demote trees and {\\[%s]} to promote trees" + (if kotl-mode:tab-flag + 'kotl-mode:demote-tree + 'kotl-mode:tab-command) + (if kotl-mode:tab-flag + 'kotl-mode:promote-tree + 'kotl-mode:untab-command)))))) + (setq kotl-mode:tab-flag 'cycle) + (when (called-interactively-p 'interactive) + (message (substitute-command-keys "Enabled Org-style TAB cycling; use {\\[kotl-mode:demote-tree]} to demote trees and {\\[kotl-mode:promote-tree]} to promotes trees"))))) (defun kotl-mode:transpose-chars (arg) "Interchange characters around point, moving forward one character. @@ -1128,22 +1300,108 @@ or after point and around or after mark are interchanged." (interactive "*p") (transpose-subr 'kotl-mode:forward-word (prefix-numeric-value arg))) -(defun kotl-mode:untab-command (arg) - "Delete backwards or promote the current tree. -Delete backwards by ARG tab stops or promote the current tree a -maximum of ARG levels. Which command is run depends on the value -of `kotl-mode:tab-flag'. Toggle its value by sending this -command an explicit ARG of 1. Use nil for ARG to run the untab -command once. - -See also the documentation strings for `kotl-mode:delete-backward-char' and -`kotl-mode:promote-tree'." - (interactive "*P") - (if (eq arg 1) - (call-interactively 'kotl-mode:toggle-tab-flag) - (setq arg (prefix-numeric-value arg)) - (cond (kotl-mode:tab-flag (kotl-mode:delete-backward-char arg)) - (t (kotl-mode:promote-tree arg))))) +(defun kotl-mode:untab-command (&optional arg) + "Shift-tab key promote, cycle visibility or delete tabs for Koutlines. +Optional prefix ARG is used as a flag or a numeric argument depending on its +value. + +When `kotl-mode:tab-flag' is: + \\='cycle: cycle views of the whole outline; + nil: promote trees a maximum of ARG levels; + t: delete backward ARG characters. +Use {M-1 TAB} to toggle its value between `cycle' and nil. + +See also the documentation strings for `kotl-mode:delete-backward-char', +`kotl-mode:promote-tree', and `kotl-mode:promote-siblings'. + +If in `cycle' mode and given either no prefix argument or a single +unviversal argument, C-u, call `kotl-mode:cycle-globally' to cycle the whole +outline through these three different states: + 1. OVERVIEW: Show only the first line of level 1 cells + 2. CONTENTS: Show the first line of all cells at all levels + 3. SHOW ALL: Show the entire outline. + +With a prefix argument of 0 when not in `cycle' mode, promote the tree at +point and refill all its cells regardless of any cell `no-fill' property. + +With a numeric prefix argument when in `cycle' mode, show cells across the +whole outline down to prefix argument level, one line per cell. Zero means +show all cell levels. + +The variable, `kotl-mode:emulate-tab', works just like +`org-cycle-emulate-tab' does in Org mode, except that it is disabled, set to +nil by default. Use it if you haven't set `kotl-mode:tab-flag' to t. It +determines where the TAB key indents or inserts TAB characters rather than +performing the functions detailed above. See its documentation string for +valid settings." + (interactive "P") + (unless (run-hook-with-args-until-success 'kotl-mode:tab-pre-hook) + (cond + ((eq kotl-mode:tab-flag t) + ;; Delete backward ARG s or other characters + (kotl-mode:delete-backward-char arg)) + ((equal arg '(4)) + ;; Single universal `arg' + (if (eq kotl-mode:tab-flag 'cycle) + ;; Cycle through whole outline views + (kotl-mode:cycle-globally) + ;; Promote siblings starting and below point up to 1 level + (kotl-mode:promote-siblings 1))) + ((equal arg '(16)) + ;; Double universal `arg'; + ;; Reset viewspec to the default + (setq last-command 'dummy) + (kvspec:reset) + (let ((message-log-max nil)) + (message "View spec reset to default: %s" kvspec:default))) + ((equal arg '(64)) + ;; Triple universal `arg' + ;; Show all trees + (kotl-mode:show-all) + (let ((message-log-max nil)) + (message "Entire outline expanded"))) + ((and (eq arg 0) (not (eq kotl-mode:tab-flag 'cycle))) + (kotl-mode:promote-tree 0)) + ((and (integerp arg) (eq kotl-mode:tab-flag 'cycle)) + ;; Across the whole outline, show cells down to ARG level, 1 line per + ;; cell; 0 means show all cell levels + (kotl-mode:show-to-level arg) + (kotl-mode:show-lines-per-cell 1)) + (t + ;; Null or any other prefix arg; + (let ((pos (point))) + (cond ((or (kotl-mode:bocp) + (and (save-excursion + (kotl-mode:beginning-of-line) + (kotl-mode:bocp)) + (not (eq kotl-mode:emulate-tab 'exc-hl-bol)))) + (if (eq kotl-mode:tab-flag 'cycle) + ;; Cycle through whole outline views + (kotl-mode:cycle-globally) + ;; Promote tree rooted at point up to ARG levels + (kotl-mode:promote-tree arg))) + ;; emulation and template completion + (buffer-read-only + (goto-char (kcell-view:start))) + ((run-hook-with-args-until-success + 'kotl-mode:before-tab-emulation-hook)) + ((and (eq kotl-mode:emulate-tab 'exc-hl-bol) + (not (kotl-mode:bocp))) + (kotl-mode:delete-backward-char arg)) + ((or (eq kotl-mode:emulate-tab t) + (and (memq kotl-mode:emulate-tab '(white whitestart)) + (save-excursion + (forward-line 0) (looking-at "[ \t]*")) + (or (and (eq kotl-mode:emulate-tab 'white) + (= (match-end 0) (line-end-position))) + (and (eq kotl-mode:emulate-tab 'whitestart) + (>= (match-end 0) pos))))) + (kotl-mode:delete-backward-char arg)) + (t + (save-excursion + (goto-char (kcell-view:start)) + (kotl-mode:untab-command arg))))))) + (run-hook-with-args-until-success 'kotl-mode:tab-post-hook))) (defun kotl-mode:zap-to-char (arg char) "Kill up to and including prefix ARG'th occurrence of CHAR. @@ -1159,7 +1417,7 @@ Goes backward if ARG is negative; error if CHAR not found." (defun kotl-mode:append-cell (contents-cell append-to-cell) "Append CONTENTS-CELL (a cell ref) to APPEND-TO-CELL (a cell ref). APPEND-TO-CELL is refilled if neither cell has a no-fill property and -kotl-mode:refill-flag is enabled." +`kotl-mode:refill-flag' is enabled." (interactive (let* ((label (kcell-view:label))) (hargs:iform-read @@ -2081,7 +2339,7 @@ The paragraph marked is the one that contains point or follows point." (push-mark (point)) (kotl-mode:end-of-buffer) (push-mark (point) nil t) - (kotl-mode:beginning-of-buffer)) + (goto-char (point-min))) (defun kotl-mode:next-cell (arg) "Move to prefix ARGth next cell (any level) within current view. @@ -2244,6 +2502,38 @@ Error if called interactively and cannot move to the desired cell." ;;; Predicates ;;; ------------------------------------------------------------------------ +(defun kotl-mode:all-cells-all-lines-visible-p () + ;; When all lines of all cells are visible (none are collapsed) + (let ((result (delq nil (kview:map-tree (lambda (_kview) + (kcell-view:collapsed-p)) + kotl-kview + t)))) + ;; result will be `nil' if all lines in all cells are visible, in + ;; which case, return t; otherwise, return nil. + (not result))) + +(defun kotl-mode:all-cells-one-line-visible-p () + ;; When only the first line of all cells are visible: + (let ((result (delq 1 (kview:map-tree + (lambda (_kview) + ;; Count visible lines only + (hypb:count-visible-lines (kcell-view:start) + (kcell-view:end-contents))) + kotl-kview + t)))) + ;; Result will be `nil' if all cells display a single line, in + ;; which case, return t; otherwise, return nil. + (not result))) + +(defun kotl-mode:at-cell-empty-first-line-p () + "If point is at the end of an empty first cell line, return t, else nil." + (let ((opoint (point))) + (save-excursion + (goto-char (kcell-view:start)) + (and (<= (point) opoint) + (looking-at "[ \t]*$") + (>= (match-end 0) opoint))))) + (defun kotl-mode:bobp () "Return point if at the start of the first cell in kview, else nil." (interactive) @@ -2318,11 +2608,57 @@ With optional NEXT-CHAR-VISIBLE, return t only if the following char is visible. (kotl-mode:to-end-of-line) (looking-at "\n*\\'"))) +(defun kotl-mode:root-first-line-only-shown-p () + ;; The only visible line in the tree is the first line of the root + ;; and there exists at least one (non-visible) child cell in the tree; + ;; otherwise, this returns nil. + (and (kcell-view:child-p) + (let ((result (delq nil (kview:map-tree (lambda (_kview) + ;; Count visible lines only + (count-lines (kcell-view:start) + (kcell-view:end-contents) + t)) + kotl-kview + nil t)))) + (equal result '(1))))) + +(defun kotl-mode:top-cells-one-line-visible-p () + ;; When only the first line of all level 1 cells are visible and + ;; everything else is invisible + (let (count + level + result) + (setq result (delq nil (kview:map-tree + (lambda (_kview) + (setq level (kcell-view:level) + ;; Count visible lines only + count (hypb:count-visible-lines + (kcell-view:start) + (kcell-view:end-contents))) + ;; Return t in any case that would make the + ;; predicate false, since we invert at the end + ;; of the whole function + (when (or (and (= level 1) + (/= count 1)) + (and (/= level 1) + (/= count 0))) + t)) + kotl-kview + t))) + ;; result will be `nil' if all level 1 cells display a single line and + ;; other cells are invisible, in which case, return t; otherwise, return + ;; nil. + (not result))) + +(defun kotl-mode:tree-collapsed-p () + "Return t if any cell in the current tree is collapsed or invisible, else nil." + (when (delq nil (kview:map-tree (lambda (_kview) (kcell-view:collapsed-p)) kotl-kview)) + t)) + ;;; ------------------------------------------------------------------------ ;;; Smart Key Support ;;; ------------------------------------------------------------------------ - (defun kotl-mode:action-key () "Collapses, expands, links to, and scrolls through koutline cells. Invoked via a key press when in kotl-mode. It assumes that its caller has @@ -2331,14 +2667,14 @@ moved the cursor to the selected buffer. If key is pressed: (1) at the end of buffer, uncollapse and unhide all cells in view; - (2) within a cell, if its subtree is hidden then show it, - otherwise hide it; + (2) at the end of a visible line, call the `action-key-eol-function'; (3) between cells or within the read-only indentation region to the left of a cell, then move point to prior location and begin creation of a klink to some other outline cell; press the Action Key twice to select the link referent cell; - (4) anywhere else, invoke `action-key-eol-function', typically to scroll up - a windowful." + (4) on a | character within an Org-style table, toggle Org Table minor mode; + (5) in an Org-style table, wrap the table cell or region; + (6) within a cell, if its subtree is hidden then show it, otherwise hide it." (interactive) (cond ((kotl-mode:eobp) (kotl-mode:show-all)) ((kotl-mode:eolp t) (funcall action-key-eol-function)) @@ -2360,7 +2696,7 @@ If key is pressed: ;; Wrap the table cell or region (org-table-wrap-region current-prefix-arg)) (t ;; On a cell line (not at the end of line). - (if (smart-outline-subtree-hidden-p) + (if (kcell-view:collapsed-p) (kotl-mode:show-tree) (kotl-mode:hide-tree)))) (kotl-mode:to-valid-position)) @@ -2374,14 +2710,15 @@ and has moved the cursor to the selected buffer. If assist-key is pressed: (1) at the end of buffer, collapse all cells and hide all non-level-one cells; - (2) on a header line but not at the beginning or end, display properties of + (2) at the end of a visible line, call the `assist-key-eol-function'; + (3) on a header line but not at the beginning or end, display properties of each cell in tree beginning at point; - (3) between cells or within the read-only indentation region to the left of + (4) between cells or within the read-only indentation region to the left of a cell, then move point to prior location and prompt to move one tree to a new location in the outline; press the Action Key twice to select the tree to move and where to move it; - (4) anywhere else, invoke `assist-key-eol-function', typically to scroll down - a windowful." + (5) anywhere else, invoke `smart-scroll-down', typically to scroll down a + windowful." (interactive) (cond ((kotl-mode:eobp) (kotl-mode:overview)) ((kotl-mode:eolp t) (funcall assist-key-eol-function)) @@ -2654,12 +2991,39 @@ CONTENTS." (kotl-mode:add-below-parent cells-to-add contents plist no-fill))))) +(defun kotl-mode:demote-siblings (arg) + "Move rest of siblings after point up to ARG levels lower in current view. +Each cell is refilled iff its `no-fill' attribute is nil and +`kotl-mode:refill-flag' is non-nil. With prefix ARG = 0, cells are deomoted +to a maximum of one level and `kotl-mode:refill-flag' is treated as true." + (interactive "*p") + (let* ((orig-id (kcell-view:idstamp)) + (lbl-sep-len (kview:label-separator-length kotl-kview)) + (orig-pos-in-cell + (- (point) (kcell-view:start nil lbl-sep-len)))) + ;; Next line ensures point is in the root of the current tree if + ;; the tree is at all hidden. + (kotl-mode:beginning-of-line) + (unwind-protect + (kview:map-cells (lambda () (kotl-mode:demote-tree arg)) + kotl-kview + (kview:map-siblings (lambda (_kview) (kcell-view:idstamp)) + kotl-kview)) + ;; Move to start of original cell + (kotl-mode:goto-cell orig-id) + ;; Move to original pos within cell + (forward-char (min orig-pos-in-cell + (- (kcell-view:end-contents) + (kcell-view:start)))) + (kotl-mode:to-valid-position)))) + (defun kotl-mode:demote-tree (arg) "Move current tree a maximum of prefix ARG levels lower in current view. Each cell is refilled iff its `no-fill' attribute is nil and -kotl-mode:refill-flag is non-nil. With prefix ARG = 0, cells are demoted up -to one level and kotl-mode:refill-flag is treated as true." +`kotl-mode:refill-flag' is non-nil. With prefix ARG = 0, cells are demoted +up to one level and `kotl-mode:refill-flag' is treated as true." (interactive "*p") + (when (null arg) (setq arg 1)) (if (< arg 0) (kotl-mode:promote-tree (- arg)) (let* ((lbl-sep-len (kview:label-separator-length kotl-kview)) @@ -2880,12 +3244,39 @@ If arg is 0, make it 1; if arg is negative, move prior to that number of trees." point-offset)) (error "(kotl-mode:move-tree): Cannot move past %d trees at the same level" num-trees)))) +(defun kotl-mode:promote-siblings (arg) + "Move rest of siblings after point up to ARG levels higher in current view. +Each cell is refilled iff its `no-fill' attribute is nil and +`kotl-mode:refill-flag' is non-nil. With prefix ARG = 0, cells are promoted +up to one level and `kotl-mode:refill-flag' is treated as true." + (interactive "*p") + (let* ((orig-id (kcell-view:idstamp)) + (lbl-sep-len (kview:label-separator-length kotl-kview)) + (orig-pos-in-cell + (- (point) (kcell-view:start nil lbl-sep-len)))) + ;; Next line ensures point is in the root of the current tree if + ;; the tree is at all hidden. + (kotl-mode:beginning-of-line) + (unwind-protect + (kview:map-cells (lambda () (kotl-mode:promote-tree arg)) + kotl-kview + (kview:map-siblings (lambda (_kview) (kcell-view:idstamp)) + kotl-kview)) + ;; Move to start of original cell + (kotl-mode:goto-cell orig-id) + ;; Move to original pos within cell + (forward-char (min orig-pos-in-cell + (- (kcell-view:end-contents) + (kcell-view:start)))) + (kotl-mode:to-valid-position)))) + (defun kotl-mode:promote-tree (arg) "Move current tree a maximum of prefix ARG levels higher in current view. Each cell is refilled iff its `no-fill' attribute is nil and -kotl-mode:refill-flag is non-nil. With prefix ARG = 0, cells are promoted up -to one level and kotl-mode:refill-flag is treated as true." +`kotl-mode:refill-flag' is non-nil. With prefix ARG = 0, cells are promoted +up to one level and `kotl-mode:refill-flag' is treated as true." (interactive "*p") + (when (null arg) (setq arg 1)) (if (< arg 0) (kotl-mode:demote-tree (- arg)) (let* ((parent) (result) @@ -3244,6 +3635,11 @@ within the current view." (kcell-view:collapse nil kview-label-sep-len)) kotl-kview all-flag t))) +(defun kotl-mode:current-level () + "Return the level of the current cell, or nil if outside of the outline text." + (let ((level (kcell-view:level))) + (when (> level 0) level))) + (defun kotl-mode:expand-tree (&optional all-flag) "Expand each visible cell of the tree rooted at point. With optional prefix ALL-FLAG non-nil, expand all cells visible within @@ -3258,6 +3654,16 @@ the current view." (outline-flag-region (point) (kcell-view:end-contents) nil)) kotl-kview all-flag t))) +(defun kotl-mode:previous-cell-level () + "Return the level of the prior cell regardless of whether it is visible. +Return 0 for the first cell in the buffer, and nil if outside of the outline +text." + (and (kotl-mode:current-level) + (or (and (/= (line-beginning-position) (point-min)) + (save-excursion (and (kcell-view:previous) + (kotl-mode:current-level))) + 0)))) + (defun kotl-mode:toggle-tree-expansion (&optional all-flag) "Collapse or expand each cell of tree rooted at point. Act on all visible cells if optional prefix arg ALL-FLAG is given. @@ -3269,6 +3675,47 @@ collapsed." (kotl-mode:expand-tree all-flag) (kotl-mode:collapse-tree all-flag))) +(defun kotl-mode:cycle-globally () + "Cycle the whole outline through 3 views. + 1. OVERVIEW: Show only the first line of level 1 cells + 2. CONTENTS: Show the first line of all cells at all levels + 3. SHOW ALL: Show the entire outline." + (interactive) + (cond ((kotl-mode:top-cells-one-line-visible-p) + ;; When only the first line all level 1 cells are visible: + ;; 1. CONTENTS: Show the first line of all cells at all levels + (kvspec:levels-to-show 0) + (kvspec:show-lines-per-cell 1)) + ((kotl-mode:all-cells-one-line-visible-p) + ;; When the first line of every cell is visible: + ;; 2. SHOW ALL: Show the entire outline + (kotl-mode:show-all)) + (t + ;; Otherwise: + ;; 3. OVERVIEW: Show only the first line of level 1 cells + (kotl-mode:top-cells)))) + +(defun kotl-mode:cycle-locally () + "Cycle the tree rooted at point through three views. + 1. FOLDED: Hide all but the first line of the tree at point + 2. CHILDREN: Hide all but the first line of the root cell at point and its + children + 3. SUBTREE: Show the entire tree at point; if there is no subtree, switch + directly from CHILDREN to FOLDED." + (interactive) + (cond ((not (kotl-mode:tree-collapsed-p)) + ;; If the whole tree is showing, then show just the first line of + ;; the root cell + (kotl-mode:show-root-first-line)) + ((kotl-mode:root-first-line-only-shown-p) + ;; If only the first line of the root cell is showing and it + ;; has at least one child, then show the first line of all of its + ;; children + (kotl-mode:show-tree-children-first-lines)) + (t + ;; Otherwise, show the whole tree + (kotl-mode:show-tree)))) + ;;; ;;;###autoload (defun kotl-mode:overview (&optional arg) @@ -3338,11 +3785,70 @@ With optional SHOW-FLAG, expand the subtree instead." (buffer-read-only)) (outline-flag-region start end (not show-flag))))) +(defun kotl-mode:show-lines-per-cell (num) + "Show NUM lines per visible cell in the tree at point. +0 means show all lines in each visible cell. + +Interactively, NUM is the prefix argument." + (interactive "p") + (if (null num) + (setq num + (read-from-minibuffer "Show how many lines per cell (0 = show all lines): " + nil nil t))) + (setq num (prefix-numeric-value num)) + (when (< num 0) + (error "(kotl-mode:show-lines-per-cell): Invalid lines per cell, `%d'" + num)) + ;; Now show NUM lines in visible cells of the current tree + (kview:map-tree (lambda (_kview) + (kcell-view:expand (point)) + (kvspec:show-lines-this-cell num)) + kotl-kview nil t)) + +(defun kotl-mode:show-root-first-line () + "For the tree rooted at point, show only the the first line of the root cell." + (kotl-mode:show-to-level 1) + (kotl-mode:show-lines-per-cell 1)) + (defun kotl-mode:show-subtree (&optional cell-ref) "Show subtree, ignoring root, at optional CELL-REF (defaults to cell at point)." (interactive) (kotl-mode:hide-subtree cell-ref t)) +(defun kotl-mode:show-to-level (levels-to-keep) + "Hide cells in tree at point at levels deeper than LEVELS-TO-KEEP (a number). +Show any hidden cells within LEVELS-TO-KEEP. 1 is the current level. 0 means +show entire tree at point. Nil means prompt for LEVELS-TO-KEEP. + +Interactively, LEVELS-TO-KEEP is the prefix argument." + (interactive "p") + (if (null levels-to-keep) + (setq levels-to-keep + (read-from-minibuffer "Show current tree cells down to level (0 = show all levels): " + nil nil t))) + (setq levels-to-keep (prefix-numeric-value levels-to-keep)) + (when (< levels-to-keep 0) + (error "(kotl-mode:show-tolevel): Must display at least one level")) + ;; Make `levels-to-keep' be relative to the root level at point when not 0 + (unless (zerop levels-to-keep) + (setq levels-to-keep (1- (+ (kcell-view:level) levels-to-keep)))) + (kview:map-tree + (lambda (_kview) + (if (/= (kcell-view:level) levels-to-keep) + (kotl-mode:show-tree) + (kotl-mode:hide-subtree) + ;; Move to last cell in hidden subtree, to skip further + ;; processing of these cells. + (if (kcell-view:next t) + (kcell-view:previous) + (goto-char (point-max))))) + kotl-kview)) + +(defun kotl-mode:show-tree-children-first-lines () + "For the tree rooted at point, show down to the first line of its children." + (kotl-mode:show-to-level 2) + (kotl-mode:show-lines-per-cell 1)) + ;;;###autoload (defun kotl-mode:hide-tree (&optional cell-ref show-flag) "Collapse tree rooted at optional CELL-REF (defaults to cell at point). @@ -4001,17 +4507,22 @@ Leave point at end of line now residing at START." ;; in which case we don't want to bind it here. (unless (lookup-key kotl-mode-map "\M-\C-h") (define-key kotl-mode-map "\M-\C-h" 'kotl-mode:hide-subtree)) - ;; Override this global binding for set-selective-display with a similar - ;; function appropriate for kotl-mode. - (define-key kotl-mode-map "\C-x$" 'kotl-mode:hide-sublevels) - (define-key kotl-mode-map [(tab)] 'kotl-mode:tab-command) ;; TAB - (define-key kotl-mode-map "\C-i" 'kotl-mode:tab-command) ;; TAB - (define-key kotl-mode-map [(shift tab)] 'kotl-mode:untab-command) ;; Shift-TAB + ;; Override this global binding for set-selective-display with a + ;; similar function appropriate for kotl-mode. + (define-key kotl-mode-map "\C-x$" 'kotl-mode:hide-sublevels) + + ;; Use {M-1 TAB} to switch TAB/S-TAB between normal kotl-mode + ;; operation and Org cycling compatibility. Show a message each + ;; time is toggled. + + (define-key kotl-mode-map [tab] 'kotl-mode:tab-command) ;; TAB + (define-key kotl-mode-map "\C-i" 'kotl-mode:tab-command) ;; TAB + + (define-key kotl-mode-map [S-tab] 'kotl-mode:untab-command) ;; Shift-TAB (define-key kotl-mode-map [S-iso-lefttab] 'kotl-mode:untab-command) ;; Shift-TAB - (define-key kotl-mode-map [backtab] 'kotl-mode:untab-command) ;; Shift-TAB - (define-key kotl-mode-map [(meta tab)] 'kotl-mode:untab-command) ;; M-TAB - (define-key kotl-mode-map "\M-\C-i" 'kotl-mode:untab-command) ;; M-TAB - (define-key kotl-mode-map "\C-c\C-i" 'kotl-mode:set-or-remove-cell-attribute) + (define-key kotl-mode-map [backtab] 'kotl-mode:untab-command) ;; Shift-TAB + + (define-key kotl-mode-map "\C-c\C-i" 'kotl-mode:set-or-remove-cell-attribute) (define-key kotl-mode-map "\C-j" 'kotl-mode:add-cell) (define-key kotl-mode-map "\M-j" 'kotl-mode:fill-paragraph) (define-key kotl-mode-map "\C-c\M-j" 'kotl-mode:fill-cell) @@ -4055,12 +4566,12 @@ Leave point at end of line now residing at START." (define-key kotl-mode-map (kbd "ESC ") 'kotl-mode:move-tree-forward) (mapc (lambda (key) (define-key kotl-mode-map key 'kotl-mode:promote-tree)) - (list (kbd "M-") (kbd "M-S-") (kbd "ESC ") - (kbd "ESC S-") (kbd "C-c C-<") (kbd "C-c C-,"))) + (list (kbd "M-S-") (kbd "M-") (kbd "ESC S-") + (kbd "ESC ") (kbd "C-c C-<") (kbd "C-c C-,"))) (mapc (lambda (key) (define-key kotl-mode-map key 'kotl-mode:demote-tree)) - (list (kbd "M-") (kbd "M-S-") (kbd "ESC ") - (kbd "ESC S-") (kbd "C-c C->") (kbd "C-c C-."))) + (list (kbd "M-S-") (kbd "M-") (kbd "ESC S-") + (kbd "ESC ") (kbd "C-c C->") (kbd "C-c C-."))) ;; When delete-selection-mode (pending-delete-mode) is enabled, make ;; these commands delete the region. diff --git a/kotl/kotl-orgtbl.el b/kotl/kotl-orgtbl.el index cd497a16..8a1fd05e 100644 --- a/kotl/kotl-orgtbl.el +++ b/kotl/kotl-orgtbl.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 10/18/2020 -;; Last-Mod: 14-Jul-24 at 23:32:40 by Bob Weiner +;; Last-Mod: 28-Sep-26 at 08:50:29 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -72,17 +72,44 @@ ;;; Public functions ;;; ************************************************************************ -;; Redefine this Org Table function to handle Koutlines as well. +;; Redefine this Org Table function on {TAB} to handle Koutlines as well. (defun orgtbl-tab (arg) "Justification and field motion for `orgtbl-mode' with Koutline support." (interactive "P") (cond ((and (derived-mode-p 'kotl-mode) arg) - (kotl-mode:tab-command (if (= (prefix-numeric-value arg) 1) nil arg))) + (kotl-mode:tab-command arg)) (arg (org-table-edit-field t)) (t (org-table-justify-field-maybe) (org-table-next-field)))) +;; Redefine this Org Table function on {S-TAB} to handle Koutlines as well. +(defun org-table-previous-field (&optional arg) + "Go to the previous field in the table. +Before doing so, re-align the table if necessary." + (interactive "P") + (if (and (derived-mode-p 'kotl-mode) arg) + (kotl-mode:untab-command arg) + (org-table-justify-field-maybe) + (org-table-maybe-recalculate-line) + (when (and org-table-automatic-realign + org-table-may-need-update) + (org-table-align)) + (when (org-at-table-hline-p) + (end-of-line)) + (let ((start (org-table-begin)) + (origin (point))) + (condition-case nil + (progn + (search-backward "|" start nil 2) + (while (looking-at-p "|\\(?:-\\|[ \t]*$\\)") + (search-backward "|" start))) + (error + (goto-char origin) + (user-error "Cannot move to previous table field")))) + (when (looking-at "| ?") + (goto-char (match-end 0))))) + (defun kotl-mode:transpose-lines-up () "Exchange current line and previous line, maintaining point location. If no previous line, exchange current with next line." diff --git a/kotl/kview.el b/kotl/kview.el index fcac7ed2..bd9b4348 100644 --- a/kotl/kview.el +++ b/kotl/kview.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 6/30/93 -;; Last-Mod: 29-Mar-26 at 12:34:05 by Bob Weiner +;; Last-Mod: 24-Sep-26 at 16:47:15 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -210,7 +210,8 @@ a cell's label and the start of its contents." (save-excursion (when pos (goto-char pos)) - (kcell-view:child visible-p lbl-sep-len))) + (when (kcell-view:child visible-p lbl-sep-len) + (kcell-view:idstamp)))) (defun kcell-view:collapse (&optional pos lbl-sep-len) "Collapse cell at optional POS or point to a single line within the current view. @@ -628,7 +629,8 @@ With optional VISIBLE-P, consider only visible siblings." (save-excursion (when pos (goto-char pos)) - (kcell-view:forward visible-p lbl-sep-len))) + (when (kcell-view:forward visible-p lbl-sep-len) + (kcell-view:idstamp)))) (defun kcell-view:start (&optional pos lbl-sep-len) "Return start position of visible cell contents from optional POS or point." diff --git a/kotl/kvspec.el b/kotl/kvspec.el index c3c3969b..1ceafa64 100644 --- a/kotl/kvspec.el +++ b/kotl/kvspec.el @@ -3,7 +3,7 @@ ;; Author: Bob Weiner ;; ;; Orig-Date: 21-Oct-95 at 15:17:07 -;; Last-Mod: 30-Apr-26 at 10:20:02 by Bob Weiner +;; Last-Mod: 23-Sep-26 at 17:58:06 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -62,10 +62,17 @@ ;;; Public variables ;;; ************************************************************************ -(defvar kvspec:current nil - "String that represents the current view spec. +(defvar-local kvspec:current nil + "String that represents the current view spec of a Koutline. It is local to each koutline. Nil value means it has not been set yet.") +(defvar-local kvspec:default nil + "String that represents the default view spec of a Koutline. +This is first non-null value that the view spec is set to, either when an +existing Koutline is read-in from disk or it is newly created and +'kvspec:update' has been called. It is local to each koutline. Nil value +means it has not been set yet.") + ;;; ************************************************************************ ;;; Public declarations ;;; ************************************************************************ @@ -90,7 +97,7 @@ It is local to each koutline. Nil value means it has not been set yet.") ) "Alist of (view-spec-character . label-type) pairs.") -(defvar kvspec:string "" +(defvar-local kvspec:string "" "String displayed in koutline modelines to reflect the current view spec. It is local to each koutline. Set this to nil to disable modeline display of the view spec settings.") @@ -107,33 +114,26 @@ characters at run-time.") (defun kvspec:activate (&optional view-spec) "Activate optional VIEW-SPEC or existing view spec in the current koutline. VIEW-SPEC is a string or t, which means recompute the current view spec. See -${hyperb:dir}/kotl/EXAMPLE.kotl#3b19c=042 for details on valid view specs." +${hyperb:dir}/kotl/EXAMPLE.kotl#3=039 for details on valid view specs." (interactive (list (read-string "Set view spec: " kvspec:current))) (kotl-mode:is-p) (kfile:narrow-to-kcells) (when (equal view-spec "") (setq view-spec nil)) - (kvspec:initialize) (kvspec:update view-spec) (kvspec:update-view)) -(defun kvspec:initialize () - "Ensure that view spec settings will be local to the current buffer." - (unless (local-variable-p 'kvspec:current (current-buffer)) - (make-local-variable 'kvspec:current) - (make-local-variable 'kvspec:string))) - (defun kvspec:levels-to-show (levels-to-keep) "Hide all cells in outline at levels deeper than LEVELS-TO-KEEP (a number). Shows any hidden cells within LEVELS-TO-KEEP. 1 is the first level. 0 means -display all levels of cells." +display all levels of cells. Nil means prompt for the value." (if (null levels-to-keep) (setq levels-to-keep (read-from-minibuffer "Show cells down to level (0 = show all levels): " nil nil t))) (setq levels-to-keep (prefix-numeric-value levels-to-keep)) - (if (< levels-to-keep 0) - (error "(kvspec:levels-to-show): Must display at least one level")) + (when (< levels-to-keep 0) + (error "(kvspec:levels-to-show): Must display at least one level")) (kview:map-tree (lambda (_kview) (if (/= (kcell-view:level) levels-to-keep) @@ -147,8 +147,13 @@ display all levels of cells." kotl-kview t) (kview:set-attr kotl-kview 'levels-to-show levels-to-keep)) +(defun kvspec:reset () + "Reset current view to `kvspec:default' view spec." + (kvspec:activate kvspec:default)) + (defun kvspec:show-lines-per-cell (num) - "Show NUM lines per visible cell; 0 means show all lines in each visible cell." + "Show NUM lines per visible cell; 0 means show all lines in each visible cell. +Applies to the entire outline." (if (or (not (integerp num)) (< num 0)) (error "(kvspec:show-lines-per-cell): Invalid lines per cell, `%d'" num)) (kview:set-attr kotl-kview 'lines-to-show num) @@ -189,6 +194,8 @@ ${hyperb:dir}/kotl/EXAMPLE.kotl#3b19c=042 for details on valid view specs." (setq kvspec:current view-spec))) ((or (eq view-spec t) (null kvspec:current)) (setq kvspec:current (kvspec:compute)))) + (unless kvspec:default + (setq kvspec:default kvspec:current)) ;; Update display using current specs. (kvspec:update-modeline)) @@ -224,7 +231,7 @@ ${hyperb:dir}/kotl/EXAMPLE.kotl#3b19c=042 for details on valid view specs." ;; it off when he resets the view specs. ;; b - blank separator lines - (if (kview:get-attr kotl-kview 'blank-lines) "b") + (when (kview:get-attr kotl-kview 'blank-lines) "b") ;; c - cutoff lines per cell (let ((lines (kview:get-attr kotl-kview 'lines-to-show))) @@ -233,7 +240,7 @@ ${hyperb:dir}/kotl/EXAMPLE.kotl#3b19c=042 for details on valid view specs." (concat "c" (int-to-string lines)))) ;; e - ellipses on - (if selective-display-ellipses "e") + (when selective-display-ellipses "e") ;; l - hide some levels (let ((levels (kview:get-attr kotl-kview 'levels-to-show))) @@ -304,17 +311,16 @@ available, the cell remains fully expanded." (outline-flag-region (1- (point)) end t))))) (defun kvspec:update-modeline () - "Setup or update display of the current kview spec in the modeline." - (if (stringp kvspec:current) + "Setup or update display of the current view spec in the modeline." + (when (stringp kvspec:current) (setq kvspec:string (format kvspec:string-format kvspec:current))) - (if (memq 'kvspec:string mode-line-format) - nil + (unless (memq 'kvspec:string mode-line-format) (setq mode-line-format (copy-sequence mode-line-format)) (let ((elt (or (memq 'mode-line-buffer-identification mode-line-format) (memq 'modeline-buffer-identification mode-line-format)))) - (if elt - (setcdr elt (cons 'kvspec:string (cdr elt))))))) + (when elt + (setcdr elt (cons 'kvspec:string (cdr elt))))))) (defun kvspec:update-view () "Update view according to current setting of local `kvspec:current' variable." diff --git a/man/hyperbole.html b/man/hyperbole.html index ccdeeee0..39f77788 100644 --- a/man/hyperbole.html +++ b/man/hyperbole.html @@ -4,7 +4,7 @@