diff --git a/ChangeLog b/ChangeLog index 7be2c20d..a83327db 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,9 +1,26 @@ +2026-04-30 Bob Weiner + +* kotl/kvspec.el (kvspec:update): If updating an existing viewspec, mark + buffer as modified so new viewspec is saved when writing out the file. + +* kotl/kfile.el (kfile:insert-attributes-v3): Fix to handle alpha and legal + numbering properly plus a period with no following spaces as the separator. + Update to 'save-excursion' and force start at (point-min) and make + args optional so can be called in 'kfile:write' as well as 'kfile:read'. + +* kotl/kotl-mode.el (kotl-mode): Prevent 'delete-trailing-whitespace-mode' + from deleting trailing whitespace from any lines within kcells. Ensure + "stripmode" package does not delete trailing whitespace from cells whose + first line is blank or editing will not start at the proper column. + +* kotl/kotl-mode.el (kotl-mode:just-one-space): Add optional N arg of how + many spaces to leave that 'just-one-space supports and update doc string. + Since works for a single line only, does not support -N for removing + newlines as 'just-one-space' does. + 2026-04-29 Bob Weiner -* kotl/kfile.el (kfile:write): Ensure "stripmode" package does not delete - trailing whitespace from cells whose first line is blank or editing - will not start at the proper column. - (kfile:insert-attributes-v3): When reading in an exising +* kotl/kfile.el (kfile:insert-attributes-v3): When reading in an exising Koutline, repair any cells that start with a blank line and have had the label separator removed or where lines of just whitespace have been remove due to packages that delete trailing spaces. Also ensure search diff --git a/kotl/kfile.el b/kotl/kfile.el index c1e31d7e..f3a5a519 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 02:33:14 by Bob Weiner +;; Last-Mod: 30-Apr-26 at 13:24:40 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -348,11 +348,8 @@ VISIBLE-ONLY-P is non-nil. Signal an error if kotl is not attached to a file." ;; Set-visited-file-name clears local-write-file-hooks that we use to save ;; koutlines properly, so reinitialize local variables. (kotl-mode) - ;; Prevent "stripspace" package from deleting trailing whitespace from the - ;; first line of cells which are blank. These need to maintain spaces after - ;; the relative identifier to ensure proper editing of the first line. - (when (bound-and-true-p stripspace-local-mode) - (stripsapce-local-mode 0)) + (kfile:insert-attributes-v3) + ;; Force a save (set-buffer-modified-p t) ;; This next line must come before the save-buffer so write-file-functions ;; can make use of it. @@ -392,120 +389,144 @@ included in the list." kotl-structure) (nreverse cell-list))) -(defun kfile:insert-attributes-v2 (_kview kcell-list) - "Set cell attributes within KVIEW for each element in KCELL-LIST. -Assume all cell contents are already in kview and that no cells are -hidden." - (let (buffer-read-only - idstamp - kcell-data) - (while - (progn - (skip-chars-forward "\n") - ;; !! TODO: Won't work if label-type is 'no. - ;; Here we search past the cell identifier - ;; for the location at which to place cell properties. - ;; Be sure not to skip past a period which may terminate the label. - (when (re-search-forward "[A-Za-z0-9]\\(\\.?[A-Za-z0-9]\\)*" nil t) - (setq kcell-data (car kcell-list) - ;; Repair invalid idstamps on the fly. - idstamp (if (vectorp kcell-data) - (or (kcell-data:idstamp kcell-data) (kview:id-increment kotl-kview)) - (kview:id-increment kotl-kview))) - (kproperty:set 'idstamp idstamp) - (kproperty:set 'kcell (car kcell-list)) - (setq kcell-list (cdr kcell-list))) - (search-forward "\n\n" nil t))))) - -(defun kfile:insert-attributes-v3 (kview kcell-vector) - "Set cell attributes within KVIEW for each element in KCELL-VECTOR. -Assume all cell contents are already in kview and that no cells are -hidden." - (let* ((kcell-num 1) - (label-separator (kview:label-separator kview)) - (label-separator-regexp (regexp-quote label-separator)) - (label-and-separator-regexp - (format "^[ \t]*\\([0-9]\\(\\.?[A-Za-z0-9]\\)*\\)\\([ \n]\\|%s\\)" - label-separator-regexp)) - (cell-start-or-cells-end-regexp - (concat label-and-separator-regexp "\\|\\'")) - mod-flag - buffer-read-only - idstamp - kcell-data - indent - start - end - next) - (cl-flet ((kotl-mode:pre-self-insert-command () nil)) - (while (progn - (skip-chars-forward "\n") - ;; !! TODO: Won't work if label-type is 'no. - ;; Here we search past the cell identifier - ;; for the location at which to place cell properties. - ;; Be sure not to skip past a period which may terminate the label. - (when (re-search-forward label-and-separator-regexp nil t) - (goto-char (match-end 1)) - ;; Repair any cells that start with blank lines and have had - ;; the label separator removed, typically due to packages that delete - ;; trailing spaces. - (unless (looking-at label-separator-regexp) - (setq mod-flag t) - ;; Remove any existing padding between label and first char of - ;; cell text and then insert label-separator - (delete-region (point) - (min (+ (point) (length label-separator)) - (save-excursion - (skip-chars-forward "^\n") (point)))) - (save-excursion (insert label-separator))) - (setq kcell-data (aref kcell-vector kcell-num) - ;; Repair invalid idstamps on the fly. - idstamp (if (vectorp kcell-data) - (or (kcell-data:idstamp kcell-data) - (kview:id-increment kotl-kview)) - (kview:id-increment kotl-kview))) - (kproperty:set 'idstamp idstamp) - (kproperty:set 'kcell (kcell-data:to-kcell-v3 kcell-data)) - (setq kcell-num (1+ kcell-num))) - (search-forward "\n\n" nil t))) - - ;; Repair any blank lines within cells whose indent has been - ;; removed due to packages that delete trailing spaces. - (goto-char (point-min)) - (when (re-search-forward label-and-separator-regexp nil t) - (goto-char (1- (match-end 0))) +(defun kfile:insert-attributes-v2 (&optional kview kcell-list) + "Set cell attributes within optional KVIEW for each element in KCELL-LIST. +Also, fix any wrong idstamps. When null, KVIEW defaults to the value +of the local variable, `kotl-kview'. + +Assume all cell contents are already in kview and that no cells are hidden." + (unless kview + (setq kview kotl-kview)) + (save-excursion + (goto-char (point-min)) + (let* ((label-separator (kview:label-separator kview)) + (label-separator-regexp (regexp-quote label-separator)) + (label-and-separator-regexp + (format "^[ \t]*\\([0-9]\\([.0-9]*[0-9]\\|[A-Za-z0-9]*\\)\\)\\([. \n]\\|%s\\)" + label-separator-regexp)) + + buffer-read-only + idstamp + kcell-data) + (cl-flet ((kotl-mode:pre-self-insert-command () nil)) + (while + (progn + (skip-chars-forward "\n") + ;; !! TODO: Won't work if label-type is 'no. + ;; Here we search past the cell identifier + ;; for the location at which to place cell properties. + ;; Be sure not to skip past a period which may terminate the label. + (when (re-search-forward label-and-separator-regexp nil t) + (goto-char (match-end 1)) + (setq kcell-data (car kcell-list) + ;; Repair invalid idstamps on the fly. + idstamp (if (vectorp kcell-data) + (or (kcell-data:idstamp kcell-data) (kview:id-increment kotl-kview)) + (kview:id-increment kotl-kview))) + (kproperty:set 'idstamp idstamp) + (kproperty:set 'kcell (car kcell-list)) + (setq kcell-list (cdr kcell-list))) + (search-forward "\n\n" nil t))))))) + +(defun kfile:insert-attributes-v3 (&optional kview kcell-vector) + "Set cell attributes within optional KVIEW for each element in KCELL-VECTOR. +Also, fix any wrong label separators, idstamps and line indents. When +null, KVIEW defaults to the value of the local variable, `kotl-kview'. If +KCELL-VECTOR is null, ignore cell attribute setting. + +Assume all cell contents are already in kview and that no cells are hidden." + (unless kview + (setq kview kotl-kview)) + (save-excursion + (goto-char (point-min)) + (let* ((kcell-num 1) + (label-separator (kview:label-separator kview)) + (label-separator-regexp (regexp-quote label-separator)) + (label-and-separator-regexp + (format "^[ \t]*\\([0-9]\\([.0-9]*[0-9]\\|[A-Za-z0-9]*\\)\\)\\([. \n]\\|%s\\)" + label-separator-regexp)) + (cell-start-or-cells-end-regexp + (concat label-and-separator-regexp "\\|\\'")) + mod-flag + buffer-read-only + idstamp + kcell-data + indent + start + end + next) + (cl-flet ((kotl-mode:pre-self-insert-command () nil)) (while (progn (skip-chars-forward "\n") ;; !! TODO: Won't work if label-type is 'no. ;; Here we search past the cell identifier ;; for the location at which to place cell properties. ;; Be sure not to skip past a period which may terminate the label. - (setq start (point) - indent nil) - (when (re-search-forward cell-start-or-cells-end-regexp nil t) - (goto-char - (if (= (match-end 0) (point-max)) - (point-max) - (1- (match-end 0)))) - (setq next (point-marker)) - (goto-char (match-beginning 0)) - (skip-chars-backward "\n") - (setq end (point-marker)) ;; end of prior cell text - (goto-char start) - (while (and (< (point) end) (search-forward "\n\n" end t)) + (when (re-search-forward label-and-separator-regexp nil t) + (goto-char (match-end 1)) + ;; Repair any cells that start with blank lines and have + ;; had the label separator removed, typically due to + ;; packages that delete trailing spaces. + (unless (looking-at label-separator-regexp) (setq mod-flag t) - (backward-char 1) - (unless indent (setq indent (kcell-view:indent))) - (insert-char ?\ indent) - (forward-char 1)) - (unless (= next (point-max)) - (goto-char next)))))) - - (when (markerp end) - (set-marker end nil)) - (when (markerp next) - (set-marker next nil)) - (when mod-flag (save-buffer))))) + ;; Remove any existing padding between label and first + ;; char of cell text and then insert label-separator + (delete-region (point) + (min (+ (point) (length label-separator)) + (save-excursion + (skip-chars-forward "^\n") (point)))) + (save-excursion (insert label-separator))) + + (when kcell-vector + (setq kcell-data (aref kcell-vector kcell-num) + ;; Repair invalid idstamps on the fly. + idstamp (if (vectorp kcell-data) + (or (kcell-data:idstamp kcell-data) + (kview:id-increment kotl-kview)) + (kview:id-increment kotl-kview))) + (kproperty:set 'idstamp idstamp) + (kproperty:set 'kcell (kcell-data:to-kcell-v3 kcell-data)) + (setq kcell-num (1+ kcell-num)))) + (search-forward "\n\n" nil t))) + + ;; Repair any blank lines within cells whose indent has been + ;; removed due to packages that delete trailing spaces. + (goto-char (point-min)) + (when (re-search-forward label-and-separator-regexp nil t) + (goto-char (1- (match-end 0))) + (while (progn + (skip-chars-forward "\n") + ;; !! TODO: Won't work if label-type is 'no. + ;; Here we search past the cell identifier for the + ;; location at which to place cell properties. Be sure + ;; not to skip past a period which may terminate the + ;; label. + (setq start (point) + indent nil) + (when (re-search-forward cell-start-or-cells-end-regexp nil t) + (goto-char + (if (= (match-end 0) (point-max)) + (point-max) + (1- (match-end 0)))) + (setq next (point-marker)) + (goto-char (match-beginning 0)) + (skip-chars-backward "\n") + (setq end (point-marker)) ;; end of prior cell text + (goto-char start) + (while (and (< (point) end) (search-forward "\n\n" end t)) + (setq mod-flag t) + (backward-char 1) + (unless indent (setq indent (kcell-view:indent))) + (insert-char ?\ indent) + (forward-char 1)) + (unless (= next (point-max)) + (goto-char next)))))) + + (when (markerp end) + (set-marker end nil)) + (when (markerp next) + (set-marker next nil)) + (when mod-flag (save-buffer)))))) (defun kfile:narrow-to-kcells () "Narrow kotl file to kcell section only." diff --git a/kotl/kotl-mode.el b/kotl/kotl-mode.el index c3126a02..46667148 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: 12-Apr-26 at 11:24:21 by Bob Weiner +;; Last-Mod: 30-Apr-26 at 13:34:27 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -32,7 +32,9 @@ (defvar completion-to-accept) (defvar mwheel-scroll-down-function) ; "mwheel" +(declare-function delete-trailing-whitespace-mode "simple") (declare-function outline-invisible-in-p "hyperbole") +(declare-function stripspace-local-mode "ext:stripspace") ;;; ************************************************************************ ;;; Public variables @@ -132,6 +134,16 @@ It provides the following keys: paragraph-separate paragraph-start selective-display-ellipses)) + ;; Prevent 'delete-trailing-whitespace-mode' from deleting trailing + ;; whitespace from any lines within kcells. + (when (bound-and-true-p delete-trailing-whitespace-mode) + (delete-trailing-whitespace-mode 0)) + ;; Prevent "stripspace" package from deleting trailing whitespace from the + ;; first line of cells which are blank. These need to maintain spaces after + ;; the relative identifier to ensure proper editing of the first line. + (when (bound-and-true-p stripspace-local-mode) + (stripspace-local-mode 0)) + ;; Fix any wrong label separators and line indents. ;; Enable Org Table editing minor mode so that necessary key binding ;; overrides are made. If not desired, the user can disable it via ;; `kotl-mode-hook'. @@ -719,14 +731,15 @@ With optional prefix argument TOP-P non-nil, refill all cells in the outline." ;; Temporarily expand, then refill cells lacking no-fill property. (kview:map-expanded-tree (lambda (_kview) (kotl-mode:fill-cell)) kotl-kview top-p)) -(defun kotl-mode:just-one-space () - "Delete all spaces and tabs around point and leave one space." +(defun kotl-mode:just-one-space (&optional N) + "Delete all spaces and tabs around point, leaving one or optional N spaces. +Does not remove any newlines as `just-one-space' does when given a negative N." (interactive "*") (save-excursion (save-restriction (save-excursion (narrow-to-region (kotl-mode:beginning-of-line) (kotl-mode:to-end-of-line))) - (just-one-space)))) + (just-one-space n)))) (defun kotl-mode:kill-line (&optional arg) "Kill ARG lines from point." diff --git a/kotl/kvspec.el b/kotl/kvspec.el index 61bd6426..993c91cf 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: 21-Nov-23 at 13:20:57 by Bob Weiner +;; Last-Mod: 30-Apr-26 at 10:20:02 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; @@ -181,7 +181,9 @@ ${hyperb:dir}/kotl/EXAMPLE.kotl#3b19c=042 for details on valid view specs." (if (string-match "\\([abcd]+\\)" view-spec) (replace-match "\\1e" t nil view-spec) (concat "e" view-spec)))) - (setq kvspec:current view-spec)) + (unless (equal kvspec:current view-spec) + (set-buffer-modified-p t) + (setq kvspec:current view-spec))) ((or (eq view-spec t) (null kvspec:current)) (setq kvspec:current (kvspec:compute)))) ;; Update display using current specs.