Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
909 lines (776 sloc) 26.2 KB
// This file is part of Notepad++ project
// Copyright (C)2003 Don HO <don.h@free.fr>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// Note that the GPL places important restrictions on "derived works", yet
// it does not provide a detailed definition of that term. To avoid
// misunderstandings, we consider an application to constitute a
// "derivative work" for the purpose of this license if it does any of the
// following:
// 1. Integrates source code from Notepad++.
// 2. Integrates/includes/aggregates Notepad++ into a proprietary executable
// installer, such as those produced by InstallShield.
// 3. Links to a library or executes a program that does any of the above.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#include <algorithm>
#include <locale>
#include <shlwapi.h>
#include "AutoCompletion.h"
#include "Notepad_plus_msgs.h"
using namespace std;
static bool isInList(generic_string word, const vector<generic_string> & wordArray)
{
for (size_t i = 0, len = wordArray.size(); i < len; ++i)
if (wordArray[i] == word)
return true;
return false;
}
static bool isAllDigits(const generic_string &str)
{
for (const auto& i : str)
{
if (i < -i || i > 255)
return false;
bool isDigit = ::isdigit(int(i)) != 0;
if (!isDigit)
return false;
}
return true;
}
bool AutoCompletion::showApiComplete()
{
if (!_funcCompletionActive)
return false;
// calculate entered word's length
int curPos = int(_pEditView->execute(SCI_GETCURRENTPOS));
int startPos = int(_pEditView->execute(SCI_WORDSTARTPOSITION, curPos, true));
if (curPos == startPos)
return false;
size_t len = (curPos > startPos)?(curPos - startPos):(startPos - curPos);
if (len >= _keyWordMaxLen)
return false;
_pEditView->execute(SCI_AUTOCSETSEPARATOR, WPARAM(' '));
_pEditView->execute(SCI_AUTOCSETIGNORECASE, _ignoreCase);
_pEditView->showAutoComletion(curPos - startPos, _keyWords.c_str());
return true;
}
bool AutoCompletion::showApiAndWordComplete()
{
auto curPos = _pEditView->execute(SCI_GETCURRENTPOS);
auto startPos = _pEditView->execute(SCI_WORDSTARTPOSITION, curPos, true);
if (curPos == startPos)
return false;
const size_t bufSize = 256;
TCHAR beginChars[bufSize];
size_t len = (curPos > startPos)?(curPos - startPos):(startPos - curPos);
if (len >= bufSize)
return false;
// Get word array
vector<generic_string> wordArray;
_pEditView->getGenericText(beginChars, bufSize, startPos, curPos);
getWordArray(wordArray, beginChars);
bool canStop = false;
for (size_t i = 0, kwlen = _keyWordArray.size(); i < kwlen; ++i)
{
if (_keyWordArray[i].compare(0, len, beginChars) == 0)
{
if (!isInList(_keyWordArray[i], wordArray))
wordArray.push_back(_keyWordArray[i]);
canStop = true;
}
else if (canStop) {
// Early out since no more strings will match
break;
}
}
sort(wordArray.begin(), wordArray.end());
// Get word list
generic_string words;
for (size_t i = 0, wordArrayLen = wordArray.size(); i < wordArrayLen; ++i)
{
words += wordArray[i];
if (i != wordArrayLen - 1)
words += TEXT(" ");
}
_pEditView->execute(SCI_AUTOCSETSEPARATOR, WPARAM(' '));
_pEditView->execute(SCI_AUTOCSETIGNORECASE, _ignoreCase);
_pEditView->showAutoComletion(curPos - startPos, words.c_str());
return true;
}
void AutoCompletion::getWordArray(vector<generic_string> & wordArray, TCHAR *beginChars)
{
const size_t bufSize = 256;
const NppGUI & nppGUI = NppParameters::getInstance()->getNppGUI();
if (nppGUI._autocIgnoreNumbers && isAllDigits(beginChars))
return;
generic_string expr(TEXT("\\<"));
expr += beginChars;
expr += TEXT("[^ \\t\\n\\r.,;:\"(){}=<>'+!\\[\\]]+");
int docLength = int(_pEditView->execute(SCI_GETLENGTH));
int flags = SCFIND_WORDSTART | SCFIND_MATCHCASE | SCFIND_REGEXP | SCFIND_POSIX;
_pEditView->execute(SCI_SETSEARCHFLAGS, flags);
int posFind = _pEditView->searchInTarget(expr.c_str(), int(expr.length()), 0, docLength);
while (posFind != -1 && posFind != -2)
{
int wordStart = int(_pEditView->execute(SCI_GETTARGETSTART));
int wordEnd = int(_pEditView->execute(SCI_GETTARGETEND));
size_t foundTextLen = wordEnd - wordStart;
if (foundTextLen < bufSize)
{
TCHAR w[bufSize];
_pEditView->getGenericText(w, bufSize, wordStart, wordEnd);
if (!isInList(w, wordArray))
wordArray.push_back(w);
}
posFind = _pEditView->searchInTarget(expr.c_str(), static_cast<int32_t>(expr.length()), wordEnd, docLength);
}
}
static generic_string addTrailingSlash(generic_string path)
{
if (path.length() >=1 && path[path.length() - 1] == '\\')
return path;
else
return path + L"\\";
}
static generic_string removeTrailingSlash(generic_string path)
{
if (path.length() >= 1 && path[path.length() - 1] == '\\')
return path.substr(0, path.length() - 1);
else
return path;
}
static bool isDirectory(generic_string path)
{
DWORD type = ::GetFileAttributes(path.c_str());
return type != INVALID_FILE_ATTRIBUTES && (type & FILE_ATTRIBUTE_DIRECTORY);
}
static bool isFile(generic_string path)
{
DWORD type = ::GetFileAttributes(path.c_str());
return type != INVALID_FILE_ATTRIBUTES && ! (type & FILE_ATTRIBUTE_DIRECTORY);
}
static bool isAllowedBeforeDriveLetter(TCHAR c)
{
locale loc;
return c == '\'' || c == '"' || c == '(' || std::isspace(c, loc);
}
static bool getRawPath(generic_string input, generic_string &rawPath_out)
{
// Try to find a path in the given input.
// Algorithm: look for a colon. The colon must be preceded by an alphabetic character.
// The alphabetic character must, in turn, be preceded by nothing, or by whitespace, or by
// a quotation mark.
locale loc;
size_t lastOccurrence = input.rfind(L":");
if (lastOccurrence == std::string::npos) // No match.
return false;
else if (lastOccurrence == 0)
return false;
else if (!std::isalpha(input[lastOccurrence - 1], loc))
return false;
else if (lastOccurrence >= 2 && !isAllowedBeforeDriveLetter(input[lastOccurrence - 2]))
return false;
rawPath_out = input.substr(lastOccurrence - 1);
return true;
}
static bool getPathsForPathCompletion(generic_string input, generic_string &rawPath_out, generic_string &pathToMatch_out)
{
generic_string rawPath;
if (! getRawPath(input, rawPath))
{
return false;
}
else if (isFile(rawPath) || isFile(removeTrailingSlash(rawPath)))
{
return false;
}
else if (isDirectory(rawPath))
{
rawPath_out = rawPath;
pathToMatch_out = rawPath;
return true;
}
else
{
size_t last_occurrence = rawPath.rfind(L"\\");
if (last_occurrence == std::string::npos) // No match.
return false;
else
{
rawPath_out = rawPath;
pathToMatch_out = rawPath.substr(0, last_occurrence);
return true;
}
}
}
void AutoCompletion::showPathCompletion()
{
// Get current line (at most MAX_PATH characters "backwards" from current caret).
generic_string currentLine;
{
const size_t bufSize = MAX_PATH;
TCHAR buf[bufSize + 1];
const size_t currentPos = static_cast<size_t>(_pEditView->execute(SCI_GETCURRENTPOS));
const auto startPos = max(0, currentPos - bufSize);
_pEditView->getGenericText(buf, bufSize + 1, startPos, currentPos);
currentLine = buf;
}
/* Try to figure out which path the user wants us to complete.
We need to know the "raw path", which is what the user actually wrote.
But we also need to know which directory to look in (pathToMatch), which might
not be the same as what the user wrote. This happens when the user types an
incomplete name.
For instance: the user wants to autocomplete "C:\Wind", and assuming that no such directory
exists, this means we should list all files and directories in C:.
*/
generic_string rawPath, pathToMatch;
if (! getPathsForPathCompletion(currentLine, rawPath, pathToMatch))
return;
// Get all files and directories in the path.
generic_string autoCompleteEntries;
{
HANDLE hFind;
WIN32_FIND_DATA data;
generic_string pathToMatchPlusSlash = addTrailingSlash(pathToMatch);
generic_string searchString = pathToMatchPlusSlash + TEXT("*.*");
hFind = ::FindFirstFile(searchString.c_str(), &data);
if (hFind != INVALID_HANDLE_VALUE)
{
// Maximum number of entries to show. Without this it appears to the user like N++ hangs when autocompleting
// some really large directories (c:\windows\winxsys on my system for instance).
const unsigned int maxEntries = 2000;
unsigned int counter = 0;
do
{
if (++counter > maxEntries)
break;
if (generic_string(data.cFileName) == TEXT(".") || generic_string(data.cFileName) == TEXT(".."))
continue;
if (! autoCompleteEntries.empty())
autoCompleteEntries += TEXT("\n");
autoCompleteEntries += pathToMatchPlusSlash;
autoCompleteEntries += data.cFileName;
if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) // If directory, add trailing slash.
autoCompleteEntries += TEXT("\\");
} while (::FindNextFile(hFind, &data));
::FindClose(hFind);
}
else
return;
}
// Show autocompletion box.
_pEditView->execute(SCI_AUTOCSETSEPARATOR, WPARAM('\n'));
_pEditView->execute(SCI_AUTOCSETIGNORECASE, true);
_pEditView->showAutoComletion(rawPath.length(), autoCompleteEntries.c_str());
return;
}
bool AutoCompletion::showWordComplete(bool autoInsert)
{
int curPos = int(_pEditView->execute(SCI_GETCURRENTPOS));
int startPos = int(_pEditView->execute(SCI_WORDSTARTPOSITION, curPos, true));
if (curPos == startPos)
return false;
const size_t bufSize = 256;
TCHAR beginChars[bufSize];
size_t len = (curPos > startPos)?(curPos - startPos):(startPos - curPos);
if (len >= bufSize)
return false;
// Get word array
vector<generic_string> wordArray;
_pEditView->getGenericText(beginChars, bufSize, startPos, curPos);
getWordArray(wordArray, beginChars);
if (wordArray.size() == 0) return false;
if (wordArray.size() == 1 && autoInsert)
{
int replacedLength = _pEditView->replaceTargetRegExMode(wordArray[0].c_str(), startPos, curPos);
_pEditView->execute(SCI_GOTOPOS, startPos + replacedLength);
return true;
}
sort(wordArray.begin(), wordArray.end());
// Get word list
generic_string words(TEXT(""));
for (size_t i = 0, wordArrayLen = wordArray.size(); i < wordArrayLen; ++i)
{
words += wordArray[i];
if (i != wordArrayLen -1)
words += TEXT(" ");
}
_pEditView->execute(SCI_AUTOCSETSEPARATOR, WPARAM(' '));
_pEditView->execute(SCI_AUTOCSETIGNORECASE, _ignoreCase);
_pEditView->showAutoComletion(curPos - startPos, words.c_str());
return true;
}
bool AutoCompletion::showFunctionComplete()
{
if (!_funcCompletionActive)
return false;
if (_funcCalltip.updateCalltip(0, true))
{
return true;
}
return false;
}
void AutoCompletion::getCloseTag(char *closeTag, size_t closeTagSize, size_t caretPos, bool isHTML)
{
if (isHTML)
{
// Skip if caretPos is within any scripting language
int style = static_cast<int>(_pEditView->execute(SCI_GETSTYLEAT, caretPos));
if (style >= SCE_HJ_START)
return;
}
char prev = static_cast<char>(_pEditView->execute(SCI_GETCHARAT, caretPos - 2));
char prevprev = static_cast<char>(_pEditView->execute(SCI_GETCHARAT, caretPos - 3));
// Closing a tag (i.e. "-->") will be ignored
if (prevprev == '-' && prev == '-')
return;
// "<toto/>" and "<toto arg="0" />" will be ignored
if (prev == '/')
return;
int flags = SCFIND_REGEXP | SCFIND_POSIX;
_pEditView->execute(SCI_SETSEARCHFLAGS, flags);
TCHAR tag2find[] = TEXT("<[^\\s>]*");
int targetStart = _pEditView->searchInTarget(tag2find, lstrlen(tag2find), caretPos, 0);
if (targetStart == -1 || targetStart == -2)
return;
int targetEnd = int(_pEditView->execute(SCI_GETTARGETEND));
int foundTextLen = targetEnd - targetStart;
if (foundTextLen < 2) // "<>" will be ignored
return;
if (size_t(foundTextLen) > closeTagSize - 2) // buffer size is not large enough. -2 for '/' & '\0'
return;
char tagHead[tagMaxLen];
_pEditView->getText(tagHead, targetStart, targetEnd);
if (tagHead[1] == '/') // "</toto>" will be ignored
return;
if (tagHead[1] == '?') // "<?" (Processing Instructions) will be ignored
return;
if (strncmp(tagHead, "<!--", 4) == 0) // Comments will be ignored
return;
if (isHTML) // for HTML: ignore void elements
{
// https://www.w3.org/TR/html5/syntax.html#void-elements
const char *disallowedTags[] = {
"area", "base", "br", "col", "embed", "hr", "img", "input",
"keygen", "link", "meta", "param", "source", "track", "wbr",
"!doctype"
};
size_t disallowedTagsLen = sizeof(disallowedTags) / sizeof(char *);
for (size_t i = 0; i < disallowedTagsLen; ++i)
{
if (strnicmp(tagHead + 1, disallowedTags[i], strlen(disallowedTags[i])) == 0)
return;
}
}
closeTag[0] = '<';
closeTag[1] = '/';
_pEditView->getText(closeTag + 2, targetStart + 1, targetEnd);
closeTag[foundTextLen+1] = '>';
closeTag[foundTextLen+2] = '\0';
}
void InsertedMatchedChars::removeInvalidElements(MatchedCharInserted mci)
{
if (mci._c == '\n' || mci._c == '\r') // create a new line, so all matched char are invalidated
{
_insertedMatchedChars.clear();
}
else
{
for (int i = int(_insertedMatchedChars.size()) - 1; i >= 0; --i)
{
if (_insertedMatchedChars[i]._pos < mci._pos)
{
auto posToDetectLine = _pEditView->execute(SCI_LINEFROMPOSITION, mci._pos);
auto startPosLine = _pEditView->execute(SCI_LINEFROMPOSITION, _insertedMatchedChars[i]._pos);
if (posToDetectLine != startPosLine) //not in the same line
{
_insertedMatchedChars.erase(_insertedMatchedChars.begin() + i);
}
}
else // current position is before matchedStartSybol Pos
{
_insertedMatchedChars.erase(_insertedMatchedChars.begin() + i);
}
}
}
}
void InsertedMatchedChars::add(MatchedCharInserted mci)
{
removeInvalidElements(mci);
_insertedMatchedChars.push_back(mci);
}
// if current pos > matchedStartSybol Pos and current pos is on the same line of matchedStartSybolPos, it'll be checked then removed
// otherwise it is just removed
// return the pos of matchedEndSybol or -1 if matchedEndSybol not found
int InsertedMatchedChars::search(char startChar, char endChar, int posToDetect)
{
if (isEmpty())
return -1;
auto posToDetectLine = _pEditView->execute(SCI_LINEFROMPOSITION, posToDetect);
for (int i = int32_t(_insertedMatchedChars.size()) - 1; i >= 0; --i)
{
if (_insertedMatchedChars[i]._c == startChar)
{
if (_insertedMatchedChars[i]._pos < posToDetect)
{
auto startPosLine = _pEditView->execute(SCI_LINEFROMPOSITION, _insertedMatchedChars[i]._pos);
if (posToDetectLine == startPosLine)
{
auto endPos = _pEditView->execute(SCI_GETLINEENDPOSITION, startPosLine);
for (int j = posToDetect; j <= endPos; ++j)
{
char aChar = static_cast<char>(_pEditView->execute(SCI_GETCHARAT, j));
if (aChar != ' ') // non space is not allowed
{
if (aChar == endChar) // found it!!!
{
_insertedMatchedChars.erase(_insertedMatchedChars.begin() + i);
return j;
}
else // whichever character, stop searching
{
_insertedMatchedChars.erase(_insertedMatchedChars.begin() + i);
return -1;
}
}
}
}
else // not in the same line
{
_insertedMatchedChars.erase(_insertedMatchedChars.begin() + i);
}
}
else // current position is before matchedStartSybol Pos
{
_insertedMatchedChars.erase(_insertedMatchedChars.begin() + i);
}
}
}
return -1;
}
void AutoCompletion::insertMatchedChars(int character, const MatchedPairConf & matchedPairConf)
{
const vector< pair<char, char> > & matchedPairs = matchedPairConf._matchedPairs;
int caretPos = static_cast<int32_t>(_pEditView->execute(SCI_GETCURRENTPOS));
const char *matchedChars = NULL;
char charPrev = static_cast<char>(_pEditView->execute(SCI_GETCHARAT, caretPos - 2));
char charNext = static_cast<char>(_pEditView->execute(SCI_GETCHARAT, caretPos));
bool isCharPrevBlank = (charPrev == ' ' || charPrev == '\t' || charPrev == '\n' || charPrev == '\r' || charPrev == '\0');
int docLen = _pEditView->getCurrentDocLen();
bool isCharNextBlank = (charNext == ' ' || charNext == '\t' || charNext == '\n' || charNext == '\r' || caretPos == docLen);
bool isInSandwich = (charPrev == '(' && charNext == ')') || (charPrev == '[' && charNext == ']') || (charPrev == '{' && charNext == '}');
// User defined matched pairs should be checked firstly
for (size_t i = 0, len = matchedPairs.size(); i < len; ++i)
{
if (int(matchedPairs[i].first) == character)
{
if (isCharNextBlank)
{
char userMatchedChar[2] = { '\0', '\0' };
userMatchedChar[0] = matchedPairs[i].second;
_pEditView->execute(SCI_INSERTTEXT, caretPos, reinterpret_cast<LPARAM>(userMatchedChar));
return;
}
}
}
// if there's no user defined matched pair found, continue to check notepad++'s one
char closeTag[tagMaxLen];
closeTag[0] = '\0';
switch (character)
{
case int('('):
if (matchedPairConf._doParentheses)
{
if (isCharNextBlank || isInSandwich)
{
matchedChars = ")";
_insertedMatchedChars.add(MatchedCharInserted(static_cast<char>(character), caretPos - 1));
}
}
break;
case int('['):
if (matchedPairConf._doBrackets)
{
if (isCharNextBlank || isInSandwich)
{
matchedChars = "]";
_insertedMatchedChars.add(MatchedCharInserted(static_cast<char>(character), caretPos - 1));
}
}
break;
case int('{'):
if (matchedPairConf._doCurlyBrackets)
{
if (isCharNextBlank || isInSandwich)
{
matchedChars = "}";
_insertedMatchedChars.add(MatchedCharInserted(static_cast<char>(character), caretPos - 1));
}
}
break;
case int('"'):
if (matchedPairConf._doDoubleQuotes)
{
if (!_insertedMatchedChars.isEmpty())
{
int pos = _insertedMatchedChars.search('"', static_cast<char>(character), caretPos);
if (pos != -1)
{
_pEditView->execute(SCI_DELETERANGE, pos, 1);
_pEditView->execute(SCI_GOTOPOS, pos);
return;
}
}
if ((isCharPrevBlank && isCharNextBlank) || isInSandwich ||
(charPrev == '(' && isCharNextBlank) || (isCharPrevBlank && charNext == ')') ||
(charPrev == '[' && isCharNextBlank) || (isCharPrevBlank && charNext == ']') ||
(charPrev == '{' && isCharNextBlank) || (isCharPrevBlank && charNext == '}'))
{
matchedChars = "\"";
_insertedMatchedChars.add(MatchedCharInserted(static_cast<char>(character), caretPos - 1));
}
}
break;
case int('\''):
if (matchedPairConf._doQuotes)
{
if (!_insertedMatchedChars.isEmpty())
{
int pos = _insertedMatchedChars.search('\'', static_cast<char>(character), caretPos);
if (pos != -1)
{
_pEditView->execute(SCI_DELETERANGE, pos, 1);
_pEditView->execute(SCI_GOTOPOS, pos);
return;
}
}
if ((isCharPrevBlank && isCharNextBlank) || isInSandwich ||
(charPrev == '(' && isCharNextBlank) || (isCharPrevBlank && charNext == ')') ||
(charPrev == '[' && isCharNextBlank) || (isCharPrevBlank && charNext == ']') ||
(charPrev == '{' && isCharNextBlank) || (isCharPrevBlank && charNext == '}'))
{
matchedChars = "'";
_insertedMatchedChars.add(MatchedCharInserted(static_cast<char>(character), caretPos - 1));
}
}
break;
case int('>'):
{
if (matchedPairConf._doHtmlXmlTag && (_curLang == L_HTML || _curLang == L_XML))
{
getCloseTag(closeTag, tagMaxLen, caretPos, _curLang == L_HTML);
if (closeTag[0] != '\0')
matchedChars = closeTag;
}
}
break;
case int(')') :
case int(']') :
case int('}') :
if (!_insertedMatchedChars.isEmpty())
{
char startChar;
if (character == int(')'))
{
if (!matchedPairConf._doParentheses)
return;
startChar = '(';
}
else if (character == int(']'))
{
if (!matchedPairConf._doBrackets)
return;
startChar = '[';
}
else // if (character == int('}'))
{
if (!matchedPairConf._doCurlyBrackets)
return;
startChar = '{';
}
int pos = _insertedMatchedChars.search(startChar, static_cast<char>(character), caretPos);
if (pos != -1)
{
_pEditView->execute(SCI_DELETERANGE, pos, 1);
_pEditView->execute(SCI_GOTOPOS, pos);
}
return;
}
break;
default:
if (!_insertedMatchedChars.isEmpty())
_insertedMatchedChars.removeInvalidElements(MatchedCharInserted(static_cast<char>(character), caretPos - 1));
}
if (matchedChars)
_pEditView->execute(SCI_INSERTTEXT, caretPos, reinterpret_cast<LPARAM>(matchedChars));
}
void AutoCompletion::update(int character)
{
if (!character)
return;
const NppGUI & nppGUI = NppParameters::getInstance()->getNppGUI();
if (!_funcCompletionActive && nppGUI._autocStatus == nppGUI.autoc_func)
return;
if (nppGUI._funcParams || _funcCalltip.isVisible())
{
if (_funcCalltip.updateCalltip(character)) //calltip visible because triggered by autocomplete, set mode
{
return; //only return in case of success, else autocomplete
}
}
//If autocomplete already active, let Scintilla handle it
if (_pEditView->execute(SCI_AUTOCACTIVE) != 0)
return;
const int wordSize = 64;
TCHAR s[wordSize];
_pEditView->getWordToCurrentPos(s, wordSize);
if (lstrlen(s) >= int(nppGUI._autocFromLen))
{
if (nppGUI._autocStatus == nppGUI.autoc_word)
showWordComplete(false);
else if (nppGUI._autocStatus == nppGUI.autoc_func)
showApiComplete();
else if (nppGUI._autocStatus == nppGUI.autoc_both)
showApiAndWordComplete();
}
}
void AutoCompletion::callTipClick(size_t direction) {
if (!_funcCompletionActive)
return;
if (direction == 1) {
_funcCalltip.showPrevOverload();
} else if (direction == 2) {
_funcCalltip.showNextOverload();
}
}
bool AutoCompletion::setLanguage(LangType language) {
if (_curLang == language)
return true;
_curLang = language;
TCHAR path[MAX_PATH];
::GetModuleFileName(NULL, path, MAX_PATH);
PathRemoveFileSpec(path);
wcscat_s(path, TEXT("\\autoCompletion\\"));
wcscat_s(path, getApiFileName());
wcscat_s(path, TEXT(".xml"));
delete _pXmlFile;
_pXmlFile = new TiXmlDocument(path);
_funcCompletionActive = _pXmlFile->LoadFile();
TiXmlNode * pAutoNode = NULL;
if (_funcCompletionActive) {
_funcCompletionActive = false; //safety
TiXmlNode * pNode = _pXmlFile->FirstChild(TEXT("NotepadPlus"));
if (!pNode)
return false;
pAutoNode = pNode = pNode->FirstChildElement(TEXT("AutoComplete"));
if (!pNode)
return false;
pNode = pNode->FirstChildElement(TEXT("KeyWord"));
if (!pNode)
return false;
_pXmlKeyword = reinterpret_cast<TiXmlElement *>(pNode);
if (!_pXmlKeyword)
return false;
_funcCompletionActive = true;
}
if (_funcCompletionActive) //try setting up environment
{
//setup defaults
_ignoreCase = true;
_funcCalltip._start = '(';
_funcCalltip._stop = ')';
_funcCalltip._param = ',';
_funcCalltip._terminal = ';';
_funcCalltip._ignoreCase = true;
_funcCalltip._additionalWordChar.clear();
TiXmlElement * pElem = pAutoNode->FirstChildElement(TEXT("Environment"));
if (pElem)
{
const TCHAR * val = 0;
val = pElem->Attribute(TEXT("ignoreCase"));
if (val && !lstrcmp(val, TEXT("no"))) {
_ignoreCase = false;
_funcCalltip._ignoreCase = false;
}
val = pElem->Attribute(TEXT("startFunc"));
if (val && val[0])
_funcCalltip._start = val[0];
val = pElem->Attribute(TEXT("stopFunc"));
if (val && val[0])
_funcCalltip._stop = val[0];
val = pElem->Attribute(TEXT("paramSeparator"));
if (val && val[0])
_funcCalltip._param = val[0];
val = pElem->Attribute(TEXT("terminal"));
if (val && val[0])
_funcCalltip._terminal = val[0];
val = pElem->Attribute(TEXT("additionalWordChar"));
if (val && val[0])
_funcCalltip._additionalWordChar = val;
}
}
if (_funcCompletionActive) {
_funcCalltip.setLanguageXML(_pXmlKeyword);
} else {
_funcCalltip.setLanguageXML(NULL);
}
_keyWords.clear();
_keyWordArray.clear();
if (_funcCompletionActive)
{
//Cache the keywords
//Iterate through all keywords
TiXmlElement *funcNode = _pXmlKeyword;
for (; funcNode; funcNode = funcNode->NextSiblingElement(TEXT("KeyWord")) )
{
const TCHAR *name = funcNode->Attribute(TEXT("name"));
if (name)
{
size_t len = lstrlen(name);
if (len)
{
_keyWordArray.push_back(name);
if (len > _keyWordMaxLen)
_keyWordMaxLen = len;
}
}
}
sort(_keyWordArray.begin(), _keyWordArray.end());
for (size_t i = 0, len = _keyWordArray.size(); i < len; ++i)
{
_keyWords.append(_keyWordArray[i]);
_keyWords.append(TEXT(" "));
}
}
return _funcCompletionActive;
}
const TCHAR * AutoCompletion::getApiFileName()
{
if (_curLang == L_USER)
{
Buffer * currentBuf = _pEditView->getCurrentBuffer();
if (currentBuf->isUserDefineLangExt())
{
return currentBuf->getUserDefineLangName();
}
}
if (_curLang >= L_EXTERNAL && _curLang < NppParameters::getInstance()->L_END)
return NppParameters::getInstance()->getELCFromIndex(_curLang - L_EXTERNAL)._name;
if (_curLang > L_EXTERNAL)
_curLang = L_TEXT;
if (_curLang == L_JAVASCRIPT)
_curLang = L_JS;
return ScintillaEditView::langNames[_curLang].lexerName;
}
You can’t perform that action at this time.