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