Browse Source

* Patch from Mattias Gaertner
- fixed writing UTF-8.
- indent multi line literals, used by asm blocks
- fixed writing (a+b).c

git-svn-id: trunk@35502 -

michael 8 years ago
parent
commit
2fbe76532f
1 changed files with 162 additions and 57 deletions
  1. 162 57
      packages/fcl-js/src/jswriter.pp

+ 162 - 57
packages/fcl-js/src/jswriter.pp

@@ -20,7 +20,7 @@ unit jswriter;
 interface
 
 uses
-  {Classes, } SysUtils, jstoken, jsbase, jstree;
+  SysUtils, jstoken, jsbase, jstree;
 
 Type
 
@@ -31,7 +31,7 @@ Type
     Function DoWrite(Const S : AnsiString) : Integer; virtual; abstract;
     Function DoWrite(Const S : UnicodeString) : Integer; virtual; abstract;
   Public
-    // All functions return the numberof bytes copied to output stream.
+    // All functions return the number of bytes copied to output stream.
     Function Write(Const S : UnicodeString) : Integer;
     Function Write(Const S : AnsiString) : Integer;
     Function WriteLn(Const S : AnsiString) : Integer;
@@ -58,6 +58,7 @@ Type
   end;
 
   { TBufferWriter }
+
   TBytes = Array of byte;
   TBufferWriter = Class(TTextWriter)
   private
@@ -157,8 +158,7 @@ Type
     Procedure WritePrimaryExpression(El: TJSPrimaryExpression);virtual;
     Procedure WriteBinary(El: TJSBinary);virtual;
   Public
-    Function EscapeString(const S: TJSString; Quote: TJSEscapeQuote = jseqDouble): String;
-    Function JSStringToStr(const S: TJSString): string;
+    Function EscapeString(const S: TJSString; Quote: TJSEscapeQuote = jseqDouble): TJSString;
     Constructor Create(AWriter : TTextWriter);
     Constructor Create(Const AFileName : String);
     Destructor Destroy; override;
@@ -172,12 +172,31 @@ Type
   end;
   EJSWriter = Class(Exception);
 
+Function UTF16ToUTF8(const S: UnicodeString): string;
+
 implementation
 
 Resourcestring
   SErrUnknownJSClass = 'Unknown javascript element class : %s';
   SErrNilNode = 'Nil node in Javascript';
 
+function HexDump(p: PChar; Count: integer): string;
+var
+  i: Integer;
+begin
+  Result:='';
+  for i:=0 to Count-1 do
+    Result:=Result+HexStr(ord(p[i]),2);
+end;
+
+function UTF16ToUTF8(const S: UnicodeString): string;
+begin
+  Result:=UTF8Encode(S);
+  // prevent UTF8 codepage appear in the strings - we don't need codepage
+  // conversion magic
+  SetCodePage(RawByteString(Result), CP_ACP, False);
+end;
+
 { TBufferWriter }
 
 function TBufferWriter.GetBufferLength: Integer;
@@ -332,13 +351,13 @@ end;
 procedure TJSWriter.Write(const U: UnicodeString);
 
 Var
-  S : UTF8String;
+  S : String;
 
 begin
   WriteIndent;
   if UseUTF8 then
     begin
-    S:=UTF8Encode(U);
+    S:=UTF16ToUTF8(U);
     FLinePos:=FLinePos+Writer.Write(S);
     end
   else
@@ -370,12 +389,12 @@ end;
 
 procedure TJSWriter.WriteLn(const U: UnicodeString);
 Var
-  S : UTF8String;
+  S : String;
 
 begin
   if UseUTF8 then
     begin
-    S:=UTF8Encode(U);
+    S:=UTF16ToUTF8(U);
     Writeln(S);
     end
   else
@@ -387,81 +406,153 @@ begin
 end;
 
 function TJSWriter.EscapeString(const S: TJSString; Quote: TJSEscapeQuote
-  ): String;
+  ): TJSString;
 
 Var
   I,J,L : Integer;
   P : TJSPChar;
