Sublime Forum

Complete whole word

#1

It seems that if the cursor is in the middle of the word the completion does not override the whole word but its left part only.

In the sample below I would expected the get_|errors to be replaced with get_saved_doc|. However the result is get_saved_doc|errors

I would be able to post fix it if I can only hook to commit_completion command. Though I see no API for that and… I feel like I am missing something and solution might be much simpler (e.g. settings).

Will appreciate any suggestions.

2 Likes

#2

this is something that really annoys me too - and I’d also love a setting for it

you can find when commit_completion is called using an EventListener - look for the on_post_text_command and on_post_window_command methods.
However, it won’t help if the autocompletion is clicked on with the mouse

2 Likes

#3

I had the same issue and wanted to insert with enter and overwrite with tab so I wrote this small plugin. Just select Tools > Developer > New Plugin… and paste;

import sublime
import sublime_plugin


class OverwriteCommitCompletionCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        view = self.view
        window = view.window()
        window.run_command("commit_completion")
        for sel in view.sel():
            point = sel.b
            word = view.word(point)
            reg = sublime.Region(point, word.end())
            if view.substr(reg).isalnum():
                view.erase(edit, reg)

Then create this keybinding

    {
        "keys": ["tab"], "command": "overwrite_commit_completion", "context":
        [
            { "key": "auto_complete_visible" }
        ]
    },
3 Likes

How to autocomplete inside a word?
#4

Thank you guys,

Both solutions work, though one relies on detecting the command execution and another one on detecting keystrokes (via settings).

And I can survive without the mouse support this is not a problem at all.

…on_post_text_command and on_post_window_command…

Thank you kingkeith, "on_post_text_command" does the trick for detecting the completion commit.

And thank you r-stein your solution works right out of box. I only had to modify the key bindings to invoke the command on “enter” button:

..."keys": ["enter"], "command": ...

And I also added handling the case when completion has opening bracket at the end.

import sublime
import sublime_plugin

class OverwriteCommitCompletionCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        view = self.view
        window = view.window()
        window.run_command("commit_completion")
        for sel in view.sel():
            point = sel.b
            word = view.word(point)
            reg = sublime.Region(point, word.end())
            if view.substr(reg).isalnum():
                view.erase(edit, reg)
                
                reg = sublime.Region(point-1, point+1)
                if view.substr(reg) == '((': 
                    view.replace(edit, reg, '(')

Thus as far as I am concerned the work around completely addresses the issue (except the mouse support).

Txs

1 Like

#5

I wrote a variation where it only replaces the remaining word, if it is a subword of the completed word. I added this because most times I am adding new words before the some word, and when I use the completion for this new word it was deleting my word.

import sublime
import sublime_plugin


class OverwriteCommitCompletionCommand(sublime_plugin.TextCommand):

    def run(self, edit):
        view = self.view
        window = view.window()
        old_selections = []

        for selection in view.sel():
            old_selections.append( selection.end() )

        selection_index = 0
        completion_offset = 0
        window.run_command( "commit_completion" )

        for selection in view.sel():
            completion_end_point   = selection.end()
            duplicated_word_region = sublime.Region( completion_end_point, view.word( completion_end_point ).end() )
            duplicated_word        = view.substr( duplicated_word_region )
            part_completed_region  = sublime.Region( old_selections[ selection_index ] + completion_offset, completion_end_point )

            # print( "selection:           " + str( selection ) )
            # print( "inserted_word:       " + view.substr( sublime.Region( view.word( completion_end_point ).begin(), completion_end_point ) ) )
            # print( "full_completed_word: " + view.substr( view.word( completion_end_point ) ) )
            # print( "duplicated_word:     " + duplicated_word )
            # print( "part_completed_word: " + view.substr( part_completed_region ) )

            # inserted_word:       OverwriteCommitCompletionCommand
            # full_completed_word: OverwriteCommitCompletionCommandCommand
            # duplicated_word:     Command
            # part_completed_word: ompletionCommand
            if duplicated_word.isalnum() \
                    and duplicated_word in view.substr( part_completed_region ):

                # print( "Erasing duplication: " + duplicated_word )
                view.erase( edit, duplicated_word_region )

                # When the completion is inserted we need to save the completion_offset to be able
                # correct the outdated selection points after the auto completion for the remaining
                # selections
                completion_offset += part_completed_region.size() - duplicated_word_region.size()

            selection_index += 1
