├── .github └── workflows │ └── build-test.yml ├── .gitignore ├── CHANGELOG.org ├── Cask ├── Demo ├── Demo.org ├── Demo.pdf └── Notes.org ├── LICENSE ├── Makefile ├── README.new.org ├── README.org ├── docs ├── README-djvu.org ├── README-orig.org ├── org-noter-demo.org ├── org_noter_tech_notes.org └── pre-merge_notes.org ├── emacs-devel.el ├── modules ├── org-noter-djvu.el ├── org-noter-nov.el └── org-noter-pdf.el ├── org-noter-core.el ├── org-noter-test-utils.el ├── org-noter.el ├── other ├── org-noter-citar.el ├── org-noter-dynamic-block.el └── org-noter-nov-overlay.el └── tests ├── org-noter-core-tests.el ├── org-noter-extra-tests.el ├── org-noter-location-tests.el └── org-noter-pdf-tests.el /.github/workflows/build-test.yml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v1 8 | - uses: actions/setup-python@v2 9 | with: 10 | python-version: '3.10' 11 | architecture: 'x64' 12 | - uses: purcell/setup-emacs@master 13 | with: 14 | version: '28.1' 15 | 16 | - uses: conao3/setup-cask@master 17 | with: 18 | version: 'snapshot' 19 | 20 | - name: Install dependencies 21 | run: cask 22 | 23 | - name: Run tests 24 | run: cask exec buttercup -L . 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/elisp 2 | ### Elisp ### 3 | # Compiled 4 | *.elc 5 | 6 | # Packaging 7 | .cask 8 | 9 | # Backup files 10 | *~ 11 | 12 | # Undo-tree save-files 13 | *.~undo-tree 14 | -------------------------------------------------------------------------------- /CHANGELOG.org: -------------------------------------------------------------------------------- 1 | * Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [[https://keepachangelog.comfiri/en/1.0.0/][Keep a 5 | Changelog]], and this project adheres to 6 | [[https://semver.org/spec/v2.0.0.html][Semantic Versioning]]. 7 | ** Unreleased 8 | *** Added 9 | - ~org-noter-property-location-lock~ (=NOTER_PAGE_LOCK=) property that 10 | inhibit ~org-noter-set-start-location~ from changing the location of a note. 11 | *** Fixed 12 | - Symbol definition is void error from [[file:other/org-noter-citar.el][org-noter-citar.el]] by removing ~citar--ensure-entries~. 13 | *** Removed 14 | - =org-noter-integration.el= that was copied from the Xi Fu's [[https://github.com/fuxialexander/org-pdftools][org-pdftools]] (see [[https://github.com/weirdNox/org-noter/pull/75][#75]]). If user wants the same functionality they can use [[https://github.com/fuxialexander/org-pdftools/blob/master/org-noter-pdftools.el][org-noter-pdftools.el]] (see [[https://github.com/fuxialexander/org-pdftools#installation][org-pdftools]] installation guide). 15 | 16 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/1.14.0...1.15.0][1.15.0]] [2022-05-31 Tue] 17 | *** Added 18 | - Option ~org-noter-create-skeleton-functions~, list of function that 19 | inserts a tree of headlines according to the outline of the document. 20 | *** Changed 21 | - Move skeleton functions to their own files. 22 | *** Renamed / Deprecated 23 | - ~org-noter-set-up-document-handler~ -> ~org-noter-set-up-document-hook~. 24 | *** Removed 25 | - Revert using ~org-element-cache-map~. 26 | *** Fixed 27 | - Not recognizing location in file-level property. 28 | - Wrong number of arguments error in ~org-noter--doc-approx-location~. 29 | - Not parsing the document property before comparing to document path. 30 | - org-noter--get-view-info don't work with drawer as root. 31 | - doc-mode and note-mode overlapping when using a single buffer. 32 | - Before first heading error in ~org-noter--focus-notes-region~. 33 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/1.13.0...1.14.0][1.14.0]] [2022-04-12 Tue] 34 | *** Added 35 | - Allow links as document property. 36 | - Option ~org-noter-open-document-functions~, list of functions that 37 | gives a buffer when passed with a document property. 38 | - [[file:other/org-noter-citar.el][org-noter-citar.el]] also give URL as document. 39 | - Handle single window layout when opening a URL. 40 | *** Changed 41 | - Use ~org-noter--get-or-read-document-property~ in 42 | ~org-noter-sync-current-note~. 43 | - Remove org-roam-bibtex dependency in [[file:other/org-noter-citar.el][org-noter-citar.el]]. 44 | *** Removed 45 | - ~org-noter--doc-approx-location-cons~ 46 | *** Fixed 47 | - Fixes for org-pdftools location link in [[https://github.com/weirdNox/org-noter/pull/93][#93]]. 48 | - Killing the base buffer of note file when 49 | ~org-noter-use-indirect-buffer~ is =nil=. 50 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/1.12.0...1.13.0][1.13.0]] [2022-03-11 Fri] 51 | *** Added 52 | - Option ~org-noter-get-buffer-file-name-hook~, a list of functions that when passed a major mode, will return the current buffer file name. 53 | - Option ~org-noter-set-up-document-handler~ 54 | - Option ~org-noter--doc-approx-location-hook~ 55 | - Option ~org-noter--get-current-view-hook~ 56 | - Option ~org-noter-get-selected-text-hook~ 57 | *** Changed 58 | - Modularization of all document formats 59 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/1.11.0...1.12.0][1.12.0]] [2022-01-17 Mon] 60 | *** Added 61 | - [[file:other/org-noter-dynamic-block.el][org-noter-dynamic-block.el]] allows using dynamic block as a note. 62 | The dynamic block have to be in the format of 63 | #+begin_example 64 | #+BEGIN: note PRECISE-LOCATION 65 | NOTE 66 | #+END: 67 | #+end_example 68 | - Option ~org-noter--get-containing-element-hook~, list of functions 69 | that will be called by ~org-noter--get-containing-element~ to get the 70 | org element of the note. 71 | - Option ~org-noter--get-location-property-hook~, a list of functions 72 | that will return the note location of an org element. 73 | *** Changed 74 | - Delegates getting the org element of the note to 75 | ~org-noter--get-containing-element~. 76 | - Using edges as precise location for pdf files. 77 | *** Fixed 78 | - "Before first heading" error when starting a session in a file with 79 | no headings. 80 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/1.10.0...1.11.0][1.11.0]] [2022-01-14 Fri] 81 | *** Added 82 | - Support an org file with no headings. 83 | - [[file:other/org-noter-citar.el][org-noter-citar.el]] a module that helps finding note files via [[https://github.com/emacs-citar/citar][citar]]. 84 | - Speed up page turning by using ~org-element-cache-map~. [YANKED] 85 | - Option ~org-noter-parse-document-property-hook~, a list of functions 86 | that return a file name for the value of the property 87 | ~org-noter-property-doc-file~. 88 | - Option ~org-noter-prefer-root-as-file-level~ (default =nil=) When 89 | non-nil, org-noter will always try to return the file-level property 90 | drawer even when there are headings. 91 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/1.9.0...1.10.0][1.10.0]] [2021-12-26 Sun] 92 | *** Added 93 | - Option ~org-noter-swap-window~ (default =nil=) 94 | - Option ~org-noter-disable-narrowing~ (default =nil=) 95 | - Option ~org-noter-use-indirect-buffer~ (default =t=) 96 | - Option ~org-noter-find-additional-notes-functions~, a list of 97 | functions that when given a document file path as argument, give out 98 | an org note file path. 99 | *** Changed 100 | - Org-noter can be called without headline. 101 | - Separate core functions to [[file:org-noter-core.el][org-noter-core.el]]. 102 | *** Fixed 103 | - Setting property drawer to read-only when there is no drawer. 104 | - Emacs crashing when note buffer is not setup. 105 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/1.8.0...1.9.0][1.9.0]] [2021-11-21 Sun] 106 | *** Added 107 | - ~org-noter-create-skeleton-epub~ for nov-mode from 108 | https://github.com/yuchen-lea/org-noter-plus. 109 | *** Renamed 110 | - ~get-location-page -> ~org-noter--get-location-page~ 111 | *** Fixed 112 | - ~org-noter-nov-overlay.el~ only make overlays when the page matches. 113 | - ~org-noter-nov-overlay.el~ only make overlays when the location is precise. 114 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/1.7.0...1.8.0][1.8.0]] [2021-09-15 Wed] 115 | *** Added 116 | - Allow keyword as a document property. 117 | #+begin_example 118 | #+NOTER_DOCUMENT: /path/to/doc.pdf 119 | #+end_example 120 | - Allow document property to be file-level 121 | #+begin_example 122 | :PROPERTIES: 123 | :NOTER_DOCUMENT: /path/to/doc.pdf 124 | :END: 125 | * First heading 126 | brah brah brah 127 | #+end_example 128 | - [[file:other/org-noter-nov-overlay.el][org-noter-nov-overlay.el]] highlight your notes with overlay in session for nov-mode. 129 | *** Changed 130 | - Use active region as precise info for djvu and epub files. 131 | *** Renamed 132 | - ~get-location-top -> ~org-noter--get-location-top~ 133 | - ~get-location-left~ -> ~org-noter--get-location-left~ 134 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/1.6.0...1.7.0][1.7.0]] [2021-01-13 Wed] 135 | *** Added 136 | - Modified version of ~pdf-util-tooltip-arrow~ called 137 | ~pdf-util-tooltip-arrow-with-image-left~ to handle x axis arrow. 138 | - ~get-location-page~, ~get-location-top~ & ~get-location-left~ to help with 139 | getting the value of precise location. 140 | *** Changed 141 | - The format for precise location of a pdf becomes =(PAGE X . Y)=. 142 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/1.5.0...1.6.0][1.6.0]] [2021-09-13 Mon] 143 | *** Added 144 | - ~org-noter-create-skeleton-djvu~ for creating skeleton for djvu files. 145 | *** Changed 146 | - ~org-noter--doc-goto-location~ also move point for djvu files. 147 | - ~org-noter-insert-note~ additionally take =NOTE-TITLE= as an argument. 148 | - Refactor most code of ~org-noter-create-skeleton~ to 149 | ~org-noter-create-skeleton-pdf~. 150 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/1.4.1...1.5.0][1.5.0]] [2021-09-04 Sat] 151 | *** Added 152 | - Support for [[https://en.wikipedia.org/wiki/DjVu][DjVu]] mode. 153 | - New [[file:README.org][README]] file. 154 | *** Changed 155 | - weirdNox's original README is in [[file:README-orig.org][README-orig.org]]. 156 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/1.4.0...1.4.1][1.4.1]][2019-10-25 Fri] 157 | *** Fixed 158 | - Missing quote in ~org-noter--get-precise-info~. 159 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/1.3.0...1.4.0][1.4.0]] [2019-09-29 Sun] 160 | *** Added 161 | - Option ~org-noter-insert-heading-hook~ for running functions after inserting notes. 162 | - Option ~org-noter-kill-frame-at-session-end~. 163 | - Command ~org-noter-jump-to-note~ to jump from annotation to org heading. 164 | - Support annotating attached document. 165 | - Use org-id for [[https://github.com/fuxialexander/org-pdftools][org-pdftools]] locations. 166 | - Use [[https://github.com/fuxialexander/org-pdftools][org-pdftools]] links by ~org-noter-create-skeleton~. 167 | - Integration with [[https://github.com/fuxialexander/org-pdftools][org-pdftools]] in [[./other/org-noter-integration][org-noter-integration.el]]. 168 | *** Fixed 169 | - Single frame deletion error. 170 | - Error about structs for Emacs < 26. 171 | - Narrowing heading with no children. 172 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/1.2.0...1.3.0][1.3.0]] [2018-10-26 Fri] 173 | *** Added 174 | - Option ~org-noter-doc-property-in-notes~ that make org-noter put document property to every new note. 175 | - Record session id as text properties in the text in note buffer. 176 | - Inserting note without question with the option ~org-noter-insert-note-no-questions~, the command 177 | ~org-noter-insert-note-toggle-no-questions~ can also be used with the keybinding =C-i=. 178 | *** Changed 179 | - ~org-noter-hide-other~ is set to true as a default. 180 | *** Fixed 181 | - Optimization of narrowing helper functions. 182 | - Killing buffer not from session. 183 | - Moving the point when focusing on notes 184 | - Unnecessary newlines with creating multiple notes. 185 | - Wrong root when creating a session in indirect buffer. 186 | - Let user insert space when creating note. 187 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/1.1.0...1.2.0][1.2.0]] [2018-08-16 Thu] 188 | *** Added 189 | - Option ~org-noter-closest-tipping-point~ can be overrode with the 190 | property =NOTER_CLOSEST_TIPPING_POINT= per file or set the value with 191 | ~org-noter-set-closest-tipping-point~. 192 | - Option ~org-noter-set-doc-split-fraction~ to control the size of the 193 | split can be overrode with the property =NOTER_DOCUMENT_SPLIT_FRACTION= 194 | or by setting ~org-noter-set-doc-split-fraction~. 195 | - ~org-noter--get-view-info~ to help with creating note. 196 | - ~only-prev~ behavior for ~org-noter-notes-window-behavior~. 197 | *** Changed 198 | - Refactor some code in ~org-noter--focus-notes-region~ to ~org-noter--show-note-entry~. 199 | - ~org-noter-insert-note~ no longer takes prefix argument. 200 | *** Renamed / Deprecated 201 | - ~org-noter--get-notes-for-current-view~ -> ~org-noter--get-current-view~ 202 | - ~org-noter-doc-split-percentage~ -> ~org-noter-doc-split-fraction~ 203 | *** Removed 204 | - ~org-noter--get-this-note-last-element~ 205 | - ~org-noter--get-this-note-end~ 206 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/1.1.0...1.1.1][1.1.1]][2018-06-26 Tue] 207 | *** Added 208 | - Option to choose ALL types of annotation in ~org-noter-create-skeleton~. 209 | - Option to choose both outline and annotation to generate skeleton. 210 | - Content and Comment of an annotation will be its children. 211 | - Subtree for URL in a page when creating a skeleton. 212 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/1.0.2...1.1.0][1.1.0]] [2018-06-18 Mon] 213 | *** Added 214 | - Fallback to using ~buffer-file-truename~ when a mode kill ~buffer-file-name~. 215 | - Using region as precise location. 216 | - Toggle ~org-noter-always-create-frame~ by calling ~org-noter~ with prefix argument 0. 217 | - Showing arrow when syncing notes with ~org-noter--arrow-location~ and 218 | ~org-noter--show-arrow~. 219 | - Option ~org-noter-arrow-delay~ to set the delay for arrow. 220 | - Option ~org-noter-notes-search-path~ to search for an org mode when 221 | start a session for a document. 222 | - Option ~org-noter-insert-selected-text-inside-note~ to make 223 | ~org-noter-insert-note~ insert the selected text to the note heading 224 | aswell. 225 | *** Changed 226 | - Convert edges of precise location of annotations in 227 | ~org-noter-create-skeleton~ to region format with 228 | ~org-noter--pdf-tools-edges-to-region~. 229 | *** Fixed 230 | - Wrong order of outline ([[https://github.com/weirdNox/org-noter/issues/23][#23]]). 231 | - Unable to create a session from EPUB by using ~nov-file-name~. 232 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/1.0.1...1.0.2][1.0.2]] [2018-05-12 Sat] 233 | *** Added 234 | - Using selection as the default title 235 | *** Changed 236 | - The outline created by ~org-noter-create-skeleton~ no longer separated with newline. 237 | *** Fixed 238 | - ~assert~ Symbol's function definition is void error. 239 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/1.0...1.0.1][1.0.1]] [2018-03-31 Sat] 240 | *** Added 241 | - [[https://stable.melpa.org/][MELPA Stable]] badge in [[file:README.org]] 242 | *** Changed 243 | - Use if-elseif in ~org-noter~. 244 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/0.12.0...1.0][1.0]] [2018-03-24 Sat] 245 | *** Added 246 | - Support for [[https://depp.brause.cc/nov.el/][nov.el]]. 247 | - Starting a session from the document. If the note file is not found, 248 | it will be created. The name of the new file is chose by the user 249 | from ~org-noter-default-notes-file-names~. 250 | - ~org-noter-create-skeleton~ can generate annotations as well. 251 | - The option ~org-noter-separate-notes-from-heading~ to insert an empty line after creating a note. 252 | - ~org-noter--note-in-view~, ~org-noter--count-notes~ & 253 | ~org-noter--get-notes-for-current-view~ as helper functions to show 254 | note count in mode line. 255 | *** Changed 256 | - ~org-noter-kill-session~ will also remove hooks and advisors related 257 | to org-noter. 258 | *** Renamed / Deprecated 259 | - ~org-noter--page-property~ -> ~org-noter--location-property~ 260 | - ~org-noter--compare-page-cons~ -> ~org-noter--compare-location-cons~ 261 | - ~org-noter-property-note-page~ -> ~org-noter-property-note-location~ 262 | - ~org-noter--page-change-handler~ -> ~org-noter--doc-page-change-handler~ 263 | - ~org-noter--ask-scroll-percentage~ -> ~org-noter--ask-precise-location~ 264 | - ~org-noter-sync-prev-page~ -> ~org-noter-sync-prev-page-or-chapter~ 265 | - ~org-noter-sync-current-page~ -> ~org-noter-sync-current-page-or-chapter~ 266 | - ~org-noter-sync-next-page~ -> ~org-noter-sync-next-page-or-chapter~ 267 | - ~org-noter--inhibit-page-handler~ -> ~org-noter--inhibit-location-change-handler~ 268 | - ~org-noter--goto-page~ -> ~org-noter--doc-goto-location~ 269 | - ~org-noter--doc-page-change-handler~ -> ~org-noter--doc-location-change-handler~ 270 | - ~org-noter--doc-view-advice~ -> ~org-noter--location-change-advice~ 271 | - ~org-noter--get-slice~ -> ~org-noter--doc-get-page-slice~ 272 | - ~org-noter--modeline-text~ -> ~org-noter--mode-line-text~ 273 | - ~org-noter--doc-current-page~ -> ~org-noter--doc-approx-location~ 274 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/0.11.0...0.12.0][0.12.0]] [2018-02-06 Tue] 275 | *** Added 276 | - Support for grouping notes in the same headline. 277 | - Demonstration in [[./Demo/Demo.org][Demo.org]]. 278 | - Resume session from a note. 279 | - ~org-noter-create-skeleton~ to generate the document outline with org 280 | headline. 281 | - Caching the structure of the file in session for faster retrieval. 282 | - Customization for reusing existing frame with 283 | ~org-noter-always-create-frame~. 284 | - Hiding unrelated note with ~org-noter-hide-other~, can be toggled with 285 | ~org-noter-set-hide-other~, and overrode with 286 | ~org-noter--property-hide-other~, =NOTER_HIDE_OTHER= . 287 | *** Changed 288 | - Reword many section of [[file:README.org][README]]. 289 | - Refactor code in ~org-noter--selected-note-page~ to its own function, 290 | ~org-noter--get-containing-heading~. 291 | - Refactor code in ~org-noter--setup-windows~ to 292 | ~org-noter--get-notes-window~. 293 | - ~org-noter-set-notes-window-location~ deletes extra frame displaying 294 | only note window. 295 | - Reuse windows if it's in the correct configuration. 296 | *** Fixed 297 | - Empty session name 298 | - Error when creating session because ~current-page~ symbol is nil. 299 | - Inconsistency in ~org-noter-sync-prev-note~. 300 | - Infinite recursion in killing a session. 301 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/0.10.0...0.11.0][0.11.0]] [2018-01-29 Mon] 302 | *** Added 303 | - Note count to mode line. 304 | - Save progress with the variable ~org-noter-auto-save-last-page~. The 305 | variable can be toggled with ~org-noter-set-auto-save-last-page~ or 306 | override in a file with ~org-noter-property-auto-save-last-page~, 307 | =NOTER_AUTO_SAVE_LAST_PAGE= property. 308 | - Store =::auto-save-last-page= property in session. 309 | *** Changed 310 | - ~org-noter-insert-note~ will select the document window when user quits 311 | while inserting note. 312 | - ~org-noter-kill-session~ will kill the frame of the notes buffer. 313 | *** Renamed / Deprecated 314 | - ~org-noter-property-behavior~ -> ~org-noter--property-behavior~ 315 | - ~org-noter-property-location~ -> ~org-noter--property-location~ 316 | - ~org-noter-property-auto-save-last-page~ -> ~org-noter--property-auto-save-last-page~ 317 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/0.9.0...0.10.0][0.10.0]] [2018-01-28 Sun] 318 | *** Added 319 | - Override global window settings with ~org-noter-property-behavior~ and 320 | ~org-noter-property-location~, =NOTER_NOTES_BEHAVIOR= and 321 | =NOTER_NOTES_LOCATION= respectively. 322 | - The macro ~org-noter--with-selected-notes-window~. 323 | - The function ~org-noter--notes-window-behavior-property~ and 324 | ~org-noter--notes-window-location-property~ to get window setting 325 | property. 326 | - The command ~org-noter-set-notes-window-behavior~ and 327 | ~org-noter-set-notes-window-location~ to set window setting. 328 | - Store =:window-behavior= and =:window-location= property in session. 329 | - ~org-noter--compare-page-cons~ to do page comparison. 330 | - Sync page commands: ~org-noter-sync-prev-page~, 331 | ~org-noter-sync-current-page~ & ~org-noter-sync-next-page~. 332 | See [[file:README.org::#keys][README]] for their keybindings and explanation. 333 | *** Changed 334 | - Refactor most of the code in ~org-noter~ command to 335 | ~org-noter--create-session~ function. 336 | - ~org-noter~ accepts relative file path. 337 | *** Renamed / Deprecated 338 | - ~org-noter--restore-windows~ -> ~org-noter--setup-windows~ 339 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/0.8.0...0.9.0][0.9.0]] [2018-01-11 Thu] 340 | *** Added 341 | - More control over the setup of windows with 342 | ~org-noter-notes-window-behavior~ and ~org-noter-notes-window-location~. 343 | - The function ~org-noter-other-window-config~ to start ~org-noter~ with 344 | another window configuration. 345 | *** Changed 346 | - Change the package name from ~alt-interleave.el~ to ~org-noter.el~. 347 | *** Renamed / Deprecated 348 | - Every function is renamed with =org-noter= as its prefix. 349 | - Every occurrence of =pdf= is replaced with =doc=. 350 | *** Fixed 351 | - ~interleave--focus-notes-region~ recentering at the beginning of the 352 | window. 353 | - Setting the beginning of the document to read-only. 354 | - Wrong ~cl-lib~ dependency declaration. 355 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/0.7.0...0.8.0][0.8.0]] [2017-12-18 Mon] 356 | *** Changed 357 | - ~interleave--selected-note-page~ uses all of the buffer to get root 358 | property value. 359 | - ~interleave--focus-notes-region~ also recenters the screen. 360 | - ~interleave--restore-windows~ narrows to root after restoring windows. 361 | - ~interleave-kill-session~ no longer asks for confirmation when the 362 | note buffer is modified. 363 | *** Fixed 364 | - Inserting headline at the end of the buffer without inserting newline. 365 | - Calling ~interleave~ with prefix argument still consider inherited tags. 366 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/0.6.0...0.7.0][0.7.0]] [2017-11-28 Tue] 367 | *** Added 368 | - ~interleave-set-start-page~ command sets the page for the start of a 369 | session. 370 | - ~interleave-other-window-config~ command that use another split 371 | direction to start. 372 | - ~interleave--selected-note-page~ to get the start page. 373 | - [[file:ideas.org][ideas.org]] to record some ideas. 374 | *** Changed 375 | - Use ~interleave--get-slice~ to get a more precise scroll percentage in 376 | localized note. 377 | - Shorten display name of PDF buffer. 378 | - Users have to choose which notes ~interleave-insert-note~ can insert. 379 | - ~interleave--narrow-to-root~ go to the content of root instead of 380 | restoring point. 381 | *** Fixed 382 | - ~interleave-kill-session~ killing modified notes buffer. 383 | - ~interleave--page-change-handler~ focusing on notes with no 384 | =INTERLEAVE_NOTE_PAGE= property. 385 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/0.5.0...0.6.0][0.6.0]] [2017-11-25 Sat] 386 | *** Added 387 | - ~interleave-insert-localized-note~ that inserts a note that associate 388 | with part of a page. 389 | - =INTERLEAVE_NOTE_PAGE= can be in the format of a cons cell; 390 | =(PAGE_NUMBER . SCROLL_PERCENT)=. 391 | *** Changed 392 | - ~interleave--goto-page~ takes number instead of string. 393 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/0.4.0...0.5.0][0.5.0]] [2017-11-24 Fri] 394 | *** Added 395 | - Installation & Usage in README. 396 | - Users can change the default title of a note with the variable 397 | ~interleave-default-heading-title~. 398 | - Opening only the directory of the PDF by passing prefix argument to 399 | ~interleave~ 400 | - Displaying name of documents when killing a session. 401 | - Storing the level of root heading in session. 402 | - Customized scroll for windows of interleave session with ~interleave--set-scroll~. 403 | *** Changed 404 | - The package is now named =alt-interleave=. 405 | - The property for the page of a note is changed back to 406 | =INTERLEAVE_NOTE_PAGE=, for compatibility with Sebastian's Interleave, 407 | users have to change this themselves as instructed in [[file:README.org::#diff][README]]. 408 | - Define minor mode for PDF and notes: ~interleave-pdf-mode~, 409 | ~interleave-notes-mode~. This is a cleaner way to handle local keybindings. 410 | - Use windows to set up the session instead of buffers. 411 | - Restore windows when a session is already opened with ~interleave--restore-windows~. 412 | *** Removed 413 | - The variable ~interleave--inhibit-next-page-change~ as no functions use it anymore. 414 | *** Fixed 415 | - Point not moving at the end of the buffer when syncing the pages. 416 | - Inserting new line in title when creating notes. 417 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/0.3.0...0.4.0][0.4.0]] [2017-11-11 Sat] 418 | *** Added 419 | - Support for [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Document-View.html][DocView mode]]. 420 | - Syncing the note file by going to the nearest note when the page 421 | changes. 422 | - Commands for syncing notes: ~interleave-sync-previous-page-note~, 423 | ~interleave-sync-next-page-note~. 424 | - Customization group called =interleave= 425 | - More details in [[file:README.org][README]] containing keybindings and features: 426 | |-----+--------------------------------| 427 | | =M-p= | ~interleave-sync-next-page-note~ | 428 | | =M-.= | ~interleave-sync-page-note~ | 429 | | =M-n= | ~interleave-sync-next-page-note~ | 430 | |-----+--------------------------------| 431 | | =i= | ~interleave-insert-note~ | 432 | | =q= | ~interleave-kill-session~ | 433 | |-----+--------------------------------| 434 | *** Renamed/Deprecated 435 | - ~interleave--property-pdf-file~ -> ~interleave-property-pdf-file~ 436 | - ~interleave--property-note-page~ -> ~interleave-property-note-page~ 437 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/0.2.0...0.3.0][0.3.0]] [2017-11-10 Fri] 438 | *** Added 439 | - The macro ~interleave--with-valid-session~ can be used for other 440 | functions to access the information of a session. 441 | - Making root heading read-only when starting a session. 442 | *** Changed 443 | - The property for the page of a note is now =INTERLEAVE_PAGE_NOTE= for 444 | compatibility. 445 | ** [[https://github.com/c1-g/org-noter-plus-djvu/compare/0.1.0...0.2.0][0.2.0]] [2017-10-11 Wed] 446 | *** Added 447 | - [[file:LICENSE][LICENSE]] file for GNU GPL3 license. 448 | - [[file:README.org][README]] file explaining the reason for a rewrite. 449 | - ~interleave-kill-session~ command to manually kill a session. 450 | - Killing session automatically with ~interleave--handle-buffer-kill~ 451 | and ~interleave--handle-delete-frame~ used as hook for delete frame 452 | functions. 453 | - The =INTERLEAVE_NOTE_PAGE= property to store page number of each note. 454 | - ~interleave-insert-note~ command that automatically make a new note 455 | with respect to the order of the page number. 456 | *** Changed 457 | - Also consider the parent of a note as a "root" if it has the same 458 | PDF as its property. 459 | - Ask user to kill the session instead of giving error when the PDF is 460 | already interleaved with another note file. 461 | - The note window is no longer set as dedicated. 462 | ** [[https://github.com/c1-g/org-noter-plus-djvu/releases/tag/0.1.0][0.1.0]] [2017-10-10 Tue] 463 | *** Added 464 | - =interleave.el= copied from [[https://github.com/rudolfochrist][Sebastian Christ]]. 465 | - Checks for PDF file path e.g. non-existent file, invalid path in 466 | ~interleave~ command. 467 | - The ability to use relative file name for PDF file. 468 | *** Changed 469 | - Storing information of a session in ~interleave--sessions~ instead of 470 | relying on text only. 471 | - Narrowing to a heading when calling ~interleave~. 472 | - Setting windows for the PDF file and the note file to be dedicated. 473 | 474 | -------------------------------------------------------------------------------- /Cask: -------------------------------------------------------------------------------- 1 | (source gnu) 2 | (source melpa-stable) 3 | 4 | (depends-on "buttercup") 5 | (depends-on "pdf-tools") 6 | 7 | (depends-on "with-simulated-input") 8 | 9 | (depends-on "log4e") 10 | -------------------------------------------------------------------------------- /Demo/Demo.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Org-noter Demonstration 2 | #+DATE: 3 | #+AUTHOR: 4 | #+OPTIONS: toc:nil 5 | #+LATEX_HEADER: \usepackage{parskip} 6 | #+LATEX_HEADER: \usepackage{titlesec} 7 | #+LATEX_HEADER: \setlength{\parindent}{15pt} 8 | #+LATEX_HEADER: \newcommand{\sectionbreak}{\clearpage} 9 | 10 | Hopefully this will give you some insight on how ~org-noter~ works! Bear with me! 11 | 12 | * Lorem ipsum? Great. 13 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse mattis nibh in mauris 14 | eleifend maximus. Aenean luctus malesuada nisl, ac interdum nisl semper at. Vestibulum 15 | consequat egestas tempus. Etiam in diam at odio egestas rhoncus eu eu ipsum. Ut sit amet 16 | porta lacus, sed mattis ante. Curabitur vehicula nulla ut hendrerit feugiat. Donec ac nunc 17 | dictum, porttitor nisi sed, consectetur enim. Vestibulum pellentesque erat mauris, at 18 | euismod enim euismod id. Maecenas efficitur luctus ex non scelerisque. Nulla leo ante, 19 | semper sit amet gravida id, eleifend sed orci. Pellentesque in risus quis nisi elementum 20 | efficitur. 21 | 22 | Nunc venenatis nunc vel dolor sollicitudin, sit amet sagittis ex molestie. Vestibulum at 23 | ante orci. Nunc scelerisque sit amet sem ac tincidunt. Aliquam scelerisque ipsum turpis, 24 | semper malesuada tellus pharetra dictum. Proin convallis tortor vitae massa maximus 25 | semper. Mauris in pretium purus. Duis sed ligula ipsum. 26 | 27 | Nulla ac massa sapien. Vivamus semper diam velit. Integer a rhoncus turpis. Suspendisse 28 | molestie pellentesque pharetra. Suspendisse dignissim tristique libero, ac bibendum nunc. 29 | Etiam congue nulla sed eros luctus, nec gravida enim blandit. Sed tincidunt tempor ante 30 | non aliquet. Praesent posuere quis augue nec elementum. Interdum et malesuada fames ac 31 | ante ipsum primis in faucibus. Etiam lectus urna, malesuada sed mollis vel, commodo in 32 | ante. Integer id nulla dignissim, ornare diam in, egestas arcu. Etiam quis nunc vel quam 33 | placerat bibendum. Integer vehicula porta mi, ut dapibus mauris. Nullam tortor mi, 34 | malesuada nec purus vitae, ultrices euismod neque. 35 | 36 | * And more! 37 | Aenean semper, nibh vel consectetur hendrerit, augue urna tempor neque, rutrum maximus 38 | velit ex id metus. Ut in tellus arcu. Vestibulum viverra dictum erat eu commodo. Morbi 39 | dignissim feugiat ornare. In volutpat, dui ac placerat porta, arcu odio vehicula risus, et 40 | pharetra diam massa a metus. Sed rutrum tristique volutpat. Donec vel auctor nibh, vitae 41 | finibus massa. Nulla pellentesque sapien non urna cursus, sodales finibus nisi sagittis. 42 | Duis eget dictum nisl. Vestibulum scelerisque, turpis vel varius interdum, tortor augue 43 | scelerisque erat, vel facilisis libero massa quis eros. In ultrices hendrerit semper. 44 | Donec pharetra non dolor ut iaculis. Sed suscipit, elit id porttitor fringilla, quam eros 45 | viverra ipsum, vitae imperdiet nunc tortor et mi. Etiam condimentum interdum quam, ac 46 | tempus massa mollis eget. Vestibulum sed nisl sit amet est commodo dapibus ac ac dolor. 47 | 48 | Suspendisse rutrum iaculis congue. Quisque nec bibendum mauris, nec porttitor ex. Cras 49 | ultrices tincidunt sem, eu ultricies nulla. Aliquam nec ex ac ipsum cursus posuere et 50 | gravida lorem. Fusce at orci a augue placerat dignissim. Aliquam eu leo in odio aliquam 51 | suscipit. Suspendisse rhoncus erat id enim dictum aliquet. Aliquam vel sem at risus 52 | fringilla molestie vel id justo. Donec vel venenatis augue. Aliquam ac ligula quis magna 53 | congue eleifend et ut justo. Nulla justo quam, malesuada in dapibus nec, dignissim non 54 | nunc. Nunc blandit, metus et varius bibendum, dui ligula pretium urna, eget tristique nibh 55 | sapien ut orci. Mauris scelerisque commodo risus non pharetra. Proin malesuada augue 56 | tellus, ac laoreet massa vestibulum vitae. Donec sit amet nulla quis turpis viverra 57 | facilisis. Suspendisse quis imperdiet arcu. 58 | 59 | * You know what follows... 60 | Suspendisse faucibus feugiat dignissim. Praesent tempus, dui id scelerisque placerat, est 61 | lorem feugiat massa, luctus faucibus est arcu a velit. Mauris elementum id urna sit amet 62 | laoreet. Cras maximus, sapien id porta sollicitudin, ligula orci auctor ex, ac luctus 63 | purus risus quis leo. Sed interdum ut tortor a elementum. Aliquam erat volutpat. Morbi 64 | accumsan imperdiet massa vel sodales. Donec ac auctor metus. 65 | 66 | Pellentesque posuere dui ac eleifend luctus. Morbi et vestibulum odio, eu porta ex. 67 | Vestibulum fermentum risus quis tellus tempor ultrices. Nam eu quam sem. Nam at interdum 68 | nunc. Cras eros augue, malesuada ac enim non, elementum cursus tellus. Integer nec nulla 69 | odio. Suspendisse volutpat nibh nec ultricies mollis. Praesent ut enim vel nisl mollis 70 | ultricies et vitae nisi. Vivamus vel tincidunt justo. Proin quis orci eget velit commodo 71 | ultricies. Suspendisse cursus ullamcorper dolor, ut pretium tellus pulvinar sit amet. 72 | Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis 73 | egestas. Cras leo quam, ornare eu erat eu, vehicula aliquam nulla. 74 | 75 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc et orci imperdiet nulla 76 | gravida venenatis. In sit amet neque purus. Sed aliquam feugiat enim. Sed eget congue 77 | tellus. Quisque eleifend ipsum at fringilla ultricies. Aliquam non justo ac purus accumsan 78 | elementum. Duis ullamcorper nulla nec arcu feugiat vestibulum. Nam id eleifend lorem. 79 | Vivamus consectetur ultricies tempor. Donec vel scelerisque odio, ac consectetur enim. 80 | 81 | Mauris elementum facilisis massa a elementum. Donec auctor dui sit amet eros pulvinar 82 | sollicitudin. Nullam auctor sagittis purus sagittis semper. Etiam blandit magna vel auctor 83 | semper. Vivamus blandit pretium orci, non fringilla ex facilisis a. Praesent sed ex risus. 84 | Donec cursus felis eu justo pretium vehicula. Sed et posuere velit. Ut volutpat tempus 85 | lacus, nec accumsan massa viverra vitae. Maecenas a dui purus. Nunc suscipit ut ligula vel 86 | sodales. Integer vitae fermentum velit, sed mattis risus. Lorem ipsum dolor sit amet, 87 | consectetur adipiscing elit. Morbi eu arcu tincidunt arcu pellentesque eleifend. Aenean 88 | non sem scelerisque, aliquam velit non, posuere ex. 89 | 90 | Mauris sit amet nisi erat. Praesent porta nisl pretium turpis finibus, vitae aliquet sem 91 | sodales. Fusce vel eros turpis. Proin cursus condimentum blandit. Vestibulum ante ipsum 92 | primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean pretium diam 93 | odio, non tempor urna consectetur at. Cras vehicula enim erat, a rhoncus magna auctor ut. 94 | Etiam risus metus, vulputate posuere finibus id, congue vitae dolor. Suspendisse quis urna 95 | quis nisi ultrices condimentum. Quisque consequat semper ex a maximus. Donec euismod 96 | rhoncus felis, vehicula laoreet mi. Pellentesque cursus lacus diam, sit amet volutpat 97 | tortor viverra sed. Nam vulputate luctus pharetra. Donec lobortis, ante non vulputate 98 | semper, eros turpis pretium nulla, quis tincidunt leo lorem non odio. Suspendisse urna 99 | ligula, euismod ac maximus pellentesque, varius at lectus. Pellentesque interdum tempor 100 | justo porta gravida. 101 | 102 | * You won't believe what comes next!.. 103 | Pellentesque posuere dui ac eleifend luctus. Morbi et vestibulum odio, eu porta ex. 104 | Vestibulum fermentum risus quis tellus tempor ultrices. Nam eu quam sem. Nam at interdum 105 | nunc. Cras eros augue, malesuada ac enim non, elementum cursus tellus. Integer nec nulla 106 | odio. Suspendisse volutpat nibh nec ultricies mollis. Praesent ut enim vel nisl mollis 107 | ultricies et vitae nisi. Vivamus vel tincidunt justo. Proin quis orci eget velit commodo 108 | ultricies. Suspendisse cursus ullamcorper dolor, ut pretium tellus pulvinar sit amet. 109 | Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis 110 | egestas. Cras leo quam, ornare eu erat eu, vehicula aliquam nulla. 111 | 112 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc et orci imperdiet nulla 113 | gravida venenatis. In sit amet neque purus. Sed aliquam feugiat enim. Sed eget congue 114 | tellus. Quisque eleifend ipsum at fringilla ultricies. Aliquam non justo ac purus accumsan 115 | elementum. Duis ullamcorper nulla nec arcu feugiat vestibulum. Nam id eleifend lorem. 116 | Vivamus consectetur ultricies tempor. Donec vel scelerisque odio, ac consectetur enim. 117 | -------------------------------------------------------------------------------- /Demo/Demo.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/c1-g/org-noter-plus-djvu/f0ad5d691eff3cc9f6eb98956a37bc64f9c5fb9f/Demo/Demo.pdf -------------------------------------------------------------------------------- /Demo/Notes.org: -------------------------------------------------------------------------------- 1 | * Demo 2 | :PROPERTIES: 3 | :NOTER_DOCUMENT: Demo.pdf 4 | :END: 5 | Notice how the other heading *is not visible* anymore? Opening a session narrows the 6 | buffer to show only what matters. 7 | 8 | ** Notes on page 2 9 | :PROPERTIES: 10 | :NOTER_PAGE: 2 11 | :END: 12 | This *automatically* becomes focused when you scroll to the associated page. 13 | 14 | ** More notes on page 2 15 | :PROPERTIES: 16 | :NOTER_PAGE: 2 17 | :END: 18 | This one also becomes visible. Notice the *note counter* on mode-line of the document. 19 | 20 | ** This note is localized 21 | :PROPERTIES: 22 | :NOTER_PAGE: (3 . 0.42222228571428577) 23 | :END: 24 | When scrolling through the document, this works the same as other notes. However, it is 25 | useful when you are using *note sync commands*, which go through every note. When using 26 | those commands to sync this note, the document view *is scrolled* to the point selected on 27 | the document. 28 | 29 | ** All sessions are independent, and use indirect buffers 30 | :PROPERTIES: 31 | :NOTER_PAGE: 4 32 | :END: 33 | You may have *multiple Org-noter sessions* open at the same time, while always keeping the 34 | initial notes buffer intact, even though it narrows the indirect buffers! 35 | 36 | Each session has: 37 | - A dedicated frame 38 | - An indirect buffer for the document 39 | - An indirect buffer for the notes 40 | 41 | (You can change the behavior and location of the notes window btw!) 42 | 43 | ** Group of notes 44 | *** Notes on page 5 45 | :PROPERTIES: 46 | :NOTER_PAGE: 5 47 | :END: 48 | This note is grouped with another one, and with things like this, you may organize notes 49 | however you want. 50 | *** Notes on page 6 51 | :PROPERTIES: 52 | :NOTER_PAGE: 6 53 | :END: 54 | Keep them tidy! Just so you know, even notes associated with pages may hold other notes 55 | inside. 56 | * Another demo 57 | :PROPERTIES: 58 | :NOTER_DOCUMENT: Demo.pdf 59 | :END: 60 | ** Notes on page 1 61 | :PROPERTIES: 62 | :NOTER_PAGE: 1 63 | :END: 64 | ** Create a session here!! 65 | :PROPERTIES: 66 | :NOTER_PAGE: 2 67 | :END: 68 | The session was created on *page 2*, where this note is! 69 | 70 | Besides, there is a command to enable auto-save and *resume where you left*, and another 71 | to set a different *start page* for the sessions. 72 | 73 | ** Isolated 74 | :PROPERTIES: 75 | :NOTER_PAGE: 3 76 | :END: 77 | This is the same document, but as we are using indirect buffers, we may scroll and zoom 78 | differently! 79 | ** Don't forget... 80 | :PROPERTIES: 81 | :NOTER_PAGE: 4 82 | :END: 83 | This is Org-mode!! You can use everything it provides, like inline $\LaTeX$ equations and 84 | babel! 85 | 86 | Besides, and a great selling point, everything lives on a separate Org notes file, 87 | searchable, taggable, however you want! 88 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | export EMACS ?= $(shell which emacs) 2 | CASK_DIR := $(shell cask package-directory) 3 | 4 | $(CASK_DIR): Cask 5 | cask install 6 | @touch $(CASK_DIR) 7 | 8 | .PHONY: cask 9 | cask: $(CASK_DIR) 10 | 11 | .PHONY: compile 12 | compile: cask 13 | ! (cask eval "(let ((byte-compile-error-on-warn f)) \ 14 | (cask-cli/build))" 2>&1 \ 15 | | egrep -a "(Warning|Error):") ; \ 16 | (ret=$$? ; cask clean-elc && exit $$ret) 17 | 18 | .PHONY: test 19 | test: compile 20 | cask exec buttercup -L . 21 | -------------------------------------------------------------------------------- /README.new.org: -------------------------------------------------------------------------------- 1 | * Org-noter: an interleaving/note-taking package for documents 2 | 3 | ~Org-noter~, by [[https://github.com/weirdNox][Gonçalo Santos]], was inspired by the now-orphaned ~Interleave~ 4 | package, by [[https://github.com/rudolfochrist][Sebastian Christ]]. In Sebastian's words (with minor edits): 5 | 6 | #+begin_quote 7 | In the past, textbooks were sometimes published as interleaved editions. That 8 | meant, each page was followed by a blank page and ambitious students/scholars 9 | had the ability to take their notes directly in their copy of the 10 | textbook. Newton and Kant were prominent representatives of this 11 | technique. [find reference] 12 | 13 | Nowadays, textbooks (or lecture materials) come in PDF format. Although almost 14 | every PDF Reader has the ability to add some notes to the PDF itself, it is 15 | not as powerful as it could be. This is what this Emacs minor mode tries to 16 | accomplish. It presents your PDF side by side with an Org Mode buffer of your 17 | notes, narrowed down to just those passages that are relevant to this 18 | particular page in the document viewer. 19 | #+end_quote 20 | 21 | Org-noter's purpose is to let you create notes that are kept in sync when you 22 | scroll through the document, but that are external to it - the notes 23 | themselves live in an Org-mode file. As such, this leverages the power of 24 | [[http://orgmode.org/][Org-mode]] (the notes may have outlines, latex fragments, babel, etc...) while 25 | acting like notes that are made /inside/ the document. Also, taking notes is 26 | very simple: just press =i= and annotate away! 27 | 28 | 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 29 | [[DJVU-read][DJVU-image-mode]]. These modes make it possible to annotate *PDF*, *EPUB*, 30 | *Microsoft Office*, DVI, PS, OpenDocument, and DJVU formatted files. Note 31 | that PDF support is our prime goal. Other format have been supported by other 32 | contributors, but we need code contributions from users of other formats to 33 | maintain/progress usability with those formats. 34 | 35 | 36 | ** Features 37 | *** original org-noter (up to 2019) 38 | - Easy to use annotation interface :: Just press =i= in the 39 | document buffer and annotate away! 40 | 41 | - Keep your notes in sync with the document :: When you take a note by pressing 42 | =i=, it saves the location where you took it so it is able to show you 43 | the notes while you scroll, automatically! 44 | 45 | - Easy navigation :: You may navigate your document as usual, seeing the notes buffer scroll and show you 46 | the respective notes; however, you may also navigate by notes, only seeing annotated pages/chapters. 47 | 48 | - Isolated interface :: Each session has its own frame and the document and notes buffers are indirect 49 | buffers to the original ones. Killing any of these things will terminate your annotation session. 50 | 51 | - Simultaneous sessions :: You may open as many annotation sessions as you wish, at the same time! The 52 | root heading of each document will be made read-only to prevent you from deleting a heading from an 53 | open session. 54 | 55 | - Resume annotating from where you left :: When ~org-noter-auto-save-last-location~ is non-nil, it will 56 | save the last location you visited and pick it up when you start another session! You may also set 57 | this per-document, [[#custom][read more here]]. 58 | 59 | - Keep your notes organized :: You may arrange your notes however you want! You can create groups and 60 | nest notes (and even nest documents inside other documents)! 61 | 62 | - Annotate ~org-attach~'ed files :: If you have any attached files, it will let you choose one as 63 | the document to annotate. 64 | 65 | *** new 66 | - 2D precise notes :: ([[https://github.com/ahmed-shariff/org-noter][Ahmed Shariff]]) Location tooltip appears at start of 67 | selected text or point of click. 68 | 69 | - Multicolumn ordering of precise notes :: (Rudimentary) With the 70 | ~NUM_COLUMNS~ property, you can specify the number of columns in a 71 | multicolumn document (or pages thereof). Precise notes are ordered 72 | vertically within columns. 73 | 74 | - Highlighting of selected text :: Default behavior (on/off) is 75 | user-customizable. Non-default behavior is activated with a =C-u= prefix 76 | to the note insertion command. 77 | 78 | - Customizable tooltip arrow colors :: ~org-noter-arrow-foreground-color~ (default 79 | orange-red) and ~org-noter-arrow-foreground-color~ (default white) are 80 | both user customizable. 81 | 82 | [TODO: make/link to screencast] 83 | 84 | ** Installation 85 | 86 | 1. clone this repo to a local directory 87 | 2. add to your init file: 88 | #+begin_src elisp 89 | (add-to-list 'load-path "/modules") 90 | (add-to-list 'load-path "") 91 | ;; then choose one or more of the following modules 92 | ;; (require 'org-noter-pdf) 93 | ;; (require 'org-noter-nov) 94 | ;; (require 'org-noter-djvu) 95 | #+end_src 96 | 97 | ** Usage 98 | 99 | If you want to give it a try without much trouble: 100 | - Just have an Org file where you want the notes to go 101 | - Create a root heading to hold the notes 102 | - Run =M-x org-noter= inside! 103 | On the first run, it will ask you for the path of the document and save it in a 104 | property. By default, it will also let you annotate an attached file [[https://orgmode.org/manual/Attachments.html][(org-attach documentation)]]. 105 | 106 | This will open a new dedicated frame where you can use [[#keys][the keybindings described here]]. 107 | 108 | More generally, there are two modes of operation. You may run 109 | =M-x org-noter=: 110 | - Inside a heading in an Org notes file :: This will associate that heading with a 111 | document and open a session with it. This mode is the one described in the example 112 | above. 113 | 114 | - In a document :: Run =M-x org-noter= when viewing a 115 | document (eg. PDF, epub...). 116 | 117 | This will try to find the respective notes file automatically. It will 118 | search in all parent folders and some specific folders set by you. See 119 | ~org-noter-default-notes-file-names~ and ~org-noter-notes-search-path~ 120 | for more information. 121 | 122 | There is, of course, more information in the docstrings of each command. 123 | 124 | ** TODO Customization 125 | There are two kinds of customizations you may do: 126 | 1. Global settings, affecting every session 127 | 2. Document-specific settings, which override the global settings 128 | 129 | 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 130 | init file. To find which settings are available, you may use the customization interface or you may just 131 | read =org-noter.el=. 132 | 133 | The best way to set *document-specific settings* is by using the utility commands provided (list below). 134 | In order to use them, you need an open session. The commands may change the settings for that session 135 | only (not surviving restarts), or for every session with that document. 136 | 137 | *List of utility commands* (check the docstrings to learn how to make the changes 138 | permanent, or revert to the default): 139 | - You may set a start location for this document, by using ~org-noter-set-start-location~. 140 | 141 | - To automatically resume from where you left, use ~org-noter-set-auto-save-last-location~. 142 | 143 | - With ~org-noter-set-notes-window-behavior~, you may change /when/ the notes window pops 144 | up. 145 | 146 | - With ~org-noter-set-notes-window-location~, you may change /where/ the notes window pops 147 | up. 148 | 149 | - ~org-noter-set-doc-split-fraction~ will ask you for the fraction of the frame that the document window 150 | occupies when split. 151 | 152 | - ~org-noter-set-hide-other~ will toggle whether or not it should hide headings not 153 | related to the executed action. 154 | 155 | - ~org-noter-set-closest-tipping-point~ will set the closest note tipping point. Also 156 | check the docstring of the variable ~org-noter-closest-tipping-point~ in order to better 157 | understand the tipping point. 158 | 159 | ** TODO Keybindings and commands 160 | :PROPERTIES: 161 | :CUSTOM_ID: keys 162 | :END: 163 | | Key | Description | Where? | 164 | | =i= | Insert note | Document buffer | 165 | | =M-i= | Insert precise note | Document buffer | 166 | | =q= | Kill session | Document buffer | 167 | | =M-p= | Sync previous page/chapter | Document and notes buffer | 168 | | =M-.= | Sync current page/chapter | Document and notes buffer | 169 | | =M-n= | Sync next page/chapter | Document and notes buffer | 170 | | =C-M-p= | Sync previous notes | Document and notes buffer | 171 | | =C-M-.= | Sync selected notes | Document and notes buffer | 172 | | =C-M-n= | Sync next notes | Document and notes buffer | 173 | 174 | You can use the usual keybindings to navigate the document 175 | (=n=, =p=, =SPC=, ...). 176 | 177 | There are two types of sync commands: 178 | - To sync a page/chapter, means it will find the [previous|current|next] page/chapter and 179 | show the corresponding notes for that page/chapter; as such, it will always pop up the 180 | notes buffer, if it does not exist. This type of command is in relation to the current 181 | page/chapter in the document. 182 | 183 | - To sync the notes, means it will find the [previous|current|next] notes and go to the 184 | corresponding location on the document. So, you need to have the notes window open, 185 | because this type of commands is in relation to the selected notes (ie, where the cursor 186 | is). 187 | 188 | When using PDF Tools, the command ~org-noter-create-skeleton~ imports the PDF outline or 189 | annotations (or both!) as notes, and it may be used, for example, as a starting point. 190 | 191 | You may also want to check the docstrings of the functions associated with the 192 | keybindings, because there is some extra functionality in some. 193 | 194 | ** Acknowledgments 195 | - [[https://github.com/rudolfochrist][Sebastian Christ]] :: Author of the ~interleave~ package, inspiration for ~org-noter~ 196 | - [[https://github.com/weirdNox][Gonçalo Santos]] :: Author of ~org-noter~ 197 | - [[https://github.com/ahmed-shariff/org-noter][Ahmed Shariff]] :: Contributor of 2-D precise notes 198 | - [[https://github.com/c1-g/org-noter-plus-djvu][Charlie Gordon]] :: Contributor of DJVU support and document-type modularization 199 | 200 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | #+STARTUP: hidestars 2 | * ORG-NOTER (fork of a fork) 3 | ** If you are new to *ORG-NOTER* 4 | 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 5 | this repo). 6 | 7 | ** 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]] 8 | - In essence, this is close to the original *weirdNox* (Gonçalo Santos) 9 | version that you find on MELPA with the refactoring that *c1-g* implemented. 10 | 11 | - djvu and epub support have also been advanced by *c1-g*, but we have not 12 | tested any of that code. See [[https://github.com/c1-g/org-noter-plus-djvu/blob/master/README.org][Charlie Gordon's README]] 13 | ([[file:docs/README-djvu.org]] in this repo). 14 | 15 | - Prior to our work, the main new feature for PDF users was 2-D precise notes 16 | (introduced by *Ahmed Shariff*, which can be inserted in two ways: 17 | 1. "Select-precise": selecting text in the pdf, followed by =M-i=. *c1-g* 18 | changed the position format to use the entire list returned by =edges=. 19 | This breaks the other way of inserting precise notes. On *dmitrym0*'s 20 | fork, select-precise notes auto-fill the note title with the selected 21 | text. This has deleterious side effects for other note insertion methods. 22 | 2. "Click-precise": =M-i=, followed by clicking a location on the page. 23 | 24 | - Standard notes can also be inserted in two ways: 25 | 1. "TAB". Hitting tab creates a note title "Notes for page ##", where the 26 | page number is inserted automatically over the octothorpes. 27 | 2. "i". The fundamental =org-noter-insert-note=. User types note title 28 | into the minibuffer. 29 | 30 | ** Stock vs refactored note insertion 31 | - more consistent use of selected text in title or body 32 | - more consistent primary default title (short-selected-text) 33 | - more consistent secondary default title ("Notes for page ") 34 | - avoids having different notes with the same heading 35 | 36 | In Stock org-noter, repeated TAB's give multiple notes. In this refactor, 37 | new notes in the same location with the same title are not made, but 38 | selected text is added to the note body. For now, precise notes are 39 | excepted from this rule. 40 | 41 | - long text-selections are enclosed in #+BEGIN_QUOTE...#+END_QUOTE 42 | - short text-selections are enclosed in ``...'' (LaTeX style) when they are 43 | not used as the title of the note. 44 | - short/long text-selections are differentiated by the custom variable 45 | =org-noter-max-short-selected-text-length= (default: 80 char) 46 | 47 | In Stock org-noter, long selections are those with more than 3 lines. 48 | 49 | - *NEW:* Highlighting of selected text 50 | 51 | Default setting is customizable, see =org-noter-highlight-selected-text=. 52 | Calling insertions with a non-nil prefix (eg, =C-u=) toggles this setting for 53 | individual note insertions. 54 | 55 | - *NEW:* multicolumn precise-notes defined by property NUM_COLUMNS 56 | 57 | Implements issue #153 in weirdNox/org-noter by adding 58 | =org-noter-pdf-convert-to-location-cons= to 59 | =org-noter--convert-to-location-cons-hook=, where a "virtual" vertical 60 | location is calculated by dividing the page into equal width columns. This 61 | is not a perfect solution, and is probably best for low-integer numbers of 62 | columns, since the page is just divided evenly into =NUM_COLUMNS= vertical 63 | strips. 64 | 65 | This can be set using =org-set-property-and-value=, but eventually we will 66 | write a user function to prevent users from mistyping the name of the 67 | property (=NUM_COLUMNS=). 68 | 69 | *** Stock 70 | | | insert-note =i= | precise note =M-i= | no-questions =C-i, TAB= | 71 | |-----------------+--------------------------------+---------------------------+-------------------------| 72 | | title prompt? | Y | Y | N | 73 | | default title 1 | prior note by location | selected-text | N/A | 74 | | default title 2 | "Notes for page #" | "Notes for page # x y" | "Notes for page #" | 75 | | new note | with new title | always | always | 76 | | body | selected-text on existing note | selected-text (> 3 lines) | none | 77 | |-----------------+--------------------------------+---------------------------+-------------------------| 78 | 79 | *** Refactored 80 | | | insert-note | precise note | no-questions | 81 | |---------------------+---------------------------+---------------------------+---------------------------| 82 | | title prompt? | Y | Y | N | 83 | | default title 1 | short-selected-text | short-selected-text | short-selected-text | 84 | | default title 2 | "Notes for page #" | "Notes for page # x y" | "Notes for page #" | 85 | | new note | with new title | always | with new title | 86 | | body | selected-text (not title) | selected-text (not title) | selected-text (not title) | 87 | |---------------------+---------------------------+---------------------------+---------------------------| 88 | | highlight selection | user setting/toggle | user setting/toggle | user setting/toggle | 89 | ** Customizations 90 | Out of respect for the existing user base of =org-noter=, almost all of the 91 | user-interface features of this fork remain the same those of as the original 92 | =org-noter=. However, users may be interested in our variations on the theme 93 | of Gonçalos Santos. 94 | *** Peter's mods 95 | In order of frequency, I use precise notes the most, standard notes next and 96 | no-questions notes the least, so I have bound keys to reflect those 97 | preferences with =i= for precise notes, =TAB= for standard notes; for 98 | no-questions (no-prompt) notes, =I= is for precise notes and =M-i= for 99 | standard notes.: 100 | #+begin_src elisp 101 | (with-eval-after-load 'org-noter 102 | (define-key org-noter-doc-mode-map (kbd "i") 'org-noter-insert-precise-note) 103 | (define-key org-noter-doc-mode-map (kbd "C-i") 'org-noter-insert-note) 104 | (define-key org-noter-doc-mode-map (kbd "I") 'org-noter-insert-precise-note-toggle-no-questions) 105 | (define-key org-noter-doc-mode-map (kbd "M-i") 'org-noter-insert-note-toggle-no-questions)) 106 | #+end_src 107 | 108 | For navigation, I use =..-sync-..-note= more than =..-sync-..-page-..=, so I 109 | bound the =note= commands to the easier-to-type =M-[p.n]= bindings and the 110 | less-used =page-..= commands to the harder-to-type =C-M-[p.n]= bindings. 111 | #+begin_src elisp 112 | (with-eval-after-load 'org-noter 113 | (define-key org-noter-doc-mode-map (kbd "M-p") 'org-noter-sync-prev-note) 114 | (define-key org-noter-doc-mode-map (kbd "M-.") 'org-noter-sync-current-note) 115 | (define-key org-noter-doc-mode-map (kbd "M-n") 'org-noter-sync-next-note) 116 | (define-key org-noter-doc-mode-map (kbd "C-M-p") 'org-noter-sync-prev-page-or-chapter) 117 | (define-key org-noter-doc-mode-map (kbd "C-M-.") 'org-noter-sync-current-page-or-chapter) 118 | (define-key org-noter-doc-mode-map (kbd "C-M-n") 'org-noter-sync-next-page-or-chapter) 119 | 120 | (define-key org-noter-notes-mode-map (kbd "M-p") 'org-noter-sync-prev-note) 121 | (define-key org-noter-notes-mode-map (kbd "M-.") 'org-noter-sync-current-note) 122 | (define-key org-noter-notes-mode-map (kbd "M-n") 'org-noter-sync-next-note) 123 | (define-key org-noter-notes-mode-map (kbd "C-M-p") 'org-noter-sync-prev-page-or-chapter) 124 | (define-key org-noter-notes-mode-map (kbd "C-M-.") 'org-noter-sync-current-page-or-chapter) 125 | (define-key org-noter-notes-mode-map (kbd "C-M-n") 'org-noter-sync-next-page-or-chapter)) 126 | #+end_src 127 | 128 | In the original code, the tooltip arrow on PDFs is *Orange-Red on White*, 129 | which works fine when the arrow is always on the left side of the page. I 130 | found that with the 2D precise notes introduced by Ahmed Shariff, I sometime 131 | had trouble locating the arrow as I navigated through my notes. The *Black 132 | on Cyan* color-scheme that I use is more jarring, hence easier to locate. 133 | #+begin_src elisp 134 | (with-eval-after-load 'org-noter 135 | (setq org-noter-arrow-background-color "cyan" 136 | org-noter-arrow-foreground-color "black")) 137 | #+end_src 138 | 139 | *** Dmitry's mods 140 | ** Features 141 | *** New 142 | 1. Use pdf-view-current-pagelabel to use the page label instead of page in 143 | default titles 144 | 145 | new function/hook =...-pretty-print-location-for-title= 146 | 147 | 2. Customizable tooltip arrow colors 148 | - =...-arrow-foreground-color= 149 | - =...-arrow-background-color= 150 | 151 | 3. Text-selection higlighting: customizable default behavior, toggle 152 | =...-highlight-selected-text= with =C-u= prefix on note-insertion 153 | commands. 154 | 155 | 4. Rudimentary support for multicolumn PDFs with inheritable =NUM_COLUMNS= 156 | property. See =...-pdf-convert-to-location-cons= 157 | 158 | *** Wishlist 159 | 1. Bind M- to precise-note, no-questions. 160 | 161 | 2. Make background of arrow transparent (see org-noter--show-arrow) 162 | maybe https://emacs.stackexchange.com/questions/45588/how-to-make-tooltip-background-transparent 163 | 164 | 3. Dedicated insert-selected-text-into-page-note 165 | 166 | 4. Internationalize precise notes to handle right-to-left languages. 167 | ** Bugs 168 | *** to fix 169 | 1. Sometimes (when?) M-p doesn't pick up the containing note-at-point right 170 | away (or at all), requiring user to manually type in the (existing) title 171 | 172 | 2. With NUM_COLUMNS > 1, point in notes document doesn't land in the correct 173 | place 174 | 175 | 3. With NUM_COLUMNS > 1, columns don't necessarily start at horizontal 176 | positions k/NUM_COLUMNS for k \in {1,..,NUM_COLUMNS}. We need to write a 177 | user interface that builds a list of horizontal fractions to delimit the 178 | columns. 179 | 180 | *** fixed 181 | 1. vertically stacked doc/notes layout fixed 182 | 183 | 2. =org-noter-sync-next-page-or-chapter= navigation fixed 184 | 185 | 3. Navigating up from a nested precise note lands in the prior note at the 186 | next level up (eg level 3 -> level 2). page notes behave properly. 187 | 188 | [file:org-noter-core.el:2179] 189 | =(org-element-property :begin (org-noter--get-containing-element))= returns 190 | the begin of the element one level up when the current note location is of 191 | the form ( . ). It works properly for locations of 192 | the form ( . ). 193 | 194 | It will be one of these two: 195 | - =org-noter--get-containing-heading= 196 | - =org-noter--check-location-property= 197 | found bug: [[file:org-noter-core.el:1023]] change test from integerp to numberp 198 | - =org-noter--get-containing-property-drawer= 199 | ** Custom variables 200 | Presently, the custom variables listed under =customize-group org-noter= is a 201 | flat list. I would like to group them into logical categories. 202 | 203 | *** start-stop 204 | - org-noter-supported-modes '(doc-view-mode pdf-view-mode nov-mode djvu-read-mode) 205 | - org-noter-auto-save-last-location nil 206 | - org-noter-default-notes-file-names '("Notes.org") 207 | - org-noter-notes-search-path '("~/Documents") 208 | - org-noter-notes-window-behavior '(start scroll) 209 | - org-noter-suggest-from-attachments t 210 | - org-noter-find-additional-notes-functions nil 211 | - org-noter-always-create-frame t 212 | - org-noter-kill-frame-at-session-end t 213 | - org-noter-use-indirect-buffer t 214 | 215 | *** layout 216 | - org-noter-notes-window-location 'horizontal-split 217 | - org-noter-doc-split-fraction '(0.5 . 0.5) 218 | - org-noter-disable-narrowing nil 219 | - org-noter-swap-window nil 220 | - org-noter-hide-other t 221 | 222 | *** note-insertion 223 | - org-noter-default-heading-title "Notes for page $p$" 224 | - org-noter-separate-notes-from-heading nil 225 | - org-noter-insert-selected-text-inside-note t 226 | - org-noter-highlight-selected-text nil 227 | - org-noter-max-short-selected-text-length 80 228 | - org-noter-insert-heading-hook nil 229 | - org-noter-insert-note-no-questions nil 230 | 231 | *** navigation-display 232 | - org-noter-arrow-delay 0.2 233 | - org-noter-arrow-horizontal-offset -0.02 234 | - org-noter-arrow-foreground-color "orange red" 235 | - org-noter-arrow-background-color "white" 236 | - org-noter-closest-tipping-point 0.3 237 | - org-noter-no-notes-exist-face 238 | - org-noter-notes-exist-face 239 | 240 | *** other 241 | - org-noter-property-doc-file "NOTER_DOCUMENT" 242 | - org-noter-property-note-location "NOTER_PAGE" 243 | - org-noter-prefer-root-as-file-level nil # used in org-noter--parse-root 244 | - org-noter-doc-property-in-notes nil 245 | -------------------------------------------------------------------------------- /docs/README-djvu.org: -------------------------------------------------------------------------------- 1 | * 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. 2 | ** What is org-noter? 3 | Org-noter is an org-mode document annotator. It is a rewrite of another project, [[https://github.com/rudolfochrist/interleave][Interleave]]. 4 | 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]]. 5 | ** Added features 6 | - =org-noter= can be used on djvu file. 7 | - =org-noter-create-skeleton= now works on djvu and pdf file. 8 | - Allow =:NOTER_DOCUMENT:= to be in file-level =:PROPERTIES:= drawer 9 | See the issue https://github.com/weirdNox/org-noter/issues/143 for details. 10 | - There is a module in the [[file:other/][other/]] directory called 11 | [[file:other/org-noter-nov-overlay.el][org-noter-nov-overlay]] that highlights your notes with overlays in 12 | nov-mode, to use it, either add it to your =load-path= or with [[https://github.com/jwiegley/use-package][use-package]], 13 | #+begin_src emacs-lisp 14 | (use-package org-noter-nov-overlay 15 | :ensure nil) 16 | #+end_src 17 | 18 | ** Goals 19 | - [ ] Precise location support djvu.el have both graphical (via 20 | =djvu-image-mode=) and textual (default) modes so maybe the textual 21 | mode should handle precise location like nov.el and graphical mode 22 | should do it like pdf-tools. 23 | ** Installation 24 | *** Manual installation 25 | 1. Clone this repo =git clone https://notabug.org/c1-g/org-noter-plus-djvu.git= 26 | 2. Add it to your =load-path=. 27 | ** Caveats 28 | *** This is a fork of a fork 🍴 29 | 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 30 | (See https://github.com/weirdNox/org-noter/pull/129). 31 | 32 | *** Conflicting keybindings 33 | The key @@html:@@i@@html:@@ (lowercase i) for inserting 34 | note in the document buffer will conflict with the default key to 35 | enter =djvu-image-mode=. So either you rebind one of them or you can 36 | just enter =djvu-image-mode= manually via @@html:@@M-x djvu-image-mode@@html:@@ 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /docs/README-orig.org: -------------------------------------------------------------------------------- 1 | [[https://melpa.org/#/org-noter][file:https://melpa.org/packages/org-noter-badge.svg]] 2 | [[https://stable.melpa.org/#/org-noter][file:https://stable.melpa.org/packages/org-noter-badge.svg]] 3 | * Org-noter - A synchronized, Org-mode, document annotator 4 | After using Sebastian Christ's amazing [[https://github.com/rudolfochrist/interleave][Interleave package]] for some time, I got some ideas 5 | on how I could improve upon it, usability and feature-wise. So I created this package from 6 | scratch with those ideas in mind! 7 | 8 | Org-noter's purpose is to let you create notes that are kept in sync when you scroll through 9 | the document, but that are external to it - the notes themselves live in an Org-mode file. 10 | As such, this leverages the power of [[http://orgmode.org/][Org-mode]] (the notes may have outlines, latex 11 | fragments, babel, etc...) while acting like notes that are made /inside/ the document. 12 | Also, taking notes is very simple: just press @@html:@@i@@html:@@ and annotate 13 | away! 14 | 15 | *Note*: While this is similar to ~interleave~, it is not intended to be a clone; 16 | therefore, /not every feature of the original mode is available/! You may prefer using the 17 | original, because this is a different take on the same idea. [[#diff][View some differences here.]] 18 | 19 | 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 20 | to annotate *PDF*, *EPUB*, *Microsoft Office*, DVI, PS, and OpenDocument. 21 | 22 | On a personal note, if you annotate and read lots of PDFs, give PDF Tools a try! It is 23 | great. 24 | 25 | ** Features 26 | - Easy to use annotation interface :: Just press @@html:@@i@@html:@@ in the 27 | document buffer and annotate away! 28 | 29 | - Keep your notes in sync with the document :: When you take a note by pressing 30 | @@html:@@i@@html:@@, it saves the location where you took it so it is able to show you 31 | the notes while you scroll, automatically! 32 | 33 | - Easy navigation :: You may navigate your document as usual, seeing the notes buffer scroll and show you 34 | the respective notes; however, you may also navigate by notes, only seeing annotated pages/chapters. 35 | 36 | - Isolated interface :: Each session has its own frame and the document and notes buffers are indirect 37 | buffers to the original ones. Killing any of these things will terminate your annotation session. 38 | 39 | - Simultaneous sessions :: You may open as many annotation sessions as you wish, at the same time! The 40 | root heading of each document will be made read-only to prevent you from deleting a heading from an 41 | open session. 42 | 43 | - Resume annotating from where you left :: When ~org-noter-auto-save-last-location~ is non-nil, it will 44 | save the last location you visited and pick it up when you start another session! You may also set 45 | this per-document, [[#custom][read more here]]. 46 | 47 | - Keep your notes organized :: You may arrange your notes however you want! You can create groups and 48 | nest notes (and even nest documents inside other documents)! 49 | 50 | - Annotate ~org-attach~'ed files :: If you have any attached files, it will let you choose one as 51 | the document to annotate. 52 | 53 | Many of these features are demonstrated in the screencast, so take a look if you are 54 | confused! 55 | 56 | ** Installation 57 | *** MELPA 58 | This package is [[https://melpa.org/#/org-noter][available from MELPA]], so if you want to install it and have added MELPA to 59 | your package archives, you can run 60 | @@html:@@M-x@@html:@@ ~package-install~ @@html:@@RET@@html:@@ ~org-noter~ 61 | 62 | *** Manual installation 63 | You can also install it manually, using =package.el=. 64 | 1. Download =org-noter.el= 65 | 2. Open it 66 | 3. Run @@html:@@M-x@@html:@@ ~package-install-from-buffer~ @@html:@@RET@@html:@@ 67 | 68 | ** Usage 69 | If you want to give it a try without much trouble: 70 | - Just have an Org file where you want the notes to go 71 | - Create a root heading to hold the notes 72 | - Run @@html:@@M-x@@html:@@ ~org-noter~ inside! 73 | On the first run, it will ask you for the path of the document and save it in a 74 | property. By default, it will also let you annotate an attached file [[https://orgmode.org/manual/Attachments.html][(org-attach documentation)]]. 75 | 76 | This will open a new dedicated frame where you can use [[#keys][the keybindings described here]]. 77 | 78 | More generally, there are two modes of operation. You may run 79 | @@html:@@M-x@@html:@@ ~org-noter~: 80 | - Inside a heading in an Org notes file :: This will associate that heading with a 81 | document and open a session with it. This mode is the one described in the example 82 | above. 83 | 84 | - In a document :: Run @@html:@@M-x@@html:@@ ~org-noter~ when viewing a 85 | document (eg. PDF, epub...). 86 | 87 | This will try to find the respective notes file automatically. It will 88 | search in all parent folders and some specific folders set by you. See 89 | ~org-noter-default-notes-file-names~ and ~org-noter-notes-search-path~ 90 | for more information. 91 | 92 | There is, of course, more information in the docstrings of each command. 93 | 94 | ** Screencast 95 | [[https://www.youtube.com/watch?v=Un0ZRXTzufo][Watch the screencast here!]] 96 | 97 | Note that this package has had some updates since this screencast was made, so, while the 98 | core functionality is the same, there may be some UX and feature differences. 99 | 100 | The files used to make this screencast are shipped with the package, so you can try this 101 | package even without creating the notes. 102 | 103 | ** Customization @@html:@@ 104 | There are two kinds of customizations you may do: 105 | 1. Global settings, affecting every session 106 | 2. Document-specific settings, which override the global settings 107 | 108 | 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 109 | init file. To find which settings are available, you may use the customization interface or you may just 110 | read =org-noter.el=. 111 | 112 | The best way to set *document-specific settings* is by using the utility commands provided (list below). 113 | In order to use them, you need an open session. The commands may change the settings for that session 114 | only (not surviving restarts), or for every session with that document. 115 | 116 | *List of utility commands* (check the docstrings to learn how to make the changes 117 | permanent, or revert to the default): 118 | - You may set a start location for this document, by using ~org-noter-set-start-location~. 119 | 120 | - To automatically resume from where you left, use ~org-noter-set-auto-save-last-location~. 121 | 122 | - With ~org-noter-set-notes-window-behavior~, you may change /when/ the notes window pops 123 | up. 124 | 125 | - With ~org-noter-set-notes-window-location~, you may change /where/ the notes window pops 126 | up. 127 | 128 | - ~org-noter-set-doc-split-fraction~ will ask you for the fraction of the frame that the document window 129 | occupies when split. 130 | 131 | - ~org-noter-set-hide-other~ will toggle whether or not it should hide headings not 132 | related to the executed action. 133 | 134 | - ~org-noter-set-closest-tipping-point~ will set the closest note tipping point. Also 135 | check the docstring of the variable ~org-noter-closest-tipping-point~ in order to better 136 | understand the tipping point. 137 | 138 | ** Keybindings and commands @@html:@@ 139 | :PROPERTIES: 140 | :CUSTOM_ID: keys 141 | :END: 142 | | Key | Description | Where? | 143 | | @@html:@@i@@html:@@ | Insert note | Document buffer | 144 | | @@html:@@M-i@@html:@@ | Insert precise note | Document buffer | 145 | | @@html:@@q@@html:@@ | Kill session | Document buffer | 146 | | @@html:@@M-p@@html:@@ | Sync previous page/chapter | Document and notes buffer | 147 | | @@html:@@M-.@@html:@@ | Sync current page/chapter | Document and notes buffer | 148 | | @@html:@@M-n@@html:@@ | Sync next page/chapter | Document and notes buffer | 149 | | @@html:@@C-M-p@@html:@@ | Sync previous notes | Document and notes buffer | 150 | | @@html:@@C-M-.@@html:@@ | Sync selected notes | Document and notes buffer | 151 | | @@html:@@C-M-n@@html:@@ | Sync next notes | Document and notes buffer | 152 | 153 | You can use the usual keybindings to navigate the document 154 | (@@html:@@n@@html:@@, @@html:@@p@@html:@@, 155 | @@html:@@SPC@@html:@@, ...). 156 | 157 | There are two types of sync commands: 158 | - To sync a page/chapter, means it will find the [previous|current|next] page/chapter and 159 | show the corresponding notes for that page/chapter; as such, it will always pop up the 160 | notes buffer, if it does not exist. This type of command is in relation to the current 161 | page/chapter in the document. 162 | 163 | - To sync the notes, means it will find the [previous|current|next] notes and go to the 164 | corresponding location on the document. So, you need to have the notes window open, 165 | because this type of commands is in relation to the selected notes (ie, where the cursor 166 | is). 167 | 168 | When using PDF Tools, the command ~org-noter-create-skeleton~ imports the PDF outline or 169 | annotations (or both!) as notes, and it may be used, for example, as a starting point. 170 | 171 | You may also want to check the docstrings of the functions associated with the 172 | keybindings, because there is some extra functionality in some. 173 | 174 | ** Why a rewrite from scratch? Why not contribute to the existing Interleave package? 175 | Doing a refactor on a foreign codebase takes a long time because of several factors, like 176 | introducing the ideas to the owner (with which he may even disagree), learning its 177 | internals, proposing pull requests, more back and forth in code review, etc... 178 | 179 | Besides, I like doing things from scratch, not only because it expands my skills, but also 180 | because it is something I find very rewarding! 181 | 182 | *** Non-exhaustive list of differences from Interleave @@html:@@ 183 | :PROPERTIES: 184 | :CUSTOM_ID: diff 185 | :END: 186 | **** New features 187 | - Each session is isolated, which means that it has its own frame with indirect buffers 188 | - Makes it possible to have several sessions simultaneously open 189 | - Doesn't narrow the original buffer, which continues completely accessible 190 | - Has precise notes (attached to a section of a page) 191 | - Also supports nov.el 192 | - Skeleton extraction (outline and/or annotations) 193 | - Being able to use the closest previous note when no notes are present on the current 194 | page 195 | - Closing all notes not related to the notes present in the current view 196 | - Possibility of overriding some global settings in each document or session 197 | 198 | **** Some annoyances fixed 199 | - Notes not sorted 200 | - Notes not synced when executing different page change commands, eg. goto-page or 201 | beginning-of-buffer 202 | - Sometimes it would start narrowing other parts of the buffer, giving errors when trying 203 | to go to notes. 204 | 205 | *** Changes to make in order to be compatible with documents created by Interleave 206 | This package only works like the multi-pdf mode of Interleave - you can't open a session 207 | without having a parent headline. 208 | 209 | For compatibility with existing notes made with Interleave, you can do one of two things: 210 | - Change the following property names inside the your documents: 211 | | Old | New | 212 | |------------------------+------------------| 213 | | =INTERLEAVE_PDF= | =NOTER_DOCUMENT= | 214 | | =INTERLEAVE_PAGE_NOTE= | =NOTER_PAGE= | 215 | 216 | - Set these variables on your init file: 217 | #+BEGIN_SRC emacs-lisp 218 | (setq org-noter-property-doc-file "INTERLEAVE_PDF" 219 | org-noter-property-note-location "INTERLEAVE_PAGE_NOTE") 220 | #+END_SRC 221 | 222 | ** Acknowledgments 223 | I must thank [[https://github.com/rudolfochrist][Sebastian]] for the original idea and inspiration. Also, many thanks to everyone who 224 | contributed more ideas, reported bugs and submitted PRs :) 225 | -------------------------------------------------------------------------------- /docs/org-noter-demo.org: -------------------------------------------------------------------------------- 1 | * Opening a notes session 2 | - open PDF 3 | - ~M-x org-noter~ 4 | - ~M-x org-noter-set-doc-split-fraction~ 5 | - ~M-x org-noter-set-notes-window-location, M-n~ (org-noter-set-layout?) side-by-side | stacked 6 | - ~M-x org-noter-create-skeleton, M-n~ 7 | 8 | * Navigating the document and the notes 9 | - ~SPC, n, Page-down, down~ to move forward in document 10 | - ~BACKSPACE, p, Page-up, up~ to move back in document 11 | - ~C-M-n, C-M-p~ to move to next/prev note 12 | - ~C-M-.~ sync document to notes 13 | - ~M-n, M-p~ to move to next/prev page with note(s). Always lands on the 14 | first note of the page. 15 | - ~M-.~ sync notes to document 16 | 17 | * Multicolumn setup (as needed) 18 | - Multiple column note ordering can be set up at the document or heading level 19 | - ~org-noter-pdf-set-columns~ inserts "COLUMN_EDGES" into the property drawer 20 | of the current heading. The command requests the number of columns and then 21 | asks you to click on the right edge of all but the last column. The 22 | property is inherited by all sub-headings. 23 | 24 | * Note insertion 25 | 26 | ** ~insert-note~ (~i~) 27 | - Inserts a note linked to the current page. If no title is specified, then 28 | default title "Notes for page

