diff --git a/README.md b/README.md index bc21b7c..18f41eb 100644 --- a/README.md +++ b/README.md @@ -6,18 +6,19 @@ If you follow the (plain-text) Zettelkasten method (as proposed by [Zettelkasten In short, it helps you manage an archive of interlinked notes that look like this: -```python -# The rise of the machines -tags = #AI #world-domination +![screenshot](sublime_zk.png) -Machines are becoming more and more intelligent and powerful. +See the **Usage** section below to see how this package might support your workflow. -This might reach a point where they might develop a consciensce of their own. - -As a consequence, they might turn evil and try to kill us all ........... [[201710282118]] -``` +## Features -See the **Feature** section below to see how this package might support your workflow. +* This plugin enables you to place wiki style links like `[[this]]` into your notes to link to other notes in your note archive. +* Clicking such a link and pressing `[ctrl]+[enter]` will open the corresponding note. +* Typing `[[` will open a list of existing notes so you can quickly link to existing notes. +* Typing `[shift]+[enter]` lets you enter a name for a new note. The new note is then created with a new note ID. +* Implicit note creation via links to non-existing notes' titles, see below. +* The ID format is YYYYMMDDHHMM - eg: 201710282111 +* Highlighting of note links ## Installation @@ -38,7 +39,7 @@ You should be all set. ### Zettelkasten note folder -No further configuration is necessary. This Zettelkasten package works with SublimeText Projects. It will use exactly the same directory where your SublimeText project file is located. +No further configuration is necessary. This Zettelkasten plugin works with SublimeText Projects. It will use exactly the same directory where your SublimeText project file is located. #### How do I create a project in SublimeText? @@ -76,16 +77,26 @@ The setting id_in_title is set to "false" You can find this setting in the file `sublime_zk.sublime-settings`. +### Highlight references to other notes + +By default, this plugin highlights links to other notes by underlying them. + +**Note:** This only applies to links containing an ID, like this one: [[201710290256]]. + +It also shows a bookmark symbol in the gutter to the left of your text. These features can be controlled via the following settings in `sublime_zk.sublime-settings`: + + +```json + // highlight links to other notes? + "highlight_note_links": "true", + + // when highlighting: also show bookmark symbols in the gutter? + "show_bookmarks_in_gutter": "true" +``` -## Features -* This package enables you to place wiki style links like `[[this]]` into your notes to link to other notes in your note archive. -* Clicking such a link and pressing `[ctrl]+[enter]` will open the corresponding note. -* Typing `[[` will open a list of existing notes so you can quickly link to existing notes. -* Typing `[shift]+[enter]` lets you enter a name for a new note. The new note is then created with a new note ID. -* Implicit note creation via links to non-existing notes' titles, see below. -* The ID format is YYYYMMDDHHMM - eg: 201710282111 +## Usage ### Creating a new note diff --git a/sublime_zk.png b/sublime_zk.png new file mode 100644 index 0000000..7a9dfa1 Binary files /dev/null and b/sublime_zk.png differ diff --git a/sublime_zk.py b/sublime_zk.py index 9d396a4..d6354cf 100755 --- a/sublime_zk.py +++ b/sublime_zk.py @@ -1,5 +1,7 @@ import sublime, sublime_plugin, os, re, subprocess, glob, datetime +# for highlighting +import threading def timestamp(): return '{:%Y%m%d%H%M}'.format(datetime.datetime.now()) @@ -117,3 +119,111 @@ class InsertWikiLinkCommand(sublime_plugin.TextCommand): def run(self, edit, args): self.view.insert(edit, self.view.sel()[0].begin(), args['text']) + + + + + + + + +class NoteLinkHighlighter(sublime_plugin.EventListener): + LINK_REGEX = r"(\[\[)[0-9]{12}(\]\])" + DEFAULT_MAX_LINKS = 1000 + + note_links_for_view = {} + scopes_for_view = {} + ignored_views = [] + highlight_semaphore = threading.Semaphore() + + def on_activated(self, view): + self.update_note_link_highlights(view) + + # Async listeners for ST3 + def on_load_async(self, view): + self.update_note_link_highlights_async(view) + + def on_modified_async(self, view): + self.update_note_link_highlights_async(view) + + def on_close(self, view): + for map in [self.note_links_for_view, self.scopes_for_view, self.ignored_views]: + if view.id() in map: + del map[view.id()] + + """The logic entry point. Find all LINKs in view, store and highlight them""" + def update_note_link_highlights(self, view): + settings = sublime.load_settings('sublime_zk.sublime-settings') + should_highlight_note_links = settings.get('highlight_note_links', "True") + should_highlight_note_links = should_highlight_note_links.lower() != "false" + + max_note_link_limit = NoteLinkHighlighter.DEFAULT_MAX_LINKS + if view.id() in NoteLinkHighlighter.ignored_views: + return + + note_links = view.find_all(NoteLinkHighlighter.LINK_REGEX) + + # update the regions to ignore the brackets + note_links = [sublime.Region(n.a+2, n.b-2) for n in note_links] + + # Avoid slowdowns for views with too many LINKs + if len(note_links) > max_note_link_limit: + print("NoteLinkHighlighter: ignoring view with %u links" % len(note_links)) + NoteLinkHighlighter.ignored_views.append(view.id()) + return + + NoteLinkHighlighter.note_links_for_view[view.id()] = note_links + + if (should_highlight_note_links): + self.highlight_note_links(view, note_links) + + def update_note_link_highlights_async(self, view): + NoteLinkHighlighter.highlight_semaphore.acquire() + try: + self.update_note_link_highlights(view) + finally: + NoteLinkHighlighter.highlight_semaphore.release() + + """Creates a set of regions from the intersection of note_links and scopes, + underlines all of them.""" + def highlight_note_links(self, view, note_links): + settings = sublime.load_settings('sublime_zk.sublime-settings') + show_bookmarks_in_gutter = settings.get('show_bookmarks_in_gutter', "True") + show_bookmarks_in_gutter = show_bookmarks_in_gutter.lower() != "false" + + # We need separate regions for each lexical scope for ST to use a proper color for the underline + scope_map = {} + for note_link in note_links: + scope_name = view.scope_name(note_link.a) + scope_map.setdefault(scope_name, []).append(note_link) + + for scope_name in scope_map: + self.underline_regions(view, scope_name, scope_map[scope_name], show_bookmarks_in_gutter) + + self.update_view_scopes(view, scope_map.keys()) + + """Apply underlining to provided regions.""" + def underline_regions(self, view, scope_name, regions, show_bookmarks=True): + if show_bookmarks: + symbol = 'bookmark' + else: + symbol = '' + + view.add_regions( + u'clickable-note_links ' + scope_name, + regions, + #scope_name + + "markup.bold", + symbol, + flags=sublime.DRAW_NO_FILL|sublime.DRAW_NO_OUTLINE|sublime.DRAW_SOLID_UNDERLINE) + + """Store new set of underlined scopes for view. Erase underlining from + scopes that were used but are not anymore.""" + def update_view_scopes(self, view, new_scopes): + old_scopes = NoteLinkHighlighter.scopes_for_view.get(view.id(), None) + if old_scopes: + unused_scopes = set(old_scopes) - set(new_scopes) + for unused_scope_name in unused_scopes: + view.erase_regions(u'clickable-note_links ' + unused_scope_name) + + NoteLinkHighlighter.scopes_for_view[view.id()] = new_scopes diff --git a/sublime_zk.sublime-settings b/sublime_zk.sublime-settings index 6d5365b..ba97f09 100755 --- a/sublime_zk.sublime-settings +++ b/sublime_zk.sublime-settings @@ -3,5 +3,14 @@ "wiki_extension": ".md", // when creating a new note, put id into title? - "id_in_title": "false" + // "false" to disable + "id_in_title": "false", + + // highlight links to other notes? + // "false" to disable + "highlight_note_links": "true", + + // when highlighting: also show bookmark symbols in the gutter? + // "false" to disable + "show_bookmarks_in_gutter": "true" }