Search code examples
autocompletesublimetext3sublimetextcode-snippetssublime-text-plugin

How to have a Sublime Text plugin override the user's default "auto_complete_selector" setting?


The rationale for this is to build an ESLint rule auto-completion plugin, but of course, I don't want ESLint rule autocompletes suggested outside my comments, and I also don't want to have to enable all suggestions within comments.

I'm trying to replicate the behavior like in this VSCode plugin for the same purpose.

I'm also not entirely certain if this behavior requires autocomplete, snippets, or both?

For those unaware of ESLint's comment syntax, it's like this:

var a = 1; // eslint-disable-line RULE_NAME

Solution

  • Given the conditions your are setting i.e.

    1. The completions should appear only in comments.
    2. The completions should appear only after certain word(s) (in this case eslint-disable and family).

    you'll need to write a custom plugin that supplies the completions and also keeps track of where the completions are to be presented.

    The following is a plugin that accomplishes this (It mimics the VS Code counterpart)

    import sublime
    import sublime_plugin
    
    
    class ESLintListener(sublime_plugin.EventListener):
    
        eslint_completions = ["camelcase","default-case","func-names","global-require","import/prefer-default-export","indent","max-len","new-cap","no-alert","no-cond-assign","no-confusing","no-console","no-extend-native","no-mixed-operators","no-new","no-param-reassign","no-shadow","no-undef","no-unused-vars","prefer-arrow-callback","prefer-rest-params","react/prop-types","wrap-iife"]
    
        def on_query_completions(self, view, prefix, locations):
            if not view.match_selector(locations[0] - 1, "source.js comment"):
                return
    
            if (
                view.substr(sublime.Region(locations[0] - 16, locations[0] - 2)) == "eslint-disable" or
                view.substr(sublime.Region(locations[0] - 21, locations[0] - 2)) == "eslint-disable-line" or
                view.substr(sublime.Region(locations[0] - 26, locations[0] - 2)) == "eslint-disable-next-line"
            ):
                return [
                    sublime.CompletionItem(
                        trigger=item,
                        annotation=item,
                        details="Eslint rule",
                        kind=(sublime.KIND_ID_FUNCTION, "E", "Eslint")
                    )
                    for item in self.eslint_completions
                ]
    
            return None
    

    The first if condition check for condition (1) and the second one check for condition (2) and also returns the desired completions if it matches.

    enter image description here

    Note that this doesn't auto complete the eslint related words, but only the rule name after that.

    I have used some new ST4 features just for a little spice (& also to provide a bit more details of what the completions are).

    In order for this to work, you need to have source.js comment in your auto_complete_selector since completions are by default disable in comments.

    Edit (based on comments):

    To avoid word & explicit completions, we use the two flags as shown below. This should mitigate other completions being shown in the AC to some extent.

    import sublime
    import sublime_plugin
    
    
    class ESLintListener(sublime_plugin.EventListener):
    
        eslint_completions = ["camelcase","default-case","func-names","global-require","import/prefer-default-export","indent","max-len","new-cap","no-alert","no-cond-assign","no-confusing","no-console","no-extend-native","no-mixed-operators","no-new","no-param-reassign","no-shadow","no-undef","no-unused-vars","prefer-arrow-callback","prefer-rest-params","react/prop-types","wrap-iife"]
    
        def on_query_completions(self, view, prefix, locations):
            if not view.match_selector(locations[0] - 1, "source.js comment"):
                return
    
            if (
                view.substr(sublime.Region(locations[0] - 16, locations[0] - 2)) == "eslint-disable" or
                view.substr(sublime.Region(locations[0] - 21, locations[0] - 2)) == "eslint-disable-line" or
                view.substr(sublime.Region(locations[0] - 26, locations[0] - 2)) == "eslint-disable-next-line"
            ):
                return (
                    [
                        sublime.CompletionItem(
                            trigger=item,
                            annotation=item,
                            details="Eslint rule",
                            kind=(sublime.KIND_ID_FUNCTION, "E", "Eslint")
                        )
                        for item in self.eslint_completions
                    ],
                    sublime.INHIBIT_WORD_COMPLETIONS | sublime.INHIBIT_EXPLICIT_COMPLETIONS
                )
    
            return None