From 815d0856d44b527c79a98ed4f61a72136686cb06 Mon Sep 17 00:00:00 2001 From: Ahmed Shariff Date: Fri, 2 Oct 2020 00:10:57 -0500 Subject: [PATCH 001/453] Adding modified pdf-util-tooltip-arrow for handling arrow position in x-axis --- org-noter.el | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/org-noter.el b/org-noter.el index 129d61c..1aa297a 100644 --- a/org-noter.el +++ b/org-noter.el @@ -51,7 +51,7 @@ (declare-function pdf-info-gettext "ext:pdf-info") (declare-function pdf-info-outline "ext:pdf-info") (declare-function pdf-info-pagelinks "ext:pdf-info") -(declare-function pdf-util-tooltip-arrow "ext:pdf-util") +;; (declare-function pdf-util-tooltip-arrow "ext:pdf-util") (declare-function pdf-view-active-region "ext:pdf-view") (declare-function pdf-view-active-region-p "ext:pdf-view") (declare-function pdf-view-active-region-text "ext:pdf-view") @@ -878,6 +878,51 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (setq event (read-event "Click where you want the start of the note to be!"))) (posn-point (event-start event))))))))) +(defun pdf-util-tooltip-arrow (image-top &optional timeout image-left) + (pdf-util-assert-pdf-window) + (when (floatp image-top) + (setq image-top + (round (* image-top (cdr (pdf-view-image-size)))))) + (when (floatp image-left) + (setq image-left + (round (* image-left (car (pdf-view-image-size)))))) + (let* (x-gtk-use-system-tooltips ;allow for display property in tooltip + (dx (if image-left + image-left + (+ (or (car (window-margins)) 0) + (car (window-fringes))))) + (dy image-top) + (pos (list dx dy dx (+ dy (* 2 (frame-char-height))))) + (vscroll + (pdf-util-required-vscroll pos)) + (tooltip-frame-parameters + `((border-width . 0) + (internal-border-width . 0) + ,@tooltip-frame-parameters)) + (tooltip-hide-delay (or timeout 3))) + (when vscroll + (image-set-window-vscroll vscroll)) + (setq dy (max 0 (- dy + (cdr (pdf-view-image-offset)) + (window-vscroll nil t) + (frame-char-height)))) + (when (overlay-get (pdf-view-current-overlay) 'before-string) + (let* ((e (window-inside-pixel-edges)) + (xw (pdf-util-with-edges (e) e-width))) + (cl-incf dx (/ (- xw (car (pdf-view-image-size t))) 2)))) + (pdf-util-tooltip-in-window + (propertize + " " 'display (propertize + "\u2192" ;;right arrow + 'display '(height 2) + 'face `(:foreground + "orange red" + :background + ,(if (bound-and-true-p pdf-view-midnight-minor-mode) + (cdr pdf-view-midnight-colors) + "white")))) + dx dy))) + (defun org-noter--show-arrow () (when (and org-noter--arrow-location (window-live-p (aref org-noter--arrow-location 1))) From 3924fd816a1e34d7be54805eec36513d1a86bc71 Mon Sep 17 00:00:00 2001 From: Ahmed Shariff Date: Fri, 2 Oct 2020 02:00:28 -0500 Subject: [PATCH 002/453] View tooltip when position in format page . (x . y) --- org-noter.el | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/org-noter.el b/org-noter.el index 1aa297a..e1130d6 100644 --- a/org-noter.el +++ b/org-noter.el @@ -293,7 +293,7 @@ The title used will be the default one." "Timer for synchronizing notes after scrolling.") (defvar org-noter--arrow-location nil - "A vector [TIMER WINDOW TOP] that shows where the arrow should appear, when idling.") + "A vector [TIMER WINDOW TOP LEFT] that shows where the arrow should appear, when idling.") (defvar org-noter--completing-read-keymap (make-sparse-keymap) "A `completing-read' keymap that let's the user insert spaces.") @@ -780,6 +780,7 @@ properties, by a margin of NEWLINES-NUMBER." (or (run-hook-with-args-until-success 'org-noter--parse-location-property-hook property) (let ((value (car (read-from-string property)))) (cond ((and (consp value) (integerp (car value)) (numberp (cdr value))) value) + ((and (consp value) (integerp (car value)) (consp (cdr value)) (numberp (cadr value)) (numberp (cddr value))) value) ((integerp value) (cons value 0)))))))) (defun org-noter--pretty-print-location (location) @@ -878,7 +879,7 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (setq event (read-event "Click where you want the start of the note to be!"))) (posn-point (event-start event))))))))) -(defun pdf-util-tooltip-arrow (image-top &optional timeout image-left) +(defun pdf-util-tooltip-arrow-with-image-left (image-top &optional timeout image-left) (pdf-util-assert-pdf-window) (when (floatp image-top) (setq image-top @@ -923,11 +924,13 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." "white")))) dx dy))) +;;(advice-add 'pdf-util-tooltip-arrow :override pdf-util-tooltip-arrow-with-image-left) + (defun org-noter--show-arrow () (when (and org-noter--arrow-location (window-live-p (aref org-noter--arrow-location 1))) (with-selected-window (aref org-noter--arrow-location 1) - (pdf-util-tooltip-arrow (aref org-noter--arrow-location 2)))) + (pdf-util-tooltip-arrow-with-image-left (aref org-noter--arrow-location 2) nil (aref org-noter--arrow-location 3)))) (setq org-noter--arrow-location nil)) (defun org-noter--doc-goto-location (location) @@ -940,19 +943,27 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." ((run-hook-with-args-until-success 'org-noter--doc-goto-location-hook mode location)) ((memq mode '(doc-view-mode pdf-view-mode)) - (if (eq mode 'doc-view-mode) - (doc-view-goto-page (car location)) - (pdf-view-goto-page (car location)) - ;; NOTE(nox): This timer is needed because the tooltip may introduce a delay, - ;; so syncing multiple pages was slow - (when (>= org-noter-arrow-delay 0) - (when org-noter--arrow-location (cancel-timer (aref org-noter--arrow-location 0))) - (setq org-noter--arrow-location - (vector (run-with-idle-timer org-noter-arrow-delay nil 'org-noter--show-arrow) - window - (cdr location))))) - (image-scroll-up (- (org-noter--conv-page-percentage-scroll (cdr location)) - (window-vscroll)))) + (let (top + (left 0)) + (if (listp (cdr location)) + (setq top (cadr location) + left (cddr location)) + (setq top (cdr location))) + + (if (eq mode 'doc-view-mode) + (doc-view-goto-page (car location)) + (pdf-view-goto-page (car location)) + ;; NOTE(nox): This timer is needed because the tooltip may introduce a delay, + ;; so syncing multiple pages was slow + (when (>= org-noter-arrow-delay 0) + (when org-noter--arrow-location (cancel-timer (aref org-noter--arrow-location 0))) + (setq org-noter--arrow-location + (vector (run-with-idle-timer org-noter-arrow-delay nil 'org-noter--show-arrow) + window + top + left)))) + (image-scroll-up (- (org-noter--conv-page-percentage-scroll top) + (window-vscroll))))) ((eq mode 'nov-mode) (setq nov-documents-index (car location)) From a83a2eb7df1edd26affdca0c9cd466193d6cff6c Mon Sep 17 00:00:00 2001 From: Ahmed Shariff Date: Fri, 2 Oct 2020 02:01:05 -0500 Subject: [PATCH 003/453] Insert heading with format page . (x . y) when no other precise-note is inserted --- org-noter.el | 49 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/org-noter.el b/org-noter.el index e1130d6..a612ad0 100644 --- a/org-noter.el +++ b/org-noter.el @@ -734,8 +734,10 @@ properties, by a margin of NEWLINES-NUMBER." (defun org-noter--doc-approx-location-cons (&optional precise-info) (cond ((memq major-mode '(doc-view-mode pdf-view-mode)) - (cons (image-mode-window-get 'page) (if (numberp precise-info) precise-info 0))) - + (cons (image-mode-window-get 'page) (if (and (consp precise-info) + (numberp (car precise-info)) + (numberp (cdr precise-info))) + precise-info 0))) ((eq major-mode 'nov-mode) (cons nov-documents-index (if (integerp precise-info) precise-info @@ -788,7 +790,7 @@ properties, by a margin of NEWLINES-NUMBER." (or (run-hook-with-args-until-success 'org-noter--pretty-print-location-hook location) (format "%s" (cond ((memq (org-noter--session-doc-mode session) '(doc-view-mode pdf-view-mode)) - (if (or (not (cdr location)) (<= (cdr location) 0)) + (if (or (not (cdr location)) (and (<= (cadr location) 0) (<= (cddr location) 0))) (car location) location)) @@ -823,26 +825,35 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (defun org-noter--doc-get-page-slice () "Return (slice-top . slice-height)." (let* ((slice (or (image-mode-window-get 'slice) '(0 0 1 1))) + (slice-left (float (nth 0 slice))) (slice-top (float (nth 1 slice))) + (slice-width (float (nth 2 slice))) (slice-height (float (nth 3 slice)))) (when (or (> slice-top 1) (> slice-height 1)) (let ((height (cdr (image-size (image-mode-window-get 'image) t)))) (setq slice-top (/ slice-top height) slice-height (/ slice-height height)))) - (cons slice-top slice-height))) - -(defun org-noter--conv-page-scroll-percentage (scroll) + (when (or (> slice-width 1) + (> slice-left 1)) + (let ((width (car (image-size (image-mode-window-get 'image) t)))) + (setq slice-width (/ slice-width height) + slice-left (/ slice-left height)))) + (list slice-top slice-height slice-left slice-width))) + +(defun org-noter--conv-page-scroll-percentage (vscroll &optional hscroll) (let* ((slice (org-noter--doc-get-page-slice)) - (display-height (cdr (image-display-size (image-get-display-property)))) - (display-percentage (/ scroll display-height)) - (percentage (+ (car slice) (* (cdr slice) display-percentage)))) - (max 0 (min 1 percentage)))) + (display-size (image-display-size (image-get-display-property))) + (display-percentage-height (/ vscroll (cdr display-size))) + (hpercentage (max 0 (min 1 (+ (nth 0 slice) (* (nth 1 slice) display-percentage-height)))))) + (if hscroll + (cons hpercentage (max 0 (min 1 (+ (nth 2 slice) (* (nth 3 slice) (/ vscroll (car display-size))))))) + (cons hpercentage 0)))) (defun org-noter--conv-page-percentage-scroll (percentage) (let* ((slice (org-noter--doc-get-page-slice)) (display-height (cdr (image-display-size (image-get-display-property)))) - (display-percentage (min 1 (max 0 (/ (- percentage (car slice)) (cdr slice))))) + (display-percentage (min 1 (max 0 (/ (- percentage (nth 0 slice)) (nth 1 slice))))) (scroll (max 0 (floor (* display-percentage display-height))))) scroll)) @@ -857,12 +868,16 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." ((eq mode 'pdf-view-mode) (if (pdf-view-active-region-p) - (cadar (pdf-view-active-region)) + (let ((edges (pdf-view-active-region))) + (cons + (cadar edges) + (caar edges))) (while (not (and (eq 'mouse-1 (car event)) (eq window (posn-window (event-start event))))) (setq event (read-event "Click where you want the start of the note to be!"))) - (org-noter--conv-page-scroll-percentage (+ (window-vscroll) - (cdr (posn-col-row (event-start event))))))) + (let ((col-row (posn-col-row (event-start event)))) + (org-noter--conv-page-scroll-percentage (+ (window-vscroll) (cdr col-row)) + (+ (window-hscroll) (car col-row)))))) ((eq mode 'doc-view-mode) (while (not (and (eq 'mouse-1 (car event)) @@ -1013,7 +1028,9 @@ L2 or, when in the same page, if L1 is the _f_irst of the two." (t (setq l1 (or (run-hook-with-args-until-success 'org-noter--convert-to-location-cons-hook l1) l1) l2 (or (run-hook-with-args-until-success 'org-noter--convert-to-location-cons-hook l2) l2)) - (org-noter--compare-location-cons comp l1 l2)))) + (if (numberp (cdr l2)) + (org-noter--compare-location-cons comp l1 l2) + (org-noter--compare-location-cons comp l1 (cons (car l2) (cadr l2))))))) (defun org-noter--show-note-entry (session note) "This will show the note entry and its children. @@ -1836,7 +1853,7 @@ defines if the text should be inserted inside the note." (buffer-substring-no-properties (mark) (point)))))) force-new (location (org-noter--doc-approx-location (or precise-info 'interactive) (gv-ref force-new))) - (view-info (org-noter--get-view-info (org-noter--get-current-view) location))) + (view-info (org-noter--get-view-info (org-noter--get-current-view) location))) (let ((inhibit-quit t)) (with-local-quit From 5bc5754cbd29f6665abdab28dc053c3c902e1e0b Mon Sep 17 00:00:00 2001 From: Ahmed Shariff Date: Tue, 15 Dec 2020 12:43:41 -0600 Subject: [PATCH 004/453] Comparison of precise locations with old and new coordinates --- org-noter.el | 80 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 54 insertions(+), 26 deletions(-) diff --git a/org-noter.el b/org-noter.el index a612ad0..6b6e25b 100644 --- a/org-noter.el +++ b/org-noter.el @@ -948,6 +948,22 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (pdf-util-tooltip-arrow-with-image-left (aref org-noter--arrow-location 2) nil (aref org-noter--arrow-location 3)))) (setq org-noter--arrow-location nil)) +(defun get-location-top (location) + "Get the top coordinate given a LOCATION vector of form [page top left] or [page top]." + (if (listp (cdr location)) + (cadr location) + (cdr location))) + +(defun get-location-page (location) + "Get the page number given a LOCATION vector of form [page top left] or [page top]." + (car location)) + +(defun get-location-left (location) + "Get the left coordinate given a LOCATION vector of form [page top left] or [page top]. If later form of vector is passed return 0." + (if (listp (cdr location)) + (cddr location) + 0)) + (defun org-noter--doc-goto-location (location) "Go to location specified by LOCATION." (org-noter--with-valid-session @@ -958,12 +974,8 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." ((run-hook-with-args-until-success 'org-noter--doc-goto-location-hook mode location)) ((memq mode '(doc-view-mode pdf-view-mode)) - (let (top - (left 0)) - (if (listp (cdr location)) - (setq top (cadr location) - left (cddr location)) - (setq top (cdr location))) + (let ((top (get-location-top location)) + (left (get-location-left location))) (if (eq mode 'doc-view-mode) (doc-view-goto-page (car location)) @@ -994,28 +1006,44 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." See `org-noter--compare-locations'" (cl-assert (and (consp l1) (consp l2))) (cond ((eq comp '=) - (and (= (car l1) (car l2)) - (= (cdr l1) (cdr l2)))) + (and (= (get-location-page l1) (get-location-page l2)) + (= (get-location-top l1) (get-location-top l2)) + (= (get-location-left l1) (get-location-left l2)))) ((eq comp '<) - (or (< (car l1) (car l2)) - (and (= (car l1) (car l2)) - (< (cdr l1) (cdr l2))))) + (or (< (get-location-page l1) (get-location-page l2)) + (and (= (get-location-page l1) (get-location-page l2)) + (< (get-location-top l1) (get-location-top l2))) + (and (= (get-location-page l1) (get-location-page l2)) + (= (get-location-top l1) (get-location-top l2)) + (< (get-location-left l1) (get-location-left l2))))) ((eq comp '<=) - (or (< (car l1) (car l2)) - (and (= (car l1) (car l2)) - (<= (cdr l1) (cdr l2))))) + (or (< (get-location-page l1) (get-location-page l2)) + (and (= (get-location-page l1) (get-location-page l2)) + (<= (get-location-top l1) (get-location-top l2))) + (and (= (get-location-page l1) (get-location-page l2)) + (= (get-location-top l1) (get-location-top l2)) + (<= (get-location-left l1) (get-location-left l2))))) ((eq comp '>) - (or (> (car l1) (car l2)) - (and (= (car l1) (car l2)) - (> (cdr l1) (cdr l2))))) + (or (> (get-location-page l1) (get-location-page l2)) + (and (= (get-location-page l1) (get-location-page l2)) + (> (get-location-top l1) (get-location-top l2))) + (and (= (get-location-page l1) (get-location-page l2)) + (= (get-location-top l1) (get-location-top l2)) + (> (get-location-left l1) (get-location-left l2))))) ((eq comp '>=) - (or (> (car l1) (car l2)) - (and (= (car l1) (car l2)) - (>= (cdr l1) (cdr l2))))) + (or (> (get-location-page l1) (get-location-page l2)) + (and (= (get-location-page l1) (get-location-page l2)) + (>= (get-location-top l1) (get-location-top l2))) + (and (= (get-location-page l1) (get-location-page l2)) + (= (get-location-top l1) (get-location-top l2)) + (>= (get-location-left l1) (get-location-left l2))))) ((eq comp '>f) - (or (> (car l1) (car l2)) - (and (= (car l1) (car l2)) - (< (cdr l1) (cdr l2))))) + (or (> (get-location-page l1) (get-location-page l2)) + (and (= (get-location-page l1) (get-location-page l2)) + (< (get-location-top l1) (get-location-top l2))) + (and (= (get-location-page l1) (get-location-page l2)) + (= (get-location-top l1) (get-location-top l2)) + (< (get-location-left l1) (get-location-left l2))))) (t (error "Comparison operator %s not known" comp)))) (defun org-noter--compare-locations (comp l1 l2) @@ -1125,10 +1153,10 @@ document property) will be opened." point location view)) (cdr hook-result)) ((eq (aref view 0) 'paged) - (> (cdr location) point)) + (> (get-location-top location) point)) ((eq (aref view 0) 'nov) - (> (cdr location) (+ (* point (- (cdr (aref view 2)) (cdr (aref view 1)))) - (cdr (aref view 1)))))))) + (> (get-location-top location) (+ (* point (- (cdr (aref view 2)) (cdr (aref view 1)))) + (cdr (aref view 1)))))))) (defun org-noter--relative-position-to-view (location view) (cond From 7f05ee902f4e2b260c8e2e98bac3c58585f09694 Mon Sep 17 00:00:00 2001 From: Ahmed Shariff Date: Tue, 15 Dec 2020 13:12:00 -0600 Subject: [PATCH 005/453] Refactor to use get-location- functions --- org-noter.el | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/org-noter.el b/org-noter.el index 6b6e25b..b5ab4d3 100644 --- a/org-noter.el +++ b/org-noter.el @@ -790,13 +790,13 @@ properties, by a margin of NEWLINES-NUMBER." (or (run-hook-with-args-until-success 'org-noter--pretty-print-location-hook location) (format "%s" (cond ((memq (org-noter--session-doc-mode session) '(doc-view-mode pdf-view-mode)) - (if (or (not (cdr location)) (and (<= (cadr location) 0) (<= (cddr location) 0))) + (if (or (not (get-location-top location)) (<= (get-location-top location) 0)) (car location) location)) ((eq (org-noter--session-doc-mode session) 'nov-mode) - (if (or (not (cdr location)) (<= (cdr location) 1)) - (car location) + (if (or (not (get-location-top location)) (<= (get-location-top location) 1)) + (get-location-page location) location))))))) (defun org-noter--get-containing-heading (&optional include-root) @@ -978,8 +978,8 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (left (get-location-left location))) (if (eq mode 'doc-view-mode) - (doc-view-goto-page (car location)) - (pdf-view-goto-page (car location)) + (doc-view-goto-page (get-location-page location)) + (pdf-view-goto-page (get-location-page location)) ;; NOTE(nox): This timer is needed because the tooltip may introduce a delay, ;; so syncing multiple pages was slow (when (>= org-noter-arrow-delay 0) @@ -993,9 +993,9 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (window-vscroll))))) ((eq mode 'nov-mode) - (setq nov-documents-index (car location)) + (setq nov-documents-index (get-location-page location)) (nov-render-document) - (goto-char (cdr location)) + (goto-char (get-location-top location)) (recenter))) ;; NOTE(nox): This needs to be here, because it would be issued anyway after ;; everything and would run org-noter--nov-scroll-handler. @@ -1163,7 +1163,7 @@ document property) will be opened." ((run-hook-with-args-until-success 'org-noter--relative-position-to-view-hook location view)) ((eq (aref view 0) 'paged) - (let ((note-page (car location)) + (let ((note-page (get-location-page location)) (view-page (aref view 1))) (cond ((< note-page view-page) 'before) ((= note-page view-page) 'inside) @@ -1947,7 +1947,7 @@ defines if the text should be inserted inside the note." (let ((reference-element-cons (org-noter--view-info-reference-for-insertion view-info)) level) (when (zerop (length title)) - (setq title (replace-regexp-in-string (regexp-quote "$p$") (number-to-string (car location)) + (setq title (replace-regexp-in-string (regexp-quote "$p$") (number-to-string (get-location-page location)) org-noter-default-heading-title))) (if reference-element-cons From ced27513cdc109431ba06903648af120c50e8dbf Mon Sep 17 00:00:00 2001 From: Ahmed Shariff Date: Wed, 13 Jan 2021 16:13:37 -0600 Subject: [PATCH 006/453] Adding (require 'pdf-tools) --- org-noter.el | 1 + 1 file changed, 1 insertion(+) diff --git a/org-noter.el b/org-noter.el index b5ab4d3..a760546 100644 --- a/org-noter.el +++ b/org-noter.el @@ -38,6 +38,7 @@ (require 'org) (require 'org-element) (require 'cl-lib) +(require 'pdf-tools) (declare-function doc-view-goto-page "doc-view") (declare-function image-display-size "image-mode") From 0653e7e450f5597bdbdfeda269b477b4c91bf56a Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 3 Sep 2021 10:45:12 +0000 Subject: [PATCH 007/453] org-noter: Allow buffer with djvu-read-mode as major mode. --- org-noter.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org-noter.el b/org-noter.el index 129d61c..c0a8a49 100644 --- a/org-noter.el +++ b/org-noter.el @@ -2160,7 +2160,7 @@ notes file, even if it finds one." (org-noter--create-session ast document-property notes-file-path)))) ;; NOTE(nox): Creating the session from the annotated document - ((memq major-mode '(doc-view-mode pdf-view-mode nov-mode)) + ((memq major-mode '(doc-view-mode pdf-view-mode nov-mode djvu-read-mode)) (if (org-noter--valid-session org-noter--session) (progn (org-noter--setup-windows org-noter--session) (select-frame-set-input-focus (org-noter--session-frame org-noter--session))) From 6673794f883ae574b7ce41437d6f24b284ee5ff9 Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 3 Sep 2021 10:55:32 +0000 Subject: [PATCH 008/453] doc-approx-location: Return a location cons for djvu-read-mode. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit org-noter uses this function to get the location (“pages”) of the document to create its session successfully. This is to make it work with djvu-read-mode. --- org-noter.el | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/org-noter.el b/org-noter.el index c0a8a49..7300dc7 100644 --- a/org-noter.el +++ b/org-noter.el @@ -741,9 +741,13 @@ properties, by a margin of NEWLINES-NUMBER." precise-info (max 1 (/ (+ (window-start) (window-end nil t)) 2))))) + ((eq major-mode 'djvu-read-mode) + (cons djvu-doc-page (if (numberp precise-info) precise-info 0))) + (t (error "Unknown document type %s" major-mode)))) (defun org-noter--doc-approx-location (&optional precise-info force-new-ref) + "TODO" (let ((window (if (org-noter--valid-session org-noter--session) (org-noter--get-doc-window) (selected-window)))) From 2a1655a3bb95a79b8d0939edfd4d910880327114 Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 3 Sep 2021 14:57:13 +0000 Subject: [PATCH 009/453] create-session: Handle djvu-read-mode location change. --- org-noter.el | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/org-noter.el b/org-noter.el index 7300dc7..65b95bf 100644 --- a/org-noter.el +++ b/org-noter.el @@ -418,6 +418,12 @@ The title used will be the default one." (advice-add 'nov-render-document :after 'org-noter--nov-scroll-handler) (add-hook 'window-scroll-functions 'org-noter--nov-scroll-handler nil t)) + ;; NOTE(c1-g): Djvu + + ((eq document-major-mode 'djvu-read-mode) + (setq-local djvu-doc (current-buffer)) + (advice-add 'djvu-init-page :after 'org-noter--location-change-advice)) + (t (error "This document handler is not supported :/"))) (org-noter-doc-mode 1) @@ -1040,7 +1046,7 @@ document property) will be opened." (org-noter--with-valid-session (let ((mode (org-noter--session-doc-mode session))) (with-selected-window (org-noter--get-doc-window) - (cond ((memq mode '(doc-view-mode pdf-view-mode)) + (cond ((memq mode '(doc-view-mode pdf-view-mode djvu-read-mode)) (vector 'paged (car (org-noter--doc-approx-location-cons)))) ((eq mode 'nov-mode) (vector 'nov From aea29e3139aff2a2c4232e212d5368f4e456f064 Mon Sep 17 00:00:00 2001 From: c1-g Date: Sat, 4 Sep 2021 00:14:48 +0000 Subject: [PATCH 010/453] insert-note & pretty-print-location: Allow djvu-read-mode. --- org-noter.el | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/org-noter.el b/org-noter.el index 65b95bf..c684dce 100644 --- a/org-noter.el +++ b/org-noter.el @@ -796,7 +796,7 @@ properties, by a margin of NEWLINES-NUMBER." (org-noter--with-valid-session (or (run-hook-with-args-until-success 'org-noter--pretty-print-location-hook location) (format "%s" (cond - ((memq (org-noter--session-doc-mode session) '(doc-view-mode pdf-view-mode)) + ((memq (org-noter--session-doc-mode session) '(doc-view-mode pdf-view-mode djvu-read-mode)) (if (or (not (cdr location)) (<= (cdr location) 0)) (car location) location)) @@ -1786,6 +1786,10 @@ defines if the text should be inserted inside the note." (mapconcat 'identity (pdf-view-active-region-text) ? ))) ((eq (org-noter--session-doc-mode session) 'nov-mode) + (when (region-active-p) + (buffer-substring-no-properties (mark) (point)))) + + ((eq (org-noter--session-doc-mode session) 'djvu-read-mode) (when (region-active-p) (buffer-substring-no-properties (mark) (point)))))) force-new From 2fa571b6f1b3bd2a1e4dfcf460c6acd9241e2d5c Mon Sep 17 00:00:00 2001 From: c1-g Date: Sat, 4 Sep 2021 00:26:43 +0000 Subject: [PATCH 011/453] =?UTF-8?q?create-session:=20Don=E2=80=99t=20make?= =?UTF-8?q?=20an=20indirect=20buffer=20of=20document=20in=20djvu.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- org-noter.el | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/org-noter.el b/org-noter.el index c684dce..dfc8433 100644 --- a/org-noter.el +++ b/org-noter.el @@ -356,7 +356,7 @@ The title used will be the default one." (document-buffer-name (generate-new-buffer-name (concat (unless raw-value-not-empty "Org-noter: ") display-name))) (document-buffer - (if (eq document-major-mode 'nov-mode) + (if (memq document-major-mode '(nov-mode djvu-read-mode)) document (make-indirect-buffer document document-buffer-name t))) @@ -421,7 +421,6 @@ The title used will be the default one." ;; NOTE(c1-g): Djvu ((eq document-major-mode 'djvu-read-mode) - (setq-local djvu-doc (current-buffer)) (advice-add 'djvu-init-page :after 'org-noter--location-change-advice)) (t (error "This document handler is not supported :/"))) From f97f2c7f36d331b01912fc6e90f93ffd6acdde48 Mon Sep 17 00:00:00 2001 From: c1-g Date: Sat, 4 Sep 2021 00:36:32 +0000 Subject: [PATCH 012/453] doc-goto-location: Handle djvu file. This function is used in handling the org-noter-sync-current-note command. --- org-noter.el | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/org-noter.el b/org-noter.el index dfc8433..522896e 100644 --- a/org-noter.el +++ b/org-noter.el @@ -903,18 +903,19 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (cond ((run-hook-with-args-until-success 'org-noter--doc-goto-location-hook mode location)) - ((memq mode '(doc-view-mode pdf-view-mode)) - (if (eq mode 'doc-view-mode) - (doc-view-goto-page (car location)) - (pdf-view-goto-page (car location)) - ;; NOTE(nox): This timer is needed because the tooltip may introduce a delay, - ;; so syncing multiple pages was slow - (when (>= org-noter-arrow-delay 0) - (when org-noter--arrow-location (cancel-timer (aref org-noter--arrow-location 0))) - (setq org-noter--arrow-location - (vector (run-with-idle-timer org-noter-arrow-delay nil 'org-noter--show-arrow) - window - (cdr location))))) + ((eq mode 'doc-view-mode) + (doc-view-goto-page (car location))) + + ((eq mode 'pdf-view-mode) + (pdf-view-goto-page (car location)) + ;; NOTE(nox): This timer is needed because the tooltip may introduce a delay, + ;; so syncing multiple pages was slow + (when (>= org-noter-arrow-delay 0) + (when org-noter--arrow-location (cancel-timer (aref org-noter--arrow-location 0))) + (setq org-noter--arrow-location + (vector (run-with-idle-timer org-noter-arrow-delay nil 'org-noter--show-arrow) + window + (cdr location)))) (image-scroll-up (- (org-noter--conv-page-percentage-scroll (cdr location)) (window-vscroll)))) @@ -922,9 +923,12 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (setq nov-documents-index (car location)) (nov-render-document) (goto-char (cdr location)) - (recenter))) + (recenter)) ;; NOTE(nox): This needs to be here, because it would be issued anyway after ;; everything and would run org-noter--nov-scroll-handler. + + ((eq mode 'djvu-read-mode) + (djvu-goto-page (car location)))) (redisplay))))) (defun org-noter--compare-location-cons (comp l1 l2) From 16c5d934654d88092cecc3b1ac879bb63f3e06db Mon Sep 17 00:00:00 2001 From: c1-g Date: Sat, 4 Sep 2021 05:13:01 +0000 Subject: [PATCH 013/453] insert-precise-note: Allow djvu-read-mode. --- org-noter.el | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/org-noter.el b/org-noter.el index 522896e..01f1c77 100644 --- a/org-noter.el +++ b/org-noter.el @@ -880,6 +880,14 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (cdr (posn-col-row (event-start event)))))) ((eq mode 'nov-mode) + (if (region-active-p) + (min (mark) (point)) + (while (not (and (eq 'mouse-1 (car event)) + (eq window (posn-window (event-start event))))) + (setq event (read-event "Click where you want the start of the note to be!"))) + (posn-point (event-start event)))) + + ((eq mode 'djvu-read-mode) (if (region-active-p) (min (mark) (point)) (while (not (and (eq 'mouse-1 (car event)) @@ -887,6 +895,7 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (setq event (read-event "Click where you want the start of the note to be!"))) (posn-point (event-start event))))))))) + (defun org-noter--show-arrow () (when (and org-noter--arrow-location (window-live-p (aref org-noter--arrow-location 1))) @@ -1793,8 +1802,15 @@ defines if the text should be inserted inside the note." (buffer-substring-no-properties (mark) (point)))) ((eq (org-noter--session-doc-mode session) 'djvu-read-mode) - (when (region-active-p) - (buffer-substring-no-properties (mark) (point)))))) + (if (region-active-p) + (buffer-substring-no-properties (mark) (point)) + (with-current-buffer (djvu-ref outline-buf) + (djvu-goto-outline) + (string-join + (butlast + (split-string + (string-trim (thing-at-point 'line t)))) + " ")))))) force-new (location (org-noter--doc-approx-location (or precise-info 'interactive) (gv-ref force-new))) (view-info (org-noter--get-view-info (org-noter--get-current-view) location))) From cf0dcfc19b41644287c71df2c6228ee750ee8511 Mon Sep 17 00:00:00 2001 From: c1-g Date: Sat, 4 Sep 2021 07:34:28 +0000 Subject: [PATCH 014/453] New README. --- README-orig.org | 224 +++++++++++++++++++++++++++++++++++++++++++++++ README.org | 227 ++---------------------------------------------- 2 files changed, 230 insertions(+), 221 deletions(-) create mode 100644 README-orig.org diff --git a/README-orig.org b/README-orig.org new file mode 100644 index 0000000..d3b9c81 --- /dev/null +++ b/README-orig.org @@ -0,0 +1,224 @@ +[[https://melpa.org/#/org-noter][file:https://melpa.org/packages/org-noter-badge.svg]] +[[https://stable.melpa.org/#/org-noter][file:https://stable.melpa.org/packages/org-noter-badge.svg]] +* Org-noter - A synchronized, Org-mode, document annotator +After using Sebastian Christ's amazing [[https://github.com/rudolfochrist/interleave][Interleave package]] for some time, I got some ideas +on how I could improve upon it, usability and feature-wise. So I created this package from +scratch with those ideas in mind! + +Org-noter's purpose is to let you create notes that are kept in sync when you scroll through +the document, but that are external to it - the notes themselves live in an Org-mode file. +As such, this leverages the power of [[http://orgmode.org/][Org-mode]] (the notes may have outlines, latex +fragments, babel, etc...) while acting like notes that are made /inside/ the document. +Also, taking notes is very simple: just press @@html:@@i@@html:@@ and annotate +away! + +*Note*: While this is similar to ~interleave~, it is not intended to be a clone; +therefore, /not every feature of the original mode is available/! You may prefer using the +original, because this is a different take on the same idea. [[#diff][View some differences here.]] + +Org-noter is compatible with [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Document-View.html][DocView]], [[https://github.com/politza/pdf-tools][PDF Tools]], and [[https://github.com/wasamasa/nov.el][Nov.el]]. These modes make it possible +to annotate *PDF*, *EPUB*, *Microsoft Office*, DVI, PS, and OpenDocument. + +On a personal note, if you annotate and read lots of PDFs, give PDF Tools a try! It is +great. + +** Features +- Easy to use annotation interface :: Just press @@html:@@i@@html:@@ in the + document buffer and annotate away! + +- Keep your notes in sync with the document :: When you take a note by pressing + @@html:@@i@@html:@@, it saves the location where you took it so it is able to show you + the notes while you scroll, automatically! + +- Easy navigation :: You may navigate your document as usual, seeing the notes buffer scroll and show you + the respective notes; however, you may also navigate by notes, only seeing annotated pages/chapters. + +- Isolated interface :: Each session has its own frame and the document and notes buffers are indirect + buffers to the original ones. Killing any of these things will terminate your annotation session. + +- Simultaneous sessions :: You may open as many annotation sessions as you wish, at the same time! The + root heading of each document will be made read-only to prevent you from deleting a heading from an + open session. + +- Resume annotating from where you left :: When ~org-noter-auto-save-last-location~ is non-nil, it will + save the last location you visited and pick it up when you start another session! You may also set + this per-document, [[#custom][read more here]]. + +- Keep your notes organized :: You may arrange your notes however you want! You can create groups and + nest notes (and even nest documents inside other documents)! + +- Annotate ~org-attach~'ed files :: If you have any attached files, it will let you choose one as + the document to annotate. + +Many of these features are demonstrated in the screencast, so take a look if you are +confused! + +** Installation +*** MELPA +This package is [[https://melpa.org/#/org-noter][available from MELPA]], so if you want to install it and have added MELPA to +your package archives, you can run +@@html:@@M-x@@html:@@ ~package-install~ @@html:@@RET@@html:@@ ~org-noter~ + +*** Manual installation +You can also install it manually, using =package.el=. +1. Download =org-noter.el= +2. Open it +3. Run @@html:@@M-x@@html:@@ ~package-install-from-buffer~ @@html:@@RET@@html:@@ + +** Usage +If you want to give it a try without much trouble: +- Just have an Org file where you want the notes to go +- Create a root heading to hold the notes +- Run @@html:@@M-x@@html:@@ ~org-noter~ inside! + On the first run, it will ask you for the path of the document and save it in a + property. By default, it will also let you annotate an attached file [[https://orgmode.org/manual/Attachments.html][(org-attach documentation)]]. + +This will open a new dedicated frame where you can use [[#keys][the keybindings described here]]. + +More generally, there are two modes of operation. You may run +@@html:@@M-x@@html:@@ ~org-noter~: +- Inside a heading in an Org notes file :: This will associate that heading with a + document and open a session with it. This mode is the one described in the example + above. + +- In a document :: Run @@html:@@M-x@@html:@@ ~org-noter~ when viewing a + document (eg. PDF, epub...). + + This will try to find the respective notes file automatically. It will + search in all parent folders and some specific folders set by you. See + ~org-noter-default-notes-file-names~ and ~org-noter-notes-search-path~ + for more information. + +There is, of course, more information in the docstrings of each command. + +** Screencast +[[https://www.youtube.com/watch?v=Un0ZRXTzufo][Watch the screencast here!]] + +Note that this package has had some updates since this screencast was made, so, while the +core functionality is the same, there may be some UX and feature differences. + +The files used to make this screencast are shipped with the package, so you can try this +package even without creating the notes. + +** Customization @@html:@@ +There are two kinds of customizations you may do: +1. Global settings, affecting every session +2. Document-specific settings, which override the global settings + +The *global settings* are changed with either the [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Easy-Customization.html#Easy-Customization][customization interface from Emacs]] or directly in your +init file. To find which settings are available, you may use the customization interface or you may just +read =org-noter.el=. + +The best way to set *document-specific settings* is by using the utility commands provided (list below). +In order to use them, you need an open session. The commands may change the settings for that session +only (not surviving restarts), or for every session with that document. + +*List of utility commands* (check the docstrings to learn how to make the changes +permanent, or revert to the default): +- You may set a start location for this document, by using ~org-noter-set-start-location~. + +- To automatically resume from where you left, use ~org-noter-set-auto-save-last-location~. + +- With ~org-noter-set-notes-window-behavior~, you may change /when/ the notes window pops + up. + +- With ~org-noter-set-notes-window-location~, you may change /where/ the notes window pops + up. + +- ~org-noter-set-doc-split-fraction~ will ask you for the fraction of the frame that the document window + occupies when split. + +- ~org-noter-set-hide-other~ will toggle whether or not it should hide headings not + related to the executed action. + +- ~org-noter-set-closest-tipping-point~ will set the closest note tipping point. Also + check the docstring of the variable ~org-noter-closest-tipping-point~ in order to better + understand the tipping point. + +** Keybindings and commands @@html:@@ +:PROPERTIES: +:CUSTOM_ID: keys +:END: +| Key | Description | Where? | +| @@html:@@i@@html:@@ | Insert note | Document buffer | +| @@html:@@M-i@@html:@@ | Insert precise note | Document buffer | +| @@html:@@q@@html:@@ | Kill session | Document buffer | +| @@html:@@M-p@@html:@@ | Sync previous page/chapter | Document and notes buffer | +| @@html:@@M-.@@html:@@ | Sync current page/chapter | Document and notes buffer | +| @@html:@@M-n@@html:@@ | Sync next page/chapter | Document and notes buffer | +| @@html:@@C-M-p@@html:@@ | Sync previous notes | Document and notes buffer | +| @@html:@@C-M-.@@html:@@ | Sync selected notes | Document and notes buffer | +| @@html:@@C-M-n@@html:@@ | Sync next notes | Document and notes buffer | + +You can use the usual keybindings to navigate the document +(@@html:@@n@@html:@@, @@html:@@p@@html:@@, +@@html:@@SPC@@html:@@, ...). + +There are two types of sync commands: +- To sync a page/chapter, means it will find the [previous|current|next] page/chapter and + show the corresponding notes for that page/chapter; as such, it will always pop up the + notes buffer, if it does not exist. This type of command is in relation to the current + page/chapter in the document. + +- To sync the notes, means it will find the [previous|current|next] notes and go to the + corresponding location on the document. So, you need to have the notes window open, + because this type of commands is in relation to the selected notes (ie, where the cursor + is). + +When using PDF Tools, the command ~org-noter-create-skeleton~ imports the PDF outline or +annotations (or both!) as notes, and it may be used, for example, as a starting point. + +You may also want to check the docstrings of the functions associated with the +keybindings, because there is some extra functionality in some. + +** Why a rewrite from scratch? Why not contribute to the existing Interleave package? +Doing a refactor on a foreign codebase takes a long time because of several factors, like +introducing the ideas to the owner (with which he may even disagree), learning its +internals, proposing pull requests, more back and forth in code review, etc... + +Besides, I like doing things from scratch, not only because it expands my skills, but also +because it is something I find very rewarding! + +*** Non-exhaustive list of differences from Interleave @@html:@@ +:PROPERTIES: +:CUSTOM_ID: diff +:END: +**** New features +- Each session is isolated, which means that it has its own frame with indirect buffers + - Makes it possible to have several sessions simultaneously open + - Doesn't narrow the original buffer, which continues completely accessible +- Has precise notes (attached to a section of a page) +- Also supports nov.el +- Skeleton extraction (outline and/or annotations) +- Being able to use the closest previous note when no notes are present on the current + page +- Closing all notes not related to the notes present in the current view +- Possibility of overriding some global settings in each document or session + +**** Some annoyances fixed +- Notes not sorted +- Notes not synced when executing different page change commands, eg. goto-page or + beginning-of-buffer +- Sometimes it would start narrowing other parts of the buffer, giving errors when trying + to go to notes. + +*** Changes to make in order to be compatible with documents created by Interleave +This package only works like the multi-pdf mode of Interleave - you can't open a session +without having a parent headline. + +For compatibility with existing notes made with Interleave, you can do one of two things: +- Change the following property names inside the your documents: + | Old | New | + |------------------------+------------------| + | =INTERLEAVE_PDF= | =NOTER_DOCUMENT= | + | =INTERLEAVE_PAGE_NOTE= | =NOTER_PAGE= | + +- Set these variables on your init file: + #+BEGIN_SRC emacs-lisp +(setq org-noter-property-doc-file "INTERLEAVE_PDF" + org-noter-property-note-location "INTERLEAVE_PAGE_NOTE") + #+END_SRC + +** Acknowledgments +I must thank [[https://github.com/rudolfochrist][Sebastian]] for the original idea and inspiration. Also, many thanks to everyone who +contributed more ideas, reported bugs and submitted PRs :) diff --git a/README.org b/README.org index d3b9c81..3d1585d 100644 --- a/README.org +++ b/README.org @@ -1,224 +1,9 @@ -[[https://melpa.org/#/org-noter][file:https://melpa.org/packages/org-noter-badge.svg]] -[[https://stable.melpa.org/#/org-noter][file:https://stable.melpa.org/packages/org-noter-badge.svg]] -* Org-noter - A synchronized, Org-mode, document annotator -After using Sebastian Christ's amazing [[https://github.com/rudolfochrist/interleave][Interleave package]] for some time, I got some ideas -on how I could improve upon it, usability and feature-wise. So I created this package from -scratch with those ideas in mind! +* Org-noter + DJVU - A fork of [[https://github.com/weirdNox/org-noter][org-noter]] with, hopefully, [[https://elpa.gnu.org/packages/djvu.html][djvu.el]] support. +** What is org-noter? +Org-noter is an org-mode document annotator. It is a rewrite of another project, [[https://github.com/rudolfochrist/interleave][Interleave]]. +Read about org-noter’s feature/usage/customization in its README [[file:README-orig.org][here]] or go visit its repository at [[https://github.com/weirdNox/org-noter][github]]. +** Why? +I just want to use org-noter with djvu file. -Org-noter's purpose is to let you create notes that are kept in sync when you scroll through -the document, but that are external to it - the notes themselves live in an Org-mode file. -As such, this leverages the power of [[http://orgmode.org/][Org-mode]] (the notes may have outlines, latex -fragments, babel, etc...) while acting like notes that are made /inside/ the document. -Also, taking notes is very simple: just press @@html:@@i@@html:@@ and annotate -away! -*Note*: While this is similar to ~interleave~, it is not intended to be a clone; -therefore, /not every feature of the original mode is available/! You may prefer using the -original, because this is a different take on the same idea. [[#diff][View some differences here.]] -Org-noter is compatible with [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Document-View.html][DocView]], [[https://github.com/politza/pdf-tools][PDF Tools]], and [[https://github.com/wasamasa/nov.el][Nov.el]]. These modes make it possible -to annotate *PDF*, *EPUB*, *Microsoft Office*, DVI, PS, and OpenDocument. - -On a personal note, if you annotate and read lots of PDFs, give PDF Tools a try! It is -great. - -** Features -- Easy to use annotation interface :: Just press @@html:@@i@@html:@@ in the - document buffer and annotate away! - -- Keep your notes in sync with the document :: When you take a note by pressing - @@html:@@i@@html:@@, it saves the location where you took it so it is able to show you - the notes while you scroll, automatically! - -- Easy navigation :: You may navigate your document as usual, seeing the notes buffer scroll and show you - the respective notes; however, you may also navigate by notes, only seeing annotated pages/chapters. - -- Isolated interface :: Each session has its own frame and the document and notes buffers are indirect - buffers to the original ones. Killing any of these things will terminate your annotation session. - -- Simultaneous sessions :: You may open as many annotation sessions as you wish, at the same time! The - root heading of each document will be made read-only to prevent you from deleting a heading from an - open session. - -- Resume annotating from where you left :: When ~org-noter-auto-save-last-location~ is non-nil, it will - save the last location you visited and pick it up when you start another session! You may also set - this per-document, [[#custom][read more here]]. - -- Keep your notes organized :: You may arrange your notes however you want! You can create groups and - nest notes (and even nest documents inside other documents)! - -- Annotate ~org-attach~'ed files :: If you have any attached files, it will let you choose one as - the document to annotate. - -Many of these features are demonstrated in the screencast, so take a look if you are -confused! - -** Installation -*** MELPA -This package is [[https://melpa.org/#/org-noter][available from MELPA]], so if you want to install it and have added MELPA to -your package archives, you can run -@@html:@@M-x@@html:@@ ~package-install~ @@html:@@RET@@html:@@ ~org-noter~ - -*** Manual installation -You can also install it manually, using =package.el=. -1. Download =org-noter.el= -2. Open it -3. Run @@html:@@M-x@@html:@@ ~package-install-from-buffer~ @@html:@@RET@@html:@@ - -** Usage -If you want to give it a try without much trouble: -- Just have an Org file where you want the notes to go -- Create a root heading to hold the notes -- Run @@html:@@M-x@@html:@@ ~org-noter~ inside! - On the first run, it will ask you for the path of the document and save it in a - property. By default, it will also let you annotate an attached file [[https://orgmode.org/manual/Attachments.html][(org-attach documentation)]]. - -This will open a new dedicated frame where you can use [[#keys][the keybindings described here]]. - -More generally, there are two modes of operation. You may run -@@html:@@M-x@@html:@@ ~org-noter~: -- Inside a heading in an Org notes file :: This will associate that heading with a - document and open a session with it. This mode is the one described in the example - above. - -- In a document :: Run @@html:@@M-x@@html:@@ ~org-noter~ when viewing a - document (eg. PDF, epub...). - - This will try to find the respective notes file automatically. It will - search in all parent folders and some specific folders set by you. See - ~org-noter-default-notes-file-names~ and ~org-noter-notes-search-path~ - for more information. - -There is, of course, more information in the docstrings of each command. - -** Screencast -[[https://www.youtube.com/watch?v=Un0ZRXTzufo][Watch the screencast here!]] - -Note that this package has had some updates since this screencast was made, so, while the -core functionality is the same, there may be some UX and feature differences. - -The files used to make this screencast are shipped with the package, so you can try this -package even without creating the notes. - -** Customization @@html:@@ -There are two kinds of customizations you may do: -1. Global settings, affecting every session -2. Document-specific settings, which override the global settings - -The *global settings* are changed with either the [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Easy-Customization.html#Easy-Customization][customization interface from Emacs]] or directly in your -init file. To find which settings are available, you may use the customization interface or you may just -read =org-noter.el=. - -The best way to set *document-specific settings* is by using the utility commands provided (list below). -In order to use them, you need an open session. The commands may change the settings for that session -only (not surviving restarts), or for every session with that document. - -*List of utility commands* (check the docstrings to learn how to make the changes -permanent, or revert to the default): -- You may set a start location for this document, by using ~org-noter-set-start-location~. - -- To automatically resume from where you left, use ~org-noter-set-auto-save-last-location~. - -- With ~org-noter-set-notes-window-behavior~, you may change /when/ the notes window pops - up. - -- With ~org-noter-set-notes-window-location~, you may change /where/ the notes window pops - up. - -- ~org-noter-set-doc-split-fraction~ will ask you for the fraction of the frame that the document window - occupies when split. - -- ~org-noter-set-hide-other~ will toggle whether or not it should hide headings not - related to the executed action. - -- ~org-noter-set-closest-tipping-point~ will set the closest note tipping point. Also - check the docstring of the variable ~org-noter-closest-tipping-point~ in order to better - understand the tipping point. - -** Keybindings and commands @@html:@@ -:PROPERTIES: -:CUSTOM_ID: keys -:END: -| Key | Description | Where? | -| @@html:@@i@@html:@@ | Insert note | Document buffer | -| @@html:@@M-i@@html:@@ | Insert precise note | Document buffer | -| @@html:@@q@@html:@@ | Kill session | Document buffer | -| @@html:@@M-p@@html:@@ | Sync previous page/chapter | Document and notes buffer | -| @@html:@@M-.@@html:@@ | Sync current page/chapter | Document and notes buffer | -| @@html:@@M-n@@html:@@ | Sync next page/chapter | Document and notes buffer | -| @@html:@@C-M-p@@html:@@ | Sync previous notes | Document and notes buffer | -| @@html:@@C-M-.@@html:@@ | Sync selected notes | Document and notes buffer | -| @@html:@@C-M-n@@html:@@ | Sync next notes | Document and notes buffer | - -You can use the usual keybindings to navigate the document -(@@html:@@n@@html:@@, @@html:@@p@@html:@@, -@@html:@@SPC@@html:@@, ...). - -There are two types of sync commands: -- To sync a page/chapter, means it will find the [previous|current|next] page/chapter and - show the corresponding notes for that page/chapter; as such, it will always pop up the - notes buffer, if it does not exist. This type of command is in relation to the current - page/chapter in the document. - -- To sync the notes, means it will find the [previous|current|next] notes and go to the - corresponding location on the document. So, you need to have the notes window open, - because this type of commands is in relation to the selected notes (ie, where the cursor - is). - -When using PDF Tools, the command ~org-noter-create-skeleton~ imports the PDF outline or -annotations (or both!) as notes, and it may be used, for example, as a starting point. - -You may also want to check the docstrings of the functions associated with the -keybindings, because there is some extra functionality in some. - -** Why a rewrite from scratch? Why not contribute to the existing Interleave package? -Doing a refactor on a foreign codebase takes a long time because of several factors, like -introducing the ideas to the owner (with which he may even disagree), learning its -internals, proposing pull requests, more back and forth in code review, etc... - -Besides, I like doing things from scratch, not only because it expands my skills, but also -because it is something I find very rewarding! - -*** Non-exhaustive list of differences from Interleave @@html:@@ -:PROPERTIES: -:CUSTOM_ID: diff -:END: -**** New features -- Each session is isolated, which means that it has its own frame with indirect buffers - - Makes it possible to have several sessions simultaneously open - - Doesn't narrow the original buffer, which continues completely accessible -- Has precise notes (attached to a section of a page) -- Also supports nov.el -- Skeleton extraction (outline and/or annotations) -- Being able to use the closest previous note when no notes are present on the current - page -- Closing all notes not related to the notes present in the current view -- Possibility of overriding some global settings in each document or session - -**** Some annoyances fixed -- Notes not sorted -- Notes not synced when executing different page change commands, eg. goto-page or - beginning-of-buffer -- Sometimes it would start narrowing other parts of the buffer, giving errors when trying - to go to notes. - -*** Changes to make in order to be compatible with documents created by Interleave -This package only works like the multi-pdf mode of Interleave - you can't open a session -without having a parent headline. - -For compatibility with existing notes made with Interleave, you can do one of two things: -- Change the following property names inside the your documents: - | Old | New | - |------------------------+------------------| - | =INTERLEAVE_PDF= | =NOTER_DOCUMENT= | - | =INTERLEAVE_PAGE_NOTE= | =NOTER_PAGE= | - -- Set these variables on your init file: - #+BEGIN_SRC emacs-lisp -(setq org-noter-property-doc-file "INTERLEAVE_PDF" - org-noter-property-note-location "INTERLEAVE_PAGE_NOTE") - #+END_SRC - -** Acknowledgments -I must thank [[https://github.com/rudolfochrist][Sebastian]] for the original idea and inspiration. Also, many thanks to everyone who -contributed more ideas, reported bugs and submitted PRs :) From b2427bec8f64ca2026f811a987f1694c1dd44b78 Mon Sep 17 00:00:00 2001 From: c1-g Date: Sat, 4 Sep 2021 08:00:01 +0000 Subject: [PATCH 015/453] Change insert-note default keybinding to (uppercase i) The key i (lowercase i) for inserting note in the document buffer has been changed to I (uppercase i) since it interfered with Djview Image minor mode key. --- README.org | 3 +++ org-noter.el | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.org b/README.org index 3d1585d..99630bb 100644 --- a/README.org +++ b/README.org @@ -4,6 +4,9 @@ Org-noter is an org-mode document annotator. It is a rewrite of another project, Read about org-noter’s feature/usage/customization in its README [[file:README-orig.org][here]] or go visit its repository at [[https://github.com/weirdNox/org-noter][github]]. ** Why? I just want to use org-noter with djvu file. +** Caveats +*** Changed default keybindings +The key @@html:@@i@@html:@@ (lowercase i) for inserting note in the document buffer has been changed to @@html:@@I@@html:@@ (uppercase i) since it interfered with Djview Image minor mode key. diff --git a/org-noter.el b/org-noter.el index 01f1c77..64d55e7 100644 --- a/org-noter.el +++ b/org-noter.el @@ -2082,7 +2082,7 @@ As such, it will only work when the notes window exists." "Minor mode for the document buffer. Keymap: \\{org-noter-doc-mode-map}" - :keymap `((,(kbd "i") . org-noter-insert-note) + :keymap `((,(kbd "I") . org-noter-insert-note) (,(kbd "C-i") . org-noter-insert-note-toggle-no-questions) (,(kbd "M-i") . org-noter-insert-precise-note) (,(kbd "q") . org-noter-kill-session) From 183532b7b8e0588810a5d501c1967cc174a30316 Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 6 Sep 2021 17:32:30 +0700 Subject: [PATCH 016/453] create-skeleton: Delegate to org-noter-create-skeleton-pdf. --- org-noter.el | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/org-noter.el b/org-noter.el index 64d55e7..e9277e7 100644 --- a/org-noter.el +++ b/org-noter.el @@ -1600,13 +1600,16 @@ want to kill." (set-frame-parameter nil 'name nil))))))) (defun org-noter-create-skeleton () - "Create notes skeleton with the PDF outline or annotations. -Only available with PDF Tools." + "Create notes skeleton based on the outline of the document." (interactive) (org-noter--with-valid-session - (cond - ((eq (org-noter--session-doc-mode session) 'pdf-view-mode) - (let* ((ast (org-noter--parse-root)) + (pcase (org-noter--session-doc-mode session) + ('pdf-view-mode (org-noter-create-skeleton-pdf)) + (_ (user-error "This command is not supported for %s" (org-noter--session-doc-mode session)))))) + +(defun org-noter-create-skeleton-pdf () + "Create notes skeleton with the PDF outline or annotations." + (let* ((ast (org-noter--parse-root)) (top-level (org-element-property :level ast)) (options '(("Outline" . (outline)) ("Annotations" . (annots)) @@ -1766,8 +1769,6 @@ Only available with PDF Tools." (outline-hide-subtree) (org-show-children 2))))) - (t (user-error "This command is only supported on PDF Tools."))))) - (defun org-noter-insert-note (&optional precise-info) "Insert note associated with the current location. From 237528070a7059b85fbf582c5b80aea57c737e08 Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 6 Sep 2021 20:35:11 +0700 Subject: [PATCH 017/453] Create org-noter-create-skeleton-djvu. --- org-noter.el | 369 +++++++++++++++++++++++++++++---------------------- 1 file changed, 212 insertions(+), 157 deletions(-) diff --git a/org-noter.el b/org-noter.el index e9277e7..ed05af2 100644 --- a/org-noter.el +++ b/org-noter.el @@ -1605,169 +1605,224 @@ want to kill." (org-noter--with-valid-session (pcase (org-noter--session-doc-mode session) ('pdf-view-mode (org-noter-create-skeleton-pdf)) + ('djvu-read-mode (org-noter-create-skeleton-djvu)) (_ (user-error "This command is not supported for %s" (org-noter--session-doc-mode session)))))) (defun org-noter-create-skeleton-pdf () "Create notes skeleton with the PDF outline or annotations." - (let* ((ast (org-noter--parse-root)) - (top-level (org-element-property :level ast)) - (options '(("Outline" . (outline)) - ("Annotations" . (annots)) - ("Both" . (outline annots)))) - answer output-data) - (with-current-buffer (org-noter--session-doc-buffer session) - (setq answer (assoc (completing-read "What do you want to import? " options nil t) options)) - - (when (memq 'outline answer) - (dolist (item (pdf-info-outline)) - (let ((type (alist-get 'type item)) - (page (alist-get 'page item)) - (depth (alist-get 'depth item)) - (title (alist-get 'title item)) - (top (alist-get 'top item))) - (when (and (eq type 'goto-dest) (> page 0)) - (push (vector title (cons page top) (1+ depth) nil) output-data))))) - - (when (memq 'annots answer) - (let ((possible-annots (list '("Highlights" . highlight) - '("Underlines" . underline) - '("Squigglies" . squiggly) - '("Text notes" . text) - '("Strikeouts" . strike-out) - '("Links" . link) - '("ALL" . all))) - chosen-annots insert-contents pages-with-links) - (while (> (length possible-annots) 1) - (let* ((chosen-string (completing-read "Which types of annotations do you want? " - possible-annots nil t)) - (chosen-pair (assoc chosen-string possible-annots))) - (cond ((eq (cdr chosen-pair) 'all) - (dolist (annot possible-annots) - (when (and (cdr annot) (not (eq (cdr annot) 'all))) - (push (cdr annot) chosen-annots))) - (setq possible-annots nil)) - ((cdr chosen-pair) - (push (cdr chosen-pair) chosen-annots) - (setq possible-annots (delq chosen-pair possible-annots)) - (when (= 1 (length chosen-annots)) (push '("DONE") possible-annots))) - (t - (setq possible-annots nil))))) - - (setq insert-contents (y-or-n-p "Should we insert the annotations contents? ")) - - (dolist (item (pdf-info-getannots)) - (let* ((type (alist-get 'type item)) - (page (alist-get 'page item)) - (edges (or (org-noter--pdf-tools-edges-to-region (alist-get 'markup-edges item)) - (alist-get 'edges item))) - (top (nth 1 edges)) - (item-subject (alist-get 'subject item)) - (item-contents (alist-get 'contents item)) - name contents) - (when (and (memq type chosen-annots) (> page 0)) - (if (eq type 'link) - (cl-pushnew page pages-with-links) - (setq name (cond ((eq type 'highlight) "Highlight") - ((eq type 'underline) "Underline") - ((eq type 'squiggly) "Squiggly") - ((eq type 'text) "Text note") - ((eq type 'strike-out) "Strikeout"))) - - (when insert-contents - (setq contents (cons (pdf-info-gettext page edges) - (and (or (and item-subject (> (length item-subject) 0)) - (and item-contents (> (length item-contents) 0))) - (concat (or item-subject "") - (if (and item-subject item-contents) "\n" "") - (or item-contents "")))))) - - (push (vector (format "%s on page %d" name page) (cons page top) 'inside contents) - output-data))))) - - (dolist (page pages-with-links) - (let ((links (pdf-info-pagelinks page)) - type) - (dolist (link links) - (setq type (alist-get 'type link)) - (unless (eq type 'goto-dest) ;; NOTE(nox): Ignore internal links - (let* ((edges (alist-get 'edges link)) - (title (alist-get 'title link)) - (top (nth 1 edges)) - (target-page (alist-get 'page link)) - target heading-text) - - (unless (and title (> (length title) 0)) (setq title (pdf-info-gettext page edges))) - - (cond - ((eq type 'uri) - (setq target (alist-get 'uri link) - heading-text (format "Link on page %d: [[%s][%s]]" page target title))) - - ((eq type 'goto-remote) - (setq target (concat "file:" (alist-get 'filename link)) - heading-text (format "Link to document on page %d: [[%s][%s]]" page target title)) - (when target-page - (setq heading-text (concat heading-text (format " (target page: %d)" target-page))))) - - (t (error "Unexpected link type"))) - - (push (vector heading-text (cons page top) 'inside nil) output-data)))))))) - - - (when output-data - (if (memq 'annots answer) - (setq output-data - (sort output-data - (lambda (e1 e2) - (or (not (aref e1 1)) - (and (aref e2 1) - (org-noter--compare-locations '< (aref e1 1) (aref e2 1))))))) - (setq output-data (nreverse output-data))) - - (push (vector "Skeleton" nil 1 nil) output-data))) + (org-noter--with-valid-session + (let* ((ast (org-noter--parse-root)) + (top-level (org-element-property :level ast)) + (options '(("Outline" . (outline)) + ("Annotations" . (annots)) + ("Both" . (outline annots)))) + answer output-data) + (with-current-buffer (org-noter--session-doc-buffer session) + (setq answer (assoc (completing-read "What do you want to import? " options nil t) options)) + + (when (memq 'outline answer) + (dolist (item (pdf-info-outline)) + (let ((type (alist-get 'type item)) + (page (alist-get 'page item)) + (depth (alist-get 'depth item)) + (title (alist-get 'title item)) + (top (alist-get 'top item))) + (when (and (eq type 'goto-dest) (> page 0)) + (push (vector title (cons page top) (1+ depth) nil) output-data))))) + + (when (memq 'annots answer) + (let ((possible-annots (list '("Highlights" . highlight) + '("Underlines" . underline) + '("Squigglies" . squiggly) + '("Text notes" . text) + '("Strikeouts" . strike-out) + '("Links" . link) + '("ALL" . all))) + chosen-annots insert-contents pages-with-links) + (while (> (length possible-annots) 1) + (let* ((chosen-string (completing-read "Which types of annotations do you want? " + possible-annots nil t)) + (chosen-pair (assoc chosen-string possible-annots))) + (cond ((eq (cdr chosen-pair) 'all) + (dolist (annot possible-annots) + (when (and (cdr annot) (not (eq (cdr annot) 'all))) + (push (cdr annot) chosen-annots))) + (setq possible-annots nil)) + ((cdr chosen-pair) + (push (cdr chosen-pair) chosen-annots) + (setq possible-annots (delq chosen-pair possible-annots)) + (when (= 1 (length chosen-annots)) (push '("DONE") possible-annots))) + (t + (setq possible-annots nil))))) + + (setq insert-contents (y-or-n-p "Should we insert the annotations contents? ")) + + (dolist (item (pdf-info-getannots)) + (let* ((type (alist-get 'type item)) + (page (alist-get 'page item)) + (edges (or (org-noter--pdf-tools-edges-to-region (alist-get 'markup-edges item)) + (alist-get 'edges item))) + (top (nth 1 edges)) + (item-subject (alist-get 'subject item)) + (item-contents (alist-get 'contents item)) + name contents) + (when (and (memq type chosen-annots) (> page 0)) + (if (eq type 'link) + (cl-pushnew page pages-with-links) + (setq name (cond ((eq type 'highlight) "Highlight") + ((eq type 'underline) "Underline") + ((eq type 'squiggly) "Squiggly") + ((eq type 'text) "Text note") + ((eq type 'strike-out) "Strikeout"))) + + (when insert-contents + (setq contents (cons (pdf-info-gettext page edges) + (and (or (and item-subject (> (length item-subject) 0)) + (and item-contents (> (length item-contents) 0))) + (concat (or item-subject "") + (if (and item-subject item-contents) "\n" "") + (or item-contents "")))))) + + (push (vector (format "%s on page %d" name page) (cons page top) 'inside contents) + output-data))))) + + (dolist (page pages-with-links) + (let ((links (pdf-info-pagelinks page)) + type) + (dolist (link links) + (setq type (alist-get 'type link)) + (unless (eq type 'goto-dest) ;; NOTE(nox): Ignore internal links + (let* ((edges (alist-get 'edges link)) + (title (alist-get 'title link)) + (top (nth 1 edges)) + (target-page (alist-get 'page link)) + target heading-text) + + (unless (and title (> (length title) 0)) (setq title (pdf-info-gettext page edges))) - (with-current-buffer (org-noter--session-notes-buffer session) - ;; NOTE(nox): org-with-wide-buffer can't be used because we want to reset the - ;; narrow region to include the new headings - (widen) + (cond + ((eq type 'uri) + (setq target (alist-get 'uri link) + heading-text (format "Link on page %d: [[%s][%s]]" page target title))) + + ((eq type 'goto-remote) + (setq target (concat "file:" (alist-get 'filename link)) + heading-text (format "Link to document on page %d: [[%s][%s]]" page target title)) + (when target-page + (setq heading-text (concat heading-text (format " (target page: %d)" target-page))))) + + (t (error "Unexpected link type"))) + + (push (vector heading-text (cons page top) 'inside nil) output-data)))))))) + + + (when output-data + (if (memq 'annots answer) + (setq output-data + (sort output-data + (lambda (e1 e2) + (or (not (aref e1 1)) + (and (aref e2 1) + (org-noter--compare-locations '< (aref e1 1) (aref e2 1))))))) + (setq output-data (nreverse output-data))) + + (push (vector "Skeleton" nil 1 nil) output-data))) + + (with-current-buffer (org-noter--session-notes-buffer session) + ;; NOTE(nox): org-with-wide-buffer can't be used because we want to reset the + ;; narrow region to include the new headings + (widen) + (save-excursion + (goto-char (org-element-property :end ast)) + + (let (last-absolute-level + title location relative-level contents + level) + (dolist (data output-data) + (setq title (aref data 0) + location (aref data 1) + relative-level (aref data 2) + contents (aref data 3)) + + (if (symbolp relative-level) + (setq level (1+ last-absolute-level)) + (setq last-absolute-level (+ top-level relative-level) + level last-absolute-level)) + + (org-noter--insert-heading level title) + + (when location + (org-entry-put nil org-noter-property-note-location (org-noter--pretty-print-location location))) + + (when org-noter-doc-property-in-notes + (org-entry-put nil org-noter-property-doc-file (org-noter--session-property-text session)) + (org-entry-put nil org-noter--property-auto-save-last-location "nil")) + + (when (car contents) + (org-noter--insert-heading (1+ level) "Contents") + (insert (car contents))) + (when (cdr contents) + (org-noter--insert-heading (1+ level) "Comment") + (insert (cdr contents))))) + + (setq ast (org-noter--parse-root)) + (org-noter--narrow-to-root ast) + (goto-char (org-element-property :begin ast)) + (outline-hide-subtree) + (org-show-children 2)))))) + +(defun org-noter-create-skeleton-djvu () + (org-noter--with-valid-session + (let* ((ast (org-noter--parse-root)) + (top-level (org-element-property :level ast)) + output-data) + (require 'thingatpt) + (with-current-buffer (djvu-ref outline-buf) + (unless (string= (buffer-string) "") + (push (vector "Skeleton" nil 1) output-data) (save-excursion - (goto-char (org-element-property :end ast)) - - (let (last-absolute-level - title location relative-level contents - level) - (dolist (data output-data) - (setq title (aref data 0) - location (aref data 1) - relative-level (aref data 2) - contents (aref data 3)) - - (if (symbolp relative-level) - (setq level (1+ last-absolute-level)) - (setq last-absolute-level (+ top-level relative-level) - level last-absolute-level)) - - (org-noter--insert-heading level title) - - (when location - (org-entry-put nil org-noter-property-note-location (org-noter--pretty-print-location location))) - - (when org-noter-doc-property-in-notes - (org-entry-put nil org-noter-property-doc-file (org-noter--session-property-text session)) - (org-entry-put nil org-noter--property-auto-save-last-location "nil")) - - (when (car contents) - (org-noter--insert-heading (1+ level) "Contents") - (insert (car contents))) - (when (cdr contents) - (org-noter--insert-heading (1+ level) "Comment") - (insert (cdr contents))))) - - (setq ast (org-noter--parse-root)) - (org-noter--narrow-to-root ast) - (goto-char (org-element-property :begin ast)) - (outline-hide-subtree) - (org-show-children 2))))) + (goto-char (point-min)) + (while (not (looking-at "^$")) + (push (vector (string-trim-right (string-trim (thing-at-point 'line t)) " [[:digit:]]+") + (list (string-trim-left (string-trim (thing-at-point 'line t)) ".* ")) + (+ 2 (how-many " " (point-at-bol) (point-at-eol)))) output-data) + (next-line) + (move-beginning-of-line 1))))) + + (with-current-buffer (org-noter--session-notes-buffer session) + ;; NOTE(nox): org-with-wide-buffer can't be used because we want to reset the + ;; narrow region to include the new headings + (widen) + (save-excursion + (goto-char (org-element-property :end ast)) + + (let (last-absolute-level + title location relative-level contents + level) + + + (dolist (data (nreverse output-data)) + (setq title (aref data 0) + location (aref data 1) + relative-level (aref data 2)) + + (setq last-absolute-level (+ top-level relative-level) + level last-absolute-level) + + (org-noter--insert-heading level title) + + (when location + (org-entry-put nil org-noter-property-note-location (org-noter--pretty-print-location location))) + + (when org-noter-doc-property-in-notes + (org-entry-put nil org-noter-property-doc-file (org-noter--session-property-text session)) + (org-entry-put nil org-noter--property-auto-save-last-location "nil")))) + + (setq ast (org-noter--parse-root)) + (org-noter--narrow-to-root ast) + (goto-char (org-element-property :begin ast)) + (outline-hide-subtree) + (org-show-children 2)))))) (defun org-noter-insert-note (&optional precise-info) "Insert note associated with the current location. From 6ae07d31059610307a6b4853ed24c4ccd3245509 Mon Sep 17 00:00:00 2001 From: c1-g Date: Tue, 7 Sep 2021 03:35:01 +0000 Subject: [PATCH 018/453] Add org-noter-create-skeleton section in README. --- README.org | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.org b/README.org index 99630bb..6ec2bd2 100644 --- a/README.org +++ b/README.org @@ -2,8 +2,13 @@ ** What is org-noter? Org-noter is an org-mode document annotator. It is a rewrite of another project, [[https://github.com/rudolfochrist/interleave][Interleave]]. Read about org-noter’s feature/usage/customization in its README [[file:README-orig.org][here]] or go visit its repository at [[https://github.com/weirdNox/org-noter][github]]. -** Why? -I just want to use org-noter with djvu file. +** Added features + - `org-noter` can be used on djvu file. + - `org-noter-create-skeleton` now works on djvu and pdf file. +** Goals + - [ ] Precise location support + djvu.el have both graphical (via `djvu-image-mode`) and textual (default) modes so maybe + the textual mode should handle precise location like nov.el and graphical mode should do it like pdf-tools. ** Caveats *** Changed default keybindings The key @@html:@@i@@html:@@ (lowercase i) for inserting note in the document buffer has been changed to @@html:@@I@@html:@@ (uppercase i) since it interfered with Djview Image minor mode key. From d41fc10649c9c165cb4dc5cf08072fe619e2c46c Mon Sep 17 00:00:00 2001 From: c1-g Date: Tue, 7 Sep 2021 07:32:16 +0000 Subject: [PATCH 019/453] Verbatim README. --- README.org | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.org b/README.org index 6ec2bd2..82f6142 100644 --- a/README.org +++ b/README.org @@ -3,11 +3,11 @@ Org-noter is an org-mode document annotator. It is a rewrite of another project, [[https://github.com/rudolfochrist/interleave][Interleave]]. Read about org-noter’s feature/usage/customization in its README [[file:README-orig.org][here]] or go visit its repository at [[https://github.com/weirdNox/org-noter][github]]. ** Added features - - `org-noter` can be used on djvu file. - - `org-noter-create-skeleton` now works on djvu and pdf file. + - =org-noter= can be used on djvu file. + - =org-noter-create-skeleton= now works on djvu and pdf file. ** Goals - [ ] Precise location support - djvu.el have both graphical (via `djvu-image-mode`) and textual (default) modes so maybe + djvu.el have both graphical (via =djvu-image-mode=) and textual (default) modes so maybe the textual mode should handle precise location like nov.el and graphical mode should do it like pdf-tools. ** Caveats *** Changed default keybindings From ac8f413d9a447ec7fb1485000acd234e01f4bf0e Mon Sep 17 00:00:00 2001 From: c1-g Date: Sat, 11 Sep 2021 10:09:47 +0000 Subject: [PATCH 020/453] Simplify getting the title of the chapter. --- org-noter.el | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/org-noter.el b/org-noter.el index ed05af2..92bfd4c 100644 --- a/org-noter.el +++ b/org-noter.el @@ -1858,15 +1858,12 @@ defines if the text should be inserted inside the note." (buffer-substring-no-properties (mark) (point)))) ((eq (org-noter--session-doc-mode session) 'djvu-read-mode) + (require 'thingatpt) (if (region-active-p) (buffer-substring-no-properties (mark) (point)) (with-current-buffer (djvu-ref outline-buf) (djvu-goto-outline) - (string-join - (butlast - (split-string - (string-trim (thing-at-point 'line t)))) - " ")))))) + (string-trim-right (string-trim (thing-at-point 'line t)) " [[:digit:]]+")))))) force-new (location (org-noter--doc-approx-location (or precise-info 'interactive) (gv-ref force-new))) (view-info (org-noter--get-view-info (org-noter--get-current-view) location))) From 131454a6928fa22969b556aed921ce4470d21b80 Mon Sep 17 00:00:00 2001 From: c1-g Date: Sat, 11 Sep 2021 15:12:24 +0000 Subject: [PATCH 021/453] goto-location: Also move point. --- org-noter.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/org-noter.el b/org-noter.el index 92bfd4c..50c327c 100644 --- a/org-noter.el +++ b/org-noter.el @@ -937,7 +937,8 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." ;; everything and would run org-noter--nov-scroll-handler. ((eq mode 'djvu-read-mode) - (djvu-goto-page (car location)))) + (djvu-goto-page (car location)) + (goto-char (cdr location)))) (redisplay))))) (defun org-noter--compare-location-cons (comp l1 l2) From 34e41a26286e3218d4d8fe0dba52c2b75ce76bb0 Mon Sep 17 00:00:00 2001 From: c1-g Date: Sat, 11 Sep 2021 16:02:26 +0000 Subject: [PATCH 022/453] insert-note: Change cond to pcase. This is to avoid evaluating (org-noter--session-doc-mode session) too much. --- org-noter.el | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/org-noter.el b/org-noter.el index 50c327c..0a458ba 100644 --- a/org-noter.el +++ b/org-noter.el @@ -1849,22 +1849,19 @@ defines if the text should be inserted inside the note." (let* ((ast (org-noter--parse-root)) (contents (org-element-contents ast)) (window (org-noter--get-notes-window 'force)) (selected-text - (cond - ((eq (org-noter--session-doc-mode session) 'pdf-view-mode) - (when (pdf-view-active-region-p) - (mapconcat 'identity (pdf-view-active-region-text) ? ))) - - ((eq (org-noter--session-doc-mode session) 'nov-mode) - (when (region-active-p) - (buffer-substring-no-properties (mark) (point)))) + (pcase (org-noter--session-doc-mode session) + ('pdf-view-mode + (when (pdf-view-active-region-p) + (mapconcat 'identity (pdf-view-active-region-text) ? ))) + + ('nov-mode + (when (region-active-p) + (buffer-substring-no-properties (mark) (point)))) - ((eq (org-noter--session-doc-mode session) 'djvu-read-mode) - (require 'thingatpt) - (if (region-active-p) - (buffer-substring-no-properties (mark) (point)) - (with-current-buffer (djvu-ref outline-buf) - (djvu-goto-outline) - (string-trim-right (string-trim (thing-at-point 'line t)) " [[:digit:]]+")))))) + ('djvu-read-mode + (when (region-active-p) + (buffer-substring-no-properties (mark) (point)))))) + force-new (location (org-noter--doc-approx-location (or precise-info 'interactive) (gv-ref force-new))) (view-info (org-noter--get-view-info (org-noter--get-current-view) location))) From 7b535d624ccd7a11e7f47b7f15afdf3bcd84f0f4 Mon Sep 17 00:00:00 2001 From: c1-g Date: Sat, 11 Sep 2021 16:04:08 +0000 Subject: [PATCH 023/453] insert-note: Merge cases of djvu-read-mode and nov-mode together --- org-noter.el | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/org-noter.el b/org-noter.el index 0a458ba..bc85c48 100644 --- a/org-noter.el +++ b/org-noter.el @@ -1854,13 +1854,9 @@ defines if the text should be inserted inside the note." (when (pdf-view-active-region-p) (mapconcat 'identity (pdf-view-active-region-text) ? ))) - ('nov-mode + ((or 'nov-mode 'djvu-read-mode) (when (region-active-p) - (buffer-substring-no-properties (mark) (point)))) - - ('djvu-read-mode - (when (region-active-p) - (buffer-substring-no-properties (mark) (point)))))) + (buffer-substring-no-properties (mark) (point)))))) force-new (location (org-noter--doc-approx-location (or precise-info 'interactive) (gv-ref force-new))) From db0ef97f3b60151ae7d183244bca6989067adc70 Mon Sep 17 00:00:00 2001 From: c1-g Date: Sun, 12 Sep 2021 03:00:43 +0000 Subject: [PATCH 024/453] insert-note: Add an option to set note title. This is for non-interactive use. --- org-noter.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/org-noter.el b/org-noter.el index bc85c48..67ef0b5 100644 --- a/org-noter.el +++ b/org-noter.el @@ -1825,7 +1825,7 @@ want to kill." (outline-hide-subtree) (org-show-children 2)))))) -(defun org-noter-insert-note (&optional precise-info) +(defun org-noter-insert-note (&optional precise-info note-title) "Insert note associated with the current location. This command will prompt for a title of the note and then insert @@ -1890,8 +1890,8 @@ defines if the text should be inserted inside the note." default-begin begin)))))) (setq collection (nreverse collection) - title (if org-noter-insert-note-no-questions - default + title (if (or org-noter-insert-note-no-questions note-title) + (or default note-title) (completing-read "Note: " collection nil nil nil nil default)) selection (unless org-noter-insert-note-no-questions (cdr (assoc title collection)))) From 4a8efe0c5c667594c73f1828022627c8341a7cec Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 13 Sep 2021 01:54:12 +0000 Subject: [PATCH 025/453] Divide code via outline-mode headings. --- org-noter.el | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/org-noter.el b/org-noter.el index 67ef0b5..d91798e 100644 --- a/org-noter.el +++ b/org-noter.el @@ -61,7 +61,7 @@ (defvar nov-file-name) ;; -------------------------------------------------------------------------------- -;; NOTE(nox): User variables +;;; User variables (defgroup org-noter nil "A synchronized, external annotator" :group 'convenience @@ -224,7 +224,7 @@ The title used will be the default one." :group 'org-noter) ;; -------------------------------------------------------------------------------- -;; NOTE(nox): Integration with other packages +;;; Integration with other packages (defcustom org-noter--check-location-property-hook nil "TODO" :group 'org-noter @@ -271,7 +271,7 @@ The title used will be the default one." :type 'hook) ;; -------------------------------------------------------------------------------- -;; NOTE(nox): Private variables or constants +;;; Private variables or constants (cl-defstruct org-noter--session id frame doc-buffer notes-buffer ast modified-tick doc-mode display-name notes-file-path property-text level num-notes-in-view window-behavior window-location doc-split-fraction auto-save-last-location @@ -326,7 +326,7 @@ The title used will be the default one." "Text property used to mark the headings with open sessions.") ;; -------------------------------------------------------------------------------- -;; NOTE(nox): Utility functions +;;; Utility functions (defun org-noter--get-new-id () (catch 'break (while t @@ -1335,7 +1335,7 @@ relative to." (throw 'other-frame frame))))) ;; -------------------------------------------------------------------------------- -;; NOTE(nox): User commands +;;; User commands (defun org-noter-set-start-location (&optional arg) "When opening a session with this document, go to the current location. With a prefix ARG, remove start location." From 924dc5550345174567a7b145dbbce4ded80590d9 Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 13 Sep 2021 09:21:21 +0700 Subject: [PATCH 026/453] Revert b2427be: Change insert-note default keybind to (uppercase i). --- README.org | 14 +++++++++----- org-noter.el | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/README.org b/README.org index 82f6142..37ebd60 100644 --- a/README.org +++ b/README.org @@ -6,12 +6,16 @@ Read about org-noter’s feature/usage/customization in its README [[file:README - =org-noter= can be used on djvu file. - =org-noter-create-skeleton= now works on djvu and pdf file. ** Goals - - [ ] Precise location support - djvu.el have both graphical (via =djvu-image-mode=) and textual (default) modes so maybe - the textual mode should handle precise location like nov.el and graphical mode should do it like pdf-tools. + - [ ] Precise location support djvu.el have both graphical (via + =djvu-image-mode=) and textual (default) modes so maybe the textual + mode should handle precise location like nov.el and graphical mode + should do it like pdf-tools. ** Caveats -*** Changed default keybindings -The key @@html:@@i@@html:@@ (lowercase i) for inserting note in the document buffer has been changed to @@html:@@I@@html:@@ (uppercase i) since it interfered with Djview Image minor mode key. +*** Conflicting keybindings +The key @@html:@@i@@html:@@ (lowercase i) for inserting +note in the document buffer will conflict with the default key to +enter =djvu-image-mode=. So either you rebind one of them or you can +just enter =djvu-image-mode= manually via @@html:@@M-x djvu-image-mode@@html:@@ diff --git a/org-noter.el b/org-noter.el index d91798e..7822e34 100644 --- a/org-noter.el +++ b/org-noter.el @@ -2129,7 +2129,7 @@ As such, it will only work when the notes window exists." "Minor mode for the document buffer. Keymap: \\{org-noter-doc-mode-map}" - :keymap `((,(kbd "I") . org-noter-insert-note) + :keymap `((,(kbd "i") . org-noter-insert-note) (,(kbd "C-i") . org-noter-insert-note-toggle-no-questions) (,(kbd "M-i") . org-noter-insert-precise-note) (,(kbd "q") . org-noter-kill-session) From 824da2b9f0491097620b45002326266c832b3701 Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 13 Sep 2021 15:02:26 +0700 Subject: [PATCH 027/453] Merge pdf-util-tooltip-arrow-with-image-left with show-arrow. --- org-noter.el | 95 +++++++++++++++++++++++++--------------------------- 1 file changed, 45 insertions(+), 50 deletions(-) diff --git a/org-noter.el b/org-noter.el index c0d3906..67db776 100644 --- a/org-noter.el +++ b/org-noter.el @@ -903,7 +903,7 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (eq window (posn-window (event-start event))))) (setq event (read-event "Click where you want the start of the note to be!"))) (posn-point (event-start event)))) - +` ((eq mode 'djvu-read-mode) (if (region-active-p) (min (mark) (point)) @@ -912,59 +912,54 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (setq event (read-event "Click where you want the start of the note to be!"))) (posn-point (event-start event))))))))) -(defun pdf-util-tooltip-arrow-with-image-left (image-top &optional timeout image-left) - (pdf-util-assert-pdf-window) - (when (floatp image-top) - (setq image-top - (round (* image-top (cdr (pdf-view-image-size)))))) - (when (floatp image-left) - (setq image-left - (round (* image-left (car (pdf-view-image-size)))))) - (let* (x-gtk-use-system-tooltips ;allow for display property in tooltip - (dx (if image-left - image-left - (+ (or (car (window-margins)) 0) - (car (window-fringes))))) - (dy image-top) - (pos (list dx dy dx (+ dy (* 2 (frame-char-height))))) - (vscroll - (pdf-util-required-vscroll pos)) - (tooltip-frame-parameters - `((border-width . 0) - (internal-border-width . 0) - ,@tooltip-frame-parameters)) - (tooltip-hide-delay (or timeout 3))) - (when vscroll - (image-set-window-vscroll vscroll)) - (setq dy (max 0 (- dy - (cdr (pdf-view-image-offset)) - (window-vscroll nil t) - (frame-char-height)))) - (when (overlay-get (pdf-view-current-overlay) 'before-string) - (let* ((e (window-inside-pixel-edges)) - (xw (pdf-util-with-edges (e) e-width))) - (cl-incf dx (/ (- xw (car (pdf-view-image-size t))) 2)))) - (pdf-util-tooltip-in-window - (propertize - " " 'display (propertize - "\u2192" ;;right arrow - 'display '(height 2) - 'face `(:foreground - "orange red" - :background - ,(if (bound-and-true-p pdf-view-midnight-minor-mode) - (cdr pdf-view-midnight-colors) - "white")))) - dx dy))) - -;;(advice-add 'pdf-util-tooltip-arrow :override pdf-util-tooltip-arrow-with-image-left) - (defun org-noter--show-arrow () (when (and org-noter--arrow-location (window-live-p (aref org-noter--arrow-location 1))) (with-selected-window (aref org-noter--arrow-location 1) - (pdf-util-tooltip-arrow-with-image-left (aref org-noter--arrow-location 2) nil (aref org-noter--arrow-location 3)))) - (setq org-noter--arrow-location nil)) + ;; From `pdf-util-tooltip-arrow'. + (pdf-util-assert-pdf-window) + (let* (x-gtk-use-system-tooltips + (image-top (if (floatp (aref org-noter--arrow-location 2)) + (round (* (aref org-noter--arrow-location 2) + (cdr (pdf-view-image-size)))))) + (image-left (if (floatp (aref org-noter--arrow-location 3)) + (round (* (aref org-noter--arrow-location 3) (car (pdf-view-image-size)))))) + + (dx (or image-left + (+ (or (car (window-margins)) 0) + (car (window-fringes))))) + (dy (or image-top 0)) + (pos (list dx dy dx (+ dy (* 2 (frame-char-height))))) + (vscroll (pdf-util-required-vscroll pos)) + (tooltip-frame-parameters + `((border-width . 0) + (internal-border-width . 0) + ,@tooltip-frame-parameters)) + (tooltip-hide-delay 3)) + + (when vscroll + (image-set-window-vscroll vscroll)) + (setq dy (max 0 (- dy + (cdr (pdf-view-image-offset)) + (window-vscroll nil t) + (frame-char-height)))) + (when (overlay-get (pdf-view-current-overlay) 'before-string) + (let* ((e (window-inside-pixel-edges)) + (xw (pdf-util-with-edges (e) e-width))) + (cl-incf dx (/ (- xw (car (pdf-view-image-size t))) 2)))) + (pdf-util-tooltip-in-window + (propertize + " " 'display (propertize + "\u2192" ;; right arrow + 'display '(height 2) + 'face `(:foreground + "orange red" + :background + ,(if (bound-and-true-p pdf-view-midnight-minor-mode) + (cdr pdf-view-midnight-colors) + "white")))) + dx dy)) + (setq org-noter--arrow-location nil)))) (defun get-location-top (location) "Get the top coordinate given a LOCATION vector of form [page top left] or [page top]." From f59cedf292ebd84c72024c7e1ec08e75e756cd6d Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 13 Sep 2021 16:13:22 +0700 Subject: [PATCH 028/453] Use region as precise-info if it is active. --- org-noter.el | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/org-noter.el b/org-noter.el index 67db776..6c6335e 100644 --- a/org-noter.el +++ b/org-noter.el @@ -882,6 +882,7 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (cons (cadar edges) (caar edges))) + (while (not (and (eq 'mouse-1 (car event)) (eq window (posn-window (event-start event))))) (setq event (read-event "Click where you want the start of the note to be!"))) @@ -898,15 +899,15 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." ((eq mode 'nov-mode) (if (region-active-p) - (min (mark) (point)) + (cons (mark) (point)) (while (not (and (eq 'mouse-1 (car event)) (eq window (posn-window (event-start event))))) (setq event (read-event "Click where you want the start of the note to be!"))) (posn-point (event-start event)))) -` + ((eq mode 'djvu-read-mode) (if (region-active-p) - (min (mark) (point)) + (cons (mark) (point)) (while (not (and (eq 'mouse-1 (car event)) (eq window (posn-window (event-start event))))) (setq event (read-event "Click where you want the start of the note to be!"))) @@ -962,17 +963,17 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (setq org-noter--arrow-location nil)))) (defun get-location-top (location) - "Get the top coordinate given a LOCATION vector of form [page top left] or [page top]." + "Get the top coordinate given a LOCATION of form (page top . left) or (page . top)." (if (listp (cdr location)) (cadr location) (cdr location))) (defun get-location-page (location) - "Get the page number given a LOCATION vector of form [page top left] or [page top]." + "Get the page number given a LOCATION of form (page top . left) or (page . top)." (car location)) (defun get-location-left (location) - "Get the left coordinate given a LOCATION vector of form [page top left] or [page top]. If later form of vector is passed return 0." + "Get the left coordinate given a LOCATION of form (page top . left) or (page . top). If later form of vector is passed return 0." (if (listp (cdr location)) (cddr location) 0)) @@ -1015,7 +1016,7 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." ((eq mode 'djvu-read-mode) (djvu-goto-page (car location)) - (goto-char (cdr location)))) + (goto-char (get-location-top location)))) (redisplay))))) (defun org-noter--compare-location-cons (comp l1 l2) From f38f313da09fa99736c123a48ebd396e650e5d3a Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 13 Sep 2021 16:16:14 +0700 Subject: [PATCH 029/453] Add prefix to get-location functions. --- org-noter.el | 54 ++++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/org-noter.el b/org-noter.el index 6c6335e..18f3538 100644 --- a/org-noter.el +++ b/org-noter.el @@ -800,12 +800,12 @@ properties, by a margin of NEWLINES-NUMBER." (or (run-hook-with-args-until-success 'org-noter--pretty-print-location-hook location) (format "%s" (cond ((memq (org-noter--session-doc-mode session) '(doc-view-mode pdf-view-mode djvu-read-mode)) - (if (or (not (get-location-top location)) (<= (get-location-top location) 0)) + (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 0)) (car location) location)) ((eq (org-noter--session-doc-mode session) 'nov-mode) - (if (or (not (get-location-top location)) (<= (get-location-top location) 1)) + (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 1)) (get-location-page location) location))))))) @@ -962,7 +962,7 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." dx dy)) (setq org-noter--arrow-location nil)))) -(defun get-location-top (location) +(defun org-noter--get-location-top (location) "Get the top coordinate given a LOCATION of form (page top . left) or (page . top)." (if (listp (cdr location)) (cadr location) @@ -972,7 +972,7 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." "Get the page number given a LOCATION of form (page top . left) or (page . top)." (car location)) -(defun get-location-left (location) +(defun org-noter--get-location-left (location) "Get the left coordinate given a LOCATION of form (page top . left) or (page . top). If later form of vector is passed return 0." (if (listp (cdr location)) (cddr location) @@ -988,8 +988,8 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." ((run-hook-with-args-until-success 'org-noter--doc-goto-location-hook mode location)) ((memq mode '(doc-view-mode pdf-view-mode)) - (let ((top (get-location-top location)) - (left (get-location-left location))) + (let ((top (org-noter--get-location-top location)) + (left (org-noter--get-location-left location))) (if (eq mode 'doc-view-mode) (doc-view-goto-page (get-location-page location)) @@ -1009,14 +1009,14 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." ((eq mode 'nov-mode) (setq nov-documents-index (get-location-page location)) (nov-render-document) - (goto-char (get-location-top location)) + (goto-char (org-noter--get-location-top location)) (recenter)) ;; NOTE(nox): This needs to be here, because it would be issued anyway after ;; everything and would run org-noter--nov-scroll-handler. ((eq mode 'djvu-read-mode) (djvu-goto-page (car location)) - (goto-char (get-location-top location)))) + (goto-char (org-noter--get-location-top location)))) (redisplay))))) (defun org-noter--compare-location-cons (comp l1 l2) @@ -1025,43 +1025,43 @@ See `org-noter--compare-locations'" (cl-assert (and (consp l1) (consp l2))) (cond ((eq comp '=) (and (= (get-location-page l1) (get-location-page l2)) - (= (get-location-top l1) (get-location-top l2)) - (= (get-location-left l1) (get-location-left l2)))) + (= (org-noter--get-location-top l1) (org-noter--get-location-top l2)) + (= (org-noter--get-location-left l1) (org-noter--get-location-left l2)))) ((eq comp '<) (or (< (get-location-page l1) (get-location-page l2)) (and (= (get-location-page l1) (get-location-page l2)) - (< (get-location-top l1) (get-location-top l2))) + (< (org-noter--get-location-top l1) (org-noter--get-location-top l2))) (and (= (get-location-page l1) (get-location-page l2)) - (= (get-location-top l1) (get-location-top l2)) - (< (get-location-left l1) (get-location-left l2))))) + (= (org-noter--get-location-top l1) (org-noter--get-location-top l2)) + (< (org-noter--get-location-left l1) (org-noter--get-location-left l2))))) ((eq comp '<=) (or (< (get-location-page l1) (get-location-page l2)) (and (= (get-location-page l1) (get-location-page l2)) - (<= (get-location-top l1) (get-location-top l2))) + (<= (org-noter--get-location-top l1) (org-noter--get-location-top l2))) (and (= (get-location-page l1) (get-location-page l2)) - (= (get-location-top l1) (get-location-top l2)) - (<= (get-location-left l1) (get-location-left l2))))) + (= (org-noter--get-location-top l1) (org-noter--get-location-top l2)) + (<= (org-noter--get-location-left l1) (org-noter--get-location-left l2))))) ((eq comp '>) (or (> (get-location-page l1) (get-location-page l2)) (and (= (get-location-page l1) (get-location-page l2)) - (> (get-location-top l1) (get-location-top l2))) + (> (org-noter--get-location-top l1) (org-noter--get-location-top l2))) (and (= (get-location-page l1) (get-location-page l2)) - (= (get-location-top l1) (get-location-top l2)) - (> (get-location-left l1) (get-location-left l2))))) + (= (org-noter--get-location-top l1) (org-noter--get-location-top l2)) + (> (org-noter--get-location-left l1) (org-noter--get-location-left l2))))) ((eq comp '>=) (or (> (get-location-page l1) (get-location-page l2)) (and (= (get-location-page l1) (get-location-page l2)) - (>= (get-location-top l1) (get-location-top l2))) + (>= (org-noter--get-location-top l1) (org-noter--get-location-top l2))) (and (= (get-location-page l1) (get-location-page l2)) - (= (get-location-top l1) (get-location-top l2)) - (>= (get-location-left l1) (get-location-left l2))))) + (= (org-noter--get-location-top l1) (org-noter--get-location-top l2)) + (>= (org-noter--get-location-left l1) (org-noter--get-location-left l2))))) ((eq comp '>f) (or (> (get-location-page l1) (get-location-page l2)) (and (= (get-location-page l1) (get-location-page l2)) - (< (get-location-top l1) (get-location-top l2))) + (< (org-noter--get-location-top l1) (org-noter--get-location-top l2))) (and (= (get-location-page l1) (get-location-page l2)) - (= (get-location-top l1) (get-location-top l2)) - (< (get-location-left l1) (get-location-left l2))))) + (= (org-noter--get-location-top l1) (org-noter--get-location-top l2)) + (< (org-noter--get-location-left l1) (org-noter--get-location-left l2))))) (t (error "Comparison operator %s not known" comp)))) (defun org-noter--compare-locations (comp l1 l2) @@ -1171,9 +1171,9 @@ document property) will be opened." point location view)) (cdr hook-result)) ((eq (aref view 0) 'paged) - (> (get-location-top location) point)) + (> (org-noter--get-location-top location) point)) ((eq (aref view 0) 'nov) - (> (get-location-top location) (+ (* point (- (cdr (aref view 2)) (cdr (aref view 1)))) + (> (org-noter--get-location-top location) (+ (* point (- (cdr (aref view 2)) (cdr (aref view 1)))) (cdr (aref view 1)))))))) (defun org-noter--relative-position-to-view (location view) From 49bc6ee2ecfc12445d4483550215b887080c9c06 Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 13 Sep 2021 16:48:42 +0700 Subject: [PATCH 030/453] Allow :NOTER_DOCUMENT: to be in file-level :PROPERTIES: drawer See the issue. https://github.com/weirdNox/org-noter/issues/143 --- org-noter.el | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/org-noter.el b/org-noter.el index 18f3538..0ca0571 100644 --- a/org-noter.el +++ b/org-noter.el @@ -509,7 +509,8 @@ If nil, the session used will be `org-noter--session'." (catch 'break (org-back-to-heading t) (while t - (when (string= (org-entry-get nil org-noter-property-doc-file) wanted-prop) + (when (string= (or (org-entry-get nil org-noter-property-doc-file) + (cadar (org-collect-keywords (list org-noter-property-doc-file)))) wanted-prop) (setq root-pos (copy-marker (point)))) (unless (org-up-heading-safe) (throw 'break t)))))))) @@ -774,7 +775,8 @@ properties, by a margin of NEWLINES-NUMBER." (setq org-noter--nov-timer (run-with-timer 0.25 nil 'org-noter--doc-location-change-handler)))) (defsubst org-noter--doc-file-property (headline) - (org-element-property (intern (concat ":" org-noter-property-doc-file)) headline)) + (or (org-element-property (intern (concat ":" org-noter-property-doc-file)) headline) + (cadar (org-collect-keywords (list org-noter-property-doc-file))))) (defun org-noter--check-location-property (arg) (let ((property (if (stringp arg) arg @@ -1389,8 +1391,9 @@ relative to." (insert-file-contents notes-path) (catch 'break (while (re-search-forward (org-re-property org-noter-property-doc-file) nil t) - (when (file-equal-p (expand-file-name (match-string 3) (file-name-directory notes-path)) - document-path) + (when (file-equal-p (or (expand-file-name (match-string 3) (file-name-directory notes-path)) + (cadar (org-collect-keywords '(org-noter-property-doc-file)))) + document-path) ;; NOTE(nox): This notes file has the document we want! (throw 'break t))))))) @@ -1398,7 +1401,8 @@ relative to." (and doc-prop (not (file-directory-p doc-prop)) (file-readable-p doc-prop))) (defun org-noter--get-or-read-document-property (inherit-prop &optional force-new) - (let ((doc-prop (and (not force-new) (org-entry-get nil org-noter-property-doc-file inherit-prop)))) + (let ((doc-prop (and (not force-new) (or (org-entry-get nil org-noter-property-doc-file inherit-prop) + (cadar (org-collect-keywords (list org-noter-property-doc-file))))))) (unless (org-noter--check-doc-prop doc-prop) (setq doc-prop nil) @@ -2189,7 +2193,9 @@ As such, it will only work when the notes window exists." (interactive) (org-noter--with-selected-notes-window "No notes window exists" - (if (string= (org-entry-get nil org-noter-property-doc-file t) (org-noter--session-property-text session)) + (if (string= (or (org-entry-get nil org-noter-property-doc-file t) + (org-collect-keywords (list org-noter-property-doc-file))) + (org-noter--session-property-text session)) (let ((location (org-noter--parse-location-property (org-noter--get-containing-heading)))) (if location (org-noter--doc-goto-location location) From bec9767940561ebb88a5dbea5eb52404d73b90f8 Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 13 Sep 2021 16:57:29 +0700 Subject: [PATCH 031/453] Make commit 49bc6ee works with org-noter-sync-current-note. --- org-noter.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org-noter.el b/org-noter.el index 0ca0571..92a8789 100644 --- a/org-noter.el +++ b/org-noter.el @@ -2194,7 +2194,7 @@ As such, it will only work when the notes window exists." (org-noter--with-selected-notes-window "No notes window exists" (if (string= (or (org-entry-get nil org-noter-property-doc-file t) - (org-collect-keywords (list org-noter-property-doc-file))) + (cadar (org-collect-keywords (list org-noter-property-doc-file)))) (org-noter--session-property-text session)) (let ((location (org-noter--parse-location-property (org-noter--get-containing-heading)))) (if location From e3ac0aa87aa36b334dc14dc7a53a417b5bd0306c Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 13 Sep 2021 17:05:05 +0700 Subject: [PATCH 032/453] Mention commit 49bc6ee in README. --- README.org | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.org b/README.org index 37ebd60..ab01ea9 100644 --- a/README.org +++ b/README.org @@ -5,12 +5,18 @@ Read about org-noter’s feature/usage/customization in its README [[file:README ** Added features - =org-noter= can be used on djvu file. - =org-noter-create-skeleton= now works on djvu and pdf file. + - Allow :NOTER_DOCUMENT: to be in file-level :PROPERTIES: drawer + See the issue https://github.com/weirdNox/org-noter/issues/143 for details. ** Goals - [ ] Precise location support djvu.el have both graphical (via =djvu-image-mode=) and textual (default) modes so maybe the textual mode should handle precise location like nov.el and graphical mode should do it like pdf-tools. ** Caveats +*** This is a fork of a fork 🍴 +The branch =djvu= (the only one I’d worked on so far) of this repository is based on Ahmed Shariff’s pull request of org-noter +(See https://github.com/weirdNox/org-noter/pull/129). + *** Conflicting keybindings The key @@html:@@i@@html:@@ (lowercase i) for inserting note in the document buffer will conflict with the default key to From 52a14db7f6b7c092a3cc358535e022134e5e7296 Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 13 Sep 2021 20:03:36 +0700 Subject: [PATCH 033/453] Add org-noter-nov-ov.el Highlight your precise notes in nov with org-noter-nov-ov.el --- other/org-noter-nov-ov.el | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 other/org-noter-nov-ov.el diff --git a/other/org-noter-nov-ov.el b/other/org-noter-nov-ov.el new file mode 100644 index 0000000..8277041 --- /dev/null +++ b/other/org-noter-nov-ov.el @@ -0,0 +1,39 @@ +;;; org-noter-nov-ov.el --- Module to highlight text in nov-mode with notes -*- lexical-binding: t; -*- + +;; Copyright (C) 2021 Charlie Gordon + +;; Author: Charlie Gordon +;; Keywords: multimedia + +;; 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, +;; 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 . + +;;; Commentary: + +;; + +;;; Code: +(require 'org-noter) +(require 'nov) + +(defun org-noter-nov-make-ov () + "TODO" + + + + ) + + +(provide 'org-noter-nov-ov) +;;; org-noter-nov-ov.el ends here + From 3ebe3547129333d76d50829134aec46bafb5b098 Mon Sep 17 00:00:00 2001 From: c1-g Date: Tue, 14 Sep 2021 07:20:48 +0700 Subject: [PATCH 034/453] Initial commit. --- other/org-noter-nov-ov.el | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/other/org-noter-nov-ov.el b/other/org-noter-nov-ov.el index 8277041..a1f29f3 100644 --- a/other/org-noter-nov-ov.el +++ b/other/org-noter-nov-ov.el @@ -20,19 +20,48 @@ ;;; Commentary: -;; +;; Highlight your precise notes in nov with org-noter-nov-ov.el ;;; Code: (require 'org-noter) (require 'nov) +(require 'seq) + +(defun org-noter-nov-go-to-origin-note (overlay) + (org-noter--with-selected-notes-window + (org-with-wide-buffer + (goto-char (overlay-get overlay 'origin))))) (defun org-noter-nov-make-ov () "TODO" - - - - ) + (org-noter--with-valid-session + (when-let* ((is-this-nov (eq (org-noter--session-doc-mode session) 'nov-mode)) + (origin-note-location (org-element-property :begin (org-element-at-point))) + (location-property (org-entry-get nil org-noter-property-note-location nil t)) + (location-cons (cdr (read location-property))) + (beg (if (consp location-cons) + (car location-cons) + location-cons)) + (end (if (consp location-cons) + (cdr location-cons) + (1+ beg))) + (ov (make-overlay beg end (org-noter--session-doc-buffer session))) + (hl-color "yellow")) + + (overlay-put ov 'button ov) + (overlay-put ov 'category 'default-button) + (overlay-put ov 'face (list :background (if org-noter-insert-note-no-questions + hl-color + (setq hl-color (read-color "Highlight color: "))) + :foreground (readable-foreground-color hl-color))) + (overlay-put ov 'mouse-face (list :background (setq hl-color (color-lighten-name hl-color 5)) + :foreground (readable-foreground-color hl-color))) + + (overlay-put ov 'origin origin-note-location) + + (overlay-put ov 'action #'org-noter-nov-go-to-origin-note)))) +(add-hook 'org-noter-insert-heading-hook #'org-noter-nov-make-ov) (provide 'org-noter-nov-ov) ;;; org-noter-nov-ov.el ends here From 55cfe57f9b5e5b926db5e1b24175e27cde2bd7b1 Mon Sep 17 00:00:00 2001 From: c1-g Date: Tue, 14 Sep 2021 07:52:10 +0700 Subject: [PATCH 035/453] Rename org-noter-nov-ov.el to org-noter-nov-overlay.el. --- org-noter.el | 5 +- other/org-noter-nov-ov.el | 68 ------------------------- other/org-noter-nov-overlay.el | 91 ++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 69 deletions(-) delete mode 100644 other/org-noter-nov-ov.el create mode 100644 other/org-noter-nov-overlay.el diff --git a/org-noter.el b/org-noter.el index 92a8789..cfc4fe3 100644 --- a/org-noter.el +++ b/org-noter.el @@ -746,7 +746,10 @@ properties, by a margin of NEWLINES-NUMBER." (numberp (cdr precise-info))) precise-info 0))) ((eq major-mode 'nov-mode) - (cons nov-documents-index (if (integerp precise-info) + (cons nov-documents-index (if (or (numberp precise-info) + (and (consp precise-info) + (numberp (car precise-info)) + (numberp (cdr precise-info)))) precise-info (max 1 (/ (+ (window-start) (window-end nil t)) 2))))) diff --git a/other/org-noter-nov-ov.el b/other/org-noter-nov-ov.el deleted file mode 100644 index a1f29f3..0000000 --- a/other/org-noter-nov-ov.el +++ /dev/null @@ -1,68 +0,0 @@ -;;; org-noter-nov-ov.el --- Module to highlight text in nov-mode with notes -*- lexical-binding: t; -*- - -;; Copyright (C) 2021 Charlie Gordon - -;; Author: Charlie Gordon -;; Keywords: multimedia - -;; 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, -;; 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 . - -;;; Commentary: - -;; Highlight your precise notes in nov with org-noter-nov-ov.el - -;;; Code: -(require 'org-noter) -(require 'nov) -(require 'seq) - -(defun org-noter-nov-go-to-origin-note (overlay) - (org-noter--with-selected-notes-window - (org-with-wide-buffer - (goto-char (overlay-get overlay 'origin))))) - -(defun org-noter-nov-make-ov () - "TODO" - (org-noter--with-valid-session - (when-let* ((is-this-nov (eq (org-noter--session-doc-mode session) 'nov-mode)) - (origin-note-location (org-element-property :begin (org-element-at-point))) - (location-property (org-entry-get nil org-noter-property-note-location nil t)) - (location-cons (cdr (read location-property))) - (beg (if (consp location-cons) - (car location-cons) - location-cons)) - (end (if (consp location-cons) - (cdr location-cons) - (1+ beg))) - (ov (make-overlay beg end (org-noter--session-doc-buffer session))) - (hl-color "yellow")) - - (overlay-put ov 'button ov) - (overlay-put ov 'category 'default-button) - (overlay-put ov 'face (list :background (if org-noter-insert-note-no-questions - hl-color - (setq hl-color (read-color "Highlight color: "))) - :foreground (readable-foreground-color hl-color))) - (overlay-put ov 'mouse-face (list :background (setq hl-color (color-lighten-name hl-color 5)) - :foreground (readable-foreground-color hl-color))) - - (overlay-put ov 'origin origin-note-location) - - (overlay-put ov 'action #'org-noter-nov-go-to-origin-note)))) - -(add-hook 'org-noter-insert-heading-hook #'org-noter-nov-make-ov) - -(provide 'org-noter-nov-ov) -;;; org-noter-nov-ov.el ends here - diff --git a/other/org-noter-nov-overlay.el b/other/org-noter-nov-overlay.el new file mode 100644 index 0000000..a4ba311 --- /dev/null +++ b/other/org-noter-nov-overlay.el @@ -0,0 +1,91 @@ +;;; org-noter-nov-overlay.el --- Module to highlight text in nov-mode with notes -*- lexical-binding: t; -*- + +;; Copyright (C) 2021 Charlie Gordon + +;; Author: Charlie Gordon +;; Keywords: multimedia + +;; 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, +;; 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 . + +;;; Commentary: + +;; Highlight your precise notes in nov with org-noter-nov-overlay.el + +;;; Code: +(require 'org-noter) +(require 'nov nil t) +(require 'seq) + +(defcustom org-noter-nov-overlay-color-property "NOTER_OVERLAY" + "A property that specifies the overlay color for `org-noter-nov-make-ov'.") + +(defcustom org-noter-nov-overlay-default-color "yellow" + "Name of the default background color of the overlay `org-noter-nov-make-ov' makes. + +Should be one of the element in `defined-colors'.") + +(defun org-noter-nov-make-overlay () + "TODO" + (org-noter--with-selected-notes-window + "No notes window exists" + (when (eq (org-noter--session-doc-mode session) 'nov-mode) + (when-let* ((location-property (org-entry-get nil org-noter-property-note-location nil t)) + (location-cons (cdr (read location-property))) + (beg (if (consp location-cons) + (car location-cons) + location-cons)) + (end (if (consp location-cons) + (cdr location-cons) + (1+ beg))) + (ov (make-overlay beg end (org-noter--session-doc-buffer session))) + (hl-color (or (org-entry-get nil org-noter-nov-overlay-color-property nil t) org-noter-nov-overlay-default-color))) + + (overlay-put ov 'button ov) + (overlay-put ov 'category 'default-button) + (overlay-put ov 'face (list :background (if org-noter-insert-note-no-questions + hl-color + (setq hl-color (read-color "Highlight color: "))) + :foreground (readable-foreground-color hl-color))) + + (org-entry-put nil org-noter-nov-overlay-color-property hl-color) + + (overlay-put ov 'mouse-face (list :background (setq hl-color (color-lighten-name hl-color 15)) + :foreground (readable-foreground-color hl-color))) + + (overlay-put ov 'origin (org-element-property :begin (org-element-at-point))) + + (overlay-put ov 'action #'org-noter-nov-overlay-sync-current-page-or-chapter))))) + +(defun org-noter-nov-make-overlay-no-question () + "Like `org-noter-nov-make-ov', but doesn't ask user to select the overlay color." + (org-noter--with-valid-session + (let ((org-noter-insert-note-no-questions t)) + (org-noter-nov-make-overlay)))) + +(defun org-noter-nov-overlay-sync-current-page-or-chapter (_overlay) + "A wrapper function for `org-noter-sync-current-page-or-chapter' +used exclusively with overlays made with `org-noter-nov-make-overlay' + +This wrapper ignores the first argument passed to it and just call +`org-noter-sync-current-page-or-chapter'." + + (org-noter-sync-current-page-or-chapter)) + +(add-hook 'org-noter-insert-heading-hook #'org-noter-nov-make-overlay) + +(add-hook 'nov-post-html-render-hook #'org-noter-nov-make-overlay-no-question) + +(provide 'org-noter-nov-overlay) +;;; org-noter-nov-ov.el ends here + From ae54300d0877383c9b7b33496a03a31055f7d36a Mon Sep 17 00:00:00 2001 From: c1-g Date: Tue, 14 Sep 2021 08:42:00 +0700 Subject: [PATCH 036/453] org-noter-nov-overlay: org-noter-nov-make-overlays command. --- other/org-noter-nov-overlay.el | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/other/org-noter-nov-overlay.el b/other/org-noter-nov-overlay.el index a4ba311..decfeab 100644 --- a/other/org-noter-nov-overlay.el +++ b/other/org-noter-nov-overlay.el @@ -35,6 +35,15 @@ Should be one of the element in `defined-colors'.") +(defun org-noter-nov-make-overlays () + (org-noter--with-selected-notes-window + (let* ((page (buffer-local-value 'nov-documents-index (org-noter--session-doc-buffer session))) + (regexp (org-re-property org-noter-property-note-location t nil (format ".*%s.*" page)))) + (org-with-wide-buffer + (goto-char (point-min)) + (while (re-search-forward regexp nil t) + (org-noter-nov-make-overlay-no-question)))))) + (defun org-noter-nov-make-overlay () "TODO" (org-noter--with-selected-notes-window @@ -84,7 +93,7 @@ This wrapper ignores the first argument passed to it and just call (add-hook 'org-noter-insert-heading-hook #'org-noter-nov-make-overlay) -(add-hook 'nov-post-html-render-hook #'org-noter-nov-make-overlay-no-question) +(add-hook 'nov-post-html-render-hook #'org-noter-nov-make-overlays) (provide 'org-noter-nov-overlay) ;;; org-noter-nov-ov.el ends here From 961fa6ae02918154d699ad24de46e4ae3aeeda8a Mon Sep 17 00:00:00 2001 From: c1-g Date: Tue, 14 Sep 2021 11:28:39 +0700 Subject: [PATCH 037/453] Also highlight the heading. --- other/org-noter-nov-overlay.el | 42 ++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/other/org-noter-nov-overlay.el b/other/org-noter-nov-overlay.el index decfeab..25bf5cc 100644 --- a/other/org-noter-nov-overlay.el +++ b/other/org-noter-nov-overlay.el @@ -57,24 +57,33 @@ Should be one of the element in `defined-colors'.") (end (if (consp location-cons) (cdr location-cons) (1+ beg))) - (ov (make-overlay beg end (org-noter--session-doc-buffer session))) - (hl-color (or (org-entry-get nil org-noter-nov-overlay-color-property nil t) org-noter-nov-overlay-default-color))) + (ov-pair (list (make-overlay beg end (org-noter--session-doc-buffer session)))) + (hl-color (or (org-entry-get nil org-noter-nov-overlay-color-property nil t) + (if org-noter-insert-note-no-questions + org-noter-nov-overlay-default-color + (read-color "Highlight color: ")))) + (hl-color-alt (color-lighten-name hl-color 15)) + (action-functions (list + #'org-noter-nov-overlay-sync-current-note + #'org-noter-nov-overlay-sync-current-page-or-chapter))) - (overlay-put ov 'button ov) - (overlay-put ov 'category 'default-button) - (overlay-put ov 'face (list :background (if org-noter-insert-note-no-questions - hl-color - (setq hl-color (read-color "Highlight color: "))) - :foreground (readable-foreground-color hl-color))) - - (org-entry-put nil org-noter-nov-overlay-color-property hl-color) - - (overlay-put ov 'mouse-face (list :background (setq hl-color (color-lighten-name hl-color 15)) - :foreground (readable-foreground-color hl-color))) + (save-excursion + (org-back-to-heading t) + (re-search-forward org-heading-regexp nil t) + (push (make-overlay (match-beginning 1) (match-end 1)) ov-pair)) - (overlay-put ov 'origin (org-element-property :begin (org-element-at-point))) + (dolist (ov ov-pair) + (overlay-put ov 'button ov) + (overlay-put ov 'category 'default-button) + (overlay-put ov 'face (list :background hl-color + :foreground (readable-foreground-color hl-color))) - (overlay-put ov 'action #'org-noter-nov-overlay-sync-current-page-or-chapter))))) + (org-entry-put nil org-noter-nov-overlay-color-property hl-color) + + (overlay-put ov 'mouse-face (list :background hl-color-alt + :foreground (readable-foreground-color hl-color-alt))) + + (overlay-put ov 'action (pop action-functions))))))) (defun org-noter-nov-make-overlay-no-question () "Like `org-noter-nov-make-ov', but doesn't ask user to select the overlay color." @@ -91,6 +100,9 @@ This wrapper ignores the first argument passed to it and just call (org-noter-sync-current-page-or-chapter)) +(defun org-noter-nov-overlay-sync-current-note (_overlay) + (org-noter-sync-current-note)) + (add-hook 'org-noter-insert-heading-hook #'org-noter-nov-make-overlay) (add-hook 'nov-post-html-render-hook #'org-noter-nov-make-overlays) From 79fe2dd29432f0e4ef5840175e42440b8ba5a294 Mon Sep 17 00:00:00 2001 From: c1-g Date: Tue, 14 Sep 2021 12:18:48 +0700 Subject: [PATCH 038/453] Mention org-noter-nov-overlay in README. --- README.org | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/README.org b/README.org index ab01ea9..47ae0d8 100644 --- a/README.org +++ b/README.org @@ -5,13 +5,25 @@ Read about org-noter’s feature/usage/customization in its README [[file:README ** Added features - =org-noter= can be used on djvu file. - =org-noter-create-skeleton= now works on djvu and pdf file. - - Allow :NOTER_DOCUMENT: to be in file-level :PROPERTIES: drawer + - Allow =:NOTER_DOCUMENT:= to be in file-level =:PROPERTIES:= drawer See the issue https://github.com/weirdNox/org-noter/issues/143 for details. + - There is a module in the [[file:other/][other/]] directory called + [[file:other/org-noter-nov-overlay.el][org-noter-nov-overlay]] that highlights your notes with overlays in + nov-mode, to use it, either add it to your =load-path= or with [[https://github.com/jwiegley/use-package][use-package]], +#+begin_src emacs-lisp + (use-package org-noter-nov-overlay + :ensure nil) +#+end_src + ** Goals - [ ] Precise location support djvu.el have both graphical (via =djvu-image-mode=) and textual (default) modes so maybe the textual mode should handle precise location like nov.el and graphical mode should do it like pdf-tools. +** Installation +*** Manual installation +1. Clone this repo =git clone https://notabug.org/c1-g/org-noter-plus-djvu.git= +2. Add it to your =load-path=. ** Caveats *** This is a fork of a fork 🍴 The branch =djvu= (the only one I’d worked on so far) of this repository is based on Ahmed Shariff’s pull request of org-noter From e3c95947804e38c2324233451d98bb0def9003f4 Mon Sep 17 00:00:00 2001 From: c1-g Date: Tue, 14 Sep 2021 18:59:19 +0700 Subject: [PATCH 039/453] Add docstring to org-noter-nov-overlay-sync-current-note. --- other/org-noter-nov-overlay.el | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/other/org-noter-nov-overlay.el b/other/org-noter-nov-overlay.el index 25bf5cc..3944f60 100644 --- a/other/org-noter-nov-overlay.el +++ b/other/org-noter-nov-overlay.el @@ -101,6 +101,11 @@ This wrapper ignores the first argument passed to it and just call (org-noter-sync-current-page-or-chapter)) (defun org-noter-nov-overlay-sync-current-note (_overlay) + "A wrapper function for `org-noter-nov-overlay-sync-current-note' +used exclusively with overlays made with `org-noter-nov-make-overlay' + +This wrapper ignores the first argument passed to it and just call +`org-noter-nov-overlay-sync-current-note'." (org-noter-sync-current-note)) (add-hook 'org-noter-insert-heading-hook #'org-noter-nov-make-overlay) From e8d3fc1b5218b4166711ead66c1280a317fb1335 Mon Sep 17 00:00:00 2001 From: c1-g Date: Wed, 15 Sep 2021 07:36:46 +0700 Subject: [PATCH 040/453] Shorten repo description. --- README.org | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.org b/README.org index 47ae0d8..b856608 100644 --- a/README.org +++ b/README.org @@ -1,4 +1,4 @@ -* Org-noter + DJVU - A fork of [[https://github.com/weirdNox/org-noter][org-noter]] with, hopefully, [[https://elpa.gnu.org/packages/djvu.html][djvu.el]] support. +* Org-noter + DJVU - A fork of [[https://github.com/weirdNox/org-noter][org-noter]] with [[https://elpa.gnu.org/packages/djvu.html][djvu.el]] support. ** What is org-noter? Org-noter is an org-mode document annotator. It is a rewrite of another project, [[https://github.com/rudolfochrist/interleave][Interleave]]. Read about org-noter’s feature/usage/customization in its README [[file:README-orig.org][here]] or go visit its repository at [[https://github.com/weirdNox/org-noter][github]]. From f74263f0e19f205d7ffe2f0bc230a01d5a814128 Mon Sep 17 00:00:00 2001 From: c1-g Date: Wed, 15 Sep 2021 07:41:38 +0700 Subject: [PATCH 041/453] Extend 49bc6ee. --- org-noter.el | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/org-noter.el b/org-noter.el index cfc4fe3..a95ea5f 100644 --- a/org-noter.el +++ b/org-noter.el @@ -509,8 +509,9 @@ If nil, the session used will be `org-noter--session'." (catch 'break (org-back-to-heading t) (while t - (when (string= (or (org-entry-get nil org-noter-property-doc-file) - (cadar (org-collect-keywords (list org-noter-property-doc-file)))) wanted-prop) + (when (string= (or (org-entry-get nil org-noter-property-doc-file t) + (cadar (org-collect-keywords (list org-noter-property-doc-file)))) + wanted-prop) (setq root-pos (copy-marker (point)))) (unless (org-up-heading-safe) (throw 'break t)))))))) @@ -779,7 +780,8 @@ properties, by a margin of NEWLINES-NUMBER." (defsubst org-noter--doc-file-property (headline) (or (org-element-property (intern (concat ":" org-noter-property-doc-file)) headline) - (cadar (org-collect-keywords (list org-noter-property-doc-file))))) + (cadar (org-collect-keywords (list org-noter-property-doc-file))) + (org-entry-get nil org-noter-property-doc-file t))) (defun org-noter--check-location-property (arg) (let ((property (if (stringp arg) arg From e417890294aee90d8eeabcb128b804c85878456b Mon Sep 17 00:00:00 2001 From: c1-g Date: Sat, 18 Sep 2021 08:56:30 +0700 Subject: [PATCH 042/453] =?UTF-8?q?get-location-page=20=E2=86=92=20org-not?= =?UTF-8?q?er=E2=80=93-get-location-page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- org-noter.el | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/org-noter.el b/org-noter.el index a95ea5f..d650833 100644 --- a/org-noter.el +++ b/org-noter.el @@ -813,7 +813,7 @@ properties, by a margin of NEWLINES-NUMBER." ((eq (org-noter--session-doc-mode session) 'nov-mode) (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 1)) - (get-location-page location) + (org-noter--get-location-page location) location))))))) (defun org-noter--get-containing-heading (&optional include-root) @@ -975,7 +975,7 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (cadr location) (cdr location))) -(defun get-location-page (location) +(defun org-noter--get-location-page (location) "Get the page number given a LOCATION of form (page top . left) or (page . top)." (car location)) @@ -999,8 +999,8 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (left (org-noter--get-location-left location))) (if (eq mode 'doc-view-mode) - (doc-view-goto-page (get-location-page location)) - (pdf-view-goto-page (get-location-page location)) + (doc-view-goto-page (org-noter--get-location-page location)) + (pdf-view-goto-page (org-noter--get-location-page location)) ;; NOTE(nox): This timer is needed because the tooltip may introduce a delay, ;; so syncing multiple pages was slow (when (>= org-noter-arrow-delay 0) @@ -1014,7 +1014,7 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (window-vscroll))))) ((eq mode 'nov-mode) - (setq nov-documents-index (get-location-page location)) + (setq nov-documents-index (org-noter--get-location-page location)) (nov-render-document) (goto-char (org-noter--get-location-top location)) (recenter)) @@ -1031,42 +1031,42 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." See `org-noter--compare-locations'" (cl-assert (and (consp l1) (consp l2))) (cond ((eq comp '=) - (and (= (get-location-page l1) (get-location-page l2)) + (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) (= (org-noter--get-location-top l1) (org-noter--get-location-top l2)) (= (org-noter--get-location-left l1) (org-noter--get-location-left l2)))) ((eq comp '<) - (or (< (get-location-page l1) (get-location-page l2)) - (and (= (get-location-page l1) (get-location-page l2)) + (or (< (org-noter--get-location-page l1) (org-noter--get-location-page l2)) + (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) (< (org-noter--get-location-top l1) (org-noter--get-location-top l2))) - (and (= (get-location-page l1) (get-location-page l2)) + (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) (= (org-noter--get-location-top l1) (org-noter--get-location-top l2)) (< (org-noter--get-location-left l1) (org-noter--get-location-left l2))))) ((eq comp '<=) - (or (< (get-location-page l1) (get-location-page l2)) - (and (= (get-location-page l1) (get-location-page l2)) + (or (< (org-noter--get-location-page l1) (org-noter--get-location-page l2)) + (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) (<= (org-noter--get-location-top l1) (org-noter--get-location-top l2))) - (and (= (get-location-page l1) (get-location-page l2)) + (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) (= (org-noter--get-location-top l1) (org-noter--get-location-top l2)) (<= (org-noter--get-location-left l1) (org-noter--get-location-left l2))))) ((eq comp '>) - (or (> (get-location-page l1) (get-location-page l2)) - (and (= (get-location-page l1) (get-location-page l2)) + (or (> (org-noter--get-location-page l1) (org-noter--get-location-page l2)) + (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) (> (org-noter--get-location-top l1) (org-noter--get-location-top l2))) - (and (= (get-location-page l1) (get-location-page l2)) + (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) (= (org-noter--get-location-top l1) (org-noter--get-location-top l2)) (> (org-noter--get-location-left l1) (org-noter--get-location-left l2))))) ((eq comp '>=) - (or (> (get-location-page l1) (get-location-page l2)) - (and (= (get-location-page l1) (get-location-page l2)) + (or (> (org-noter--get-location-page l1) (org-noter--get-location-page l2)) + (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) (>= (org-noter--get-location-top l1) (org-noter--get-location-top l2))) - (and (= (get-location-page l1) (get-location-page l2)) + (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) (= (org-noter--get-location-top l1) (org-noter--get-location-top l2)) (>= (org-noter--get-location-left l1) (org-noter--get-location-left l2))))) ((eq comp '>f) - (or (> (get-location-page l1) (get-location-page l2)) - (and (= (get-location-page l1) (get-location-page l2)) + (or (> (org-noter--get-location-page l1) (org-noter--get-location-page l2)) + (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) (< (org-noter--get-location-top l1) (org-noter--get-location-top l2))) - (and (= (get-location-page l1) (get-location-page l2)) + (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) (= (org-noter--get-location-top l1) (org-noter--get-location-top l2)) (< (org-noter--get-location-left l1) (org-noter--get-location-left l2))))) (t (error "Comparison operator %s not known" comp)))) @@ -1188,7 +1188,7 @@ document property) will be opened." ((run-hook-with-args-until-success 'org-noter--relative-position-to-view-hook location view)) ((eq (aref view 0) 'paged) - (let ((note-page (get-location-page location)) + (let ((note-page (org-noter--get-location-page location)) (view-page (aref view 1))) (cond ((< note-page view-page) 'before) ((= note-page view-page) 'inside) @@ -2031,7 +2031,7 @@ defines if the text should be inserted inside the note." (let ((reference-element-cons (org-noter--view-info-reference-for-insertion view-info)) level) (when (zerop (length title)) - (setq title (replace-regexp-in-string (regexp-quote "$p$") (number-to-string (get-location-page location)) + (setq title (replace-regexp-in-string (regexp-quote "$p$") (number-to-string (org-noter--get-location-page location)) org-noter-default-heading-title))) (if reference-element-cons From 500273224739b39a0173e842fc42b51afff4e262 Mon Sep 17 00:00:00 2001 From: c1-g Date: Sat, 18 Sep 2021 11:24:38 +0700 Subject: [PATCH 043/453] Only make overlays if the page matches. --- other/org-noter-nov-overlay.el | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/other/org-noter-nov-overlay.el b/other/org-noter-nov-overlay.el index 3944f60..62e5877 100644 --- a/other/org-noter-nov-overlay.el +++ b/other/org-noter-nov-overlay.el @@ -38,11 +38,16 @@ Should be one of the element in `defined-colors'.") (defun org-noter-nov-make-overlays () (org-noter--with-selected-notes-window (let* ((page (buffer-local-value 'nov-documents-index (org-noter--session-doc-buffer session))) - (regexp (org-re-property org-noter-property-note-location t nil (format ".*%s.*" page)))) + (regexp (org-re-property org-noter-property-note-location t))) (org-with-wide-buffer (goto-char (point-min)) (while (re-search-forward regexp nil t) - (org-noter-nov-make-overlay-no-question)))))) + (when-let ((location (org-entry-get nil org-noter-property-note-location nil t))) + (when (eq (if (consp (read location)) + (car (read location)) + (read location)) + page) + (org-noter-nov-make-overlay-no-question)))))))) (defun org-noter-nov-make-overlay () "TODO" From 6ad1bfebf22b57d51a0d5432a677bde0cb1a5668 Mon Sep 17 00:00:00 2001 From: c1-g Date: Thu, 11 Nov 2021 09:39:29 +0700 Subject: [PATCH 044/453] Only make overlay when location is in the form of (PAGE BEGIN . END) --- other/org-noter-nov-overlay.el | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/other/org-noter-nov-overlay.el b/other/org-noter-nov-overlay.el index 62e5877..0b81c19 100644 --- a/other/org-noter-nov-overlay.el +++ b/other/org-noter-nov-overlay.el @@ -56,12 +56,8 @@ Should be one of the element in `defined-colors'.") (when (eq (org-noter--session-doc-mode session) 'nov-mode) (when-let* ((location-property (org-entry-get nil org-noter-property-note-location nil t)) (location-cons (cdr (read location-property))) - (beg (if (consp location-cons) - (car location-cons) - location-cons)) - (end (if (consp location-cons) - (cdr location-cons) - (1+ beg))) + (beg (and (consp location-cons) (car location-cons))) + (end (cdr location-cons)) (ov-pair (list (make-overlay beg end (org-noter--session-doc-buffer session)))) (hl-color (or (org-entry-get nil org-noter-nov-overlay-color-property nil t) (if org-noter-insert-note-no-questions From 243bb57257a90ab002b871e302fec16c12347ab8 Mon Sep 17 00:00:00 2001 From: c1-g Date: Tue, 16 Nov 2021 12:12:27 +0700 Subject: [PATCH 045/453] =?UTF-8?q?Don=E2=80=99t=20add=20hook=20to=20org-n?= =?UTF-8?q?oter-insert-heading-hook.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This messed up org-noter-create-skeleton so I have to remove it. --- other/org-noter-nov-overlay.el | 2 -- 1 file changed, 2 deletions(-) diff --git a/other/org-noter-nov-overlay.el b/other/org-noter-nov-overlay.el index 0b81c19..4872dbc 100644 --- a/other/org-noter-nov-overlay.el +++ b/other/org-noter-nov-overlay.el @@ -109,8 +109,6 @@ This wrapper ignores the first argument passed to it and just call `org-noter-nov-overlay-sync-current-note'." (org-noter-sync-current-note)) -(add-hook 'org-noter-insert-heading-hook #'org-noter-nov-make-overlay) - (add-hook 'nov-post-html-render-hook #'org-noter-nov-make-overlays) (provide 'org-noter-nov-overlay) From b9ddcbd9858fb4d04a784051e3624d3b21f573c1 Mon Sep 17 00:00:00 2001 From: c1-g Date: Tue, 16 Nov 2021 15:37:00 +0700 Subject: [PATCH 046/453] Add code from org-noter-plus Another package extending org-noter made by Yuchen Li. At https://github.com/yuchen-lea/org-noter-plus. --- org-noter.el | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/org-noter.el b/org-noter.el index d650833..b465f85 100644 --- a/org-noter.el +++ b/org-noter.el @@ -1712,6 +1712,7 @@ want to kill." (pcase (org-noter--session-doc-mode session) ('pdf-view-mode (org-noter-create-skeleton-pdf)) ('djvu-read-mode (org-noter-create-skeleton-djvu)) + ('nov-mode (org-noter-create-skeleton-epub)) (_ (user-error "This command is not supported for %s" (org-noter--session-doc-mode session)))))) (defun org-noter-create-skeleton-pdf () @@ -1905,7 +1906,6 @@ want to kill." (let (last-absolute-level title location relative-level contents level) - (dolist (data (nreverse output-data)) (setq title (aref data 0) @@ -1930,6 +1930,62 @@ want to kill." (outline-hide-subtree) (org-show-children 2)))))) +;; Shamelessly stolen code from Yuchen Li. +;; This code is originally from org-noter-plus package. +;; At https://github.com/yuchen-lea/org-noter-plus + +(defun org-noter--handle-nov-toc-item (ol depth) + (require 'dom) + (mapcar (lambda (li) + (mapcar (lambda (a-or-ol) + (pcase-exhaustive (dom-tag a-or-ol) + ('a + (vector :depth depth + :title (dom-text a-or-ol) + :href (dom-attr a-or-ol 'href))) + ('ol + (org-noter--handle-nov-toc-item a-or-ol + (1+ depth))))) + (dom-children li))) + (dom-children ol))) + +(defun org-noter--nov-outline-info () + "Epub outline with nov link." + (require 'esxml) + (require 'nov) + (require 'dom) + (org-noter--with-valid-session + (with-current-buffer (org-noter--session-doc-buffer session) + (let* ((toc-path (cdr (aref nov-documents 0))) + (toc-tree (with-temp-buffer + (insert (nov-ncx-to-html toc-path)) + (replace-regexp "\n" + "" + nil + (point-min) + (point-max)) + (libxml-parse-html-region (point-min) + (point-max)))) + output-data + (origin-index nov-documents-index) + (origin-point (point))) + (dolist (item + (flatten-tree (org-noter--handle-nov-toc-item toc-tree 1))) + ;; TODO vector or alist? + (let ((depth (aref item 1)) + (title (aref item 3)) + (url (aref item 5))) + (apply 'nov-visit-relative-file + (nov-url-filename-and-target url)) + (when (not (integerp nov-documents-index)) + (setq nov-documents-index 0)) + (push (vector title depth nov-documents-index (point)) output-data))) + (nov-goto-document origin-index) + (goto-char origin-point) + (nreverse output-data))))) + + + (defun org-noter-insert-note (&optional precise-info note-title) "Insert note associated with the current location. From 9ff2bf0c1ea9c7b5983e527362eb9448df71bebb Mon Sep 17 00:00:00 2001 From: c1-g Date: Tue, 16 Nov 2021 16:41:10 +0700 Subject: [PATCH 047/453] Add create-skeleton function for epub. --- org-noter.el | 90 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 57 insertions(+), 33 deletions(-) diff --git a/org-noter.el b/org-noter.el index b465f85..f9704c7 100644 --- a/org-noter.el +++ b/org-noter.el @@ -1935,54 +1935,78 @@ want to kill." ;; At https://github.com/yuchen-lea/org-noter-plus (defun org-noter--handle-nov-toc-item (ol depth) - (require 'dom) (mapcar (lambda (li) (mapcar (lambda (a-or-ol) (pcase-exhaustive (dom-tag a-or-ol) ('a (vector :depth depth :title (dom-text a-or-ol) - :href (dom-attr a-or-ol 'href))) + :href (esxml-node-attribute 'href a-or-ol))) ('ol (org-noter--handle-nov-toc-item a-or-ol - (1+ depth))))) + (1+ depth))))) (dom-children li))) (dom-children ol))) -(defun org-noter--nov-outline-info () +(defun org-noter-create-skeleton-epub () "Epub outline with nov link." - (require 'esxml) + (require 'esxml) (require 'nov) (require 'dom) (org-noter--with-valid-session - (with-current-buffer (org-noter--session-doc-buffer session) - (let* ((toc-path (cdr (aref nov-documents 0))) - (toc-tree (with-temp-buffer - (insert (nov-ncx-to-html toc-path)) - (replace-regexp "\n" - "" - nil - (point-min) - (point-max)) - (libxml-parse-html-region (point-min) - (point-max)))) - output-data - (origin-index nov-documents-index) - (origin-point (point))) - (dolist (item - (flatten-tree (org-noter--handle-nov-toc-item toc-tree 1))) - ;; TODO vector or alist? - (let ((depth (aref item 1)) - (title (aref item 3)) - (url (aref item 5))) - (apply 'nov-visit-relative-file - (nov-url-filename-and-target url)) - (when (not (integerp nov-documents-index)) - (setq nov-documents-index 0)) - (push (vector title depth nov-documents-index (point)) output-data))) - (nov-goto-document origin-index) - (goto-char origin-point) - (nreverse output-data))))) + (let* ((ast (org-noter--parse-root)) + (top-level (org-element-property :level ast)) + output-data) + (with-current-buffer (org-noter--session-doc-buffer session) + (let* ((toc-path (cdr (aref nov-documents 0))) + (toc-tree (with-temp-buffer + (insert (nov-ncx-to-html toc-path)) + (replace-regexp "\n" + "" + nil + (point-min) + (point-max)) + (libxml-parse-html-region (point-min) + (point-max)))) + (origin-index nov-documents-index) + (origin-point (point))) + (dolist (item + (nreverse (flatten-tree (org-noter--handle-nov-toc-item toc-tree 1)))) + (let ((relative-level (aref item 1)) + (title (aref item 3)) + (url (aref item 5))) + (apply 'nov-visit-relative-file + (nov-url-filename-and-target url)) + (when (not (integerp nov-documents-index)) + (setq nov-documents-index 0)) + (push (vector title (list nov-documents-index (point)) relative-level) output-data))) + (push (vector "Skeleton" (list 0) 1) output-data) + + (nov-goto-document origin-index) + (goto-char origin-point))) + + (with-current-buffer (org-noter--session-notes-buffer session) + (dolist (data output-data) + (setq title (aref data 0) + location (aref data 1) + relative-level (aref data 2)) + + (setq last-absolute-level (+ top-level relative-level) + level last-absolute-level) + + (org-noter--insert-heading level title) + + (when location + (org-entry-put nil org-noter-property-note-location (org-noter--pretty-print-location location))) + + (when org-noter-doc-property-in-notes + (org-entry-put nil org-noter-property-doc-file (org-noter--session-property-text session)) + (org-entry-put nil org-noter--property-auto-save-last-location "nil"))) + (setq ast (org-noter--parse-root)) + (org-noter--narrow-to-root ast) + (goto-char (org-element-property :begin ast)) + (outline-hide-subtree) + (org-show-children 2))))) From fb283e6fa35787913ed7c7016e4a88eefd3aa36b Mon Sep 17 00:00:00 2001 From: c1-g Date: Tue, 16 Nov 2021 17:20:18 +0700 Subject: [PATCH 048/453] Go to the end of the element before inserting skeleton. --- org-noter.el | 47 ++++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/org-noter.el b/org-noter.el index f9704c7..7199cb9 100644 --- a/org-noter.el +++ b/org-noter.el @@ -1984,29 +1984,30 @@ want to kill." (nov-goto-document origin-index) (goto-char origin-point))) - - (with-current-buffer (org-noter--session-notes-buffer session) - (dolist (data output-data) - (setq title (aref data 0) - location (aref data 1) - relative-level (aref data 2)) - - (setq last-absolute-level (+ top-level relative-level) - level last-absolute-level) - - (org-noter--insert-heading level title) - - (when location - (org-entry-put nil org-noter-property-note-location (org-noter--pretty-print-location location))) - - (when org-noter-doc-property-in-notes - (org-entry-put nil org-noter-property-doc-file (org-noter--session-property-text session)) - (org-entry-put nil org-noter--property-auto-save-last-location "nil"))) - (setq ast (org-noter--parse-root)) - (org-noter--narrow-to-root ast) - (goto-char (org-element-property :begin ast)) - (outline-hide-subtree) - (org-show-children 2))))) + (save-excursion + (goto-char (org-element-property :end ast)) + (with-current-buffer (org-noter--session-notes-buffer session) + (dolist (data output-data) + (setq title (aref data 0) + location (aref data 1) + relative-level (aref data 2)) + + (setq last-absolute-level (+ top-level relative-level) + level last-absolute-level) + + (org-noter--insert-heading level title) + + (when location + (org-entry-put nil org-noter-property-note-location (org-noter--pretty-print-location location))) + + (when org-noter-doc-property-in-notes + (org-entry-put nil org-noter-property-doc-file (org-noter--session-property-text session)) + (org-entry-put nil org-noter--property-auto-save-last-location "nil"))) + (setq ast (org-noter--parse-root)) + (org-noter--narrow-to-root ast) + (goto-char (org-element-property :begin ast)) + (outline-hide-subtree) + (org-show-children 2)))))) From d5a7d0c74088f8923e998d07dbffb36031bbc3f1 Mon Sep 17 00:00:00 2001 From: c1-g Date: Tue, 16 Nov 2021 17:31:00 +0700 Subject: [PATCH 049/453] More strict headings criteria. Now org-noter-nov-make-overlays will only make overlay for every heading that has the org-noter-property-note-location property in the form of (PAGE BEG . END) and nothing else. --- other/org-noter-nov-overlay.el | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/other/org-noter-nov-overlay.el b/other/org-noter-nov-overlay.el index 4872dbc..077e9ad 100644 --- a/other/org-noter-nov-overlay.el +++ b/other/org-noter-nov-overlay.el @@ -38,25 +38,24 @@ Should be one of the element in `defined-colors'.") (defun org-noter-nov-make-overlays () (org-noter--with-selected-notes-window (let* ((page (buffer-local-value 'nov-documents-index (org-noter--session-doc-buffer session))) - (regexp (org-re-property org-noter-property-note-location t))) + (regexp (org-re-property org-noter-property-note-location t nil + (rx "(" (+ space) "%d" (+ space) + (+ digit) (+ space) "." (+ space) + (+ digit) (+ space) ")")))) (org-with-wide-buffer (goto-char (point-min)) (while (re-search-forward regexp nil t) (when-let ((location (org-entry-get nil org-noter-property-note-location nil t))) - (when (eq (if (consp (read location)) - (car (read location)) - (read location)) - page) - (org-noter-nov-make-overlay-no-question)))))))) + (org-noter-nov-make-overlay-no-question))))))) (defun org-noter-nov-make-overlay () "TODO" (org-noter--with-selected-notes-window "No notes window exists" (when (eq (org-noter--session-doc-mode session) 'nov-mode) - (when-let* ((location-property (org-entry-get nil org-noter-property-note-location nil t)) + (let* ((location-property (org-entry-get nil org-noter-property-note-location nil t)) (location-cons (cdr (read location-property))) - (beg (and (consp location-cons) (car location-cons))) + (beg (car location-cons)) (end (cdr location-cons)) (ov-pair (list (make-overlay beg end (org-noter--session-doc-buffer session)))) (hl-color (or (org-entry-get nil org-noter-nov-overlay-color-property nil t) From 0a3ceeb261c85e74f0808893f3913a98474fa2ba Mon Sep 17 00:00:00 2001 From: Ahmed Shariff Date: Tue, 16 Nov 2021 19:34:55 -0600 Subject: [PATCH 050/453] Fix for emacs crashing when notes-buffer not setup in time. --- org-noter.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/org-noter.el b/org-noter.el index a760546..3fe68d3 100644 --- a/org-noter.el +++ b/org-noter.el @@ -440,12 +440,12 @@ The title used will be the default one." (unless target-location (setq target-location (org-noter--parse-location-property (org-noter--get-containing-heading t))))) - (org-noter--setup-windows session) - ;; NOTE(nox): This timer is for preventing reflowing too soon. (run-with-idle-timer 0.05 nil (lambda () + ;; NOTE(ahmed-shariff): setup-window run here to avoid crash when notes buffer not setup in time + (org-noter--setup-windows session) (with-current-buffer document-buffer (let ((org-noter--inhibit-location-change-handler t)) (when target-location (org-noter--doc-goto-location target-location))) From 875ba55cf4d32ac2d1b3a1452d896789b9cb228c Mon Sep 17 00:00:00 2001 From: c1-g Date: Sun, 21 Nov 2021 08:20:59 +0700 Subject: [PATCH 051/453] Fix regexp. --- other/org-noter-nov-overlay.el | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/other/org-noter-nov-overlay.el b/other/org-noter-nov-overlay.el index 077e9ad..f4cd0eb 100644 --- a/other/org-noter-nov-overlay.el +++ b/other/org-noter-nov-overlay.el @@ -24,13 +24,13 @@ ;;; Code: (require 'org-noter) -(require 'nov nil t) +(require 'nov) (require 'seq) (defcustom org-noter-nov-overlay-color-property "NOTER_OVERLAY" "A property that specifies the overlay color for `org-noter-nov-make-ov'.") -(defcustom org-noter-nov-overlay-default-color "yellow" +(defcustom org-noter-nov-overlay-default-color "SkyBlue" "Name of the default background color of the overlay `org-noter-nov-make-ov' makes. Should be one of the element in `defined-colors'.") @@ -39,9 +39,10 @@ Should be one of the element in `defined-colors'.") (org-noter--with-selected-notes-window (let* ((page (buffer-local-value 'nov-documents-index (org-noter--session-doc-buffer session))) (regexp (org-re-property org-noter-property-note-location t nil - (rx "(" (+ space) "%d" (+ space) - (+ digit) (+ space) "." (+ space) - (+ digit) (+ space) ")")))) + (format (rx "(" (* space) "%d" (+ space) + (+ digit) (+ space) "." (+ space) + (+ digit) (* space) ")") + page)))) (org-with-wide-buffer (goto-char (point-min)) (while (re-search-forward regexp nil t) From 0c21db7d4773478377dbf9432a2a9818024d73cc Mon Sep 17 00:00:00 2001 From: Ahmed Shariff Date: Fri, 26 Nov 2021 00:45:03 -0600 Subject: [PATCH 052/453] Disbaling making title and properies read-only --- org-noter.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org-noter.el b/org-noter.el index 3fe68d3..9064fdf 100644 --- a/org-noter.el +++ b/org-noter.el @@ -561,7 +561,7 @@ If nil, the session used will be `org-noter--session'." (add-text-properties (max 1 (1- begin)) begin '(read-only t)) (add-text-properties begin (1- title-begin) `(read-only t front-sticky t ,org-noter--id-text-property ,id)) (add-text-properties (1- title-begin) title-begin '(read-only t rear-nonsticky t)) - (add-text-properties (1- contents-begin) (1- properties-end) '(read-only t)) + ;; (add-text-properties (1- contents-begin) (1- properties-end) '(read-only t)) (add-text-properties (1- properties-end) properties-end '(read-only t rear-nonsticky t)) (set-buffer-modified-p modified))))) From 58b24e7740e1f13ab02a693bf4b6982b841172ce Mon Sep 17 00:00:00 2001 From: c1-g Date: Thu, 2 Dec 2021 09:02:43 +0700 Subject: [PATCH 053/453] =?UTF-8?q?Remove=20=E2=80=9Cissue=20under=20first?= =?UTF-8?q?=20headline=E2=80=9D=20requirement.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Done by removing the org-back-to-heading command that give errors when the point is before the first heading in org-noter--parse-root. --- org-noter.el | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/org-noter.el b/org-noter.el index 8a7c926..4e297af 100644 --- a/org-noter.el +++ b/org-noter.el @@ -507,11 +507,10 @@ If nil, the session used will be `org-noter--session'." (with-current-buffer notes-buffer (org-with-wide-buffer (catch 'break - (org-back-to-heading t) - (while t - (when (string= (or (org-entry-get nil org-noter-property-doc-file t) - (cadar (org-collect-keywords (list org-noter-property-doc-file)))) - wanted-prop) + (while t + (when (string= (or (org-entry-get nil org-noter-property-doc-file t) + (cadar (org-collect-keywords (list org-noter-property-doc-file)))) + wanted-prop) (setq root-pos (copy-marker (point)))) (unless (org-up-heading-safe) (throw 'break t)))))))) @@ -533,6 +532,8 @@ If nil, the session used will be `org-noter--session'." (with-current-buffer (marker-buffer root-pos) (org-with-wide-buffer (goto-char (marker-position root-pos)) + (when (org-before-first-heading-p) + (org-next-visible-heading 1)) (org-narrow-to-subtree) (setq ast (car (org-element-contents (org-element-parse-buffer 'greater-element)))) (when (and (not (vectorp info)) (org-noter--valid-session session)) @@ -2391,9 +2392,7 @@ notes file, even if it finds one." (cond ;; NOTE(nox): Creating the session from notes file ((eq major-mode 'org-mode) - (when (org-before-first-heading-p) - (user-error "`org-noter' must be issued inside a heading")) - + (let* ((notes-file-path (buffer-file-name)) (document-property (org-noter--get-or-read-document-property (not (equal arg '(4))) (equal arg '(16)))) From 00fb4813ab2545a5b5da415f69869c5770ab82b4 Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 20 Dec 2021 15:16:30 +0700 Subject: [PATCH 054/453] Add org-noter-use-indirect-buffer option. User can choose whether to make an indirect buffer of the notes file or not. --- org-noter.el | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/org-noter.el b/org-noter.el index 4e297af..8e4503f 100644 --- a/org-noter.el +++ b/org-noter.el @@ -134,6 +134,13 @@ When nil, it will use the selected frame if it does not belong to any other sess :group 'org-noter :type 'boolean) +(defcustom org-noter-use-indirect-buffer t + "When non-nil, org-noter will create an indirect buffer of the calling +org file as a note buffer of the session. +When nil, it will use the real buffer." + :group 'org-noter + :type 'boolean) + (defcustom org-noter-suggest-from-attachments t "When non-nil, org-noter will suggest files from the attachments when creating a session, if the document is missing." @@ -362,9 +369,11 @@ The title used will be the default one." (make-indirect-buffer document document-buffer-name t))) (notes-buffer - (make-indirect-buffer - (or (buffer-base-buffer) (current-buffer)) - (generate-new-buffer-name (concat "Notes of " display-name)) t)) + (if org-noter-use-indirect-buffer + (make-indirect-buffer + (or (buffer-base-buffer) (current-buffer)) + (generate-new-buffer-name (concat "Notes of " display-name)) t) + (current-buffer))) (session (make-org-noter--session From bafed912cee54a3c3d112f7b7e7e328e010cac5f Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 20 Dec 2021 15:44:25 +0700 Subject: [PATCH 055/453] Add org-noter-disable-narrowing option. --- org-noter.el | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/org-noter.el b/org-noter.el index 8e4503f..449fb24 100644 --- a/org-noter.el +++ b/org-noter.el @@ -134,6 +134,11 @@ When nil, it will use the selected frame if it does not belong to any other sess :group 'org-noter :type 'boolean) +(defcustom org-noter-disable-narrowing nil + "Disable narrowing in notes/org buffer." + :group 'org-noter + :type 'boolean) + (defcustom org-noter-use-indirect-buffer t "When non-nil, org-noter will create an indirect buffer of the calling org file as a note buffer of the session. @@ -695,7 +700,8 @@ properties, by a margin of NEWLINES-NUMBER." (set-window-dedicated-p doc-window t) (with-current-buffer notes-buffer - (org-noter--narrow-to-root (org-noter--parse-root session)) + (unless org-noter-disable-narrowing + (org-noter--narrow-to-root (org-noter--parse-root session))) (setq notes-window (org-noter--get-notes-window 'start)) (org-noter--set-notes-scroll notes-window)))))) From 0a9de710e50a918a8a623230e1af1ad60c495d16 Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 20 Dec 2021 17:58:05 +0700 Subject: [PATCH 056/453] Add org-noter-swap-window option. --- org-noter.el | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/org-noter.el b/org-noter.el index 449fb24..af44aaa 100644 --- a/org-noter.el +++ b/org-noter.el @@ -146,6 +146,15 @@ When nil, it will use the real buffer." :group 'org-noter :type 'boolean) +(defcustom org-noter-swap-window nil + "By default `org-noter' will make a session by setting the buffer of the selected window +to the document buffer then split with the window of the notes buffer on the right. + +If this variable is non-nil, the buffers of the two windows will be the other way around." + :group 'org-noter + :type 'boolean) + + (defcustom org-noter-suggest-from-attachments t "When non-nil, org-noter will suggest files from the attachments when creating a session, if the document is missing." @@ -694,16 +703,41 @@ properties, by a margin of NEWLINES-NUMBER." (let* ((doc-buffer (org-noter--session-doc-buffer session)) (doc-window (selected-window)) (notes-buffer (org-noter--session-notes-buffer session)) + (window-location (org-noter--session-window-location session)) notes-window) (set-window-buffer doc-window doc-buffer) - (set-window-dedicated-p doc-window t) (with-current-buffer notes-buffer (unless org-noter-disable-narrowing (org-noter--narrow-to-root (org-noter--parse-root session))) (setq notes-window (org-noter--get-notes-window 'start)) - (org-noter--set-notes-scroll notes-window)))))) + (org-noter--set-notes-scroll notes-window)) + + (when org-noter-swap-window + (cl-labels ((swap-windows (window1 window2) + "Swap the buffers of WINDOW1 and WINDOW2." + (let ((buffer1 (window-buffer window1)) + (buffer2 (window-buffer window2))) + (set-window-buffer window1 buffer2) + (set-window-buffer window2 buffer1) + (select-window window2)))) + (let ((frame (window-frame notes-window))) + (when (and (frame-live-p frame) + (not (eq frame (selected-frame)))) + (select-frame-set-input-focus (window-frame notes-window))) + (when (and (window-live-p notes-window) + (not (eq notes-window doc-window))) + (swap-windows notes-window doc-window)))) + + (if (eq window-location 'horizontal-split) + (shrink-window (- (window-total-width) (ceiling (* (car (org-noter--session-doc-split-fraction session)) + (window-total-width)))) + t) + (shrink-window (- (window-total-width) (ceiling (* (cdr (org-noter--session-doc-split-fraction session)) + (window-total-height))))))) + + (set-window-dedicated-p doc-window t))))) (defmacro org-noter--with-selected-notes-window (error-str &rest body) (declare (debug ([&optional stringp] body))) From bd84276b0b0b0eaaefbe9831b9ee6185f7108ef9 Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 20 Dec 2021 21:43:43 +0700 Subject: [PATCH 057/453] Set dedicated window for swapped document window. --- org-noter.el | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/org-noter.el b/org-noter.el index af44aaa..2c0636b 100644 --- a/org-noter.el +++ b/org-noter.el @@ -736,8 +736,13 @@ properties, by a margin of NEWLINES-NUMBER." t) (shrink-window (- (window-total-width) (ceiling (* (cdr (org-noter--session-doc-split-fraction session)) (window-total-height))))))) - - (set-window-dedicated-p doc-window t))))) + + (if org-noter-swap-window + ;; the variable NOTES-WINDOW here is really + ;; the document window since the two got swapped + (set-window-dedicated-p notes-window t) + ;; It's not swapped so set it normally + (set-window-dedicated-p doc-window t)))))) (defmacro org-noter--with-selected-notes-window (error-str &rest body) (declare (debug ([&optional stringp] body))) From 4388d26318cc56e11f8e7b0200994f4238d5dc17 Mon Sep 17 00:00:00 2001 From: c1-g Date: Thu, 23 Dec 2021 09:25:30 +0700 Subject: [PATCH 058/453] =?UTF-8?q?Don=E2=80=99t=20set=20property=20drawer?= =?UTF-8?q?=20to=20read-only=20when=20there=E2=80=99s=20no=20drawer.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- org-noter.el | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/org-noter.el b/org-noter.el index 2c0636b..a83cf23 100644 --- a/org-noter.el +++ b/org-noter.el @@ -593,9 +593,10 @@ If nil, the session used will be `org-noter--session'." (add-text-properties begin (1- title-begin) `(read-only t front-sticky t ,org-noter--id-text-property ,id)) (add-text-properties (1- title-begin) title-begin '(read-only t rear-nonsticky t)) ;; (add-text-properties (1- contents-begin) (1- properties-end) '(read-only t)) - (add-text-properties (1- properties-end) properties-end - '(read-only t rear-nonsticky t)) - (set-buffer-modified-p modified))))) + (when properties-end + (add-text-properties (1- properties-end) properties-end + '(read-only t rear-nonsticky t)) + (set-buffer-modified-p modified)))))) (defun org-noter--unset-text-properties (ast) (when ast @@ -604,9 +605,11 @@ If nil, the session used will be `org-noter--session'." (end (org-noter--get-properties-end ast t)) (inhibit-read-only t) (modified (buffer-modified-p))) - (remove-list-of-text-properties (max 1 (1- begin)) end - `(read-only front-sticky rear-nonsticky ,org-noter--id-text-property)) - (set-buffer-modified-p modified))))) + (when end + (remove-list-of-text-properties (max 1 (1- begin)) end + `(read-only front-sticky rear-nonsticky ,org-noter--id-text-property)) + + (set-buffer-modified-p modified)))))) (defun org-noter--set-notes-scroll (window &rest ignored) (when window From c1ed2454a6fce1e184971f5b58579bc03b447660 Mon Sep 17 00:00:00 2001 From: c1-g Date: Thu, 13 Jan 2022 13:39:41 +0700 Subject: [PATCH 059/453] Separate core function to its own file. --- org-noter-core.el | 2408 +++++++++++++++++++++++++++++++++++++++++++++ org-noter.el | 2373 +------------------------------------------- 2 files changed, 2411 insertions(+), 2370 deletions(-) create mode 100644 org-noter-core.el diff --git a/org-noter-core.el b/org-noter-core.el new file mode 100644 index 0000000..0477c62 --- /dev/null +++ b/org-noter-core.el @@ -0,0 +1,2408 @@ +;;; org-noter-core.el --- Core functions of Org-noter -*- lexical-binding: t; -*- + +;; Copyright (C) 2017-2018 Gonçalo Santos + +;; Author: Gonçalo Santos (aka. weirdNox@GitHub) +;; Homepage: https://github.com/weirdNox/org-noter +;; Keywords: lisp pdf interleave annotate external sync notes documents org-mode +;; Package-Requires: ((emacs "24.4") (cl-lib "0.6") (org "9.0")) +;; Version: 1.4.1 + +;; This file is not part of GNU Emacs. + +;; 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 . + +;;; Commentary: + +;; The idea is to let you create notes that are kept in sync when you scroll through the +;; document, but that are external to it - the notes themselves live in an Org-mode file. As +;; such, this leverages the power of Org-mode (the notes may have outlines, latex fragments, +;; babel, etc...) while acting like notes that are made /in/ the document. + +;; Also, I must thank Sebastian for the original idea and inspiration! +;; Link to the original Interleave package: +;; https://github.com/rudolfochrist/interleave + +;;; Code: +(require 'org) +(require 'org-element) +(require 'cl-lib) +(require 'pdf-tools) + +(declare-function doc-view-goto-page "doc-view") +(declare-function image-display-size "image-mode") +(declare-function image-get-display-property "image-mode") +(declare-function image-mode-window-get "image-mode") +(declare-function image-scroll-up "image-mode") +(declare-function nov-render-document "ext:nov") +(declare-function org-attach-dir "org-attach") +(declare-function org-attach-file-list "org-attach") +(declare-function pdf-info-getannots "ext:pdf-info") +(declare-function pdf-info-gettext "ext:pdf-info") +(declare-function pdf-info-outline "ext:pdf-info") +(declare-function pdf-info-pagelinks "ext:pdf-info") +;; (declare-function pdf-util-tooltip-arrow "ext:pdf-util") +(declare-function pdf-view-active-region "ext:pdf-view") +(declare-function pdf-view-active-region-p "ext:pdf-view") +(declare-function pdf-view-active-region-text "ext:pdf-view") +(declare-function pdf-view-goto-page "ext:pdf-view") +(declare-function pdf-view-mode "ext:pdf-view") +(defvar nov-documents-index) +(defvar nov-file-name) + +;; -------------------------------------------------------------------------------- +;;; User variables +(defgroup org-noter nil + "A synchronized, external annotator" + :group 'convenience + :version "25.3.1") + +(defcustom org-noter-property-doc-file "NOTER_DOCUMENT" + "Name of the property that specifies the document." + :group 'org-noter + :type 'string) + +(defcustom org-noter-property-note-location "NOTER_PAGE" + "Name of the property that specifies the location of the current note. +The default value is still NOTER_PAGE for backwards compatibility." + :group 'org-noter + :type 'string) + +(defcustom org-noter-default-heading-title "Notes for page $p$" + "The default title for headings created with `org-noter-insert-note'. +$p$ is replaced with the number of the page or chapter you are in +at the moment." + :group 'org-noter + :type 'string) + +(defcustom org-noter-notes-window-behavior '(start scroll) + "This setting specifies in what situations the notes window should be created. + +When the list contains: +- `start', the window will be created when starting a `org-noter' session. +- `scroll', it will be created when you go to a location with an associated note. +- `only-prev', it will be created when you go to a location without notes, but that + has previous notes that are shown." + :group 'org-noter + :type '(set (const :tag "Session start" start) + (const :tag "Scroll to location with notes" scroll) + (const :tag "Scroll to location with previous notes only" only-prev))) + +(defcustom org-noter-notes-window-location 'horizontal-split + "Whether the notes should appear in the main frame (horizontal or vertical split) or in a separate frame. + +Note that this will only have effect on session startup if `start' +is member of `org-noter-notes-window-behavior' (which see)." + :group 'org-noter + :type '(choice (const :tag "Horizontal" horizontal-split) + (const :tag "Vertical" vertical-split) + (const :tag "Other frame" other-frame))) + +(define-obsolete-variable-alias 'org-noter-doc-split-percentage 'org-noter-doc-split-fraction "1.2.0") +(defcustom org-noter-doc-split-fraction '(0.5 . 0.5) + "Fraction of the frame that the document window will occupy when split. +This is a cons of the type (HORIZONTAL-FRACTION . VERTICAL-FRACTION)." + :group 'org-noter + :type '(cons (number :tag "Horizontal fraction") (number :tag "Vertical fraction"))) + +(defcustom org-noter-auto-save-last-location nil + "When non-nil, save the last visited location automatically; when starting a new session, go to that location." + :group 'org-noter + :type 'boolean) + +(defcustom org-noter-hide-other t + "When non-nil, hide all headings not related to the command used. +For example, when scrolling to pages with notes, collapse all the +notes that are not annotating the current page." + :group 'org-noter + :type 'boolean) + +(defcustom org-noter-always-create-frame t + "When non-nil, org-noter will always create a new frame for the session. +When nil, it will use the selected frame if it does not belong to any other session." + :group 'org-noter + :type 'boolean) + +(defcustom org-noter-disable-narrowing nil + "Disable narrowing in notes/org buffer." + :group 'org-noter + :type 'boolean) + +(defcustom org-noter-use-indirect-buffer t + "When non-nil, org-noter will create an indirect buffer of the calling +org file as a note buffer of the session. +When nil, it will use the real buffer." + :group 'org-noter + :type 'boolean) + +(defcustom org-noter-swap-window nil + "By default `org-noter' will make a session by setting the buffer of the selected window +to the document buffer then split with the window of the notes buffer on the right. + +If this variable is non-nil, the buffers of the two windows will be the other way around." + :group 'org-noter + :type 'boolean) + + +(defcustom org-noter-suggest-from-attachments t + "When non-nil, org-noter will suggest files from the attachments +when creating a session, if the document is missing." + :group 'org-noter + :type 'boolean) + +(defcustom org-noter-separate-notes-from-heading nil + "When non-nil, add an empty line between each note's heading and content." + :group 'org-noter + :type 'boolean) + +(defcustom org-noter-insert-selected-text-inside-note t + "When non-nil, it will automatically append the selected text into an existing note." + :group 'org-noter + :type 'boolean) + +(defcustom org-noter-closest-tipping-point 0.3 + "Defines when to show the closest previous note. + +Let x be (this value)*100. The following schematic represents the +view (eg. a page of a PDF): + ++----+ +| | -> If there are notes in here, the closest previous note is not shown ++----+--> Tipping point, at x% of the view +| | -> When _all_ notes are in here, below the tipping point, the closest +| | previous note will be shown. ++----+ + +When this value is negative, disable this feature. + +This setting may be overridden in a document with the function +`org-noter-set-closest-tipping-point', which see." + :group 'org-noter + :type 'number) + +(defcustom org-noter-default-notes-file-names '("Notes.org") + "List of possible names for the default notes file, in increasing order of priority." + :group 'org-noter + :type '(repeat string)) + +(defcustom org-noter-notes-search-path '("~/Documents") + "List of paths to check (non recursively) when searching for a notes file." + :group 'org-noter + :type '(repeat string)) + +(defcustom org-noter-arrow-delay 0.2 + "Number of seconds from when the command was invoked until the tooltip arrow appears. + +When set to a negative number, the arrow tooltip is disabled. +This is needed in order to keep Emacs from hanging when doing many syncs." + :group 'org-noter + :type 'number) + +(defcustom org-noter-doc-property-in-notes nil + "If non-nil, every new note will have the document property too. +This makes moving notes out of the root heading easier." + :group 'org-noter + :type 'boolean) + +(defcustom org-noter-insert-note-no-questions nil + "When non-nil, `org-noter-insert-note' won't ask for a title and will always insert a new note. +The title used will be the default one." + :group 'org-noter + :type 'boolean) + +(defcustom org-noter-kill-frame-at-session-end t + "If non-nil, `org-noter-kill-session' will delete the frame if others exist on the current display.'" + :group 'org-noter + :type 'boolean) + +(defcustom org-noter-insert-heading-hook nil + "Hook being run after inserting a new heading." + :group 'org-noter + :type 'hook) + +(defface org-noter-no-notes-exist-face + '((t + :foreground "chocolate" + :weight bold)) + "Face for modeline note count, when 0." + :group 'org-noter) + +(defface org-noter-notes-exist-face + '((t + :foreground "SpringGreen" + :weight bold)) + "Face for modeline note count, when not 0." + :group 'org-noter) + +;; -------------------------------------------------------------------------------- +;;; Integration with other packages +(defcustom org-noter--check-location-property-hook nil + "TODO" + :group 'org-noter + :type 'hook) + +(defcustom org-noter--parse-location-property-hook nil + "TODO" + :group 'org-noter + :type 'hook) + +(defcustom org-noter--pretty-print-location-hook nil + "TODO" + :group 'org-noter + :type 'hook) + +(defcustom org-noter--convert-to-location-cons-hook nil + "TODO" + :group 'org-noter + :type 'hook) + +(defcustom org-noter--doc-goto-location-hook nil + "TODO" + :group 'org-noter + :type 'hook) + +(defcustom org-noter--note-after-tipping-point-hook nil + "TODO" + :group 'org-noter + :type 'hook) + +(defcustom org-noter--relative-position-to-view-hook nil + "TODO" + :group 'org-noter + :type 'hook) + +(defcustom org-noter--get-precise-info-hook nil + "TODO" + :group 'org-noter + :type 'hook) + +(defcustom org-noter--doc-approx-location-hook nil + "TODO" + :group 'org-noter + :type 'hook) + +;; -------------------------------------------------------------------------------- +;;; Private variables or constants +(cl-defstruct org-noter--session + id frame doc-buffer notes-buffer ast modified-tick doc-mode display-name notes-file-path property-text + level num-notes-in-view window-behavior window-location doc-split-fraction auto-save-last-location + hide-other closest-tipping-point) + +(defvar org-noter--sessions nil + "List of `org-noter' sessions.") + +(defvar-local org-noter--session nil + "Session associated with the current buffer.") + +(defvar org-noter--inhibit-location-change-handler nil + "Prevent location change from updating point in notes.") + +(defvar org-noter--start-location-override nil + "Used to open the session from the document in the right page.") + +(defvar-local org-noter--nov-timer nil + "Timer for synchronizing notes after scrolling.") + +(defvar org-noter--arrow-location nil + "A vector [TIMER WINDOW TOP LEFT] that shows where the arrow should appear, when idling.") + +(defvar org-noter--completing-read-keymap (make-sparse-keymap) + "A `completing-read' keymap that let's the user insert spaces.") + +(set-keymap-parent org-noter--completing-read-keymap minibuffer-local-completion-map) +(define-key org-noter--completing-read-keymap (kbd "SPC") 'self-insert-command) + +(defconst org-noter--property-behavior "NOTER_NOTES_BEHAVIOR" + "Property for overriding global `org-noter-notes-window-behavior'.") + +(defconst org-noter--property-location "NOTER_NOTES_LOCATION" + "Property for overriding global `org-noter-notes-window-location'.") + +(defconst org-noter--property-doc-split-fraction "NOTER_DOCUMENT_SPLIT_FRACTION" + "Property for overriding global `org-noter-doc-split-fraction'.") + +(defconst org-noter--property-auto-save-last-location "NOTER_AUTO_SAVE_LAST_LOCATION" + "Property for overriding global `org-noter-auto-save-last-location'.") + +(defconst org-noter--property-hide-other "NOTER_HIDE_OTHER" + "Property for overriding global `org-noter-hide-other'.") + +(defconst org-noter--property-closest-tipping-point "NOTER_CLOSEST_TIPPING_POINT" + "Property for overriding global `org-noter-closest-tipping-point'.") + +(defconst org-noter--note-search-no-recurse (delete 'headline (append org-element-all-elements nil)) + "List of elements that shouldn't be recursed into when searching for notes.") + +(defconst org-noter--id-text-property 'org-noter-session-id + "Text property used to mark the headings with open sessions.") + +;; -------------------------------------------------------------------------------- +;;; Utility functions +(defun org-noter--get-new-id () + (catch 'break + (while t + (let ((id (random most-positive-fixnum))) + (unless (cl-loop for session in org-noter--sessions + when (= (org-noter--session-id session) id) return t) + (throw 'break id)))))) + +(defmacro org-noter--property-or-default (name) + (let ((function-name (intern (concat "org-noter--" (symbol-name name) "-property"))) + (variable (intern (concat "org-noter-" (symbol-name name))))) + `(let ((prop-value (,function-name ast))) + (cond ((eq prop-value 'disable) nil) + (prop-value) + (t ,variable))))) + +(defun org-noter--create-session (ast document-property-value notes-file-path) + (let* ((raw-value-not-empty (> (length (org-element-property :raw-value ast)) 0)) + (display-name (if raw-value-not-empty + (org-element-property :raw-value ast) + (file-name-nondirectory document-property-value))) + (frame-name (format "Emacs Org-noter - %s" display-name)) + + (document (find-file-noselect document-property-value)) + (document-path (expand-file-name document-property-value)) + (document-major-mode (buffer-local-value 'major-mode document)) + (document-buffer-name + (generate-new-buffer-name (concat (unless raw-value-not-empty "Org-noter: ") display-name))) + (document-buffer + (if (memq document-major-mode '(nov-mode djvu-read-mode)) + document + (make-indirect-buffer document document-buffer-name t))) + + (notes-buffer + (if org-noter-use-indirect-buffer + (make-indirect-buffer + (or (buffer-base-buffer) (current-buffer)) + (generate-new-buffer-name (concat "Notes of " display-name)) t) + (current-buffer))) + + (session + (make-org-noter--session + :id (org-noter--get-new-id) + :display-name display-name + :frame + (if (or org-noter-always-create-frame + (catch 'has-session + (dolist (test-session org-noter--sessions) + (when (eq (org-noter--session-frame test-session) (selected-frame)) + (throw 'has-session t))))) + (make-frame `((name . ,frame-name) (fullscreen . maximized))) + (set-frame-parameter nil 'name frame-name) + (selected-frame)) + :doc-mode document-major-mode + :property-text document-property-value + :notes-file-path notes-file-path + :doc-buffer document-buffer + :notes-buffer notes-buffer + :level (org-element-property :level ast) + :window-behavior (org-noter--property-or-default notes-window-behavior) + :window-location (org-noter--property-or-default notes-window-location) + :doc-split-fraction (org-noter--property-or-default doc-split-fraction) + :auto-save-last-location (org-noter--property-or-default auto-save-last-location) + :hide-other (org-noter--property-or-default hide-other) + :closest-tipping-point (org-noter--property-or-default closest-tipping-point) + :modified-tick -1)) + + (target-location org-noter--start-location-override) + (starting-point (point))) + + (add-hook 'delete-frame-functions 'org-noter--handle-delete-frame) + (push session org-noter--sessions) + + (with-current-buffer document-buffer + (cond + ;; NOTE(nox): PDF Tools + ((eq document-major-mode 'pdf-view-mode) + (setq buffer-file-name document-path) + (pdf-view-mode) + (add-hook 'pdf-view-after-change-page-hook 'org-noter--doc-location-change-handler nil t)) + + ;; NOTE(nox): DocView + ((eq document-major-mode 'doc-view-mode) + (setq buffer-file-name document-path) + (doc-view-mode) + (advice-add 'doc-view-goto-page :after 'org-noter--location-change-advice)) + + ;; NOTE(nox): Nov.el + ((eq document-major-mode 'nov-mode) + (rename-buffer document-buffer-name) + (advice-add 'nov-render-document :after 'org-noter--nov-scroll-handler) + (add-hook 'window-scroll-functions 'org-noter--nov-scroll-handler nil t)) + + ;; NOTE(c1-g): Djvu + + ((eq document-major-mode 'djvu-read-mode) + (advice-add 'djvu-init-page :after 'org-noter--location-change-advice)) + + (t (error "This document handler is not supported :/"))) + + (org-noter-doc-mode 1) + (setq org-noter--session session) + (add-hook 'kill-buffer-hook 'org-noter--handle-kill-buffer nil t)) + + (with-current-buffer notes-buffer + (org-noter-notes-mode 1) + ;; NOTE(nox): This is needed because a session created in an indirect buffer would use the point of + ;; the base buffer (as this buffer is indirect to the base!) + (goto-char starting-point) + (setq buffer-file-name notes-file-path + org-noter--session session + fringe-indicator-alist '((truncation . nil))) + (add-hook 'kill-buffer-hook 'org-noter--handle-kill-buffer nil t) + (add-hook 'window-scroll-functions 'org-noter--set-notes-scroll nil t) + (org-noter--set-text-properties (org-noter--parse-root (vector notes-buffer document-property-value)) + (org-noter--session-id session)) + (unless target-location + (setq target-location (org-noter--parse-location-property (org-noter--get-containing-heading t))))) + + ;; NOTE(nox): This timer is for preventing reflowing too soon. + (run-with-idle-timer + 0.05 nil + (lambda () + ;; NOTE(ahmed-shariff): setup-window run here to avoid crash when notes buffer not setup in time + (org-noter--setup-windows session) + (with-current-buffer document-buffer + (let ((org-noter--inhibit-location-change-handler t)) + (when target-location (org-noter--doc-goto-location target-location))) + (org-noter--doc-location-change-handler)))))) + +(defun org-noter--valid-session (session) + (when session + (if (and (frame-live-p (org-noter--session-frame session)) + (buffer-live-p (org-noter--session-doc-buffer session)) + (buffer-live-p (org-noter--session-notes-buffer session))) + t + (org-noter-kill-session session) + nil))) + +(defmacro org-noter--with-valid-session (&rest body) + (declare (debug (body))) + `(let ((session org-noter--session)) + (when (org-noter--valid-session session) + (progn ,@body)))) + +(defun org-noter--handle-kill-buffer () + (org-noter--with-valid-session + (let ((buffer (current-buffer)) + (notes-buffer (org-noter--session-notes-buffer session)) + (doc-buffer (org-noter--session-doc-buffer session))) + ;; NOTE(nox): This needs to be checked in order to prevent session killing because of + ;; temporary buffers with the same local variables + (when (or (eq buffer notes-buffer) + (eq buffer doc-buffer)) + (org-noter-kill-session session))))) + +(defun org-noter--handle-delete-frame (frame) + (dolist (session org-noter--sessions) + (when (eq (org-noter--session-frame session) frame) + (org-noter-kill-session session)))) + +(defun org-noter--parse-root (&optional info) + "Parse and return the root AST. +When used, the INFO argument may be an org-noter session or a vector [NotesBuffer PropertyText]. +If nil, the session used will be `org-noter--session'." + (let* ((arg-is-session (org-noter--session-p info)) + (session (or (and arg-is-session info) org-noter--session)) + root-pos ast) + (cond + ((and (not arg-is-session) (vectorp info)) + ;; NOTE(nox): Use arguments to find heading, by trying to find the outermost parent heading with + ;; the specified property + (let ((notes-buffer (aref info 0)) + (wanted-prop (aref info 1))) + (unless (and (buffer-live-p notes-buffer) (stringp wanted-prop) + (eq (buffer-local-value 'major-mode notes-buffer) 'org-mode)) + (error "Error parsing root with invalid arguments")) + + (with-current-buffer notes-buffer + (org-with-wide-buffer + (catch 'break + (while t + (when (string= (or (org-entry-get nil org-noter-property-doc-file t) + (cadar (org-collect-keywords (list org-noter-property-doc-file)))) + wanted-prop) + (setq root-pos (copy-marker (point)))) + (unless (org-up-heading-safe) (throw 'break t)))))))) + + ((org-noter--valid-session session) + ;; NOTE(nox): Use session to find heading + (or (and (= (buffer-chars-modified-tick (org-noter--session-notes-buffer session)) + (org-noter--session-modified-tick session)) + (setq ast (org-noter--session-ast session))) ; NOTE(nox): Cached version! + + ;; NOTE(nox): Find session id text property + (with-current-buffer (org-noter--session-notes-buffer session) + (org-with-wide-buffer + (let ((pos (text-property-any (point-min) (point-max) org-noter--id-text-property + (org-noter--session-id session)))) + (when pos (setq root-pos (copy-marker pos))))))))) + + (unless ast + (unless root-pos (error "Root heading not found")) + (with-current-buffer (marker-buffer root-pos) + (org-with-wide-buffer + (goto-char (marker-position root-pos)) + (when (org-before-first-heading-p) + (org-next-visible-heading 1)) + (org-narrow-to-subtree) + (setq ast (car (org-element-contents (org-element-parse-buffer 'greater-element)))) + (when (and (not (vectorp info)) (org-noter--valid-session session)) + (setf (org-noter--session-ast session) ast + (org-noter--session-modified-tick session) (buffer-chars-modified-tick)))))) + ast)) + +(defun org-noter--get-properties-end (ast &optional force-trim) + (when ast + (let* ((contents (org-element-contents ast)) + (section (org-element-map contents 'section 'identity nil t 'headline)) + (properties (org-element-map section 'property-drawer 'identity nil t)) + properties-end) + (if (not properties) + (org-element-property :contents-begin ast) + (setq properties-end (org-element-property :end properties)) + (when (or force-trim + (= (org-element-property :end section) properties-end)) + (while (not (eq (char-before properties-end) ?:)) + (setq properties-end (1- properties-end)))) + properties-end)))) + +(defun org-noter--set-text-properties (ast id) + (org-with-wide-buffer + (when ast + (let* ((level (org-element-property :level ast)) + (begin (org-element-property :begin ast)) + (title-begin (+ 1 level begin)) + (contents-begin (org-element-property :contents-begin ast)) + (properties-end (org-noter--get-properties-end ast t)) + (inhibit-read-only t) + (modified (buffer-modified-p))) + (add-text-properties (max 1 (1- begin)) begin '(read-only t)) + (add-text-properties begin (1- title-begin) `(read-only t front-sticky t ,org-noter--id-text-property ,id)) + (add-text-properties (1- title-begin) title-begin '(read-only t rear-nonsticky t)) + ;; (add-text-properties (1- contents-begin) (1- properties-end) '(read-only t)) + (when properties-end + (add-text-properties (1- properties-end) properties-end + '(read-only t rear-nonsticky t)) + (set-buffer-modified-p modified)))))) + +(defun org-noter--unset-text-properties (ast) + (when ast + (org-with-wide-buffer + (let* ((begin (org-element-property :begin ast)) + (end (org-noter--get-properties-end ast t)) + (inhibit-read-only t) + (modified (buffer-modified-p))) + (when end + (remove-list-of-text-properties (max 1 (1- begin)) end + `(read-only front-sticky rear-nonsticky ,org-noter--id-text-property)) + + (set-buffer-modified-p modified)))))) + +(defun org-noter--set-notes-scroll (window &rest ignored) + (when window + (with-selected-window window + (org-noter--with-valid-session + (let* ((level (org-noter--session-level session)) + (goal (* (1- level) 2)) + (current-scroll (window-hscroll))) + (when (and (bound-and-true-p org-indent-mode) (< current-scroll goal)) + (scroll-right current-scroll) + (scroll-left goal t))))))) + +(defun org-noter--insert-heading (level title &optional newlines-number location) + "Insert a new heading at LEVEL with TITLE. +The point will be at the start of the contents, after any +properties, by a margin of NEWLINES-NUMBER." + (setq newlines-number (or newlines-number 1)) + (org-insert-heading nil t) + (let* ((initial-level (org-element-property :level (org-element-at-point))) + (changer (if (> level initial-level) 'org-do-demote 'org-do-promote)) + (number-of-times (abs (- level initial-level)))) + (dotimes (_ number-of-times) (funcall changer)) + (insert (org-trim (replace-regexp-in-string "\n" " " title))) + + (org-end-of-subtree) + (unless (bolp) (insert "\n")) + (org-N-empty-lines-before-current (1- newlines-number)) + + (when location + (org-entry-put nil org-noter-property-note-location (org-noter--pretty-print-location location)) + + (when org-noter-doc-property-in-notes + (org-noter--with-valid-session + (org-entry-put nil org-noter-property-doc-file (org-noter--session-property-text session)) + (org-entry-put nil org-noter--property-auto-save-last-location "nil")))) + + (run-hooks 'org-noter-insert-heading-hook))) + +(defun org-noter--narrow-to-root (ast) + (when ast + (save-excursion + (goto-char (org-element-property :contents-begin ast)) + (org-show-entry) + (org-narrow-to-subtree) + (org-cycle-hide-drawers 'all)))) + +(defun org-noter--get-doc-window () + (org-noter--with-valid-session + (or (get-buffer-window (org-noter--session-doc-buffer session) + (org-noter--session-frame session)) + (org-noter--setup-windows org-noter--session) + (get-buffer-window (org-noter--session-doc-buffer session) + (org-noter--session-frame session))))) + +(defun org-noter--get-notes-window (&optional type) + (org-noter--with-valid-session + (let ((notes-buffer (org-noter--session-notes-buffer session)) + (window-location (org-noter--session-window-location session)) + (window-behavior (org-noter--session-window-behavior session)) + notes-window) + (or (get-buffer-window notes-buffer t) + (when (or (eq type 'force) (memq type window-behavior)) + (if (eq window-location 'other-frame) + (let ((restore-frame (selected-frame))) + (switch-to-buffer-other-frame notes-buffer) + (setq notes-window (get-buffer-window notes-buffer t)) + (x-focus-frame restore-frame) + (raise-frame (window-frame notes-window))) + + (with-selected-window (org-noter--get-doc-window) + (let ((horizontal (eq window-location 'horizontal-split))) + (setq + notes-window + (if (window-combined-p nil horizontal) + ;; NOTE(nox): Reuse already existent window + (let ((sibling-window (or (window-next-sibling) (window-prev-sibling)))) + (or (window-top-child sibling-window) (window-left-child sibling-window) + sibling-window)) + + (if horizontal + (split-window-right (ceiling (* (car (org-noter--session-doc-split-fraction session)) + (window-total-width)))) + (split-window-below (ceiling (* (cdr (org-noter--session-doc-split-fraction session)) + (window-total-height))))))))) + + (set-window-buffer notes-window notes-buffer)) + notes-window))))) + +(defun org-noter--setup-windows (session) + "Setup windows when starting session, respecting user configuration." + (when (org-noter--valid-session session) + (with-selected-frame (org-noter--session-frame session) + (delete-other-windows) + (let* ((doc-buffer (org-noter--session-doc-buffer session)) + (doc-window (selected-window)) + (notes-buffer (org-noter--session-notes-buffer session)) + (window-location (org-noter--session-window-location session)) + notes-window) + + (set-window-buffer doc-window doc-buffer) + + (with-current-buffer notes-buffer + (unless org-noter-disable-narrowing + (org-noter--narrow-to-root (org-noter--parse-root session))) + (setq notes-window (org-noter--get-notes-window 'start)) + (org-noter--set-notes-scroll notes-window)) + + (when org-noter-swap-window + (cl-labels ((swap-windows (window1 window2) + "Swap the buffers of WINDOW1 and WINDOW2." + (let ((buffer1 (window-buffer window1)) + (buffer2 (window-buffer window2))) + (set-window-buffer window1 buffer2) + (set-window-buffer window2 buffer1) + (select-window window2)))) + (let ((frame (window-frame notes-window))) + (when (and (frame-live-p frame) + (not (eq frame (selected-frame)))) + (select-frame-set-input-focus (window-frame notes-window))) + (when (and (window-live-p notes-window) + (not (eq notes-window doc-window))) + (swap-windows notes-window doc-window)))) + + (if (eq window-location 'horizontal-split) + (shrink-window (- (window-total-width) (ceiling (* (car (org-noter--session-doc-split-fraction session)) + (window-total-width)))) + t) + (shrink-window (- (window-total-width) (ceiling (* (cdr (org-noter--session-doc-split-fraction session)) + (window-total-height))))))) + + (if org-noter-swap-window + ;; the variable NOTES-WINDOW here is really + ;; the document window since the two got swapped + (set-window-dedicated-p notes-window t) + ;; It's not swapped so set it normally + (set-window-dedicated-p doc-window t)))))) + +(defmacro org-noter--with-selected-notes-window (error-str &rest body) + (declare (debug ([&optional stringp] body))) + (let ((with-error (stringp error-str))) + `(org-noter--with-valid-session + (let ((notes-window (org-noter--get-notes-window))) + (if notes-window + (with-selected-window notes-window + ,(if with-error + `(progn ,@body) + (if body + `(progn ,error-str ,@body) + `(progn ,error-str)))) + ,(when with-error `(user-error "%s" ,error-str))))))) + +(defun org-noter--notes-window-behavior-property (ast) + (let ((property (org-element-property (intern (concat ":" org-noter--property-behavior)) ast)) + value) + (when (and (stringp property) (> (length property) 0)) + (setq value (car (read-from-string property))) + (when (listp value) value)))) + +(defun org-noter--notes-window-location-property (ast) + (let ((property (org-element-property (intern (concat ":" org-noter--property-location)) ast)) + value) + (when (and (stringp property) (> (length property) 0)) + (setq value (intern property)) + (when (memq value '(horizontal-split vertical-split other-frame)) value)))) + +(defun org-noter--doc-split-fraction-property (ast) + (let ((property (org-element-property (intern (concat ":" org-noter--property-doc-split-fraction)) ast)) + value) + (when (and (stringp property) (> (length property) 0)) + (setq value (car (read-from-string property))) + (when (consp value) value)))) + +(defun org-noter--auto-save-last-location-property (ast) + (let ((property (org-element-property (intern (concat ":" org-noter--property-auto-save-last-location)) ast))) + (when (and (stringp property) (> (length property) 0)) + (if (intern property) t 'disable)))) + +(defun org-noter--hide-other-property (ast) + (let ((property (org-element-property (intern (concat ":" org-noter--property-hide-other)) ast))) + (when (and (stringp property) (> (length property) 0)) + (if (intern property) t 'disable)))) + +(defun org-noter--closest-tipping-point-property (ast) + (let ((property (org-element-property (intern (concat ":" org-noter--property-closest-tipping-point)) ast))) + (when (and (stringp property) (> (length property) 0)) + (ignore-errors (string-to-number property))))) + +(defun org-noter--doc-approx-location-cons (&optional precise-info) + (cond + ((memq major-mode '(doc-view-mode pdf-view-mode)) + (cons (image-mode-window-get 'page) (if (and (consp precise-info) + (numberp (car precise-info)) + (numberp (cdr precise-info))) + precise-info 0))) + ((eq major-mode 'nov-mode) + (cons nov-documents-index (if (or (numberp precise-info) + (and (consp precise-info) + (numberp (car precise-info)) + (numberp (cdr precise-info)))) + precise-info + (max 1 (/ (+ (window-start) (window-end nil t)) 2))))) + + ((eq major-mode 'djvu-read-mode) + (cons djvu-doc-page (if (numberp precise-info) precise-info 0))) + + (t (error "Unknown document type %s" major-mode)))) + +(defun org-noter--doc-approx-location (&optional precise-info force-new-ref) + "TODO" + (let ((window (if (org-noter--valid-session org-noter--session) + (org-noter--get-doc-window) + (selected-window)))) + (cl-assert window) + (with-selected-window window + (or (run-hook-with-args-until-success 'org-noter--doc-approx-location-hook major-mode + precise-info force-new-ref) + (org-noter--doc-approx-location-cons precise-info))))) + +(defun org-noter--location-change-advice (&rest _) + (org-noter--with-valid-session (org-noter--doc-location-change-handler))) + +(defun org-noter--nov-scroll-handler (&rest _) + (when org-noter--nov-timer (cancel-timer org-noter--nov-timer)) + (unless org-noter--inhibit-location-change-handler + (setq org-noter--nov-timer (run-with-timer 0.25 nil 'org-noter--doc-location-change-handler)))) + +(defsubst org-noter--doc-file-property (headline) + (or (org-element-property (intern (concat ":" org-noter-property-doc-file)) headline) + (cadar (org-collect-keywords (list org-noter-property-doc-file))) + (org-entry-get nil org-noter-property-doc-file t))) + +(defun org-noter--check-location-property (arg) + (let ((property (if (stringp arg) arg + (org-element-property (intern (concat ":" org-noter-property-note-location)) arg)))) + (when (and (stringp property) (> (length property) 0)) + (or (run-hook-with-args-until-success 'org-noter--check-location-property-hook property) + (let ((value (car (read-from-string property)))) + (or (and (consp value) (integerp (car value)) (numberp (cdr value))) + (integerp value))))))) + +(defun org-noter--parse-location-property (arg) + (let ((property (if (stringp arg) arg + (org-element-property (intern (concat ":" org-noter-property-note-location)) arg)))) + (when (and (stringp property) (> (length property) 0)) + (or (run-hook-with-args-until-success 'org-noter--parse-location-property-hook property) + (let ((value (car (read-from-string property)))) + (cond ((and (consp value) (integerp (car value)) (numberp (cdr value))) value) + ((and (consp value) (integerp (car value)) (consp (cdr value)) (numberp (cadr value)) (numberp (cddr value))) value) + ((integerp value) (cons value 0)))))))) + +(defun org-noter--pretty-print-location (location) + (org-noter--with-valid-session + (or (run-hook-with-args-until-success 'org-noter--pretty-print-location-hook location) + (format "%s" (cond + ((memq (org-noter--session-doc-mode session) '(doc-view-mode pdf-view-mode djvu-read-mode)) + (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 0)) + (car location) + location)) + + ((eq (org-noter--session-doc-mode session) 'nov-mode) + (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 1)) + (org-noter--get-location-page location) + location))))))) + +(defun org-noter--get-containing-heading (&optional include-root) + "Get smallest containing heading that encloses the point and has location property. +If the point isn't inside any heading with location property, return the outer heading. +When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." + (org-noter--with-valid-session + (org-with-wide-buffer + (unless (org-before-first-heading-p) + (org-back-to-heading t) + (let (previous) + (catch 'break + (while t + (let ((prop (org-noter--check-location-property (org-entry-get nil org-noter-property-note-location))) + (at-root (equal (org-noter--session-id session) + (get-text-property (point) org-noter--id-text-property))) + (heading (org-element-at-point))) + (when (and prop (or include-root (not at-root))) + (throw 'break heading)) + + (when (or at-root (not (org-up-heading-safe))) + (throw 'break (if include-root heading previous))) + + (setq previous heading))))))))) + +(defun org-noter--doc-get-page-slice () + "Return (slice-top . slice-height)." + (let* ((slice (or (image-mode-window-get 'slice) '(0 0 1 1))) + (slice-left (float (nth 0 slice))) + (slice-top (float (nth 1 slice))) + (slice-width (float (nth 2 slice))) + (slice-height (float (nth 3 slice)))) + (when (or (> slice-top 1) + (> slice-height 1)) + (let ((height (cdr (image-size (image-mode-window-get 'image) t)))) + (setq slice-top (/ slice-top height) + slice-height (/ slice-height height)))) + (when (or (> slice-width 1) + (> slice-left 1)) + (let ((width (car (image-size (image-mode-window-get 'image) t)))) + (setq slice-width (/ slice-width height) + slice-left (/ slice-left height)))) + (list slice-top slice-height slice-left slice-width))) + +(defun org-noter--conv-page-scroll-percentage (vscroll &optional hscroll) + (let* ((slice (org-noter--doc-get-page-slice)) + (display-size (image-display-size (image-get-display-property))) + (display-percentage-height (/ vscroll (cdr display-size))) + (hpercentage (max 0 (min 1 (+ (nth 0 slice) (* (nth 1 slice) display-percentage-height)))))) + (if hscroll + (cons hpercentage (max 0 (min 1 (+ (nth 2 slice) (* (nth 3 slice) (/ vscroll (car display-size))))))) + (cons hpercentage 0)))) + +(defun org-noter--conv-page-percentage-scroll (percentage) + (let* ((slice (org-noter--doc-get-page-slice)) + (display-height (cdr (image-display-size (image-get-display-property)))) + (display-percentage (min 1 (max 0 (/ (- percentage (nth 0 slice)) (nth 1 slice))))) + (scroll (max 0 (floor (* display-percentage display-height))))) + scroll)) + +(defun org-noter--get-precise-info () + (org-noter--with-valid-session + (let ((window (org-noter--get-doc-window)) + (mode (org-noter--session-doc-mode session)) + event) + (with-selected-window window + (cond + ((run-hook-with-args-until-success 'org-noter--get-precise-info-hook mode)) + + ((eq mode 'pdf-view-mode) + (if (pdf-view-active-region-p) + (let ((edges (pdf-view-active-region))) + (cons + (cadar edges) + (caar edges))) + + (while (not (and (eq 'mouse-1 (car event)) + (eq window (posn-window (event-start event))))) + (setq event (read-event "Click where you want the start of the note to be!"))) + (let ((col-row (posn-col-row (event-start event)))) + (org-noter--conv-page-scroll-percentage (+ (window-vscroll) (cdr col-row)) + (+ (window-hscroll) (car col-row)))))) + + ((eq mode 'doc-view-mode) + (while (not (and (eq 'mouse-1 (car event)) + (eq window (posn-window (event-start event))))) + (setq event (read-event "Click where you want the start of the note to be!"))) + (org-noter--conv-page-scroll-percentage (+ (window-vscroll) + (cdr (posn-col-row (event-start event)))))) + + ((eq mode 'nov-mode) + (if (region-active-p) + (cons (mark) (point)) + (while (not (and (eq 'mouse-1 (car event)) + (eq window (posn-window (event-start event))))) + (setq event (read-event "Click where you want the start of the note to be!"))) + (posn-point (event-start event)))) + + ((eq mode 'djvu-read-mode) + (if (region-active-p) + (cons (mark) (point)) + (while (not (and (eq 'mouse-1 (car event)) + (eq window (posn-window (event-start event))))) + (setq event (read-event "Click where you want the start of the note to be!"))) + (posn-point (event-start event))))))))) + +(defun org-noter--show-arrow () + (when (and org-noter--arrow-location + (window-live-p (aref org-noter--arrow-location 1))) + (with-selected-window (aref org-noter--arrow-location 1) + ;; From `pdf-util-tooltip-arrow'. + (pdf-util-assert-pdf-window) + (let* (x-gtk-use-system-tooltips + (image-top (if (floatp (aref org-noter--arrow-location 2)) + (round (* (aref org-noter--arrow-location 2) + (cdr (pdf-view-image-size)))))) + (image-left (if (floatp (aref org-noter--arrow-location 3)) + (round (* (aref org-noter--arrow-location 3) (car (pdf-view-image-size)))))) + + (dx (or image-left + (+ (or (car (window-margins)) 0) + (car (window-fringes))))) + (dy (or image-top 0)) + (pos (list dx dy dx (+ dy (* 2 (frame-char-height))))) + (vscroll (pdf-util-required-vscroll pos)) + (tooltip-frame-parameters + `((border-width . 0) + (internal-border-width . 0) + ,@tooltip-frame-parameters)) + (tooltip-hide-delay 3)) + + (when vscroll + (image-set-window-vscroll vscroll)) + (setq dy (max 0 (- dy + (cdr (pdf-view-image-offset)) + (window-vscroll nil t) + (frame-char-height)))) + (when (overlay-get (pdf-view-current-overlay) 'before-string) + (let* ((e (window-inside-pixel-edges)) + (xw (pdf-util-with-edges (e) e-width))) + (cl-incf dx (/ (- xw (car (pdf-view-image-size t))) 2)))) + (pdf-util-tooltip-in-window + (propertize + " " 'display (propertize + "\u2192" ;; right arrow + 'display '(height 2) + 'face `(:foreground + "orange red" + :background + ,(if (bound-and-true-p pdf-view-midnight-minor-mode) + (cdr pdf-view-midnight-colors) + "white")))) + dx dy)) + (setq org-noter--arrow-location nil)))) + +(defun org-noter--get-location-top (location) + "Get the top coordinate given a LOCATION of form (page top . left) or (page . top)." + (if (listp (cdr location)) + (cadr location) + (cdr location))) + +(defun org-noter--get-location-page (location) + "Get the page number given a LOCATION of form (page top . left) or (page . top)." + (car location)) + +(defun org-noter--get-location-left (location) + "Get the left coordinate given a LOCATION of form (page top . left) or (page . top). If later form of vector is passed return 0." + (if (listp (cdr location)) + (cddr location) + 0)) + +(defun org-noter--doc-goto-location (location) + "Go to location specified by LOCATION." + (org-noter--with-valid-session + (let ((window (org-noter--get-doc-window)) + (mode (org-noter--session-doc-mode session))) + (with-selected-window window + (cond + ((run-hook-with-args-until-success 'org-noter--doc-goto-location-hook mode location)) + + ((memq mode '(doc-view-mode pdf-view-mode)) + (let ((top (org-noter--get-location-top location)) + (left (org-noter--get-location-left location))) + + (if (eq mode 'doc-view-mode) + (doc-view-goto-page (org-noter--get-location-page location)) + (pdf-view-goto-page (org-noter--get-location-page location)) + ;; NOTE(nox): This timer is needed because the tooltip may introduce a delay, + ;; so syncing multiple pages was slow + (when (>= org-noter-arrow-delay 0) + (when org-noter--arrow-location (cancel-timer (aref org-noter--arrow-location 0))) + (setq org-noter--arrow-location + (vector (run-with-idle-timer org-noter-arrow-delay nil 'org-noter--show-arrow) + window + top + left)))) + (image-scroll-up (- (org-noter--conv-page-percentage-scroll top) + (window-vscroll))))) + + ((eq mode 'nov-mode) + (setq nov-documents-index (org-noter--get-location-page location)) + (nov-render-document) + (goto-char (org-noter--get-location-top location)) + (recenter)) + ;; NOTE(nox): This needs to be here, because it would be issued anyway after + ;; everything and would run org-noter--nov-scroll-handler. + + ((eq mode 'djvu-read-mode) + (djvu-goto-page (car location)) + (goto-char (org-noter--get-location-top location)))) + (redisplay))))) + +(defun org-noter--compare-location-cons (comp l1 l2) + "Compare L1 and L2, which are location cons. +See `org-noter--compare-locations'" + (cl-assert (and (consp l1) (consp l2))) + (cond ((eq comp '=) + (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) + (= (org-noter--get-location-top l1) (org-noter--get-location-top l2)) + (= (org-noter--get-location-left l1) (org-noter--get-location-left l2)))) + ((eq comp '<) + (or (< (org-noter--get-location-page l1) (org-noter--get-location-page l2)) + (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) + (< (org-noter--get-location-top l1) (org-noter--get-location-top l2))) + (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) + (= (org-noter--get-location-top l1) (org-noter--get-location-top l2)) + (< (org-noter--get-location-left l1) (org-noter--get-location-left l2))))) + ((eq comp '<=) + (or (< (org-noter--get-location-page l1) (org-noter--get-location-page l2)) + (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) + (<= (org-noter--get-location-top l1) (org-noter--get-location-top l2))) + (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) + (= (org-noter--get-location-top l1) (org-noter--get-location-top l2)) + (<= (org-noter--get-location-left l1) (org-noter--get-location-left l2))))) + ((eq comp '>) + (or (> (org-noter--get-location-page l1) (org-noter--get-location-page l2)) + (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) + (> (org-noter--get-location-top l1) (org-noter--get-location-top l2))) + (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) + (= (org-noter--get-location-top l1) (org-noter--get-location-top l2)) + (> (org-noter--get-location-left l1) (org-noter--get-location-left l2))))) + ((eq comp '>=) + (or (> (org-noter--get-location-page l1) (org-noter--get-location-page l2)) + (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) + (>= (org-noter--get-location-top l1) (org-noter--get-location-top l2))) + (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) + (= (org-noter--get-location-top l1) (org-noter--get-location-top l2)) + (>= (org-noter--get-location-left l1) (org-noter--get-location-left l2))))) + ((eq comp '>f) + (or (> (org-noter--get-location-page l1) (org-noter--get-location-page l2)) + (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) + (< (org-noter--get-location-top l1) (org-noter--get-location-top l2))) + (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) + (= (org-noter--get-location-top l1) (org-noter--get-location-top l2)) + (< (org-noter--get-location-left l1) (org-noter--get-location-left l2))))) + (t (error "Comparison operator %s not known" comp)))) + +(defun org-noter--compare-locations (comp l1 l2) + "Compare L1 and L2. +When COMP is '<, '<=, '>, or '>=, it works as expected. +When COMP is '>f, it will return t when L1 is a page greater than +L2 or, when in the same page, if L1 is the _f_irst of the two." + (cond ((not l1) nil) + ((not l2) t) + (t + (setq l1 (or (run-hook-with-args-until-success 'org-noter--convert-to-location-cons-hook l1) l1) + l2 (or (run-hook-with-args-until-success 'org-noter--convert-to-location-cons-hook l2) l2)) + (if (numberp (cdr l2)) + (org-noter--compare-location-cons comp l1 l2) + (org-noter--compare-location-cons comp l1 (cons (car l2) (cadr l2))))))) + +(defun org-noter--show-note-entry (session note) + "This will show the note entry and its children. +Every direct subheading _until_ the first heading that doesn't +belong to the same view (ie. until a heading with location or +document property) will be opened." + (save-excursion + (goto-char (org-element-property :contents-begin note)) + (org-show-set-visibility t) + (org-element-map (org-element-contents note) 'headline + (lambda (headline) + (let ((doc-file (org-noter--doc-file-property headline))) + (if (or (and doc-file (not (string= doc-file (org-noter--session-property-text session)))) + (org-noter--check-location-property headline)) + t + (goto-char (org-element-property :begin headline)) + (org-show-entry) + (org-show-children) + nil))) + nil t org-element-all-elements))) + +(defun org-noter--focus-notes-region (view-info) + (org-noter--with-selected-notes-window + (if (org-noter--session-hide-other session) + (save-excursion + (goto-char (org-element-property :begin (org-noter--parse-root))) + (outline-hide-subtree)) + (org-cycle-hide-drawers 'all)) + + (let* ((notes-cons (org-noter--view-info-notes view-info)) + (regions (or (org-noter--view-info-regions view-info) + (org-noter--view-info-prev-regions view-info))) + (point-before (point)) + target-region + point-inside-target-region) + (cond + (notes-cons + (dolist (note-cons notes-cons) (org-noter--show-note-entry session (car note-cons))) + + (setq target-region (or (catch 'result (dolist (region regions) + (when (and (>= point-before (car region)) + (or (save-restriction (goto-char (cdr region)) (eobp)) + (< point-before (cdr region)))) + (setq point-inside-target-region t) + (throw 'result region)))) + (car regions))) + + (let ((begin (car target-region)) (end (cdr target-region)) num-lines + (target-char (if point-inside-target-region + point-before + (org-noter--get-properties-end (caar notes-cons)))) + (window-start (window-start)) (window-end (window-end nil t))) + (setq num-lines (count-screen-lines begin end)) + + (cond + ((> num-lines (window-height)) + (goto-char begin) + (recenter 0)) + + ((< begin window-start) + (goto-char begin) + (recenter 0)) + + ((> end window-end) + (goto-char end) + (recenter -2))) + + (goto-char target-char))) + + (t (org-noter--show-note-entry session (org-noter--parse-root))))) + + (org-cycle-show-empty-lines t))) + +(defun org-noter--get-current-view () + "Return a vector with the current view information." + (org-noter--with-valid-session + (let ((mode (org-noter--session-doc-mode session))) + (with-selected-window (org-noter--get-doc-window) + (cond ((memq mode '(doc-view-mode pdf-view-mode djvu-read-mode)) + (vector 'paged (car (org-noter--doc-approx-location-cons)))) + ((eq mode 'nov-mode) + (vector 'nov + (org-noter--doc-approx-location-cons (window-start)) + (org-noter--doc-approx-location-cons (window-end nil t)))) + (t (error "Unknown document type"))))))) + +(defun org-noter--note-after-tipping-point (point location view) + ;; NOTE(nox): This __assumes__ the note is inside the view! + (let (hook-result) + (cond + ((setq hook-result (run-hook-with-args-until-success 'org-noter--note-after-tipping-point-hook + point location view)) + (cdr hook-result)) + ((eq (aref view 0) 'paged) + (> (org-noter--get-location-top location) point)) + ((eq (aref view 0) 'nov) + (> (org-noter--get-location-top location) (+ (* point (- (cdr (aref view 2)) (cdr (aref view 1)))) + (cdr (aref view 1)))))))) + +(defun org-noter--relative-position-to-view (location view) + (cond + ((run-hook-with-args-until-success 'org-noter--relative-position-to-view-hook location view)) + + ((eq (aref view 0) 'paged) + (let ((note-page (org-noter--get-location-page location)) + (view-page (aref view 1))) + (cond ((< note-page view-page) 'before) + ((= note-page view-page) 'inside) + (t 'after)))) + + ((eq (aref view 0) 'nov) + (let ((view-top (aref view 1)) + (view-bot (aref view 2))) + (cond ((org-noter--compare-locations '< location view-top) 'before) + ((org-noter--compare-locations '<= location view-bot) 'inside) + (t 'after)))))) + +(defmacro org-noter--view-region-finish (info &optional terminating-headline) + `(when ,info + ,(if terminating-headline + `(push (cons (aref ,info 1) (min (aref ,info 2) (org-element-property :begin ,terminating-headline))) + (gv-deref (aref ,info 0))) + `(push (cons (aref ,info 1) (aref ,info 2)) (gv-deref (aref ,info 0)))) + (setq ,info nil))) + +(defmacro org-noter--view-region-add (info list-name headline) + `(progn + (when (and ,info (not (eq (aref ,info 3) ',list-name))) + (org-noter--view-region-finish ,info ,headline)) + + (if ,info + (setf (aref ,info 2) (max (aref ,info 2) (org-element-property :end ,headline))) + (setq ,info (vector (gv-ref ,list-name) + (org-element-property :begin ,headline) (org-element-property :end ,headline) + ',list-name))))) + +;; NOTE(nox): notes is a list of (HEADING . HEADING-TO-INSERT-TEXT-BEFORE): +;; - HEADING is the root heading of the note +;; - SHOULD-ADD-SPACE indicates if there should be extra spacing when inserting text to the note (ie. the +;; note has contents) +(cl-defstruct org-noter--view-info notes regions prev-regions reference-for-insertion) + +(defun org-noter--get-view-info (view &optional new-location) + "Return VIEW related information. + +When optional NEW-LOCATION is provided, it will be used to find +the best heading to serve as a reference to create the new one +relative to." + (when view + (org-noter--with-valid-session + (let ((contents (org-element-contents (org-noter--parse-root))) + (preamble t) + notes-in-view regions-in-view + reference-for-insertion reference-location + (all-after-tipping-point t) + (closest-tipping-point (and (>= (org-noter--session-closest-tipping-point session) 0) + (org-noter--session-closest-tipping-point session))) + closest-notes closest-notes-regions closest-notes-location + ignore-until-level + current-region-info) ;; NOTE(nox): [REGIONS-LIST-PTR START MAX-END REGIONS-LIST-NAME] + + (org-element-map contents 'headline + (lambda (headline) + (let ((doc-file (org-noter--doc-file-property headline)) + (location (org-noter--parse-location-property headline))) + (when (and ignore-until-level (<= (org-element-property :level headline) ignore-until-level)) + (setq ignore-until-level nil)) + + (cond + (ignore-until-level) ;; NOTE(nox): This heading is ignored, do nothing + + ((and doc-file (not (string= doc-file (org-noter--session-property-text session)))) + (org-noter--view-region-finish current-region-info headline) + (setq ignore-until-level (org-element-property :level headline)) + (when (and preamble new-location + (or (not reference-for-insertion) + (>= (org-element-property :begin headline) + (org-element-property :end (cdr reference-for-insertion))))) + (setq reference-for-insertion (cons 'after headline)))) + + (location + (let ((relative-position (org-noter--relative-position-to-view location view))) + (cond + ((eq relative-position 'inside) + (push (cons headline nil) notes-in-view) + + (org-noter--view-region-add current-region-info regions-in-view headline) + + (setq all-after-tipping-point + (and all-after-tipping-point (org-noter--note-after-tipping-point + closest-tipping-point location view)))) + + (t + (when current-region-info + (let ((note-cons-to-change (cond ((eq (aref current-region-info 3) 'regions-in-view) + (car notes-in-view)) + ((eq (aref current-region-info 3) 'closest-notes-regions) + (car closest-notes))))) + (when (< (org-element-property :begin headline) + (org-element-property :end (car note-cons-to-change))) + (setcdr note-cons-to-change headline)))) + + (let ((eligible-for-before (and closest-tipping-point all-after-tipping-point + (eq relative-position 'before)))) + (cond ((and eligible-for-before + (org-noter--compare-locations '> location closest-notes-location)) + (setq closest-notes (list (cons headline nil)) + closest-notes-location location + current-region-info nil + closest-notes-regions nil) + (org-noter--view-region-add current-region-info closest-notes-regions headline)) + + ((and eligible-for-before (equal location closest-notes-location)) + (push (cons headline nil) closest-notes) + (org-noter--view-region-add current-region-info closest-notes-regions headline)) + + (t (org-noter--view-region-finish current-region-info headline))))))) + + (when new-location + (setq preamble nil) + (cond ((and (org-noter--compare-locations '<= location new-location) + (or (eq (car reference-for-insertion) 'before) + (org-noter--compare-locations '>= location reference-location))) + (setq reference-for-insertion (cons 'after headline) + reference-location location)) + + ((and (eq (car reference-for-insertion) 'after) + (< (org-element-property :begin headline) + (org-element-property :end (cdr reference-for-insertion))) + (org-noter--compare-locations '>= location new-location)) + (setq reference-for-insertion (cons 'before headline) + reference-location location))))) + + (t + (when (and preamble new-location + (or (not reference-for-insertion) + (>= (org-element-property :begin headline) + (org-element-property :end (cdr reference-for-insertion))))) + (setq reference-for-insertion (cons 'after headline))))))) + nil nil org-noter--note-search-no-recurse) + + (org-noter--view-region-finish current-region-info) + + (setf (org-noter--session-num-notes-in-view session) (length notes-in-view)) + + (when all-after-tipping-point (setq notes-in-view (append closest-notes notes-in-view))) + + (make-org-noter--view-info + :notes (nreverse notes-in-view) + :regions (nreverse regions-in-view) + :prev-regions (nreverse closest-notes-regions) + :reference-for-insertion reference-for-insertion))))) + +(defun org-noter--make-view-info-for-single-note (session headline) + (let ((not-belonging-element + (org-element-map (org-element-contents headline) 'headline + (lambda (headline) + (let ((doc-file (org-noter--doc-file-property headline))) + (and (or (and doc-file (not (string= doc-file (org-noter--session-property-text session)))) + (org-noter--check-location-property headline)) + headline))) + nil t))) + + (make-org-noter--view-info + ;; NOTE(nox): The cdr is only used when inserting, doesn't matter here + :notes (list (cons headline nil)) + :regions (list (cons (org-element-property :begin headline) + (or (and not-belonging-element (org-element-property :begin not-belonging-element)) + (org-element-property :end headline))))))) + +(defun org-noter--doc-location-change-handler () + (org-noter--with-valid-session + (let ((view-info (org-noter--get-view-info (org-noter--get-current-view)))) + (force-mode-line-update t) + (unless org-noter--inhibit-location-change-handler + (org-noter--get-notes-window (cond ((org-noter--view-info-regions view-info) 'scroll) + ((org-noter--view-info-prev-regions view-info) 'only-prev))) + (org-noter--focus-notes-region view-info))) + + (when (org-noter--session-auto-save-last-location session) (org-noter-set-start-location)))) + +(defun org-noter--mode-line-text () + (org-noter--with-valid-session + (let* ((number-of-notes (or (org-noter--session-num-notes-in-view session) 0))) + (cond ((= number-of-notes 0) (propertize " 0 notes " 'face 'org-noter-no-notes-exist-face)) + ((= number-of-notes 1) (propertize " 1 note " 'face 'org-noter-notes-exist-face)) + (t (propertize (format " %d notes " number-of-notes) 'face 'org-noter-notes-exist-face)))))) + +;; NOTE(nox): From machc/pdf-tools-org +(defun org-noter--pdf-tools-edges-to-region (edges) + "Get 4-entry region (LEFT TOP RIGHT BOTTOM) from several EDGES." + (when edges + (let ((left0 (nth 0 (car edges))) + (top0 (nth 1 (car edges))) + (bottom0 (nth 3 (car edges))) + (top1 (nth 1 (car (last edges)))) + (right1 (nth 2 (car (last edges)))) + (bottom1 (nth 3 (car (last edges))))) + (list left0 + (+ top0 (/ (- bottom0 top0) 3)) + right1 + (- bottom1 (/ (- bottom1 top1) 3)))))) + +(defun org-noter--check-if-document-is-annotated-on-file (document-path notes-path) + ;; NOTE(nox): In order to insert the correct file contents + (let ((buffer (find-buffer-visiting notes-path))) + (when buffer (with-current-buffer buffer (save-buffer))) + + (with-temp-buffer + (insert-file-contents notes-path) + (catch 'break + (while (re-search-forward (org-re-property org-noter-property-doc-file) nil t) + (when (file-equal-p (or (expand-file-name (match-string 3) (file-name-directory notes-path)) + (cadar (org-collect-keywords '(org-noter-property-doc-file)))) + document-path) + ;; NOTE(nox): This notes file has the document we want! + (throw 'break t))))))) + +(defsubst org-noter--check-doc-prop (doc-prop) + (and doc-prop (not (file-directory-p doc-prop)) (file-readable-p doc-prop))) + +(defun org-noter--get-or-read-document-property (inherit-prop &optional force-new) + (let ((doc-prop (and (not force-new) (or (org-entry-get nil org-noter-property-doc-file inherit-prop) + (cadar (org-collect-keywords (list org-noter-property-doc-file))))))) + (unless (org-noter--check-doc-prop doc-prop) + (setq doc-prop nil) + + (when org-noter-suggest-from-attachments + (require 'org-attach) + (let* ((attach-dir (org-attach-dir)) + (attach-list (and attach-dir (org-attach-file-list attach-dir)))) + (when (and attach-list (y-or-n-p "Do you want to annotate an attached file?")) + (setq doc-prop (completing-read "File to annotate: " attach-list nil t)) + (when doc-prop (setq doc-prop (file-relative-name (expand-file-name doc-prop attach-dir))))))) + + (unless (org-noter--check-doc-prop doc-prop) + (setq doc-prop (expand-file-name + (read-file-name + "Invalid or no document property found. Please specify a document path: " nil nil t))) + (when (or (file-directory-p doc-prop) (not (file-readable-p doc-prop))) (user-error "Invalid file path")) + (when (y-or-n-p "Do you want a relative file name? ") (setq doc-prop (file-relative-name doc-prop)))) + + (org-entry-put nil org-noter-property-doc-file doc-prop)) + doc-prop)) + +(defun org-noter--other-frames (&optional this-frame) + "Returns non-`nil' when there is at least another frame" + (setq this-frame (or this-frame (selected-frame))) + (catch 'other-frame + (dolist (frame (visible-frame-list)) + (unless (or (eq this-frame frame) + (frame-parent frame) + (frame-parameter frame 'delete-before)) + (throw 'other-frame frame))))) + +;; -------------------------------------------------------------------------------- +;;; User commands +(defun org-noter-set-start-location (&optional arg) + "When opening a session with this document, go to the current location. +With a prefix ARG, remove start location." + (interactive "P") + (org-noter--with-valid-session + (let ((inhibit-read-only t) + (ast (org-noter--parse-root)) + (location (org-noter--doc-approx-location 'interactive))) + (with-current-buffer (org-noter--session-notes-buffer session) + (org-with-wide-buffer + (goto-char (org-element-property :begin ast)) + (if arg + (org-entry-delete nil org-noter-property-note-location) + (org-entry-put nil org-noter-property-note-location + (org-noter--pretty-print-location location)))))))) + +(defun org-noter-set-auto-save-last-location (arg) + "This toggles saving the last visited location for this document. +With a prefix ARG, delete the current setting and use the default." + (interactive "P") + (org-noter--with-valid-session + (let ((inhibit-read-only t) + (ast (org-noter--parse-root)) + (new-setting (if arg + org-noter-auto-save-last-location + (not (org-noter--session-auto-save-last-location session))))) + (setf (org-noter--session-auto-save-last-location session) + new-setting) + (with-current-buffer (org-noter--session-notes-buffer session) + (org-with-wide-buffer + (goto-char (org-element-property :begin ast)) + (if arg + (org-entry-delete nil org-noter--property-auto-save-last-location) + (org-entry-put nil org-noter--property-auto-save-last-location (format "%s" new-setting))) + (unless new-setting (org-entry-delete nil org-noter-property-note-location))))))) + +(defun org-noter-set-hide-other (arg) + "This toggles hiding other headings for the current session. +- With a prefix \\[universal-argument], set the current setting permanently for this document. +- With a prefix \\[universal-argument] \\[universal-argument], remove the setting and use the default." + (interactive "P") + (org-noter--with-valid-session + (let* ((inhibit-read-only t) + (ast (org-noter--parse-root)) + (persistent + (cond ((equal arg '(4)) 'write) + ((equal arg '(16)) 'remove))) + (new-setting + (cond ((eq persistent 'write) (org-noter--session-hide-other session)) + ((eq persistent 'remove) org-noter-hide-other) + ('other-cases (not (org-noter--session-hide-other session)))))) + (setf (org-noter--session-hide-other session) new-setting) + (when persistent + (with-current-buffer (org-noter--session-notes-buffer session) + (org-with-wide-buffer + (goto-char (org-element-property :begin ast)) + (if (eq persistent 'write) + (org-entry-put nil org-noter--property-hide-other (format "%s" new-setting)) + (org-entry-delete nil org-noter--property-hide-other)))))))) + +(defun org-noter-set-closest-tipping-point (arg) + "This sets the closest note tipping point (see `org-noter-closest-tipping-point') +- With a prefix \\[universal-argument], set it permanently for this document. +- With a prefix \\[universal-argument] \\[universal-argument], remove the setting and use the default." + (interactive "P") + (org-noter--with-valid-session + (let* ((ast (org-noter--parse-root)) + (inhibit-read-only t) + (persistent (cond ((equal arg '(4)) 'write) + ((equal arg '(16)) 'remove))) + (new-setting (if (eq persistent 'remove) + org-noter-closest-tipping-point + (read-number "New tipping point: " (org-noter--session-closest-tipping-point session))))) + (setf (org-noter--session-closest-tipping-point session) new-setting) + (when persistent + (with-current-buffer (org-noter--session-notes-buffer session) + (org-with-wide-buffer + (goto-char (org-element-property :begin ast)) + (if (eq persistent 'write) + (org-entry-put nil org-noter--property-closest-tipping-point (format "%f" new-setting)) + (org-entry-delete nil org-noter--property-closest-tipping-point)))))))) + +(defun org-noter-set-notes-window-behavior (arg) + "Set the notes window behaviour for the current session. +With a prefix ARG, it becomes persistent for that document. + +See `org-noter-notes-window-behavior' for more information." + (interactive "P") + (org-noter--with-valid-session + (let* ((inhibit-read-only t) + (ast (org-noter--parse-root)) + (possible-behaviors (list '("Default" . default) + '("On start" . start) + '("On scroll" . scroll) + '("On scroll to location that only has previous notes" . only-prev) + '("Never" . never))) + chosen-behaviors) + + (while (> (length possible-behaviors) 1) + (let ((chosen-pair (assoc (completing-read "Behavior: " possible-behaviors nil t) possible-behaviors))) + (cond ((eq (cdr chosen-pair) 'default) (setq possible-behaviors nil)) + + ((eq (cdr chosen-pair) 'never) (setq chosen-behaviors (list 'never) + possible-behaviors nil)) + + ((eq (cdr chosen-pair) 'done) (setq possible-behaviors nil)) + + (t (push (cdr chosen-pair) chosen-behaviors) + (setq possible-behaviors (delq chosen-pair possible-behaviors)) + (when (= (length chosen-behaviors) 1) + (setq possible-behaviors (delq (rassq 'default possible-behaviors) possible-behaviors) + possible-behaviors (delq (rassq 'never possible-behaviors) possible-behaviors)) + (push (cons "Done" 'done) possible-behaviors)))))) + + (setf (org-noter--session-window-behavior session) + (or chosen-behaviors org-noter-notes-window-behavior)) + + (when arg + (with-current-buffer (org-noter--session-notes-buffer session) + (org-with-wide-buffer + (goto-char (org-element-property :begin ast)) + (if chosen-behaviors + (org-entry-put nil org-noter--property-behavior (format "%s" chosen-behaviors)) + (org-entry-delete nil org-noter--property-behavior)))))))) + +(defun org-noter-set-notes-window-location (arg) + "Set the notes window default location for the current session. +With a prefix ARG, it becomes persistent for that document. + +See `org-noter-notes-window-behavior' for more information." + (interactive "P") + (org-noter--with-valid-session + (let* ((inhibit-read-only t) + (ast (org-noter--parse-root)) + (location-possibilities + '(("Default" . nil) + ("Horizontal split" . horizontal-split) + ("Vertical split" . vertical-split) + ("Other frame" . other-frame))) + (location + (cdr (assoc (completing-read "Location: " location-possibilities nil t) + location-possibilities))) + (notes-buffer (org-noter--session-notes-buffer session))) + + (setf (org-noter--session-window-location session) + (or location org-noter-notes-window-location)) + + (let (exists) + (dolist (window (get-buffer-window-list notes-buffer nil t)) + (setq exists t) + (with-selected-frame (window-frame window) + (if (= (count-windows) 1) + (delete-frame) + (delete-window window)))) + (when exists (org-noter--get-notes-window 'force))) + + (when arg + (with-current-buffer notes-buffer + (org-with-wide-buffer + (goto-char (org-element-property :begin ast)) + (if location + (org-entry-put nil org-noter--property-location + (format "%s" location)) + (org-entry-delete nil org-noter--property-location)))))))) + +(defun org-noter-set-doc-split-fraction (arg) + "Set the fraction of the frame that the document window will occupy when split. +- With a prefix \\[universal-argument], set it permanently for this document. +- With a prefix \\[universal-argument] \\[universal-argument], remove the setting and use the default." + (interactive "P") + (org-noter--with-valid-session + (let* ((ast (org-noter--parse-root)) + (inhibit-read-only t) + (persistent (cond ((equal arg '(4)) 'write) + ((equal arg '(16)) 'remove))) + (current-setting (org-noter--session-doc-split-fraction session)) + (new-setting + (if (eq persistent 'remove) + org-noter-doc-split-fraction + (cons (read-number "Horizontal fraction: " (car current-setting)) + (read-number "Vertical fraction: " (cdr current-setting)))))) + (setf (org-noter--session-doc-split-fraction session) new-setting) + (when (org-noter--get-notes-window) + (with-current-buffer (org-noter--session-doc-buffer session) + (delete-other-windows) + (org-noter--get-notes-window 'force))) + + (when persistent + (with-current-buffer (org-noter--session-notes-buffer session) + (org-with-wide-buffer + (goto-char (org-element-property :begin ast)) + (if (eq persistent 'write) + (org-entry-put nil org-noter--property-doc-split-fraction (format "%s" new-setting)) + (org-entry-delete nil org-noter--property-doc-split-fraction)))))))) + +(defun org-noter-kill-session (&optional session) + "Kill an `org-noter' session. + +When called interactively, if there is no prefix argument and the +buffer has an annotation session, it will kill it; else, it will +show a list of open `org-noter' sessions, asking for which to +kill. + +When called from elisp code, you have to pass in the SESSION you +want to kill." + (interactive "P") + (when (and (called-interactively-p 'any) (> (length org-noter--sessions) 0)) + ;; NOTE(nox): `session' is representing a prefix argument + (if (and org-noter--session (not session)) + (setq session org-noter--session) + (setq session nil) + (let (collection default doc-display-name notes-file-name display) + (dolist (session org-noter--sessions) + (setq doc-display-name (org-noter--session-display-name session) + notes-file-name (file-name-nondirectory + (org-noter--session-notes-file-path session)) + display (concat doc-display-name " - " notes-file-name)) + (when (eq session org-noter--session) (setq default display)) + (push (cons display session) collection)) + (setq session (cdr (assoc (completing-read "Which session? " collection nil t + nil nil default) + collection)))))) + + (when (and session (memq session org-noter--sessions)) + (setq org-noter--sessions (delq session org-noter--sessions)) + + (when (eq (length org-noter--sessions) 0) + (remove-hook 'delete-frame-functions 'org-noter--handle-delete-frame) + (advice-remove 'doc-view-goto-page 'org-noter--location-change-advice) + (advice-remove 'nov-render-document 'org-noter--nov-scroll-handler)) + + (let* ((ast (org-noter--parse-root session)) + (frame (org-noter--session-frame session)) + (notes-buffer (org-noter--session-notes-buffer session)) + (base-buffer (buffer-base-buffer notes-buffer)) + (notes-modified (buffer-modified-p base-buffer)) + (doc-buffer (org-noter--session-doc-buffer session))) + + (dolist (window (get-buffer-window-list notes-buffer nil t)) + (with-selected-frame (window-frame window) + (if (= (count-windows) 1) + (when (org-noter--other-frames) (delete-frame)) + (delete-window window)))) + + (with-current-buffer notes-buffer + (remove-hook 'kill-buffer-hook 'org-noter--handle-kill-buffer t) + (restore-buffer-modified-p nil)) + (kill-buffer notes-buffer) + + (with-current-buffer base-buffer + (org-noter--unset-text-properties ast) + (set-buffer-modified-p notes-modified)) + + (with-current-buffer doc-buffer + (remove-hook 'kill-buffer-hook 'org-noter--handle-kill-buffer t)) + (kill-buffer doc-buffer) + + (when (frame-live-p frame) + (if (and (org-noter--other-frames) org-noter-kill-frame-at-session-end) + (delete-frame frame) + (progn + (delete-other-windows) + (set-frame-parameter nil 'name nil))))))) + +(defun org-noter-create-skeleton () + "Create notes skeleton based on the outline of the document." + (interactive) + (org-noter--with-valid-session + (pcase (org-noter--session-doc-mode session) + ('pdf-view-mode (org-noter-create-skeleton-pdf)) + ('djvu-read-mode (org-noter-create-skeleton-djvu)) + ('nov-mode (org-noter-create-skeleton-epub)) + (_ (user-error "This command is not supported for %s" (org-noter--session-doc-mode session)))))) + +(defun org-noter-create-skeleton-pdf () + "Create notes skeleton with the PDF outline or annotations." + (org-noter--with-valid-session + (let* ((ast (org-noter--parse-root)) + (top-level (org-element-property :level ast)) + (options '(("Outline" . (outline)) + ("Annotations" . (annots)) + ("Both" . (outline annots)))) + answer output-data) + (with-current-buffer (org-noter--session-doc-buffer session) + (setq answer (assoc (completing-read "What do you want to import? " options nil t) options)) + + (when (memq 'outline answer) + (dolist (item (pdf-info-outline)) + (let ((type (alist-get 'type item)) + (page (alist-get 'page item)) + (depth (alist-get 'depth item)) + (title (alist-get 'title item)) + (top (alist-get 'top item))) + (when (and (eq type 'goto-dest) (> page 0)) + (push (vector title (cons page top) (1+ depth) nil) output-data))))) + + (when (memq 'annots answer) + (let ((possible-annots (list '("Highlights" . highlight) + '("Underlines" . underline) + '("Squigglies" . squiggly) + '("Text notes" . text) + '("Strikeouts" . strike-out) + '("Links" . link) + '("ALL" . all))) + chosen-annots insert-contents pages-with-links) + (while (> (length possible-annots) 1) + (let* ((chosen-string (completing-read "Which types of annotations do you want? " + possible-annots nil t)) + (chosen-pair (assoc chosen-string possible-annots))) + (cond ((eq (cdr chosen-pair) 'all) + (dolist (annot possible-annots) + (when (and (cdr annot) (not (eq (cdr annot) 'all))) + (push (cdr annot) chosen-annots))) + (setq possible-annots nil)) + ((cdr chosen-pair) + (push (cdr chosen-pair) chosen-annots) + (setq possible-annots (delq chosen-pair possible-annots)) + (when (= 1 (length chosen-annots)) (push '("DONE") possible-annots))) + (t + (setq possible-annots nil))))) + + (setq insert-contents (y-or-n-p "Should we insert the annotations contents? ")) + + (dolist (item (pdf-info-getannots)) + (let* ((type (alist-get 'type item)) + (page (alist-get 'page item)) + (edges (or (org-noter--pdf-tools-edges-to-region (alist-get 'markup-edges item)) + (alist-get 'edges item))) + (top (nth 1 edges)) + (item-subject (alist-get 'subject item)) + (item-contents (alist-get 'contents item)) + name contents) + (when (and (memq type chosen-annots) (> page 0)) + (if (eq type 'link) + (cl-pushnew page pages-with-links) + (setq name (cond ((eq type 'highlight) "Highlight") + ((eq type 'underline) "Underline") + ((eq type 'squiggly) "Squiggly") + ((eq type 'text) "Text note") + ((eq type 'strike-out) "Strikeout"))) + + (when insert-contents + (setq contents (cons (pdf-info-gettext page edges) + (and (or (and item-subject (> (length item-subject) 0)) + (and item-contents (> (length item-contents) 0))) + (concat (or item-subject "") + (if (and item-subject item-contents) "\n" "") + (or item-contents "")))))) + + (push (vector (format "%s on page %d" name page) (cons page top) 'inside contents) + output-data))))) + + (dolist (page pages-with-links) + (let ((links (pdf-info-pagelinks page)) + type) + (dolist (link links) + (setq type (alist-get 'type link)) + (unless (eq type 'goto-dest) ;; NOTE(nox): Ignore internal links + (let* ((edges (alist-get 'edges link)) + (title (alist-get 'title link)) + (top (nth 1 edges)) + (target-page (alist-get 'page link)) + target heading-text) + + (unless (and title (> (length title) 0)) (setq title (pdf-info-gettext page edges))) + + (cond + ((eq type 'uri) + (setq target (alist-get 'uri link) + heading-text (format "Link on page %d: [[%s][%s]]" page target title))) + + ((eq type 'goto-remote) + (setq target (concat "file:" (alist-get 'filename link)) + heading-text (format "Link to document on page %d: [[%s][%s]]" page target title)) + (when target-page + (setq heading-text (concat heading-text (format " (target page: %d)" target-page))))) + + (t (error "Unexpected link type"))) + + (push (vector heading-text (cons page top) 'inside nil) output-data)))))))) + + + (when output-data + (if (memq 'annots answer) + (setq output-data + (sort output-data + (lambda (e1 e2) + (or (not (aref e1 1)) + (and (aref e2 1) + (org-noter--compare-locations '< (aref e1 1) (aref e2 1))))))) + (setq output-data (nreverse output-data))) + + (push (vector "Skeleton" nil 1 nil) output-data))) + + (with-current-buffer (org-noter--session-notes-buffer session) + ;; NOTE(nox): org-with-wide-buffer can't be used because we want to reset the + ;; narrow region to include the new headings + (widen) + (save-excursion + (goto-char (org-element-property :end ast)) + + (let (last-absolute-level + title location relative-level contents + level) + (dolist (data output-data) + (setq title (aref data 0) + location (aref data 1) + relative-level (aref data 2) + contents (aref data 3)) + + (if (symbolp relative-level) + (setq level (1+ last-absolute-level)) + (setq last-absolute-level (+ top-level relative-level) + level last-absolute-level)) + + (org-noter--insert-heading level title) + + (when location + (org-entry-put nil org-noter-property-note-location (org-noter--pretty-print-location location))) + + (when org-noter-doc-property-in-notes + (org-entry-put nil org-noter-property-doc-file (org-noter--session-property-text session)) + (org-entry-put nil org-noter--property-auto-save-last-location "nil")) + + (when (car contents) + (org-noter--insert-heading (1+ level) "Contents") + (insert (car contents))) + (when (cdr contents) + (org-noter--insert-heading (1+ level) "Comment") + (insert (cdr contents))))) + + (setq ast (org-noter--parse-root)) + (org-noter--narrow-to-root ast) + (goto-char (org-element-property :begin ast)) + (outline-hide-subtree) + (org-show-children 2)))))) + +(defun org-noter-create-skeleton-djvu () + (org-noter--with-valid-session + (let* ((ast (org-noter--parse-root)) + (top-level (org-element-property :level ast)) + output-data) + (require 'thingatpt) + (with-current-buffer (djvu-ref outline-buf) + (unless (string= (buffer-string) "") + (push (vector "Skeleton" nil 1) output-data) + (save-excursion + (goto-char (point-min)) + (while (not (looking-at "^$")) + (push (vector (string-trim-right (string-trim (thing-at-point 'line t)) " [[:digit:]]+") + (list (string-trim-left (string-trim (thing-at-point 'line t)) ".* ")) + (+ 2 (how-many " " (point-at-bol) (point-at-eol)))) output-data) + (next-line) + (move-beginning-of-line 1))))) + + (with-current-buffer (org-noter--session-notes-buffer session) + ;; NOTE(nox): org-with-wide-buffer can't be used because we want to reset the + ;; narrow region to include the new headings + (widen) + (save-excursion + (goto-char (org-element-property :end ast)) + + (let (last-absolute-level + title location relative-level contents + level) + + (dolist (data (nreverse output-data)) + (setq title (aref data 0) + location (aref data 1) + relative-level (aref data 2)) + + (setq last-absolute-level (+ top-level relative-level) + level last-absolute-level) + + (org-noter--insert-heading level title) + + (when location + (org-entry-put nil org-noter-property-note-location (org-noter--pretty-print-location location))) + + (when org-noter-doc-property-in-notes + (org-entry-put nil org-noter-property-doc-file (org-noter--session-property-text session)) + (org-entry-put nil org-noter--property-auto-save-last-location "nil")))) + + (setq ast (org-noter--parse-root)) + (org-noter--narrow-to-root ast) + (goto-char (org-element-property :begin ast)) + (outline-hide-subtree) + (org-show-children 2)))))) + +;; Shamelessly stolen code from Yuchen Li. +;; This code is originally from org-noter-plus package. +;; At https://github.com/yuchen-lea/org-noter-plus + +(defun org-noter--handle-nov-toc-item (ol depth) + (mapcar (lambda (li) + (mapcar (lambda (a-or-ol) + (pcase-exhaustive (dom-tag a-or-ol) + ('a + (vector :depth depth + :title (dom-text a-or-ol) + :href (esxml-node-attribute 'href a-or-ol))) + ('ol + (org-noter--handle-nov-toc-item a-or-ol + (1+ depth))))) + (dom-children li))) + (dom-children ol))) + +(defun org-noter-create-skeleton-epub () + "Epub outline with nov link." + (require 'esxml) + (require 'nov) + (require 'dom) + (org-noter--with-valid-session + (let* ((ast (org-noter--parse-root)) + (top-level (org-element-property :level ast)) + output-data) + (with-current-buffer (org-noter--session-doc-buffer session) + (let* ((toc-path (cdr (aref nov-documents 0))) + (toc-tree (with-temp-buffer + (insert (nov-ncx-to-html toc-path)) + (replace-regexp "\n" + "" + nil + (point-min) + (point-max)) + (libxml-parse-html-region (point-min) + (point-max)))) + (origin-index nov-documents-index) + (origin-point (point))) + (dolist (item + (nreverse (flatten-tree (org-noter--handle-nov-toc-item toc-tree 1)))) + (let ((relative-level (aref item 1)) + (title (aref item 3)) + (url (aref item 5))) + (apply 'nov-visit-relative-file + (nov-url-filename-and-target url)) + (when (not (integerp nov-documents-index)) + (setq nov-documents-index 0)) + (push (vector title (list nov-documents-index (point)) relative-level) output-data))) + (push (vector "Skeleton" (list 0) 1) output-data) + + (nov-goto-document origin-index) + (goto-char origin-point))) + (save-excursion + (goto-char (org-element-property :end ast)) + (with-current-buffer (org-noter--session-notes-buffer session) + (dolist (data output-data) + (setq title (aref data 0) + location (aref data 1) + relative-level (aref data 2)) + + (setq last-absolute-level (+ top-level relative-level) + level last-absolute-level) + + (org-noter--insert-heading level title) + + (when location + (org-entry-put nil org-noter-property-note-location (org-noter--pretty-print-location location))) + + (when org-noter-doc-property-in-notes + (org-entry-put nil org-noter-property-doc-file (org-noter--session-property-text session)) + (org-entry-put nil org-noter--property-auto-save-last-location "nil"))) + (setq ast (org-noter--parse-root)) + (org-noter--narrow-to-root ast) + (goto-char (org-element-property :begin ast)) + (outline-hide-subtree) + (org-show-children 2)))))) + + + +(defun org-noter-insert-note (&optional precise-info note-title) + "Insert note associated with the current location. + +This command will prompt for a title of the note and then insert +it in the notes buffer. When the input is empty, a title based on +`org-noter-default-heading-title' will be generated. + +If there are other notes related to the current location, the +prompt will also suggest them. Depending on the value of the +variable `org-noter-closest-tipping-point', it may also +suggest the closest previous note. + +PRECISE-INFO makes the new note associated with a more +specific location (see `org-noter-insert-precise-note' for more +info). + +When you insert into an existing note and have text selected on +the document buffer, the variable `org-noter-insert-selected-text-inside-note' +defines if the text should be inserted inside the note." + (interactive) + (org-noter--with-valid-session + (let* ((ast (org-noter--parse-root)) (contents (org-element-contents ast)) + (window (org-noter--get-notes-window 'force)) + (selected-text + (pcase (org-noter--session-doc-mode session) + ('pdf-view-mode + (when (pdf-view-active-region-p) + (mapconcat 'identity (pdf-view-active-region-text) ? ))) + + ((or 'nov-mode 'djvu-read-mode) + (when (region-active-p) + (buffer-substring-no-properties (mark) (point)))))) + + force-new + (location (org-noter--doc-approx-location (or precise-info 'interactive) (gv-ref force-new))) + (view-info (org-noter--get-view-info (org-noter--get-current-view) location))) + + (let ((inhibit-quit t)) + (with-local-quit + (select-frame-set-input-focus (window-frame window)) + (select-window window) + + ;; IMPORTANT(nox): Need to be careful changing the next part, it is a bit + ;; complicated to get it right... + + (let ((point (point)) + (minibuffer-local-completion-map org-noter--completing-read-keymap) + collection default default-begin title selection + (empty-lines-number (if org-noter-separate-notes-from-heading 2 1))) + + (cond + ;; NOTE(nox): Both precise and without questions will create new notes + ((or precise-info force-new) + (setq default (and selected-text (replace-regexp-in-string "\n" " " selected-text)))) + (org-noter-insert-note-no-questions) + (t + (dolist (note-cons (org-noter--view-info-notes view-info)) + (let ((display (org-element-property :raw-value (car note-cons))) + (begin (org-element-property :begin (car note-cons)))) + (push (cons display note-cons) collection) + (when (and (>= point begin) (> begin (or default-begin 0))) + (setq default display + default-begin begin)))))) + + (setq collection (nreverse collection) + title (if (or org-noter-insert-note-no-questions note-title) + (or default note-title) + (completing-read "Note: " collection nil nil nil nil default)) + selection (unless org-noter-insert-note-no-questions (cdr (assoc title collection)))) + + (if selection + ;; NOTE(nox): Inserting on an existing note + (let* ((note (car selection)) + (insert-before-element (cdr selection)) + (has-content + (eq (org-element-map (org-element-contents note) org-element-all-elements + (lambda (element) + (if (org-noter--check-location-property element) + 'stop + (not (memq (org-element-type element) '(section property-drawer))))) + nil t) + t))) + (when has-content (setq empty-lines-number 2)) + (if insert-before-element + (goto-char (org-element-property :begin insert-before-element)) + (goto-char (org-element-property :end note))) + + + (if (org-at-heading-p) + (progn + (org-N-empty-lines-before-current empty-lines-number) + (forward-line -1)) + (unless (bolp) (insert "\n")) + (org-N-empty-lines-before-current (1- empty-lines-number))) + + (when (and org-noter-insert-selected-text-inside-note selected-text) (insert selected-text))) + + ;; NOTE(nox): Inserting a new note + (let ((reference-element-cons (org-noter--view-info-reference-for-insertion view-info)) + level) + (when (zerop (length title)) + (setq title (replace-regexp-in-string (regexp-quote "$p$") (number-to-string (org-noter--get-location-page location)) + org-noter-default-heading-title))) + + (if reference-element-cons + (progn + (cond + ((eq (car reference-element-cons) 'before) + (goto-char (org-element-property :begin (cdr reference-element-cons)))) + ((eq (car reference-element-cons) 'after) + (goto-char (org-element-property :end (cdr reference-element-cons))))) + + ;; NOTE(nox): This is here to make the automatic "should insert blank" work better. + (when (org-at-heading-p) (backward-char)) + + (setq level (org-element-property :level (cdr reference-element-cons)))) + + (goto-char (org-element-map contents 'section + (lambda (section) (org-element-property :end section)) + nil t org-element-all-elements)) + (setq level (1+ (org-element-property :level ast)))) + + ;; NOTE(nox): This is needed to insert in the right place + (outline-show-entry) + (org-noter--insert-heading level title empty-lines-number location) + (when (org-noter--session-hide-other session) (org-overview)) + + (setf (org-noter--session-num-notes-in-view session) + (1+ (org-noter--session-num-notes-in-view session))))) + + (org-show-set-visibility t) + (org-cycle-hide-drawers 'all) + (org-cycle-show-empty-lines t))) + (when quit-flag + ;; NOTE(nox): If this runs, it means the user quitted while creating a note, so + ;; revert to the previous window. + (select-frame-set-input-focus (org-noter--session-frame session)) + (select-window (get-buffer-window (org-noter--session-doc-buffer session)))))))) + +(defun org-noter-insert-precise-note (&optional toggle-no-questions) + "Insert note associated with a specific location. +This will ask you to click where you want to scroll to when you +sync the document to this note. You should click on the top of +that part. Will always create a new note. + +When text is selected, it will automatically choose the top of +the selected text as the location and the text itself as the +title of the note (you may change it anyway!). + +See `org-noter-insert-note' docstring for more." + (interactive "P") + (org-noter--with-valid-session + (let ((org-noter-insert-note-no-questions (if toggle-no-questions + (not org-noter-insert-note-no-questions) + org-noter-insert-note-no-questions))) + (org-noter-insert-note (org-noter--get-precise-info))))) + + +(defun org-noter-insert-note-toggle-no-questions () + "Insert note associated with the current location. +This is like `org-noter-insert-note', except it will toggle `org-noter-insert-note-no-questions'" + (interactive) + (org-noter--with-valid-session + (let ((org-noter-insert-note-no-questions (not org-noter-insert-note-no-questions))) + (org-noter-insert-note)))) + +(defmacro org-noter--map-ignore-headings-with-doc-file (contents match-first &rest body) + `(let (ignore-until-level) + (org-element-map ,contents 'headline + (lambda (headline) + (let ((doc-file (org-noter--doc-file-property headline)) + (location (org-noter--parse-location-property headline))) + (when (and ignore-until-level (<= (org-element-property :level headline) ignore-until-level)) + (setq ignore-until-level nil)) + + (cond + (ignore-until-level nil) ;; NOTE(nox): This heading is ignored, do nothing + ((and doc-file (not (string= doc-file (org-noter--session-property-text session)))) + (setq ignore-until-level (org-element-property :level headline)) nil) + (t ,@body)))) + nil ,match-first org-noter--note-search-no-recurse))) + +(defun org-noter-sync-prev-page-or-chapter () + "Show previous page or chapter that has notes, in relation to the current page or chapter. +This will force the notes window to popup." + (interactive) + (org-noter--with-valid-session + (let ((this-location (org-noter--doc-approx-location 0)) + (contents (org-element-contents (org-noter--parse-root))) + target-location) + (org-noter--get-notes-window 'force) + + (org-noter--map-ignore-headings-with-doc-file + contents nil + (when (and (org-noter--compare-locations '< location this-location) + (org-noter--compare-locations '>f location target-location)) + (setq target-location location))) + + (org-noter--get-notes-window 'force) + (select-window (org-noter--get-doc-window)) + (if target-location + (org-noter--doc-goto-location target-location) + (user-error "There are no more previous pages or chapters with notes"))))) + +(defun org-noter-sync-current-page-or-chapter () + "Show current page or chapter notes. +This will force the notes window to popup." + (interactive) + (org-noter--with-valid-session + (let ((window (org-noter--get-notes-window 'force))) + (select-frame-set-input-focus (window-frame window)) + (select-window window) + (org-noter--doc-location-change-handler)))) + +(defun org-noter-sync-next-page-or-chapter () + "Show next page or chapter that has notes, in relation to the current page or chapter. +This will force the notes window to popup." + (interactive) + (org-noter--with-valid-session + (let ((this-location (org-noter--doc-approx-location most-positive-fixnum)) + (contents (org-element-contents (org-noter--parse-root))) + target-location) + + (org-noter--map-ignore-headings-with-doc-file + contents nil + (when (and (org-noter--compare-locations '> location this-location) + (org-noter--compare-locations '< location target-location)) + (setq target-location location))) + + (org-noter--get-notes-window 'force) + (select-window (org-noter--get-doc-window)) + (if target-location + (org-noter--doc-goto-location target-location) + (user-error "There are no more following pages or chapters with notes"))))) + +(defun org-noter-sync-prev-note () + "Go to the location of the previous note, in relation to where the point is. +As such, it will only work when the notes window exists." + (interactive) + (org-noter--with-selected-notes-window + "No notes window exists" + (let ((org-noter--inhibit-location-change-handler t) + (contents (org-element-contents (org-noter--parse-root))) + (current-begin (org-element-property :begin (org-noter--get-containing-heading))) + previous) + (when current-begin + (org-noter--map-ignore-headings-with-doc-file + contents t + (when location + (if (= current-begin (org-element-property :begin headline)) + t + (setq previous headline) + nil)))) + + (if previous + (progn + ;; NOTE(nox): This needs to be manual so we can focus the correct note + (org-noter--doc-goto-location (org-noter--parse-location-property previous)) + (org-noter--focus-notes-region (org-noter--make-view-info-for-single-note session previous))) + (user-error "There is no previous note")))) + (select-window (org-noter--get-doc-window))) + +(defun org-noter-sync-current-note () + "Go the location of the selected note, in relation to where the point is. +As such, it will only work when the notes window exists." + (interactive) + (org-noter--with-selected-notes-window + "No notes window exists" + (if (string= (or (org-entry-get nil org-noter-property-doc-file t) + (cadar (org-collect-keywords (list org-noter-property-doc-file)))) + (org-noter--session-property-text session)) + (let ((location (org-noter--parse-location-property (org-noter--get-containing-heading)))) + (if location + (org-noter--doc-goto-location location) + (user-error "No note selected"))) + (user-error "You are inside a different document"))) + (let ((window (org-noter--get-doc-window))) + (select-frame-set-input-focus (window-frame window)) + (select-window window))) + +(defun org-noter-sync-next-note () + "Go to the location of the next note, in relation to where the point is. +As such, it will only work when the notes window exists." + (interactive) + (org-noter--with-selected-notes-window + "No notes window exists" + (let ((org-noter--inhibit-location-change-handler t) + (contents (org-element-contents (org-noter--parse-root))) + next) + + (org-noter--map-ignore-headings-with-doc-file + contents t + (when (and location (< (point) (org-element-property :begin headline))) + (setq next headline))) + + (if next + (progn + (org-noter--doc-goto-location (org-noter--parse-location-property next)) + (org-noter--focus-notes-region (org-noter--make-view-info-for-single-note session next))) + (user-error "There is no next note")))) + (select-window (org-noter--get-doc-window))) + +(define-minor-mode org-noter-doc-mode + "Minor mode for the document buffer. +Keymap: +\\{org-noter-doc-mode-map}" + :keymap `((,(kbd "i") . org-noter-insert-note) + (,(kbd "C-i") . org-noter-insert-note-toggle-no-questions) + (,(kbd "M-i") . org-noter-insert-precise-note) + (,(kbd "q") . org-noter-kill-session) + (,(kbd "M-p") . org-noter-sync-prev-page-or-chapter) + (,(kbd "M-.") . org-noter-sync-current-page-or-chapter) + (,(kbd "M-n") . org-noter-sync-next-page-or-chapter) + (,(kbd "C-M-p") . org-noter-sync-prev-note) + (,(kbd "C-M-.") . org-noter-sync-current-note) + (,(kbd "C-M-n") . org-noter-sync-next-note)) + + (let ((mode-line-segment '(:eval (org-noter--mode-line-text)))) + (if org-noter-doc-mode + (if (symbolp (car-safe mode-line-format)) + (setq mode-line-format (list mode-line-segment mode-line-format)) + (push mode-line-segment mode-line-format)) + (setq mode-line-format (delete mode-line-segment mode-line-format))))) + +(define-minor-mode org-noter-notes-mode + "Minor mode for the notes buffer. +Keymap: +\\{org-noter-notes-mode-map}" + :keymap `((,(kbd "M-p") . org-noter-sync-prev-page-or-chapter) + (,(kbd "M-.") . org-noter-sync-current-page-or-chapter) + (,(kbd "M-n") . org-noter-sync-next-page-or-chapter) + (,(kbd "C-M-p") . org-noter-sync-prev-note) + (,(kbd "C-M-.") . org-noter-sync-current-note) + (,(kbd "C-M-n") . org-noter-sync-next-note))) + +(provide 'org-noter-core) +;;; org-noter-core.el ends here diff --git a/org-noter.el b/org-noter.el index a83cf23..c41e083 100644 --- a/org-noter.el +++ b/org-noter.el @@ -35,2374 +35,7 @@ ;; https://github.com/rudolfochrist/interleave ;;; Code: -(require 'org) -(require 'org-element) -(require 'cl-lib) -(require 'pdf-tools) - -(declare-function doc-view-goto-page "doc-view") -(declare-function image-display-size "image-mode") -(declare-function image-get-display-property "image-mode") -(declare-function image-mode-window-get "image-mode") -(declare-function image-scroll-up "image-mode") -(declare-function nov-render-document "ext:nov") -(declare-function org-attach-dir "org-attach") -(declare-function org-attach-file-list "org-attach") -(declare-function pdf-info-getannots "ext:pdf-info") -(declare-function pdf-info-gettext "ext:pdf-info") -(declare-function pdf-info-outline "ext:pdf-info") -(declare-function pdf-info-pagelinks "ext:pdf-info") -;; (declare-function pdf-util-tooltip-arrow "ext:pdf-util") -(declare-function pdf-view-active-region "ext:pdf-view") -(declare-function pdf-view-active-region-p "ext:pdf-view") -(declare-function pdf-view-active-region-text "ext:pdf-view") -(declare-function pdf-view-goto-page "ext:pdf-view") -(declare-function pdf-view-mode "ext:pdf-view") -(defvar nov-documents-index) -(defvar nov-file-name) - -;; -------------------------------------------------------------------------------- -;;; User variables -(defgroup org-noter nil - "A synchronized, external annotator" - :group 'convenience - :version "25.3.1") - -(defcustom org-noter-property-doc-file "NOTER_DOCUMENT" - "Name of the property that specifies the document." - :group 'org-noter - :type 'string) - -(defcustom org-noter-property-note-location "NOTER_PAGE" - "Name of the property that specifies the location of the current note. -The default value is still NOTER_PAGE for backwards compatibility." - :group 'org-noter - :type 'string) - -(defcustom org-noter-default-heading-title "Notes for page $p$" - "The default title for headings created with `org-noter-insert-note'. -$p$ is replaced with the number of the page or chapter you are in -at the moment." - :group 'org-noter - :type 'string) - -(defcustom org-noter-notes-window-behavior '(start scroll) - "This setting specifies in what situations the notes window should be created. - -When the list contains: -- `start', the window will be created when starting a `org-noter' session. -- `scroll', it will be created when you go to a location with an associated note. -- `only-prev', it will be created when you go to a location without notes, but that - has previous notes that are shown." - :group 'org-noter - :type '(set (const :tag "Session start" start) - (const :tag "Scroll to location with notes" scroll) - (const :tag "Scroll to location with previous notes only" only-prev))) - -(defcustom org-noter-notes-window-location 'horizontal-split - "Whether the notes should appear in the main frame (horizontal or vertical split) or in a separate frame. - -Note that this will only have effect on session startup if `start' -is member of `org-noter-notes-window-behavior' (which see)." - :group 'org-noter - :type '(choice (const :tag "Horizontal" horizontal-split) - (const :tag "Vertical" vertical-split) - (const :tag "Other frame" other-frame))) - -(define-obsolete-variable-alias 'org-noter-doc-split-percentage 'org-noter-doc-split-fraction "1.2.0") -(defcustom org-noter-doc-split-fraction '(0.5 . 0.5) - "Fraction of the frame that the document window will occupy when split. -This is a cons of the type (HORIZONTAL-FRACTION . VERTICAL-FRACTION)." - :group 'org-noter - :type '(cons (number :tag "Horizontal fraction") (number :tag "Vertical fraction"))) - -(defcustom org-noter-auto-save-last-location nil - "When non-nil, save the last visited location automatically; when starting a new session, go to that location." - :group 'org-noter - :type 'boolean) - -(defcustom org-noter-hide-other t - "When non-nil, hide all headings not related to the command used. -For example, when scrolling to pages with notes, collapse all the -notes that are not annotating the current page." - :group 'org-noter - :type 'boolean) - -(defcustom org-noter-always-create-frame t - "When non-nil, org-noter will always create a new frame for the session. -When nil, it will use the selected frame if it does not belong to any other session." - :group 'org-noter - :type 'boolean) - -(defcustom org-noter-disable-narrowing nil - "Disable narrowing in notes/org buffer." - :group 'org-noter - :type 'boolean) - -(defcustom org-noter-use-indirect-buffer t - "When non-nil, org-noter will create an indirect buffer of the calling -org file as a note buffer of the session. -When nil, it will use the real buffer." - :group 'org-noter - :type 'boolean) - -(defcustom org-noter-swap-window nil - "By default `org-noter' will make a session by setting the buffer of the selected window -to the document buffer then split with the window of the notes buffer on the right. - -If this variable is non-nil, the buffers of the two windows will be the other way around." - :group 'org-noter - :type 'boolean) - - -(defcustom org-noter-suggest-from-attachments t - "When non-nil, org-noter will suggest files from the attachments -when creating a session, if the document is missing." - :group 'org-noter - :type 'boolean) - -(defcustom org-noter-separate-notes-from-heading nil - "When non-nil, add an empty line between each note's heading and content." - :group 'org-noter - :type 'boolean) - -(defcustom org-noter-insert-selected-text-inside-note t - "When non-nil, it will automatically append the selected text into an existing note." - :group 'org-noter - :type 'boolean) - -(defcustom org-noter-closest-tipping-point 0.3 - "Defines when to show the closest previous note. - -Let x be (this value)*100. The following schematic represents the -view (eg. a page of a PDF): - -+----+ -| | -> If there are notes in here, the closest previous note is not shown -+----+--> Tipping point, at x% of the view -| | -> When _all_ notes are in here, below the tipping point, the closest -| | previous note will be shown. -+----+ - -When this value is negative, disable this feature. - -This setting may be overridden in a document with the function -`org-noter-set-closest-tipping-point', which see." - :group 'org-noter - :type 'number) - -(defcustom org-noter-default-notes-file-names '("Notes.org") - "List of possible names for the default notes file, in increasing order of priority." - :group 'org-noter - :type '(repeat string)) - -(defcustom org-noter-notes-search-path '("~/Documents") - "List of paths to check (non recursively) when searching for a notes file." - :group 'org-noter - :type '(repeat string)) - -(defcustom org-noter-arrow-delay 0.2 - "Number of seconds from when the command was invoked until the tooltip arrow appears. - -When set to a negative number, the arrow tooltip is disabled. -This is needed in order to keep Emacs from hanging when doing many syncs." - :group 'org-noter - :type 'number) - -(defcustom org-noter-doc-property-in-notes nil - "If non-nil, every new note will have the document property too. -This makes moving notes out of the root heading easier." - :group 'org-noter - :type 'boolean) - -(defcustom org-noter-insert-note-no-questions nil - "When non-nil, `org-noter-insert-note' won't ask for a title and will always insert a new note. -The title used will be the default one." - :group 'org-noter - :type 'boolean) - -(defcustom org-noter-kill-frame-at-session-end t - "If non-nil, `org-noter-kill-session' will delete the frame if others exist on the current display.'" - :group 'org-noter - :type 'boolean) - -(defcustom org-noter-insert-heading-hook nil - "Hook being run after inserting a new heading." - :group 'org-noter - :type 'hook) - -(defface org-noter-no-notes-exist-face - '((t - :foreground "chocolate" - :weight bold)) - "Face for modeline note count, when 0." - :group 'org-noter) - -(defface org-noter-notes-exist-face - '((t - :foreground "SpringGreen" - :weight bold)) - "Face for modeline note count, when not 0." - :group 'org-noter) - -;; -------------------------------------------------------------------------------- -;;; Integration with other packages -(defcustom org-noter--check-location-property-hook nil - "TODO" - :group 'org-noter - :type 'hook) - -(defcustom org-noter--parse-location-property-hook nil - "TODO" - :group 'org-noter - :type 'hook) - -(defcustom org-noter--pretty-print-location-hook nil - "TODO" - :group 'org-noter - :type 'hook) - -(defcustom org-noter--convert-to-location-cons-hook nil - "TODO" - :group 'org-noter - :type 'hook) - -(defcustom org-noter--doc-goto-location-hook nil - "TODO" - :group 'org-noter - :type 'hook) - -(defcustom org-noter--note-after-tipping-point-hook nil - "TODO" - :group 'org-noter - :type 'hook) - -(defcustom org-noter--relative-position-to-view-hook nil - "TODO" - :group 'org-noter - :type 'hook) - -(defcustom org-noter--get-precise-info-hook nil - "TODO" - :group 'org-noter - :type 'hook) - -(defcustom org-noter--doc-approx-location-hook nil - "TODO" - :group 'org-noter - :type 'hook) - -;; -------------------------------------------------------------------------------- -;;; Private variables or constants -(cl-defstruct org-noter--session - id frame doc-buffer notes-buffer ast modified-tick doc-mode display-name notes-file-path property-text - level num-notes-in-view window-behavior window-location doc-split-fraction auto-save-last-location - hide-other closest-tipping-point) - -(defvar org-noter--sessions nil - "List of `org-noter' sessions.") - -(defvar-local org-noter--session nil - "Session associated with the current buffer.") - -(defvar org-noter--inhibit-location-change-handler nil - "Prevent location change from updating point in notes.") - -(defvar org-noter--start-location-override nil - "Used to open the session from the document in the right page.") - -(defvar-local org-noter--nov-timer nil - "Timer for synchronizing notes after scrolling.") - -(defvar org-noter--arrow-location nil - "A vector [TIMER WINDOW TOP LEFT] that shows where the arrow should appear, when idling.") - -(defvar org-noter--completing-read-keymap (make-sparse-keymap) - "A `completing-read' keymap that let's the user insert spaces.") - -(set-keymap-parent org-noter--completing-read-keymap minibuffer-local-completion-map) -(define-key org-noter--completing-read-keymap (kbd "SPC") 'self-insert-command) - -(defconst org-noter--property-behavior "NOTER_NOTES_BEHAVIOR" - "Property for overriding global `org-noter-notes-window-behavior'.") - -(defconst org-noter--property-location "NOTER_NOTES_LOCATION" - "Property for overriding global `org-noter-notes-window-location'.") - -(defconst org-noter--property-doc-split-fraction "NOTER_DOCUMENT_SPLIT_FRACTION" - "Property for overriding global `org-noter-doc-split-fraction'.") - -(defconst org-noter--property-auto-save-last-location "NOTER_AUTO_SAVE_LAST_LOCATION" - "Property for overriding global `org-noter-auto-save-last-location'.") - -(defconst org-noter--property-hide-other "NOTER_HIDE_OTHER" - "Property for overriding global `org-noter-hide-other'.") - -(defconst org-noter--property-closest-tipping-point "NOTER_CLOSEST_TIPPING_POINT" - "Property for overriding global `org-noter-closest-tipping-point'.") - -(defconst org-noter--note-search-no-recurse (delete 'headline (append org-element-all-elements nil)) - "List of elements that shouldn't be recursed into when searching for notes.") - -(defconst org-noter--id-text-property 'org-noter-session-id - "Text property used to mark the headings with open sessions.") - -;; -------------------------------------------------------------------------------- -;;; Utility functions -(defun org-noter--get-new-id () - (catch 'break - (while t - (let ((id (random most-positive-fixnum))) - (unless (cl-loop for session in org-noter--sessions - when (= (org-noter--session-id session) id) return t) - (throw 'break id)))))) - -(defmacro org-noter--property-or-default (name) - (let ((function-name (intern (concat "org-noter--" (symbol-name name) "-property"))) - (variable (intern (concat "org-noter-" (symbol-name name))))) - `(let ((prop-value (,function-name ast))) - (cond ((eq prop-value 'disable) nil) - (prop-value) - (t ,variable))))) - -(defun org-noter--create-session (ast document-property-value notes-file-path) - (let* ((raw-value-not-empty (> (length (org-element-property :raw-value ast)) 0)) - (display-name (if raw-value-not-empty - (org-element-property :raw-value ast) - (file-name-nondirectory document-property-value))) - (frame-name (format "Emacs Org-noter - %s" display-name)) - - (document (find-file-noselect document-property-value)) - (document-path (expand-file-name document-property-value)) - (document-major-mode (buffer-local-value 'major-mode document)) - (document-buffer-name - (generate-new-buffer-name (concat (unless raw-value-not-empty "Org-noter: ") display-name))) - (document-buffer - (if (memq document-major-mode '(nov-mode djvu-read-mode)) - document - (make-indirect-buffer document document-buffer-name t))) - - (notes-buffer - (if org-noter-use-indirect-buffer - (make-indirect-buffer - (or (buffer-base-buffer) (current-buffer)) - (generate-new-buffer-name (concat "Notes of " display-name)) t) - (current-buffer))) - - (session - (make-org-noter--session - :id (org-noter--get-new-id) - :display-name display-name - :frame - (if (or org-noter-always-create-frame - (catch 'has-session - (dolist (test-session org-noter--sessions) - (when (eq (org-noter--session-frame test-session) (selected-frame)) - (throw 'has-session t))))) - (make-frame `((name . ,frame-name) (fullscreen . maximized))) - (set-frame-parameter nil 'name frame-name) - (selected-frame)) - :doc-mode document-major-mode - :property-text document-property-value - :notes-file-path notes-file-path - :doc-buffer document-buffer - :notes-buffer notes-buffer - :level (org-element-property :level ast) - :window-behavior (org-noter--property-or-default notes-window-behavior) - :window-location (org-noter--property-or-default notes-window-location) - :doc-split-fraction (org-noter--property-or-default doc-split-fraction) - :auto-save-last-location (org-noter--property-or-default auto-save-last-location) - :hide-other (org-noter--property-or-default hide-other) - :closest-tipping-point (org-noter--property-or-default closest-tipping-point) - :modified-tick -1)) - - (target-location org-noter--start-location-override) - (starting-point (point))) - - (add-hook 'delete-frame-functions 'org-noter--handle-delete-frame) - (push session org-noter--sessions) - - (with-current-buffer document-buffer - (cond - ;; NOTE(nox): PDF Tools - ((eq document-major-mode 'pdf-view-mode) - (setq buffer-file-name document-path) - (pdf-view-mode) - (add-hook 'pdf-view-after-change-page-hook 'org-noter--doc-location-change-handler nil t)) - - ;; NOTE(nox): DocView - ((eq document-major-mode 'doc-view-mode) - (setq buffer-file-name document-path) - (doc-view-mode) - (advice-add 'doc-view-goto-page :after 'org-noter--location-change-advice)) - - ;; NOTE(nox): Nov.el - ((eq document-major-mode 'nov-mode) - (rename-buffer document-buffer-name) - (advice-add 'nov-render-document :after 'org-noter--nov-scroll-handler) - (add-hook 'window-scroll-functions 'org-noter--nov-scroll-handler nil t)) - - ;; NOTE(c1-g): Djvu - - ((eq document-major-mode 'djvu-read-mode) - (advice-add 'djvu-init-page :after 'org-noter--location-change-advice)) - - (t (error "This document handler is not supported :/"))) - - (org-noter-doc-mode 1) - (setq org-noter--session session) - (add-hook 'kill-buffer-hook 'org-noter--handle-kill-buffer nil t)) - - (with-current-buffer notes-buffer - (org-noter-notes-mode 1) - ;; NOTE(nox): This is needed because a session created in an indirect buffer would use the point of - ;; the base buffer (as this buffer is indirect to the base!) - (goto-char starting-point) - (setq buffer-file-name notes-file-path - org-noter--session session - fringe-indicator-alist '((truncation . nil))) - (add-hook 'kill-buffer-hook 'org-noter--handle-kill-buffer nil t) - (add-hook 'window-scroll-functions 'org-noter--set-notes-scroll nil t) - (org-noter--set-text-properties (org-noter--parse-root (vector notes-buffer document-property-value)) - (org-noter--session-id session)) - (unless target-location - (setq target-location (org-noter--parse-location-property (org-noter--get-containing-heading t))))) - - ;; NOTE(nox): This timer is for preventing reflowing too soon. - (run-with-idle-timer - 0.05 nil - (lambda () - ;; NOTE(ahmed-shariff): setup-window run here to avoid crash when notes buffer not setup in time - (org-noter--setup-windows session) - (with-current-buffer document-buffer - (let ((org-noter--inhibit-location-change-handler t)) - (when target-location (org-noter--doc-goto-location target-location))) - (org-noter--doc-location-change-handler)))))) - -(defun org-noter--valid-session (session) - (when session - (if (and (frame-live-p (org-noter--session-frame session)) - (buffer-live-p (org-noter--session-doc-buffer session)) - (buffer-live-p (org-noter--session-notes-buffer session))) - t - (org-noter-kill-session session) - nil))) - -(defmacro org-noter--with-valid-session (&rest body) - (declare (debug (body))) - `(let ((session org-noter--session)) - (when (org-noter--valid-session session) - (progn ,@body)))) - -(defun org-noter--handle-kill-buffer () - (org-noter--with-valid-session - (let ((buffer (current-buffer)) - (notes-buffer (org-noter--session-notes-buffer session)) - (doc-buffer (org-noter--session-doc-buffer session))) - ;; NOTE(nox): This needs to be checked in order to prevent session killing because of - ;; temporary buffers with the same local variables - (when (or (eq buffer notes-buffer) - (eq buffer doc-buffer)) - (org-noter-kill-session session))))) - -(defun org-noter--handle-delete-frame (frame) - (dolist (session org-noter--sessions) - (when (eq (org-noter--session-frame session) frame) - (org-noter-kill-session session)))) - -(defun org-noter--parse-root (&optional info) - "Parse and return the root AST. -When used, the INFO argument may be an org-noter session or a vector [NotesBuffer PropertyText]. -If nil, the session used will be `org-noter--session'." - (let* ((arg-is-session (org-noter--session-p info)) - (session (or (and arg-is-session info) org-noter--session)) - root-pos ast) - (cond - ((and (not arg-is-session) (vectorp info)) - ;; NOTE(nox): Use arguments to find heading, by trying to find the outermost parent heading with - ;; the specified property - (let ((notes-buffer (aref info 0)) - (wanted-prop (aref info 1))) - (unless (and (buffer-live-p notes-buffer) (stringp wanted-prop) - (eq (buffer-local-value 'major-mode notes-buffer) 'org-mode)) - (error "Error parsing root with invalid arguments")) - - (with-current-buffer notes-buffer - (org-with-wide-buffer - (catch 'break - (while t - (when (string= (or (org-entry-get nil org-noter-property-doc-file t) - (cadar (org-collect-keywords (list org-noter-property-doc-file)))) - wanted-prop) - (setq root-pos (copy-marker (point)))) - (unless (org-up-heading-safe) (throw 'break t)))))))) - - ((org-noter--valid-session session) - ;; NOTE(nox): Use session to find heading - (or (and (= (buffer-chars-modified-tick (org-noter--session-notes-buffer session)) - (org-noter--session-modified-tick session)) - (setq ast (org-noter--session-ast session))) ; NOTE(nox): Cached version! - - ;; NOTE(nox): Find session id text property - (with-current-buffer (org-noter--session-notes-buffer session) - (org-with-wide-buffer - (let ((pos (text-property-any (point-min) (point-max) org-noter--id-text-property - (org-noter--session-id session)))) - (when pos (setq root-pos (copy-marker pos))))))))) - - (unless ast - (unless root-pos (error "Root heading not found")) - (with-current-buffer (marker-buffer root-pos) - (org-with-wide-buffer - (goto-char (marker-position root-pos)) - (when (org-before-first-heading-p) - (org-next-visible-heading 1)) - (org-narrow-to-subtree) - (setq ast (car (org-element-contents (org-element-parse-buffer 'greater-element)))) - (when (and (not (vectorp info)) (org-noter--valid-session session)) - (setf (org-noter--session-ast session) ast - (org-noter--session-modified-tick session) (buffer-chars-modified-tick)))))) - ast)) - -(defun org-noter--get-properties-end (ast &optional force-trim) - (when ast - (let* ((contents (org-element-contents ast)) - (section (org-element-map contents 'section 'identity nil t 'headline)) - (properties (org-element-map section 'property-drawer 'identity nil t)) - properties-end) - (if (not properties) - (org-element-property :contents-begin ast) - (setq properties-end (org-element-property :end properties)) - (when (or force-trim - (= (org-element-property :end section) properties-end)) - (while (not (eq (char-before properties-end) ?:)) - (setq properties-end (1- properties-end)))) - properties-end)))) - -(defun org-noter--set-text-properties (ast id) - (org-with-wide-buffer - (when ast - (let* ((level (org-element-property :level ast)) - (begin (org-element-property :begin ast)) - (title-begin (+ 1 level begin)) - (contents-begin (org-element-property :contents-begin ast)) - (properties-end (org-noter--get-properties-end ast t)) - (inhibit-read-only t) - (modified (buffer-modified-p))) - (add-text-properties (max 1 (1- begin)) begin '(read-only t)) - (add-text-properties begin (1- title-begin) `(read-only t front-sticky t ,org-noter--id-text-property ,id)) - (add-text-properties (1- title-begin) title-begin '(read-only t rear-nonsticky t)) - ;; (add-text-properties (1- contents-begin) (1- properties-end) '(read-only t)) - (when properties-end - (add-text-properties (1- properties-end) properties-end - '(read-only t rear-nonsticky t)) - (set-buffer-modified-p modified)))))) - -(defun org-noter--unset-text-properties (ast) - (when ast - (org-with-wide-buffer - (let* ((begin (org-element-property :begin ast)) - (end (org-noter--get-properties-end ast t)) - (inhibit-read-only t) - (modified (buffer-modified-p))) - (when end - (remove-list-of-text-properties (max 1 (1- begin)) end - `(read-only front-sticky rear-nonsticky ,org-noter--id-text-property)) - - (set-buffer-modified-p modified)))))) - -(defun org-noter--set-notes-scroll (window &rest ignored) - (when window - (with-selected-window window - (org-noter--with-valid-session - (let* ((level (org-noter--session-level session)) - (goal (* (1- level) 2)) - (current-scroll (window-hscroll))) - (when (and (bound-and-true-p org-indent-mode) (< current-scroll goal)) - (scroll-right current-scroll) - (scroll-left goal t))))))) - -(defun org-noter--insert-heading (level title &optional newlines-number location) - "Insert a new heading at LEVEL with TITLE. -The point will be at the start of the contents, after any -properties, by a margin of NEWLINES-NUMBER." - (setq newlines-number (or newlines-number 1)) - (org-insert-heading nil t) - (let* ((initial-level (org-element-property :level (org-element-at-point))) - (changer (if (> level initial-level) 'org-do-demote 'org-do-promote)) - (number-of-times (abs (- level initial-level)))) - (dotimes (_ number-of-times) (funcall changer)) - (insert (org-trim (replace-regexp-in-string "\n" " " title))) - - (org-end-of-subtree) - (unless (bolp) (insert "\n")) - (org-N-empty-lines-before-current (1- newlines-number)) - - (when location - (org-entry-put nil org-noter-property-note-location (org-noter--pretty-print-location location)) - - (when org-noter-doc-property-in-notes - (org-noter--with-valid-session - (org-entry-put nil org-noter-property-doc-file (org-noter--session-property-text session)) - (org-entry-put nil org-noter--property-auto-save-last-location "nil")))) - - (run-hooks 'org-noter-insert-heading-hook))) - -(defun org-noter--narrow-to-root (ast) - (when ast - (save-excursion - (goto-char (org-element-property :contents-begin ast)) - (org-show-entry) - (org-narrow-to-subtree) - (org-cycle-hide-drawers 'all)))) - -(defun org-noter--get-doc-window () - (org-noter--with-valid-session - (or (get-buffer-window (org-noter--session-doc-buffer session) - (org-noter--session-frame session)) - (org-noter--setup-windows org-noter--session) - (get-buffer-window (org-noter--session-doc-buffer session) - (org-noter--session-frame session))))) - -(defun org-noter--get-notes-window (&optional type) - (org-noter--with-valid-session - (let ((notes-buffer (org-noter--session-notes-buffer session)) - (window-location (org-noter--session-window-location session)) - (window-behavior (org-noter--session-window-behavior session)) - notes-window) - (or (get-buffer-window notes-buffer t) - (when (or (eq type 'force) (memq type window-behavior)) - (if (eq window-location 'other-frame) - (let ((restore-frame (selected-frame))) - (switch-to-buffer-other-frame notes-buffer) - (setq notes-window (get-buffer-window notes-buffer t)) - (x-focus-frame restore-frame) - (raise-frame (window-frame notes-window))) - - (with-selected-window (org-noter--get-doc-window) - (let ((horizontal (eq window-location 'horizontal-split))) - (setq - notes-window - (if (window-combined-p nil horizontal) - ;; NOTE(nox): Reuse already existent window - (let ((sibling-window (or (window-next-sibling) (window-prev-sibling)))) - (or (window-top-child sibling-window) (window-left-child sibling-window) - sibling-window)) - - (if horizontal - (split-window-right (ceiling (* (car (org-noter--session-doc-split-fraction session)) - (window-total-width)))) - (split-window-below (ceiling (* (cdr (org-noter--session-doc-split-fraction session)) - (window-total-height))))))))) - - (set-window-buffer notes-window notes-buffer)) - notes-window))))) - -(defun org-noter--setup-windows (session) - "Setup windows when starting session, respecting user configuration." - (when (org-noter--valid-session session) - (with-selected-frame (org-noter--session-frame session) - (delete-other-windows) - (let* ((doc-buffer (org-noter--session-doc-buffer session)) - (doc-window (selected-window)) - (notes-buffer (org-noter--session-notes-buffer session)) - (window-location (org-noter--session-window-location session)) - notes-window) - - (set-window-buffer doc-window doc-buffer) - - (with-current-buffer notes-buffer - (unless org-noter-disable-narrowing - (org-noter--narrow-to-root (org-noter--parse-root session))) - (setq notes-window (org-noter--get-notes-window 'start)) - (org-noter--set-notes-scroll notes-window)) - - (when org-noter-swap-window - (cl-labels ((swap-windows (window1 window2) - "Swap the buffers of WINDOW1 and WINDOW2." - (let ((buffer1 (window-buffer window1)) - (buffer2 (window-buffer window2))) - (set-window-buffer window1 buffer2) - (set-window-buffer window2 buffer1) - (select-window window2)))) - (let ((frame (window-frame notes-window))) - (when (and (frame-live-p frame) - (not (eq frame (selected-frame)))) - (select-frame-set-input-focus (window-frame notes-window))) - (when (and (window-live-p notes-window) - (not (eq notes-window doc-window))) - (swap-windows notes-window doc-window)))) - - (if (eq window-location 'horizontal-split) - (shrink-window (- (window-total-width) (ceiling (* (car (org-noter--session-doc-split-fraction session)) - (window-total-width)))) - t) - (shrink-window (- (window-total-width) (ceiling (* (cdr (org-noter--session-doc-split-fraction session)) - (window-total-height))))))) - - (if org-noter-swap-window - ;; the variable NOTES-WINDOW here is really - ;; the document window since the two got swapped - (set-window-dedicated-p notes-window t) - ;; It's not swapped so set it normally - (set-window-dedicated-p doc-window t)))))) - -(defmacro org-noter--with-selected-notes-window (error-str &rest body) - (declare (debug ([&optional stringp] body))) - (let ((with-error (stringp error-str))) - `(org-noter--with-valid-session - (let ((notes-window (org-noter--get-notes-window))) - (if notes-window - (with-selected-window notes-window - ,(if with-error - `(progn ,@body) - (if body - `(progn ,error-str ,@body) - `(progn ,error-str)))) - ,(when with-error `(user-error "%s" ,error-str))))))) - -(defun org-noter--notes-window-behavior-property (ast) - (let ((property (org-element-property (intern (concat ":" org-noter--property-behavior)) ast)) - value) - (when (and (stringp property) (> (length property) 0)) - (setq value (car (read-from-string property))) - (when (listp value) value)))) - -(defun org-noter--notes-window-location-property (ast) - (let ((property (org-element-property (intern (concat ":" org-noter--property-location)) ast)) - value) - (when (and (stringp property) (> (length property) 0)) - (setq value (intern property)) - (when (memq value '(horizontal-split vertical-split other-frame)) value)))) - -(defun org-noter--doc-split-fraction-property (ast) - (let ((property (org-element-property (intern (concat ":" org-noter--property-doc-split-fraction)) ast)) - value) - (when (and (stringp property) (> (length property) 0)) - (setq value (car (read-from-string property))) - (when (consp value) value)))) - -(defun org-noter--auto-save-last-location-property (ast) - (let ((property (org-element-property (intern (concat ":" org-noter--property-auto-save-last-location)) ast))) - (when (and (stringp property) (> (length property) 0)) - (if (intern property) t 'disable)))) - -(defun org-noter--hide-other-property (ast) - (let ((property (org-element-property (intern (concat ":" org-noter--property-hide-other)) ast))) - (when (and (stringp property) (> (length property) 0)) - (if (intern property) t 'disable)))) - -(defun org-noter--closest-tipping-point-property (ast) - (let ((property (org-element-property (intern (concat ":" org-noter--property-closest-tipping-point)) ast))) - (when (and (stringp property) (> (length property) 0)) - (ignore-errors (string-to-number property))))) - -(defun org-noter--doc-approx-location-cons (&optional precise-info) - (cond - ((memq major-mode '(doc-view-mode pdf-view-mode)) - (cons (image-mode-window-get 'page) (if (and (consp precise-info) - (numberp (car precise-info)) - (numberp (cdr precise-info))) - precise-info 0))) - ((eq major-mode 'nov-mode) - (cons nov-documents-index (if (or (numberp precise-info) - (and (consp precise-info) - (numberp (car precise-info)) - (numberp (cdr precise-info)))) - precise-info - (max 1 (/ (+ (window-start) (window-end nil t)) 2))))) - - ((eq major-mode 'djvu-read-mode) - (cons djvu-doc-page (if (numberp precise-info) precise-info 0))) - - (t (error "Unknown document type %s" major-mode)))) - -(defun org-noter--doc-approx-location (&optional precise-info force-new-ref) - "TODO" - (let ((window (if (org-noter--valid-session org-noter--session) - (org-noter--get-doc-window) - (selected-window)))) - (cl-assert window) - (with-selected-window window - (or (run-hook-with-args-until-success 'org-noter--doc-approx-location-hook major-mode - precise-info force-new-ref) - (org-noter--doc-approx-location-cons precise-info))))) - -(defun org-noter--location-change-advice (&rest _) - (org-noter--with-valid-session (org-noter--doc-location-change-handler))) - -(defun org-noter--nov-scroll-handler (&rest _) - (when org-noter--nov-timer (cancel-timer org-noter--nov-timer)) - (unless org-noter--inhibit-location-change-handler - (setq org-noter--nov-timer (run-with-timer 0.25 nil 'org-noter--doc-location-change-handler)))) - -(defsubst org-noter--doc-file-property (headline) - (or (org-element-property (intern (concat ":" org-noter-property-doc-file)) headline) - (cadar (org-collect-keywords (list org-noter-property-doc-file))) - (org-entry-get nil org-noter-property-doc-file t))) - -(defun org-noter--check-location-property (arg) - (let ((property (if (stringp arg) arg - (org-element-property (intern (concat ":" org-noter-property-note-location)) arg)))) - (when (and (stringp property) (> (length property) 0)) - (or (run-hook-with-args-until-success 'org-noter--check-location-property-hook property) - (let ((value (car (read-from-string property)))) - (or (and (consp value) (integerp (car value)) (numberp (cdr value))) - (integerp value))))))) - -(defun org-noter--parse-location-property (arg) - (let ((property (if (stringp arg) arg - (org-element-property (intern (concat ":" org-noter-property-note-location)) arg)))) - (when (and (stringp property) (> (length property) 0)) - (or (run-hook-with-args-until-success 'org-noter--parse-location-property-hook property) - (let ((value (car (read-from-string property)))) - (cond ((and (consp value) (integerp (car value)) (numberp (cdr value))) value) - ((and (consp value) (integerp (car value)) (consp (cdr value)) (numberp (cadr value)) (numberp (cddr value))) value) - ((integerp value) (cons value 0)))))))) - -(defun org-noter--pretty-print-location (location) - (org-noter--with-valid-session - (or (run-hook-with-args-until-success 'org-noter--pretty-print-location-hook location) - (format "%s" (cond - ((memq (org-noter--session-doc-mode session) '(doc-view-mode pdf-view-mode djvu-read-mode)) - (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 0)) - (car location) - location)) - - ((eq (org-noter--session-doc-mode session) 'nov-mode) - (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 1)) - (org-noter--get-location-page location) - location))))))) - -(defun org-noter--get-containing-heading (&optional include-root) - "Get smallest containing heading that encloses the point and has location property. -If the point isn't inside any heading with location property, return the outer heading. -When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." - (org-noter--with-valid-session - (org-with-wide-buffer - (unless (org-before-first-heading-p) - (org-back-to-heading t) - (let (previous) - (catch 'break - (while t - (let ((prop (org-noter--check-location-property (org-entry-get nil org-noter-property-note-location))) - (at-root (equal (org-noter--session-id session) - (get-text-property (point) org-noter--id-text-property))) - (heading (org-element-at-point))) - (when (and prop (or include-root (not at-root))) - (throw 'break heading)) - - (when (or at-root (not (org-up-heading-safe))) - (throw 'break (if include-root heading previous))) - - (setq previous heading))))))))) - -(defun org-noter--doc-get-page-slice () - "Return (slice-top . slice-height)." - (let* ((slice (or (image-mode-window-get 'slice) '(0 0 1 1))) - (slice-left (float (nth 0 slice))) - (slice-top (float (nth 1 slice))) - (slice-width (float (nth 2 slice))) - (slice-height (float (nth 3 slice)))) - (when (or (> slice-top 1) - (> slice-height 1)) - (let ((height (cdr (image-size (image-mode-window-get 'image) t)))) - (setq slice-top (/ slice-top height) - slice-height (/ slice-height height)))) - (when (or (> slice-width 1) - (> slice-left 1)) - (let ((width (car (image-size (image-mode-window-get 'image) t)))) - (setq slice-width (/ slice-width height) - slice-left (/ slice-left height)))) - (list slice-top slice-height slice-left slice-width))) - -(defun org-noter--conv-page-scroll-percentage (vscroll &optional hscroll) - (let* ((slice (org-noter--doc-get-page-slice)) - (display-size (image-display-size (image-get-display-property))) - (display-percentage-height (/ vscroll (cdr display-size))) - (hpercentage (max 0 (min 1 (+ (nth 0 slice) (* (nth 1 slice) display-percentage-height)))))) - (if hscroll - (cons hpercentage (max 0 (min 1 (+ (nth 2 slice) (* (nth 3 slice) (/ vscroll (car display-size))))))) - (cons hpercentage 0)))) - -(defun org-noter--conv-page-percentage-scroll (percentage) - (let* ((slice (org-noter--doc-get-page-slice)) - (display-height (cdr (image-display-size (image-get-display-property)))) - (display-percentage (min 1 (max 0 (/ (- percentage (nth 0 slice)) (nth 1 slice))))) - (scroll (max 0 (floor (* display-percentage display-height))))) - scroll)) - -(defun org-noter--get-precise-info () - (org-noter--with-valid-session - (let ((window (org-noter--get-doc-window)) - (mode (org-noter--session-doc-mode session)) - event) - (with-selected-window window - (cond - ((run-hook-with-args-until-success 'org-noter--get-precise-info-hook mode)) - - ((eq mode 'pdf-view-mode) - (if (pdf-view-active-region-p) - (let ((edges (pdf-view-active-region))) - (cons - (cadar edges) - (caar edges))) - - (while (not (and (eq 'mouse-1 (car event)) - (eq window (posn-window (event-start event))))) - (setq event (read-event "Click where you want the start of the note to be!"))) - (let ((col-row (posn-col-row (event-start event)))) - (org-noter--conv-page-scroll-percentage (+ (window-vscroll) (cdr col-row)) - (+ (window-hscroll) (car col-row)))))) - - ((eq mode 'doc-view-mode) - (while (not (and (eq 'mouse-1 (car event)) - (eq window (posn-window (event-start event))))) - (setq event (read-event "Click where you want the start of the note to be!"))) - (org-noter--conv-page-scroll-percentage (+ (window-vscroll) - (cdr (posn-col-row (event-start event)))))) - - ((eq mode 'nov-mode) - (if (region-active-p) - (cons (mark) (point)) - (while (not (and (eq 'mouse-1 (car event)) - (eq window (posn-window (event-start event))))) - (setq event (read-event "Click where you want the start of the note to be!"))) - (posn-point (event-start event)))) - - ((eq mode 'djvu-read-mode) - (if (region-active-p) - (cons (mark) (point)) - (while (not (and (eq 'mouse-1 (car event)) - (eq window (posn-window (event-start event))))) - (setq event (read-event "Click where you want the start of the note to be!"))) - (posn-point (event-start event))))))))) - -(defun org-noter--show-arrow () - (when (and org-noter--arrow-location - (window-live-p (aref org-noter--arrow-location 1))) - (with-selected-window (aref org-noter--arrow-location 1) - ;; From `pdf-util-tooltip-arrow'. - (pdf-util-assert-pdf-window) - (let* (x-gtk-use-system-tooltips - (image-top (if (floatp (aref org-noter--arrow-location 2)) - (round (* (aref org-noter--arrow-location 2) - (cdr (pdf-view-image-size)))))) - (image-left (if (floatp (aref org-noter--arrow-location 3)) - (round (* (aref org-noter--arrow-location 3) (car (pdf-view-image-size)))))) - - (dx (or image-left - (+ (or (car (window-margins)) 0) - (car (window-fringes))))) - (dy (or image-top 0)) - (pos (list dx dy dx (+ dy (* 2 (frame-char-height))))) - (vscroll (pdf-util-required-vscroll pos)) - (tooltip-frame-parameters - `((border-width . 0) - (internal-border-width . 0) - ,@tooltip-frame-parameters)) - (tooltip-hide-delay 3)) - - (when vscroll - (image-set-window-vscroll vscroll)) - (setq dy (max 0 (- dy - (cdr (pdf-view-image-offset)) - (window-vscroll nil t) - (frame-char-height)))) - (when (overlay-get (pdf-view-current-overlay) 'before-string) - (let* ((e (window-inside-pixel-edges)) - (xw (pdf-util-with-edges (e) e-width))) - (cl-incf dx (/ (- xw (car (pdf-view-image-size t))) 2)))) - (pdf-util-tooltip-in-window - (propertize - " " 'display (propertize - "\u2192" ;; right arrow - 'display '(height 2) - 'face `(:foreground - "orange red" - :background - ,(if (bound-and-true-p pdf-view-midnight-minor-mode) - (cdr pdf-view-midnight-colors) - "white")))) - dx dy)) - (setq org-noter--arrow-location nil)))) - -(defun org-noter--get-location-top (location) - "Get the top coordinate given a LOCATION of form (page top . left) or (page . top)." - (if (listp (cdr location)) - (cadr location) - (cdr location))) - -(defun org-noter--get-location-page (location) - "Get the page number given a LOCATION of form (page top . left) or (page . top)." - (car location)) - -(defun org-noter--get-location-left (location) - "Get the left coordinate given a LOCATION of form (page top . left) or (page . top). If later form of vector is passed return 0." - (if (listp (cdr location)) - (cddr location) - 0)) - -(defun org-noter--doc-goto-location (location) - "Go to location specified by LOCATION." - (org-noter--with-valid-session - (let ((window (org-noter--get-doc-window)) - (mode (org-noter--session-doc-mode session))) - (with-selected-window window - (cond - ((run-hook-with-args-until-success 'org-noter--doc-goto-location-hook mode location)) - - ((memq mode '(doc-view-mode pdf-view-mode)) - (let ((top (org-noter--get-location-top location)) - (left (org-noter--get-location-left location))) - - (if (eq mode 'doc-view-mode) - (doc-view-goto-page (org-noter--get-location-page location)) - (pdf-view-goto-page (org-noter--get-location-page location)) - ;; NOTE(nox): This timer is needed because the tooltip may introduce a delay, - ;; so syncing multiple pages was slow - (when (>= org-noter-arrow-delay 0) - (when org-noter--arrow-location (cancel-timer (aref org-noter--arrow-location 0))) - (setq org-noter--arrow-location - (vector (run-with-idle-timer org-noter-arrow-delay nil 'org-noter--show-arrow) - window - top - left)))) - (image-scroll-up (- (org-noter--conv-page-percentage-scroll top) - (window-vscroll))))) - - ((eq mode 'nov-mode) - (setq nov-documents-index (org-noter--get-location-page location)) - (nov-render-document) - (goto-char (org-noter--get-location-top location)) - (recenter)) - ;; NOTE(nox): This needs to be here, because it would be issued anyway after - ;; everything and would run org-noter--nov-scroll-handler. - - ((eq mode 'djvu-read-mode) - (djvu-goto-page (car location)) - (goto-char (org-noter--get-location-top location)))) - (redisplay))))) - -(defun org-noter--compare-location-cons (comp l1 l2) - "Compare L1 and L2, which are location cons. -See `org-noter--compare-locations'" - (cl-assert (and (consp l1) (consp l2))) - (cond ((eq comp '=) - (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) - (= (org-noter--get-location-top l1) (org-noter--get-location-top l2)) - (= (org-noter--get-location-left l1) (org-noter--get-location-left l2)))) - ((eq comp '<) - (or (< (org-noter--get-location-page l1) (org-noter--get-location-page l2)) - (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) - (< (org-noter--get-location-top l1) (org-noter--get-location-top l2))) - (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) - (= (org-noter--get-location-top l1) (org-noter--get-location-top l2)) - (< (org-noter--get-location-left l1) (org-noter--get-location-left l2))))) - ((eq comp '<=) - (or (< (org-noter--get-location-page l1) (org-noter--get-location-page l2)) - (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) - (<= (org-noter--get-location-top l1) (org-noter--get-location-top l2))) - (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) - (= (org-noter--get-location-top l1) (org-noter--get-location-top l2)) - (<= (org-noter--get-location-left l1) (org-noter--get-location-left l2))))) - ((eq comp '>) - (or (> (org-noter--get-location-page l1) (org-noter--get-location-page l2)) - (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) - (> (org-noter--get-location-top l1) (org-noter--get-location-top l2))) - (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) - (= (org-noter--get-location-top l1) (org-noter--get-location-top l2)) - (> (org-noter--get-location-left l1) (org-noter--get-location-left l2))))) - ((eq comp '>=) - (or (> (org-noter--get-location-page l1) (org-noter--get-location-page l2)) - (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) - (>= (org-noter--get-location-top l1) (org-noter--get-location-top l2))) - (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) - (= (org-noter--get-location-top l1) (org-noter--get-location-top l2)) - (>= (org-noter--get-location-left l1) (org-noter--get-location-left l2))))) - ((eq comp '>f) - (or (> (org-noter--get-location-page l1) (org-noter--get-location-page l2)) - (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) - (< (org-noter--get-location-top l1) (org-noter--get-location-top l2))) - (and (= (org-noter--get-location-page l1) (org-noter--get-location-page l2)) - (= (org-noter--get-location-top l1) (org-noter--get-location-top l2)) - (< (org-noter--get-location-left l1) (org-noter--get-location-left l2))))) - (t (error "Comparison operator %s not known" comp)))) - -(defun org-noter--compare-locations (comp l1 l2) - "Compare L1 and L2. -When COMP is '<, '<=, '>, or '>=, it works as expected. -When COMP is '>f, it will return t when L1 is a page greater than -L2 or, when in the same page, if L1 is the _f_irst of the two." - (cond ((not l1) nil) - ((not l2) t) - (t - (setq l1 (or (run-hook-with-args-until-success 'org-noter--convert-to-location-cons-hook l1) l1) - l2 (or (run-hook-with-args-until-success 'org-noter--convert-to-location-cons-hook l2) l2)) - (if (numberp (cdr l2)) - (org-noter--compare-location-cons comp l1 l2) - (org-noter--compare-location-cons comp l1 (cons (car l2) (cadr l2))))))) - -(defun org-noter--show-note-entry (session note) - "This will show the note entry and its children. -Every direct subheading _until_ the first heading that doesn't -belong to the same view (ie. until a heading with location or -document property) will be opened." - (save-excursion - (goto-char (org-element-property :contents-begin note)) - (org-show-set-visibility t) - (org-element-map (org-element-contents note) 'headline - (lambda (headline) - (let ((doc-file (org-noter--doc-file-property headline))) - (if (or (and doc-file (not (string= doc-file (org-noter--session-property-text session)))) - (org-noter--check-location-property headline)) - t - (goto-char (org-element-property :begin headline)) - (org-show-entry) - (org-show-children) - nil))) - nil t org-element-all-elements))) - -(defun org-noter--focus-notes-region (view-info) - (org-noter--with-selected-notes-window - (if (org-noter--session-hide-other session) - (save-excursion - (goto-char (org-element-property :begin (org-noter--parse-root))) - (outline-hide-subtree)) - (org-cycle-hide-drawers 'all)) - - (let* ((notes-cons (org-noter--view-info-notes view-info)) - (regions (or (org-noter--view-info-regions view-info) - (org-noter--view-info-prev-regions view-info))) - (point-before (point)) - target-region - point-inside-target-region) - (cond - (notes-cons - (dolist (note-cons notes-cons) (org-noter--show-note-entry session (car note-cons))) - - (setq target-region (or (catch 'result (dolist (region regions) - (when (and (>= point-before (car region)) - (or (save-restriction (goto-char (cdr region)) (eobp)) - (< point-before (cdr region)))) - (setq point-inside-target-region t) - (throw 'result region)))) - (car regions))) - - (let ((begin (car target-region)) (end (cdr target-region)) num-lines - (target-char (if point-inside-target-region - point-before - (org-noter--get-properties-end (caar notes-cons)))) - (window-start (window-start)) (window-end (window-end nil t))) - (setq num-lines (count-screen-lines begin end)) - - (cond - ((> num-lines (window-height)) - (goto-char begin) - (recenter 0)) - - ((< begin window-start) - (goto-char begin) - (recenter 0)) - - ((> end window-end) - (goto-char end) - (recenter -2))) - - (goto-char target-char))) - - (t (org-noter--show-note-entry session (org-noter--parse-root))))) - - (org-cycle-show-empty-lines t))) - -(defun org-noter--get-current-view () - "Return a vector with the current view information." - (org-noter--with-valid-session - (let ((mode (org-noter--session-doc-mode session))) - (with-selected-window (org-noter--get-doc-window) - (cond ((memq mode '(doc-view-mode pdf-view-mode djvu-read-mode)) - (vector 'paged (car (org-noter--doc-approx-location-cons)))) - ((eq mode 'nov-mode) - (vector 'nov - (org-noter--doc-approx-location-cons (window-start)) - (org-noter--doc-approx-location-cons (window-end nil t)))) - (t (error "Unknown document type"))))))) - -(defun org-noter--note-after-tipping-point (point location view) - ;; NOTE(nox): This __assumes__ the note is inside the view! - (let (hook-result) - (cond - ((setq hook-result (run-hook-with-args-until-success 'org-noter--note-after-tipping-point-hook - point location view)) - (cdr hook-result)) - ((eq (aref view 0) 'paged) - (> (org-noter--get-location-top location) point)) - ((eq (aref view 0) 'nov) - (> (org-noter--get-location-top location) (+ (* point (- (cdr (aref view 2)) (cdr (aref view 1)))) - (cdr (aref view 1)))))))) - -(defun org-noter--relative-position-to-view (location view) - (cond - ((run-hook-with-args-until-success 'org-noter--relative-position-to-view-hook location view)) - - ((eq (aref view 0) 'paged) - (let ((note-page (org-noter--get-location-page location)) - (view-page (aref view 1))) - (cond ((< note-page view-page) 'before) - ((= note-page view-page) 'inside) - (t 'after)))) - - ((eq (aref view 0) 'nov) - (let ((view-top (aref view 1)) - (view-bot (aref view 2))) - (cond ((org-noter--compare-locations '< location view-top) 'before) - ((org-noter--compare-locations '<= location view-bot) 'inside) - (t 'after)))))) - -(defmacro org-noter--view-region-finish (info &optional terminating-headline) - `(when ,info - ,(if terminating-headline - `(push (cons (aref ,info 1) (min (aref ,info 2) (org-element-property :begin ,terminating-headline))) - (gv-deref (aref ,info 0))) - `(push (cons (aref ,info 1) (aref ,info 2)) (gv-deref (aref ,info 0)))) - (setq ,info nil))) - -(defmacro org-noter--view-region-add (info list-name headline) - `(progn - (when (and ,info (not (eq (aref ,info 3) ',list-name))) - (org-noter--view-region-finish ,info ,headline)) - - (if ,info - (setf (aref ,info 2) (max (aref ,info 2) (org-element-property :end ,headline))) - (setq ,info (vector (gv-ref ,list-name) - (org-element-property :begin ,headline) (org-element-property :end ,headline) - ',list-name))))) - -;; NOTE(nox): notes is a list of (HEADING . HEADING-TO-INSERT-TEXT-BEFORE): -;; - HEADING is the root heading of the note -;; - SHOULD-ADD-SPACE indicates if there should be extra spacing when inserting text to the note (ie. the -;; note has contents) -(cl-defstruct org-noter--view-info notes regions prev-regions reference-for-insertion) - -(defun org-noter--get-view-info (view &optional new-location) - "Return VIEW related information. - -When optional NEW-LOCATION is provided, it will be used to find -the best heading to serve as a reference to create the new one -relative to." - (when view - (org-noter--with-valid-session - (let ((contents (org-element-contents (org-noter--parse-root))) - (preamble t) - notes-in-view regions-in-view - reference-for-insertion reference-location - (all-after-tipping-point t) - (closest-tipping-point (and (>= (org-noter--session-closest-tipping-point session) 0) - (org-noter--session-closest-tipping-point session))) - closest-notes closest-notes-regions closest-notes-location - ignore-until-level - current-region-info) ;; NOTE(nox): [REGIONS-LIST-PTR START MAX-END REGIONS-LIST-NAME] - - (org-element-map contents 'headline - (lambda (headline) - (let ((doc-file (org-noter--doc-file-property headline)) - (location (org-noter--parse-location-property headline))) - (when (and ignore-until-level (<= (org-element-property :level headline) ignore-until-level)) - (setq ignore-until-level nil)) - - (cond - (ignore-until-level) ;; NOTE(nox): This heading is ignored, do nothing - - ((and doc-file (not (string= doc-file (org-noter--session-property-text session)))) - (org-noter--view-region-finish current-region-info headline) - (setq ignore-until-level (org-element-property :level headline)) - (when (and preamble new-location - (or (not reference-for-insertion) - (>= (org-element-property :begin headline) - (org-element-property :end (cdr reference-for-insertion))))) - (setq reference-for-insertion (cons 'after headline)))) - - (location - (let ((relative-position (org-noter--relative-position-to-view location view))) - (cond - ((eq relative-position 'inside) - (push (cons headline nil) notes-in-view) - - (org-noter--view-region-add current-region-info regions-in-view headline) - - (setq all-after-tipping-point - (and all-after-tipping-point (org-noter--note-after-tipping-point - closest-tipping-point location view)))) - - (t - (when current-region-info - (let ((note-cons-to-change (cond ((eq (aref current-region-info 3) 'regions-in-view) - (car notes-in-view)) - ((eq (aref current-region-info 3) 'closest-notes-regions) - (car closest-notes))))) - (when (< (org-element-property :begin headline) - (org-element-property :end (car note-cons-to-change))) - (setcdr note-cons-to-change headline)))) - - (let ((eligible-for-before (and closest-tipping-point all-after-tipping-point - (eq relative-position 'before)))) - (cond ((and eligible-for-before - (org-noter--compare-locations '> location closest-notes-location)) - (setq closest-notes (list (cons headline nil)) - closest-notes-location location - current-region-info nil - closest-notes-regions nil) - (org-noter--view-region-add current-region-info closest-notes-regions headline)) - - ((and eligible-for-before (equal location closest-notes-location)) - (push (cons headline nil) closest-notes) - (org-noter--view-region-add current-region-info closest-notes-regions headline)) - - (t (org-noter--view-region-finish current-region-info headline))))))) - - (when new-location - (setq preamble nil) - (cond ((and (org-noter--compare-locations '<= location new-location) - (or (eq (car reference-for-insertion) 'before) - (org-noter--compare-locations '>= location reference-location))) - (setq reference-for-insertion (cons 'after headline) - reference-location location)) - - ((and (eq (car reference-for-insertion) 'after) - (< (org-element-property :begin headline) - (org-element-property :end (cdr reference-for-insertion))) - (org-noter--compare-locations '>= location new-location)) - (setq reference-for-insertion (cons 'before headline) - reference-location location))))) - - (t - (when (and preamble new-location - (or (not reference-for-insertion) - (>= (org-element-property :begin headline) - (org-element-property :end (cdr reference-for-insertion))))) - (setq reference-for-insertion (cons 'after headline))))))) - nil nil org-noter--note-search-no-recurse) - - (org-noter--view-region-finish current-region-info) - - (setf (org-noter--session-num-notes-in-view session) (length notes-in-view)) - - (when all-after-tipping-point (setq notes-in-view (append closest-notes notes-in-view))) - - (make-org-noter--view-info - :notes (nreverse notes-in-view) - :regions (nreverse regions-in-view) - :prev-regions (nreverse closest-notes-regions) - :reference-for-insertion reference-for-insertion))))) - -(defun org-noter--make-view-info-for-single-note (session headline) - (let ((not-belonging-element - (org-element-map (org-element-contents headline) 'headline - (lambda (headline) - (let ((doc-file (org-noter--doc-file-property headline))) - (and (or (and doc-file (not (string= doc-file (org-noter--session-property-text session)))) - (org-noter--check-location-property headline)) - headline))) - nil t))) - - (make-org-noter--view-info - ;; NOTE(nox): The cdr is only used when inserting, doesn't matter here - :notes (list (cons headline nil)) - :regions (list (cons (org-element-property :begin headline) - (or (and not-belonging-element (org-element-property :begin not-belonging-element)) - (org-element-property :end headline))))))) - -(defun org-noter--doc-location-change-handler () - (org-noter--with-valid-session - (let ((view-info (org-noter--get-view-info (org-noter--get-current-view)))) - (force-mode-line-update t) - (unless org-noter--inhibit-location-change-handler - (org-noter--get-notes-window (cond ((org-noter--view-info-regions view-info) 'scroll) - ((org-noter--view-info-prev-regions view-info) 'only-prev))) - (org-noter--focus-notes-region view-info))) - - (when (org-noter--session-auto-save-last-location session) (org-noter-set-start-location)))) - -(defun org-noter--mode-line-text () - (org-noter--with-valid-session - (let* ((number-of-notes (or (org-noter--session-num-notes-in-view session) 0))) - (cond ((= number-of-notes 0) (propertize " 0 notes " 'face 'org-noter-no-notes-exist-face)) - ((= number-of-notes 1) (propertize " 1 note " 'face 'org-noter-notes-exist-face)) - (t (propertize (format " %d notes " number-of-notes) 'face 'org-noter-notes-exist-face)))))) - -;; NOTE(nox): From machc/pdf-tools-org -(defun org-noter--pdf-tools-edges-to-region (edges) - "Get 4-entry region (LEFT TOP RIGHT BOTTOM) from several EDGES." - (when edges - (let ((left0 (nth 0 (car edges))) - (top0 (nth 1 (car edges))) - (bottom0 (nth 3 (car edges))) - (top1 (nth 1 (car (last edges)))) - (right1 (nth 2 (car (last edges)))) - (bottom1 (nth 3 (car (last edges))))) - (list left0 - (+ top0 (/ (- bottom0 top0) 3)) - right1 - (- bottom1 (/ (- bottom1 top1) 3)))))) - -(defun org-noter--check-if-document-is-annotated-on-file (document-path notes-path) - ;; NOTE(nox): In order to insert the correct file contents - (let ((buffer (find-buffer-visiting notes-path))) - (when buffer (with-current-buffer buffer (save-buffer))) - - (with-temp-buffer - (insert-file-contents notes-path) - (catch 'break - (while (re-search-forward (org-re-property org-noter-property-doc-file) nil t) - (when (file-equal-p (or (expand-file-name (match-string 3) (file-name-directory notes-path)) - (cadar (org-collect-keywords '(org-noter-property-doc-file)))) - document-path) - ;; NOTE(nox): This notes file has the document we want! - (throw 'break t))))))) - -(defsubst org-noter--check-doc-prop (doc-prop) - (and doc-prop (not (file-directory-p doc-prop)) (file-readable-p doc-prop))) - -(defun org-noter--get-or-read-document-property (inherit-prop &optional force-new) - (let ((doc-prop (and (not force-new) (or (org-entry-get nil org-noter-property-doc-file inherit-prop) - (cadar (org-collect-keywords (list org-noter-property-doc-file))))))) - (unless (org-noter--check-doc-prop doc-prop) - (setq doc-prop nil) - - (when org-noter-suggest-from-attachments - (require 'org-attach) - (let* ((attach-dir (org-attach-dir)) - (attach-list (and attach-dir (org-attach-file-list attach-dir)))) - (when (and attach-list (y-or-n-p "Do you want to annotate an attached file?")) - (setq doc-prop (completing-read "File to annotate: " attach-list nil t)) - (when doc-prop (setq doc-prop (file-relative-name (expand-file-name doc-prop attach-dir))))))) - - (unless (org-noter--check-doc-prop doc-prop) - (setq doc-prop (expand-file-name - (read-file-name - "Invalid or no document property found. Please specify a document path: " nil nil t))) - (when (or (file-directory-p doc-prop) (not (file-readable-p doc-prop))) (user-error "Invalid file path")) - (when (y-or-n-p "Do you want a relative file name? ") (setq doc-prop (file-relative-name doc-prop)))) - - (org-entry-put nil org-noter-property-doc-file doc-prop)) - doc-prop)) - -(defun org-noter--other-frames (&optional this-frame) - "Returns non-`nil' when there is at least another frame" - (setq this-frame (or this-frame (selected-frame))) - (catch 'other-frame - (dolist (frame (visible-frame-list)) - (unless (or (eq this-frame frame) - (frame-parent frame) - (frame-parameter frame 'delete-before)) - (throw 'other-frame frame))))) - -;; -------------------------------------------------------------------------------- -;;; User commands -(defun org-noter-set-start-location (&optional arg) - "When opening a session with this document, go to the current location. -With a prefix ARG, remove start location." - (interactive "P") - (org-noter--with-valid-session - (let ((inhibit-read-only t) - (ast (org-noter--parse-root)) - (location (org-noter--doc-approx-location 'interactive))) - (with-current-buffer (org-noter--session-notes-buffer session) - (org-with-wide-buffer - (goto-char (org-element-property :begin ast)) - (if arg - (org-entry-delete nil org-noter-property-note-location) - (org-entry-put nil org-noter-property-note-location - (org-noter--pretty-print-location location)))))))) - -(defun org-noter-set-auto-save-last-location (arg) - "This toggles saving the last visited location for this document. -With a prefix ARG, delete the current setting and use the default." - (interactive "P") - (org-noter--with-valid-session - (let ((inhibit-read-only t) - (ast (org-noter--parse-root)) - (new-setting (if arg - org-noter-auto-save-last-location - (not (org-noter--session-auto-save-last-location session))))) - (setf (org-noter--session-auto-save-last-location session) - new-setting) - (with-current-buffer (org-noter--session-notes-buffer session) - (org-with-wide-buffer - (goto-char (org-element-property :begin ast)) - (if arg - (org-entry-delete nil org-noter--property-auto-save-last-location) - (org-entry-put nil org-noter--property-auto-save-last-location (format "%s" new-setting))) - (unless new-setting (org-entry-delete nil org-noter-property-note-location))))))) - -(defun org-noter-set-hide-other (arg) - "This toggles hiding other headings for the current session. -- With a prefix \\[universal-argument], set the current setting permanently for this document. -- With a prefix \\[universal-argument] \\[universal-argument], remove the setting and use the default." - (interactive "P") - (org-noter--with-valid-session - (let* ((inhibit-read-only t) - (ast (org-noter--parse-root)) - (persistent - (cond ((equal arg '(4)) 'write) - ((equal arg '(16)) 'remove))) - (new-setting - (cond ((eq persistent 'write) (org-noter--session-hide-other session)) - ((eq persistent 'remove) org-noter-hide-other) - ('other-cases (not (org-noter--session-hide-other session)))))) - (setf (org-noter--session-hide-other session) new-setting) - (when persistent - (with-current-buffer (org-noter--session-notes-buffer session) - (org-with-wide-buffer - (goto-char (org-element-property :begin ast)) - (if (eq persistent 'write) - (org-entry-put nil org-noter--property-hide-other (format "%s" new-setting)) - (org-entry-delete nil org-noter--property-hide-other)))))))) - -(defun org-noter-set-closest-tipping-point (arg) - "This sets the closest note tipping point (see `org-noter-closest-tipping-point') -- With a prefix \\[universal-argument], set it permanently for this document. -- With a prefix \\[universal-argument] \\[universal-argument], remove the setting and use the default." - (interactive "P") - (org-noter--with-valid-session - (let* ((ast (org-noter--parse-root)) - (inhibit-read-only t) - (persistent (cond ((equal arg '(4)) 'write) - ((equal arg '(16)) 'remove))) - (new-setting (if (eq persistent 'remove) - org-noter-closest-tipping-point - (read-number "New tipping point: " (org-noter--session-closest-tipping-point session))))) - (setf (org-noter--session-closest-tipping-point session) new-setting) - (when persistent - (with-current-buffer (org-noter--session-notes-buffer session) - (org-with-wide-buffer - (goto-char (org-element-property :begin ast)) - (if (eq persistent 'write) - (org-entry-put nil org-noter--property-closest-tipping-point (format "%f" new-setting)) - (org-entry-delete nil org-noter--property-closest-tipping-point)))))))) - -(defun org-noter-set-notes-window-behavior (arg) - "Set the notes window behaviour for the current session. -With a prefix ARG, it becomes persistent for that document. - -See `org-noter-notes-window-behavior' for more information." - (interactive "P") - (org-noter--with-valid-session - (let* ((inhibit-read-only t) - (ast (org-noter--parse-root)) - (possible-behaviors (list '("Default" . default) - '("On start" . start) - '("On scroll" . scroll) - '("On scroll to location that only has previous notes" . only-prev) - '("Never" . never))) - chosen-behaviors) - - (while (> (length possible-behaviors) 1) - (let ((chosen-pair (assoc (completing-read "Behavior: " possible-behaviors nil t) possible-behaviors))) - (cond ((eq (cdr chosen-pair) 'default) (setq possible-behaviors nil)) - - ((eq (cdr chosen-pair) 'never) (setq chosen-behaviors (list 'never) - possible-behaviors nil)) - - ((eq (cdr chosen-pair) 'done) (setq possible-behaviors nil)) - - (t (push (cdr chosen-pair) chosen-behaviors) - (setq possible-behaviors (delq chosen-pair possible-behaviors)) - (when (= (length chosen-behaviors) 1) - (setq possible-behaviors (delq (rassq 'default possible-behaviors) possible-behaviors) - possible-behaviors (delq (rassq 'never possible-behaviors) possible-behaviors)) - (push (cons "Done" 'done) possible-behaviors)))))) - - (setf (org-noter--session-window-behavior session) - (or chosen-behaviors org-noter-notes-window-behavior)) - - (when arg - (with-current-buffer (org-noter--session-notes-buffer session) - (org-with-wide-buffer - (goto-char (org-element-property :begin ast)) - (if chosen-behaviors - (org-entry-put nil org-noter--property-behavior (format "%s" chosen-behaviors)) - (org-entry-delete nil org-noter--property-behavior)))))))) - -(defun org-noter-set-notes-window-location (arg) - "Set the notes window default location for the current session. -With a prefix ARG, it becomes persistent for that document. - -See `org-noter-notes-window-behavior' for more information." - (interactive "P") - (org-noter--with-valid-session - (let* ((inhibit-read-only t) - (ast (org-noter--parse-root)) - (location-possibilities - '(("Default" . nil) - ("Horizontal split" . horizontal-split) - ("Vertical split" . vertical-split) - ("Other frame" . other-frame))) - (location - (cdr (assoc (completing-read "Location: " location-possibilities nil t) - location-possibilities))) - (notes-buffer (org-noter--session-notes-buffer session))) - - (setf (org-noter--session-window-location session) - (or location org-noter-notes-window-location)) - - (let (exists) - (dolist (window (get-buffer-window-list notes-buffer nil t)) - (setq exists t) - (with-selected-frame (window-frame window) - (if (= (count-windows) 1) - (delete-frame) - (delete-window window)))) - (when exists (org-noter--get-notes-window 'force))) - - (when arg - (with-current-buffer notes-buffer - (org-with-wide-buffer - (goto-char (org-element-property :begin ast)) - (if location - (org-entry-put nil org-noter--property-location - (format "%s" location)) - (org-entry-delete nil org-noter--property-location)))))))) - -(defun org-noter-set-doc-split-fraction (arg) - "Set the fraction of the frame that the document window will occupy when split. -- With a prefix \\[universal-argument], set it permanently for this document. -- With a prefix \\[universal-argument] \\[universal-argument], remove the setting and use the default." - (interactive "P") - (org-noter--with-valid-session - (let* ((ast (org-noter--parse-root)) - (inhibit-read-only t) - (persistent (cond ((equal arg '(4)) 'write) - ((equal arg '(16)) 'remove))) - (current-setting (org-noter--session-doc-split-fraction session)) - (new-setting - (if (eq persistent 'remove) - org-noter-doc-split-fraction - (cons (read-number "Horizontal fraction: " (car current-setting)) - (read-number "Vertical fraction: " (cdr current-setting)))))) - (setf (org-noter--session-doc-split-fraction session) new-setting) - (when (org-noter--get-notes-window) - (with-current-buffer (org-noter--session-doc-buffer session) - (delete-other-windows) - (org-noter--get-notes-window 'force))) - - (when persistent - (with-current-buffer (org-noter--session-notes-buffer session) - (org-with-wide-buffer - (goto-char (org-element-property :begin ast)) - (if (eq persistent 'write) - (org-entry-put nil org-noter--property-doc-split-fraction (format "%s" new-setting)) - (org-entry-delete nil org-noter--property-doc-split-fraction)))))))) - -(defun org-noter-kill-session (&optional session) - "Kill an `org-noter' session. - -When called interactively, if there is no prefix argument and the -buffer has an annotation session, it will kill it; else, it will -show a list of open `org-noter' sessions, asking for which to -kill. - -When called from elisp code, you have to pass in the SESSION you -want to kill." - (interactive "P") - (when (and (called-interactively-p 'any) (> (length org-noter--sessions) 0)) - ;; NOTE(nox): `session' is representing a prefix argument - (if (and org-noter--session (not session)) - (setq session org-noter--session) - (setq session nil) - (let (collection default doc-display-name notes-file-name display) - (dolist (session org-noter--sessions) - (setq doc-display-name (org-noter--session-display-name session) - notes-file-name (file-name-nondirectory - (org-noter--session-notes-file-path session)) - display (concat doc-display-name " - " notes-file-name)) - (when (eq session org-noter--session) (setq default display)) - (push (cons display session) collection)) - (setq session (cdr (assoc (completing-read "Which session? " collection nil t - nil nil default) - collection)))))) - - (when (and session (memq session org-noter--sessions)) - (setq org-noter--sessions (delq session org-noter--sessions)) - - (when (eq (length org-noter--sessions) 0) - (remove-hook 'delete-frame-functions 'org-noter--handle-delete-frame) - (advice-remove 'doc-view-goto-page 'org-noter--location-change-advice) - (advice-remove 'nov-render-document 'org-noter--nov-scroll-handler)) - - (let* ((ast (org-noter--parse-root session)) - (frame (org-noter--session-frame session)) - (notes-buffer (org-noter--session-notes-buffer session)) - (base-buffer (buffer-base-buffer notes-buffer)) - (notes-modified (buffer-modified-p base-buffer)) - (doc-buffer (org-noter--session-doc-buffer session))) - - (dolist (window (get-buffer-window-list notes-buffer nil t)) - (with-selected-frame (window-frame window) - (if (= (count-windows) 1) - (when (org-noter--other-frames) (delete-frame)) - (delete-window window)))) - - (with-current-buffer notes-buffer - (remove-hook 'kill-buffer-hook 'org-noter--handle-kill-buffer t) - (restore-buffer-modified-p nil)) - (kill-buffer notes-buffer) - - (with-current-buffer base-buffer - (org-noter--unset-text-properties ast) - (set-buffer-modified-p notes-modified)) - - (with-current-buffer doc-buffer - (remove-hook 'kill-buffer-hook 'org-noter--handle-kill-buffer t)) - (kill-buffer doc-buffer) - - (when (frame-live-p frame) - (if (and (org-noter--other-frames) org-noter-kill-frame-at-session-end) - (delete-frame frame) - (progn - (delete-other-windows) - (set-frame-parameter nil 'name nil))))))) - -(defun org-noter-create-skeleton () - "Create notes skeleton based on the outline of the document." - (interactive) - (org-noter--with-valid-session - (pcase (org-noter--session-doc-mode session) - ('pdf-view-mode (org-noter-create-skeleton-pdf)) - ('djvu-read-mode (org-noter-create-skeleton-djvu)) - ('nov-mode (org-noter-create-skeleton-epub)) - (_ (user-error "This command is not supported for %s" (org-noter--session-doc-mode session)))))) - -(defun org-noter-create-skeleton-pdf () - "Create notes skeleton with the PDF outline or annotations." - (org-noter--with-valid-session - (let* ((ast (org-noter--parse-root)) - (top-level (org-element-property :level ast)) - (options '(("Outline" . (outline)) - ("Annotations" . (annots)) - ("Both" . (outline annots)))) - answer output-data) - (with-current-buffer (org-noter--session-doc-buffer session) - (setq answer (assoc (completing-read "What do you want to import? " options nil t) options)) - - (when (memq 'outline answer) - (dolist (item (pdf-info-outline)) - (let ((type (alist-get 'type item)) - (page (alist-get 'page item)) - (depth (alist-get 'depth item)) - (title (alist-get 'title item)) - (top (alist-get 'top item))) - (when (and (eq type 'goto-dest) (> page 0)) - (push (vector title (cons page top) (1+ depth) nil) output-data))))) - - (when (memq 'annots answer) - (let ((possible-annots (list '("Highlights" . highlight) - '("Underlines" . underline) - '("Squigglies" . squiggly) - '("Text notes" . text) - '("Strikeouts" . strike-out) - '("Links" . link) - '("ALL" . all))) - chosen-annots insert-contents pages-with-links) - (while (> (length possible-annots) 1) - (let* ((chosen-string (completing-read "Which types of annotations do you want? " - possible-annots nil t)) - (chosen-pair (assoc chosen-string possible-annots))) - (cond ((eq (cdr chosen-pair) 'all) - (dolist (annot possible-annots) - (when (and (cdr annot) (not (eq (cdr annot) 'all))) - (push (cdr annot) chosen-annots))) - (setq possible-annots nil)) - ((cdr chosen-pair) - (push (cdr chosen-pair) chosen-annots) - (setq possible-annots (delq chosen-pair possible-annots)) - (when (= 1 (length chosen-annots)) (push '("DONE") possible-annots))) - (t - (setq possible-annots nil))))) - - (setq insert-contents (y-or-n-p "Should we insert the annotations contents? ")) - - (dolist (item (pdf-info-getannots)) - (let* ((type (alist-get 'type item)) - (page (alist-get 'page item)) - (edges (or (org-noter--pdf-tools-edges-to-region (alist-get 'markup-edges item)) - (alist-get 'edges item))) - (top (nth 1 edges)) - (item-subject (alist-get 'subject item)) - (item-contents (alist-get 'contents item)) - name contents) - (when (and (memq type chosen-annots) (> page 0)) - (if (eq type 'link) - (cl-pushnew page pages-with-links) - (setq name (cond ((eq type 'highlight) "Highlight") - ((eq type 'underline) "Underline") - ((eq type 'squiggly) "Squiggly") - ((eq type 'text) "Text note") - ((eq type 'strike-out) "Strikeout"))) - - (when insert-contents - (setq contents (cons (pdf-info-gettext page edges) - (and (or (and item-subject (> (length item-subject) 0)) - (and item-contents (> (length item-contents) 0))) - (concat (or item-subject "") - (if (and item-subject item-contents) "\n" "") - (or item-contents "")))))) - - (push (vector (format "%s on page %d" name page) (cons page top) 'inside contents) - output-data))))) - - (dolist (page pages-with-links) - (let ((links (pdf-info-pagelinks page)) - type) - (dolist (link links) - (setq type (alist-get 'type link)) - (unless (eq type 'goto-dest) ;; NOTE(nox): Ignore internal links - (let* ((edges (alist-get 'edges link)) - (title (alist-get 'title link)) - (top (nth 1 edges)) - (target-page (alist-get 'page link)) - target heading-text) - - (unless (and title (> (length title) 0)) (setq title (pdf-info-gettext page edges))) - - (cond - ((eq type 'uri) - (setq target (alist-get 'uri link) - heading-text (format "Link on page %d: [[%s][%s]]" page target title))) - - ((eq type 'goto-remote) - (setq target (concat "file:" (alist-get 'filename link)) - heading-text (format "Link to document on page %d: [[%s][%s]]" page target title)) - (when target-page - (setq heading-text (concat heading-text (format " (target page: %d)" target-page))))) - - (t (error "Unexpected link type"))) - - (push (vector heading-text (cons page top) 'inside nil) output-data)))))))) - - - (when output-data - (if (memq 'annots answer) - (setq output-data - (sort output-data - (lambda (e1 e2) - (or (not (aref e1 1)) - (and (aref e2 1) - (org-noter--compare-locations '< (aref e1 1) (aref e2 1))))))) - (setq output-data (nreverse output-data))) - - (push (vector "Skeleton" nil 1 nil) output-data))) - - (with-current-buffer (org-noter--session-notes-buffer session) - ;; NOTE(nox): org-with-wide-buffer can't be used because we want to reset the - ;; narrow region to include the new headings - (widen) - (save-excursion - (goto-char (org-element-property :end ast)) - - (let (last-absolute-level - title location relative-level contents - level) - (dolist (data output-data) - (setq title (aref data 0) - location (aref data 1) - relative-level (aref data 2) - contents (aref data 3)) - - (if (symbolp relative-level) - (setq level (1+ last-absolute-level)) - (setq last-absolute-level (+ top-level relative-level) - level last-absolute-level)) - - (org-noter--insert-heading level title) - - (when location - (org-entry-put nil org-noter-property-note-location (org-noter--pretty-print-location location))) - - (when org-noter-doc-property-in-notes - (org-entry-put nil org-noter-property-doc-file (org-noter--session-property-text session)) - (org-entry-put nil org-noter--property-auto-save-last-location "nil")) - - (when (car contents) - (org-noter--insert-heading (1+ level) "Contents") - (insert (car contents))) - (when (cdr contents) - (org-noter--insert-heading (1+ level) "Comment") - (insert (cdr contents))))) - - (setq ast (org-noter--parse-root)) - (org-noter--narrow-to-root ast) - (goto-char (org-element-property :begin ast)) - (outline-hide-subtree) - (org-show-children 2)))))) - -(defun org-noter-create-skeleton-djvu () - (org-noter--with-valid-session - (let* ((ast (org-noter--parse-root)) - (top-level (org-element-property :level ast)) - output-data) - (require 'thingatpt) - (with-current-buffer (djvu-ref outline-buf) - (unless (string= (buffer-string) "") - (push (vector "Skeleton" nil 1) output-data) - (save-excursion - (goto-char (point-min)) - (while (not (looking-at "^$")) - (push (vector (string-trim-right (string-trim (thing-at-point 'line t)) " [[:digit:]]+") - (list (string-trim-left (string-trim (thing-at-point 'line t)) ".* ")) - (+ 2 (how-many " " (point-at-bol) (point-at-eol)))) output-data) - (next-line) - (move-beginning-of-line 1))))) - - (with-current-buffer (org-noter--session-notes-buffer session) - ;; NOTE(nox): org-with-wide-buffer can't be used because we want to reset the - ;; narrow region to include the new headings - (widen) - (save-excursion - (goto-char (org-element-property :end ast)) - - (let (last-absolute-level - title location relative-level contents - level) - - (dolist (data (nreverse output-data)) - (setq title (aref data 0) - location (aref data 1) - relative-level (aref data 2)) - - (setq last-absolute-level (+ top-level relative-level) - level last-absolute-level) - - (org-noter--insert-heading level title) - - (when location - (org-entry-put nil org-noter-property-note-location (org-noter--pretty-print-location location))) - - (when org-noter-doc-property-in-notes - (org-entry-put nil org-noter-property-doc-file (org-noter--session-property-text session)) - (org-entry-put nil org-noter--property-auto-save-last-location "nil")))) - - (setq ast (org-noter--parse-root)) - (org-noter--narrow-to-root ast) - (goto-char (org-element-property :begin ast)) - (outline-hide-subtree) - (org-show-children 2)))))) - -;; Shamelessly stolen code from Yuchen Li. -;; This code is originally from org-noter-plus package. -;; At https://github.com/yuchen-lea/org-noter-plus - -(defun org-noter--handle-nov-toc-item (ol depth) - (mapcar (lambda (li) - (mapcar (lambda (a-or-ol) - (pcase-exhaustive (dom-tag a-or-ol) - ('a - (vector :depth depth - :title (dom-text a-or-ol) - :href (esxml-node-attribute 'href a-or-ol))) - ('ol - (org-noter--handle-nov-toc-item a-or-ol - (1+ depth))))) - (dom-children li))) - (dom-children ol))) - -(defun org-noter-create-skeleton-epub () - "Epub outline with nov link." - (require 'esxml) - (require 'nov) - (require 'dom) - (org-noter--with-valid-session - (let* ((ast (org-noter--parse-root)) - (top-level (org-element-property :level ast)) - output-data) - (with-current-buffer (org-noter--session-doc-buffer session) - (let* ((toc-path (cdr (aref nov-documents 0))) - (toc-tree (with-temp-buffer - (insert (nov-ncx-to-html toc-path)) - (replace-regexp "\n" - "" - nil - (point-min) - (point-max)) - (libxml-parse-html-region (point-min) - (point-max)))) - (origin-index nov-documents-index) - (origin-point (point))) - (dolist (item - (nreverse (flatten-tree (org-noter--handle-nov-toc-item toc-tree 1)))) - (let ((relative-level (aref item 1)) - (title (aref item 3)) - (url (aref item 5))) - (apply 'nov-visit-relative-file - (nov-url-filename-and-target url)) - (when (not (integerp nov-documents-index)) - (setq nov-documents-index 0)) - (push (vector title (list nov-documents-index (point)) relative-level) output-data))) - (push (vector "Skeleton" (list 0) 1) output-data) - - (nov-goto-document origin-index) - (goto-char origin-point))) - (save-excursion - (goto-char (org-element-property :end ast)) - (with-current-buffer (org-noter--session-notes-buffer session) - (dolist (data output-data) - (setq title (aref data 0) - location (aref data 1) - relative-level (aref data 2)) - - (setq last-absolute-level (+ top-level relative-level) - level last-absolute-level) - - (org-noter--insert-heading level title) - - (when location - (org-entry-put nil org-noter-property-note-location (org-noter--pretty-print-location location))) - - (when org-noter-doc-property-in-notes - (org-entry-put nil org-noter-property-doc-file (org-noter--session-property-text session)) - (org-entry-put nil org-noter--property-auto-save-last-location "nil"))) - (setq ast (org-noter--parse-root)) - (org-noter--narrow-to-root ast) - (goto-char (org-element-property :begin ast)) - (outline-hide-subtree) - (org-show-children 2)))))) - - - -(defun org-noter-insert-note (&optional precise-info note-title) - "Insert note associated with the current location. - -This command will prompt for a title of the note and then insert -it in the notes buffer. When the input is empty, a title based on -`org-noter-default-heading-title' will be generated. - -If there are other notes related to the current location, the -prompt will also suggest them. Depending on the value of the -variable `org-noter-closest-tipping-point', it may also -suggest the closest previous note. - -PRECISE-INFO makes the new note associated with a more -specific location (see `org-noter-insert-precise-note' for more -info). - -When you insert into an existing note and have text selected on -the document buffer, the variable `org-noter-insert-selected-text-inside-note' -defines if the text should be inserted inside the note." - (interactive) - (org-noter--with-valid-session - (let* ((ast (org-noter--parse-root)) (contents (org-element-contents ast)) - (window (org-noter--get-notes-window 'force)) - (selected-text - (pcase (org-noter--session-doc-mode session) - ('pdf-view-mode - (when (pdf-view-active-region-p) - (mapconcat 'identity (pdf-view-active-region-text) ? ))) - - ((or 'nov-mode 'djvu-read-mode) - (when (region-active-p) - (buffer-substring-no-properties (mark) (point)))))) - - force-new - (location (org-noter--doc-approx-location (or precise-info 'interactive) (gv-ref force-new))) - (view-info (org-noter--get-view-info (org-noter--get-current-view) location))) - - (let ((inhibit-quit t)) - (with-local-quit - (select-frame-set-input-focus (window-frame window)) - (select-window window) - - ;; IMPORTANT(nox): Need to be careful changing the next part, it is a bit - ;; complicated to get it right... - - (let ((point (point)) - (minibuffer-local-completion-map org-noter--completing-read-keymap) - collection default default-begin title selection - (empty-lines-number (if org-noter-separate-notes-from-heading 2 1))) - - (cond - ;; NOTE(nox): Both precise and without questions will create new notes - ((or precise-info force-new) - (setq default (and selected-text (replace-regexp-in-string "\n" " " selected-text)))) - (org-noter-insert-note-no-questions) - (t - (dolist (note-cons (org-noter--view-info-notes view-info)) - (let ((display (org-element-property :raw-value (car note-cons))) - (begin (org-element-property :begin (car note-cons)))) - (push (cons display note-cons) collection) - (when (and (>= point begin) (> begin (or default-begin 0))) - (setq default display - default-begin begin)))))) - - (setq collection (nreverse collection) - title (if (or org-noter-insert-note-no-questions note-title) - (or default note-title) - (completing-read "Note: " collection nil nil nil nil default)) - selection (unless org-noter-insert-note-no-questions (cdr (assoc title collection)))) - - (if selection - ;; NOTE(nox): Inserting on an existing note - (let* ((note (car selection)) - (insert-before-element (cdr selection)) - (has-content - (eq (org-element-map (org-element-contents note) org-element-all-elements - (lambda (element) - (if (org-noter--check-location-property element) - 'stop - (not (memq (org-element-type element) '(section property-drawer))))) - nil t) - t))) - (when has-content (setq empty-lines-number 2)) - (if insert-before-element - (goto-char (org-element-property :begin insert-before-element)) - (goto-char (org-element-property :end note))) - - - (if (org-at-heading-p) - (progn - (org-N-empty-lines-before-current empty-lines-number) - (forward-line -1)) - (unless (bolp) (insert "\n")) - (org-N-empty-lines-before-current (1- empty-lines-number))) - - (when (and org-noter-insert-selected-text-inside-note selected-text) (insert selected-text))) - - ;; NOTE(nox): Inserting a new note - (let ((reference-element-cons (org-noter--view-info-reference-for-insertion view-info)) - level) - (when (zerop (length title)) - (setq title (replace-regexp-in-string (regexp-quote "$p$") (number-to-string (org-noter--get-location-page location)) - org-noter-default-heading-title))) - - (if reference-element-cons - (progn - (cond - ((eq (car reference-element-cons) 'before) - (goto-char (org-element-property :begin (cdr reference-element-cons)))) - ((eq (car reference-element-cons) 'after) - (goto-char (org-element-property :end (cdr reference-element-cons))))) - - ;; NOTE(nox): This is here to make the automatic "should insert blank" work better. - (when (org-at-heading-p) (backward-char)) - - (setq level (org-element-property :level (cdr reference-element-cons)))) - - (goto-char (org-element-map contents 'section - (lambda (section) (org-element-property :end section)) - nil t org-element-all-elements)) - (setq level (1+ (org-element-property :level ast)))) - - ;; NOTE(nox): This is needed to insert in the right place - (outline-show-entry) - (org-noter--insert-heading level title empty-lines-number location) - (when (org-noter--session-hide-other session) (org-overview)) - - (setf (org-noter--session-num-notes-in-view session) - (1+ (org-noter--session-num-notes-in-view session))))) - - (org-show-set-visibility t) - (org-cycle-hide-drawers 'all) - (org-cycle-show-empty-lines t))) - (when quit-flag - ;; NOTE(nox): If this runs, it means the user quitted while creating a note, so - ;; revert to the previous window. - (select-frame-set-input-focus (org-noter--session-frame session)) - (select-window (get-buffer-window (org-noter--session-doc-buffer session)))))))) - -(defun org-noter-insert-precise-note (&optional toggle-no-questions) - "Insert note associated with a specific location. -This will ask you to click where you want to scroll to when you -sync the document to this note. You should click on the top of -that part. Will always create a new note. - -When text is selected, it will automatically choose the top of -the selected text as the location and the text itself as the -title of the note (you may change it anyway!). - -See `org-noter-insert-note' docstring for more." - (interactive "P") - (org-noter--with-valid-session - (let ((org-noter-insert-note-no-questions (if toggle-no-questions - (not org-noter-insert-note-no-questions) - org-noter-insert-note-no-questions))) - (org-noter-insert-note (org-noter--get-precise-info))))) - - -(defun org-noter-insert-note-toggle-no-questions () - "Insert note associated with the current location. -This is like `org-noter-insert-note', except it will toggle `org-noter-insert-note-no-questions'" - (interactive) - (org-noter--with-valid-session - (let ((org-noter-insert-note-no-questions (not org-noter-insert-note-no-questions))) - (org-noter-insert-note)))) - -(defmacro org-noter--map-ignore-headings-with-doc-file (contents match-first &rest body) - `(let (ignore-until-level) - (org-element-map ,contents 'headline - (lambda (headline) - (let ((doc-file (org-noter--doc-file-property headline)) - (location (org-noter--parse-location-property headline))) - (when (and ignore-until-level (<= (org-element-property :level headline) ignore-until-level)) - (setq ignore-until-level nil)) - - (cond - (ignore-until-level nil) ;; NOTE(nox): This heading is ignored, do nothing - ((and doc-file (not (string= doc-file (org-noter--session-property-text session)))) - (setq ignore-until-level (org-element-property :level headline)) nil) - (t ,@body)))) - nil ,match-first org-noter--note-search-no-recurse))) - -(defun org-noter-sync-prev-page-or-chapter () - "Show previous page or chapter that has notes, in relation to the current page or chapter. -This will force the notes window to popup." - (interactive) - (org-noter--with-valid-session - (let ((this-location (org-noter--doc-approx-location 0)) - (contents (org-element-contents (org-noter--parse-root))) - target-location) - (org-noter--get-notes-window 'force) - - (org-noter--map-ignore-headings-with-doc-file - contents nil - (when (and (org-noter--compare-locations '< location this-location) - (org-noter--compare-locations '>f location target-location)) - (setq target-location location))) - - (org-noter--get-notes-window 'force) - (select-window (org-noter--get-doc-window)) - (if target-location - (org-noter--doc-goto-location target-location) - (user-error "There are no more previous pages or chapters with notes"))))) - -(defun org-noter-sync-current-page-or-chapter () - "Show current page or chapter notes. -This will force the notes window to popup." - (interactive) - (org-noter--with-valid-session - (let ((window (org-noter--get-notes-window 'force))) - (select-frame-set-input-focus (window-frame window)) - (select-window window) - (org-noter--doc-location-change-handler)))) - -(defun org-noter-sync-next-page-or-chapter () - "Show next page or chapter that has notes, in relation to the current page or chapter. -This will force the notes window to popup." - (interactive) - (org-noter--with-valid-session - (let ((this-location (org-noter--doc-approx-location most-positive-fixnum)) - (contents (org-element-contents (org-noter--parse-root))) - target-location) - - (org-noter--map-ignore-headings-with-doc-file - contents nil - (when (and (org-noter--compare-locations '> location this-location) - (org-noter--compare-locations '< location target-location)) - (setq target-location location))) - - (org-noter--get-notes-window 'force) - (select-window (org-noter--get-doc-window)) - (if target-location - (org-noter--doc-goto-location target-location) - (user-error "There are no more following pages or chapters with notes"))))) - -(defun org-noter-sync-prev-note () - "Go to the location of the previous note, in relation to where the point is. -As such, it will only work when the notes window exists." - (interactive) - (org-noter--with-selected-notes-window - "No notes window exists" - (let ((org-noter--inhibit-location-change-handler t) - (contents (org-element-contents (org-noter--parse-root))) - (current-begin (org-element-property :begin (org-noter--get-containing-heading))) - previous) - (when current-begin - (org-noter--map-ignore-headings-with-doc-file - contents t - (when location - (if (= current-begin (org-element-property :begin headline)) - t - (setq previous headline) - nil)))) - - (if previous - (progn - ;; NOTE(nox): This needs to be manual so we can focus the correct note - (org-noter--doc-goto-location (org-noter--parse-location-property previous)) - (org-noter--focus-notes-region (org-noter--make-view-info-for-single-note session previous))) - (user-error "There is no previous note")))) - (select-window (org-noter--get-doc-window))) - -(defun org-noter-sync-current-note () - "Go the location of the selected note, in relation to where the point is. -As such, it will only work when the notes window exists." - (interactive) - (org-noter--with-selected-notes-window - "No notes window exists" - (if (string= (or (org-entry-get nil org-noter-property-doc-file t) - (cadar (org-collect-keywords (list org-noter-property-doc-file)))) - (org-noter--session-property-text session)) - (let ((location (org-noter--parse-location-property (org-noter--get-containing-heading)))) - (if location - (org-noter--doc-goto-location location) - (user-error "No note selected"))) - (user-error "You are inside a different document"))) - (let ((window (org-noter--get-doc-window))) - (select-frame-set-input-focus (window-frame window)) - (select-window window))) - -(defun org-noter-sync-next-note () - "Go to the location of the next note, in relation to where the point is. -As such, it will only work when the notes window exists." - (interactive) - (org-noter--with-selected-notes-window - "No notes window exists" - (let ((org-noter--inhibit-location-change-handler t) - (contents (org-element-contents (org-noter--parse-root))) - next) - - (org-noter--map-ignore-headings-with-doc-file - contents t - (when (and location (< (point) (org-element-property :begin headline))) - (setq next headline))) - - (if next - (progn - (org-noter--doc-goto-location (org-noter--parse-location-property next)) - (org-noter--focus-notes-region (org-noter--make-view-info-for-single-note session next))) - (user-error "There is no next note")))) - (select-window (org-noter--get-doc-window))) - -(define-minor-mode org-noter-doc-mode - "Minor mode for the document buffer. -Keymap: -\\{org-noter-doc-mode-map}" - :keymap `((,(kbd "i") . org-noter-insert-note) - (,(kbd "C-i") . org-noter-insert-note-toggle-no-questions) - (,(kbd "M-i") . org-noter-insert-precise-note) - (,(kbd "q") . org-noter-kill-session) - (,(kbd "M-p") . org-noter-sync-prev-page-or-chapter) - (,(kbd "M-.") . org-noter-sync-current-page-or-chapter) - (,(kbd "M-n") . org-noter-sync-next-page-or-chapter) - (,(kbd "C-M-p") . org-noter-sync-prev-note) - (,(kbd "C-M-.") . org-noter-sync-current-note) - (,(kbd "C-M-n") . org-noter-sync-next-note)) - - (let ((mode-line-segment '(:eval (org-noter--mode-line-text)))) - (if org-noter-doc-mode - (if (symbolp (car-safe mode-line-format)) - (setq mode-line-format (list mode-line-segment mode-line-format)) - (push mode-line-segment mode-line-format)) - (setq mode-line-format (delete mode-line-segment mode-line-format))))) - -(define-minor-mode org-noter-notes-mode - "Minor mode for the notes buffer. -Keymap: -\\{org-noter-notes-mode-map}" - :keymap `((,(kbd "M-p") . org-noter-sync-prev-page-or-chapter) - (,(kbd "M-.") . org-noter-sync-current-page-or-chapter) - (,(kbd "M-n") . org-noter-sync-next-page-or-chapter) - (,(kbd "C-M-p") . org-noter-sync-prev-note) - (,(kbd "C-M-.") . org-noter-sync-current-note) - (,(kbd "C-M-n") . org-noter-sync-next-note))) +(require 'org-noter-core) ;;;###autoload (defun org-noter (&optional arg) @@ -2506,8 +139,8 @@ notes file, even if it finds one." (document-used-path (expand-file-name document-name document-directory)) (search-names (append org-noter-default-notes-file-names (list (concat document-base ".org")))) - notes-files-annotating ; List of files annotating document - notes-files ; List of found notes files (annotating or not) + notes-files-annotating ; List of files annotating document + notes-files ; List of found notes files (annotating or not) (document-location (org-noter--doc-approx-location))) From 5a952628e2c78e3eee9bf7462a55f97587758ab0 Mon Sep 17 00:00:00 2001 From: c1-g Date: Thu, 13 Jan 2022 20:03:33 +0700 Subject: [PATCH 060/453] Simplify org-noter command. --- org-noter.el | 74 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 30 deletions(-) diff --git a/org-noter.el b/org-noter.el index c41e083..2e7473d 100644 --- a/org-noter.el +++ b/org-noter.el @@ -35,8 +35,14 @@ ;; https://github.com/rudolfochrist/interleave ;;; Code: +(require 'org-element) +(require 'cl-lib) + (require 'org-noter-core) +(declare-function org-entry-put "org") +(declare-function org-with-wide-buffer "org-macs") + ;;;###autoload (defun org-noter (&optional arg) "Start `org-noter' session. @@ -82,38 +88,46 @@ notes file, even if it finds one." (cond ;; NOTE(nox): Creating the session from notes file ((eq major-mode 'org-mode) - (let* ((notes-file-path (buffer-file-name)) - (document-property (org-noter--get-or-read-document-property (not (equal arg '(4))) - (equal arg '(16)))) + (document-property (org-noter--get-or-read-document-property + (not (equal arg '(4))) + (equal arg '(16)))) (org-noter-always-create-frame - (if (and (numberp arg) (= arg 0)) (not org-noter-always-create-frame) org-noter-always-create-frame)) - (ast (org-noter--parse-root (vector (current-buffer) document-property)))) - - (when (catch 'should-continue - (when (or (numberp arg) (eq arg '-)) - (cond ((> (prefix-numeric-value arg) 0) - (find-file document-property) - (throw 'should-continue nil)) - ((< (prefix-numeric-value arg) 0) - (find-file (file-name-directory document-property)) - (throw 'should-continue nil)))) - - ;; NOTE(nox): Check if it is an existing session - (let ((id (get-text-property (org-element-property :begin ast) org-noter--id-text-property)) - session) - (when id - (setq session (cl-loop for test-session in org-noter--sessions - when (= (org-noter--session-id test-session) id) - return test-session)) - (when session - (let* ((org-noter--session session) - (location (org-noter--parse-location-property (org-noter--get-containing-heading)))) - (org-noter--setup-windows session) - (when location (org-noter--doc-goto-location location)) - (select-frame-set-input-focus (org-noter--session-frame session))) - (throw 'should-continue nil)))) - t) + (if (and (numberp arg) (= arg 0)) + (not org-noter-always-create-frame) + org-noter-always-create-frame)) + (ast (org-noter--parse-root (vector (current-buffer) document-property))) + (session-id (get-text-property (org-element-property :begin ast) org-noter--id-text-property)) + session) + + ;; Check for prefix value + (if (or (numberp arg) (eq arg '-)) + ;; Yes, user's given a prefix value. + (cond ((> (prefix-numeric-value arg) 0) + ;; Is the prefix value greater than 0? + (find-file document-property)) + ;; Open the document like `find-file'. + + ;; Is the prefix value less than 0? + ((< (prefix-numeric-value arg) 0) + ;; Open the folder containing the document. + (find-file (file-name-directory document-property)))) + + ;; No, user didn't give a prefix value + ;; NOTE(nox): Check if it is an existing session + (when session-id + (setq session (cl-loop for session in org-noter--sessions + when (= (org-noter--session-id session) session-id) + return session)))) + + (if session + (let* ((org-noter--session session) + (location (org-noter--parse-location-property + (org-noter--get-containing-heading)))) + (org-noter--setup-windows session) + (when location (org-noter--doc-goto-location location)) + (select-frame-set-input-focus (org-noter--session-frame session))) + ;; It's not an existing session, create a new session. (org-noter--create-session ast document-property notes-file-path)))) ;; NOTE(nox): Creating the session from the annotated document From a7ff66cc1d1f3682894bdc6e84903481acab2855 Mon Sep 17 00:00:00 2001 From: c1-g Date: Thu, 13 Jan 2022 20:09:45 +0700 Subject: [PATCH 061/453] (feat) Org-noter finds additional notes path with custom functions Via org-noter-find-additional-notes-functions, user can have org noter find additional notes for the document buffer. --- org-noter-core.el | 16 ++++++++++++++++ org-noter.el | 4 +++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/org-noter-core.el b/org-noter-core.el index 0477c62..64bbc1a 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -231,6 +231,22 @@ The title used will be the default one." :group 'org-noter :type 'hook) +(defcustom org-noter-find-additional-notes-functions nil + "Functions that when given a document file path as argument, give out +an org note file path. + +The functions in this list must accept 1 argument, a file name. +The argument will be given by `org-noter'. + +The return value must be a path to an org file. No matter if it's +an absolute or relative path, the file name will be expanded to +each directory set in `org-noter-notes-search-path' to test if it exists. + +If it exists, it will be listed as a candidate that `org-noter' will have +the user select to use as the note file of the document." + :group 'org-noter + :type 'hook) + (defface org-noter-no-notes-exist-face '((t :foreground "chocolate" diff --git a/org-noter.el b/org-noter.el index 2e7473d..9d9a0b4 100644 --- a/org-noter.el +++ b/org-noter.el @@ -152,7 +152,9 @@ notes file, even if it finds one." ;; be the same as `buffer-file-name', but is needed for the truename workaround (document-used-path (expand-file-name document-name document-directory)) - (search-names (append org-noter-default-notes-file-names (list (concat document-base ".org")))) + (search-names (append org-noter-default-notes-file-names + (list (concat document-base ".org")) + (list (run-hook-with-args-until-success 'org-noter-find-additional-notes-functions document-path)))) notes-files-annotating ; List of files annotating document notes-files ; List of found notes files (annotating or not) From 433901fab37931fb69543d260cb4ad59b6cdf78c Mon Sep 17 00:00:00 2001 From: c1-g Date: Thu, 13 Jan 2022 20:11:57 +0700 Subject: [PATCH 062/453] (minor) Don't use the deprecated cons cell form of anonymous face. --- org-noter.el | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/org-noter.el b/org-noter.el index 9d9a0b4..6aa2b1b 100644 --- a/org-noter.el +++ b/org-noter.el @@ -198,8 +198,7 @@ notes file, even if it finds one." (when (file-exists-p file-name) (setq file-name (propertize file-name 'display (concat file-name - (propertize " -- Exists!" - 'face '(foreground-color . "green"))))) + (propertize " -- Exists!" 'face '(:foregorund "green"))))) (push file-name list-of-possible-targets) (throw 'break nil)) @@ -219,8 +218,7 @@ notes file, even if it finds one." (when (file-exists-p file-name) (setq file-name (propertize file-name 'display (concat file-name - (propertize " -- Exists!" - 'face '(foreground-color . "green")))))) + (propertize " -- Exists!" 'face '(:foreground "green")))))) (push file-name list-of-possible-targets))))) (setq target (completing-read "Where do you want to save it? " list-of-possible-targets From 6e5827be59eca80f09bda8506345de837191d980 Mon Sep 17 00:00:00 2001 From: c1-g Date: Thu, 13 Jan 2022 20:16:38 +0700 Subject: [PATCH 063/453] (minor) Simplify org-noter. --- org-noter.el | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/org-noter.el b/org-noter.el index 6aa2b1b..90b9d1e 100644 --- a/org-noter.el +++ b/org-noter.el @@ -244,21 +244,20 @@ notes file, even if it finds one." (file-name-directory (car notes-files))))) (setq notes-files-annotating notes-files))) - (when (> (length (cl-delete-duplicates notes-files-annotating :test 'equal)) 1) + (when (> (length (delete-dups notes-files-annotating)) 1) (setq notes-files-annotating (list (completing-read "Which notes file should we open? " notes-files-annotating nil t)))) (with-current-buffer (find-file-noselect (car notes-files-annotating)) - (org-with-wide-buffer - (catch 'break - (goto-char (point-min)) - (while (re-search-forward (org-re-property org-noter-property-doc-file) nil t) - (when (file-equal-p (expand-file-name (match-string 3) - (file-name-directory (car notes-files-annotating))) - document-path) - (let ((org-noter--start-location-override document-location)) - (org-noter)) - (throw 'break t))))))))))) + (org-with-point-at (point-min) + (catch 'break + (while (re-search-forward (org-re-property org-noter-property-doc-file) nil t) + (when (file-equal-p (expand-file-name (match-string 3) + (file-name-directory (car notes-files-annotating))) + document-path) + (let ((org-noter--start-location-override document-location)) + (org-noter arg)) + (throw 'break t))))))))))) (provide 'org-noter) From 91c2b01f2f503c0720866ac57123309857e64bf3 Mon Sep 17 00:00:00 2001 From: c1-g Date: Sun, 26 Dec 2021 13:38:11 +0700 Subject: [PATCH 064/453] (fix) Split window properly. --- org-noter-core.el | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 64bbc1a..3947b4e 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -708,7 +708,7 @@ properties, by a margin of NEWLINES-NUMBER." (if horizontal (split-window-right (ceiling (* (car (org-noter--session-doc-split-fraction session)) (window-total-width)))) - (split-window-below (ceiling (* (cdr (org-noter--session-doc-split-fraction session)) + (split-window-below (ceiling (* (cadr (org-noter--session-doc-split-fraction session)) (window-total-height))))))))) (set-window-buffer notes-window notes-buffer)) @@ -750,11 +750,12 @@ properties, by a margin of NEWLINES-NUMBER." (swap-windows notes-window doc-window)))) (if (eq window-location 'horizontal-split) - (shrink-window (- (window-total-width) (ceiling (* (car (org-noter--session-doc-split-fraction session)) - (window-total-width)))) - t) - (shrink-window (- (window-total-width) (ceiling (* (cdr (org-noter--session-doc-split-fraction session)) - (window-total-height))))))) + (enlarge-window (- (ceiling (* (- 1 (car (org-noter--session-doc-split-fraction session))) + (frame-width))) + (window-total-width)) t) + (enlarge-window (- (ceiling (* (- 1 (cadr (org-noter--session-doc-split-fraction session))) + (frame-height))) + (window-total-height))))) (if org-noter-swap-window ;; the variable NOTES-WINDOW here is really From 73c0e791be1b0ee07fa4ec202cf947446b9e6e5c Mon Sep 17 00:00:00 2001 From: c1-g Date: Thu, 13 Jan 2022 21:16:50 +0700 Subject: [PATCH 065/453] (feat) Add predicate function org-noter-no-heading-p. This predicate outputs nil when there's atleast one heading in the buffer otherwise it outputs the point-max of the buffer. --- org-noter-core.el | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/org-noter-core.el b/org-noter-core.el index 3947b4e..958abf6 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -365,6 +365,13 @@ the user select to use as the note file of the document." ;; -------------------------------------------------------------------------------- ;;; Utility functions + +(defun org-noter--no-heading-p () + "Return nil if the current buffer has atleast one heading. +Otherwise return the maximum value for point." + (save-excursion + (and (org-before-first-heading-p) (org-next-visible-heading 1)))) + (defun org-noter--get-new-id () (catch 'break (while t From f08d1ac5140731047f7a2c70cc17af57bdf92466 Mon Sep 17 00:00:00 2001 From: c1-g Date: Thu, 13 Jan 2022 21:28:15 +0700 Subject: [PATCH 066/453] (fix) org-parse-root: Handle when there is no heading. --- org-noter-core.el | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 958abf6..9182ea5 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -574,18 +574,21 @@ If nil, the session used will be `org-noter--session'." (when pos (setq root-pos (copy-marker pos))))))))) (unless ast - (unless root-pos (error "Root heading not found")) + (unless root-pos (if (org-noter--no-heading-p) + (setq root-pos (copy-marker (point-min))) + (error "Root heading not found"))) (with-current-buffer (marker-buffer root-pos) - (org-with-wide-buffer - (goto-char (marker-position root-pos)) - (when (org-before-first-heading-p) - (org-next-visible-heading 1)) - (org-narrow-to-subtree) - (setq ast (car (org-element-contents (org-element-parse-buffer 'greater-element)))) - (when (and (not (vectorp info)) (org-noter--valid-session session)) - (setf (org-noter--session-ast session) ast - (org-noter--session-modified-tick session) (buffer-chars-modified-tick)))))) - ast)) + (org-with-point-at (marker-position root-pos) + (unless (org-noter--no-heading-p) + (when (org-before-first-heading-p) + (org-next-visible-heading 1)) + (org-narrow-to-subtree)) + + (setq ast (car (org-element-contents (org-element-parse-buffer 'greater-element)))) + (when (and (not (vectorp info)) (org-noter--valid-session session)) + (setf (org-noter--session-ast session) ast + (org-noter--session-modified-tick session) (buffer-chars-modified-tick))))) + ast)) (defun org-noter--get-properties-end (ast &optional force-trim) (when ast From 122fc0cfda85c653728da634a50b3c653270ac1d Mon Sep 17 00:00:00 2001 From: c1-g Date: Sun, 26 Dec 2021 11:34:06 +0700 Subject: [PATCH 067/453] =?UTF-8?q?(fix)=20Assign=20level=20to=200=20when?= =?UTF-8?q?=20session=20doesn=E2=80=99t=20have=20a=20heading.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- org-noter-core.el | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 9182ea5..3f43860 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -430,7 +430,7 @@ Otherwise return the maximum value for point." :notes-file-path notes-file-path :doc-buffer document-buffer :notes-buffer notes-buffer - :level (org-element-property :level ast) + :level (or (org-element-property :level ast) 0) :window-behavior (org-noter--property-or-default notes-window-behavior) :window-location (org-noter--property-or-default notes-window-location) :doc-split-fraction (org-noter--property-or-default doc-split-fraction) @@ -608,7 +608,7 @@ If nil, the session used will be `org-noter--session'." (defun org-noter--set-text-properties (ast id) (org-with-wide-buffer (when ast - (let* ((level (org-element-property :level ast)) + (let* ((level (or (org-element-property :level ast) 0)) (begin (org-element-property :begin ast)) (title-begin (+ 1 level begin)) (contents-begin (org-element-property :contents-begin ast)) @@ -1804,7 +1804,7 @@ want to kill." "Create notes skeleton with the PDF outline or annotations." (org-noter--with-valid-session (let* ((ast (org-noter--parse-root)) - (top-level (org-element-property :level ast)) + (top-level (or (org-element-property :level ast) 0)) (options '(("Outline" . (outline)) ("Annotations" . (annots)) ("Both" . (outline annots)))) @@ -1966,7 +1966,7 @@ want to kill." (defun org-noter-create-skeleton-djvu () (org-noter--with-valid-session (let* ((ast (org-noter--parse-root)) - (top-level (org-element-property :level ast)) + (top-level (or (org-element-property :level ast) 0)) output-data) (require 'thingatpt) (with-current-buffer (djvu-ref outline-buf) @@ -2040,7 +2040,7 @@ want to kill." (require 'dom) (org-noter--with-valid-session (let* ((ast (org-noter--parse-root)) - (top-level (org-element-property :level ast)) + (top-level (or (org-element-property :level ast) 0)) output-data) (with-current-buffer (org-noter--session-doc-buffer session) (let* ((toc-path (cdr (aref nov-documents 0))) From c25199604839529bd236b3b085d14cb08a00f687 Mon Sep 17 00:00:00 2001 From: c1-g Date: Sun, 26 Dec 2021 11:37:29 +0700 Subject: [PATCH 068/453] (fix) Set the text props in file-level drawer when level is 0. --- org-noter-core.el | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 3f43860..98182b8 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -615,13 +615,18 @@ If nil, the session used will be `org-noter--session'." (properties-end (org-noter--get-properties-end ast t)) (inhibit-read-only t) (modified (buffer-modified-p))) - (add-text-properties (max 1 (1- begin)) begin '(read-only t)) - (add-text-properties begin (1- title-begin) `(read-only t front-sticky t ,org-noter--id-text-property ,id)) - (add-text-properties (1- title-begin) title-begin '(read-only t rear-nonsticky t)) - ;; (add-text-properties (1- contents-begin) (1- properties-end) '(read-only t)) - (when properties-end - (add-text-properties (1- properties-end) properties-end - '(read-only t rear-nonsticky t)) + (if (= level 0) + (when properties-end + (add-text-properties contents-begin properties-end + `(read-only t rear-nonsticky t ,org-noter--id-text-property ,id)) + (set-buffer-modified-p modified)) + (add-text-properties (max 1 (1- begin)) begin '(read-only t)) + (add-text-properties begin (1- title-begin) `(read-only t front-sticky t ,org-noter--id-text-property ,id)) + (add-text-properties (1- title-begin) title-begin '(read-only t rear-nonsticky t)) + ;; (add-text-properties (1- contents-begin) (1- properties-end) '(read-only t)) + (when properties-end + (add-text-properties (1- properties-end) properties-end + '(read-only t rear-nonsticky t))) (set-buffer-modified-p modified)))))) (defun org-noter--unset-text-properties (ast) From bdd789d3accd2e4e321e621348a4e4820e901904 Mon Sep 17 00:00:00 2001 From: c1-g Date: Sun, 26 Dec 2021 11:38:47 +0700 Subject: [PATCH 069/453] (fix) When no headings, the end of the prop drawer is file-level. --- org-noter-core.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/org-noter-core.el b/org-noter-core.el index 98182b8..0fd173f 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -594,7 +594,8 @@ If nil, the session used will be `org-noter--session'." (when ast (let* ((contents (org-element-contents ast)) (section (org-element-map contents 'section 'identity nil t 'headline)) - (properties (org-element-map section 'property-drawer 'identity nil t)) + (properties (or (org-element-map section 'property-drawer 'identity nil t) + (org-element-map contents 'property-drawer 'identity nil t))) properties-end) (if (not properties) (org-element-property :contents-begin ast) From 7fc6c281daae271a942886698b1eaa5de26a3760 Mon Sep 17 00:00:00 2001 From: c1-g Date: Thu, 13 Jan 2022 21:35:28 +0700 Subject: [PATCH 070/453] (fix) Don't show, hide or narrow to heading when no heading. --- org-noter-core.el | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 0fd173f..1e6ec98 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -681,7 +681,7 @@ properties, by a margin of NEWLINES-NUMBER." (run-hooks 'org-noter-insert-heading-hook))) (defun org-noter--narrow-to-root (ast) - (when ast + (when (and ast (not (org-noter--no-heading-p))) (save-excursion (goto-char (org-element-property :contents-begin ast)) (org-show-entry) @@ -1201,7 +1201,8 @@ document property) will be opened." (if (org-noter--session-hide-other session) (save-excursion (goto-char (org-element-property :begin (org-noter--parse-root))) - (outline-hide-subtree)) + (unless (org-noter--no-heading-p) + (outline-hide-subtree))) (org-cycle-hide-drawers 'all)) (let* ((notes-cons (org-noter--view-info-notes view-info)) @@ -2134,10 +2135,10 @@ defines if the text should be inserted inside the note." ((or 'nov-mode 'djvu-read-mode) (when (region-active-p) (buffer-substring-no-properties (mark) (point)))))) - + force-new (location (org-noter--doc-approx-location (or precise-info 'interactive) (gv-ref force-new))) - (view-info (org-noter--get-view-info (org-noter--get-current-view) location))) + (view-info (org-noter--get-view-info (org-noter--get-current-view) location))) (let ((inhibit-quit t)) (with-local-quit @@ -2225,7 +2226,7 @@ defines if the text should be inserted inside the note." (setq level (1+ (org-element-property :level ast)))) ;; NOTE(nox): This is needed to insert in the right place - (outline-show-entry) + (unless (org-noter--no-heading-p) (outline-show-entry)) (org-noter--insert-heading level title empty-lines-number location) (when (org-noter--session-hide-other session) (org-overview)) From 8639663902f0ad11079f4c46db9d46f8e71ddbe8 Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 14 Jan 2022 08:24:33 +0700 Subject: [PATCH 071/453] (fix) Unbalanced parentheses. --- org-noter-core.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org-noter-core.el b/org-noter-core.el index 1e6ec98..e0e4bd2 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -588,7 +588,7 @@ If nil, the session used will be `org-noter--session'." (when (and (not (vectorp info)) (org-noter--valid-session session)) (setf (org-noter--session-ast session) ast (org-noter--session-modified-tick session) (buffer-chars-modified-tick))))) - ast)) + ast))) (defun org-noter--get-properties-end (ast &optional force-trim) (when ast From ed0cccdd56a85c980524625718fa957c5ea1b92a Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 14 Jan 2022 08:33:07 +0700 Subject: [PATCH 072/453] (fix) Automatically use the next heading if root is not at point. --- org-noter-core.el | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index e0e4bd2..eac4399 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -576,14 +576,13 @@ If nil, the session used will be `org-noter--session'." (unless ast (unless root-pos (if (org-noter--no-heading-p) (setq root-pos (copy-marker (point-min))) - (error "Root heading not found"))) + (org-next-visible-heading 1) + (setq root-pos (copy-marker (point))))) (with-current-buffer (marker-buffer root-pos) (org-with-point-at (marker-position root-pos) - (unless (org-noter--no-heading-p) - (when (org-before-first-heading-p) - (org-next-visible-heading 1)) - (org-narrow-to-subtree)) - + (if (org-at-heading-p) + (org-narrow-to-subtree) + (org-hide-drawer-toggle 'force)) (setq ast (car (org-element-contents (org-element-parse-buffer 'greater-element)))) (when (and (not (vectorp info)) (org-noter--valid-session session)) (setf (org-noter--session-ast session) ast From 0d6ab72eceba62cbba862d7dfd5e4f84a346fdd4 Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 14 Jan 2022 09:04:38 +0700 Subject: [PATCH 073/453] (fix) org-noter-parse-root: Finally return ast. Previously the final return value of ast was in UNLESS form so when ast is non-nil the function doesn't return the ast. This is to fix that. --- org-noter-core.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index eac4399..093ae83 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -586,8 +586,8 @@ If nil, the session used will be `org-noter--session'." (setq ast (car (org-element-contents (org-element-parse-buffer 'greater-element)))) (when (and (not (vectorp info)) (org-noter--valid-session session)) (setf (org-noter--session-ast session) ast - (org-noter--session-modified-tick session) (buffer-chars-modified-tick))))) - ast))) + (org-noter--session-modified-tick session) (buffer-chars-modified-tick)))))) + ast)) (defun org-noter--get-properties-end (ast &optional force-trim) (when ast From 1541525079902cb56f5ae547c07341f12213d79d Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 14 Jan 2022 09:07:41 +0700 Subject: [PATCH 074/453] (minor) Ensure that root is at a heading or a file-level drawer. --- org-noter-core.el | 1 + 1 file changed, 1 insertion(+) diff --git a/org-noter-core.el b/org-noter-core.el index 093ae83..b8ab0bd 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -580,6 +580,7 @@ If nil, the session used will be `org-noter--session'." (setq root-pos (copy-marker (point))))) (with-current-buffer (marker-buffer root-pos) (org-with-point-at (marker-position root-pos) + (org-back-to-heading-or-point-min t) (if (org-at-heading-p) (org-narrow-to-subtree) (org-hide-drawer-toggle 'force)) From 78ece30617c06a7b356a09be758d350e876d487d Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 14 Jan 2022 09:08:31 +0700 Subject: [PATCH 075/453] (minor) Use a surer way to get the doc property of entry. --- org-noter-core.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org-noter-core.el b/org-noter-core.el index b8ab0bd..7eefc6a 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -554,7 +554,7 @@ If nil, the session used will be `org-noter--session'." (org-with-wide-buffer (catch 'break (while t - (when (string= (or (org-entry-get nil org-noter-property-doc-file t) + (when (string= (or (org-noter--get-or-read-document-property t) (cadar (org-collect-keywords (list org-noter-property-doc-file)))) wanted-prop) (setq root-pos (copy-marker (point)))) From f0b1c1822cd124b78439cb203133a622732edb55 Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 14 Jan 2022 09:37:04 +0700 Subject: [PATCH 076/453] (fix) If root is a property drawer, view info contents is the parent This is so that when we turn to the next page org-noter can sync the heading based on the page number. --- org-noter-core.el | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/org-noter-core.el b/org-noter-core.el index 7eefc6a..d4db94e 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1326,7 +1326,10 @@ the best heading to serve as a reference to create the new one relative to." (when view (org-noter--with-valid-session - (let ((contents (org-element-contents (org-noter--parse-root))) + (let ((contents (if (= 0 (org-noter--session-level session)) + (org-element-contents + (org-element-property :parent (org-noter--parse-root))) + (org-element-contents (org-noter--parse-root)))) (preamble t) notes-in-view regions-in-view reference-for-insertion reference-location From aaf500bf1eb20ed5c3afa305108d6c1a770e043c Mon Sep 17 00:00:00 2001 From: c1-g Date: Sun, 26 Dec 2021 10:53:04 +0700 Subject: [PATCH 077/453] (feat) Add citar module. --- other/org-noter-citar.el | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 other/org-noter-citar.el diff --git a/other/org-noter-citar.el b/other/org-noter-citar.el new file mode 100644 index 0000000..81a2448 --- /dev/null +++ b/other/org-noter-citar.el @@ -0,0 +1,37 @@ +;;; org-noter-citar.el --- Module for finding note files from `citar' -*- lexical-binding: t; -*- + +;; Copyright (C) 2021 c1-g + +;; Author: c1-g +;; Keywords: convenience + +;; 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 . + +;;; Code: +(require 'citar) + +(defun org-noter-citar-find-key-from-this-file (filename) + (let* ((entry-alist (mapcan (lambda (entry) + (when-let ((file (citar-get-value citar-file-variable entry))) + (list (cons file (citar-get-value "=key=" entry))))) + (citar--get-candidates))) + (key (alist-get filename entry-alist nil nil (lambda (s regexp) + (string-match-p regexp s))))) + (when key + (concat key ".org")))) + +(add-to-list 'org-noter-find-additional-notes-functions #'org-noter-citar-find-key-from-this-file) + +(provide 'org-noter-citar) +;;; org-noter-citar.el ends here From 3591fd001a933d19f37176bbb5857fafaab6dca4 Mon Sep 17 00:00:00 2001 From: c1-g Date: Thu, 13 Jan 2022 21:07:29 +0700 Subject: [PATCH 078/453] (feat) Add org-noter-parse-document-property-hook. --- org-noter-core.el | 32 +++++++++++++++++++++++++++++--- other/org-noter-citar.el | 19 ++++++++++++++++++- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index d4db94e..b3c0ea3 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -263,6 +263,26 @@ the user select to use as the note file of the document." ;; -------------------------------------------------------------------------------- ;;; Integration with other packages + +(defcustom org-noter-parse-document-property-hook nil + "The list of functions that return a file name for the value of +the property `org-noter-property-doc-file' + +This is used by `org-noter--get-or-read-document-property' and +`org-noter--doc-file-property'. + +This is added for integration with other packages. + +For example, the module `org-noter-citar' adds the function +`org-noter-citar-find-document-from-refs' to this list which when +the property \"NOTER_DOCUMENT\" (the default value of +`org-noter-property-doc-file') of an org file passed to it is a +citation key, it will return the path to the note file associated +with the citation key and that path will be used for other +operations instead of the real value of the property." + :group 'org-noter + :type 'hook) + (defcustom org-noter--check-location-property-hook nil "TODO" :group 'org-noter @@ -870,9 +890,11 @@ properties, by a margin of NEWLINES-NUMBER." (setq org-noter--nov-timer (run-with-timer 0.25 nil 'org-noter--doc-location-change-handler)))) (defsubst org-noter--doc-file-property (headline) - (or (org-element-property (intern (concat ":" org-noter-property-doc-file)) headline) - (cadar (org-collect-keywords (list org-noter-property-doc-file))) - (org-entry-get nil org-noter-property-doc-file t))) + (let ((doc-prop (or (org-element-property (intern (concat ":" org-noter-property-doc-file)) headline) + (cadar (org-collect-keywords (list org-noter-property-doc-file))) + (org-entry-get nil org-noter-property-doc-file t)))) + (or (run-hook-with-args-until-success 'org-noter-parse-document-property-hook doc-prop) + doc-prop))) (defun org-noter--check-location-property (arg) (let ((property (if (stringp arg) arg @@ -1503,6 +1525,10 @@ relative to." (defun org-noter--get-or-read-document-property (inherit-prop &optional force-new) (let ((doc-prop (and (not force-new) (or (org-entry-get nil org-noter-property-doc-file inherit-prop) (cadar (org-collect-keywords (list org-noter-property-doc-file))))))) + + (setq doc-prop (or (run-hook-with-args-until-success 'org-noter-parse-document-property-hook doc-prop) + doc-prop)) + (unless (org-noter--check-doc-prop doc-prop) (setq doc-prop nil) diff --git a/other/org-noter-citar.el b/other/org-noter-citar.el index 81a2448..746c04e 100644 --- a/other/org-noter-citar.el +++ b/other/org-noter-citar.el @@ -21,6 +21,21 @@ ;;; Code: (require 'citar) +(defun org-noter-citar-find-document-from-refs (cite-key) + "Return a note file associated with CITE-KEY. +When there is more than one note files associated with CITE-KEY, have +user select one of them." + (require 'orb-utils) + (when (and (stringp cite-key) (string-match orb-utils-citekey-re cite-key)) + (let* ((key (match-string 1 cite-key)) + (files (citar-file--files-for-multiple-entries + (citar--ensure-entries (list key)) + (append citar-library-paths citar-notes-paths) nil))) + (cond ((= (length files) 1) + (car files)) + ((> (length files) 1) + (completing-read (format "Which file from %s?: " key) files)))))) + (defun org-noter-citar-find-key-from-this-file (filename) (let* ((entry-alist (mapcan (lambda (entry) (when-let ((file (citar-get-value citar-file-variable entry))) @@ -29,7 +44,9 @@ (key (alist-get filename entry-alist nil nil (lambda (s regexp) (string-match-p regexp s))))) (when key - (concat key ".org")))) + (file-name-with-extension key "org")))) + +(add-to-list 'org-noter-parse-document-property-hook #'org-noter-citar-find-document-from-refs) (add-to-list 'org-noter-find-additional-notes-functions #'org-noter-citar-find-key-from-this-file) From dea82a6ca3ff7a414d3480a307cfc5a8b52619ca Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 14 Jan 2022 22:32:01 +0700 Subject: [PATCH 079/453] (fix) Speed up page turning by using org-element-cache-map. This commit focuses on the org-noter--get-view-info function, a function that help org-noter decides the best heading to serve as a "reference" heading. Previously, the function uses org-element-map which is quite slow and should only be used when buffer modification is needed, which in this case, it isn't. I made two changes to this function. First, use the more performant variant of org-element-map, org-element-cache-map. Second, make the function works when the root element is a file-level property drawer. Since org-noter--get-view-info works by mapping all elements in the note buffer every time user changes a page; consequently, when user have a really large note with a lot of headings each referencing some pages in the document, the user has to wait for the function to map all the headings before the page is turned. --- org-noter-core.el | 66 ++++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index b3c0ea3..69e46ae 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1348,10 +1348,7 @@ the best heading to serve as a reference to create the new one relative to." (when view (org-noter--with-valid-session - (let ((contents (if (= 0 (org-noter--session-level session)) - (org-element-contents - (org-element-property :parent (org-noter--parse-root))) - (org-element-contents (org-noter--parse-root)))) + (let ((root-pos (org-element-property :begin (org-noter--session-ast session))) (preamble t) notes-in-view regions-in-view reference-for-insertion reference-location @@ -1362,32 +1359,35 @@ relative to." ignore-until-level current-region-info) ;; NOTE(nox): [REGIONS-LIST-PTR START MAX-END REGIONS-LIST-NAME] - (org-element-map contents 'headline - (lambda (headline) - (let ((doc-file (org-noter--doc-file-property headline)) - (location (org-noter--parse-location-property headline))) - (when (and ignore-until-level (<= (org-element-property :level headline) ignore-until-level)) + (with-current-buffer (if org-noter-use-indirect-buffer + (buffer-base-buffer (org-noter--session-notes-buffer session)) + (org-noter--session-notes-buffer session)) + (org-element-cache-map + (lambda (element) + (let ((doc-file (org-noter--doc-file-property element)) + (location (org-noter--parse-location-property element))) + (when (and ignore-until-level (<= (or (org-element-property :level element) 0) ignore-until-level)) (setq ignore-until-level nil)) (cond (ignore-until-level) ;; NOTE(nox): This heading is ignored, do nothing ((and doc-file (not (string= doc-file (org-noter--session-property-text session)))) - (org-noter--view-region-finish current-region-info headline) - (setq ignore-until-level (org-element-property :level headline)) + (org-noter--view-region-finish current-region-info element) + (setq ignore-until-level (or (org-element-property :level element) 0)) (when (and preamble new-location (or (not reference-for-insertion) - (>= (org-element-property :begin headline) + (>= (org-element-property :begin element) (org-element-property :end (cdr reference-for-insertion))))) - (setq reference-for-insertion (cons 'after headline)))) + (setq reference-for-insertion (cons 'after element)))) (location (let ((relative-position (org-noter--relative-position-to-view location view))) (cond ((eq relative-position 'inside) - (push (cons headline nil) notes-in-view) + (push (cons element nil) notes-in-view) - (org-noter--view-region-add current-region-info regions-in-view headline) + (org-noter--view-region-add current-region-info regions-in-view element) (setq all-after-tipping-point (and all-after-tipping-point (org-noter--note-after-tipping-point @@ -1399,49 +1399,51 @@ relative to." (car notes-in-view)) ((eq (aref current-region-info 3) 'closest-notes-regions) (car closest-notes))))) - (when (< (org-element-property :begin headline) - (org-element-property :end (car note-cons-to-change))) - (setcdr note-cons-to-change headline)))) + (when (< (org-element-property :begin element) + (org-element-property :end (car note-cons-to-change))) + (setcdr note-cons-to-change element)))) (let ((eligible-for-before (and closest-tipping-point all-after-tipping-point - (eq relative-position 'before)))) + (eq relative-position 'before) + (not (= root-pos (org-element-property :begin element)))))) (cond ((and eligible-for-before (org-noter--compare-locations '> location closest-notes-location)) - (setq closest-notes (list (cons headline nil)) + (setq closest-notes (list (cons element nil)) closest-notes-location location current-region-info nil closest-notes-regions nil) - (org-noter--view-region-add current-region-info closest-notes-regions headline)) + (org-noter--view-region-add current-region-info closest-notes-regions element)) ((and eligible-for-before (equal location closest-notes-location)) - (push (cons headline nil) closest-notes) - (org-noter--view-region-add current-region-info closest-notes-regions headline)) + (push (cons element nil) closest-notes) + (org-noter--view-region-add current-region-info closest-notes-regions element)) - (t (org-noter--view-region-finish current-region-info headline))))))) + (t (org-noter--view-region-finish current-region-info element))))))) (when new-location (setq preamble nil) (cond ((and (org-noter--compare-locations '<= location new-location) (or (eq (car reference-for-insertion) 'before) (org-noter--compare-locations '>= location reference-location))) - (setq reference-for-insertion (cons 'after headline) + (setq reference-for-insertion (cons 'after element) reference-location location)) ((and (eq (car reference-for-insertion) 'after) - (< (org-element-property :begin headline) - (org-element-property :end (cdr reference-for-insertion))) + (< (org-element-property :begin element) + (org-element-property :end (cdr reference-for-insertion))) (org-noter--compare-locations '>= location new-location)) - (setq reference-for-insertion (cons 'before headline) + (setq reference-for-insertion (cons 'before element) reference-location location))))) (t (when (and preamble new-location (or (not reference-for-insertion) - (>= (org-element-property :begin headline) + (>= (org-element-property :begin element) (org-element-property :end (cdr reference-for-insertion))))) - (setq reference-for-insertion (cons 'after headline))))))) - nil nil org-noter--note-search-no-recurse) - + (setq reference-for-insertion (cons 'after element))))))) + :granularity 'element + :restrict-elements '(headline special-block))) + (org-noter--view-region-finish current-region-info) (setf (org-noter--session-num-notes-in-view session) (length notes-in-view)) From d2cd7449f8d2f64dab963e67dc6283bda0eb8c27 Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 14 Jan 2022 23:16:06 +0700 Subject: [PATCH 080/453] (minor) Add org-noter-prefer-root-as-file-level. --- org-noter-core.el | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 69e46ae..dfaea9b 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -121,6 +121,15 @@ This is a cons of the type (HORIZONTAL-FRACTION . VERTICAL-FRACTION)." :group 'org-noter :type 'boolean) +(defcustom org-noter-prefer-root-as-file-level nil + "When non-nil, org-noter will always try to return the file-level property drawer +even when there are headings. + +With the default value nil, org-noter will always use the first heading as root when +there is at least one heading." + :group 'org-noter + :type 'boolean) + (defcustom org-noter-hide-other t "When non-nil, hide all headings not related to the command used. For example, when scrolling to pages with notes, collapse all the @@ -577,7 +586,10 @@ If nil, the session used will be `org-noter--session'." (when (string= (or (org-noter--get-or-read-document-property t) (cadar (org-collect-keywords (list org-noter-property-doc-file)))) wanted-prop) - (setq root-pos (copy-marker (point)))) + (setq root-pos (copy-marker (if (and org-noter-prefer-root-as-file-level + (eq 'property-drawer (org-element-type (org-element-at-point 1)))) + (point-min) + (point))))) (unless (org-up-heading-safe) (throw 'break t)))))))) ((org-noter--valid-session session) @@ -594,7 +606,7 @@ If nil, the session used will be `org-noter--session'." (when pos (setq root-pos (copy-marker pos))))))))) (unless ast - (unless root-pos (if (org-noter--no-heading-p) + (unless root-pos (if (or org-noter-prefer-root-as-file-level (org-noter--no-heading-p)) (setq root-pos (copy-marker (point-min))) (org-next-visible-heading 1) (setq root-pos (copy-marker (point))))) From 956c29fdde9b957a5b4055b781f9a44581e077fd Mon Sep 17 00:00:00 2001 From: c1-g Date: Sun, 16 Jan 2022 09:53:13 +0700 Subject: [PATCH 081/453] (fix) Don't use cached root. --- org-noter-core.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org-noter-core.el b/org-noter-core.el index dfaea9b..cfc6807 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1360,7 +1360,7 @@ the best heading to serve as a reference to create the new one relative to." (when view (org-noter--with-valid-session - (let ((root-pos (org-element-property :begin (org-noter--session-ast session))) + (let ((root-pos (org-element-property :begin (org-noter--parse-root))) (preamble t) notes-in-view regions-in-view reference-for-insertion reference-location From d13525823530a13a1b40f972f6f49e9e325f4bf5 Mon Sep 17 00:00:00 2001 From: c1-g Date: Sun, 16 Jan 2022 09:54:41 +0700 Subject: [PATCH 082/453] (fix) Don't check for org-noter-use-indirect-buffer. --- org-noter-core.el | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index cfc6807..b35f44e 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1371,9 +1371,8 @@ relative to." ignore-until-level current-region-info) ;; NOTE(nox): [REGIONS-LIST-PTR START MAX-END REGIONS-LIST-NAME] - (with-current-buffer (if org-noter-use-indirect-buffer - (buffer-base-buffer (org-noter--session-notes-buffer session)) - (org-noter--session-notes-buffer session)) + (with-current-buffer (or (buffer-base-buffer (org-noter--session-notes-buffer session)) + (org-noter--session-notes-buffer session)) (org-element-cache-map (lambda (element) (let ((doc-file (org-noter--doc-file-property element)) From 56136c8c244a751a2ce7e5cb9b031d395f478600 Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 17 Jan 2022 10:32:31 +0700 Subject: [PATCH 083/453] Add org-noter-dynamic-block.el --- other/org-noter-dynamic-block.el | 196 +++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 other/org-noter-dynamic-block.el diff --git a/other/org-noter-dynamic-block.el b/other/org-noter-dynamic-block.el new file mode 100644 index 0000000..64b81a2 --- /dev/null +++ b/other/org-noter-dynamic-block.el @@ -0,0 +1,196 @@ +;;; org-noter-dynamic-block.el --- Use special blocks as notes -*- lexical-binding: t; -*- + +;; Copyright (C) 2021 c1-g + +;; Author: c1-g +;; Keywords: multimedia + +;; 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 . + +;;; Commentary: + +;; + +;;; Code: +(defun org-noter-insert-precise-dynamic-block (&optional toggle-no-questions) + "Insert note associated with a specific location. +This will ask you to click where you want to scroll to when you +sync the document to this note. You should click on the top of +that part. Will always create a new note. + +When text is selected, it will automatically choose the top of +the selected text as the location and the text itself as the +title of the note (you may change it anyway!). + +See `org-noter-insert-note' docstring for more." + (interactive "P") + (org-noter--with-valid-session + (let ((org-noter-insert-note-no-questions (if toggle-no-questions + (not org-noter-insert-note-no-questions) + org-noter-insert-note-no-questions))) + (org-noter-insert-dynamic-block (org-noter--get-precise-info))))) + +(defun org-noter-insert-dynamic-block (&optional precise-info) + "Insert note associated with the current location. + +This command will prompt for a title of the note and then insert +it in the notes buffer. When the input is empty, a title based on +`org-noter-default-heading-title' will be generated. + +If there are other notes related to the current location, the +prompt will also suggest them. Depending on the value of the +variable `org-noter-closest-tipping-point', it may also +suggest the closest previous note. + +PRECISE-INFO makes the new note associated with a more +specific location (see `org-noter-insert-precise-note' for more +info). + +When you insert into an existing note and have text selected on +the document buffer, the variable `org-noter-insert-selected-text-inside-note' +defines if the text should be inserted inside the note." + (interactive) + (org-noter--with-valid-session + (let* ((ast (org-noter--parse-root)) + (contents (org-element-contents ast)) + (window (org-noter--get-notes-window 'force)) + (selected-text + (pcase (org-noter--session-doc-mode session) + ('pdf-view-mode + (when (pdf-view-active-region-p) + (mapconcat 'identity (pdf-view-active-region-text) ? ))) + + ((or 'nov-mode 'djvu-read-mode) + (when (region-active-p) + (buffer-substring-no-properties (mark) (point)))))) + + force-new + (location (org-noter--doc-approx-location (or precise-info 'interactive) (gv-ref force-new))) + (view-info (org-noter--get-view-info (org-noter--get-current-view) location))) + + (let ((inhibit-quit t)) + (with-local-quit + (select-frame-set-input-focus (window-frame window)) + (select-window window) + + ;; IMPORTANT(nox): Need to be careful changing the next part, it is a bit + ;; complicated to get it right... + + (let ((point (point)) + (minibuffer-local-completion-map org-noter--completing-read-keymap) + collection default default-begin title + (empty-lines-number (if org-noter-separate-notes-from-heading 2 1))) + + (cond + ;; NOTE(nox): Both precise and without questions will create new notes + ((or precise-info force-new) + (setq default (and selected-text (replace-regexp-in-string "\n" " " selected-text)))) + (org-noter-insert-note-no-questions) + (t + (dolist (note-cons (org-noter--view-info-notes view-info)) + (let ((display (org-element-property :raw-value (car note-cons))) + (begin (org-element-property :begin (car note-cons)))) + (push (cons display note-cons) collection) + (when (and (>= point begin) (> begin (or default-begin 0))) + (setq default display + default-begin begin)))))) + + ;; NOTE(nox): Inserting a new note + (let ((reference-element-cons (org-noter--view-info-reference-for-insertion view-info)) + level) + + (if reference-element-cons + (progn + (cond + ((eq (car reference-element-cons) 'before) + (goto-char (org-element-property :begin (cdr reference-element-cons)))) + ((eq (car reference-element-cons) 'after) + (goto-char (org-element-property :end (cdr reference-element-cons))))) + ;; NOTE(nox): This is here to make the automatic "should insert blank" work better. + (when (org-at-heading-p) (backward-char)) + (setq level (org-element-property :level (cdr reference-element-cons)))) + + (goto-char (or (org-element-map contents 'section + (lambda (section) (org-element-property :end section)) + nil t org-element-all-elements) + (org-element-map ast 'section + (lambda (section) (org-element-property :end section)) + nil t org-element-all-elements)))) + + ;; (setq level (1+ (or (org-element-property :level ast) 0)))) + + ;; NOTE(nox): This is needed to insert in the right place + (unless (org-noter--no-heading-p) (outline-show-entry)) + ;; (org-noter--insert-heading level title empty-lines-number location) + (insert + "\n" + (string-join (list (format "#+BEGIN: note %s" + (if location + (concat ":" org-noter-property-note-location + (format " %S" location)) + "")) + (if selected-text + (org-make-link-string + (concat "extract:" + (let ((buffer-read-only t)) + (bir-extract-region 0 0 selected-text))) + selected-text) + "") + "#+END:") + "\n") + "\n") + + (when (org-noter--session-hide-other session) (org-overview)) + + (setf (org-noter--session-num-notes-in-view session) + (1+ (org-noter--session-num-notes-in-view session))))) + + (org-show-set-visibility t) + (org-cycle-hide-drawers 'all) + (org-cycle-show-empty-lines t))) + + (when quit-flag + ;; NOTE(nox): If this runs, it means the user quitted while creating a note, so + ;; revert to the previous window. + (select-frame-set-input-focus (org-noter--session-frame session)) + (select-window (get-buffer-window (org-noter--session-doc-buffer session))))))) + +(defun org-dblock-write:note (params) + (let ((location (plist-get params + (intern (concat ":" org-noter-property-note-location)))) + (content (plist-get params :content)) + (session org-noter--session) + (origin-window (selected-window)) + (origin-location)) + + (org-noter--with-valid-session + (setq origin-location (org-noter--doc-approx-location)) + (when (and location + (org-noter--get-location-top location) + (org-noter--get-location-left location)) + (org-noter--doc-goto-location location) + (with-current-buffer (org-noter--session-doc-buffer session) + (setq content + (pcase major-mode + ('pdf-view-mode (pdf-info-gettext (car location) (cdr location))) + ((or 'nov-mode 'djvu-read-mode) + (buffer-substring (org-noter--get-location-top location) + (org-noter--get-location-left location)))))) + (org-noter--doc-goto-location origin-location) + (select-window origin-window))) + (insert content))) + +(provide 'org-noter-dynamic-block) +;;; org-noter-dynamic-block.el ends here + From a9a5a3461e73daa0d40ade24e6468bc454793f8f Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 17 Jan 2022 10:34:01 +0700 Subject: [PATCH 084/453] Even more precise info for pdf-tools. --- org-noter-core.el | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index b35f44e..e25b6d9 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -865,9 +865,9 @@ properties, by a margin of NEWLINES-NUMBER." (defun org-noter--doc-approx-location-cons (&optional precise-info) (cond ((memq major-mode '(doc-view-mode pdf-view-mode)) - (cons (image-mode-window-get 'page) (if (and (consp precise-info) + (cons (image-mode-window-get 'page) (if (and (listp precise-info) (numberp (car precise-info)) - (numberp (cdr precise-info))) + (numberp (cadr precise-info))) precise-info 0))) ((eq major-mode 'nov-mode) (cons nov-documents-index (if (or (numberp precise-info) @@ -1011,9 +1011,7 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." ((eq mode 'pdf-view-mode) (if (pdf-view-active-region-p) (let ((edges (pdf-view-active-region))) - (cons - (cadar edges) - (caar edges))) + (car edges)) (while (not (and (eq 'mouse-1 (car event)) (eq window (posn-window (event-start event))))) @@ -1107,7 +1105,9 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (defun org-noter--get-location-left (location) "Get the left coordinate given a LOCATION of form (page top . left) or (page . top). If later form of vector is passed return 0." (if (listp (cdr location)) - (cddr location) + (if (listp (cddr location)) + (caddr location) + (cddr location)) 0)) (defun org-noter--doc-goto-location (location) From 20542464b24c8c0ae8a03e03412758f95dc7be40 Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 17 Jan 2022 11:22:36 +0700 Subject: [PATCH 085/453] org-noter--get-view-info: Use variable to restrict elements. --- org-noter-core.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index e25b6d9..4568578 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -386,8 +386,8 @@ operations instead of the real value of the property." (defconst org-noter--property-closest-tipping-point "NOTER_CLOSEST_TIPPING_POINT" "Property for overriding global `org-noter-closest-tipping-point'.") -(defconst org-noter--note-search-no-recurse (delete 'headline (append org-element-all-elements nil)) - "List of elements that shouldn't be recursed into when searching for notes.") +(defconst org-noter--note-search-element-type '(headline) + "List of elements that should be searched for notes.") (defconst org-noter--id-text-property 'org-noter-session-id "Text property used to mark the headings with open sessions.") @@ -1453,7 +1453,7 @@ relative to." (org-element-property :end (cdr reference-for-insertion))))) (setq reference-for-insertion (cons 'after element))))))) :granularity 'element - :restrict-elements '(headline special-block))) + :restrict-elements org-noter--note-search-element-type)) (org-noter--view-region-finish current-region-info) From 521f9860c28a3f512e89e936ec158a16844607f6 Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 17 Jan 2022 11:23:33 +0700 Subject: [PATCH 086/453] Load org-noter-core. --- other/org-noter-dynamic-block.el | 2 ++ 1 file changed, 2 insertions(+) diff --git a/other/org-noter-dynamic-block.el b/other/org-noter-dynamic-block.el index 64b81a2..4c9932d 100644 --- a/other/org-noter-dynamic-block.el +++ b/other/org-noter-dynamic-block.el @@ -23,6 +23,8 @@ ;; ;;; Code: +(require 'org-noter-core) + (defun org-noter-insert-precise-dynamic-block (&optional toggle-no-questions) "Insert note associated with a specific location. This will ask you to click where you want to scroll to when you From bf09817cffd1f26b7f1ec5a019b15e6ac2a2992b Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 17 Jan 2022 11:29:05 +0700 Subject: [PATCH 087/453] Delegate getting containing headline to --get-containing-element. --- org-noter-core.el | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 4568578..eff69f2 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -272,6 +272,12 @@ the user select to use as the note file of the document." ;; -------------------------------------------------------------------------------- ;;; Integration with other packages +(defcustom org-noter--get-containing-element-hook '(org-noter--get-containing-heading) + "The list of functions that will be called by +`org-noter--get-containing-element' to get the org element of the note +at point." + :group 'org-noter + :type 'hook) (defcustom org-noter-parse-document-property-hook nil "The list of functions that return a file name for the value of @@ -518,7 +524,7 @@ Otherwise return the maximum value for point." (org-noter--set-text-properties (org-noter--parse-root (vector notes-buffer document-property-value)) (org-noter--session-id session)) (unless target-location - (setq target-location (org-noter--parse-location-property (org-noter--get-containing-heading t))))) + (setq target-location (org-noter--parse-location-property (org-noter--get-containing-element t))))) ;; NOTE(nox): This timer is for preventing reflowing too soon. (run-with-idle-timer @@ -941,6 +947,10 @@ properties, by a margin of NEWLINES-NUMBER." (org-noter--get-location-page location) location))))))) +;; TODO: Documentation +(defun org-noter--get-containing-element (&optional include-root) + (run-hook-with-args-until-success 'org-noter--get-containing-element-hook include-root)) + (defun org-noter--get-containing-heading (&optional include-root) "Get smallest containing heading that encloses the point and has location property. If the point isn't inside any heading with location property, return the outer heading. @@ -2388,7 +2398,7 @@ As such, it will only work when the notes window exists." "No notes window exists" (let ((org-noter--inhibit-location-change-handler t) (contents (org-element-contents (org-noter--parse-root))) - (current-begin (org-element-property :begin (org-noter--get-containing-heading))) + (current-begin (org-element-property :begin (org-noter--get-containing-element))) previous) (when current-begin (org-noter--map-ignore-headings-with-doc-file @@ -2416,7 +2426,7 @@ As such, it will only work when the notes window exists." (if (string= (or (org-entry-get nil org-noter-property-doc-file t) (cadar (org-collect-keywords (list org-noter-property-doc-file)))) (org-noter--session-property-text session)) - (let ((location (org-noter--parse-location-property (org-noter--get-containing-heading)))) + (let ((location (org-noter--parse-location-property (org-noter--get-containing-element)))) (if location (org-noter--doc-goto-location location) (user-error "No note selected"))) From f40f0974b28e5d7cbde08583ddc03517f1f41de3 Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 17 Jan 2022 11:31:54 +0700 Subject: [PATCH 088/453] Add org-noter--get-location-property-hook. --- org-noter-core.el | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index eff69f2..e1d8231 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -272,6 +272,17 @@ the user select to use as the note file of the document." ;; -------------------------------------------------------------------------------- ;;; Integration with other packages +(defcustom org-noter--get-location-property-hook nil + "The list of functions that will return the note location of an org element. + +These functions must accept one argument, an org element. +These functions is used by `org-noter--parse-location-property' and +`org-noter--check-location-property' when they can't find the note location +of the org element given to them, that org element will be passed to +the functions in this list." + :group 'org-noter + :type 'hook) + (defcustom org-noter--get-containing-element-hook '(org-noter--get-containing-heading) "The list of functions that will be called by `org-noter--get-containing-element' to get the org element of the note @@ -916,7 +927,10 @@ properties, by a margin of NEWLINES-NUMBER." (defun org-noter--check-location-property (arg) (let ((property (if (stringp arg) arg - (org-element-property (intern (concat ":" org-noter-property-note-location)) arg)))) + (or (org-element-property + (intern (concat ":" org-noter-property-note-location)) arg) + (run-hook-with-args-until-success + 'org-noter--get-location-property-hook arg))))) (when (and (stringp property) (> (length property) 0)) (or (run-hook-with-args-until-success 'org-noter--check-location-property-hook property) (let ((value (car (read-from-string property)))) @@ -925,7 +939,10 @@ properties, by a margin of NEWLINES-NUMBER." (defun org-noter--parse-location-property (arg) (let ((property (if (stringp arg) arg - (org-element-property (intern (concat ":" org-noter-property-note-location)) arg)))) + (or (org-element-property + (intern (concat ":" org-noter-property-note-location)) arg) + (run-hook-with-args-until-success + 'org-noter--get-location-property-hook arg))))) (when (and (stringp property) (> (length property) 0)) (or (run-hook-with-args-until-success 'org-noter--parse-location-property-hook property) (let ((value (car (read-from-string property)))) From 6d5031a96c4014bf40bd1d4b5527b77bbcd54470 Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 17 Jan 2022 11:32:13 +0700 Subject: [PATCH 089/453] Make dynamic block syncable. --- other/org-noter-dynamic-block.el | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/other/org-noter-dynamic-block.el b/other/org-noter-dynamic-block.el index 4c9932d..3fdf043 100644 --- a/other/org-noter-dynamic-block.el +++ b/other/org-noter-dynamic-block.el @@ -193,6 +193,26 @@ defines if the text should be inserted inside the note." (select-window origin-window))) (insert content))) +(defun org-noter--get-location-dynamic-block (dblock) + (let ((params (read (concat "(" (org-element-property :arguments dblock) ")")))) + (format "%S" (plist-get params (intern (concat ":" org-noter-property-note-location)))))) + +(defun org-noter-get-containing-dynamic-block (&optional _include-root) + (org-noter--with-valid-session + (org-with-wide-buffer + (let ((elt (org-element-at-point))) + (catch 'break + (while (org-element-property :parent elt) + (cond + ((eq (org-element-type elt) 'dynamic-block) + (throw 'break elt)) + (t + (setq elt (org-element-property :parent elt)))))))))) + +(add-hook 'org-noter--get-containing-element-hook #'org-noter-get-containing-dynamic-block) + +(add-hook 'org-noter--get-location-property-hook #'org-noter--get-location-dynamic-block) + (provide 'org-noter-dynamic-block) ;;; org-noter-dynamic-block.el ends here From 338e453356c9fd7a6e943a13906c42a7b2de3040 Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 17 Jan 2022 12:02:50 +0700 Subject: [PATCH 090/453] (minor) Use forward-line instead of next-line. The elisp manual says it's more reliable. --- org-noter-core.el | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index e1d8231..12527a9 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -2054,8 +2054,7 @@ want to kill." (push (vector (string-trim-right (string-trim (thing-at-point 'line t)) " [[:digit:]]+") (list (string-trim-left (string-trim (thing-at-point 'line t)) ".* ")) (+ 2 (how-many " " (point-at-bol) (point-at-eol)))) output-data) - (next-line) - (move-beginning-of-line 1))))) + (forward-line))))) (with-current-buffer (org-noter--session-notes-buffer session) ;; NOTE(nox): org-with-wide-buffer can't be used because we want to reset the From 95aa8894de06e6224d1a3be1684a653caaeaacc8 Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 17 Jan 2022 12:04:49 +0700 Subject: [PATCH 091/453] Have djvu-read-mode approximate precise info location like nov-mode. --- org-noter-core.el | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/org-noter-core.el b/org-noter-core.el index 12527a9..97b3b61 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -895,7 +895,12 @@ properties, by a margin of NEWLINES-NUMBER." (max 1 (/ (+ (window-start) (window-end nil t)) 2))))) ((eq major-mode 'djvu-read-mode) - (cons djvu-doc-page (if (numberp precise-info) precise-info 0))) + (cons djvu-doc-page (if (or (numberp precise-info) + (and (consp precise-info) + (numberp (car precise-info)) + (numberp (cdr precise-info)))) + precise-info + (max 1 (/ (+ (window-start) (window-end nil t)) 2))))) (t (error "Unknown document type %s" major-mode)))) From 5eee7fdcddd7dae053ebddd7df460ce22d16fd96 Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 17 Jan 2022 12:06:01 +0700 Subject: [PATCH 092/453] (fix) Remove "Before first heading" error. --- org-noter-core.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 97b3b61..1cc9ca3 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -730,11 +730,11 @@ properties, by a margin of NEWLINES-NUMBER." (run-hooks 'org-noter-insert-heading-hook))) (defun org-noter--narrow-to-root (ast) - (when (and ast (not (org-noter--no-heading-p))) + (when ast (save-excursion (goto-char (org-element-property :contents-begin ast)) (org-show-entry) - (org-narrow-to-subtree) + (when (org-at-heading-p) (org-narrow-to-subtree)) (org-cycle-hide-drawers 'all)))) (defun org-noter--get-doc-window () @@ -2092,7 +2092,7 @@ want to kill." (setq ast (org-noter--parse-root)) (org-noter--narrow-to-root ast) (goto-char (org-element-property :begin ast)) - (outline-hide-subtree) + (when (org-at-heading-p) (outline-hide-subtree)) (org-show-children 2)))))) ;; Shamelessly stolen code from Yuchen Li. From a81ab7a0984dfc651bbbcda39a9f1204ee8e88c9 Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 17 Jan 2022 12:06:37 +0700 Subject: [PATCH 093/453] (fix) org-noter-kill-session: Don't kill base buffer when it's nil. --- org-noter-core.el | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 1cc9ca3..404cf51 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1856,10 +1856,11 @@ want to kill." (restore-buffer-modified-p nil)) (kill-buffer notes-buffer) - (with-current-buffer base-buffer - (org-noter--unset-text-properties ast) - (set-buffer-modified-p notes-modified)) - + (when base-buffer + (with-current-buffer base-buffer + (org-noter--unset-text-properties ast) + (set-buffer-modified-p notes-modified))) + (with-current-buffer doc-buffer (remove-hook 'kill-buffer-hook 'org-noter--handle-kill-buffer t)) (kill-buffer doc-buffer) From f8bcd38e1ebbc7f27717d6666adaa6f999442d53 Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 17 Jan 2022 12:07:28 +0700 Subject: [PATCH 094/453] (fix) Use a surer way to get doc file property. --- org-noter-core.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/org-noter-core.el b/org-noter-core.el index 404cf51..0786683 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -2445,8 +2445,9 @@ As such, it will only work when the notes window exists." (interactive) (org-noter--with-selected-notes-window "No notes window exists" - (if (string= (or (org-entry-get nil org-noter-property-doc-file t) + (if (string= (or (org-noter--get-or-read-document-property t) (cadar (org-collect-keywords (list org-noter-property-doc-file)))) + (org-noter--session-property-text session)) (let ((location (org-noter--parse-location-property (org-noter--get-containing-element)))) (if location From 53c60da3670c7f677ff1e38e003c6a4f7c536257 Mon Sep 17 00:00:00 2001 From: c1-g Date: Mon, 17 Jan 2022 12:48:54 +0700 Subject: [PATCH 095/453] Simplify dynamic block insertion. --- other/org-noter-dynamic-block.el | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/other/org-noter-dynamic-block.el b/other/org-noter-dynamic-block.el index 3fdf043..a2878f6 100644 --- a/other/org-noter-dynamic-block.el +++ b/other/org-noter-dynamic-block.el @@ -142,13 +142,7 @@ defines if the text should be inserted inside the note." (concat ":" org-noter-property-note-location (format " %S" location)) "")) - (if selected-text - (org-make-link-string - (concat "extract:" - (let ((buffer-read-only t)) - (bir-extract-region 0 0 selected-text))) - selected-text) - "") + (or selected-text "") "#+END:") "\n") "\n") From 33a5aa7115741a5bee4646750c301818daba4fa2 Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 4 Mar 2022 10:04:54 +0700 Subject: [PATCH 096/453] (feat) Store supported modes in a custom variable This is so that modules can add their modes to the list. --- org-noter-core.el | 5 +++++ org-noter.el | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/org-noter-core.el b/org-noter-core.el index 0786683..7d10dd9 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -68,6 +68,11 @@ :group 'convenience :version "25.3.1") +(defcustom org-noter-supported-modes '(doc-view-mode pdf-view-mode nov-mode djvu-read-mode) + "Major modes that are supported by org-noter." + :group 'org-noter + :type '(repeat symbol)) + (defcustom org-noter-property-doc-file "NOTER_DOCUMENT" "Name of the property that specifies the document." :group 'org-noter diff --git a/org-noter.el b/org-noter.el index 90b9d1e..5084f24 100644 --- a/org-noter.el +++ b/org-noter.el @@ -131,7 +131,7 @@ notes file, even if it finds one." (org-noter--create-session ast document-property notes-file-path)))) ;; NOTE(nox): Creating the session from the annotated document - ((memq major-mode '(doc-view-mode pdf-view-mode nov-mode djvu-read-mode)) + ((memq major-mode org-noter-supported-modes) (if (org-noter--valid-session org-noter--session) (progn (org-noter--setup-windows org-noter--session) (select-frame-set-input-focus (org-noter--session-frame org-noter--session))) From 6d53e7ebd6489f195ae03e4ac7f746a473dad535 Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 4 Mar 2022 10:06:35 +0700 Subject: [PATCH 097/453] (feat) Get special file name from a hook --- org-noter-core.el | 13 +++++++++++++ org-noter.el | 3 ++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/org-noter-core.el b/org-noter-core.el index 7d10dd9..6b57b2c 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -314,6 +314,19 @@ operations instead of the real value of the property." :group 'org-noter :type 'hook) +(defcustom org-noter-get-buffer-file-name-hook '(org-noter-get-buffer-file-name-nov) + "Functions that when passed a major mode, will return the current buffer file name. + +This is used by the `org-noter' command to determine the file name when +user calls `org-noter' on a document buffer. + +For example, `nov-mode', a renderer for EPUB documents uses a unique variable +called `nov-file-name' to store the file name of its document while the other +major modes uses the `buffer-file-name' variable." + :group 'org-noter + :type 'hook) + + (defcustom org-noter--check-location-property-hook nil "TODO" :group 'org-noter diff --git a/org-noter.el b/org-noter.el index 5084f24..2441ff9 100644 --- a/org-noter.el +++ b/org-noter.el @@ -138,7 +138,8 @@ notes file, even if it finds one." ;; NOTE(nox): `buffer-file-truename' is a workaround for modes that delete ;; `buffer-file-name', and may not have the same results - (let* ((buffer-file-name (or buffer-file-name (bound-and-true-p nov-file-name))) + (let* ((buffer-file-name (or (run-hook-with-args-until-success 'org-noter-get-buffer-file-name-hook major-mode) + buffer-file-name)) (document-path (or buffer-file-name buffer-file-truename (error "This buffer does not seem to be visiting any file"))) (document-name (file-name-nondirectory document-path)) From 2a51fca6def06424634adcf74105bbcc49c836d4 Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 4 Mar 2022 10:07:00 +0700 Subject: [PATCH 098/453] (fix) Don't make indirect buffer of document Why is this needed? --- org-noter-core.el | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 6b57b2c..9874398 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -464,10 +464,7 @@ Otherwise return the maximum value for point." (document-major-mode (buffer-local-value 'major-mode document)) (document-buffer-name (generate-new-buffer-name (concat (unless raw-value-not-empty "Org-noter: ") display-name))) - (document-buffer - (if (memq document-major-mode '(nov-mode djvu-read-mode)) - document - (make-indirect-buffer document document-buffer-name t))) + (document-buffer document) (notes-buffer (if org-noter-use-indirect-buffer From dc4011ea3b18a569d736933ec105863642681a80 Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 4 Mar 2022 10:08:19 +0700 Subject: [PATCH 099/453] (minor) Separate nov in its own module --- modules/org-noter-nov.el | 36 ++++++++++++++++++++++++++++++++++++ org-noter-core.el | 1 + 2 files changed, 37 insertions(+) create mode 100644 modules/org-noter-nov.el diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el new file mode 100644 index 0000000..d098727 --- /dev/null +++ b/modules/org-noter-nov.el @@ -0,0 +1,36 @@ +;;; org-noter-nov.el --- Integration with Nov.el -*- lexical-binding: t; -*- + +;; Copyright (C) 2022 c1-g + +;; Author: c1-g +;; Keywords: multimedia + +;; 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 . + +;;; Commentary: + +;; + +;;; Code: + + +(defun org-noter-get-buffer-file-name-nov () + (bound-and-true-p nov-file-name)) + + +(defun org-noter-nov-approx-location-cons (&optional precise-info _force-new-ref) + ) + +(provide 'org-noter-nov) +;;; org-noter-nov.el ends here diff --git a/org-noter-core.el b/org-noter-core.el index 9874398..ba3702c 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -40,6 +40,7 @@ (require 'cl-lib) (require 'pdf-tools) +(require 'org-noter-nov) (declare-function doc-view-goto-page "doc-view") (declare-function image-display-size "image-mode") (declare-function image-get-display-property "image-mode") From b809bdce2f8b14c8b09b1c094b8019c1c4388b06 Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 4 Mar 2022 10:20:36 +0700 Subject: [PATCH 100/453] (minor) Move nov-mode's cons approximation to its own file --- modules/org-noter-nov.el | 7 ++++++- org-noter-core.el | 8 -------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el index d098727..439f229 100644 --- a/modules/org-noter-nov.el +++ b/modules/org-noter-nov.el @@ -30,7 +30,12 @@ (defun org-noter-nov-approx-location-cons (&optional precise-info _force-new-ref) - ) + (cons nov-documents-index (if (or (numberp precise-info) + (and (consp precise-info) + (numberp (car precise-info)) + (numberp (cdr precise-info)))) + precise-info + (max 1 (/ (+ (window-start) (window-end nil t)) 2))))) (provide 'org-noter-nov) ;;; org-noter-nov.el ends here diff --git a/org-noter-core.el b/org-noter-core.el index ba3702c..8dbb29f 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -902,14 +902,6 @@ properties, by a margin of NEWLINES-NUMBER." (numberp (car precise-info)) (numberp (cadr precise-info))) precise-info 0))) - ((eq major-mode 'nov-mode) - (cons nov-documents-index (if (or (numberp precise-info) - (and (consp precise-info) - (numberp (car precise-info)) - (numberp (cdr precise-info)))) - precise-info - (max 1 (/ (+ (window-start) (window-end nil t)) 2))))) - ((eq major-mode 'djvu-read-mode) (cons djvu-doc-page (if (or (numberp precise-info) (and (consp precise-info) From ec21d1da220ce89ef356b948e487c7a3ef275609 Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 4 Mar 2022 10:21:19 +0700 Subject: [PATCH 101/453] (minor) Move error message of location approximation --- org-noter-core.el | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 8dbb29f..6bd1154 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -908,9 +908,7 @@ properties, by a margin of NEWLINES-NUMBER." (numberp (car precise-info)) (numberp (cdr precise-info)))) precise-info - (max 1 (/ (+ (window-start) (window-end nil t)) 2))))) - - (t (error "Unknown document type %s" major-mode)))) + (max 1 (/ (+ (window-start) (window-end nil t)) 2))))))) (defun org-noter--doc-approx-location (&optional precise-info force-new-ref) "TODO" @@ -919,9 +917,10 @@ properties, by a margin of NEWLINES-NUMBER." (selected-window)))) (cl-assert window) (with-selected-window window - (or (run-hook-with-args-until-success 'org-noter--doc-approx-location-hook major-mode - precise-info force-new-ref) - (org-noter--doc-approx-location-cons precise-info))))) + (or (run-hook-with-args-until-success + 'org-noter--doc-approx-location-hook major-mode + precise-info force-new-ref) + (error "Unknown document type %s" major-mode))))) (defun org-noter--location-change-advice (&rest _) (org-noter--with-valid-session (org-noter--doc-location-change-handler))) From 9b52db3bef6226f5beca6e3457f46486acff7225 Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 4 Mar 2022 10:57:43 +0700 Subject: [PATCH 102/453] (faet) Add org-noter-set-up-document-handler This is for setting up the document buffer when creating a new session. Like advising the window scrolling with a location handler. --- org-noter-core.el | 51 ++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 6bd1154..701c3dc 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -327,6 +327,11 @@ major modes uses the `buffer-file-name' variable." :group 'org-noter :type 'hook) +(defcustom org-noter-set-up-document-handler nil + "TODO" + :group 'org-noter + :type 'hook) + (defcustom org-noter--check-location-property-hook nil "TODO" @@ -508,31 +513,27 @@ Otherwise return the maximum value for point." (push session org-noter--sessions) (with-current-buffer document-buffer - (cond - ;; NOTE(nox): PDF Tools - ((eq document-major-mode 'pdf-view-mode) - (setq buffer-file-name document-path) - (pdf-view-mode) - (add-hook 'pdf-view-after-change-page-hook 'org-noter--doc-location-change-handler nil t)) - - ;; NOTE(nox): DocView - ((eq document-major-mode 'doc-view-mode) - (setq buffer-file-name document-path) - (doc-view-mode) - (advice-add 'doc-view-goto-page :after 'org-noter--location-change-advice)) - - ;; NOTE(nox): Nov.el - ((eq document-major-mode 'nov-mode) - (rename-buffer document-buffer-name) - (advice-add 'nov-render-document :after 'org-noter--nov-scroll-handler) - (add-hook 'window-scroll-functions 'org-noter--nov-scroll-handler nil t)) - - ;; NOTE(c1-g): Djvu - - ((eq document-major-mode 'djvu-read-mode) - (advice-add 'djvu-init-page :after 'org-noter--location-change-advice)) - - (t (error "This document handler is not supported :/"))) + (or (run-hook-with-args-until-success + 'org-noter-set-up-document-handler + document-major-mode) + (cond + ;; NOTE(nox): PDF Tools + ((eq document-major-mode 'pdf-view-mode) + (setq buffer-file-name document-path) + (pdf-view-mode) + (add-hook 'pdf-view-after-change-page-hook 'org-noter--doc-location-change-handler nil t)) + + ;; NOTE(nox): DocView + ((eq document-major-mode 'doc-view-mode) + (setq buffer-file-name document-path) + (doc-view-mode) + (advice-add 'doc-view-goto-page :after 'org-noter--location-change-advice)) + ;; NOTE(c1-g): Djvu + + ((eq document-major-mode 'djvu-read-mode) + (advice-add 'djvu-init-page :after 'org-noter--location-change-advice)) + + (error "This document handler is not supported :/"))) (org-noter-doc-mode 1) (setq org-noter--session session) From fd2e7c724abca2c9aadf438b1dca86c223ba2a9d Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 4 Mar 2022 11:02:31 +0700 Subject: [PATCH 103/453] (minor) Add handler for nov-mode --- modules/org-noter-nov.el | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el index 439f229..0bedc2f 100644 --- a/modules/org-noter-nov.el +++ b/modules/org-noter-nov.el @@ -37,5 +37,11 @@ precise-info (max 1 (/ (+ (window-start) (window-end nil t)) 2))))) +(defun org-noter-nov-setup-handler (major-mode) + (when (eq major-mode 'nov-mode) + (rename-buffer document-buffer-name) + (advice-add 'nov-render-document :after 'org-noter--nov-scroll-handler) + (add-hook 'window-scroll-functions 'org-noter--nov-scroll-handler nil t))) + (provide 'org-noter-nov) ;;; org-noter-nov.el ends here From dc0611d98f09f4de393109045dbe2d795472b48a Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 4 Mar 2022 11:04:29 +0700 Subject: [PATCH 104/453] (minor) Location Approximation functions take major-mode --- modules/org-noter-nov.el | 15 ++++++++------- org-noter-core.el | 4 ++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el index 0bedc2f..c4783c4 100644 --- a/modules/org-noter-nov.el +++ b/modules/org-noter-nov.el @@ -29,13 +29,14 @@ (bound-and-true-p nov-file-name)) -(defun org-noter-nov-approx-location-cons (&optional precise-info _force-new-ref) - (cons nov-documents-index (if (or (numberp precise-info) - (and (consp precise-info) - (numberp (car precise-info)) - (numberp (cdr precise-info)))) - precise-info - (max 1 (/ (+ (window-start) (window-end nil t)) 2))))) +(defun org-noter-nov-approx-location-cons (major-mode &optional precise-info _force-new-ref) + (when (eq major-mode 'nov-mode) + (cons nov-documents-index (if (or (numberp precise-info) + (and (consp precise-info) + (numberp (car precise-info)) + (numberp (cdr precise-info)))) + precise-info + (max 1 (/ (+ (window-start) (window-end nil t)) 2)))))) (defun org-noter-nov-setup-handler (major-mode) (when (eq major-mode 'nov-mode) diff --git a/org-noter-core.el b/org-noter-core.el index 701c3dc..b8d1135 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -327,7 +327,7 @@ major modes uses the `buffer-file-name' variable." :group 'org-noter :type 'hook) -(defcustom org-noter-set-up-document-handler nil +(defcustom org-noter-set-up-document-handler '(org-noter-nov-setup-handler) "TODO" :group 'org-noter :type 'hook) @@ -911,7 +911,7 @@ properties, by a margin of NEWLINES-NUMBER." precise-info (max 1 (/ (+ (window-start) (window-end nil t)) 2))))))) -(defun org-noter--doc-approx-location (&optional precise-info force-new-ref) +(defun org-noter--doc-approx-location (&optional major-mode precise-info force-new-ref) "TODO" (let ((window (if (org-noter--valid-session org-noter--session) (org-noter--get-doc-window) From 5c5cf9a809941fb3c623ea1297283ad7713cfa72 Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 4 Mar 2022 11:06:22 +0700 Subject: [PATCH 105/453] (add) Move pdf-tools approx location function to its module --- modules/org-noter-pdf.el | 35 +++++++++++++++++++++++++++++++++++ org-noter-core.el | 6 +----- 2 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 modules/org-noter-pdf.el diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el new file mode 100644 index 0000000..0ea8bdf --- /dev/null +++ b/modules/org-noter-pdf.el @@ -0,0 +1,35 @@ +;;; org-noter-pdf.el --- Modules for PDF-Tools and DocView mode -*- lexical-binding: t; -*- + +;; Copyright (C) 2022 c1-g + +;; Author: c1-g +;; Keywords: multimedia + +;; 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 . + +;;; Commentary: + +;; + +;;; Code: + +(defun org-noter-pdf-approx-location-cons (major-mode &optional precise-info _force-new-ref) + (when (memq major-mode '(doc-view-mode pdf-view-mode)) + (cons (image-mode-window-get 'page) (if (and (listp precise-info) + (numberp (car precise-info)) + (numberp (cadr precise-info))) + precise-info 0)))) + +(provide 'org-noter-pdf) +;;; org-noter-pdf.el ends here diff --git a/org-noter-core.el b/org-noter-core.el index b8d1135..43a895e 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -41,6 +41,7 @@ (require 'pdf-tools) (require 'org-noter-nov) +(require 'org-noter-pdf) (declare-function doc-view-goto-page "doc-view") (declare-function image-display-size "image-mode") (declare-function image-get-display-property "image-mode") @@ -898,11 +899,6 @@ properties, by a margin of NEWLINES-NUMBER." (defun org-noter--doc-approx-location-cons (&optional precise-info) (cond - ((memq major-mode '(doc-view-mode pdf-view-mode)) - (cons (image-mode-window-get 'page) (if (and (listp precise-info) - (numberp (car precise-info)) - (numberp (cadr precise-info))) - precise-info 0))) ((eq major-mode 'djvu-read-mode) (cons djvu-doc-page (if (or (numberp precise-info) (and (consp precise-info) From c2451de949707f478319b2c0ced687659deb4c14 Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 4 Mar 2022 11:10:32 +0700 Subject: [PATCH 106/453] (minor) Move pdf-tools & doc-view location handler to its module --- modules/org-noter-pdf.el | 12 ++++++++++++ org-noter-core.el | 16 ++++------------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 0ea8bdf..83807f3 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -31,5 +31,17 @@ (numberp (cadr precise-info))) precise-info 0)))) +(defun org-noter-pdf-view-setup-handler (major-mode) + (when (eq document-major-mode 'pdf-view-mode) + (setq buffer-file-name document-path) + (pdf-view-mode) + (add-hook 'pdf-view-after-change-page-hook 'org-noter--doc-location-change-handler nil t))) + +(defun org-noter-doc-view-setup-handler (major-mode) + (when (eq document-major-mode 'doc-view-mode) + (setq buffer-file-name document-path) + (doc-view-mode) + (advice-add 'doc-view-goto-page :after 'org-noter--location-change-advice))) + (provide 'org-noter-pdf) ;;; org-noter-pdf.el ends here diff --git a/org-noter-core.el b/org-noter-core.el index 43a895e..0067a07 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -328,7 +328,10 @@ major modes uses the `buffer-file-name' variable." :group 'org-noter :type 'hook) -(defcustom org-noter-set-up-document-handler '(org-noter-nov-setup-handler) +(defcustom org-noter-set-up-document-handler + '(org-noter-nov-setup-handler + org-noter-pdf-view-setup-handler + org-noter-doc-view-setup-handler) "TODO" :group 'org-noter :type 'hook) @@ -518,17 +521,6 @@ Otherwise return the maximum value for point." 'org-noter-set-up-document-handler document-major-mode) (cond - ;; NOTE(nox): PDF Tools - ((eq document-major-mode 'pdf-view-mode) - (setq buffer-file-name document-path) - (pdf-view-mode) - (add-hook 'pdf-view-after-change-page-hook 'org-noter--doc-location-change-handler nil t)) - - ;; NOTE(nox): DocView - ((eq document-major-mode 'doc-view-mode) - (setq buffer-file-name document-path) - (doc-view-mode) - (advice-add 'doc-view-goto-page :after 'org-noter--location-change-advice)) ;; NOTE(c1-g): Djvu ((eq document-major-mode 'djvu-read-mode) From e5bb1663e2e9b433416c846fdd660c7837914486 Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 4 Mar 2022 11:19:03 +0700 Subject: [PATCH 107/453] (feat) add djvu modules & seperation pretty print location hook --- modules/org-noter-djvu.el | 34 ++++++++++++++++++++++++++++++++++ modules/org-noter-nov.el | 7 +++++++ modules/org-noter-pdf.el | 6 ++++++ org-noter-core.el | 14 +++----------- 4 files changed, 50 insertions(+), 11 deletions(-) create mode 100644 modules/org-noter-djvu.el diff --git a/modules/org-noter-djvu.el b/modules/org-noter-djvu.el new file mode 100644 index 0000000..8d2635d --- /dev/null +++ b/modules/org-noter-djvu.el @@ -0,0 +1,34 @@ +;;; org-noter-djvu.el --- Module for DJVU -*- lexical-binding: t; -*- + +;; Copyright (C) 2022 c1-g + +;; Author: c1-g +;; Keywords: multimedia + +;; 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 . + +;;; Commentary: + +;; + +;;; Code: + +(defun org-noter-djvu--pretty-print-location (location) + (when (eq (org-noter--session-doc-mode session) '(djvu-read-mode)) + (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 0)) + (car location) + location))) + +(provide 'org-noter-djvu) +;;; org-noter-djvu.el ends here diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el index c4783c4..abc8a5d 100644 --- a/modules/org-noter-nov.el +++ b/modules/org-noter-nov.el @@ -44,5 +44,12 @@ (advice-add 'nov-render-document :after 'org-noter--nov-scroll-handler) (add-hook 'window-scroll-functions 'org-noter--nov-scroll-handler nil t))) +(defun org-noter-nov--pretty-print-location (location) + (when (eq (org-noter--session-doc-mode session) 'nov-mode) + (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 1)) + (org-noter--get-location-page location) + location))) + + (provide 'org-noter-nov) ;;; org-noter-nov.el ends here diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 83807f3..a022d48 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -43,5 +43,11 @@ (doc-view-mode) (advice-add 'doc-view-goto-page :after 'org-noter--location-change-advice))) +(defun org-noter-pdf--pretty-print-location (location) + (when (memq (org-noter--session-doc-mode session) '(doc-view-mode pdf-view-mode)) + (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 0)) + (car location) + location))) + (provide 'org-noter-pdf) ;;; org-noter-pdf.el ends here diff --git a/org-noter-core.el b/org-noter-core.el index 0067a07..bc30a43 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -42,6 +42,8 @@ (require 'org-noter-nov) (require 'org-noter-pdf) +(require 'org-noter-djvu) + (declare-function doc-view-goto-page "doc-view") (declare-function image-display-size "image-mode") (declare-function image-get-display-property "image-mode") @@ -953,17 +955,7 @@ properties, by a margin of NEWLINES-NUMBER." (defun org-noter--pretty-print-location (location) (org-noter--with-valid-session - (or (run-hook-with-args-until-success 'org-noter--pretty-print-location-hook location) - (format "%s" (cond - ((memq (org-noter--session-doc-mode session) '(doc-view-mode pdf-view-mode djvu-read-mode)) - (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 0)) - (car location) - location)) - - ((eq (org-noter--session-doc-mode session) 'nov-mode) - (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 1)) - (org-noter--get-location-page location) - location))))))) + (run-hook-with-args-until-success 'org-noter--pretty-print-location-hook major-mode location))) ;; TODO: Documentation (defun org-noter--get-containing-element (&optional include-root) From 1dde390faf7baffaf02902980442dfe9a9d062fb Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 4 Mar 2022 11:22:02 +0700 Subject: [PATCH 108/453] (minor) Pretty print functions take major-mode --- modules/org-noter-djvu.el | 4 ++-- modules/org-noter-nov.el | 4 ++-- modules/org-noter-pdf.el | 4 ++-- org-noter-core.el | 4 +++- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/modules/org-noter-djvu.el b/modules/org-noter-djvu.el index 8d2635d..b62ccec 100644 --- a/modules/org-noter-djvu.el +++ b/modules/org-noter-djvu.el @@ -24,8 +24,8 @@ ;;; Code: -(defun org-noter-djvu--pretty-print-location (location) - (when (eq (org-noter--session-doc-mode session) '(djvu-read-mode)) +(defun org-noter-djvu--pretty-print-location (major-mode location) + (when (eq major-mode '(djvu-read-mode)) (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 0)) (car location) location))) diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el index abc8a5d..63ff5b7 100644 --- a/modules/org-noter-nov.el +++ b/modules/org-noter-nov.el @@ -44,8 +44,8 @@ (advice-add 'nov-render-document :after 'org-noter--nov-scroll-handler) (add-hook 'window-scroll-functions 'org-noter--nov-scroll-handler nil t))) -(defun org-noter-nov--pretty-print-location (location) - (when (eq (org-noter--session-doc-mode session) 'nov-mode) +(defun org-noter-nov--pretty-print-location (major-mode location) + (when (eq major-mode 'nov-mode) (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 1)) (org-noter--get-location-page location) location))) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index a022d48..29d7adf 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -43,8 +43,8 @@ (doc-view-mode) (advice-add 'doc-view-goto-page :after 'org-noter--location-change-advice))) -(defun org-noter-pdf--pretty-print-location (location) - (when (memq (org-noter--session-doc-mode session) '(doc-view-mode pdf-view-mode)) +(defun org-noter-pdf--pretty-print-location (major-mode location) + (when (memq major-mode '(doc-view-mode pdf-view-mode)) (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 0)) (car location) location))) diff --git a/org-noter-core.el b/org-noter-core.el index bc30a43..f18b599 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -955,7 +955,9 @@ properties, by a margin of NEWLINES-NUMBER." (defun org-noter--pretty-print-location (location) (org-noter--with-valid-session - (run-hook-with-args-until-success 'org-noter--pretty-print-location-hook major-mode location))) + (run-hook-with-args-until-success + 'org-noter--pretty-print-location-hook + (org-noter--session-doc-mode session) location))) ;; TODO: Documentation (defun org-noter--get-containing-element (&optional include-root) From 56be40d6ec097d2cffdc50888d21e8633815ae5a Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 4 Mar 2022 11:23:33 +0700 Subject: [PATCH 109/453] (minor) Add all pretty print functions to hook --- org-noter-core.el | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/org-noter-core.el b/org-noter-core.el index f18b599..4023540 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -349,7 +349,10 @@ major modes uses the `buffer-file-name' variable." :group 'org-noter :type 'hook) -(defcustom org-noter--pretty-print-location-hook nil +(defcustom org-noter--pretty-print-location-hook + '(org-noter-nov--pretty-print-location + org-noter-pdf--pretty-print-location + org-noter-djvu--pretty-print-location) "TODO" :group 'org-noter :type 'hook) From 16c0be9702497169c167c72040a251f38d347cac Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 4 Mar 2022 11:28:13 +0700 Subject: [PATCH 110/453] (minor) Separate get-precise-info to modules --- modules/org-noter-djvu.el | 9 ++++++++ modules/org-noter-nov.el | 9 ++++++++ modules/org-noter-pdf.el | 21 +++++++++++++++++++ org-noter-core.el | 43 +++++---------------------------------- 4 files changed, 44 insertions(+), 38 deletions(-) diff --git a/modules/org-noter-djvu.el b/modules/org-noter-djvu.el index b62ccec..4efb807 100644 --- a/modules/org-noter-djvu.el +++ b/modules/org-noter-djvu.el @@ -30,5 +30,14 @@ (car location) location))) +(defun org-noter-djvu--get-precise-info (major-mode) + (when (eq mode 'djvu-read-mode) + (if (region-active-p) + (cons (mark) (point)) + (while (not (and (eq 'mouse-1 (car event)) + (eq window (posn-window (event-start event))))) + (setq event (read-event "Click where you want the start of the note to be!"))) + (posn-point (event-start event))))) + (provide 'org-noter-djvu) ;;; org-noter-djvu.el ends here diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el index 63ff5b7..6591920 100644 --- a/modules/org-noter-nov.el +++ b/modules/org-noter-nov.el @@ -50,6 +50,15 @@ (org-noter--get-location-page location) location))) +(defun org-noter-nov--get-precise-info (major-mode) + (when (eq major-mode 'nov-mode) + (if (region-active-p) + (cons (mark) (point)) + (while (not (and (eq 'mouse-1 (car event)) + (eq window (posn-window (event-start event))))) + (setq event (read-event "Click where you want the start of the note to be!"))) + (posn-point (event-start event))))) + (provide 'org-noter-nov) ;;; org-noter-nov.el ends here diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 29d7adf..faf258b 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -49,5 +49,26 @@ (car location) location))) +(defun org-noter-pdf--get-precise-info (major-mode) + (when (eq major-mode 'pdf-view-mode) + (if (pdf-view-active-region-p) + (let ((edges (pdf-view-active-region))) + (car edges)) + + (while (not (and (eq 'mouse-1 (car event)) + (eq window (posn-window (event-start event))))) + (setq event (read-event "Click where you want the start of the note to be!"))) + (let ((col-row (posn-col-row (event-start event)))) + (org-noter--conv-page-scroll-percentage (+ (window-vscroll) (cdr col-row)) + (+ (window-hscroll) (car col-row))))))) + +(defun org-noter-doc--get-precise-info (major-mode) + (when (eq major-mode 'doc-view-mode) + (while (not (and (eq 'mouse-1 (car event)) + (eq window (posn-window (event-start event))))) + (setq event (read-event "Click where you want the start of the note to be!"))) + (org-noter--conv-page-scroll-percentage (+ (window-vscroll) + (cdr (posn-col-row (event-start event))))))) + (provide 'org-noter-pdf) ;;; org-noter-pdf.el ends here diff --git a/org-noter-core.el b/org-noter-core.el index 4023540..3f4a0cc 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -377,7 +377,10 @@ major modes uses the `buffer-file-name' variable." :group 'org-noter :type 'hook) -(defcustom org-noter--get-precise-info-hook nil +(defcustom org-noter--get-precise-info-hook '(org-noter-nov--get-precise-info + org-noter-djvu--get-precise-info + org-noter-pdf--get-precise-info + org-noter-doc--get-precise-info) "TODO" :group 'org-noter :type 'hook) @@ -1030,43 +1033,7 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (mode (org-noter--session-doc-mode session)) event) (with-selected-window window - (cond - ((run-hook-with-args-until-success 'org-noter--get-precise-info-hook mode)) - - ((eq mode 'pdf-view-mode) - (if (pdf-view-active-region-p) - (let ((edges (pdf-view-active-region))) - (car edges)) - - (while (not (and (eq 'mouse-1 (car event)) - (eq window (posn-window (event-start event))))) - (setq event (read-event "Click where you want the start of the note to be!"))) - (let ((col-row (posn-col-row (event-start event)))) - (org-noter--conv-page-scroll-percentage (+ (window-vscroll) (cdr col-row)) - (+ (window-hscroll) (car col-row)))))) - - ((eq mode 'doc-view-mode) - (while (not (and (eq 'mouse-1 (car event)) - (eq window (posn-window (event-start event))))) - (setq event (read-event "Click where you want the start of the note to be!"))) - (org-noter--conv-page-scroll-percentage (+ (window-vscroll) - (cdr (posn-col-row (event-start event)))))) - - ((eq mode 'nov-mode) - (if (region-active-p) - (cons (mark) (point)) - (while (not (and (eq 'mouse-1 (car event)) - (eq window (posn-window (event-start event))))) - (setq event (read-event "Click where you want the start of the note to be!"))) - (posn-point (event-start event)))) - - ((eq mode 'djvu-read-mode) - (if (region-active-p) - (cons (mark) (point)) - (while (not (and (eq 'mouse-1 (car event)) - (eq window (posn-window (event-start event))))) - (setq event (read-event "Click where you want the start of the note to be!"))) - (posn-point (event-start event))))))))) + (run-hook-with-args-until-success 'org-noter--get-precise-info-hook mode))))) (defun org-noter--show-arrow () (when (and org-noter--arrow-location From fa91ca6dde3ef55ba90fb990962b7667cd6c463e Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 4 Mar 2022 11:31:42 +0700 Subject: [PATCH 111/453] (minor) Add doc approx location to hook --- modules/org-noter-djvu.el | 8 ++++++++ org-noter-core.el | 5 ++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/modules/org-noter-djvu.el b/modules/org-noter-djvu.el index 4efb807..76f909e 100644 --- a/modules/org-noter-djvu.el +++ b/modules/org-noter-djvu.el @@ -30,6 +30,14 @@ (car location) location))) +(defun org-noter-djvu-approx-location-cons (major-mode &optional precise-info _force-new-ref) + (cons djvu-doc-page (if (or (numberp precise-info) + (and (consp precise-info) + (numberp (car precise-info)) + (numberp (cdr precise-info)))) + precise-info + (max 1 (/ (+ (window-start) (window-end nil t)) 2))))) + (defun org-noter-djvu--get-precise-info (major-mode) (when (eq mode 'djvu-read-mode) (if (region-active-p) diff --git a/org-noter-core.el b/org-noter-core.el index 3f4a0cc..2369041 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -385,7 +385,10 @@ major modes uses the `buffer-file-name' variable." :group 'org-noter :type 'hook) -(defcustom org-noter--doc-approx-location-hook nil +(defcustom org-noter--doc-approx-location-hook + '(org-noter-nov-approx-location-cons + org-noter-pdf-approx-location-cons + org-noter-djvu-approx-location-cons) "TODO" :group 'org-noter :type 'hook) From 17ca5d492532f98095b1a5e643fc6f0233a3cdd5 Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 4 Mar 2022 11:36:44 +0700 Subject: [PATCH 112/453] (minor) Seperate doc-goto-location-hook --- modules/org-noter-djvu.el | 5 +++++ modules/org-noter-nov.el | 9 +++++++++ modules/org-noter-pdf.el | 21 +++++++++++++++++++++ org-noter-core.el | 39 +++++---------------------------------- 4 files changed, 40 insertions(+), 34 deletions(-) diff --git a/modules/org-noter-djvu.el b/modules/org-noter-djvu.el index 76f909e..f8fcc44 100644 --- a/modules/org-noter-djvu.el +++ b/modules/org-noter-djvu.el @@ -47,5 +47,10 @@ (setq event (read-event "Click where you want the start of the note to be!"))) (posn-point (event-start event))))) +(defun org-noter-djvu-goto-location (mode location) + (when (eq mode 'djvu-read-mode) + (djvu-goto-page (car location)) + (goto-char (org-noter--get-location-top location)))) + (provide 'org-noter-djvu) ;;; org-noter-djvu.el ends here diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el index 6591920..dbacbf5 100644 --- a/modules/org-noter-nov.el +++ b/modules/org-noter-nov.el @@ -59,6 +59,15 @@ (setq event (read-event "Click where you want the start of the note to be!"))) (posn-point (event-start event))))) +(defun org-noter-nov-goto-location (mode location) + (when (eq mode 'nov-mode) + (setq nov-documents-index (org-noter--get-location-page location)) + (nov-render-document) + (goto-char (org-noter--get-location-top location)) + ;; NOTE(nox): This needs to be here, because it would be issued anyway after + ;; everything and would run org-noter--nov-scroll-handler. + (recenter))) + (provide 'org-noter-nov) ;;; org-noter-nov.el ends here diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index faf258b..0e5b7e4 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -70,5 +70,26 @@ (org-noter--conv-page-scroll-percentage (+ (window-vscroll) (cdr (posn-col-row (event-start event))))))) + +(defun org-noter-pdf-goto-location (mode location) + (when (memq mode '(doc-view-mode pdf-view-mode)) + (let ((top (org-noter--get-location-top location)) + (left (org-noter--get-location-left location))) + + (if (eq mode 'doc-view-mode) + (doc-view-goto-page (org-noter--get-location-page location)) + (pdf-view-goto-page (org-noter--get-location-page location)) + ;; NOTE(nox): This timer is needed because the tooltip may introduce a delay, + ;; so syncing multiple pages was slow + (when (>= org-noter-arrow-delay 0) + (when org-noter--arrow-location (cancel-timer (aref org-noter--arrow-location 0))) + (setq org-noter--arrow-location + (vector (run-with-idle-timer org-noter-arrow-delay nil 'org-noter--show-arrow) + window + top + left)))) + (image-scroll-up (- (org-noter--conv-page-percentage-scroll top) + (window-vscroll)))))) + (provide 'org-noter-pdf) ;;; org-noter-pdf.el ends here diff --git a/org-noter-core.el b/org-noter-core.el index 2369041..a6a2b4e 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -362,7 +362,10 @@ major modes uses the `buffer-file-name' variable." :group 'org-noter :type 'hook) -(defcustom org-noter--doc-goto-location-hook nil +(defcustom org-noter--doc-goto-location-hook + '(org-noter-pdf-goto-location + org-noter-nov-goto-location + org-noter-djvu-goto-location) "TODO" :group 'org-noter :type 'hook) @@ -1111,39 +1114,7 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (let ((window (org-noter--get-doc-window)) (mode (org-noter--session-doc-mode session))) (with-selected-window window - (cond - ((run-hook-with-args-until-success 'org-noter--doc-goto-location-hook mode location)) - - ((memq mode '(doc-view-mode pdf-view-mode)) - (let ((top (org-noter--get-location-top location)) - (left (org-noter--get-location-left location))) - - (if (eq mode 'doc-view-mode) - (doc-view-goto-page (org-noter--get-location-page location)) - (pdf-view-goto-page (org-noter--get-location-page location)) - ;; NOTE(nox): This timer is needed because the tooltip may introduce a delay, - ;; so syncing multiple pages was slow - (when (>= org-noter-arrow-delay 0) - (when org-noter--arrow-location (cancel-timer (aref org-noter--arrow-location 0))) - (setq org-noter--arrow-location - (vector (run-with-idle-timer org-noter-arrow-delay nil 'org-noter--show-arrow) - window - top - left)))) - (image-scroll-up (- (org-noter--conv-page-percentage-scroll top) - (window-vscroll))))) - - ((eq mode 'nov-mode) - (setq nov-documents-index (org-noter--get-location-page location)) - (nov-render-document) - (goto-char (org-noter--get-location-top location)) - (recenter)) - ;; NOTE(nox): This needs to be here, because it would be issued anyway after - ;; everything and would run org-noter--nov-scroll-handler. - - ((eq mode 'djvu-read-mode) - (djvu-goto-page (car location)) - (goto-char (org-noter--get-location-top location)))) + (run-hook-with-args-until-success 'org-noter--doc-goto-location-hook mode location) (redisplay))))) (defun org-noter--compare-location-cons (comp l1 l2) From a1473ec0ca5df2ff6eac851aa16880b4f73386d2 Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 4 Mar 2022 11:43:49 +0700 Subject: [PATCH 113/453] (add) org-noter--get-current-view-hook --- modules/org-noter-djvu.el | 4 ++++ modules/org-noter-nov.el | 6 ++++++ modules/org-noter-pdf.el | 4 ++++ org-noter-core.el | 20 +++++++++++++------- 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/modules/org-noter-djvu.el b/modules/org-noter-djvu.el index f8fcc44..99a831c 100644 --- a/modules/org-noter-djvu.el +++ b/modules/org-noter-djvu.el @@ -52,5 +52,9 @@ (djvu-goto-page (car location)) (goto-char (org-noter--get-location-top location)))) +(defun org-noter-djvu--get-current-view (mode) + (when (eq mode 'djvu-read-mode) + (vector 'paged (car (org-noter--doc-approx-location-cons))))) + (provide 'org-noter-djvu) ;;; org-noter-djvu.el ends here diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el index dbacbf5..bf66513 100644 --- a/modules/org-noter-nov.el +++ b/modules/org-noter-nov.el @@ -68,6 +68,12 @@ ;; everything and would run org-noter--nov-scroll-handler. (recenter))) +(defun org-noter-nov--get-current-view (mode) + (when (eq mode 'nov-mode) + (vector 'nov + (org-noter--doc-approx-location-cons (window-start)) + (org-noter--doc-approx-location-cons (window-end nil t))))) + (provide 'org-noter-nov) ;;; org-noter-nov.el ends here diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 0e5b7e4..8df2756 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -91,5 +91,9 @@ (image-scroll-up (- (org-noter--conv-page-percentage-scroll top) (window-vscroll)))))) +(defun org-noter-pdf--get-current-view (mode) + (when (memq mode '(doc-view-mode pdf-view-mode)) + (vector 'paged (car (org-noter--doc-approx-location-cons))))) + (provide 'org-noter-pdf) ;;; org-noter-pdf.el ends here diff --git a/org-noter-core.el b/org-noter-core.el index a6a2b4e..b68ab89 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -388,6 +388,17 @@ major modes uses the `buffer-file-name' variable." :group 'org-noter :type 'hook) + +(defcustom org-noter--get-current-view-hook + '(org-noter-nov--get-current-view + org-noter-djvu--get-current-view + org-noter-pdf--get-current-view) + "TODO" + :group 'org-noter + :type 'hook) + + + (defcustom org-noter--doc-approx-location-hook '(org-noter-nov-approx-location-cons org-noter-pdf-approx-location-cons @@ -1254,13 +1265,8 @@ document property) will be opened." (org-noter--with-valid-session (let ((mode (org-noter--session-doc-mode session))) (with-selected-window (org-noter--get-doc-window) - (cond ((memq mode '(doc-view-mode pdf-view-mode djvu-read-mode)) - (vector 'paged (car (org-noter--doc-approx-location-cons)))) - ((eq mode 'nov-mode) - (vector 'nov - (org-noter--doc-approx-location-cons (window-start)) - (org-noter--doc-approx-location-cons (window-end nil t)))) - (t (error "Unknown document type"))))))) + (or (run-hook-with-args-until-success 'org-noter--get-current-view-hook) + (error "Unknown document type")))))) (defun org-noter--note-after-tipping-point (point location view) ;; NOTE(nox): This __assumes__ the note is inside the view! From c89e5bb0ed5db1def4eb924da9cbc93ed67874af Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 4 Mar 2022 11:50:11 +0700 Subject: [PATCH 114/453] (add) org-noter-get-selected-text-hook For when creating a new note headline, the selected text will be the title of the headline. --- modules/org-noter-djvu.el | 5 +++++ modules/org-noter-nov.el | 4 ++++ modules/org-noter-pdf.el | 5 +++++ org-noter-core.el | 19 +++++++++++-------- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/modules/org-noter-djvu.el b/modules/org-noter-djvu.el index 99a831c..d4bc9aa 100644 --- a/modules/org-noter-djvu.el +++ b/modules/org-noter-djvu.el @@ -56,5 +56,10 @@ (when (eq mode 'djvu-read-mode) (vector 'paged (car (org-noter--doc-approx-location-cons))))) +(defun org-noter-djvu--get-selected-text (mode) + (when (and (eq mode 'djvu-read-mode) + (region-active-p)) + (buffer-substring-no-properties (mark) (point)))) + (provide 'org-noter-djvu) ;;; org-noter-djvu.el ends here diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el index bf66513..f41a6da 100644 --- a/modules/org-noter-nov.el +++ b/modules/org-noter-nov.el @@ -74,6 +74,10 @@ (org-noter--doc-approx-location-cons (window-start)) (org-noter--doc-approx-location-cons (window-end nil t))))) +(defun org-noter-nov--get-selected-text (mode) + (when (and (eq mode 'nov-mode) (region-active-p)) + (buffer-substring-no-properties (mark) (point)))) + (provide 'org-noter-nov) ;;; org-noter-nov.el ends here diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 8df2756..b0df404 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -95,5 +95,10 @@ (when (memq mode '(doc-view-mode pdf-view-mode)) (vector 'paged (car (org-noter--doc-approx-location-cons))))) +(defun org-noter-pdf--get-selected-text (mode) + (when (and (eq mode 'pdf-view-mode) + (pdf-view-active-region-p)) + (mapconcat 'identity (pdf-view-active-region-text) ? ))) + (provide 'org-noter-pdf) ;;; org-noter-pdf.el ends here diff --git a/org-noter-core.el b/org-noter-core.el index b68ab89..4a1465c 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -338,6 +338,14 @@ major modes uses the `buffer-file-name' variable." :group 'org-noter :type 'hook) +(defcustom org-noter-get-selected-text-hook + '(org-noter-nov--get-selected-text + org-noter-pdf--get-selected-text + org-noter-djvu--get-selected-text) + "TODO" + :group 'org-noter + :type 'hook) + (defcustom org-noter--check-location-property-hook nil "TODO" @@ -2141,14 +2149,9 @@ defines if the text should be inserted inside the note." (let* ((ast (org-noter--parse-root)) (contents (org-element-contents ast)) (window (org-noter--get-notes-window 'force)) (selected-text - (pcase (org-noter--session-doc-mode session) - ('pdf-view-mode - (when (pdf-view-active-region-p) - (mapconcat 'identity (pdf-view-active-region-text) ? ))) - - ((or 'nov-mode 'djvu-read-mode) - (when (region-active-p) - (buffer-substring-no-properties (mark) (point)))))) + (run-hook-with-args-until-success + 'org-noter-get-selected-text-hook + (org-noter--session-doc-mode session))) force-new (location (org-noter--doc-approx-location (or precise-info 'interactive) (gv-ref force-new))) From 9ed7e3bf2685432420dfc20af402491472cd559e Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 4 Mar 2022 11:50:54 +0700 Subject: [PATCH 115/453] (minor) Fix indentation --- org-noter-core.el | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 4a1465c..be140c2 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -388,10 +388,11 @@ major modes uses the `buffer-file-name' variable." :group 'org-noter :type 'hook) -(defcustom org-noter--get-precise-info-hook '(org-noter-nov--get-precise-info - org-noter-djvu--get-precise-info - org-noter-pdf--get-precise-info - org-noter-doc--get-precise-info) +(defcustom org-noter--get-precise-info-hook + '(org-noter-nov--get-precise-info + org-noter-djvu--get-precise-info + org-noter-pdf--get-precise-info + org-noter-doc--get-precise-info) "TODO" :group 'org-noter :type 'hook) @@ -405,8 +406,6 @@ major modes uses the `buffer-file-name' variable." :group 'org-noter :type 'hook) - - (defcustom org-noter--doc-approx-location-hook '(org-noter-nov-approx-location-cons org-noter-pdf-approx-location-cons From bdb7aacc5b59fbd54a79d26b532d80989bcb6627 Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 4 Mar 2022 14:49:49 +0700 Subject: [PATCH 116/453] (add) Handler for djvu --- modules/org-noter-djvu.el | 5 +++++ modules/org-noter-nov.el | 3 ++- modules/org-noter-pdf.el | 14 ++++++++------ org-noter-core.el | 17 +++++------------ 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/modules/org-noter-djvu.el b/modules/org-noter-djvu.el index d4bc9aa..1970250 100644 --- a/modules/org-noter-djvu.el +++ b/modules/org-noter-djvu.el @@ -47,6 +47,11 @@ (setq event (read-event "Click where you want the start of the note to be!"))) (posn-point (event-start event))))) +(defun org-noter-djvu-setup-handler (major-mode) + (when (eq major-mode 'djvu-read-mode) + (advice-add 'djvu-init-page :after 'org-noter--location-change-advice) + t)) + (defun org-noter-djvu-goto-location (mode location) (when (eq mode 'djvu-read-mode) (djvu-goto-page (car location)) diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el index f41a6da..6ccb308 100644 --- a/modules/org-noter-nov.el +++ b/modules/org-noter-nov.el @@ -42,7 +42,8 @@ (when (eq major-mode 'nov-mode) (rename-buffer document-buffer-name) (advice-add 'nov-render-document :after 'org-noter--nov-scroll-handler) - (add-hook 'window-scroll-functions 'org-noter--nov-scroll-handler nil t))) + (add-hook 'window-scroll-functions 'org-noter--nov-scroll-handler nil t) + t)) (defun org-noter-nov--pretty-print-location (major-mode location) (when (eq major-mode 'nov-mode) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index b0df404..c5826cc 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -32,16 +32,18 @@ precise-info 0)))) (defun org-noter-pdf-view-setup-handler (major-mode) - (when (eq document-major-mode 'pdf-view-mode) - (setq buffer-file-name document-path) + (when (eq major-mode 'pdf-view-mode) + ;; (setq buffer-file-name document-path) (pdf-view-mode) - (add-hook 'pdf-view-after-change-page-hook 'org-noter--doc-location-change-handler nil t))) + (add-hook 'pdf-view-after-change-page-hook 'org-noter--doc-location-change-handler nil t) + t)) (defun org-noter-doc-view-setup-handler (major-mode) - (when (eq document-major-mode 'doc-view-mode) - (setq buffer-file-name document-path) + (when (eq major-mode 'doc-view-mode) + ;; (setq buffer-file-name document-path) (doc-view-mode) - (advice-add 'doc-view-goto-page :after 'org-noter--location-change-advice))) + (advice-add 'doc-view-goto-page :after 'org-noter--location-change-advice) + t)) (defun org-noter-pdf--pretty-print-location (major-mode location) (when (memq major-mode '(doc-view-mode pdf-view-mode)) diff --git a/org-noter-core.el b/org-noter-core.el index be140c2..05ada52 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -333,7 +333,8 @@ major modes uses the `buffer-file-name' variable." (defcustom org-noter-set-up-document-handler '(org-noter-nov-setup-handler org-noter-pdf-view-setup-handler - org-noter-doc-view-setup-handler) + org-noter-doc-view-setup-handler + org-noter-djvu-setup-handler) "TODO" :group 'org-noter :type 'hook) @@ -549,16 +550,8 @@ Otherwise return the maximum value for point." (push session org-noter--sessions) (with-current-buffer document-buffer - (or (run-hook-with-args-until-success - 'org-noter-set-up-document-handler - document-major-mode) - (cond - ;; NOTE(c1-g): Djvu - - ((eq document-major-mode 'djvu-read-mode) - (advice-add 'djvu-init-page :after 'org-noter--location-change-advice)) - - (error "This document handler is not supported :/"))) + (or (run-hook-with-args-until-success 'org-noter-set-up-document-handler document-major-mode) + (error "This document handler is not supported :/")) (org-noter-doc-mode 1) (setq org-noter--session session) @@ -1272,7 +1265,7 @@ document property) will be opened." (org-noter--with-valid-session (let ((mode (org-noter--session-doc-mode session))) (with-selected-window (org-noter--get-doc-window) - (or (run-hook-with-args-until-success 'org-noter--get-current-view-hook) + (or (run-hook-with-args-until-success 'org-noter--get-current-view-hook mode) (error "Unknown document type")))))) (defun org-noter--note-after-tipping-point (point location view) From d10aa0c2a0f76bd82b33fd037ded545d6109d391 Mon Sep 17 00:00:00 2001 From: c1-g Date: Thu, 10 Mar 2022 21:53:52 +0700 Subject: [PATCH 117/453] (fix) org-pdftools location link causes error https://github.com/weirdNox/org-noter/pull/93 --- org-noter-core.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/org-noter-core.el b/org-noter-core.el index b35f44e..f769371 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1582,7 +1582,8 @@ With a prefix ARG, remove start location." (org-noter--with-valid-session (let ((inhibit-read-only t) (ast (org-noter--parse-root)) - (location (org-noter--doc-approx-location 'interactive))) + (location (org-noter--doc-approx-location + (when (called-interactively-p 'any) 'interactive)))) (with-current-buffer (org-noter--session-notes-buffer session) (org-with-wide-buffer (goto-char (org-element-property :begin ast)) From fc5ea8ec74fef3e3a9a7460ac25f859535cb6653 Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 11 Mar 2022 12:13:33 +0700 Subject: [PATCH 118/453] (fix) Don't kill base buffer after killing session --- org-noter-core.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/org-noter-core.el b/org-noter-core.el index f769371..ac70706 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1823,7 +1823,8 @@ want to kill." (with-current-buffer notes-buffer (remove-hook 'kill-buffer-hook 'org-noter--handle-kill-buffer t) (restore-buffer-modified-p nil)) - (kill-buffer notes-buffer) + (unless org-noter-use-indirect-buffer + (kill-buffer notes-buffer)) (with-current-buffer base-buffer (org-noter--unset-text-properties ast) From 4a390feb43251c6305f5fd7038113045adab3001 Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 11 Mar 2022 12:13:55 +0700 Subject: [PATCH 119/453] (fix) Handle when there's no base buffer --- org-noter-core.el | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index ac70706..061424d 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1826,9 +1826,10 @@ want to kill." (unless org-noter-use-indirect-buffer (kill-buffer notes-buffer)) - (with-current-buffer base-buffer - (org-noter--unset-text-properties ast) - (set-buffer-modified-p notes-modified)) + (when base-buffer + (with-current-buffer base-buffer + (org-noter--unset-text-properties ast) + (set-buffer-modified-p notes-modified))) (with-current-buffer doc-buffer (remove-hook 'kill-buffer-hook 'org-noter--handle-kill-buffer t)) From fdafe5920cbc8cfdc0a3ee2ed46cae2b2f3fd119 Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 11 Mar 2022 12:14:20 +0700 Subject: [PATCH 120/453] (fix) Use org-noter--get-or-read-document-property to get doc file --- org-noter-core.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org-noter-core.el b/org-noter-core.el index 061424d..ae02d43 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -2416,7 +2416,7 @@ As such, it will only work when the notes window exists." (interactive) (org-noter--with-selected-notes-window "No notes window exists" - (if (string= (or (org-entry-get nil org-noter-property-doc-file t) + (if (string= (or (org-noter--get-or-read-document-property t) (cadar (org-collect-keywords (list org-noter-property-doc-file)))) (org-noter--session-property-text session)) (let ((location (org-noter--parse-location-property (org-noter--get-containing-heading)))) From 5f8798cec3a3500c18802d41ab7e1be25b56c363 Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 11 Mar 2022 12:15:44 +0700 Subject: [PATCH 121/453] (change) Remove org-roam-bibtex dependency in citar module --- other/org-noter-citar.el | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/other/org-noter-citar.el b/other/org-noter-citar.el index 746c04e..d9915b8 100644 --- a/other/org-noter-citar.el +++ b/other/org-noter-citar.el @@ -21,12 +21,18 @@ ;;; Code: (require 'citar) +;; Regexp stolen from org-roam-bibtex; orb-utils-citekey-re. +(defvar org-noter-citar-cite-key-re + "\\(?2:\\(?:Autocite[*s]?\\|Cite\\(?:a\\(?:l\\(?:[pt]\\*\\|[pt]\\)\\|uthor\\*?\\)\\|[pt]\\*\\|[pst]\\)?\\|Notecite\\|P\\(?:arencites?\\|notecite\\)\\|Smartcites?\\|Textcites?\\|autocite[*s]?\\|bibentry\\|cite\\(?:a\\(?:l\\(?:[pt]\\*\\|[pt]\\)\\|uthor\\*?\\)\\|date\\*?\\|num\\|p\\*\\|t\\(?:\\*\\|ext\\|itle\\*?\\)\\|url\\|year\\(?:\\*\\|par\\)?\\|[*pst]\\)?\\|f\\(?:notecite\\|oot\\(?:cite\\(?:s\\|texts?\\)?\\|fullcite\\)\\|ullcite\\)\\|no\\(?:\\(?:te\\)?cite\\)\\|p\\(?:arencite[*s]?\\|notecite\\)\\|s\\(?:martcites?\\|upercites?\\)\\|textcites?\\)\\):\\(?:\\(?1:[.0-:A-Z_a-z-]+\\)\\|[^&]*?&\\(?1:[!#-+./:<>-@^-`{-~[:word:]-]+\\)\\)\\|@\\(?1:[!#-+./:<>-@^-`{-~[:word:]-]+\\)" + "Universal regexp to match citations in ROAM_REFS. + +Supports Org-ref v2 and v3 and Org-cite.") + (defun org-noter-citar-find-document-from-refs (cite-key) "Return a note file associated with CITE-KEY. When there is more than one note files associated with CITE-KEY, have user select one of them." - (require 'orb-utils) - (when (and (stringp cite-key) (string-match orb-utils-citekey-re cite-key)) + (when (and (stringp cite-key) (string-match org-noter-citar-cite-key-re cite-key)) (let* ((key (match-string 1 cite-key)) (files (citar-file--files-for-multiple-entries (citar--ensure-entries (list key)) From 740d6e57ee2c3b216caac376f4553eff8a12e13c Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 11 Mar 2022 12:21:58 +0700 Subject: [PATCH 122/453] (fix) Works with org-noter-pdftools by passing just location --- modules/org-noter-djvu.el | 11 ++++++----- modules/org-noter-nov.el | 11 ++++++----- modules/org-noter-pdf.el | 13 +++++++------ org-noter-core.el | 3 +-- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/modules/org-noter-djvu.el b/modules/org-noter-djvu.el index 1970250..ea3d6ea 100644 --- a/modules/org-noter-djvu.el +++ b/modules/org-noter-djvu.el @@ -24,11 +24,12 @@ ;;; Code: -(defun org-noter-djvu--pretty-print-location (major-mode location) - (when (eq major-mode '(djvu-read-mode)) - (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 0)) - (car location) - location))) +(defun org-noter-djvu--pretty-print-location (location) + (org-noter--with-valid-session + (when (eq (org-noter--session-doc-mode session) 'djvu-read-mode) + (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 0)) + (car location) + location)))) (defun org-noter-djvu-approx-location-cons (major-mode &optional precise-info _force-new-ref) (cons djvu-doc-page (if (or (numberp precise-info) diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el index 6ccb308..c513992 100644 --- a/modules/org-noter-nov.el +++ b/modules/org-noter-nov.el @@ -45,11 +45,12 @@ (add-hook 'window-scroll-functions 'org-noter--nov-scroll-handler nil t) t)) -(defun org-noter-nov--pretty-print-location (major-mode location) - (when (eq major-mode 'nov-mode) - (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 1)) - (org-noter--get-location-page location) - location))) +(defun org-noter-nov--pretty-print-location (location) + (org-noter--with-valid-session + (when (eq (org-noter--session-doc-mode session) 'nov-mode) + (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 1)) + (org-noter--get-location-page location) + location)))) (defun org-noter-nov--get-precise-info (major-mode) (when (eq major-mode 'nov-mode) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index c5826cc..8686528 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -45,11 +45,12 @@ (advice-add 'doc-view-goto-page :after 'org-noter--location-change-advice) t)) -(defun org-noter-pdf--pretty-print-location (major-mode location) - (when (memq major-mode '(doc-view-mode pdf-view-mode)) - (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 0)) - (car location) - location))) +(defun org-noter-pdf--pretty-print-location (location) + (org-noter--with-valid-session + (when (memq (org-noter--session-doc-mode session) '(doc-view-mode pdf-view-mode)) + (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 0)) + (car location) + location)))) (defun org-noter-pdf--get-precise-info (major-mode) (when (eq major-mode 'pdf-view-mode) @@ -95,7 +96,7 @@ (defun org-noter-pdf--get-current-view (mode) (when (memq mode '(doc-view-mode pdf-view-mode)) - (vector 'paged (car (org-noter--doc-approx-location-cons))))) + (vector 'paged (car (org-noter-pdf-approx-location-cons mode))))) (defun org-noter-pdf--get-selected-text (mode) (when (and (eq mode 'pdf-view-mode) diff --git a/org-noter-core.el b/org-noter-core.el index 05ada52..f022497 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -979,8 +979,7 @@ properties, by a margin of NEWLINES-NUMBER." (defun org-noter--pretty-print-location (location) (org-noter--with-valid-session (run-hook-with-args-until-success - 'org-noter--pretty-print-location-hook - (org-noter--session-doc-mode session) location))) + 'org-noter--pretty-print-location-hook location))) ;; TODO: Documentation (defun org-noter--get-containing-element (&optional include-root) From 97d17e6dd138b8e42b771d1ca7b79c7b4061fe83 Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 11 Mar 2022 12:33:43 +0700 Subject: [PATCH 123/453] (add) Allow bracket link as a document property This link is automatically opened. --- org-noter-core.el | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index f022497..f7e20f9 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -495,26 +495,46 @@ Otherwise return the maximum value for point." (prop-value) (t ,variable))))) +(defun org-noter-parse-link (s) + (pcase (with-temp-buffer + (let ((org-inhibit-startup nil)) + (insert s) + (org-mode) + (goto-char (point-min)) + (org-element-link-parser))) + (`nil nil) + (link link))) + (defun org-noter--create-session (ast document-property-value notes-file-path) (let* ((raw-value-not-empty (> (length (org-element-property :raw-value ast)) 0)) + (link-p (string-match-p org-bracket-link-regexp document-property-value)) (display-name (if raw-value-not-empty (org-element-property :raw-value ast) - (file-name-nondirectory document-property-value))) - (frame-name (format "Emacs Org-noter - %s" display-name)) + (if link-p + document-property-value + (file-name-nondirectory document-property-value)))) - (document (find-file-noselect document-property-value)) - (document-path (expand-file-name document-property-value)) - (document-major-mode (buffer-local-value 'major-mode document)) + (frame-name (format "Emacs Org-noter - %s" display-name)) + (document (if link-p + (progn (org-link-open-from-string document-property-value) + (current-buffer)) + (find-file-noselect document-property-value))) + (document-major-mode (if link-p + (org-element-property + :type (org-noter-parse-link document-property-value)) + (buffer-local-value 'major-mode document))) (document-buffer-name (generate-new-buffer-name (concat (unless raw-value-not-empty "Org-noter: ") display-name))) (document-buffer document) (notes-buffer - (if org-noter-use-indirect-buffer - (make-indirect-buffer - (or (buffer-base-buffer) (current-buffer)) - (generate-new-buffer-name (concat "Notes of " display-name)) t) - (current-buffer))) + (progn (when (and org-window-config-before-follow-link link-p) + (set-window-configuration org-window-config-before-follow-link)) + (if org-noter-use-indirect-buffer + (make-indirect-buffer + (or (buffer-base-buffer) (current-buffer)) + (generate-new-buffer-name (concat "Notes of " display-name)) t) + (current-buffer)))) (session (make-org-noter--session From 33ac81199f71ea8a8b4fef7d1e521dc05c74019e Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 11 Mar 2022 12:38:12 +0700 Subject: [PATCH 124/453] (add) Allow link as property of note/root --- org-noter-core.el | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index f7e20f9..c73f3c2 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -647,7 +647,8 @@ If nil, the session used will be `org-noter--session'." ;; the specified property (let ((notes-buffer (aref info 0)) (wanted-prop (aref info 1))) - (unless (and (buffer-live-p notes-buffer) (stringp wanted-prop) + (unless (and (buffer-live-p notes-buffer) (or (stringp wanted-prop) + (eq 'link (org-element-type wanted-prop))) (eq (buffer-local-value 'major-mode notes-buffer) 'org-mode)) (error "Error parsing root with invalid arguments")) @@ -1524,7 +1525,8 @@ relative to." (throw 'break t))))))) (defsubst org-noter--check-doc-prop (doc-prop) - (and doc-prop (not (file-directory-p doc-prop)) (file-readable-p doc-prop))) + (and doc-prop (or (string-match-p org-link-bracket-re doc-prop) + (and (not (file-directory-p doc-prop)) (file-readable-p doc-prop))))) (defun org-noter--get-or-read-document-property (inherit-prop &optional force-new) (let ((doc-prop (and (not force-new) (or (org-entry-get nil org-noter-property-doc-file inherit-prop) From bf82922832f26c92fa81b47997a9a95e197aa35d Mon Sep 17 00:00:00 2001 From: c1-g Date: Fri, 11 Mar 2022 12:38:29 +0700 Subject: [PATCH 125/453] (fix) Indentation --- org-noter-core.el | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index c73f3c2..6c3ef96 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -532,7 +532,8 @@ Otherwise return the maximum value for point." (set-window-configuration org-window-config-before-follow-link)) (if org-noter-use-indirect-buffer (make-indirect-buffer - (or (buffer-base-buffer) (current-buffer)) + (or (buffer-base-buffer) + (current-buffer)) (generate-new-buffer-name (concat "Notes of " display-name)) t) (current-buffer)))) @@ -1299,7 +1300,7 @@ document property) will be opened." (> (org-noter--get-location-top location) point)) ((eq (aref view 0) 'nov) (> (org-noter--get-location-top location) (+ (* point (- (cdr (aref view 2)) (cdr (aref view 1)))) - (cdr (aref view 1)))))))) + (cdr (aref view 1)))))))) (defun org-noter--relative-position-to-view (location view) (cond From 13b4021fd6e1054455dc6495d068a683e466021d Mon Sep 17 00:00:00 2001 From: c1-g Date: Tue, 15 Mar 2022 09:36:24 +0700 Subject: [PATCH 126/453] (fix) Get document mode from session object --- modules/org-noter-nov.el | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el index c513992..7ec6ca4 100644 --- a/modules/org-noter-nov.el +++ b/modules/org-noter-nov.el @@ -30,13 +30,14 @@ (defun org-noter-nov-approx-location-cons (major-mode &optional precise-info _force-new-ref) - (when (eq major-mode 'nov-mode) - (cons nov-documents-index (if (or (numberp precise-info) - (and (consp precise-info) - (numberp (car precise-info)) - (numberp (cdr precise-info)))) - precise-info - (max 1 (/ (+ (window-start) (window-end nil t)) 2)))))) + (org-noter--with-valid-session + (when (eq (org-noter--session-doc-mode session) 'nov-mode) + (cons nov-documents-index (if (or (numberp precise-info) + (and (consp precise-info) + (numberp (car precise-info)) + (numberp (cdr precise-info)))) + precise-info + (max 1 (/ (+ (window-start) (window-end nil t)) 2))))))) (defun org-noter-nov-setup-handler (major-mode) (when (eq major-mode 'nov-mode) From 4a6d1914e5c694449de9a013ed8cb106b484a044 Mon Sep 17 00:00:00 2001 From: c1-g Date: Tue, 15 Mar 2022 09:36:51 +0700 Subject: [PATCH 127/453] (fix) document-buffer-name is undefined --- modules/org-noter-nov.el | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el index 7ec6ca4..6b5e619 100644 --- a/modules/org-noter-nov.el +++ b/modules/org-noter-nov.el @@ -41,7 +41,6 @@ (defun org-noter-nov-setup-handler (major-mode) (when (eq major-mode 'nov-mode) - (rename-buffer document-buffer-name) (advice-add 'nov-render-document :after 'org-noter--nov-scroll-handler) (add-hook 'window-scroll-functions 'org-noter--nov-scroll-handler nil t) t)) From 00d06e05586961b389377e6dac783236d7d2bc20 Mon Sep 17 00:00:00 2001 From: c1-g Date: Wed, 16 Mar 2022 15:15:58 +0700 Subject: [PATCH 128/453] (fix) Properties should be string --- modules/org-noter-djvu.el | 6 +++--- modules/org-noter-nov.el | 10 +++++----- modules/org-noter-pdf.el | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/org-noter-djvu.el b/modules/org-noter-djvu.el index ea3d6ea..35156c7 100644 --- a/modules/org-noter-djvu.el +++ b/modules/org-noter-djvu.el @@ -27,9 +27,9 @@ (defun org-noter-djvu--pretty-print-location (location) (org-noter--with-valid-session (when (eq (org-noter--session-doc-mode session) 'djvu-read-mode) - (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 0)) - (car location) - location)))) + (format "%s" (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 0)) + (car location) + location))))) (defun org-noter-djvu-approx-location-cons (major-mode &optional precise-info _force-new-ref) (cons djvu-doc-page (if (or (numberp precise-info) diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el index 6b5e619..35a0e97 100644 --- a/modules/org-noter-nov.el +++ b/modules/org-noter-nov.el @@ -48,9 +48,9 @@ (defun org-noter-nov--pretty-print-location (location) (org-noter--with-valid-session (when (eq (org-noter--session-doc-mode session) 'nov-mode) - (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 1)) - (org-noter--get-location-page location) - location)))) + (format "%s" (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 1)) + (org-noter--get-location-page location) + location))))) (defun org-noter-nov--get-precise-info (major-mode) (when (eq major-mode 'nov-mode) @@ -73,8 +73,8 @@ (defun org-noter-nov--get-current-view (mode) (when (eq mode 'nov-mode) (vector 'nov - (org-noter--doc-approx-location-cons (window-start)) - (org-noter--doc-approx-location-cons (window-end nil t))))) + (org-noter-nov-approx-location-cons mode (window-start)) + (org-noter-nov-approx-location-cons mode (window-end nil t))))) (defun org-noter-nov--get-selected-text (mode) (when (and (eq mode 'nov-mode) (region-active-p)) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 8686528..f705ca8 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -48,9 +48,9 @@ (defun org-noter-pdf--pretty-print-location (location) (org-noter--with-valid-session (when (memq (org-noter--session-doc-mode session) '(doc-view-mode pdf-view-mode)) - (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 0)) - (car location) - location)))) + (format "%s" (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 0)) + (car location) + location))))) (defun org-noter-pdf--get-precise-info (major-mode) (when (eq major-mode 'pdf-view-mode) From deab6f2751e099f5e316e5db5735b251345d3bee Mon Sep 17 00:00:00 2001 From: c1-g Date: Wed, 16 Mar 2022 15:20:40 +0700 Subject: [PATCH 129/453] (add) Also takes URL --- org-noter-core.el | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/org-noter-core.el b/org-noter-core.el index 535b117..a10682a 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -470,6 +470,25 @@ major modes uses the `buffer-file-name' variable." (defconst org-noter--id-text-property 'org-noter-session-id "Text property used to mark the headings with open sessions.") +(defvar org-noter--url-regexp + (concat + "\\b\\(\\(www\\.\\|\\(s?https?\\|ftp\\|file\\|gopher\\|" + "nntp\\|news\\|telnet\\|wais\\|mailto\\|info\\):\\)" + "\\(//[-a-z0-9_.]+:[0-9]*\\)?" + (let ((chars "-a-z0-9_=#$@~%&*+\\/[:word:]") + (punct "!?:;.,")) + (concat + "\\(?:" + ;; Match paired parentheses, e.g. in Wikipedia URLs: + ;; http://thread.gmane.org/47B4E3B2.3050402@gmail.com + "[" chars punct "]+" "(" "[" chars punct "]+" ")" + "\\(?:" "[" chars punct "]+" "[" chars "]" "\\)?" + "\\|" + "[" chars punct "]+" "[" chars "]" + "\\)")) + "\\)") + "Regular expression that matches URLs.") + ;; -------------------------------------------------------------------------------- ;;; Utility functions @@ -507,7 +526,8 @@ Otherwise return the maximum value for point." (defun org-noter--create-session (ast document-property-value notes-file-path) (let* ((raw-value-not-empty (> (length (org-element-property :raw-value ast)) 0)) - (link-p (string-match-p org-bracket-link-regexp document-property-value)) + (link-p (or (string-match-p org-bracket-link-regexp document-property-value) + (string-match-p org-noter--url-regexp document-property-value))) (display-name (if raw-value-not-empty (org-element-property :raw-value ast) (if link-p From 8ed82443b5b542cc3729335b062ecc451a0a1d1e Mon Sep 17 00:00:00 2001 From: c1-g Date: Wed, 16 Mar 2022 15:24:57 +0700 Subject: [PATCH 130/453] (minor) Pass raw property as mode --- org-noter-core.el | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index a10682a..2a4ba78 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -540,8 +540,7 @@ Otherwise return the maximum value for point." (current-buffer)) (find-file-noselect document-property-value))) (document-major-mode (if link-p - (org-element-property - :type (org-noter-parse-link document-property-value)) + document-property-value (buffer-local-value 'major-mode document))) (document-buffer-name (generate-new-buffer-name (concat (unless raw-value-not-empty "Org-noter: ") display-name))) From c2b3cd791fcf1e1d9f5d3a36060dd75ba704fe7f Mon Sep 17 00:00:00 2001 From: c1-g Date: Wed, 16 Mar 2022 15:25:34 +0700 Subject: [PATCH 131/453] (minor) Use org-noter--get-containing-element instead org-noter--get-containing-element is made to support other type of element. --- org-noter.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org-noter.el b/org-noter.el index 2441ff9..4002971 100644 --- a/org-noter.el +++ b/org-noter.el @@ -123,7 +123,7 @@ notes file, even if it finds one." (if session (let* ((org-noter--session session) (location (org-noter--parse-location-property - (org-noter--get-containing-heading)))) + (org-noter--get-containing-element)))) (org-noter--setup-windows session) (when location (org-noter--doc-goto-location location)) (select-frame-set-input-focus (org-noter--session-frame session))) From cc52f392315ad63410fa6956fe9e19980b4eaa8e Mon Sep 17 00:00:00 2001 From: c1-g Date: Wed, 16 Mar 2022 15:28:17 +0700 Subject: [PATCH 132/453] (add) Also pass raw property to handler --- org-noter-core.el | 1 + 1 file changed, 1 insertion(+) diff --git a/org-noter-core.el b/org-noter-core.el index 2a4ba78..e43ce78 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -591,6 +591,7 @@ Otherwise return the maximum value for point." (with-current-buffer document-buffer (or (run-hook-with-args-until-success 'org-noter-set-up-document-handler document-major-mode) + (run-hook-with-args-until-success 'org-noter-set-up-document-handler document-property-value) (error "This document handler is not supported :/")) (org-noter-doc-mode 1) From c943698a693bba3338733b0155cc127bc3c5c46d Mon Sep 17 00:00:00 2001 From: c1-g Date: Wed, 16 Mar 2022 15:29:10 +0700 Subject: [PATCH 133/453] (chore) Remove unused function org-noter--doc-approx-location-cons has been moved to each module as: org-noter-nov-approx-location-cons org-noter-pdf-approx-location-cons org-noter-djvu-approx-location-cons --- org-noter-core.el | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index e43ce78..79462ab 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -956,16 +956,6 @@ properties, by a margin of NEWLINES-NUMBER." (when (and (stringp property) (> (length property) 0)) (ignore-errors (string-to-number property))))) -(defun org-noter--doc-approx-location-cons (&optional precise-info) - (cond - ((eq major-mode 'djvu-read-mode) - (cons djvu-doc-page (if (or (numberp precise-info) - (and (consp precise-info) - (numberp (car precise-info)) - (numberp (cdr precise-info)))) - precise-info - (max 1 (/ (+ (window-start) (window-end nil t)) 2))))))) - (defun org-noter--doc-approx-location (&optional major-mode precise-info force-new-ref) "TODO" (let ((window (if (org-noter--valid-session org-noter--session) From d1b48811572cf2817b6212af6263c5ff1436f322 Mon Sep 17 00:00:00 2001 From: c1-g Date: Wed, 16 Mar 2022 15:32:10 +0700 Subject: [PATCH 134/453] (add) Allow url as document property --- org-noter-core.el | 1 + 1 file changed, 1 insertion(+) diff --git a/org-noter-core.el b/org-noter-core.el index 79462ab..58e546f 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1537,6 +1537,7 @@ relative to." (defsubst org-noter--check-doc-prop (doc-prop) (and doc-prop (or (string-match-p org-link-bracket-re doc-prop) + (string-match-p org-noter--url-regexp doc-prop) (and (not (file-directory-p doc-prop)) (file-readable-p doc-prop))))) (defun org-noter--get-or-read-document-property (inherit-prop &optional force-new) From 20abdc68dcc54acfd34f9ffb2d4ecda5c66873bd Mon Sep 17 00:00:00 2001 From: c1-g Date: Thu, 17 Mar 2022 15:01:54 +0700 Subject: [PATCH 135/453] (add) org-noter-open-document-functions Functions that gives a buffer when passed with a document property. Used by org-noter--create-session when creating a new session. --- org-noter-core.el | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 58e546f..cdc31d3 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -415,6 +415,12 @@ major modes uses the `buffer-file-name' variable." :group 'org-noter :type 'hook) +(defcustom org-noter-open-document-functions nil + "Functions that gives a buffer when passed with a document property. +Used by `org-noter--create-session' when creating a new session." + :group 'org-noter + :type 'hook) + ;; -------------------------------------------------------------------------------- ;;; Private variables or constants (cl-defstruct org-noter--session @@ -535,10 +541,11 @@ Otherwise return the maximum value for point." (file-name-nondirectory document-property-value)))) (frame-name (format "Emacs Org-noter - %s" display-name)) - (document (if link-p - (progn (org-link-open-from-string document-property-value) - (current-buffer)) - (find-file-noselect document-property-value))) + (document (or (run-hook-with-args-until-success 'org-noter-open-document-functions document-property-value) + (if link-p + (progn (org-link-open-from-string document-property-value) + (current-buffer)) + (find-file-noselect document-property-value)))) (document-major-mode (if link-p document-property-value (buffer-local-value 'major-mode document))) From 6b87987cbaf63d57243c08e5a8e83f1d117b2dc2 Mon Sep 17 00:00:00 2001 From: c1-g Date: Thu, 17 Mar 2022 15:02:52 +0700 Subject: [PATCH 136/453] (fix) Lexical binding returns major-mode as nil --- org-noter-core.el | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index cdc31d3..f4c5969 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -963,17 +963,18 @@ properties, by a margin of NEWLINES-NUMBER." (when (and (stringp property) (> (length property) 0)) (ignore-errors (string-to-number property))))) -(defun org-noter--doc-approx-location (&optional major-mode precise-info force-new-ref) +(defun org-noter--doc-approx-location (&optional mode precise-info force-new-ref) "TODO" (let ((window (if (org-noter--valid-session org-noter--session) (org-noter--get-doc-window) (selected-window)))) (cl-assert window) (with-selected-window window + (setq mode (or mode major-mode)) (or (run-hook-with-args-until-success - 'org-noter--doc-approx-location-hook major-mode + 'org-noter--doc-approx-location-hook mode precise-info force-new-ref) - (error "Unknown document type %s" major-mode))))) + (error "Unknown document type %s" mode))))) (defun org-noter--location-change-advice (&rest _) (org-noter--with-valid-session (org-noter--doc-location-change-handler))) From a5bc9afb9a64b9355c9671543bfe0bb9b74ff27b Mon Sep 17 00:00:00 2001 From: c1-g Date: Thu, 17 Mar 2022 15:03:24 +0700 Subject: [PATCH 137/453] (add) Returns url as document --- other/org-noter-citar.el | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/other/org-noter-citar.el b/other/org-noter-citar.el index d9915b8..de5fb32 100644 --- a/other/org-noter-citar.el +++ b/other/org-noter-citar.el @@ -34,13 +34,16 @@ When there is more than one note files associated with CITE-KEY, have user select one of them." (when (and (stringp cite-key) (string-match org-noter-citar-cite-key-re cite-key)) (let* ((key (match-string 1 cite-key)) + (entries (citar--ensure-entries (list key))) (files (citar-file--files-for-multiple-entries - (citar--ensure-entries (list key)) - (append citar-library-paths citar-notes-paths) nil))) - (cond ((= (length files) 1) - (car files)) - ((> (length files) 1) - (completing-read (format "Which file from %s?: " key) files)))))) + entries + (append citar-library-paths citar-notes-paths) nil)) + (url (list (citar-get-link (car entries)))) + (documents (flatten-list (append files url)))) + (cond ((= (length documents) 1) + (car documents)) + ((> (length documents) 1) + (completing-read (format "Which document from %s?: " key) documents)))))) (defun org-noter-citar-find-key-from-this-file (filename) (let* ((entry-alist (mapcan (lambda (entry) From 4f23f5bcd1a026967bb0b0986242f56b05887c0b Mon Sep 17 00:00:00 2001 From: c1-g Date: Tue, 29 Mar 2022 15:42:16 +0700 Subject: [PATCH 138/453] (fix) Void symbols --- modules/org-noter-djvu.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/org-noter-djvu.el b/modules/org-noter-djvu.el index 35156c7..8da25a8 100644 --- a/modules/org-noter-djvu.el +++ b/modules/org-noter-djvu.el @@ -40,7 +40,7 @@ (max 1 (/ (+ (window-start) (window-end nil t)) 2))))) (defun org-noter-djvu--get-precise-info (major-mode) - (when (eq mode 'djvu-read-mode) + (when (eq major-mode 'djvu-read-mode) (if (region-active-p) (cons (mark) (point)) (while (not (and (eq 'mouse-1 (car event)) @@ -60,7 +60,7 @@ (defun org-noter-djvu--get-current-view (mode) (when (eq mode 'djvu-read-mode) - (vector 'paged (car (org-noter--doc-approx-location-cons))))) + (vector 'paged (car (org-noter-djvu-approx-location-cons mode))))) (defun org-noter-djvu--get-selected-text (mode) (when (and (eq mode 'djvu-read-mode) From ffda6ff4c1ed6151bb2907dd9542621da9b746d2 Mon Sep 17 00:00:00 2001 From: c1-g Date: Tue, 12 Apr 2022 12:12:11 +0700 Subject: [PATCH 139/453] (feat) Handle single window layout scenario Single window means that org-noter doesn't know what document buffer the note file is annotating. --- org-noter-core.el | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index f4c5969..08e9c35 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -563,6 +563,11 @@ Otherwise return the maximum value for point." (generate-new-buffer-name (concat "Notes of " display-name)) t) (current-buffer)))) + (single (eq (or (buffer-base-buffer document-buffer) + document-buffer) + (or (buffer-base-buffer notes-buffer) + notes-buffer))) + (session (make-org-noter--session :id (org-noter--get-new-id) @@ -621,15 +626,16 @@ Otherwise return the maximum value for point." (setq target-location (org-noter--parse-location-property (org-noter--get-containing-element t))))) ;; NOTE(nox): This timer is for preventing reflowing too soon. - (run-with-idle-timer - 0.05 nil - (lambda () - ;; NOTE(ahmed-shariff): setup-window run here to avoid crash when notes buffer not setup in time - (org-noter--setup-windows session) - (with-current-buffer document-buffer - (let ((org-noter--inhibit-location-change-handler t)) - (when target-location (org-noter--doc-goto-location target-location))) - (org-noter--doc-location-change-handler)))))) + (unless single + (run-with-idle-timer + 0.05 nil + (lambda () + ;; NOTE(ahmed-shariff): setup-window run here to avoid crash when notes buffer not setup in time + (org-noter--setup-windows session) + (with-current-buffer document-buffer + (let ((org-noter--inhibit-location-change-handler t)) + (when target-location (org-noter--doc-goto-location target-location))) + (org-noter--doc-location-change-handler))))))) (defun org-noter--valid-session (session) (when session From b8c8914d9a6e325be8309899c4d3f28c379df2cc Mon Sep 17 00:00:00 2001 From: c1-g Date: Tue, 12 Apr 2022 12:14:33 +0700 Subject: [PATCH 140/453] (fix) Calling document mode on a note window --- org-noter-core.el | 1 + 1 file changed, 1 insertion(+) diff --git a/org-noter-core.el b/org-noter-core.el index 08e9c35..f9cdb25 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -611,6 +611,7 @@ Otherwise return the maximum value for point." (add-hook 'kill-buffer-hook 'org-noter--handle-kill-buffer nil t)) (with-current-buffer notes-buffer + (org-noter-doc-mode -1) (org-noter-notes-mode 1) ;; NOTE(nox): This is needed because a session created in an indirect buffer would use the point of ;; the base buffer (as this buffer is indirect to the base!) From 30e80c11a596844c7255b24e5107df5032496e15 Mon Sep 17 00:00:00 2001 From: c1-g Date: Wed, 20 Apr 2022 21:06:08 +0700 Subject: [PATCH 141/453] (minor) Move org-noter-create-skeleton-pdf to its own module --- modules/org-noter-pdf.el | 164 +++++++++++++++++++++++++++++++++++++++ org-noter-core.el | 164 --------------------------------------- 2 files changed, 164 insertions(+), 164 deletions(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index f705ca8..848a56c 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -103,5 +103,169 @@ (pdf-view-active-region-p)) (mapconcat 'identity (pdf-view-active-region-text) ? ))) +(defun org-noter-create-skeleton-pdf (mode) + "Create notes skeleton with the PDF outline or annotations." + (when (eq mode 'pdf-view-mode) + (org-noter--with-valid-session + (let* ((ast (org-noter--parse-root)) + (top-level (or (org-element-property :level ast) 0)) + (options '(("Outline" . (outline)) + ("Annotations" . (annots)) + ("Both" . (outline annots)))) + answer output-data) + (with-current-buffer (org-noter--session-doc-buffer session) + (setq answer (assoc (completing-read "What do you want to import? " options nil t) options)) + + (when (memq 'outline answer) + (dolist (item (pdf-info-outline)) + (let ((type (alist-get 'type item)) + (page (alist-get 'page item)) + (depth (alist-get 'depth item)) + (title (alist-get 'title item)) + (top (alist-get 'top item))) + (when (and (eq type 'goto-dest) (> page 0)) + (push (vector title (cons page top) (1+ depth) nil) output-data))))) + + (when (memq 'annots answer) + (let ((possible-annots (list '("Highlights" . highlight) + '("Underlines" . underline) + '("Squigglies" . squiggly) + '("Text notes" . text) + '("Strikeouts" . strike-out) + '("Links" . link) + '("ALL" . all))) + chosen-annots insert-contents pages-with-links) + (while (> (length possible-annots) 1) + (let* ((chosen-string (completing-read "Which types of annotations do you want? " + possible-annots nil t)) + (chosen-pair (assoc chosen-string possible-annots))) + (cond ((eq (cdr chosen-pair) 'all) + (dolist (annot possible-annots) + (when (and (cdr annot) (not (eq (cdr annot) 'all))) + (push (cdr annot) chosen-annots))) + (setq possible-annots nil)) + ((cdr chosen-pair) + (push (cdr chosen-pair) chosen-annots) + (setq possible-annots (delq chosen-pair possible-annots)) + (when (= 1 (length chosen-annots)) (push '("DONE") possible-annots))) + (t + (setq possible-annots nil))))) + + (setq insert-contents (y-or-n-p "Should we insert the annotations contents? ")) + + (dolist (item (pdf-info-getannots)) + (let* ((type (alist-get 'type item)) + (page (alist-get 'page item)) + (edges (or (org-noter--pdf-tools-edges-to-region (alist-get 'markup-edges item)) + (alist-get 'edges item))) + (top (nth 1 edges)) + (item-subject (alist-get 'subject item)) + (item-contents (alist-get 'contents item)) + name contents) + (when (and (memq type chosen-annots) (> page 0)) + (if (eq type 'link) + (cl-pushnew page pages-with-links) + (setq name (cond ((eq type 'highlight) "Highlight") + ((eq type 'underline) "Underline") + ((eq type 'squiggly) "Squiggly") + ((eq type 'text) "Text note") + ((eq type 'strike-out) "Strikeout"))) + + (when insert-contents + (setq contents (cons (pdf-info-gettext page edges) + (and (or (and item-subject (> (length item-subject) 0)) + (and item-contents (> (length item-contents) 0))) + (concat (or item-subject "") + (if (and item-subject item-contents) "\n" "") + (or item-contents "")))))) + + (push (vector (format "%s on page %d" name page) (cons page top) 'inside contents) + output-data))))) + + (dolist (page pages-with-links) + (let ((links (pdf-info-pagelinks page)) + type) + (dolist (link links) + (setq type (alist-get 'type link)) + (unless (eq type 'goto-dest) ;; NOTE(nox): Ignore internal links + (let* ((edges (alist-get 'edges link)) + (title (alist-get 'title link)) + (top (nth 1 edges)) + (target-page (alist-get 'page link)) + target heading-text) + + (unless (and title (> (length title) 0)) (setq title (pdf-info-gettext page edges))) + + (cond + ((eq type 'uri) + (setq target (alist-get 'uri link) + heading-text (format "Link on page %d: [[%s][%s]]" page target title))) + + ((eq type 'goto-remote) + (setq target (concat "file:" (alist-get 'filename link)) + heading-text (format "Link to document on page %d: [[%s][%s]]" page target title)) + (when target-page + (setq heading-text (concat heading-text (format " (target page: %d)" target-page))))) + + (t (error "Unexpected link type"))) + + (push (vector heading-text (cons page top) 'inside nil) output-data)))))))) + + + (when output-data + (if (memq 'annots answer) + (setq output-data + (sort output-data + (lambda (e1 e2) + (or (not (aref e1 1)) + (and (aref e2 1) + (org-noter--compare-locations '< (aref e1 1) (aref e2 1))))))) + (setq output-data (nreverse output-data))) + + (push (vector "Skeleton" nil 1 nil) output-data))) + + (with-current-buffer (org-noter--session-notes-buffer session) + ;; NOTE(nox): org-with-wide-buffer can't be used because we want to reset the + ;; narrow region to include the new headings + (widen) + (save-excursion + (goto-char (org-element-property :end ast)) + + (let (last-absolute-level + title location relative-level contents + level) + (dolist (data output-data) + (setq title (aref data 0) + location (aref data 1) + relative-level (aref data 2) + contents (aref data 3)) + + (if (symbolp relative-level) + (setq level (1+ last-absolute-level)) + (setq last-absolute-level (+ top-level relative-level) + level last-absolute-level)) + + (org-noter--insert-heading level title) + + (when location + (org-entry-put nil org-noter-property-note-location (org-noter--pretty-print-location location))) + + (when org-noter-doc-property-in-notes + (org-entry-put nil org-noter-property-doc-file (org-noter--session-property-text session)) + (org-entry-put nil org-noter--property-auto-save-last-location "nil")) + + (when (car contents) + (org-noter--insert-heading (1+ level) "Contents") + (insert (car contents))) + (when (cdr contents) + (org-noter--insert-heading (1+ level) "Comment") + (insert (cdr contents))))) + + (setq ast (org-noter--parse-root)) + (org-noter--narrow-to-root ast) + (goto-char (org-element-property :begin ast)) + (outline-hide-subtree) + (org-show-children 2))))))) + (provide 'org-noter-pdf) ;;; org-noter-pdf.el ends here diff --git a/org-noter-core.el b/org-noter-core.el index f9cdb25..8d45e7b 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1867,174 +1867,10 @@ want to kill." (interactive) (org-noter--with-valid-session (pcase (org-noter--session-doc-mode session) - ('pdf-view-mode (org-noter-create-skeleton-pdf)) ('djvu-read-mode (org-noter-create-skeleton-djvu)) ('nov-mode (org-noter-create-skeleton-epub)) (_ (user-error "This command is not supported for %s" (org-noter--session-doc-mode session)))))) -(defun org-noter-create-skeleton-pdf () - "Create notes skeleton with the PDF outline or annotations." - (org-noter--with-valid-session - (let* ((ast (org-noter--parse-root)) - (top-level (or (org-element-property :level ast) 0)) - (options '(("Outline" . (outline)) - ("Annotations" . (annots)) - ("Both" . (outline annots)))) - answer output-data) - (with-current-buffer (org-noter--session-doc-buffer session) - (setq answer (assoc (completing-read "What do you want to import? " options nil t) options)) - - (when (memq 'outline answer) - (dolist (item (pdf-info-outline)) - (let ((type (alist-get 'type item)) - (page (alist-get 'page item)) - (depth (alist-get 'depth item)) - (title (alist-get 'title item)) - (top (alist-get 'top item))) - (when (and (eq type 'goto-dest) (> page 0)) - (push (vector title (cons page top) (1+ depth) nil) output-data))))) - - (when (memq 'annots answer) - (let ((possible-annots (list '("Highlights" . highlight) - '("Underlines" . underline) - '("Squigglies" . squiggly) - '("Text notes" . text) - '("Strikeouts" . strike-out) - '("Links" . link) - '("ALL" . all))) - chosen-annots insert-contents pages-with-links) - (while (> (length possible-annots) 1) - (let* ((chosen-string (completing-read "Which types of annotations do you want? " - possible-annots nil t)) - (chosen-pair (assoc chosen-string possible-annots))) - (cond ((eq (cdr chosen-pair) 'all) - (dolist (annot possible-annots) - (when (and (cdr annot) (not (eq (cdr annot) 'all))) - (push (cdr annot) chosen-annots))) - (setq possible-annots nil)) - ((cdr chosen-pair) - (push (cdr chosen-pair) chosen-annots) - (setq possible-annots (delq chosen-pair possible-annots)) - (when (= 1 (length chosen-annots)) (push '("DONE") possible-annots))) - (t - (setq possible-annots nil))))) - - (setq insert-contents (y-or-n-p "Should we insert the annotations contents? ")) - - (dolist (item (pdf-info-getannots)) - (let* ((type (alist-get 'type item)) - (page (alist-get 'page item)) - (edges (or (org-noter--pdf-tools-edges-to-region (alist-get 'markup-edges item)) - (alist-get 'edges item))) - (top (nth 1 edges)) - (item-subject (alist-get 'subject item)) - (item-contents (alist-get 'contents item)) - name contents) - (when (and (memq type chosen-annots) (> page 0)) - (if (eq type 'link) - (cl-pushnew page pages-with-links) - (setq name (cond ((eq type 'highlight) "Highlight") - ((eq type 'underline) "Underline") - ((eq type 'squiggly) "Squiggly") - ((eq type 'text) "Text note") - ((eq type 'strike-out) "Strikeout"))) - - (when insert-contents - (setq contents (cons (pdf-info-gettext page edges) - (and (or (and item-subject (> (length item-subject) 0)) - (and item-contents (> (length item-contents) 0))) - (concat (or item-subject "") - (if (and item-subject item-contents) "\n" "") - (or item-contents "")))))) - - (push (vector (format "%s on page %d" name page) (cons page top) 'inside contents) - output-data))))) - - (dolist (page pages-with-links) - (let ((links (pdf-info-pagelinks page)) - type) - (dolist (link links) - (setq type (alist-get 'type link)) - (unless (eq type 'goto-dest) ;; NOTE(nox): Ignore internal links - (let* ((edges (alist-get 'edges link)) - (title (alist-get 'title link)) - (top (nth 1 edges)) - (target-page (alist-get 'page link)) - target heading-text) - - (unless (and title (> (length title) 0)) (setq title (pdf-info-gettext page edges))) - - (cond - ((eq type 'uri) - (setq target (alist-get 'uri link) - heading-text (format "Link on page %d: [[%s][%s]]" page target title))) - - ((eq type 'goto-remote) - (setq target (concat "file:" (alist-get 'filename link)) - heading-text (format "Link to document on page %d: [[%s][%s]]" page target title)) - (when target-page - (setq heading-text (concat heading-text (format " (target page: %d)" target-page))))) - - (t (error "Unexpected link type"))) - - (push (vector heading-text (cons page top) 'inside nil) output-data)))))))) - - - (when output-data - (if (memq 'annots answer) - (setq output-data - (sort output-data - (lambda (e1 e2) - (or (not (aref e1 1)) - (and (aref e2 1) - (org-noter--compare-locations '< (aref e1 1) (aref e2 1))))))) - (setq output-data (nreverse output-data))) - - (push (vector "Skeleton" nil 1 nil) output-data))) - - (with-current-buffer (org-noter--session-notes-buffer session) - ;; NOTE(nox): org-with-wide-buffer can't be used because we want to reset the - ;; narrow region to include the new headings - (widen) - (save-excursion - (goto-char (org-element-property :end ast)) - - (let (last-absolute-level - title location relative-level contents - level) - (dolist (data output-data) - (setq title (aref data 0) - location (aref data 1) - relative-level (aref data 2) - contents (aref data 3)) - - (if (symbolp relative-level) - (setq level (1+ last-absolute-level)) - (setq last-absolute-level (+ top-level relative-level) - level last-absolute-level)) - - (org-noter--insert-heading level title) - - (when location - (org-entry-put nil org-noter-property-note-location (org-noter--pretty-print-location location))) - - (when org-noter-doc-property-in-notes - (org-entry-put nil org-noter-property-doc-file (org-noter--session-property-text session)) - (org-entry-put nil org-noter--property-auto-save-last-location "nil")) - - (when (car contents) - (org-noter--insert-heading (1+ level) "Contents") - (insert (car contents))) - (when (cdr contents) - (org-noter--insert-heading (1+ level) "Comment") - (insert (cdr contents))))) - - (setq ast (org-noter--parse-root)) - (org-noter--narrow-to-root ast) - (goto-char (org-element-property :begin ast)) - (outline-hide-subtree) - (org-show-children 2)))))) - (defun org-noter-create-skeleton-djvu () (org-noter--with-valid-session (let* ((ast (org-noter--parse-root)) From 7d4ec1026e83650c728199fdd27c9c318c4a146e Mon Sep 17 00:00:00 2001 From: c1-g Date: Wed, 20 Apr 2022 21:08:43 +0700 Subject: [PATCH 142/453] (minor) Move org-noter-create-skeleton-djvu to its own module --- modules/org-noter-djvu.el | 52 +++++++++++++++++++++++++++++++++++++++ org-noter-core.el | 52 --------------------------------------- 2 files changed, 52 insertions(+), 52 deletions(-) diff --git a/modules/org-noter-djvu.el b/modules/org-noter-djvu.el index 8da25a8..3021f47 100644 --- a/modules/org-noter-djvu.el +++ b/modules/org-noter-djvu.el @@ -67,5 +67,57 @@ (region-active-p)) (buffer-substring-no-properties (mark) (point)))) +(defun org-noter-create-skeleton-djvu () + (when (eq mode 'djvu-read-mode) + (org-noter--with-valid-session + (let* ((ast (org-noter--parse-root)) + (top-level (or (org-element-property :level ast) 0)) + output-data) + (require 'thingatpt) + (with-current-buffer (djvu-ref outline-buf) + (unless (string= (buffer-string) "") + (push (vector "Skeleton" nil 1) output-data) + (save-excursion + (goto-char (point-min)) + (while (not (looking-at "^$")) + (push (vector (string-trim-right (string-trim (thing-at-point 'line t)) " [[:digit:]]+") + (list (string-trim-left (string-trim (thing-at-point 'line t)) ".* ")) + (+ 2 (how-many " " (point-at-bol) (point-at-eol)))) output-data) + (forward-line))))) + + (with-current-buffer (org-noter--session-notes-buffer session) + ;; NOTE(nox): org-with-wide-buffer can't be used because we want to reset the + ;; narrow region to include the new headings + (widen) + (save-excursion + (goto-char (org-element-property :end ast)) + + (let (last-absolute-level + title location relative-level contents + level) + + (dolist (data (nreverse output-data)) + (setq title (aref data 0) + location (aref data 1) + relative-level (aref data 2)) + + (setq last-absolute-level (+ top-level relative-level) + level last-absolute-level) + + (org-noter--insert-heading level title) + + (when location + (org-entry-put nil org-noter-property-note-location (org-noter--pretty-print-location location))) + + (when org-noter-doc-property-in-notes + (org-entry-put nil org-noter-property-doc-file (org-noter--session-property-text session)) + (org-entry-put nil org-noter--property-auto-save-last-location "nil")))) + + (setq ast (org-noter--parse-root)) + (org-noter--narrow-to-root ast) + (goto-char (org-element-property :begin ast)) + (when (org-at-heading-p) (outline-hide-subtree)) + (org-show-children 2))))))) + (provide 'org-noter-djvu) ;;; org-noter-djvu.el ends here diff --git a/org-noter-core.el b/org-noter-core.el index 8d45e7b..7827a82 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1867,61 +1867,9 @@ want to kill." (interactive) (org-noter--with-valid-session (pcase (org-noter--session-doc-mode session) - ('djvu-read-mode (org-noter-create-skeleton-djvu)) ('nov-mode (org-noter-create-skeleton-epub)) (_ (user-error "This command is not supported for %s" (org-noter--session-doc-mode session)))))) -(defun org-noter-create-skeleton-djvu () - (org-noter--with-valid-session - (let* ((ast (org-noter--parse-root)) - (top-level (or (org-element-property :level ast) 0)) - output-data) - (require 'thingatpt) - (with-current-buffer (djvu-ref outline-buf) - (unless (string= (buffer-string) "") - (push (vector "Skeleton" nil 1) output-data) - (save-excursion - (goto-char (point-min)) - (while (not (looking-at "^$")) - (push (vector (string-trim-right (string-trim (thing-at-point 'line t)) " [[:digit:]]+") - (list (string-trim-left (string-trim (thing-at-point 'line t)) ".* ")) - (+ 2 (how-many " " (point-at-bol) (point-at-eol)))) output-data) - (forward-line))))) - - (with-current-buffer (org-noter--session-notes-buffer session) - ;; NOTE(nox): org-with-wide-buffer can't be used because we want to reset the - ;; narrow region to include the new headings - (widen) - (save-excursion - (goto-char (org-element-property :end ast)) - - (let (last-absolute-level - title location relative-level contents - level) - - (dolist (data (nreverse output-data)) - (setq title (aref data 0) - location (aref data 1) - relative-level (aref data 2)) - - (setq last-absolute-level (+ top-level relative-level) - level last-absolute-level) - - (org-noter--insert-heading level title) - - (when location - (org-entry-put nil org-noter-property-note-location (org-noter--pretty-print-location location))) - - (when org-noter-doc-property-in-notes - (org-entry-put nil org-noter-property-doc-file (org-noter--session-property-text session)) - (org-entry-put nil org-noter--property-auto-save-last-location "nil")))) - - (setq ast (org-noter--parse-root)) - (org-noter--narrow-to-root ast) - (goto-char (org-element-property :begin ast)) - (when (org-at-heading-p) (outline-hide-subtree)) - (org-show-children 2)))))) - ;; Shamelessly stolen code from Yuchen Li. ;; This code is originally from org-noter-plus package. ;; At https://github.com/yuchen-lea/org-noter-plus From e55cfd50372a3177af87a6f00e82cfef68e2b447 Mon Sep 17 00:00:00 2001 From: c1-g Date: Wed, 20 Apr 2022 21:10:09 +0700 Subject: [PATCH 143/453] (minor) Move org-noter-create-skeleton-epub to its own module --- modules/org-noter-nov.el | 80 ++++++++++++++++++++++++++++++++++++++ org-noter-core.el | 84 ---------------------------------------- 2 files changed, 80 insertions(+), 84 deletions(-) diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el index 35a0e97..682dbf0 100644 --- a/modules/org-noter-nov.el +++ b/modules/org-noter-nov.el @@ -81,5 +81,85 @@ (buffer-substring-no-properties (mark) (point)))) +;; Shamelessly stolen code from Yuchen Li. +;; This code is originally from org-noter-plus package. +;; At https://github.com/yuchen-lea/org-noter-plus + +(defun org-noter--handle-nov-toc-item (ol depth) + (mapcar (lambda (li) + (mapcar (lambda (a-or-ol) + (pcase-exhaustive (dom-tag a-or-ol) + ('a + (vector :depth depth + :title (dom-text a-or-ol) + :href (esxml-node-attribute 'href a-or-ol))) + ('ol + (org-noter--handle-nov-toc-item a-or-ol + (1+ depth))))) + (dom-children li))) + (dom-children ol))) + +(defun org-noter-create-skeleton-epub () + "Epub outline with nov link." + (require 'esxml) + (require 'nov) + (require 'dom) + (org-noter--with-valid-session + (let* ((ast (org-noter--parse-root)) + (top-level (or (org-element-property :level ast) 0)) + output-data) + (with-current-buffer (org-noter--session-doc-buffer session) + (let* ((toc-path (cdr (aref nov-documents 0))) + (toc-tree (with-temp-buffer + (insert (nov-ncx-to-html toc-path)) + (replace-regexp "\n" + "" + nil + (point-min) + (point-max)) + (libxml-parse-html-region (point-min) + (point-max)))) + (origin-index nov-documents-index) + (origin-point (point))) + (dolist (item + (nreverse (flatten-tree (org-noter--handle-nov-toc-item toc-tree 1)))) + (let ((relative-level (aref item 1)) + (title (aref item 3)) + (url (aref item 5))) + (apply 'nov-visit-relative-file + (nov-url-filename-and-target url)) + (when (not (integerp nov-documents-index)) + (setq nov-documents-index 0)) + (push (vector title (list nov-documents-index (point)) relative-level) output-data))) + (push (vector "Skeleton" (list 0) 1) output-data) + + (nov-goto-document origin-index) + (goto-char origin-point))) + (save-excursion + (goto-char (org-element-property :end ast)) + (with-current-buffer (org-noter--session-notes-buffer session) + (dolist (data output-data) + (setq title (aref data 0) + location (aref data 1) + relative-level (aref data 2)) + + (setq last-absolute-level (+ top-level relative-level) + level last-absolute-level) + + (org-noter--insert-heading level title) + + (when location + (org-entry-put nil org-noter-property-note-location (org-noter--pretty-print-location location))) + + (when org-noter-doc-property-in-notes + (org-entry-put nil org-noter-property-doc-file (org-noter--session-property-text session)) + (org-entry-put nil org-noter--property-auto-save-last-location "nil"))) + (setq ast (org-noter--parse-root)) + (org-noter--narrow-to-root ast) + (goto-char (org-element-property :begin ast)) + (outline-hide-subtree) + (org-show-children 2)))))) + + (provide 'org-noter-nov) ;;; org-noter-nov.el ends here diff --git a/org-noter-core.el b/org-noter-core.el index 7827a82..de65153 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1866,90 +1866,6 @@ want to kill." "Create notes skeleton based on the outline of the document." (interactive) (org-noter--with-valid-session - (pcase (org-noter--session-doc-mode session) - ('nov-mode (org-noter-create-skeleton-epub)) - (_ (user-error "This command is not supported for %s" (org-noter--session-doc-mode session)))))) - -;; Shamelessly stolen code from Yuchen Li. -;; This code is originally from org-noter-plus package. -;; At https://github.com/yuchen-lea/org-noter-plus - -(defun org-noter--handle-nov-toc-item (ol depth) - (mapcar (lambda (li) - (mapcar (lambda (a-or-ol) - (pcase-exhaustive (dom-tag a-or-ol) - ('a - (vector :depth depth - :title (dom-text a-or-ol) - :href (esxml-node-attribute 'href a-or-ol))) - ('ol - (org-noter--handle-nov-toc-item a-or-ol - (1+ depth))))) - (dom-children li))) - (dom-children ol))) - -(defun org-noter-create-skeleton-epub () - "Epub outline with nov link." - (require 'esxml) - (require 'nov) - (require 'dom) - (org-noter--with-valid-session - (let* ((ast (org-noter--parse-root)) - (top-level (or (org-element-property :level ast) 0)) - output-data) - (with-current-buffer (org-noter--session-doc-buffer session) - (let* ((toc-path (cdr (aref nov-documents 0))) - (toc-tree (with-temp-buffer - (insert (nov-ncx-to-html toc-path)) - (replace-regexp "\n" - "" - nil - (point-min) - (point-max)) - (libxml-parse-html-region (point-min) - (point-max)))) - (origin-index nov-documents-index) - (origin-point (point))) - (dolist (item - (nreverse (flatten-tree (org-noter--handle-nov-toc-item toc-tree 1)))) - (let ((relative-level (aref item 1)) - (title (aref item 3)) - (url (aref item 5))) - (apply 'nov-visit-relative-file - (nov-url-filename-and-target url)) - (when (not (integerp nov-documents-index)) - (setq nov-documents-index 0)) - (push (vector title (list nov-documents-index (point)) relative-level) output-data))) - (push (vector "Skeleton" (list 0) 1) output-data) - - (nov-goto-document origin-index) - (goto-char origin-point))) - (save-excursion - (goto-char (org-element-property :end ast)) - (with-current-buffer (org-noter--session-notes-buffer session) - (dolist (data output-data) - (setq title (aref data 0) - location (aref data 1) - relative-level (aref data 2)) - - (setq last-absolute-level (+ top-level relative-level) - level last-absolute-level) - - (org-noter--insert-heading level title) - - (when location - (org-entry-put nil org-noter-property-note-location (org-noter--pretty-print-location location))) - - (when org-noter-doc-property-in-notes - (org-entry-put nil org-noter-property-doc-file (org-noter--session-property-text session)) - (org-entry-put nil org-noter--property-auto-save-last-location "nil"))) - (setq ast (org-noter--parse-root)) - (org-noter--narrow-to-root ast) - (goto-char (org-element-property :begin ast)) - (outline-hide-subtree) - (org-show-children 2)))))) - - (defun org-noter-insert-note (&optional precise-info note-title) "Insert note associated with the current location. From 846f54af51f601eebe368aa256dc877de1870e6f Mon Sep 17 00:00:00 2001 From: c1-g Date: Wed, 20 Apr 2022 21:21:34 +0700 Subject: [PATCH 144/453] (add) org-noter-create-skeleton-functions Function that inserts a tree of headlines according to the outline of the document. The functions will be given a major mode of the document and must return a non-nil value when the outline is created. --- org-noter-core.el | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/org-noter-core.el b/org-noter-core.el index de65153..1358553 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -415,6 +415,16 @@ major modes uses the `buffer-file-name' variable." :group 'org-noter :type 'hook) +(defcustom org-noter-create-skeleton-functions nil + "Function that inserts a tree of headlines according to the outline of the document. + +The functions will be given a major mode of the document and must +return a non-nil value when the outline is created. + +Used by `org-noter-create-skeleton'." + :group 'org-noter + :type 'hook) + (defcustom org-noter-open-document-functions nil "Functions that gives a buffer when passed with a document property. Used by `org-noter--create-session' when creating a new session." @@ -1866,6 +1876,10 @@ want to kill." "Create notes skeleton based on the outline of the document." (interactive) (org-noter--with-valid-session + (or (run-hook-with-args-until-success 'org-noter-create-skeleton-functions + (org-noter--session-doc-mode session)) + (user-error "This command is not supported for %s" + (org-noter--session-doc-mode session))))) (defun org-noter-insert-note (&optional precise-info note-title) "Insert note associated with the current location. From 71129de9e54d1f04a9d675f2032be7fb1416179d Mon Sep 17 00:00:00 2001 From: c1-g Date: Wed, 20 Apr 2022 21:23:24 +0700 Subject: [PATCH 145/453] (fix) Not recognizing location in file-level property drawer --- org-noter-core.el | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/org-noter-core.el b/org-noter-core.el index 1358553..5427644 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -292,7 +292,8 @@ the functions in this list." :group 'org-noter :type 'hook) -(defcustom org-noter--get-containing-element-hook '(org-noter--get-containing-heading) +(defcustom org-noter--get-containing-element-hook '(org-noter--get-containing-heading + org-noter--get-containing-property-drawer) "The list of functions that will be called by `org-noter--get-containing-element' to get the org element of the note at point." @@ -1065,6 +1066,19 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (setq previous heading))))))))) +(defun org-noter--get-containing-property-drawer (&optional include-root) + "Get smallest containing heading that encloses the point and has location property. +If the point isn't inside any heading with location property, return the outer heading. +When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." + (org-noter--with-valid-session + (org-with-point-at (point-min) + (when (org-before-first-heading-p) + (let ((prop (org-entry-get nil org-noter-property-note-location)) + (at-root (equal (org-noter--session-id session) + (get-text-property (point) org-noter--id-text-property)))) + (when (and (org-noter--check-location-property prop) (or include-root (not at-root))) + prop)))))) + (defun org-noter--doc-get-page-slice () "Return (slice-top . slice-height)." (let* ((slice (or (image-mode-window-get 'slice) '(0 0 1 1))) From f8ba393bbbe0e8094f251ee6aa08288e712295fd Mon Sep 17 00:00:00 2001 From: c1-g Date: Wed, 20 Apr 2022 21:24:54 +0700 Subject: [PATCH 146/453] (fix) Wrong number of arguments in org-noter--doc-approx-location --- org-noter-core.el | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 5427644..050d038 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -981,18 +981,16 @@ properties, by a margin of NEWLINES-NUMBER." (when (and (stringp property) (> (length property) 0)) (ignore-errors (string-to-number property))))) -(defun org-noter--doc-approx-location (&optional mode precise-info force-new-ref) +(defun org-noter--doc-approx-location (&optional precise-info force-new-ref) "TODO" (let ((window (if (org-noter--valid-session org-noter--session) (org-noter--get-doc-window) (selected-window)))) (cl-assert window) (with-selected-window window - (setq mode (or mode major-mode)) (or (run-hook-with-args-until-success - 'org-noter--doc-approx-location-hook mode - precise-info force-new-ref) - (error "Unknown document type %s" mode))))) + 'org-noter--doc-approx-location-hook major-mode precise-info force-new-ref) + (error "Unknown document type %s" major-mode))))) (defun org-noter--location-change-advice (&rest _) (org-noter--with-valid-session (org-noter--doc-location-change-handler))) From 539bfb4ad0a93f1c3f1fd9c7df303f1c3448f7a3 Mon Sep 17 00:00:00 2001 From: c1-g Date: Wed, 20 Apr 2022 21:25:40 +0700 Subject: [PATCH 147/453] (chore) Remove unused variable --- org-noter-core.el | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 050d038..bfef55d 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1115,8 +1115,7 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (defun org-noter--get-precise-info () (org-noter--with-valid-session (let ((window (org-noter--get-doc-window)) - (mode (org-noter--session-doc-mode session)) - event) + (mode (org-noter--session-doc-mode session))) (with-selected-window window (run-hook-with-args-until-success 'org-noter--get-precise-info-hook mode))))) From 0ab9795937acef8a9bef88e9c793101b39d039f2 Mon Sep 17 00:00:00 2001 From: c1-g Date: Wed, 20 Apr 2022 21:26:13 +0700 Subject: [PATCH 148/453] (fix) Not parsing the document prop before checking if it's root This happens when, for example, the document property is a citekey like this: "cite:&johndoe2020" and org-noter will parse this to get the file name of the document and store this: "/path/to/johndoe2020.pdf" as the file that we wanted. Without parsing, org-noter will be matching "cite:&johndoe2020" to "/path/to/johndoe2020.pdf" and give false results. --- org-noter-core.el | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index bfef55d..4aace88 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -702,13 +702,17 @@ If nil, the session used will be `org-noter--session'." (org-with-wide-buffer (catch 'break (while t - (when (string= (or (org-noter--get-or-read-document-property t) - (cadar (org-collect-keywords (list org-noter-property-doc-file)))) - wanted-prop) - (setq root-pos (copy-marker (if (and org-noter-prefer-root-as-file-level - (eq 'property-drawer (org-element-type (org-element-at-point 1)))) - (point-min) - (point))))) + (let ((document-property (or (org-entry-get nil org-noter-property-doc-file t) + (cadar (org-collect-keywords (list org-noter-property-doc-file)))))) + (when (string= (or (run-hook-with-args-until-success 'org-noter-parse-document-property-hook document-property) + document-property) + wanted-prop) + (setq root-pos (copy-marker (if (and org-noter-prefer-root-as-file-level + (save-excursion + (goto-char (point-min)) + (eq 'property-drawer (org-element-type (org-element-at-point))))) + (point-min) + (point)))))) (unless (org-up-heading-safe) (throw 'break t)))))))) ((org-noter--valid-session session) From db8eb5b8b5af1232809c900a4ec643f8ee791abb Mon Sep 17 00:00:00 2001 From: c1-g Date: Thu, 21 Apr 2022 13:46:44 +0700 Subject: [PATCH 149/453] (feat) Completely separate the modules from core file --- modules/org-noter-djvu.el | 15 +++++++++ modules/org-noter-nov.el | 17 +++++++++- modules/org-noter-pdf.el | 19 +++++++++++ org-noter-core.el | 68 +++++++++++++++------------------------ 4 files changed, 76 insertions(+), 43 deletions(-) diff --git a/modules/org-noter-djvu.el b/modules/org-noter-djvu.el index 3021f47..82bef8e 100644 --- a/modules/org-noter-djvu.el +++ b/modules/org-noter-djvu.el @@ -23,6 +23,7 @@ ;; ;;; Code: +(require 'org-noter) (defun org-noter-djvu--pretty-print-location (location) (org-noter--with-valid-session @@ -31,6 +32,8 @@ (car location) location))))) +(add-to-list 'org-noter--pretty-print-location-hook #'org-noter-djvu--pretty-print-location) + (defun org-noter-djvu-approx-location-cons (major-mode &optional precise-info _force-new-ref) (cons djvu-doc-page (if (or (numberp precise-info) (and (consp precise-info) @@ -39,6 +42,8 @@ precise-info (max 1 (/ (+ (window-start) (window-end nil t)) 2))))) +(add-to-list 'org-noter--doc-approx-location-hook #'org-noter-djvu-approx-location-cons) + (defun org-noter-djvu--get-precise-info (major-mode) (when (eq major-mode 'djvu-read-mode) (if (region-active-p) @@ -48,25 +53,35 @@ (setq event (read-event "Click where you want the start of the note to be!"))) (posn-point (event-start event))))) +(add-to-list 'org-noter--get-precise-info-hook #'org-noter-djvu--get-precise-info) + (defun org-noter-djvu-setup-handler (major-mode) (when (eq major-mode 'djvu-read-mode) (advice-add 'djvu-init-page :after 'org-noter--location-change-advice) t)) +(add-to-list 'org-noter-set-up-document-hook #'org-noter-djvu-setup-handler) + (defun org-noter-djvu-goto-location (mode location) (when (eq mode 'djvu-read-mode) (djvu-goto-page (car location)) (goto-char (org-noter--get-location-top location)))) +(add-to-list 'org-noter--doc-goto-location-hook #'org-noter-djvu-goto-location) + (defun org-noter-djvu--get-current-view (mode) (when (eq mode 'djvu-read-mode) (vector 'paged (car (org-noter-djvu-approx-location-cons mode))))) +(add-to-list 'org-noter--get-current-view-hook #'org-noter-djvu--get-current-view) + (defun org-noter-djvu--get-selected-text (mode) (when (and (eq mode 'djvu-read-mode) (region-active-p)) (buffer-substring-no-properties (mark) (point)))) +(add-to-list 'org-noter-get-selected-text-hook #'org-noter-djvu--get-selected-text) + (defun org-noter-create-skeleton-djvu () (when (eq mode 'djvu-read-mode) (org-noter--with-valid-session diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el index 682dbf0..e6f6d51 100644 --- a/modules/org-noter-nov.el +++ b/modules/org-noter-nov.el @@ -23,7 +23,7 @@ ;; ;;; Code: - +(require 'org-noter) (defun org-noter-get-buffer-file-name-nov () (bound-and-true-p nov-file-name)) @@ -39,12 +39,16 @@ precise-info (max 1 (/ (+ (window-start) (window-end nil t)) 2))))))) +(add-to-list 'org-noter--doc-approx-location-hook #'org-noter-nov-approx-location-cons) + (defun org-noter-nov-setup-handler (major-mode) (when (eq major-mode 'nov-mode) (advice-add 'nov-render-document :after 'org-noter--nov-scroll-handler) (add-hook 'window-scroll-functions 'org-noter--nov-scroll-handler nil t) t)) +(add-to-list 'org-noter-set-up-document-hook #'org-noter-nov-setup-handler) + (defun org-noter-nov--pretty-print-location (location) (org-noter--with-valid-session (when (eq (org-noter--session-doc-mode session) 'nov-mode) @@ -52,6 +56,9 @@ (org-noter--get-location-page location) location))))) +(add-to-list 'org-noter--pretty-print-location-hook #'org-noter-nov--pretty-print-location) + + (defun org-noter-nov--get-precise-info (major-mode) (when (eq major-mode 'nov-mode) (if (region-active-p) @@ -61,6 +68,8 @@ (setq event (read-event "Click where you want the start of the note to be!"))) (posn-point (event-start event))))) +(add-to-list 'org-noter--get-precise-info-hook #'org-noter-nov--get-precise-info) + (defun org-noter-nov-goto-location (mode location) (when (eq mode 'nov-mode) (setq nov-documents-index (org-noter--get-location-page location)) @@ -70,16 +79,22 @@ ;; everything and would run org-noter--nov-scroll-handler. (recenter))) +(add-to-list 'org-noter--doc-goto-location-hook #'org-noter-nov-goto-location) + (defun org-noter-nov--get-current-view (mode) (when (eq mode 'nov-mode) (vector 'nov (org-noter-nov-approx-location-cons mode (window-start)) (org-noter-nov-approx-location-cons mode (window-end nil t))))) +(add-to-list 'org-noter--get-current-view-hook #'org-noter-nov--get-current-view) + (defun org-noter-nov--get-selected-text (mode) (when (and (eq mode 'nov-mode) (region-active-p)) (buffer-substring-no-properties (mark) (point)))) +(add-to-list 'org-noter-get-selected-text-hook #'org-noter-nov--get-selected-text) + ;; Shamelessly stolen code from Yuchen Li. ;; This code is originally from org-noter-plus package. diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 848a56c..fd67019 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -23,6 +23,7 @@ ;; ;;; Code: +(require 'org-noter) (defun org-noter-pdf-approx-location-cons (major-mode &optional precise-info _force-new-ref) (when (memq major-mode '(doc-view-mode pdf-view-mode)) @@ -31,6 +32,8 @@ (numberp (cadr precise-info))) precise-info 0)))) +(add-to-list 'org-noter--doc-approx-location-hook #'org-noter-pdf-approx-location-cons) + (defun org-noter-pdf-view-setup-handler (major-mode) (when (eq major-mode 'pdf-view-mode) ;; (setq buffer-file-name document-path) @@ -38,6 +41,8 @@ (add-hook 'pdf-view-after-change-page-hook 'org-noter--doc-location-change-handler nil t) t)) +(add-to-list 'org-noter-set-up-document-hook #'org-noter-pdf-view-setup-handler) + (defun org-noter-doc-view-setup-handler (major-mode) (when (eq major-mode 'doc-view-mode) ;; (setq buffer-file-name document-path) @@ -45,6 +50,8 @@ (advice-add 'doc-view-goto-page :after 'org-noter--location-change-advice) t)) +(add-to-list 'org-noter-set-up-document-hook #'org-noter-doc-view-setup-handler) + (defun org-noter-pdf--pretty-print-location (location) (org-noter--with-valid-session (when (memq (org-noter--session-doc-mode session) '(doc-view-mode pdf-view-mode)) @@ -52,6 +59,8 @@ (car location) location))))) +(add-to-list 'org-noter--pretty-print-location-hook #'org-noter-pdf--pretty-print-location) + (defun org-noter-pdf--get-precise-info (major-mode) (when (eq major-mode 'pdf-view-mode) (if (pdf-view-active-region-p) @@ -65,6 +74,8 @@ (org-noter--conv-page-scroll-percentage (+ (window-vscroll) (cdr col-row)) (+ (window-hscroll) (car col-row))))))) +(add-to-list 'org-noter--get-precise-info-hook #'org-noter-pdf--get-precise-info) + (defun org-noter-doc--get-precise-info (major-mode) (when (eq major-mode 'doc-view-mode) (while (not (and (eq 'mouse-1 (car event)) @@ -73,6 +84,8 @@ (org-noter--conv-page-scroll-percentage (+ (window-vscroll) (cdr (posn-col-row (event-start event))))))) +(add-to-list 'org-noter--get-precise-info-hook #'org-noter-doc--get-precise-info) + (defun org-noter-pdf-goto-location (mode location) (when (memq mode '(doc-view-mode pdf-view-mode)) @@ -94,15 +107,21 @@ (image-scroll-up (- (org-noter--conv-page-percentage-scroll top) (window-vscroll)))))) +(add-to-list 'org-noter--doc-goto-location-hook #'org-noter-pdf-goto-location) + (defun org-noter-pdf--get-current-view (mode) (when (memq mode '(doc-view-mode pdf-view-mode)) (vector 'paged (car (org-noter-pdf-approx-location-cons mode))))) +(add-to-list 'org-noter--get-current-view-hook #'org-noter-pdf--get-current-view) + (defun org-noter-pdf--get-selected-text (mode) (when (and (eq mode 'pdf-view-mode) (pdf-view-active-region-p)) (mapconcat 'identity (pdf-view-active-region-text) ? ))) +(add-to-list 'org-noter-get-selected-text-hook #'org-noter-pdf--get-selected-text) + (defun org-noter-create-skeleton-pdf (mode) "Create notes skeleton with the PDF outline or annotations." (when (eq mode 'pdf-view-mode) diff --git a/org-noter-core.el b/org-noter-core.el index 4aace88..4b9bebf 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -40,10 +40,6 @@ (require 'cl-lib) (require 'pdf-tools) -(require 'org-noter-nov) -(require 'org-noter-pdf) -(require 'org-noter-djvu) - (declare-function doc-view-goto-page "doc-view") (declare-function image-display-size "image-mode") (declare-function image-get-display-property "image-mode") @@ -331,19 +327,12 @@ major modes uses the `buffer-file-name' variable." :group 'org-noter :type 'hook) -(defcustom org-noter-set-up-document-handler - '(org-noter-nov-setup-handler - org-noter-pdf-view-setup-handler - org-noter-doc-view-setup-handler - org-noter-djvu-setup-handler) +(defcustom org-noter-set-up-document-hook nil "TODO" :group 'org-noter :type 'hook) -(defcustom org-noter-get-selected-text-hook - '(org-noter-nov--get-selected-text - org-noter-pdf--get-selected-text - org-noter-djvu--get-selected-text) +(defcustom org-noter-get-selected-text-hook nil "TODO" :group 'org-noter :type 'hook) @@ -359,10 +348,7 @@ major modes uses the `buffer-file-name' variable." :group 'org-noter :type 'hook) -(defcustom org-noter--pretty-print-location-hook - '(org-noter-nov--pretty-print-location - org-noter-pdf--pretty-print-location - org-noter-djvu--pretty-print-location) +(defcustom org-noter--pretty-print-location-hook nil "TODO" :group 'org-noter :type 'hook) @@ -372,10 +358,7 @@ major modes uses the `buffer-file-name' variable." :group 'org-noter :type 'hook) -(defcustom org-noter--doc-goto-location-hook - '(org-noter-pdf-goto-location - org-noter-nov-goto-location - org-noter-djvu-goto-location) +(defcustom org-noter--doc-goto-location-hook nil "TODO" :group 'org-noter :type 'hook) @@ -390,28 +373,18 @@ major modes uses the `buffer-file-name' variable." :group 'org-noter :type 'hook) -(defcustom org-noter--get-precise-info-hook - '(org-noter-nov--get-precise-info - org-noter-djvu--get-precise-info - org-noter-pdf--get-precise-info - org-noter-doc--get-precise-info) +(defcustom org-noter--get-precise-info-hook nil "TODO" :group 'org-noter :type 'hook) -(defcustom org-noter--get-current-view-hook - '(org-noter-nov--get-current-view - org-noter-djvu--get-current-view - org-noter-pdf--get-current-view) +(defcustom org-noter--get-current-view-hook nil "TODO" :group 'org-noter :type 'hook) -(defcustom org-noter--doc-approx-location-hook - '(org-noter-nov-approx-location-cons - org-noter-pdf-approx-location-cons - org-noter-djvu-approx-location-cons) +(defcustom org-noter--doc-approx-location-hook nil "TODO" :group 'org-noter :type 'hook) @@ -1021,6 +994,7 @@ properties, by a margin of NEWLINES-NUMBER." (or (run-hook-with-args-until-success 'org-noter--check-location-property-hook property) (let ((value (car (read-from-string property)))) (or (and (consp value) (integerp (car value)) (numberp (cdr value))) + (and (consp value) (integerp (car value)) (integerp (cadr value)) (integerp (cddr value))) (integerp value))))))) (defun org-noter--parse-location-property (arg) @@ -1938,13 +1912,17 @@ defines if the text should be inserted inside the note." (let ((point (point)) (minibuffer-local-completion-map org-noter--completing-read-keymap) - collection default default-begin title selection + collection default default-begin title selection quote-p (empty-lines-number (if org-noter-separate-notes-from-heading 2 1))) (cond ;; NOTE(nox): Both precise and without questions will create new notes ((or precise-info force-new) - (setq default (and selected-text (replace-regexp-in-string "\n" " " selected-text)))) + (setq quote-p (with-temp-buffer + (insert selected-text) + (> (how-many "\n" (point-min)) 2))) + (setq default (and selected-text + (replace-regexp-in-string "\n" " " selected-text)))) (org-noter-insert-note-no-questions) (t (dolist (note-cons (org-noter--view-info-notes view-info)) @@ -1991,8 +1969,9 @@ defines if the text should be inserted inside the note." ;; NOTE(nox): Inserting a new note (let ((reference-element-cons (org-noter--view-info-reference-for-insertion view-info)) level) - (when (zerop (length title)) - (setq title (replace-regexp-in-string (regexp-quote "$p$") (number-to-string (org-noter--get-location-page location)) + (when (or quote-p (zerop (length title))) + (setq title (replace-regexp-in-string (regexp-quote "$p$") + (org-noter--pretty-print-location location) org-noter-default-heading-title))) (if reference-element-cons @@ -2008,14 +1987,19 @@ defines if the text should be inserted inside the note." (setq level (org-element-property :level (cdr reference-element-cons)))) - (goto-char (org-element-map contents 'section - (lambda (section) (org-element-property :end section)) - nil t org-element-all-elements)) - (setq level (1+ (org-element-property :level ast)))) + (goto-char (or (org-element-map contents 'section + (lambda (section) (org-element-property :end section)) + nil t org-element-all-elements) + (point-max)))) + + (setq level (1+ (or (org-element-property :level ast) 0))) ;; NOTE(nox): This is needed to insert in the right place (unless (org-noter--no-heading-p) (outline-show-entry)) (org-noter--insert-heading level title empty-lines-number location) + (when quote-p + (save-excursion + (insert "#+BEGIN_QUOTE\n" selected-text "\n#+END_QUOTE"))) (when (org-noter--session-hide-other session) (org-overview)) (setf (org-noter--session-num-notes-in-view session) From 41b60cc2759e5a7624599608d960409a4d1ca706 Mon Sep 17 00:00:00 2001 From: c1-g Date: Thu, 21 Apr 2022 13:47:20 +0700 Subject: [PATCH 150/453] (minor) Use rx to generate regexp string instead --- other/org-noter-citar.el | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/other/org-noter-citar.el b/other/org-noter-citar.el index de5fb32..000ac1e 100644 --- a/other/org-noter-citar.el +++ b/other/org-noter-citar.el @@ -20,10 +20,33 @@ ;;; Code: (require 'citar) +(require 'org-ref) ;; Regexp stolen from org-roam-bibtex; orb-utils-citekey-re. (defvar org-noter-citar-cite-key-re - "\\(?2:\\(?:Autocite[*s]?\\|Cite\\(?:a\\(?:l\\(?:[pt]\\*\\|[pt]\\)\\|uthor\\*?\\)\\|[pt]\\*\\|[pst]\\)?\\|Notecite\\|P\\(?:arencites?\\|notecite\\)\\|Smartcites?\\|Textcites?\\|autocite[*s]?\\|bibentry\\|cite\\(?:a\\(?:l\\(?:[pt]\\*\\|[pt]\\)\\|uthor\\*?\\)\\|date\\*?\\|num\\|p\\*\\|t\\(?:\\*\\|ext\\|itle\\*?\\)\\|url\\|year\\(?:\\*\\|par\\)?\\|[*pst]\\)?\\|f\\(?:notecite\\|oot\\(?:cite\\(?:s\\|texts?\\)?\\|fullcite\\)\\|ullcite\\)\\|no\\(?:\\(?:te\\)?cite\\)\\|p\\(?:arencite[*s]?\\|notecite\\)\\|s\\(?:martcites?\\|upercites?\\)\\|textcites?\\)\\):\\(?:\\(?1:[.0-:A-Z_a-z-]+\\)\\|[^&]*?&\\(?1:[!#-+./:<>-@^-`{-~[:word:]-]+\\)\\)\\|@\\(?1:[!#-+./:<>-@^-`{-~[:word:]-]+\\)" + (rx + (or + (seq (group-n 2 (regexp + ;; If Org-ref is available, use its types + ;; default to "cite" + (if (boundp 'org-ref-cite-types) + (regexp-opt + (mapcar + (lambda (el) + ;; Org-ref v3 cite type is a list of strings + ;; Org-ref v2 cite type is a plain string + (or (car-safe el) el)) + org-ref-cite-types)) + "cite"))) + ":" + (or + ;; Org-ref v2 style `cite:links' + (group-n 1 (+ (any "a-zA-Z0-9_:.-"))) + ;; Org-ref v3 style `cite:Some&key' + (seq (*? (not "&")) "&" + (group-n 1 (+ (any "!#-+./:<>-@^-`{-~-" word)))))) + ;; Org-cite [cite/@citations] + (seq "@" (group-n 1 (+ (any "!#-+./:<>-@^-`{-~-" word)))))) "Universal regexp to match citations in ROAM_REFS. Supports Org-ref v2 and v3 and Org-cite.") From b69ce2424fe3745e59d49c1331c7a60f6d8e7b21 Mon Sep 17 00:00:00 2001 From: c1-g Date: Sun, 1 May 2022 05:24:49 +0700 Subject: [PATCH 151/453] (minor) Clearer error messages --- org-noter-core.el | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 4b9bebf..96f41de 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1575,9 +1575,19 @@ relative to." (unless (org-noter--check-doc-prop doc-prop) (setq doc-prop (expand-file-name (read-file-name - "Invalid or no document property found. Please specify a document path: " nil nil t))) - (when (or (file-directory-p doc-prop) (not (file-readable-p doc-prop))) (user-error "Invalid file path")) - (when (y-or-n-p "Do you want a relative file name? ") (setq doc-prop (file-relative-name doc-prop)))) + (cond + ((null doc-prop) "No document property found. Please specify a document path: ") + ((file-directory-p doc-prop) + (format "Document property (\"%s\") is a directory. Please specify a document file: " + doc-prop)) + ((not (file-readable-p doc-prop)) + (format "The file specified by the document property \"%s\" is unreadable. Please specify a new document: " + doc-prop))) + nil nil t))) + (when (or (file-directory-p doc-prop) (not (file-readable-p doc-prop))) + (user-error "Invalid file path")) + (when (y-or-n-p "Do you want a relative file name? ") + (setq doc-prop (file-relative-name doc-prop)))) (org-entry-put nil org-noter-property-doc-file doc-prop)) doc-prop)) From d91d4a2a00dde0bd9ea459295eeb70e777e3ef1c Mon Sep 17 00:00:00 2001 From: c1-g Date: Sun, 1 May 2022 05:25:27 +0700 Subject: [PATCH 152/453] =?UTF-8?q?org-noter-set-up-document-handler=20?= =?UTF-8?q?=E2=86=92=20org-noter-set-up-document-hook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For consistency with other hooks. --- org-noter-core.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 96f41de..735d4ea 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -586,8 +586,8 @@ Otherwise return the maximum value for point." (push session org-noter--sessions) (with-current-buffer document-buffer - (or (run-hook-with-args-until-success 'org-noter-set-up-document-handler document-major-mode) - (run-hook-with-args-until-success 'org-noter-set-up-document-handler document-property-value) + (or (run-hook-with-args-until-success 'org-noter-set-up-document-hook document-major-mode) + (run-hook-with-args-until-success 'org-noter-set-up-document-hook document-property-value) (error "This document handler is not supported :/")) (org-noter-doc-mode 1) From 0c0acaae716f66f493b299082e5150d232292e78 Mon Sep 17 00:00:00 2001 From: c1-g Date: Sun, 1 May 2022 05:38:59 +0700 Subject: [PATCH 153/453] (minor) Revert using org-element-cache-map org-element-cache-map is too recent to use. --- org-noter-core.el | 163 +++++++++++++++++++++++----------------------- 1 file changed, 82 insertions(+), 81 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 735d4ea..f1c6660 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1390,87 +1390,88 @@ relative to." (with-current-buffer (or (buffer-base-buffer (org-noter--session-notes-buffer session)) (org-noter--session-notes-buffer session)) - (org-element-cache-map - (lambda (element) - (let ((doc-file (org-noter--doc-file-property element)) - (location (org-noter--parse-location-property element))) - (when (and ignore-until-level (<= (or (org-element-property :level element) 0) ignore-until-level)) - (setq ignore-until-level nil)) - - (cond - (ignore-until-level) ;; NOTE(nox): This heading is ignored, do nothing - - ((and doc-file (not (string= doc-file (org-noter--session-property-text session)))) - (org-noter--view-region-finish current-region-info element) - (setq ignore-until-level (or (org-element-property :level element) 0)) - (when (and preamble new-location - (or (not reference-for-insertion) - (>= (org-element-property :begin element) - (org-element-property :end (cdr reference-for-insertion))))) - (setq reference-for-insertion (cons 'after element)))) - - (location - (let ((relative-position (org-noter--relative-position-to-view location view))) - (cond - ((eq relative-position 'inside) - (push (cons element nil) notes-in-view) - - (org-noter--view-region-add current-region-info regions-in-view element) - - (setq all-after-tipping-point - (and all-after-tipping-point (org-noter--note-after-tipping-point - closest-tipping-point location view)))) - - (t - (when current-region-info - (let ((note-cons-to-change (cond ((eq (aref current-region-info 3) 'regions-in-view) - (car notes-in-view)) - ((eq (aref current-region-info 3) 'closest-notes-regions) - (car closest-notes))))) - (when (< (org-element-property :begin element) - (org-element-property :end (car note-cons-to-change))) - (setcdr note-cons-to-change element)))) - - (let ((eligible-for-before (and closest-tipping-point all-after-tipping-point - (eq relative-position 'before) - (not (= root-pos (org-element-property :begin element)))))) - (cond ((and eligible-for-before - (org-noter--compare-locations '> location closest-notes-location)) - (setq closest-notes (list (cons element nil)) - closest-notes-location location - current-region-info nil - closest-notes-regions nil) - (org-noter--view-region-add current-region-info closest-notes-regions element)) - - ((and eligible-for-before (equal location closest-notes-location)) - (push (cons element nil) closest-notes) - (org-noter--view-region-add current-region-info closest-notes-regions element)) - - (t (org-noter--view-region-finish current-region-info element))))))) - - (when new-location - (setq preamble nil) - (cond ((and (org-noter--compare-locations '<= location new-location) - (or (eq (car reference-for-insertion) 'before) - (org-noter--compare-locations '>= location reference-location))) - (setq reference-for-insertion (cons 'after element) - reference-location location)) - - ((and (eq (car reference-for-insertion) 'after) - (< (org-element-property :begin element) - (org-element-property :end (cdr reference-for-insertion))) - (org-noter--compare-locations '>= location new-location)) - (setq reference-for-insertion (cons 'before element) - reference-location location))))) - - (t - (when (and preamble new-location - (or (not reference-for-insertion) - (>= (org-element-property :begin element) - (org-element-property :end (cdr reference-for-insertion))))) - (setq reference-for-insertion (cons 'after element))))))) - :granularity 'element - :restrict-elements org-noter--note-search-element-type)) + (org-element-map contents org-noter--note-search-element-type + (lambda (element) + (let ((doc-file (org-noter--doc-file-property element)) + (location (org-noter--parse-location-property element))) + (when (and ignore-until-level (<= (org-element-property :level element) ignore-until-level)) + (setq ignore-until-level nil)) + + (cond + (ignore-until-level) ;; NOTE(nox): This heading is ignored, do nothing + + ((and doc-file (not (string= doc-file (org-noter--session-property-text session)))) + (org-noter--view-region-finish current-region-info element) + (setq ignore-until-level (org-element-property :level element)) + (when (and preamble new-location + (or (not reference-for-insertion) + (>= (org-element-property :begin element) + (org-element-property :end (cdr reference-for-insertion))))) + (setq reference-for-insertion (cons 'after element)))) + + (location + (let ((relative-position (org-noter--relative-position-to-view location view))) + (cond + ((eq relative-position 'inside) + (push (cons element nil) notes-in-view) + + (org-noter--view-region-add current-region-info regions-in-view element) + + (setq all-after-tipping-point + (and all-after-tipping-point (org-noter--note-after-tipping-point + closest-tipping-point location view)))) + + (t + (when current-region-info + (let ((note-cons-to-change (cond ((eq (aref current-region-info 3) 'regions-in-view) + (car notes-in-view)) + ((eq (aref current-region-info 3) 'closest-notes-regions) + (car closest-notes))))) + (when (< (org-element-property :begin element) + (org-element-property :end (car note-cons-to-change))) + (setcdr note-cons-to-change element)))) + + (let ((eligible-for-before (and closest-tipping-point all-after-tipping-point + (eq relative-position 'before)))) + (cond ((and eligible-for-before + (org-noter--compare-locations '> location closest-notes-location)) + (setq closest-notes (list (cons element nil)) + closest-notes-location location + current-region-info nil + closest-notes-regions nil) + (org-noter--view-region-add current-region-info closest-notes-regions element)) + + ((and eligible-for-before (equal location closest-notes-location)) + (push (cons element nil) closest-notes) + (org-noter--view-region-add current-region-info closest-notes-regions element)) + + (t (org-noter--view-region-finish current-region-info element))))))) + + (when new-location + (setq preamble nil) + (cond ((and (org-noter--compare-locations '<= location new-location) + (or (eq (car reference-for-insertion) 'before) + (org-noter--compare-locations '>= location reference-location))) + (setq reference-for-insertion (cons 'after element) + reference-location location)) + + ((and (eq (car reference-for-insertion) 'after) + (< (org-element-property :begin element) + (org-element-property :end (cdr reference-for-insertion))) + (org-noter--compare-locations '>= location new-location)) + (setq reference-for-insertion (cons 'before element) + reference-location location))))) + + (t + (when (and preamble new-location + (or (not reference-for-insertion) + (>= (org-element-property :begin element) + (org-element-property :end (cdr reference-for-insertion))))) + (setq reference-for-insertion (cons 'after element))))))) + nil nil (mapcar (lambda (el) + (unless (memq el org-noter--note-search-element-type) + el)) + org-element-all-elements))) (org-noter--view-region-finish current-region-info) From e8021a657b4d0be2e5daacd3e4191b42c8c9b371 Mon Sep 17 00:00:00 2001 From: c1-g Date: Thu, 12 May 2022 15:29:04 +0700 Subject: [PATCH 154/453] (minor) Check for mode first before creating outline --- modules/org-noter-djvu.el | 20 ++++--- modules/org-noter-nov.el | 121 +++++++++++++++++++------------------- 2 files changed, 74 insertions(+), 67 deletions(-) diff --git a/modules/org-noter-djvu.el b/modules/org-noter-djvu.el index 82bef8e..c23f9d2 100644 --- a/modules/org-noter-djvu.el +++ b/modules/org-noter-djvu.el @@ -35,12 +35,13 @@ (add-to-list 'org-noter--pretty-print-location-hook #'org-noter-djvu--pretty-print-location) (defun org-noter-djvu-approx-location-cons (major-mode &optional precise-info _force-new-ref) - (cons djvu-doc-page (if (or (numberp precise-info) - (and (consp precise-info) - (numberp (car precise-info)) - (numberp (cdr precise-info)))) - precise-info - (max 1 (/ (+ (window-start) (window-end nil t)) 2))))) + (when (eq major-mode 'djvu-read-mode) + (cons djvu-doc-page (if (or (numberp precise-info) + (and (consp precise-info) + (numberp (car precise-info)) + (numberp (cdr precise-info)))) + precise-info + (max 1 (/ (+ (window-start) (window-end nil t)) 2)))))) (add-to-list 'org-noter--doc-approx-location-hook #'org-noter-djvu-approx-location-cons) @@ -82,7 +83,7 @@ (add-to-list 'org-noter-get-selected-text-hook #'org-noter-djvu--get-selected-text) -(defun org-noter-create-skeleton-djvu () +(defun org-noter-create-skeleton-djvu (mode) (when (eq mode 'djvu-read-mode) (org-noter--with-valid-session (let* ((ast (org-noter--parse-root)) @@ -132,7 +133,10 @@ (org-noter--narrow-to-root ast) (goto-char (org-element-property :begin ast)) (when (org-at-heading-p) (outline-hide-subtree)) - (org-show-children 2))))))) + (org-show-children 2))) + output-data)))) + +(add-to-list 'org-noter-create-skeleton-functions #'org-noter-create-skeleton-djvu) (provide 'org-noter-djvu) ;;; org-noter-djvu.el ends here diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el index e6f6d51..6e043d6 100644 --- a/modules/org-noter-nov.el +++ b/modules/org-noter-nov.el @@ -114,67 +114,70 @@ (dom-children li))) (dom-children ol))) -(defun org-noter-create-skeleton-epub () +(defun org-noter-create-skeleton-epub (mode) "Epub outline with nov link." - (require 'esxml) - (require 'nov) - (require 'dom) - (org-noter--with-valid-session - (let* ((ast (org-noter--parse-root)) - (top-level (or (org-element-property :level ast) 0)) - output-data) - (with-current-buffer (org-noter--session-doc-buffer session) - (let* ((toc-path (cdr (aref nov-documents 0))) - (toc-tree (with-temp-buffer - (insert (nov-ncx-to-html toc-path)) - (replace-regexp "\n" - "" - nil - (point-min) - (point-max)) - (libxml-parse-html-region (point-min) - (point-max)))) - (origin-index nov-documents-index) - (origin-point (point))) - (dolist (item - (nreverse (flatten-tree (org-noter--handle-nov-toc-item toc-tree 1)))) - (let ((relative-level (aref item 1)) - (title (aref item 3)) - (url (aref item 5))) - (apply 'nov-visit-relative-file - (nov-url-filename-and-target url)) - (when (not (integerp nov-documents-index)) - (setq nov-documents-index 0)) - (push (vector title (list nov-documents-index (point)) relative-level) output-data))) - (push (vector "Skeleton" (list 0) 1) output-data) + (when (eq mode 'nov-mode) + (require 'esxml) + (require 'nov) + (require 'dom) + (org-noter--with-valid-session + (let* ((ast (org-noter--parse-root)) + (top-level (or (org-element-property :level ast) 0)) + output-data) + (with-current-buffer (org-noter--session-doc-buffer session) + (let* ((toc-path (cdr (aref nov-documents 0))) + (toc-tree (with-temp-buffer + (insert (nov-ncx-to-html toc-path)) + (replace-regexp "\n" + "" + nil + (point-min) + (point-max)) + (libxml-parse-html-region (point-min) + (point-max)))) + (origin-index nov-documents-index) + (origin-point (point))) + (dolist (item + (nreverse (flatten-tree (org-noter--handle-nov-toc-item toc-tree 1)))) + (let ((relative-level (aref item 1)) + (title (aref item 3)) + (url (aref item 5))) + (apply 'nov-visit-relative-file + (nov-url-filename-and-target url)) + (when (not (integerp nov-documents-index)) + (setq nov-documents-index 0)) + (push (vector title (list nov-documents-index (point)) relative-level) output-data))) + (push (vector "Skeleton" (list 0) 1) output-data) - (nov-goto-document origin-index) - (goto-char origin-point))) - (save-excursion - (goto-char (org-element-property :end ast)) - (with-current-buffer (org-noter--session-notes-buffer session) - (dolist (data output-data) - (setq title (aref data 0) - location (aref data 1) - relative-level (aref data 2)) - - (setq last-absolute-level (+ top-level relative-level) - level last-absolute-level) - - (org-noter--insert-heading level title) - - (when location - (org-entry-put nil org-noter-property-note-location (org-noter--pretty-print-location location))) - - (when org-noter-doc-property-in-notes - (org-entry-put nil org-noter-property-doc-file (org-noter--session-property-text session)) - (org-entry-put nil org-noter--property-auto-save-last-location "nil"))) - (setq ast (org-noter--parse-root)) - (org-noter--narrow-to-root ast) - (goto-char (org-element-property :begin ast)) - (outline-hide-subtree) - (org-show-children 2)))))) - + (nov-goto-document origin-index) + (goto-char origin-point))) + (save-excursion + (goto-char (org-element-property :end ast)) + (with-current-buffer (org-noter--session-notes-buffer session) + (dolist (data output-data) + (setq title (aref data 0) + location (aref data 1) + relative-level (aref data 2)) + + (setq last-absolute-level (+ top-level relative-level) + level last-absolute-level) + + (org-noter--insert-heading level title) + + (when location + (org-entry-put nil org-noter-property-note-location (org-noter--pretty-print-location location))) + + (when org-noter-doc-property-in-notes + (org-entry-put nil org-noter-property-doc-file (org-noter--session-property-text session)) + (org-entry-put nil org-noter--property-auto-save-last-location "nil"))) + (setq ast (org-noter--parse-root)) + (org-noter--narrow-to-root ast) + (goto-char (org-element-property :begin ast)) + (outline-hide-subtree) + (org-show-children 2))) + output-data)))) + +(add-to-list 'org-noter-create-skeleton-functions #'org-noter-create-skeleton-epub) (provide 'org-noter-nov) ;;; org-noter-nov.el ends here From a400fe14149b9520b9efef04e9ed53a2b9f07883 Mon Sep 17 00:00:00 2001 From: c1-g Date: Thu, 12 May 2022 15:30:21 +0700 Subject: [PATCH 155/453] (minor) Indentation --- modules/org-noter-pdf.el | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index fd67019..b87dd63 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -137,11 +137,11 @@ (when (memq 'outline answer) (dolist (item (pdf-info-outline)) - (let ((type (alist-get 'type item)) - (page (alist-get 'page item)) + (let ((type (alist-get 'type item)) + (page (alist-get 'page item)) (depth (alist-get 'depth item)) (title (alist-get 'title item)) - (top (alist-get 'top item))) + (top (alist-get 'top item))) (when (and (eq type 'goto-dest) (> page 0)) (push (vector title (cons page top) (1+ depth) nil) output-data))))) @@ -173,8 +173,8 @@ (setq insert-contents (y-or-n-p "Should we insert the annotations contents? ")) (dolist (item (pdf-info-getannots)) - (let* ((type (alist-get 'type item)) - (page (alist-get 'page item)) + (let* ((type (alist-get 'type item)) + (page (alist-get 'page item)) (edges (or (org-noter--pdf-tools-edges-to-region (alist-get 'markup-edges item)) (alist-get 'edges item))) (top (nth 1 edges)) @@ -184,10 +184,10 @@ (when (and (memq type chosen-annots) (> page 0)) (if (eq type 'link) (cl-pushnew page pages-with-links) - (setq name (cond ((eq type 'highlight) "Highlight") - ((eq type 'underline) "Underline") - ((eq type 'squiggly) "Squiggly") - ((eq type 'text) "Text note") + (setq name (cond ((eq type 'highlight) "Highlight") + ((eq type 'underline) "Underline") + ((eq type 'squiggly) "Squiggly") + ((eq type 'text) "Text note") ((eq type 'strike-out) "Strikeout"))) (when insert-contents @@ -205,7 +205,7 @@ (let ((links (pdf-info-pagelinks page)) type) (dolist (link links) - (setq type (alist-get 'type link)) + (setq type (alist-get 'type link)) (unless (eq type 'goto-dest) ;; NOTE(nox): Ignore internal links (let* ((edges (alist-get 'edges link)) (title (alist-get 'title link)) @@ -254,10 +254,10 @@ title location relative-level contents level) (dolist (data output-data) - (setq title (aref data 0) - location (aref data 1) + (setq title (aref data 0) + location (aref data 1) relative-level (aref data 2) - contents (aref data 3)) + contents (aref data 3)) (if (symbolp relative-level) (setq level (1+ last-absolute-level)) @@ -284,7 +284,10 @@ (org-noter--narrow-to-root ast) (goto-char (org-element-property :begin ast)) (outline-hide-subtree) - (org-show-children 2))))))) + (org-show-children 2))) + output-data)))) + +(add-to-list 'org-noter-create-skeleton-functions #'org-noter-create-skeleton-pdf) (provide 'org-noter-pdf) ;;; org-noter-pdf.el ends here From f0ba12d7791467785a739f43a9fb5b25b51ba6e1 Mon Sep 17 00:00:00 2001 From: c1-g Date: Thu, 12 May 2022 15:30:48 +0700 Subject: [PATCH 156/453] (fix) org-noter--get-view-info don't work with drawer as root --- org-noter-core.el | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index f1c6660..0ab3037 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1377,7 +1377,10 @@ the best heading to serve as a reference to create the new one relative to." (when view (org-noter--with-valid-session - (let ((root-pos (org-element-property :begin (org-noter--parse-root))) + (let ((contents (if (= 0 (org-noter--session-level session)) + (org-element-contents + (org-element-property :parent (org-noter--parse-root))) + (org-element-contents (org-noter--parse-root)))) (preamble t) notes-in-view regions-in-view reference-for-insertion reference-location @@ -1468,10 +1471,7 @@ relative to." (>= (org-element-property :begin element) (org-element-property :end (cdr reference-for-insertion))))) (setq reference-for-insertion (cons 'after element))))))) - nil nil (mapcar (lambda (el) - (unless (memq el org-noter--note-search-element-type) - el)) - org-element-all-elements))) + nil nil (delete 'headline (append org-element-all-elements nil)))) (org-noter--view-region-finish current-region-info) From 7ab72983215ffb74cb9402c4928e2e4bb386865c Mon Sep 17 00:00:00 2001 From: c1-g Date: Thu, 12 May 2022 15:31:44 +0700 Subject: [PATCH 157/453] (minor) Remove directory from options --- other/org-noter-citar.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/other/org-noter-citar.el b/other/org-noter-citar.el index 000ac1e..3fc6f2a 100644 --- a/other/org-noter-citar.el +++ b/other/org-noter-citar.el @@ -21,6 +21,7 @@ ;;; Code: (require 'citar) (require 'org-ref) +(require 'seq) ;; Regexp stolen from org-roam-bibtex; orb-utils-citekey-re. (defvar org-noter-citar-cite-key-re @@ -62,7 +63,7 @@ user select one of them." entries (append citar-library-paths citar-notes-paths) nil)) (url (list (citar-get-link (car entries)))) - (documents (flatten-list (append files url)))) + (documents (flatten-list (append (seq-remove #'file-directory-p files) url)))) (cond ((= (length documents) 1) (car documents)) ((> (length documents) 1) From 826b26697467c991fea96ab61efb0f4f453e3116 Mon Sep 17 00:00:00 2001 From: c1-g Date: Tue, 17 May 2022 09:02:34 +0700 Subject: [PATCH 158/453] (fix) doc-mode and note-mode overlap when using a single buffer --- org-noter-core.el | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 0ab3037..2de98e8 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -595,7 +595,6 @@ Otherwise return the maximum value for point." (add-hook 'kill-buffer-hook 'org-noter--handle-kill-buffer nil t)) (with-current-buffer notes-buffer - (org-noter-doc-mode -1) (org-noter-notes-mode 1) ;; NOTE(nox): This is needed because a session created in an indirect buffer would use the point of ;; the base buffer (as this buffer is indirect to the base!) @@ -1930,7 +1929,7 @@ defines if the text should be inserted inside the note." ;; NOTE(nox): Both precise and without questions will create new notes ((or precise-info force-new) (setq quote-p (with-temp-buffer - (insert selected-text) + (insert (or selected-text "")) (> (how-many "\n" (point-min)) 2))) (setq default (and selected-text (replace-regexp-in-string "\n" " " selected-text)))) @@ -2220,7 +2219,9 @@ Keymap: (,(kbd "M-n") . org-noter-sync-next-page-or-chapter) (,(kbd "C-M-p") . org-noter-sync-prev-note) (,(kbd "C-M-.") . org-noter-sync-current-note) - (,(kbd "C-M-n") . org-noter-sync-next-note))) + (,(kbd "C-M-n") . org-noter-sync-next-note)) + (if org-noter-doc-mode + (org-noter-doc-mode -1))) (provide 'org-noter-core) ;;; org-noter-core.el ends here From f141acb9f385b6775791fda5807598192a32c21f Mon Sep 17 00:00:00 2001 From: c1-g Date: Tue, 31 May 2022 11:54:01 +0700 Subject: [PATCH 159/453] fix: Before first heading error when hiding other notes --- org-noter-core.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org-noter-core.el b/org-noter-core.el index 2de98e8..8b23515 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1256,7 +1256,7 @@ document property) will be opened." (if (org-noter--session-hide-other session) (save-excursion (goto-char (org-element-property :begin (org-noter--parse-root))) - (unless (org-noter--no-heading-p) + (unless (org-before-first-heading-p) (outline-hide-subtree))) (org-cycle-hide-drawers 'all)) From 3c3fcf597165ae491d79a9a4b7b71b6d3acb172e Mon Sep 17 00:00:00 2001 From: c1-g Date: Tue, 31 May 2022 11:54:45 +0700 Subject: [PATCH 160/453] fix document-major-mode = nil --- org-noter-core.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org-noter-core.el b/org-noter-core.el index 8b23515..2b3cc8e 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -530,7 +530,7 @@ Otherwise return the maximum value for point." (progn (org-link-open-from-string document-property-value) (current-buffer)) (find-file-noselect document-property-value)))) - (document-major-mode (if link-p + (document-major-mode (if (or link-p (eq document (current-buffer))) document-property-value (buffer-local-value 'major-mode document))) (document-buffer-name From 1bf46f1acfe8ad41cad7b1e9fd9053423e27ff9a Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Thu, 7 Jul 2022 22:14:47 -0400 Subject: [PATCH 161/453] Ensure that title is actually used when setting a note. --- org-noter-core.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 2b3cc8e..bce062a 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1945,7 +1945,7 @@ defines if the text should be inserted inside the note." (setq collection (nreverse collection) title (if (or org-noter-insert-note-no-questions note-title) - (or default note-title) + (or org-noter-default-heading-title note-title) (completing-read "Note: " collection nil nil nil nil default)) selection (unless org-noter-insert-note-no-questions (cdr (assoc title collection)))) @@ -1982,7 +1982,7 @@ defines if the text should be inserted inside the note." (when (or quote-p (zerop (length title))) (setq title (replace-regexp-in-string (regexp-quote "$p$") (org-noter--pretty-print-location location) - org-noter-default-heading-title))) + title))) (if reference-element-cons (progn From ac1d1dd3926ea6b7784cfa5f10cbaf57f72507e4 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Thu, 18 Aug 2022 15:32:51 -0700 Subject: [PATCH 162/453] Highlight a precise note. --- modules/org-noter-pdf.el | 16 ++++++++++++++++ org-noter-core.el | 8 ++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index b87dd63..ce85a4e 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -90,6 +90,7 @@ (defun org-noter-pdf-goto-location (mode location) (when (memq mode '(doc-view-mode pdf-view-mode)) (let ((top (org-noter--get-location-top location)) + (window (org-noter--get-doc-window)) (left (org-noter--get-location-left location))) (if (eq mode 'doc-view-mode) @@ -289,5 +290,20 @@ (add-to-list 'org-noter-create-skeleton-functions #'org-noter-create-skeleton-pdf) +(add-to-list 'org-noter--parse-location-property-hook #'org-noter-pdf--parse-location) + +(defun org-noter-pdf--parse-location (arg) + "return a pdf location from an existing property. expecting (page left)" + (let* ((location (car (read-from-string arg)))) + location)) + +(defun org-noter-pdf--create-missing-annotation () + "Add a highlight from a selected note." + (let* ((location (org-noter--parse-location-property (org-noter--get-containing-element)))) + (with-selected-window (org-noter--get-doc-window) + (org-noter-pdf-goto-location 'pdf-view-mode location) + (pdf-annot-add-highlight-markup-annotation (cdr location))))) + + (provide 'org-noter-pdf) ;;; org-noter-pdf.el ends here diff --git a/org-noter-core.el b/org-noter-core.el index bce062a..b307745 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -2039,8 +2039,12 @@ See `org-noter-insert-note' docstring for more." (org-noter--with-valid-session (let ((org-noter-insert-note-no-questions (if toggle-no-questions (not org-noter-insert-note-no-questions) - org-noter-insert-note-no-questions))) - (org-noter-insert-note (org-noter--get-precise-info))))) + org-noter-insert-note-no-questions)) + (precise-info (org-noter--get-precise-info))) + (message (format "dmitry --- %s" precise-info)) + ;; TODO: This is all wrong. + (pdf-annot-add-highlight-markup-annotation precise-info) + (org-noter-insert-note precise-info)))) (defun org-noter-insert-note-toggle-no-questions () From 53c8e4799d7ea362e81c3064307cffb85f94b488 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sat, 15 Oct 2022 12:37:33 -0700 Subject: [PATCH 163/453] Add support for pdf buffer fetching. --- modules/org-noter-pdf.el | 6 ++++++ org-noter-core.el | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index ce85a4e..02f04ed 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -32,6 +32,12 @@ (numberp (cadr precise-info))) precise-info 0)))) +(defun org-noter-get-buffer-file-name-pdf (&optional arg) + ;;(bound-and-true-p nov-file-name) + "/Users/dmitrymarkushevich/org-roam/pubs/solove-nothing-to-hide.pdf") + + + (add-to-list 'org-noter--doc-approx-location-hook #'org-noter-pdf-approx-location-cons) (defun org-noter-pdf-view-setup-handler (major-mode) diff --git a/org-noter-core.el b/org-noter-core.el index b307745..7da4290 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -315,7 +315,7 @@ operations instead of the real value of the property." :group 'org-noter :type 'hook) -(defcustom org-noter-get-buffer-file-name-hook '(org-noter-get-buffer-file-name-nov) +(defcustom org-noter-get-buffer-file-name-hook '(org-noter-get-buffer-file-name-nov org-noter-get-buffer-file-name-pdf) "Functions that when passed a major mode, will return the current buffer file name. This is used by the `org-noter' command to determine the file name when From 261bf854d68f044732d6d5afbdaeec6dcf317e64 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sat, 15 Oct 2022 12:38:19 -0700 Subject: [PATCH 164/453] Remove nils (thats what org-noter-find-additional-notes-function returns right now) --- org-noter.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/org-noter.el b/org-noter.el index 4002971..03a95c1 100644 --- a/org-noter.el +++ b/org-noter.el @@ -153,9 +153,9 @@ notes file, even if it finds one." ;; be the same as `buffer-file-name', but is needed for the truename workaround (document-used-path (expand-file-name document-name document-directory)) - (search-names (append org-noter-default-notes-file-names - (list (concat document-base ".org")) - (list (run-hook-with-args-until-success 'org-noter-find-additional-notes-functions document-path)))) + (search-names (remove nil (append org-noter-default-notes-file-names + (list (concat document-base ".org")) + (list (run-hook-with-args-until-success 'org-noter-find-additional-notes-functions document-path))))) notes-files-annotating ; List of files annotating document notes-files ; List of found notes files (annotating or not) From 7dd7f163034307a2f1d9f69c71c0d51c3ec679bd Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Wed, 19 Oct 2022 07:02:52 -0700 Subject: [PATCH 165/453] The hook that calls org-noter-get-buffer-file-name-nov passes in a major mode. Looks like its optional though. --- modules/org-noter-nov.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el index 6e043d6..c651989 100644 --- a/modules/org-noter-nov.el +++ b/modules/org-noter-nov.el @@ -20,12 +20,12 @@ ;;; Commentary: -;; +;; ;;; Code: (require 'org-noter) -(defun org-noter-get-buffer-file-name-nov () +(defun org-noter-get-buffer-file-name-nov (&optional major-mode) (bound-and-true-p nov-file-name)) @@ -148,7 +148,7 @@ (setq nov-documents-index 0)) (push (vector title (list nov-documents-index (point)) relative-level) output-data))) (push (vector "Skeleton" (list 0) 1) output-data) - + (nov-goto-document origin-index) (goto-char origin-point))) (save-excursion From cb20d47f5239001dbf694457bf3bbb1032cd45f5 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sun, 30 Oct 2022 15:50:57 -0700 Subject: [PATCH 166/453] Return the correct file name for a pdf buffer. --- modules/org-noter-pdf.el | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 02f04ed..8e2374f 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -35,6 +35,9 @@ (defun org-noter-get-buffer-file-name-pdf (&optional arg) ;;(bound-and-true-p nov-file-name) "/Users/dmitrymarkushevich/org-roam/pubs/solove-nothing-to-hide.pdf") +(defun org-noter-get-buffer-file-name-pdf (&optional major-mode) + "Return the file naming backing the document buffer" + (bound-and-true-p pdf-file-name)) From 027b280d530898047bf7e43b91ad3b1fedc7834a Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sun, 30 Oct 2022 15:51:30 -0700 Subject: [PATCH 167/453] Scaffolding for correctly dealing with precise notes positions. --- modules/org-noter-pdf.el | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 8e2374f..e12adfa 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -32,15 +32,17 @@ (numberp (cadr precise-info))) precise-info 0)))) -(defun org-noter-get-buffer-file-name-pdf (&optional arg) - ;;(bound-and-true-p nov-file-name) - "/Users/dmitrymarkushevich/org-roam/pubs/solove-nothing-to-hide.pdf") (defun org-noter-get-buffer-file-name-pdf (&optional major-mode) "Return the file naming backing the document buffer" (bound-and-true-p pdf-file-name)) +(defun org-noter-pdf-check-location-property (&optional property) + "Check if PROPERTY is a valid location property" + t) + +(add-to-list 'org-noter--check-location-property-hook #'org-noter-pdf-check-location-property) (add-to-list 'org-noter--doc-approx-location-hook #'org-noter-pdf-approx-location-cons) (defun org-noter-pdf-view-setup-handler (major-mode) From 658e3c78246b32bbb307899ec3834347c38f0ee0 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sun, 30 Oct 2022 16:40:46 -0700 Subject: [PATCH 168/453] Add Cask and Makefile for testing and building. --- Cask | 5 +++++ Makefile | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 Cask create mode 100644 Makefile diff --git a/Cask b/Cask new file mode 100644 index 0000000..698eaff --- /dev/null +++ b/Cask @@ -0,0 +1,5 @@ +(source gnu) +(source melpa-stable) + +(depends-on "buttercup") +(depends-on "pdf-tools") diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e2d2cef --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +export EMACS ?= $(shell which emacs) +CASK_DIR := $(shell cask package-directory) + +$(CASK_DIR): Cask + cask install + @touch $(CASK_DIR) + +.PHONY: cask +cask: $(CASK_DIR) + +.PHONY: compile +compile: cask + ! (cask eval "(let ((byte-compile-error-on-warn f)) \ + (cask-cli/build))" 2>&1 \ + | egrep -a "(Warning|Error):") ; \ + (ret=$$? ; cask clean-elc && exit $$ret) + +.PHONY: test +test: compile + cask exec buttercup -L . From 28d3a5e82f9ba665191e850fa482071f7024f10d Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sun, 30 Oct 2022 16:41:23 -0700 Subject: [PATCH 169/453] Add initial test for org-noter-pdf-chcek-location-property. --- modules/org-noter-pdf.el | 4 ++-- tests/org-noter-pdf-tests.el | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 tests/org-noter-pdf-tests.el diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index e12adfa..a45904d 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -20,7 +20,7 @@ ;;; Commentary: -;; +;; ;;; Code: (require 'org-noter) @@ -39,7 +39,7 @@ (defun org-noter-pdf-check-location-property (&optional property) "Check if PROPERTY is a valid location property" - t) + (equal 5 (length (read property)))) (add-to-list 'org-noter--check-location-property-hook #'org-noter-pdf-check-location-property) diff --git a/tests/org-noter-pdf-tests.el b/tests/org-noter-pdf-tests.el new file mode 100644 index 0000000..0870812 --- /dev/null +++ b/tests/org-noter-pdf-tests.el @@ -0,0 +1,15 @@ +(add-to-list 'load-path "modules") +(require 'org-noter-pdf) +(describe "org-noter-pdf-functionality" + (describe "location functionality" + (it "can correctly recognize precise notes location" + (expect + (org-noter-pdf-check-location-property "(16 0.3073263558515699 0.7254290171606864 0.2274024738344434 0.65600624024961)") + :to-equal t)) + (it "correctly rejects non location properties" + (expect + (org-noter-pdf-check-location-property "(asdf)") + :to-equal nil)) + + ) + ) From f51372c234ab1b6eb39c74f449be9cf32f938843 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 4 Dec 2022 17:58:29 -0800 Subject: [PATCH 170/453] bugfix (org-noter.el) search-names should not end with nil list search-names was modified in [a7ff66c], sometimes leaving a nil list as its last item, which causes `expand-file-name' to error out. --- org-noter.el | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/org-noter.el b/org-noter.el index 4002971..81aa005 100644 --- a/org-noter.el +++ b/org-noter.el @@ -161,6 +161,10 @@ notes file, even if it finds one." (document-location (org-noter--doc-approx-location))) + ;; remove nil lists at end of search-names + (while (equal (last search-names) (list nil)) + (nbutlast search-names)) + ;; NOTE(nox): Check the search path (dolist (path org-noter-notes-search-path) (dolist (name search-names) From 374b35de896805c809015b439aaad8e129edcbb8 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 4 Dec 2022 18:15:46 -0800 Subject: [PATCH 171/453] bugfix (--get-precise-info-hook) pdf needs window and event for each of pdf, nov and djvu, `window' is passed in and `event' is intialized as nil internally --- modules/org-noter-djvu.el | 11 ++++++----- modules/org-noter-nov.el | 11 ++++++----- modules/org-noter-pdf.el | 28 +++++++++++++++------------- org-noter-core.el | 2 +- 4 files changed, 28 insertions(+), 24 deletions(-) diff --git a/modules/org-noter-djvu.el b/modules/org-noter-djvu.el index c23f9d2..a562397 100644 --- a/modules/org-noter-djvu.el +++ b/modules/org-noter-djvu.el @@ -45,14 +45,15 @@ (add-to-list 'org-noter--doc-approx-location-hook #'org-noter-djvu-approx-location-cons) -(defun org-noter-djvu--get-precise-info (major-mode) +(defun org-noter-djvu--get-precise-info (major-mode window) (when (eq major-mode 'djvu-read-mode) (if (region-active-p) (cons (mark) (point)) - (while (not (and (eq 'mouse-1 (car event)) - (eq window (posn-window (event-start event))))) - (setq event (read-event "Click where you want the start of the note to be!"))) - (posn-point (event-start event))))) + (let ((event nil)) + (while (not (and (eq 'mouse-1 (car event)) + (eq window (posn-window (event-start event))))) + (setq event (read-event "Click where you want the start of the note to be!"))) + (posn-point (event-start event)))))) (add-to-list 'org-noter--get-precise-info-hook #'org-noter-djvu--get-precise-info) diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el index 6e043d6..b70b4e2 100644 --- a/modules/org-noter-nov.el +++ b/modules/org-noter-nov.el @@ -59,14 +59,15 @@ (add-to-list 'org-noter--pretty-print-location-hook #'org-noter-nov--pretty-print-location) -(defun org-noter-nov--get-precise-info (major-mode) +(defun org-noter-nov--get-precise-info (major-mode window) (when (eq major-mode 'nov-mode) (if (region-active-p) (cons (mark) (point)) - (while (not (and (eq 'mouse-1 (car event)) - (eq window (posn-window (event-start event))))) - (setq event (read-event "Click where you want the start of the note to be!"))) - (posn-point (event-start event))))) + (let ((event nil)) + (while (not (and (eq 'mouse-1 (car event)) + (eq window (posn-window (event-start event))))) + (setq event (read-event "Click where you want the start of the note to be!"))) + (posn-point (event-start event)))))) (add-to-list 'org-noter--get-precise-info-hook #'org-noter-nov--get-precise-info) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index b87dd63..bd1333f 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -61,28 +61,30 @@ (add-to-list 'org-noter--pretty-print-location-hook #'org-noter-pdf--pretty-print-location) -(defun org-noter-pdf--get-precise-info (major-mode) +(defun org-noter-pdf--get-precise-info (major-mode window) (when (eq major-mode 'pdf-view-mode) (if (pdf-view-active-region-p) (let ((edges (pdf-view-active-region))) (car edges)) - (while (not (and (eq 'mouse-1 (car event)) - (eq window (posn-window (event-start event))))) - (setq event (read-event "Click where you want the start of the note to be!"))) - (let ((col-row (posn-col-row (event-start event)))) - (org-noter--conv-page-scroll-percentage (+ (window-vscroll) (cdr col-row)) - (+ (window-hscroll) (car col-row))))))) + (let ((event nil)) + (while (not (and (eq 'mouse-1 (car event)) + (eq window (posn-window (event-start event))))) + (setq event (read-event "Click where you want the start of the note to be!"))) + (let ((col-row (posn-col-row (event-start event)))) + (org-noter--conv-page-scroll-percentage (+ (window-vscroll) (cdr col-row)) + (+ (window-hscroll) (car col-row)))))))) (add-to-list 'org-noter--get-precise-info-hook #'org-noter-pdf--get-precise-info) -(defun org-noter-doc--get-precise-info (major-mode) +(defun org-noter-doc--get-precise-info (major-mode window) (when (eq major-mode 'doc-view-mode) - (while (not (and (eq 'mouse-1 (car event)) - (eq window (posn-window (event-start event))))) - (setq event (read-event "Click where you want the start of the note to be!"))) - (org-noter--conv-page-scroll-percentage (+ (window-vscroll) - (cdr (posn-col-row (event-start event))))))) + (let ((event nil)) + (while (not (and (eq 'mouse-1 (car event)) + (eq window (posn-window (event-start event))))) + (setq event (read-event "Click where you want the start of the note to be!"))) + (org-noter--conv-page-scroll-percentage (+ (window-vscroll) + (cdr (posn-col-row (event-start event)))))))) (add-to-list 'org-noter--get-precise-info-hook #'org-noter-doc--get-precise-info) diff --git a/org-noter-core.el b/org-noter-core.el index 2b3cc8e..07c91e1 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1094,7 +1094,7 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (let ((window (org-noter--get-doc-window)) (mode (org-noter--session-doc-mode session))) (with-selected-window window - (run-hook-with-args-until-success 'org-noter--get-precise-info-hook mode))))) + (run-hook-with-args-until-success 'org-noter--get-precise-info-hook mode window))))) (defun org-noter--show-arrow () (when (and org-noter--arrow-location From 1190d0262d8306b0a5d088c77f203d77e0f27384 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 4 Dec 2022 18:20:45 -0800 Subject: [PATCH 172/453] bugfix (org-noter-pdf.el) precise-info fixes 1. in `org-noter-pdf-approx-location-cons', precise-info is a cons, not a list (as defined in 2nd hunk). 2. for selected region precise note, move the pointer left by 0.02 --- modules/org-noter-pdf.el | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index bd1333f..eff9227 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -27,9 +27,9 @@ (defun org-noter-pdf-approx-location-cons (major-mode &optional precise-info _force-new-ref) (when (memq major-mode '(doc-view-mode pdf-view-mode)) - (cons (image-mode-window-get 'page) (if (and (listp precise-info) + (cons (image-mode-window-get 'page) (if (and (consp precise-info) (numberp (car precise-info)) - (numberp (cadr precise-info))) + (numberp (cdr precise-info))) precise-info 0)))) (add-to-list 'org-noter--doc-approx-location-hook #'org-noter-pdf-approx-location-cons) @@ -65,7 +65,9 @@ (when (eq major-mode 'pdf-view-mode) (if (pdf-view-active-region-p) (let ((edges (pdf-view-active-region))) - (car edges)) + (cons + (cadar edges) + (- (caar edges) 0.02))) (let ((event nil)) (while (not (and (eq 'mouse-1 (car event)) From 02a2f346bdb81d501b03c96bce2ea53fa5c4dc5c Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 4 Dec 2022 18:26:02 -0800 Subject: [PATCH 173/453] bugfix (--doc-goto-location-hook) pdf requires window so to be consistent, nov and djvu need window as an optional argument, which they don't use. --- modules/org-noter-djvu.el | 2 +- modules/org-noter-nov.el | 2 +- modules/org-noter-pdf.el | 2 +- org-noter-core.el | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/org-noter-djvu.el b/modules/org-noter-djvu.el index a562397..fd93021 100644 --- a/modules/org-noter-djvu.el +++ b/modules/org-noter-djvu.el @@ -64,7 +64,7 @@ (add-to-list 'org-noter-set-up-document-hook #'org-noter-djvu-setup-handler) -(defun org-noter-djvu-goto-location (mode location) +(defun org-noter-djvu-goto-location (mode location &optional window) (when (eq mode 'djvu-read-mode) (djvu-goto-page (car location)) (goto-char (org-noter--get-location-top location)))) diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el index b70b4e2..7d9e373 100644 --- a/modules/org-noter-nov.el +++ b/modules/org-noter-nov.el @@ -71,7 +71,7 @@ (add-to-list 'org-noter--get-precise-info-hook #'org-noter-nov--get-precise-info) -(defun org-noter-nov-goto-location (mode location) +(defun org-noter-nov-goto-location (mode location &optional window) (when (eq mode 'nov-mode) (setq nov-documents-index (org-noter--get-location-page location)) (nov-render-document) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index eff9227..1445f58 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -91,7 +91,7 @@ (add-to-list 'org-noter--get-precise-info-hook #'org-noter-doc--get-precise-info) -(defun org-noter-pdf-goto-location (mode location) +(defun org-noter-pdf-goto-location (mode location window) (when (memq mode '(doc-view-mode pdf-view-mode)) (let ((top (org-noter--get-location-top location)) (left (org-noter--get-location-left location))) diff --git a/org-noter-core.el b/org-noter-core.el index 07c91e1..84e346a 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1169,7 +1169,7 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (let ((window (org-noter--get-doc-window)) (mode (org-noter--session-doc-mode session))) (with-selected-window window - (run-hook-with-args-until-success 'org-noter--doc-goto-location-hook mode location) + (run-hook-with-args-until-success 'org-noter--doc-goto-location-hook mode location window) (redisplay))))) (defun org-noter--compare-location-cons (comp l1 l2) From 6135f1a8c93b1eb2be0796b0c4c0411784b3f651 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 4 Dec 2022 18:33:01 -0800 Subject: [PATCH 174/453] (org-noter-nov.el) complete modularization of nov Also, get-buffer-file-name-nov is called with one argument, though it is not used. The changes here are untested, as I do not use org-noter-nov.el --- modules/org-noter-nov.el | 3 ++- org-noter-core.el | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el index 7d9e373..07724d6 100644 --- a/modules/org-noter-nov.el +++ b/modules/org-noter-nov.el @@ -25,9 +25,10 @@ ;;; Code: (require 'org-noter) -(defun org-noter-get-buffer-file-name-nov () +(defun org-noter-get-buffer-file-name-nov (mode) (bound-and-true-p nov-file-name)) +(add-to-list 'org-noter-get-buffer-file-name-hook #'org-noter-get-buffer-file-name-nov) (defun org-noter-nov-approx-location-cons (major-mode &optional precise-info _force-new-ref) (org-noter--with-valid-session diff --git a/org-noter-core.el b/org-noter-core.el index 84e346a..dddf4bc 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -315,7 +315,7 @@ operations instead of the real value of the property." :group 'org-noter :type 'hook) -(defcustom org-noter-get-buffer-file-name-hook '(org-noter-get-buffer-file-name-nov) +(defcustom org-noter-get-buffer-file-name-hook nil "Functions that when passed a major mode, will return the current buffer file name. This is used by the `org-noter' command to determine the file name when From 9dfac53b4478c71fd4f827314b545c90834925a6 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 4 Dec 2022 18:42:40 -0800 Subject: [PATCH 175/453] bufix (org-noter-core.el): various 1. `org-noter--note-search-no-recurse' is used inside `org-noter--map-ignore-headings-with-doc-file' 2. `org-link-bracket-re' is favored over `org-bracket-link-regexp' after org-mode version 9.3 3. kill the notes buffer **when** an indirect buffer is used, not **unless** it is used --- org-noter-core.el | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index dddf4bc..522303d 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -454,6 +454,9 @@ Used by `org-noter--create-session' when creating a new session." (defconst org-noter--property-closest-tipping-point "NOTER_CLOSEST_TIPPING_POINT" "Property for overriding global `org-noter-closest-tipping-point'.") +(defconst org-noter--note-search-no-recurse (delete 'headline (append org-element-all-elements nil)) + "List of elements that shouldn't be recursed into when searching for notes.") + (defconst org-noter--note-search-element-type '(headline) "List of elements that should be searched for notes.") @@ -516,7 +519,7 @@ Otherwise return the maximum value for point." (defun org-noter--create-session (ast document-property-value notes-file-path) (let* ((raw-value-not-empty (> (length (org-element-property :raw-value ast)) 0)) - (link-p (or (string-match-p org-bracket-link-regexp document-property-value) + (link-p (or (string-match-p org-link-bracket-re document-property-value) (string-match-p org-noter--url-regexp document-property-value))) (display-name (if raw-value-not-empty (org-element-property :raw-value ast) @@ -1852,7 +1855,7 @@ want to kill." (with-current-buffer notes-buffer (remove-hook 'kill-buffer-hook 'org-noter--handle-kill-buffer t) (restore-buffer-modified-p nil)) - (unless org-noter-use-indirect-buffer + (when org-noter-use-indirect-buffer (kill-buffer notes-buffer)) (when base-buffer From dfe7df2fccbb574815ce27b66101255af32a435b Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 4 Dec 2022 18:48:59 -0800 Subject: [PATCH 176/453] revert hunk 1 of [5eee7fd] '(fix) Remove "Before first heading" error.' I don't really understand this bit of code, especially what `ast' is, but it breaks narrowing when multiple documents' notes are stored in a single file. --- org-noter-core.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 522303d..158dc98 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -811,11 +811,11 @@ properties, by a margin of NEWLINES-NUMBER." (run-hooks 'org-noter-insert-heading-hook))) (defun org-noter--narrow-to-root (ast) - (when ast + (when (and ast (not (org-noter--no-heading-p))) (save-excursion (goto-char (org-element-property :contents-begin ast)) (org-show-entry) - (when (org-at-heading-p) (org-narrow-to-subtree)) + (org-narrow-to-subtree) (org-cycle-hide-drawers 'all)))) (defun org-noter--get-doc-window () From 11fc0a814118850ceb298be61906a0fd3077a3c8 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 4 Dec 2022 18:52:14 -0800 Subject: [PATCH 177/453] similar to [8308a3a] "revert unused with-current-buffer" cbpnk branch ends this block with `org-noter--note-search-no-recurse' --- org-noter-core.el | 160 +++++++++++++++++++++++----------------------- 1 file changed, 79 insertions(+), 81 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 158dc98..c2dfca3 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1393,87 +1393,85 @@ relative to." ignore-until-level current-region-info) ;; NOTE(nox): [REGIONS-LIST-PTR START MAX-END REGIONS-LIST-NAME] - (with-current-buffer (or (buffer-base-buffer (org-noter--session-notes-buffer session)) - (org-noter--session-notes-buffer session)) - (org-element-map contents org-noter--note-search-element-type - (lambda (element) - (let ((doc-file (org-noter--doc-file-property element)) - (location (org-noter--parse-location-property element))) - (when (and ignore-until-level (<= (org-element-property :level element) ignore-until-level)) - (setq ignore-until-level nil)) - - (cond - (ignore-until-level) ;; NOTE(nox): This heading is ignored, do nothing - - ((and doc-file (not (string= doc-file (org-noter--session-property-text session)))) - (org-noter--view-region-finish current-region-info element) - (setq ignore-until-level (org-element-property :level element)) - (when (and preamble new-location - (or (not reference-for-insertion) - (>= (org-element-property :begin element) - (org-element-property :end (cdr reference-for-insertion))))) - (setq reference-for-insertion (cons 'after element)))) - - (location - (let ((relative-position (org-noter--relative-position-to-view location view))) - (cond - ((eq relative-position 'inside) - (push (cons element nil) notes-in-view) - - (org-noter--view-region-add current-region-info regions-in-view element) - - (setq all-after-tipping-point - (and all-after-tipping-point (org-noter--note-after-tipping-point - closest-tipping-point location view)))) - - (t - (when current-region-info - (let ((note-cons-to-change (cond ((eq (aref current-region-info 3) 'regions-in-view) - (car notes-in-view)) - ((eq (aref current-region-info 3) 'closest-notes-regions) - (car closest-notes))))) - (when (< (org-element-property :begin element) - (org-element-property :end (car note-cons-to-change))) - (setcdr note-cons-to-change element)))) - - (let ((eligible-for-before (and closest-tipping-point all-after-tipping-point - (eq relative-position 'before)))) - (cond ((and eligible-for-before - (org-noter--compare-locations '> location closest-notes-location)) - (setq closest-notes (list (cons element nil)) - closest-notes-location location - current-region-info nil - closest-notes-regions nil) - (org-noter--view-region-add current-region-info closest-notes-regions element)) - - ((and eligible-for-before (equal location closest-notes-location)) - (push (cons element nil) closest-notes) - (org-noter--view-region-add current-region-info closest-notes-regions element)) - - (t (org-noter--view-region-finish current-region-info element))))))) - - (when new-location - (setq preamble nil) - (cond ((and (org-noter--compare-locations '<= location new-location) - (or (eq (car reference-for-insertion) 'before) - (org-noter--compare-locations '>= location reference-location))) - (setq reference-for-insertion (cons 'after element) - reference-location location)) - - ((and (eq (car reference-for-insertion) 'after) - (< (org-element-property :begin element) - (org-element-property :end (cdr reference-for-insertion))) - (org-noter--compare-locations '>= location new-location)) - (setq reference-for-insertion (cons 'before element) - reference-location location))))) - - (t - (when (and preamble new-location - (or (not reference-for-insertion) - (>= (org-element-property :begin element) - (org-element-property :end (cdr reference-for-insertion))))) - (setq reference-for-insertion (cons 'after element))))))) - nil nil (delete 'headline (append org-element-all-elements nil)))) + (org-element-map contents org-noter--note-search-element-type + (lambda (element) + (let ((doc-file (org-noter--doc-file-property element)) + (location (org-noter--parse-location-property element))) + (when (and ignore-until-level (<= (org-element-property :level element) ignore-until-level)) + (setq ignore-until-level nil)) + + (cond + (ignore-until-level) ;; NOTE(nox): This heading is ignored, do nothing + + ((and doc-file (not (string= doc-file (org-noter--session-property-text session)))) + (org-noter--view-region-finish current-region-info element) + (setq ignore-until-level (org-element-property :level element)) + (when (and preamble new-location + (or (not reference-for-insertion) + (>= (org-element-property :begin element) + (org-element-property :end (cdr reference-for-insertion))))) + (setq reference-for-insertion (cons 'after element)))) + + (location + (let ((relative-position (org-noter--relative-position-to-view location view))) + (cond + ((eq relative-position 'inside) + (push (cons element nil) notes-in-view) + + (org-noter--view-region-add current-region-info regions-in-view element) + + (setq all-after-tipping-point + (and all-after-tipping-point (org-noter--note-after-tipping-point + closest-tipping-point location view)))) + + (t + (when current-region-info + (let ((note-cons-to-change (cond ((eq (aref current-region-info 3) 'regions-in-view) + (car notes-in-view)) + ((eq (aref current-region-info 3) 'closest-notes-regions) + (car closest-notes))))) + (when (< (org-element-property :begin element) + (org-element-property :end (car note-cons-to-change))) + (setcdr note-cons-to-change element)))) + + (let ((eligible-for-before (and closest-tipping-point all-after-tipping-point + (eq relative-position 'before)))) + (cond ((and eligible-for-before + (org-noter--compare-locations '> location closest-notes-location)) + (setq closest-notes (list (cons element nil)) + closest-notes-location location + current-region-info nil + closest-notes-regions nil) + (org-noter--view-region-add current-region-info closest-notes-regions element)) + + ((and eligible-for-before (equal location closest-notes-location)) + (push (cons element nil) closest-notes) + (org-noter--view-region-add current-region-info closest-notes-regions element)) + + (t (org-noter--view-region-finish current-region-info element))))))) + + (when new-location + (setq preamble nil) + (cond ((and (org-noter--compare-locations '<= location new-location) + (or (eq (car reference-for-insertion) 'before) + (org-noter--compare-locations '>= location reference-location))) + (setq reference-for-insertion (cons 'after element) + reference-location location)) + + ((and (eq (car reference-for-insertion) 'after) + (< (org-element-property :begin element) + (org-element-property :end (cdr reference-for-insertion))) + (org-noter--compare-locations '>= location new-location)) + (setq reference-for-insertion (cons 'before element) + reference-location location))))) + + (t + (when (and preamble new-location + (or (not reference-for-insertion) + (>= (org-element-property :begin element) + (org-element-property :end (cdr reference-for-insertion))))) + (setq reference-for-insertion (cons 'after element))))))) + nil nil org-noter--note-search-no-recurse) (org-noter--view-region-finish current-region-info) From 60ceee55a8f39d27e16ef526cf367f8ace528c70 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sat, 15 Oct 2022 12:38:19 -0700 Subject: [PATCH 178/453] Remove nils (thats what org-noter-find-additional-notes-function returns right now) fix by dmitrym supersedes my fix. cleaner solution. --- org-noter.el | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/org-noter.el b/org-noter.el index 81aa005..03a95c1 100644 --- a/org-noter.el +++ b/org-noter.el @@ -153,18 +153,14 @@ notes file, even if it finds one." ;; be the same as `buffer-file-name', but is needed for the truename workaround (document-used-path (expand-file-name document-name document-directory)) - (search-names (append org-noter-default-notes-file-names - (list (concat document-base ".org")) - (list (run-hook-with-args-until-success 'org-noter-find-additional-notes-functions document-path)))) + (search-names (remove nil (append org-noter-default-notes-file-names + (list (concat document-base ".org")) + (list (run-hook-with-args-until-success 'org-noter-find-additional-notes-functions document-path))))) notes-files-annotating ; List of files annotating document notes-files ; List of found notes files (annotating or not) (document-location (org-noter--doc-approx-location))) - ;; remove nil lists at end of search-names - (while (equal (last search-names) (list nil)) - (nbutlast search-names)) - ;; NOTE(nox): Check the search path (dolist (path org-noter-notes-search-path) (dolist (name search-names) From ed52f1ade17cefef9567fac692e8ac90ae6097e6 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sun, 30 Oct 2022 15:51:30 -0700 Subject: [PATCH 179/453] Scaffolding for correctly dealing with precise notes positions. --- modules/org-noter-pdf.el | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 1445f58..3d377c4 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -32,6 +32,17 @@ (numberp (cdr precise-info))) precise-info 0)))) +(defun org-noter-get-buffer-file-name-pdf (&optional major-mode) + "Return the file naming backing the document buffer" + (bound-and-true-p pdf-file-name)) + + +(defun org-noter-pdf-check-location-property (&optional property) + "Check if PROPERTY is a valid location property" + t) + + +(add-to-list 'org-noter--check-location-property-hook #'org-noter-pdf-check-location-property) (add-to-list 'org-noter--doc-approx-location-hook #'org-noter-pdf-approx-location-cons) (defun org-noter-pdf-view-setup-handler (major-mode) From bb56ac1c7a1d5c89b9421d290c9d5dbcfae17c68 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Tue, 6 Dec 2022 21:57:23 -0800 Subject: [PATCH 180/453] add hook for new pdf fn in prior commit --- modules/org-noter-pdf.el | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 3d377c4..1115df8 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -32,18 +32,19 @@ (numberp (cdr precise-info))) precise-info 0)))) +(add-to-list 'org-noter--doc-approx-location-hook #'org-noter-pdf-approx-location-cons) + (defun org-noter-get-buffer-file-name-pdf (&optional major-mode) "Return the file naming backing the document buffer" (bound-and-true-p pdf-file-name)) +(add-to-list 'org-noter-get-buffer-file-name-hook #'org-noter-get-buffer-file-name-pdf) (defun org-noter-pdf-check-location-property (&optional property) "Check if PROPERTY is a valid location property" t) - (add-to-list 'org-noter--check-location-property-hook #'org-noter-pdf-check-location-property) -(add-to-list 'org-noter--doc-approx-location-hook #'org-noter-pdf-approx-location-cons) (defun org-noter-pdf-view-setup-handler (major-mode) (when (eq major-mode 'pdf-view-mode) From 339cf08d1bab7d766167153b57be80ec72d5a644 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Tue, 6 Dec 2022 22:47:20 -0800 Subject: [PATCH 181/453] rename README.org to README-djvu.org --- README.org => README-djvu.org | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename README.org => README-djvu.org (100%) diff --git a/README.org b/README-djvu.org similarity index 100% rename from README.org rename to README-djvu.org From 7a25c47e60153a51df883f3d799e59d7a5b61f6d Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Tue, 6 Dec 2022 22:48:06 -0800 Subject: [PATCH 182/453] README.org for this fork (petermao) --- README.org | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 README.org diff --git a/README.org b/README.org new file mode 100644 index 0000000..e8bb17c --- /dev/null +++ b/README.org @@ -0,0 +1,40 @@ +* ORG-NOTER (fork of a fork) +** This is a fork of the *c1-g* fork with some commits picked from *dmitrym* + - In essence, this is close to the original *weirdNox* (Gonçalo Santos) + version that you find on MELPA with the refactoring that *c1-g* implemented + + - djvu and epub support have also been advanced by *c1-g*, but I have not + tested any of that code. + + - The main new feature for PDF users is 2-D precise notes, which can be + inserted in two ways: + 1. "Select-precise": selecting text in the pdf, followed by =M-i=. *c1-g* + changed the position format to use the entire list returned by =edges=. + This breaks the other way of inserting precise notes. On *dmitrym*'s + fork, select-precise notes auto-fill the note title with the selected + text. This has deleterious side effects for other note insertion methods. + 2. "Click-precise": =M-i=, followed by clicking a location on the page. + The location recorded by =org-noter= is typically inaccurate in the + horizontal direction. This is something I want to fix. + + + - Standard notes can also be inserted in two ways: + 1. "TAB". Hitting tab creates a note title "Notes for page ##", where the + page number is inserted automatically over the octothorpes. + 2. "i". The fundamental =org-noter-insert-note=. User types note title + into the minibuffer. +** Note insertion desired features + 1. The position of the precise note should be at the click (with a + configurable horizontal offset) or at the upper-left of the selected text + (or configurable to one of the corners or ends of the selected area). + 2. Highlighting of selected text should be a user settable configuration option + 3. For any of the note insertion methods, if there is any selected text, then + it should be the default title. (or there should be a configurable + switch) + 4. Add property and/or configuration to make the arrow left or right + pointing. + +** Bugs to fix + 1. Navigation of nested notes is spotty. =C-M-p= and =C-M-.= have trouble + detecting nested notes. + From 829cefd4d5684dd83cdd0ff2a5c22a5c5e4f7587 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sat, 24 Dec 2022 11:55:23 -0800 Subject: [PATCH 183/453] Added a test file for core functionality. --- tests/org-noter-core-tests.el | 52 +++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 tests/org-noter-core-tests.el diff --git a/tests/org-noter-core-tests.el b/tests/org-noter-core-tests.el new file mode 100644 index 0000000..477e2dd --- /dev/null +++ b/tests/org-noter-core-tests.el @@ -0,0 +1,52 @@ +(add-to-list 'load-path "modules") +(require 'org) +(require 'org-noter-pdf) + +(defvar mock-contents-simple-notes-file + "* solove-nothing-to-hide +:PROPERTIES: +:NOTER_DOCUMENT: pubs/solove-nothing-to-hide.pdf +:END: +") + +(defun with-mock-contents (contents lambda) + (message "\n--------------------------------------------") + + ;; TODO: when an assert fails in buttercup, an exception (??) is thrown, + ;; so temp file isnt being cleaned up. This is the sledgehammer approach. + ;; Needs to be fixed so that it's cleaned up properly. + (when (boundp 'org-noter-test-file) + (progn + (message (format "Removing org-noter-test-file: %s\n" org-noter-test-file)) + (delete-file org-noter-test-file))) + + + (let* ((tempfile (make-temp-file "Notes" nil ".org" contents))) + (message (format "Creating a tempfile: %s\n" tempfile)) + (setq org-noter-test-file tempfile) + (message "Opening the file..") + (org-mode) + (find-file tempfile) + (org-mode) + (message "Starting the test..") + (message "%s" (buffer-string)) + (funcall lambda) + (message "About to kill buffer..") + (kill-current-buffer) + (message (format "Removing tempfile %s" tempfile)) + (delete-file tempfile) + (message "+++++++++++++++++++++++++++++++++++++++++") + )) + +(describe "org-noter-core" + (describe "note taking functionality" + (it "can parse a note file ast that is not empty" + (with-mock-contents + mock-contents-simple-notes-file + '(lambda () (let ((mock-ast (org-noter--parse-root))) + (message "%s" mock-ast) + (expect mock-ast :not :to-be nil))) + ) + ) + ) + ) From a55e4557131c712d16426c64987f83fdae788664 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sun, 25 Dec 2022 18:23:09 -0800 Subject: [PATCH 184/453] Added a skeleton for a note-adding test. First stab. --- tests/org-noter-core-tests.el | 71 +++++++++++++++++++++++++++++++---- 1 file changed, 64 insertions(+), 7 deletions(-) diff --git a/tests/org-noter-core-tests.el b/tests/org-noter-core-tests.el index 477e2dd..eb51b01 100644 --- a/tests/org-noter-core-tests.el +++ b/tests/org-noter-core-tests.el @@ -1,5 +1,4 @@ (add-to-list 'load-path "modules") -(require 'org) (require 'org-noter-pdf) (defvar mock-contents-simple-notes-file @@ -9,7 +8,10 @@ :END: ") + + (defun with-mock-contents (contents lambda) + "Create a real buffer with CONTENTS and then execute the LAMBDA" (message "\n--------------------------------------------") ;; TODO: when an assert fails in buttercup, an exception (??) is thrown, @@ -19,8 +21,6 @@ (progn (message (format "Removing org-noter-test-file: %s\n" org-noter-test-file)) (delete-file org-noter-test-file))) - - (let* ((tempfile (make-temp-file "Notes" nil ".org" contents))) (message (format "Creating a tempfile: %s\n" tempfile)) (setq org-noter-test-file tempfile) @@ -38,15 +38,72 @@ (message "+++++++++++++++++++++++++++++++++++++++++") )) +(defun wakka (mode) + (message "🧪org-noter-core-test-return-text") + "⚠️org-noter-core-test-return-text") + + + +(defun org-noter-core-test-return-text (mode) + (message "org-noter-core-test-return-text") + "org-noter-core-test-return-text") + +(defun org-noter-core-test-document-property (&optional param) + (message "🧪org-noter-core-test-document-property %s" param) + org-noter-test-file) + +(defun org-noter-core-test-view-setup-handler (&optional param) + (message "org-noter-core-test-view-setup-handler") + t) + +(defun org-noter-core-test-open-document-functions (&optional doc) + (message "org-noter-core-test-open-document-functions") + (find-file (org-noter-core-test-document-property))) + +(defun org-noter-core-test-approx-location (&optional a b) + (message "approx-location") + (list 0 0 0 0 0)) + +(defun wakka1 (a b c) + (message "approx-location") + (list 0 0 0 0 0)) + +(defun org-noter-core-test-get-current-view (mode) + t) + + + + (describe "org-noter-core" (describe "note taking functionality" + (before-each + (spy-on 'org-noter-core-test-return-text)) + (it "can parse a note file ast that is not empty" (with-mock-contents mock-contents-simple-notes-file '(lambda () (let ((mock-ast (org-noter--parse-root))) (message "%s" mock-ast) - (expect mock-ast :not :to-be nil))) - ) - ) - ) + (expect mock-ast :not :to-be nil))))) + + (it "can take a basic note" + (add-to-list 'org-noter-get-selected-text-hook #'org-noter-core-test-return-text) + (add-to-list 'org-noter-get-selected-text-hook #'wakka) + (add-to-list 'org-noter-parse-document-property-hook #'org-noter-core-test-document-property) + (add-to-list 'org-noter-set-up-document-hook #'org-noter-core-test-view-setup-handler) + (add-to-list 'org-noter-open-document-functions #'org-noter-core-test-open-document-functions) + (add-to-list 'org-noter--doc-approx-location-hook #'org-noter-core-test-approx-location) + (add-to-list 'org-noter--doc-approx-location-hook #'wakka1) + (add-to-list 'org-noter--get-current-view-hook #'org-noter-core-test-get-current-view) + + (with-mock-contents + mock-contents-simple-notes-file + '(lambda () + (message "Creating a session") + (org-noter--create-session (org-noter--parse-root) "NOTER_DOCUMENT" org-noter-test-file) + (message "Created a session") + (org-noter-insert-note nil "NEW NOTE") + (message "with note: %s" (buffer-string)) + (expect 'org-noter-core-test-return-text :to-have-been-called)))) ) + ) From ceb473b53fae9f523eb1012ecfb7e684e61229b9 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sun, 25 Dec 2022 18:37:43 -0800 Subject: [PATCH 185/453] Toggle an org-noter setting to make the test pass. --- tests/org-noter-core-tests.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/org-noter-core-tests.el b/tests/org-noter-core-tests.el index eb51b01..b4cb670 100644 --- a/tests/org-noter-core-tests.el +++ b/tests/org-noter-core-tests.el @@ -77,7 +77,9 @@ (describe "org-noter-core" (describe "note taking functionality" (before-each - (spy-on 'org-noter-core-test-return-text)) + ;; if this is not set; make-session fails and the test crashes with a stack overflow. + (setq org-noter-always-create-frame nil) + (spy-on 'org-noter-core-test-return-text)) (it "can parse a note file ast that is not empty" (with-mock-contents From 0114d66436e8bf1728e237d1c0fa6e8bb72b3b78 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sun, 25 Dec 2022 18:51:31 -0800 Subject: [PATCH 186/453] Clean up test code. --- tests/org-noter-core-tests.el | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/tests/org-noter-core-tests.el b/tests/org-noter-core-tests.el index b4cb670..81dae14 100644 --- a/tests/org-noter-core-tests.el +++ b/tests/org-noter-core-tests.el @@ -38,15 +38,15 @@ (message "+++++++++++++++++++++++++++++++++++++++++") )) -(defun wakka (mode) +(defun org-noter-test-get-selected-text (mode) (message "🧪org-noter-core-test-return-text") - "⚠️org-noter-core-test-return-text") - - + "⚠️org-noter-core-test-return-text +org-noter-core-test-return-text +org-noter-core-test-return-text +org-noter-core-test-return-text +org-noter-core-test-return-text +") -(defun org-noter-core-test-return-text (mode) - (message "org-noter-core-test-return-text") - "org-noter-core-test-return-text") (defun org-noter-core-test-document-property (&optional param) (message "🧪org-noter-core-test-document-property %s" param) @@ -60,11 +60,7 @@ (message "org-noter-core-test-open-document-functions") (find-file (org-noter-core-test-document-property))) -(defun org-noter-core-test-approx-location (&optional a b) - (message "approx-location") - (list 0 0 0 0 0)) - -(defun wakka1 (a b c) +(defun org-noter-core-test-approx-location (&optional a b c) (message "approx-location") (list 0 0 0 0 0)) @@ -79,7 +75,8 @@ (before-each ;; if this is not set; make-session fails and the test crashes with a stack overflow. (setq org-noter-always-create-frame nil) - (spy-on 'org-noter-core-test-return-text)) + (spy-on 'org-noter-test-get-selected-text) + ) (it "can parse a note file ast that is not empty" (with-mock-contents @@ -89,23 +86,20 @@ (expect mock-ast :not :to-be nil))))) (it "can take a basic note" - (add-to-list 'org-noter-get-selected-text-hook #'org-noter-core-test-return-text) - (add-to-list 'org-noter-get-selected-text-hook #'wakka) + (add-to-list 'org-noter-get-selected-text-hook #'org-noter-test-get-selected-text) (add-to-list 'org-noter-parse-document-property-hook #'org-noter-core-test-document-property) (add-to-list 'org-noter-set-up-document-hook #'org-noter-core-test-view-setup-handler) (add-to-list 'org-noter-open-document-functions #'org-noter-core-test-open-document-functions) (add-to-list 'org-noter--doc-approx-location-hook #'org-noter-core-test-approx-location) - (add-to-list 'org-noter--doc-approx-location-hook #'wakka1) (add-to-list 'org-noter--get-current-view-hook #'org-noter-core-test-get-current-view) (with-mock-contents mock-contents-simple-notes-file '(lambda () - (message "Creating a session") (org-noter--create-session (org-noter--parse-root) "NOTER_DOCUMENT" org-noter-test-file) - (message "Created a session") (org-noter-insert-note nil "NEW NOTE") (message "with note: %s" (buffer-string)) - (expect 'org-noter-core-test-return-text :to-have-been-called)))) + (expect 'org-noter-test-get-selected-text :to-have-been-called) + (expect (string-match "Notes for page" (buffer-string)) :not :to-be nil)))) ) ) From e087b4bd4c044c483ea4b43db77e9a61bd4a3595 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sun, 25 Dec 2022 18:51:52 -0800 Subject: [PATCH 187/453] Comment out pdf highlights from the core code. --- org-noter-core.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org-noter-core.el b/org-noter-core.el index 7da4290..5eb224b 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -2043,7 +2043,7 @@ See `org-noter-insert-note' docstring for more." (precise-info (org-noter--get-precise-info))) (message (format "dmitry --- %s" precise-info)) ;; TODO: This is all wrong. - (pdf-annot-add-highlight-markup-annotation precise-info) + ;;(pdf-annot-add-highlight-markup-annotation precise-info) (org-noter-insert-note precise-info)))) From e4655b42aa5d97fd82182b7c12e4f958dfc67f60 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Mon, 26 Dec 2022 17:16:54 -0800 Subject: [PATCH 188/453] Add with-simulated-input to enter precise note heading. --- Cask | 2 ++ tests/org-noter-core-tests.el | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/Cask b/Cask index 698eaff..45f15a5 100644 --- a/Cask +++ b/Cask @@ -3,3 +3,5 @@ (depends-on "buttercup") (depends-on "pdf-tools") + +(depends-on "with-simulated-input") \ No newline at end of file diff --git a/tests/org-noter-core-tests.el b/tests/org-noter-core-tests.el index 81dae14..7dbe917 100644 --- a/tests/org-noter-core-tests.el +++ b/tests/org-noter-core-tests.el @@ -1,5 +1,6 @@ (add-to-list 'load-path "modules") (require 'org-noter-pdf) +(require 'with-simulated-input) (defvar mock-contents-simple-notes-file "* solove-nothing-to-hide @@ -102,4 +103,25 @@ org-noter-core-test-return-text (expect 'org-noter-test-get-selected-text :to-have-been-called) (expect (string-match "Notes for page" (buffer-string)) :not :to-be nil)))) ) + + + (it "can take a precise note" + (add-to-list 'org-noter-get-selected-text-hook #'org-noter-test-get-selected-text) + (add-to-list 'org-noter-parse-document-property-hook #'org-noter-core-test-document-property) + (add-to-list 'org-noter-set-up-document-hook #'org-noter-core-test-view-setup-handler) + (add-to-list 'org-noter-open-document-functions #'org-noter-core-test-open-document-functions) + (add-to-list 'org-noter--doc-approx-location-hook #'org-noter-core-test-approx-location) + (add-to-list 'org-noter--get-current-view-hook #'org-noter-core-test-get-current-view) + + (with-mock-contents + mock-contents-simple-notes-file + '(lambda () + (org-noter--create-session (org-noter--parse-root) "NOTER_DOCUMENT" org-noter-test-file) + (with-simulated-input "precise SPC note RET" + (org-noter-insert-precise-note)) + (message "with note: %s" (buffer-string)) + (expect (string-match "precise note" (buffer-string)) :not :to-be nil) + ))) + + ) From e19c2f684f61ae5aceed5bff39135003d5a5f508 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Mon, 26 Dec 2022 18:41:34 -0800 Subject: [PATCH 189/453] Commenting on org-noter-core functionality --- org-noter-core.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/org-noter-core.el b/org-noter-core.el index 5eb224b..df48779 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -385,7 +385,9 @@ major modes uses the `buffer-file-name' variable." :type 'hook) (defcustom org-noter--doc-approx-location-hook nil - "TODO" + "This returns an approximate location if no precise info is passed: (PAGE 0) + or if precise info is passed, it's (PAGE 0 0 0 0) where 0s are the precise coords) +" :group 'org-noter :type 'hook) From 338cf1dd986979482bd619026a70ff89a22d2b65 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Mon, 26 Dec 2022 18:42:21 -0800 Subject: [PATCH 190/453] Refactored test setup; precise note test implementation. --- tests/org-noter-core-tests.el | 63 +++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/tests/org-noter-core-tests.el b/tests/org-noter-core-tests.el index 7dbe917..1855ae7 100644 --- a/tests/org-noter-core-tests.el +++ b/tests/org-noter-core-tests.el @@ -61,24 +61,45 @@ org-noter-core-test-return-text (message "org-noter-core-test-open-document-functions") (find-file (org-noter-core-test-document-property))) -(defun org-noter-core-test-approx-location (&optional a b c) - (message "approx-location") - (list 0 0 0 0 0)) +(defun org-noter-core-test-approx-location (major-mode &optional precise-info _force-new-ref) + (message "approx-location %s" precise-info) + (cons 99 precise-info)) (defun org-noter-core-test-get-current-view (mode) t) +(defun org-noter-core-test-get-precise-info (mode) + (message "🧪org-noter-core-test-get-precise-info %s" mode) + (list 1 2 3 4)) +(defun org-noter-core-test-pretty-print-location (location) + (format "%s" location)) (describe "org-noter-core" - (describe "note taking functionality" (before-each ;; if this is not set; make-session fails and the test crashes with a stack overflow. (setq org-noter-always-create-frame nil) - (spy-on 'org-noter-test-get-selected-text) + + + ;; setup spies so we can verify that things have been called + (spy-on 'org-noter-test-get-selected-text :and-call-through) + (spy-on 'org-noter-core-test-approx-location :and-call-through) + (spy-on 'org-noter-core-test-get-precise-info :and-call-through) + + + + (add-to-list 'org-noter-get-selected-text-hook #'org-noter-test-get-selected-text) + (add-to-list 'org-noter-parse-document-property-hook #'org-noter-core-test-document-property) + (add-to-list 'org-noter-set-up-document-hook #'org-noter-core-test-view-setup-handler) + (add-to-list 'org-noter-open-document-functions #'org-noter-core-test-open-document-functions) + (add-to-list 'org-noter--doc-approx-location-hook #'org-noter-core-test-approx-location) + (add-to-list 'org-noter--get-current-view-hook #'org-noter-core-test-get-current-view) + (add-to-list 'org-noter--get-precise-info-hook #'org-noter-core-test-get-precise-info) + (add-to-list 'org-noter--pretty-print-location-hook #'org-noter-core-test-pretty-print-location) ) + (describe "note taking functionality" (it "can parse a note file ast that is not empty" (with-mock-contents mock-contents-simple-notes-file @@ -87,13 +108,6 @@ org-noter-core-test-return-text (expect mock-ast :not :to-be nil))))) (it "can take a basic note" - (add-to-list 'org-noter-get-selected-text-hook #'org-noter-test-get-selected-text) - (add-to-list 'org-noter-parse-document-property-hook #'org-noter-core-test-document-property) - (add-to-list 'org-noter-set-up-document-hook #'org-noter-core-test-view-setup-handler) - (add-to-list 'org-noter-open-document-functions #'org-noter-core-test-open-document-functions) - (add-to-list 'org-noter--doc-approx-location-hook #'org-noter-core-test-approx-location) - (add-to-list 'org-noter--get-current-view-hook #'org-noter-core-test-get-current-view) - (with-mock-contents mock-contents-simple-notes-file '(lambda () @@ -102,17 +116,18 @@ org-noter-core-test-return-text (message "with note: %s" (buffer-string)) (expect 'org-noter-test-get-selected-text :to-have-been-called) (expect (string-match "Notes for page" (buffer-string)) :not :to-be nil)))) - ) - (it "can take a precise note" - (add-to-list 'org-noter-get-selected-text-hook #'org-noter-test-get-selected-text) - (add-to-list 'org-noter-parse-document-property-hook #'org-noter-core-test-document-property) - (add-to-list 'org-noter-set-up-document-hook #'org-noter-core-test-view-setup-handler) - (add-to-list 'org-noter-open-document-functions #'org-noter-core-test-open-document-functions) - (add-to-list 'org-noter--doc-approx-location-hook #'org-noter-core-test-approx-location) - (add-to-list 'org-noter--get-current-view-hook #'org-noter-core-test-get-current-view) + (with-mock-contents + mock-contents-simple-notes-file + '(lambda () + (org-noter--create-session (org-noter--parse-root) "NOTER_DOCUMENT" org-noter-test-file) + (with-simulated-input "precise SPC note RET" + (org-noter-insert-precise-note)) + (message "with note: %s" (buffer-string)) + (expect (string-match "precise note" (buffer-string)) :not :to-be nil)))) + (it "precise note has precise data" (with-mock-contents mock-contents-simple-notes-file '(lambda () @@ -120,8 +135,14 @@ org-noter-core-test-return-text (with-simulated-input "precise SPC note RET" (org-noter-insert-precise-note)) (message "with note: %s" (buffer-string)) - (expect (string-match "precise note" (buffer-string)) :not :to-be nil) + + (expect (string-match "NOTER_PAGE:" (buffer-string)) :not :to-be nil) + (expect (string-match "BEGIN_QUOTE" (buffer-string)) :not :to-be nil) + (expect 'org-noter-core-test-get-precise-info :to-have-been-called) ))) + + ) +) From 1e762af2e3110bc6696082cfe72ce44a85744c54 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Tue, 27 Dec 2022 10:50:39 -0800 Subject: [PATCH 191/453] Add a utility method to create a noter sessions. --- tests/org-noter-core-tests.el | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/org-noter-core-tests.el b/tests/org-noter-core-tests.el index 1855ae7..bb399c3 100644 --- a/tests/org-noter-core-tests.el +++ b/tests/org-noter-core-tests.el @@ -75,6 +75,8 @@ org-noter-core-test-return-text (defun org-noter-core-test-pretty-print-location (location) (format "%s" location)) +(defun org-noter-core-test-create-session () + (org-noter--create-session (org-noter--parse-root) "NOTER_DOCUMENT" org-noter-test-file)) (describe "org-noter-core" (before-each @@ -111,7 +113,7 @@ org-noter-core-test-return-text (with-mock-contents mock-contents-simple-notes-file '(lambda () - (org-noter--create-session (org-noter--parse-root) "NOTER_DOCUMENT" org-noter-test-file) + (org-noter-core-test-create-session) (org-noter-insert-note nil "NEW NOTE") (message "with note: %s" (buffer-string)) (expect 'org-noter-test-get-selected-text :to-have-been-called) @@ -121,7 +123,7 @@ org-noter-core-test-return-text (with-mock-contents mock-contents-simple-notes-file '(lambda () - (org-noter--create-session (org-noter--parse-root) "NOTER_DOCUMENT" org-noter-test-file) + (org-noter-core-test-create-session) (with-simulated-input "precise SPC note RET" (org-noter-insert-precise-note)) (message "with note: %s" (buffer-string)) @@ -131,7 +133,7 @@ org-noter-core-test-return-text (with-mock-contents mock-contents-simple-notes-file '(lambda () - (org-noter--create-session (org-noter--parse-root) "NOTER_DOCUMENT" org-noter-test-file) + (org-noter-core-test-create-session) (with-simulated-input "precise SPC note RET" (org-noter-insert-precise-note)) (message "with note: %s" (buffer-string)) From 47d3feb847319cce4f03d52a727b688fd23dccce Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Tue, 27 Dec 2022 11:11:35 -0800 Subject: [PATCH 192/453] [feature] Add a precise highlight hook to core. Remove hardcoded pdf precise note highlight in core and move it to pdf via the new hook. Add tests. --- modules/org-noter-pdf.el | 8 ++++++++ org-noter-core.el | 12 +++++++++--- tests/org-noter-core-tests.el | 24 ++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index a45904d..3f6d163 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -316,5 +316,13 @@ (pdf-annot-add-highlight-markup-annotation (cdr location))))) + + +(add-to-list 'org-noter-highlight-precise-note-hook #'org-noter-pdf-highlight-location) +(defun org-noter-pdf-highlight-location (mode precise-location) + "Highlight a precise location in PDF" + (when (memq major-mode '(doc-view-mode pdf-view-mode)) + (pdf-annot-add-highlight-markup-annotation precise-info))) + (provide 'org-noter-pdf) ;;; org-noter-pdf.el ends here diff --git a/org-noter-core.el b/org-noter-core.el index df48779..1189ec2 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -407,6 +407,14 @@ Used by `org-noter--create-session' when creating a new session." :group 'org-noter :type 'hook) +(defcustom org-noter-highlight-precise-note-hook nil + "When a precise note is created this will be called with the `MAJOR-MODE' and `PRECISE-INFO'. +This can be used in pdf-mode for example to add a permanent highlight to the document." + :group 'org-noter + :type 'hook) + + + ;; -------------------------------------------------------------------------------- ;;; Private variables or constants (cl-defstruct org-noter--session @@ -2043,9 +2051,7 @@ See `org-noter-insert-note' docstring for more." (not org-noter-insert-note-no-questions) org-noter-insert-note-no-questions)) (precise-info (org-noter--get-precise-info))) - (message (format "dmitry --- %s" precise-info)) - ;; TODO: This is all wrong. - ;;(pdf-annot-add-highlight-markup-annotation precise-info) + (run-hook-with-args-until-success 'org-noter-highlight-precise-note-hook major-mode precise-info) (org-noter-insert-note precise-info)))) diff --git a/tests/org-noter-core-tests.el b/tests/org-noter-core-tests.el index bb399c3..16387b7 100644 --- a/tests/org-noter-core-tests.el +++ b/tests/org-noter-core-tests.el @@ -75,6 +75,10 @@ org-noter-core-test-return-text (defun org-noter-core-test-pretty-print-location (location) (format "%s" location)) +(defun org-noter-core-test-highlight-location (major-mode precise-info) + t) + + (defun org-noter-core-test-create-session () (org-noter--create-session (org-noter--parse-root) "NOTER_DOCUMENT" org-noter-test-file)) @@ -88,6 +92,7 @@ org-noter-core-test-return-text (spy-on 'org-noter-test-get-selected-text :and-call-through) (spy-on 'org-noter-core-test-approx-location :and-call-through) (spy-on 'org-noter-core-test-get-precise-info :and-call-through) + (spy-on 'org-noter-core-test-highlight-location :and-call-through) @@ -99,6 +104,9 @@ org-noter-core-test-return-text (add-to-list 'org-noter--get-current-view-hook #'org-noter-core-test-get-current-view) (add-to-list 'org-noter--get-precise-info-hook #'org-noter-core-test-get-precise-info) (add-to-list 'org-noter--pretty-print-location-hook #'org-noter-core-test-pretty-print-location) + (add-to-list 'org-noter--pretty-print-location-hook #'org-noter-core-test-pretty-print-location) + (add-to-list 'org-noter-highlight-precise-note-hook #'org-noter-core-test-highlight-location) + ) (describe "note taking functionality" @@ -143,6 +151,22 @@ org-noter-core-test-return-text (expect 'org-noter-core-test-get-precise-info :to-have-been-called) ))) + (it "precise note calls the highlight hook" + (with-mock-contents + mock-contents-simple-notes-file + '(lambda () + (org-noter-core-test-create-session) + (with-simulated-input "precise SPC note RET" + (org-noter-insert-precise-note)) + (message "with note: %s" (buffer-string)) + + (expect 'org-noter-core-test-highlight-location :to-have-been-called) + (expect (spy-calls-all-args 'org-noter-core-test-highlight-location) + :to-equal + '((org-mode + (1 2 3 4))))) + )) + From aede53b400b05307208f58dea77bb5f303f8b801 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Tue, 27 Dec 2022 18:35:43 -0800 Subject: [PATCH 193/453] Insert a highlight only when the note is added. In other words; no highlight when note is aborted. --- org-noter-core.el | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 1189ec2..79affd3 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -2051,8 +2051,10 @@ See `org-noter-insert-note' docstring for more." (not org-noter-insert-note-no-questions) org-noter-insert-note-no-questions)) (precise-info (org-noter--get-precise-info))) - (run-hook-with-args-until-success 'org-noter-highlight-precise-note-hook major-mode precise-info) - (org-noter-insert-note precise-info)))) + (org-noter-insert-note precise-info) + (select-frame-set-input-focus (org-noter--session-frame session)) + (select-window (get-buffer-window (org-noter--session-doc-buffer session))) + (run-hook-with-args-until-success 'org-noter-highlight-precise-note-hook major-mode precise-info)))) (defun org-noter-insert-note-toggle-no-questions () From 176bb9cafedb85309db8ed3152fdca328edd1a36 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Tue, 27 Dec 2022 18:36:41 -0800 Subject: [PATCH 194/453] Bug: check that the correct mode is set before adding a highlight. --- modules/org-noter-pdf.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 3f6d163..1bb4cc4 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -321,7 +321,7 @@ (add-to-list 'org-noter-highlight-precise-note-hook #'org-noter-pdf-highlight-location) (defun org-noter-pdf-highlight-location (mode precise-location) "Highlight a precise location in PDF" - (when (memq major-mode '(doc-view-mode pdf-view-mode)) + (when (memq mode '(doc-view-mode pdf-view-mode)) (pdf-annot-add-highlight-markup-annotation precise-info))) (provide 'org-noter-pdf) From e5dc6d165e59aa4ed94c325a83d316f21a4612f6 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Tue, 27 Dec 2022 18:45:38 -0800 Subject: [PATCH 195/453] Add test to make sure that when note is aborted no highlight is created. --- tests/org-noter-core-tests.el | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/org-noter-core-tests.el b/tests/org-noter-core-tests.el index 16387b7..b2d3848 100644 --- a/tests/org-noter-core-tests.el +++ b/tests/org-noter-core-tests.el @@ -170,5 +170,16 @@ org-noter-core-test-return-text + ;; hit C-g when entering a note; expect no highlight + (it "precise note DOES NOT call the highlight hook when the note is aborted" + (with-mock-contents + mock-contents-simple-notes-file + '(lambda () + (org-noter-core-test-create-session) + ;; this is how you trap a C-g + (condition-case nil + (with-simulated-input "C-g" (org-noter-insert-precise-note)) + (quit nil)) + (expect 'org-noter-core-test-highlight-location :not :to-have-been-called)))) ) ) From 0721bd92e811df698b3f78ea17ac3b0ff1ffd649 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Tue, 27 Dec 2022 18:46:19 -0800 Subject: [PATCH 196/453] Clean up tests/add comments. --- tests/org-noter-core-tests.el | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/tests/org-noter-core-tests.el b/tests/org-noter-core-tests.el index b2d3848..8ebd7cb 100644 --- a/tests/org-noter-core-tests.el +++ b/tests/org-noter-core-tests.el @@ -110,6 +110,7 @@ org-noter-core-test-return-text ) (describe "note taking functionality" + ;; checking to make sure that `with-mock-contents` works fine. (it "can parse a note file ast that is not empty" (with-mock-contents mock-contents-simple-notes-file @@ -117,6 +118,7 @@ org-noter-core-test-return-text (message "%s" mock-ast) (expect mock-ast :not :to-be nil))))) + ;; basic note should insert a default heading (it "can take a basic note" (with-mock-contents mock-contents-simple-notes-file @@ -127,6 +129,7 @@ org-noter-core-test-return-text (expect 'org-noter-test-get-selected-text :to-have-been-called) (expect (string-match "Notes for page" (buffer-string)) :not :to-be nil)))) + ;; enter a heading when taking a precise note; expect the heading to be there. (it "can take a precise note" (with-mock-contents mock-contents-simple-notes-file @@ -137,6 +140,7 @@ org-noter-core-test-return-text (message "with note: %s" (buffer-string)) (expect (string-match "precise note" (buffer-string)) :not :to-be nil)))) + ;; there should be precise data in the note properties when entering a precise note (it "precise note has precise data" (with-mock-contents mock-contents-simple-notes-file @@ -144,13 +148,12 @@ org-noter-core-test-return-text (org-noter-core-test-create-session) (with-simulated-input "precise SPC note RET" (org-noter-insert-precise-note)) - (message "with note: %s" (buffer-string)) - - (expect (string-match "NOTER_PAGE:" (buffer-string)) :not :to-be nil) - (expect (string-match "BEGIN_QUOTE" (buffer-string)) :not :to-be nil) + (expect (string-match "NOTER_PAGE:" (buffer-string)) :not :to-be nil) + (expect (string-match "BEGIN_QUOTE" (buffer-string)) :not :to-be nil) (expect 'org-noter-core-test-get-precise-info :to-have-been-called) ))) + ;; highlight code should be called when a precise note is entered (it "precise note calls the highlight hook" (with-mock-contents mock-contents-simple-notes-file @@ -158,17 +161,11 @@ org-noter-core-test-return-text (org-noter-core-test-create-session) (with-simulated-input "precise SPC note RET" (org-noter-insert-precise-note)) - (message "with note: %s" (buffer-string)) - (expect 'org-noter-core-test-highlight-location :to-have-been-called) (expect (spy-calls-all-args 'org-noter-core-test-highlight-location) :to-equal '((org-mode - (1 2 3 4))))) - )) - - - + (1 2 3 4))))))) ;; hit C-g when entering a note; expect no highlight (it "precise note DOES NOT call the highlight hook when the note is aborted" From 629fbb64220e36d7443b6c9a369a8a49d0874b6d Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Thu, 29 Dec 2022 10:06:13 -0800 Subject: [PATCH 197/453] [WIP] Try to deal with precise vs non precise location better. --- org-noter-core.el | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/org-noter-core.el b/org-noter-core.el index 79affd3..f8ac203 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1163,7 +1163,10 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (defun org-noter--get-location-page (location) "Get the page number given a LOCATION of form (page top . left) or (page . top)." - (car location)) + (message "===> %s" location) + (if (listp location) + (car location) + location)) (defun org-noter--get-location-left (location) "Get the left coordinate given a LOCATION of form (page top . left) or (page . top). If later form of vector is passed return 0." From 6ddca1111d1da4f2a3bc8458eb75afe68e3b4862 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Thu, 29 Dec 2022 10:06:50 -0800 Subject: [PATCH 198/453] Fix incorrect variable used in the call. --- modules/org-noter-pdf.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 1bb4cc4..619a4b3 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -322,7 +322,7 @@ (defun org-noter-pdf-highlight-location (mode precise-location) "Highlight a precise location in PDF" (when (memq mode '(doc-view-mode pdf-view-mode)) - (pdf-annot-add-highlight-markup-annotation precise-info))) + (pdf-annot-add-highlight-markup-annotation precise-location))) (provide 'org-noter-pdf) ;;; org-noter-pdf.el ends here From c095e8c0ddbee148e3418b78c3f560b3a31f0f5c Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Thu, 29 Dec 2022 10:08:44 -0800 Subject: [PATCH 199/453] Add startup script that is used for testing emacs sessions. --- emacs-devel.el | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 emacs-devel.el diff --git a/emacs-devel.el b/emacs-devel.el new file mode 100644 index 0000000..2cecb8e --- /dev/null +++ b/emacs-devel.el @@ -0,0 +1,7 @@ +(require 'cask "/opt/homebrew/share/emacs/site-lisp/cask/cask.el") +(cask-initialize ".") + +(setq mac-option-modifier 'meta) + +(push (expand-file-name ".") load-path) +(require 'org-noter) From 524f27131e1e02bd383262946cbcab5dfad9f229 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Fri, 30 Dec 2022 12:05:50 -0800 Subject: [PATCH 200/453] Adding some notes for context. --- docs/org_noter_tech_notes.org | 89 +++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 docs/org_noter_tech_notes.org diff --git a/docs/org_noter_tech_notes.org b/docs/org_noter_tech_notes.org new file mode 100644 index 0000000..950d158 --- /dev/null +++ b/docs/org_noter_tech_notes.org @@ -0,0 +1,89 @@ +:PROPERTIES: +:ID: 4333050B-D293-4A41-8A14-00E6248FD17B +:DRILL_LAST_INTERVAL: -1.0 +:DRILL_REPEATS_SINCE_FAIL: 1 +:DRILL_TOTAL_REPEATS: 1 +:DRILL_FAILURE_COUNT: 1 +:DRILL_AVERAGE_QUALITY: 1.0 +:DRILL_EASE: 2.5 +:NEXT_REVIEW: [2022-12-29 Thu] +:MATURITY: seedling +:LAST_REVIEW: [2022-12-30 Fri] +:END: +#+title: org-noter-tech-notes +#+filetags: :seedling: + +* Brief history of org-noter + +[[https://github.com/weirdNox/org-noter][org-noter]] (2018-2020), a re-implementation of the [[https://github.com/rudolfochrist/interleave/][interleave packaage]] (2015-2018) by weirdNox: + +#+begin_quote +Yeah, I made org-noter because it is something I need for studying everyday, but I bet that if I didn't use it, then I would probably lose interest too... We don't have time for everything, so decisions must be made :P +#+end_quote + + [[https://github.com/rudolfochrist/interleave/issues/55][source]] + +In early 2022, c1-g created a fork, [[https://github.com/c1-g/org-noter-plus-djvu][org-noter-plus-djvu]] that split up note creation functionality from the underlying document format making it possible to take notes with pdf, epub and djvu documents. + + +* Tech Notes + +** Session +org-noter session contains all the relevant info for the current session. +- notes file (or buffer) +- backing document +- ... + +prereq for getting everything else going see ==make-org-noter-session==. + +** Hooks +Hooks are used extensively to manage "non-core" functionality, that is functionality that is mode dependent. + +There are mode specific implementation in =modules/= directory, as well as in =tests/= (for testing). + + +*** Errors +An error like so: +#+begin_src shell + Lisp nesting exceeds ‘max-lisp-eval-depth’ +#+end_src + +possibly indicates that a ~run-hook-with-args-until-success~ has failed: + +#+begin_src elisp + (run-hook-with-args-until-success 'org-noter-set-up-document-hook document-property-value) +#+end_src + +in my experience the error indicates a problem with one of the hooks. For example in the code above one of the hooks in =org-noter-set-up-document-hook= may not be valid elisp code (requires more than one argument or another lisp issue). + +I haven't figured out a good way to identify these. + +** Notes + +There are two types of notes: + +- regular notes +- precise notes + + + +*** Regular Notes + +Notes attached to a "page". + +#+begin_src org-mode +:PROPERTIES: +:NOTER_PAGE: 2 +:END: +#+end_src + + +*** Precise notes + +Precise notes include a coordinate vector that allows to identify the selection in the backing document and act accordingly, ie create a highlight. + +#+begin_src org-mode +:PROPERTIES: +:NOTER_PAGE: (75 0.14417344173441735 0.7955390334572491 0.6834688346883468 0.8199091284593144) +:END: +#+end_src From 5e8bc112ceabf3aca92ab6e1cc57065e67da3f49 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Fri, 30 Dec 2022 12:17:41 -0800 Subject: [PATCH 201/453] Additional documentation. --- docs/org_noter_tech_notes.org | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/docs/org_noter_tech_notes.org b/docs/org_noter_tech_notes.org index 950d158..a7f39f8 100644 --- a/docs/org_noter_tech_notes.org +++ b/docs/org_noter_tech_notes.org @@ -34,7 +34,7 @@ org-noter session contains all the relevant info for the current session. - backing document - ... -prereq for getting everything else going see ==make-org-noter-session==. +prereq for getting everything else going see =make-org-noter-session=. ** Hooks Hooks are used extensively to manage "non-core" functionality, that is functionality that is mode dependent. @@ -87,3 +87,21 @@ Precise notes include a coordinate vector that allows to identify the selection :NOTER_PAGE: (75 0.14417344173441735 0.7955390334572491 0.6834688346883468 0.8199091284593144) :END: #+end_src + +** Locations + +Location is a property of every note (see [[Notes]]). + +The concept seems to be poorly defined currently, and most of the code lives in =core=, but maybe it should move to document implementation. + +Currently it's either + +=:NOTER_PAGE: (75 0.14417344173441735 0.7955390334572491 0.6834688346883468 0.8199091284593144)= + +or + +=:NOTER_PAGE: 2= + +** Note taking behavior + +Sophisticated note taking behavior is possible, based on selection size, etc see [[https://github.com/petermao/org-noter/blob/doc/README.org][Peter's matrix]]. From 892197f511793ec9fad598f7d0440c53fea52956 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Fri, 30 Dec 2022 12:22:01 -0800 Subject: [PATCH 202/453] Add TOC. --- docs/org_noter_tech_notes.org | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/org_noter_tech_notes.org b/docs/org_noter_tech_notes.org index a7f39f8..bf8b992 100644 --- a/docs/org_noter_tech_notes.org +++ b/docs/org_noter_tech_notes.org @@ -13,6 +13,17 @@ #+title: org-noter-tech-notes #+filetags: :seedling: +Context for developing org-noter. + +* TOC :TOC: +- [[#brief-history-of-org-noter][Brief history of org-noter]] +- [[#tech-notes][Tech Notes]] + - [[#session][Session]] + - [[#hooks][Hooks]] + - [[#notes][Notes]] + - [[#locations][Locations]] + - [[#note-taking-behavior][Note taking behavior]] + * Brief history of org-noter [[https://github.com/weirdNox/org-noter][org-noter]] (2018-2020), a re-implementation of the [[https://github.com/rudolfochrist/interleave/][interleave packaage]] (2015-2018) by weirdNox: From fdeb6bf3dd70cc74d0aa5feab7da2606c861fc05 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Fri, 30 Dec 2022 16:28:52 -0800 Subject: [PATCH 203/453] Remove incorrect hook. --- modules/org-noter-pdf.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 619a4b3..5d09d8b 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -42,7 +42,7 @@ (equal 5 (length (read property)))) -(add-to-list 'org-noter--check-location-property-hook #'org-noter-pdf-check-location-property) +;; (add-to-list 'org-noter--check-location-property-hook #'org-noter-pdf-check-location-property) (add-to-list 'org-noter--doc-approx-location-hook #'org-noter-pdf-approx-location-cons) (defun org-noter-pdf-view-setup-handler (major-mode) From e2e3447bce7c7bb81f56f1d1115a29e55b8907be Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Fri, 30 Dec 2022 16:30:03 -0800 Subject: [PATCH 204/453] Add a test for verifying view info functionality. --- tests/org-noter-core-tests.el | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/tests/org-noter-core-tests.el b/tests/org-noter-core-tests.el index 8ebd7cb..9f4ee6f 100644 --- a/tests/org-noter-core-tests.el +++ b/tests/org-noter-core-tests.el @@ -9,6 +9,19 @@ :END: ") +(defvar mock-contents-simple-notes-file-with-a-single-note + "* solove-nothing-to-hide +:PROPERTIES: +:NOTER_DOCUMENT: pubs/solove-nothing-to-hide.pdf +:END: +** Note from page 1 +:PROPERTIES: +:NOTER_PAGE: 99 +:END: + +") + + (defun with-mock-contents (contents lambda) @@ -82,6 +95,9 @@ org-noter-core-test-return-text (defun org-noter-core-test-create-session () (org-noter--create-session (org-noter--parse-root) "NOTER_DOCUMENT" org-noter-test-file)) +(defun org-noter-core-test-get-current-view (mode) + 'org-noter-core-test-view) + (describe "org-noter-core" (before-each ;; if this is not set; make-session fails and the test crashes with a stack overflow. @@ -95,6 +111,7 @@ org-noter-core-test-return-text (spy-on 'org-noter-core-test-highlight-location :and-call-through) + (spy-on 'org-noter-core-test-get-current-view :and-call-through) (add-to-list 'org-noter-get-selected-text-hook #'org-noter-test-get-selected-text) (add-to-list 'org-noter-parse-document-property-hook #'org-noter-core-test-document-property) @@ -131,14 +148,18 @@ org-noter-core-test-return-text ;; enter a heading when taking a precise note; expect the heading to be there. (it "can take a precise note" + (it "can get view info" (with-mock-contents - mock-contents-simple-notes-file + mock-contents-simple-notes-file-with-a-single-note '(lambda () (org-noter-core-test-create-session) - (with-simulated-input "precise SPC note RET" - (org-noter-insert-precise-note)) - (message "with note: %s" (buffer-string)) - (expect (string-match "precise note" (buffer-string)) :not :to-be nil)))) + (let* ((session org-noter--session) + (view-info (org-noter--get-view-info (org-noter--get-current-view)))) + (message "%s" view-info) + (expect 'org-noter-core-test-get-current-view :to-have-been-called) + ;; Next TODO: why is the buffer being set incorrectly. + (expect (buffer-name (org-noter--session-notes-buffer session)) :to-equal "Notes of solove-nothing-to-hide") + )))) ;; there should be precise data in the note properties when entering a precise note (it "precise note has precise data" From 0c9b4834bb315150010bd96573962bdb2fe6e4ab Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Fri, 30 Dec 2022 18:30:26 -0800 Subject: [PATCH 205/453] Add header to test notes. org-noter--parse-root doesn't work without it. --- tests/org-noter-core-tests.el | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/tests/org-noter-core-tests.el b/tests/org-noter-core-tests.el index 9f4ee6f..c6f7a6c 100644 --- a/tests/org-noter-core-tests.el +++ b/tests/org-noter-core-tests.el @@ -3,14 +3,24 @@ (require 'with-simulated-input) (defvar mock-contents-simple-notes-file - "* solove-nothing-to-hide + " +:PROPERTIES: +:ID: FAKE_1 +:END: +#+TITLE: Test book notes (simple) +* solove-nothing-to-hide :PROPERTIES: :NOTER_DOCUMENT: pubs/solove-nothing-to-hide.pdf :END: ") (defvar mock-contents-simple-notes-file-with-a-single-note - "* solove-nothing-to-hide + ":PROPERTIES: +:ID: FAKE_90283 +:END: +#+TITLE: Test book notes + +* solove-nothing-to-hide :PROPERTIES: :NOTER_DOCUMENT: pubs/solove-nothing-to-hide.pdf :END: @@ -18,8 +28,8 @@ :PROPERTIES: :NOTER_PAGE: 99 :END: - -") +" +) From 35f3dc7a7848d64df928bca67a39cd1c1911f1d8 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Fri, 30 Dec 2022 18:32:23 -0800 Subject: [PATCH 206/453] Add a test for session creation and view-info. --- tests/org-noter-core-tests.el | 44 ++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/tests/org-noter-core-tests.el b/tests/org-noter-core-tests.el index c6f7a6c..ad66340 100644 --- a/tests/org-noter-core-tests.el +++ b/tests/org-noter-core-tests.el @@ -158,18 +158,14 @@ org-noter-core-test-return-text ;; enter a heading when taking a precise note; expect the heading to be there. (it "can take a precise note" - (it "can get view info" (with-mock-contents - mock-contents-simple-notes-file-with-a-single-note + mock-contents-simple-notes-file '(lambda () (org-noter-core-test-create-session) - (let* ((session org-noter--session) - (view-info (org-noter--get-view-info (org-noter--get-current-view)))) - (message "%s" view-info) - (expect 'org-noter-core-test-get-current-view :to-have-been-called) - ;; Next TODO: why is the buffer being set incorrectly. - (expect (buffer-name (org-noter--session-notes-buffer session)) :to-equal "Notes of solove-nothing-to-hide") - )))) + (with-simulated-input "precise SPC note RET" + (org-noter-insert-precise-note)) + (message "with note: %s" (buffer-string)) + (expect (string-match "precise note" (buffer-string)) :not :to-be nil)))) ;; there should be precise data in the note properties when entering a precise note (it "precise note has precise data" @@ -209,5 +205,35 @@ org-noter-core-test-return-text (with-simulated-input "C-g" (org-noter-insert-precise-note)) (quit nil)) (expect 'org-noter-core-test-highlight-location :not :to-have-been-called)))) + + + + (describe "session creation" + ;; check that the narrowed buffer is named correctly + (it "narrowed buffer is named correctly" + (with-mock-contents + mock-contents-simple-notes-file-with-a-single-note + '(lambda () + (org-noter-core-test-create-session) + (let* ((session org-noter--session)) + (expect (buffer-name (org-noter--session-notes-buffer session)) :to-equal "Notes of solove-nothing-to-hide") + )))) + + ) + + + + (describe "view-info" + (it "can get view info" + (with-mock-contents + mock-contents-simple-notes-file-with-a-single-note + '(lambda () + (org-noter-core-test-create-session) + (let* ((view-info (org-noter--get-view-info (org-noter--get-current-view)))) + (message "%s" view-info) + (expect 'org-noter-core-test-get-current-view :to-have-been-called) + )))) + ) + ) ) From 5002df73c41f7d89c902c732f44b643a3d6f2bf2 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Fri, 30 Dec 2022 18:33:00 -0800 Subject: [PATCH 207/453] Add additional notes. --- docs/org_noter_tech_notes.org | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/docs/org_noter_tech_notes.org b/docs/org_noter_tech_notes.org index bf8b992..2186696 100644 --- a/docs/org_noter_tech_notes.org +++ b/docs/org_noter_tech_notes.org @@ -23,6 +23,12 @@ Context for developing org-noter. - [[#notes][Notes]] - [[#locations][Locations]] - [[#note-taking-behavior][Note taking behavior]] + - [[#notes-file][Notes file]] +- [[#solove-nothing-to-hide][solove-nothing-to-hide]] + - [[#note-from-page-1][Note from page 1]] +- [[#development][Development]] + - [[#requirements][Requirements]] + - [[#unit-tests][Unit tests]] * Brief history of org-noter @@ -116,3 +122,30 @@ or ** Note taking behavior Sophisticated note taking behavior is possible, based on selection size, etc see [[https://github.com/petermao/org-noter/blob/doc/README.org][Peter's matrix]]. + + +** Notes file + +=notes.org= is in this format: + +#+begin_src org-mode +:PROPERTIES: +:ID: FAKE_90283 +:END: +#+TITLE: Test book notes + +* solove-nothing-to-hide +:PROPERTIES: +:NOTER_DOCUMENT: pubs/solove-nothing-to-hide.pdf +:END: +** Note from page 1 +:PROPERTIES: +:NOTER_PAGE: 99 +:END: +#+end_src + +Omitting the header causes =org-noter--parse-root= to work incorrectly. + +* Development +** Requirements +** Unit tests From 45f97103451b1c3b9b56fd383c6c02acfd4a5611 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Fri, 30 Dec 2022 18:41:34 -0800 Subject: [PATCH 208/453] Pull notes, view-info and session tests into their own sections. --- tests/org-noter-core-tests.el | 71 +++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/tests/org-noter-core-tests.el b/tests/org-noter-core-tests.el index ad66340..9506637 100644 --- a/tests/org-noter-core-tests.el +++ b/tests/org-noter-core-tests.el @@ -32,6 +32,11 @@ ) +;;;;;;;;;;; +;; helpers +(defun org-noter-core-test-create-session () + "Call this manually with an existing notes buffer to generate a new session" + (org-noter--create-session (org-noter--parse-root) "some-pdf" org-noter-test-file)) (defun with-mock-contents (contents lambda) @@ -62,6 +67,10 @@ (message "+++++++++++++++++++++++++++++++++++++++++") )) + +;;;;;;;;;;;;;;;;;;;;;;;;;; +;; hooks - org-noter calls these + (defun org-noter-test-get-selected-text (mode) (message "🧪org-noter-core-test-return-text") "⚠️org-noter-core-test-return-text @@ -101,13 +110,10 @@ org-noter-core-test-return-text (defun org-noter-core-test-highlight-location (major-mode precise-info) t) - -(defun org-noter-core-test-create-session () - (org-noter--create-session (org-noter--parse-root) "NOTER_DOCUMENT" org-noter-test-file)) - (defun org-noter-core-test-get-current-view (mode) 'org-noter-core-test-view) + (describe "org-noter-core" (before-each ;; if this is not set; make-session fails and the test crashes with a stack overflow. @@ -119,8 +125,6 @@ org-noter-core-test-return-text (spy-on 'org-noter-core-test-approx-location :and-call-through) (spy-on 'org-noter-core-test-get-precise-info :and-call-through) (spy-on 'org-noter-core-test-highlight-location :and-call-through) - - (spy-on 'org-noter-core-test-get-current-view :and-call-through) (add-to-list 'org-noter-get-selected-text-hook #'org-noter-test-get-selected-text) @@ -136,6 +140,8 @@ org-noter-core-test-return-text ) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (describe "note taking functionality" ;; checking to make sure that `with-mock-contents` works fine. (it "can parse a note file ast that is not empty" @@ -206,34 +212,35 @@ org-noter-core-test-return-text (quit nil)) (expect 'org-noter-core-test-highlight-location :not :to-have-been-called)))) + ) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (describe "session creation" + ;; check that the narrowed buffer is named correctly + (it "narrowed buffer is named correctly" + (with-mock-contents + mock-contents-simple-notes-file-with-a-single-note + '(lambda () + (org-noter-core-test-create-session) + (let* ((session org-noter--session)) + (expect (buffer-name (org-noter--session-notes-buffer session)) :to-equal "Notes of solove-nothing-to-hide") + )))) - (describe "session creation" - ;; check that the narrowed buffer is named correctly - (it "narrowed buffer is named correctly" - (with-mock-contents - mock-contents-simple-notes-file-with-a-single-note - '(lambda () - (org-noter-core-test-create-session) - (let* ((session org-noter--session)) - (expect (buffer-name (org-noter--session-notes-buffer session)) :to-equal "Notes of solove-nothing-to-hide") - )))) - - ) - - + ) - (describe "view-info" - (it "can get view info" - (with-mock-contents - mock-contents-simple-notes-file-with-a-single-note - '(lambda () - (org-noter-core-test-create-session) - (let* ((view-info (org-noter--get-view-info (org-noter--get-current-view)))) - (message "%s" view-info) - (expect 'org-noter-core-test-get-current-view :to-have-been-called) - )))) - ) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (describe "view-info" + (it "can get view info" + (with-mock-contents + mock-contents-simple-notes-file-with-a-single-note + '(lambda () + (org-noter-core-test-create-session) + (let* ((view-info (org-noter--get-view-info (org-noter--get-current-view)))) + (message "%s" view-info) + (expect 'org-noter-core-test-get-current-view :to-have-been-called) + )))) + ) - ) ) From cca480a7b72290021aafe57eb5eef698e86e86e8 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sat, 31 Dec 2022 10:44:02 -0800 Subject: [PATCH 209/453] How to run unit tests. --- docs/org_noter_tech_notes.org | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/org_noter_tech_notes.org b/docs/org_noter_tech_notes.org index 2186696..44ab856 100644 --- a/docs/org_noter_tech_notes.org +++ b/docs/org_noter_tech_notes.org @@ -27,7 +27,6 @@ Context for developing org-noter. - [[#solove-nothing-to-hide][solove-nothing-to-hide]] - [[#note-from-page-1][Note from page 1]] - [[#development][Development]] - - [[#requirements][Requirements]] - [[#unit-tests][Unit tests]] * Brief history of org-noter @@ -147,5 +146,16 @@ Sophisticated note taking behavior is possible, based on selection size, etc see Omitting the header causes =org-noter--parse-root= to work incorrectly. * Development -** Requirements ** Unit tests +*** Requirements + +- [[https://github.com/cask/cask][Cask]], a project management tool for Emacs +- [[https://github.com/jorgenschaefer/emacs-buttercup][Buttercup]], behavior driven Emacs testing + +*** Mac + +#+begin_src shell + brew install cask + cask # install dependencies + cask exec buttercup -L . # in the root of the project +#+end_src From 9a171cc1558d92b62d1e81e5bab9f413a36b2ce5 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sat, 31 Dec 2022 10:45:33 -0800 Subject: [PATCH 210/453] Additional documentation. --- docs/org_noter_tech_notes.org | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/org_noter_tech_notes.org b/docs/org_noter_tech_notes.org index 44ab856..da8143c 100644 --- a/docs/org_noter_tech_notes.org +++ b/docs/org_noter_tech_notes.org @@ -157,5 +157,5 @@ Omitting the header causes =org-noter--parse-root= to work incorrectly. #+begin_src shell brew install cask cask # install dependencies - cask exec buttercup -L . # in the root of the project + cask exec buttercup -L . # exec unit tests, in the root of the project #+end_src From da3b5b475d327a56f75681f2b283301d4284dafc Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sat, 31 Dec 2022 12:03:33 -0800 Subject: [PATCH 211/453] Add tests: verify that session is created correctly. --- tests/org-noter-core-tests.el | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/org-noter-core-tests.el b/tests/org-noter-core-tests.el index 9506637..99a1be5 100644 --- a/tests/org-noter-core-tests.el +++ b/tests/org-noter-core-tests.el @@ -36,7 +36,7 @@ ;; helpers (defun org-noter-core-test-create-session () "Call this manually with an existing notes buffer to generate a new session" - (org-noter--create-session (org-noter--parse-root) "some-pdf" org-noter-test-file)) + (org-noter--create-session (org-noter--parse-root) "pubs/solove-nothing-to-hide.pdf" org-noter-test-file)) (defun with-mock-contents (contents lambda) @@ -227,8 +227,25 @@ org-noter-core-test-return-text (expect (buffer-name (org-noter--session-notes-buffer session)) :to-equal "Notes of solove-nothing-to-hide") )))) + ;; check that session properties are set correctly + (it "session properties are set correctly" + (with-mock-contents + mock-contents-simple-notes-file-with-a-single-note + '(lambda () + (org-noter-core-test-create-session) + (let* ((session org-noter--session)) + (message "0000000000000000000000 %s" (type-of (org-noter--session-display-name session))) + (expect (org-noter--session-property-text session) :to-equal "pubs/solove-nothing-to-hide.pdf") + (expect (org-noter--session-display-name session) :to-equal "solove-nothing-to-hide") + (expect (org-noter--session-notes-file-path session) :to-equal org-noter-test-file) + (expect (buffer-file-name (org-noter--session-notes-buffer session)) :to-equal org-noter-test-file) + ;; TODO: Need test-specific-major mode somehow? + ;; (expect (org-noter--session-doc-mode session) :to-equal 'org-core-test) + )))) + ) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (describe "view-info" From 77f6fca31ed91237b01e9fe8e0d57490070f857d Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sun, 1 Jan 2023 20:07:00 -0800 Subject: [PATCH 212/453] Add github action for building/testing. --- .github/workflows/build-test.yml | 19 +++++++++++ org-noter-core.el | 57 +++++++++++++++++++++++++++++--- org-noter.el | 4 +++ 3 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/build-test.yml diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml new file mode 100644 index 0000000..2caf59e --- /dev/null +++ b/.github/workflows/build-test.yml @@ -0,0 +1,19 @@ +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: actions/setup-python@v2 + with: + python-version: '3.6' + architecture: 'x64' + - uses: purcell/setup-emacs@master + with: + version: '28' + + - uses: conao3/setup-cask@master + with: + version: 'snapshot' + + - name: Run tests + run: cask exec buttercup -L . diff --git a/org-noter-core.el b/org-noter-core.el index f8ac203..d5d0cab 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -526,6 +526,7 @@ Otherwise return the maximum value for point." (defun org-noter--create-session (ast document-property-value notes-file-path) (let* ((raw-value-not-empty (> (length (org-element-property :raw-value ast)) 0)) + (dummy (message "----1")) (link-p (or (string-match-p org-bracket-link-regexp document-property-value) (string-match-p org-noter--url-regexp document-property-value))) (display-name (if raw-value-not-empty @@ -534,19 +535,30 @@ Otherwise return the maximum value for point." document-property-value (file-name-nondirectory document-property-value)))) + (dummy1 (message "\n!----99 %s" display-name)) + (dummy1 (message "\n!----98 %s" (org-element-property :raw-value ast))) + (frame-name (format "Emacs Org-noter - %s" display-name)) (document (or (run-hook-with-args-until-success 'org-noter-open-document-functions document-property-value) (if link-p (progn (org-link-open-from-string document-property-value) (current-buffer)) (find-file-noselect document-property-value)))) + (dummy (message "----1.5 doc %s %s" document document-property-value)) (document-major-mode (if (or link-p (eq document (current-buffer))) document-property-value (buffer-local-value 'major-mode document))) + + (dummy (message "----2 %s" document-major-mode)) + + (dummu (message "1display name = %s" display-name)) (document-buffer-name (generate-new-buffer-name (concat (unless raw-value-not-empty "Org-noter: ") display-name))) (document-buffer document) + + + (notes-buffer (progn (when (and org-window-config-before-follow-link link-p) (set-window-configuration org-window-config-before-follow-link)) @@ -557,11 +569,17 @@ Otherwise return the maximum value for point." (generate-new-buffer-name (concat "Notes of " display-name)) t) (current-buffer)))) + + + (dummy (message "----3")) (single (eq (or (buffer-base-buffer document-buffer) document-buffer) (or (buffer-base-buffer notes-buffer) notes-buffer))) + + + (dummu (message "2display name = %s" (type-of display-name))) (session (make-org-noter--session :id (org-noter--get-new-id) @@ -590,6 +608,7 @@ Otherwise return the maximum value for point." :modified-tick -1)) (target-location org-noter--start-location-override) + (dummy (message "----4")) (starting-point (point))) (add-hook 'delete-frame-functions 'org-noter--handle-delete-frame) @@ -604,11 +623,17 @@ Otherwise return the maximum value for point." (setq org-noter--session session) (add-hook 'kill-buffer-hook 'org-noter--handle-kill-buffer nil t)) + + (message "----5") + (with-current-buffer notes-buffer + + (message "----5.1") (org-noter-notes-mode 1) ;; NOTE(nox): This is needed because a session created in an indirect buffer would use the point of ;; the base buffer (as this buffer is indirect to the base!) (goto-char starting-point) + (message "----5.2") (setq buffer-file-name notes-file-path org-noter--session session fringe-indicator-alist '((truncation . nil))) @@ -616,8 +641,15 @@ Otherwise return the maximum value for point." (add-hook 'window-scroll-functions 'org-noter--set-notes-scroll nil t) (org-noter--set-text-properties (org-noter--parse-root (vector notes-buffer document-property-value)) (org-noter--session-id session)) + + (message "----5.7") (unless target-location - (setq target-location (org-noter--parse-location-property (org-noter--get-containing-element t))))) + (setq target-location (org-noter--parse-location-property (org-noter--get-containing-element t)))) + (message "----5.8") + ) + + + (message "----6") ;; NOTE(nox): This timer is for preventing reflowing too soon. (unless single @@ -994,11 +1026,13 @@ properties, by a margin of NEWLINES-NUMBER." doc-prop))) (defun org-noter--check-location-property (arg) + (message "13 %s %s" org-noter--get-location-property-hook arg) (let ((property (if (stringp arg) arg (or (org-element-property (intern (concat ":" org-noter-property-note-location)) arg) (run-hook-with-args-until-success 'org-noter--get-location-property-hook arg))))) + (message "-13.1 %s %s" property org-noter--check-location-property-hook ) (when (and (stringp property) (> (length property) 0)) (or (run-hook-with-args-until-success 'org-noter--check-location-property-hook property) (let ((value (car (read-from-string property)))) @@ -1007,6 +1041,7 @@ properties, by a margin of NEWLINES-NUMBER." (integerp value))))))) (defun org-noter--parse-location-property (arg) + (message "8 %s" arg) (let ((property (if (stringp arg) arg (or (org-element-property (intern (concat ":" org-noter-property-note-location)) arg) @@ -1026,12 +1061,15 @@ properties, by a margin of NEWLINES-NUMBER." ;; TODO: Documentation (defun org-noter--get-containing-element (&optional include-root) + (message "9 %s" include-root) + (message "9 %s" org-noter--get-containing-element-hook) (run-hook-with-args-until-success 'org-noter--get-containing-element-hook include-root)) (defun org-noter--get-containing-heading (&optional include-root) "Get smallest containing heading that encloses the point and has location property. If the point isn't inside any heading with location property, return the outer heading. When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." + (message "10") (org-noter--with-valid-session (org-with-wide-buffer (unless (org-before-first-heading-p) @@ -1040,9 +1078,12 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (catch 'break (while t (let ((prop (org-noter--check-location-property (org-entry-get nil org-noter-property-note-location))) + (dummy (message "10.1")) (at-root (equal (org-noter--session-id session) (get-text-property (point) org-noter--id-text-property))) - (heading (org-element-at-point))) + (dummy (message "10.2")) + (heading (org-element-at-point)) + ) (when (and prop (or include-root (not at-root))) (throw 'break heading)) @@ -1055,6 +1096,7 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." "Get smallest containing heading that encloses the point and has location property. If the point isn't inside any heading with location property, return the outer heading. When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." + (message "11") (org-noter--with-valid-session (org-with-point-at (point-min) (when (org-before-first-heading-p) @@ -1403,10 +1445,15 @@ relative to." ignore-until-level current-region-info) ;; NOTE(nox): [REGIONS-LIST-PTR START MAX-END REGIONS-LIST-NAME] + (message "---buffer %s " (org-noter--session-notes-buffer session)) + (with-current-buffer (or (buffer-base-buffer (org-noter--session-notes-buffer session)) (org-noter--session-notes-buffer session)) + (message "-----1 %s" major-mode) + (message "---%s" org-noter--note-search-element-type) (org-element-map contents org-noter--note-search-element-type (lambda (element) + (message "-> %s" element) (let ((doc-file (org-noter--doc-file-property element)) (location (org-noter--parse-location-property element))) (when (and ignore-until-level (<= (org-element-property :level element) ignore-until-level)) @@ -1484,7 +1531,7 @@ relative to." (org-element-property :end (cdr reference-for-insertion))))) (setq reference-for-insertion (cons 'after element))))))) nil nil (delete 'headline (append org-element-all-elements nil)))) - + (org-noter--view-region-finish current-region-info) (setf (org-noter--session-num-notes-in-view session) (length notes-in-view)) @@ -1573,7 +1620,7 @@ relative to." (setq doc-prop (or (run-hook-with-args-until-success 'org-noter-parse-document-property-hook doc-prop) doc-prop)) - + (unless (org-noter--check-doc-prop doc-prop) (setq doc-prop nil) @@ -2014,7 +2061,7 @@ defines if the text should be inserted inside the note." (lambda (section) (org-element-property :end section)) nil t org-element-all-elements) (point-max)))) - + (setq level (1+ (or (org-element-property :level ast) 0))) ;; NOTE(nox): This is needed to insert in the right place diff --git a/org-noter.el b/org-noter.el index 03a95c1..6e5d817 100644 --- a/org-noter.el +++ b/org-noter.el @@ -89,14 +89,18 @@ notes file, even if it finds one." ;; NOTE(nox): Creating the session from notes file ((eq major-mode 'org-mode) (let* ((notes-file-path (buffer-file-name)) + ;; document-property is the backing documentl. eg: pdf or epub (document-property (org-noter--get-or-read-document-property (not (equal arg '(4))) (equal arg '(16)))) + (dummy (message "doc prop ===== %s" document-property)) + (dummy (message "buffer file name ===== %s" notes-file-path)) (org-noter-always-create-frame (if (and (numberp arg) (= arg 0)) (not org-noter-always-create-frame) org-noter-always-create-frame)) (ast (org-noter--parse-root (vector (current-buffer) document-property))) + (dummy (message "~~~~~ %s" ast)) (session-id (get-text-property (org-element-property :begin ast) org-noter--id-text-property)) session) From 7fa38f014baad2a9f978e3732a3c50b61a9fced2 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sun, 1 Jan 2023 20:07:59 -0800 Subject: [PATCH 213/453] Add github action for building/testing. --- .github/workflows/build-test.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 2caf59e..3f0b53c 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -1,3 +1,8 @@ +on: + push: + branches: + - master + jobs: build: runs-on: ubuntu-latest From 25907029d1e6702ebc97c9e3170eef70086fdbe0 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sun, 1 Jan 2023 20:08:51 -0800 Subject: [PATCH 214/453] Update python version to 3.10 --- .github/workflows/build-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 3f0b53c..06cafbc 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v1 - uses: actions/setup-python@v2 with: - python-version: '3.6' + python-version: '3.10' architecture: 'x64' - uses: purcell/setup-emacs@master with: From bf6d211e554ba88f94f47f2ffbb06d82e9350799 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sun, 1 Jan 2023 20:11:07 -0800 Subject: [PATCH 215/453] Update emacs version in github action. --- .github/workflows/build-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 06cafbc..fea64cf 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -14,7 +14,7 @@ jobs: architecture: 'x64' - uses: purcell/setup-emacs@master with: - version: '28' + version: '28.1' - uses: conao3/setup-cask@master with: From 4e80f48d327e8930d9850c049e08638bbc678f83 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sun, 1 Jan 2023 20:19:49 -0800 Subject: [PATCH 216/453] Run cask init. --- .github/workflows/build-test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index fea64cf..22dbfd1 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -20,5 +20,8 @@ jobs: with: version: 'snapshot' + - name: Install dependencies + run: cask + - name: Run tests run: cask exec buttercup -L . From f53482681d09186c75ff23ce74a4286fc377d2d4 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sun, 1 Jan 2023 20:28:55 -0800 Subject: [PATCH 217/453] Remove accidental debug output. --- org-noter-core.el | 57 +++++------------------------------------------ org-noter.el | 4 ---- 2 files changed, 5 insertions(+), 56 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index d5d0cab..f8ac203 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -526,7 +526,6 @@ Otherwise return the maximum value for point." (defun org-noter--create-session (ast document-property-value notes-file-path) (let* ((raw-value-not-empty (> (length (org-element-property :raw-value ast)) 0)) - (dummy (message "----1")) (link-p (or (string-match-p org-bracket-link-regexp document-property-value) (string-match-p org-noter--url-regexp document-property-value))) (display-name (if raw-value-not-empty @@ -535,30 +534,19 @@ Otherwise return the maximum value for point." document-property-value (file-name-nondirectory document-property-value)))) - (dummy1 (message "\n!----99 %s" display-name)) - (dummy1 (message "\n!----98 %s" (org-element-property :raw-value ast))) - (frame-name (format "Emacs Org-noter - %s" display-name)) (document (or (run-hook-with-args-until-success 'org-noter-open-document-functions document-property-value) (if link-p (progn (org-link-open-from-string document-property-value) (current-buffer)) (find-file-noselect document-property-value)))) - (dummy (message "----1.5 doc %s %s" document document-property-value)) (document-major-mode (if (or link-p (eq document (current-buffer))) document-property-value (buffer-local-value 'major-mode document))) - - (dummy (message "----2 %s" document-major-mode)) - - (dummu (message "1display name = %s" display-name)) (document-buffer-name (generate-new-buffer-name (concat (unless raw-value-not-empty "Org-noter: ") display-name))) (document-buffer document) - - - (notes-buffer (progn (when (and org-window-config-before-follow-link link-p) (set-window-configuration org-window-config-before-follow-link)) @@ -569,17 +557,11 @@ Otherwise return the maximum value for point." (generate-new-buffer-name (concat "Notes of " display-name)) t) (current-buffer)))) - - - (dummy (message "----3")) (single (eq (or (buffer-base-buffer document-buffer) document-buffer) (or (buffer-base-buffer notes-buffer) notes-buffer))) - - - (dummu (message "2display name = %s" (type-of display-name))) (session (make-org-noter--session :id (org-noter--get-new-id) @@ -608,7 +590,6 @@ Otherwise return the maximum value for point." :modified-tick -1)) (target-location org-noter--start-location-override) - (dummy (message "----4")) (starting-point (point))) (add-hook 'delete-frame-functions 'org-noter--handle-delete-frame) @@ -623,17 +604,11 @@ Otherwise return the maximum value for point." (setq org-noter--session session) (add-hook 'kill-buffer-hook 'org-noter--handle-kill-buffer nil t)) - - (message "----5") - (with-current-buffer notes-buffer - - (message "----5.1") (org-noter-notes-mode 1) ;; NOTE(nox): This is needed because a session created in an indirect buffer would use the point of ;; the base buffer (as this buffer is indirect to the base!) (goto-char starting-point) - (message "----5.2") (setq buffer-file-name notes-file-path org-noter--session session fringe-indicator-alist '((truncation . nil))) @@ -641,15 +616,8 @@ Otherwise return the maximum value for point." (add-hook 'window-scroll-functions 'org-noter--set-notes-scroll nil t) (org-noter--set-text-properties (org-noter--parse-root (vector notes-buffer document-property-value)) (org-noter--session-id session)) - - (message "----5.7") (unless target-location - (setq target-location (org-noter--parse-location-property (org-noter--get-containing-element t)))) - (message "----5.8") - ) - - - (message "----6") + (setq target-location (org-noter--parse-location-property (org-noter--get-containing-element t))))) ;; NOTE(nox): This timer is for preventing reflowing too soon. (unless single @@ -1026,13 +994,11 @@ properties, by a margin of NEWLINES-NUMBER." doc-prop))) (defun org-noter--check-location-property (arg) - (message "13 %s %s" org-noter--get-location-property-hook arg) (let ((property (if (stringp arg) arg (or (org-element-property (intern (concat ":" org-noter-property-note-location)) arg) (run-hook-with-args-until-success 'org-noter--get-location-property-hook arg))))) - (message "-13.1 %s %s" property org-noter--check-location-property-hook ) (when (and (stringp property) (> (length property) 0)) (or (run-hook-with-args-until-success 'org-noter--check-location-property-hook property) (let ((value (car (read-from-string property)))) @@ -1041,7 +1007,6 @@ properties, by a margin of NEWLINES-NUMBER." (integerp value))))))) (defun org-noter--parse-location-property (arg) - (message "8 %s" arg) (let ((property (if (stringp arg) arg (or (org-element-property (intern (concat ":" org-noter-property-note-location)) arg) @@ -1061,15 +1026,12 @@ properties, by a margin of NEWLINES-NUMBER." ;; TODO: Documentation (defun org-noter--get-containing-element (&optional include-root) - (message "9 %s" include-root) - (message "9 %s" org-noter--get-containing-element-hook) (run-hook-with-args-until-success 'org-noter--get-containing-element-hook include-root)) (defun org-noter--get-containing-heading (&optional include-root) "Get smallest containing heading that encloses the point and has location property. If the point isn't inside any heading with location property, return the outer heading. When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." - (message "10") (org-noter--with-valid-session (org-with-wide-buffer (unless (org-before-first-heading-p) @@ -1078,12 +1040,9 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (catch 'break (while t (let ((prop (org-noter--check-location-property (org-entry-get nil org-noter-property-note-location))) - (dummy (message "10.1")) (at-root (equal (org-noter--session-id session) (get-text-property (point) org-noter--id-text-property))) - (dummy (message "10.2")) - (heading (org-element-at-point)) - ) + (heading (org-element-at-point))) (when (and prop (or include-root (not at-root))) (throw 'break heading)) @@ -1096,7 +1055,6 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." "Get smallest containing heading that encloses the point and has location property. If the point isn't inside any heading with location property, return the outer heading. When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." - (message "11") (org-noter--with-valid-session (org-with-point-at (point-min) (when (org-before-first-heading-p) @@ -1445,15 +1403,10 @@ relative to." ignore-until-level current-region-info) ;; NOTE(nox): [REGIONS-LIST-PTR START MAX-END REGIONS-LIST-NAME] - (message "---buffer %s " (org-noter--session-notes-buffer session)) - (with-current-buffer (or (buffer-base-buffer (org-noter--session-notes-buffer session)) (org-noter--session-notes-buffer session)) - (message "-----1 %s" major-mode) - (message "---%s" org-noter--note-search-element-type) (org-element-map contents org-noter--note-search-element-type (lambda (element) - (message "-> %s" element) (let ((doc-file (org-noter--doc-file-property element)) (location (org-noter--parse-location-property element))) (when (and ignore-until-level (<= (org-element-property :level element) ignore-until-level)) @@ -1531,7 +1484,7 @@ relative to." (org-element-property :end (cdr reference-for-insertion))))) (setq reference-for-insertion (cons 'after element))))))) nil nil (delete 'headline (append org-element-all-elements nil)))) - + (org-noter--view-region-finish current-region-info) (setf (org-noter--session-num-notes-in-view session) (length notes-in-view)) @@ -1620,7 +1573,7 @@ relative to." (setq doc-prop (or (run-hook-with-args-until-success 'org-noter-parse-document-property-hook doc-prop) doc-prop)) - + (unless (org-noter--check-doc-prop doc-prop) (setq doc-prop nil) @@ -2061,7 +2014,7 @@ defines if the text should be inserted inside the note." (lambda (section) (org-element-property :end section)) nil t org-element-all-elements) (point-max)))) - + (setq level (1+ (or (org-element-property :level ast) 0))) ;; NOTE(nox): This is needed to insert in the right place diff --git a/org-noter.el b/org-noter.el index 6e5d817..03a95c1 100644 --- a/org-noter.el +++ b/org-noter.el @@ -89,18 +89,14 @@ notes file, even if it finds one." ;; NOTE(nox): Creating the session from notes file ((eq major-mode 'org-mode) (let* ((notes-file-path (buffer-file-name)) - ;; document-property is the backing documentl. eg: pdf or epub (document-property (org-noter--get-or-read-document-property (not (equal arg '(4))) (equal arg '(16)))) - (dummy (message "doc prop ===== %s" document-property)) - (dummy (message "buffer file name ===== %s" notes-file-path)) (org-noter-always-create-frame (if (and (numberp arg) (= arg 0)) (not org-noter-always-create-frame) org-noter-always-create-frame)) (ast (org-noter--parse-root (vector (current-buffer) document-property))) - (dummy (message "~~~~~ %s" ast)) (session-id (get-text-property (org-element-property :begin ast) org-noter--id-text-property)) session) From d920d9a9d3f86237acfea0eee480a2c3226f4d9e Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Mon, 2 Jan 2023 21:49:10 -0800 Subject: [PATCH 218/453] +org-noter-dev.el: intial commit proposed change to org-noter-insert-note --- org-noter-dev.el | 179 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 org-noter-dev.el diff --git a/org-noter-dev.el b/org-noter-dev.el new file mode 100644 index 0000000..3d25fba --- /dev/null +++ b/org-noter-dev.el @@ -0,0 +1,179 @@ +(defcustom org-noter-max-short-length 80 + "Maximum length of a short text selection" + :group 'org-noter + :type 'integer) + +(defun phm/org-noter-insert-note (&optional precise-info) + "Insert note associated with the current location. + +This command will prompt for a title of the note and then insert +it in the notes buffer. When the input is empty, a title based on +`org-noter-default-heading-title' will be generated. + +If there are other notes related to the current location, the +prompt will also suggest them. Depending on the value of the +variable `org-noter-closest-tipping-point', it may also +suggest the closest previous note. + +PRECISE-INFO makes the new note associated with a more +specific location (see `org-noter-insert-precise-note' for more +info). + +When you insert into an existing note and have text selected on +the document buffer, the variable `org-noter-insert-selected-text-inside-note' +defines if the text should be inserted inside the note. + +Guiding principles for this (phm/) refactor + 1. The preferred title is the one the user enters in the minibuffer. + 2. Selected text should be used in the note, either as the title or in the body + 3. Refrain from making notes in the same location with the same title + 4. Precise notes generally have different locations, always make new + precise notes +" + (interactive) + (org-noter--with-valid-session + (let* ((ast (org-noter--parse-root)) (contents (org-element-contents ast)) + (window (org-noter--get-notes-window 'force)) + (selected-text (run-hook-with-args-until-success + 'org-noter-get-selected-text-hook + (org-noter--session-doc-mode session))) + + force-new + (location (org-noter--doc-approx-location (or precise-info 'interactive) (gv-ref force-new))) + (view-info (org-noter--get-view-info (org-noter--get-current-view) location))) + + (let ((inhibit-quit t) + (short-selected-text (if (<= (length selected-text) org-noter-max-short-length) + selected-text))) + (with-local-quit + (select-frame-set-input-focus (window-frame window)) + (select-window window) + + ;; IMPORTANT(nox): Need to be careful changing the next part, it is a bit + ;; complicated to get it right... + + (let ((point (point)) + (minibuffer-local-completion-map org-noter--completing-read-keymap) + collection default-begin title prompt-title existing-note this-title note-body + (default-title (replace-regexp-in-string (regexp-quote "$p$") + (org-noter--pretty-print-location location) + org-noter-default-heading-title)) + (empty-lines-number (if org-noter-separate-notes-from-heading 2 1))) + + ;; NOTE(phm): prompt for title + (unless org-noter-insert-note-no-questions + ;; construct colletction, this-title and default-begin + (dolist (note-cons (org-noter--view-info-notes view-info)) + (let ((display (org-element-property :raw-value (car note-cons))) + (begin (org-element-property :begin (car note-cons)))) + (push (cons display note-cons) collection) + (when (and (>= point begin) (> begin (or default-begin 0))) + (setq this-title display + default-begin begin))))) + + (setq collection (nreverse collection) + ;; prompt for title (unless no-Q's) + prompt-title (unless org-noter-insert-note-no-questions + (completing-read "Note: " collection nil nil nil nil nil)) + ;; NOTE(phm): define title and body of note + title (if (> (length prompt-title) 0) prompt-title + (or short-selected-text default-title)) + note-body (unless (equal title short-selected-text) selected-text) + ;; is this an existing note? skip for precise notes + existing-note (unless precise-info (cdr (assoc title collection)))) + + (if existing-note + ;; NOTE(nox): Inserting on an existing note + (let* ((note (car existing-note)) + (insert-before-element (cdr existing-note)) + (has-content + (eq (org-element-map (org-element-contents note) org-element-all-elements + (lambda (element) + (if (org-noter--check-location-property element) + 'stop + (not (memq (org-element-type element) '(section property-drawer))))) + nil t) + t))) + (when has-content (setq empty-lines-number 2)) + (if insert-before-element + (goto-char (org-element-property :begin insert-before-element)) + (goto-char (org-element-property :end note))) + + (if (org-at-heading-p) + (progn + (org-N-empty-lines-before-current empty-lines-number) + (forward-line -1)) + (unless (bolp) (insert "\n")) + (org-N-empty-lines-before-current (1- empty-lines-number))) + + (when (and org-noter-insert-selected-text-inside-note note-body) (insert note-body))) + + ;; NOTE(nox): Inserting a new note + (let ((reference-element-cons (org-noter--view-info-reference-for-insertion view-info)) + level) + (if reference-element-cons + (progn + (cond + ((eq (car reference-element-cons) 'before) + (goto-char (org-element-property :begin (cdr reference-element-cons)))) + ((eq (car reference-element-cons) 'after) + (goto-char (org-element-property :end (cdr reference-element-cons))))) + + ;; NOTE(nox): This is here to make the automatic "should insert blank" work better. + (when (org-at-heading-p) (backward-char)) + + (setq level (org-element-property :level (cdr reference-element-cons)))) + + (goto-char (or (org-element-map contents 'section + (lambda (section) (org-element-property :end section)) + nil t org-element-all-elements) + (point-max)))) + + (setq level (1+ (or (org-element-property :level ast) 0))) + + ;; NOTE(nox): This is needed to insert in the right place + (unless (org-noter--no-heading-p) (outline-show-entry)) + (org-noter--insert-heading level title empty-lines-number location) + (when note-body + (save-excursion + (insert "#+BEGIN_QUOTE\n" note-body "\n#+END_QUOTE"))) + (when (org-noter--session-hide-other session) (org-overview)) + + (setf (org-noter--session-num-notes-in-view session) + (1+ (org-noter--session-num-notes-in-view session))))) + + (org-show-set-visibility t) + (org-cycle-hide-drawers 'all) + (org-cycle-show-empty-lines t))) + (when quit-flag + ;; NOTE(nox): If this runs, it means the user quitted while creating a note, so + ;; revert to the previous window. + (select-frame-set-input-focus (org-noter--session-frame session)) + (select-window (get-buffer-window (org-noter--session-doc-buffer session)))))))) + +(defun phm/org-noter-insert-precise-note (&optional toggle-no-questions) + "Insert note associated with a specific location. +This will ask you to click where you want to scroll to when you +sync the document to this note. You should click on the top of +that part. Will always create a new note. + +When text is selected, it will automatically choose the top of +the selected text as the location and the text itself as the +title of the note (you may change it anyway!). + +See `org-noter-insert-note' docstring for more." + (interactive "P") + (org-noter--with-valid-session + (let ((org-noter-insert-note-no-questions (if toggle-no-questions + (not org-noter-insert-note-no-questions) + org-noter-insert-note-no-questions))) + (phm/org-noter-insert-note (org-noter--get-precise-info))))) + + +(defun phm/org-noter-insert-note-toggle-no-questions () + "Insert note associated with the current location. +This is like `org-noter-insert-note', except it will toggle `org-noter-insert-note-no-questions'" + (interactive) + (org-noter--with-valid-session + (let ((org-noter-insert-note-no-questions (not org-noter-insert-note-no-questions))) + (phm/org-noter-insert-note)))) From 0bb22d0231601097422835902a3cd3a913675c8d Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Mon, 2 Jan 2023 22:15:13 -0800 Subject: [PATCH 219/453] only put #+(BEGIN|END)_QUOTE around long selections --- org-noter-dev.el | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/org-noter-dev.el b/org-noter-dev.el index 3d25fba..23de74b 100644 --- a/org-noter-dev.el +++ b/org-noter-dev.el @@ -128,7 +128,7 @@ Guiding principles for this (phm/) refactor (lambda (section) (org-element-property :end section)) nil t org-element-all-elements) (point-max)))) - + (setq level (1+ (or (org-element-property :level ast) 0))) ;; NOTE(nox): This is needed to insert in the right place @@ -136,7 +136,9 @@ Guiding principles for this (phm/) refactor (org-noter--insert-heading level title empty-lines-number location) (when note-body (save-excursion - (insert "#+BEGIN_QUOTE\n" note-body "\n#+END_QUOTE"))) + (if short-selected-text + (insert note-body) + (insert "#+BEGIN_QUOTE\n" note-body "\n#+END_QUOTE")))) (when (org-noter--session-hide-other session) (org-overview)) (setf (org-noter--session-num-notes-in-view session) From 2c7a750cc28caebc1f6cdfa025f55e9892829e23 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Tue, 3 Jan 2023 19:07:55 -0800 Subject: [PATCH 220/453] bugfix: zero length strings are not nil but (length nil) is zero, so I use that to check the suitability of potential titles. --- org-noter-dev.el | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/org-noter-dev.el b/org-noter-dev.el index 23de74b..4e2e436 100644 --- a/org-noter-dev.el +++ b/org-noter-dev.el @@ -76,8 +76,9 @@ Guiding principles for this (phm/) refactor prompt-title (unless org-noter-insert-note-no-questions (completing-read "Note: " collection nil nil nil nil nil)) ;; NOTE(phm): define title and body of note - title (if (> (length prompt-title) 0) prompt-title - (or short-selected-text default-title)) + title (cond ((> (length prompt-title) 0) prompt-title) + ((> (length short-selected-text) 0) short-selected-text) + (t default-title)) note-body (unless (equal title short-selected-text) selected-text) ;; is this an existing note? skip for precise notes existing-note (unless precise-info (cdr (assoc title collection)))) From 52783c9584fae072b0cbaedc5f49b01cf1e03b47 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Tue, 3 Jan 2023 19:08:52 -0800 Subject: [PATCH 221/453] feature-fix: only force new notes when note is precise This is a departure from nox's org-noter, where no-questions always makes a new note. I don't see the value of having multiple notes with the same heading. --- org-noter-dev.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/org-noter-dev.el b/org-noter-dev.el index 4e2e436..c193329 100644 --- a/org-noter-dev.el +++ b/org-noter-dev.el @@ -60,8 +60,8 @@ Guiding principles for this (phm/) refactor org-noter-default-heading-title)) (empty-lines-number (if org-noter-separate-notes-from-heading 2 1))) - ;; NOTE(phm): prompt for title - (unless org-noter-insert-note-no-questions + ;; NOTE(phm): prompt for title unless this is a precise note + (unless precise-info ;; construct colletction, this-title and default-begin (dolist (note-cons (org-noter--view-info-notes view-info)) (let ((display (org-element-property :raw-value (car note-cons))) From 192f03836453ac6c42e844d4549076850659d92e Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Tue, 3 Jan 2023 19:11:14 -0800 Subject: [PATCH 222/453] keybinding for the test commands --- org-noter-dev.el | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/org-noter-dev.el b/org-noter-dev.el index c193329..f41125c 100644 --- a/org-noter-dev.el +++ b/org-noter-dev.el @@ -180,3 +180,7 @@ This is like `org-noter-insert-note', except it will toggle `org-noter-insert-no (org-noter--with-valid-session (let ((org-noter-insert-note-no-questions (not org-noter-insert-note-no-questions))) (phm/org-noter-insert-note)))) + +(define-key org-noter-doc-mode-map (kbd "i") 'phm/org-noter-insert-note) +(define-key org-noter-doc-mode-map (kbd "M-i") 'phm/org-noter-insert-precise-note) +(define-key org-noter-doc-mode-map (kbd "C-i") 'phm/org-noter-insert-note-toggle-no-questions) From 6d3befbefea1152de8b8da3ef7074ceba077e4f4 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Tue, 3 Jan 2023 20:58:21 -0800 Subject: [PATCH 223/453] provide short-selected-text or default-title as default prompt In completing-read, C-n brings up the default argument. short-selected-text is either a string of 1 to org-noter-max-short-length chars or nil default-title is short-selected-text or org-noter-default-heading-title + location prompt-title no longer needed. --- org-noter-dev.el | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/org-noter-dev.el b/org-noter-dev.el index f41125c..05e171c 100644 --- a/org-noter-dev.el +++ b/org-noter-dev.el @@ -43,7 +43,8 @@ Guiding principles for this (phm/) refactor (view-info (org-noter--get-view-info (org-noter--get-current-view) location))) (let ((inhibit-quit t) - (short-selected-text (if (<= (length selected-text) org-noter-max-short-length) + (short-selected-text (if (and (> (length selected-text) 0) + (<= (length selected-text) org-noter-max-short-length)) selected-text))) (with-local-quit (select-frame-set-input-focus (window-frame window)) @@ -54,10 +55,11 @@ Guiding principles for this (phm/) refactor (let ((point (point)) (minibuffer-local-completion-map org-noter--completing-read-keymap) - collection default-begin title prompt-title existing-note this-title note-body - (default-title (replace-regexp-in-string (regexp-quote "$p$") - (org-noter--pretty-print-location location) - org-noter-default-heading-title)) + collection default-begin title existing-note this-title note-body + (default-title (or short-selected-text + (replace-regexp-in-string (regexp-quote "$p$") + (org-noter--pretty-print-location location) + org-noter-default-heading-title))) (empty-lines-number (if org-noter-separate-notes-from-heading 2 1))) ;; NOTE(phm): prompt for title unless this is a precise note @@ -73,12 +75,8 @@ Guiding principles for this (phm/) refactor (setq collection (nreverse collection) ;; prompt for title (unless no-Q's) - prompt-title (unless org-noter-insert-note-no-questions - (completing-read "Note: " collection nil nil nil nil nil)) - ;; NOTE(phm): define title and body of note - title (cond ((> (length prompt-title) 0) prompt-title) - ((> (length short-selected-text) 0) short-selected-text) - (t default-title)) + title (if org-noter-insert-note-no-questions default-title + (completing-read "Note: " collection nil nil nil nil default-title)) note-body (unless (equal title short-selected-text) selected-text) ;; is this an existing note? skip for precise notes existing-note (unless precise-info (cdr (assoc title collection)))) From dcf23e7011b238874a0208cf453a05e39d48b2ae Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Tue, 3 Jan 2023 21:16:30 -0800 Subject: [PATCH 224/453] clean up code around construction of "collection" collection is only used to determine if we are adding to an existing note. default-begin and this-title were not being used outside of the code block, so they have been removed. --- org-noter-dev.el | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/org-noter-dev.el b/org-noter-dev.el index 05e171c..643647d 100644 --- a/org-noter-dev.el +++ b/org-noter-dev.el @@ -55,7 +55,7 @@ Guiding principles for this (phm/) refactor (let ((point (point)) (minibuffer-local-completion-map org-noter--completing-read-keymap) - collection default-begin title existing-note this-title note-body + collection title note-body existing-note (default-title (or short-selected-text (replace-regexp-in-string (regexp-quote "$p$") (org-noter--pretty-print-location location) @@ -64,14 +64,10 @@ Guiding principles for this (phm/) refactor ;; NOTE(phm): prompt for title unless this is a precise note (unless precise-info - ;; construct colletction, this-title and default-begin + ;; construct collection for matching existing notes (dolist (note-cons (org-noter--view-info-notes view-info)) - (let ((display (org-element-property :raw-value (car note-cons))) - (begin (org-element-property :begin (car note-cons)))) - (push (cons display note-cons) collection) - (when (and (>= point begin) (> begin (or default-begin 0))) - (setq this-title display - default-begin begin))))) + (let ((display (org-element-property :raw-value (car note-cons)))) + (push (cons display note-cons) collection)))) (setq collection (nreverse collection) ;; prompt for title (unless no-Q's) From d487acd678acaae1ca569898604814ece4e73cee Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Tue, 3 Jan 2023 21:31:03 -0800 Subject: [PATCH 225/453] update README.org for note insertion refactor --- README.org | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.org b/README.org index e8bb17c..874edce 100644 --- a/README.org +++ b/README.org @@ -38,3 +38,21 @@ 1. Navigation of nested notes is spotty. =C-M-p= and =C-M-.= have trouble detecting nested notes. +** Trying out the refactor of org-noter-insert-note + 1. load `org-noter-dev.el' + - defines org-noter-max-short-note + - defines replacement for note insertion commands + - sets default note insertion keybindings to the new commands + 2. Guiding principles for this refactor + - The preferred title is the one the user enters in the minibuffer. + - Selected text should be used in the note, either as the title or in the body + - If there is no title input and no selected text, then use the default title + - Refrain from making notes in the same location with the same title + - Precise notes generally have different locations, so always make new + precise notes + 3. Caveats + - QUOTE block delimiters are not consistently applied + - There may be cleaner ways to implement some of the logical tests, namely + empty string vs. nil situations. + - Not all combinations of text selection, entry command and note title + have been tested. From 1981988883dfb9a7dce4c6937e292b4bb41d5a1a Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Tue, 3 Jan 2023 21:47:14 -0800 Subject: [PATCH 226/453] regularize when QUOTE delimiters are used --- README.org | 2 +- org-noter-dev.el | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.org b/README.org index 874edce..20a479d 100644 --- a/README.org +++ b/README.org @@ -51,7 +51,7 @@ - Precise notes generally have different locations, so always make new precise notes 3. Caveats - - QUOTE block delimiters are not consistently applied + - QUOTE block delimiters only appear on long selected text - There may be cleaner ways to implement some of the logical tests, namely empty string vs. nil situations. - Not all combinations of text selection, entry command and note title diff --git a/org-noter-dev.el b/org-noter-dev.el index 643647d..b4085ca 100644 --- a/org-noter-dev.el +++ b/org-noter-dev.el @@ -101,7 +101,10 @@ Guiding principles for this (phm/) refactor (unless (bolp) (insert "\n")) (org-N-empty-lines-before-current (1- empty-lines-number))) - (when (and org-noter-insert-selected-text-inside-note note-body) (insert note-body))) + (when (and org-noter-insert-selected-text-inside-note note-body) + (if short-selected-text + (insert note-body) + (insert "#+BEGIN_QUOTE\n" note-body "\n#+END_QUOTE")))) ;; NOTE(nox): Inserting a new note (let ((reference-element-cons (org-noter--view-info-reference-for-insertion view-info)) From 572585d931c108be4df24e6c3fb19f35b3019f2d Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Thu, 5 Jan 2023 22:47:38 -0800 Subject: [PATCH 227/453] bugfix? Maybe this fixes multi-level/nested notes --- org-noter-dev.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/org-noter-dev.el b/org-noter-dev.el index b4085ca..c1b842c 100644 --- a/org-noter-dev.el +++ b/org-noter-dev.el @@ -127,7 +127,8 @@ Guiding principles for this (phm/) refactor nil t org-element-all-elements) (point-max)))) - (setq level (1+ (or (org-element-property :level ast) 0))) + (setq level (or level + (1+ (or (org-element-property :level ast) 0)))) ;; NOTE(nox): This is needed to insert in the right place (unless (org-noter--no-heading-p) (outline-show-entry)) From 9f3cb80564935e223845c6e691767273c733c1e3 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Fri, 6 Jan 2023 18:51:00 -0800 Subject: [PATCH 228/453] implement LaTeX style quote around short-selected-text implementation for issue #162 --- org-noter-dev.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/org-noter-dev.el b/org-noter-dev.el index c1b842c..fd37e7f 100644 --- a/org-noter-dev.el +++ b/org-noter-dev.el @@ -103,7 +103,7 @@ Guiding principles for this (phm/) refactor (when (and org-noter-insert-selected-text-inside-note note-body) (if short-selected-text - (insert note-body) + (insert "``" note-body "''") (insert "#+BEGIN_QUOTE\n" note-body "\n#+END_QUOTE")))) ;; NOTE(nox): Inserting a new note @@ -136,7 +136,7 @@ Guiding principles for this (phm/) refactor (when note-body (save-excursion (if short-selected-text - (insert note-body) + (insert "``" note-body "''") (insert "#+BEGIN_QUOTE\n" note-body "\n#+END_QUOTE")))) (when (org-noter--session-hide-other session) (org-overview)) From 4b937f01ba981b0b6d8b7bc3325bf709b1839448 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sat, 7 Jan 2023 14:47:44 -0800 Subject: [PATCH 229/453] add documentation for refactored note insertion --- README.org | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/README.org b/README.org index 20a479d..798601a 100644 --- a/README.org +++ b/README.org @@ -51,8 +51,43 @@ - Precise notes generally have different locations, so always make new precise notes 3. Caveats - - QUOTE block delimiters only appear on long selected text - There may be cleaner ways to implement some of the logical tests, namely empty string vs. nil situations. - Not all combinations of text selection, entry command and note title have been tested. + +** Stock vs refactored note insertion + - more consistent use of selected text in title or body + - more consistent primary default title (short-selected-text) + - more consistent secondary default title ("Notes for page ") + - avoids having different notes with the same heading + + In Stock org-noter, repeated TAB's give multiple notes. In this refactor, + new notes in the same location with the same title are note made. For now, + precise notes are excepted from this rule. + + - long quotes are enclosed in #+BEGIN_QUOTE...#+END_QUOTE + - short quotes are enclosed in ``...'' (LaTeX style) + - short/long are differentiated by customizable variable (default 80 char) + + In Stock org-noter, long selections have more than 3 lines. + +*** Stock + | | insert-note =i= | precise note =M-i= | no-questions =C-i, TAB= | + |-----------------+--------------------------------+---------------------------+-------------------------| + | title prompt? | Y | Y | N | + | default title 1 | prior note by location | selected-text | N/A | + | default title 2 | "Notes for page #" | "Notes for page # x y" | "Notes for page #" | + | new note | with new title | never | never | + | body | selected-text on existing note | selected-text (> 3 lines) | none | + |-----------------+--------------------------------+---------------------------+-------------------------| + +*** Refactored + | | insert-note =i= | precise note =M-i= | no-questions =C-i, TAB= | + |-----------------+---------------------------+------------------------+---------------------------| + | title prompt? | Y | Y | N | + | default title 1 | short-selected-text | short-selected-text | short-selected-text | + | default title 2 | "Notes for page #" | "Notes for page # x y" | "Notes for page #" | + | new note | with new title | never | with new title | + | body | selected-text (not title) | long selected-text | selected-text (not title) | + |-----------------+---------------------------+------------------------+---------------------------| From 7d05d4c0f0fb76eaea43308235082a6ee7d8868c Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 8 Jan 2023 00:00:08 -0800 Subject: [PATCH 230/453] interactive mouse event tester --- org-noter-dev.el | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/org-noter-dev.el b/org-noter-dev.el index fd37e7f..d138b40 100644 --- a/org-noter-dev.el +++ b/org-noter-dev.el @@ -179,6 +179,27 @@ This is like `org-noter-insert-note', except it will toggle `org-noter-insert-no (let ((org-noter-insert-note-no-questions (not org-noter-insert-note-no-questions))) (phm/org-noter-insert-note)))) +(defun phm/org-noter-test-mouse-event () + "test code for precise notes in pdfs" + (interactive) + (let ((event nil) + (window (car (window-list)))) + (while (not (and (eq 'mouse-1 (car event)) + (eq window (posn-window (event-start event))))) + (setq event (read-event "Click where you want the start of the note to be!"))) + (let ((col-row (posn-col-row (event-start event))) + (win-edges (window-inside-edges)) + (display-size (image-display-size (image-get-display-property)))) ; from next fn + (org-noter--conv-page-scroll-percentage (+ (window-vscroll) (cdr col-row)) + (+ (window-hscroll) (car col-row))) + (message (format "%f %f %f %f; Disp (%f %f); Win (%d %d)" + (window-vscroll) (cdr col-row) + (window-hscroll) (car col-row) + (car display-size) (cdr display-size) + (- (nth 2 win-edges) (nth 0 win-edges)) + (- (nth 3 win-edges) (nth 1 win-edges))))))) + (define-key org-noter-doc-mode-map (kbd "i") 'phm/org-noter-insert-note) (define-key org-noter-doc-mode-map (kbd "M-i") 'phm/org-noter-insert-precise-note) (define-key org-noter-doc-mode-map (kbd "C-i") 'phm/org-noter-insert-note-toggle-no-questions) +(define-key org-noter-doc-mode-map (kbd "e") 'phm/org-noter-test-mouse-event) From 5756d61c41ee85d1cd682f6ae8ea8069227460dc Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 8 Jan 2023 00:01:54 -0800 Subject: [PATCH 231/453] bugfix: clicked precise note position calculation horizontal position needs to know the left edge of the page in the window. --- org-noter-core.el | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index c2dfca3..ea28ccf 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1077,13 +1077,21 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (list slice-top slice-height slice-left slice-width))) (defun org-noter--conv-page-scroll-percentage (vscroll &optional hscroll) + "Convert CHAR based position to percent-base position" (let* ((slice (org-noter--doc-get-page-slice)) - (display-size (image-display-size (image-get-display-property))) - (display-percentage-height (/ vscroll (cdr display-size))) - (hpercentage (max 0 (min 1 (+ (nth 0 slice) (* (nth 1 slice) display-percentage-height)))))) - (if hscroll - (cons hpercentage (max 0 (min 1 (+ (nth 2 slice) (* (nth 3 slice) (/ vscroll (car display-size))))))) - (cons hpercentage 0)))) + (display-size (image-display-size (image-get-display-property))) ;(width height) + (display-width (car display-size)) + (display-height (cdr display-size)) + (window-geom (window-inside-edges)) ; (L T R B) + (display-left-edge (/ (- (nth 2 window-geom) (nth 0 window-geom) display-width) 2)) + (display-percentage-v (/ vscroll display-height)) + (percentage-v (max 0 (min 1 (+ (nth 0 slice) (* (nth 1 slice) display-percentage-v))))) + (display-percentage-h 0) + (percentage-h 0)) + (when hscroll + (setq display-percentage-h (/ (- hscroll display-left-edge) display-width) + percentage-h (max 0 (min 1 (+ (nth 2 slice) (* (nth 3 slice) display-percentage-h)))))) + (cons percentage-v percentage-h))) (defun org-noter--conv-page-percentage-scroll (percentage) (let* ((slice (org-noter--doc-get-page-slice)) From 21e43fc1344084c1932038372d76bf669fc9adce Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 8 Jan 2023 00:04:12 -0800 Subject: [PATCH 232/453] pdf--get-precise-info: apply horizontal offset to precise positions to try to keep the arrow from blocking the area of interest. Also, for a selected region, place the arrow at the top/left of the selected region. Internationalization for right-to-left text should happen some time, but not now. --- modules/org-noter-pdf.el | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 1115df8..b8afc08 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -75,19 +75,23 @@ (defun org-noter-pdf--get-precise-info (major-mode window) (when (eq major-mode 'pdf-view-mode) - (if (pdf-view-active-region-p) - (let ((edges (pdf-view-active-region))) - (cons - (cadar edges) - (- (caar edges) 0.02))) - - (let ((event nil)) - (while (not (and (eq 'mouse-1 (car event)) - (eq window (posn-window (event-start event))))) - (setq event (read-event "Click where you want the start of the note to be!"))) - (let ((col-row (posn-col-row (event-start event)))) - (org-noter--conv-page-scroll-percentage (+ (window-vscroll) (cdr col-row)) - (+ (window-hscroll) (car col-row)))))))) + (let (v-position h-position) + (if (pdf-view-active-region-p) + (let ((edges (car (pdf-view-active-region)))) + (setq v-position (min (nth 1 edges) (nth 3 edges)) + h-position (min (nth 0 edges) (nth 2 edges)))) + + (let ((event nil)) + (while (not (and (eq 'mouse-1 (car event)) + (eq window (posn-window (event-start event))))) + (setq event (read-event "Click where you want the start of the note to be!"))) + (let* ((col-row (posn-col-row (event-start event))) + (click-position (org-noter--conv-page-scroll-percentage (+ (window-vscroll) (cdr col-row)) + (+ (window-hscroll) (car col-row))))) + (setq v-position (car click-position) + h-position (cdr click-position))))) + (setq h-position (max 0 (+ h-position -0.02))) + (cons v-position h-position)))) (add-to-list 'org-noter--get-precise-info-hook #'org-noter-pdf--get-precise-info) From 609c007b2023796249b01d2e13b599c7e638f3d3 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 8 Jan 2023 15:37:37 -0800 Subject: [PATCH 233/453] + defcustom org-noter-arrow-horizontal-offset implemented for pdfs --- README.org | 5 ----- modules/org-noter-pdf.el | 2 +- org-noter-core.el | 9 +++++++++ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/README.org b/README.org index 798601a..efecbc0 100644 --- a/README.org +++ b/README.org @@ -14,9 +14,6 @@ fork, select-precise notes auto-fill the note title with the selected text. This has deleterious side effects for other note insertion methods. 2. "Click-precise": =M-i=, followed by clicking a location on the page. - The location recorded by =org-noter= is typically inaccurate in the - horizontal direction. This is something I want to fix. - - Standard notes can also be inserted in two ways: 1. "TAB". Hitting tab creates a note title "Notes for page ##", where the @@ -35,8 +32,6 @@ pointing. ** Bugs to fix - 1. Navigation of nested notes is spotty. =C-M-p= and =C-M-.= have trouble - detecting nested notes. ** Trying out the refactor of org-noter-insert-note 1. load `org-noter-dev.el' diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index b8afc08..b5a64eb 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -90,7 +90,7 @@ (+ (window-hscroll) (car col-row))))) (setq v-position (car click-position) h-position (cdr click-position))))) - (setq h-position (max 0 (+ h-position -0.02))) + (setq h-position (max 0 (+ h-position org-noter-arrow-horizontal-offset))) (cons v-position h-position)))) (add-to-list 'org-noter--get-precise-info-hook #'org-noter-pdf--get-precise-info) diff --git a/org-noter-core.el b/org-noter-core.el index ea28ccf..6449a94 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -223,6 +223,15 @@ This is needed in order to keep Emacs from hanging when doing many syncs." :group 'org-noter :type 'number) +(defcustom org-noter-arrow-horizontal-offset -0.02 + "Horizontal offset in the position of the tooltip arrow relative + +to precise location. Units are % of page, positive values move +the arrow to the right, while negative values move it to the +left." + :group 'org-noter + :type 'number) + (defcustom org-noter-doc-property-in-notes nil "If non-nil, every new note will have the document property too. This makes moving notes out of the root heading easier." From 980aa8febb3f076bf1ea99e4649a621c57e81121 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 8 Jan 2023 15:41:44 -0800 Subject: [PATCH 234/453] add features wishlist to README.org --- README.org | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/README.org b/README.org index efecbc0..dc3d1ea 100644 --- a/README.org +++ b/README.org @@ -86,3 +86,53 @@ | new note | with new title | never | with new title | | body | selected-text (not title) | long selected-text | selected-text (not title) | |-----------------+---------------------------+------------------------+---------------------------| +** Features wishlist + 1. Expand location handling to include (L T R B) edge info + + Types of location + - Page + - Page pos-v + - Page pos-v . pos-h + - Page pos-h pos-v pos-right pos-bottom + + Swap v and h for consistency (T L B R) ordering? Not consistent w/ + image.el, but consistent with org-noter. + + + Possible functions affected. + - [ ] 943:(defun org-noter--notes-window-location-property (ast) + - [ ] 957:(defun org-noter--auto-save-last-location-property (ast) + - [ ] 972:(defun org-noter--doc-approx-location (&optional precise-info force-new-ref) + - [ ] 983:(defun org-noter--location-change-advice (&rest _) + - [ ] 998:(defun org-noter--check-location-property (arg) + - [ ] 1011:(defun org-noter--parse-location-property (arg) + - [ ] 1024:(defun org-noter--pretty-print-location (location) + - [ ] 1168:(defun org-noter--get-location-top (location) + - [ ] 1174:(defun org-noter--get-location-page (location) + - [ ] 1178:(defun org-noter--get-location-left (location) + - [ ] 1186:(defun org-noter--doc-goto-location (location) + - [ ] 1195:(defun org-noter--compare-location-cons (comp l1 l2) + - [ ] 1240:(defun org-noter--compare-locations (comp l1 l2) + - [ ] 1391:(defun org-noter--get-view-info (view &optional new-location) + - [ ] 1522:(defun org-noter--doc-location-change-handler () + - [ ] 1625:(defun org-noter-set-start-location (&optional arg) + - [ ] 1642:(defun org-noter-set-auto-save-last-location (arg) + - [ ] 1751:(defun org-noter-set-notes-window-location (arg) + + 2. [@2] Use pdf-view-current-pagelabel to use the page label instead of page in + default titles + + May require altering the org-noter-X--pretty-print-location-hook(s). + + 3. Profile org-noter -- runs slow on large books (500+ pages) + + 4. Bind M- to precise-note, no-questions. + + 5. Make background of arrow transparent (see org-noter--show-arrow) + + 6. Handle 2-column pdfs with notes in read-order. + + Need to define a document property TWO-COLUMN and reference it. Maybe + make page a real number with #.0 for the left column and #.5 for the right + column? + From 3a8dac33fcdb4c288510df99fe99b07f7626372d Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 8 Jan 2023 16:27:36 -0800 Subject: [PATCH 235/453] add highlighting of long selected text and defcustom to (en/dis)able feature --- README.org | 42 +++++------------------------------------- org-noter-dev.el | 8 ++++++++ 2 files changed, 13 insertions(+), 37 deletions(-) diff --git a/README.org b/README.org index dc3d1ea..d41bbc0 100644 --- a/README.org +++ b/README.org @@ -87,50 +87,18 @@ | body | selected-text (not title) | long selected-text | selected-text (not title) | |-----------------+---------------------------+------------------------+---------------------------| ** Features wishlist - 1. Expand location handling to include (L T R B) edge info - - Types of location - - Page - - Page pos-v - - Page pos-v . pos-h - - Page pos-h pos-v pos-right pos-bottom - - Swap v and h for consistency (T L B R) ordering? Not consistent w/ - image.el, but consistent with org-noter. - - - Possible functions affected. - - [ ] 943:(defun org-noter--notes-window-location-property (ast) - - [ ] 957:(defun org-noter--auto-save-last-location-property (ast) - - [ ] 972:(defun org-noter--doc-approx-location (&optional precise-info force-new-ref) - - [ ] 983:(defun org-noter--location-change-advice (&rest _) - - [ ] 998:(defun org-noter--check-location-property (arg) - - [ ] 1011:(defun org-noter--parse-location-property (arg) - - [ ] 1024:(defun org-noter--pretty-print-location (location) - - [ ] 1168:(defun org-noter--get-location-top (location) - - [ ] 1174:(defun org-noter--get-location-page (location) - - [ ] 1178:(defun org-noter--get-location-left (location) - - [ ] 1186:(defun org-noter--doc-goto-location (location) - - [ ] 1195:(defun org-noter--compare-location-cons (comp l1 l2) - - [ ] 1240:(defun org-noter--compare-locations (comp l1 l2) - - [ ] 1391:(defun org-noter--get-view-info (view &optional new-location) - - [ ] 1522:(defun org-noter--doc-location-change-handler () - - [ ] 1625:(defun org-noter-set-start-location (&optional arg) - - [ ] 1642:(defun org-noter-set-auto-save-last-location (arg) - - [ ] 1751:(defun org-noter-set-notes-window-location (arg) - - 2. [@2] Use pdf-view-current-pagelabel to use the page label instead of page in + 1. Use pdf-view-current-pagelabel to use the page label instead of page in default titles May require altering the org-noter-X--pretty-print-location-hook(s). - 3. Profile org-noter -- runs slow on large books (500+ pages) + 2. Profile org-noter -- runs slow on large books (500+ pages) - 4. Bind M- to precise-note, no-questions. + 3. Bind M- to precise-note, no-questions. - 5. Make background of arrow transparent (see org-noter--show-arrow) + 4. Make background of arrow transparent (see org-noter--show-arrow) - 6. Handle 2-column pdfs with notes in read-order. + 5. Handle 2-column pdfs with notes in read-order. Need to define a document property TWO-COLUMN and reference it. Maybe make page a real number with #.0 for the left column and #.5 for the right diff --git a/org-noter-dev.el b/org-noter-dev.el index d138b40..c44fa5c 100644 --- a/org-noter-dev.el +++ b/org-noter-dev.el @@ -3,6 +3,11 @@ :group 'org-noter :type 'integer) +(defcustom org-noter-highlight-long-selections t + "Highlight long text selections in document" + :group 'org-noter + :type 'boolean) + (defun phm/org-noter-insert-note (&optional precise-info) "Insert note associated with the current location. @@ -47,6 +52,9 @@ Guiding principles for this (phm/) refactor (<= (length selected-text) org-noter-max-short-length)) selected-text))) (with-local-quit + (if (and org-noter-highlight-long-selections selected-text (not short-selected-text)) + (pdf-annot-add-highlight-markup-annotation (pdf-view-active-region t))) + (select-frame-set-input-focus (window-frame window)) (select-window window) From 9d437bfeecd617f6ee701eb18f787d8cae31eb81 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sun, 8 Jan 2023 18:34:32 -0800 Subject: [PATCH 236/453] [Feature] Preserve highlights as properties. --- modules/org-noter-pdf.el | 39 ++++++++++++++++++++------------------- org-noter-core.el | 21 +++++++++++++++++---- 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 5d09d8b..7328caa 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -25,11 +25,17 @@ ;;; Code: (require 'org-noter) +(defun org-noter-pdf-get-highlight-location () + (if (pdf-view-active-region-p) + (list 'PDF-HIGHLIGHT (image-mode-window-get 'page) (pdf-view-active-region)) + nil)) + +(add-to-list 'org-noter--get-highlight-location-hook #'org-noter-pdf-get-highlight-location) (defun org-noter-pdf-approx-location-cons (major-mode &optional precise-info _force-new-ref) (when (memq major-mode '(doc-view-mode pdf-view-mode)) - (cons (image-mode-window-get 'page) (if (and (listp precise-info) + (cons (image-mode-window-get 'page) (if (and (consp precise-info) (numberp (car precise-info)) - (numberp (cadr precise-info))) + (numberp (cdr precise-info))) precise-info 0)))) (defun org-noter-get-buffer-file-name-pdf (&optional major-mode) @@ -72,31 +78,25 @@ (add-to-list 'org-noter--pretty-print-location-hook #'org-noter-pdf--pretty-print-location) -(defun org-noter-pdf--get-precise-info (major-mode) - (when (eq major-mode 'pdf-view-mode) (if (pdf-view-active-region-p) - (let ((edges (pdf-view-active-region))) - (car edges)) +(defun org-noter-pdf--get-precise-info (major-mode window) + (when (eq major-mode 'pdf-view-mode) + (if (pdf-view-active-region-p) + (let ((edges (pdf-view-active-region))) + (cons + (cadar edges) + (- (caar edges) 0.02))) + (let ((event nil)) (while (not (and (eq 'mouse-1 (car event)) (eq window (posn-window (event-start event))))) (setq event (read-event "Click where you want the start of the note to be!"))) (let ((col-row (posn-col-row (event-start event)))) (org-noter--conv-page-scroll-percentage (+ (window-vscroll) (cdr col-row)) - (+ (window-hscroll) (car col-row))))))) + (+ (window-hscroll) (car col-row)))))))) (add-to-list 'org-noter--get-precise-info-hook #'org-noter-pdf--get-precise-info) -(defun org-noter-doc--get-precise-info (major-mode) - (when (eq major-mode 'doc-view-mode) - (while (not (and (eq 'mouse-1 (car event)) - (eq window (posn-window (event-start event))))) - (setq event (read-event "Click where you want the start of the note to be!"))) - (org-noter--conv-page-scroll-percentage (+ (window-vscroll) - (cdr (posn-col-row (event-start event))))))) - -(add-to-list 'org-noter--get-precise-info-hook #'org-noter-doc--get-precise-info) - (defun org-noter-pdf-goto-location (mode location) (when (memq mode '(doc-view-mode pdf-view-mode)) @@ -321,8 +321,9 @@ (add-to-list 'org-noter-highlight-precise-note-hook #'org-noter-pdf-highlight-location) (defun org-noter-pdf-highlight-location (mode precise-location) "Highlight a precise location in PDF" - (when (memq mode '(doc-view-mode pdf-view-mode)) - (pdf-annot-add-highlight-markup-annotation precise-location))) + (when (and (memq mode '(doc-view-mode pdf-view-mode)) + (pdf-view-active-region-p)) + (pdf-annot-add-highlight-markup-annotation (pdf-view-active-region)))) (provide 'org-noter-pdf) ;;; org-noter-pdf.el ends here diff --git a/org-noter-core.el b/org-noter-core.el index f8ac203..cbf93d1 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -245,6 +245,11 @@ The title used will be the default one." :group 'org-noter :type 'hook) +(defcustom org-noter--get-highlight-location-hook nil + "Hook that runs to get the location of a highlight" + :group 'org-noter + :type 'hook) + (defcustom org-noter-find-additional-notes-functions nil "Functions that when given a document file path as argument, give out an org note file path. @@ -1104,7 +1109,7 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (let ((window (org-noter--get-doc-window)) (mode (org-noter--session-doc-mode session))) (with-selected-window window - (run-hook-with-args-until-success 'org-noter--get-precise-info-hook mode))))) + (run-hook-with-args-until-success 'org-noter--get-precise-info-hook mode window))))) (defun org-noter--show-arrow () (when (and org-noter--arrow-location @@ -1893,7 +1898,7 @@ want to kill." (user-error "This command is not supported for %s" (org-noter--session-doc-mode session))))) -(defun org-noter-insert-note (&optional precise-info note-title) +(defun org-noter-insert-note (&optional precise-info note-title highlight-location) "Insert note associated with the current location. This command will prompt for a title of the note and then insert @@ -2020,6 +2025,7 @@ defines if the text should be inserted inside the note." ;; NOTE(nox): This is needed to insert in the right place (unless (org-noter--no-heading-p) (outline-show-entry)) (org-noter--insert-heading level title empty-lines-number location) + (org-entry-put nil "PDF_HIGHLIGHT" (format "%s" highlight-location)) (when quote-p (save-excursion (insert "#+BEGIN_QUOTE\n" selected-text "\n#+END_QUOTE"))) @@ -2053,12 +2059,19 @@ See `org-noter-insert-note' docstring for more." (let ((org-noter-insert-note-no-questions (if toggle-no-questions (not org-noter-insert-note-no-questions) org-noter-insert-note-no-questions)) - (precise-info (org-noter--get-precise-info))) - (org-noter-insert-note precise-info) + (precise-info (org-noter--get-precise-info)) + (highlight-location (org-noter--get-highlight-location))) + + (org-noter-insert-note precise-info nil highlight-location) (select-frame-set-input-focus (org-noter--session-frame session)) (select-window (get-buffer-window (org-noter--session-doc-buffer session))) + + ;; TODO precise info is wrong here. should be removed (run-hook-with-args-until-success 'org-noter-highlight-precise-note-hook major-mode precise-info)))) +(defun org-noter--get-highlight-location () + (with-selected-window (org-noter--get-doc-window) + (run-hook-with-args-until-success 'org-noter--get-highlight-location-hook))) (defun org-noter-insert-note-toggle-no-questions () "Insert note associated with the current location. From 0d92a5d6ca0041d52cacb12003a8ef34979e3ab6 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sun, 8 Jan 2023 18:36:34 -0800 Subject: [PATCH 237/453] Removing stale code. --- modules/org-noter-pdf.el | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 7328caa..f2d69da 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -78,7 +78,6 @@ (add-to-list 'org-noter--pretty-print-location-hook #'org-noter-pdf--pretty-print-location) - (if (pdf-view-active-region-p) (defun org-noter-pdf--get-precise-info (major-mode window) (when (eq major-mode 'pdf-view-mode) From bd3c4bd798cf573c1d4905d67c76a250876ad4a7 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Mon, 9 Jan 2023 16:56:45 -0800 Subject: [PATCH 238/453] add to feature/bug list --- README.org | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.org b/README.org index d41bbc0..0912c89 100644 --- a/README.org +++ b/README.org @@ -32,6 +32,8 @@ pointing. ** Bugs to fix + 1. Sometimes (when?) M-p doesn't pick up the containing note-at-point right + away (or at all), requiring user to manually type in the (existing) title ** Trying out the refactor of org-noter-insert-note 1. load `org-noter-dev.el' @@ -104,3 +106,4 @@ make page a real number with #.0 for the left column and #.5 for the right column? + 6. Dedicated insert-selected-text-into-page-note From 74da8e5107be36ec6ff33ea07ac3f039cb625512 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Mon, 9 Jan 2023 17:12:18 -0800 Subject: [PATCH 239/453] whitespace cleanup on all org-noter*.el files --- modules/org-noter-djvu.el | 2 +- modules/org-noter-nov.el | 4 ++-- modules/org-noter-pdf.el | 4 ++-- org-noter-core.el | 22 +++++++++++----------- org-noter-dev.el | 3 +-- 5 files changed, 17 insertions(+), 18 deletions(-) diff --git a/modules/org-noter-djvu.el b/modules/org-noter-djvu.el index fd93021..a174b72 100644 --- a/modules/org-noter-djvu.el +++ b/modules/org-noter-djvu.el @@ -20,7 +20,7 @@ ;;; Commentary: -;; +;; ;;; Code: (require 'org-noter) diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el index 07724d6..20a4101 100644 --- a/modules/org-noter-nov.el +++ b/modules/org-noter-nov.el @@ -20,7 +20,7 @@ ;;; Commentary: -;; +;; ;;; Code: (require 'org-noter) @@ -150,7 +150,7 @@ (setq nov-documents-index 0)) (push (vector title (list nov-documents-index (point)) relative-level) output-data))) (push (vector "Skeleton" (list 0) 1) output-data) - + (nov-goto-document origin-index) (goto-char origin-point))) (save-excursion diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index b5a64eb..3c325dc 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -20,7 +20,7 @@ ;;; Commentary: -;; +;; ;;; Code: (require 'org-noter) @@ -78,7 +78,7 @@ (let (v-position h-position) (if (pdf-view-active-region-p) (let ((edges (car (pdf-view-active-region)))) - (setq v-position (min (nth 1 edges) (nth 3 edges)) + (setq v-position (min (nth 1 edges) (nth 3 edges)) h-position (min (nth 0 edges) (nth 2 edges)))) (let ((event nil)) diff --git a/org-noter-core.el b/org-noter-core.el index 6449a94..a80ca42 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -674,7 +674,7 @@ If nil, the session used will be `org-noter--session'." (cond ((and (not arg-is-session) (vectorp info)) ;; NOTE(nox): Use arguments to find heading, by trying to find the outermost parent heading with - ;; the specified property + ;; the specified property (let ((notes-buffer (aref info 0)) (wanted-prop (aref info 1))) (unless (and (buffer-live-p notes-buffer) (or (stringp wanted-prop) @@ -685,7 +685,7 @@ If nil, the session used will be `org-noter--session'." (with-current-buffer notes-buffer (org-with-wide-buffer (catch 'break - (while t + (while t (let ((document-property (or (org-entry-get nil org-noter-property-doc-file t) (cadar (org-collect-keywords (list org-noter-property-doc-file)))))) (when (string= (or (run-hook-with-args-until-success 'org-noter-parse-document-property-hook document-property) @@ -1018,7 +1018,7 @@ properties, by a margin of NEWLINES-NUMBER." (or (run-hook-with-args-until-success 'org-noter--parse-location-property-hook property) (let ((value (car (read-from-string property)))) (cond ((and (consp value) (integerp (car value)) (numberp (cdr value))) value) - ((and (consp value) (integerp (car value)) (consp (cdr value)) (numberp (cadr value)) (numberp (cddr value))) value) + ((and (consp value) (integerp (car value)) (consp (cdr value)) (numberp (cadr value)) (numberp (cddr value))) value) ((integerp value) (cons value 0)))))))) (defun org-noter--pretty-print-location (location) @@ -1069,9 +1069,9 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (defun org-noter--doc-get-page-slice () "Return (slice-top . slice-height)." (let* ((slice (or (image-mode-window-get 'slice) '(0 0 1 1))) - (slice-left (float (nth 0 slice))) + (slice-left (float (nth 0 slice))) (slice-top (float (nth 1 slice))) - (slice-width (float (nth 2 slice))) + (slice-width (float (nth 2 slice))) (slice-height (float (nth 3 slice)))) (when (or (> slice-top 1) (> slice-height 1)) @@ -1129,7 +1129,7 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (image-left (if (floatp (aref org-noter--arrow-location 3)) (round (* (aref org-noter--arrow-location 3) (car (pdf-view-image-size)))))) - (dx (or image-left + (dx (or image-left (+ (or (car (window-margins)) 0) (car (window-fringes))))) (dy (or image-top 0)) @@ -1247,9 +1247,9 @@ L2 or, when in the same page, if L1 is the _f_irst of the two." (t (setq l1 (or (run-hook-with-args-until-success 'org-noter--convert-to-location-cons-hook l1) l1) l2 (or (run-hook-with-args-until-success 'org-noter--convert-to-location-cons-hook l2) l2)) - (if (numberp (cdr l2)) + (if (numberp (cdr l2)) (org-noter--compare-location-cons comp l1 l2) - (org-noter--compare-location-cons comp l1 (cons (car l2) (cadr l2))))))) + (org-noter--compare-location-cons comp l1 (cons (car l2) (cadr l2))))))) (defun org-noter--show-note-entry (session note) "This will show the note entry and its children. @@ -1489,7 +1489,7 @@ relative to." (org-element-property :end (cdr reference-for-insertion))))) (setq reference-for-insertion (cons 'after element))))))) nil nil org-noter--note-search-no-recurse) - + (org-noter--view-region-finish current-region-info) (setf (org-noter--session-num-notes-in-view session) (length notes-in-view)) @@ -1578,7 +1578,7 @@ relative to." (setq doc-prop (or (run-hook-with-args-until-success 'org-noter-parse-document-property-hook doc-prop) doc-prop)) - + (unless (org-noter--check-doc-prop doc-prop) (setq doc-prop nil) @@ -2019,7 +2019,7 @@ defines if the text should be inserted inside the note." (lambda (section) (org-element-property :end section)) nil t org-element-all-elements) (point-max)))) - + (setq level (1+ (or (org-element-property :level ast) 0))) ;; NOTE(nox): This is needed to insert in the right place diff --git a/org-noter-dev.el b/org-noter-dev.el index c44fa5c..e0530f5 100644 --- a/org-noter-dev.el +++ b/org-noter-dev.el @@ -33,8 +33,7 @@ Guiding principles for this (phm/) refactor 2. Selected text should be used in the note, either as the title or in the body 3. Refrain from making notes in the same location with the same title 4. Precise notes generally have different locations, always make new - precise notes -" + precise notes" (interactive) (org-noter--with-valid-session (let* ((ast (org-noter--parse-root)) (contents (org-element-contents ast)) From fde8f3e10f4c0c0b62c1a5d673560427830e81f5 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Mon, 9 Jan 2023 19:18:22 -0800 Subject: [PATCH 240/453] preparatory notes for Friday's discussion --- README.org | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/README.org b/README.org index 0912c89..f18fcd1 100644 --- a/README.org +++ b/README.org @@ -107,3 +107,102 @@ column? 6. Dedicated insert-selected-text-into-page-note +** Deltas w/ dmitrym +*** *-get-buffer-file-name-* + My arglist: =(mode)= + Dmitry's arglist: =(&optional major-mode)= + + P: =major-mode= is a native elisp function, =mode= is a better name + D: the arg is not used, so the =&optional= is appropriate + + proposal: =(&optional mode)= +*** *-get-precise-info-* + My arglist: =(major-mode window)= + D's arglist: =(major-mode)= + + P: calling function already calls =org-noter--get-doc-window= + P: =window= is used in all document modes + + proposal: change =major-mode= to =mode=, pass in =window= +*** *-goto-location* + Mine uses =window= as the 3rd arg to ...-hook; + Dmitry's calls =org-noter--get-doc-window= in ...pdf...-hook + + P: calling function already calls =org-noter--get-doc-window= + D: nov and djvu don't need the =window= argument + + proposal: we discuss this one +*** *-check-location-property-* + P: t (short circuit) + D: (equal 5 (length (read property)))) + + location can be + 1. page + 2. page v-pos + 3. page v-pos . h-pos + + neither function works properly. need to read the calling function to + determine course of action.. +*** *doc--get-precise-info-* + I need to read this one. +*** -get-buffer-file-name-hook + P: nil + D: =(...nov ...pdf) + + should be nil in org-noter-core and set in modules. +*** --doc-approx-location-hook + D: references 5-element location + + docstring needs to be update on D's side. +*** --note-search-no-recurse + P: called in org-noter--get-view-info by org-element-map + D: missing + + these are equivalent +#+begin_src diff +- nil nil (delete 'headline (append org-element-all-elements nil)))) ++ nil nil org-noter--note-search-no-recurse) +#+end_src + + but this is used by org-noter--map-ignore-headings-with-doc-file, which is + used by all of the sync functions + + probably should keep it, and since we keepit, use it in org-noter--get-view-info +*** org-noter--create-session + P: =org-link-bracket-re= + D: =org-bracket-link-regexp= (obsolete) + + keep mine. +*** (defun org-noter--narrow-to-root (ast) + Dmitry: red + Mine : green +#+begin_src diff +- (when ast ++ (when (and ast (not (org-noter--no-heading-p))) + (save-excursion + (goto-char (org-element-property :contents-begin ast)) + (org-show-entry) +- (when (org-at-heading-p) (org-narrow-to-subtree)) ++ (org-narrow-to-subtree) + (org-cycle-hide-drawers 'all)))) +#+end_src +*** (defun org-noter--get-location-page (location) + #+begin_src diff + "Get the page number given a LOCATION of form (page top . left) or (page . top)." +- (message "===> %s" location) +- (if (listp location) +- (car location) +- location)) ++ (car location)) + #+end_src + +** precise note fix +*** *pdf--get-precise-info-* + P: fixed calculation of precise location + D: legacy code + + keep mine. +*** org-noter--conv-page-scroll-percentage + diffs are part of my precise-note fix. +** highlighting + my solution is compact, but too pdf-specific. From 4216778e362941db99c2e0f1f7506f874c197387 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Mon, 9 Jan 2023 21:25:15 -0800 Subject: [PATCH 241/453] add diff code blocks to README.org --- README.org | 148 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 103 insertions(+), 45 deletions(-) diff --git a/README.org b/README.org index f18fcd1..83225fc 100644 --- a/README.org +++ b/README.org @@ -108,33 +108,74 @@ 6. Dedicated insert-selected-text-into-page-note ** Deltas w/ dmitrym + In the diffs below the color coding is + #+begin_src diff +- Dmitry ++ Peter + #+end_src *** *-get-buffer-file-name-* - My arglist: =(mode)= - Dmitry's arglist: =(&optional major-mode)= +#+begin_src diff +-(defun org-noter-get-buffer-file-name-* (&optional major-mode) ++(defun org-noter-get-buffer-file-name-* (mode) + (bound-and-true-p *-file-name)) - P: =major-mode= is a native elisp function, =mode= is a better name - D: the arg is not used, so the =&optional= is appropriate ++(add-to-list 'org-noter-get-buffer-file-name-hook #'org-noter-get-buffer-file-name-*) +#+end_src - proposal: =(&optional mode)= -*** *-get-precise-info-* - My arglist: =(major-mode window)= - D's arglist: =(major-mode)= + - =major-mode= is a native elisp function, =mode= is a better name + - the arg is not used, so the =&optional= is appropriate + - for the =pdf= variant, we both use =(&optional major-mode)= - P: calling function already calls =org-noter--get-doc-window= - P: =window= is used in all document modes + proposal: =(&optional mode)= or remove the argument completely. +*** -get-buffer-file-name-hook +#+begin_src diff +-(defcustom org-noter-get-buffer-file-name-hook '(org-noter-get-buffer-file-name-nov org-noter-get-buffer-file-name-pdf) ++(defcustom org-noter-get-buffer-file-name-hook nil +#+end_src - proposal: change =major-mode= to =mode=, pass in =window= -*** *-goto-location* - Mine uses =window= as the 3rd arg to ...-hook; - Dmitry's calls =org-noter--get-doc-window= in ...pdf...-hook + should be nil in =org-noter-core= and set in modules. +*** *-get-precise-info-* +#+begin_src diff +-(defun org-noter-*--get-precise-info (major-mode) ++(defun org-noter-*--get-precise-info (major-mode window) + (when (eq major-mode 'djvu-read-mode) + (if (region-active-p) + (cons (mark) (point)) +- (while (not (and (eq 'mouse-1 (car event)) +- (eq window (posn-window (event-start event))))) +- (setq event (read-event "Click where you want the start of the note to be!"))) +- (posn-point (event-start event))))) ++ (let ((event nil)) ++ (while (not (and (eq 'mouse-1 (car event)) ++ (eq window (posn-window (event-start event))))) ++ (setq event (read-event "Click where you want the start of the note to be!"))) ++ (posn-point (event-start event)))))) +#+end_src - P: calling function already calls =org-noter--get-doc-window= - D: nov and djvu don't need the =window= argument + - calling function already calls =org-noter--get-doc-window= + - =window= is used in all document modes - proposal: we discuss this one -*** *-check-location-property-* - P: t (short circuit) - D: (equal 5 (length (read property)))) + proposal: change =major-mode= to =mode=, pass in =window= +*** *-goto-location +#+begin_src diff +-(defun org-noter-pdf-goto-location (mode location) ++(defun org-noter-pdf-goto-location (mode location window) + (when (memq mode '(doc-view-mode pdf-view-mode)) + (let ((top (org-noter--get-location-top location)) +- (window (org-noter--get-doc-window)) + (left (org-noter--get-location-left location))) +#+end_src + - calling function already calls =org-noter--get-doc-window= + - nov and djvu don't need the =window= argument + + proposal: we discuss this one, but I think it's better to not call functions unnecessarily +*** *-check-location-property + #+begin_src diff + (defun org-noter-pdf-check-location-property (&optional property) + "Check if PROPERTY is a valid location property" +- (equal 5 (length (read property)))) ++ t) + #+end_src location can be 1. page @@ -143,39 +184,59 @@ neither function works properly. need to read the calling function to determine course of action.. -*** *doc--get-precise-info-* - I need to read this one. -*** -get-buffer-file-name-hook - P: nil - D: =(...nov ...pdf) - - should be nil in org-noter-core and set in modules. +*** -doc--get-precise-info +#+begin_src diff ++(defun org-noter-doc--get-precise-info (major-mode window) ++ (when (eq major-mode 'doc-view-mode) + (let ((event nil)) + (while (not (and (eq 'mouse-1 (car event)) + (eq window (posn-window (event-start event))))) + (setq event (read-event "Click where you want the start of the note to be!"))) +- (let ((col-row (posn-col-row (event-start event)))) +- (org-noter--conv-page-scroll-percentage (+ (window-vscroll) (cdr col-row)) +- (+ (window-hscroll) (car col-row)))))))) ++ (org-noter--conv-page-scroll-percentage (+ (window-vscroll) ++ (cdr (posn-col-row (event-start event)))))))) +#+end_src + I need to read this one. Not sure if diff did it right... *** --doc-approx-location-hook - D: references 5-element location - - docstring needs to be update on D's side. + #+begin_src diff + (defcustom org-noter--doc-approx-location-hook nil +- "This returns an approximate location if no precise info is passed: (PAGE 0) +- or if precise info is passed, it's (PAGE 0 0 0 0) where 0s are the precise coords) +-" ++ "TODO" + :group 'org-noter + :type 'hook) + #+end_src + + docstring needs to be updated. *** --note-search-no-recurse - P: called in org-noter--get-view-info by org-element-map - D: missing +#+begin_src diff ++(defconst org-noter--note-search-no-recurse (delete 'headline (append org-element-all-elements nil)) ++ "List of elements that shouldn't be recursed into when searching for notes.") +#+end_src - these are equivalent + called in =org-noter--get-view-info= by =org-element-map= #+begin_src diff - nil nil (delete 'headline (append org-element-all-elements nil)))) + nil nil org-noter--note-search-no-recurse) #+end_src - but this is used by org-noter--map-ignore-headings-with-doc-file, which is + but this defconst is used by =org-noter--map-ignore-headings-with-doc-file=, which is used by all of the sync functions - probably should keep it, and since we keepit, use it in org-noter--get-view-info + probably should keep it, and since we keep it, use it in =org-noter--get-view-info= *** org-noter--create-session - P: =org-link-bracket-re= - D: =org-bracket-link-regexp= (obsolete) - - keep mine. + #+begin_src diff + (defun org-noter--create-session (ast document-property-value notes-file-path) + (let* ((raw-value-not-empty (> (length (org-element-property :raw-value ast)) 0)) +- (link-p (or (string-match-p org-bracket-link-regexp document-property-value) ++ (link-p (or (string-match-p org-link-bracket-re document-property-value) + (string-match-p org-noter--url-regexp document-property-value))) + #+end_src + =org-link-bracket-re= is obsolete. keep mine. *** (defun org-noter--narrow-to-root (ast) - Dmitry: red - Mine : green #+begin_src diff - (when ast + (when (and ast (not (org-noter--no-heading-p))) @@ -198,11 +259,8 @@ ** precise note fix *** *pdf--get-precise-info-* - P: fixed calculation of precise location - D: legacy code - keep mine. *** org-noter--conv-page-scroll-percentage diffs are part of my precise-note fix. ** highlighting - my solution is compact, but too pdf-specific. + my solution is compact, but too pdf-specific. need to sketch out Dmitry's solution From 1ee3f5aaa3706e9a85103b58e338b3c53f896dff Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Tue, 10 Jan 2023 06:45:10 -0800 Subject: [PATCH 242/453] Ensure that a highlight property only inserted when a highlight is available. --- org-noter-core.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/org-noter-core.el b/org-noter-core.el index cbf93d1..beae0d1 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -2025,7 +2025,8 @@ defines if the text should be inserted inside the note." ;; NOTE(nox): This is needed to insert in the right place (unless (org-noter--no-heading-p) (outline-show-entry)) (org-noter--insert-heading level title empty-lines-number location) - (org-entry-put nil "PDF_HIGHLIGHT" (format "%s" highlight-location)) + (when highlight-location + (org-entry-put nil "HIGHLIGHT" (format "%s" highlight-location))) (when quote-p (save-excursion (insert "#+BEGIN_QUOTE\n" selected-text "\n#+END_QUOTE"))) From 02e9ff6cded2c46c3e50062c7bf1a3c5f4407b06 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Tue, 10 Jan 2023 06:46:43 -0800 Subject: [PATCH 243/453] Update the signature for the get-precise-info hook. --- tests/org-noter-core-tests.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/org-noter-core-tests.el b/tests/org-noter-core-tests.el index 99a1be5..885fa65 100644 --- a/tests/org-noter-core-tests.el +++ b/tests/org-noter-core-tests.el @@ -100,7 +100,7 @@ org-noter-core-test-return-text (defun org-noter-core-test-get-current-view (mode) t) -(defun org-noter-core-test-get-precise-info (mode) +(defun org-noter-core-test-get-precise-info (mode window) (message "🧪org-noter-core-test-get-precise-info %s" mode) (list 1 2 3 4)) From f291e5f4990e50be2696f9b7ef6dd631468a269e Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Tue, 10 Jan 2023 06:47:16 -0800 Subject: [PATCH 244/453] Add unit tests for persisting highlights in org. --- tests/org-noter-core-tests.el | 42 +++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/org-noter-core-tests.el b/tests/org-noter-core-tests.el index 885fa65..096db59 100644 --- a/tests/org-noter-core-tests.el +++ b/tests/org-noter-core-tests.el @@ -113,6 +113,8 @@ org-noter-core-test-return-text (defun org-noter-core-test-get-current-view (mode) 'org-noter-core-test-view) +(defun org-noter-core-test-get-highlight-location () + "HARDCODED_HIGHLIGHT_LOCATION") (describe "org-noter-core" (before-each @@ -260,4 +262,44 @@ org-noter-core-test-return-text )))) ) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (describe "persistent highlights" + (describe "no hooks are setup for precise note highlights" + ;; if no hooks for highlights are setup we expect no :HIGHLIGHT: property + (it "can take a precise note without a highlight appearing" + (with-mock-contents + mock-contents-simple-notes-file + '(lambda () + (org-noter-core-test-create-session) + (with-simulated-input "precise SPC note RET" + (org-noter-insert-precise-note)) + (message "--- no highlight with note: %s" (buffer-string)) + (expect (string-match ":HIGHLIGHT:" (buffer-string)) :to-be nil))))) + + + (describe "hooks for persistent highlights are setup" + ;; setup hooks for highlighting + (before-each + (add-to-list 'org-noter--get-highlight-location-hook #'org-noter-core-test-get-highlight-location) + (spy-on 'org-noter-core-test-get-highlight-location :and-call-through) + ) + ;; now that the hooks for highlights are setup, we expect :HIGHLIGHT: property to appear. + (it "can take a precise note WITH a highlight appearing" + (with-mock-contents + mock-contents-simple-notes-file + '(lambda () + (org-noter-core-test-create-session) + (with-simulated-input "precise SPC note RET" + (org-noter-insert-precise-note)) + (message "with note: %s" (buffer-string)) + (expect (string-match "\\:HIGHLIGHT\\:" (buffer-string)) :not :to-be nil) + (expect (string-match "HARDCODED_HIGHLIGHT_LOCATION" (buffer-string)) :not :to-be nil))))) + + ) + + + + + ) From 1212a104541149630e4a4868e99df617f2d3489d Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Tue, 10 Jan 2023 06:47:50 -0800 Subject: [PATCH 245/453] Add location unit tests. --- tests/org-noter-core-tests.el | 43 +++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/org-noter-core-tests.el b/tests/org-noter-core-tests.el index 096db59..7557edf 100644 --- a/tests/org-noter-core-tests.el +++ b/tests/org-noter-core-tests.el @@ -262,6 +262,49 @@ org-noter-core-test-return-text )))) ) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (describe "locations" + (defvar test-precise-location '(3 1 . 0.1)) + (defvar test-simple-location '(3 1)) + (defvar test-extra-precise-location '(4 1 0.1 0.2 0.3)) + + (describe "precise locations" + (it "can get page from a precise location" + (expect (org-noter--get-location-page test-precise-location) :to-equal 3)) + + (it "can get top from a precise location" + (expect (org-noter--get-location-top test-precise-location) :to-equal 1)) + + (it "can get left from a precise location" + (expect (org-noter--get-location-left test-precise-location) :to-equal 0.1)) + ) + + (describe "simple locations" + + (it "doesn't get a left location for simple location" + (expect (org-noter--get-location-left test-simple-location) :to-equal nil) + ) + + (it "can get top from a simple location" + (expect (org-noter--get-location-top test-simple-location) :to-equal 1)) + + (it "can get page from a simple location" + (expect (org-noter--get-location-page test-simple-location) :to-equal 3)) + ) + + (describe "extra precise locations" + (it "can get page from an extra precise location" + (expect (org-noter--get-location-page test-extra-precise-location) :to-equal 4)) + + (it "can get top from an extra precise location" + (expect (org-noter--get-location-top test-extra-precise-location) :to-equal 1)) + + + (it "can get left from an extra precise location" + (expect (org-noter--get-location-left test-extra-precise-location) :to-equal 0.1))) + ) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (describe "persistent highlights" From 04cdce58aca994d039e12183ed8c9653c012f896 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Tue, 10 Jan 2023 06:48:23 -0800 Subject: [PATCH 246/453] Run unit tests on every push. --- .github/workflows/build-test.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 22dbfd1..f558b97 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -1,7 +1,4 @@ -on: - push: - branches: - - master +on: push jobs: build: From b018dafc4b931d068136044d8d02b307582160ea Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Tue, 10 Jan 2023 06:52:39 -0800 Subject: [PATCH 247/453] (whitespace-cleanup) --- org-noter-core.el | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index beae0d1..1d61e3b 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -677,7 +677,7 @@ If nil, the session used will be `org-noter--session'." (cond ((and (not arg-is-session) (vectorp info)) ;; NOTE(nox): Use arguments to find heading, by trying to find the outermost parent heading with - ;; the specified property + ;; the specified property (let ((notes-buffer (aref info 0)) (wanted-prop (aref info 1))) (unless (and (buffer-live-p notes-buffer) (or (stringp wanted-prop) @@ -688,7 +688,7 @@ If nil, the session used will be `org-noter--session'." (with-current-buffer notes-buffer (org-with-wide-buffer (catch 'break - (while t + (while t (let ((document-property (or (org-entry-get nil org-noter-property-doc-file t) (cadar (org-collect-keywords (list org-noter-property-doc-file)))))) (when (string= (or (run-hook-with-args-until-success 'org-noter-parse-document-property-hook document-property) @@ -1021,7 +1021,7 @@ properties, by a margin of NEWLINES-NUMBER." (or (run-hook-with-args-until-success 'org-noter--parse-location-property-hook property) (let ((value (car (read-from-string property)))) (cond ((and (consp value) (integerp (car value)) (numberp (cdr value))) value) - ((and (consp value) (integerp (car value)) (consp (cdr value)) (numberp (cadr value)) (numberp (cddr value))) value) + ((and (consp value) (integerp (car value)) (consp (cdr value)) (numberp (cadr value)) (numberp (cddr value))) value) ((integerp value) (cons value 0)))))))) (defun org-noter--pretty-print-location (location) @@ -1072,9 +1072,9 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (defun org-noter--doc-get-page-slice () "Return (slice-top . slice-height)." (let* ((slice (or (image-mode-window-get 'slice) '(0 0 1 1))) - (slice-left (float (nth 0 slice))) + (slice-left (float (nth 0 slice))) (slice-top (float (nth 1 slice))) - (slice-width (float (nth 2 slice))) + (slice-width (float (nth 2 slice))) (slice-height (float (nth 3 slice)))) (when (or (> slice-top 1) (> slice-height 1)) @@ -1094,7 +1094,7 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (display-percentage-height (/ vscroll (cdr display-size))) (hpercentage (max 0 (min 1 (+ (nth 0 slice) (* (nth 1 slice) display-percentage-height)))))) (if hscroll - (cons hpercentage (max 0 (min 1 (+ (nth 2 slice) (* (nth 3 slice) (/ vscroll (car display-size))))))) + (cons hpercentage (max 0 (min 1 (+ (nth 2 slice) (* (nth 3 slice) (/ vscroll (car display-size))))))) (cons hpercentage 0)))) (defun org-noter--conv-page-percentage-scroll (percentage) @@ -1124,7 +1124,7 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (image-left (if (floatp (aref org-noter--arrow-location 3)) (round (* (aref org-noter--arrow-location 3) (car (pdf-view-image-size)))))) - (dx (or image-left + (dx (or image-left (+ (or (car (window-margins)) 0) (car (window-fringes))))) (dy (or image-top 0)) @@ -1245,9 +1245,9 @@ L2 or, when in the same page, if L1 is the _f_irst of the two." (t (setq l1 (or (run-hook-with-args-until-success 'org-noter--convert-to-location-cons-hook l1) l1) l2 (or (run-hook-with-args-until-success 'org-noter--convert-to-location-cons-hook l2) l2)) - (if (numberp (cdr l2)) + (if (numberp (cdr l2)) (org-noter--compare-location-cons comp l1 l2) - (org-noter--compare-location-cons comp l1 (cons (car l2) (cadr l2))))))) + (org-noter--compare-location-cons comp l1 (cons (car l2) (cadr l2))))))) (defun org-noter--show-note-entry (session note) "This will show the note entry and its children. @@ -1489,7 +1489,7 @@ relative to." (org-element-property :end (cdr reference-for-insertion))))) (setq reference-for-insertion (cons 'after element))))))) nil nil (delete 'headline (append org-element-all-elements nil)))) - + (org-noter--view-region-finish current-region-info) (setf (org-noter--session-num-notes-in-view session) (length notes-in-view)) @@ -1578,7 +1578,7 @@ relative to." (setq doc-prop (or (run-hook-with-args-until-success 'org-noter-parse-document-property-hook doc-prop) doc-prop)) - + (unless (org-noter--check-doc-prop doc-prop) (setq doc-prop nil) @@ -2019,7 +2019,7 @@ defines if the text should be inserted inside the note." (lambda (section) (org-element-property :end section)) nil t org-element-all-elements) (point-max)))) - + (setq level (1+ (or (org-element-property :level ast) 0))) ;; NOTE(nox): This is needed to insert in the right place From 2eb85fc6c35444dff0035a04c5d24c6218ffff03 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Wed, 11 Jan 2023 15:11:01 -0800 Subject: [PATCH 248/453] Use ht module to structure highlight (+ unit tests). --- Cask | 3 ++- modules/org-noter-pdf.el | 20 ++++++++++++-------- tests/org-noter-pdf-tests.el | 31 ++++++++++++++++++++++--------- 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/Cask b/Cask index 45f15a5..4e78066 100644 --- a/Cask +++ b/Cask @@ -3,5 +3,6 @@ (depends-on "buttercup") (depends-on "pdf-tools") +(depends-on "ht") -(depends-on "with-simulated-input") \ No newline at end of file +(depends-on "with-simulated-input") diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index f2d69da..0aa75e5 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -24,13 +24,23 @@ ;;; Code: (require 'org-noter) +(require 'ht) (defun org-noter-pdf-get-highlight-location () - (if (pdf-view-active-region-p) - (list 'PDF-HIGHLIGHT (image-mode-window-get 'page) (pdf-view-active-region)) + "If there's an active pdf selection, returns a ht that contains all +the relevant info (page, coordinates, highlight type). + +Otherwise returns nil" + (-if-let* ((_ (pdf-view-active-region-p)) + (page (image-mode-window-get 'page)) + (coords (pdf-view-active-region))) + (ht->plist (ht ('PAGE page) + ('TYPE 'PDF-HIGHLIGHT) + ('COORDS coords))) nil)) (add-to-list 'org-noter--get-highlight-location-hook #'org-noter-pdf-get-highlight-location) + (defun org-noter-pdf-approx-location-cons (major-mode &optional precise-info _force-new-ref) (when (memq major-mode '(doc-view-mode pdf-view-mode)) (cons (image-mode-window-get 'page) (if (and (consp precise-info) @@ -43,12 +53,6 @@ (bound-and-true-p pdf-file-name)) -(defun org-noter-pdf-check-location-property (&optional property) - "Check if PROPERTY is a valid location property" - (equal 5 (length (read property)))) - - -;; (add-to-list 'org-noter--check-location-property-hook #'org-noter-pdf-check-location-property) (add-to-list 'org-noter--doc-approx-location-hook #'org-noter-pdf-approx-location-cons) (defun org-noter-pdf-view-setup-handler (major-mode) diff --git a/tests/org-noter-pdf-tests.el b/tests/org-noter-pdf-tests.el index 0870812..b6212b3 100644 --- a/tests/org-noter-pdf-tests.el +++ b/tests/org-noter-pdf-tests.el @@ -1,15 +1,28 @@ (add-to-list 'load-path "modules") (require 'org-noter-pdf) (describe "org-noter-pdf-functionality" + ;; todo refactor 👇 (describe "location functionality" - (it "can correctly recognize precise notes location" - (expect - (org-noter-pdf-check-location-property "(16 0.3073263558515699 0.7254290171606864 0.2274024738344434 0.65600624024961)") - :to-equal t)) - (it "correctly rejects non location properties" - (expect - (org-noter-pdf-check-location-property "(asdf)") - :to-equal nil)) + ) + + + (describe "pdf specific highlight functionality" + (before-each + (spy-on 'pdf-view-active-region-p :and-return-value t) + (spy-on 'pdf-view-active-region :and-return-value '(0.1 0.2 0.3 0.4)) + (spy-on 'image-mode-window-get :and-return-value 747) + ) + + (it "can get coordinates from pdf-view" + (let ((highlight-info (org-noter-pdf-get-highlight-location)) + (expected-highlight-info (ht ('PAGE 747) + ('TYPE 'PDF-HIGHLIGHT) + ('COORDS '(0.1 0.2 0.3 0.4))))) + (expect 'pdf-view-active-region-p :to-have-been-called) + (expect highlight-info :to-equal (ht->plist expected-highlight-info)))) ) - ) + + + +) From b38fa2992f5fdcd58d21b54bcbcb62a2704cd1af Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Wed, 11 Jan 2023 15:12:06 -0800 Subject: [PATCH 249/453] Remove stale code; add comments. --- tests/org-noter-core-tests.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/org-noter-core-tests.el b/tests/org-noter-core-tests.el index 7557edf..5cf71e3 100644 --- a/tests/org-noter-core-tests.el +++ b/tests/org-noter-core-tests.el @@ -129,6 +129,7 @@ org-noter-core-test-return-text (spy-on 'org-noter-core-test-highlight-location :and-call-through) (spy-on 'org-noter-core-test-get-current-view :and-call-through) + ;; register all the hooks so we can fake a org-noter-test mode (add-to-list 'org-noter-get-selected-text-hook #'org-noter-test-get-selected-text) (add-to-list 'org-noter-parse-document-property-hook #'org-noter-core-test-document-property) (add-to-list 'org-noter-set-up-document-hook #'org-noter-core-test-view-setup-handler) @@ -137,7 +138,6 @@ org-noter-core-test-return-text (add-to-list 'org-noter--get-current-view-hook #'org-noter-core-test-get-current-view) (add-to-list 'org-noter--get-precise-info-hook #'org-noter-core-test-get-precise-info) (add-to-list 'org-noter--pretty-print-location-hook #'org-noter-core-test-pretty-print-location) - (add-to-list 'org-noter--pretty-print-location-hook #'org-noter-core-test-pretty-print-location) (add-to-list 'org-noter-highlight-precise-note-hook #'org-noter-core-test-highlight-location) ) From dacde433a1190e098b16f729d5509acd8dfb2062 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Wed, 11 Jan 2023 15:28:26 -0800 Subject: [PATCH 250/453] Add test utils file for common test functionality. --- org-noter-test-utils.el | 120 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 org-noter-test-utils.el diff --git a/org-noter-test-utils.el b/org-noter-test-utils.el new file mode 100644 index 0000000..5491fb2 --- /dev/null +++ b/org-noter-test-utils.el @@ -0,0 +1,120 @@ + +(require 'log4e) + +;; org-noter-test logger = ont +(log4e:deflogger "ont" "ont %t [%l] %m" "%H:%M:%S") +(ont--log-enable-logging) +(ont--log-enable-debugging) +(ont--log-enable-messaging) +(ont--log-set-level 'info) +(ont--log-debug "ont") + + +;;;;;;;;;;; +;; helpers +(defun org-noter-core-test-create-session () + "Call this manually with an existing notes buffer to generate a new session" + (org-noter--create-session (org-noter--parse-root) "pubs/solove-nothing-to-hide.pdf" org-noter-test-file)) + + +(defun with-mock-contents (contents lambda) + "Create a real buffer with CONTENTS and then execute the LAMBDA" + (ont--log-debug "\n--------------------------------------------") + + ;; TODO: when an assert fails in buttercup, an exception (??) is thrown, + ;; so temp file isnt being cleaned up. This is the sledgehammer approach. + ;; Needs to be fixed so that it's cleaned up properly. + (when (boundp 'org-noter-test-file) + (progn + (ont--log-debug (format "Removing org-noter-test-file: %s\n" org-noter-test-file)) + (delete-file org-noter-test-file))) + (let* ((tempfile (make-temp-file "Notes" nil ".org" contents))) + (ont--log-debug (format "Creating a tempfile: %s\n" tempfile)) + (setq org-noter-test-file tempfile) + (ont--log-debug "Opening the file..") + (org-mode) + (find-file tempfile) + (org-mode) + (ont--log-debug "Starting the test..") + (ont--log-debug "%s" (buffer-string)) + (funcall lambda) + (ont--log-debug "About to kill buffer..") + (kill-current-buffer) + (ont--log-debug (format "Removing tempfile %s" tempfile)) + (delete-file tempfile) + (ont--log-debug "+++++++++++++++++++++++++++++++++++++++++") + )) + + +;;;;;;;;;;;;;;;;;;;;;;;;;; +;; hooks - org-noter calls these + +(defun org-noter-test-get-selected-text (mode) + "⚠️org-noter-core-test-return-text +org-noter-core-test-return-text +org-noter-core-test-return-text +org-noter-core-test-return-text +org-noter-core-test-return-text +") + + +(defun org-noter-core-test-document-property (&optional param) + org-noter-test-file) + +(defun org-noter-core-test-view-setup-handler (&optional param) + t) + +(defun org-noter-core-test-open-document-functions (&optional doc) + (find-file (org-noter-core-test-document-property))) + +(defun org-noter-core-test-approx-location (major-mode &optional precise-info _force-new-ref) + (cons 99 precise-info)) + +(defun org-noter-core-test-get-current-view (mode) + t) + +;; TODO This doesn't look right +(defun org-noter-core-test-get-precise-info (mode window) + (list 1 2 3 4)) + +(defun org-noter-core-test-pretty-print-location (location) + (format "%s" location)) + +(defun org-noter-core-test-highlight-location (major-mode precise-info) + t) + +(defun org-noter-core-test-get-current-view (mode) + 'org-noter-core-test-view) + +(defun org-noter-core-test-get-highlight-location () + "HARDCODED_HIGHLIGHT_LOCATION") + + +(defun create-org-noter-test-session () + + ;; if this is not set; make-session fails and the test crashes with a stack overflow. + (setq org-noter-always-create-frame nil) + + ;; setup spies so we can verify that things have been called + (spy-on 'org-noter-test-get-selected-text :and-call-through) + (spy-on 'org-noter-core-test-approx-location :and-call-through) + (spy-on 'org-noter-core-test-get-precise-info :and-call-through) + (spy-on 'org-noter-core-test-highlight-location :and-call-through) + (spy-on 'org-noter-core-test-get-current-view :and-call-through) + + ;; register all the hooks so we can fake a org-noter-test mode + (add-to-list 'org-noter-get-selected-text-hook #'org-noter-test-get-selected-text) + (add-to-list 'org-noter-parse-document-property-hook #'org-noter-core-test-document-property) + (add-to-list 'org-noter-set-up-document-hook #'org-noter-core-test-view-setup-handler) + (add-to-list 'org-noter-open-document-functions #'org-noter-core-test-open-document-functions) + (add-to-list 'org-noter--doc-approx-location-hook #'org-noter-core-test-approx-location) + (add-to-list 'org-noter--get-current-view-hook #'org-noter-core-test-get-current-view) + (add-to-list 'org-noter--get-precise-info-hook #'org-noter-core-test-get-precise-info) + (add-to-list 'org-noter--pretty-print-location-hook #'org-noter-core-test-pretty-print-location) + (add-to-list 'org-noter-highlight-precise-note-hook #'org-noter-core-test-highlight-location) + ) + + + + +(provide 'org-noter-test-utils) From 3e0a5733868690fdfe029294c05a6eb8dd6b523c Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Wed, 11 Jan 2023 15:28:56 -0800 Subject: [PATCH 251/453] Add a logging library. --- Cask | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cask b/Cask index 4e78066..371c8b8 100644 --- a/Cask +++ b/Cask @@ -6,3 +6,5 @@ (depends-on "ht") (depends-on "with-simulated-input") + +(depends-on "log4e") From b21f493ecda7b6cee0297859a2e1bd19358ad8bf Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Wed, 11 Jan 2023 15:29:36 -0800 Subject: [PATCH 252/453] Extract test session creation functionality out of org-noter-core-tests file. --- org-noter-core.el | 1 - tests/org-noter-core-tests.el | 120 ++-------------------------------- tests/org-noter-pdf-tests.el | 2 + 3 files changed, 6 insertions(+), 117 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 1d61e3b..ecc8c8e 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1168,7 +1168,6 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (defun org-noter--get-location-page (location) "Get the page number given a LOCATION of form (page top . left) or (page . top)." - (message "===> %s" location) (if (listp location) (car location) location)) diff --git a/tests/org-noter-core-tests.el b/tests/org-noter-core-tests.el index 5cf71e3..db0c851 100644 --- a/tests/org-noter-core-tests.el +++ b/tests/org-noter-core-tests.el @@ -1,6 +1,7 @@ (add-to-list 'load-path "modules") (require 'org-noter-pdf) (require 'with-simulated-input) +(require 'org-noter-test-utils) (defvar mock-contents-simple-notes-file " @@ -32,115 +33,10 @@ ) -;;;;;;;;;;; -;; helpers -(defun org-noter-core-test-create-session () - "Call this manually with an existing notes buffer to generate a new session" - (org-noter--create-session (org-noter--parse-root) "pubs/solove-nothing-to-hide.pdf" org-noter-test-file)) - - -(defun with-mock-contents (contents lambda) - "Create a real buffer with CONTENTS and then execute the LAMBDA" - (message "\n--------------------------------------------") - - ;; TODO: when an assert fails in buttercup, an exception (??) is thrown, - ;; so temp file isnt being cleaned up. This is the sledgehammer approach. - ;; Needs to be fixed so that it's cleaned up properly. - (when (boundp 'org-noter-test-file) - (progn - (message (format "Removing org-noter-test-file: %s\n" org-noter-test-file)) - (delete-file org-noter-test-file))) - (let* ((tempfile (make-temp-file "Notes" nil ".org" contents))) - (message (format "Creating a tempfile: %s\n" tempfile)) - (setq org-noter-test-file tempfile) - (message "Opening the file..") - (org-mode) - (find-file tempfile) - (org-mode) - (message "Starting the test..") - (message "%s" (buffer-string)) - (funcall lambda) - (message "About to kill buffer..") - (kill-current-buffer) - (message (format "Removing tempfile %s" tempfile)) - (delete-file tempfile) - (message "+++++++++++++++++++++++++++++++++++++++++") - )) - - -;;;;;;;;;;;;;;;;;;;;;;;;;; -;; hooks - org-noter calls these - -(defun org-noter-test-get-selected-text (mode) - (message "🧪org-noter-core-test-return-text") - "⚠️org-noter-core-test-return-text -org-noter-core-test-return-text -org-noter-core-test-return-text -org-noter-core-test-return-text -org-noter-core-test-return-text -") - - -(defun org-noter-core-test-document-property (&optional param) - (message "🧪org-noter-core-test-document-property %s" param) - org-noter-test-file) - -(defun org-noter-core-test-view-setup-handler (&optional param) - (message "org-noter-core-test-view-setup-handler") - t) - -(defun org-noter-core-test-open-document-functions (&optional doc) - (message "org-noter-core-test-open-document-functions") - (find-file (org-noter-core-test-document-property))) - -(defun org-noter-core-test-approx-location (major-mode &optional precise-info _force-new-ref) - (message "approx-location %s" precise-info) - (cons 99 precise-info)) - -(defun org-noter-core-test-get-current-view (mode) - t) - -(defun org-noter-core-test-get-precise-info (mode window) - (message "🧪org-noter-core-test-get-precise-info %s" mode) - (list 1 2 3 4)) - -(defun org-noter-core-test-pretty-print-location (location) - (format "%s" location)) - -(defun org-noter-core-test-highlight-location (major-mode precise-info) - t) - -(defun org-noter-core-test-get-current-view (mode) - 'org-noter-core-test-view) - -(defun org-noter-core-test-get-highlight-location () - "HARDCODED_HIGHLIGHT_LOCATION") - (describe "org-noter-core" - (before-each - ;; if this is not set; make-session fails and the test crashes with a stack overflow. - (setq org-noter-always-create-frame nil) - - - ;; setup spies so we can verify that things have been called - (spy-on 'org-noter-test-get-selected-text :and-call-through) - (spy-on 'org-noter-core-test-approx-location :and-call-through) - (spy-on 'org-noter-core-test-get-precise-info :and-call-through) - (spy-on 'org-noter-core-test-highlight-location :and-call-through) - (spy-on 'org-noter-core-test-get-current-view :and-call-through) - - ;; register all the hooks so we can fake a org-noter-test mode - (add-to-list 'org-noter-get-selected-text-hook #'org-noter-test-get-selected-text) - (add-to-list 'org-noter-parse-document-property-hook #'org-noter-core-test-document-property) - (add-to-list 'org-noter-set-up-document-hook #'org-noter-core-test-view-setup-handler) - (add-to-list 'org-noter-open-document-functions #'org-noter-core-test-open-document-functions) - (add-to-list 'org-noter--doc-approx-location-hook #'org-noter-core-test-approx-location) - (add-to-list 'org-noter--get-current-view-hook #'org-noter-core-test-get-current-view) - (add-to-list 'org-noter--get-precise-info-hook #'org-noter-core-test-get-precise-info) - (add-to-list 'org-noter--pretty-print-location-hook #'org-noter-core-test-pretty-print-location) - (add-to-list 'org-noter-highlight-precise-note-hook #'org-noter-core-test-highlight-location) - - ) + (before-each + (create-org-noter-test-session) + ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -150,7 +46,6 @@ org-noter-core-test-return-text (with-mock-contents mock-contents-simple-notes-file '(lambda () (let ((mock-ast (org-noter--parse-root))) - (message "%s" mock-ast) (expect mock-ast :not :to-be nil))))) ;; basic note should insert a default heading @@ -160,7 +55,6 @@ org-noter-core-test-return-text '(lambda () (org-noter-core-test-create-session) (org-noter-insert-note nil "NEW NOTE") - (message "with note: %s" (buffer-string)) (expect 'org-noter-test-get-selected-text :to-have-been-called) (expect (string-match "Notes for page" (buffer-string)) :not :to-be nil)))) @@ -172,7 +66,6 @@ org-noter-core-test-return-text (org-noter-core-test-create-session) (with-simulated-input "precise SPC note RET" (org-noter-insert-precise-note)) - (message "with note: %s" (buffer-string)) (expect (string-match "precise note" (buffer-string)) :not :to-be nil)))) ;; there should be precise data in the note properties when entering a precise note @@ -236,7 +129,6 @@ org-noter-core-test-return-text '(lambda () (org-noter-core-test-create-session) (let* ((session org-noter--session)) - (message "0000000000000000000000 %s" (type-of (org-noter--session-display-name session))) (expect (org-noter--session-property-text session) :to-equal "pubs/solove-nothing-to-hide.pdf") (expect (org-noter--session-display-name session) :to-equal "solove-nothing-to-hide") (expect (org-noter--session-notes-file-path session) :to-equal org-noter-test-file) @@ -257,7 +149,6 @@ org-noter-core-test-return-text '(lambda () (org-noter-core-test-create-session) (let* ((view-info (org-noter--get-view-info (org-noter--get-current-view)))) - (message "%s" view-info) (expect 'org-noter-core-test-get-current-view :to-have-been-called) )))) ) @@ -317,7 +208,6 @@ org-noter-core-test-return-text (org-noter-core-test-create-session) (with-simulated-input "precise SPC note RET" (org-noter-insert-precise-note)) - (message "--- no highlight with note: %s" (buffer-string)) (expect (string-match ":HIGHLIGHT:" (buffer-string)) :to-be nil))))) @@ -335,10 +225,8 @@ org-noter-core-test-return-text (org-noter-core-test-create-session) (with-simulated-input "precise SPC note RET" (org-noter-insert-precise-note)) - (message "with note: %s" (buffer-string)) (expect (string-match "\\:HIGHLIGHT\\:" (buffer-string)) :not :to-be nil) (expect (string-match "HARDCODED_HIGHLIGHT_LOCATION" (buffer-string)) :not :to-be nil))))) - ) diff --git a/tests/org-noter-pdf-tests.el b/tests/org-noter-pdf-tests.el index b6212b3..9b27319 100644 --- a/tests/org-noter-pdf-tests.el +++ b/tests/org-noter-pdf-tests.el @@ -1,5 +1,7 @@ (add-to-list 'load-path "modules") (require 'org-noter-pdf) +(require 'org-noter-test-utils) + (describe "org-noter-pdf-functionality" ;; todo refactor 👇 (describe "location functionality" From 226d89e117b565097eb768cbc5683650278dce70 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Wed, 11 Jan 2023 15:51:42 -0800 Subject: [PATCH 253/453] Move "mock" contents to utils. --- org-noter-test-utils.el | 32 ++++++++++++++++++++++++++++++++ tests/org-noter-core-tests.el | 29 ----------------------------- 2 files changed, 32 insertions(+), 29 deletions(-) diff --git a/org-noter-test-utils.el b/org-noter-test-utils.el index 5491fb2..6466a37 100644 --- a/org-noter-test-utils.el +++ b/org-noter-test-utils.el @@ -10,6 +10,38 @@ (ont--log-debug "ont") +(defvar mock-contents-simple-notes-file + " +:PROPERTIES: +:ID: FAKE_1 +:END: +#+TITLE: Test book notes (simple) +* solove-nothing-to-hide +:PROPERTIES: +:NOTER_DOCUMENT: pubs/solove-nothing-to-hide.pdf +:END: +") + +(defvar mock-contents-simple-notes-file-with-a-single-note + ":PROPERTIES: +:ID: FAKE_90283 +:END: +#+TITLE: Test book notes + +* solove-nothing-to-hide +:PROPERTIES: +:NOTER_DOCUMENT: pubs/solove-nothing-to-hide.pdf +:END: +** Note from page 1 +:PROPERTIES: +:NOTER_PAGE: 99 +:END: +" +) + + + + ;;;;;;;;;;; ;; helpers (defun org-noter-core-test-create-session () diff --git a/tests/org-noter-core-tests.el b/tests/org-noter-core-tests.el index db0c851..2d81a04 100644 --- a/tests/org-noter-core-tests.el +++ b/tests/org-noter-core-tests.el @@ -3,35 +3,6 @@ (require 'with-simulated-input) (require 'org-noter-test-utils) -(defvar mock-contents-simple-notes-file - " -:PROPERTIES: -:ID: FAKE_1 -:END: -#+TITLE: Test book notes (simple) -* solove-nothing-to-hide -:PROPERTIES: -:NOTER_DOCUMENT: pubs/solove-nothing-to-hide.pdf -:END: -") - -(defvar mock-contents-simple-notes-file-with-a-single-note - ":PROPERTIES: -:ID: FAKE_90283 -:END: -#+TITLE: Test book notes - -* solove-nothing-to-hide -:PROPERTIES: -:NOTER_DOCUMENT: pubs/solove-nothing-to-hide.pdf -:END: -** Note from page 1 -:PROPERTIES: -:NOTER_PAGE: 99 -:END: -" -) - (describe "org-noter-core" (before-each From ed65eb413c0ee9c46884163bc7857c74b9bf265d Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Wed, 11 Jan 2023 15:53:40 -0800 Subject: [PATCH 254/453] Add end to end test for persiting pdf highlights. --- tests/org-noter-pdf-tests.el | 38 ++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/tests/org-noter-pdf-tests.el b/tests/org-noter-pdf-tests.el index 9b27319..e39b876 100644 --- a/tests/org-noter-pdf-tests.el +++ b/tests/org-noter-pdf-tests.el @@ -2,6 +2,11 @@ (require 'org-noter-pdf) (require 'org-noter-test-utils) + +(defvar expected-highlight-info (ht ('PAGE 747) + ('TYPE 'PDF-HIGHLIGHT) + ('COORDS '(0.1 0.2 0.3 0.4)))) + (describe "org-noter-pdf-functionality" ;; todo refactor 👇 (describe "location functionality" @@ -16,15 +21,32 @@ ) (it "can get coordinates from pdf-view" - (let ((highlight-info (org-noter-pdf-get-highlight-location)) - (expected-highlight-info (ht ('PAGE 747) - ('TYPE 'PDF-HIGHLIGHT) - ('COORDS '(0.1 0.2 0.3 0.4))))) - + (let ((highlight-info (org-noter-pdf-get-highlight-location))) (expect 'pdf-view-active-region-p :to-have-been-called) (expect highlight-info :to-equal (ht->plist expected-highlight-info)))) - ) - + (describe "highlight persistence" + (before-each + (create-org-noter-test-session) + ;; (create-org-noter-test-session) sets up a highlight hook, so we have to reset it back. + ;; this might be ok for now? maybe filter out all "-core-test-" hooks instead? + (setq org-noter--get-highlight-location-hook '(org-noter-pdf-get-highlight-location)) + ) + (it "can take a precise note WITH a highlight appearing" + (with-mock-contents + mock-contents-simple-notes-file + '(lambda () + (org-noter-core-test-create-session) + (with-simulated-input "precise SPC note RET" + (org-noter-insert-precise-note)) + (ont--log-debug "%s" (buffer-string)) + (expect (string-match "\\:HIGHLIGHT\\:" (buffer-string)) :not :to-be nil) + (expect (string-match (format "%s" (ht->plist expected-highlight-info)) (buffer-string)) :not :to-be nil) + ) + + )) + + ) -) + ) + ) From 5fb008de2a1ac644ca6f0640923b12cf14e1ab25 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Wed, 11 Jan 2023 21:57:05 -0800 Subject: [PATCH 255/453] tag some diffs with commit ID hashes --- README.org | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/README.org b/README.org index 83225fc..01f8650 100644 --- a/README.org +++ b/README.org @@ -198,7 +198,7 @@ + (org-noter--conv-page-scroll-percentage (+ (window-vscroll) + (cdr (posn-col-row (event-start event)))))))) #+end_src - I need to read this one. Not sure if diff did it right... +Dmitry removed this function at [9d437bf] *** --doc-approx-location-hook #+begin_src diff (defcustom org-noter--doc-approx-location-hook nil @@ -211,7 +211,7 @@ #+end_src docstring needs to be updated. -*** --note-search-no-recurse +*** --note-search-no-recurse :11fc0a8:9dfac53: #+begin_src diff +(defconst org-noter--note-search-no-recurse (delete 'headline (append org-element-all-elements nil)) + "List of elements that shouldn't be recursed into when searching for notes.") @@ -227,7 +227,7 @@ used by all of the sync functions probably should keep it, and since we keep it, use it in =org-noter--get-view-info= -*** org-noter--create-session +*** org-noter--create-session :9dfac53: #+begin_src diff (defun org-noter--create-session (ast document-property-value notes-file-path) (let* ((raw-value-not-empty (> (length (org-element-property :raw-value ast)) 0)) @@ -236,7 +236,7 @@ (string-match-p org-noter--url-regexp document-property-value))) #+end_src =org-link-bracket-re= is obsolete. keep mine. -*** (defun org-noter--narrow-to-root (ast) +*** org-noter--narrow-to-root (ast) :dfe7df2: #+begin_src diff - (when ast + (when (and ast (not (org-noter--no-heading-p))) @@ -247,7 +247,10 @@ + (org-narrow-to-subtree) (org-cycle-hide-drawers 'all)))) #+end_src -*** (defun org-noter--get-location-page (location) + "I don't really understand this bit of code, especially what `ast' is, but + it breaks narrowing when multiple documents' notes are stored in a single + file." +*** org-noter--get-location-page (location) :c1g:c1ed245: #+begin_src diff "Get the page number given a LOCATION of form (page top . left) or (page . top)." - (message "===> %s" location) @@ -256,7 +259,17 @@ - location)) + (car location)) #+end_src - +*** org-noter-kill-session :9dfac53: + #+begin_src diff + (with-current-buffer notes-buffer + (remove-hook 'kill-buffer-hook 'org-noter--handle-kill-buffer t) + (restore-buffer-modified-p nil)) +- (unless org-noter-use-indirect-buffer ++ (when org-noter-use-indirect-buffer + (kill-buffer notes-buffer)) + #+end_src + kill the notes buffer **when** an indirect buffer is used, not **unless** it + is used ** precise note fix *** *pdf--get-precise-info-* keep mine. From 5aa3019fa85fbc843f18e6151955af53190aedd0 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Thu, 12 Jan 2023 23:21:57 -0800 Subject: [PATCH 256/453] README.org: add probable actions on deltas --- README.org | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/README.org b/README.org index 01f8650..2ffb2e8 100644 --- a/README.org +++ b/README.org @@ -127,6 +127,11 @@ - for the =pdf= variant, we both use =(&optional major-mode)= proposal: =(&optional mode)= or remove the argument completely. + + ACTIONS: + 1. major-mode -> mode in module files + 2. use &optional when the argument is not used in the function + *** -get-buffer-file-name-hook #+begin_src diff -(defcustom org-noter-get-buffer-file-name-hook '(org-noter-get-buffer-file-name-nov org-noter-get-buffer-file-name-pdf) @@ -134,6 +139,8 @@ #+end_src should be nil in =org-noter-core= and set in modules. + + ACTION: *** *-get-precise-info-* #+begin_src diff -(defun org-noter-*--get-precise-info (major-mode) @@ -168,7 +175,10 @@ - calling function already calls =org-noter--get-doc-window= - nov and djvu don't need the =window= argument - proposal: we discuss this one, but I think it's better to not call functions unnecessarily + proposal: we discuss this one, but I think it's better to not call functions + unnecessarily + + ACTION: *** *-check-location-property #+begin_src diff (defun org-noter-pdf-check-location-property (&optional property) @@ -199,6 +209,8 @@ + (cdr (posn-col-row (event-start event)))))))) #+end_src Dmitry removed this function at [9d437bf] + +ACTION: *** --doc-approx-location-hook #+begin_src diff (defcustom org-noter--doc-approx-location-hook nil @@ -211,6 +223,8 @@ Dmitry removed this function at [9d437bf] #+end_src docstring needs to be updated. + + ACTION: *** --note-search-no-recurse :11fc0a8:9dfac53: #+begin_src diff +(defconst org-noter--note-search-no-recurse (delete 'headline (append org-element-all-elements nil)) @@ -226,7 +240,12 @@ Dmitry removed this function at [9d437bf] but this defconst is used by =org-noter--map-ignore-headings-with-doc-file=, which is used by all of the sync functions - probably should keep it, and since we keep it, use it in =org-noter--get-view-info= + probably should keep it, and since we keep it, use it in + =org-noter--get-view-info= + + ACTION: safe for Dmitry to cherry-pick these commits, but + =with-current-buffer= call gets removed. This is the one change I took from + ~cbpnk~ *** org-noter--create-session :9dfac53: #+begin_src diff (defun org-noter--create-session (ast document-property-value notes-file-path) @@ -236,6 +255,8 @@ Dmitry removed this function at [9d437bf] (string-match-p org-noter--url-regexp document-property-value))) #+end_src =org-link-bracket-re= is obsolete. keep mine. + + ACTION: safe for Dmitry to cherry-pick *** org-noter--narrow-to-root (ast) :dfe7df2: #+begin_src diff - (when ast @@ -250,6 +271,8 @@ Dmitry removed this function at [9d437bf] "I don't really understand this bit of code, especially what `ast' is, but it breaks narrowing when multiple documents' notes are stored in a single file." + + ACTION: safe for Dmitry to cherry-pick *** org-noter--get-location-page (location) :c1g:c1ed245: #+begin_src diff "Get the page number given a LOCATION of form (page top . left) or (page . top)." @@ -270,6 +293,8 @@ Dmitry removed this function at [9d437bf] #+end_src kill the notes buffer **when** an indirect buffer is used, not **unless** it is used + + ACTION: safe for Dmitry to cherry-pick ** precise note fix *** *pdf--get-precise-info-* keep mine. From f42f4ad97980ef5a77a4c1d225b5548bdbad364d Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Fri, 13 Jan 2023 12:15:30 -0800 Subject: [PATCH 257/453] update README.org w/ notes from 2023-01-13 meeting --- README.org | 49 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/README.org b/README.org index 2ffb2e8..2df2bab 100644 --- a/README.org +++ b/README.org @@ -75,19 +75,20 @@ | title prompt? | Y | Y | N | | default title 1 | prior note by location | selected-text | N/A | | default title 2 | "Notes for page #" | "Notes for page # x y" | "Notes for page #" | - | new note | with new title | never | never | + | new note | with new title | always | always | | body | selected-text on existing note | selected-text (> 3 lines) | none | |-----------------+--------------------------------+---------------------------+-------------------------| *** Refactored - | | insert-note =i= | precise note =M-i= | no-questions =C-i, TAB= | - |-----------------+---------------------------+------------------------+---------------------------| - | title prompt? | Y | Y | N | - | default title 1 | short-selected-text | short-selected-text | short-selected-text | - | default title 2 | "Notes for page #" | "Notes for page # x y" | "Notes for page #" | - | new note | with new title | never | with new title | - | body | selected-text (not title) | long selected-text | selected-text (not title) | - |-----------------+---------------------------+------------------------+---------------------------| + | | insert-note =i= | precise note =M-i= | no-questions =C-i, TAB= | + |----------------------+---------------------------+---------------------------+---------------------------| + | title prompt? | Y | Y | N | + | default title 1 | short-selected-text | short-selected-text | short-selected-text | + | default title 2 | "Notes for page #" | "Notes for page # x y" | "Notes for page #" | + | new note | with new title | always | with new title | + | body | selected-text (not title) | selected-text (not title) | selected-text (not title) | + |----------------------+---------------------------+---------------------------+---------------------------| + | highlight selection? | ?? | ?? | ?? | ** Features wishlist 1. Use pdf-view-current-pagelabel to use the page label instead of page in default titles @@ -128,7 +129,7 @@ proposal: =(&optional mode)= or remove the argument completely. - ACTIONS: + ACTIONS: Do this in our own repos before merge 1. major-mode -> mode in module files 2. use &optional when the argument is not used in the function @@ -140,7 +141,7 @@ should be nil in =org-noter-core= and set in modules. - ACTION: + ACTION: already converged *** *-get-precise-info-* #+begin_src diff -(defun org-noter-*--get-precise-info (major-mode) @@ -163,6 +164,8 @@ - =window= is used in all document modes proposal: change =major-mode= to =mode=, pass in =window= + + ACTION: (done) Dmitry took mine *** *-goto-location #+begin_src diff -(defun org-noter-pdf-goto-location (mode location) @@ -179,6 +182,8 @@ unnecessarily ACTION: + pass in window, use &optional as appropriate. + *** *-check-location-property #+begin_src diff (defun org-noter-pdf-check-location-property (&optional property) @@ -194,6 +199,9 @@ neither function works properly. need to read the calling function to determine course of action.. + + ACTION: done, gone on Dmitry's side. + P: check diff, remove if it's still there. *** -doc--get-precise-info #+begin_src diff +(defun org-noter-doc--get-precise-info (major-mode window) @@ -210,7 +218,7 @@ #+end_src Dmitry removed this function at [9d437bf] -ACTION: +ACTION: Dmitry revive on his side. *** --doc-approx-location-hook #+begin_src diff (defcustom org-noter--doc-approx-location-hook nil @@ -224,7 +232,7 @@ ACTION: docstring needs to be updated. - ACTION: + ACTION: Dmitry reverted *** --note-search-no-recurse :11fc0a8:9dfac53: #+begin_src diff +(defconst org-noter--note-search-no-recurse (delete 'headline (append org-element-all-elements nil)) @@ -273,7 +281,7 @@ ACTION: file." ACTION: safe for Dmitry to cherry-pick -*** org-noter--get-location-page (location) :c1g:c1ed245: +*** org-noter--get-location-page (location) commit? #+begin_src diff "Get the page number given a LOCATION of form (page top . left) or (page . top)." - (message "===> %s" location) @@ -282,6 +290,8 @@ ACTION: - location)) + (car location)) #+end_src + + ACTION: Peter -- what happens with bare page? does (car location) make an error? *** org-noter-kill-session :9dfac53: #+begin_src diff (with-current-buffer notes-buffer @@ -302,3 +312,14 @@ ACTION: diffs are part of my precise-note fix. ** highlighting my solution is compact, but too pdf-specific. need to sketch out Dmitry's solution + - in precise-notes + 1. hook to make the highlight in the document + 2. hook to preserve highlight in org-noter file + - what contexts would HL be used? + 1. only precise? (current implementation) + 2. anytime (Peter's implementation) + - how does this play with quoting text? + 1. HL Y/N + 2. Quoting Y/N + 3. No HL if Q + 4. No Q if NL From c6895fbb9d1b44b92f3d742623382c592bb19f1e Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Fri, 13 Jan 2023 12:16:19 -0800 Subject: [PATCH 258/453] remove highlighting (will use dmitrym's development) this implementation is pdf-specific, so if anything, it should be in modules/org-noter-pdf.el, but Dmitry's dev on this topic is much more comprehensive. --- org-noter-dev.el | 8 -------- 1 file changed, 8 deletions(-) diff --git a/org-noter-dev.el b/org-noter-dev.el index e0530f5..1597293 100644 --- a/org-noter-dev.el +++ b/org-noter-dev.el @@ -3,11 +3,6 @@ :group 'org-noter :type 'integer) -(defcustom org-noter-highlight-long-selections t - "Highlight long text selections in document" - :group 'org-noter - :type 'boolean) - (defun phm/org-noter-insert-note (&optional precise-info) "Insert note associated with the current location. @@ -51,9 +46,6 @@ Guiding principles for this (phm/) refactor (<= (length selected-text) org-noter-max-short-length)) selected-text))) (with-local-quit - (if (and org-noter-highlight-long-selections selected-text (not short-selected-text)) - (pdf-annot-add-highlight-markup-annotation (pdf-view-active-region t))) - (select-frame-set-input-focus (window-frame window)) (select-window window) From dfab6ff4e684c96939e1b65702ceb3263354da4c Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Fri, 13 Jan 2023 16:15:26 -0800 Subject: [PATCH 259/453] (pre-merge) in modules, change arg "major-mode" to "mode" also confirmed that unused args (required for equivalent functions in other document modes) are after &optional. --- modules/org-noter-djvu.el | 12 ++++++------ modules/org-noter-nov.el | 14 +++++++------- modules/org-noter-pdf.el | 22 +++++++++++----------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/modules/org-noter-djvu.el b/modules/org-noter-djvu.el index a174b72..f15b325 100644 --- a/modules/org-noter-djvu.el +++ b/modules/org-noter-djvu.el @@ -34,8 +34,8 @@ (add-to-list 'org-noter--pretty-print-location-hook #'org-noter-djvu--pretty-print-location) -(defun org-noter-djvu-approx-location-cons (major-mode &optional precise-info _force-new-ref) - (when (eq major-mode 'djvu-read-mode) +(defun org-noter-djvu-approx-location-cons (mode &optional precise-info _force-new-ref) + (when (eq mode 'djvu-read-mode) (cons djvu-doc-page (if (or (numberp precise-info) (and (consp precise-info) (numberp (car precise-info)) @@ -45,8 +45,8 @@ (add-to-list 'org-noter--doc-approx-location-hook #'org-noter-djvu-approx-location-cons) -(defun org-noter-djvu--get-precise-info (major-mode window) - (when (eq major-mode 'djvu-read-mode) +(defun org-noter-djvu--get-precise-info (mode window) + (when (eq mode 'djvu-read-mode) (if (region-active-p) (cons (mark) (point)) (let ((event nil)) @@ -57,8 +57,8 @@ (add-to-list 'org-noter--get-precise-info-hook #'org-noter-djvu--get-precise-info) -(defun org-noter-djvu-setup-handler (major-mode) - (when (eq major-mode 'djvu-read-mode) +(defun org-noter-djvu-setup-handler (mode) + (when (eq mode 'djvu-read-mode) (advice-add 'djvu-init-page :after 'org-noter--location-change-advice) t)) diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el index 20a4101..18d9701 100644 --- a/modules/org-noter-nov.el +++ b/modules/org-noter-nov.el @@ -25,14 +25,14 @@ ;;; Code: (require 'org-noter) -(defun org-noter-get-buffer-file-name-nov (mode) +(defun org-noter-get-buffer-file-name-nov (&optional mode) (bound-and-true-p nov-file-name)) (add-to-list 'org-noter-get-buffer-file-name-hook #'org-noter-get-buffer-file-name-nov) -(defun org-noter-nov-approx-location-cons (major-mode &optional precise-info _force-new-ref) +(defun org-noter-nov-approx-location-cons (mode &optional precise-info _force-new-ref) (org-noter--with-valid-session - (when (eq (org-noter--session-doc-mode session) 'nov-mode) + (when (eq mode 'nov-mode) (cons nov-documents-index (if (or (numberp precise-info) (and (consp precise-info) (numberp (car precise-info)) @@ -42,8 +42,8 @@ (add-to-list 'org-noter--doc-approx-location-hook #'org-noter-nov-approx-location-cons) -(defun org-noter-nov-setup-handler (major-mode) - (when (eq major-mode 'nov-mode) +(defun org-noter-nov-setup-handler (mode) + (when (eq mode 'nov-mode) (advice-add 'nov-render-document :after 'org-noter--nov-scroll-handler) (add-hook 'window-scroll-functions 'org-noter--nov-scroll-handler nil t) t)) @@ -60,8 +60,8 @@ (add-to-list 'org-noter--pretty-print-location-hook #'org-noter-nov--pretty-print-location) -(defun org-noter-nov--get-precise-info (major-mode window) - (when (eq major-mode 'nov-mode) +(defun org-noter-nov--get-precise-info (mode window) + (when (eq mode 'nov-mode) (if (region-active-p) (cons (mark) (point)) (let ((event nil)) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 3c325dc..ba99067 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -25,8 +25,8 @@ ;;; Code: (require 'org-noter) -(defun org-noter-pdf-approx-location-cons (major-mode &optional precise-info _force-new-ref) - (when (memq major-mode '(doc-view-mode pdf-view-mode)) +(defun org-noter-pdf-approx-location-cons (mode &optional precise-info _force-new-ref) + (when (memq mode '(doc-view-mode pdf-view-mode)) (cons (image-mode-window-get 'page) (if (and (consp precise-info) (numberp (car precise-info)) (numberp (cdr precise-info))) @@ -34,7 +34,7 @@ (add-to-list 'org-noter--doc-approx-location-hook #'org-noter-pdf-approx-location-cons) -(defun org-noter-get-buffer-file-name-pdf (&optional major-mode) +(defun org-noter-get-buffer-file-name-pdf (&optional mode) "Return the file naming backing the document buffer" (bound-and-true-p pdf-file-name)) @@ -46,8 +46,8 @@ (add-to-list 'org-noter--check-location-property-hook #'org-noter-pdf-check-location-property) -(defun org-noter-pdf-view-setup-handler (major-mode) - (when (eq major-mode 'pdf-view-mode) +(defun org-noter-pdf-view-setup-handler (mode) + (when (eq mode 'pdf-view-mode) ;; (setq buffer-file-name document-path) (pdf-view-mode) (add-hook 'pdf-view-after-change-page-hook 'org-noter--doc-location-change-handler nil t) @@ -55,8 +55,8 @@ (add-to-list 'org-noter-set-up-document-hook #'org-noter-pdf-view-setup-handler) -(defun org-noter-doc-view-setup-handler (major-mode) - (when (eq major-mode 'doc-view-mode) +(defun org-noter-doc-view-setup-handler (mode) + (when (eq mode 'doc-view-mode) ;; (setq buffer-file-name document-path) (doc-view-mode) (advice-add 'doc-view-goto-page :after 'org-noter--location-change-advice) @@ -73,8 +73,8 @@ (add-to-list 'org-noter--pretty-print-location-hook #'org-noter-pdf--pretty-print-location) -(defun org-noter-pdf--get-precise-info (major-mode window) - (when (eq major-mode 'pdf-view-mode) +(defun org-noter-pdf--get-precise-info (mode window) + (when (eq mode 'pdf-view-mode) (let (v-position h-position) (if (pdf-view-active-region-p) (let ((edges (car (pdf-view-active-region)))) @@ -95,8 +95,8 @@ (add-to-list 'org-noter--get-precise-info-hook #'org-noter-pdf--get-precise-info) -(defun org-noter-doc--get-precise-info (major-mode window) - (when (eq major-mode 'doc-view-mode) +(defun org-noter-doc--get-precise-info (mode window) + (when (eq mode 'doc-view-mode) (let ((event nil)) (while (not (and (eq 'mouse-1 (car event)) (eq window (posn-window (event-start event))))) From d6511395d7f847135e6142c294081b2c619ebd87 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Fri, 13 Jan 2023 17:14:42 -0800 Subject: [PATCH 260/453] (pre-merge) remove org-noter-pdf-check-location-property --- modules/org-noter-pdf.el | 6 ------ 1 file changed, 6 deletions(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index ba99067..6d3bae4 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -40,12 +40,6 @@ (add-to-list 'org-noter-get-buffer-file-name-hook #'org-noter-get-buffer-file-name-pdf) -(defun org-noter-pdf-check-location-property (&optional property) - "Check if PROPERTY is a valid location property" - t) - -(add-to-list 'org-noter--check-location-property-hook #'org-noter-pdf-check-location-property) - (defun org-noter-pdf-view-setup-handler (mode) (when (eq mode 'pdf-view-mode) ;; (setq buffer-file-name document-path) From 866c8c8c7a04f23c03c724394bd81c0e443931ea Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Fri, 13 Jan 2023 17:15:47 -0800 Subject: [PATCH 261/453] (pre-merge) update README.org with task status and mtg notes --- README.org | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/README.org b/README.org index 2df2bab..b6339d5 100644 --- a/README.org +++ b/README.org @@ -111,10 +111,10 @@ ** Deltas w/ dmitrym In the diffs below the color coding is #+begin_src diff -- Dmitry -+ Peter +- Dmitry [f3f5a05] ++ Peter [6488cc6] #+end_src -*** *-get-buffer-file-name-* +*** [PM:DONE, DM:TBD] *-get-buffer-file-name-* #+begin_src diff -(defun org-noter-get-buffer-file-name-* (&optional major-mode) +(defun org-noter-get-buffer-file-name-* (mode) @@ -133,7 +133,7 @@ 1. major-mode -> mode in module files 2. use &optional when the argument is not used in the function -*** -get-buffer-file-name-hook +*** [PM:DONE, DM:TBD] -get-buffer-file-name-hook #+begin_src diff -(defcustom org-noter-get-buffer-file-name-hook '(org-noter-get-buffer-file-name-nov org-noter-get-buffer-file-name-pdf) +(defcustom org-noter-get-buffer-file-name-hook nil @@ -142,7 +142,7 @@ should be nil in =org-noter-core= and set in modules. ACTION: already converged -*** *-get-precise-info-* +*** [PM:DONE, DM:TBD] *-get-precise-info-* #+begin_src diff -(defun org-noter-*--get-precise-info (major-mode) +(defun org-noter-*--get-precise-info (major-mode window) @@ -166,7 +166,7 @@ proposal: change =major-mode= to =mode=, pass in =window= ACTION: (done) Dmitry took mine -*** *-goto-location +*** [PM:DONE, DM:TBD] *-goto-location #+begin_src diff -(defun org-noter-pdf-goto-location (mode location) +(defun org-noter-pdf-goto-location (mode location window) @@ -184,7 +184,7 @@ ACTION: pass in window, use &optional as appropriate. -*** *-check-location-property +*** [PM:DONE, DM:DONE] *-check-location-property #+begin_src diff (defun org-noter-pdf-check-location-property (&optional property) "Check if PROPERTY is a valid location property" @@ -202,7 +202,7 @@ ACTION: done, gone on Dmitry's side. P: check diff, remove if it's still there. -*** -doc--get-precise-info +*** [PM:DONE, DM:TBD] -doc--get-precise-info #+begin_src diff +(defun org-noter-doc--get-precise-info (major-mode window) + (when (eq major-mode 'doc-view-mode) @@ -219,7 +219,7 @@ Dmitry removed this function at [9d437bf] ACTION: Dmitry revive on his side. -*** --doc-approx-location-hook +*** [PM:DONE, DM:TBD] --doc-approx-location-hook #+begin_src diff (defcustom org-noter--doc-approx-location-hook nil - "This returns an approximate location if no precise info is passed: (PAGE 0) @@ -233,7 +233,7 @@ ACTION: Dmitry revive on his side. docstring needs to be updated. ACTION: Dmitry reverted -*** --note-search-no-recurse :11fc0a8:9dfac53: +*** [PM:DONE, DM:TBD] --note-search-no-recurse :11fc0a8:9dfac53: #+begin_src diff +(defconst org-noter--note-search-no-recurse (delete 'headline (append org-element-all-elements nil)) + "List of elements that shouldn't be recursed into when searching for notes.") @@ -254,7 +254,7 @@ ACTION: Dmitry revive on his side. ACTION: safe for Dmitry to cherry-pick these commits, but =with-current-buffer= call gets removed. This is the one change I took from ~cbpnk~ -*** org-noter--create-session :9dfac53: +*** [PM:DONE, DM:TBD] org-noter--create-session :9dfac53: #+begin_src diff (defun org-noter--create-session (ast document-property-value notes-file-path) (let* ((raw-value-not-empty (> (length (org-element-property :raw-value ast)) 0)) @@ -265,7 +265,7 @@ ACTION: Dmitry revive on his side. =org-link-bracket-re= is obsolete. keep mine. ACTION: safe for Dmitry to cherry-pick -*** org-noter--narrow-to-root (ast) :dfe7df2: +*** [PM:DONE, DM:TBD] org-noter--narrow-to-root (ast) :dfe7df2: #+begin_src diff - (when ast + (when (and ast (not (org-noter--no-heading-p))) @@ -281,7 +281,7 @@ ACTION: Dmitry revive on his side. file." ACTION: safe for Dmitry to cherry-pick -*** org-noter--get-location-page (location) commit? +*** [PM:DONE, DM:TBD] org-noter--get-location-page (location) :DM:629fbb6: #+begin_src diff "Get the page number given a LOCATION of form (page top . left) or (page . top)." - (message "===> %s" location) @@ -291,8 +291,20 @@ ACTION: Dmitry revive on his side. + (car location)) #+end_src - ACTION: Peter -- what happens with bare page? does (car location) make an error? -*** org-noter-kill-session :9dfac53: + ACTION: Peter -- what happens with a page note (no precise location)? does (car location) make an + error? + Answer: No, (car location) works fine because for a page note, location is a + cons cell, e.g. (19 . 0) by the time it reaches this function. + + @DM -- I think we should go back to the original (car location). + + HISTORY: + - 5bc5754 Ahmed Shariff original code + - c1ed245 c1g moved code from org-noter.el to org-noter-core.el, changing + function name + - 629fbb6 introduced by DM + +*** [PM:DONE, DM:TBD] org-noter-kill-session :9dfac53: #+begin_src diff (with-current-buffer notes-buffer (remove-hook 'kill-buffer-hook 'org-noter--handle-kill-buffer t) @@ -305,6 +317,7 @@ ACTION: Dmitry revive on his side. is used ACTION: safe for Dmitry to cherry-pick +*** [DM:TBD] use cl-lib or native elisp hash tables rather than the =ht= package. ** precise note fix *** *pdf--get-precise-info-* keep mine. From c68ad5fcce5be757341a620deebc4a61b41ec787 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Fri, 13 Jan 2023 17:41:25 -0800 Subject: [PATCH 262/453] remove test-mouse-event from noter-dev --- org-noter-dev.el | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/org-noter-dev.el b/org-noter-dev.el index 1597293..3fd2052 100644 --- a/org-noter-dev.el +++ b/org-noter-dev.el @@ -178,26 +178,6 @@ This is like `org-noter-insert-note', except it will toggle `org-noter-insert-no (let ((org-noter-insert-note-no-questions (not org-noter-insert-note-no-questions))) (phm/org-noter-insert-note)))) -(defun phm/org-noter-test-mouse-event () - "test code for precise notes in pdfs" - (interactive) - (let ((event nil) - (window (car (window-list)))) - (while (not (and (eq 'mouse-1 (car event)) - (eq window (posn-window (event-start event))))) - (setq event (read-event "Click where you want the start of the note to be!"))) - (let ((col-row (posn-col-row (event-start event))) - (win-edges (window-inside-edges)) - (display-size (image-display-size (image-get-display-property)))) ; from next fn - (org-noter--conv-page-scroll-percentage (+ (window-vscroll) (cdr col-row)) - (+ (window-hscroll) (car col-row))) - (message (format "%f %f %f %f; Disp (%f %f); Win (%d %d)" - (window-vscroll) (cdr col-row) - (window-hscroll) (car col-row) - (car display-size) (cdr display-size) - (- (nth 2 win-edges) (nth 0 win-edges)) - (- (nth 3 win-edges) (nth 1 win-edges))))))) - (define-key org-noter-doc-mode-map (kbd "i") 'phm/org-noter-insert-note) (define-key org-noter-doc-mode-map (kbd "M-i") 'phm/org-noter-insert-precise-note) (define-key org-noter-doc-mode-map (kbd "C-i") 'phm/org-noter-insert-note-toggle-no-questions) From be742dc0abf630013f97539e0dc58c2d50b209f2 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Fri, 13 Jan 2023 18:39:54 -0800 Subject: [PATCH 263/453] org-noter-pdf--parse-location breaks page notes org-noter--parse-location-property handles the standard location properties without assistance, so the pdf-specific version is not needed. --- modules/org-noter-pdf.el | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 4a57612..8687f64 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -324,13 +324,6 @@ Otherwise returns nil" (add-to-list 'org-noter-create-skeleton-functions #'org-noter-create-skeleton-pdf) -(add-to-list 'org-noter--parse-location-property-hook #'org-noter-pdf--parse-location) - -(defun org-noter-pdf--parse-location (arg) - "return a pdf location from an existing property. expecting (page left)" - (let* ((location (car (read-from-string arg)))) - location)) - (defun org-noter-pdf--create-missing-annotation () "Add a highlight from a selected note." (let* ((location (org-noter--parse-location-property (org-noter--get-containing-element)))) @@ -338,10 +331,8 @@ Otherwise returns nil" (org-noter-pdf-goto-location 'pdf-view-mode location) (pdf-annot-add-highlight-markup-annotation (cdr location))))) - - - (add-to-list 'org-noter-highlight-precise-note-hook #'org-noter-pdf-highlight-location) + (defun org-noter-pdf-highlight-location (mode precise-location) "Highlight a precise location in PDF" (when (and (memq mode '(doc-view-mode pdf-view-mode)) From c6578a447a3448e309980834d56dbdbbbb632eeb Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Fri, 13 Jan 2023 19:16:03 -0800 Subject: [PATCH 264/453] update README.org with status of actions from mtg, fix typo org-bracket-link-regexp is obsolete, not the other one. --- README.org | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/README.org b/README.org index b6339d5..4c3464b 100644 --- a/README.org +++ b/README.org @@ -114,7 +114,7 @@ - Dmitry [f3f5a05] + Peter [6488cc6] #+end_src -*** [PM:DONE, DM:TBD] *-get-buffer-file-name-* +*** DONE *-get-buffer-file-name-* #+begin_src diff -(defun org-noter-get-buffer-file-name-* (&optional major-mode) +(defun org-noter-get-buffer-file-name-* (mode) @@ -133,7 +133,7 @@ 1. major-mode -> mode in module files 2. use &optional when the argument is not used in the function -*** [PM:DONE, DM:TBD] -get-buffer-file-name-hook +*** DONE -get-buffer-file-name-hook #+begin_src diff -(defcustom org-noter-get-buffer-file-name-hook '(org-noter-get-buffer-file-name-nov org-noter-get-buffer-file-name-pdf) +(defcustom org-noter-get-buffer-file-name-hook nil @@ -142,7 +142,7 @@ should be nil in =org-noter-core= and set in modules. ACTION: already converged -*** [PM:DONE, DM:TBD] *-get-precise-info-* +*** DONE *-get-precise-info-* #+begin_src diff -(defun org-noter-*--get-precise-info (major-mode) +(defun org-noter-*--get-precise-info (major-mode window) @@ -166,7 +166,7 @@ proposal: change =major-mode= to =mode=, pass in =window= ACTION: (done) Dmitry took mine -*** [PM:DONE, DM:TBD] *-goto-location +*** DONE *-goto-location #+begin_src diff -(defun org-noter-pdf-goto-location (mode location) +(defun org-noter-pdf-goto-location (mode location window) @@ -184,7 +184,7 @@ ACTION: pass in window, use &optional as appropriate. -*** [PM:DONE, DM:DONE] *-check-location-property +*** DONE *-check-location-property #+begin_src diff (defun org-noter-pdf-check-location-property (&optional property) "Check if PROPERTY is a valid location property" @@ -202,7 +202,7 @@ ACTION: done, gone on Dmitry's side. P: check diff, remove if it's still there. -*** [PM:DONE, DM:TBD] -doc--get-precise-info +*** DONE -doc--get-precise-info #+begin_src diff +(defun org-noter-doc--get-precise-info (major-mode window) + (when (eq major-mode 'doc-view-mode) @@ -219,7 +219,7 @@ Dmitry removed this function at [9d437bf] ACTION: Dmitry revive on his side. -*** [PM:DONE, DM:TBD] --doc-approx-location-hook +*** DONE --doc-approx-location-hook #+begin_src diff (defcustom org-noter--doc-approx-location-hook nil - "This returns an approximate location if no precise info is passed: (PAGE 0) @@ -233,7 +233,7 @@ ACTION: Dmitry revive on his side. docstring needs to be updated. ACTION: Dmitry reverted -*** [PM:DONE, DM:TBD] --note-search-no-recurse :11fc0a8:9dfac53: +*** DONE --note-search-no-recurse :11fc0a8:9dfac53: #+begin_src diff +(defconst org-noter--note-search-no-recurse (delete 'headline (append org-element-all-elements nil)) + "List of elements that shouldn't be recursed into when searching for notes.") @@ -254,7 +254,7 @@ ACTION: Dmitry revive on his side. ACTION: safe for Dmitry to cherry-pick these commits, but =with-current-buffer= call gets removed. This is the one change I took from ~cbpnk~ -*** [PM:DONE, DM:TBD] org-noter--create-session :9dfac53: +*** DONE org-noter--create-session :9dfac53: #+begin_src diff (defun org-noter--create-session (ast document-property-value notes-file-path) (let* ((raw-value-not-empty (> (length (org-element-property :raw-value ast)) 0)) @@ -262,10 +262,10 @@ ACTION: Dmitry revive on his side. + (link-p (or (string-match-p org-link-bracket-re document-property-value) (string-match-p org-noter--url-regexp document-property-value))) #+end_src - =org-link-bracket-re= is obsolete. keep mine. + =org-bracket-link-regexp= is obsolete. keep mine. ACTION: safe for Dmitry to cherry-pick -*** [PM:DONE, DM:TBD] org-noter--narrow-to-root (ast) :dfe7df2: +*** DONE org-noter--narrow-to-root (ast) :dfe7df2: #+begin_src diff - (when ast + (when (and ast (not (org-noter--no-heading-p))) @@ -281,7 +281,7 @@ ACTION: Dmitry revive on his side. file." ACTION: safe for Dmitry to cherry-pick -*** [PM:DONE, DM:TBD] org-noter--get-location-page (location) :DM:629fbb6: +*** DONE org-noter--get-location-page (location) :DM:629fbb6: #+begin_src diff "Get the page number given a LOCATION of form (page top . left) or (page . top)." - (message "===> %s" location) @@ -304,7 +304,7 @@ ACTION: Dmitry revive on his side. function name - 629fbb6 introduced by DM -*** [PM:DONE, DM:TBD] org-noter-kill-session :9dfac53: +*** DONE org-noter-kill-session :9dfac53: #+begin_src diff (with-current-buffer notes-buffer (remove-hook 'kill-buffer-hook 'org-noter--handle-kill-buffer t) @@ -317,7 +317,7 @@ ACTION: Dmitry revive on his side. is used ACTION: safe for Dmitry to cherry-pick -*** [DM:TBD] use cl-lib or native elisp hash tables rather than the =ht= package. +*** TODO use cl-lib or native elisp hash tables rather than the =ht= package. ** precise note fix *** *pdf--get-precise-info-* keep mine. From 55ae36c4f2b3ac93c3c91e2d11747fa596195fcc Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Fri, 13 Jan 2023 19:36:38 -0800 Subject: [PATCH 265/453] Refactor highlighting functionality. 1. Use cl-defstruct instead of ht for storing highlights. 2. Introduce another hook (`org-noter--pretty-print-highlight-location-hook`) that is used to serialize highlights to org. There are currently 3 hooks: - org-noter--get-highlight-location-hook - get the location of a highlight - org-noter--highlight-precise-note-note-hook - highlight based on org-noter--get-highlight-location-hook - org-noter--pretty-print-highlight-location-hook - get a string representation of the highlight location to store it in org. --- modules/org-noter-pdf.el | 23 +++++++++++++-------- org-noter-core.el | 38 ++++++++++++++++++++++++++--------- tests/org-noter-core-tests.el | 6 +----- tests/org-noter-pdf-tests.el | 12 +++++------ 4 files changed, 49 insertions(+), 30 deletions(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 0aa75e5..b6d46fe 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -24,22 +24,29 @@ ;;; Code: (require 'org-noter) -(require 'ht) -(defun org-noter-pdf-get-highlight-location () - "If there's an active pdf selection, returns a ht that contains all -the relevant info (page, coordinates, highlight type). + +(cl-defstruct pdf-highlight page coords) + + +(defun org-noter-pdf--get-highlight () + "If there's an active pdf selection, returns a that contains all +the relevant info (page, coordinates) Otherwise returns nil" (-if-let* ((_ (pdf-view-active-region-p)) (page (image-mode-window-get 'page)) (coords (pdf-view-active-region))) - (ht->plist (ht ('PAGE page) - ('TYPE 'PDF-HIGHLIGHT) - ('COORDS coords))) + (make-pdf-highlight :page page :coords coords) nil)) -(add-to-list 'org-noter--get-highlight-location-hook #'org-noter-pdf-get-highlight-location) + + +(defun org-noter-pdf--pretty-print-highlight (highlight-info) + (format "%s" highlight-info)) + +(add-to-list 'org-noter--pretty-print-highlight-location-hook #'org-noter-pdf--pretty-print-highlight) +(add-to-list 'org-noter--get-highlight-location-hook 'org-noter-pdf--get-highlight) (defun org-noter-pdf-approx-location-cons (major-mode &optional precise-info _force-new-ref) (when (memq major-mode '(doc-view-mode pdf-view-mode)) diff --git a/org-noter-core.el b/org-noter-core.el index ecc8c8e..43fdb7f 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -39,6 +39,7 @@ (require 'org-element) (require 'cl-lib) (require 'pdf-tools) +(require 'dash) (declare-function doc-view-goto-page "doc-view") (declare-function image-display-size "image-mode") @@ -245,11 +246,22 @@ The title used will be the default one." :group 'org-noter :type 'hook) +(defcustom org-noter--pretty-print-highlight-location-hook nil + "Hook that runs to serialize a highlight location so that it can be stored in org." + :group 'org-noter + :type 'hook) + (defcustom org-noter--get-highlight-location-hook nil "Hook that runs to get the location of a highlight" :group 'org-noter :type 'hook) +(defcustom org-noter-highlight-precise-note-hook nil + "When a precise note is created this will be called with the `MAJOR-MODE' and `PRECISE-INFO'. +This can be used in pdf-mode for example to add a permanent highlight to the document." + :group 'org-noter + :type 'hook) + (defcustom org-noter-find-additional-notes-functions nil "Functions that when given a document file path as argument, give out an org note file path. @@ -412,12 +424,6 @@ Used by `org-noter--create-session' when creating a new session." :group 'org-noter :type 'hook) -(defcustom org-noter-highlight-precise-note-hook nil - "When a precise note is created this will be called with the `MAJOR-MODE' and `PRECISE-INFO'. -This can be used in pdf-mode for example to add a permanent highlight to the document." - :group 'org-noter - :type 'hook) - ;; -------------------------------------------------------------------------------- @@ -2024,8 +2030,10 @@ defines if the text should be inserted inside the note." ;; NOTE(nox): This is needed to insert in the right place (unless (org-noter--no-heading-p) (outline-show-entry)) (org-noter--insert-heading level title empty-lines-number location) - (when highlight-location - (org-entry-put nil "HIGHLIGHT" (format "%s" highlight-location))) + ;; store the highlight in org IF we have a highlight AND can serialize it. + (when-let ((highlight-location) + (serialized-highlight (org-noter--get-serialized-highlight highlight-location))) + (org-entry-put nil "HIGHLIGHT" serialized-highlight)) (when quote-p (save-excursion (insert "#+BEGIN_QUOTE\n" selected-text "\n#+END_QUOTE"))) @@ -2066,13 +2074,23 @@ See `org-noter-insert-note' docstring for more." (select-frame-set-input-focus (org-noter--session-frame session)) (select-window (get-buffer-window (org-noter--session-doc-buffer session))) - ;; TODO precise info is wrong here. should be removed - (run-hook-with-args-until-success 'org-noter-highlight-precise-note-hook major-mode precise-info)))) + ;; this adds the highlight to the document + (run-hook-with-args-until-success 'org-noter-highlight-precise-note-hook major-mode highlight-location) + ))) (defun org-noter--get-highlight-location () + "Returns a highlight location. This is mode specific. +In PDF it's a the page nubmer and 4 coordinates for the highglight. This is delegated to each document mode." (with-selected-window (org-noter--get-doc-window) (run-hook-with-args-until-success 'org-noter--get-highlight-location-hook))) +(defun org-noter--get-serialized-highlight (highlight-location) +"Returns a string representation of the HIGHLIGHT-LOCATION. This is delegated to each document mode (eg pdf)" + (run-hook-with-args-until-success 'org-noter--pretty-print-highlight-location-hook highlight-location)) + + + + (defun org-noter-insert-note-toggle-no-questions () "Insert note associated with the current location. This is like `org-noter-insert-note', except it will toggle `org-noter-insert-note-no-questions'" diff --git a/tests/org-noter-core-tests.el b/tests/org-noter-core-tests.el index 2d81a04..990a9ef 100644 --- a/tests/org-noter-core-tests.el +++ b/tests/org-noter-core-tests.el @@ -60,11 +60,7 @@ (org-noter-core-test-create-session) (with-simulated-input "precise SPC note RET" (org-noter-insert-precise-note)) - (expect 'org-noter-core-test-highlight-location :to-have-been-called) - (expect (spy-calls-all-args 'org-noter-core-test-highlight-location) - :to-equal - '((org-mode - (1 2 3 4))))))) + (expect 'org-noter-core-test-highlight-location :to-have-been-called)))) ;; hit C-g when entering a note; expect no highlight (it "precise note DOES NOT call the highlight hook when the note is aborted" diff --git a/tests/org-noter-pdf-tests.el b/tests/org-noter-pdf-tests.el index e39b876..fe03ecd 100644 --- a/tests/org-noter-pdf-tests.el +++ b/tests/org-noter-pdf-tests.el @@ -3,9 +3,7 @@ (require 'org-noter-test-utils) -(defvar expected-highlight-info (ht ('PAGE 747) - ('TYPE 'PDF-HIGHLIGHT) - ('COORDS '(0.1 0.2 0.3 0.4)))) +(defvar expected-highlight-info (make-pdf-highlight :page 747 :coords '(0.1 0.2 0.3 0.4))) (describe "org-noter-pdf-functionality" ;; todo refactor 👇 @@ -21,16 +19,16 @@ ) (it "can get coordinates from pdf-view" - (let ((highlight-info (org-noter-pdf-get-highlight-location))) + (let ((highlight-info (org-noter-pdf--get-highlight))) (expect 'pdf-view-active-region-p :to-have-been-called) - (expect highlight-info :to-equal (ht->plist expected-highlight-info)))) + (expect highlight-info :to-equal expected-highlight-info))) (describe "highlight persistence" (before-each (create-org-noter-test-session) ;; (create-org-noter-test-session) sets up a highlight hook, so we have to reset it back. ;; this might be ok for now? maybe filter out all "-core-test-" hooks instead? - (setq org-noter--get-highlight-location-hook '(org-noter-pdf-get-highlight-location)) + (setq org-noter--get-highlight-location-hook '(org-noter-pdf--get-highlight)) ) (it "can take a precise note WITH a highlight appearing" (with-mock-contents @@ -41,7 +39,7 @@ (org-noter-insert-precise-note)) (ont--log-debug "%s" (buffer-string)) (expect (string-match "\\:HIGHLIGHT\\:" (buffer-string)) :not :to-be nil) - (expect (string-match (format "%s" (ht->plist expected-highlight-info)) (buffer-string)) :not :to-be nil) + (expect (string-match (format "%s" expected-highlight-info) (buffer-string)) :not :to-be nil) ) )) From 7a7ac6f792000e6b398515b22dbbe341613b0536 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sat, 14 Jan 2023 13:29:52 -0800 Subject: [PATCH 266/453] update README.org - navigation bug --- README.org | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.org b/README.org index 4c3464b..4588798 100644 --- a/README.org +++ b/README.org @@ -34,6 +34,8 @@ ** Bugs to fix 1. Sometimes (when?) M-p doesn't pick up the containing note-at-point right away (or at all), requiring user to manually type in the (existing) title + 2. Navigating up from a nested precise note lands in the prior note at the + next level up (eg level 3 -> level 2). page notes behave properly. ** Trying out the refactor of org-noter-insert-note 1. load `org-noter-dev.el' @@ -96,6 +98,8 @@ May require altering the org-noter-X--pretty-print-location-hook(s). 2. Profile org-noter -- runs slow on large books (500+ pages) + org-element-map is taking 85% of the CPU. Org 9.6 should have a faster + way that Charlie Gordon explored. 3. Bind M- to precise-note, no-questions. From aaaa34e54d12bbbd14c1fb976e3f039f97a8e9ac Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sat, 14 Jan 2023 15:05:20 -0800 Subject: [PATCH 267/453] notes on debugging navigation problem --- README.org | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.org b/README.org index 4588798..a916ab4 100644 --- a/README.org +++ b/README.org @@ -37,6 +37,18 @@ 2. Navigating up from a nested precise note lands in the prior note at the next level up (eg level 3 -> level 2). page notes behave properly. + [file:org-noter-core.el:2179] + =(org-element-property :begin (org-noter--get-containing-element))= returns + the begin of the element one level up when the current note location is of + the form ( . ). It works properly for locations of + the form ( . ). + + It will be one of these two: + - =org-noter--get-containing-heading= + - =org-noter--check-location-property= + found bug: [[file:org-noter-core.el:1023]] change test from integerp to numberp + - =org-noter--get-containing-property-drawer= + ** Trying out the refactor of org-noter-insert-note 1. load `org-noter-dev.el' - defines org-noter-max-short-note From b8364ee40262d66b2316cb220792f92547bc463c Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sat, 14 Jan 2023 15:05:53 -0800 Subject: [PATCH 268/453] bugfix: check-location-property was failing on 2D-precise-locations --- org-noter-core.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org-noter-core.el b/org-noter-core.el index 365c240..c5a1a6c 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1020,7 +1020,7 @@ properties, by a margin of NEWLINES-NUMBER." (or (run-hook-with-args-until-success 'org-noter--check-location-property-hook property) (let ((value (car (read-from-string property)))) (or (and (consp value) (integerp (car value)) (numberp (cdr value))) - (and (consp value) (integerp (car value)) (integerp (cadr value)) (integerp (cddr value))) + (and (consp value) (integerp (car value)) (numberp (cadr value)) (numberp (cddr value))) (integerp value))))))) (defun org-noter--parse-location-property (arg) From 484e08b9e32b666ffdfaf5ecf170783e7d34427f Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sat, 14 Jan 2023 23:09:47 -0800 Subject: [PATCH 269/453] update bug notes in README, + notes on multicolumn precise --- README.org | 46 +++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/README.org b/README.org index a916ab4..2980fc7 100644 --- a/README.org +++ b/README.org @@ -31,23 +31,25 @@ 4. Add property and/or configuration to make the arrow left or right pointing. -** Bugs to fix - 1. Sometimes (when?) M-p doesn't pick up the containing note-at-point right - away (or at all), requiring user to manually type in the (existing) title - 2. Navigating up from a nested precise note lands in the prior note at the - next level up (eg level 3 -> level 2). page notes behave properly. - - [file:org-noter-core.el:2179] - =(org-element-property :begin (org-noter--get-containing-element))= returns - the begin of the element one level up when the current note location is of - the form ( . ). It works properly for locations of - the form ( . ). - - It will be one of these two: - - =org-noter--get-containing-heading= - - =org-noter--check-location-property= - found bug: [[file:org-noter-core.el:1023]] change test from integerp to numberp - - =org-noter--get-containing-property-drawer= +** Bugs +*** to fix + 1. Sometimes (when?) M-p doesn't pick up the containing note-at-point right + away (or at all), requiring user to manually type in the (existing) title +*** fixed + 1. Navigating up from a nested precise note lands in the prior note at the + next level up (eg level 3 -> level 2). page notes behave properly. + + [file:org-noter-core.el:2179] + =(org-element-property :begin (org-noter--get-containing-element))= returns + the begin of the element one level up when the current note location is of + the form ( . ). It works properly for locations of + the form ( . ). + + It will be one of these two: + - =org-noter--get-containing-heading= + - =org-noter--check-location-property= + found bug: [[file:org-noter-core.el:1023]] change test from integerp to numberp + - =org-noter--get-containing-property-drawer= ** Trying out the refactor of org-noter-insert-note 1. load `org-noter-dev.el' @@ -123,6 +125,16 @@ make page a real number with #.0 for the left column and #.5 for the right column? + need to write a function in modules/...pdf + =org-noter--convert-to-location-cons-hook= + #+begin_src elisp + (defun org-noter-pdf-convert-to-location-cons (location) + "returns location in a new format for multicolumn documents" + ;; location = (page v-pos h-pos) + + ;; return a scalar page position (+ page (/ v-pos ncol) (/ (floor (* h-pos ncol)) ncol)) + + #+end_src 6. Dedicated insert-selected-text-into-page-note ** Deltas w/ dmitrym In the diffs below the color coding is From 4bc485fa92d3bf056d0397519bc7f59aab327432 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sat, 14 Jan 2023 23:10:53 -0800 Subject: [PATCH 270/453] update keybindings in org-noter-dev.el Precise notes : i Page notes : TAB No Q's : M-i I find this arragement more to my liking. "i" and "TAB" are easy to type. Give the more commonly used one to "i" and the second choice to "TAB". No Q's requires little other input, so the "M-i" is more tolerable there. --- org-noter-dev.el | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/org-noter-dev.el b/org-noter-dev.el index 3fd2052..9e69318 100644 --- a/org-noter-dev.el +++ b/org-noter-dev.el @@ -178,7 +178,6 @@ This is like `org-noter-insert-note', except it will toggle `org-noter-insert-no (let ((org-noter-insert-note-no-questions (not org-noter-insert-note-no-questions))) (phm/org-noter-insert-note)))) -(define-key org-noter-doc-mode-map (kbd "i") 'phm/org-noter-insert-note) -(define-key org-noter-doc-mode-map (kbd "M-i") 'phm/org-noter-insert-precise-note) -(define-key org-noter-doc-mode-map (kbd "C-i") 'phm/org-noter-insert-note-toggle-no-questions) -(define-key org-noter-doc-mode-map (kbd "e") 'phm/org-noter-test-mouse-event) +(define-key org-noter-doc-mode-map (kbd "C-i") 'phm/org-noter-insert-note) +(define-key org-noter-doc-mode-map (kbd "i") 'phm/org-noter-insert-precise-note) +(define-key org-noter-doc-mode-map (kbd "M-i") 'phm/org-noter-insert-note-toggle-no-questions) From 1a705a08c73f072674db4a42cd88a31a3c7c311b Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sat, 14 Jan 2023 23:14:34 -0800 Subject: [PATCH 271/453] +doc for org-noter-pdf-approx-location-cons, -merge chaff Two copies of org-noter-get-buffer-file-name-pdf snuck into the merge. Removed one. --- modules/org-noter-pdf.el | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 8687f64..807359d 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -42,17 +42,14 @@ Otherwise returns nil" (add-to-list 'org-noter--get-highlight-location-hook #'org-noter-pdf-get-highlight-location) (defun org-noter-pdf-approx-location-cons (mode &optional precise-info _force-new-ref) + "Returns (page . 0) except when creating a precise-note, +where (page v-pos . h-pos) is returned" (when (memq mode '(doc-view-mode pdf-view-mode)) (cons (image-mode-window-get 'page) (if (and (consp precise-info) (numberp (car precise-info)) (numberp (cdr precise-info))) precise-info 0)))) -(defun org-noter-get-buffer-file-name-pdf (&optional major-mode) - "Return the file naming backing the document buffer" - (bound-and-true-p pdf-file-name)) - - (add-to-list 'org-noter--doc-approx-location-hook #'org-noter-pdf-approx-location-cons) (defun org-noter-get-buffer-file-name-pdf (&optional mode) From 2747649b009546590114d2c475d18e52629467d5 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sat, 14 Jan 2023 23:16:21 -0800 Subject: [PATCH 272/453] WIP multicolumn precise notes Doesn't work yet because the focus going into this function needs to be on the NOTES buffer, but somehow it is on the DOC buffer. Mathematically, this works if `ncol' is hard-coded (eg, try 2). --- modules/org-noter-pdf.el | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 807359d..5625db8 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -336,5 +336,21 @@ where (page v-pos . h-pos) is returned" (pdf-view-active-region-p)) (pdf-annot-add-highlight-markup-annotation (pdf-view-active-region)))) +(defun org-noter-pdf-convert-to-location-cons (location) + "converts (page v . h) precise locations so that v represents the +fractional distance through the page along column. Output is nil +for standard notes and (page v') for precise notes" + (if-let* ((_ (and (consp location) (consp (cdr location)))) + (bb (current-buffer)) ; debugging code - we are in the doc window, + ; but need to be in the notes window for next + ; line to work + (ncol (max 1 (string-to-number (or (org-entry-get nil "NUM_COLUMNS" t) "1")))) + (page (car location)) + (v-pos (cadr location)) + (h-pos (cddr location))) + (cons page (+ (/ v-pos ncol) (/ (float (floor (* h-pos ncol))) ncol))))) + +(add-to-list 'org-noter--convert-to-location-cons-hook #'org-noter-pdf-convert-to-location-cons) + (provide 'org-noter-pdf) ;;; org-noter-pdf.el ends here From c9fb9e0f84d4901046a4e8fc369278dd2b04de10 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 15 Jan 2023 09:59:28 -0800 Subject: [PATCH 273/453] multi-column precise notes implemented Move call to org-noter--get-view-info later (in org-noter-insert-note) so that it is called with the NOTES buffer in focus. New variable (current-view (org-noter--get-current-view)) sits where view-info used to be because it needs to be called with the DOC buffer in focus. --- org-noter-dev.el | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/org-noter-dev.el b/org-noter-dev.el index 9e69318..fdfc804 100644 --- a/org-noter-dev.el +++ b/org-noter-dev.el @@ -39,7 +39,7 @@ Guiding principles for this (phm/) refactor force-new (location (org-noter--doc-approx-location (or precise-info 'interactive) (gv-ref force-new))) - (view-info (org-noter--get-view-info (org-noter--get-current-view) location))) + (current-view (org-noter--get-current-view))) (let ((inhibit-quit t) (short-selected-text (if (and (> (length selected-text) 0) @@ -52,7 +52,8 @@ Guiding principles for this (phm/) refactor ;; IMPORTANT(nox): Need to be careful changing the next part, it is a bit ;; complicated to get it right... - (let ((point (point)) + (let ((view-info (org-noter--get-view-info current-view location)) + (point (point)) (minibuffer-local-completion-map org-noter--completing-read-keymap) collection title note-body existing-note (default-title (or short-selected-text From 0fd569b97890922d94afd7a24b960920624781f0 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 15 Jan 2023 10:38:30 -0800 Subject: [PATCH 274/453] update README.org with notes on multi-column precise-notes --- README.org | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/README.org b/README.org index 2980fc7..f24de94 100644 --- a/README.org +++ b/README.org @@ -84,6 +84,15 @@ - short/long are differentiated by customizable variable (default 80 char) In Stock org-noter, long selections have more than 3 lines. + - NEW: multicolumn precise-notes defined by property NUM_COLUMNS + + Implements issue #153 in weirdNox/org-noter by adding + =org-noter-pdf-convert-to-location-cons= to + =org-noter--convert-to-location-cons-hook=, where a "virtual" vertical + location is calculated by dividing the page into equal width columns. This + is not a perfect solution, and is probably best for low-integer numbers of + columns, since the page is just divided evenly into NUM_COLUMNS vertical + strips. *** Stock | | insert-note =i= | precise note =M-i= | no-questions =C-i, TAB= | @@ -119,23 +128,7 @@ 4. Make background of arrow transparent (see org-noter--show-arrow) - 5. Handle 2-column pdfs with notes in read-order. - - Need to define a document property TWO-COLUMN and reference it. Maybe - make page a real number with #.0 for the left column and #.5 for the right - column? - - need to write a function in modules/...pdf - =org-noter--convert-to-location-cons-hook= - #+begin_src elisp - (defun org-noter-pdf-convert-to-location-cons (location) - "returns location in a new format for multicolumn documents" - ;; location = (page v-pos h-pos) - - ;; return a scalar page position (+ page (/ v-pos ncol) (/ (floor (* h-pos ncol)) ncol)) - - #+end_src - 6. Dedicated insert-selected-text-into-page-note + 5. Dedicated insert-selected-text-into-page-note ** Deltas w/ dmitrym In the diffs below the color coding is #+begin_src diff From 395c9fa823f6acf580139dc8d4c6889957ff185e Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 15 Jan 2023 15:08:58 -0800 Subject: [PATCH 275/453] bugfix: -insert-note, suppress QUOTE block with empty text selection formerly, a #+(BEGIN|END)_QUOTE block would show up with 0-length selected-text. --- README.org | 2 +- org-noter-dev.el | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.org b/README.org index f24de94..b40b2e2 100644 --- a/README.org +++ b/README.org @@ -105,7 +105,7 @@ |-----------------+--------------------------------+---------------------------+-------------------------| *** Refactored - | | insert-note =i= | precise note =M-i= | no-questions =C-i, TAB= | + | | insert-note | precise note | no-questions | |----------------------+---------------------------+---------------------------+---------------------------| | title prompt? | Y | Y | N | | default title 1 | short-selected-text | short-selected-text | short-selected-text | diff --git a/org-noter-dev.el b/org-noter-dev.el index fdfc804..7aa1ddd 100644 --- a/org-noter-dev.el +++ b/org-noter-dev.el @@ -36,13 +36,13 @@ Guiding principles for this (phm/) refactor (selected-text (run-hook-with-args-until-success 'org-noter-get-selected-text-hook (org-noter--session-doc-mode session))) - + (selected-text-p (> (length selected-text) 0)) force-new (location (org-noter--doc-approx-location (or precise-info 'interactive) (gv-ref force-new))) (current-view (org-noter--get-current-view))) (let ((inhibit-quit t) - (short-selected-text (if (and (> (length selected-text) 0) + (short-selected-text (if (and selected-text-p (<= (length selected-text) org-noter-max-short-length)) selected-text))) (with-local-quit @@ -73,7 +73,9 @@ Guiding principles for this (phm/) refactor ;; prompt for title (unless no-Q's) title (if org-noter-insert-note-no-questions default-title (completing-read "Note: " collection nil nil nil nil default-title)) - note-body (unless (equal title short-selected-text) selected-text) + note-body (if (and selected-text-p + (not (equal title short-selected-text))) + selected-text) ;; is this an existing note? skip for precise notes existing-note (unless precise-info (cdr (assoc title collection)))) From 49c54a47eb341264cb1fa0b13ef6ea33d21a097e Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 15 Jan 2023 15:33:05 -0800 Subject: [PATCH 276/453] Refactor org-noter-insert-note Also add defcustom `org-noter-highlight-selected-text' to allow opt-in to Dmitry's highlighting code for pdfs --- org-noter-core.el | 122 ++++++++++++++++++++++++++-------------------- 1 file changed, 70 insertions(+), 52 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index c5a1a6c..ace1252 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -421,13 +421,24 @@ Used by `org-noter--create-session' when creating a new session." :group 'org-noter :type 'hook) +(defcustom org-noter-highlight-selected-text nil + "Highlight selected-text when creating precise notes" + :group 'org-noter + :type 'boolean) + (defcustom org-noter-highlight-precise-note-hook nil "When a precise note is created this will be called with the `MAJOR-MODE' and `PRECISE-INFO'. This can be used in pdf-mode for example to add a permanent highlight to the document." :group 'org-noter :type 'hook) - +(defcustom org-noter-max-short-length 80 + "Maximum length of a short text selection. Short text selections +may be used as note title. When they are quoted in the note, +they are quoted as ``short-selected-text'' rather than inside a +QUOTE-block." + :group 'org-noter + :type 'integer) ;; -------------------------------------------------------------------------------- ;;; Private variables or constants @@ -1920,7 +1931,8 @@ want to kill." This command will prompt for a title of the note and then insert it in the notes buffer. When the input is empty, a title based on -`org-noter-default-heading-title' will be generated. +either the selected text (if it is <= `org-noter-max-short-length') +or `org-noter-default-heading-title' will be generated. If there are other notes related to the current location, the prompt will also suggest them. Depending on the value of the @@ -1933,21 +1945,30 @@ info). When you insert into an existing note and have text selected on the document buffer, the variable `org-noter-insert-selected-text-inside-note' -defines if the text should be inserted inside the note." +defines if the text should be inserted inside the note. + +Guiding principles for note generation + 1. The preferred title is the one the user enters in the minibuffer. + 2. Selected text should be used in the note, either as the title or in the body + 3. Refrain from making notes in the same location with the same title + 4. Precise notes generally have different locations, so always make new + precise notes" (interactive) (org-noter--with-valid-session (let* ((ast (org-noter--parse-root)) (contents (org-element-contents ast)) (window (org-noter--get-notes-window 'force)) - (selected-text - (run-hook-with-args-until-success - 'org-noter-get-selected-text-hook - (org-noter--session-doc-mode session))) - + (selected-text (run-hook-with-args-until-success + 'org-noter-get-selected-text-hook + (org-noter--session-doc-mode session))) + (selected-text-p (> (length selected-text) 0)) force-new (location (org-noter--doc-approx-location (or precise-info 'interactive) (gv-ref force-new))) - (view-info (org-noter--get-view-info (org-noter--get-current-view) location))) + (current-view (org-noter--get-current-view))) - (let ((inhibit-quit t)) + (let ((inhibit-quit t) + (short-selected-text (if (and selected-text-p + (<= (length selected-text) org-noter-max-short-length)) + selected-text))) (with-local-quit (select-frame-set-input-focus (window-frame window)) (select-window window) @@ -1955,39 +1976,37 @@ defines if the text should be inserted inside the note." ;; IMPORTANT(nox): Need to be careful changing the next part, it is a bit ;; complicated to get it right... - (let ((point (point)) + (let ((view-info (org-noter--get-view-info current-view location)) + (point (point)) (minibuffer-local-completion-map org-noter--completing-read-keymap) - collection default default-begin title selection quote-p + collection title note-body existing-note + (default-title (or short-selected-text + (replace-regexp-in-string (regexp-quote "$p$") + (org-noter--pretty-print-location location) + org-noter-default-heading-title))) (empty-lines-number (if org-noter-separate-notes-from-heading 2 1))) - (cond - ;; NOTE(nox): Both precise and without questions will create new notes - ((or precise-info force-new) - (setq quote-p (with-temp-buffer - (insert (or selected-text "")) - (> (how-many "\n" (point-min)) 2))) - (setq default (and selected-text - (replace-regexp-in-string "\n" " " selected-text)))) - (org-noter-insert-note-no-questions) - (t + ;; NOTE(phm): prompt for title unless this is a precise note + (unless precise-info + ;; construct collection for matching existing notes (dolist (note-cons (org-noter--view-info-notes view-info)) - (let ((display (org-element-property :raw-value (car note-cons))) - (begin (org-element-property :begin (car note-cons)))) - (push (cons display note-cons) collection) - (when (and (>= point begin) (> begin (or default-begin 0))) - (setq default display - default-begin begin)))))) + (let ((display (org-element-property :raw-value (car note-cons)))) + (push (cons display note-cons) collection)))) (setq collection (nreverse collection) - title (if (or org-noter-insert-note-no-questions note-title) - (or org-noter-default-heading-title note-title) - (completing-read "Note: " collection nil nil nil nil default)) - selection (unless org-noter-insert-note-no-questions (cdr (assoc title collection)))) - - (if selection + ;; prompt for title (unless no-Q's) + title (if org-noter-insert-note-no-questions default-title + (completing-read "Note: " collection nil nil nil nil default-title)) + note-body (if (and selected-text-p + (not (equal title short-selected-text))) + selected-text) + ;; is this an existing note? skip for precise notes + existing-note (unless precise-info (cdr (assoc title collection)))) + + (if existing-note ;; NOTE(nox): Inserting on an existing note - (let* ((note (car selection)) - (insert-before-element (cdr selection)) + (let* ((note (car existing-note)) + (insert-before-element (cdr existing-note)) (has-content (eq (org-element-map (org-element-contents note) org-element-all-elements (lambda (element) @@ -2001,7 +2020,6 @@ defines if the text should be inserted inside the note." (goto-char (org-element-property :begin insert-before-element)) (goto-char (org-element-property :end note))) - (if (org-at-heading-p) (progn (org-N-empty-lines-before-current empty-lines-number) @@ -2009,16 +2027,14 @@ defines if the text should be inserted inside the note." (unless (bolp) (insert "\n")) (org-N-empty-lines-before-current (1- empty-lines-number))) - (when (and org-noter-insert-selected-text-inside-note selected-text) (insert selected-text))) + (when (and org-noter-insert-selected-text-inside-note note-body) + (if short-selected-text + (insert "``" note-body "''") + (insert "#+BEGIN_QUOTE\n" note-body "\n#+END_QUOTE")))) ;; NOTE(nox): Inserting a new note (let ((reference-element-cons (org-noter--view-info-reference-for-insertion view-info)) level) - (when (or quote-p (zerop (length title))) - (setq title (replace-regexp-in-string (regexp-quote "$p$") - (org-noter--pretty-print-location location) - title))) - (if reference-element-cons (progn (cond @@ -2037,16 +2053,17 @@ defines if the text should be inserted inside the note." nil t org-element-all-elements) (point-max)))) - (setq level (1+ (or (org-element-property :level ast) 0))) + (setq level (or level + (1+ (or (org-element-property :level ast) 0)))) ;; NOTE(nox): This is needed to insert in the right place (unless (org-noter--no-heading-p) (outline-show-entry)) (org-noter--insert-heading level title empty-lines-number location) - (when highlight-location - (org-entry-put nil "HIGHLIGHT" (format "%s" highlight-location))) - (when quote-p + (when note-body (save-excursion - (insert "#+BEGIN_QUOTE\n" selected-text "\n#+END_QUOTE"))) + (if short-selected-text + (insert "``" note-body "''") + (insert "#+BEGIN_QUOTE\n" note-body "\n#+END_QUOTE")))) (when (org-noter--session-hide-other session) (org-overview)) (setf (org-noter--session-num-notes-in-view session) @@ -2081,11 +2098,12 @@ See `org-noter-insert-note' docstring for more." (highlight-location (org-noter--get-highlight-location))) (org-noter-insert-note precise-info nil highlight-location) - (select-frame-set-input-focus (org-noter--session-frame session)) - (select-window (get-buffer-window (org-noter--session-doc-buffer session))) + (when org-noter-highlight-selected-text + (select-frame-set-input-focus (org-noter--session-frame session)) + (select-window (get-buffer-window (org-noter--session-doc-buffer session))) - ;; TODO precise info is wrong here. should be removed - (run-hook-with-args-until-success 'org-noter-highlight-precise-note-hook major-mode precise-info)))) + ;; TODO precise info is wrong here. should be removed + (run-hook-with-args-until-success 'org-noter-highlight-precise-note-hook major-mode precise-info))))) (defun org-noter--get-highlight-location () (with-selected-window (org-noter--get-doc-window) From 2f2755c9565476d47b2b6581bfbbb5412f896628 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 15 Jan 2023 15:56:40 -0800 Subject: [PATCH 277/453] update documentation, remove org-noter-dev.el --- README.org | 286 +++------------------------------- docs/org_noter_tech_notes.org | 11 ++ docs/pre-merge_notes.org | 210 +++++++++++++++++++++++++ org-noter-dev.el | 186 ---------------------- 4 files changed, 243 insertions(+), 450 deletions(-) create mode 100644 docs/pre-merge_notes.org delete mode 100644 org-noter-dev.el diff --git a/README.org b/README.org index b40b2e2..9964ba5 100644 --- a/README.org +++ b/README.org @@ -6,8 +6,8 @@ - djvu and epub support have also been advanced by *c1-g*, but I have not tested any of that code. - - The main new feature for PDF users is 2-D precise notes, which can be - inserted in two ways: + - The main new feature for PDF users is 2-D precise notes (introduced by + *Ahmed Shariff*, which can be inserted in two ways: 1. "Select-precise": selecting text in the pdf, followed by =M-i=. *c1-g* changed the position format to use the entire list returned by =edges=. This breaks the other way of inserting precise notes. On *dmitrym*'s @@ -20,54 +20,6 @@ page number is inserted automatically over the octothorpes. 2. "i". The fundamental =org-noter-insert-note=. User types note title into the minibuffer. -** Note insertion desired features - 1. The position of the precise note should be at the click (with a - configurable horizontal offset) or at the upper-left of the selected text - (or configurable to one of the corners or ends of the selected area). - 2. Highlighting of selected text should be a user settable configuration option - 3. For any of the note insertion methods, if there is any selected text, then - it should be the default title. (or there should be a configurable - switch) - 4. Add property and/or configuration to make the arrow left or right - pointing. - -** Bugs -*** to fix - 1. Sometimes (when?) M-p doesn't pick up the containing note-at-point right - away (or at all), requiring user to manually type in the (existing) title -*** fixed - 1. Navigating up from a nested precise note lands in the prior note at the - next level up (eg level 3 -> level 2). page notes behave properly. - - [file:org-noter-core.el:2179] - =(org-element-property :begin (org-noter--get-containing-element))= returns - the begin of the element one level up when the current note location is of - the form ( . ). It works properly for locations of - the form ( . ). - - It will be one of these two: - - =org-noter--get-containing-heading= - - =org-noter--check-location-property= - found bug: [[file:org-noter-core.el:1023]] change test from integerp to numberp - - =org-noter--get-containing-property-drawer= - -** Trying out the refactor of org-noter-insert-note - 1. load `org-noter-dev.el' - - defines org-noter-max-short-note - - defines replacement for note insertion commands - - sets default note insertion keybindings to the new commands - 2. Guiding principles for this refactor - - The preferred title is the one the user enters in the minibuffer. - - Selected text should be used in the note, either as the title or in the body - - If there is no title input and no selected text, then use the default title - - Refrain from making notes in the same location with the same title - - Precise notes generally have different locations, so always make new - precise notes - 3. Caveats - - There may be cleaner ways to implement some of the logical tests, namely - empty string vs. nil situations. - - Not all combinations of text selection, entry command and note title - have been tested. ** Stock vs refactored note insertion - more consistent use of selected text in title or body @@ -129,222 +81,28 @@ 4. Make background of arrow transparent (see org-noter--show-arrow) 5. Dedicated insert-selected-text-into-page-note -** Deltas w/ dmitrym - In the diffs below the color coding is - #+begin_src diff -- Dmitry [f3f5a05] -+ Peter [6488cc6] - #+end_src -*** DONE *-get-buffer-file-name-* -#+begin_src diff --(defun org-noter-get-buffer-file-name-* (&optional major-mode) -+(defun org-noter-get-buffer-file-name-* (mode) - (bound-and-true-p *-file-name)) - -+(add-to-list 'org-noter-get-buffer-file-name-hook #'org-noter-get-buffer-file-name-*) -#+end_src - - - =major-mode= is a native elisp function, =mode= is a better name - - the arg is not used, so the =&optional= is appropriate - - for the =pdf= variant, we both use =(&optional major-mode)= - - proposal: =(&optional mode)= or remove the argument completely. - - ACTIONS: Do this in our own repos before merge - 1. major-mode -> mode in module files - 2. use &optional when the argument is not used in the function - -*** DONE -get-buffer-file-name-hook -#+begin_src diff --(defcustom org-noter-get-buffer-file-name-hook '(org-noter-get-buffer-file-name-nov org-noter-get-buffer-file-name-pdf) -+(defcustom org-noter-get-buffer-file-name-hook nil -#+end_src - - should be nil in =org-noter-core= and set in modules. - - ACTION: already converged -*** DONE *-get-precise-info-* -#+begin_src diff --(defun org-noter-*--get-precise-info (major-mode) -+(defun org-noter-*--get-precise-info (major-mode window) - (when (eq major-mode 'djvu-read-mode) - (if (region-active-p) - (cons (mark) (point)) -- (while (not (and (eq 'mouse-1 (car event)) -- (eq window (posn-window (event-start event))))) -- (setq event (read-event "Click where you want the start of the note to be!"))) -- (posn-point (event-start event))))) -+ (let ((event nil)) -+ (while (not (and (eq 'mouse-1 (car event)) -+ (eq window (posn-window (event-start event))))) -+ (setq event (read-event "Click where you want the start of the note to be!"))) -+ (posn-point (event-start event)))))) -#+end_src - - - calling function already calls =org-noter--get-doc-window= - - =window= is used in all document modes - - proposal: change =major-mode= to =mode=, pass in =window= - - ACTION: (done) Dmitry took mine -*** DONE *-goto-location -#+begin_src diff --(defun org-noter-pdf-goto-location (mode location) -+(defun org-noter-pdf-goto-location (mode location window) - (when (memq mode '(doc-view-mode pdf-view-mode)) - (let ((top (org-noter--get-location-top location)) -- (window (org-noter--get-doc-window)) - (left (org-noter--get-location-left location))) -#+end_src - - calling function already calls =org-noter--get-doc-window= - - nov and djvu don't need the =window= argument - - proposal: we discuss this one, but I think it's better to not call functions - unnecessarily - - ACTION: - pass in window, use &optional as appropriate. -*** DONE *-check-location-property - #+begin_src diff - (defun org-noter-pdf-check-location-property (&optional property) - "Check if PROPERTY is a valid location property" -- (equal 5 (length (read property)))) -+ t) - #+end_src - - location can be - 1. page - 2. page v-pos - 3. page v-pos . h-pos - - neither function works properly. need to read the calling function to - determine course of action.. - - ACTION: done, gone on Dmitry's side. - P: check diff, remove if it's still there. -*** DONE -doc--get-precise-info -#+begin_src diff -+(defun org-noter-doc--get-precise-info (major-mode window) -+ (when (eq major-mode 'doc-view-mode) - (let ((event nil)) - (while (not (and (eq 'mouse-1 (car event)) - (eq window (posn-window (event-start event))))) - (setq event (read-event "Click where you want the start of the note to be!"))) -- (let ((col-row (posn-col-row (event-start event)))) -- (org-noter--conv-page-scroll-percentage (+ (window-vscroll) (cdr col-row)) -- (+ (window-hscroll) (car col-row)))))))) -+ (org-noter--conv-page-scroll-percentage (+ (window-vscroll) -+ (cdr (posn-col-row (event-start event)))))))) -#+end_src -Dmitry removed this function at [9d437bf] - -ACTION: Dmitry revive on his side. -*** DONE --doc-approx-location-hook - #+begin_src diff - (defcustom org-noter--doc-approx-location-hook nil -- "This returns an approximate location if no precise info is passed: (PAGE 0) -- or if precise info is passed, it's (PAGE 0 0 0 0) where 0s are the precise coords) --" -+ "TODO" - :group 'org-noter - :type 'hook) - #+end_src - - docstring needs to be updated. - - ACTION: Dmitry reverted -*** DONE --note-search-no-recurse :11fc0a8:9dfac53: -#+begin_src diff -+(defconst org-noter--note-search-no-recurse (delete 'headline (append org-element-all-elements nil)) -+ "List of elements that shouldn't be recursed into when searching for notes.") -#+end_src - - called in =org-noter--get-view-info= by =org-element-map= -#+begin_src diff -- nil nil (delete 'headline (append org-element-all-elements nil)))) -+ nil nil org-noter--note-search-no-recurse) -#+end_src - - but this defconst is used by =org-noter--map-ignore-headings-with-doc-file=, which is - used by all of the sync functions - - probably should keep it, and since we keep it, use it in - =org-noter--get-view-info= - - ACTION: safe for Dmitry to cherry-pick these commits, but - =with-current-buffer= call gets removed. This is the one change I took from - ~cbpnk~ -*** DONE org-noter--create-session :9dfac53: - #+begin_src diff - (defun org-noter--create-session (ast document-property-value notes-file-path) - (let* ((raw-value-not-empty (> (length (org-element-property :raw-value ast)) 0)) -- (link-p (or (string-match-p org-bracket-link-regexp document-property-value) -+ (link-p (or (string-match-p org-link-bracket-re document-property-value) - (string-match-p org-noter--url-regexp document-property-value))) - #+end_src - =org-bracket-link-regexp= is obsolete. keep mine. - - ACTION: safe for Dmitry to cherry-pick -*** DONE org-noter--narrow-to-root (ast) :dfe7df2: -#+begin_src diff -- (when ast -+ (when (and ast (not (org-noter--no-heading-p))) - (save-excursion - (goto-char (org-element-property :contents-begin ast)) - (org-show-entry) -- (when (org-at-heading-p) (org-narrow-to-subtree)) -+ (org-narrow-to-subtree) - (org-cycle-hide-drawers 'all)))) -#+end_src - "I don't really understand this bit of code, especially what `ast' is, but - it breaks narrowing when multiple documents' notes are stored in a single - file." - - ACTION: safe for Dmitry to cherry-pick -*** DONE org-noter--get-location-page (location) :DM:629fbb6: - #+begin_src diff - "Get the page number given a LOCATION of form (page top . left) or (page . top)." -- (message "===> %s" location) -- (if (listp location) -- (car location) -- location)) -+ (car location)) - #+end_src - - ACTION: Peter -- what happens with a page note (no precise location)? does (car location) make an - error? - Answer: No, (car location) works fine because for a page note, location is a - cons cell, e.g. (19 . 0) by the time it reaches this function. - - @DM -- I think we should go back to the original (car location). - - HISTORY: - - 5bc5754 Ahmed Shariff original code - - c1ed245 c1g moved code from org-noter.el to org-noter-core.el, changing - function name - - 629fbb6 introduced by DM + 6. Internationalize precise notes to handle right-to-left languages. +** Bugs +*** to fix + 1. Sometimes (when?) M-p doesn't pick up the containing note-at-point right + away (or at all), requiring user to manually type in the (existing) title +*** fixed + 1. Navigating up from a nested precise note lands in the prior note at the + next level up (eg level 3 -> level 2). page notes behave properly. -*** DONE org-noter-kill-session :9dfac53: - #+begin_src diff - (with-current-buffer notes-buffer - (remove-hook 'kill-buffer-hook 'org-noter--handle-kill-buffer t) - (restore-buffer-modified-p nil)) -- (unless org-noter-use-indirect-buffer -+ (when org-noter-use-indirect-buffer - (kill-buffer notes-buffer)) - #+end_src - kill the notes buffer **when** an indirect buffer is used, not **unless** it - is used + [file:org-noter-core.el:2179] + =(org-element-property :begin (org-noter--get-containing-element))= returns + the begin of the element one level up when the current note location is of + the form ( . ). It works properly for locations of + the form ( . ). - ACTION: safe for Dmitry to cherry-pick -*** TODO use cl-lib or native elisp hash tables rather than the =ht= package. -** precise note fix -*** *pdf--get-precise-info-* - keep mine. -*** org-noter--conv-page-scroll-percentage - diffs are part of my precise-note fix. -** highlighting + It will be one of these two: + - =org-noter--get-containing-heading= + - =org-noter--check-location-property= + found bug: [[file:org-noter-core.el:1023]] change test from integerp to numberp + - =org-noter--get-containing-property-drawer= +** Highlighting my solution is compact, but too pdf-specific. need to sketch out Dmitry's solution - in precise-notes 1. hook to make the highlight in the document @@ -356,4 +114,4 @@ ACTION: Dmitry revive on his side. 1. HL Y/N 2. Quoting Y/N 3. No HL if Q - 4. No Q if NL + 4. No Q if HL diff --git a/docs/org_noter_tech_notes.org b/docs/org_noter_tech_notes.org index da8143c..3bd5aef 100644 --- a/docs/org_noter_tech_notes.org +++ b/docs/org_noter_tech_notes.org @@ -159,3 +159,14 @@ Omitting the header causes =org-noter--parse-root= to work incorrectly. cask # install dependencies cask exec buttercup -L . # exec unit tests, in the root of the project #+end_src + +*** GNU/Linux + +#+begin_src shell + git clone https://github.com/cask/cask.git + make -C cask install + + cd + cask # install dependencies (in root of project, 1 time) + cask exec buttercup -L . # exec unit tests, in the root of the project +#+end_src diff --git a/docs/pre-merge_notes.org b/docs/pre-merge_notes.org new file mode 100644 index 0000000..70a5711 --- /dev/null +++ b/docs/pre-merge_notes.org @@ -0,0 +1,210 @@ +* Pre-merge deltas w/ *dmitrym0* + In the diffs below the color coding is + #+begin_src diff +- Dmitry [f3f5a05] ++ Peter [6488cc6] + #+end_src +** DONE *-get-buffer-file-name-* +#+begin_src diff +-(defun org-noter-get-buffer-file-name-* (&optional major-mode) ++(defun org-noter-get-buffer-file-name-* (mode) + (bound-and-true-p *-file-name)) + ++(add-to-list 'org-noter-get-buffer-file-name-hook #'org-noter-get-buffer-file-name-*) +#+end_src + + - =major-mode= is a native elisp function, =mode= is a better name + - the arg is not used, so the =&optional= is appropriate + - for the =pdf= variant, we both use =(&optional major-mode)= + + proposal: =(&optional mode)= or remove the argument completely. + + ACTIONS: Do this in our own repos before merge + 1. major-mode -> mode in module files + 2. use &optional when the argument is not used in the function + +** DONE -get-buffer-file-name-hook +#+begin_src diff +-(defcustom org-noter-get-buffer-file-name-hook '(org-noter-get-buffer-file-name-nov org-noter-get-buffer-file-name-pdf) ++(defcustom org-noter-get-buffer-file-name-hook nil +#+end_src + + should be nil in =org-noter-core= and set in modules. + + ACTION: already converged +** DONE *-get-precise-info-* +#+begin_src diff +-(defun org-noter-*--get-precise-info (major-mode) ++(defun org-noter-*--get-precise-info (major-mode window) + (when (eq major-mode 'djvu-read-mode) + (if (region-active-p) + (cons (mark) (point)) +- (while (not (and (eq 'mouse-1 (car event)) +- (eq window (posn-window (event-start event))))) +- (setq event (read-event "Click where you want the start of the note to be!"))) +- (posn-point (event-start event))))) ++ (let ((event nil)) ++ (while (not (and (eq 'mouse-1 (car event)) ++ (eq window (posn-window (event-start event))))) ++ (setq event (read-event "Click where you want the start of the note to be!"))) ++ (posn-point (event-start event)))))) +#+end_src + + - calling function already calls =org-noter--get-doc-window= + - =window= is used in all document modes + + proposal: change =major-mode= to =mode=, pass in =window= + + ACTION: (done) Dmitry took mine +** DONE *-goto-location +#+begin_src diff +-(defun org-noter-pdf-goto-location (mode location) ++(defun org-noter-pdf-goto-location (mode location window) + (when (memq mode '(doc-view-mode pdf-view-mode)) + (let ((top (org-noter--get-location-top location)) +- (window (org-noter--get-doc-window)) + (left (org-noter--get-location-left location))) +#+end_src + - calling function already calls =org-noter--get-doc-window= + - nov and djvu don't need the =window= argument + + proposal: we discuss this one, but I think it's better to not call functions + unnecessarily + + ACTION: + pass in window, use &optional as appropriate. + +** DONE *-check-location-property + #+begin_src diff + (defun org-noter-pdf-check-location-property (&optional property) + "Check if PROPERTY is a valid location property" +- (equal 5 (length (read property)))) ++ t) + #+end_src + + location can be + 1. page + 2. page v-pos + 3. page v-pos . h-pos + + neither function works properly. need to read the calling function to + determine course of action.. + + ACTION: done, gone on Dmitry's side. + P: check diff, remove if it's still there. +** DONE -doc--get-precise-info +#+begin_src diff ++(defun org-noter-doc--get-precise-info (major-mode window) ++ (when (eq major-mode 'doc-view-mode) + (let ((event nil)) + (while (not (and (eq 'mouse-1 (car event)) + (eq window (posn-window (event-start event))))) + (setq event (read-event "Click where you want the start of the note to be!"))) +- (let ((col-row (posn-col-row (event-start event)))) +- (org-noter--conv-page-scroll-percentage (+ (window-vscroll) (cdr col-row)) +- (+ (window-hscroll) (car col-row)))))))) ++ (org-noter--conv-page-scroll-percentage (+ (window-vscroll) ++ (cdr (posn-col-row (event-start event)))))))) +#+end_src +Dmitry removed this function at [9d437bf] + +ACTION: Dmitry revive on his side. +** DONE --doc-approx-location-hook + #+begin_src diff + (defcustom org-noter--doc-approx-location-hook nil +- "This returns an approximate location if no precise info is passed: (PAGE 0) +- or if precise info is passed, it's (PAGE 0 0 0 0) where 0s are the precise coords) +-" ++ "TODO" + :group 'org-noter + :type 'hook) + #+end_src + + docstring needs to be updated. + + ACTION: Dmitry reverted +** DONE --note-search-no-recurse :11fc0a8:9dfac53: +#+begin_src diff ++(defconst org-noter--note-search-no-recurse (delete 'headline (append org-element-all-elements nil)) ++ "List of elements that shouldn't be recursed into when searching for notes.") +#+end_src + + called in =org-noter--get-view-info= by =org-element-map= +#+begin_src diff +- nil nil (delete 'headline (append org-element-all-elements nil)))) ++ nil nil org-noter--note-search-no-recurse) +#+end_src + + but this defconst is used by =org-noter--map-ignore-headings-with-doc-file=, which is + used by all of the sync functions + + probably should keep it, and since we keep it, use it in + =org-noter--get-view-info= + + ACTION: safe for Dmitry to cherry-pick these commits, but + =with-current-buffer= call gets removed. This is the one change I took from + ~cbpnk~ +** DONE org-noter--create-session :9dfac53: + #+begin_src diff + (defun org-noter--create-session (ast document-property-value notes-file-path) + (let* ((raw-value-not-empty (> (length (org-element-property :raw-value ast)) 0)) +- (link-p (or (string-match-p org-bracket-link-regexp document-property-value) ++ (link-p (or (string-match-p org-link-bracket-re document-property-value) + (string-match-p org-noter--url-regexp document-property-value))) + #+end_src + =org-bracket-link-regexp= is obsolete. keep mine. + + ACTION: safe for Dmitry to cherry-pick +** DONE org-noter--narrow-to-root (ast) :dfe7df2: +#+begin_src diff +- (when ast ++ (when (and ast (not (org-noter--no-heading-p))) + (save-excursion + (goto-char (org-element-property :contents-begin ast)) + (org-show-entry) +- (when (org-at-heading-p) (org-narrow-to-subtree)) ++ (org-narrow-to-subtree) + (org-cycle-hide-drawers 'all)))) +#+end_src + "I don't really understand this bit of code, especially what `ast' is, but + it breaks narrowing when multiple documents' notes are stored in a single + file." + + ACTION: safe for Dmitry to cherry-pick +** DONE org-noter--get-location-page (location) :DM:629fbb6: + #+begin_src diff + "Get the page number given a LOCATION of form (page top . left) or (page . top)." +- (message "===> %s" location) +- (if (listp location) +- (car location) +- location)) ++ (car location)) + #+end_src + + ACTION: Peter -- what happens with a page note (no precise location)? does (car location) make an + error? + Answer: No, (car location) works fine because for a page note, location is a + cons cell, e.g. (19 . 0) by the time it reaches this function. + + @DM -- I think we should go back to the original (car location). + + HISTORY: + - 5bc5754 Ahmed Shariff original code + - c1ed245 c1g moved code from org-noter.el to org-noter-core.el, changing + function name + - 629fbb6 introduced by DM + +** DONE org-noter-kill-session :9dfac53: + #+begin_src diff + (with-current-buffer notes-buffer + (remove-hook 'kill-buffer-hook 'org-noter--handle-kill-buffer t) + (restore-buffer-modified-p nil)) +- (unless org-noter-use-indirect-buffer ++ (when org-noter-use-indirect-buffer + (kill-buffer notes-buffer)) + #+end_src + kill the notes buffer **when** an indirect buffer is used, not **unless** it + is used + + ACTION: safe for Dmitry to cherry-pick +** TODO use cl-lib or native elisp hash tables rather than the =ht= package. diff --git a/org-noter-dev.el b/org-noter-dev.el deleted file mode 100644 index 7aa1ddd..0000000 --- a/org-noter-dev.el +++ /dev/null @@ -1,186 +0,0 @@ -(defcustom org-noter-max-short-length 80 - "Maximum length of a short text selection" - :group 'org-noter - :type 'integer) - -(defun phm/org-noter-insert-note (&optional precise-info) - "Insert note associated with the current location. - -This command will prompt for a title of the note and then insert -it in the notes buffer. When the input is empty, a title based on -`org-noter-default-heading-title' will be generated. - -If there are other notes related to the current location, the -prompt will also suggest them. Depending on the value of the -variable `org-noter-closest-tipping-point', it may also -suggest the closest previous note. - -PRECISE-INFO makes the new note associated with a more -specific location (see `org-noter-insert-precise-note' for more -info). - -When you insert into an existing note and have text selected on -the document buffer, the variable `org-noter-insert-selected-text-inside-note' -defines if the text should be inserted inside the note. - -Guiding principles for this (phm/) refactor - 1. The preferred title is the one the user enters in the minibuffer. - 2. Selected text should be used in the note, either as the title or in the body - 3. Refrain from making notes in the same location with the same title - 4. Precise notes generally have different locations, always make new - precise notes" - (interactive) - (org-noter--with-valid-session - (let* ((ast (org-noter--parse-root)) (contents (org-element-contents ast)) - (window (org-noter--get-notes-window 'force)) - (selected-text (run-hook-with-args-until-success - 'org-noter-get-selected-text-hook - (org-noter--session-doc-mode session))) - (selected-text-p (> (length selected-text) 0)) - force-new - (location (org-noter--doc-approx-location (or precise-info 'interactive) (gv-ref force-new))) - (current-view (org-noter--get-current-view))) - - (let ((inhibit-quit t) - (short-selected-text (if (and selected-text-p - (<= (length selected-text) org-noter-max-short-length)) - selected-text))) - (with-local-quit - (select-frame-set-input-focus (window-frame window)) - (select-window window) - - ;; IMPORTANT(nox): Need to be careful changing the next part, it is a bit - ;; complicated to get it right... - - (let ((view-info (org-noter--get-view-info current-view location)) - (point (point)) - (minibuffer-local-completion-map org-noter--completing-read-keymap) - collection title note-body existing-note - (default-title (or short-selected-text - (replace-regexp-in-string (regexp-quote "$p$") - (org-noter--pretty-print-location location) - org-noter-default-heading-title))) - (empty-lines-number (if org-noter-separate-notes-from-heading 2 1))) - - ;; NOTE(phm): prompt for title unless this is a precise note - (unless precise-info - ;; construct collection for matching existing notes - (dolist (note-cons (org-noter--view-info-notes view-info)) - (let ((display (org-element-property :raw-value (car note-cons)))) - (push (cons display note-cons) collection)))) - - (setq collection (nreverse collection) - ;; prompt for title (unless no-Q's) - title (if org-noter-insert-note-no-questions default-title - (completing-read "Note: " collection nil nil nil nil default-title)) - note-body (if (and selected-text-p - (not (equal title short-selected-text))) - selected-text) - ;; is this an existing note? skip for precise notes - existing-note (unless precise-info (cdr (assoc title collection)))) - - (if existing-note - ;; NOTE(nox): Inserting on an existing note - (let* ((note (car existing-note)) - (insert-before-element (cdr existing-note)) - (has-content - (eq (org-element-map (org-element-contents note) org-element-all-elements - (lambda (element) - (if (org-noter--check-location-property element) - 'stop - (not (memq (org-element-type element) '(section property-drawer))))) - nil t) - t))) - (when has-content (setq empty-lines-number 2)) - (if insert-before-element - (goto-char (org-element-property :begin insert-before-element)) - (goto-char (org-element-property :end note))) - - (if (org-at-heading-p) - (progn - (org-N-empty-lines-before-current empty-lines-number) - (forward-line -1)) - (unless (bolp) (insert "\n")) - (org-N-empty-lines-before-current (1- empty-lines-number))) - - (when (and org-noter-insert-selected-text-inside-note note-body) - (if short-selected-text - (insert "``" note-body "''") - (insert "#+BEGIN_QUOTE\n" note-body "\n#+END_QUOTE")))) - - ;; NOTE(nox): Inserting a new note - (let ((reference-element-cons (org-noter--view-info-reference-for-insertion view-info)) - level) - (if reference-element-cons - (progn - (cond - ((eq (car reference-element-cons) 'before) - (goto-char (org-element-property :begin (cdr reference-element-cons)))) - ((eq (car reference-element-cons) 'after) - (goto-char (org-element-property :end (cdr reference-element-cons))))) - - ;; NOTE(nox): This is here to make the automatic "should insert blank" work better. - (when (org-at-heading-p) (backward-char)) - - (setq level (org-element-property :level (cdr reference-element-cons)))) - - (goto-char (or (org-element-map contents 'section - (lambda (section) (org-element-property :end section)) - nil t org-element-all-elements) - (point-max)))) - - (setq level (or level - (1+ (or (org-element-property :level ast) 0)))) - - ;; NOTE(nox): This is needed to insert in the right place - (unless (org-noter--no-heading-p) (outline-show-entry)) - (org-noter--insert-heading level title empty-lines-number location) - (when note-body - (save-excursion - (if short-selected-text - (insert "``" note-body "''") - (insert "#+BEGIN_QUOTE\n" note-body "\n#+END_QUOTE")))) - (when (org-noter--session-hide-other session) (org-overview)) - - (setf (org-noter--session-num-notes-in-view session) - (1+ (org-noter--session-num-notes-in-view session))))) - - (org-show-set-visibility t) - (org-cycle-hide-drawers 'all) - (org-cycle-show-empty-lines t))) - (when quit-flag - ;; NOTE(nox): If this runs, it means the user quitted while creating a note, so - ;; revert to the previous window. - (select-frame-set-input-focus (org-noter--session-frame session)) - (select-window (get-buffer-window (org-noter--session-doc-buffer session)))))))) - -(defun phm/org-noter-insert-precise-note (&optional toggle-no-questions) - "Insert note associated with a specific location. -This will ask you to click where you want to scroll to when you -sync the document to this note. You should click on the top of -that part. Will always create a new note. - -When text is selected, it will automatically choose the top of -the selected text as the location and the text itself as the -title of the note (you may change it anyway!). - -See `org-noter-insert-note' docstring for more." - (interactive "P") - (org-noter--with-valid-session - (let ((org-noter-insert-note-no-questions (if toggle-no-questions - (not org-noter-insert-note-no-questions) - org-noter-insert-note-no-questions))) - (phm/org-noter-insert-note (org-noter--get-precise-info))))) - - -(defun phm/org-noter-insert-note-toggle-no-questions () - "Insert note associated with the current location. -This is like `org-noter-insert-note', except it will toggle `org-noter-insert-note-no-questions'" - (interactive) - (org-noter--with-valid-session - (let ((org-noter-insert-note-no-questions (not org-noter-insert-note-no-questions))) - (phm/org-noter-insert-note)))) - -(define-key org-noter-doc-mode-map (kbd "C-i") 'phm/org-noter-insert-note) -(define-key org-noter-doc-mode-map (kbd "i") 'phm/org-noter-insert-precise-note) -(define-key org-noter-doc-mode-map (kbd "M-i") 'phm/org-noter-insert-note-toggle-no-questions) From 7e4580de88579fd9cdb3c48be1d5182d5fd76635 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Mon, 16 Jan 2023 10:43:30 -0800 Subject: [PATCH 278/453] Rename highlight-precise-note-hook to add-highlight-hook for clarity. --- modules/org-noter-pdf.el | 3 ++- org-noter-core.el | 8 +------- org-noter-test-utils.el | 9 ++++++--- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 064d4cc..0d14e80 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -335,10 +335,11 @@ where (page v-pos . h-pos) is returned" (org-noter-pdf-goto-location 'pdf-view-mode location) (pdf-annot-add-highlight-markup-annotation (cdr location))))) -(add-to-list 'org-noter-highlight-precise-note-hook #'org-noter-pdf-highlight-location) +(add-to-list 'org-noter--add-highlight-hook #'org-noter-pdf-highlight-location) (defun org-noter-pdf-highlight-location (mode precise-location) "Highlight a precise location in PDF" + (message "---> %s %s" mode precise-location) (when (and (memq mode '(doc-view-mode pdf-view-mode)) (pdf-view-active-region-p)) (pdf-annot-add-highlight-markup-annotation (pdf-view-active-region)))) diff --git a/org-noter-core.el b/org-noter-core.el index 3acd39e..d11db33 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -265,7 +265,7 @@ The title used will be the default one." :group 'org-noter :type 'hook) -(defcustom org-noter-highlight-precise-note-hook nil +(defcustom org-noter--add-highlight-hook nil "When a precise note is created this will be called with the `MAJOR-MODE' and `PRECISE-INFO'. This can be used in pdf-mode for example to add a permanent highlight to the document." :group 'org-noter @@ -438,12 +438,6 @@ Used by `org-noter--create-session' when creating a new session." :group 'org-noter :type 'boolean) -(defcustom org-noter-highlight-precise-note-hook nil - "When a precise note is created this will be called with the `MAJOR-MODE' and `PRECISE-INFO'. -This can be used in pdf-mode for example to add a permanent highlight to the document." - :group 'org-noter - :type 'hook) - (defcustom org-noter-max-short-length 80 "Maximum length of a short text selection. Short text selections may be used as note title. When they are quoted in the note, diff --git a/org-noter-test-utils.el b/org-noter-test-utils.el index 6466a37..0468bab 100644 --- a/org-noter-test-utils.el +++ b/org-noter-test-utils.el @@ -112,7 +112,7 @@ org-noter-core-test-return-text (defun org-noter-core-test-pretty-print-location (location) (format "%s" location)) -(defun org-noter-core-test-highlight-location (major-mode precise-info) +(defun org-noter-core-test-add-highlight (major-mode precise-info) t) (defun org-noter-core-test-get-current-view (mode) @@ -127,11 +127,13 @@ org-noter-core-test-return-text ;; if this is not set; make-session fails and the test crashes with a stack overflow. (setq org-noter-always-create-frame nil) + (setq org-noter-highlight-selected-text t) + ;; setup spies so we can verify that things have been called (spy-on 'org-noter-test-get-selected-text :and-call-through) (spy-on 'org-noter-core-test-approx-location :and-call-through) (spy-on 'org-noter-core-test-get-precise-info :and-call-through) - (spy-on 'org-noter-core-test-highlight-location :and-call-through) + (spy-on 'org-noter-core-test-add-highlight :and-call-through) (spy-on 'org-noter-core-test-get-current-view :and-call-through) ;; register all the hooks so we can fake a org-noter-test mode @@ -143,7 +145,8 @@ org-noter-core-test-return-text (add-to-list 'org-noter--get-current-view-hook #'org-noter-core-test-get-current-view) (add-to-list 'org-noter--get-precise-info-hook #'org-noter-core-test-get-precise-info) (add-to-list 'org-noter--pretty-print-location-hook #'org-noter-core-test-pretty-print-location) - (add-to-list 'org-noter-highlight-precise-note-hook #'org-noter-core-test-highlight-location) + (add-to-list 'org-noter--add-highlight-hook #'org-noter-core-test-add-highlight) + (add-to-list 'org-noter--get-highlight-location-hook #'org-noter-core-test-get-highlight-location) ) From 18d01908f1d811a1bacaf33ac56cfcddef2b2d1a Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Mon, 16 Jan 2023 10:46:33 -0800 Subject: [PATCH 279/453] Correct the tests after merging with peter's branch. --- tests/org-noter-core-tests.el | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/org-noter-core-tests.el b/tests/org-noter-core-tests.el index 990a9ef..0b19b96 100644 --- a/tests/org-noter-core-tests.el +++ b/tests/org-noter-core-tests.el @@ -25,7 +25,8 @@ mock-contents-simple-notes-file '(lambda () (org-noter-core-test-create-session) - (org-noter-insert-note nil "NEW NOTE") + (let ((org-noter-insert-note-no-questions t)) + (org-noter-insert-note nil "NEW NOTE")) (expect 'org-noter-test-get-selected-text :to-have-been-called) (expect (string-match "Notes for page" (buffer-string)) :not :to-be nil)))) @@ -60,7 +61,7 @@ (org-noter-core-test-create-session) (with-simulated-input "precise SPC note RET" (org-noter-insert-precise-note)) - (expect 'org-noter-core-test-highlight-location :to-have-been-called)))) + (expect 'org-noter-core-test-add-highlight :to-have-been-called)))) ;; hit C-g when entering a note; expect no highlight (it "precise note DOES NOT call the highlight hook when the note is aborted" @@ -72,7 +73,7 @@ (condition-case nil (with-simulated-input "C-g" (org-noter-insert-precise-note)) (quit nil)) - (expect 'org-noter-core-test-highlight-location :not :to-have-been-called)))) + (expect 'org-noter-core-test-add-highlight :not :to-have-been-called)))) ) @@ -168,6 +169,9 @@ (describe "persistent highlights" (describe "no hooks are setup for precise note highlights" ;; if no hooks for highlights are setup we expect no :HIGHLIGHT: property + (before-each + (setq org-noter--get-highlight-location-hook '()) + ) (it "can take a precise note without a highlight appearing" (with-mock-contents mock-contents-simple-notes-file From 527a9e503d9219544c7d4261bd84981a952a3c37 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Mon, 16 Jan 2023 10:48:32 -0800 Subject: [PATCH 280/453] =?UTF-8?q?Call=20the=20correct=20hook=20when=20hi?= =?UTF-8?q?ghlighting=20=F0=9F=A4=A6=F0=9F=8F=BB=E2=80=8D=E2=99=82?= =?UTF-8?q?=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- org-noter-core.el | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index d11db33..f6be230 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -2112,9 +2112,8 @@ See `org-noter-insert-note' docstring for more." (select-frame-set-input-focus (org-noter--session-frame session)) (select-window (get-buffer-window (org-noter--session-doc-buffer session))) - ;; this adds the highlight to the document - (run-hook-with-args-until-success 'org-noter-highlight-precise-note-hook major-mode highlight-location) - )))) + ;; this adds the highlight to the document + (run-hook-with-args-until-success 'org-noter--add-highlight-hook major-mode highlight-location))))) (defun org-noter--get-highlight-location () "Returns a highlight location. This is mode specific. From 27128503c5f8b0ad49c9947f62841094d8eed7da Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Mon, 16 Jan 2023 13:47:38 -0800 Subject: [PATCH 281/453] add HIGHLIGHT property to note when applicable --- org-noter-core.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org-noter-core.el b/org-noter-core.el index f6be230..8d50dc3 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -2068,7 +2068,7 @@ Guiding principles for note generation ;; store the highlight in org IF we have a highlight AND can serialize it. (when-let ((highlight-location) (serialized-highlight (org-noter--get-serialized-highlight highlight-location))) - (org-entry-put nil "HIGHLIGHT" serialized-highlight)) + (org-set-property "HIGHLIGHT" serialized-highlight)) (when note-body (save-excursion (if short-selected-text From 93dad61e6c6e916678aad69e68f7d460c2ad3275 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Mon, 16 Jan 2023 14:19:47 -0800 Subject: [PATCH 282/453] move highlighting utility funcs into utility section of code --- org-noter-core.el | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 8d50dc3..6ee55cf 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1654,6 +1654,16 @@ relative to." (frame-parameter frame 'delete-before)) (throw 'other-frame frame))))) +(defun org-noter--get-highlight-location () + "Returns a highlight location. This is mode specific. +In PDF it's a the page number and 4 coordinates for the highlight. This is delegated to each document mode." + (with-selected-window (org-noter--get-doc-window) + (run-hook-with-args-until-success 'org-noter--get-highlight-location-hook))) + +(defun org-noter--get-serialized-highlight (highlight-location) +"Returns a string representation of the HIGHLIGHT-LOCATION. This is delegated to each document mode (eg pdf)" + (run-hook-with-args-until-success 'org-noter--pretty-print-highlight-location-hook highlight-location)) + ;; -------------------------------------------------------------------------------- ;;; User commands (defun org-noter-set-start-location (&optional arg) @@ -2115,16 +2125,6 @@ See `org-noter-insert-note' docstring for more." ;; this adds the highlight to the document (run-hook-with-args-until-success 'org-noter--add-highlight-hook major-mode highlight-location))))) -(defun org-noter--get-highlight-location () - "Returns a highlight location. This is mode specific. -In PDF it's a the page nubmer and 4 coordinates for the highglight. This is delegated to each document mode." - (with-selected-window (org-noter--get-doc-window) - (run-hook-with-args-until-success 'org-noter--get-highlight-location-hook))) - -(defun org-noter--get-serialized-highlight (highlight-location) -"Returns a string representation of the HIGHLIGHT-LOCATION. This is delegated to each document mode (eg pdf)" - (run-hook-with-args-until-success 'org-noter--pretty-print-highlight-location-hook highlight-location)) - From 89a4ee180eea18ead0dbbe799143b68823ca29a5 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Mon, 16 Jan 2023 14:21:47 -0800 Subject: [PATCH 283/453] move highlighting into org-noter-insert-note --- org-noter-core.el | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 6ee55cf..4246d6d 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1942,7 +1942,7 @@ want to kill." (user-error "This command is not supported for %s" (org-noter--session-doc-mode session))))) -(defun org-noter-insert-note (&optional precise-info note-title highlight-location) +(defun org-noter-insert-note (&optional precise-info note-title) "Insert note associated with the current location. This command will prompt for a title of the note and then insert @@ -1979,6 +1979,7 @@ Guiding principles for note generation (selected-text-p (> (length selected-text) 0)) force-new (location (org-noter--doc-approx-location (or precise-info 'interactive) (gv-ref force-new))) + (highlight-location (org-noter--get-highlight-location)) (current-view (org-noter--get-current-view))) (let ((inhibit-quit t) @@ -1986,6 +1987,9 @@ Guiding principles for note generation (<= (length selected-text) org-noter-max-short-length)) selected-text))) (with-local-quit + (when org-noter-highlight-selected-text + (run-hook-with-args-until-success 'org-noter--add-highlight-hook major-mode highlight-location)) + (select-frame-set-input-focus (window-frame window)) (select-window window) @@ -2106,27 +2110,14 @@ that part. Will always create a new note. When text is selected, it will automatically choose the top of the selected text as the location and the text itself as the -title of the note (you may change it anyway!). +default title of the note if the text is <= +`org-noter-max-short-length' (you may change it anyway!). See `org-noter-insert-note' docstring for more." (interactive "P") (org-noter--with-valid-session - (let ((org-noter-insert-note-no-questions (if toggle-no-questions - (not org-noter-insert-note-no-questions) - org-noter-insert-note-no-questions)) - (precise-info (org-noter--get-precise-info)) - (highlight-location (org-noter--get-highlight-location))) - - (org-noter-insert-note precise-info nil highlight-location) - (when org-noter-highlight-selected-text - (select-frame-set-input-focus (org-noter--session-frame session)) - (select-window (get-buffer-window (org-noter--session-doc-buffer session))) - - ;; this adds the highlight to the document - (run-hook-with-args-until-success 'org-noter--add-highlight-hook major-mode highlight-location))))) - - - + (let ((precise-info (org-noter--get-precise-info))) + (org-noter-insert-note precise-info)))) (defun org-noter-insert-note-toggle-no-questions () "Insert note associated with the current location. From 9bb9d2988e1035e61cc07ea6398fb1ddd7da37e1 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Mon, 16 Jan 2023 14:54:40 -0800 Subject: [PATCH 284/453] Prefix (C-u) toggles -highlight-selected-text Also, add function org-noter-insert-precise-note-toggle-no-questions, bound to M-I --- org-noter-core.el | 45 +++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 4246d6d..2f68019 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1942,7 +1942,7 @@ want to kill." (user-error "This command is not supported for %s" (org-noter--session-doc-mode session))))) -(defun org-noter-insert-note (&optional precise-info note-title) +(defun org-noter-insert-note (&optional toggle-highlight precise-info note-title) "Insert note associated with the current location. This command will prompt for a title of the note and then insert @@ -1969,7 +1969,7 @@ Guiding principles for note generation 3. Refrain from making notes in the same location with the same title 4. Precise notes generally have different locations, so always make new precise notes" - (interactive) + (interactive "P") (org-noter--with-valid-session (let* ((ast (org-noter--parse-root)) (contents (org-element-contents ast)) (window (org-noter--get-notes-window 'force)) @@ -1979,17 +1979,17 @@ Guiding principles for note generation (selected-text-p (> (length selected-text) 0)) force-new (location (org-noter--doc-approx-location (or precise-info 'interactive) (gv-ref force-new))) - (highlight-location (org-noter--get-highlight-location)) (current-view (org-noter--get-current-view))) - (let ((inhibit-quit t) - (short-selected-text (if (and selected-text-p - (<= (length selected-text) org-noter-max-short-length)) - selected-text))) - (with-local-quit - (when org-noter-highlight-selected-text - (run-hook-with-args-until-success 'org-noter--add-highlight-hook major-mode highlight-location)) + (let* ((inhibit-quit t) + (short-selected-text (if (and selected-text-p + (<= (length selected-text) org-noter-max-short-length)) + selected-text)) + (org-noter-highlight-selected-text (if toggle-highlight (not org-noter-highlight-selected-text) + org-noter-highlight-selected-text)) + (highlight-location (if org-noter-highlight-selected-text (org-noter--get-highlight-location)))) + (with-local-quit (select-frame-set-input-focus (window-frame window)) (select-window window) @@ -2095,14 +2095,18 @@ Guiding principles for note generation (org-show-set-visibility t) (org-cycle-hide-drawers 'all) - (org-cycle-show-empty-lines t))) + (org-cycle-show-empty-lines t) + (when org-noter-highlight-selected-text ; return to DOC window and highlight text + (select-frame-set-input-focus (org-noter--session-frame session)) + (select-window (get-buffer-window (org-noter--session-doc-buffer session))) + (run-hook-with-args-until-success 'org-noter--add-highlight-hook major-mode highlight-location)))) (when quit-flag ;; NOTE(nox): If this runs, it means the user quitted while creating a note, so ;; revert to the previous window. (select-frame-set-input-focus (org-noter--session-frame session)) (select-window (get-buffer-window (org-noter--session-doc-buffer session)))))))) -(defun org-noter-insert-precise-note (&optional toggle-no-questions) +(defun org-noter-insert-precise-note (&optional toggle-highlight) "Insert note associated with a specific location. This will ask you to click where you want to scroll to when you sync the document to this note. You should click on the top of @@ -2117,15 +2121,23 @@ See `org-noter-insert-note' docstring for more." (interactive "P") (org-noter--with-valid-session (let ((precise-info (org-noter--get-precise-info))) - (org-noter-insert-note precise-info)))) + (org-noter-insert-note toggle-highlight precise-info)))) -(defun org-noter-insert-note-toggle-no-questions () +(defun org-noter-insert-note-toggle-no-questions (&optional toggle-highlight) "Insert note associated with the current location. This is like `org-noter-insert-note', except it will toggle `org-noter-insert-note-no-questions'" - (interactive) + (interactive "P") + (org-noter--with-valid-session + (let ((org-noter-insert-note-no-questions (not org-noter-insert-note-no-questions))) + (org-noter-insert-note toggle-highlight)))) + +(defun org-noter-insert-precise-note-toggle-no-questions (&optional toggle-highlight) + "Insert note associated with the current location. +This is like `org-noter-insert-precise-note', except it will toggle `org-noter-insert-note-no-questions'" + (interactive "P") (org-noter--with-valid-session (let ((org-noter-insert-note-no-questions (not org-noter-insert-note-no-questions))) - (org-noter-insert-note)))) + (org-noter-insert-precise-note toggle-highlight)))) (defmacro org-noter--map-ignore-headings-with-doc-file (contents match-first &rest body) `(let (ignore-until-level) @@ -2271,6 +2283,7 @@ Keymap: :keymap `((,(kbd "i") . org-noter-insert-note) (,(kbd "C-i") . org-noter-insert-note-toggle-no-questions) (,(kbd "M-i") . org-noter-insert-precise-note) + (,(kbd "M-I") . org-noter-insert-precise-note-toggle-no-questions) (,(kbd "q") . org-noter-kill-session) (,(kbd "M-p") . org-noter-sync-prev-page-or-chapter) (,(kbd "M-.") . org-noter-sync-current-page-or-chapter) From 6360aa49cb6748ec893014bbd23ed05f42fa8601 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 15 Jan 2023 23:14:40 -0800 Subject: [PATCH 285/453] move -pdf-tools-edges-to-region from -core.el to modules/-pdf.el This fn was left behind in c1g's refactor to modules. --- modules/org-noter-pdf.el | 15 +++++++++++++++ org-noter-core.el | 15 --------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 0d14e80..dc43444 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -161,6 +161,21 @@ where (page v-pos . h-pos) is returned" (add-to-list 'org-noter-get-selected-text-hook #'org-noter-pdf--get-selected-text) +;; NOTE(nox): From machc/pdf-tools-org +(defun org-noter--pdf-tools-edges-to-region (edges) + "Get 4-entry region (LEFT TOP RIGHT BOTTOM) from several EDGES." + (when edges + (let ((left0 (nth 0 (car edges))) + (top0 (nth 1 (car edges))) + (bottom0 (nth 3 (car edges))) + (top1 (nth 1 (car (last edges)))) + (right1 (nth 2 (car (last edges)))) + (bottom1 (nth 3 (car (last edges))))) + (list left0 + (+ top0 (/ (- bottom0 top0) 3)) + right1 + (- bottom1 (/ (- bottom1 top1) 3)))))) + (defun org-noter-create-skeleton-pdf (mode) "Create notes skeleton with the PDF outline or annotations." (when (eq mode 'pdf-view-mode) diff --git a/org-noter-core.el b/org-noter-core.el index 2f68019..9645741 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1571,21 +1571,6 @@ relative to." ((= number-of-notes 1) (propertize " 1 note " 'face 'org-noter-notes-exist-face)) (t (propertize (format " %d notes " number-of-notes) 'face 'org-noter-notes-exist-face)))))) -;; NOTE(nox): From machc/pdf-tools-org -(defun org-noter--pdf-tools-edges-to-region (edges) - "Get 4-entry region (LEFT TOP RIGHT BOTTOM) from several EDGES." - (when edges - (let ((left0 (nth 0 (car edges))) - (top0 (nth 1 (car edges))) - (bottom0 (nth 3 (car edges))) - (top1 (nth 1 (car (last edges)))) - (right1 (nth 2 (car (last edges)))) - (bottom1 (nth 3 (car (last edges))))) - (list left0 - (+ top0 (/ (- bottom0 top0) 3)) - right1 - (- bottom1 (/ (- bottom1 top1) 3)))))) - (defun org-noter--check-if-document-is-annotated-on-file (document-path notes-path) ;; NOTE(nox): In order to insert the correct file contents (let ((buffer (find-buffer-visiting notes-path))) From 3dad1df5f4792583d411dab4dfdd4f004795a4ae Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Mon, 16 Jan 2023 10:11:42 -0800 Subject: [PATCH 286/453] Move nov variables from -core to modules/..-nov More things left behind in modularization. --- modules/org-noter-nov.el | 3 +++ org-noter-core.el | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el index 18d9701..8d3181a 100644 --- a/modules/org-noter-nov.el +++ b/modules/org-noter-nov.el @@ -25,6 +25,9 @@ ;;; Code: (require 'org-noter) +(defvar nov-documents-index) +(defvar nov-file-name) + (defun org-noter-get-buffer-file-name-nov (&optional mode) (bound-and-true-p nov-file-name)) diff --git a/org-noter-core.el b/org-noter-core.el index 9645741..b580921 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -59,8 +59,6 @@ (declare-function pdf-view-active-region-text "ext:pdf-view") (declare-function pdf-view-goto-page "ext:pdf-view") (declare-function pdf-view-mode "ext:pdf-view") -(defvar nov-documents-index) -(defvar nov-file-name) ;; -------------------------------------------------------------------------------- ;;; User variables From c0acbaea35941ef6f8afb9598c86c973b23347b1 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Mon, 16 Jan 2023 17:26:26 -0800 Subject: [PATCH 287/453] move text-selection related vars into User Variables section of code Also, rename org-noter-max-short-length to org-noter-max-short-selected-text-length. --- org-noter-core.el | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index b580921..fe806e8 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -269,6 +269,21 @@ This can be used in pdf-mode for example to add a permanent highlight to the doc :group 'org-noter :type 'hook) +(defcustom org-noter-highlight-selected-text nil + "If non-nil, highlight selected-text when creating notes. This +variable is temporarily toggled by prefixing the insertion +command with any non-nil prefix such as \\[universal-argument]." + :group 'org-noter + :type 'boolean) + +(defcustom org-noter-max-short-selected-text-length 80 + "Maximum length of a short text selection. Short text selections +may be used as note title. When they are quoted in the note, +they are quoted as ``short-selected-text'' rather than inside a +QUOTE-block." + :group 'org-noter + :type 'integer) + (defcustom org-noter-find-additional-notes-functions nil "Functions that when given a document file path as argument, give out an org note file path. @@ -431,19 +446,6 @@ Used by `org-noter--create-session' when creating a new session." :group 'org-noter :type 'hook) -(defcustom org-noter-highlight-selected-text nil - "Highlight selected-text when creating precise notes" - :group 'org-noter - :type 'boolean) - -(defcustom org-noter-max-short-length 80 - "Maximum length of a short text selection. Short text selections -may be used as note title. When they are quoted in the note, -they are quoted as ``short-selected-text'' rather than inside a -QUOTE-block." - :group 'org-noter - :type 'integer) - ;; -------------------------------------------------------------------------------- ;;; Private variables or constants (cl-defstruct org-noter--session @@ -1930,7 +1932,7 @@ want to kill." This command will prompt for a title of the note and then insert it in the notes buffer. When the input is empty, a title based on -either the selected text (if it is <= `org-noter-max-short-length') +either the selected text (if it is <= `org-noter-max-short-selected-text-length') or `org-noter-default-heading-title' will be generated. If there are other notes related to the current location, the @@ -1966,7 +1968,7 @@ Guiding principles for note generation (let* ((inhibit-quit t) (short-selected-text (if (and selected-text-p - (<= (length selected-text) org-noter-max-short-length)) + (<= (length selected-text) org-noter-max-short-selected-text-length)) selected-text)) (org-noter-highlight-selected-text (if toggle-highlight (not org-noter-highlight-selected-text) org-noter-highlight-selected-text)) @@ -2098,7 +2100,7 @@ that part. Will always create a new note. When text is selected, it will automatically choose the top of the selected text as the location and the text itself as the default title of the note if the text is <= -`org-noter-max-short-length' (you may change it anyway!). +`org-noter-max-short-selected-text-length' (you may change it anyway!). See `org-noter-insert-note' docstring for more." (interactive "P") From b9a7b99d4ff6a68d1351307c33e8ed66ebd6d3ed Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Mon, 16 Jan 2023 17:30:11 -0800 Subject: [PATCH 288/453] move highlighting hooks into "Integration w/ other packages" section --- org-noter-core.el | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index fe806e8..06a8a74 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -253,21 +253,20 @@ The title used will be the default one." :group 'org-noter :type 'hook) -(defcustom org-noter--pretty-print-highlight-location-hook nil - "Hook that runs to serialize a highlight location so that it can be stored in org." - :group 'org-noter - :type 'hook) - -(defcustom org-noter--get-highlight-location-hook nil - "Hook that runs to get the location of a highlight" +(defcustom org-noter-highlight-selected-text nil + "If non-nil, highlight selected-text when creating notes. This +variable is temporarily toggled by prefixing the insertion +command with any non-nil prefix such as \\[universal-argument]." :group 'org-noter - :type 'hook) + :type 'boolean) -(defcustom org-noter--add-highlight-hook nil - "When a precise note is created this will be called with the `MAJOR-MODE' and `PRECISE-INFO'. -This can be used in pdf-mode for example to add a permanent highlight to the document." +(defcustom org-noter-max-short-selected-text-length 80 + "Maximum length of a short text selection. Short text selections +may be used as note title. When they are quoted in the note, +they are quoted as ``short-selected-text'' rather than inside a +QUOTE-block." :group 'org-noter - :type 'hook) + :type 'integer) (defcustom org-noter-highlight-selected-text nil "If non-nil, highlight selected-text when creating notes. This @@ -376,7 +375,6 @@ major modes uses the `buffer-file-name' variable." :group 'org-noter :type 'hook) - (defcustom org-noter--check-location-property-hook nil "TODO" :group 'org-noter @@ -402,6 +400,22 @@ major modes uses the `buffer-file-name' variable." :group 'org-noter :type 'hook) +(defcustom org-noter--pretty-print-highlight-location-hook nil + "Hook that runs to serialize a highlight location so that it can be stored in org." + :group 'org-noter + :type 'hook) + +(defcustom org-noter--get-highlight-location-hook nil + "Hook that runs to get the location of a highlight" + :group 'org-noter + :type 'hook) + +(defcustom org-noter--add-highlight-hook nil + "When a precise note is created this will be called with the `MAJOR-MODE' and `PRECISE-INFO'. +This can be used in pdf-mode for example to add a permanent highlight to the document." + :group 'org-noter + :type 'hook) + (defcustom org-noter--note-after-tipping-point-hook nil "TODO" :group 'org-noter @@ -417,7 +431,6 @@ major modes uses the `buffer-file-name' variable." :group 'org-noter :type 'hook) - (defcustom org-noter--get-current-view-hook nil "TODO" :group 'org-noter From 218e5924b64ae7b96ccb5731c6ab6e13e88560ce Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Mon, 16 Jan 2023 17:43:04 -0800 Subject: [PATCH 289/453] create subgroup org-noter-module-hooks for module hooks trying to tidy up the org-noter customization group. Users aren't expected to set up those hooks on their own. --- org-noter-core.el | 47 ++++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 06a8a74..e9d60ee 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -315,6 +315,11 @@ the user select to use as the note file of the document." ;; -------------------------------------------------------------------------------- ;;; Integration with other packages +(defgroup org-noter-module-hooks nil + "Hooks for integrating org-noter with other packages (pdfview, nov, djvu)" + :group 'org-noter + :version "25.3.1") + (defcustom org-noter--get-location-property-hook nil "The list of functions that will return the note location of an org element. @@ -323,7 +328,7 @@ These functions is used by `org-noter--parse-location-property' and `org-noter--check-location-property' when they can't find the note location of the org element given to them, that org element will be passed to the functions in this list." - :group 'org-noter + :group 'org-noter-module-hooks :type 'hook) (defcustom org-noter--get-containing-element-hook '(org-noter--get-containing-heading @@ -331,7 +336,7 @@ the functions in this list." "The list of functions that will be called by `org-noter--get-containing-element' to get the org element of the note at point." - :group 'org-noter + :group 'org-noter-module-hooks :type 'hook) (defcustom org-noter-parse-document-property-hook nil @@ -350,7 +355,7 @@ the property \"NOTER_DOCUMENT\" (the default value of citation key, it will return the path to the note file associated with the citation key and that path will be used for other operations instead of the real value of the property." - :group 'org-noter + :group 'org-noter-module-hooks :type 'hook) (defcustom org-noter-get-buffer-file-name-hook nil @@ -362,85 +367,85 @@ user calls `org-noter' on a document buffer. For example, `nov-mode', a renderer for EPUB documents uses a unique variable called `nov-file-name' to store the file name of its document while the other major modes uses the `buffer-file-name' variable." - :group 'org-noter + :group 'org-noter-module-hooks :type 'hook) (defcustom org-noter-set-up-document-hook nil "TODO" - :group 'org-noter + :group 'org-noter-module-hooks :type 'hook) (defcustom org-noter-get-selected-text-hook nil "TODO" - :group 'org-noter + :group 'org-noter-module-hooks :type 'hook) (defcustom org-noter--check-location-property-hook nil "TODO" - :group 'org-noter + :group 'org-noter-module-hooks :type 'hook) (defcustom org-noter--parse-location-property-hook nil "TODO" - :group 'org-noter + :group 'org-noter-module-hooks :type 'hook) (defcustom org-noter--pretty-print-location-hook nil "TODO" - :group 'org-noter + :group 'org-noter-module-hooks :type 'hook) (defcustom org-noter--convert-to-location-cons-hook nil "TODO" - :group 'org-noter + :group 'org-noter-module-hooks :type 'hook) (defcustom org-noter--doc-goto-location-hook nil "TODO" - :group 'org-noter + :group 'org-noter-module-hooks :type 'hook) (defcustom org-noter--pretty-print-highlight-location-hook nil "Hook that runs to serialize a highlight location so that it can be stored in org." - :group 'org-noter + :group 'org-noter-module-hooks :type 'hook) (defcustom org-noter--get-highlight-location-hook nil "Hook that runs to get the location of a highlight" - :group 'org-noter + :group 'org-noter-module-hooks :type 'hook) (defcustom org-noter--add-highlight-hook nil "When a precise note is created this will be called with the `MAJOR-MODE' and `PRECISE-INFO'. This can be used in pdf-mode for example to add a permanent highlight to the document." - :group 'org-noter + :group 'org-noter-module-hooks :type 'hook) (defcustom org-noter--note-after-tipping-point-hook nil "TODO" - :group 'org-noter + :group 'org-noter-module-hooks :type 'hook) (defcustom org-noter--relative-position-to-view-hook nil "TODO" - :group 'org-noter + :group 'org-noter-module-hooks :type 'hook) (defcustom org-noter--get-precise-info-hook nil "TODO" - :group 'org-noter + :group 'org-noter-module-hooks :type 'hook) (defcustom org-noter--get-current-view-hook nil "TODO" - :group 'org-noter + :group 'org-noter-module-hooks :type 'hook) (defcustom org-noter--doc-approx-location-hook nil "This returns an approximate location if no precise info is passed: (PAGE 0) or if precise info is passed, it's (PAGE 0 0 0 0) where 0s are the precise coords) " - :group 'org-noter + :group 'org-noter-module-hooks :type 'hook) (defcustom org-noter-create-skeleton-functions nil @@ -450,13 +455,13 @@ The functions will be given a major mode of the document and must return a non-nil value when the outline is created. Used by `org-noter-create-skeleton'." - :group 'org-noter + :group 'org-noter-module-hooks :type 'hook) (defcustom org-noter-open-document-functions nil "Functions that gives a buffer when passed with a document property. Used by `org-noter--create-session' when creating a new session." - :group 'org-noter + :group 'org-noter-module-hooks :type 'hook) ;; -------------------------------------------------------------------------------- From 68b23b698a3e276db504b02b17ca844687278375 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Mon, 16 Jan 2023 18:25:21 -0800 Subject: [PATCH 290/453] Update documentation to make it more new-user-friendly --- README.org | 79 ++++++++++++++----------- README-djvu.org => docs/README-djvu.org | 0 README-orig.org => docs/README-orig.org | 0 docs/pre-merge_notes.org | 2 +- 4 files changed, 44 insertions(+), 37 deletions(-) rename README-djvu.org => docs/README-djvu.org (100%) rename README-orig.org => docs/README-orig.org (100%) diff --git a/README.org b/README.org index 9964ba5..748856a 100644 --- a/README.org +++ b/README.org @@ -1,13 +1,19 @@ * ORG-NOTER (fork of a fork) -** This is a fork of the *c1-g* fork with some commits picked from *dmitrym* + +** If you are new to *ORG-NOTER* + Please first look at [[https://github.com/petermao/org-noter/blob/master/docs/README-orig.org][Gonçalo Santos's README]] ([[file:docs/README-orig.org]] in + this repo). + +** This is a refactor of the *c1-g* fork done collaborative by [[https://github.com/petermao/org-noter][*petermao*]] and [[https://github.com/dmitrym0/org-noter-plus-djvu][*dmitrym0*]] - In essence, this is close to the original *weirdNox* (Gonçalo Santos) - version that you find on MELPA with the refactoring that *c1-g* implemented + version that you find on MELPA with the refactoring that *c1-g* implemented. - - djvu and epub support have also been advanced by *c1-g*, but I have not - tested any of that code. + - djvu and epub support have also been advanced by *c1-g*, but we have not + tested any of that code. See [[https://github.com/petermao/org-noter/blob/master/docs/README-djvu.org][Charlie Gordon's README]] + ([[file:docs/README-djvu.org]] in this repo). - - The main new feature for PDF users is 2-D precise notes (introduced by - *Ahmed Shariff*, which can be inserted in two ways: + - Prior to our work, the main new feature for PDF users was 2-D precise notes + (introduced by *Ahmed Shariff*, which can be inserted in two ways: 1. "Select-precise": selecting text in the pdf, followed by =M-i=. *c1-g* changed the position format to use the entire list returned by =edges=. This breaks the other way of inserting precise notes. On *dmitrym*'s @@ -28,24 +34,38 @@ - avoids having different notes with the same heading In Stock org-noter, repeated TAB's give multiple notes. In this refactor, - new notes in the same location with the same title are note made. For now, - precise notes are excepted from this rule. + new notes in the same location with the same title are not made, but + selected text is added to the note body. For now, precise notes are + excepted from this rule. + + - long text-selections are enclosed in #+BEGIN_QUOTE...#+END_QUOTE + - short text-selections are enclosed in ``...'' (LaTeX style) when they are + not used as the title of the note. + - short/long text-selections are differentiated by the custom variable + =org-noter-max-short-selected-text-length= (default: 80 char) - - long quotes are enclosed in #+BEGIN_QUOTE...#+END_QUOTE - - short quotes are enclosed in ``...'' (LaTeX style) - - short/long are differentiated by customizable variable (default 80 char) + In Stock org-noter, long selections are those with more than 3 lines. - In Stock org-noter, long selections have more than 3 lines. - - NEW: multicolumn precise-notes defined by property NUM_COLUMNS + - *NEW:* Highlighting of selected text + + Default setting is customizable, see =org-noter-highlight-selected-text=. + Calling insertions with a non-nil prefix (eg, =C-u=) toggles this setting for + individual note insertions. + + - *NEW:* multicolumn precise-notes defined by property NUM_COLUMNS Implements issue #153 in weirdNox/org-noter by adding =org-noter-pdf-convert-to-location-cons= to =org-noter--convert-to-location-cons-hook=, where a "virtual" vertical location is calculated by dividing the page into equal width columns. This is not a perfect solution, and is probably best for low-integer numbers of - columns, since the page is just divided evenly into NUM_COLUMNS vertical + columns, since the page is just divided evenly into =NUM_COLUMNS= vertical strips. + This can be set using =org-set-property-and-value=, but eventually we will + write a user function to prevent users from mistyping the name of the + property (=NUM_COLUMNS=). + *** Stock | | insert-note =i= | precise note =M-i= | no-questions =C-i, TAB= | |-----------------+--------------------------------+---------------------------+-------------------------| @@ -57,15 +77,15 @@ |-----------------+--------------------------------+---------------------------+-------------------------| *** Refactored - | | insert-note | precise note | no-questions | - |----------------------+---------------------------+---------------------------+---------------------------| - | title prompt? | Y | Y | N | - | default title 1 | short-selected-text | short-selected-text | short-selected-text | - | default title 2 | "Notes for page #" | "Notes for page # x y" | "Notes for page #" | - | new note | with new title | always | with new title | - | body | selected-text (not title) | selected-text (not title) | selected-text (not title) | - |----------------------+---------------------------+---------------------------+---------------------------| - | highlight selection? | ?? | ?? | ?? | + | | insert-note | precise note | no-questions | + |---------------------+---------------------------+---------------------------+---------------------------| + | title prompt? | Y | Y | N | + | default title 1 | short-selected-text | short-selected-text | short-selected-text | + | default title 2 | "Notes for page #" | "Notes for page # x y" | "Notes for page #" | + | new note | with new title | always | with new title | + | body | selected-text (not title) | selected-text (not title) | selected-text (not title) | + |---------------------+---------------------------+---------------------------+---------------------------| + | highlight selection | user setting/toggle | user setting/toggle | user setting/toggle | ** Features wishlist 1. Use pdf-view-current-pagelabel to use the page label instead of page in default titles @@ -102,16 +122,3 @@ - =org-noter--check-location-property= found bug: [[file:org-noter-core.el:1023]] change test from integerp to numberp - =org-noter--get-containing-property-drawer= -** Highlighting - my solution is compact, but too pdf-specific. need to sketch out Dmitry's solution - - in precise-notes - 1. hook to make the highlight in the document - 2. hook to preserve highlight in org-noter file - - what contexts would HL be used? - 1. only precise? (current implementation) - 2. anytime (Peter's implementation) - - how does this play with quoting text? - 1. HL Y/N - 2. Quoting Y/N - 3. No HL if Q - 4. No Q if HL diff --git a/README-djvu.org b/docs/README-djvu.org similarity index 100% rename from README-djvu.org rename to docs/README-djvu.org diff --git a/README-orig.org b/docs/README-orig.org similarity index 100% rename from README-orig.org rename to docs/README-orig.org diff --git a/docs/pre-merge_notes.org b/docs/pre-merge_notes.org index 70a5711..3ebded9 100644 --- a/docs/pre-merge_notes.org +++ b/docs/pre-merge_notes.org @@ -207,4 +207,4 @@ ACTION: Dmitry revive on his side. is used ACTION: safe for Dmitry to cherry-pick -** TODO use cl-lib or native elisp hash tables rather than the =ht= package. +** DONE use cl-lib or native elisp hash tables rather than the =ht= package. From 768f43682e1c328b232d8dfffe01c28ecb3ca21a Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Mon, 16 Jan 2023 18:29:35 -0800 Subject: [PATCH 291/453] @dmitrym0: do we need dash just for -if-let*? this commit passes tests. Is there a reason to prefer the dash package version of the command over the built-in elisp version? --- modules/org-noter-pdf.el | 2 +- org-noter-core.el | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index dc43444..15c84d0 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -34,7 +34,7 @@ the relevant info (page, coordinates) Otherwise returns nil" - (-if-let* ((_ (pdf-view-active-region-p)) + (if-let* ((_ (pdf-view-active-region-p)) (page (image-mode-window-get 'page)) (coords (pdf-view-active-region))) (make-pdf-highlight :page page :coords coords) diff --git a/org-noter-core.el b/org-noter-core.el index e9d60ee..79b2244 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -39,7 +39,6 @@ (require 'org-element) (require 'cl-lib) (require 'pdf-tools) -(require 'dash) (declare-function doc-view-goto-page "doc-view") (declare-function image-display-size "image-mode") From 385967f8fee4f47775c61e4a4c5398517a838965 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Mon, 16 Jan 2023 20:35:58 -0800 Subject: [PATCH 292/453] Use selection as title and body for precise notes. --- org-noter-core.el | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index f6be230..9700ae0 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -2001,11 +2001,20 @@ Guiding principles for note generation (setq collection (nreverse collection) ;; prompt for title (unless no-Q's) - title (if org-noter-insert-note-no-questions default-title - (completing-read "Note: " collection nil nil nil nil default-title)) - note-body (if (and selected-text-p - (not (equal title short-selected-text))) - selected-text) + title (cond + ;; for precise notes we want the selection to be the title + ((and precise-info selected-text-p) + selected-text) + (org-noter-insert-note-no-questions + default-title) + (t + (completing-read "Note: " collection nil nil nil nil default-title))) + note-body (cond + ;; if we're making a precise note; note-body should be selected-text + ((and precise-info selected-text-p) + selected-text) + ((and selected-text-p (not (equal title short-selected-text))) + selected-text)) ;; is this an existing note? skip for precise notes existing-note (unless precise-info (cdr (assoc title collection)))) From d61624986e82ac3141474d74088a178ea9d71c6f Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Mon, 16 Jan 2023 20:58:17 -0800 Subject: [PATCH 293/453] Make the note title a part of completing read when taking a precise note. --- org-noter-core.el | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 9700ae0..df716d6 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1999,12 +1999,14 @@ Guiding principles for note generation (let ((display (org-element-property :raw-value (car note-cons)))) (push (cons display note-cons) collection)))) + ;; we're doing a precise note, so we're not going to insert into an existing note/heading. + ;; make the selected text available in completing read instead. + (if (and precise-info selected-text-p) + (cl-pushnew selected-text collection)) + (setq collection (nreverse collection) ;; prompt for title (unless no-Q's) title (cond - ;; for precise notes we want the selection to be the title - ((and precise-info selected-text-p) - selected-text) (org-noter-insert-note-no-questions default-title) (t From b2c7ea1f1b84d1cb14c5f935dc5f2dbd12708041 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Tue, 17 Jan 2023 19:06:33 -0800 Subject: [PATCH 294/453] fix links in README.org --- README.org | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.org b/README.org index 748856a..7224efb 100644 --- a/README.org +++ b/README.org @@ -1,15 +1,15 @@ * ORG-NOTER (fork of a fork) ** If you are new to *ORG-NOTER* - Please first look at [[https://github.com/petermao/org-noter/blob/master/docs/README-orig.org][Gonçalo Santos's README]] ([[file:docs/README-orig.org]] in + Please first look at [[https://github.com/weirdNox/org-noter/blob/master/README.org][Gonçalo Santos's README]] ([[file:docs/README-orig.org]] in this repo). -** This is a refactor of the *c1-g* fork done collaborative by [[https://github.com/petermao/org-noter][*petermao*]] and [[https://github.com/dmitrym0/org-noter-plus-djvu][*dmitrym0*]] +** This is a refactor of the *c1-g* fork done collaboratively by [[https://github.com/petermao/org-noter][petermao]] and [[https://github.com/dmitrym0/org-noter-plus-djvu][dmitrym0]] - In essence, this is close to the original *weirdNox* (Gonçalo Santos) version that you find on MELPA with the refactoring that *c1-g* implemented. - djvu and epub support have also been advanced by *c1-g*, but we have not - tested any of that code. See [[https://github.com/petermao/org-noter/blob/master/docs/README-djvu.org][Charlie Gordon's README]] + tested any of that code. See [[https://github.com/c1-g/org-noter-plus-djvu/blob/master/README.org][Charlie Gordon's README]] ([[file:docs/README-djvu.org]] in this repo). - Prior to our work, the main new feature for PDF users was 2-D precise notes From 9a70e292177560eff8359d4141991c658dabe9a0 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Wed, 18 Jan 2023 14:48:59 -0800 Subject: [PATCH 295/453] Revert "Make the note title a part of completing read when taking a precise note." This reverts commit d61624986e82ac3141474d74088a178ea9d71c6f. --- org-noter-core.el | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index df716d6..9700ae0 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -1999,14 +1999,12 @@ Guiding principles for note generation (let ((display (org-element-property :raw-value (car note-cons)))) (push (cons display note-cons) collection)))) - ;; we're doing a precise note, so we're not going to insert into an existing note/heading. - ;; make the selected text available in completing read instead. - (if (and precise-info selected-text-p) - (cl-pushnew selected-text collection)) - (setq collection (nreverse collection) ;; prompt for title (unless no-Q's) title (cond + ;; for precise notes we want the selection to be the title + ((and precise-info selected-text-p) + selected-text) (org-noter-insert-note-no-questions default-title) (t From 366ede4054e0ce52faa874eb1d9ad6b068547dfe Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Wed, 18 Jan 2023 14:49:09 -0800 Subject: [PATCH 296/453] Revert "Use selection as title and body for precise notes." This reverts commit 385967f8fee4f47775c61e4a4c5398517a838965. --- org-noter-core.el | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 9700ae0..f6be230 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -2001,20 +2001,11 @@ Guiding principles for note generation (setq collection (nreverse collection) ;; prompt for title (unless no-Q's) - title (cond - ;; for precise notes we want the selection to be the title - ((and precise-info selected-text-p) - selected-text) - (org-noter-insert-note-no-questions - default-title) - (t - (completing-read "Note: " collection nil nil nil nil default-title))) - note-body (cond - ;; if we're making a precise note; note-body should be selected-text - ((and precise-info selected-text-p) - selected-text) - ((and selected-text-p (not (equal title short-selected-text))) - selected-text)) + title (if org-noter-insert-note-no-questions default-title + (completing-read "Note: " collection nil nil nil nil default-title)) + note-body (if (and selected-text-p + (not (equal title short-selected-text))) + selected-text) ;; is this an existing note? skip for precise notes existing-note (unless precise-info (cdr (assoc title collection)))) From d160a0ff07c23bcee50feae6ded3375206b2bea0 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Thu, 19 Jan 2023 11:45:32 -0800 Subject: [PATCH 297/453] Test custom behavior with org-noter--insert-heading advice. --- tests/org-noter-extra-tests.el | 55 ++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 tests/org-noter-extra-tests.el diff --git a/tests/org-noter-extra-tests.el b/tests/org-noter-extra-tests.el new file mode 100644 index 0000000..0296730 --- /dev/null +++ b/tests/org-noter-extra-tests.el @@ -0,0 +1,55 @@ + +(add-to-list 'load-path "modules") +(require 'org-noter-pdf) +(require 'with-simulated-input) +(require 'org-noter-test-utils) + + +(describe "org-noter very custom behavior" + (before-each + (create-org-noter-test-session) + ) + (describe "with advice" + (before-each + (setq org-noter-max-short-length 80000) + (define-advice org-noter--insert-heading (:after (level title &optional newlines-number location) add-full-body-quote) + "Advice for org-noter--insert-heading. + + When inserting a precise note insert the text of the note in the body as an org mode QUOTE block. + + =org-noter-max-short-length= should be set to a large value to short circuit the normal behavior: + =(setq org-noter-max-short-length 80000)=" + + ;; this tells us it's a precise note that's being invoked. + (if (consp location) + (insert (format "#+BEGIN_QUOTE\n%s\n#+END_QUOTE" title)))) + (create-org-noter-test-session) + ) + (after-each + (setq org-noter-max-short-length 80) + (advice-remove #'org-noter--insert-heading 'org-noter--insert-heading@add-full-body-quote) + ) + (it "should insert the highlighted text as an org-mode QUOTE when advice is enabled." + (with-mock-contents + mock-contents-simple-notes-file + '(lambda () + (org-noter-core-test-create-session) + ;; we're not specifying the note title + (with-simulated-input "RET" + (org-noter-insert-precise-note)) + (let* ((expected-heading (regexp-quote (format "** %s" (org-trim (replace-regexp-in-string "\n" " " (org-noter-test-get-selected-text nil))))))) + (expect (string-match "HARDCODED_HIGHLIGHT_LOCATION" (buffer-string)) :not :to-be nil) + (expect (string-match "BEGIN_QUOTE" (buffer-string)) :not :to-be nil) + (expect (string-match "END_QUOTE" (buffer-string)) :not :to-be nil) + (expect (string-match expected-heading (buffer-string)) :not :to-be nil)))))) + + + (describe "without advice" + (it "should revert back to standard title" + (with-mock-contents + mock-contents-simple-notes-file + '(lambda () + (org-noter-core-test-create-session) + (with-simulated-input "RET" + (org-noter-insert-precise-note)) + (expect (string-match "\\*\\* Notes for page" (buffer-string)) :not :to-be nil)))))) From 76c6b1ddb4b95d2988f28968d66e0b856dafbd46 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Fri, 20 Jan 2023 21:55:08 -0800 Subject: [PATCH 298/453] Initial work on the location testing. --- tests/org-noter-location-tests.el | 57 +++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 tests/org-noter-location-tests.el diff --git a/tests/org-noter-location-tests.el b/tests/org-noter-location-tests.el new file mode 100644 index 0000000..05122ad --- /dev/null +++ b/tests/org-noter-location-tests.el @@ -0,0 +1,57 @@ +(add-to-list 'load-path "modules") +(require 'org-noter-test-utils) + +(defvar mock-contents-simple-notes-file-with-locations + " +:PROPERTIES: +:ID: FAKE_1 +:END: +#+TITLE: Test book notes (simple) +* solove-nothing-to-hide +:PROPERTIES: +:NOTER_DOCUMENT: pubs/solove-nothing-to-hide.pdf +:END: +** Heading1 +:PROPERTIES: +:NOTER_PAGE: 40 +:END: +** Heading2 +:PROPERTIES: +:NOTER_PAGE: (41 0.09 . 0.16) +:HIGHLIGHT: #s(pdf-highlight 41 ((0.18050847457627117 0.09406231628453851 0.6957627118644067 0.12110523221634333))) +:END: +#+BEGIN_QUOTE +Test +#+END_QUOTE + +") + + + + +(describe "org-noter locations" + (describe "basic location parsing works" + (before-each + ) + + (describe "page locations" + (before-each + (create-org-noter-test-session) + ) + (it "can parse a page location" + (with-mock-contents + mock-contents-simple-notes-file-with-locations + '(lambda () + (org-noter-core-test-create-session) + (search-forward "Heading2") + (message "%s" (org-noter--get-containing-heading)) + (expect (org-noter--get-containing-heading) :not :to-be nil) + (expect (org-noter--parse-location-property (org-noter--get-containing-element)) :to-equal (read "(41 0.09 . 0.16)")) + ) + + )) + + ) + + ) + ) From 98a5ddd17fdebe104f84f916d30b3600ec99bb59 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Fri, 20 Jan 2023 21:56:25 -0800 Subject: [PATCH 299/453] Remove debug output. --- tests/org-noter-location-tests.el | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/org-noter-location-tests.el b/tests/org-noter-location-tests.el index 05122ad..2f10103 100644 --- a/tests/org-noter-location-tests.el +++ b/tests/org-noter-location-tests.el @@ -44,7 +44,6 @@ Test '(lambda () (org-noter-core-test-create-session) (search-forward "Heading2") - (message "%s" (org-noter--get-containing-heading)) (expect (org-noter--get-containing-heading) :not :to-be nil) (expect (org-noter--parse-location-property (org-noter--get-containing-element)) :to-equal (read "(41 0.09 . 0.16)")) ) From cd3c86cd801df5744f5022e7f47fc31d9e7a7540 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Fri, 20 Jan 2023 22:25:18 -0800 Subject: [PATCH 300/453] revert [49bc6ee], [f74263f] - remove all calls to org-collect-keywords This was killing performance, making note navigation take seconds to complete. The original commits, by c1g, were supposed to resolve issue #143. Unfortunately, the calls to `org-collect-keywords' make navigation in large PDFs intollerably slow. --- org-noter-core.el | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 79b2244..735024a 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -735,8 +735,7 @@ If nil, the session used will be `org-noter--session'." (org-with-wide-buffer (catch 'break (while t - (let ((document-property (or (org-entry-get nil org-noter-property-doc-file t) - (cadar (org-collect-keywords (list org-noter-property-doc-file)))))) + (let ((document-property (org-entry-get nil org-noter-property-doc-file t))) (when (string= (or (run-hook-with-args-until-success 'org-noter-parse-document-property-hook document-property) document-property) wanted-prop) @@ -1039,7 +1038,6 @@ properties, by a margin of NEWLINES-NUMBER." (defsubst org-noter--doc-file-property (headline) (let ((doc-prop (or (org-element-property (intern (concat ":" org-noter-property-doc-file)) headline) - (cadar (org-collect-keywords (list org-noter-property-doc-file))) (org-entry-get nil org-noter-property-doc-file t)))) (or (run-hook-with-args-until-success 'org-noter-parse-document-property-hook doc-prop) doc-prop))) @@ -1597,9 +1595,8 @@ relative to." (insert-file-contents notes-path) (catch 'break (while (re-search-forward (org-re-property org-noter-property-doc-file) nil t) - (when (file-equal-p (or (expand-file-name (match-string 3) (file-name-directory notes-path)) - (cadar (org-collect-keywords '(org-noter-property-doc-file)))) - document-path) + (when (file-equal-p (expand-file-name (match-string 3) (file-name-directory notes-path)) + document-path) ;; NOTE(nox): This notes file has the document we want! (throw 'break t))))))) @@ -1609,8 +1606,7 @@ relative to." (and (not (file-directory-p doc-prop)) (file-readable-p doc-prop))))) (defun org-noter--get-or-read-document-property (inherit-prop &optional force-new) - (let ((doc-prop (and (not force-new) (or (org-entry-get nil org-noter-property-doc-file inherit-prop) - (cadar (org-collect-keywords (list org-noter-property-doc-file))))))) + (let ((doc-prop (and (not force-new) (org-entry-get nil org-noter-property-doc-file inherit-prop)))) (setq doc-prop (or (run-hook-with-args-until-success 'org-noter-parse-document-property-hook doc-prop) doc-prop)) @@ -2243,9 +2239,7 @@ As such, it will only work when the notes window exists." (interactive) (org-noter--with-selected-notes-window "No notes window exists" - (if (string= (or (org-noter--get-or-read-document-property t) - (cadar (org-collect-keywords (list org-noter-property-doc-file)))) - + (if (string= (org-noter--get-or-read-document-property t) (org-noter--session-property-text session)) (let ((location (org-noter--parse-location-property (org-noter--get-containing-element)))) (if location From 031aa6b9fbb0f5871d2bae4466504a520a1bbd14 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Fri, 20 Jan 2023 22:29:23 -0800 Subject: [PATCH 301/453] documentation only: sync..-page-or-chapter these navigation commands are meant to only hit the page notes, skipping over precise notes. Currently, the forward navigation fails by getting stuck on precise notes. --- org-noter-core.el | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 735024a..c43cd17 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -2154,8 +2154,8 @@ This is like `org-noter-insert-precise-note', except it will toggle `org-noter-i nil ,match-first org-noter--note-search-no-recurse))) (defun org-noter-sync-prev-page-or-chapter () - "Show previous page or chapter that has notes, in relation to the current page or chapter. -This will force the notes window to popup." + "Show previous page or chapter that has notes, in relation to the current page or +chapter, skipping precise notes. This will force the notes window to popup." (interactive) (org-noter--with-valid-session (let ((this-location (org-noter--doc-approx-location 0)) @@ -2186,8 +2186,8 @@ This will force the notes window to popup." (org-noter--doc-location-change-handler)))) (defun org-noter-sync-next-page-or-chapter () - "Show next page or chapter that has notes, in relation to the current page or chapter. -This will force the notes window to popup." + "Show next page or chapter that has notes, in relation to the current page or +chapter, skipping precise notes. This will force the notes window to popup." (interactive) (org-noter--with-valid-session (let ((this-location (org-noter--doc-approx-location most-positive-fixnum)) From 56a45e0ac65d11fc540228e98b45dc4982084545 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 22 Jan 2023 13:38:11 -0800 Subject: [PATCH 302/453] fix sync-next-page-or-chapter navigation GS injects `most-postitive-fixnum' as the precise vertical location of a page when searching forward, so -approx-location-cons functions MUST be able to handle (page . v) type locations. This was broken at Ahmed Shariff's 3rd commit. Also, in "next" navigation, org-element-map can stop as soon as it finds a match, so the "nil" in the call to macro `org-noter--map-ignore-headings-with-doc-file' works with "t" and avoids extra iterations of org-element-map. --- modules/org-noter-pdf.el | 9 +++++---- org-noter-core.el | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 15c84d0..e6c1e75 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -50,11 +50,12 @@ Otherwise returns nil" (defun org-noter-pdf-approx-location-cons (mode &optional precise-info _force-new-ref) "Returns (page . 0) except when creating a precise-note, -where (page v-pos . h-pos) is returned" +where (pabe v-pos) or (page v-pos . h-pos) is returned" (when (memq mode '(doc-view-mode pdf-view-mode)) - (cons (image-mode-window-get 'page) (if (and (consp precise-info) - (numberp (car precise-info)) - (numberp (cdr precise-info))) + (cons (image-mode-window-get 'page) (if (or (numberp precise-info) + (and (consp precise-info) + (numberp (car precise-info)) + (numberp (cdr precise-info)))) precise-info 0)))) (add-to-list 'org-noter--doc-approx-location-hook #'org-noter-pdf-approx-location-cons) diff --git a/org-noter-core.el b/org-noter-core.el index c43cd17..d7cf09b 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -2195,7 +2195,7 @@ chapter, skipping precise notes. This will force the notes window to popup." target-location) (org-noter--map-ignore-headings-with-doc-file - contents nil + contents t (when (and (org-noter--compare-locations '> location this-location) (org-noter--compare-locations '< location target-location)) (setq target-location location))) From 0f507e063347da22e66bf9e0304bb6db8fce94dd Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 22 Jan 2023 13:51:13 -0800 Subject: [PATCH 303/453] update README.org (performance issue solved) --- README.org | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/README.org b/README.org index 7224efb..d40c20b 100644 --- a/README.org +++ b/README.org @@ -92,17 +92,13 @@ May require altering the org-noter-X--pretty-print-location-hook(s). - 2. Profile org-noter -- runs slow on large books (500+ pages) - org-element-map is taking 85% of the CPU. Org 9.6 should have a faster - way that Charlie Gordon explored. + 2. Bind M- to precise-note, no-questions. - 3. Bind M- to precise-note, no-questions. + 3. Make background of arrow transparent (see org-noter--show-arrow) - 4. Make background of arrow transparent (see org-noter--show-arrow) + 4. Dedicated insert-selected-text-into-page-note - 5. Dedicated insert-selected-text-into-page-note - - 6. Internationalize precise notes to handle right-to-left languages. + 5. Internationalize precise notes to handle right-to-left languages. ** Bugs *** to fix 1. Sometimes (when?) M-p doesn't pick up the containing note-at-point right From 591156ec1111564c33edd5a4f4f2a9f0ebd48855 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Tue, 24 Jan 2023 18:05:43 -0800 Subject: [PATCH 304/453] Revert "documentation only: sync..-page-or-chapter" This reverts commit 031aa6b9fbb0f5871d2bae4466504a520a1bbd14. GS's original documentation describes the functions properly. --- org-noter-core.el | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index d7cf09b..506e7ad 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -2154,8 +2154,8 @@ This is like `org-noter-insert-precise-note', except it will toggle `org-noter-i nil ,match-first org-noter--note-search-no-recurse))) (defun org-noter-sync-prev-page-or-chapter () - "Show previous page or chapter that has notes, in relation to the current page or -chapter, skipping precise notes. This will force the notes window to popup." + "Show previous page or chapter that has notes, in relation to the current page or chapter. +This will force the notes window to popup." (interactive) (org-noter--with-valid-session (let ((this-location (org-noter--doc-approx-location 0)) @@ -2186,8 +2186,8 @@ This will force the notes window to popup." (org-noter--doc-location-change-handler)))) (defun org-noter-sync-next-page-or-chapter () - "Show next page or chapter that has notes, in relation to the current page or -chapter, skipping precise notes. This will force the notes window to popup." + "Show next page or chapter that has notes, in relation to the current page or chapter. +This will force the notes window to popup." (interactive) (org-noter--with-valid-session (let ((this-location (org-noter--doc-approx-location most-positive-fixnum)) From ed7bd32a12f73e9b71f188d25dc5dbd204ebc2d9 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Tue, 24 Jan 2023 18:20:28 -0800 Subject: [PATCH 305/453] bugfix: vertical doc/notes layout was not working --- org-noter-core.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 506e7ad..1a7274b 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -911,7 +911,7 @@ properties, by a margin of NEWLINES-NUMBER." (if horizontal (split-window-right (ceiling (* (car (org-noter--session-doc-split-fraction session)) (window-total-width)))) - (split-window-below (ceiling (* (cadr (org-noter--session-doc-split-fraction session)) + (split-window-below (ceiling (* (cdr (org-noter--session-doc-split-fraction session)) (window-total-height))))))))) (set-window-buffer notes-window notes-buffer)) @@ -956,7 +956,7 @@ properties, by a margin of NEWLINES-NUMBER." (enlarge-window (- (ceiling (* (- 1 (car (org-noter--session-doc-split-fraction session))) (frame-width))) (window-total-width)) t) - (enlarge-window (- (ceiling (* (- 1 (cadr (org-noter--session-doc-split-fraction session))) + (enlarge-window (- (ceiling (* (- 1 (cdr (org-noter--session-doc-split-fraction session))) (frame-height))) (window-total-height))))) From 8e32e696e18a4e84ea92ccd8ac6c21f1107d0d3e Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Tue, 24 Jan 2023 18:41:22 -0800 Subject: [PATCH 306/453] make fore/back-ground colors of tooltip arrow customizable Original "orange red" over "white" is default. I prefer "black" over "cyan" as it shows up better, especially when precise notes can be anywhere on the page. --- org-noter-core.el | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index 1a7274b..eb1d779 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -230,6 +230,16 @@ left." :group 'org-noter :type 'number) +(defcustom org-noter-arrow-foreground-color "orange red" + "Default color of the tooltip arrow" + :group 'org-noter + :type 'string) + +(defcustom org-noter-arrow-background-color "white" + "Default background color of the tooltip arrow" + :group 'org-noter + :type 'string) + (defcustom org-noter-doc-property-in-notes nil "If non-nil, every new note will have the document property too. This makes moving notes out of the root heading easier." @@ -1204,11 +1214,11 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." "\u2192" ;; right arrow 'display '(height 2) 'face `(:foreground - "orange red" + ,org-noter-arrow-foreground-color :background ,(if (bound-and-true-p pdf-view-midnight-minor-mode) (cdr pdf-view-midnight-colors) - "white")))) + org-noter-arrow-background-color)))) dx dy)) (setq org-noter--arrow-location nil)))) From d7c4a5c0e5f803a635929a5e56721ba735a2868f Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Tue, 24 Jan 2023 19:10:35 -0800 Subject: [PATCH 307/453] remove duplicated defcustoms --- org-noter-core.el | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/org-noter-core.el b/org-noter-core.el index eb1d779..2ee19ed 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -277,21 +277,6 @@ QUOTE-block." :group 'org-noter :type 'integer) -(defcustom org-noter-highlight-selected-text nil - "If non-nil, highlight selected-text when creating notes. This -variable is temporarily toggled by prefixing the insertion -command with any non-nil prefix such as \\[universal-argument]." - :group 'org-noter - :type 'boolean) - -(defcustom org-noter-max-short-selected-text-length 80 - "Maximum length of a short text selection. Short text selections -may be used as note title. When they are quoted in the note, -they are quoted as ``short-selected-text'' rather than inside a -QUOTE-block." - :group 'org-noter - :type 'integer) - (defcustom org-noter-find-additional-notes-functions nil "Functions that when given a document file path as argument, give out an org note file path. From b45b0d7ee35faf7667b7c9c9941a69e047f6194e Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Tue, 24 Jan 2023 19:46:50 -0800 Subject: [PATCH 308/453] update README.org with proposed defcustom organization --- README.org | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/README.org b/README.org index d40c20b..32274cd 100644 --- a/README.org +++ b/README.org @@ -1,5 +1,5 @@ +#+STARTUP: hidestars * ORG-NOTER (fork of a fork) - ** If you are new to *ORG-NOTER* Please first look at [[https://github.com/weirdNox/org-noter/blob/master/README.org][Gonçalo Santos's README]] ([[file:docs/README-orig.org]] in this repo). @@ -118,3 +118,47 @@ - =org-noter--check-location-property= found bug: [[file:org-noter-core.el:1023]] change test from integerp to numberp - =org-noter--get-containing-property-drawer= +** Custom variables + Presently, the custom variables listed under =customize-group org-noter= is a + flat list. I would like to group them into logical categories. + +*** start-stop + - org-noter-supported-modes '(doc-view-mode pdf-view-mode nov-mode djvu-read-mode) + - org-noter-auto-save-last-location nil + - org-noter-default-notes-file-names '("Notes.org") + - org-noter-notes-search-path '("~/Documents") + - org-noter-notes-window-behavior '(start scroll) + - org-noter-suggest-from-attachments t + - org-noter-find-additional-notes-functions nil + - org-noter-kill-frame-at-session-end t + +*** layout + - org-noter-notes-window-location 'horizontal-split + - org-noter-doc-split-fraction '(0.5 . 0.5) + - org-noter-always-create-frame t + - org-noter-disable-narrowing nil + - org-noter-use-indirect-buffer t + - org-noter-swap-window nil + - org-noter-hide-other t + +*** note-insertion + - org-noter-default-heading-title "Notes for page $p$" + - org-noter-separate-notes-from-heading nil + - org-noter-insert-selected-text-inside-note t + - org-noter-highlight-selected-text nil + - org-noter-max-short-selected-text-length 80 + - org-noter-insert-heading-hook nil + - org-noter-insert-note-no-questions nil + - org-noter-doc-property-in-notes nil + +*** navigation-display + - org-noter-arrow-delay 0.2 + - org-noter-arrow-horizontal-offset -0.02 + - org-noter-arrow-foreground-color "orange red" + - org-noter-arrow-background-color "white" + - org-noter-closest-tipping-point 0.3 + +*** other + - org-noter-property-doc-file "NOTER_DOCUMENT" + - org-noter-property-note-location "NOTER_PAGE" + - org-noter-prefer-root-as-file-level nil # used in org-noter--parse-root From bdfed51ee139e42d9bd3a341b5cfb92f14ec5eb5 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Tue, 24 Jan 2023 23:19:52 -0800 Subject: [PATCH 309/453] yet more additions to README.org (customizations) --- README.org | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/README.org b/README.org index 32274cd..87be808 100644 --- a/README.org +++ b/README.org @@ -86,6 +86,57 @@ | body | selected-text (not title) | selected-text (not title) | selected-text (not title) | |---------------------+---------------------------+---------------------------+---------------------------| | highlight selection | user setting/toggle | user setting/toggle | user setting/toggle | +** Customizations + Out of respect for the existing user base of =org-noter=, almost all of the + user-interface features of this fork remain the same those of as the original + =org-noter=. However, users may be interested in our variations on the theme + of Gonçalos Santos. +*** Peter's mods + In order of frequency, I use precise notes the most, standard notes next and + no-questions notes the least, so I have bound keys to reflect those + preferences with =i= for precise notes, =TAB= for standard notes; for + no-questions (no-prompt) notes, =I= is for precise notes and =M-i= for + standard notes.: + #+begin_src elisp + (with-eval-after-load 'org-noter + (define-key org-noter-doc-mode-map (kbd "i") 'org-noter-insert-precise-note) + (define-key org-noter-doc-mode-map (kbd "C-i") 'org-noter-insert-note) + (define-key org-noter-doc-mode-map (kbd "I") 'org-noter-insert-precise-note-toggle-no-questions) + (define-key org-noter-doc-mode-map (kbd "M-i") 'org-noter-insert-note-toggle-no-questions)) + #+end_src + + For navigation, I use =..-sync-..-note= more than =..-sync-..-page-..=, so I + bound the =note= commands to the easier-to-type =M-[p.n]= bindings and the + less-used =page-..= commands to the harder-to-type =C-M-[p.n]= bindings. + #+begin_src elisp + (with-eval-after-load 'org-noter + (define-key org-noter-doc-mode-map (kbd "M-p") 'org-noter-sync-prev-note) + (define-key org-noter-doc-mode-map (kbd "M-.") 'org-noter-sync-current-note) + (define-key org-noter-doc-mode-map (kbd "M-n") 'org-noter-sync-next-note) + (define-key org-noter-doc-mode-map (kbd "C-M-p") 'org-noter-sync-prev-page-or-chapter) + (define-key org-noter-doc-mode-map (kbd "C-M-.") 'org-noter-sync-current-page-or-chapter) + (define-key org-noter-doc-mode-map (kbd "C-M-n") 'org-noter-sync-next-page-or-chapter) + + (define-key org-noter-notes-mode-map (kbd "M-p") 'org-noter-sync-prev-note) + (define-key org-noter-notes-mode-map (kbd "M-.") 'org-noter-sync-current-note) + (define-key org-noter-notes-mode-map (kbd "M-n") 'org-noter-sync-next-note) + (define-key org-noter-notes-mode-map (kbd "C-M-p") 'org-noter-sync-prev-page-or-chapter) + (define-key org-noter-notes-mode-map (kbd "C-M-.") 'org-noter-sync-current-page-or-chapter) + (define-key org-noter-notes-mode-map (kbd "C-M-n") 'org-noter-sync-next-page-or-chapter)) + #+end_src + + In the original code, the tooltip arrow on PDFs is *Orange-Red on White*, + which works fine when the arrow is always on the left side of the page. I + found that with the 2D precise notes introduced by Ahmed Shariff, I sometime + had trouble locating the arrow as I navigated through my notes. The *Black + on Cyan* color-scheme that I use is more jarring, hence easier to locate. + #+begin_src elisp + (with-eval-after-load 'org-noter + (setq org-noter-arrow-background-color "cyan" + org-noter-arrow-foreground-color "black")) + #+end_src + +*** Dmitry's mods ** Features wishlist 1. Use pdf-view-current-pagelabel to use the page label instead of page in default titles @@ -104,7 +155,11 @@ 1. Sometimes (when?) M-p doesn't pick up the containing note-at-point right away (or at all), requiring user to manually type in the (existing) title *** fixed - 1. Navigating up from a nested precise note lands in the prior note at the + 1. vertically stacked doc/notes layout fixed + + 2. =org-noter-sync-next-page-or-chapter= navigation fixed + + 3. Navigating up from a nested precise note lands in the prior note at the next level up (eg level 3 -> level 2). page notes behave properly. [file:org-noter-core.el:2179] @@ -157,6 +212,8 @@ - org-noter-arrow-foreground-color "orange red" - org-noter-arrow-background-color "white" - org-noter-closest-tipping-point 0.3 + - org-noter-no-notes-exist-face + - org-noter-notes-exist-face *** other - org-noter-property-doc-file "NOTER_DOCUMENT" From cd3071d5fcb30a6af409359095222014d207d35e Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Tue, 24 Jan 2023 23:20:29 -0800 Subject: [PATCH 310/453] remove 'ht' from Cask dependencies --- Cask | 1 - 1 file changed, 1 deletion(-) diff --git a/Cask b/Cask index 371c8b8..19b869b 100644 --- a/Cask +++ b/Cask @@ -3,7 +3,6 @@ (depends-on "buttercup") (depends-on "pdf-tools") -(depends-on "ht") (depends-on "with-simulated-input") From 601f08e7b88cf3e4088e202ad8c24110c82bbfd5 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sat, 28 Jan 2023 17:10:41 -0800 Subject: [PATCH 311/453] reformat (page H V) default title to use pagelabel etc. Also converts H and V to percentages and reduces precision for readability. --- modules/org-noter-djvu.el | 1 + modules/org-noter-nov.el | 2 +- modules/org-noter-pdf.el | 23 +++++++++++++++++++++++ org-noter-core.el | 15 ++++++++++++++- 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/modules/org-noter-djvu.el b/modules/org-noter-djvu.el index f15b325..7beee2f 100644 --- a/modules/org-noter-djvu.el +++ b/modules/org-noter-djvu.el @@ -33,6 +33,7 @@ location))))) (add-to-list 'org-noter--pretty-print-location-hook #'org-noter-djvu--pretty-print-location) +(add-to-list 'org-noter--pretty-print-location-for-title-hook #'org-noter-djvu--pretty-print-location) (defun org-noter-djvu-approx-location-cons (mode &optional precise-info _force-new-ref) (when (eq mode 'djvu-read-mode) diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el index 8d3181a..87aba6a 100644 --- a/modules/org-noter-nov.el +++ b/modules/org-noter-nov.el @@ -61,7 +61,7 @@ location))))) (add-to-list 'org-noter--pretty-print-location-hook #'org-noter-nov--pretty-print-location) - +(add-to-list 'org-noter--pretty-print-location-for-title-hook #'org-noter-nov--pretty-print-location) (defun org-noter-nov--get-precise-info (mode window) (when (eq mode 'nov-mode) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index e6c1e75..5ab2fee 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -85,6 +85,7 @@ where (pabe v-pos) or (page v-pos . h-pos) is returned" (add-to-list 'org-noter-set-up-document-hook #'org-noter-doc-view-setup-handler) (defun org-noter-pdf--pretty-print-location (location) + "Full precision location for property drawers" (org-noter--with-valid-session (when (memq (org-noter--session-doc-mode session) '(doc-view-mode pdf-view-mode)) (format "%s" (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 0)) @@ -93,6 +94,28 @@ where (pabe v-pos) or (page v-pos . h-pos) is returned" (add-to-list 'org-noter--pretty-print-location-hook #'org-noter-pdf--pretty-print-location) +(defun org-noter-pdf--pretty-print-location-for-title (location) + "Human readable location with page label and v/h percentages. Doc-view falls back to original pp function." + (org-noter--with-valid-session + (let ((mode (org-noter--session-doc-mode session)) + (vpos (org-noter--get-location-top location)) + (hpos (org-noter--get-location-left location)) + (vtxt "") (htxt "") + pagelabel) + (cond ((eq mode 'pdf-view-mode) ; for default title, reference pagelabel instead of page + (if (> hpos 0) + (setq htxt (format " H: %d%%" (round (* 100 hpos))))) + (if (or (> vpos 0) (> hpos 0)) + (setq vtxt (format " V: %d%%" (round (* 100 vpos))))) + (save-excursion + (select-window (org-noter--get-doc-window)) + (setq pagelabel (pdf-view-current-pagelabel))) + (format "%s%s%s" pagelabel vtxt htxt)) + ((eq mode 'doc-view-mode) ; fall back to original pp for doc-mode + (org-noter-pdf--pretty-print-location location)))))) + +(add-to-list 'org-noter--pretty-print-location-for-title-hook #'org-noter-pdf--pretty-print-location-for-title) + (defun org-noter-pdf--get-precise-info (mode window) (when (eq mode 'pdf-view-mode) (let (v-position h-position) diff --git a/org-noter-core.el b/org-noter-core.el index 2ee19ed..9beacc5 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -389,6 +389,11 @@ major modes uses the `buffer-file-name' variable." :group 'org-noter-module-hooks :type 'hook) +(defcustom org-noter--pretty-print-location-for-title-hook nil + "TODO" + :group 'org-noter-module-hooks + :type 'hook) + (defcustom org-noter--convert-to-location-cons-hook nil "TODO" :group 'org-noter-module-hooks @@ -1064,10 +1069,18 @@ properties, by a margin of NEWLINES-NUMBER." ((integerp value) (cons value 0)))))))) (defun org-noter--pretty-print-location (location) + "Original pretty-print for property drawer." (org-noter--with-valid-session (run-hook-with-args-until-success 'org-noter--pretty-print-location-hook location))) +(defun org-noter--pretty-print-location-for-title (location) + "Pretty-print for titles. Compared to the original +functions/hook, this one may present more human-readable text" + (org-noter--with-valid-session + (run-hook-with-args-until-success + 'org-noter--pretty-print-location-for-title-hook location))) + ;; TODO: Documentation (defun org-noter--get-containing-element (&optional include-root) (run-hook-with-args-until-success 'org-noter--get-containing-element-hook include-root)) @@ -1995,7 +2008,7 @@ Guiding principles for note generation collection title note-body existing-note (default-title (or short-selected-text (replace-regexp-in-string (regexp-quote "$p$") - (org-noter--pretty-print-location location) + (org-noter--pretty-print-location-for-title location) org-noter-default-heading-title))) (empty-lines-number (if org-noter-separate-notes-from-heading 2 1))) From 23deec9df8c48353e1a533059de100ebdabaf474 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sat, 28 Jan 2023 21:41:35 -0800 Subject: [PATCH 312/453] update README: Features wishlist updated w/ new features list --- README.org | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/README.org b/README.org index 87be808..f047fd2 100644 --- a/README.org +++ b/README.org @@ -137,19 +137,32 @@ #+end_src *** Dmitry's mods -** Features wishlist +** Features +*** New 1. Use pdf-view-current-pagelabel to use the page label instead of page in default titles - May require altering the org-noter-X--pretty-print-location-hook(s). + new function/hook =...-pretty-print-location-for-title= - 2. Bind M- to precise-note, no-questions. + 2. Customizable tooltip arrow colors + - =...-arrow-foreground-color= + - =...-arrow-background-color= - 3. Make background of arrow transparent (see org-noter--show-arrow) + 3. Text-selection higlighting: customizable default behavior, toggle + =...-highlight-selected-text= with =C-u= prefix on note-insertion + commands. - 4. Dedicated insert-selected-text-into-page-note + 4. Rudimentary support for multicolumn PDFs with inheritable =NUM_COLUMNS= + property. See =...-pdf-convert-to-location-cons= - 5. Internationalize precise notes to handle right-to-left languages. +*** Wishlist + 1. Bind M- to precise-note, no-questions. + + 2. Make background of arrow transparent (see org-noter--show-arrow) + + 3. Dedicated insert-selected-text-into-page-note + + 4. Internationalize precise notes to handle right-to-left languages. ** Bugs *** to fix 1. Sometimes (when?) M-p doesn't pick up the containing note-at-point right From dd8ce956686c37f6348697cf63a7d5fb47cba716 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 29 Jan 2023 17:48:21 -0800 Subject: [PATCH 313/453] update documentation on -pdf-convert-to-lcoation-cons for multicol Division by NUM_COLUMNS is unnecessary, since the vertical position can take on any positive value. Comparison of positions is by page and then vertical position, so this saves a division operation while preserving functionality. --- modules/org-noter-pdf.el | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 5ab2fee..291f720 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -384,9 +384,10 @@ where (pabe v-pos) or (page v-pos . h-pos) is returned" (pdf-annot-add-highlight-markup-annotation (pdf-view-active-region)))) (defun org-noter-pdf-convert-to-location-cons (location) - "converts (page v . h) precise locations so that v represents the -fractional distance through the page along column. Output is nil -for standard notes and (page v') for precise notes" + "converts (page v . h) precise locations to (page v') such that +v' represents the fractional distance through the page along +columns, so it takes values between 0 and NUM_COLUMNS. Output is +nil for standard notes and (page v') for precise notes" (if-let* ((_ (and (consp location) (consp (cdr location)))) (bb (current-buffer)) ; debugging code - we are in the doc window, ; but need to be in the notes window for next @@ -395,7 +396,7 @@ for standard notes and (page v') for precise notes" (page (car location)) (v-pos (cadr location)) (h-pos (cddr location))) - (cons page (+ (/ v-pos ncol) (/ (float (floor (* h-pos ncol))) ncol))))) + (cons page (+ v-pos (float (floor (* h-pos ncol))))))) (add-to-list 'org-noter--convert-to-location-cons-hook #'org-noter-pdf-convert-to-location-cons) From 7e9751a3e1a564da422e9eaa8ba0550d1e1b3659 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 29 Jan 2023 17:50:50 -0800 Subject: [PATCH 314/453] README.org: add link of possible way forward w/ tooltip transparency --- README.org | 1 + 1 file changed, 1 insertion(+) diff --git a/README.org b/README.org index f047fd2..8f3ff72 100644 --- a/README.org +++ b/README.org @@ -159,6 +159,7 @@ 1. Bind M- to precise-note, no-questions. 2. Make background of arrow transparent (see org-noter--show-arrow) + maybe https://emacs.stackexchange.com/questions/45588/how-to-make-tooltip-background-transparent 3. Dedicated insert-selected-text-into-page-note From af20a66d5dc6d35fd812afae08c571edb9898558 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 29 Jan 2023 18:17:33 -0800 Subject: [PATCH 315/453] README.org: document some multicolumn issues --- README.org | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.org b/README.org index 8f3ff72..3f05c95 100644 --- a/README.org +++ b/README.org @@ -168,6 +168,15 @@ *** to fix 1. Sometimes (when?) M-p doesn't pick up the containing note-at-point right away (or at all), requiring user to manually type in the (existing) title + + 2. With NUM_COLUMNS > 1, point in notes document doesn't land in the correct + place + + 3. With NUM_COLUMNS > 1, columns don't necessarily start at horizontal + positions k/NUM_COLUMNS for k \in {1,..,NUM_COLUMNS}. We need to write a + user interface that builds a list of horizontal fractions to delimit the + columns. + *** fixed 1. vertically stacked doc/notes layout fixed From f8d37805a36c46b0ad172b3c04f2f76c7630b3a7 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 29 Jan 2023 22:37:37 -0800 Subject: [PATCH 316/453] prepare README.new.org for release --- README.new.org | 199 +++++++++++++++++++++++++++++++++++++++++++++++++ README.org | 2 +- 2 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 README.new.org diff --git a/README.new.org b/README.new.org new file mode 100644 index 0000000..f6858d3 --- /dev/null +++ b/README.new.org @@ -0,0 +1,199 @@ +* Org-noter: an interleaving/note-taking package for documents + + ~Org-noter~, by [[https://github.com/weirdNox][Gonçalo Santos]], was inspired by the now-orphaned ~Interleave~ + package, by [[https://github.com/rudolfochrist][Sebastian Christ]]. In Sebastian's words (with minor edits): + + #+begin_quote + In the past, textbooks were sometimes published as interleaved editions. That + meant, each page was followed by a blank page and ambitious students/scholars + had the ability to take their notes directly in their copy of the + textbook. Newton and Kant were prominent representatives of this + technique. [find reference] + + Nowadays, textbooks (or lecture materials) come in PDF format. Although almost + every PDF Reader has the ability to add some notes to the PDF itself, it is + not as powerful as it could be. This is what this Emacs minor mode tries to + accomplish. It presents your PDF side by side with an Org Mode buffer of your + notes, narrowed down to just those passages that are relevant to this + particular page in the document viewer. + #+end_quote + + Org-noter's purpose is to let you create notes that are kept in sync when you + scroll through the document, but that are external to it - the notes + themselves live in an Org-mode file. As such, this leverages the power of + [[http://orgmode.org/][Org-mode]] (the notes may have outlines, latex fragments, babel, etc...) while + acting like notes that are made /inside/ the document. Also, taking notes is + very simple: just press =i= and annotate away! + + Org-noter is compatible with [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Document-View.html][DocView]], [[https://github.com/politza/pdf-tools][PDF Tools]], [[https://github.com/wasamasa/nov.el][Nov.el]], and + [[DJVU-read][DJVU-image-mode]]. These modes make it possible to annotate *PDF*, *EPUB*, + *Microsoft Office*, DVI, PS, OpenDocument, and DJVU formatted files. Note + that PDF support is our prime goal. Other format have been supported by other + contributors, but we need code contributions from users of other formats to + maintain/progress usability with those formats. + + +** Features +*** original org-noter (up to 2019) +- Easy to use annotation interface :: Just press =i= in the + document buffer and annotate away! + +- Keep your notes in sync with the document :: When you take a note by pressing + =i=, it saves the location where you took it so it is able to show you + the notes while you scroll, automatically! + +- Easy navigation :: You may navigate your document as usual, seeing the notes buffer scroll and show you + the respective notes; however, you may also navigate by notes, only seeing annotated pages/chapters. + +- Isolated interface :: Each session has its own frame and the document and notes buffers are indirect + buffers to the original ones. Killing any of these things will terminate your annotation session. + +- Simultaneous sessions :: You may open as many annotation sessions as you wish, at the same time! The + root heading of each document will be made read-only to prevent you from deleting a heading from an + open session. + +- Resume annotating from where you left :: When ~org-noter-auto-save-last-location~ is non-nil, it will + save the last location you visited and pick it up when you start another session! You may also set + this per-document, [[#custom][read more here]]. + +- Keep your notes organized :: You may arrange your notes however you want! You can create groups and + nest notes (and even nest documents inside other documents)! + +- Annotate ~org-attach~'ed files :: If you have any attached files, it will let you choose one as + the document to annotate. + +*** new + - 2D precise notes :: ([[https://github.com/ahmed-shariff/org-noter][Ahmed Shariff]]) Location tooltip appears at start of + selected text or point of click. + + - Multicolumn ordering of precise notes :: (Rudimentary) With the + ~NUM_COLUMNS~ property, you can specify the number of columns in a + multicolumn document (or pages thereof). Precise notes are ordered + vertically within columns. + + - Highlighting of selected text :: Default behavior (on/off) is + user-customizable. Non-default behavior is activated with a =C-u= prefix + to the note insertion command. + + - Customizable tooltip arrow colors :: ~org-noter-arrow-foreground-color~ (default + orange-red) and ~org-noter-arrow-foreground-color~ (default white) are + both user customizable. + +[TODO: make/link to screencast] + +** Installation + + 1. clone this repo to a local directory + 2. add to your init file: + #+begin_src elisp + (add-to-list 'load-path "/modules") + (add-to-list 'load-path "") + ;; then choose one or more of the following modules + ;; (require 'org-noter-pdf) + ;; (require 'org-noter-nov) + ;; (require 'org-noter-djvu) + #+end_src + +** Usage + +If you want to give it a try without much trouble: +- Just have an Org file where you want the notes to go +- Create a root heading to hold the notes +- Run =M-x org-noter= inside! + On the first run, it will ask you for the path of the document and save it in a + property. By default, it will also let you annotate an attached file [[https://orgmode.org/manual/Attachments.html][(org-attach documentation)]]. + +This will open a new dedicated frame where you can use [[#keys][the keybindings described here]]. + +More generally, there are two modes of operation. You may run +=M-x org-noter=: +- Inside a heading in an Org notes file :: This will associate that heading with a + document and open a session with it. This mode is the one described in the example + above. + +- In a document :: Run =M-x org-noter= when viewing a + document (eg. PDF, epub...). + + This will try to find the respective notes file automatically. It will + search in all parent folders and some specific folders set by you. See + ~org-noter-default-notes-file-names~ and ~org-noter-notes-search-path~ + for more information. + +There is, of course, more information in the docstrings of each command. + +** TODO Customization +There are two kinds of customizations you may do: +1. Global settings, affecting every session +2. Document-specific settings, which override the global settings + +The *global settings* are changed with either the [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Easy-Customization.html#Easy-Customization][customization interface from Emacs]] or directly in your +init file. To find which settings are available, you may use the customization interface or you may just +read =org-noter.el=. + +The best way to set *document-specific settings* is by using the utility commands provided (list below). +In order to use them, you need an open session. The commands may change the settings for that session +only (not surviving restarts), or for every session with that document. + +*List of utility commands* (check the docstrings to learn how to make the changes +permanent, or revert to the default): +- You may set a start location for this document, by using ~org-noter-set-start-location~. + +- To automatically resume from where you left, use ~org-noter-set-auto-save-last-location~. + +- With ~org-noter-set-notes-window-behavior~, you may change /when/ the notes window pops + up. + +- With ~org-noter-set-notes-window-location~, you may change /where/ the notes window pops + up. + +- ~org-noter-set-doc-split-fraction~ will ask you for the fraction of the frame that the document window + occupies when split. + +- ~org-noter-set-hide-other~ will toggle whether or not it should hide headings not + related to the executed action. + +- ~org-noter-set-closest-tipping-point~ will set the closest note tipping point. Also + check the docstring of the variable ~org-noter-closest-tipping-point~ in order to better + understand the tipping point. + +** TODO Keybindings and commands +:PROPERTIES: +:CUSTOM_ID: keys +:END: +| Key | Description | Where? | +| =i= | Insert note | Document buffer | +| =M-i= | Insert precise note | Document buffer | +| =q= | Kill session | Document buffer | +| =M-p= | Sync previous page/chapter | Document and notes buffer | +| =M-.= | Sync current page/chapter | Document and notes buffer | +| =M-n= | Sync next page/chapter | Document and notes buffer | +| =C-M-p= | Sync previous notes | Document and notes buffer | +| =C-M-.= | Sync selected notes | Document and notes buffer | +| =C-M-n= | Sync next notes | Document and notes buffer | + +You can use the usual keybindings to navigate the document +(=n=, =p=, =SPC=, ...). + +There are two types of sync commands: +- To sync a page/chapter, means it will find the [previous|current|next] page/chapter and + show the corresponding notes for that page/chapter; as such, it will always pop up the + notes buffer, if it does not exist. This type of command is in relation to the current + page/chapter in the document. + +- To sync the notes, means it will find the [previous|current|next] notes and go to the + corresponding location on the document. So, you need to have the notes window open, + because this type of commands is in relation to the selected notes (ie, where the cursor + is). + +When using PDF Tools, the command ~org-noter-create-skeleton~ imports the PDF outline or +annotations (or both!) as notes, and it may be used, for example, as a starting point. + +You may also want to check the docstrings of the functions associated with the +keybindings, because there is some extra functionality in some. + +** Acknowledgments + - [[https://github.com/rudolfochrist][Sebastian Christ]] :: Author of the ~interleave~ package, inspiration for ~org-noter~ + - [[https://github.com/weirdNox][Gonçalo Santos]] :: Author of ~org-noter~ + - [[https://github.com/ahmed-shariff/org-noter][Ahmed Shariff]] :: Contributor of 2-D precise notes + - [[https://github.com/c1-g/org-noter-plus-djvu][Charlie Gordon]] :: Contributor of DJVU support and document-type modularization + diff --git a/README.org b/README.org index 3f05c95..3c3c072 100644 --- a/README.org +++ b/README.org @@ -16,7 +16,7 @@ (introduced by *Ahmed Shariff*, which can be inserted in two ways: 1. "Select-precise": selecting text in the pdf, followed by =M-i=. *c1-g* changed the position format to use the entire list returned by =edges=. - This breaks the other way of inserting precise notes. On *dmitrym*'s + This breaks the other way of inserting precise notes. On *dmitrym0*'s fork, select-precise notes auto-fill the note title with the selected text. This has deleterious side effects for other note insertion methods. 2. "Click-precise": =M-i=, followed by clicking a location on the page. From 66c88ac27e87f5685dd5a6d4ef18414cc25119ff Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sun, 29 Jan 2023 23:52:47 -0800 Subject: [PATCH 317/453] bugfix: -pretty-print-location-for-title was leaving focus in doc instead of sending it to the notes window for note insertion --- modules/org-noter-pdf.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 291f720..4075eb7 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -107,9 +107,9 @@ where (pabe v-pos) or (page v-pos . h-pos) is returned" (setq htxt (format " H: %d%%" (round (* 100 hpos))))) (if (or (> vpos 0) (> hpos 0)) (setq vtxt (format " V: %d%%" (round (* 100 vpos))))) - (save-excursion - (select-window (org-noter--get-doc-window)) - (setq pagelabel (pdf-view-current-pagelabel))) + (select-window (org-noter--get-doc-window)) + (setq pagelabel (pdf-view-current-pagelabel)) + (select-window (org-noter--get-notes-window)) (format "%s%s%s" pagelabel vtxt htxt)) ((eq mode 'doc-view-mode) ; fall back to original pp for doc-mode (org-noter-pdf--pretty-print-location location)))))) From aca67403b45eeb990691ddc93aa2b108e8623bfe Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Mon, 30 Jan 2023 18:23:49 -0800 Subject: [PATCH 318/453] better multicolumn alg. use COLUMN_EDGES instead of NUM_COLUMNS COLUMN_EDGES is an ordered list of the right sides of the columns, ending with 1. I will need to write a user interface to generate the property values interactively --- modules/org-noter-pdf.el | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 4075eb7..c715f72 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -386,17 +386,22 @@ where (pabe v-pos) or (page v-pos . h-pos) is returned" (defun org-noter-pdf-convert-to-location-cons (location) "converts (page v . h) precise locations to (page v') such that v' represents the fractional distance through the page along -columns, so it takes values between 0 and NUM_COLUMNS. Output is -nil for standard notes and (page v') for precise notes" +columns, so it takes values between 0 and the number of columns. +Each column is specified by its right edge as a fractional +horizontal position. Output is nil for standard notes and (page +v') for precise notes." (if-let* ((_ (and (consp location) (consp (cdr location)))) (bb (current-buffer)) ; debugging code - we are in the doc window, ; but need to be in the notes window for next ; line to work - (ncol (max 1 (string-to-number (or (org-entry-get nil "NUM_COLUMNS" t) "1")))) + (column-edges-string (org-entry-get nil "COLUMN_EDGES" t)) + (right-edge-list (car (read-from-string column-edges-string))) + ;;(ncol (length left-edge-list)) (page (car location)) (v-pos (cadr location)) - (h-pos (cddr location))) - (cons page (+ v-pos (float (floor (* h-pos ncol))))))) + (h-pos (cddr location)) + (column-index (seq-position right-edge-list h-pos #'>=))) + (cons page (+ v-pos column-index)))) (add-to-list 'org-noter--convert-to-location-cons-hook #'org-noter-pdf-convert-to-location-cons) From 0a584e81eabeca5a4ed8fe533b1a1097b03bc675 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Wed, 1 Feb 2023 22:34:17 -0800 Subject: [PATCH 319/453] notes for demo screencast, change keybinding for precise-no-Qs it makes more sense for insert-precise-note-toggle-no-questions to have the keybinding C-M-i then we have the more logically consistent: | | insert-note | insert-precise-note | |------------------+-------------+---------------------| | prompt for title | i | M-i | | no-questions | C-i | C-M-i | --- docs/org-noter-demo.org | 70 +++++++++++++++++++++++++++++++++++++++++ org-noter-core.el | 2 +- 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 docs/org-noter-demo.org diff --git a/docs/org-noter-demo.org b/docs/org-noter-demo.org new file mode 100644 index 0000000..fc3ad90 --- /dev/null +++ b/docs/org-noter-demo.org @@ -0,0 +1,70 @@ +* Opening a notes session + - open PDF + - ~M-x org-noter~ + - ~M-x org-noter-set-doc-split-fraction~ + - ~M-x org-noter-set-notes-window-location, M-n~ (org-noter-set-layout?) side-by-side | stacked + - ~M-x org-noter-create-skeleton, M-n~ + +* Navigating the document and the notes + - ~SPC, n, Page-down, down~ to move forward in document + - ~BACKSPACE, p, Page-up, up~ to move back in document + - ~C-M-n, C-M-p~ to move to next/prev note + - ~C-M-.~ sync document to notes + - ~M-n, M-p~ to move to next/prev page with note(s). Always lands on the + first note of the page. + - ~M-.~ sync notes to document + +* Multicolumn setup (as needed) + - Multiple column note ordering can be set up at the document or heading level + - ~org-noter-pdf-set-columns~ inserts "COLUMN_EDGES" into the property drawer + of the current heading. The command requests the number of columns and then + asks you to click on the right edge of all but the last column. The + property is inherited by all sub-headings. + +* Note insertion + +** ~insert-note~ (~i~) + - Inserts a note linked to the current page. If no title is specified, then + default title "Notes for page

" is used, where

is the pagelabel if + it exists or the page number. + - If text is selected AND it is "short" (see ~defcustom + org-noter-max-short-selected-text-length~) the the selected text becomes + the default title. + - If you type in a title, then the selected text is quoted in the body of the + note. Short selected text is set in ``LaTeX-style quotes,'' + #+begin_quote + while long selected text is set inside QUOTE block delimiters. + #+end_quote + - At the title prompt =Note:=, you can use ~M-p~ to "up-arrow" prior note + headings, or ~M-n~ to select from the defaults. + - If you choose a prior note heading, then selected text will be quoted in + that heading. + +** ~insert-precise-note~ (~M-i~) + - Precise notes always create a new note, even if you choose an existing + prior heading. + - Precise notes are linked to a specific point on the page specified with + vertical and horizontal coordinates. + - The multicolumn property ~COLUMN_EDGES~, set by + ~org-noter-pdf-set-columns~, governs the ordering of precise notes on a + page. + - If no title is specified, then default title "Notes for page

V: % H: + %" is used, where

is the pagelabel if it exists or the page number, + is the vertical distance from the top and is the horizontal + position from the left. + - The behavior with selected text (default title, quoting in the body) is the + same as for ~insert-note~. + +** No-questions note insertion + - ~defcustom org-noter-insert-note-no-questions~ is default ~nil~. If set to + ~t~, the note title minibuffer prompt is bypassed and a note is always + create with the default title. Activate this setting if you rarely or + never type in your own titles. + - Both note insertion styles have a ~toggle-no-questions~ variant to get the + non-default behavior. + - Default keybinding for the ~toggle-no-questions~ variant adds the + control-key (~C-i~ and ~C-M-i~, respectively). +** Highlighting + - ~defcustom org-noter-highlight-selected-text~ controls the default + highlighting behavior of selected text. + - ~C-u~ prefix to any note insertion command toggles this behavior diff --git a/org-noter-core.el b/org-noter-core.el index 9beacc5..016484e 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -2287,7 +2287,7 @@ Keymap: :keymap `((,(kbd "i") . org-noter-insert-note) (,(kbd "C-i") . org-noter-insert-note-toggle-no-questions) (,(kbd "M-i") . org-noter-insert-precise-note) - (,(kbd "M-I") . org-noter-insert-precise-note-toggle-no-questions) + (,(kbd "C-M-i") . org-noter-insert-precise-note-toggle-no-questions) (,(kbd "q") . org-noter-kill-session) (,(kbd "M-p") . org-noter-sync-prev-page-or-chapter) (,(kbd "M-.") . org-noter-sync-current-page-or-chapter) From 7c851151bdbc99e5fbe6fec5f1eb640f5a3ffb83 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Wed, 1 Feb 2023 22:39:43 -0800 Subject: [PATCH 320/453] new command to interactively set column edges for multicolumn PDFs --- modules/org-noter-pdf.el | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index c715f72..7a77658 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -405,5 +405,31 @@ v') for precise notes." (add-to-list 'org-noter--convert-to-location-cons-hook #'org-noter-pdf-convert-to-location-cons) +(defun org-noter-pdf-set-columns (num-columns) + "Interactively set the COLUMN_EDGES property for the current heading. + +The number of columns can be given as a prefix or in the +minibuffer. The user is then prompted to click on the right edge +of each column, except for the last one. Subheadings of the +current heading inherit the COLUMN_EDGES property." + (interactive "NEnter number of columns: ") + (select-window (org-noter--get-doc-window)) + (let (event + edge-list + (window (car (window-list)))) + (dotimes (ii (1- num-columns)) + (while (not (and (eq 'mouse-1 (car event)) + (eq window (posn-window (event-start event))))) + (setq event (read-event (format "Click on the right boundary of column %d" (1+ ii))))) + (let* ((col-row (posn-col-row (event-start event))) + (click-position (org-noter--conv-page-scroll-percentage (+ (window-vscroll) (cdr col-row)) + (+ (window-hscroll) (car col-row)))) + (h-position (cdr click-position))) + (setq event nil) + (setq edge-list (append edge-list (list h-position))))) + (setq edge-list (append edge-list '(1))) + (select-window (org-noter--get-notes-window)) + (org-entry-put nil "COLUMN_EDGES" (format "%s" (princ edge-list))))) + (provide 'org-noter-pdf) ;;; org-noter-pdf.el ends here From 973d7cc1933de5e50b0e105438d9f703fe7983aa Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Thu, 2 Feb 2023 22:36:17 -0800 Subject: [PATCH 321/453] make new defgroups for defcustom variables --- README.org | 6 ++-- org-noter-core.el | 73 ++++++++++++++++++++++++++++++----------------- 2 files changed, 50 insertions(+), 29 deletions(-) diff --git a/README.org b/README.org index 3c3c072..6a97961 100644 --- a/README.org +++ b/README.org @@ -208,14 +208,14 @@ - org-noter-notes-window-behavior '(start scroll) - org-noter-suggest-from-attachments t - org-noter-find-additional-notes-functions nil + - org-noter-always-create-frame t - org-noter-kill-frame-at-session-end t + - org-noter-use-indirect-buffer t *** layout - org-noter-notes-window-location 'horizontal-split - org-noter-doc-split-fraction '(0.5 . 0.5) - - org-noter-always-create-frame t - org-noter-disable-narrowing nil - - org-noter-use-indirect-buffer t - org-noter-swap-window nil - org-noter-hide-other t @@ -227,7 +227,6 @@ - org-noter-max-short-selected-text-length 80 - org-noter-insert-heading-hook nil - org-noter-insert-note-no-questions nil - - org-noter-doc-property-in-notes nil *** navigation-display - org-noter-arrow-delay 0.2 @@ -242,3 +241,4 @@ - org-noter-property-doc-file "NOTER_DOCUMENT" - org-noter-property-note-location "NOTER_PAGE" - org-noter-prefer-root-as-file-level nil # used in org-noter--parse-root + - org-noter-doc-property-in-notes nil diff --git a/org-noter-core.el b/org-noter-core.el index 016484e..d3c6b57 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -66,6 +66,21 @@ :group 'convenience :version "25.3.1") +(defgroup org-noter-layout nil + "Org-noter layout and visibility variables" + :group 'org-noter + :version "28.2") + +(defgroup org-noter-navigation nil + "Org-noter navigation and display variables" + :group 'org-noter + :version "28.2") + +(defgroup org-noter-insertion nil + "Org-noter note-insertion variables" + :group 'org-noter + :version "28.2") + (defcustom org-noter-supported-modes '(doc-view-mode pdf-view-mode nov-mode djvu-read-mode) "Major modes that are supported by org-noter." :group 'org-noter @@ -86,7 +101,7 @@ The default value is still NOTER_PAGE for backwards compatibility." "The default title for headings created with `org-noter-insert-note'. $p$ is replaced with the number of the page or chapter you are in at the moment." - :group 'org-noter + :group 'org-noter-insertion :type 'string) (defcustom org-noter-notes-window-behavior '(start scroll) @@ -107,7 +122,7 @@ When the list contains: Note that this will only have effect on session startup if `start' is member of `org-noter-notes-window-behavior' (which see)." - :group 'org-noter + :group 'org-noter-layout :type '(choice (const :tag "Horizontal" horizontal-split) (const :tag "Vertical" vertical-split) (const :tag "Other frame" other-frame))) @@ -116,7 +131,7 @@ is member of `org-noter-notes-window-behavior' (which see)." (defcustom org-noter-doc-split-fraction '(0.5 . 0.5) "Fraction of the frame that the document window will occupy when split. This is a cons of the type (HORIZONTAL-FRACTION . VERTICAL-FRACTION)." - :group 'org-noter + :group 'org-noter-layout :type '(cons (number :tag "Horizontal fraction") (number :tag "Vertical fraction"))) (defcustom org-noter-auto-save-last-location nil @@ -137,7 +152,7 @@ there is at least one heading." "When non-nil, hide all headings not related to the command used. For example, when scrolling to pages with notes, collapse all the notes that are not annotating the current page." - :group 'org-noter + :group 'org-noter-layout :type 'boolean) (defcustom org-noter-always-create-frame t @@ -148,7 +163,7 @@ When nil, it will use the selected frame if it does not belong to any other sess (defcustom org-noter-disable-narrowing nil "Disable narrowing in notes/org buffer." - :group 'org-noter + :group 'org-noter-layout :type 'boolean) (defcustom org-noter-use-indirect-buffer t @@ -163,7 +178,7 @@ When nil, it will use the real buffer." to the document buffer then split with the window of the notes buffer on the right. If this variable is non-nil, the buffers of the two windows will be the other way around." - :group 'org-noter + :group 'org-noter-layout :type 'boolean) @@ -175,12 +190,12 @@ when creating a session, if the document is missing." (defcustom org-noter-separate-notes-from-heading nil "When non-nil, add an empty line between each note's heading and content." - :group 'org-noter + :group 'org-noter-insertion :type 'boolean) (defcustom org-noter-insert-selected-text-inside-note t "When non-nil, it will automatically append the selected text into an existing note." - :group 'org-noter + :group 'org-noter-insertion :type 'boolean) (defcustom org-noter-closest-tipping-point 0.3 @@ -200,7 +215,7 @@ When this value is negative, disable this feature. This setting may be overridden in a document with the function `org-noter-set-closest-tipping-point', which see." - :group 'org-noter + :group 'org-noter-navigation :type 'number) (defcustom org-noter-default-notes-file-names '("Notes.org") @@ -218,8 +233,9 @@ This setting may be overridden in a document with the function When set to a negative number, the arrow tooltip is disabled. This is needed in order to keep Emacs from hanging when doing many syncs." - :group 'org-noter - :type 'number) + :group 'org-noter-navigation + :type 'number + :version "28.2") (defcustom org-noter-arrow-horizontal-offset -0.02 "Horizontal offset in the position of the tooltip arrow relative @@ -227,18 +243,21 @@ This is needed in order to keep Emacs from hanging when doing many syncs." to precise location. Units are % of page, positive values move the arrow to the right, while negative values move it to the left." - :group 'org-noter - :type 'number) + :group 'org-noter-navigation + :type 'number + :version "28.2") (defcustom org-noter-arrow-foreground-color "orange red" "Default color of the tooltip arrow" - :group 'org-noter - :type 'string) + :group 'org-noter-navigation + :type 'string + :version "28.2") (defcustom org-noter-arrow-background-color "white" "Default background color of the tooltip arrow" - :group 'org-noter - :type 'string) + :group 'org-noter-navigation + :type 'string + :version "28.2") (defcustom org-noter-doc-property-in-notes nil "If non-nil, every new note will have the document property too. @@ -249,7 +268,7 @@ This makes moving notes out of the root heading easier." (defcustom org-noter-insert-note-no-questions nil "When non-nil, `org-noter-insert-note' won't ask for a title and will always insert a new note. The title used will be the default one." - :group 'org-noter + :group 'org-noter-insertion :type 'boolean) (defcustom org-noter-kill-frame-at-session-end t @@ -259,23 +278,25 @@ The title used will be the default one." (defcustom org-noter-insert-heading-hook nil "Hook being run after inserting a new heading." - :group 'org-noter + :group 'org-noter-insertion :type 'hook) (defcustom org-noter-highlight-selected-text nil "If non-nil, highlight selected-text when creating notes. This variable is temporarily toggled by prefixing the insertion command with any non-nil prefix such as \\[universal-argument]." - :group 'org-noter - :type 'boolean) + :group 'org-noter-insertion + :type 'boolean + :version "28.2") (defcustom org-noter-max-short-selected-text-length 80 "Maximum length of a short text selection. Short text selections may be used as note title. When they are quoted in the note, they are quoted as ``short-selected-text'' rather than inside a QUOTE-block." - :group 'org-noter - :type 'integer) + :group 'org-noter-insertion + :type 'integer + :version "28.2") (defcustom org-noter-find-additional-notes-functions nil "Functions that when given a document file path as argument, give out @@ -298,21 +319,21 @@ the user select to use as the note file of the document." :foreground "chocolate" :weight bold)) "Face for modeline note count, when 0." - :group 'org-noter) + :group 'org-noter-navigation) (defface org-noter-notes-exist-face '((t :foreground "SpringGreen" :weight bold)) "Face for modeline note count, when not 0." - :group 'org-noter) + :group 'org-noter-navigation) ;; -------------------------------------------------------------------------------- ;;; Integration with other packages (defgroup org-noter-module-hooks nil "Hooks for integrating org-noter with other packages (pdfview, nov, djvu)" :group 'org-noter - :version "25.3.1") + :version "28.2") (defcustom org-noter--get-location-property-hook nil "The list of functions that will return the note location of an org element. From 51496d571415f182d3917d48e6f3fb9fdade443c Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Thu, 2 Feb 2023 22:37:45 -0800 Subject: [PATCH 322/453] nothing to see--purely cosmetic. --- modules/org-noter-pdf.el | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 7a77658..c9dfc24 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -40,13 +40,12 @@ Otherwise returns nil" (make-pdf-highlight :page page :coords coords) nil)) - +(add-to-list 'org-noter--get-highlight-location-hook 'org-noter-pdf--get-highlight) (defun org-noter-pdf--pretty-print-highlight (highlight-info) (format "%s" highlight-info)) (add-to-list 'org-noter--pretty-print-highlight-location-hook #'org-noter-pdf--pretty-print-highlight) -(add-to-list 'org-noter--get-highlight-location-hook 'org-noter-pdf--get-highlight) (defun org-noter-pdf-approx-location-cons (mode &optional precise-info _force-new-ref) "Returns (page . 0) except when creating a precise-note, From 780a8f5dc4030cefc16cad3d8e195dc3bcdd85bb Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sat, 4 Feb 2023 10:20:57 -0800 Subject: [PATCH 323/453] -bug (navigation scrolling) +feature (vscroll buffer) bug: image-scroll-up expects an integer, but was being given a float. This caused the call not to work as intended. Former behavior would case the display to "walk" up with repeated calls to `org-noter-sync-current-note'. feature: defcustom `org-noter-vscroll-buffer' allows the user to customize how far below the top of the window the page should be scrolled to. Default 5, while 0 gives what I believe was the intended behavior. --- modules/org-noter-pdf.el | 3 ++- org-noter-core.el | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index c9dfc24..b86f5a2 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -167,7 +167,8 @@ where (pabe v-pos) or (page v-pos . h-pos) is returned" top left)))) (image-scroll-up (- (org-noter--conv-page-percentage-scroll top) - (window-vscroll)))))) + (floor (+ (window-vscroll) org-noter-vscroll-buffer))) + )))) (add-to-list 'org-noter--doc-goto-location-hook #'org-noter-pdf-goto-location) diff --git a/org-noter-core.el b/org-noter-core.el index d3c6b57..9960025 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -259,6 +259,14 @@ left." :type 'string :version "28.2") +(defcustom org-noter-vscroll-buffer 5 + "Number of document display lines to leave above note location +when navigating to precise note. A value of 0 places the precise +note at the top of the window when possible." + :group 'org-noter-navigation + :type 'number + :version "28.2") + (defcustom org-noter-doc-property-in-notes nil "If non-nil, every new note will have the document property too. This makes moving notes out of the root heading easier." From 97f4bd757491979d6dbd7acec0564e47a9e26551 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Sat, 4 Feb 2023 15:38:26 -0800 Subject: [PATCH 324/453] arrow positioning: move horizontal shift into --show-arrow This is an attmept to make the arrow position less dependent on display magnification. Formerly, `org-noter-arrow-horizontal-offset' was in "percentage of page" units. The arrow, however, is displayed with constant display-pixels units, so the needed offset as a percentage of the page was less for higher magnification. Also, the offset was encoded in the location of the note, which could potentially affect placement of the note in multicolumn contexts because the encoded position would be shifted from the click position. Yet another issue is that precise locations based on selected text was not being treated the same way as clicked precise locations. This commit changes `org-noter-arrow-horizontal-offset' to display-pixels units and shifts the arrow in `org-noter--show-arrow', rather than shifting the precise location in the `...-get-precise-info' function. --- modules/org-noter-pdf.el | 4 +--- org-noter-core.el | 28 ++++++++++++++++------------ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index b86f5a2..7df167c 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -132,7 +132,6 @@ where (pabe v-pos) or (page v-pos . h-pos) is returned" (+ (window-hscroll) (car col-row))))) (setq v-position (car click-position) h-position (cdr click-position))))) - (setq h-position (max 0 (+ h-position org-noter-arrow-horizontal-offset))) (cons v-position h-position)))) (add-to-list 'org-noter--get-precise-info-hook #'org-noter-pdf--get-precise-info) @@ -167,8 +166,7 @@ where (pabe v-pos) or (page v-pos . h-pos) is returned" top left)))) (image-scroll-up (- (org-noter--conv-page-percentage-scroll top) - (floor (+ (window-vscroll) org-noter-vscroll-buffer))) - )))) + (floor (+ (window-vscroll) org-noter-vscroll-buffer))))))) (add-to-list 'org-noter--doc-goto-location-hook #'org-noter-pdf-goto-location) diff --git a/org-noter-core.el b/org-noter-core.el index 9960025..8665c93 100644 --- a/org-noter-core.el +++ b/org-noter-core.el @@ -237,12 +237,14 @@ This is needed in order to keep Emacs from hanging when doing many syncs." :type 'number :version "28.2") -(defcustom org-noter-arrow-horizontal-offset -0.02 +(defcustom org-noter-arrow-horizontal-offset -20 "Horizontal offset in the position of the tooltip arrow relative -to precise location. Units are % of page, positive values move -the arrow to the right, while negative values move it to the -left." +to precise location. Units are display pixels; positive values +move the arrow to the right, while negative values move it to the +left. The intent is to move the arrow so that it does not cover +text of intereest, but roundoff errors cause the arrow position +still to be dependent upon magnification at the 1-em level" :group 'org-noter-navigation :type 'number :version "28.2") @@ -1207,12 +1209,12 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." ;; From `pdf-util-tooltip-arrow'. (pdf-util-assert-pdf-window) (let* (x-gtk-use-system-tooltips - (image-top (if (floatp (aref org-noter--arrow-location 2)) - (round (* (aref org-noter--arrow-location 2) - (cdr (pdf-view-image-size)))))) - (image-left (if (floatp (aref org-noter--arrow-location 3)) - (round (* (aref org-noter--arrow-location 3) (car (pdf-view-image-size)))))) - + (arrow-top (aref org-noter--arrow-location 2)) ; % of page + (arrow-left (aref org-noter--arrow-location 3)) + (image-top (if (floatp arrow-top) + (round (* arrow-top (cdr (pdf-view-image-size)))))) ; pixel location on page (magnification-dependent) + (image-left (if (floatp arrow-left) + (floor (* arrow-left (car (pdf-view-image-size)))))) (dx (or image-left (+ (or (car (window-margins)) 0) (car (window-fringes))))) @@ -1233,8 +1235,10 @@ When INCLUDE-ROOT is non-nil, the root heading is also eligible to be returned." (frame-char-height)))) (when (overlay-get (pdf-view-current-overlay) 'before-string) (let* ((e (window-inside-pixel-edges)) - (xw (pdf-util-with-edges (e) e-width))) - (cl-incf dx (/ (- xw (car (pdf-view-image-size t))) 2)))) + (xw (pdf-util-with-edges (e) e-width)) + (display-left-margin (/ (- xw (car (pdf-view-image-size t))) 2))) + (cl-incf dx display-left-margin))) + (setq dx (max 0 (+ dx org-noter-arrow-horizontal-offset))) (pdf-util-tooltip-in-window (propertize " " 'display (propertize From ee208537095b241a868ab9352b7aeae6f0b3c2df Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sun, 5 Feb 2023 16:07:03 -0800 Subject: [PATCH 325/453] Add a hook to handle org-noter--pretty-print-location-for-title --- org-noter-test-utils.el | 3 +++ 1 file changed, 3 insertions(+) diff --git a/org-noter-test-utils.el b/org-noter-test-utils.el index 0468bab..1aa0e08 100644 --- a/org-noter-test-utils.el +++ b/org-noter-test-utils.el @@ -121,6 +121,8 @@ org-noter-core-test-return-text (defun org-noter-core-test-get-highlight-location () "HARDCODED_HIGHLIGHT_LOCATION") +(defun org-noter-core-test-pretty-print-location-for-title (location) + "TEST PRETTY PRINT LOCATION") (defun create-org-noter-test-session () @@ -145,6 +147,7 @@ org-noter-core-test-return-text (add-to-list 'org-noter--get-current-view-hook #'org-noter-core-test-get-current-view) (add-to-list 'org-noter--get-precise-info-hook #'org-noter-core-test-get-precise-info) (add-to-list 'org-noter--pretty-print-location-hook #'org-noter-core-test-pretty-print-location) + (add-to-list 'org-noter--pretty-print-location-for-title-hook #'org-noter-core-test-pretty-print-location-for-title) (add-to-list 'org-noter--add-highlight-hook #'org-noter-core-test-add-highlight) (add-to-list 'org-noter--get-highlight-location-hook #'org-noter-core-test-get-highlight-location) ) From 64395d9884b47f29e5c3291fc2c41d3bb3a83b9c Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sun, 5 Feb 2023 16:42:26 -0800 Subject: [PATCH 326/453] Disable extra tests for now. --- tests/org-noter-extra-tests.el | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/org-noter-extra-tests.el b/tests/org-noter-extra-tests.el index 0296730..0cd9103 100644 --- a/tests/org-noter-extra-tests.el +++ b/tests/org-noter-extra-tests.el @@ -6,6 +6,7 @@ (describe "org-noter very custom behavior" +(xdescribe "org-noter very custom behavior" (before-each (create-org-noter-test-session) ) From a7fdda405a6802c597a6225358e4e47a762a1b5c Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sun, 5 Feb 2023 16:42:40 -0800 Subject: [PATCH 327/453] This is part of the solution to the failing tests. --- tests/org-noter-extra-tests.el | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/org-noter-extra-tests.el b/tests/org-noter-extra-tests.el index 0cd9103..31b9995 100644 --- a/tests/org-noter-extra-tests.el +++ b/tests/org-noter-extra-tests.el @@ -12,6 +12,7 @@ ) (describe "with advice" (before-each + (setq org-noter-insert-selected-text-inside-note f) (setq org-noter-max-short-length 80000) (define-advice org-noter--insert-heading (:after (level title &optional newlines-number location) add-full-body-quote) "Advice for org-noter--insert-heading. From aa89111ace400e0bfbfa63de19ada750f06e9a89 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sun, 5 Feb 2023 16:45:40 -0800 Subject: [PATCH 328/453] =?UTF-8?q?Actually=20commit=20"test=20disable"=20?= =?UTF-8?q?(>=E1=83=9A)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/org-noter-extra-tests.el | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/org-noter-extra-tests.el b/tests/org-noter-extra-tests.el index 31b9995..13a07fb 100644 --- a/tests/org-noter-extra-tests.el +++ b/tests/org-noter-extra-tests.el @@ -5,7 +5,6 @@ (require 'org-noter-test-utils) -(describe "org-noter very custom behavior" (xdescribe "org-noter very custom behavior" (before-each (create-org-noter-test-session) From f66614e50b0a5f0c51d32adc039e82f0942692eb Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sun, 5 Feb 2023 18:58:06 -0800 Subject: [PATCH 329/453] Fix advice errors. --- tests/org-noter-extra-tests.el | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/org-noter-extra-tests.el b/tests/org-noter-extra-tests.el index 13a07fb..4851b67 100644 --- a/tests/org-noter-extra-tests.el +++ b/tests/org-noter-extra-tests.el @@ -6,6 +6,7 @@ (xdescribe "org-noter very custom behavior" +(describe "org-noter very custom behavior" (before-each (create-org-noter-test-session) ) @@ -21,6 +22,8 @@ =org-noter-max-short-length= should be set to a large value to short circuit the normal behavior: =(setq org-noter-max-short-length 80000)=" + ;; (setq org-noter-insert-selected-text-inside-note nil) + (setq org-noter-max-short-selected-text-length 700000) ;; this tells us it's a precise note that's being invoked. (if (consp location) (insert (format "#+BEGIN_QUOTE\n%s\n#+END_QUOTE" title)))) From ab14f899ecde63fbe3cb74fcd87e762dfd45abd7 Mon Sep 17 00:00:00 2001 From: Dmitry M Date: Sun, 5 Feb 2023 19:04:14 -0800 Subject: [PATCH 330/453] Clean up tests. --- tests/org-noter-extra-tests.el | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tests/org-noter-extra-tests.el b/tests/org-noter-extra-tests.el index 4851b67..ae3e75e 100644 --- a/tests/org-noter-extra-tests.el +++ b/tests/org-noter-extra-tests.el @@ -5,15 +5,14 @@ (require 'org-noter-test-utils) -(xdescribe "org-noter very custom behavior" (describe "org-noter very custom behavior" (before-each (create-org-noter-test-session) ) (describe "with advice" (before-each - (setq org-noter-insert-selected-text-inside-note f) - (setq org-noter-max-short-length 80000) + (setq org-noter-max-short-selected-text-length 700000) + (define-advice org-noter--insert-heading (:after (level title &optional newlines-number location) add-full-body-quote) "Advice for org-noter--insert-heading. @@ -22,15 +21,13 @@ =org-noter-max-short-length= should be set to a large value to short circuit the normal behavior: =(setq org-noter-max-short-length 80000)=" - ;; (setq org-noter-insert-selected-text-inside-note nil) - (setq org-noter-max-short-selected-text-length 700000) ;; this tells us it's a precise note that's being invoked. (if (consp location) (insert (format "#+BEGIN_QUOTE\n%s\n#+END_QUOTE" title)))) (create-org-noter-test-session) ) (after-each - (setq org-noter-max-short-length 80) + (setq org-noter-max-short-selected-text-length 80) (advice-remove #'org-noter--insert-heading 'org-noter--insert-heading@add-full-body-quote) ) (it "should insert the highlighted text as an org-mode QUOTE when advice is enabled." From 439bb40a8a417b07c93f943c7adaa5f63f14140b Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Wed, 8 Feb 2023 22:11:52 -0800 Subject: [PATCH 331/453] simplify package loading (add-to-list 'load-path ) (require 'org-noter) internally loads /modules loads modules based on modes listed in `org-noter-supported-modes' --- modules/org-noter-djvu.el | 2 -- modules/org-noter-nov.el | 2 -- modules/org-noter-pdf.el | 3 --- org-noter.el | 9 +++++++++ 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/modules/org-noter-djvu.el b/modules/org-noter-djvu.el index 7beee2f..d5f73f0 100644 --- a/modules/org-noter-djvu.el +++ b/modules/org-noter-djvu.el @@ -23,8 +23,6 @@ ;; ;;; Code: -(require 'org-noter) - (defun org-noter-djvu--pretty-print-location (location) (org-noter--with-valid-session (when (eq (org-noter--session-doc-mode session) 'djvu-read-mode) diff --git a/modules/org-noter-nov.el b/modules/org-noter-nov.el index 87aba6a..7106823 100644 --- a/modules/org-noter-nov.el +++ b/modules/org-noter-nov.el @@ -23,8 +23,6 @@ ;; ;;; Code: -(require 'org-noter) - (defvar nov-documents-index) (defvar nov-file-name) diff --git a/modules/org-noter-pdf.el b/modules/org-noter-pdf.el index 7df167c..f91fdef 100644 --- a/modules/org-noter-pdf.el +++ b/modules/org-noter-pdf.el @@ -23,9 +23,6 @@ ;; ;;; Code: -(require 'org-noter) - - (cl-defstruct pdf-highlight page coords) diff --git a/org-noter.el b/org-noter.el index 03a95c1..903a231 100644 --- a/org-noter.el +++ b/org-noter.el @@ -43,6 +43,15 @@ (declare-function org-entry-put "org") (declare-function org-with-wide-buffer "org-macs") +(add-to-list 'load-path (concat (file-name-directory load-file-name) "modules/")) +(when (or (memq 'doc-view-mode org-noter-supported-modes) + (memq 'pdf-view-mode org-noter-supported-modes)) + (require 'org-noter-pdf)) +(when (memq 'nov-mode org-noter-supported-modes) + (require 'org-noter-nov)) +(when (memq 'djvu-read-mode org-noter-supported-modes) + (require 'org-noter-djvu)) + ;;;###autoload (defun org-noter (&optional arg) "Start `org-noter' session. From b636d356bfe5f823d9b5a9042581499b4b3430d5 Mon Sep 17 00:00:00 2001 From: Peter Mao Date: Fri, 10 Feb 2023 21:59:58 -0800 Subject: [PATCH 332/453] +MobyDick.pdf and Notes.org for tests --- tests/MobyDick.pdf | Bin 0 -> 1390564 bytes tests/Notes.org | 978 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 978 insertions(+) create mode 100644 tests/MobyDick.pdf create mode 100644 tests/Notes.org diff --git a/tests/MobyDick.pdf b/tests/MobyDick.pdf new file mode 100644 index 0000000000000000000000000000000000000000..7e2aa237d7c40d1aa1ce3c27560bbf90d64e86ee GIT binary patch literal 1390564 zcmb5VW3Vi1vn@Pr+qP}nwr$(CZLMkBwzZ~ht!W!~pYI;L5$}$B_E$eDx~jXQBkLJG za%7HtNEJlHXc_5Pp-AVKM%JL12p9tPZ5CoVgv>otLO*sU+EunyVOe;M55$`iR*R&U* zqs(`jck0mbdw4Z73s(|?B9B4R8zx9npn@U|z0bhH6$ zP@7CZdO->r+?QpTBbxfr8Ia1TFL;!!9|%;pB+oOpDt-`nZV&*T=QM{PS?7@`!Bc?& zV9vW7yc^&F=r$;J9$Ae_2}K>-9H9p3GOAyi0USYV!FLe#Me-oa0GOwM<$8h_ayDeF zIgUv57wm0{N(O*JVlvHVQSng7V_LsU7!H7fI94#o73w?W@?a5n5KCA5m)Nko8G+MsEbsOmofdmpRb$z zpHH8X-DvrIvzxqqu+Kr}gN|SOB17m{tt^+8YqyuR-tT@GD=_`vqif<=Xe?W|THFyH zC9{A{T;40rvO{sI)d0^JJAWMJI{BojHTys>d? z^t8IL(X8p2S){j~B6#b6Z7-T>RCA+unPh|+;#ssGZ_eH?GF0|WhnFcCF$dw%IWO~8 z4?eXB4nFpNw!j^Z@Yq5``0b4N3I z<7*y=Q%V+iF|mhxyWLWSD}r7QhRpEssR)#vT9!IYHK{%;ii*j%njLSfGPRPr!=ui- zXhMz~I#9T)o-GPzcJA+sjuuuYUm()C%Cnsme_S8Ynj9S@Y^z9mZztz(Hd^3z@N(9T zk0q5VPbk)qX7~x&PvY>!@B8w3QUR6a&u}{NGyBj9moWHr-h`Wk1H; zZe-La!*3bw-H$fYOuOdnOUbtzZFfvQw)2+D_3UtEbYjc+11k!b;B)@yD_YY_TtL)a z*4ljhqQre1!^SIWIKpw2fyOJk-&!rT-a|i&QSIk!cXltCG`CRby_>FTuV*GvV%X%g zC!pVS9Q^K!p?p|MUOsBHQVAIPjPoq&+(4!M1I6j~{sSd%dd!=uT5T8|^&rMnY*w)AyS%n=s|-5W`}FX}kwm|uyF$L5Mwy8HJ*V7TTvKQI)&R5PCWUM;ge_$< zJ8S!9Kd~lgQ1*%<#R)h=tw+HbKT^xV|#F#?`vAoV2@^n0ndvOjy_UTxGu>qiv|*i%M*`#;8R zCFG@wD9S~(hL@1|P4moR&n9eXtq4VFY5k(&$$@ySEH1b5QF{dqFGH(*_Q3jefhAAh ztmpw|h|;TQUahs%g49}QPhBnMeHFrtQC950x2G8?aG#2nN&-3=A;leyPEQ4V)dE!r z1!&BAG_FN&g+vl72>UJ>FN91oXb3?K$e19yXI*p$WXKAupo{$HrJN0*pkiW$gqp@m zdIl;qRKfW7m{16*AzV_8xOg}`{oV2W<%Aeq67YoMD=2gY9R#T)92kXV;5v0iQx#!X zlF5yMQ02gk5ErDXLC@yh75J67YH`!^Rs~ z22?Qfy5jR-UIhriqjfS7}a(a8hp5^*2jB~Oj8zna(ScGP<=1;&$)_>(7EyST4{@( zpQg@HQEfwB1V*~jac7M;xuQDaMwpwYT`G-)U70&djF1^^jed}KO0ti1SF;rL(5dAY zxeiaPi(2L0_=;pvt-B()S}{F4HFgMtd?IM_hikhO{nsA`wsr8CBV_P@)4f9gFOIdx z_Sm} z>0$%2{_tGN4!i$$K0<|WOs45VN_%Mi*q|OTTGYR9odvsjID}Ia9D?O%ye1sn6@4ZP zyX35Zjc>^YWT!>+z_`7Pbkx57notve-kck(k3k;i47}kUSXX?Xh{0O}a{=HRn{Xjo zIV7s4iMtnvE|`F*5pyU}^+1cRJE$vsP1$+rSxeAG#}qlP@R*sH%Ggaa-$H#k5w4!P z)UBp#HKOa=8F6sPb`lV)qRd;eurZ2>b#9M~xhaz|JheTf;|y2tS?)c8bnUS7}YO zdEgg?Uw3{VY4@L8&o0N8A;w5Z$;mn|>_P8qUYP;21Aya4>J@>77Fveoh)pukDdMi* zb!t7+&4(X(X^Z}Q;I%JC;BEV5;kt7Un}H7-y?f<;bFvPKjhOdO-P*IwZtgu1ya(XuzqR#A zUhO;4#%0V2r2P-t(|cH*IQP!E@j%spUkc-UGB$3b`_kywrXhdB7dL48adt*YEFPc1 zbY*yc{Ekfa5=+c=FUkxtbL;$Oy>l7HPUm?mOG)jhfAKT9X*;+uO6OF0NjxI88EZUP zat+6i$U6ldlrtI%%l|`WpzmD&0b!%C$1%%*5EkBw^o2dlb}EXz0{N3_upOt#P%wQcj+R&4ut&^@o?!L>1unwt> z6m{KH6atJkvavG$)t~4~8HnTY<78+Q0QvMpkIMpF*(B`Red&y(cOaJ-UfCyqc^?E>fd0iHsu?XhjPN=vTusE9a#U2Y9V+&6C7Tp6;}98wn!5J{oN zR$w1eZzu^ZY`!`pLIuqagi#Qa2*~W_J9;sf+ScIA3=x_5%Cj@kzBP-ZX+2QG1haI~ z%bK}eGo?0BztoUoU)xP$TQXYEy5725YjpZ_iAw~4WVwX>6{3Wcf{mjJLS3V-AqLZ;Xw(=K6UEbs zL7v3ai^A~O>uPw~o<-cPh83a)s4r$9pxG~S-2;TQMX?w+M1TQP zv{)Gg>S;x6%t6eGsjP0o5V>{KvnXr8g|Zks2a0-zItM6$n}OjNGaH2@Ms<6O2pp*; zFcbp*m0+IYoC>RkF_Q(Rq25$V2w_Zv5(5PcZ%|+87+^_k7~qOMz-d5T#Da)M-dYsY z0?``;?IXI-ID=|tpb4uAkr!;*L(mSF7K>5ekOjD876_u3);%2XlzvN_9bbZs1JNQZ znuS`=bBO8QAHYb65dloX#Sj86IS8?25R^TB3kHgDOQ2z}aZhuUaIC3>I$&CvgUd0T3qakf` zix&c7{6;Z=Nr*_I4Md2TVx9|e3L;vwyfNK}p}vd0^H*gKbIr^0AfU=sYn)$3A9RYblpshY~W>LGgzJk~1fD z$=-uBbNw+Q8`LL;>uM;Hq(J=5INqeM6@e26>2&DfxZ| zTl^F5G06U#E3$8krhujvEwfIgT5MWm6{ad}#~m%~t6G*h894gL+gxFDdj9lN>9~n^ z7^F^%=GKExi4aF>h%recetKZ%P^-h?_Vgi)+9_BoNl*V&^oD_?jv=P@BAz5>2irKF zv#@&aFD`v0Xe~l-Qo_!rglC}TIbYV<#nHp(B$Z3 zc{RP`-j|b>?2mnlx-r~?8 z*$jh0L*6x#6tQDoAc7=RXsupj8uDR6V+Je&ILKgWUN#C5N|9X>AQu_5_ z>yc9{N-kMc^l4tHRFB=1X&is=#Ykbg*D~V5&qyuQ%nx_K!QXE_<*X{6^LXf>cLeWR zL@xMQqGvQA7?ri3M6$!@umWvU_g3adDOTuyyT2cXd2^}K6aB*{D{}LBwVPbo=I;K% z(Bb<^6G5b`*7N)5VLSc99XSswUYR`k1%Kyy#}{=qN#Eg)8~VnMs=j?pPjHsSMfthO z=%@W%xm^R?&)-0YRv4vCpUi*W zRl({<;E|3@A75mnRYw~lb9H6C%<7^%yH$IcU=0*DXh$Mg@8|g$uDUjxoiip>|A?h0 zeNj)=oA+v_xot78v?u%7Z$tj=jk51{++MCk%x@Xn|Lx+}o8RaD^KNCExBHKu)2@Iz zdr#3$eVh9X!x1d?2b`J_wzr|gO4x2vmI8RuE$_;Tw!+iM)DN4%*8w9RNjRRVWJ1b40aCNMcD2%pyIGMV) zNG??8%FL8`karYD>ott6jBO!cn8rmxNNXpVHq5l;#x5BIsWl}p(I7F>t9Laj>S1H4 zZLq??vK)0lM2iTVV&Yz|lSXGvutVv}3_c0UJ&1ru>@Ce$$u1W}@a*VG0N5Cs-~tg< z5QH}PFP$0x6$K6w4vKM26>e2NT*pmDu=Jm%hX4h1$-;Px!0i($jxW%502`|E{fA*qQ&atc*>bipya@5PkVV zrHMs}fIdubWZG-awZk*sinn0tqWJlNWSmnx3uqjKmfNn>ZiDiFf9Fk4lp&qcIPw>d zDd8kkq_c#=A^M^A5|_6=3^9s<+RV#6kD*;=l@mC^A%?pP|3M@rLCq19Oe+a7p=J+; zFM4WGm~DvrCMQz0j(SafImss-tZN&I;lGd?JS1~fT@`X z)rs0V7i)LAF&py?LFi#{d_I?!tLWMzSAO2(T0|iMg6WCtzCP+~_p-Hd!ZRdVrGsk1 zS>jpn_T6r6XLY3`L;22`N)g-CQ>R)FP>uit7&t_wJy++Yt9|ik;j6)c@U33V2ffAK zU3qO=VU$V8Kd)17K#_ zkxaVA6@i$0eX8m`h zu(C4!W2E?|$Tns(AcVZ|foh7XfD*-$grqel#@;h<5d5f8b*WtYhrmcYJNG|BQJmSE zd1+ODoqwpEiJW!@5Z(DBPdJl#5H(L{8b$sje-fonjdZXirzFKb=`LQ?T7soW%id-` zw*4fU@X)8qFa_i|dnAbF*3n9lkPZ#l@r6BWt@F)j3_wY0P?Cei-iKF~WqKK})Xw-+ z24=V#iwjL)AU%qB>Fnb}-d#i6J?($J$Ke}(gg3m)JNekOi+Z||p z*!jW+Ov>t6)u00I)#+NI{pMPo-CTha_PA`_f4|*{v9*Le!%esUov54reK^%lLRycD z$jVZdLdz0ey~0R%sAOG&Q4quBV9YUij?nj%xNo*n;ZiK?HRV(Fq_*T8v07$f`I#(VWTfjmXP{UBfD z*bA{uJMAXt-AbD|PLwoG^TiI@%JJ{OV`E_Z$G{6! znM&FuK03_4YcuoQjvF zh5`f;ZLa@;k_tqqw*Uo97#RGf^-EvmP-C^sT7dIaC00Q{5CsSS<_SxNx7+o zn~rp_Xoy2&2xMZ=cNxrPhrEvI%1(6*Nek)1S9kmEyOC>;56=0wTzQuT z4UOph7COY>xNOZ^#b@%gX|2}BJvLIkE6fA}Xxv>+2of}(AyerpHhNz26E#-8lN3^= z_XeFnpk^dPL@5G5`bt*?-(1ihb+B3#wd*^wp%gdyesgi^-+FVEkc)fjnTwk_u>Ame zA;GhZ9FeRscQ))DOX8z)AAH`_wB21Ea0JD~{xSrQOHx)pY|BK96hPpZ?~hclSma8P z0fmq5YAn#)5h{Pz`PLHCs~EwBvL*K5aWhY|m!i4TGFeA3N(VonzPJriIvAtdNjGXY zW+^q3j?Z&RVz=GS(ax0eAr_`cF!y9wt%y(4Bn8R}+W2mNin`C(fRtDDd3?}~jARt*g`v`86rgGH@ zYG8|1Y;Uadq`{28yDGUZ8(|d*@!apv)s%n1rVa1kz?I`)Es+0gJh3rx{$se7sLUkg z5Fqs4D0@KTu>=q(aPGqtU6#urn442i(5AllhXgQiD|ed0ajr|y3EMnv1o5=+0^ zcM1%=oV%ORNsU!43nzdw1wv3EWdnRd=2}gqS`=L;OqX(RGM3PDsQ9{#Un_e_N-@S#j4q83M)%(;xBJ z&u_VPIgb(@22kj?8o?b%uh`AB#idkf>(47IO|+wz$&~gLG88XRg?o($&3hH5sk?sl z?dosvKkl0!xwkb|CA?t*PSe)#Igf#)A!o>YC_m9|;nzk75)sZ40EQNa6Uc8*J2}%l zqy7T(@QyeZjGY&=X0S*CeQVLabQ~MY$4#&P6!cdF@%!PY%TJQ~HmlR(TV=xLGW<0@ zG5X>4S1b889MjfxevF&; zyxB91{@Fq3q?{B!ngzhNh2w)jm$8{FFV5}_szV$Il-q=%4o{}@rC4tG`O3w!DpTYs z3tlN-Ps7&NoD(b0zMU(sq+=WS|Mx?n*;fNpY1dCLwf_?2tN(=DV7nov9? zecJZgTRY!pb~!nB++&NAlYnUBL1z2oAKOCnL%fCdI+rFOfza?>%~N>3)?PmLHF6kCJF(P$VJjZDm(|9I+WL|Zo=mleskuWvBE zi{4ikpKenr1VjXoL`{eU9$FDksC`2;+HBs0XrS*j#olz@Ou?PnX=q5swXLqIYU;Ss zdE7v3#XBJ4ZLMrrVX}W}DGSi_Ta@_kdDWWD{s!fFJeM!XB z#VWGe4ChgVmRjXRL>CK=!m&06SZ;cKbX!9#nQY~^3X*m!@;8$DpLQi=c6%bZr0~Hw zU{U(tBp|qH*m=e8$&yJ2>JYME-Z4^1T9Dvr#iu?@m5^k@@GZq4;ur&fKt(amN~jp4 zDb4LdC&kOCjU=|U39Odxp)leIm#nQS8t)SrbNtf=9&POvFl}(mAb6N&5g)&^O?ddf zCp-WisqE;stbvr5z$d)XmXkZ;t2^;LiV+_vvbqk&(v)X*iZR%p$sX>MEmY-HsqF%j z&+V9(RW2;3Dq6MVOVhOQpscNqp!U~Cm*Ksv&U>WfYCP(M@DS}#gp4y3Nm>lWelIzP zW|oxAV(w6c0XJg@)k;5?CV4r|gi$ZEW*6`1I14M|%SJTF6u}Cntz*L~rj6W0JA_O{ z%7r-=*$e?fuy6qI?s+|mz+mvmrrb${{l)JgovL?dqC6T;9BNN^?ck{=ybkb{M}M8H zK>w1kgh9UTVcz|179*I97Z_mtgGT`$jwE=A7-BLo&*1mg%i+y}&}}EY1)z>$YBH?s zC3$%~oVoeiJ6mPs@(XX6OK2|Bf3*1pi!tJp<@q>s`#QROA3mM(^-3SuV9^i=>4@_#?IcR@Z?ks-8Ql~lE51tbCD}7B>}4`q&1G!7}|aQ zz~h*TlJsAF4xw`ThU2P1S8l z+VAOJK#K`<(QGGYw}dj}Cn%#Sz08#v7w45dptsT6ExuX4k7fV#ejk53wITd0f?JiM zrlv6xTIm&g0Zl@moOPlyZ9`o}6YwSMqKic>E}#_;x>CDzqP8^x;BXP7`Eu)hpmAJJ@NVA1^oCR3kt*7*Wu#G7K&T<_h+^ z%??DC9VpUjX>KJgj%y4xy`x5(k=M474MqY}nMByB0wV!dUq(KAy49p#;l5)3REdG$ zVj{b&Ojc~)2_VdclZ*zLUe;qf>Q6;70(h9y=PFujcLJFl2KJMAV;P*rMjT zGW)zd4#<+JjsC1#=221TQDa`;hsR+_JWyD|KXi^5)Y22!z)ZaMZej~z;34R^8u<$4 zE6Htt{W1g)12l;E=nOW;?*$iL*Qp=VnKA_t^*yie7x!KARj5uShG|TJx0CHTetv*O z{*gC)=&KT*pl2d{$g5m_cnVp-7RmxP(52FV4Hey;I;1 zF^Tbh2j4$%MQn~ecmWrJ-+&V3=L;2=gHK)E60zS{9D2ZG)_WeXICa1Dh5N$3k-VhOsDIUk^cI32cne(WJp+C^LG;(n-XJnW z5Rbj^)|sC=LG;r1`egwgoJHLr8eFL11l}Yf0p>%{S*j=0Ve6FP0Nq0$s7BBVQD#zd{Yw`$In9$=;+;#d}A5XAAMuuYlr)|7Zir2_isVe zYZ|*}00^$bL&>r8FydPVw5$5Yim{wlyvyn_70s7t;z4^gw1p1E=J4;a*z~D99Nd*Q zs%^CjHZ{=`BO)HODB=IpPjasQ-F`pb%?)lCpa6j8m6$&i5!RY-nDx=t;`L6=X^|-nVUO zicw-GYQh^}8fPC4@QCd(9M>4cZgTP3xup0t%ai*x;rc|-7E8GhZ-g1zlXXRa$?7k` zB=vhWI7u-GW>}B33E<`G>xlsN`I|=~7}T1M3l?)$+bPIxPYbt2#;}PH*g5v?b&|*z}!N7-iq%*PQ^KX|#^jWwo z&(!7D<8#~!(&2KP8 z2n^LRslj67*m+&|?|*#@6YPMXD2Dl--3fg5Bj~NsZp0@nae$WVH8$`evW|ct-Hyhd zH8cJOwIv!n!TNh}8r_gR!#sPA3C3}tS97YUIUcvIQ&D`wCyfyH&LNk$HyTc)*(N2C zE#E{TGCl13d~3DH^`XSD3Y>=Mb<}}P{||OP2!X%-bb=c~x4D?0ZP%^u1^&Uus6TIS z-P>;>p_%7~*IrOO`0P*buqv2tyj?&IA8>;^+<*~x0JQ(Q5GZ;160-M!H@M>!h`j-{ z@_|>NH1R=KIP40@g4Y!hU`z7-`0>t*e^4an5mwjpSj7@5D_TK-Xf(8Vt|oNhvw|H^ z?+P?`t08pZG=deJX8+CAdgYCV_%2-Wj*GKUv;Xf>j-B~mo#X#3Mp#+@{}dy#?*CDY zoT@v70HgR86#*Ek7)uCnOjvtgT>RCc)`c9YO`IB_|3I0$yuOUF{${>H1 zACwG4kS;d?MWSfvOUAb}=4;GJHLdhgHRm)_h`myyVN2#vb?#w1y&8$x)Ze_Qu~50% z5s{Rbl+OHW+(lMW2_okl4W+4?CuQ-}ZV8+FlUTzpZIJz!aYQtYA=?H1C zKVTZtb!|Y--0bD13_YMMH!nMCamM?IdXzKRL8rB8>YlH~+Ji?%fF7aJKLdx>dHrI^ zKZ9ZSv{NO>t&4ytKnzdk9P0vUpl^nRbK+x)UB+CsXOn}6dyWPD)mrJMRs;$(fT0m@ z7hV|^H6r)6_6ytLwLp+c#dDzIwa@-%gb1T&nGU+Paj|u3i*A4af_npW996mPL*D#k z1Xg##g{5rgy*C3jWsyX@Jb|IdzB?JxIB3s{MhAW>Xx?IhU}2;gyKVQ0eAMR@(xJj` z$*50GsV>+WrImAt4aIprB@(!9>)>Hc4p#Z4_<_qSnE$gA29DJ$m@tO0=5riGe!)#M zy~)i=N{GNNhbLY;r%mxLCpf&)2k)r&n_WZ|RNLENVl_2%H)-<0PZ`)L-yBdllqX;c)m|p1?B@w0E zi9z6i+_Ok^HCGsdMmcBk40kHzz~u(6tQ7BlrDF}wv70|F)g3!cla_W_tug*p%Em60 zEHeOSAntiyc1yJbrVFK`nX6%~iK*=*oRyec)OSK*J_D|RMk#iFXx;vuujAuJtz|Q$ zMcxZ~wPk+m>O9c5(w=^^$KDM+Es!yY9Ppbk1B*((we6KI1aE-~XVc@uU;-dOk6faP zhE?YY`4#x>4b%Oi(oB8sNXSYKw!8t0weEz{|8bvVRk|Vpi}6e}u{-&Gi_a$OY2p|X zwSUog-MzuV@eY<(sWAO-9y#mZcv7r?O&0&(<9{FBvQ-{%+9dd^!A0#d%*+THVq)!d zA+yoEC}i@7v?^o#$iGFWgU$rMW^BcT6EM%Y$`ATg0g=)1__Oz$ymK5&`MQ1^5(rA{` zwi3#=88=5WDhelM>I_nzUQ||1Xh|Z>F6WMvQK}A==;$ioVzkNuT0yOtxgl7m&AN`B zd6rYsTXLyDu@Ns7_Znc8^eqi$mfwjZ)8nF%F!u^jLJZ75k2Odkp)rdG!(`5Ckk46a zAD|>&xqj%HYR>hBB`w#pfl<3SxlZB~rfl;lvd7)I^;a;PTIp~miGMtqFI)y(+*)?Z zY-y7jG&-72!jAp(-k`36!F!A2tk5`?sePOaoM1#9Qnvf~ePT4KamhyD9g@@2W;>IL zI8g=hny?Zu8aS#==XuqEKe5TFcp0HQWCNc`!$a8_L)u}2DAh?MMcCVlI-XU`vnyN~ z_tr)N`NB(S@Tzq74%gNF^r6I)+x%)Dpgy($OZ0Gxs~^nddVsR%gN5@Xr%YUVnhHdq zo1P8L&Q=@y4*)IqchKMA=x-b=&cED*e~P12RcU7|PK4eMbq6VtNs6QsUCu-go9-#D zih$MBDCY$5%*=a&5`m1{n>N;(U;U(T-hx@z@?W2wZ#;dk9=H#Jg3&6)!EX-)#w_xS zf<#1Mh7dU@%uRr6 zvDJg5HbyPTkA``Q7K%95GwJq1qXkIT^vLzgTRpE1F6L|3u2;k%Ak-Df7e)*FYr{F$ zsi0yag%bmLo?v3*Ex>1HJm=;jtMxXnb`}8mqvKL6n0Sd!O?YT4E)dD_w+3$az8E)Q zp#=dDnfEin7pN;N39#@Wp@uIHEA&mVAwtblgthRFWalp8OboQo%}BMoi(*_^o@(X}D%*z><&| zV~c<*@2*Wx+bVUDX3^iL$E&tl)oAN4a!+LP#d*+EME_V8!V3>Xcx~*_m>hjENJda= z4vwoTbDm5-`9WkMkP2n*hak_t!v8v4IaqfEpGA!eyk9_ltV~pE@kYVnTJd!qB94mC zv8BDlT>T!B1Ac-nP8taTvqpeurw-H>=30u$A$tpq%dclJ=7QElhh(|#m$G+bw=x-b zP?=}po8BhHx^5-T;T_wFa7QQ^upD&b-p_PHXFS&2*!+RB7Oq9dF8a|AHsk`K6*>9N zu^4zrwje);>}nlKKX{G5xw#h@Pb#?v4m#x_I?*jZp+xXOxk{{wf@70BP( zpA5d5<7kinstIbCth{9d?gEd^GwkrQ9c)j%478d}m4gqH2cDL&jaiis=<2-+T-VUr z$rYZP=6=Yxv}^ACqxP+vR@CMFvxu!OT{3tp37}W|RW9Ab!oh)l{CU8yCaeSc7Z@yud>dZwQ@C zL!20!>BFaYn*DRa3sNW%RY-YH{P6sG9uDu>llazKJ?Y}yZ8K@2k2*po-}FV8ntkZ` zknZtwde_E&6BobP7tbH)*IDu6N(ikY&(?>rv|EyIq<(U(HCx{94eP@yqnivNLIGra&BrHAl0p$BvpY*UbIuH?IqY?90=B zKU~L@Tl#Xr+|=0?YK2bDQ*y)fXZq}+%f1P zuT`8zIDRVX;#Xg7>0d#XOM98Z_^GIww}C^yhTskiY*Uhp>I*H2`dj9C_k1HUWQPES zffvW2n1{9a1?SaxItE>9#e0#Tw&)!Jy$5=T^&XC^UU0lFhK~zriBa|eMQ|^+ zs^!~vF(T2F?KOb$7|r+K1;wjzvEQs52qvygG$L66eE^$)=On1g*%9NnpXJd=|LO~? zvKlX~h66GdpfVT&acxwrarIkD6ODzN?R;*PT~xVbbY!{{tSKD}EsBLqk}?FcICUl5 z*>f&K0B2{#!4E(!_d%*7SzfhAJW=gTo*daHXBI(&8)2QRzDk(TfRigo{ONN zzQCa-ZSTX}F|kN4)o*AzduE*{?1NB4(cmnB@6{$csu1#y+(B6c--y5u>_i1{M_8F> zXKnbW;`x{m5!e8S5ZVr-EpShVU;@~AdVVcZI8>WmmzWXWyOVI{#Win;b-h=Px%UJy zk!q^{<0ISb)l`cazX|G#=Lq^4*m=NjzBnEz?SRzCpNG+XRy;cPqQMWftpECYa!SM? zr*fEFPbHoLG?CXWMjW*pb+FG&p%TN)@mM{Tbq^?|@)-m23_d7+xsz4T88 zce|sp%0B(1QLt5OvxoQu@3JyC~rFvkJh{ZflRY_X0P%`Jvzy%_f}u*pDRV1+*9 zvBEpVv4^f~80T{gRf#9S>fY@kH6@4amo0iW9tgzs-2R2UvcdLx2%XXqfYO%7ez$uM zTxl5_sLw@SfKCNClpBzS4aMPr{_GtIB6{p=gwP~nwTl$uVOKcl)t2?__s1_bITD|G z$D!oF&U%_0G4^_bz1LeWS_&LJ0E0R^nS$tMw@&kOlQP2Y{wb390fBwf!c$-<1YrHP z+5FgZKbYS%1+gz1V-N1I%+;Qtu}Dt<6E+Sta1ag6`C*731z3e!RSy7}li71hXfnPm zEZC%LOa}vZfr(UT=|6CJ=w?CibXt1g32?&H&XBS=$+(nOj7aa6{`N29rH+Xc+9r1%a|KIjS`nvmNc=C5dzp?#Fs2?mx$`)@o%Uq6FF z;TkwdLSKQ+2Q)}%;ADIcN<7n8ZwaelHw`<+nUMLZJiNspf>3S%#EJ9udt+*_LdR%f z;*uK}7!-g4Y2*+weMa2Z#HjDmu7p1FF2@_04V;#QGW4q0^0FRC^s(`dn+HNj7QNO8+CYoia;@)kt2TLzupSR#E_h2sZfI;k{Ra2E9V$P zhxkozCb`D{l3IAOHb&TW5I~H0dI$A=1V96nz}bG?owbBjdlZ*W2LL^{OaJ2<5C?p< zWaX7qYytxg`5d3PfWbf?<9mJY+(kVug5EvPJLNs$03i%FMgIRpKH*?r6Q+PVh) z5eKTP01*%kahp4L@UF`v@@z@gQ2<)Tx_=dnx0T}n;5$>Ui`}lSHPLYQ!pMy;dT5>& zbo}J=g*6s;CMp<&H=EUrmBq=G4av<9pDT`!ju&=s96CeZ4Rf+K9K^5aJZ~AJ{NEAQE9B zE6FdS{=`)MzOSHSOli8)Vlnq%i^s!@Fh9+n*FWs3JF1SVX2;uB-s$L)IqB!MO_SFd4j%jqnMLme4bPOJz z7B1#GFxjZg0aSM>`F_E`v(Lq%c&e^R$n3Sl#Pz%AiomnM#&ph>IN~r2k zIH{$Mswg&5_AvS_Y-V#P`Dr57i7aaIW13l|zu`T|CJs$iLFc;LE~@l2=_Z2gbMAlR z?LA1+nUPYx8&Ha{Heu5a=K;5^erh*0n(dpa7`~B8GOgBwZNqw87j)6#uw7}s?!a~K zbMHK^#(D>zF!AnU^*JB!ppu@kx9PEl(dNxOAWgTuW;ehm02=OcL;Pi8o9Lal(w!aH zCORjoQ*f}fng`ln4yzR9_W2I!t)5sCU2^-P$Z_=G`jMbda@5G_yeDAt#WJ_N zt}x#0Az7x5UnNwsnjculWVA!Pow5up{~DPTtP}(%+}9Ww7vqgMr&P~_&LNM)b!+4s zN365&ng#T8**olxd0w11S|IerMAC55Q7kjHx6|SQ;hc@}W-g%h1XSbA+8;${0cY^^ z;ycW4f!9FrRQv1y7UNj=YBx>>tU&WjB*`c?32w=Io{I~Vd=E`A+0GNWv7gz={I0`2 z(N~w{F4y)`X#=tQ5M{8*9BR;JyrAiTPhiqp4KSgjjXf3=dj)CfEQ!(yam89VZ$aJZ z_Q88ZqE2GEv^ju?MX&OBKb-9o>^Q9iq;%Ah=f4UDm0$wUsOGKML>!&l7L4d<3qN#x z0$LE9NX+1-?z0Vt&Fx`<5`05Ze2&t?vms<`umdV_kObfeCWhky%U-76PLfsFn7~W5 zVZ0)Q$8ssQ=Ril{v|l1hs@>eWvx&!5m0Zj1s$E+BnM4g~^~hqAh2M{+I!q}d;&fVb z#Z`ZETunjrws2~9zlF;-L;3^HeSR%gH=E4S1vPheOXbDBM4L)5?~gSzob_d4^(=-& z1r)KvHHY$Xt<-Ag&W$|0^^2o+xudhLG}op3r{9)9&{ZBFwL}B-3aksKfY@Dr?<*b* zt8@a)tkt-!?U`g<;H)Baa7Ewcg;brC)Bp_JGef%86!Td|Mk`j#Tysf2JB%|2Msq3C z@HAWcFP^nGsYuhD);;2DM63g{pX$TVoPbd2NJP|392NB@X^MGuCeAdiA8e#b9RNWbVF(6C=^7X=&pd`tb1jJEG$ZUziU0~LH)ToFP7>mX zg?PfMKjM}(#~NM1w8P9u5q&Cd58S-i9}h}ED3+Fll##a0WyPEn2_{kqsRj+pXal)j zpQ|ew*$IiH$(w=BP`~CBM4Wo1?%&*K2nPnFy)))y(@ezSP(+IK6?&1BPX&QtO7P{5 zthN4OblRx0X6qRgR%y1<3ZNZ}j=hFe-oaZt$`WCKMcNx+u_Z)34LK|&Q$vjHbX}-^ zm>)yc__8ozev&re9WxUYvJ%$Nj21d~40LXNe*A3%m*Aa+8>{uoJK022mp+;j(ps1Dyb9B(ie zhVp?RQA;38*eF=ZB!dshTn!=cz0-iPPDcuwNty1U=AI2bk$onyeWd6kz-pKg<-X-@ zl&W5XfLc{S`^h!P5}^TD1pNM!qwTxM|Do(1n>78R@4vQf+jie=+vc?0J#E{zZB299 zwr$(Cok{ASN>ce&<(KOPoJUukT4(LGKD!oWHHDYJ%#>Na3TwO6u!a*KC zE;7|o9H2Rd`kgEK zdq~7n7YH^85CV9cXf7omwp4H6p12HdJ-Yc2oo@NsKiE-68_+3z-c6+YN?5ZjZN9Lv z>u1UGzHfxubC^F=3V=j1krn;C-&_0F_P@8X;(CTL=V^Tuh1yfZBwIeZt}d@fb&zrl zN22sK7{3v

@&!O9K3dY4=Y8aQ;tCyAn+ur;TP5-R5en)4BX8 zru+`^ruVi_iz#))evSSIo{2$a`0l0(R~x#!>2%DQA|~nixn3PRxA!-)8PK%{v7O52 z;;sdyr{J;O0kY{J9>*?q0zWfIhZ41XlY$_&yQ$HoN$1S#5V{HdvM1jr_7_JxnZGOYLf(fo8?+*CBnzl%W{rHIH#i9o_%z231+TXm7&or~=lC3Kq9Kd8#A z6sF zf{=59@OdrifsMKSRCMpCmTs46dH&9B85nTL+(hapDiJUCg+`yhocU7H6fAmRv&RSk z@-eygyyA5tH;^>SH7aNG7apJk`Sn_}H2bIIdHePTy#x;F&=2j>Es~>lCQXA~_PB2t z@byJ9k;vphSO4Sa>Hpev-6wZO2hd`h-Anvum?v;25#ZB>`^WP2PTb(C9UFdQ^;s_o zCGl8>LHU}FlnpELQzDb9p@1sWbK;@Negv=MLbrNJDD6OoP9l(CxRIQ*nwHgn~0U9itNj?Qkex+eHxYh z3VL-<4jOWLG(2mKd%1974JOJ z@AV4{{S-W)A4ih5nd}%U|JKgvgZlaqzxZ}09b=X3zrQf?UT|r5L)#0tjni+>AO({j zRNQr4c0#2E%aNRq9oISHRY|FSTLzECC^S5*?4ENcZg{c^dgB+FYCZtN?Z6h|W~h@A zTrD~N5(!UUoqvtW^5xb{B3f0e!eJDV;M8jv^+Vl;9fj3MPmSCrxT-4CIhyjguM28wkH$1)1fg2$ir+G+>9I;7n9_`sF0lH5-Z*p9 zO9#?6u0lI*jVOUL@Znp#(o$p3^M11fk&62jJPQ} zf2^Fgx~d+?zv#Ufi(u!0uQXRQ@_1WZ|9VZH-4YHPI4ybDtqv^1VAt=pZwUhGUFbe$ zxB5dJXa7hboMBqzX2AW03&DCs@P}a$ZyLM)bS)poiNdB&qmcZHdTk2 z%h5LCfg*n9?fm7nunk|z^GN(+M>}>l%{#uJg_W}QhZAE%Z{H7@1;#Qr(%&t4pGiY1 zwqTL5*$lf|BO+vq7UY(^NelZ=4MN3vl|a^1j;rtE?x{*k(MVr`#t1TuM%U%$*C>D+ z_MwY%Dc|bAWCZuZebVFE>h6*-i)X~GwpjsU0wy+!>`|%FnQq8w&JJGs7wk`)Jo?*^&?G{6) zi`!uScK`RB3iKp>Mglx_2dr zAEvRG6DX5I{MK=Q-MVV;#$iW6{vBwH?u?ZijI`uaEHtRW=Z8av5s+U`HrY3docH*( zzD$hRQ8+G4E*PrW*QJ`CI*|b<3A~y=8kn=iaXt9Ddb+$@7m4?`8V94u5xXFyZfV|- z4N*LG7Gxc`A(BlnCb`Z641(?5;B&EK`Q(U&0>7ylJ*OAi)rLzM(9+wm)uM(@DYGuE z`s8Y*12`a|4!j{?Z3R{M?iJlQo$h2h?&#)Y_H?!H%Cd@88>Z==y>&v&8njew`m#VW z>sx2TNE5qTy%Bv+JXqb(J|p8PCh3a zkyDh1hx==-bM>a&LtrmRv^~pPGu1G4(%YRc+iJmsX1BAfCJXG_2(qiW8sAQ*OSa$U zt;w#>RL3NN=G7Z&vD~a${9IFb(Qa&BSOj5 z$dJ9w13=~Cl!t|vkt#@AWUWaV!D9Hy0xXW-w!m#0mkNSOG)21HmC8VW;Vh0A{TdE5 zT>7E~aq^mQBDRsL1YUDHfX?N}PwoIek3ZFydO}8>-TPP6E=1r^1JHP|j4$UEFO*Jh zpJ;u#8=ZiRYy4TyZv*Y6=*oF3EU4zR`-}Yxfx%V2bJnHgl=Ep>cQ^yRZc!5KgVT0B z?{Qg@6Tljl+2x|YoL$l~Oo>JK%5T39-h*ey;?(>KYt&82L49KkHD}i;J6j$H=h}`s zmyM+w-DZ7Te3Z?&fc==VC%jKmyIo8)6^#l29_*@NB`vXHns_{lB0D2Xf2KP;vSiDQ z+*Lb`7gB$Jc2zh5A8ic14m!hIgyIa0pK)n}X>Y68y9u>`HP|^HUyS~U)#)J?B>Grf zHpB7}9#85(i)2~V_JHC0AXgt09KM+ZT6SY~5+u{rf?6|4xUd&^7?7#8Ft9t3O!(3Kg^|Z_`e<82q5?%IOC%~v1cF+Dj%?0|Gc0~l7bru@fUh`> z2D)E-+6h0LcA*yM+tLxeZr_Q_I&>*JD;-!JeG~5vA?eic^n~rVEZ!1Lbscz5%g6PB zHIurdxCaf&f%kIH1qW)xut~Chs|m5t=1DwZ>;c-BJ=b{iqw7%Q4>5!nn@hX8p0j`* zg7T5Y$m7>om2bIn7PKXNeb%v&0gr%B&N)6D7bg<}p&(7C%Iqzi!f92tpxBt89NIH# ziQH+qqRC%;+M^2;aGST66@Q{K+Ko5!E~qLLV`xPXS~B}<<7d&$Q+L%CIEs#+BrSNy zZ?AF&zQ&%GpI?Qq^qySzWY9kd0VuB%SiSvYn)&htZ5Y^r1N<-O6(`nbWN;EDV-fh9 zVP;m0-UIW4FoLTO`&i%(@4K|H8{Io+k37Wf^kT&|%8nHNdHsm%V@7rWCKuYhDB_llpfG=i+b@ZgNQFo5Gf54I$bn|AZ zs*vKRJykhnSJlPL`5F-C(fn_UUSt znpXc8&2$u?Qw=7W*aX!NCNDBcRf(p%&O0+qi;EC9Iwu9ZhXa1|R}1f2nsxZwQOuIv z5aYC}R7@4w29YDYU33u<#b1v|HoAt3DDTMn)9NiO3*+hFTkUh~A&*i;E)R&|wEMBw ze`FfgKilFamR~cv51~mSWy|eN*rz$6}eupO8Q$pJ3g1=B5O^;a?ju2PC%R@MInt4F52F5$uo$ z)yD^KWf5__gjE|@(9&CkRFb`AGXgrfS2)+o`l+O&xtLFd(YNpb-Kd6<2?-G zzFaY-Lx5_tOcA|AOk#Ae)gbw3M~NTxQ)bc#c!6yvO4Fv`ZQh~>A7{bKugMvCa^HKN zx(PU*u5!4=NTkpxt{-x#;Zz}F+Xyu*^a!%ESr4ssd+CS_J*onU)e^x(*zeuowAB!} z>8JCl^Q9kP7@j`-|Bxg9)$3$o`k&*8V*YQliR@}*((aP+F;|<~UlE+9v>iGrhd>Ft zNVF)k_srT~A6J-wOlI_tVDTbgkS%>q{@bVPA;g>cQ!`xK2SCr-66X;2=HZ(h2Bl$g zF=xm5@yo6s;&A$gfE%Hodm1@rvqL@f1E5>0VQs_jhQG(N;iErQLH(DdlQk$WH zfw4u>^8tZcK#f5<`^-kX$c#MX5ur z6B$z3_8T31S8tKhu@B$VHOjn92z$fY_(tn;^9blQ5)Zdzk3%l6(9mYn-wfJjT&}fn zNH5^K_{Jm)NGJw^(7jm2JR^U##Hbmw$*2KhdZ43RQ-GVmUi}A3IRH}ezA34co?vfpVTc_0Sn;=PWm&WVPAG_7y;&iMZH)9M{e2X^j%B z=2IcMnWxh04RA7OLc6A0)D`5hL>cD31M+;0#iQX!Oy6%btfM-pGZsvSz$x{OChZqw zXRBgcPuZmvVNoO;!2OJ6@q!1(&cQ2q-Ofg2Q-=_R)SUhG-jmd9>}AFZKGdg2h()NZRu*bJz%Th8V5!tkho}-n2*Z3?0jKG2<3xK2ZICy1>qf^ z->X@CYvh+Icp{TtqJlp#Bt}HY@n-}mc}q$x+laglcdq`SM98BFjwq0=2He_7<~j8aOBIsx_XS zc7lC57DzNQ7o;ucN8@!x7?6pz4r%eT*kdIOn(8>L)!$^{z=|S3+PM*9f_n$l=Be_% z#i@>3_;EPi_0sofpT-hf_!1)QnM>Fn*)&DMEEW1bUtj|cx zmplncO$}3bP_ZP3I9%qLp@ZLA$|umJWYvR*h}RjPkI5UU&FUCxERK`V?C|c3EP~rI z(-LS$)juPm1tQiVZrQeFqMqSpvzP+sHQEo4BDzvUgc^~c$B<~1SM6|4AR2|(;=e5d zri!>rUkO50Zh8pRtzel@jMBD!+Zuz-A#Z?&w{+E zxTh&C-w>R{V!d1tk*5-ZKQ0G6lXTS4&VZ>oq=j47V_@-Hkwj9eWlP$Us^&szn{Y&o zBhF+;hYjHy~lKNatJ}jGEF-|dcaAC^xP;~s*RZbjbGuSky&~%=cuOe zVjQ67#nTvoy2%?_e48{po9mPez=OD=_pj<(4pQJxDYg}>KhC8j4 zBdPvN6zv`CsajwhMGIn>GrYwIi19vf^Bkb82<@ffg{Tv8kRTAInOP*LH-IglZ}5gP z;vtc{tGf0IiC5lpQ#NduYW0hv&8Us^hr~i{VVWA8^fO9?d;7DgK5TDZS~m~)NMcxQ zgdtESY9fvJSz>KCOu6O3#=}p5R$fVIaEB~p=)pVmV2VPI3Cm_uewY)c*|*q$&rSXa3o^G+X!kW z6Stp`-=YhAxxFib1HsgG$eq6PiKlPiQNrS228w7O4}U_57a9b&aX_iV+OHWu!@~y! zR|L)79(~5`YrRiGABp$S0@$2vDOsM^?-j2H@X6Iwi znOe5BN<1?`Si`f3XvUH$BvHOP^Y#G(NxZW4O8k=z6^I1256{Qj^|8kctx=+Hrqt}Y zUcFEDGQp-*qI4PNQ14lx;rG&AQG4OX`_;KFs&yD91p=gK%AE6!A$IrCt!J0m`WULz zSvA1)cx|YEn5%Tat#G_mrxlj__pcO)!qa2KZ!%yBzGn73Y~e9Kxk6JGxyt&UFkI9^ zP%4o*Uw5z!h1&4MUWG(w^!Q-H3tJH2Gib~65wW)QU}9c2^1v`l-q8-1Oiz2eWQgrn z1XzV@vT7f4=opfNcd6)Vl$=ZYD7u*NNDY6GNKB!7KzRw@r)pMW(*k z<+vn~3!(Soqoef}?QJ@gyg!L9SGiY)&YqA1((pYDPnOZFo_?d=0*{W z>(-@}gNKa)_KZUFw3&K|k|iN^FJO5DF1co*T$&Oe!dNHhCuFDXc;l^I$m523?|0OO z{Eda&Se0HL^h&?`>F$zP|F_|_+GD~RpbYe)ZGlXKZ&7-YS7ZWbrK zA5k?tTTb?JU3e1Cr!4`4?Tc;}1ype-Ou&MXnMAA^LeK06d83BLcC^FQ*-}->S6?9u zBUtBH?@)mYk6_T&37CK0Bh~Dv2uv6-;(XEQ!P+1GZ1ONlJLwFpmF((TI=oeuCrHlr| z*S6G|AaM_Zje$t`9|4-R<;nZJ%=V5pE}+0Hdd>YXA(oD3NS|Oj7gy@e_;=>gj$Du* zUa7S_iT<7V5SDYbgD2QSuq{6YjU$4XKfKb?X}P~#!78tb4zas1|8-5%EL|IIb*e8` zq`0UVrTvTYF14iPTW7?8KH*e|0|PAwIc2RQZu;Kjl_zT;xK;5*0~3OMQ>$x|yZ$1g zY>mH4onw}zo|$hw4xW_#X_+5-6T=8ZRZ>GzPhFj_Jmy9W*TyET9FmCmC{g?4##N~t zh;ctl>tnQw`R4W%aT8H+*6RpY;U~j^dEC>Ib{IgIjrF?9bC26UA6I|-`{6W@6TwkI zP-=h(7Unz;S!{#r7%0dEQmK0P+sMRQ!JWrxBbOUubxpjJ;&kwHDj(u4=GsBRa%5U6 zR2k)VyEoj0$@yFtQ5^=KB-m5whMW?w#tk;_l(#8CAu?fOEL@yam+Pm;mN10bD9*dk z02=)?1PIy40${m2+BGRuqj#`AfK{p+3{uhoQMW~OHAwc)cw=A|TseK?_K+)X5ZqQM zElBG%I}2pQr8b}EX-JNo`$qh&hWu*S_Sl@~KQQ6J0je-xy}g46U9ceS6JAey78QET z<%%aM(hx={xiJB52Dm@g8=j zrL_H1JN8D`|2@hok20$*L*PuVfC&*?x-4)Pn;0GqK)J=Hk)x!stR@M@?SW6S`=vMS zAisZBzVhtKq~%~=O${ddBkZr1u8X&Ln_&$ZT>}5qlJIhIrXh(oNNI%C3biVI;yn3> zvV4Aq_hdOZ-+M-M>u1TpclI};RmLx-*tY!s&XBr@j~rYzBUf)Qez}8ucst$Ppr->3 z8@HTS%t6d{iNQ3kmRb(7@sQb_2bserzwAKP!}Juqiu#m?o(!bq&4ug$N_>^{qSNcM z&pMZd*Fr~)v{OWJDE@o8i;s=%Jru`mcM-38muw2yZYx1dgftd}6IkmF>@zfja4ek- z`~Y4f07g7<>M*osPyqM$YD5GZ)8UeUo>L?vF9s(0VxF&n5)13))wf6p97#FHy%u!EZ-bGNr$JKiYFSh0 z9mDXD+`?;Eq@YC+8EPEK6KFVu*x7~y8%?uqLNNHl{C{SACf#+M@u9D8#4^$(A6hJ{ z)_K$UZYciB?uhp64T^-hU_g^$yS|B>#buMjm7alWFbn(CKh_#_*T?JQwZL3%e9V8i z5nTUKW?*4v`TsE-(MsBAMfQEEP4{6@HFfZaiY8P(pw`#9TWu<$p+rCV-=mVqQcPfM zjn6A(h=TK%j-5>km$uFa=e3BpQ}07F|LllTxN+WTyXB57H7qm_-=rZaB=WPV_V%}y z*KeP@yeCG1o{2=-ApmtCZ>*j0vd72FQ*Dde1>~|f%h7x#!vQ)JDhE^ZS<5nAs#H3( zPFz$u(tB&IQhbo0HbG`_UF6tCw_{P!CV{qTHv3wIPtk`4QMmNqXI4|;7n>r2CHMZV zzSuM+v}CPgj(JspUuvX|@nKz4g^{b%{!LNn5snYrWQCqf3`?sja%63j4L&Ng%EaBS zgvrhEnR8F}uhoi#e*gw|mozj$Wk}cmKad1QQbi|B=)0v=Wulbg+O8Ilii_xMa1o@(Df75zx*ko8w zTCYm&yMdsLLGV~s>%+|$omVa$<5l(kI3(<;5q`kLLtR!NF(4p!jMhu|zZU9nvApFM zOMu$X7QZJY)iF;wA3tb zYP|Rgwdz4Y&>06XbH^&3%8$dC3O}Ih-@hZt1sn$`J6=%1FT_NtNw*Qh8!9h#By3j~ zaXw@~aa2_F0N3}6&eAbZOk{t-E=t3-pLsx1wXFL<6EoQ)e|CZQIEC4 zo!mbP$zxRJx7Wz4VwIXz@>)7zL@Xm~$knC(sF6~4`xueB!q4?9^>G&ZW zv;E4gSO>w0x@Em0W}m$8PyDy=;qx(hRez?H@`W7xvE1tjeU@OIJQ4~?#td>Tnl4`w zG=gms(Z=koMu>mBbmw#|#*?0N>KC7Iz164~QO;w6Jp!o;XxD(OCbDfG=WHf^dSZN6 z{$j{JXqbgKG?+lqpgJn`R^!Cf+`2~hz(99yyYy1C=lhw04?&6tlK%TRPc`JV)1b?# zuch|ylRiaJA(j>98f(Z0A%x9uvK2J~362wuh$mzrdq#j;p^0O~rEPjZ5XF;FQY5F9 zjaGWZ%RT39+`E#5;ETvomQEmmnz};jlmv^4YN)gAy!A+u;SY5mvmOrAfEw_~wAz-0 z*8645f)jmb=ViWZOwOg3+m7yL_H@$J&KUuLmiT&TJH;dH3$z$keEdwF`o zKk^vUh+{C3X-npN5j}|lhk|Y!=j&sO@ba z5!Jw={mrWr^ytNGb3aIZJJtyQx+a0Gg<%$%Y=Z0VVy$=hH9)#3 zveeG&p}>V-{IkDo#5)-R%Igw&isx(FR~}xpyT3@n_^r8XTL&9s$YZ>qG7@i$T~Ali zG48_*8F2TbOEWt3%c z7EM(B4nB^A1DQBnXw@Do%*?>sD|oI8TQ=Wr=VJ=NYhZYcecBSgk^{V=Xw9k!n<5Ca z-2$Us7m5A8lK2Wq+{m27ONF6C_bZeo)_f98i@`Rgp~VKmUh)mes)>ew{LS^XcSt@|!Jmx#G?R$e9poRzRa7VEOSsI5 zQn!Qj{z@fzzJ}`-)sgI{2GuEqBOW)<#}$~L0!yi%S3!{{fmfyaFyW9Eb+pT*{x45S zAXptP-R%f~uooHp_%qd6PK}##q{0b;Zol7yfoQY(O^PXAe%uKOcCvV*4T9?;{(yG?G1tghvmHY7!r z_;_8{_Cfl3FLv|&^?GMZh$)7ZLXPs6q(BRKtIV=-V?+y0_plMwbXF55Gom{vQN@jT z{hU$slP#P3w+_x@y9&qV6)H0L4vO{H?lK8{C0o}<6rc*D;XC@^uk{27YIVCU6;|fE_uIzkiHr-=SYO8KlP)5T=S)qdj{(20)k{QH1C4u=wedUS| zk0>h0Gx5osFWUgjn&(Rknfwg2kW?lSeR&eJ9gZ@mkI^nBT3m-REK}lm)QFy5jSN0k ze4Bs=M?mn7@Z zM5=MAQybpwoOzJrx=gM9E+9Vs986y1nxtZHLrq^iU4!n9(4$#&O(-^1w3sv%WP+jQ zXcx5087sV24%GQ3j`$Qy33`MoXsuroEEQ1B6${hNM}#2GEkm|Lujs5twkxYgsk?^o zq^Akb6&@^JZ>9R{q!y&9f>dIIHdJ}{K30%V>HBmsHnl4I9}`1N=UY;vY4POSHSY-&Uk