Browse Source

org-index.el, version 5.3.0: focus can now be a list; cleaned up dependencies

U-IHM-NOTEBOOK\Olli 8 years ago
parent
commit
0ea6845711
1 changed files with 136 additions and 75 deletions
  1. 136 75
      contrib/lisp/org-index.el

+ 136 - 75
contrib/lisp/org-index.el

@@ -3,7 +3,7 @@
 ;; Copyright (C) 2011-2017 Free Software Foundation, Inc.
 ;; Copyright (C) 2011-2017 Free Software Foundation, Inc.
 
 
 ;; Author: Marc Ihm <org-index@2484.de>
 ;; Author: Marc Ihm <org-index@2484.de>
-;; Version: 5.2.3
+;; Version: 5.3.0
 ;; Keywords: outlines index
 ;; Keywords: outlines index
 
 
 ;; This file is not part of GNU Emacs.
 ;; This file is not part of GNU Emacs.
@@ -85,6 +85,10 @@
 
 
 ;;; Change Log:
 ;;; Change Log:
 
 
+;;   [2017-03-26 Su] Version 5.3.0
+;;   - Focused can now be on a list of nodes (instead of a single one)
+;;   - Cleaned up undeclared dependencies
+;;
 ;;   [2017-02-18 Sa] Version 5.2.3
 ;;   [2017-02-18 Sa] Version 5.2.3
 ;;   - New command 'focus'
 ;;   - New command 'focus'
 ;;   - Speeded up org-index--parse-table with the stored property "max-ref"
 ;;   - Speeded up org-index--parse-table with the stored property "max-ref"
