Menu

[r4761]: / branches / parsnip / Src / Main / CS.SourceCode.Hiliter.Brushes.pas  Maximize  Restore  History

Download this file

484 lines (428 with data), 15.0 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
{
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/
*
* Copyright (C) 2013, Peter Johnson (www.delphidabbler.com).
*
* $Rev$
* $Date$
*
* Classes and types used to encapsulate a syntax highlighter brush.
}
unit CS.SourceCode.Hiliter.Brushes;
interface
uses
Collections.Lists,
SynEditHighlighter;
type
/// <summary>Record that encapsulates information about a syntax highlighter
/// brush attribute.</summary>
TSyntaxHiliterAttr = record
strict private
var
/// <summary>Value of ID property.</summary>
fID: string;
/// <summary>Value of FriendlyName property.</summary>
fFriendlyName: string;
public
/// <summary>Constructs a new record with the given ID and friendly name.
/// </summary>
constructor Create(const AID, AFriendlyName: string);
/// <summary>The attiribute's unique ID.</summary>
property ID: string read fID;
/// <summary>The attribute's friendly name suitable for displaying to
/// users.</summary>
property FriendlyName: string read fFriendlyName;
class function Compare(const Left, Right: TSyntaxHiliterAttr): Integer;
static;
function Hash: Integer;
end;
type
/// <summary>Encapsulates a syntax highlighter "brush" that supports syntax
/// highlighting source code in a certain language.</summary>
TSyntaxHiliterBrush = class abstract(TObject)
strict protected
/// <summary>Read accessor for the ID property.</summary>
function GetID: string; virtual; abstract;
/// <summary>Read accessor for the FriendlyName property.</summary>
function GetFriendlyName: string; virtual; abstract;
/// <summary>Read accessor for the SampleSourceCode property.</summary>
function GetSampleSourceCode: string; virtual; abstract;
public
/// <summary>Creates and returns a copy of this object.</summary<
function Clone: TSyntaxHiliterBrush; virtual; abstract;
/// <summary>Creates a SynEdit highlighter compatible highlighter object
/// suitable for use with the SynEdit control.</summary>
function CreateHighlighter: TSynCustomHighlighter; virtual; abstract;
/// <summary>Checks if this brush is null, i.e. performs no highlighting.
/// </summary>
function IsNull: Boolean; virtual; abstract;
/// <summary>Returns an array of highlighter attributes supported by the
/// brush.</summary>
function SupportedAttrs: TArray<TSyntaxHiliterAttr>; virtual; abstract;
/// <summary>Checks if the characters of the given brush ID are valid for
/// use in a brush identifier.</summary>
class function IsValidBrushID(const ID: string): Boolean;
/// <summary>Brush's unique ID string.</summary>
property ID: string read GetID;
/// <summary>Friendly name of brush, suitable for displaying to users.
/// </summary>
property FriendlyName: string read GetFriendlyName;
/// <summary>Sample source code for use in demonstrating the brush.
/// </summary>
property SampleSourceCode: string read GetSampleSourceCode;
end;
type
/// <summary>Container for methods that manipulate and provide information
/// about supported syntax highlighter brushes.</summary>
TSyntaxHiliterBrushes = record
strict private
class var
/// <summary>List of supported SynEdit based highlighter brushes.
/// </summary>
fSupportedHiliters: TList<TSynCustomHighlighterClass>;
/// <summary>Finds and returns the SynEdit highlighter class that
/// implements the brush with the given ID.</summary>
class function FindHiliterClass(const ID: string):
TSynCustomHighlighterClass; static;
public
const
/// <summary>Unique ID of Null brush.</summary>
NullBrushID = '_Null_';
public
/// <summary>Creates and initialises list of supported syntax highlighter
/// brushes.</summary>
class constructor Create;
/// <summary>Destroys class level objects.</summary>
class destructor Destroy;
/// <summary>Checks if the highlighter brush with the given ID exists.
/// </summary>
class function BrushExists(const ID: string): Boolean; static;
/// <summary>Creates and returns an instance of the brush object with the
/// given ID.</summary>
/// <remarks>It is the caller's responsibility to free the return object.
/// </remarks>
class function CreateBrush(const ID: string): TSyntaxHiliterBrush; static;
/// <summary>Creates and returns a null highlighter brush instance.
/// </summary>
/// <remarks>It is the caller's responsibility to free the return object.
/// </remarks>
class function CreateNullBrush: TSyntaxHiliterBrush; static;
/// <summary>Returns an array of IDs of supported highlighter brushes.
/// </summary>
class function SupportedBrushIDs: TArray<string>; static;
/// <summary>Returns ID of Pascal brush.</summary>
/// <remarks>Pascal is a special language in CodeSnip and there are
/// occasions when a Pascal brush ID is needed explicitly. This method
/// provides a safe way to get the ID.</remarks>
class function PascalBrushID: string; static;
class function AllSupportedAttrs: TArray<TSyntaxHiliterAttr>; static;
end;
implementation
uses
// Delphi
SysUtils,
Generics.Defaults,
// 3rd party
Collections.Base,
Collections.Sets,
SynEditStrConst,
SynHighlighterHtml,
SynHighlighterJScript,
SynHighlighterPas,
SynHighlighterPHP,
// Project
CS.Utils.Hashes,
UComparers,
UStrUtils;
type
/// <summary>Encapsulates a syntax highlighter brush that uses a wrapped
/// SynEdit highlighter component to perform the highlighting.</summary>
TSynEditBrush = class sealed(TSyntaxHiliterBrush)
strict private
var
/// <summary>Class of the SynEdit highlighter that this class
/// encapsulates.</summary>
fHighlighterClass: TSynCustomHighlighterClass;
strict protected
/// <summary>Read accessor for ID property.</summary>
/// <remarks>Gets the value from the wrapper SynEdit highlighter.</remarks>
function GetID: string; override;
/// <summary>Read accessor for FriendlyName property.</summary>
/// <remarks>Gets the value from the wrapper SynEdit highlighter.</remarks>
function GetFriendlyName: string; override;
/// <summary>Read accessor for the SampleSourceCode property.</summary>
/// <remarks>Gets the value from the wrapper SynEdit highlighter.</remarks>
function GetSampleSourceCode: string; override;
public
/// <summary>Creates and returns a copy of this object.</summary<
function Clone: TSyntaxHiliterBrush; override;
/// <summary>Constructs a new object instance that wraps the given SynEdit
/// highlighter component.</summary>
constructor Create(const HighlighterClass: TSynCustomHighlighterClass);
/// <summary>Creates a SynEdit highlighter compatible highlighter object
/// suitable for use with the SynEdit control.</summary>
/// <remarks>This highlighter has no styling associated with it. It is up
/// to the caller to apply the required styling.</remarks>
function CreateHighlighter: TSynCustomHighlighter; override;
/// <summary>Checks if this brush is null, i.e. performs no highlighting.
/// </summary>
/// <returns>Boolean. False.</returns>
function IsNull: Boolean; override;
/// <summary>Returns an array of highlighter attributes supported by the
/// brush.</summary>
function SupportedAttrs: TArray<TSyntaxHiliterAttr>; override;
end;
type
/// <summary>Encapsulates a null syntax highlighter object that has no effect
/// on content passed to it.</summary>
TNullBrush = class sealed(TSyntaxHiliterBrush)
strict protected
/// <summary>Read accessor for ID property.</summary>
/// <remarks>Always returns 'Null', which may not be used by any other
/// brush.</remarks>
function GetID: string; override;
/// <summary>Read accessor for FriendlyName property.</summary>
/// <remarks>Always returns 'None'.</remarks>
function GetFriendlyName: string; override;
/// <summary>Read accessor for the SampleSourceCode property.</summary>
/// <remarks>Returns some "Lorem Ipsum" text.</remarks>
function GetSampleSourceCode: string; override;
public
/// <summary>Creates and returns a copy of this object.</summary<
function Clone: TSyntaxHiliterBrush; override;
/// <summary>Creates a SynEdit highlighter compatible highlighter object
/// suitable for use with the SynEdit control.</summary>
/// <remarks>Actually this class simply returns nil, which is valid for
/// assigning to a SynEdit control to force it to skip highlighting of its
/// content.</remarks>
function CreateHighlighter: TSynCustomHighlighter; override;
/// <summary>Checks if this brush is null, i.e. performs no highlighting.
/// </summary>
/// <returns>Boolean. True.</returns>
function IsNull: Boolean; override;
/// <summary>Returns an array of highlighter attributes supported by the
/// brush.</summary>
/// <remarks>Returns an empty array: a null brush supports no attributes.
/// </remarks>
function SupportedAttrs: TArray<TSyntaxHiliterAttr>; override;
end;
{ TSyntaxHiliterBrushes }
class function TSyntaxHiliterBrushes.AllSupportedAttrs:
TArray<TSyntaxHiliterAttr>;
var
BrushID: string;
Brush: TSyntaxHiliterBrush;
AllAttrs: TLinkedSet<TSyntaxHiliterAttr>;
Attr: TSyntaxHiliterAttr;
begin
AllAttrs := TLinkedSet<TSyntaxHiliterAttr>.Create(
TRulesFactory<TSyntaxHiliterAttr>.Construct(
function (const Left, Right: TSyntaxHiliterAttr): Integer
begin
Result := TSyntaxHiliterAttr.Compare(Left, Right);
end,
function (const Value: TSyntaxHiliterAttr): Integer
begin
Result := Value.Hash;
end
)
);
try
for BrushID in SupportedBrushIDs do
begin
Brush := TSyntaxHiliterBrushes.CreateBrush(BrushID);
try
for Attr in Brush.SupportedAttrs do
if not AllAttrs.Contains(Attr) then
AllAttrs.Add(Attr);
finally
Brush.Free;
end;
end;
Result := AllAttrs.ToArray;
finally
AllAttrs.Free;
end;
end;
class function TSyntaxHiliterBrushes.BrushExists(
const ID: string): Boolean;
begin
Result := Assigned(FindHiliterClass(ID));
end;
class constructor TSyntaxHiliterBrushes.Create;
begin
fSupportedHiliters := TList<TSynCustomHighlighterClass>.Create;
with fSupportedHiliters do
begin
Add(TSynHTMLSyn);
Add(TSynJScriptSyn);
Add(TSynPasSyn);
Add(TSynPHPSyn);
end;
end;
class function TSyntaxHiliterBrushes.CreateBrush(const ID: string):
TSyntaxHiliterBrush;
var
Cls: TSynCustomHighlighterClass;
begin
Cls := FindHiliterClass(ID);
if Assigned(Cls) then
Result := TSynEditBrush.Create(Cls)
else
Result := TNullBrush.Create;
end;
class function TSyntaxHiliterBrushes.CreateNullBrush: TSyntaxHiliterBrush;
begin
Result := TNullBrush.Create;
end;
class destructor TSyntaxHiliterBrushes.Destroy;
begin
fSupportedHiliters.Free;
end;
class function TSyntaxHiliterBrushes.FindHiliterClass(const ID: string):
TSynCustomHighlighterClass;
var
Cls: TSynCustomHighlighterClass;
begin
for Cls in fSupportedHiliters do
begin
if StrSameText(Cls.GetLanguageName, ID) then
Exit(Cls);
end;
Result := nil;
end;
class function TSyntaxHiliterBrushes.PascalBrushID: string;
begin
Result := TSynPasSyn.GetLanguageName;
end;
class function TSyntaxHiliterBrushes.SupportedBrushIDs: TArray<string>;
var
I: Integer;
begin
SetLength(Result, fSupportedHiliters.Count);
for I := 0 to Pred(fSupportedHiliters.Count) do
Result[I] := fSupportedHiliters[I].GetLanguageName;
end;
{ TSyntaxHiliterBrush }
class function TSyntaxHiliterBrush.IsValidBrushID(const ID: string): Boolean;
var
Ch: Char;
begin
// Requirements are those for a SynEdit language identifier, i.e.:
// EITHER "<Unknown>"
// OR one or more of 'A'..'Z', 'a'..'z', '0'..'9', '_' or '-'
if ID = EmptyStr then
Exit(False);
if StrSameText(ID, SYNS_LangUnknown) then
Exit(True);
for Ch in ID do
begin
if not CharInSet(Ch, ['A'..'Z', 'a'..'z', '0'..'9', '_', '-']) then
Exit(False);
end;
Result := True;
end;
{ TSynEditBrush }
function TSynEditBrush.Clone: TSyntaxHiliterBrush;
begin
Result := TSynEditBrush.Create(fHighlighterClass);
end;
constructor TSynEditBrush.Create(
const HighlighterClass: TSynCustomHighlighterClass);
begin
inherited Create;
fHighlighterClass := HighlighterClass;
end;
function TSynEditBrush.CreateHighlighter: TSynCustomHighlighter;
begin
Result := fHighlighterClass.Create(nil);
end;
function TSynEditBrush.GetFriendlyName: string;
begin
Result := fHighlighterClass.GetFriendlyLanguageName;
end;
function TSynEditBrush.GetID: string;
begin
Result := fHighlighterClass.GetLanguageName;
end;
function TSynEditBrush.GetSampleSourceCode: string;
var
Highlighter: TSynCustomHighlighter;
begin
Highlighter := CreateHighlighter;
try
Result := Highlighter.SampleSource;
finally
Highlighter.Free;
end;
end;
function TSynEditBrush.IsNull: Boolean;
begin
Result := False;
end;
function TSynEditBrush.SupportedAttrs: TArray<TSyntaxHiliterAttr>;
var
Hiliter: TSynCustomHighlighter;
I: Integer;
begin
Hiliter := CreateHighlighter;
try
SetLength(Result, Hiliter.AttrCount);
for I := 0 to Pred(Hiliter.AttrCount) do
Result[I] := TSyntaxHiliterAttr.Create(
Hiliter.Attribute[I].Name,
Hiliter.Attribute[I].FriendlyName
);
finally
Hiliter.Free;
end;
end;
{ TNullBrush }
function TNullBrush.Clone: TSyntaxHiliterBrush;
begin
Result := TNullBrush.Create;
end;
function TNullBrush.CreateHighlighter: TSynCustomHighlighter;
begin
Result := nil;
end;
function TNullBrush.GetFriendlyName: string;
resourcestring
sFriendlyName = 'None';
begin
Result := sFriendlyName;
end;
function TNullBrush.GetID: string;
begin
Result := TSyntaxHiliterBrushes.NullBrushID;
end;
function TNullBrush.GetSampleSourceCode: string;
begin
Result := 'Lorem ipsum dolor sit amet,'#13#10'consectetur adipiscing elit.';
end;
function TNullBrush.IsNull: Boolean;
begin
Result := True;
end;
function TNullBrush.SupportedAttrs: TArray<TSyntaxHiliterAttr>;
begin
SetLength(Result, 0);
end;
{ TSyntaxHiliterAttr }
class function TSyntaxHiliterAttr.Compare(const Left,
Right: TSyntaxHiliterAttr): Integer;
begin
Result := StrCompareText(Left.ID, Right.ID);
end;
constructor TSyntaxHiliterAttr.Create(const AID, AFriendlyName: string);
begin
fID := AID;
fFriendlyName := AFriendlyName;
end;
function TSyntaxHiliterAttr.Hash: Integer;
begin
Result := TextHash(fID);
end;
end.
Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.