summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorburaq <buraq@epfl.ch>2004-05-26 18:07:35 +0000
committerburaq <buraq@epfl.ch>2004-05-26 18:07:35 +0000
commit8dcb4da871915fcbd55b062843332d5b9c2e0ac8 (patch)
treef6cc698e4549c3c0db1c534ffd456ba79929d93a
parent02a6574294afe8355be2654e96ec9ec5f540306c (diff)
downloadscala-8dcb4da871915fcbd55b062843332d5b9c2e0ac8.tar.gz
scala-8dcb4da871915fcbd55b062843332d5b9c2e0ac8.tar.bz2
scala-8dcb4da871915fcbd55b062843332d5b9c2e0ac8.zip
pretty-print + whitespace trimming
-rw-r--r--config/list/library.lst1
-rw-r--r--sources/scala/tools/scalac/ast/parser/MarkupParser.scala64
-rw-r--r--sources/scala/tools/scalac/ast/parser/Parser.scala2
-rw-r--r--sources/scala/tools/scalac/ast/parser/Scanner.scala8
-rw-r--r--sources/scala/xml/PrettyPrinter.scala194
-rw-r--r--sources/scala/xml/Utility.scala53
-rw-r--r--sources/scalac/CompilerCommand.java5
-rw-r--r--sources/scalac/Global.java2
-rw-r--r--sources/scalac/util/Names.java1
-rw-r--r--test/files/jvm/xmlLiterals.check43
-rw-r--r--test/files/jvm/xmlLiterals.scala4
11 files changed, 288 insertions, 89 deletions
diff --git a/config/list/library.lst b/config/list/library.lst
index bbf8cdcc38..0bc4c3cf8f 100644
--- a/config/list/library.lst
+++ b/config/list/library.lst
@@ -163,6 +163,7 @@ xml/EntityRef.scala
xml/FactoryAdapter.scala
xml/Node.scala
xml/NodeSeq.scala
+xml/PrettyPrinter.scala
xml/ProcInstr.scala
xml/Text.scala
xml/Utility.scala
diff --git a/sources/scala/tools/scalac/ast/parser/MarkupParser.scala b/sources/scala/tools/scalac/ast/parser/MarkupParser.scala
index 19ecbf1194..765adc3c93 100644
--- a/sources/scala/tools/scalac/ast/parser/MarkupParser.scala
+++ b/sources/scala/tools/scalac/ast/parser/MarkupParser.scala
@@ -19,7 +19,7 @@ import scala.collection.mutable.Buffer;
import scalac.symtab.Modifiers;
package scala.tools.scalac.ast.parser {
-class MarkupParser( unit:Unit, s:Scanner, p:Parser ) {
+class MarkupParser( unit:Unit, s:Scanner, p:Parser, preserveWS:boolean ) {
import Tokens.{EMPTY, LBRACE, RBRACE} ;
import scala.tools.scalac.ast.{TreeList => myTreeList}
@@ -160,7 +160,7 @@ class MarkupParser( unit:Unit, s:Scanner, p:Parser ) {
Predef.Array[Tree]( t, _emptyMap( pos ) )
} else {
Predef.Array[Tree]( t, _emptyMap( pos ), make.Typed(
- pos, makeXMLseq(pos, args), make.Ident(pos, TypeNames.WILDCARD_STAR)))
+ pos, makeXMLseq(pos, args ), make.Ident(pos, TypeNames.WILDCARD_STAR)))
};
make.Apply( pos, _scala_xml_Elem( pos ), constrArgs )
}
@@ -174,9 +174,9 @@ class MarkupParser( unit:Unit, s:Scanner, p:Parser ) {
make.New( pos, constr );
};
// create scala.xml.Text here <: scala.xml.Node
- def makeText( pos: int, txt:String ):Tree =
+ def makeText( pos: int, txt:String ):Tree = {
makeText( pos, gen.mkStringLit( pos, txt ));
-
+ }
// create
def makeComment( pos: int, comment:scala.xml.Comment ):Tree =
makeComment( pos, gen.mkStringLit( pos, comment.text ));
@@ -265,30 +265,40 @@ class MarkupParser( unit:Unit, s:Scanner, p:Parser ) {
if( null == res ) ts else res;
}
+ def isEmptyText(t:Tree) = t match {
+ case Tree$Literal(atree.AConstant.STRING("")) => true;
+ case _ => false;
+ }
def makeXMLseq( pos:int, args:Array[Tree] ) = {
- val blocArr = new Array[Tree] ( 1 + args.length );
+ val ts = new TreeList();
+ //val blocArr = new Array[Tree] ( 1 + args.length );
val constr = _scala_collection_mutable_ArrayBuffer( pos );
val n = p.fresh();
val nIdent = make.Ident(pos, n);
- blocArr( 0 ) = make.ValDef(pos, Modifiers.MUTABLE, n, Tree.Empty,
- make.New( pos, constr ));
+ //blocArr( 0 )
+ ts.append( make.ValDef(pos, Modifiers.MUTABLE, n, Tree.Empty,
+ make.New( pos, constr )));
var i = 0; while( i < args.length ) {
val ipos = args(i).pos;
- val t = make.Apply( ipos,
- make.Select( ipos, nIdent, _append ),
- Predef.Array[Tree]( convertToText( args( i ) )));
+ if( !isEmptyText( args( i ))) {
+ ts.append(
+ make.Apply( ipos,
+ make.Select( ipos, nIdent, _append ),
+ Predef.Array[Tree]( convertToText( args( i ) )))
+ )
+ }
i = i + 1;
- blocArr( i ) = t
}
- make.Block( pos, blocArr, nIdent );
+ make.Block( pos, ts.toArray(), nIdent );
}
def makeXMLseqPat( pos:int, args:Array[Tree] ) = {
make.Apply( pos, _scala_Seq( pos ), args );
}
- def makeXML(pos:int,n:Name,args:Array[Tree],attrMap:ListMap[Name,Tree]):Tree = {
+ /** @todo: create map here directly */
+ def makeXML(pos:int,n:Name,args:Array[Tree],attrMap:ListMap[Name,Tree]):Tree={
var t = makeXML( pos, n, args );
if( attrMap.isEmpty ) {
t
@@ -309,6 +319,29 @@ class MarkupParser( unit:Unit, s:Scanner, p:Parser ) {
}
}
+ def trimWS( str:String ) = {
+ val sb = new StringBuffer();
+ val z:Seq[Char] = str;
+ var ws = false;
+ for( val c <- z ) {
+ if( s.xIsSpace( c ) ) {
+ if( !ws ) {
+ ws = true; sb.append( ' ' )
+ }
+ } else {
+ ws = false;
+ sb.append( c );
+ }
+ }
+ if( ' ' == sb.charAt( 0 ) ) {
+ sb.deleteCharAt( 0 )
+ }
+ val len = sb.length();
+ if( len > 0 && s.xIsSpace( sb.charAt( len-1 ) )) {
+ sb.setLength( len - 1 )
+ }
+ sb.toString();
+ }
/** xLiteral = xExpr { xExpr }
* @return Scala representation of this xml literal
@@ -452,7 +485,10 @@ class MarkupParser( unit:Unit, s:Scanner, p:Parser ) {
ts.append( makeEntityRef( s.pos, n ));
}
case _ => // text content
- ts.append( makeText( s.pos, s.xText ));
+ var text = s.xText;
+ if( !preserveWS ) text = trimWS( text );
+ if( text.length() > 0 )
+ ts.append( makeText( s.pos, text ));
}
}
}
diff --git a/sources/scala/tools/scalac/ast/parser/Parser.scala b/sources/scala/tools/scalac/ast/parser/Parser.scala
index 54f33cc900..dac75db0d0 100644
--- a/sources/scala/tools/scalac/ast/parser/Parser.scala
+++ b/sources/scala/tools/scalac/ast/parser/Parser.scala
@@ -38,7 +38,7 @@ class Parser(unit: Unit) {
/** the markup parser
*/
- val xmlp = new MarkupParser( unit, s, this );
+ val xmlp = new MarkupParser( unit, s, this, unit.global.xmlPreserveWS );
/** the tree factory
*/
diff --git a/sources/scala/tools/scalac/ast/parser/Scanner.scala b/sources/scala/tools/scalac/ast/parser/Scanner.scala
index 9b3646c032..8acc2d78b2 100644
--- a/sources/scala/tools/scalac/ast/parser/Scanner.scala
+++ b/sources/scala/tools/scalac/ast/parser/Scanner.scala
@@ -942,7 +942,7 @@ class Scanner(_unit: Unit) extends TokenData {
/* unit.global.xmlMarkup && */ ( token == IDENTIFIER )&&( name == LT );
}
- def xIsSpace = ch match {
+ def xIsSpace( ch:char ) = ch match {
case ' ' | '\t' | CR | LF => true
case _ => false;
}
@@ -950,13 +950,13 @@ class Scanner(_unit: Unit) extends TokenData {
/** skip optional space S?
*/
def xSpaceOpt = {
- while( xIsSpace ) { xNext; }
+ while( xIsSpace( ch ) ) { xNext; }
}
/** scan [3] S ::= (#x20 | #x9 | #xD | #xA)+
*/
def xSpace = {
- if( xIsSpace ) {
+ if( xIsSpace( ch ) ) {
xNext; xSpaceOpt
} else {
xSyntaxError("whitespace expected");
@@ -1168,7 +1168,7 @@ class Scanner(_unit: Unit) extends TokenData {
def xProcInstr:scala.xml.ProcInstr = {
val sb:StringBuffer = new StringBuffer();
val n = xName;
- if( xIsSpace ) {
+ if( xIsSpace( ch ) ) {
xSpace;
while( true ) {
if( ch=='?' && { sb.append( ch ); xNext; ch == '>' } ) {
diff --git a/sources/scala/xml/PrettyPrinter.scala b/sources/scala/xml/PrettyPrinter.scala
new file mode 100644
index 0000000000..edee411acf
--- /dev/null
+++ b/sources/scala/xml/PrettyPrinter.scala
@@ -0,0 +1,194 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+** $Id$
+\* */
+
+package scala.xml ;
+
+import java.lang.StringBuffer ; /* Java dependency! */
+import scala.collection.Map ;
+
+/** Utility functions for processing instances of bound and not bound XML
+** classes, as well as escaping text nodes
+**/
+
+class PrettyPrinter( width:Int, step:Int ) {
+
+ class BrokenException() extends java.lang.Exception();
+
+ class Item ;
+ case object Break extends Item {
+ override def toString() = "\\";
+ };
+ case class Box( col:Int, s:String ) extends Item;
+ case class Para( s:String ) extends Item;
+
+ var items:List[Item] = Nil;
+
+ var cur = 0;
+
+ def reset() = {
+ cur = 0;
+ items = Nil;
+ }
+
+ /** try to cut at whitespace */
+ def cut( s:String, ind:Int ):List[Item] = {
+ val tmp = width - cur;
+ if( s.length() < tmp )
+ return List(Box(ind,s));
+ val sb = new StringBuffer();
+ var i = s.indexOf(' ');
+ if( i > tmp ) throw new BrokenException(); // cannot break
+
+ var last = i::Nil;
+ while( i < tmp ) {
+ last = i::last;
+ i = s.indexOf(' ', i );
+ }
+ var res:List[Item] = Nil;
+ while( Nil != last ) try {
+ val b = Box( ind, s.substring( 0, last.head ));
+ cur = ind;
+ res = b :: Break :: cut( s.substring( last.head, s.length()), ind );
+ // backtrac
+ } catch {
+ case _:BrokenException => last = last.tail;
+ }
+ throw new BrokenException()
+ }
+
+ /** try to make indented box, if possible, else para */
+ def makeBox( ind:Int, s:String ) = {
+ if( cur < ind )
+ cur == ind;
+ if( cur + s.length() > width ) { // fits in this line
+ items = Box( ind, s ) :: items;
+ cur = cur + s.length()
+ } else try {
+ for( val b <- cut( s, ind ).elements ) // break it up
+ items = b :: items
+ } catch {
+ case _:BrokenException => makePara( ind, s ); // give up, para
+ }
+ }
+
+ // dont respect indent in para, but afterwards
+ def makePara( ind:Int, s:String ) = {
+ items = Break::Para( s )::Break::items;
+ cur = ind;
+ }
+
+ // respect indent
+ def makeBreak() = { // using wrapping here...
+ items = Break::items;
+ cur = 0;
+ }
+
+ def leafTag( n:Node ) = {
+ val sb = new StringBuffer("<");
+ sb.append( n.label );
+ Utility.attr2xml( n.attribute.elements, sb );
+ sb.append("/>");
+ sb.toString();
+ }
+
+ def startTag( n:Node ) = {
+ val sb = new StringBuffer("<");
+ sb.append( n.label );
+ Utility.attr2xml( n.attribute.elements, sb );
+ sb.append('>');
+ sb.toString();
+ }
+
+ /* serializes an instance of Node to a string that contains well-formed XML
+ **/
+ def toPrettyXML( n:Node ):String = {
+ traverse( n, 0 );
+ val sb = new StringBuffer();
+ var cur = 0;
+ //Console.println( items.reverse );
+ for( val b <- items.reverse ) b match {
+ case Break =>
+ sb.append('\n'); // on windows: \r\n
+ cur = 0;
+ case Box(i, s) =>
+ while( cur < i ) {
+ sb.append(' ');
+ cur = cur + 1;
+ }
+ sb.append( s );
+ case Para( s ) =>
+ sb.append( s );
+ }
+ sb.toString();
+ }
+
+// def toLastWS( len:Int, s:String ) = {
+// val sb = new StringBuffer();
+// while( len
+// attr2xml( attribute.elements, sb );
+// sb.append('>');
+// sb.toString();
+// }
+
+ def breakable( n:Node ):boolean = {
+ val it = n.child.elements;
+ while( it.hasNext )
+ it.next match {
+ case _:Text | _:CharData | _:Comment | _:EntityRef | _:ProcInstr =>
+ case _:Node => return true;
+ }
+ return false
+ }
+ /** @param tail: what we'd like to sqeeze in */
+ def traverse( node:Node, ind:int ):Unit = {
+ node match {
+
+ case _:Text | _:CharData | _:Comment | _:EntityRef | _:ProcInstr =>
+ makeBox( ind, node.toString() );
+
+ case _:Node =>
+ val test = node.toString();
+
+ if( ( test.length() < width - cur ) // all ?
+ &&( !breakable( node ))) {
+ makeBox( ind, test );
+ } else { // start tag + content + end tag
+ //Console.println(node.label+" ind="+ind);
+ val stg = startTag( node );
+ val endTag = "</"+node.label+">";
+ val len2 = node.label.length() + 1;
+
+ if( stg.length() < width - cur ) { // start tag fits
+
+ makeBox( ind, stg );
+ makeBreak();
+ traverse( node.child.elements, ind + step );
+ makeBox( ind, endTag );
+
+ } else if( len2 < width - cur ) {
+ // <start label + attrs + tag + content + end tag
+ makeBox( ind, stg.substring( 0, len2 ));
+ makeBreak();
+ makeBox( ind, stg.substring( len2, stg.length() ));
+ makeBreak();
+ traverse( node.child.elements, ind + step );
+ makeBox( cur, endTag );
+ }
+ }
+ }
+ }
+
+ def traverse( it:Iterator[Node], ind:int ):unit = {
+ for( val c <- it ) {
+ traverse( c, ind );
+ makeBreak();
+ }
+ }
+
+}
diff --git a/sources/scala/xml/Utility.scala b/sources/scala/xml/Utility.scala
index aab2f6b63f..df95177ed3 100644
--- a/sources/scala/xml/Utility.scala
+++ b/sources/scala/xml/Utility.scala
@@ -72,63 +72,22 @@ object Utility {
}
}
- /* serializes an instance of Node to a string that contains well-formed XML
- **/
-/*
- def toPrettyXML( n:Node, indent:int ):String = { n match {
- case Text( t ) =>
- escape( t );
- case x:AttributedNode =>
- val s = new StringBuffer();
- for( val i<-List.range(0,indent) ) {
- val _ = s.append(" ");
- {}
- }
- val spaces = s.toString();
- s.append('<');
- s.append( x.label );
- if( null != x.attributes ) {
- s.append( attr2xml( x.attributes.elements ) );{}
- }
- s.append('>');
- val childrenString = toXML( x.children.elements, 0 );
- if( indent + 2 * x.label.length() + 4 + childrenString.length() < COLS ) {
- s.append( childrenString );
- s.append("</");
- s.append( x.label );
- s.append('>');
- s.append('\n');
- } else {
- s.append('\n');
- val childrenString = toXML( x.children.elements, indent+1 ) ;
- s.append( childrenString );
- s.append('\n');
- s.append( spaces );
- s.append("</");
- s.append( x.label );
- s.append('>');
- }
- s.toString()
- }}
- def toXML( ch:Iterator[Node], ind:int ):String = {
- ch.foldLeft ("") { (s:String,n:Node) => {
- val t:String = toPrettyXML( n, ind ); s + t
- }}
- }
-todo: better pretty printing */
-
+ /*
def toXML( ch:Iterator[Node] ):String = {
ch.foldLeft ("") { (s:String,n:Node) => s + n.toString() }
}
+*/
/** for a Node n, returns string representation of n.attributes **/
def attr2xml( attrib:Iterator[Pair[String, String]], sb:StringBuffer ):Unit = {
for( val x <- attrib ) {
sb.append( " " );
sb.append( x._1 );
- sb.append("=\"");
+ sb.append("=");
+ val sep = { if( x._2.indexOf('"') != -1 ) '\'' else '"' };
+ sb.append( sep );
sb.append( x._2 );
- sb.append("\"");
+ sb.append( sep );
}
}
diff --git a/sources/scalac/CompilerCommand.java b/sources/scalac/CompilerCommand.java
index 343fec3202..b47041d665 100644
--- a/sources/scalac/CompilerCommand.java
+++ b/sources/scalac/CompilerCommand.java
@@ -84,6 +84,7 @@ public class CompilerCommand extends CommandParser {
public final BooleanOptionParser Xshortname;
public final BooleanOptionParser Xmarkup;
public final BooleanOptionParser Xnewmatch;
+ public final BooleanOptionParser XpreserveWS;
//########################################################################
// Public Constructors
@@ -245,6 +246,10 @@ public class CompilerCommand extends CommandParser {
"Xnewmatch", "new pattern matching",
false),
+ this.XpreserveWS = new BooleanOptionParser(this,
+ "XpreserveWS", "don't trim whitespace in XML literals",
+ false),
+
this.unknown_options = new UnknownOptionParser(this),
this.files = new ScalaFileArgumentParser(this),
diff --git a/sources/scalac/Global.java b/sources/scalac/Global.java
index 74c8dd58a7..1fd795f0b9 100644
--- a/sources/scalac/Global.java
+++ b/sources/scalac/Global.java
@@ -61,6 +61,7 @@ public abstract class Global {
public final boolean explaintypes;
public final boolean uniqid;
public final boolean newMatch;
+ public final boolean xmlPreserveWS;
public final boolean printtypes;
public final boolean printtokens;
@@ -215,6 +216,7 @@ public abstract class Global {
this.debug = args.debug.value;
this.uniqid = args.uniqid.value;
this.newMatch = args.Xnewmatch.value;
+ this.xmlPreserveWS = args.XpreserveWS.value;
this.explaintypes = args.explaintypes.value;
this.printtypes = args.types.value;
this.printtokens = args.print.tokens;
diff --git a/sources/scalac/util/Names.java b/sources/scalac/util/Names.java
index 4ab401a010..b9d8ab1257 100644
--- a/sources/scalac/util/Names.java
+++ b/sources/scalac/util/Names.java
@@ -130,6 +130,7 @@ public class Names {
public static final Name Short = Name.fromString("Short");
public static final Name String = Name.fromString("String");
public static final Name Symbol = Name.fromString("Symbol");
+ public static final Name Text = Name.fromString("Text");
public static final Name Throwable = Name.fromString("Throwable");
public static final Name Try = Name.fromString("Try");
public static final Name Tuple = Name.fromString("Tuple");
diff --git a/test/files/jvm/xmlLiterals.check b/test/files/jvm/xmlLiterals.check
index cf4493ca54..c68baec2d9 100644
--- a/test/files/jvm/xmlLiterals.check
+++ b/test/files/jvm/xmlLiterals.check
@@ -16,29 +16,28 @@ passed ok
passed ok
passed ok
Test03Servlet
+<html><head><title>ModularFormatting</title></head><body><h2>Welcome</h2><p>What follows is an example of modular formatting.</p><p><table align="center"><tr><td bgcolor="#AAAAFF" color="#222255"><h1>message</h1></td></tr></table></p><hr></hr><p>Complicated layout tasks can be encapsulated and outsourced.</p><h2>Bye!</h2></body></html>
<html>
- <head>
- <title>ModularFormatting</title>
- </head>
- <body>
- <h2>Welcome</h2>
- <p>
- What follows is an example of modular formatting.
- </p>
- <p>
- <table align="center">
- <tr>
- <td bgcolor="#AAAAFF" color="#222255"><h1> message </h1></td>
- </tr>
- </table>
- </p>
- <hr></hr>
- <p>
- Complicated layout tasks can be encapsulated and outsourced.
- </p>
- <h2>Bye!</h2>
- </body>
- </html>
+ <head>
+ <title>ModularFormatting</title>
+ </head>
+ <body>
+ <h2>Welcome</h2>
+ <p>What follows is an example of modular formatting.</p>
+ <p>
+ <table align="center">
+ <tr>
+ <td bgcolor="#AAAAFF" color="#222255">
+ <h1>message</h1>
+ </td>
+ </tr>
+ </table>
+ </p>
+ <hr></hr>
+ <p>Complicated layout tasks can be encapsulated and outsourced.</p>
+ <h2>Bye!</h2>
+ </body>
+</html>
Test04
ArrayBuffer(<foo></foo>, <bar>Text</bar>, <foo></foo>)
<foo></foo>
diff --git a/test/files/jvm/xmlLiterals.scala b/test/files/jvm/xmlLiterals.scala
index 6054801bcf..3b20525159 100644
--- a/test/files/jvm/xmlLiterals.scala
+++ b/test/files/jvm/xmlLiterals.scala
@@ -219,7 +219,9 @@ object Test03Servlet {
}
def main( args:Array[String] ) = {
- Console.println( doGetXML() );
+ val x = doGetXML();
+ Console.println( x );
+ Console.println( new PrettyPrinter( 80, 2 ).toPrettyXML( x ));
}
}