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