summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Parri <simonparri@ganzeria.com>2025-09-02 09:40:32 -0500
committerSimon Parri <simonparri@ganzeria.com>2025-09-02 09:40:32 -0500
commit72927bf3ec66eb7f895aeee6cc6b2ca06aa12f55 (patch)
treef02c5686a5ef03edefc41751b59addae325783dc
downloadol-file-paged-72927bf3ec66eb7f895aeee6cc6b2ca06aa12f55.tar.gz
ol-file-paged-72927bf3ec66eb7f895aeee6cc6b2ca06aa12f55.zip
Add version 0.50HEADv0.50master
-rw-r--r--.gitignore1
-rw-r--r--README.org9
-rw-r--r--ol-file-paged.el151
3 files changed, 161 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b25c15b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*~
diff --git a/README.org b/README.org
new file mode 100644
index 0000000..632253b
--- /dev/null
+++ b/README.org
@@ -0,0 +1,9 @@
+#+TITLE: ol-file-paged
+#+SUBTITLE: Org file links with associated page number
+
+* =ol-file-paged=
+/Org file links with associated page number./
+
+=ol-file-paged= introduces a new type of link to Org-mode. It is a =file:= link that also encodes a page number, making it suitable for referring to paged media such as PDFs. It currently integrates with the built-in =doc-view-mode= and =pdf-tools=. Please read the package’s description and function docstrings to learn how to use it.
+
+*Warning: This project is still version < 1.0, and is therefore /not guaranteed to be stable/. I maintain it for myself, but do not have capacity to improve or test it beyond my needs.* If you find a bug, feel free to report it, but don’t expect there not to be any.
diff --git a/ol-file-paged.el b/ol-file-paged.el
new file mode 100644
index 0000000..ad473ec
--- /dev/null
+++ b/ol-file-paged.el
@@ -0,0 +1,151 @@
+;;; ol-file-paged.el --- Org file links with associated page number -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2025 Simon Parri
+
+;; Author: Simon Parri <simonparri@ganzeria.com>
+;; Keywords: multimedia, hypermedia, docs
+;; Package-Requires: ((emacs "24.4") (compat "28.1") (seq "1") (org "9.3"))
+;; Version: 0.50
+
+;; This program 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.
+
+;; This program 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 <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This package provides a link type, "file-paged", that includes both the
+;; name of the file and the page number.
+
+;; Example: [[file-paged:doc.pdf::10]] is a link to doc.pdf at page 10.
+
+;; ol-file-paged currently supports DocView and `pdf-tools'. To support more
+;; paged-media viewers, see `ol-file-paged-get-page-alist',
+;; `ol-file-paged-get-title-alist' and `ol-file-paged-goto-page-alist'.
+
+;;; Code:
+
+(require 'ol)
+
+(defun ol-file-paged--substrings (regexp string &optional start)
+ "Return the list of all grouops of REGEXP in STRING.
+
+Optional argument START causes search to start at that index in STRING."
+ (declare (side-effect-free t))
+ (save-match-data
+ (when (string-match regexp string start)
+ (mapcar
+ (lambda (x)
+ (if (and (car x) (cadr x))
+ (substring string (car x) (cadr x))
+ ""))
+ (seq-partition
+ (match-data) 2)))))
+
+(defun ol-file-paged--assoc (alist &optional buffer)
+ "Find the `cdr' of the element in ALIST whose key is BUFFER's major mode.
+
+If BUFFER is nil, use the current buffer."
+ (with-current-buffer (or buffer (current-buffer))
+ (letrec
+ ((f (lambda (mode)
+ (if-let ((fn (assoc mode alist)))
+ (cdr fn)
+ (when-let ((parent (get mode 'derived-mode-parent)))
+ (funcall f parent))))))
+ (funcall f major-mode))))
+
+(defvar ol-file-paged-get-page-alist
+ ;; FIXME: When doc-view and pdf-tools don't use these macros
+ ;; then change this code
+ '((doc-view-mode . (lambda () (doc-view-current-page)))
+ (pdf-view-mode . (lambda () (pdf-view-current-page))))
+ "Alist of (MAJOR-MODE . FUNCTION) pairs.
+
+Used by `ol-file-paged-get-page' to find the current page in a buffer in
+MAJOR-MODE. FUNCTION is called with no arguments in the buffer in
+question.")
+
+(defun ol-file-paged-get-page (&optional buffer)
+ "Get the current page of BUFFER.
+
+If BUFFER is nil, use the current buffer."
+ (with-current-buffer (or buffer (current-buffer))
+ (when-let ((f (ol-file-paged--assoc ol-file-paged-get-page-alist)))
+ (funcall f))))
+
+(defvar ol-file-paged-get-title-alist
+ '()
+ "Alist of (MAJOR-MODE . FUNCTION) pairs.
+
+Used by `ol-file-paged-get-title' to find the title of a buffer in
+MAJOR-MODE. FUNCTION is called with no arguments in the buffer in
+question.")
+
+(defun ol-file-paged-get-title (&optional buffer)
+ "Get the title of BUFFER.
+
+If BUFFER is nil, use the current buffer."
+ (with-current-buffer (or buffer (current-buffer))
+ (when-let ((f (ol-file-paged--assoc
+ ol-file-paged-get-title-alist)))
+ (funcall f))))
+
+;;;###autoload
+(defun ol-file-paged-store ()
+ "Store a link to the current file+page.
+
+Does nothing if `ol-file-paged-get-page' cannot determine the current
+page."
+ (when (apply #'derived-mode-p
+ (mapcar #'car ol-file-paged-get-page-alist))
+ (let ((page (ol-file-paged-get-page)))
+ (org-link-store-props
+ :type "file-paged"
+ :link (format "file-paged:%s::%d" buffer-file-name page)
+ :title (ol-file-paged-get-title)))))
+
+(defvar ol-file-paged-goto-page-alist
+ '((doc-view-mode . doc-view-goto-page)
+ (pdf-view-mode . pdf-view-goto-page))
+ "Alist of (MAJOR-MODE . FUNCTION) pairs.
+
+Used by `ol-file-paged-open' to jump to a given page in a buffer in
+MAJOR-MODE. FUNCTION is called with the page number in the buffer in
+question.")
+
+;;;###autoload
+(defun ol-file-paged-open (link &optional arg)
+ "Open LINK when LINK is an file-paged link.
+
+ARG is ignored."
+ (ignore arg)
+ (let* ((x (cdr
+ (ol-file-paged--substrings
+ "\\`\\(.+\\)::\\(.+\\)\\'"
+ link)))
+ (file (car x))
+ (page (cadr x)))
+ (org-open-file file t)
+ (with-current-buffer (find-buffer-visiting file)
+ (when-let ((f (ol-file-paged--assoc
+ ol-file-paged-goto-page-alist)))
+ (funcall f (string-to-number page))))))
+
+;;;###autoload
+(with-eval-after-load 'ol
+ (org-link-set-parameters
+ "file-paged"
+ :store #'ol-file-paged-store
+ :follow #'ol-file-paged-open))
+
+(provide 'ol-file-paged)
+;;; ol-file-paged.el ends here