Files
HeidiSQL/components/synedit/Source/SynHighlighterPython.pas
2021-03-16 20:12:46 +01:00

1335 lines
34 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: SynHighlighterPython.pas, released 2000-06-23.
The Original Code is based on the odPySyn.pas file from the
mwEdit component suite by Martin Waldenburg and other developers, the Initial
Author of this file is Olivier Deckmyn.
Portions created by M.Utku Karatas and Dennis Chuah.
Unicode translation by Maël Hörz.
All Rights Reserved.
Contributors to the SynEdit and mwEdit projects 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: SynHighlighterPython.pas,v 1.18.2.7 2008/09/14 16:25:02 maelh Exp $
You may retrieve the latest version of this file at the SynEdit home page,
located at http://SynEdit.SourceForge.net
Known Issues:
-------------------------------------------------------------------------------}
{
@abstract(A Python language highlighter for SynEdit)
@author(Olivier Deckmyn, converted to SynEdit by David Muir <dhmn@dmsoftware.co.uk>)
@created(unknown, converted to SynEdit on 2000-06-23)
@lastmod(2003-02-13)
The SynHighlighterPython implements a highlighter for Python for the SynEdit projects.
}
unit SynHighlighterPython;
{$I SynEdit.inc}
interface
uses
Graphics,
SynEditHighlighter,
SynEditTypes,
SynUnicode,
{$IFDEF SYN_CodeFolding}
SynEditCodeFolding,
SynRegExpr,
{$ENDIF}
SysUtils,
Classes;
const
ALPHA_CHARS = ['_', 'a'..'z', 'A'..'Z'];
type
TtkTokenKind = (tkComment, tkIdentifier, tkKey, tkNull, tkNumber, tkSpace,
tkString, tkSymbol, tkNonKeyword, tkTrippleQuotedString,
tkSystemDefined, tkHex, tkOct, tkFloat, tkUnknown);
TRangeState = (rsANil, rsComment, rsUnknown, rsMultilineString, rsMultilineString2,
rsMultilineString3 //this is to indicate if a string is made multiline by backslash char at line end (as in C++ highlighter)
);
type
{$IFDEF SYN_CodeFolding}
TSynPythonSyn = class(TSynCustomCodeFoldingHighlighter)
{$ELSE}
TSynPythonSyn = class(TSynCustomHighLighter)
{$ENDIF}
private
FStringStarter: WideChar; // used only for rsMultilineString3 stuff
FRange: TRangeState;
FTokenID: TtkTokenKind;
FKeywords: TUnicodeStringList;
FStringAttri: TSynHighlighterAttributes;
FDocStringAttri: TSynHighlighterAttributes;
FNumberAttri: TSynHighlighterAttributes;
FHexAttri: TSynHighlighterAttributes;
FOctalAttri: TSynHighlighterAttributes;
FFloatAttri: TSynHighlighterAttributes;
FKeyAttri: TSynHighlighterAttributes;
FNonKeyAttri: TSynHighlighterAttributes;
FSystemAttri: TSynHighlighterAttributes;
FSymbolAttri: TSynHighlighterAttributes;
FCommentAttri: TSynHighlighterAttributes;
FIdentifierAttri: TSynHighlighterAttributes;
FSpaceAttri: TSynHighlighterAttributes;
FErrorAttri: TSynHighlighterAttributes;
{$IFDEF SYN_CodeFolding}
BlockOpenerRE : TRegExpr;
{$ENDIF}
function IdentKind(MayBe: PWideChar): TtkTokenKind;
procedure SymbolProc;
procedure CRProc;
procedure CommentProc;
procedure GreaterProc;
procedure IdentProc;
procedure LFProc;
procedure LowerProc;
procedure NullProc;
procedure NumberProc;
procedure SpaceProc;
procedure PreStringProc;
procedure UnicodeStringProc;
procedure StringProc;
procedure String2Proc;
procedure StringEndProc(EndChar: WideChar);
procedure UnknownProc;
protected
function GetSampleSource: UnicodeString; override;
function IsFilterStored: Boolean; override;
function GetKeywordIdentifiers: TUnicodeStringList;
property Keywords: TUnicodeStringList read FKeywords;
property TokenID: TtkTokenKind read FTokenID;
public
class function GetLanguageName: string; override;
class function GetFriendlyLanguageName: UnicodeString; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function GetDefaultAttribute(Index: Integer): TSynHighlighterAttributes;
override;
function GetEol: Boolean; override;
function GetRange: Pointer; override;
function GetTokenID: TtkTokenKind;
function GetTokenAttribute: TSynHighlighterAttributes; override;
function GetTokenKind: Integer; override;
procedure Next; override;
procedure SetRange(Value: Pointer); override;
procedure ResetRange; override;
{$IFDEF SYN_CodeFolding}
procedure InitFoldRanges(FoldRanges : TSynFoldRanges); override;
procedure ScanForFoldRanges(FoldRanges: TSynFoldRanges;
LinesToScan: TStrings; FromLine: Integer; ToLine: Integer); override;
{$ENDIF}
published
property CommentAttri: TSynHighlighterAttributes read FCommentAttri
write FCommentAttri;
property IdentifierAttri: TSynHighlighterAttributes read FIdentifierAttri
write FIdentifierAttri;
property KeyAttri: TSynHighlighterAttributes read FKeyAttri write FKeyAttri;
property NonKeyAttri: TSynHighlighterAttributes read FNonKeyAttri
write FNonKeyAttri;
property SystemAttri: TSynHighlighterAttributes read FSystemAttri
write FSystemAttri;
property NumberAttri: TSynHighlighterAttributes read FNumberAttri
write FNumberAttri;
property HexAttri: TSynHighlighterAttributes read FHexAttri
write FHexAttri;
property OctalAttri: TSynHighlighterAttributes read FOctalAttri
write FOctalAttri;
property FloatAttri: TSynHighlighterAttributes read FFloatAttri
write FFloatAttri;
property SpaceAttri: TSynHighlighterAttributes read FSpaceAttri
write FSpaceAttri;
property StringAttri: TSynHighlighterAttributes read FStringAttri
write FStringAttri;
property DocStringAttri: TSynHighlighterAttributes read FDocStringAttri
write FDocStringAttri;
property SymbolAttri: TSynHighlighterAttributes read FSymbolAttri
write FSymbolAttri;
property ErrorAttri: TSynHighlighterAttributes read FErrorAttri
write FErrorAttri;
end;
implementation
uses
SynEditStrConst;
var
GlobalKeywords: TUnicodeStringList;
function TSynPythonSyn.GetKeywordIdentifiers: TUnicodeStringList;
const
// No need to localise keywords!
// List of keywords
KEYWORDCOUNT = 32;
KEYWORDS: array [1..KEYWORDCOUNT] of UnicodeString =
(
'and',
'as',
'assert',
'break',
'class',
'continue',
'def',
'del',
'elif',
'else',
'except',
'exec',
'finally',
'for',
'from',
'global',
'if',
'import',
'in',
'is',
'lambda',
'nonlocal',
'not',
'or',
'pass',
'print',
'raise',
'return',
'try',
'while',
'with',
'yield'
);
// List of non-keyword identifiers
NONKEYWORDCOUNT = 65;
NONKEYWORDS: array [1..NONKEYWORDCOUNT] of UnicodeString =
(
'__future__',
'__import__',
'abs',
'apply',
'buffer',
'callable',
'chr',
'cmp',
'coerce',
'compile',
'complex',
'delattr',
'dict',
'dir',
'divmod',
'eval',
'execfile',
'False',
'file',
'filter',
'float',
'getattr',
'globals',
'hasattr',
'hash',
'help',
'hex',
'id',
'input',
'int',
'intern',
'isinstance',
'issubclass',
'iter',
'len',
'list',
'locals',
'long',
'None',
'NotImplemented',
'map',
'max',
'min',
'oct',
'open',
'ord',
'pow',
'range',
'raw_input',
'reduce',
'reload',
'repr',
'round',
'self',
'setattr',
'slice',
'str',
'True',
'tuple',
'type',
'unichr',
'unicode',
'vars',
'xrange',
'zip'
);
var
f: Integer;
begin
if not Assigned (GlobalKeywords) then
begin
// Create the string list of keywords - only once
GlobalKeywords := TUnicodeStringList.Create;
for f := 1 to KEYWORDCOUNT do
GlobalKeywords.AddObject(KEYWORDS[f], Pointer(Ord(tkKey)));
for f := 1 to NONKEYWORDCOUNT do
GlobalKeywords.AddObject(NONKEYWORDS[f], Pointer(Ord(tkNonKeyword)));
end; // if
Result := GlobalKeywords;
end;
function TSynPythonSyn.IdentKind(MayBe: PWideChar): TtkTokenKind;
var
i: Integer;
temp: PWideChar;
s: UnicodeString;
begin
// Extract the identifier out - it is assumed to terminate in a
// non-alphanumeric character
FToIdent := MayBe;
temp := MayBe;
while IsIdentChar(temp^) do
Inc(temp);
FStringLen := temp - FToIdent;
// Check to see if it is a keyword
SetString(s, FToIdent, FStringLen);
if FKeywords.Find(s, i) then
begin
// TUnicodeStringList is not case sensitive!
if s <> FKeywords[i] then
i := -1;
end
else
i := -1;
if i <> -1 then
Result := TtkTokenKind(FKeywords.Objects[i])
// Check if it is a system identifier (__*__)
else if (FStringLen >= 5) and
(MayBe[0] = '_') and (MayBe[1] = '_') and (MayBe[2] <> '_') and
(MayBe[FStringLen - 1] = '_') and (MayBe[FStringLen - 2] = '_') and
(MayBe[FStringLen - 3] <> '_') then
Result := tkSystemDefined
// Check for names of class and functions - not optimal
else if ( (WideCompareStr(Trim(Copy(FLine, 0, Length(FLine) - Length(FToIdent))), 'def')=0) or (WideCompareStr(Trim(Copy(FLine, 0, Length(FLine) - Length(FToIdent))), 'class')=0) ) then
Result := tkSystemDefined
// Else, hey, it is an ordinary run-of-the-mill identifier!
else
Result := tkIdentifier;
end;
constructor TSynPythonSyn.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FCaseSensitive := True;
FKeywords := TUnicodeStringList.Create;
FKeywords.Sorted := True;
FKeywords.Duplicates := dupError;
FKeywords.Assign (GetKeywordIdentifiers);
if not FKeywords.Sorted then
FKeywords.Sort;
{$IFDEF SYN_CodeFolding}
BlockOpenerRE := TRegExpr.Create;
BlockOpenerRE.Expression := // ':\s*(#.*)?$';
'^(def|class|while|for|if|else|elif|try|except|with'+
'|(async[ \t]+def)|(async[ \t]+with)|(async[ \t]+for))\b';
{$ENDIF}
FRange := rsUnknown;
FCommentAttri := TSynHighlighterAttributes.Create(SYNS_AttrComment, SYNS_FriendlyAttrComment);
FCommentAttri.Foreground := clGray;
FCommentAttri.Style := [fsItalic];
AddAttribute(FCommentAttri);
FIdentifierAttri := TSynHighlighterAttributes.Create(SYNS_AttrIdentifier, SYNS_FriendlyAttrIdentifier);
AddAttribute(FIdentifierAttri);
FKeyAttri := TSynHighlighterAttributes.Create(SYNS_AttrReservedWord, SYNS_FriendlyAttrReservedWord);
FKeyAttri.Style := [fsBold];
AddAttribute(FKeyAttri);
FNonKeyAttri := TSynHighlighterAttributes.Create (SYNS_AttrNonReservedKeyword, SYNS_FriendlyAttrNonReservedKeyword);
FNonKeyAttri.Foreground := clNavy;
FNonKeyAttri.Style := [fsBold];
AddAttribute (FNonKeyAttri);
FSystemAttri := TSynHighlighterAttributes.Create (SYNS_AttrSystem, SYNS_FriendlyAttrSystem);
FSystemAttri.Style := [fsBold];
AddAttribute (FSystemAttri);
FNumberAttri := TSynHighlighterAttributes.Create(SYNS_AttrNumber, SYNS_FriendlyAttrNumber);
FNumberAttri.Foreground := clBlue;
AddAttribute(FNumberAttri);
FHexAttri := TSynHighlighterAttributes.Create(SYNS_AttrHexadecimal, SYNS_FriendlyAttrHexadecimal);
FHexAttri.Foreground := clBlue;
AddAttribute(FHexAttri);
FOctalAttri := TSynHighlighterAttributes.Create(SYNS_AttrOctal, SYNS_FriendlyAttrOctal);
FOctalAttri.Foreground := clBlue;
AddAttribute(FOctalAttri);
FFloatAttri := TSynHighlighterAttributes.Create(SYNS_AttrFloat, SYNS_FriendlyAttrFloat);
FFloatAttri.Foreground := clBlue;
AddAttribute(FFloatAttri);
FSpaceAttri := TSynHighlighterAttributes.Create(SYNS_AttrSpace, SYNS_FriendlyAttrSpace);
AddAttribute(FSpaceAttri);
FStringAttri := TSynHighlighterAttributes.Create(SYNS_AttrString, SYNS_FriendlyAttrString);
FStringAttri.Foreground := clBlue;
AddAttribute(FStringAttri);
FDocStringAttri := TSynHighlighterAttributes.Create(SYNS_AttrDocumentation, SYNS_FriendlyAttrDocumentation);
FDocStringAttri.Foreground := clTeal;
AddAttribute(FDocStringAttri);
FSymbolAttri := TSynHighlighterAttributes.Create(SYNS_AttrSymbol, SYNS_FriendlyAttrSymbol);
AddAttribute(FSymbolAttri);
FErrorAttri := TSynHighlighterAttributes.Create(SYNS_AttrSyntaxError, SYNS_FriendlyAttrSyntaxError);
FErrorAttri.Foreground := clRed;
AddAttribute(FErrorAttri);
SetAttributesOnChange(DefHighlightChange);
FDefaultFilter := SYNS_FilterPython;
end; { Create }
destructor TSynPythonSyn.Destroy;
begin
{$IFDEF SYN_CodeFolding}
BlockOpenerRE.Free;
{$ENDIF}
FKeywords.Free;
inherited;
end;
procedure TSynPythonSyn.SymbolProc;
begin
Inc(Run);
FTokenID := tkSymbol;
end;
procedure TSynPythonSyn.CRProc;
begin
FTokenID := tkSpace;
case FLine[Run + 1] of
#10: Inc(Run, 2);
else
Inc(Run);
end;
end;
procedure TSynPythonSyn.CommentProc;
begin
FTokenID := tkComment;
Inc(Run);
while not IsLineEnd(Run) do
Inc(Run);
end;
procedure TSynPythonSyn.GreaterProc;
begin
case FLine[Run + 1] of
'=': begin
Inc(Run, 2);
FTokenID := tkSymbol;
end;
else begin
Inc(Run);
FTokenID := tkSymbol;
end;
end;
end;
procedure TSynPythonSyn.IdentProc;
begin
FTokenID := IdentKind((FLine + Run));
Inc(Run, FStringLen);
end;
procedure TSynPythonSyn.LFProc;
begin
FTokenID := tkSpace;
Inc(Run);
end;
procedure TSynPythonSyn.LowerProc;
begin
case FLine[Run + 1] of
'=': begin
Inc(Run, 2);
FTokenID := tkSymbol;
end;
'>': begin
Inc(Run, 2);
FTokenID := tkSymbol;
end
else begin
Inc(Run);
FTokenID := tkSymbol;
end;
end;
end;
procedure TSynPythonSyn.NullProc;
begin
FTokenID := tkNull;
Inc(Run);
end;
procedure TSynPythonSyn.NumberProc;
type
TNumberState =
(
nsStart,
nsDotFound,
nsFloatNeeded,
nsHex,
nsOct,
nsExpFound
);
var
temp: WideChar;
State: TNumberState;
function CheckSpecialCases: Boolean;
begin
case temp of
// Look for dot (.)
'.': begin
// .45
if CharInSet(FLine[Run], ['0'..'9']) then
begin
Inc (Run);
FTokenID := tkFloat;
State := nsDotFound;
// Non-number dot
end else begin
// Ellipsis
if (FLine[Run] = '.') and (FLine[Run+1] = '.') then
Inc (Run, 2);
FTokenID := tkSymbol;
Result := False;
Exit;
end; // if
end; // DOT
// Look for zero (0)
'0': begin
temp := FLine[Run];
// 0x123ABC
if CharInSet(temp, ['x', 'X']) then begin
Inc (Run);
FTokenID := tkHex;
State := nsHex;
// 0.45
end else if temp = '.' then begin
Inc (Run);
State := nsDotFound;
FTokenID := tkFloat;
end else if CharInSet(temp, ['0'..'9']) then begin
Inc (Run);
// 0123 or 0123.45
if CharInSet(temp, ['0'..'7']) then begin
FTokenID := tkOct;
State := nsOct;
// 0899.45
end else begin
FTokenID := tkFloat;
State := nsFloatNeeded;
end; // if
end; // if
end; // ZERO
end; // case
Result := True;
end; // CheckSpecialCases
function HandleBadNumber: Boolean;
begin
Result := False;
FTokenID := tkUnknown;
// Ignore all tokens till end of "number"
while IsIdentChar(FLine[Run]) or (FLine[Run] = '.') do
Inc (Run);
end; // HandleBadNumber
function HandleExponent: Boolean;
begin
State := nsExpFound;
FTokenID := tkFloat;
// Skip e[+/-]
if CharInSet(FLine[Run+1], ['+', '-']) then
Inc (Run);
// Invalid token : 1.0e
if not CharInSet(FLine[Run+1], ['0'..'9']) then begin
Inc (Run);
Result := HandleBadNumber;
Exit;
end; // if
Result := True;
end; // HandleExponent
function HandleDot: Boolean;
begin
// Check for ellipsis
Result := (FLine[Run+1] <> '.') or (FLine[Run+2] <> '.');
if Result then begin
State := nsDotFound;
FTokenID := tkFloat;
end; // if
end; // HandleDot
function CheckStart: Boolean;
begin
// 1234
if CharInSet(temp, ['0'..'9']) then begin
Result := True;
//123e4
end else if CharInSet(temp, ['e', 'E']) then begin
Result := HandleExponent;
// 123.45j
end else if CharInSet(temp, ['j', 'J']) then begin
Inc (Run);
FTokenID := tkFloat;
Result := False;
// 123.45
end else if temp = '.' then begin
Result := HandleDot;
// Error!
end else if IsIdentChar(temp) then begin
Result := HandleBadNumber;
// End of number
end else begin
Result := False;
end; // if
end; // CheckStart
function CheckDotFound: Boolean;
begin
// 1.0e4
if CharInSet(temp, ['e', 'E']) then begin
Result := HandleExponent;
// 123.45
end else if CharInSet(temp, ['0'..'9']) then begin
Result := True;
// 123.45j
end else if CharInSet(temp, ['j', 'J']) then begin
Inc (Run);
Result := False;
// 123.45.45: Error!
end else if temp = '.' then begin
Result := False;
if HandleDot then
HandleBadNumber;
// Error!
end else if IsIdentChar(temp) then begin
Result := HandleBadNumber;
// End of number
end else begin
Result := False;
end; // if
end; // CheckDotFound
function CheckFloatNeeded: Boolean;
begin
// 091.0e4
if CharInSet(temp, ['e', 'E']) then begin
Result := HandleExponent;
// 0912345
end else if CharInSet(temp, ['0'..'9']) then begin
Result := True;
// 09123.45
end else if temp = '.' then begin
Result := HandleDot or HandleBadNumber; // Bad octal
// 09123.45j
end else if CharInSet(temp, ['j', 'J']) then begin
Inc (Run);
Result := False;
// End of number (error: Bad oct number) 0912345
end else begin
Result := HandleBadNumber;
end;
end; // CheckFloatNeeded
function CheckHex: Boolean;
begin
// 0x123ABC
if CharInSet(temp, ['a'..'f', 'A'..'F', '0'..'9']) then
begin
Result := True;
// 0x123ABCL
end else if CharInSet(temp, ['l', 'L']) then begin
Inc (Run);
Result := False;
// 0x123.45: Error!
end else if temp = '.' then begin
Result := False;
if HandleDot then
HandleBadNumber;
// Error!
end else if IsIdentChar(temp) then begin
Result := HandleBadNumber;
// End of number
end else begin
Result := False;
end; // if
end; // CheckHex
function CheckOct: Boolean;
begin
// 012345
if CharInSet(temp, ['0'..'9']) then begin
if not CharInSet(temp, ['0'..'7']) then begin
State := nsFloatNeeded;
FTokenID := tkFloat;
end; // if
Result := True;
// 012345L
end else if CharInSet(temp, ['l', 'L']) then begin
Inc (Run);
Result := False;
// 0123e4
end else if CharInSet(temp, ['e', 'E']) then begin
Result := HandleExponent;
// 0123j
end else if CharInSet(temp, ['j', 'J']) then begin
Inc (Run);
FTokenID := tkFloat;
Result := False;
// 0123.45
end else if temp = '.' then begin
Result := HandleDot;
// Error!
end else if IsIdentChar(temp) then begin
Result := HandleBadNumber;
// End of number
end else begin
Result := False;
end; // if
end; // CheckOct
function CheckExpFound: Boolean;
begin
// 1e+123
if CharInSet(temp, ['0'..'9']) then begin
Result := True;
// 1e+123j
end else if CharInSet(temp, ['j', 'J']) then begin
Inc (Run);
Result := False;
// 1e4.5: Error!
end else if temp = '.' then begin
Result := False;
if HandleDot then
HandleBadNumber;
// Error!
end else if IsIdentChar(temp) then begin
Result := HandleBadNumber;
// End of number
end else begin
Result := False;
end; // if
end; // CheckExpFound
begin
State := nsStart;
FTokenID := tkNumber;
temp := FLine[Run];
Inc (Run);
// Special cases
if not CheckSpecialCases then
Exit;
// Use a state machine to parse numbers
while True do begin
temp := FLine[Run];
case State of
nsStart:
if not CheckStart then Exit;
nsDotFound:
if not CheckDotFound then Exit;
nsFloatNeeded:
if not CheckFloatNeeded then Exit;
nsHex:
if not CheckHex then Exit;
nsOct:
if not CheckOct then Exit;
nsExpFound:
if not CheckExpFound then Exit;
end; // case
Inc (Run);
end; // while
end;
procedure TSynPythonSyn.SpaceProc;
begin
Inc(Run);
FTokenID := tkSpace;
while (FLine[Run] <= #32) and not IsLineEnd(Run) do Inc(Run);
end;
procedure TSynPythonSyn.String2Proc;
var
BackslashCount: Integer;
begin
FTokenID := tkString;
if (FLine[Run + 1] = '"') and (FLine[Run + 2] = '"') then
begin
FTokenID := tkTrippleQuotedString;
Inc(Run, 3);
FRange := rsMultilineString2;
while FLine[Run] <> #0 do
begin
case FLine[Run] of
'\':
begin
{ If we're looking at a backslash, and the following character is an
end quote, and it's preceeded by an odd number of backslashes, then
it shouldn't mark the end of the string. If it's preceeded by an
even number, then it should. !!!THIS RULE DOESNT APPLY IN RAW STRINGS}
if FLine[Run + 1] = '"' then
begin
BackslashCount := 1;
while ((Run > BackslashCount) and (FLine[Run - BackslashCount] = '\')) do
BackslashCount := BackslashCount + 1;
if (BackslashCount mod 2 = 1) then Inc(Run)
end;
Inc(Run);
end;// '\':
'"':
if (FLine[Run + 1] = '"') and (FLine[Run + 2] = '"') then begin
FRange := rsUnknown;
Inc(Run, 3);
Exit;
end else
Inc(Run);
#10: Exit;
#13: Exit;
else
Inc(Run);
end;
end;
end
else //if short string
repeat
case FLine[Run] of
#0, #10, #13:
begin
if FLine[Run-1] = '\' then
begin
FStringStarter := '"';
FRange := rsMultilineString3;
end;
Break;
end;
{The same backslash stuff above...}
'\':
begin
if FLine[Run + 1] = '"' then
begin
BackslashCount := 1;
while ((Run > BackslashCount) and (FLine[Run - BackslashCount] = '\')) do
BackslashCount := BackslashCount + 1;
if (BackslashCount mod 2 = 1) then Inc(Run)
end;
Inc(Run);
end;// '\':
else Inc(Run);
end; //case
until (FLine[Run] = '"');
if FLine[Run] <> #0 then Inc(Run);
end;
procedure TSynPythonSyn.PreStringProc;
var
temp: WideChar;
begin
// Handle python raw strings
// r""
temp := FLine[Run + 1];
if temp = '''' then
begin
Inc (Run);
StringProc;
end
else if temp = '"' then
begin
Inc (Run);
String2Proc;
end
else
begin
// If not followed by quote char, must be ident
IdentProc;
end; // if
end;
procedure TSynPythonSyn.UnicodeStringProc;
begin
// Handle python raw and unicode strings
// Valid syntax: u"", or ur""
if CharInSet(FLine[Run + 1], ['r', 'R']) and
CharInSet(FLine[Run + 2], ['''', '"']) then
begin
// for ur, Remove the "u" and...
Inc (Run);
end;
// delegate to raw strings
PreStringProc;
end;
procedure TSynPythonSyn.StringProc;
var
FBackslashCount: Integer;
begin
FTokenID := tkString;
if (FLine[Run + 1] = #39) and (FLine[Run + 2] = #39) then begin
FTokenID := tkTrippleQuotedString;
Inc(Run, 3);
FRange:=rsMultilineString;
while FLine[Run] <> #0 do begin
case FLine[Run] of
'\': begin
{ If we're looking at a backslash, and the following character is an
end quote, and it's preceeded by an odd number of backslashes, then
it shouldn't mark the end of the string. If it's preceeded by an
even number, then it should. !!!THIS RULE DOESNT APPLY IN RAW STRINGS}
if FLine[Run + 1] = #39 then
begin
FBackslashCount := 1;
while ((Run > FBackslashCount) and (FLine[Run - FBackslashCount] = '\')) do
FBackslashCount := FBackslashCount + 1;
if (FBackslashCount mod 2 = 1) then Inc(Run)
end;
Inc(Run);
end;// '\':
#39:
if (FLine[Run + 1] = #39) and (FLine[Run + 2] = #39) then begin
FRange := rsUnknown;
Inc(Run, 3);
Exit;
end else
Inc(Run);
#10: Exit;
#13: Exit;
else
Inc(Run);
end;
end;
end
else //if short string
repeat
case FLine[Run] of
#0, #10, #13 : begin
if FLine[Run-1] = '\' then begin
FStringStarter := #39;
FRange := rsMultilineString3;
end;
Break;
end;
{The same backslash stuff above...}
'\':
begin
if FLine[Run + 1] = #39 then
begin
FBackslashCount := 1;
while ((Run > FBackslashCount) and (FLine[Run - FBackslashCount] = '\')) do
FBackslashCount := FBackslashCount + 1;
if (FBackslashCount mod 2 = 1) then Inc(Run)
end;
Inc(Run);
end;// '\':
else Inc(Run);
end; //case
until (FLine[Run] = #39);
if FLine[Run] <> #0 then Inc(Run);
end;
procedure TSynPythonSyn.StringEndProc(EndChar: WideChar);
var
BackslashCount: Integer;
begin
if FRange = rsMultilineString3 then
FTokenID := tkString
else
FTokenID := tkTrippleQuotedString;
case FLine[Run] of
#0:
begin
NullProc;
Exit;
end;
#10:
begin
LFProc;
Exit;
end;
#13:
begin
CRProc;
Exit;
end;
end;
if FRange = rsMultilineString3 then begin
repeat
if FLine[Run]=FStringStarter then begin
Inc(Run);
FRange:=rsUnknown;
Exit;
end else if FLine[Run]='\' then ; {The same backslash stuff above...}
begin
if FLine[Run + 1] = FStringStarter then
begin
BackslashCount := 1;
while ((Run > BackslashCount) and (FLine[Run - BackslashCount] = '\')) do
BackslashCount := BackslashCount + 1;
if (BackslashCount mod 2 = 1) then Inc(Run);
end;
end;// if FLine[Run]...
Inc(Run);
until IsLineEnd(Run);
if FLine[Run-1]<>'\' then begin
FRange:=rsUnknown;
Exit;
end;
end else
repeat
if FLine[Run] = '\' then
begin
if FLine[Run + 1] = EndChar then
begin
BackslashCount := 1;
while ((Run > BackslashCount) and (FLine[Run - BackslashCount] = '\')) do
BackslashCount := BackslashCount + 1;
if (BackslashCount mod 2 = 1) then Inc(Run, 2);
end;
end;// if FLine[Run]...
if (FLine[Run]=EndChar) and (FLine[Run+1]=EndChar) and (FLine[Run+2]=EndChar) then begin
Inc(Run,3);
FRange:=rsUnknown;
Exit;
end;
Inc(Run);
until IsLineEnd(Run);
end;
procedure TSynPythonSyn.UnknownProc;
begin
Inc(Run);
FTokenID := tkUnknown;
end;
procedure TSynPythonSyn.Next;
begin
FTokenPos := Run;
case FRange of
rsMultilineString:
StringEndProc(#39);
rsMultilineString2:
StringEndProc('"');
rsMultilineString3:
StringEndProc(FStringStarter);
else
case FLine[Run] of
'&', '}', '{', ':', ',', ']', '[', '*', '`',
'^', ')', '(', ';', '/', '=', '-', '+', '!', '\',
'%', '|', '~' :
SymbolProc;
#13: CRProc;
'#': CommentProc;
'>': GreaterProc;
'A'..'Q', 'S', 'T', 'V'..'Z', 'a'..'q', 's', 't', 'v'..'z', '_': IdentProc;
#10: LFProc;
'<': LowerProc;
#0: NullProc;
'.', '0'..'9': NumberProc;
#1..#9, #11, #12, #14..#32: SpaceProc;
'r', 'R': PreStringProc;
'u', 'U': UnicodeStringProc;
'''': StringProc;
'"': String2Proc;
else UnknownProc;
end;
end;
inherited;
end;
function TSynPythonSyn.GetDefaultAttribute(Index: Integer): TSynHighlighterAttributes;
begin
case Index of
SYN_ATTR_COMMENT: Result := FCommentAttri;
SYN_ATTR_KEYWORD: Result := FKeyAttri;
SYN_ATTR_WHITESPACE: Result := FSpaceAttri;
SYN_ATTR_SYMBOL: Result := FSymbolAttri;
else
Result := nil;
end;
end;
function TSynPythonSyn.GetEol: Boolean;
begin
Result := Run = FLineLen + 1;
end;
function TSynPythonSyn.GetRange: Pointer;
begin
Result := Pointer(FRange);
end;
function TSynPythonSyn.GetTokenID: TtkTokenKind;
begin
Result := FTokenID;
end;
function TSynPythonSyn.GetTokenAttribute: TSynHighlighterAttributes;
begin
case FTokenID of
tkComment: Result := FCommentAttri;
tkIdentifier: Result := FIdentifierAttri;
tkKey: Result := FKeyAttri;
tkNonKeyword: Result := FNonKeyAttri;
tkSystemDefined: Result := FSystemAttri;
tkNumber: Result := FNumberAttri;
tkHex: Result := FHexAttri;
tkOct: Result := FOctalAttri;
tkFloat: Result := FFloatAttri;
tkSpace: Result := FSpaceAttri;
tkString: Result := FStringAttri;
tkTrippleQuotedString: Result := FDocStringAttri;
tkSymbol: Result := FSymbolAttri;
tkUnknown: Result := FErrorAttri;
else
Result := nil;
end;
end;
function TSynPythonSyn.GetTokenKind: Integer;
begin
Result := Ord(FTokenID);
end;
procedure TSynPythonSyn.ResetRange;
begin
FRange := rsUnknown;
end;
{$IFDEF SYN_CodeFolding}
procedure TSynPythonSyn.InitFoldRanges(FoldRanges: TSynFoldRanges);
begin
inherited;
FoldRanges.CodeFoldingMode := cfmIndentation;
end;
procedure TSynPythonSyn.ScanForFoldRanges(FoldRanges: TSynFoldRanges;
LinesToScan: TStrings; FromLine, ToLine: Integer);
var
CurLine: string;
LeftTrimmedLine : string;
Line: Integer;
Indent : Integer;
TabW : integer;
FoldType : integer;
const
MultiLineStringFoldType = 2;
ClassDefType = 3;
FunctionDefType = 4;
function IsMultiLineString(Line : integer; Range : TRangeState; Fold : Boolean): Boolean;
begin
Result := True;
if TRangeState(GetLineRange(LinesToScan, Line)) = Range then
begin
if (TRangeState(GetLineRange(LinesToScan, Line - 1)) <> Range) and Fold then
FoldRanges.StartFoldRange(Line + 1, MultiLineStringFoldType)
else
FoldRanges.NoFoldInfo(Line + 1);
end
else if (TRangeState(GetLineRange(LinesToScan, Line - 1)) = Range) and Fold then
begin
FoldRanges.StopFoldRange(Line + 1, MultiLineStringFoldType);
end else
Result := False;
end;
function FoldRegion(Line: Integer): Boolean;
begin
Result := False;
if Uppercase(Copy(LeftTrimmedLine, 1, 7)) = '#REGION' then
begin
FoldRanges.StartFoldRange(Line + 1, FoldRegionType);
Result := True;
end
else if Uppercase(Copy(LeftTrimmedLine, 1, 10)) = '#ENDREGION' then
begin
FoldRanges.StopFoldRange(Line + 1, FoldRegionType);
Result := True;
end;
end;
function LeftSpaces: Integer;
var
p: PWideChar;
begin
p := PWideChar(CurLine);
if Assigned(p) then
begin
Result := 0;
while (p^ >= #1) and (p^ <= #32) do
begin
if (p^ = #9) then
Inc(Result, TabW)
else
Inc(Result);
Inc(p);
end;
end
else
Result := 0;
end;
begin
// Deal with multiline strings
for Line := FromLine to ToLine do begin
if IsMultiLineString(Line, rsMultilineString, True) or
IsMultiLineString(Line, rsMultilineString2, True) or
IsMultiLineString(Line, rsMultilineString3, False)
then
Continue;
// Find Fold regions
CurLine := LinesToScan[Line];
LeftTrimmedLine := TrimLeft(CurLine);
// Skip empty lines
if LeftTrimmedLine = '' then begin
FoldRanges.NoFoldInfo(Line + 1);
Continue;
end;
// Find Fold regions
if FoldRegion(Line) then
Continue;
TabW := TabWidth(LinesToScan);
Indent := LeftSpaces;
// find fold openers
if BlockOpenerRE.Exec(LeftTrimmedLine) then
begin
if BlockOpenerRE.Match[1] = 'class' then
FoldType := ClassDefType
else if Pos('def', BlockOpenerRE.Match[1]) >= 1 then
FoldType := FunctionDefType
else
FoldType := 1;
FoldRanges.StartFoldRange(Line + 1, FoldType, Indent);
Continue;
end;
FoldRanges.StopFoldRange(Line + 1, 1, Indent)
end;
end;
{$ENDIF}
procedure TSynPythonSyn.SetRange(Value: Pointer);
begin
FRange := TRangeState(Value);
end;
function TSynPythonSyn.IsFilterStored: Boolean;
begin
Result := FDefaultFilter <> SYNS_FilterPython;
end;
class function TSynPythonSyn.GetLanguageName: string;
begin
Result := SYNS_LangPython;
end;
function TSynPythonSyn.GetSampleSource: UnicodeString;
begin
Result :=
'#!/usr/local/bin/python'#13#10 +
'import string, sys'#13#10 +
'""" If no arguments were given, print a helpful message """'#13#10 +
'if len(sys.argv)==1:'#13#10 +
' print ''Usage: celsius temp1 temp2 ...'''#13#10 +
' sys.exit(0)';
end;
class function TSynPythonSyn.GetFriendlyLanguageName: UnicodeString;
begin
Result := SYNS_FriendlyLangPython;
end;
initialization
{$IFNDEF SYN_CPPB_1}
RegisterPlaceableHighlighter(TSynPythonSyn);
{$ENDIF}
finalization
GlobalKeywords.Free;
end.