;;; klink.el --- Implicit reference to a Koutline kcell -*- lexical-binding: t; -*- ;; ;; Author: Bob Weiner ;; ;; Orig-Date: 15-Nov-93 at 12:15:16 ;; Last-Mod: 17-Jun-23 at 16:50:34 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; ;; Copyright (C) 1993-2022 Free Software Foundation, Inc. ;; See the "../HY-COPY" file for license information. ;; ;; This file is part of GNU Hyperbole. ;;; Commentary: ;; ;;; link = ;; < pathname [, cell-ref] [, position] > ;; < @ cell-ref > ;; In same buffer ;; < journal-name, journal-item-number [, cell-ref] [, position] > ;; ;;; pathname = ;; path ;; display path in Emacs buffer ;; !path ;; execute pathname within a shell ;; &path ;; execute path as a windowed program ;; -path ;; Load as an Emacs Lisp program ;; ;;; cell-ref = ;; cell - 1a, 012, 1.2, 1a=012 (both relative and absolute ids separated ;; by an equal sign) ;; range - 1a-5c, 1a-+3 (include 3 cells past 1a) (not yet implemented) ;; tree - 1a+ (not yet implemented) ;; ;; optionally followed by a period and 1 or more relative position specs ;; (not yet implemented): ;; ;; previous-cell - .b ;; down-a-level - .d ;; end-of-branch - .e ;; follow-next-link - .l ;; return-to-prev-location - .r ;; return-to-prev-buffer - .rf ;; sibling - .s, .2s for 2 siblings forward ;; tail-of-tree - .t ;; up-a-level - .u ;; last char of cell - .f ;; ;; and then optionally followed by any amount of whitespace, a pipe `|' ;; character and then one or more view specification characters. (Augment ;; viewspec characters may be given instead, preceded by a colon. They are ;; ignored for now.) ;; ;;; position (relative to cell start) = (not yet implemented) ;; char-pos, e.g. 28 or C28 ;; word-num, e.g. W5 ;; line-num, e.g. L2 ;; paragraph-num, e.g. P3 ;; regexp-match, e.g. "regexp" ;; ;;; Code: ;;; ************************************************************************ ;;; Other required Elisp libraries ;;; ************************************************************************ (require 'subr-x) ;; For string-trim (require 'hmouse-tag) ;; For smart-c-include-regexp (eval-when-compile (require 'hbut)) ;; For defib. ;;; ************************************************************************ ;;; Public variables ;;; ************************************************************************ (defcustom klink:ignore-modes '(occur-mode moccur-mode amoccur-mode shell-mode telnet-mode ssh-mode term-mode) "Major modes in which to ignore potential klinks to avoid false positives." :type '(list function) :group 'hyperbole-koutliner) (defcustom klink:c-style-modes '(c-mode c++-mode objc-mode java-mode) "C-related major modes with where klinks appear only within comments." :type '(list function) :group 'hyperbole-koutliner) ;;; ************************************************************************ ;;; Public functions ;;; ************************************************************************ (defun klink:absolute (label-and-pos) "Return an absolute klink string from LABEL-AND-POS list. With point in a klink's source buffer and LABEL-AND-POS a list of (klink-label, klink-start, klink-end) including delimiters, return an absolute klink string. Klink returned is of the form: \"\". See documentation for `kcell:ref-to-id' for valid cell-ref formats." (when (and (derived-mode-p 'kotl-mode) label-and-pos (listp label-and-pos)) (let* ((file-and-cell-ref (klink:parse (car label-and-pos))) (file (or (car file-and-cell-ref) buffer-file-name)) (cell-ref (nth 1 file-and-cell-ref))) (klink:set-yank-handler (format "<%s, %s>" (expand-file-name file) cell-ref))))) ;;;###autoload (defun klink:create (reference) "Insert at point an implicit link 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 ;; Don't change the name or delete default-dir used here. It is referenced ;; in "hargs.el" for argument getting. (let ((default-dir default-directory)) (barf-if-buffer-read-only) (save-excursion (hargs:iform-read (list 'interactive "*+LInsert link to <[file,] cell-id [|vspecs]>: "))))) (barf-if-buffer-read-only) ;; 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. (and (stringp reference) (> (length reference) 0) (eq (aref reference 0) ?\() (setq reference (replace-regexp-in-string "\\\"" "" reference nil t))) (let ((default-dir default-directory) file-ref cell-ref) (setq reference (klink:parse reference) file-ref (car reference) cell-ref (nth 1 reference)) ;; Don't need filename if link is to a cell in current buffer. (when (and file-ref (equal 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)) ;; "./" prefix, if any. (if (string-match "^\\./" file-ref) (setq file-ref (substring file-ref (match-end 0)))) (insert "<" file-ref) (if cell-ref (insert ", " cell-ref)) (insert ">")) (cell-ref (insert "<@ " cell-ref ">")) (t (error "(klink:create) Invalid reference, `%s'" reference))))) ;;;###autoload (defun klink:at-p () "Return non-nil iff point is within a klink. See documentation for the `actypes::link-to-kotl' function for valid klink formats. Value returned is a list of: link-label, link-start-position, and link-end-position, (including delimiters)." (let (bol label-and-pos referent path) (when (and ;; Avoid false matches in certain modes. (not (memq major-mode klink:ignore-modes)) ;; If this is an OO-Browser listing buffer, ignore anything that ;; looks like a klink, e.g. a C++