From 7b807732758336c7d761a989152157ad44efbfc9 Mon Sep 17 00:00:00 2001 From: Martijn Laan <1092369+martijnlaan@users.noreply.github.com> Date: Thu, 13 Jun 2024 21:49:05 +0200 Subject: [PATCH] Improve handling of overlapping indicators and selections if there's multiple selections. Wouldn't be necessary if I knew how to tell Scintilla to prioritize selection over indicators. --- Components/ScintEdit.pas | 38 ++++++++++++++++++-- Projects/Src/CompForm.pas | 75 +++++++++++++++++++++------------------ 2 files changed, 76 insertions(+), 37 deletions(-) diff --git a/Components/ScintEdit.pas b/Components/ScintEdit.pas index e591dc3a2..368c93341 100644 --- a/Components/ScintEdit.pas +++ b/Components/ScintEdit.pas @@ -52,10 +52,14 @@ TScintEditChangeInfo = record TScintRange = record StartPos, EndPos: Integer; constructor Create(const AStartPos, AEndPos: Integer); + function Empty: Boolean; function Overlaps(const ARange: TScintRange): Boolean; function Within(const ARange: TScintRange): Boolean; end; - TScintRangeList = TList; + TScintRangeList = class(TList) + function Overlaps(const ARange: TScintRange; + var AOverlappingRange: TScintRange): Boolean; + end; TScintRawCharSet = set of AnsiChar; TScintRawString = type RawByteString; TScintRectangle = record @@ -240,6 +244,7 @@ TScintEdit = class(TWinControl) function GetPositionOfMatchingBrace(const Pos: Integer): Integer; function GetRawTextLength: Integer; function GetRawTextRange(const StartPos, EndPos: Integer): TScintRawString; + procedure GetSelections(const RangeList: TScintRangeList); function GetStyleAtPosition(const Pos: Integer): TScintStyleNumber; function GetTextRange(const StartPos, EndPos: Integer): String; function GetVisibleLineFromDocLine(const DocLine: Integer): Integer; @@ -1034,6 +1039,16 @@ function TScintEdit.GetSelection: TScintRange; Result.EndPos := Call(SCI_GETSELECTIONEND, 0, 0); end; +procedure TScintEdit.GetSelections(const RangeList: TScintRangeList); +begin + RangeList.Clear; + for var I := 0 to SelectionCount-1 do begin + var StartPos := Call(SCI_GETSELECTIONNSTART, I, 0); + var EndPos := Call(SCI_GETSELECTIONNEND, I, 0); + RangeList.Add(TScintRange.Create(StartPos, EndPos)); + end; +end; + function TScintEdit.GetSelectionAnchorPosition(Selection: Integer): Integer; begin Result := Call(SCI_GETSELECTIONNANCHOR, Selection, 0); @@ -2429,7 +2444,12 @@ constructor TScintRange.Create(const AStartPos, AEndPos: Integer); function TScintRange.Overlaps(const ARange: TScintRange): Boolean; begin - Result := (StartPos <= ARange.EndPos) and (EndPos >= ARange.StartPos); + Result := not ARange.Empty and (StartPos <= ARange.EndPos) and (EndPos >= ARange.StartPos); +end; + +function TScintRange.Empty: Boolean; +begin + Result := StartPos = EndPos; end; function TScintRange.Within(const ARange: TScintRange): Boolean; @@ -2437,4 +2457,18 @@ function TScintRange.Within(const ARange: TScintRange): Boolean; Result := (StartPos >= ARange.StartPos) and (EndPos <= ARange.EndPos); end; +{ TScintRangeList } + +function TScintRangeList.Overlaps(const ARange: TScintRange; + var AOverlappingRange: TScintRange): Boolean; +begin + for var Item in Self do begin + if Item.Overlaps(ARange) then begin + AOverlappingRange := Item; + Exit(True); + end; + end; + Result := False; +end; + end. diff --git a/Projects/Src/CompForm.pas b/Projects/Src/CompForm.pas index b4635fe54..3bda350a5 100644 --- a/Projects/Src/CompForm.pas +++ b/Projects/Src/CompForm.pas @@ -3281,72 +3281,77 @@ procedure TCompileForm.UpdateOccurrenceIndicators(const AMemo: TCompScintEdit); procedure FindTextAndAddRanges(const AMemo: TCompScintEdit; const TextToFind: TScintRawString; const Options: TScintFindOptions; - const SelNotEmpty: Boolean; const Selection: TScintRange; - const ARangeList: TScintRangeList); + const SelectionRanges, IndicatorRanges: TScintRangeList); begin if ScintRawStringIsBlank(TextToFind) then Exit; var StartPos := 0; var EndPos := AMemo.RawTextLength; - var Range: TScintRange; + var FoundRange: TScintRange; while (StartPos < EndPos) and - AMemo.FindRawText(StartPos, EndPos, TextToFind, Options, Range) do begin - StartPos := Range.EndPos; + AMemo.FindRawText(StartPos, EndPos, TextToFind, Options, FoundRange) do begin + StartPos := FoundRange.EndPos; { Don't add indicators on lines which have a line marker } - var Line := AMemo.GetLineFromPosition(Range.StartPos); + var Line := AMemo.GetLineFromPosition(FoundRange.StartPos); var Markers := AMemo.GetMarkers(Line); if Markers * [mmLineError, mmLineBreakpointBad, mmLineStep] <> [] then Continue; - { Add indicator while making sure it does not overlap the regular selection - styling (only looks at main selection and not any additional selections - atm - so if you ctrl+double click a word and then do the same on an - occurrence somewhere else the additional selection becomes hidden by the - indicator except for the very top and bottom (due to use of - INDIC_STRAIGHTBOX instead of INDIC_FULLBOX) } - if SelNotEmpty and Range.Overlaps(Selection) then begin - if Range.StartPos < Selection.StartPos then - ARangeList.Add(TScintRange.Create(Range.StartPos, Selection.StartPos)); - if Range.EndPos > Selection.EndPos then - ARangeList.Add(TScintRange.Create(Selection.EndPos, Range.EndPos)); + { Add indicator while making sure it does not overlap any regular selection + styling for either the main selection or any additional selection. Does + not account for an indicator overlapping more than 1 selection. } + var OverlappingSelection: TScintRange; + if SelectionRanges.Overlaps(FoundRange, OverlappingSelection) then begin + if FoundRange.StartPos < OverlappingSelection.StartPos then + IndicatorRanges.Add(TScintRange.Create(FoundRange.StartPos, OverlappingSelection.StartPos)); + if FoundRange.EndPos > OverlappingSelection.EndPos then + IndicatorRanges.Add(TScintRange.Create(OverlappingSelection.EndPos, FoundRange.EndPos)); end else - ARangeList.Add(TScintRange.Create(Range.StartPos, Range.EndPos)); + IndicatorRanges.Add(FoundRange); end; end; begin { Add occurrence indicators for the word at cursor if there's any and the - selection is within this word. On top of those add occurrence indicators for - the selected text if there's any. Don't do anything of the selection is not - single line. All of these things are just like VSCode. } + main selection is within this word. On top of those add occurrence indicators + for the main selected text if there's any. Don't do anything if the main + selection is not single line. All of these things are just like VSCode. } - var Selection: TScintRange; - var SelNotEmpty := AMemo.SelNotEmpty(Selection); - var SelSingleLine := AMemo.GetLineFromPosition(Selection.StartPos) = - AMemo.GetLineFromPosition(Selection.EndPos); + var MainSelection: TScintRange; + var MainSelNotEmpty := AMemo.SelNotEmpty(MainSelection); + var MainSelSingleLine := AMemo.GetLineFromPosition(MainSelection.StartPos) = + AMemo.GetLineFromPosition(MainSelection.EndPos); - var RangeList := TScintRangeList.Create; + var IndicatorRanges: TScintRangeList := nil; + var SelectionRanges: TScintRangeList := nil; try - if FOptions.HighlightWordAtCursorOccurrences and (AMemo.CaretVirtualSpace = 0) and SelSingleLine then begin + IndicatorRanges := TScintRangeList.Create; + SelectionRanges := TScintRangeList.Create; + + if FOptions.HighlightWordAtCursorOccurrences and (AMemo.CaretVirtualSpace = 0) and MainSelSingleLine then begin var Word := AMemo.WordAtCursorRange; - if (Word.StartPos <> Word.EndPos) and Selection.Within(Word) then begin + if (Word.StartPos <> Word.EndPos) and MainSelection.Within(Word) then begin var TextToIndicate := AMemo.GetRawTextRange(Word.StartPos, Word.EndPos); - FindTextAndAddRanges(AMemo, TextToIndicate, GetWordOccurrenceFindOptions, SelNotEmpty, Selection, RangeList); + AMemo.GetSelections(SelectionRanges); { Gets any additional selections as well } + FindTextAndAddRanges(AMemo, TextToIndicate, GetWordOccurrenceFindOptions, SelectionRanges, IndicatorRanges); end; end; - AMemo.UpdateIndicators(RangeList, inWordAtCursorOccurrence); + AMemo.UpdateIndicators(IndicatorRanges, inWordAtCursorOccurrence); - RangeList.Clear; - if FOptions.HighlightSelTextOccurrences and SelNotEmpty and SelSingleLine then begin + IndicatorRanges.Clear; + if FOptions.HighlightSelTextOccurrences and MainSelNotEmpty and MainSelSingleLine then begin var TextToIndicate := AMemo.RawSelText; - FindTextAndAddRanges(AMemo, TextToIndicate, GetSelTextOccurrenceFindOptions, SelNotEmpty, Selection, RangeList); + if SelectionRanges.Count = 0 then { If 0 then we didn't already call GetSelections above} + AMemo.GetSelections(SelectionRanges); + FindTextAndAddRanges(AMemo, TextToIndicate, GetSelTextOccurrenceFindOptions,SelectionRanges, IndicatorRanges); end; - AMemo.UpdateIndicators(RangeList, inSelTextOccurrence); + AMemo.UpdateIndicators(IndicatorRanges, inSelTextOccurrence); finally - RangeList.Free; + SelectionRanges.Free; + IndicatorRanges.Free; end; end;