" is used, where

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

V: % H: 52 | %" is used, where

is the pagelabel if it exists or the page number, 53 | is the vertical distance from the top and is the horizontal 54 | position from the left. 55 | - The behavior with selected text (default title, quoting in the body) is the 56 | same as for ~insert-note~. 57 | 58 | ** No-questions note insertion 59 | - ~defcustom org-noter-insert-note-no-questions~ is default ~nil~. If set to 60 | ~t~, the note title minibuffer prompt is bypassed and a note is always 61 | create with the default title. Activate this setting if you rarely or 62 | never type in your own titles. 63 | - Both note insertion styles have a ~toggle-no-questions~ variant to get the 64 | non-default behavior. 65 | - Default keybinding for the ~toggle-no-questions~ variant adds the 66 | control-key (~C-i~ and ~C-M-i~, respectively). 67 | ** Highlighting 68 | - ~defcustom org-noter-highlight-selected-text~ controls the default 69 | highlighting behavior of selected text. 70 | - ~C-u~ prefix to any note insertion command toggles this behavior 71 | -------------------------------------------------------------------------------- /docs/org_noter_tech_notes.org: -------------------------------------------------------------------------------- 1 | :PROPERTIES: 2 | :ID: 4333050B-D293-4A41-8A14-00E6248FD17B 3 | :DRILL_LAST_INTERVAL: -1.0 4 | :DRILL_REPEATS_SINCE_FAIL: 1 5 | :DRILL_TOTAL_REPEATS: 1 6 | :DRILL_FAILURE_COUNT: 1 7 | :DRILL_AVERAGE_QUALITY: 1.0 8 | :DRILL_EASE: 2.5 9 | :NEXT_REVIEW: [2022-12-29 Thu] 10 | :MATURITY: seedling 11 | :LAST_REVIEW: [2022-12-30 Fri] 12 | :END: 13 | #+title: org-noter-tech-notes 14 | #+filetags: :seedling: 15 | 16 | Context for developing org-noter. 17 | 18 | * TOC :TOC: 19 | - [[#brief-history-of-org-noter][Brief history of org-noter]] 20 | - [[#tech-notes][Tech Notes]] 21 | - [[#session][Session]] 22 | - [[#hooks][Hooks]] 23 | - [[#notes][Notes]] 24 | - [[#locations][Locations]] 25 | - [[#note-taking-behavior][Note taking behavior]] 26 | - [[#notes-file][Notes file]] 27 | - [[#solove-nothing-to-hide][solove-nothing-to-hide]] 28 | - [[#note-from-page-1][Note from page 1]] 29 | - [[#development][Development]] 30 | - [[#unit-tests][Unit tests]] 31 | 32 | * Brief history of org-noter 33 | 34 | [[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: 35 | 36 | #+begin_quote 37 | 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 38 | #+end_quote 39 | 40 | [[https://github.com/rudolfochrist/interleave/issues/55][source]] 41 | 42 | 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. 43 | 44 | 45 | * Tech Notes 46 | 47 | ** Session 48 | org-noter session contains all the relevant info for the current session. 49 | - notes file (or buffer) 50 | - backing document 51 | - ... 52 | 53 | prereq for getting everything else going see =make-org-noter-session=. 54 | 55 | ** Hooks 56 | Hooks are used extensively to manage "non-core" functionality, that is functionality that is mode dependent. 57 | 58 | There are mode specific implementation in =modules/= directory, as well as in =tests/= (for testing). 59 | 60 | 61 | *** Errors 62 | An error like so: 63 | #+begin_src shell 64 | Lisp nesting exceeds ‘max-lisp-eval-depth’ 65 | #+end_src 66 | 67 | possibly indicates that a ~run-hook-with-args-until-success~ has failed: 68 | 69 | #+begin_src elisp 70 | (run-hook-with-args-until-success 'org-noter-set-up-document-hook document-property-value) 71 | #+end_src 72 | 73 | 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). 74 | 75 | I haven't figured out a good way to identify these. 76 | 77 | ** Notes 78 | 79 | There are two types of notes: 80 | 81 | - regular notes 82 | - precise notes 83 | 84 | 85 | 86 | *** Regular Notes 87 | 88 | Notes attached to a "page". 89 | 90 | #+begin_src org-mode 91 | :PROPERTIES: 92 | :NOTER_PAGE: 2 93 | :END: 94 | #+end_src 95 | 96 | 97 | *** Precise notes 98 | 99 | Precise notes include a coordinate vector that allows to identify the selection in the backing document and act accordingly, ie create a highlight. 100 | 101 | #+begin_src org-mode 102 | :PROPERTIES: 103 | :NOTER_PAGE: (75 0.14417344173441735 0.7955390334572491 0.6834688346883468 0.8199091284593144) 104 | :END: 105 | #+end_src 106 | 107 | ** Locations 108 | 109 | Location is a property of every note (see [[Notes]]). 110 | 111 | The concept seems to be poorly defined currently, and most of the code lives in =core=, but maybe it should move to document implementation. 112 | 113 | Currently it's either 114 | 115 | =:NOTER_PAGE: (75 0.14417344173441735 0.7955390334572491 0.6834688346883468 0.8199091284593144)= 116 | 117 | or 118 | 119 | =:NOTER_PAGE: 2= 120 | 121 | ** Note taking behavior 122 | 123 | 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]]. 124 | 125 | 126 | ** Notes file 127 | 128 | =notes.org= is in this format: 129 | 130 | #+begin_src org-mode 131 | :PROPERTIES: 132 | :ID: FAKE_90283 133 | :END: 134 | #+TITLE: Test book notes 135 | 136 | * solove-nothing-to-hide 137 | :PROPERTIES: 138 | :NOTER_DOCUMENT: pubs/solove-nothing-to-hide.pdf 139 | :END: 140 | ** Note from page 1 141 | :PROPERTIES: 142 | :NOTER_PAGE: 99 143 | :END: 144 | #+end_src 145 | 146 | Omitting the header causes =org-noter--parse-root= to work incorrectly. 147 | 148 | * Development 149 | ** Unit tests 150 | *** Requirements 151 | 152 | - [[https://github.com/cask/cask][Cask]], a project management tool for Emacs 153 | - [[https://github.com/jorgenschaefer/emacs-buttercup][Buttercup]], behavior driven Emacs testing 154 | 155 | *** Mac 156 | 157 | #+begin_src shell 158 | brew install cask 159 | cask # install dependencies 160 | cask exec buttercup -L . # exec unit tests, in the root of the project 161 | #+end_src 162 | 163 | *** GNU/Linux 164 | 165 | #+begin_src shell 166 | git clone https://github.com/cask/cask.git 167 | make -C cask install 168 | 169 | cd 170 | cask # install dependencies (in root of project, 1 time) 171 | cask exec buttercup -L . # exec unit tests, in the root of the project 172 | #+end_src 173 | -------------------------------------------------------------------------------- /docs/pre-merge_notes.org: -------------------------------------------------------------------------------- 1 | * Pre-merge deltas w/ *dmitrym0* 2 | In the diffs below the color coding is 3 | #+begin_src diff 4 | - Dmitry [f3f5a05] 5 | + Peter [6488cc6] 6 | #+end_src 7 | ** DONE *-get-buffer-file-name-* 8 | #+begin_src diff 9 | -(defun org-noter-get-buffer-file-name-* (&optional major-mode) 10 | +(defun org-noter-get-buffer-file-name-* (mode) 11 | (bound-and-true-p *-file-name)) 12 | 13 | +(add-to-list 'org-noter-get-buffer-file-name-hook #'org-noter-get-buffer-file-name-*) 14 | #+end_src 15 | 16 | - =major-mode= is a native elisp function, =mode= is a better name 17 | - the arg is not used, so the =&optional= is appropriate 18 | - for the =pdf= variant, we both use =(&optional major-mode)= 19 | 20 | proposal: =(&optional mode)= or remove the argument completely. 21 | 22 | ACTIONS: Do this in our own repos before merge 23 | 1. major-mode -> mode in module files 24 | 2. use &optional when the argument is not used in the function 25 | 26 | ** DONE -get-buffer-file-name-hook 27 | #+begin_src diff 28 | -(defcustom org-noter-get-buffer-file-name-hook '(org-noter-get-buffer-file-name-nov org-noter-get-buffer-file-name-pdf) 29 | +(defcustom org-noter-get-buffer-file-name-hook nil 30 | #+end_src 31 | 32 | should be nil in =org-noter-core= and set in modules. 33 | 34 | ACTION: already converged 35 | ** DONE *-get-precise-info-* 36 | #+begin_src diff 37 | -(defun org-noter-*--get-precise-info (major-mode) 38 | +(defun org-noter-*--get-precise-info (major-mode window) 39 | (when (eq major-mode 'djvu-read-mode) 40 | (if (region-active-p) 41 | (cons (mark) (point)) 42 | - (while (not (and (eq 'mouse-1 (car event)) 43 | - (eq window (posn-window (event-start event))))) 44 | - (setq event (read-event "Click where you want the start of the note to be!"))) 45 | - (posn-point (event-start event))))) 46 | + (let ((event nil)) 47 | + (while (not (and (eq 'mouse-1 (car event)) 48 | + (eq window (posn-window (event-start event))))) 49 | + (setq event (read-event "Click where you want the start of the note to be!"))) 50 | + (posn-point (event-start event)))))) 51 | #+end_src 52 | 53 | - calling function already calls =org-noter--get-doc-window= 54 | - =window= is used in all document modes 55 | 56 | proposal: change =major-mode= to =mode=, pass in =window= 57 | 58 | ACTION: (done) Dmitry took mine 59 | ** DONE *-goto-location 60 | #+begin_src diff 61 | -(defun org-noter-pdf-goto-location (mode location) 62 | +(defun org-noter-pdf-goto-location (mode location window) 63 | (when (memq mode '(doc-view-mode pdf-view-mode)) 64 | (let ((top (org-noter--get-location-top location)) 65 | - (window (org-noter--get-doc-window)) 66 | (left (org-noter--get-location-left location))) 67 | #+end_src 68 | - calling function already calls =org-noter--get-doc-window= 69 | - nov and djvu don't need the =window= argument 70 | 71 | proposal: we discuss this one, but I think it's better to not call functions 72 | unnecessarily 73 | 74 | ACTION: 75 | pass in window, use &optional as appropriate. 76 | 77 | ** DONE *-check-location-property 78 | #+begin_src diff 79 | (defun org-noter-pdf-check-location-property (&optional property) 80 | "Check if PROPERTY is a valid location property" 81 | - (equal 5 (length (read property)))) 82 | + t) 83 | #+end_src 84 | 85 | location can be 86 | 1. page 87 | 2. page v-pos 88 | 3. page v-pos . h-pos 89 | 90 | neither function works properly. need to read the calling function to 91 | determine course of action.. 92 | 93 | ACTION: done, gone on Dmitry's side. 94 | P: check diff, remove if it's still there. 95 | ** DONE -doc--get-precise-info 96 | #+begin_src diff 97 | +(defun org-noter-doc--get-precise-info (major-mode window) 98 | + (when (eq major-mode 'doc-view-mode) 99 | (let ((event nil)) 100 | (while (not (and (eq 'mouse-1 (car event)) 101 | (eq window (posn-window (event-start event))))) 102 | (setq event (read-event "Click where you want the start of the note to be!"))) 103 | - (let ((col-row (posn-col-row (event-start event)))) 104 | - (org-noter--conv-page-scroll-percentage (+ (window-vscroll) (cdr col-row)) 105 | - (+ (window-hscroll) (car col-row)))))))) 106 | + (org-noter--conv-page-scroll-percentage (+ (window-vscroll) 107 | + (cdr (posn-col-row (event-start event)))))))) 108 | #+end_src 109 | Dmitry removed this function at [9d437bf] 110 | 111 | ACTION: Dmitry revive on his side. 112 | ** DONE --doc-approx-location-hook 113 | #+begin_src diff 114 | (defcustom org-noter--doc-approx-location-hook nil 115 | - "This returns an approximate location if no precise info is passed: (PAGE 0) 116 | - or if precise info is passed, it's (PAGE 0 0 0 0) where 0s are the precise coords) 117 | -" 118 | + "TODO" 119 | :group 'org-noter 120 | :type 'hook) 121 | #+end_src 122 | 123 | docstring needs to be updated. 124 | 125 | ACTION: Dmitry reverted 126 | ** DONE --note-search-no-recurse :11fc0a8:9dfac53: 127 | #+begin_src diff 128 | +(defconst org-noter--note-search-no-recurse (delete 'headline (append org-element-all-elements nil)) 129 | + "List of elements that shouldn't be recursed into when searching for notes.") 130 | #+end_src 131 | 132 | called in =org-noter--get-view-info= by =org-element-map= 133 | #+begin_src diff 134 | - nil nil (delete 'headline (append org-element-all-elements nil)))) 135 | + nil nil org-noter--note-search-no-recurse) 136 | #+end_src 137 | 138 | but this defconst is used by =org-noter--map-ignore-headings-with-doc-file=, which is 139 | used by all of the sync functions 140 | 141 | probably should keep it, and since we keep it, use it in 142 | =org-noter--get-view-info= 143 | 144 | ACTION: safe for Dmitry to cherry-pick these commits, but 145 | =with-current-buffer= call gets removed. This is the one change I took from 146 | ~cbpnk~ 147 | ** DONE org-noter--create-session :9dfac53: 148 | #+begin_src diff 149 | (defun org-noter--create-session (ast document-property-value notes-file-path) 150 | (let* ((raw-value-not-empty (> (length (org-element-property :raw-value ast)) 0)) 151 | - (link-p (or (string-match-p org-bracket-link-regexp document-property-value) 152 | + (link-p (or (string-match-p org-link-bracket-re document-property-value) 153 | (string-match-p org-noter--url-regexp document-property-value))) 154 | #+end_src 155 | =org-bracket-link-regexp= is obsolete. keep mine. 156 | 157 | ACTION: safe for Dmitry to cherry-pick 158 | ** DONE org-noter--narrow-to-root (ast) :dfe7df2: 159 | #+begin_src diff 160 | - (when ast 161 | + (when (and ast (not (org-noter--no-heading-p))) 162 | (save-excursion 163 | (goto-char (org-element-property :contents-begin ast)) 164 | (org-show-entry) 165 | - (when (org-at-heading-p) (org-narrow-to-subtree)) 166 | + (org-narrow-to-subtree) 167 | (org-cycle-hide-drawers 'all)))) 168 | #+end_src 169 | "I don't really understand this bit of code, especially what `ast' is, but 170 | it breaks narrowing when multiple documents' notes are stored in a single 171 | file." 172 | 173 | ACTION: safe for Dmitry to cherry-pick 174 | ** DONE org-noter--get-location-page (location) :DM:629fbb6: 175 | #+begin_src diff 176 | "Get the page number given a LOCATION of form (page top . left) or (page . top)." 177 | - (message "===> %s" location) 178 | - (if (listp location) 179 | - (car location) 180 | - location)) 181 | + (car location)) 182 | #+end_src 183 | 184 | ACTION: Peter -- what happens with a page note (no precise location)? does (car location) make an 185 | error? 186 | Answer: No, (car location) works fine because for a page note, location is a 187 | cons cell, e.g. (19 . 0) by the time it reaches this function. 188 | 189 | @DM -- I think we should go back to the original (car location). 190 | 191 | HISTORY: 192 | - 5bc5754 Ahmed Shariff original code 193 | - c1ed245 c1g moved code from org-noter.el to org-noter-core.el, changing 194 | function name 195 | - 629fbb6 introduced by DM 196 | 197 | ** DONE org-noter-kill-session :9dfac53: 198 | #+begin_src diff 199 | (with-current-buffer notes-buffer 200 | (remove-hook 'kill-buffer-hook 'org-noter--handle-kill-buffer t) 201 | (restore-buffer-modified-p nil)) 202 | - (unless org-noter-use-indirect-buffer 203 | + (when org-noter-use-indirect-buffer 204 | (kill-buffer notes-buffer)) 205 | #+end_src 206 | kill the notes buffer **when** an indirect buffer is used, not **unless** it 207 | is used 208 | 209 | ACTION: safe for Dmitry to cherry-pick 210 | ** DONE use cl-lib or native elisp hash tables rather than the =ht= package. 211 | -------------------------------------------------------------------------------- /emacs-devel.el: -------------------------------------------------------------------------------- 1 | (require 'cask "/opt/homebrew/share/emacs/site-lisp/cask/cask.el") 2 | (cask-initialize ".") 3 | 4 | (setq mac-option-modifier 'meta) 5 | 6 | (push (expand-file-name ".") load-path) 7 | (require 'org-noter) 8 | -------------------------------------------------------------------------------- /modules/org-noter-djvu.el: -------------------------------------------------------------------------------- 1 | ;;; org-noter-djvu.el --- Module for DJVU -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2022 c1-g 4 | 5 | ;; Author: c1-g 6 | ;; Keywords: multimedia 7 | 8 | ;; This program is free software; you can redistribute it and/or modify 9 | ;; it under the terms of the GNU General Public License as published by 10 | ;; the Free Software Foundation, either version 3 of the License, or 11 | ;; (at your option) any later version. 12 | 13 | ;; This program is distributed in the hope that it will be useful, 14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ;; GNU General Public License for more details. 17 | 18 | ;; You should have received a copy of the GNU General Public License 19 | ;; along with this program. If not, see . 20 | 21 | ;;; Commentary: 22 | 23 | ;; 24 | 25 | ;;; Code: 26 | (require 'org-noter) 27 | 28 | (defun org-noter-djvu--pretty-print-location (location) 29 | (org-noter--with-valid-session 30 | (when (eq (org-noter--session-doc-mode session) 'djvu-read-mode) 31 | (format "%s" (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 0)) 32 | (car location) 33 | location))))) 34 | 35 | (add-to-list 'org-noter--pretty-print-location-hook #'org-noter-djvu--pretty-print-location) 36 | (add-to-list 'org-noter--pretty-print-location-for-title-hook #'org-noter-djvu--pretty-print-location) 37 | 38 | (defun org-noter-djvu-approx-location-cons (mode &optional precise-info _force-new-ref) 39 | (when (eq mode 'djvu-read-mode) 40 | (cons djvu-doc-page (if (or (numberp precise-info) 41 | (and (consp precise-info) 42 | (numberp (car precise-info)) 43 | (numberp (cdr precise-info)))) 44 | precise-info 45 | (max 1 (/ (+ (window-start) (window-end nil t)) 2)))))) 46 | 47 | (add-to-list 'org-noter--doc-approx-location-hook #'org-noter-djvu-approx-location-cons) 48 | 49 | (defun org-noter-djvu--get-precise-info (mode window) 50 | (when (eq mode 'djvu-read-mode) 51 | (if (region-active-p) 52 | (cons (mark) (point)) 53 | (let ((event nil)) 54 | (while (not (and (eq 'mouse-1 (car event)) 55 | (eq window (posn-window (event-start event))))) 56 | (setq event (read-event "Click where you want the start of the note to be!"))) 57 | (posn-point (event-start event)))))) 58 | 59 | (add-to-list 'org-noter--get-precise-info-hook #'org-noter-djvu--get-precise-info) 60 | 61 | (defun org-noter-djvu-setup-handler (mode) 62 | (when (eq mode 'djvu-read-mode) 63 | (advice-add 'djvu-init-page :after 'org-noter--location-change-advice) 64 | t)) 65 | 66 | (add-to-list 'org-noter-set-up-document-hook #'org-noter-djvu-setup-handler) 67 | 68 | (defun org-noter-djvu-goto-location (mode location &optional window) 69 | (when (eq mode 'djvu-read-mode) 70 | (djvu-goto-page (car location)) 71 | (goto-char (org-noter--get-location-top location)))) 72 | 73 | (add-to-list 'org-noter--doc-goto-location-hook #'org-noter-djvu-goto-location) 74 | 75 | (defun org-noter-djvu--get-current-view (mode) 76 | (when (eq mode 'djvu-read-mode) 77 | (vector 'paged (car (org-noter-djvu-approx-location-cons mode))))) 78 | 79 | (add-to-list 'org-noter--get-current-view-hook #'org-noter-djvu--get-current-view) 80 | 81 | (defun org-noter-djvu--get-selected-text (mode) 82 | (when (and (eq mode 'djvu-read-mode) 83 | (region-active-p)) 84 | (buffer-substring-no-properties (mark) (point)))) 85 | 86 | (add-to-list 'org-noter-get-selected-text-hook #'org-noter-djvu--get-selected-text) 87 | 88 | (defun org-noter-create-skeleton-djvu (mode) 89 | (when (eq mode 'djvu-read-mode) 90 | (org-noter--with-valid-session 91 | (let* ((ast (org-noter--parse-root)) 92 | (top-level (or (org-element-property :level ast) 0)) 93 | output-data) 94 | (require 'thingatpt) 95 | (with-current-buffer (djvu-ref outline-buf) 96 | (unless (string= (buffer-string) "") 97 | (push (vector "Skeleton" nil 1) output-data) 98 | (save-excursion 99 | (goto-char (point-min)) 100 | (while (not (looking-at "^$")) 101 | (push (vector (string-trim-right (string-trim (thing-at-point 'line t)) " [[:digit:]]+") 102 | (list (string-trim-left (string-trim (thing-at-point 'line t)) ".* ")) 103 | (+ 2 (how-many " " (point-at-bol) (point-at-eol)))) output-data) 104 | (forward-line))))) 105 | 106 | (with-current-buffer (org-noter--session-notes-buffer session) 107 | ;; NOTE(nox): org-with-wide-buffer can't be used because we want to reset the 108 | ;; narrow region to include the new headings 109 | (widen) 110 | (save-excursion 111 | (goto-char (org-element-property :end ast)) 112 | 113 | (let (last-absolute-level 114 | title location relative-level contents 115 | level) 116 | 117 | (dolist (data (nreverse output-data)) 118 | (setq title (aref data 0) 119 | location (aref data 1) 120 | relative-level (aref data 2)) 121 | 122 | (setq last-absolute-level (+ top-level relative-level) 123 | level last-absolute-level) 124 | 125 | (org-noter--insert-heading level title) 126 | 127 | (when location 128 | (org-entry-put nil org-noter-property-note-location (org-noter--pretty-print-location location))) 129 | 130 | (when org-noter-doc-property-in-notes 131 | (org-entry-put nil org-noter-property-doc-file (org-noter--session-property-text session)) 132 | (org-entry-put nil org-noter--property-auto-save-last-location "nil")))) 133 | 134 | (setq ast (org-noter--parse-root)) 135 | (org-noter--narrow-to-root ast) 136 | (goto-char (org-element-property :begin ast)) 137 | (when (org-at-heading-p) (outline-hide-subtree)) 138 | (org-show-children 2))) 139 | output-data)))) 140 | 141 | (add-to-list 'org-noter-create-skeleton-functions #'org-noter-create-skeleton-djvu) 142 | 143 | (provide 'org-noter-djvu) 144 | ;;; org-noter-djvu.el ends here 145 | -------------------------------------------------------------------------------- /modules/org-noter-nov.el: -------------------------------------------------------------------------------- 1 | ;;; org-noter-nov.el --- Integration with Nov.el -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2022 c1-g 4 | 5 | ;; Author: c1-g 6 | ;; Keywords: multimedia 7 | 8 | ;; This program is free software; you can redistribute it and/or modify 9 | ;; it under the terms of the GNU General Public License as published by 10 | ;; the Free Software Foundation, either version 3 of the License, or 11 | ;; (at your option) any later version. 12 | 13 | ;; This program is distributed in the hope that it will be useful, 14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ;; GNU General Public License for more details. 17 | 18 | ;; You should have received a copy of the GNU General Public License 19 | ;; along with this program. If not, see . 20 | 21 | ;;; Commentary: 22 | 23 | ;; 24 | 25 | ;;; Code: 26 | (require 'org-noter) 27 | 28 | (defvar nov-documents-index) 29 | (defvar nov-file-name) 30 | 31 | (defun org-noter-get-buffer-file-name-nov (&optional mode) 32 | (bound-and-true-p nov-file-name)) 33 | 34 | (add-to-list 'org-noter-get-buffer-file-name-hook #'org-noter-get-buffer-file-name-nov) 35 | 36 | (defun org-noter-nov-approx-location-cons (mode &optional precise-info _force-new-ref) 37 | (org-noter--with-valid-session 38 | (when (eq mode 'nov-mode) 39 | (cons nov-documents-index (if (or (numberp precise-info) 40 | (and (consp precise-info) 41 | (numberp (car precise-info)) 42 | (numberp (cdr precise-info)))) 43 | precise-info 44 | (max 1 (/ (+ (window-start) (window-end nil t)) 2))))))) 45 | 46 | (add-to-list 'org-noter--doc-approx-location-hook #'org-noter-nov-approx-location-cons) 47 | 48 | (defun org-noter-nov-setup-handler (mode) 49 | (when (eq mode 'nov-mode) 50 | (advice-add 'nov-render-document :after 'org-noter--nov-scroll-handler) 51 | (add-hook 'window-scroll-functions 'org-noter--nov-scroll-handler nil t) 52 | t)) 53 | 54 | (add-to-list 'org-noter-set-up-document-hook #'org-noter-nov-setup-handler) 55 | 56 | (defun org-noter-nov--pretty-print-location (location) 57 | (org-noter--with-valid-session 58 | (when (eq (org-noter--session-doc-mode session) 'nov-mode) 59 | (format "%s" (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 1)) 60 | (org-noter--get-location-page location) 61 | location))))) 62 | 63 | (add-to-list 'org-noter--pretty-print-location-hook #'org-noter-nov--pretty-print-location) 64 | (add-to-list 'org-noter--pretty-print-location-for-title-hook #'org-noter-nov--pretty-print-location) 65 | 66 | (defun org-noter-nov--get-precise-info (mode window) 67 | (when (eq mode 'nov-mode) 68 | (if (region-active-p) 69 | (cons (mark) (point)) 70 | (let ((event nil)) 71 | (while (not (and (eq 'mouse-1 (car event)) 72 | (eq window (posn-window (event-start event))))) 73 | (setq event (read-event "Click where you want the start of the note to be!"))) 74 | (posn-point (event-start event)))))) 75 | 76 | (add-to-list 'org-noter--get-precise-info-hook #'org-noter-nov--get-precise-info) 77 | 78 | (defun org-noter-nov-goto-location (mode location &optional window) 79 | (when (eq mode 'nov-mode) 80 | (setq nov-documents-index (org-noter--get-location-page location)) 81 | (nov-render-document) 82 | (goto-char (org-noter--get-location-top location)) 83 | ;; NOTE(nox): This needs to be here, because it would be issued anyway after 84 | ;; everything and would run org-noter--nov-scroll-handler. 85 | (recenter))) 86 | 87 | (add-to-list 'org-noter--doc-goto-location-hook #'org-noter-nov-goto-location) 88 | 89 | (defun org-noter-nov--get-current-view (mode) 90 | (when (eq mode 'nov-mode) 91 | (vector 'nov 92 | (org-noter-nov-approx-location-cons mode (window-start)) 93 | (org-noter-nov-approx-location-cons mode (window-end nil t))))) 94 | 95 | (add-to-list 'org-noter--get-current-view-hook #'org-noter-nov--get-current-view) 96 | 97 | (defun org-noter-nov--get-selected-text (mode) 98 | (when (and (eq mode 'nov-mode) (region-active-p)) 99 | (buffer-substring-no-properties (mark) (point)))) 100 | 101 | (add-to-list 'org-noter-get-selected-text-hook #'org-noter-nov--get-selected-text) 102 | 103 | 104 | ;; Shamelessly stolen code from Yuchen Li. 105 | ;; This code is originally from org-noter-plus package. 106 | ;; At https://github.com/yuchen-lea/org-noter-plus 107 | 108 | (defun org-noter--handle-nov-toc-item (ol depth) 109 | (mapcar (lambda (li) 110 | (mapcar (lambda (a-or-ol) 111 | (pcase-exhaustive (dom-tag a-or-ol) 112 | ('a 113 | (vector :depth depth 114 | :title (dom-text a-or-ol) 115 | :href (esxml-node-attribute 'href a-or-ol))) 116 | ('ol 117 | (org-noter--handle-nov-toc-item a-or-ol 118 | (1+ depth))))) 119 | (dom-children li))) 120 | (dom-children ol))) 121 | 122 | (defun org-noter-create-skeleton-epub (mode) 123 | "Epub outline with nov link." 124 | (when (eq mode 'nov-mode) 125 | (require 'esxml) 126 | (require 'nov) 127 | (require 'dom) 128 | (org-noter--with-valid-session 129 | (let* ((ast (org-noter--parse-root)) 130 | (top-level (or (org-element-property :level ast) 0)) 131 | output-data) 132 | (with-current-buffer (org-noter--session-doc-buffer session) 133 | (let* ((toc-path (cdr (aref nov-documents 0))) 134 | (toc-tree (with-temp-buffer 135 | (insert (nov-ncx-to-html toc-path)) 136 | (replace-regexp "\n" 137 | "" 138 | nil 139 | (point-min) 140 | (point-max)) 141 | (libxml-parse-html-region (point-min) 142 | (point-max)))) 143 | (origin-index nov-documents-index) 144 | (origin-point (point))) 145 | (dolist (item 146 | (nreverse (flatten-tree (org-noter--handle-nov-toc-item toc-tree 1)))) 147 | (let ((relative-level (aref item 1)) 148 | (title (aref item 3)) 149 | (url (aref item 5))) 150 | (apply 'nov-visit-relative-file 151 | (nov-url-filename-and-target url)) 152 | (when (not (integerp nov-documents-index)) 153 | (setq nov-documents-index 0)) 154 | (push (vector title (list nov-documents-index (point)) relative-level) output-data))) 155 | (push (vector "Skeleton" (list 0) 1) output-data) 156 | 157 | (nov-goto-document origin-index) 158 | (goto-char origin-point))) 159 | (save-excursion 160 | (goto-char (org-element-property :end ast)) 161 | (with-current-buffer (org-noter--session-notes-buffer session) 162 | (dolist (data output-data) 163 | (setq title (aref data 0) 164 | location (aref data 1) 165 | relative-level (aref data 2)) 166 | 167 | (setq last-absolute-level (+ top-level relative-level) 168 | level last-absolute-level) 169 | 170 | (org-noter--insert-heading level title) 171 | 172 | (when location 173 | (org-entry-put nil org-noter-property-note-location (org-noter--pretty-print-location location))) 174 | 175 | (when org-noter-doc-property-in-notes 176 | (org-entry-put nil org-noter-property-doc-file (org-noter--session-property-text session)) 177 | (org-entry-put nil org-noter--property-auto-save-last-location "nil"))) 178 | (setq ast (org-noter--parse-root)) 179 | (org-noter--narrow-to-root ast) 180 | (goto-char (org-element-property :begin ast)) 181 | (outline-hide-subtree) 182 | (org-show-children 2))) 183 | output-data)))) 184 | 185 | (add-to-list 'org-noter-create-skeleton-functions #'org-noter-create-skeleton-epub) 186 | 187 | (provide 'org-noter-nov) 188 | ;;; org-noter-nov.el ends here 189 | -------------------------------------------------------------------------------- /modules/org-noter-pdf.el: -------------------------------------------------------------------------------- 1 | ;;; org-noter-pdf.el --- Modules for PDF-Tools and DocView mode -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2022 c1-g 4 | 5 | ;; Author: c1-g 6 | ;; Keywords: multimedia 7 | 8 | ;; This program is free software; you can redistribute it and/or modify 9 | ;; it under the terms of the GNU General Public License as published by 10 | ;; the Free Software Foundation, either version 3 of the License, or 11 | ;; (at your option) any later version. 12 | 13 | ;; This program is distributed in the hope that it will be useful, 14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ;; GNU General Public License for more details. 17 | 18 | ;; You should have received a copy of the GNU General Public License 19 | ;; along with this program. If not, see . 20 | 21 | ;;; Commentary: 22 | 23 | ;; 24 | 25 | ;;; Code: 26 | (require 'org-noter) 27 | 28 | 29 | (cl-defstruct pdf-highlight page coords) 30 | 31 | 32 | (defun org-noter-pdf--get-highlight () 33 | "If there's an active pdf selection, returns a that contains all 34 | the relevant info (page, coordinates) 35 | 36 | Otherwise returns nil" 37 | (if-let* ((_ (pdf-view-active-region-p)) 38 | (page (image-mode-window-get 'page)) 39 | (coords (pdf-view-active-region))) 40 | (make-pdf-highlight :page page :coords coords) 41 | nil)) 42 | 43 | (add-to-list 'org-noter--get-highlight-location-hook 'org-noter-pdf--get-highlight) 44 | 45 | (defun org-noter-pdf--pretty-print-highlight (highlight-info) 46 | (format "%s" highlight-info)) 47 | 48 | (add-to-list 'org-noter--pretty-print-highlight-location-hook #'org-noter-pdf--pretty-print-highlight) 49 | 50 | (defun org-noter-pdf-approx-location-cons (mode &optional precise-info _force-new-ref) 51 | "Returns (page . 0) except when creating a precise-note, 52 | where (pabe v-pos) or (page v-pos . h-pos) is returned" 53 | (when (memq mode '(doc-view-mode pdf-view-mode)) 54 | (cons (image-mode-window-get 'page) (if (or (numberp precise-info) 55 | (and (consp precise-info) 56 | (numberp (car precise-info)) 57 | (numberp (cdr precise-info)))) 58 | precise-info 0)))) 59 | 60 | (add-to-list 'org-noter--doc-approx-location-hook #'org-noter-pdf-approx-location-cons) 61 | 62 | (defun org-noter-get-buffer-file-name-pdf (&optional mode) 63 | "Return the file naming backing the document buffer" 64 | (bound-and-true-p pdf-file-name)) 65 | 66 | (add-to-list 'org-noter-get-buffer-file-name-hook #'org-noter-get-buffer-file-name-pdf) 67 | 68 | (defun org-noter-pdf-view-setup-handler (mode) 69 | (when (eq mode 'pdf-view-mode) 70 | ;; (setq buffer-file-name document-path) 71 | (pdf-view-mode) 72 | (add-hook 'pdf-view-after-change-page-hook 'org-noter--doc-location-change-handler nil t) 73 | t)) 74 | 75 | (add-to-list 'org-noter-set-up-document-hook #'org-noter-pdf-view-setup-handler) 76 | 77 | (defun org-noter-doc-view-setup-handler (mode) 78 | (when (eq mode 'doc-view-mode) 79 | ;; (setq buffer-file-name document-path) 80 | (doc-view-mode) 81 | (advice-add 'doc-view-goto-page :after 'org-noter--location-change-advice) 82 | t)) 83 | 84 | (add-to-list 'org-noter-set-up-document-hook #'org-noter-doc-view-setup-handler) 85 | 86 | (defun org-noter-pdf--pretty-print-location (location) 87 | "Full precision location for property drawers" 88 | (org-noter--with-valid-session 89 | (when (memq (org-noter--session-doc-mode session) '(doc-view-mode pdf-view-mode)) 90 | (format "%s" (if (or (not (org-noter--get-location-top location)) (<= (org-noter--get-location-top location) 0)) 91 | (car location) 92 | location))))) 93 | 94 | (add-to-list 'org-noter--pretty-print-location-hook #'org-noter-pdf--pretty-print-location) 95 | 96 | (defun org-noter-pdf--pretty-print-location-for-title (location) 97 | "Human readable location with page label and v/h percentages. Doc-view falls back to original pp function." 98 | (org-noter--with-valid-session 99 | (let ((mode (org-noter--session-doc-mode session)) 100 | (vpos (org-noter--get-location-top location)) 101 | (hpos (org-noter--get-location-left location)) 102 | (vtxt "") (htxt "") 103 | pagelabel) 104 | (cond ((eq mode 'pdf-view-mode) ; for default title, reference pagelabel instead of page 105 | (if (> hpos 0) 106 | (setq htxt (format " H: %d%%" (round (* 100 hpos))))) 107 | (if (or (> vpos 0) (> hpos 0)) 108 | (setq vtxt (format " V: %d%%" (round (* 100 vpos))))) 109 | (select-window (org-noter--get-doc-window)) 110 | (setq pagelabel (pdf-view-current-pagelabel)) 111 | (select-window (org-noter--get-notes-window)) 112 | (format "%s%s%s" pagelabel vtxt htxt)) 113 | ((eq mode 'doc-view-mode) ; fall back to original pp for doc-mode 114 | (org-noter-pdf--pretty-print-location location)))))) 115 | 116 | (add-to-list 'org-noter--pretty-print-location-for-title-hook #'org-noter-pdf--pretty-print-location-for-title) 117 | 118 | (defun org-noter-pdf--get-precise-info (mode window) 119 | (when (eq mode 'pdf-view-mode) 120 | (let (v-position h-position) 121 | (if (pdf-view-active-region-p) 122 | (let ((edges (car (pdf-view-active-region)))) 123 | (setq v-position (min (nth 1 edges) (nth 3 edges)) 124 | h-position (min (nth 0 edges) (nth 2 edges)))) 125 | 126 | (let ((event nil)) 127 | (while (not (and (eq 'mouse-1 (car event)) 128 | (eq window (posn-window (event-start event))))) 129 | (setq event (read-event "Click where you want the start of the note to be!"))) 130 | (let* ((col-row (posn-col-row (event-start event))) 131 | (click-position (org-noter--conv-page-scroll-percentage (+ (window-vscroll) (cdr col-row)) 132 | (+ (window-hscroll) (car col-row))))) 133 | (setq v-position (car click-position) 134 | h-position (cdr click-position))))) 135 | (cons v-position h-position)))) 136 | 137 | (add-to-list 'org-noter--get-precise-info-hook #'org-noter-pdf--get-precise-info) 138 | 139 | (defun org-noter-doc--get-precise-info (mode window) 140 | (when (eq mode 'doc-view-mode) 141 | (let ((event nil)) 142 | (while (not (and (eq 'mouse-1 (car event)) 143 | (eq window (posn-window (event-start event))))) 144 | (setq event (read-event "Click where you want the start of the note to be!"))) 145 | (org-noter--conv-page-scroll-percentage (+ (window-vscroll) 146 | (cdr (posn-col-row (event-start event)))))))) 147 | 148 | (add-to-list 'org-noter--get-precise-info-hook #'org-noter-doc--get-precise-info) 149 | 150 | (defun org-noter-pdf-goto-location (mode location window) 151 | (when (memq mode '(doc-view-mode pdf-view-mode)) 152 | (let ((top (org-noter--get-location-top location)) 153 | (window (org-noter--get-doc-window)) 154 | (left (org-noter--get-location-left location))) 155 | 156 | (if (eq mode 'doc-view-mode) 157 | (doc-view-goto-page (org-noter--get-location-page location)) 158 | (pdf-view-goto-page (org-noter--get-location-page location)) 159 | ;; NOTE(nox): This timer is needed because the tooltip may introduce a delay, 160 | ;; so syncing multiple pages was slow 161 | (when (>= org-noter-arrow-delay 0) 162 | (when org-noter--arrow-location (cancel-timer (aref org-noter--arrow-location 0))) 163 | (setq org-noter--arrow-location 164 | (vector (run-with-idle-timer org-noter-arrow-delay nil 'org-noter--show-arrow) 165 | window 166 | top 167 | left)))) 168 | (image-scroll-up (- (org-noter--conv-page-percentage-scroll top) 169 | (floor (+ (window-vscroll) org-noter-vscroll-buffer))))))) 170 | 171 | (add-to-list 'org-noter--doc-goto-location-hook #'org-noter-pdf-goto-location) 172 | 173 | (defun org-noter-pdf--get-current-view (mode) 174 | (when (memq mode '(doc-view-mode pdf-view-mode)) 175 | (vector 'paged (car (org-noter-pdf-approx-location-cons mode))))) 176 | 177 | (add-to-list 'org-noter--get-current-view-hook #'org-noter-pdf--get-current-view) 178 | 179 | (defun org-noter-pdf--get-selected-text (mode) 180 | (when (and (eq mode 'pdf-view-mode) 181 | (pdf-view-active-region-p)) 182 | (mapconcat 'identity (pdf-view-active-region-text) ? ))) 183 | 184 | (add-to-list 'org-noter-get-selected-text-hook #'org-noter-pdf--get-selected-text) 185 | 186 | ;; NOTE(nox): From machc/pdf-tools-org 187 | (defun org-noter--pdf-tools-edges-to-region (edges) 188 | "Get 4-entry region (LEFT TOP RIGHT BOTTOM) from several EDGES." 189 | (when edges 190 | (let ((left0 (nth 0 (car edges))) 191 | (top0 (nth 1 (car edges))) 192 | (bottom0 (nth 3 (car edges))) 193 | (top1 (nth 1 (car (last edges)))) 194 | (right1 (nth 2 (car (last edges)))) 195 | (bottom1 (nth 3 (car (last edges))))) 196 | (list left0 197 | (+ top0 (/ (- bottom0 top0) 3)) 198 | right1 199 | (- bottom1 (/ (- bottom1 top1) 3)))))) 200 | 201 | (defun org-noter-create-skeleton-pdf (mode) 202 | "Create notes skeleton with the PDF outline or annotations." 203 | (when (eq mode 'pdf-view-mode) 204 | (org-noter--with-valid-session 205 | (let* ((ast (org-noter--parse-root)) 206 | (top-level (or (org-element-property :level ast) 0)) 207 | (options '(("Outline" . (outline)) 208 | ("Annotations" . (annots)) 209 | ("Both" . (outline annots)))) 210 | answer output-data) 211 | (with-current-buffer (org-noter--session-doc-buffer session) 212 | (setq answer (assoc (completing-read "What do you want to import? " options nil t) options)) 213 | 214 | (when (memq 'outline answer) 215 | (dolist (item (pdf-info-outline)) 216 | (let ((type (alist-get 'type item)) 217 | (page (alist-get 'page item)) 218 | (depth (alist-get 'depth item)) 219 | (title (alist-get 'title item)) 220 | (top (alist-get 'top item))) 221 | (when (and (eq type 'goto-dest) (> page 0)) 222 | (push (vector title (cons page top) (1+ depth) nil) output-data))))) 223 | 224 | (when (memq 'annots answer) 225 | (let ((possible-annots (list '("Highlights" . highlight) 226 | '("Underlines" . underline) 227 | '("Squigglies" . squiggly) 228 | '("Text notes" . text) 229 | '("Strikeouts" . strike-out) 230 | '("Links" . link) 231 | '("ALL" . all))) 232 | chosen-annots insert-contents pages-with-links) 233 | (while (> (length possible-annots) 1) 234 | (let* ((chosen-string (completing-read "Which types of annotations do you want? " 235 | possible-annots nil t)) 236 | (chosen-pair (assoc chosen-string possible-annots))) 237 | (cond ((eq (cdr chosen-pair) 'all) 238 | (dolist (annot possible-annots) 239 | (when (and (cdr annot) (not (eq (cdr annot) 'all))) 240 | (push (cdr annot) chosen-annots))) 241 | (setq possible-annots nil)) 242 | ((cdr chosen-pair) 243 | (push (cdr chosen-pair) chosen-annots) 244 | (setq possible-annots (delq chosen-pair possible-annots)) 245 | (when (= 1 (length chosen-annots)) (push '("DONE") possible-annots))) 246 | (t 247 | (setq possible-annots nil))))) 248 | 249 | (setq insert-contents (y-or-n-p "Should we insert the annotations contents? ")) 250 | 251 | (dolist (item (pdf-info-getannots)) 252 | (let* ((type (alist-get 'type item)) 253 | (page (alist-get 'page item)) 254 | (edges (or (org-noter--pdf-tools-edges-to-region (alist-get 'markup-edges item)) 255 | (alist-get 'edges item))) 256 | (top (nth 1 edges)) 257 | (item-subject (alist-get 'subject item)) 258 | (item-contents (alist-get 'contents item)) 259 | name contents) 260 | (when (and (memq type chosen-annots) (> page 0)) 261 | (if (eq type 'link) 262 | (cl-pushnew page pages-with-links) 263 | (setq name (cond ((eq type 'highlight) "Highlight") 264 | ((eq type 'underline) "Underline") 265 | ((eq type 'squiggly) "Squiggly") 266 | ((eq type 'text) "Text note") 267 | ((eq type 'strike-out) "Strikeout"))) 268 | 269 | (when insert-contents 270 | (setq contents (cons (pdf-info-gettext page edges) 271 | (and (or (and item-subject (> (length item-subject) 0)) 272 | (and item-contents (> (length item-contents) 0))) 273 | (concat (or item-subject "") 274 | (if (and item-subject item-contents) "\n" "") 275 | (or item-contents "")))))) 276 | 277 | (push (vector (format "%s on page %d" name page) (cons page top) 'inside contents) 278 | output-data))))) 279 | 280 | (dolist (page pages-with-links) 281 | (let ((links (pdf-info-pagelinks page)) 282 | type) 283 | (dolist (link links) 284 | (setq type (alist-get 'type link)) 285 | (unless (eq type 'goto-dest) ;; NOTE(nox): Ignore internal links 286 | (let* ((edges (alist-get 'edges link)) 287 | (title (alist-get 'title link)) 288 | (top (nth 1 edges)) 289 | (target-page (alist-get 'page link)) 290 | target heading-text) 291 | 292 | (unless (and title (> (length title) 0)) (setq title (pdf-info-gettext page edges))) 293 | 294 | (cond 295 | ((eq type 'uri) 296 | (setq target (alist-get 'uri link) 297 | heading-text (format "Link on page %d: [[%s][%s]]" page target title))) 298 | 299 | ((eq type 'goto-remote) 300 | (setq target (concat "file:" (alist-get 'filename link)) 301 | heading-text (format "Link to document on page %d: [[%s][%s]]" page target title)) 302 | (when target-page 303 | (setq heading-text (concat heading-text (format " (target page: %d)" target-page))))) 304 | 305 | (t (error "Unexpected link type"))) 306 | 307 | (push (vector heading-text (cons page top) 'inside nil) output-data)))))))) 308 | 309 | 310 | (when output-data 311 | (if (memq 'annots answer) 312 | (setq output-data 313 | (sort output-data 314 | (lambda (e1 e2) 315 | (or (not (aref e1 1)) 316 | (and (aref e2 1) 317 | (org-noter--compare-locations '< (aref e1 1) (aref e2 1))))))) 318 | (setq output-data (nreverse output-data))) 319 | 320 | (push (vector "Skeleton" nil 1 nil) output-data))) 321 | 322 | (with-current-buffer (org-noter--session-notes-buffer session) 323 | ;; NOTE(nox): org-with-wide-buffer can't be used because we want to reset the 324 | ;; narrow region to include the new headings 325 | (widen) 326 | (save-excursion 327 | (goto-char (org-element-property :end ast)) 328 | 329 | (let (last-absolute-level 330 | title location relative-level contents 331 | level) 332 | (dolist (data output-data) 333 | (setq title (aref data 0) 334 | location (aref data 1) 335 | relative-level (aref data 2) 336 | contents (aref data 3)) 337 | 338 | (if (symbolp relative-level) 339 | (setq level (1+ last-absolute-level)) 340 | (setq last-absolute-level (+ top-level relative-level) 341 | level last-absolute-level)) 342 | 343 | (org-noter--insert-heading level title) 344 | 345 | (when location 346 | (org-entry-put nil org-noter-property-note-location (org-noter--pretty-print-location location))) 347 | 348 | (when org-noter-doc-property-in-notes 349 | (org-entry-put nil org-noter-property-doc-file (org-noter--session-property-text session)) 350 | (org-entry-put nil org-noter--property-auto-save-last-location "nil")) 351 | 352 | (when (car contents) 353 | (org-noter--insert-heading (1+ level) "Contents") 354 | (insert (car contents))) 355 | (when (cdr contents) 356 | (org-noter--insert-heading (1+ level) "Comment") 357 | (insert (cdr contents))))) 358 | 359 | (setq ast (org-noter--parse-root)) 360 | (org-noter--narrow-to-root ast) 361 | (goto-char (org-element-property :begin ast)) 362 | (outline-hide-subtree) 363 | (org-show-children 2))) 364 | output-data)))) 365 | 366 | (add-to-list 'org-noter-create-skeleton-functions #'org-noter-create-skeleton-pdf) 367 | 368 | (defun org-noter-pdf--create-missing-annotation () 369 | "Add a highlight from a selected note." 370 | (let* ((location (org-noter--parse-location-property (org-noter--get-containing-element)))) 371 | (with-selected-window (org-noter--get-doc-window) 372 | (org-noter-pdf-goto-location 'pdf-view-mode location) 373 | (pdf-annot-add-highlight-markup-annotation (cdr location))))) 374 | 375 | (add-to-list 'org-noter--add-highlight-hook #'org-noter-pdf-highlight-location) 376 | 377 | (defun org-noter-pdf-highlight-location (mode precise-location) 378 | "Highlight a precise location in PDF" 379 | (message "---> %s %s" mode precise-location) 380 | (when (and (memq mode '(doc-view-mode pdf-view-mode)) 381 | (pdf-view-active-region-p)) 382 | (pdf-annot-add-highlight-markup-annotation (pdf-view-active-region)))) 383 | 384 | (defun org-noter-pdf-convert-to-location-cons (location) 385 | "converts (page v . h) precise locations to (page v') such that 386 | v' represents the fractional distance through the page along 387 | columns, so it takes values between 0 and the number of columns. 388 | Each column is specified by its right edge as a fractional 389 | horizontal position. Output is nil for standard notes and (page 390 | v') for precise notes." 391 | (if-let* ((_ (and (consp location) (consp (cdr location)))) 392 | (bb (current-buffer)) ; debugging code - we are in the doc window, 393 | ; but need to be in the notes window for next 394 | ; line to work 395 | (column-edges-string (org-entry-get nil "COLUMN_EDGES" t)) 396 | (right-edge-list (car (read-from-string column-edges-string))) 397 | ;;(ncol (length left-edge-list)) 398 | (page (car location)) 399 | (v-pos (cadr location)) 400 | (h-pos (cddr location)) 401 | (column-index (seq-position right-edge-list h-pos #'>=))) 402 | (cons page (+ v-pos column-index)))) 403 | 404 | (add-to-list 'org-noter--convert-to-location-cons-hook #'org-noter-pdf-convert-to-location-cons) 405 | 406 | (defun org-noter-pdf-set-columns (num-columns) 407 | "Interactively set the COLUMN_EDGES property for the current heading. 408 | 409 | The number of columns can be given as a prefix or in the 410 | minibuffer. The user is then prompted to click on the right edge 411 | of each column, except for the last one. Subheadings of the 412 | current heading inherit the COLUMN_EDGES property." 413 | (interactive "NEnter number of columns: ") 414 | (select-window (org-noter--get-doc-window)) 415 | (let (event 416 | edge-list 417 | (window (car (window-list)))) 418 | (dotimes (ii (1- num-columns)) 419 | (while (not (and (eq 'mouse-1 (car event)) 420 | (eq window (posn-window (event-start event))))) 421 | (setq event (read-event (format "Click on the right boundary of column %d" (1+ ii))))) 422 | (let* ((col-row (posn-col-row (event-start event))) 423 | (click-position (org-noter--conv-page-scroll-percentage (+ (window-vscroll) (cdr col-row)) 424 | (+ (window-hscroll) (car col-row)))) 425 | (h-position (cdr click-position))) 426 | (setq event nil) 427 | (setq edge-list (append edge-list (list h-position))))) 428 | (setq edge-list (append edge-list '(1))) 429 | (select-window (org-noter--get-notes-window)) 430 | (org-entry-put nil "COLUMN_EDGES" (format "%s" (princ edge-list))))) 431 | 432 | (provide 'org-noter-pdf) 433 | ;;; org-noter-pdf.el ends here 434 | -------------------------------------------------------------------------------- /org-noter-test-utils.el: -------------------------------------------------------------------------------- 1 | 2 | (require 'log4e) 3 | 4 | ;; org-noter-test logger = ont 5 | (log4e:deflogger "ont" "ont %t [%l] %m" "%H:%M:%S") 6 | (ont--log-enable-logging) 7 | (ont--log-enable-debugging) 8 | (ont--log-enable-messaging) 9 | (ont--log-set-level 'info) 10 | (ont--log-debug "ont") 11 | 12 | 13 | (defvar mock-contents-simple-notes-file 14 | " 15 | :PROPERTIES: 16 | :ID: FAKE_1 17 | :END: 18 | #+TITLE: Test book notes (simple) 19 | * solove-nothing-to-hide 20 | :PROPERTIES: 21 | :NOTER_DOCUMENT: pubs/solove-nothing-to-hide.pdf 22 | :END: 23 | ") 24 | 25 | (defvar mock-contents-simple-notes-file-with-a-single-note 26 | ":PROPERTIES: 27 | :ID: FAKE_90283 28 | :END: 29 | #+TITLE: Test book notes 30 | 31 | * solove-nothing-to-hide 32 | :PROPERTIES: 33 | :NOTER_DOCUMENT: pubs/solove-nothing-to-hide.pdf 34 | :END: 35 | ** Note from page 1 36 | :PROPERTIES: 37 | :NOTER_PAGE: 99 38 | :END: 39 | " 40 | ) 41 | 42 | 43 | 44 | 45 | ;;;;;;;;;;; 46 | ;; helpers 47 | (defun org-noter-core-test-create-session () 48 | "Call this manually with an existing notes buffer to generate a new session" 49 | (org-noter--create-session (org-noter--parse-root) "pubs/solove-nothing-to-hide.pdf" org-noter-test-file)) 50 | 51 | 52 | (defun with-mock-contents (contents lambda) 53 | "Create a real buffer with CONTENTS and then execute the LAMBDA" 54 | (ont--log-debug "\n--------------------------------------------") 55 | 56 | ;; TODO: when an assert fails in buttercup, an exception (??) is thrown, 57 | ;; so temp file isnt being cleaned up. This is the sledgehammer approach. 58 | ;; Needs to be fixed so that it's cleaned up properly. 59 | (when (boundp 'org-noter-test-file) 60 | (progn 61 | (ont--log-debug (format "Removing org-noter-test-file: %s\n" org-noter-test-file)) 62 | (delete-file org-noter-test-file))) 63 | (let* ((tempfile (make-temp-file "Notes" nil ".org" contents))) 64 | (ont--log-debug (format "Creating a tempfile: %s\n" tempfile)) 65 | (setq org-noter-test-file tempfile) 66 | (ont--log-debug "Opening the file..") 67 | (org-mode) 68 | (find-file tempfile) 69 | (org-mode) 70 | (ont--log-debug "Starting the test..") 71 | (ont--log-debug "%s" (buffer-string)) 72 | (funcall lambda) 73 | (ont--log-debug "About to kill buffer..") 74 | (kill-current-buffer) 75 | (ont--log-debug (format "Removing tempfile %s" tempfile)) 76 | (delete-file tempfile) 77 | (ont--log-debug "+++++++++++++++++++++++++++++++++++++++++") 78 | )) 79 | 80 | 81 | ;;;;;;;;;;;;;;;;;;;;;;;;;; 82 | ;; hooks - org-noter calls these 83 | 84 | (defun org-noter-test-get-selected-text (mode) 85 | "⚠️org-noter-core-test-return-text 86 | org-noter-core-test-return-text 87 | org-noter-core-test-return-text 88 | org-noter-core-test-return-text 89 | org-noter-core-test-return-text 90 | ") 91 | 92 | 93 | (defun org-noter-core-test-document-property (&optional param) 94 | org-noter-test-file) 95 | 96 | (defun org-noter-core-test-view-setup-handler (&optional param) 97 | t) 98 | 99 | (defun org-noter-core-test-open-document-functions (&optional doc) 100 | (find-file (org-noter-core-test-document-property))) 101 | 102 | (defun org-noter-core-test-approx-location (major-mode &optional precise-info _force-new-ref) 103 | (cons 99 precise-info)) 104 | 105 | (defun org-noter-core-test-get-current-view (mode) 106 | t) 107 | 108 | ;; TODO This doesn't look right 109 | (defun org-noter-core-test-get-precise-info (mode window) 110 | (list 1 2 3 4)) 111 | 112 | (defun org-noter-core-test-pretty-print-location (location) 113 | (format "%s" location)) 114 | 115 | (defun org-noter-core-test-add-highlight (major-mode precise-info) 116 | t) 117 | 118 | (defun org-noter-core-test-get-current-view (mode) 119 | 'org-noter-core-test-view) 120 | 121 | (defun org-noter-core-test-get-highlight-location () 122 | "HARDCODED_HIGHLIGHT_LOCATION") 123 | 124 | (defun org-noter-core-test-pretty-print-location-for-title (location) 125 | "TEST PRETTY PRINT LOCATION") 126 | 127 | (defun create-org-noter-test-session () 128 | 129 | ;; if this is not set; make-session fails and the test crashes with a stack overflow. 130 | (setq org-noter-always-create-frame nil) 131 | 132 | (setq org-noter-highlight-selected-text t) 133 | 134 | ;; setup spies so we can verify that things have been called 135 | (spy-on 'org-noter-test-get-selected-text :and-call-through) 136 | (spy-on 'org-noter-core-test-approx-location :and-call-through) 137 | (spy-on 'org-noter-core-test-get-precise-info :and-call-through) 138 | (spy-on 'org-noter-core-test-add-highlight :and-call-through) 139 | (spy-on 'org-noter-core-test-get-current-view :and-call-through) 140 | 141 | ;; register all the hooks so we can fake a org-noter-test mode 142 | (add-to-list 'org-noter-get-selected-text-hook #'org-noter-test-get-selected-text) 143 | (add-to-list 'org-noter-parse-document-property-hook #'org-noter-core-test-document-property) 144 | (add-to-list 'org-noter-set-up-document-hook #'org-noter-core-test-view-setup-handler) 145 | (add-to-list 'org-noter-open-document-functions #'org-noter-core-test-open-document-functions) 146 | (add-to-list 'org-noter--doc-approx-location-hook #'org-noter-core-test-approx-location) 147 | (add-to-list 'org-noter--get-current-view-hook #'org-noter-core-test-get-current-view) 148 | (add-to-list 'org-noter--get-precise-info-hook #'org-noter-core-test-get-precise-info) 149 | (add-to-list 'org-noter--pretty-print-location-hook #'org-noter-core-test-pretty-print-location) 150 | (add-to-list 'org-noter--pretty-print-location-for-title-hook #'org-noter-core-test-pretty-print-location-for-title) 151 | (add-to-list 'org-noter--add-highlight-hook #'org-noter-core-test-add-highlight) 152 | (add-to-list 'org-noter--get-highlight-location-hook #'org-noter-core-test-get-highlight-location) 153 | ) 154 | 155 | 156 | 157 | 158 | (provide 'org-noter-test-utils) 159 | -------------------------------------------------------------------------------- /org-noter.el: -------------------------------------------------------------------------------- 1 | ;;; org-noter.el --- A synchronized, Org-mode, document annotator -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2017-2018 Gonçalo Santos 4 | 5 | ;; Author: Gonçalo Santos (aka. weirdNox@GitHub) 6 | ;; Homepage: https://github.com/weirdNox/org-noter 7 | ;; Keywords: lisp pdf interleave annotate external sync notes documents org-mode 8 | ;; Package-Requires: ((emacs "24.4") (cl-lib "0.6") (org "9.0")) 9 | ;; Version: 1.4.1 10 | 11 | ;; This file is not part of GNU Emacs. 12 | 13 | ;; This program is free software; you can redistribute it and/or modify 14 | ;; it under the terms of the GNU General Public License as published by 15 | ;; the Free Software Foundation, either version 3 of the License, or 16 | ;; (at your option) any later version. 17 | 18 | ;; This program is distributed in the hope that it will be useful, 19 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | ;; GNU General Public License for more details. 22 | 23 | ;; You should have received a copy of the GNU General Public License 24 | ;; along with this program. If not, see . 25 | 26 | ;;; Commentary: 27 | 28 | ;; The idea is to let you create notes that are kept in sync when you scroll through the 29 | ;; document, but that are external to it - the notes themselves live in an Org-mode file. As 30 | ;; such, this leverages the power of Org-mode (the notes may have outlines, latex fragments, 31 | ;; babel, etc...) while acting like notes that are made /in/ the document. 32 | 33 | ;; Also, I must thank Sebastian for the original idea and inspiration! 34 | ;; Link to the original Interleave package: 35 | ;; https://github.com/rudolfochrist/interleave 36 | 37 | ;;; Code: 38 | (require 'org-element) 39 | (require 'cl-lib) 40 | 41 | (require 'org-noter-core) 42 | 43 | (declare-function org-entry-put "org") 44 | (declare-function org-with-wide-buffer "org-macs") 45 | 46 | ;;;###autoload 47 | (defun org-noter (&optional arg) 48 | "Start `org-noter' session. 49 | 50 | There are two modes of operation. You may create the session from: 51 | - The Org notes file 52 | - The document to be annotated (PDF, EPUB, ...) 53 | 54 | - Creating the session from notes file ----------------------------------------- 55 | This will open a session for taking your notes, with indirect 56 | buffers to the document and the notes side by side. Your current 57 | window configuration won't be changed, because this opens in a 58 | new frame. 59 | 60 | You only need to run this command inside a heading (which will 61 | hold the notes for this document). If no document path property is found, 62 | this command will ask you for the target file. 63 | 64 | With a prefix universal argument ARG, only check for the property 65 | in the current heading, don't inherit from parents. 66 | 67 | With 2 prefix universal arguments ARG, ask for a new document, 68 | even if the current heading annotates one. 69 | 70 | With a prefix number ARG: 71 | - Greater than 0: Open the document like `find-file' 72 | - Equal to 0: Create session with `org-noter-always-create-frame' toggled 73 | - Less than 0: Open the folder containing the document 74 | 75 | - Creating the session from the document --------------------------------------- 76 | This will try to find a notes file in any of the parent folders. 77 | The names it will search for are defined in `org-noter-default-notes-file-names'. 78 | It will also try to find a notes file with the same name as the 79 | document, giving it the maximum priority. 80 | 81 | When it doesn't find anything, it will interactively ask you what 82 | you want it to do. The target notes file must be in a parent 83 | folder (direct or otherwise) of the document. 84 | 85 | You may pass a prefix ARG in order to make it let you choose the 86 | notes file, even if it finds one." 87 | (interactive "P") 88 | (cond 89 | ;; NOTE(nox): Creating the session from notes file 90 | ((eq major-mode 'org-mode) 91 | (let* ((notes-file-path (buffer-file-name)) 92 | (document-property (org-noter--get-or-read-document-property 93 | (not (equal arg '(4))) 94 | (equal arg '(16)))) 95 | (org-noter-always-create-frame 96 | (if (and (numberp arg) (= arg 0)) 97 | (not org-noter-always-create-frame) 98 | org-noter-always-create-frame)) 99 | (ast (org-noter--parse-root (vector (current-buffer) document-property))) 100 | (session-id (get-text-property (org-element-property :begin ast) org-noter--id-text-property)) 101 | session) 102 | 103 | ;; Check for prefix value 104 | (if (or (numberp arg) (eq arg '-)) 105 | ;; Yes, user's given a prefix value. 106 | (cond ((> (prefix-numeric-value arg) 0) 107 | ;; Is the prefix value greater than 0? 108 | (find-file document-property)) 109 | ;; Open the document like `find-file'. 110 | 111 | ;; Is the prefix value less than 0? 112 | ((< (prefix-numeric-value arg) 0) 113 | ;; Open the folder containing the document. 114 | (find-file (file-name-directory document-property)))) 115 | 116 | ;; No, user didn't give a prefix value 117 | ;; NOTE(nox): Check if it is an existing session 118 | (when session-id 119 | (setq session (cl-loop for session in org-noter--sessions 120 | when (= (org-noter--session-id session) session-id) 121 | return session)))) 122 | 123 | (if session 124 | (let* ((org-noter--session session) 125 | (location (org-noter--parse-location-property 126 | (org-noter--get-containing-element)))) 127 | (org-noter--setup-windows session) 128 | (when location (org-noter--doc-goto-location location)) 129 | (select-frame-set-input-focus (org-noter--session-frame session))) 130 | ;; It's not an existing session, create a new session. 131 | (org-noter--create-session ast document-property notes-file-path)))) 132 | 133 | ;; NOTE(nox): Creating the session from the annotated document 134 | ((memq major-mode org-noter-supported-modes) 135 | (if (org-noter--valid-session org-noter--session) 136 | (progn (org-noter--setup-windows org-noter--session) 137 | (select-frame-set-input-focus (org-noter--session-frame org-noter--session))) 138 | 139 | ;; NOTE(nox): `buffer-file-truename' is a workaround for modes that delete 140 | ;; `buffer-file-name', and may not have the same results 141 | (let* ((buffer-file-name (or (run-hook-with-args-until-success 'org-noter-get-buffer-file-name-hook major-mode) 142 | buffer-file-name)) 143 | (document-path (or buffer-file-name buffer-file-truename 144 | (error "This buffer does not seem to be visiting any file"))) 145 | (document-name (file-name-nondirectory document-path)) 146 | (document-base (file-name-base document-name)) 147 | (document-directory (if buffer-file-name 148 | (file-name-directory buffer-file-name) 149 | (if (file-equal-p document-name buffer-file-truename) 150 | default-directory 151 | (file-name-directory buffer-file-truename)))) 152 | ;; NOTE(nox): This is the path that is actually going to be used, and should 153 | ;; be the same as `buffer-file-name', but is needed for the truename workaround 154 | (document-used-path (expand-file-name document-name document-directory)) 155 | 156 | (search-names (flatten-list 157 | (append org-noter-default-notes-file-names 158 | (list (concat document-base ".org")) 159 | (run-hook-with-args-until-success 160 | 'org-noter-find-additional-notes-functions document-path)))) 161 | notes-files-annotating ; List of files annotating document 162 | notes-files ; List of found notes files (annotating or not) 163 | 164 | (document-location (org-noter--doc-approx-location))) 165 | 166 | ;; NOTE(nox): Check the search path 167 | (dolist (path org-noter-notes-search-path) 168 | (dolist (name search-names) 169 | (let ((file-name (expand-file-name name path))) 170 | (when (file-exists-p file-name) 171 | (push file-name notes-files) 172 | (when (org-noter--check-if-document-is-annotated-on-file document-path file-name) 173 | (push file-name notes-files-annotating)))))) 174 | 175 | ;; NOTE(nox): `search-names' is in reverse order, so we only need to (push ...) 176 | ;; and it will end up in the correct order 177 | (dolist (name search-names) 178 | (let ((directory (locate-dominating-file document-directory name)) 179 | file) 180 | (when directory 181 | (setq file (expand-file-name name directory)) 182 | (unless (member file notes-files) (push file notes-files)) 183 | (when (org-noter--check-if-document-is-annotated-on-file document-path file) 184 | (push file notes-files-annotating))))) 185 | 186 | (setq search-names (nreverse search-names)) 187 | 188 | (when (or arg (not notes-files-annotating)) 189 | (when (or arg (not notes-files)) 190 | (let* ((notes-file-name (completing-read "What name do you want the notes to have? " 191 | search-names nil t)) 192 | list-of-possible-targets 193 | target) 194 | 195 | ;; NOTE(nox): Create list of targets from current path 196 | (catch 'break 197 | (let ((current-directory document-directory) 198 | file-name) 199 | (while t 200 | (setq file-name (expand-file-name notes-file-name current-directory)) 201 | (when (file-exists-p file-name) 202 | (setq file-name (propertize file-name 'display 203 | (concat file-name 204 | (propertize " -- Exists!" 'face '(:foregorund "green"))))) 205 | (push file-name list-of-possible-targets) 206 | (throw 'break nil)) 207 | 208 | (push file-name list-of-possible-targets) 209 | 210 | (when (string= current-directory 211 | (setq current-directory 212 | (file-name-directory (directory-file-name current-directory)))) 213 | (throw 'break nil))))) 214 | (setq list-of-possible-targets (nreverse list-of-possible-targets)) 215 | 216 | ;; NOTE(nox): Create list of targets from search path 217 | (dolist (path org-noter-notes-search-path) 218 | (when (file-exists-p path) 219 | (let ((file-name (expand-file-name notes-file-name path))) 220 | (unless (member file-name list-of-possible-targets) 221 | (when (file-exists-p file-name) 222 | (setq file-name (propertize file-name 'display 223 | (concat file-name 224 | (propertize " -- Exists!" 'face '(:foreground "green")))))) 225 | (push file-name list-of-possible-targets))))) 226 | 227 | (setq target (completing-read "Where do you want to save it? " list-of-possible-targets 228 | nil t)) 229 | (set-text-properties 0 (length target) nil target) 230 | (unless (file-exists-p target) (write-region "" nil target)) 231 | 232 | (setq notes-files (list target)))) 233 | 234 | (when (> (length notes-files) 1) 235 | (setq notes-files (list (completing-read "In which notes file should we create the heading? " 236 | notes-files nil t)))) 237 | 238 | (if (member (car notes-files) notes-files-annotating) 239 | ;; NOTE(nox): This is needed in order to override with the arg 240 | (setq notes-files-annotating notes-files) 241 | (with-current-buffer (find-file-noselect (car notes-files)) 242 | (goto-char (point-max)) 243 | (insert (if (save-excursion (beginning-of-line) (looking-at "[[:space:]]*$")) "" "\n") 244 | "* " document-base) 245 | (org-entry-put nil org-noter-property-doc-file 246 | (file-relative-name document-used-path 247 | (file-name-directory (car notes-files))))) 248 | (setq notes-files-annotating notes-files))) 249 | 250 | (when (> (length (delete-dups notes-files-annotating)) 1) 251 | (setq notes-files-annotating (list (completing-read "Which notes file should we open? " 252 | notes-files-annotating nil t)))) 253 | 254 | (with-current-buffer (find-file-noselect (car notes-files-annotating)) 255 | (org-with-point-at (point-min) 256 | (catch 'break 257 | (while (re-search-forward (org-re-property org-noter-property-doc-file) nil t) 258 | (when (file-equal-p (expand-file-name (match-string 3) 259 | (file-name-directory (car notes-files-annotating))) 260 | document-path) 261 | (let ((org-noter--start-location-override document-location)) 262 | (org-noter arg)) 263 | (throw 'break t))))))))))) 264 | 265 | (provide 'org-noter) 266 | 267 | ;;; org-noter.el ends here 268 | -------------------------------------------------------------------------------- /other/org-noter-citar.el: -------------------------------------------------------------------------------- 1 | ;;; org-noter-citar.el --- Module for finding note files from `citar' -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2021 c1-g 4 | 5 | ;; Author: c1-g 6 | ;; Keywords: convenience 7 | 8 | ;; This program is free software; you can redistribute it and/or modify 9 | ;; it under the terms of the GNU General Public License as published by 10 | ;; the Free Software Foundation, either version 3 of the License, or 11 | ;; (at your option) any later version. 12 | 13 | ;; This program is distributed in the hope that it will be useful, 14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ;; GNU General Public License for more details. 17 | 18 | ;; You should have received a copy of the GNU General Public License 19 | ;; along with this program. If not, see . 20 | 21 | ;;; Code: 22 | (require 'citar) 23 | (require 'org-ref) 24 | (require 'seq) 25 | 26 | ;; Regexp stolen from org-roam-bibtex; orb-utils-citekey-re. 27 | (defvar org-noter-citar-cite-key-re 28 | (rx 29 | (or 30 | (seq (group-n 2 (regexp 31 | ;; If Org-ref is available, use its types 32 | ;; default to "cite" 33 | (if (boundp 'org-ref-cite-types) 34 | (regexp-opt 35 | (mapcar 36 | (lambda (el) 37 | ;; Org-ref v3 cite type is a list of strings 38 | ;; Org-ref v2 cite type is a plain string 39 | (or (car-safe el) el)) 40 | org-ref-cite-types)) 41 | "cite"))) 42 | ":" 43 | (or 44 | ;; Org-ref v2 style `cite:links' 45 | (group-n 1 (+ (any "a-zA-Z0-9_:.-"))) 46 | ;; Org-ref v3 style `cite:Some&key' 47 | (seq (*? (not "&")) "&" 48 | (group-n 1 (+ (any "!#-+./:<>-@^-`{-~-" word)))))) 49 | ;; Org-cite [cite/@citations] 50 | (seq "@" (group-n 1 (+ (any "!#-+./:<>-@^-`{-~-" word)))))) 51 | "Universal regexp to match citations in ROAM_REFS. 52 | 53 | Supports Org-ref v2 and v3 and Org-cite.") 54 | 55 | (defun org-noter-citar-find-document-from-refs (cite-key) 56 | "Return a note file associated with CITE-KEY. 57 | When there is more than one note files associated with CITE-KEY, have 58 | user select one of them." 59 | (when (and (stringp cite-key) (string-match org-noter-citar-cite-key-re cite-key)) 60 | (let* ((key (match-string 1 cite-key)) 61 | (files (hash-table-values (citar-get-files key))) 62 | (url (hash-table-values (citar-get-links key))) 63 | (documents (seq-remove #'file-directory-p (append (flatten-list files) (flatten-list url))))) 64 | (cond ((= (length documents) 1) 65 | (car documents)) 66 | ((> (length documents) 1) 67 | (completing-read (format "Which document from %s?: " key) documents)))))) 68 | 69 | (defun org-noter-citar-find-notes-from-this-file (filename) 70 | (let* ((citekey) 71 | (entry-alist (maphash (lambda (key val) 72 | (when-let ((file (citar-get-value citar-file-variable val))) 73 | (when (string-match-p filename file) 74 | (push key citekey)))) 75 | (citar-get-entries)))) 76 | 77 | (flatten-list (mapcan (lambda (node) 78 | (org-id-find-id-file (car (split-string node)))) 79 | (gethash (car citekey) (citar-get-notes citekey)))))) 80 | 81 | (add-to-list 'org-noter-parse-document-property-hook #'org-noter-citar-find-document-from-refs) 82 | 83 | (add-to-list 'org-noter-find-additional-notes-functions #'org-noter-citar-find-notes-from-this-file) 84 | 85 | (provide 'org-noter-citar) 86 | ;;; org-noter-citar.el ends here 87 | -------------------------------------------------------------------------------- /other/org-noter-dynamic-block.el: -------------------------------------------------------------------------------- 1 | ;;; org-noter-dynamic-block.el --- Use special blocks as notes -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2021 c1-g 4 | 5 | ;; Author: c1-g 6 | ;; Keywords: multimedia 7 | 8 | ;; This program is free software; you can redistribute it and/or modify 9 | ;; it under the terms of the GNU General Public License as published by 10 | ;; the Free Software Foundation, either version 3 of the License, or 11 | ;; (at your option) any later version. 12 | 13 | ;; This program is distributed in the hope that it will be useful, 14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ;; GNU General Public License for more details. 17 | 18 | ;; You should have received a copy of the GNU General Public License 19 | ;; along with this program. If not, see . 20 | 21 | ;;; Commentary: 22 | 23 | ;; 24 | 25 | ;;; Code: 26 | (require 'org-noter-core) 27 | 28 | (defun org-noter-insert-precise-dynamic-block (&optional toggle-no-questions) 29 | "Insert note associated with a specific location. 30 | This will ask you to click where you want to scroll to when you 31 | sync the document to this note. You should click on the top of 32 | that part. Will always create a new note. 33 | 34 | When text is selected, it will automatically choose the top of 35 | the selected text as the location and the text itself as the 36 | title of the note (you may change it anyway!). 37 | 38 | See `org-noter-insert-note' docstring for more." 39 | (interactive "P") 40 | (org-noter--with-valid-session 41 | (let ((org-noter-insert-note-no-questions (if toggle-no-questions 42 | (not org-noter-insert-note-no-questions) 43 | org-noter-insert-note-no-questions))) 44 | (org-noter-insert-dynamic-block (org-noter--get-precise-info))))) 45 | 46 | (defun org-noter-insert-dynamic-block (&optional precise-info) 47 | "Insert note associated with the current location. 48 | 49 | This command will prompt for a title of the note and then insert 50 | it in the notes buffer. When the input is empty, a title based on 51 | `org-noter-default-heading-title' will be generated. 52 | 53 | If there are other notes related to the current location, the 54 | prompt will also suggest them. Depending on the value of the 55 | variable `org-noter-closest-tipping-point', it may also 56 | suggest the closest previous note. 57 | 58 | PRECISE-INFO makes the new note associated with a more 59 | specific location (see `org-noter-insert-precise-note' for more 60 | info). 61 | 62 | When you insert into an existing note and have text selected on 63 | the document buffer, the variable `org-noter-insert-selected-text-inside-note' 64 | defines if the text should be inserted inside the note." 65 | (interactive) 66 | (org-noter--with-valid-session 67 | (let* ((ast (org-noter--parse-root)) 68 | (contents (org-element-contents ast)) 69 | (window (org-noter--get-notes-window 'force)) 70 | (selected-text 71 | (pcase (org-noter--session-doc-mode session) 72 | ('pdf-view-mode 73 | (when (pdf-view-active-region-p) 74 | (mapconcat 'identity (pdf-view-active-region-text) ? ))) 75 | 76 | ((or 'nov-mode 'djvu-read-mode) 77 | (when (region-active-p) 78 | (buffer-substring-no-properties (mark) (point)))))) 79 | 80 | force-new 81 | (location (org-noter--doc-approx-location (or precise-info 'interactive) (gv-ref force-new))) 82 | (view-info (org-noter--get-view-info (org-noter--get-current-view) location))) 83 | 84 | (let ((inhibit-quit t)) 85 | (with-local-quit 86 | (select-frame-set-input-focus (window-frame window)) 87 | (select-window window) 88 | 89 | ;; IMPORTANT(nox): Need to be careful changing the next part, it is a bit 90 | ;; complicated to get it right... 91 | 92 | (let ((point (point)) 93 | (minibuffer-local-completion-map org-noter--completing-read-keymap) 94 | collection default default-begin title 95 | (empty-lines-number (if org-noter-separate-notes-from-heading 2 1))) 96 | 97 | (cond 98 | ;; NOTE(nox): Both precise and without questions will create new notes 99 | ((or precise-info force-new) 100 | (setq default (and selected-text (replace-regexp-in-string "\n" " " selected-text)))) 101 | (org-noter-insert-note-no-questions) 102 | (t 103 | (dolist (note-cons (org-noter--view-info-notes view-info)) 104 | (let ((display (org-element-property :raw-value (car note-cons))) 105 | (begin (org-element-property :begin (car note-cons)))) 106 | (push (cons display note-cons) collection) 107 | (when (and (>= point begin) (> begin (or default-begin 0))) 108 | (setq default display 109 | default-begin begin)))))) 110 | 111 | ;; NOTE(nox): Inserting a new note 112 | (let ((reference-element-cons (org-noter--view-info-reference-for-insertion view-info)) 113 | level) 114 | 115 | (if reference-element-cons 116 | (progn 117 | (cond 118 | ((eq (car reference-element-cons) 'before) 119 | (goto-char (org-element-property :begin (cdr reference-element-cons)))) 120 | ((eq (car reference-element-cons) 'after) 121 | (goto-char (org-element-property :end (cdr reference-element-cons))))) 122 | ;; NOTE(nox): This is here to make the automatic "should insert blank" work better. 123 | (when (org-at-heading-p) (backward-char)) 124 | (setq level (org-element-property :level (cdr reference-element-cons)))) 125 | 126 | (goto-char (or (org-element-map contents 'section 127 | (lambda (section) (org-element-property :end section)) 128 | nil t org-element-all-elements) 129 | (org-element-map ast 'section 130 | (lambda (section) (org-element-property :end section)) 131 | nil t org-element-all-elements)))) 132 | 133 | ;; (setq level (1+ (or (org-element-property :level ast) 0)))) 134 | 135 | ;; NOTE(nox): This is needed to insert in the right place 136 | (unless (org-noter--no-heading-p) (outline-show-entry)) 137 | ;; (org-noter--insert-heading level title empty-lines-number location) 138 | (insert 139 | "\n" 140 | (string-join (list (format "#+BEGIN: note %s" 141 | (if location 142 | (concat ":" org-noter-property-note-location 143 | (format " %S" location)) 144 | "")) 145 | (or selected-text "") 146 | "#+END:") 147 | "\n") 148 | "\n") 149 | 150 | (when (org-noter--session-hide-other session) (org-overview)) 151 | 152 | (setf (org-noter--session-num-notes-in-view session) 153 | (1+ (org-noter--session-num-notes-in-view session))))) 154 | 155 | (org-show-set-visibility t) 156 | (org-cycle-hide-drawers 'all) 157 | (org-cycle-show-empty-lines t))) 158 | 159 | (when quit-flag 160 | ;; NOTE(nox): If this runs, it means the user quitted while creating a note, so 161 | ;; revert to the previous window. 162 | (select-frame-set-input-focus (org-noter--session-frame session)) 163 | (select-window (get-buffer-window (org-noter--session-doc-buffer session))))))) 164 | 165 | (defun org-dblock-write:note (params) 166 | (let ((location (plist-get params 167 | (intern (concat ":" org-noter-property-note-location)))) 168 | (content (plist-get params :content)) 169 | (session org-noter--session) 170 | (origin-window (selected-window)) 171 | (origin-location)) 172 | 173 | (org-noter--with-valid-session 174 | (setq origin-location (org-noter--doc-approx-location)) 175 | (when (and location 176 | (org-noter--get-location-top location) 177 | (org-noter--get-location-left location)) 178 | (org-noter--doc-goto-location location) 179 | (with-current-buffer (org-noter--session-doc-buffer session) 180 | (setq content 181 | (pcase major-mode 182 | ('pdf-view-mode (pdf-info-gettext (car location) (cdr location))) 183 | ((or 'nov-mode 'djvu-read-mode) 184 | (buffer-substring (org-noter--get-location-top location) 185 | (org-noter--get-location-left location)))))) 186 | (org-noter--doc-goto-location origin-location) 187 | (select-window origin-window))) 188 | (insert content))) 189 | 190 | (defun org-noter--get-location-dynamic-block (dblock) 191 | (let ((params (read (concat "(" (org-element-property :arguments dblock) ")")))) 192 | (format "%S" (plist-get params (intern (concat ":" org-noter-property-note-location)))))) 193 | 194 | (defun org-noter-get-containing-dynamic-block (&optional _include-root) 195 | (org-noter--with-valid-session 196 | (org-with-wide-buffer 197 | (let ((elt (org-element-at-point))) 198 | (catch 'break 199 | (while (org-element-property :parent elt) 200 | (cond 201 | ((eq (org-element-type elt) 'dynamic-block) 202 | (throw 'break elt)) 203 | (t 204 | (setq elt (org-element-property :parent elt)))))))))) 205 | 206 | (add-hook 'org-noter--get-containing-element-hook #'org-noter-get-containing-dynamic-block) 207 | 208 | (add-hook 'org-noter--get-location-property-hook #'org-noter--get-location-dynamic-block) 209 | 210 | (provide 'org-noter-dynamic-block) 211 | ;;; org-noter-dynamic-block.el ends here 212 | 213 | -------------------------------------------------------------------------------- /other/org-noter-nov-overlay.el: -------------------------------------------------------------------------------- 1 | ;;; org-noter-nov-overlay.el --- Module to highlight text in nov-mode with notes -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2021 Charlie Gordon 4 | 5 | ;; Author: Charlie Gordon 6 | ;; Keywords: multimedia 7 | 8 | ;; This program is free software; you can redistribute it and/or modify 9 | ;; it under the terms of the GNU General Public License as published by 10 | ;; the Free Software Foundation, either version 3 of the License, 11 | ;; any later version. 12 | 13 | ;; This program is distributed in the hope that it will be useful, 14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ;; GNU General Public License for more details. 17 | 18 | ;; You should have received a copy of the GNU General Public License 19 | ;; along with this program. If not, see . 20 | 21 | ;;; Commentary: 22 | 23 | ;; Highlight your precise notes in nov with org-noter-nov-overlay.el 24 | 25 | ;;; Code: 26 | (require 'org-noter) 27 | (require 'nov) 28 | (require 'seq) 29 | 30 | (defcustom org-noter-nov-overlay-color-property "NOTER_OVERLAY" 31 | "A property that specifies the overlay color for `org-noter-nov-make-ov'.") 32 | 33 | (defcustom org-noter-nov-overlay-default-color "SkyBlue" 34 | "Name of the default background color of the overlay `org-noter-nov-make-ov' makes. 35 | 36 | Should be one of the element in `defined-colors'.") 37 | 38 | (defun org-noter-nov-make-overlays () 39 | (org-noter--with-selected-notes-window 40 | (let* ((page (buffer-local-value 'nov-documents-index (org-noter--session-doc-buffer session))) 41 | (regexp (org-re-property org-noter-property-note-location t nil 42 | (format (rx "(" (* space) "%d" (+ space) 43 | (+ digit) (+ space) "." (+ space) 44 | (+ digit) (* space) ")") 45 | page)))) 46 | (org-with-wide-buffer 47 | (goto-char (point-min)) 48 | (while (re-search-forward regexp nil t) 49 | (when-let ((location (org-entry-get nil org-noter-property-note-location nil t))) 50 | (org-noter-nov-make-overlay-no-question))))))) 51 | 52 | (defun org-noter-nov-make-overlay () 53 | "TODO" 54 | (org-noter--with-selected-notes-window 55 | "No notes window exists" 56 | (when (eq (org-noter--session-doc-mode session) 'nov-mode) 57 | (let* ((location-property (org-entry-get nil org-noter-property-note-location nil t)) 58 | (location-cons (cdr (read location-property))) 59 | (beg (car location-cons)) 60 | (end (cdr location-cons)) 61 | (ov-pair (list (make-overlay beg end (org-noter--session-doc-buffer session)))) 62 | (hl-color (or (org-entry-get nil org-noter-nov-overlay-color-property nil t) 63 | (if org-noter-insert-note-no-questions 64 | org-noter-nov-overlay-default-color 65 | (read-color "Highlight color: ")))) 66 | (hl-color-alt (color-lighten-name hl-color 15)) 67 | (action-functions (list 68 | #'org-noter-nov-overlay-sync-current-note 69 | #'org-noter-nov-overlay-sync-current-page-or-chapter))) 70 | 71 | (save-excursion 72 | (org-back-to-heading t) 73 | (re-search-forward org-heading-regexp nil t) 74 | (push (make-overlay (match-beginning 1) (match-end 1)) ov-pair)) 75 | 76 | (dolist (ov ov-pair) 77 | (overlay-put ov 'button ov) 78 | (overlay-put ov 'category 'default-button) 79 | (overlay-put ov 'face (list :background hl-color 80 | :foreground (readable-foreground-color hl-color))) 81 | 82 | (org-entry-put nil org-noter-nov-overlay-color-property hl-color) 83 | 84 | (overlay-put ov 'mouse-face (list :background hl-color-alt 85 | :foreground (readable-foreground-color hl-color-alt))) 86 | 87 | (overlay-put ov 'action (pop action-functions))))))) 88 | 89 | (defun org-noter-nov-make-overlay-no-question () 90 | "Like `org-noter-nov-make-ov', but doesn't ask user to select the overlay color." 91 | (org-noter--with-valid-session 92 | (let ((org-noter-insert-note-no-questions t)) 93 | (org-noter-nov-make-overlay)))) 94 | 95 | (defun org-noter-nov-overlay-sync-current-page-or-chapter (_overlay) 96 | "A wrapper function for `org-noter-sync-current-page-or-chapter' 97 | used exclusively with overlays made with `org-noter-nov-make-overlay' 98 | 99 | This wrapper ignores the first argument passed to it and just call 100 | `org-noter-sync-current-page-or-chapter'." 101 | 102 | (org-noter-sync-current-page-or-chapter)) 103 | 104 | (defun org-noter-nov-overlay-sync-current-note (_overlay) 105 | "A wrapper function for `org-noter-nov-overlay-sync-current-note' 106 | used exclusively with overlays made with `org-noter-nov-make-overlay' 107 | 108 | This wrapper ignores the first argument passed to it and just call 109 | `org-noter-nov-overlay-sync-current-note'." 110 | (org-noter-sync-current-note)) 111 | 112 | (add-hook 'nov-post-html-render-hook #'org-noter-nov-make-overlays) 113 | 114 | (provide 'org-noter-nov-overlay) 115 | ;;; org-noter-nov-ov.el ends here 116 | 117 | -------------------------------------------------------------------------------- /tests/org-noter-core-tests.el: -------------------------------------------------------------------------------- 1 | (add-to-list 'load-path "modules") 2 | (require 'org-noter-pdf) 3 | (require 'with-simulated-input) 4 | (require 'org-noter-test-utils) 5 | 6 | 7 | (describe "org-noter-core" 8 | (before-each 9 | (create-org-noter-test-session) 10 | ) 11 | 12 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 13 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 14 | (describe "note taking functionality" 15 | ;; checking to make sure that `with-mock-contents` works fine. 16 | (it "can parse a note file ast that is not empty" 17 | (with-mock-contents 18 | mock-contents-simple-notes-file 19 | '(lambda () (let ((mock-ast (org-noter--parse-root))) 20 | (expect mock-ast :not :to-be nil))))) 21 | 22 | ;; basic note should insert a default heading 23 | (it "can take a basic note" 24 | (with-mock-contents 25 | mock-contents-simple-notes-file 26 | '(lambda () 27 | (org-noter-core-test-create-session) 28 | (let ((org-noter-insert-note-no-questions t)) 29 | (org-noter-insert-note nil "NEW NOTE")) 30 | (expect 'org-noter-test-get-selected-text :to-have-been-called) 31 | (expect (string-match "Notes for page" (buffer-string)) :not :to-be nil)))) 32 | 33 | ;; enter a heading when taking a precise note; expect the heading to be there. 34 | (it "can take a precise note" 35 | (with-mock-contents 36 | mock-contents-simple-notes-file 37 | '(lambda () 38 | (org-noter-core-test-create-session) 39 | (with-simulated-input "precise SPC note RET" 40 | (org-noter-insert-precise-note)) 41 | (expect (string-match "precise note" (buffer-string)) :not :to-be nil)))) 42 | 43 | ;; there should be precise data in the note properties when entering a precise note 44 | (it "precise note has precise data" 45 | (with-mock-contents 46 | mock-contents-simple-notes-file 47 | '(lambda () 48 | (org-noter-core-test-create-session) 49 | (with-simulated-input "precise SPC note RET" 50 | (org-noter-insert-precise-note)) 51 | (expect (string-match "NOTER_PAGE:" (buffer-string)) :not :to-be nil) 52 | (expect (string-match "BEGIN_QUOTE" (buffer-string)) :not :to-be nil) 53 | (expect 'org-noter-core-test-get-precise-info :to-have-been-called) 54 | ))) 55 | 56 | ;; highlight code should be called when a precise note is entered 57 | (it "precise note calls the highlight hook" 58 | (with-mock-contents 59 | mock-contents-simple-notes-file 60 | '(lambda () 61 | (org-noter-core-test-create-session) 62 | (with-simulated-input "precise SPC note RET" 63 | (org-noter-insert-precise-note)) 64 | (expect 'org-noter-core-test-add-highlight :to-have-been-called)))) 65 | 66 | ;; hit C-g when entering a note; expect no highlight 67 | (it "precise note DOES NOT call the highlight hook when the note is aborted" 68 | (with-mock-contents 69 | mock-contents-simple-notes-file 70 | '(lambda () 71 | (org-noter-core-test-create-session) 72 | ;; this is how you trap a C-g 73 | (condition-case nil 74 | (with-simulated-input "C-g" (org-noter-insert-precise-note)) 75 | (quit nil)) 76 | (expect 'org-noter-core-test-add-highlight :not :to-have-been-called)))) 77 | 78 | ) 79 | 80 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 81 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 82 | (describe "session creation" 83 | ;; check that the narrowed buffer is named correctly 84 | (it "narrowed buffer is named correctly" 85 | (with-mock-contents 86 | mock-contents-simple-notes-file-with-a-single-note 87 | '(lambda () 88 | (org-noter-core-test-create-session) 89 | (let* ((session org-noter--session)) 90 | (expect (buffer-name (org-noter--session-notes-buffer session)) :to-equal "Notes of solove-nothing-to-hide") 91 | )))) 92 | 93 | ;; check that session properties are set correctly 94 | (it "session properties are set correctly" 95 | (with-mock-contents 96 | mock-contents-simple-notes-file-with-a-single-note 97 | '(lambda () 98 | (org-noter-core-test-create-session) 99 | (let* ((session org-noter--session)) 100 | (expect (org-noter--session-property-text session) :to-equal "pubs/solove-nothing-to-hide.pdf") 101 | (expect (org-noter--session-display-name session) :to-equal "solove-nothing-to-hide") 102 | (expect (org-noter--session-notes-file-path session) :to-equal org-noter-test-file) 103 | (expect (buffer-file-name (org-noter--session-notes-buffer session)) :to-equal org-noter-test-file) 104 | ;; TODO: Need test-specific-major mode somehow? 105 | ;; (expect (org-noter--session-doc-mode session) :to-equal 'org-core-test) 106 | )))) 107 | 108 | ) 109 | 110 | 111 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 112 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 113 | (describe "view-info" 114 | (it "can get view info" 115 | (with-mock-contents 116 | mock-contents-simple-notes-file-with-a-single-note 117 | '(lambda () 118 | (org-noter-core-test-create-session) 119 | (let* ((view-info (org-noter--get-view-info (org-noter--get-current-view)))) 120 | (expect 'org-noter-core-test-get-current-view :to-have-been-called) 121 | )))) 122 | ) 123 | 124 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 125 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 126 | (describe "locations" 127 | (defvar test-precise-location '(3 1 . 0.1)) 128 | (defvar test-simple-location '(3 1)) 129 | (defvar test-extra-precise-location '(4 1 0.1 0.2 0.3)) 130 | 131 | (describe "precise locations" 132 | (it "can get page from a precise location" 133 | (expect (org-noter--get-location-page test-precise-location) :to-equal 3)) 134 | 135 | (it "can get top from a precise location" 136 | (expect (org-noter--get-location-top test-precise-location) :to-equal 1)) 137 | 138 | (it "can get left from a precise location" 139 | (expect (org-noter--get-location-left test-precise-location) :to-equal 0.1)) 140 | ) 141 | 142 | (describe "simple locations" 143 | 144 | (it "doesn't get a left location for simple location" 145 | (expect (org-noter--get-location-left test-simple-location) :to-equal nil) 146 | ) 147 | 148 | (it "can get top from a simple location" 149 | (expect (org-noter--get-location-top test-simple-location) :to-equal 1)) 150 | 151 | (it "can get page from a simple location" 152 | (expect (org-noter--get-location-page test-simple-location) :to-equal 3)) 153 | ) 154 | 155 | (describe "extra precise locations" 156 | (it "can get page from an extra precise location" 157 | (expect (org-noter--get-location-page test-extra-precise-location) :to-equal 4)) 158 | 159 | (it "can get top from an extra precise location" 160 | (expect (org-noter--get-location-top test-extra-precise-location) :to-equal 1)) 161 | 162 | 163 | (it "can get left from an extra precise location" 164 | (expect (org-noter--get-location-left test-extra-precise-location) :to-equal 0.1))) 165 | ) 166 | 167 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 168 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 169 | (describe "persistent highlights" 170 | (describe "no hooks are setup for precise note highlights" 171 | ;; if no hooks for highlights are setup we expect no :HIGHLIGHT: property 172 | (before-each 173 | (setq org-noter--get-highlight-location-hook '()) 174 | ) 175 | (it "can take a precise note without a highlight appearing" 176 | (with-mock-contents 177 | mock-contents-simple-notes-file 178 | '(lambda () 179 | (org-noter-core-test-create-session) 180 | (with-simulated-input "precise SPC note RET" 181 | (org-noter-insert-precise-note)) 182 | (expect (string-match ":HIGHLIGHT:" (buffer-string)) :to-be nil))))) 183 | 184 | 185 | (describe "hooks for persistent highlights are setup" 186 | ;; setup hooks for highlighting 187 | (before-each 188 | (add-to-list 'org-noter--get-highlight-location-hook #'org-noter-core-test-get-highlight-location) 189 | (spy-on 'org-noter-core-test-get-highlight-location :and-call-through) 190 | ) 191 | ;; now that the hooks for highlights are setup, we expect :HIGHLIGHT: property to appear. 192 | (it "can take a precise note WITH a highlight appearing" 193 | (with-mock-contents 194 | mock-contents-simple-notes-file 195 | '(lambda () 196 | (org-noter-core-test-create-session) 197 | (with-simulated-input "precise SPC note RET" 198 | (org-noter-insert-precise-note)) 199 | (expect (string-match "\\:HIGHLIGHT\\:" (buffer-string)) :not :to-be nil) 200 | (expect (string-match "HARDCODED_HIGHLIGHT_LOCATION" (buffer-string)) :not :to-be nil))))) 201 | ) 202 | 203 | 204 | 205 | 206 | 207 | ) 208 | -------------------------------------------------------------------------------- /tests/org-noter-extra-tests.el: -------------------------------------------------------------------------------- 1 | 2 | (add-to-list 'load-path "modules") 3 | (require 'org-noter-pdf) 4 | (require 'with-simulated-input) 5 | (require 'org-noter-test-utils) 6 | 7 | 8 | (describe "org-noter very custom behavior" 9 | (before-each 10 | (create-org-noter-test-session) 11 | ) 12 | (describe "with advice" 13 | (before-each 14 | (setq org-noter-max-short-selected-text-length 700000) 15 | 16 | (define-advice org-noter--insert-heading (:after (level title &optional newlines-number location) add-full-body-quote) 17 | "Advice for org-noter--insert-heading. 18 | 19 | When inserting a precise note insert the text of the note in the body as an org mode QUOTE block. 20 | 21 | =org-noter-max-short-length= should be set to a large value to short circuit the normal behavior: 22 | =(setq org-noter-max-short-length 80000)=" 23 | 24 | ;; this tells us it's a precise note that's being invoked. 25 | (if (consp location) 26 | (insert (format "#+BEGIN_QUOTE\n%s\n#+END_QUOTE" title)))) 27 | (create-org-noter-test-session) 28 | ) 29 | (after-each 30 | (setq org-noter-max-short-selected-text-length 80) 31 | (advice-remove #'org-noter--insert-heading 'org-noter--insert-heading@add-full-body-quote) 32 | ) 33 | (it "should insert the highlighted text as an org-mode QUOTE when advice is enabled." 34 | (with-mock-contents 35 | mock-contents-simple-notes-file 36 | '(lambda () 37 | (org-noter-core-test-create-session) 38 | ;; we're not specifying the note title 39 | (with-simulated-input "RET" 40 | (org-noter-insert-precise-note)) 41 | (let* ((expected-heading (regexp-quote (format "** %s" (org-trim (replace-regexp-in-string "\n" " " (org-noter-test-get-selected-text nil))))))) 42 | (expect (string-match "HARDCODED_HIGHLIGHT_LOCATION" (buffer-string)) :not :to-be nil) 43 | (expect (string-match "BEGIN_QUOTE" (buffer-string)) :not :to-be nil) 44 | (expect (string-match "END_QUOTE" (buffer-string)) :not :to-be nil) 45 | (expect (string-match expected-heading (buffer-string)) :not :to-be nil)))))) 46 | 47 | 48 | (describe "without advice" 49 | (it "should revert back to standard title" 50 | (with-mock-contents 51 | mock-contents-simple-notes-file 52 | '(lambda () 53 | (org-noter-core-test-create-session) 54 | (with-simulated-input "RET" 55 | (org-noter-insert-precise-note)) 56 | (expect (string-match "\\*\\* Notes for page" (buffer-string)) :not :to-be nil)))))) 57 | -------------------------------------------------------------------------------- /tests/org-noter-location-tests.el: -------------------------------------------------------------------------------- 1 | (add-to-list 'load-path "modules") 2 | (require 'org-noter-test-utils) 3 | 4 | (defvar mock-contents-simple-notes-file-with-locations 5 | " 6 | :PROPERTIES: 7 | :ID: FAKE_1 8 | :END: 9 | #+TITLE: Test book notes (simple) 10 | * solove-nothing-to-hide 11 | :PROPERTIES: 12 | :NOTER_DOCUMENT: pubs/solove-nothing-to-hide.pdf 13 | :END: 14 | ** Heading1 15 | :PROPERTIES: 16 | :NOTER_PAGE: 40 17 | :END: 18 | ** Heading2 19 | :PROPERTIES: 20 | :NOTER_PAGE: (41 0.09 . 0.16) 21 | :HIGHLIGHT: #s(pdf-highlight 41 ((0.18050847457627117 0.09406231628453851 0.6957627118644067 0.12110523221634333))) 22 | :END: 23 | #+BEGIN_QUOTE 24 | Test 25 | #+END_QUOTE 26 | 27 | ") 28 | 29 | 30 | 31 | 32 | (describe "org-noter locations" 33 | (describe "basic location parsing works" 34 | (before-each 35 | ) 36 | 37 | (describe "page locations" 38 | (before-each 39 | (create-org-noter-test-session) 40 | ) 41 | (it "can parse a page location" 42 | (with-mock-contents 43 | mock-contents-simple-notes-file-with-locations 44 | '(lambda () 45 | (org-noter-core-test-create-session) 46 | (search-forward "Heading2") 47 | (expect (org-noter--get-containing-heading) :not :to-be nil) 48 | (expect (org-noter--parse-location-property (org-noter--get-containing-element)) :to-equal (read "(41 0.09 . 0.16)")) 49 | ) 50 | 51 | )) 52 | 53 | ) 54 | 55 | ) 56 | ) 57 | -------------------------------------------------------------------------------- /tests/org-noter-pdf-tests.el: -------------------------------------------------------------------------------- 1 | (add-to-list 'load-path "modules") 2 | (require 'org-noter-pdf) 3 | (require 'org-noter-test-utils) 4 | 5 | 6 | (defvar expected-highlight-info (make-pdf-highlight :page 747 :coords '(0.1 0.2 0.3 0.4))) 7 | 8 | (describe "org-noter-pdf-functionality" 9 | ;; todo refactor 👇 10 | (describe "location functionality" 11 | ) 12 | 13 | 14 | (describe "pdf specific highlight functionality" 15 | (before-each 16 | (spy-on 'pdf-view-active-region-p :and-return-value t) 17 | (spy-on 'pdf-view-active-region :and-return-value '(0.1 0.2 0.3 0.4)) 18 | (spy-on 'image-mode-window-get :and-return-value 747) 19 | ) 20 | 21 | (it "can get coordinates from pdf-view" 22 | (let ((highlight-info (org-noter-pdf--get-highlight))) 23 | (expect 'pdf-view-active-region-p :to-have-been-called) 24 | (expect highlight-info :to-equal expected-highlight-info))) 25 | 26 | (describe "highlight persistence" 27 | (before-each 28 | (create-org-noter-test-session) 29 | ;; (create-org-noter-test-session) sets up a highlight hook, so we have to reset it back. 30 | ;; this might be ok for now? maybe filter out all "-core-test-" hooks instead? 31 | (setq org-noter--get-highlight-location-hook '(org-noter-pdf--get-highlight)) 32 | ) 33 | (it "can take a precise note WITH a highlight appearing" 34 | (with-mock-contents 35 | mock-contents-simple-notes-file 36 | '(lambda () 37 | (org-noter-core-test-create-session) 38 | (with-simulated-input "precise SPC note RET" 39 | (org-noter-insert-precise-note)) 40 | (ont--log-debug "%s" (buffer-string)) 41 | (expect (string-match "\\:HIGHLIGHT\\:" (buffer-string)) :not :to-be nil) 42 | (expect (string-match (format "%s" expected-highlight-info) (buffer-string)) :not :to-be nil) 43 | ) 44 | 45 | )) 46 | 47 | ) 48 | 49 | ) 50 | ) 51 | --------------------------------------------------------------------------------