/components/synedit/synbeautifier.pas
Pascal | 1810 lines | 1409 code | 217 blank | 184 comment | 148 complexity | 224650010fd300b1ae7c059b8b652bb3 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception
- {-------------------------------------------------------------------------------
- The contents of this file are subject to the Mozilla Public License
- Version 1.1 (the "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
- http://www.mozilla.org/MPL/
- Software distributed under the License is distributed on an "AS IS" basis,
- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
- the specific language governing rights and limitations under the License.
- The Original Code is: SynHighlighterGeneral.pas, released 2000-04-07.
- The Original Code is based on the mwGeneralSyn.pas file from the
- mwEdit component suite by Martin Waldenburg and other developers, the Initial
- Author of this file is Martin Waldenburg.
- Portions written by Martin Waldenburg are copyright 1999 Martin Waldenburg.
- All Rights Reserved.
- Contributors to the SynEdit and mwEdit projects are listed in the
- Contributors.txt file.
- Alternatively, the contents of this file may be used under the terms of the
- GNU General Public License Version 2 or later (the "GPL"), in which case
- the provisions of the GPL are applicable instead of those above.
- If you wish to allow use of your version of this file only under the terms
- of the GPL and not to allow others to use your version of this file
- under the MPL, indicate your decision by deleting the provisions above and
- replace them with the notice and other provisions required by the GPL.
- If you do not delete the provisions above, a recipient may use your version
- of this file under either the MPL or the GPL.
- $Id: SynHighlighterGeneral.pas,v 1.3 2000/11/08 22:09:59 mghie Exp $
- You may retrieve the latest version of this file at the SynEdit home page,
- located at http://SynEdit.SourceForge.net
- }
- unit SynBeautifier;
- {$I synedit.inc}
- interface
- uses
- Classes, SysUtils, LCLProc, SynEditMiscClasses, SynEditMiscProcs, LazSynEditText, SynEditPointClasses,
- SynEditKeyCmds, SynHighlighterPas, SynEditHighlighterFoldBase, SynRegExpr;
- type
- TSynCustomBeautifier = class;
- // Callback for indent
- TSynBeautifierSetIndentProc =
- procedure(
- (* LinePos:
- 1-based, the line that should be changed *)
- LinePos: Integer;
- (* Indent:
- New indent in spaces (Logical = Physical *)
- Indent: Integer;
- (* RelativeToLinePos:
- Indent specifies +/- offset from indent on RTLine (0: for absolute indent) *)
- RelativeToLinePos: Integer = 0;
- (* IndentChars:
- String used to build indent; maybe empty, single char, or string (usually 1 tab or 1 space)
- The String will be repeated and cut as needed, then filled with spaces at the end
- * NOTE: If this is specified the TSynBeautifierIndentType is ignored
- *)
- IndentChars: String = '';
- (* IndentCharsFromLinePos:
- Use tab/space mix from this Line for indent (if specified > 0)
- "IndentChars" will only be used, if the found tab/space mix is to short
- * NOTE: If this is specified the TSynBeautifierIndentType is ignored
- *)
- IndentCharsFromLinePos: Integer = 0
- ) of object;
- // Event triggered if Lines may needs Indend
- TSynBeautifierGetIndentEvent =
- function(
- Sender: TObject; // the beautifier
- Editor: TObject; // the synedit
- LogCaret, OldLogCaret: TPoint; // Caret after and before the edit action
- FirstLinePos, LastLinePos: Integer; // Changed lines. this can include lines outside the range of OldLogCaret to LogCaret
- Reason: TSynEditorCommand; // what caused the event
- SetIndentProc: TSynBeautifierSetIndentProc
- ): boolean of object;
- { TSynCustomBeautifier }
- TSynCustomBeautifier = class(TComponent)
- private
- FAutoIndent: Boolean;
- FOnGetDesiredIndent: TSynBeautifierGetIndentEvent;
- FCurrentEditor: TSynEditBase; // For callback / applyIndent
- FCurrentLines: TSynEditStrings;
- protected
- procedure DoBeforeCommand(const ACaret: TSynEditCaret;
- var Command: TSynEditorCommand); virtual; abstract;
- procedure DoAfterCommand(const ACaret: TSynEditCaret;
- var Command: TSynEditorCommand;
- StartLinePos, EndLinePos: Integer); virtual; abstract;
- property CurrentEditor: TSynEditBase read FCurrentEditor;
- property CurrentLines: TSynEditStrings read FCurrentLines;
- public
- procedure Assign(Src: TPersistent); override;
- function GetCopy: TSynCustomBeautifier;
- procedure BeforeCommand(const Editor: TSynEditBase; const Lines: TSynEditStrings;
- const ACaret: TSynEditCaret; var Command: TSynEditorCommand;
- InitialCmd: TSynEditorCommand);
- procedure AfterCommand(const Editor: TSynEditBase; const Lines: TSynEditStrings;
- const ACaret: TSynEditCaret; var Command: TSynEditorCommand;
- InitialCmd: TSynEditorCommand; StartLinePos, EndLinePos: Integer);
- // GetDesiredIndentForLine: Returns the 1-based Physical x pos
- function GetDesiredIndentForLine
- (Editor: TSynEditBase; const Lines: TSynEditStrings;
- const ACaret: TSynEditCaret): Integer; virtual; abstract;
- function GetDesiredIndentForLine
- (Editor: TSynEditBase; const Lines: TSynEditStrings;
- const ACaret: TSynEditCaret; out ReplaceIndent: Boolean;
- out DesiredIndent: String): Integer; virtual; abstract;
- property OnGetDesiredIndent: TSynBeautifierGetIndentEvent
- read FOnGetDesiredIndent write FOnGetDesiredIndent;
- property AutoIndent: Boolean read FAutoIndent write FAutoIndent;
- end;
- TSynCustomBeautifierClass = class of TSynCustomBeautifier;
- TSynBeautifierIndentType = (
- sbitSpace, sbitCopySpaceTab,
- sbitPositionCaret,
- sbitConvertToTabSpace, // convert to tabs, fill with spcaces if needed
- sbitConvertToTabOnly // convert to tabs, even if shorter
- );
- { TSynBeautifier }
- TSynBeautifier = class(TSynCustomBeautifier)
- private
- FIndentType: TSynBeautifierIndentType;
- protected
- FLogicalIndentLen: Integer;
- function GetLine(AnIndex: Integer): String; virtual;
- procedure GetIndentInfo(const LinePos: Integer;
- out ACharMix: String; out AnIndent: Integer;
- AStopScanAtLine: Integer = 0);
- // requiring FCurrentEditor, FCurrentLines
- procedure DoBeforeCommand(const ACaret: TSynEditCaret;
- var Command: TSynEditorCommand); override;
- procedure DoAfterCommand(const ACaret: TSynEditCaret;
- var Command: TSynEditorCommand;
- StartLinePos, EndLinePos: Integer); override;
- function GetIndent(const LinePos: Integer; out BasedOnLine: Integer;
- AStopScanAtLine: Integer = 0): Integer;
- function AdjustCharMix(DesiredIndent: Integer; CharMix, AppendMix: String): String;
- function CreateTabSpaceMix(var DesiredIndent: Integer; OnlyTabs: Boolean): String;
- function GetCharMix(const LinePos: Integer; var Indent: Integer;
- var IndentCharsFromLinePos: Integer;
- ModifyIndent: Boolean = False): String;
- procedure ApplyIndent(LinePos: Integer; Indent: Integer;
- RelativeToLinePos: Integer = 0; IndentChars: String = '';
- IndentCharsFromLinePos: Integer = 0);
- function UnIndentLine(const ACaret: TSynEditCaret; out CaretNewX: Integer): Boolean;
- public
- procedure Assign(Src: TPersistent); override;
- // Retruns a 0-based position (even 0-based physical)
- function GetIndentForLine(Editor: TSynEditBase; const Line: string;
- Physical: boolean): Integer;
- function GetDesiredIndentForLine
- (Editor: TSynEditBase; const Lines: TSynEditStrings;
- const ACaret: TSynEditCaret): Integer; override;
- function GetDesiredIndentForLine
- (Editor: TSynEditBase; const Lines: TSynEditStrings;
- const ACaret: TSynEditCaret; out ReplaceIndent: Boolean;
- out DesiredIndent: String): Integer; override;
- published
- property IndentType: TSynBeautifierIndentType read FIndentType write FIndentType;
- end;
- TSynCommentContineMode = (
- sccNoPrefix, // May still do indent, if matched
- sccPrefixAlways, // If the pattern did not match all will be done, except the indent AFTER the prefix (can not be detected)
- sccPrefixMatch
- //sccPrefixMatchFirst
- //sccPrefixMatchPrev
- //sccPrefixMatchPrevTwo // last 2 lines match
- //sccPrefixMatchPrevTwoExact // last 2 lines match and same result for prefix
- );
- TSynCommentMatchLine = (
- sclMatchFirst, // Match the first line of the comment to get substitutes for Prefix ($1)
- sclMatchPrev // Match the previous line of the comment to get substitutes for Prefix ($1)
- //sclMatchNestedFirst // For nested comments, first line of this nest.
- );
- TSynCommentMatchMode = (
- // Which parts of the first line to match sclMatchFirst or sclMatchPrev (if prev = first)
- scmMatchAfterOpening, // will not include (*,{,//. The ^ will match the first char after
- scmMatchOpening, // will include (*,{,//. The ^ will match the ({/
- scmMatchWholeLine, // Match the entire line
- scmMatchAtAsterisk // AnsiComment only, will match the * of (*, but not the (
- );
- TSynCommentIndentFlag = (
- // * For Matching lines (FCommentMode)
- // By default indent is the same as for none comment lines (none overrides sciAlignOpen)
- sciNone, // Does not Indent comment lines (Prefix may contain a fixed indent)
- sciAlignOpen, // Indent to real opening pos on first line, if comment does not start at BOL "Foo(); (*"
- // Will force every new line back to Align.
- // To align only once, use IndentFirstLineExtra=MaxInt
- // sciAdd...: if (only if) previous line had started with the opening token "(*" or "{".
- // or if sciAlignOpen is set
- // This is not done for "//", as Comment Extension will add a new "//"
- // But Applies to sciNone
- sciAddTokenLen, // add 1 or 2 spaces to indent (for the length of the token)
- // in case of scmMatchAtAsterisk, 1 space is added. ("(" only)
- sciAddPastTokenIndent, // Adds any indent found past the opening token "(*", "{" or "//".
- // For "//" this is added after the nem "//", but before the prefix.
- sciMatchOnlyTokenLen, // Apply the Above only if first line matches. (Only if sciAddTokenLen is specified)
- sciMatchOnlyPastTokenIndent,
- sciAlignOnlyTokenLen, // Apply the Above only if sciAlignOpen was used (include via max)
- sciAlignOnlyPastTokenIndent,
- // flag to ignore spaces, that are matched.
- // flag to be smart, if not matched
- sciApplyIndentForNoMatch // Apply above rules For NONE Matching lines (FCommentMode),
- // includes FIndentFirstLineExtra
- );
- TSynCommentIndentFlags = set of TSynCommentIndentFlag;
- TSynCommentExtendMode = (
- sceNever, // Never Extend
- sceAlways, // Always
- sceSplitLine, // If the line was split (caret was not at EOL, when enter was pressed
- sceMatching, // If the line matched (even if sccPrefixAlways or sccNoPrefix
- sceMatchingSplitLine
- );
- TSynCommentType = (sctAnsi, sctBor, sctSlash);
- // end mode
- { TSynBeautifierPascal }
- TSynBeautifierPascal = class(TSynBeautifier)
- private
- FIndentMode: Array [TSynCommentType] of TSynCommentIndentFlags;
- FIndentFirstLineExtra: Array [TSynCommentType] of String;
- FIndentFirstLineMax: Array [TSynCommentType] of Integer;
- FCommentMode: Array [TSynCommentType] of TSynCommentContineMode;
- FMatchMode: Array [TSynCommentType] of TSynCommentMatchMode;
- FMatchLine: Array [TSynCommentType] of TSynCommentMatchLine;
- FMatch: Array [TSynCommentType] of String;
- FPrefix: Array [TSynCommentType] of String;
- FCommentIndent: Array [TSynCommentType] of TSynBeautifierIndentType;
- FEolMatch: Array [TSynCommentType] of String;
- FEolMode: Array [TSynCommentType] of TSynCommentContineMode;
- FEolPostfix: Array [TSynCommentType] of String;
- FEolSkipLongerLine: Array [TSynCommentType] of Boolean;
- FExtendSlashCommentMode: TSynCommentExtendMode;
- private
- FPasHighlighter: TSynPasSyn;
- FCaretAtEOL: Boolean;
- FStringBreakAppend: String;
- FStringBreakEnabled: Boolean;
- FStringBreakPrefix: String;
- FWorkLine: Integer; // 1 based
- FWorkFoldType: TSynCommentType;
- FGetLineAfterComment: Boolean;
- FRegExprEngine: TRegExpr;
- FCacheFoldEndLvlIdx, FCacheFoldEndLvl: Integer;
- FCacheCommentLvlIdx, FCacheCommentLvl: Integer;
- FCacheFoldTypeForIdx: Integer;
- FCacheFoldType: TPascalCodeFoldBlockType;
- FCacheFirstLineForIdx, FCacheFirstLine: Integer;
- FCacheSlashColumnForIdx, FCacheSlashColumn: Integer;
- FCacheCommentStartColForIdx, FCacheCommentStartCol: Integer;
- FCacheLastHlTokenForIdx, FCacheLastHlTokenCol: Integer;
- FCacheLastHlTokenKind: TtkTokenKind;
- FCacheWorkFoldEndLvl: Integer;
- FCacheWorkCommentLvl: Integer;
- FCacheWorkFoldType: TPascalCodeFoldBlockType;
- FCacheWorkFirstLine: Integer;
- FCacheWorkSlashCol: Integer;
- FCacheWorkCommentStartCol: Integer;
- protected
- function IsPasHighlighter: Boolean;
- procedure InitCache;
- procedure InitPasHighlighter;
- // Generic Helpers
- function GetFoldEndLevelForIdx(AIndex: Integer): Integer;
- function GetFoldCommentLevelForIdx(AIndex: Integer): Integer;
- function GetFoldTypeAtEndOfLineForIdx(AIndex: Integer): TPascalCodeFoldBlockType;
- function GetFirstCommentLineForIdx(AIndex: Integer): Integer; // Result is 1 based
- function GetSlashStartColumnForIdx(AIndex: Integer): Integer;
- function GetLastHlTokenForIdx(AIndex: Integer; out APos: Integer): TtkTokenKind;
- function GetCommentStartColForIdx(AIndex: Integer): Integer; // Returns Column (logic) for GetFirstCommentLineForIdx(AIndex)
- // Values based on FWorkLine
- function GetFoldEndLevel: Integer;
- function GetFoldCommentLevel: Integer;
- function GetFoldTypeAtEndOfLine: TPascalCodeFoldBlockType;
- function GetFirstCommentLine: Integer; // Result is 1 based
- function GetSlashStartColumn: Integer; // Acts on FWorkLine-1
- function GetCommentStartCol: Integer; // Acts on GetFirstCommentLineForIdx(FWorkLine) / Logical Resulc
- function GetMatchStartColForIdx(AIndex: Integer): Integer; // Res=1-based
- function GetMatchStartPos(AIndex: Integer = -1; AForceFirst: Boolean = False): TPoint; // Res=1-based / AIndex-1 is only used for sclMatchPrev
- protected
- function GetLine(AnIndex: Integer): String; override;
- procedure DoBeforeCommand(const ACaret: TSynEditCaret;
- var Command: TSynEditorCommand); override;
- procedure DoAfterCommand(const ACaret: TSynEditCaret;
- var Command: TSynEditorCommand;
- StartLinePos, EndLinePos: Integer); override;
- procedure DoNewLineInString(AStringStartY, AStringStartX: Integer;
- const ACaret: TSynEditCaret;
- var Command: TSynEditorCommand;
- StartLinePos, EndLinePos: Integer);
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure Assign(Src: TPersistent); override;
- // Retruns a 0-based position (even 0-based physical)
- published
- // *** coments with (* *)
- (* AnsiIndentFirstLineMax:
- * For comments that do NOT start at the BOL in their opening line " Foo; ( * comment":
- if AnsiIndentFirstLineMax is is set:
- - If the comment starts before or at the "Max-Column, then sciAlignOpen is always used.
- - If the comment starts after max column, then the specified mode is used.
- If the specified mode is sciAlignOpen it will be cut at Max
- * For comments that start at BOL in their opening line " ( * comment":
- This is ignored
- AnsiIndentFirstLineExtra:
- For comments that do NOT start at the BOL, an extra indent is added
- on the 2nd line (except if sciAlignOpen is used (in Flags, or via A.I.FirstLineMax))
- *)
- property AnsiIndentMode: TSynCommentIndentFlags read FIndentMode[sctAnsi]
- write FIndentMode[sctAnsi];
- property AnsiIndentFirstLineMax: Integer read FIndentFirstLineMax[sctAnsi]
- write FIndentFirstLineMax[sctAnsi];
- property AnsiIndentFirstLineExtra: String read FIndentFirstLineExtra[sctAnsi]
- write FIndentFirstLineExtra[sctAnsi];
- (* match should start with ^, and leading spaces should not be in () "^\s?(\*\*?\*?)"
- prefix can refer to $1 and should have spaces to indent after the match "$1 "
- *)
- property AnsiCommentMode: TSynCommentContineMode read FCommentMode[sctAnsi]
- write FCommentMode[sctAnsi];
- property AnsiMatch: String read FMatch[sctAnsi]
- write FMatch[sctAnsi];
- property AnsiPrefix : String read FPrefix[sctAnsi]
- write FPrefix[sctAnsi];
- property AnsiMatchMode: TSynCommentMatchMode read FMatchMode[sctAnsi]
- write FMatchMode[sctAnsi];
- property AnsiMatchLine: TSynCommentMatchLine read FMatchLine[sctAnsi]
- write FMatchLine[sctAnsi];
- property AnsiCommentIndent: TSynBeautifierIndentType read FCommentIndent[sctAnsi]
- write FCommentIndent[sctAnsi];
- // Add postfix at EOL
- property AnsiEolMode: TSynCommentContineMode read FEolMode[sctAnsi]
- write FEolMode[sctAnsi];
- property AnsiEolPostfix: String read FEolPostfix[sctAnsi]
- write FEolPostfix[sctAnsi];
- property AnsiEolMatch: String read FEolMatch[sctAnsi]
- write FEolMatch[sctAnsi];
- property AnsiEolSkipLongerLine: Boolean read FEolSkipLongerLine[sctAnsi]
- write FEolSkipLongerLine[sctAnsi];
- // *** coments with { }
- property BorIndentMode: TSynCommentIndentFlags read FIndentMode[sctBor]
- write FIndentMode[sctBor];
- property BorIndentFirstLineMax: Integer read FIndentFirstLineMax[sctBor]
- write FIndentFirstLineMax[sctBor];
- property BorIndentFirstLineExtra: String read FIndentFirstLineExtra[sctBor]
- write FIndentFirstLineExtra[sctBor];
- property BorCommentMode: TSynCommentContineMode read FCommentMode[sctBor]
- write FCommentMode[sctBor];
- property BorMatch: String read FMatch[sctBor]
- write FMatch[sctBor];
- property BorPrefix : String read FPrefix[sctBor]
- write FPrefix[sctBor];
- property BorMatchMode: TSynCommentMatchMode read FMatchMode[sctBor]
- write FMatchMode[sctBor];
- property BorMatchLine: TSynCommentMatchLine read FMatchLine[sctBor]
- write FMatchLine[sctBor];
- property BorCommentIndent: TSynBeautifierIndentType read FCommentIndent[sctBor]
- write FCommentIndent[sctBor];
- property BorEolMode: TSynCommentContineMode read FEolMode[sctBor]
- write FEolMode[sctBor];
- property BorEolPostfix : String read FEolPostfix[sctBor]
- write FEolPostfix[sctBor];
- property BorEolMatch: String read FEolMatch[sctBor]
- write FEolMatch[sctBor];
- property BorEolSkipLongerLine: Boolean read FEolSkipLongerLine[sctBor]
- write FEolSkipLongerLine[sctBor];
- // *** coments with //
- // Continue only, if Extended
- property ExtendSlashCommentMode: TSynCommentExtendMode read FExtendSlashCommentMode
- write FExtendSlashCommentMode;
- property SlashIndentMode: TSynCommentIndentFlags read FIndentMode[sctSlash]
- write FIndentMode[sctSlash];
- property SlashIndentFirstLineMax: Integer read FIndentFirstLineMax[sctSlash]
- write FIndentFirstLineMax[sctSlash];
- property SlashIndentFirstLineExtra: String read FIndentFirstLineExtra[sctSlash]
- write FIndentFirstLineExtra[sctSlash];
- property SlashCommentMode: TSynCommentContineMode read FCommentMode[sctSlash]
- write FCommentMode[sctSlash];
- property SlashMatch: String read FMatch[sctSlash]
- write FMatch[sctSlash];
- property SlashPrefix : String read FPrefix[sctSlash]
- write FPrefix[sctSlash];
- property SlashMatchMode: TSynCommentMatchMode read FMatchMode[sctSlash]
- write FMatchMode[sctSlash];
- property SlashMatchLine: TSynCommentMatchLine read FMatchLine[sctSlash]
- write FMatchLine[sctSlash];
- property SlashCommentIndent: TSynBeautifierIndentType read FCommentIndent[sctSlash]
- write FCommentIndent[sctSlash];
- property SlashEolMode: TSynCommentContineMode read FEolMode[sctSlash]
- write FEolMode[sctSlash];
- property SlashEolPostfix : String read FEolPostfix[sctSlash]
- write FEolPostfix[sctSlash];
- property SlashEolMatch: String read FEolMatch[sctSlash]
- write FEolMatch[sctSlash];
- property SlashEolSkipLongerLine: Boolean read FEolSkipLongerLine[sctSlash]
- write FEolSkipLongerLine[sctSlash];
- // String
- property StringBreakEnabled: Boolean read FStringBreakEnabled write FStringBreakEnabled;
- property StringBreakAppend: String read FStringBreakAppend write FStringBreakAppend;
- property StringBreakPrefix: String read FStringBreakPrefix write FStringBreakPrefix;
- end;
- function dbgs(ACommentType: TSynCommentType): String; overload;
- function dbgs(AContinueMode: TSynCommentContineMode): String; overload;
- function dbgs(AMatchLine: TSynCommentMatchLine): String; overload;
- function dbgs(AMatchMode: TSynCommentMatchMode): String; overload;
- function dbgs(AIndentFlag: TSynCommentIndentFlag): String; overload;
- function dbgs(AIndentFlags: TSynCommentIndentFlags): String; overload;
- function dbgs(AExtendMode: TSynCommentExtendMode): String; overload;
- implementation
- uses SynEdit;
- function dbgs(ACommentType: TSynCommentType): String;
- begin
- Result := ''; WriteStr(Result, ACommentType);
- end;
- function dbgs(AContinueMode: TSynCommentContineMode): String;
- begin
- Result := ''; WriteStr(Result, AContinueMode);
- end;
- function dbgs(AMatchLine: TSynCommentMatchLine): String;
- begin
- Result := ''; WriteStr(Result, AMatchLine);
- end;
- function dbgs(AMatchMode: TSynCommentMatchMode): String;
- begin
- Result := ''; WriteStr(Result, AMatchMode);
- end;
- function dbgs(AIndentFlag: TSynCommentIndentFlag): String;
- begin
- Result := ''; WriteStr(Result, AIndentFlag);
- end;
- function dbgs(AIndentFlags: TSynCommentIndentFlags): String;
- var
- i: TSynCommentIndentFlag;
- begin
- Result := '';
- for i := low(TSynCommentIndentFlag) to high(TSynCommentIndentFlag) do
- if i in AIndentFlags then
- if Result = ''
- then Result := dbgs(i)
- else Result := Result + ',' + dbgs(i);
- if Result <> '' then
- Result := '[' + Result + ']';
- end;
- function dbgs(AExtendMode: TSynCommentExtendMode): String;
- begin
- Result := ''; WriteStr(Result, AExtendMode);
- end;
- { TSynBeautifierPascal }
- function TSynBeautifierPascal.IsPasHighlighter: Boolean;
- begin
- Result := (TSynEdit(FCurrentEditor).Highlighter <> nil) and
- (TSynEdit(FCurrentEditor).Highlighter is TSynPasSyn);
- end;
- procedure TSynBeautifierPascal.InitCache;
- begin
- FCacheFoldEndLvlIdx := -1;
- FCacheCommentLvlIdx := -1;
- FCacheFoldTypeForIdx := -1;
- FCacheFirstLineForIdx := -1;
- FCacheSlashColumnForIdx := -1;
- FCacheCommentStartColForIdx := -1;
- FCacheWorkFoldEndLvl := -2;
- FCacheWorkCommentLvl := -2;
- FCacheWorkFoldType := cfbtIfThen; // dummy for not yet cached
- FCacheWorkFirstLine := -2;
- FCacheWorkSlashCol := -2;
- FCacheWorkCommentStartCol := -2;
- end;
- procedure TSynBeautifierPascal.InitPasHighlighter;
- begin
- FPasHighlighter := TSynPasSyn(TSynEdit(FCurrentEditor).Highlighter);
- FPasHighlighter.CurrentLines := FCurrentLines;
- FPasHighlighter.ScanRanges;
- end;
- function TSynBeautifierPascal.GetFoldEndLevelForIdx(AIndex: Integer): Integer;
- begin
- Result := FCacheFoldEndLvl;
- if AIndex = FCacheFoldEndLvlIdx then
- exit;
- FCacheFoldEndLvlIdx := AIndex;
- FCacheFoldEndLvl := FPasHighlighter.FoldBlockEndLevel(AIndex, FOLDGROUP_PASCAL, [sfbIncludeDisabled]);
- Result := FCacheFoldEndLvl;
- end;
- function TSynBeautifierPascal.GetFoldCommentLevelForIdx(AIndex: Integer): Integer;
- var
- tmp: Pointer;
- Block: TPascalCodeFoldBlockType;
- begin
- Result := FCacheCommentLvl;
- if AIndex = FCacheCommentLvlIdx then
- exit;
- FCacheCommentLvlIdx := AIndex;
- FCacheCommentLvl := GetFoldEndLevelForIdx(AIndex) - 1;
- while (FCacheCommentLvl > 0) do begin
- FPasHighlighter.FoldBlockNestedTypes(AIndex , FCacheCommentLvl - 1,
- tmp, FOLDGROUP_PASCAL, [sfbIncludeDisabled]);
- Block:=TPascalCodeFoldBlockType(PtrUInt(tmp));
- if not (Block in [cfbtAnsiComment, cfbtBorCommand, cfbtSlashComment]) then
- break;
- dec(FCacheCommentLvl);
- end;
- inc(FCacheCommentLvl);
- Result := FCacheCommentLvl;
- end;
- function TSynBeautifierPascal.GetFoldTypeAtEndOfLineForIdx(AIndex: Integer): TPascalCodeFoldBlockType;
- var
- EndLevel: Integer;
- tmp: Pointer;
- begin
- // TODO cfbtNestedComment
- Result := FCacheFoldType;
- if AIndex = FCacheFoldTypeForIdx then
- exit;
- FCacheFoldTypeForIdx := AIndex;
- EndLevel := GetFoldEndLevelForIdx(AIndex);
- if (EndLevel > 0) then
- begin
- if FPasHighlighter.FoldBlockNestedTypes(AIndex, EndLevel - 1,
- tmp, FOLDGROUP_PASCAL, [sfbIncludeDisabled])
- then
- FCacheFoldType:=TPascalCodeFoldBlockType(PtrUInt(tmp))
- else
- FCacheFoldType := cfbtNone;
- end;
- while (FCacheFoldType = cfbtNestedComment) and (EndLevel > 1) do begin
- dec(EndLevel);
- if FPasHighlighter.FoldBlockNestedTypes(AIndex, EndLevel - 1,
- tmp, FOLDGROUP_PASCAL, [sfbIncludeDisabled])
- then
- FCacheFoldType:=TPascalCodeFoldBlockType(PtrUInt(tmp))
- else
- FCacheFoldType := cfbtNone;
- end;
- Result := FCacheFoldType;
- end;
- function TSynBeautifierPascal.GetFirstCommentLineForIdx(AIndex: Integer): Integer;
- var
- FoldType: TPascalCodeFoldBlockType;
- ANewIndex, EndLvl, CommentLvl: Integer;
- begin
- Result := FCacheFirstLine;
- if AIndex = FCacheFirstLineForIdx then
- exit;
- ANewIndex := AIndex;
- FoldType := GetFoldTypeAtEndOfLineForIdx(ANewIndex);
- EndLvl := GetFoldEndLevelForIdx(ANewIndex);
- //if (EndLvl = 0) or not(FoldType in [cfbtAnsiComment, cfbtBorCommand, cfbtSlashComment]) and
- // (AIndex > 0) and (GetSlashStartColumnForIdx(AIndex - 1) > 0)
- //then begin
- // dec(ANewIndex);
- // FoldType := GetFoldTypeAtEndOfLineForIdx(ANewIndex);
- // EndLvl := GetFoldEndLevelForIdx(ANewIndex);
- //end;
- if (EndLvl = 0) or not(FoldType in [cfbtAnsiComment, cfbtBorCommand, cfbtSlashComment])
- then begin
- Result := ToPos(AIndex) - 1; // 1 based - the line above ANewIndex
- // maybe the line above has a trailing comment
- //if (AIndex <> ANewIndex) and (ANewIndex > 0) and (GetSlashStartColumnForIdx(ANewIndex-1) > 0) then
- // Result := ToPos(ANewIndex) - 1;
- exit;
- end;
- FCacheFirstLineForIdx := AIndex;
- FCacheFirstLine := ToPos(ANewIndex) - 1;
- CommentLvl := GetFoldCommentLevelForIdx(ANewIndex);
- while (FCacheFirstLine > 0) do begin
- if CommentLvl > FPasHighlighter.FoldBlockMinLevel(FCacheFirstLine-1, FOLDGROUP_PASCAL, [sfbIncludeDisabled]) then
- break;
- dec(FCacheFirstLine);
- end;
- if FoldType = cfbtSlashComment then begin
- // maybe the line above has a trailing comment
- if GetSlashStartColumnForIdx(ToIdx(FCacheFirstLine)) > 0 then
- dec(FCacheFirstLine);
- end;
- Result := FCacheFirstLine;
- //debugln(['FIRST LINE ', FCacheFirstLine]);
- end;
- function TSynBeautifierPascal.GetSlashStartColumnForIdx(AIndex: Integer): Integer;
- var
- Tk: TtkTokenKind;
- begin
- Result := FCacheSlashColumn;
- if AIndex = FCacheSlashColumnForIdx then
- exit;
- FCacheSlashColumnForIdx := AIndex;
- Tk := GetLastHlTokenForIdx(AIndex, FCacheSlashColumn);
- if Tk <> SynHighlighterPas.tkComment then
- FCacheSlashColumn := -1;
- Result := FCacheSlashColumn;
- end;
- function TSynBeautifierPascal.GetLastHlTokenForIdx(AIndex: Integer; out
- APos: Integer): TtkTokenKind;
- begin
- Result := FCacheLastHlTokenKind;
- APos := FCacheLastHlTokenCol;
- if AIndex = FCacheLastHlTokenForIdx then
- exit;
- FCacheSlashColumnForIdx := AIndex;
- FCacheLastHlTokenKind := SynHighlighterPas.tkUnknown;
- FCacheLastHlTokenCol := -1;
- if (FCurrentLines[AIndex] <> '') then begin
- FPasHighlighter.StartAtLineIndex(AIndex);
- while not FPasHighlighter.GetEol do begin
- FCacheLastHlTokenKind := TtkTokenKind(FPasHighlighter.GetTokenKind);
- FCacheLastHlTokenCol := FPasHighlighter.GetTokenPos + 1;
- FPasHighlighter.Next;
- end;
- end;
- Result := FCacheLastHlTokenKind;
- APos := FCacheLastHlTokenCol;
- end;
- function TSynBeautifierPascal.GetCommentStartColForIdx(AIndex: Integer): Integer;
- var
- nl: TLazSynFoldNodeInfoList;
- i: Integer;
- FoldCommentLvl: Integer;
- begin
- Result := FCacheCommentStartCol;
- if AIndex = FCacheCommentStartColForIdx then
- exit;
- FCacheCommentStartColForIdx := AIndex;
- if (GetFoldEndLevelForIdx(AIndex) = 0) or
- not(GetFoldTypeAtEndOfLineForIdx(AIndex) in [cfbtAnsiComment, cfbtBorCommand, cfbtSlashComment])
- then begin
- // must be SlashComment
- FCacheCommentStartCol := GetSlashStartColumnForIdx(AIndex - 1);
- //FCacheCommentStartCol := GetSlashStartColumnForIdx(AIndex);
- //if FCacheCommentStartCol > 0 then begin
- // i := GetSlashStartColumnForIdx(AIndex - 1);
- // if i > 0 then
- // FCacheCommentStartCol := i;
- //end;
- Result := FCacheCommentStartCol;
- //debugln(['FIRST COL prev-// ', FCacheCommentStartCol]);
- exit;
- end;
- FCacheCommentStartCol := -1;
- FoldCommentLvl := GetFoldCommentLevelForIdx(AIndex);
- nl := FPasHighlighter.FoldNodeInfo[GetFirstCommentLineForIdx(AIndex) - 1];
- nl.AddReference;
- nl.ClearFilter;
- nl.ActionFilter := [sfaOpen];
- nl.GroupFilter := FOLDGROUP_PASCAL;
- i := nl.Count - 1;
- while i >= 0 do begin
- if (not (sfaInvalid in nl[i].FoldAction)) and
- (nl[i].NestLvlEnd = FoldCommentLvl)
- then begin
- FCacheCommentStartCol := nl[i].LogXStart + 1; // Highlighter pos are 0 based
- break;
- end;
- dec(i);
- end;
- nl.ReleaseReference;
- //debugln(['FIRST COL ', FCacheCommentStartCol]);
- Result := FCacheCommentStartCol;
- end;
- function TSynBeautifierPascal.GetFoldEndLevel: Integer;
- begin
- Result := FCacheWorkFoldEndLvl;
- if FCacheWorkFoldEndLvl > -2 then
- exit;
- FCacheWorkFoldEndLvl := GetFoldEndLevelForIdx(FWorkLine-1);
- Result := FCacheWorkFoldEndLvl;
- end;
- function TSynBeautifierPascal.GetFoldCommentLevel: Integer;
- begin
- Result := FCacheWorkCommentLvl;
- if FCacheWorkCommentLvl > -2 then
- exit;
- FCacheWorkCommentLvl := GetFoldCommentLevelForIdx(FWorkLine-1);
- Result := FCacheWorkCommentLvl;
- end;
- function TSynBeautifierPascal.GetFoldTypeAtEndOfLine: TPascalCodeFoldBlockType;
- begin
- Result := FCacheWorkFoldType;
- if FCacheWorkFoldType <> cfbtIfThen then
- exit;
- FCacheWorkFoldType := GetFoldTypeAtEndOfLineForIdx(FWorkLine-1);
- Result := FCacheWorkFoldType;
- end;
- function TSynBeautifierPascal.GetFirstCommentLine: Integer;
- begin
- Result := FCacheWorkFirstLine;
- if FCacheWorkFirstLine > -2 then
- exit;
- FCacheWorkFirstLine := GetFirstCommentLineForIdx(FWorkLine-1);
- Result := FCacheWorkFirstLine;
- end;
- function TSynBeautifierPascal.GetSlashStartColumn: Integer;
- begin
- Result := FCacheWorkSlashCol;
- if FCacheWorkSlashCol > -2 then
- exit;
- FCacheWorkSlashCol := GetSlashStartColumnForIdx(FWorkLine-2);
- Result := FCacheWorkSlashCol;
- end;
- function TSynBeautifierPascal.GetCommentStartCol: Integer;
- begin
- Result := FCacheWorkCommentStartCol;
- if FCacheWorkCommentStartCol > -2 then
- exit;
- FCacheWorkCommentStartCol := GetCommentStartColForIdx(FWorkLine-1);
- Result := FCacheWorkCommentStartCol;
- end;
- function TSynBeautifierPascal.GetMatchStartColForIdx(AIndex: Integer): Integer;
- begin
- if ToPos(AIndex) = GetFirstCommentLine then begin
- // Match on FirstLine
- case FMatchMode[FWorkFoldType] of
- scmMatchAfterOpening: begin
- if FWorkFoldType = sctBor
- then Result := GetCommentStartCol + 1
- else Result := GetCommentStartCol + 2;
- end;
- scmMatchOpening: Result := GetCommentStartCol;
- scmMatchWholeLine: Result := 1;
- scmMatchAtAsterisk: Result := GetCommentStartCol + 1;
- end;
- end
- else begin
- // Match on prev, not first
- case FMatchMode[FWorkFoldType] of
- scmMatchAfterOpening, scmMatchOpening, scmMatchAtAsterisk:
- Result := 1 + GetIndentForLine(nil, FCurrentLines[AIndex], False);
- scmMatchWholeLine:
- Result := 1;
- end;
- end;
- end;
- function TSynBeautifierPascal.GetMatchStartPos(AIndex: Integer; AForceFirst: Boolean): TPoint;
- begin
- if AIndex < 0 then
- AIndex := ToIdx(FWorkLine);
- if AForceFirst then
- Result.Y := GetFirstCommentLine
- else
- case FMatchLine[FWorkFoldType] of
- sclMatchFirst: Result.Y := GetFirstCommentLine;
- sclMatchPrev: Result.Y := ToPos(AIndex)-1; // FWorkLine - 1
- end;
- Result.X := GetMatchStartColForIdx(ToIdx(Result.Y));
- end;
- function TSynBeautifierPascal.GetLine(AnIndex: Integer): String;
- var
- ReplacedPrefix: String;
- Indent, PreFixPos: Integer;
- r: Boolean;
- p: TPoint;
- begin
- if not FGetLineAfterComment then begin
- Result := inherited GetLine(AnIndex);
- exit;
- end;
- // Need to cut of existing Prefix to find indent after prefix
- if AnIndex < GetFirstCommentLine-1 then begin
- Result := '';
- //debugln(['GETLINE FROM (< First) ', AnIndex,' ''', FCurrentLines[AnIndex], ''' to empty']);
- end;
- // 1) Run the match for this line (match prev, or first)
- // and see if we can find the replaced prefix in this line
- if AnIndex > GetFirstCommentLine-1 then begin
- p := GetMatchStartPos(AnIndex);
- FRegExprEngine.InputString:= copy(FCurrentLines[ToIdx(p.y)], p.x, MaxInt);
- FRegExprEngine.Expression := FMatch[FWorkFoldType];
- if FRegExprEngine.ExecPos(1) then begin
- ReplacedPrefix := FRegExprEngine.Substitute(FPrefix[FWorkFoldType]);
- while (ReplacedPrefix <> '') and (ReplacedPrefix[1] in [#9, ' ']) do
- delete(ReplacedPrefix, 1, 1);
- while (ReplacedPrefix <> '') and (ReplacedPrefix[length(ReplacedPrefix)] in [#9, ' ']) do
- delete(ReplacedPrefix, length(ReplacedPrefix), 1);
- if ReplacedPrefix <> '' then begin
- Result := FCurrentLines[AnIndex];
- PreFixPos := pos(ReplacedPrefix, Result);
- if (PreFixPos > 0) and (PreFixPos <= GetMatchStartColForIdx(AnIndex)) then begin
- delete(Result, 1, PreFixPos - 1 + Length(ReplacedPrefix));
- //debugln(['GETLINE FROM (1) ', AnIndex,' ''', FCurrentLines[AnIndex], ''' to ''', Result, '''']);
- exit;
- end;
- end;
- end;
- end;
- // 2) See, if the current-1 line can be matched
- r := False;
- Result := FCurrentLines[AnIndex];
- Indent := GetMatchStartColForIdx(AnIndex);
- FRegExprEngine.InputString:= copy(FCurrentLines[AnIndex], Indent, MaxInt);
- FRegExprEngine.Expression := FMatch[FWorkFoldType];
- r := FRegExprEngine.ExecPos(1);
- if (not r) and (Indent > 1) and
- ((p.y <> GetFirstCommentLine) or (FMatchMode[FWorkFoldType] = scmMatchWholeLine))
- then begin
- // try whole line
- // TODO: only if not first, or if setting
- FRegExprEngine.InputString := FCurrentLines[AnIndex];
- r := FRegExprEngine.ExecPos(1);
- if r then
- Indent := 1;
- end;
- if (r) then begin
- Indent := Indent + FRegExprEngine.MatchPos[0] - 1 + FRegExprEngine.MatchLen[0];
- Result := FCurrentLines[AnIndex];
- if (Indent <= Length(Result)) then
- while (Indent > 1) and (Result[Indent] in [#9, ' ']) do
- dec(Indent);
- inc(Indent);
- if Indent > 0 then begin
- Result := copy(Result, Indent, MaxInt);
- //debugln(['GETLINE FROM (2) ', AnIndex,' ''', FCurrentLines[AnIndex], ''' to ''', Result, '''']);
- exit;
- end;
- end;
- // 3) maybe try currest replace, if different from 1?
- // Nothing found
- Result := '';
- //debugln(['GETLINE FROM (X) ', AnIndex,' ''', FCurrentLines[AnIndex], ''' to empty']);
- end;
- procedure TSynBeautifierPascal.DoBeforeCommand(const ACaret: TSynEditCaret;
- var Command: TSynEditorCommand);
- begin
- FCaretAtEOL := ACaret.BytePos > Length(FCurrentLines[ToIdx(ACaret.LinePos)]);
- FGetLineAfterComment := False;
- inherited DoBeforeCommand(ACaret, Command);
- end;
- procedure TSynBeautifierPascal.DoAfterCommand(const ACaret: TSynEditCaret;
- var Command: TSynEditorCommand; StartLinePos, EndLinePos: Integer);
- var
- WorkLine, PrevLineShlasCol: Integer;
- ReplacedPrefix: String; // Each run matches only one Type
- MatchResultIntern, MatchedBOLIntern: Array [Boolean] of Boolean; // Each run matches only one Type
- function CheckMatch(AType: TSynCommentType; AFailOnNoPattern: Boolean = False;
- AForceFirst: Boolean = False): Boolean;
- var
- p: TPoint;
- begin
- if (FMatch[AType] = '') and AFailOnNoPattern then begin
- Result := False;
- exit;
- end;
- if MatchedBOLIntern[AForceFirst] then begin
- Result := MatchResultIntern[AForceFirst];
- exit;
- end;
- p := GetMatchStartPos(-1, AForceFirst);
- FRegExprEngine.InputString:= copy(FCurrentLines[ToIdx(p.y)], p.x, MaxInt);
- FRegExprEngine.Expression := FMatch[AType];
- if not FRegExprEngine.ExecPos(1) then begin
- ReplacedPrefix := FRegExprEngine.Substitute(FPrefix[AType]);
- MatchedBOLIntern[AForceFirst] := True;
- MatchResultIntern[AForceFirst] := False;
- Result := MatchResultIntern[AForceFirst];
- exit;
- end;
- ReplacedPrefix := FRegExprEngine.Substitute(FPrefix[AType]);
- MatchedBOLIntern[AForceFirst] := True;
- MatchResultIntern[AForceFirst] := True;
- Result := MatchResultIntern[AForceFirst];
- end;
- function IsFoldTypeEnabled(AType: TSynCommentType): Boolean;
- begin
- Result := ( ( (AType <> sctSlash) or
- ( ((not FCaretAtEOL) and (FExtendSlashCommentMode <> sceNever)) or
- ((FCaretAtEOL) and not(FExtendSlashCommentMode in [sceNever, sceSplitLine, sceMatchingSplitLine]))
- )
- ) and
- ( (FIndentMode[AType] <> []) or
- (FCommentMode[AType] <> sccNoPrefix) or
- (FEolMode[AType] <> sccNoPrefix)
- ))
- end;
- var
- Indent, dummy: Integer;
- s: String;
- FoldTyp: TSynCommentType;
- AnyEnabled: Boolean;
- ExtendSlash, BeSmart, Matching: Boolean;
- PreviousIsFirst, IsAtBOL, DidAlignOpen: Boolean;
- IndentTypeBackup: TSynBeautifierIndentType;
- begin
- if (EndLinePos < 1) or
- ((Command <> ecLineBreak) and (Command <> ecInsertLine)) or
- (not IsPasHighlighter)
- then begin
- inherited DoAfterCommand(ACaret, Command, StartLinePos, EndLinePos);
- exit;
- end;
- AnyEnabled := False;
- for FoldTyp := low(TSynCommentType) to high(TSynCommentType) do
- AnyEnabled := AnyEnabled or IsFoldTypeEnabled(FoldTyp);
- if (not AnyEnabled) and (not FStringBreakEnabled) then begin
- inherited DoAfterCommand(ACaret, Command, StartLinePos, EndLinePos);
- exit;
- end;
- InitCache;
- InitPasHighlighter;
- FGetLineAfterComment := False;
- MatchedBOLIntern[True] := False;
- MatchedBOLIntern[False] := False;
- PrevLineShlasCol := -1;
- dummy := 0;
- if (Command = ecLineBreak)
- then WorkLine := ACaret.LinePos
- else WorkLine := ACaret.LinePos + 1;
- FWorkLine := WorkLine;
- // Find Foldtype
- case GetFoldTypeAtEndOfLine of
- cfbtAnsiComment: FoldTyp := sctAnsi;
- cfbtBorCommand: FoldTyp := sctBor;
- cfbtSlashComment: FoldTyp := sctSlash;
- else
- begin
- if (FCurrentLines[ToIdx(WorkLine)-1] <> '') and
- (FExtendSlashCommentMode <> sceNever) and
- ( (not FCaretAtEOL) or not(FExtendSlashCommentMode in [sceSplitLine, sceMatchingSplitLine]) )
- then begin
- PrevLineShlasCol := GetSlashStartColumn;
- if PrevLineShlasCol > 0 then
- FoldTyp := sctSlash;
- end;
- if PrevLineShlasCol < 0 then begin
- if (FCurrentLines[ToIdx(WorkLine)-1] <> '') and
- (GetLastHlTokenForIdx(ToIdx(WorkLine)-1, dummy) = SynHighlighterPas.tkString)
- then
- DoNewLineInString(WorkLine - 1, dummy, ACaret, Command, StartLinePos, EndLinePos)
- else
- inherited DoAfterCommand(ACaret, Command, StartLinePos, EndLinePos);
- exit;
- end;
- end;
- end;
- if not IsFoldTypeEnabled(FoldTyp) then begin
- inherited DoAfterCommand(ACaret, Command, StartLinePos, EndLinePos);
- exit;
- end;
- FWorkFoldType := FoldTyp;
- // Check if we need extend
- ExtendSlash := False;
- if (FoldTyp = sctSlash) and (ACaret.OldLineBytePos.x > GetSlashStartColumn+2) then begin
- // Check if extension is needed
- case FExtendSlashCommentMode of
- sceAlways: ExtendSlash := True;
- sceSplitLine: ExtendSlash := not FCaretAtEOL;
- sceMatching: ExtendSlash := CheckMatch(FoldTyp);
- sceMatchingSplitLine: ExtendSlash := CheckMatch(FoldTyp) and (not FCaretAtEOL);
- end;
- if not ExtendSlash then begin
- inherited DoAfterCommand(ACaret, Command, StartLinePos, EndLinePos);
- exit;
- end;
- end;
- // Indent
- Matching := CheckMatch(FoldTyp);
- PreviousIsFirst := (GetFirstCommentLine = WorkLine -1);
- DidAlignOpen := False;
- IsAtBOL := True;
- if PreviousIsFirst then
- IsAtBOL := GetCommentStartCol = 1 + GetIndentForLine(nil, FCurrentLines[ToIdx(GetFirstCommentLine)], False);
- // Aply indent before prefix
- if Matching or (FCommentMode[FoldTyp] = sccPrefixAlways) or (sciApplyIndentForNoMatch in FIndentMode[FoldTyp])
- then begin
- IndentTypeBackup := IndentType;
- try
- if IndentType = sbitPositionCaret then
- IndentType := sbitSpace;
- if IndentType = sbitConvertToTabOnly then
- IndentType := sbitConvertToTabSpace;
- if PreviousIsFirst and not IsAtBOL and
- (FIndentFirstLineMax[FoldTyp] > 0) and
- ( (FIndentFirstLineMax[FoldTyp] + 1 >= GetCommentStartCol) or
- (FIndentMode[FoldTyp] * [sciNone, sciAlignOpen] = [sciAlignOpen])
- )
- then begin
- // Use sciAlignOpen
- Indent := Min(
- FCurrentLines.LogicalToPhysicalCol(FCurrentLines[ToIdx(GetFirstCommentLine)], ToIdx(GetFirstCommentLine), GetCommentStartCol-1),
- FIndentFirstLineMax[FoldTyp]);
- s := GetCharMix(WorkLine, Indent, dummy);
- FLogicalIndentLen := length(s);
- FCurrentLines.EditInsert(1, WorkLine, s);
- DidAlignOpen := True;
- end
- else
- if (FIndentMode[FoldTyp] * [sciNone, sciAlignOpen] = [sciAlignOpen]) and
- (GetCommentStartCol > 0)
- then begin
- Indent := FCurrentLines.LogicalToPhysicalCol(FCurrentLines[ToIdx(GetFirstCommentLine)], ToIdx(GetFirstCommentLine), GetCommentStartCol-1);
- if FIndentFirstLineMax[FoldTyp] > 0
- then Indent := Min(Indent, FIndentFirstLineMax[FoldTyp]);
- s := GetCharMix(WorkLine, Indent, dummy);
- FLogicalIndentLen := length(s);
- FCurrentLines.EditInsert(1, WorkLine, s);
- DidAlignOpen := True;
- end
- else
- if (sciNone in FIndentMode[FoldTyp]) then begin
- // No indent
- end
- else
- begin
- inherited DoAfterCommand(ACaret, Command, StartLinePos, EndLinePos);
- end;
- finally
- IndentType := IndentTypeBackup;
- end;
- // AnsiIndentFirstLineExtra
- if PreviousIsFirst and (not IsAtBOL) and (not DidAlignOpen) then begin
- FCurrentLines.EditInsert(1 + FLogicalIndentLen, WorkLine, FIndentFirstLineExtra[FoldTyp]);
- FLogicalIndentLen := FLogicalIndentLen + length(FIndentFirstLineExtra[FoldTyp]);
- end;
- end
- else
- inherited DoAfterCommand(ACaret, Command, StartLinePos, EndLinePos);
- Indent := 0; // Extra Indent
- BeSmart := (PreviousIsFirst or (sciAlignOpen in FIndentMode[FoldTyp])) and
- (Matching or ExtendSlash or (sciApplyIndentForNoMatch in FIndentMode[FoldTyp]) or
- (FCommentMode[FoldTyp] = sccPrefixAlways) );
- // sciAddTokenLen -- Spaces for (* or {
- if BeSmart and
- ( (sciAddTokenLen in FIndentMode[FoldTyp]) and
- ( (not(sciMatchOnlyTokenLen in FIndentMode[FoldTyp])) or CheckMatch(FoldTyp, False, True) ) and
- ( (not(sciAlignOnlyTokenLen in FIndentMode[FoldTyp])) or DidAlignOpen )
- )
- then begin
- case FoldTyp of
- sctAnsi: if FMatchMode[FoldTyp] = scmMatchAtAsterisk
- then Indent := 1
- else Indent := 2;
- sctBor: Indent := 1;
- sctSlash: if ExtendSlash
- then Indent := 0 // do the slashes
- else Indent := 2;
- end;
- end;
- // sciAddPastTokenIndent -- Spaces from after (* or { (to go befare prefix e.g " { * foo")
- if BeSmart and
- ( (sciAddPastTokenIndent in FIndentMode[FoldTyp]) and
- ( (not(sciMatchOnlyPastTokenIndent in FIndentMode[FoldTyp])) or CheckMatch(FoldTyp, False, True) ) and
- ( (not(sciAlignOnlyPastTokenIndent in FIndentMode[FoldTyp])) or DidAlignOpen )
- ) and
- (GetCommentStartCol > 0) // foundStartCol
- then begin
- case FoldTyp of
- // ignores scmMatchAtAsterisk
- sctAnsi: s := copy(FCurrentLines[ToIdx(GetFirstCommentLine)], GetCommentStartCol+2, MaxInt);
- sctBor: s := copy(FCurrentLines[ToIdx(GetFirstCommentLine)], GetCommentStartCol+1, MaxInt);
- sctSlash: s := copy(FCurrentLines[ToIdx(GetFirstCommentLine)], GetCommentStartCol+2, MaxInt);
- end;
- Indent := Indent + GetIndentForLine(nil, s, False);
- end;
- // Extend //
- if ExtendSlash then begin
- FCurrentLines.EditInsert(1 + FLogicalIndentLen, WorkLine, '//');
- FLogicalIndentLen := FLogicalIndentLen + 2;
- end;
- if (Indent > 0) then begin
- FCurrentLines.EditInsert(1 + FLogicalIndentLen, WorkLine, StringOfChar(' ', Indent ));
- FLogicalIndentLen := FLogicalIndentLen + Indent;
- end;
- // Apply prefix
- if (FCommentMode[FoldTyp] = sccPrefixAlways) or
- ((FCommentMode[FoldTyp] = sccPrefixMatch) and Matching)
- then begin
- FCurrentLines.EditInsert(1 + FLogicalIndentLen, WorkLine, ReplacedPrefix);
- FLogicalIndentLen := FLogicalIndentLen + length(ReplacedPrefix);
- // Post prefix indent
- FGetLineAfterComment := True;
- try
- GetIndentInfo(WorkLine, s, Indent, GetFirstCommentLine);
- if s <> '' then begin
- FCurrentLines.EditInsert(1 + FLogicalIndentLen, WorkLine, s);
- FLogicalIndentLen := FLogicalIndentLen + length(s); // logical (Indent is phisical)
- end
- else
- FLogicalIndentLen := FLogicalIndentLen + Indent; // maybe position caret
- finally
- FGetLineAfterComment := False;
- end;
- end;
- if (Command = ecLineBreak) then begin
- ACaret.IncForcePastEOL;
- ACaret.BytePos := 1 + FLogicalIndentLen;
- ACaret.DecForcePastEOL;
- end;
- end;
- procedure TSynBeautifierPascal.DoNewLineInString(AStringStartY, AStringStartX: Integer;
- const ACaret: TSynEditCaret; var Command: TSynEditorCommand; StartLinePos,
- EndLinePos: Integer);
- var
- s: String;
- WorkLine: Integer;
- f: Boolean;
- begin
- inherited DoAfterCommand(ACaret, Command, StartLinePos, EndLinePos);
- if not FStringBreakEnabled then
- exit;
- if (Command = ecLineBreak)
- then WorkLine := ACaret.LinePos - 1
- else WorkLine := ACaret.LinePos;
- s := FCurrentLines[ToIdx(WorkLine)];
- if (AStringStartX < 1) or (AStringStartX > Length(s)) or (s[AStringStartX] <> '''') then
- exit;
- f := False;
- while AStringStartX <= length(s) do begin
- if (s[AStringStartX] = '''') then f := not f;
- inc(AStringStartX);
- end;
- if not f then exit;
- ACaret.IncForcePastEOL;
- FCurrentLines.EditInsert(1 + length(s), WorkLine, '''' + FStringBreakAppend);
- //if Command = ecInsertLine then
- // ACaret.BytePos := ACaret.BytePos + Length(FStringBreakAppend) + 1;
- FCurrentLines.EditInsert(1 + FLogicalIndentLen, WorkLine + 1, FStringBreakPrefix + '''');
- if (Command = ecLineBreak) then
- ACaret.BytePos := ACaret.BytePos + Length(FStringBreakPrefix) + 1;
- ACaret.DecForcePastEOL;
- end;
- constructor TSynBeautifierPascal.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- FRegExprEngine := TRegExpr.Create;
- FRegExprEngine.ModifierI := True;
- FIndentMode[sctAnsi] := [sciAddTokenLen, sciAddPastTokenIndent];
- FIndentFirstLineExtra[sctAnsi] := '';
- FIndentFirstLineMax[sctAnsi] := 0;
- FCommentMode[sctAnsi] := sccPrefixMatch;
- FMatch[sctAnsi] := '^ ?(\*)';
- FMatchMode[sctAnsi] := scmMatchAfterOpening;
- FMatchLine[sctAnsi] := sclMatchPrev;
- FPrefix[sctAnsi] := '$1';
- FEolMatch[sctAnsi] := '';
- FEolMode[sctAnsi] := sccNoPrefix;
- FEolPostfix[sctAnsi] := '';
- FEolSkipLongerLine[sctAnsi] := False;
- FIndentMode[sctBor] := [sciAddTokenLen, sciAddPastTokenIndent];
- FIndentFirstLineExtra[sctBor] := '';
- FIndentFirstLineMax[sctBor] := 0;
- FCommentMode[sctBor] := sccPrefixMatch;
- FMatch[sctBor] := '^ ?(\*)';
- FMatchMode[sctBor] := scmMatchAfterOpening;
- FMatchLine[sctBor] := sclMatchPrev;
- FPrefix[sctBor] := '$1';
- FEolMatch[sctBor] := '';
- FEolMode[sctBor] := sccNoPrefix;
- FEolPostfix[sctBor] := '';
- FEolSkipLongerLine[sctBor] := False;
- FExtendSlashCommentMode := sceNever;
- FIndentMode[sctSlash] := [];
- FIndentFirstLineExtra[sctSlash] := '';
- FIndentFirstLineMax[sctSlash] := 0;
- FCommentMode[sctSlash] := sccPrefixMatch;
- FMatch[sctSlash] := '^ ?(\*)';
- FMatchMode[sctSlash] := scmMatchAfterOpening;
- FMatchLine[sctSlash] := sclMatchPrev;
- FPrefix[sctSlash] := '$1';
- FEolMatch[sctSlash] := '';
- FEolMode[sctSlash] := sccNoPrefix;
- FEolPostfix[sctSlash] := '';
- FEolSkipLongerLine[sctSlash] := False;
- FStringBreakEnabled := False;
- FStringBreakAppend := ' +';
- FStringBreakPrefix := '';
- end;
- destructor TSynBeautifierPascal.Destroy;
- begin
- inherited Destroy;
- FreeAndNil(FRegExprEngine);
- end;
- procedure TSynBeautifierPascal.Assign(Src: TPersistent);
- var
- i: TSynCommentType;
- begin
- inherited Assign(Src);
- if not(Src is TSynBeautifierPascal) then exit;
- FExtendSlashCommentMode := TSynBeautifierPascal(Src).FExtendSlashCommentMode;
- for i := low(TSynCommentType) to high(TSynCommentType) do begin
- FIndentMode[i] := TSynBeautifierPascal(Src).FIndentMode[i];
- FIndentFirstLineExtra[i] := TSynBeautifierPascal(Src).FIndentFirstLineExtra[i];
- FIndentFirstLineMax[i] := TSynBeautifierPascal(Src).FIndentFirstLineMax[i];
- FCommentMode[i] := TSynBeautifierPascal(Src).FCommentMode[i];
- FMatch[i] := TSynBeautifierPascal(Src).FMatch[i];
- FMatchMode[i] := TSynBeautifierPascal(Src).FMatchMode[i];
- FMatchLine[i] := TSynBeautifierPascal(Src).FMatchLine[i];
- FPrefix[i] := TSynBeautifierPascal(Src).FPrefix[i];
- FEolMatch[i] := TSynBeautifierPascal(Src).FEolMatch[i];
- FEolMode[i] := TSynBeautifierPascal(Src).FEolMode[i];
- FEolPostfix[i] := TSynBeautifierPascal(Src).FEolPostfix[i];
- FEolSkipLongerLine[i] := TSynBeautifierPascal(Src).FEolSkipLongerLine[i];
- end;
- FStringBreakAppend := TSynBeautifierPascal(Src).FStringBreakAppend;
- FStringBreakEnabled := TSynBeautifierPascal(Src).FStringBreakEnabled;
- FStringBreakPrefix := TSynBeautifierPascal(Src).FStringBreakPrefix;
- end;
- { TSynCustomBeautifier }
- procedure TSynCustomBeautifier.Assign(Src: TPersistent);
- begin
- if assigned(Src) and (src is TSynCustomBeautifier) then begin
- FCurrentEditor := TSynCustomBeautifier(Src).FCurrentEditor;
- FCurrentLines := TSynCustomBeautifier(Src).FCurrentLines;
- FOnGetDesiredIndent := TSynCustomBeautifier(Src).FOnGetDesiredIndent;
- FAutoIndent := TSynCustomBeautifier(Src).FAutoIndent;
- end
- else
- inherited;
- end;
- function TSynCustomBeautifier.GetCopy: TSynCustomBeautifier;
- begin
- // Since all synedits share one beautifier, create a temp instance.
- // Todo: have individual beautifiers
- Result := TSynCustomBeautifierClass(self.ClassType).Create(nil);
- Result.assign(self);
- end;
- procedure TSynCustomBeautifier.BeforeCommand(const Editor: TSynEditBase;
- const Lines: TSynEditStrings; const ACaret: TSynEditCaret;
- var Command: TSynEditorCommand; InitialCmd: TSynEditorCommand);
- begin
- // Must be called on GetCopy
- // Todo: have individual beautifiers
- FCurrentEditor := Editor;
- FCurrentLines := Lines;
- DoBeforeCommand(ACaret, Command);
- end;
- procedure TSynCustomBeautifier.AfterCommand(const Editor: TSynEditBase;
- const Lines: TSynEditStrings; const ACaret: TSynEditCaret;
- var Command: TSynEditorCommand; InitialCmd: TSynEditorCommand;
- StartLinePos, EndLinePos: Integer);
- begin
- // Must be called on GetCopy
- // Todo: have individual beautifiers
- FCurrentEditor := Editor;
- FCurrentLines := Lines;
- DoAfterCommand(ACaret, Command, StartLinePos, EndLinePos);
- end;
- { TSynBeautifier }
- procedure TSynBeautifier.Assign(Src: TPersistent);
- begin
- inherited Assign(Src);
- if assigned(Src) and (src is TSynBeautifier) then begin
- FIndentType := TSynBeautifier(Src).FIndentType;
- FCurrentEditor := TSynBeautifier(Src).FCurrentEditor;
- FCurrentLines := TSynBeautifier(Src).FCurrentLines;
- end;
- end;
- function TSynBeautifier.GetLine(AnIndex: Integer): String;
- begin
- Result := FCurrentLines[AnIndex];
- end;
- procedure TSynBeautifier.GetIndentInfo(const LinePos: Integer; out ACharMix: String; out
- AnIndent: Integer; AStopScanAtLine: Integer);
- var
- b: Integer;
- begin
- ACharMix := '';
- if (GetLine(LinePos-2) = '') and (GetLine(LinePos-1) <> '') then
- AnIndent := 0
- else
- AnIndent := GetIndent(LinePos, b, AStopScanAtLine);
- if AnIndent > 0 then begin
- ACharMix := GetCharMix(LinePos, AnIndent, b, True);
- if (FIndentType = sbitPositionCaret) and (GetLine(LinePos-1) = '') then
- ACharMix := '';
- end;
- end;
- procedure TSynBeautifier.DoBeforeCommand(const ACaret: TSynEditCaret;
- var Command: TSynEditorCommand);
- var
- x: Integer;
- begin
- if (Command = ecDeleteLastChar) and
- (FAutoIndent) and
- (ACaret.CharPos > 1) and
- (not TCustomSynEdit(FCurrentEditor).ReadOnly) and
- ( (not TCustomSynEdit(FCurrentEditor).SelAvail) or
- (eoPersistentBlock in TCustomSynEdit(FCurrentEditor).Options2) ) and
- (GetIndentForLine(FCurrentEditor, ACaret.LineText, True) = ACaret.CharPos - 1)
- then begin
- FCurrentLines.UndoList.CurrentReason := ecSmartUnindent;
- UnIndentLine(ACaret, x);
- ACaret.CharPos := x;
- Command := ecNone;
- end;
- end;
- procedure TSynBeautifier.DoAfterCommand(const ACaret: TSynEditCaret;
- var Command: TSynEditorCommand; StartLinePos, EndLinePos: Integer);
- var
- y, Indent: Integer;
- s: String;
- begin
- FLogicalIndentLen := 0;
- if EndLinePos < 1 then
- exit;
- if assigned(FOnGetDesiredIndent) and
- FOnGetDesiredIndent(self, FCurrentEditor, ACaret.LineBytePos,
- ACaret.OldLineBytePos, StartLinePos, EndLinePos, Command,
- @ApplyIndent)
- then
- exit;
- if ((Command = ecLineBreak) or (Command = ecInsertLine)) and FAutoIndent then begin
- if (Command = ecLineBreak) then
- y := ACaret.LinePos
- else
- y := ACaret.LinePos + 1;
- GetIndentInfo(y, s, Indent);
- if s <> '' then begin;
- FCurrentLines.EditInsert(1, y, s);
- FLogicalIndentLen := length(s);
- end;
- if (Command = ecLineBreak) then begin
- ACaret.IncForcePastEOL;
- ACaret.CharPos := Indent + 1;
- ACaret.DecForcePastEOL;
- end;
- end;
- end;
- function TSynBeautifier.UnIndentLine(const ACaret: TSynEditCaret;
- out CaretNewX: Integer): Boolean;
- var
- SpaceCount1, SpaceCount2: Integer;
- BackCounter, LogSpacePos, FillSpace: Integer;
- LogCaret: TPoint;
- Line, Temp: String;
- begin
- Line := ACaret.LineText;
- SpaceCount1 := GetIndentForLine(FCurrentEditor, Line, true); // physical, desired pos
- SpaceCount2 := 0;
- if (SpaceCount1 > 0) then begin
- BackCounter := ACaret.LinePos - 2;
- while BackCounter >= 0 do begin
- Temp := FCurrentLines[BackCounter];
- SpaceCount2 := GetIndentForLine(FCurrentEditor, Temp, true);
- if (SpaceCount2 < SpaceCount1) and (temp <> '') then
- break;
- Dec(BackCounter);
- end;
- end;
- if SpaceCount2 >= SpaceCount1 then
- SpaceCount2 := 0;
- // remove visible spaces
- LogSpacePos := FCurrentLines.PhysicalToLogicalCol(Line, ACaret.LinePos-1, SpaceCount2 + 1);
- FillSpace := SpaceCount2 + 1 - FCurrentLines.LogicalToPhysicalCol(Line, ACaret.LinePos-1, LogSpacePos);
- LogCaret := ACaret.LineBytePos;
- CaretNewX := SpaceCount2 + 1;
- FCurrentLines.EditDelete(LogSpacePos, ACaret.LinePos, LogCaret.X - LogSpacePos);
- if FillSpace > 0 then
- FCurrentLines.EditInsert(LogSpacePos, ACaret.LinePos, StringOfChar(' ', FillSpace));
- Result := True;
- end;
- function TSynBeautifier.GetIndent(const LinePos: Integer; out BasedOnLine: Integer;
- AStopScanAtLine: Integer): Integer;
- var
- Temp: string;
- begin
- if AStopScanAtLine > 0 then
- dec(AStopScanAtLine); // Convert to index
- BasedOnLine := LinePos - 1; // Convert to index
- while (BasedOnLine > AStopScanAtLine) do begin
- dec(BasedOnLine);
- Temp := GetLine(BasedOnLine);
- if Temp <> '' then begin
- Result := GetIndentForLine(FCurrentEditor, Temp, True);
- exit;
- end;
- end;
- Result := 0;
- //BasedOnLine := LinePos;
- //Result := GetIndentForLine(FCurrentEditor, GetLine(BasedOnLine), True);
- end;
- function TSynBeautifier.AdjustCharMix(DesiredIndent: Integer; CharMix, AppendMix: String): String;
- var
- i: Integer;
- CurLen: Integer;
- begin
- CurLen := FCurrentLines.LogicalToPhysicalCol(CharMix, -1, length(CharMix)+1) - 1; // TODO: Need the real index of the line
- if AppendMix <> '' then begin
- while CurLen < DesiredIndent do begin
- CharMix := CharMix + AppendMix;
- CurLen := FCurrentLines.LogicalToPhysicalCol(CharMix, -1, length(CharMix)+1) - 1; // TODO: Need the real index of the line
- end
- end;
- i := length(CharMix);
- while CurLen > DesiredIndent do begin
- Dec(i);
- CurLen := FCurrentLines.LogicalToPhysicalCol(CharMix, -1, i+1) - 1; // TODO: Need the real index of the line
- end;
- CharMix := copy(CharMix, 1, i) + StringOfChar(' ', DesiredIndent - CurLen);
- Result := CharMix;
- end;
- function TSynBeautifier.CreateTabSpaceMix(var DesiredIndent: Integer;
- OnlyTabs: Boolean): String;
- var
- CurLen: Integer;
- begin
- CurLen := 0;
- Result := '';
- while CurLen < DesiredIndent do begin
- Result := Result + #9;
- CurLen := FCurrentLines.LogicalToPhysicalCol(Result, -1, length(Result)+1) - 1; // TODO: Need the real index of the line
- end;
- if CurLen = DesiredIndent then
- exit;
- Delete(Result, Length(Result), 1);
- if OnlyTabs then begin
- CurLen := FCurrentLines.LogicalToPhysicalCol(Result, -1, length(Result)+1) - 1; // TODO: Need the real index of the line
- DesiredIndent := CurLen;
- exit;
- end;
- repeat
- Result := Result + ' ';
- CurLen := FCurrentLines.LogicalToPhysicalCol(Result, -1, length(Result)+1) - 1; // TODO: Need the real index of the line
- until CurLen >= DesiredIndent;
- end;
- function TSynBeautifier.GetCharMix(const LinePos: Integer; var Indent: Integer;
- var IndentCharsFromLinePos: Integer; ModifyIndent: Boolean): String;
- var
- Temp, KnownMix, BasedMix: string;
- KnownPhysLen, PhysLen: Integer;
- BackCounter: LongInt;
- OrigIndent: Integer;
- begin
- OrigIndent := Indent;
- case FIndentType of
- sbitSpace, sbitPositionCaret:
- begin
- IndentCharsFromLinePos := 0;
- Result := StringOfChar(' ', Indent);
- if not ModifyIndent then Indent := OrigIndent;
- exit;
- end;
- sbitConvertToTabSpace:
- begin
- IndentCharsFromLinePos := 0;
- Result := CreateTabSpaceMix(Indent, False);
- exit;
- if not ModifyIndent then Indent := OrigIndent;
- end;
- sbitConvertToTabOnly:
- begin
- IndentCharsFromLinePos := 0;
- Result := CreateTabSpaceMix(Indent, True);
- if not ModifyIndent then Indent := OrigIndent;
- exit;
- end;
- end;
- if (IndentCharsFromLinePos > 0) and (IndentCharsFromLinePos <= FCurrentLines.Count) then
- begin
- Temp := GetLine(IndentCharsFromLinePos);
- KnownMix := copy(Temp, 1, GetIndentForLine(FCurrentEditor, Temp, False));
- end
- else
- KnownMix := '';
- BasedMix := KnownMix;
- KnownPhysLen := GetIndentForLine(FCurrentEditor, KnownMix, True);
- BackCounter := LinePos;
- while (BackCounter > 0) and (KnownPhysLen < Indent) do begin
- dec(BackCounter);
- Temp := GetLine(BackCounter);
- if Temp <> '' then begin
- Temp := copy(Temp, 1, GetIndentForLine(FCurrentEditor, Temp, False));
- PhysLen := GetIndentForLine(FCurrentEditor, Temp, True);
- if (PhysLen > KnownPhysLen) and (copy(temp, 1, length(BasedMix)) = BasedMix) then
- begin
- KnownMix := Temp;
- KnownPhysLen := PhysLen;
- IndentCharsFromLinePos := BackCounter + 1;
- end;
- end;
- end;
- Result := AdjustCharMix(Indent, KnownMix, '');
- if not ModifyIndent then Indent := OrigIndent;
- end;
- procedure TSynBeautifier.ApplyIndent(LinePos: Integer;
- Indent: Integer; RelativeToLinePos: Integer; IndentChars: String = '';
- IndentCharsFromLinePos: Integer = 0);
- var
- CharMix: String;
- i: Integer;
- begin
- if (LinePos < 1) or (LinePos > FCurrentEditor.Lines.Count) then
- exit;
- // calculate the final indent needed
- if (RelativeToLinePos > 0) and (RelativeToLinePos <= FCurrentEditor.Lines.Count) then
- Indent := Indent + GetIndentForLine(FCurrentEditor, GetLine(RelativeToLinePos-1), True);
- if Indent< 0 then
- Indent := 0;
- // Calculate the charmix
- CharMix := '';
- if Indent > 0 then begin
- if (IndentCharsFromLinePos > 0) and (IndentCharsFromLinePos <= FCurrentEditor.Lines.Count) then begin
- CharMix := GetLine(IndentCharsFromLinePos-1);
- i := GetIndentForLine(FCurrentEditor, CharMix, False);
- CharMix := AdjustCharMix(Indent, copy(CharMix, 1, i), IndentChars);
- end
- else if IndentChars <> '' then begin
- CharMix := AdjustCharMix(Indent, '', IndentChars);
- end
- else begin
- i := LinePos;
- CharMix := GetCharMix(LinePos, Indent, i);
- end;
- end;
- {$IFDEF VerboseIndenter}
- DebugLn(['TSynBeautifier.ApplyIndent IndentChars="',dbgstr(IndentChars),' Indent=',Indent]);
- {$ENDIF}
- i := GetIndentForLine(FCurrentEditor, GetLine(LinePos-1), False);
- FCurrentLines.EditDelete(1, LinePos, i);
- if (CharMix <> '') and not((FIndentType = sbitPositionCaret) and (GetLine(LinePos-1) = '')) then
- FCurrentLines.EditInsert(1, LinePos, CharMix);
- FLogicalIndentLen := length(CharMix);
- {$IFDEF VerboseIndenter}
- DebugLn(['TSynBeautifier.ApplyIndent Line="',dbgstr(FCurrentLines.ExpandedStrings[LinePos-1]),'"']);
- {$ENDIF}
- end;
- function TSynBeautifier.GetIndentForLine(Editor: TSynEditBase; const Line: string; Physical: boolean): Integer;
- var
- p: PChar;
- begin
- p := PChar(Line);
- if Assigned(p) then begin
- Result := 0;
- while p^ in [#1..#32] do begin
- Inc(p);
- Inc(Result);
- end;
- if Physical and (Result>0) then
- Result := FCurrentLines.LogicalToPhysicalCol(Line, -1, Result+1)-1; // TODO, Need the real index of the line
- end else
- Result := 0;
- end;
- function TSynBeautifier.GetDesiredIndentForLine(Editor: TSynEditBase;
- const Lines: TSynEditStrings; const ACaret: TSynEditCaret;
- out ReplaceIndent: Boolean; out DesiredIndent: String): Integer;
- var
- BackCounter, PhysLen: Integer;
- Temp: string;
- FoundLine: LongInt;
- begin
- Result := 1;
- FCurrentLines := Lines; // for GetCurrentIndent
- BackCounter := ACaret.LinePos - 1;
- if BackCounter > 0 then
- repeat
- Dec(BackCounter);
- Temp := Lines[BackCounter];
- Result := GetIndentForLine(Editor, Temp, True) + 1; // Physical
- until (BackCounter = 0) or (Temp <> '');
- FoundLine := BackCounter + 1;
- ReplaceIndent := False;
- //if assigned(FOnGetDesiredIndent) then
- // FOnGetDesiredIndent(Editor, ACaret.LineBytePos, ACaret.LinePos, Result,
- // FoundLine, ReplaceIndent);
- //if Result < 0 then exit;
- //if FoundLine > 0 then
- // Temp := Lines[FoundLine-1]
- //else
- // FoundLine := BackCounter + 1;
- Temp := copy(Temp, 1, GetIndentForLine(Editor, Temp, False));
- case FIndentType of
- sbitCopySpaceTab:
- begin
- DesiredIndent := copy(Temp, 1,
- Lines.PhysicalToLogicalCol(Temp, FoundLine - 1, Result) - 1);
- PhysLen := Lines.LogicalToPhysicalCol(Temp, ACaret.LinePos - 1, Length(Temp) + 1);
- if PhysLen < Result then
- DesiredIndent := DesiredIndent + StringOfChar(' ', Result - PhysLen);
- end;
- sbitConvertToTabSpace:
- begin
- dec(Result);
- DesiredIndent := CreateTabSpaceMix(Result, False);
- inc(Result);
- end;
- sbitConvertToTabOnly:
- begin
- dec(Result);
- DesiredIndent := CreateTabSpaceMix(Result, True);
- inc(Result);
- end;
- else
- DesiredIndent := StringOfChar(' ', Result - 1);
- end;
- end;
- function TSynBeautifier.GetDesiredIndentForLine(Editor: TSynEditBase;
- const Lines: TSynEditStrings; const ACaret: TSynEditCaret): Integer;
- var
- Dummy: String;
- Replace: Boolean;
- begin
- Result := GetDesiredIndentForLine(Editor, Lines, ACaret, Replace, Dummy);
- end;
- end.