Skip to content

Instantly share code, notes, and snippets.

@OdatNurd
Last active January 17, 2023 19:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save OdatNurd/b82adb2430c28066fbddfecaf49b8861 to your computer and use it in GitHub Desktop.
Save OdatNurd/b82adb2430c28066fbddfecaf49b8861 to your computer and use it in GitHub Desktop.
Simple Text Highlight Plugin
import sublime
import sublime_plugin
import functools
import re
# The key the key that we use to add regions for the word currently under the
# cursor (when that is turned on), the style of the regions added, and the
# scope that represents the color to use when words are highlighted.
CURRENT_WORD_KEY='_sel_word'
CURRENT_WORD_FLAGS=sublime.DRAW_NO_OUTLINE | sublime.DRAW_NO_FILL | sublime.DRAW_SOLID_UNDERLINE
CURRENT_WORD_SCOPE = 'region.redish'
# The regex that is used to verify that the word under the cursor is one that
# should be highlighted. Note that the word is extracted following the rules of
# the 'word_separators' setting.
WORD_REGEX = re.compile(r'[\w]+')
# When using the functionality to highlight all instances of selected words,
# this sets the region key to use (which will be suffixed with a number) and
# the list of region colors to use, which are color scheme scopes that will be
# matched with the current color scheme to provide the color.
#
# Note that the colors can be any scope, but since they're scopes be careful to
# pick colors that will work in your own color scheme.
#
# The defaults below are the -ish colors which always map the closest possible
# color in the current color scheme. These may not be 100% what you expect
# (for example, yellow and orange may map to the same colors).
SELECTION_REGION_KEY='_sel_region'
SELECTION_REGION_FLAGS=sublime.DRAW_NO_FILL
SELECTION_REGION_COLORS=[
"region.redish",
"region.orangish",
"region.yellowish",
"region.greenish",
"region.cyanish",
"region.bluish",
"region.purplish",
"region.pinkish",
]
# Sample key bindings for the below commands; These could also be easily added
# to the command palette as well if desired.
# { "keys": ["ctrl+shift+h"], "command": "toggle_word_highlight" },
# { "keys": ["ctrl+alt+h"], "command": "highlight_selected_text" },
# { "keys": ["ctrl+alt+r"], "command": "remove_selection_highlights" },
def _highlight_cursor_word_instances(view):
"""
Add a highlight to all instances of the word that is currently under the
first cursor, if it appears to be a valid word.
Validity is determined by the WORD_REGEX.
"""
pt = view.sel()[0].b
word = view.substr(view.word(pt))
if WORD_REGEX.match(word):
regions = view.find_all(fr'\b{word}\b')
view.add_regions(CURRENT_WORD_KEY, regions, CURRENT_WORD_SCOPE,
flags=CURRENT_WORD_FLAGS)
class ToggleWordHighlightCommand(sublime_plugin.TextCommand):
"""
Toggles between showing all instances of the word currently under the first
cursor as highlighted on and off. When turned on it will immediately
trigger for the word under the cursor (if any) and when turned off all
highlights will go away.
"""
def run(self, edit):
active = not self.view.settings().get('highlight_current_word', False)
self.view.settings().set("highlight_current_word", active)
if active:
_highlight_cursor_word_instances(self.view)
else:
self.view.erase_regions(CURRENT_WORD_KEY)
class HighlightSelectedTextCommand(sublime_plugin.TextCommand):
"""
If the first selection is non empty, highlight all instances of it in the
current file with the next available color from the list of colors. When
the color list is exhausted, it cycles back to the start.
"""
def run(self, edit):
selected = self.view.substr(self.view.sel()[0])
regions = self.view.find_all(selected, flags=sublime.LITERAL)
idx = self.view.settings().get("_sel_idx", 0)
scope_color = SELECTION_REGION_COLORS[idx]
key = f'{SELECTION_REGION_KEY}_{idx}'
self.view.add_regions(key, regions, scope_color,
flags=SELECTION_REGION_FLAGS)
idx = (idx + 1) % len(SELECTION_REGION_COLORS)
self.view.settings().set("_sel_idx", idx)
def is_enabled(self):
return len(self.view.sel()[0]) > 0
class RemoveSelectionHighlightsCommand(sublime_plugin.TextCommand):
"""
Remove all of the selection highlights that were added to the current file
by the highlight_selected_text command. The color sequence is also reset
back to the initial state.
"""
def run(self, edit):
self.view.settings().erase('_sel_idx')
for idx in range(len(SELECTION_REGION_COLORS)):
self.view.erase_regions(f'{SELECTION_REGION_KEY}_{idx}')
class HighWordsEventListener(sublime_plugin.ViewEventListener):
"""
For any view where the highlight_current_word setting is set, wait 250ms
after the selection changes and then highlight all instances of the word
that is currently under the first cursor.
"""
pending = 0
@classmethod
def is_applicable(cls, settings):
return settings.get('highlight_current_word', False)
def on_selection_modified_async(self):
self.pending += 1
sublime.set_timeout_async(functools.partial(self.update_found_text), 250)
def update_found_text(self):
self.pending -= 1
if self.pending != 0:
return
_highlight_cursor_word_instances(self.view)
@OdatNurd
Copy link
Author

A simple plugin in the same general vein as the TextMarker, coded live on my Twitch Channel.

This implements three commands:

  1. toggle_word_highlight will turn on and off (in the current file) highlighting of the word under the first cursor in the buffer.
  2. highlight_selected_text requires that you select some text, and will mark that text with a colored box that is persistent until you close the file, quit Sublime or use the following command to remove the highlights.
  3. remove_selection_highlights will remove all manually added selection highlights from the buffer.

The values at the top of the plugin allow for some configuration, such as the colors to use and the style of the highlights to add.

Colors are specified as scopes, which are things that your color scheme will interpret as colors. The example uses the -ish colors, which causes Sublime to chose the color that matches the closest from the current color scheme. If you have a specific color scheme you want to support, you can use scopes from there, or add your own custom scopes to your color scheme to get exactly the colors you want.

The selected text regions will remain in place until you remove them yourself, close the file, or quit Sublime. If you add sublime.PERSISTENT to the appropriate flags variable, Sublime will persist the regions in the session, so that quitting and restarting will keep the regions in place.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment