Disregard the version I posted in my previous post. It is bugged and does not work.
This version contains a proper working implementation of a feature in which the current word is boosted to the top if it previously occurred and is otherwise not shown.
from Npp import * # BEGIN SETTINGS AUTOCOMPLETION_MIN_LEN = 2 # min length of word to trigger autocompletion CHARS_TO_MATCH = r'[\w_-]' # characters that can be in "words" (by default most letters, digits, underscores, and dashes) USE_LANGUAGE_IGNORECASE = True # use the document's lexer language setting for ignoring case DEFAULT_IGNORECASE = False # should case be ignored if not using language ignorecase? ENABLED_EXTENSIONS = { '', # files with no extension yet 'csv', 'txt', 'md', 'xml', 'json', 'tsv', 'log', 'dump', 'yaml', 'yml', } # only use for files with these extensions MAX_FILE_SIZE = 200_000 # do not try autocompleting for files with more bytes than this CURRENT_WORD_ONLY_IF_IN_TEXT = True # END_SETTINGS def on_match(m, ctr, ignorecase): '''increase the count of the current word by 1 if ignorecase, store only the uppercase version of each word''' word = m.group(0) if ignorecase: word = word.upper() ctr.setdefault(word, 0) ctr[word] += 1 def getWordRangeUnderCaret(): '''get the start and end of the word under the caret''' pos = editor.getCurrentPos() word_start_pos = editor.wordStartPosition(pos, True) word_end_pos = editor.wordEndPosition(pos, True) return word_start_pos, word_end_pos def getExtension(fname): for ii in range(len(fname) - 1, -1, -1): if fname[ii] == '.': break if ii == 0: return '' return fname[ii + 1:] def onCharInsert(notif): '''Find all words in the document prefixed by the word under the caret and show those words for autocompletion sorted by their frequency. Ignore words with length less than AUTOCOMPLETION_MIN_LEN. May ignore the case of words based on the lexer language (e.g., will ignore case in SQL but not in Python)''' if editor.getLength() > MAX_FILE_SIZE: return ext = getExtension(notepad.getCurrentFilename()) if ext not in ENABLED_EXTENSIONS: return word_start_pos, word_end_pos = getWordRangeUnderCaret() word_length = word_end_pos - word_start_pos word = editor.getRangePointer(word_start_pos, word_length).strip() if word_length < AUTOCOMPLETION_MIN_LEN: return ctr = {} # anything preceded by a non-word-char and starting with the current word match_pat = '(?<!{0}){1}({0}*)'.format(CHARS_TO_MATCH, word) ignorecase = DEFAULT_IGNORECASE if USE_LANGUAGE_IGNORECASE: ignorecase = editor.autoCGetIgnoreCase() else: editor.autoCSetIgnoreCase(ignorecase) if ignorecase: # match case-insenstively if that's the language default match_pat = '(?i)' + match_pat editor.research(match_pat, lambda m: on_match(m, ctr, ignorecase)) if CURRENT_WORD_ONLY_IF_IN_TEXT: if ignorecase: upword = word.upper() if upword in ctr: if ctr[upword] > 1: # word earlier in text, move to front ctr[upword] = 10_000_000_000 else: del ctr[upword] # word not in text, remove elif word in ctr: if ctr[word] > 1: ctr[word] = 10_000_000_000 else: del ctr[word] autocomp = sorted(ctr, key = lambda x: ctr[x], reverse=True) autocomp_str = ' '.join(autocomp) editor.autoCShow(word_length, autocomp_str) if __name__ == '__main__': try: CALLBACK_ADDED except NameError: CALLBACK_ADDED = 1 editor.callback(onCharInsert, [SCINTILLANOTIFICATION.CHARADDED])