| 1 | unit UBFHighlighter; | 
|---|
| 2 | (* | 
|---|
| 3 | This is an example how to implement your own highlighter. | 
|---|
| 4 |  | 
|---|
| 5 | This example does allow to specify different colors for | 
|---|
| 6 | - text (defaults to not-highlighted) | 
|---|
| 7 | - spaces  (defaults to silver frame) | 
|---|
| 8 | - words, separated by spaces, that start with a,e,i,o,u  (defaults to bold) | 
|---|
| 9 | - the word "not"  (defaults to red background) | 
|---|
| 10 |  | 
|---|
| 11 | See comments below and http://wiki.lazarus.freepascal.org/SynEdit_Highlighter | 
|---|
| 12 |  | 
|---|
| 13 | How it works: | 
|---|
| 14 |  | 
|---|
| 15 | - Creation | 
|---|
| 16 | The Highlighter creates Attributes that it can return the Words and Spaces. | 
|---|
| 17 |  | 
|---|
| 18 | - SetLine | 
|---|
| 19 | Is called by SynEdit before a line gets painted (or before highlight info is needed) | 
|---|
| 20 | This is also called, each time the text changes fol *all* changed lines | 
|---|
| 21 | and may even be called for all lines after the change up to the end of text. | 
|---|
| 22 |  | 
|---|
| 23 | After SetLine was called "GetToken*" should return information about the | 
|---|
| 24 | first token on the line. | 
|---|
| 25 | Note: Spaces are token too. | 
|---|
| 26 |  | 
|---|
| 27 | - Next | 
|---|
| 28 | Scan to the next token, on the line that was set by "SetLine" | 
|---|
| 29 | "GetToken*"  should return info about that next token. | 
|---|
| 30 |  | 
|---|
| 31 | - GetEOL | 
|---|
| 32 | Returns True, if "Next" was called while on the last token of the line. | 
|---|
| 33 |  | 
|---|
| 34 | - GetTokenEx, GetTokenAttribute | 
|---|
| 35 | Provide info about the token found by "Next" | 
|---|
| 36 |  | 
|---|
| 37 | - Next, GetEOL. GetToken* | 
|---|
| 38 | Are used by SynEdit to iterate over the Line. | 
|---|
| 39 | Important: The tokens returned for each line, must represent the original | 
|---|
| 40 | line-text (mothing added, nothing left out), and be returned in the correct order. | 
|---|
| 41 |  | 
|---|
| 42 | They are called very often and should perform ath high speed. | 
|---|
| 43 |  | 
|---|
| 44 | - GetToken, GetTokenPos, GetTokenKind | 
|---|
| 45 | SynEdit uses them e.g for finding matching brackets. If GetTokenKind returns different values per Attribute, then brackets only match, if they are of the same kind (e.g, if there was a string attribute, brackets outside a string would not match brackets inside a string) | 
|---|
| 46 |  | 
|---|
| 47 |  | 
|---|
| 48 | *) | 
|---|
| 49 |  | 
|---|
| 50 | {$mode objfpc}{$H+} | 
|---|
| 51 |  | 
|---|
| 52 | interface | 
|---|
| 53 |  | 
|---|
| 54 | uses | 
|---|
| 55 | Classes, SysUtils, Graphics, SynEditTypes, SynEditHighlighter; | 
|---|
| 56 |  | 
|---|
| 57 | type | 
|---|
| 58 |  | 
|---|
| 59 | { TSynBrainFuckHl } | 
|---|
| 60 |  | 
|---|
| 61 |  | 
|---|
| 62 | TSynBrainFuckHl = class(TSynCustomHighlighter) | 
|---|
| 63 | private | 
|---|
| 64 | FCommentAttri: TSynHighlighterAttributes; | 
|---|
| 65 | FIoAttri: TSynHighlighterAttributes; | 
|---|
| 66 | FLoopAttri: TSynHighlighterAttributes; | 
|---|
| 67 | FMemoryAttri: TSynHighlighterAttributes; | 
|---|
| 68 | FPointerAttri: TSynHighlighterAttributes; | 
|---|
| 69 | FSpaceAttri: TSynHighlighterAttributes; | 
|---|
| 70 | procedure SetCommentAttri(AValue: TSynHighlighterAttributes); | 
|---|
| 71 | procedure SetMemoryAttri(AValue: TSynHighlighterAttributes); | 
|---|
| 72 | procedure SetPointerAttri(AValue: TSynHighlighterAttributes); | 
|---|
| 73 | procedure SetSpaceAttri(AValue: TSynHighlighterAttributes); | 
|---|
| 74 | procedure SetIoAttri(AValue: TSynHighlighterAttributes); | 
|---|
| 75 | procedure SetLoopAttri(AValue: TSynHighlighterAttributes); | 
|---|
| 76 | function IsWhiteSpace(C: Char): Boolean; | 
|---|
| 77 | protected | 
|---|
| 78 | // accesible for the other examples | 
|---|
| 79 | FTokenPos, FTokenEnd: Integer; | 
|---|
| 80 | FLineText: String; | 
|---|
| 81 | public | 
|---|
| 82 | procedure SetLine(const NewValue: String; LineNumber: Integer); override; | 
|---|
| 83 | procedure Next; override; | 
|---|
| 84 | function  GetEol: Boolean; override; | 
|---|
| 85 | procedure GetTokenEx(out TokenStart: PChar; out TokenLength: integer); override; | 
|---|
| 86 | function  GetTokenAttribute: TSynHighlighterAttributes; override; | 
|---|
| 87 | public | 
|---|
| 88 | function GetToken: String; override; | 
|---|
| 89 | function GetTokenPos: Integer; override; | 
|---|
| 90 | function GetTokenKind: integer; override; | 
|---|
| 91 | function GetDefaultAttribute(Index: Integer): TSynHighlighterAttributes; override; | 
|---|
| 92 | constructor Create(AOwner: TComponent); override; | 
|---|
| 93 | published | 
|---|
| 94 | property IoAttri: TSynHighlighterAttributes read FIoAttri | 
|---|
| 95 | write SetIoAttri; | 
|---|
| 96 | property LoopAttri: TSynHighlighterAttributes read FLoopAttri | 
|---|
| 97 | write SetLoopAttri; | 
|---|
| 98 | property PointerAttri: TSynHighlighterAttributes read FPointerAttri | 
|---|
| 99 | write SetPointerAttri; | 
|---|
| 100 | property MemoryAttri: TSynHighlighterAttributes read FMemoryAttri | 
|---|
| 101 | write SetMemoryAttri; | 
|---|
| 102 | property SpaceAttri: TSynHighlighterAttributes read FSpaceAttri | 
|---|
| 103 | write SetSpaceAttri; | 
|---|
| 104 | property CommentAttri: TSynHighlighterAttributes read FCommentAttri | 
|---|
| 105 | write SetCommentAttri; | 
|---|
| 106 | end; | 
|---|
| 107 |  | 
|---|
| 108 | implementation | 
|---|
| 109 |  | 
|---|
| 110 | constructor TSynBrainFuckHl.Create(AOwner: TComponent); | 
|---|
| 111 | begin | 
|---|
| 112 | inherited Create(AOwner); | 
|---|
| 113 |  | 
|---|
| 114 | (* Create and initialize the attributes *) | 
|---|
| 115 | FIoAttri := TSynHighlighterAttributes.Create('keyword', 'keyword'); | 
|---|
| 116 | AddAttribute(FIoAttri); | 
|---|
| 117 | FIoAttri.Style := [fsBold]; | 
|---|
| 118 |  | 
|---|
| 119 | FLoopAttri := TSynHighlighterAttributes.Create('loop', 'loop'); | 
|---|
| 120 | AddAttribute(FLoopAttri); | 
|---|
| 121 | FLoopAttri.Style := [fsBold]; | 
|---|
| 122 | FLoopAttri.Foreground := clRed; | 
|---|
| 123 |  | 
|---|
| 124 | FPointerAttri := TSynHighlighterAttributes.Create('pointer', 'pointer'); | 
|---|
| 125 | AddAttribute(FPointerAttri); | 
|---|
| 126 | FPointerAttri.Style := [fsBold]; | 
|---|
| 127 | FPointerAttri.Foreground := clBlue; | 
|---|
| 128 |  | 
|---|
| 129 | FMemoryAttri := TSynHighlighterAttributes.Create('memory', 'memory'); | 
|---|
| 130 | AddAttribute(FMemoryAttri); | 
|---|
| 131 | FMemoryAttri.Style := [fsBold]; | 
|---|
| 132 | FMemoryAttri.Foreground := clGreen; | 
|---|
| 133 |  | 
|---|
| 134 | FCommentAttri := TSynHighlighterAttributes.Create('comment', 'comment'); | 
|---|
| 135 | AddAttribute(FCommentAttri); | 
|---|
| 136 | FCommentAttri.Foreground := clSilver; | 
|---|
| 137 |  | 
|---|
| 138 | FSpaceAttri := TSynHighlighterAttributes.Create('space', 'space'); | 
|---|
| 139 | AddAttribute(FSpaceAttri); | 
|---|
| 140 | //FSpaceAttri.FrameColor := clSilver; | 
|---|
| 141 | //FSpaceAttri.FrameEdges := sfeAround; | 
|---|
| 142 |  | 
|---|
| 143 | // Ensure the HL reacts to changes in the attributes. Do this once, if all attributes are created | 
|---|
| 144 | SetAttributesOnChange(@DefHighlightChange); | 
|---|
| 145 | end; | 
|---|
| 146 |  | 
|---|
| 147 | (* Setters for attributes / This allows using in Object inspector*) | 
|---|
| 148 | procedure TSynBrainFuckHl.SetCommentAttri(AValue: TSynHighlighterAttributes); | 
|---|
| 149 | begin | 
|---|
| 150 | FCommentAttri.Assign(AValue); | 
|---|
| 151 | end; | 
|---|
| 152 |  | 
|---|
| 153 | procedure TSynBrainFuckHl.SetMemoryAttri(AValue: TSynHighlighterAttributes); | 
|---|
| 154 | begin | 
|---|
| 155 | FMemoryAttri.Assign(AValue); | 
|---|
| 156 | end; | 
|---|
| 157 |  | 
|---|
| 158 | procedure TSynBrainFuckHl.SetPointerAttri(AValue: TSynHighlighterAttributes); | 
|---|
| 159 | begin | 
|---|
| 160 | FPointerAttri.Assign(AValue); | 
|---|
| 161 | end; | 
|---|
| 162 |  | 
|---|
| 163 | procedure TSynBrainFuckHl.SetSpaceAttri(AValue: TSynHighlighterAttributes); | 
|---|
| 164 | begin | 
|---|
| 165 | FSpaceAttri.Assign(AValue); | 
|---|
| 166 | end; | 
|---|
| 167 |  | 
|---|
| 168 | procedure TSynBrainFuckHl.SetIoAttri(AValue: TSynHighlighterAttributes); | 
|---|
| 169 | begin | 
|---|
| 170 | FIoAttri.Assign(AValue); | 
|---|
| 171 | end; | 
|---|
| 172 |  | 
|---|
| 173 | procedure TSynBrainFuckHl.SetLoopAttri(AValue: TSynHighlighterAttributes); | 
|---|
| 174 | begin | 
|---|
| 175 | FLoopAttri.Assign(AValue); | 
|---|
| 176 | end; | 
|---|
| 177 |  | 
|---|
| 178 | function TSynBrainFuckHl.IsWhiteSpace(C: Char): Boolean; | 
|---|
| 179 | begin | 
|---|
| 180 | Result := C in [#9, ' ']; | 
|---|
| 181 | end; | 
|---|
| 182 |  | 
|---|
| 183 | procedure TSynBrainFuckHl.SetLine(const NewValue: String; LineNumber: Integer); | 
|---|
| 184 | begin | 
|---|
| 185 | inherited; | 
|---|
| 186 | FLineText := NewValue; | 
|---|
| 187 | // Next will start at "FTokenEnd", so set this to 1 | 
|---|
| 188 | FTokenEnd := 1; | 
|---|
| 189 | Next; | 
|---|
| 190 | end; | 
|---|
| 191 |  | 
|---|
| 192 | procedure TSynBrainFuckHl.Next; | 
|---|
| 193 | var | 
|---|
| 194 | L: Integer; | 
|---|
| 195 | begin | 
|---|
| 196 | // FTokenEnd should be at the start of the next Token (which is the Token we want) | 
|---|
| 197 | FTokenPos := FTokenEnd; | 
|---|
| 198 | // assume empty, will only happen for EOL | 
|---|
| 199 | FTokenEnd := FTokenPos; | 
|---|
| 200 |  | 
|---|
| 201 | // Scan forward | 
|---|
| 202 | // FTokenEnd will be set 1 after the last char. That is: | 
|---|
| 203 | // - The first char of the next token | 
|---|
| 204 | // - or past the end of line (which allows GetEOL to work) | 
|---|
| 205 |  | 
|---|
| 206 | L := Length(FLineText); | 
|---|
| 207 | If FTokenPos > L then | 
|---|
| 208 | // At line end | 
|---|
| 209 | Exit | 
|---|
| 210 | else | 
|---|
| 211 | if FLineText[FTokenEnd] in [#9, ' '] then | 
|---|
| 212 | // At Space? Find end of spaces | 
|---|
| 213 | while (FTokenEnd <= L) and (FLineText[FTokenEnd] in [#0..#32]) do | 
|---|
| 214 | Inc(FTokenEnd) | 
|---|
| 215 | else | 
|---|
| 216 | // At None-Space? Find end of None-spaces | 
|---|
| 217 | //while (FTokenEnd <= L) and not(FLineText[FTokenEnd] in [#9, ' ']) do | 
|---|
| 218 | Inc(FTokenEnd); | 
|---|
| 219 | end; | 
|---|
| 220 |  | 
|---|
| 221 | function TSynBrainFuckHl.GetEol: Boolean; | 
|---|
| 222 | begin | 
|---|
| 223 | Result := FTokenPos > Length(FLineText); | 
|---|
| 224 | end; | 
|---|
| 225 |  | 
|---|
| 226 | procedure TSynBrainFuckHl.GetTokenEx(out TokenStart: PChar; out TokenLength: integer); | 
|---|
| 227 | begin | 
|---|
| 228 | TokenStart := @FLineText[FTokenPos]; | 
|---|
| 229 | TokenLength := FTokenEnd - FTokenPos; | 
|---|
| 230 | end; | 
|---|
| 231 |  | 
|---|
| 232 | function TSynBrainFuckHl.GetTokenAttribute: TSynHighlighterAttributes; | 
|---|
| 233 | begin | 
|---|
| 234 | // Match the text, specified by FTokenPos and FTokenEnd | 
|---|
| 235 | if IsWhiteSpace(FLineText[FTokenPos]) then | 
|---|
| 236 | Result := SpaceAttri | 
|---|
| 237 | else | 
|---|
| 238 | if FLineText[FTokenPos] in ['[', ']'] then | 
|---|
| 239 | Result := LoopAttri | 
|---|
| 240 | else | 
|---|
| 241 | if FLineText[FTokenPos] in ['<', '>'] then | 
|---|
| 242 | Result := PointerAttri | 
|---|
| 243 | else | 
|---|
| 244 | if FLineText[FTokenPos] in ['+', '-'] then | 
|---|
| 245 | Result := MemoryAttri | 
|---|
| 246 | else | 
|---|
| 247 | if FLineText[FTokenPos] in ['.', ','] then | 
|---|
| 248 | Result := IoAttri | 
|---|
| 249 | else | 
|---|
| 250 | Result := CommentAttri; | 
|---|
| 251 | end; | 
|---|
| 252 |  | 
|---|
| 253 | function TSynBrainFuckHl.GetToken: String; | 
|---|
| 254 | begin | 
|---|
| 255 | Result := Copy(FLineText, FTokenPos, FTokenEnd - FTokenPos); | 
|---|
| 256 | end; | 
|---|
| 257 |  | 
|---|
| 258 | function TSynBrainFuckHl.GetTokenPos: Integer; | 
|---|
| 259 | begin | 
|---|
| 260 | Result := FTokenPos - 1; | 
|---|
| 261 | end; | 
|---|
| 262 |  | 
|---|
| 263 | function TSynBrainFuckHl.GetDefaultAttribute(Index: Integer): TSynHighlighterAttributes; | 
|---|
| 264 | begin | 
|---|
| 265 | // Some default attributes | 
|---|
| 266 | case Index of | 
|---|
| 267 | SYN_ATTR_COMMENT: Result := FCommentAttri; | 
|---|
| 268 | SYN_ATTR_KEYWORD: Result := FIoAttri; | 
|---|
| 269 | SYN_ATTR_WHITESPACE: Result := FSpaceAttri; | 
|---|
| 270 | else Result := nil; | 
|---|
| 271 | end; | 
|---|
| 272 | end; | 
|---|
| 273 |  | 
|---|
| 274 | function TSynBrainFuckHl.GetTokenKind: integer; | 
|---|
| 275 | var | 
|---|
| 276 | a: TSynHighlighterAttributes; | 
|---|
| 277 | begin | 
|---|
| 278 | // Map Attribute into a unique number | 
|---|
| 279 | a := GetTokenAttribute; | 
|---|
| 280 | Result := 0; | 
|---|
| 281 | if a = FSpaceAttri then Result := 1 | 
|---|
| 282 | else if a = FIoAttri then Result := 2 | 
|---|
| 283 | else if a = FCommentAttri then Result := 3 | 
|---|
| 284 | else if a = FLoopAttri then Result := 4 | 
|---|
| 285 | else if a = FPointerAttri then Result := 5 | 
|---|
| 286 | else if a = FMemoryAttri then Result := 6; | 
|---|
| 287 | end; | 
|---|
| 288 |  | 
|---|
| 289 | end. | 
|---|
| 290 |  | 
|---|