1 // Scintilla source code edit control
2 /** @file Editor.cxx
3  ** Main code for the edit control.
4  **/
5 // Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>
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 #include <cstdio>
13 #include <cmath>
14 
15 #include <stdexcept>
16 #include <string>
17 #include <string_view>
18 #include <vector>
19 #include <map>
20 #include <forward_list>
21 #include <algorithm>
22 #include <iterator>
23 #include <memory>
24 #include <chrono>
25 
26 #include "Platform.h"
27 
28 #include "ILoader.h"
29 #include "ILexer.h"
30 #include "Scintilla.h"
31 
32 #include "CharacterSet.h"
33 #include "CharacterCategory.h"
34 #include "Position.h"
35 #include "UniqueString.h"
36 #include "SplitVector.h"
37 #include "Partitioning.h"
38 #include "RunStyles.h"
39 #include "ContractionState.h"
40 #include "CellBuffer.h"
41 #include "PerLine.h"
42 #include "KeyMap.h"
43 #include "Indicator.h"
44 #include "LineMarker.h"
45 #include "Style.h"
46 #include "ViewStyle.h"
47 #include "CharClassify.h"
48 #include "Decoration.h"
49 #include "CaseFolder.h"
50 #include "Document.h"
51 #include "UniConversion.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 "ElapsedPeriod.h"
59 
60 using namespace Scintilla;
61 
62 namespace {
63 
64 /*
65 	return whether this modification represents an operation that
66 	may reasonably be deferred (not done now OR [possibly] at all)
67 */
CanDeferToLastStep(const DocModification & mh)68 constexpr bool CanDeferToLastStep(const DocModification &mh) noexcept {
69 	if (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE))
70 		return true;	// CAN skip
71 	if (!(mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)))
72 		return false;	// MUST do
73 	if (mh.modificationType & SC_MULTISTEPUNDOREDO)
74 		return true;	// CAN skip
75 	return false;		// PRESUMABLY must do
76 }
77 
CanEliminate(const DocModification & mh)78 constexpr bool CanEliminate(const DocModification &mh) noexcept {
79 	return
80 	    (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) != 0;
81 }
82 
83 /*
84 	return whether this modification represents the FINAL step
85 	in a [possibly lengthy] multi-step Undo/Redo sequence
86 */
IsLastStep(const DocModification & mh)87 constexpr bool IsLastStep(const DocModification &mh) noexcept {
88 	return
89 	    (mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)) != 0
90 	    && (mh.modificationType & SC_MULTISTEPUNDOREDO) != 0
91 	    && (mh.modificationType & SC_LASTSTEPINUNDOREDO) != 0
92 	    && (mh.modificationType & SC_MULTILINEUNDOREDO) != 0;
93 }
94 
95 }
96 
Timer()97 Timer::Timer() noexcept :
98 		ticking(false), ticksToWait(0), tickerID{} {}
99 
Idler()100 Idler::Idler() noexcept :
101 		state(false), idlerID(0) {}
102 
IsAllSpacesOrTabs(std::string_view sv)103 static constexpr bool IsAllSpacesOrTabs(std::string_view sv) noexcept {
104 	for (const char ch : sv) {
105 		// This is safe because IsSpaceOrTab() will return false for null terminators
106 		if (!IsSpaceOrTab(ch))
107 			return false;
108 	}
109 	return true;
110 }
111 
Editor()112 Editor::Editor() : durationWrapOneLine(0.00001, 0.000001, 0.0001) {
113 	ctrlID = 0;
114 
115 	stylesValid = false;
116 	technology = SC_TECHNOLOGY_DEFAULT;
117 	scaleRGBAImage = 100.0f;
118 
119 	cursorMode = SC_CURSORNORMAL;
120 
121 	hasFocus = false;
122 	errorStatus = 0;
123 	mouseDownCaptures = true;
124 	mouseWheelCaptures = true;
125 
126 	lastClickTime = 0;
127 	doubleClickCloseThreshold = Point(3, 3);
128 	dwellDelay = SC_TIME_FOREVER;
129 	ticksToDwell = SC_TIME_FOREVER;
130 	dwelling = false;
131 	ptMouseLast.x = 0;
132 	ptMouseLast.y = 0;
133 	inDragDrop = ddNone;
134 	dropWentOutside = false;
135 	posDrop = SelectionPosition(Sci::invalidPosition);
136 	hotSpotClickPos = INVALID_POSITION;
137 	selectionType = selChar;
138 
139 	lastXChosen = 0;
140 	lineAnchorPos = 0;
141 	originalAnchorPos = 0;
142 	wordSelectAnchorStartPos = 0;
143 	wordSelectAnchorEndPos = 0;
144 	wordSelectInitialCaretPos = -1;
145 
146 	caretPolicies.x = { CARET_SLOP | CARET_EVEN, 50 };
147 	caretPolicies.y = { CARET_EVEN, 0 };
148 
149 	visiblePolicy = { 0, 0 };
150 
151 	searchAnchor = 0;
152 
153 	xCaretMargin = 50;
154 	horizontalScrollBarVisible = true;
155 	scrollWidth = 2000;
156 	verticalScrollBarVisible = true;
157 	endAtLastLine = true;
158 	caretSticky = SC_CARETSTICKY_OFF;
159 	marginOptions = SC_MARGINOPTION_NONE;
160 	mouseSelectionRectangularSwitch = false;
161 	multipleSelection = false;
162 	additionalSelectionTyping = false;
163 	multiPasteMode = SC_MULTIPASTE_ONCE;
164 	virtualSpaceOptions = SCVS_NONE;
165 
166 	targetRange = SelectionSegment();
167 	searchFlags = 0;
168 
169 	topLine = 0;
170 	posTopLine = 0;
171 
172 	lengthForEncode = -1;
173 
174 	needUpdateUI = 0;
175 	ContainerNeedsUpdate(SC_UPDATE_CONTENT);
176 
177 	paintState = notPainting;
178 	paintAbandonedByStyling = false;
179 	paintingAllText = false;
180 	willRedrawAll = false;
181 	idleStyling = SC_IDLESTYLING_NONE;
182 	needIdleStyling = false;
183 
184 	modEventMask = SC_MODEVENTMASKALL;
185 	commandEvents = true;
186 
187 	pdoc->AddWatcher(this, 0);
188 
189 	recordingMacro = false;
190 	foldAutomatic = 0;
191 
192 	convertPastes = true;
193 
194 	SetRepresentations();
195 }
196 
~Editor()197 Editor::~Editor() {
198 	pdoc->RemoveWatcher(this, 0);
199 	DropGraphics(true);
200 }
201 
Finalise()202 void Editor::Finalise() {
203 	SetIdle(false);
204 	CancelModes();
205 }
206 
SetRepresentations()207 void Editor::SetRepresentations() {
208 	reprs.Clear();
209 
210 	// C0 control set
211 	const char *const reps[] = {
212 		"NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
213 		"BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
214 		"DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
215 		"CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
216 	};
217 	for (size_t j=0; j < std::size(reps); j++) {
218 		const char c[2] = { static_cast<char>(j), 0 };
219 		reprs.SetRepresentation(c, reps[j]);
220 	}
221 
222 	// C1 control set
223 	// As well as Unicode mode, ISO-8859-1 should use these
224 	if (IsUnicodeMode()) {
225 		const char *const repsC1[] = {
226 			"PAD", "HOP", "BPH", "NBH", "IND", "NEL", "SSA", "ESA",
227 			"HTS", "HTJ", "VTS", "PLD", "PLU", "RI", "SS2", "SS3",
228 			"DCS", "PU1", "PU2", "STS", "CCH", "MW", "SPA", "EPA",
229 			"SOS", "SGCI", "SCI", "CSI", "ST", "OSC", "PM", "APC"
230 		};
231 		for (size_t j=0; j < std::size(repsC1); j++) {
232 			const char c1[3] = { '\xc2',  static_cast<char>(0x80+j), 0 };
233 			reprs.SetRepresentation(c1, repsC1[j]);
234 		}
235 		reprs.SetRepresentation("\xe2\x80\xa8", "LS");
236 		reprs.SetRepresentation("\xe2\x80\xa9", "PS");
237 	}
238 
239 	// UTF-8 invalid bytes
240 	if (IsUnicodeMode()) {
241 		for (int k=0x80; k < 0x100; k++) {
242 			const char hiByte[2] = {  static_cast<char>(k), 0 };
243 			char hexits[5];	// Really only needs 4 but that causes warning from gcc 7.1
244 			sprintf(hexits, "x%2X", k);
245 			reprs.SetRepresentation(hiByte, hexits);
246 		}
247 	} else if (pdoc->dbcsCodePage) {
248 		// DBCS invalid single lead bytes
249 		for (int k = 0x80; k < 0x100; k++) {
250 			const char ch = static_cast<char>(k);
251 			if (pdoc->IsDBCSLeadByteNoExcept(ch)  || pdoc->IsDBCSLeadByteInvalid(ch)) {
252 				const char hiByte[2] = { ch, 0 };
253 				char hexits[5];	// Really only needs 4 but that causes warning from gcc 7.1
254 				sprintf(hexits, "x%2X", k);
255 				reprs.SetRepresentation(hiByte, hexits);
256 			}
257 		}
258 	}
259 }
260 
DropGraphics(bool freeObjects)261 void Editor::DropGraphics(bool freeObjects) {
262 	marginView.DropGraphics(freeObjects);
263 	view.DropGraphics(freeObjects);
264 }
265 
AllocateGraphics()266 void Editor::AllocateGraphics() {
267 	marginView.AllocateGraphics(vs);
268 	view.AllocateGraphics(vs);
269 }
270 
InvalidateStyleData()271 void Editor::InvalidateStyleData() {
272 	stylesValid = false;
273 	vs.technology = technology;
274 	DropGraphics(false);
275 	AllocateGraphics();
276 	view.llc.Invalidate(LineLayout::llInvalid);
277 	view.posCache.Clear();
278 }
279 
InvalidateStyleRedraw()280 void Editor::InvalidateStyleRedraw() {
281 	NeedWrapping();
282 	InvalidateStyleData();
283 	Redraw();
284 }
285 
RefreshStyleData()286 void Editor::RefreshStyleData() {
287 	if (!stylesValid) {
288 		stylesValid = true;
289 		AutoSurface surface(this);
290 		if (surface) {
291 			vs.Refresh(*surface, pdoc->tabInChars);
292 		}
293 		SetScrollBars();
294 		SetRectangularRange();
295 	}
296 }
297 
GetVisibleOriginInMain() const298 Point Editor::GetVisibleOriginInMain() const {
299 	return Point(0, 0);
300 }
301 
DocumentPointFromView(Point ptView) const302 PointDocument Editor::DocumentPointFromView(Point ptView) const {
303 	PointDocument ptDocument(ptView);
304 	if (wMargin.GetID()) {
305 		const Point ptOrigin = GetVisibleOriginInMain();
306 		ptDocument.x += ptOrigin.x;
307 		ptDocument.y += ptOrigin.y;
308 	} else {
309 		ptDocument.x += xOffset;
310 		ptDocument.y += topLine * vs.lineHeight;
311 	}
312 	return ptDocument;
313 }
314 
TopLineOfMain() const315 Sci::Line Editor::TopLineOfMain() const {
316 	if (wMargin.GetID())
317 		return 0;
318 	else
319 		return topLine;
320 }
321 
GetClientRectangle() const322 PRectangle Editor::GetClientRectangle() const {
323 	return wMain.GetClientPosition();
324 }
325 
GetClientDrawingRectangle()326 PRectangle Editor::GetClientDrawingRectangle() {
327 	return GetClientRectangle();
328 }
329 
GetTextRectangle() const330 PRectangle Editor::GetTextRectangle() const {
331 	PRectangle rc = GetClientRectangle();
332 	rc.left += vs.textStart;
333 	rc.right -= vs.rightMarginWidth;
334 	return rc;
335 }
336 
LinesOnScreen() const337 Sci::Line Editor::LinesOnScreen() const {
338 	const PRectangle rcClient = GetClientRectangle();
339 	const int htClient = static_cast<int>(rcClient.bottom - rcClient.top);
340 	//Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
341 	return htClient / vs.lineHeight;
342 }
343 
LinesToScroll() const344 Sci::Line Editor::LinesToScroll() const {
345 	const Sci::Line retVal = LinesOnScreen() - 1;
346 	if (retVal < 1)
347 		return 1;
348 	else
349 		return retVal;
350 }
351 
MaxScrollPos() const352 Sci::Line Editor::MaxScrollPos() const {
353 	//Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n",
354 	//LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);
355 	Sci::Line retVal = pcs->LinesDisplayed();
356 	if (endAtLastLine) {
357 		retVal -= LinesOnScreen();
358 	} else {
359 		retVal--;
360 	}
361 	if (retVal < 0) {
362 		return 0;
363 	} else {
364 		return retVal;
365 	}
366 }
367 
ClampPositionIntoDocument(SelectionPosition sp) const368 SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const {
369 	if (sp.Position() < 0) {
370 		return SelectionPosition(0);
371 	} else if (sp.Position() > pdoc->Length()) {
372 		return SelectionPosition(pdoc->Length());
373 	} else {
374 		// If not at end of line then set offset to 0
375 		if (!pdoc->IsLineEndPosition(sp.Position()))
376 			sp.SetVirtualSpace(0);
377 		return sp;
378 	}
379 }
380 
LocationFromPosition(SelectionPosition pos,PointEnd pe)381 Point Editor::LocationFromPosition(SelectionPosition pos, PointEnd pe) {
382 	const PRectangle rcClient = GetTextRectangle();
383 	RefreshStyleData();
384 	AutoSurface surface(this);
385 	return view.LocationFromPosition(surface, *this, pos, topLine, vs, pe, rcClient);
386 }
387 
LocationFromPosition(Sci::Position pos,PointEnd pe)388 Point Editor::LocationFromPosition(Sci::Position pos, PointEnd pe) {
389 	return LocationFromPosition(SelectionPosition(pos), pe);
390 }
391 
XFromPosition(SelectionPosition sp)392 int Editor::XFromPosition(SelectionPosition sp) {
393 	const Point pt = LocationFromPosition(sp);
394 	return static_cast<int>(pt.x) - vs.textStart + xOffset;
395 }
396 
SPositionFromLocation(Point pt,bool canReturnInvalid,bool charPosition,bool virtualSpace)397 SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace) {
398 	RefreshStyleData();
399 	AutoSurface surface(this);
400 
401 	PRectangle rcClient = GetTextRectangle();
402 	// May be in scroll view coordinates so translate back to main view
403 	const Point ptOrigin = GetVisibleOriginInMain();
404 	rcClient.Move(-ptOrigin.x, -ptOrigin.y);
405 
406 	if (canReturnInvalid) {
407 		if (!rcClient.Contains(pt))
408 			return SelectionPosition(INVALID_POSITION);
409 		if (pt.x < vs.textStart)
410 			return SelectionPosition(INVALID_POSITION);
411 		if (pt.y < 0)
412 			return SelectionPosition(INVALID_POSITION);
413 	}
414 	const PointDocument ptdoc = DocumentPointFromView(pt);
415 	return view.SPositionFromLocation(surface, *this, ptdoc, canReturnInvalid,
416 		charPosition, virtualSpace, vs, rcClient);
417 }
418 
PositionFromLocation(Point pt,bool canReturnInvalid,bool charPosition)419 Sci::Position Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) {
420 	return SPositionFromLocation(pt, canReturnInvalid, charPosition, false).Position();
421 }
422 
423 /**
424 * Find the document position corresponding to an x coordinate on a particular document line.
425 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
426 * This method is used for rectangular selections and does not work on wrapped lines.
427 */
SPositionFromLineX(Sci::Line lineDoc,int x)428 SelectionPosition Editor::SPositionFromLineX(Sci::Line lineDoc, int x) {
429 	RefreshStyleData();
430 	if (lineDoc >= pdoc->LinesTotal())
431 		return SelectionPosition(pdoc->Length());
432 	//Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
433 	AutoSurface surface(this);
434 	return view.SPositionFromLineX(surface, *this, lineDoc, x, vs);
435 }
436 
PositionFromLineX(Sci::Line lineDoc,int x)437 Sci::Position Editor::PositionFromLineX(Sci::Line lineDoc, int x) {
438 	return SPositionFromLineX(lineDoc, x).Position();
439 }
440 
LineFromLocation(Point pt) const441 Sci::Line Editor::LineFromLocation(Point pt) const {
442 	return pcs->DocFromDisplay(static_cast<int>(pt.y) / vs.lineHeight + topLine);
443 }
444 
SetTopLine(Sci::Line topLineNew)445 void Editor::SetTopLine(Sci::Line topLineNew) {
446 	if ((topLine != topLineNew) && (topLineNew >= 0)) {
447 		topLine = topLineNew;
448 		ContainerNeedsUpdate(SC_UPDATE_V_SCROLL);
449 	}
450 	posTopLine = pdoc->LineStart(pcs->DocFromDisplay(topLine));
451 }
452 
453 /**
454  * If painting then abandon the painting because a wider redraw is needed.
455  * @return true if calling code should stop drawing.
456  */
AbandonPaint()457 bool Editor::AbandonPaint() {
458 	if ((paintState == painting) && !paintingAllText) {
459 		paintState = paintAbandoned;
460 	}
461 	return paintState == paintAbandoned;
462 }
463 
RedrawRect(PRectangle rc)464 void Editor::RedrawRect(PRectangle rc) {
465 	//Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
466 
467 	// Clip the redraw rectangle into the client area
468 	const PRectangle rcClient = GetClientRectangle();
469 	if (rc.top < rcClient.top)
470 		rc.top = rcClient.top;
471 	if (rc.bottom > rcClient.bottom)
472 		rc.bottom = rcClient.bottom;
473 	if (rc.left < rcClient.left)
474 		rc.left = rcClient.left;
475 	if (rc.right > rcClient.right)
476 		rc.right = rcClient.right;
477 
478 	if ((rc.bottom > rc.top) && (rc.right > rc.left)) {
479 		wMain.InvalidateRectangle(rc);
480 	}
481 }
482 
DiscardOverdraw()483 void Editor::DiscardOverdraw() {
484 	// Overridden on platforms that may draw outside visible area.
485 }
486 
Redraw()487 void Editor::Redraw() {
488 	//Platform::DebugPrintf("Redraw all\n");
489 	const PRectangle rcClient = GetClientRectangle();
490 	wMain.InvalidateRectangle(rcClient);
491 	if (wMargin.GetID())
492 		wMargin.InvalidateAll();
493 	//wMain.InvalidateAll();
494 }
495 
RedrawSelMargin(Sci::Line line,bool allAfter)496 void Editor::RedrawSelMargin(Sci::Line line, bool allAfter) {
497 	const bool markersInText = vs.maskInLine || vs.maskDrawInText;
498 	if (!wMargin.GetID() || markersInText) {	// May affect text area so may need to abandon and retry
499 		if (AbandonPaint()) {
500 			return;
501 		}
502 	}
503 	if (wMargin.GetID() && markersInText) {
504 		Redraw();
505 		return;
506 	}
507 	PRectangle rcMarkers = GetClientRectangle();
508 	if (!markersInText) {
509 		// Normal case: just draw the margin
510 		rcMarkers.right = rcMarkers.left + vs.fixedColumnWidth;
511 	}
512 	if (line != -1) {
513 		PRectangle rcLine = RectangleFromRange(Range(pdoc->LineStart(line)), 0);
514 
515 		// Inflate line rectangle if there are image markers with height larger than line height
516 		if (vs.largestMarkerHeight > vs.lineHeight) {
517 			const int delta = (vs.largestMarkerHeight - vs.lineHeight + 1) / 2;
518 			rcLine.top -= delta;
519 			rcLine.bottom += delta;
520 			if (rcLine.top < rcMarkers.top)
521 				rcLine.top = rcMarkers.top;
522 			if (rcLine.bottom > rcMarkers.bottom)
523 				rcLine.bottom = rcMarkers.bottom;
524 		}
525 
526 		rcMarkers.top = rcLine.top;
527 		if (!allAfter)
528 			rcMarkers.bottom = rcLine.bottom;
529 		if (rcMarkers.Empty())
530 			return;
531 	}
532 	if (wMargin.GetID()) {
533 		const Point ptOrigin = GetVisibleOriginInMain();
534 		rcMarkers.Move(-ptOrigin.x, -ptOrigin.y);
535 		wMargin.InvalidateRectangle(rcMarkers);
536 	} else {
537 		wMain.InvalidateRectangle(rcMarkers);
538 	}
539 }
540 
RectangleFromRange(Range r,int overlap)541 PRectangle Editor::RectangleFromRange(Range r, int overlap) {
542 	const Sci::Line minLine = pcs->DisplayFromDoc(
543 		pdoc->SciLineFromPosition(r.First()));
544 	const Sci::Line maxLine = pcs->DisplayLastFromDoc(
545 		pdoc->SciLineFromPosition(r.Last()));
546 	const PRectangle rcClientDrawing = GetClientDrawingRectangle();
547 	PRectangle rc;
548 	const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0;
549 	rc.left = static_cast<XYPOSITION>(vs.textStart - leftTextOverlap);
550 	rc.top = static_cast<XYPOSITION>((minLine - TopLineOfMain()) * vs.lineHeight - overlap);
551 	if (rc.top < rcClientDrawing.top)
552 		rc.top = rcClientDrawing.top;
553 	// Extend to right of prepared area if any to prevent artifacts from caret line highlight
554 	rc.right = rcClientDrawing.right;
555 	rc.bottom = static_cast<XYPOSITION>((maxLine - TopLineOfMain() + 1) * vs.lineHeight + overlap);
556 
557 	return rc;
558 }
559 
InvalidateRange(Sci::Position start,Sci::Position end)560 void Editor::InvalidateRange(Sci::Position start, Sci::Position end) {
561 	RedrawRect(RectangleFromRange(Range(start, end), view.LinesOverlap() ? vs.lineOverlap : 0));
562 }
563 
CurrentPosition() const564 Sci::Position Editor::CurrentPosition() const {
565 	return sel.MainCaret();
566 }
567 
SelectionEmpty() const568 bool Editor::SelectionEmpty() const noexcept {
569 	return sel.Empty();
570 }
571 
SelectionStart()572 SelectionPosition Editor::SelectionStart() {
573 	return sel.RangeMain().Start();
574 }
575 
SelectionEnd()576 SelectionPosition Editor::SelectionEnd() {
577 	return sel.RangeMain().End();
578 }
579 
SetRectangularRange()580 void Editor::SetRectangularRange() {
581 	if (sel.IsRectangular()) {
582 		const int xAnchor = XFromPosition(sel.Rectangular().anchor);
583 		int xCaret = XFromPosition(sel.Rectangular().caret);
584 		if (sel.selType == Selection::selThin) {
585 			xCaret = xAnchor;
586 		}
587 		const Sci::Line lineAnchorRect =
588 			pdoc->SciLineFromPosition(sel.Rectangular().anchor.Position());
589 		const Sci::Line lineCaret =
590 			pdoc->SciLineFromPosition(sel.Rectangular().caret.Position());
591 		const int increment = (lineCaret > lineAnchorRect) ? 1 : -1;
592 		AutoSurface surface(this);
593 		for (Sci::Line line=lineAnchorRect; line != lineCaret+increment; line += increment) {
594 			SelectionRange range(
595 				view.SPositionFromLineX(surface, *this, line, xCaret, vs),
596 				view.SPositionFromLineX(surface, *this, line, xAnchor, vs));
597 			if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) == 0)
598 				range.ClearVirtualSpace();
599 			if (line == lineAnchorRect)
600 				sel.SetSelection(range);
601 			else
602 				sel.AddSelectionWithoutTrim(range);
603 		}
604 	}
605 }
606 
ThinRectangularRange()607 void Editor::ThinRectangularRange() {
608 	if (sel.IsRectangular()) {
609 		sel.selType = Selection::selThin;
610 		if (sel.Rectangular().caret < sel.Rectangular().anchor) {
611 			sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).caret, sel.Range(0).anchor);
612 		} else {
613 			sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).anchor, sel.Range(0).caret);
614 		}
615 		SetRectangularRange();
616 	}
617 }
618 
InvalidateSelection(SelectionRange newMain,bool invalidateWholeSelection)619 void Editor::InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection) {
620 	if (sel.Count() > 1 || !(sel.RangeMain().anchor == newMain.anchor) || sel.IsRectangular()) {
621 		invalidateWholeSelection = true;
622 	}
623 	Sci::Position firstAffected = std::min(sel.RangeMain().Start().Position(), newMain.Start().Position());
624 	// +1 for lastAffected ensures caret repainted
625 	Sci::Position lastAffected = std::max(newMain.caret.Position()+1, newMain.anchor.Position());
626 	lastAffected = std::max(lastAffected, sel.RangeMain().End().Position());
627 	if (invalidateWholeSelection) {
628 		for (size_t r=0; r<sel.Count(); r++) {
629 			firstAffected = std::min(firstAffected, sel.Range(r).caret.Position());
630 			firstAffected = std::min(firstAffected, sel.Range(r).anchor.Position());
631 			lastAffected = std::max(lastAffected, sel.Range(r).caret.Position()+1);
632 			lastAffected = std::max(lastAffected, sel.Range(r).anchor.Position());
633 		}
634 	}
635 	ContainerNeedsUpdate(SC_UPDATE_SELECTION);
636 	InvalidateRange(firstAffected, lastAffected);
637 }
638 
InvalidateWholeSelection()639 void Editor::InvalidateWholeSelection() {
640 	InvalidateSelection(sel.RangeMain(), true);
641 }
642 
643 /* For Line selection - the anchor and caret are always
644    at the beginning and end of the region lines. */
LineSelectionRange(SelectionPosition currentPos_,SelectionPosition anchor_) const645 SelectionRange Editor::LineSelectionRange(SelectionPosition currentPos_, SelectionPosition anchor_) const {
646 	if (currentPos_ > anchor_) {
647 		anchor_ = SelectionPosition(
648 			pdoc->LineStart(pdoc->LineFromPosition(anchor_.Position())));
649 		currentPos_ = SelectionPosition(
650 			pdoc->LineEnd(pdoc->LineFromPosition(currentPos_.Position())));
651 	} else {
652 		currentPos_ = SelectionPosition(
653 			pdoc->LineStart(pdoc->LineFromPosition(currentPos_.Position())));
654 		anchor_ = SelectionPosition(
655 			pdoc->LineEnd(pdoc->LineFromPosition(anchor_.Position())));
656 	}
657 	return SelectionRange(currentPos_, anchor_);
658 }
659 
SetSelection(SelectionPosition currentPos_,SelectionPosition anchor_)660 void Editor::SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_) {
661 	currentPos_ = ClampPositionIntoDocument(currentPos_);
662 	anchor_ = ClampPositionIntoDocument(anchor_);
663 	const Sci::Line currentLine = pdoc->SciLineFromPosition(currentPos_.Position());
664 	SelectionRange rangeNew(currentPos_, anchor_);
665 	if (sel.selType == Selection::selLines) {
666 		rangeNew = LineSelectionRange(currentPos_, anchor_);
667 	}
668 	if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
669 		InvalidateSelection(rangeNew);
670 	}
671 	sel.RangeMain() = rangeNew;
672 	SetRectangularRange();
673 	ClaimSelection();
674 	SetHoverIndicatorPosition(sel.MainCaret());
675 
676 	if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
677 		RedrawSelMargin();
678 	}
679 	QueueIdleWork(WorkNeeded::workUpdateUI);
680 }
681 
SetSelection(Sci::Position currentPos_,Sci::Position anchor_)682 void Editor::SetSelection(Sci::Position currentPos_, Sci::Position anchor_) {
683 	SetSelection(SelectionPosition(currentPos_), SelectionPosition(anchor_));
684 }
685 
686 // Just move the caret on the main selection
SetSelection(SelectionPosition currentPos_)687 void Editor::SetSelection(SelectionPosition currentPos_) {
688 	currentPos_ = ClampPositionIntoDocument(currentPos_);
689 	const Sci::Line currentLine = pdoc->SciLineFromPosition(currentPos_.Position());
690 	if (sel.Count() > 1 || !(sel.RangeMain().caret == currentPos_)) {
691 		InvalidateSelection(SelectionRange(currentPos_));
692 	}
693 	if (sel.IsRectangular()) {
694 		sel.Rectangular() =
695 			SelectionRange(SelectionPosition(currentPos_), sel.Rectangular().anchor);
696 		SetRectangularRange();
697 	} else if (sel.selType == Selection::selLines) {
698 		sel.RangeMain() = LineSelectionRange(currentPos_, sel.RangeMain().anchor);
699 	} else {
700 		sel.RangeMain() =
701 			SelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor);
702 	}
703 	ClaimSelection();
704 	SetHoverIndicatorPosition(sel.MainCaret());
705 
706 	if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
707 		RedrawSelMargin();
708 	}
709 	QueueIdleWork(WorkNeeded::workUpdateUI);
710 }
711 
SetSelection(int currentPos_)712 void Editor::SetSelection(int currentPos_) {
713 	SetSelection(SelectionPosition(currentPos_));
714 }
715 
SetEmptySelection(SelectionPosition currentPos_)716 void Editor::SetEmptySelection(SelectionPosition currentPos_) {
717 	const Sci::Line currentLine = pdoc->SciLineFromPosition(currentPos_.Position());
718 	SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_));
719 	if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
720 		InvalidateSelection(rangeNew);
721 	}
722 	sel.Clear();
723 	sel.RangeMain() = rangeNew;
724 	SetRectangularRange();
725 	ClaimSelection();
726 	SetHoverIndicatorPosition(sel.MainCaret());
727 
728 	if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
729 		RedrawSelMargin();
730 	}
731 	QueueIdleWork(WorkNeeded::workUpdateUI);
732 }
733 
SetEmptySelection(Sci::Position currentPos_)734 void Editor::SetEmptySelection(Sci::Position currentPos_) {
735 	SetEmptySelection(SelectionPosition(currentPos_));
736 }
737 
MultipleSelectAdd(AddNumber addNumber)738 void Editor::MultipleSelectAdd(AddNumber addNumber) {
739 	if (SelectionEmpty() || !multipleSelection) {
740 		// Select word at caret
741 		const Sci::Position startWord = pdoc->ExtendWordSelect(sel.MainCaret(), -1, true);
742 		const Sci::Position endWord = pdoc->ExtendWordSelect(startWord, 1, true);
743 		TrimAndSetSelection(endWord, startWord);
744 
745 	} else {
746 
747 		if (!pdoc->HasCaseFolder())
748 			pdoc->SetCaseFolder(CaseFolderForEncoding());
749 
750 		const Range rangeMainSelection(sel.RangeMain().Start().Position(), sel.RangeMain().End().Position());
751 		const std::string selectedText = RangeText(rangeMainSelection.start, rangeMainSelection.end);
752 
753 		const Range rangeTarget(targetRange.start.Position(), targetRange.end.Position());
754 		std::vector<Range> searchRanges;
755 		// Search should be over the target range excluding the current selection so
756 		// may need to search 2 ranges, after the selection then before the selection.
757 		if (rangeTarget.Overlaps(rangeMainSelection)) {
758 			// Common case is that the selection is completely within the target but
759 			// may also have overlap at start or end.
760 			if (rangeMainSelection.end < rangeTarget.end)
761 				searchRanges.push_back(Range(rangeMainSelection.end, rangeTarget.end));
762 			if (rangeTarget.start < rangeMainSelection.start)
763 				searchRanges.push_back(Range(rangeTarget.start, rangeMainSelection.start));
764 		} else {
765 			// No overlap
766 			searchRanges.push_back(rangeTarget);
767 		}
768 
769 		for (std::vector<Range>::const_iterator it = searchRanges.begin(); it != searchRanges.end(); ++it) {
770 			Sci::Position searchStart = it->start;
771 			const Sci::Position searchEnd = it->end;
772 			for (;;) {
773 				Sci::Position lengthFound = selectedText.length();
774 				const Sci::Position pos = pdoc->FindText(searchStart, searchEnd,
775 					selectedText.c_str(), searchFlags, &lengthFound);
776 				if (pos >= 0) {
777 					sel.AddSelection(SelectionRange(pos + lengthFound, pos));
778 					ContainerNeedsUpdate(SC_UPDATE_SELECTION);
779 					ScrollRange(sel.RangeMain());
780 					Redraw();
781 					if (addNumber == addOne)
782 						return;
783 					searchStart = pos + lengthFound;
784 				} else {
785 					break;
786 				}
787 			}
788 		}
789 	}
790 }
791 
RangeContainsProtected(Sci::Position start,Sci::Position end) const792 bool Editor::RangeContainsProtected(Sci::Position start, Sci::Position end) const noexcept {
793 	if (vs.ProtectionActive()) {
794 		if (start > end) {
795 			const Sci::Position t = start;
796 			start = end;
797 			end = t;
798 		}
799 		for (Sci::Position pos = start; pos < end; pos++) {
800 			if (vs.styles[pdoc->StyleIndexAt(pos)].IsProtected())
801 				return true;
802 		}
803 	}
804 	return false;
805 }
806 
SelectionContainsProtected() const807 bool Editor::SelectionContainsProtected() const {
808 	for (size_t r=0; r<sel.Count(); r++) {
809 		if (RangeContainsProtected(sel.Range(r).Start().Position(),
810 			sel.Range(r).End().Position())) {
811 			return true;
812 		}
813 	}
814 	return false;
815 }
816 
817 /**
818  * Asks document to find a good position and then moves out of any invisible positions.
819  */
MovePositionOutsideChar(Sci::Position pos,Sci::Position moveDir,bool checkLineEnd) const820 Sci::Position Editor::MovePositionOutsideChar(Sci::Position pos, Sci::Position moveDir, bool checkLineEnd) const {
821 	return MovePositionOutsideChar(SelectionPosition(pos), moveDir, checkLineEnd).Position();
822 }
823 
MovePositionOutsideChar(SelectionPosition pos,Sci::Position moveDir,bool checkLineEnd) const824 SelectionPosition Editor::MovePositionOutsideChar(SelectionPosition pos, Sci::Position moveDir, bool checkLineEnd) const {
825 	const Sci::Position posMoved = pdoc->MovePositionOutsideChar(pos.Position(), moveDir, checkLineEnd);
826 	if (posMoved != pos.Position())
827 		pos.SetPosition(posMoved);
828 	if (vs.ProtectionActive()) {
829 		if (moveDir > 0) {
830 			if ((pos.Position() > 0) && vs.styles[pdoc->StyleIndexAt(pos.Position() - 1)].IsProtected()) {
831 				while ((pos.Position() < pdoc->Length()) &&
832 				        (vs.styles[pdoc->StyleIndexAt(pos.Position())].IsProtected()))
833 					pos.Add(1);
834 			}
835 		} else if (moveDir < 0) {
836 			if (vs.styles[pdoc->StyleIndexAt(pos.Position())].IsProtected()) {
837 				while ((pos.Position() > 0) &&
838 				        (vs.styles[pdoc->StyleIndexAt(pos.Position() - 1)].IsProtected()))
839 					pos.Add(-1);
840 			}
841 		}
842 	}
843 	return pos;
844 }
845 
MovedCaret(SelectionPosition newPos,SelectionPosition previousPos,bool ensureVisible,CaretPolicies policies)846 void Editor::MovedCaret(SelectionPosition newPos, SelectionPosition previousPos,
847 	bool ensureVisible, CaretPolicies policies) {
848 	const Sci::Line currentLine = pdoc->SciLineFromPosition(newPos.Position());
849 	if (ensureVisible) {
850 		// In case in need of wrapping to ensure DisplayFromDoc works.
851 		if (currentLine >= wrapPending.start) {
852 			if (WrapLines(WrapScope::wsAll)) {
853 				Redraw();
854 			}
855 		}
856 		const XYScrollPosition newXY = XYScrollToMakeVisible(
857 			SelectionRange(posDrag.IsValid() ? posDrag : newPos), xysDefault, policies);
858 		if (previousPos.IsValid() && (newXY.xOffset == xOffset)) {
859 			// simple vertical scroll then invalidate
860 			ScrollTo(newXY.topLine);
861 			InvalidateSelection(SelectionRange(previousPos), true);
862 		} else {
863 			SetXYScroll(newXY);
864 		}
865 	}
866 
867 	ShowCaretAtCurrentPosition();
868 	NotifyCaretMove();
869 
870 	ClaimSelection();
871 	SetHoverIndicatorPosition(sel.MainCaret());
872 	QueueIdleWork(WorkNeeded::workUpdateUI);
873 
874 	if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
875 		RedrawSelMargin();
876 	}
877 }
878 
MovePositionTo(SelectionPosition newPos,Selection::selTypes selt,bool ensureVisible)879 void Editor::MovePositionTo(SelectionPosition newPos, Selection::selTypes selt, bool ensureVisible) {
880 	const SelectionPosition spCaret = ((sel.Count() == 1) && sel.Empty()) ?
881 		sel.Last() : SelectionPosition(INVALID_POSITION);
882 
883 	const Sci::Position delta = newPos.Position() - sel.MainCaret();
884 	newPos = ClampPositionIntoDocument(newPos);
885 	newPos = MovePositionOutsideChar(newPos, delta);
886 	if (!multipleSelection && sel.IsRectangular() && (selt == Selection::selStream)) {
887 		// Can't turn into multiple selection so clear additional selections
888 		InvalidateSelection(SelectionRange(newPos), true);
889 		sel.DropAdditionalRanges();
890 	}
891 	if (!sel.IsRectangular() && (selt == Selection::selRectangle)) {
892 		// Switching to rectangular
893 		InvalidateSelection(sel.RangeMain(), false);
894 		SelectionRange rangeMain = sel.RangeMain();
895 		sel.Clear();
896 		sel.Rectangular() = rangeMain;
897 	}
898 	if (selt != Selection::noSel) {
899 		sel.selType = selt;
900 	}
901 	if (selt != Selection::noSel || sel.MoveExtends()) {
902 		SetSelection(newPos);
903 	} else {
904 		SetEmptySelection(newPos);
905 	}
906 
907 	MovedCaret(newPos, spCaret, ensureVisible, caretPolicies);
908 }
909 
MovePositionTo(Sci::Position newPos,Selection::selTypes selt,bool ensureVisible)910 void Editor::MovePositionTo(Sci::Position newPos, Selection::selTypes selt, bool ensureVisible) {
911 	MovePositionTo(SelectionPosition(newPos), selt, ensureVisible);
912 }
913 
MovePositionSoVisible(SelectionPosition pos,int moveDir)914 SelectionPosition Editor::MovePositionSoVisible(SelectionPosition pos, int moveDir) {
915 	pos = ClampPositionIntoDocument(pos);
916 	pos = MovePositionOutsideChar(pos, moveDir);
917 	const Sci::Line lineDoc = pdoc->SciLineFromPosition(pos.Position());
918 	if (pcs->GetVisible(lineDoc)) {
919 		return pos;
920 	} else {
921 		Sci::Line lineDisplay = pcs->DisplayFromDoc(lineDoc);
922 		if (moveDir > 0) {
923 			// lineDisplay is already line before fold as lines in fold use display line of line after fold
924 			lineDisplay = std::clamp<Sci::Line>(lineDisplay, 0, pcs->LinesDisplayed());
925 			return SelectionPosition(
926 				pdoc->LineStart(pcs->DocFromDisplay(lineDisplay)));
927 		} else {
928 			lineDisplay = std::clamp<Sci::Line>(lineDisplay - 1, 0, pcs->LinesDisplayed());
929 			return SelectionPosition(
930 				pdoc->LineEnd(pcs->DocFromDisplay(lineDisplay)));
931 		}
932 	}
933 }
934 
MovePositionSoVisible(Sci::Position pos,int moveDir)935 SelectionPosition Editor::MovePositionSoVisible(Sci::Position pos, int moveDir) {
936 	return MovePositionSoVisible(SelectionPosition(pos), moveDir);
937 }
938 
PointMainCaret()939 Point Editor::PointMainCaret() {
940 	return LocationFromPosition(sel.Range(sel.Main()).caret);
941 }
942 
943 /**
944  * Choose the x position that the caret will try to stick to
945  * as it moves up and down.
946  */
SetLastXChosen()947 void Editor::SetLastXChosen() {
948 	const Point pt = PointMainCaret();
949 	lastXChosen = static_cast<int>(pt.x) + xOffset;
950 }
951 
ScrollTo(Sci::Line line,bool moveThumb)952 void Editor::ScrollTo(Sci::Line line, bool moveThumb) {
953 	const Sci::Line topLineNew = std::clamp<Sci::Line>(line, 0, MaxScrollPos());
954 	if (topLineNew != topLine) {
955 		// Try to optimise small scrolls
956 #ifndef UNDER_CE
957 		const Sci::Line linesToMove = topLine - topLineNew;
958 		const bool performBlit = (std::abs(linesToMove) <= 10) && (paintState == notPainting);
959 		willRedrawAll = !performBlit;
960 #endif
961 		SetTopLine(topLineNew);
962 		// Optimize by styling the view as this will invalidate any needed area
963 		// which could abort the initial paint if discovered later.
964 		StyleAreaBounded(GetClientRectangle(), true);
965 #ifndef UNDER_CE
966 		// Perform redraw rather than scroll if many lines would be redrawn anyway.
967 		if (performBlit) {
968 			ScrollText(linesToMove);
969 		} else {
970 			Redraw();
971 		}
972 		willRedrawAll = false;
973 #else
974 		Redraw();
975 #endif
976 		if (moveThumb) {
977 			SetVerticalScrollPos();
978 		}
979 	}
980 }
981 
ScrollText(Sci::Line)982 void Editor::ScrollText(Sci::Line /* linesToMove */) {
983 	//Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
984 	Redraw();
985 }
986 
HorizontalScrollTo(int xPos)987 void Editor::HorizontalScrollTo(int xPos) {
988 	//Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
989 	if (xPos < 0)
990 		xPos = 0;
991 	if (!Wrapping() && (xOffset != xPos)) {
992 		xOffset = xPos;
993 		ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
994 		SetHorizontalScrollPos();
995 		RedrawRect(GetClientRectangle());
996 	}
997 }
998 
VerticalCentreCaret()999 void Editor::VerticalCentreCaret() {
1000 	const Sci::Line lineDoc =
1001 		pdoc->SciLineFromPosition(sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret());
1002 	const Sci::Line lineDisplay = pcs->DisplayFromDoc(lineDoc);
1003 	const Sci::Line newTop = lineDisplay - (LinesOnScreen() / 2);
1004 	if (topLine != newTop) {
1005 		SetTopLine(newTop > 0 ? newTop : 0);
1006 		RedrawRect(GetClientRectangle());
1007 	}
1008 }
1009 
MoveSelectedLines(int lineDelta)1010 void Editor::MoveSelectedLines(int lineDelta) {
1011 
1012 	if (sel.IsRectangular()) {
1013 		return;
1014 	}
1015 
1016 	// if selection doesn't start at the beginning of the line, set the new start
1017 	Sci::Position selectionStart = SelectionStart().Position();
1018 	const Sci::Line startLine = pdoc->SciLineFromPosition(selectionStart);
1019 	const Sci::Position beginningOfStartLine = pdoc->LineStart(startLine);
1020 	selectionStart = beginningOfStartLine;
1021 
1022 	// if selection doesn't end at the beginning of a line greater than that of the start,
1023 	// then set it at the beginning of the next one
1024 	Sci::Position selectionEnd = SelectionEnd().Position();
1025 	const Sci::Line endLine = pdoc->SciLineFromPosition(selectionEnd);
1026 	const Sci::Position beginningOfEndLine = pdoc->LineStart(endLine);
1027 	bool appendEol = false;
1028 	if (selectionEnd > beginningOfEndLine
1029 		|| selectionStart == selectionEnd) {
1030 		selectionEnd = pdoc->LineStart(endLine + 1);
1031 		appendEol = (selectionEnd == pdoc->Length() && pdoc->SciLineFromPosition(selectionEnd) == endLine);
1032 	}
1033 
1034 	// if there's nowhere for the selection to move
1035 	// (i.e. at the beginning going up or at the end going down),
1036 	// stop it right there!
1037 	if ((selectionStart == 0 && lineDelta < 0)
1038 		|| (selectionEnd == pdoc->Length() && lineDelta > 0)
1039 	        || selectionStart == selectionEnd) {
1040 		return;
1041 	}
1042 
1043 	UndoGroup ug(pdoc);
1044 
1045 	if (lineDelta > 0 && selectionEnd == pdoc->LineStart(pdoc->LinesTotal() - 1)) {
1046 		SetSelection(pdoc->MovePositionOutsideChar(selectionEnd - 1, -1), selectionEnd);
1047 		ClearSelection();
1048 		selectionEnd = CurrentPosition();
1049 	}
1050 	SetSelection(selectionStart, selectionEnd);
1051 
1052 	SelectionText selectedText;
1053 	CopySelectionRange(&selectedText);
1054 
1055 	const Point currentLocation = LocationFromPosition(CurrentPosition());
1056 	const Sci::Line currentLine = LineFromLocation(currentLocation);
1057 
1058 	if (appendEol)
1059 		SetSelection(pdoc->MovePositionOutsideChar(selectionStart - 1, -1), selectionEnd);
1060 	ClearSelection();
1061 
1062 	const char *eol = StringFromEOLMode(pdoc->eolMode);
1063 	if (currentLine + lineDelta >= pdoc->LinesTotal())
1064 		pdoc->InsertString(pdoc->Length(), eol, strlen(eol));
1065 	GoToLine(currentLine + lineDelta);
1066 
1067 	Sci::Position selectionLength = pdoc->InsertString(CurrentPosition(), selectedText.Data(), selectedText.Length());
1068 	if (appendEol) {
1069 		const Sci::Position lengthInserted = pdoc->InsertString(CurrentPosition() + selectionLength, eol, strlen(eol));
1070 		selectionLength += lengthInserted;
1071 	}
1072 	SetSelection(CurrentPosition(), CurrentPosition() + selectionLength);
1073 }
1074 
MoveSelectedLinesUp()1075 void Editor::MoveSelectedLinesUp() {
1076 	MoveSelectedLines(-1);
1077 }
1078 
MoveSelectedLinesDown()1079 void Editor::MoveSelectedLinesDown() {
1080 	MoveSelectedLines(1);
1081 }
1082 
MoveCaretInsideView(bool ensureVisible)1083 void Editor::MoveCaretInsideView(bool ensureVisible) {
1084 	const PRectangle rcClient = GetTextRectangle();
1085 	const Point pt = PointMainCaret();
1086 	if (pt.y < rcClient.top) {
1087 		MovePositionTo(SPositionFromLocation(
1088 		            Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top)),
1089 					false, false, UserVirtualSpace()),
1090 					Selection::noSel, ensureVisible);
1091 	} else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
1092 		const ptrdiff_t yOfLastLineFullyDisplayed = static_cast<ptrdiff_t>(rcClient.top) + (LinesOnScreen() - 1) * vs.lineHeight;
1093 		MovePositionTo(SPositionFromLocation(
1094 		            Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top + yOfLastLineFullyDisplayed)),
1095 					false, false, UserVirtualSpace()),
1096 		        Selection::noSel, ensureVisible);
1097 	}
1098 }
1099 
DisplayFromPosition(Sci::Position pos)1100 Sci::Line Editor::DisplayFromPosition(Sci::Position pos) {
1101 	AutoSurface surface(this);
1102 	return view.DisplayFromPosition(surface, *this, pos, vs);
1103 }
1104 
1105 /**
1106  * Ensure the caret is reasonably visible in context.
1107  *
1108 Caret policy in Scintilla
1109 
1110 If slop is set, we can define a slop value.
1111 This value defines an unwanted zone (UZ) where the caret is... unwanted.
1112 This zone is defined as a number of pixels near the vertical margins,
1113 and as a number of lines near the horizontal margins.
1114 By keeping the caret away from the edges, it is seen within its context,
1115 so it is likely that the identifier that the caret is on can be completely seen,
1116 and that the current line is seen with some of the lines following it which are
1117 often dependent on that line.
1118 
1119 If strict is set, the policy is enforced... strictly.
1120 The caret is centred on the display if slop is not set,
1121 and cannot go in the UZ if slop is set.
1122 
1123 If jumps is set, the display is moved more energetically
1124 so the caret can move in the same direction longer before the policy is applied again.
1125 '3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
1126 
1127 If even is not set, instead of having symmetrical UZs,
1128 the left and bottom UZs are extended up to right and top UZs respectively.
1129 This way, we favour the displaying of useful information: the beginning of lines,
1130 where most code reside, and the lines after the caret, eg. the body of a function.
1131 
1132      |        |       |      |                                            |
1133 slop | strict | jumps | even | Caret can go to the margin                 | When reaching limit (caret going out of
1134      |        |       |      |                                            | visibility or going into the UZ) display is...
1135 -----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
1136   0  |   0    |   0   |   0  | Yes                                        | moved to put caret on top/on right
1137   0  |   0    |   0   |   1  | Yes                                        | moved by one position
1138   0  |   0    |   1   |   0  | Yes                                        | moved to put caret on top/on right
1139   0  |   0    |   1   |   1  | Yes                                        | centred on the caret
1140   0  |   1    |   -   |   0  | Caret is always on top/on right of display | -
1141   0  |   1    |   -   |   1  | No, caret is always centred                | -
1142   1  |   0    |   0   |   0  | Yes                                        | moved to put caret out of the asymmetrical UZ
1143   1  |   0    |   0   |   1  | Yes                                        | moved to put caret out of the UZ
1144   1  |   0    |   1   |   0  | Yes                                        | moved to put caret at 3UZ of the top or right margin
1145   1  |   0    |   1   |   1  | Yes                                        | moved to put caret at 3UZ of the margin
1146   1  |   1    |   -   |   0  | Caret is always at UZ of top/right margin  | -
1147   1  |   1    |   0   |   1  | No, kept out of UZ                         | moved by one position
1148   1  |   1    |   1   |   1  | No, kept out of UZ                         | moved to put caret at 3UZ of the margin
1149 */
1150 
XYScrollToMakeVisible(const SelectionRange & range,const XYScrollOptions options,CaretPolicies policies)1151 Editor::XYScrollPosition Editor::XYScrollToMakeVisible(const SelectionRange &range,
1152 	const XYScrollOptions options, CaretPolicies policies) {
1153 	const PRectangle rcClient = GetTextRectangle();
1154 	const Point ptOrigin = GetVisibleOriginInMain();
1155 	const Point pt = LocationFromPosition(range.caret) + ptOrigin;
1156 	const Point ptAnchor = LocationFromPosition(range.anchor) + ptOrigin;
1157 	const Point ptBottomCaret(pt.x, pt.y + vs.lineHeight - 1);
1158 
1159 	XYScrollPosition newXY(xOffset, topLine);
1160 	if (rcClient.Empty()) {
1161 		return newXY;
1162 	}
1163 
1164 	// Vertical positioning
1165 	if ((options & xysVertical) && (pt.y < rcClient.top || ptBottomCaret.y >= rcClient.bottom || (policies.y.policy & CARET_STRICT) != 0)) {
1166 		const Sci::Line lineCaret = DisplayFromPosition(range.caret.Position());
1167 		const Sci::Line linesOnScreen = LinesOnScreen();
1168 		const Sci::Line halfScreen = std::max(linesOnScreen - 1, static_cast<Sci::Line>(2)) / 2;
1169 		const bool bSlop = (policies.y.policy & CARET_SLOP) != 0;
1170 		const bool bStrict = (policies.y.policy & CARET_STRICT) != 0;
1171 		const bool bJump = (policies.y.policy & CARET_JUMPS) != 0;
1172 		const bool bEven = (policies.y.policy & CARET_EVEN) != 0;
1173 
1174 		// It should be possible to scroll the window to show the caret,
1175 		// but this fails to remove the caret on GTK+
1176 		if (bSlop) {	// A margin is defined
1177 			Sci::Line yMoveT, yMoveB;
1178 			if (bStrict) {
1179 				Sci::Line yMarginT, yMarginB;
1180 				if (!(options & xysUseMargin)) {
1181 					// In drag mode, avoid moves
1182 					// otherwise, a double click will select several lines.
1183 					yMarginT = yMarginB = 0;
1184 				} else {
1185 					// yMarginT must equal to caretYSlop, with a minimum of 1 and
1186 					// a maximum of slightly less than half the heigth of the text area.
1187 					yMarginT = std::clamp<Sci::Line>(policies.y.slop, 1, halfScreen);
1188 					if (bEven) {
1189 						yMarginB = yMarginT;
1190 					} else {
1191 						yMarginB = linesOnScreen - yMarginT - 1;
1192 					}
1193 				}
1194 				yMoveT = yMarginT;
1195 				if (bEven) {
1196 					if (bJump) {
1197 						yMoveT = std::clamp<Sci::Line>(policies.y.slop * 3, 1, halfScreen);
1198 					}
1199 					yMoveB = yMoveT;
1200 				} else {
1201 					yMoveB = linesOnScreen - yMoveT - 1;
1202 				}
1203 				if (lineCaret < topLine + yMarginT) {
1204 					// Caret goes too high
1205 					newXY.topLine = lineCaret - yMoveT;
1206 				} else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {
1207 					// Caret goes too low
1208 					newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1209 				}
1210 			} else {	// Not strict
1211 				yMoveT = bJump ? policies.y.slop * 3 : policies.y.slop;
1212 				yMoveT = std::clamp<Sci::Line>(yMoveT, 1, halfScreen);
1213 				if (bEven) {
1214 					yMoveB = yMoveT;
1215 				} else {
1216 					yMoveB = linesOnScreen - yMoveT - 1;
1217 				}
1218 				if (lineCaret < topLine) {
1219 					// Caret goes too high
1220 					newXY.topLine = lineCaret - yMoveT;
1221 				} else if (lineCaret > topLine + linesOnScreen - 1) {
1222 					// Caret goes too low
1223 					newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1224 				}
1225 			}
1226 		} else {	// No slop
1227 			if (!bStrict && !bJump) {
1228 				// Minimal move
1229 				if (lineCaret < topLine) {
1230 					// Caret goes too high
1231 					newXY.topLine = lineCaret;
1232 				} else if (lineCaret > topLine + linesOnScreen - 1) {
1233 					// Caret goes too low
1234 					if (bEven) {
1235 						newXY.topLine = lineCaret - linesOnScreen + 1;
1236 					} else {
1237 						newXY.topLine = lineCaret;
1238 					}
1239 				}
1240 			} else {	// Strict or going out of display
1241 				if (bEven) {
1242 					// Always center caret
1243 					newXY.topLine = lineCaret - halfScreen;
1244 				} else {
1245 					// Always put caret on top of display
1246 					newXY.topLine = lineCaret;
1247 				}
1248 			}
1249 		}
1250 		if (!(range.caret == range.anchor)) {
1251 			const Sci::Line lineAnchor = DisplayFromPosition(range.anchor.Position());
1252 			if (lineAnchor < lineCaret) {
1253 				// Shift up to show anchor or as much of range as possible
1254 				newXY.topLine = std::min(newXY.topLine, lineAnchor);
1255 				newXY.topLine = std::max(newXY.topLine, lineCaret - LinesOnScreen());
1256 			} else {
1257 				// Shift down to show anchor or as much of range as possible
1258 				newXY.topLine = std::max(newXY.topLine, lineAnchor - LinesOnScreen());
1259 				newXY.topLine = std::min(newXY.topLine, lineCaret);
1260 			}
1261 		}
1262 		newXY.topLine = std::clamp<Sci::Line>(newXY.topLine, 0, MaxScrollPos());
1263 	}
1264 
1265 	// Horizontal positioning
1266 	if ((options & xysHorizontal) && !Wrapping()) {
1267 		const int halfScreen = std::max(static_cast<int>(rcClient.Width()) - 4, 4) / 2;
1268 		const bool bSlop = (policies.x.policy & CARET_SLOP) != 0;
1269 		const bool bStrict = (policies.x.policy & CARET_STRICT) != 0;
1270 		const bool bJump = (policies.x.policy & CARET_JUMPS) != 0;
1271 		const bool bEven = (policies.x.policy & CARET_EVEN) != 0;
1272 
1273 		if (bSlop) {	// A margin is defined
1274 			int xMoveL, xMoveR;
1275 			if (bStrict) {
1276 				int xMarginL, xMarginR;
1277 				if (!(options & xysUseMargin)) {
1278 					// In drag mode, avoid moves unless very near of the margin
1279 					// otherwise, a simple click will select text.
1280 					xMarginL = xMarginR = 2;
1281 				} else {
1282 					// xMargin must equal to caretXSlop, with a minimum of 2 and
1283 					// a maximum of slightly less than half the width of the text area.
1284 					xMarginR = std::clamp(policies.x.slop, 2, halfScreen);
1285 					if (bEven) {
1286 						xMarginL = xMarginR;
1287 					} else {
1288 						xMarginL = static_cast<int>(rcClient.Width()) - xMarginR - 4;
1289 					}
1290 				}
1291 				if (bJump && bEven) {
1292 					// Jump is used only in even mode
1293 					xMoveL = xMoveR = std::clamp(policies.x.slop * 3, 1, halfScreen);
1294 				} else {
1295 					xMoveL = xMoveR = 0;	// Not used, avoid a warning
1296 				}
1297 				if (pt.x < rcClient.left + xMarginL) {
1298 					// Caret is on the left of the display
1299 					if (bJump && bEven) {
1300 						newXY.xOffset -= xMoveL;
1301 					} else {
1302 						// Move just enough to allow to display the caret
1303 						newXY.xOffset -= static_cast<int>((rcClient.left + xMarginL) - pt.x);
1304 					}
1305 				} else if (pt.x >= rcClient.right - xMarginR) {
1306 					// Caret is on the right of the display
1307 					if (bJump && bEven) {
1308 						newXY.xOffset += xMoveR;
1309 					} else {
1310 						// Move just enough to allow to display the caret
1311 						newXY.xOffset += static_cast<int>(pt.x - (rcClient.right - xMarginR) + 1);
1312 					}
1313 				}
1314 			} else {	// Not strict
1315 				xMoveR = bJump ? policies.x.slop * 3 : policies.x.slop;
1316 				xMoveR = std::clamp(xMoveR, 1, halfScreen);
1317 				if (bEven) {
1318 					xMoveL = xMoveR;
1319 				} else {
1320 					xMoveL = static_cast<int>(rcClient.Width()) - xMoveR - 4;
1321 				}
1322 				if (pt.x < rcClient.left) {
1323 					// Caret is on the left of the display
1324 					newXY.xOffset -= xMoveL;
1325 				} else if (pt.x >= rcClient.right) {
1326 					// Caret is on the right of the display
1327 					newXY.xOffset += xMoveR;
1328 				}
1329 			}
1330 		} else {	// No slop
1331 			if (bStrict ||
1332 			        (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {
1333 				// Strict or going out of display
1334 				if (bEven) {
1335 					// Center caret
1336 					newXY.xOffset += static_cast<int>(pt.x - rcClient.left - halfScreen);
1337 				} else {
1338 					// Put caret on right
1339 					newXY.xOffset += static_cast<int>(pt.x - rcClient.right + 1);
1340 				}
1341 			} else {
1342 				// Move just enough to allow to display the caret
1343 				if (pt.x < rcClient.left) {
1344 					// Caret is on the left of the display
1345 					if (bEven) {
1346 						newXY.xOffset -= static_cast<int>(rcClient.left - pt.x);
1347 					} else {
1348 						newXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;
1349 					}
1350 				} else if (pt.x >= rcClient.right) {
1351 					// Caret is on the right of the display
1352 					newXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;
1353 				}
1354 			}
1355 		}
1356 		// In case of a jump (find result) largely out of display, adjust the offset to display the caret
1357 		if (pt.x + xOffset < rcClient.left + newXY.xOffset) {
1358 			newXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 2;
1359 		} else if (pt.x + xOffset >= rcClient.right + newXY.xOffset) {
1360 			newXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 2;
1361 			if (vs.IsBlockCaretStyle() || view.imeCaretBlockOverride) {
1362 				// Ensure we can see a good portion of the block caret
1363 				newXY.xOffset += static_cast<int>(vs.aveCharWidth);
1364 			}
1365 		}
1366 		if (!(range.caret == range.anchor)) {
1367 			if (ptAnchor.x < pt.x) {
1368 				// Shift to left to show anchor or as much of range as possible
1369 				const int maxOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.left) - 1;
1370 				const int minOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 1;
1371 				newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1372 				newXY.xOffset = std::max(newXY.xOffset, minOffset);
1373 			} else {
1374 				// Shift to right to show anchor or as much of range as possible
1375 				const int minOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.right) + 1;
1376 				const int maxOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 1;
1377 				newXY.xOffset = std::max(newXY.xOffset, minOffset);
1378 				newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1379 			}
1380 		}
1381 		if (newXY.xOffset < 0) {
1382 			newXY.xOffset = 0;
1383 		}
1384 	}
1385 
1386 	return newXY;
1387 }
1388 
SetXYScroll(XYScrollPosition newXY)1389 void Editor::SetXYScroll(XYScrollPosition newXY) {
1390 	if ((newXY.topLine != topLine) || (newXY.xOffset != xOffset)) {
1391 		if (newXY.topLine != topLine) {
1392 			SetTopLine(newXY.topLine);
1393 			SetVerticalScrollPos();
1394 		}
1395 		if (newXY.xOffset != xOffset) {
1396 			xOffset = newXY.xOffset;
1397 			ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
1398 			if (newXY.xOffset > 0) {
1399 				const PRectangle rcText = GetTextRectangle();
1400 				if (horizontalScrollBarVisible &&
1401 					rcText.Width() + xOffset > scrollWidth) {
1402 					scrollWidth = xOffset + static_cast<int>(rcText.Width());
1403 					SetScrollBars();
1404 				}
1405 			}
1406 			SetHorizontalScrollPos();
1407 		}
1408 		Redraw();
1409 		UpdateSystemCaret();
1410 	}
1411 }
1412 
ScrollRange(SelectionRange range)1413 void Editor::ScrollRange(SelectionRange range) {
1414 	SetXYScroll(XYScrollToMakeVisible(range, xysDefault, caretPolicies));
1415 }
1416 
EnsureCaretVisible(bool useMargin,bool vert,bool horiz)1417 void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
1418 	SetXYScroll(XYScrollToMakeVisible(SelectionRange(posDrag.IsValid() ? posDrag : sel.RangeMain().caret),
1419 		static_cast<XYScrollOptions>((useMargin?xysUseMargin:0)|(vert?xysVertical:0)|(horiz?xysHorizontal:0)),
1420 		caretPolicies));
1421 }
1422 
ShowCaretAtCurrentPosition()1423 void Editor::ShowCaretAtCurrentPosition() {
1424 	if (hasFocus) {
1425 		caret.active = true;
1426 		caret.on = true;
1427 		FineTickerCancel(tickCaret);
1428 		if (caret.period > 0)
1429 			FineTickerStart(tickCaret, caret.period, caret.period/10);
1430 	} else {
1431 		caret.active = false;
1432 		caret.on = false;
1433 		FineTickerCancel(tickCaret);
1434 	}
1435 	InvalidateCaret();
1436 }
1437 
DropCaret()1438 void Editor::DropCaret() {
1439 	caret.active = false;
1440 	FineTickerCancel(tickCaret);
1441 	InvalidateCaret();
1442 }
1443 
CaretSetPeriod(int period)1444 void Editor::CaretSetPeriod(int period) {
1445 	if (caret.period != period) {
1446 		caret.period = period;
1447 		caret.on = true;
1448 		FineTickerCancel(tickCaret);
1449 		if ((caret.active) && (caret.period > 0))
1450 			FineTickerStart(tickCaret, caret.period, caret.period/10);
1451 		InvalidateCaret();
1452 	}
1453 }
1454 
InvalidateCaret()1455 void Editor::InvalidateCaret() {
1456 	if (posDrag.IsValid()) {
1457 		InvalidateRange(posDrag.Position(), posDrag.Position() + 1);
1458 	} else {
1459 		for (size_t r=0; r<sel.Count(); r++) {
1460 			InvalidateRange(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1);
1461 		}
1462 	}
1463 	UpdateSystemCaret();
1464 }
1465 
NotifyCaretMove()1466 void Editor::NotifyCaretMove() {
1467 }
1468 
UpdateSystemCaret()1469 void Editor::UpdateSystemCaret() {
1470 }
1471 
Wrapping() const1472 bool Editor::Wrapping() const noexcept {
1473 	return vs.wrapState != eWrapNone;
1474 }
1475 
NeedWrapping(Sci::Line docLineStart,Sci::Line docLineEnd)1476 void Editor::NeedWrapping(Sci::Line docLineStart, Sci::Line docLineEnd) {
1477 //Platform::DebugPrintf("\nNeedWrapping: %0d..%0d\n", docLineStart, docLineEnd);
1478 	if (wrapPending.AddRange(docLineStart, docLineEnd)) {
1479 		view.llc.Invalidate(LineLayout::llPositions);
1480 	}
1481 	// Wrap lines during idle.
1482 	if (Wrapping() && wrapPending.NeedsWrap()) {
1483 		SetIdle(true);
1484 	}
1485 }
1486 
WrapOneLine(Surface * surface,Sci::Line lineToWrap)1487 bool Editor::WrapOneLine(Surface *surface, Sci::Line lineToWrap) {
1488 	AutoLineLayout ll(view.llc, view.RetrieveLineLayout(lineToWrap, *this));
1489 	int linesWrapped = 1;
1490 	if (ll) {
1491 		view.LayoutLine(*this, lineToWrap, surface, vs, ll, wrapWidth);
1492 		linesWrapped = ll->lines;
1493 	}
1494 	return pcs->SetHeight(lineToWrap, linesWrapped +
1495 		(vs.annotationVisible ? pdoc->AnnotationLines(lineToWrap) : 0));
1496 }
1497 
1498 // Perform  wrapping for a subset of the lines needing wrapping.
1499 // wsAll: wrap all lines which need wrapping in this single call
1500 // wsVisible: wrap currently visible lines
1501 // wsIdle: wrap one page + 100 lines
1502 // Return true if wrapping occurred.
WrapLines(WrapScope ws)1503 bool Editor::WrapLines(WrapScope ws) {
1504 	Sci::Line goodTopLine = topLine;
1505 	bool wrapOccurred = false;
1506 	if (!Wrapping()) {
1507 		if (wrapWidth != LineLayout::wrapWidthInfinite) {
1508 			wrapWidth = LineLayout::wrapWidthInfinite;
1509 			for (Sci::Line lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
1510 				pcs->SetHeight(lineDoc, 1 +
1511 					(vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0));
1512 			}
1513 			wrapOccurred = true;
1514 		}
1515 		wrapPending.Reset();
1516 
1517 	} else if (wrapPending.NeedsWrap()) {
1518 		wrapPending.start = std::min(wrapPending.start, pdoc->LinesTotal());
1519 		if (!SetIdle(true)) {
1520 			// Idle processing not supported so full wrap required.
1521 			ws = WrapScope::wsAll;
1522 		}
1523 		// Decide where to start wrapping
1524 		Sci::Line lineToWrap = wrapPending.start;
1525 		Sci::Line lineToWrapEnd = std::min(wrapPending.end, pdoc->LinesTotal());
1526 		const Sci::Line lineDocTop = pcs->DocFromDisplay(topLine);
1527 		const Sci::Line subLineTop = topLine - pcs->DisplayFromDoc(lineDocTop);
1528 		if (ws == WrapScope::wsVisible) {
1529 			lineToWrap = std::clamp(lineDocTop-5, wrapPending.start, pdoc->LinesTotal());
1530 			// Priority wrap to just after visible area.
1531 			// Since wrapping could reduce display lines, treat each
1532 			// as taking only one display line.
1533 			lineToWrapEnd = lineDocTop;
1534 			Sci::Line lines = LinesOnScreen() + 1;
1535 			while ((lineToWrapEnd < pcs->LinesInDoc()) && (lines>0)) {
1536 				if (pcs->GetVisible(lineToWrapEnd))
1537 					lines--;
1538 				lineToWrapEnd++;
1539 			}
1540 			// .. and if the paint window is outside pending wraps
1541 			if ((lineToWrap > wrapPending.end) || (lineToWrapEnd < wrapPending.start)) {
1542 				// Currently visible text does not need wrapping
1543 				return false;
1544 			}
1545 		} else if (ws == WrapScope::wsIdle) {
1546 			// Try to keep time taken by wrapping reasonable so interaction remains smooth.
1547 			const double secondsAllowed = 0.01;
1548 			const Sci::Line linesInAllowedTime = std::clamp<Sci::Line>(
1549 				static_cast<Sci::Line>(secondsAllowed / durationWrapOneLine.Duration()),
1550 				LinesOnScreen() + 50, 0x10000);
1551 			lineToWrapEnd = lineToWrap + linesInAllowedTime;
1552 		}
1553 		const Sci::Line lineEndNeedWrap = std::min(wrapPending.end, pdoc->LinesTotal());
1554 		lineToWrapEnd = std::min(lineToWrapEnd, lineEndNeedWrap);
1555 
1556 		// Ensure all lines being wrapped are styled.
1557 		pdoc->EnsureStyledTo(pdoc->LineStart(lineToWrapEnd));
1558 
1559 		if (lineToWrap < lineToWrapEnd) {
1560 
1561 			PRectangle rcTextArea = GetClientRectangle();
1562 			rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
1563 			rcTextArea.right -= vs.rightMarginWidth;
1564 			wrapWidth = static_cast<int>(rcTextArea.Width());
1565 			RefreshStyleData();
1566 			AutoSurface surface(this);
1567 			if (surface) {
1568 //Platform::DebugPrintf("Wraplines: scope=%0d need=%0d..%0d perform=%0d..%0d\n", ws, wrapPending.start, wrapPending.end, lineToWrap, lineToWrapEnd);
1569 
1570 				const Sci::Line linesBeingWrapped = lineToWrapEnd - lineToWrap;
1571 				ElapsedPeriod epWrapping;
1572 				while (lineToWrap < lineToWrapEnd) {
1573 					if (WrapOneLine(surface, lineToWrap)) {
1574 						wrapOccurred = true;
1575 					}
1576 					wrapPending.Wrapped(lineToWrap);
1577 					lineToWrap++;
1578 				}
1579 				durationWrapOneLine.AddSample(linesBeingWrapped, epWrapping.Duration());
1580 
1581 				goodTopLine = pcs->DisplayFromDoc(lineDocTop) + std::min(
1582 					subLineTop, static_cast<Sci::Line>(pcs->GetHeight(lineDocTop)-1));
1583 			}
1584 		}
1585 
1586 		// If wrapping is done, bring it to resting position
1587 		if (wrapPending.start >= lineEndNeedWrap) {
1588 			wrapPending.Reset();
1589 		}
1590 	}
1591 
1592 	if (wrapOccurred) {
1593 		SetScrollBars();
1594 		SetTopLine(std::clamp<Sci::Line>(goodTopLine, 0, MaxScrollPos()));
1595 		SetVerticalScrollPos();
1596 	}
1597 
1598 	return wrapOccurred;
1599 }
1600 
LinesJoin()1601 void Editor::LinesJoin() {
1602 	if (!RangeContainsProtected(targetRange.start.Position(), targetRange.end.Position())) {
1603 		UndoGroup ug(pdoc);
1604 		bool prevNonWS = true;
1605 		for (Sci::Position pos = targetRange.start.Position(); pos < targetRange.end.Position(); pos++) {
1606 			if (pdoc->IsPositionInLineEnd(pos)) {
1607 				targetRange.end.Add(-pdoc->LenChar(pos));
1608 				pdoc->DelChar(pos);
1609 				if (prevNonWS) {
1610 					// Ensure at least one space separating previous lines
1611 					const Sci::Position lengthInserted = pdoc->InsertString(pos, " ", 1);
1612 					targetRange.end.Add(lengthInserted);
1613 				}
1614 			} else {
1615 				prevNonWS = pdoc->CharAt(pos) != ' ';
1616 			}
1617 		}
1618 	}
1619 }
1620 
StringFromEOLMode(int eolMode)1621 const char *Editor::StringFromEOLMode(int eolMode) noexcept {
1622 	if (eolMode == SC_EOL_CRLF) {
1623 		return "\r\n";
1624 	} else if (eolMode == SC_EOL_CR) {
1625 		return "\r";
1626 	} else {
1627 		return "\n";
1628 	}
1629 }
1630 
LinesSplit(int pixelWidth)1631 void Editor::LinesSplit(int pixelWidth) {
1632 	if (!RangeContainsProtected(targetRange.start.Position(), targetRange.end.Position())) {
1633 		if (pixelWidth == 0) {
1634 			const PRectangle rcText = GetTextRectangle();
1635 			pixelWidth = static_cast<int>(rcText.Width());
1636 		}
1637 		const Sci::Line lineStart = pdoc->SciLineFromPosition(targetRange.start.Position());
1638 		Sci::Line lineEnd = pdoc->SciLineFromPosition(targetRange.end.Position());
1639 		const char *eol = StringFromEOLMode(pdoc->eolMode);
1640 		UndoGroup ug(pdoc);
1641 		for (Sci::Line line = lineStart; line <= lineEnd; line++) {
1642 			AutoSurface surface(this);
1643 			AutoLineLayout ll(view.llc, view.RetrieveLineLayout(line, *this));
1644 			if (surface && ll) {
1645 				const Sci::Position posLineStart = pdoc->LineStart(line);
1646 				view.LayoutLine(*this, line, surface, vs, ll, pixelWidth);
1647 				Sci::Position lengthInsertedTotal = 0;
1648 				for (int subLine = 1; subLine < ll->lines; subLine++) {
1649 					const Sci::Position lengthInserted = pdoc->InsertString(
1650 						posLineStart + lengthInsertedTotal + ll->LineStart(subLine),
1651 						eol, strlen(eol));
1652 					targetRange.end.Add(lengthInserted);
1653 					lengthInsertedTotal += lengthInserted;
1654 				}
1655 			}
1656 			lineEnd = pdoc->SciLineFromPosition(targetRange.end.Position());
1657 		}
1658 	}
1659 }
1660 
PaintSelMargin(Surface * surfaceWindow,const PRectangle & rc)1661 void Editor::PaintSelMargin(Surface *surfaceWindow, const PRectangle &rc) {
1662 	if (vs.fixedColumnWidth == 0)
1663 		return;
1664 
1665 	AllocateGraphics();
1666 	RefreshStyleData();
1667 	RefreshPixMaps(surfaceWindow);
1668 
1669 	// On GTK+ with Ubuntu overlay scroll bars, the surface may have been finished
1670 	// at this point. The Initialised call checks for this case and sets the status
1671 	// to be bad which avoids crashes in following calls.
1672 	if (!surfaceWindow->Initialised()) {
1673 		return;
1674 	}
1675 
1676 	PRectangle rcMargin = GetClientRectangle();
1677 	const Point ptOrigin = GetVisibleOriginInMain();
1678 	rcMargin.Move(0, -ptOrigin.y);
1679 	rcMargin.left = 0;
1680 	rcMargin.right = static_cast<XYPOSITION>(vs.fixedColumnWidth);
1681 
1682 	if (!rc.Intersects(rcMargin))
1683 		return;
1684 
1685 	Surface *surface;
1686 	if (view.bufferedDraw) {
1687 		surface = marginView.pixmapSelMargin.get();
1688 	} else {
1689 		surface = surfaceWindow;
1690 	}
1691 
1692 	// Clip vertically to paint area to avoid drawing line numbers
1693 	if (rcMargin.bottom > rc.bottom)
1694 		rcMargin.bottom = rc.bottom;
1695 	if (rcMargin.top < rc.top)
1696 		rcMargin.top = rc.top;
1697 
1698 	marginView.PaintMargin(surface, topLine, rc, rcMargin, *this, vs);
1699 
1700 	if (view.bufferedDraw) {
1701 		surfaceWindow->Copy(rcMargin, Point(rcMargin.left, rcMargin.top), *marginView.pixmapSelMargin);
1702 	}
1703 }
1704 
RefreshPixMaps(Surface * surfaceWindow)1705 void Editor::RefreshPixMaps(Surface *surfaceWindow) {
1706 	view.RefreshPixMaps(surfaceWindow, wMain.GetID(), vs);
1707 	marginView.RefreshPixMaps(surfaceWindow, wMain.GetID(), vs);
1708 	if (view.bufferedDraw) {
1709 		const PRectangle rcClient = GetClientRectangle();
1710 		if (!view.pixmapLine->Initialised()) {
1711 
1712 			view.pixmapLine->InitPixMap(static_cast<int>(rcClient.Width()), vs.lineHeight,
1713 			        surfaceWindow, wMain.GetID());
1714 		}
1715 		if (!marginView.pixmapSelMargin->Initialised()) {
1716 			marginView.pixmapSelMargin->InitPixMap(vs.fixedColumnWidth,
1717 				static_cast<int>(rcClient.Height()), surfaceWindow, wMain.GetID());
1718 		}
1719 	}
1720 }
1721 
Paint(Surface * surfaceWindow,PRectangle rcArea)1722 void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
1723 	//Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
1724 	//	paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
1725 	AllocateGraphics();
1726 
1727 	RefreshStyleData();
1728 	if (paintState == paintAbandoned)
1729 		return;	// Scroll bars may have changed so need redraw
1730 	RefreshPixMaps(surfaceWindow);
1731 
1732 	paintAbandonedByStyling = false;
1733 
1734 	StyleAreaBounded(rcArea, false);
1735 
1736 	const PRectangle rcClient = GetClientRectangle();
1737 	//Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d)   %d\n",
1738 	//	rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
1739 
1740 	if (NotifyUpdateUI()) {
1741 		RefreshStyleData();
1742 		RefreshPixMaps(surfaceWindow);
1743 	}
1744 
1745 	// Wrap the visible lines if needed.
1746 	if (WrapLines(WrapScope::wsVisible)) {
1747 		// The wrapping process has changed the height of some lines so
1748 		// abandon this paint for a complete repaint.
1749 		if (AbandonPaint()) {
1750 			return;
1751 		}
1752 		RefreshPixMaps(surfaceWindow);	// In case pixmaps invalidated by scrollbar change
1753 	}
1754 
1755 	if (!marginView.pixmapSelPattern->Initialised()) {
1756 		// When Direct2D is used, pixmap creation may fail with D2DERR_RECREATE_TARGET so
1757 		// abandon this paint to avoid further failures.
1758 		// Main drawing surface and pixmaps should be recreated by next paint.
1759 		return;
1760 	}
1761 
1762 	if (!view.bufferedDraw)
1763 		surfaceWindow->SetClip(rcArea);
1764 
1765 	if (paintState != paintAbandoned) {
1766 		if (vs.marginInside) {
1767 			PaintSelMargin(surfaceWindow, rcArea);
1768 			PRectangle rcRightMargin = rcClient;
1769 			rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
1770 			if (rcArea.Intersects(rcRightMargin)) {
1771 				surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back);
1772 			}
1773 		} else { // Else separate view so separate paint event but leftMargin included to allow overlap
1774 			PRectangle rcLeftMargin = rcArea;
1775 			rcLeftMargin.left = 0;
1776 			rcLeftMargin.right = rcLeftMargin.left + vs.leftMarginWidth;
1777 			if (rcArea.Intersects(rcLeftMargin)) {
1778 				surfaceWindow->FillRectangle(rcLeftMargin, vs.styles[STYLE_DEFAULT].back);
1779 			}
1780 		}
1781 	}
1782 
1783 	if (paintState == paintAbandoned) {
1784 		// Either styling or NotifyUpdateUI noticed that painting is needed
1785 		// outside the current painting rectangle
1786 		//Platform::DebugPrintf("Abandoning paint\n");
1787 		if (Wrapping()) {
1788 			if (paintAbandonedByStyling) {
1789 				// Styling has spilled over a line end, such as occurs by starting a multiline
1790 				// comment. The width of subsequent text may have changed, so rewrap.
1791 				NeedWrapping(pcs->DocFromDisplay(topLine));
1792 			}
1793 		}
1794 		return;
1795 	}
1796 
1797 	view.PaintText(surfaceWindow, *this, rcArea, rcClient, vs);
1798 
1799 	if (horizontalScrollBarVisible && trackLineWidth && (view.lineWidthMaxSeen > scrollWidth)) {
1800 		scrollWidth = view.lineWidthMaxSeen;
1801 		if (!FineTickerRunning(tickWiden)) {
1802 			FineTickerStart(tickWiden, 50, 5);
1803 		}
1804 	}
1805 
1806 	NotifyPainted();
1807 }
1808 
1809 // This is mostly copied from the Paint method but with some things omitted
1810 // such as the margin markers, line numbers, selection and caret
1811 // Should be merged back into a combined Draw method.
FormatRange(bool draw,const Sci_RangeToFormat * pfr)1812 Sci::Position Editor::FormatRange(bool draw, const Sci_RangeToFormat *pfr) {
1813 	if (!pfr)
1814 		return 0;
1815 
1816 	AutoSurface surface(pfr->hdc, this, SC_TECHNOLOGY_DEFAULT);
1817 	if (!surface)
1818 		return 0;
1819 	AutoSurface surfaceMeasure(pfr->hdcTarget, this, SC_TECHNOLOGY_DEFAULT);
1820 	if (!surfaceMeasure) {
1821 		return 0;
1822 	}
1823 	return view.FormatRange(draw, pfr, surface, surfaceMeasure, *this, vs);
1824 }
1825 
TextWidth(int style,const char * text)1826 int Editor::TextWidth(int style, const char *text) {
1827 	RefreshStyleData();
1828 	AutoSurface surface(this);
1829 	if (surface) {
1830 		return static_cast<int>(surface->WidthText(vs.styles[style].font, text));
1831 	} else {
1832 		return 1;
1833 	}
1834 }
1835 
1836 // Empty method is overridden on GTK+ to show / hide scrollbars
ReconfigureScrollBars()1837 void Editor::ReconfigureScrollBars() {}
1838 
SetScrollBars()1839 void Editor::SetScrollBars() {
1840 	RefreshStyleData();
1841 
1842 	const Sci::Line nMax = MaxScrollPos();
1843 	const Sci::Line nPage = LinesOnScreen();
1844 	const bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
1845 	if (modified) {
1846 		DwellEnd(true);
1847 	}
1848 
1849 	// TODO: ensure always showing as many lines as possible
1850 	// May not be, if, for example, window made larger
1851 	if (topLine > MaxScrollPos()) {
1852 		SetTopLine(std::clamp<Sci::Line>(topLine, 0, MaxScrollPos()));
1853 		SetVerticalScrollPos();
1854 		Redraw();
1855 	}
1856 	if (modified) {
1857 		if (!AbandonPaint())
1858 			Redraw();
1859 	}
1860 	//Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
1861 }
1862 
ChangeSize()1863 void Editor::ChangeSize() {
1864 	DropGraphics(false);
1865 	SetScrollBars();
1866 	if (Wrapping()) {
1867 		PRectangle rcTextArea = GetClientRectangle();
1868 		rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
1869 		rcTextArea.right -= vs.rightMarginWidth;
1870 		if (wrapWidth != rcTextArea.Width()) {
1871 			NeedWrapping();
1872 			Redraw();
1873 		}
1874 	}
1875 }
1876 
RealizeVirtualSpace(Sci::Position position,Sci::Position virtualSpace)1877 Sci::Position Editor::RealizeVirtualSpace(Sci::Position position, Sci::Position virtualSpace) {
1878 	if (virtualSpace > 0) {
1879 		const Sci::Line line = pdoc->SciLineFromPosition(position);
1880 		const Sci::Position indent = pdoc->GetLineIndentPosition(line);
1881 		if (indent == position) {
1882 			return pdoc->SetLineIndentation(line, pdoc->GetLineIndentation(line) + virtualSpace);
1883 		} else {
1884 			std::string spaceText(virtualSpace, ' ');
1885 			const Sci::Position lengthInserted = pdoc->InsertString(position, spaceText.c_str(), virtualSpace);
1886 			position += lengthInserted;
1887 		}
1888 	}
1889 	return position;
1890 }
1891 
RealizeVirtualSpace(const SelectionPosition & position)1892 SelectionPosition Editor::RealizeVirtualSpace(const SelectionPosition &position) {
1893 	// Return the new position with no virtual space
1894 	return SelectionPosition(RealizeVirtualSpace(position.Position(), position.VirtualSpace()));
1895 }
1896 
AddChar(char ch)1897 void Editor::AddChar(char ch) {
1898 	char s[2];
1899 	s[0] = ch;
1900 	s[1] = '\0';
1901 	InsertCharacter(std::string_view(s, 1), CharacterSource::directInput);
1902 }
1903 
FilterSelections()1904 void Editor::FilterSelections() {
1905 	if (!additionalSelectionTyping && (sel.Count() > 1)) {
1906 		InvalidateWholeSelection();
1907 		sel.DropAdditionalRanges();
1908 	}
1909 }
1910 
1911 // InsertCharacter inserts a character encoded in document code page.
InsertCharacter(std::string_view sv,CharacterSource charSource)1912 void Editor::InsertCharacter(std::string_view sv, CharacterSource charSource) {
1913 	if (sv.empty()) {
1914 		return;
1915 	}
1916 	FilterSelections();
1917 	{
1918 		UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
1919 
1920 		// Vector elements point into selection in order to change selection.
1921 		std::vector<SelectionRange *> selPtrs;
1922 		for (size_t r = 0; r < sel.Count(); r++) {
1923 			selPtrs.push_back(&sel.Range(r));
1924 		}
1925 		// Order selections by position in document.
1926 		std::sort(selPtrs.begin(), selPtrs.end(),
1927 			[](const SelectionRange *a, const SelectionRange *b) noexcept {return *a < *b;});
1928 
1929 		// Loop in reverse to avoid disturbing positions of selections yet to be processed.
1930 		for (std::vector<SelectionRange *>::reverse_iterator rit = selPtrs.rbegin();
1931 			rit != selPtrs.rend(); ++rit) {
1932 			SelectionRange *currentSel = *rit;
1933 			if (!RangeContainsProtected(currentSel->Start().Position(),
1934 				currentSel->End().Position())) {
1935 				Sci::Position positionInsert = currentSel->Start().Position();
1936 				if (!currentSel->Empty()) {
1937 					if (currentSel->Length()) {
1938 						pdoc->DeleteChars(positionInsert, currentSel->Length());
1939 						currentSel->ClearVirtualSpace();
1940 					} else {
1941 						// Range is all virtual so collapse to start of virtual space
1942 						currentSel->MinimizeVirtualSpace();
1943 					}
1944 				} else if (inOverstrike) {
1945 					if (positionInsert < pdoc->Length()) {
1946 						if (!pdoc->IsPositionInLineEnd(positionInsert)) {
1947 							pdoc->DelChar(positionInsert);
1948 							currentSel->ClearVirtualSpace();
1949 						}
1950 					}
1951 				}
1952 				positionInsert = RealizeVirtualSpace(positionInsert, currentSel->caret.VirtualSpace());
1953 				const Sci::Position lengthInserted = pdoc->InsertString(positionInsert, sv.data(), sv.length());
1954 				if (lengthInserted > 0) {
1955 					currentSel->caret.SetPosition(positionInsert + lengthInserted);
1956 					currentSel->anchor.SetPosition(positionInsert + lengthInserted);
1957 				}
1958 				currentSel->ClearVirtualSpace();
1959 				// If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
1960 				if (Wrapping()) {
1961 					AutoSurface surface(this);
1962 					if (surface) {
1963 						if (WrapOneLine(surface, pdoc->SciLineFromPosition(positionInsert))) {
1964 							SetScrollBars();
1965 							SetVerticalScrollPos();
1966 							Redraw();
1967 						}
1968 					}
1969 				}
1970 			}
1971 		}
1972 	}
1973 	if (Wrapping()) {
1974 		SetScrollBars();
1975 	}
1976 	ThinRectangularRange();
1977 	// If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
1978 	EnsureCaretVisible();
1979 	// Avoid blinking during rapid typing:
1980 	ShowCaretAtCurrentPosition();
1981 	if ((caretSticky == SC_CARETSTICKY_OFF) ||
1982 		((caretSticky == SC_CARETSTICKY_WHITESPACE) && !IsAllSpacesOrTabs(sv))) {
1983 		SetLastXChosen();
1984 	}
1985 
1986 	int ch = static_cast<unsigned char>(sv[0]);
1987 	if (pdoc->dbcsCodePage != SC_CP_UTF8) {
1988 		if (sv.length() > 1) {
1989 			// DBCS code page or DBCS font character set.
1990 			ch = (ch << 8) | static_cast<unsigned char>(sv[1]);
1991 		}
1992 	} else {
1993 		if ((ch < 0xC0) || (1 == sv.length())) {
1994 			// Handles UTF-8 characters between 0x01 and 0x7F and single byte
1995 			// characters when not in UTF-8 mode.
1996 			// Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
1997 			// characters representing themselves.
1998 		} else {
1999 			unsigned int utf32[1] = { 0 };
2000 			UTF32FromUTF8(sv, utf32, std::size(utf32));
2001 			ch = utf32[0];
2002 		}
2003 	}
2004 	NotifyChar(ch, charSource);
2005 
2006 	if (recordingMacro && charSource != CharacterSource::tentativeInput) {
2007 		std::string copy(sv); // ensure NUL-terminated
2008 		NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(copy.data()));
2009 	}
2010 }
2011 
ClearBeforeTentativeStart()2012 void Editor::ClearBeforeTentativeStart() {
2013 	// Make positions for the first composition string.
2014 	FilterSelections();
2015 	UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
2016 	for (size_t r = 0; r<sel.Count(); r++) {
2017 		if (!RangeContainsProtected(sel.Range(r).Start().Position(),
2018 			sel.Range(r).End().Position())) {
2019 			const Sci::Position positionInsert = sel.Range(r).Start().Position();
2020 			if (!sel.Range(r).Empty()) {
2021 				if (sel.Range(r).Length()) {
2022 					pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
2023 					sel.Range(r).ClearVirtualSpace();
2024 				} else {
2025 					// Range is all virtual so collapse to start of virtual space
2026 					sel.Range(r).MinimizeVirtualSpace();
2027 				}
2028 			}
2029 			RealizeVirtualSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
2030 			sel.Range(r).ClearVirtualSpace();
2031 		}
2032 	}
2033 }
2034 
InsertPaste(const char * text,Sci::Position len)2035 void Editor::InsertPaste(const char *text, Sci::Position len) {
2036 	if (multiPasteMode == SC_MULTIPASTE_ONCE) {
2037 		SelectionPosition selStart = sel.Start();
2038 		selStart = RealizeVirtualSpace(selStart);
2039 		const Sci::Position lengthInserted = pdoc->InsertString(selStart.Position(), text, len);
2040 		if (lengthInserted > 0) {
2041 			SetEmptySelection(selStart.Position() + lengthInserted);
2042 		}
2043 	} else {
2044 		// SC_MULTIPASTE_EACH
2045 		for (size_t r=0; r<sel.Count(); r++) {
2046 			if (!RangeContainsProtected(sel.Range(r).Start().Position(),
2047 				sel.Range(r).End().Position())) {
2048 				Sci::Position positionInsert = sel.Range(r).Start().Position();
2049 				if (!sel.Range(r).Empty()) {
2050 					if (sel.Range(r).Length()) {
2051 						pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
2052 						sel.Range(r).ClearVirtualSpace();
2053 					} else {
2054 						// Range is all virtual so collapse to start of virtual space
2055 						sel.Range(r).MinimizeVirtualSpace();
2056 					}
2057 				}
2058 				positionInsert = RealizeVirtualSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
2059 				const Sci::Position lengthInserted = pdoc->InsertString(positionInsert, text, len);
2060 				if (lengthInserted > 0) {
2061 					sel.Range(r).caret.SetPosition(positionInsert + lengthInserted);
2062 					sel.Range(r).anchor.SetPosition(positionInsert + lengthInserted);
2063 				}
2064 				sel.Range(r).ClearVirtualSpace();
2065 			}
2066 		}
2067 	}
2068 }
2069 
InsertPasteShape(const char * text,Sci::Position len,PasteShape shape)2070 void Editor::InsertPasteShape(const char *text, Sci::Position len, PasteShape shape) {
2071 	std::string convertedText;
2072 	if (convertPastes) {
2073 		// Convert line endings of the paste into our local line-endings mode
2074 		convertedText = Document::TransformLineEnds(text, len, pdoc->eolMode);
2075 		len = convertedText.length();
2076 		text = convertedText.c_str();
2077 	}
2078 	if (shape == pasteRectangular) {
2079 		PasteRectangular(sel.Start(), text, len);
2080 	} else {
2081 		if (shape == pasteLine) {
2082 			const Sci::Position insertPos =
2083 				pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret()));
2084 			Sci::Position lengthInserted = pdoc->InsertString(insertPos, text, len);
2085 			// add the newline if necessary
2086 			if ((len > 0) && (text[len - 1] != '\n' && text[len - 1] != '\r')) {
2087 				const char *endline = StringFromEOLMode(pdoc->eolMode);
2088 				const Sci::Position length = strlen(endline);
2089 				lengthInserted += pdoc->InsertString(insertPos + lengthInserted, endline, length);
2090 			}
2091 			if (sel.MainCaret() == insertPos) {
2092 				SetEmptySelection(sel.MainCaret() + lengthInserted);
2093 			}
2094 		} else {
2095 			InsertPaste(text, len);
2096 		}
2097 	}
2098 }
2099 
ClearSelection(bool retainMultipleSelections)2100 void Editor::ClearSelection(bool retainMultipleSelections) {
2101 	if (!sel.IsRectangular() && !retainMultipleSelections)
2102 		FilterSelections();
2103 	UndoGroup ug(pdoc);
2104 	for (size_t r=0; r<sel.Count(); r++) {
2105 		if (!sel.Range(r).Empty()) {
2106 			if (!RangeContainsProtected(sel.Range(r).Start().Position(),
2107 				sel.Range(r).End().Position())) {
2108 				pdoc->DeleteChars(sel.Range(r).Start().Position(),
2109 					sel.Range(r).Length());
2110 				sel.Range(r) = SelectionRange(sel.Range(r).Start());
2111 			}
2112 		}
2113 	}
2114 	ThinRectangularRange();
2115 	sel.RemoveDuplicates();
2116 	ClaimSelection();
2117 	SetHoverIndicatorPosition(sel.MainCaret());
2118 }
2119 
ClearAll()2120 void Editor::ClearAll() {
2121 	{
2122 		UndoGroup ug(pdoc);
2123 		if (0 != pdoc->Length()) {
2124 			pdoc->DeleteChars(0, pdoc->Length());
2125 		}
2126 		if (!pdoc->IsReadOnly()) {
2127 			pcs->Clear();
2128 			pdoc->AnnotationClearAll();
2129 			pdoc->MarginClearAll();
2130 		}
2131 	}
2132 
2133 	view.ClearAllTabstops();
2134 
2135 	sel.Clear();
2136 	SetTopLine(0);
2137 	SetVerticalScrollPos();
2138 	InvalidateStyleRedraw();
2139 }
2140 
ClearDocumentStyle()2141 void Editor::ClearDocumentStyle() {
2142 	pdoc->decorations->DeleteLexerDecorations();
2143 	pdoc->StartStyling(0);
2144 	pdoc->SetStyleFor(pdoc->Length(), 0);
2145 	pcs->ShowAll();
2146 	SetAnnotationHeights(0, pdoc->LinesTotal());
2147 	pdoc->ClearLevels();
2148 }
2149 
CopyAllowLine()2150 void Editor::CopyAllowLine() {
2151 	SelectionText selectedText;
2152 	CopySelectionRange(&selectedText, true);
2153 	CopyToClipboard(selectedText);
2154 }
2155 
Cut()2156 void Editor::Cut() {
2157 	pdoc->CheckReadOnly();
2158 	if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
2159 		Copy();
2160 		ClearSelection();
2161 	}
2162 }
2163 
PasteRectangular(SelectionPosition pos,const char * ptr,Sci::Position len)2164 void Editor::PasteRectangular(SelectionPosition pos, const char *ptr, Sci::Position len) {
2165 	if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
2166 		return;
2167 	}
2168 	sel.Clear();
2169 	sel.RangeMain() = SelectionRange(pos);
2170 	Sci::Line line = pdoc->SciLineFromPosition(sel.MainCaret());
2171 	UndoGroup ug(pdoc);
2172 	sel.RangeMain().caret = RealizeVirtualSpace(sel.RangeMain().caret);
2173 	const int xInsert = XFromPosition(sel.RangeMain().caret);
2174 	bool prevCr = false;
2175 	while ((len > 0) && IsEOLChar(ptr[len-1]))
2176 		len--;
2177 	for (Sci::Position i = 0; i < len; i++) {
2178 		if (IsEOLChar(ptr[i])) {
2179 			if ((ptr[i] == '\r') || (!prevCr))
2180 				line++;
2181 			if (line >= pdoc->LinesTotal()) {
2182 				if (pdoc->eolMode != SC_EOL_LF)
2183 					pdoc->InsertString(pdoc->Length(), "\r", 1);
2184 				if (pdoc->eolMode != SC_EOL_CR)
2185 					pdoc->InsertString(pdoc->Length(), "\n", 1);
2186 			}
2187 			// Pad the end of lines with spaces if required
2188 			sel.RangeMain().caret.SetPosition(PositionFromLineX(line, xInsert));
2189 			if ((XFromPosition(sel.RangeMain().caret) < xInsert) && (i + 1 < len)) {
2190 				while (XFromPosition(sel.RangeMain().caret) < xInsert) {
2191 					assert(pdoc);
2192 					const Sci::Position lengthInserted = pdoc->InsertString(sel.MainCaret(), " ", 1);
2193 					sel.RangeMain().caret.Add(lengthInserted);
2194 				}
2195 			}
2196 			prevCr = ptr[i] == '\r';
2197 		} else {
2198 			const Sci::Position lengthInserted = pdoc->InsertString(sel.MainCaret(), ptr + i, 1);
2199 			sel.RangeMain().caret.Add(lengthInserted);
2200 			prevCr = false;
2201 		}
2202 	}
2203 	SetEmptySelection(pos);
2204 }
2205 
CanPaste()2206 bool Editor::CanPaste() {
2207 	return !pdoc->IsReadOnly() && !SelectionContainsProtected();
2208 }
2209 
Clear()2210 void Editor::Clear() {
2211 	// If multiple selections, don't delete EOLS
2212 	if (sel.Empty()) {
2213 		bool singleVirtual = false;
2214 		if ((sel.Count() == 1) &&
2215 			!RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1) &&
2216 			sel.RangeMain().Start().VirtualSpace()) {
2217 			singleVirtual = true;
2218 		}
2219 		UndoGroup ug(pdoc, (sel.Count() > 1) || singleVirtual);
2220 		for (size_t r=0; r<sel.Count(); r++) {
2221 			if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
2222 				if (sel.Range(r).Start().VirtualSpace()) {
2223 					if (sel.Range(r).anchor < sel.Range(r).caret)
2224 						sel.Range(r) = SelectionRange(RealizeVirtualSpace(sel.Range(r).anchor.Position(), sel.Range(r).anchor.VirtualSpace()));
2225 					else
2226 						sel.Range(r) = SelectionRange(RealizeVirtualSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace()));
2227 				}
2228 				if ((sel.Count() == 1) || !pdoc->IsPositionInLineEnd(sel.Range(r).caret.Position())) {
2229 					pdoc->DelChar(sel.Range(r).caret.Position());
2230 					sel.Range(r).ClearVirtualSpace();
2231 				}  // else multiple selection so don't eat line ends
2232 			} else {
2233 				sel.Range(r).ClearVirtualSpace();
2234 			}
2235 		}
2236 	} else {
2237 		ClearSelection();
2238 	}
2239 	sel.RemoveDuplicates();
2240 	ShowCaretAtCurrentPosition();		// Avoid blinking
2241 }
2242 
SelectAll()2243 void Editor::SelectAll() {
2244 	sel.Clear();
2245 	SetSelection(0, pdoc->Length());
2246 	Redraw();
2247 }
2248 
Undo()2249 void Editor::Undo() {
2250 	if (pdoc->CanUndo()) {
2251 		InvalidateCaret();
2252 		const Sci::Position newPos = pdoc->Undo();
2253 		if (newPos >= 0)
2254 			SetEmptySelection(newPos);
2255 		EnsureCaretVisible();
2256 	}
2257 }
2258 
Redo()2259 void Editor::Redo() {
2260 	if (pdoc->CanRedo()) {
2261 		const Sci::Position newPos = pdoc->Redo();
2262 		if (newPos >= 0)
2263 			SetEmptySelection(newPos);
2264 		EnsureCaretVisible();
2265 	}
2266 }
2267 
DelCharBack(bool allowLineStartDeletion)2268 void Editor::DelCharBack(bool allowLineStartDeletion) {
2269 	RefreshStyleData();
2270 	if (!sel.IsRectangular())
2271 		FilterSelections();
2272 	if (sel.IsRectangular())
2273 		allowLineStartDeletion = false;
2274 	UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty());
2275 	if (sel.Empty()) {
2276 		for (size_t r=0; r<sel.Count(); r++) {
2277 			if (!RangeContainsProtected(sel.Range(r).caret.Position() - 1, sel.Range(r).caret.Position())) {
2278 				if (sel.Range(r).caret.VirtualSpace()) {
2279 					sel.Range(r).caret.SetVirtualSpace(sel.Range(r).caret.VirtualSpace() - 1);
2280 					sel.Range(r).anchor.SetVirtualSpace(sel.Range(r).caret.VirtualSpace());
2281 				} else {
2282 					const Sci::Line lineCurrentPos =
2283 						pdoc->SciLineFromPosition(sel.Range(r).caret.Position());
2284 					if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.Range(r).caret.Position())) {
2285 						if (pdoc->GetColumn(sel.Range(r).caret.Position()) <= pdoc->GetLineIndentation(lineCurrentPos) &&
2286 								pdoc->GetColumn(sel.Range(r).caret.Position()) > 0 && pdoc->backspaceUnindents) {
2287 							UndoGroup ugInner(pdoc, !ug.Needed());
2288 							const int indentation = pdoc->GetLineIndentation(lineCurrentPos);
2289 							const int indentationStep = pdoc->IndentSize();
2290 							int indentationChange = indentation % indentationStep;
2291 							if (indentationChange == 0)
2292 								indentationChange = indentationStep;
2293 							const Sci::Position posSelect = pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationChange);
2294 							// SetEmptySelection
2295 							sel.Range(r) = SelectionRange(posSelect);
2296 						} else {
2297 							pdoc->DelCharBack(sel.Range(r).caret.Position());
2298 						}
2299 					}
2300 				}
2301 			} else {
2302 				sel.Range(r).ClearVirtualSpace();
2303 			}
2304 		}
2305 		ThinRectangularRange();
2306 	} else {
2307 		ClearSelection();
2308 	}
2309 	sel.RemoveDuplicates();
2310 	ContainerNeedsUpdate(SC_UPDATE_SELECTION);
2311 	// Avoid blinking during rapid typing:
2312 	ShowCaretAtCurrentPosition();
2313 }
2314 
ModifierFlags(bool shift,bool ctrl,bool alt,bool meta,bool super)2315 int Editor::ModifierFlags(bool shift, bool ctrl, bool alt, bool meta, bool super) noexcept {
2316 	return
2317 		(shift ? SCI_SHIFT : 0) |
2318 		(ctrl ? SCI_CTRL : 0) |
2319 		(alt ? SCI_ALT : 0) |
2320 		(meta ? SCI_META : 0) |
2321 		(super ? SCI_SUPER : 0);
2322 }
2323 
NotifyFocus(bool focus)2324 void Editor::NotifyFocus(bool focus) {
2325 	SCNotification scn = {};
2326 	scn.nmhdr.code = focus ? SCN_FOCUSIN : SCN_FOCUSOUT;
2327 	NotifyParent(scn);
2328 }
2329 
SetCtrlID(int identifier)2330 void Editor::SetCtrlID(int identifier) {
2331 	ctrlID = identifier;
2332 }
2333 
NotifyStyleToNeeded(Sci::Position endStyleNeeded)2334 void Editor::NotifyStyleToNeeded(Sci::Position endStyleNeeded) {
2335 	SCNotification scn = {};
2336 	scn.nmhdr.code = SCN_STYLENEEDED;
2337 	scn.position = endStyleNeeded;
2338 	NotifyParent(scn);
2339 }
2340 
NotifyStyleNeeded(Document *,void *,Sci::Position endStyleNeeded)2341 void Editor::NotifyStyleNeeded(Document *, void *, Sci::Position endStyleNeeded) {
2342 	NotifyStyleToNeeded(endStyleNeeded);
2343 }
2344 
NotifyLexerChanged(Document *,void *)2345 void Editor::NotifyLexerChanged(Document *, void *) {
2346 }
2347 
NotifyErrorOccurred(Document *,void *,int status)2348 void Editor::NotifyErrorOccurred(Document *, void *, int status) {
2349 	errorStatus = status;
2350 }
2351 
NotifyChar(int ch,CharacterSource charSource)2352 void Editor::NotifyChar(int ch, CharacterSource charSource) {
2353 	SCNotification scn = {};
2354 	scn.nmhdr.code = SCN_CHARADDED;
2355 	scn.ch = ch;
2356 	scn.characterSource = static_cast<int>(charSource);
2357 	NotifyParent(scn);
2358 }
2359 
NotifySavePoint(bool isSavePoint)2360 void Editor::NotifySavePoint(bool isSavePoint) {
2361 	SCNotification scn = {};
2362 	if (isSavePoint) {
2363 		scn.nmhdr.code = SCN_SAVEPOINTREACHED;
2364 	} else {
2365 		scn.nmhdr.code = SCN_SAVEPOINTLEFT;
2366 	}
2367 	NotifyParent(scn);
2368 }
2369 
NotifyModifyAttempt()2370 void Editor::NotifyModifyAttempt() {
2371 	SCNotification scn = {};
2372 	scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
2373 	NotifyParent(scn);
2374 }
2375 
NotifyDoubleClick(Point pt,int modifiers)2376 void Editor::NotifyDoubleClick(Point pt, int modifiers) {
2377 	SCNotification scn = {};
2378 	scn.nmhdr.code = SCN_DOUBLECLICK;
2379 	scn.line = LineFromLocation(pt);
2380 	scn.position = PositionFromLocation(pt, true);
2381 	scn.modifiers = modifiers;
2382 	NotifyParent(scn);
2383 }
2384 
NotifyHotSpotDoubleClicked(Sci::Position position,int modifiers)2385 void Editor::NotifyHotSpotDoubleClicked(Sci::Position position, int modifiers) {
2386 	SCNotification scn = {};
2387 	scn.nmhdr.code = SCN_HOTSPOTDOUBLECLICK;
2388 	scn.position = position;
2389 	scn.modifiers = modifiers;
2390 	NotifyParent(scn);
2391 }
2392 
NotifyHotSpotClicked(Sci::Position position,int modifiers)2393 void Editor::NotifyHotSpotClicked(Sci::Position position, int modifiers) {
2394 	SCNotification scn = {};
2395 	scn.nmhdr.code = SCN_HOTSPOTCLICK;
2396 	scn.position = position;
2397 	scn.modifiers = modifiers;
2398 	NotifyParent(scn);
2399 }
2400 
NotifyHotSpotReleaseClick(Sci::Position position,int modifiers)2401 void Editor::NotifyHotSpotReleaseClick(Sci::Position position, int modifiers) {
2402 	SCNotification scn = {};
2403 	scn.nmhdr.code = SCN_HOTSPOTRELEASECLICK;
2404 	scn.position = position;
2405 	scn.modifiers = modifiers;
2406 	NotifyParent(scn);
2407 }
2408 
NotifyUpdateUI()2409 bool Editor::NotifyUpdateUI() {
2410 	if (needUpdateUI) {
2411 		SCNotification scn = {};
2412 		scn.nmhdr.code = SCN_UPDATEUI;
2413 		scn.updated = needUpdateUI;
2414 		NotifyParent(scn);
2415 		needUpdateUI = 0;
2416 		return true;
2417 	}
2418 	return false;
2419 }
2420 
NotifyPainted()2421 void Editor::NotifyPainted() {
2422 	SCNotification scn = {};
2423 	scn.nmhdr.code = SCN_PAINTED;
2424 	NotifyParent(scn);
2425 }
2426 
NotifyIndicatorClick(bool click,Sci::Position position,int modifiers)2427 void Editor::NotifyIndicatorClick(bool click, Sci::Position position, int modifiers) {
2428 	const int mask = pdoc->decorations->AllOnFor(position);
2429 	if ((click && mask) || pdoc->decorations->ClickNotified()) {
2430 		SCNotification scn = {};
2431 		pdoc->decorations->SetClickNotified(click);
2432 		scn.nmhdr.code = click ? SCN_INDICATORCLICK : SCN_INDICATORRELEASE;
2433 		scn.modifiers = modifiers;
2434 		scn.position = position;
2435 		NotifyParent(scn);
2436 	}
2437 }
2438 
NotifyMarginClick(Point pt,int modifiers)2439 bool Editor::NotifyMarginClick(Point pt, int modifiers) {
2440 	const int marginClicked = vs.MarginFromLocation(pt);
2441 	if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
2442 		const Sci::Position position = pdoc->LineStart(LineFromLocation(pt));
2443 		if ((vs.ms[marginClicked].mask & SC_MASK_FOLDERS) && (foldAutomatic & SC_AUTOMATICFOLD_CLICK)) {
2444 			const bool ctrl = (modifiers & SCI_CTRL) != 0;
2445 			const bool shift = (modifiers & SCI_SHIFT) != 0;
2446 			const Sci::Line lineClick = pdoc->SciLineFromPosition(position);
2447 			if (shift && ctrl) {
2448 				FoldAll(SC_FOLDACTION_TOGGLE);
2449 			} else {
2450 				const int levelClick = pdoc->GetLevel(lineClick);
2451 				if (levelClick & SC_FOLDLEVELHEADERFLAG) {
2452 					if (shift) {
2453 						// Ensure all children visible
2454 						FoldExpand(lineClick, SC_FOLDACTION_EXPAND, levelClick);
2455 					} else if (ctrl) {
2456 						FoldExpand(lineClick, SC_FOLDACTION_TOGGLE, levelClick);
2457 					} else {
2458 						// Toggle this line
2459 						FoldLine(lineClick, SC_FOLDACTION_TOGGLE);
2460 					}
2461 				}
2462 			}
2463 			return true;
2464 		}
2465 		SCNotification scn = {};
2466 		scn.nmhdr.code = SCN_MARGINCLICK;
2467 		scn.modifiers = modifiers;
2468 		scn.position = position;
2469 		scn.margin = marginClicked;
2470 		NotifyParent(scn);
2471 		return true;
2472 	} else {
2473 		return false;
2474 	}
2475 }
2476 
NotifyMarginRightClick(Point pt,int modifiers)2477 bool Editor::NotifyMarginRightClick(Point pt, int modifiers) {
2478 	const int marginRightClicked = vs.MarginFromLocation(pt);
2479 	if ((marginRightClicked >= 0) && vs.ms[marginRightClicked].sensitive) {
2480 		const Sci::Position position = pdoc->LineStart(LineFromLocation(pt));
2481 		SCNotification scn = {};
2482 		scn.nmhdr.code = SCN_MARGINRIGHTCLICK;
2483 		scn.modifiers = modifiers;
2484 		scn.position = position;
2485 		scn.margin = marginRightClicked;
2486 		NotifyParent(scn);
2487 		return true;
2488 	} else {
2489 		return false;
2490 	}
2491 }
2492 
NotifyNeedShown(Sci::Position pos,Sci::Position len)2493 void Editor::NotifyNeedShown(Sci::Position pos, Sci::Position len) {
2494 	SCNotification scn = {};
2495 	scn.nmhdr.code = SCN_NEEDSHOWN;
2496 	scn.position = pos;
2497 	scn.length = len;
2498 	NotifyParent(scn);
2499 }
2500 
NotifyDwelling(Point pt,bool state)2501 void Editor::NotifyDwelling(Point pt, bool state) {
2502 	SCNotification scn = {};
2503 	scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
2504 	scn.position = PositionFromLocation(pt, true);
2505 	scn.x = static_cast<int>(pt.x + vs.ExternalMarginWidth());
2506 	scn.y = static_cast<int>(pt.y);
2507 	NotifyParent(scn);
2508 }
2509 
NotifyZoom()2510 void Editor::NotifyZoom() {
2511 	SCNotification scn = {};
2512 	scn.nmhdr.code = SCN_ZOOM;
2513 	NotifyParent(scn);
2514 }
2515 
2516 // Notifications from document
NotifyModifyAttempt(Document *,void *)2517 void Editor::NotifyModifyAttempt(Document *, void *) {
2518 	//Platform::DebugPrintf("** Modify Attempt\n");
2519 	NotifyModifyAttempt();
2520 }
2521 
NotifySavePoint(Document *,void *,bool atSavePoint)2522 void Editor::NotifySavePoint(Document *, void *, bool atSavePoint) {
2523 	//Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
2524 	NotifySavePoint(atSavePoint);
2525 }
2526 
CheckModificationForWrap(DocModification mh)2527 void Editor::CheckModificationForWrap(DocModification mh) {
2528 	if (mh.modificationType & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT)) {
2529 		view.llc.Invalidate(LineLayout::llCheckTextAndStyle);
2530 		const Sci::Line lineDoc = pdoc->SciLineFromPosition(mh.position);
2531 		const Sci::Line lines = std::max(static_cast<Sci::Line>(0), mh.linesAdded);
2532 		if (Wrapping()) {
2533 			NeedWrapping(lineDoc, lineDoc + lines + 1);
2534 		}
2535 		RefreshStyleData();
2536 		// Fix up annotation heights
2537 		SetAnnotationHeights(lineDoc, lineDoc + lines + 2);
2538 	}
2539 }
2540 
2541 namespace {
2542 
2543 // Move a position so it is still after the same character as before the insertion.
MovePositionForInsertion(Sci::Position position,Sci::Position startInsertion,Sci::Position length)2544 constexpr Sci::Position MovePositionForInsertion(Sci::Position position, Sci::Position startInsertion, Sci::Position length) noexcept {
2545 	if (position > startInsertion) {
2546 		return position + length;
2547 	}
2548 	return position;
2549 }
2550 
2551 // Move a position so it is still after the same character as before the deletion if that
2552 // character is still present else after the previous surviving character.
MovePositionForDeletion(Sci::Position position,Sci::Position startDeletion,Sci::Position length)2553 constexpr Sci::Position MovePositionForDeletion(Sci::Position position, Sci::Position startDeletion, Sci::Position length) noexcept {
2554 	if (position > startDeletion) {
2555 		const Sci::Position endDeletion = startDeletion + length;
2556 		if (position > endDeletion) {
2557 			return position - length;
2558 		} else {
2559 			return startDeletion;
2560 		}
2561 	} else {
2562 		return position;
2563 	}
2564 }
2565 
2566 }
2567 
NotifyModified(Document *,DocModification mh,void *)2568 void Editor::NotifyModified(Document *, DocModification mh, void *) {
2569 	ContainerNeedsUpdate(SC_UPDATE_CONTENT);
2570 	if (paintState == painting) {
2571 		CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
2572 	}
2573 	if (mh.modificationType & SC_MOD_CHANGELINESTATE) {
2574 		if (paintState == painting) {
2575 			CheckForChangeOutsidePaint(
2576 			    Range(pdoc->LineStart(mh.line),
2577 					pdoc->LineStart(mh.line + 1)));
2578 		} else {
2579 			// Could check that change is before last visible line.
2580 			Redraw();
2581 		}
2582 	}
2583 	if (mh.modificationType & SC_MOD_CHANGETABSTOPS) {
2584 		Redraw();
2585 	}
2586 	if (mh.modificationType & SC_MOD_LEXERSTATE) {
2587 		if (paintState == painting) {
2588 			CheckForChangeOutsidePaint(
2589 			    Range(mh.position, mh.position + mh.length));
2590 		} else {
2591 			Redraw();
2592 		}
2593 	}
2594 	if (mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) {
2595 		if (mh.modificationType & SC_MOD_CHANGESTYLE) {
2596 			pdoc->IncrementStyleClock();
2597 		}
2598 		if (paintState == notPainting) {
2599 			const Sci::Line lineDocTop = pcs->DocFromDisplay(topLine);
2600 			if (mh.position < pdoc->LineStart(lineDocTop)) {
2601 				// Styling performed before this view
2602 				Redraw();
2603 			} else {
2604 				InvalidateRange(mh.position, mh.position + mh.length);
2605 			}
2606 		}
2607 		if (mh.modificationType & SC_MOD_CHANGESTYLE) {
2608 			view.llc.Invalidate(LineLayout::llCheckTextAndStyle);
2609 		}
2610 	} else {
2611 		// Move selection and brace highlights
2612 		if (mh.modificationType & SC_MOD_INSERTTEXT) {
2613 			sel.MovePositions(true, mh.position, mh.length);
2614 			braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
2615 			braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
2616 		} else if (mh.modificationType & SC_MOD_DELETETEXT) {
2617 			sel.MovePositions(false, mh.position, mh.length);
2618 			braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
2619 			braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
2620 		}
2621 		if ((mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) && pcs->HiddenLines()) {
2622 			// Some lines are hidden so may need shown.
2623 			const Sci::Line lineOfPos = pdoc->SciLineFromPosition(mh.position);
2624 			Sci::Position endNeedShown = mh.position;
2625 			if (mh.modificationType & SC_MOD_BEFOREINSERT) {
2626 				if (pdoc->ContainsLineEnd(mh.text, mh.length) && (mh.position != pdoc->LineStart(lineOfPos)))
2627 					endNeedShown = pdoc->LineStart(lineOfPos+1);
2628 			} else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
2629 				// If the deletion includes any EOL then we extend the need shown area.
2630 				endNeedShown = mh.position + mh.length;
2631 				Sci::Line lineLast = pdoc->SciLineFromPosition(mh.position+mh.length);
2632 				for (Sci::Line line = lineOfPos + 1; line <= lineLast; line++) {
2633 					const Sci::Line lineMaxSubord = pdoc->GetLastChild(line, -1, -1);
2634 					if (lineLast < lineMaxSubord) {
2635 						lineLast = lineMaxSubord;
2636 						endNeedShown = pdoc->LineEnd(lineLast);
2637 					}
2638 				}
2639 			}
2640 			NeedShown(mh.position, endNeedShown - mh.position);
2641 		}
2642 		if (mh.linesAdded != 0) {
2643 			// Update contraction state for inserted and removed lines
2644 			// lineOfPos should be calculated in context of state before modification, shouldn't it
2645 			Sci::Line lineOfPos = pdoc->SciLineFromPosition(mh.position);
2646 			if (mh.position > pdoc->LineStart(lineOfPos))
2647 				lineOfPos++;	// Affecting subsequent lines
2648 			if (mh.linesAdded > 0) {
2649 				pcs->InsertLines(lineOfPos, mh.linesAdded);
2650 			} else {
2651 				pcs->DeleteLines(lineOfPos, -mh.linesAdded);
2652 			}
2653 			view.LinesAddedOrRemoved(lineOfPos, mh.linesAdded);
2654 		}
2655 		if (mh.modificationType & SC_MOD_CHANGEANNOTATION) {
2656 			const Sci::Line lineDoc = pdoc->SciLineFromPosition(mh.position);
2657 			if (vs.annotationVisible) {
2658 				if (pcs->SetHeight(lineDoc, pcs->GetHeight(lineDoc) + static_cast<int>(mh.annotationLinesAdded))) {
2659 					SetScrollBars();
2660 				}
2661 				Redraw();
2662 			}
2663 		}
2664 		CheckModificationForWrap(mh);
2665 		if (mh.linesAdded != 0) {
2666 			// Avoid scrolling of display if change before current display
2667 			if (mh.position < posTopLine && !CanDeferToLastStep(mh)) {
2668 				const Sci::Line newTop = std::clamp<Sci::Line>(topLine + mh.linesAdded, 0, MaxScrollPos());
2669 				if (newTop != topLine) {
2670 					SetTopLine(newTop);
2671 					SetVerticalScrollPos();
2672 				}
2673 			}
2674 
2675 			if (paintState == notPainting && !CanDeferToLastStep(mh)) {
2676 				if (SynchronousStylingToVisible()) {
2677 					QueueIdleWork(WorkNeeded::workStyle, pdoc->Length());
2678 				}
2679 				Redraw();
2680 			}
2681 		} else {
2682 			if (paintState == notPainting && mh.length && !CanEliminate(mh)) {
2683 				if (SynchronousStylingToVisible()) {
2684 					QueueIdleWork(WorkNeeded::workStyle, mh.position + mh.length);
2685 				}
2686 				InvalidateRange(mh.position, mh.position + mh.length);
2687 			}
2688 		}
2689 	}
2690 
2691 	if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {
2692 		SetScrollBars();
2693 	}
2694 
2695 	if ((mh.modificationType & SC_MOD_CHANGEMARKER) || (mh.modificationType & SC_MOD_CHANGEMARGIN)) {
2696 		if ((!willRedrawAll) && ((paintState == notPainting) || !PaintContainsMargin())) {
2697 			if (mh.modificationType & SC_MOD_CHANGEFOLD) {
2698 				// Fold changes can affect the drawing of following lines so redraw whole margin
2699 				RedrawSelMargin(marginView.highlightDelimiter.isEnabled ? -1 : mh.line - 1, true);
2700 			} else {
2701 				RedrawSelMargin(mh.line);
2702 			}
2703 		}
2704 	}
2705 	if ((mh.modificationType & SC_MOD_CHANGEFOLD) && (foldAutomatic & SC_AUTOMATICFOLD_CHANGE)) {
2706 		FoldChanged(mh.line, mh.foldLevelNow, mh.foldLevelPrev);
2707 	}
2708 
2709 	// NOW pay the piper WRT "deferred" visual updates
2710 	if (IsLastStep(mh)) {
2711 		SetScrollBars();
2712 		Redraw();
2713 	}
2714 
2715 	// If client wants to see this modification
2716 	if (mh.modificationType & modEventMask) {
2717 		if (commandEvents) {
2718 			if ((mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) == 0) {
2719 				// Real modification made to text of document.
2720 				NotifyChange();	// Send EN_CHANGE
2721 			}
2722 		}
2723 
2724 		SCNotification scn = {};
2725 		scn.nmhdr.code = SCN_MODIFIED;
2726 		scn.position = mh.position;
2727 		scn.modificationType = mh.modificationType;
2728 		scn.text = mh.text;
2729 		scn.length = mh.length;
2730 		scn.linesAdded = mh.linesAdded;
2731 		scn.line = mh.line;
2732 		scn.foldLevelNow = mh.foldLevelNow;
2733 		scn.foldLevelPrev = mh.foldLevelPrev;
2734 		scn.token = static_cast<int>(mh.token);
2735 		scn.annotationLinesAdded = mh.annotationLinesAdded;
2736 		NotifyParent(scn);
2737 	}
2738 }
2739 
NotifyDeleted(Document *,void *)2740 void Editor::NotifyDeleted(Document *, void *) noexcept {
2741 	/* Do nothing */
2742 }
2743 
NotifyMacroRecord(unsigned int iMessage,uptr_t wParam,sptr_t lParam)2744 void Editor::NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
2745 
2746 	// Enumerates all macroable messages
2747 	switch (iMessage) {
2748 	case SCI_CUT:
2749 	case SCI_COPY:
2750 	case SCI_PASTE:
2751 	case SCI_CLEAR:
2752 	case SCI_REPLACESEL:
2753 	case SCI_ADDTEXT:
2754 	case SCI_INSERTTEXT:
2755 	case SCI_APPENDTEXT:
2756 	case SCI_CLEARALL:
2757 	case SCI_SELECTALL:
2758 	case SCI_GOTOLINE:
2759 	case SCI_GOTOPOS:
2760 	case SCI_SEARCHANCHOR:
2761 	case SCI_SEARCHNEXT:
2762 	case SCI_SEARCHPREV:
2763 	case SCI_LINEDOWN:
2764 	case SCI_LINEDOWNEXTEND:
2765 	case SCI_PARADOWN:
2766 	case SCI_PARADOWNEXTEND:
2767 	case SCI_LINEUP:
2768 	case SCI_LINEUPEXTEND:
2769 	case SCI_PARAUP:
2770 	case SCI_PARAUPEXTEND:
2771 	case SCI_CHARLEFT:
2772 	case SCI_CHARLEFTEXTEND:
2773 	case SCI_CHARRIGHT:
2774 	case SCI_CHARRIGHTEXTEND:
2775 	case SCI_WORDLEFT:
2776 	case SCI_WORDLEFTEXTEND:
2777 	case SCI_WORDRIGHT:
2778 	case SCI_WORDRIGHTEXTEND:
2779 	case SCI_WORDPARTLEFT:
2780 	case SCI_WORDPARTLEFTEXTEND:
2781 	case SCI_WORDPARTRIGHT:
2782 	case SCI_WORDPARTRIGHTEXTEND:
2783 	case SCI_WORDLEFTEND:
2784 	case SCI_WORDLEFTENDEXTEND:
2785 	case SCI_WORDRIGHTEND:
2786 	case SCI_WORDRIGHTENDEXTEND:
2787 	case SCI_HOME:
2788 	case SCI_HOMEEXTEND:
2789 	case SCI_LINEEND:
2790 	case SCI_LINEENDEXTEND:
2791 	case SCI_HOMEWRAP:
2792 	case SCI_HOMEWRAPEXTEND:
2793 	case SCI_LINEENDWRAP:
2794 	case SCI_LINEENDWRAPEXTEND:
2795 	case SCI_DOCUMENTSTART:
2796 	case SCI_DOCUMENTSTARTEXTEND:
2797 	case SCI_DOCUMENTEND:
2798 	case SCI_DOCUMENTENDEXTEND:
2799 	case SCI_STUTTEREDPAGEUP:
2800 	case SCI_STUTTEREDPAGEUPEXTEND:
2801 	case SCI_STUTTEREDPAGEDOWN:
2802 	case SCI_STUTTEREDPAGEDOWNEXTEND:
2803 	case SCI_PAGEUP:
2804 	case SCI_PAGEUPEXTEND:
2805 	case SCI_PAGEDOWN:
2806 	case SCI_PAGEDOWNEXTEND:
2807 	case SCI_EDITTOGGLEOVERTYPE:
2808 	case SCI_CANCEL:
2809 	case SCI_DELETEBACK:
2810 	case SCI_TAB:
2811 	case SCI_BACKTAB:
2812 	case SCI_FORMFEED:
2813 	case SCI_VCHOME:
2814 	case SCI_VCHOMEEXTEND:
2815 	case SCI_VCHOMEWRAP:
2816 	case SCI_VCHOMEWRAPEXTEND:
2817 	case SCI_VCHOMEDISPLAY:
2818 	case SCI_VCHOMEDISPLAYEXTEND:
2819 	case SCI_DELWORDLEFT:
2820 	case SCI_DELWORDRIGHT:
2821 	case SCI_DELWORDRIGHTEND:
2822 	case SCI_DELLINELEFT:
2823 	case SCI_DELLINERIGHT:
2824 	case SCI_LINECOPY:
2825 	case SCI_LINECUT:
2826 	case SCI_LINEDELETE:
2827 	case SCI_LINETRANSPOSE:
2828 	case SCI_LINEREVERSE:
2829 	case SCI_LINEDUPLICATE:
2830 	case SCI_LOWERCASE:
2831 	case SCI_UPPERCASE:
2832 	case SCI_LINESCROLLDOWN:
2833 	case SCI_LINESCROLLUP:
2834 	case SCI_DELETEBACKNOTLINE:
2835 	case SCI_HOMEDISPLAY:
2836 	case SCI_HOMEDISPLAYEXTEND:
2837 	case SCI_LINEENDDISPLAY:
2838 	case SCI_LINEENDDISPLAYEXTEND:
2839 	case SCI_SETSELECTIONMODE:
2840 	case SCI_LINEDOWNRECTEXTEND:
2841 	case SCI_LINEUPRECTEXTEND:
2842 	case SCI_CHARLEFTRECTEXTEND:
2843 	case SCI_CHARRIGHTRECTEXTEND:
2844 	case SCI_HOMERECTEXTEND:
2845 	case SCI_VCHOMERECTEXTEND:
2846 	case SCI_LINEENDRECTEXTEND:
2847 	case SCI_PAGEUPRECTEXTEND:
2848 	case SCI_PAGEDOWNRECTEXTEND:
2849 	case SCI_SELECTIONDUPLICATE:
2850 	case SCI_COPYALLOWLINE:
2851 	case SCI_VERTICALCENTRECARET:
2852 	case SCI_MOVESELECTEDLINESUP:
2853 	case SCI_MOVESELECTEDLINESDOWN:
2854 	case SCI_SCROLLTOSTART:
2855 	case SCI_SCROLLTOEND:
2856 		break;
2857 
2858 		// Filter out all others like display changes. Also, newlines are redundant
2859 		// with char insert messages.
2860 	case SCI_NEWLINE:
2861 	default:
2862 		//		printf("Filtered out %ld of macro recording\n", iMessage);
2863 		return;
2864 	}
2865 
2866 	// Send notification
2867 	SCNotification scn = {};
2868 	scn.nmhdr.code = SCN_MACRORECORD;
2869 	scn.message = iMessage;
2870 	scn.wParam = wParam;
2871 	scn.lParam = lParam;
2872 	NotifyParent(scn);
2873 }
2874 
2875 // Something has changed that the container should know about
ContainerNeedsUpdate(int flags)2876 void Editor::ContainerNeedsUpdate(int flags) noexcept {
2877 	needUpdateUI |= flags;
2878 }
2879 
2880 /**
2881  * Force scroll and keep position relative to top of window.
2882  *
2883  * If stuttered = true and not already at first/last row, move to first/last row of window.
2884  * If stuttered = true and already at first/last row, scroll as normal.
2885  */
PageMove(int direction,Selection::selTypes selt,bool stuttered)2886 void Editor::PageMove(int direction, Selection::selTypes selt, bool stuttered) {
2887 	Sci::Line topLineNew;
2888 	SelectionPosition newPos;
2889 
2890 	const Sci::Line currentLine = pdoc->SciLineFromPosition(sel.MainCaret());
2891 	const Sci::Line topStutterLine = topLine + caretPolicies.y.slop;
2892 	const Sci::Line bottomStutterLine =
2893 	    pdoc->SciLineFromPosition(PositionFromLocation(
2894 	                Point::FromInts(lastXChosen - xOffset, direction * vs.lineHeight * static_cast<int>(LinesToScroll()))))
2895 	    - caretPolicies.y.slop - 1;
2896 
2897 	if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
2898 		topLineNew = topLine;
2899 		newPos = SPositionFromLocation(Point::FromInts(lastXChosen - xOffset, vs.lineHeight * caretPolicies.y.slop),
2900 			false, false, UserVirtualSpace());
2901 
2902 	} else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
2903 		topLineNew = topLine;
2904 		newPos = SPositionFromLocation(Point::FromInts(lastXChosen - xOffset, vs.lineHeight * static_cast<int>(LinesToScroll() - caretPolicies.y.slop)),
2905 			false, false, UserVirtualSpace());
2906 
2907 	} else {
2908 		const Point pt = LocationFromPosition(sel.MainCaret());
2909 
2910 		topLineNew = std::clamp<Sci::Line>(
2911 		            topLine + direction * LinesToScroll(), 0, MaxScrollPos());
2912 		newPos = SPositionFromLocation(
2913 			Point::FromInts(lastXChosen - xOffset, static_cast<int>(pt.y) +
2914 				direction * (vs.lineHeight * static_cast<int>(LinesToScroll()))),
2915 			false, false, UserVirtualSpace());
2916 	}
2917 
2918 	if (topLineNew != topLine) {
2919 		SetTopLine(topLineNew);
2920 		MovePositionTo(newPos, selt);
2921 		Redraw();
2922 		SetVerticalScrollPos();
2923 	} else {
2924 		MovePositionTo(newPos, selt);
2925 	}
2926 }
2927 
ChangeCaseOfSelection(int caseMapping)2928 void Editor::ChangeCaseOfSelection(int caseMapping) {
2929 	UndoGroup ug(pdoc);
2930 	for (size_t r=0; r<sel.Count(); r++) {
2931 		SelectionRange current = sel.Range(r);
2932 		SelectionRange currentNoVS = current;
2933 		currentNoVS.ClearVirtualSpace();
2934 		const size_t rangeBytes = currentNoVS.Length();
2935 		if (rangeBytes > 0) {
2936 			std::string sText = RangeText(currentNoVS.Start().Position(), currentNoVS.End().Position());
2937 
2938 			std::string sMapped = CaseMapString(sText, caseMapping);
2939 
2940 			if (sMapped != sText) {
2941 				size_t firstDifference = 0;
2942 				while (sMapped[firstDifference] == sText[firstDifference])
2943 					firstDifference++;
2944 				size_t lastDifferenceText = sText.size() - 1;
2945 				size_t lastDifferenceMapped = sMapped.size() - 1;
2946 				while (sMapped[lastDifferenceMapped] == sText[lastDifferenceText]) {
2947 					lastDifferenceText--;
2948 					lastDifferenceMapped--;
2949 				}
2950 				const size_t endDifferenceText = sText.size() - 1 - lastDifferenceText;
2951 				pdoc->DeleteChars(
2952 					currentNoVS.Start().Position() + firstDifference,
2953 					rangeBytes - firstDifference - endDifferenceText);
2954 				const Sci::Position lengthChange = lastDifferenceMapped - firstDifference + 1;
2955 				const Sci::Position lengthInserted = pdoc->InsertString(
2956 					currentNoVS.Start().Position() + firstDifference,
2957 					sMapped.c_str() + firstDifference,
2958 					lengthChange);
2959 				// Automatic movement changes selection so reset to exactly the same as it was.
2960 				const Sci::Position diffSizes = sMapped.size() - sText.size() + lengthInserted - lengthChange;
2961 				if (diffSizes != 0) {
2962 					if (current.anchor > current.caret)
2963 						current.anchor.Add(diffSizes);
2964 					else
2965 						current.caret.Add(diffSizes);
2966 				}
2967 				sel.Range(r) = current;
2968 			}
2969 		}
2970 	}
2971 }
2972 
LineTranspose()2973 void Editor::LineTranspose() {
2974 	const Sci::Line line = pdoc->SciLineFromPosition(sel.MainCaret());
2975 	if (line > 0) {
2976 		UndoGroup ug(pdoc);
2977 
2978 		const Sci::Position startPrevious = pdoc->LineStart(line - 1);
2979 		const std::string linePrevious = RangeText(startPrevious, pdoc->LineEnd(line - 1));
2980 
2981 		Sci::Position startCurrent = pdoc->LineStart(line);
2982 		const std::string lineCurrent = RangeText(startCurrent, pdoc->LineEnd(line));
2983 
2984 		pdoc->DeleteChars(startCurrent, lineCurrent.length());
2985 		pdoc->DeleteChars(startPrevious, linePrevious.length());
2986 		startCurrent -= linePrevious.length();
2987 
2988 		startCurrent += pdoc->InsertString(startPrevious, lineCurrent.c_str(),
2989 			lineCurrent.length());
2990 		pdoc->InsertString(startCurrent, linePrevious.c_str(),
2991 			linePrevious.length());
2992 		// Move caret to start of current line
2993 		MovePositionTo(SelectionPosition(startCurrent));
2994 	}
2995 }
2996 
LineReverse()2997 void Editor::LineReverse() {
2998 	const Sci::Line lineStart =
2999 		pdoc->SciLineFromPosition(sel.RangeMain().Start().Position());
3000 	const Sci::Line lineEnd =
3001 		pdoc->SciLineFromPosition(sel.RangeMain().End().Position()-1);
3002 	const Sci::Line lineDiff = lineEnd - lineStart;
3003 	if (lineDiff <= 0)
3004 		return;
3005 	UndoGroup ug(pdoc);
3006 	for (Sci::Line i=(lineDiff+1)/2-1; i>=0; --i) {
3007 		const Sci::Line lineNum2 = lineEnd - i;
3008 		const Sci::Line lineNum1 = lineStart + i;
3009 		Sci::Position lineStart2 = pdoc->LineStart(lineNum2);
3010 		const Sci::Position lineStart1 = pdoc->LineStart(lineNum1);
3011 		const std::string line2 = RangeText(lineStart2, pdoc->LineEnd(lineNum2));
3012 		const std::string line1 = RangeText(lineStart1, pdoc->LineEnd(lineNum1));
3013 		const Sci::Position lineLen2 = line2.length();
3014 		const Sci::Position lineLen1 = line1.length();
3015 		pdoc->DeleteChars(lineStart2, lineLen2);
3016 		pdoc->DeleteChars(lineStart1, lineLen1);
3017 		lineStart2 -= lineLen1;
3018 		pdoc->InsertString(lineStart2, line1.c_str(), lineLen1);
3019 		pdoc->InsertString(lineStart1, line2.c_str(), lineLen2);
3020 	}
3021 	// Wholly select all affected lines
3022 	sel.RangeMain() = SelectionRange(pdoc->LineStart(lineStart),
3023 		pdoc->LineStart(lineEnd+1));
3024 }
3025 
Duplicate(bool forLine)3026 void Editor::Duplicate(bool forLine) {
3027 	if (sel.Empty()) {
3028 		forLine = true;
3029 	}
3030 	UndoGroup ug(pdoc);
3031 	const char *eol = "";
3032 	Sci::Position eolLen = 0;
3033 	if (forLine) {
3034 		eol = StringFromEOLMode(pdoc->eolMode);
3035 		eolLen = strlen(eol);
3036 	}
3037 	for (size_t r=0; r<sel.Count(); r++) {
3038 		SelectionPosition start = sel.Range(r).Start();
3039 		SelectionPosition end = sel.Range(r).End();
3040 		if (forLine) {
3041 			const Sci::Line line = pdoc->SciLineFromPosition(sel.Range(r).caret.Position());
3042 			start = SelectionPosition(pdoc->LineStart(line));
3043 			end = SelectionPosition(pdoc->LineEnd(line));
3044 		}
3045 		std::string text = RangeText(start.Position(), end.Position());
3046 		Sci::Position lengthInserted = eolLen;
3047 		if (forLine)
3048 			lengthInserted = pdoc->InsertString(end.Position(), eol, eolLen);
3049 		pdoc->InsertString(end.Position() + lengthInserted, text.c_str(), text.length());
3050 	}
3051 	if (sel.Count() && sel.IsRectangular()) {
3052 		SelectionPosition last = sel.Last();
3053 		if (forLine) {
3054 			const Sci::Line line = pdoc->SciLineFromPosition(last.Position());
3055 			last = SelectionPosition(last.Position() +
3056 				pdoc->LineStart(line+1) - pdoc->LineStart(line));
3057 		}
3058 		if (sel.Rectangular().anchor > sel.Rectangular().caret)
3059 			sel.Rectangular().anchor = last;
3060 		else
3061 			sel.Rectangular().caret = last;
3062 		SetRectangularRange();
3063 	}
3064 }
3065 
CancelModes()3066 void Editor::CancelModes() {
3067 	sel.SetMoveExtends(false);
3068 }
3069 
NewLine()3070 void Editor::NewLine() {
3071 	InvalidateWholeSelection();
3072 	if (sel.IsRectangular() || !additionalSelectionTyping) {
3073 		// Remove non-main ranges
3074 		sel.DropAdditionalRanges();
3075 	}
3076 
3077 	UndoGroup ug(pdoc, !sel.Empty() || (sel.Count() > 1));
3078 
3079 	// Clear each range
3080 	if (!sel.Empty()) {
3081 		ClearSelection();
3082 	}
3083 
3084 	// Insert each line end
3085 	size_t countInsertions = 0;
3086 	for (size_t r = 0; r < sel.Count(); r++) {
3087 		sel.Range(r).ClearVirtualSpace();
3088 		const char *eol = StringFromEOLMode(pdoc->eolMode);
3089 		const Sci::Position positionInsert = sel.Range(r).caret.Position();
3090 		const Sci::Position insertLength = pdoc->InsertString(positionInsert, eol, strlen(eol));
3091 		if (insertLength > 0) {
3092 			sel.Range(r) = SelectionRange(positionInsert + insertLength);
3093 			countInsertions++;
3094 		}
3095 	}
3096 
3097 	// Perform notifications after all the changes as the application may change the
3098 	// selections in response to the characters.
3099 	for (size_t i = 0; i < countInsertions; i++) {
3100 		const char *eol = StringFromEOLMode(pdoc->eolMode);
3101 		while (*eol) {
3102 			NotifyChar(*eol, CharacterSource::directInput);
3103 			if (recordingMacro) {
3104 				char txt[2];
3105 				txt[0] = *eol;
3106 				txt[1] = '\0';
3107 				NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
3108 			}
3109 			eol++;
3110 		}
3111 	}
3112 
3113 	SetLastXChosen();
3114 	SetScrollBars();
3115 	EnsureCaretVisible();
3116 	// Avoid blinking during rapid typing:
3117 	ShowCaretAtCurrentPosition();
3118 }
3119 
PositionUpOrDown(SelectionPosition spStart,int direction,int lastX)3120 SelectionPosition Editor::PositionUpOrDown(SelectionPosition spStart, int direction, int lastX) {
3121 	const Point pt = LocationFromPosition(spStart);
3122 	int skipLines = 0;
3123 
3124 	if (vs.annotationVisible) {
3125 		const Sci::Line lineDoc = pdoc->SciLineFromPosition(spStart.Position());
3126 		const Point ptStartLine = LocationFromPosition(pdoc->LineStart(lineDoc));
3127 		const int subLine = static_cast<int>(pt.y - ptStartLine.y) / vs.lineHeight;
3128 
3129 		if (direction < 0 && subLine == 0) {
3130 			const Sci::Line lineDisplay = pcs->DisplayFromDoc(lineDoc);
3131 			if (lineDisplay > 0) {
3132 				skipLines = pdoc->AnnotationLines(pcs->DocFromDisplay(lineDisplay - 1));
3133 			}
3134 		} else if (direction > 0 && subLine >= (pcs->GetHeight(lineDoc) - 1 - pdoc->AnnotationLines(lineDoc))) {
3135 			skipLines = pdoc->AnnotationLines(lineDoc);
3136 		}
3137 	}
3138 
3139 	const Sci::Line newY = static_cast<Sci::Line>(pt.y) + (1 + skipLines) * direction * vs.lineHeight;
3140 	if (lastX < 0) {
3141 		lastX = static_cast<int>(pt.x) + xOffset;
3142 	}
3143 	SelectionPosition posNew = SPositionFromLocation(
3144 		Point::FromInts(lastX - xOffset, static_cast<int>(newY)), false, false, UserVirtualSpace());
3145 
3146 	if (direction < 0) {
3147 		// Line wrapping may lead to a location on the same line, so
3148 		// seek back if that is the case.
3149 		Point ptNew = LocationFromPosition(posNew.Position());
3150 		while ((posNew.Position() > 0) && (pt.y == ptNew.y)) {
3151 			posNew.Add(-1);
3152 			posNew.SetVirtualSpace(0);
3153 			ptNew = LocationFromPosition(posNew.Position());
3154 		}
3155 	} else if (direction > 0 && posNew.Position() != pdoc->Length()) {
3156 		// There is an equivalent case when moving down which skips
3157 		// over a line.
3158 		Point ptNew = LocationFromPosition(posNew.Position());
3159 		while ((posNew.Position() > spStart.Position()) && (ptNew.y > newY)) {
3160 			posNew.Add(-1);
3161 			posNew.SetVirtualSpace(0);
3162 			ptNew = LocationFromPosition(posNew.Position());
3163 		}
3164 	}
3165 	return posNew;
3166 }
3167 
CursorUpOrDown(int direction,Selection::selTypes selt)3168 void Editor::CursorUpOrDown(int direction, Selection::selTypes selt) {
3169 	if ((selt == Selection::noSel) && sel.MoveExtends()) {
3170 		selt = !sel.IsRectangular() ? Selection::selStream : Selection::selRectangle;
3171 	}
3172 	SelectionPosition caretToUse = sel.Range(sel.Main()).caret;
3173 	if (sel.IsRectangular()) {
3174 		if (selt ==  Selection::noSel) {
3175 			caretToUse = (direction > 0) ? sel.Limits().end : sel.Limits().start;
3176 		} else {
3177 			caretToUse = sel.Rectangular().caret;
3178 		}
3179 	}
3180 	if (selt == Selection::selRectangle) {
3181 		const SelectionRange rangeBase = sel.IsRectangular() ? sel.Rectangular() : sel.RangeMain();
3182 		if (!sel.IsRectangular()) {
3183 			InvalidateWholeSelection();
3184 			sel.DropAdditionalRanges();
3185 		}
3186 		const SelectionPosition posNew = MovePositionSoVisible(
3187 			PositionUpOrDown(caretToUse, direction, lastXChosen), direction);
3188 		sel.selType = Selection::selRectangle;
3189 		sel.Rectangular() = SelectionRange(posNew, rangeBase.anchor);
3190 		SetRectangularRange();
3191 		MovedCaret(posNew, caretToUse, true, caretPolicies);
3192 	} else if (sel.selType == Selection::selLines && sel.MoveExtends()) {
3193 		// Calculate new caret position and call SetSelection(), which will ensure whole lines are selected.
3194 		const SelectionPosition posNew = MovePositionSoVisible(
3195 			PositionUpOrDown(caretToUse, direction, -1), direction);
3196 		SetSelection(posNew, sel.Range(sel.Main()).anchor);
3197 	} else {
3198 		InvalidateWholeSelection();
3199 		if (!additionalSelectionTyping || (sel.IsRectangular())) {
3200 			sel.DropAdditionalRanges();
3201 		}
3202 		sel.selType = Selection::selStream;
3203 		for (size_t r = 0; r < sel.Count(); r++) {
3204 			const int lastX = (r == sel.Main()) ? lastXChosen : -1;
3205 			const SelectionPosition spCaretNow = sel.Range(r).caret;
3206 			const SelectionPosition posNew = MovePositionSoVisible(
3207 				PositionUpOrDown(spCaretNow, direction, lastX), direction);
3208 			sel.Range(r) = selt == Selection::selStream ?
3209 				SelectionRange(posNew, sel.Range(r).anchor) : SelectionRange(posNew);
3210 		}
3211 		sel.RemoveDuplicates();
3212 		MovedCaret(sel.RangeMain().caret, caretToUse, true, caretPolicies);
3213 	}
3214 }
3215 
ParaUpOrDown(int direction,Selection::selTypes selt)3216 void Editor::ParaUpOrDown(int direction, Selection::selTypes selt) {
3217 	Sci::Line lineDoc;
3218 	const Sci::Position savedPos = sel.MainCaret();
3219 	do {
3220 		MovePositionTo(SelectionPosition(direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret())), selt);
3221 		lineDoc = pdoc->SciLineFromPosition(sel.MainCaret());
3222 		if (direction > 0) {
3223 			if (sel.MainCaret() >= pdoc->Length() && !pcs->GetVisible(lineDoc)) {
3224 				if (selt == Selection::noSel) {
3225 					MovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos)));
3226 				}
3227 				break;
3228 			}
3229 		}
3230 	} while (!pcs->GetVisible(lineDoc));
3231 }
3232 
RangeDisplayLine(Sci::Line lineVisible)3233 Range Editor::RangeDisplayLine(Sci::Line lineVisible) {
3234 	RefreshStyleData();
3235 	AutoSurface surface(this);
3236 	return view.RangeDisplayLine(surface, *this, lineVisible, vs);
3237 }
3238 
StartEndDisplayLine(Sci::Position pos,bool start)3239 Sci::Position Editor::StartEndDisplayLine(Sci::Position pos, bool start) {
3240 	RefreshStyleData();
3241 	AutoSurface surface(this);
3242 	const Sci::Position posRet = view.StartEndDisplayLine(surface, *this, pos, start, vs);
3243 	if (posRet == INVALID_POSITION) {
3244 		return pos;
3245 	} else {
3246 		return posRet;
3247 	}
3248 }
3249 
3250 namespace {
3251 
HighShortFromWParam(uptr_t x)3252 constexpr short HighShortFromWParam(uptr_t x) {
3253 	return static_cast<short>(x >> 16);
3254 }
3255 
LowShortFromWParam(uptr_t x)3256 constexpr short LowShortFromWParam(uptr_t x) {
3257 	return static_cast<short>(x & 0xffff);
3258 }
3259 
WithExtends(unsigned int iMessage)3260 constexpr unsigned int WithExtends(unsigned int iMessage) noexcept {
3261 	switch (iMessage) {
3262 	case SCI_CHARLEFT: return SCI_CHARLEFTEXTEND;
3263 	case SCI_CHARRIGHT: return SCI_CHARRIGHTEXTEND;
3264 
3265 	case SCI_WORDLEFT: return SCI_WORDLEFTEXTEND;
3266 	case SCI_WORDRIGHT: return SCI_WORDRIGHTEXTEND;
3267 	case SCI_WORDLEFTEND: return SCI_WORDLEFTENDEXTEND;
3268 	case SCI_WORDRIGHTEND: return SCI_WORDRIGHTENDEXTEND;
3269 	case SCI_WORDPARTLEFT: return SCI_WORDPARTLEFTEXTEND;
3270 	case SCI_WORDPARTRIGHT: return SCI_WORDPARTRIGHTEXTEND;
3271 
3272 	case SCI_HOME: return SCI_HOMEEXTEND;
3273 	case SCI_HOMEDISPLAY: return SCI_HOMEDISPLAYEXTEND;
3274 	case SCI_HOMEWRAP: return SCI_HOMEWRAPEXTEND;
3275 	case SCI_VCHOME: return SCI_VCHOMEEXTEND;
3276 	case SCI_VCHOMEDISPLAY: return SCI_VCHOMEDISPLAYEXTEND;
3277 	case SCI_VCHOMEWRAP: return SCI_VCHOMEWRAPEXTEND;
3278 
3279 	case SCI_LINEEND: return SCI_LINEENDEXTEND;
3280 	case SCI_LINEENDDISPLAY: return SCI_LINEENDDISPLAYEXTEND;
3281 	case SCI_LINEENDWRAP: return SCI_LINEENDWRAPEXTEND;
3282 
3283 	default:	return iMessage;
3284 	}
3285 }
3286 
NaturalDirection(unsigned int iMessage)3287 constexpr int NaturalDirection(unsigned int iMessage) noexcept {
3288 	switch (iMessage) {
3289 	case SCI_CHARLEFT:
3290 	case SCI_CHARLEFTEXTEND:
3291 	case SCI_CHARLEFTRECTEXTEND:
3292 	case SCI_WORDLEFT:
3293 	case SCI_WORDLEFTEXTEND:
3294 	case SCI_WORDLEFTEND:
3295 	case SCI_WORDLEFTENDEXTEND:
3296 	case SCI_WORDPARTLEFT:
3297 	case SCI_WORDPARTLEFTEXTEND:
3298 	case SCI_HOME:
3299 	case SCI_HOMEEXTEND:
3300 	case SCI_HOMEDISPLAY:
3301 	case SCI_HOMEDISPLAYEXTEND:
3302 	case SCI_HOMEWRAP:
3303 	case SCI_HOMEWRAPEXTEND:
3304 		// VC_HOME* mostly goes back
3305 	case SCI_VCHOME:
3306 	case SCI_VCHOMEEXTEND:
3307 	case SCI_VCHOMEDISPLAY:
3308 	case SCI_VCHOMEDISPLAYEXTEND:
3309 	case SCI_VCHOMEWRAP:
3310 	case SCI_VCHOMEWRAPEXTEND:
3311 		return -1;
3312 
3313 	default:
3314 		return 1;
3315 	}
3316 }
3317 
IsRectExtend(unsigned int iMessage,bool isRectMoveExtends)3318 constexpr bool IsRectExtend(unsigned int iMessage, bool isRectMoveExtends) noexcept {
3319 	switch (iMessage) {
3320 	case SCI_CHARLEFTRECTEXTEND:
3321 	case SCI_CHARRIGHTRECTEXTEND:
3322 	case SCI_HOMERECTEXTEND:
3323 	case SCI_VCHOMERECTEXTEND:
3324 	case SCI_LINEENDRECTEXTEND:
3325 		return true;
3326 	default:
3327 		if (isRectMoveExtends) {
3328 			// Handle SCI_SETSELECTIONMODE(SC_SEL_RECTANGLE) and subsequent movements.
3329 			switch (iMessage) {
3330 			case SCI_CHARLEFTEXTEND:
3331 			case SCI_CHARRIGHTEXTEND:
3332 			case SCI_HOMEEXTEND:
3333 			case SCI_VCHOMEEXTEND:
3334 			case SCI_LINEENDEXTEND:
3335 				return true;
3336 			default:
3337 				return false;
3338 			}
3339 		}
3340 		return false;
3341 	}
3342 }
3343 
3344 }
3345 
VCHomeDisplayPosition(Sci::Position position)3346 Sci::Position Editor::VCHomeDisplayPosition(Sci::Position position) {
3347 	const Sci::Position homePos = pdoc->VCHomePosition(position);
3348 	const Sci::Position viewLineStart = StartEndDisplayLine(position, true);
3349 	if (viewLineStart > homePos)
3350 		return viewLineStart;
3351 	else
3352 		return homePos;
3353 }
3354 
VCHomeWrapPosition(Sci::Position position)3355 Sci::Position Editor::VCHomeWrapPosition(Sci::Position position) {
3356 	const Sci::Position homePos = pdoc->VCHomePosition(position);
3357 	const Sci::Position viewLineStart = StartEndDisplayLine(position, true);
3358 	if ((viewLineStart < position) && (viewLineStart > homePos))
3359 		return viewLineStart;
3360 	else
3361 		return homePos;
3362 }
3363 
LineEndWrapPosition(Sci::Position position)3364 Sci::Position Editor::LineEndWrapPosition(Sci::Position position) {
3365 	const Sci::Position endPos = StartEndDisplayLine(position, false);
3366 	const Sci::Position realEndPos = pdoc->LineEndPosition(position);
3367 	if (endPos > realEndPos      // if moved past visible EOLs
3368 		|| position >= endPos) // if at end of display line already
3369 		return realEndPos;
3370 	else
3371 		return endPos;
3372 }
3373 
HorizontalMove(unsigned int iMessage)3374 int Editor::HorizontalMove(unsigned int iMessage) {
3375 	if (sel.selType == Selection::selLines) {
3376 		return 0; // horizontal moves with line selection have no effect
3377 	}
3378 	if (sel.MoveExtends()) {
3379 		iMessage = WithExtends(iMessage);
3380 	}
3381 
3382 	if (!multipleSelection && !sel.IsRectangular()) {
3383 		// Simplify selection down to 1
3384 		sel.SetSelection(sel.RangeMain());
3385 	}
3386 
3387 	// Invalidate each of the current selections
3388 	InvalidateWholeSelection();
3389 
3390 	if (IsRectExtend(iMessage, sel.IsRectangular() && sel.MoveExtends())) {
3391 		const SelectionRange rangeBase = sel.IsRectangular() ? sel.Rectangular() : sel.RangeMain();
3392 		if (!sel.IsRectangular()) {
3393 			sel.DropAdditionalRanges();
3394 		}
3395 		// Will change to rectangular if not currently rectangular
3396 		SelectionPosition spCaret = rangeBase.caret;
3397 		switch (iMessage) {
3398 		case SCI_CHARLEFTRECTEXTEND:
3399 		case SCI_CHARLEFTEXTEND: // only when sel.IsRectangular() && sel.MoveExtends()
3400 			if (pdoc->IsLineEndPosition(spCaret.Position()) && spCaret.VirtualSpace()) {
3401 				spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
3402 			} else if ((virtualSpaceOptions & SCVS_NOWRAPLINESTART) == 0 || pdoc->GetColumn(spCaret.Position()) > 0) {
3403 				spCaret = SelectionPosition(spCaret.Position() - 1);
3404 			}
3405 			break;
3406 		case SCI_CHARRIGHTRECTEXTEND:
3407 		case SCI_CHARRIGHTEXTEND: // only when sel.IsRectangular() && sel.MoveExtends()
3408 			if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) && pdoc->IsLineEndPosition(sel.MainCaret())) {
3409 				spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
3410 			} else {
3411 				spCaret = SelectionPosition(spCaret.Position() + 1);
3412 			}
3413 			break;
3414 		case SCI_HOMERECTEXTEND:
3415 		case SCI_HOMEEXTEND: // only when sel.IsRectangular() && sel.MoveExtends()
3416 			spCaret = SelectionPosition(
3417 				pdoc->LineStart(pdoc->LineFromPosition(spCaret.Position())));
3418 			break;
3419 		case SCI_VCHOMERECTEXTEND:
3420 		case SCI_VCHOMEEXTEND: // only when sel.IsRectangular() && sel.MoveExtends()
3421 			spCaret = SelectionPosition(pdoc->VCHomePosition(spCaret.Position()));
3422 			break;
3423 		case SCI_LINEENDRECTEXTEND:
3424 		case SCI_LINEENDEXTEND: // only when sel.IsRectangular() && sel.MoveExtends()
3425 			spCaret = SelectionPosition(pdoc->LineEndPosition(spCaret.Position()));
3426 			break;
3427 		}
3428 		const int directionMove = (spCaret < rangeBase.caret) ? -1 : 1;
3429 		spCaret = MovePositionSoVisible(spCaret, directionMove);
3430 		sel.selType = Selection::selRectangle;
3431 		sel.Rectangular() = SelectionRange(spCaret, rangeBase.anchor);
3432 		SetRectangularRange();
3433 	} else if (sel.IsRectangular()) {
3434 		// Not a rectangular extension so switch to stream.
3435 		SelectionPosition selAtLimit = (NaturalDirection(iMessage) > 0) ? sel.Limits().end : sel.Limits().start;
3436 		switch (iMessage) {
3437 		case SCI_HOME:
3438 			selAtLimit = SelectionPosition(
3439 				pdoc->LineStart(pdoc->LineFromPosition(selAtLimit.Position())));
3440 			break;
3441 		case SCI_VCHOME:
3442 			selAtLimit = SelectionPosition(pdoc->VCHomePosition(selAtLimit.Position()));
3443 			break;
3444 		case SCI_LINEEND:
3445 			selAtLimit = SelectionPosition(pdoc->LineEndPosition(selAtLimit.Position()));
3446 			break;
3447 		}
3448 		sel.selType = Selection::selStream;
3449 		sel.SetSelection(SelectionRange(selAtLimit));
3450 	} else {
3451 		if (!additionalSelectionTyping) {
3452 			InvalidateWholeSelection();
3453 			sel.DropAdditionalRanges();
3454 		}
3455 		for (size_t r = 0; r < sel.Count(); r++) {
3456 			const SelectionPosition spCaretNow = sel.Range(r).caret;
3457 			SelectionPosition spCaret = spCaretNow;
3458 			switch (iMessage) {
3459 			case SCI_CHARLEFT:
3460 			case SCI_CHARLEFTEXTEND:
3461 				if (spCaret.VirtualSpace()) {
3462 					spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
3463 				} else if ((virtualSpaceOptions & SCVS_NOWRAPLINESTART) == 0 || pdoc->GetColumn(spCaret.Position()) > 0) {
3464 					spCaret = SelectionPosition(spCaret.Position() - 1);
3465 				}
3466 				break;
3467 			case SCI_CHARRIGHT:
3468 			case SCI_CHARRIGHTEXTEND:
3469 				if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(spCaret.Position())) {
3470 					spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
3471 				} else {
3472 					spCaret = SelectionPosition(spCaret.Position() + 1);
3473 				}
3474 				break;
3475 			case SCI_WORDLEFT:
3476 			case SCI_WORDLEFTEXTEND:
3477 				spCaret = SelectionPosition(pdoc->NextWordStart(spCaret.Position(), -1));
3478 				break;
3479 			case SCI_WORDRIGHT:
3480 			case SCI_WORDRIGHTEXTEND:
3481 				spCaret = SelectionPosition(pdoc->NextWordStart(spCaret.Position(), 1));
3482 				break;
3483 			case SCI_WORDLEFTEND:
3484 			case SCI_WORDLEFTENDEXTEND:
3485 				spCaret = SelectionPosition(pdoc->NextWordEnd(spCaret.Position(), -1));
3486 				break;
3487 			case SCI_WORDRIGHTEND:
3488 			case SCI_WORDRIGHTENDEXTEND:
3489 				spCaret = SelectionPosition(pdoc->NextWordEnd(spCaret.Position(), 1));
3490 				break;
3491 			case SCI_WORDPARTLEFT:
3492 			case SCI_WORDPARTLEFTEXTEND:
3493 				spCaret = SelectionPosition(pdoc->WordPartLeft(spCaret.Position()));
3494 				break;
3495 			case SCI_WORDPARTRIGHT:
3496 			case SCI_WORDPARTRIGHTEXTEND:
3497 				spCaret = SelectionPosition(pdoc->WordPartRight(spCaret.Position()));
3498 				break;
3499 			case SCI_HOME:
3500 			case SCI_HOMEEXTEND:
3501 				spCaret = SelectionPosition(
3502 					pdoc->LineStart(pdoc->LineFromPosition(spCaret.Position())));
3503 				break;
3504 			case SCI_HOMEDISPLAY:
3505 			case SCI_HOMEDISPLAYEXTEND:
3506 				spCaret = SelectionPosition(StartEndDisplayLine(spCaret.Position(), true));
3507 				break;
3508 			case SCI_HOMEWRAP:
3509 			case SCI_HOMEWRAPEXTEND:
3510 				spCaret = MovePositionSoVisible(StartEndDisplayLine(spCaret.Position(), true), -1);
3511 				if (spCaretNow <= spCaret)
3512 					spCaret = SelectionPosition(
3513 						pdoc->LineStart(pdoc->LineFromPosition(spCaret.Position())));
3514 				break;
3515 			case SCI_VCHOME:
3516 			case SCI_VCHOMEEXTEND:
3517 				// VCHome alternates between beginning of line and beginning of text so may move back or forwards
3518 				spCaret = SelectionPosition(pdoc->VCHomePosition(spCaret.Position()));
3519 				break;
3520 			case SCI_VCHOMEDISPLAY:
3521 			case SCI_VCHOMEDISPLAYEXTEND:
3522 				spCaret = SelectionPosition(VCHomeDisplayPosition(spCaret.Position()));
3523 				break;
3524 			case SCI_VCHOMEWRAP:
3525 			case SCI_VCHOMEWRAPEXTEND:
3526 				spCaret = SelectionPosition(VCHomeWrapPosition(spCaret.Position()));
3527 				break;
3528 			case SCI_LINEEND:
3529 			case SCI_LINEENDEXTEND:
3530 				spCaret = SelectionPosition(pdoc->LineEndPosition(spCaret.Position()));
3531 				break;
3532 			case SCI_LINEENDDISPLAY:
3533 			case SCI_LINEENDDISPLAYEXTEND:
3534 				spCaret = SelectionPosition(StartEndDisplayLine(spCaret.Position(), false));
3535 				break;
3536 			case SCI_LINEENDWRAP:
3537 			case SCI_LINEENDWRAPEXTEND:
3538 				spCaret = SelectionPosition(LineEndWrapPosition(spCaret.Position()));
3539 				break;
3540 
3541 			default:
3542 				PLATFORM_ASSERT(false);
3543 			}
3544 
3545 			const int directionMove = (spCaret < spCaretNow) ? -1 : 1;
3546 			spCaret = MovePositionSoVisible(spCaret, directionMove);
3547 
3548 			// Handle move versus extend, and special behaviour for non-empty left/right
3549 			switch (iMessage) {
3550 			case SCI_CHARLEFT:
3551 			case SCI_CHARRIGHT:
3552 				if (sel.Range(r).Empty()) {
3553 					sel.Range(r) = SelectionRange(spCaret);
3554 				} else {
3555 					sel.Range(r) = SelectionRange(
3556 						(iMessage == SCI_CHARLEFT) ? sel.Range(r).Start() : sel.Range(r).End());
3557 				}
3558 				break;
3559 
3560 			case SCI_WORDLEFT:
3561 			case SCI_WORDRIGHT:
3562 			case SCI_WORDLEFTEND:
3563 			case SCI_WORDRIGHTEND:
3564 			case SCI_WORDPARTLEFT:
3565 			case SCI_WORDPARTRIGHT:
3566 			case SCI_HOME:
3567 			case SCI_HOMEDISPLAY:
3568 			case SCI_HOMEWRAP:
3569 			case SCI_VCHOME:
3570 			case SCI_VCHOMEDISPLAY:
3571 			case SCI_VCHOMEWRAP:
3572 			case SCI_LINEEND:
3573 			case SCI_LINEENDDISPLAY:
3574 			case SCI_LINEENDWRAP:
3575 				sel.Range(r) = SelectionRange(spCaret);
3576 				break;
3577 
3578 			case SCI_CHARLEFTEXTEND:
3579 			case SCI_CHARRIGHTEXTEND:
3580 			case SCI_WORDLEFTEXTEND:
3581 			case SCI_WORDRIGHTEXTEND:
3582 			case SCI_WORDLEFTENDEXTEND:
3583 			case SCI_WORDRIGHTENDEXTEND:
3584 			case SCI_WORDPARTLEFTEXTEND:
3585 			case SCI_WORDPARTRIGHTEXTEND:
3586 			case SCI_HOMEEXTEND:
3587 			case SCI_HOMEDISPLAYEXTEND:
3588 			case SCI_HOMEWRAPEXTEND:
3589 			case SCI_VCHOMEEXTEND:
3590 			case SCI_VCHOMEDISPLAYEXTEND:
3591 			case SCI_VCHOMEWRAPEXTEND:
3592 			case SCI_LINEENDEXTEND:
3593 			case SCI_LINEENDDISPLAYEXTEND:
3594 			case SCI_LINEENDWRAPEXTEND: {
3595 				SelectionRange rangeNew = SelectionRange(spCaret, sel.Range(r).anchor);
3596 				sel.TrimOtherSelections(r, SelectionRange(rangeNew));
3597 				sel.Range(r) = rangeNew;
3598 				}
3599 				break;
3600 
3601 			default:
3602 				PLATFORM_ASSERT(false);
3603 			}
3604 		}
3605 	}
3606 
3607 	sel.RemoveDuplicates();
3608 
3609 	MovedCaret(sel.RangeMain().caret, SelectionPosition(INVALID_POSITION), true, caretPolicies);
3610 
3611 	// Invalidate the new state of the selection
3612 	InvalidateWholeSelection();
3613 
3614 	SetLastXChosen();
3615 	// Need the line moving and so forth from MovePositionTo
3616 	return 0;
3617 }
3618 
DelWordOrLine(unsigned int iMessage)3619 int Editor::DelWordOrLine(unsigned int iMessage) {
3620 	// Virtual space may be realised for SCI_DELWORDRIGHT or SCI_DELWORDRIGHTEND
3621 	// which means 2 actions so wrap in an undo group.
3622 
3623 	// Rightwards and leftwards deletions differ in treatment of virtual space.
3624 	// Clear virtual space for leftwards, realise for rightwards.
3625 	const bool leftwards = (iMessage == SCI_DELWORDLEFT) || (iMessage == SCI_DELLINELEFT);
3626 
3627 	if (!additionalSelectionTyping) {
3628 		InvalidateWholeSelection();
3629 		sel.DropAdditionalRanges();
3630 	}
3631 
3632 	UndoGroup ug0(pdoc, (sel.Count() > 1) || !leftwards);
3633 
3634 	for (size_t r = 0; r < sel.Count(); r++) {
3635 		if (leftwards) {
3636 			// Delete to the left so first clear the virtual space.
3637 			sel.Range(r).ClearVirtualSpace();
3638 		} else {
3639 			// Delete to the right so first realise the virtual space.
3640 			sel.Range(r) = SelectionRange(
3641 				RealizeVirtualSpace(sel.Range(r).caret));
3642 		}
3643 
3644 		Range rangeDelete;
3645 		switch (iMessage) {
3646 		case SCI_DELWORDLEFT:
3647 			rangeDelete = Range(
3648 				pdoc->NextWordStart(sel.Range(r).caret.Position(), -1),
3649 				sel.Range(r).caret.Position());
3650 			break;
3651 		case SCI_DELWORDRIGHT:
3652 			rangeDelete = Range(
3653 				sel.Range(r).caret.Position(),
3654 				pdoc->NextWordStart(sel.Range(r).caret.Position(), 1));
3655 			break;
3656 		case SCI_DELWORDRIGHTEND:
3657 			rangeDelete = Range(
3658 				sel.Range(r).caret.Position(),
3659 				pdoc->NextWordEnd(sel.Range(r).caret.Position(), 1));
3660 			break;
3661 		case SCI_DELLINELEFT:
3662 			rangeDelete = Range(
3663 				pdoc->LineStart(pdoc->LineFromPosition(sel.Range(r).caret.Position())),
3664 				sel.Range(r).caret.Position());
3665 			break;
3666 		case SCI_DELLINERIGHT:
3667 			rangeDelete = Range(
3668 				sel.Range(r).caret.Position(),
3669 				pdoc->LineEnd(pdoc->LineFromPosition(sel.Range(r).caret.Position())));
3670 			break;
3671 		}
3672 		if (!RangeContainsProtected(rangeDelete.start, rangeDelete.end)) {
3673 			pdoc->DeleteChars(rangeDelete.start, rangeDelete.end - rangeDelete.start);
3674 		}
3675 	}
3676 
3677 	// May need something stronger here: can selections overlap at this point?
3678 	sel.RemoveDuplicates();
3679 
3680 	MovedCaret(sel.RangeMain().caret, SelectionPosition(INVALID_POSITION), true, caretPolicies);
3681 
3682 	// Invalidate the new state of the selection
3683 	InvalidateWholeSelection();
3684 
3685 	SetLastXChosen();
3686 	return 0;
3687 }
3688 
KeyCommand(unsigned int iMessage)3689 int Editor::KeyCommand(unsigned int iMessage) {
3690 	switch (iMessage) {
3691 	case SCI_LINEDOWN:
3692 		CursorUpOrDown(1, Selection::noSel);
3693 		break;
3694 	case SCI_LINEDOWNEXTEND:
3695 		CursorUpOrDown(1, Selection::selStream);
3696 		break;
3697 	case SCI_LINEDOWNRECTEXTEND:
3698 		CursorUpOrDown(1, Selection::selRectangle);
3699 		break;
3700 	case SCI_PARADOWN:
3701 		ParaUpOrDown(1, Selection::noSel);
3702 		break;
3703 	case SCI_PARADOWNEXTEND:
3704 		ParaUpOrDown(1, Selection::selStream);
3705 		break;
3706 	case SCI_LINESCROLLDOWN:
3707 		ScrollTo(topLine + 1);
3708 		MoveCaretInsideView(false);
3709 		break;
3710 	case SCI_LINEUP:
3711 		CursorUpOrDown(-1, Selection::noSel);
3712 		break;
3713 	case SCI_LINEUPEXTEND:
3714 		CursorUpOrDown(-1, Selection::selStream);
3715 		break;
3716 	case SCI_LINEUPRECTEXTEND:
3717 		CursorUpOrDown(-1, Selection::selRectangle);
3718 		break;
3719 	case SCI_PARAUP:
3720 		ParaUpOrDown(-1, Selection::noSel);
3721 		break;
3722 	case SCI_PARAUPEXTEND:
3723 		ParaUpOrDown(-1, Selection::selStream);
3724 		break;
3725 	case SCI_LINESCROLLUP:
3726 		ScrollTo(topLine - 1);
3727 		MoveCaretInsideView(false);
3728 		break;
3729 
3730 	case SCI_CHARLEFT:
3731 	case SCI_CHARLEFTEXTEND:
3732 	case SCI_CHARLEFTRECTEXTEND:
3733 	case SCI_CHARRIGHT:
3734 	case SCI_CHARRIGHTEXTEND:
3735 	case SCI_CHARRIGHTRECTEXTEND:
3736 	case SCI_WORDLEFT:
3737 	case SCI_WORDLEFTEXTEND:
3738 	case SCI_WORDRIGHT:
3739 	case SCI_WORDRIGHTEXTEND:
3740 	case SCI_WORDLEFTEND:
3741 	case SCI_WORDLEFTENDEXTEND:
3742 	case SCI_WORDRIGHTEND:
3743 	case SCI_WORDRIGHTENDEXTEND:
3744 	case SCI_WORDPARTLEFT:
3745 	case SCI_WORDPARTLEFTEXTEND:
3746 	case SCI_WORDPARTRIGHT:
3747 	case SCI_WORDPARTRIGHTEXTEND:
3748 	case SCI_HOME:
3749 	case SCI_HOMEEXTEND:
3750 	case SCI_HOMERECTEXTEND:
3751 	case SCI_HOMEDISPLAY:
3752 	case SCI_HOMEDISPLAYEXTEND:
3753 	case SCI_HOMEWRAP:
3754 	case SCI_HOMEWRAPEXTEND:
3755 	case SCI_VCHOME:
3756 	case SCI_VCHOMEEXTEND:
3757 	case SCI_VCHOMERECTEXTEND:
3758 	case SCI_VCHOMEDISPLAY:
3759 	case SCI_VCHOMEDISPLAYEXTEND:
3760 	case SCI_VCHOMEWRAP:
3761 	case SCI_VCHOMEWRAPEXTEND:
3762 	case SCI_LINEEND:
3763 	case SCI_LINEENDEXTEND:
3764 	case SCI_LINEENDRECTEXTEND:
3765 	case SCI_LINEENDDISPLAY:
3766 	case SCI_LINEENDDISPLAYEXTEND:
3767 	case SCI_LINEENDWRAP:
3768 	case SCI_LINEENDWRAPEXTEND:
3769 		return HorizontalMove(iMessage);
3770 
3771 	case SCI_DOCUMENTSTART:
3772 		MovePositionTo(0);
3773 		SetLastXChosen();
3774 		break;
3775 	case SCI_DOCUMENTSTARTEXTEND:
3776 		MovePositionTo(0, Selection::selStream);
3777 		SetLastXChosen();
3778 		break;
3779 	case SCI_DOCUMENTEND:
3780 		MovePositionTo(pdoc->Length());
3781 		SetLastXChosen();
3782 		break;
3783 	case SCI_DOCUMENTENDEXTEND:
3784 		MovePositionTo(pdoc->Length(), Selection::selStream);
3785 		SetLastXChosen();
3786 		break;
3787 	case SCI_STUTTEREDPAGEUP:
3788 		PageMove(-1, Selection::noSel, true);
3789 		break;
3790 	case SCI_STUTTEREDPAGEUPEXTEND:
3791 		PageMove(-1, Selection::selStream, true);
3792 		break;
3793 	case SCI_STUTTEREDPAGEDOWN:
3794 		PageMove(1, Selection::noSel, true);
3795 		break;
3796 	case SCI_STUTTEREDPAGEDOWNEXTEND:
3797 		PageMove(1, Selection::selStream, true);
3798 		break;
3799 	case SCI_PAGEUP:
3800 		PageMove(-1);
3801 		break;
3802 	case SCI_PAGEUPEXTEND:
3803 		PageMove(-1, Selection::selStream);
3804 		break;
3805 	case SCI_PAGEUPRECTEXTEND:
3806 		PageMove(-1, Selection::selRectangle);
3807 		break;
3808 	case SCI_PAGEDOWN:
3809 		PageMove(1);
3810 		break;
3811 	case SCI_PAGEDOWNEXTEND:
3812 		PageMove(1, Selection::selStream);
3813 		break;
3814 	case SCI_PAGEDOWNRECTEXTEND:
3815 		PageMove(1, Selection::selRectangle);
3816 		break;
3817 	case SCI_EDITTOGGLEOVERTYPE:
3818 		inOverstrike = !inOverstrike;
3819 		ContainerNeedsUpdate(SC_UPDATE_SELECTION);
3820 		ShowCaretAtCurrentPosition();
3821 		SetIdle(true);
3822 		break;
3823 	case SCI_CANCEL:            	// Cancel any modes - handled in subclass
3824 		// Also unselect text
3825 		CancelModes();
3826 		if ((sel.Count() > 1) && !sel.IsRectangular()) {
3827 			// Drop additional selections
3828 			InvalidateWholeSelection();
3829 			sel.DropAdditionalRanges();
3830 		}
3831 		break;
3832 	case SCI_DELETEBACK:
3833 		DelCharBack(true);
3834 		if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
3835 			SetLastXChosen();
3836 		}
3837 		EnsureCaretVisible();
3838 		break;
3839 	case SCI_DELETEBACKNOTLINE:
3840 		DelCharBack(false);
3841 		if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
3842 			SetLastXChosen();
3843 		}
3844 		EnsureCaretVisible();
3845 		break;
3846 	case SCI_TAB:
3847 		Indent(true);
3848 		if (caretSticky == SC_CARETSTICKY_OFF) {
3849 			SetLastXChosen();
3850 		}
3851 		EnsureCaretVisible();
3852 		ShowCaretAtCurrentPosition();		// Avoid blinking
3853 		break;
3854 	case SCI_BACKTAB:
3855 		Indent(false);
3856 		if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
3857 			SetLastXChosen();
3858 		}
3859 		EnsureCaretVisible();
3860 		ShowCaretAtCurrentPosition();		// Avoid blinking
3861 		break;
3862 	case SCI_NEWLINE:
3863 		NewLine();
3864 		break;
3865 	case SCI_FORMFEED:
3866 		AddChar('\f');
3867 		break;
3868 	case SCI_ZOOMIN:
3869 		if (vs.zoomLevel < 20) {
3870 			vs.zoomLevel++;
3871 			InvalidateStyleRedraw();
3872 			NotifyZoom();
3873 		}
3874 		break;
3875 	case SCI_ZOOMOUT:
3876 		if (vs.zoomLevel > -10) {
3877 			vs.zoomLevel--;
3878 			InvalidateStyleRedraw();
3879 			NotifyZoom();
3880 		}
3881 		break;
3882 
3883 	case SCI_DELWORDLEFT:
3884 	case SCI_DELWORDRIGHT:
3885 	case SCI_DELWORDRIGHTEND:
3886 	case SCI_DELLINELEFT:
3887 	case SCI_DELLINERIGHT:
3888 		return DelWordOrLine(iMessage);
3889 
3890 	case SCI_LINECOPY: {
3891 			const Sci::Line lineStart = pdoc->SciLineFromPosition(SelectionStart().Position());
3892 			const Sci::Line lineEnd = pdoc->SciLineFromPosition(SelectionEnd().Position());
3893 			CopyRangeToClipboard(pdoc->LineStart(lineStart),
3894 				pdoc->LineStart(lineEnd + 1));
3895 		}
3896 		break;
3897 	case SCI_LINECUT: {
3898 			const Sci::Line lineStart = pdoc->SciLineFromPosition(SelectionStart().Position());
3899 			const Sci::Line lineEnd = pdoc->SciLineFromPosition(SelectionEnd().Position());
3900 			const Sci::Position start = pdoc->LineStart(lineStart);
3901 			const Sci::Position end = pdoc->LineStart(lineEnd + 1);
3902 			SetSelection(start, end);
3903 			Cut();
3904 			SetLastXChosen();
3905 		}
3906 		break;
3907 	case SCI_LINEDELETE: {
3908 			const Sci::Line line = pdoc->SciLineFromPosition(sel.MainCaret());
3909 			const Sci::Position start = pdoc->LineStart(line);
3910 			const Sci::Position end = pdoc->LineStart(line + 1);
3911 			pdoc->DeleteChars(start, end - start);
3912 		}
3913 		break;
3914 	case SCI_LINETRANSPOSE:
3915 		LineTranspose();
3916 		break;
3917 	case SCI_LINEREVERSE:
3918 		LineReverse();
3919 		break;
3920 	case SCI_LINEDUPLICATE:
3921 		Duplicate(true);
3922 		break;
3923 	case SCI_SELECTIONDUPLICATE:
3924 		Duplicate(false);
3925 		break;
3926 	case SCI_LOWERCASE:
3927 		ChangeCaseOfSelection(cmLower);
3928 		break;
3929 	case SCI_UPPERCASE:
3930 		ChangeCaseOfSelection(cmUpper);
3931 		break;
3932 	case SCI_SCROLLTOSTART:
3933 		ScrollTo(0);
3934 		break;
3935 	case SCI_SCROLLTOEND:
3936 		ScrollTo(MaxScrollPos());
3937 		break;
3938 	}
3939 	return 0;
3940 }
3941 
KeyDefault(int,int)3942 int Editor::KeyDefault(int, int) {
3943 	return 0;
3944 }
3945 
KeyDownWithModifiers(int key,int modifiers,bool * consumed)3946 int Editor::KeyDownWithModifiers(int key, int modifiers, bool *consumed) {
3947 	DwellEnd(false);
3948 	const int msg = kmap.Find(key, modifiers);
3949 	if (msg) {
3950 		if (consumed)
3951 			*consumed = true;
3952 		return static_cast<int>(WndProc(msg, 0, 0));
3953 	} else {
3954 		if (consumed)
3955 			*consumed = false;
3956 		return KeyDefault(key, modifiers);
3957 	}
3958 }
3959 
Indent(bool forwards)3960 void Editor::Indent(bool forwards) {
3961 	UndoGroup ug(pdoc);
3962 	for (size_t r=0; r<sel.Count(); r++) {
3963 		const Sci::Line lineOfAnchor =
3964 			pdoc->SciLineFromPosition(sel.Range(r).anchor.Position());
3965 		Sci::Position caretPosition = sel.Range(r).caret.Position();
3966 		const Sci::Line lineCurrentPos = pdoc->SciLineFromPosition(caretPosition);
3967 		if (lineOfAnchor == lineCurrentPos) {
3968 			if (forwards) {
3969 				pdoc->DeleteChars(sel.Range(r).Start().Position(), sel.Range(r).Length());
3970 				caretPosition = sel.Range(r).caret.Position();
3971 				if (pdoc->GetColumn(caretPosition) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
3972 						pdoc->tabIndents) {
3973 					const int indentation = pdoc->GetLineIndentation(lineCurrentPos);
3974 					const int indentationStep = pdoc->IndentSize();
3975 					const Sci::Position posSelect = pdoc->SetLineIndentation(
3976 						lineCurrentPos, indentation + indentationStep - indentation % indentationStep);
3977 					sel.Range(r) = SelectionRange(posSelect);
3978 				} else {
3979 					if (pdoc->useTabs) {
3980 						const Sci::Position lengthInserted = pdoc->InsertString(caretPosition, "\t", 1);
3981 						sel.Range(r) = SelectionRange(caretPosition + lengthInserted);
3982 					} else {
3983 						int numSpaces = (pdoc->tabInChars) -
3984 								(pdoc->GetColumn(caretPosition) % (pdoc->tabInChars));
3985 						if (numSpaces < 1)
3986 							numSpaces = pdoc->tabInChars;
3987 						const std::string spaceText(numSpaces, ' ');
3988 						const Sci::Position lengthInserted = pdoc->InsertString(caretPosition, spaceText.c_str(),
3989 							spaceText.length());
3990 						sel.Range(r) = SelectionRange(caretPosition + lengthInserted);
3991 					}
3992 				}
3993 			} else {
3994 				if (pdoc->GetColumn(caretPosition) <= pdoc->GetLineIndentation(lineCurrentPos) &&
3995 						pdoc->tabIndents) {
3996 					const int indentation = pdoc->GetLineIndentation(lineCurrentPos);
3997 					const int indentationStep = pdoc->IndentSize();
3998 					const Sci::Position posSelect = pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
3999 					sel.Range(r) = SelectionRange(posSelect);
4000 				} else {
4001 					Sci::Position newColumn = ((pdoc->GetColumn(caretPosition) - 1) / pdoc->tabInChars) *
4002 							pdoc->tabInChars;
4003 					if (newColumn < 0)
4004 						newColumn = 0;
4005 					Sci::Position newPos = caretPosition;
4006 					while (pdoc->GetColumn(newPos) > newColumn)
4007 						newPos--;
4008 					sel.Range(r) = SelectionRange(newPos);
4009 				}
4010 			}
4011 		} else {	// Multiline
4012 			const Sci::Position anchorPosOnLine = sel.Range(r).anchor.Position() -
4013 				pdoc->LineStart(lineOfAnchor);
4014 			const Sci::Position currentPosPosOnLine = caretPosition -
4015 				pdoc->LineStart(lineCurrentPos);
4016 			// Multiple lines selected so indent / dedent
4017 			const Sci::Line lineTopSel = std::min(lineOfAnchor, lineCurrentPos);
4018 			Sci::Line lineBottomSel = std::max(lineOfAnchor, lineCurrentPos);
4019 			if (pdoc->LineStart(lineBottomSel) == sel.Range(r).anchor.Position() || pdoc->LineStart(lineBottomSel) == caretPosition)
4020 				lineBottomSel--;  	// If not selecting any characters on a line, do not indent
4021 			pdoc->Indent(forwards, lineBottomSel, lineTopSel);
4022 			if (lineOfAnchor < lineCurrentPos) {
4023 				if (currentPosPosOnLine == 0)
4024 					sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos),
4025 						pdoc->LineStart(lineOfAnchor));
4026 				else
4027 					sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos + 1),
4028 						pdoc->LineStart(lineOfAnchor));
4029 			} else {
4030 				if (anchorPosOnLine == 0)
4031 					sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos),
4032 						pdoc->LineStart(lineOfAnchor));
4033 				else
4034 					sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos),
4035 						pdoc->LineStart(lineOfAnchor + 1));
4036 			}
4037 		}
4038 	}
4039 	ContainerNeedsUpdate(SC_UPDATE_SELECTION);
4040 }
4041 
4042 class CaseFolderASCII : public CaseFolderTable {
4043 public:
CaseFolderASCII()4044 	CaseFolderASCII() noexcept {
4045 		StandardASCII();
4046 	}
4047 };
4048 
4049 
CaseFolderForEncoding()4050 CaseFolder *Editor::CaseFolderForEncoding() {
4051 	// Simple default that only maps ASCII upper case to lower case.
4052 	return new CaseFolderASCII();
4053 }
4054 
4055 /**
4056  * Search of a text in the document, in the given range.
4057  * @return The position of the found text, -1 if not found.
4058  */
FindText(uptr_t wParam,sptr_t lParam)4059 Sci::Position Editor::FindText(
4060     uptr_t wParam,		///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
4061     ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
4062     sptr_t lParam) {	///< @c Sci_TextToFind structure: The text to search for in the given range.
4063 
4064 	Sci_TextToFind *ft = static_cast<Sci_TextToFind *>(PtrFromSPtr(lParam));
4065 	Sci::Position lengthFound = strlen(ft->lpstrText);
4066 	if (!pdoc->HasCaseFolder())
4067 		pdoc->SetCaseFolder(CaseFolderForEncoding());
4068 	try {
4069 		const Sci::Position pos = pdoc->FindText(
4070 			static_cast<Sci::Position>(ft->chrg.cpMin),
4071 			static_cast<Sci::Position>(ft->chrg.cpMax),
4072 			ft->lpstrText,
4073 			static_cast<int>(wParam),
4074 			&lengthFound);
4075 		if (pos != -1) {
4076 			ft->chrgText.cpMin = static_cast<Sci_PositionCR>(pos);
4077 			ft->chrgText.cpMax = static_cast<Sci_PositionCR>(pos + lengthFound);
4078 		}
4079 		return pos;
4080 	} catch (RegexError &) {
4081 		errorStatus = SC_STATUS_WARN_REGEX;
4082 		return -1;
4083 	}
4084 }
4085 
4086 /**
4087  * Relocatable search support : Searches relative to current selection
4088  * point and sets the selection to the found text range with
4089  * each search.
4090  */
4091 /**
4092  * Anchor following searches at current selection start: This allows
4093  * multiple incremental interactive searches to be macro recorded
4094  * while still setting the selection to found text so the find/select
4095  * operation is self-contained.
4096  */
SearchAnchor()4097 void Editor::SearchAnchor() {
4098 	searchAnchor = SelectionStart().Position();
4099 }
4100 
4101 /**
4102  * Find text from current search anchor: Must call @c SearchAnchor first.
4103  * Used for next text and previous text requests.
4104  * @return The position of the found text, -1 if not found.
4105  */
SearchText(unsigned int iMessage,uptr_t wParam,sptr_t lParam)4106 Sci::Position Editor::SearchText(
4107     unsigned int iMessage,		///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV.
4108     uptr_t wParam,				///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
4109     ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
4110     sptr_t lParam) {			///< The text to search for.
4111 
4112 	const char *txt = CharPtrFromSPtr(lParam);
4113 	Sci::Position pos = INVALID_POSITION;
4114 	Sci::Position lengthFound = strlen(txt);
4115 	if (!pdoc->HasCaseFolder())
4116 		pdoc->SetCaseFolder(CaseFolderForEncoding());
4117 	try {
4118 		if (iMessage == SCI_SEARCHNEXT) {
4119 			pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
4120 					static_cast<int>(wParam),
4121 					&lengthFound);
4122 		} else {
4123 			pos = pdoc->FindText(searchAnchor, 0, txt,
4124 					static_cast<int>(wParam),
4125 					&lengthFound);
4126 		}
4127 	} catch (RegexError &) {
4128 		errorStatus = SC_STATUS_WARN_REGEX;
4129 		return INVALID_POSITION;
4130 	}
4131 	if (pos != INVALID_POSITION) {
4132 		SetSelection(pos, pos + lengthFound);
4133 	}
4134 
4135 	return pos;
4136 }
4137 
CaseMapString(const std::string & s,int caseMapping)4138 std::string Editor::CaseMapString(const std::string &s, int caseMapping) {
4139 	std::string ret(s);
4140 	for (char &ch : ret) {
4141 		switch (caseMapping) {
4142 			case cmUpper:
4143 				ch = MakeUpperCase(ch);
4144 				break;
4145 			case cmLower:
4146 				ch = MakeLowerCase(ch);
4147 				break;
4148 		}
4149 	}
4150 	return ret;
4151 }
4152 
4153 /**
4154  * Search for text in the target range of the document.
4155  * @return The position of the found text, -1 if not found.
4156  */
SearchInTarget(const char * text,Sci::Position length)4157 Sci::Position Editor::SearchInTarget(const char *text, Sci::Position length) {
4158 	Sci::Position lengthFound = length;
4159 
4160 	if (!pdoc->HasCaseFolder())
4161 		pdoc->SetCaseFolder(CaseFolderForEncoding());
4162 	try {
4163 		const Sci::Position pos = pdoc->FindText(targetRange.start.Position(), targetRange.end.Position(), text,
4164 				searchFlags,
4165 				&lengthFound);
4166 		if (pos != -1) {
4167 			targetRange.start.SetPosition(pos);
4168 			targetRange.end.SetPosition(pos + lengthFound);
4169 		}
4170 		return pos;
4171 	} catch (RegexError &) {
4172 		errorStatus = SC_STATUS_WARN_REGEX;
4173 		return -1;
4174 	}
4175 }
4176 
GoToLine(Sci::Line lineNo)4177 void Editor::GoToLine(Sci::Line lineNo) {
4178 	if (lineNo > pdoc->LinesTotal())
4179 		lineNo = pdoc->LinesTotal();
4180 	if (lineNo < 0)
4181 		lineNo = 0;
4182 	SetEmptySelection(pdoc->LineStart(lineNo));
4183 	ShowCaretAtCurrentPosition();
4184 	EnsureCaretVisible();
4185 }
4186 
Close(Point pt1,Point pt2,Point threshold)4187 static bool Close(Point pt1, Point pt2, Point threshold) noexcept {
4188 	const Point ptDifference = pt2 - pt1;
4189 	if (std::abs(ptDifference.x) > threshold.x)
4190 		return false;
4191 	if (std::abs(ptDifference.y) > threshold.y)
4192 		return false;
4193 	return true;
4194 }
4195 
RangeText(Sci::Position start,Sci::Position end) const4196 std::string Editor::RangeText(Sci::Position start, Sci::Position end) const {
4197 	if (start < end) {
4198 		const Sci::Position len = end - start;
4199 		std::string ret(len, '\0');
4200 		pdoc->GetCharRange(ret.data(), start, len);
4201 		return ret;
4202 	}
4203 	return std::string();
4204 }
4205 
CopySelectionRange(SelectionText * ss,bool allowLineCopy)4206 void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) {
4207 	if (sel.Empty()) {
4208 		if (allowLineCopy) {
4209 			const Sci::Line currentLine = pdoc->SciLineFromPosition(sel.MainCaret());
4210 			const Sci::Position start = pdoc->LineStart(currentLine);
4211 			const Sci::Position end = pdoc->LineEnd(currentLine);
4212 
4213 			std::string text = RangeText(start, end);
4214 			if (pdoc->eolMode != SC_EOL_LF)
4215 				text.push_back('\r');
4216 			if (pdoc->eolMode != SC_EOL_CR)
4217 				text.push_back('\n');
4218 			ss->Copy(text, pdoc->dbcsCodePage,
4219 				vs.styles[STYLE_DEFAULT].characterSet, false, true);
4220 		}
4221 	} else {
4222 		std::string text;
4223 		std::vector<SelectionRange> rangesInOrder = sel.RangesCopy();
4224 		if (sel.selType == Selection::selRectangle)
4225 			std::sort(rangesInOrder.begin(), rangesInOrder.end());
4226 		for (const SelectionRange &current : rangesInOrder) {
4227 				text.append(RangeText(current.Start().Position(), current.End().Position()));
4228 			if (sel.selType == Selection::selRectangle) {
4229 				if (pdoc->eolMode != SC_EOL_LF)
4230 					text.push_back('\r');
4231 				if (pdoc->eolMode != SC_EOL_CR)
4232 					text.push_back('\n');
4233 			}
4234 		}
4235 		ss->Copy(text, pdoc->dbcsCodePage,
4236 			vs.styles[STYLE_DEFAULT].characterSet, sel.IsRectangular(), sel.selType == Selection::selLines);
4237 	}
4238 }
4239 
CopyRangeToClipboard(Sci::Position start,Sci::Position end)4240 void Editor::CopyRangeToClipboard(Sci::Position start, Sci::Position end) {
4241 	start = pdoc->ClampPositionIntoDocument(start);
4242 	end = pdoc->ClampPositionIntoDocument(end);
4243 	SelectionText selectedText;
4244 	std::string text = RangeText(start, end);
4245 	selectedText.Copy(text,
4246 		pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
4247 	CopyToClipboard(selectedText);
4248 }
4249 
CopyText(size_t length,const char * text)4250 void Editor::CopyText(size_t length, const char *text) {
4251 	SelectionText selectedText;
4252 	selectedText.Copy(std::string(text, length),
4253 		pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
4254 	CopyToClipboard(selectedText);
4255 }
4256 
SetDragPosition(SelectionPosition newPos)4257 void Editor::SetDragPosition(SelectionPosition newPos) {
4258 	if (newPos.Position() >= 0) {
4259 		newPos = MovePositionOutsideChar(newPos, 1);
4260 		posDrop = newPos;
4261 	}
4262 	if (!(posDrag == newPos)) {
4263 		const CaretPolicies dragCaretPolicies = {
4264 			CaretPolicy(CARET_SLOP | CARET_STRICT | CARET_EVEN, 50),
4265 			CaretPolicy(CARET_SLOP | CARET_STRICT | CARET_EVEN, 2)
4266 		};
4267 		MovedCaret(newPos, posDrag, true, dragCaretPolicies);
4268 
4269 		caret.on = true;
4270 		FineTickerCancel(tickCaret);
4271 		if ((caret.active) && (caret.period > 0) && (newPos.Position() < 0))
4272 			FineTickerStart(tickCaret, caret.period, caret.period/10);
4273 		InvalidateCaret();
4274 		posDrag = newPos;
4275 		InvalidateCaret();
4276 	}
4277 }
4278 
DisplayCursor(Window::Cursor c)4279 void Editor::DisplayCursor(Window::Cursor c) {
4280 	if (cursorMode == SC_CURSORNORMAL)
4281 		wMain.SetCursor(c);
4282 	else
4283 		wMain.SetCursor(static_cast<Window::Cursor>(cursorMode));
4284 }
4285 
DragThreshold(Point ptStart,Point ptNow)4286 bool Editor::DragThreshold(Point ptStart, Point ptNow) {
4287 	const Point ptDiff = ptStart - ptNow;
4288 	const XYPOSITION distanceSquared = ptDiff.x * ptDiff.x + ptDiff.y * ptDiff.y;
4289 	return distanceSquared > 16.0f;
4290 }
4291 
StartDrag()4292 void Editor::StartDrag() {
4293 	// Always handled by subclasses
4294 	//SetMouseCapture(true);
4295 	//DisplayCursor(Window::cursorArrow);
4296 }
4297 
DropAt(SelectionPosition position,const char * value,size_t lengthValue,bool moving,bool rectangular)4298 void Editor::DropAt(SelectionPosition position, const char *value, size_t lengthValue, bool moving, bool rectangular) {
4299 	//Platform::DebugPrintf("DropAt %d %d\n", inDragDrop, position);
4300 	if (inDragDrop == ddDragging)
4301 		dropWentOutside = false;
4302 
4303 	const bool positionWasInSelection = PositionInSelection(position.Position());
4304 
4305 	const bool positionOnEdgeOfSelection =
4306 	    (position == SelectionStart()) || (position == SelectionEnd());
4307 
4308 	if ((inDragDrop != ddDragging) || !(positionWasInSelection) ||
4309 	        (positionOnEdgeOfSelection && !moving)) {
4310 
4311 		const SelectionPosition selStart = SelectionStart();
4312 		const SelectionPosition selEnd = SelectionEnd();
4313 
4314 		UndoGroup ug(pdoc);
4315 
4316 		SelectionPosition positionAfterDeletion = position;
4317 		if ((inDragDrop == ddDragging) && moving) {
4318 			// Remove dragged out text
4319 			if (rectangular || sel.selType == Selection::selLines) {
4320 				for (size_t r=0; r<sel.Count(); r++) {
4321 					if (position >= sel.Range(r).Start()) {
4322 						if (position > sel.Range(r).End()) {
4323 							positionAfterDeletion.Add(-sel.Range(r).Length());
4324 						} else {
4325 							positionAfterDeletion.Add(-SelectionRange(position, sel.Range(r).Start()).Length());
4326 						}
4327 					}
4328 				}
4329 			} else {
4330 				if (position > selStart) {
4331 					positionAfterDeletion.Add(-SelectionRange(selEnd, selStart).Length());
4332 				}
4333 			}
4334 			ClearSelection();
4335 		}
4336 		position = positionAfterDeletion;
4337 
4338 		std::string convertedText = Document::TransformLineEnds(value, lengthValue, pdoc->eolMode);
4339 
4340 		if (rectangular) {
4341 			PasteRectangular(position, convertedText.c_str(), convertedText.length());
4342 			// Should try to select new rectangle but it may not be a rectangle now so just select the drop position
4343 			SetEmptySelection(position);
4344 		} else {
4345 			position = MovePositionOutsideChar(position, sel.MainCaret() - position.Position());
4346 			position = RealizeVirtualSpace(position);
4347 			const Sci::Position lengthInserted = pdoc->InsertString(
4348 				position.Position(), convertedText.c_str(), convertedText.length());
4349 			if (lengthInserted > 0) {
4350 				SelectionPosition posAfterInsertion = position;
4351 				posAfterInsertion.Add(lengthInserted);
4352 				SetSelection(posAfterInsertion, position);
4353 			}
4354 		}
4355 	} else if (inDragDrop == ddDragging) {
4356 		SetEmptySelection(position);
4357 	}
4358 }
4359 
DropAt(SelectionPosition position,const char * value,bool moving,bool rectangular)4360 void Editor::DropAt(SelectionPosition position, const char *value, bool moving, bool rectangular) {
4361 	DropAt(position, value, strlen(value), moving, rectangular);
4362 }
4363 
4364 /**
4365  * @return true if given position is inside the selection,
4366  */
PositionInSelection(Sci::Position pos)4367 bool Editor::PositionInSelection(Sci::Position pos) {
4368 	pos = MovePositionOutsideChar(pos, sel.MainCaret() - pos);
4369 	for (size_t r=0; r<sel.Count(); r++) {
4370 		if (sel.Range(r).Contains(pos))
4371 			return true;
4372 	}
4373 	return false;
4374 }
4375 
PointInSelection(Point pt)4376 bool Editor::PointInSelection(Point pt) {
4377 	const SelectionPosition pos = SPositionFromLocation(pt, false, true);
4378 	const Point ptPos = LocationFromPosition(pos);
4379 	for (size_t r=0; r<sel.Count(); r++) {
4380 		const SelectionRange &range = sel.Range(r);
4381 		if (range.Contains(pos)) {
4382 			bool hit = true;
4383 			if (pos == range.Start()) {
4384 				// see if just before selection
4385 				if (pt.x < ptPos.x) {
4386 					hit = false;
4387 				}
4388 			}
4389 			if (pos == range.End()) {
4390 				// see if just after selection
4391 				if (pt.x > ptPos.x) {
4392 					hit = false;
4393 				}
4394 			}
4395 			if (hit)
4396 				return true;
4397 		}
4398 	}
4399 	return false;
4400 }
4401 
PointInSelMargin(Point pt) const4402 bool Editor::PointInSelMargin(Point pt) const {
4403 	// Really means: "Point in a margin"
4404 	if (vs.fixedColumnWidth > 0) {	// There is a margin
4405 		PRectangle rcSelMargin = GetClientRectangle();
4406 		rcSelMargin.right = static_cast<XYPOSITION>(vs.textStart - vs.leftMarginWidth);
4407 		rcSelMargin.left = static_cast<XYPOSITION>(vs.textStart - vs.fixedColumnWidth);
4408 		const Point ptOrigin = GetVisibleOriginInMain();
4409 		rcSelMargin.Move(0, -ptOrigin.y);
4410 		return rcSelMargin.ContainsWholePixel(pt);
4411 	} else {
4412 		return false;
4413 	}
4414 }
4415 
GetMarginCursor(Point pt) const4416 Window::Cursor Editor::GetMarginCursor(Point pt) const noexcept {
4417 	int x = 0;
4418 	for (const MarginStyle &m : vs.ms) {
4419 		if ((pt.x >= x) && (pt.x < x + m.width))
4420 			return static_cast<Window::Cursor>(m.cursor);
4421 		x += m.width;
4422 	}
4423 	return Window::cursorReverseArrow;
4424 }
4425 
TrimAndSetSelection(Sci::Position currentPos_,Sci::Position anchor_)4426 void Editor::TrimAndSetSelection(Sci::Position currentPos_, Sci::Position anchor_) {
4427 	sel.TrimSelection(SelectionRange(currentPos_, anchor_));
4428 	SetSelection(currentPos_, anchor_);
4429 }
4430 
LineSelection(Sci::Position lineCurrentPos_,Sci::Position lineAnchorPos_,bool wholeLine)4431 void Editor::LineSelection(Sci::Position lineCurrentPos_, Sci::Position lineAnchorPos_, bool wholeLine) {
4432 	Sci::Position selCurrentPos, selAnchorPos;
4433 	if (wholeLine) {
4434 		const Sci::Line lineCurrent_ = pdoc->SciLineFromPosition(lineCurrentPos_);
4435 		const Sci::Line lineAnchor_ = pdoc->SciLineFromPosition(lineAnchorPos_);
4436 		if (lineAnchorPos_ < lineCurrentPos_) {
4437 			selCurrentPos = pdoc->LineStart(lineCurrent_ + 1);
4438 			selAnchorPos = pdoc->LineStart(lineAnchor_);
4439 		} else if (lineAnchorPos_ > lineCurrentPos_) {
4440 			selCurrentPos = pdoc->LineStart(lineCurrent_);
4441 			selAnchorPos = pdoc->LineStart(lineAnchor_ + 1);
4442 		} else { // Same line, select it
4443 			selCurrentPos = pdoc->LineStart(lineAnchor_ + 1);
4444 			selAnchorPos = pdoc->LineStart(lineAnchor_);
4445 		}
4446 	} else {
4447 		if (lineAnchorPos_ < lineCurrentPos_) {
4448 			selCurrentPos = StartEndDisplayLine(lineCurrentPos_, false) + 1;
4449 			selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
4450 			selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
4451 		} else if (lineAnchorPos_ > lineCurrentPos_) {
4452 			selCurrentPos = StartEndDisplayLine(lineCurrentPos_, true);
4453 			selAnchorPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
4454 			selAnchorPos = pdoc->MovePositionOutsideChar(selAnchorPos, 1);
4455 		} else { // Same line, select it
4456 			selCurrentPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
4457 			selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
4458 			selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
4459 		}
4460 	}
4461 	TrimAndSetSelection(selCurrentPos, selAnchorPos);
4462 }
4463 
WordSelection(Sci::Position pos)4464 void Editor::WordSelection(Sci::Position pos) {
4465 	if (pos < wordSelectAnchorStartPos) {
4466 		// Extend backward to the word containing pos.
4467 		// Skip ExtendWordSelect if the line is empty or if pos is after the last character.
4468 		// This ensures that a series of empty lines isn't counted as a single "word".
4469 		if (!pdoc->IsLineEndPosition(pos))
4470 			pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos + 1, 1), -1);
4471 		TrimAndSetSelection(pos, wordSelectAnchorEndPos);
4472 	} else if (pos > wordSelectAnchorEndPos) {
4473 		// Extend forward to the word containing the character to the left of pos.
4474 		// Skip ExtendWordSelect if the line is empty or if pos is the first position on the line.
4475 		// This ensures that a series of empty lines isn't counted as a single "word".
4476 		if (pos > pdoc->LineStart(pdoc->LineFromPosition(pos)))
4477 			pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos - 1, -1), 1);
4478 		TrimAndSetSelection(pos, wordSelectAnchorStartPos);
4479 	} else {
4480 		// Select only the anchored word
4481 		if (pos >= originalAnchorPos)
4482 			TrimAndSetSelection(wordSelectAnchorEndPos, wordSelectAnchorStartPos);
4483 		else
4484 			TrimAndSetSelection(wordSelectAnchorStartPos, wordSelectAnchorEndPos);
4485 	}
4486 }
4487 
DwellEnd(bool mouseMoved)4488 void Editor::DwellEnd(bool mouseMoved) {
4489 	if (mouseMoved)
4490 		ticksToDwell = dwellDelay;
4491 	else
4492 		ticksToDwell = SC_TIME_FOREVER;
4493 	if (dwelling && (dwellDelay < SC_TIME_FOREVER)) {
4494 		dwelling = false;
4495 		NotifyDwelling(ptMouseLast, dwelling);
4496 	}
4497 	FineTickerCancel(tickDwell);
4498 }
4499 
MouseLeave()4500 void Editor::MouseLeave() {
4501 	SetHotSpotRange(nullptr);
4502 	if (!HaveMouseCapture()) {
4503 		ptMouseLast = Point(-1, -1);
4504 		DwellEnd(true);
4505 	}
4506 }
4507 
AllowVirtualSpace(int virtualSpaceOptions,bool rectangular)4508 static constexpr bool AllowVirtualSpace(int virtualSpaceOptions, bool rectangular) noexcept {
4509 	return (!rectangular && ((virtualSpaceOptions & SCVS_USERACCESSIBLE) != 0))
4510 		|| (rectangular && ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) != 0));
4511 }
4512 
ButtonDownWithModifiers(Point pt,unsigned int curTime,int modifiers)4513 void Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, int modifiers) {
4514 	SetHoverIndicatorPoint(pt);
4515 	//Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);
4516 	ptMouseLast = pt;
4517 	const bool ctrl = (modifiers & SCI_CTRL) != 0;
4518 	const bool shift = (modifiers & SCI_SHIFT) != 0;
4519 	const bool alt = (modifiers & SCI_ALT) != 0;
4520 	SelectionPosition newPos = SPositionFromLocation(pt, false, false, AllowVirtualSpace(virtualSpaceOptions, alt));
4521 	newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
4522 	SelectionPosition newCharPos = SPositionFromLocation(pt, false, true, false);
4523 	newCharPos = MovePositionOutsideChar(newCharPos, -1);
4524 	inDragDrop = ddNone;
4525 	sel.SetMoveExtends(false);
4526 
4527 	if (NotifyMarginClick(pt, modifiers))
4528 		return;
4529 
4530 	NotifyIndicatorClick(true, newPos.Position(), modifiers);
4531 
4532 	const bool inSelMargin = PointInSelMargin(pt);
4533 	// In margin ctrl+(double)click should always select everything
4534 	if (ctrl && inSelMargin) {
4535 		SelectAll();
4536 		lastClickTime = curTime;
4537 		lastClick = pt;
4538 		return;
4539 	}
4540 	if (shift && !inSelMargin) {
4541 		SetSelection(newPos);
4542 	}
4543 	if ((curTime < (lastClickTime+Platform::DoubleClickTime())) && Close(pt, lastClick, doubleClickCloseThreshold)) {
4544 		//Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
4545 		SetMouseCapture(true);
4546 		FineTickerStart(tickScroll, 100, 10);
4547 		if (!ctrl || !multipleSelection || (selectionType != selChar && selectionType != selWord))
4548 			SetEmptySelection(newPos.Position());
4549 		bool doubleClick = false;
4550 		if (inSelMargin) {
4551 			// Inside margin selection type should be either selSubLine or selWholeLine.
4552 			if (selectionType == selSubLine) {
4553 				// If it is selSubLine, we're inside a *double* click and word wrap is enabled,
4554 				// so we switch to selWholeLine in order to select whole line.
4555 				selectionType = selWholeLine;
4556 			} else if (selectionType != selSubLine && selectionType != selWholeLine) {
4557 				// If it is neither, reset selection type to line selection.
4558 				selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
4559 			}
4560 		} else {
4561 			if (selectionType == selChar) {
4562 				selectionType = selWord;
4563 				doubleClick = true;
4564 			} else if (selectionType == selWord) {
4565 				// Since we ended up here, we're inside a *triple* click, which should always select
4566 				// whole line regardless of word wrap being enabled or not.
4567 				selectionType = selWholeLine;
4568 			} else {
4569 				selectionType = selChar;
4570 				originalAnchorPos = sel.MainCaret();
4571 			}
4572 		}
4573 
4574 		if (selectionType == selWord) {
4575 			Sci::Position charPos = originalAnchorPos;
4576 			if (sel.MainCaret() == originalAnchorPos) {
4577 				charPos = PositionFromLocation(pt, false, true);
4578 				charPos = MovePositionOutsideChar(charPos, -1);
4579 			}
4580 
4581 			Sci::Position startWord, endWord;
4582 			if ((sel.MainCaret() >= originalAnchorPos) && !pdoc->IsLineEndPosition(charPos)) {
4583 				startWord = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(charPos + 1, 1), -1);
4584 				endWord = pdoc->ExtendWordSelect(charPos, 1);
4585 			} else {
4586 				// Selecting backwards, or anchor beyond last character on line. In these cases,
4587 				// we select the word containing the character to the *left* of the anchor.
4588 				if (charPos > pdoc->LineStart(pdoc->LineFromPosition(charPos))) {
4589 					startWord = pdoc->ExtendWordSelect(charPos, -1);
4590 					endWord = pdoc->ExtendWordSelect(startWord, 1);
4591 				} else {
4592 					// Anchor at start of line; select nothing to begin with.
4593 					startWord = charPos;
4594 					endWord = charPos;
4595 				}
4596 			}
4597 
4598 			wordSelectAnchorStartPos = startWord;
4599 			wordSelectAnchorEndPos = endWord;
4600 			wordSelectInitialCaretPos = sel.MainCaret();
4601 			WordSelection(wordSelectInitialCaretPos);
4602 		} else if (selectionType == selSubLine || selectionType == selWholeLine) {
4603 			lineAnchorPos = newPos.Position();
4604 			LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
4605 			//Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
4606 		} else {
4607 			SetEmptySelection(sel.MainCaret());
4608 		}
4609 		//Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
4610 		if (doubleClick) {
4611 			NotifyDoubleClick(pt, modifiers);
4612 			if (PositionIsHotspot(newCharPos.Position()))
4613 				NotifyHotSpotDoubleClicked(newCharPos.Position(), modifiers);
4614 		}
4615 	} else {	// Single click
4616 		if (inSelMargin) {
4617 			if (sel.IsRectangular() || (sel.Count() > 1)) {
4618 				InvalidateWholeSelection();
4619 				sel.Clear();
4620 			}
4621 			sel.selType = Selection::selStream;
4622 			if (!shift) {
4623 				// Single click in margin: select whole line or only subline if word wrap is enabled
4624 				lineAnchorPos = newPos.Position();
4625 				selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
4626 				LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
4627 			} else {
4628 				// Single shift+click in margin: select from line anchor to clicked line
4629 				if (sel.MainAnchor() > sel.MainCaret())
4630 					lineAnchorPos = sel.MainAnchor() - 1;
4631 				else
4632 					lineAnchorPos = sel.MainAnchor();
4633 				// Reset selection type if there is an empty selection.
4634 				// This ensures that we don't end up stuck in previous selection mode, which is no longer valid.
4635 				// Otherwise, if there's a non empty selection, reset selection type only if it differs from selSubLine and selWholeLine.
4636 				// This ensures that we continue selecting in the same selection mode.
4637 				if (sel.Empty() || (selectionType != selSubLine && selectionType != selWholeLine))
4638 					selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
4639 				LineSelection(newPos.Position(), lineAnchorPos, selectionType == selWholeLine);
4640 			}
4641 
4642 			SetDragPosition(SelectionPosition(Sci::invalidPosition));
4643 			SetMouseCapture(true);
4644 			FineTickerStart(tickScroll, 100, 10);
4645 		} else {
4646 			if (PointIsHotspot(pt)) {
4647 				NotifyHotSpotClicked(newCharPos.Position(), modifiers);
4648 				hotSpotClickPos = newCharPos.Position();
4649 			}
4650 			if (!shift) {
4651 				if (PointInSelection(pt) && !SelectionEmpty())
4652 					inDragDrop = ddInitial;
4653 				else
4654 					inDragDrop = ddNone;
4655 			}
4656 			SetMouseCapture(true);
4657 			FineTickerStart(tickScroll, 100, 10);
4658 			if (inDragDrop != ddInitial) {
4659 				SetDragPosition(SelectionPosition(Sci::invalidPosition));
4660 				if (!shift) {
4661 					if (ctrl && multipleSelection) {
4662 						const SelectionRange range(newPos);
4663 						sel.TentativeSelection(range);
4664 						InvalidateSelection(range, true);
4665 					} else {
4666 						InvalidateSelection(SelectionRange(newPos), true);
4667 						if (sel.Count() > 1)
4668 							Redraw();
4669 						if ((sel.Count() > 1) || (sel.selType != Selection::selStream))
4670 							sel.Clear();
4671 						sel.selType = alt ? Selection::selRectangle : Selection::selStream;
4672 						SetSelection(newPos, newPos);
4673 					}
4674 				}
4675 				SelectionPosition anchorCurrent = newPos;
4676 				if (shift)
4677 					anchorCurrent = sel.IsRectangular() ?
4678 						sel.Rectangular().anchor : sel.RangeMain().anchor;
4679 				sel.selType = alt ? Selection::selRectangle : Selection::selStream;
4680 				selectionType = selChar;
4681 				originalAnchorPos = sel.MainCaret();
4682 				sel.Rectangular() = SelectionRange(newPos, anchorCurrent);
4683 				SetRectangularRange();
4684 			}
4685 		}
4686 	}
4687 	lastClickTime = curTime;
4688 	lastClick = pt;
4689 	lastXChosen = static_cast<int>(pt.x) + xOffset;
4690 	ShowCaretAtCurrentPosition();
4691 }
4692 
RightButtonDownWithModifiers(Point pt,unsigned int,int modifiers)4693 void Editor::RightButtonDownWithModifiers(Point pt, unsigned int, int modifiers) {
4694 	if (NotifyMarginRightClick(pt, modifiers))
4695 		return;
4696 }
4697 
PositionIsHotspot(Sci::Position position) const4698 bool Editor::PositionIsHotspot(Sci::Position position) const {
4699 	return vs.styles[pdoc->StyleIndexAt(position)].hotspot;
4700 }
4701 
PointIsHotspot(Point pt)4702 bool Editor::PointIsHotspot(Point pt) {
4703 	const Sci::Position pos = PositionFromLocation(pt, true, true);
4704 	if (pos == INVALID_POSITION)
4705 		return false;
4706 	return PositionIsHotspot(pos);
4707 }
4708 
SetHoverIndicatorPosition(Sci::Position position)4709 void Editor::SetHoverIndicatorPosition(Sci::Position position) {
4710 	const Sci::Position hoverIndicatorPosPrev = hoverIndicatorPos;
4711 	hoverIndicatorPos = INVALID_POSITION;
4712 	if (!vs.indicatorsDynamic)
4713 		return;
4714 	if (position != INVALID_POSITION) {
4715 		for (const IDecoration *deco : pdoc->decorations->View()) {
4716 			if (vs.indicators[deco->Indicator()].IsDynamic()) {
4717 				if (pdoc->decorations->ValueAt(deco->Indicator(), position)) {
4718 					hoverIndicatorPos = position;
4719 				}
4720 			}
4721 		}
4722 	}
4723 	if (hoverIndicatorPosPrev != hoverIndicatorPos) {
4724 		Redraw();
4725 	}
4726 }
4727 
SetHoverIndicatorPoint(Point pt)4728 void Editor::SetHoverIndicatorPoint(Point pt) {
4729 	if (!vs.indicatorsDynamic) {
4730 		SetHoverIndicatorPosition(INVALID_POSITION);
4731 	} else {
4732 		SetHoverIndicatorPosition(PositionFromLocation(pt, true, true));
4733 	}
4734 }
4735 
SetHotSpotRange(const Point * pt)4736 void Editor::SetHotSpotRange(const Point *pt) {
4737 	if (pt) {
4738 		const Sci::Position pos = PositionFromLocation(*pt, false, true);
4739 
4740 		// If we don't limit this to word characters then the
4741 		// range can encompass more than the run range and then
4742 		// the underline will not be drawn properly.
4743 		Range hsNew;
4744 		hsNew.start = pdoc->ExtendStyleRange(pos, -1, vs.hotspotSingleLine);
4745 		hsNew.end = pdoc->ExtendStyleRange(pos, 1, vs.hotspotSingleLine);
4746 
4747 		// Only invalidate the range if the hotspot range has changed...
4748 		if (!(hsNew == hotspot)) {
4749 			if (hotspot.Valid()) {
4750 				InvalidateRange(hotspot.start, hotspot.end);
4751 			}
4752 			hotspot = hsNew;
4753 			InvalidateRange(hotspot.start, hotspot.end);
4754 		}
4755 	} else {
4756 		if (hotspot.Valid()) {
4757 			InvalidateRange(hotspot.start, hotspot.end);
4758 		}
4759 		hotspot = Range(Sci::invalidPosition);
4760 	}
4761 }
4762 
GetHotSpotRange() const4763 Range Editor::GetHotSpotRange() const noexcept {
4764 	return hotspot;
4765 }
4766 
ButtonMoveWithModifiers(Point pt,unsigned int,int modifiers)4767 void Editor::ButtonMoveWithModifiers(Point pt, unsigned int, int modifiers) {
4768 	if (ptMouseLast != pt) {
4769 		DwellEnd(true);
4770 	}
4771 
4772 	SelectionPosition movePos = SPositionFromLocation(pt, false, false,
4773 		AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
4774 	movePos = MovePositionOutsideChar(movePos, sel.MainCaret() - movePos.Position());
4775 
4776 	if (inDragDrop == ddInitial) {
4777 		if (DragThreshold(ptMouseLast, pt)) {
4778 			SetMouseCapture(false);
4779 			FineTickerCancel(tickScroll);
4780 			SetDragPosition(movePos);
4781 			CopySelectionRange(&drag);
4782 			StartDrag();
4783 		}
4784 		return;
4785 	}
4786 
4787 	ptMouseLast = pt;
4788 	PRectangle rcClient = GetClientRectangle();
4789 	const Point ptOrigin = GetVisibleOriginInMain();
4790 	rcClient.Move(0, -ptOrigin.y);
4791 	if ((dwellDelay < SC_TIME_FOREVER) && rcClient.Contains(pt)) {
4792 		FineTickerStart(tickDwell, dwellDelay, dwellDelay/10);
4793 	}
4794 	//Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
4795 	if (HaveMouseCapture()) {
4796 
4797 		// Slow down autoscrolling/selection
4798 		autoScrollTimer.ticksToWait -= timer.tickSize;
4799 		if (autoScrollTimer.ticksToWait > 0)
4800 			return;
4801 		autoScrollTimer.ticksToWait = autoScrollDelay;
4802 
4803 		// Adjust selection
4804 		if (posDrag.IsValid()) {
4805 			SetDragPosition(movePos);
4806 		} else {
4807 			if (selectionType == selChar) {
4808 				if (sel.selType == Selection::selStream && (modifiers & SCI_ALT) && mouseSelectionRectangularSwitch) {
4809 					sel.selType = Selection::selRectangle;
4810 				}
4811 				if (sel.IsRectangular()) {
4812 					sel.Rectangular() = SelectionRange(movePos, sel.Rectangular().anchor);
4813 					SetSelection(movePos, sel.RangeMain().anchor);
4814 				} else if (sel.Count() > 1) {
4815 					InvalidateSelection(sel.RangeMain(), false);
4816 					const SelectionRange range(movePos, sel.RangeMain().anchor);
4817 					sel.TentativeSelection(range);
4818 					InvalidateSelection(range, true);
4819 				} else {
4820 					SetSelection(movePos, sel.RangeMain().anchor);
4821 				}
4822 			} else if (selectionType == selWord) {
4823 				// Continue selecting by word
4824 				if (movePos.Position() == wordSelectInitialCaretPos) {  // Didn't move
4825 					// No need to do anything. Previously this case was lumped
4826 					// in with "Moved forward", but that can be harmful in this
4827 					// case: a handler for the NotifyDoubleClick re-adjusts
4828 					// the selection for a fancier definition of "word" (for
4829 					// example, in Perl it is useful to include the leading
4830 					// '$', '%' or '@' on variables for word selection). In this
4831 					// the ButtonMove() called via TickFor() for auto-scrolling
4832 					// could result in the fancier word selection adjustment
4833 					// being unmade.
4834 				} else {
4835 					wordSelectInitialCaretPos = -1;
4836 					WordSelection(movePos.Position());
4837 				}
4838 			} else {
4839 				// Continue selecting by line
4840 				LineSelection(movePos.Position(), lineAnchorPos, selectionType == selWholeLine);
4841 			}
4842 		}
4843 
4844 		// Autoscroll
4845 		const Sci::Line lineMove = DisplayFromPosition(movePos.Position());
4846 		if (pt.y > rcClient.bottom) {
4847 			ScrollTo(lineMove - LinesOnScreen() + 1);
4848 			Redraw();
4849 		} else if (pt.y < rcClient.top) {
4850 			ScrollTo(lineMove);
4851 			Redraw();
4852 		}
4853 		EnsureCaretVisible(false, false, true);
4854 
4855 		if (hotspot.Valid() && !PointIsHotspot(pt))
4856 			SetHotSpotRange(nullptr);
4857 
4858 		if (hotSpotClickPos != INVALID_POSITION && PositionFromLocation(pt, true, true) != hotSpotClickPos) {
4859 			if (inDragDrop == ddNone) {
4860 				DisplayCursor(Window::cursorText);
4861 			}
4862 			hotSpotClickPos = INVALID_POSITION;
4863 		}
4864 
4865 	} else {
4866 		if (vs.fixedColumnWidth > 0) {	// There is a margin
4867 			if (PointInSelMargin(pt)) {
4868 				DisplayCursor(GetMarginCursor(pt));
4869 				SetHotSpotRange(nullptr);
4870 				return; 	// No need to test for selection
4871 			}
4872 		}
4873 		// Display regular (drag) cursor over selection
4874 		if (PointInSelection(pt) && !SelectionEmpty()) {
4875 			DisplayCursor(Window::cursorArrow);
4876 		} else {
4877 			SetHoverIndicatorPoint(pt);
4878 			if (PointIsHotspot(pt)) {
4879 				DisplayCursor(Window::cursorHand);
4880 				SetHotSpotRange(&pt);
4881 			} else {
4882 				if (hoverIndicatorPos != Sci::invalidPosition)
4883 					DisplayCursor(Window::cursorHand);
4884 				else
4885 					DisplayCursor(Window::cursorText);
4886 				SetHotSpotRange(nullptr);
4887 			}
4888 		}
4889 	}
4890 }
4891 
ButtonUpWithModifiers(Point pt,unsigned int curTime,int modifiers)4892 void Editor::ButtonUpWithModifiers(Point pt, unsigned int curTime, int modifiers) {
4893 	//Platform::DebugPrintf("ButtonUp %d %d\n", HaveMouseCapture(), inDragDrop);
4894 	SelectionPosition newPos = SPositionFromLocation(pt, false, false,
4895 		AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
4896 	if (hoverIndicatorPos != INVALID_POSITION)
4897 		InvalidateRange(newPos.Position(), newPos.Position() + 1);
4898 	newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
4899 	if (inDragDrop == ddInitial) {
4900 		inDragDrop = ddNone;
4901 		SetEmptySelection(newPos);
4902 		selectionType = selChar;
4903 		originalAnchorPos = sel.MainCaret();
4904 	}
4905 	if (hotSpotClickPos != INVALID_POSITION && PointIsHotspot(pt)) {
4906 		hotSpotClickPos = INVALID_POSITION;
4907 		SelectionPosition newCharPos = SPositionFromLocation(pt, false, true, false);
4908 		newCharPos = MovePositionOutsideChar(newCharPos, -1);
4909 		NotifyHotSpotReleaseClick(newCharPos.Position(), modifiers & SCI_CTRL);
4910 	}
4911 	if (HaveMouseCapture()) {
4912 		if (PointInSelMargin(pt)) {
4913 			DisplayCursor(GetMarginCursor(pt));
4914 		} else {
4915 			DisplayCursor(Window::cursorText);
4916 			SetHotSpotRange(nullptr);
4917 		}
4918 		ptMouseLast = pt;
4919 		SetMouseCapture(false);
4920 		FineTickerCancel(tickScroll);
4921 		NotifyIndicatorClick(false, newPos.Position(), 0);
4922 		if (inDragDrop == ddDragging) {
4923 			const SelectionPosition selStart = SelectionStart();
4924 			const SelectionPosition selEnd = SelectionEnd();
4925 			if (selStart < selEnd) {
4926 				if (drag.Length()) {
4927 					const Sci::Position length = drag.Length();
4928 					if (modifiers & SCI_CTRL) {
4929 						const Sci::Position lengthInserted = pdoc->InsertString(
4930 							newPos.Position(), drag.Data(), length);
4931 						if (lengthInserted > 0) {
4932 							SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
4933 						}
4934 					} else if (newPos < selStart) {
4935 						pdoc->DeleteChars(selStart.Position(), drag.Length());
4936 						const Sci::Position lengthInserted = pdoc->InsertString(
4937 							newPos.Position(), drag.Data(), length);
4938 						if (lengthInserted > 0) {
4939 							SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
4940 						}
4941 					} else if (newPos > selEnd) {
4942 						pdoc->DeleteChars(selStart.Position(), drag.Length());
4943 						newPos.Add(-static_cast<Sci::Position>(drag.Length()));
4944 						const Sci::Position lengthInserted = pdoc->InsertString(
4945 							newPos.Position(), drag.Data(), length);
4946 						if (lengthInserted > 0) {
4947 							SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
4948 						}
4949 					} else {
4950 						SetEmptySelection(newPos.Position());
4951 					}
4952 					drag.Clear();
4953 				}
4954 				selectionType = selChar;
4955 			}
4956 		} else {
4957 			if (selectionType == selChar) {
4958 				if (sel.Count() > 1) {
4959 					sel.RangeMain() =
4960 						SelectionRange(newPos, sel.Range(sel.Count() - 1).anchor);
4961 					InvalidateWholeSelection();
4962 				} else {
4963 					SetSelection(newPos, sel.RangeMain().anchor);
4964 				}
4965 			}
4966 			sel.CommitTentative();
4967 		}
4968 		SetRectangularRange();
4969 		lastClickTime = curTime;
4970 		lastClick = pt;
4971 		lastXChosen = static_cast<int>(pt.x) + xOffset;
4972 		if (sel.selType == Selection::selStream) {
4973 			SetLastXChosen();
4974 		}
4975 		inDragDrop = ddNone;
4976 		EnsureCaretVisible(false);
4977 	}
4978 }
4979 
Idle()4980 bool Editor::Idle() {
4981 	NotifyUpdateUI();
4982 
4983 	bool needWrap = Wrapping() && wrapPending.NeedsWrap();
4984 
4985 	if (needWrap) {
4986 		// Wrap lines during idle.
4987 		WrapLines(WrapScope::wsIdle);
4988 		// No more wrapping
4989 		needWrap = wrapPending.NeedsWrap();
4990 	} else if (needIdleStyling) {
4991 		IdleStyling();
4992 	}
4993 
4994 	// Add more idle things to do here, but make sure idleDone is
4995 	// set correctly before the function returns. returning
4996 	// false will stop calling this idle function until SetIdle() is
4997 	// called again.
4998 
4999 	const bool idleDone = !needWrap && !needIdleStyling; // && thatDone && theOtherThingDone...
5000 
5001 	return !idleDone;
5002 }
5003 
TickFor(TickReason reason)5004 void Editor::TickFor(TickReason reason) {
5005 	switch (reason) {
5006 		case tickCaret:
5007 			caret.on = !caret.on;
5008 			if (caret.active) {
5009 				InvalidateCaret();
5010 			}
5011 			break;
5012 		case tickScroll:
5013 			// Auto scroll
5014 			ButtonMoveWithModifiers(ptMouseLast, 0, 0);
5015 			break;
5016 		case tickWiden:
5017 			SetScrollBars();
5018 			FineTickerCancel(tickWiden);
5019 			break;
5020 		case tickDwell:
5021 			if ((!HaveMouseCapture()) &&
5022 				(ptMouseLast.y >= 0)) {
5023 				dwelling = true;
5024 				NotifyDwelling(ptMouseLast, dwelling);
5025 			}
5026 			FineTickerCancel(tickDwell);
5027 			break;
5028 		default:
5029 			// tickPlatform handled by subclass
5030 			break;
5031 	}
5032 }
5033 
5034 // FineTickerStart is be overridden by subclasses that support fine ticking so
5035 // this method should never be called.
FineTickerRunning(TickReason)5036 bool Editor::FineTickerRunning(TickReason) {
5037 	assert(false);
5038 	return false;
5039 }
5040 
5041 // FineTickerStart is be overridden by subclasses that support fine ticking so
5042 // this method should never be called.
FineTickerStart(TickReason,int,int)5043 void Editor::FineTickerStart(TickReason, int, int) {
5044 	assert(false);
5045 }
5046 
5047 // FineTickerCancel is be overridden by subclasses that support fine ticking so
5048 // this method should never be called.
FineTickerCancel(TickReason)5049 void Editor::FineTickerCancel(TickReason) {
5050 	assert(false);
5051 }
5052 
SetFocusState(bool focusState)5053 void Editor::SetFocusState(bool focusState) {
5054 	hasFocus = focusState;
5055 	NotifyFocus(hasFocus);
5056 	if (!hasFocus) {
5057 		CancelModes();
5058 	}
5059 	ShowCaretAtCurrentPosition();
5060 }
5061 
PositionAfterArea(PRectangle rcArea) const5062 Sci::Position Editor::PositionAfterArea(PRectangle rcArea) const {
5063 	// The start of the document line after the display line after the area
5064 	// This often means that the line after a modification is restyled which helps
5065 	// detect multiline comment additions and heals single line comments
5066 	const Sci::Line lineAfter = TopLineOfMain() + static_cast<Sci::Line>(rcArea.bottom - 1) / vs.lineHeight + 1;
5067 	if (lineAfter < pcs->LinesDisplayed())
5068 		return pdoc->LineStart(pcs->DocFromDisplay(lineAfter) + 1);
5069 	else
5070 		return pdoc->Length();
5071 }
5072 
5073 // Style to a position within the view. If this causes a change at end of last line then
5074 // affects later lines so style all the viewed text.
StyleToPositionInView(Sci::Position pos)5075 void Editor::StyleToPositionInView(Sci::Position pos) {
5076 	Sci::Position endWindow = PositionAfterArea(GetClientDrawingRectangle());
5077 	if (pos > endWindow)
5078 		pos = endWindow;
5079 	const int styleAtEnd = pdoc->StyleIndexAt(pos-1);
5080 	pdoc->EnsureStyledTo(pos);
5081 	if ((endWindow > pos) && (styleAtEnd != pdoc->StyleIndexAt(pos-1))) {
5082 		// Style at end of line changed so is multi-line change like starting a comment
5083 		// so require rest of window to be styled.
5084 		DiscardOverdraw();	// Prepared bitmaps may be invalid
5085 		// DiscardOverdraw may have truncated client drawing area so recalculate endWindow
5086 		endWindow = PositionAfterArea(GetClientDrawingRectangle());
5087 		pdoc->EnsureStyledTo(endWindow);
5088 	}
5089 }
5090 
PositionAfterMaxStyling(Sci::Position posMax,bool scrolling) const5091 Sci::Position Editor::PositionAfterMaxStyling(Sci::Position posMax, bool scrolling) const {
5092 	if (SynchronousStylingToVisible()) {
5093 		// Both states do not limit styling
5094 		return posMax;
5095 	}
5096 
5097 	// Try to keep time taken by styling reasonable so interaction remains smooth.
5098 	// When scrolling, allow less time to ensure responsive
5099 	const double secondsAllowed = scrolling ? 0.005 : 0.02;
5100 
5101 	const Sci::Line linesToStyle = std::clamp(
5102 		static_cast<int>(secondsAllowed / pdoc->durationStyleOneLine.Duration()),
5103 		10, 0x10000);
5104 	const Sci::Line stylingMaxLine = std::min(
5105 		pdoc->SciLineFromPosition(pdoc->GetEndStyled()) + linesToStyle,
5106 		pdoc->LinesTotal());
5107 	return std::min(pdoc->LineStart(stylingMaxLine), posMax);
5108 }
5109 
StartIdleStyling(bool truncatedLastStyling)5110 void Editor::StartIdleStyling(bool truncatedLastStyling) {
5111 	if ((idleStyling == SC_IDLESTYLING_ALL) || (idleStyling == SC_IDLESTYLING_AFTERVISIBLE)) {
5112 		if (pdoc->GetEndStyled() < pdoc->Length()) {
5113 			// Style remainder of document in idle time
5114 			needIdleStyling = true;
5115 		}
5116 	} else if (truncatedLastStyling) {
5117 		needIdleStyling = true;
5118 	}
5119 
5120 	if (needIdleStyling) {
5121 		SetIdle(true);
5122 	}
5123 }
5124 
5125 // Style for an area but bound the amount of styling to remain responsive
StyleAreaBounded(PRectangle rcArea,bool scrolling)5126 void Editor::StyleAreaBounded(PRectangle rcArea, bool scrolling) {
5127 	const Sci::Position posAfterArea = PositionAfterArea(rcArea);
5128 	const Sci::Position posAfterMax = PositionAfterMaxStyling(posAfterArea, scrolling);
5129 	if (posAfterMax < posAfterArea) {
5130 		// Idle styling may be performed before current visible area
5131 		// Style a bit now then style further in idle time
5132 		pdoc->StyleToAdjustingLineDuration(posAfterMax);
5133 	} else {
5134 		// Can style all wanted now.
5135 		StyleToPositionInView(posAfterArea);
5136 	}
5137 	StartIdleStyling(posAfterMax < posAfterArea);
5138 }
5139 
IdleStyling()5140 void Editor::IdleStyling() {
5141 	const Sci::Position posAfterArea = PositionAfterArea(GetClientRectangle());
5142 	const Sci::Position endGoal = (idleStyling >= SC_IDLESTYLING_AFTERVISIBLE) ?
5143 		pdoc->Length() : posAfterArea;
5144 	const Sci::Position posAfterMax = PositionAfterMaxStyling(endGoal, false);
5145 	pdoc->StyleToAdjustingLineDuration(posAfterMax);
5146 	if (pdoc->GetEndStyled() >= endGoal) {
5147 		needIdleStyling = false;
5148 	}
5149 }
5150 
IdleWork()5151 void Editor::IdleWork() {
5152 	// Style the line after the modification as this allows modifications that change just the
5153 	// line of the modification to heal instead of propagating to the rest of the window.
5154 	if (workNeeded.items & WorkNeeded::workStyle) {
5155 		StyleToPositionInView(pdoc->LineStart(pdoc->LineFromPosition(workNeeded.upTo) + 2));
5156 	}
5157 	NotifyUpdateUI();
5158 	workNeeded.Reset();
5159 }
5160 
QueueIdleWork(WorkNeeded::workItems items,Sci::Position upTo)5161 void Editor::QueueIdleWork(WorkNeeded::workItems items, Sci::Position upTo) {
5162 	workNeeded.Need(items, upTo);
5163 }
5164 
PaintContains(PRectangle rc)5165 bool Editor::PaintContains(PRectangle rc) {
5166 	if (rc.Empty()) {
5167 		return true;
5168 	} else {
5169 		return rcPaint.Contains(rc);
5170 	}
5171 }
5172 
PaintContainsMargin()5173 bool Editor::PaintContainsMargin() {
5174 	if (wMargin.GetID()) {
5175 		// With separate margin view, paint of text view
5176 		// never contains margin.
5177 		return false;
5178 	}
5179 	PRectangle rcSelMargin = GetClientRectangle();
5180 	rcSelMargin.right = static_cast<XYPOSITION>(vs.textStart);
5181 	return PaintContains(rcSelMargin);
5182 }
5183 
CheckForChangeOutsidePaint(Range r)5184 void Editor::CheckForChangeOutsidePaint(Range r) {
5185 	if (paintState == painting && !paintingAllText) {
5186 		//Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
5187 		if (!r.Valid())
5188 			return;
5189 
5190 		PRectangle rcRange = RectangleFromRange(r, 0);
5191 		const PRectangle rcText = GetTextRectangle();
5192 		if (rcRange.top < rcText.top) {
5193 			rcRange.top = rcText.top;
5194 		}
5195 		if (rcRange.bottom > rcText.bottom) {
5196 			rcRange.bottom = rcText.bottom;
5197 		}
5198 
5199 		if (!PaintContains(rcRange)) {
5200 			AbandonPaint();
5201 			paintAbandonedByStyling = true;
5202 		}
5203 	}
5204 }
5205 
SetBraceHighlight(Sci::Position pos0,Sci::Position pos1,int matchStyle)5206 void Editor::SetBraceHighlight(Sci::Position pos0, Sci::Position pos1, int matchStyle) {
5207 	if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
5208 		if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
5209 			CheckForChangeOutsidePaint(Range(braces[0]));
5210 			CheckForChangeOutsidePaint(Range(pos0));
5211 			braces[0] = pos0;
5212 		}
5213 		if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
5214 			CheckForChangeOutsidePaint(Range(braces[1]));
5215 			CheckForChangeOutsidePaint(Range(pos1));
5216 			braces[1] = pos1;
5217 		}
5218 		bracesMatchStyle = matchStyle;
5219 		if (paintState == notPainting) {
5220 			Redraw();
5221 		}
5222 	}
5223 }
5224 
SetAnnotationHeights(Sci::Line start,Sci::Line end)5225 void Editor::SetAnnotationHeights(Sci::Line start, Sci::Line end) {
5226 	if (vs.annotationVisible) {
5227 		RefreshStyleData();
5228 		bool changedHeight = false;
5229 		for (Sci::Line line=start; line<end && line<pdoc->LinesTotal(); line++) {
5230 			int linesWrapped = 1;
5231 			if (Wrapping()) {
5232 				AutoSurface surface(this);
5233 				AutoLineLayout ll(view.llc, view.RetrieveLineLayout(line, *this));
5234 				if (surface && ll) {
5235 					view.LayoutLine(*this, line, surface, vs, ll, wrapWidth);
5236 					linesWrapped = ll->lines;
5237 				}
5238 			}
5239 			if (pcs->SetHeight(line, pdoc->AnnotationLines(line) + linesWrapped))
5240 				changedHeight = true;
5241 		}
5242 		if (changedHeight) {
5243 			Redraw();
5244 		}
5245 	}
5246 }
5247 
SetDocPointer(Document * document)5248 void Editor::SetDocPointer(Document *document) {
5249 	//Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
5250 	pdoc->RemoveWatcher(this, 0);
5251 	pdoc->Release();
5252 	if (!document) {
5253 		pdoc = new Document(SC_DOCUMENTOPTION_DEFAULT);
5254 	} else {
5255 		pdoc = document;
5256 	}
5257 	pdoc->AddRef();
5258 	pcs = ContractionStateCreate(pdoc->IsLarge());
5259 
5260 	// Ensure all positions within document
5261 	sel.Clear();
5262 	targetRange = SelectionSegment();
5263 
5264 	braces[0] = Sci::invalidPosition;
5265 	braces[1] = Sci::invalidPosition;
5266 
5267 	vs.ReleaseAllExtendedStyles();
5268 
5269 	SetRepresentations();
5270 
5271 	// Reset the contraction state to fully shown.
5272 	pcs->Clear();
5273 	pcs->InsertLines(0, pdoc->LinesTotal() - 1);
5274 	SetAnnotationHeights(0, pdoc->LinesTotal());
5275 	view.llc.Deallocate();
5276 	NeedWrapping();
5277 
5278 	hotspot = Range(Sci::invalidPosition);
5279 	hoverIndicatorPos = Sci::invalidPosition;
5280 
5281 	view.ClearAllTabstops();
5282 
5283 	pdoc->AddWatcher(this, 0);
5284 	SetScrollBars();
5285 	Redraw();
5286 }
5287 
SetAnnotationVisible(int visible)5288 void Editor::SetAnnotationVisible(int visible) {
5289 	if (vs.annotationVisible != visible) {
5290 		const bool changedFromOrToHidden = ((vs.annotationVisible != 0) != (visible != 0));
5291 		vs.annotationVisible = visible;
5292 		if (changedFromOrToHidden) {
5293 			const int dir = vs.annotationVisible ? 1 : -1;
5294 			for (Sci::Line line=0; line<pdoc->LinesTotal(); line++) {
5295 				const int annotationLines = pdoc->AnnotationLines(line);
5296 				if (annotationLines > 0) {
5297 					pcs->SetHeight(line, pcs->GetHeight(line) + annotationLines * dir);
5298 				}
5299 			}
5300 			SetScrollBars();
5301 		}
5302 		Redraw();
5303 	}
5304 }
5305 
5306 /**
5307  * Recursively expand a fold, making lines visible except where they have an unexpanded parent.
5308  */
ExpandLine(Sci::Line line)5309 Sci::Line Editor::ExpandLine(Sci::Line line) {
5310 	const Sci::Line lineMaxSubord = pdoc->GetLastChild(line);
5311 	line++;
5312 	while (line <= lineMaxSubord) {
5313 		pcs->SetVisible(line, line, true);
5314 		const int level = pdoc->GetLevel(line);
5315 		if (level & SC_FOLDLEVELHEADERFLAG) {
5316 			if (pcs->GetExpanded(line)) {
5317 				line = ExpandLine(line);
5318 			} else {
5319 				line = pdoc->GetLastChild(line);
5320 			}
5321 		}
5322 		line++;
5323 	}
5324 	return lineMaxSubord;
5325 }
5326 
SetFoldExpanded(Sci::Line lineDoc,bool expanded)5327 void Editor::SetFoldExpanded(Sci::Line lineDoc, bool expanded) {
5328 	if (pcs->SetExpanded(lineDoc, expanded)) {
5329 		RedrawSelMargin();
5330 	}
5331 }
5332 
FoldLine(Sci::Line line,int action)5333 void Editor::FoldLine(Sci::Line line, int action) {
5334 	if (line >= 0) {
5335 		if (action == SC_FOLDACTION_TOGGLE) {
5336 			if ((pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) == 0) {
5337 				line = pdoc->GetFoldParent(line);
5338 				if (line < 0)
5339 					return;
5340 			}
5341 			action = (pcs->GetExpanded(line)) ? SC_FOLDACTION_CONTRACT : SC_FOLDACTION_EXPAND;
5342 		}
5343 
5344 		if (action == SC_FOLDACTION_CONTRACT) {
5345 			const Sci::Line lineMaxSubord = pdoc->GetLastChild(line);
5346 			if (lineMaxSubord > line) {
5347 				pcs->SetExpanded(line, false);
5348 				pcs->SetVisible(line + 1, lineMaxSubord, false);
5349 
5350 				const Sci::Line lineCurrent =
5351 					pdoc->SciLineFromPosition(sel.MainCaret());
5352 				if (lineCurrent > line && lineCurrent <= lineMaxSubord) {
5353 					// This does not re-expand the fold
5354 					EnsureCaretVisible();
5355 				}
5356 			}
5357 
5358 		} else {
5359 			if (!(pcs->GetVisible(line))) {
5360 				EnsureLineVisible(line, false);
5361 				GoToLine(line);
5362 			}
5363 			pcs->SetExpanded(line, true);
5364 			ExpandLine(line);
5365 		}
5366 
5367 		SetScrollBars();
5368 		Redraw();
5369 	}
5370 }
5371 
FoldExpand(Sci::Line line,int action,int level)5372 void Editor::FoldExpand(Sci::Line line, int action, int level) {
5373 	bool expanding = action == SC_FOLDACTION_EXPAND;
5374 	if (action == SC_FOLDACTION_TOGGLE) {
5375 		expanding = !pcs->GetExpanded(line);
5376 	}
5377 	// Ensure child lines lexed and fold information extracted before
5378 	// flipping the state.
5379 	pdoc->GetLastChild(line, LevelNumber(level));
5380 	SetFoldExpanded(line, expanding);
5381 	if (expanding && (pcs->HiddenLines() == 0))
5382 		// Nothing to do
5383 		return;
5384 	const Sci::Line lineMaxSubord = pdoc->GetLastChild(line, LevelNumber(level));
5385 	line++;
5386 	pcs->SetVisible(line, lineMaxSubord, expanding);
5387 	while (line <= lineMaxSubord) {
5388 		const int levelLine = pdoc->GetLevel(line);
5389 		if (levelLine & SC_FOLDLEVELHEADERFLAG) {
5390 			SetFoldExpanded(line, expanding);
5391 		}
5392 		line++;
5393 	}
5394 	SetScrollBars();
5395 	Redraw();
5396 }
5397 
ContractedFoldNext(Sci::Line lineStart) const5398 Sci::Line Editor::ContractedFoldNext(Sci::Line lineStart) const {
5399 	for (Sci::Line line = lineStart; line<pdoc->LinesTotal();) {
5400 		if (!pcs->GetExpanded(line) && (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG))
5401 			return line;
5402 		line = pcs->ContractedNext(line+1);
5403 		if (line < 0)
5404 			return -1;
5405 	}
5406 
5407 	return -1;
5408 }
5409 
5410 /**
5411  * Recurse up from this line to find any folds that prevent this line from being visible
5412  * and unfold them all.
5413  */
EnsureLineVisible(Sci::Line lineDoc,bool enforcePolicy)5414 void Editor::EnsureLineVisible(Sci::Line lineDoc, bool enforcePolicy) {
5415 
5416 	// In case in need of wrapping to ensure DisplayFromDoc works.
5417 	if (lineDoc >= wrapPending.start) {
5418 		if (WrapLines(WrapScope::wsAll)) {
5419 			Redraw();
5420 		}
5421 	}
5422 
5423 	if (!pcs->GetVisible(lineDoc)) {
5424 		// Back up to find a non-blank line
5425 		Sci::Line lookLine = lineDoc;
5426 		int lookLineLevel = pdoc->GetLevel(lookLine);
5427 		while ((lookLine > 0) && (lookLineLevel & SC_FOLDLEVELWHITEFLAG)) {
5428 			lookLineLevel = pdoc->GetLevel(--lookLine);
5429 		}
5430 		Sci::Line lineParent = pdoc->GetFoldParent(lookLine);
5431 		if (lineParent < 0) {
5432 			// Backed up to a top level line, so try to find parent of initial line
5433 			lineParent = pdoc->GetFoldParent(lineDoc);
5434 		}
5435 		if (lineParent >= 0) {
5436 			if (lineDoc != lineParent)
5437 				EnsureLineVisible(lineParent, enforcePolicy);
5438 			if (!pcs->GetExpanded(lineParent)) {
5439 				pcs->SetExpanded(lineParent, true);
5440 				ExpandLine(lineParent);
5441 			}
5442 		}
5443 		SetScrollBars();
5444 		Redraw();
5445 	}
5446 	if (enforcePolicy) {
5447 		const Sci::Line lineDisplay = pcs->DisplayFromDoc(lineDoc);
5448 		if (visiblePolicy.policy & VISIBLE_SLOP) {
5449 			if ((topLine > lineDisplay) || ((visiblePolicy.policy & VISIBLE_STRICT) && (topLine + visiblePolicy.slop > lineDisplay))) {
5450 				SetTopLine(std::clamp<Sci::Line>(lineDisplay - visiblePolicy.slop, 0, MaxScrollPos()));
5451 				SetVerticalScrollPos();
5452 				Redraw();
5453 			} else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||
5454 			        ((visiblePolicy.policy & VISIBLE_STRICT) && (lineDisplay > topLine + LinesOnScreen() - 1 - visiblePolicy.slop))) {
5455 				SetTopLine(std::clamp<Sci::Line>(lineDisplay - LinesOnScreen() + 1 + visiblePolicy.slop, 0, MaxScrollPos()));
5456 				SetVerticalScrollPos();
5457 				Redraw();
5458 			}
5459 		} else {
5460 			if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (visiblePolicy.policy & VISIBLE_STRICT)) {
5461 				SetTopLine(std::clamp<Sci::Line>(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
5462 				SetVerticalScrollPos();
5463 				Redraw();
5464 			}
5465 		}
5466 	}
5467 }
5468 
FoldAll(int action)5469 void Editor::FoldAll(int action) {
5470 	pdoc->EnsureStyledTo(pdoc->Length());
5471 	const Sci::Line maxLine = pdoc->LinesTotal();
5472 	bool expanding = action == SC_FOLDACTION_EXPAND;
5473 	if (action == SC_FOLDACTION_TOGGLE) {
5474 		// Discover current state
5475 		for (int lineSeek = 0; lineSeek < maxLine; lineSeek++) {
5476 			if (pdoc->GetLevel(lineSeek) & SC_FOLDLEVELHEADERFLAG) {
5477 				expanding = !pcs->GetExpanded(lineSeek);
5478 				break;
5479 			}
5480 		}
5481 	}
5482 	if (expanding) {
5483 		pcs->SetVisible(0, maxLine-1, true);
5484 		for (int line = 0; line < maxLine; line++) {
5485 			const int levelLine = pdoc->GetLevel(line);
5486 			if (levelLine & SC_FOLDLEVELHEADERFLAG) {
5487 				SetFoldExpanded(line, true);
5488 			}
5489 		}
5490 	} else {
5491 		for (Sci::Line line = 0; line < maxLine; line++) {
5492 			const int level = pdoc->GetLevel(line);
5493 			if ((level & SC_FOLDLEVELHEADERFLAG) &&
5494 					(SC_FOLDLEVELBASE == LevelNumber(level))) {
5495 				SetFoldExpanded(line, false);
5496 				const Sci::Line lineMaxSubord = pdoc->GetLastChild(line, -1);
5497 				if (lineMaxSubord > line) {
5498 					pcs->SetVisible(line + 1, lineMaxSubord, false);
5499 				}
5500 			}
5501 		}
5502 	}
5503 	SetScrollBars();
5504 	Redraw();
5505 }
5506 
FoldChanged(Sci::Line line,int levelNow,int levelPrev)5507 void Editor::FoldChanged(Sci::Line line, int levelNow, int levelPrev) {
5508 	if (levelNow & SC_FOLDLEVELHEADERFLAG) {
5509 		if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
5510 			// Adding a fold point.
5511 			if (pcs->SetExpanded(line, true)) {
5512 				RedrawSelMargin();
5513 			}
5514 			FoldExpand(line, SC_FOLDACTION_EXPAND, levelPrev);
5515 		}
5516 	} else if (levelPrev & SC_FOLDLEVELHEADERFLAG) {
5517 		const Sci::Line prevLine = line - 1;
5518 		const int prevLineLevel = pdoc->GetLevel(prevLine);
5519 
5520 		// Combining two blocks where the first block is collapsed (e.g. by deleting the line(s) which separate(s) the two blocks)
5521 		if ((LevelNumber(prevLineLevel) == LevelNumber(levelNow)) && !pcs->GetVisible(prevLine))
5522 			FoldLine(pdoc->GetFoldParent(prevLine), SC_FOLDACTION_EXPAND);
5523 
5524 		if (!pcs->GetExpanded(line)) {
5525 			// Removing the fold from one that has been contracted so should expand
5526 			// otherwise lines are left invisible with no way to make them visible
5527 			if (pcs->SetExpanded(line, true)) {
5528 				RedrawSelMargin();
5529 			}
5530 			// Combining two blocks where the second one is collapsed (e.g. by adding characters in the line which separates the two blocks)
5531 			FoldExpand(line, SC_FOLDACTION_EXPAND, levelPrev);
5532 		}
5533 	}
5534 	if (!(levelNow & SC_FOLDLEVELWHITEFLAG) &&
5535 	        (LevelNumber(levelPrev) > LevelNumber(levelNow))) {
5536 		if (pcs->HiddenLines()) {
5537 			// See if should still be hidden
5538 			const Sci::Line parentLine = pdoc->GetFoldParent(line);
5539 			if ((parentLine < 0) || (pcs->GetExpanded(parentLine) && pcs->GetVisible(parentLine))) {
5540 				pcs->SetVisible(line, line, true);
5541 				SetScrollBars();
5542 				Redraw();
5543 			}
5544 		}
5545 	}
5546 
5547 	// Combining two blocks where the first one is collapsed (e.g. by adding characters in the line which separates the two blocks)
5548 	if (!(levelNow & SC_FOLDLEVELWHITEFLAG) && (LevelNumber(levelPrev) < LevelNumber(levelNow))) {
5549 		if (pcs->HiddenLines()) {
5550 			const Sci::Line parentLine = pdoc->GetFoldParent(line);
5551 			if (!pcs->GetExpanded(parentLine) && pcs->GetVisible(line))
5552 				FoldLine(parentLine, SC_FOLDACTION_EXPAND);
5553 		}
5554 	}
5555 }
5556 
NeedShown(Sci::Position pos,Sci::Position len)5557 void Editor::NeedShown(Sci::Position pos, Sci::Position len) {
5558 	if (foldAutomatic & SC_AUTOMATICFOLD_SHOW) {
5559 		const Sci::Line lineStart = pdoc->SciLineFromPosition(pos);
5560 		const Sci::Line lineEnd = pdoc->SciLineFromPosition(pos+len);
5561 		for (Sci::Line line = lineStart; line <= lineEnd; line++) {
5562 			EnsureLineVisible(line, false);
5563 		}
5564 	} else {
5565 		NotifyNeedShown(pos, len);
5566 	}
5567 }
5568 
GetTag(char * tagValue,int tagNumber)5569 Sci::Position Editor::GetTag(char *tagValue, int tagNumber) {
5570 	const char *text = nullptr;
5571 	Sci::Position length = 0;
5572 	if ((tagNumber >= 1) && (tagNumber <= 9)) {
5573 		char name[3] = "\\?";
5574 		name[1] = static_cast<char>(tagNumber + '0');
5575 		length = 2;
5576 		text = pdoc->SubstituteByPosition(name, &length);
5577 	}
5578 	if (tagValue) {
5579 		if (text)
5580 			memcpy(tagValue, text, length + 1);
5581 		else
5582 			*tagValue = '\0';
5583 	}
5584 	return length;
5585 }
5586 
ReplaceTarget(bool replacePatterns,const char * text,Sci::Position length)5587 Sci::Position Editor::ReplaceTarget(bool replacePatterns, const char *text, Sci::Position length) {
5588 	UndoGroup ug(pdoc);
5589 	if (length == -1)
5590 		length = strlen(text);
5591 	if (replacePatterns) {
5592 		text = pdoc->SubstituteByPosition(text, &length);
5593 		if (!text) {
5594 			return 0;
5595 		}
5596 	}
5597 
5598 	// Remove the text inside the range
5599 	if (targetRange.Length() > 0)
5600 		pdoc->DeleteChars(targetRange.start.Position(), targetRange.Length());
5601 	targetRange.end = targetRange.start;
5602 
5603 	// Realize virtual space of target start
5604 	Sci::Position startAfterSpaceInsertion = RealizeVirtualSpace(targetRange.start.Position(), targetRange.start.VirtualSpace());
5605 	targetRange.start.SetPosition(startAfterSpaceInsertion);
5606 	targetRange.end = targetRange.start;
5607 
5608 	// Insert the new text
5609 	const Sci::Position lengthInserted = pdoc->InsertString(targetRange.start.Position(), text, length);
5610 	targetRange.end.SetPosition(targetRange.start.Position() + lengthInserted);
5611 	return length;
5612 }
5613 
IsUnicodeMode() const5614 bool Editor::IsUnicodeMode() const noexcept {
5615 	return pdoc && (SC_CP_UTF8 == pdoc->dbcsCodePage);
5616 }
5617 
CodePage() const5618 int Editor::CodePage() const noexcept {
5619 	if (pdoc)
5620 		return pdoc->dbcsCodePage;
5621 	else
5622 		return 0;
5623 }
5624 
WrapCount(Sci::Line line)5625 Sci::Line Editor::WrapCount(Sci::Line line) {
5626 	AutoSurface surface(this);
5627 	AutoLineLayout ll(view.llc, view.RetrieveLineLayout(line, *this));
5628 
5629 	if (surface && ll) {
5630 		view.LayoutLine(*this, line, surface, vs, ll, wrapWidth);
5631 		return ll->lines;
5632 	} else {
5633 		return 1;
5634 	}
5635 }
5636 
AddStyledText(const char * buffer,Sci::Position appendLength)5637 void Editor::AddStyledText(const char *buffer, Sci::Position appendLength) {
5638 	// The buffer consists of alternating character bytes and style bytes
5639 	const Sci::Position textLength = appendLength / 2;
5640 	std::string text(textLength, '\0');
5641 	Sci::Position i;
5642 	for (i = 0; i < textLength; i++) {
5643 		text[i] = buffer[i*2];
5644 	}
5645 	const Sci::Position lengthInserted = pdoc->InsertString(CurrentPosition(), text.c_str(), textLength);
5646 	for (i = 0; i < textLength; i++) {
5647 		text[i] = buffer[i*2+1];
5648 	}
5649 	pdoc->StartStyling(CurrentPosition());
5650 	pdoc->SetStyles(textLength, text.c_str());
5651 	SetEmptySelection(sel.MainCaret() + lengthInserted);
5652 }
5653 
ValidMargin(uptr_t wParam) const5654 bool Editor::ValidMargin(uptr_t wParam) const noexcept {
5655 	return wParam < vs.ms.size();
5656 }
5657 
StyleSetMessage(unsigned int iMessage,uptr_t wParam,sptr_t lParam)5658 void Editor::StyleSetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5659 	vs.EnsureStyle(wParam);
5660 	switch (iMessage) {
5661 	case SCI_STYLESETFORE:
5662 		vs.styles[wParam].fore = ColourDesired(static_cast<int>(lParam));
5663 		break;
5664 	case SCI_STYLESETBACK:
5665 		vs.styles[wParam].back = ColourDesired(static_cast<int>(lParam));
5666 		break;
5667 	case SCI_STYLESETBOLD:
5668 		vs.styles[wParam].weight = lParam != 0 ? SC_WEIGHT_BOLD : SC_WEIGHT_NORMAL;
5669 		break;
5670 	case SCI_STYLESETWEIGHT:
5671 		vs.styles[wParam].weight = static_cast<int>(lParam);
5672 		break;
5673 	case SCI_STYLESETITALIC:
5674 		vs.styles[wParam].italic = lParam != 0;
5675 		break;
5676 	case SCI_STYLESETEOLFILLED:
5677 		vs.styles[wParam].eolFilled = lParam != 0;
5678 		break;
5679 	case SCI_STYLESETSIZE:
5680 		vs.styles[wParam].size = static_cast<int>(lParam * SC_FONT_SIZE_MULTIPLIER);
5681 		break;
5682 	case SCI_STYLESETSIZEFRACTIONAL:
5683 		vs.styles[wParam].size = static_cast<int>(lParam);
5684 		break;
5685 	case SCI_STYLESETFONT:
5686 		if (lParam != 0) {
5687 			vs.SetStyleFontName(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
5688 		}
5689 		break;
5690 	case SCI_STYLESETUNDERLINE:
5691 		vs.styles[wParam].underline = lParam != 0;
5692 		break;
5693 	case SCI_STYLESETCASE:
5694 		vs.styles[wParam].caseForce = static_cast<Style::ecaseForced>(lParam);
5695 		break;
5696 	case SCI_STYLESETCHARACTERSET:
5697 		vs.styles[wParam].characterSet = static_cast<int>(lParam);
5698 		pdoc->SetCaseFolder(nullptr);
5699 		break;
5700 	case SCI_STYLESETVISIBLE:
5701 		vs.styles[wParam].visible = lParam != 0;
5702 		break;
5703 	case SCI_STYLESETCHANGEABLE:
5704 		vs.styles[wParam].changeable = lParam != 0;
5705 		break;
5706 	case SCI_STYLESETHOTSPOT:
5707 		vs.styles[wParam].hotspot = lParam != 0;
5708 		break;
5709 	}
5710 	InvalidateStyleRedraw();
5711 }
5712 
StyleGetMessage(unsigned int iMessage,uptr_t wParam,sptr_t lParam)5713 sptr_t Editor::StyleGetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5714 	vs.EnsureStyle(wParam);
5715 	switch (iMessage) {
5716 	case SCI_STYLEGETFORE:
5717 		return vs.styles[wParam].fore.AsInteger();
5718 	case SCI_STYLEGETBACK:
5719 		return vs.styles[wParam].back.AsInteger();
5720 	case SCI_STYLEGETBOLD:
5721 		return vs.styles[wParam].weight > SC_WEIGHT_NORMAL;
5722 	case SCI_STYLEGETWEIGHT:
5723 		return vs.styles[wParam].weight;
5724 	case SCI_STYLEGETITALIC:
5725 		return vs.styles[wParam].italic ? 1 : 0;
5726 	case SCI_STYLEGETEOLFILLED:
5727 		return vs.styles[wParam].eolFilled ? 1 : 0;
5728 	case SCI_STYLEGETSIZE:
5729 		return vs.styles[wParam].size / SC_FONT_SIZE_MULTIPLIER;
5730 	case SCI_STYLEGETSIZEFRACTIONAL:
5731 		return vs.styles[wParam].size;
5732 	case SCI_STYLEGETFONT:
5733 		return StringResult(lParam, vs.styles[wParam].fontName);
5734 	case SCI_STYLEGETUNDERLINE:
5735 		return vs.styles[wParam].underline ? 1 : 0;
5736 	case SCI_STYLEGETCASE:
5737 		return static_cast<int>(vs.styles[wParam].caseForce);
5738 	case SCI_STYLEGETCHARACTERSET:
5739 		return vs.styles[wParam].characterSet;
5740 	case SCI_STYLEGETVISIBLE:
5741 		return vs.styles[wParam].visible ? 1 : 0;
5742 	case SCI_STYLEGETCHANGEABLE:
5743 		return vs.styles[wParam].changeable ? 1 : 0;
5744 	case SCI_STYLEGETHOTSPOT:
5745 		return vs.styles[wParam].hotspot ? 1 : 0;
5746 	}
5747 	return 0;
5748 }
5749 
SetSelectionNMessage(unsigned int iMessage,uptr_t wParam,sptr_t lParam)5750 void Editor::SetSelectionNMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5751 	InvalidateRange(sel.Range(wParam).Start().Position(), sel.Range(wParam).End().Position());
5752 
5753 	switch (iMessage) {
5754 	case SCI_SETSELECTIONNCARET:
5755 		sel.Range(wParam).caret.SetPosition(lParam);
5756 		break;
5757 
5758 	case SCI_SETSELECTIONNANCHOR:
5759 		sel.Range(wParam).anchor.SetPosition(lParam);
5760 		break;
5761 
5762 	case SCI_SETSELECTIONNCARETVIRTUALSPACE:
5763 		sel.Range(wParam).caret.SetVirtualSpace(lParam);
5764 		break;
5765 
5766 	case SCI_SETSELECTIONNANCHORVIRTUALSPACE:
5767 		sel.Range(wParam).anchor.SetVirtualSpace(lParam);
5768 		break;
5769 
5770 	case SCI_SETSELECTIONNSTART:
5771 		sel.Range(wParam).anchor.SetPosition(lParam);
5772 		break;
5773 
5774 	case SCI_SETSELECTIONNEND:
5775 		sel.Range(wParam).caret.SetPosition(lParam);
5776 		break;
5777 	}
5778 
5779 	InvalidateRange(sel.Range(wParam).Start().Position(), sel.Range(wParam).End().Position());
5780 	ContainerNeedsUpdate(SC_UPDATE_SELECTION);
5781 }
5782 
StringResult(sptr_t lParam,const char * val)5783 sptr_t Editor::StringResult(sptr_t lParam, const char *val) noexcept {
5784 	const size_t len = val ? strlen(val) : 0;
5785 	if (lParam) {
5786 		char *ptr = CharPtrFromSPtr(lParam);
5787 		if (val)
5788 			memcpy(ptr, val, len+1);
5789 		else
5790 			*ptr = 0;
5791 	}
5792 	return len;	// Not including NUL
5793 }
5794 
BytesResult(sptr_t lParam,const unsigned char * val,size_t len)5795 sptr_t Editor::BytesResult(sptr_t lParam, const unsigned char *val, size_t len) noexcept {
5796 	// No NUL termination: len is number of valid/displayed bytes
5797 	if ((lParam) && (len > 0)) {
5798 		char *ptr = CharPtrFromSPtr(lParam);
5799 		if (val)
5800 			memcpy(ptr, val, len);
5801 		else
5802 			*ptr = 0;
5803 	}
5804 	return val ? len : 0;
5805 }
5806 
WndProc(unsigned int iMessage,uptr_t wParam,sptr_t lParam)5807 sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5808 	//Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
5809 
5810 	// Optional macro recording hook
5811 	if (recordingMacro)
5812 		NotifyMacroRecord(iMessage, wParam, lParam);
5813 
5814 	switch (iMessage) {
5815 
5816 	case SCI_GETTEXT: {
5817 			if (lParam == 0)
5818 				return pdoc->Length() + 1;
5819 			if (wParam == 0)
5820 				return 0;
5821 			char *ptr = CharPtrFromSPtr(lParam);
5822 			const Sci_Position len = std::min<Sci_Position>(wParam - 1, pdoc->Length());
5823 			pdoc->GetCharRange(ptr, 0, len);
5824 			ptr[len] = '\0';
5825 			return len;
5826 		}
5827 
5828 	case SCI_SETTEXT: {
5829 			if (lParam == 0)
5830 				return 0;
5831 			UndoGroup ug(pdoc);
5832 			pdoc->DeleteChars(0, pdoc->Length());
5833 			SetEmptySelection(0);
5834 			const char *text = CharPtrFromSPtr(lParam);
5835 			pdoc->InsertString(0, text, strlen(text));
5836 			return 1;
5837 		}
5838 
5839 	case SCI_GETTEXTLENGTH:
5840 		return pdoc->Length();
5841 
5842 	case SCI_CUT:
5843 		Cut();
5844 		SetLastXChosen();
5845 		break;
5846 
5847 	case SCI_COPY:
5848 		Copy();
5849 		break;
5850 
5851 	case SCI_COPYALLOWLINE:
5852 		CopyAllowLine();
5853 		break;
5854 
5855 	case SCI_VERTICALCENTRECARET:
5856 		VerticalCentreCaret();
5857 		break;
5858 
5859 	case SCI_MOVESELECTEDLINESUP:
5860 		MoveSelectedLinesUp();
5861 		break;
5862 
5863 	case SCI_MOVESELECTEDLINESDOWN:
5864 		MoveSelectedLinesDown();
5865 		break;
5866 
5867 	case SCI_COPYRANGE:
5868 		CopyRangeToClipboard(static_cast<Sci::Position>(wParam), lParam);
5869 		break;
5870 
5871 	case SCI_COPYTEXT:
5872 		CopyText(wParam, CharPtrFromSPtr(lParam));
5873 		break;
5874 
5875 	case SCI_PASTE:
5876 		Paste();
5877 		if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5878 			SetLastXChosen();
5879 		}
5880 		EnsureCaretVisible();
5881 		break;
5882 
5883 	case SCI_CLEAR:
5884 		Clear();
5885 		SetLastXChosen();
5886 		EnsureCaretVisible();
5887 		break;
5888 
5889 	case SCI_UNDO:
5890 		Undo();
5891 		SetLastXChosen();
5892 		break;
5893 
5894 	case SCI_CANUNDO:
5895 		return (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0;
5896 
5897 	case SCI_EMPTYUNDOBUFFER:
5898 		pdoc->DeleteUndoHistory();
5899 		return 0;
5900 
5901 	case SCI_GETFIRSTVISIBLELINE:
5902 		return topLine;
5903 
5904 	case SCI_SETFIRSTVISIBLELINE:
5905 		ScrollTo(static_cast<Sci::Line>(wParam));
5906 		break;
5907 
5908 	case SCI_GETLINE: {	// Risk of overwriting the end of the buffer
5909 			const Sci::Position lineStart =
5910 				pdoc->LineStart(static_cast<Sci::Line>(wParam));
5911 			const Sci::Position lineEnd =
5912 				pdoc->LineStart(static_cast<Sci::Line>(wParam + 1));
5913 			// not NUL terminated
5914 			const Sci::Position len = lineEnd - lineStart;
5915 			if (lParam == 0) {
5916 				return len;
5917 			}
5918 			char *ptr = CharPtrFromSPtr(lParam);
5919 			pdoc->GetCharRange(ptr, lineStart, len);
5920 			return len;
5921 		}
5922 
5923 	case SCI_GETLINECOUNT:
5924 		if (pdoc->LinesTotal() == 0)
5925 			return 1;
5926 		else
5927 			return pdoc->LinesTotal();
5928 
5929 	case SCI_GETMODIFY:
5930 		return !pdoc->IsSavePoint();
5931 
5932 	case SCI_SETSEL: {
5933 			Sci::Position nStart = static_cast<Sci::Position>(wParam);
5934 			Sci::Position nEnd = lParam;
5935 			if (nEnd < 0)
5936 				nEnd = pdoc->Length();
5937 			if (nStart < 0)
5938 				nStart = nEnd; 	// Remove selection
5939 			InvalidateSelection(SelectionRange(nStart, nEnd));
5940 			sel.Clear();
5941 			sel.selType = Selection::selStream;
5942 			SetSelection(nEnd, nStart);
5943 			EnsureCaretVisible();
5944 		}
5945 		break;
5946 
5947 	case SCI_GETSELTEXT: {
5948 			SelectionText selectedText;
5949 			CopySelectionRange(&selectedText);
5950 			if (lParam == 0) {
5951 				return selectedText.LengthWithTerminator();
5952 			} else {
5953 				char *ptr = CharPtrFromSPtr(lParam);
5954 				size_t iChar = selectedText.Length();
5955 				if (iChar) {
5956 					memcpy(ptr, selectedText.Data(), iChar);
5957 					ptr[iChar++] = '\0';
5958 				} else {
5959 					ptr[0] = '\0';
5960 				}
5961 				return iChar;
5962 			}
5963 		}
5964 
5965 	case SCI_LINEFROMPOSITION:
5966 		if (static_cast<Sci::Position>(wParam) < 0)
5967 			return 0;
5968 		return pdoc->LineFromPosition(static_cast<Sci::Position>(wParam));
5969 
5970 	case SCI_POSITIONFROMLINE:
5971 		if (static_cast<Sci::Position>(wParam) < 0)
5972 			wParam = pdoc->LineFromPosition(SelectionStart().Position());
5973 		if (wParam == 0)
5974 			return 0; 	// Even if there is no text, there is a first line that starts at 0
5975 		if (static_cast<Sci::Line>(wParam) > pdoc->LinesTotal())
5976 			return -1;
5977 		//if (wParam > pdoc->LineFromPosition(pdoc->Length()))	// Useful test, anyway...
5978 		//	return -1;
5979 		return pdoc->LineStart(static_cast<Sci::Position>(wParam));
5980 
5981 		// Replacement of the old Scintilla interpretation of EM_LINELENGTH
5982 	case SCI_LINELENGTH:
5983 		if ((static_cast<Sci::Position>(wParam) < 0) ||
5984 		        (static_cast<Sci::Position>(wParam) > pdoc->LineFromPosition(pdoc->Length())))
5985 			return 0;
5986 		return pdoc->LineStart(static_cast<Sci::Position>(wParam) + 1) - pdoc->LineStart(static_cast<Sci::Position>(wParam));
5987 
5988 	case SCI_REPLACESEL: {
5989 			if (lParam == 0)
5990 				return 0;
5991 			UndoGroup ug(pdoc);
5992 			ClearSelection();
5993 			const char *replacement = CharPtrFromSPtr(lParam);
5994 			const Sci::Position lengthInserted = pdoc->InsertString(
5995 				sel.MainCaret(), replacement, strlen(replacement));
5996 			SetEmptySelection(sel.MainCaret() + lengthInserted);
5997 			SetLastXChosen();
5998 			EnsureCaretVisible();
5999 		}
6000 		break;
6001 
6002 	case SCI_SETTARGETSTART:
6003 		targetRange.start.SetPosition(static_cast<Sci::Position>(wParam));
6004 		break;
6005 
6006 	case SCI_GETTARGETSTART:
6007 		return targetRange.start.Position();
6008 
6009 	case SCI_SETTARGETSTARTVIRTUALSPACE:
6010 		targetRange.start.SetVirtualSpace(static_cast<Sci::Position>(wParam));
6011 		break;
6012 
6013 	case SCI_GETTARGETSTARTVIRTUALSPACE:
6014 		return targetRange.start.VirtualSpace();
6015 
6016 	case SCI_SETTARGETEND:
6017 		targetRange.end.SetPosition(static_cast<Sci::Position>(wParam));
6018 		break;
6019 
6020 	case SCI_GETTARGETEND:
6021 		return targetRange.end.Position();
6022 
6023 	case SCI_SETTARGETENDVIRTUALSPACE:
6024 		targetRange.end.SetVirtualSpace(static_cast<Sci::Position>(wParam));
6025 		break;
6026 
6027 	case SCI_GETTARGETENDVIRTUALSPACE:
6028 		return targetRange.end.VirtualSpace();
6029 
6030 	case SCI_SETTARGETRANGE:
6031 		targetRange.start.SetPosition(static_cast<Sci::Position>(wParam));
6032 		targetRange.end.SetPosition(lParam);
6033 		break;
6034 
6035 	case SCI_TARGETWHOLEDOCUMENT:
6036 		targetRange.start.SetPosition(0);
6037 		targetRange.end.SetPosition(pdoc->Length());
6038 		break;
6039 
6040 	case SCI_TARGETFROMSELECTION:
6041 		targetRange.start = sel.RangeMain().Start();
6042 		targetRange.end = sel.RangeMain().End();
6043 		break;
6044 
6045 	case SCI_GETTARGETTEXT: {
6046 			std::string text = RangeText(targetRange.start.Position(), targetRange.end.Position());
6047 			return BytesResult(lParam, reinterpret_cast<const unsigned char *>(text.c_str()), text.length());
6048 		}
6049 
6050 	case SCI_REPLACETARGET:
6051 		PLATFORM_ASSERT(lParam);
6052 		return ReplaceTarget(false, CharPtrFromSPtr(lParam), static_cast<Sci::Position>(wParam));
6053 
6054 	case SCI_REPLACETARGETRE:
6055 		PLATFORM_ASSERT(lParam);
6056 		return ReplaceTarget(true, CharPtrFromSPtr(lParam), static_cast<Sci::Position>(wParam));
6057 
6058 	case SCI_SEARCHINTARGET:
6059 		PLATFORM_ASSERT(lParam);
6060 		return SearchInTarget(CharPtrFromSPtr(lParam), static_cast<Sci::Position>(wParam));
6061 
6062 	case SCI_SETSEARCHFLAGS:
6063 		searchFlags = static_cast<int>(wParam);
6064 		break;
6065 
6066 	case SCI_GETSEARCHFLAGS:
6067 		return searchFlags;
6068 
6069 	case SCI_GETTAG:
6070 		return GetTag(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
6071 
6072 	case SCI_POSITIONBEFORE:
6073 		return pdoc->MovePositionOutsideChar(static_cast<Sci::Position>(wParam) - 1, -1, true);
6074 
6075 	case SCI_POSITIONAFTER:
6076 		return pdoc->MovePositionOutsideChar(static_cast<Sci::Position>(wParam) + 1, 1, true);
6077 
6078 	case SCI_POSITIONRELATIVE:
6079 		return std::clamp<Sci::Position>(pdoc->GetRelativePosition(
6080 			static_cast<Sci::Position>(wParam), lParam),
6081 			0, pdoc->Length());
6082 
6083 	case SCI_POSITIONRELATIVECODEUNITS:
6084 		return std::clamp<Sci::Position>(pdoc->GetRelativePositionUTF16(
6085 			static_cast<Sci::Position>(wParam), lParam),
6086 			0, pdoc->Length());
6087 
6088 	case SCI_LINESCROLL:
6089 		ScrollTo(topLine + static_cast<Sci::Line>(lParam));
6090 		HorizontalScrollTo(xOffset + static_cast<int>(wParam) * static_cast<int>(vs.spaceWidth));
6091 		return 1;
6092 
6093 	case SCI_SETXOFFSET:
6094 		xOffset = static_cast<int>(wParam);
6095 		ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
6096 		SetHorizontalScrollPos();
6097 		Redraw();
6098 		break;
6099 
6100 	case SCI_GETXOFFSET:
6101 		return xOffset;
6102 
6103 	case SCI_CHOOSECARETX:
6104 		SetLastXChosen();
6105 		break;
6106 
6107 	case SCI_SCROLLCARET:
6108 		EnsureCaretVisible();
6109 		break;
6110 
6111 	case SCI_SETREADONLY:
6112 		pdoc->SetReadOnly(wParam != 0);
6113 		return 1;
6114 
6115 	case SCI_GETREADONLY:
6116 		return pdoc->IsReadOnly();
6117 
6118 	case SCI_CANPASTE:
6119 		return CanPaste();
6120 
6121 	case SCI_POINTXFROMPOSITION:
6122 		if (lParam < 0) {
6123 			return 0;
6124 		} else {
6125 			const Point pt = LocationFromPosition(lParam);
6126 			// Convert to view-relative
6127 			return static_cast<int>(pt.x) - vs.textStart + vs.fixedColumnWidth;
6128 		}
6129 
6130 	case SCI_POINTYFROMPOSITION:
6131 		if (lParam < 0) {
6132 			return 0;
6133 		} else {
6134 			const Point pt = LocationFromPosition(lParam);
6135 			return static_cast<int>(pt.y);
6136 		}
6137 
6138 	case SCI_FINDTEXT:
6139 		return FindText(wParam, lParam);
6140 
6141 	case SCI_GETTEXTRANGE: {
6142 			if (lParam == 0)
6143 				return 0;
6144 			Sci_TextRange *tr = static_cast<Sci_TextRange *>(PtrFromSPtr(lParam));
6145 			Sci::Position cpMax = static_cast<Sci::Position>(tr->chrg.cpMax);
6146 			if (cpMax == -1)
6147 				cpMax = pdoc->Length();
6148 			PLATFORM_ASSERT(cpMax <= pdoc->Length());
6149 			Sci::Position len = cpMax - tr->chrg.cpMin; 	// No -1 as cpMin and cpMax are referring to inter character positions
6150 			pdoc->GetCharRange(tr->lpstrText, tr->chrg.cpMin, len);
6151 			// Spec says copied text is terminated with a NUL
6152 			tr->lpstrText[len] = '\0';
6153 			return len; 	// Not including NUL
6154 		}
6155 
6156 	case SCI_HIDESELECTION:
6157 		view.hideSelection = wParam != 0;
6158 		Redraw();
6159 		break;
6160 
6161 	case SCI_FORMATRANGE:
6162 		return FormatRange(wParam != 0, static_cast<Sci_RangeToFormat *>(PtrFromSPtr(lParam)));
6163 
6164 	case SCI_GETMARGINLEFT:
6165 		return vs.leftMarginWidth;
6166 
6167 	case SCI_GETMARGINRIGHT:
6168 		return vs.rightMarginWidth;
6169 
6170 	case SCI_SETMARGINLEFT:
6171 		lastXChosen += static_cast<int>(lParam) - vs.leftMarginWidth;
6172 		vs.leftMarginWidth = static_cast<int>(lParam);
6173 		InvalidateStyleRedraw();
6174 		break;
6175 
6176 	case SCI_SETMARGINRIGHT:
6177 		vs.rightMarginWidth = static_cast<int>(lParam);
6178 		InvalidateStyleRedraw();
6179 		break;
6180 
6181 		// Control specific mesages
6182 
6183 	case SCI_ADDTEXT: {
6184 			if (lParam == 0)
6185 				return 0;
6186 			const Sci::Position lengthInserted = pdoc->InsertString(
6187 				CurrentPosition(), CharPtrFromSPtr(lParam), static_cast<Sci::Position>(wParam));
6188 			SetEmptySelection(sel.MainCaret() + lengthInserted);
6189 			return 0;
6190 		}
6191 
6192 	case SCI_ADDSTYLEDTEXT:
6193 		if (lParam)
6194 			AddStyledText(CharPtrFromSPtr(lParam), static_cast<Sci::Position>(wParam));
6195 		return 0;
6196 
6197 	case SCI_INSERTTEXT: {
6198 			if (lParam == 0)
6199 				return 0;
6200 			Sci::Position insertPos = static_cast<Sci::Position>(wParam);
6201 			if (insertPos == -1)
6202 				insertPos = CurrentPosition();
6203 			Sci::Position newCurrent = CurrentPosition();
6204 			const char *sz = CharPtrFromSPtr(lParam);
6205 			const Sci::Position lengthInserted = pdoc->InsertString(insertPos, sz, strlen(sz));
6206 			if (newCurrent > insertPos)
6207 				newCurrent += lengthInserted;
6208 			SetEmptySelection(newCurrent);
6209 			return 0;
6210 		}
6211 
6212 	case SCI_CHANGEINSERTION:
6213 		PLATFORM_ASSERT(lParam);
6214 		pdoc->ChangeInsertion(CharPtrFromSPtr(lParam), static_cast<Sci::Position>(wParam));
6215 		return 0;
6216 
6217 	case SCI_APPENDTEXT:
6218 		pdoc->InsertString(pdoc->Length(),
6219 			CharPtrFromSPtr(lParam), static_cast<Sci::Position>(wParam));
6220 		return 0;
6221 
6222 	case SCI_CLEARALL:
6223 		ClearAll();
6224 		return 0;
6225 
6226 	case SCI_DELETERANGE:
6227 		pdoc->DeleteChars(static_cast<Sci::Position>(wParam), lParam);
6228 		return 0;
6229 
6230 	case SCI_CLEARDOCUMENTSTYLE:
6231 		ClearDocumentStyle();
6232 		return 0;
6233 
6234 	case SCI_SETUNDOCOLLECTION:
6235 		pdoc->SetUndoCollection(wParam != 0);
6236 		return 0;
6237 
6238 	case SCI_GETUNDOCOLLECTION:
6239 		return pdoc->IsCollectingUndo();
6240 
6241 	case SCI_BEGINUNDOACTION:
6242 		pdoc->BeginUndoAction();
6243 		return 0;
6244 
6245 	case SCI_ENDUNDOACTION:
6246 		pdoc->EndUndoAction();
6247 		return 0;
6248 
6249 	case SCI_GETCARETPERIOD:
6250 		return caret.period;
6251 
6252 	case SCI_SETCARETPERIOD:
6253 		CaretSetPeriod(static_cast<int>(wParam));
6254 		break;
6255 
6256 	case SCI_GETWORDCHARS:
6257 		return pdoc->GetCharsOfClass(CharClassify::ccWord, UCharPtrFromSPtr(lParam));
6258 
6259 	case SCI_SETWORDCHARS: {
6260 			pdoc->SetDefaultCharClasses(false);
6261 			if (lParam == 0)
6262 				return 0;
6263 			pdoc->SetCharClasses(ConstUCharPtrFromSPtr(lParam), CharClassify::ccWord);
6264 		}
6265 		break;
6266 
6267 	case SCI_GETWHITESPACECHARS:
6268 		return pdoc->GetCharsOfClass(CharClassify::ccSpace, UCharPtrFromSPtr(lParam));
6269 
6270 	case SCI_SETWHITESPACECHARS: {
6271 			if (lParam == 0)
6272 				return 0;
6273 			pdoc->SetCharClasses(ConstUCharPtrFromSPtr(lParam), CharClassify::ccSpace);
6274 		}
6275 		break;
6276 
6277 	case SCI_GETPUNCTUATIONCHARS:
6278 		return pdoc->GetCharsOfClass(CharClassify::ccPunctuation, UCharPtrFromSPtr(lParam));
6279 
6280 	case SCI_SETPUNCTUATIONCHARS: {
6281 			if (lParam == 0)
6282 				return 0;
6283 			pdoc->SetCharClasses(ConstUCharPtrFromSPtr(lParam), CharClassify::ccPunctuation);
6284 		}
6285 		break;
6286 
6287 	case SCI_SETCHARSDEFAULT:
6288 		pdoc->SetDefaultCharClasses(true);
6289 		break;
6290 
6291 	case SCI_SETCHARACTERCATEGORYOPTIMIZATION:
6292 		pdoc->SetCharacterCategoryOptimization(static_cast<int>(wParam));
6293 		break;
6294 
6295 	case SCI_GETCHARACTERCATEGORYOPTIMIZATION:
6296 		return pdoc->CharacterCategoryOptimization();
6297 
6298 	case SCI_GETLENGTH:
6299 		return pdoc->Length();
6300 
6301 	case SCI_ALLOCATE:
6302 		pdoc->Allocate(static_cast<Sci::Position>(wParam));
6303 		break;
6304 
6305 	case SCI_GETCHARAT:
6306 		return pdoc->CharAt(static_cast<Sci::Position>(wParam));
6307 
6308 	case SCI_SETCURRENTPOS:
6309 		if (sel.IsRectangular()) {
6310 			sel.Rectangular().caret.SetPosition(static_cast<Sci::Position>(wParam));
6311 			SetRectangularRange();
6312 			Redraw();
6313 		} else {
6314 			SetSelection(static_cast<Sci::Position>(wParam), sel.MainAnchor());
6315 		}
6316 		break;
6317 
6318 	case SCI_GETCURRENTPOS:
6319 		return sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret();
6320 
6321 	case SCI_SETANCHOR:
6322 		if (sel.IsRectangular()) {
6323 			sel.Rectangular().anchor.SetPosition(static_cast<Sci::Position>(wParam));
6324 			SetRectangularRange();
6325 			Redraw();
6326 		} else {
6327 			SetSelection(sel.MainCaret(), static_cast<Sci::Position>(wParam));
6328 		}
6329 		break;
6330 
6331 	case SCI_GETANCHOR:
6332 		return sel.IsRectangular() ? sel.Rectangular().anchor.Position() : sel.MainAnchor();
6333 
6334 	case SCI_SETSELECTIONSTART:
6335 		SetSelection(std::max(sel.MainCaret(), static_cast<Sci::Position>(wParam)), static_cast<Sci::Position>(wParam));
6336 		break;
6337 
6338 	case SCI_GETSELECTIONSTART:
6339 		return sel.LimitsForRectangularElseMain().start.Position();
6340 
6341 	case SCI_SETSELECTIONEND:
6342 		SetSelection(static_cast<Sci::Position>(wParam), std::min(sel.MainAnchor(), static_cast<Sci::Position>(wParam)));
6343 		break;
6344 
6345 	case SCI_GETSELECTIONEND:
6346 		return sel.LimitsForRectangularElseMain().end.Position();
6347 
6348 	case SCI_SETEMPTYSELECTION:
6349 		SetEmptySelection(static_cast<Sci::Position>(wParam));
6350 		break;
6351 
6352 	case SCI_SETPRINTMAGNIFICATION:
6353 		view.printParameters.magnification = static_cast<int>(wParam);
6354 		break;
6355 
6356 	case SCI_GETPRINTMAGNIFICATION:
6357 		return view.printParameters.magnification;
6358 
6359 	case SCI_SETPRINTCOLOURMODE:
6360 		view.printParameters.colourMode = static_cast<int>(wParam);
6361 		break;
6362 
6363 	case SCI_GETPRINTCOLOURMODE:
6364 		return view.printParameters.colourMode;
6365 
6366 	case SCI_SETPRINTWRAPMODE:
6367 		view.printParameters.wrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone;
6368 		break;
6369 
6370 	case SCI_GETPRINTWRAPMODE:
6371 		return view.printParameters.wrapState;
6372 
6373 	case SCI_GETSTYLEAT:
6374 		if (static_cast<Sci::Position>(wParam) >= pdoc->Length())
6375 			return 0;
6376 		else
6377 			return pdoc->StyleAt(static_cast<Sci::Position>(wParam));
6378 
6379 	case SCI_REDO:
6380 		Redo();
6381 		break;
6382 
6383 	case SCI_SELECTALL:
6384 		SelectAll();
6385 		break;
6386 
6387 	case SCI_SETSAVEPOINT:
6388 		pdoc->SetSavePoint();
6389 		break;
6390 
6391 	case SCI_GETSTYLEDTEXT: {
6392 			if (lParam == 0)
6393 				return 0;
6394 			Sci_TextRange *tr = static_cast<Sci_TextRange *>(PtrFromSPtr(lParam));
6395 			Sci::Position iPlace = 0;
6396 			for (Sci::Position iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
6397 				tr->lpstrText[iPlace++] = pdoc->CharAt(iChar);
6398 				tr->lpstrText[iPlace++] = pdoc->StyleAt(iChar);
6399 			}
6400 			tr->lpstrText[iPlace] = '\0';
6401 			tr->lpstrText[iPlace + 1] = '\0';
6402 			return iPlace;
6403 		}
6404 
6405 	case SCI_CANREDO:
6406 		return (pdoc->CanRedo() && !pdoc->IsReadOnly()) ? 1 : 0;
6407 
6408 	case SCI_MARKERLINEFROMHANDLE:
6409 		return pdoc->LineFromHandle(static_cast<int>(wParam));
6410 
6411 	case SCI_MARKERDELETEHANDLE:
6412 		pdoc->DeleteMarkFromHandle(static_cast<int>(wParam));
6413 		break;
6414 
6415 	case SCI_GETVIEWWS:
6416 		return vs.viewWhitespace;
6417 
6418 	case SCI_SETVIEWWS:
6419 		vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(wParam);
6420 		Redraw();
6421 		break;
6422 
6423 	case SCI_GETTABDRAWMODE:
6424 		return vs.tabDrawMode;
6425 
6426 	case SCI_SETTABDRAWMODE:
6427 		vs.tabDrawMode = static_cast<TabDrawMode>(wParam);
6428 		Redraw();
6429 		break;
6430 
6431 	case SCI_GETWHITESPACESIZE:
6432 		return vs.whitespaceSize;
6433 
6434 	case SCI_SETWHITESPACESIZE:
6435 		vs.whitespaceSize = static_cast<int>(wParam);
6436 		Redraw();
6437 		break;
6438 
6439 	case SCI_POSITIONFROMPOINT:
6440 		return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
6441 					    false, false);
6442 
6443 	case SCI_POSITIONFROMPOINTCLOSE:
6444 		return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
6445 					    true, false);
6446 
6447 	case SCI_CHARPOSITIONFROMPOINT:
6448 		return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
6449 					    false, true);
6450 
6451 	case SCI_CHARPOSITIONFROMPOINTCLOSE:
6452 		return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
6453 					    true, true);
6454 
6455 	case SCI_GOTOLINE:
6456 		GoToLine(static_cast<Sci::Line>(wParam));
6457 		break;
6458 
6459 	case SCI_GOTOPOS:
6460 		SetEmptySelection(static_cast<Sci::Position>(wParam));
6461 		EnsureCaretVisible();
6462 		break;
6463 
6464 	case SCI_GETCURLINE: {
6465 			const Sci::Line lineCurrentPos = pdoc->SciLineFromPosition(sel.MainCaret());
6466 			const Sci::Position lineStart = pdoc->LineStart(lineCurrentPos);
6467 			const Sci::Position lineEnd = pdoc->LineStart(lineCurrentPos + 1);
6468 			if (lParam == 0) {
6469 				return 1 + lineEnd - lineStart;
6470 			}
6471 			PLATFORM_ASSERT(wParam > 0);
6472 			char *ptr = CharPtrFromSPtr(lParam);
6473 			const Sci::Position len = std::min<uptr_t>(lineEnd - lineStart, wParam - 1);
6474 			pdoc->GetCharRange(ptr, lineStart, len);
6475 			ptr[len] = '\0';
6476 			return sel.MainCaret() - lineStart;
6477 		}
6478 
6479 	case SCI_GETENDSTYLED:
6480 		return pdoc->GetEndStyled();
6481 
6482 	case SCI_GETEOLMODE:
6483 		return pdoc->eolMode;
6484 
6485 	case SCI_SETEOLMODE:
6486 		pdoc->eolMode = static_cast<int>(wParam);
6487 		break;
6488 
6489 	case SCI_SETLINEENDTYPESALLOWED:
6490 		if (pdoc->SetLineEndTypesAllowed(static_cast<int>(wParam))) {
6491 			pcs->Clear();
6492 			pcs->InsertLines(0, pdoc->LinesTotal() - 1);
6493 			SetAnnotationHeights(0, pdoc->LinesTotal());
6494 			InvalidateStyleRedraw();
6495 		}
6496 		break;
6497 
6498 	case SCI_GETLINEENDTYPESALLOWED:
6499 		return pdoc->GetLineEndTypesAllowed();
6500 
6501 	case SCI_GETLINEENDTYPESACTIVE:
6502 		return pdoc->GetLineEndTypesActive();
6503 
6504 	case SCI_STARTSTYLING:
6505 		pdoc->StartStyling(static_cast<Sci::Position>(wParam));
6506 		break;
6507 
6508 	case SCI_SETSTYLING:
6509 		if (static_cast<Sci::Position>(wParam) < 0)
6510 			errorStatus = SC_STATUS_FAILURE;
6511 		else
6512 			pdoc->SetStyleFor(static_cast<Sci::Position>(wParam), static_cast<char>(lParam));
6513 		break;
6514 
6515 	case SCI_SETSTYLINGEX:             // Specify a complete styling buffer
6516 		if (lParam == 0)
6517 			return 0;
6518 		pdoc->SetStyles(static_cast<Sci::Position>(wParam), CharPtrFromSPtr(lParam));
6519 		break;
6520 
6521 	case SCI_SETBUFFEREDDRAW:
6522 		view.bufferedDraw = wParam != 0;
6523 		break;
6524 
6525 	case SCI_GETBUFFEREDDRAW:
6526 		return view.bufferedDraw;
6527 
6528 #ifdef INCLUDE_DEPRECATED_FEATURES
6529 	case SCI_GETTWOPHASEDRAW:
6530 		return view.phasesDraw == EditView::phasesTwo;
6531 
6532 	case SCI_SETTWOPHASEDRAW:
6533 		if (view.SetTwoPhaseDraw(wParam != 0))
6534 			InvalidateStyleRedraw();
6535 		break;
6536 #endif
6537 
6538 	case SCI_GETPHASESDRAW:
6539 		return view.phasesDraw;
6540 
6541 	case SCI_SETPHASESDRAW:
6542 		if (view.SetPhasesDraw(static_cast<int>(wParam)))
6543 			InvalidateStyleRedraw();
6544 		break;
6545 
6546 	case SCI_SETFONTQUALITY:
6547 		vs.extraFontFlag &= ~SC_EFF_QUALITY_MASK;
6548 		vs.extraFontFlag |= (wParam & SC_EFF_QUALITY_MASK);
6549 		InvalidateStyleRedraw();
6550 		break;
6551 
6552 	case SCI_GETFONTQUALITY:
6553 		return (vs.extraFontFlag & SC_EFF_QUALITY_MASK);
6554 
6555 	case SCI_SETTABWIDTH:
6556 		if (wParam > 0) {
6557 			pdoc->tabInChars = static_cast<int>(wParam);
6558 			if (pdoc->indentInChars == 0)
6559 				pdoc->actualIndentInChars = pdoc->tabInChars;
6560 		}
6561 		InvalidateStyleRedraw();
6562 		break;
6563 
6564 	case SCI_GETTABWIDTH:
6565 		return pdoc->tabInChars;
6566 
6567 	case SCI_SETTABMINIMUMWIDTH:
6568 		SetAppearance(view.tabWidthMinimumPixels, static_cast<int>(wParam));
6569 		break;
6570 
6571 	case SCI_GETTABMINIMUMWIDTH:
6572 		return view.tabWidthMinimumPixels;
6573 
6574 	case SCI_CLEARTABSTOPS:
6575 		if (view.ClearTabstops(static_cast<Sci::Line>(wParam))) {
6576 			const DocModification mh(SC_MOD_CHANGETABSTOPS, 0, 0, 0, nullptr, static_cast<Sci::Line>(wParam));
6577 			NotifyModified(pdoc, mh, nullptr);
6578 		}
6579 		break;
6580 
6581 	case SCI_ADDTABSTOP:
6582 		if (view.AddTabstop(static_cast<Sci::Line>(wParam), static_cast<int>(lParam))) {
6583 			const DocModification mh(SC_MOD_CHANGETABSTOPS, 0, 0, 0, nullptr, static_cast<Sci::Line>(wParam));
6584 			NotifyModified(pdoc, mh, nullptr);
6585 		}
6586 		break;
6587 
6588 	case SCI_GETNEXTTABSTOP:
6589 		return view.GetNextTabstop(static_cast<Sci::Line>(wParam), static_cast<int>(lParam));
6590 
6591 	case SCI_SETINDENT:
6592 		pdoc->indentInChars = static_cast<int>(wParam);
6593 		if (pdoc->indentInChars != 0)
6594 			pdoc->actualIndentInChars = pdoc->indentInChars;
6595 		else
6596 			pdoc->actualIndentInChars = pdoc->tabInChars;
6597 		InvalidateStyleRedraw();
6598 		break;
6599 
6600 	case SCI_GETINDENT:
6601 		return pdoc->indentInChars;
6602 
6603 	case SCI_SETUSETABS:
6604 		pdoc->useTabs = wParam != 0;
6605 		InvalidateStyleRedraw();
6606 		break;
6607 
6608 	case SCI_GETUSETABS:
6609 		return pdoc->useTabs;
6610 
6611 	case SCI_SETLINEINDENTATION:
6612 		pdoc->SetLineIndentation(static_cast<Sci::Line>(wParam), lParam);
6613 		break;
6614 
6615 	case SCI_GETLINEINDENTATION:
6616 		return pdoc->GetLineIndentation(static_cast<Sci::Line>(wParam));
6617 
6618 	case SCI_GETLINEINDENTPOSITION:
6619 		return pdoc->GetLineIndentPosition(static_cast<Sci::Line>(wParam));
6620 
6621 	case SCI_SETTABINDENTS:
6622 		pdoc->tabIndents = wParam != 0;
6623 		break;
6624 
6625 	case SCI_GETTABINDENTS:
6626 		return pdoc->tabIndents;
6627 
6628 	case SCI_SETBACKSPACEUNINDENTS:
6629 		pdoc->backspaceUnindents = wParam != 0;
6630 		break;
6631 
6632 	case SCI_GETBACKSPACEUNINDENTS:
6633 		return pdoc->backspaceUnindents;
6634 
6635 	case SCI_SETMOUSEDWELLTIME:
6636 		dwellDelay = static_cast<int>(wParam);
6637 		ticksToDwell = dwellDelay;
6638 		break;
6639 
6640 	case SCI_GETMOUSEDWELLTIME:
6641 		return dwellDelay;
6642 
6643 	case SCI_WORDSTARTPOSITION:
6644 		return pdoc->ExtendWordSelect(static_cast<Sci::Position>(wParam), -1, lParam != 0);
6645 
6646 	case SCI_WORDENDPOSITION:
6647 		return pdoc->ExtendWordSelect(static_cast<Sci::Position>(wParam), 1, lParam != 0);
6648 
6649 	case SCI_ISRANGEWORD:
6650 		return pdoc->IsWordAt(static_cast<Sci::Position>(wParam), lParam);
6651 
6652 	case SCI_SETIDLESTYLING:
6653 		idleStyling = static_cast<int>(wParam);
6654 		break;
6655 
6656 	case SCI_GETIDLESTYLING:
6657 		return idleStyling;
6658 
6659 	case SCI_SETWRAPMODE:
6660 		if (vs.SetWrapState(static_cast<int>(wParam))) {
6661 			xOffset = 0;
6662 			ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
6663 			InvalidateStyleRedraw();
6664 			ReconfigureScrollBars();
6665 		}
6666 		break;
6667 
6668 	case SCI_GETWRAPMODE:
6669 		return vs.wrapState;
6670 
6671 	case SCI_SETWRAPVISUALFLAGS:
6672 		if (vs.SetWrapVisualFlags(static_cast<int>(wParam))) {
6673 			InvalidateStyleRedraw();
6674 			ReconfigureScrollBars();
6675 		}
6676 		break;
6677 
6678 	case SCI_GETWRAPVISUALFLAGS:
6679 		return vs.wrapVisualFlags;
6680 
6681 	case SCI_SETWRAPVISUALFLAGSLOCATION:
6682 		if (vs.SetWrapVisualFlagsLocation(static_cast<int>(wParam))) {
6683 			InvalidateStyleRedraw();
6684 		}
6685 		break;
6686 
6687 	case SCI_GETWRAPVISUALFLAGSLOCATION:
6688 		return vs.wrapVisualFlagsLocation;
6689 
6690 	case SCI_SETWRAPSTARTINDENT:
6691 		if (vs.SetWrapVisualStartIndent(static_cast<int>(wParam))) {
6692 			InvalidateStyleRedraw();
6693 			ReconfigureScrollBars();
6694 		}
6695 		break;
6696 
6697 	case SCI_GETWRAPSTARTINDENT:
6698 		return vs.wrapVisualStartIndent;
6699 
6700 	case SCI_SETWRAPINDENTMODE:
6701 		if (vs.SetWrapIndentMode(static_cast<int>(wParam))) {
6702 			InvalidateStyleRedraw();
6703 			ReconfigureScrollBars();
6704 		}
6705 		break;
6706 
6707 	case SCI_GETWRAPINDENTMODE:
6708 		return vs.wrapIndentMode;
6709 
6710 	case SCI_SETLAYOUTCACHE:
6711 		view.llc.SetLevel(static_cast<int>(wParam));
6712 		break;
6713 
6714 	case SCI_GETLAYOUTCACHE:
6715 		return view.llc.GetLevel();
6716 
6717 	case SCI_SETPOSITIONCACHE:
6718 		view.posCache.SetSize(wParam);
6719 		break;
6720 
6721 	case SCI_GETPOSITIONCACHE:
6722 		return view.posCache.GetSize();
6723 
6724 	case SCI_SETSCROLLWIDTH:
6725 		PLATFORM_ASSERT(wParam > 0);
6726 		if ((wParam > 0) && (wParam != static_cast<unsigned int>(scrollWidth))) {
6727 			view.lineWidthMaxSeen = 0;
6728 			scrollWidth = static_cast<int>(wParam);
6729 			SetScrollBars();
6730 		}
6731 		break;
6732 
6733 	case SCI_GETSCROLLWIDTH:
6734 		return scrollWidth;
6735 
6736 	case SCI_SETSCROLLWIDTHTRACKING:
6737 		trackLineWidth = wParam != 0;
6738 		break;
6739 
6740 	case SCI_GETSCROLLWIDTHTRACKING:
6741 		return trackLineWidth;
6742 
6743 	case SCI_LINESJOIN:
6744 		LinesJoin();
6745 		break;
6746 
6747 	case SCI_LINESSPLIT:
6748 		LinesSplit(static_cast<int>(wParam));
6749 		break;
6750 
6751 	case SCI_TEXTWIDTH:
6752 		PLATFORM_ASSERT(wParam < vs.styles.size());
6753 		PLATFORM_ASSERT(lParam);
6754 		return TextWidth(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
6755 
6756 	case SCI_TEXTHEIGHT:
6757 		RefreshStyleData();
6758 		return vs.lineHeight;
6759 
6760 	case SCI_SETENDATLASTLINE:
6761 		PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
6762 		if (endAtLastLine != (wParam != 0)) {
6763 			endAtLastLine = wParam != 0;
6764 			SetScrollBars();
6765 		}
6766 		break;
6767 
6768 	case SCI_GETENDATLASTLINE:
6769 		return endAtLastLine;
6770 
6771 	case SCI_SETCARETSTICKY:
6772 		PLATFORM_ASSERT(wParam <= SC_CARETSTICKY_WHITESPACE);
6773 		if (wParam <= SC_CARETSTICKY_WHITESPACE) {
6774 			caretSticky = static_cast<int>(wParam);
6775 		}
6776 		break;
6777 
6778 	case SCI_GETCARETSTICKY:
6779 		return caretSticky;
6780 
6781 	case SCI_TOGGLECARETSTICKY:
6782 		caretSticky = !caretSticky;
6783 		break;
6784 
6785 	case SCI_GETCOLUMN:
6786 		return pdoc->GetColumn(static_cast<Sci::Position>(wParam));
6787 
6788 	case SCI_FINDCOLUMN:
6789 		return pdoc->FindColumn(static_cast<Sci::Line>(wParam), lParam);
6790 
6791 	case SCI_SETHSCROLLBAR :
6792 		if (horizontalScrollBarVisible != (wParam != 0)) {
6793 			horizontalScrollBarVisible = wParam != 0;
6794 			SetScrollBars();
6795 			ReconfigureScrollBars();
6796 		}
6797 		break;
6798 
6799 	case SCI_GETHSCROLLBAR:
6800 		return horizontalScrollBarVisible;
6801 
6802 	case SCI_SETVSCROLLBAR:
6803 		if (verticalScrollBarVisible != (wParam != 0)) {
6804 			verticalScrollBarVisible = wParam != 0;
6805 			SetScrollBars();
6806 			ReconfigureScrollBars();
6807 			if (verticalScrollBarVisible)
6808 				SetVerticalScrollPos();
6809 		}
6810 		break;
6811 
6812 	case SCI_GETVSCROLLBAR:
6813 		return verticalScrollBarVisible;
6814 
6815 	case SCI_SETINDENTATIONGUIDES:
6816 		vs.viewIndentationGuides = static_cast<IndentView>(wParam);
6817 		Redraw();
6818 		break;
6819 
6820 	case SCI_GETINDENTATIONGUIDES:
6821 		return vs.viewIndentationGuides;
6822 
6823 	case SCI_SETHIGHLIGHTGUIDE:
6824 		if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
6825 			highlightGuideColumn = static_cast<int>(wParam);
6826 			Redraw();
6827 		}
6828 		break;
6829 
6830 	case SCI_GETHIGHLIGHTGUIDE:
6831 		return highlightGuideColumn;
6832 
6833 	case SCI_GETLINEENDPOSITION:
6834 		return pdoc->LineEnd(static_cast<Sci::Line>(wParam));
6835 
6836 	case SCI_SETCODEPAGE:
6837 		if (ValidCodePage(static_cast<int>(wParam))) {
6838 			if (pdoc->SetDBCSCodePage(static_cast<int>(wParam))) {
6839 				pcs->Clear();
6840 				pcs->InsertLines(0, pdoc->LinesTotal() - 1);
6841 				SetAnnotationHeights(0, pdoc->LinesTotal());
6842 				InvalidateStyleRedraw();
6843 				SetRepresentations();
6844 			}
6845 		}
6846 		break;
6847 
6848 	case SCI_GETCODEPAGE:
6849 		return pdoc->dbcsCodePage;
6850 
6851 	case SCI_SETIMEINTERACTION:
6852 		imeInteraction = static_cast<EditModel::IMEInteraction>(wParam);
6853 		break;
6854 
6855 	case SCI_GETIMEINTERACTION:
6856 		return imeInteraction;
6857 
6858 	case SCI_SETBIDIRECTIONAL:
6859 		// SCI_SETBIDIRECTIONAL is implemented on platform subclasses if they support bidirectional text.
6860 		break;
6861 
6862 	case SCI_GETBIDIRECTIONAL:
6863 		return static_cast<sptr_t>(bidirectional);
6864 
6865 	case SCI_GETLINECHARACTERINDEX:
6866 		return pdoc->LineCharacterIndex();
6867 
6868 	case SCI_ALLOCATELINECHARACTERINDEX:
6869 		pdoc->AllocateLineCharacterIndex(static_cast<int>(wParam));
6870 		break;
6871 
6872 	case SCI_RELEASELINECHARACTERINDEX:
6873 		pdoc->ReleaseLineCharacterIndex(static_cast<int>(wParam));
6874 		break;
6875 
6876 	case SCI_LINEFROMINDEXPOSITION:
6877 		return pdoc->LineFromPositionIndex(static_cast<Sci::Position>(wParam), static_cast<int>(lParam));
6878 
6879 	case SCI_INDEXPOSITIONFROMLINE:
6880 		return pdoc->IndexLineStart(static_cast<Sci::Line>(wParam), static_cast<int>(lParam));
6881 
6882 		// Marker definition and setting
6883 	case SCI_MARKERDEFINE:
6884 		if (wParam <= MARKER_MAX) {
6885 			vs.markers[wParam].markType = static_cast<int>(lParam);
6886 			vs.CalcLargestMarkerHeight();
6887 		}
6888 		InvalidateStyleData();
6889 		RedrawSelMargin();
6890 		break;
6891 
6892 	case SCI_MARKERSYMBOLDEFINED:
6893 		if (wParam <= MARKER_MAX)
6894 			return vs.markers[wParam].markType;
6895 		else
6896 			return 0;
6897 
6898 	case SCI_MARKERSETFORE:
6899 		if (wParam <= MARKER_MAX)
6900 			vs.markers[wParam].fore = ColourDesired(static_cast<int>(lParam));
6901 		InvalidateStyleData();
6902 		RedrawSelMargin();
6903 		break;
6904 	case SCI_MARKERSETBACKSELECTED:
6905 		if (wParam <= MARKER_MAX)
6906 			vs.markers[wParam].backSelected = ColourDesired(static_cast<int>(lParam));
6907 		InvalidateStyleData();
6908 		RedrawSelMargin();
6909 		break;
6910 	case SCI_MARKERENABLEHIGHLIGHT:
6911 		marginView.highlightDelimiter.isEnabled = wParam == 1;
6912 		RedrawSelMargin();
6913 		break;
6914 	case SCI_MARKERSETBACK:
6915 		if (wParam <= MARKER_MAX)
6916 			vs.markers[wParam].back = ColourDesired(static_cast<int>(lParam));
6917 		InvalidateStyleData();
6918 		RedrawSelMargin();
6919 		break;
6920 	case SCI_MARKERSETALPHA:
6921 		if (wParam <= MARKER_MAX)
6922 			vs.markers[wParam].alpha = static_cast<int>(lParam);
6923 		InvalidateStyleRedraw();
6924 		break;
6925 	case SCI_MARKERADD: {
6926 			const int markerID = pdoc->AddMark(static_cast<Sci::Line>(wParam), static_cast<int>(lParam));
6927 			return markerID;
6928 		}
6929 	case SCI_MARKERADDSET:
6930 		if (lParam != 0)
6931 			pdoc->AddMarkSet(static_cast<Sci::Line>(wParam), static_cast<int>(lParam));
6932 		break;
6933 
6934 	case SCI_MARKERDELETE:
6935 		pdoc->DeleteMark(static_cast<Sci::Line>(wParam), static_cast<int>(lParam));
6936 		break;
6937 
6938 	case SCI_MARKERDELETEALL:
6939 		pdoc->DeleteAllMarks(static_cast<int>(wParam));
6940 		break;
6941 
6942 	case SCI_MARKERGET:
6943 		return pdoc->GetMark(static_cast<Sci::Line>(wParam));
6944 
6945 	case SCI_MARKERNEXT:
6946 		return pdoc->MarkerNext(static_cast<Sci::Line>(wParam), static_cast<int>(lParam));
6947 
6948 	case SCI_MARKERPREVIOUS: {
6949 			for (Sci::Line iLine = static_cast<Sci::Line>(wParam); iLine >= 0; iLine--) {
6950 				if ((pdoc->GetMark(iLine) & lParam) != 0)
6951 					return iLine;
6952 			}
6953 		}
6954 		return -1;
6955 
6956 	case SCI_MARKERDEFINEPIXMAP:
6957 		if (wParam <= MARKER_MAX) {
6958 			vs.markers[wParam].SetXPM(CharPtrFromSPtr(lParam));
6959 			vs.CalcLargestMarkerHeight();
6960 		}
6961 		InvalidateStyleData();
6962 		RedrawSelMargin();
6963 		break;
6964 
6965 	case SCI_RGBAIMAGESETWIDTH:
6966 		sizeRGBAImage.x = static_cast<XYPOSITION>(wParam);
6967 		break;
6968 
6969 	case SCI_RGBAIMAGESETHEIGHT:
6970 		sizeRGBAImage.y = static_cast<XYPOSITION>(wParam);
6971 		break;
6972 
6973 	case SCI_RGBAIMAGESETSCALE:
6974 		scaleRGBAImage = static_cast<float>(wParam);
6975 		break;
6976 
6977 	case SCI_MARKERDEFINERGBAIMAGE:
6978 		if (wParam <= MARKER_MAX) {
6979 			vs.markers[wParam].SetRGBAImage(sizeRGBAImage, scaleRGBAImage / 100.0f, ConstUCharPtrFromSPtr(lParam));
6980 			vs.CalcLargestMarkerHeight();
6981 		}
6982 		InvalidateStyleData();
6983 		RedrawSelMargin();
6984 		break;
6985 
6986 	case SCI_SETMARGINTYPEN:
6987 		if (ValidMargin(wParam)) {
6988 			vs.ms[wParam].style = static_cast<int>(lParam);
6989 			InvalidateStyleRedraw();
6990 		}
6991 		break;
6992 
6993 	case SCI_GETMARGINTYPEN:
6994 		if (ValidMargin(wParam))
6995 			return vs.ms[wParam].style;
6996 		else
6997 			return 0;
6998 
6999 	case SCI_SETMARGINWIDTHN:
7000 		if (ValidMargin(wParam)) {
7001 			// Short-circuit if the width is unchanged, to avoid unnecessary redraw.
7002 			if (vs.ms[wParam].width != lParam) {
7003 				lastXChosen += static_cast<int>(lParam) - vs.ms[wParam].width;
7004 				vs.ms[wParam].width = static_cast<int>(lParam);
7005 				InvalidateStyleRedraw();
7006 			}
7007 		}
7008 		break;
7009 
7010 	case SCI_GETMARGINWIDTHN:
7011 		if (ValidMargin(wParam))
7012 			return vs.ms[wParam].width;
7013 		else
7014 			return 0;
7015 
7016 	case SCI_SETMARGINMASKN:
7017 		if (ValidMargin(wParam)) {
7018 			vs.ms[wParam].mask = static_cast<int>(lParam);
7019 			InvalidateStyleRedraw();
7020 		}
7021 		break;
7022 
7023 	case SCI_GETMARGINMASKN:
7024 		if (ValidMargin(wParam))
7025 			return vs.ms[wParam].mask;
7026 		else
7027 			return 0;
7028 
7029 	case SCI_SETMARGINSENSITIVEN:
7030 		if (ValidMargin(wParam)) {
7031 			vs.ms[wParam].sensitive = lParam != 0;
7032 			InvalidateStyleRedraw();
7033 		}
7034 		break;
7035 
7036 	case SCI_GETMARGINSENSITIVEN:
7037 		if (ValidMargin(wParam))
7038 			return vs.ms[wParam].sensitive ? 1 : 0;
7039 		else
7040 			return 0;
7041 
7042 	case SCI_SETMARGINCURSORN:
7043 		if (ValidMargin(wParam))
7044 			vs.ms[wParam].cursor = static_cast<int>(lParam);
7045 		break;
7046 
7047 	case SCI_GETMARGINCURSORN:
7048 		if (ValidMargin(wParam))
7049 			return vs.ms[wParam].cursor;
7050 		else
7051 			return 0;
7052 
7053 	case SCI_SETMARGINBACKN:
7054 		if (ValidMargin(wParam)) {
7055 			vs.ms[wParam].back = ColourDesired(static_cast<int>(lParam));
7056 			InvalidateStyleRedraw();
7057 		}
7058 		break;
7059 
7060 	case SCI_GETMARGINBACKN:
7061 		if (ValidMargin(wParam))
7062 			return vs.ms[wParam].back.AsInteger();
7063 		else
7064 			return 0;
7065 
7066 	case SCI_SETMARGINS:
7067 		if (wParam < 1000)
7068 			vs.ms.resize(wParam);
7069 		break;
7070 
7071 	case SCI_GETMARGINS:
7072 		return vs.ms.size();
7073 
7074 	case SCI_STYLECLEARALL:
7075 		vs.ClearStyles();
7076 		InvalidateStyleRedraw();
7077 		break;
7078 
7079 	case SCI_STYLESETFORE:
7080 	case SCI_STYLESETBACK:
7081 	case SCI_STYLESETBOLD:
7082 	case SCI_STYLESETWEIGHT:
7083 	case SCI_STYLESETITALIC:
7084 	case SCI_STYLESETEOLFILLED:
7085 	case SCI_STYLESETSIZE:
7086 	case SCI_STYLESETSIZEFRACTIONAL:
7087 	case SCI_STYLESETFONT:
7088 	case SCI_STYLESETUNDERLINE:
7089 	case SCI_STYLESETCASE:
7090 	case SCI_STYLESETCHARACTERSET:
7091 	case SCI_STYLESETVISIBLE:
7092 	case SCI_STYLESETCHANGEABLE:
7093 	case SCI_STYLESETHOTSPOT:
7094 		StyleSetMessage(iMessage, wParam, lParam);
7095 		break;
7096 
7097 	case SCI_STYLEGETFORE:
7098 	case SCI_STYLEGETBACK:
7099 	case SCI_STYLEGETBOLD:
7100 	case SCI_STYLEGETWEIGHT:
7101 	case SCI_STYLEGETITALIC:
7102 	case SCI_STYLEGETEOLFILLED:
7103 	case SCI_STYLEGETSIZE:
7104 	case SCI_STYLEGETSIZEFRACTIONAL:
7105 	case SCI_STYLEGETFONT:
7106 	case SCI_STYLEGETUNDERLINE:
7107 	case SCI_STYLEGETCASE:
7108 	case SCI_STYLEGETCHARACTERSET:
7109 	case SCI_STYLEGETVISIBLE:
7110 	case SCI_STYLEGETCHANGEABLE:
7111 	case SCI_STYLEGETHOTSPOT:
7112 		return StyleGetMessage(iMessage, wParam, lParam);
7113 
7114 	case SCI_STYLERESETDEFAULT:
7115 		vs.ResetDefaultStyle();
7116 		InvalidateStyleRedraw();
7117 		break;
7118 
7119 #ifdef INCLUDE_DEPRECATED_FEATURES
7120 	case SCI_SETSTYLEBITS:
7121 		vs.EnsureStyle(0xff);
7122 		break;
7123 
7124 	case SCI_GETSTYLEBITS:
7125 		return 8;
7126 #endif
7127 
7128 	case SCI_SETLINESTATE:
7129 		return pdoc->SetLineState(static_cast<Sci::Line>(wParam), static_cast<int>(lParam));
7130 
7131 	case SCI_GETLINESTATE:
7132 		return pdoc->GetLineState(static_cast<Sci::Line>(wParam));
7133 
7134 	case SCI_GETMAXLINESTATE:
7135 		return pdoc->GetMaxLineState();
7136 
7137 	case SCI_GETCARETLINEVISIBLE:
7138 		return vs.showCaretLineBackground;
7139 	case SCI_SETCARETLINEVISIBLE:
7140 		vs.showCaretLineBackground = wParam != 0;
7141 		InvalidateStyleRedraw();
7142 		break;
7143 	case SCI_GETCARETLINEVISIBLEALWAYS:
7144 		return vs.alwaysShowCaretLineBackground;
7145 	case SCI_SETCARETLINEVISIBLEALWAYS:
7146 		vs.alwaysShowCaretLineBackground = wParam != 0;
7147 		InvalidateStyleRedraw();
7148 		break;
7149 
7150 	case SCI_GETCARETLINEFRAME:
7151 		return vs.caretLineFrame;
7152 	case SCI_SETCARETLINEFRAME:
7153 		vs.caretLineFrame = static_cast<int>(wParam);
7154 		InvalidateStyleRedraw();
7155 		break;
7156 	case SCI_GETCARETLINEBACK:
7157 		return vs.caretLineBackground.AsInteger();
7158 	case SCI_SETCARETLINEBACK:
7159 		vs.caretLineBackground = ColourDesired(static_cast<int>(wParam));
7160 		InvalidateStyleRedraw();
7161 		break;
7162 	case SCI_GETCARETLINEBACKALPHA:
7163 		return vs.caretLineAlpha;
7164 	case SCI_SETCARETLINEBACKALPHA:
7165 		vs.caretLineAlpha = static_cast<int>(wParam);
7166 		InvalidateStyleRedraw();
7167 		break;
7168 
7169 		// Folding messages
7170 
7171 	case SCI_VISIBLEFROMDOCLINE:
7172 		return pcs->DisplayFromDoc(static_cast<Sci::Line>(wParam));
7173 
7174 	case SCI_DOCLINEFROMVISIBLE:
7175 		return pcs->DocFromDisplay(static_cast<Sci::Line>(wParam));
7176 
7177 	case SCI_WRAPCOUNT:
7178 		return WrapCount(static_cast<Sci::Line>(wParam));
7179 
7180 	case SCI_SETFOLDLEVEL: {
7181 			const int prev = pdoc->SetLevel(static_cast<Sci::Line>(wParam), static_cast<int>(lParam));
7182 			if (prev != static_cast<int>(lParam))
7183 				RedrawSelMargin();
7184 			return prev;
7185 		}
7186 
7187 	case SCI_GETFOLDLEVEL:
7188 		return pdoc->GetLevel(static_cast<Sci::Line>(wParam));
7189 
7190 	case SCI_GETLASTCHILD:
7191 		return pdoc->GetLastChild(static_cast<Sci::Line>(wParam), static_cast<int>(lParam));
7192 
7193 	case SCI_GETFOLDPARENT:
7194 		return pdoc->GetFoldParent(static_cast<Sci::Line>(wParam));
7195 
7196 	case SCI_SHOWLINES:
7197 		pcs->SetVisible(static_cast<Sci::Line>(wParam), static_cast<Sci::Line>(lParam), true);
7198 		SetScrollBars();
7199 		Redraw();
7200 		break;
7201 
7202 	case SCI_HIDELINES:
7203 		if (wParam > 0)
7204 			pcs->SetVisible(static_cast<Sci::Line>(wParam), static_cast<Sci::Line>(lParam), false);
7205 		SetScrollBars();
7206 		Redraw();
7207 		break;
7208 
7209 	case SCI_GETLINEVISIBLE:
7210 		return pcs->GetVisible(static_cast<Sci::Line>(wParam));
7211 
7212 	case SCI_GETALLLINESVISIBLE:
7213 		return pcs->HiddenLines() ? 0 : 1;
7214 
7215 	case SCI_SETFOLDEXPANDED:
7216 		SetFoldExpanded(static_cast<Sci::Line>(wParam), lParam != 0);
7217 		break;
7218 
7219 	case SCI_GETFOLDEXPANDED:
7220 		return pcs->GetExpanded(static_cast<Sci::Line>(wParam));
7221 
7222 	case SCI_SETAUTOMATICFOLD:
7223 		foldAutomatic = static_cast<int>(wParam);
7224 		break;
7225 
7226 	case SCI_GETAUTOMATICFOLD:
7227 		return foldAutomatic;
7228 
7229 	case SCI_SETFOLDFLAGS:
7230 		foldFlags = static_cast<int>(wParam);
7231 		Redraw();
7232 		break;
7233 
7234 	case SCI_TOGGLEFOLDSHOWTEXT:
7235 		pcs->SetFoldDisplayText(static_cast<Sci::Line>(wParam), CharPtrFromSPtr(lParam));
7236 		FoldLine(static_cast<Sci::Line>(wParam), SC_FOLDACTION_TOGGLE);
7237 		break;
7238 
7239 	case SCI_FOLDDISPLAYTEXTSETSTYLE:
7240 		foldDisplayTextStyle = static_cast<int>(wParam);
7241 		Redraw();
7242 		break;
7243 
7244 	case SCI_FOLDDISPLAYTEXTGETSTYLE:
7245 		return foldDisplayTextStyle;
7246 
7247 	case SCI_SETDEFAULTFOLDDISPLAYTEXT:
7248 		SetDefaultFoldDisplayText(CharPtrFromSPtr(lParam));
7249 		Redraw();
7250 		break;
7251 
7252 	case SCI_GETDEFAULTFOLDDISPLAYTEXT:
7253 		return StringResult(lParam, GetDefaultFoldDisplayText());
7254 
7255 	case SCI_TOGGLEFOLD:
7256 		FoldLine(static_cast<Sci::Line>(wParam), SC_FOLDACTION_TOGGLE);
7257 		break;
7258 
7259 	case SCI_FOLDLINE:
7260 		FoldLine(static_cast<Sci::Line>(wParam), static_cast<int>(lParam));
7261 		break;
7262 
7263 	case SCI_FOLDCHILDREN:
7264 		FoldExpand(static_cast<Sci::Line>(wParam), static_cast<int>(lParam), pdoc->GetLevel(static_cast<int>(wParam)));
7265 		break;
7266 
7267 	case SCI_FOLDALL:
7268 		FoldAll(static_cast<int>(wParam));
7269 		break;
7270 
7271 	case SCI_EXPANDCHILDREN:
7272 		FoldExpand(static_cast<Sci::Line>(wParam), SC_FOLDACTION_EXPAND, static_cast<int>(lParam));
7273 		break;
7274 
7275 	case SCI_CONTRACTEDFOLDNEXT:
7276 		return ContractedFoldNext(static_cast<Sci::Line>(wParam));
7277 
7278 	case SCI_ENSUREVISIBLE:
7279 		EnsureLineVisible(static_cast<Sci::Line>(wParam), false);
7280 		break;
7281 
7282 	case SCI_ENSUREVISIBLEENFORCEPOLICY:
7283 		EnsureLineVisible(static_cast<Sci::Line>(wParam), true);
7284 		break;
7285 
7286 	case SCI_SCROLLRANGE:
7287 		ScrollRange(SelectionRange(static_cast<Sci::Position>(wParam), lParam));
7288 		break;
7289 
7290 	case SCI_SEARCHANCHOR:
7291 		SearchAnchor();
7292 		break;
7293 
7294 	case SCI_SEARCHNEXT:
7295 	case SCI_SEARCHPREV:
7296 		return SearchText(iMessage, wParam, lParam);
7297 
7298 	case SCI_SETXCARETPOLICY:
7299 		caretPolicies.x = CaretPolicy(wParam, lParam);
7300 		break;
7301 
7302 	case SCI_SETYCARETPOLICY:
7303 		caretPolicies.y = CaretPolicy(wParam, lParam);
7304 		break;
7305 
7306 	case SCI_SETVISIBLEPOLICY:
7307 		visiblePolicy = CaretPolicy(wParam, lParam);
7308 		break;
7309 
7310 	case SCI_LINESONSCREEN:
7311 		return LinesOnScreen();
7312 
7313 	case SCI_SETSELFORE:
7314 		vs.selColours.fore = ColourOptional(wParam, lParam);
7315 		vs.selAdditionalForeground = ColourDesired(static_cast<int>(lParam));
7316 		InvalidateStyleRedraw();
7317 		break;
7318 
7319 	case SCI_SETSELBACK:
7320 		vs.selColours.back = ColourOptional(wParam, lParam);
7321 		vs.selAdditionalBackground = ColourDesired(static_cast<int>(lParam));
7322 		InvalidateStyleRedraw();
7323 		break;
7324 
7325 	case SCI_SETSELALPHA:
7326 		vs.selAlpha = static_cast<int>(wParam);
7327 		vs.selAdditionalAlpha = static_cast<int>(wParam);
7328 		InvalidateStyleRedraw();
7329 		break;
7330 
7331 	case SCI_GETSELALPHA:
7332 		return vs.selAlpha;
7333 
7334 	case SCI_GETSELEOLFILLED:
7335 		return vs.selEOLFilled;
7336 
7337 	case SCI_SETSELEOLFILLED:
7338 		vs.selEOLFilled = wParam != 0;
7339 		InvalidateStyleRedraw();
7340 		break;
7341 
7342 	case SCI_SETWHITESPACEFORE:
7343 		vs.whitespaceColours.fore = ColourOptional(wParam, lParam);
7344 		InvalidateStyleRedraw();
7345 		break;
7346 
7347 	case SCI_SETWHITESPACEBACK:
7348 		vs.whitespaceColours.back = ColourOptional(wParam, lParam);
7349 		InvalidateStyleRedraw();
7350 		break;
7351 
7352 	case SCI_SETCARETFORE:
7353 		vs.caretcolour = ColourDesired(static_cast<int>(wParam));
7354 		InvalidateStyleRedraw();
7355 		break;
7356 
7357 	case SCI_GETCARETFORE:
7358 		return vs.caretcolour.AsInteger();
7359 
7360 	case SCI_SETCARETSTYLE:
7361 		if (wParam <= (CARETSTYLE_BLOCK | CARETSTYLE_OVERSTRIKE_BLOCK | CARETSTYLE_BLOCK_AFTER))
7362 			vs.caretStyle = static_cast<int>(wParam);
7363 		else
7364 			/* Default to the line caret */
7365 			vs.caretStyle = CARETSTYLE_LINE;
7366 		InvalidateStyleRedraw();
7367 		break;
7368 
7369 	case SCI_GETCARETSTYLE:
7370 		return vs.caretStyle;
7371 
7372 	case SCI_SETCARETWIDTH:
7373 		vs.caretWidth = std::clamp(static_cast<int>(wParam), 0, 3);
7374 		InvalidateStyleRedraw();
7375 		break;
7376 
7377 	case SCI_GETCARETWIDTH:
7378 		return vs.caretWidth;
7379 
7380 	case SCI_ASSIGNCMDKEY:
7381 		kmap.AssignCmdKey(LowShortFromWParam(wParam),
7382 			HighShortFromWParam(wParam), static_cast<unsigned int>(lParam));
7383 		break;
7384 
7385 	case SCI_CLEARCMDKEY:
7386 		kmap.AssignCmdKey(LowShortFromWParam(wParam),
7387 			HighShortFromWParam(wParam), SCI_NULL);
7388 		break;
7389 
7390 	case SCI_CLEARALLCMDKEYS:
7391 		kmap.Clear();
7392 		break;
7393 
7394 	case SCI_INDICSETSTYLE:
7395 		if (wParam <= INDICATOR_MAX) {
7396 			vs.indicators[wParam].sacNormal.style = static_cast<int>(lParam);
7397 			vs.indicators[wParam].sacHover.style = static_cast<int>(lParam);
7398 			InvalidateStyleRedraw();
7399 		}
7400 		break;
7401 
7402 	case SCI_INDICGETSTYLE:
7403 		return (wParam <= INDICATOR_MAX) ? vs.indicators[wParam].sacNormal.style : 0;
7404 
7405 	case SCI_INDICSETFORE:
7406 		if (wParam <= INDICATOR_MAX) {
7407 			vs.indicators[wParam].sacNormal.fore = ColourDesired(static_cast<int>(lParam));
7408 			vs.indicators[wParam].sacHover.fore = ColourDesired(static_cast<int>(lParam));
7409 			InvalidateStyleRedraw();
7410 		}
7411 		break;
7412 
7413 	case SCI_INDICGETFORE:
7414 		return (wParam <= INDICATOR_MAX) ? vs.indicators[wParam].sacNormal.fore.AsInteger() : 0;
7415 
7416 	case SCI_INDICSETHOVERSTYLE:
7417 		if (wParam <= INDICATOR_MAX) {
7418 			vs.indicators[wParam].sacHover.style = static_cast<int>(lParam);
7419 			InvalidateStyleRedraw();
7420 		}
7421 		break;
7422 
7423 	case SCI_INDICGETHOVERSTYLE:
7424 		return (wParam <= INDICATOR_MAX) ? vs.indicators[wParam].sacHover.style : 0;
7425 
7426 	case SCI_INDICSETHOVERFORE:
7427 		if (wParam <= INDICATOR_MAX) {
7428 			vs.indicators[wParam].sacHover.fore = ColourDesired(static_cast<int>(lParam));
7429 			InvalidateStyleRedraw();
7430 		}
7431 		break;
7432 
7433 	case SCI_INDICGETHOVERFORE:
7434 		return (wParam <= INDICATOR_MAX) ? vs.indicators[wParam].sacHover.fore.AsInteger() : 0;
7435 
7436 	case SCI_INDICSETFLAGS:
7437 		if (wParam <= INDICATOR_MAX) {
7438 			vs.indicators[wParam].SetFlags(static_cast<int>(lParam));
7439 			InvalidateStyleRedraw();
7440 		}
7441 		break;
7442 
7443 	case SCI_INDICGETFLAGS:
7444 		return (wParam <= INDICATOR_MAX) ? vs.indicators[wParam].Flags() : 0;
7445 
7446 	case SCI_INDICSETUNDER:
7447 		if (wParam <= INDICATOR_MAX) {
7448 			vs.indicators[wParam].under = lParam != 0;
7449 			InvalidateStyleRedraw();
7450 		}
7451 		break;
7452 
7453 	case SCI_INDICGETUNDER:
7454 		return (wParam <= INDICATOR_MAX) ? vs.indicators[wParam].under : 0;
7455 
7456 	case SCI_INDICSETALPHA:
7457 		if (wParam <= INDICATOR_MAX && lParam >=0 && lParam <= 255) {
7458 			vs.indicators[wParam].fillAlpha = static_cast<int>(lParam);
7459 			InvalidateStyleRedraw();
7460 		}
7461 		break;
7462 
7463 	case SCI_INDICGETALPHA:
7464 		return (wParam <= INDICATOR_MAX) ? vs.indicators[wParam].fillAlpha : 0;
7465 
7466 	case SCI_INDICSETOUTLINEALPHA:
7467 		if (wParam <= INDICATOR_MAX && lParam >=0 && lParam <= 255) {
7468 			vs.indicators[wParam].outlineAlpha = static_cast<int>(lParam);
7469 			InvalidateStyleRedraw();
7470 		}
7471 		break;
7472 
7473 	case SCI_INDICGETOUTLINEALPHA:
7474 		return (wParam <= INDICATOR_MAX) ? vs.indicators[wParam].outlineAlpha : 0;
7475 
7476 	case SCI_SETINDICATORCURRENT:
7477 		pdoc->DecorationSetCurrentIndicator(static_cast<int>(wParam));
7478 		break;
7479 	case SCI_GETINDICATORCURRENT:
7480 		return pdoc->decorations->GetCurrentIndicator();
7481 	case SCI_SETINDICATORVALUE:
7482 		pdoc->decorations->SetCurrentValue(static_cast<int>(wParam));
7483 		break;
7484 	case SCI_GETINDICATORVALUE:
7485 		return pdoc->decorations->GetCurrentValue();
7486 
7487 	case SCI_INDICATORFILLRANGE:
7488 		pdoc->DecorationFillRange(static_cast<Sci::Position>(wParam),
7489 			pdoc->decorations->GetCurrentValue(), lParam);
7490 		break;
7491 
7492 	case SCI_INDICATORCLEARRANGE:
7493 		pdoc->DecorationFillRange(static_cast<Sci::Position>(wParam), 0,
7494 			lParam);
7495 		break;
7496 
7497 	case SCI_INDICATORALLONFOR:
7498 		return pdoc->decorations->AllOnFor(static_cast<Sci::Position>(wParam));
7499 
7500 	case SCI_INDICATORVALUEAT:
7501 		return pdoc->decorations->ValueAt(static_cast<int>(wParam), lParam);
7502 
7503 	case SCI_INDICATORSTART:
7504 		return pdoc->decorations->Start(static_cast<int>(wParam), lParam);
7505 
7506 	case SCI_INDICATOREND:
7507 		return pdoc->decorations->End(static_cast<int>(wParam), lParam);
7508 
7509 	case SCI_LINEDOWN:
7510 	case SCI_LINEDOWNEXTEND:
7511 	case SCI_PARADOWN:
7512 	case SCI_PARADOWNEXTEND:
7513 	case SCI_LINEUP:
7514 	case SCI_LINEUPEXTEND:
7515 	case SCI_PARAUP:
7516 	case SCI_PARAUPEXTEND:
7517 	case SCI_CHARLEFT:
7518 	case SCI_CHARLEFTEXTEND:
7519 	case SCI_CHARRIGHT:
7520 	case SCI_CHARRIGHTEXTEND:
7521 	case SCI_WORDLEFT:
7522 	case SCI_WORDLEFTEXTEND:
7523 	case SCI_WORDRIGHT:
7524 	case SCI_WORDRIGHTEXTEND:
7525 	case SCI_WORDLEFTEND:
7526 	case SCI_WORDLEFTENDEXTEND:
7527 	case SCI_WORDRIGHTEND:
7528 	case SCI_WORDRIGHTENDEXTEND:
7529 	case SCI_HOME:
7530 	case SCI_HOMEEXTEND:
7531 	case SCI_LINEEND:
7532 	case SCI_LINEENDEXTEND:
7533 	case SCI_HOMEWRAP:
7534 	case SCI_HOMEWRAPEXTEND:
7535 	case SCI_LINEENDWRAP:
7536 	case SCI_LINEENDWRAPEXTEND:
7537 	case SCI_DOCUMENTSTART:
7538 	case SCI_DOCUMENTSTARTEXTEND:
7539 	case SCI_DOCUMENTEND:
7540 	case SCI_DOCUMENTENDEXTEND:
7541 	case SCI_SCROLLTOSTART:
7542 	case SCI_SCROLLTOEND:
7543 
7544 	case SCI_STUTTEREDPAGEUP:
7545 	case SCI_STUTTEREDPAGEUPEXTEND:
7546 	case SCI_STUTTEREDPAGEDOWN:
7547 	case SCI_STUTTEREDPAGEDOWNEXTEND:
7548 
7549 	case SCI_PAGEUP:
7550 	case SCI_PAGEUPEXTEND:
7551 	case SCI_PAGEDOWN:
7552 	case SCI_PAGEDOWNEXTEND:
7553 	case SCI_EDITTOGGLEOVERTYPE:
7554 	case SCI_CANCEL:
7555 	case SCI_DELETEBACK:
7556 	case SCI_TAB:
7557 	case SCI_BACKTAB:
7558 	case SCI_NEWLINE:
7559 	case SCI_FORMFEED:
7560 	case SCI_VCHOME:
7561 	case SCI_VCHOMEEXTEND:
7562 	case SCI_VCHOMEWRAP:
7563 	case SCI_VCHOMEWRAPEXTEND:
7564 	case SCI_VCHOMEDISPLAY:
7565 	case SCI_VCHOMEDISPLAYEXTEND:
7566 	case SCI_ZOOMIN:
7567 	case SCI_ZOOMOUT:
7568 	case SCI_DELWORDLEFT:
7569 	case SCI_DELWORDRIGHT:
7570 	case SCI_DELWORDRIGHTEND:
7571 	case SCI_DELLINELEFT:
7572 	case SCI_DELLINERIGHT:
7573 	case SCI_LINECOPY:
7574 	case SCI_LINECUT:
7575 	case SCI_LINEDELETE:
7576 	case SCI_LINETRANSPOSE:
7577 	case SCI_LINEREVERSE:
7578 	case SCI_LINEDUPLICATE:
7579 	case SCI_LOWERCASE:
7580 	case SCI_UPPERCASE:
7581 	case SCI_LINESCROLLDOWN:
7582 	case SCI_LINESCROLLUP:
7583 	case SCI_WORDPARTLEFT:
7584 	case SCI_WORDPARTLEFTEXTEND:
7585 	case SCI_WORDPARTRIGHT:
7586 	case SCI_WORDPARTRIGHTEXTEND:
7587 	case SCI_DELETEBACKNOTLINE:
7588 	case SCI_HOMEDISPLAY:
7589 	case SCI_HOMEDISPLAYEXTEND:
7590 	case SCI_LINEENDDISPLAY:
7591 	case SCI_LINEENDDISPLAYEXTEND:
7592 	case SCI_LINEDOWNRECTEXTEND:
7593 	case SCI_LINEUPRECTEXTEND:
7594 	case SCI_CHARLEFTRECTEXTEND:
7595 	case SCI_CHARRIGHTRECTEXTEND:
7596 	case SCI_HOMERECTEXTEND:
7597 	case SCI_VCHOMERECTEXTEND:
7598 	case SCI_LINEENDRECTEXTEND:
7599 	case SCI_PAGEUPRECTEXTEND:
7600 	case SCI_PAGEDOWNRECTEXTEND:
7601 	case SCI_SELECTIONDUPLICATE:
7602 		return KeyCommand(iMessage);
7603 
7604 	case SCI_BRACEHIGHLIGHT:
7605 		SetBraceHighlight(static_cast<Sci::Position>(wParam), lParam, STYLE_BRACELIGHT);
7606 		break;
7607 
7608 	case SCI_BRACEHIGHLIGHTINDICATOR:
7609 		if (lParam >= 0 && lParam <= INDICATOR_MAX) {
7610 			vs.braceHighlightIndicatorSet = wParam != 0;
7611 			vs.braceHighlightIndicator = static_cast<int>(lParam);
7612 		}
7613 		break;
7614 
7615 	case SCI_BRACEBADLIGHT:
7616 		SetBraceHighlight(static_cast<Sci::Position>(wParam), -1, STYLE_BRACEBAD);
7617 		break;
7618 
7619 	case SCI_BRACEBADLIGHTINDICATOR:
7620 		if (lParam >= 0 && lParam <= INDICATOR_MAX) {
7621 			vs.braceBadLightIndicatorSet = wParam != 0;
7622 			vs.braceBadLightIndicator = static_cast<int>(lParam);
7623 		}
7624 		break;
7625 
7626 	case SCI_BRACEMATCH:
7627 		// wParam is position of char to find brace for,
7628 		// lParam is maximum amount of text to restyle to find it
7629 		return pdoc->BraceMatch(static_cast<Sci::Position>(wParam), lParam);
7630 
7631 	case SCI_GETVIEWEOL:
7632 		return vs.viewEOL;
7633 
7634 	case SCI_SETVIEWEOL:
7635 		vs.viewEOL = wParam != 0;
7636 		InvalidateStyleRedraw();
7637 		break;
7638 
7639 	case SCI_SETZOOM: {
7640 			const int zoomLevel = static_cast<int>(wParam);
7641 			if (zoomLevel != vs.zoomLevel) {
7642 				vs.zoomLevel = zoomLevel;
7643 				InvalidateStyleRedraw();
7644 				NotifyZoom();
7645 			}
7646 			break;
7647 		}
7648 
7649 	case SCI_GETZOOM:
7650 		return vs.zoomLevel;
7651 
7652 	case SCI_GETEDGECOLUMN:
7653 		return vs.theEdge.column;
7654 
7655 	case SCI_SETEDGECOLUMN:
7656 		vs.theEdge.column = static_cast<int>(wParam);
7657 		InvalidateStyleRedraw();
7658 		break;
7659 
7660 	case SCI_GETEDGEMODE:
7661 		return vs.edgeState;
7662 
7663 	case SCI_SETEDGEMODE:
7664 		vs.edgeState = static_cast<int>(wParam);
7665 		InvalidateStyleRedraw();
7666 		break;
7667 
7668 	case SCI_GETEDGECOLOUR:
7669 		return vs.theEdge.colour.AsInteger();
7670 
7671 	case SCI_SETEDGECOLOUR:
7672 		vs.theEdge.colour = ColourDesired(static_cast<int>(wParam));
7673 		InvalidateStyleRedraw();
7674 		break;
7675 
7676 	case SCI_MULTIEDGEADDLINE:
7677 		vs.theMultiEdge.push_back(EdgeProperties(wParam, lParam));
7678 		InvalidateStyleRedraw();
7679 		break;
7680 
7681 	case SCI_MULTIEDGECLEARALL:
7682 		std::vector<EdgeProperties>().swap(vs.theMultiEdge); // Free vector and memory, C++03 compatible
7683 		InvalidateStyleRedraw();
7684 		break;
7685 
7686 	case SCI_GETACCESSIBILITY:
7687 		return SC_ACCESSIBILITY_DISABLED;
7688 
7689 	case SCI_SETACCESSIBILITY:
7690 		// May be implemented by platform code.
7691 		break;
7692 
7693 	case SCI_GETDOCPOINTER:
7694 		return reinterpret_cast<sptr_t>(pdoc);
7695 
7696 	case SCI_SETDOCPOINTER:
7697 		CancelModes();
7698 		SetDocPointer(static_cast<Document *>(PtrFromSPtr(lParam)));
7699 		return 0;
7700 
7701 	case SCI_CREATEDOCUMENT: {
7702 			Document *doc = new Document(static_cast<int>(lParam));
7703 			doc->AddRef();
7704 			doc->Allocate(static_cast<Sci::Position>(wParam));
7705 			pcs = ContractionStateCreate(pdoc->IsLarge());
7706 			return reinterpret_cast<sptr_t>(doc);
7707 		}
7708 
7709 	case SCI_ADDREFDOCUMENT:
7710 		(static_cast<Document *>(PtrFromSPtr(lParam)))->AddRef();
7711 		break;
7712 
7713 	case SCI_RELEASEDOCUMENT:
7714 		(static_cast<Document *>(PtrFromSPtr(lParam)))->Release();
7715 		break;
7716 
7717 	case SCI_GETDOCUMENTOPTIONS:
7718 		return pdoc->Options();
7719 
7720 	case SCI_CREATELOADER: {
7721 			Document *doc = new Document(static_cast<int>(lParam));
7722 			doc->AddRef();
7723 			doc->Allocate(static_cast<Sci::Position>(wParam));
7724 			doc->SetUndoCollection(false);
7725 			pcs = ContractionStateCreate(pdoc->IsLarge());
7726 			return reinterpret_cast<sptr_t>(static_cast<ILoader *>(doc));
7727 		}
7728 
7729 	case SCI_SETMODEVENTMASK:
7730 		modEventMask = static_cast<int>(wParam);
7731 		return 0;
7732 
7733 	case SCI_GETMODEVENTMASK:
7734 		return modEventMask;
7735 
7736 	case SCI_SETCOMMANDEVENTS:
7737 		commandEvents = static_cast<bool>(wParam);
7738 		return 0;
7739 
7740 	case SCI_GETCOMMANDEVENTS:
7741 		return commandEvents;
7742 
7743 	case SCI_CONVERTEOLS:
7744 		pdoc->ConvertLineEnds(static_cast<int>(wParam));
7745 		SetSelection(sel.MainCaret(), sel.MainAnchor());	// Ensure selection inside document
7746 		return 0;
7747 
7748 	case SCI_SETLENGTHFORENCODE:
7749 		lengthForEncode = static_cast<Sci::Position>(wParam);
7750 		return 0;
7751 
7752 	case SCI_SELECTIONISRECTANGLE:
7753 		return sel.selType == Selection::selRectangle ? 1 : 0;
7754 
7755 	case SCI_SETSELECTIONMODE: {
7756 			switch (wParam) {
7757 			case SC_SEL_STREAM:
7758 				sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
7759 				sel.selType = Selection::selStream;
7760 				break;
7761 			case SC_SEL_RECTANGLE:
7762 				sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selRectangle));
7763 				sel.selType = Selection::selRectangle;
7764 				sel.Rectangular() = sel.RangeMain(); // adjust current selection
7765 				break;
7766 			case SC_SEL_LINES:
7767 				sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selLines));
7768 				sel.selType = Selection::selLines;
7769 				SetSelection(sel.RangeMain().caret, sel.RangeMain().anchor); // adjust current selection
7770 				break;
7771 			case SC_SEL_THIN:
7772 				sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selThin));
7773 				sel.selType = Selection::selThin;
7774 				break;
7775 			default:
7776 				sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
7777 				sel.selType = Selection::selStream;
7778 			}
7779 			InvalidateWholeSelection();
7780 			break;
7781 		}
7782 	case SCI_GETSELECTIONMODE:
7783 		switch (sel.selType) {
7784 		case Selection::selStream:
7785 			return SC_SEL_STREAM;
7786 		case Selection::selRectangle:
7787 			return SC_SEL_RECTANGLE;
7788 		case Selection::selLines:
7789 			return SC_SEL_LINES;
7790 		case Selection::selThin:
7791 			return SC_SEL_THIN;
7792 		default:	// ?!
7793 			return SC_SEL_STREAM;
7794 		}
7795 	case SCI_GETMOVEEXTENDSSELECTION:
7796 		return sel.MoveExtends();
7797 	case SCI_GETLINESELSTARTPOSITION:
7798 	case SCI_GETLINESELENDPOSITION: {
7799 			const SelectionSegment segmentLine(
7800 				SelectionPosition(pdoc->LineStart(static_cast<Sci::Position>(wParam))),
7801 				SelectionPosition(pdoc->LineEnd(static_cast<Sci::Position>(wParam))));
7802 			for (size_t r=0; r<sel.Count(); r++) {
7803 				const SelectionSegment portion = sel.Range(r).Intersect(segmentLine);
7804 				if (portion.start.IsValid()) {
7805 					return (iMessage == SCI_GETLINESELSTARTPOSITION) ? portion.start.Position() : portion.end.Position();
7806 				}
7807 			}
7808 			return INVALID_POSITION;
7809 		}
7810 
7811 	case SCI_SETOVERTYPE:
7812 		if (inOverstrike != (wParam != 0)) {
7813 			inOverstrike = wParam != 0;
7814 			ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7815 			ShowCaretAtCurrentPosition();
7816 			SetIdle(true);
7817 		}
7818 		break;
7819 
7820 	case SCI_GETOVERTYPE:
7821 		return inOverstrike ? 1 : 0;
7822 
7823 	case SCI_SETFOCUS:
7824 		SetFocusState(wParam != 0);
7825 		break;
7826 
7827 	case SCI_GETFOCUS:
7828 		return hasFocus;
7829 
7830 	case SCI_SETSTATUS:
7831 		errorStatus = static_cast<int>(wParam);
7832 		break;
7833 
7834 	case SCI_GETSTATUS:
7835 		return errorStatus;
7836 
7837 	case SCI_SETMOUSEDOWNCAPTURES:
7838 		mouseDownCaptures = wParam != 0;
7839 		break;
7840 
7841 	case SCI_GETMOUSEDOWNCAPTURES:
7842 		return mouseDownCaptures;
7843 
7844 	case SCI_SETMOUSEWHEELCAPTURES:
7845 		mouseWheelCaptures = wParam != 0;
7846 		break;
7847 
7848 	case SCI_GETMOUSEWHEELCAPTURES:
7849 		return mouseWheelCaptures;
7850 
7851 	case SCI_SETCURSOR:
7852 		cursorMode = static_cast<int>(wParam);
7853 		DisplayCursor(Window::cursorText);
7854 		break;
7855 
7856 	case SCI_GETCURSOR:
7857 		return cursorMode;
7858 
7859 	case SCI_SETCONTROLCHARSYMBOL:
7860 		vs.controlCharSymbol = static_cast<int>(wParam);
7861 		InvalidateStyleRedraw();
7862 		break;
7863 
7864 	case SCI_GETCONTROLCHARSYMBOL:
7865 		return vs.controlCharSymbol;
7866 
7867 	case SCI_SETREPRESENTATION:
7868 		reprs.SetRepresentation(ConstCharPtrFromUPtr(wParam), ConstCharPtrFromSPtr(lParam));
7869 		break;
7870 
7871 	case SCI_GETREPRESENTATION: {
7872 			const Representation *repr = reprs.RepresentationFromCharacter(
7873 				ConstCharPtrFromUPtr(wParam), UTF8MaxBytes);
7874 			if (repr) {
7875 				return StringResult(lParam, repr->stringRep.c_str());
7876 			}
7877 			return 0;
7878 		}
7879 
7880 	case SCI_CLEARREPRESENTATION:
7881 		reprs.ClearRepresentation(ConstCharPtrFromUPtr(wParam));
7882 		break;
7883 
7884 	case SCI_STARTRECORD:
7885 		recordingMacro = true;
7886 		return 0;
7887 
7888 	case SCI_STOPRECORD:
7889 		recordingMacro = false;
7890 		return 0;
7891 
7892 	case SCI_MOVECARETINSIDEVIEW:
7893 		MoveCaretInsideView();
7894 		break;
7895 
7896 	case SCI_SETFOLDMARGINCOLOUR:
7897 		vs.foldmarginColour = ColourOptional(wParam, lParam);
7898 		InvalidateStyleRedraw();
7899 		break;
7900 
7901 	case SCI_SETFOLDMARGINHICOLOUR:
7902 		vs.foldmarginHighlightColour = ColourOptional(wParam, lParam);
7903 		InvalidateStyleRedraw();
7904 		break;
7905 
7906 	case SCI_SETHOTSPOTACTIVEFORE:
7907 		vs.hotspotColours.fore = ColourOptional(wParam, lParam);
7908 		InvalidateStyleRedraw();
7909 		break;
7910 
7911 	case SCI_GETHOTSPOTACTIVEFORE:
7912 		return vs.hotspotColours.fore.AsInteger();
7913 
7914 	case SCI_SETHOTSPOTACTIVEBACK:
7915 		vs.hotspotColours.back = ColourOptional(wParam, lParam);
7916 		InvalidateStyleRedraw();
7917 		break;
7918 
7919 	case SCI_GETHOTSPOTACTIVEBACK:
7920 		return vs.hotspotColours.back.AsInteger();
7921 
7922 	case SCI_SETHOTSPOTACTIVEUNDERLINE:
7923 		vs.hotspotUnderline = wParam != 0;
7924 		InvalidateStyleRedraw();
7925 		break;
7926 
7927 	case SCI_GETHOTSPOTACTIVEUNDERLINE:
7928 		return vs.hotspotUnderline ? 1 : 0;
7929 
7930 	case SCI_SETHOTSPOTSINGLELINE:
7931 		vs.hotspotSingleLine = wParam != 0;
7932 		InvalidateStyleRedraw();
7933 		break;
7934 
7935 	case SCI_GETHOTSPOTSINGLELINE:
7936 		return vs.hotspotSingleLine ? 1 : 0;
7937 
7938 	case SCI_SETPASTECONVERTENDINGS:
7939 		convertPastes = wParam != 0;
7940 		break;
7941 
7942 	case SCI_GETPASTECONVERTENDINGS:
7943 		return convertPastes ? 1 : 0;
7944 
7945 	case SCI_GETCHARACTERPOINTER:
7946 		return reinterpret_cast<sptr_t>(pdoc->BufferPointer());
7947 
7948 	case SCI_GETRANGEPOINTER:
7949 		return reinterpret_cast<sptr_t>(pdoc->RangePointer(
7950 			static_cast<Sci::Position>(wParam), lParam));
7951 
7952 	case SCI_GETGAPPOSITION:
7953 		return pdoc->GapPosition();
7954 
7955 	case SCI_SETEXTRAASCENT:
7956 		vs.extraAscent = static_cast<int>(wParam);
7957 		InvalidateStyleRedraw();
7958 		break;
7959 
7960 	case SCI_GETEXTRAASCENT:
7961 		return vs.extraAscent;
7962 
7963 	case SCI_SETEXTRADESCENT:
7964 		vs.extraDescent = static_cast<int>(wParam);
7965 		InvalidateStyleRedraw();
7966 		break;
7967 
7968 	case SCI_GETEXTRADESCENT:
7969 		return vs.extraDescent;
7970 
7971 	case SCI_MARGINSETSTYLEOFFSET:
7972 		vs.marginStyleOffset = static_cast<int>(wParam);
7973 		InvalidateStyleRedraw();
7974 		break;
7975 
7976 	case SCI_MARGINGETSTYLEOFFSET:
7977 		return vs.marginStyleOffset;
7978 
7979 	case SCI_SETMARGINOPTIONS:
7980 		marginOptions = static_cast<int>(wParam);
7981 		break;
7982 
7983 	case SCI_GETMARGINOPTIONS:
7984 		return marginOptions;
7985 
7986 	case SCI_MARGINSETTEXT:
7987 		pdoc->MarginSetText(static_cast<Sci::Line>(wParam), CharPtrFromSPtr(lParam));
7988 		break;
7989 
7990 	case SCI_MARGINGETTEXT: {
7991 			const StyledText st = pdoc->MarginStyledText(static_cast<Sci::Line>(wParam));
7992 			return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
7993 		}
7994 
7995 	case SCI_MARGINSETSTYLE:
7996 		pdoc->MarginSetStyle(static_cast<Sci::Line>(wParam), static_cast<int>(lParam));
7997 		break;
7998 
7999 	case SCI_MARGINGETSTYLE: {
8000 			const StyledText st = pdoc->MarginStyledText(static_cast<Sci::Line>(wParam));
8001 			return st.style;
8002 		}
8003 
8004 	case SCI_MARGINSETSTYLES:
8005 		pdoc->MarginSetStyles(static_cast<Sci::Line>(wParam), ConstUCharPtrFromSPtr(lParam));
8006 		break;
8007 
8008 	case SCI_MARGINGETSTYLES: {
8009 			const StyledText st = pdoc->MarginStyledText(static_cast<Sci::Line>(wParam));
8010 			return BytesResult(lParam, st.styles, st.length);
8011 		}
8012 
8013 	case SCI_MARGINTEXTCLEARALL:
8014 		pdoc->MarginClearAll();
8015 		break;
8016 
8017 	case SCI_ANNOTATIONSETTEXT:
8018 		pdoc->AnnotationSetText(static_cast<Sci::Line>(wParam), CharPtrFromSPtr(lParam));
8019 		break;
8020 
8021 	case SCI_ANNOTATIONGETTEXT: {
8022 			const StyledText st = pdoc->AnnotationStyledText(static_cast<Sci::Line>(wParam));
8023 			return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
8024 		}
8025 
8026 	case SCI_ANNOTATIONGETSTYLE: {
8027 			const StyledText st = pdoc->AnnotationStyledText(static_cast<Sci::Line>(wParam));
8028 			return st.style;
8029 		}
8030 
8031 	case SCI_ANNOTATIONSETSTYLE:
8032 		pdoc->AnnotationSetStyle(static_cast<Sci::Line>(wParam), static_cast<int>(lParam));
8033 		break;
8034 
8035 	case SCI_ANNOTATIONSETSTYLES:
8036 		pdoc->AnnotationSetStyles(static_cast<Sci::Line>(wParam), ConstUCharPtrFromSPtr(lParam));
8037 		break;
8038 
8039 	case SCI_ANNOTATIONGETSTYLES: {
8040 			const StyledText st = pdoc->AnnotationStyledText(static_cast<Sci::Line>(wParam));
8041 			return BytesResult(lParam, st.styles, st.length);
8042 		}
8043 
8044 	case SCI_ANNOTATIONGETLINES:
8045 		return pdoc->AnnotationLines(static_cast<Sci::Line>(wParam));
8046 
8047 	case SCI_ANNOTATIONCLEARALL:
8048 		pdoc->AnnotationClearAll();
8049 		break;
8050 
8051 	case SCI_ANNOTATIONSETVISIBLE:
8052 		SetAnnotationVisible(static_cast<int>(wParam));
8053 		break;
8054 
8055 	case SCI_ANNOTATIONGETVISIBLE:
8056 		return vs.annotationVisible;
8057 
8058 	case SCI_ANNOTATIONSETSTYLEOFFSET:
8059 		vs.annotationStyleOffset = static_cast<int>(wParam);
8060 		InvalidateStyleRedraw();
8061 		break;
8062 
8063 	case SCI_ANNOTATIONGETSTYLEOFFSET:
8064 		return vs.annotationStyleOffset;
8065 
8066 	case SCI_RELEASEALLEXTENDEDSTYLES:
8067 		vs.ReleaseAllExtendedStyles();
8068 		break;
8069 
8070 	case SCI_ALLOCATEEXTENDEDSTYLES:
8071 		return vs.AllocateExtendedStyles(static_cast<int>(wParam));
8072 
8073 	case SCI_ADDUNDOACTION:
8074 		pdoc->AddUndoAction(static_cast<Sci::Position>(wParam), lParam & UNDO_MAY_COALESCE);
8075 		break;
8076 
8077 	case SCI_SETMOUSESELECTIONRECTANGULARSWITCH:
8078 		mouseSelectionRectangularSwitch = wParam != 0;
8079 		break;
8080 
8081 	case SCI_GETMOUSESELECTIONRECTANGULARSWITCH:
8082 		return mouseSelectionRectangularSwitch;
8083 
8084 	case SCI_SETMULTIPLESELECTION:
8085 		multipleSelection = wParam != 0;
8086 		InvalidateCaret();
8087 		break;
8088 
8089 	case SCI_GETMULTIPLESELECTION:
8090 		return multipleSelection;
8091 
8092 	case SCI_SETADDITIONALSELECTIONTYPING:
8093 		additionalSelectionTyping = wParam != 0;
8094 		InvalidateCaret();
8095 		break;
8096 
8097 	case SCI_GETADDITIONALSELECTIONTYPING:
8098 		return additionalSelectionTyping;
8099 
8100 	case SCI_SETMULTIPASTE:
8101 		multiPasteMode = static_cast<int>(wParam);
8102 		break;
8103 
8104 	case SCI_GETMULTIPASTE:
8105 		return multiPasteMode;
8106 
8107 	case SCI_SETADDITIONALCARETSBLINK:
8108 		view.additionalCaretsBlink = wParam != 0;
8109 		InvalidateCaret();
8110 		break;
8111 
8112 	case SCI_GETADDITIONALCARETSBLINK:
8113 		return view.additionalCaretsBlink;
8114 
8115 	case SCI_SETADDITIONALCARETSVISIBLE:
8116 		view.additionalCaretsVisible = wParam != 0;
8117 		InvalidateCaret();
8118 		break;
8119 
8120 	case SCI_GETADDITIONALCARETSVISIBLE:
8121 		return view.additionalCaretsVisible;
8122 
8123 	case SCI_GETSELECTIONS:
8124 		return sel.Count();
8125 
8126 	case SCI_GETSELECTIONEMPTY:
8127 		return sel.Empty();
8128 
8129 	case SCI_CLEARSELECTIONS:
8130 		sel.Clear();
8131 		ContainerNeedsUpdate(SC_UPDATE_SELECTION);
8132 		Redraw();
8133 		break;
8134 
8135 	case SCI_SETSELECTION:
8136 		sel.SetSelection(SelectionRange(static_cast<Sci::Position>(wParam), lParam));
8137 		Redraw();
8138 		break;
8139 
8140 	case SCI_ADDSELECTION:
8141 		sel.AddSelection(SelectionRange(static_cast<Sci::Position>(wParam), lParam));
8142 		ContainerNeedsUpdate(SC_UPDATE_SELECTION);
8143 		Redraw();
8144 		break;
8145 
8146 	case SCI_DROPSELECTIONN:
8147 		sel.DropSelection(static_cast<size_t>(wParam));
8148 		ContainerNeedsUpdate(SC_UPDATE_SELECTION);
8149 		Redraw();
8150 		break;
8151 
8152 	case SCI_SETMAINSELECTION:
8153 		sel.SetMain(static_cast<size_t>(wParam));
8154 		ContainerNeedsUpdate(SC_UPDATE_SELECTION);
8155 		Redraw();
8156 		break;
8157 
8158 	case SCI_GETMAINSELECTION:
8159 		return sel.Main();
8160 
8161 	case SCI_SETSELECTIONNCARET:
8162 	case SCI_SETSELECTIONNANCHOR:
8163 	case SCI_SETSELECTIONNCARETVIRTUALSPACE:
8164 	case SCI_SETSELECTIONNANCHORVIRTUALSPACE:
8165 	case SCI_SETSELECTIONNSTART:
8166 	case SCI_SETSELECTIONNEND:
8167 		SetSelectionNMessage(iMessage, wParam, lParam);
8168 		break;
8169 
8170 	case SCI_GETSELECTIONNCARET:
8171 		return sel.Range(wParam).caret.Position();
8172 
8173 	case SCI_GETSELECTIONNANCHOR:
8174 		return sel.Range(wParam).anchor.Position();
8175 
8176 	case SCI_GETSELECTIONNCARETVIRTUALSPACE:
8177 		return sel.Range(wParam).caret.VirtualSpace();
8178 
8179 	case SCI_GETSELECTIONNANCHORVIRTUALSPACE:
8180 		return sel.Range(wParam).anchor.VirtualSpace();
8181 
8182 	case SCI_GETSELECTIONNSTART:
8183 		return sel.Range(wParam).Start().Position();
8184 
8185 	case SCI_GETSELECTIONNSTARTVIRTUALSPACE:
8186 		return sel.Range(wParam).Start().VirtualSpace();
8187 
8188 	case SCI_GETSELECTIONNEND:
8189 		return sel.Range(wParam).End().Position();
8190 
8191 	case SCI_GETSELECTIONNENDVIRTUALSPACE:
8192 		return sel.Range(wParam).End().VirtualSpace();
8193 
8194 	case SCI_SETRECTANGULARSELECTIONCARET:
8195 		if (!sel.IsRectangular())
8196 			sel.Clear();
8197 		sel.selType = Selection::selRectangle;
8198 		sel.Rectangular().caret.SetPosition(static_cast<Sci::Position>(wParam));
8199 		SetRectangularRange();
8200 		Redraw();
8201 		break;
8202 
8203 	case SCI_GETRECTANGULARSELECTIONCARET:
8204 		return sel.Rectangular().caret.Position();
8205 
8206 	case SCI_SETRECTANGULARSELECTIONANCHOR:
8207 		if (!sel.IsRectangular())
8208 			sel.Clear();
8209 		sel.selType = Selection::selRectangle;
8210 		sel.Rectangular().anchor.SetPosition(static_cast<Sci::Position>(wParam));
8211 		SetRectangularRange();
8212 		Redraw();
8213 		break;
8214 
8215 	case SCI_GETRECTANGULARSELECTIONANCHOR:
8216 		return sel.Rectangular().anchor.Position();
8217 
8218 	case SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE:
8219 		if (!sel.IsRectangular())
8220 			sel.Clear();
8221 		sel.selType = Selection::selRectangle;
8222 		sel.Rectangular().caret.SetVirtualSpace(static_cast<Sci::Position>(wParam));
8223 		SetRectangularRange();
8224 		Redraw();
8225 		break;
8226 
8227 	case SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE:
8228 		return sel.Rectangular().caret.VirtualSpace();
8229 
8230 	case SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
8231 		if (!sel.IsRectangular())
8232 			sel.Clear();
8233 		sel.selType = Selection::selRectangle;
8234 		sel.Rectangular().anchor.SetVirtualSpace(static_cast<Sci::Position>(wParam));
8235 		SetRectangularRange();
8236 		Redraw();
8237 		break;
8238 
8239 	case SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
8240 		return sel.Rectangular().anchor.VirtualSpace();
8241 
8242 	case SCI_SETVIRTUALSPACEOPTIONS:
8243 		virtualSpaceOptions = static_cast<int>(wParam);
8244 		break;
8245 
8246 	case SCI_GETVIRTUALSPACEOPTIONS:
8247 		return virtualSpaceOptions;
8248 
8249 	case SCI_SETADDITIONALSELFORE:
8250 		vs.selAdditionalForeground = ColourDesired(static_cast<int>(wParam));
8251 		InvalidateStyleRedraw();
8252 		break;
8253 
8254 	case SCI_SETADDITIONALSELBACK:
8255 		vs.selAdditionalBackground = ColourDesired(static_cast<int>(wParam));
8256 		InvalidateStyleRedraw();
8257 		break;
8258 
8259 	case SCI_SETADDITIONALSELALPHA:
8260 		vs.selAdditionalAlpha = static_cast<int>(wParam);
8261 		InvalidateStyleRedraw();
8262 		break;
8263 
8264 	case SCI_GETADDITIONALSELALPHA:
8265 		return vs.selAdditionalAlpha;
8266 
8267 	case SCI_SETADDITIONALCARETFORE:
8268 		vs.additionalCaretColour = ColourDesired(static_cast<int>(wParam));
8269 		InvalidateStyleRedraw();
8270 		break;
8271 
8272 	case SCI_GETADDITIONALCARETFORE:
8273 		return vs.additionalCaretColour.AsInteger();
8274 
8275 	case SCI_ROTATESELECTION:
8276 		sel.RotateMain();
8277 		InvalidateWholeSelection();
8278 		break;
8279 
8280 	case SCI_SWAPMAINANCHORCARET:
8281 		InvalidateSelection(sel.RangeMain());
8282 		sel.RangeMain().Swap();
8283 		break;
8284 
8285 	case SCI_MULTIPLESELECTADDNEXT:
8286 		MultipleSelectAdd(addOne);
8287 		break;
8288 
8289 	case SCI_MULTIPLESELECTADDEACH:
8290 		MultipleSelectAdd(addEach);
8291 		break;
8292 
8293 	case SCI_CHANGELEXERSTATE:
8294 		pdoc->ChangeLexerState(static_cast<Sci::Position>(wParam), lParam);
8295 		break;
8296 
8297 	case SCI_SETIDENTIFIER:
8298 		SetCtrlID(static_cast<int>(wParam));
8299 		break;
8300 
8301 	case SCI_GETIDENTIFIER:
8302 		return GetCtrlID();
8303 
8304 	case SCI_SETTECHNOLOGY:
8305 		// No action by default
8306 		break;
8307 
8308 	case SCI_GETTECHNOLOGY:
8309 		return technology;
8310 
8311 	case SCI_COUNTCHARACTERS:
8312 		return pdoc->CountCharacters(static_cast<Sci::Position>(wParam), lParam);
8313 
8314 	case SCI_COUNTCODEUNITS:
8315 		return pdoc->CountUTF16(static_cast<Sci::Position>(wParam), lParam);
8316 
8317 	default:
8318 		return DefWndProc(iMessage, wParam, lParam);
8319 	}
8320 	//Platform::DebugPrintf("end wnd proc\n");
8321 	return 0;
8322 }
8323