PageRenderTime 62ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/components/synedit/synhighlightermulti.pas

http://github.com/graemeg/lazarus
Pascal | 2023 lines | 1705 code | 197 blank | 121 comment | 230 complexity | 1e0d5448b33fbb746dec220f1af5305e MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception
  1. {-------------------------------------------------------------------------------
  2. The contents of this file are subject to the Mozilla Public License
  3. Version 1.1 (the "License"); you may not use this file except in compliance
  4. with the License. You may obtain a copy of the License at
  5. http://www.mozilla.org/MPL/
  6. Software distributed under the License is distributed on an "AS IS" basis,
  7. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
  8. the specific language governing rights and limitations under the License.
  9. The Original Code is: SynHighlighterMulti.pas, released 2000-06-23.
  10. The Original Code is based on mwMultiSyn.pas by Willo van der Merwe, part of the
  11. mwEdit component suite.
  12. Contributors to the SynEdit and mwEdit projects are listed in the
  13. Contributors.txt file.
  14. Alternatively, the contents of this file may be used under the terms of the
  15. GNU General Public License Version 2 or later (the "GPL"), in which case
  16. the provisions of the GPL are applicable instead of those above.
  17. If you wish to allow use of your version of this file only under the terms
  18. of the GPL and not to allow others to use your version of this file
  19. under the MPL, indicate your decision by deleting the provisions above and
  20. replace them with the notice and other provisions required by the GPL.
  21. If you do not delete the provisions above, a recipient may use your version
  22. of this file under either the MPL or the GPL.
  23. You may retrieve the latest version of this file at the SynEdit home page,
  24. located at http://SynEdit.SourceForge.net
  25. -------------------------------------------------------------------------------}
  26. {
  27. @created(1999, converted to SynEdit 2000-06-23)
  28. @author(Willo van der Merwe <willo@wack.co.za>
  29. @converted to SynEdit by David Muir <dhm@dmsoftware.co.uk>)
  30. @mostly rewritten for Lazarus by M. Friebe 04/2010
  31. The SynHighlighterMulti unit provides SynEdit with a multiple-highlighter syntax highlighter.
  32. This highlighter can be used to highlight text in which several languages are present, such as HTML.
  33. For example, in HTML as well as HTML tags there can also be JavaScript and/or VBScript present.
  34. }
  35. unit SynHighlighterMulti;
  36. {$I synedit.inc}
  37. {$IFDEF SynDebug}
  38. {$DEFINE SynDebugMultiHL}
  39. {$ENDIF}
  40. interface
  41. uses
  42. Classes, Graphics, SysUtils, LCLProc, math,
  43. SynRegExpr, SynEditStrConst, SynEditTypes, SynEditTextBase,
  44. SynEditHighlighter,
  45. {$IFDEF SynDebugMultiHL}LazLoggerBase{$ELSE}LazLoggerDummy{$ENDIF}, LazUTF8
  46. ;
  47. type
  48. TSynHighlighterMultiScheme=class;
  49. TSynMultiSyn = class;
  50. TSynHLightMultiVirtualSection = record
  51. // X(Char): 1-based
  52. // Y(Line): 0-based
  53. StartPos, EndPos: TPoint;
  54. TokenStartPos, TokenEndPos: Integer;
  55. VirtualLine: Integer;
  56. end;
  57. PSynHLightMultiVirtualSection = ^TSynHLightMultiVirtualSection;
  58. { TSynHLightMultiSectionList }
  59. (* List of all parts of the original TextBuffer, which are to be scanned by one highlighter *)
  60. TSynHLightMultiSectionList=class(TSynEditStorageMem)
  61. private
  62. function GetSection(Index: Integer): TSynHLightMultiVirtualSection;
  63. function GetSectionPointer(Index: Integer): PSynHLightMultiVirtualSection;
  64. procedure SetSection(Index: Integer; const AValue: TSynHLightMultiVirtualSection);
  65. public
  66. constructor Create;
  67. procedure Debug;
  68. procedure Insert(AnIndex: Integer; AnSection: TSynHLightMultiVirtualSection);
  69. procedure Delete(AnIndex: Integer);
  70. property Sections[Index: Integer]: TSynHLightMultiVirtualSection
  71. read GetSection write SetSection; default;
  72. property PSections[Index: Integer]: PSynHLightMultiVirtualSection
  73. read GetSectionPointer;
  74. function IndexOfFirstSectionAtLineIdx(ALineIdx: Integer; ACharPos: Integer = -1;
  75. UseNext: Boolean = True): Integer;
  76. function IndexOfFirstSectionAtVirtualIdx(ALineIdx: Integer; AGetLastSection: Boolean = False): Integer;
  77. function VirtualIdxToRealIdx(AVLineIdx: Integer): Integer;
  78. end;
  79. { TSynHLightMultiVirtualLines }
  80. TSynHLightMultiVirtualLines=class(TSynEditStringsBase)
  81. private
  82. FFirstHLChangedLine: Integer;
  83. FLastHLChangedLine: Integer;
  84. FRangeList: TSynManagedStorageMemList;
  85. FRealLines: TSynEditStringsBase;
  86. FScheme: TSynHighlighterMultiScheme;
  87. FSectionList: TSynHLightMultiSectionList;
  88. FRScanStartedWithLineCount: Integer;
  89. FRScanStartedAtVLine: Integer;
  90. FRegionScanStartRangeIndex: Integer;
  91. FRegionScanRangeIndex: Integer;
  92. FLastPCharLine: String;
  93. protected
  94. function GetRange(Index: Pointer): TSynManagedStorageMem; override;
  95. procedure PutRange(Index: Pointer; const ARange: TSynManagedStorageMem); override;
  96. function Get(Index: integer): string; override;
  97. procedure Put(Index: integer; const S: string); override; // should not be called ever
  98. function GetCount: integer; override;
  99. public
  100. constructor Create(ALines: TSynEditStringsBase);
  101. destructor Destroy; override;
  102. procedure Clear; override; // should not be called ever
  103. procedure Delete(Index: Integer); override; // should not be called ever
  104. procedure Insert(Index: Integer; const S: string); override; // should not be called ever
  105. function GetPChar(ALineIndex: Integer; out ALen: Integer): PChar; override; // experimental
  106. procedure SendHighlightChanged(aIndex, aCount: Integer); override;
  107. procedure PrepareRegionScan(AStartLineIdx: Integer);
  108. procedure FinishRegionScan(AEndLineIdx: Integer);
  109. procedure RegionScanUpdateFirstRegionEnd(AnEndPoint: TPoint; ATokenEndPos: Integer);
  110. procedure RegionScanUpdateOrInsertRegion(AStartPoint, AnEndPoint: TPoint;
  111. ATokenStartPos, ATokenEndPos: Integer);
  112. procedure RegionScanUpdateLastRegionStart(AStartPoint: TPoint;
  113. ATokenStartPos: Integer; ALineIndex: Integer);
  114. procedure RealLinesInserted(AIndex, ACount: Integer);
  115. procedure RealLinesDeleted(AIndex, ACount: Integer);
  116. procedure RealLinesChanged(AIndex, ACount: Integer);
  117. procedure ResetHLChangedLines;
  118. property FirstHLChangedLine: Integer read FFirstHLChangedLine;
  119. property LastHLChangedLine: Integer read FLastHLChangedLine;
  120. property SectionList: TSynHLightMultiSectionList read FSectionList;
  121. property Scheme: TSynHighlighterMultiScheme
  122. read FScheme write FScheme;
  123. end;
  124. { TSynHLightMultiVirtualLinesList }
  125. TSynHLightMultiVirtualLinesList=class(TFPList)
  126. private
  127. function GetVLines(Index: Integer): TSynHLightMultiVirtualLines;
  128. procedure PutVLines(Index: Integer; const AValue: TSynHLightMultiVirtualLines);
  129. public
  130. property Items[Index: Integer]: TSynHLightMultiVirtualLines
  131. read GetVLines write PutVLines; default;
  132. end;
  133. TOnCheckMarker=procedure(Sender: TObject; var StartPos, MarkerLen: Integer;
  134. var MarkerText: String) of object;
  135. { TSynHighlighterMultiScheme }
  136. TSynHighlighterMultiScheme = class(TCollectionItem)
  137. private
  138. FNeedHLScan: Boolean;
  139. FStartExpr, FEndExpr: string;
  140. FConvertedStartExpr, FConvertedEndExpr: String;
  141. FStartExprScanner, FEndExprScanner: TRegExpr;
  142. FStartLineSet, FEndLineSet: Boolean;
  143. FLastMatchLen: Integer;
  144. FHighlighter: TSynCustomHighLighter;
  145. fMarkerAttri: TSynHighlighterAttributes;
  146. fSchemeName: TComponentName;
  147. fCaseSensitive: Boolean;
  148. fOnCheckStartMarker: TOnCheckMarker;
  149. fOnCheckEndMarker: TOnCheckMarker;
  150. FVirtualLines: TSynHLightMultiVirtualLines;
  151. function GetConvertedLine: String;
  152. function GetConvertedEndExpr: String;
  153. function GetConvertedStartExpr: String;
  154. procedure MarkerAttriChanged(Sender: TObject);
  155. procedure SetMarkerAttri(const Value: TSynHighlighterAttributes);
  156. procedure SetHighlighter(const Value: TSynCustomHighlighter);
  157. procedure SetEndExpr(const Value: string);
  158. procedure SetStartExpr(const Value: string);
  159. procedure SetCaseSensitive(const Value: Boolean);
  160. procedure SetVirtualLines(const AValue: TSynHLightMultiVirtualLines);
  161. protected
  162. function GetDisplayName: String; override;
  163. procedure SetDisplayName(const Value: String); override;
  164. public
  165. constructor Create(TheCollection: TCollection); override;
  166. destructor Destroy; override;
  167. public
  168. procedure ClearLinesSet;
  169. function FindStartPosInLine(ASearchPos: Integer): Integer;
  170. function FindEndPosInLine(ASearchPos: Integer): Integer;
  171. property LastMatchLen: Integer read FLastMatchLen;
  172. property NeedHLScan: Boolean read FNeedHLScan;
  173. public
  174. property VirtualLines: TSynHLightMultiVirtualLines
  175. read FVirtualLines write SetVirtualLines;
  176. published
  177. property CaseSensitive: Boolean read fCaseSensitive write SetCaseSensitive
  178. default True;
  179. property StartExpr: string read fStartExpr write SetStartExpr;
  180. property EndExpr: string read fEndExpr write SetEndExpr;
  181. property Highlighter: TSynCustomHighlighter read fHighlighter
  182. write SetHighlighter;
  183. property MarkerAttri: TSynHighlighterAttributes read fMarkerAttri
  184. write SetMarkerAttri;
  185. property SchemeName: TComponentName read fSchemeName write fSchemeName;
  186. property OnCheckStartMarker: TOnCheckMarker read fOnCheckStartMarker write fOnCheckStartMarker;
  187. property OnCheckEndMarker: TOnCheckMarker read fOnCheckEndMarker write fOnCheckEndMarker;
  188. end;
  189. { TSynHighlighterMultiSchemeList }
  190. TSynHighlighterMultiSchemeList = class(TCollection)
  191. private
  192. FCurrentLine, FConvertedCurrentLine: String;
  193. FOwner: TSynMultiSyn;
  194. function GetConvertedCurrentLine: String;
  195. function GetItems(Index: integer): TSynHighlighterMultiScheme;
  196. procedure SetCurrentLine(const AValue: String);
  197. procedure SetItems(Index: integer; const Value: TSynHighlighterMultiScheme);
  198. protected
  199. function GetOwner: TPersistent; override;
  200. procedure Update(Item: TCollectionItem); override;
  201. procedure Notify(Item: TCollectionItem;Action: TCollectionNotification); override;
  202. public
  203. constructor Create(aOwner: TSynMultiSyn);
  204. property Items[aIndex: integer]: TSynHighlighterMultiScheme read GetItems write SetItems;
  205. default;
  206. function IndexOf(AnItem: TSynHighlighterMultiScheme): Integer;
  207. public
  208. property ConvertedCurrentLine: String read GetConvertedCurrentLine;
  209. property CurrentLine: String read FCurrentLine write SetCurrentLine;
  210. property Owner: TSynMultiSyn read FOwner;
  211. end;
  212. { TSynHighlighterMultiRangeList }
  213. TSynHighlighterMultiRangeList = class(TSynHighlighterRangeList)
  214. private
  215. FLines: TSynEditStringsBase;
  216. FDefaultVirtualLines: TSynHLightMultiVirtualLines;
  217. FVirtualLines: TSynHLightMultiVirtualLinesList;
  218. function GetVirtualLines(Index: TSynHighlighterMultiScheme): TSynHLightMultiVirtualLines;
  219. protected
  220. procedure LineTextChanged(AIndex: Integer; ACount: Integer = 1); override;
  221. procedure InsertedLines(AIndex, ACount: Integer); override;
  222. procedure DeletedLines(AIndex, ACount: Integer); override;
  223. public
  224. constructor Create(ALines: TSynEditStringsBase);
  225. destructor Destroy; override;
  226. procedure ClearVLines;
  227. procedure UpdateForScheme(AScheme: TSynHighlighterMultiSchemeList);
  228. procedure CleanUpForScheme(AScheme: TSynHighlighterMultiSchemeList);
  229. procedure CopyToScheme(AScheme: TSynHighlighterMultiSchemeList);
  230. property DefaultVirtualLines: TSynHLightMultiVirtualLines read FDefaultVirtualLines;
  231. property VirtualLines[Index: TSynHighlighterMultiScheme]: TSynHLightMultiVirtualLines
  232. read GetVirtualLines; // write SetVirtualLines;
  233. end;
  234. TRunSectionInfo = record
  235. SectionIdx: Integer;
  236. VirtualStartPos: Integer; // Position in the Virtual line (without token)
  237. FirstChar, LastChar: Integer; // Position of the Real Line that is mapped
  238. TokenFirstChar, TokenLastChar: Integer;
  239. end;
  240. { TSynMultiSyn }
  241. TSynMultiSyn = class(TSynCustomHighLighter)
  242. private
  243. FDefaultLanguageName: String;
  244. FCurScheme: TSynHighlighterMultiScheme;
  245. function GetCurrentRanges: TSynHighlighterMultiRangeList;
  246. function GetDefaultVirtualLines: TSynHLightMultiVirtualLines;
  247. function GetKnownMultiRanges(Index: Integer): TSynHighlighterMultiRangeList;
  248. procedure SetDefaultHighlighter(const Value: TSynCustomHighLighter);
  249. procedure SetSchemes(const Value: TSynHighlighterMultiSchemeList);
  250. function CurrentVirtualLines: TSynHLightMultiVirtualLines;
  251. protected
  252. FSchemes: TSynHighlighterMultiSchemeList;
  253. FDefaultHighlighter: TSynCustomHighLighter;
  254. FLine: string;
  255. FCurLineIndex, FLineLen: Integer;
  256. FTokenPos: integer;
  257. FTokenKind: integer;
  258. FTokenAttr: TSynHighlighterAttributes;
  259. FRun: Integer;
  260. FRunSectionInfo: Array of TRunSectionInfo;
  261. FSampleSource: string;
  262. function GetIdentChars: TSynIdentChars; override;
  263. function GetDefaultAttribute(Index: integer): TSynHighlighterAttributes; override;
  264. function GetAttribCount: integer; override;
  265. function GetAttribute(idx: integer): TSynHighlighterAttributes; override;
  266. function GetSampleSource: string; override;
  267. procedure SetSampleSource(Value: string); override;
  268. procedure HookHighlighter(aHL: TSynCustomHighlighter);
  269. procedure UnhookHighlighter(aHL: TSynCustomHighlighter);
  270. procedure Notification(aComp: TComponent; aOp: TOperation); override;
  271. function CreateRangeList(ALines: TSynEditStringsBase): TSynHighlighterRangeList; override;
  272. procedure BeforeDetachedFromRangeList(ARangeList: TSynHighlighterRangeList); override;
  273. procedure SetCurrentLines(const AValue: TSynEditStringsBase); override;
  274. procedure SchemeItemChanged(Item: TObject);
  275. procedure SchemeChanged;
  276. procedure DetachHighlighter(AHighlighter: TSynCustomHighlighter; AScheme: TSynHighlighterMultiScheme);
  277. procedure AttachHighlighter(AHighlighter: TSynCustomHighlighter; AScheme: TSynHighlighterMultiScheme);
  278. function PerformScan(StartIndex, EndIndex: Integer; ForceEndIndex: Boolean = False): Integer; override;
  279. property CurrentRanges: TSynHighlighterMultiRangeList read GetCurrentRanges;
  280. property KnownRanges[Index: Integer]: TSynHighlighterMultiRangeList read GetKnownMultiRanges;
  281. public
  282. class function GetLanguageName: string; override;
  283. public
  284. constructor Create(AOwner: TComponent); override;
  285. destructor Destroy; override;
  286. procedure Next; override;
  287. function GetEol: Boolean; override;
  288. function GetToken: string; override;
  289. procedure GetTokenEx(out TokenStart: PChar; out TokenLength: integer); override;
  290. function GetTokenAttribute: TSynHighlighterAttributes; override;
  291. function GetTokenKind: integer; override;
  292. function GetTokenPos: Integer; override; // 0-based
  293. procedure SetLine(const NewValue: string; LineNumber: Integer); override;
  294. function GetRange: Pointer; override;
  295. procedure SetRange(Value: Pointer); override;
  296. procedure ResetRange; override;
  297. public
  298. property DefaultVirtualLines: TSynHLightMultiVirtualLines read GetDefaultVirtualLines;
  299. published
  300. property Schemes: TSynHighlighterMultiSchemeList read fSchemes write SetSchemes;
  301. property DefaultHighlighter: TSynCustomHighLighter read fDefaultHighlighter
  302. write SetDefaultHighlighter;
  303. property DefaultLanguageName: String read fDefaultLanguageName
  304. write fDefaultLanguageName;
  305. end;
  306. function dbgs(const ASect: TSynHLightMultiVirtualSection): String; overload;
  307. implementation
  308. var
  309. SYNDEBUG_MULTIHL: PLazLoggerLogGroup;
  310. const
  311. TokenKindPerHighlighter = 100;
  312. operator > (p1, p2 : TPoint) b : boolean;
  313. begin
  314. Result := (p1.y > p2.y) or ( (p1.y = p2.y) and (p1.x > p2.x) );
  315. end;
  316. operator >= (p1, p2 : TPoint) b : boolean;
  317. begin
  318. Result := (p1.y > p2.y) or ( (p1.y = p2.y) and (p1.x >= p2.x) );
  319. end;
  320. operator < (p1, p2 : TPoint) b : boolean;
  321. begin
  322. Result := (p1.y < p2.y) or ( (p1.y = p2.y) and (p1.x < p2.x) );
  323. end;
  324. function dbgs(const ASect: TSynHLightMultiVirtualSection): String;
  325. begin
  326. Result := Format('Start=%s, End=%s, VLine=%d, TokStart=%d, TokEnd=%d',
  327. [dbgs(ASect.StartPos), dbgs(ASect.EndPos), ASect.VirtualLine, ASect.TokenStartPos, ASect.TokenEndPos]);
  328. end;
  329. { TSynHLightMultiSectionList }
  330. function TSynHLightMultiSectionList.GetSection(Index: Integer): TSynHLightMultiVirtualSection;
  331. begin
  332. {$IFDEF AssertSynMemIndex}
  333. if (Index < 0) or (Index >= Count) then
  334. raise Exception.Create(Format('TSynHLightMultiSectionList.GetSection - Bad Index cnt= %d idx= %d',[Count, Index]));
  335. {$ENDIF}
  336. Result := PSynHLightMultiVirtualSection(ItemPointer[Index])^;
  337. end;
  338. function TSynHLightMultiSectionList.GetSectionPointer(Index: Integer): PSynHLightMultiVirtualSection;
  339. begin
  340. {$IFDEF AssertSynMemIndex}
  341. if (Index < 0) or (Index >= Count) then
  342. raise Exception.Create(Format('TSynHLightMultiSectionList.GetSectionPointer - Bad Index cnt= %d idx= %d',[Count, Index]));
  343. {$ENDIF}
  344. Result := PSynHLightMultiVirtualSection(ItemPointer[Index]);
  345. end;
  346. procedure TSynHLightMultiSectionList.SetSection(Index: Integer;
  347. const AValue: TSynHLightMultiVirtualSection);
  348. begin
  349. {$IFDEF AssertSynMemIndex}
  350. if (Index < 0) or (Index >= Count) then
  351. raise Exception.Create(Format('TSynHLightMultiSectionList.SetSection - Bad Index cnt= %d idx= %d',[Count, Index]));
  352. {$ENDIF}
  353. PSynHLightMultiVirtualSection(ItemPointer[Index])^ := AValue;
  354. end;
  355. constructor TSynHLightMultiSectionList.Create;
  356. begin
  357. inherited;
  358. ItemSize := SizeOf(TSynHLightMultiVirtualSection);
  359. end;
  360. procedure TSynHLightMultiSectionList.Debug;
  361. var
  362. i: Integer;
  363. begin
  364. debugln(SYNDEBUG_MULTIHL, ['SectionList ', dbgs(self), ' Count=', Count]);
  365. for i := 0 to Count - 1 do
  366. debugln(SYNDEBUG_MULTIHL, [' ', i, ': ', dbgs(PSections[i]^)]);
  367. end;
  368. procedure TSynHLightMultiSectionList.Insert(AnIndex: Integer;
  369. AnSection: TSynHLightMultiVirtualSection);
  370. begin
  371. InsertRows(AnIndex, 1);
  372. Sections[AnIndex] := AnSection;
  373. end;
  374. procedure TSynHLightMultiSectionList.Delete(AnIndex: Integer);
  375. begin
  376. DeleteRows(AnIndex, 1);
  377. if (Capacity > 16) and (Capacity > (Count * 2)) then
  378. Capacity := Capacity - (Count div 2);
  379. end;
  380. function TSynHLightMultiSectionList.IndexOfFirstSectionAtLineIdx(ALineIdx: Integer;
  381. ACharPos: Integer = -1; UseNext: Boolean = True): Integer;
  382. var
  383. p, p1, p2: Integer;
  384. s: PSynHLightMultiVirtualSection;
  385. begin
  386. Result := -1;
  387. p2 := Count;
  388. if p2 = 0 then begin
  389. if UseNext then Result := 0;
  390. exit;
  391. end;
  392. p1 := p2 div 2;
  393. dec(p2);
  394. s := PSynHLightMultiVirtualSection(ItemPointer[p1]);
  395. if (ALineIdx < s^.StartPos.y) or ( (ALineIdx = s^.StartPos.y) and (ACharPos < s^.StartPos.x) )
  396. then begin // target is in 0 .. p1-1
  397. p2 := p1 - 1;
  398. p1 := 0;
  399. end;
  400. while (p1 < p2) do begin
  401. p := (p1 + p2 + 1) div 2;
  402. s := PSynHLightMultiVirtualSection(ItemPointer[p]);
  403. if (ALineIdx < s^.StartPos.y) or
  404. ( (ALineIdx = s^.StartPos.y) and (ACharPos < s^.StartPos.x) )
  405. then
  406. p2 := p - 1 // target is in p1 .. p-1
  407. else
  408. p1 := p; // target is in p .. p2
  409. end;
  410. s := PSynHLightMultiVirtualSection(ItemPointer[p1]);
  411. if ( (s^.StartPos.y > ALineIdx) or ((s^.StartPos.y = ALineIdx) and (s^.StartPos.x > ACharPos)) )
  412. then begin
  413. dec(p1);
  414. if p1 >= 0 then
  415. s := PSynHLightMultiVirtualSection(ItemPointer[p1]);
  416. end;
  417. if (p1 < 0) or (s^.EndPos.y < ALineIdx) or
  418. ( (s^.EndPos.y = ALineIdx) and (s^.EndPos.x < ACharPos) )
  419. then begin
  420. if UseNext then
  421. Result := p1 + 1 // Could be p1 = Count // behind end
  422. else
  423. Result := -1;
  424. end
  425. else begin
  426. Result := p1;
  427. end;
  428. end;
  429. function TSynHLightMultiSectionList.IndexOfFirstSectionAtVirtualIdx(ALineIdx: Integer;
  430. AGetLastSection: Boolean): Integer;
  431. var
  432. p, p1, p2: Integer;
  433. s: PSynHLightMultiVirtualSection;
  434. begin
  435. Result := -1;
  436. p2 := Count;
  437. if p2 = 0 then
  438. exit;
  439. p1 := p2 div 2;
  440. dec(p2);
  441. s := PSynHLightMultiVirtualSection(ItemPointer[p1]);
  442. if (ALineIdx < s^.VirtualLine) then begin
  443. p2 := p1 - 1; // target is in 0 .. p1-1
  444. p1 := 0;
  445. end;
  446. while (p1 < p2) do begin
  447. p := (p1 + p2 + 1) div 2;
  448. s := PSynHLightMultiVirtualSection(ItemPointer[p]);
  449. if (ALineIdx < s^.VirtualLine) then
  450. p2 := p - 1 // target is in p1 .. p-1
  451. else
  452. p1 := p; // target is in p .. p2
  453. end;
  454. s := PSynHLightMultiVirtualSection(ItemPointer[p1]);
  455. if (ALineIdx = s^.VirtualLine) and (not AGetLastSection) then begin
  456. while (p1 >= 0) and (s^.VirtualLine = ALineIdx) do begin
  457. dec(p1);
  458. if p1 >= 0 then
  459. s := PSynHLightMultiVirtualSection(ItemPointer[p1]);
  460. end;
  461. if (p1 < 0) or (s^.VirtualLine + s^.EndPos.y - s^.StartPos.y < ALineIdx) then
  462. inc(p1);
  463. end else begin
  464. p2 := Count;
  465. while (p1 < p2) and (s^.VirtualLine < ALineIdx) do begin
  466. inc(p1);
  467. if p1 < p2 then
  468. s := PSynHLightMultiVirtualSection(ItemPointer[p1]);
  469. end;
  470. if (p1 = p2) or (s^.VirtualLine > ALineIdx) then
  471. dec(p1);
  472. end;
  473. Result := p1;
  474. end;
  475. function TSynHLightMultiSectionList.VirtualIdxToRealIdx(AVLineIdx: Integer): Integer;
  476. var
  477. i: Integer;
  478. begin
  479. if Count = 0 then exit(AVLineIdx);
  480. i := IndexOfFirstSectionAtVirtualIdx(AVLineIdx, True);
  481. if i < 0 then exit(AVLineIdx);
  482. Result := PSections[i]^.StartPos.y + AVLineIdx;
  483. end;
  484. { TSynHLightMultiVirtualLines }
  485. function TSynHLightMultiVirtualLines.GetRange(Index: Pointer): TSynManagedStorageMem;
  486. begin
  487. Result := FRangeList[Index];
  488. end;
  489. procedure TSynHLightMultiVirtualLines.PutRange(Index: Pointer; const ARange: TSynManagedStorageMem);
  490. begin
  491. FRangeList[Index] := ARange;
  492. if ARange <> nil then begin
  493. ARange.Capacity := Count;
  494. ARange.Count := Count;
  495. end;
  496. end;
  497. function TSynHLightMultiVirtualLines.Get(Index: integer): string;
  498. var
  499. i, i2, c1, c2: Integer;
  500. s: TSynHLightMultiVirtualSection;
  501. t: String;
  502. begin
  503. i := FSectionList.IndexOfFirstSectionAtVirtualIdx(Index);
  504. if (i < 0) or (i >= FSectionList.Count) then
  505. exit('');
  506. s := FSectionList[i];
  507. i2 := s.StartPos.y + Index - s.VirtualLine;
  508. t := FRealLines[i2];
  509. c1 := 1;
  510. if Index = s.VirtualLine then c1 := s.StartPos.x;
  511. c2 := length(t);
  512. if Index = s.VirtualLine + s.EndPos.y - s.StartPos.y then c2 := s.EndPos.x;
  513. Result := copy(t, c1, c2 - c1 + 1);
  514. inc(i);
  515. while (i < FSectionList.Count) do begin
  516. s := FSectionList[i];
  517. if Index <> s.VirtualLine then break;
  518. t := FRealLines[s.StartPos.y];
  519. c1 := s.StartPos.x;
  520. c2 := length(t);
  521. if s.EndPos.y = s.StartPos.y then c2 := s.EndPos.x;
  522. Result := Result + copy(t, c1, c2 - c1 + 1);
  523. inc(i);
  524. end;
  525. end;
  526. procedure TSynHLightMultiVirtualLines.Put(Index: integer; const S: string);
  527. begin
  528. raise Exception.Create('Not allowed');
  529. end;
  530. procedure TSynHLightMultiVirtualLines.Clear;
  531. begin
  532. raise Exception.Create('Not allowed');
  533. end;
  534. procedure TSynHLightMultiVirtualLines.Delete(Index: Integer);
  535. begin
  536. raise Exception.Create('Not allowed');
  537. end;
  538. procedure TSynHLightMultiVirtualLines.Insert(Index: Integer; const S: string);
  539. begin
  540. raise Exception.Create('Not allowed');
  541. end;
  542. function TSynHLightMultiVirtualLines.GetPChar(ALineIndex: Integer; out ALen: Integer): PChar;
  543. begin
  544. FLastPCharLine := Get(ALineIndex);
  545. ALen := length(FLastPCharLine);
  546. Result := PChar(FLastPCharLine);
  547. end;
  548. function TSynHLightMultiVirtualLines.GetCount: integer;
  549. var
  550. s: TSynHLightMultiVirtualSection;
  551. begin
  552. if FSectionList.Count = 0 then
  553. exit(0);
  554. s := FSectionList[FSectionList.Count - 1];
  555. Result := s.VirtualLine + 1 + s.EndPos.y - s.StartPos.y;
  556. end;
  557. procedure TSynHLightMultiVirtualLines.SendHighlightChanged(aIndex, aCount: Integer);
  558. begin
  559. if (FFirstHLChangedLine < 0) or (FFirstHLChangedLine > aIndex) then
  560. FFirstHLChangedLine := aIndex;
  561. if (FLastHLChangedLine < aIndex + aCount - 1) then
  562. FLastHLChangedLine := aIndex + aCount - 1;
  563. end;
  564. constructor TSynHLightMultiVirtualLines.Create(ALines: TSynEditStringsBase);
  565. begin
  566. FRangeList := TSynManagedStorageMemList.Create;
  567. FSectionList := TSynHLightMultiSectionList.Create;
  568. FRealLines := ALines;
  569. end;
  570. destructor TSynHLightMultiVirtualLines.Destroy;
  571. begin
  572. inherited Destroy;
  573. FreeAndNil(FSectionList);
  574. FreeAndNil(FRangeList);
  575. end;
  576. procedure TSynHLightMultiVirtualLines.PrepareRegionScan(AStartLineIdx: Integer);
  577. var
  578. p: PSynHLightMultiVirtualSection;
  579. begin
  580. FRegionScanRangeIndex := FSectionList.IndexOfFirstSectionAtLineIdx(AStartLineIdx, -1 ,True);
  581. FRegionScanStartRangeIndex := FRegionScanRangeIndex;
  582. FRScanStartedWithLineCount := Count;
  583. if FRegionScanRangeIndex < FSectionList.Count then
  584. FRScanStartedAtVLine := FSectionList[FRegionScanRangeIndex].VirtualLine
  585. else if FSectionList.Count = 0 then
  586. FRScanStartedAtVLine := 0
  587. else begin
  588. p := FSectionList.PSections[FSectionList.Count - 1];
  589. FRScanStartedAtVLine := p^.VirtualLine + p^.EndPos.y - p^.StartPos.y + 1;
  590. end;
  591. {$IFDEF SynDebugMultiHL}
  592. debugln(SYNDEBUG_MULTIHL, ['TSynHLightMultiVirtualLines.PrepareRegionScan ', dbgs(self),
  593. ' FRegionScanRangeIndex=', FRegionScanRangeIndex, ' FRScanStartedWithLineCount=', FRScanStartedWithLineCount,
  594. ' FSectionList.Count=', FSectionList.Count, ' FRScanStartedAtVLine=', FRScanStartedAtVLine
  595. ]);
  596. {$ENDIF}
  597. end;
  598. procedure TSynHLightMultiVirtualLines.FinishRegionScan(AEndLineIdx: Integer);
  599. var
  600. i, NewVLine, LastVline, LastEnd: Integer;
  601. s: TSynHLightMultiVirtualSection;
  602. VDiff: Integer;
  603. begin
  604. {$IFDEF SynDebugMultiHL}
  605. debugln(SYNDEBUG_MULTIHL, ['TSynHLightMultiVirtualLines.FinishRegionScan AEndLineIdx=', AEndLineIdx]);
  606. {$ENDIF}
  607. while (FRegionScanRangeIndex < FSectionList.Count) and
  608. (FSectionList.Sections[FRegionScanRangeIndex].StartPos.y <= AEndLineIdx)
  609. do
  610. FSectionList.Delete(FRegionScanRangeIndex);
  611. VDiff := 0;
  612. {$IFDEF SynDebugMultiHL}
  613. DebugLn(SYNDEBUG_MULTIHL, ['***** ', FRegionScanStartRangeIndex, ' cnt ', FSectionList.Count]);
  614. {$ENDIF}
  615. if FRegionScanStartRangeIndex < FSectionList.Count then begin
  616. // fix virtual lines on sections
  617. if (FRegionScanStartRangeIndex > 0) then begin
  618. s := FSectionList.Sections[FRegionScanStartRangeIndex-1];
  619. NewVLine := s.VirtualLine + s.EndPos.y - s.StartPos.y;
  620. {$IFDEF SynDebugMultiHL}
  621. DebugLn(SYNDEBUG_MULTIHL, ['A ', NewVLine]);
  622. {$ENDIF}
  623. LastEnd := s.EndPos.y;
  624. end
  625. else begin
  626. NewVLine := 0;
  627. {$IFDEF SynDebugMultiHL}
  628. DebugLn(SYNDEBUG_MULTIHL, ['B ', NewVLine]);
  629. {$ENDIF}
  630. LastEnd := FSectionList.Sections[FRegionScanStartRangeIndex].StartPos.y;
  631. end;
  632. LastVline := NewVLine;
  633. for i := FRegionScanStartRangeIndex to FSectionList.Count - 1 do begin
  634. s := FSectionList.Sections[i];
  635. if s.StartPos.y > LastEnd then
  636. inc(NewVLine);
  637. if i = FRegionScanRangeIndex then
  638. VDiff := NewVLine - s.VirtualLine; // adjust ranges
  639. FSectionList.PSections[i]^.VirtualLine := NewVLine;
  640. NewVLine := NewVLine + s.EndPos.y - s.StartPos.y;
  641. LastEnd := s.EndPos.y;
  642. end;
  643. end
  644. else
  645. LastVline := 0; // ToDo: Initialize LastVline properly.
  646. if VDiff = 0 then
  647. VDiff := Count - FRScanStartedWithLineCount;
  648. if VDiff < 0 then begin
  649. FRangeList.ChildDeleteRows(FRScanStartedAtVLine, -VDiff);
  650. FRangeList.CallDeletedLines(FRScanStartedAtVLine, -VDiff);
  651. end
  652. else if VDiff > 0 then begin
  653. FRangeList.ChildInsertRows(FRScanStartedAtVLine, VDiff);
  654. FRangeList.CallInsertedLines(FRScanStartedAtVLine, VDiff);
  655. end;
  656. FRangeList.CallLineTextChanged(FRScanStartedAtVLine, LastVline - FRScanStartedAtVLine + 1);
  657. end;
  658. procedure TSynHLightMultiVirtualLines.RegionScanUpdateFirstRegionEnd(AnEndPoint: TPoint;
  659. ATokenEndPos: Integer);
  660. var
  661. p: PSynHLightMultiVirtualSection;
  662. begin
  663. p := FSectionList.PSections[FRegionScanRangeIndex];
  664. {$IFDEF SynDebugMultiHL}
  665. debugln(SYNDEBUG_MULTIHL, ['TSynHLightMultiVirtualLines.RegionScanUpdateFirstRegionEnd',
  666. ' AnEndPoint', dbgs(AnEndPoint), ' ATokenEndPos=', ATokenEndPos, ' FRegionScanRangeIndex=', FRegionScanRangeIndex,
  667. ' p^.StartPos=', dbgs(p^.StartPos), ' p^.EndPos=', dbgs(p^.EndPos)
  668. ]);
  669. {$ENDIF}
  670. p^.EndPos := AnEndPoint;
  671. p^.TokenEndPos := ATokenEndPos;
  672. inc(FRegionScanRangeIndex);
  673. end;
  674. procedure TSynHLightMultiVirtualLines.RegionScanUpdateOrInsertRegion(AStartPoint,
  675. AnEndPoint: TPoint; ATokenStartPos, ATokenEndPos: Integer);
  676. var
  677. Sect: TSynHLightMultiVirtualSection;
  678. p: PSynHLightMultiVirtualSection;
  679. begin
  680. {$IFDEF SynDebugMultiHL}
  681. debugln(SYNDEBUG_MULTIHL, ['TSynHLightMultiVirtualLines.RegionScanUpdateOrInsertRegion',
  682. ' AStartPoint=', dbgs(AStartPoint), ' AnEndPoint=', dbgs(AnEndPoint),
  683. ' ATokenStartPos=', ATokenStartPos, ' ATokenEndPos=', ATokenEndPos,
  684. ' FRegionScanRangeIndex=', FRegionScanRangeIndex
  685. ]);
  686. {$ENDIF}
  687. if (FRegionScanRangeIndex = FSectionList.Count)
  688. or (FSectionList.Sections[FRegionScanRangeIndex].StartPos > AnEndPoint)
  689. then begin
  690. Sect.StartPos := AStartPoint;
  691. Sect.EndPos := AnEndPoint;
  692. Sect.TokenStartPos := ATokenStartPos;
  693. Sect.TokenEndPos := ATokenEndPos;
  694. Sect.VirtualLine := 0;
  695. FSectionList.Insert(FRegionScanRangeIndex, Sect);
  696. end else begin
  697. p := FSectionList.PSections[FRegionScanRangeIndex];
  698. p^.StartPos := AStartPoint;
  699. p^.EndPos := AnEndPoint;
  700. p^.TokenStartPos := ATokenStartPos;
  701. p^.TokenEndPos := ATokenEndPos;
  702. end;
  703. inc(FRegionScanRangeIndex);
  704. end;
  705. procedure TSynHLightMultiVirtualLines.RegionScanUpdateLastRegionStart(AStartPoint: TPoint;
  706. ATokenStartPos: Integer; ALineIndex: Integer);
  707. var
  708. p: PSynHLightMultiVirtualSection;
  709. begin
  710. while (FRegionScanRangeIndex < FSectionList.Count) and
  711. (FSectionList.Sections[FRegionScanRangeIndex].EndPos.y <= ALineIndex)
  712. do
  713. FSectionList.Delete(FRegionScanRangeIndex);
  714. p := FSectionList.PSections[FRegionScanRangeIndex];
  715. p^.StartPos := AStartPoint;
  716. p^.TokenStartPos := ATokenStartPos;
  717. inc(FRegionScanRangeIndex);
  718. end;
  719. procedure TSynHLightMultiVirtualLines.RealLinesInserted(AIndex, ACount: Integer);
  720. var
  721. i, VLineDiff: Integer;
  722. s: TSynHLightMultiVirtualSection;
  723. p: PSynHLightMultiVirtualSection;
  724. begin
  725. i := FSectionList.IndexOfFirstSectionAtLineIdx(AIndex, -1, True);
  726. if i = FSectionList.Count then exit;
  727. VLineDiff := 0;
  728. s := FSectionList[i];
  729. if AIndex > s.StartPos.y then begin
  730. p := FSectionList.PSections[i];
  731. FRangeList.ChildInsertRows(p^.VirtualLine + AIndex - p^.StartPos.y, ACount);
  732. FRangeList.CallInsertedLines(p^.VirtualLine + AIndex - p^.StartPos.y, ACount);
  733. p^.EndPos.y := p^.EndPos.y + ACount;
  734. inc(i);
  735. VLineDiff := ACount;
  736. end;
  737. while i < FSectionList.Count do begin
  738. p := FSectionList.PSections[i];
  739. p^.StartPos.y := p^.StartPos.y + ACount;
  740. p^.EndPos.y := p^.EndPos.y + ACount;
  741. p^.VirtualLine := p^.VirtualLine + VLineDiff;
  742. inc(i);
  743. end;
  744. end;
  745. procedure TSynHLightMultiVirtualLines.RealLinesDeleted(AIndex, ACount: Integer);
  746. var
  747. i: Integer;
  748. CountInSection, PrevEndVLine, FirstVLine, VLineCount: Integer;
  749. p: PSynHLightMultiVirtualSection;
  750. procedure DelVLines;
  751. begin
  752. if VLineCount > 0 then begin
  753. FRangeList.ChildDeleteRows(FirstVLine, VLineCount);
  754. FRangeList.CallDeletedLines(FirstVLine, VLineCount);
  755. end;
  756. end;
  757. begin
  758. i := FSectionList.IndexOfFirstSectionAtLineIdx(AIndex, -1, True);
  759. if i = FSectionList.Count then exit;
  760. p := FSectionList.PSections[i];
  761. VLineCount := 0; // Count of deleted virtual lines
  762. FirstVLine := p^.VirtualLine; // First deleted virtual line
  763. PrevEndVLine := -1; // Keep track of overlap, when next section starts on the same V-line as previous sectian ends
  764. if AIndex > p^.StartPos.y then begin
  765. // Real-lines starting in the middle of the Section
  766. CountInSection := Min(AIndex + ACount, p^.EndPos.y + 1) - AIndex;
  767. FirstVLine := p^.VirtualLine + AIndex - p^.StartPos.y;
  768. PrevEndVLine := p^.VirtualLine + p^.EndPos.y - p^.EndPos.y;
  769. p^.EndPos.y := p^.EndPos.y - CountInSection;
  770. inc(i);
  771. if i = FSectionList.Count then begin
  772. DelVLines;
  773. exit;
  774. end;
  775. p := FSectionList.PSections[i];
  776. VLineCount := CountInSection;
  777. end;
  778. while p^.EndPos.y < AIndex + ACount do begin
  779. // Completly delete node (All Real lines deleted)
  780. VLineCount := VLineCount + p^.EndPos.y - p^.StartPos.y + 1;
  781. if PrevEndVLine = p^.VirtualLine then
  782. dec(VLineCount);
  783. PrevEndVLine := p^.VirtualLine + p^.EndPos.y - p^.EndPos.y;
  784. FSectionList.Delete(i);
  785. if i = FSectionList.Count then begin
  786. DelVLines;
  787. exit;
  788. end;
  789. p := FSectionList.PSections[i];
  790. end;
  791. if AIndex + ACount > p^.StartPos.y then begin
  792. // Some real-lines at the start of section are deleted
  793. p^.VirtualLine := p^.VirtualLine - VLineCount;
  794. CountInSection := ACount - (p^.StartPos.y - AIndex);
  795. VLineCount := VLineCount + CountInSection;
  796. if PrevEndVLine = p^.VirtualLine then
  797. dec(VLineCount);
  798. p^.StartPos.y := p^.StartPos.y - (ACount - CountInSection);
  799. p^.EndPos.y := p^.EndPos.y - ACount;
  800. assert(p^.EndPos.y >= p^.StartPos.y, 'TSynHLightMultiVirtualLines.RealLinesDeleted: p^.EndPos.y >= p^.StartPos.y');
  801. inc(i);
  802. end;
  803. // Adjust StartPos for all sections, after the deleted.
  804. while i < FSectionList.Count do begin
  805. p := FSectionList.PSections[i];
  806. p^.StartPos.y := p^.StartPos.y - ACount;
  807. p^.EndPos.y := p^.EndPos.y - ACount;
  808. p^.VirtualLine := p^.VirtualLine - VLineCount;
  809. inc(i);
  810. end;
  811. DelVLines;
  812. end;
  813. procedure TSynHLightMultiVirtualLines.RealLinesChanged(AIndex, ACount: Integer);
  814. var
  815. i, VLine1, VLine2: Integer;
  816. s: TSynHLightMultiVirtualSection;
  817. begin
  818. i := FSectionList.IndexOfFirstSectionAtLineIdx(AIndex, -1, True);
  819. if i = FSectionList.Count then exit;
  820. s := FSectionList[i];
  821. VLine1 := s.VirtualLine + AIndex - s.StartPos.y;
  822. i := FSectionList.IndexOfFirstSectionAtLineIdx(AIndex + ACount - 1, -1, True);
  823. if i = FSectionList.Count then
  824. VLine2 := Count-1
  825. else begin
  826. s := FSectionList[i];
  827. VLine2 := s.VirtualLine + AIndex + ACount - 1 - s.StartPos.y;
  828. end;
  829. FRangeList.CallLineTextChanged(VLine1, VLine2 - VLine1 + 1);
  830. end;
  831. procedure TSynHLightMultiVirtualLines.ResetHLChangedLines;
  832. begin
  833. FFirstHLChangedLine := -1;
  834. FLastHLChangedLine := -1;
  835. end;
  836. { TSynHLightMultiVirtualLinesList }
  837. function TSynHLightMultiVirtualLinesList.GetVLines(Index: Integer): TSynHLightMultiVirtualLines;
  838. begin
  839. Result := TSynHLightMultiVirtualLines(inherited Items[Index]);
  840. end;
  841. procedure TSynHLightMultiVirtualLinesList.PutVLines(Index: Integer;
  842. const AValue: TSynHLightMultiVirtualLines);
  843. begin
  844. inherited Items[Index] := AValue;
  845. end;
  846. { TSynHighlighterMultiRangeList }
  847. function TSynHighlighterMultiRangeList.GetVirtualLines(Index: TSynHighlighterMultiScheme): TSynHLightMultiVirtualLines;
  848. var
  849. i: Integer;
  850. begin
  851. Result := nil;
  852. for i := 0 to FVirtualLines.Count - 1 do
  853. if FVirtualLines[i].Scheme = Index then
  854. exit(FVirtualLines[i]);
  855. end;
  856. procedure TSynHighlighterMultiRangeList.LineTextChanged(AIndex: Integer; ACount: Integer);
  857. var
  858. i: Integer;
  859. begin
  860. inherited LineTextChanged(AIndex, ACount);
  861. for i := 0 to FVirtualLines.Count - 1 do
  862. FVirtualLines[i].RealLinesChanged(AIndex, ACount);
  863. FDefaultVirtualLines.RealLinesChanged(AIndex, ACount);
  864. end;
  865. procedure TSynHighlighterMultiRangeList.InsertedLines(AIndex, ACount: Integer);
  866. var
  867. i: Integer;
  868. begin
  869. inherited InsertedLines(AIndex, ACount);
  870. for i := 0 to FVirtualLines.Count - 1 do
  871. FVirtualLines[i].RealLinesInserted(AIndex, ACount);
  872. FDefaultVirtualLines.RealLinesInserted(AIndex, ACount);
  873. end;
  874. procedure TSynHighlighterMultiRangeList.DeletedLines(AIndex, ACount: Integer);
  875. var
  876. i: Integer;
  877. begin
  878. inherited DeletedLines(AIndex, ACount);
  879. for i := 0 to FVirtualLines.Count - 1 do
  880. FVirtualLines[i].RealLinesDeleted(AIndex, ACount);
  881. FDefaultVirtualLines.RealLinesDeleted(AIndex, ACount);
  882. end;
  883. constructor TSynHighlighterMultiRangeList.Create(ALines: TSynEditStringsBase);
  884. begin
  885. inherited Create;
  886. FLines := ALines;
  887. FVirtualLines := TSynHLightMultiVirtualLinesList.Create;
  888. end;
  889. destructor TSynHighlighterMultiRangeList.Destroy;
  890. begin
  891. inherited Destroy;
  892. ClearVLines;
  893. FreeAndNil(FVirtualLines);
  894. end;
  895. procedure TSynHighlighterMultiRangeList.ClearVLines;
  896. begin
  897. FreeAndNil(FDefaultVirtualLines);
  898. while FVirtualLines.Count > 0 do begin
  899. FVirtualLines[0].Destroy;
  900. FVirtualLines.Delete(0);
  901. end;
  902. FVirtualLines.Clear;
  903. end;
  904. procedure TSynHighlighterMultiRangeList.UpdateForScheme(AScheme: TSynHighlighterMultiSchemeList);
  905. var
  906. i: Integer;
  907. NewVline: TSynHLightMultiVirtualLines;
  908. begin
  909. for i := FVirtualLines.Count - 1 downto 0 do
  910. if AScheme.IndexOf(FVirtualLines[i].Scheme) < 0 then begin
  911. FVirtualLines[i].Destroy;
  912. FVirtualLines.Delete(i);
  913. end;
  914. if FDefaultVirtualLines = nil then
  915. FDefaultVirtualLines := TSynHLightMultiVirtualLines.Create(FLines);
  916. for i := 0 to AScheme.Count - 1 do
  917. if VirtualLines[AScheme[i]] = nil then begin
  918. NewVline := TSynHLightMultiVirtualLines.Create(FLines);
  919. NewVline.Scheme := AScheme[i];
  920. FVirtualLines.Add(NewVline);
  921. if AScheme[i].Highlighter <> nil then
  922. AScheme[i].Highlighter.AttachToLines(NewVline);
  923. end;
  924. end;
  925. procedure TSynHighlighterMultiRangeList.CleanUpForScheme(AScheme: TSynHighlighterMultiSchemeList);
  926. // Called before destruction / in detach
  927. var
  928. i: Integer;
  929. begin
  930. for i := 0 to AScheme.Count - 1 do
  931. if (VirtualLines[AScheme[i]] <> nil) and (AScheme[i].Highlighter <> nil) then
  932. AScheme[i].Highlighter.DetachFromLines(VirtualLines[AScheme[i]]);
  933. end;
  934. procedure TSynHighlighterMultiRangeList.CopyToScheme(AScheme: TSynHighlighterMultiSchemeList);
  935. var
  936. i: Integer;
  937. begin
  938. for i := 0 to AScheme.Count - 1 do
  939. AScheme[i].VirtualLines := FVirtualLines[i];
  940. end;
  941. { TSynMultiSyn }
  942. function TSynMultiSyn.CurrentVirtualLines: TSynHLightMultiVirtualLines;
  943. begin
  944. if FCurScheme <> nil then
  945. Result := FCurScheme.VirtualLines
  946. else
  947. Result := DefaultVirtualLines;
  948. end;
  949. constructor TSynMultiSyn.Create(AOwner: TComponent);
  950. begin
  951. inherited Create(AOwner);
  952. fSchemes := TSynHighlighterMultiSchemeList.Create(Self);
  953. FCurScheme := nil;
  954. end;
  955. destructor TSynMultiSyn.Destroy;
  956. var
  957. s: TSynHighlighterMultiSchemeList;
  958. begin
  959. s := FSchemes;
  960. FSchemes := nil;
  961. s.Free;
  962. { unhook notification handlers }
  963. DefaultHighlighter := nil;
  964. inherited Destroy;
  965. end;
  966. function TSynMultiSyn.PerformScan(StartIndex, EndIndex: Integer; ForceEndIndex: Boolean = False): Integer;
  967. var
  968. i, j, c: Integer;
  969. SearchPos, NewSearchPos, TmpSearchPos: Integer;
  970. CurRegStart: TPoint;
  971. CurRegTokenPos: Integer;
  972. LineText: string;
  973. procedure StartScheme(NewScheme: TSynHighlighterMultiScheme;
  974. StartAtLine, StartAtChar, TokenAtChar: Integer);
  975. var
  976. pt: TPoint;
  977. begin
  978. //debugln(['StartScheme NewScheme=',dbgs(NewScheme),' StartAtLine=',StartAtLine,' StartAtChar=',StartAtChar,' TokenAtChar=',TokenAtChar]);
  979. pt := Point(TokenAtChar-1, StartAtLine);
  980. if CurRegStart.y < 0 then
  981. DefaultVirtualLines.RegionScanUpdateFirstRegionEnd(pt, 0)
  982. else
  983. if pt >= CurRegStart then
  984. DefaultVirtualLines.RegionScanUpdateOrInsertRegion(CurRegStart, pt, 0, 0);
  985. FCurScheme := NewScheme;
  986. CurRegStart.y := StartAtLine;
  987. CurRegStart.x := StartAtChar;
  988. CurRegTokenPos := TokenAtChar;
  989. end;
  990. procedure EndScheme(EndAtLine, EndAtChar, TokenEndChar: Integer);
  991. var
  992. pt: TPoint;
  993. begin
  994. //debugln(['EndScheme EndAtLine=',EndAtLine,' EndAtChar=',EndAtChar,' TokenAtChar=',TokenEndChar]);
  995. pt := Point(EndAtChar, EndAtLine);
  996. if CurRegStart.y < 0 then
  997. FCurScheme.VirtualLines.RegionScanUpdateFirstRegionEnd(pt, TokenEndChar)
  998. else
  999. if pt >= CurRegStart then
  1000. FCurScheme.VirtualLines.RegionScanUpdateOrInsertRegion
  1001. (CurRegStart, pt, CurRegTokenPos, TokenEndChar);
  1002. FCurScheme := nil;
  1003. CurRegStart.y := EndAtLine;
  1004. CurRegStart.x := TokenEndChar + 1;
  1005. CurRegTokenPos := 0;
  1006. end;
  1007. begin
  1008. (* Scan regions *)
  1009. Result := StartIndex;
  1010. {$IFDEF SynDebugMultiHL}
  1011. debugln(SYNDEBUG_MULTIHL, ['TSynMultiSyn.PerformScan StartIndex=', Result]);
  1012. {$ENDIF}
  1013. // last node may need to extend to next line
  1014. // TODO: instead check, that FCurScheme is cvered by region
  1015. // p := DefaultVirtualLines.SectionList.PSections[DefaultVirtualLines.FRegionScanRangeIndex]
  1016. // p := FCurScheme.VirtualLines .SectionList.PSections[FCurScheme.VirtualLines.FRegionScanRangeIndex];
  1017. if Result > 0 then dec(Result);
  1018. c := CurrentLines.Count - 1;
  1019. if c < 0 then begin
  1020. // Clear ?
  1021. exit;
  1022. end;
  1023. DefaultVirtualLines.PrepareRegionScan(Result);
  1024. for i := 0 to Schemes.Count - 1 do begin
  1025. Schemes[i].VirtualLines.ResetHLChangedLines;
  1026. Schemes[i].VirtualLines.PrepareRegionScan(Result);
  1027. end;
  1028. CurRegStart.y := -1;
  1029. if Result = 0 then begin
  1030. CurRegStart.y := 0;
  1031. CurRegStart.x := 1;
  1032. CurRegTokenPos := 1;
  1033. end
  1034. else
  1035. CurRegTokenPos := 0;
  1036. StartAtLineIndex(Result); // Set FCurScheme
  1037. dec(Result);
  1038. repeat
  1039. inc(Result);
  1040. if Result <> StartIndex then
  1041. ContinueNextLine;
  1042. LineText := CurrentLines[Result];
  1043. FSchemes.CurrentLine := LineText;
  1044. SearchPos := 1;
  1045. while SearchPos <= length(LineText) do begin
  1046. if FCurScheme <> nil then begin
  1047. // Find Endpoint for CurScheme
  1048. NewSearchPos := FCurScheme.FindEndPosInLine(SearchPos);
  1049. if NewSearchPos <= 0 then
  1050. break; // Ends in next line
  1051. SearchPos := NewSearchPos + FCurScheme.LastMatchLen;
  1052. EndScheme(Result, NewSearchPos - 1, SearchPos - 1);
  1053. end
  1054. else begin
  1055. // Find new start of a Scheme
  1056. NewSearchPos := -1;
  1057. for i := 0 to Schemes.Count - 1 do begin
  1058. TmpSearchPos := Schemes.Items[i].FindStartPosInLine(SearchPos);
  1059. if (NewSearchPos < 0) or ((TmpSearchPos > 0) and (TmpSearchPos < NewSearchPos)) then begin
  1060. j := i;
  1061. NewSearchPos := TmpSearchPos;
  1062. end;
  1063. end;
  1064. if NewSearchPos <= 0 then
  1065. break; // Not in this line
  1066. SearchPos := NewSearchPos + Schemes[j].LastMatchLen;
  1067. StartScheme(Schemes[j], Result, SearchPos, NewSearchPos);
  1068. end;
  1069. end;
  1070. until ((not UpdateRangeInfoAtLine(Result)) and (Result > EndIndex))
  1071. or (Result = c);
  1072. if Result = c then begin
  1073. i := length(CurrentLines[c]) + 1;
  1074. if FCurScheme = nil then
  1075. StartScheme(nil, c, i, i) // DefaultVirtualLines.RegionScanUpdateFirstRegionEnd(pt, 0)
  1076. else
  1077. EndScheme(c, i, i);
  1078. end
  1079. else if CurRegStart.y > 0 then begin
  1080. if FCurScheme = nil
  1081. then DefaultVirtualLines.RegionScanUpdateLastRegionStart(CurRegStart, 0, Result)
  1082. else FCurScheme.VirtualLines.RegionScanUpdateLastRegionStart(CurRegStart, CurRegTokenPos, Result);
  1083. end
  1084. else begin
  1085. // nothing changed, keep current
  1086. if FCurScheme = nil
  1087. then inc(DefaultVirtualLines.FRegionScanRangeIndex)
  1088. else inc(FCurScheme.VirtualLines.FRegionScanRangeIndex);
  1089. end;
  1090. DefaultVirtualLines.FinishRegionScan(Result);
  1091. for i := 0 to Schemes.Count - 1 do
  1092. Schemes[i].VirtualLines.FinishRegionScan(Result);
  1093. (* Scan nested Highlighters *)
  1094. for i := 0 to Schemes.Count - 1 do
  1095. if Schemes[i].Highlighter <> nil then begin
  1096. Schemes[i].Highlighter.ScanRanges;
  1097. j := Schemes[i].VirtualLines.SectionList.VirtualIdxToRealIdx(Schemes[i].VirtualLines.LastHLChangedLine);
  1098. if Result < j then
  1099. Result := j;
  1100. end;
  1101. if FDefaultHighlighter <> nil then begin
  1102. FDefaultHighlighter.ScanRanges;
  1103. j := DefaultVirtualLines.SectionList.VirtualIdxToRealIdx(DefaultVirtualLines.LastHLChangedLine);
  1104. if Result < j then
  1105. Result := j;
  1106. end;
  1107. end;
  1108. function TSynMultiSyn.GetAttribCount: integer;
  1109. var
  1110. i: Integer;
  1111. begin
  1112. Result := Schemes.Count;
  1113. for i := 0 to Schemes.Count - 1 do
  1114. if Schemes[i].Highlighter <> nil then
  1115. inc(Result, Schemes[i].Highlighter.AttrCount);
  1116. if DefaultHighlighter <> nil then
  1117. Inc(Result, DefaultHighlighter.AttrCount);
  1118. end;
  1119. function TSynMultiSyn.GetAttribute(
  1120. idx: integer): TSynHighlighterAttributes;
  1121. var
  1122. i, j: Integer;
  1123. begin
  1124. if DefaultHighlighter <> nil then begin
  1125. j := DefaultHighlighter.AttrCount;
  1126. if idx < j then
  1127. exit(DefaultHighlighter.Attribute[idx]);
  1128. dec(idx, j);
  1129. end;
  1130. for i := 0 to Schemes.Count - 1 do begin
  1131. if idx = 0 then
  1132. exit(Schemes[i].MarkerAttri);
  1133. dec(idx);
  1134. if Schemes[i].Highlighter <> nil then begin
  1135. j := Schemes[i].Highlighter.AttrCount;
  1136. if idx < j then
  1137. exit(Schemes[i].Highlighter.Attribute[idx]);
  1138. dec(idx, j);
  1139. end;
  1140. end;
  1141. Result := nil;
  1142. raise Exception.Create('bad attr idx');
  1143. end;
  1144. function TSynMultiSyn.GetDefaultAttribute(Index: integer): TSynHighlighterAttributes;
  1145. var
  1146. iHL: TSynCustomHighlighter;
  1147. begin
  1148. if (FCurScheme <> nil) and (FCurScheme.Highlighter <> nil) then
  1149. iHL := FCurScheme.Highlighter
  1150. else
  1151. iHL := DefaultHighlighter;
  1152. { the typecast to TSynMultiSyn is only necessary because the
  1153. GetDefaultAttribute method is protected.
  1154. And don't worry: this really works }
  1155. if iHL <> nil then begin
  1156. Result := TSynMultiSyn(iHL).GetDefaultAttribute(Index)
  1157. end else
  1158. Result := nil;
  1159. end;
  1160. function TSynMultiSyn.GetEol: Boolean;
  1161. begin
  1162. Result := FTokenPos > FLineLen;
  1163. end;
  1164. function TSynMultiSyn.GetIdentChars: TSynIdentChars;
  1165. begin
  1166. if FCurScheme <> nil then
  1167. Result := FCurScheme.Highlighter.IdentChars
  1168. else if DefaultHighlighter <> nil then
  1169. Result := DefaultHighlighter.IdentChars
  1170. else
  1171. Result := inherited GetIdentChars;
  1172. end;
  1173. class function TSynMultiSyn.GetLanguageName: string;
  1174. begin
  1175. Result := SYNS_LangGeneralMulti;
  1176. end;
  1177. function TSynMultiSyn.GetRange: Pointer;
  1178. begin
  1179. Result := FCurScheme;
  1180. end;
  1181. function TSynMultiSyn.GetToken: string;
  1182. begin
  1183. SetString(Result, (PChar(FLine) + FTokenPos - 1), FRun - FTokenPos);
  1184. end;
  1185. procedure TSynMultiSyn.GetTokenEx(out TokenStart: PChar;
  1186. out TokenLength: integer);
  1187. begin
  1188. TokenLength := FRun-FTokenPos;
  1189. if TokenLength > 0 then begin
  1190. TokenStart := @fLine[FTokenPos];
  1191. end else begin
  1192. TokenStart := nil;
  1193. end;
  1194. end;
  1195. function TSynMultiSyn.GetTokenAttribute: TSynHighlighterAttributes;
  1196. begin
  1197. Result := FTokenAttr;
  1198. end;
  1199. function TSynMultiSyn.GetTokenKind: integer;
  1200. begin
  1201. Result := FTokenKind;
  1202. end;
  1203. function TSynMultiSyn.GetTokenPos: Integer;
  1204. begin
  1205. Result := fTokenPos - 1;
  1206. end;
  1207. procedure TSynMultiSyn.HookHighlighter(aHL: TSynCustomHighlighter);
  1208. begin
  1209. aHL.HookAttrChangeEvent( @DefHighlightChange );
  1210. end;
  1211. procedure TSynMultiSyn.Next;
  1212. procedure NextRunSection(ASchemeIdx: Integer);
  1213. var
  1214. VLines: TSynHLightMultiVirtualLines;
  1215. idx: Integer;
  1216. s: TSynHLightMultiVirtualSection;
  1217. x1, x2, tx1, tx2: Integer;
  1218. begin
  1219. if ASchemeIdx > 0 then
  1220. VLines := Schemes[ASchemeIdx-1].VirtualLines
  1221. else
  1222. VLines := DefaultVirtualLines;
  1223. idx := FRunSectionInfo[ASchemeIdx].SectionIdx + 1;
  1224. FRunSectionInfo[ASchemeIdx].SectionIdx := -1;
  1225. if (idx < 0) or (idx >= VLines.SectionList.Count) then
  1226. exit;
  1227. s := VLines.SectionList[idx];
  1228. if s.StartPos.y > FCurLineIndex then
  1229. exit;
  1230. FRunSectionInfo[ASchemeIdx].SectionIdx := idx;
  1231. FRunSectionInfo[ASchemeIdx].VirtualStartPos :=
  1232. FRunSectionInfo[ASchemeIdx].VirtualStartPos +
  1233. FRunSectionInfo[ASchemeIdx].LastChar - FRunSectionInfo[ASchemeIdx].FirstChar + 1;
  1234. if s.StartPos.y = FCurLineIndex then begin
  1235. x1 := s.StartPos.x;
  1236. tx1 := s.TokenStartPos;
  1237. if tx1 = 0 then
  1238. tx1 := x1;
  1239. end else begin
  1240. x1 := 1;
  1241. tx1 := 1;
  1242. end;
  1243. if s.EndPos.y = FCurLineIndex then begin
  1244. x2 := s.EndPos.x;
  1245. tx2 := s.TokenEndPos;
  1246. if tx2 = 0 then
  1247. tx2 := x2;
  1248. end else begin
  1249. x2 := length(CurrentLines[FCurLineIndex]);
  1250. tx2 := x2;
  1251. end;
  1252. FRunSectionInfo[ASchemeIdx].FirstChar := x1;
  1253. FRunSectionInfo[ASchemeIdx].LastChar := x2;
  1254. FRunSectionInfo[ASchemeIdx].TokenFirstChar := tx1;
  1255. FRunSectionInfo[ASchemeIdx].TokenLastChar := tx2;
  1256. end;
  1257. var
  1258. idx: Integer;
  1259. RSect: TRunSectionInfo;
  1260. HL: TSynCustomHighlighter;
  1261. dummy: PChar;
  1262. tkpos, tklen: Integer;
  1263. begin
  1264. //debugln(['--- Next at ',FRun]);
  1265. FTokenPos := FRun;
  1266. FTokenAttr := nil;
  1267. FTokenKind := 0;
  1268. if FRun > FLineLen then
  1269. exit;
  1270. idx := high(FRunSectionInfo);
  1271. while (idx >= 0) and
  1272. ( (FRunSectionInfo[idx].SectionIdx < 0) or
  1273. not ( (FRun >= FRunSectionInfo[idx].TokenFirstChar) and
  1274. (FRun <= FRunSectionInfo[idx].TokenLastChar) ) )
  1275. do
  1276. dec(idx);
  1277. if idx < 0 then begin
  1278. //debugln(['*** XXXXX No section found XXXXX ***']);
  1279. FRun := FLineLen + 1;
  1280. FTokenAttr := nil;
  1281. FTokenKind := 0;
  1282. exit;
  1283. end;
  1284. RSect := FRunSectionInfo[idx];
  1285. //with RSect do debugln([' RSect ',idx,': SectIdx=', SectionIdx, ' Fc=',FirstChar,' LC=',LastChar,' TkFC=',TokenFirstChar, ' TkLC=',TokenLastChar]);
  1286. if RSect.SectionIdx < 0 then begin
  1287. //debugln(['*** XXXXX section missing XXXXX ***']);
  1288. FRun := FLineLen + 1;
  1289. FTokenAttr := nil;
  1290. FTokenKind := 0;
  1291. exit;
  1292. end;
  1293. if (idx > 0) and (FRun < RSect.FirstChar) then begin
  1294. FTokenAttr := Schemes[idx-1].FMarkerAttri;
  1295. FTokenKind := 1;
  1296. FRun := RSect.FirstChar;
  1297. //debugln([' start-token ', FRun]);
  1298. end
  1299. else if (idx > 0) and (FRun > RSect.LastChar) then begin
  1300. FTokenAttr := Schemes[idx-1].FMarkerAttri;
  1301. FTokenKind := 1;
  1302. FRun := RSect.TokenLastChar + 1;
  1303. //debugln([' end-token ', FRun]);
  1304. end
  1305. else begin
  1306. if idx = 0 then
  1307. HL := DefaultHighlighter
  1308. else
  1309. HL := Schemes[idx-1].Highlighter;
  1310. if HL <> nil then begin
  1311. repeat
  1312. HL.GetTokenEx(dummy, tklen);
  1313. tkpos := HL.GetTokenPos + 1;
  1314. if tkpos - RSect.VirtualStartPos + RSect.FirstChar + tklen - 1 < FRun then begin
  1315. //debugln('>');
  1316. HL.Next
  1317. end else
  1318. break;
  1319. until HL.GetEol;
  1320. if not HL.GetEol then begin
  1321. FTokenAttr := HL.GetTokenAttribute;
  1322. FTokenKind := idx * TokenKindPerHighlighter + HL.GetTokenKind;
  1323. FRun := Min(tkpos - RSect.VirtualStartPos + RSect.FirstChar + tklen,
  1324. RSect.LastChar + 1);
  1325. //debugln([' FOUND-token ', FRun, ' t=',copy(FLine, FTokenPos, 2),'... kind=',FTokenKind, ' subhl: tkpos=',tkpos,' tklen=',tklen, ' t=', copy(dummy,1,tklen) ]);
  1326. end
  1327. else
  1328. HL := nil;
  1329. end;
  1330. if (HL = nil) then begin
  1331. FTokenAttr := nil;
  1332. FTokenKind := 0;
  1333. FRun := RSect.LastChar + 1;
  1334. //debugln([' no HL ', FRun]);
  1335. end;
  1336. end;
  1337. if (FRun > RSect.TokenLastChar) then
  1338. NextRunSection(idx);
  1339. end;
  1340. procedure TSynMultiSyn.Notification(aComp: TComponent; aOp: TOperation);
  1341. var
  1342. i: Integer;
  1343. begin
  1344. inherited;
  1345. if (aOp = opRemove) and (Schemes <> nil) then begin
  1346. if (aComp = DefaultHighlighter) then
  1347. DefaultHighlighter := nil;
  1348. for i := 0 to Schemes.Count - 1 do
  1349. if aComp = Schemes[i].Highlighter then
  1350. Schemes[i].Highlighter := nil;
  1351. end;
  1352. end;
  1353. function TSynMultiSyn.CreateRangeList(ALines: TSynEditStringsBase): TSynHighlighterRangeList;
  1354. var
  1355. NewRangeList: TSynHighlighterMultiRangeList;
  1356. begin
  1357. NewRangeList := TSynHighlighterMultiRangeList.Create(ALines);
  1358. NewRangeList.UpdateForScheme(Schemes);
  1359. NewRangeList.CopyToScheme(Schemes);
  1360. if FDefaultHighlighter <> nil then
  1361. FDefaultHighlighter.AttachToLines(NewRangeList.DefaultVirtualLines);
  1362. Result := NewRangeList;
  1363. end;
  1364. procedure TSynMultiSyn.BeforeDetachedFromRangeList(ARangeList: TSynHighlighterRangeList);
  1365. begin
  1366. inherited BeforeDetachedFromRangeList(ARangeList);
  1367. if (Schemes <> nil) and (ARangeList.RefCount = 0) then begin
  1368. TSynHighlighterMultiRangeList(ARangeList).CleanUpForScheme(Schemes);
  1369. if (TSynHighlighterMultiRangeList(ARangeList).DefaultVirtualLines <> nil) and
  1370. (DefaultHighlighter <> nil)
  1371. then
  1372. DefaultHighlighter.DetachFromLines(TSynHighlighterMultiRangeList(ARangeList).DefaultVirtualLines);
  1373. end;
  1374. end;
  1375. procedure TSynMultiSyn.SetCurrentLines(const AValue: TSynEditStringsBase);
  1376. begin
  1377. inherited SetCurrentLines(AValue);
  1378. CurrentRanges.CopyToScheme(Schemes);
  1379. if FDefaultHighlighter <> nil then
  1380. FDefaultHighlighter.CurrentLines := CurrentRanges.DefaultVirtualLines;
  1381. end;
  1382. procedure TSynMultiSyn.ResetRange;
  1383. begin
  1384. FCurScheme := nil;
  1385. if DefaultHighlighter <> nil then begin
  1386. DefaultHighlighter.ResetRange;
  1387. end;
  1388. end;
  1389. procedure TSynMultiSyn.SetDefaultHighlighter(
  1390. const Value: TSynCustomHighLighter);
  1391. const
  1392. sDefaultHlSetToSelf = 'Not allowed';
  1393. var
  1394. i: Integer;
  1395. begin
  1396. if DefaultHighlighter = Value then exit;
  1397. if Value = Self then
  1398. raise Exception.Create( sDefaultHlSetToSelf );
  1399. if DefaultHighlighter <> nil then begin
  1400. DefaultHighlighter.RemoveFreeNotification(Self);
  1401. UnhookHighlighter( DefaultHighlighter );
  1402. for i := 0 to KnownLines.Count - 1 do
  1403. DefaultHighlighter.DetachFromLines(KnownRanges[i].DefaultVirtualLines);
  1404. end;
  1405. fDefaultHighlighter := Value;
  1406. if DefaultHighlighter <> nil then begin
  1407. HookHighlighter( DefaultHighlighter );
  1408. DefaultHighlighter.FreeNotification(Self);
  1409. for i := 0 to KnownLines.Count - 1 do
  1410. DefaultHighlighter.AttachToLines(KnownRanges[i].DefaultVirtualLines);
  1411. end;
  1412. { yes, it's necessary }
  1413. if not( csDestroying in ComponentState ) then
  1414. DefHighlightChange( Self );
  1415. end;
  1416. function TSynMultiSyn.GetDefaultVirtualLines: TSynHLightMultiVirtualLines;
  1417. begin
  1418. Result := CurrentRanges.DefaultVirtualLines;
  1419. end;
  1420. function TSynMultiSyn.GetKnownMultiRanges(Index: Integer): TSynHighlighterMultiRangeList;
  1421. begin
  1422. Result := TSynHighlighterMultiRangeList(inherited KnownRanges[Index])
  1423. end;
  1424. function TSynMultiSyn.GetCurrentRanges: TSynHighlighterMultiRangeList;
  1425. begin
  1426. Result := TSynHighlighterMultiRangeList(inherited CurrentRanges)
  1427. end;
  1428. procedure TSynMultiSyn.SetLine(const NewValue: string;
  1429. LineNumber: Integer);
  1430. procedure InitRunSection(ASchemeIdx: Integer);
  1431. var
  1432. VLines: TSynHLightMultiVirtualLines;
  1433. HL: TSynCustomHighlighter;
  1434. s: TSynHLightMultiVirtualSection;
  1435. idx, x1, x2, tx1, tx2: Integer;
  1436. begin
  1437. FRunSectionInfo[ASchemeIdx].SectionIdx := -1;
  1438. if ASchemeIdx > 0 then begin
  1439. VLines := Schemes[ASchemeIdx-1].VirtualLines;
  1440. HL := Schemes[ASchemeIdx-1].Highlighter;
  1441. end else begin
  1442. VLines := DefaultVirtualLines;
  1443. HL := DefaultHighlighter;
  1444. end;
  1445. idx := VLines.SectionList.IndexOfFirstSectionAtLineIdx(FCurLineIndex);
  1446. if (idx < 0) or (idx >= VLines.SectionList.Count) then
  1447. exit;
  1448. s := VLines.SectionList[idx];
  1449. if s.StartPos.y > FCurLineIndex then
  1450. exit;
  1451. FRunSectionInfo[ASchemeIdx].SectionIdx := idx;
  1452. FRunSectionInfo[ASchemeIdx].VirtualStartPos := 1;
  1453. if s.StartPos.y = FCurLineIndex then begin
  1454. x1 := s.StartPos.x;
  1455. tx1 := s.TokenStartPos;
  1456. if tx1 = 0 then
  1457. tx1 := x1;
  1458. end else begin
  1459. x1 := 1;
  1460. tx1 := 1;
  1461. end;
  1462. if s.EndPos.y = FCurLineIndex then begin
  1463. x2 := s.EndPos.x;
  1464. tx2 := s.TokenEndPos;
  1465. if tx2 = 0 then
  1466. tx2 := x2;
  1467. end else begin
  1468. x2 := length(CurrentLines[FCurLineIndex]);
  1469. tx2 := x2;
  1470. end;
  1471. FRunSectionInfo[ASchemeIdx].FirstChar := x1;
  1472. FRunSectionInfo[ASchemeIdx].LastChar := x2;
  1473. FRunSectionInfo[ASchemeIdx].TokenFirstChar := tx1;
  1474. FRunSectionInfo[ASchemeIdx].TokenLastChar := tx2;
  1475. if HL <> nil then
  1476. HL.StartAtLineIndex(s.VirtualLine + FCurLineIndex - s.StartPos.y);
  1477. //with FRunSectionInfo[ASchemeIdx] do debugln([' RunSection ',ASchemeIdx,': SectIdx=', SectionIdx, ' Fc=',FirstChar,' LC=',LastChar,' TkFC=',TokenFirstChar, ' TkLC=',TokenLastChar, ' VLine=',s.VirtualLine + FCurLineIndex - s.StartPos.y]);
  1478. end;
  1479. var
  1480. i: Integer;
  1481. begin
  1482. if IsScanning then exit;
  1483. inherited;
  1484. FCurLineIndex := LineNumber;
  1485. FLine := NewValue;
  1486. FLineLen := length(FLine);
  1487. fRun := 1;
  1488. FTokenPos := 1;
  1489. FTokenAttr := nil;
  1490. FTokenKind := 0;
  1491. //debugln(['>>>>> Setting Line ',FCurLineIndex,' = ',FLine]);
  1492. for i := 0 to high(FRunSectionInfo) do
  1493. InitRunSection(i);
  1494. Next;
  1495. end;
  1496. procedure TSynMultiSyn.SetRange(Value: Pointer);
  1497. begin
  1498. inherited;
  1499. FCurScheme := TSynHighlighterMultiScheme(Value);
  1500. end;
  1501. procedure TSynMultiSyn.SetSchemes(const Value: TSynHighlighterMultiSchemeList);
  1502. begin
  1503. fSchemes.Assign(Value);
  1504. end;
  1505. procedure TSynMultiSyn.UnhookHighlighter(aHL: TSynCustomHighlighter);
  1506. begin
  1507. if csDestroying in aHL.ComponentState then
  1508. Exit;
  1509. aHL.UnhookAttrChangeEvent( @DefHighlightChange );
  1510. end;
  1511. function TSynMultiSyn.GetSampleSource: string;
  1512. begin
  1513. Result := fSampleSource;
  1514. end;
  1515. procedure TSynMultiSyn.SetSampleSource(Value: string);
  1516. begin
  1517. fSampleSource := Value;
  1518. end;
  1519. procedure TSynMultiSyn.SchemeItemChanged(Item: TObject);
  1520. var
  1521. i: Integer;
  1522. begin
  1523. if Schemes = nil then exit;
  1524. FAttributeChangeNeedScan := (Item <> nil) and (TSynHighlighterMultiScheme(Item).NeedHLScan);
  1525. DefHighlightChange( Item );
  1526. for i := 0 to KnownLines.Count - 1 do
  1527. KnownRanges[i].InvalidateAll;
  1528. end;
  1529. procedure TSynMultiSyn.SchemeChanged;
  1530. var
  1531. i: Integer;
  1532. begin
  1533. if Schemes = nil then exit;
  1534. SetLength(FRunSectionInfo, Schemes.Count + 1); // include default
  1535. for i := 0 to KnownLines.Count - 1 do
  1536. KnownRanges[i].UpdateForScheme(Schemes);
  1537. if CurrentLines <> nil then
  1538. CurrentRanges.CopyToScheme(Schemes);
  1539. SchemeItemChanged(nil);
  1540. end;
  1541. procedure TSynMultiSyn.DetachHighlighter(AHighlighter: TSynCustomHighlighter;
  1542. AScheme: TSynHighlighterMultiScheme);
  1543. var
  1544. i: Integer;
  1545. begin
  1546. for i := 0 to KnownLines.Count - 1 do
  1547. AHighlighter.DetachFromLines(KnownRanges[i].VirtualLines[AScheme]);
  1548. end;
  1549. procedure TSynMultiSyn.AttachHighlighter(AHighlighter: TSynCustomHighlighter;
  1550. AScheme: TSynHighlighterMultiScheme);
  1551. var
  1552. i: Integer;
  1553. begin
  1554. for i := 0 to KnownLines.Count - 1 do
  1555. AHighlighter.AttachToLines(KnownRanges[i].VirtualLines[AScheme]);
  1556. end;
  1557. { TSynHighlighterMultiSchemeList }
  1558. constructor TSynHighlighterMultiSchemeList.Create(aOwner: TSynMultiSyn);
  1559. begin
  1560. inherited Create(TSynHighlighterMultiScheme);
  1561. FOwner := aOwner;
  1562. end;
  1563. function TSynHighlighterMultiSchemeList.IndexOf(AnItem: TSynHighlighterMultiScheme): Integer;
  1564. begin
  1565. Result := Count - 1;
  1566. while (Result >= 0) and (Items[Result] <> AnItem) do
  1567. dec(Result);
  1568. end;
  1569. function TSynHighlighterMultiSchemeList.GetItems(Index: integer): TSynHighlighterMultiScheme;
  1570. begin
  1571. Result := inherited Items[Index] as TSynHighlighterMultiScheme;
  1572. end;
  1573. function TSynHighlighterMultiSchemeList.GetConvertedCurrentLine: String;
  1574. begin
  1575. if FConvertedCurrentLine = '' then
  1576. FConvertedCurrentLine := UTF8UpperCase(FCurrentLine);
  1577. Result := FConvertedCurrentLine;
  1578. end;
  1579. procedure TSynHighlighterMultiSchemeList.SetCurrentLine(const AValue: String);
  1580. var
  1581. i: Integer;
  1582. begin
  1583. if FCurrentLine = AValue then exit;
  1584. FCurrentLine := AValue;
  1585. FConvertedCurrentLine := '';
  1586. for i := 0 to Count - 1 do
  1587. Items[i].ClearLinesSet;
  1588. end;
  1589. function TSynHighlighterMultiSchemeList.GetOwner: TPersistent;
  1590. begin
  1591. Result := Owner;
  1592. end;
  1593. procedure TSynHighlighterMultiSchemeList.SetItems(Index: integer; const Value: TSynHighlighterMultiScheme);
  1594. begin
  1595. inherited Items[Index] := Value;
  1596. end;
  1597. procedure TSynHighlighterMultiSchemeList.Update(Item: TCollectionItem);
  1598. begin
  1599. // property of an Item changed
  1600. Owner.SchemeItemChanged(Item);
  1601. end;
  1602. procedure TSynHighlighterMultiSchemeList.Notify(Item: TCollectionItem;
  1603. Action: TCollectionNotification);
  1604. begin
  1605. // Item added/removed
  1606. inherited Notify(Item, Action);
  1607. Owner.SchemeChanged;
  1608. end;
  1609. { TSynHighlighterMultiScheme }
  1610. function TSynHighlighterMultiScheme.GetConvertedLine: String;
  1611. begin
  1612. if FCaseSensitive then
  1613. Result := TSynHighlighterMultiSchemeList(Collection).CurrentLine
  1614. else
  1615. Result := TSynHighlighterMultiSchemeList(Collection).ConvertedCurrentLine;
  1616. end;
  1617. function TSynHighlighterMultiScheme.GetConvertedEndExpr: String;
  1618. begin
  1619. if FCaseSensitive then
  1620. Result := FEndExpr
  1621. else begin
  1622. if FConvertedEndExpr = '' then
  1623. FConvertedEndExpr := Utf8UpperCase(FEndExpr);
  1624. Result := FConvertedEndExpr;
  1625. end;
  1626. end;
  1627. function TSynHighlighterMultiScheme.GetConvertedStartExpr: String;
  1628. begin
  1629. if FCaseSensitive then
  1630. Result := FStartExpr
  1631. else begin
  1632. if FConvertedStartExpr = '' then
  1633. FConvertedStartExpr := Utf8UpperCase(FStartExpr);
  1634. Result := FConvertedStartExpr;
  1635. end;
  1636. end;
  1637. constructor TSynHighlighterMultiScheme.Create(TheCollection: TCollection);
  1638. begin
  1639. FStartExprScanner := TRegExpr.Create;
  1640. FEndExprScanner := TRegExpr.Create;
  1641. fCaseSensitive := True;
  1642. fMarkerAttri := TSynHighlighterAttributes.Create(@SYNS_AttrMarker, SYNS_XML_AttrMarker);
  1643. fMarkerAttri.OnChange := @MarkerAttriChanged;
  1644. MarkerAttri.Background := clYellow;
  1645. MarkerAttri.Style := [fsBold];
  1646. MarkerAttri.InternalSaveDefaultValues;
  1647. inherited Create(TheCollection); // Calls notify, all setup must be done
  1648. end;
  1649. destructor TSynHighlighterMultiScheme.Destroy;
  1650. begin
  1651. { unhook notification handlers }
  1652. Highlighter := nil;
  1653. fMarkerAttri.Free;
  1654. inherited Destroy;
  1655. FreeAndNil(FStartExprScanner);
  1656. FreeAndNil(FEndExprScanner);
  1657. end;
  1658. procedure TSynHighlighterMultiScheme.ClearLinesSet;
  1659. begin
  1660. FStartLineSet := False;
  1661. FEndLineSet := False;
  1662. end;
  1663. function TSynHighlighterMultiScheme.FindStartPosInLine(ASearchPos: Integer): Integer;
  1664. var
  1665. t: String;
  1666. begin
  1667. if (FStartExprScanner.Expression = '') or (FEndExprScanner.Expression = '') then
  1668. exit(-1);
  1669. if not FStartLineSet then begin
  1670. FStartExprScanner.InputString := GetConvertedLine;
  1671. FStartLineSet := True;
  1672. end;
  1673. Repeat
  1674. if FStartExprScanner.Exec(ASearchPos) then begin
  1675. Result := FStartExprScanner.MatchPos[0];
  1676. FLastMatchLen := FStartExprScanner.MatchLen[0];
  1677. if Assigned(OnCheckStartMarker) then begin
  1678. t := FStartExprScanner.Match[0];
  1679. OnCheckStartMarker(TSynHighlighterMultiSchemeList(Collection).Owner, Result, FLastMatchLen, t);
  1680. if (t <> '') and (FLastMatchLen > 0) then
  1681. exit;
  1682. ASearchPos := FStartExprScanner.MatchPos[0] + 1;
  1683. end
  1684. else
  1685. exit;
  1686. end
  1687. else begin
  1688. Result := -1;
  1689. FLastMatchLen := 0;
  1690. exit;
  1691. end;
  1692. until False;
  1693. end;
  1694. function TSynHighlighterMultiScheme.FindEndPosInLine(ASearchPos: Integer): Integer;
  1695. var
  1696. t: String;
  1697. begin
  1698. if not FEndLineSet then begin
  1699. FEndExprScanner.InputString := GetConvertedLine;
  1700. FEndLineSet:= True;
  1701. end;
  1702. Repeat
  1703. if FEndExprScanner.Exec(ASearchPos) then begin
  1704. Result := FEndExprScanner.MatchPos[0];
  1705. FLastMatchLen := FEndExprScanner.MatchLen[0];
  1706. if Assigned(OnCheckEndMarker) then begin
  1707. t := FEndExprScanner.Match[0];
  1708. OnCheckEndMarker(TSynHighlighterMultiSchemeList(Collection).Owner, Result, FLastMatchLen, t);
  1709. if (t <> '') and (FLastMatchLen > 0) then
  1710. exit;
  1711. ASearchPos := FEndExprScanner.MatchPos[0] + 1;
  1712. end
  1713. else
  1714. exit;
  1715. end
  1716. else begin
  1717. Result := -1;
  1718. FLastMatchLen := 0;
  1719. exit;
  1720. end;
  1721. until False;
  1722. end;
  1723. function TSynHighlighterMultiScheme.GetDisplayName: String;
  1724. begin
  1725. if SchemeName <> '' then
  1726. Result := SchemeName
  1727. else
  1728. Result := inherited GetDisplayName;
  1729. end;
  1730. procedure TSynHighlighterMultiScheme.MarkerAttriChanged(Sender: TObject);
  1731. begin
  1732. Changed( False );
  1733. end;
  1734. procedure TSynHighlighterMultiScheme.SetCaseSensitive(const Value: Boolean);
  1735. begin
  1736. if fCaseSensitive <> Value then
  1737. begin
  1738. fCaseSensitive := Value;
  1739. FStartExprScanner.Expression := GetConvertedStartExpr;
  1740. FEndExprScanner.Expression := GetConvertedEndExpr;
  1741. ClearLinesSet;
  1742. FNeedHLScan := True;
  1743. Changed( False );
  1744. FNeedHLScan := False;
  1745. end;
  1746. end;
  1747. procedure TSynHighlighterMultiScheme.SetVirtualLines(const AValue: TSynHLightMultiVirtualLines);
  1748. begin
  1749. FVirtualLines := AValue;
  1750. if FHighlighter <> nil then
  1751. FHighlighter.CurrentLines := AValue;
  1752. end;
  1753. procedure TSynHighlighterMultiScheme.SetDisplayName(const Value: String);
  1754. begin
  1755. SchemeName := Value;
  1756. end;
  1757. procedure TSynHighlighterMultiScheme.SetEndExpr(const Value: string);
  1758. var OldValue: String;
  1759. begin
  1760. if fEndExpr <> Value then
  1761. begin
  1762. OldValue := GetConvertedEndExpr;
  1763. FConvertedEndExpr := '';
  1764. FEndExpr := Value;
  1765. FEndExprScanner.Expression := GetConvertedEndExpr;
  1766. FNeedHLScan := True;
  1767. if GetConvertedEndExpr <> OldValue then
  1768. Changed( False );
  1769. FNeedHLScan := False;
  1770. end;
  1771. end;
  1772. procedure TSynHighlighterMultiScheme.SetHighlighter(const Value: TSynCustomHighLighter);
  1773. var
  1774. ParentHLighter: TSynMultiSyn;
  1775. begin
  1776. if Highlighter <> Value then
  1777. begin
  1778. if (Value = TSynHighlighterMultiSchemeList(Collection).Owner) then
  1779. raise Exception.Create('circular highlighter not allowed');
  1780. ParentHLighter := TSynHighlighterMultiSchemeList(Collection).Owner;
  1781. if Highlighter <> nil then begin
  1782. Highlighter.RemoveFreeNotification(ParentHLighter);
  1783. ParentHLighter.UnhookHighlighter(Highlighter);
  1784. ParentHLighter.DetachHighlighter(Highlighter, Self);
  1785. end;
  1786. fHighlighter := Value;
  1787. if Highlighter <> nil then begin
  1788. ParentHLighter.AttachHighlighter(Highlighter, Self);
  1789. Highlighter.FreeNotification(ParentHLighter);
  1790. if FVirtualLines <> nil then
  1791. FHighlighter.CurrentLines := FVirtualLines;
  1792. end;
  1793. FNeedHLScan := True;
  1794. Changed(False);
  1795. FNeedHLScan := False;
  1796. end;
  1797. end;
  1798. procedure TSynHighlighterMultiScheme.SetMarkerAttri(const Value: TSynHighlighterAttributes);
  1799. begin
  1800. fMarkerAttri.Assign(Value);
  1801. end;
  1802. procedure TSynHighlighterMultiScheme.SetStartExpr(const Value: string);
  1803. var OldValue: String;
  1804. begin
  1805. if fStartExpr <> Value then
  1806. begin
  1807. OldValue := GetConvertedStartExpr;
  1808. FConvertedStartExpr := '';
  1809. FStartExpr := Value;
  1810. FStartExprScanner.Expression := GetConvertedStartExpr;
  1811. FNeedHLScan := True; // TODO: only if EndScanne.Expression <> '' ?
  1812. if GetConvertedStartExpr <> OldValue then
  1813. Changed( False );
  1814. FNeedHLScan := False;
  1815. end;
  1816. end;
  1817. initialization
  1818. SYNDEBUG_MULTIHL := DebugLogger.RegisterLogGroup('SYNDEBUG_MULTIHL', False);
  1819. end.