lazarus/components/synedit/synhighlighterhashentries.pas
2021-02-05 11:23:37 +00:00

216 lines
7.5 KiB
ObjectPascal

{-------------------------------------------------------------------------------
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
the specific language governing rights and limitations under the License.
The Original Code is: SynHighlighterHashEntries.pas, released 2000-04-21.
The Initial Author of this file is Michael Hieke.
Portions created by Michael Hieke are Copyright 2000 Michael Hieke.
All Rights Reserved.
Contributors to the SynEdit project are listed in the Contributors.txt file.
Alternatively, the contents of this file may be used under the terms of the
GNU General Public License Version 2 or later (the "GPL"), in which case
the provisions of the GPL are applicable instead of those above.
If you wish to allow use of your version of this file only under the terms
of the GPL and not to allow others to use your version of this file
under the MPL, indicate your decision by deleting the provisions above and
replace them with the notice and other provisions required by the GPL.
If you do not delete the provisions above, a recipient may use your version
of this file under either the MPL or the GPL.
$Id$
You may retrieve the latest version of this file at the SynEdit home page,
located at http://SynEdit.SourceForge.net
Known Issues:
-------------------------------------------------------------------------------}
{
@abstract(Support classes for SynEdit highlighters that create the keyword lists at runtime.)
@author(Michael Hieke)
@created(2000-04-21)
@lastmod(2001-09-07)
The classes in this unit can be used to use the hashing algorithm while still
having the ability to change the set of keywords.
}
unit SynHighlighterHashEntries;
{$I SynEdit.inc}
interface
uses
SysUtils, SynEditTypes, Classes;
type
{ Class to hold the keyword to recognize, its length and its token kind. The
keywords that have the same hashvalue are stored in a single-linked list,
with the Next property pointing to the next entry. The entries are ordered
over the keyword length. }
TSynHashEntry = class(TObject)
protected
{ Points to the next keyword entry with the same hashvalue. }
fNext: TSynHashEntry;
{ Length of the keyword. }
fKeyLen: integer;
{ The keyword itself. }
fKeyword: string;
{ Keyword token kind, has to be typecasted to the real token kind type. }
fKind: integer;
public
{ Adds a keyword entry with the same hashvalue. Depending on the length of
the two keywords it might return Self and store NewEntry in the Next
pointer, or return NewEntry and make the Next property of NewEntry point
to Self. This way the order of keyword length is preserved. }
function AddEntry(NewEntry: TSynHashEntry): TSynHashEntry; virtual;
{ Creates a keyword entry for the given keyword and token kind. }
constructor Create(const AKey: string; AKind: integer);
{ Destroys the keyword entry and all other keyword entries Next points to. }
destructor Destroy; override;
public
{ The keyword itself. }
property Keyword: string read fKeyword;
{ Length of the keyword. }
property KeywordLen: integer read fKeyLen;
{ Keyword token kind, has to be typecasted to the real token kind type. }
property Kind: integer read fKind;
{ Points to the next keyword entry with the same hashvalue. }
property Next: TSynHashEntry read fNext;
end;
{ A list of keyword entries, stored as single-linked lists under the hashvalue
of the keyword. }
TSynHashEntryList = class(TList)
protected
{ Returns the first keyword entry for a given hashcalue, or nil. }
function Get(HashKey: Integer): TSynHashEntry;
{ Adds a keyword entry under its hashvalue. Will grow the list count when
necessary, so the maximum hashvalue should be limited outside. The correct
order of keyword entries is maintained. }
procedure Put(HashKey: Integer; Entry: TSynHashEntry);
public
{ Clears the list and frees all contained keyword entries. }
procedure Clear; override;
public
{ Type-safe access to the first keyword entry for a hashvalue. }
property Items[Index: integer]: TSynHashEntry read Get write Put; default;
end;
{ Procedural type for adding keyword entries to a TSynHashEntryList when
iterating over all the keywords contained in a string. }
TEnumerateKeywordEvent = procedure(AKeyword: string; AKind: integer)
of object;
{ This procedure will call AKeywordProc for all keywords in KeywordList. A
keyword is considered any number of successive chars that are contained in
Identifiers, with chars not contained in Identifiers before and after them. }
procedure EnumerateKeywords(AKind: integer; KeywordList: string;
Identifiers: TSynIdentChars; AKeywordProc: TEnumerateKeywordEvent);
implementation
procedure EnumerateKeywords(AKind: integer; KeywordList: string;
Identifiers: TSynIdentChars; AKeywordProc: TEnumerateKeywordEvent);
var
pStart, pEnd: PChar;
Keyword: string;
begin
if Assigned(AKeywordProc) and (KeywordList <> '') then begin
pEnd := PChar(KeywordList);
pStart := pEnd;
repeat
// skip over chars that are not in Identifiers
while (pStart^ <> #0) and not (pStart^ in Identifiers) do
Inc(pStart);
if pStart^ = #0 then break;
// find the last char that is in Identifiers
pEnd := pStart + 1;
while (pEnd^ <> #0) and (pEnd^ in Identifiers) do
Inc(pEnd);
// call the AKeywordProc with the keyword
SetString(Keyword, pStart, pEnd - pStart);
AKeywordProc(Keyword, AKind);
Keyword := '';
// pEnd points to a char not in Identifiers, restart after that
pStart := pEnd + 1;
until (pStart^ = #0) or (pEnd^ = #0);
end;
end;
{ TSynHashEntry }
constructor TSynHashEntry.Create(const AKey: string; AKind: integer);
begin
inherited Create;
fKeyLen := Length(AKey);
fKeyword := AKey;
fKind := AKind;
end;
destructor TSynHashEntry.Destroy;
begin
fNext.Free;
inherited Destroy;
end;
function TSynHashEntry.AddEntry(NewEntry: TSynHashEntry): TSynHashEntry;
begin
Result := Self;
if Assigned(NewEntry) then begin
if CompareText(NewEntry.Keyword, fKeyword) = 0 then
raise Exception.CreateFmt('Keyword "%s" already in list', [fKeyword]);
if NewEntry.fKeyLen < fKeyLen then begin
NewEntry.fNext := Self;
Result := NewEntry;
end else if Assigned(fNext) then
fNext := fNext.AddEntry(NewEntry)
else
fNext := NewEntry;
end;
end;
{ TSynHashEntryList }
procedure TSynHashEntryList.Clear;
var
i: integer;
begin
for i := 0 to Count - 1 do
TSynHashEntry(Items[i]).Free;
inherited Clear;
end;
function TSynHashEntryList.Get(HashKey: Integer): TSynHashEntry;
begin
if (HashKey >= 0) and (HashKey < Count) then
Result := TSynHashEntry(inherited Items[HashKey])
else
Result := nil;
end;
procedure TSynHashEntryList.Put(HashKey: Integer; Entry: TSynHashEntry);
var
ListEntry: TSynHashEntry;
begin
if HashKey >= Count then
Count := HashKey + 1;
ListEntry := TSynHashEntry(inherited Items[HashKey]);
// if there is already a hashentry for this hashvalue let it decide
// where to put the new entry in its single linked list
if Assigned(ListEntry) then
Entry := ListEntry.AddEntry(Entry);
inherited Items[HashKey] := Entry;
end;
end.