/components/synedit/syneditmarkupbracket.pp

http://github.com/graemeg/lazarus · Puppet · 281 lines · 244 code · 37 blank · 0 comment · 31 complexity · bb3e69b52493d0963b05fa2af88fc774 MD5 · raw file

  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. Alternatively, the contents of this file may be used under the terms of the
  10. GNU General Public License Version 2 or later (the "GPL"), in which case
  11. the provisions of the GPL are applicable instead of those above.
  12. If you wish to allow use of your version of this file only under the terms
  13. of the GPL and not to allow others to use your version of this file
  14. under the MPL, indicate your decision by deleting the provisions above and
  15. replace them with the notice and other provisions required by the GPL.
  16. If you do not delete the provisions above, a recipient may use your version
  17. of this file under either the MPL or the GPL.
  18. -------------------------------------------------------------------------------}
  19. unit SynEditMarkupBracket;
  20. {$mode objfpc}{$H+}
  21. interface
  22. uses
  23. Classes, SysUtils, Graphics, SynEditMarkup, SynEditMiscClasses, Controls, LCLProc;
  24. type
  25. TSynEditBracketHighlightStyle = (
  26. sbhsLeftOfCursor,
  27. sbhsRightOfCursor,
  28. sbhsBoth
  29. );
  30. { TSynEditMarkupBracket }
  31. TSynEditMarkupBracket = class(TSynEditMarkup)
  32. private
  33. // Physical Position
  34. FBracketHighlightPos: TPoint;
  35. FBracketHighlightAntiPos: TPoint;
  36. FHighlightStyle: TSynEditBracketHighlightStyle;
  37. FNeedInvalidate: Boolean;
  38. procedure SetHighlightStyle(const AValue: TSynEditBracketHighlightStyle);
  39. protected
  40. procedure FindMatchingBracketPair(LogCaret: TPoint;
  41. var StartBracket, EndBracket: TPoint);
  42. procedure DoCaretChanged(Sender: TObject); override;
  43. procedure DoTopLineChanged(OldTopLine : Integer); override;
  44. procedure DoLinesInWindoChanged(OldLinesInWindow : Integer); override;
  45. procedure DoTextChanged(StartLine, EndLine, ACountDiff: Integer); override;
  46. procedure DoMarkupChanged(AMarkup: TSynSelectedColor); override;
  47. procedure DoEnabledChanged(Sender: TObject); override;
  48. procedure DoVisibleChanged(AVisible: Boolean); override;
  49. public
  50. constructor Create(ASynEdit: TSynEditBase);
  51. procedure DecPaintLock; override;
  52. function GetMarkupAttributeAtRowCol(const aRow: Integer;
  53. const aStartCol: TLazSynDisplayTokenBound;
  54. const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor; override;
  55. procedure GetNextMarkupColAfterRowCol(const aRow: Integer;
  56. const aStartCol: TLazSynDisplayTokenBound;
  57. const AnRtlInfo: TLazSynDisplayRtlInfo;
  58. out ANextPhys, ANextLog: Integer); override;
  59. procedure InvalidateBracketHighlight;
  60. property HighlightStyle: TSynEditBracketHighlightStyle read FHighlightStyle write SetHighlightStyle;
  61. end;
  62. implementation
  63. uses
  64. SynEdit;
  65. { TSynEditMarkupBracket }
  66. constructor TSynEditMarkupBracket.Create(ASynEdit : TSynEditBase);
  67. begin
  68. inherited Create(ASynEdit);
  69. FBracketHighlightPos.Y := -1;
  70. FBracketHighlightAntiPos.Y := -1;
  71. FHighlightStyle := sbhsBoth;
  72. MarkupInfo.Foreground := clNone;
  73. MarkupInfo.Background := clNone;
  74. MarkupInfo.Style := [fsBold];
  75. MarkupInfo.StyleMask := [];
  76. end;
  77. procedure TSynEditMarkupBracket.DecPaintLock;
  78. begin
  79. inherited DecPaintLock;
  80. if (FPaintLock = 0) and FNeedInvalidate then
  81. InvalidateBracketHighlight;
  82. end;
  83. procedure TSynEditMarkupBracket.SetHighlightStyle(
  84. const AValue: TSynEditBracketHighlightStyle);
  85. begin
  86. if FHighlightStyle <> AValue then
  87. begin
  88. FHighlightStyle := AValue;
  89. InvalidateBracketHighlight;
  90. end;
  91. end;
  92. procedure TSynEditMarkupBracket.FindMatchingBracketPair(LogCaret: TPoint; var StartBracket,
  93. EndBracket: TPoint);
  94. const
  95. Brackets: set of Char = ['(',')','{','}','[',']', '''', '"' ];
  96. var
  97. StartLine: string;
  98. x: Integer;
  99. begin
  100. StartBracket.Y := -1;
  101. EndBracket.Y := -1;
  102. if (LogCaret.Y < 1) or (LogCaret.Y > Lines.Count) or (LogCaret.X < 1) then
  103. Exit;
  104. StartLine := Lines[LogCaret.Y - 1];
  105. // check for bracket, left of cursor
  106. if (HighlightStyle in [sbhsLeftOfCursor, sbhsBoth]) and (LogCaret.x > 1) then
  107. begin
  108. x := Lines.LogicPosAddChars(StartLine, LogCaret.x, -1);
  109. if (x <= length(StartLine)) and (StartLine[x] in Brackets) then
  110. begin
  111. StartBracket := LogCaret;
  112. StartBracket.x := x;
  113. EndBracket := TCustomSynEdit(SynEdit).FindMatchingBracketLogical(StartBracket, False, False, False, False);
  114. if EndBracket.y < 0 then
  115. StartBracket.y := -1;
  116. Exit;
  117. end;
  118. end;
  119. // check for bracket after caret
  120. if (HighlightStyle in [sbhsRightOfCursor, sbhsBoth]) then
  121. begin
  122. x := LogCaret.x ;
  123. if (x <= length(StartLine)) and (StartLine[x] in Brackets) then
  124. begin
  125. StartBracket := LogCaret;
  126. EndBracket := TCustomSynEdit(SynEdit).FindMatchingBracketLogical(LogCaret, False, False, False, False);
  127. if EndBracket.y < 0 then
  128. StartBracket.y := -1;
  129. end;
  130. end;
  131. end;
  132. procedure TSynEditMarkupBracket.DoCaretChanged(Sender: TObject);
  133. begin
  134. InvalidateBracketHighlight;
  135. end;
  136. procedure TSynEditMarkupBracket.DoTopLineChanged(OldTopLine: Integer);
  137. begin
  138. InvalidateBracketHighlight;
  139. end;
  140. procedure TSynEditMarkupBracket.DoLinesInWindoChanged(OldLinesInWindow: Integer);
  141. begin
  142. InvalidateBracketHighlight;
  143. end;
  144. procedure TSynEditMarkupBracket.DoTextChanged(StartLine, EndLine,
  145. ACountDiff: Integer);
  146. begin
  147. InvalidateBracketHighlight;
  148. end;
  149. procedure TSynEditMarkupBracket.DoMarkupChanged(AMarkup: TSynSelectedColor);
  150. begin
  151. InvalidateBracketHighlight;
  152. end;
  153. procedure TSynEditMarkupBracket.DoEnabledChanged(Sender: TObject);
  154. begin
  155. InvalidateBracketHighlight;
  156. end;
  157. procedure TSynEditMarkupBracket.DoVisibleChanged(AVisible: Boolean);
  158. begin
  159. inherited DoVisibleChanged(AVisible);
  160. if SynEdit.IsVisible then
  161. InvalidateBracketHighlight;
  162. end;
  163. procedure TSynEditMarkupBracket.InvalidateBracketHighlight;
  164. var
  165. NewPos, NewAntiPos, SwapPos : TPoint;
  166. begin
  167. FNeedInvalidate := True;
  168. if (Caret = nil) or (not SynEdit.HandleAllocated) or (FPaintLock > 0) or
  169. (not SynEdit.IsVisible)
  170. then
  171. exit;
  172. FNeedInvalidate := False;
  173. NewPos.Y:=-1;
  174. NewAntiPos.Y:=-1;
  175. if eoBracketHighlight in TCustomSynEdit(SynEdit).Options
  176. then FindMatchingBracketPair(Caret.LineBytePos, NewPos, NewAntiPos);
  177. // Always keep ordered
  178. if (NewAntiPos.Y > 0)
  179. and ((NewAntiPos.Y < NewPos.Y) or ((NewAntiPos.Y = NewPos.Y) and (NewAntiPos.X < NewPos.X)))
  180. then begin
  181. SwapPos := NewAntiPos;
  182. NewAntiPos := NewPos;
  183. NewPos := SwapPos;
  184. end;
  185. // invalidate old bracket highlighting, if changed
  186. if (FBracketHighlightPos.Y > 0)
  187. and ((FBracketHighlightPos.Y <> NewPos.Y) or (FBracketHighlightPos.X <> NewPos.X))
  188. then begin
  189. //DebugLn('TCustomSynEdit.InvalidateBracketHighlight A Y=',dbgs(FBracketHighlightPos));
  190. InvalidateSynLines(FBracketHighlightPos.Y,FBracketHighlightPos.Y);
  191. end;
  192. if (FBracketHighlightAntiPos.Y > 0)
  193. and (FBracketHighlightPos.Y <> FBracketHighlightAntiPos.Y)
  194. and ((FBracketHighlightAntiPos.Y <> NewAntiPos.Y) or (FBracketHighlightAntiPos.X <> NewAntiPos.X))
  195. then
  196. InvalidateSynLines(FBracketHighlightAntiPos.Y,FBracketHighlightAntiPos.Y);
  197. // invalidate new bracket highlighting, if changed
  198. if NewPos.Y>0 then begin
  199. //DebugLn('TCustomSynEdit.InvalidateBracketHighlight C Y=',dbgs(NewPos.Y),' X=',dbgs(NewPos.X),' Y=',dbgs(NewAntiPos.Y),' X=',dbgs(NewAntiPos.X));
  200. if ((FBracketHighlightPos.Y <> NewPos.Y) or (FBracketHighlightPos.X <> NewPos.X))
  201. then InvalidateSynLines(NewPos.Y, NewPos.Y);
  202. if ((NewPos.Y <> NewAntiPos.Y)
  203. or ((FBracketHighlightPos.Y = NewPos.Y) and (FBracketHighlightPos.X = NewPos.X))
  204. )
  205. and ((FBracketHighlightAntiPos.Y <> NewAntiPos.Y) or (FBracketHighlightAntiPos.X <> NewAntiPos.X))
  206. then InvalidateSynLines(NewAntiPos.Y, NewAntiPos.Y);
  207. end;
  208. FBracketHighlightPos := NewPos;
  209. FBracketHighlightAntiPos := NewAntiPos;
  210. // DebugLn('TCustomSynEdit.InvalidateBracketHighlight C P=',dbgs(NewPos),' A=',dbgs(NewAntiPos), ' LP=',dbgs(fLogicalPos),' LA',dbgs(fLogicalAntiPos));
  211. end;
  212. function TSynEditMarkupBracket.GetMarkupAttributeAtRowCol(const aRow: Integer;
  213. const aStartCol: TLazSynDisplayTokenBound; const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
  214. begin
  215. Result := nil;
  216. if ((FBracketHighlightPos.y = aRow) and (FBracketHighlightPos.x = aStartCol.Logical))
  217. or ((FBracketHighlightAntiPos.y = aRow) and (FBracketHighlightAntiPos.x = aStartCol.Logical))
  218. then begin
  219. Result := MarkupInfo;
  220. MarkupInfo.SetFrameBoundsLog(aStartCol.Logical, aStartCol.Logical + 1); // bracket is alvays 1 byte
  221. end;
  222. end;
  223. procedure TSynEditMarkupBracket.GetNextMarkupColAfterRowCol(const aRow: Integer;
  224. const aStartCol: TLazSynDisplayTokenBound; const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys,
  225. ANextLog: Integer);
  226. begin
  227. ANextLog := -1;
  228. ANextPhys := -1;
  229. if (FBracketHighlightPos.y = aRow) then begin
  230. if (FBracketHighlightPos.x > aStartCol.Logical )
  231. then ANextLog := FBracketHighlightPos.x
  232. else if (FBracketHighlightPos.x + 1 > aStartCol.Logical )
  233. then ANextLog := FBracketHighlightPos.x + 1; // end of bracket
  234. end;
  235. if (FBracketHighlightAntiPos.y = aRow) then begin
  236. if (FBracketHighlightAntiPos.x > aStartCol.Logical )
  237. and ((FBracketHighlightAntiPos.x < ANextLog) or (ANextLog < 0))
  238. then ANextLog := FBracketHighlightAntiPos.x
  239. else if (FBracketHighlightAntiPos.x + 1 > aStartCol.Logical )
  240. and ((FBracketHighlightAntiPos.x + 1 < ANextLog) or (ANextLog < 0))
  241. then ANextLog := FBracketHighlightAntiPos.x + 1;
  242. end
  243. end;
  244. end.