Skip to content

Commit

Permalink
Improve handling of overlapping indicators and selections if there's …
Browse files Browse the repository at this point in the history
…multiple selections. Wouldn't be necessary if I knew how to tell Scintilla to prioritize selection over indicators.
  • Loading branch information
martijnlaan committed Jun 13, 2024
1 parent b45c41f commit 7b80773
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 37 deletions.
38 changes: 36 additions & 2 deletions Components/ScintEdit.pas
Original file line number Diff line number Diff line change
Expand Up @@ -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<TScintRange>;
TScintRangeList = class(TList<TScintRange>)
function Overlaps(const ARange: TScintRange;
var AOverlappingRange: TScintRange): Boolean;
end;
TScintRawCharSet = set of AnsiChar;
TScintRawString = type RawByteString;
TScintRectangle = record
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -2429,12 +2444,31 @@ 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;
begin
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.
75 changes: 40 additions & 35 deletions Projects/Src/CompForm.pas
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down

0 comments on commit 7b80773

Please sign in to comment.