Skip to content

Latest commit

 

History

History
524 lines (418 loc) · 14.7 KB

org-link-minor-mode.org

File metadata and controls

524 lines (418 loc) · 14.7 KB

org-link-minor-mode

Header

The header has the following structure:

;;; <<title-line>>
;;
;; <<copyright>>
;;
;; <<package-info>>
;;
;; <<gnu-std-header>>
;;
;;; Commentary:
;;
;; <<commentary>>
;;
;;; Code:

Title line

The title line is the first line in the file and shows the name of the file and a short description. It also turns on {{{var(lexical-binding)}}}.

org-link-minor-mode.el --- Enable org-mode links in non-org modes -*- lexical-binding: t; -*-

Metadata

Copyright

Copyright (C) 2012-2020

Email

Sean O'Halpin <sean.ohalpin@gmail.com>

Dates

Created

20120825

Modified

(format-time-string "%Y%m%d")

Version

0.0.3

Package-Version

(format-time-string "%Y%m%d.%H%M")

Package info

Specify the package metadata and that it requires at least Org 8 and Emacs 24.3 (for lexical-binding and setq-local):

Author: <<email>>
Maintainer: <<email>>
Created: <<created>>
Modified: <<modified()>>
Version: <<version>>
Package-Requires: ((emacs "24.3"))
Package-Version: <<package-version()>>
Keywords: hypermedia
Url: https://github.com/seanohalpin/org-link-minor-mode

Changes for org v9: Stefan-W. Hahn <stefan dot hahn at s-hahn dot de>

GNU GPL 3 comment

All files submitted to ELPA should contain the standard GNU GPL 3 comment:

This file 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, or (at your option)
any later version.

This file 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 this program.  If not, see <http://www.gnu.org/licenses/>.

Commentary

A brief explanation of the package:

