Org-marginalia lets you highlight text, and write margin notes (marginalia) for any text file in a separate Org file.
Figure 1. Left: Org-mode with text enlarged; Right marginalia file with the inline image display on
Refer to the screenshots below for a teaser of what it can do.
Figure 2. Left: main note with some text highlighted in green; Right: margin notes in a marginalia file
Figure 3. Left: org-roam-buffer
showing backlinks from the marginal notes; Right: main Org note. Org-marinalia can automatically add links with Org-ID from marginal notes back to the main file. This works well with Org-roam’s backlink (V2)
Figure 4. Main note can be any text files. Left: an .el
file with a highlight; Right: marginalia file
- Auto-activation of Org-marginalia Mode when Opening Files with Marginal Notes
-
A new global minor mode
org-marginalia-global-tracking-mode
has been introduced as of 0.0.6. It saves and tracks files that have marginal notes. When it is active, visiting a file being tracked automatically turns onorg-marginalia-mode
, loading highlights previously saved in the marginalia file. The files being tracked are saved inorg-marginalia-tracking-file
, which you can customize. The default file is named.org-marginalia-tracking
in your Emacs configuration directory (user-emacs-directory
). - Support for Org ID and Org-roam
-
This is only applicable when the main source note is an Org file.
As of 0.0.6, seamless workflow with Org-roam has been added by generating ID links automatically.
If user option
org-marginalia-use-org-id
is non-nil (default) and Org ID is available in the main note, Org-marginalia will create a link back to the source note with using an Org-ID link instead of a normal file link. Org-marginalia looks at property inheritance for ID properties. When the immediate headline where a highlight belongs does not have an ID, the ID of a higher headline or file property is used when available. When none is available, Org-marginalia falls back to a normal file link. When a new marginalia file is created andorg-marginalia-use-org-id
is
non-nil, Org-marginalia will add an ID property to the file level. This is mainly to support Org-roam’s backlink feature for marginalia files.
- Use of Overlays, no longer Text-Properties
- This makes easier to add a highlighter overlay on top of text regions that already have faces (e.g. syntax-highlighted part of source code).
- Now turning off minor mode will turn off the highlights
-
It is still recommended to use
org-marginalia-toggle
to temporarily hide/show highlights. This is because turning offorg-marginalia-mode
will stop tracking of the locations of highlights in the current buffer. **Any** (however minor) change will likely result in mismatching the locations of saved highlights and the current buffer’s text content.
- Installation
- Usage
- Customizing
- Known Limitations
- Changelog
- Credits
- Feedback
- License
- Marginalia for org-marginalia.el
- Local Variables
This package is not available on MELPA. Manual installation is required.
Ensure to have Org Mode 9.4 or later (tested on 9.4.2). This package uses org-collect-keywords
, which does not exist in an earlier version.
Store both of the .el
files in the repo in your load-path, and put this in your init file:
(add-to-list 'load-path "~/local-repos/org-marginalia/")
(require 'org-marginalia-global-tracking)
(require 'org-marginalia)
By loading org-marginalia
, it will also pull in Org mode. You might like to defer loading of Org as it might take long time. As of version 0.0.6, you can do so with loading only org-marginalia-global-tracking
, which does not load org
automatically.
For example, I use this in my init file.
;; Set `load-path'
(add-to-list 'load-path "~/local-repos/org-marginalia")
;; Load only `org-marginalia-global-tracking'
;; and turn it on for automatic loading of highlights
;; for the files tracked
(load-library "org-marginalia-global-tracking")
(org-marginalia-global-tracking-mode 1)
;; Set keybindings `org-marginalia-mark' is bound to global-map so that you can
;; call it globally before the library is loaded. In order to make
;; `org-marginalia-mark' and `org-marginalia-mode' callable, use `autoload'.
;; When this package is available in MELPA, `autoload' should not be required.
(autoload #'org-marginalia-mark "org-marginalia" nil t)
(autoload #'org-marginalia-mode "org-marginalia" nil t)
(define-key global-map (kbd "C-c m") #'org-marginalia-mark)
;; The rest of keybidings are done only on loading `org-marginalia'
(with-eval-after-load 'org-marginalia
(define-key org-marginalia-mode-map (kbd "C-c n o") #'org-marginalia-open)
(define-key org-marginalia-mode-map (kbd "C-c n ]") #'org-marginalia-next)
(define-key org-marginalia-mode-map (kbd "C-c n [") #'org-marginalia-prev)
(define-key org-marginalia-mode-map (kbd "C-c n r") #'org-marginalia-remove))
org-marginalia-global-tracking-mode
A global minor mode to save and track files that have marginal notes.
When active, visiting a file being tracked automatically turns on org-marginalia-mode
, which loads highlights previously saved in the marginalia file.
The files being tracked are saved in org-marginalia-tracking-file
, which you can customize. The default file is named .org-marginalia-tracking
in your Emacs configuration directory (user-emacs-directory
).
org-marginalia-mode
Org-marginalia is a local minor mode. Toggle it on/off with using org-marginalia-mode
. On activating, it loads your saved highlights from the marginalia file (defined by org-marginalia-notes-file-path
), and enables automatic saving of highlights. The automatic saving is achieved via function org-marginalia-save
added to after-save-hook
.
org-marginalia-mark
Select a region of text, and call org-marginalia-mark
to highlight the region. It will generate a new ID, and start tracking the location – so you can edit text around the highlighted text. Do not cut, copy and paste as the highlight will disappear (you can immediately undo
to recover the text region along the highlights). To create a new marginal note entry in the marginalia file, save the buffer.
org-marginalia-save
By default, Org-marginalia automatically creates or updates corresponding entries in the marginalia file with location and text of highlights on saving the buffer. Nevertheless, you can manually call org-marginalia-save
to do so (automatic process also call this command).
If user option org-marginalia-use-org-id
is non-nil, Org-marginalia will
create a link back to the source note with using an Org-ID link instead of a
normal file link.
When a new marginalia file is created and org-marginalia-use-org-id
is
non-nil, Org-marginalia will add an ID property to the file level. This is mainly to support Org-roam’s backlink feature for marginalia files.
org-marginalia-open
Move your cursor on the highlighted text, and call org-marginalia-open
to open the relevant margin notes in a separate window. Your cursor will move to the marginalia buffer narrowed to the relevant margin notes entry. You can edit the marginalia buffer as a normal Org buffer. Once you have done editing, you may simply save and close the it (kill it or close the window) as per your normal workflow. Technically, the marginalia buffer is a cloned indirect buffer of the marginalia file.
org-marginalia-load
This command visits the marginalia file and loads the saved highlights onto the current buffer. If there is no margin notes for it, it will output a message in the echo. Highlights tracked locally by Org-marginalia cannot persist when you kill the buffer, or quit Emacs. When you re-launch Emacs, ensure to turn on org-marginalia-mode
to load the highlights. Loading is automatically done when you activate the minor mode.
org-marginalia-remove
This command removes the highlight at point. It will remove the highlight, and remove the properties from the marginalia, but will keep the headline and notes in tact.
You can pass a universal argument (C-u
by default). If this is the case, the command additionally deletes the entire heading subtree, along with the notes you have written, for the highlight.
org-marginalia-next
Move to the next highlight if any. If there is none below the cursor, and there is a highlight above, loop back to the top one. If the point has moved to the next highlight, this function enables transient map with `set-transient-map’. You don’t have to press the keybinding prefix again to move further to the next. That is, you can do a key sequence like this:
C-c n ] ] ] ]
If you have the same prefix for `org-marginalia-prev’, you can combine it in the sequence like so:
C-c n ] ] [ [
This lets your cursor back to where you started (next next prev prev)
org-marginalia-prev
Move to the previous highlight if any. If there is none above the cursor, and there is a highlight below, loop back to the bottom one. This function enables transient map. See org-marginalia-next
for detail.
org-marginalia-toggle
Toggle showing/hiding of highlighters in current buffer. It only affects the display of the highlighters. When hidden, highlights’ locations are still kept tracked; thus, upon buffer-save the correct locations are still recorded in the marginalia file.
`Org-marginalia` only provides its mode map, and does not bind any keys to it. As an example, you coud do something like this below.
(define-key org-marginalia-mode-map (kbd "C-c n o") #'org-marginalia-open)
(define-key org-marginalia-mode-map (kbd "C-c m") #'org-marginalia-mark)
(define-key org-marginalia-mode-map (kbd "C-c n ]") #'org-marginalia-next)
(define-key org-marginalia-mode-map (kbd "C-c n [") #'org-marginalia-prev)
Currently only “elementary” functions are defined in the package; for example, mark
, save
, and open
are all separate functions. You can string these together to compose a more fluid operation to suite your own workflow. A very useful set of such chained commands have been suggesetd by holtzermann17 in Org-roam’s Discourse discussion (adjusted to reflect the change of the prefix from om/
to org-marginalia-
) .
I will try to incorporate these into the package when I have more time to focus on it – I find them useful, but there are some plans I have had, and want to think of how I can incoprate these suggestions better with my ideas.
(defun org-marginalia-make-annotation ()
(interactive)
(let ((mark-end (region-end)))
(org-marginalia-mark (region-beginning) (region-end))
(org-marginalia-save)
(org-marginalia-open (1- mark-end))
(end-of-buffer)))
(define-key org-marginalia-mode-map (kbd "C-c M")
#'org-marginalia-make-annotation)
(defun org-marginalia-browse-forward ()
(interactive)
(let ((buf (current-buffer)))
(org-marginalia-next) (org-marginalia-open (point))
(pop-to-buffer buf nil t)))
(define-key org-marginalia-mode-map (kbd "C-c n }")
#'org-marginalia-browse-forward)
(defun org-marginalia-browse-backward ()
(interactive)
(let ((buf (current-buffer)))
(org-marginalia-prev) (org-marginalia-open (point))
(pop-to-buffer buf nil t)))
(define-key org-marginalia-mode-map (kbd "C-c n {")
#'org-marginalia-browse-backward)
- You can customize settings in the
org-marginalia
group. - Highlight’s face can be changed via
org-marginalia-highlighter
- Marginalia file is defined by
org-marginalia-notes-file-path
- Your files with marginal notes are saved and tracked in
org-marginalia-tracking-file
(when tracking is turned on via the global minor modeorg-marginalia-global-tracking-mode
) - You can use Org-ID to create links from marginal notes back to their main
notes when
org-marginalia-use-org-id
is on (default is on). This option also enables Org-marginalia to add an ID property when a new marginalia file is being created. This is to support seamless workflow with Org-roam.
- Copy & pasting loses highlights
- Overlays are not part of the kill; thus cannot be yanked.
- Undo highlight does not undo it
- Overlays are not part of the undo list; you cannot undo highlighting. Use
org-marginalia-remove
command instead. - Moving source files and marginalia file
- Move your files and marginalia file to another directory does not update the source path recorded in the marginalia file. It will be confusing. Try not to do this.
Feature:
- feat: Add
org-marginalia-global-tracking-mode
with a separate .el file - feat: Use Org-ID to create a link from the marginal notes back to the main file
Add Customizable variable
org-marginalia-use-org-id
; default ist
Change:
- chg: Highlights are now overlay; no longer text-properties
Improvement to existing functions
- add: Deactivate mark after highlighting
- add: org-marginalia-remove can take C-u to delete
Fix & Internal Refactor
- intrnl: Add housekeeping for
org-marginalia-highlights
variable - fix: org-id-uuid is not found
- fix: Add highlighter face def for terminal
- break: Replace the prefix “om/” in the source code with “org-marginalia”
- break: Remove default keybindings; add examples in readme instead. Addresses [#3](nobiot#3)
- feat: Add transient navigation to next/prev See § Credits for the piece of code to achieve the transient map I used.
- feat: Add om/toggle for show/hide highlighters
- feat: Add om/next and /prev
- break: Change om/open-at-point to org-marginalia-open
- break: Change om/save-all to org-marginalia-save
Initial alpha release. I consider it to be the minimal viable scope.
To create this package, I was inspired by the following packages. I did not copy any part of them, but borrowed some ideas from them – e.g. saving the margin notes in a separate file.
- Ov-highlight
- John Kitchin’s (author of Org-ref). Great UX for markers with hydra. Saves the marker info and comments directly within the Org file as Base64 encoded string. It uses overlays with using `ov` package.
- Annotate.el
- Bastian Bechtold’s (author of Org-journal). Unique display of annotations right next to (or on top of) the text. It seems to be designed for very short annotations, and perhaps for code review (programming practice); I have seen recent issues reported when used with variable-pitch fonts (prose).
- Org-annotate-file
- Part of Org’s contrib library. It seems to be designed to annotate a whole file in a separate Org file, rather than specific text items.
- InPlaceAnnotations (ipa-mode)
- It looks similar to Annotate.el above.
- Transient navigation feature
- To implement the transient navigation feature, I liberally copied the relevant code from a wonderful Emacs package, Binder by Paul W. Rankin (GitHub user rnkn).
Feedback welcome in this repo, or in Org-roam Discourse forum.
Edit: Now the features 1 & 2 have been implemented… I want to add a little more, to attend to the known limitations to see if I can remove some of them.
I am aiming to keep this package to be small and focused. I plan to add the following features, and probably consider it to be feature complete for my purposes.
- DONE v0.0.3
om/toggle
to toggle show/hide of highlights without losing them - DONE
om/next
andom/prev
to easily navigate highlighted regions in the buffer This is done (v0.0.2), but I would like to try a transient (don’t want to repeat the prefix everytime): transient done with v0.0.4.
This work is licensed under a GPLv3 license. For a full copy of the licese, refer to LICENSE.
This section is used as a demonstration and a collection of my ideas for this package.I need to think it through.
Do I want to reveal invisible elments to move, or keep it hidden.
At the moment, om/list-highlights-positions
has been changed to return beginning points of visible ones only – this can be changed to make it opsitonal arg. For example, if I want to list all, including the hidden ones, do I want to just visible ones?
Some syntactic elements keep their faces descpite being marked. It appears to be the way font-lock-mode works. Experimenting. This might also lead to a way for copy and paste (need to deal with duplicate IDs)
;; Comment dddd ;; This is considered ;; Comment ;;Comment dd jit-lock-register (defun) (jit-lock-register #'my/font-lock-fn) (font-lock-unfontify-buffer) (jit-lock-refontify) (font-lock-fontify-buffer) (point);; comment ;; Comment (my/font-lock-fn 155 160) (let ((beg 1) (end 10)) (list beg end)) (defun my/font-lock-fn (beg end &optional context) (unless context (list beg) (if (get-char-property beg 'om/id) (font-lock-unfontify-region beg end))))org-marginalia org-marginalia
Adding overlay does not set the buffer modified. It’s more fluid with save operation. You cannot use `undo’ to undo highlighter.
org-marginaliaDo not add the evaporate t
property for the highlight’s overlay. By remaining in the buffer, undo
puts overlays in their original location when text regions get killed and subsequently the kill gets undone.
Using overlays instead of text-properties has an advantage of easy composition of faces; e.g. when marking on a comment line in emacs-lisp-mode
, the highlighters face won’t be composed onto the underlying syntax face for comments. Overlay can make it easy to add an additional face to comments and other syntactically font-locked regions.
It’s more intuitive if editing the text both on the beg and end points of the highlight overlay does not extend it. Pass FRONT-ADVANCE; keep REAR-ADVANCE as default.
The arguments FRONT-ADVANCE and REAR-ADVANCE specify the marker insertion type for the start of the overlay and for the end of the overlay, respectively. *Note Marker Insertion Types::. If they are both ‘nil’, the default, then the overlay extends to include any text inserted at the beginning, but not text inserted at the end. If FRONT-ADVANCE is non-‘nil’, text inserted at the beginning of the overlay is excluded from the overlay. If REAR-ADVANCE is non-‘nil’, text inserted at the end of the overlay is included in the overlay.