//////////////////////////////////////////////////////////////////////////////////// // $Revision: 70987 $ //////////////////////////////////////////////////////////////////////////////////// // Copyright 2010 SlickEdit Inc. // You may modify, copy, and distribute the Slick-C Code (modified or unmodified) // only if all of the following conditions are met: // (1) You do not include the Slick-C Code in any product or application // designed to run independently of SlickEdit software programs; // (2) You do not use the SlickEdit name, logos or other SlickEdit // trademarks to market Your application; // (3) You provide a copy of this license with the Slick-C Code; and // (4) You agree to indemnify, hold harmless and defend SlickEdit from and // against any loss, damage, claims or lawsuits, including attorney's fees, // that arise or result from the use or distribution of Your application. //////////////////////////////////////////////////////////////////////////////////// #pragma option(pedantic,on) #region Imports #include "slick.sh" #include "tagsdb.sh" #include "markers.sh" #include "color.sh" #include "minihtml.sh" #include "autocomplete.sh" #import "alias.e" #import "autobracket.e" #import "beautifier.e" #import "c.e" #import "cbrowser.e" #import "ccode.e" #import "cfg.e" #import "codehelp.e" #import "compword.e" #import "context.e" #import "dlgman.e" #import "guireplace.e" #import "html.e" #import "ini.e" #import "listproc.e" #import "main.e" #import "markfilt.e" #import "math.e" #import "notifications.e" #import "pushtag.e" #import "recmacro.e" #import "saveload.e" #import "setupext.e" #import "slickc.e" #import "stdcmds.e" #import "stdprocs.e" #import "surround.e" #import "tagform.e" #import "tags.e" #import "treeview.e" #import "vc.e" #import "vi.e" #import "se/tags/TaggingGuard.e" #require "se/lang/api/LanguageSettings.e" #endregion using se.lang.api.LanguageSettings; /////////////////////////////////////////////////////////////////////////////// // AUTO COMPLETION (autocomplete.e) // // Author: Dennis Brueni // History: // 07-14-2005: initial version // /////////////////////////////////////////////////////////////////////////////// // This module implements the automatical completion system for SlickEdit. // // Automatic completion provides a comprehensive system to tie together // many of the most important features of SlickEdit into one consistent // framework. Auto-completion works by dynamically suggesting completion // possibilities when they are discovered, and providing a one key solution // for selecting to perform the auto-completion, when is then executed as // if the specific command had actually been typed. // // This frees the user from having to remember the key combination // associated with various completion mechanisms, all they really need // to remember is Space key. And if they are an advanced user, they also // can use Ctrl+Shift+Space to incrementally pull in part of the word. // // The features pulled into this system include: // // 1) Language specific syntax expansion // 2) Keyword completion // 3) Alias expansion // 4) Symbol completion using tagging // 5) Complete prev/next matches // // For the text box and combo box controls, this same system supplements // the existing completion mechanisms by providing a dynamic system for // displaying the matches, much like that used by the Windows file chooser. // // For the SlickEdit command line, this system supplements command history // as well as command argument completion using the same display mechanism. // /////////////////////////////////////////////////////////////////////////////// // Tree control on _auto_complete_list_form // This is used in a few places outside of the // tree's event table _control ctltree; /////////////////////////////////////////////////////////////////////////////// /** * Return the configuration variable def_auto_complete_options. * * @return int * @see def_auto_complete_options */ int GetDefAutoCompleteOptions() { return def_auto_complete_options; } /** * Minimum expandable symbol length to trigger auto completion. *
* The actual configurable options are stored per extension in * def_autocompletemin_[ext]. This is used as a global default setting. * * @default 1 * @categories Configuration_Variables */ int def_auto_complete_minimum_length = 1; /** * Delay before auto completion is displayed (in milliseconds). *
* Not used when auto completion is active. * * @default 250 milliseconds * @categories Configuration_Variables */ int def_auto_complete_idle_time = 250; /** * Delay before auto completion list is updated (in milliseconds). *
* When auto completion is active, this is the delay before it is updated * when you are typing identifier characters. Not used when auto completion * list is not displayed. * * @default 50 milliseconds * @categories Configuration_Variables */ int def_auto_complete_update_idle_time = 50; /** * Auto-complete should stop searching for completions after this * amount of time. This is done to prevent large typing delays. * * @default 500 milliseconds (1/2 second) * @categories Configuration_Variables */ int def_auto_complete_timeout_time = 500; /** * Auto-complete should stop searching for completions after this * amount of time. This is done to prevent large delays. * This setting is used when auto-complete is forced by invoking * list-members (but not auto-list members). * * @default 15000 milliseconds (15 seconds) * @categories Configuration_Variables */ int def_auto_complete_timeout_forced = 15000; /** * Maximum number of items to find for auto symbol completion * * @default 100 * @categories Configuration_Variables */ int def_auto_complete_max_symbols = 100; /** * Maximum number of items to find for auto word completion * * @default 100 * @categories Configuration_Variables */ int def_auto_complete_max_words = 100; /** * Maximum number of symbols to display function prototypes for * * @default 20 * @categories Configuration_Variables */ int def_auto_complete_max_prototypes = 20; /////////////////////////////////////////////////////////////////////////////// /** * This struct is used to control the auto completion gui and results. */ struct AUTO_COMPLETE_RESULTS { // window id of editor control, text box, or combo box int editor; // buffer identifier of last completion (editor.p_buf_id) int buffer; // line offset of last completion typeless lineoffset; // column number of last completion (editor.p_col) int column; // p_col for start of prefix int start_col; // p_col for the end of the word (including all chars to replace) int end_col; // last modify type for buffer long lastModified; // Context Tagging symbol information VS_TAG_IDEXP_INFO idexp_info; // word prefix _str prefix; // Is this the auto-list members case where they typed an operator bool operatorTyped; // Is this the auto-list compatible parameters case where they typed a paren or comma? bool wasListParameters; // Is this iteration forcing showing categories bool doShowCategories; // Expected type for listing compatible values for arguments _str expected_type; // Expected return type for listing compatible values for arguments VS_TAG_RETURN_TYPE expected_rt; // Expected name for argument being listed _str expected_name; // information about symbols we already looked up VS_TAG_RETURN_TYPE visited:[]; // start column to begin removing old identifier at int removeStartCol; // number of characters to remove int removeLen; // allow insertion of longest unique prefix bool allowInsertLongest; // allow showing info and comments for this item? bool allowShowInfo; // only allow explicit replace action bool allowImplicitReplace; // was auto-complete forced or invoked on timer bool wasForced; // was auto-complete forced or invoked on timer bool wasListSymbols; // is auto-complete active? bool isActive; // may need to restart auto-complete bool maybeRestartListParametersAfterKey; // original content of line _str origLineContent; // original start column int origCol; // original word prefix _str origPrefix; // position to move to when replacing word typeless replaceWordPos; // modify date of buffer, to verify before replacing word int replaceWordLastModified; // completed words and thier comment information AUTO_COMPLETE_INFO words[]; // index of current words selected int wordIndex; // was there an exact match found? bool foundExactMatch; // picture type created by _MarkerTypeAlloc int markerType; // picture index created by _StreamMarkerAdd int streamMarkerIndex; // window id of comment form (for single matches) int commentForm; // window id of list form (for multiple matches) int listForm; // timer id for displaying auto completion items and comments int timerId; // ignore any switch-buffer events that may be triggered while // auto-complete is updating it's list of results bool ignoreSwitchBufAndModuleLoadEvents; }; /////////////////////////////////////////////////////////////////////////////// // auto completion results static AUTO_COMPLETE_RESULTS gAutoCompleteResults; /////////////////////////////////////////////////////////////////////////////// // maps a priority number to a category name. This way we do not have // to store the category name for every single auto-complete result. // static _str gAutoCompleteCategoryNames:[]; /////////////////////////////////////////////////////////////////////////////// // picture of light bulb shown in gutter of editor control int _pic_light_bulb=0; int _pic_keyword=0; int _pic_syntax=0; int _pic_alias=0; int _pic_complete_next=0; int _pic_complete_prev=0; /////////////////////////////////////////////////////////////////////////////// defeventtab auto_complete_key_overrides; def ESC=AutoCompleteDoKey; // cancel auto complete def C_C=AutoCompleteDoKey; // copy comments def C_A=AutoCompleteDoKey; // select all comments def C_U=AutoCompleteDoKey; // deselect all comments def C_G=AutoCompleteDoKey; // maybe cancel def ENTER=AutoCompleteDoKey; // auto complete on ENTER def TAB=AutoCompleteDoKey; // next auto completion choice def S_TAB=AutoCompleteDoKey; // prev auto completion choice def BACKSPACE=AutoCompleteDoKey; // backspace def DEL=AutoCompleteDoKey; // delete def UP=AutoCompleteDoKey; // next auto completion def DOWN=AutoCompleteDoKey; // prev auto completion def LEFT=AutoCompleteDoKey; // move cursor to the left def RIGHT=AutoCompleteDoKey; // move corsor to the right def PGDN=AutoCompleteDoKey; // page down auto completions def PGUP=AutoCompleteDoKey; // page up auto completions def C_I=AutoCompleteDoKey; // next auto completion def C_K=AutoCompleteDoKey; // prev auto completion def C_PGDN=AutoCompleteDoKey; // next auto completion comment def C_PGUP=AutoCompleteDoKey; // prev auto completion comment def S_PGDN=AutoCompleteDoKey; // page down comments def S_PGUP=AutoCompleteDoKey; // page up comments def S_HOME=AutoCompleteDoKey; // top of comments def S_END=AutoCompleteDoKey; // bottom of comments def S_DOWN=AutoCompleteDoKey; // move auto-complete list above line def S_UP=AutoCompleteDoKey; // move auto-complete list below line def S_LEFT=AutoCompleteDoKey; // move auto-complete comments to left def S_RIGHT=AutoCompleteDoKey; // move auto-complete comment to right def "C- "=AutoCompleteDoKey; // insert longest unique prefix completion def "C-S- "=AutoCompleteDoKey; // complete one character def " "-\127=AutoCompleteDoKey; // update auto complete def "A-."=AutoCompleteDoKey; // cycle through categories def "M-."=AutoCompleteDoKey; // cycle through categories def "A-M-."=AutoCompleteDoKey; // cycle through categories def "A-,"=AutoCompleteDoKey; // force list-compatible symbols def "M-,"=AutoCompleteDoKey; // force list-compatible symbols def "A-M-,"=AutoCompleteDoKey; // force list-compatible symbols /////////////////////////////////////////////////////////////////////////////// // initialization code ran when editor is started // definit() { gAutoCompleteResults.origLineContent=null; gAutoCompleteResults.removeLen=0; gAutoCompleteResults.allowInsertLongest=true; gAutoCompleteResults.allowShowInfo=false; gAutoCompleteResults.allowImplicitReplace=false; gAutoCompleteResults.wasForced=false; gAutoCompleteResults.wasListSymbols=false; gAutoCompleteResults.wasListParameters=false; gAutoCompleteResults.doShowCategories=false; gAutoCompleteResults.operatorTyped=false; // initialize results struct gAutoCompleteResults.editor=0; gAutoCompleteResults.buffer=0; gAutoCompleteResults.lineoffset=0; gAutoCompleteResults.column=0; gAutoCompleteResults.start_col= -1; gAutoCompleteResults.end_col=-1; gAutoCompleteResults.lastModified=0; gAutoCompleteResults.words._makeempty(); gAutoCompleteResults.wordIndex=-1; gAutoCompleteResults.foundExactMatch=false; gAutoCompleteResults.isActive=false; gAutoCompleteResults.maybeRestartListParametersAfterKey = false; gAutoCompleteResults.markerType=-1; gAutoCompleteResults.streamMarkerIndex=-1; gAutoCompleteResults.commentForm=0; gAutoCompleteResults.listForm=0; gAutoCompleteResults.timerId=-1; gAutoCompleteResults.ignoreSwitchBufAndModuleLoadEvents=false; gAutoCompleteResults.replaceWordPos=""; gAutoCompleteResults.expected_type=""; gAutoCompleteResults.expected_name=""; gAutoCompleteResults.visited._makeempty(); tag_idexp_info_init(gAutoCompleteResults.idexp_info); tag_return_type_init(gAutoCompleteResults.expected_rt); processAutoCompleteOptions := LanguageSettings.getAutoCompleteOptions("process"); processAutoCompleteOptions &= ~(AUTO_COMPLETE_UNIQUE|AUTO_COMPLETE_WORDS|AUTO_COMPLETE_KEYWORDS|AUTO_COMPLETE_NO_INSERT_SELECTED); value:=LanguageSettings.getAutoCompleteOptions("process", processAutoCompleteOptions+1); if (value!=processAutoCompleteOptions) { LanguageSettings.setAutoCompleteOptions("process", processAutoCompleteOptions); } gAutoCompleteCategoryNames._makeempty(); gAutoCompleteCategoryNames:[(int)AUTO_COMPLETE_KEYWORD_PRIORITY ] = "Keywords"; gAutoCompleteCategoryNames:[(int)AUTO_COMPLETE_SYNTAX_PRIORITY ] = "Syntax Expansion"; gAutoCompleteCategoryNames:[(int)AUTO_COMPLETE_ALIAS_PRIORITY ] = "Aliases"; gAutoCompleteCategoryNames:[(int)AUTO_COMPLETE_COMPATIBLE_PRIORITY ] = "Compatible"; gAutoCompleteCategoryNames:[(int)AUTO_COMPLETE_LOCALS_PRIORITY ] = "Locals"; gAutoCompleteCategoryNames:[(int)AUTO_COMPLETE_MEMBERS_PRIORITY ] = "Members"; gAutoCompleteCategoryNames:[(int)AUTO_COMPLETE_CURRENT_FILE_PRIORITY ] = "Current file"; gAutoCompleteCategoryNames:[(int)AUTO_COMPLETE_SYMBOL_PRIORITY ] = "Symbols"; gAutoCompleteCategoryNames:[(int)AUTO_COMPLETE_WORD_COMPLETION_PRIORITY] = "Words"; gAutoCompleteCategoryNames:[(int)AUTO_COMPLETE_FILES_PRIORITY ] = "Files"; gAutoCompleteCategoryNames:[(int)AUTO_COMPLETE_ARGUMENT_PRIORITY ] = "Arguments"; } bool isAutoCompletePoundIncludeSupported(_str langId) { return (pos(" "langId" ", AUTOCOMPLETE_POUND_INCLUDE_LANGS) != 0); } static const AUTOCOMPLETE_POUND_INCLUDE_LANGS= " c e ansic m ch as java cfscript phpscript idl cs applescript "; void _UpgradeAutoCompleteSettings(_str config_migrated_from_version) { // if we did not upgrade from anything, don't worry about it if (config_migrated_from_version == "") return; // get the major/minor version parse config_migrated_from_version with auto major "." auto minor "." auto revision "." .; // we changed up this option in v16 if (major < 16) { // this is the value we are going to set everything to value := def_c_expand_include; _str poundIncludeLangs[]; split(strip(AUTOCOMPLETE_POUND_INCLUDE_LANGS, "B", " "), " ", poundIncludeLangs); for (i := 0; i < poundIncludeLangs._length(); i++) { langId := poundIncludeLangs[i]; if (langId == "e") { // this was on by default for slick-c, let's leave it that way LanguageSettings.setAutoCompletePoundIncludeOption(langId, AC_POUND_INCLUDE_QUOTED_ON_SPACE); } else { LanguageSettings.setAutoCompletePoundIncludeOption(langId, value); } } } // increase the delay before showing symbols in the preview window after mouse-hover over if (major == 16 || major == 17) { if (def_tag_hover_delay == 50) { def_tag_hover_delay = 500; _config_modify_flags(CFGMODIFY_DEFVAR); } } } /** * Auto-complete replace word callback for syntax expansions. * * Note that syntax expansion keys off of all the text from the start of * the current line, so the removeStartCol needs to be adjusted to reflect * the first non-blank column of the line. * * @param insertWord (input) selected word * @param prefix (input) word prefix under cursor * @param removeStartCol (output) set to column where completion was done * @param removeLen (output) set to length of completion * @param onlyInsertWord (input) only insert new word, do not expand syntax * @param symbol (input) symbol information (always null) */ void _autocomplete_space(_str insertWord,_str prefix,int &removeStartCol,int &removeLen,bool onlyInsertWord,struct VS_TAG_BROWSE_INFO symbol) { // do not modify read-only files if (_QReadOnly()) { _readonly_error(0); return; } // move to beginning of word to replace p_col -= length(prefix); // now move to the beginning of the line, because syntax expansion // works from the first non-blank column. orig_col := p_col; first_non_blank(); removeStartCol = p_col; // calculate the number of characters of text to replace removeLen = length(prefix); if (p_col < orig_col && lowcase(get_text(orig_col-p_col)) == lowcase(substr(insertWord,1,orig_col-p_col))) { removeLen += (orig_col - p_col); } // now replace the word _delete_text(removeLen); _insert_text(insertWord); // and then invoke syntax expansion on space if (!onlyInsertWord && last_event() != " ") { autocomplete_space(); } } /////////////////////////////////////////////////////////////////////////////// /** * SPACE BAR in auto complete mode *
* New binding of SPACE key when in auto complete mode. * Executes the underlying binding for the space key. * * @appliesTo Edit_Window, Editor_Control * * @categories Edit_Window_Methods, Editor_Control_Methods */ _command void autocomplete_space() name_info(","VSARG2_MULTI_CURSOR|VSARG2_CMDLINE|VSARG2_REQUIRES_EDITORCTL|VSARG2_LASTKEY) { AutoCompleteDefaultKey(" ",true,false); } /////////////////////////////////////////////////////////////////////////////// /** * Get the auto complete picture type. *
* If ');
}
/**
* Add an auto-complete category to the catalog of completion
* categories.
*/
void AutoCompleteAddCategory(int priorityLevel, _str categoryName)
{
gAutoCompleteCategoryNames:[priorityLevel] = categoryName;
}
/**
* Is the given word match a symbol?
*/
static bool AutoCompleteWordIsSymbol(AUTO_COMPLETE_INFO &word)
{
if (word.symbol == null) {
return false;
}
if (word.priorityLevel < AUTO_COMPLETE_FIRST_SYMBOL_PRIORITY) {
return false;
}
if (word.priorityLevel > AUTO_COMPLETE_LAST_SYMBOL_PRIORITY) {
return false;
}
return true;
}
/**
* Add an item to the array of auto completions being populated by
* one of the auto completion callbacks.
*
* @param words array of completion results
* @param priority priority for this result category
* @param word completion word
* @param command command for processing word
* @param comments description of word
* @param symbol symbol information
*/
void AutoCompleteAddResult(AUTO_COMPLETE_INFO (&words)[],
int priority,
_str displayWord,
void (*pfnReplaceWord)(_str insertWord,_str prefix,int &removeStartCol,int &removeLen,bool onlyInsertWord,struct VS_TAG_BROWSE_INFO symbol)=null,
_str comments="",
VS_TAG_BROWSE_INFO &symbol=null,
bool caseSensitive=true,
int bitmapIndex=0,
_str insertWord=null
)
{
//say("AutoCompleteAddResult: word="displayWord);
AUTO_COMPLETE_INFO info;
info.priorityLevel = priority;
info.displayWord = displayWord;
info.displayArguments = false;
info.comments = comments;
info.comment_flags = VSCODEHELP_COMMENTFLAG_HTML;
info.pfnReplaceWord=pfnReplaceWord;
info.symbol = symbol;
info.caseSensitive=caseSensitive;
info.bitmapIndex=bitmapIndex;
if (insertWord==null) {
info.insertWord=displayWord;
} else {
info.insertWord=insertWord;
}
words :+= info;
}
/**
* Indicate that there was an exact match to the word prefix.
* This will disable auto-selection.
*/
void AutoCompleteFoundExactMatch()
{
gAutoCompleteResults.foundExactMatch = true;
}
/**
* Generate a suggestion for what would be expanded by syntax expansion
* (space expansion).
*
* This function calls the extension specific callback function
* _[ext]_get_syntax_completions(var words, _str prefix='')
*
* @param options auto completion options
*
* @return 0 on success, <0 on error.
*/
static int AutoCompleteGetSyntaxExpansion(int options, bool forceUpdate=false)
{
// get the current line
orig_line := "";
get_line(orig_line);
// set min_abbrev to -1 if we are forcing the list no matter what
min_abbrev := (forceUpdate? -1 : 0);
// now find the callback and call it
index := _FindLanguageCallbackIndex("_%s_get_syntax_completions", p_LangId);
if (index > 0) {
return call_index(gAutoCompleteResults.words, strip(orig_line), min_abbrev, index);
}
return STRING_NOT_FOUND_RC;
}
/**
* For case insensetive languages, call the extension specific
* callback to adjust the keyword case of keywords, and syntax
* expansion results.
*
* @param kw keyword or string to adjust case of
* @return keyword in language specific case
*/
_str AutoCompleteKeywordCase(_str kw, _str sample="")
{
// do nothing for case sensitive languages
if (p_EmbeddedCaseSensitive) return kw;
// get index of keyword_case callback function
keyword_case_index := _FindLanguageCallbackIndex("_%s_keyword_case");
if (!keyword_case_index) return kw;
// call the extension specific function
return call_index(kw, false, sample, keyword_case_index);
}
/**
* Generic function used for finding the syntax expansion hints
* available, assuming we are given a list of "space words", as
* normally required by {@link min_abbrev2()}.
*
* Unless the
* NOTE: aliases must be an exact match, so there can be only one
*
* @param options auto completion options
*
* @return 0 on success, <0 on error.
*/
static int AutoCompleteGetAliasExpansion(int options, bool forceUpdate=false)
{
return find_alias_completions(gAutoCompleteResults.words, forceUpdate);
}
static int AutoCompleteGetAllSymbols(_str (&errorArgs)[],
int auto_complete_options,
bool forceUpdate,
_str lastid,
_str lastid_prefix,
bool matchAnyWord,
bool caseSensitive,
int context_flags,
int symbol_priority,
VS_TAG_IDEXP_INFO idexp_info,
int &num_symbols, int max_symbols,
VS_TAG_RETURN_TYPE (&visited):[] )
{
origNumWords := gAutoCompleteResults.words._length();
symbolsStatus := 0;
status := AutoCompleteGetSymbols(errorArgs,
auto_complete_options,
forceUpdate,
lastid, caseSensitive,
VS_TAGFILTER_ANYTHING,
context_flags,
symbol_priority,
gAutoCompleteResults.idexp_info,
num_symbols, max_symbols,
gAutoCompleteResults.visited);
if (!symbolsStatus && status < 0) symbolsStatus = status;
if (!status && symbolsStatus == VSCODEHELPRC_NO_SYMBOLS_FOUND) symbolsStatus=0;
if (_CheckTimeout()) {
return VSCODEHELPRC_LIST_MEMBERS_TIMEOUT;
}
if (gAutoCompleteResults.words._length() - origNumWords > def_auto_complete_max_symbols) {
return VSCODEHELPRC_LIST_MEMBERS_LIMITED;
}
if (length(lastid_prefix) < length(lastid)) {
_str newErrorArgs[];
status = AutoCompleteGetSymbols(newErrorArgs,
auto_complete_options,
forceUpdate,
lastid_prefix, caseSensitive,
VS_TAGFILTER_ANYTHING,
context_flags,
symbol_priority,
idexp_info,
num_symbols, max_symbols,
gAutoCompleteResults.visited);
if (!symbolsStatus && status < 0) {
errorArgs = newErrorArgs;
symbolsStatus = status;
}
if (!status && symbolsStatus == VSCODEHELPRC_NO_SYMBOLS_FOUND) symbolsStatus=0;
if (_CheckTimeout()) {
return VSCODEHELPRC_LIST_MEMBERS_TIMEOUT;
}
if (gAutoCompleteResults.words._length() - origNumWords > def_auto_complete_max_symbols) {
return VSCODEHELPRC_LIST_MEMBERS_LIMITED;
}
}
if (caseSensitive && !(_GetCodehelpFlags() & VSCODEHELPFLAG_LIST_MEMBERS_CASE_SENSITIVE)) {
_str newErrorArgs[];
status = AutoCompleteGetSymbols(newErrorArgs,
auto_complete_options,
forceUpdate,
lastid, false,
VS_TAGFILTER_ANYTHING|(caseSensitive? VS_TAGFILTER_CASESENSITIVE:0),
context_flags,
symbol_priority,
idexp_info,
num_symbols, max_symbols,
gAutoCompleteResults.visited);
if (!symbolsStatus && status < 0) {
errorArgs = newErrorArgs;
symbolsStatus = status;
}
if (!status && symbolsStatus == VSCODEHELPRC_NO_SYMBOLS_FOUND) symbolsStatus=0;
if (_CheckTimeout()) {
return VSCODEHELPRC_LIST_MEMBERS_TIMEOUT;
}
if (gAutoCompleteResults.words._length() - origNumWords > def_auto_complete_max_symbols) {
return VSCODEHELPRC_LIST_MEMBERS_LIMITED;
}
if (length(lastid_prefix) < length(lastid)) {
status = AutoCompleteGetSymbols(newErrorArgs,
auto_complete_options,
forceUpdate,
lastid_prefix, false,
VS_TAGFILTER_ANYTHING|(caseSensitive? VS_TAGFILTER_CASESENSITIVE:0),
context_flags,
symbol_priority,
idexp_info,
num_symbols, max_symbols,
gAutoCompleteResults.visited);
if (!symbolsStatus && status < 0) {
errorArgs = newErrorArgs;
symbolsStatus = status;
}
if (!status && symbolsStatus == VSCODEHELPRC_NO_SYMBOLS_FOUND) symbolsStatus=0;
if (_CheckTimeout()) {
return VSCODEHELPRC_LIST_MEMBERS_TIMEOUT;
}
if (gAutoCompleteResults.words._length() - origNumWords > def_auto_complete_max_symbols) {
return VSCODEHELPRC_LIST_MEMBERS_LIMITED;
}
}
}
if (matchAnyWord && length(lastid_prefix) > 0 &&
!(idexp_info.info_flags & VSAUTOCODEINFO_IN_PREPROCESSING) &&
!(idexp_info.info_flags & VSAUTOCODEINFO_IN_STRING_OR_NUMBER)
) {
_str newErrorArgs[];
status = AutoCompleteGetSymbols(newErrorArgs,
auto_complete_options,
forceUpdate,
"", caseSensitive,
VS_TAGFILTER_ANYTHING,
context_flags,
symbol_priority,
idexp_info,
num_symbols, max_symbols,
gAutoCompleteResults.visited);
if (!symbolsStatus && status < 0) {
errorArgs = newErrorArgs;
symbolsStatus = status;
}
if (!status && symbolsStatus == VSCODEHELPRC_NO_SYMBOLS_FOUND) symbolsStatus=0;
if (_CheckTimeout()) {
return VSCODEHELPRC_LIST_MEMBERS_TIMEOUT;
}
if (gAutoCompleteResults.words._length() - origNumWords > def_auto_complete_max_symbols) {
return VSCODEHELPRC_LIST_MEMBERS_LIMITED;
}
}
return symbolsStatus;
}
/**
* Generate a suggestion for what would be completed by the
* "codehelp_complete" command.
*
* @param options auto completion options
*
* @return 0 on success, <0 on error.
*/
static int AutoCompleteGetSymbols(_str (&errorArgs)[],
int auto_complete_options,
bool forceUpdate,
_str lastid_prefix,
bool caseSensitive,
int pushtag_flags,
int context_flags,
int symbol_priority,
VS_TAG_IDEXP_INFO idexp_info,
int &num_symbols, int max_symbols,
VS_TAG_RETURN_TYPE (&visited):[])
{
// Make sure that tagging is supported for this extension
if (!_istagging_supported(p_LangId)) {
//say("AutoCompleteGetSymbols: TAGGING NOT SUPPORTED");
return TAGGING_NOT_SUPPORTED_FOR_FILE_RC;
}
// Make sure the current context is up-to-date the first time we start
// auto-complete, but let the old results ride as we are typing.
// This gives us a slightly better response type while typing and
// refreshing the auto complete list.
isActive := AutoCompleteActive();
if (!isActive) {
MaybeBuildTagFile(p_LangId,false,true);
_UpdateContextAndTokens(true,forceUpdate);
_UpdateLocals(true);
}
// make sure that the context doesn't get modified by a background thread.
se.tags.TaggingGuard sentry;
sentry.lockContext(false);
sentry.lockMatches(true);
// compose the expansion prefix
word := "";
orig_word := idexp_info.lastid;
if (!forceUpdate && orig_word == "") {
//say("AutoCompleteGetSymbols: EMPTY PREFIX");
return VSCODEHELPRC_CONTEXT_NOT_VALID;
}
if (!forceUpdate && idexp_info.lastidstart_col+length(idexp_info.lastid) != p_col) {
//say("AutoCompleteGetSymbols: INVALID COLUMN");
return STRING_NOT_FOUND_RC;
}
tag_push_matches();
tag_clear_matches();
save_pos(auto p);
status := _Embeddedfind_context_tags(idexp_info.errorArgs,
idexp_info.prefixexp,
lastid_prefix,
idexp_info.lastidstart_offset,
idexp_info.info_flags,
idexp_info.otherinfo,
false, max_symbols,
false, caseSensitive,
pushtag_flags,
context_flags,
visited);
restore_pos(p);
if (status < 0) {
tag_pop_matches();
//say("AutoCompleteGetSymbols: FIND CONTEXT TAGS FAILED status="status);
errorArgs = idexp_info.errorArgs;
if (errorArgs._length()==0) errorArgs[1] = lastid_prefix;
return status;
}
// no matches, then we are out of here
num_matches := tag_get_num_of_matches();
if (num_matches <= 0) {
tag_pop_matches();
//say("AutoCompleteGetSymbols: NO MATCHES");
if (errorArgs._length()<=1 || length(errorArgs[1]) < length(lastid_prefix)) {
errorArgs[1] = lastid_prefix;
}
return VSCODEHELPRC_NO_SYMBOLS_FOUND;
}
// look up the replace symbol callback for this language
void (*pfnReplaceWord)(_str insertWord,_str prefix,int &removeStartCol,int &removeLen,bool onlyInsertWord,struct VS_TAG_BROWSE_INFO symbol) = null;
index := _FindLanguageCallbackIndex("_%s_autocomplete_replace_symbol", p_LangId);
if (index > 0) {
pfnReplaceWord = name_index2funptr(index);
}
// get the auto completion options
case_options := caseSensitive? "":"I";
// add each match to the list
for (i:=1; i <= num_matches; ++i) {
// get the symbol information
tag_get_match_info(i, auto cm);
// skip anonymous identifiers
if (cm.flags & SE_TAG_FLAG_ANONYMOUS) {
continue;
}
// get the completed symbol name
word = cm.member_name;
// check for overloaded operators
if (cm.flags & SE_TAG_FLAG_OPERATOR) {
word = "operator ":+cm.member_name;
}
if (word == orig_word) {
AutoCompleteFoundExactMatch();
}
// Add the single unique match with it's comments
if (lastid_prefix!="" && pos(lastid_prefix, word, 1, case_options) != 1) {
continue;
}
if (!forceUpdate && _file_eq(cm.file_name,p_buf_name) && cm.line_no == p_RLine) {
continue;
}
// Add the single unique match with it's comments
AutoCompleteAddResult(gAutoCompleteResults.words,
symbol_priority,
word,
pfnReplaceWord,
cm.doc_comments,
cm);
}
// success
tag_pop_matches();
num_symbols += num_matches;
//say("AutoCompleteGetSymbols: SUCCESS, num matches="num_matches);
return 0;
}
/**
* Generate a suggestion for what would be completed by the
* function argument listing of symbols with compatible values
*
* @param options auto completion options
*
* @return 0 on success, <0 on error.
*/
static int AutoCompleteGetCompatibleValues(_str (&errorArgs)[],
int options,
bool forceUpdate,
_str lastid_prefix,
bool caseSensitive,
VS_TAG_IDEXP_INFO idexp_info,
_str expected_type,
VS_TAG_RETURN_TYPE rt,
_str expected_name,
VS_TAG_RETURN_TYPE (&visited):[],
int depth=0
)
{
if (_chdebug) {
isay(depth, "AutoCompleteGetCompatibleValues: HERE");
}
// Completely disable this feature for SlickEdit Standard
if (!_haveContextTagging()) {
return VSRC_FEATURE_REQUIRES_PRO_EDITION;
}
// Make sure that tagging is supported for this extension
if (!_istagging_supported(p_LangId)) {
return TAGGING_NOT_SUPPORTED_FOR_FILE_RC;
}
// Make sure the current context is up-to-date the first time we start
// auto-complete, but let the old results ride as we are typing.
// This gives us a slightly better response type while typing and
// refreshing the auto complete list.
isActive := AutoCompleteActive();
if (!isActive) {
MaybeBuildTagFile(p_LangId,false,true);
_UpdateContextAndTokens(true,forceUpdate);
_UpdateLocals(true);
}
// make sure that the context doesn't get modified by a background thread.
se.tags.TaggingGuard sentry;
sentry.lockContext(false);
sentry.lockMatches(true);
// compose the expansion prefix
word := "";
orig_word := idexp_info.lastid;
// make sure that we aren't looking for an empty string
if (!forceUpdate && idexp_info.lastidstart_col+length(idexp_info.lastid) != p_col) {
return VSCODEHELPRC_CONTEXT_NOT_VALID;
}
// look up the compatible symbol matches
tag_push_matches();
tag_clear_matches();
status := _Embeddedinsert_auto_params(errorArgs,
gAutoCompleteResults.editor,
0, // insert into match set
idexp_info.prefixexp,
idexp_info.lastid,
lastid_prefix,
idexp_info.lastidstart_offset,
expected_type,
rt,
expected_name,
idexp_info.info_flags,
idexp_info.otherinfo,
visited);
if (status < 0) {
tag_pop_matches();
return status;
}
// no matches, then we are out of here
num_matches := tag_get_num_of_matches();
if (num_matches <= 0) {
tag_pop_matches();
errorArgs[1] = lastid_prefix;
return VSCODEHELPRC_NO_SYMBOLS_FOUND;
}
// look up the replace symbol callback for this language
void (*pfnReplaceWord)(_str insertWord,_str prefix,int &removeStartCol,int &removeLen,bool onlyInsertWord,struct VS_TAG_BROWSE_INFO symbol) = null;
index := _FindLanguageCallbackIndex("_%s_autocomplete_replace_symbol", p_LangId);
if (index > 0) {
pfnReplaceWord = name_index2funptr(index);
}
// get the auto completion options
auto_complete_options := AutoCompleteGetOptions();
case_options := caseSensitive? "":"I";
// add each match to the list
for (i:=1; i <= num_matches; ++i) {
// get the symbol information
tag_get_match_info(i, auto cm);
// get the completed symbol name
word = cm.member_name;
if (word == orig_word) {
AutoCompleteFoundExactMatch();
}
if (lastid_prefix!="" && pos(lastid_prefix, word, 1, case_options) != 1) {
//continue;
}
if (_file_eq(cm.file_name,p_buf_name) && cm.line_no == p_RLine) {
continue;
}
// Add the single unique match with it's comments
AutoCompleteAddResult(gAutoCompleteResults.words,
AUTO_COMPLETE_COMPATIBLE_PRIORITY,
word,
pfnReplaceWord, //_symbol_autocomplete,
"",
cm);
}
// success
tag_pop_matches();
return 0;
}
/**
*
*
* @author dbrueni (1/11/18)
*/
static void AutoCompleteGetSymbolComments()
{
int word_index = gAutoCompleteResults.wordIndex;
if (word_index < 0 || word_index >= gAutoCompleteResults.words._length()) {
return;
}
if (gAutoCompleteResults.words[word_index].comments != "") {
return;
}
if (gAutoCompleteResults.words[word_index].symbol == null) {
return;
}
// get the editor control and make it current
orig_wid := p_window_id;
editorctl_wid := AutoCompleteGetEditorWid();
if (!_iswindow_valid(editorctl_wid)) {
return;
}
p_window_id = editorctl_wid;
// get the symbol information for the current item
VS_TAG_BROWSE_INFO cm = gAutoCompleteResults.words[word_index].symbol;
// get the create a symbol declaration
tag_info := "";
auto_complete_options := AutoCompleteGetOptions();
if (auto_complete_options & AUTO_COMPLETE_SHOW_DECL) {
tag_info=extension_get_decl(p_LangId, cm, VSCODEHELPDCLFLAG_VERBOSE);
tag_info="";
}
comment_flags := VSCODEHELP_COMMENTFLAG_HTML;
comment_info := "";
if (_haveContextTagging() && (auto_complete_options & AUTO_COMPLETE_SHOW_COMMENTS)) {
if (cm.file_name != "") {
_ExtractTagComments2(comment_flags,
comment_info, 150, /*_xmlcfg_find_simple has large comment */
cm.member_name, cm.file_name, cm.line_no
);
}
if (comment_info!="") {
_str param_name=(cm.type_name=="param")? cm.member_name:"";
_make_html_comments(comment_info,comment_flags,cm.return_type,param_name,true,cm.language);
}
}
// concatenate the comment information
_str comments = tag_info;
if (tag_info == "") {
comments = comment_info;
} else if (comment_info != "") {
comments :+= "
* This frees the user from having to remember the key combination
* associated with various completion mechanisms, all they really need
* to remember is Space key. And if they are an advanced user, they also
* can use Ctrl+Shift+Space to incrementally pull in part of an auto
* completion.
*
* The features pulled into this system include:
*
* For the text box and combo box controls, this same system supplements
* the existing completion mechanisms by providing a dynamic system for
* displaying the matches, much like that used by the Windows file chooser.
*
* For the SlickEdit command line, this system supplements command history
* as well as command argument completion using the same display mechanism.
*
* @see c_space
* @see expand_alias
* @see codehelp_complete
* @see complete_next
* @see complete_prev
* @see file_match
* @see tag_match
*
* @appliesTo Edit_Window, Editor_Control, Text_Box, Combo_Box
* @categories Completion_Functions, Combo_Box_Methods, Edit_Window_Methods, Editor_Control_Methods, Text_Box_Methods
*/
_command int autocomplete() name_info(','VSARG2_TEXT_BOX|VSARG2_REQUIRES_EDITORCTL|VSARG2_LASTKEY)
{
if (AutoCompleteActive() && AutoCompleteRunCommand()) {
return 0;
}
_str errorArgs[];
status := AutoCompleteUpdateInfo(true,true,true,false,true,null,null,null,null,null,false,errorArgs);
if (status < 0) {
msg := _CodeHelpRC(status,errorArgs);
message(msg);
}
return status;
}
/**
* @return Returns the name of the autocomplete function pointer function
*/
static _str AutoCompleteGetReplaceWordFunctionName(typeless fp)
{
if (fp == null) return "";
if (fp == _autocomplete_space) return "_autocomplete_space";
if (fp == _autocomplete_expand_alias) return "_autocomplete_expand_alias";
if (fp == _autocomplete_process) return "_autocomplete_process";
if (fp == _autocomplete_prev) return "_autocomplete_prev";
if (fp == _autocomplete_next) return "_autocomplete_next";
if (_isfunptr(fp)) {
if (substr((_str)fp, 1, 1) :== "&") {
int index = (int)substr((_str)fp, 2);
if (name_type(index) == PROC_TYPE) {
return translate(name_name(index), "_", "-");
}
}
}
return "";
}
/**
* Perform the command suggested by the auto completion
*/
bool AutoCompleteRunCommand(_str terminationKey="")
{
// make sure completion results are active
if (!AutoCompleteActive()) {
return false;
}
// if the termination key was not space, tab, or enter, then
// do not replace the word.
if (!gAutoCompleteResults.allowImplicitReplace &&
terminationKey :!= " " && terminationKey :!= "\t" && terminationKey :!= ENTER) {
return false;
}
// make sure that the editor is still ready to go
editorctl_wid := AutoCompleteGetEditorWid();
if (!_iswindow_valid(editorctl_wid) || !editorctl_wid._isEditorCtl() ||
gAutoCompleteResults.lineoffset != editorctl_wid.point() ||
gAutoCompleteResults.column != editorctl_wid.p_col ||
gAutoCompleteResults.buffer != editorctl_wid.p_buf_id ) {
return false;
}
// make sure the current item selected is valid
int word_index = gAutoCompleteResults.wordIndex;
if (word_index < 0 || word_index >= gAutoCompleteResults.words._length()) {
return false;
}
// save the original window ID and make the editor current
orig_wid := p_window_id;
p_window_id = editorctl_wid;
// start embedded mode
typeless orig_values;
int embedded = _EmbeddedStart(orig_values);
// look up callback for replacing symbols (before and after)
void (*pfnBeforeReplaceAction)(AUTO_COMPLETE_INFO &word, VS_TAG_IDEXP_INFO &idexp_info, _str terminationKey) = null;
index := _FindLanguageCallbackIndex("_%s_autocomplete_before_replace", p_LangId);
if (index > 0) {
pfnBeforeReplaceAction = name_index2funptr(index);
}
bool (*pfnAfterReplaceAction)(AUTO_COMPLETE_INFO &word, VS_TAG_IDEXP_INFO &idexp_info, _str terminationKey) = null;
index = _FindLanguageCallbackIndex("_%s_autocomplete_after_replace", p_LangId);
if (index > 0) {
pfnAfterReplaceAction = name_index2funptr(index);
}
// replace the prefix with the selected word
word := gAutoCompleteResults.words[word_index];
prefix := gAutoCompleteResults.prefix;
idexp_info := gAutoCompleteResults.idexp_info;
if (pfnBeforeReplaceAction != null && word.symbol != null) {
(*pfnBeforeReplaceAction)(word, idexp_info, terminationKey);
}
AutoCompleteReplaceWordForEachMultiCursor(word.pfnReplaceWord, prefix, word.insertWord, false, word.symbol);
AutoCompleteTerminate();
// call language specific post-processing function
doDefaultActions := true;
if (pfnAfterReplaceAction != null && word.symbol != null) {
doDefaultActions = (*pfnAfterReplaceAction)(word, idexp_info, terminationKey);
}
// call generic function to deal with jumping into function arg help
doBeautify := true;
if (doDefaultActions && AutoCompleteWordIsSymbol(word)) {
// if option to insert an open paren for functions is enabled
if (_GetCodehelpFlags() & VSCODEHELPFLAG_INSERT_OPEN_PAREN) {
// and the termination key is space or tab or enter
if (terminationKey=="" || terminationKey==TAB || terminationKey==ENTER) {
// and we aren't in some crazy special case
info_flags := (idexp_info != null)? idexp_info.info_flags : 0;
if (!(info_flags & VSAUTOCODEINFO_IN_JAVADOC_COMMENT) &&
!(info_flags & VSAUTOCODEINFO_LASTID_FOLLOWED_BY_PAREN)) {
// and this genuinely is a function type
if (tag_tree_type_is_func(word.symbol.type_name)) {
// does this caption have a parenthesis? (is it a function?)
caption := tag_make_caption_from_browse_info(word.symbol, include_class:true, include_args:false, include_tab:false);
if (pos("(", caption)) {
// if we have an open paren, then insert open paren and go directly
// into function help, unless name is already followed by a paren.
// kind of language specific...
last_event("(");
auto_functionhelp_key();
doBeautify = false;
}
}
}
}
}
// generic handling for quotes, parens, and braces as insertions
if (word.insertWord == "\"\"" ||
word.insertWord == "''" ||
word.insertWord == "``" ||
word.insertWord == "()" ||
word.insertWord == "[]") {
if (get_text(2, (int)_QROffset()-2) == word.insertWord) {
p_col -= 2;
_delete_text(2);
if (!AutoBracketKeyin(_first_char(word.insertWord))) {
keyin(_last_char(word.insertWord));
}
}
}
}
// get out of embedded language mode
if (embedded == 1) {
_EmbeddedEnd(orig_values);
}
// we did it!
if (_iswindow_valid(orig_wid)) {
p_window_id = orig_wid;
}
if (doBeautify && beautify_alias_expansion(p_LangId)) {
long markers[];
save_pos(auto pp);
p_col=1;
low := _QROffset();
_end_line();
high := _QROffset();
restore_pos(pp);
if (high > low) {
if (!_macro('S')) {
_undo('S');
}
new_beautify_range(low, high, markers, true, false, false, BEAUT_FLAG_TYPING|BEAUT_FLAG_COMPLETION);
}
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
/**
* Get the auto complete options wich are a bitset of
*
* The options are stored per extension type. If the options
* are not yet defined for an extension, then use
*
* The options are stored per extension type using
*
* This settings is stored per extension type. If the options
* are not yet defined for an extension, then use
*
* This settings is stored per extension type. If the options
* are not yet defined for an extension, then use
*
* NOTE: If 'key' is bound to a macro that closes the current
* window and leaves focus in another window, especially one
* that is not an editor control, and the window ID found in
*
* The completions are separated into categories (folders).
* Each item in the tree uses it's tree index to refer back
* to the index of that item in the list of words.
*
* @param words list of auto complete items
* @param current index of word in list to make active
*/
static void AutoCompleteUpdateList(AUTO_COMPLETE_INFO (&words)[], int current)
{
// make sure auto-complete is active
editorctl_wid := AutoCompleteGetEditorWid();
if (!_iswindow_valid(editorctl_wid)) {
return;
}
// set up symbol browser bitmaps
hadItemSelected := gAutoCompleteResults.allowImplicitReplace;
cb_prepare_expand(p_active_form, p_window_id, TREE_ROOT_INDEX);
// set up the tree for updating
gAutoCompleteResults.isActive=false;
auto_complete_options := AutoCompleteGetOptionsMaybeShowCategories(editorctl_wid.p_LangId);
p_LevelIndent = _dx2lx(SM_TWIP, 4);
_TreeSetInfo(TREE_ROOT_INDEX, 1, -1, 01, TREENODE_HIDEINDICATOR);
_TreeBeginUpdate(TREE_ROOT_INDEX, "", "T");
// if the list is too long, then do not show function arguments
int categories:[];
showAllPrototypes := false;
i := 0;
n := words._length();
if (auto_complete_options & AUTO_COMPLETE_SHOW_PROTOTYPES) {
showAllPrototypes = true;
if (n > def_auto_complete_max_prototypes) {
bool uniqueCaptions:[];
bool uniqueWords:[];
for (i=0; idef_auto_complete_options
has the AUTO_COMPLETE_SHOW_BAR
* option set, then set flags to draw the original word with the expansion bar.
*
* @param resetFlags Force the picture flags to be updated.
* Use this when def_auto_complete_options changes.
*
* @return Integer handle for the pic type.
*
* @see _MarkerTypeAlloc
* @see _MarkerTypeSetFlags
*/
static int AutoCompleteGetPicType(bool resetFlags=false)
{
if (gAutoCompleteResults.markerType <= 0) {
gAutoCompleteResults.markerType = _MarkerTypeAlloc();
if (gAutoCompleteResults.markerType < 0) {
return gAutoCompleteResults.markerType;
}
resetFlags=true;
}
if (resetFlags) {
//auto_complete_options := AutoCompleteGetOptions();
//int pic_flags = VSMARKERTYPEFLAG_DRAW_NONE;
//if (auto_complete_options & AUTO_COMPLETE_SHOW_BAR) {
// pic_flags = VSMARKERTYPEFLAG_DRAW_EXPANSION;
//}
//_MarkerTypeSetFlags(gAutoCompleteResults.markerType, pic_flags);
}
return gAutoCompleteResults.markerType;
}
/**
* Get the auto complete picture that is displayed in the gutter
* when a completion becomes available.
*/
static int AutoCompleteGetPicture()
{
auto_complete_options := AutoCompleteGetOptions();
if (!(auto_complete_options & AUTO_COMPLETE_SHOW_BULB)) {
if (p_KeepPictureGutter) p_KeepPictureGutter = false;
return 0;
}
if (_pic_light_bulb <= 0) {
_pic_light_bulb = _find_or_add_picture("_ed_lightbulb.svg");
}
if (!p_KeepPictureGutter) {
p_KeepPictureGutter = true;
refresh();
}
return _pic_light_bulb;
}
/**
* Display the list of auto-completion results
*
* @return 1 on success
*/
static int AutoCompleteShowList(bool doListMembers)
{
// terminate mouse-over help if it is active
TerminateMouseOverHelp();
// get the screen position of the editor control
wx := 0;
wy := 0;
_map_xy(p_window_id,0,wx,wy,SM_TWIP);
wy += _dy2ly(SM_TWIP, p_font_height);
// get the position of the start of the word
cy := p_cursor_y;
cx := p_cursor_x;
px := _text_width(gAutoCompleteResults.prefix);
px = _lx2dx(p_xyscale_mode, px);
cx -= px;
cy += 1;
_dxy2lxy(SM_TWIP, cx, cy);
// adjust position of form so we don't overlap function help
function_help_form_wid := ParameterHelpFormWid();
if (function_help_form_wid > 0) {
fx := 0;
fy := function_help_form_wid.p_y;
fh := function_help_form_wid.p_height;
if ( wy+cy <= fy+fh ) {
cy += fh;
}
}
// get text positioning information
tx := (gAutoCompleteResults.start_col-p_col)*p_font_width+p_cursor_x;
ty := p_cursor_y;
th := p_font_height;
// now show the window
list_wid := 0;
orig_wid := p_window_id;
cx += wx;
cy += wy;
if (gAutoCompleteResults.listForm <= 0) {
list_wid = show("-hidden -nocenter _auto_complete_list_form",
gAutoCompleteResults.words,
gAutoCompleteResults.wordIndex);
if (list_wid != 0) {
list_wid._move_window(cx,cy,list_wid.p_width,list_wid.p_height);
list_wid._get_window(cx,cy,wx,wy);
orig_wid.AutoCompletePositionForm(list_wid, function_help_form_wid);
auto_complete_options := AutoCompleteGetOptions();
if (doListMembers || (auto_complete_options & AUTO_COMPLETE_SHOW_LIST)) {
list_wid._ShowWindow(SW_SHOWNOACTIVATE);
}
list_wid.ctltree.p_MouseActivate=MA_NOACTIVATE;
gAutoCompleteResults.listForm = list_wid;
// color the current node if they have an item selected
if (gAutoCompleteResults.wordIndex>=0 && gAutoCompleteResults.allowImplicitReplace) {
list_wid.ctltree.p_AlwaysColorCurrent=true;
}
}
// make sure focus goes back to the editor
p_window_id = orig_wid;
_set_focus();
} else {
// update the current list of items
tree_wid := AutoCompleteGetTreeWid();
if (tree_wid > 0) {
list_wid = gAutoCompleteResults.listForm;
if (gAutoCompleteResults.wordIndex < 0 || !gAutoCompleteResults.allowImplicitReplace) {
gAutoCompleteResults.allowImplicitReplace=false;
gAutoCompleteResults.allowShowInfo=false;
}
tree_wid.AutoCompleteUpdateList(gAutoCompleteResults.words,
gAutoCompleteResults.wordIndex);
if (tree_wid._TreeGetNumChildren(TREE_ROOT_INDEX) > 0) {
orig_wid.AutoCompletePositionForm(list_wid, function_help_form_wid);
} else {
AutoCompleteTerminate();
}
} else {
gAutoCompleteResults.listForm = 0;
list_wid = 0;
}
}
// success
return 0;
}
/**
* Reposition the auto-complete list form compensating for the available
* screen real-estate and the location of the function parameter help form.
* The current window should be the editor control.
*
* @param form_wid auto-complete list form
* @param function_help_form_wid function help form window ID
* @param prefer_positioning_above try to position form above or below line?
*/
static void AutoCompletePositionForm(int form_wid,
int function_help_form_wid,
bool prefer_positioning_above=false)
{
// get the text position information
// tx x cooridinate of pivot identifier
// ty y cooridinate of pivot line
// th line height
col := gAutoCompleteResults.start_col;
tx := (col-p_col)*p_font_width+p_cursor_x;
ty := p_cursor_y;
th := p_font_height;
// get the X and Y position
x := tx;
y := ty+th;
_map_xy(p_window_id,0,x,y);
// adjust the position to compenate for the height of the
// function help form if it is currently being displayed.
if (function_help_form_wid && _iswindow_valid(function_help_form_wid)) {
fx := 0;
fy := _ly2dy(SM_TWIP,function_help_form_wid.p_y);
if ( y<=fy ) {
y = fy+_ly2dy(SM_TWIP,function_help_form_wid.p_height);
}
}
// get the total size of the screen (for the current monitor)
vx := vy := vw := vh := 0;
_GetVisibleScreenFromPoint(x,y,vx,vy,vw,vh);
// do not let the form come up off-screen to the left
if ( x < vx ) {
x = vx;
}
// get the height of the auto-complete list form in pixels
h := _ly2dy(form_wid.p_xyscale_mode,form_wid.p_height);
// check if we can just reduce height of form to fit on screen
if (y+h > vy+vh && y+th*10 < vy+vh) {
h = vy+vh-y-1;
}
// otherwise check if we should move the list form above
// the current line
if ((y+h >= vy+vh && ty >= h) ||
(prefer_positioning_above && ty-h >= vy) ||
(y+h >= vy+vh && tymin_abbrev
argument is explicitely passed in,
* this function assumes that the minimum expandable keyword length is
* stored in the 3rd item of "name_info" for the current extension.
*
* @param words array of auto complete items
* @param space_words language specific array or hash table
* of completion keywords
* @param min_abbrev minimum keyword prefix length
*/
int AutoCompleteGetSyntaxSpaceWords(var words, typeless &space_words, _str prefix="", int min_abbrev=0)
{
// get the current line
orig_line := "";
get_line(orig_line);
line := strip(orig_line,'T');
orig_word := strip(line);
if ( line!="" && p_col!=text_col(_rawText(line))+1 ) {
return STRING_NOT_FOUND_RC;
}
// did they give us a min_abbrev argument
if (min_abbrev == 0) {
tmp_abbrev := LanguageSettings.getMinimumAbbreviation(p_LangId);
if (isnumber(tmp_abbrev)) {
min_abbrev = tmp_abbrev;
}
tmp_abbrev = LanguageSettings.getAutoCompleteMinimumLength(p_LangId);
if (isnumber(tmp_abbrev) && tmp_abbrev < min_abbrev) {
min_abbrev = tmp_abbrev;
}
}
// is the word under the cursor shorter than the min_abbrev setting?
if (length(orig_word) < min_abbrev) {
return STRING_NOT_FOUND_RC;
}
// find matches among the space words
aliasfilename := "";
// if syntax expansion is turned off, we need to turn it on for just a second
synExpOff := !LanguageSettings.getSyntaxExpansion(p_LangId);
if (synExpOff) LanguageSettings.setSyntaxExpansion(p_LangId, true);
word := min_abbrev2(orig_word,space_words,"",aliasfilename,false,true);
// turn it back off now
if (synExpOff) LanguageSettings.setSyntaxExpansion(p_LangId, false);
if (aliasfilename==null || (word=="" && min_abbrev>0)) {
return STRING_NOT_FOUND_RC;
}
// get picture indexes for _f_code.svg
if (!_pic_syntax) {
_pic_syntax = load_picture(-1,"_f_code.svg");
if (_pic_syntax >= 0) {
set_name_info(_pic_syntax, "Syntax expansion");
}
}
// add each match
_str all_matches = word;
while (all_matches != "") {
// get the match word
parse all_matches with word ";" all_matches;
// get the syntax expansion information for this word
SYNTAX_EXPANSION_INFO sei;
sei.statement="";
if (space_words._varformat()==VF_HASHTAB && space_words._indexin(lowcase(word))) {
sei = space_words:[lowcase(word)];
}
// skip this word if it is an exact match and syntax
// expansion does nothing special with it
if (word==orig_word && space_words._varformat()==VF_HASHTAB) {
if (sei.statement=="" || sei.statement == orig_word) {
continue;
}
}
// get the comment for this word
_str insertWord = word;
bigComment := "Syntax expansion for ":+word;
if (sei.statement != "") {
word = sei.statement;
bigComment = word:+"
":+AutoCompleteParagraphTag():+bigComment;
}
// if the language is case in-sensitive, try to auto-case the keywords
word = AutoCompleteKeywordCase(word, orig_word);
insertWord = AutoCompleteKeywordCase(insertWord, orig_word);
// add it to the list of results
AutoCompleteAddResult(words,
AUTO_COMPLETE_SYNTAX_PRIORITY,
word,
_autocomplete_space,
bigComment,
null,
p_EmbeddedCaseSensitive,
_pic_syntax,
insertWord);
}
// success
return 0;
}
/**
* Get the list of keywords that could be automatically completed
* based on the current identifier prefix.
*
* @param options auto completion options
* @param forceUpdate ignore min-abbrev settings
*
* @return 0 on success, <0 on error.
*/
static int AutoCompleteGetKeywords(int options, bool forceUpdate=false)
{
// get the correct lexer name
lexer_name := p_EmbeddedLexerName;
if (lexer_name=="") lexer_name = p_lexer_name;
// Do we even have a lexer for this mode?
if (lexer_name == "") {
return STRING_NOT_FOUND_RC;
}
// Are we in a comment or a string, then bail out
color := _clex_find(0,'G');
if (color == CFG_STRING || color == CFG_COMMENT) {
return STRING_NOT_FOUND_RC;
}
// cope with case-sensitivity issues
prefix := gAutoCompleteResults.prefix;
case_sensitive := p_EmbeddedCaseSensitive;
if (!case_sensitive) {
prefix=upcase(prefix);
}
// special case for preprocessing keywords
preprocessingPrefix := "";
if ((gAutoCompleteResults.idexp_info != null) &&
(gAutoCompleteResults.idexp_info.info_flags & VSAUTOCODEINFO_IN_PREPROCESSING)) {
if (gAutoCompleteResults.idexp_info.info_flags & VSAUTOCODEINFO_IN_PREPROCESSING_ARGS) {
return STRING_NOT_FOUND_RC;
}
preprocessingPrefix = gAutoCompleteResults.idexp_info.prefixexp;
prefix = preprocessingPrefix :+ prefix;
}
// did they give us a min_abbrev argument
// is the word under the cursor shorter than the min_abbrev setting?
if (!forceUpdate && !_LanguageInheritsFrom("xml") && !_LanguageInheritsFrom("html") && !_LanguageInheritsFrom("dtd")) {
min_abbrev := 0;
tmp_abbrev := LanguageSettings.getMinimumAbbreviation(p_LangId);
if (isnumber(tmp_abbrev)) {
min_abbrev = tmp_abbrev;
}
tmp_abbrev = LanguageSettings.getAutoCompleteMinimumLength(p_LangId);
if (isnumber(tmp_abbrev) && tmp_abbrev < min_abbrev) {
min_abbrev = tmp_abbrev;
}
if (length(prefix) < min_abbrev) {
return STRING_NOT_FOUND_RC;
}
}
// get the color coding profile for this language
handle := _plugin_get_profile(VSCFGPACKAGE_COLORCODING_PROFILES,lexer_name);
if (handle<0) {
return FILE_NOT_FOUND_RC;
}
// create a hash table of words we have already seen
// (these are words suggested by syntax expansion)
bool syntax_words:[];
n := gAutoCompleteResults.words._length();
for (i:=0; i
" :+ comment_info;
}
// restore the window ID and upate the comments
p_window_id = orig_wid;
gAutoCompleteResults.words[word_index].comments = comments;
gAutoCompleteResults.words[word_index].comment_flags = comment_flags|VSCODEHELP_COMMENTFLAG_HTML;;
}
/**
* Generate a suggestion for what could be completed by the "complete-list" command.
*
* @param options auto completion options
*
* @return 0 on success, <0 on error.
*/
static int AutoCompleteGetWordCompletions(int options, _str prefixexp="", bool forceUpdate=false)
{
find_complete_list_completions(gAutoCompleteResults.words, def_auto_complete_max_words, prefixexp, forceUpdate);
return 0;
}
/**
* Generate suggestions for extension-specific completions.
* In particular, generate suggestions for command arguments in
* the process buffer.
*
* @param options auto completion options
*
* @return 0 on success, <0 on error.
*/
static void AutoCompleteGetLanguageSpecificArguments(int options, bool forceUpdate=false)
{
index := _FindLanguageCallbackIndex("_%s_autocomplete_get_arguments");
if (index) {
call_index(gAutoCompleteResults.words, forceUpdate, index);
}
}
/**
* Generate suggestions for what could be expanded using
* argument completion for the current object.
*/
static int AutoCompleteGetArguments(int options)
{
callback_name := "";
typeless completion_flags=0;
parse p_completion with callback_name ":" completion_flags;
if (callback_name == NONE_ARG) {
return STRING_NOT_FOUND_RC;
}
index := find_index(callback_name:+"_match",PROC_TYPE|COMMAND_TYPE);
if (!index || !index_callable(index)) {
return STRING_NOT_FOUND_RC;
}
find_first := true;
for (;; find_first=false) {
_str result = call_index(p_text, find_first, index);
if (result == "") {
break;
}
if (p_completion==TAG_ARG) {
tag_decompose_tag_browse_info(result, auto cm);
AutoCompleteAddResult(gAutoCompleteResults.words,
AUTO_COMPLETE_ARGUMENT_PRIORITY,
cm.member_name,
null,
"",
cm);
} else {
AutoCompleteAddResult(gAutoCompleteResults.words,
AUTO_COMPLETE_ARGUMENT_PRIORITY,
result,
null,
"Argument "result);
}
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////
/**
* Is auto completion active?
*/
bool AutoCompleteActive()
{
return gAutoCompleteResults.editor != 0 && gAutoCompleteResults.isActive;
}
/**
* Should we restart auto-list-parameters (after use typed space, for example)?
*/
bool AutoCompleteDoRestartListParameters()
{
if (AutoCompleteActive()) return false;
if (gAutoCompleteResults.wasListParameters) {
return gAutoCompleteResults.maybeRestartListParametersAfterKey;
}
return false;
}
/**
* @return Return the window ID of the editor control
* displaying auto completion results right now.
*/
static int AutoCompleteGetEditorWid()
{
int wid = gAutoCompleteResults.editor;
if (wid > 0 && _iswindow_valid(wid) && wid._isEditorCtl()) {
return wid;
}
return 0;
}
/**
* @return Return the window ID of the tree being
* displaying the list of auto complete results.
*/
static int AutoCompleteGetTreeWid()
{
_nocheck _control ctltree;
int wid = gAutoCompleteResults.listForm;
if (wid > 0 && _iswindow_valid(wid) && wid._find_control("ctltree")) {
return wid.ctltree;
}
return 0;
}
/**
* @return Return the window ID of the comment form
* displaying the comments for the selected completion.
*/
static int AutoCompleteGetCommentsWid()
{
int wid = gAutoCompleteResults.commentForm;
if (wid > 0 && _iswindow_valid(wid)) {
return wid;
}
return 0;
}
/**
* Break out of auto completion mode.
*/
void AutoCompleteTerminate()
{
// clear out auto-complete results
gAutoCompleteResults.idexp_info = null;
gAutoCompleteResults.expected_rt = null;
gAutoCompleteResults.expected_type="";
gAutoCompleteResults.expected_name="";
gAutoCompleteResults.visited._makeempty();
gAutoCompleteResults.words._makeempty();
gAutoCompleteResults.origLineContent=null;
gAutoCompleteResults.removeLen=0;
gAutoCompleteResults.allowInsertLongest=true;
gAutoCompleteResults.allowShowInfo=false;
gAutoCompleteResults.allowImplicitReplace=false;
gAutoCompleteResults.wordIndex = -1;
gAutoCompleteResults.wasForced = false;
gAutoCompleteResults.wasListSymbols = false;
gAutoCompleteResults.ignoreSwitchBufAndModuleLoadEvents=false;
// make sure that auto complete is actually active
orig_wid := p_window_id;
editorctl_wid := AutoCompleteGetEditorWid();
if (!_iswindow_valid(editorctl_wid)) {
return;
}
p_window_id = editorctl_wid;
// first kill the pesky timer
AutoCompleteKillTimer();
// uncomment this to debug when auto-complete fails
//_UpdateSlickCStack();
if (gAutoCompleteResults.idexp_info != null) {
say("AutoCompleteTerminate H"__LINE__": AUTO COMPLETE TERMINATED ******");
_StackDump();
_dump_var(gAutoCompleteResults);
say("AutoCompleteTerminate H"__LINE__": I WILL BE BACK. ***************");
}
// remove the light bulb picture
doRefresh := false;
if (gAutoCompleteResults.streamMarkerIndex >= 0) {
_StreamMarkerRemove(gAutoCompleteResults.streamMarkerIndex);
gAutoCompleteResults.streamMarkerIndex=-1;
doRefresh = true;
}
// remove the word completion
_bbhelp('C');
// remove the comment form
comment_wid := AutoCompleteGetCommentsWid();
if (comment_wid != 0) {
comment_wid._delete_window();
doRefresh=true;
}
gAutoCompleteResults.commentForm = 0;
// remove the list form
list_wid := AutoCompleteGetTreeWid();
if (list_wid != 0) {
gAutoCompleteResults.listForm._delete_window();
doRefresh=true;
}
gAutoCompleteResults.listForm = 0;
// remove the event table from the editor control
_RemoveEventtab(defeventtab auto_complete_key_overrides);
gAutoCompleteResults.editor = 0;
gAutoCompleteResults.isActive = false;
// reset the position information
gAutoCompleteResults.lineoffset = 0;
gAutoCompleteResults.column = 0;
gAutoCompleteResults.buffer = 0;
gAutoCompleteResults.lastModified = 0;
// no current item
gAutoCompleteResults.wordIndex = -1;
// force refresh if necessary
if (doRefresh) {
refresh();
}
// restore the original window, if still valid
if (_iswindow_valid(orig_wid)) {
p_window_id = orig_wid;
}
}
// Is the prefix in the list
static bool AutoCompletePrefixValid(_str prefix)
{
// not in list help?
if (AutoCompleteGetEditorWid() != 0) {
return false;
}
if (_last_char(prefix)=="(") {
return false;
}
int i;
count := gAutoCompleteResults.words._length();
for (i=0;iAutoCompleteDoKey()
* and the autocomplete
command.
*
* @param alwaysUpdate Update now, do not wait for idle timer
* @param forceUpdate Force update, independent of auto complete options
*
* @return 0 on success, <0 on error.
*
* @see autocomplete
*
* @appliesTo Edit_Window, Editor_Control, Text_Box, Combo_Box
* @categories Completion_Functions, Combo_Box_Methods, Edit_Window_Methods, Editor_Control_Methods, Text_Box_Methods
*/
int AutoCompleteUpdateInfo(bool alwaysUpdate=false,
bool forceUpdate=false,
bool doInsertLongest=false,
bool operatorTyped=false,
bool prefixMatch=true,
VS_TAG_IDEXP_INFO idexp_info=null,
_str expected_type=null,
VS_TAG_RETURN_TYPE expected_rt=null,
_str expected_name=null,
bool selectMatchingItem=false,
bool doListParameters=false,
_str (&errorArgs)[]=null
)
{
// turn off auto-copmlete if we have mulitple cursors
if (_MultiCursorAlreadyLooping()) {
//say("AutoCompleteUpdateInfo: MULTIPLE CURSORS");
return 0;
}
// have we waited long enough yet?
errorArgs._makeempty();
wasActive := AutoCompleteActive();
idle := (wasActive? def_auto_complete_update_idle_time:def_auto_complete_idle_time);
elapsed := _idle_time_elapsed();
if (!alwaysUpdate && !forceUpdate && elapsed < idle) {
//say("AutoCompleteUpdateInfo: NOT ENOUGH IDLE TIME ELAPSED");
return 0;
}
// switch to the editor that has focus
focus_wid := _get_focus();
// has it been too long (15 seconds) since the last character was typed ?
if (!wasActive && !alwaysUpdate && !forceUpdate &&
elapsed > def_auto_complete_timeout_forced) {
//say("AutoCompleteUpdateInfo: TOO LATE - DEADLINE EXPIRED");
return 0;
}
// make sure the focus wid is valid
if (!_iswindow_valid(focus_wid)) {
AutoCompleteTerminate();
say("AutoCompleteUpdateInfo: FOCUS WINDOWS NOT VALID");
return INVALID_OBJECT_HANDLE_RC;
}
// If one of the auto complete dialogs has obtained focus,
// just parlay that focus to the editor control
if (wasActive) {
if (focus_wid == gAutoCompleteResults.listForm ||
focus_wid.p_active_form == gAutoCompleteResults.listForm ||
focus_wid == AutoCompleteGetTreeWid() ||
focus_wid == gAutoCompleteResults.commentForm ||
focus_wid.p_active_form == gAutoCompleteResults.commentForm) {
focus_wid = gAutoCompleteResults.editor;
}
}
// make sure the focus wid is an editor control
if (!focus_wid._isEditorCtl()) {
AutoCompleteTerminate();
say("AutoCompleteUpdateInfo: FOCUS WINDOW NOT AN EDITOR CONTROL");
return INVALID_OBJECT_HANDLE_RC;
}
// no auto-complete in hex mode
if (focus_wid.p_hex_mode == HM_HEX_ON) {
AutoCompleteTerminate();
say("AutoCompleteUpdateInfo: HEX MODE");
return VSRC_CFG_HEX_MODE_COLOR;
}
// ok, now switch to the designated editor control
orig_wid := p_window_id;
p_window_id = focus_wid;
// if the context is not yet up-to-date, then don't update yet
// unless we are forcing update, then update it immediately
if (!alwaysUpdate && !forceUpdate && !wasActive && !_ContextIsUpToDate(elapsed, MODIFYFLAG_CONTEXT_UPDATED|MODIFYFLAG_TOKENLIST_UPDATED)) {
p_window_id = orig_wid;
//say("AutoCompleteUpdateInfo: CONTEXT NOT YET UP-TO-DATE");
return 0;
}
// is there a selection active
wasForced := (wasActive && gAutoCompleteResults.wasForced);
if (!forceUpdate && !wasForced && select_active()) {
AutoCompleteTerminate();
p_window_id = orig_wid;
say("AutoCompleteUpdateInfo: SELECTION CURRENTLY ACTIVE");
return 0;
}
// is word completion active?
if (!forceUpdate && CompleteWordActive()) {
AutoCompleteTerminate();
p_window_id = orig_wid;
say("AutoCompleteUpdateInfo: COMPLETE WORD ACTIVE");
return COMMAND_CANCELLED_RC;
}
// is auto completion completely disabled?
auto_complete_options := AutoCompleteGetOptions();
if (!forceUpdate && !(auto_complete_options & AUTO_COMPLETE_ENABLE) && !wasActive) {
if ((!operatorTyped && !doListParameters) || !(_GetCodehelpFlags() & VSCODEHELPFLAG_AUTO_LIST_MEMBERS)) {
AutoCompleteTerminate();
p_window_id = orig_wid;
say("AutoCompleteUpdateInfo: AUTO COMPLETE DISABLED");
return VSRC_COMMAND_IS_DISABLED_FOR_THIS_OBJECT_OR_STATE;
}
}
// disable auto-complete options that do not apply if we don't have context tagging
if (!_haveContextTagging()) {
auto_complete_options &= ~AUTO_COMPLETE_MEMBERS;
auto_complete_options &= ~AUTO_COMPLETE_SHOW_COMMENTS;
auto_complete_options &= ~AUTO_COMPLETE_EXTENSION_ARGS;
auto_complete_options &= ~AUTO_COMPLETE_LANGUAGE_ARGS;
auto_complete_options &= ~AUTO_COMPLETE_ARGUMENTS;
auto_complete_options &= ~AUTO_COMPLETE_SHOW_PROTOTYPES;
}
// has the cursor moved, then blow away the previous suggestion
origForceUpdate := forceUpdate;
needsUpdate := forceUpdate;
useQuickPrefix := false;
getQuickPrefix(useQuickPrefix, needsUpdate);
if (!wasActive && expected_type != null && expected_type != "") forceUpdate = true;
// still up to date with modify flags?
//say("h2 force="forceUpdate" update="(p_ModifyFlags & MODIFYFLAG_AUTO_COMPLETE_UPDATED));
if (!forceUpdate && !needsUpdate && ((p_ModifyFlags & MODIFYFLAG_AUTO_COMPLETE_UPDATED))) {
p_window_id = orig_wid;
//say("AutoCompleteUpdateInfo: ALREADY UP-TO-DATE OR INACTIVE");
return 0;
}
// The buffer hasn't changed, yet p_Modified was reset.
// A tag file update must have reset p_ModifyFlags.
if (!forceUpdate && !needsUpdate &&
(gAutoCompleteResults.lastModified == p_LastModified) &&
(!(p_ModifyFlags & MODIFYFLAG_AUTO_COMPLETE_UPDATED))) {
operatorTyped = gAutoCompleteResults.operatorTyped;
idexp_info = gAutoCompleteResults.idexp_info;
expected_type = gAutoCompleteResults.expected_type;
expected_rt = gAutoCompleteResults.expected_rt;
expected_name = gAutoCompleteResults.expected_name;
doListParameters = gAutoCompleteResults.wasListParameters;
useQuickPrefix = false;
needsUpdate = true;
}
// has the buffer been modified?
if (!forceUpdate && !needsUpdate && !p_modify) {
p_window_id = orig_wid;
//say("AutoCompleteUpdateInfo: BUFFER IS UNCHANGED");
return 0;
}
// Determine which timeout amount to use.
// Give it more time if it was forced by typing a keystroke.
timeout_time := def_auto_complete_timeout_time;
if (alwaysUpdate && forceUpdate && !operatorTyped) {
timeout_time = def_auto_complete_timeout_forced;
}
// check if the tag database is busy and we can't get a lock.
se.tags.TaggingGuard sentry;
status := 0;
dbName := _GetWorkspaceTagsFilename();
if (dbName != "" && _haveContextTagging()) {
haveDBLock := tag_trylock_db(dbName);
if (!forceUpdate && !haveDBLock) {
p_window_id = orig_wid;
//say("AutoCompleteUpdateInfo: COULD NOT GET DATABASE LOCK");
return TAGGING_TIMEOUT_RC;
}
// replace the trylock with a guard to handle all function return paths
status = sentry.lockDatabase(dbName, timeout_time);
if (haveDBLock) {
tag_unlock_db(dbName);
}
if (status < 0) {
p_window_id = orig_wid;
//say("AutoCompleteUpdateInfo: DATABASE LOCK TIMED OUT");
return status;
}
}
// update modify flags and positional information
p_ModifyFlags |= MODIFYFLAG_AUTO_COMPLETE_UPDATED;
// Verify that we are at the end of an identifier
if (!origForceUpdate && p_col==1) {
AutoCompleteTerminate();
p_window_id = orig_wid;
say("AutoCompleteUpdateInfo: END OF IDENTIFIER");
return 0;
}
// Need to add _extra_word_chars for fundamental mode and
// when in comments or strings in order to support auto completion
// on text words.
// Here I add the _extra_word_chars for all cases but if this causes
// a problem we should change this.
//
ch := get_text();
word_chars := _clex_identifier_chars():+_extra_word_chars;
if (!forceUpdate &&
!gAutoCompleteResults.wasForced &&
!gAutoCompleteResults.wasListSymbols && idexp_info==null &&
pos('^['word_chars']$',ch,1,'re') > 0) {
AutoCompleteTerminate();
p_window_id = orig_wid;
say("AutoCompleteUpdateInfo: NOT ON AN ID CHAR");
return VSCODEHELPRC_CONTEXT_NOT_VALID;
}
// add file separate character for directory lookups
extra_chars := "";
if (_LanguageInheritsFrom("bat") ||
_LanguageInheritsFrom("process") ||
_LanguageInheritsFrom("bourneshell") ||
_LanguageInheritsFrom("csh") ||
_LanguageInheritsFrom("ini") ||
_LanguageInheritsFrom("mak") ||
_LanguageInheritsFrom("powerwhell")) {
extra_chars = _escape_re_chars(FILESEP);
if (_isWindows()) {
extra_chars :+= _escape_re_chars(FILESEP2);
extra_chars :+= _escape_re_chars(":");
}
}
// check that the last key press event was an identifier character
// Also allow ON_KEYSTATECHANGE because they may have just released SHIFT
wasForced = (wasActive && gAutoCompleteResults.wasForced);
if (!forceUpdate && !operatorTyped) {
// There has been an edit and a key has been pressed.
lev := event2name(last_event(null,true));
// Active, Terminate the list if the prefix changes
// non-active, Start the list if a valid key is pressed.
if (length(lev) == 1 && !pos('^['extra_chars:+word_chars']$', lev, 1, 'r') &&
// We definitely don't want the list to close when pressing ,upcase-word, etc.
!useQuickPrefix &&
!(lev=='"' && wasActive && get_text()=='"') &&
!(last_event(null,true)==BACKSPACE && wasActive) &&
!(last_event(null,true)==TAB && wasActive)
) {
//say("lev="lev" index="event2index(last_event(null,true)));
AutoCompleteTerminate();
say("AutoCompleteUpdateInfo: LAST KEY PRESS WAS NOT ID CHAR");
return 0;
}
}
status=0;
if (!wasActive) {
gAutoCompleteResults.operatorTyped=operatorTyped;
gAutoCompleteResults.wasListParameters=doListParameters;
gAutoCompleteResults.doShowCategories=false;
gAutoCompleteResults.wasForced=forceUpdate;
gAutoCompleteResults.expected_type=expected_type;
gAutoCompleteResults.expected_rt=expected_rt;
gAutoCompleteResults.expected_name=expected_name;
gAutoCompleteResults.visited._makeempty();
gAutoCompleteResults.wasListSymbols = (idexp_info != null);
} else if ((expected_type != null && expected_type != "") || expected_rt != null) {
gAutoCompleteResults.expected_type=expected_type;
gAutoCompleteResults.expected_rt=expected_rt;
gAutoCompleteResults.expected_name=expected_name;
gAutoCompleteResults.wasListSymbols = true;
forceUpdate = (forceUpdate || gAutoCompleteResults.wasForced);
} else {
expected_type = gAutoCompleteResults.expected_type;
expected_rt = gAutoCompleteResults.expected_rt;
expected_name = gAutoCompleteResults.expected_name;
forceUpdate = (forceUpdate || gAutoCompleteResults.wasForced);
}
gAutoCompleteResults.lineoffset = point();
gAutoCompleteResults.column = p_col;
gAutoCompleteResults.buffer = p_buf_id;
gAutoCompleteResults.lastModified = p_LastModified;
if (!wasActive || gAutoCompleteResults.editor != p_window_id) {
gAutoCompleteResults.editor = p_window_id;
gAutoCompleteResults.editor._AddEventtab(defeventtab auto_complete_key_overrides);
}
// temporarily disable auto-complete switch-buf and module load hooks
// they will be re-enabled by AutoCompleteTerminate(), or immedately after
// this function finishes gathering results.
gAutoCompleteResults.ignoreSwitchBufAndModuleLoadEvents = true;
// modify flags
FAKEOUT_MODIFY_FLAGS := (MODIFYFLAG_CONTEXT_UPDATED|MODIFYFLAG_LOCALS_UPDATED|MODIFYFLAG_STATEMENTS_UPDATED);
// enable symbols if they forced update
symbolsStatus := 0;
isListSymbols := gAutoCompleteResults.wasListSymbols;
origIdExpression := gAutoCompleteResults.idexp_info;
if (isListSymbols) {
auto_complete_options |= AUTO_COMPLETE_SYMBOLS;
}
if (useQuickPrefix) {
gAutoCompleteResults.idexp_info.lastid=gAutoCompleteResults.prefix;
// Forget about a minimum since the list is already up. Can't do this
// if a call back is defined!!!
//if ((!origForceUpdate && length(complete_arg)
*
* AUTO_COMPLETE_*
.
* def_auto_complete_options
as the default.
*
* @param lang language ID -- see {@link p_LangId}
*
* @return bitset of AUTO_COMPLETE_* options.
*
* @appliesTo Edit_Window, Editor_Control, Text_Box, Combo_Box
* @categories Completion_Functions
*/
AutoCompleteFlags AutoCompleteGetOptions(_str lang="")
{
if (lang=="") {
editorctl_wid := AutoCompleteGetEditorWid();
if (_iswindow_valid(editorctl_wid) && editorctl_wid._isEditorCtl()) {
lang = editorctl_wid.p_LangId;
} else if (_isEditorCtl()) {
lang = p_LangId;
} else {
return def_auto_complete_options;
}
}
return (AutoCompleteFlags)LanguageSettings.getAutoCompleteOptions(lang, def_auto_complete_options);
}
static AutoCompleteFlags AutoCompleteGetOptionsMaybeShowCategories(_str lang="")
{
auto_complete_options := AutoCompleteGetOptions(lang);
if ( gAutoCompleteResults.doShowCategories ) {
auto_complete_options |= AUTO_COMPLETE_SHOW_CATEGORIES;
}
return auto_complete_options;
}
/**
* Set the auto complete options wich are a bitset of
* AUTO_COMPLETE_*
.
* def_autocomplete_[ext]
.
*
* @param lang language ID -- see {@link p_LangId}
* @param flags bitset of AUTO_COMPLETE_* options
*
* @appliesTo Edit_Window, Editor_Control, Text_Box, Combo_Box
* @categories Completion_Functions
*/
void AutoCompleteSetOptions(_str lang, int flags)
{
LanguageSettings.setAutoCompleteOptions(lang, flags);
}
/**
* Get the minimum length identifier prefix that will
* allow auto complete to be triggered. This is closely
* related to the mimimum expandable keyword length common
* to most language specific syntax expansion options.
* def_auto_complete_minimum_length
as the default.
*
* @param lang language ID -- see {@link p_LangId}
*
* @return minimum length setting
*
* @appliesTo Edit_Window, Editor_Control, Text_Box, Combo_Box
* @categories Completion_Functions
*/
int AutoCompleteGetMinimumLength(_str lang)
{
if (lang=="" && _isEditorCtl()) {
lang = p_LangId;
}
return LanguageSettings.getAutoCompleteMinimumLength(lang);
}
/**
* Get the minimum length identifier prefix that will
* allow auto complete to be triggered. This is closely
* related to the mimimum expandable keyword length common
* to most language specific syntax expansion options.
* def_auto_complete_minimum_length
as the default.
*
* @param lang language ID -- see {@link p_LangId}
* @param min minimum prefix length
*
* @appliesTo Edit_Window, Editor_Control, Text_Box, Combo_Box
* @categories Completion_Functions
*/
void AutoCompleteSetMinimumLength(_str lang, int min)
{
LanguageSettings.setAutoCompleteMinimumLength(lang, min);
}
///////////////////////////////////////////////////////////////////////////////
// returns 0 if there were not overloaded tags to cycle through
// otherwise, return "inc" on success.
static int AutoCompleteNextComment(int inc)
{
VSAUTOCODE_ARG_INFO (*plist)[];
_nocheck _control ctlminihtml1;
form_wid := gAutoCompleteResults.commentForm;
if (!_iswindow_valid(form_wid)) {
return 0;
}
prefer_left_placement := false;
list_wid := gAutoCompleteResults.listForm;
if (_iswindow_valid(list_wid) && form_wid.p_x < list_wid.p_x) {
prefer_left_placement = true;
}
TagIndex := 0;
HYPERTEXTSTACK stack = form_wid.ctlminihtml1.p_user;
if (stack.HyperTextTop>=0) {
TagIndex=stack.s[stack.HyperTextTop].TagIndex;
plist= &stack.s[stack.HyperTextTop].TagList;
}
int orig_TagIndex=TagIndex;
TagIndex+=inc;
if (TagIndex<0 && plist->_length() > 0) {
TagIndex=plist->_length()-1;
} else if (TagIndex>=plist->_length()) {
TagIndex=0;
}
if (TagIndex!=orig_TagIndex) {
if (stack.HyperTextTop>=0) {
stack.s[stack.HyperTextTop].TagIndex=TagIndex;
form_wid.ctlminihtml1.p_user=stack;
} else {
//gFunctionHelpTagIndex=TagIndex;
}
form_wid.ShowCommentHelp(false, true,
form_wid,
AutoCompleteGetEditorWid());
}
return 0;
}
/**
* Callback for handling keypresses during auto complete mode.
* This function passes through to the default key mappings
* for handling one key press.
* gAutoCompleteResults.editor
is recycled, we may
* add the event tab back to the wrong window.
*
* @param key key press
* @param doTerminate force auto complete to terminate
*/
static void AutoCompleteDefaultKey(_str key,bool doTerminate,bool called_from_AutoCompleteDoKey=true)
{
last_index(prev_index("",'C'),'C');
_RemoveEventtab(defeventtab auto_complete_key_overrides);
if (doTerminate) {
AutoCompleteTerminate();
if (called_from_AutoCompleteDoKey && _macro('KI')) _macro('KK',key);
maybe_dokey(key);
} else {
editorctl_wid := p_window_id;
if (called_from_AutoCompleteDoKey && _macro('KI')) _macro('KK',key);
maybe_dokey(key);
if (_iswindow_valid(editorctl_wid) && AutoCompleteActive()) {
editorctl_wid._AddEventtab(defeventtab auto_complete_key_overrides);
}
}
}
static int _UTF8CharRead2OrMore(int i,_str &str,int &ch32) {
int first = _asc(substr(str,i,1));
if ((first & 0xe0) == 0xc0) { //110, 2 byte encoding
if (i + 2 -1 > length(str)) {
ch32 = -1;
return 1;
}
// 5 bits of first byte and 6 bits from second byte
ch32 = (((first & 0x1f)) << 6) | (_asc(substr(str,i+1,1)) & 0x3f);
return 2;
}
if ((first & 0xf0) == 0xe0) { //1110, 3 byte encoding
if (i + 3 -1 > length(str)) {
ch32 = -1;
return 1;
}
// 4 bits of first byte, 6 bits from second,6 bits from third
ch32 = (((first & 0xf)) << 12) |
(((_asc(substr(str,i+1,1)) & 0x3f)) << 6) |
(((_asc(substr(str,i+2,1)) & 0x3f)));
return 3;
}
if ((first & 0xf8) == 0xf0) { //11110, 4 byte encoding
if (i + 4 -1 > length(str)) {
ch32 = -1;
return 1;
}
// 4 bits of first byte, 6 bits from second-forth
ch32 = (((first & 0x7)) << 18) |
(((_asc(substr(str,i+1,1)) & 0x3f)) << 12) |
(((_asc(substr(str,i+2,1)) & 0x3f)) << 6) |
(((_asc(substr(str,i+3,1)) & 0x3f)));
return 4;
}
if ((first & 0xfc) == 0xf8) { //111110, 5 byte encoding
if (i + 5 -1 > length(str)) {
ch32 = -1;
return 1;
}
// 4 bits of first byte, 6 bits from second-fifth
ch32 = (((first & 0x3)) << 24) |
(((_asc(substr(str,i+1,1)) & 0x3f)) << 18) |
(((_asc(substr(str,i+2,1)) & 0x3f)) << 12) |
(((_asc(substr(str,i+3,1)) & 0x3f)) << 6) |
(((_asc(substr(str,i+4,1)) & 0x3f)));
return 5;
}
if ((first & 0xfe) == 0xfc) { //1111110, 6 byte encoding
if (i + 6 -1 > length(str)) {
ch32 = -1;
return 1;
}
// 4 bits of first byte, 6 bits from second-fifth
ch32 = (((first & 0x1)) << 30) |
(((_asc(substr(str,i+1,1)) & 0x3f)) << 24) |
(((_asc(substr(str,i+2,1)) & 0x3f)) << 18) |
(((_asc(substr(str,i+3,1)) & 0x3f)) << 12) |
(((_asc(substr(str,i+4,1)) & 0x3f)) << 6) |
(((_asc(substr(str,i+5,1)) & 0x3f)));
return 6;
}
ch32 = -1;
return 1;
}
int _UTF8CountChars(_str str) {
i := 1;
count := 0;
for (;i<=length(str);++count) {
ch := substr(str,i,1);
if (_asc(ch)>=128) {
auto len=_UTF8CharRead2OrMore(i,str,auto ch32);
i+=len;
} else {
++i;
}
}
return count;
}
void _kbdmacro_add_string(_str str) {
i := 1;
for (;i<=length(str);) {
ch := substr(str,i,1);
if (_asc(ch)>=128) {
auto len=_UTF8CharRead2OrMore(i,str,auto ch32);
_macro('KK',_UTF8Chr(ch32));
i+=len;
} else {
_macro('KK',ch);
++i;
}
}
}
static void AutoCompleteReplaceWordForEachMultiCursor(
void (*pfnReplaceWord)(_str insertWord,_str prefix,int &removeStartCol,int &removeLen,bool onlyInsertWord,struct VS_TAG_BROWSE_INFO symbol),
_str prefix,
_str insertWord,
bool onlyInsertWord,
struct VS_TAG_BROWSE_INFO symbol
)
{
target_wid := p_window_id;
//activate_window(target_wid);
already_looping := _MultiCursorAlreadyLooping();
multicursor := !already_looping && _MultiCursor();
if (_QReadOnly()) {
_readonly_error(0);
return;
}
if (gAutoCompleteResults.origLineContent!=null) {
replace_line(gAutoCompleteResults.origLineContent);
p_col=gAutoCompleteResults.origCol;
prefix=gAutoCompleteResults.origPrefix;
gAutoCompleteResults.removeLen=0;
gAutoCompleteResults.origLineContent=null;
}
for (ff:=1;;ff=0) {
if (_MultiCursor()) {
if(!_MultiCursorNext(ff)) {
break;
}
}
AutoCompleteReplaceWord(pfnReplaceWord, prefix, insertWord, onlyInsertWord, symbol);
if (!multicursor) {
if (!already_looping) _MultiCursorLoopDone();
break;
}
if (target_wid!=p_window_id) {
_MultiCursorLoopDone();
break;
}
}
activate_window(target_wid);
}
static void AutoCompleteReplaceWord(
void (*pfnReplaceWord)(_str insertWord,_str prefix,int &removeStartCol,int &removeLen,bool onlyInsertWord,struct VS_TAG_BROWSE_INFO symbol),
_str prefix,
_str insertWord,
bool onlyInsertWord,
struct VS_TAG_BROWSE_INFO symbol
)
{
if (_QReadOnly()) {
_readonly_error(0);
return;
}
save_pos(auto cur_pos);
if (onlyInsertWord && p_undo_steps) {
//say((gAutoCompleteResults.replaceWordPos==cur_pos)" "p_undo_steps" "(gAutoCompleteResults.replaceWordLastModified==p_LastModified));
if (gAutoCompleteResults.replaceWordPos==cur_pos &&
gAutoCompleteResults.replaceWordLastModified==p_LastModified
) {
_undo();
//_str line;get_line(line);say("line="line);
} else {
// Save the old buffer pos information. Otherwise, undo will cause cursor to jump around
_next_buffer('hr');_prev_buffer('hr');
}
}
if (gAutoCompleteResults.origLineContent!=null) {
replace_line(gAutoCompleteResults.origLineContent);
p_col=gAutoCompleteResults.origCol;
prefix=gAutoCompleteResults.origPrefix;
gAutoCompleteResults.removeLen=0;
gAutoCompleteResults.origLineContent=null;
}
// calculate whether or not we should delete the part of the
// identifier to the right of the cursor
numExtraChars := (gAutoCompleteResults.end_col-p_col);
if (!(_GetCodehelpFlags() & VSCODEHELPFLAG_REPLACE_IDENTIFIER) ||
(numExtraChars <= 0) ||
((_GetCodehelpFlags() & VSCODEHELPFLAG_PRESERVE_IDENTIFIER) &&
!(gAutoCompleteResults.wasForced) &&
(gAutoCompleteResults.operatorTyped || gAutoCompleteResults.wasListParameters))) {
numExtraChars = 0;
}
utf8_extraChars := get_text(numExtraChars);
if (pfnReplaceWord!=null) {
if (numExtraChars > 0) _delete_text(numExtraChars);
gAutoCompleteResults.removeLen=0;
(*pfnReplaceWord)(insertWord,prefix,
gAutoCompleteResults.removeStartCol,
gAutoCompleteResults.removeLen,
onlyInsertWord, symbol);
} else {
p_col -= length(prefix);
_delete_text(length(prefix)+numExtraChars);
_insert_text(insertWord);
}
// for macro recording, just insert the rest of the word
_macro('m',_macro('s'));
if (gAutoCompleteResults.origLineContent!=null) {
_macro_append("replace_line("_quote(gAutoCompleteResults.origLineContent)");");
_macro_append("p_col="gAutoCompleteResults.origCol";");
}
if (_macro('KI')) {
// Remove keys for displaying or scrolling list
//_str string=_macro('KG'); // Fetch current recording
_str chars=prefix;
if (p_UTF8) {
chars=prefix;
} else {
chars=_MultiByteToUTF8(prefix);
}
int count=_UTF8CountChars(chars);
i := 0;
for (;i