From 5b9b72c29fb4095310cbd8f519bd4ea69a9ebbc3 Mon Sep 17 00:00:00 2001 From: bw Date: Sun, 27 Sep 2026 02:39:07 -0400 Subject: [PATCH 1/6] kotl-mode.el - Add Org-like outline cycling; update to V9.2.0 --- ChangeLog | 102 +++++++ HY-ABOUT | 2 +- HY-ANNOUNCE | 2 +- HY-NEWS | 4 + Makefile | 4 +- README.md | 2 +- README.md.html | 6 +- README.toc.md | 4 +- hargs.el | 6 +- hbut.el | 8 +- hibtypes.el | 23 +- hpath.el | 16 +- hui.el | 18 +- hversion.el | 4 +- hypb.el | 21 +- hyperbole.el | 9 +- kotl/EXAMPLE.kotl | 104 ++++---- kotl/kfile.el | 23 +- kotl/klink.el | 13 +- kotl/kotl-mode.el | 644 ++++++++++++++++++++++++++++++++++++++++----- kotl/kview.el | 8 +- kotl/kvspec.el | 54 ++-- man/hyperbole.html | 272 ++++++++++++------- man/hyperbole.info | Bin 701428 -> 704544 bytes man/hyperbole.pdf | Bin 1733902 -> 1735659 bytes man/hyperbole.texi | 137 ++++++---- 26 files changed, 1132 insertions(+), 354 deletions(-) diff --git a/ChangeLog b/ChangeLog index b511c5fa..1bf425e6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,109 @@ +2026-09-27 Bob Weiner + +* 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 and M-1 as a prefix to toggle 'kotl-mode:tab-flag'. + (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:tab-command) + kotl-mode:untab-command): Remove use of ARG=1 + to toggle 'kotl-mode:tab-flag'. Use 'kotl-mode:toggle-tab-flag' + instead, bound to {C-tab}. + (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..1b8f06a8 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: 20-Sep-26 at 22:29:53 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -385,13 +385,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/hpath.el b/hpath.el index 915037f8..4e8a63f9 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: 20-Sep-26 at 22:29:32 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. @@ -1690,7 +1702,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/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..b72f445d 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,22 +64,31 @@ 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} or {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. + A 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. + + {C-TAB} toggles the function of {TAB} and {S-TAB} between + promotion/demotion and Org-like tree visibility cycling. See + the documentation of `kotl-mode:tab-command' and + `kotl-mode:untab-command' for full details. + + To insert a literal tab character, use {C-q TAB} or adjust the + value of `kotl-mode:emulate-tab' to suit your needs. - 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. + 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. Set the variable, kotl-mode:refill-flag, to t if you want moving, promoting, demoting, exchanging, splitting and appending cells to @@ -270,10 +283,10 @@ 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. + 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 unfamiliar with this sort of file). Files exported from the @@ -385,7 +398,7 @@ |----------------------------+---------------------------| | Promotion Inside Org Table | Demotion Inside Org Table | |----------------------------+---------------------------| - | {M-0 M-TAB} | {M-0 TAB} | + | {M-0 S-TAB} | {M-0 TAB} | | {C-c C-,} | {C-c C-.} | | {C-c C-<} | {C-c C->} | |----------------------------+---------------------------| @@ -393,19 +406,18 @@ |-----------------------------+----------------------------| | Promotion Outside Org Table | Demotion Outside Org Table | |-----------------------------+----------------------------| - | {M-TAB} or {Shift-TAB} | {TAB} | + | {S-TAB} | {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 | symbols 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 help on utilizing 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 +440,7 @@  "ben" ;; kvspec:current -69 ;; id-counter +70 ;; id-counter alpha ;; label-type 4 ;; label-min-width ". " ;; label-separator @@ -436,15 +448,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 70)] [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 @@ -573,4 +585,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] 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..904f35a5 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: 27-Sep-26 at 02:38:02 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -40,6 +40,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 +85,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 +126,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 +292,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 +839,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 +848,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 +1064,112 @@ 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. + +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. +Toggle its value between `cycle' and nil with \\[kotl-mode:toggle-tab-flag]. + +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': + When point is at the beginning of a headline, rotate the tree at point + through 3 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 without a single universal argument, C-u, call +`kotl-mode:cycle-locally': + With a single \\[universal-argument] prefix, cycle the whole outline through + these 3 modes: + 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." + (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"))) + ((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 +1181,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 +1285,106 @@ 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. + +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. +Toggle its value between `cycle' and nil with \\[kotl-mode:toggle-tab-flag]. + +See also the documentation strings for `kotl-mode:delete-backward-char', +`kotl-mode:promote-tree', and 'kotl-mode:promote-siblings'. + +If in `cycle' mode without a prefix argument, call `kotl-mode:cycle-locally': + When point is at the beginning of a headline, rotate the tree at point + through 3 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 without a single universal argument, C-u, call +`kotl-mode:cycle-locally': + With a single \\[universal-argument] prefix, cycle the whole outline through + these 3 modes: + 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." + (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)) + ;; 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) + ;; 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 +1400,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 +2322,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 +2485,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,6 +2591,53 @@ 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 ;;; ------------------------------------------------------------------------ @@ -2331,13 +2651,15 @@ 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 + (4) on a | character within an Org-style table, toggle Org Table minor mode; + (5) within a cell, if its subtree is hidden then show it, + otherwise hide it; + (6) anywhere else, invoke `action-key-eol-function', typically to scroll up a windowful." (interactive) (cond ((kotl-mode:eobp) (kotl-mode:show-all)) @@ -2654,12 +2976,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 +3229,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 +3620,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 +3639,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 +3660,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 +3770,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 +4492,26 @@ 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 "" as binding 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 [C-tab] 'kotl-mode:toggle-tab-flag) ;; Shift-TAB + + (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 [M-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 "\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 +4555,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/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..37d3f076 100644 --- a/man/hyperbole.html +++ b/man/hyperbole.html @@ -4,7 +4,7 @@