branch: externals/hyperbole commit fd551e63d9b0504dffa82d75c3af1d7eca4117d3 Author: Robert Weiner <r...@gnu.org> Commit: Robert Weiner <r...@gnu.org>
kexport:html remove extra vertical padding in tree roots Add Koutliner support for reading and writing top-level 0 cell attributes. Fix a number of small bugs. --- ChangeLog | 35 +++++++++++- HY-NEWS | 4 ++ hactypes.el | 9 ++++ hmouse-tag.el | 3 +- kotl/kcell.el | 94 ++++++++++++++++---------------- kotl/kexport.el | 15 ++++-- kotl/kfile.el | 109 +++++++++++++++++++------------------ kotl/kotl-mode.el | 145 +++++++++++++++++++++++++++++--------------------- kotl/kview.el | 18 ++++--- test/kexport-tests.el | 13 ++--- 10 files changed, 265 insertions(+), 180 deletions(-) diff --git a/ChangeLog b/ChangeLog index eaa6ae26aa..3623913355 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,36 @@ +2022-01-19 Bob Weiner <r...@gnu.org> + +* hactypes.el (link-to-texinfo-node, link-to-Info-index-item, link-to-Info-node): + Remove any runs of tabs or newlines that might be in name and replace each + with a single space. + +* kotl/kcell.el (kcell:ref-to-id): + kotl/kview.el (kcell-view:cell-from-ref): Support lookup of top-cell with + idstamp = 0. + kotl/kcell.el (kcell:create): Prevent multiple adds of kcell and idstamp + attributes. + kotl/kfile.el (kfile:read-v2, kfile:read-v4-or-v3): + kotl/kview.el (kview:create): Add 3rd optional parameter, top-cell-attributes, + so can restore complete set of top-cell attributes when reading. + kotl/kcell.el (kcell:create-top): Change to take entire plist as arg rather + than individual attributes. + kotl/kotl-mode.el (kotl-mode:get-cell-attribute, kotl-mode:remove-cell-attribute, + kotl-mode:set-cell-attribute): Add optional prefix + arg of TOP-CELL-FLAG which if non-nil means edit the top cell's attributes. + +* test/kexport-tests.el (kexport:html-sets-title-and-header): + kotl/kexport.el (kexport:html): Update so title and header come from either + top-level 0 cell 'title attribute or the kotl filename without any .kotl + suffix. + +2022-01-18 Bob Weiner <r...@gnu.org> + +* kotl/kotl-mode.el (kotl-mode:delete-char): Fix to handle Org table use outside + koutlines. + +* hmouse-tag.el (smart-tags-display): Fix change in xref format by using + this test: (eq (type-of find-tag-result) 'xref-item) + 2022-01-17 Bob Weiner <r...@gnu.org> * hmouse-tag.el (smart-lisp-htype-tag): Add to transform ibtype and actype tags @@ -581,7 +614,7 @@ * test/kexport-tests.el (kexport:html-creates-html-file) (kexport:html-sets-title-and-header, kexport:html-contains-each-cell) (kexport:html-creates-hierarchy) - (kexport:display-creates-html-file-and-displayes-it) + (kexport:display-creates-html-file-and-displays-it) (kexport:buffer-calls-kexport:html): Add kexport tests. 2021-09-26 Mats Lidell <ma...@gnu.org> diff --git a/HY-NEWS b/HY-NEWS index 794b540981..4971302874 100644 --- a/HY-NEWS +++ b/HY-NEWS @@ -203,6 +203,10 @@ arg of t to force archiving of any existing file and starting with a fresh EXAMPLE.kotl file. + - Koutlines have a hidden top-level root cell 0 that allows referring + to the whole outline as a tree. Now attributes of this cell can be + set or retrieved like any other cell. + - Modes to Ignore Klinks: C-style languages use <includes> that can be mistaken for klinks. New customizations 'klink:ignore-modes' and 'klink:c-style-modes' set the modes where klink matches are ignored. diff --git a/hactypes.el b/hactypes.el index 7faca6f0a5..6e55842165 100644 --- a/hactypes.el +++ b/hactypes.el @@ -460,6 +460,9 @@ item-name is available. Filename may be given without the .info suffix." (interactive "+XInfo (file)index-item-name to link to: ") (require 'info) + (when (stringp index-item) + ;; Remove any tabs or newlines that might be in index-item. + (setq index-item (replace-regexp-in-string "[ \t\n\r\f]+" " " index-item t t))) (if (and (stringp index-item) (string-match "^(\\([^\)]+\\))\\(.*\\)" index-item)) (id-info-item index-item) (hypb:error "(link-to-Info-index-entry): Invalid Info index item: `%s'" index-item))) @@ -471,6 +474,9 @@ button creation, completion for both filename and node names is available. Filename may be given without the .info suffix." (interactive "+IInfo (file)nodename to link to: ") (require 'info) + (when (stringp string) + ;; Remove any tabs or newlines that might be in string. + (setq string (replace-regexp-in-string "[ \t\n\r\f]+" " " string t t))) (if (and (stringp string) (string-match "^(\\([^\)]+\\))\\(.*\\)" string)) (id-info string) (hypb:error "(link-to-Info-node): Invalid Info node: `%s'" string))) @@ -616,6 +622,9 @@ Return t if found, nil if not." "Display the Texinfo FILE and NODE (a string). FILE may be a string or nil, in which case the current buffer is used." (interactive "fTexinfo file to link to: \nsNode within file to link to: ") + (when (stringp node) + ;; Remove any tabs or newlines that might be in node name. + (setq node (replace-regexp-in-string "[ \t\n\r\f]+" " " node t t))) (let (node-point) (if file (set-buffer (find-find-noselect (hpath:substitute-value file))) diff --git a/hmouse-tag.el b/hmouse-tag.el index 05ec9caca3..4ed5d973b9 100644 --- a/hmouse-tag.el +++ b/hmouse-tag.el @@ -1389,7 +1389,8 @@ See the \"${hyperb:dir}/smart-clib-sym\" script for more information." ;; InfoDock and XEmacs (hpath:display-buffer (car find-tag-result)) (goto-char (cdr find-tag-result))) - ((vectorp find-tag-result) + ((or (eq (type-of find-tag-result) 'xref-item) + (vectorp find-tag-result)) ;; Newer GNU Emacs with xref.el (hpath:display-buffer (xref-item-buffer find-tag-result)) (goto-char (xref-item-position find-tag-result))) diff --git a/kotl/kcell.el b/kotl/kcell.el index a312a1d0fd..80590a7c55 100644 --- a/kotl/kcell.el +++ b/kotl/kcell.el @@ -50,18 +50,18 @@ not already there." (unless (klabel:idstamp-p idstamp) (error "(kcell:create): Invalid `idstamp' argument: '%s'" idstamp)) (nconc - (list 'kcell t) - (list 'idstamp idstamp) + (unless (memq 'kcell plist) + (list 'kcell t)) + (unless (memq 'idstamp plist) + (list 'idstamp idstamp)) (unless (memq 'creator plist) (list 'creator hyperb:user-email 'create-time (htz:date-sortable-gmt))) plist)) -(defun kcell:create-top (&optional file counter) +(defun kcell:create-top (&optional top-cell-attributes) "Return a new koutline top cell optionally attached to FILE with current idstamp COUNTER." - (kcell:create 0 - ;; id-counter = max idstamp value given out in this koutline - (list 'id-counter (or counter 0) 'file file))) + (kcell:create 0 top-cell-attributes)) (defalias 'kcell:get-attr 'plist-get) @@ -101,47 +101,51 @@ Augment capabilities not yet implemented and ignored for now: 2. Any of the above id forms followed by a period and some alpha characters indicating a location relative to the id." (cond ((integerp cell-ref) - (when (kproperty:position 'idstamp cell-ref) - cell-ref)) + (if (zerop cell-ref) + 0 + (when (kproperty:position 'idstamp cell-ref) + cell-ref))) ((stringp cell-ref) (setq cell-ref (hypb:replace-match-string "\\s-+" cell-ref "" t)) - (let (specs - result) - ;; Ignore Augment :viewspecs. - (when (string-match ":" cell-ref) - (setq cell-ref (substring cell-ref 0 (match-beginning 0)))) - ;; Separate koutline |viewspecs from cell id. - (when (string-match "\\(\\.[a-zA-Z]\\||\\)" cell-ref) - (setq specs (substring cell-ref (match-beginning 1)) - cell-ref (substring cell-ref 0 (match-beginning 0)))) - (setq result - (cond - ((string-match "[^.= \t\n\r\f0-9a-zA-Z]" cell-ref) nil) - ((or (string-match "^\\([.0-9a-zA-Z]+\\)=\\(0[0-9]*\\)$" - cell-ref) - ;; idstamp only - (string-match "^\\(\\)\\(0[0-9]*\\)$" cell-ref)) - (setq result (string-to-number (match-string 2 cell-ref))) - ;; Validate that idstamp value exists, else return nil - (when (kproperty:position 'idstamp result) - result)) - ((string-match "^\\([.0-9a-zA-Z]+\\)$" cell-ref) - ;; relative label - (setq result (match-string 1 cell-ref)) - (save-excursion - (goto-char (point-min)) - (when (re-search-forward (concat "^[ \t]*" (regexp-quote result) - (regexp-quote (kview:label-separator kview))) - nil t) - - (setq result (string-to-number (kcell-view:idstamp))) - ;; Validate that idstamp value exists, else return nil - (when (kproperty:position 'idstamp result) - result)))))) - (cond (result - (if specs (concat result specs) result)) - (specs - (when (eq ?| (aref specs 0)) specs))))))) + (if (string-equal cell-ref "0") + 0 + (let (specs + result) + ;; Ignore Augment :viewspecs. + (when (string-match ":" cell-ref) + (setq cell-ref (substring cell-ref 0 (match-beginning 0)))) + ;; Separate koutline |viewspecs from cell id. + (when (string-match "\\(\\.[a-zA-Z]\\||\\)" cell-ref) + (setq specs (substring cell-ref (match-beginning 1)) + cell-ref (substring cell-ref 0 (match-beginning 0)))) + (setq result + (cond + ((string-match "[^.= \t\n\r\f0-9a-zA-Z]" cell-ref) nil) + ((or (string-match "^\\([.0-9a-zA-Z]+\\)=\\(0[0-9]*\\)$" + cell-ref) + ;; idstamp only + (string-match "^\\(\\)\\(0[0-9]*\\)$" cell-ref)) + (setq result (string-to-number (match-string 2 cell-ref))) + ;; Validate that idstamp value exists, else return nil + (when (kproperty:position 'idstamp result) + result)) + ((string-match "^\\([.0-9a-zA-Z]+\\)$" cell-ref) + ;; relative label + (setq result (match-string 1 cell-ref)) + (save-excursion + (goto-char (point-min)) + (when (re-search-forward (concat "^[ \t]*" (regexp-quote result) + (regexp-quote (kview:label-separator kview))) + nil t) + + (setq result (string-to-number (kcell-view:idstamp))) + ;; Validate that idstamp value exists, else return nil + (when (kproperty:position 'idstamp result) + result)))))) + (cond (result + (if specs (concat result specs) result)) + (specs + (when (eq ?| (aref specs 0)) specs)))))))) (defun kcell:remove-attr (kcell attribute) "Remove KCELL's ATTRIBUTE, if any, and return modified KCELL." diff --git a/kotl/kexport.el b/kotl/kexport.el index f9efef8211..15978b2cce 100644 --- a/kotl/kexport.el +++ b/kotl/kexport.el @@ -145,15 +145,18 @@ li { background-color: inherit; cursor: pointer; display: block; + font-size: 0; outline: inherit; } .collapsible:hover { background-color: #FAFAD2; + font-size: 0; } .content { display: block; + font-size: 0; } </style>\n" "CSS that styles collapsible HTML-exported Koutline parent cells") @@ -391,7 +394,8 @@ hard newlines are not used. Also converts Urls and Klinks into Html hyperlinks. (t (error "(kexport:html): `%s' is an invalid `output-to' argument" output-to)))) (standard-output (get-buffer output-to-buf-name)) - title) + ;; Get any title attribute from cell 0, invisible root of the outline + (title (kcell:get-attr (kcell-view:cell-from-ref 0) 'title))) (with-current-buffer standard-output (setq buffer-read-only nil @@ -406,10 +410,11 @@ hard newlines are not used. Also converts Urls and Klinks into Html hyperlinks. (if (called-interactively-p 'interactive) (setq title (read-string (format "Title for %s: " output-to-buf-name) title)) - ;; Otherwise, use the first line of the first cell as the default HTML document title. - (setq title (save-excursion - (kotl-mode:beginning-of-buffer) - (kcell-view:contents))) + ;; Otherwise, use any previously retrieved title attribute or if + ;; none, then the name of the current file sans the .kotl suffix. + (unless title + (setq title (file-name-sans-extension (file-name-nondirectory + buffer-file-name)))) (when (string-match "\n" title) (setq title (substring title 0 (match-beginning 0))))) diff --git a/kotl/kfile.el b/kotl/kfile.el index 8e9809e48e..e4a61d72dd 100644 --- a/kotl/kfile.el +++ b/kotl/kfile.el @@ -102,22 +102,22 @@ Return the new kview." "Create a new koutline file attached to BUFFER, with a single empty level 1 kotl cell. Return file's kview." (or buffer (setq buffer (current-buffer))) - (if (not (bufferp buffer)) - (error "(kfile:create): Invalid buffer argument, %s" buffer)) + (unless (bufferp buffer) + (error "(kfile:create): Invalid buffer argument, %s" buffer)) (set-buffer buffer) - (if buffer-read-only - (error "(kfile:create): %s is read-only" buffer)) + (when buffer-read-only + (error "(kfile:create): %s is read-only" buffer)) (widen) (let ((empty-p (zerop (buffer-size))) import-from view standard-output) - (if (not empty-p) - ;; This is a foreign file whose elements must be converted into - ;; koutline cells. - (progn (setq import-from (kimport:copy-and-set-buffer buffer)) - (set-buffer buffer) - (erase-buffer))) ;; We copied the contents to `import-from'. + (unless empty-p + ;; This is a foreign file whose elements must be converted into + ;; koutline cells. + (setq import-from (kimport:copy-and-set-buffer buffer)) + (set-buffer buffer) + (erase-buffer)) ;; We copied the contents to `import-from'. (setq view (kview:create (buffer-name buffer)) standard-output (current-buffer)) @@ -149,8 +149,8 @@ Return file's kview." (kimport:file import-from (current-buffer)) ;; If import buffer name starts with a space, kill it, as it is no ;; longer needed. - (if (eq ?\ (aref (buffer-name import-from) 0)) - (kill-buffer import-from))) + (when (eq ?\ (aref (buffer-name import-from) 0)) + (kill-buffer import-from))) view)) @@ -200,8 +200,8 @@ Return the new view." ;; kcell-list is a depth-first list of kcells to be attached to the cell ;; contents within the kview down below. (setq kcell-list (kfile:build-structure-v2 kotl-structure cell-data) - view (kview:create (buffer-name buffer) cell-count label-type - level-indent label-separator label-min-width)) + view (kview:create (buffer-name buffer) cell-count nil label-type + level-indent label-separator label-min-width)) ;; (kfile:narrow-to-kcells) (goto-char (point-min)) @@ -236,8 +236,9 @@ If V3-FLAG is true, read as a version-3 buffer." level-indent (read) cell-data (read)) ;; - (setq view (kview:create (buffer-name buffer) cell-count label-type - level-indent label-separator label-min-width)) + (setq top-cell-attributes (aref (aref cell-data 0) 1) + view (kview:create (buffer-name buffer) cell-count top-cell-attributes + label-type level-indent label-separator label-min-width)) ;; (kfile:narrow-to-kcells) (goto-char (point-min)) @@ -294,8 +295,8 @@ VISIBLE-ONLY-P is non-nil. Signal an error if kotl is not attached to a file." ;; (widen) (goto-char (point-min)) - (if (search-forward "\n\^_\n" nil t) - (delete-region (point-min) (match-end 0))) + (when (search-forward "\n\^_\n" nil t) + (delete-region (point-min) (match-end 0))) (princ ";; -*- Mode: kotl -*- \n") (prin1 kfile:version) (princ " ;; file-format\n\^_\n") @@ -323,7 +324,7 @@ VISIBLE-ONLY-P is non-nil. Signal an error if kotl is not attached to a file." ;; buffer is narrowed here, only the narrowed portion will be saved to ;; the file. Narrow as an option since saving only the portion of the ;; file visible in a view may be useful in some situations. - (if visible-only-p (kfile:narrow-to-kcells)) + (when visible-only-p (kfile:narrow-to-kcells)) ;; ;; Return point to its original position as given by the opoint marker. (goto-char opoint) @@ -334,12 +335,12 @@ VISIBLE-ONLY-P is non-nil. Signal an error if kotl is not attached to a file." (defun kfile:write (file) "Write current outline to FILE." (interactive "FWrite outline file: ") - (if (or (null file) (string-equal file "")) - (error "(kfile:write): Invalid file name, \"%s\"" file)) + (when (or (null file) (string-equal file "")) + (error "(kfile:write): Invalid file name, \"%s\"" file)) ;; If arg is just a directory, use same file name, but in that directory. - (if (and (file-directory-p file) buffer-file-name) - (setq file (concat (file-name-as-directory file) - (file-name-nondirectory buffer-file-name)))) + (when (and (file-directory-p file) buffer-file-name) + (setq file (concat (file-name-as-directory file) + (file-name-nondirectory buffer-file-name)))) (kcell:set-attr (kview:top-cell kview) 'file file) (set-visited-file-name file) ;; Set-visited-file-name clears local-write-file-hooks that we use to save @@ -396,10 +397,9 @@ hidden." ;; 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. - (if (re-search-forward "[A-Za-z0-9]\\(\\.?[A-Za-z0-9]\\)*" nil t) - (progn - (kproperty:add-properties (car kcell-list)) - (setq kcell-list (cdr kcell-list)))) + (when (re-search-forward "[A-Za-z0-9]\\(\\.?[A-Za-z0-9]\\)*" nil t) + (kproperty:add-properties (car kcell-list)) + (setq kcell-list (cdr kcell-list))) (search-forward "\n\n" nil t))))) (defun kfile:insert-attributes-v3 (kview kcell-vector) @@ -415,32 +415,31 @@ hidden." ;; 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. - (if (re-search-forward "[A-Za-z0-9]\\(\\.?[A-Za-z0-9]\\)*" nil t) - (progn - (kproperty:add-properties - (kcell-data:to-kcell-v3 (aref kcell-vector kcell-num))) - (setq kcell-num (1+ kcell-num)))) + (when (re-search-forward "[A-Za-z0-9]\\(\\.?[A-Za-z0-9]\\)*" nil t) + (kproperty:add-properties + (kcell-data:to-kcell-v3 (aref kcell-vector kcell-num))) + (setq kcell-num (1+ kcell-num))) (search-forward "\n\n" nil t))))) (defun kfile:narrow-to-kcells () "Narrow kotl file to kcell section only." (interactive) - (if (kview:is-p kview) - (let ((start-text) (end-text)) - (save-excursion - (widen) - (goto-char (point-min)) - ;; Skip to start of kcells. - (if (search-forward "\n\^_" nil t) - (setq start-text (1+ (match-end 0)))) - ;; Skip past end of kcells. - (if (and start-text (search-forward "\n\^_" nil t)) - (setq end-text (1+ (match-beginning 0)))) - (if (and start-text end-text) - (progn (narrow-to-region start-text end-text) - (goto-char (point-min))) - (error - "(kfile:narrow-to-kcells): Cannot find start or end of kcells")))))) + (when (kview:is-p kview) + (let ((start-text) (end-text)) + (save-excursion + (widen) + (goto-char (point-min)) + ;; Skip to start of kcells. + (when (search-forward "\n\^_" nil t) + (setq start-text (1+ (match-end 0)))) + ;; Skip past end of kcells. + (when (and start-text (search-forward "\n\^_" nil t)) + (setq end-text (1+ (match-beginning 0)))) + (if (and start-text end-text) + (progn (narrow-to-region start-text end-text) + (goto-char (point-min))) + (error + "(kfile:narrow-to-kcells): Cannot find start or end of kcells")))))) (defun kfile:print-to-string (object) "Return a string containing OBJECT, any Lisp object, in pretty-printed form. @@ -488,8 +487,8 @@ handle, whenever this is possible." (delete-region (point) (progn (skip-chars-forward " \t") (point))) - (if (not (eq ?' (char-after (1- (point))))) - (insert ?\n))) + (when (not (eq ?' (char-after (1- (point))))) + (insert ?\n))) ((condition-case () (prog1 t (up-list 1)) (error nil)) @@ -499,8 +498,8 @@ handle, whenever this is possible." (delete-region (point) (progn (skip-chars-forward " \t") (point))) - (if (not (eq ?' (char-after (1- (point))))) - (insert ?\n))) + (when (not (eq ?' (char-after (1- (point))))) + (insert ?\n))) (t (goto-char (point-max))))) (goto-char (point-min)) (indent-sexp) @@ -519,8 +518,8 @@ Output stream is STREAM, or value of `standard-output' (which see)." (let ((filename)) (while (not filename) (setq filename (read-file-name prompt nil nil existing-p)) - (if (or (null filename) (equal filename "")) - (progn (beep) (setq filename nil)))) + (when (or (null filename) (equal filename "")) + (beep) (setq filename nil))) filename)) (provide 'kfile) diff --git a/kotl/kotl-mode.el b/kotl/kotl-mode.el index e5ebab8fb5..fe584725c4 100644 --- a/kotl/kotl-mode.el +++ b/kotl/kotl-mode.el @@ -411,53 +411,57 @@ Does not delete across cell boundaries." arg (prefix-numeric-value current-prefix-arg)))) (unless arg (setq arg 1)) - (let ((del-count 0) - (indent (kcell-view:indent)) - count start end) - (cond ((> arg 0) - (if (kotl-mode:eocp) - (error "(kotl-mode:delete-char): End of cell") - (setq end (kcell-view:end) - arg (min arg (- end (point)))) - (while (and (> arg 0) (not (kotl-mode:eocp))) - (if (kotl-mode:eolp) - (if (not (eq ?\ (char-syntax (following-char)))) - (setq arg 0 - del-count (1- del-count)) - (delete-char 1 kill-flag) - ;; There may be non-whitespace characters in the - ;; indent area. Don't delete them. - (setq count indent) - (while (and (> count 0) - (eq ?\ (char-syntax (following-char)))) - (delete-char 1) - (setq count (1- count)))) - (delete-char 1 kill-flag)) - (setq arg (1- arg) - del-count (1+ del-count))))) - ((< arg 0) - (if (kotl-mode:bocp) - (error "(kotl-mode:delete-char): Beginning of cell") - (setq start (kcell-view:start) - arg (max arg (- start (point)))) - (while (and (< arg 0) (not (kotl-mode:bocp))) - (if (kotl-mode:bolp) - (if (not (eq ?\ (char-syntax (preceding-char)))) - (setq arg 0 - del-count (1- del-count)) - ;; There may be non-whitespace characters in the - ;; indent area. Don't delete them. - (setq count indent) - (while (and (> count 0) - (eq ?\ (char-syntax (preceding-char)))) - (delete-char -1) - (setq count (1- count))) - (if (zerop count) - (delete-char -1 kill-flag))) - (delete-char -1 kill-flag)) - (setq arg (1+ arg) - del-count (1+ del-count)))))) - del-count)) + + (if (not (and (boundp 'kview) (kview:is-p kview))) + ;; Support use within Org tables outside of the Koutliner + (delete-char arg kill-flag) + (let ((del-count 0) + (indent (kcell-view:indent)) + count start end) + (cond ((> arg 0) + (if (kotl-mode:eocp) + (error "(kotl-mode:delete-char): End of cell") + (setq end (kcell-view:end) + arg (min arg (- end (point)))) + (while (and (> arg 0) (not (kotl-mode:eocp))) + (if (kotl-mode:eolp) + (if (not (eq ?\ (char-syntax (following-char)))) + (setq arg 0 + del-count (1- del-count)) + (delete-char 1 kill-flag) + ;; There may be non-whitespace characters in the + ;; indent area. Don't delete them. + (setq count indent) + (while (and (> count 0) + (eq ?\ (char-syntax (following-char)))) + (delete-char 1) + (setq count (1- count)))) + (delete-char 1 kill-flag)) + (setq arg (1- arg) + del-count (1+ del-count))))) + ((< arg 0) + (if (kotl-mode:bocp) + (error "(kotl-mode:delete-char): Beginning of cell") + (setq start (kcell-view:start) + arg (max arg (- start (point)))) + (while (and (< arg 0) (not (kotl-mode:bocp))) + (if (kotl-mode:bolp) + (if (not (eq ?\ (char-syntax (preceding-char)))) + (setq arg 0 + del-count (1- del-count)) + ;; There may be non-whitespace characters in the + ;; indent area. Don't delete them. + (setq count indent) + (while (and (> count 0) + (eq ?\ (char-syntax (preceding-char)))) + (delete-char -1) + (setq count (1- count))) + (if (zerop count) + (delete-char -1 kill-flag))) + (delete-char -1 kill-flag)) + (setq arg (1+ arg) + del-count (1+ del-count)))))) + del-count))) (defun kotl-mode:delete-horizontal-space () "Delete all spaces and tabs around point." @@ -2464,8 +2468,10 @@ to one level and kotl-mode:refill-flag is treated as true." (unless parent (error "(kotl-mode:promote-tree): Cannot promote any further"))))) -(defun kotl-mode:remove-cell-attribute (attribute &optional pos) - "Remove ATTRIBUTE from the current cell or the cell at optional POS." +(defun kotl-mode:remove-cell-attribute (attribute &optional pos top-cell-flag) + "Remove ATTRIBUTE from the current cell or the cell at optional POS. +With optional prefix arg TOP-CELL-FLAG non-nil, removethe hidden top cell's +ATTRIBUTE and ignore any value of POS." (interactive (let* ((plist (copy-sequence (kcell-view:plist))) (existing-attributes plist) @@ -2490,18 +2496,25 @@ to one level and kotl-mode:refill-flag is treated as true." existing-attributes)))))) (beep)) (setq attribute (intern attribute)) - (list attribute nil))) + (list attribute nil top-cell-flag))) (barf-if-buffer-read-only) - (kcell-view:remove-attr attribute pos) + (if top-cell-flag + (kcell:remove-attr (kview:top-cell kview) attribute) + (kcell-view:remove-attr attribute pos)) ;; Note that buffer needs to be saved to store modified property list. (set-buffer-modified-p t) (when (called-interactively-p 'interactive) (message "Attribute `%s' removed from cell <%s>." - attribute (kcell-view:label pos)))) + attribute (if top-cell-flag + "0" + (kcell-view:label pos))))) -(defun kotl-mode:set-cell-attribute (attribute value &optional pos) +(defun kotl-mode:set-cell-attribute (attribute value &optional pos top-cell-flag) "Include ATTRIBUTE VALUE with the current cell or the cell at optional POS. -Replace any existing value that ATTRIBUTE has. +Replace any existing value that ATTRIBUTE has. With optional prefix arg +TOP-CELL-FLAG non-nil, modify the hidden top cell's ATTRIBUTE and ignore any +value of POS. + When called interactively, display the setting in the minibuffer as confirmation." (interactive @@ -2535,14 +2548,18 @@ confirmation." (prin1-to-string value t))) (setq value (read-minibuffer (format "Set property `%s' to (use double quotes around a string): " attribute)))) - (list attribute value nil))) + (list attribute value nil current-prefix-arg))) (barf-if-buffer-read-only) - (kcell-view:set-attr attribute value pos) + (if top-cell-flag + (kcell:set-attr (kview:top-cell kview) attribute value) + (kcell-view:set-attr attribute value pos)) ;; Note that buffer needs to be saved to store new attribute value. (set-buffer-modified-p t) (when (called-interactively-p 'interactive) (message "Attribute `%s' set to `%s' in cell <%s>." - attribute value (kcell-view:label pos)))) + attribute value (if top-cell-flag + "0" + (kcell-view:label pos))))) (defun kotl-mode:set-or-remove-cell-attribute (arg) "With prefix ARG, interactively run kotl-mode:remove-cell-attribute; otherwise, run kotl-mode:set-cell-attribute." @@ -2898,14 +2915,22 @@ See also the documentation for `kotl-mode:cell-attributes'." ;; (< cells-flag 1) (t (kotl-mode:cell-attributes t)))))))) -(defun kotl-mode:get-cell-attribute (attribute &optional pos) +(defun kotl-mode:get-cell-attribute (attribute &optional pos top-cell-flag) "Return ATTRIBUTE's value for the current cell or the cell at optional POS. +With optional prefix arg TOP-CELL-FLAG non-nil, return the hidden top cell's +ATTRIBUTE and ignore any value of POS. + When called interactively, it displays the value in the minibuffer." (interactive "SCurrent cell attribute to get: ") - (let ((value (kcell-view:get-attr attribute pos))) + (let ((value + (if top-cell-flag + (kcell:get-attr (kview:top-cell kview) attribute) + (kcell-view:get-attr attribute pos)))) (when (called-interactively-p 'interactive) (message "Attribute \"%s\" = `%s' in cell <%s>." - attribute value (kcell-view:label pos))) + attribute value (if top-cell-flag + "0" + (kcell-view:label pos)))) value)) ;;; ------------------------------------------------------------------------ diff --git a/kotl/kview.el b/kotl/kview.el index 619112a748..86ffcda6ea 100644 --- a/kotl/kview.el +++ b/kotl/kview.el @@ -121,9 +121,11 @@ Trigger an error if CELL-REF is not a string or is not found." (integerp cell-ref)) (let ((idstamp (kcell:ref-to-id cell-ref)) pos) - (or (and idstamp (setq pos (kproperty:position 'idstamp idstamp)) - (kcell-view:cell pos)) - (error "(kcell:get-from-ref): No such Koutline cell: '%s'" cell-ref))) + (cond ((and idstamp (zerop idstamp)) + (kview:top-cell kview)) + ((and idstamp (setq pos (kproperty:position 'idstamp idstamp))) + (kcell-view:cell pos)) + (t (error "(kcell:get-from-ref): No such Koutline cell: '%s'" cell-ref)))) (error "(kcell:get-from-ref): cell-ref arg must be a string, not: %s" cell-ref))) (defun kcell-view:child (&optional visible-p label-sep-len) @@ -589,9 +591,9 @@ level." (overlays-at (or pos (point)))))))) (defun kview:create (buffer-name - &optional id-counter label-type level-indent - label-separator label-min-width blank-lines - levels-to-show lines-to-show) + &optional id-counter top-cell-attributes + label-type level-indent label-separator + label-min-width blank-lines levels-to-show lines-to-show) "Return a new kview for BUFFER-NAME. Optional ID-COUNTER is the maximum permanent id previously given out in this outline. Optional LABEL-TYPE, LEVEL-INDENT, LABEL-SEPARATOR, LABEL-MIN-WIDTH, @@ -617,11 +619,13 @@ BLANK-LINES, LEVELS-TO-SHOW, and LINES-TO-SHOW may also be given, otherwise defa ;; Don't recreate view if it exists. (unless (and (boundp 'kview) (kview:is-p kview) (eq (kview:buffer kview) buf)) (make-local-variable 'kview) + (unless top-cell-attributes + (setq top-cell-attributes (list 'file buffer-file-name 'id-counter id-counter))) (setq kview (list 'kview 'plist (list 'view-buffer (current-buffer) 'top-cell - (kcell:create-top buffer-file-name id-counter) + (kcell:create-top top-cell-attributes) 'label-type (or label-type kview:default-label-type) 'label-min-width (or label-min-width kview:default-label-min-width) diff --git a/test/kexport-tests.el b/test/kexport-tests.el index 76630375a5..2699a56e45 100644 --- a/test/kexport-tests.el +++ b/test/kexport-tests.el @@ -39,9 +39,10 @@ (delete-file html-file))))) (ert-deftest kexport:html-sets-title-and-header () - "kexport:html set title and header from first cell." - (let ((kotl-file (make-temp-file "hypb" nil ".kotl")) - (html-file (make-temp-file "hypb" nil ".html"))) + "kexport:html set title and header from kotl filename without suffix." + (let* ((kotl-file (make-temp-file "hypb" nil ".kotl")) + (html-file (make-temp-file "hypb" nil ".html")) + (title (file-name-sans-extension (file-name-nondirectory kotl-file)))) (unwind-protect (progn (find-file kotl-file) @@ -52,8 +53,8 @@ (kexport:html kotl-file html-file)) (find-file html-file) (goto-char (point-min)) - (re-search-forward "<title>first</title>") - (re-search-forward "<h1>first</h1>")) + (re-search-forward (format "<title>%s</title>" title)) + (re-search-forward (format "<h1>%s</h1>" title))) (progn (delete-file kotl-file) (delete-file html-file))))) @@ -104,7 +105,7 @@ (delete-file kotl-file) (delete-file html-file))))) -(ert-deftest kexport:display-creates-html-file-and-displayes-it () +(ert-deftest kexport:display-creates-html-file-and-displays-it () "kexport:display creates html file and displays it in external browser." (let* ((kotl-file (make-temp-file "hypb" nil ".kotl")) (html-file (concat (file-name-sans-extension kotl-file) ".html"))