Brian Slesinsky has submitted this change and it was merged.
Change subject: Super Dev Mode: deemphasize unused Java lines in
codeserver's UI
......................................................................
Super Dev Mode: deemphasize unused Java lines in codeserver's UI
Modified the code server to render Java files as HTML when browsing
interactively. (JavaScript debuggers continue to load the plain text.)
In the HTML, lines that make no contribution to the JavaScript according
to the source map are greyed out.
This implementation is pretty inefficient since we parse the entire
source map on every html page load, but it seems to suffice.
Change-Id: Id16bcd2287630cebf7afb8bbbe0a198b28f5ada9
Review-Link: https://gwt-review.googlesource.com/#/c/3600/
---
M dev/codeserver/java/com/google/gwt/dev/codeserver/HtmlWriter.java
A dev/codeserver/java/com/google/gwt/dev/codeserver/ReverseSourceMap.java
M dev/codeserver/java/com/google/gwt/dev/codeserver/SourceHandler.java
M dev/codeserver/java/com/google/gwt/dev/codeserver/SourceMap.java
4 files changed, 142 insertions(+), 15 deletions(-)
Approvals:
Matthew Dempsky: Looks good to me, approved
Leeroy Jenkins: Verified
diff --git
a/dev/codeserver/java/com/google/gwt/dev/codeserver/HtmlWriter.java
b/dev/codeserver/java/com/google/gwt/dev/codeserver/HtmlWriter.java
index 7cde2c4..ddf7db4 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/HtmlWriter.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/HtmlWriter.java
@@ -17,10 +17,12 @@
class HtmlWriter {
private static final Set<String> ALLOWED_TAGS =
Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
- "html", "head", "title", "style", "body", "pre", "span", "h1", "h2",
"a",
+ "html", "head", "title", "style",
+ "body", "h1", "h2", "h3", "h4", "h5", "h6", "a", "pre", "span",
"table", "tr", "td")));
private static final Set<String> ALLOWED_ATTS =
- Collections.unmodifiableSet(new
HashSet<String>(Arrays.asList("class=", "href=")));
+ Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
+ "class=", "href=")));
private final Writer out;
diff --git
a/dev/codeserver/java/com/google/gwt/dev/codeserver/ReverseSourceMap.java
b/dev/codeserver/java/com/google/gwt/dev/codeserver/ReverseSourceMap.java
new file mode 100644
index 0000000..a000d71
--- /dev/null
+++
b/dev/codeserver/java/com/google/gwt/dev/codeserver/ReverseSourceMap.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
not
+ * use this file except in compliance with the License. You may obtain a
copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+
+package com.google.gwt.dev.codeserver;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.dev.util.Util;
+import com.google.gwt.thirdparty.debugging.sourcemap.SourceMapConsumerV3;
+import
com.google.gwt.thirdparty.debugging.sourcemap.SourceMapParseException;
+
+/**
+ * A mapping from Java lines to JavaScript.
+ */
+class ReverseSourceMap {
+ private final SourceMapConsumerV3 consumer;
+
+ private ReverseSourceMap(SourceMapConsumerV3 consumer) {
+ this.consumer = consumer;
+ }
+
+ /**
+ * Reads a source map from disk and parses it into an in-memory
representation.
+ * If it can't be loaded, logs a warning and returns an empty source map.
+ */
+ static ReverseSourceMap load(TreeLogger logger, ModuleState moduleState)
{
+ SourceMapConsumerV3 consumer = new SourceMapConsumerV3();
+ String unparsed = Util.readFileAsString(moduleState.findSourceMap());
+ try {
+ consumer.parse(unparsed);
+ return new ReverseSourceMap(consumer);
+ } catch (SourceMapParseException e) {
+ logger.log(TreeLogger.WARN, "can't parse source map", e);
+ return new ReverseSourceMap(null);
+ }
+ }
+
+ /**
+ * Returns true if the given line in a Java file has any corresponding
JavaScript in
+ * the GWT compiler's output. (The source file's path is relative to the
source root directory
+ * where the GWT compiler found it.)
+ */
+ boolean appearsInJavaScript(String path, int lineNumber) {
+ // TODO: getReverseMapping() seems to be off by one (lines numbered
from zero). Why?
+ return consumer != null && !consumer.getReverseMapping(path,
lineNumber - 1, -1).isEmpty();
+ }
+}
diff --git
a/dev/codeserver/java/com/google/gwt/dev/codeserver/SourceHandler.java
b/dev/codeserver/java/com/google/gwt/dev/codeserver/SourceHandler.java
index e24755a..e3bc45b 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/SourceHandler.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/SourceHandler.java
@@ -20,8 +20,11 @@
import com.google.gwt.dev.json.JsonArray;
import com.google.gwt.dev.json.JsonObject;
+import java.io.BufferedReader;
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -83,7 +86,7 @@
sendSourceMap(moduleName, request, response);
} else if (rest.endsWith(".java")) {
- sendSourceFile(moduleName, rest, response);
+ sendSourceFile(moduleName, rest, request.getQueryString(), response);
} else {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
@@ -106,8 +109,7 @@
private void sendSourceMap(String moduleName, HttpServletRequest request,
HttpServletResponse response) throws IOException {
- ModuleState moduleState = modules.get(moduleName);
- SourceMap map = SourceMap.load(moduleState.findSourceMap());
+ SourceMap map = loadSourceMap(moduleName);
// hack: rewrite the source map so that each filename is a URL
String serverPrefix = String.format("http://%s:%d/sourcemaps/%s/",
request.getServerName(),
@@ -121,8 +123,7 @@
private void sendDirectoryListPage(String moduleName,
HttpServletResponse response)
throws IOException {
- ModuleState moduleState = modules.get(moduleName);
- SourceMap map = SourceMap.load(moduleState.findSourceMap());
+ SourceMap map = loadSourceMap(moduleName);
JsonObject config = new JsonObject();
config.put("moduleName", moduleName);
@@ -140,8 +141,7 @@
private void sendFileListPage(String moduleName, String rest,
HttpServletResponse response)
throws IOException {
- ModuleState moduleState = modules.get(moduleName);
- SourceMap map = SourceMap.load(moduleState.findSourceMap());
+ SourceMap map = loadSourceMap(moduleName);
JsonObject config = new JsonObject();
config.put("moduleName", moduleName);
@@ -150,24 +150,90 @@
for (String name : map.getSourceFilesInDirectory(rest)) {
JsonObject file = new JsonObject();
file.put("name", name);
- file.put("link", name);
+ file.put("link", name + "?html");
files.add(file);
}
config.put("files", files);
PageUtil.sendJsonAndHtml("config", config, "filelist.html", response,
logger);
}
- private void sendSourceFile(String moduleName, String rest,
HttpServletResponse response)
+ /**
+ * Sends an HTTP response containing a Java source. It will be sent as
plain text by default,
+ * or as HTML if the query string is equal to "html".
+ */
+ private void sendSourceFile(String moduleName, String sourcePath, String
query,
+ HttpServletResponse response)
throws IOException {
ModuleState moduleState = modules.get(moduleName);
- InputStream pageBytes = moduleState.openSourceFile(rest);
+ InputStream pageBytes = moduleState.openSourceFile(sourcePath);
if (pageBytes == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
- logger.log(TreeLogger.WARN, "unknown source file: " + rest);
+ logger.log(TreeLogger.WARN, "unknown source file: " + sourcePath);
return;
}
- PageUtil.sendStream("text/plain", pageBytes, response);
+ if (query != null && query.equals("html")) {
+ BufferedReader reader = new BufferedReader(new
InputStreamReader(pageBytes));
+ sendSourceFileAsHtml(moduleName, sourcePath, reader, response);
+ } else {
+ PageUtil.sendStream("text/plain", pageBytes, response);
+ }
+ }
+
+ /**
+ * Sends an HTTP response containing Java source rendered as HTML. The
lines of source
+ * that have corresponding JavaScript will be highlighted (as determined
by reading the
+ * source map).
+ */
+ private void sendSourceFileAsHtml(String moduleName, String sourcePath,
BufferedReader lines,
+ HttpServletResponse response) throws IOException {
+
+ ReverseSourceMap sourceMap = ReverseSourceMap.load(logger,
modules.get(moduleName));
+
+ File sourceFile = new File(sourcePath);
+
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.setContentType("text/html");
+
+ HtmlWriter out = new HtmlWriter(response.getWriter());
+ out.startTag("html").nl();
+ out.startTag("head").nl();
+ out.startTag("title").text(sourceFile.getName() + " (GWT Code
Server)").endTag("title").nl();
+ out.startTag("style").nl();
+ out.text(".unused { color: grey; }").nl();
+ out.text(".used { color: black; }").nl();
+ out.text(".title { margin-top: 0; }").nl();
+ out.endTag("style").nl();
+ out.endTag("head").nl();
+ out.startTag("body").nl();
+
+
out.startTag("a", "href=", ".").text(sourceFile.getParent()).endTag("a").nl();
+
out.startTag("h1", "class=", "title").text(sourceFile.getName()).endTag("h1").nl();
+
+ out.startTag("pre", "class=", "unused").nl();
+ try {
+ int lineNumber = 1;
+ for (String line = lines.readLine(); line != null; line =
lines.readLine()) {
+ if (sourceMap.appearsInJavaScript(sourcePath, lineNumber)) {
+
out.startTag("span", "class=", "used").text(line).endTag("span").nl();
+ } else {
+ out.text(line).nl();
+ }
+ lineNumber++;
+ }
+
+ } finally {
+ lines.close();
+ }
+ out.endTag("pre").nl();
+
+ out.endTag("body").nl();
+ out.endTag("html").nl();
+ }
+
+ private SourceMap loadSourceMap(String moduleName) {
+ ModuleState moduleState = modules.get(moduleName);
+ return SourceMap.load(moduleState.findSourceMap());
}
}
diff --git
a/dev/codeserver/java/com/google/gwt/dev/codeserver/SourceMap.java
b/dev/codeserver/java/com/google/gwt/dev/codeserver/SourceMap.java
index dd584b8..3c13186 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/SourceMap.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/SourceMap.java
@@ -93,7 +93,7 @@
/**
* Returns a sorted list of all filenames in the given directory.
*/
- public List<String> getSourceFilesInDirectory(String parent) {
+ List<String> getSourceFilesInDirectory(String parent) {
if (!parent.endsWith("/")) {
throw new IllegalArgumentException("unexpected: " + parent);
}
--
To view, visit https://gwt-review.googlesource.com/3600
To unsubscribe, visit https://gwt-review.googlesource.com/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: Id16bcd2287630cebf7afb8bbbe0a198b28f5ada9
Gerrit-PatchSet: 3
Gerrit-Project: gwt
Gerrit-Branch: master
Gerrit-Owner: Brian Slesinsky <[email protected]>
Gerrit-Reviewer: Brian Slesinsky <[email protected]>
Gerrit-Reviewer: John Stalcup <[email protected]>
Gerrit-Reviewer: Leeroy Jenkins <[email protected]>
Gerrit-Reviewer: Matthew Dempsky <[email protected]>
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors
---
You received this message because you are subscribed to the Google Groups "GWT Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.