瀏覽代碼

Merge branch 'compilerdirective-highlighting'

Martijn Laan 7 年之前
父節點
當前提交
af7802fa9d
共有 5 個文件被更改,包括 274 次插入43 次删除
  1. 15 0
      Components/ScintEdit.pas
  2. 243 30
      Components/ScintStylerInnoSetup.pas
  3. 14 0
      Projects/CompForm.pas
  4. 0 12
      Projects/CompWizard.pas
  5. 2 1
      whatsnew.htm

+ 15 - 0
Components/ScintEdit.pas

@@ -357,6 +357,7 @@ type
     procedure CommitStyle(const Style: TScintStyleNumber);
     function ConsumeAllRemaining: Boolean;
     function ConsumeChar(const C: AnsiChar): Boolean;
+    function ConsumeCharIn(const Chars: TScintRawCharSet): Boolean;
     function ConsumeChars(const Chars: TScintRawCharSet): Boolean;
     function ConsumeCharsNot(const Chars: TScintRawCharSet): Boolean;
     function ConsumeString(const Chars: TScintRawCharSet): TScintRawString;
@@ -367,6 +368,7 @@ type
     function LineTextSpans(const S: TScintRawString): Boolean; virtual;
     function NextCharIs(const C: AnsiChar): Boolean;
     function PreviousCharIn(const Chars: TScintRawCharSet): Boolean;
+    procedure ResetCurIndexTo(Index: Integer);
     procedure ReplaceText(StartIndex, EndIndex: Integer; const C: AnsiChar);
     procedure StyleNeeded; virtual; abstract;
     property CaretIndex: Integer read FCaretIndex;
@@ -2002,6 +2004,13 @@ begin
     Inc(FCurIndex);
 end;
 
+function TScintCustomStyler.ConsumeCharIn(const Chars: TScintRawCharSet): Boolean;
+begin
+  Result := (FCurIndex <= FTextLen) and (FText[FCurIndex] in Chars);
+  if Result then
+    Inc(FCurIndex);
+end;
+
 function TScintCustomStyler.ConsumeChars(const Chars: TScintRawCharSet): Boolean;
 begin
   Result := False;
@@ -2086,4 +2095,10 @@ begin
     P[I-1] := C;
 end;
 
+procedure TScintCustomStyler.ResetCurIndexTo(Index: Integer);
+begin
+  FCurIndex := Index;
+  FStyleStartIndex := Index;
+end;
+
 end.

+ 243 - 30
Components/ScintStylerInnoSetup.pas

@@ -46,11 +46,15 @@ type
 
   TInnoSetupStylerStyle = (stDefault, stCompilerDirective,
     stComment, stSection, stSymbol, stKeyword, stParameterValue,
-    stEventFunction, stConstant, stMessageArg, stPascalString, stPascalNumber);
+    stEventFunction, stConstant, stMessageArg,
+    stPascalReservedWord, stPascalString, stPascalNumber,
+    stISPPReservedWord, stISPPString, stISPPNumber);
 
   TInnoSetupStyler = class(TScintCustomStyler)
   private
     FKeywordList: array[TInnoSetupStylerSection] of AnsiString;