@@ -175,11 +179,12 @@
 ;;; Code:
 ;;; Code:
 
 
 (require 'org-table)
 (require 'org-table)
+(require 'org-id)
 (require 'cl-lib)
 (require 'cl-lib)
 (require 'widget)
 (require 'widget)
 
 
 ;; Version of this package
 ;; Version of this package
-(defvar org-index-version "5.2.3" "Version of `org-index', format is major.minor.bugfix, where \"major\" are incompatible changes and \"minor\" are new features.")
+(defvar org-index-version "5.3.0" "Version of `org-index', format is major.minor.bugfix, where \"major\" are incompatible changes and \"minor\" are new features.")
 
 
 ;; customizable options
 ;; customizable options
 (defgroup org-index nil
 (defgroup org-index nil
@@ -224,14 +229,14 @@ mixed  First, show all index entries, which have been
   :type 'key-sequence)
   :type 'key-sequence)
 
 
 (defcustom org-index-idle-delay 68
 (defcustom org-index-idle-delay 68
-  "Delay in seconds after which buffer will sorted or fontified when emacs is idle."
+  "Delay in seconds after which buffer will sorted or fontified when Emacs is idle."
   :group 'org-index
   :group 'org-index
   :type 'integer)
   :type 'integer)
 
 
 (defcustom org-index-prepare-when-idle nil
 (defcustom org-index-prepare-when-idle nil
-  "Optionally fontify and sort index-table when idle, so that first interactive call is faster.
-You only need this if your index has grown so large, that first invocation of org-index needs
-a noticable amount of time."
+  "Fontify and sort index-table when idle to make first call faster.
+You only need this if your index has grown so large, that first
+invocation of `org-index' needs a noticable amount of time."
   :group 'org-index
   :group 'org-index
   :initialize 'custom-initialize-set
   :initialize 'custom-initialize-set
   :set (lambda (var val)
   :set (lambda (var val)
@@ -303,7 +308,8 @@ those pieces."
 (defvar org-index--saved-positions nil "Saved positions within current buffer and index buffer; filled by ‘org-index--save-positions’.")
 (defvar org-index--saved-positions nil "Saved positions within current buffer and index buffer; filled by ‘org-index--save-positions’.")
 (defvar org-index--headings nil "Headlines of index-table as a string.")
 (defvar org-index--headings nil "Headlines of index-table as a string.")
 (defvar org-index--headings-visible nil "Visible part of headlines of index-table as a string.")
 (defvar org-index--headings-visible nil "Visible part of headlines of index-table as a string.")
-(defvar org-index--id-focused-node nil "Id of focused node (if any).")
+(defvar org-index--ids-focused-nodes nil "Ids of focused node (if any).")
+(defvar org-index--id-last-goto-focus nil "Id of last node, that has been jumped to.")
 
 
 ;; Variables to hold context and state
 ;; Variables to hold context and state
 (defvar org-index--last-fingerprint nil "Fingerprint of last line created.")
 (defvar org-index--last-fingerprint nil "Fingerprint of last line created.")
@@ -367,31 +373,31 @@ if VALUE cannot be found."
 (defun org-index (&optional command search-ref arg)
 (defun org-index (&optional command search-ref arg)
   "Fast search-index for selected org nodes and things outside of org.
   "Fast search-index for selected org nodes and things outside of org.
 
 
-org-index creates and updates an index table with keywords; each line
-either points to a heading in org, references something outside or
-carries a snippet of text to yank.  The index table is searched for
-keywords through an incremental occur; results are sorted by usage
-count and date, so that frequently used entries appear first among
-the results.
+This function creates and updates an index table with keywords;
+each line either points to a heading in org, references something
+outside or carries a snippet of text to yank.  The index table is
+searched for keywords by means of an incremental occur; results
+are sorted by usage count and date, so that frequently used
+entries appear first.
 
 
 References are decorated numbers (e.g. 'R237' or '--455--'); they are
 References are decorated numbers (e.g. 'R237' or '--455--'); they are
 well suited to be used outside of org, e.g. in folder names, ticket
 well suited to be used outside of org, e.g. in folder names, ticket
 systems or on printed documents.
 systems or on printed documents.
 
 
-On first invocation org-index will help to create a dedicated node
+On first invocation this function will help to create a dedicated node
 for its index table.
 for its index table.
 
 
 To start building up your index, use subcommands 'add', 'ref' and
 To start building up your index, use subcommands 'add', 'ref' and
 'yank' to create entries and use 'occur' to find them.
 'yank' to create entries and use 'occur' to find them.
 
 
-This is version 5.2.3 of org-index.el.
+This is version 5.3.0 of org-index.el.
 
 
 
 
 The function `org-index' is the only interactive function of this
 The function `org-index' is the only interactive function of this
 package and its main entry point; it will present you with a list
 package and its main entry point; it will present you with a list
 of subcommands to choose from:
 of subcommands to choose from:
 
 
-\(Note the one-letter shortcuts, e.g. [o]; used like 'C-c i o'.)
+\(Note the one-letter shortcuts, e.g. [o]; used like `\\[org-index-dispatch] o'.)
 
 
   occur: [o] Incrementally show matching lines from index.
   occur: [o] Incrementally show matching lines from index.
     Result is updated after every keystroke.  You may enter a
     Result is updated after every keystroke.  You may enter a
@@ -400,7 +406,7 @@ of subcommands to choose from:
 
 
   add: [a] Add the current node to index.
   add: [a] Add the current node to index.
     So that (e.g.) it can be found through the subcommand
     So that (e.g.) it can be found through the subcommand
-    'occur'. Update index, if node is already present.
+    'occur'.  Update index, if node is already present.
 
 
   kill: [k] Kill (delete) the current node from index.
   kill: [k] Kill (delete) the current node from index.
     Can be invoked from index, from occur or from a headline.
     Can be invoked from index, from occur or from a headline.
@@ -428,16 +434,20 @@ of subcommands to choose from:
   edit: [e] Present current line in edit buffer.
   edit: [e] Present current line in edit buffer.
     Can be invoked from index, from occur or from a headline.
     Can be invoked from index, from occur or from a headline.
 
 
-  help: Show complete help text of org-index.
+  help: Show complete help text of `org-index'.
 
 
-  focus: [f] Return to focus-node; need to set-focus before.
-    The focused node is a single and special node, the location
-    of which is remembered and which can be found with a single
-    key-sequence; it need not be part of the index though.  This
-    can be useful, if you mostly work in a single node, but make
-    frequent excursions to others.
+  focus: [f] Return to first focused node; repeat to see them all.
+    With prefix: reverse order.  You Need to set-focus before.
+    The focused nodes are kept in a short list and can be found
+    by hitting a single key; they need not be part of the index
+    though.  This can be useful, if you work in one or few nodes,
+    but make frequent excursions to others, which are part of the
+    index.
 
 
-  set-focus: [F] Set focus-node for command focus.
+  set-focus: [F] Set focus to current node, with prefix: append.
+    To truncate the list of focused nodes, just focus on a single
+    node; to remove current node from focus list supply a double
+    prefix.
 
 
   short-help: [?] Show one-line description of each subcommand.
   short-help: [?] Show one-line description of each subcommand.
     I.e. show this list but only first sentence each.
     I.e. show this list but only first sentence each.
@@ -450,7 +460,7 @@ of subcommands to choose from:
     by count, reference or last access.
     by count, reference or last access.
 
 
   find-ref: Search for given reference in all org-buffers.
   find-ref: Search for given reference in all org-buffers.
-    A wrapper to employ emacs standard `multi-occur' function;
+    A wrapper to employ Emacs standard `multi-occur' function;
     asks for reference.
     asks for reference.
 
 
   highlight: Highlight or unhighlight all references.
   highlight: Highlight or unhighlight all references.
@@ -463,18 +473,18 @@ of subcommands to choose from:
 If you invoke `org-index' for the first time, an assistant will be
 If you invoke `org-index' for the first time, an assistant will be
 invoked, that helps you to create your own index.
 invoked, that helps you to create your own index.
 
 
-Invoke `org-customize' to tweak the behaviour of org-index.
+Invoke `org-customize' to tweak the behaviour of `org-index'.
 
 
-Optionally bind `org-index-dispatch' to a key, e.g. 'C-c i' in
-the global keymap to invoke the most important subcommands with
-a single key.
+This includes the global key `org-index-dispatch-key' to invoke
+the most important subcommands with one additional key.
 
 
 A numeric prefix argument is used as a reference number for
 A numeric prefix argument is used as a reference number for
-commands, that need one (e.g. 'head').
+commands, that need one (e.g. 'head') or to modify their
+behaviour (e.g. 'focus').
 
 
 Use from elisp: Optional argument COMMAND is a symbol naming the
 Use from elisp: Optional argument COMMAND is a symbol naming the
-command to execute. SEARCH-REF specifies a reference to search
-for, if needed. ARG allows passing in a prefix argument as in
+command to execute.  SEARCH-REF specifies a reference to search
+for, if needed.  ARG allows passing in a prefix argument as in
 interactive calls."
 interactive calls."
 
 
   (interactive "i\ni\nP")
   (interactive "i\ni\nP")
@@ -557,9 +567,9 @@ interactive calls."
         (unless search-ref
         (unless search-ref
           (if (eq command 'index)
           (if (eq command 'index)
               (let ((r (org-index--read-search-for-index)))
               (let ((r (org-index--read-search-for-index)))
-                (setq search-ref (first r))
-                (setq search-id (second r))
-                (setq search-fingerprint (third r)))
+                (setq search-ref (cl-first r))
+                (setq search-id (cl-second r))
+                (setq search-fingerprint (cl-third r)))
             (unless (and (eq command 'head)
             (unless (and (eq command 'head)
                          org-index--within-index-node
                          org-index--within-index-node
                          (org-at-table-p))
                          (org-at-table-p))
@@ -710,7 +720,7 @@ interactive calls."
 
 
        ((eq command 'ping)
        ((eq command 'ping)
 
 
-        (let ((moved-up 0) id info reached-top)
+        (let ((moved-up 0) id info reached-top done)
 
 
           (unless (string= major-mode "org-mode") (error "No node at point"))
           (unless (string= major-mode "org-mode") (error "No node at point"))
           ;; take id from current node or reference
           ;; take id from current node or reference
@@ -721,17 +731,16 @@ interactive calls."
           ;; move up until we find a node in index
           ;; move up until we find a node in index
           (save-excursion
           (save-excursion
             (outline-back-to-heading)
             (outline-back-to-heading)
-            (while (not (or info
-                            reached-top))
+            (while (not done)
               (if id
               (if id
                   (setq info (org-index--on 'id id
                   (setq info (org-index--on 'id id
                                (mapcar (lambda (x) (org-index--get-or-set-field x))
                                (mapcar (lambda (x) (org-index--get-or-set-field x))
-                                       (list 'ref 'count 'created 'last-accessed 'category 'keywords 'ref)))))
+                                       (list 'keywords 'count 'created 'last-accessed 'category 'ref)))))
 
 
               (setq reached-top (= (org-outline-level) 1))
               (setq reached-top (= (org-outline-level) 1))
 
 
-              (unless (or info
-                          reached-top)
+              (if (or info reached-top)
+                  (setq done t)
                 (outline-up-heading 1 t)
                 (outline-up-heading 1 t)
                 (cl-incf moved-up))
                 (cl-incf moved-up))
 
 
@@ -741,9 +750,9 @@ interactive calls."
               (progn
               (progn
                 (setq message-text
                 (setq message-text
                       (apply 'format
                       (apply 'format
-                             (append (list "'%s'%shas been accessed %s times between %s and %s; category is '%s', keywords are '%s'"
+                             (append (list "'%s'%s has been accessed %s times between %s and %s; category is '%s', reference is '%s'"
                                            (pop info)
                                            (pop info)
-                                           (if (> moved-up 0) (format " (parent node, %d level up) " moved-up) " "))
+                                           (if (> moved-up 0) (format " (parent node, %d level up)" moved-up) ""))
                                      info)))
                                      info)))
                 (setq kill-new-text (car (last info))))
                 (setq kill-new-text (car (last info))))
             (setq message-text "Neither this node nor any of its parents is part of index"))))
             (setq message-text "Neither this node nor any of its parents is part of index"))))
@@ -835,9 +844,9 @@ interactive calls."
                    (symbol-name sort)
                    (symbol-name sort)
                    org-index-sort-by
                    org-index-sort-by
                    org-index-idle-delay
                    org-index-idle-delay
-                   (second groups-and-counts)
+                   (cl-second groups-and-counts)
                    (symbol-name sort)
                    (symbol-name sort)
-                   (third groups-and-counts))))
+                   (cl-third groups-and-counts))))
 
 
            ((memq sort-what '(region buffer))
            ((memq sort-what '(region buffer))
             (org-index--do-sort-lines sort-what)
             (org-index--do-sort-lines sort-what)
@@ -863,25 +872,11 @@ interactive calls."
 
 
 
 
        ((eq command 'focus)
        ((eq command 'focus)
-
-        (if org-index--id-focused-node
-            (let (marker)
-              (setq marker (org-id-find org-index--id-focused-node 'marker))
-              (unless marker (error "Could not find focus-node"))
-              (pop-to-buffer-same-window (marker-buffer marker))
-              (goto-char (marker-position marker))
-              (org-index--unfold-buffer)
-              (move-marker marker nil)
-              (setq message-text "Jumped to focus-node"))
-          (setq message-text "No focus-node, use set-focus")))
+        (setq message-text (org-index--goto-focus arg)))
 
 
        
        
        ((eq command 'set-focus)
        ((eq command 'set-focus)
-        (let ((focus-id (org-id-get-create)))
-          (with-current-buffer org-index--buffer
-            (org-entry-put org-index--point "id-focused-node" focus-id)
-            (setq org-index--id-focused-node focus-id)
-            (setq message-text "Focus has been set on current node"))))
+        (setq message-text (org-index--set-focus arg)))
 
 
        
        
        ((eq command 'maintain)
        ((eq command 'maintain)
@@ -1033,7 +1028,7 @@ Optional argument WITH-SHORT-HELP displays help screen upfront."
       (with-temp-buffer
       (with-temp-buffer
         (insert (documentation 'org-index))
         (insert (documentation 'org-index))
         (goto-char (point-min))
         (goto-char (point-min))
-        (search-forward (concat "  " (symbol-name (first org-index--commands)) ": "))
+        (search-forward (concat "  " (symbol-name (cl-first org-index--commands)) ": "))
         (forward-line 0)
         (forward-line 0)
         (kill-region (point-min) (point))
         (kill-region (point-min) (point))
         (search-forward (concat "  " (symbol-name (car (last org-index--commands))) ": "))
         (search-forward (concat "  " (symbol-name (car (last org-index--commands))) ": "))
@@ -1061,7 +1056,7 @@ Optional argument WITH-SHORT-HELP displays help screen upfront."
         (insert (org-index--get-short-help-text))
         (insert (org-index--get-short-help-text))
         (goto-char (point-min))
         (goto-char (point-min))
         (while (< (point) (point-max))
         (while (< (point) (point-max))
-          (when (looking-at "^  \\([-a-z]+\\) +: +\\[\\([a-z?]\\)\\] ")
+          (when (looking-at "^  \\([-a-z]+\\)[ \t]+: +\\[\\([a-z?]\\)\\] ")
             (setq org-index--shortcut-chars
             (setq org-index--shortcut-chars
                   (cons (cons (match-string 2) (intern (match-string 1)))
                   (cons (cons (match-string 2) (intern (match-string 1)))
                         org-index--shortcut-chars)))
                         org-index--shortcut-chars)))
@@ -1071,6 +1066,71 @@ Optional argument WITH-SHORT-HELP displays help screen upfront."
         org-index--shortcut-chars)))
         org-index--shortcut-chars)))
 
 
 
 
+(defun org-index--goto-focus (arg)
+  "Goto focus node, one after the other; with ARG: reverse."
+  (if org-index--ids-focused-nodes
+      (let ((maybe-reverse (lambda (&rest x) (if (equal arg '(4)) (reverse x) x)))
+            last-id next-ids marker)
+        (setq last-id (or org-index--id-last-goto-focus
+                          (last org-index--ids-focused-nodes)))
+        (setq next-id
+              (car (or (cdr-safe (member last-id
+                                         (apply maybe-reverse
+                                                (append org-index--ids-focused-nodes
+                                                        org-index--ids-focused-nodes))))
+                       (apply maybe-reverse org-index--ids-focused-nodes))))
+        (or (setq marker (org-id-find next-id 'marker))
+            (error "Could not find focus-node with id %s" next-id))
+        (setq org-index--id-last-goto-focus next-id)
+        (pop-to-buffer-same-window (marker-buffer marker))
+        (goto-char (marker-position marker))
+        (org-index--unfold-buffer)
+        (move-marker marker nil)
+        (if (cdr org-index--ids-focused-nodes) 
+            (format "Jumped to %s focus-node (out of %d)"
+                    (if (equal arg '(4)) "previous" "next")
+                    (length org-index--ids-focused-nodes))
+          "Jumped to single focus-node"))
+      "No nodes in focus, use set-focus"))
+
+
+(defun org-index--set-focus (arg)
+  "Set focus node, with prefix ARG, append to list, with double prefix: delete."
+  (let (id text)
+
+    (setq text
+          (cond
+
+           ((not arg)
+            (setq id (org-id-get-create))
+            (setq org-index--ids-focused-nodes (list id))
+            "Focus has been set on current node (1 node in focus)")
+
+           ((equal arg '(4))
+            (setq id (org-id-get-create))
+            (unless (member id org-index--ids-focused-nodes)
+              (setq org-index--ids-focused-nodes (cons id org-index--ids-focused-nodes)))
+            (setq org-index--id-last-goto-focus id)
+            "Current node has been appended to list of focused nodes (%d node%s in focus)")
+
+           ((equal arg '(16))
+            (setq id (org-id-get))
+            (if (and id  (member id org-index--ids-focused-nodes))
+                (progn
+                  (setq org-index--id-last-goto-focus
+                        (or (car-safe (cdr-safe (member id (reverse (append org-index--ids-focused-nodes
+                                                                            org-index--ids-focused-nodes)))))
+                            org-index--id-last-goto-focus))
+                  (setq org-index--ids-focused-nodes (delete id org-index--ids-focused-nodes))
+                  "Current node has been removed from list of focused nodes (%d node%s in focus)")
+              "Current node has not been in list of focused nodes (%d node%s in focus)"))))
+    
+    (with-current-buffer org-index--buffer
+      (org-entry-put org-index--point "ids-focused-nodes" (string-join org-index--ids-focused-nodes " ")))
+    
+    (format text (length org-index--ids-focused-nodes) (if (cdr org-index--ids-focused-nodes) "s" ""))))
+
+
 (defun org-index--do-edit ()
 (defun org-index--do-edit ()
   "Perform command edit."
   "Perform command edit."
   (let ((maxlen 0) cols-vals buffer-keymap field-keymap keywords-pos val)
   (let ((maxlen 0) cols-vals buffer-keymap field-keymap keywords-pos val)
@@ -1107,13 +1167,13 @@ Optional argument WITH-SHORT-HELP displays help screen upfront."
     ;; we need two different keymaps
     ;; we need two different keymaps
     (setq buffer-keymap (make-sparse-keymap))
     (setq buffer-keymap (make-sparse-keymap))
     (set-keymap-parent buffer-keymap widget-keymap)
     (set-keymap-parent buffer-keymap widget-keymap)
-    (define-key buffer-keymap (kbd "C-c C-c") 'org-index--edit-c-c-c-c)
-    (define-key buffer-keymap (kbd "C-c C-k") 'org-index--edit-c-c-c-k)
+    (define-key buffer-keymap (kbd "C-c C-c") 'org-index--edit-accept)
+    (define-key buffer-keymap (kbd "C-c C-k") 'org-index--edit-abort)
       
       
     (setq field-keymap (make-sparse-keymap))
     (setq field-keymap (make-sparse-keymap))
     (set-keymap-parent field-keymap widget-field-keymap)
     (set-keymap-parent field-keymap widget-field-keymap)
-    (define-key field-keymap (kbd "C-c C-c") 'org-index--edit-c-c-c-c)
-    (define-key field-keymap (kbd "C-c C-k") 'org-index--edit-c-c-c-k)
+    (define-key field-keymap (kbd "C-c C-c") 'org-index--edit-accept)
+    (define-key field-keymap (kbd "C-c C-k") 'org-index--edit-abort)
 
 
     ;; prepare buffer
     ;; prepare buffer
     (setq org-index--context-index (cons (point) (org-index--line-in-canonical-form)))
     (setq org-index--context-index (cons (point) (org-index--line-in-canonical-form)))
@@ -1141,8 +1201,8 @@ Optional argument WITH-SHORT-HELP displays help screen upfront."
     "Editing a single line from index"))
     "Editing a single line from index"))
   
   
 
 
-(defun org-index--edit-c-c-c-c ()
-  "Function to invoke on C-c C-c in Edit buffer."
+(defun org-index--edit-accept ()
+  "Function to accept editing in Edit buffer."
   (interactive)
   (interactive)
 
 
   (let ((obuf (get-buffer org-index--occur-buffer-name))
   (let ((obuf (get-buffer org-index--occur-buffer-name))
@@ -1204,8 +1264,8 @@ Optional argument WITH-SHORT-HELP displays help screen upfront."
     (message "Index line has been edited.")))
     (message "Index line has been edited.")))
 
 
 
 
-(defun org-index--edit-c-c-c-k ()
-  "Function invoked on C-c C-k in Edit buffer."
+(defun org-index--edit-abort ()
+  "Function to abort editing in Edit buffer."
   (interactive)
   (interactive)
   (kill-buffer org-index--edit-buffer-name)
   (kill-buffer org-index--edit-buffer-name)
   (setq org-index--context-index nil)
   (setq org-index--context-index nil)
@@ -1472,8 +1532,9 @@ Optional argument CHECK-SORT-MIXED triggers resorting if mixed and stale."
       (unless org-index--head (org-index--get-decoration-from-ref-field ref-field))
       (unless org-index--head (org-index--get-decoration-from-ref-field ref-field))
       (setq org-index--maxrefnum (org-index--extract-refnum ref-field))
       (setq org-index--maxrefnum (org-index--extract-refnum ref-field))
     
     
-      ;; Get id of focused node (if any)
-      (setq org-index--id-focused-node (org-entry-get nil "id-focused-node"))
+      ;; Get ids of focused node (if any)
+      (setq org-index--ids-focused-nodes (split-string (or (org-entry-get nil "ids-focused-nodes") "")))
+      (org-entry-delete (point) "id-focused-node") ; migrate (kind of) from previous versions
 
 
       ;; save position below hline
       ;; save position below hline
       (org-index--go-below-hline)
       (org-index--go-below-hline)
@@ -2972,7 +3033,7 @@ If OTHER in separate window."
                   (setq yank (replace-regexp-in-string (regexp-quote "\\vert") "|" yank nil 'literal))
                   (setq yank (replace-regexp-in-string (regexp-quote "\\vert") "|" yank nil 'literal))
                   (kill-new yank)
                   (kill-new yank)
                   (org-mark-ring-goto)
                   (org-mark-ring-goto)
-                  (if (s-starts-with-p "http" yank)
+                  (if (string= (substring yank 0 3) "http")
                       (progn
                       (progn
                         (browse-url yank)
                         (browse-url yank)
                         (format "Opened '%s' in browser (and copied it too)" yank))
                         (format "Opened '%s' in browser (and copied it too)" yank))