// This file is part of Notepad++ project // Copyright (C)2021 Don HO // 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 3 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, see . #include #include #include #include #include #include #include "ScintillaEditView.h" #include "Parameters.h" #include "localization.h" #include "Sorters.h" #include "verifySignedfile.h" #include "ILexer.h" #include "Lexilla.h" using namespace std; // initialize the static variable bool ScintillaEditView::_SciInit = false; int ScintillaEditView::_refCount = 0; UserDefineDialog ScintillaEditView::_userDefineDlg; const int ScintillaEditView::_SC_MARGE_LINENUMBER = 0; const int ScintillaEditView::_SC_MARGE_SYMBOL = 1; const int ScintillaEditView::_SC_MARGE_CHANGEHISTORY = 2; const int ScintillaEditView::_SC_MARGE_FOLDER = 3; WNDPROC ScintillaEditView::_scintillaDefaultProc = NULL; string ScintillaEditView::_defaultCharList = ""; /* SC_MARKNUM_* | Arrow Plus/minus Circle tree Box tree ------------------------------------------------------------------------------------------------------------- FOLDEROPEN | SC_MARK_ARROWDOWN SC_MARK_MINUS SC_MARK_CIRCLEMINUS SC_MARK_BOXMINUS FOLDER | SC_MARK_ARROW SC_MARK_PLUS SC_MARK_CIRCLEPLUS SC_MARK_BOXPLUS FOLDERSUB | SC_MARK_EMPTY SC_MARK_EMPTY SC_MARK_VLINE SC_MARK_VLINE FOLDERTAIL | SC_MARK_EMPTY SC_MARK_EMPTY SC_MARK_LCORNERCURVE SC_MARK_LCORNER FOLDEREND | SC_MARK_EMPTY SC_MARK_EMPTY SC_MARK_CIRCLEPLUSCONNECTED SC_MARK_BOXPLUSCONNECTED FOLDEROPENMID | SC_MARK_EMPTY SC_MARK_EMPTY SC_MARK_CIRCLEMINUSCONNECTED SC_MARK_BOXMINUSCONNECTED FOLDERMIDTAIL | SC_MARK_EMPTY SC_MARK_EMPTY SC_MARK_TCORNERCURVE SC_MARK_TCORNER */ const int ScintillaEditView::_markersArray[][NB_FOLDER_STATE] = { {SC_MARKNUM_FOLDEROPEN, SC_MARKNUM_FOLDER, SC_MARKNUM_FOLDERSUB, SC_MARKNUM_FOLDERTAIL, SC_MARKNUM_FOLDEREND, SC_MARKNUM_FOLDEROPENMID, SC_MARKNUM_FOLDERMIDTAIL}, {SC_MARK_MINUS, SC_MARK_PLUS, SC_MARK_EMPTY, SC_MARK_EMPTY, SC_MARK_EMPTY, SC_MARK_EMPTY, SC_MARK_EMPTY}, {SC_MARK_ARROWDOWN, SC_MARK_ARROW, SC_MARK_EMPTY, SC_MARK_EMPTY, SC_MARK_EMPTY, SC_MARK_EMPTY, SC_MARK_EMPTY}, {SC_MARK_CIRCLEMINUS, SC_MARK_CIRCLEPLUS,SC_MARK_VLINE, SC_MARK_LCORNERCURVE, SC_MARK_CIRCLEPLUSCONNECTED, SC_MARK_CIRCLEMINUSCONNECTED, SC_MARK_TCORNERCURVE}, {SC_MARK_BOXMINUS, SC_MARK_BOXPLUS, SC_MARK_VLINE, SC_MARK_LCORNER, SC_MARK_BOXPLUSCONNECTED, SC_MARK_BOXMINUSCONNECTED, SC_MARK_TCORNER} }; // Array with all the names of all languages // The order of lang type (enum LangType) must be respected LanguageNameInfo ScintillaEditView::_langNameInfoArray[L_EXTERNAL + 1] = { // //_langName _shortName _longName _langID _lexerID // {L"normal", L"Normal text", L"Normal text file", L_TEXT, "null"}, {L"php", L"PHP", L"PHP Hypertext Preprocessor file", L_PHP, "phpscript"}, {L"c", L"C", L"C source file", L_C, "cpp"}, {L"cpp", L"C++", L"C++ source file", L_CPP, "cpp"}, {L"cs", L"C#", L"C# source file", L_CS, "cpp"}, {L"objc", L"Objective-C", L"Objective-C source file", L_OBJC, "objc"}, {L"java", L"Java", L"Java source file", L_JAVA, "cpp"}, {L"rc", L"RC", L"Windows Resource file", L_RC, "cpp"}, {L"html", L"HTML", L"Hyper Text Markup Language file", L_HTML, "hypertext"}, {L"xml", L"XML", L"eXtensible Markup Language file", L_XML, "xml"}, {L"makefile", L"Makefile", L"Makefile", L_MAKEFILE, "makefile"}, {L"pascal", L"Pascal", L"Pascal source file", L_PASCAL, "pascal"}, {L"batch", L"Batch", L"Batch file", L_BATCH, "batch"}, {L"ini", L"ini", L"MS ini file", L_INI, "props"}, {L"nfo", L"NFO", L"MSDOS Style/ASCII Art", L_ASCII, "null"}, {L"udf", L"udf", L"User Defined language file", L_USER, "user"}, {L"asp", L"ASP", L"Active Server Pages script file", L_ASP, "hypertext"}, {L"sql", L"SQL", L"Structured Query Language file", L_SQL, "sql"}, {L"vb", L"Visual Basic", L"Visual Basic file", L_VB, "vb"}, {L"javascript", L"JavaScript", L"JavaScript file", L_JS, "cpp"}, {L"css", L"CSS", L"Cascade Style Sheets File", L_CSS, "css"}, {L"perl", L"Perl", L"Perl source file", L_PERL, "perl"}, {L"python", L"Python", L"Python file", L_PYTHON, "python"}, {L"lua", L"Lua", L"Lua source File", L_LUA, "lua"}, {L"tex", L"TeX", L"TeX file", L_TEX, "tex"}, {L"fortran", L"Fortran free form", L"Fortran free form source file", L_FORTRAN, "fortran"}, {L"bash", L"Shell", L"Unix script file", L_BASH, "bash"}, {L"actionscript", L"ActionScript", L"Flash ActionScript file", L_FLASH, "cpp"}, {L"nsis", L"NSIS", L"Nullsoft Scriptable Install System script file", L_NSIS, "nsis"}, {L"tcl", L"TCL", L"Tool Command Language file", L_TCL, "tcl"}, {L"lisp", L"Lisp", L"List Processing language file", L_LISP, "lisp"}, {L"scheme", L"Scheme", L"Scheme file", L_SCHEME, "lisp"}, {L"asm", L"Assembly", L"Assembly language source file", L_ASM, "asm"}, {L"diff", L"Diff", L"Diff file", L_DIFF, "diff"}, {L"props", L"Properties file", L"Properties file", L_PROPS, "props"}, {L"postscript", L"PostScript", L"PostScript file", L_PS, "ps"}, {L"ruby", L"Ruby", L"Ruby file", L_RUBY, "ruby"}, {L"smalltalk", L"Smalltalk", L"Smalltalk file", L_SMALLTALK, "smalltalk"}, {L"vhdl", L"VHDL", L"VHSIC Hardware Description Language file", L_VHDL, "vhdl"}, {L"kix", L"KiXtart", L"KiXtart file", L_KIX, "kix"}, {L"autoit", L"AutoIt", L"AutoIt", L_AU3, "au3"}, {L"caml", L"CAML", L"Categorical Abstract Machine Language", L_CAML, "caml"}, {L"ada", L"Ada", L"Ada file", L_ADA, "ada"}, {L"verilog", L"Verilog", L"Verilog file", L_VERILOG, "verilog"}, {L"matlab", L"MATLAB", L"MATrix LABoratory", L_MATLAB, "matlab"}, {L"haskell", L"Haskell", L"Haskell", L_HASKELL, "haskell"}, {L"inno", L"Inno Setup", L"Inno Setup script", L_INNO, "inno"}, {L"searchResult", L"Internal Search", L"Internal Search", L_SEARCHRESULT, "searchResult"}, {L"cmake", L"CMake", L"CMake file", L_CMAKE, "cmake"}, {L"yaml", L"YAML", L"YAML Ain't Markup Language", L_YAML, "yaml"}, {L"cobol", L"COBOL", L"COmmon Business Oriented Language", L_COBOL, "COBOL"}, {L"gui4cli", L"Gui4Cli", L"Gui4Cli file", L_GUI4CLI, "gui4cli"}, {L"d", L"D", L"D programming language", L_D, "d"}, {L"powershell", L"PowerShell", L"Windows PowerShell", L_POWERSHELL, "powershell"}, {L"r", L"R", L"R programming language", L_R, "r"}, {L"jsp", L"JSP", L"JavaServer Pages script file", L_JSP, "hypertext"}, {L"coffeescript", L"CoffeeScript", L"CoffeeScript file", L_COFFEESCRIPT, "coffeescript"}, {L"json", L"json", L"JSON file", L_JSON, "json"}, {L"javascript.js", L"JavaScript", L"JavaScript file", L_JAVASCRIPT, "cpp"}, {L"fortran77", L"Fortran fixed form", L"Fortran fixed form source file", L_FORTRAN_77, "f77"}, {L"baanc", L"BaanC", L"BaanC File", L_BAANC, "baan"}, {L"srec", L"S-Record", L"Motorola S-Record binary data", L_SREC, "srec"}, {L"ihex", L"Intel HEX", L"Intel HEX binary data", L_IHEX, "ihex"}, {L"tehex", L"Tektronix extended HEX", L"Tektronix extended HEX binary data", L_TEHEX, "tehex"}, {L"swift", L"Swift", L"Swift file", L_SWIFT, "cpp"}, {L"asn1", L"ASN.1", L"Abstract Syntax Notation One file", L_ASN1, "asn1"}, {L"avs", L"AviSynth", L"AviSynth scripts files", L_AVS, "avs"}, {L"blitzbasic", L"BlitzBasic", L"BlitzBasic file", L_BLITZBASIC, "blitzbasic"}, {L"purebasic", L"PureBasic", L"PureBasic file", L_PUREBASIC, "purebasic"}, {L"freebasic", L"FreeBasic", L"FreeBasic file", L_FREEBASIC, "freebasic"}, {L"csound", L"Csound", L"Csound file", L_CSOUND, "csound"}, {L"erlang", L"Erlang", L"Erlang file", L_ERLANG, "erlang"}, {L"escript", L"ESCRIPT", L"ESCRIPT file", L_ESCRIPT, "escript"}, {L"forth", L"Forth", L"Forth file", L_FORTH, "forth"}, {L"latex", L"LaTeX", L"LaTeX file", L_LATEX, "latex"}, {L"mmixal", L"MMIXAL", L"MMIXAL file", L_MMIXAL, "mmixal"}, {L"nim", L"Nim", L"Nim file", L_NIM, "nimrod"}, {L"nncrontab", L"Nncrontab", L"extended crontab file", L_NNCRONTAB, "nncrontab"}, {L"oscript", L"OScript", L"OScript source file", L_OSCRIPT, "oscript"}, {L"rebol", L"REBOL", L"REBOL file", L_REBOL, "rebol"}, {L"registry", L"registry", L"registry file", L_REGISTRY, "registry"}, {L"rust", L"Rust", L"Rust file", L_RUST, "rust"}, {L"spice", L"Spice", L"spice file", L_SPICE, "spice"}, {L"txt2tags", L"txt2tags", L"txt2tags file", L_TXT2TAGS, "txt2tags"}, {L"visualprolog", L"Visual Prolog", L"Visual Prolog file", L_VISUALPROLOG, "visualprolog"}, {L"typescript", L"TypeScript", L"TypeScript file", L_TYPESCRIPT, "cpp"}, {L"json5", L"json5", L"JSON5 file", L_JSON5, "json"}, {L"mssql", L"mssql", L"Microsoft Transact-SQL (SQL Server) file", L_MSSQL, "mssql"}, {L"gdscript", L"GDScript", L"GDScript file", L_GDSCRIPT, "gdscript"}, {L"hollywood", L"Hollywood", L"Hollywood script", L_HOLLYWOOD, "hollywood"}, {L"go", L"Go", L"Go source file", L_GOLANG, "cpp"}, {L"raku", L"Raku", L"Raku source file", L_RAKU, "raku"}, {L"toml", L"TOML", L"Tom's Obvious Minimal Language file", L_TOML, "toml"}, {L"ext", L"External", L"External", L_EXTERNAL, "null"} }; size_t getNbDigits(size_t aNum, size_t base) { size_t nbDigits = 0; do { ++nbDigits; aNum /= base; } while (aNum != 0); return nbDigits; } bool isCharSingleQuote(__inout wchar_t const c) { if (c == L'\'' || c == L'\u2019' || c == L'\u2018') return true; else return false; } void ScintillaEditView::init(HINSTANCE hInst, HWND hPere) { if (!_SciInit) { if (!Scintilla_RegisterClasses(hInst)) { throw std::runtime_error("ScintillaEditView::init : SCINTILLA ERROR - Scintilla_RegisterClasses failed"); } _SciInit = true; } Window::init(hInst, hPere); _hSelf = ::CreateWindowEx( 0,\ L"Scintilla",\ L"Notepad++",\ WS_CHILD | WS_VSCROLL | WS_HSCROLL | WS_CLIPCHILDREN | WS_EX_RTLREADING,\ 0, 0, 100, 100,\ _hParent,\ NULL,\ _hInst,\ NULL); if (!_hSelf) { throw std::runtime_error("ScintillaEditView::init : CreateWindowEx() function return null"); } NppDarkMode::setDarkScrollBar(_hSelf); _pScintillaFunc = (SCINTILLA_FUNC)::SendMessage(_hSelf, SCI_GETDIRECTFUNCTION, 0, 0); _pScintillaPtr = (SCINTILLA_PTR)::SendMessage(_hSelf, SCI_GETDIRECTPOINTER, 0, 0); _userDefineDlg.init(_hInst, _hParent, this); if (!_pScintillaFunc) { throw std::runtime_error("ScintillaEditView::init : SCI_GETDIRECTFUNCTION message failed"); } if (!_pScintillaPtr) { throw std::runtime_error("ScintillaEditView::init : SCI_GETDIRECTPOINTER message failed"); } execute(SCI_SETMODEVENTMASK, MODEVENTMASK_OFF); execute(SCI_SETIDLESTYLING, SC_IDLESTYLING_ALL, 0); execute(SCI_SETMARGINMASKN, _SC_MARGE_FOLDER, SC_MASK_FOLDERS); showMargin(_SC_MARGE_FOLDER, true); execute(SCI_SETMARGINMASKN, _SC_MARGE_SYMBOL, (1 << MARK_BOOKMARK) | (1 << MARK_HIDELINESBEGIN) | (1 << MARK_HIDELINESEND)); execute(SCI_SETMARGINMASKN, _SC_MARGE_CHANGEHISTORY, (1 << SC_MARKNUM_HISTORY_REVERTED_TO_ORIGIN) | (1 << SC_MARKNUM_HISTORY_SAVED) | (1 << SC_MARKNUM_HISTORY_MODIFIED) | (1 << SC_MARKNUM_HISTORY_REVERTED_TO_MODIFIED)); COLORREF modifiedColor = RGB(255, 128, 0); //COLORREF savedColor = RGB(0, 255, 0); //COLORREF revertedToModifiedColor = RGB(255, 255, 0); //COLORREF revertedToOriginColor = RGB(0, 0, 255); execute(SCI_MARKERSETBACK, SC_MARKNUM_HISTORY_MODIFIED, modifiedColor); //execute(SCI_MARKERSETBACK, SC_MARKNUM_HISTORY_SAVED, savedColor); //execute(SCI_MARKERSETBACK, SC_MARKNUM_HISTORY_REVERTED_TO_MODIFIED, revertedToModifiedColor); //execute(SCI_MARKERSETBACK, SC_MARKNUM_HISTORY_REVERTED_TO_ORIGIN, revertedToOriginColor); execute(SCI_MARKERSETALPHA, MARK_BOOKMARK, 70); const COLORREF hiddenLinesGreen = RGB(0x77, 0xCC, 0x77); long hiddenLinesGreenWithAlpha = hiddenLinesGreen | 0xFF000000; setElementColour(SC_ELEMENT_HIDDEN_LINE, hiddenLinesGreenWithAlpha); if (NppParameters::getInstance()._dpiManager.scaleX(100) >= 150) { execute(SCI_RGBAIMAGESETWIDTH, 18); execute(SCI_RGBAIMAGESETHEIGHT, 18); execute(SCI_MARKERDEFINERGBAIMAGE, MARK_BOOKMARK, reinterpret_cast(bookmark18)); execute(SCI_MARKERDEFINERGBAIMAGE, MARK_HIDELINESBEGIN, reinterpret_cast(hidelines_begin18)); execute(SCI_MARKERDEFINERGBAIMAGE, MARK_HIDELINESEND, reinterpret_cast(hidelines_end18)); } else { execute(SCI_RGBAIMAGESETWIDTH, 14); execute(SCI_RGBAIMAGESETHEIGHT, 14); execute(SCI_MARKERDEFINERGBAIMAGE, MARK_BOOKMARK, reinterpret_cast(bookmark14)); execute(SCI_MARKERDEFINERGBAIMAGE, MARK_HIDELINESBEGIN, reinterpret_cast(hidelines_begin14)); execute(SCI_MARKERDEFINERGBAIMAGE, MARK_HIDELINESEND, reinterpret_cast(hidelines_end14)); } execute(SCI_SETMARGINSENSITIVEN, _SC_MARGE_FOLDER, true); // Make margin sensitive for getting notification on mouse click execute(SCI_SETMARGINSENSITIVEN, _SC_MARGE_SYMBOL, true); // Make margin sensitive for getting notification on mouse click execute(SCI_SETFOLDFLAGS, SC_FOLDFLAG_LINEAFTER_CONTRACTED); execute(SCI_SETSCROLLWIDTHTRACKING, true); execute(SCI_SETSCROLLWIDTH, 1); //default empty document: override default width of 2000 // smart hilighting execute(SCI_INDICSETSTYLE, SCE_UNIVERSAL_FOUND_STYLE_SMART, INDIC_ROUNDBOX); execute(SCI_INDICSETSTYLE, SCE_UNIVERSAL_FOUND_STYLE, INDIC_ROUNDBOX); execute(SCI_INDICSETSTYLE, SCE_UNIVERSAL_FOUND_STYLE_INC, INDIC_ROUNDBOX); execute(SCI_INDICSETSTYLE, SCE_UNIVERSAL_TAGMATCH, INDIC_ROUNDBOX); execute(SCI_INDICSETSTYLE, SCE_UNIVERSAL_TAGATTR, INDIC_ROUNDBOX); execute(SCI_INDICSETSTYLE, SCE_UNIVERSAL_FOUND_STYLE_EXT1, INDIC_ROUNDBOX); execute(SCI_INDICSETSTYLE, SCE_UNIVERSAL_FOUND_STYLE_EXT2, INDIC_ROUNDBOX); execute(SCI_INDICSETSTYLE, SCE_UNIVERSAL_FOUND_STYLE_EXT3, INDIC_ROUNDBOX); execute(SCI_INDICSETSTYLE, SCE_UNIVERSAL_FOUND_STYLE_EXT4, INDIC_ROUNDBOX); execute(SCI_INDICSETSTYLE, SCE_UNIVERSAL_FOUND_STYLE_EXT5, INDIC_ROUNDBOX); execute(SCI_INDICSETALPHA, SCE_UNIVERSAL_FOUND_STYLE_SMART, 100); execute(SCI_INDICSETALPHA, SCE_UNIVERSAL_FOUND_STYLE, 100); execute(SCI_INDICSETALPHA, SCE_UNIVERSAL_FOUND_STYLE_INC, 100); execute(SCI_INDICSETALPHA, SCE_UNIVERSAL_TAGMATCH, 100); execute(SCI_INDICSETALPHA, SCE_UNIVERSAL_TAGATTR, 100); execute(SCI_INDICSETALPHA, SCE_UNIVERSAL_FOUND_STYLE_EXT1, 100); execute(SCI_INDICSETALPHA, SCE_UNIVERSAL_FOUND_STYLE_EXT2, 100); execute(SCI_INDICSETALPHA, SCE_UNIVERSAL_FOUND_STYLE_EXT3, 100); execute(SCI_INDICSETALPHA, SCE_UNIVERSAL_FOUND_STYLE_EXT4, 100); execute(SCI_INDICSETALPHA, SCE_UNIVERSAL_FOUND_STYLE_EXT5, 100); execute(SCI_INDICSETUNDER, SCE_UNIVERSAL_FOUND_STYLE_SMART, true); execute(SCI_INDICSETUNDER, SCE_UNIVERSAL_FOUND_STYLE, true); execute(SCI_INDICSETUNDER, SCE_UNIVERSAL_FOUND_STYLE_INC, true); execute(SCI_INDICSETUNDER, SCE_UNIVERSAL_TAGMATCH, true); execute(SCI_INDICSETUNDER, SCE_UNIVERSAL_TAGATTR, true); execute(SCI_INDICSETUNDER, SCE_UNIVERSAL_FOUND_STYLE_EXT1, true); execute(SCI_INDICSETUNDER, SCE_UNIVERSAL_FOUND_STYLE_EXT2, true); execute(SCI_INDICSETUNDER, SCE_UNIVERSAL_FOUND_STYLE_EXT3, true); execute(SCI_INDICSETUNDER, SCE_UNIVERSAL_FOUND_STYLE_EXT4, true); execute(SCI_INDICSETUNDER, SCE_UNIVERSAL_FOUND_STYLE_EXT5, true); NppGUI& nppGui = (NppParameters::getInstance()).getNppGUI(); HMODULE hNtdllModule = ::GetModuleHandle(L"ntdll.dll"); FARPROC isWINE = nullptr; if (hNtdllModule) isWINE = ::GetProcAddress(hNtdllModule, "wine_get_version"); if (isWINE || // There is a performance issue under WINE when DirectWrite is ON, so we turn it off if user uses Notepad++ under WINE ::IsWindowsServer()) // In the case of Windows Server Core, DirectWrite cannot be on. nppGui._writeTechnologyEngine = defaultTechnology; if (nppGui._writeTechnologyEngine == directWriteTechnology) { execute(SCI_SETTECHNOLOGY, SC_TECHNOLOGY_DIRECTWRITE); // If useDirectWrite is turned off, leave the technology setting untouched, // so that existing plugins using SCI_SETTECHNOLOGY behave like before } _codepage = ::GetACP(); ::SetWindowLongPtr(_hSelf, GWLP_USERDATA, reinterpret_cast(this)); _callWindowProc = CallWindowProc; _scintillaDefaultProc = reinterpret_cast(::SetWindowLongPtr(_hSelf, GWLP_WNDPROC, reinterpret_cast(scintillaStatic_Proc))); if (_defaultCharList.empty()) { auto defaultCharListLen = execute(SCI_GETWORDCHARS); char *defaultCharList = new char[defaultCharListLen + 1]; if(defaultCharList) { execute(SCI_GETWORDCHARS, 0, reinterpret_cast(defaultCharList)); defaultCharList[defaultCharListLen] = '\0'; _defaultCharList = defaultCharList; delete[] defaultCharList; } } execute(SCI_SETMODEVENTMASK, MODEVENTMASK_ON); //Get the startup document and make a buffer for it so it can be accessed like a file attachDefaultDoc(); } LRESULT CALLBACK ScintillaEditView::scintillaStatic_Proc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { ScintillaEditView *pScint = (ScintillaEditView *)(::GetWindowLongPtr(hwnd, GWLP_USERDATA)); if (Message == WM_MOUSEWHEEL || Message == WM_MOUSEHWHEEL) { POINT pt{}; POINTS pts = MAKEPOINTS(lParam); POINTSTOPOINT(pt, pts); HWND hwndOnMouse = WindowFromPoint(pt); //Hack for Synaptics TouchPad Driver char synapticsHack[26]{}; GetClassNameA(hwndOnMouse, (LPSTR)&synapticsHack, 26); bool isSynpnatic = std::string(synapticsHack) == "SynTrackCursorWindowClass"; bool makeTouchPadCompetible = ((NppParameters::getInstance()).getSVP())._disableAdvancedScrolling; if (pScint && (isSynpnatic || makeTouchPadCompetible)) return (pScint->scintillaNew_Proc(hwnd, Message, wParam, lParam)); const ScintillaEditView* pScintillaOnMouse = reinterpret_cast(::GetWindowLongPtr(hwndOnMouse, GWLP_USERDATA)); if (pScintillaOnMouse != pScint) return ::SendMessage(hwndOnMouse, Message, wParam, lParam); } if (pScint) return (pScint->scintillaNew_Proc(hwnd, Message, wParam, lParam)); else return ::DefWindowProc(hwnd, Message, wParam, lParam); } LRESULT ScintillaEditView::scintillaNew_Proc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { switch (Message) { case NPPM_INTERNAL_REFRESHDARKMODE: { NppDarkMode::setDarkScrollBar(_hSelf); return TRUE; } case WM_MOUSEHWHEEL : { ::CallWindowProc(_scintillaDefaultProc, hwnd, WM_HSCROLL, ((short)HIWORD(wParam) > 0)?SB_LINERIGHT:SB_LINELEFT, 0); return TRUE; } case WM_MOUSEWHEEL : { if (LOWORD(wParam) & MK_RBUTTON) { ::SendMessage(_hParent, Message, wParam, lParam); return TRUE; } if (LOWORD(wParam) & MK_SHIFT) { // move 3 columns at a time ::CallWindowProc(_scintillaDefaultProc, hwnd, WM_HSCROLL, ((short)HIWORD(wParam) < 0) ? SB_LINERIGHT : SB_LINELEFT, 0); ::CallWindowProc(_scintillaDefaultProc, hwnd, WM_HSCROLL, ((short)HIWORD(wParam) < 0) ? SB_LINERIGHT : SB_LINELEFT, 0); ::CallWindowProc(_scintillaDefaultProc, hwnd, WM_HSCROLL, ((short)HIWORD(wParam) < 0) ? SB_LINERIGHT : SB_LINELEFT, 0); return TRUE; } //Have to perform the scroll first, because the first/last line do not get updated untill after the scroll has been parsed LRESULT scrollResult = ::CallWindowProc(_scintillaDefaultProc, hwnd, Message, wParam, lParam); return scrollResult; } case WM_IME_REQUEST: { if (wParam == IMR_RECONVERTSTRING) { intptr_t textLength = 0; intptr_t selectSize = 0; char smallTextBuffer[128] = { '\0' }; char * selectedStr = smallTextBuffer; RECONVERTSTRING * reconvert = (RECONVERTSTRING *)lParam; // does nothing with a rectangular selection if (execute(SCI_SELECTIONISRECTANGLE, 0, 0)) return 0; // get the codepage of the text size_t cp = execute(SCI_GETCODEPAGE); UINT codepage = static_cast(cp); // get the current text selection Sci_CharacterRangeFull range = getSelection(); if (range.cpMax == range.cpMin) { // no selection: select the current word instead expandWordSelection(); range = getSelection(); } selectSize = range.cpMax - range.cpMin; // does nothing if still no luck with the selection if (selectSize == 0) return 0; if (static_cast(selectSize + 1) > sizeof(smallTextBuffer)) selectedStr = new char[selectSize + 1]; getText(selectedStr, range.cpMin, range.cpMax); if (reconvert == NULL) { // convert the selection to Unicode, and get the number // of bytes required for the converted text textLength = sizeof(wchar_t) * ::MultiByteToWideChar(codepage, 0, selectedStr, (int)selectSize, NULL, 0); } else { // convert the selection to Unicode, and store it at the end of the structure. // Beware: For a Unicode IME, dwStrLen , dwCompStrLen, and dwTargetStrLen // are wchar_t values, that is, character counts. The members dwStrOffset, // dwCompStrOffset, and dwTargetStrOffset specify byte counts. textLength = ::MultiByteToWideChar( codepage, 0, selectedStr, (int)selectSize, (LPWSTR)((LPSTR)reconvert + sizeof(RECONVERTSTRING)), static_cast(reconvert->dwSize - sizeof(RECONVERTSTRING))); // fill the structure reconvert->dwVersion = 0; reconvert->dwStrLen = static_cast(textLength); reconvert->dwStrOffset = sizeof(RECONVERTSTRING); reconvert->dwCompStrLen = static_cast(textLength); reconvert->dwCompStrOffset = 0; reconvert->dwTargetStrLen = reconvert->dwCompStrLen; reconvert->dwTargetStrOffset = reconvert->dwCompStrOffset; textLength *= sizeof(wchar_t); } if (selectedStr != smallTextBuffer) delete [] selectedStr; // return the total length of the structure return sizeof(RECONVERTSTRING) + textLength; } break; } case WM_CHAR: { // prevent "control characters" from being entered in text // (don't need to be concerned about Tab or CR or LF etc here) if ((NppParameters::getInstance()).getSVP()._npcNoInputC0 && (wParam <= 31 || wParam == 127)) { return FALSE; } break; } case WM_KEYUP: { if (wParam == VK_PRIOR || wParam == VK_NEXT) { // find hotspots SCNotification notification = {}; notification.nmhdr.code = SCN_PAINTED; notification.nmhdr.hwndFrom = _hSelf; notification.nmhdr.idFrom = ::GetDlgCtrlID(_hSelf); ::SendMessage(_hParent, WM_NOTIFY, LINKTRIGGERED, reinterpret_cast(¬ification)); } break; } case WM_KEYDOWN: { struct MultiCaretInfo { int _len2remove; size_t _selIndex; MultiCaretInfo(int len, size_t n) : _len2remove(len), _selIndex(n) {}; }; SHORT ctrl = GetKeyState(VK_CONTROL); SHORT alt = GetKeyState(VK_MENU); SHORT shift = GetKeyState(VK_SHIFT); bool isColumnSelection = (execute(SCI_GETSELECTIONMODE) == SC_SEL_RECTANGLE) || (execute(SCI_GETSELECTIONMODE) == SC_SEL_THIN); bool column2MultSelect = (NppParameters::getInstance()).getSVP()._columnSel2MultiEdit; if (wParam == VK_DELETE) { if (!(shift & 0x8000) && !(ctrl & 0x8000) && !(alt & 0x8000)) // DEL & Multi-edit { size_t nbSelections = execute(SCI_GETSELECTIONS); if (nbSelections > 1) // Multi-edit { vector edgeOfEol; // parir , pair int nbCaseForScint = 0; for (size_t i = 0; i < nbSelections; ++i) { LRESULT posStart = execute(SCI_GETSELECTIONNSTART, i); LRESULT posEnd = execute(SCI_GETSELECTIONNEND, i); if (posStart != posEnd) { ++nbCaseForScint; } else // posStart == posEnd) { size_t docLen = getCurrentDocLen(); char eolStr[3]; Sci_TextRangeFull tr; tr.chrg.cpMin = posStart; tr.chrg.cpMax = posEnd + 2; if (tr.chrg.cpMax > static_cast(docLen)) { tr.chrg.cpMax = docLen; } tr.lpstrText = eolStr; if (tr.chrg.cpMin != tr.chrg.cpMax) execute(SCI_GETTEXTRANGEFULL, 0, reinterpret_cast(&tr)); // Remember EOL length // in the case of other characters let Scintilla do its job int len2remove = -1; if (eolStr[0] == '\r' && eolStr[1] == '\n') len2remove = 2; else if (eolStr[0] == '\r' || eolStr[0] == '\n') len2remove = 1; if (len2remove == -1) ++nbCaseForScint; else edgeOfEol.push_back(MultiCaretInfo(len2remove, i)); } } execute(SCI_BEGINUNDOACTION); // Let Scitilla do its job, if any if (nbCaseForScint) _callWindowProc(_scintillaDefaultProc, hwnd, Message, wParam, lParam); // then do our job, if it's not column mode if (!isColumnSelection) { for (const auto& i : edgeOfEol) { // because the current caret modification will change the other caret positions, // so we get them dynamically in the loop. LRESULT posStart = execute(SCI_GETSELECTIONNSTART, i._selIndex); LRESULT posEnd = execute(SCI_GETSELECTIONNEND, i._selIndex); replaceTarget(L"", posStart, posEnd + i._len2remove); execute(SCI_SETSELECTIONNSTART, i._selIndex, posStart); execute(SCI_SETSELECTIONNEND, i._selIndex, posStart); } } execute(SCI_ENDUNDOACTION); return TRUE; } } } else if (isColumnSelection && column2MultSelect) { // // Transform the column selection to multi-edit // switch (wParam) { case VK_LEFT: case VK_RIGHT: case VK_UP: case VK_DOWN: case VK_HOME: case VK_END: case VK_RETURN: case VK_BACK: execute(SCI_SETSELECTIONMODE, SC_SEL_STREAM); // When it's rectangular selection and the arrow keys are pressed, we switch the mode for having multiple carets. execute(SCI_SETSELECTIONMODE, SC_SEL_STREAM); // the 2nd call for removing the unwanted selection while moving carets. // Solution suggested by Neil Hodgson. See: // https://sourceforge.net/p/scintilla/bugs/2412/ break; case VK_ESCAPE: { int selection = static_cast(execute(SCI_GETMAINSELECTION, 0, 0)); int caret = static_cast(execute(SCI_GETSELECTIONNCARET, selection, 0)); execute(SCI_SETSELECTION, caret, caret); execute(SCI_SETSELECTIONMODE, SC_SEL_STREAM); break; } default: break; } } break; } case WM_VSCROLL : { break; } case WM_RBUTTONDOWN: { bool rightClickKeepsSelection = ((NppParameters::getInstance()).getSVP())._rightClickKeepsSelection; if (rightClickKeepsSelection) { size_t clickX = GET_X_LPARAM(lParam); size_t marginX = execute(SCI_POINTXFROMPOSITION, 0, 0); if (clickX >= marginX) { // if right-click in the editing area (not the margins!), // don't let this go to Scintilla because it will // move the caret to the right-clicked location, // cancelling any selection made by the user return TRUE; } } break; } } return _callWindowProc(_scintillaDefaultProc, hwnd, Message, wParam, lParam); } #define DEFAULT_FONT_NAME "Courier New" void ScintillaEditView::setSpecialStyle(const Style & styleToSet) { int styleID = styleToSet._styleID; if ( styleToSet._colorStyle & COLORSTYLE_FOREGROUND ) execute(SCI_STYLESETFORE, styleID, styleToSet._fgColor); if ( styleToSet._colorStyle & COLORSTYLE_BACKGROUND ) execute(SCI_STYLESETBACK, styleID, styleToSet._bgColor); if (!styleToSet._fontName.empty()) { if (!NppParameters::getInstance().isInFontList(styleToSet._fontName)) { execute(SCI_STYLESETFONT, styleID, reinterpret_cast(DEFAULT_FONT_NAME)); } else { std::string fontNameA = wstring2string(styleToSet._fontName, CP_UTF8); execute(SCI_STYLESETFONT, styleID, reinterpret_cast(fontNameA.c_str())); } } int fontStyle = styleToSet._fontStyle; if (fontStyle != STYLE_NOT_USED) { execute(SCI_STYLESETBOLD, styleID, fontStyle & FONTSTYLE_BOLD); execute(SCI_STYLESETITALIC, styleID, fontStyle & FONTSTYLE_ITALIC); execute(SCI_STYLESETUNDERLINE, styleID, fontStyle & FONTSTYLE_UNDERLINE); } if (styleToSet._fontSize > 0) execute(SCI_STYLESETSIZE, styleID, styleToSet._fontSize); } void ScintillaEditView::setHotspotStyle(const Style& styleToSet) { StyleMap* styleMap; if ( _hotspotStyles.find(_currentBuffer) == _hotspotStyles.end() ) { _hotspotStyles[_currentBuffer] = new StyleMap; } styleMap = _hotspotStyles[_currentBuffer]; (*styleMap)[styleToSet._styleID] = styleToSet; setStyle(styleToSet); } void ScintillaEditView::setStyle(Style styleToSet) { GlobalOverride & go = NppParameters::getInstance().getGlobalOverrideStyle(); if (go.isEnable()) { const Style * pStyle = NppParameters::getInstance().getMiscStylerArray().findByName(L"Global override"); if (pStyle) { if (go.enableFg) { if (pStyle->_colorStyle & COLORSTYLE_FOREGROUND) { styleToSet._colorStyle |= COLORSTYLE_FOREGROUND; styleToSet._fgColor = pStyle->_fgColor; } else { if (styleToSet._styleID == STYLE_DEFAULT) //if global is set to transparent, use default style color styleToSet._colorStyle |= COLORSTYLE_FOREGROUND; else styleToSet._colorStyle &= ~COLORSTYLE_FOREGROUND; } } if (go.enableBg) { if (pStyle->_colorStyle & COLORSTYLE_BACKGROUND) { styleToSet._colorStyle |= COLORSTYLE_BACKGROUND; styleToSet._bgColor = pStyle->_bgColor; } else { if (styleToSet._styleID == STYLE_DEFAULT) //if global is set to transparent, use default style color styleToSet._colorStyle |= COLORSTYLE_BACKGROUND; else styleToSet._colorStyle &= ~COLORSTYLE_BACKGROUND; } } if (go.enableFont && !pStyle->_fontName.empty()) styleToSet._fontName = pStyle->_fontName; if (go.enableFontSize && (pStyle->_fontSize > 0)) styleToSet._fontSize = pStyle->_fontSize; if (pStyle->_fontStyle != STYLE_NOT_USED) { if (go.enableBold) { if (pStyle->_fontStyle & FONTSTYLE_BOLD) styleToSet._fontStyle |= FONTSTYLE_BOLD; else styleToSet._fontStyle &= ~FONTSTYLE_BOLD; } if (go.enableItalic) { if (pStyle->_fontStyle & FONTSTYLE_ITALIC) styleToSet._fontStyle |= FONTSTYLE_ITALIC; else styleToSet._fontStyle &= ~FONTSTYLE_ITALIC; } if (go.enableUnderLine) { if (pStyle->_fontStyle & FONTSTYLE_UNDERLINE) styleToSet._fontStyle |= FONTSTYLE_UNDERLINE; else styleToSet._fontStyle &= ~FONTSTYLE_UNDERLINE; } } } } setSpecialStyle(styleToSet); } void ScintillaEditView::setXmlLexer(LangType type) { if (type == L_XML) { const wchar_t *pKwArray[NB_LIST] = {NULL}; setLexerFromLangID(L_XML); for (int i = 0 ; i < 4 ; ++i) execute(SCI_SETKEYWORDS, i, reinterpret_cast(L"")); makeStyle(type, pKwArray); // the XML portion of the lexer only allows substyles for attributes, not for tags (since it treats all tags the same), // so allocate all 8 substyles to attributes populateSubStyleKeywords(type, SCE_H_ATTRIBUTE, 8, LANG_INDEX_SUBSTYLE1, pKwArray); execute(SCI_SETPROPERTY, reinterpret_cast("lexer.xml.allow.scripts"), reinterpret_cast("0")); } else if ((type == L_HTML) || (type == L_PHP) || (type == L_ASP) || (type == L_JSP)) { setLexerFromLangID(L_HTML); setHTMLLexer(); setEmbeddedJSLexer(); setEmbeddedPhpLexer(); setEmbeddedAspLexer(); } execute(SCI_SETPROPERTY, reinterpret_cast("fold"), reinterpret_cast("1")); execute(SCI_SETPROPERTY, reinterpret_cast("fold.compact"), reinterpret_cast("0")); execute(SCI_SETPROPERTY, reinterpret_cast("fold.html"), reinterpret_cast("1")); // This allow to fold comment strem in php/javascript code execute(SCI_SETPROPERTY, reinterpret_cast("fold.hypertext.comment"), reinterpret_cast("1")); } void ScintillaEditView::setHTMLLexer() { const wchar_t *pKwArray[NB_LIST] = {NULL}; makeStyle(L_HTML, pKwArray); basic_string keywordList(""); if (pKwArray[LANG_INDEX_INSTR]) { basic_string kwlW = pKwArray[LANG_INDEX_INSTR]; keywordList = wstring2string(kwlW, CP_ACP); } execute(SCI_SETKEYWORDS, 0, reinterpret_cast(getCompleteKeywordList(keywordList, L_HTML, LANG_INDEX_INSTR))); // HTML allows substyle lists for both tags and attributes, so allocate four of each populateSubStyleKeywords(L_HTML, SCE_H_TAG, 4, LANG_INDEX_SUBSTYLE1, pKwArray); populateSubStyleKeywords(L_HTML, SCE_H_ATTRIBUTE, 4, LANG_INDEX_SUBSTYLE5, pKwArray); } void ScintillaEditView::setEmbeddedJSLexer() { const wchar_t *pKwArray[NB_LIST] = {NULL}; makeStyle(L_JS, pKwArray); basic_string keywordList(""); if (pKwArray[LANG_INDEX_INSTR]) { basic_string kwlW = pKwArray[LANG_INDEX_INSTR]; keywordList = wstring2string(kwlW, CP_ACP); } execute(SCI_SETKEYWORDS, 1, reinterpret_cast(getCompleteKeywordList(keywordList, L_JS, LANG_INDEX_INSTR))); populateSubStyleKeywords(L_JS, SCE_HJ_WORD, 8, LANG_INDEX_SUBSTYLE1, pKwArray); execute(SCI_STYLESETEOLFILLED, SCE_HJ_DEFAULT, true); execute(SCI_STYLESETEOLFILLED, SCE_HJ_COMMENT, true); execute(SCI_STYLESETEOLFILLED, SCE_HJ_COMMENTDOC, true); execute(SCI_STYLESETEOLFILLED, SCE_HJ_TEMPLATELITERAL, true); execute(SCI_STYLESETEOLFILLED, SCE_HJA_TEMPLATELITERAL, true); } void ScintillaEditView::setJsonLexer(bool isJson5) { setLexerFromLangID(isJson5 ? L_JSON5 : L_JSON); const wchar_t *pKwArray[NB_LIST] = {NULL}; makeStyle(L_JSON, pKwArray); string keywordList; string keywordList2; if (pKwArray[LANG_INDEX_INSTR]) { wstring kwlW = pKwArray[LANG_INDEX_INSTR]; keywordList = wstring2string(kwlW, CP_ACP); } if (pKwArray[LANG_INDEX_INSTR2]) { wstring kwlW = pKwArray[LANG_INDEX_INSTR2]; keywordList2 = wstring2string(kwlW, CP_ACP); } execute(SCI_SETKEYWORDS, 0, reinterpret_cast(getCompleteKeywordList(keywordList, L_JSON, LANG_INDEX_INSTR))); execute(SCI_SETKEYWORDS, 1, reinterpret_cast(getCompleteKeywordList(keywordList2, L_JSON, LANG_INDEX_INSTR2))); execute(SCI_SETPROPERTY, reinterpret_cast("fold"), reinterpret_cast("1")); execute(SCI_SETPROPERTY, reinterpret_cast("fold.compact"), reinterpret_cast("0")); execute(SCI_SETPROPERTY, reinterpret_cast("lexer.json.escape.sequence"), reinterpret_cast("1")); if (isJson5) execute(SCI_SETPROPERTY, reinterpret_cast("lexer.json.allow.comments"), reinterpret_cast("1")); } void ScintillaEditView::setEmbeddedPhpLexer() { const wchar_t *pKwArray[NB_LIST] = {NULL}; makeStyle(L_PHP, pKwArray); basic_string keywordList(""); if (pKwArray[LANG_INDEX_INSTR]) { basic_string kwlW = pKwArray[LANG_INDEX_INSTR]; keywordList = wstring2string(kwlW, CP_ACP); } execute(SCI_SETKEYWORDS, 4, reinterpret_cast(getCompleteKeywordList(keywordList, L_PHP, LANG_INDEX_INSTR))); populateSubStyleKeywords(L_PHP, SCE_HPHP_WORD, 8, LANG_INDEX_SUBSTYLE1, pKwArray); execute(SCI_STYLESETEOLFILLED, SCE_HPHP_DEFAULT, true); execute(SCI_STYLESETEOLFILLED, SCE_HPHP_COMMENT, true); } void ScintillaEditView::setEmbeddedAspLexer() { const wchar_t *pKwArray[NB_LIST] = {NULL}; makeStyle(L_ASP, pKwArray); basic_string keywordList(""); if (pKwArray[LANG_INDEX_INSTR]) { basic_string kwlW = pKwArray[LANG_INDEX_INSTR]; keywordList = wstring2string(kwlW, CP_ACP); } execute(SCI_SETPROPERTY, reinterpret_cast("asp.default.language"), reinterpret_cast("2")); execute(SCI_SETKEYWORDS, 2, reinterpret_cast(getCompleteKeywordList(keywordList, L_VB, LANG_INDEX_INSTR))); populateSubStyleKeywords(L_ASP, SCE_HB_WORD, 8, LANG_INDEX_SUBSTYLE1, pKwArray); execute(SCI_STYLESETEOLFILLED, SCE_HBA_DEFAULT, true); } void ScintillaEditView::setUserLexer(const wchar_t *userLangName) { int setKeywordsCounter = 0; setLexerFromLangID(L_USER); UserLangContainer * userLangContainer = userLangName? NppParameters::getInstance().getULCFromName(userLangName):_userDefineDlg._pCurrentUserLang; if (!userLangContainer) return; UINT codepage = CP_ACP; UniMode unicodeMode = _currentBuffer->getUnicodeMode(); int encoding = _currentBuffer->getEncoding(); if (encoding == -1) { if (unicodeMode == uniUTF8 || unicodeMode == uniCookie) codepage = CP_UTF8; } else { codepage = CP_OEMCP; // system OEM code page might not match user selection for character set, // but this is the best match WideCharToMultiByte offers } execute(SCI_SETPROPERTY, reinterpret_cast("fold"), reinterpret_cast("1")); execute(SCI_SETPROPERTY, reinterpret_cast("userDefine.isCaseIgnored"), reinterpret_cast(userLangContainer->_isCaseIgnored ? "1":"0")); execute(SCI_SETPROPERTY, reinterpret_cast("userDefine.allowFoldOfComments"), reinterpret_cast(userLangContainer->_allowFoldOfComments ? "1":"0")); execute(SCI_SETPROPERTY, reinterpret_cast("userDefine.foldCompact"), reinterpret_cast(userLangContainer->_foldCompact ? "1":"0")); char name[] = "userDefine.prefixKeywords0"; for (int i=0 ; i(name), reinterpret_cast(userLangContainer->_isPrefix[i] ? "1" : "0")); } char* temp = new char[max_char]; for (int i = 0 ; i < SCE_USER_KWLIST_TOTAL ; ++i) { WcharMbcsConvertor& wmc = WcharMbcsConvertor::getInstance(); const char * keyWords_char = wmc.wchar2char(userLangContainer->_keywordLists[i], codepage); if (globalMappper().setLexerMapper.find(i) != globalMappper().setLexerMapper.end()) { execute(SCI_SETPROPERTY, reinterpret_cast(globalMappper().setLexerMapper[i].c_str()), reinterpret_cast(keyWords_char)); } else // OPERATORS2, FOLDERS_IN_CODE2, FOLDERS_IN_COMMENT, KEYWORDS1-8 { bool inDoubleQuote = false; bool inSingleQuote = false; bool nonWSFound = false; int index = 0; for (size_t j=0, len = strlen(keyWords_char); j ' ') // copy non-whitespace unconditionally { temp[index++] = keyWords_char[j]; if (nonWSFound == false) nonWSFound = true; } else if (nonWSFound == true && keyWords_char[j-1] != '"' && keyWords_char[j+1] != '"' && keyWords_char[j+1] > ' ') { temp[index++] = inDoubleQuote ? '\v' : '\b'; } else continue; } else { temp[index++] = keyWords_char[j]; } } temp[index++] = 0; execute(SCI_SETKEYWORDS, setKeywordsCounter++, reinterpret_cast(temp)); } } delete[] temp; char intBuffer[32]; sprintf(intBuffer, "%d", userLangContainer->_forcePureLC); execute(SCI_SETPROPERTY, reinterpret_cast("userDefine.forcePureLC"), reinterpret_cast(intBuffer)); sprintf(intBuffer, "%d", userLangContainer->_decimalSeparator); execute(SCI_SETPROPERTY, reinterpret_cast("userDefine.decimalSeparator"), reinterpret_cast(intBuffer)); // at the end (position SCE_USER_KWLIST_TOTAL) send id values sprintf(intBuffer, "%" PRIuPTR, reinterpret_cast(userLangContainer->getName())); // use numeric value of wchar_t pointer execute(SCI_SETPROPERTY, reinterpret_cast("userDefine.udlName"), reinterpret_cast(intBuffer)); sprintf(intBuffer, "%" PRIuPTR, reinterpret_cast(_currentBufferID)); // use numeric value of BufferID pointer execute(SCI_SETPROPERTY, reinterpret_cast("userDefine.currentBufferID"), reinterpret_cast(intBuffer)); for (const Style & style : userLangContainer->_styles) { if (style._styleID == STYLE_NOT_USED) continue; char nestingBuffer[32]; sprintf(nestingBuffer, "userDefine.nesting.%02d", style._styleID); sprintf(intBuffer, "%d", style._nesting); execute(SCI_SETPROPERTY, reinterpret_cast(nestingBuffer), reinterpret_cast(intBuffer)); setStyle(style); } } void ScintillaEditView::setExternalLexer(LangType typeDoc) { int id = typeDoc - L_EXTERNAL; ExternalLangContainer& externalLexer = NppParameters::getInstance().getELCFromIndex(id); if (!externalLexer.fnCL) return; ILexer5* iLex5 = externalLexer.fnCL(externalLexer._name.c_str()); if (!iLex5) return; execute(SCI_SETILEXER, 0, reinterpret_cast(iLex5)); ::SendMessage(_hParent, NPPM_INTERNAL_EXTERNALLEXERBUFFER, 0, (LPARAM)getCurrentBufferID()); WcharMbcsConvertor& wmc = WcharMbcsConvertor::getInstance(); const wchar_t* lexerNameW = wmc.char2wchar(externalLexer._name.c_str(), CP_ACP); LexerStyler *pStyler = (NppParameters::getInstance().getLStylerArray()).getLexerStylerByName(lexerNameW); if (pStyler) { for (const Style & style : *pStyler) { setStyle(style); if (style._keywordClass >= 0 && style._keywordClass <= KEYWORDSET_MAX) { basic_string keywordList(""); if (!style._keywords.empty()) { keywordList = wstring2string(style._keywords, CP_ACP); } execute(SCI_SETKEYWORDS, style._keywordClass, reinterpret_cast(getCompleteKeywordList(keywordList, typeDoc, style._keywordClass))); } } } } void ScintillaEditView::setCppLexer(LangType langType) { const char *cppInstrs; const char *cppTypes; const char *cppGlobalclass; const wchar_t *doxygenKeyWords = NppParameters::getInstance().getWordList(L_CPP, LANG_INDEX_TYPE2); setLexerFromLangID(L_CPP); if (langType != L_RC) { if (doxygenKeyWords) { WcharMbcsConvertor& wmc = WcharMbcsConvertor::getInstance(); const char * doxygenKeyWords_char = wmc.wchar2char(doxygenKeyWords, CP_ACP); execute(SCI_SETKEYWORDS, 2, reinterpret_cast(doxygenKeyWords_char)); } } const wchar_t *pKwArray[NB_LIST] = {NULL}; makeStyle(langType, pKwArray); basic_string keywordListInstruction(""); basic_string keywordListType(""); basic_string keywordListGlobalclass(""); if (pKwArray[LANG_INDEX_INSTR]) { basic_string kwlW = pKwArray[LANG_INDEX_INSTR]; keywordListInstruction = wstring2string(kwlW, CP_ACP); } cppInstrs = getCompleteKeywordList(keywordListInstruction, langType, LANG_INDEX_INSTR); if (pKwArray[LANG_INDEX_TYPE]) { basic_string kwlW = pKwArray[LANG_INDEX_TYPE]; keywordListType = wstring2string(kwlW, CP_ACP); } cppTypes = getCompleteKeywordList(keywordListType, langType, LANG_INDEX_TYPE); if (pKwArray[LANG_INDEX_INSTR2]) { basic_string kwlW = pKwArray[LANG_INDEX_INSTR2]; keywordListGlobalclass = wstring2string(kwlW, CP_ACP); } cppGlobalclass = getCompleteKeywordList(keywordListGlobalclass, langType, LANG_INDEX_INSTR2); execute(SCI_SETKEYWORDS, 0, reinterpret_cast(cppInstrs)); execute(SCI_SETKEYWORDS, 1, reinterpret_cast(cppTypes)); execute(SCI_SETKEYWORDS, 3, reinterpret_cast(cppGlobalclass)); populateSubStyleKeywords(langType, SCE_C_IDENTIFIER, 8, LANG_INDEX_SUBSTYLE1, pKwArray); execute(SCI_SETPROPERTY, reinterpret_cast("fold"), reinterpret_cast("1")); execute(SCI_SETPROPERTY, reinterpret_cast("fold.compact"), reinterpret_cast("0")); execute(SCI_SETPROPERTY, reinterpret_cast("fold.comment"), reinterpret_cast("1")); execute(SCI_SETPROPERTY, reinterpret_cast("fold.cpp.comment.explicit"), reinterpret_cast("0")); execute(SCI_SETPROPERTY, reinterpret_cast("fold.preprocessor"), reinterpret_cast("1")); // Disable track preprocessor to avoid incorrect detection. // In the most of cases, the symbols are defined outside of file. execute(SCI_SETPROPERTY, reinterpret_cast("lexer.cpp.track.preprocessor"), reinterpret_cast("0")); } void ScintillaEditView::setJsLexer() { const wchar_t *doxygenKeyWords = NppParameters::getInstance().getWordList(L_CPP, LANG_INDEX_TYPE2); setLexerFromLangID(L_JAVASCRIPT); const wchar_t *pKwArray[NB_LIST] = {NULL}; makeStyle(L_JAVASCRIPT, pKwArray); if (doxygenKeyWords) { WcharMbcsConvertor& wmc = WcharMbcsConvertor::getInstance(); const char * doxygenKeyWords_char = wmc.wchar2char(doxygenKeyWords, CP_ACP); execute(SCI_SETKEYWORDS, 2, reinterpret_cast(doxygenKeyWords_char)); } const wchar_t *newLexerName = ScintillaEditView::_langNameInfoArray[L_JAVASCRIPT]._langName; LexerStyler *pNewStyler = (NppParameters::getInstance().getLStylerArray()).getLexerStylerByName(newLexerName); if (pNewStyler) // New js styler is available, so we can use it do more modern styling { for (const Style & style : *pNewStyler) { setStyle(style); } basic_string keywordListInstruction(""); basic_string keywordListType(""); basic_string keywordListInstruction2(""); if (pKwArray[LANG_INDEX_INSTR]) { basic_string kwlW = pKwArray[LANG_INDEX_INSTR]; keywordListInstruction = wstring2string(kwlW, CP_ACP); } const char *jsInstrs = getCompleteKeywordList(keywordListInstruction, L_JAVASCRIPT, LANG_INDEX_INSTR); if (pKwArray[LANG_INDEX_TYPE]) { basic_string kwlW = pKwArray[LANG_INDEX_TYPE]; keywordListType = wstring2string(kwlW, CP_ACP); } const char *jsTypes = getCompleteKeywordList(keywordListType, L_JAVASCRIPT, LANG_INDEX_TYPE); if (pKwArray[LANG_INDEX_INSTR2]) { basic_string kwlW = pKwArray[LANG_INDEX_INSTR2]; keywordListInstruction2 = wstring2string(kwlW, CP_ACP); } const char *jsInstrs2 = getCompleteKeywordList(keywordListInstruction2, L_JAVASCRIPT, LANG_INDEX_INSTR2); execute(SCI_SETKEYWORDS, 0, reinterpret_cast(jsInstrs)); execute(SCI_SETKEYWORDS, 1, reinterpret_cast(jsTypes)); execute(SCI_SETKEYWORDS, 3, reinterpret_cast(jsInstrs2)); populateSubStyleKeywords(L_JAVASCRIPT, SCE_C_IDENTIFIER, 8, LANG_INDEX_SUBSTYLE1, pKwArray); } else // New js styler is not available, we use the old styling for the sake of retro-compatibility { const wchar_t *lexerName = ScintillaEditView::_langNameInfoArray[L_JS]._langName; LexerStyler *pOldStyler = (NppParameters::getInstance().getLStylerArray()).getLexerStylerByName(lexerName); if (pOldStyler) { for (Style style : *pOldStyler) //not by reference, but copy { int cppID = style._styleID; switch (style._styleID) { case SCE_HJ_DEFAULT: cppID = SCE_C_DEFAULT; break; case SCE_HJ_WORD: cppID = SCE_C_IDENTIFIER; break; case SCE_HJ_SYMBOLS: cppID = SCE_C_OPERATOR; break; case SCE_HJ_COMMENT: cppID = SCE_C_COMMENT; break; case SCE_HJ_COMMENTLINE: cppID = SCE_C_COMMENTLINE; break; case SCE_HJ_COMMENTDOC: cppID = SCE_C_COMMENTDOC; break; case SCE_HJ_NUMBER: cppID = SCE_C_NUMBER; break; case SCE_HJ_KEYWORD: cppID = SCE_C_WORD; break; case SCE_HJ_DOUBLESTRING: cppID = SCE_C_STRING; break; case SCE_HJ_SINGLESTRING: cppID = SCE_C_CHARACTER; break; case SCE_HJ_REGEX: cppID = SCE_C_REGEX; break; } style._styleID = cppID; setStyle(style); } } execute(SCI_STYLESETEOLFILLED, SCE_C_DEFAULT, true); execute(SCI_STYLESETEOLFILLED, SCE_C_COMMENTLINE, true); execute(SCI_STYLESETEOLFILLED, SCE_C_COMMENT, true); execute(SCI_STYLESETEOLFILLED, SCE_C_COMMENTDOC, true); makeStyle(L_JS, pKwArray); basic_string keywordListInstruction(""); if (pKwArray[LANG_INDEX_INSTR]) { basic_string kwlW = pKwArray[LANG_INDEX_INSTR]; keywordListInstruction = wstring2string(kwlW, CP_ACP); } const char *jsEmbeddedInstrs = getCompleteKeywordList(keywordListInstruction, L_JS, LANG_INDEX_INSTR); execute(SCI_SETKEYWORDS, 0, reinterpret_cast(jsEmbeddedInstrs)); } execute(SCI_SETPROPERTY, reinterpret_cast("fold"), reinterpret_cast("1")); execute(SCI_SETPROPERTY, reinterpret_cast("fold.compact"), reinterpret_cast("0")); execute(SCI_SETPROPERTY, reinterpret_cast("fold.comment"), reinterpret_cast("1")); execute(SCI_SETPROPERTY, reinterpret_cast("fold.cpp.comment.explicit"), reinterpret_cast("0")); execute(SCI_SETPROPERTY, reinterpret_cast("fold.preprocessor"), reinterpret_cast("1")); // Disable track preprocessor to avoid incorrect detection. // In the most of cases, the symbols are defined outside of file. execute(SCI_SETPROPERTY, reinterpret_cast("lexer.cpp.track.preprocessor"), reinterpret_cast("0")); execute(SCI_SETPROPERTY, reinterpret_cast("lexer.cpp.backquoted.strings"), reinterpret_cast("1")); execute(SCI_SETPROPERTY, reinterpret_cast("lexer.cpp.backquoted.strings"), reinterpret_cast("2")); } void ScintillaEditView::setTclLexer() { const char *tclInstrs; const char *tclTypes; setLexerFromLangID(L_TCL); const wchar_t *pKwArray[NB_LIST] = {NULL}; makeStyle(L_TCL, pKwArray); basic_string keywordListInstruction(""); basic_string keywordListType(""); if (pKwArray[LANG_INDEX_INSTR]) { basic_string kwlW = pKwArray[LANG_INDEX_INSTR]; keywordListInstruction = wstring2string(kwlW, CP_ACP); } tclInstrs = getCompleteKeywordList(keywordListInstruction, L_TCL, LANG_INDEX_INSTR); if (pKwArray[LANG_INDEX_TYPE]) { basic_string kwlW = pKwArray[LANG_INDEX_TYPE]; keywordListType = wstring2string(kwlW, CP_ACP); } tclTypes = getCompleteKeywordList(keywordListType, L_TCL, LANG_INDEX_TYPE); execute(SCI_SETKEYWORDS, 0, reinterpret_cast(tclInstrs)); execute(SCI_SETKEYWORDS, 1, reinterpret_cast(tclTypes)); } void ScintillaEditView::setObjCLexer(LangType langType) { setLexerFromLangID(L_OBJC); const wchar_t *pKwArray[NB_LIST] = {NULL}; makeStyle(langType, pKwArray); basic_string objcInstr1Kwl(""); if (pKwArray[LANG_INDEX_INSTR]) { objcInstr1Kwl = wstring2string(pKwArray[LANG_INDEX_INSTR], CP_ACP); } const char *objcInstrs = getCompleteKeywordList(objcInstr1Kwl, langType, LANG_INDEX_INSTR); basic_string objcInstr2Kwl(""); if (pKwArray[LANG_INDEX_INSTR2]) { objcInstr2Kwl = wstring2string(pKwArray[LANG_INDEX_INSTR2], CP_ACP); } const char *objCDirective = getCompleteKeywordList(objcInstr2Kwl, langType, LANG_INDEX_INSTR2); basic_string objcTypeKwl(""); if (pKwArray[LANG_INDEX_TYPE]) { objcTypeKwl = wstring2string(pKwArray[LANG_INDEX_TYPE], CP_ACP); } const char *objcTypes = getCompleteKeywordList(objcTypeKwl, langType, LANG_INDEX_TYPE); basic_string objcType2Kwl(""); if (pKwArray[LANG_INDEX_TYPE2]) { objcType2Kwl = wstring2string(pKwArray[LANG_INDEX_TYPE2], CP_ACP); } const char *objCQualifier = getCompleteKeywordList(objcType2Kwl, langType, LANG_INDEX_TYPE2); basic_string doxygenKeyWordsString(""); const wchar_t *doxygenKeyWordsW = NppParameters::getInstance().getWordList(L_CPP, LANG_INDEX_TYPE2); if (doxygenKeyWordsW) { doxygenKeyWordsString = wstring2string(doxygenKeyWordsW, CP_ACP); } const char *doxygenKeyWords = doxygenKeyWordsString.c_str(); execute(SCI_SETKEYWORDS, 0, reinterpret_cast(objcInstrs)); execute(SCI_SETKEYWORDS, 1, reinterpret_cast(objcTypes)); execute(SCI_SETKEYWORDS, 2, reinterpret_cast(doxygenKeyWords)); execute(SCI_SETKEYWORDS, 3, reinterpret_cast(objCDirective)); execute(SCI_SETKEYWORDS, 4, reinterpret_cast(objCQualifier)); execute(SCI_SETPROPERTY, reinterpret_cast("fold"), reinterpret_cast("1")); execute(SCI_SETPROPERTY, reinterpret_cast("fold.compact"), reinterpret_cast("0")); execute(SCI_SETPROPERTY, reinterpret_cast("fold.comment"), reinterpret_cast("1")); execute(SCI_SETPROPERTY, reinterpret_cast("fold.cpp.comment.explicit"), reinterpret_cast("0")); execute(SCI_SETPROPERTY, reinterpret_cast("fold.preprocessor"), reinterpret_cast("1")); } void ScintillaEditView::setTypeScriptLexer() { const wchar_t* doxygenKeyWords = NppParameters::getInstance().getWordList(L_CPP, LANG_INDEX_TYPE2); setLexerFromLangID(L_TYPESCRIPT); if (doxygenKeyWords) { WcharMbcsConvertor& wmc = WcharMbcsConvertor::getInstance(); const char* doxygenKeyWords_char = wmc.wchar2char(doxygenKeyWords, CP_ACP); execute(SCI_SETKEYWORDS, 2, reinterpret_cast(doxygenKeyWords_char)); } const wchar_t *pKwArray[NB_LIST] = {NULL}; makeStyle(L_TYPESCRIPT, pKwArray); auto getKeywordList = [&pKwArray](const int i) { if (pKwArray[i]) { basic_string kwlW = pKwArray[i]; return wstring2string(kwlW, CP_ACP); } return basic_string(""); }; std::string keywordListInstruction = getKeywordList(LANG_INDEX_INSTR); const char* tsInstructions = getCompleteKeywordList(keywordListInstruction, L_TYPESCRIPT, LANG_INDEX_INSTR); string keywordListType = getKeywordList(LANG_INDEX_TYPE); const char* tsTypes = getCompleteKeywordList(keywordListType, L_TYPESCRIPT, LANG_INDEX_TYPE); execute(SCI_SETKEYWORDS, 0, reinterpret_cast(tsInstructions)); execute(SCI_SETKEYWORDS, 1, reinterpret_cast(tsTypes)); populateSubStyleKeywords(L_TYPESCRIPT, SCE_C_IDENTIFIER, 8, LANG_INDEX_SUBSTYLE1, pKwArray); execute(SCI_SETPROPERTY, reinterpret_cast("fold"), reinterpret_cast("1")); execute(SCI_SETPROPERTY, reinterpret_cast("fold.compact"), reinterpret_cast("0")); execute(SCI_SETPROPERTY, reinterpret_cast("fold.comment"), reinterpret_cast("1")); execute(SCI_SETPROPERTY, reinterpret_cast("fold.cpp.comment.explicit"), reinterpret_cast("0")); execute(SCI_SETPROPERTY, reinterpret_cast("fold.preprocessor"), reinterpret_cast("1")); execute(SCI_SETPROPERTY, reinterpret_cast("lexer.cpp.track.preprocessor"), reinterpret_cast("0")); execute(SCI_SETPROPERTY, reinterpret_cast("lexer.cpp.backquoted.strings"), reinterpret_cast("1")); } void ScintillaEditView::setKeywords(LangType langType, const char *keywords, int index) { std::basic_string wordList; wordList = (keywords)?keywords:""; execute(SCI_SETKEYWORDS, index, reinterpret_cast(getCompleteKeywordList(wordList, langType, index))); } void ScintillaEditView::populateSubStyleKeywords(LangType langType, int baseStyleID, int numSubStyles, int firstLangIndex, const wchar_t **pKwArray) { WcharMbcsConvertor& wmc = WcharMbcsConvertor::getInstance(); int firstID = execute(SCI_ALLOCATESUBSTYLES, baseStyleID, numSubStyles) & 0xFF; if(pKwArray && (firstID>=0)) { for (int i = 0; i < numSubStyles; i++) { int ss = firstLangIndex + i; int styleID = firstID + i; basic_string userWords = pKwArray[ss] ? wmc.wchar2char(pKwArray[ss], CP_ACP) : ""; execute(SCI_SETIDENTIFIERS, styleID, reinterpret_cast(getCompleteKeywordList(userWords, langType, ss))); } } } void ScintillaEditView::setLexer(LangType langType, int whichList, int baseStyleID, int numSubStyles) { setLexerFromLangID(langType); const wchar_t *pKwArray[NB_LIST] = {NULL}; makeStyle(langType, pKwArray); WcharMbcsConvertor& wmc = WcharMbcsConvertor::getInstance(); if (whichList & LIST_0) { const char * keyWords_char = wmc.wchar2char(pKwArray[LANG_INDEX_INSTR], CP_ACP); setKeywords(langType, keyWords_char, LANG_INDEX_INSTR); } if (whichList & LIST_1) { const char * keyWords_char = wmc.wchar2char(pKwArray[LANG_INDEX_INSTR2], CP_ACP); setKeywords(langType, keyWords_char, LANG_INDEX_INSTR2); } if (whichList & LIST_2) { const char * keyWords_char = wmc.wchar2char(pKwArray[LANG_INDEX_TYPE], CP_ACP); setKeywords(langType, keyWords_char, LANG_INDEX_TYPE); } if (whichList & LIST_3) { const char * keyWords_char = wmc.wchar2char(pKwArray[LANG_INDEX_TYPE2], CP_ACP); setKeywords(langType, keyWords_char, LANG_INDEX_TYPE2); } if (whichList & LIST_4) { const char * keyWords_char = wmc.wchar2char(pKwArray[LANG_INDEX_TYPE3], CP_ACP); setKeywords(langType, keyWords_char, LANG_INDEX_TYPE3); } if (whichList & LIST_5) { const char * keyWords_char = wmc.wchar2char(pKwArray[LANG_INDEX_TYPE4], CP_ACP); setKeywords(langType, keyWords_char, LANG_INDEX_TYPE4); } if (whichList & LIST_6) { const char * keyWords_char = wmc.wchar2char(pKwArray[LANG_INDEX_TYPE5], CP_ACP); setKeywords(langType, keyWords_char, LANG_INDEX_TYPE5); } if (whichList & LIST_7) { const char * keyWords_char = wmc.wchar2char(pKwArray[LANG_INDEX_TYPE6], CP_ACP); setKeywords(langType, keyWords_char, LANG_INDEX_TYPE6); } if (whichList & LIST_8) { const char * keyWords_char = wmc.wchar2char(pKwArray[LANG_INDEX_TYPE7], CP_ACP); setKeywords(langType, keyWords_char, LANG_INDEX_TYPE7); } if (baseStyleID != STYLE_NOT_USED) { populateSubStyleKeywords(langType, baseStyleID, numSubStyles, LANG_INDEX_SUBSTYLE1, pKwArray); } execute(SCI_SETPROPERTY, reinterpret_cast("fold"), reinterpret_cast("1")); execute(SCI_SETPROPERTY, reinterpret_cast("fold.compact"), reinterpret_cast("0")); execute(SCI_SETPROPERTY, reinterpret_cast("fold.comment"), reinterpret_cast("1")); } void ScintillaEditView::makeStyle(LangType language, const wchar_t **keywordArray) { const wchar_t * lexerName = ScintillaEditView::_langNameInfoArray[language]._langName; LexerStyler *pStyler = (NppParameters::getInstance().getLStylerArray()).getLexerStylerByName(lexerName); if (pStyler) { for (const Style & style : *pStyler) { setStyle(style); if (keywordArray) { if ((style._keywordClass != STYLE_NOT_USED) && (!style._keywords.empty())) keywordArray[style._keywordClass] = style._keywords.c_str(); } } } } void ScintillaEditView::restoreDefaultWordChars() { execute(SCI_SETWORDCHARS, 0, reinterpret_cast(_defaultCharList.c_str())); } void ScintillaEditView::addCustomWordChars() { NppParameters& nppParam = NppParameters::getInstance(); const NppGUI & nppGUI = nppParam.getNppGUI(); if (nppGUI._customWordChars.empty()) return; string chars2addStr; for (size_t i = 0; i < nppGUI._customWordChars.length(); ++i) { bool found = false; char char2check = nppGUI._customWordChars[i]; for (size_t j = 0; j < _defaultCharList.length(); ++j) { char wordChar = _defaultCharList[j]; if (char2check == wordChar) { found = true; break; } } if (!found) { chars2addStr.push_back(char2check); } } if (!chars2addStr.empty()) { string newCharList = _defaultCharList; newCharList += chars2addStr; execute(SCI_SETWORDCHARS, 0, reinterpret_cast(newCharList.c_str())); } } void ScintillaEditView::setWordChars() { NppParameters& nppParam = NppParameters::getInstance(); const NppGUI & nppGUI = nppParam.getNppGUI(); if (nppGUI._isWordCharDefault) restoreDefaultWordChars(); else addCustomWordChars(); } void ScintillaEditView::setCRLF(long color) { NppParameters& nppParams = NppParameters::getInstance(); const ScintillaViewParams& svp = nppParams.getSVP(); COLORREF eolCustomColor = liteGrey; if (color == -1) { StyleArray& stylers = nppParams.getMiscStylerArray(); Style* pStyle = stylers.findByName(L"EOL custom color"); if (pStyle) { eolCustomColor = pStyle->_fgColor; } } else { eolCustomColor = color; } ScintillaViewParams::crlfMode eolMode = svp._eolMode; long appearance = SC_REPRESENTATION_BLOB; if (eolMode == ScintillaViewParams::crlfMode::plainText) appearance = SC_REPRESENTATION_PLAIN; else if (eolMode == ScintillaViewParams::crlfMode::plainTextCustomColor) appearance = SC_REPRESENTATION_PLAIN | SC_REPRESENTATION_COLOUR; else if (eolMode == ScintillaViewParams::crlfMode::roundedRectangleText) appearance = SC_REPRESENTATION_BLOB; else if (eolMode == ScintillaViewParams::crlfMode::roundedRectangleTextCustomColor) appearance = SC_REPRESENTATION_BLOB | SC_REPRESENTATION_COLOUR; const wchar_t* cr = L"\x0d"; const wchar_t* lf = L"\x0a"; long alphaEolCustomColor = eolCustomColor; alphaEolCustomColor |= 0xFF000000; // add alpha color to make DirectWrite mode work execute(SCI_SETREPRESENTATIONCOLOUR, reinterpret_cast(cr), alphaEolCustomColor); execute(SCI_SETREPRESENTATIONCOLOUR, reinterpret_cast(lf), alphaEolCustomColor); execute(SCI_SETREPRESENTATIONAPPEARANCE, reinterpret_cast(cr), appearance); execute(SCI_SETREPRESENTATIONAPPEARANCE, reinterpret_cast(lf), appearance); redraw(); } void ScintillaEditView::setNpcAndCcUniEOL(long color) { NppParameters& nppParams = NppParameters::getInstance(); const ScintillaViewParams& svp = nppParams.getSVP(); COLORREF npcCustomColor = liteGrey; if (color == -1) { StyleArray& stylers = nppParams.getMiscStylerArray(); Style* pStyle = stylers.findByName(g_npcStyleName); if (pStyle) { npcCustomColor = pStyle->_fgColor; } } else { npcCustomColor = color; } const long appearance = svp._npcCustomColor ? SC_REPRESENTATION_BLOB | SC_REPRESENTATION_COLOUR : SC_REPRESENTATION_BLOB; const long alphaNpcCustomColor = npcCustomColor | 0xFF000000; // add alpha color to make DirectWrite mode work if (svp._npcShow) { for (const auto& invChar : g_nonPrintingChars) { execute(SCI_SETREPRESENTATIONCOLOUR, reinterpret_cast(invChar.at(0)), alphaNpcCustomColor); execute(SCI_SETREPRESENTATIONAPPEARANCE, reinterpret_cast(invChar.at(0)), appearance); } } if (svp._ccUniEolShow && svp._npcIncludeCcUniEol) { for (const auto& invChar : g_ccUniEolChars) { execute(SCI_SETREPRESENTATIONCOLOUR, reinterpret_cast(invChar.at(0)), alphaNpcCustomColor); execute(SCI_SETREPRESENTATIONAPPEARANCE, reinterpret_cast(invChar.at(0)), appearance); } } redraw(); } void ScintillaEditView::defineDocType(LangType typeDoc) { StyleArray & stylers = NppParameters::getInstance().getMiscStylerArray(); Style * pStyleDefault = stylers.findByID(STYLE_DEFAULT); if (pStyleDefault) { pStyleDefault->_colorStyle = COLORSTYLE_ALL; //override transparency setStyle(*pStyleDefault); } execute(SCI_STYLECLEARALL); Style defaultIndicatorStyle; const Style * pStyle; defaultIndicatorStyle._styleID = SCE_UNIVERSAL_FOUND_STYLE; defaultIndicatorStyle._bgColor = red; pStyle = stylers.findByID(defaultIndicatorStyle._styleID); setSpecialIndicator(pStyle ? *pStyle : defaultIndicatorStyle); defaultIndicatorStyle._styleID = SCE_UNIVERSAL_FOUND_STYLE_SMART; defaultIndicatorStyle._bgColor = liteGreen; pStyle = stylers.findByID(defaultIndicatorStyle._styleID); setSpecialIndicator(pStyle ? *pStyle : defaultIndicatorStyle); defaultIndicatorStyle._styleID = SCE_UNIVERSAL_FOUND_STYLE_INC; defaultIndicatorStyle._bgColor = blue; pStyle = stylers.findByID(defaultIndicatorStyle._styleID); setSpecialIndicator(pStyle ? *pStyle : defaultIndicatorStyle); defaultIndicatorStyle._styleID = SCE_UNIVERSAL_TAGMATCH; defaultIndicatorStyle._bgColor = RGB(0x80, 0x00, 0xFF); pStyle = stylers.findByID(defaultIndicatorStyle._styleID); setSpecialIndicator(pStyle ? *pStyle : defaultIndicatorStyle); defaultIndicatorStyle._styleID = SCE_UNIVERSAL_TAGATTR; defaultIndicatorStyle._bgColor = yellow; pStyle = stylers.findByID(defaultIndicatorStyle._styleID); setSpecialIndicator(pStyle ? *pStyle : defaultIndicatorStyle); defaultIndicatorStyle._styleID = SCE_UNIVERSAL_FOUND_STYLE_EXT1; defaultIndicatorStyle._bgColor = cyan; pStyle = stylers.findByID(defaultIndicatorStyle._styleID); setSpecialIndicator(pStyle ? *pStyle : defaultIndicatorStyle); defaultIndicatorStyle._styleID = SCE_UNIVERSAL_FOUND_STYLE_EXT2; defaultIndicatorStyle._bgColor = orange; pStyle = stylers.findByID(defaultIndicatorStyle._styleID); setSpecialIndicator(pStyle ? *pStyle : defaultIndicatorStyle); defaultIndicatorStyle._styleID = SCE_UNIVERSAL_FOUND_STYLE_EXT3; defaultIndicatorStyle._bgColor = yellow; pStyle = stylers.findByID(defaultIndicatorStyle._styleID); setSpecialIndicator(pStyle ? *pStyle : defaultIndicatorStyle); defaultIndicatorStyle._styleID = SCE_UNIVERSAL_FOUND_STYLE_EXT4; defaultIndicatorStyle._bgColor = purple; pStyle = stylers.findByID(defaultIndicatorStyle._styleID); setSpecialIndicator(pStyle ? *pStyle : defaultIndicatorStyle); defaultIndicatorStyle._styleID = SCE_UNIVERSAL_FOUND_STYLE_EXT5; defaultIndicatorStyle._bgColor = darkGreen; pStyle = stylers.findByID(defaultIndicatorStyle._styleID); setSpecialIndicator(pStyle ? *pStyle : defaultIndicatorStyle); // Il faut surtout faire un test ici avant d'exécuter SCI_SETCODEPAGE // Sinon y'aura un soucis de performance! if (isCJK()) { if (getCurrentBuffer()->getUnicodeMode() == uni8Bit) { if (typeDoc == L_CSS || typeDoc == L_CAML || typeDoc == L_ASM || typeDoc == L_MATLAB) execute(SCI_SETCODEPAGE, CP_ACP); else execute(SCI_SETCODEPAGE, _codepage); } } ScintillaViewParams & svp = (ScintillaViewParams &)NppParameters::getInstance().getSVP(); if (svp._folderStyle != FOLDER_STYLE_NONE) showMargin(_SC_MARGE_FOLDER, isNeededFolderMarge(typeDoc)); switch (typeDoc) { case L_C : case L_CPP : case L_JAVA : case L_RC : case L_CS : case L_FLASH : case L_SWIFT: case L_GOLANG: setCppLexer(typeDoc); break; case L_JS: case L_JAVASCRIPT: setJsLexer(); break; case L_TCL : setTclLexer(); break; case L_OBJC : setObjCLexer(typeDoc); break; case L_PHP : case L_ASP : case L_JSP : case L_HTML : case L_XML : setXmlLexer(typeDoc); break; case L_JSON: setJsonLexer(); break; case L_JSON5: setJsonLexer(true); break; case L_CSS : setCssLexer(); break; case L_LUA : setLuaLexer(); break; case L_MAKEFILE : setMakefileLexer(); break; case L_INI : setPropsLexer(false); break; case L_USER : { const wchar_t * langExt = _currentBuffer->getUserDefineLangName(); if (langExt[0]) setUserLexer(langExt); else setUserLexer(); break; } case L_ASCII : { LexerStyler *pStyler = (NppParameters::getInstance().getLStylerArray()).getLexerStylerByName(L"nfo"); Style nfoStyle; nfoStyle._styleID = STYLE_DEFAULT; nfoStyle._fontName = L"Lucida Console"; nfoStyle._fontSize = 10; if (pStyler) { const Style * pDefStyle = pStyler->findByName(L"DEFAULT"); if (pDefStyle) { nfoStyle._bgColor = pDefStyle->_bgColor; nfoStyle._fgColor = pDefStyle->_fgColor; nfoStyle._colorStyle = pDefStyle->_colorStyle; } } setSpecialStyle(nfoStyle); execute(SCI_STYLECLEARALL); Buffer* buf = MainFileManager.getBufferByID(_currentBufferID); if (buf->getEncoding() == NPP_CP_DOS_437 && !buf->isDirty()) { MainFileManager.reloadBuffer(buf); } } break; case L_SQL : setSqlLexer(); break; case L_MSSQL : setMSSqlLexer(); break; case L_VB : setVBLexer(); break; case L_PASCAL : setPascalLexer(); break; case L_PERL : setPerlLexer(); break; case L_PYTHON : setPythonLexer(); break; case L_BATCH : setBatchLexer(); break; case L_TEX : setTeXLexer(); break; case L_NSIS : setNsisLexer(); break; case L_BASH : setBashLexer(); break; case L_FORTRAN : setFortranLexer(); break; case L_FORTRAN_77 : setFortran77Lexer(); break; case L_LISP : setLispLexer(); break; case L_SCHEME : setSchemeLexer(); break; case L_ASM : setAsmLexer(); break; case L_DIFF : setDiffLexer(); break; case L_PROPS : setPropsLexer(); break; case L_PS : setPostscriptLexer(); break; case L_RUBY : setRubyLexer(); break; case L_SMALLTALK : setSmalltalkLexer(); break; case L_VHDL : setVhdlLexer(); break; case L_KIX : setKixLexer(); break; case L_CAML : setCamlLexer(); break; case L_ADA : setAdaLexer(); break; case L_VERILOG : setVerilogLexer(); break; case L_AU3 : setAutoItLexer(); break; case L_MATLAB : setMatlabLexer(); break; case L_HASKELL : setHaskellLexer(); break; case L_INNO : setInnoLexer(); break; case L_CMAKE : setCmakeLexer(); break; case L_YAML : setYamlLexer(); break; case L_COBOL : setCobolLexer(); break; case L_GUI4CLI : setGui4CliLexer(); break; case L_D : setDLexer(); break; case L_POWERSHELL : setPowerShellLexer(); break; case L_R : setRLexer(); break; case L_COFFEESCRIPT : setCoffeeScriptLexer(); break; case L_BAANC: setBaanCLexer(); break; case L_SREC : setSrecLexer(); break; case L_IHEX : setIHexLexer(); break; case L_TEHEX : setTEHexLexer(); break; case L_ASN1 : setAsn1Lexer(); break; case L_AVS : setAVSLexer(); break; case L_BLITZBASIC : setBlitzBasicLexer(); break; case L_PUREBASIC : setPureBasicLexer(); break; case L_FREEBASIC : setFreeBasicLexer(); break; case L_CSOUND : setCsoundLexer(); break; case L_ERLANG : setErlangLexer(); break; case L_ESCRIPT : setESCRIPTLexer(); break; case L_FORTH : setForthLexer(); break; case L_LATEX : setLatexLexer(); break; case L_MMIXAL : setMMIXALLexer(); break; case L_NIM : setNimrodLexer(); break; case L_NNCRONTAB : setNncrontabLexer(); break; case L_OSCRIPT : setOScriptLexer(); break; case L_REBOL : setREBOLLexer(); break; case L_REGISTRY : setRegistryLexer(); break; case L_RUST : setRustLexer(); break; case L_SPICE : setSpiceLexer(); break; case L_TXT2TAGS : setTxt2tagsLexer(); break; case L_VISUALPROLOG: setVisualPrologLexer(); break; case L_TYPESCRIPT: setTypeScriptLexer(); break; case L_GDSCRIPT: setGDScriptLexer(); break; case L_HOLLYWOOD: setHollywoodLexer(); break; case L_RAKU: setRakuLexer(); break; case L_TOML: setTomlLexer(); break; case L_TEXT : default : if (typeDoc >= L_EXTERNAL && typeDoc < NppParameters::getInstance().L_END) setExternalLexer(typeDoc); else setLexerFromLangID(L_TEXT); break; } //All the global styles should put here pStyle = stylers.findByID(STYLE_INDENTGUIDE); if (pStyle) { setStyle(*pStyle); } pStyle = stylers.findByID(STYLE_BRACELIGHT); if (pStyle) { setStyle(*pStyle); } //setStyle(STYLE_CONTROLCHAR, liteGrey); pStyle = stylers.findByID(STYLE_BRACEBAD); if (pStyle) { setStyle(*pStyle); } pStyle = stylers.findByID(STYLE_LINENUMBER); if (pStyle) { setSpecialStyle(*pStyle); } setTabSettings(NppParameters::getInstance().getLangFromID(typeDoc)); if (svp._indentGuideLineShow) { const auto currentIndentMode = execute(SCI_GETINDENTATIONGUIDES); // Python like indentation, excludes lexers (Nim, VB, YAML, etc.) // that includes tailing empty or whitespace only lines in folding block. const int docIndentMode = isPythonStyleIndentation(typeDoc) ? SC_IV_LOOKFORWARD : SC_IV_LOOKBOTH; if (currentIndentMode != docIndentMode) execute(SCI_SETINDENTATIONGUIDES, docIndentMode); } execute(SCI_SETLAYOUTCACHE, SC_CACHE_DOCUMENT, 0); execute(SCI_STARTSTYLING, 0, 0); } Document ScintillaEditView::getBlankDocument() { if(_blankDocument==0) { _blankDocument=static_cast(execute(SCI_CREATEDOCUMENT,0,SC_DOCUMENTOPTION_TEXT_LARGE)); execute(SCI_ADDREFDOCUMENT,0,_blankDocument); } return _blankDocument; } BufferID ScintillaEditView::attachDefaultDoc() { // get the doc pointer attached (by default) on the view Scintilla Document doc = execute(SCI_GETDOCPOINTER, 0, 0); execute(SCI_ADDREFDOCUMENT, 0, doc); BufferID id = MainFileManager.bufferFromDocument(doc, _isMainEditZone); Buffer * buf = MainFileManager.getBufferByID(id); MainFileManager.addBufferReference(id, this); //add a reference. Notepad only shows the buffer in tabbar _currentBufferID = id; _currentBuffer = buf; bufferUpdated(buf, BufferChangeMask); //make sure everything is in sync with the buffer, since no reference exists return id; } void ScintillaEditView::saveCurrentPos() { //Save data so, that the current topline becomes visible again after restoring. size_t displayedLine = execute(SCI_GETFIRSTVISIBLELINE); size_t docLine = execute(SCI_DOCLINEFROMVISIBLE, displayedLine); //linenumber of the line displayed in the top size_t offset = displayedLine - execute(SCI_VISIBLEFROMDOCLINE, docLine); //use this to calc offset of wrap. If no wrap this should be zero size_t wrapCount = execute(SCI_WRAPCOUNT, docLine); Buffer * buf = MainFileManager.getBufferByID(_currentBufferID); Position pos; // the correct visible line number pos._firstVisibleLine = docLine; pos._startPos = execute(SCI_GETANCHOR); pos._endPos = execute(SCI_GETCURRENTPOS); pos._xOffset = execute(SCI_GETXOFFSET); pos._selMode = execute(SCI_GETSELECTIONMODE); pos._scrollWidth = execute(SCI_GETSCROLLWIDTH); pos._offset = offset; pos._wrapCount = wrapCount; buf->setPosition(pos, this); } // restore current position is executed in two steps. // The detection wrap state done in the pre step function: // if wrap is enabled, then _positionRestoreNeeded is activated // so post step function will be cakked in the next SCN_PAINTED message void ScintillaEditView::restoreCurrentPosPreStep() { Buffer * buf = MainFileManager.getBufferByID(_currentBufferID); const Position & pos = buf->getPosition(this); execute(SCI_SETSELECTIONMODE, pos._selMode); //enable execute(SCI_SETANCHOR, pos._startPos); execute(SCI_SETCURRENTPOS, pos._endPos); execute(SCI_CANCEL); //disable if (!isWrap()) //only offset if not wrapping, otherwise the offset isnt needed at all { execute(SCI_SETSCROLLWIDTH, pos._scrollWidth); execute(SCI_SETXOFFSET, pos._xOffset); } execute(SCI_CHOOSECARETX); // choose current x position intptr_t lineToShow = execute(SCI_VISIBLEFROMDOCLINE, pos._firstVisibleLine); execute(SCI_SETFIRSTVISIBLELINE, lineToShow); if (isWrap()) { // Enable flag 'positionRestoreNeeded' so that function restoreCurrentPosPostStep get called // once scintilla send SCN_PAITED notification _positionRestoreNeeded = true; } _restorePositionRetryCount = 0; } // If wrap is enabled, the post step function will be called in the next SCN_PAINTED message // to scroll several lines to set the first visible line to the correct wrapped line. void ScintillaEditView::restoreCurrentPosPostStep() { if (!_positionRestoreNeeded) return; Buffer * buf = MainFileManager.getBufferByID(_currentBufferID); const Position & pos = buf->getPosition(this); ++_restorePositionRetryCount; // Scintilla can send several SCN_PAINTED notifications before the buffer is ready to be displayed. // this post step function is therefore iterated several times in a maximum of 8 iterations. // 8 is an arbitrary number. 2 is a minimum. Maximum value is unknown. if (_restorePositionRetryCount > 8) { // Abort the position restoring process. Buffer topology may have changed _positionRestoreNeeded = false; return; } intptr_t displayedLine = execute(SCI_GETFIRSTVISIBLELINE); intptr_t docLine = execute(SCI_DOCLINEFROMVISIBLE, displayedLine); //linenumber of the line displayed in the // check docLine must equals saved position if (docLine != pos._firstVisibleLine) { // Scintilla has paint the buffer but the position is not correct. intptr_t lineToShow = execute(SCI_VISIBLEFROMDOCLINE, pos._firstVisibleLine); execute(SCI_SETFIRSTVISIBLELINE, lineToShow); } else if (pos._offset > 0) { // don't scroll anything if the wrap count is different than the saved one. // Buffer update may be in progress (in case wrap is enabled) intptr_t wrapCount = execute(SCI_WRAPCOUNT, docLine); if (wrapCount == pos._wrapCount) { scroll(0, pos._offset); _positionRestoreNeeded = false; } } else { // Buffer position is correct, and there is no scroll to apply _positionRestoreNeeded = false; } } void ScintillaEditView::restyleBuffer() { execute(SCI_CLEARDOCUMENTSTYLE); execute(SCI_COLOURISE, 0, -1); _currentBuffer->setNeedsLexing(false); } void ScintillaEditView::styleChange() { defineDocType(_currentBuffer->getLangType()); restyleBuffer(); } bool ScintillaEditView::setLexerFromLangID(int langID) // Internal lexer only { if (langID >= L_EXTERNAL) return false; const char* lexerNameID = _langNameInfoArray[langID]._lexerID; execute(SCI_SETILEXER, 0, reinterpret_cast(CreateLexer(lexerNameID))); return true; } void ScintillaEditView::activateBuffer(BufferID buffer, bool force) { if (buffer == BUFFER_INVALID) return; if (!force && buffer == _currentBuffer) return; Buffer * newBuf = MainFileManager.getBufferByID(buffer); // before activating another document, we get the current position // from the Scintilla view then save it to the current document saveCurrentPos(); // get foldStateInfo of current doc std::vector lineStateVector; getCurrentFoldStates(lineStateVector); // put the state into the future ex buffer _currentBuffer->setHeaderLineState(lineStateVector, this); _prevBuffer = _currentBuffer; _currentBufferID = buffer; //the magical switch happens here _currentBuffer = newBuf; const bool isSameLangType = _prevBuffer != nullptr && ((_prevBuffer == _currentBuffer) || (_prevBuffer->getLangType() == _currentBuffer->getLangType())); const int currentLangInt = static_cast(_currentBuffer->getLangType()); const bool isFirstActiveBuffer = (_currentBuffer->getLastLangType() != currentLangInt); if (isFirstActiveBuffer) // Entering the tab for the 1st time { // change the doc, this operation will decrease // the ref count of old current doc and increase the one of the new doc. FileManager should manage the rest // Note that the actual reference in the Buffer itself is NOT decreased, Notepad_plus does that if neccessary execute(SCI_SETMODEVENTMASK, MODEVENTMASK_OFF); execute(SCI_SETDOCPOINTER, 0, _currentBuffer->getDocument()); execute(SCI_SETMODEVENTMASK, MODEVENTMASK_ON); // Due to execute(SCI_CLEARDOCUMENTSTYLE); in defineDocType() function // defineDocType() function should be called here, but not be after the fold info loop defineDocType(_currentBuffer->getLangType()); } else if (isSameLangType) // After the 2nd entering with the same language type { // No need to call defineDocType() since it's the same language type execute(SCI_SETMODEVENTMASK, MODEVENTMASK_OFF); execute(SCI_SETDOCPOINTER, 0, _currentBuffer->getDocument()); execute(SCI_SETMODEVENTMASK, MODEVENTMASK_ON); } else // Entering the tab for the 2nd or more times, with the different language type { // In order to improve the performance of switch-in on the 2nd or more times for the large files, // a blank document is used for accelerate defineDocType() call. execute(SCI_SETMODEVENTMASK, MODEVENTMASK_OFF); execute(SCI_SETDOCPOINTER, 0, getBlankDocument()); execute(SCI_SETMODEVENTMASK, MODEVENTMASK_ON); defineDocType(_currentBuffer->getLangType()); execute(SCI_SETMODEVENTMASK, MODEVENTMASK_OFF); execute(SCI_SETDOCPOINTER, 0, _currentBuffer->getDocument()); execute(SCI_SETMODEVENTMASK, MODEVENTMASK_ON); } _currentBuffer->setLastLangType(currentLangInt); setWordChars(); if (_currentBuffer->getNeedsLexing()) { restyleBuffer(); } maintainStateForNpc(); // Everything should be updated, but the language bufferUpdated(_currentBuffer, (BufferChangeMask & ~BufferChangeLanguage)); // restore the collapsed info const std::vector & lineStateVectorNew = newBuf->getHeaderLineState(this); syncFoldStateWith(lineStateVectorNew); restoreCurrentPosPreStep(); //runMarkers(true, 0, true, false); restoreHiddenLines(); setCRLF(); NppParameters& nppParam = NppParameters::getInstance(); const ScintillaViewParams& svp = nppParam.getSVP(); int enabledCHFlag = SC_CHANGE_HISTORY_DISABLED; if (svp._isChangeHistoryMarginEnabled || svp._isChangeHistoryIndicatorEnabled) { enabledCHFlag = SC_CHANGE_HISTORY_ENABLED; if (svp._isChangeHistoryMarginEnabled) enabledCHFlag |= SC_CHANGE_HISTORY_MARKERS; if (svp._isChangeHistoryIndicatorEnabled) enabledCHFlag |= SC_CHANGE_HISTORY_INDICATORS; } execute(SCI_SETCHANGEHISTORY, enabledCHFlag); if (isTextDirectionRTL() != buffer->isRTL()) changeTextDirection(buffer->isRTL()); return; //all done } void ScintillaEditView::getCurrentFoldStates(std::vector & lineStateVector) { // xCodeOptimization1304: For active document get folding state from Scintilla. // The code using SCI_CONTRACTEDFOLDNEXT is usually 10%-50% faster than checking each line of the document!! size_t contractedFoldHeaderLine = 0; do { contractedFoldHeaderLine = execute(SCI_CONTRACTEDFOLDNEXT, contractedFoldHeaderLine); if (static_cast(contractedFoldHeaderLine) != -1) { //-- Store contracted line lineStateVector.push_back(contractedFoldHeaderLine); //-- Start next search with next line ++contractedFoldHeaderLine; } } while (static_cast(contractedFoldHeaderLine) != -1); } void ScintillaEditView::syncFoldStateWith(const std::vector & lineStateVectorNew) { size_t nbLineState = lineStateVectorNew.size(); for (size_t i = 0 ; i < nbLineState ; ++i) { auto line = lineStateVectorNew.at(i); fold(line, false); } } void ScintillaEditView::bufferUpdated(Buffer * buffer, int mask) { //actually only care about language and lexing etc if (buffer == _currentBuffer) { if (mask & BufferChangeLanguage) { defineDocType(buffer->getLangType()); foldAll(fold_uncollapse); } if (mask & BufferChangeLexing) { if (buffer->getNeedsLexing()) { restyleBuffer(); //sets to false, this will apply to any other view aswell } //else nothing, otherwise infinite loop } if (mask & BufferChangeFormat) { execute(SCI_SETEOLMODE, static_cast(_currentBuffer->getEolFormat())); } if (mask & BufferChangeReadonly) { execute(SCI_SETREADONLY, _currentBuffer->isReadOnly()); } if (mask & BufferChangeUnicode) { int enc = CP_ACP; if (buffer->getUnicodeMode() == uni8Bit) { //either 0 or CJK codepage LangType typeDoc = buffer->getLangType(); if (isCJK()) { if (typeDoc == L_CSS || typeDoc == L_CAML || typeDoc == L_ASM || typeDoc == L_MATLAB) enc = CP_ACP; //you may also want to set charsets here, not yet implemented else enc = _codepage; } else enc = CP_ACP; } else //CP UTF8 for all unicode enc = SC_CP_UTF8; execute(SCI_SETCODEPAGE, enc); } } } bool ScintillaEditView::isFoldIndentationBased() const { const auto lexer = execute(SCI_GETLEXER); // search IndentAmount in scintilla\lexers folder return lexer == SCLEX_PYTHON || lexer == SCLEX_COFFEESCRIPT || lexer == SCLEX_HASKELL || lexer == SCLEX_NIMROD || lexer == SCLEX_VB || lexer == SCLEX_YAML ; } namespace { struct FoldLevelStack { int levelCount = 0; // 1-based level number intptr_t levelStack[MAX_FOLD_COLLAPSE_LEVEL]{}; void push(intptr_t level) { while (levelCount != 0 && level <= levelStack[levelCount - 1]) { --levelCount; } levelStack[levelCount++] = level; } }; } void ScintillaEditView::collapseFoldIndentationBased(int level2Collapse, bool mode) { execute(SCI_COLOURISE, 0, -1); FoldLevelStack levelStack; ++level2Collapse; // 1-based level number const intptr_t maxLine = execute(SCI_GETLINECOUNT); intptr_t line = 0; while (line < maxLine) { intptr_t level = execute(SCI_GETFOLDLEVEL, line); if (level & SC_FOLDLEVELHEADERFLAG) { level &= SC_FOLDLEVELNUMBERMASK; // don't need the actually level number, only the relationship. levelStack.push(level); if (level2Collapse == levelStack.levelCount) { if (isFolded(line) != mode) { fold(line, mode); } // skip all children lines, required to avoid buffer overrun. line = execute(SCI_GETLASTCHILD, line, -1); } } ++line; } runMarkers(true, 0, true, false); } void ScintillaEditView::collapse(int level2Collapse, bool mode) { if (isFoldIndentationBased()) { collapseFoldIndentationBased(level2Collapse, mode); return; } execute(SCI_COLOURISE, 0, -1); intptr_t maxLine = execute(SCI_GETLINECOUNT); for (int line = 0; line < maxLine; ++line) { intptr_t level = execute(SCI_GETFOLDLEVEL, line); if (level & SC_FOLDLEVELHEADERFLAG) { level -= SC_FOLDLEVELBASE; if (level2Collapse == (level & SC_FOLDLEVELNUMBERMASK)) if (isFolded(line) != mode) { fold(line, mode); } } } runMarkers(true, 0, true, false); } void ScintillaEditView::foldCurrentPos(bool mode) { auto currentLine = getCurrentLineNumber(); fold(currentLine, mode); } bool ScintillaEditView::isCurrentLineFolded() const { auto currentLine = getCurrentLineNumber(); intptr_t headerLine; auto level = execute(SCI_GETFOLDLEVEL, currentLine); if (level & SC_FOLDLEVELHEADERFLAG) headerLine = currentLine; else { headerLine = execute(SCI_GETFOLDPARENT, currentLine); if (headerLine == -1) return false; } bool isExpanded = execute(SCI_GETFOLDEXPANDED, headerLine); return !isExpanded; } void ScintillaEditView::fold(size_t line, bool mode) { auto endStyled = execute(SCI_GETENDSTYLED); auto len = execute(SCI_GETTEXTLENGTH); if (endStyled < len) execute(SCI_COLOURISE, 0, -1); intptr_t headerLine; auto level = execute(SCI_GETFOLDLEVEL, line); if (level & SC_FOLDLEVELHEADERFLAG) headerLine = line; else { headerLine = execute(SCI_GETFOLDPARENT, line); if (headerLine == -1) return; } if (isFolded(headerLine) != mode) { execute(SCI_TOGGLEFOLD, headerLine); SCNotification scnN{}; scnN.nmhdr.code = SCN_FOLDINGSTATECHANGED; scnN.nmhdr.hwndFrom = _hSelf; scnN.nmhdr.idFrom = 0; scnN.line = headerLine; scnN.foldLevelNow = isFolded(headerLine)?1:0; //folded:1, unfolded:0 ::SendMessage(_hParent, WM_NOTIFY, 0, reinterpret_cast(&scnN)); } } void ScintillaEditView::foldAll(bool mode) { auto maxLine = execute(SCI_GETLINECOUNT); for (int line = 0; line < maxLine; ++line) { auto level = execute(SCI_GETFOLDLEVEL, line); if (level & SC_FOLDLEVELHEADERFLAG) if (isFolded(line) != mode) fold(line, mode); } } void ScintillaEditView::getText(char *dest, size_t start, size_t end) const { Sci_TextRangeFull tr{}; tr.chrg.cpMin = static_cast(start); tr.chrg.cpMax = static_cast(end); tr.lpstrText = dest; execute(SCI_GETTEXTRANGEFULL, 0, reinterpret_cast(&tr)); } wstring ScintillaEditView::getGenericTextAsString(size_t start, size_t end) const { assert(end > start); const size_t bufSize = end - start + 1; wchar_t *buf = new wchar_t[bufSize]; getGenericText(buf, bufSize, start, end); wstring text = buf; delete[] buf; return text; } void ScintillaEditView::getGenericText(wchar_t *dest, size_t destlen, size_t start, size_t end) const { WcharMbcsConvertor& wmc = WcharMbcsConvertor::getInstance(); char *destA = new char[end - start + 1]; getText(destA, start, end); size_t cp = execute(SCI_GETCODEPAGE); const wchar_t *destW = wmc.char2wchar(destA, cp); wcsncpy_s(dest, destlen, destW, _TRUNCATE); delete [] destA; } // "mstart" and "mend" are pointers to indexes in the read string, // which are converted to the corresponding indexes in the returned wchar_t string. void ScintillaEditView::getGenericText(wchar_t *dest, size_t destlen, size_t start, size_t end, intptr_t* mstart, intptr_t* mend) const { WcharMbcsConvertor& wmc = WcharMbcsConvertor::getInstance(); char *destA = new char[end - start + 1]; getText(destA, start, end); size_t cp = execute(SCI_GETCODEPAGE) ; const wchar_t *destW = wmc.char2wchar(destA, cp, mstart, mend); wcsncpy_s(dest, destlen, destW, _TRUNCATE); delete [] destA; } void ScintillaEditView::insertGenericTextFrom(size_t position, const wchar_t *text2insert) const { WcharMbcsConvertor& wmc = WcharMbcsConvertor::getInstance(); size_t cp = execute(SCI_GETCODEPAGE); const char *text2insertA = wmc.wchar2char(text2insert, cp); execute(SCI_INSERTTEXT, position, reinterpret_cast(text2insertA)); } void ScintillaEditView::replaceSelWith(const char * replaceText) { execute(SCI_REPLACESEL, 0, reinterpret_cast(replaceText)); } void ScintillaEditView::getVisibleStartAndEndPosition(intptr_t* startPos, intptr_t* endPos) { assert(startPos != NULL && endPos != NULL); // Get the position of the 1st and last showing chars from the edit view RECT rcEditView{}; getClientRect(rcEditView); LRESULT pos = execute(SCI_POSITIONFROMPOINT, 0, 0); LRESULT line = execute(SCI_LINEFROMPOSITION, pos); *startPos = execute(SCI_POSITIONFROMLINE, line); pos = execute(SCI_POSITIONFROMPOINT, static_cast(rcEditView.right - rcEditView.left), static_cast(rcEditView.bottom - rcEditView.top)); line = execute(SCI_LINEFROMPOSITION, pos); *endPos = execute(SCI_GETLINEENDPOSITION, line); } char * ScintillaEditView::getWordFromRange(char * txt, size_t size, size_t pos1, size_t pos2) { if (!size) return NULL; if (pos1 > pos2) { size_t tmp = pos1; pos1 = pos2; pos2 = tmp; } if (size < pos2 - pos1) return NULL; getText(txt, pos1, pos2); return txt; } char * ScintillaEditView::getWordOnCaretPos(char * txt, size_t size) { if (!size) return NULL; pair range = getWordRange(); return getWordFromRange(txt, size, range.first, range.second); } wchar_t * ScintillaEditView::getGenericWordOnCaretPos(wchar_t * txt, int size) { WcharMbcsConvertor& wmc = WcharMbcsConvertor::getInstance(); size_t cp = execute(SCI_GETCODEPAGE); char *txtA = new char[size + 1]; getWordOnCaretPos(txtA, size); const wchar_t * txtW = wmc.char2wchar(txtA, cp); wcscpy_s(txt, size, txtW); delete [] txtA; return txt; } char * ScintillaEditView::getSelectedText(char * txt, size_t size, bool expand) { if (!size) return NULL; Sci_CharacterRangeFull range = getSelection(); if (range.cpMax == range.cpMin && expand) { expandWordSelection(); range = getSelection(); } if (!(static_cast(size) > (range.cpMax - range.cpMin))) //there must be atleast 1 byte left for zero terminator { range.cpMax = range.cpMin + size -1; //keep room for zero terminator } //getText(txt, range.cpMin, range.cpMax); return getWordFromRange(txt, size, range.cpMin, range.cpMax); } wchar_t * ScintillaEditView::getGenericSelectedText(wchar_t * txt, int size, bool expand) { WcharMbcsConvertor& wmc = WcharMbcsConvertor::getInstance(); size_t cp = execute(SCI_GETCODEPAGE); char *txtA = new char[size + 1]; getSelectedText(txtA, size, expand); const wchar_t * txtW = wmc.char2wchar(txtA, cp); wcscpy_s(txt, size, txtW); delete [] txtA; return txt; } intptr_t ScintillaEditView::searchInTarget(const wchar_t * text2Find, size_t lenOfText2Find, size_t fromPos, size_t toPos) const { execute(SCI_SETTARGETRANGE, fromPos, toPos); WcharMbcsConvertor& wmc = WcharMbcsConvertor::getInstance(); size_t cp = execute(SCI_GETCODEPAGE); const char *text2FindA = wmc.wchar2char(text2Find, cp); size_t text2FindALen = strlen(text2FindA); size_t len = (lenOfText2Find > text2FindALen) ? lenOfText2Find : text2FindALen; return execute(SCI_SEARCHINTARGET, len, reinterpret_cast(text2FindA)); } void ScintillaEditView::appandGenericText(const wchar_t * text2Append) const { WcharMbcsConvertor& wmc = WcharMbcsConvertor::getInstance(); size_t cp = execute(SCI_GETCODEPAGE); const char *text2AppendA =wmc.wchar2char(text2Append, cp); execute(SCI_APPENDTEXT, strlen(text2AppendA), reinterpret_cast(text2AppendA)); } void ScintillaEditView::addGenericText(const wchar_t * text2Append) const { WcharMbcsConvertor& wmc = WcharMbcsConvertor::getInstance(); size_t cp = execute(SCI_GETCODEPAGE); const char *text2AppendA =wmc.wchar2char(text2Append, cp); execute(SCI_ADDTEXT, strlen(text2AppendA), reinterpret_cast(text2AppendA)); } void ScintillaEditView::addGenericText(const wchar_t * text2Append, intptr_t* mstart, intptr_t* mend) const { WcharMbcsConvertor& wmc = WcharMbcsConvertor::getInstance(); size_t cp = execute(SCI_GETCODEPAGE); const char *text2AppendA =wmc.wchar2char(text2Append, cp, mstart, mend); execute(SCI_ADDTEXT, strlen(text2AppendA), reinterpret_cast(text2AppendA)); } intptr_t ScintillaEditView::replaceTarget(const wchar_t * str2replace, intptr_t fromTargetPos, intptr_t toTargetPos) const { if (fromTargetPos != -1 || toTargetPos != -1) { execute(SCI_SETTARGETRANGE, fromTargetPos, toTargetPos); } WcharMbcsConvertor& wmc = WcharMbcsConvertor::getInstance(); size_t cp = execute(SCI_GETCODEPAGE); const char *str2replaceA = wmc.wchar2char(str2replace, cp); return execute(SCI_REPLACETARGET, static_cast(-1), reinterpret_cast(str2replaceA)); } intptr_t ScintillaEditView::replaceTargetRegExMode(const wchar_t * re, intptr_t fromTargetPos, intptr_t toTargetPos) const { if (fromTargetPos != -1 || toTargetPos != -1) { execute(SCI_SETTARGETRANGE, fromTargetPos, toTargetPos); } WcharMbcsConvertor& wmc = WcharMbcsConvertor::getInstance(); size_t cp = execute(SCI_GETCODEPAGE); const char *reA = wmc.wchar2char(re, cp); return execute(SCI_REPLACETARGETRE, static_cast(-1), reinterpret_cast(reA)); } void ScintillaEditView::showAutoComletion(size_t lenEntered, const wchar_t* list) { WcharMbcsConvertor& wmc = WcharMbcsConvertor::getInstance(); size_t cp = execute(SCI_GETCODEPAGE); const char *listA = wmc.wchar2char(list, cp); execute(SCI_AUTOCSHOW, lenEntered, reinterpret_cast(listA)); NppDarkMode::setDarkAutoCompletion(); } void ScintillaEditView::showCallTip(size_t startPos, const wchar_t * def) { WcharMbcsConvertor& wmc = WcharMbcsConvertor::getInstance(); size_t cp = execute(SCI_GETCODEPAGE); const char *defA = wmc.wchar2char(def, cp); execute(SCI_CALLTIPSHOW, startPos, reinterpret_cast(defA)); } wstring ScintillaEditView::getLine(size_t lineNumber) const { size_t lineLen = execute(SCI_LINELENGTH, lineNumber); const size_t bufSize = lineLen + 1; std::unique_ptr buf = std::make_unique(bufSize); getLine(lineNumber, buf.get(), bufSize); return buf.get(); } void ScintillaEditView::getLine(size_t lineNumber, wchar_t * line, size_t lineBufferLen) const { // make sure the buffer length is enough to get the whole line size_t lineLen = execute(SCI_LINELENGTH, lineNumber); if (lineLen >= lineBufferLen) return; WcharMbcsConvertor& wmc = WcharMbcsConvertor::getInstance(); size_t cp = execute(SCI_GETCODEPAGE); char *lineA = new char[lineBufferLen]; // From Scintilla documentation for SCI_GETLINE: "The buffer is not terminated by a 0 character." memset(lineA, 0x0, sizeof(char) * lineBufferLen); execute(SCI_GETLINE, lineNumber, reinterpret_cast(lineA)); const wchar_t *lineW = wmc.char2wchar(lineA, cp); lstrcpyn(line, lineW, static_cast(lineBufferLen)); delete [] lineA; } void ScintillaEditView::addText(size_t length, const char *buf) { execute(SCI_ADDTEXT, length, reinterpret_cast(buf)); } void ScintillaEditView::beginOrEndSelect(bool isColumnMode) { auto currPos = execute(SCI_GETCURRENTPOS); if (_beginSelectPosition == -1) { _beginSelectPosition = currPos; } else { execute(SCI_CHANGESELECTIONMODE, static_cast(isColumnMode ? SC_SEL_RECTANGLE : SC_SEL_STREAM)); execute(isColumnMode ? SCI_SETANCHOR : SCI_SETSEL, static_cast(_beginSelectPosition), static_cast(currPos)); _beginSelectPosition = -1; } } void ScintillaEditView::showMargin(int whichMarge, bool willBeShowed) { if (whichMarge == _SC_MARGE_LINENUMBER) { bool forcedToHide = !willBeShowed; updateLineNumbersMargin(forcedToHide); } else { DPIManager& dpiManager = NppParameters::getInstance()._dpiManager; int width = dpiManager.scaleX(3); if (whichMarge == _SC_MARGE_SYMBOL) width = dpiManager.scaleX(16); else if (whichMarge == _SC_MARGE_FOLDER) width = dpiManager.scaleX(14); execute(SCI_SETMARGINWIDTHN, whichMarge, willBeShowed ? width : 0); } } void ScintillaEditView::showChangeHistoryMargin(bool willBeShowed) { DPIManager& dpiManager = NppParameters::getInstance()._dpiManager; int width = dpiManager.scaleX(9); execute(SCI_SETMARGINWIDTHN, _SC_MARGE_CHANGEHISTORY, willBeShowed ? width : 0); } void ScintillaEditView::updateBeginEndSelectPosition(bool is_insert, size_t position, size_t length) { if (_beginSelectPosition != -1 && static_cast(position) < _beginSelectPosition - 1) { if (is_insert) _beginSelectPosition += length; else _beginSelectPosition -= length; assert(_beginSelectPosition >= 0); } } void ScintillaEditView::marginClick(Sci_Position position, int modifiers) { size_t lineClick = execute(SCI_LINEFROMPOSITION, position, 0); intptr_t levelClick = execute(SCI_GETFOLDLEVEL, lineClick, 0); if (levelClick & SC_FOLDLEVELHEADERFLAG) { if (modifiers & SCMOD_SHIFT) { // Ensure all children visible execute(SCI_SETFOLDEXPANDED, lineClick, 1); expand(lineClick, true, true, 100, levelClick); } else if (modifiers & SCMOD_CTRL) { if (isFolded(lineClick)) { // Contract this line and all children execute(SCI_SETFOLDEXPANDED, lineClick, 0); expand(lineClick, false, true, 0, levelClick); } else { // Expand this line and all children execute(SCI_SETFOLDEXPANDED, lineClick, 1); expand(lineClick, true, true, 100, levelClick); } } else { // Toggle this line bool mode = isFolded(lineClick); fold(lineClick, !mode); runMarkers(true, lineClick, true, false); } } } void ScintillaEditView::expand(size_t& line, bool doExpand, bool force, intptr_t visLevels, intptr_t level) { size_t lineMaxSubord = execute(SCI_GETLASTCHILD, line, level & SC_FOLDLEVELNUMBERMASK); ++line; while (line <= lineMaxSubord) { if (force) { execute(((visLevels > 0) ? SCI_SHOWLINES : SCI_HIDELINES), line, line); } else { if (doExpand) execute(SCI_SHOWLINES, line, line); } intptr_t levelLine = level; if (levelLine == -1) levelLine = execute(SCI_GETFOLDLEVEL, line, 0); if (levelLine & SC_FOLDLEVELHEADERFLAG) { if (force) { if (visLevels > 1) execute(SCI_SETFOLDEXPANDED, line, 1); else execute(SCI_SETFOLDEXPANDED, line, 0); expand(line, doExpand, force, visLevels - 1); } else { if (doExpand) { if (!isFolded(line)) execute(SCI_SETFOLDEXPANDED, line, 1); expand(line, true, force, visLevels - 1); } else expand(line, false, force, visLevels - 1); } } else ++line; } runMarkers(true, 0, true, false); } void ScintillaEditView::performGlobalStyles() { NppParameters& nppParams = NppParameters::getInstance(); const ScintillaViewParams& svp = nppParams.getSVP(); StyleArray& stylers = nppParams.getMiscStylerArray(); const Style* pStyle{}; if (svp._currentLineHiliteMode != LINEHILITE_NONE) { pStyle = stylers.findByName(L"Current line background colour"); if (pStyle) { setElementColour(SC_ELEMENT_CARET_LINE_BACK, pStyle->_bgColor); } } execute(SCI_SETCARETLINEFRAME, (svp._currentLineHiliteMode == LINEHILITE_FRAME) ? svp._currentLineFrameWidth : 0); COLORREF selectColorBack = grey; COLORREF selectColorFore = black; pStyle = stylers.findByName(L"Selected text colour"); if (pStyle) { selectColorBack = pStyle->_bgColor; selectColorFore = pStyle->_fgColor; } //execute(SCI_SETSELBACK, 1, selectColorBack); setElementColour(SC_ELEMENT_SELECTION_BACK, selectColorBack); // SCI_SETSELBACK is deprecated setElementColour(SC_ELEMENT_SELECTION_INACTIVE_BACK, selectColorBack); COLORREF selectMultiSelectColorBack = liteGrey; pStyle = stylers.findByName(L"Multi-selected text color"); if (pStyle) { selectMultiSelectColorBack = pStyle->_bgColor; } setElementColour(SC_ELEMENT_SELECTION_ADDITIONAL_BACK, selectMultiSelectColorBack); if (nppParams.isSelectFgColorEnabled()) { //execute(SCI_SETSELFORE, 1, selectColorFore); setElementColour(SC_ELEMENT_SELECTION_TEXT, selectColorFore); // SCI_SETSELFORE is deprecated setElementColour(SC_ELEMENT_SELECTION_INACTIVE_TEXT, selectColorFore); setElementColour(SC_ELEMENT_SELECTION_ADDITIONAL_TEXT, selectColorFore); } COLORREF caretColor = black; pStyle = stylers.findByName(L"Caret colour"); if (pStyle) { caretColor = pStyle->_fgColor; } //execute(SCI_SETCARETFORE, caretColor); setElementColour(SC_ELEMENT_CARET, caretColor); // SCI_SETCARETFORE is deprecated COLORREF multiEditCaretColor = darkGrey; pStyle = stylers.findByName(L"Multi-edit carets color"); if (pStyle) multiEditCaretColor = pStyle->_fgColor; setElementColour(SC_ELEMENT_CARET_ADDITIONAL, multiEditCaretColor); COLORREF edgeColor = liteGrey; pStyle = stylers.findByName(L"Edge colour"); if (pStyle) { edgeColor = pStyle->_fgColor; } execute(SCI_SETEDGECOLOUR, edgeColor); ::SendMessage(_hParent, NPPM_INTERNAL_EDGEMULTISETSIZE, 0, 0); COLORREF foldMarginColor = grey; COLORREF foldMarginHiColor = white; pStyle = stylers.findByName(L"Fold margin"); if (pStyle) { foldMarginHiColor = pStyle->_fgColor; foldMarginColor = pStyle->_bgColor; } execute(SCI_SETFOLDMARGINCOLOUR, true, foldMarginColor); execute(SCI_SETFOLDMARGINHICOLOUR, true, foldMarginHiColor); COLORREF bookmarkMarginColor = veryLiteGrey; pStyle = stylers.findByName(L"Bookmark margin"); if (!pStyle) { pStyle = stylers.findByName(L"Line number margin"); // "Line number margin" is used only for getting the bg color for _SC_MARGE_SYMBOL. if (pStyle) // "Line number margin" has its own style (styleID="33") for setting its bg & fg color { bookmarkMarginColor = pStyle->_bgColor; } } else { bookmarkMarginColor = pStyle->_bgColor; } execute(SCI_SETMARGINTYPEN, _SC_MARGE_SYMBOL, SC_MARGIN_COLOUR); execute(SCI_SETMARGINBACKN, _SC_MARGE_SYMBOL, bookmarkMarginColor); COLORREF changeHistoryMarginColor = veryLiteGrey; pStyle = stylers.findByName(L"Change History margin"); if (!pStyle) { pStyle = stylers.findByName(L"Line number margin"); if (pStyle) { changeHistoryMarginColor = pStyle->_bgColor; } } else { changeHistoryMarginColor = pStyle->_bgColor; } execute(SCI_SETMARGINTYPEN, _SC_MARGE_CHANGEHISTORY, SC_MARGIN_COLOUR); execute(SCI_SETMARGINBACKN, _SC_MARGE_CHANGEHISTORY, changeHistoryMarginColor); COLORREF changeModifiedfgColor = orange; COLORREF changeModifiedbgColor = orange; pStyle = stylers.findByName(L"Change History modified"); if (pStyle) { changeModifiedfgColor = pStyle->_fgColor; changeModifiedbgColor = pStyle->_bgColor; } execute(SCI_MARKERSETFORE, SC_MARKNUM_HISTORY_MODIFIED, changeModifiedfgColor); execute(SCI_MARKERSETBACK, SC_MARKNUM_HISTORY_MODIFIED, changeModifiedbgColor); execute(SCI_INDICSETFORE, INDICATOR_HISTORY_MODIFIED_INSERTION, changeModifiedfgColor); execute(SCI_INDICSETFORE, INDICATOR_HISTORY_MODIFIED_DELETION, changeModifiedfgColor); COLORREF changeRevertModifiedfgColor = yellowGreen; COLORREF changeRevertModifiedbgColor = yellowGreen; pStyle = stylers.findByName(L"Change History revert modified"); if (pStyle) { changeRevertModifiedfgColor = pStyle->_fgColor; changeRevertModifiedbgColor = pStyle->_bgColor; } execute(SCI_MARKERSETFORE, SC_MARKNUM_HISTORY_REVERTED_TO_MODIFIED, changeRevertModifiedfgColor); execute(SCI_MARKERSETBACK, SC_MARKNUM_HISTORY_REVERTED_TO_MODIFIED, changeRevertModifiedbgColor); execute(SCI_INDICSETFORE, INDICATOR_HISTORY_REVERTED_TO_MODIFIED_INSERTION, changeRevertModifiedfgColor); execute(SCI_INDICSETFORE, INDICATOR_HISTORY_REVERTED_TO_MODIFIED_DELETION, changeRevertModifiedfgColor); COLORREF changeRevertOriginfgColor = darkCyan; COLORREF changeRevertOriginbgColor = darkCyan; pStyle = stylers.findByName(L"Change History revert origin"); if (pStyle) { changeRevertOriginfgColor = pStyle->_fgColor; changeRevertOriginbgColor = pStyle->_bgColor; } execute(SCI_MARKERSETFORE, SC_MARKNUM_HISTORY_REVERTED_TO_ORIGIN, changeRevertOriginfgColor); execute(SCI_MARKERSETBACK, SC_MARKNUM_HISTORY_REVERTED_TO_ORIGIN, changeRevertOriginbgColor); execute(SCI_INDICSETFORE, INDICATOR_HISTORY_REVERTED_TO_ORIGIN_INSERTION, changeRevertOriginfgColor); execute(SCI_INDICSETFORE, INDICATOR_HISTORY_REVERTED_TO_ORIGIN_DELETION, changeRevertOriginfgColor); COLORREF changeSavedfgColor = midGreen; COLORREF changeSavedbgColor = midGreen; pStyle = stylers.findByName(L"Change History saved"); if (pStyle) { changeSavedfgColor = pStyle->_fgColor; changeSavedbgColor = pStyle->_bgColor; } execute(SCI_MARKERSETFORE, SC_MARKNUM_HISTORY_SAVED, changeSavedfgColor); execute(SCI_MARKERSETBACK, SC_MARKNUM_HISTORY_SAVED, changeSavedbgColor); execute(SCI_INDICSETFORE, INDICATOR_HISTORY_SAVED_INSERTION, changeSavedfgColor); execute(SCI_INDICSETFORE, INDICATOR_HISTORY_SAVED_DELETION, changeSavedfgColor); COLORREF urlHoveredFG = grey; pStyle = stylers.findByName(L"URL hovered"); if (pStyle) { urlHoveredFG = pStyle->_fgColor; } execute(SCI_INDICSETHOVERFORE, URL_INDIC, urlHoveredFG); COLORREF foldfgColor = white, foldbgColor = grey, activeFoldFgColor = red; getFoldColor(foldfgColor, foldbgColor, activeFoldFgColor); for (int j = 0 ; j < NB_FOLDER_STATE ; ++j) defineMarker(_markersArray[FOLDER_TYPE][j], _markersArray[svp._folderStyle][j], foldfgColor, foldbgColor, activeFoldFgColor); execute(SCI_MARKERENABLEHIGHLIGHT, true); COLORREF wsSymbolFgColor = black; pStyle = stylers.findByName(L"White space symbol"); if (pStyle) { wsSymbolFgColor = pStyle->_fgColor; } execute(SCI_SETWHITESPACEFORE, true, wsSymbolFgColor); COLORREF eolCustomColor = liteGrey; pStyle = stylers.findByName(L"EOL custom color"); if (pStyle) { eolCustomColor = pStyle->_fgColor; } setCRLF(eolCustomColor); COLORREF npcCustomColor = liteGrey; pStyle = stylers.findByName(g_npcStyleName); if (pStyle) { npcCustomColor = pStyle->_fgColor; } setNpcAndCcUniEOL(npcCustomColor); } void ScintillaEditView::showNpc(bool willBeShowed, bool isSearchResult) { const auto& svp = NppParameters::getInstance().getSVP(); if (willBeShowed) { const auto& mode = static_cast(svp._npcMode); for (const auto& invChar : g_nonPrintingChars) { execute(SCI_SETREPRESENTATION, reinterpret_cast(invChar.at(0)), reinterpret_cast(invChar.at(mode))); } if (svp._npcCustomColor) { setNpcAndCcUniEOL(); } // in some case npc representation is not redrawn correctly on first line // therefore use of showEOL(isShownEol()) instead of redraw() showEOL(isShownEol()); } else { execute(SCI_CLEARALLREPRESENTATIONS); // SCI_CLEARALLREPRESENTATIONS will also reset CRLF and CcUniEOL if (!isSearchResult && svp._eolMode != svp.roundedRectangleText) { setCRLF(); } showCcUniEol(svp._ccUniEolShow); } } void ScintillaEditView::showCcUniEol(bool willBeShowed, bool isSearchResult) { const auto& svp = NppParameters::getInstance().getSVP(); if (willBeShowed) { const auto& mode = static_cast(svp._npcIncludeCcUniEol ? svp._npcMode : ScintillaViewParams::npcMode::abbreviation); for (const auto& invChar : g_ccUniEolChars) { execute(SCI_SETREPRESENTATION, reinterpret_cast(invChar.at(0)), reinterpret_cast(invChar.at(mode))); } if (svp._npcIncludeCcUniEol && svp._npcCustomColor) { setNpcAndCcUniEOL(); } } else { execute(SCI_CLEARALLREPRESENTATIONS); for (const auto& invChar : g_ccUniEolChars) { execute(SCI_SETREPRESENTATION, reinterpret_cast(invChar.at(0)), reinterpret_cast(g_ZWSP)); execute(SCI_SETREPRESENTATIONAPPEARANCE, reinterpret_cast(invChar.at(0)), SC_REPRESENTATION_PLAIN); } // SCI_CLEARALLREPRESENTATIONS will also reset CRLF and NPC if (!isSearchResult && svp._eolMode != svp.roundedRectangleText) { setCRLF(); } if (svp._npcShow) { showNpc(); return; // showEOL(isShownEol()) already in showNpc() } } // in some case C0, C1 and Unicode EOL representations are not redrawn correctly on first line // therefore use of showEOL(isShownEol()) instead of redraw() showEOL(isShownEol()); } void ScintillaEditView::showIndentGuideLine(bool willBeShowed) { auto typeDoc = _currentBuffer->getLangType(); const int docIndentMode = isPythonStyleIndentation(typeDoc) ? SC_IV_LOOKFORWARD : SC_IV_LOOKBOTH; execute(SCI_SETINDENTATIONGUIDES, willBeShowed ? docIndentMode : SC_IV_NONE); } void ScintillaEditView::setLineIndent(size_t line, size_t indent) const { size_t nbSelections = execute(SCI_GETSELECTIONS); if (nbSelections == 1) { Sci_CharacterRangeFull crange = getSelection(); int64_t posBefore = execute(SCI_GETLINEINDENTPOSITION, line); execute(SCI_SETLINEINDENTATION, line, indent); int64_t posAfter = execute(SCI_GETLINEINDENTPOSITION, line); long long posDifference = posAfter - posBefore; if (posAfter > posBefore) { // Move selection on if (crange.cpMin >= posBefore) { crange.cpMin += static_cast(posDifference); } if (crange.cpMax >= posBefore) { crange.cpMax += static_cast(posDifference); } } else if (posAfter < posBefore) { // Move selection back if (crange.cpMin >= posAfter) { if (crange.cpMin >= posBefore) crange.cpMin += static_cast(posDifference); else crange.cpMin = static_cast(posAfter); } if (crange.cpMax >= posAfter) { if (crange.cpMax >= posBefore) crange.cpMax += static_cast(posDifference); else crange.cpMax = static_cast(posAfter); } } execute(SCI_SETSEL, crange.cpMin, crange.cpMax); } else { execute(SCI_BEGINUNDOACTION); for (size_t i = 0; i < nbSelections; ++i) { LRESULT posStart = execute(SCI_GETSELECTIONNSTART, i); LRESULT posEnd = execute(SCI_GETSELECTIONNEND, i); size_t l = execute(SCI_LINEFROMPOSITION, posStart); int64_t posBefore = execute(SCI_GETLINEINDENTPOSITION, l); execute(SCI_SETLINEINDENTATION, l, indent); int64_t posAfter = execute(SCI_GETLINEINDENTPOSITION, l); long long posDifference = posAfter - posBefore; if (posAfter > posBefore) { // Move selection on if (posStart >= posBefore) { posStart += static_cast(posDifference); } if (posEnd >= posBefore) { posEnd += static_cast(posDifference); } } else if (posAfter < posBefore) { // Move selection back if (posStart >= posAfter) { if (posStart >= posBefore) posStart += static_cast(posDifference); else posStart = static_cast(posAfter); } if (posEnd >= posAfter) { if (posEnd >= posBefore) posEnd += static_cast(posDifference); else posEnd = static_cast(posAfter); } } execute(SCI_SETSELECTIONNSTART, i, posStart); execute(SCI_SETSELECTIONNEND, i, posEnd); } execute(SCI_ENDUNDOACTION); } } void ScintillaEditView::updateLineNumberWidth() { const ScintillaViewParams& svp = NppParameters::getInstance().getSVP(); if (svp._lineNumberMarginShow) { auto linesVisible = execute(SCI_LINESONSCREEN); if (linesVisible) { int nbDigits = 0; if (svp._lineNumberMarginDynamicWidth) { auto firstVisibleLineVis = execute(SCI_GETFIRSTVISIBLELINE); auto lastVisibleLineVis = linesVisible + firstVisibleLineVis + 1; auto lastVisibleLineDoc = execute(SCI_DOCLINEFROMVISIBLE, lastVisibleLineVis); nbDigits = nbDigitsFromNbLines(lastVisibleLineDoc); nbDigits = nbDigits < 3 ? 3 : nbDigits; } else { auto nbLines = execute(SCI_GETLINECOUNT); nbDigits = nbDigitsFromNbLines(nbLines); nbDigits = nbDigits < 4 ? 4 : nbDigits; } auto pixelWidth = 8 + nbDigits * execute(SCI_TEXTWIDTH, STYLE_LINENUMBER, reinterpret_cast("8")); execute(SCI_SETMARGINWIDTHN, _SC_MARGE_LINENUMBER, pixelWidth); } } } const char * ScintillaEditView::getCompleteKeywordList(std::basic_string & kwl, LangType langType, int keywordIndex) { kwl += " "; const wchar_t *defKwl_generic = NppParameters::getInstance().getWordList(langType, keywordIndex); WcharMbcsConvertor& wmc = WcharMbcsConvertor::getInstance(); const char * defKwl = wmc.wchar2char(defKwl_generic, CP_ACP); kwl += defKwl?defKwl:""; return kwl.c_str(); } void ScintillaEditView::setMultiSelections(const ColumnModeInfos & cmi) { for (size_t i = 0, len = cmi.size(); i < len ; ++i) { if (cmi[i].isValid()) { intptr_t selStart = cmi[i]._direction == L2R?cmi[i]._selLpos:cmi[i]._selRpos; intptr_t selEnd = cmi[i]._direction == L2R?cmi[i]._selRpos:cmi[i]._selLpos; execute(SCI_SETSELECTIONNSTART, i, selStart); execute(SCI_SETSELECTIONNEND, i, selEnd); } if (cmi[i]._nbVirtualAnchorSpc) execute(SCI_SETSELECTIONNANCHORVIRTUALSPACE, i, cmi[i]._nbVirtualAnchorSpc); if (cmi[i]._nbVirtualCaretSpc) execute(SCI_SETSELECTIONNCARETVIRTUALSPACE, i, cmi[i]._nbVirtualCaretSpc); } } // Get selection range (fromLine, toLine) for the specified selection // specify selectionNumber = -1 for the MAIN selection pair ScintillaEditView::getSelectionLinesRange(intptr_t selectionNumber /* = -1 */) const { size_t nbSelections = execute(SCI_GETSELECTIONS); size_t start_pos, end_pos; if ((selectionNumber < 0) || (static_cast(selectionNumber) >= nbSelections)) { start_pos = execute(SCI_GETSELECTIONSTART); end_pos = execute(SCI_GETSELECTIONEND); } else { start_pos = execute(SCI_GETSELECTIONNSTART, selectionNumber); end_pos = execute(SCI_GETSELECTIONNEND, selectionNumber); } size_t line1 = execute(SCI_LINEFROMPOSITION, start_pos); size_t line2 = execute(SCI_LINEFROMPOSITION, end_pos); if ((line1 != line2) && (static_cast(execute(SCI_POSITIONFROMLINE, line2)) == end_pos)) { // if the end of the selection includes the line-ending, // then don't include the following line in the range --line2; } return pair(line1, line2); } void ScintillaEditView::currentLinesUp() const { execute(SCI_MOVESELECTEDLINESUP); } void ScintillaEditView::currentLinesDown() const { execute(SCI_MOVESELECTEDLINESDOWN); // Ensure the selection is within view execute(SCI_SCROLLRANGE, execute(SCI_GETSELECTIONEND), execute(SCI_GETSELECTIONSTART)); } // Case converts the document byte range [start:end] in place and returns the // change in its length in bytes. On any error, does nothing and returns zero. intptr_t ScintillaEditView::caseConvertRange(intptr_t start, intptr_t end, TextCase caseToConvert) { if (end <= start || uintptr_t(end) - uintptr_t(start) > INT_MAX/2) return 0; unsigned codepage = getCurrentBuffer()->getUnicodeMode() == uni8Bit ? _codepage : CP_UTF8; int mbLen = int(end - start); const int mbLenMax = 2 * mbLen + 1; // allow final NUL + substantial expansion char *mbStr = new char[mbLenMax]; getText(mbStr, start, end); if (int wideLen = ::MultiByteToWideChar(codepage, 0, mbStr, mbLen, NULL, 0)) { wchar_t *wideStr = new wchar_t[wideLen]; // not NUL terminated ::MultiByteToWideChar(codepage, 0, mbStr, mbLen, wideStr, wideLen); changeCase(wideStr, wideLen, caseToConvert); if (int mbLenOut = ::WideCharToMultiByte(codepage, 0, wideStr, wideLen, mbStr, mbLenMax, NULL, NULL)) { // mbStr isn't NUL terminated either at this point mbLen = mbLenOut; execute(SCI_SETTARGETRANGE, start, end); execute(SCI_REPLACETARGET, mbLen, reinterpret_cast(mbStr)); } delete [] wideStr; } delete [] mbStr; return (start + mbLen) - end; } void ScintillaEditView::changeCase(__inout wchar_t * const strWToConvert, const int & nbChars, const TextCase & caseToConvert) const { if (strWToConvert == nullptr || nbChars == 0) return; switch (caseToConvert) { case UPPERCASE: { for (int i = 0; i < nbChars; ++i) { strWToConvert[i] = (wchar_t)(UINT_PTR)::CharUpperW(reinterpret_cast(strWToConvert[i])); } break; } //case UPPERCASE case LOWERCASE: { for (int i = 0; i < nbChars; ++i) { strWToConvert[i] = (wchar_t)(UINT_PTR)::CharLowerW(reinterpret_cast(strWToConvert[i])); } break; } //case LOWERCASE case PROPERCASE_FORCE: case PROPERCASE_BLEND: { for (int i = 0; i < nbChars; ++i) { if (::IsCharAlphaW(strWToConvert[i])) { // Exception for single quote and smart single quote if ((i < 2) ? false : (isCharSingleQuote(strWToConvert[i - 1]) && ::IsCharAlphaNumericW(strWToConvert[i - 2]))) { if (caseToConvert == PROPERCASE_FORCE) strWToConvert[i] = (wchar_t)(UINT_PTR)::CharLowerW(reinterpret_cast(strWToConvert[i])); } else if ((i < 1) ? true : !::IsCharAlphaNumericW(strWToConvert[i - 1])) strWToConvert[i] = (wchar_t)(UINT_PTR)::CharUpperW(reinterpret_cast(strWToConvert[i])); else if (caseToConvert == PROPERCASE_FORCE) strWToConvert[i] = (wchar_t)(UINT_PTR)::CharLowerW(reinterpret_cast(strWToConvert[i])); } } break; } //case PROPERCASE case SENTENCECASE_FORCE: case SENTENCECASE_BLEND: { bool isNewSentence = true; bool wasEolR = false; bool wasEolN = false; for (int i = 0; i < nbChars; ++i) { if (::IsCharAlphaW(strWToConvert[i])) { if (isNewSentence) { strWToConvert[i] = (wchar_t)(UINT_PTR)::CharUpperW(reinterpret_cast(strWToConvert[i])); isNewSentence = false; } else if (caseToConvert == SENTENCECASE_FORCE) { strWToConvert[i] = (wchar_t)(UINT_PTR)::CharLowerW(reinterpret_cast(strWToConvert[i])); } wasEolR = false; wasEolN = false; //An exception if (strWToConvert[i] == L'i' && ((i < 1) ? false : (::iswspace(strWToConvert[i - 1]) || strWToConvert[i - 1] == L'(' || strWToConvert[i - 1] == L'"')) && ((i + 1 == nbChars) ? false : (::iswspace(strWToConvert[i + 1]) || strWToConvert[i + 1] == L'\''))) { strWToConvert[i] = L'I'; } } else if (strWToConvert[i] == L'.' || strWToConvert[i] == L'!' || strWToConvert[i] == L'?') { if ((i + 1 == nbChars) ? true : ::IsCharAlphaNumericW(strWToConvert[i + 1])) isNewSentence = false; else isNewSentence = true; } else if (strWToConvert[i] == L'\r') { if (wasEolR) isNewSentence = true; else wasEolR = true; } else if (strWToConvert[i] == L'\n') { if (wasEolN) isNewSentence = true; else wasEolN = true; } } break; } //case SENTENCECASE case INVERTCASE: { for (int i = 0; i < nbChars; ++i) { if (::IsCharLowerW(strWToConvert[i])) strWToConvert[i] = (wchar_t)(UINT_PTR)::CharUpperW(reinterpret_cast(strWToConvert[i])); else strWToConvert[i] = (wchar_t)(UINT_PTR)::CharLowerW(reinterpret_cast(strWToConvert[i])); } break; } //case INVERTCASE case RANDOMCASE: { for (int i = 0; i < nbChars; ++i) { if (::IsCharAlphaW(strWToConvert[i])) { if (std::rand() & true) strWToConvert[i] = (wchar_t)(UINT_PTR)::CharUpperW(reinterpret_cast(strWToConvert[i])); else strWToConvert[i] = (wchar_t)(UINT_PTR)::CharLowerW(reinterpret_cast(strWToConvert[i])); } } break; } //case RANDOMCASE } //switch (caseToConvert) } void ScintillaEditView::convertSelectedTextTo(const TextCase & caseToConvert) { if (execute(SCI_GETSELECTIONS) > 1) // Multi-Selection || Column mode { execute(SCI_BEGINUNDOACTION); ColumnModeInfos cmi = getColumnModeSelectInfo(); // The fixup logic needs the selections to be sorted, but that has visible side effects, // like the highlighted row jumping around, so try to restore the original order afterwards. bool reversed = !cmi.empty() && cmi.back()._selLpos < cmi.front()._selLpos; std::sort(cmi.begin(), cmi.end(), SortInPositionOrder()); intptr_t sizedelta = 0; for (ColumnModeInfo& info : cmi) { info._selLpos += sizedelta; sizedelta += caseConvertRange(info._selLpos, info._selRpos + sizedelta, caseToConvert); info._selRpos += sizedelta; } if (reversed) std::reverse(cmi.begin(), cmi.end()); setMultiSelections(cmi); execute(SCI_ENDUNDOACTION); return; } size_t selectionStart = execute(SCI_GETSELECTIONSTART); size_t selectionEnd = execute(SCI_GETSELECTIONEND); if (selectionStart < selectionEnd) { selectionEnd += caseConvertRange(selectionStart, selectionEnd, caseToConvert); execute(SCI_SETSEL, selectionStart, selectionEnd); } } pair ScintillaEditView::getWordRange() { size_t caretPos = execute(SCI_GETCURRENTPOS, 0, 0); size_t startPos = execute(SCI_WORDSTARTPOSITION, caretPos, true); size_t endPos = execute(SCI_WORDENDPOSITION, caretPos, true); return pair(startPos, endPos); } bool ScintillaEditView::expandWordSelection() { pair wordRange = getWordRange(); if (wordRange.first != wordRange.second) { execute(SCI_SETSELECTIONSTART, wordRange.first); execute(SCI_SETSELECTIONEND, wordRange.second); return true; } return false; } ColumnModeInfos ScintillaEditView::getColumnModeSelectInfo() { ColumnModeInfos columnModeInfos; if (execute(SCI_GETSELECTIONS) > 1) // Multi-Selection || Column mode { intptr_t nbSel = execute(SCI_GETSELECTIONS); for (int i = 0 ; i < nbSel ; ++i) { intptr_t absPosSelStartPerLine = execute(SCI_GETSELECTIONNANCHOR, i); intptr_t absPosSelEndPerLine = execute(SCI_GETSELECTIONNCARET, i); intptr_t nbVirtualAnchorSpc = execute(SCI_GETSELECTIONNANCHORVIRTUALSPACE, i); intptr_t nbVirtualCaretSpc = execute(SCI_GETSELECTIONNCARETVIRTUALSPACE, i); if (absPosSelStartPerLine == absPosSelEndPerLine && execute(SCI_SELECTIONISRECTANGLE)) { bool dir = nbVirtualAnchorSpc absPosSelEndPerLine) columnModeInfos.push_back(ColumnModeInfo(absPosSelEndPerLine, absPosSelStartPerLine, i, R2L, nbVirtualAnchorSpc, nbVirtualCaretSpc)); else columnModeInfos.push_back(ColumnModeInfo(absPosSelStartPerLine, absPosSelEndPerLine, i, L2R, nbVirtualAnchorSpc, nbVirtualCaretSpc)); } } return columnModeInfos; } void ScintillaEditView::columnReplace(ColumnModeInfos & cmi, const wchar_t *str) { intptr_t totalDiff = 0; for (size_t i = 0, len = cmi.size(); i < len ; ++i) { if (cmi[i].isValid()) { intptr_t len2beReplace = cmi[i]._selRpos - cmi[i]._selLpos; intptr_t diff = lstrlen(str) - len2beReplace; cmi[i]._selLpos += totalDiff; cmi[i]._selRpos += totalDiff; bool hasVirtualSpc = cmi[i]._nbVirtualAnchorSpc > 0; if (hasVirtualSpc) // if virtual space is present, then insert space { for (intptr_t j = 0, k = cmi[i]._selLpos; j < cmi[i]._nbVirtualCaretSpc ; ++j, ++k) { execute(SCI_INSERTTEXT, k, reinterpret_cast(" ")); } cmi[i]._selLpos += cmi[i]._nbVirtualAnchorSpc; cmi[i]._selRpos += cmi[i]._nbVirtualCaretSpc; } execute(SCI_SETTARGETRANGE, cmi[i]._selLpos, cmi[i]._selRpos); WcharMbcsConvertor& wmc = WcharMbcsConvertor::getInstance(); size_t cp = execute(SCI_GETCODEPAGE); const char *strA = wmc.wchar2char(str, cp); execute(SCI_REPLACETARGET, static_cast(-1), reinterpret_cast(strA)); if (hasVirtualSpc) { totalDiff += cmi[i]._nbVirtualAnchorSpc + lstrlen(str); // Now there's no more virtual space cmi[i]._nbVirtualAnchorSpc = 0; cmi[i]._nbVirtualCaretSpc = 0; } else { totalDiff += diff; } cmi[i]._selRpos += diff; } } } void ScintillaEditView::columnReplace(ColumnModeInfos & cmi, size_t initial, size_t incr, size_t repeat, UCHAR format, ColumnEditorParam::leadingChoice lead) { assert(repeat > 0); // If there is no column mode info available, no need to do anything // If required a message can be shown to user, that select column properly or something similar // It is just a double check as taken in callee method (in case this method is called from multiple places) if (cmi.size() <= 0) return; // 0000 00 00 : Dec BASE_10 // 0000 00 01 : Hex BASE_16 // 0000 00 10 : Oct BASE_08 // 0000 00 11 : Bin BASE_02 //Defined in ScintillaEditView.h : //const UCHAR MASK_FORMAT = 0x03; UCHAR f = format & MASK_FORMAT; int base = 10; if (f == BASE_16) base = 16; else if (f == BASE_08) base = 8; else if (f == BASE_02) base = 2; const int stringSize = 512; char str[stringSize]; // Compute the numbers to be placed at each column. std::vector numbers; size_t curNumber = initial; const size_t kiMaxSize = cmi.size(); while (numbers.size() < kiMaxSize) { for (size_t i = 0; i < repeat; i++) { numbers.push_back(curNumber); if (numbers.size() >= kiMaxSize) { break; } } curNumber += incr; } const size_t kibEnd = getNbDigits(*numbers.rbegin(), base); const size_t kibInit = getNbDigits(initial, base); const size_t kib = std::max(kibInit, kibEnd); intptr_t totalDiff = 0; const size_t len = cmi.size(); for (size_t i = 0 ; i < len ; i++) { if (cmi[i].isValid()) { const intptr_t len2beReplaced = cmi[i]._selRpos - cmi[i]._selLpos; const intptr_t diff = kib - len2beReplaced; cmi[i]._selLpos += totalDiff; cmi[i]._selRpos += totalDiff; variedFormatNumber2String(str, stringSize, numbers.at(i), base, kib, lead); const bool hasVirtualSpc = cmi[i]._nbVirtualAnchorSpc > 0; if (hasVirtualSpc) // if virtual space is present, then insert space { for (intptr_t j = 0, k = cmi[i]._selLpos; j < cmi[i]._nbVirtualCaretSpc ; ++j, ++k) { execute(SCI_INSERTTEXT, k, reinterpret_cast(" ")); } cmi[i]._selLpos += cmi[i]._nbVirtualAnchorSpc; cmi[i]._selRpos += cmi[i]._nbVirtualCaretSpc; } execute(SCI_SETTARGETRANGE, cmi[i]._selLpos, cmi[i]._selRpos); execute(SCI_REPLACETARGET, static_cast(-1), reinterpret_cast(str)); if (hasVirtualSpc) { totalDiff += cmi[i]._nbVirtualAnchorSpc + strlen(str); // Now there's no more virtual space cmi[i]._nbVirtualAnchorSpc = 0; cmi[i]._nbVirtualCaretSpc = 0; } else { totalDiff += diff; } cmi[i]._selRpos += diff; } } } bool ScintillaEditView::getIndicatorRange(size_t indicatorNumber, size_t* from, size_t* to, size_t* cur) { size_t curPos = execute(SCI_GETCURRENTPOS); size_t indicMsk = execute(SCI_INDICATORALLONFOR, curPos); if (!(static_cast(indicMsk) & (1 << indicatorNumber))) return false; size_t startPos = execute(SCI_INDICATORSTART, indicatorNumber, curPos); size_t endPos = execute(SCI_INDICATOREND, indicatorNumber, curPos); if ((curPos < startPos) || (curPos > endPos)) return false; if (from) *from = startPos; if (to) *to = endPos; if (cur) *cur = curPos; return true; } void ScintillaEditView::scrollPosToCenter(size_t pos) { _positionRestoreNeeded = false; execute(SCI_GOTOPOS, pos); size_t line = execute(SCI_LINEFROMPOSITION, pos); size_t firstVisibleDisplayLine = execute(SCI_GETFIRSTVISIBLELINE); size_t firstVisibleDocLine = execute(SCI_DOCLINEFROMVISIBLE, firstVisibleDisplayLine); size_t nbLine = execute(SCI_LINESONSCREEN, firstVisibleDisplayLine); size_t lastVisibleDocLine = execute(SCI_DOCLINEFROMVISIBLE, firstVisibleDisplayLine + nbLine); size_t middleLine; if (line - firstVisibleDocLine < lastVisibleDocLine - line) middleLine = firstVisibleDocLine + nbLine/2; else middleLine = lastVisibleDocLine - nbLine/2; size_t nbLines2scroll = line - middleLine; scroll(0, nbLines2scroll); execute(SCI_ENSUREVISIBLEENFORCEPOLICY, line); } void ScintillaEditView::hideLines() { //Folding can screw up hide lines badly if it unfolds a hidden section. //Adding runMarkers(hide, foldstart) directly (folding on single document) can help //Special func on buffer. If markers are added, create notification with location of start, and hide bool set to true size_t startLine = execute(SCI_LINEFROMPOSITION, execute(SCI_GETSELECTIONSTART)); size_t endLine = execute(SCI_LINEFROMPOSITION, execute(SCI_GETSELECTIONEND)); //perform range check: cannot hide very first and very last lines //Offset them one off the edges, and then check if they are within the reasonable size_t nbLines = execute(SCI_GETLINECOUNT); if (nbLines < 3) return; //cannot possibly hide anything if (!startLine) ++startLine; if (endLine == (nbLines-1)) --endLine; if (startLine > endLine) return; //tried to hide line at edge int scope = 0; bool recentMarkerWasOpen = false; auto removeMarker = [this, &scope, &recentMarkerWasOpen](size_t line, int markerMask) { auto state = execute(SCI_MARKERGET, line) & markerMask; bool closePresent = (state & (1 << MARK_HIDELINESEND)) != 0; bool openPresent = (state & (1 << MARK_HIDELINESBEGIN)) != 0; if (closePresent) { execute(SCI_MARKERDELETE, line, MARK_HIDELINESEND); recentMarkerWasOpen = false; --scope; } if (openPresent) { execute(SCI_MARKERDELETE, line, MARK_HIDELINESBEGIN); recentMarkerWasOpen = true; ++scope; } }; size_t startMarker = startLine - 1; size_t endMarker = endLine + 1; // Previous markers must be removed in the selected region: removeMarker(startMarker, 1 << MARK_HIDELINESBEGIN); for (size_t i = startLine; i <= endLine; ++i) removeMarker(i, (1 << MARK_HIDELINESBEGIN) | (1 << MARK_HIDELINESEND)); removeMarker(endMarker, 1 << MARK_HIDELINESEND); // When hiding lines just below/above other hidden lines, // merge them into one hidden section: if (scope == 0 && recentMarkerWasOpen) { // Special case: user wants to hide every line in between other hidden sections. // Both "while" loops are executed (merge with above AND below hidden section): while (scope == 0 && static_cast(startMarker) >= 0) removeMarker(--startMarker, 1 << MARK_HIDELINESBEGIN); while (scope != 0 && endMarker < nbLines) removeMarker(++endMarker, 1 << MARK_HIDELINESEND); } else { // User wants to hide some lines below/above other hidden section. // If true, only one "while" loop is executed (merge with adjacent hidden section): while (scope < 0 && static_cast(startMarker) >= 0) removeMarker(--startMarker, 1 << MARK_HIDELINESBEGIN); while (scope > 0 && endMarker < nbLines) removeMarker(++endMarker, 1 << MARK_HIDELINESEND); } execute(SCI_MARKERADD, startMarker, MARK_HIDELINESBEGIN); execute(SCI_MARKERADD, endMarker, MARK_HIDELINESEND); _currentBuffer->setHideLineChanged(true, startMarker); } bool ScintillaEditView::markerMarginClick(intptr_t lineNumber) { auto state = execute(SCI_MARKERGET, lineNumber); bool openPresent = (state & (1 << MARK_HIDELINESBEGIN)) != 0; bool closePresent = (state & (1 << MARK_HIDELINESEND)) != 0; if (!openPresent && !closePresent) return false; //Special func on buffer. First call show with location of opening marker. Then remove the marker manually if (openPresent) { closePresent = false; // when there are two overlapping markers, always open the lower section _currentBuffer->setHideLineChanged(false, lineNumber); } if (closePresent) { openPresent = false; intptr_t i = lineNumber - 1; for (; i >= 0 && !openPresent; i--) { state = execute(SCI_MARKERGET, i); openPresent = (state & (1 << MARK_HIDELINESBEGIN)) != 0; } if (openPresent) { _currentBuffer->setHideLineChanged(false, i + 1); } else // problem -> only close but no open: let's remove the errno close marker { execute(SCI_MARKERDELETE, lineNumber, MARK_HIDELINESEND); } } return true; } void ScintillaEditView::notifyMarkers(Buffer * buf, bool isHide, size_t location, bool del) { if (buf != _currentBuffer) //if not visible buffer dont do a thing return; runMarkers(isHide, location, false, del); } //Run through full document. When switching in or opening folding //hide is false only when user click on margin void ScintillaEditView::runMarkers(bool doHide, size_t searchStart, bool endOfDoc, bool doDelete) { //Removes markers if opening /* AllLines = (start,ENDOFDOCUMENT) Hide: Run through all lines. Find open hiding marker: set hiding start Find closing: if (hiding): Hide lines between now and start if (endOfDoc = false) return else search for other hidden sections Show: Run through all lines Find open hiding marker set last start Find closing: Show from last start. Stop. Find closed folding header: Show from last start to folding header Skip to LASTCHILD Set last start to lastchild */ size_t maxLines = execute(SCI_GETLINECOUNT); if (doHide) { auto startHiding = searchStart; bool isInSection = false; for (auto i = searchStart; i < maxLines; ++i) { auto state = execute(SCI_MARKERGET, i); if ( ((state & (1 << MARK_HIDELINESEND)) != 0) ) { if (isInSection) { execute(SCI_HIDELINES, startHiding, i-1); if (!endOfDoc) { return; //done, only single section requested } //otherwise keep going } isInSection = false; } if ((state & (1 << MARK_HIDELINESBEGIN)) != 0) { isInSection = true; startHiding = i+1; } } } else { auto startShowing = searchStart; bool isInSection = false; for (auto i = searchStart; i < maxLines; ++i) { auto state = execute(SCI_MARKERGET, i); if ((state & (1 << MARK_HIDELINESBEGIN)) != 0 && !isInSection) { isInSection = true; if (doDelete) { execute(SCI_MARKERDELETE, i, MARK_HIDELINESBEGIN); } else { startShowing = i + 1; } } else if ( (state & (1 << MARK_HIDELINESEND)) != 0) { if (doDelete) { execute(SCI_MARKERDELETE, i, MARK_HIDELINESEND); if (!endOfDoc) { return; //done, only single section requested } //otherwise keep going isInSection = false; } else if (isInSection) { if (startShowing >= i) { //because of fold skipping, we passed the close tag. In that case we cant do anything if (!endOfDoc) { return; } else { isInSection = false; // assume we passed the close tag continue; } } execute(SCI_SHOWLINES, startShowing, i-1); if (!endOfDoc) { return; //done, only single section requested } //otherwise keep going isInSection = false; } } auto levelLine = execute(SCI_GETFOLDLEVEL, i, 0); if (levelLine & SC_FOLDLEVELHEADERFLAG) { //fold section. Dont show lines if fold is closed if (isInSection && !isFolded(i)) { execute(SCI_SHOWLINES, startShowing, i); } } } } } void ScintillaEditView::restoreHiddenLines() { int line = 0; while (line != -1) { line = static_cast(execute(SCI_MARKERNEXT, line, 1 << MARK_HIDELINESBEGIN)); if (line != -1) { int startHiding = line + 1; line = static_cast(execute(SCI_MARKERNEXT, line, 1 << MARK_HIDELINESEND)); if (line != -1) { execute(SCI_HIDELINES, startHiding, line - 1); } } } } void ScintillaEditView::setTabSettings(Lang* lang) { if (lang && lang->_tabSize != -1 && lang->_tabSize != 0) { if (lang->_langID == L_JAVASCRIPT) { Lang* ljs = NppParameters::getInstance().getLangFromID(L_JS); execute(SCI_SETTABWIDTH, ljs->_tabSize > 0 ? ljs->_tabSize : lang->_tabSize); execute(SCI_SETUSETABS, !ljs->_isTabReplacedBySpace); execute(SCI_SETBACKSPACEUNINDENTS, ljs->_isBackspaceUnindent); } else { execute(SCI_SETTABWIDTH, lang->_tabSize); execute(SCI_SETUSETABS, !lang->_isTabReplacedBySpace); execute(SCI_SETBACKSPACEUNINDENTS, lang->_isBackspaceUnindent); } } else { const NppGUI& nppgui = NppParameters::getInstance().getNppGUI(); execute(SCI_SETTABWIDTH, nppgui._tabSize > 0 ? nppgui._tabSize : 4); execute(SCI_SETUSETABS, !nppgui._tabReplacedBySpace); execute(SCI_SETBACKSPACEUNINDENTS, nppgui._backspaceUnindent); } } void ScintillaEditView::insertNewLineAboveCurrentLine() { wstring newline = getEOLString(); const auto current_line = getCurrentLineNumber(); if (current_line == 0) { // Special handling if caret is at first line. insertGenericTextFrom(0, newline.c_str()); } else { const auto eol_length = newline.length(); const auto position = execute(SCI_POSITIONFROMLINE, current_line) - eol_length; insertGenericTextFrom(position, newline.c_str()); } execute(SCI_SETEMPTYSELECTION, execute(SCI_POSITIONFROMLINE, current_line)); } void ScintillaEditView::insertNewLineBelowCurrentLine() { wstring newline = getEOLString(); const auto line_count = execute(SCI_GETLINECOUNT); const auto current_line = getCurrentLineNumber(); if (current_line == line_count - 1) { // Special handling if caret is at last line. appandGenericText(newline.c_str()); } else { const auto eol_length = newline.length(); const auto position = eol_length + execute(SCI_GETLINEENDPOSITION, current_line); insertGenericTextFrom(position, newline.c_str()); } execute(SCI_SETEMPTYSELECTION, execute(SCI_POSITIONFROMLINE, current_line + 1)); } void ScintillaEditView::sortLines(size_t fromLine, size_t toLine, ISorter* pSort) { if (fromLine >= toLine) { return; } const auto startPos = execute(SCI_POSITIONFROMLINE, fromLine); const auto endPos = execute(SCI_POSITIONFROMLINE, toLine) + execute(SCI_LINELENGTH, toLine); const wstring text = getGenericTextAsString(startPos, endPos); std::vector splitText; stringSplit(text, getEOLString(), splitText); const size_t lineCount = execute(SCI_GETLINECOUNT); const bool sortEntireDocument = toLine == lineCount - 1; if (!sortEntireDocument) { if (splitText.rbegin()->empty()) { splitText.pop_back(); } } assert(toLine - fromLine + 1 == splitText.size()); pSort->sort(splitText); wstring joined; stringJoin(splitText, getEOLString(), joined); if (sortEntireDocument) { assert(joined.length() == text.length()); } else { assert(joined.length() + getEOLString().length() == text.length()); joined += getEOLString(); } if (text != joined) { replaceTarget(joined.c_str(), startPos, endPos); } } bool ScintillaEditView::isTextDirectionRTL() const { long exStyle = static_cast(::GetWindowLongPtr(_hSelf, GWL_EXSTYLE)); return (exStyle & WS_EX_LAYOUTRTL) != 0; } void ScintillaEditView::changeTextDirection(bool isRTL) { if (isTextDirectionRTL() == isRTL) return; NppParameters& nppParamInst = NppParameters::getInstance(); if (isRTL && nppParamInst.getNppGUI()._writeTechnologyEngine == directWriteTechnology) // RTL is not compatible with Direct Write Technology { static bool theWarningIsGiven = false; if (!theWarningIsGiven) { (nppParamInst.getNativeLangSpeaker())->messageBox("RTLvsDirectWrite", getHSelf(), L"RTL is not compatible with Direct Write mode. Please disable DirectWrite mode in MISC. section of Preferences dialog, and restart Notepad++.", L"Cannot run RTL", MB_OK | MB_APPLMODAL); theWarningIsGiven = true; } return; } long exStyle = static_cast(::GetWindowLongPtr(_hSelf, GWL_EXSTYLE)); exStyle = isRTL ? (exStyle | WS_EX_LAYOUTRTL) : (exStyle & (~WS_EX_LAYOUTRTL)); ::SetWindowLongPtr(_hSelf, GWL_EXSTYLE, exStyle); if (isRTL) { execute(SCI_ASSIGNCMDKEY, SCK_RIGHT, SCI_CHARLEFT); execute(SCI_ASSIGNCMDKEY, SCK_RIGHT + (SCMOD_SHIFT << 16), SCI_CHARLEFTEXTEND); execute(SCI_ASSIGNCMDKEY, SCK_RIGHT + ((SCMOD_SHIFT + SCMOD_ALT) << 16), SCI_CHARLEFTRECTEXTEND); execute(SCI_ASSIGNCMDKEY, SCK_RIGHT + (SCMOD_CTRL << 16), SCI_WORDLEFT); execute(SCI_ASSIGNCMDKEY, SCK_RIGHT + ((SCMOD_SHIFT + SCMOD_CTRL) << 16), SCI_WORDLEFTEXTEND); execute(SCI_ASSIGNCMDKEY, SCK_LEFT, SCI_CHARRIGHT); execute(SCI_ASSIGNCMDKEY, SCK_LEFT + (SCMOD_SHIFT << 16), SCI_CHARRIGHTEXTEND); execute(SCI_ASSIGNCMDKEY, SCK_LEFT + ((SCMOD_SHIFT + SCMOD_ALT) << 16), SCI_CHARRIGHTRECTEXTEND); execute(SCI_ASSIGNCMDKEY, SCK_LEFT + (SCMOD_CTRL << 16), SCI_WORDRIGHT); execute(SCI_ASSIGNCMDKEY, SCK_LEFT + ((SCMOD_SHIFT + SCMOD_CTRL) << 16), SCI_WORDRIGHTEXTEND); } else { execute(SCI_ASSIGNCMDKEY, SCK_RIGHT, SCI_CHARRIGHT); execute(SCI_ASSIGNCMDKEY, SCK_RIGHT + (SCMOD_SHIFT << 16), SCI_CHARRIGHTEXTEND); execute(SCI_ASSIGNCMDKEY, SCK_RIGHT + ((SCMOD_SHIFT + SCMOD_ALT) << 16), SCI_CHARRIGHTRECTEXTEND); execute(SCI_ASSIGNCMDKEY, SCK_RIGHT + (SCMOD_CTRL << 16), SCI_WORDRIGHT); execute(SCI_ASSIGNCMDKEY, SCK_RIGHT + ((SCMOD_SHIFT + SCMOD_CTRL) << 16), SCI_WORDRIGHTEXTEND); execute(SCI_ASSIGNCMDKEY, SCK_LEFT, SCI_CHARLEFT); execute(SCI_ASSIGNCMDKEY, SCK_LEFT + (SCMOD_SHIFT << 16), SCI_CHARLEFTEXTEND); execute(SCI_ASSIGNCMDKEY, SCK_LEFT + ((SCMOD_SHIFT + SCMOD_ALT) << 16), SCI_CHARLEFTRECTEXTEND); execute(SCI_ASSIGNCMDKEY, SCK_LEFT + (SCMOD_CTRL << 16), SCI_WORDLEFT); execute(SCI_ASSIGNCMDKEY, SCK_LEFT + ((SCMOD_SHIFT + SCMOD_CTRL) << 16), SCI_WORDLEFTEXTEND); } Buffer* buf = getCurrentBuffer(); buf->setRTL(isRTL); } wstring ScintillaEditView::getEOLString() const { intptr_t eol_mode = execute(SCI_GETEOLMODE); if (eol_mode == SC_EOL_CRLF) { return L"\r\n"; } else if (eol_mode == SC_EOL_LF) { return L"\n"; } else { return L"\r"; } } void ScintillaEditView::setBorderEdge(bool doWithBorderEdge) { long style = static_cast(::GetWindowLongPtr(_hSelf, GWL_STYLE)); long exStyle = static_cast(::GetWindowLongPtr(_hSelf, GWL_EXSTYLE)); if (NppDarkMode::isEnabled()) { exStyle &= ~WS_EX_CLIENTEDGE; if (doWithBorderEdge) style |= WS_BORDER; else style &= ~WS_BORDER; } else { style &= ~WS_BORDER; if (doWithBorderEdge) exStyle |= WS_EX_CLIENTEDGE; else exStyle &= ~WS_EX_CLIENTEDGE; } ::SetWindowLongPtr(_hSelf, GWL_STYLE, style); ::SetWindowLongPtr(_hSelf, GWL_EXSTYLE, exStyle); ::SetWindowPos(_hSelf, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); } void ScintillaEditView::getFoldColor(COLORREF& fgColor, COLORREF& bgColor, COLORREF& activeFgColor) { StyleArray & stylers = NppParameters::getInstance().getMiscStylerArray(); const Style * pStyle = stylers.findByName(L"Fold"); if (pStyle) { fgColor = pStyle->_bgColor; bgColor = pStyle->_fgColor; } pStyle = stylers.findByName(L"Fold active"); if (pStyle) { activeFgColor = pStyle->_fgColor; } } int ScintillaEditView::getTextZoneWidth() const { RECT editorRect; getClientRect(editorRect); intptr_t marginWidths = 0; for (int m = 0; m < 4; ++m) { marginWidths += execute(SCI_GETMARGINWIDTHN, m); } return editorRect.right - editorRect.left - static_cast(marginWidths); } pair ScintillaEditView::getSelectedCharsAndLinesCount(long long maxSelectionsForLineCount /* = -1 */) const { pair selectedCharsAndLines(0, 0); selectedCharsAndLines.first = getUnicodeSelectedLength(); size_t nbSelections = execute(SCI_GETSELECTIONS); if (nbSelections == 1) { pair lineRange = getSelectionLinesRange(); selectedCharsAndLines.second = lineRange.second - lineRange.first + 1; } else if (execute(SCI_SELECTIONISRECTANGLE)) { selectedCharsAndLines.second = nbSelections; } else if ((maxSelectionsForLineCount == -1) || // -1 means process ALL of the selections (nbSelections <= static_cast(maxSelectionsForLineCount))) { // selections are obtained from Scintilla in the order user creates them, // not in a lowest-to-highest position-based order; // to be able to get a line-count that can't count the same line more than once, // we have to reorder the lines touched // by selection into low-to-high line number order before processing them further vector< pair > v; for (size_t s = 0; s < nbSelections; ++s) { v.push_back(getSelectionLinesRange(s)); } sort(v.begin(), v.end()); intptr_t previousSecondLine = -1; for (const auto& lineRange : v) { selectedCharsAndLines.second += lineRange.second - lineRange.first; if (lineRange.first != static_cast(previousSecondLine)) { ++selectedCharsAndLines.second; } previousSecondLine = static_cast(lineRange.second); } } return selectedCharsAndLines; } size_t ScintillaEditView::getUnicodeSelectedLength() const { size_t length = 0; size_t nbSelections = execute(SCI_GETSELECTIONS); for (size_t s = 0; s < nbSelections; ++s) { size_t start = execute(SCI_GETSELECTIONNSTART, s); size_t end = execute(SCI_GETSELECTIONNEND, s); length += execute(SCI_COUNTCHARACTERS, start, end); } return length; } void ScintillaEditView::markedTextToClipboard(int indiStyle, bool doAll /*= false*/) { int styleIndicators[] = { SCE_UNIVERSAL_FOUND_STYLE_EXT1, SCE_UNIVERSAL_FOUND_STYLE_EXT2, SCE_UNIVERSAL_FOUND_STYLE_EXT3, SCE_UNIVERSAL_FOUND_STYLE_EXT4, SCE_UNIVERSAL_FOUND_STYLE_EXT5, -1 // end signifier }; if (!doAll) { styleIndicators[0] = indiStyle; styleIndicators[1] = -1; } // vector of pairs: starting position of styled text, and styled text std::vector> styledVect; const wstring cr = L"\r"; const wstring lf = L"\n"; bool textContainsLineEndingChar = false; for (int si = 0; styleIndicators[si] != -1; ++si) { size_t pos = execute(SCI_INDICATOREND, styleIndicators[si], 0); if (pos > 0) { bool atEndOfIndic = execute(SCI_INDICATORVALUEAT, styleIndicators[si], 0) != 0; size_t prevPos = pos; if (atEndOfIndic) prevPos = 0; do { if (atEndOfIndic) { wstring styledText = getGenericTextAsString(prevPos, pos); if (!textContainsLineEndingChar) { if (styledText.find(cr) != std::string::npos || styledText.find(lf) != std::string::npos) { textContainsLineEndingChar = true; } } styledVect.push_back(::make_pair(prevPos, styledText)); } atEndOfIndic = !atEndOfIndic; prevPos = pos; pos = execute(SCI_INDICATOREND, styleIndicators[si], pos); } while (pos != prevPos); } } if (styledVect.size() > 0) { if (doAll) { // sort by starting position of styled text std::sort(styledVect.begin(), styledVect.end()); } const wstring delim = (textContainsLineEndingChar && styledVect.size() > 1) ? L"\r\n----\r\n" : L"\r\n"; wstring joined; for (const auto& item : styledVect) { joined += delim + item.second; } joined = joined.substr(delim.length()); if (styledVect.size() > 1) { joined += L"\r\n"; } str2Clipboard(joined, NULL); } } void ScintillaEditView::removeAnyDuplicateLines() { size_t fromLine = 0, toLine = 0; bool hasLineSelection = false; auto selStart = execute(SCI_GETSELECTIONSTART); auto selEnd = execute(SCI_GETSELECTIONEND); hasLineSelection = selStart != selEnd; if (hasLineSelection) { const pair lineRange = getSelectionLinesRange(); // One single line selection is not allowed. if (lineRange.first == lineRange.second) { return; } fromLine = lineRange.first; toLine = lineRange.second; } else { // No selection. fromLine = 0; toLine = execute(SCI_GETLINECOUNT) - 1; } if (fromLine >= toLine) { return; } const auto startPos = execute(SCI_POSITIONFROMLINE, fromLine); const auto endPos = execute(SCI_POSITIONFROMLINE, toLine) + execute(SCI_LINELENGTH, toLine); const wstring text = getGenericTextAsString(startPos, endPos); std::vector linesVect; stringSplit(text, getEOLString(), linesVect); const size_t lineCount = execute(SCI_GETLINECOUNT); const bool doingEntireDocument = toLine == lineCount - 1; if (!doingEntireDocument) { if (linesVect.rbegin()->empty()) { linesVect.pop_back(); } } size_t origSize = linesVect.size(); size_t newSize = vecRemoveDuplicates(linesVect); if (origSize != newSize) { wstring joined; stringJoin(linesVect, getEOLString(), joined); if (!doingEntireDocument) { joined += getEOLString(); } if (text != joined) { replaceTarget(joined.c_str(), startPos, endPos); } } } bool ScintillaEditView::pasteToMultiSelection() const { size_t nbSelections = execute(SCI_GETSELECTIONS); if (nbSelections <= 1) return false; // "MSDEVColumnSelect" is column format from Scintilla CLIPFORMAT cfColumnSelect = static_cast(::RegisterClipboardFormat(L"MSDEVColumnSelect")); if (!::IsClipboardFormatAvailable(cfColumnSelect) || !::OpenClipboard(NULL)) return false; HANDLE clipboardData = ::GetClipboardData(CF_UNICODETEXT); if (!clipboardData) { ::CloseClipboard(); return false; } LPVOID clipboardDataPtr = ::GlobalLock(clipboardData); if (!clipboardDataPtr) { ::CloseClipboard(); return false; } wstring clipboardStr = static_cast(clipboardDataPtr); ::GlobalUnlock(clipboardData); ::CloseClipboard(); vector clipboardStrings; stringSplit(clipboardStr, getEOLString(), clipboardStrings); clipboardStrings.erase(clipboardStrings.cend() - 1); // remove the last empty string size_t nbClipboardStr = clipboardStrings.size(); if (nbSelections >= nbClipboardStr) // enough holes for every insertion, keep holes empty if there are some left { execute(SCI_BEGINUNDOACTION); for (size_t i = 0; i < nbClipboardStr; ++i) { LRESULT posStart = execute(SCI_GETSELECTIONNSTART, i); LRESULT posEnd = execute(SCI_GETSELECTIONNEND, i); replaceTarget(clipboardStrings[i].c_str(), posStart, posEnd); posStart += clipboardStrings[i].length(); execute(SCI_SETSELECTIONNSTART, i, posStart); execute(SCI_SETSELECTIONNEND, i, posStart); } execute(SCI_ENDUNDOACTION); return true; } else if (nbSelections < nbClipboardStr) // not enough holes for insertion, every hole has several insertions { size_t nbStr2takeFromClipboard = nbClipboardStr / nbSelections; execute(SCI_BEGINUNDOACTION); size_t j = 0; for (size_t i = 0; i < nbSelections; ++i) { LRESULT posStart = execute(SCI_GETSELECTIONNSTART, i); LRESULT posEnd = execute(SCI_GETSELECTIONNEND, i); wstring severalStr; wstring eol = getEOLString(); for (size_t k = 0; k < nbStr2takeFromClipboard && j < nbClipboardStr; ++k) { severalStr += clipboardStrings[j]; severalStr += eol; ++j; } // remove the latest added EOL severalStr.erase(severalStr.length() - eol.length()); replaceTarget(severalStr.c_str(), posStart, posEnd); posStart += severalStr.length(); execute(SCI_SETSELECTIONNSTART, i, posStart); execute(SCI_SETSELECTIONNEND, i, posStart); } execute(SCI_ENDUNDOACTION); return true; } return false; }