AtomLinter / linter-spell

Multilingual grammar-specific spell checking for Atom and linter using Ispell compatible interface such as GNU Aspell or Hunspell.
https://atom.io/packages/linter-spell
MIT License
14 stars 1 forks source link

[enhancement] Support for wildcard scopes #64

Open Aerijo opened 6 years ago

Aerijo commented 6 years ago

I would appreciate it if there was a way to ignore the current scope when only part of it matches one given in the checkedScopes object; ie. the scope is more specific than the one in checkedScopes. For example, say I have the following LaTeX document:

\section{Section 1}

The scope of \section in this case is entity.name.section.section.latex, text.tex.latex. Instead of having to specify a precise match against entity.name.section.section.latex in checkedScopes (and all it's relatives, like .subsection, .paragraph, etc) I feel it would be much easier / more intuitive to have a wildcard pattern such as entity.name.*. With this, the scopes given by the grammar provider can be as specific as desired, while still being correctly categorised by the spell checker. The applications are broader than this: as a grammar package author, I feel I give the user the most control / customisability when they can make the most specific adjustments to their theme. To this end, for something like \genericCommand, I scope it as support.function.general.$1.latex, where the $1 is replaced by whatever the name of the command is.

An alternative solution on the grammar package side would be to add an additional meta scope to everything that should not be spell checked. I feel that this is less optimal, as it adds unnecessary bloat to all the command scopes, and Atom already gives up after ~100 scope matches per line due to performance issues (allegedly; I set it to 300 and am fine).

I have attempted a partial solution with my limited familiarity of this package:

// ./lib/grammar-manager.js
isIgnored (scopeDescriptor) {
    let path = scopeDescriptor.getScopesArray()
    let i = path.length
    while (i--) {
      if (this.checkedScopes.has(path[i])) {
        const v = this.checkedScopes.get(path[i])
        return !(_.isFunction(v) ? v() : v)
      } else {
        for (let [k, v] of this.checkedScopes) {
          if (path[i].startsWith(k)) {
            return !(_.isFunction(v) ? v() : v)
          }
        }
      }
    }
    return true
  }

It effectively tacks onto the isIgnored function and cycles through this.checkedScopes to find if any keys match the beginning of the scope of path[i]. If they do, the match is considered a success and it returns the same way as the original check does.

The above code does not implement the * syntax, but it probably should for the sake of not breaking backwards compatibility / introducing unwanted side effects. I also think it's incredibly inefficient, but my (limited) testing doesn't feel any slower than normal. I believe the actual spell checking takes the majority of the time.

If anyone supports this and feels they can do better, please do. I would appreciate it, but I understand if it's not a priority.