branch: externals/hyperbole
commit 6494cb00a85084713af04f8e07c626f3fa835f12
Author: bw <r...@gnu.org>
Commit: bw <r...@gnu.org>

    klink.el - Fix klink yank handler file-name handling
---
 ChangeLog     | 14 ++++++++++++
 kotl/klink.el | 72 +++++++++++++++++++++++++++++++++++++++++------------------
 2 files changed, 64 insertions(+), 22 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index d4741b3b6d..129fad4b51 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2025-06-21  Bob Weiner  <r...@gnu.org>
+
+* kotl/klink.el (klink:create): Rewrite to set missing klink yank handler on
+    any newly created klink.
+                (klink:yank-handler): Fix by making first group optional rather
+    than what is in the group, so it matches to klinks like, <#1>.
+                (klink:set-yank-handler): Add full path 'file-name' property
+    for klink and retrieve it in klink:yank-handler if not a part of the klink
+    string text.  Prevent klink yank-handler text properties from being
+    sticky/inherited by neighboring text by adding to
+    'text-property-default-nonsticky'.
+                (klink:create-link): Add as an autoload to return a klink 
string.
+    Use in 'klink:create' which inserts the link into the current buffer.
+
 2025-06-20  Bob Weiner  <r...@gnu.org>
 
 * kotl/kotl-mode.el (kotl-mode:kill-region):
diff --git a/kotl/klink.el b/kotl/klink.el
index 8bbd01c4c0..1333e8fc92 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:     18-Aug-24 at 09:42:48 by Mats Lidell
+;; Last-Mod:     21-Jun-25 at 13:15:56 by Bob Weiner
 ;;
 ;; SPDX-License-Identifier: GPL-3.0-or-later
 ;;
@@ -136,7 +136,21 @@ See documentation for `kcell:ref-to-id' for valid cell-ref 
formats."
      (save-excursion
        (hargs:iform-read
        '(interactive "*+LInsert link to <[file]#cell-id[|vspecs]>: ")))))
-  (barf-if-buffer-read-only)
+  (insert (klink:create-link reference)))
+
+;;;###autoload
+(defun klink:create-link (reference)
+  "Return a klink implicit link string to REFERENCE.
+REFERENCE should be a cell-ref or a string containing \"filename#cell-ref\".
+See documentation for `kcell:ref-to-id' for valid cell-ref formats."
+  (interactive
+   (progn
+     ;; This `default-directory' setting is referenced in "hargs.el" for 
argument getting.
+     (hattr:set 'hbut:current 'dir default-directory)
+     (save-excursion
+       (hargs:iform-read
+       '(interactive "*+LLink to <[file]#cell-id[|vspecs]>: ")))))
+
   ;; Reference generally is a string.  It may be a list as a string, e.g.
   ;; "(\"file\" \"cell\")", in which case, we remove the unneeded internal
   ;; double quotes and then parse it with pattern matching.
@@ -146,7 +160,7 @@ See documentation for `kcell:ref-to-id' for valid cell-ref 
formats."
   ;; This `default-directory' setting is referenced in "hargs.el" for
   ;; getting arguments.
   (hattr:set 'hbut:current 'dir default-directory)
-  (let (file-ref cell-ref)
+  (let (file-ref cell-ref klink)
     (setq reference (klink:parse reference)
          file-ref  (car reference)
          cell-ref  (nth 1 reference))
@@ -154,17 +168,18 @@ See documentation for `kcell:ref-to-id' for valid 
cell-ref formats."
     (when (and file-ref (equal (hypb:buffer-file-name)
                               (expand-file-name file-ref default-directory)))
       (setq file-ref nil))
-    (cond (file-ref
-          (setq file-ref (hpath:relative-to file-ref))
-          ;; Remove "./" prefix, if any.
-          (when (string-match "^\\./" file-ref)
-            (setq file-ref (substring file-ref (match-end 0))))
-          (insert "<" file-ref)
-          (when cell-ref
-            (insert "#" cell-ref))
-          (insert ">"))
-         (cell-ref (insert "<#" cell-ref ">"))
-         (t  (error "(klink:create) Invalid reference, `%s'" reference)))))
+    (setq klink (cond (file-ref
+                      (setq file-ref (hpath:relative-to file-ref))
+                      ;; Remove "./" prefix, if any.
+                      (when (string-match "^\\./" file-ref)
+                        (setq file-ref (substring file-ref (match-end 0))))
+                      (concat "<" file-ref
+                              (when cell-ref
+                                (concat "#" cell-ref))
+                              ">"))
+                     (cell-ref (concat "<#" cell-ref ">"))
+                     (t  (error "(klink:create-link) Invalid reference, `%s'" 
reference))))
+    (klink:set-yank-handler klink)))
 
 ;;;###autoload
 (defun klink:at-p ()
@@ -234,12 +249,20 @@ link-end-position, (including delimiters)."
                     (string-match "\\`[a-zA-Z!/]" referent))))
       label-and-pos)))
 
+;; Prevent klink yank-handler text properties from being
+;; sticky/inherited by neighboring text.
+(cl-pushnew '(file-name . t) text-property-default-nonsticky)
+(cl-pushnew '(yank-handler . t) text-property-default-nonsticky)
+(cl-pushnew '(yank-excluded-properties . t) text-property-default-nonsticky)
+
 (defun klink:set-yank-handler (klink)
   "Add yank-handler to KLINK and return the modified KLINK.
 Link is made relative when yanked into the same koutline or the
 same directory."
-  (add-text-properties 0 (length klink)
-                      (list 'yank-handler '(klink:yank-handler)
+  (add-text-properties
+   0 (length klink)
+                      (list 'file-name buffer-file-name
+                            'yank-handler '(klink:yank-handler)
                             'yank-excluded-properties (cons 'yank-handler 
(get-text-property 0 'yank-excluded-properties klink)))
                       klink)
   klink)
@@ -371,18 +394,23 @@ Assume point is in klink referent buffer, where the klink 
points."
              (klink:replace-label klink link-buf start new-label)))))
 
 (defun klink:yank-handler (klink)
-  (if (string-match "<\\([^,]+?\\)[#,][ \t]*\\(.+\\)" klink)
-      (let* ((file (match-string 1 klink))
+  (if (string-match "<\\([^,]+\\)?[#,][ \t]*\\(.+\\)" klink)
+      (let* ((file (or (match-string 1 klink)
+                      (get-text-property 0 'file-name klink)))
             (rest (match-string 2 klink))
-            (dir (file-name-directory file)))
-       (cond ((equal file (hypb:buffer-file-name))
+            (dir (file-name-directory file))
+            (buf-file (hypb:buffer-file-name)))
+       (cond ((equal file buf-file)
               ;; Remove the klink filename since yanking into the
               ;; same file
               (insert (format "<#%s" rest)))
-             ((and (hypb:buffer-file-name) (equal dir (file-name-directory 
(hypb:buffer-file-name))))
+             ((and buf-file (equal dir (file-name-directory buf-file)))
               ;; Use filename without dir since yanking into same directory
               (insert (format "<%s#%s" (file-name-nondirectory file) rest)))
-             (t (insert klink))))
+             (t (if (or (not file)
+                        (and file (string-prefix-p file (substring klink 1))))
+                    (insert klink)
+                  (insert (format "<%s#%s" file rest))))))
     (insert klink)))
                                 
 (provide 'klink)

Reply via email to