Enables org-mode links of the form:

  http://www.bbc.co.uk
  man:emacs
  info:emacs
  <http://www.bbc.co.uk>
  [[http://www.bbc.co.uk][BBC]]
  [[org-link-minor-mode]]
  [2012-08-18]
  <2012-08-18>

Note that `org-toggle-link-display' will also work when this mode
is enabled.

Code

Require org-mode

We require org-element to avoid the error

Error during redisplay: (jit-lock-function 1) signaled (void-variable org-element-paragraph-separate)
(require 'org-element)

Declarations for byte-code compiler

;; Following declarations are necessary to make the byte compiler happy.

;; For org v8 compatibility (if used with org v9)
(declare-function org-activate-plain-links "org" (limit))
(declare-function org-activate-angle-links "org" (limit))
(declare-function org-activate-bracket-links "org" (limit))
(declare-function org-decompose-region "org-compat" (beg end))

;; For org v9 compatibility (if used with org v8)
(declare-function org-activate-links "org" (limit))
(declare-function org-activate-dates "org" (limit))

Using the {{{macro(define-minor-mode)}}} macro

The simplest way to define a new minor mode is to use the {{{fn(define-minor-mode)}}} macro:

;;;###autoload
(define-minor-mode org-link-minor-mode
  "Toggle display of org-mode style links in non-org-mode buffers."
  :lighter " org-link"
  :keymap org-link-minor-mode-map
  <<body>>)

The {{{kw(autoload)}}} magic comment is used by emacs at build time to add the {{{fn(org-link-minor-mode)}}} function to {{{file(loaddefs.el)}}}.

The {{{kw(:lighter)}}} keyword parameter defines what appears in the mode line.

The body

(let ((lk org-highlight-links)
      org-link-minor-mode-keywords)
  <<set-up-font-lock-keywords>>
  <<enter-exit-mode>>)

We start the body by setting up the font lock keywords in the variable {{{var(org-link-minor-mode-keywords)}}}, using org-mode’s own {{{fn(org-activate-xxx)}}} functions to do the heavy lifting.

Note: we do this before entering the mode so the definition of {{{var(org-link-minor-mode-keywords)}}} is available for when we want to leave the mode.

(if (fboundp 'org-activate-links)
    ;; from Org v9.2
    (setq org-link-minor-mode-keywords
          (list
           '(org-activate-links)
           (when (memq 'tag lk) '(org-activate-tags (1 'org-tag prepend)))
           (when (memq 'radio lk) '(org-activate-target-links (1 'org-link t)))
           (when (memq 'date lk) '(org-activate-dates (0 'org-date t)))
           (when (memq 'footnote lk) '(org-activate-footnote-links))))
  (setq org-link-minor-mode-keywords
        (list
         (when (memq 'tag lk) '(org-activate-tags (1 'org-tag prepend)))
         (when (memq 'angle lk) '(org-activate-angle-links (0 'org-link t)))
         (when (memq 'plain lk) '(org-activate-plain-links (0 'org-link t)))
         (when (memq 'bracket lk) '(org-activate-bracket-links (0 'org-link t)))
         (when (memq 'radio lk) '(org-activate-target-links (0 'org-link t)))
         (when (memq 'date lk) '(org-activate-dates (0 'org-date t)))
         (when (memq 'footnote lk) '(org-activate-footnote-links)))))

We then branch depending on whether we’re entering or exiting the mode:

(if org-link-minor-mode
    <<enter-minor-mode>>
  <<exit-minor-mode>>)

Entering the minor mode

If we’re already in {{{mode(org-mode)}}}, display a message and switch {{{mode(org-link-minor-mode)}}} off. We need to do it this way as by this point we’ve already entered the minor mode ({{{mode(org-link-minor-mode)}}} is {{{const(t))}}}:

(if (derived-mode-p 'org-mode)
    (progn
      (message "org-mode doesn't need org-link-minor-mode")
      (org-link-minor-mode -1))
  <<enter-minor-mode-body>>)

Minor mode body

When we enter the minor mode, we

  1. add the font lock keywords
  2. define the keymap that will be active within links
  3. set the unfontify function
  4. set up {{{var(org-descriptive-links)}}}
  5. turn on link display

Turning on org-link highlighting

Add the font-lock specification:

(font-lock-add-keywords nil org-link-minor-mode-keywords t)

Org mode associates the {{{var(org-mouse-map)}}} keymap with links. This becomes active when the mouse or point is over a link.

We can use this to enable {{{key(return)}}} to follow link (and {{{key(tab)}}} to next link, {{{key(backtab)}}} to previous, etc.):

(kill-local-variable 'org-mouse-map)
(setq-local org-mouse-map
            (let ((map (make-sparse-keymap)))
              (define-key map [return] 'org-open-at-point)
              (define-key map [tab] 'org-next-link)
              (define-key map [backtab] 'org-previous-link)
              (define-key map [mouse-2] 'org-open-at-point)
              (define-key map [follow-link] 'mouse-face)
              map))

Reusing {{{var(org-mouse-map)}}} like this is a hack. This keymap is set as a text property of links in {{{fn(org-activate-links)}}}, etc. so it’s simpler to co-opt it than to replace those functions.

Make {{{var(org-descriptive-links)}}} local, turning it off. We will switch descriptive links on when we call {{{fn(org-toggle-link-display)}}} below.

(setq-local org-descriptive-links nil)

This is the magic that makes the link body appear if you backspace into it (or use replace to make it no longer a link):

(setq-local font-lock-unfontify-region-function
            'org-link-minor-mode--unfontify-region)

The documentation for {{{fn(font-lock-unfontify-region-function)}}} is a bit sparse but reading {{{fn(org-unfontify-region)}}} at least shows you what it should do.

Finally, we refontify the buffer using {{{fn(org-toggle-link-display)}}}. This will reset {{{var(org-descriptive-links)}}} to {{{const(t)}}}.

(org-toggle-link-display)

Exiting the minor mode

Again, we don’t run this code if we’re already in org-mode:

(unless (derived-mode-p 'org-mode)
  <<exit-minor-mode-body>>)

Remove all org-link font-lock properties:

(font-lock-remove-keywords nil org-link-minor-mode-keywords)

Turn off link display:

(setq org-descriptive-links t)
(org-toggle-link-display)

Kill the local variables:

(kill-local-variable 'org-descriptive-links)
(kill-local-variable 'org-mouse-map)
(kill-local-variable 'font-lock-unfontify-region-function)

Remove text properties

{{{fn(org-unfontify-region)}}} does not remove the {{{prop(help-echo)}}}, {{{prop(htmlize-link)}}} and {{{prop(rear-nonsticky)}}} properties, so I’ve copied that function and added those in.

Note: This looks like a bug in {{{fn(org-unfontify-region)}}}.

We can ignore the {{{prop(fontified)}}} property as that belongs to {{{mode(font-lock-mode)}}}.

(defun org-link-minor-mode--unfontify-region (beg end)
  "Remove org-link fontification between BEG and END."
  (font-lock-default-unfontify-region beg end)
  (let* ((buffer-undo-list t)
         (inhibit-read-only t) (inhibit-point-motion-hooks t)
         (inhibit-modification-hooks t)
         deactivate-mark buffer-file-name buffer-file-truename)
    (if (fboundp 'org-decompose-region)
        (org-decompose-region beg end)
      (decompose-region beg end))
    (remove-text-properties beg end
                            '(mouse-face t keymap t org-linked-text t
                                         invisible t intangible t
                                         help-echo t rear-nonsticky t
                                         htmlize-link t
                                         org-no-flyspell t org-emphasis t))
    (org-remove-font-lock-display-properties beg end)))

Note that org-link-minor-mode-unfontify-region is obsolete.

(defalias 'org-link-minor-mode-unfontify-region 'org-link-minor-mode--unfontify-region)
(make-obsolete 'org-link-minor-mode-unfontify-region 'org-link-minor-mode--unfontify-region
               "org-link-minor-mode 0.0.3")

The keymap is defined in case you want to define your own key bindings for this mode. It is not used by {{{mode(org-link-minor-mode)}}} itself.

(defvar org-link-minor-mode-map (make-sparse-keymap)
  "Local keymap.")
(make-variable-buffer-local 'org-link-minor-mode-map)

Provide

Finally, we add the {{{fn(provide)}}} feature clause so that we can later {{{code((require ‘org-link-minor-mode))}}}.

(provide 'org-link-minor-mode)

Complete source

Here is the complete source:

<<header>>

<<requires>>

<<declarations-for-byte-code>>

<<org-link-minor-mode--unfontify-region>>

<<keymap>>

<<define-minor-mode>>

<<provide>>
;;; org-link-minor-mode.el ends here