0 Likes

#6

Update, Since Sublime Text build ~3134, we need to wait until Sublime Text insert the completion. So, I put the completion code on another command and ran it after the commit completion command.

import sublime
import sublime_plugin


class OverwriteCommitCompletionCommand(sublime_plugin.TextCommand):
    """
        Complete whole word
        https://forum.sublimetext.com/t/complete-whole-word/26375

        It is possible to pass an array to a command without **kargs?
        https://forum.sublimetext.com/t/it-is-possible-to-pass-an-array-to-a-command-without-kargs/28969
    """

    def run(self, edit):
        view = self.view
        window = view.window()
        old_selections = []

        for selection in view.sel():
            old_selections.append( selection.end() )

        window.run_command( "commit_completion" )
        window.run_command( "overwrite_commit_completion_assistant", { "old_selections" : old_selections } )

class OverwriteCommitCompletionAssistantCommand( sublime_plugin.TextCommand ):
    """
        Save the edit when running a Sublime Text 3 plugin
        https://stackoverflow.com/questions/20466014/save-the-edit-when-running-a-sublime-text-3-plugin
    """

    def run( self, edit, old_selections ):
        """
            Since Sublime Text build ~3134, we need to wait until Sublime Text insert the completion.
        """
        view              = self.view
        selection_index   = 0
        completion_offset = 0

        for selection in view.sel():
            completion_end_point   = selection.end()
            duplicated_word_region = sublime.Region( completion_end_point, view.word( completion_end_point ).end() )
            duplicated_word        = view.substr( duplicated_word_region )
            part_completed_region  = sublime.Region( old_selections[ selection_index ] + completion_offset, completion_end_point )

            # print( "selection:           " + str( selection ) )
            # print( "selection_word:      " + str( view.substr( selection ) ) )
            # print( "inserted_word:       " + view.substr( sublime.Region( view.word( completion_end_point ).begin(), completion_end_point ) ) )
            # print( "full_completed_word: " + view.substr( view.word( completion_end_point ) ) )
            # print( "duplicated_word:     " + duplicated_word )
            # print( "part_completed_word: " + view.substr( part_completed_region ) )

            # inserted_word:       OverwriteCommitCompletionCommand
            # full_completed_word: OverwriteCommitCompletionCommandCommand
            # duplicated_word:     Command
            # part_completed_word: ompletionCommand
            if duplicated_word.isalnum() \
                    and duplicated_word in view.substr( part_completed_region ):

                # print( "Erasing duplication: " + duplicated_word )
                view.erase( edit, duplicated_word_region )

                # When the completion is inserted we need to save the completion_offset to be able
                # correct the outdated selection points after the auto completion for the remaining
                # selections
                completion_offset += part_completed_region.size() - duplicated_word_region.size()

            selection_index += 1



""" OverwriteCommitCompletionCommand

OverwriteCommitCompletionCommand
OverwriteCommitCompletionCommand

tooMuchLeft
tooMuchLeft
tooMuchLeft

"""
  1. It is possible to pass an array to a command without **kargs?
  2. Dev Build 3133
  3. Dev Build 3134
0 Likes

#7

unfortunately this plugin seems to prevent the JS get snippet from working properly

it cuts off after the the selection so that the substitution after the caret is removed i.e. instead of getElementsByTagName('') it gives getElementsByT('')

1 Like

#8

Thanks for reporting. I created a package to hold that snippet: https://github.com/evandrocoan/SublimeOverwriteCommitCompletion

Also, when I can I should write proper Unit Tests for this functionality fixing its bugs: https://github.com/evandrocoan/SublimeStudio/issues/69

0 Likes