Warning: this is an htmlized version!
The original is here, and the conversion rules are here. |
;;; eev-kla.el -- kill link to anchor and friends. -*- lexical-binding: nil; -*- ;; Copyright (C) 2022-2024 Free Software Foundation, Inc. ;; ;; This file is part of GNU eev. ;; ;; GNU eev is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; ;; GNU eev is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. ;; ;; Author: Eduardo Ochs <[email protected]> ;; Maintainer: Eduardo Ochs <[email protected]> ;; Version: 20240103 ;; Keywords: e-scripts ;; ;; Latest version: <http://anggtwu.net/eev-current/eev-kla.el> ;; htmlized: <http://anggtwu.net/eev-current/eev-kla.el.html> ;; See also: <http://anggtwu.net/eev-current/eev-beginner.el.html> ;; <http://anggtwu.net/eev-intros/find-kla-intro.html> ;; (find-kla-intro) ;; «.a-test» (to "a-test") ;; «.more-tests» (to "more-tests") ;; «.test-elsewhere» (to "test-elsewhere") ;; «.the-video» (to "the-video") ;; ;; «.ee-kl-expand» (to "ee-kl-expand") ;; «.default-args» (to "default-args") ;; «.best-lrcd» (to "best-lrcd") ;; «.ee-kl-r-c-d» (to "ee-kl-r-c-d") ;; «.shorter-fnames» (to "shorter-fnames") ;; «.generate-sexps» (to "generate-sexps") ;; «.ee-kl-kill» (to "ee-kl-kill") ;; «.kill-sexps» (to "kill-sexps") ;; «.ee-kl-insert» (to "ee-kl-insert") ;; «.eekla2» (to "eekla2") ;; «.aliases» (to "aliases") ;;; Commentary: ;; «a-test» (to ".a-test") ;; Try this: ;; ;; M-x eekla ;; ;; You will see this message in the echo area: ;; ;; Copied to the kill ring: (find-eev "eev-kla.el" "a-test") ;; ;; Congratulations! You have just "killed a link to an anchor"! =) ;; What happened in this test was that `eekla' has generated a link to ;; the anchor above and "killed it", in the sense of "copied it to the ;; kill ring"... ;; ;; Most of the docs about eev-kla.el are in this intro: ;; ;; http://anggtwu.net/eev-intros/find-kla-intro.html ;; (find-kla-intro) ;; ;; but some tests can't be run from the intro because they need to be ;; run from a file "in which the conversion c,d<-filename works"; this ;; is explained in details in the intro. ;; ;; Let's see an example: this ;; ;; (find-eev "eev-kla.el" "tests") ;; ;; is a hyperlink to this file, and to the anchor above - try it! The ;; function `find-eev' was defined by a call to `code-c-d' like the ;; one below, ;; ;; ;; (find-code-c-d "eev" ee-eev-source-directory :anchor) ;; (code-c-d "eev" ee-eev-source-directory :anchor) ;; ;; that was run from this module of eev: ;; ;; (find-eev "eev-code.el" "code-c-d-s") ;; ;; That `code-c-d' defined the function `find-eev' in the "right" way, ;; and added an entry for "eev" in `ee-code-c-d-pairs'. You can see ;; that entry by running this, ;; ;; (find-eppp ee-code-c-d-pairs "\"eev\"") ;; ;; or by running ;; ;; (find-kla-links) ;; ;; and exploring the sexps in the temporary buffer that ;; `find-kla-links' generates. ;; «more-tests» (to ".more-tests") ;; Now try: ;; ;; M-x eeklf ;; M-x eeklt ;; ;; You should get these messages in the echo area: ;; ;; Copied to the kill ring: (find-eevfile "eev-kla.el") ;; Copied to the kill ring: (to "more-tests") ;; ;; The results of the next tests will depend on what is in the region. ;; If you mark this "foo" - without the quotes - and try ;; ;; M-x eeklas ;; M-x eeklfs ;; M-x eeklts ;; ;; you will get these messages in the echo area: ;; ;; Copied to the kill ring: (find-eev "eev-kla.el" "more-tests" "foo") ;; Copied to the kill ring: (find-eevfile "eev-kla.el" "foo") ;; Copied to the kill ring: (to "more-tests" "foo") ;; «test-elsewhere» (to ".test-elsewhere") ;; Now try to create a link to another file. Run this to open one of ;; the files in the Emacs sources, and to go to the first occurrence ;; of the string "build specific" in it: ;; ;; (find-efile "comint.el" "build specific") ;; (eek "<up> M-3 M-e") ;; ;; Then mark the string "build specific", and run: ;; ;; M-x eeklf ;; M-x eeklfs ;; ;; You should get these messages in the echo area: ;; ;; Copied to the kill ring: (find-efile "comint.el") ;; Copied to the kill ring: (find-efile "comint.el" "build specific") ;; «the-video» (to ".the-video") ;; I recorded a video about this for the EmacsConf2022. ;; The video is here: ;; (find-eev2022klavideo "0:00") ;; and the page about it is here: ;; http://anggtwu.net/emacsconf2022-kla.html ;;; _ _ _ ;;; ___ ___ | | _| | _____ ___ __ __ _ _ __ __| | ;;; / _ \/ _ \_____| |/ / |_____ / _ \ \/ / '_ \ / _` | '_ \ / _` | ;;; | __/ __/_____| <| |_____| __/> <| |_) | (_| | | | | (_| | ;;; \___|\___| |_|\_\_| \___/_/\_\ .__/ \__,_|_| |_|\__,_| ;;; |_| ;; «ee-kl-expand» (to ".ee-kl-expand") ;; See: (find-kla-intro "15. Symlinks") ;; (defvar ee-kl-transforms nil "Set this if you need to support symlinks in eev-kla.el. The value of this variable should be a list of pairs of this form: (regexp replacement).") (defun ee-kl-transform (fname) "Transform FNAME into a canonical form using regexps. For each pair (regexp replacement) in `ee-kl-transforms' this function replaces all occurrences of the regexp in FNAME by the corresponding replacement." (cl-loop for (regexp repl) in ee-kl-transforms do (setq fname (replace-regexp-in-string regexp repl fname))) fname) (defun ee-kl-expand (fname) "Expand FNAME using `ee-expand'. This function also runs `ee-kl-transform' on the result, but `ee-kl-transform' is usually a no-op." (ee-kl-transform (ee-expand fname))) ;;; ____ __ _ _ ;;; | _ \ ___ / _| __ _ _ _| | |_ __ _ _ __ __ _ ___ ;;; | | | |/ _ \ |_ / _` | | | | | __| / _` | '__/ _` / __| ;;; | |_| | __/ _| (_| | |_| | | |_ | (_| | | | (_| \__ \ ;;; |____/ \___|_| \__,_|\__,_|_|\__| \__,_|_| \__, |___/ ;;; |___/ ;; «default-args» (to ".default-args") ;; See: (find-kla-intro "9. `cl-defun'") ;; (find-kla-intro "10. The default `c', `d', and `r'") (defun ee-kl-fname () (or (buffer-file-name) default-directory)) (defun ee-kl-anchor () (ee-preceding-tag-flash)) (defun ee-kl-region () (buffer-substring-no-properties (point) (mark))) ;; Used in: (find-eev "eev-kl-here.el" "kl") ;; Test: (ee-kl-line) ;; (defun ee-kl-line () (interactive "P") (let* ((start (ee-bol-skip-invisible)) (end (ee-eol-skip-invisible)) (str0 (buffer-substring start end)) (str (ee-no-properties str0))) (eeflash+ start end eeflash-copy) str)) ;;; ____ _ _ _ ;;; | __ ) ___ ___| |_ | | _ __ ___ __| | ;;; | _ \ / _ \/ __| __| | |_____| '__|____ / __|____ / _` | ;;; | |_) | __/\__ \ |_ | |_____| | |_____| (_|_____| (_| | ;;; |____/ \___||___/\__| |_| |_| \___| \__,_| ;;; ;; «best-lrcd» (to ".best-lrcd") ;; These functions try to choose the "best" `c-d' for a filename. They ;; filter `ee-code-c-d-pairs' to find all the `c-d's that "match" that ;; filename, then they choose the best one, and they return it ;; converted to an `l-r-c-d'. The ideas and the terminology are ;; explained here: ;; (find-kla-intro "7. The best `l-r-c-d'") ;; ;; Tests: (find-eppp (ee-kl-cds)) ;; (find-eppp (ee-kl-lrcds)) ;; (ee-kl-lrcd) (defun ee-kl-prefixp (prefix str) "If STR starts with PREFIX then return STR minus that prefix. When STR doesn't start with PREFIX, return nil." (and (<= (length prefix) (length str)) (equal prefix (substring str 0 (length prefix))) (substring str (length prefix)))) (defun ee-kl-cds () "Return a copy of `ee-code-c-d-pairs' with all `d's ee-kl-expanded." (cl-loop for (c d) in ee-code-c-d-pairs collect (list c (ee-kl-expand d)))) (cl-defun ee-kl-lrcds (&key fname) "Return all the `c-d's in (ee-kl-cds) that match FNAME. Each matching `c-d' is converted to an `l-r-c-d'." (setq fname (or fname (ee-kl-fname))) (cl-loop for (c d) in (ee-kl-cds) if (ee-kl-prefixp d fname) collect (let* ((r (ee-kl-prefixp d fname)) (l (length r))) (list l r c d)))) (cl-defun ee-kl-lrcd (&key fname) "Return the best lrcd in (ee-kl-lrcds FNAME). If (ee-kl-lrcds FNAME) doesn't return any matching `lrcd's, return nil." (setq fname (or fname (ee-kl-fname))) (let* ((lrcds (ee-kl-lrcds :fname fname)) (l< (lambda (lrcd1 lrcd2) (< (car lrcd1) (car lrcd2)))) (lrcds-sorted (sort lrcds l<))) (car lrcds-sorted))) ;;; ____ __ _ _ _ ;;; | _ \ ___ / _| __ _ _ _| | |_ _ __ ___ __| | ;;; | | | |/ _ \ |_ / _` | | | | | __| | '__| / __| / _` | ;;; | |_| | __/ _| (_| | |_| | | |_ | | _ | (__ _ | (_| | ;;; |____/ \___|_| \__,_|\__,_|_|\__| |_|( ) \___( ) \__,_| ;;; |/ |/ ;; «ee-kl-r-c-d» (to ".ee-kl-r-c-d") ;; See: (find-kla-intro "10. The default `c', `d', and `r'") ;; Tests: (ee-kl-r) ;; (ee-kl-c) ;; (ee-kl-d) (cl-defun ee-kl-r (&key fname) (setq fname (or fname (ee-kl-fname))) (nth 1 (ee-kl-lrcd :fname fname))) (cl-defun ee-kl-c (&key fname) (setq fname (or fname (ee-kl-fname))) (nth 2 (ee-kl-lrcd :fname fname))) (cl-defun ee-kl-d (&key fname) (setq fname (or fname (ee-kl-fname))) (nth 3 (ee-kl-lrcd :fname fname))) ;; «shorter-fnames» (to ".shorter-fnames") ;; See: (find-kla-intro "6. The components") ;; (find-kla-intro "6. The components" "living fossils") ;; (cl-defun ee-kl-shortfname (&key fname c r) (setq fname (or fname (ee-kl-fname)) r (or r (ee-kl-r :fname fname))) r) (cl-defun ee-kl-shorterfname (&key fname c r) (setq fname (or fname (ee-kl-fname)) r (or r (ee-kl-r :fname fname))) r) ;;; ____ ;;; / ___| _____ ___ __ ___ ;;; \___ \ / _ \ \/ / '_ \/ __| ;;; ___) | __/> <| |_) \__ \ ;;; |____/ \___/_/\_\ .__/|___/ ;;; |_| ;; ;; «generate-sexps» (to ".generate-sexps") ;; Functions that generate sexps. Tests: ;; (ee-kl-find-c) ;; (ee-kl-find-cfile) ;; (ee-kl-sexp-kla) ;; (ee-kl-sexp-klas :region "foo") ;; (ee-kl-sexp-klf) ;; (ee-kl-sexp-klfs :region "foo") ;; See also: ;; (find-kla-intro "12. The functions that generate sexps") ;; (cl-defun ee-kl-find-c (&key fname c) "Generate a symbol of the form find-{c}." (setq fname (or fname (ee-kl-fname)) c (or c (ee-kl-c :fname fname))) (intern (format "find-%s" c))) (cl-defun ee-kl-find-cfile (&key fname c) "Generate a symbol of the form find-{c}file." (setq fname (or fname (ee-kl-fname)) c (or c (ee-kl-c :fname fname))) (intern (format "find-%sfile" c))) (cl-defun ee-kl-sexp-kla (&key fname c r anchor) "<K>ill <l>ink to <a>nchor - make sexp." (setq fname (or fname (ee-kl-fname)) c (or c (ee-kl-c :fname fname)) r (or r (ee-kl-r :fname fname)) anchor (or anchor (ee-kl-anchor))) (list (ee-kl-find-c :fname fname :c c) (ee-kl-shorterfname :fname fname :c c :r r) anchor)) (cl-defun ee-kl-sexp-kla0 (&key fname c r anchor) "<K>ill <l>ink to <a>nchor, without the anchor - make sexp." (setq fname (or fname (ee-kl-fname)) c (or c (ee-kl-c :fname fname)) r (or r (ee-kl-r :fname fname))) (list (ee-kl-find-c :fname fname :c c) (ee-kl-shorterfname :fname fname :c c :r r))) (cl-defun ee-kl-sexp-klas (&key fname c r anchor region) "<K>ill <l>ink to <a>nchor and <s>tring - make sexp." (setq fname (or fname (ee-kl-fname)) c (or c (ee-kl-c :fname fname)) r (or r (ee-kl-r :fname fname)) anchor (or anchor (ee-kl-anchor)) region (or region (ee-kl-region))) (list (ee-kl-find-c :fname fname :c c) (ee-kl-shorterfname :fname fname :c c :r r) anchor region)) (cl-defun ee-kl-sexp-klf (&key fname c r) "<K>ill <l>ink to <f>ile - make sexp." (setq fname (or fname (ee-kl-fname)) c (or c (ee-kl-c :fname fname)) r (or r (ee-kl-r :fname fname))) (list (ee-kl-find-cfile :fname fname :c c) (ee-kl-shortfname :fname fname :c c :r r))) (cl-defun ee-kl-sexp-klfs (&key fname c r region) "<K>ill <l>ink to <f>ile and <s>tring - make sexp." (setq fname (or fname (ee-kl-fname)) c (or c (ee-kl-c :fname fname)) r (or r (ee-kl-r :fname fname)) region (or region (ee-kl-region))) (list (ee-kl-find-cfile :fname fname :c c) (ee-kl-shortfname :fname fname :c c :r r) region)) (cl-defun ee-kl-sexp-klt (&key anchor) "<K>ill <l>ink to a (<t>o ...) - make sexp." (setq anchor (or anchor (ee-kl-anchor))) (list 'to anchor)) (cl-defun ee-kl-sexp-klts (&key anchor region) "<K>ill <l>ink to a (<t>o ... ...) - make sexp." (setq anchor (or anchor (ee-kl-anchor)) region (or region (ee-kl-region))) (list 'to anchor region)) ;;; _ _ _ _ _ _ ;;; ___ ___ | | _| | | | _(_) | | ;;; / _ \/ _ \_____| |/ / |_____| |/ / | | | ;;; | __/ __/_____| <| |_____| <| | | | ;;; \___|\___| |_|\_\_| |_|\_\_|_|_| ;;; ;; «ee-kl-kill» (to ".ee-kl-kill") ;; See: (find-kla-intro "13. Killing and inserting") ;; Tests: (ee-kl-link-to-string "(foo)\n") ;; (ee-kl-link-to-string '(foo)) (defun ee-kl-kill (link) "Kill LINK and show a message. Here \"kill\" means \"put it in the kill ring.\"" (setq link (ee-kl-link-to-string link)) (let ((link0 (replace-regexp-in-string "\n$" "" link))) (kill-new link) (message "Copied to the kill ring: %s" link0))) (defun ee-kl-link-to-string (link) "Convert LINK to a string using `ee-S'. If LINK is already a string, return it unchanged. If LINK is a sexp, convert it to a string with `ee-S' and append a newline to it." (if (stringp link) link (concat (ee-S link) "\n"))) ;;; _ ___ _ _ ;;; | |/ (_) | |___ ;;; | ' /| | | / __| ;;; | . \| | | \__ \ ;;; |_|\_\_|_|_|___/ ;;; ;; «kill-sexps» (to ".kill-sexps") ;; Commands that push sexps into the kill ring. ;; (defun eekla () "<K>ill <L>ink to <A>nchor. Put in the kill ring a link to the preceding anchor." (interactive) (ee-kl-kill (ee-kl-sexp-kla))) (defun eekla0 () "<K>ill <L>ink to <A>nchor, without the anchor. Put in the kill ring a shortened link to the file." (interactive) (ee-kl-kill (ee-kl-sexp-kla0))) (defun eeklas () "<K>ill <L>ink to <A>nchor and <S>tring. Put in the kill ring a link to the preceding anchor." (interactive) (ee-kl-kill (ee-kl-sexp-klas))) (defun eeklf () "<K>ill <L>ink to <F>ile." (interactive) (ee-kl-kill (ee-kl-sexp-klf))) (defun eeklfs () "<K>ill <L>ink to <F>ile and <S>tring." (interactive) (ee-kl-kill (ee-kl-sexp-klfs))) (defun eeklt () "<K>ill <L>ink to a (<T>o ...)." (interactive) (ee-kl-kill (ee-kl-sexp-klt))) (defun eeklts () "<K>ill <L>ink to a (<T>o ... ...)." (interactive) (ee-kl-kill (ee-kl-sexp-klts))) ;;; _ _ _ _ ;;; ___ ___ | | _| | (_)_ __ ___ ___ _ __| |_ ;;; / _ \/ _ \_____| |/ / |_____| | '_ \/ __|/ _ \ '__| __| ;;; | __/ __/_____| <| |_____| | | | \__ \ __/ | | |_ ;;; \___|\___| |_|\_\_| |_|_| |_|___/\___|_| \__| ;;; ;; «ee-kl-insert» (to ".ee-kl-insert") ;; See: (find-kla-intro "13. Killing and inserting") ;; Tests: (ee-kl-comment-prefix) ;; (ee-kl-insert "(foo)\n") ;; (defun ee-kl-comment-prefix (&optional mode) "This a quick hack. Override it to add support for more languages." (let ((plist '(emacs-lisp-mode ";; " haskell-mode "-- " lua-mode "-- " python-mode "# " agda2-mode "-- " latex-mode "%% "))) (plist-get plist (or mode major-mode)))) (defun ee-kl-link-to-string-with-comment (link) (concat (or (ee-kl-comment-prefix) "# ") (ee-kl-link-to-string link))) (defun ee-kl-insert (&optional link) "Insert (ee-kl-comment-prefix) and then LINK." (interactive) (insert (ee-kl-link-to-string-with-comment (car kill-ring)))) ;;; _ _ ____ ;;; ___ ___| | _| | __ _|___ \ ;;; / _ \/ _ \ |/ / |/ _` | __) | ;;; | __/ __/ <| | (_| |/ __/ ;;; \___|\___|_|\_\_|\__,_|_____| ;;; ;; «eekla2» (to ".eekla2") ;; See: (find-kla-intro "14. Bidirectional hyperlinks") ;; (find-eev2022klavideo "06:07") ;; Based on: ;; (find-eev "eev-flash.el" "specs") ;; (find-eev "eev-tlinks.el" "ee-copy-rest" "eeflash-copy") ;; but lasts longer. ;; (defvar ee-kla2-flash-spec '(highlight 2.0)) (defun ee-kla2-flash (pos1 pos2) "Highlight the region between POS1 and POS2 using `ee-kla2-flash-spec'." (eeflash pos1 (point) ee-kla2-flash-spec)) (defun ee-kla2-goto-bol () "Move to the beginning of the line. When not at BOL, move to the beginning of the next line." (when (not (= (ee-bol) (point))) ; when not at bol (move-beginning-of-line 2)) ; do <down> C-a (point)) (defun ee-kla2-insert (link) "Move to the beginning of the line, insert LINK, and highlight it." (let* ((line (ee-kl-link-to-string-with-comment link)) (pos-before-line (ee-kla2-goto-bol))) (insert line) (ee-kla2-flash pos-before-line (point)))) (defun eekla2 () "Insert a link \"to here\" \"there\" and a link \"to there\" \"here\"." (interactive) (let* ((link1 (ee-kl-sexp-kla)) (link2 (prog2 (other-window 1) (ee-kl-sexp-kla) (other-window -1)))) (ee-kla2-insert link2) (other-window 1) (ee-kla2-insert link1) (other-window -1))) ;;; _ _ _ ;;; / \ | (_) __ _ ___ ___ ___ ;;; / _ \ | | |/ _` / __|/ _ \/ __| ;;; / ___ \| | | (_| \__ \ __/\__ \ ;;; /_/ \_\_|_|\__,_|___/\___||___/ ;;; ;; «aliases» (to ".aliases") ;; See: (find-kla-intro "4. Aliases") ;; I use these aliases: ;; (defalias 'kla 'eekla) ;; (defalias 'kla0 'eekla0) ;; (defalias 'klas 'eeklas) ;; (defalias 'klf 'eeklf) ;; (defalias 'klfs 'eeklfs) ;; (defalias 'klt 'eeklt) ;; (defalias 'klts 'eeklts) ;; (defalias 'kli 'ee-kl-insert) ;; (defalias 'kla2 'eekla2) (provide 'eev-kla) ;; Local Variables: ;; coding: utf-8-unix ;; no-byte-compile: t ;; End: