summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala25
-rw-r--r--src/compiler/scala/tools/nsc/Main.scala19
-rw-r--r--src/compiler/scala/tools/nsc/Settings.scala1
-rw-r--r--src/compiler/scala/tools/nsc/doc/DocGenerator.scala724
-rw-r--r--src/compiler/scala/tools/nsc/doc/DocUtil.scala82
-rw-r--r--src/compiler/scala/tools/nsc/models/Models.scala91
-rw-r--r--src/compiler/scala/tools/nsc/models/SemanticTokens.scala2
-rw-r--r--src/compiler/scala/tools/nsc/util/NameTransformer.scala4
8 files changed, 666 insertions, 282 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index 35373f9914..978bbf1da0 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -32,7 +32,6 @@ class Global(val settings: Settings, val reporter: Reporter) extends SymbolTable
with CompilationUnits
{
-
// sub-components --------------------------------------------------
object treePrinters extends TreePrinters {
@@ -83,7 +82,6 @@ class Global(val settings: Settings, val reporter: Reporter) extends SymbolTable
if (onlyPresentation) new HashMap[Symbol,String];
else null;
-
// reporting -------------------------------------------------------
def error(msg: String) = reporter.error(null, msg);
@@ -100,6 +98,27 @@ class Global(val settings: Settings, val reporter: Reporter) extends SymbolTable
def log(msg: Object): unit =
if (settings.log contains phase.name) inform("[log " + phase + "] " + msg);
+ class ErrorWithPosition(val pos : Int, val error : Throwable) extends Error;
+
+ def tryWith[T](pos : Int, body : => T) : T = try {
+ body;
+ } catch {
+ case e : ErrorWithPosition => throw e;
+ case te: TypeError => throw te;
+ case e : Error => throw new ErrorWithPosition(pos, e);
+ case e : RuntimeException => throw new ErrorWithPosition(pos, e);
+ }
+ def catchWith[T](source : SourceFile, body : => T) : T = try {
+ body;
+ } catch {
+ case e : ErrorWithPosition =>
+ logError("POS: " + source.dbg(e.pos), e);
+ throw e.error;
+ }
+
+
+
+
def logError(msg: String, t : Throwable): Unit = {};
def abort(msg: String) = throw new Error(msg);
@@ -305,7 +324,7 @@ class Global(val settings: Settings, val reporter: Reporter) extends SymbolTable
private var curRun: Run = NoRun;
override def currentRun: Run = curRun;
- def onlyPresentation = false;
+ def onlyPresentation = settings.doc.value;
class Run extends CompilerRun {
var currentUnit : CompilationUnit = _;
diff --git a/src/compiler/scala/tools/nsc/Main.scala b/src/compiler/scala/tools/nsc/Main.scala
index 8a40c8a42c..4ea6108330 100644
--- a/src/compiler/scala/tools/nsc/Main.scala
+++ b/src/compiler/scala/tools/nsc/Main.scala
@@ -7,6 +7,8 @@ package scala.tools.nsc;
import scala.tools.nsc.util.{Position};
import scala.tools.nsc.reporters.{Reporter, ConsoleReporter};
+import scala.tools.nsc.doc.DocGenerator;
+
/** The main class for NSC, a compiler for the programming
* language Scala.
@@ -48,13 +50,24 @@ object Main extends Object with EvalLoop {
reporter.info(null, command.usageMsg, true)
else {
try {
- val compiler = new Global(command.settings, reporter);
+ object compiler extends Global(command.settings, reporter);
if (command.settings.resident.value)
resident(compiler);
else if (command.files.isEmpty)
reporter.info(null, command.usageMsg, true)
- else
- (new compiler.Run) compile command.files;
+ else {
+ val run = new compiler.Run;
+ run compile command.files;
+
+
+ if (command.settings.doc.value) {
+ object generator extends DocGenerator {
+ val global : compiler.type = compiler;
+ val outdir = command.settings.outdir.value;
+ };
+ generator.process(run.units);
+ }
+ }
} catch {
case ex @ FatalError(msg) =>
if (command.settings.debug.value)
diff --git a/src/compiler/scala/tools/nsc/Settings.scala b/src/compiler/scala/tools/nsc/Settings.scala
index 26ad1d57a0..20ba58bf29 100644
--- a/src/compiler/scala/tools/nsc/Settings.scala
+++ b/src/compiler/scala/tools/nsc/Settings.scala
@@ -52,6 +52,7 @@ class Settings(error: String => unit) {
} else null
}
+ val doc = BooleanSetting("-doc", "Generate documentation");
val debuginfo = BooleanSetting("-g", "Generate debugging info")
val nowarnings = BooleanSetting("-nowarn", "Generate no warnings")
val noassertions = BooleanSetting("-noassert", "Generate no assertions and assumptions")
diff --git a/src/compiler/scala/tools/nsc/doc/DocGenerator.scala b/src/compiler/scala/tools/nsc/doc/DocGenerator.scala
index d1d719497a..ae1c8d4bb2 100644
--- a/src/compiler/scala/tools/nsc/doc/DocGenerator.scala
+++ b/src/compiler/scala/tools/nsc/doc/DocGenerator.scala
@@ -1,120 +1,469 @@
package scala.tools.nsc.doc;
import scala.tools.nsc._;
-import java.io.File;
+import java.io._;
import scala.tools.nsc.models._;
import scala.collection.immutable._;
import scala.xml._;
abstract class DocGenerator extends Models {
import global._;
+ import DocUtil._;
+ def outdir : String;
+ def contentFrame = "contentFrame";
+ def classesFrame = "classesFrame";
+ def modulesFrame = "modulesFrame";
+ def emptyMap = ListMap.Empty[Kind,TreeSet[HasTree]];
- def dquote(str : String) = Text("\"" + str + "\"");
- val header = <meta http-equiv="content-type" content="text/html; charset=iso-8859-1"/>
- <meta name="generator" content="scaladoc (1.4.0.4)"/>
- <link rel="stylesheet" type="text/css" href="style.css"/>
- <script type="text/javascript" src="script.js"></script>;
+ override def acceptPrivate = false;
- def page = <HTML></HTML>;
+ abstract class Frame extends UrlContext {
+ def path : String; // relative to outdir
+ def relative : String = {
+ assert(path != null);
+ var idx = 0;
+ var ct = "";
+ while (idx != -1) {
+ idx = path.indexOf('/', idx);
+ //System.err.println(path + " idx=" + idx);
+ ct = ct + (if (idx != -1) "../" else "");
+ idx = idx + (if (idx == -1) 0 else 1);
+ }
+ ct;
+ }
+
+ def body : NodeSeq;
+ def title : String;
+ def save(nodes : NodeSeq) = {
+ val path0 = outdir + "/" + path + ".html";
+ System.err.println("Writing to " + path0);
+ val file = new File(path0);
+ val parent = file.getParentFile();
+ if (!parent.exists()) parent.mkdirs();
+ val writer = new FileWriter(file);
+ val str = dtype + "\n" + nodes.toString();
+ writer.write(str, 0, str.length());
+ writer.close();
+ }
+ def urlFor(sym : Symbol, target : String) : NodeSeq =
+ aref(urlFor(sym), target, sym.nameString);
+
+
+ def urlFor0(sym : Symbol, orig : Symbol) : String = {
+ (if (sym == NoSymbol) {
+ "XXX";
+ } else if (sym.owner.isPackageClass) sym.fullNameString('/');
+ else urlFor0(sym.owner, orig) + "." + Utility.escape(sym.nameString)) + (sym match {
+ case msym : ModuleSymbol => "$object";
+ case csym : ClassSymbol => "";
+ case _ =>
+ System.err.println("XXX: class or object " + orig + " not found in " + sym);
+ "XXXXX";
+
+ })
+ }
+ def urlFor(sym : Symbol) : String = {
+ sym match {
+ case msym : ModuleSymbol => urlFor0(sym,sym) + ".html";
+ case csym : ClassSymbol => urlFor0(sym,sym) + ".html";
+ case _ => urlFor(sym.owner) + "#" + Utility.escape(sym.nameString);
+ }
+ }
+ def hasBody = true;
+
+ save(page(title, body, hasBody));
+ }
+
+ abstract class ListModuleFrame extends Frame {
+ val path = "modules";
+ val title = "List of all packages";
+ def modules : TreeMap[String,ModuleClassSymbol];
+ def body : NodeSeq = {
+ val x = div0("Scala 2") concat
+ aref("all-classes.html", classesFrame, "All objects and classes");
+ val y = <P/><B>Packages</B>
+ <TABLE class="list"><TR><TD style="white-space:nowrap;">
+ { {
+ for (val top <- modules.elements.toList) yield
+ {br(aref(urlFor(top._2), classesFrame, top._2.fullNameString('.')))};
+ } }
+ </TD></TR></TABLE>;
+ x.concat(y);
+ }
+ }
+
+ abstract class ListModuleContentFrame extends Frame {
+ val path = "root-content";
+ val title = "All Packages";
+ def modules : TreeMap[String,ModuleClassSymbol];
+ def body : NodeSeq = {
+ <SPAN><DIV class="page-title">
+ Scala 2
+ <BR/>API Specification
+ </DIV>
+ This document is the API specification for Scala 2.
+ <P/><HR/>
+ <TABLE cellpadding="3" class="member">
+ <TR><TD colspan="2" class="title">
+ Package Summary
+ </TD>
+ </TR>{ {
+ for (val top <- modules.elements.toList) yield
+ <TR><TD class="signature">
+ <CODE>{Text("package")}
+ {(aref(top._2.fullNameString('/') + "$content.html", "_self", top._2.fullNameString('.')))}</CODE>
+ </TD></TR>;
+ } }
+ </TABLE>
+ </SPAN>;
+ }
+ }
+
+ abstract class ListClassFrame extends Frame {
+ def classes : ListMap[Kind,TreeSet[HasTree]];
+
+ def navLabel : String;
+
+ def body : NodeSeq = {
+ val nav = <TABLE class="navigation"><TR><TD valign="top" class="navigation-links">
+ {aref(path + ".html", contentFrame, navLabel)}
+ </TD></TR></TABLE><P/>;
+
+
+ val body = <SPAN> { { for (val kind <- KINDS; classes.contains(kind)) yield {
+ val x = <B>{Text(pluralFor(kind))}</B>;
+
+ val y = <TABLE class="list"><TR><TD style="white-space;nowrap;">
+ { {
+ for (val mmbr <- classes(kind).toList) yield
+ br(urlFor(mmbr.tree.symbol, contentFrame));
+ } }
+ </TD></TR></TABLE>;
+ val ret :NodeSeq = x.concat(y);
+ ret;
+ } } } </SPAN>;
+
+ nav.concat(body);
+ }
+ }
+
+ abstract class ContentFrame0 extends Frame {
+ def extendsFor(mmbr : HasTree) : NodeSeq = mmbr match {
+ case mmbr : ImplMod =>
+ if (!mmbr.treey.impl.parents.isEmpty)
+ <SPAN><dd><code>{Text(" extends ")}</code>
+ {forType(mmbr.treey.impl.parents.head.tpe)}</dd>
+ { { for (val parent <- mmbr.treey.impl.parents.tail)
+ yield <dd><code>{Text(" with ")}</code>
+ {forType(parent.tpe)}</dd>;
+ } } </SPAN>;
+ else NodeSeq.Empty;
+ case _ => NodeSeq.Empty;
+ }
+ def fullHeader(mmbr : HasTree) : NodeSeq = <SPAN>{ {
+ if (!mmbr.isInstanceOf[ImplMod]) {
+ <a name = {Utility.escape(mmbr.tree.symbol.nameString)}></a>;
+ } else NodeSeq.Empty;
+ } }<DL><DT>
+ { { for (val str <- stringsFor(mmbr.mods)) yield (Text(str + " ")) } }
+ <CODE>{ Text(codeFor(mmbr.kind)) }</CODE>
+ <EM>{ Text(mmbr.tree.symbol.nameString) }</EM>
+ { typesFor(mmbr) }{ argsFor(mmbr)}{resultFor(mmbr) }
+ </DT> { extendsFor(mmbr) }
+ </DL> { fullComment(mmbr) } <HR/>
+ { lists(mmbr) } </SPAN>;
+
+ def lists(mmbr : HasTree) = mmbr match {
+ case cmod : ImplMod => <SPAN>{ listMembersShort(mmbr) }
+ { listMembersFull (mmbr) }</SPAN>
+ case _ => NodeSeq.Empty;
+ }
+
+
+ def listMembersShort(mmbr : HasTree) : NodeSeq = if (mmbr.isInstanceOf[Composite]) {
+ val map = organize(mmbr.asInstanceOf[Composite], emptyMap);
+ <SPAN> { {
+ for (val kind <- KINDS; map.contains(kind)) yield {
+ val x = <TABLE cellpadding="3" class="member">
+ <TR><TD colspan="2" class="title">{Text(labelFor(kind))} Summary</TD></TR>
+ { {
+ for (val mmbr <- map(kind).toList) yield shortHeader(mmbr);
+ } }
+ </TABLE>;
+ br(x);
+ }
+ } } </SPAN>
+ } else NodeSeq.Empty;
+
+ def listMembersFull(mmbr : HasTree) : NodeSeq = if (mmbr.isInstanceOf[Composite]) {
+ val map = organize(mmbr.asInstanceOf[Composite], emptyMap);
+ val mmbrx = mmbr;
+ val pathx = path;
+ for (val kind0 <- OBJECT :: CLASS :: Nil; map.contains(kind0)) for (val mmbr <- map(kind0)) {
+ new ContentFrame {
+ def clazz = mmbr.asInstanceOf[ImplMod];
+ def kind = kind0;
+ def title = labelFor(kind0) + " " + mmbr.tree.symbol.nameString + " in " + codeFor(mmbrx.kind) + " " + mmbr.tree.symbol.owner.fullNameString('.');
+ }
+ }
+ <SPAN> { {
+ for (val kind <- KINDS; map.contains(kind) && kind != OBJECT && kind != CLASS) yield {
+ val header = <table cellpadding="3" class="member-detail">
+ <tr><td class="member-title">{Text(labelFor(kind))} Detail</td></tr>
+ </table>;
+ val body = for (val mmbr <- map(kind).toList) yield <SPAN>{fullHeader(mmbr)}</SPAN>;
+ header.concat(body);
+ }
+ } } </SPAN>;
+ } else NodeSeq.Empty;
+
+ def shortHeader(mmbr : HasTree) : NodeSeq = {
+ <TR>
+ <TD valign="top" class="modifiers">
+ { { for (val str <- stringsFor(mmbr.mods)) yield <CODE>{(Text(str + " "))}</CODE>; } }
+ </TD>
+ <TD class="signature">
+ <CODE>{Text(codeFor(mmbr.kind))}</CODE>
+ <EM>{urlFor(mmbr.tree.symbol, contentFrame)}</EM>
+ { typesFor(mmbr) }
+ { argsFor(mmbr) }
+ {resultFor(mmbr) }
+ <BR>{shortComment(mmbr)}</BR>
+ </TD>
+ </TR>;
+ }
+
+
+ def fullComment(mmbr : HasTree) : NodeSeq = {
+ if (comments.contains(mmbr.tree.symbol))
+ comment(comments(mmbr.tree.symbol), false) else NodeSeq.Empty;
+ };
+ def shortComment(mmbr : HasTree) : NodeSeq = {
+ if (comments.contains(mmbr.tree.symbol))
+ comment(comments(mmbr.tree.symbol), true) else NodeSeq.Empty;
+ };
+ def ifT (cond : Boolean, nodes : NodeSeq) = if (cond) nodes else NodeSeq.Empty;
+ def ifT (tree : Tree, nodes : NodeSeq, before : Boolean) = {
+ if (tree != EmptyTree &&
+ tree.tpe.symbol != definitions.AnyClass &&
+ tree.tpe.symbol != definitions.AllClass) {
+ if (before) nodes.concat(forTree(tree));
+ else forTree(tree).concat(nodes);
+ } else NodeSeq.Empty;
+ }
+
+ def forType(tpe : Type) : NodeSeq =
+ urlFor(tpe.symbol, contentFrame);
+
+ def forTree(tree : Tree) : NodeSeq = tree match {
+ case vdef : ValDef =>
+ Text(vdef.symbol.name.toString()).concat(Text(" : ")).concat(forTree(vdef.tpt));
+ case sel : Select => forTree(sel.qualifier).concat(Text(sel.symbol.nameString));
+ case tree : AbsTypeDef =>
+ ifT(tree.lo, Text(" <: "), false).
+ concat(Text(tree.symbol.nameString)).concat(ifT(tree.hi, Text(" <: "), true));
+ case tpt : TypeTree => urlFor(tpt.tpe.symbol, contentFrame);
+ case id : Ident => Text("YY: " + id.symbol.nameString);
+ case EmptyTree => NodeSeq.Empty;
+ case _ => Text("XX=" + tree.getClass() + " " + tree.toString());
+ }
+ def forTrees(trees : List[Tree]) : NodeSeq = {
+ if (trees.isEmpty) NodeSeq.Empty;
+ else {
+ val head = forTree(trees.head);
+ head.concat(if (trees.tail.isEmpty) NodeSeq.Empty
+ else Text(", ")).concat(forTrees(trees.tail));
+ }
+ }
+
+ def surround(open : String, close : String, node : NodeSeq) : NodeSeq =
+ Text(open).concat(node).concat(Text(close));
+
+ def typesFor(ht : HasTree) : NodeSeq = {
+ val tparams = ht.tree match {
+ case cdef : ClassDef => cdef.tparams;
+ case ddef : DefDef => ddef.tparams;
+ case adef : AliasTypeDef => adef.tparams;
+ case _ => Nil;
+ }
+ if (tparams.isEmpty) Text("");
+ else surround("[", "]", forTrees(tparams));
+ }
+ def argsFor(ht : HasTree) : NodeSeq = ht.tree match {
+ case ddef : DefDef =>
+ if (!ddef.vparamss.isEmpty &&
+ (!ddef.vparamss.tail.isEmpty || !ddef.vparamss.head.isEmpty)) {
+ val nodes = for (val vparams <- ddef.vparamss)
+ yield surround("(", ")", forTrees(vparams));
+ nodes.flatMap(x => x.asList);
+ } else NodeSeq.Empty;
+ case _ => NodeSeq.Empty;
+ }
+ def resultFor(ht : HasTree) : NodeSeq = ht.tree match {
+ case vdef : ValOrDefDef =>
+ if (!vdef.symbol.nameString.equals("this"))
+ Text(" : ").concat(forTree(vdef.tpt));
+ else NodeSeq.Empty;
+ case _ => NodeSeq.Empty;
+ }
+ }
+
+ abstract class ListClassContentFrame extends ContentFrame0 {
+ def classes : ListMap[Kind,TreeSet[HasTree]];
+ def module : ModuleClassSymbol;
+
+ def path = module.fullNameString('/') + "$content";
+ def title = "All Classes and Objects in " + module.fullNameString('.');
+
+ def body : NodeSeq = {
+ <SPAN><DIV class="page-title">
+ Scala 2
+ <BR/>API Specification
+ </DIV>
+ This document is the API specification for Scala 2.
+ <P/>
+ { {
+ for (val kind <- KINDS; classes.contains(kind)) yield {
+ <SPAN><HR/><TABLE cellpadding="3" class="member">
+ <TR><TD colspan="2" class="title">
+ {labelFor(kind)} Summary
+ </TD></TR>{ {
+ for (val mmbr <- classes(kind).toList) yield shortHeader(mmbr);
+ } }
+ </TABLE></SPAN>
+ }
+ } }
+ </SPAN>;
+ }
+ }
- val emptyMap = ListMap.Empty[Kind,TreeSet[HasTree]];
- def process(units : Iterator[CompilationUnit], outdir : String) : Unit = {
- val outdir0 = new File(outdir);
- if (!outdir0.exists()) outdir0.mkdir();
+ abstract class ContentFrame extends ContentFrame0 {
+ def clazz : ImplMod;
+ def kind : Kind;
+ def body : NodeSeq = <SPAN>{navigation}{header0}{fullHeader(clazz)}</SPAN>;
+
+ final def path = urlFor0(clazz.tree.symbol,clazz.tree.symbol);
+
+ def navigation : NodeSeq =
+ <TABLE class="navigation">
+ <TR>
+ <TD valign="top" class="navigation-links">
+ <TABLE><TR>
+ <TD class="navigation-enabled">{aref("root-page.html", "_self", "Overview")}</TD>
+ <TD class="navigation-enabled">{aref("index.html" , "_self", "Index" )}</TD>
+ <TD class="navigation-enabled">{aref("help.html" , "_self", "Help" )}</TD>
+ </TR></TABLE>
+ </TD>
+ <TD align="right" valign="top" style="white-space:nowrap;" rowspan="2">
+ {div0("Scala 2")}
+ </TD>
+ </TR>
+ <TR><TD></TD></TR>
+ </TABLE>;
+
+ def header0 : NodeSeq = <SPAN>
+ <HR/> in {aref(urlFor(clazz.tree.symbol.owner), "_self", clazz.tree.symbol.owner.fullNameString('.'))}
+ <DIV class="entity">
+ {Text(codeFor(kind))}
+ <SPAN class="entity">{Text(clazz.tree.symbol.nameString)}</SPAN>
+ </DIV><HR/>
+ </SPAN>;
+
+
+
+
+ }
+
+ def process(units : Iterator[CompilationUnit]) : Unit = {
var members = emptyMap;
- var topLevel = ListMap.Empty[ModuleSymbol,ListMap[Kind,TreeSet[HasTree]]];
+
+ var topLevel = ListMap.Empty[ModuleClassSymbol,ListMap[Kind,TreeSet[HasTree]]];
for (val unit <- units) {
val sourceMod = new SourceMod(unit);
for (val mmbr <- sourceMod.members) mmbr.tree match {
case cdef: ImplDef =>
assert(cdef.symbol.owner != NoSymbol);
- val sym = cdef.symbol.owner.asInstanceOf[ModuleSymbol];
+ val sym = cdef.symbol.owner.asInstanceOf[ModuleClassSymbol];
if (!topLevel.contains(sym)) topLevel = topLevel.update(sym, emptyMap);
topLevel = topLevel.update(sym, organize0(mmbr, topLevel(sym)));
case _ => throw new Error("unknown: " + mmbr.tree + " " + mmbr.tree.getClass());
}
}
- var packages = new TreeMap[String,ModuleSymbol];
- for (val top <- topLevel.elements)
- packages = packages.insert(top._1.fullNameString, top._1);
-
-
- val packageFrame = <HTML>
- <HEAD><TITLE>List of modules</TITLE>
- {header}
- </HEAD>
- <body>
- <div class="doctitle-larger">
- Scala<br/>2.0
- </div>
- <a href="module-page.html" target="classesFrame">All objects and classes</a><p/>
- <b>Modules</b>
- <table class="list">
- <tr>
- <td style="white-space:nowrap;">
- { {
- for (val top <- packages.elements)
- yield urlFor0(top._2, "classesFrame");
- } }
- </td></tr></table></body></HTML>;
-
-
- System.out.println("PACKAGE_FRAME");
- System.out.println(packageFrame);
-
-
- // output HTML for each package.
+
+ val modules0 = {
+ var modules0 = new TreeMap[String,ModuleClassSymbol];
+ for (val top <- topLevel.elements)
+ modules0 = modules0.insert(top._1.fullNameString, top._1);
+ modules0;
+ };
+
+ new ListModuleFrame {
+ def modules = modules0;
+ };
+ new ListModuleContentFrame {
+ def modules = modules0;
+ };
+
+ new ListClassFrame {
+ def classes = {
+ var allClasses = emptyMap;
+ for (val top <- topLevel.elements)
+ allClasses = merge(allClasses, top._2);
+ allClasses;
+ }
+ def title = "List of all classes and objects";
+ def path = "all-classes";
+ def navLabel = "root-page";
+ };
+ // class from for each module.
for (val top <- topLevel.elements) {
- val sym = top._1;
+ val module = top._1;
val members = top._2;
- val index = {process(members, sym)};
-
- System.out.println("SAVE-TO: " + sym);
- System.out.println(index);
+ new ListClassFrame {
+ def title = "List of classes and objects in package " + module.fullNameString('.');
+ def classes = top._2;
+ def path = module.fullNameString('/');
+ def navLabel = module.fullNameString('.');
+ };
+ val module0 = module;
+ new ListClassContentFrame {
+ def classes = top._2;
+ def module = module0;
+ };
+
+ // do root frame for each class and object
+ for (val kind <- members.elements) for (val mmbr <- kind._2.toList) {
+ val kind0 = kind._1;
+ new ContentFrame {
+ def title = labelFor(kind0) + " " + mmbr.tree.symbol.nameString + " in package " + mmbr.tree.symbol.owner.fullNameString('.');
+ def clazz = mmbr.asInstanceOf[ImplMod];
+ def kind = kind0;
+ }
+ }
}
- }
- def nameFor0(sym : Symbol) : NodeSeq = Text(sym.fullNameString);
- def urlFor0(sym : Symbol, target : String) : NodeSeq =
- <a href={urlFor1(sym)} target={("\"" + target + "\"")}>{nameFor0(sym)}</a><br/>;
+ new Frame {
+ def title = "Scala Library Documentation";
+ def body = index;
+ def path = "index";
+ override def hasBody = false;
+ };
- def urlFor2(sym : Symbol) : String = {
- if (sym == sym.toplevelClass) "";
- else {
- val rest = urlFor2(sym.owner);
- val rest0 = if (rest.equals("")) rest else rest + ".";
- rest0 + sym.nameString;
- }
}
- def urlFor1(sym : Symbol) : String = sym match {
- case _ : ModuleSymbol =>
- ("\"" + sym.fullNameString('/') + "/module-page.html\"");
- case csym : ClassSymbol =>
- if (csym == csym.toplevelClass)
- "\"" + csym.fullNameString('/') + "-class-page.html\"";
- else urlFor1(sym.toplevelClass) + "#" + urlFor2(sym);
- case _ => urlFor1(sym.toplevelClass) + "#" + urlFor2(sym);
- }
- def process(members : ListMap[Kind,TreeSet[HasTree]], sym : Symbol) : NodeSeq = {
- for (val kind <- KINDS; members.contains(kind)) yield process(kind, members(kind), sym);
-
- urlFor0(sym, "classesFrame");
-
- };
-
def organize(c : Composite, map0 : ListMap[Kind,TreeSet[HasTree]]) = {
var map = map0;
+ //System.err.println("MEMBERS: " + c.members.toList);
for (val mmbr <- c.members.toList) map = organize0(mmbr, map);
map;
}
@@ -122,188 +471,83 @@ abstract class DocGenerator extends Models {
var map = map0;
if (!map.contains(mmbr.kind))
map = map.update(mmbr.kind, new TreeSet[HasTree]);
+ val sz = map(mmbr.kind).size;
map = map.update(mmbr.kind, map(mmbr.kind) + mmbr);
+ if (map(mmbr.kind).size == sz)
+ System.err.println(""+mmbr + " not added");
map;
}
- def process(kind : Kind, set : TreeSet[HasTree], sym : Symbol) : Node = {
- val ret =
- <table cellpadding="3" class="member">
- <tr>
- <td colspan="2" class="title">
- { { labelFor(kind) + " Summary"; } }
- </td>
- </tr>
- { { for (val mmbr <- set.toList) yield process(mmbr, sym); } }
- </table>;
- ret;
- }
- def stringsFor(mods : Modifiers) = {
- var modString : List[String] = Nil;
- if (mods . isPrivate ) modString = "private" :: modString;
- if (mods . isProtected) modString = "protected" :: modString;
- if (mods . isOverride ) modString = "override" :: modString;
- if (mods . isAbstract ) modString = "abstract" :: modString;
- if (mods . isCase ) modString = "case" :: modString;
- if (mods . isSealed ) modString = "sealed" :: modString;
- if (mods . isFinal ) modString = "final" :: modString;
- if (mods . isMixin ) modString = "mixin" :: modString;
- modString;
- }
-
-
- def targetFor(ht : HasTree) : String = {
- // compute a URL.
- "Foo";
- }
- def forSymbol(symbol : Symbol, tpe : Type) : NodeSeq =
- Text(symbol.nameString);
-
-
- def ifT (cond : Boolean, nodes : NodeSeq) = if (cond) nodes else NodeSeq.Empty;
- def ifT (tree : Tree, nodes : NodeSeq, before : Boolean) =
- if (tree != EmptyTree) {
- if (before) nodes.concat(forTree(tree));
- else forTree(tree).concat(nodes);
- } else NodeSeq.Empty;
-
- def forTree(tree : Tree) : NodeSeq = tree match {
- case vdef : ValDef =>
- Text(vdef.symbol.name.toString()).concat(Text(" : ")).concat(forTree(vdef.tpt));
- case id : Ident => forSymbol(id.symbol, id.tpe);
- case sel : Select => forTree(sel.qualifier).concat(forSymbol(sel.symbol, sel.tpe));
- case tree : AbsTypeDef =>
- ifT(tree.lo, Text(" <: "), true).
- concat(forSymbol(tree.symbol, tree.tpe)).concat(ifT(tree.hi, Text(" <: "), false));
- case EmptyTree => NodeSeq.Empty;
- case _ => Text("XX=" + tree.toString());
- }
- def forTrees(trees : List[Tree]) : NodeSeq = {
- if (trees.isEmpty) NodeSeq.Empty;
- else {
- val head = forTree(trees.head);
- head.concat(if (trees.tail.isEmpty) NodeSeq.Empty else Text(", ")).concat(forTrees(trees.tail));
- }
- }
-
- def surround(open : String, close : String, node : NodeSeq) : NodeSeq =
- Text(open).concat(node).concat(Text(close));
+ def parse(str : String) : NodeSeq = {
+ new SpecialNode {
+ def label = "#PCDATA";
+ def toString(sb:StringBuffer): StringBuffer = {
+ sb.append(str.trim());
+ sb;
+ }
- def typesFor(ht : HasTree) : NodeSeq = {
- val tparams = ht.tree match {
- case cdef : ClassDef => cdef.tparams;
- case ddef : DefDef => ddef.tparams;
- case adef : AliasTypeDef => adef.tparams;
- case _ => Nil;
}
- if (tparams.isEmpty) Text("");
- else surround("[", "]", forTrees(tparams));
- }
- def argsFor(ht : HasTree) : NodeSeq = ht.tree match {
- case ddef : DefDef =>
- val nodes = for (val vparams <- ddef.vparamss)
- yield surround("(", ")", forTrees(vparams));
- nodes.flatMap(x => x.asList);
- case _ => NodeSeq.Empty;
- }
- def urlFor(ht : HasTree) : String =
- "\"unknown\"";
-
- def nameFor(ht : HasTree) : NodeSeq =
- <A href={ urlFor(ht) } target="_self"> { forSymbol(ht.tree.symbol, null) } </A>;
-
- def resultFor(ht : HasTree) : NodeSeq = ht.tree match {
- case vdef : ValOrDefDef =>
- Text(" : ").concat(forTree(vdef.tpt));
- case cdef : ImplDef =>
- if (cdef.impl.parents.isEmpty) NodeSeq.Empty;
- else Text("extends ").concat(forTrees(cdef.impl.parents));
- case _ => NodeSeq.Empty;
- }
+ /*
+ import java.io.StringReader;
+ import org.xml.sax.InputSource;
+ val isrc1 = new InputSource(new StringReader(str));
+ val parsedxml1 = XML.load(isrc1);
+ if (parsedxml1 == null) Text("BAD_COMMENT???");
+ else parsedxml1;
+ */
+ };
def comment(comment : String, isShort : Boolean) : NodeSeq = {
var ret : List[Node] = Nil;
- if (comment != null) {
- // strip out any stars.
- var comment0 = comment.trim();
- assert(comment0.startsWith(JDOC_START));
- comment0 = comment0.substring(JDOC_START.length());
- assert(comment0.endsWith(JDOC_END));
- comment0 = comment0.substring(0, comment0.length() - JDOC_END.length());
- var idx = 0;
- while (idx != -1) {
- idx = comment0.indexOf('*', idx);
- if (idx != -1)
- comment0 = comment0.substring(0, idx) +
- comment0.substring(idx + 1, comment0.length());
- }
- val tokenizer = new java.util.StringTokenizer(comment0, "@");
- val body = tokenizer.nextToken();
-
- var attributes : List[String] = Nil;
- if (!isShort) while (tokenizer.hasMoreElements())
- attributes = attributes ::: (tokenizer.nextToken() :: Nil);
- val node = <BR> { Text(comment) } </BR>;
- val nodes = { { for (val a <- attributes) yield <BR> { Text(a) } </BR>; } };
- val nodes0 : NodeSeq = node :: nodes;
- nodes0;
- } else NodeSeq.Empty;
+ assert(comment != null);
+ // strip out any stars.
+ var comment0 = comment.trim();
+ assert(comment0.startsWith(JDOC_START));
+ comment0 = comment0.substring(JDOC_START.length());
+ assert(comment0.endsWith(JDOC_END));
+ comment0 = comment0.substring(0, comment0.length() - JDOC_END.length());
+ var idx = 0;
+ while (idx != -1) {
+ idx = comment0.indexOf('*', idx);
+ if (idx != -1)
+ comment0 = comment0.substring(0, idx) +
+ comment0.substring(idx + 1, comment0.length());
+ }
+ val tokenizer = new java.util.StringTokenizer(comment0, "@");
+ val body = tokenizer.nextToken();
+ var attributes : List[Tuple2[String,String]] = Nil;
+ if (!isShort) while (tokenizer.hasMoreElements()) {
+ val attr = tokenizer.nextToken();
+ val div = attr.indexOf(' ');
+ val tuple = if (div == -1) new Tuple2(attr,"");
+ else new Tuple2(attr.substring(0, div), attr.substring(div + 1, attr.length()));
+ attributes = attributes ::: (tuple :: Nil);
+ }
+ if (isShort) <SPAN>{parse(body)}</SPAN>;
+ else <SPAN><DL><DD>{parse(body)}</DD></DL><DL>
+ { {
+ for (val attr <- attributes) yield
+ <DT style="margin-top:10px;"><B>{Text(attr._1 + ":")}</B></DT>
+ <DD>{(parse(attr._2))}</DD>;
+ } } </DL></SPAN>;
};
- def process(ht : HasTree, sym : Symbol) : scala.xml.Node = {
- val comment0 = if (comments.contains(ht.tree.symbol))
- comments(ht.tree.symbol) else null;
-
-
- val index =
- <tr>
- <td valign="top" class="modifiers">
- &nbsp;
- <code>{ {
- (for (val mod <- stringsFor(ht.mods)) yield Text(mod + " "));
- } }</code>
- </td>
- <td class="signature">
- <code>
- { labelFor(ht.kind).toLowerCase() }
- { nameFor(ht) }
- { typesFor(ht) }
- { argsFor(ht) }
- {resultFor(ht) }
- </code>
- { comment(comment0, true) }
- </td>
- </tr>;
-
- val body = <X></X>;
-
-
-
-
+ val index = {
+ <frameset cols="25%, 75%">
+ <frameset rows="50%, 50%">
+ <frame src="modules.html" name={modulesFrame}></frame>
+ <frame src="all-classes.html" name={classesFrame}></frame>
+ </frameset>
+ <frame src="root-content.html" name={contentFrame}></frame>
+ </frameset>;
+ }
+ val root = <b></b>;
- index;
- }
-/*
- val index0 = <HTML>
- <HEAD><TITLE>API Documentation</TITLE></HEAD>
- <FRAMESET cols="20%,80%" title="" onLoad="top.loadFrames()">
- <FRAMESET rows="30%,70%" title="" onLoad="top.loadFrames()">
- <FRAME src="overview-frame.html" name="packageListFrame" title="All Packages">
- <FRAME src="allclasses-frame.html" name="packageFrame" title="All classes and interfaces (except non-static nested types)">
- </FRAMESET>
- <FRAME src="overview-summary.html" name="classFrame" title="Package, class and interface descriptions" scrolling="yes">
- </FRAMESET>
- </HTML>;
-*/
-
- def index = {
- val index0 = <HTML></HTML>;
- }
private val JDOC_START = "/**";
private val JDOC_END = "*/";
diff --git a/src/compiler/scala/tools/nsc/doc/DocUtil.scala b/src/compiler/scala/tools/nsc/doc/DocUtil.scala
new file mode 100644
index 0000000000..baadcab546
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/doc/DocUtil.scala
@@ -0,0 +1,82 @@
+package scala.tools.nsc.doc;
+
+import scala.xml._;
+import scala.collection.immutable._;
+import java.io._;
+
+object DocUtil {
+
+ def dquote(str : String) : NodeSeq = {
+ DQUOTE :: Text(str) :: DQUOTE :: Nil;
+
+ }
+ object DQUOTE extends SpecialNode {
+
+ def toString(sb:StringBuffer) = {
+ sb.append("\""); sb;
+ }
+ def label = "#PCDATA";
+
+ }
+ def br(nodes: NodeSeq) : NodeSeq = {
+ val x = <BR/>;
+ nodes.concat(x);
+ }
+
+
+
+
+
+ mixin abstract class UrlContext {
+
+ def relative : String;
+ def aref(href0: String, target : String, text : String) : NodeSeq = {
+ val href = Utility.escape(href0);
+ if (target.indexOf('<') != -1) throw new Error(target);
+
+ val t0 = Text(text);
+
+ <a href={(relative + href)} target={(target)}>{t0}</a>;
+ }
+ val header = <meta http-equiv="content-type" content="text/html; charset=iso-8859-1"/>
+ <meta name="generator" content="scaladoc (1.4.0.4)"/>
+ <link rel="stylesheet" type="text/css" href={ relative + "style.css" }/>
+ <script type="text/javascript" src={relative + "script.js"}></script>;
+
+ def body0(hasBody : Boolean, nodes : NodeSeq) : NodeSeq =
+ if (!hasBody) nodes; else <BODY>{nodes}</BODY>;
+
+ val dtype = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">";
+
+ def page(title : String, body : NodeSeq, hasBody : Boolean) : NodeSeq =
+ <HTML>
+ <HEAD><TITLE>{Text(title)}</TITLE>
+ {header}
+ </HEAD>
+ {body0(hasBody, body)}
+ </HTML>;
+
+ }
+
+
+
+
+ def div0(title : String) : NodeSeq = <DIV class="doctitle-larger">{Text(title)}</DIV>;
+
+
+ def merge[T](ts0 : TreeSet[T], ts1 : TreeSet[T]): TreeSet[T] = {
+ var ts = ts0;
+ for (val t <- ts1.toList) ts = ts + t;
+ ts;
+ }
+ def merge[T,S <: Ordered[S]](ts0 : ListMap[T,TreeSet[S]], ts1 : ListMap[T,TreeSet[S]]):
+ ListMap[T,TreeSet[S]] = {
+ var ts = ts0;
+ for (val t <- ts1.elements) {
+ if (!ts.contains(t._1))
+ ts = ts.update(t._1, new TreeSet[S]);
+ ts = ts.update(t._1, merge(ts(t._1), t._2));
+ }
+ ts;
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/models/Models.scala b/src/compiler/scala/tools/nsc/models/Models.scala
index 0dce3da399..9f2eca3164 100644
--- a/src/compiler/scala/tools/nsc/models/Models.scala
+++ b/src/compiler/scala/tools/nsc/models/Models.scala
@@ -12,7 +12,10 @@ abstract class Models {
val global : Global;
import global._;
+ def acceptPrivate = true;
+
abstract class Kind {}
+ object CONSTRUCTOR extends Kind;
object OBJECT extends Kind;
object CLASS extends Kind;
object TRAIT extends Kind;
@@ -22,7 +25,7 @@ abstract class Models {
object ARG extends Kind;
object TPARAM extends Kind;
- val KINDS = TPARAM :: CLASS :: TRAIT :: OBJECT :: VAL :: VAR :: DEF :: Nil;
+ val KINDS = CONSTRUCTOR :: TPARAM :: CLASS :: TRAIT :: OBJECT :: VAL :: VAR :: DEF :: Nil;
def labelFor(kind : Kind) : String = kind match {
case OBJECT => "Object";
@@ -33,13 +36,39 @@ abstract class Models {
case VAR => "Var";
case ARG => "Arg";
case TPARAM => "Type";
+ case CONSTRUCTOR => "Constructor";
};
+ def stringsFor(mods : Modifiers) = {
+ var modString : List[String] = Nil;
+ if (mods . isPrivate ) modString = "private" :: modString;
+ if (mods . isProtected) modString = "protected" :: modString;
+ if (mods . isOverride ) modString = "override" :: modString;
+ if (mods . isAbstract ) modString = "abstract" :: modString;
+ if (mods . isCase ) modString = "case" :: modString;
+ if (mods . isSealed ) modString = "sealed" :: modString;
+ if (mods . isFinal ) modString = "final" :: modString;
+ if (mods . isMixin ) modString = "mixin" :: modString;
+ modString;
+ }
+ def codeFor(kind : Kind) : String = kind match {
+ case CONSTRUCTOR => codeFor(DEF);
+ case _ => labelFor(kind).toLowerCase();
+ }
+
+ def pluralFor(kind : Kind) : String = kind match {
+ case CLASS => "Classes";
+ case _ => labelFor(kind) + "s";
+ };
+
def kindOf(term0 : Symbol) = {
if (term0.isVariable) VAR;
else if (term0.isValueParameter) ARG;
- else if (term0.isMethod) DEF;
+ else if (term0.isMethod) {
+ if (term0.nameString.equals("this")) CONSTRUCTOR;
+ else DEF;
+ }
else if (term0.isClass) CLASS;
else if (term0.isModule) OBJECT;
else if (term0.isValue ) VAL;
@@ -90,6 +119,10 @@ abstract class Models {
ret;
}
+ def mods1(tree: Tree) = tree match {
+ case mdef : MemberDef => mdef.mods
+ case _ => NoMods
+ }
abstract class HasTree(val parent : Composite) extends Model with Ordered[HasTree] {
var tree : Tree = _;
@@ -99,39 +132,20 @@ abstract class Models {
}
def replacedBy(tree0 : Tree) : Boolean = true;
def text : String = textFor(tree);
- def mods = tree match {
- case mdef : MemberDef => mdef.mods
- case _ => NoMods
- }
+ var mods0 = NoMods;
- override def toString() : String = tree.symbol.toString();
+ def mods = if (mods0 != NoMods) mods0 else mods1(tree);
+
+ override def toString() : String = tree.toString();
def compareTo [b >: HasTree <% Ordered[b]](that: b): Int = that match {
- case ht : HasTree => toString().compareTo(ht.toString());
+ case ht : HasTree =>
+ val result = tree.symbol.nameString.compareTo(ht.tree.symbol.nameString);
+ if (result != 0) result;
+ else toString().compareTo(ht.toString());
}
def kind = kindOf(tree.symbol);
-
- /*
- public static String getText(Trees$Tree tree) {
- if (tree instanceof Trees$ValOrDefDef) {
- if (tree instanceof Trees$DefDef) {
- Trees$DefDef ddef = (Trees$DefDef) tree;
- ret += (listToString(ddef.tparams(), "[", "]", TYPE_E2S) +
- listToString(ddef.vparamss(), "", "", PARAM_E2S));
- }
- Trees$ValOrDefDef vdef = (Trees$ValOrDefDef) tree;
- ret += (" : " + vdef.tpt());
- }
- if (tree instanceof Trees$AbsTypeDef) {
- Trees$AbsTypeDef tp = (Trees$AbsTypeDef) tree;
- ret += (tp.hi() != null ? " <: " + tp.hi() : "") +
- (tp.lo() != null ? " >: " + tp.lo() : "");
- }
- return ret;
- }*/
-
-
override def add(from : Composite, model : HasTree) : Unit = { parent.add(from, model); }
override def remove(from : Composite, model : HasTree) : Unit = { parent.remove(from, model); }
}
@@ -189,6 +203,8 @@ abstract class Models {
if (mmbr2 != null) {
var found = false;
for (val mmbr <- members) if (!found && mmbr.replacedBy(mmbr2)) {
+ //System.err.println("REPLACE: " + mmbr + " with " + mmbr2);
+ mmbr.mods0 = mods1(mmbr1);
found = true;
updated = mmbr.update(mmbr2) || updated;
marked += mmbr;
@@ -197,7 +213,9 @@ abstract class Models {
updated = true;
val add = modelFor(mmbr2, this);
add.update(mmbr2);
+ val sz = members.size;
members += (add);
+ assert(members.size == sz + 1);
marked += add;
}
}
@@ -217,7 +235,7 @@ abstract class Models {
override def replacedBy(tree0 : Tree) : Boolean = if (super.replacedBy(tree0) && tree0.isInstanceOf[MemberDef]) {
val tree1 = tree0.asInstanceOf[MemberDef];
- treex.name == tree1.name;
+ treex.toString().equals(tree1.toString());
} else false;
override def update(tree0 : Tree): Boolean = {
@@ -262,7 +280,8 @@ abstract class Models {
def treey = tree.asInstanceOf[ImplDef];
override def replacedBy(tree0 : Tree) : Boolean = (super.replacedBy(tree0) && tree0.isInstanceOf[ImplDef]);
override def isMember(tree : Tree) : Boolean = (super.isMember(tree) ||
- (tree.isInstanceOf[ValOrDefDef]
+ (tree.isInstanceOf[ValOrDefDef] &&
+ (acceptPrivate || !tree.asInstanceOf[ValOrDefDef].mods.isPrivate)
/* && !tree.asInstanceOf[ValOrDefDef].mods.isPrivate */
/* && !tree.asInstanceOf[ValOrDefDef].mods.isAccessor */) ||
tree.isInstanceOf[AliasTypeDef]);
@@ -273,13 +292,18 @@ abstract class Models {
val ddef = tree.asInstanceOf[DefDef];
if (ddef.mods.isAccessor && ddef.symbol != null) {
val sym = ddef.symbol.accessed;
- val ret = for (val member <- members; member.symbol == sym) yield member;
+ val ret = for (val member <- members; member.symbol == sym) yield {
+ member;
+ }
if (ret.isEmpty) null;
else ret.head;
} else tree;
} else super.member(tree, members);
+
def sym = tree0.symbol;
if (tree0 == null || sym.pos == Position.NOPOS) null;
+ else if (!acceptPrivate &&
+ tree0.isInstanceOf[ValOrDefDef] && tree0.asInstanceOf[ValOrDefDef].mods.isPrivate) null;
else tree0;
}
@@ -314,11 +338,10 @@ abstract class Models {
case pdef : PackageDef => update0(pdef.stats);
case _ =>
}
+
override def add(from : Composite, model : HasTree) : Unit = if (listener != null) { listener.add(from, model); }
override def remove(from : Composite, model : HasTree) : Unit = if (listener != null) { listener.remove(from, model); }
- private val foo = 10;
-
override def isMember(tree : Tree) : Boolean = super.isMember(tree) || tree.isInstanceOf[Import];
}
diff --git a/src/compiler/scala/tools/nsc/models/SemanticTokens.scala b/src/compiler/scala/tools/nsc/models/SemanticTokens.scala
index c8081a3a7b..e59e5b61f0 100644
--- a/src/compiler/scala/tools/nsc/models/SemanticTokens.scala
+++ b/src/compiler/scala/tools/nsc/models/SemanticTokens.scala
@@ -231,7 +231,7 @@ class SemanticTokens(val compiler: Global) {
try {
//TPT=scala.Iterator[DocGenerator.this.compiler0.CompilationUnit] 260 class scala.tools.nsc.ast.Trees$TypeTree scala.Iterator[DocGenerator.this.compiler0.CompilationUnit] class scala.tools.nsc.symtab.Types$$anon$5
if (tree.tpt == null || tree.tpt.tpe == null) {
- System.err.println("BAD: " + tree.tpt + " in " + tree);
+ //System.err.println("BAD: " + tree.tpt + " in " + tree);
} else {
//System.err.println("TPT=" + tree.tpt + " " + tree.tpt.pos + " " + tree.tpt.getClass() + " " + tree.tpt.tpe + " " + tree.tpt.tpe.getClass() + " " + tree.tpt.tpe.getClass().getSuperclass());
build(tree.tpt);
diff --git a/src/compiler/scala/tools/nsc/util/NameTransformer.scala b/src/compiler/scala/tools/nsc/util/NameTransformer.scala
index 5fe825eb5e..ddf98f33f0 100644
--- a/src/compiler/scala/tools/nsc/util/NameTransformer.scala
+++ b/src/compiler/scala/tools/nsc/util/NameTransformer.scala
@@ -61,8 +61,10 @@ object NameTransformer {
}
/** Replace $op_name by corresponding operator symbol */
- def decode(name: String): String = {
+ def decode(name0: String): String = {
//System.out.println("decode: " + name);//DEBUG
+ val name = if (name0.endsWith("<init>")) name0.substring(0, name0.length() - ("<init>").length()) + "this";
+ else name0;
var buf: StringBuffer = null;
val len = name.length();
var i = 0;