Enhance User Defined Language System for supporting more than one UDL file.

Several UDL xml files can be loaded to allow to manage/share UDL more easily.

1. The old file userDefineLang.xml is kept in its old location and is still used.
2. The new folder userDefineLangs is added beside of the old UDL default file. Any UDL xml file can go into the folder userDefineLangs and will be loaded as UDL.
3. A UDL xml file must contain one (or several) user defined language(s).
4. The created UDL via UDL dialog and imported UDL are saved in  userDefineLang.xml (default UDL file).

https://notepad-plus-plus.org/community/topic/17072/new-enhancement-for-user-defined-language-system
This commit is contained in:
Don HO 2019-02-07 23:40:17 +01:00
parent 51f10bdba5
commit dfb9b5e330
7 changed files with 264 additions and 79 deletions

View File

@ -1238,3 +1238,29 @@ bool deleteFileOrFolder(const generic_string& f2delete)
delete[] actionFolder;
return (res == 0);
}
// Get a vector of full file paths in a given folder. File extension type filter should be *.*, *.xml, *.dll... according the type of file you want to get.
void getFilesInFolder(std::vector<generic_string>& files, const generic_string& extTypeFilter, const generic_string& inFolder)
{
generic_string filter = inFolder;
PathAppend(filter, extTypeFilter);
WIN32_FIND_DATA foundData;
HANDLE hFindFile = ::FindFirstFile(filter.c_str(), &foundData);
if (hFindFile != INVALID_HANDLE_VALUE && !(foundData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
generic_string foundFullPath = inFolder;
PathAppend(foundFullPath, foundData.cFileName);
files.push_back(foundFullPath);
while (::FindNextFile(hFindFile, &foundData))
{
generic_string foundFullPath2 = inFolder;
PathAppend(foundFullPath2, foundData.cFileName);
files.push_back(foundFullPath2);
}
}
::FindClose(hFindFile);
}

View File

@ -196,3 +196,5 @@ std::wstring s2ws(const std::string& str);
std::string ws2s(const std::wstring& wstr);
bool deleteFileOrFolder(const generic_string& f2delete);
void getFilesInFolder(std::vector<generic_string>& files, const generic_string& extTypeFilter, const generic_string& inFolder);

View File

@ -928,8 +928,7 @@ void Notepad_plus::saveDockingParams()
void Notepad_plus::saveUserDefineLangs()
{
if (ScintillaEditView::getUserDefineDlg()->isDirty())
(NppParameters::getInstance())->writeUserDefinedLang();
(NppParameters::getInstance())->writeNeed2SaveUDL();
}

View File

@ -829,11 +829,6 @@ int FileDialog::_dialogFileBoxId = (NppParameters::getInstance())->getWinVersion
NppParameters::NppParameters()
{
// init import UDL array
_nbImportedULD = 0;
for (int i = 0 ; i < NB_MAX_IMPORTED_UDL ; ++i)
_importedULD[i] = nullptr;
//Get windows version
_winVersion = getWindowsVersion();
@ -1229,8 +1224,12 @@ bool NppParameters::load()
//-----------------------------------//
// userDefineLang.xml : for per user //
//-----------------------------------//
_userDefineLangPath = _userPath;
generic_string userDefineLangsFolderPath = _userDefineLangPath = _userPath;
PathAppend(_userDefineLangPath, TEXT("userDefineLang.xml"));
PathAppend(userDefineLangsFolderPath, TEXT("userDefineLangs"));
std::vector<generic_string> udlFiles;
getFilesInFolder(udlFiles, TEXT("*.xml"), userDefineLangsFolderPath);
_pXmlUserLangDoc = new TiXmlDocument(_userDefineLangPath);
loadOkay = _pXmlUserLangDoc->LoadFile();
@ -1241,7 +1240,27 @@ bool NppParameters::load()
isAllLaoded = false;
}
else
getUserDefineLangsFromXmlTree();
{
auto r = addUserDefineLangsFromXmlTree(_pXmlUserLangDoc);
if (r.second - r.first > 0)
_pXmlUserLangsDoc.push_back(UdlXmlFileState(_pXmlUserLangDoc, false, r));
}
for (const auto& i : udlFiles)
{
auto udlDoc = new TiXmlDocument(i);
loadOkay = udlDoc->LoadFile();
if (!loadOkay)
{
delete udlDoc;
}
else
{
auto r = addUserDefineLangsFromXmlTree(udlDoc);
if (r.second - r.first > 0)
_pXmlUserLangsDoc.push_back(UdlXmlFileState(udlDoc, false, r));
}
}
//----------------------------------------------//
// nativeLang.xml : for per user //
@ -1402,14 +1421,12 @@ void NppParameters::destroyInstance()
delete _pXmlDoc;
delete _pXmlUserDoc;
delete _pXmlUserStylerDoc;
delete _pXmlUserLangDoc;
for (int i = 0 ; i < _nbImportedULD ; ++i)
//delete _pXmlUserLangDoc; will be deleted in the vector
for (auto l : _pXmlUserLangsDoc)
{
delete _importedULD[i];
_importedULD[i] = nullptr;
delete l._udlXmlDoc;
}
_nbImportedULD = 0;
delete _pXmlNativeLangDocA;
delete _pXmlToolIconsDoc;
@ -1661,14 +1678,14 @@ bool NppParameters::getUserParametersFromXmlTree()
}
bool NppParameters::getUserDefineLangsFromXmlTree(TiXmlDocument *tixmldoc)
std::pair<unsigned char, unsigned char> NppParameters::addUserDefineLangsFromXmlTree(TiXmlDocument *tixmldoc)
{
if (!tixmldoc)
return false;
return std::pair<unsigned char, unsigned char>(0, 0);
TiXmlNode *root = tixmldoc->FirstChild(TEXT("NotepadPlus"));
if (!root)
return false;
return std::pair<unsigned char, unsigned char>(0, 0);
return feedUserLang(root);
}
@ -2588,10 +2605,9 @@ bool NppParameters::getShortcuts(TiXmlNode *node, Shortcut & sc)
}
bool NppParameters::feedUserLang(TiXmlNode *node)
std::pair<unsigned char, unsigned char> NppParameters::feedUserLang(TiXmlNode *node)
{
bool isEverythingOK = true;
bool hasFoundElement = false;
int iBegin = _nbUserLang;
for (TiXmlNode *childNode = node->FirstChildElement(TEXT("UserLang"));
childNode && (_nbUserLang < NB_MAX_USER_LANG);
@ -2600,7 +2616,6 @@ bool NppParameters::feedUserLang(TiXmlNode *node)
const TCHAR *name = (childNode->ToElement())->Attribute(TEXT("name"));
const TCHAR *ext = (childNode->ToElement())->Attribute(TEXT("ext"));
const TCHAR *udlVersion = (childNode->ToElement())->Attribute(TEXT("udlVersion"));
hasFoundElement = true;
try {
if (!name || !name[0] || !ext)
throw std::runtime_error("NppParameters::feedUserLang : UserLang name is missing");
@ -2639,26 +2654,29 @@ bool NppParameters::feedUserLang(TiXmlNode *node)
} catch (std::exception e) {
delete _userLangArray[--_nbUserLang];
isEverythingOK = false;
}
}
if (isEverythingOK)
isEverythingOK = hasFoundElement;
return isEverythingOK;
int iEnd = _nbUserLang;
return pair<unsigned char, unsigned char>(iBegin, iEnd);
}
bool NppParameters::importUDLFromFile(generic_string sourceFile)
{
if (_nbImportedULD >= NB_MAX_IMPORTED_UDL)
return false;
TiXmlDocument *pXmlUserLangDoc = new TiXmlDocument(sourceFile);
bool loadOkay = pXmlUserLangDoc->LoadFile();
if (loadOkay)
{
loadOkay = getUserDefineLangsFromXmlTree(pXmlUserLangDoc);
auto r = addUserDefineLangsFromXmlTree(pXmlUserLangDoc);
loadOkay = (r.second - r.first) != 0;
if (loadOkay)
{
_pXmlUserLangsDoc.push_back(UdlXmlFileState(nullptr, true, r));
// imported UDL from xml file will be added into default udl, so we should make default udl dirty
setUdlXmlDirtyFromXmlDoc(_pXmlUserLangDoc);
}
_importedULD[_nbImportedULD++] = pXmlUserLangDoc;
}
delete pXmlUserLangDoc;
return loadOkay;
}
@ -2863,33 +2881,97 @@ bool NppParameters::writeSettingsFilesOnCloudForThe1stTime(const generic_string
return true;
}
/*
Default UDL + Created + Imported
void NppParameters::writeUserDefinedLang()
*/
void NppParameters::writeDefaultUDL()
{
bool firstCleanDone = false;
for (auto udl : _pXmlUserLangsDoc)
{
if (!_pXmlUserLangDoc)
{
//do the treatment
_pXmlUserLangDoc = new TiXmlDocument(_userDefineLangPath);
}
//before remove the branch, we allocate and copy the TCHAR * which will be destroyed
stylerStrOp(DUP);
bool toDelete = (udl._indexRange.second - udl._indexRange.first) == 0;
if ((!udl._udlXmlDoc || udl._udlXmlDoc == _pXmlUserLangDoc) && udl._isDirty && !toDelete) // new created or/and imported UDL plus _pXmlUserLangDoc (if exist)
{
TiXmlNode *root = _pXmlUserLangDoc->FirstChild(TEXT("NotepadPlus"));
if (root)
if (root && !firstCleanDone)
{
_pXmlUserLangDoc->RemoveChild(root);
}
_pXmlUserLangDoc->InsertEndChild(TiXmlElement(TEXT("NotepadPlus")));
firstCleanDone = true;
}
root = _pXmlUserLangDoc->FirstChild(TEXT("NotepadPlus"));
for (int i = 0 ; i < _nbUserLang ; ++i)
for (int i = udl._indexRange.first; i < udl._indexRange.second; ++i)
{
insertUserLang2Tree(root, _userLangArray[i]);
}
}
}
if (firstCleanDone)
{
_pXmlUserLangDoc->SaveFile();
}
else
{
if (::PathFileExists(_userDefineLangPath.c_str()))
{
::DeleteFile(_userDefineLangPath.c_str());
}
}
}
void NppParameters::writeNonDefaultUDL()
{
for (auto udl : _pXmlUserLangsDoc)
{
if (udl._isDirty && udl._udlXmlDoc != nullptr && udl._udlXmlDoc != _pXmlUserLangDoc)
{
if (udl._indexRange.second == udl._indexRange.first) // no more udl for this xmldoc container
{
// no need to save, delete file
const TCHAR* docFilePath = udl._udlXmlDoc->Value();
if (docFilePath && ::PathFileExists(docFilePath))
{
::DeleteFile(docFilePath);
}
}
else
{
TiXmlNode *root = udl._udlXmlDoc->FirstChild(TEXT("NotepadPlus"));
if (root)
{
udl._udlXmlDoc->RemoveChild(root);
}
udl._udlXmlDoc->InsertEndChild(TiXmlElement(TEXT("NotepadPlus")));
root = udl._udlXmlDoc->FirstChild(TEXT("NotepadPlus"));
for (int i = udl._indexRange.first; i < udl._indexRange.second; ++i)
{
insertUserLang2Tree(root, _userLangArray[i]);
}
udl._udlXmlDoc->SaveFile();
}
}
}
}
void NppParameters::writeNeed2SaveUDL()
{
stylerStrOp(DUP);
writeDefaultUDL();
writeNonDefaultUDL();
stylerStrOp(FREE);
}
@ -3142,11 +3224,18 @@ int NppParameters::addUserLangToEnd(const UserLangContainer & userLang, const TC
{
if (isExistingUserLangName(newName))
return -1;
unsigned char iBegin = _nbUserLang;
_userLangArray[_nbUserLang] = new UserLangContainer();
*(_userLangArray[_nbUserLang]) = userLang;
_userLangArray[_nbUserLang]->_name = newName;
++_nbUserLang;
unsigned char iEnd = _nbUserLang;
_pXmlUserLangsDoc.push_back(UdlXmlFileState(nullptr, true, make_pair(iBegin, iEnd)));
// imported UDL from xml file will be added into default udl, so we should make default udl dirty
setUdlXmlDirtyFromXmlDoc(_pXmlUserLangDoc);
return _nbUserLang-1;
}
@ -3160,6 +3249,8 @@ void NppParameters::removeUserLang(size_t index)
for (int32_t i = static_cast<int32_t>(index); i < (_nbUserLang - 1); ++i)
_userLangArray[i] = _userLangArray[i+1];
_nbUserLang--;
removeIndexFromXmlUdls(index);
}
@ -6625,6 +6716,74 @@ void NppParameters::safeWow64EnableWow64FsRedirection(BOOL Wow64FsEnableRedirect
}
}
void NppParameters::setUdlXmlDirtyFromIndex(size_t i)
{
for (auto& uxfs : _pXmlUserLangsDoc)
{
if (i >= uxfs._indexRange.first && i < uxfs._indexRange.second)
{
uxfs._isDirty = true;
return;
}
}
}
/*
Considering we have done:
load default UDL: 3 languges
load a UDL file: 1 languge
load a UDL file: 2 languges
create a UDL: 1 languge
imported a UDL: 1 languge
the evolution to remove UDL one by one:
0[D1] 0[D1] 0[D1] [D1] [D1]
1[D2] 1[D2] 1[D2] 0[D2] [D2]
2[D3] [DUDL, <0,3>] 2[D3] [DUDL, <0,3>] 2[D3] [DUDL, <0,3>] 1[D3] [DUDL, <0,2>] [D3] [DUDL, <0,0>]
3[U1] [NUDL, <3,4>] 3[U1] [NUDL, <3,4>] 3[U1] [NUDL, <3,4>] 2[U1] [NUDL, <2,3>] [U1] [NUDL, <0,0>]
4[U2] 4[U2] [U2] [U2] [U2]
5[U2] [NUDL, <4,6>] 5[U2] [NUDL, <4,6>] 4[U2] [NUDL, <4,5>] 3[U2] [NUDL, <3,4>] 0[U2] [NUDL, <0,1>]
6[C1] [NULL, <6,7>] [C1] [NULL, <6,6>] [C1] [NULL, <5,5>] [C1] [NULL, <4,4>] [C1] [NULL, <1,1>]
7[I1] [NULL, <7,8>] 6[I1] [NULL, <6,7>] 5[I1] [NULL, <5,6>] 4[I1] [NULL, <4,5>] 1[I1] [NULL, <1,2>]
*/
void NppParameters::removeIndexFromXmlUdls(size_t i)
{
bool isUpdateBegin = false;
for (auto& uxfs : _pXmlUserLangsDoc)
{
// Find index
if (!isUpdateBegin && (i >= uxfs._indexRange.first && i < uxfs._indexRange.second)) // found it
{
if (uxfs._indexRange.second > 0)
uxfs._indexRange.second -= 1;
uxfs._isDirty = true;
isUpdateBegin = true;
}
// Update
else if (isUpdateBegin)
{
if (uxfs._indexRange.first > 0)
uxfs._indexRange.first -= 1;
if (uxfs._indexRange.second > 0)
uxfs._indexRange.second -= 1;
}
}
}
void NppParameters::setUdlXmlDirtyFromXmlDoc(const TiXmlDocument* xmlDoc)
{
for (auto& uxfs : _pXmlUserLangsDoc)
{
if (xmlDoc == uxfs._udlXmlDoc)
{
uxfs._isDirty = true;
return;
}
}
}
Date::Date(const TCHAR *dateStr)
{

View File

@ -1267,6 +1267,14 @@ private:
};
struct UdlXmlFileState final {
TiXmlDocument* _udlXmlDoc = nullptr;
bool _isDirty = false;
std::pair<unsigned char, unsigned char> _indexRange;
UdlXmlFileState(TiXmlDocument* doc, bool isDirty, std::pair<unsigned char, unsigned char> range) : _udlXmlDoc(doc), _isDirty(isDirty), _indexRange(range) {};
};
const int NB_LANG = 100;
const bool DUP = true;
const bool FREE = false;
@ -1274,9 +1282,6 @@ const bool FREE = false;
const int RECENTFILES_SHOWFULLPATH = -1;
const int RECENTFILES_SHOWONLYFILENAME = 0;
class NppParameters final
{
public:
@ -1414,7 +1419,9 @@ public:
void getExternalLexerFromXmlTree(TiXmlDocument *doc);
std::vector<TiXmlDocument *> * getExternalLexerDoc() { return &_pXmlExternalLexerDoc; };
void writeUserDefinedLang();
void writeDefaultUDL();
void writeNonDefaultUDL();
void writeNeed2SaveUDL();
void writeShortcuts();
void writeSession(const Session & session, const TCHAR *fileName = NULL);
bool writeFindHistory();
@ -1625,6 +1632,9 @@ public:
generic_string static getSpecialFolderLocation(int folderKind);
void setUdlXmlDirtyFromIndex(size_t i);
void setUdlXmlDirtyFromXmlDoc(const TiXmlDocument* xmlDoc);
void removeIndexFromXmlUdls(size_t i);
private:
NppParameters();
@ -1636,20 +1646,15 @@ private:
TiXmlDocument *_pXmlUserDoc = nullptr;
TiXmlDocument *_pXmlUserStylerDoc = nullptr;
TiXmlDocument *_pXmlUserLangDoc = nullptr;
std::vector<UdlXmlFileState> _pXmlUserLangsDoc;
TiXmlDocument *_pXmlToolIconsDoc = nullptr;
TiXmlDocument *_pXmlShortcutDoc = nullptr;
TiXmlDocument *_pXmlSessionDoc = nullptr;
TiXmlDocument *_pXmlBlacklistDoc = nullptr;
TiXmlDocument *_importedULD[NB_MAX_IMPORTED_UDL];
TiXmlDocumentA *_pXmlNativeLangDocA = nullptr;
TiXmlDocumentA *_pXmlContextMenuDocA = nullptr;
int _nbImportedULD;
std::vector<TiXmlDocument *> _pXmlExternalLexerDoc;
NppGUI _nppGUI;
@ -1669,7 +1674,7 @@ private:
FindHistory _findHistory;
UserLangContainer *_userLangArray[NB_MAX_USER_LANG];
int _nbUserLang = 0;
unsigned char _nbUserLang = 0; // won't be exceeded to 255;
generic_string _userDefineLangPath;
ExternalLangContainer *_externalLangArray[NB_MAX_EXTERNAL_LANG];
int _nbExternalLang = 0;
@ -1769,11 +1774,7 @@ private:
void getLangKeywordsFromXmlTree();
bool getUserParametersFromXmlTree();
bool getUserStylersFromXmlTree();
bool getUserDefineLangsFromXmlTree(TiXmlDocument *tixmldoc);
bool getUserDefineLangsFromXmlTree()
{
return getUserDefineLangsFromXmlTree(_pXmlUserLangDoc);
}
std::pair<unsigned char, unsigned char> addUserDefineLangsFromXmlTree(TiXmlDocument *tixmldoc);
bool getShortcutsFromXmlTree();
@ -1793,7 +1794,7 @@ private:
void feedProjectPanelsParameters(TiXmlNode *node);
void feedFileBrowserParameters(TiXmlNode *node);
bool feedStylerArray(TiXmlNode *node);
bool feedUserLang(TiXmlNode *node);
std::pair<unsigned char, unsigned char> feedUserLang(TiXmlNode *node);
void feedUserStyles(TiXmlNode *node);
void feedUserKeywordList(TiXmlNode *node);
void feedUserSettings(TiXmlNode *node);

View File

@ -929,13 +929,14 @@ void UserDefineDialog::enableLangAndControlsBy(size_t index)
}
void UserDefineDialog::updateDlg()
{
if (!_isDirty)
{
int i = static_cast<int32_t>(::SendDlgItemMessage(_hSelf, IDC_LANGNAME_COMBO, CB_GETCURSEL, 0, 0));
if (i > 0)
_isDirty = true;
if (i > 0) // the first menu item is generic UDL
{
NppParameters *pNppParam = NppParameters::getInstance();
pNppParam->setUdlXmlDirtyFromIndex(i - 1);
}
::SendDlgItemMessage(_hSelf, IDC_LANGNAME_IGNORECASE_CHECK, BM_SETCHECK, _pUserLang->_isCaseIgnored, 0);
_folderStyleDlg.updateDlg();
_keyWordsStyleDlg.updateDlg();
@ -1265,7 +1266,6 @@ INT_PTR CALLBACK UserDefineDialog::run_dlgProc(UINT message, WPARAM wParam, LPAR
auto i = ::SendDlgItemMessage(_hSelf, IDC_LANGNAME_COMBO, CB_GETCURSEL, 0, 0);
reloadLangCombo();
::SendDlgItemMessage(_hSelf, IDC_LANGNAME_COMBO, CB_SETCURSEL, i, 0);
_isDirty = true;
printStr(TEXT("Import successful."));
}
else

View File

@ -353,7 +353,6 @@ public :
void changeStyle();
bool isDocked() const {return _status == DOCK;};
void setDockStatus(bool isDocked) {_status = isDocked;};
bool isDirty() const {return _isDirty;};
HWND getFolderHandle() const {
return _folderStyleDlg.getHSelf();
};
@ -384,7 +383,6 @@ private :
int _currentHight = 0;
int _yScrollPos = 0;
int _prevHightVal = 0;
bool _isDirty = false;
void getActualPosSize() {
::GetWindowRect(_hSelf, &_dlgPos);
_dlgPos.right -= _dlgPos.left;