[IMPROVE] Reorganize the xml tag match hilite.
git-svn-id: svn://svn.tuxfamily.org/svnroot/notepadplus/repository@283 f5eea248-9336-0410-98b8-ebc06183d4e3pull/343/head^2
parent
d8973543ad
commit
7523faff29
|
@ -39,6 +39,7 @@
|
|||
#include "xpm_icons.h"
|
||||
#include <time.h>
|
||||
#include <algorithm>
|
||||
#include "xmlMatchedTagsHighlighter.h"
|
||||
|
||||
const char Notepad_plus::_className[32] = NOTEPAD_PP_CLASS_NAME;
|
||||
const char *urlHttpRegExpr = "http://[a-z0-9_\\-\\+.:?&@=/%#]*";
|
||||
|
@ -2212,7 +2213,8 @@ BOOL Notepad_plus::notify(SCNotification *notification)
|
|||
case SCN_UPDATEUI:
|
||||
{
|
||||
braceMatch();
|
||||
tagMatch();
|
||||
XmlMatchedTagsHighlighter xmlTagMatchHiliter(_pEditView);
|
||||
xmlTagMatchHiliter.tagMatch();
|
||||
markSelectedText();
|
||||
updateStatusBar();
|
||||
AutoCompletion * autoC = isFromPrimary?&_autoCompleteMain:&_autoCompleteSub;
|
||||
|
@ -2398,428 +2400,6 @@ void Notepad_plus::findMatchingBracePos(int & braceAtCaret, int & braceOpposite)
|
|||
braceOpposite = int(_pEditView->execute(SCI_BRACEMATCH, braceAtCaret, 0));
|
||||
}
|
||||
|
||||
int Notepad_plus::getFirstTokenPosFrom(int targetStart, int targetEnd, const char *token, pair<int, int> & foundPos)
|
||||
{
|
||||
//int start = currentPos;
|
||||
//int end = (direction == DIR_LEFT)?0:_pEditView->getCurrentDocLen();
|
||||
|
||||
_pEditView->execute(SCI_SETTARGETSTART, targetStart);
|
||||
_pEditView->execute(SCI_SETTARGETEND, targetEnd);
|
||||
_pEditView->execute(SCI_SETSEARCHFLAGS, SCFIND_REGEXP|SCFIND_POSIX);
|
||||
int posFind = _pEditView->execute(SCI_SEARCHINTARGET, (WPARAM)strlen(token), (LPARAM)token);
|
||||
if (posFind != -1)
|
||||
{
|
||||
foundPos.first = _pEditView->execute(SCI_GETTARGETSTART);
|
||||
foundPos.second = _pEditView->execute(SCI_GETTARGETEND);
|
||||
}
|
||||
return posFind;
|
||||
}
|
||||
|
||||
TagCateg Notepad_plus::getTagCategory(XmlMatchedTagsPos & tagsPos, int curPos)
|
||||
{
|
||||
pair<int, int> foundPos;
|
||||
|
||||
int docLen = _pEditView->getCurrentDocLen();
|
||||
|
||||
int gtPos = getFirstTokenPosFrom(curPos, 0, ">", foundPos);
|
||||
int ltPos = getFirstTokenPosFrom(curPos, 0, "<", foundPos);
|
||||
if (ltPos != -1)
|
||||
{
|
||||
if ((gtPos != -1) && (ltPos < gtPos))
|
||||
return outOfTag;
|
||||
|
||||
// Now we are sure about that we are inside of tag
|
||||
// We'll try to determinate the tag category :
|
||||
// tagOpen : <Tag>, <Tag Attr="1" >
|
||||
// tagClose : </Tag>
|
||||
// tagSigle : <Tag/>, <Tag Attr="0" />
|
||||
int charAfterLt = _pEditView->execute(SCI_GETCHARAT, ltPos+1);
|
||||
if (!charAfterLt)
|
||||
return unknownPb;
|
||||
|
||||
if ((char)charAfterLt == ' ')
|
||||
return invalidTag;
|
||||
|
||||
// so now we are sure we have tag sign '<'
|
||||
// We'll see on the right
|
||||
int gtPosOnR = getFirstTokenPosFrom(curPos, docLen, ">", foundPos);
|
||||
int ltPosOnR = getFirstTokenPosFrom(curPos, docLen, "<", foundPos);
|
||||
|
||||
if (gtPosOnR == -1)
|
||||
return invalidTag;
|
||||
|
||||
if ((ltPosOnR != -1) && (ltPosOnR < gtPosOnR))
|
||||
return invalidTag;
|
||||
|
||||
if ((char)charAfterLt == '/')
|
||||
{
|
||||
int char2AfterLt = _pEditView->execute(SCI_GETCHARAT, ltPos+1+1);
|
||||
|
||||
if (!char2AfterLt)
|
||||
return unknownPb;
|
||||
|
||||
if ((char)char2AfterLt == ' ')
|
||||
return invalidTag;
|
||||
|
||||
tagsPos.tagCloseStart = ltPos;
|
||||
tagsPos.tagCloseEnd = gtPosOnR + 1;
|
||||
return tagClose;
|
||||
}
|
||||
else
|
||||
{
|
||||
// it's sure for not being a tagClose
|
||||
// So we determinate if it's tagSingle or tagOpen
|
||||
tagsPos.tagOpenStart = ltPos;
|
||||
tagsPos.tagOpenEnd = gtPosOnR + 1;
|
||||
|
||||
int charBeforeLt = _pEditView->execute(SCI_GETCHARAT, gtPosOnR-1);
|
||||
if ((char)charBeforeLt == '/')
|
||||
return inSingleTag;
|
||||
|
||||
return tagOpen;
|
||||
}
|
||||
}
|
||||
|
||||
return outOfTag;
|
||||
}
|
||||
|
||||
bool Notepad_plus::getMatchedTagPos(int searchStart, int searchEnd, const char *tag2find, const char *oppositeTag2find, vector<int> oppositeTagFound, XmlMatchedTagsPos & tagsPos)
|
||||
{
|
||||
const bool search2Left = false;
|
||||
const bool search2Right = true;
|
||||
|
||||
bool direction = searchEnd > searchStart;
|
||||
|
||||
pair<int, int> foundPos;
|
||||
int ltPosOnR = getFirstTokenPosFrom(searchStart, searchEnd, tag2find, foundPos);
|
||||
if (ltPosOnR == -1)
|
||||
return false;
|
||||
|
||||
TagCateg tc = outOfTag;
|
||||
if (direction == search2Left)
|
||||
{
|
||||
tc = getTagCategory(tagsPos, ltPosOnR+2);
|
||||
|
||||
if (tc != tagOpen && tc != inSingleTag)
|
||||
return false;
|
||||
if (tc == inSingleTag)
|
||||
{
|
||||
int start = foundPos.first;
|
||||
int end = searchEnd;
|
||||
return getMatchedTagPos(start, end, tag2find, oppositeTag2find, oppositeTagFound, tagsPos);
|
||||
}
|
||||
//oppositeTagFound.push_back(foundPos.first);
|
||||
}
|
||||
|
||||
pair<int, int> oppositeTagPos;
|
||||
int s = foundPos.first;
|
||||
int e = tagsPos.tagOpenEnd;
|
||||
if (direction == search2Left)
|
||||
{
|
||||
s = foundPos.second;
|
||||
e = tagsPos.tagCloseStart;
|
||||
}
|
||||
|
||||
int ltTag = getFirstTokenPosFrom(s, e, oppositeTag2find, oppositeTagPos);
|
||||
|
||||
if (ltTag == -1)
|
||||
{
|
||||
if (direction == search2Left)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
tagsPos.tagCloseStart = foundPos.first;
|
||||
tagsPos.tagCloseEnd = foundPos.second;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (isInList(ltTag, oppositeTagFound))
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
ltTag = getFirstTokenPosFrom(ltTag, e, oppositeTag2find, oppositeTagPos);
|
||||
if (ltTag == -1)
|
||||
{
|
||||
if (direction == search2Left)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
tagsPos.tagCloseStart = foundPos.first;
|
||||
tagsPos.tagCloseEnd = foundPos.second;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (!isInList(ltTag, oppositeTagFound))
|
||||
{
|
||||
oppositeTagFound.push_back(ltTag);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (direction == search2Left)
|
||||
{
|
||||
XmlMatchedTagsPos tmpTagsPos;
|
||||
getTagCategory(tmpTagsPos, ltTag+1);
|
||||
ltTag = tmpTagsPos.tagCloseEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
oppositeTagFound.push_back(ltTag);
|
||||
}
|
||||
|
||||
int start, end;
|
||||
if (direction == search2Left)
|
||||
{
|
||||
start = foundPos.first;
|
||||
end = searchEnd;
|
||||
}
|
||||
else
|
||||
{
|
||||
start = foundPos.second;
|
||||
end = searchEnd;
|
||||
}
|
||||
|
||||
return getMatchedTagPos(start, end, tag2find, oppositeTag2find, oppositeTagFound, tagsPos);
|
||||
}
|
||||
|
||||
|
||||
bool Notepad_plus::getXmlMatchedTagsPos(XmlMatchedTagsPos & tagsPos)
|
||||
{
|
||||
// get word where caret is on
|
||||
int caretPos = _pEditView->execute(SCI_GETCURRENTPOS);
|
||||
|
||||
int docLen = _pEditView->getCurrentDocLen();
|
||||
|
||||
// determinate the nature of current word : tagOpen, tagClose or outOfTag
|
||||
TagCateg tagCateg = getTagCategory(tagsPos, caretPos);
|
||||
|
||||
static const char tagNameChars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_:";
|
||||
|
||||
switch (tagCateg)
|
||||
{
|
||||
case tagOpen : // if tagOpen search right
|
||||
{
|
||||
_pEditView->execute(SCI_SETWORDCHARS, 0, (LPARAM)tagNameChars);
|
||||
int startPos = _pEditView->execute(SCI_WORDSTARTPOSITION, tagsPos.tagOpenStart+1, true);
|
||||
int endPos = _pEditView->execute(SCI_WORDENDPOSITION, tagsPos.tagOpenStart+1, true);
|
||||
tagsPos.tagNameEnd = endPos;
|
||||
|
||||
_pEditView->execute(SCI_SETCHARSDEFAULT);
|
||||
char * tagName = new char[endPos-startPos+1];
|
||||
|
||||
_pEditView->getText(tagName, startPos, endPos);
|
||||
|
||||
string closeTag = "</";
|
||||
closeTag += tagName;
|
||||
closeTag += "[ ]*>";
|
||||
|
||||
string openTag = "<";
|
||||
openTag += tagName;
|
||||
openTag += "[ >]";
|
||||
|
||||
delete [] tagName;
|
||||
|
||||
vector<int> passedTagList;
|
||||
return getMatchedTagPos(tagsPos.tagOpenEnd, docLen, closeTag.c_str(), openTag.c_str(), passedTagList, tagsPos);
|
||||
}
|
||||
|
||||
case tagClose : // if tagClose search left
|
||||
{
|
||||
_pEditView->execute(SCI_SETWORDCHARS, 0, (LPARAM)tagNameChars);
|
||||
int startPos = _pEditView->execute(SCI_WORDSTARTPOSITION, tagsPos.tagCloseStart+2, true);
|
||||
int endPos = _pEditView->execute(SCI_WORDENDPOSITION, tagsPos.tagCloseStart+2, true);
|
||||
|
||||
_pEditView->execute(SCI_SETCHARSDEFAULT);
|
||||
char * tagName = new char[endPos-startPos+1];
|
||||
_pEditView->getText(tagName, startPos, endPos);
|
||||
|
||||
string openTag = "<";
|
||||
openTag += tagName;
|
||||
|
||||
string closeTag = "</";
|
||||
closeTag += tagName;
|
||||
closeTag += "[ ]*>";
|
||||
|
||||
delete [] tagName;
|
||||
|
||||
vector<int> passedTagList;
|
||||
bool isFound = getMatchedTagPos(tagsPos.tagCloseStart, 0, openTag.c_str(), closeTag.c_str(), passedTagList, tagsPos);
|
||||
if (isFound)
|
||||
tagsPos.tagNameEnd = tagsPos.tagOpenStart + 1 + (endPos - startPos);
|
||||
|
||||
return isFound;
|
||||
}
|
||||
|
||||
case inSingleTag : // if in single tag
|
||||
{
|
||||
_pEditView->execute(SCI_SETWORDCHARS, 0, (LPARAM)tagNameChars);
|
||||
int endPos = _pEditView->execute(SCI_WORDENDPOSITION, tagsPos.tagOpenStart+1, true);
|
||||
tagsPos.tagNameEnd = endPos;
|
||||
_pEditView->execute(SCI_SETCHARSDEFAULT);
|
||||
|
||||
tagsPos.tagCloseStart = -1;
|
||||
tagsPos.tagCloseEnd = -1;
|
||||
return true;
|
||||
}
|
||||
default: // if outOfTag, just quit
|
||||
return false;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
vector< pair<int, int> > Notepad_plus::getAttributesPos(int start, int end)
|
||||
{
|
||||
vector< pair<int, int> > attributes;
|
||||
|
||||
int bufLen = end - start + 1;
|
||||
char *buf = new char[bufLen+1];
|
||||
_pEditView->getText(buf, start, end);
|
||||
|
||||
enum {\
|
||||
attr_invalid,\
|
||||
attr_key,\
|
||||
attr_pre_assign,\
|
||||
attr_assign,\
|
||||
attr_string,\
|
||||
attr_value,\
|
||||
attr_valid\
|
||||
} state = attr_invalid;
|
||||
|
||||
int startPos = -1;
|
||||
int oneMoreChar = 1;
|
||||
int i = 0;
|
||||
for (; i < bufLen ; i++)
|
||||
{
|
||||
switch (buf[i])
|
||||
{
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
case '\r':
|
||||
{
|
||||
if (state == attr_key)
|
||||
state = attr_pre_assign;
|
||||
else if (state == attr_value)
|
||||
{
|
||||
state = attr_valid;
|
||||
oneMoreChar = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case '=':
|
||||
{
|
||||
if (state == attr_key || state == attr_pre_assign)
|
||||
state = attr_assign;
|
||||
else if (state == attr_assign || state == attr_value)
|
||||
state = attr_invalid;
|
||||
}
|
||||
break;
|
||||
|
||||
case '"':
|
||||
{
|
||||
if (state == attr_string)
|
||||
{
|
||||
state = attr_valid;
|
||||
oneMoreChar = 1;
|
||||
}
|
||||
else if (state == attr_key || state == attr_pre_assign || state == attr_value)
|
||||
state = attr_invalid;
|
||||
else if (state == attr_assign)
|
||||
state = attr_string;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
if (state == attr_invalid)
|
||||
{
|
||||
state = attr_key;
|
||||
startPos = i;
|
||||
}
|
||||
else if (state == attr_pre_assign)
|
||||
state = attr_invalid;
|
||||
else if (state == attr_assign)
|
||||
state = attr_value;
|
||||
}
|
||||
}
|
||||
|
||||
if (state == attr_valid)
|
||||
{
|
||||
attributes.push_back(pair<int, int>(start+startPos, start+i+oneMoreChar));
|
||||
state = attr_invalid;
|
||||
}
|
||||
}
|
||||
if (state == attr_value)
|
||||
attributes.push_back(pair<int, int>(start+startPos, start+i-1));
|
||||
|
||||
delete [] buf;
|
||||
return attributes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Notepad_plus::tagMatch()
|
||||
{
|
||||
const NppGUI & nppGUI = (NppParameters::getInstance())->getNppGUI();
|
||||
if (!nppGUI._enableTagsMatchHilite)
|
||||
return;
|
||||
|
||||
// Clean up all marks of previous action
|
||||
_pEditView->clearIndicator(SCE_UNIVERSAL_TAGMATCH);
|
||||
_pEditView->clearIndicator(SCE_UNIVERSAL_TAGATTR);
|
||||
|
||||
// Detect the current lang type. It works only with html and xml
|
||||
LangType lang = (_pEditView->getCurrentBuffer())->getLangType();
|
||||
if (lang != L_XML && lang != L_HTML && lang != L_PHP && lang != L_ASP)
|
||||
return;
|
||||
|
||||
// Get the original targets to restore after tag matching operation
|
||||
int originalStartPos = _pEditView->execute(SCI_GETTARGETSTART);
|
||||
int originalEndPos = _pEditView->execute(SCI_GETTARGETEND);
|
||||
|
||||
// Detect if it's a xml/html tag. If yes, Colour it!
|
||||
XmlMatchedTagsPos xmlTags;
|
||||
if (getXmlMatchedTagsPos(xmlTags))
|
||||
{
|
||||
_pEditView->execute(SCI_SETINDICATORCURRENT, SCE_UNIVERSAL_TAGMATCH);
|
||||
|
||||
int openTagTailLen = 2;
|
||||
// We colourise the close tag firstly
|
||||
if ((xmlTags.tagCloseStart != -1) && (xmlTags.tagCloseEnd != -1))
|
||||
{
|
||||
_pEditView->execute(SCI_INDICATORFILLRANGE, xmlTags.tagCloseStart, xmlTags.tagCloseEnd - xmlTags.tagCloseStart);
|
||||
// tag close is present, so it's not single tag
|
||||
openTagTailLen = 1;
|
||||
}
|
||||
|
||||
// Now the open tag and its attributs
|
||||
_pEditView->execute(SCI_INDICATORFILLRANGE, xmlTags.tagOpenStart, xmlTags.tagNameEnd - xmlTags.tagOpenStart);
|
||||
_pEditView->execute(SCI_INDICATORFILLRANGE, xmlTags.tagOpenEnd - openTagTailLen, openTagTailLen);
|
||||
|
||||
if (nppGUI._enableTagAttrsHilite)
|
||||
{
|
||||
vector< pair<int, int> > attributes = getAttributesPos(xmlTags.tagNameEnd, xmlTags.tagOpenEnd - openTagTailLen);
|
||||
_pEditView->execute(SCI_SETINDICATORCURRENT, SCE_UNIVERSAL_TAGATTR);
|
||||
for (size_t i = 0 ; i < attributes.size() ; i++)
|
||||
{
|
||||
_pEditView->execute(SCI_INDICATORFILLRANGE, attributes[i].first, attributes[i].second - attributes[i].first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// restore the original targets to avoid the conflit with search/replace function
|
||||
_pEditView->execute(SCI_SETTARGETSTART, originalStartPos);
|
||||
_pEditView->execute(SCI_SETTARGETEND, originalEndPos);
|
||||
}
|
||||
|
||||
void Notepad_plus::braceMatch()
|
||||
{
|
||||
|
|
|
@ -89,8 +89,6 @@ struct iconLocator {
|
|||
|
||||
class FileDialog;
|
||||
|
||||
enum TagCateg {tagOpen, tagClose, inSingleTag, outOfTag, invalidTag, unknownPb};
|
||||
|
||||
class Notepad_plus : public Window {
|
||||
enum comment_mode {cm_comment, cm_uncomment, cm_toggle};
|
||||
public:
|
||||
|
@ -269,14 +267,6 @@ private:
|
|||
|
||||
//For Dynamic selection highlight
|
||||
CharacterRange _prevSelectedRange;
|
||||
struct XmlMatchedTagsPos {
|
||||
int tagOpenStart;
|
||||
int tagNameEnd;
|
||||
int tagOpenEnd;
|
||||
|
||||
int tagCloseStart;
|
||||
int tagCloseEnd;
|
||||
};
|
||||
|
||||
struct ActivateAppInfo {
|
||||
bool _isActivated;
|
||||
|
@ -675,19 +665,6 @@ private:
|
|||
void findMatchingBracePos(int & braceAtCaret, int & braceOpposite);
|
||||
void braceMatch();
|
||||
|
||||
int getFirstTokenPosFrom(int targetStart, int targetEnd, const char *token, pair<int, int> & foundPos);
|
||||
TagCateg getTagCategory(XmlMatchedTagsPos & tagsPos, int curPos);
|
||||
bool getMatchedTagPos(int searchStart, int searchEnd, const char *tag2find, const char *oppositeTag2find, vector<int> oppositeTagFound, XmlMatchedTagsPos & tagsPos);
|
||||
bool getXmlMatchedTagsPos(XmlMatchedTagsPos & tagsPos);
|
||||
vector< pair<int, int> > getAttributesPos(int start, int end);
|
||||
bool isInList(int element, vector<int> elementList) {
|
||||
for (size_t i = 0 ; i < elementList.size() ; i++)
|
||||
if (element == elementList[i])
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
void tagMatch();
|
||||
|
||||
void activateNextDoc(bool direction);
|
||||
void activateDoc(int pos);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//this file is part of notepad++
|
||||
//Copyright (C)2003 Don HO ( donho@altern.org )
|
||||
//Copyright (C)2003 Don HO <donho@altern.org>
|
||||
//
|
||||
//This program is free software; you can redistribute it and/or
|
||||
//modify it under the terms of the GNU General Public License
|
||||
|
|
|
@ -0,0 +1,442 @@
|
|||
//this file is part of notepad++
|
||||
//Copyright (C)2003 Don HO <donho@altern.org>
|
||||
//
|
||||
//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.
|
||||
//
|
||||
//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 "xmlMatchedTagsHighlighter.h"
|
||||
#include "ScintillaEditView.h"
|
||||
|
||||
int XmlMatchedTagsHighlighter::getFirstTokenPosFrom(int targetStart, int targetEnd, const char *token, pair<int, int> & foundPos)
|
||||
{
|
||||
//int start = currentPos;
|
||||
//int end = (direction == DIR_LEFT)?0:_pEditView->getCurrentDocLen();
|
||||
|
||||
_pEditView->execute(SCI_SETTARGETSTART, targetStart);
|
||||
_pEditView->execute(SCI_SETTARGETEND, targetEnd);
|
||||
_pEditView->execute(SCI_SETSEARCHFLAGS, SCFIND_REGEXP|SCFIND_POSIX);
|
||||
int posFind = _pEditView->execute(SCI_SEARCHINTARGET, (WPARAM)strlen(token), (LPARAM)token);
|
||||
if (posFind != -1)
|
||||
{
|
||||
foundPos.first = _pEditView->execute(SCI_GETTARGETSTART);
|
||||
foundPos.second = _pEditView->execute(SCI_GETTARGETEND);
|
||||
}
|
||||
return posFind;
|
||||
}
|
||||
|
||||
TagCateg XmlMatchedTagsHighlighter::getTagCategory(XmlMatchedTagsPos & tagsPos, int curPos)
|
||||
{
|
||||
pair<int, int> foundPos;
|
||||
|
||||
int docLen = _pEditView->getCurrentDocLen();
|
||||
|
||||
int gtPos = getFirstTokenPosFrom(curPos, 0, ">", foundPos);
|
||||
int ltPos = getFirstTokenPosFrom(curPos, 0, "<", foundPos);
|
||||
if (ltPos != -1)
|
||||
{
|
||||
if ((gtPos != -1) && (ltPos < gtPos))
|
||||
return outOfTag;
|
||||
|
||||
// Now we are sure about that we are inside of tag
|
||||
// We'll try to determinate the tag category :
|
||||
// tagOpen : <Tag>, <Tag Attr="1" >
|
||||
// tagClose : </Tag>
|
||||
// tagSigle : <Tag/>, <Tag Attr="0" />
|
||||
int charAfterLt = _pEditView->execute(SCI_GETCHARAT, ltPos+1);
|
||||
if (!charAfterLt)
|
||||
return unknownPb;
|
||||
|
||||
if ((char)charAfterLt == ' ')
|
||||
return invalidTag;
|
||||
|
||||
// so now we are sure we have tag sign '<'
|
||||
// We'll see on the right
|
||||
int gtPosOnR = getFirstTokenPosFrom(curPos, docLen, ">", foundPos);
|
||||
int ltPosOnR = getFirstTokenPosFrom(curPos, docLen, "<", foundPos);
|
||||
|
||||
if (gtPosOnR == -1)
|
||||
return invalidTag;
|
||||
|
||||
if ((ltPosOnR != -1) && (ltPosOnR < gtPosOnR))
|
||||
return invalidTag;
|
||||
|
||||
if ((char)charAfterLt == '/')
|
||||
{
|
||||
int char2AfterLt = _pEditView->execute(SCI_GETCHARAT, ltPos+1+1);
|
||||
|
||||
if (!char2AfterLt)
|
||||
return unknownPb;
|
||||
|
||||
if ((char)char2AfterLt == ' ')
|
||||
return invalidTag;
|
||||
|
||||
tagsPos.tagCloseStart = ltPos;
|
||||
tagsPos.tagCloseEnd = gtPosOnR + 1;
|
||||
return tagClose;
|
||||
}
|
||||
else
|
||||
{
|
||||
// it's sure for not being a tagClose
|
||||
// So we determinate if it's tagSingle or tagOpen
|
||||
tagsPos.tagOpenStart = ltPos;
|
||||
tagsPos.tagOpenEnd = gtPosOnR + 1;
|
||||
|
||||
int charBeforeLt = _pEditView->execute(SCI_GETCHARAT, gtPosOnR-1);
|
||||
if ((char)charBeforeLt == '/')
|
||||
return inSingleTag;
|
||||
|
||||
return tagOpen;
|
||||
}
|
||||
}
|
||||
|
||||
return outOfTag;
|
||||
}
|
||||
|
||||
bool XmlMatchedTagsHighlighter::getMatchedTagPos(int searchStart, int searchEnd, const char *tag2find, const char *oppositeTag2find, vector<int> oppositeTagFound, XmlMatchedTagsPos & tagsPos)
|
||||
{
|
||||
const bool search2Left = false;
|
||||
const bool search2Right = true;
|
||||
|
||||
bool direction = searchEnd > searchStart;
|
||||
|
||||
pair<int, int> foundPos;
|
||||
int ltPosOnR = getFirstTokenPosFrom(searchStart, searchEnd, tag2find, foundPos);
|
||||
if (ltPosOnR == -1)
|
||||
return false;
|
||||
|
||||
TagCateg tc = outOfTag;
|
||||
if (direction == search2Left)
|
||||
{
|
||||
tc = getTagCategory(tagsPos, ltPosOnR+2);
|
||||
|
||||
if (tc != tagOpen && tc != inSingleTag)
|
||||
return false;
|
||||
if (tc == inSingleTag)
|
||||
{
|
||||
int start = foundPos.first;
|
||||
int end = searchEnd;
|
||||
return getMatchedTagPos(start, end, tag2find, oppositeTag2find, oppositeTagFound, tagsPos);
|
||||
}
|
||||
//oppositeTagFound.push_back(foundPos.first);
|
||||
}
|
||||
|
||||
pair<int, int> oppositeTagPos;
|
||||
int s = foundPos.first;
|
||||
int e = tagsPos.tagOpenEnd;
|
||||
if (direction == search2Left)
|
||||
{
|
||||
s = foundPos.second;
|
||||
e = tagsPos.tagCloseStart;
|
||||
}
|
||||
|
||||
int ltTag = getFirstTokenPosFrom(s, e, oppositeTag2find, oppositeTagPos);
|
||||
|
||||
if (ltTag == -1)
|
||||
{
|
||||
if (direction == search2Left)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
tagsPos.tagCloseStart = foundPos.first;
|
||||
tagsPos.tagCloseEnd = foundPos.second;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (isInList(ltTag, oppositeTagFound))
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
ltTag = getFirstTokenPosFrom(ltTag, e, oppositeTag2find, oppositeTagPos);
|
||||
if (ltTag == -1)
|
||||
{
|
||||
if (direction == search2Left)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
tagsPos.tagCloseStart = foundPos.first;
|
||||
tagsPos.tagCloseEnd = foundPos.second;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (!isInList(ltTag, oppositeTagFound))
|
||||
{
|
||||
oppositeTagFound.push_back(ltTag);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (direction == search2Left)
|
||||
{
|
||||
XmlMatchedTagsPos tmpTagsPos;
|
||||
getTagCategory(tmpTagsPos, ltTag+1);
|
||||
ltTag = tmpTagsPos.tagCloseEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
oppositeTagFound.push_back(ltTag);
|
||||
}
|
||||
|
||||
int start, end;
|
||||
if (direction == search2Left)
|
||||
{
|
||||
start = foundPos.first;
|
||||
end = searchEnd;
|
||||
}
|
||||
else
|
||||
{
|
||||
start = foundPos.second;
|
||||
end = searchEnd;
|
||||
}
|
||||
|
||||
return getMatchedTagPos(start, end, tag2find, oppositeTag2find, oppositeTagFound, tagsPos);
|
||||
}
|
||||
|
||||
|
||||
bool XmlMatchedTagsHighlighter::getXmlMatchedTagsPos(XmlMatchedTagsPos & tagsPos)
|
||||
{
|
||||
// get word where caret is on
|
||||
int caretPos = _pEditView->execute(SCI_GETCURRENTPOS);
|
||||
|
||||
int docLen = _pEditView->getCurrentDocLen();
|
||||
|
||||
// determinate the nature of current word : tagOpen, tagClose or outOfTag
|
||||
TagCateg tagCateg = getTagCategory(tagsPos, caretPos);
|
||||
|
||||
static const char tagNameChars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_:";
|
||||
|
||||
switch (tagCateg)
|
||||
{
|
||||
case tagOpen : // if tagOpen search right
|
||||
{
|
||||
_pEditView->execute(SCI_SETWORDCHARS, 0, (LPARAM)tagNameChars);
|
||||
int startPos = _pEditView->execute(SCI_WORDSTARTPOSITION, tagsPos.tagOpenStart+1, true);
|
||||
int endPos = _pEditView->execute(SCI_WORDENDPOSITION, tagsPos.tagOpenStart+1, true);
|
||||
tagsPos.tagNameEnd = endPos;
|
||||
|
||||
_pEditView->execute(SCI_SETCHARSDEFAULT);
|
||||
char * tagName = new char[endPos-startPos+1];
|
||||
|
||||
_pEditView->getText(tagName, startPos, endPos);
|
||||
|
||||
string closeTag = "</";
|
||||
closeTag += tagName;
|
||||
closeTag += "[ ]*>";
|
||||
|
||||
string openTag = "<";
|
||||
openTag += tagName;
|
||||
openTag += "[ >]";
|
||||
|
||||
delete [] tagName;
|
||||
|
||||
vector<int> passedTagList;
|
||||
return getMatchedTagPos(tagsPos.tagOpenEnd, docLen, closeTag.c_str(), openTag.c_str(), passedTagList, tagsPos);
|
||||
}
|
||||
|
||||
case tagClose : // if tagClose search left
|
||||
{
|
||||
_pEditView->execute(SCI_SETWORDCHARS, 0, (LPARAM)tagNameChars);
|
||||
int startPos = _pEditView->execute(SCI_WORDSTARTPOSITION, tagsPos.tagCloseStart+2, true);
|
||||
int endPos = _pEditView->execute(SCI_WORDENDPOSITION, tagsPos.tagCloseStart+2, true);
|
||||
|
||||
_pEditView->execute(SCI_SETCHARSDEFAULT);
|
||||
char * tagName = new char[endPos-startPos+1];
|
||||
_pEditView->getText(tagName, startPos, endPos);
|
||||
|
||||
string openTag = "<";
|
||||
openTag += tagName;
|
||||
|
||||
string closeTag = "</";
|
||||
closeTag += tagName;
|
||||
closeTag += "[ ]*>";
|
||||
|
||||
delete [] tagName;
|
||||
|
||||
vector<int> passedTagList;
|
||||
bool isFound = getMatchedTagPos(tagsPos.tagCloseStart, 0, openTag.c_str(), closeTag.c_str(), passedTagList, tagsPos);
|
||||
if (isFound)
|
||||
tagsPos.tagNameEnd = tagsPos.tagOpenStart + 1 + (endPos - startPos);
|
||||
|
||||
return isFound;
|
||||
}
|
||||
|
||||
case inSingleTag : // if in single tag
|
||||
{
|
||||
_pEditView->execute(SCI_SETWORDCHARS, 0, (LPARAM)tagNameChars);
|
||||
int endPos = _pEditView->execute(SCI_WORDENDPOSITION, tagsPos.tagOpenStart+1, true);
|
||||
tagsPos.tagNameEnd = endPos;
|
||||
_pEditView->execute(SCI_SETCHARSDEFAULT);
|
||||
|
||||
tagsPos.tagCloseStart = -1;
|
||||
tagsPos.tagCloseEnd = -1;
|
||||
return true;
|
||||
}
|
||||
default: // if outOfTag, just quit
|
||||
return false;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
vector< pair<int, int> > XmlMatchedTagsHighlighter::getAttributesPos(int start, int end)
|
||||
{
|
||||
vector< pair<int, int> > attributes;
|
||||
|
||||
int bufLen = end - start + 1;
|
||||
char *buf = new char[bufLen+1];
|
||||
_pEditView->getText(buf, start, end);
|
||||
|
||||
enum {\
|
||||
attr_invalid,\
|
||||
attr_key,\
|
||||
attr_pre_assign,\
|
||||
attr_assign,\
|
||||
attr_string,\
|
||||
attr_value,\
|
||||
attr_valid\
|
||||
} state = attr_invalid;
|
||||
|
||||
int startPos = -1;
|
||||
int oneMoreChar = 1;
|
||||
int i = 0;
|
||||
for (; i < bufLen ; i++)
|
||||
{
|
||||
switch (buf[i])
|
||||
{
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
case '\r':
|
||||
{
|
||||
if (state == attr_key)
|
||||
state = attr_pre_assign;
|
||||
else if (state == attr_value)
|
||||
{
|
||||
state = attr_valid;
|
||||
oneMoreChar = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case '=':
|
||||
{
|
||||
if (state == attr_key || state == attr_pre_assign)
|
||||
state = attr_assign;
|
||||
else if (state == attr_assign || state == attr_value)
|
||||
state = attr_invalid;
|
||||
}
|
||||
break;
|
||||
|
||||
case '"':
|
||||
{
|
||||
if (state == attr_string)
|
||||
{
|
||||
state = attr_valid;
|
||||
oneMoreChar = 1;
|
||||
}
|
||||
else if (state == attr_key || state == attr_pre_assign || state == attr_value)
|
||||
state = attr_invalid;
|
||||
else if (state == attr_assign)
|
||||
state = attr_string;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
if (state == attr_invalid)
|
||||
{
|
||||
state = attr_key;
|
||||
startPos = i;
|
||||
}
|
||||
else if (state == attr_pre_assign)
|
||||
state = attr_invalid;
|
||||
else if (state == attr_assign)
|
||||
state = attr_value;
|
||||
}
|
||||
}
|
||||
|
||||
if (state == attr_valid)
|
||||
{
|
||||
attributes.push_back(pair<int, int>(start+startPos, start+i+oneMoreChar));
|
||||
state = attr_invalid;
|
||||
}
|
||||
}
|
||||
if (state == attr_value)
|
||||
attributes.push_back(pair<int, int>(start+startPos, start+i-1));
|
||||
|
||||
delete [] buf;
|
||||
return attributes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void XmlMatchedTagsHighlighter::tagMatch()
|
||||
{
|
||||
const NppGUI & nppGUI = (NppParameters::getInstance())->getNppGUI();
|
||||
if (!nppGUI._enableTagsMatchHilite)
|
||||
return;
|
||||
|
||||
// Clean up all marks of previous action
|
||||
_pEditView->clearIndicator(SCE_UNIVERSAL_TAGMATCH);
|
||||
_pEditView->clearIndicator(SCE_UNIVERSAL_TAGATTR);
|
||||
|
||||
// Detect the current lang type. It works only with html and xml
|
||||
LangType lang = (_pEditView->getCurrentBuffer())->getLangType();
|
||||
if (lang != L_XML && lang != L_HTML && lang != L_PHP && lang != L_ASP)
|
||||
return;
|
||||
|
||||
// Get the original targets to restore after tag matching operation
|
||||
int originalStartPos = _pEditView->execute(SCI_GETTARGETSTART);
|
||||
int originalEndPos = _pEditView->execute(SCI_GETTARGETEND);
|
||||
|
||||
// Detect if it's a xml/html tag. If yes, Colour it!
|
||||
XmlMatchedTagsPos xmlTags;
|
||||
if (getXmlMatchedTagsPos(xmlTags))
|
||||
{
|
||||
_pEditView->execute(SCI_SETINDICATORCURRENT, SCE_UNIVERSAL_TAGMATCH);
|
||||
|
||||
int openTagTailLen = 2;
|
||||
// We colourise the close tag firstly
|
||||
if ((xmlTags.tagCloseStart != -1) && (xmlTags.tagCloseEnd != -1))
|
||||
{
|
||||
_pEditView->execute(SCI_INDICATORFILLRANGE, xmlTags.tagCloseStart, xmlTags.tagCloseEnd - xmlTags.tagCloseStart);
|
||||
// tag close is present, so it's not single tag
|
||||
openTagTailLen = 1;
|
||||
}
|
||||
|
||||
// Now the open tag and its attributs
|
||||
_pEditView->execute(SCI_INDICATORFILLRANGE, xmlTags.tagOpenStart, xmlTags.tagNameEnd - xmlTags.tagOpenStart);
|
||||
_pEditView->execute(SCI_INDICATORFILLRANGE, xmlTags.tagOpenEnd - openTagTailLen, openTagTailLen);
|
||||
|
||||
if (nppGUI._enableTagAttrsHilite)
|
||||
{
|
||||
vector< pair<int, int> > attributes = getAttributesPos(xmlTags.tagNameEnd, xmlTags.tagOpenEnd - openTagTailLen);
|
||||
_pEditView->execute(SCI_SETINDICATORCURRENT, SCE_UNIVERSAL_TAGATTR);
|
||||
for (size_t i = 0 ; i < attributes.size() ; i++)
|
||||
{
|
||||
_pEditView->execute(SCI_INDICATORFILLRANGE, attributes[i].first, attributes[i].second - attributes[i].first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// restore the original targets to avoid the conflit with search/replace function
|
||||
_pEditView->execute(SCI_SETTARGETSTART, originalStartPos);
|
||||
_pEditView->execute(SCI_SETTARGETEND, originalEndPos);
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
//this file is part of notepad++
|
||||
//Copyright (C)2003 Don HO <donho@altern.org>
|
||||
//
|
||||
//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.
|
||||
//
|
||||
//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.
|
||||
|
||||
#ifndef XMLMATCHEDTAGSHIGHLIGHTER_H
|
||||
#define XMLMATCHEDTAGSHIGHLIGHTER_H
|
||||
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
|
||||
class ScintillaEditView;
|
||||
|
||||
enum TagCateg {tagOpen, tagClose, inSingleTag, outOfTag, invalidTag, unknownPb};
|
||||
|
||||
class XmlMatchedTagsHighlighter {
|
||||
public:
|
||||
XmlMatchedTagsHighlighter(ScintillaEditView *pEditView):_pEditView(pEditView){};
|
||||
void tagMatch();
|
||||
|
||||
private:
|
||||
struct XmlMatchedTagsPos {
|
||||
int tagOpenStart;
|
||||
int tagNameEnd;
|
||||
int tagOpenEnd;
|
||||
|
||||
int tagCloseStart;
|
||||
int tagCloseEnd;
|
||||
};
|
||||
|
||||
ScintillaEditView *_pEditView;
|
||||
|
||||
int getFirstTokenPosFrom(int targetStart, int targetEnd, const char *token, std::pair<int, int> & foundPos);
|
||||
TagCateg getTagCategory(XmlMatchedTagsPos & tagsPos, int curPos);
|
||||
bool getMatchedTagPos(int searchStart, int searchEnd, const char *tag2find, const char *oppositeTag2find, vector<int> oppositeTagFound, XmlMatchedTagsPos & tagsPos);
|
||||
bool getXmlMatchedTagsPos(XmlMatchedTagsPos & tagsPos);
|
||||
vector< pair<int, int> > getAttributesPos(int start, int end);
|
||||
bool isInList(int element, vector<int> elementList) {
|
||||
for (size_t i = 0 ; i < elementList.size() ; i++)
|
||||
if (element == elementList[i])
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
};
|
||||
|
||||
#endif //XMLMATCHEDTAGSHIGHLIGHTER_H
|
||||
|
|
@ -1,3 +1,20 @@
|
|||
//this file is part of notepad++
|
||||
//Copyright (C)2003 Don HO ( donho@altern.org )
|
||||
//
|
||||
//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.
|
||||
//
|
||||
//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 <windows.h>
|
||||
#include "preferenceDlg.h"
|
||||
#include "SysMsg.h"
|
||||
|
|
|
@ -437,6 +437,10 @@
|
|||
RelativePath="..\src\WinControls\ColourPicker\WordStyleDlg.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\ScitillaComponent\xmlMatchedTagsHighlighter.cpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
|
@ -603,11 +607,11 @@
|
|||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\resource.h"
|
||||
RelativePath="..\src\WinControls\Preference\resource.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\WinControls\Preference\resource.h"
|
||||
RelativePath="..\src\resource.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
|
@ -750,6 +754,10 @@
|
|||
RelativePath="..\src\WinControls\ColourPicker\WordStyleDlg.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\ScitillaComponent\xmlMatchedTagsHighlighter.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\xpm_icons.h"
|
||||
>
|
||||
|
|
Loading…
Reference in New Issue