+  R: TJSString;
 
 begin
   I:=1;
   J:=1;
-  Result:='';
+  R:='';
   L:=Length(S);
   P:=TJSPChar(S);
   While I<=L do
     begin
     if (P^ in [#0..#31,'"','''','/','\']) then
       begin
-      Result:=Result+JSStringToStr(Copy(S,J,I-J));
+      R:=R+Copy(S,J,I-J);
       Case P^ of
-        '\' : Result:=Result+'\\';
-        '/' : Result:=Result+'\/';
-        '"' : if Quote=jseqSingle then Result:=Result+'"' else Result:=Result+'\"';
-        '''': if Quote=jseqDouble then Result:=Result+'''' else Result:=Result+'\''';
-        #0..#7,#11,#14..#31: Result:=Result+'\x'+hexStr(ord(P^),2);
-        #8  : Result:=Result+'\b';
-        #9  : Result:=Result+'\t';
-        #10 : Result:=Result+'\n';
-        #12 : Result:=Result+'\f';
-        #13 : Result:=Result+'\r';
+        '\' : R:=R+'\\';
+        '/' : R:=R+'\/';
+        '"' : if Quote=jseqSingle then R:=R+'"' else R:=R+'\"';
+        '''': if Quote=jseqDouble then R:=R+'''' else R:=R+'\''';
+        #0..#7,#11,#14..#31: R:=R+'\x'+TJSString(hexStr(ord(P^),2));
+        #8  : R:=R+'\b';
+        #9  : R:=R+'\t';
+        #10 : R:=R+'\n';
+        #12 : R:=R+'\f';
+        #13 : R:=R+'\r';
       end;
       J:=I+1;
       end;
     Inc(I);
     Inc(P);
     end;
-  Result:=Result+JSStringToStr(Copy(S,J,I-1));
-end;
-
-function TJSWriter.JSStringToStr(const S: TJSString): string;
-begin
-  if UseUTF8 then
-    Result:=UTF8Encode(S)
-  else
-    Result:=String(S);
+  R:=R+Copy(S,J,I-1);
+  Result:=R;
 end;
 
 procedure TJSWriter.WriteValue(V: TJSValue);
+const
+  TabWidth = 4;
+
+  function GetLineIndent(var p: PWideChar): integer;
+  var
+    h: PWideChar;
+  begin
+    h:=p;
+    Result:=0;
+    repeat
+      case h^ of
+      #0: break;
+      #9: Result:=Result+(TabWidth-Result mod TabWidth);
+      ' ': inc(Result);
+      else break;
+      end;
+      inc(h);
+    until false;
+    p:=h;
+  end;
+
+  function SkipToNextLineStart(p: PWideChar): PWideChar;
+  begin
+    repeat
+      case p^ of
+      #0: break;
+      #10,#13:
+        begin
+        if (p[1] in [#10,#13]) and (p^<>p[1]) then
+          inc(p,2)
+        else
+          inc(p);
+        break;
+        end
+      else inc(p);
+      end;
+    until false;
+    Result:=p;
+  end;
 
 Var
   S : String;
   JS: TJSString;
+  p, StartP: PWideChar;
+  MinIndent, CurLineIndent: Integer;
 begin
   if V.CustomValue<>'' then
-    S:=JSStringToStr(V.CustomValue)
-  else
-    Case V.ValueType of
-      jstUNDEFINED : S:='undefined';
-      jstNull : s:='null';
-      jstBoolean : if V.AsBoolean then s:='true' else s:='false';
-      jstString :
-        begin
-        JS:=V.AsString;
-        if Pos('"',JS)>0 then
-          S:=''''+EscapeString(JS,jseqSingle)+''''
-        else
-          S:='"'+EscapeString(JS,jseqDouble)+'"';
-        end;
-      jstNumber :
-        if Frac(V.AsNumber)=0 then // this needs to be improved
-          Str(Round(V.AsNumber),S)
-        else
-          Str(V.AsNumber,S);
-      jstObject : ;
-      jstReference : ;
-      JSTCompletion : ;
+    begin
+    JS:=V.CustomValue;
+    if JS='' then exit;
+
+    p:=SkipToNextLineStart(PWideChar(JS));
+    if p^=#0 then
+      begin
+      // simple value
+      Write(JS);
+      exit;
+      end;
+
+    // multi line value
+
+    // find minimum indent
+    MinIndent:=-1;
+    repeat
+      CurLineIndent:=GetLineIndent(p);
+      if (MinIndent<0) or (MinIndent>CurLineIndent) then
+        MinIndent:=CurLineIndent;
+      p:=SkipToNextLineStart(p);
+    until p^=#0;
+
+    // write value lines indented
+    p:=PWideChar(JS);
+    GetLineIndent(p); // the first line is already indented, skip
+    repeat
+      StartP:=p;
+      p:=SkipToNextLineStart(StartP);
+      Write(copy(JS,StartP-PWideChar(JS)+1,p-StartP));
+      if p^=#0 then break;
+      CurLineIndent:=GetLineIndent(p);
+      Write(StringOfChar(FIndentChar,FCurIndent+CurLineIndent-MinIndent));
+    until false;
+
+    exit;
     end;
+  Case V.ValueType of
+    jstUNDEFINED : S:='undefined';
+    jstNull : s:='null';
+    jstBoolean : if V.AsBoolean then s:='true' else s:='false';
+    jstString :
+      begin
+      JS:=V.AsString;
+      if Pos('"',JS)>0 then
+        JS:=''''+EscapeString(JS,jseqSingle)+''''
+      else
+        JS:='"'+EscapeString(JS,jseqDouble)+'"';
+      Write(JS);
+      exit;
+      end;
+    jstNumber :
+      if Frac(V.AsNumber)=0 then // this needs to be improved
+        Str(Round(V.AsNumber),S)
+      else
+        Str(V.AsNumber,S);
+    jstObject : ;
+    jstReference : ;
+    JSTCompletion : ;
+  end;
   Write(S);
 end;
 
@@ -680,10 +771,24 @@ end;
 
 procedure TJSWriter.WriteMemberExpression(El: TJSMemberExpression);
 
+var
+  MExpr: TJSElement;
 begin
   if El is TJSNewMemberExpression then
     Write('new ');
-  WriteJS(El.MExpr);
+  MExpr:=El.MExpr;
+  if (MExpr is TJSPrimaryExpression)
+      or (MExpr is TJSDotMemberExpression)
+      or (MExpr is TJSBracketMemberExpression)
+      or (MExpr is TJSCallExpression)
+      or (MExpr is TJSLiteral) then
+    WriteJS(MExpr)
+  else
+    begin
+    Write('(');
+    WriteJS(MExpr);
+    Write(')');
+    end;
   if El is TJSDotMemberExpression then
     begin
     write('.');
@@ -1309,23 +1414,23 @@ begin
   Result:=DoWrite(S);
 end;
 
-Function TTextWriter.Write(Const S: String) : integer;
+Function TTextWriter.Write(Const S: AnsiString) : integer;
 begin
   Result:=DoWrite(S);
 end;
 
-Function TTextWriter.WriteLn(Const S: String) : Integer;
+Function TTextWriter.WriteLn(Const S: AnsiString) : Integer;
 begin
   Result:=DoWrite(S)+DoWrite(sLineBreak);
 end;
 
-Function TTextWriter.Write(Const Fmt: String; Args: Array of const) : Integer;
+Function TTextWriter.Write(Const Fmt: AnsiString; Args: Array of const) : Integer;
 
 begin
   Result:=DoWrite(Format(Fmt,Args));
 end;
 
-Function TTextWriter.WriteLn(Const Fmt: String; Args: Array of const) : integer;
+Function TTextWriter.WriteLn(Const Fmt: AnsiString; Args: Array of const) : integer;
 begin
   Result:=WriteLn(Format(Fmt,Args));
 end;