Sublime Forum

Added_words and ignored_word contain empty strings

#1
  1. I have spell_check set to true.
  2. I send the next_misspelling command (with a custom keybinding, in case that matters).
  3. The result is that the next misspelling is highlighted.
  4. I send the add_word command (also with a custom keybinding).
  5. ST3 adds the empty string to the added_words field in my user preferences file.
  6. I would like it to add the highlighted (misspelled) word to the added_words field.

If I right click the misspelling and select “Add ZZZ” from the drop down menu, where “ZZZ” is the misspelled word, it adds the misspelling to my added_words field.

Where have I gone wrong? Or have I stumbled on a bug?

0 Likes

#2

you may find that you need to send an argument to add_word which will tell it which word to add - I have the idea that maybe it is not clever enough to use the selection…

1 Like

#3

I see that if I were using a snippet, I could use $SELECTION as an argument. How can I send the selection to add_word, though?

0 Likes

#4

It definitely seems like both the add_word and ignore_word commands require you to provide them an argument of word to tell them the word to apply. I played around a bit to see if I could find an argument that would make it use the selection directly, but was unable to find anything obvious.

The following plugin provides an add_or_ignore_words command that adds this sort of functionality. You can say words="something" to add that single word, words=["something", "otherthing"] to add multiple words in one shot, or if you don’t specify a value for words at all, the selection at all of the cursors is used as the list of words.

Note that it doesn’t check that the words in question are already in the appropriate list or not nor does it check that the words you provide are actual words (i.e. no inner white space, no punctuation, etc), although that could be added.

import sublime
import sublime_plugin


class AddOrIgnoreWordsCommand(sublime_plugin.TextCommand):
    """
    Given either a single word, a list of words or a selection of words, add or
    ignore them for spell checking purposes. The selection is only consulted
    when no explicit words are provided.
    """
    def run(self, edit, add, words=None):
        if words is None:
            words = [self.view.substr(s) for s in self.view.sel()]
        elif not isinstance(words, list):
            words = [words]

        cmd = "add_word" if add else "ignore_word"

        for word in set(words):
            self.view.run_command(cmd, {"word": word})


    def is_enabled(self, add, words=None):
        return words is not None or all([s.size() > 0 for s in self.view.sel()])
0 Likes

#5

Thanks, @OdatNurd, I’ve modified your plugin to get a bit closer to what I want. I’m new to working with ST3 under the hood, so definitely open to any suggestions.

The one thing I can think that this still doesn’t do is check whether the selected word is already in the added_words list.

import sublime
import sublime_plugin

class AddOrIgnoreSelectedWord(sublime_plugin.TextCommand):
	"""
	Based on the current selection, add a single word to the `added_words`
	or `ignored_words` list in Preferences.
	"""
	def run(self, edit, cmd):
		
		if not cmd in ['add_word', 'ignore_word']:
			sublime.error_message("Must call AddOrIgnoreSelectedWord with cmd = 'add_word' or 'ignore_word'")

		w = [self.view.substr(s) for s in self.view.sel()]

		if not len(w) == 1:
			sublime.error_message("Must select exactly one region.")
			return

		w = w[0].split()

		if not len(w) == 1:
			sublime.error_message("Must select exactly one word.")
			return

		self.view.run_command(cmd, {"word": w[0]})
0 Likes

#6

You can do that with something like this:

settings = sublime.load_settings("Preferences.sublime-settings")
existing = settings.get("added_words", [])
if "word" not in existing:
    print('not in the list')

You can substitute "ignored_words" for "added_words" depending on which command you’re using.

0 Likes

#7

Thanks, I’ll add that in.

It seems like you have a good bit more experience with these things than me – are there any parts of my plugin that stick out to you as “not pythonic” or “not ST3-ic”?

Specifically, my use of [0] seems clunky.

0 Likes

#8

I don’t know that I’m any particular judge of things being properly pythonic (I often check SO to see if I’m contravening any defined idioms, for example) or Sublime-y, for that matter. What I would do and what’s a good idea may or may not jive at any given point. :slight_smile:

If you only intend to take the first selection instead of all of them, it’s possibly cleaner to flip the order of extracting the words and checking how many so that you only trigger on a single selection and generate an error right away. Then w is always just a single string and you don’t need to subscript it:

if not len(self.view.sel()) == 1:
    sublime.error_message("Must select exactly one region.")
    return

w = self.view.substr(self.view.sel()[0])

Note however that as defined in your post, this will trigger even if there is no outwardly visible selection, which may not be what you want.

There is one sublime.Region in view.sel() for every caret that is in the buffer (which can be none at all, in which case the list is empty). The Region tells you where it starts and ends as character offsets in the buffer, and also tells you the size() in characters that the region spans.

Regular carets with no selection have a begin() and end() that are the same character, which makes the size() report 0, which causes view.substr() to always return an empty string.

This is missing a return, so it will warn you that the command isn’t an expected one, but it will try to run it anyway. Sublime silently ignores attempts to run commands that don’t exist, so that’s not really any big deal though.

One last thing would be that technically it’s not just white space that separates things into words, punctuation does as well. The word_separators setting contains the characters that Sublime considers word separators for various commands, so you could consult that and see if any of the characters in it appear in the extracted word.

An example of something like that might be the following:

# Get the list of word separators
sepchars = self.view.settings().get("word_separators", "")

# If w contains white space or a separator, it's more than one word.
for c in sepchars + " \t\n":
    if c in w:
        print("this is more than one word")
        return
0 Likes