PageRenderTime 59ms CodeModel.GetById 13ms app.highlight 22ms RepoModel.GetById 1ms app.codeStats 3ms

/components/synedit/synbeautifier.pas

http://github.com/graemeg/lazarus
Pascal | 1810 lines | 1409 code | 217 blank | 184 comment | 148 complexity | 224650010fd300b1ae7c059b8b652bb3 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1{-------------------------------------------------------------------------------
   2The contents of this file are subject to the Mozilla Public License
   3Version 1.1 (the "License"); you may not use this file except in compliance
   4with the License. You may obtain a copy of the License at
   5http://www.mozilla.org/MPL/
   6
   7Software distributed under the License is distributed on an "AS IS" basis,
   8WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
   9the specific language governing rights and limitations under the License.
  10
  11The Original Code is: SynHighlighterGeneral.pas, released 2000-04-07.
  12The Original Code is based on the mwGeneralSyn.pas file from the
  13mwEdit component suite by Martin Waldenburg and other developers, the Initial
  14Author of this file is Martin Waldenburg.
  15Portions written by Martin Waldenburg are copyright 1999 Martin Waldenburg.
  16All Rights Reserved.
  17
  18Contributors to the SynEdit and mwEdit projects are listed in the
  19Contributors.txt file.
  20
  21Alternatively, the contents of this file may be used under the terms of the
  22GNU General Public License Version 2 or later (the "GPL"), in which case
  23the provisions of the GPL are applicable instead of those above.
  24If you wish to allow use of your version of this file only under the terms
  25of the GPL and not to allow others to use your version of this file
  26under the MPL, indicate your decision by deleting the provisions above and
  27replace them with the notice and other provisions required by the GPL.
  28If you do not delete the provisions above, a recipient may use your version
  29of this file under either the MPL or the GPL.
  30
  31$Id: SynHighlighterGeneral.pas,v 1.3 2000/11/08 22:09:59 mghie Exp $
  32
  33You may retrieve the latest version of this file at the SynEdit home page,
  34located at http://SynEdit.SourceForge.net
  35}
  36unit SynBeautifier;
  37
  38{$I synedit.inc}
  39
  40interface
  41
  42uses
  43  Classes, SysUtils, LCLProc, SynEditMiscClasses, SynEditMiscProcs, LazSynEditText, SynEditPointClasses,
  44  SynEditKeyCmds, SynHighlighterPas, SynEditHighlighterFoldBase, SynRegExpr;
  45
  46type
  47
  48  TSynCustomBeautifier = class;
  49
  50  // Callback for indent
  51  TSynBeautifierSetIndentProc =
  52    procedure(
  53      (* LinePos:
  54           1-based, the line that should be changed *)
  55      LinePos: Integer;
  56      (* Indent:
  57           New indent in spaces (Logical = Physical *)
  58      Indent: Integer;
  59      (* RelativeToLinePos:
  60           Indent specifies +/- offset from indent on RTLine (0: for absolute indent) *)
  61      RelativeToLinePos: Integer = 0;
  62      (* IndentChars:
  63           String used to build indent; maybe empty, single char, or string (usually 1 tab or 1 space)
  64           The String will be repeated and cut as needed, then filled with spaces at the end
  65         * NOTE: If this is specified the TSynBeautifierIndentType is ignored
  66      *)
  67      IndentChars: String = '';
  68      (* IndentCharsFromLinePos:
  69           Use tab/space mix from this Line for indent (if specified > 0)
  70           "IndentChars" will only be used, if the found tab/space mix is to short
  71         * NOTE: If this is specified the TSynBeautifierIndentType is ignored
  72      *)
  73      IndentCharsFromLinePos: Integer = 0
  74    ) of object;
  75
  76  // Event triggered if Lines may needs Indend
  77  TSynBeautifierGetIndentEvent =
  78    function(
  79      Sender: TObject;                       // the beautifier
  80      Editor: TObject;                       // the synedit
  81      LogCaret, OldLogCaret: TPoint;         // Caret after and before the edit action
  82      FirstLinePos, LastLinePos: Integer;    // Changed lines. this can include lines outside the range of OldLogCaret to LogCaret
  83      Reason: TSynEditorCommand;             // what caused the event
  84      SetIndentProc: TSynBeautifierSetIndentProc
  85     ): boolean of object;
  86
  87
  88  { TSynCustomBeautifier }
  89
  90  TSynCustomBeautifier = class(TComponent)
  91  private
  92    FAutoIndent: Boolean;
  93    FOnGetDesiredIndent: TSynBeautifierGetIndentEvent;
  94    FCurrentEditor: TSynEditBase; // For callback / applyIndent
  95    FCurrentLines: TSynEditStrings;
  96  protected
  97    procedure DoBeforeCommand(const ACaret: TSynEditCaret;
  98                              var Command: TSynEditorCommand); virtual; abstract;
  99    procedure DoAfterCommand(const ACaret: TSynEditCaret;
 100                             var Command: TSynEditorCommand;
 101                             StartLinePos, EndLinePos: Integer); virtual; abstract;
 102    property CurrentEditor: TSynEditBase read FCurrentEditor;
 103    property CurrentLines: TSynEditStrings read FCurrentLines;
 104  public
 105    procedure Assign(Src: TPersistent); override;
 106    function  GetCopy: TSynCustomBeautifier;
 107    procedure BeforeCommand(const Editor: TSynEditBase; const Lines: TSynEditStrings;
 108                            const ACaret: TSynEditCaret; var Command: TSynEditorCommand;
 109                            InitialCmd: TSynEditorCommand);
 110    procedure AfterCommand(const Editor: TSynEditBase; const Lines: TSynEditStrings;
 111                           const ACaret: TSynEditCaret; var Command: TSynEditorCommand;
 112                           InitialCmd: TSynEditorCommand; StartLinePos, EndLinePos: Integer);
 113    // GetDesiredIndentForLine: Returns the 1-based Physical x pos
 114    function GetDesiredIndentForLine
 115             (Editor: TSynEditBase; const Lines: TSynEditStrings;
 116              const ACaret: TSynEditCaret): Integer; virtual; abstract;
 117    function GetDesiredIndentForLine
 118             (Editor: TSynEditBase; const Lines: TSynEditStrings;
 119              const ACaret: TSynEditCaret; out ReplaceIndent: Boolean;
 120              out DesiredIndent: String): Integer; virtual; abstract;
 121    property OnGetDesiredIndent: TSynBeautifierGetIndentEvent
 122      read FOnGetDesiredIndent write FOnGetDesiredIndent;
 123    property AutoIndent: Boolean read FAutoIndent write FAutoIndent;
 124  end;
 125
 126  TSynCustomBeautifierClass = class of TSynCustomBeautifier;
 127  TSynBeautifierIndentType = (
 128    sbitSpace, sbitCopySpaceTab,
 129    sbitPositionCaret,
 130    sbitConvertToTabSpace,   // convert to tabs, fill with spcaces if needed
 131    sbitConvertToTabOnly     // convert to tabs, even if shorter
 132  );
 133
 134  { TSynBeautifier }
 135
 136  TSynBeautifier = class(TSynCustomBeautifier)
 137  private
 138    FIndentType: TSynBeautifierIndentType;
 139  protected
 140    FLogicalIndentLen: Integer;
 141
 142    function GetLine(AnIndex: Integer): String; virtual;
 143    procedure GetIndentInfo(const LinePos: Integer;
 144                            out ACharMix: String; out AnIndent: Integer;
 145                            AStopScanAtLine: Integer = 0);
 146    // requiring FCurrentEditor, FCurrentLines
 147    procedure DoBeforeCommand(const ACaret: TSynEditCaret;
 148                              var Command: TSynEditorCommand); override;
 149    procedure DoAfterCommand(const ACaret: TSynEditCaret;
 150                             var Command: TSynEditorCommand;
 151                             StartLinePos, EndLinePos: Integer); override;
 152    function GetIndent(const LinePos: Integer; out BasedOnLine: Integer;
 153                       AStopScanAtLine: Integer = 0): Integer;
 154    function AdjustCharMix(DesiredIndent: Integer; CharMix, AppendMix: String): String;
 155    function CreateTabSpaceMix(var DesiredIndent: Integer; OnlyTabs: Boolean): String;
 156    function GetCharMix(const LinePos: Integer; var Indent: Integer;
 157                        var IndentCharsFromLinePos: Integer;
 158                        ModifyIndent: Boolean = False): String;
 159    procedure ApplyIndent(LinePos: Integer; Indent: Integer;
 160                          RelativeToLinePos: Integer = 0; IndentChars: String = '';
 161                          IndentCharsFromLinePos: Integer = 0);
 162    function UnIndentLine(const ACaret: TSynEditCaret; out CaretNewX: Integer): Boolean;
 163  public
 164    procedure Assign(Src: TPersistent); override;
 165    // Retruns a 0-based position (even 0-based physical)
 166    function GetIndentForLine(Editor: TSynEditBase; const Line: string;
 167                        Physical: boolean): Integer;
 168    function GetDesiredIndentForLine
 169             (Editor: TSynEditBase; const Lines: TSynEditStrings;
 170              const ACaret: TSynEditCaret): Integer; override;
 171    function GetDesiredIndentForLine
 172             (Editor: TSynEditBase; const Lines: TSynEditStrings;
 173              const ACaret: TSynEditCaret; out ReplaceIndent: Boolean;
 174              out DesiredIndent: String): Integer; override;
 175  published
 176    property IndentType: TSynBeautifierIndentType read FIndentType write FIndentType;
 177  end;
 178
 179  TSynCommentContineMode = (
 180      sccNoPrefix,      // May still do indent, if matched
 181      sccPrefixAlways,  // If the pattern did not match all will be done, except the indent AFTER the prefix (can not be detected)
 182      sccPrefixMatch
 183      //sccPrefixMatchFirst
 184      //sccPrefixMatchPrev
 185      //sccPrefixMatchPrevTwo           // last 2 lines match
 186      //sccPrefixMatchPrevTwoExact      // last 2 lines match and same result for prefix
 187    );
 188
 189  TSynCommentMatchLine = (
 190      sclMatchFirst, // Match the first line of the comment to get substitutes for Prefix ($1)
 191      sclMatchPrev   // Match the previous line of the comment to get substitutes for Prefix ($1)
 192      //sclMatchNestedFirst // For nested comments, first line of this nest.
 193    );
 194
 195  TSynCommentMatchMode = (
 196    // Which parts of the first line to match sclMatchFirst or sclMatchPrev (if prev = first)
 197      scmMatchAfterOpening, // will not include (*,{,//. The ^ will match the first char after
 198      scmMatchOpening,      // will include (*,{,//. The ^ will match the ({/
 199      scmMatchWholeLine,    // Match the entire line
 200      scmMatchAtAsterisk    // AnsiComment only, will match the * of (*, but not the (
 201    );
 202
 203  TSynCommentIndentFlag = (
 204    // * For Matching lines (FCommentMode)
 205      // By default indent is the same as for none comment lines (none overrides sciAlignOpen)
 206      sciNone,      // Does not Indent comment lines (Prefix may contain a fixed indent)
 207      sciAlignOpen, // Indent to real opening pos on first line, if comment does not start at BOL "Foo(); (*"
 208                    // Will force every new line back to Align.
 209                    // To align only once, use IndentFirstLineExtra=MaxInt
 210
 211      // sciAdd...: if (only if) previous line had started with the opening token "(*" or "{".
 212      //            or if sciAlignOpen is set
 213      //            This is not done for "//", as Comment Extension will add a new "//"
 214      //            But Applies to sciNone
 215      sciAddTokenLen,        // add 1 or 2 spaces to indent (for the length of the token)
 216                             // in case of scmMatchAtAsterisk, 1 space is added. ("(" only)
 217      sciAddPastTokenIndent, // Adds any indent found past the opening token  "(*", "{" or "//".
 218                             // For "//" this is added after the nem "//", but before the prefix.
 219
 220
 221      sciMatchOnlyTokenLen,        // Apply the Above only if first line matches. (Only if sciAddTokenLen is specified)
 222      sciMatchOnlyPastTokenIndent,
 223      sciAlignOnlyTokenLen,        // Apply the Above only if sciAlignOpen was used (include via max)
 224      sciAlignOnlyPastTokenIndent,
 225
 226      // flag to ignore spaces, that are matched.
 227      // flag to be smart, if not matched
 228
 229      sciApplyIndentForNoMatch  // Apply above rules For NONE Matching lines (FCommentMode),
 230                                // includes FIndentFirstLineExtra
 231    );
 232  TSynCommentIndentFlags = set of TSynCommentIndentFlag;
 233
 234  TSynCommentExtendMode = (
 235      sceNever,                // Never Extend
 236      sceAlways,               // Always
 237      sceSplitLine,            // If the line was split (caret was not at EOL, when enter was pressed
 238      sceMatching,             // If the line matched (even if sccPrefixAlways or sccNoPrefix
 239      sceMatchingSplitLine
 240    );
 241
 242  TSynCommentType = (sctAnsi, sctBor, sctSlash);
 243
 244    // end mode
 245  { TSynBeautifierPascal }
 246
 247  TSynBeautifierPascal = class(TSynBeautifier)
 248  private
 249    FIndentMode:           Array [TSynCommentType] of TSynCommentIndentFlags;
 250    FIndentFirstLineExtra: Array [TSynCommentType] of String;
 251    FIndentFirstLineMax:   Array [TSynCommentType] of Integer;
 252
 253    FCommentMode:          Array [TSynCommentType] of TSynCommentContineMode;
 254    FMatchMode:            Array [TSynCommentType] of TSynCommentMatchMode;
 255    FMatchLine:            Array [TSynCommentType] of TSynCommentMatchLine;
 256    FMatch:                Array [TSynCommentType] of String;
 257    FPrefix:               Array [TSynCommentType] of String;
 258    FCommentIndent:        Array [TSynCommentType] of TSynBeautifierIndentType;
 259
 260    FEolMatch:             Array [TSynCommentType] of String;
 261    FEolMode:              Array [TSynCommentType] of TSynCommentContineMode;
 262    FEolPostfix:           Array [TSynCommentType] of String;
 263    FEolSkipLongerLine:  Array [TSynCommentType] of Boolean;
 264
 265    FExtendSlashCommentMode: TSynCommentExtendMode;
 266
 267  private
 268    FPasHighlighter: TSynPasSyn;
 269
 270    FCaretAtEOL: Boolean;
 271    FStringBreakAppend: String;
 272    FStringBreakEnabled: Boolean;
 273    FStringBreakPrefix: String;
 274    FWorkLine: Integer; // 1 based
 275    FWorkFoldType: TSynCommentType;
 276    FGetLineAfterComment: Boolean;
 277    FRegExprEngine: TRegExpr;
 278
 279    FCacheFoldEndLvlIdx,         FCacheFoldEndLvl: Integer;
 280    FCacheCommentLvlIdx,         FCacheCommentLvl: Integer;
 281    FCacheFoldTypeForIdx: Integer;
 282                                 FCacheFoldType: TPascalCodeFoldBlockType;
 283    FCacheFirstLineForIdx,       FCacheFirstLine: Integer;
 284    FCacheSlashColumnForIdx,     FCacheSlashColumn: Integer;
 285    FCacheCommentStartColForIdx, FCacheCommentStartCol: Integer;
 286    FCacheLastHlTokenForIdx,     FCacheLastHlTokenCol: Integer;
 287    FCacheLastHlTokenKind:       TtkTokenKind;
 288
 289    FCacheWorkFoldEndLvl: Integer;
 290    FCacheWorkCommentLvl: Integer;
 291    FCacheWorkFoldType: TPascalCodeFoldBlockType;
 292    FCacheWorkFirstLine: Integer;
 293    FCacheWorkSlashCol: Integer;
 294    FCacheWorkCommentStartCol: Integer;
 295
 296  protected
 297    function  IsPasHighlighter: Boolean;
 298    procedure InitCache;
 299    procedure InitPasHighlighter;
 300    // Generic Helpers
 301    function  GetFoldEndLevelForIdx(AIndex: Integer): Integer;
 302    function  GetFoldCommentLevelForIdx(AIndex: Integer): Integer;
 303    function  GetFoldTypeAtEndOfLineForIdx(AIndex: Integer): TPascalCodeFoldBlockType;
 304    function  GetFirstCommentLineForIdx(AIndex: Integer): Integer; // Result is 1 based
 305    function  GetSlashStartColumnForIdx(AIndex: Integer): Integer;
 306    function  GetLastHlTokenForIdx(AIndex: Integer; out APos: Integer): TtkTokenKind;
 307    function  GetCommentStartColForIdx(AIndex: Integer): Integer; // Returns Column (logic) for GetFirstCommentLineForIdx(AIndex)
 308    // Values based on FWorkLine
 309    function  GetFoldEndLevel: Integer;
 310    function  GetFoldCommentLevel: Integer;
 311    function  GetFoldTypeAtEndOfLine: TPascalCodeFoldBlockType;
 312    function  GetFirstCommentLine: Integer; // Result is 1 based
 313    function  GetSlashStartColumn: Integer; // Acts on FWorkLine-1
 314    function  GetCommentStartCol: Integer;  // Acts on GetFirstCommentLineForIdx(FWorkLine) / Logical Resulc
 315
 316    function  GetMatchStartColForIdx(AIndex: Integer): Integer; // Res=1-based
 317    function  GetMatchStartPos(AIndex: Integer = -1; AForceFirst: Boolean = False): TPoint; // Res=1-based / AIndex-1 is only used for sclMatchPrev
 318
 319  protected
 320    function GetLine(AnIndex: Integer): String; override;
 321
 322    procedure DoBeforeCommand(const ACaret: TSynEditCaret;
 323                              var Command: TSynEditorCommand); override;
 324    procedure DoAfterCommand(const ACaret: TSynEditCaret;
 325                             var Command: TSynEditorCommand;
 326                             StartLinePos, EndLinePos: Integer); override;
 327    procedure DoNewLineInString(AStringStartY, AStringStartX: Integer;
 328                                const ACaret: TSynEditCaret;
 329                                var Command: TSynEditorCommand;
 330                                StartLinePos, EndLinePos: Integer);
 331  public
 332    constructor Create(AOwner: TComponent); override;
 333    destructor Destroy; override;
 334    procedure Assign(Src: TPersistent); override;
 335    // Retruns a 0-based position (even 0-based physical)
 336  published
 337    // *** coments with (* *)
 338
 339    (* AnsiIndentFirstLineMax:
 340       * For comments that do NOT start at the BOL in their opening line "  Foo; ( * comment":
 341         if AnsiIndentFirstLineMax is is set:
 342         - If the comment starts before or at the "Max-Column, then sciAlignOpen is always used.
 343         - If the comment starts after max column, then the specified mode is used.
 344           If the specified mode is sciAlignOpen it will be cut at Max
 345       * For comments that start at BOL in their opening line "  ( * comment":
 346         This is ignored
 347
 348
 349       AnsiIndentFirstLineExtra:
 350         For comments that do NOT start at the BOL, an extra indent is added
 351         on the 2nd line (except if sciAlignOpen is used (in Flags, or via A.I.FirstLineMax))
 352    *)
 353    property AnsiIndentMode: TSynCommentIndentFlags read FIndentMode[sctAnsi]
 354                                                   write FIndentMode[sctAnsi];
 355    property AnsiIndentFirstLineMax:   Integer     read FIndentFirstLineMax[sctAnsi]
 356                                                   write FIndentFirstLineMax[sctAnsi];
 357    property AnsiIndentFirstLineExtra: String      read FIndentFirstLineExtra[sctAnsi]
 358                                                   write FIndentFirstLineExtra[sctAnsi];
 359
 360    (* match should start with ^, and leading spaces should not be in ()  "^\s?(\*\*?\*?)"
 361       prefix can refer to $1 and should have spaces to indent after the match "$1 "
 362    *)
 363    property AnsiCommentMode: TSynCommentContineMode read FCommentMode[sctAnsi]
 364                                                     write FCommentMode[sctAnsi];
 365    property AnsiMatch:       String                 read FMatch[sctAnsi]
 366                                                     write FMatch[sctAnsi];
 367    property AnsiPrefix :     String                 read FPrefix[sctAnsi]
 368                              write FPrefix[sctAnsi];
 369    property AnsiMatchMode:   TSynCommentMatchMode   read FMatchMode[sctAnsi]
 370                                                     write FMatchMode[sctAnsi];
 371    property AnsiMatchLine:   TSynCommentMatchLine   read FMatchLine[sctAnsi]
 372                                                     write FMatchLine[sctAnsi];
 373    property AnsiCommentIndent: TSynBeautifierIndentType read FCommentIndent[sctAnsi]
 374                                                         write FCommentIndent[sctAnsi];
 375
 376    // Add postfix at EOL
 377    property AnsiEolMode: TSynCommentContineMode read FEolMode[sctAnsi]
 378                                                 write FEolMode[sctAnsi];
 379    property AnsiEolPostfix: String              read FEolPostfix[sctAnsi]
 380                                                 write FEolPostfix[sctAnsi];
 381    property AnsiEolMatch:   String              read FEolMatch[sctAnsi]
 382                                                 write FEolMatch[sctAnsi];
 383    property AnsiEolSkipLongerLine: Boolean      read FEolSkipLongerLine[sctAnsi]
 384                                                 write FEolSkipLongerLine[sctAnsi];
 385
 386    // *** coments with { }
 387
 388    property BorIndentMode: TSynCommentIndentFlags read FIndentMode[sctBor]
 389                                                  write FIndentMode[sctBor];
 390    property BorIndentFirstLineMax:   Integer     read FIndentFirstLineMax[sctBor]
 391                                                  write FIndentFirstLineMax[sctBor];
 392    property BorIndentFirstLineExtra: String      read FIndentFirstLineExtra[sctBor]
 393                                                  write FIndentFirstLineExtra[sctBor];
 394
 395    property BorCommentMode: TSynCommentContineMode read FCommentMode[sctBor]
 396                                                    write FCommentMode[sctBor];
 397    property BorMatch:       String                 read FMatch[sctBor]
 398                                                    write FMatch[sctBor];
 399    property BorPrefix :     String                 read FPrefix[sctBor]
 400                             write FPrefix[sctBor];
 401    property BorMatchMode:   TSynCommentMatchMode   read FMatchMode[sctBor]
 402                                                    write FMatchMode[sctBor];
 403    property BorMatchLine:   TSynCommentMatchLine   read FMatchLine[sctBor]
 404                                                    write FMatchLine[sctBor];
 405    property BorCommentIndent: TSynBeautifierIndentType read FCommentIndent[sctBor]
 406                                                         write FCommentIndent[sctBor];
 407
 408    property BorEolMode: TSynCommentContineMode read FEolMode[sctBor]
 409                                                write FEolMode[sctBor];
 410    property BorEolPostfix : String             read FEolPostfix[sctBor]
 411                             write FEolPostfix[sctBor];
 412    property BorEolMatch:    String             read FEolMatch[sctBor]
 413                                                write FEolMatch[sctBor];
 414    property BorEolSkipLongerLine: Boolean      read FEolSkipLongerLine[sctBor]
 415                                                write FEolSkipLongerLine[sctBor];
 416
 417    // *** coments with //
 418    // Continue only, if Extended
 419
 420    property ExtendSlashCommentMode: TSynCommentExtendMode read FExtendSlashCommentMode
 421                                                           write FExtendSlashCommentMode;
 422
 423    property SlashIndentMode: TSynCommentIndentFlags read FIndentMode[sctSlash]
 424                                                    write FIndentMode[sctSlash];
 425    property SlashIndentFirstLineMax:   Integer     read FIndentFirstLineMax[sctSlash]
 426                                                    write FIndentFirstLineMax[sctSlash];
 427    property SlashIndentFirstLineExtra: String      read FIndentFirstLineExtra[sctSlash]
 428                                                    write FIndentFirstLineExtra[sctSlash];
 429
 430    property SlashCommentMode: TSynCommentContineMode read FCommentMode[sctSlash]
 431                                                      write FCommentMode[sctSlash];
 432    property SlashMatch:     String                   read FMatch[sctSlash]
 433                                                      write FMatch[sctSlash];
 434    property SlashPrefix :   String                   read FPrefix[sctSlash]
 435                             write FPrefix[sctSlash];
 436    property SlashMatchMode: TSynCommentMatchMode     read FMatchMode[sctSlash]
 437                                                      write FMatchMode[sctSlash];
 438    property SlashMatchLine:   TSynCommentMatchLine   read FMatchLine[sctSlash]
 439                                                      write FMatchLine[sctSlash];
 440    property SlashCommentIndent: TSynBeautifierIndentType read FCommentIndent[sctSlash]
 441                                                         write FCommentIndent[sctSlash];
 442
 443    property SlashEolMode: TSynCommentContineMode read FEolMode[sctSlash]
 444                                                  write FEolMode[sctSlash];
 445    property SlashEolPostfix : String             read FEolPostfix[sctSlash]
 446                               write FEolPostfix[sctSlash];
 447    property SlashEolMatch:    String             read FEolMatch[sctSlash]
 448                                                  write FEolMatch[sctSlash];
 449    property SlashEolSkipLongerLine: Boolean      read FEolSkipLongerLine[sctSlash]
 450                                                  write FEolSkipLongerLine[sctSlash];
 451    // String
 452    property StringBreakEnabled: Boolean read FStringBreakEnabled write FStringBreakEnabled;
 453    property StringBreakAppend: String read FStringBreakAppend write FStringBreakAppend;
 454    property StringBreakPrefix: String read FStringBreakPrefix write FStringBreakPrefix;
 455  end;
 456
 457function dbgs(ACommentType: TSynCommentType): String; overload;
 458function dbgs(AContinueMode: TSynCommentContineMode): String; overload;
 459function dbgs(AMatchLine: TSynCommentMatchLine): String; overload;
 460function dbgs(AMatchMode: TSynCommentMatchMode): String; overload;
 461function dbgs(AIndentFlag: TSynCommentIndentFlag): String; overload;
 462function dbgs(AIndentFlags: TSynCommentIndentFlags): String; overload;
 463function dbgs(AExtendMode: TSynCommentExtendMode): String; overload;
 464
 465implementation
 466uses SynEdit;
 467
 468function dbgs(ACommentType: TSynCommentType): String;
 469begin
 470  Result := ''; WriteStr(Result, ACommentType);
 471end;
 472
 473function dbgs(AContinueMode: TSynCommentContineMode): String;
 474begin
 475  Result := ''; WriteStr(Result, AContinueMode);
 476end;
 477
 478function dbgs(AMatchLine: TSynCommentMatchLine): String;
 479begin
 480  Result := ''; WriteStr(Result, AMatchLine);
 481end;
 482
 483function dbgs(AMatchMode: TSynCommentMatchMode): String;
 484begin
 485  Result := ''; WriteStr(Result, AMatchMode);
 486end;
 487
 488function dbgs(AIndentFlag: TSynCommentIndentFlag): String;
 489begin
 490  Result := ''; WriteStr(Result, AIndentFlag);
 491end;
 492
 493function dbgs(AIndentFlags: TSynCommentIndentFlags): String;
 494var
 495  i: TSynCommentIndentFlag;
 496begin
 497  Result := '';
 498  for i := low(TSynCommentIndentFlag) to high(TSynCommentIndentFlag) do
 499    if i in AIndentFlags then
 500      if Result = ''
 501      then Result := dbgs(i)
 502      else Result := Result + ',' + dbgs(i);
 503  if Result <> '' then
 504    Result := '[' + Result + ']';
 505end;
 506
 507function dbgs(AExtendMode: TSynCommentExtendMode): String;
 508begin
 509  Result := ''; WriteStr(Result, AExtendMode);
 510end;
 511
 512
 513{ TSynBeautifierPascal }
 514
 515function TSynBeautifierPascal.IsPasHighlighter: Boolean;
 516begin
 517  Result := (TSynEdit(FCurrentEditor).Highlighter <> nil) and
 518            (TSynEdit(FCurrentEditor).Highlighter is TSynPasSyn);
 519end;
 520
 521procedure TSynBeautifierPascal.InitCache;
 522begin
 523  FCacheFoldEndLvlIdx         := -1;
 524  FCacheCommentLvlIdx         := -1;
 525  FCacheFoldTypeForIdx        := -1;
 526  FCacheFirstLineForIdx       := -1;
 527  FCacheSlashColumnForIdx     := -1;
 528  FCacheCommentStartColForIdx := -1;
 529
 530  FCacheWorkFoldEndLvl         := -2;
 531  FCacheWorkCommentLvl         := -2;
 532  FCacheWorkFoldType           := cfbtIfThen; // dummy for not yet cached
 533  FCacheWorkFirstLine          := -2;
 534  FCacheWorkSlashCol           := -2;
 535  FCacheWorkCommentStartCol := -2;
 536
 537end;
 538
 539procedure TSynBeautifierPascal.InitPasHighlighter;
 540begin
 541  FPasHighlighter := TSynPasSyn(TSynEdit(FCurrentEditor).Highlighter);
 542  FPasHighlighter.CurrentLines := FCurrentLines;
 543  FPasHighlighter.ScanRanges;
 544end;
 545
 546function TSynBeautifierPascal.GetFoldEndLevelForIdx(AIndex: Integer): Integer;
 547begin
 548  Result := FCacheFoldEndLvl;
 549  if AIndex = FCacheFoldEndLvlIdx then
 550    exit;
 551  FCacheFoldEndLvlIdx := AIndex;
 552  FCacheFoldEndLvl := FPasHighlighter.FoldBlockEndLevel(AIndex, FOLDGROUP_PASCAL, [sfbIncludeDisabled]);
 553  Result := FCacheFoldEndLvl;
 554end;
 555
 556function TSynBeautifierPascal.GetFoldCommentLevelForIdx(AIndex: Integer): Integer;
 557var
 558  tmp: Pointer;
 559  Block: TPascalCodeFoldBlockType;
 560begin
 561  Result := FCacheCommentLvl;
 562  if AIndex = FCacheCommentLvlIdx then
 563    exit;
 564  FCacheCommentLvlIdx := AIndex;
 565
 566  FCacheCommentLvl := GetFoldEndLevelForIdx(AIndex) - 1;
 567  while (FCacheCommentLvl > 0) do begin
 568    FPasHighlighter.FoldBlockNestedTypes(AIndex , FCacheCommentLvl - 1,
 569                 tmp, FOLDGROUP_PASCAL, [sfbIncludeDisabled]);
 570    Block:=TPascalCodeFoldBlockType(PtrUInt(tmp));
 571    if not (Block in [cfbtAnsiComment, cfbtBorCommand, cfbtSlashComment]) then
 572      break;
 573    dec(FCacheCommentLvl);
 574  end;
 575  inc(FCacheCommentLvl);
 576  Result := FCacheCommentLvl;
 577end;
 578
 579function TSynBeautifierPascal.GetFoldTypeAtEndOfLineForIdx(AIndex: Integer): TPascalCodeFoldBlockType;
 580var
 581  EndLevel: Integer;
 582  tmp: Pointer;
 583begin
 584  // TODO cfbtNestedComment
 585  Result := FCacheFoldType;
 586  if AIndex = FCacheFoldTypeForIdx then
 587    exit;
 588
 589  FCacheFoldTypeForIdx := AIndex;
 590  EndLevel := GetFoldEndLevelForIdx(AIndex);
 591  if (EndLevel > 0) then
 592  begin
 593    if FPasHighlighter.FoldBlockNestedTypes(AIndex, EndLevel - 1,
 594       tmp, FOLDGROUP_PASCAL, [sfbIncludeDisabled])
 595    then
 596      FCacheFoldType:=TPascalCodeFoldBlockType(PtrUInt(tmp))
 597    else
 598      FCacheFoldType := cfbtNone;
 599  end;
 600
 601  while (FCacheFoldType = cfbtNestedComment) and (EndLevel > 1) do begin
 602    dec(EndLevel);
 603    if FPasHighlighter.FoldBlockNestedTypes(AIndex, EndLevel - 1,
 604         tmp, FOLDGROUP_PASCAL, [sfbIncludeDisabled])
 605    then
 606      FCacheFoldType:=TPascalCodeFoldBlockType(PtrUInt(tmp))
 607    else
 608      FCacheFoldType := cfbtNone;
 609  end;
 610
 611  Result := FCacheFoldType;
 612end;
 613
 614function TSynBeautifierPascal.GetFirstCommentLineForIdx(AIndex: Integer): Integer;
 615var
 616  FoldType: TPascalCodeFoldBlockType;
 617  ANewIndex, EndLvl, CommentLvl: Integer;
 618begin
 619  Result := FCacheFirstLine;
 620  if AIndex = FCacheFirstLineForIdx then
 621    exit;
 622
 623  ANewIndex := AIndex;
 624  FoldType := GetFoldTypeAtEndOfLineForIdx(ANewIndex);
 625  EndLvl   := GetFoldEndLevelForIdx(ANewIndex);
 626
 627  //if (EndLvl = 0) or not(FoldType in [cfbtAnsiComment, cfbtBorCommand, cfbtSlashComment]) and
 628  //   (AIndex > 0) and (GetSlashStartColumnForIdx(AIndex - 1) > 0)
 629  //then begin
 630  //  dec(ANewIndex);
 631  //  FoldType := GetFoldTypeAtEndOfLineForIdx(ANewIndex);
 632  //  EndLvl   := GetFoldEndLevelForIdx(ANewIndex);
 633  //end;
 634
 635  if (EndLvl = 0) or not(FoldType in [cfbtAnsiComment, cfbtBorCommand, cfbtSlashComment])
 636  then begin
 637    Result := ToPos(AIndex) - 1; // 1 based - the line above ANewIndex
 638    // maybe the line above has a trailing comment
 639    //if (AIndex <> ANewIndex) and (ANewIndex > 0) and (GetSlashStartColumnForIdx(ANewIndex-1) > 0) then
 640    //  Result := ToPos(ANewIndex) - 1;
 641    exit;
 642  end;
 643
 644  FCacheFirstLineForIdx := AIndex;
 645  FCacheFirstLine       := ToPos(ANewIndex) - 1;
 646  CommentLvl            := GetFoldCommentLevelForIdx(ANewIndex);
 647
 648  while (FCacheFirstLine > 0) do begin
 649    if CommentLvl > FPasHighlighter.FoldBlockMinLevel(FCacheFirstLine-1, FOLDGROUP_PASCAL, [sfbIncludeDisabled]) then
 650      break;
 651    dec(FCacheFirstLine);
 652  end;
 653
 654  if FoldType = cfbtSlashComment then begin
 655    // maybe the line above has a trailing comment
 656    if GetSlashStartColumnForIdx(ToIdx(FCacheFirstLine)) > 0 then
 657      dec(FCacheFirstLine);
 658  end;
 659
 660  Result := FCacheFirstLine;
 661//debugln(['FIRST LINE ', FCacheFirstLine]);
 662end;
 663
 664function TSynBeautifierPascal.GetSlashStartColumnForIdx(AIndex: Integer): Integer;
 665var
 666  Tk: TtkTokenKind;
 667begin
 668  Result := FCacheSlashColumn;
 669  if AIndex = FCacheSlashColumnForIdx then
 670    exit;
 671
 672  FCacheSlashColumnForIdx := AIndex;
 673  Tk := GetLastHlTokenForIdx(AIndex, FCacheSlashColumn);
 674  if Tk <> SynHighlighterPas.tkComment then
 675    FCacheSlashColumn := -1;
 676  Result := FCacheSlashColumn;
 677end;
 678
 679function TSynBeautifierPascal.GetLastHlTokenForIdx(AIndex: Integer; out
 680  APos: Integer): TtkTokenKind;
 681begin
 682  Result := FCacheLastHlTokenKind;
 683  APos   := FCacheLastHlTokenCol;
 684  if AIndex = FCacheLastHlTokenForIdx then
 685    exit;
 686
 687  FCacheSlashColumnForIdx := AIndex;
 688  FCacheLastHlTokenKind   := SynHighlighterPas.tkUnknown;
 689  FCacheLastHlTokenCol    := -1;
 690  if (FCurrentLines[AIndex] <> '') then begin
 691    FPasHighlighter.StartAtLineIndex(AIndex);
 692    while not FPasHighlighter.GetEol do begin
 693      FCacheLastHlTokenKind := TtkTokenKind(FPasHighlighter.GetTokenKind);
 694      FCacheLastHlTokenCol := FPasHighlighter.GetTokenPos + 1;
 695      FPasHighlighter.Next;
 696    end;
 697  end;
 698  Result := FCacheLastHlTokenKind;
 699  APos   := FCacheLastHlTokenCol;
 700end;
 701
 702function TSynBeautifierPascal.GetCommentStartColForIdx(AIndex: Integer): Integer;
 703var
 704  nl: TLazSynFoldNodeInfoList;
 705  i: Integer;
 706  FoldCommentLvl: Integer;
 707begin
 708  Result := FCacheCommentStartCol;
 709  if AIndex = FCacheCommentStartColForIdx then
 710    exit;
 711  FCacheCommentStartColForIdx := AIndex;
 712
 713  if (GetFoldEndLevelForIdx(AIndex) = 0) or
 714     not(GetFoldTypeAtEndOfLineForIdx(AIndex) in [cfbtAnsiComment, cfbtBorCommand, cfbtSlashComment])
 715  then begin
 716    // must be SlashComment
 717    FCacheCommentStartCol := GetSlashStartColumnForIdx(AIndex - 1);
 718    //FCacheCommentStartCol := GetSlashStartColumnForIdx(AIndex);
 719    //if FCacheCommentStartCol > 0 then begin
 720    //  i := GetSlashStartColumnForIdx(AIndex - 1);
 721    //  if i > 0 then
 722    //    FCacheCommentStartCol := i;
 723    //end;
 724    Result := FCacheCommentStartCol;
 725//debugln(['FIRST COL prev-// ', FCacheCommentStartCol]);
 726    exit;
 727  end;
 728
 729  FCacheCommentStartCol := -1;
 730  FoldCommentLvl := GetFoldCommentLevelForIdx(AIndex);
 731  nl := FPasHighlighter.FoldNodeInfo[GetFirstCommentLineForIdx(AIndex) - 1];
 732  nl.AddReference;
 733  nl.ClearFilter;
 734  nl.ActionFilter := [sfaOpen];
 735  nl.GroupFilter  := FOLDGROUP_PASCAL;
 736  i := nl.Count - 1;
 737  while i >= 0 do  begin
 738    if (not (sfaInvalid in nl[i].FoldAction)) and
 739       (nl[i].NestLvlEnd = FoldCommentLvl)
 740    then begin
 741      FCacheCommentStartCol := nl[i].LogXStart + 1;  // Highlighter pos are 0 based
 742      break;
 743    end;
 744    dec(i);
 745  end;
 746  nl.ReleaseReference;
 747//debugln(['FIRST COL ', FCacheCommentStartCol]);
 748
 749  Result := FCacheCommentStartCol;
 750end;
 751
 752function TSynBeautifierPascal.GetFoldEndLevel: Integer;
 753begin
 754  Result := FCacheWorkFoldEndLvl;
 755  if FCacheWorkFoldEndLvl > -2 then
 756    exit;
 757  FCacheWorkFoldEndLvl := GetFoldEndLevelForIdx(FWorkLine-1);
 758  Result := FCacheWorkFoldEndLvl;
 759end;
 760
 761function TSynBeautifierPascal.GetFoldCommentLevel: Integer;
 762begin
 763  Result := FCacheWorkCommentLvl;
 764  if FCacheWorkCommentLvl > -2 then
 765    exit;
 766  FCacheWorkCommentLvl := GetFoldCommentLevelForIdx(FWorkLine-1);
 767  Result := FCacheWorkCommentLvl;
 768end;
 769
 770function TSynBeautifierPascal.GetFoldTypeAtEndOfLine: TPascalCodeFoldBlockType;
 771begin
 772  Result := FCacheWorkFoldType;
 773  if FCacheWorkFoldType <> cfbtIfThen then
 774    exit;
 775  FCacheWorkFoldType := GetFoldTypeAtEndOfLineForIdx(FWorkLine-1);
 776  Result := FCacheWorkFoldType;
 777end;
 778
 779function TSynBeautifierPascal.GetFirstCommentLine: Integer;
 780begin
 781  Result := FCacheWorkFirstLine;
 782  if FCacheWorkFirstLine > -2 then
 783    exit;
 784  FCacheWorkFirstLine := GetFirstCommentLineForIdx(FWorkLine-1);
 785  Result := FCacheWorkFirstLine;
 786end;
 787
 788function TSynBeautifierPascal.GetSlashStartColumn: Integer;
 789begin
 790  Result := FCacheWorkSlashCol;
 791  if FCacheWorkSlashCol > -2 then
 792    exit;
 793  FCacheWorkSlashCol := GetSlashStartColumnForIdx(FWorkLine-2);
 794  Result := FCacheWorkSlashCol;
 795end;
 796
 797function TSynBeautifierPascal.GetCommentStartCol: Integer;
 798begin
 799  Result := FCacheWorkCommentStartCol;
 800  if FCacheWorkCommentStartCol > -2 then
 801    exit;
 802  FCacheWorkCommentStartCol := GetCommentStartColForIdx(FWorkLine-1);
 803  Result := FCacheWorkCommentStartCol;
 804end;
 805
 806function TSynBeautifierPascal.GetMatchStartColForIdx(AIndex: Integer): Integer;
 807begin
 808  if ToPos(AIndex) = GetFirstCommentLine then begin
 809    // Match on FirstLine
 810    case FMatchMode[FWorkFoldType] of
 811      scmMatchAfterOpening: begin
 812          if FWorkFoldType = sctBor
 813          then Result := GetCommentStartCol + 1
 814          else Result := GetCommentStartCol + 2;
 815        end;
 816      scmMatchOpening:    Result := GetCommentStartCol;
 817      scmMatchWholeLine:  Result := 1;
 818      scmMatchAtAsterisk: Result := GetCommentStartCol + 1;
 819    end;
 820  end
 821  else begin
 822    // Match on prev, not first
 823    case FMatchMode[FWorkFoldType] of
 824      scmMatchAfterOpening, scmMatchOpening, scmMatchAtAsterisk:
 825          Result := 1 + GetIndentForLine(nil, FCurrentLines[AIndex], False);
 826      scmMatchWholeLine:
 827          Result := 1;
 828    end;
 829  end;
 830end;
 831
 832function TSynBeautifierPascal.GetMatchStartPos(AIndex: Integer; AForceFirst: Boolean): TPoint;
 833begin
 834  if AIndex < 0 then
 835    AIndex := ToIdx(FWorkLine);
 836
 837  if AForceFirst then
 838    Result.Y := GetFirstCommentLine
 839  else
 840    case FMatchLine[FWorkFoldType] of
 841      sclMatchFirst: Result.Y := GetFirstCommentLine;
 842      sclMatchPrev:  Result.Y := ToPos(AIndex)-1; // FWorkLine - 1
 843    end;
 844
 845  Result.X := GetMatchStartColForIdx(ToIdx(Result.Y));
 846end;
 847
 848
 849function TSynBeautifierPascal.GetLine(AnIndex: Integer): String;
 850var
 851  ReplacedPrefix: String;
 852  Indent, PreFixPos: Integer;
 853  r: Boolean;
 854  p: TPoint;
 855begin
 856  if not FGetLineAfterComment then begin
 857    Result := inherited GetLine(AnIndex);
 858    exit;
 859  end;
 860
 861  // Need to cut of existing Prefix to find indent after prefix
 862
 863  if AnIndex < GetFirstCommentLine-1 then begin
 864    Result := '';
 865//debugln(['GETLINE FROM (< First) ', AnIndex,' ''', FCurrentLines[AnIndex], ''' to empty']);
 866  end;
 867
 868  // 1) Run the match for this line (match prev, or first)
 869  //    and see if we can find the replaced prefix in this line
 870  if AnIndex > GetFirstCommentLine-1 then begin
 871    p := GetMatchStartPos(AnIndex);
 872    FRegExprEngine.InputString:= copy(FCurrentLines[ToIdx(p.y)], p.x, MaxInt);
 873    FRegExprEngine.Expression := FMatch[FWorkFoldType];
 874    if FRegExprEngine.ExecPos(1) then begin
 875      ReplacedPrefix := FRegExprEngine.Substitute(FPrefix[FWorkFoldType]);
 876      while (ReplacedPrefix <> '') and (ReplacedPrefix[1] in [#9, ' ']) do
 877        delete(ReplacedPrefix, 1, 1);
 878      while (ReplacedPrefix <> '') and (ReplacedPrefix[length(ReplacedPrefix)] in [#9, ' ']) do
 879        delete(ReplacedPrefix, length(ReplacedPrefix), 1);
 880      if ReplacedPrefix <> '' then begin
 881        Result := FCurrentLines[AnIndex];
 882        PreFixPos := pos(ReplacedPrefix, Result);
 883        if (PreFixPos > 0) and (PreFixPos <= GetMatchStartColForIdx(AnIndex)) then begin
 884          delete(Result, 1, PreFixPos - 1 + Length(ReplacedPrefix));
 885//debugln(['GETLINE FROM (1) ', AnIndex,' ''', FCurrentLines[AnIndex], ''' to ''', Result, '''']);
 886          exit;
 887        end;
 888      end;
 889    end;
 890  end;
 891
 892  // 2) See, if the current-1 line can be matched
 893  r := False;
 894  Result := FCurrentLines[AnIndex];
 895  Indent := GetMatchStartColForIdx(AnIndex);
 896  FRegExprEngine.InputString:= copy(FCurrentLines[AnIndex], Indent, MaxInt);
 897  FRegExprEngine.Expression := FMatch[FWorkFoldType];
 898  r := FRegExprEngine.ExecPos(1);
 899  if (not r) and (Indent > 1) and
 900     ((p.y <> GetFirstCommentLine) or (FMatchMode[FWorkFoldType] = scmMatchWholeLine))
 901  then begin
 902    // try whole line
 903// TODO: only if not first, or if setting
 904    FRegExprEngine.InputString := FCurrentLines[AnIndex];
 905    r := FRegExprEngine.ExecPos(1);
 906    if r then
 907      Indent := 1;
 908  end;
 909  if (r) then begin
 910    Indent := Indent + FRegExprEngine.MatchPos[0] - 1 + FRegExprEngine.MatchLen[0];
 911    Result := FCurrentLines[AnIndex];
 912    if (Indent <= Length(Result)) then
 913      while (Indent > 1) and (Result[Indent] in [#9, ' ']) do
 914        dec(Indent);
 915    inc(Indent);
 916    if Indent > 0 then begin
 917      Result := copy(Result, Indent, MaxInt);
 918//debugln(['GETLINE FROM (2) ', AnIndex,' ''', FCurrentLines[AnIndex], ''' to ''', Result, '''']);
 919      exit;
 920    end;
 921  end;
 922
 923  // 3) maybe try currest replace, if different from 1?
 924
 925  // Nothing found
 926  Result := '';
 927//debugln(['GETLINE FROM (X) ', AnIndex,' ''', FCurrentLines[AnIndex], ''' to empty']);
 928end;
 929
 930procedure TSynBeautifierPascal.DoBeforeCommand(const ACaret: TSynEditCaret;
 931  var Command: TSynEditorCommand);
 932begin
 933  FCaretAtEOL := ACaret.BytePos > Length(FCurrentLines[ToIdx(ACaret.LinePos)]);
 934  FGetLineAfterComment := False;
 935  inherited DoBeforeCommand(ACaret, Command);
 936end;
 937
 938procedure TSynBeautifierPascal.DoAfterCommand(const ACaret: TSynEditCaret;
 939  var Command: TSynEditorCommand; StartLinePos, EndLinePos: Integer);
 940
 941var
 942  WorkLine, PrevLineShlasCol: Integer;
 943  ReplacedPrefix: String; // Each run matches only one Type
 944  MatchResultIntern, MatchedBOLIntern: Array [Boolean] of Boolean;  // Each run matches only one Type
 945
 946  function CheckMatch(AType: TSynCommentType; AFailOnNoPattern: Boolean = False;
 947    AForceFirst: Boolean = False): Boolean;
 948  var
 949    p: TPoint;
 950  begin
 951    if (FMatch[AType] = '') and AFailOnNoPattern then begin
 952      Result := False;
 953      exit;
 954    end;
 955
 956    if MatchedBOLIntern[AForceFirst] then begin
 957      Result := MatchResultIntern[AForceFirst];
 958      exit;
 959    end;
 960
 961    p := GetMatchStartPos(-1, AForceFirst);
 962
 963    FRegExprEngine.InputString:= copy(FCurrentLines[ToIdx(p.y)], p.x, MaxInt);
 964    FRegExprEngine.Expression := FMatch[AType];
 965    if not FRegExprEngine.ExecPos(1) then begin
 966      ReplacedPrefix := FRegExprEngine.Substitute(FPrefix[AType]);
 967      MatchedBOLIntern[AForceFirst] := True;
 968      MatchResultIntern[AForceFirst] := False;
 969      Result := MatchResultIntern[AForceFirst];
 970      exit;
 971    end;
 972
 973    ReplacedPrefix := FRegExprEngine.Substitute(FPrefix[AType]);
 974    MatchedBOLIntern[AForceFirst] := True;
 975    MatchResultIntern[AForceFirst] := True;
 976    Result := MatchResultIntern[AForceFirst];
 977
 978  end;
 979
 980  function IsFoldTypeEnabled(AType: TSynCommentType): Boolean;
 981  begin
 982  Result := (  ( (AType <> sctSlash) or
 983               ( ((not FCaretAtEOL) and (FExtendSlashCommentMode <> sceNever)) or
 984                 ((FCaretAtEOL)     and not(FExtendSlashCommentMode in [sceNever, sceSplitLine, sceMatchingSplitLine]))
 985               )
 986             ) and
 987             ( (FIndentMode[AType] <> []) or
 988               (FCommentMode[AType] <> sccNoPrefix) or
 989               (FEolMode[AType] <> sccNoPrefix)
 990            ))
 991  end;
 992
 993var
 994  Indent, dummy: Integer;
 995  s: String;
 996  FoldTyp: TSynCommentType;
 997  AnyEnabled: Boolean;
 998  ExtendSlash, BeSmart, Matching: Boolean;
 999  PreviousIsFirst, IsAtBOL, DidAlignOpen: Boolean;
1000
1001  IndentTypeBackup: TSynBeautifierIndentType;
1002
1003begin
1004  if (EndLinePos < 1) or
1005     ((Command <> ecLineBreak) and (Command <> ecInsertLine)) or
1006     (not IsPasHighlighter)
1007  then begin
1008    inherited DoAfterCommand(ACaret, Command, StartLinePos, EndLinePos);
1009    exit;
1010  end;
1011
1012  AnyEnabled := False;
1013  for FoldTyp := low(TSynCommentType) to high(TSynCommentType) do
1014    AnyEnabled := AnyEnabled or IsFoldTypeEnabled(FoldTyp);
1015  if (not AnyEnabled) and (not FStringBreakEnabled) then begin
1016    inherited DoAfterCommand(ACaret, Command, StartLinePos, EndLinePos);
1017    exit;
1018  end;
1019
1020  InitCache;
1021  InitPasHighlighter;
1022  FGetLineAfterComment := False;
1023  MatchedBOLIntern[True] := False;
1024  MatchedBOLIntern[False] := False;
1025  PrevLineShlasCol := -1;
1026  dummy := 0;
1027
1028  if (Command = ecLineBreak)
1029  then WorkLine := ACaret.LinePos
1030  else WorkLine := ACaret.LinePos + 1;
1031  FWorkLine := WorkLine;
1032
1033
1034  // Find Foldtype
1035  case GetFoldTypeAtEndOfLine of
1036    cfbtAnsiComment:  FoldTyp := sctAnsi;
1037    cfbtBorCommand:   FoldTyp := sctBor;
1038    cfbtSlashComment: FoldTyp := sctSlash;
1039    else
1040      begin
1041        if (FCurrentLines[ToIdx(WorkLine)-1] <> '') and
1042           (FExtendSlashCommentMode <> sceNever) and
1043           ( (not FCaretAtEOL) or not(FExtendSlashCommentMode in [sceSplitLine, sceMatchingSplitLine]) )
1044        then begin
1045          PrevLineShlasCol := GetSlashStartColumn;
1046          if PrevLineShlasCol > 0 then
1047            FoldTyp := sctSlash;
1048        end;
1049        if PrevLineShlasCol < 0 then begin
1050          if (FCurrentLines[ToIdx(WorkLine)-1] <> '') and
1051             (GetLastHlTokenForIdx(ToIdx(WorkLine)-1, dummy) = SynHighlighterPas.tkString)
1052          then
1053            DoNewLineInString(WorkLine - 1, dummy, ACaret, Command, StartLinePos, EndLinePos)
1054          else
1055            inherited DoAfterCommand(ACaret, Command, StartLinePos, EndLinePos);
1056          exit;
1057        end;
1058      end;
1059  end;
1060
1061  if not IsFoldTypeEnabled(FoldTyp) then begin
1062    inherited DoAfterCommand(ACaret, Command, StartLinePos, EndLinePos);
1063    exit;
1064  end;
1065
1066  FWorkFoldType := FoldTyp;
1067
1068  // Check if we need extend
1069  ExtendSlash := False;
1070  if (FoldTyp = sctSlash) and (ACaret.OldLineBytePos.x > GetSlashStartColumn+2) then begin
1071    // Check if extension is needed
1072    case FExtendSlashCommentMode of
1073      sceAlways:    ExtendSlash := True;
1074      sceSplitLine: ExtendSlash := not FCaretAtEOL;
1075      sceMatching:  ExtendSlash := CheckMatch(FoldTyp);
1076      sceMatchingSplitLine: ExtendSlash := CheckMatch(FoldTyp) and (not FCaretAtEOL);
1077    end;
1078
1079    if not ExtendSlash then begin
1080      inherited DoAfterCommand(ACaret, Command, StartLinePos, EndLinePos);
1081      exit;
1082    end;
1083  end;
1084
1085  // Indent
1086  Matching := CheckMatch(FoldTyp);
1087  PreviousIsFirst := (GetFirstCommentLine = WorkLine -1);
1088  DidAlignOpen := False;
1089  IsAtBOL := True;
1090  if PreviousIsFirst then
1091    IsAtBOL := GetCommentStartCol = 1 + GetIndentForLine(nil, FCurrentLines[ToIdx(GetFirstCommentLine)], False);
1092
1093
1094  // Aply indent before prefix
1095  if Matching or (FCommentMode[FoldTyp] = sccPrefixAlways) or (sciApplyIndentForNoMatch in FIndentMode[FoldTyp])
1096  then begin
1097    IndentTypeBackup := IndentType;
1098    try
1099      if IndentType = sbitPositionCaret then
1100        IndentType := sbitSpace;
1101      if IndentType = sbitConvertToTabOnly then
1102        IndentType := sbitConvertToTabSpace;
1103
1104      if PreviousIsFirst and not IsAtBOL and
1105         (FIndentFirstLineMax[FoldTyp] > 0) and
1106         ( (FIndentFirstLineMax[FoldTyp] + 1 >= GetCommentStartCol) or
1107           (FIndentMode[FoldTyp] * [sciNone, sciAlignOpen] = [sciAlignOpen])
1108         )
1109      then begin
1110        // Use sciAlignOpen
1111        Indent := Min(
1112          FCurrentLines.LogicalToPhysicalCol(FCurrentLines[ToIdx(GetFirstCommentLine)], ToIdx(GetFirstCommentLine), GetCommentStartCol-1),
1113          FIndentFirstLineMax[FoldTyp]);
1114        s := GetCharMix(WorkLine, Indent, dummy);
1115        FLogicalIndentLen := length(s);
1116        FCurrentLines.EditInsert(1, WorkLine, s);
1117        DidAlignOpen := True;
1118      end
1119      else
1120      if (FIndentMode[FoldTyp] * [sciNone, sciAlignOpen] = [sciAlignOpen]) and
1121         (GetCommentStartCol > 0)
1122      then begin
1123        Indent := FCurrentLines.LogicalToPhysicalCol(FCurrentLines[ToIdx(GetFirstCommentLine)], ToIdx(GetFirstCommentLine), GetCommentStartCol-1);
1124        if FIndentFirstLineMax[FoldTyp] > 0
1125        then Indent := Min(Indent, FIndentFirstLineMax[FoldTyp]);
1126        s := GetCharMix(WorkLine, Indent, dummy);
1127        FLogicalIndentLen := length(s);
1128        FCurrentLines.EditInsert(1, WorkLine, s);
1129        DidAlignOpen := True;
1130      end
1131      else
1132      if (sciNone in FIndentMode[FoldTyp]) then begin
1133        // No indent
1134      end
1135      else
1136      begin
1137        inherited DoAfterCommand(ACaret, Command, StartLinePos, EndLinePos);
1138      end;
1139    finally
1140      IndentType := IndentTypeBackup;
1141    end;
1142
1143    // AnsiIndentFirstLineExtra
1144    if PreviousIsFirst and (not IsAtBOL) and (not DidAlignOpen) then begin
1145      FCurrentLines.EditInsert(1 + FLogicalIndentLen, WorkLine, FIndentFirstLineExtra[FoldTyp]);
1146      FLogicalIndentLen := FLogicalIndentLen + length(FIndentFirstLineExtra[FoldTyp]);
1147    end;
1148
1149  end
1150  else
1151    inherited DoAfterCommand(ACaret, Command, StartLinePos, EndLinePos);
1152
1153  Indent := 0; // Extra Indent
1154  BeSmart := (PreviousIsFirst or (sciAlignOpen in FIndentMode[FoldTyp])) and
1155             (Matching or ExtendSlash or (sciApplyIndentForNoMatch in FIndentMode[FoldTyp]) or
1156              (FCommentMode[FoldTyp] = sccPrefixAlways)  );
1157
1158  // sciAddTokenLen -- Spaces for (* or {
1159  if BeSmart and
1160     ( (sciAddTokenLen in FIndentMode[FoldTyp]) and
1161       ( (not(sciMatchOnlyTokenLen in FIndentMode[FoldTyp])) or CheckMatch(FoldTyp, False, True) ) and
1162       ( (not(sciAlignOnlyTokenLen in FIndentMode[FoldTyp])) or DidAlignOpen )
1163     )
1164  then begin
1165    case FoldTyp of
1166      sctAnsi:  if FMatchMode[FoldTyp] = scmMatchAtAsterisk
1167                then Indent := 1
1168                else Indent := 2;
1169      sctBor:   Indent := 1;
1170      sctSlash: if ExtendSlash
1171                then Indent := 0 // do the slashes
1172                else Indent := 2;
1173    end;
1174  end;
1175
1176  // sciAddPastTokenIndent -- Spaces from after (* or { (to go befare prefix e.g " {  * foo")
1177  if BeSmart and
1178     ( (sciAddPastTokenIndent in FIndentMode[FoldTyp]) and
1179       ( (not(sciMatchOnlyPastTokenIndent in FIndentMode[FoldTyp])) or CheckMatch(FoldTyp, False, True) ) and
1180       ( (not(sciAlignOnlyPastTokenIndent in FIndentMode[FoldTyp])) or DidAlignOpen )
1181     ) and
1182     (GetCommentStartCol > 0) // foundStartCol
1183  then begin
1184    case FoldTyp of
1185      // ignores scmMatchAtAsterisk
1186      sctAnsi:  s := copy(FCurrentLines[ToIdx(GetFirstCommentLine)], GetCommentStartCol+2, MaxInt);
1187      sctBor:   s := copy(FCurrentLines[ToIdx(GetFirstCommentLine)], GetCommentStartCol+1, MaxInt);
1188      sctSlash: s := copy(FCurrentLines[ToIdx(GetFirstCommentLine)], GetCommentStartCol+2, MaxInt);
1189    end;
1190    Indent := Indent + GetIndentForLine(nil, s, False);
1191  end;
1192  // Extend //
1193  if ExtendSlash then begin
1194    FCurrentLines.EditInsert(1 + FLogicalIndentLen, WorkLine, '//');
1195    FLogicalIndentLen := FLogicalIndentLen + 2;
1196  end;
1197  if (Indent > 0) then begin
1198    FCurrentLines.EditInsert(1 + FLogicalIndentLen, WorkLine, StringOfChar(' ', Indent ));
1199    FLogicalIndentLen := FLogicalIndentLen + Indent;
1200  end;
1201
1202  // Apply prefix
1203  if (FCommentMode[FoldTyp] = sccPrefixAlways) or
1204     ((FCommentMode[FoldTyp] = sccPrefixMatch) and Matching)
1205  then begin
1206    FCurrentLines.EditInsert(1 + FLogicalIndentLen, WorkLine, ReplacedPrefix);
1207    FLogicalIndentLen := FLogicalIndentLen + length(ReplacedPrefix);
1208
1209    // Post  prefix indent
1210    FGetLineAfterComment := True;
1211    try
1212      GetIndentInfo(WorkLine, s, Indent, GetFirstCommentLine);
1213      if s <> '' then begin
1214        FCurrentLines.EditInsert(1 + FLogicalIndentLen, WorkLine, s);
1215        FLogicalIndentLen := FLogicalIndentLen + length(s); // logical (Indent is phisical)
1216      end
1217      else
1218        FLogicalIndentLen := FLogicalIndentLen + Indent; // maybe position caret
1219    finally
1220      FGetLineAfterComment := False;
1221    end;
1222  end;
1223
1224
1225  if (Command = ecLineBreak) then begin
1226    ACaret.IncForcePastEOL;
1227    ACaret.BytePos := 1 + FLogicalIndentLen;
1228    ACaret.DecForcePastEOL;
1229  end;
1230
1231end;
1232
1233procedure TSynBeautifierPascal.DoNewLineInString(AStringStartY, AStringStartX: Integer;
1234  const ACaret: TSynEditCaret; var Command: TSynEditorCommand; StartLinePos,
1235  EndLinePos: Integer);
1236var
1237  s: String;
1238  WorkLine: Integer;
1239  f: Boolean;
1240begin
1241  inherited DoAfterCommand(ACaret, Command, StartLinePos, EndLinePos);
1242  if not FStringBreakEnabled then
1243    exit;
1244
1245  if (Command = ecLineBreak)
1246  then WorkLine := ACaret.LinePos - 1
1247  else WorkLine := ACaret.LinePos;
1248
1249  s := FCurrentLines[ToIdx(WorkLine)];
1250  if (AStringStartX < 1) or (AStringStartX > Length(s)) or (s[AStringStartX] <> '''') then
1251    exit;
1252  f := False;
1253  while AStringStartX <= length(s) do begin
1254    if (s[AStringSta…

Large files files are truncated, but you can click here to view the full file