Skip to content

Commit

Permalink
Merge pull request #30 from licht1stein/backlinks
Browse files Browse the repository at this point in the history
Add obsidian-backlink-jump
  • Loading branch information
licht1stein authored Oct 18, 2022
2 parents e1eeb03 + aaa0aaa commit d4eb953
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 9 deletions.
25 changes: 20 additions & 5 deletions README.org
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ Put this in your ~init.el~:
;; Use either `obsidian-insert-wikilink' or `obsidian-insert-link':
(bind-key (kbd "C-c C-l") 'obsidian-insert-wikilink 'obsidian-mode-map)

;; Following backlinks
(bind-key (kbd "C-c C-b") 'obsidian-backlink-jump 'obsidian-mode-map)


;; Optionally you can also bind `obsidian-jump' and `obsidian-capture'
;; replace "YOUR_BINDING" with the key of your choice:
(bind-key (kbd "YOUR_BINDING") 'obsidian-jump)
Expand All @@ -48,10 +52,12 @@ Put this in your ~init.el~:
;; This directory will be used for `obsidian-capture' if set.
(obsidian-inbox-directory "Inbox")
:bind (:map obsidian-mode-map
;; Replace C-c C-o with Obsidian.el's implementation. It's ok to use another key binding.
("C-c C-o" . obsidian-follow-link-at-point)
;; If you prefer you can use `obsidian-insert-link'
("C-c C-l" . obsidian-insert-wikilink)))
;; Replace C-c C-o with Obsidian.el's implementation. It's ok to use another key binding.
("C-c C-o" . obsidian-follow-link-at-point)
;; Jump to backlinks
("C-c C-b" . obsidian-backlink-jump)
;; If you prefer you can use `obsidian-insert-link'
("C-c C-l" . obsidian-insert-wikilink)))
#+end_src

Optionally you can specify ~obsidian-inbox-directory~, it will be used by ~obsidian-capture~ to
Expand All @@ -74,7 +80,8 @@ Obsidian.el must empower us to stay in Emacs for things that make sense in Emacs
- [X] Jumping between notes
- [X] Searching all notes
- [X] Finding all notes with a tag
- [ ] Viewing and following backlinks
- [X] Following backlinks
- [ ] Viewing backlinks in a separate list

When all of the above is ready we will almost never need the Obsidian app on desktop, but will still be able to use it on mobile or when specifically needed.

Expand Down Expand Up @@ -157,6 +164,13 @@ Quickly jump between notes using ~obsidian-jump~
M-x obsidian-jump RET
#+end_src

** Following backlinks
You can quickly jump to backlinks to current file using ~obsidian-backlink-jump~

#+begin_src
M-x obsidian-backlink-jump RET
#+end_src

*** Aliases
If you have YAML front matter in your note, Obsidian.el will find aliases in it and add them to the ~obsidian-jump~ selection. Both ~aliases~ and ~alias~ keys are supported.

Expand Down Expand Up @@ -193,6 +207,7 @@ Use ~obsidian-tag-find~ to list all notes that contain a tag. Let's you choose a
- [X] Obsidian minor for matching .md files
- [X] Jumping between notes
- [X] Following links
- [X] Following backlinks

* Why obsidian.el and not...
** Obsidian App itself, Athens Research or any other great app?
Expand Down
50 changes: 49 additions & 1 deletion obsidian.el
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
;; Author: Mykhaylo Bilyanskyy
;; URL: https://github.com./licht1stein/obsidian.el
;; Keywords: obsidian, pkm, convenience
;; Version: 1.1.5
;; Version: 1.1.6
;; Package-Requires: ((emacs "27.2") (s "1.12.0") (dash "2.13") (markdown-mode "2.5") (elgrep "1.0.0") (yaml "0.5.1"))

;; This file is NOT part of GNU Emacs.
Expand Down Expand Up @@ -76,6 +76,11 @@ When run interactively asks user to specify the path."

(defvar obsidian--tag-regex "#[[:alnum:]-_/+]+" "Regex pattern used to find tags in Obsidian files.")

(defvar obsidian--basic-wikilink-regex "\\[\\[[[:graph:][:blank:]]*\\]\\]"
"Regex pattern used to find wikilinks.")
(defvar obsidian--basic-markdown-link-regex "\\[[[:graph:][:blank:]]+\\]\([[:graph:][:blank:]]*\)"
"Regex pattern used to find markdown links.")

(defvar obsidian--aliases-map (make-hash-table :test 'equal) "Alist of all Obsidian aliases.")

(defun obsidian--clear-aliases-map ()
Expand Down Expand Up @@ -437,6 +442,49 @@ See `markdown-follow-link-at-point' and
"Find RE in the Obsidian vault."
(elgrep obsidian-directory "\.md" re :recursive t :case-fold-search t :exclude-file-re "~"))

