From 24fba590fa295885d2e974966e1659d7ebebbd71 Mon Sep 17 00:00:00 2001 From: Basile Burg Date: Tue, 1 Jul 2014 11:48:52 +0200 Subject: [PATCH] r14 --- lazproj/coedit.lpi | 12 +- lazproj/coedit.lpr | 2 +- src/ce_d2syn.pas | 124 +------ src/ce_dlang.pas | 831 ++++++++++++++++++++++++++++++++++++++++++ src/ce_dlangutils.pas | 135 +++++++ src/ce_editor.pas | 43 ++- src/ce_main.lfm | 66 ++-- src/ce_main.pas | 14 +- src/ce_synmemo.pas | 17 +- src/ce_widget.pas | 7 +- 10 files changed, 1088 insertions(+), 163 deletions(-) create mode 100644 src/ce_dlang.pas create mode 100644 src/ce_dlangutils.pas diff --git a/lazproj/coedit.lpi b/lazproj/coedit.lpi index ec0441ed..cc62d073 100644 --- a/lazproj/coedit.lpi +++ b/lazproj/coedit.lpi @@ -128,7 +128,7 @@ - + @@ -228,6 +228,16 @@ + + + + + + + + + + diff --git a/lazproj/coedit.lpr b/lazproj/coedit.lpr index 4098f904..85d751c0 100644 --- a/lazproj/coedit.lpr +++ b/lazproj/coedit.lpr @@ -9,7 +9,7 @@ uses Interfaces, Forms, lazcontrols, runtimetypeinfocontrols, ce_widget, ce_dmdwrap, ce_common, ce_synmemo, ce_main, ce_messages, ce_editor, ce_projinspect, ce_projconf, jsonparser, ce_project, - ce_widgettypes, ce_staticexplorer, ce_search; + ce_widgettypes, ce_staticexplorer, ce_search, ce_dlang, ce_dlangutils; {$R *.res} diff --git a/src/ce_d2syn.pas b/src/ce_d2syn.pas index f1cf0885..ad75fe51 100644 --- a/src/ce_d2syn.pas +++ b/src/ce_d2syn.pas @@ -6,7 +6,8 @@ interface uses Classes, SysUtils, Graphics, - SynEditHighlighter, SynEditHighlighterFoldBase, SynEditTypes; + SynEditHighlighter, SynEditHighlighterFoldBase, SynEditTypes, + ce_dlangutils; const @@ -152,95 +153,6 @@ type implementation -function isWhite(const c: Char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} -begin - result := c in [#0..#32]; -end; - -function isSpace(const c: Char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} -begin - result := c in [#9,' ']; -end; - -function isAlpha(const c: Char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} -begin - result := (c in ['a'..'z']) or (c in ['A'..'Z']); -end; - -function isNumber(const c: Char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} -begin - result := (c in ['0'..'9']); -end; - -function isDigit(const c: Char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} -begin - result := (c in ['0'..'1']); -end; - -function isAlNum(const c: Char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} -begin - result := isAlpha(c) or isNumber(c); -end; - -function isHex(const c: Char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} -begin - result := isNumber(c) or (c in ['A'..'F']) or (c in ['a'..'f']); -end; - -function isSymbol(const c: char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} -begin - result := c in [';', '{', '}', '(', ')', '[', ']', ',', '.', ':' , '"', #39, '?']; -end; - -function isOperator(const c: char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} -begin - result := c in ['/', '*', '-', '+', '%', '>', '<', '=', '!', - '&', '|', '^', '~', '$']; -end; - -function isDoubleOperator(const s: string): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} -begin - result := false; - case s[1] of - '.': result := (s[2] = '.'); - - '>': result := s[2] in ['>', '=']; - '<': result := s[2] in ['<', '=', '>']; - '=': result := s[2] in ['=', '>']; - '!': result := s[2] in ['=', '>', '<']; - - '+': result := s[2] in ['+', '=']; - '-': result := s[2] in ['-', '=']; - '/': result := s[2] in ['=']; - '*': result := s[2] in ['=']; - '%': result := s[2] in ['=']; - '~': result := s[2] in ['=']; - - '&': result := s[2] in ['&', '=']; - '|': result := s[2] in ['|', '=']; - '^': result := s[2] in ['^', '=']; - end; -end; - -function isTripleOperator(const s: string): boolean; {$IFNDEF DEBUG} inline; {$ENDIF} -begin - result := false; - case s[1] of - '.': result := (s[2] = '.') and (s[3] = '.'); - '^': result := (s[2] = '^') and (s[3] = '='); - '>': result := (s[2] = '>') and (s[3] in ['>', '=']); - '<': result := ((s[2] = '<') and (s[3] in ['<', '='])) - or (s[2] = '>') and (s[3] = '='); - '!': result := ((s[2] = '<') and (s[3] in ['>', '='])) - or ((s[2] = '>')and (s[3] = '=')); - end; -end; - -function isQuadOperator(const s: string): boolean; {$IFNDEF DEBUG} inline; {$ENDIF} -begin - result := (s = '>>>=') or (s = '!<>='); -end; - {$IFDEF USE_DICT_LINKEDCHARMAP} constructor TD2Dictionary.create; var @@ -526,6 +438,7 @@ TODO: - string literals: escape bug: std.path/std.regex: "\\" - comments: correct nested comments handling. } +{$BOOLEVAL ON} procedure TSynD2Syn.next; label _postString1; @@ -670,7 +583,7 @@ begin fRange := rkNone; readNext; // check postfix - if readCurr in ['c','w','d'] then + if isStringPostfix(readCurr) then readNext; exit; end; @@ -679,7 +592,7 @@ begin begin if fRange = rkNone then begin - // check hex/WYSIWYG prefix + // check WYSIWYG/hex prefix if readCurr in ['r','x'] then begin if not (readNext = '"') then @@ -695,7 +608,7 @@ begin begin readNext; // check postfix - if readCurr in ['c','w','d'] then + if isStringPostfix(readCurr) then readNext; end; fTokKind := tkStrng; @@ -720,7 +633,7 @@ begin fRange := rkNone; readNext; // check postfix - if readCurr in ['c','w','d'] then + if isStringPostfix(readCurr) then readNext; exit; end; @@ -736,7 +649,7 @@ begin begin readNext; // check postfix - if readCurr in ['c','w','d'] then + if isStringPostfix(readCurr) then readNext; end; fTokKind := tkStrng; @@ -778,30 +691,30 @@ begin end; // symbols 2: operators - if isOperator(readCurr) then + if isOperator1(readCurr) then begin fTokKind := tkSymbl; - while isOperator(readNext) do (*!*); + while isOperator1(readNext) do (*!*); case fTokStop - fTokStart of 1:begin - if not isOperator(readCurr) then exit + if not isOperator1(readCurr) then exit else Dec(fTokStop); end; 2:begin - if (not isOperator(readCurr)) and - isDoubleOperator(fLineBuf[fTokStart..fTokStop-1]) + if (not isOperator1(readCurr)) and + isOperator2(fLineBuf[fTokStart..fTokStop-1]) then exit else Dec(fTokStop, 2); end; 3:begin - if (not isOperator(readCurr)) and - isTripleOperator(fLineBuf[fTokStart..fTokStop-1]) + if (not isOperator1(readCurr)) and + isOperator3(fLineBuf[fTokStart..fTokStop-1]) then exit else Dec(fTokStop, 3); end; 4:begin - if (not isOperator(readCurr)) and - isQuadOperator(fLineBuf[fTokStart..fTokStop-1]) + if (not isOperator1(readCurr)) and + isOperator4(fLineBuf[fTokStart..fTokStop-1]) then exit else Dec(fTokStop, 4); end; @@ -817,7 +730,7 @@ begin begin if isWhite(readNext) then break; if isSymbol(readCurr) then break; - if isOperator(readCurr) then break; + if isOperator1(readCurr) then break; end; if fKeyWords.find(fLineBuf[FTokStart..fTokStop-1]) then fTokKind := tkKeywd @@ -832,6 +745,7 @@ begin // Should not happend assert(false); end; +{$BOOLEVAL OFF} function TSynD2Syn.GetEol: Boolean; begin diff --git a/src/ce_dlang.pas b/src/ce_dlang.pas new file mode 100644 index 00000000..3d403e08 --- /dev/null +++ b/src/ce_dlang.pas @@ -0,0 +1,831 @@ +unit ce_dlang; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, ce_dlangutils; + +const + + D2Kw: array[0..109] of string = + ( 'abstract', 'alias', 'align', 'asm', 'assert', 'auto', + 'body', 'bool', 'break', 'byte', + 'case', 'cast', 'catch', 'cdouble', 'cent', 'cfloat', 'char', 'class', + 'const', 'continue', 'creal', + 'dchar', 'debug', 'default', 'delegate', 'delete', 'deprecated', 'do', 'double', + 'else', 'enum', 'export', 'extern', + 'false', 'final', 'finally', 'float', 'for', 'foreach', + 'foreach_reverse', 'function', + 'goto', + 'idouble', 'if', 'ifloat', 'immutable', 'import', 'in', 'inout', 'int', + 'interface', 'invariant', 'ireal', 'is', + 'lazy', 'long', + 'macro', 'mixin', 'module', + 'new', 'nothrow', 'null', + 'out', 'override', + 'package', 'pragma', 'private', 'protected', 'ptrdiff_t', 'public', 'pure', + 'real', 'ref', 'return', + 'size_t', 'scope', 'shared', 'short', 'static', 'string', 'struct', + 'super', 'switch', 'synchronized', + 'template', 'this', 'throw', 'true', 'try', 'typedef', 'typeid', 'typeof', + 'ubyte', 'ucent', 'uint', 'ulong', 'union', 'unittest', 'ushort', + 'version', 'void', 'volatile', + 'wchar', 'while', 'with', + '__FILE__', '__MODULE__', '__LINE__', '__FUNCTION__', '__PRETTY_FUNCTION__' + ); + +type + + (** + * sector for an array of Keyword with a common hash. + *) + TD2DictionaryEntry = record + filled: Boolean; + values: array of string; + end; + + (** + * Dictionary for the D2 keywords. + *) + TD2Dictionary = object + private + fLongest, fShortest: NativeInt; + fEntries: array[Byte] of TD2DictionaryEntry; + function toHash(const aValue: string): Byte; {$IFNDEF DEBUG}inline;{$ENDIF} + procedure addEntry(const aValue: string); + public + constructor create; + destructor destroy; // do not remove even if empty (compat with char-map version) + function find(const aValue: string): boolean; + end; + + (** + * Represents the pointer in a source file. + * Automatically updates the line and the column. + *) + TReaderHead = object + private + fLineIndex: Integer; + fColumnIndex: Integer; + fAbsoluteIndex: Integer; + fReaderHead: PChar; + function getColAndLine: TPoint; + public + constructor create(const aText: PChar; const aColAndLine: TPoint); + procedure setReader(const aText: PChar; const aColAndLine: TPoint); + // + function next: PChar; + function previous: PChar; + // + property AbsoluteIndex: Integer read fAbsoluteIndex; + property LineIndex: Integer read fLineIndex; + property ColumnIndex: Integer read fColumnIndex; + property LineAnColumn: TPoint read getColAndLine; + // + property head: PChar read fReaderHead; + end; + + TLexTokenKind = (ltkIllegal, ltkChar, ltkComment, ltkIdentifier, ltkKeyword, + ltkNumber, ltkOperator, ltkString, ltkSymbol); + +const + LexTokenKindString : array[TLexTokenKind] of string = + ( 'Illegal', 'Character', 'Comment', 'Identifier', 'Keyword', + 'Number', 'Operator', 'String', 'Symbol'); + +type + + (***************************************************************************** + * Lexer token + *) + PLexToken = ^TLexToken; + TLexToken = record + position: TPoint; + kind: TLexTokenKind; + data: string; + end; + + (***************************************************************************** + * List of lexer tokens + *) + TLexTokenList = class(TList) + private + function getToken(index: integer): TLexToken; + public + procedure clear; override; + procedure addToken(aValue: PLexToken); + property token[index: integer]: TLexToken read getToken; + end; + + TLexTokenEnumerator = class + fList: TLexTokenList; + fIndex: Integer; + function GetCurrent: TLexToken; + function MoveNext: Boolean; + property Current: TLexToken read GetCurrent; + end; + + (***************************************************************************** + * Error record + *) + PLexError = ^TLexError; + TLexError = record + position: TPoint; + msg: string; + end; + + (***************************************************************************** + * Error list + *) + TLexErrorList = class(TList) + private + function getError(index: integer): TLexError; + public + procedure clear; override; + procedure addError(aValue: PLexError); + property error[index: integer]: TLexError read getError; + end; + + TLexErrorEnumerator = class + fList: TLexErrorList; + fIndex: Integer; + function GetCurrent: TLexError; + function MoveNext: Boolean; + property Current: TLexError read GetCurrent; + end; + + operator enumerator(aTokenList: TLexTokenList): TLexTokenEnumerator; + operator enumerator(anErrorList: TLexErrorList): TLexErrorEnumerator; + + (***************************************************************************** + * Lexes aText and fills aList with the TLexToken found. + *) + procedure lex(const aText: string; const aList: TLexTokenList); + (***************************************************************************** + * Detects various syntaxic error in a TLexTokenList + *) + procedure checkSyntaxicErrors(const aTokenList: TLexTokenList; const anErrorList: TLexErrorList); + + + + (***************************************************************************** + * Compares two TPoints. + *) + operator = (lhs: TPoint; rhs: TPoint): boolean; + +implementation + +var + D2Dictionary: TD2Dictionary; + +{$REGION TReaderHead------------------------------------------------------------} +operator = (lhs: TPoint; rhs: TPoint): boolean; +begin + exit( (lhs.y = rhs.y) and (lhs.x = rhs.x) ); +end; + +constructor TReaderHead.create(const aText: PChar; const aColAndLine: TPoint); +begin + setReader(aText,aColAndLine); +end; + +procedure TReaderHead.setReader(const aText: PChar; const aColAndLine: TPoint); +begin + fLineIndex := aColAndLine.y; + fColumnIndex := aColAndLine.x; + fReaderHead := aText; + while (LineAnColumn <> aColAndLine) do next; + // + // editor not 0 based ln index + if fLineIndex = 0 then fLineIndex := 1; +end; + +function TReaderHead.getColAndLine: TPoint; +begin + exit( Point(fColumnIndex, fLineIndex) ); +end; + +function TReaderHead.next: PChar; +begin + Inc(fReaderHead); + Inc(fAbsoluteIndex); + Inc(fColumnIndex); + if (fReaderHead^ = #10) then + begin + Inc(fLineIndex); + fColumnIndex := 0; + end; + exit(fReaderHead); +end; + +function TReaderHead.previous: PChar; +begin + // note: it breaks the column but not the line count + Dec(fReaderHead); + Dec(fColumnIndex); + Dec(fAbsoluteIndex); + exit(fReaderHead); +end; +{$ENDREGION} + +{$REGION TD2Dictionary----------------------------------------------------------} +constructor TD2Dictionary.create; +var + value: string; +begin + for value in D2Kw do + addEntry(value); +end; + +destructor TD2Dictionary.destroy; +begin +end; + +{$IFDEF DEBUG}{$R-}{$ENDIF} +function TD2Dictionary.toHash(const aValue: string): Byte; +var + i: Integer; +begin + result := 0; + for i := 1 to length(aValue) do result += + (Byte(aValue[i]) shl (4 and (1-i))) xor 25; +end; +{$IFDEF DEBUG}{$R+}{$ENDIF} + +procedure TD2Dictionary.addEntry(const aValue: string); +var + hash: Byte; +begin + if find(aValue) then exit; + hash := toHash(aValue); + fEntries[hash].filled := true; + setLength(fEntries[hash].values, length(fEntries[hash].values) + 1); + fEntries[hash].values[high(fEntries[hash].values)] := aValue; + if fLongest <= length(aValue) then + fLongest := length(aValue); + if fShortest >= length(aValue) then + fShortest := length(aValue); +end; + +function TD2Dictionary.find(const aValue: string): boolean; +var + hash: Byte; + i: NativeInt; +begin + result := false; + if length(aValue) > fLongest then exit; + if length(aValue) < fShortest then exit; + hash := toHash(aValue); + if (not fEntries[hash].filled) then exit(false); + for i:= 0 to high(fEntries[hash].values) do + if fEntries[hash].values[i] = aValue then exit(true); +end; +{$ENDREGION} + +{$REGION Lexing-----------------------------------------------------------------} +function TLexTokenList.getToken(index: integer): TLexToken; +begin + result := PLexToken(Items[index])^; +end; + +procedure TLexTokenList.clear; +begin + while Count > 0 do + begin + Dispose( PLexToken(Items[Count-1]) ); + Delete(Count-1); + end; +end; + +procedure TLexTokenList.addToken(aValue: PLexToken); +begin + add(Pointer(aValue)); +end; + +function TLexTokenEnumerator.GetCurrent: TLexToken; +begin + exit(fList.token[fIndex]); +end; + +function TLexTokenEnumerator.MoveNext: Boolean; +begin + Inc(fIndex); + exit(fIndex < fList.Count); +end; + +operator enumerator(aTokenList: TLexTokenList): TLexTokenEnumerator; +begin + result := TLexTokenEnumerator.Create; + result.fList := aTokenList; + result.fIndex := -1; +end; + +{$BOOLEVAL ON} +procedure lex(const aText: string; const aList: TLexTokenList); +var + reader: TReaderHead; + identifier: string; + + function isOutOfBound: boolean; + begin + exit(reader.AbsoluteIndex > length(aText)) + end; + + procedure addToken(aTk: TLexTokenKind); + var + ptk: PLexToken; + begin + ptk := new(PLexToken); + ptk^.kind := aTk; + ptk^.position := reader.LineAnColumn; + ptk^.data := identifier; + aList.Add(ptk); + end; + +begin + + reader.create(@aText[1], Point(0,0)); + while (true) do + begin + + if isOutOfBound then + exit; + + identifier := ''; + + // skip blanks + while isWhite(reader.head^) do + begin + reader.next; + if isOutOfBound then exit; + end; + + // line comment + if (reader.head^ = '/') then + begin + if (reader.next^ = '/') then + begin + if isOutOfBound then exit; + while (reader.head^ <> #10) do + begin + reader.next; + identifier += reader.head^; + if isOutOfBound then exit; + end; + reader.next; + addToken(ltkComment); + continue; + end + else + reader.previous; + end; + + // block comments 1 + if (reader.head^ = '/') then + begin + if (reader.next^ = '*') then + begin + if isOutOfBound then exit; + while (reader.head^ <> '*') or (reader.next^ <> '/') do + if isOutOfBound then exit; + reader.next; + addToken(ltkComment); + continue; + end + else + reader.previous; + end; + + // block comments 2 + if (reader.head^ = '/') then + begin + if (reader.next^ = '+') then + begin + if isOutOfBound then exit; + while (reader.head^ <> '+') or (reader.next^ <> '/') do + if isOutOfBound then exit; + reader.next; + addToken(ltkComment); + continue; + end + else + reader.previous; + end; + + // string 1, note: same escape error as in SynD2Syn + if (reader.head^ in ['r', 'x']) then + begin + if not (reader.next^ = '"') then + reader.previous; + end; + if (reader.head^ = '"') then + begin + reader.next; + if isOutOfBound then exit; + if (reader.head^ = '"') then + begin + reader.next; + addToken(ltkString); + continue; + end; + while (true) do + begin + if reader.head^ = '\' then + begin + reader.next; + if (reader.head^ = '"') then + begin + reader.next; + continue; + end; + end; + if (reader.head^ = '"') then + break; + identifier += reader.head^; + reader.next; + if isOutOfBound then exit; + end; + if isStringPostfix(reader.next^) then + reader.next; + addToken(ltkString); + continue; + end; + + // string 2 + if (reader.head^ = '`') then + begin + reader.next; + if isOutOfBound then exit; + while (reader.head^ <> '`') do + begin + identifier += reader.head^; + reader.next; + if isOutOfBound then exit; + end; + if isStringPostfix(reader.next^) then + reader.next; + if isOutOfBound then exit; + addToken(ltkString); + continue; + end; + + //chars, note: same escape error as in SynD2Syn + if (reader.head^ = #39) then + begin + reader.next; + if isOutOfBound then exit; + if (reader.head^ = #39) then + begin + reader.next; + addToken(ltkString); + continue; + end; + while (true) do + begin + if reader.head^ = '\' then + begin + reader.next; + if (reader.head^ = #39) then + begin + reader.next; + continue; + end; + end; + if (reader.head^ = #39) then + break; + identifier += reader.head^; + reader.next; + if isOutOfBound then exit; + end; + reader.next; + addToken(ltkChar); + continue; + end; + + // check negative float '-0.' + if (reader.head^ = '-') then + begin + identifier += reader.head^; + if reader.next^ = '0' then + begin + if reader.next^ = '.' then + reader.previous // back to 0, get into "binary/hex numbr/float" + else + begin + reader.previous; + reader.previous; // back to - + identifier := ''; + end; + end + else + begin + reader.previous; // back to - + identifier := ''; + end; + end; + + // + suffixes + // + exponent + // float .xxxx + + // binary/hex numbr/float + if (reader.head^ = '0') then + begin + identifier += reader.head^; + if (reader.next^ in ['b','B']) then + begin + identifier += reader.head^; + while isBit(reader.next^) or (reader.head^ = '_') do + begin + if isOutOfBound then exit; + identifier += reader.head^; + end; + addToken(ltkNumber); + continue; + end + else reader.previous; + if (reader.next^ in ['x','X']) then + begin + identifier += reader.head^; + while isHex(reader.next^) or (reader.head^ = '_') do + begin + if isOutOfBound then exit; + identifier += reader.head^; + end; + addToken(ltkNumber); + continue; + end + else reader.previous; + if (reader.next^ = '.') then + begin + identifier += reader.head^; + while isNumber(reader.next^) do + begin + if isOutOfBound then exit; + identifier += reader.head^; + end; + addToken(ltkNumber); + continue; + end + else reader.previous; + identifier := ''; + end; + + // check negative float/int '-xxx' + if (reader.head^ = '-') then + begin + identifier += reader.head^; + if not isNumber(reader.next^) then + begin + reader.previous; // back to '-' + identifier := ''; + end; + end; + + // numbers + if isNumber(reader.head^) then + begin + identifier += reader.head^; + while isNumber(reader.next^) or (reader.head^ = '_') do + begin + if isOutOfBound then exit; + identifier += reader.head^; + end; + addToken(ltkNumber); + continue; + end; + + // symbols + if isSymbol(reader.head^) then + begin + identifier += reader.head^; + reader.next; + addToken(ltkSymbol); + if isOutOfBound then exit; + continue; + end; + + // operators + if isOperator1(reader.head^) then + begin + identifier += reader.head^; + while isOperator1(reader.next^) do + begin + if isOutOfBound then exit; + identifier += reader.head^; + end; + case length(identifier) of + 4:begin + if (not isOperator1(reader.head^)) and + isOperator4(identifier) then + begin + addToken(ltkOperator); + continue; + end; + end; + 3:begin + if (not isOperator1(reader.head^)) and + isOperator3(identifier) then + begin + addToken(ltkOperator); + continue; + end; + end; + 2:begin + if (not isOperator1(reader.head^)) and + isOperator2(identifier) then + begin + addToken(ltkOperator); + continue; + end; + end; + 1:begin + if not isOperator1(reader.head^) + then + begin + addToken(ltkOperator); + continue; + end; + end; + end; + end; + + // identifier accum + if isFirstIdentifier(reader.head^) then + begin + while isIdentifier(reader.head^) do + begin + identifier += reader.head^; + reader.next; + if isOutOfBound then exit; + end; + if D2Dictionary.find(identifier) then + addToken(ltkKeyword) + else + addToken(ltkIdentifier); + continue; + end; + + // error + identifier += ' (unrecognized lexer input)'; + addToken(ltkIllegal); + + end; +end; +{$BOOLEVAL OFF} +{$ENDREGION} + +{$REGION Syntaxic errors} +function TLexErrorList.getError(index: integer): TLexError; +begin + result := PLexError(Items[index])^; +end; + +procedure TLexErrorList.clear; +begin + while Count > 0 do + begin + Dispose( PLexError(Items[Count-1]) ); + Delete(Count-1); + end; +end; + +procedure TLexErrorList.addError(aValue: PLexError); +begin + add(Pointer(aValue)); +end; + +function TLexErrorEnumerator.GetCurrent: TLexError; +begin + exit(fList.error[fIndex]); +end; + +function TLexErrorEnumerator.MoveNext: Boolean; +begin + Inc(fIndex); + exit(fIndex < fList.Count); +end; + +operator enumerator(anErrorList: TLexErrorList): TLexErrorEnumerator; +begin + result := TLexErrorEnumerator.Create; + result.fList := anErrorList; + result.fIndex := -1; +end; + +procedure checkSyntaxicErrors(const aTokenList: TLexTokenList; const anErrorList: TLexErrorList); +const + errPrefix = 'syntaxic error: '; +var + tk, old1, old2: TLexToken; + err: PLexError; + tkIndex: NativeInt; + pareCnt, curlCnt, squaCnt: NativeInt; +procedure addError(const aMsg: string); +begin + err := new(PLexError); + err^.msg := errPrefix + aMsg; + err^.position := aTokenList.token[tkIndex].position; + anErrorList.addError(err); +end; +label + _preSeq; +begin + + tkIndex := -1; + pareCnt := 0; + curlCnt := 0; + squaCnt := 0; + + for tk in aTokenList do + begin + + // token index + Inc(tkIndex); + + // brackets count + if tk.kind = ltkSymbol then + begin + case tk.data of + '(': Inc(pareCnt); + '{': Inc(curlCnt); + '[': Inc(squaCnt); + ')': Dec(pareCnt); + '}': Dec(curlCnt); + ']': Dec(squaCnt); + end; + + // only for the first occurence + if pareCnt = -1 then + addError('a left parenthesis is missing'); + if curlCnt = -1 then + addError('a left curly bracket is missing'); + if squaCnt = -1 then + addError('a left square bracket is missing'); + + // at the end + if (tkIndex = aTokenList.Count-1) then + begin + if pareCnt > 0 then + addError('a right parenthesis is missing'); + if curlCnt > 0 then + addError('a right curly bracket is missing'); + if squaCnt > 0 then + addError('a right square bracket is missing'); + end; + + goto _preSeq; + end; + + // lexer invalid token + if tk.kind = ltkIllegal then + begin + addError(tk.data); + goto _preSeq; + end; + + // invalid numbers + if tk.kind = ltkNumber then + begin + + goto _preSeq; + end; + +_preSeq: + // invalid sequences + if tkIndex > 0 then // can use old1 + begin + if (old1.kind = ltkKeyword) and (tk.kind = ltkKeyword) then + if old1.data = tk.data then + addError('keyword is duplicated'); + + // needs negative numbr to be tokenized correctly: ... = -1; '-' is currently token as an operator. + if (old1.kind = ltkOperator) and (tk.kind = ltkOperator) then + addError('operator rhs cannot be an operator'); + + if (old1.kind = ltkNumber) and (tk.kind = ltkNumber) then + addError('symbol or operator expected after number'); + + end; + if tkIndex > 1 then // can use old2 + begin + + end; + + + old1 := tk; + old2 := old1; + end; + + +end; +{$ENDREGION} + +initialization + D2Dictionary.create; +finalization + D2Dictionary.destroy; +end. + diff --git a/src/ce_dlangutils.pas b/src/ce_dlangutils.pas new file mode 100644 index 00000000..684aa91d --- /dev/null +++ b/src/ce_dlangutils.pas @@ -0,0 +1,135 @@ +unit ce_dlangutils; + +{$mode objfpc}{$H+} + +interface + +uses + SysUtils; + +function isWhite(const c: Char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} +function isSpace(const c: Char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} +function isAlpha(const c: Char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} +function isNumber(const c: Char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} +function isBit(const c: Char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} +function isAlNum(const c: Char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} +function isHex(const c: Char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} +function isSymbol(const c: char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} +function isOperator1(const c: char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} +function isOperator2(const s: string): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} +function isOperator3(const s: string): boolean; {$IFNDEF DEBUG} inline; {$ENDIF} +function isOperator4(const s: string): boolean; {$IFNDEF DEBUG} inline; {$ENDIF} +function isStringPostfix(const c: char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} +function isIdentifier(const c: char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} +function isFirstIdentifier(const c: char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} + +implementation + +{$BOOLEVAL ON} +function isWhite(const c: Char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} +begin + exit(c in [#0..#32]); +end; + +function isSpace(const c: Char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} +begin + exit(c in [#9,' ']); +end; + +function isAlpha(const c: Char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} +begin + exit((c in ['a'..'z']) or (c in ['A'..'Z'])); +end; + +function isNumber(const c: Char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} +begin + exit(c in ['0'..'9']); +end; + +function isBit(const c: Char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} +begin + exit((c in ['0'..'1'])); +end; + +function isAlNum(const c: Char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} +begin + exit(isAlpha(c) or isNumber(c)); +end; + +function isHex(const c: Char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} +begin + exit(isNumber(c) or (c in ['A'..'F']) or (c in ['a'..'f'])); +end; + +function isSymbol(const c: char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} +begin + exit(c in [';', '{', '}', '(', ')', '[', ']', ',', '.', ':' , '"', #39, '?', '$']); +end; + +function isOperator1(const c: char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} +begin + exit(c in ['/', '*', '-', '+', '%', '>', '<', '=', '!', + '&', '|', '^', '~']); +end; + +function isOperator2(const s: string): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} +begin + result := false; + case s[1] of + '.': result := (s[2] = '.'); + + '>': result := s[2] in ['>', '=']; + '<': result := s[2] in ['<', '=', '>']; + '=': result := s[2] in ['=', '>']; + '!': result := s[2] in ['=', '>', '<']; + + '+': result := s[2] in ['+', '=']; + '-': result := s[2] in ['-', '=']; + '/': result := s[2] in ['=']; + '*': result := s[2] in ['=']; + '%': result := s[2] in ['=']; + '~': result := s[2] in ['=']; + + '&': result := s[2] in ['&', '=']; + '|': result := s[2] in ['|', '=']; + '^': result := s[2] in ['^', '=']; + end; +end; + +function isOperator3(const s: string): boolean; {$IFNDEF DEBUG} inline; {$ENDIF} +begin + result := false; + case s[1] of + '.': result := (s[2] = '.') and (s[3] = '.'); + '^': result := (s[2] = '^') and (s[3] = '='); + '>': result := (s[2] = '>') and (s[3] in ['>', '=']); + '<': result := ((s[2] = '<') and (s[3] in ['<', '='])) + or (s[2] = '>') and (s[3] = '='); + '!': result := ((s[2] = '<') and (s[3] in ['>', '='])) + or ((s[2] = '>')and (s[3] = '=')); + end; +end; + +function isOperator4(const s: string): boolean; {$IFNDEF DEBUG} inline; {$ENDIF} +begin + result := (s = '>>>=') or (s = '!<>='); +end; + +function isStringPostfix(const c: char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} +begin + exit(c in ['c', 'w', 'd']); +end; + +function isIdentifier(const c: char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} +begin + exit((not isSymbol(c)) and (not isOperator1(c)) and (not isWhite(c))); +end; + +function isFirstIdentifier(const c: char): boolean; {$IFNDEF DEBUG}inline;{$ENDIF} +begin + exit(isIdentifier(c) and (not isNumber(c))); +end; +{$BOOLEVAL OFF} + +end. + diff --git a/src/ce_editor.pas b/src/ce_editor.pas index 11a605d3..03d91322 100644 --- a/src/ce_editor.pas +++ b/src/ce_editor.pas @@ -8,7 +8,8 @@ uses Classes, SysUtils, FileUtil, ExtendedNotebook, Forms, Controls, lcltype, Graphics, SynEditKeyCmds, ComCtrls, SynEditHighlighter, ExtCtrls, Menus, SynEditHighlighterFoldBase, SynMacroRecorder, SynPluginSyncroEdit, SynEdit, - SynHighlighterLFM, ce_widget, ce_d2syn, ce_synmemo, ce_common, AnchorDocking; + SynHighlighterLFM, ce_widget, ce_d2syn, ce_synmemo, ce_common, AnchorDocking, + ce_dlang; type { TCEEditorWidget } @@ -26,7 +27,6 @@ type fKeyChanged: boolean; fSyncEdit: TSynPluginSyncroEdit; procedure memoKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); - procedure memoKeyPress(Sender: TObject; var Key: Char); procedure memoMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure memoChange(Sender: TObject); procedure memoMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); @@ -138,7 +138,6 @@ begin memo.OnMouseDown := @memoMouseDown; memo.OnChange := @memoChange; memo.OnMouseMove := @memoMouseMove; - memo.OnKeyPress := @memoKeyPress; // pageControl.ActivePage := sheet; //http://bugs.freepascal.org/view.php?id=26320 @@ -158,15 +157,13 @@ end; procedure TCEEditorWidget.memoKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin -end; - -procedure TCEEditorWidget.memoKeyPress(Sender: TObject; var Key: Char); -begin - if (sender is TCESynMemo) then +if (sender is TCESynMemo) then identifierToD2Syn(TCESynMemo(Sender)); + fKeyChanged := true; case Byte(Key) of - VK_UNKNOWN..VK_BACK: exit; - VK_PRIOR..VK_HELP: exit; + VK_UNKNOWN..VK_XBUTTON2: exit; + VK_SHIFT..VK_HELP: fKeyChanged := false; + VK_LWIN..VK_SLEEP: exit; VK_F1..$91: exit; end; fKeyChanged := true; @@ -200,6 +197,9 @@ const modstr: array[boolean] of string = ('...', 'MODIFIED'); var ed: TCESynMemo; + tokLst: TLexTokenList; + errLst: TLexErrorList; + err: TLexError; begin ed := getCurrentEditor; if ed <> nil then @@ -210,8 +210,31 @@ begin end; // if fKeyChanged then if editorIndex <> -1 then + begin mainForm.docChangeNotify(Self, editorIndex); + + mainForm.MessageWidget.List.Clear; + tokLst := TLexTokenList.Create; + errLst := TLexErrorList.Create; + try + lex( ed.Lines.Text, tokLst ); + checkSyntaxicErrors( tokLst, errLst); + + for err in errLst do + mainForm.MessageWidget.addMessage(format( + '%s (@line:%4.d @char:%.4d)',[err.msg, err.position.y, err.position.x])); + + mainForm.MessageWidget.scrollToBack; + + finally + tokLst.Free; + errLst.Free; + end; + + + end; fKeyChanged := false; + end; end. diff --git a/src/ce_main.lfm b/src/ce_main.lfm index 71471e14..4d461d75 100644 --- a/src/ce_main.lfm +++ b/src/ce_main.lfm @@ -2547,39 +2547,39 @@ object CEMainForm: TCEMainForm 0000000000330000003300000033000000332D73BAAF1B3D60523F93D4FF3F93 D4FF102438413578BAC300000024000000000000000000000000000000000000 0000000000000000000000000000000000000000001F00000008000000330000 - 0033000000040000002400000000B3B3B1EFB0B0ADFFAEAEACFFAEAEABFFAEAE - ABFFAEAEABFFADAEABFFAEAEABFFAFAFADFFB0B0AEEAB3B3B100B5B5B300B5B5 - B300B5B5B300B5B5B300B5B5B300AFAFADFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9E9E9FFAEAEACA6B3B3B100B5B5 - B300B5B5B300B5B5B300B5B5B300AEAEACFFFFFFFFFFE0DFDEFFE1DFDEFFE1E0 - DFFFE1DFDEFFE0DFDEFFFFFFFFFFA5A5A3FFFFFFFFFFE8E8E8FFAFAFACA7B3B3 - B100B5B5B300B5B5B300B6B6B400AEAEABFFFFFFFFFFE3E3E2FFE4E4E3FFE4E5 - E4FFE4E4E3FFE3E3E2FFFFFFFFFFA7A7A5FFECECEBFFFFFFFFFFEAEAEAFFB1B1 - AFACB6B6B400B8B8B600B9B9B700ADAEABFFFFFFFFFFE7E5E4FFE8E7E6FFE8E7 - E6FFE8E7E6FFE7E6E5FFFFFFFFFFCDCDCCFFAAAAA8FFADADABFFFFFFFFFFB3B3 - B1FFB8B9B7008A8886008C898700ADADABFFFFFFFFFFE9E9E8FFEAEAE9FFEAEA - E9FFEAEAE9FFECECEBFFFAFAF9FFFFFFFFFFFFFFFFFF7B7976FF7E7B79FFB8B8 - B6FF979593008D8B89008F8D8B00ADADABFFFFFFFFFFECEBEAFFEDECEBFFEDEC - EBFFEEEDECFFF3F2F1FF7B7977FF8C8987FFCBCAC8FFB2AFADFFB5B2B0FFA3A3 - A1FF979593FF8D8B89FF908E8B00ADADABFFFFFFFFFFEFEFEEFFEFEFEEFFEFEF - EEFFF1F1F0FFF7F8F7FF8B8987FFD8D5D5FF9B9996FFD0CECDFFD1CFCEFF9D9B - 99FFDCDAD9FF918F8DFF94929000ADADABFFFFFFFFFFF2F1F0FFF2F1F0FFF3F1 - F0FFF6F5F4FFFFFEFDFFB6B3B3FF9B9996FFBCBAB9FF92908FFF92908FFFBCBA - B9FF9E9C9AFF6C6B699B908E8C00ADADABFFFFFFFFFFF4F4F3FFF4F4F3FFF5F6 - F4FFFCFCFBFF73706EFFABA9A8FFCCCAC9FF918F8DFFE1E1E1FFECECEBFF918F - 8CFFCDCBCBFFB1AFAEFF8F8D8BFFADADABFFFFFFFFFFF6F6F6FFF6F6F6FFF7F8 - F8FFFEFFFFFF726F6DFFAAA8A7FFCAC8C7FF8E8C8BFFFFFFFFFFFFFFFFFF8E8C - 8BFFCBC9C8FFB0AEADFF8E8C8AFFADADABFFFFFFFFFFFAF8F8FFF9F8F8FFFAF9 - F9FFFEFDFDFFE1E1E0FFC2C1C0FF959392FFB5B3B2FF8D8B89FF8D8B88FFB5B3 - B2FF999796FF646260894847465BAEAEABFFFFFFFFFFFDFDFDFFFCFCFDFFFCFD - FDFFFFFFFFFFFFFFFFFF84817FFFCCCAC9FF949291FFC5C3C2FFC6C4C3FF9694 - 93FFD0CECDFF8B8988FF0000000AB0B0ADFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - FFFFFFFFFFFFFFFFFFFF787573FF817E7CFFDDDCDDFFA5A3A2FFA7A5A4FFA4A3 - A1FF878583FF868482FF88868400A4A4A2C0AFAFADFFAEAEABFFADADABFFADAD - ABFFAEAEACFFB1B1AEFFB6B6B4FFB7B8B6FFB9BAB7FF797776FF7A7877FFADAE - ACB9000000330000003388868400000000000000003300000033000000330000 - 0033000000330000003300000033000000330000003300000033000000330000 - 0022000000000000000000000000FFFFFF000000000000000000000000000000 + 0033000000040000002400000000BB871F00BB871F00BB871E00B9841A00B67E + 0FEAC4973BFFC79D49FFC39538FFB37904FFB47A07FFB47A07FFB47A08FFB57C + 0AFFB67F0FFFB88114FFBA851B23BB871F00BB871F00BA861D00B7801283E4CF + A7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFE9DABAFFEEE2C9FFB67F14BFBB871F00BB871F00B9851B00B27A09E5FFFF + FFFFFFFFFFFFFEFEFBFFFEFEFBFFFEFEFCFFFEFEFCFFFEFEFCFFFFFFFEFFFFFF + FFFFC69941FFECDFC2FFC19234FFBB871F00BB871E00B9831800C89E49FFFFFF + FFFFFCF9F3FFFBF8F1FFFBF8F1FFFBF8F1FFFBF8F1FFFCF9F3FFFDFBF6FFFFFF + FFFFB27701FFBA851BFFBC8921FFBB871F00BB861E00B882150CCCA556FFFFFF + FFFFF9F6EBFFF9F5EAFFFAF6ECFFFBF7EDFFFDF9EEFFFFFCF1FFFFFFFCFFFFFF + FFFFAB7405CE0000003300000033BB871F00BB861D00B07B1134DBBF88FFFFFF + FFFFF7F2E5FFFAF4E6FFFEF7E9FFFFF9EAFFFFFCEDFF767574FF777778FFF8E9 + CAFFAA7200A8888D99008B8E9700BB871F00BA861D00AD790E73EADBBBFFFAF7 + EEFFF6EFDEFFFCF4E2FF7B797AFF8B8A89FFD2CCBFFFB1AFAEFFB1B1B1FFC3B2 + 8EFF939499FF8C8B8EFF8E8E8E00BB871F00BA851C00AA760C9BF0E4CCFFF6EF + E0FFF4ECD7FFFAF2DCFF8B8A8AFFD7D6D6FF9A9997FFD0CECEFFD0CFCFFF9C9A + 9AFFDBDADBFF918F8EFF94929000BB871F00BA851C00B0790BD2FFFFFFFFF1E9 + D2FFF4EBD2FFFCF2D7FFB4AFA2FF9B9997FFBCBABAFF939191FF929191FFBCBA + BAFF9E9C9AFF6C6B699B908E8C00BB871E00B9841900B8831BEFFFFFFFFFF2E9 + D3FFF7EDD4FF747577FFABA9AAFFCCCACAFF92908FFFDCD2B9FFE9E7E2FF918F + 8EFFCECCCBFFB1AFAEFF8F8D8BFFB98419FFB67E0EFFB67F0FFFC0902EFFC08E + 29FFC59128FF747578FFAAA8A9FFCAC8C7FF8F8D8EFFFFF2D2FFFFFFF5FF8F8D + 8DFFCBC9C9FFB0AEADFF8E8C8AFFB78012FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFE3E3DFFFC2C1BBFF959493FFB5B3B4FF8D8C8CFF8D8C8BFFB6B4 + B3FF999796FF646260894847465BAC7A14CADCC189FFF5F0E0FFF4EDDBFFF4ED + DBFFF6EFDBFFFDF4DCFF878789FFCECDCEFF969698FFC6C5C6FFC7C5C5FF9896 + 95FFD1CFCEFF8B8988FF0000000A60440E44B88114FFB67E0FFFB57D0DFFB57D + 0CFFB67E0CFFBC7F06FF7F8188FF86878AFFA87F2DFFA8A8ABFFAAA8A8FF5250 + 4F698A8886FF868482FF88868400000000070000003300000033000000330000 + 003300000033000000330000003300000033000000337C7C81FF7E7D7DFF0000 + 000E000000330000003388868400000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000033000000330000 + 0000000000000000000000000000FFFFFF000000000000000000000000000000 0000000000000000000000000000A6A4A133A5A39FE5AEACA9FFB9B6B5FFABA9 A5FFA7A5A2D2A8A6A323FFFFFF00FFFFFF000000000000000000000000000000 00000000000000000000A19F9C48AEACAAFFDADAD9FFF0EEEFFFECEBEAFFE2E0 diff --git a/src/ce_main.pas b/src/ce_main.pas index 38a706a5..9c4e1a9e 100644 --- a/src/ce_main.pas +++ b/src/ce_main.pas @@ -16,7 +16,7 @@ type TCEMainForm = class; (** - * Encapsulates the options. + * Encapsulates the options in a writable component. * note: likely to change however needed to test correctly Coedit. *) TCEOptions = class(TComponent) @@ -45,7 +45,7 @@ type procedure saveToFile(const aFilename: string); procedure loadFromFile(const aFilename: string); procedure beforeSave; - procedure afterSave; + procedure afterLoad; procedure DefineProperties(Filer: TFiler); override; end; @@ -375,8 +375,8 @@ begin actEdIndent.Enabled := true; actEdUnIndent.Enabled := true; // - actFileCompAndRun.Enabled := true; - actFileCompAndRunWithArgs.Enabled := true; + actFileCompAndRun.Enabled := curr.isDSource; + actFileCompAndRunWithArgs.Enabled := curr.isDSource; actFileSave.Enabled := true; actFileSaveAs.Enabled := true; actFileClose.Enabled := true; @@ -694,7 +694,7 @@ var begin if fEditWidg = nil then exit; if fEditWidg.editorIndex < 0 then exit; - if fEditWidg.editor[fEditWidg.editorIndex].Highlighter = LfmSyn + if fEditWidg.editor[fEditWidg.editorIndex].isProjectSource then exit; // str := fEditWidg.editor[fEditWidg.editorIndex].fileName; @@ -1327,10 +1327,10 @@ begin except exit; end; - afterSave; + afterLoad; end; -procedure TCEOptions.afterSave; +procedure TCEOptions.afterLoad; var widg: TCEWidget; begin diff --git a/src/ce_synmemo.pas b/src/ce_synmemo.pas index 75bbf3c6..26e617f9 100644 --- a/src/ce_synmemo.pas +++ b/src/ce_synmemo.pas @@ -15,12 +15,17 @@ type fFilename: string; fModified: boolean; fAssocProject: TCEProject; + function getIfDSource: Boolean; + function getIfConfig: Boolean; public constructor Create(aOwner: TComponent); override; // property fileName: string read fFilename write fFilename; property modified: boolean read fModified write fModified; property project: TCEProject read fAssocProject write fAssocProject; + // + property isDSource: boolean read getIfDSource; + property isProjectSource: boolean read getIfConfig; end; var @@ -29,7 +34,7 @@ var implementation uses - graphics; + graphics, ce_main; constructor TCESynMemo.Create(aOwner: TComponent); begin @@ -52,6 +57,16 @@ begin Highlighter := D2Syn; end; +function TCESynMemo.getIfDSource: Boolean; +begin + exit(Highlighter = D2Syn); +end; + +function TCESynMemo.getIfConfig: Boolean; +begin + exit(Highlighter = mainForm.LfmSyn); +end; + initialization D2Syn := TSynD2Syn.create(nil); finalization diff --git a/src/ce_widget.pas b/src/ce_widget.pas index 801704f2..e1c1158b 100644 --- a/src/ce_widget.pas +++ b/src/ce_widget.pas @@ -100,9 +100,6 @@ type implementation {$R *.lfm} -uses - ce_main; - (******************************************************************************* * TCEWidget *) @@ -119,8 +116,8 @@ begin fUpdaterAuto.OnTimer := @updaterAutoProc; fUpdaterDelay := TTimer.Create(self); - updaterByLoopInterval := 50; - updaterByDelayDuration := 1000; + updaterByLoopInterval := 70; + updaterByDelayDuration := 1250; DockMaster.MakeDockable(Self, true, true, true); DockMaster.GetAnchorSite(Self).Header.HeaderPosition := adlhpTop;