| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740 |
- unit IDE.IDEScintEdit;
- {
- Inno Setup
- Copyright (C) 1997-2024 Jordan Russell
- Portions by Martijn Laan
- For conditions of distribution and use, see LICENSE.TXT.
- Compiler IDE's TScintEdit
- }
- interface
- uses
- Windows, Graphics, Classes, Menus, Generics.Collections,
- ScintInt, ScintEdit, ModernColors;
- const
- { Memo margin numbers }
- mmLineNumbers = 0;
- mmIcons = 1;
- mmChangeHistory = 2;
- mmFolding = 3;
- { Memo marker icon and line marker numbers }
- mmiHasEntry = 0; { grey dot }
- mmiEntryProcessed = 1; { green dot }
- mmiBreakpoint = 2; { stop sign }
- mmiBreakpointGood = 3; { stop sign + check }
- mmiBreakpointBad = 4; { stop sign + X }
- mmiStep = 5; { blue arrow }
- mmiBreakpointStep = 6; { blue arrow on top of a stop sign + check }
- mmiMask = $7F;
- mlmError = 10; { maroon line highlight }
- mlmBreakpointBad = 11; { ugly olive line highlight }
- mlmStep = 12; { blue line highlight }
- { Memo indicator numbers - Note: inSquiggly and inPendingSquiggly are 0 and 1
- in ScintStylerInnoSetup and must be first and second here. Also note: even
- though inSquiggly and inPendingSquiggly are exclusive we still need 2 indicators
- (instead of 1 indicator with 2 values) because inPendingSquiggly is always
- hidden and in inSquiggly is not. }
- minSquiggly = INDICATOR_CONTAINER;
- minPendingSquiggly = INDICATOR_CONTAINER+1;
- minWordAtCursorOccurrence = INDICATOR_CONTAINER+2;
- minSelTextOccurrence = INDICATOR_CONTAINER+3;
- minMax = minSelTextOccurrence;
- { Just some invalid value used to indicate an unknown/uninitialized compiler FileIndex value }
- UnknownCompilerFileIndex = -2;
- type
- TLineState = (lnUnknown, lnHasEntry, lnEntryProcessed); { Not related to TScintLineState }
- PLineStateArray = ^TLineStateArray;
- TLineStateArray = array[0..0] of TLineState;
- TSaveEncoding = (seAuto, seUTF8WithBOM, seUTF8WithoutBOM);
- TIDEScintIndicatorNumber = 0..minMax;
- { Keymaps - Note: Scintilla's default keymap is the same or at least nearly
- the same as Visual Studio's }
- TIDEScintKeyMappingType = (kmtDefault, kmtVSCode);
- { Commands which require more than 1 parameterless SCI_XXXXX and need help
- from the container }
- TIDEScintComplexCommand = (ccNone, ccSelectNextOccurrence,
- ccSelectAllOccurrences, ccSelectAllFindMatches, ccSimplifySelection,
- ccUnfoldLine, ccFoldLine, ccToggleLinesComment, ccAddCursorUp,
- ccAddCursorDown, ccBraceMatch, ccAddCursorsToLineEnds);
- TIDEScintEdit = class(TScintEdit)
- private
- type
- TIDEScintComplexCommands = TDictionary<TShortCut, TIDEScintComplexCommand>;
- TIDEScintComplexCommandsReversed = TDictionary<TIDEScintComplexCommand, TShortCut>;
- var
- FKeyMappingType: TIDEScintKeyMappingType;
- FSmartHome: Boolean;
- FComplexCommands: TIDEScintComplexCommands;
- FComplexCommandsReversed: TIDEScintComplexCommandsReversed;
- FUseFolding: Boolean;
- FTheme: TTheme;
- FOpeningFile: Boolean;
- FUsed: Boolean; { The IDE only shows 1 memo at a time so can't use .Visible to check if a memo is used }
- FIndicatorCount: array[TIDEScintIndicatorNumber] of Integer;
- FIndicatorHash: array[TIDEScintIndicatorNumber] of String;
- procedure AddComplexCommand(const ShortCut: TShortCut;
- Command: TIDEScintComplexCommand; const AlternativeShortCut: Boolean = False);
- procedure SetUseFolding(const Value: Boolean);
- procedure SetKeyMappingType(const Value: TIDEScintKeyMappingType);
- procedure SetSmartHome(const Value: Boolean);
- procedure UpdateComplexCommands;
- procedure UpdateSmartHome;
- protected
- procedure CreateWnd; override;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- property Theme: TTheme read FTheme write FTheme;
- property OpeningFile: Boolean read FOpeningFile write FOpeningFile;
- property Used: Boolean read FUsed write FUsed;
- function GetComplexCommand(const ShortCut: TShortCut): TIDEScintComplexCommand;
- function GetComplexCommandShortCut(const Command: TIDEScintComplexCommand): TShortCut;
- function GetRectExtendShiftState(const Desired: Boolean): TShiftState;
- procedure UpdateIndicators(const Ranges: TScintRangeList;
- const IndicatorNumber: TIDEScintIndicatorNumber);
- procedure UpdateWidthsAndSizes(const IconMarkersWidth,
- BaseChangeHistoryWidth, BaseFolderMarkersWidth, LeftBlankMarginWidth,
- RightBlankMarginWidth, SquigglyWidth, CaretWidth, WhiteSpaceSize: Integer);
- procedure UpdateThemeColorsAndStyleAttributes;
- published
- property KeyMappingType: TIDEScintKeyMappingType read FKeyMappingType write SetKeyMappingType default kmtDefault;
- property SmartHome: Boolean read FSmartHome write SetSmartHome default True;
- property UseFolding: Boolean read FUseFolding write SetUseFolding default True;
- end;
- TIDEScintFileEdit = class(TIDEScintEdit)
- private
- FBreakPoints: TList<Integer>;
- FCompilerFileIndex: Integer;
- FFilename: String;
- FFileLastWriteTime: TFileTime;
- FSaveEncoding: TSaveEncoding;
- public
- ErrorLine, ErrorCaretPosition: Integer;
- StepLine: Integer;
- LineState: PLineStateArray;
- LineStateCapacity, LineStateCount: Integer;
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- property BreakPoints: TList<Integer> read FBreakPoints;
- property Filename: String read FFileName write FFilename;
- property CompilerFileIndex: Integer read FCompilerFileIndex write FCompilerFileIndex;
- property FileLastWriteTime: TFileTime read FFileLastWriteTime write FFileLastWriteTime;
- property SaveEncoding: TSaveEncoding read FSaveEncoding write FSaveEncoding;
- end;
- TIDEScintEditNavItem = record
- Memo: TIDEScintEdit;
- Line, Column, VirtualSpace: Integer;
- constructor Create(const AMemo: TIDEScintEdit);
- function EqualMemoAndLine(const ANavItem: TIDEScintEditNavItem): Boolean;
- procedure Invalidate;
- function Valid: Boolean;
- end;
- { Not using TStack since it lacks a way the keep a maximum amount of items by discarding the oldest }
- TIDEScintEditNavStack = class(TList<TIDEScintEditNavItem>)
- public
- function LinesDeleted(const AMemo: TIDEScintEdit; const FirstLine, LineCount: Integer): Boolean;
- procedure LinesInserted(const AMemo: TIDEScintEdit; const FirstLine, LineCount: Integer);
- procedure Optimize;
- function RemoveMemo(const AMemo: TIDEScintEdit): Boolean;
- function RemoveMemoBadLines(const AMemo: TIDEScintEdit): Boolean;
- end;
- TIDEScintEditNavStacks = class
- private
- FBackNavStack: TIDEScintEditNavStack;
- FForwardNavStack: TIDEScintEditNavStack;
- public
- constructor Create;
- destructor Destroy; override;
- function AddNewBackForJump(const OldNavItem, NewNavItem: TIDEScintEditNavItem): Boolean;
- procedure Clear;
- procedure Limit;
- function LinesDeleted(const AMemo: TIDEScintEdit; const FirstLine, LineCount: Integer): Boolean;
- procedure LinesInserted(const AMemo: TIDEScintEdit; const FirstLine, LineCount: Integer);
- function RemoveMemo(const AMemo: TIDEScintEdit): Boolean;
- function RemoveMemoBadLines(const AMemo: TIDEScintEdit): Boolean;
- property Back: TIDEScintEditNavStack read FBackNavStack;
- property Forward: TIDEScintEditNavStack read FForwardNavStack;
- end;
- implementation
- uses
- SysUtils, SHA256, ScintInt.InnoSetup;
-
- { TIDEScintEdit }
- constructor TIDEScintEdit.Create(AOwner: TComponent);
- begin
- inherited;
- FComplexCommands := TIDEScintComplexCommands.Create;
- FComplexCommandsReversed := TIDEScintComplexCommandsReversed.Create;
- FKeyMappingType := kmtDefault;
- FSmartHome := True;
- UpdateComplexCommands;
- FUseFolding := True;
- end;
- destructor TIDEScintEdit.Destroy;
- begin
- FComplexCommandsReversed.Free;
- FComplexCommands.Free;
-
- inherited;
- end;
- procedure TIDEScintEdit.CreateWnd;
- begin
- inherited;
- { Some notes about Scintilla versions:
- -What about using Calltips and SCN_DWELLSTART to show variable evalutions?
- -5.2.3: "Applications should move to SCI_GETTEXTRANGEFULL, SCI_FINDTEXTFULL,
- and SCI_FORMATRANGEFULL from their predecessors as they will be
- deprecated." So our use of SCI_GETTEXTRANGE and SCI_FORMATRANGE needs
- to be updated but that also means we should do many more changes to
- replace all the Integer positions with a 'TScintPosition = type
- NativeInt'. Does not actually change anything until there's a
- 64-bit build...
- Later SCI_GETSTYLEDTEXTFULL was also added but we don't use it at
- the time of writing. }
- Call(SCI_AUTOCSETAUTOHIDE, 0, 0);
- Call(SCI_AUTOCSETCANCELATSTART, 0, 0);
- Call(SCI_AUTOCSETDROPRESTOFWORD, 1, 0);
- Call(SCI_AUTOCSETIGNORECASE, 1, 0);
- Call(SCI_AUTOCSETOPTIONS, SC_AUTOCOMPLETE_FIXED_SIZE, 0); { Removes the ugly WS_THICKFRAME header at the cost of resizability }
- Call(SCI_AUTOCSETMAXHEIGHT, 12, 0);
- Call(SCI_AUTOCSETMINWIDTH, 50, 0);
- Call(SCI_AUTOCSETMAXWIDTH, 50, 0);
- { Same color as AutoComplete's border color, works well for both dark and light themes }
- var BorderColor := ColorToRGB(clWindowFrame);
- Call(SCI_CALLTIPSETFOREBORDER, BorderColor, BorderColor);
- Call(SCI_SETMULTIPLESELECTION, 1, 0);
- Call(SCI_SETADDITIONALSELECTIONTYPING, 1, 0);
- Call(SCI_SETMULTIPASTE, SC_MULTIPASTE_EACH, 0);
- Call(SCI_SETCOPYSEPARATOR, 0, LineEndingString);
- Call(SCI_SETUNDOSELECTIONHISTORY, SC_UNDO_SELECTION_HISTORY_ENABLED, 0);
- AssignCmdKey('Z', [ssShift, ssCtrl], SCI_REDO);
-
- Call(SCI_SETSCROLLWIDTH, 1024 * Call(SCI_TEXTWIDTH, 0, 'X'), 0);
- Call(SCI_INDICSETSTYLE, minSquiggly, INDIC_SQUIGGLE); { Overwritten by TCompForm.SyncEditorOptions }
- Call(SCI_INDICSETFORE, minSquiggly, clRed); { May be overwritten by UpdateThemeColorsAndStyleAttributes }
- Call(SCI_INDICSETSTYLE, minPendingSquiggly, INDIC_HIDDEN);
- Call(SCI_INDICSETSTYLE, minWordAtCursorOccurrence, INDIC_STRAIGHTBOX);
- Call(SCI_INDICSETFORE, minWordAtCursorOccurrence, clSilver); { May be overwritten by UpdateThemeColorsAndStyleAttributes }
- Call(SCI_INDICSETALPHA, minWordAtCursorOccurrence, SC_ALPHA_OPAQUE);
- Call(SCI_INDICSETOUTLINEALPHA, minWordAtCursorOccurrence, SC_ALPHA_OPAQUE);
- Call(SCI_INDICSETUNDER, minWordAtCursorOccurrence, 1);
- Call(SCI_INDICSETSTYLE, minSelTextOccurrence, INDIC_STRAIGHTBOX);
- Call(SCI_INDICSETFORE, minSelTextOccurrence, clSilver); { May be overwritten by UpdateThemeColorsAndStyleAttributes }
- Call(SCI_INDICSETALPHA, minSelTextOccurrence, SC_ALPHA_OPAQUE);
- Call(SCI_INDICSETOUTLINEALPHA, minSelTextOccurrence, SC_ALPHA_OPAQUE);
- Call(SCI_INDICSETUNDER, minSelTextOccurrence, 1);
- { Set up the gutter column with line numbers - avoid Scintilla's 'reverse arrow'
- cursor which is not a standard Windows cursor so is just confusing, especially
- because the line numbers are clickable to select lines. Note: width of the
- column is set up for us by TScintEdit.UpdateLineNumbersWidth. }
- Call(SCI_SETMARGINCURSORN, mmLineNumbers, SC_CURSORARROW);
- { Set up the gutter column with breakpoint etc symbols }
- Call(SCI_SETMARGINTYPEN, mmIcons, SC_MARGIN_SYMBOL);
- Call(SCI_SETMARGINMASKN, mmIcons, mmiMask);
- Call(SCI_SETMARGINSENSITIVEN, mmIcons, 1); { Makes it send SCN_MARGIN(RIGHT)CLICK instead of selecting lines }
- Call(SCI_SETMARGINCURSORN, mmIcons, SC_CURSORARROW);
- { Set up the gutter column with change history. Note: width of the column is
- set up by UpdateMarginsAndSquigglyAndCaretWidths. Also see
- https://scintilla.org/ChangeHistory.html }
- Call(SCI_SETMARGINTYPEN, mmChangeHistory, SC_MARGIN_SYMBOL);
- Call(SCI_SETMARGINMASKN, mmChangeHistory, SC_MASK_HISTORY);
- Call(SCI_SETMARGINCURSORN, mmChangeHistory, SC_CURSORARROW);
- { Set up the gutter column with folding markers. Note: width of the column is
- set up by UpdateMarginsAndSquigglyAndCaretWidths. }
- Call(SCI_SETMARGINTYPEN, mmFolding, SC_MARGIN_SYMBOL);
- Call(SCI_SETMARGINMASKN, mmFolding, LPARAM(SC_MASK_FOLDERS));
- Call(SCI_SETMARGINCURSORN, mmFolding, SC_CURSORARROW);
- Call(SCI_SETMARGINSENSITIVEN, mmFolding, 1);
- Call(SCI_SETAUTOMATICFOLD, SC_AUTOMATICFOLD_SHOW or SC_AUTOMATICFOLD_CLICK or SC_AUTOMATICFOLD_CHANGE, 0);
- Call(SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_ARROWDOWN);
- Call(SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_ARROW);
- Call(SCI_MARKERDEFINE, SC_MARKNUM_FOLDERSUB, SC_MARK_EMPTY);
- Call(SCI_MARKERDEFINE, SC_MARKNUM_FOLDERTAIL, SC_MARK_EMPTY);
- Call(SCI_MARKERDEFINE, SC_MARKNUM_FOLDEREND, SC_MARK_EMPTY);
- Call(SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPENMID, SC_MARK_EMPTY);
- Call(SCI_MARKERDEFINE, SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_EMPTY);
- FoldFlags := [{sffLevelNumbers, }sffLineAfterContracted]; { sffLevelNumbers can be used to debug fold levels}
- { Set up the line markers }
- Call(SCI_MARKERDEFINE, mlmError, SC_MARK_BACKFORE);
- Call(SCI_MARKERSETFORE, mlmError, clWhite);
- Call(SCI_MARKERSETBACK, mlmError, clMaroon);
- Call(SCI_MARKERDEFINE, mlmBreakpointBad, SC_MARK_BACKFORE);
- Call(SCI_MARKERSETFORE, mlmBreakpointBad, clLime);
- Call(SCI_MARKERSETBACK, mlmBreakpointBad, clOlive);
- Call(SCI_MARKERDEFINE, mlmStep, SC_MARK_BACKFORE);
- Call(SCI_MARKERSETFORE, mlmStep, clWhite);
- Call(SCI_MARKERSETBACK, mlmStep, clBlue); { May be overwritten by UpdateThemeColorsAndStyleAttributes }
- end;
- procedure TIDEScintEdit.AddComplexCommand(const ShortCut: TShortCut;
- Command: TIDEScintComplexCommand; const AlternativeShortCut: Boolean);
- begin
- if Command = ccNone then
- raise Exception.Create('Command = ccNone');
- FComplexCommands.Add(ShortCut, Command);
- if not AlternativeShortCut then
- FComplexCommandsReversed.Add(Command, ShortCut);
- end;
- function TIDEScintEdit.GetComplexCommand(
- const ShortCut: TShortCut): TIDEScintComplexCommand;
- begin
- if not FComplexCommands.TryGetValue(ShortCut, Result) or
- (ReadOnly and (Result = ccToggleLinesComment)) then
- Result := ccNone;
- end;
- function TIDEScintEdit.GetComplexCommandShortCut(
- const Command: TIDEScintComplexCommand): TShortCut;
- begin
- Result := FComplexCommandsReversed[Command];
- end;
- function TIDEScintEdit.GetRectExtendShiftState(
- const Desired: Boolean): TShiftState;
- begin
- Result := [ssShift, ssAlt];
- if ((FKeyMappingType = kmtVSCode) and Desired) or
- ((FKeyMappingType <> kmtVSCode) and not Desired) then
- Include(Result, ssCtrl);
- end;
- procedure TIDEScintEdit.SetKeyMappingType(
- const Value: TIDEScintKeyMappingType);
- begin
- if FKeyMappingType <> Value then begin
- FKeyMappingType := Value;
- Call(SCI_RESETALLCMDKEYS, Ord(FKeyMappingType = kmtVSCode), 0);
- if FKeyMappingType = kmtDefault then begin
- { Take some compatible improvements from the VSCode map }
- AssignCmdKey('C', [ssCtrl], SCI_COPYALLOWLINE);
- AssignCmdKey(SCK_INSERT, [ssCtrl], SCI_COPYALLOWLINE);
- AssignCmdKey('X', [ssCtrl], SCI_CUTALLOWLINE);
- AssignCmdKey(SCK_DELETE, [ssShift], SCI_CUTALLOWLINE);
- AssignCmdKey(SCK_UP, [ssAlt], SCI_MOVESELECTEDLINESUP);
- AssignCmdKey(SCK_DOWN, [ssAlt], SCI_MOVESELECTEDLINESDOWN);
- end;
- Call(SCI_SETMOUSEMAPPING, Ord(FKeyMappingType = kmtVSCode), 0);
- ClearCmdKey('/', [ssCtrl]); { Will be used by ccToggleLinesComment }
- ClearCmdKey('\', [ssCtrl]);
- if not FSmartHome then { Scintilla defaults to smart home (VCHOME*) }
- UpdateSmartHome;
- UpdateComplexCommands;
- end;
- end;
- procedure TIDEScintEdit.SetSmartHome(const Value: Boolean);
- begin
- if FSmartHome <> Value then begin
- FSmartHome := Value;
- UpdateSmartHome;
- end;
- end;
- procedure TIDEScintEdit.UpdateSmartHome;
- const
- Commands: array [Boolean] of array [0..2] of TScintCommand =
- ((SCI_HOME, SCI_HOMEEXTEND, SCI_HOMERECTEXTEND),
- (SCI_VCHOME, SCI_VCHOMEEXTEND, SCI_VCHOMERECTEXTEND));
- begin
- AssignCmdKey(SCK_HOME, [], Commands[FSmartHome][0]);
- AssignCmdKey(SCK_HOME, [ssShift], Commands[FSmartHome][1]);
- AssignCmdKey(SCK_HOME, GetRectExtendShiftState(True), Commands[FSmartHome][2]);
- end;
- procedure TIDEScintEdit.UpdateComplexCommands;
- begin
- FComplexCommands.Clear;
- FComplexCommandsReversed.Clear;
- { Normally VK_OEM_1 is ;, VK_OEM_6 is ], VK_OEM_4 is [, VK_OEM_2 is /, and VK_OEM_5 is \
- See CompFunc's NewShortcutToText for how it's is handled when they are different.
- Note: all VK_OEM shortcuts must have a menu item so the user can see what the
- shortcut is for their kayboard layout. }
- if FKeyMappingType = kmtVSCode then begin
- { Use freed Ctrl+D and Ctrl+Shift+L }
- AddComplexCommand(ShortCut(KeyToKeyCode('D'), [ssCtrl]), ccSelectNextOccurrence);
- AddComplexCommand(ShortCut(KeyToKeyCode('L'), [ssShift, ssCtrl]), ccSelectAllOccurrences);
- AddComplexCommand(ShortCut(VK_F2, [ssCtrl]), ccSelectAllOccurrences, True);
- end else begin
- AddComplexCommand(ShortCut(VK_OEM_PERIOD, [ssShift, ssAlt]), ccSelectNextOccurrence);
- AddComplexCommand(ShortCut(VK_OEM_1, [ssShift, ssAlt]), ccSelectAllOccurrences);
- end;
- AddComplexCommand(ShortCut(VK_RETURN, [ssAlt]), ccSelectAllFindMatches);
- AddComplexCommand(ShortCut(VK_ESCAPE, []), ccSimplifySelection);
- AddComplexCommand(ShortCut(VK_OEM_6, [ssShift, ssCtrl]), ccUnfoldLine);
- AddComplexCommand(ShortCut(VK_OEM_4, [ssShift, ssCtrl]), ccFoldLine);
- AddComplexCommand(ShortCut(VK_UP, [ssCtrl, ssAlt]), ccAddCursorUp);
- AddComplexCommand(ShortCut(VK_DOWN, [ssCtrl, ssAlt]), ccAddCursorDown);
- { Use freed Ctrl+/ }
- AddComplexCommand(ShortCut(VK_OEM_2, [ssCtrl]), ccToggleLinesComment); { Also see GetComplexCommand for ReadOnly check }
- AddComplexCommand(ShortCut(VK_OEM_5, [ssShift, ssCtrl]), ccBraceMatch);
- AddComplexCommand(ShortCut(KeyToKeyCode('I'), [ssShift, ssAlt]), ccAddCursorsToLineEnds);
- end;
- procedure TIDEScintEdit.SetUseFolding(const Value: Boolean);
- begin
- if FUseFolding <> Value then begin
- FUseFolding := Value;
- { If FUseFolding is True then caller must set the margin width using
- UpdateMarginsAndSquigglyAndCaretWidths else we set it to 0 now }
- if not FUseFolding then begin
- Call(SCI_FOLDALL, SC_FOLDACTION_EXPAND, 0);
- Call(SCI_SETMARGINWIDTHN, 3, 0);
- end;
- end;
- end;
- procedure TIDEScintEdit.UpdateIndicators(const Ranges: TScintRangeList;
- const IndicatorNumber: TIDEScintIndicatorNumber);
- function HashRanges(const Ranges: TScintRangeList): String;
- begin
- if Ranges.Count > 0 then begin
- var Context: TSHA256Context;
- SHA256Init(Context);
- for var Range in Ranges do
- SHA256Update(Context, Range, SizeOf(Range));
- Result := SHA256DigestToString(SHA256Final(Context));
- end else
- Result := '';
- end;
- begin
- var NewCount := Ranges.Count;
- var NewHash: String;
- var GotNewHash := False;
- var Update := NewCount <> FIndicatorCount[IndicatorNumber];
- if not Update and (NewCount <> 0) then begin
- NewHash := HashRanges(Ranges);
- GotNewHash := True;
- Update := NewHash <> FIndicatorHash[IndicatorNumber];
- end;
- if Update then begin
- Self.ClearIndicators(IndicatorNumber);
- for var Range in Ranges do
- Self.SetIndicators(Range.StartPos, Range.EndPos, IndicatorNumber, True);
- if not GotNewHash then
- NewHash := HashRanges(Ranges);
- FIndicatorCount[IndicatorNumber] := NewCount;
- FIndicatorHash[IndicatorNumber] := NewHash;
- end;
- end;
- procedure TIDEScintEdit.UpdateWidthsAndSizes(const IconMarkersWidth,
- BaseChangeHistoryWidth, BaseFolderMarkersWidth, LeftBlankMarginWidth,
- RightBlankMarginWidth, SquigglyWidth, CaretWidth, WhiteSpaceSize: Integer);
- begin
- Call(SCI_SETMARGINWIDTHN, mmIcons, IconMarkersWidth);
- var ChangeHistoryWidth: Integer;
- if ChangeHistory <> schDisabled then
- ChangeHistoryWidth := BaseChangeHistoryWidth
- else
- ChangeHistoryWidth := 0; { Current this is just the preprocessor output memo }
- Call(SCI_SETMARGINWIDTHN, mmChangeHistory, ChangeHistoryWidth);
- var FolderMarkersWidth: Integer;
- if FUseFolding then
- FolderMarkersWidth := BaseFolderMarkersWidth
- else
- FolderMarkersWidth := 0;
- Call(SCI_SETMARGINWIDTHN, mmFolding, FolderMarkersWidth);
- { Note: the first parameter is unused so the value '0' doesn't mean anything below }
- Call(SCI_SETMARGINLEFT, 0, LeftBlankMarginWidth);
- Call(SCI_SETMARGINRIGHT, 0, RightBlankMarginWidth);
- Call(SCI_INDICSETSTROKEWIDTH, minSquiggly, SquigglyWidth);
- Call(SCI_SETCARETWIDTH, CaretWidth, 0);
- Call(SCI_SETWHITESPACESIZE, WhiteSpaceSize, 0);
- end;
- procedure TIDEScintEdit.UpdateThemeColorsAndStyleAttributes;
- begin
- if FTheme <> nil then begin { Always True at the moment }
- Font.Color := FTheme.Colors[tcFore];
- Color := FTheme.Colors[tcBack];
- Call(SCI_SETELEMENTCOLOUR, SC_ELEMENT_LIST, FTheme.Colors[tcFore] or (SC_ALPHA_OPAQUE shl 24));
- Call(SCI_SETELEMENTCOLOUR, SC_ELEMENT_LIST_BACK, FTheme.Colors[tcIntelliBack] or (SC_ALPHA_OPAQUE shl 24));
- var Options := Call(SCI_AUTOCGETOPTIONS, 0, 0);
- if FTheme.Dark then
- Options := Options or SC_AUTOCOMPLETE_DARK_MODE
- else
- Options := Options and not SC_AUTOCOMPLETE_DARK_MODE;
- Call(SCI_AUTOCSETOPTIONS, Options, 0);
- Call(SCI_CALLTIPSETFORE, FTheme.Colors[tcFore], 0);
- Call(SCI_CALLTIPSETBACK, FTheme.Colors[tcIntelliBack], 0);
- Call(SCI_CALLTIPSETFOREHLT, FTheme.Colors[tcBlue], 0);
- var SelBackColor := FTheme.Colors[tcSelBack];
- Call(SCI_SETELEMENTCOLOUR, SC_ELEMENT_SELECTION_BACK, SelBackColor);
- Call(SCI_SETELEMENTCOLOUR, SC_ELEMENT_SELECTION_ADDITIONAL_BACK, SelBackColor);
- Call(SCI_SETELEMENTCOLOUR, SC_ELEMENT_SELECTION_SECONDARY_BACK, SelBackColor);
- Call(SCI_SETELEMENTCOLOUR, SC_ELEMENT_SELECTION_INACTIVE_BACK, SelBackColor);
- Call(SCI_SETELEMENTCOLOUR, SC_ELEMENT_SELECTION_INACTIVE_ADDITIONAL_BACK, SelBackColor);
- Call(SCI_SETELEMENTCOLOUR, SC_ELEMENT_WHITE_SPACE, FTheme.Colors[tcIndentGuideFore] or (SC_ALPHA_OPAQUE shl 24));
- Call(SCI_SETELEMENTCOLOUR, SC_ELEMENT_FOLD_LINE, FTheme.Colors[tcIndentGuideFore] or (70 shl 24));
- Call(SCI_SETFOLDMARGINCOLOUR, Ord(True), FTheme.Colors[tcBack]);
- Call(SCI_SETFOLDMARGINHICOLOUR, Ord(True), FTheme.Colors[tcBack]);
- Call(SCI_INDICSETFORE, minSquiggly, FTheme.Colors[tcRed]);
- Call(SCI_INDICSETFORE, minWordAtCursorOccurrence, FTheme.Colors[tcWordAtCursorOccurrenceBack]);
- Call(SCI_INDICSETFORE, minSelTextOccurrence, FTheme.Colors[tcSelTextOccurrenceBack]);
- Call(SCI_MARKERSETBACK, mlmStep, FTheme.Colors[tcBlue]);
-
- Call(SCI_MARKERSETFORE, SC_MARKNUM_HISTORY_REVERTED_TO_ORIGIN, FTheme.Colors[tcBlue]); { To reproduce: open a file, press enter, save, undo }
- Call(SCI_MARKERSETBACK, SC_MARKNUM_HISTORY_REVERTED_TO_ORIGIN, FTheme.Colors[tcBlue]);
- Call(SCI_MARKERSETFORE, SC_MARKNUM_HISTORY_SAVED, FTheme.Colors[tcGreen]);
- Call(SCI_MARKERSETBACK, SC_MARKNUM_HISTORY_SAVED, FTheme.Colors[tcGreen]);
- Call(SCI_MARKERSETFORE, SC_MARKNUM_HISTORY_MODIFIED, FTheme.Colors[tcReallyOrange]);
- Call(SCI_MARKERSETBACK, SC_MARKNUM_HISTORY_MODIFIED, FTheme.Colors[tcReallyOrange]);
- Call(SCI_MARKERSETFORE, SC_MARKNUM_HISTORY_REVERTED_TO_MODIFIED, FTheme.Colors[tcTeal]); { To reproduce: open a file, press space, press backspace, save, press enter, save, undo }
- Call(SCI_MARKERSETBACK, SC_MARKNUM_HISTORY_REVERTED_TO_MODIFIED, FTheme.Colors[tcTeal]);
- end;
- UpdateStyleAttributes;
- end;
- { TIDEScintFileEdit }
- constructor TIDEScintFileEdit.Create;
- begin
- inherited;
- FBreakPoints := TList<Integer>.Create;
- end;
- destructor TIDEScintFileEdit.Destroy;
- begin
- FBreakPoints.Free;
- inherited;
- end;
- { TIDEScintEditNavItem }
- constructor TIDEScintEditNavItem.Create(const AMemo: TIDEScintEdit);
- begin
- Memo := AMemo;
- Line := AMemo.CaretLine;
- Column := AMemo.CaretColumn;
- VirtualSpace := AMemo.CaretVirtualSpace;
- end;
- function TIDEScintEditNavItem.EqualMemoAndLine(
- const ANavItem: TIDEScintEditNavItem): Boolean;
- begin
- Result := (Memo = ANavItem.Memo) and (Line = ANavItem.Line);
- end;
- procedure TIDEScintEditNavItem.Invalidate;
- begin
- Memo := nil;
- end;
- function TIDEScintEditNavItem.Valid: Boolean;
- begin
- Result := (Memo <> nil) and (Line < Memo.Lines.Count); { Line check: see MemoLinesDeleted and RemoveMemoBadLinesFromNav }
- end;
- { TIDEScintEditNavStack }
- function TIDEScintEditNavStack.LinesDeleted(const AMemo: TIDEScintEdit;
- const FirstLine, LineCount: Integer): Boolean;
- begin
- Result := False;
- for var I := Count-1 downto 0 do begin
- var NavItem := Items[I];
- if NavItem.Memo = AMemo then begin
- var Line := NavItem.Line;
- if Line >= FirstLine then begin
- if Line < FirstLine + LineCount then begin
- Delete(I);
- Result := True;
- end else begin
- NavItem.Line := Line - LineCount;
- Items[I] := NavItem;
- end;
- end;
- end;
- end;
- if Result then
- Optimize;
- end;
- procedure TIDEScintEditNavStack.LinesInserted(const AMemo: TIDEScintEdit;
- const FirstLine, LineCount: Integer);
- begin
- for var I := 0 to Count-1 do begin
- var NavItem := Items[I];
- if NavItem.Memo = AMemo then begin
- var Line := NavItem.Line;
- if Line >= FirstLine then begin
- NavItem.Line := Line + LineCount;
- Items[I] := NavItem;
- end;
- end;
- end;
- end;
- procedure TIDEScintEditNavStack.Optimize;
- begin
- { Turn two entries for the same memo and line which are next to each other
- into one entry, ignoring column differences (like Visual Studio 2022)
- Note: doesn't yet look at CompForm's FCurrentNavItem to see if a stack's top
- item is the same so it doesnt optimize that situation atm }
- for var I := Count-1 downto 1 do
- if Items[I].EqualMemoAndLine(Items[I-1]) then
- Delete(I);
- end;
- function TIDEScintEditNavStack.RemoveMemo(
- const AMemo: TIDEScintEdit): Boolean;
- begin
- Result := False;
- for var I := Count-1 downto 0 do begin
- if Items[I].Memo = AMemo then begin
- Delete(I);
- Result := True;
- end;
- end;
- if Result then
- Optimize;
- end;
- function TIDEScintEditNavStack.RemoveMemoBadLines(
- const AMemo: TIDEScintEdit): Boolean;
- begin
- Result := False;
- var LastGoodLine := AMemo.Lines.Count-1;
- for var I := Count-1 downto 0 do begin
- if (Items[I].Memo = AMemo) and (Items[I].Line > LastGoodLine) then begin
- Delete(I);
- Result := True;
- end;
- end;
- if Result then
- Optimize;
- end;
- { TIDEScintEditNavStacks }
- constructor TIDEScintEditNavStacks.Create;
- begin
- inherited;
- FBackNavStack := TIDEScintEditNavStack.Create;
- FForwardNavStack := TIDEScintEditNavStack.Create;
- end;
- destructor TIDEScintEditNavStacks.Destroy;
- begin
- FForwardNavStack.Free;
- FBackNavStack.Free;
- inherited;
- end;
- function TIDEScintEditNavStacks.AddNewBackForJump(const OldNavItem,
- NewNavItem: TIDEScintEditNavItem): Boolean;
- begin
- { Want a new item when changing tabs or moving at least 11 lines at once,
- similar to Visual Studio 2022, see:
- https://learn.microsoft.com/en-us/archive/blogs/zainnab/navigate-backward-and-navigate-forward
- Note: not doing the other stuff listed in the article atm }
- Result := (OldNavItem.Memo <> NewNavItem.Memo) or
- (Abs(OldNavItem.Line - NewNavItem.Line) >= 11);
- if Result then begin
- FBackNavStack.Add(OldNavItem);
- Limit;
- end;
- end;
- procedure TIDEScintEditNavStacks.Clear;
- begin
- FBackNavStack.Clear;
- FForwardNavStack.Clear;
- end;
- procedure TIDEScintEditNavStacks.Limit;
- begin
- { The dropdown showing both stacks + the current nav item should show at most
- 16 items just like Visual Studio 2022 }
- if FBackNavStack.Count + FForwardNavStack.Count >= 15 then
- FBackNavStack.Delete(0);
- end;
- function TIDEScintEditNavStacks.LinesDeleted(const AMemo: TIDEScintEdit;
- const FirstLine, LineCount: Integer): Boolean;
- begin
- Result := FBackNavStack.LinesDeleted(AMemo, FirstLine, LineCount);
- Result := FForwardNavStack.LinesDeleted(AMemo, FirstLine, LineCount) or Result;
- end;
- procedure TIDEScintEditNavStacks.LinesInserted(const AMemo: TIDEScintEdit;
- const FirstLine, LineCount: Integer);
- begin
- FBackNavStack.LinesInserted(AMemo, FirstLine, LineCount);
- FForwardNavStack.LinesInserted(AMemo, FirstLine, LineCount);
- end;
- function TIDEScintEditNavStacks.RemoveMemo(
- const AMemo: TIDEScintEdit): Boolean;
- begin
- Result := FBackNavStack.RemoveMemo(AMemo);
- Result := FForwardNavStack.RemoveMemo(AMemo) or Result;
- end;
- function TIDEScintEditNavStacks.RemoveMemoBadLines(
- const AMemo: TIDEScintEdit): Boolean;
- begin
- Result := FBackNavStack.RemoveMemoBadLines(AMemo);
- Result := FForwardNavStack.RemoveMemoBadLines(AMemo) or Result;
- end;
- end.
|