+    FIsppInstalled: Boolean;
+    procedure ApplyPendingSquigglyFromToIndex(const StartIndex, EndIndex: Integer);
     procedure ApplyPendingSquigglyFromIndex(const StartIndex: Integer);
     procedure ApplySquigglyFromIndex(const StartIndex: Integer);
     procedure BuildKeywordListFromEnumType(const Section: TInnoSetupStylerSection;
@@ -64,6 +68,8 @@ type
     procedure HandleCodeSection(var SpanState: TInnoSetupStylerSpanState);
     procedure HandleKeyValueSection(const Section: TInnoSetupStylerSection);
     procedure HandleParameterSection(const ValidParameters: array of TInnoSetupStylerParamInfo);
+    procedure HandleCompilerDirective(const InlineDirective: Boolean;
+      const InlineDirectiveEndIndex: Integer);
     procedure PreStyleInlineISPPDirectives;
     procedure SkipWhitespace;
     procedure SquigglifyUntilChars(const Chars: TScintRawCharSet;
@@ -82,6 +88,7 @@ type
     class function IsParamSection(const Section: TInnoSetupStylerSection): Boolean;
     class function IsSymbolStyle(const Style: TScintStyleNumber): Boolean;
     property KeywordList[Section: TInnoSetupStylerSection]: AnsiString read GetKeywordList;
+    property IsppInstalled: Boolean read FIsppInstalled write FIsppInstalled;
   end;
 
 implementation
@@ -418,8 +425,6 @@ const
     (Name: 'OnlyBelowVersion'));
 
 const
-  stPascalReservedWord = stKeyword;
-
   inSquiggly = 0;
   inPendingSquiggly = 1;
 
@@ -435,6 +440,9 @@ const
   PascalIdentFirstChars = AlphaUnderscoreChars;
   PascalIdentChars = AlphaDigitUnderscoreChars;
 
+  ISPPIdentFirstChars = AlphaUnderscoreChars;
+  ISPPIdentChars = AlphaDigitUnderscoreChars;
+
 function SameRawText(const S1, S2: TScintRawString): Boolean;
 var
   Len, I: Integer;
@@ -545,12 +553,17 @@ begin
   BuildKeywordListFromParameters(scUninstallRun, RunSectionParameters);
 end;
 
-procedure TInnoSetupStyler.ApplyPendingSquigglyFromIndex(const StartIndex: Integer);
+procedure TInnoSetupStyler.ApplyPendingSquigglyFromToIndex(const StartIndex, EndIndex: Integer);
 begin
-  if (CaretIndex >= StartIndex) and (CaretIndex <= CurIndex) then
-    ApplyIndicators([inPendingSquiggly], StartIndex, CurIndex - 1)
+  if (CaretIndex >= StartIndex) and (CaretIndex <= EndIndex) then
+    ApplyIndicators([inPendingSquiggly], StartIndex, EndIndex)
   else
-    ApplyIndicators([inSquiggly], StartIndex, CurIndex - 1);
+    ApplyIndicators([inSquiggly], StartIndex, EndIndex);
+end;
+
+procedure TInnoSetupStyler.ApplyPendingSquigglyFromIndex(const StartIndex: Integer);
+begin
+  ApplyPendingSquigglyFromToIndex(StartIndex, CurIndex - 1);
 end;
 
 procedure TInnoSetupStyler.ApplySquigglyFromIndex(const StartIndex: Integer);
@@ -661,12 +674,12 @@ begin
       stComment: Attributes.ForeColor := clGreen;
       stSection: Attributes.FontStyle := [fsBold];
       stSymbol: Attributes.ForeColor := $707070;
-      stKeyword: Attributes.ForeColor := clBlue;
+      stKeyword, stPascalReservedWord, stISPPReservedWord: Attributes.ForeColor := clBlue;
       //stParameterValue: Attributes.ForeColor := clTeal;
       stEventFunction: Attributes.FontStyle := [fsBold];
       stConstant: Attributes.ForeColor := $C00080;
       stMessageArg: Attributes.ForeColor := $FF8000;
-      stPascalString, stPascalNumber: Attributes.ForeColor := clMaroon;
+      stPascalString, stPascalNumber, stISPPString, stISPPNumber: Attributes.ForeColor := clMaroon;
     end;
   end
   else begin
@@ -790,12 +803,12 @@ begin
         '''':
           begin
             while True do begin
-              ConsumeCharsNot(['''']);
-              if not ConsumeChar('''') then begin
+              ConsumeCharsNot([C]);
+              if not ConsumeChar(C) then begin
                 CommitStyleSqPending(stPascalString);
                 Break;
               end;
-              if not ConsumeChar('''') then begin
+              if not ConsumeChar(C) then begin
                 CommitStyle(stPascalString);
                 Break;
               end;
@@ -810,24 +823,219 @@ begin
           end;
         '$':
           begin
-            ConsumeChars(HexDigitChars);
+            if not ConsumeChars(HexDigitChars) then
+              CommitStyleSqPending(stPascalNumber);
             CommitStyle(stPascalNumber);
           end;
         '#':
           begin
-            if ConsumeChar('$') then
-              ConsumeChars(HexDigitChars)
-            else
-              ConsumeChars(DigitChars);
+            if ConsumeChar('$') then begin
+              if not ConsumeChars(HexDigitChars) then
+                 CommitStyleSqPending(stPascalString);
+            end
+            else if not ConsumeChars(DigitChars) then
+              CommitStyleSqPending(stPascalString);
             CommitStyle(stPascalString);
           end;
       else
         { Illegal character }
-        CommitStyleSq(stSymbol, True); 
+        CommitStyleSq(stSymbol, True);
+      end;
+    end;
+    SkipWhitespace;
+  end;
+end;
+
+procedure TInnoSetupStyler.HandleCompilerDirective(const InlineDirective: Boolean; const InlineDirectiveEndIndex: Integer);
+
+  function EndOfDirective: Boolean;
+  begin
+    Result := EndOfLine or (InlineDirective and (CurIndex > InlineDirectiveEndIndex));
+  end;
+
+  procedure FinishDirectiveNameOrShorthand(const RequiresParameter: Boolean);
+  begin
+    if RequiresParameter then begin
+      ConsumeChars(WhitespaceChars); { This will give the whitespace the stCompilerDirective style instead of stDefault but that's ok }
+      CommitStyleSq(stCompilerDirective, EndOfDirective);
+    end else
+      CommitStyle(stCompilerDirective);
+  end;
+
+  function FinishConsumingStarComment: Boolean;
+  begin
+    Result := False;
+    while True do begin
+      ConsumeCharsNot(['*']);
+      if not ConsumeChar('*') then
+        Break;
+      if ConsumeChar('/') then begin
+        Result := True;
+        Break;
+      end;
+    end;
+    if Result then
+      CommitStyle(stComment)
+    else
+      CommitStyleSqPending(stComment);
+  end;
+
+const
+  ISPPReservedWords: array[0..16] of TScintRawString = (
+    'private', 'protected', 'public', 'any', 'int',
+    'str', 'func', 'option', 'parseroption', 'inlinestart',
+    'inlineend', 'message', 'warning', 'error',
+    'verboselevel', 'include', 'spansymbol');
+type
+  TISPPDirective = record
+    Name: TScintRawString;
+    RequiresParameter: Boolean;
+  end;
+const
+  ISPPDirectives: array[0..22] of TISPPDirective = (
+    (Name: 'define'; RequiresParameter: True),
+    (Name: 'dim'; RequiresParameter: True),
+    (Name: 'redim'; RequiresParameter: True),
+    (Name: 'undef'; RequiresParameter: True),
+    (Name: 'include'; RequiresParameter: True),
+    (Name: 'file'; RequiresParameter: True),
+    (Name: 'emit'; RequiresParameter: True),
+    (Name: 'expr'; RequiresParameter: True),
+    (Name: 'insert'; RequiresParameter: True),
+    (Name: 'append'; RequiresParameter: False),
+    (Name: 'if'; RequiresParameter: True),
+    (Name: 'elif'; RequiresParameter: False { bug in ISPP? }),
+    (Name: 'else'; RequiresParameter: False),
+    (Name: 'endif'; RequiresParameter: False),
+    (Name: 'ifdef'; RequiresParameter: True),
+    (Name: 'ifndef'; RequiresParameter: True),
+    (Name: 'ifexist'; RequiresParameter: True),
+    (Name: 'ifnexist'; RequiresParameter: True),
+    (Name: 'for'; RequiresParameter: True),
+    (Name: 'sub'; RequiresParameter: True),
+    (Name: 'endsub'; RequiresParameter: False),
+    (Name: 'pragma'; RequiresParameter: False),
+    (Name: 'error'; RequiresParameter: False));
+  ISPPDirectiveShorthands: TScintRawCharSet =
+    [':' {define},
+     'x' {undef},
+     '+' {include},
+     '=' {emit},
+     '!' {expr}];
+var
+  S: TScintRawString;
+  StartIndex, I: Integer;
+  C: AnsiChar;
+  NeedIspp: Boolean;
+begin
+  StartIndex := CurIndex;
+  if InlineDirective then begin
+    ConsumeChar('{');
+    NeedIspp := True;
+  end else
+    NeedIspp := False; { Might be updated later to True later }
+  ConsumeChar('#');
+  CommitStyle(stCompilerDirective);
+
+  { Directive name or shorthand }
+  SkipWhiteSpace;
+  if ConsumeCharIn(ISPPDirectiveShorthands) then begin
+    NeedIspp := True;
+    FinishDirectiveNameOrShorthand(True); { All shorthands require a parameter }
+  end
+  else begin
+    S := ConsumeString(ISPPIdentChars);
+    for I := Low(ISPPDirectives) to High(ISPPDirectives) do
+      if SameRawText(S, ISPPDirectives[I].Name) then begin
+        NeedIspp := not SameRawText(S, 'include'); { Built-in preprocessor only supports '#include' }
+        FinishDirectiveNameOrShorthand(ISPPDirectives[I].RequiresParameter);
+        Break;
+      end;
+    if InlineDirective then
+      CommitStyle(stDefault) { #emit shorthand was used (='#' directly followed by an expression): not an error }
+    else
+      CommitStyleSqPending(stCompilerDirective);
+  end;
+
+  { Rest of the directive }
+  SkipWhitespace;
+  if not NeedIspp then
+    NeedIspp := CurChar <> '"'; { Built-in preprocessor requires a '"' quoted string after the '#include' and doesn't support anything else }
+  while not EndOfDirective do begin
+    if CurChar in ISPPIdentFirstChars then begin
+      S := ConsumeString(ISPPIdentChars);
+      for I := Low(ISPPReservedWords) to High(ISPPReservedWords) do
+        if SameRawText(S, ISPPReservedWords[I]) then begin
+          CommitStyle(stISPPReservedWord);
+          Break;
+        end;
+      CommitStyle(stDefault)
+    end
+    else if ConsumeChars(DigitChars) then begin
+      if not CurCharIs('.') or not NextCharIs('.') then begin
+        if ConsumeChar('.') then
+          ConsumeChars(DigitChars);
+        C := CurChar;
+        if C in ['X', 'x'] then begin
+          ConsumeChar(C);
+          if not ConsumeChars(HexDigitChars) then
+            CommitStyleSqPending(stISPPNumber);
+        end;
+        ConsumeChars(['L', 'U', 'l', 'u']);
+      end;
+      CommitStyle(stISPPNumber);
+    end
+    else begin
+      C := CurChar;
+      ConsumeChar(C);
+      case C of
+        '!', '&', '=', '|', '^', '>', '<', '+', '-', '/', '%', '*',
+        '?', ':', ',', '.', '~', '(', '[', '{', ')', ']', '}', '@',
+        '#':
+          begin
+            if (C = '/') and ConsumeChar('*') then
+              FinishConsumingStarComment
+            else if InlineDirective and (C = '}') then
+              CommitStyle(stCompilerDirective) (* Closing '}' of the ISPP inline directive *)
+            else
+              CommitStyle(stSymbol);
+          end;
+        ';':
+          begin
+            if not InlineDirective then
+              ConsumeAllRemaining
+            else
+              ConsumeCharsNot(['}']);
+            CommitStyle(stComment);
+          end;
+        '''', '"':
+          begin
+            while True do begin
+              ConsumeCharsNot([C]);
+              if not ConsumeChar(C) then begin
+                CommitStyleSqPending(stISPPString);
+                Break;
+              end;
+              if not ConsumeChar(C) then begin
+                CommitStyle(stISPPString);
+                Break;
+              end;
+            end;
+          end;
+      else
+        { Illegal character }
+        CommitStyleSq(stSymbol, True);
       end;
     end;
     SkipWhitespace;
   end;
+
+  if NeedIspp and not IsppInstalled then begin
+    if InlineDirective then
+      ApplyPendingSquigglyFromToIndex(StartIndex + 1, InlineDirectiveEndIndex - 1)
+    else
+      ApplyPendingSquigglyFromIndex(StartIndex + 1);
+  end;
 end;
 
 procedure TInnoSetupStyler.HandleParameterSection(
@@ -1035,6 +1243,8 @@ begin
        not(Text[I-2] in LineEndChars) then begin
       ReplaceText(I, I, ' ');
       ApplyStyle(Ord(stSymbol), I, I);
+      if not IsppInstalled then
+        ApplyIndicators([inSquiggly], I, I);
     end;
   end;
 
@@ -1052,16 +1262,16 @@ begin
             Break;
           end;
         end;
+        ResetCurIndexTo(StartIndex);
+        try
+          HandleCompilerDirective(True, I - 1);
+        finally
+          ResetCurIndexTo(0);
+        end;
+        if not Valid then
+          ApplyPendingSquigglyFromToIndex(StartIndex, I - 1);
         { Replace the directive with spaces to prevent any further processing }
         ReplaceText(StartIndex, I - 1, ' ');
-        if Valid then
-          ApplyStyle(Ord(stCompilerDirective), StartIndex, I - 1)
-        else begin
-          if (CaretIndex >= StartIndex) and (CaretIndex <= I) then
-            ApplyIndicators([inPendingSquiggly], StartIndex, I - 1)
-          else
-            ApplyIndicators([inSquiggly], StartIndex, I - 1);
-        end;
       end
       else
         Inc(I);
@@ -1081,7 +1291,7 @@ var
   IsWhitespace: Boolean;
 begin
   { Consume and squigglify all non-whitespace characters until one of Chars
-    is encountered } 
+    is encountered }
   while not EndOfLine and not CurCharIn(Chars) do begin
     IsWhitespace := CurCharIn(WhitespaceChars);
     ConsumeChar(CurChar);
@@ -1136,6 +1346,10 @@ begin
     ConsumeAllRemaining;
     CommitStyle(stComment);
   end
+  else if CurCharIs('/') and NextCharIs('/') then begin
+    ConsumeAllRemaining;
+    CommitStyleSq(stComment, not IsppInstalled and (Section <> scCode))
+  end
   else if ConsumeChar('[') then begin
     SectionEnd := ConsumeChar('/');
     S := ConsumeString(AlphaUnderscoreChars);
@@ -1153,9 +1367,8 @@ begin
     Section := scNone;
     SquigglifyUntilChars([], stDefault);
   end
-  else if ConsumeChar('#') then begin
-    ConsumeAllRemaining;
-    CommitStyle(stCompilerDirective);
+  else if CurCharIs('#') then begin
+    HandleCompilerDirective(False, -1);
   end
   else begin
     case Section of

+ 14 - 0
Projects/CompForm.pas

@@ -422,6 +422,8 @@ var
   CommandLineWizard: Boolean;
 
 function GenerateGuid: String;
+function ISPPInstalled: Boolean;
+function ISCryptInstalled: Boolean;
 procedure InitFormFont(Form: TForm);
 
 implementation
@@ -550,6 +552,17 @@ begin
   end;
 end;
 
+function ISPPInstalled: Boolean;
+begin
+  Result := NewFileExists(PathExtractPath(NewParamStr(0)) + 'ISPP.dll');
+end;
+
+function ISCryptInstalled: Boolean;
+begin
+  Result := NewFileExists(PathExtractPath(NewParamStr(0)) + 'iscrypt.dll');
+end;
+
+
 { TISScintEdit }
 
 procedure TISScintEdit.CreateWnd;
@@ -843,6 +856,7 @@ begin
   SetFakeShortCut(BStopCompile, VK_ESCAPE, []);
 
   MemoStyler := TInnoSetupStyler.Create(Self);
+  MemoStyler.IsppInstalled := IsppInstalled;
 
   Memo := TISScintEdit.Create(Self);
   Memo.AcceptDroppedFiles := True;

+ 0 - 12
Projects/CompWizard.pas

@@ -141,8 +141,6 @@ type
     FResultScript: String;
     function FixLabel(const S: String): String;
     procedure SetWizardName(const WizardName: String);
-    function ISPPInstalled: Boolean;
-    function ISCryptInstalled: Boolean;
     procedure CurPageChanged;
     function SkipCurPage: Boolean;
     procedure AddWizardFile(const Source: String; const RecurseSubDirs, CreateAllSubDirs: Boolean);
@@ -216,16 +214,6 @@ begin
   FWizardName := WizardName;
 end;
 
-function TWizardForm.ISPPInstalled(): Boolean;
-begin
-  Result := NewFileExists(PathExtractPath(NewParamStr(0)) + 'ISPP.dll');
-end;
-
-function TWizardForm.ISCryptInstalled(): Boolean;
-begin
-  Result := NewFileExists(PathExtractPath(NewParamStr(0)) + 'iscrypt.dll');
-end;
-
 { --- }
 
 {$IFDEF IS_D7}

+ 2 - 1
whatsnew.htm

@@ -28,6 +28,7 @@ For conditions of distribution and use, see <a href="http://www.jrsoftware.org/f
 
 <p><a name="5.5.10"></a><span class="ver">5.5.10 </span><span class="date">(?)</span></p>
 <ul>
+<li>Compiler IDE change: Syntax highlighting and syntax error underlining support for <tt>#include</tt> and ISPP has been significantly improved (with ISPP installed: <a href="https://i.imgur.com/nPiun9k.png">before</a> and <a href="https://i.imgur.com/HoJaBRP.png">after</a>, without ISPP installed: <a href="https://i.imgur.com/nPiun9k.png">before</a> and <a href="https://i.imgur.com/ro0ejL3.png">after</a>. Based on a contribution by <a href="https://github.com/KngStr" target="_blank">KngStr</a> via <a href="https://github.com/jrsoftware" target="_blank">GitHub</a>.</li>
 <li>The <tt>WizardImageFile</tt> and <tt>WizardSmallImageFile</tt> [Setup] section directives now may list multiple files. This can be used to include multiple sizes of the wizard images to avoid blurred images on high DPI systems. See the help file for a list of recommended sizes. Note: when you test this be sure to first log out and back in after any DPI change.</li>
 <li>Any [Files] entries with the <tt>deleteafterinstall</tt> flag or with <tt>DestDir</tt> set to <tt>{tmp}</tt> or a subdirectory of <tt>{tmp}</tt> are no longer included in the <tt>EstimatedSize</tt> value in the Uninstall registry key.</li>
 <li><b>Change in default behavior:</b> If [Setup] section directive <tt>DisableWelcomePage</tt> is set (which it is by default), then the title of the wizard now includes <tt>AppVerName</tt> instead of <tt>AppName</tt>, in other words: it now includes the version number of the application. If <tt>WindowVisible</tt> is set to <tt>yes</tt> this applies to the background window instead.</li>
@@ -118,7 +119,7 @@ For conditions of distribution and use, see <a href="http://www.jrsoftware.org/f
 <ul>
   <li>Added new function <tt>CurrentSourceFileName</tt>, which returns the source file name of the [Files] entry that is currently being processed. The returned name may include constants. <i>Note:</i> Do not attempt to call this function from outside a Check, BeforeInstall or AfterInstall event function belonging to a [Files] entry with the <tt>external</tt> flag.</li>
   <li>The already-existing <tt>StrToFloat</tt> and <tt>FloatToStr</tt> functions are now documented.</li>
-  <li>Strings and numbers are now highlighted. Based on contribution by <a href="https://github.com/maldoinc" target="_blank">maldoinc</a> via <a href="https://github.com/jrsoftware" target="_blank">GitHub</a>.</li>
+  <li>Strings and numbers are now highlighted. Based on a contribution by <a href="https://github.com/maldoinc" target="_blank">maldoinc</a> via <a href="https://github.com/jrsoftware" target="_blank">GitHub</a>.</li>
   <li>Unicode Inno Setup: Added new class <tt>TStringStream</tt>.</li>
 </ul>
 </li>