xref: /MusicPlayer2/scintilla/src/ScintillaBase.cxx (revision f6e57922066e258d96270dbad8a21f4d0d0d85e3)
1 // Scintilla source code edit control
2 /** @file ScintillaBase.cxx
3  ** An enhanced subclass of Editor with calltips, autocomplete and context menu.
4  **/
5 // Copyright 1998-2003 by Neil Hodgson <[email protected]>
6 // The License.txt file describes the conditions under which this software may be distributed.
7 
8 #include <cstddef>
9 #include <cstdlib>
10 #include <cassert>
11 #include <cstring>
12 
13 #include <stdexcept>
14 #include <string>
15 #include <string_view>
16 #include <vector>
17 #include <map>
18 #include <algorithm>
19 #include <memory>
20 
21 #include "Platform.h"
22 
23 #include "ILoader.h"
24 #include "ILexer.h"
25 #include "Scintilla.h"
26 
27 #include "SciLexer.h"
28 
29 #include "PropSetSimple.h"
30 #include "CharacterCategory.h"
31 
32 #include "LexerModule.h"
33 #include "Catalogue.h"
34 
35 #include "Position.h"
36 #include "UniqueString.h"
37 #include "SplitVector.h"
38 #include "Partitioning.h"
39 #include "RunStyles.h"
40 #include "ContractionState.h"
41 #include "CellBuffer.h"
42 #include "CallTip.h"
43 #include "KeyMap.h"
44 #include "Indicator.h"
45 #include "LineMarker.h"
46 #include "Style.h"
47 #include "ViewStyle.h"
48 #include "CharClassify.h"
49 #include "Decoration.h"
50 #include "CaseFolder.h"
51 #include "Document.h"
52 #include "Selection.h"
53 #include "PositionCache.h"
54 #include "EditModel.h"
55 #include "MarginView.h"
56 #include "EditView.h"
57 #include "Editor.h"
58 #include "AutoComplete.h"
59 #include "ScintillaBase.h"
60 
61 #include "ExternalLexer.h"
62 #include "../win32/resource.h"
63 
64 #undef max
65 #undef min
66 
67 using namespace Scintilla;
68 
69 static std::wstring LoadStringRes(UINT id, const wchar_t* back_str = nullptr)
70 {
71     wchar_t buf[256];
72     HMODULE hModule = GetModuleHandle(_T("SciLexer.dll"));
73     LoadStringW(hModule, id, buf, 256);
74     std::wstring result = buf;
75     if (result.empty())
76     {
77         switch (id)
78         {
79         case IDS_UNDO:
80             result = L"Undo";
81             break;
82         case IDS_REDO:
83             result = L"Rndo";
84             break;
85         case IDS_CUT:
86             result = L"Cut";
87             break;
88         case IDS_COPY:
89             result = L"Copy";
90             break;
91         case IDS_PASTE:
92             result = L"Paste";
93             break;
94         case IDS_DELETE:
95             result = L"Clear";
96             break;
97         case IDS_SELECT_ALL:
98             result = L"Select All";
99             break;
100         default:
101             break;
102         }
103     }
104     if (back_str != nullptr)
105         result += back_str;
106     return result;
107 }
108 
109 ScintillaBase::ScintillaBase() {
110 	displayPopupMenu = SC_POPUP_ALL;
111 	listType = 0;
112 	maxListWidth = 0;
113 	multiAutoCMode = SC_MULTIAUTOC_ONCE;
114 #ifdef SCI_LEXER
115 	Scintilla_LinkLexers();
116 #endif
117 }
118 
119 ScintillaBase::~ScintillaBase() {
120 }
121 
122 void ScintillaBase::Finalise() {
123 	Editor::Finalise();
124 	popup.Destroy();
125 }
126 
127 void ScintillaBase::AddCharUTF(const char *s, unsigned int len, bool /*treatAsDBCS*/) {
128 	InsertCharacter(std::string_view(s, len), CharacterSource::directInput);
129 }
130 
131 void ScintillaBase::InsertCharacter(std::string_view sv, CharacterSource charSource) {
132 	const bool isFillUp = ac.Active() && ac.IsFillUpChar(sv[0]);
133 	if (!isFillUp) {
134 		Editor::InsertCharacter(sv, charSource);
135 	}
136 	if (ac.Active()) {
137 		AutoCompleteCharacterAdded(sv[0]);
138 		// For fill ups add the character after the autocompletion has
139 		// triggered so containers see the key so can display a calltip.
140 		if (isFillUp) {
141 			Editor::InsertCharacter(sv, charSource);
142 		}
143 	}
144 }
145 
146 void ScintillaBase::Command(int cmdId) {
147 
148 	switch (cmdId) {
149 
150 	case idAutoComplete:  	// Nothing to do
151 
152 		break;
153 
154 	case idCallTip:  	// Nothing to do
155 
156 		break;
157 
158 	case idcmdUndo:
159 		WndProc(SCI_UNDO, 0, 0);
160 		break;
161 
162 	case idcmdRedo:
163 		WndProc(SCI_REDO, 0, 0);
164 		break;
165 
166 	case idcmdCut:
167 		WndProc(SCI_CUT, 0, 0);
168 		break;
169 
170 	case idcmdCopy:
171 		WndProc(SCI_COPY, 0, 0);
172 		break;
173 
174 	case idcmdPaste:
175 		WndProc(SCI_PASTE, 0, 0);
176 		break;
177 
178 	case idcmdDelete:
179 		WndProc(SCI_CLEAR, 0, 0);
180 		break;
181 
182 	case idcmdSelectAll:
183 		WndProc(SCI_SELECTALL, 0, 0);
184 		break;
185 	}
186 }
187 
188 int ScintillaBase::KeyCommand(unsigned int iMessage) {
189 	// Most key commands cancel autocompletion mode
190 	if (ac.Active()) {
191 		switch (iMessage) {
192 			// Except for these
193 		case SCI_LINEDOWN:
194 			AutoCompleteMove(1);
195 			return 0;
196 		case SCI_LINEUP:
197 			AutoCompleteMove(-1);
198 			return 0;
199 		case SCI_PAGEDOWN:
200 			AutoCompleteMove(ac.lb->GetVisibleRows());
201 			return 0;
202 		case SCI_PAGEUP:
203 			AutoCompleteMove(-ac.lb->GetVisibleRows());
204 			return 0;
205 		case SCI_VCHOME:
206 			AutoCompleteMove(-5000);
207 			return 0;
208 		case SCI_LINEEND:
209 			AutoCompleteMove(5000);
210 			return 0;
211 		case SCI_DELETEBACK:
212 			DelCharBack(true);
213 			AutoCompleteCharacterDeleted();
214 			EnsureCaretVisible();
215 			return 0;
216 		case SCI_DELETEBACKNOTLINE:
217 			DelCharBack(false);
218 			AutoCompleteCharacterDeleted();
219 			EnsureCaretVisible();
220 			return 0;
221 		case SCI_TAB:
222 			AutoCompleteCompleted(0, SC_AC_TAB);
223 			return 0;
224 		case SCI_NEWLINE:
225 			AutoCompleteCompleted(0, SC_AC_NEWLINE);
226 			return 0;
227 
228 		default:
229 			AutoCompleteCancel();
230 		}
231 	}
232 
233 	if (ct.inCallTipMode) {
234 		if (
235 		    (iMessage != SCI_CHARLEFT) &&
236 		    (iMessage != SCI_CHARLEFTEXTEND) &&
237 		    (iMessage != SCI_CHARRIGHT) &&
238 		    (iMessage != SCI_CHARRIGHTEXTEND) &&
239 		    (iMessage != SCI_EDITTOGGLEOVERTYPE) &&
240 		    (iMessage != SCI_DELETEBACK) &&
241 		    (iMessage != SCI_DELETEBACKNOTLINE)
242 		) {
243 			ct.CallTipCancel();
244 		}
245 		if ((iMessage == SCI_DELETEBACK) || (iMessage == SCI_DELETEBACKNOTLINE)) {
246 			if (sel.MainCaret() <= ct.posStartCallTip) {
247 				ct.CallTipCancel();
248 			}
249 		}
250 	}
251 	return Editor::KeyCommand(iMessage);
252 }
253 
254 void ScintillaBase::ListNotify(ListBoxEvent *plbe) {
255 	switch (plbe->event) {
256 	case ListBoxEvent::EventType::selectionChange:
257 		AutoCompleteSelection();
258 		break;
259 	case ListBoxEvent::EventType::doubleClick:
260 		AutoCompleteCompleted(0, SC_AC_DOUBLECLICK);
261 		break;
262 	}
263 }
264 
265 void ScintillaBase::AutoCompleteInsert(Sci::Position startPos, Sci::Position removeLen, const char *text, Sci::Position textLen) {
266 	UndoGroup ug(pdoc);
267 	if (multiAutoCMode == SC_MULTIAUTOC_ONCE) {
268 		pdoc->DeleteChars(startPos, removeLen);
269 		const Sci::Position lengthInserted = pdoc->InsertString(startPos, text, textLen);
270 		SetEmptySelection(startPos + lengthInserted);
271 	} else {
272 		// SC_MULTIAUTOC_EACH
273 		for (size_t r=0; r<sel.Count(); r++) {
274 			if (!RangeContainsProtected(sel.Range(r).Start().Position(),
275 				sel.Range(r).End().Position())) {
276 				Sci::Position positionInsert = sel.Range(r).Start().Position();
277 				positionInsert = RealizeVirtualSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
278 				if (positionInsert - removeLen >= 0) {
279 					positionInsert -= removeLen;
280 					pdoc->DeleteChars(positionInsert, removeLen);
281 				}
282 				const Sci::Position lengthInserted = pdoc->InsertString(positionInsert, text, textLen);
283 				if (lengthInserted > 0) {
284 					sel.Range(r).caret.SetPosition(positionInsert + lengthInserted);
285 					sel.Range(r).anchor.SetPosition(positionInsert + lengthInserted);
286 				}
287 				sel.Range(r).ClearVirtualSpace();
288 			}
289 		}
290 	}
291 }
292 
293 void ScintillaBase::AutoCompleteStart(Sci::Position lenEntered, const char *list) {
294 	//Platform::DebugPrintf("AutoComplete %s\n", list);
295 	ct.CallTipCancel();
296 
297 	if (ac.chooseSingle && (listType == 0)) {
298 		if (list && !strchr(list, ac.GetSeparator())) {
299 			const char *typeSep = strchr(list, ac.GetTypesep());
300 			const Sci::Position lenInsert = typeSep ?
301 				(typeSep-list) : strlen(list);
302 			if (ac.ignoreCase) {
303 				// May need to convert the case before invocation, so remove lenEntered characters
304 				AutoCompleteInsert(sel.MainCaret() - lenEntered, lenEntered, list, lenInsert);
305 			} else {
306 				AutoCompleteInsert(sel.MainCaret(), 0, list + lenEntered, lenInsert - lenEntered);
307 			}
308 			ac.Cancel();
309 			return;
310 		}
311 	}
312 	ac.Start(wMain, idAutoComplete, sel.MainCaret(), PointMainCaret(),
313 				lenEntered, vs.lineHeight, IsUnicodeMode(), technology);
314 
315 	const PRectangle rcClient = GetClientRectangle();
316 	Point pt = LocationFromPosition(sel.MainCaret() - lenEntered);
317 	PRectangle rcPopupBounds = wMain.GetMonitorRect(pt);
318 	if (rcPopupBounds.Height() == 0)
319 		rcPopupBounds = rcClient;
320 
321 	int heightLB = ac.heightLBDefault;
322 	int widthLB = ac.widthLBDefault;
323 	if (pt.x >= rcClient.right - widthLB) {
324 		HorizontalScrollTo(static_cast<int>(xOffset + pt.x - rcClient.right + widthLB));
325 		Redraw();
326 		pt = PointMainCaret();
327 	}
328 	if (wMargin.Created()) {
329 		pt = pt + GetVisibleOriginInMain();
330 	}
331 	PRectangle rcac;
332 	rcac.left = pt.x - ac.lb->CaretFromEdge();
333 	if (pt.y >= rcPopupBounds.bottom - heightLB &&  // Won't fit below.
334 	        pt.y >= (rcPopupBounds.bottom + rcPopupBounds.top) / 2) { // and there is more room above.
335 		rcac.top = pt.y - heightLB;
336 		if (rcac.top < rcPopupBounds.top) {
337 			heightLB -= static_cast<int>(rcPopupBounds.top - rcac.top);
338 			rcac.top = rcPopupBounds.top;
339 		}
340 	} else {
341 		rcac.top = pt.y + vs.lineHeight;
342 	}
343 	rcac.right = rcac.left + widthLB;
344 	rcac.bottom = static_cast<XYPOSITION>(std::min(static_cast<int>(rcac.top) + heightLB, static_cast<int>(rcPopupBounds.bottom)));
345 	ac.lb->SetPositionRelative(rcac, &wMain);
346 	ac.lb->SetFont(vs.styles[STYLE_DEFAULT].font);
347 	const unsigned int aveCharWidth = static_cast<unsigned int>(vs.styles[STYLE_DEFAULT].aveCharWidth);
348 	ac.lb->SetAverageCharWidth(aveCharWidth);
349 	ac.lb->SetDelegate(this);
350 
351 	ac.SetList(list ? list : "");
352 
353 	// Fiddle the position of the list so it is right next to the target and wide enough for all its strings
354 	PRectangle rcList = ac.lb->GetDesiredRect();
355 	const int heightAlloced = static_cast<int>(rcList.bottom - rcList.top);
356 	widthLB = std::max(widthLB, static_cast<int>(rcList.right - rcList.left));
357 	if (maxListWidth != 0)
358 		widthLB = std::min(widthLB, static_cast<int>(aveCharWidth)*maxListWidth);
359 	// Make an allowance for large strings in list
360 	rcList.left = pt.x - ac.lb->CaretFromEdge();
361 	rcList.right = rcList.left + widthLB;
362 	if (((pt.y + vs.lineHeight) >= (rcPopupBounds.bottom - heightAlloced)) &&  // Won't fit below.
363 	        ((pt.y + vs.lineHeight / 2) >= (rcPopupBounds.bottom + rcPopupBounds.top) / 2)) { // and there is more room above.
364 		rcList.top = pt.y - heightAlloced;
365 	} else {
366 		rcList.top = pt.y + vs.lineHeight;
367 	}
368 	rcList.bottom = rcList.top + heightAlloced;
369 	ac.lb->SetPositionRelative(rcList, &wMain);
370 	ac.Show(true);
371 	if (lenEntered != 0) {
372 		AutoCompleteMoveToCurrentWord();
373 	}
374 }
375 
376 void ScintillaBase::AutoCompleteCancel() {
377 	if (ac.Active()) {
378 		SCNotification scn = {};
379 		scn.nmhdr.code = SCN_AUTOCCANCELLED;
380 		scn.wParam = 0;
381 		scn.listType = 0;
382 		NotifyParent(scn);
383 	}
384 	ac.Cancel();
385 }
386 
387 void ScintillaBase::AutoCompleteMove(int delta) {
388 	ac.Move(delta);
389 }
390 
391 void ScintillaBase::AutoCompleteMoveToCurrentWord() {
392 	std::string wordCurrent = RangeText(ac.posStart - ac.startLen, sel.MainCaret());
393 	ac.Select(wordCurrent.c_str());
394 }
395 
396 void ScintillaBase::AutoCompleteSelection() {
397 	const int item = ac.GetSelection();
398 	std::string selected;
399 	if (item != -1) {
400 		selected = ac.GetValue(item);
401 	}
402 
403 	SCNotification scn = {};
404 	scn.nmhdr.code = SCN_AUTOCSELECTIONCHANGE;
405 	scn.message = 0;
406 	scn.wParam = listType;
407 	scn.listType = listType;
408 	const Sci::Position firstPos = ac.posStart - ac.startLen;
409 	scn.position = firstPos;
410 	scn.lParam = firstPos;
411 	scn.text = selected.c_str();
412 	NotifyParent(scn);
413 }
414 
415 void ScintillaBase::AutoCompleteCharacterAdded(char ch) {
416 	if (ac.IsFillUpChar(ch)) {
417 		AutoCompleteCompleted(ch, SC_AC_FILLUP);
418 	} else if (ac.IsStopChar(ch)) {
419 		AutoCompleteCancel();
420 	} else {
421 		AutoCompleteMoveToCurrentWord();
422 	}
423 }
424 
425 void ScintillaBase::AutoCompleteCharacterDeleted() {
426 	if (sel.MainCaret() < ac.posStart - ac.startLen) {
427 		AutoCompleteCancel();
428 	} else if (ac.cancelAtStartPos && (sel.MainCaret() <= ac.posStart)) {
429 		AutoCompleteCancel();
430 	} else {
431 		AutoCompleteMoveToCurrentWord();
432 	}
433 	SCNotification scn = {};
434 	scn.nmhdr.code = SCN_AUTOCCHARDELETED;
435 	scn.wParam = 0;
436 	scn.listType = 0;
437 	NotifyParent(scn);
438 }
439 
440 void ScintillaBase::AutoCompleteCompleted(char ch, unsigned int completionMethod) {
441 	const int item = ac.GetSelection();
442 	if (item == -1) {
443 		AutoCompleteCancel();
444 		return;
445 	}
446 	const std::string selected = ac.GetValue(item);
447 
448 	ac.Show(false);
449 
450 	SCNotification scn = {};
451 	scn.nmhdr.code = listType > 0 ? SCN_USERLISTSELECTION : SCN_AUTOCSELECTION;
452 	scn.message = 0;
453 	scn.ch = ch;
454 	scn.listCompletionMethod = completionMethod;
455 	scn.wParam = listType;
456 	scn.listType = listType;
457 	const Sci::Position firstPos = ac.posStart - ac.startLen;
458 	scn.position = firstPos;
459 	scn.lParam = firstPos;
460 	scn.text = selected.c_str();
461 	NotifyParent(scn);
462 
463 	if (!ac.Active())
464 		return;
465 	ac.Cancel();
466 
467 	if (listType > 0)
468 		return;
469 
470 	Sci::Position endPos = sel.MainCaret();
471 	if (ac.dropRestOfWord)
472 		endPos = pdoc->ExtendWordSelect(endPos, 1, true);
473 	if (endPos < firstPos)
474 		return;
475 	AutoCompleteInsert(firstPos, endPos - firstPos, selected.c_str(), selected.length());
476 	SetLastXChosen();
477 
478 	scn.nmhdr.code = SCN_AUTOCCOMPLETED;
479 	NotifyParent(scn);
480 
481 }
482 
483 int ScintillaBase::AutoCompleteGetCurrent() const {
484 	if (!ac.Active())
485 		return -1;
486 	return ac.GetSelection();
487 }
488 
489 int ScintillaBase::AutoCompleteGetCurrentText(char *buffer) const {
490 	if (ac.Active()) {
491 		const int item = ac.GetSelection();
492 		if (item != -1) {
493 			const std::string selected = ac.GetValue(item);
494 			if (buffer)
495 				memcpy(buffer, selected.c_str(), selected.length()+1);
496 			return static_cast<int>(selected.length());
497 		}
498 	}
499 	if (buffer)
500 		*buffer = '\0';
501 	return 0;
502 }
503 
504 void ScintillaBase::CallTipShow(Point pt, const char *defn) {
505 	ac.Cancel();
506 	// If container knows about STYLE_CALLTIP then use it in place of the
507 	// STYLE_DEFAULT for the face name, size and character set. Also use it
508 	// for the foreground and background colour.
509 	const int ctStyle = ct.UseStyleCallTip() ? STYLE_CALLTIP : STYLE_DEFAULT;
510 	if (ct.UseStyleCallTip()) {
511 		ct.SetForeBack(vs.styles[STYLE_CALLTIP].fore, vs.styles[STYLE_CALLTIP].back);
512 	}
513 	if (wMargin.Created()) {
514 		pt = pt + GetVisibleOriginInMain();
515 	}
516 	PRectangle rc = ct.CallTipStart(sel.MainCaret(), pt,
517 		vs.lineHeight,
518 		defn,
519 		vs.styles[ctStyle].fontName,
520 		vs.styles[ctStyle].sizeZoomed,
521 		CodePage(),
522 		vs.styles[ctStyle].characterSet,
523 		vs.technology,
524 		wMain);
525 	// If the call-tip window would be out of the client
526 	// space
527 	const PRectangle rcClient = GetClientRectangle();
528 	const int offset = vs.lineHeight + static_cast<int>(rc.Height());
529 	// adjust so it displays above the text.
530 	if (rc.bottom > rcClient.bottom && rc.Height() < rcClient.Height()) {
531 		rc.top -= offset;
532 		rc.bottom -= offset;
533 	}
534 	// adjust so it displays below the text.
535 	if (rc.top < rcClient.top && rc.Height() < rcClient.Height()) {
536 		rc.top += offset;
537 		rc.bottom += offset;
538 	}
539 	// Now display the window.
540 	CreateCallTipWindow(rc);
541 	ct.wCallTip.SetPositionRelative(rc, &wMain);
542 	ct.wCallTip.Show();
543 }
544 
545 void ScintillaBase::CallTipClick() {
546 	SCNotification scn = {};
547 	scn.nmhdr.code = SCN_CALLTIPCLICK;
548 	scn.position = ct.clickPlace;
549 	NotifyParent(scn);
550 }
551 
552 bool ScintillaBase::ShouldDisplayPopup(Point ptInWindowCoordinates) const {
553 	return (displayPopupMenu == SC_POPUP_ALL ||
554 		(displayPopupMenu == SC_POPUP_TEXT && !PointInSelMargin(ptInWindowCoordinates)));
555 }
556 
557 void ScintillaBase::ContextMenu(Point pt) {
558 	if (displayPopupMenu) {
559 		const bool writable = !WndProc(SCI_GETREADONLY, 0, 0);
560 		popup.CreatePopUp();
561 		AddToPopUp(LoadStringRes(IDS_UNDO, _T("\tCtrl+Z")).c_str(), idcmdUndo, writable && pdoc->CanUndo());
562 		AddToPopUp(LoadStringRes(IDS_REDO, _T("\tCtrl+Y")).c_str(), idcmdRedo, writable && pdoc->CanRedo());
563 		AddToPopUp(_T(""));
564 		AddToPopUp(LoadStringRes(IDS_CUT, _T("\tCtrl+X")).c_str(), idcmdCut, writable && !sel.Empty());
565         AddToPopUp(LoadStringRes(IDS_COPY, _T("\tCtrl+C")).c_str(), idcmdCopy, !sel.Empty());
566 		AddToPopUp(LoadStringRes(IDS_PASTE, _T("\tCtrl+V")).c_str(), idcmdPaste, writable && WndProc(SCI_CANPASTE, 0, 0));
567 		AddToPopUp(LoadStringRes(IDS_DELETE).c_str(), idcmdDelete, writable && !sel.Empty());
568 		AddToPopUp(_T(""));
569 		AddToPopUp(LoadStringRes(IDS_SELECT_ALL, _T("\tCtrl+A")).c_str(), idcmdSelectAll);
570 		popup.Show(pt, wMain);
571 	}
572 }
573 
574 void ScintillaBase::CancelModes() {
575 	AutoCompleteCancel();
576 	ct.CallTipCancel();
577 	Editor::CancelModes();
578 }
579 
580 void ScintillaBase::ButtonDownWithModifiers(Point pt, unsigned int curTime, int modifiers) {
581 	CancelModes();
582 	Editor::ButtonDownWithModifiers(pt, curTime, modifiers);
583 }
584 
585 void ScintillaBase::RightButtonDownWithModifiers(Point pt, unsigned int curTime, int modifiers) {
586 	CancelModes();
587 	Editor::RightButtonDownWithModifiers(pt, curTime, modifiers);
588 }
589 
590 namespace Scintilla {
591 
592 class LexState : public LexInterface {
593 	const LexerModule *lexCurrent;
594 	void SetLexerModule(const LexerModule *lex);
595 	PropSetSimple props;
596 	int interfaceVersion;
597 public:
598 	int lexLanguage;
599 
600 	explicit LexState(Document *pdoc_);
601 	void SetInstance(ILexer5 *instance_);
602 	// Deleted so LexState objects can not be copied.
603 	LexState(const LexState &) = delete;
604 	LexState(LexState &&) = delete;
605 	LexState &operator=(const LexState &) = delete;
606 	LexState &operator=(LexState &&) = delete;
607 	~LexState() override;
608 	void SetLexer(uptr_t wParam);
609 	void SetLexerLanguage(const char *languageName);
610 
611 	const char *DescribeWordListSets();
612 	void SetWordList(int n, const char *wl);
613 	int GetIdentifier() const;
614 	const char *GetName() const;
615 	void *PrivateCall(int operation, void *pointer);
616 	const char *PropertyNames();
617 	int PropertyType(const char *name);
618 	const char *DescribeProperty(const char *name);
619 	void PropSet(const char *key, const char *val);
620 	const char *PropGet(const char *key) const;
621 	int PropGetInt(const char *key, int defaultValue=0) const;
622 	size_t PropGetExpanded(const char *key, char *result) const;
623 
624 	int LineEndTypesSupported() override;
625 	int AllocateSubStyles(int styleBase, int numberStyles);
626 	int SubStylesStart(int styleBase);
627 	int SubStylesLength(int styleBase);
628 	int StyleFromSubStyle(int subStyle);
629 	int PrimaryStyleFromStyle(int style);
630 	void FreeSubStyles();
631 	void SetIdentifiers(int style, const char *identifiers);
632 	int DistanceToSecondaryStyles();
633 	const char *GetSubStyleBases();
634 	int NamedStyles();
635 	const char *NameOfStyle(int style);
636 	const char *TagsOfStyle(int style);
637 	const char *DescriptionOfStyle(int style);
638 };
639 
640 }
641 
642 LexState::LexState(Document *pdoc_) : LexInterface(pdoc_) {
643 	lexCurrent = nullptr;
644 	performingStyle = false;
645 	interfaceVersion = lvRelease4;
646 	lexLanguage = SCLEX_CONTAINER;
647 }
648 
649 LexState::~LexState() {
650 	if (instance) {
651 		instance->Release();
652 		instance = nullptr;
653 	}
654 }
655 
656 void LexState::SetInstance(ILexer5 *instance_) {
657 	if (instance) {
658 		instance->Release();
659 		instance = nullptr;
660 	}
661 	instance = instance_;
662 	pdoc->LexerChanged();
663 }
664 
665 LexState *ScintillaBase::DocumentLexState() {
666 	if (!pdoc->GetLexInterface()) {
667 		pdoc->SetLexInterface(std::make_unique<LexState>(pdoc));
668 	}
669 	return dynamic_cast<LexState *>(pdoc->GetLexInterface());
670 }
671 
672 void LexState::SetLexerModule(const LexerModule *lex) {
673 	if (lex != lexCurrent) {
674 		if (instance) {
675 			instance->Release();
676 			instance = nullptr;
677 		}
678 		interfaceVersion = lvRelease4;
679 		lexCurrent = lex;
680 		if (lexCurrent) {
681 			instance = lexCurrent->Create();
682 			interfaceVersion = instance->Version();
683 		}
684 		pdoc->LexerChanged();
685 	}
686 }
687 
688 void LexState::SetLexer(uptr_t wParam) {
689 	lexLanguage = static_cast<int>(wParam);
690 	if (lexLanguage == SCLEX_CONTAINER) {
691 		SetLexerModule(nullptr);
692 	} else {
693 		const LexerModule *lex = Catalogue::Find(lexLanguage);
694 		if (!lex)
695 			lex = Catalogue::Find(SCLEX_NULL);
696 		SetLexerModule(lex);
697 	}
698 }
699 
700 void LexState::SetLexerLanguage(const char *languageName) {
701 	const LexerModule *lex = Catalogue::Find(languageName);
702 	if (!lex)
703 		lex = Catalogue::Find(SCLEX_NULL);
704 	if (lex)
705 		lexLanguage = lex->GetLanguage();
706 	SetLexerModule(lex);
707 }
708 
709 const char *LexState::DescribeWordListSets() {
710 	if (instance) {
711 		return instance->DescribeWordListSets();
712 	} else {
713 		return nullptr;
714 	}
715 }
716 
717 void LexState::SetWordList(int n, const char *wl) {
718 	if (instance) {
719 		const Sci_Position firstModification = instance->WordListSet(n, wl);
720 		if (firstModification >= 0) {
721 			pdoc->ModifiedAt(firstModification);
722 		}
723 	}
724 }
725 
726 int LexState::GetIdentifier() const {
727 	if (lexCurrent) {
728 		return lexCurrent->GetLanguage();
729 	}
730 	if (instance) {
731 		if (instance->Version() >= lvRelease5) {
732 			return instance->GetIdentifier();
733 		}
734 	}
735 	return SCLEX_CONTAINER;
736 }
737 
738 const char *LexState::GetName() const {
739 	if (lexCurrent) {
740 		return lexCurrent->languageName;
741 	}
742 	if (instance) {
743 		if (instance->Version() >= lvRelease5) {
744 			return instance->GetName();
745 		}
746 	}
747 	return "";
748 }
749 
750 void *LexState::PrivateCall(int operation, void *pointer) {
751 	if (pdoc && instance) {
752 		return instance->PrivateCall(operation, pointer);
753 	} else {
754 		return nullptr;
755 	}
756 }
757 
758 const char *LexState::PropertyNames() {
759 	if (instance) {
760 		return instance->PropertyNames();
761 	} else {
762 		return nullptr;
763 	}
764 }
765 
766 int LexState::PropertyType(const char *name) {
767 	if (instance) {
768 		return instance->PropertyType(name);
769 	} else {
770 		return SC_TYPE_BOOLEAN;
771 	}
772 }
773 
774 const char *LexState::DescribeProperty(const char *name) {
775 	if (instance) {
776 		return instance->DescribeProperty(name);
777 	} else {
778 		return nullptr;
779 	}
780 }
781 
782 void LexState::PropSet(const char *key, const char *val) {
783 	props.Set(key, val, strlen(key), strlen(val));
784 	if (instance) {
785 		const Sci_Position firstModification = instance->PropertySet(key, val);
786 		if (firstModification >= 0) {
787 			pdoc->ModifiedAt(firstModification);
788 		}
789 	}
790 }
791 
792 const char *LexState::PropGet(const char *key) const {
793 	return props.Get(key);
794 }
795 
796 int LexState::PropGetInt(const char *key, int defaultValue) const {
797 	return props.GetInt(key, defaultValue);
798 }
799 
800 size_t LexState::PropGetExpanded(const char *key, char *result) const {
801 	return props.GetExpanded(key, result);
802 }
803 
804 int LexState::LineEndTypesSupported() {
805 	if (instance) {
806 		return instance->LineEndTypesSupported();
807 	}
808 	return 0;
809 }
810 
811 int LexState::AllocateSubStyles(int styleBase, int numberStyles) {
812 	if (instance) {
813 		return instance->AllocateSubStyles(styleBase, numberStyles);
814 	}
815 	return -1;
816 }
817 
818 int LexState::SubStylesStart(int styleBase) {
819 	if (instance) {
820 		return instance->SubStylesStart(styleBase);
821 	}
822 	return -1;
823 }
824 
825 int LexState::SubStylesLength(int styleBase) {
826 	if (instance) {
827 		return instance->SubStylesLength(styleBase);
828 	}
829 	return 0;
830 }
831 
832 int LexState::StyleFromSubStyle(int subStyle) {
833 	if (instance) {
834 		return instance->StyleFromSubStyle(subStyle);
835 	}
836 	return 0;
837 }
838 
839 int LexState::PrimaryStyleFromStyle(int style) {
840 	if (instance) {
841 		return instance->PrimaryStyleFromStyle(style);
842 	}
843 	return 0;
844 }
845 
846 void LexState::FreeSubStyles() {
847 	if (instance) {
848 		instance->FreeSubStyles();
849 	}
850 }
851 
852 void LexState::SetIdentifiers(int style, const char *identifiers) {
853 	if (instance) {
854 		instance->SetIdentifiers(style, identifiers);
855 		pdoc->ModifiedAt(0);
856 	}
857 }
858 
859 int LexState::DistanceToSecondaryStyles() {
860 	if (instance) {
861 		return instance->DistanceToSecondaryStyles();
862 	}
863 	return 0;
864 }
865 
866 const char *LexState::GetSubStyleBases() {
867 	if (instance) {
868 		return instance->GetSubStyleBases();
869 	}
870 	return "";
871 }
872 
873 int LexState::NamedStyles() {
874 	if (instance) {
875 		return instance->NamedStyles();
876 	} else {
877 		return -1;
878 	}
879 }
880 
881 const char *LexState::NameOfStyle(int style) {
882 	if (instance) {
883 		return instance->NameOfStyle(style);
884 	} else {
885 		return nullptr;
886 	}
887 }
888 
889 const char *LexState::TagsOfStyle(int style) {
890 	if (instance) {
891 		return instance->TagsOfStyle(style);
892 	} else {
893 		return nullptr;
894 	}
895 }
896 
897 const char *LexState::DescriptionOfStyle(int style) {
898 	if (instance) {
899 		return instance->DescriptionOfStyle(style);
900 	} else {
901 		return nullptr;
902 	}
903 }
904 
905 void ScintillaBase::NotifyStyleToNeeded(Sci::Position endStyleNeeded) {
906 	if (DocumentLexState()->GetIdentifier() != SCLEX_CONTAINER) {
907 		const Sci::Line lineEndStyled =
908 			pdoc->SciLineFromPosition(pdoc->GetEndStyled());
909 		const Sci::Position endStyled =
910 			pdoc->LineStart(lineEndStyled);
911 		DocumentLexState()->Colourise(endStyled, endStyleNeeded);
912 		return;
913 	}
914 	Editor::NotifyStyleToNeeded(endStyleNeeded);
915 }
916 
917 void ScintillaBase::NotifyLexerChanged(Document *, void *) {
918 	vs.EnsureStyle(0xff);
919 }
920 
921 sptr_t ScintillaBase::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
922 	switch (iMessage) {
923 	case SCI_AUTOCSHOW:
924 		listType = 0;
925 		AutoCompleteStart(static_cast<Sci::Position>(wParam), ConstCharPtrFromSPtr(lParam));
926 		break;
927 
928 	case SCI_AUTOCCANCEL:
929 		ac.Cancel();
930 		break;
931 
932 	case SCI_AUTOCACTIVE:
933 		return ac.Active();
934 
935 	case SCI_AUTOCPOSSTART:
936 		return ac.posStart;
937 
938 	case SCI_AUTOCCOMPLETE:
939 		AutoCompleteCompleted(0, SC_AC_COMMAND);
940 		break;
941 
942 	case SCI_AUTOCSETSEPARATOR:
943 		ac.SetSeparator(static_cast<char>(wParam));
944 		break;
945 
946 	case SCI_AUTOCGETSEPARATOR:
947 		return ac.GetSeparator();
948 
949 	case SCI_AUTOCSTOPS:
950 		ac.SetStopChars(ConstCharPtrFromSPtr(lParam));
951 		break;
952 
953 	case SCI_AUTOCSELECT:
954 		ac.Select(ConstCharPtrFromSPtr(lParam));
955 		break;
956 
957 	case SCI_AUTOCGETCURRENT:
958 		return AutoCompleteGetCurrent();
959 
960 	case SCI_AUTOCGETCURRENTTEXT:
961 		return AutoCompleteGetCurrentText(CharPtrFromSPtr(lParam));
962 
963 	case SCI_AUTOCSETCANCELATSTART:
964 		ac.cancelAtStartPos = wParam != 0;
965 		break;
966 
967 	case SCI_AUTOCGETCANCELATSTART:
968 		return ac.cancelAtStartPos;
969 
970 	case SCI_AUTOCSETFILLUPS:
971 		ac.SetFillUpChars(ConstCharPtrFromSPtr(lParam));
972 		break;
973 
974 	case SCI_AUTOCSETCHOOSESINGLE:
975 		ac.chooseSingle = wParam != 0;
976 		break;
977 
978 	case SCI_AUTOCGETCHOOSESINGLE:
979 		return ac.chooseSingle;
980 
981 	case SCI_AUTOCSETIGNORECASE:
982 		ac.ignoreCase = wParam != 0;
983 		break;
984 
985 	case SCI_AUTOCGETIGNORECASE:
986 		return ac.ignoreCase;
987 
988 	case SCI_AUTOCSETCASEINSENSITIVEBEHAVIOUR:
989 		ac.ignoreCaseBehaviour = static_cast<unsigned int>(wParam);
990 		break;
991 
992 	case SCI_AUTOCGETCASEINSENSITIVEBEHAVIOUR:
993 		return ac.ignoreCaseBehaviour;
994 
995 	case SCI_AUTOCSETMULTI:
996 		multiAutoCMode = static_cast<int>(wParam);
997 		break;
998 
999 	case SCI_AUTOCGETMULTI:
1000 		return multiAutoCMode;
1001 
1002 	case SCI_AUTOCSETORDER:
1003 		ac.autoSort = static_cast<int>(wParam);
1004 		break;
1005 
1006 	case SCI_AUTOCGETORDER:
1007 		return ac.autoSort;
1008 
1009 	case SCI_USERLISTSHOW:
1010 		listType = static_cast<int>(wParam);
1011 		AutoCompleteStart(0, ConstCharPtrFromSPtr(lParam));
1012 		break;
1013 
1014 	case SCI_AUTOCSETAUTOHIDE:
1015 		ac.autoHide = wParam != 0;
1016 		break;
1017 
1018 	case SCI_AUTOCGETAUTOHIDE:
1019 		return ac.autoHide;
1020 
1021 	case SCI_AUTOCSETDROPRESTOFWORD:
1022 		ac.dropRestOfWord = wParam != 0;
1023 		break;
1024 
1025 	case SCI_AUTOCGETDROPRESTOFWORD:
1026 		return ac.dropRestOfWord;
1027 
1028 	case SCI_AUTOCSETMAXHEIGHT:
1029 		ac.lb->SetVisibleRows(static_cast<int>(wParam));
1030 		break;
1031 
1032 	case SCI_AUTOCGETMAXHEIGHT:
1033 		return ac.lb->GetVisibleRows();
1034 
1035 	case SCI_AUTOCSETMAXWIDTH:
1036 		maxListWidth = static_cast<int>(wParam);
1037 		break;
1038 
1039 	case SCI_AUTOCGETMAXWIDTH:
1040 		return maxListWidth;
1041 
1042 	case SCI_REGISTERIMAGE:
1043 		ac.lb->RegisterImage(static_cast<int>(wParam), ConstCharPtrFromSPtr(lParam));
1044 		break;
1045 
1046 	case SCI_REGISTERRGBAIMAGE:
1047 		ac.lb->RegisterRGBAImage(static_cast<int>(wParam), static_cast<int>(sizeRGBAImage.x), static_cast<int>(sizeRGBAImage.y),
1048 			ConstUCharPtrFromSPtr(lParam));
1049 		break;
1050 
1051 	case SCI_CLEARREGISTEREDIMAGES:
1052 		ac.lb->ClearRegisteredImages();
1053 		break;
1054 
1055 	case SCI_AUTOCSETTYPESEPARATOR:
1056 		ac.SetTypesep(static_cast<char>(wParam));
1057 		break;
1058 
1059 	case SCI_AUTOCGETTYPESEPARATOR:
1060 		return ac.GetTypesep();
1061 
1062 	case SCI_CALLTIPSHOW:
1063 		CallTipShow(LocationFromPosition(wParam),
1064 			ConstCharPtrFromSPtr(lParam));
1065 		break;
1066 
1067 	case SCI_CALLTIPCANCEL:
1068 		ct.CallTipCancel();
1069 		break;
1070 
1071 	case SCI_CALLTIPACTIVE:
1072 		return ct.inCallTipMode;
1073 
1074 	case SCI_CALLTIPPOSSTART:
1075 		return ct.posStartCallTip;
1076 
1077 	case SCI_CALLTIPSETPOSSTART:
1078 		ct.posStartCallTip = wParam;
1079 		break;
1080 
1081 	case SCI_CALLTIPSETHLT:
1082 		ct.SetHighlight(wParam, lParam);
1083 		break;
1084 
1085 	case SCI_CALLTIPSETBACK:
1086 		ct.colourBG = ColourDesired(static_cast<int>(wParam));
1087 		vs.styles[STYLE_CALLTIP].back = ct.colourBG;
1088 		InvalidateStyleRedraw();
1089 		break;
1090 
1091 	case SCI_CALLTIPSETFORE:
1092 		ct.colourUnSel = ColourDesired(static_cast<int>(wParam));
1093 		vs.styles[STYLE_CALLTIP].fore = ct.colourUnSel;
1094 		InvalidateStyleRedraw();
1095 		break;
1096 
1097 	case SCI_CALLTIPSETFOREHLT:
1098 		ct.colourSel = ColourDesired(static_cast<int>(wParam));
1099 		InvalidateStyleRedraw();
1100 		break;
1101 
1102 	case SCI_CALLTIPUSESTYLE:
1103 		ct.SetTabSize(static_cast<int>(wParam));
1104 		InvalidateStyleRedraw();
1105 		break;
1106 
1107 	case SCI_CALLTIPSETPOSITION:
1108 		ct.SetPosition(wParam != 0);
1109 		InvalidateStyleRedraw();
1110 		break;
1111 
1112 	case SCI_USEPOPUP:
1113 		displayPopupMenu = static_cast<int>(wParam);
1114 		break;
1115 
1116 	case SCI_SETLEXER:
1117 		DocumentLexState()->SetLexer(static_cast<int>(wParam));
1118 		break;
1119 
1120 	case SCI_GETLEXER:
1121 		return DocumentLexState()->GetIdentifier();
1122 
1123 	case SCI_SETILEXER:
1124 		DocumentLexState()->SetInstance(reinterpret_cast<ILexer5 *>(lParam));
1125 		return 0;
1126 
1127 	case SCI_COLOURISE:
1128 		if (DocumentLexState()->lexLanguage == SCLEX_CONTAINER) {
1129 			pdoc->ModifiedAt(static_cast<Sci::Position>(wParam));
1130 			NotifyStyleToNeeded((lParam == -1) ? pdoc->Length() : lParam);
1131 		} else {
1132 			DocumentLexState()->Colourise(static_cast<Sci::Position>(wParam), lParam);
1133 		}
1134 		Redraw();
1135 		break;
1136 
1137 	case SCI_SETPROPERTY:
1138 		DocumentLexState()->PropSet(ConstCharPtrFromUPtr(wParam),
1139 		          ConstCharPtrFromSPtr(lParam));
1140 		break;
1141 
1142 	case SCI_GETPROPERTY:
1143 		return StringResult(lParam, DocumentLexState()->PropGet(ConstCharPtrFromUPtr(wParam)));
1144 
1145 	case SCI_GETPROPERTYEXPANDED:
1146 		return DocumentLexState()->PropGetExpanded(ConstCharPtrFromUPtr(wParam),
1147 			CharPtrFromSPtr(lParam));
1148 
1149 	case SCI_GETPROPERTYINT:
1150 		return DocumentLexState()->PropGetInt(ConstCharPtrFromUPtr(wParam), static_cast<int>(lParam));
1151 
1152 	case SCI_SETKEYWORDS:
1153 		DocumentLexState()->SetWordList(static_cast<int>(wParam), ConstCharPtrFromSPtr(lParam));
1154 		break;
1155 
1156 	case SCI_SETLEXERLANGUAGE:
1157 		DocumentLexState()->SetLexerLanguage(ConstCharPtrFromSPtr(lParam));
1158 		break;
1159 
1160 	case SCI_GETLEXERLANGUAGE:
1161 		return StringResult(lParam, DocumentLexState()->GetName());
1162 
1163 #ifdef SCI_LEXER
1164 	case SCI_LOADLEXERLIBRARY:
1165 		ExternalLexerLoad(ConstCharPtrFromSPtr(lParam));
1166 		break;
1167 #endif
1168 
1169 	case SCI_PRIVATELEXERCALL:
1170 		return reinterpret_cast<sptr_t>(
1171 			DocumentLexState()->PrivateCall(static_cast<int>(wParam), reinterpret_cast<void *>(lParam)));
1172 
1173 #ifdef INCLUDE_DEPRECATED_FEATURES
1174 	case SCI_GETSTYLEBITSNEEDED:
1175 		return 8;
1176 #endif
1177 
1178 	case SCI_PROPERTYNAMES:
1179 		return StringResult(lParam, DocumentLexState()->PropertyNames());
1180 
1181 	case SCI_PROPERTYTYPE:
1182 		return DocumentLexState()->PropertyType(ConstCharPtrFromUPtr(wParam));
1183 
1184 	case SCI_DESCRIBEPROPERTY:
1185 		return StringResult(lParam,
1186 				    DocumentLexState()->DescribeProperty(ConstCharPtrFromUPtr(wParam)));
1187 
1188 	case SCI_DESCRIBEKEYWORDSETS:
1189 		return StringResult(lParam, DocumentLexState()->DescribeWordListSets());
1190 
1191 	case SCI_GETLINEENDTYPESSUPPORTED:
1192 		return DocumentLexState()->LineEndTypesSupported();
1193 
1194 	case SCI_ALLOCATESUBSTYLES:
1195 		return DocumentLexState()->AllocateSubStyles(static_cast<int>(wParam), static_cast<int>(lParam));
1196 
1197 	case SCI_GETSUBSTYLESSTART:
1198 		return DocumentLexState()->SubStylesStart(static_cast<int>(wParam));
1199 
1200 	case SCI_GETSUBSTYLESLENGTH:
1201 		return DocumentLexState()->SubStylesLength(static_cast<int>(wParam));
1202 
1203 	case SCI_GETSTYLEFROMSUBSTYLE:
1204 		return DocumentLexState()->StyleFromSubStyle(static_cast<int>(wParam));
1205 
1206 	case SCI_GETPRIMARYSTYLEFROMSTYLE:
1207 		return DocumentLexState()->PrimaryStyleFromStyle(static_cast<int>(wParam));
1208 
1209 	case SCI_FREESUBSTYLES:
1210 		DocumentLexState()->FreeSubStyles();
1211 		break;
1212 
1213 	case SCI_SETIDENTIFIERS:
1214 		DocumentLexState()->SetIdentifiers(static_cast<int>(wParam),
1215 						   ConstCharPtrFromSPtr(lParam));
1216 		break;
1217 
1218 	case SCI_DISTANCETOSECONDARYSTYLES:
1219 		return DocumentLexState()->DistanceToSecondaryStyles();
1220 
1221 	case SCI_GETSUBSTYLEBASES:
1222 		return StringResult(lParam, DocumentLexState()->GetSubStyleBases());
1223 
1224 	case SCI_GETNAMEDSTYLES:
1225 		return DocumentLexState()->NamedStyles();
1226 
1227 	case SCI_NAMEOFSTYLE:
1228 		return StringResult(lParam, DocumentLexState()->
1229 				    NameOfStyle(static_cast<int>(wParam)));
1230 
1231 	case SCI_TAGSOFSTYLE:
1232 		return StringResult(lParam, DocumentLexState()->
1233 				    TagsOfStyle(static_cast<int>(wParam)));
1234 
1235 	case SCI_DESCRIPTIONOFSTYLE:
1236 		return StringResult(lParam, DocumentLexState()->
1237 				    DescriptionOfStyle(static_cast<int>(wParam)));
1238 
1239 	default:
1240 		return Editor::WndProc(iMessage, wParam, lParam);
1241 	}
1242 	return 0;
1243 }
1244