summaryrefslogtreecommitdiff
path: root/lisp/garden-preview.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/garden-preview.el')
-rw-r--r--lisp/garden-preview.el111
1 files changed, 111 insertions, 0 deletions
diff --git a/lisp/garden-preview.el b/lisp/garden-preview.el
new file mode 100644
index 0000000..f7f89a0
--- /dev/null
+++ b/lisp/garden-preview.el
@@ -0,0 +1,111 @@
+;;; garden-preview.el --- side-window note preview -*- lexical-binding: t; -*-
+
+;;; Commentary:
+
+;; Shows garden notes in a read-only side window on the right. Two
+;; entry points: `garden-preview-show' for one-off peeks (wired to
+;; per-row eye buttons), and `garden-preview-mode', a buffer-local
+;; minor mode for the VUI dashboards that previews the note under
+;; point as you move around. Buffers opened purely for previewing
+;; are killed again by `garden-preview-quit'.
+
+;;; Code:
+
+(require 'garden-core)
+(require 'cl-lib)
+
+(declare-function vui-element-at "vui")
+(declare-function vui-element-get "vui")
+
+(defcustom garden-preview-width 45
+ "Width of the preview side window in columns."
+ :type 'integer :group 'garden)
+
+(defvar garden-preview--opened nil
+ "Buffers the preview opened itself and may kill on quit.")
+
+(defvar-local garden-preview--owned nil
+ "Non-nil when this buffer was created by `garden-preview-show'.")
+
+(defvar garden-preview--last nil
+ "File shown in the preview window most recently.")
+
+(defun garden-preview-show (file)
+ "Display FILE read-only in a side window on the right.
+Focus returns to the window that was selected before the call."
+ (interactive "fPreview note: ")
+ (let* ((existing (find-buffer-visiting file))
+ (buf (or existing (find-file-noselect file)))
+ (origin (selected-window)))
+ (if existing
+ (progn
+ (with-current-buffer buf (setq garden-preview--owned nil))
+ (setq garden-preview--opened (delq buf garden-preview--opened)))
+ (with-current-buffer buf
+ (setq garden-preview--owned t)
+ (cl-pushnew buf garden-preview--opened)))
+ (setq garden-preview--last file)
+ (with-current-buffer buf (setq buffer-read-only t))
+ (or (with-selected-window (window-main-window)
+ (display-buffer-in-side-window
+ buf
+ `((side . right) (slot . 0) (window-width . ,garden-preview-width)
+ (window-parameters . ((no-delete-other-windows . t))))))
+ (display-buffer buf '((display-buffer-reuse-window display-buffer-use-some-window)
+ (inhibit-same-window . t))))
+ (select-window origin)))
+
+(defun garden-preview-quit ()
+ "Close the preview window and kill buffers opened for previewing.
+Only buffers that the preview itself created are killed."
+ (interactive)
+ (when-let* ((buf (and garden-preview--last (find-buffer-visiting garden-preview--last)))
+ (win (get-buffer-window buf)))
+ (when (window-parameter win 'window-side)
+ (delete-window win)))
+ (dolist (buf garden-preview--opened)
+ (when (and (buffer-live-p buf)
+ (buffer-local-value 'garden-preview--owned buf)
+ (not (buffer-modified-p buf))
+ (not (get-buffer-window buf 'visible)))
+ (kill-buffer buf)))
+ (setq garden-preview--opened nil
+ garden-preview--last nil))
+
+(defun garden-preview--file-at-line ()
+ "Return the note file referenced by a VUI element on the current line, if any.
+Works with both `button.el' text buttons (vui 1.3+) and widget fields."
+ (save-excursion
+ (let ((end (line-end-position))
+ (file nil))
+ (beginning-of-line)
+ (while (and (not file) (< (point) end))
+ (let* ((elt (vui-element-at (point)))
+ (echo (and elt (vui-element-get elt 'help-echo))))
+ (when (and (stringp echo)
+ (string-suffix-p ".org" echo)
+ (file-exists-p echo))
+ (setq file echo)))
+ (forward-char 1))
+ file)))
+
+(defun garden-preview--post-command ()
+ "Preview the note under point when it changed."
+ (when-let* ((file (garden-preview--file-at-line)))
+ (unless (equal file garden-preview--last)
+ (garden-preview-show file))))
+
+;;;###autoload
+(define-minor-mode garden-preview-mode
+ "Preview the note under point in a right side window.
+Buffer-local; meant for the garden VUI dashboards."
+ :lighter " peek"
+ (if garden-preview-mode
+ (progn
+ (add-hook 'post-command-hook #'garden-preview--post-command nil t)
+ (garden-preview--post-command))
+ (remove-hook 'post-command-hook #'garden-preview--post-command t)
+ (garden-preview-quit)))
+
+(provide 'garden-preview)
+;;; garden-preview.el ends here