(defun obsidian--link-p (s)
"Check if S matches any of the link regexes."
(or (s-matches-p obsidian--basic-wikilink-regex s)
(s-matches-p obsidian--basic-markdown-link-regex s)))

(defun obsidian--elgrep-get-context (match)
"Get :context out of MATCH produced by elgrep."
(let* ((result (->> match
(nth 1)
-flatten))
(context (plist-get result :context)))
context))

(defun obsidian--mention-link-p (match)
"Check if MATCH produced by `obsidian--grep' is a link."
(obsidian--link-p (obsidian--elgrep-get-context match)))

(defun obsidian--find-links-to-file (filename)
"Find any mention of FILENAME in the vault."
(->> (file-name-sans-extension filename)
obsidian--grep
(-filter #'obsidian--mention-link-p)
(-map #'car)))

(defun obsidian--completing-read-for-matches (coll)
"Take a COLL of matches produced by elgrep and make a list for completing read."
(let* ((dict (make-hash-table :test 'equal))
(_ (-map (lambda (f) (puthash f (obsidian--expand-file-name f) dict)) coll)))
dict))

;;;###autoload
(defun obsidian-backlink-jump ()
"Select a backlink to this file and follow it."
(interactive)
(let* ((backlinks (obsidian--find-links-to-file (file-name-nondirectory (buffer-file-name))))
(dict (obsidian--completing-read-for-matches backlinks))
(choices (-sort #'string< (-distinct (hash-table-keys dict)))))
(if choices
(let* ((choice (completing-read "Jump to: " choices))
(target (obsidian--get-alias choice (gethash choice dict))))
(find-file target))
(message "No backlinks found."))))

;;;###autoload
(defun obsidian-search ()
"Search Obsidian vault for input."
Expand Down
22 changes: 22 additions & 0 deletions tests/test-obsidian.el
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,25 @@ key4:
(it "check that front-matter is ignored if not at the top of file"
(expect (obsidian-find-yaml-front-matter obsidian--test-incorret-front-matter--not-start-of-file)
:to-equal nil)))

(describe "obsidian--link-p"
(it "non link"
(expect (obsidian--link-p "not link") :to-equal nil))

(it "wiki link"
(expect (obsidian--link-p "[[foo.md]]") :to-equal t)
(expect (obsidian--link-p "[[foo]]") :to-equal t)
(expect (obsidian--link-p "[[foo|annotated link]]") :to-equal t))

(it "markdown link"
(expect (obsidian--link-p "[foo](bar)") :to-equal t)
(expect (obsidian--link-p "[foo](bar.md)") :to-equal t)))

(describe "obsidian--find-links-to-file"
(before-all (obsidian-specify-path obsidian--test-dir))
(after-all (obsidian-specify-path obsidian--test--original-dir))

(it "1.md"
(expect (obsidian--find-links-to-file "1.md") :to-equal '("2.md"))))

(provide 'test-obsidian)
1 change: 1 addition & 0 deletions tests/test_vault/1.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor i

#tag1
#nested/tag

6 changes: 3 additions & 3 deletions tests/test_vault/2.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#Tag2

[insert link md](subdir/2-sub.md)
[insert link md](subdir/2-sub.md) text after markdown link

[[inbox/2022-07-24.md|insert link wiki]]

Expand All @@ -9,9 +9,9 @@ wiki ext url: [[http://example.com|example]]

md link: [2-sub with spaces and буквы](subdir/2-sub%20with%20spaces%20and%20буквы.md)

wiki link: [[Subdir/2-sub with spaces and буквы.md|2-sub with spaces and буквы]]
wiki link: [[Subdir/2-sub with spaces and буквы.md|2-sub with spaces and буквы]] some text after link

[[subdir/2-sub with spaces and буквы.md]]
[[subdir/2-sub with spaces and буквы.md]] foo bar spam

[2-sub with spaces and буквы](2-sub%20with%20spaces%20and%20буквы.md)

Expand Down
3 changes: 3 additions & 0 deletions tests/test_vault/subdir/1.md
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
subdir/1.md


[[2-sub with spaces and буквы.md]] foo bar spam

0 comments on commit d4eb953

Please sign in to comment.