| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 compilerIsolate(port) { | |
| 6 Runner runner = new Runner(); | |
| 7 runner.init(); | |
| 8 | |
| 9 port.receive((msg, replyTo) { | |
| 10 replyTo.send(runner.update(msg)); | |
| 11 }); | |
| 12 } | |
| 13 | |
| 14 main() { | |
| 15 html.document.query('#status').innerHTML = 'Initializing...'; | |
| 16 setOutline(msg) { | |
| 17 html.document.query('#out').innerHTML = msg; | |
| 18 } | |
| 19 final codeDiv = html.document.query('#code'); | |
| 20 final popup = html.document.query('#popup'); | |
| 21 | |
| 22 // The port for communicating with the compiler isolate. | |
| 23 var port = null; | |
| 24 | |
| 25 // Should be called when [codeDiv] has changed. For now, call it always | |
| 26 // on mouse up and key up events. | |
| 27 update() { | |
| 28 if (port === null) return; | |
| 29 port.call(codeDiv.text).then(setOutline); | |
| 30 } | |
| 31 | |
| 32 hide(Element e) { | |
| 33 e.style.visibility = 'hidden'; | |
| 34 } | |
| 35 | |
| 36 show(Element e) { | |
| 37 e.style.visibility = 'visible'; | |
| 38 } | |
| 39 | |
| 40 insertTextAtPoint(Event e, String newText) { | |
| 41 e.preventDefault(); | |
| 42 var selection = html.window.getSelection(); | |
| 43 var range = selection.getRangeAt(0); | |
| 44 var offset = range.startOffset; | |
| 45 Text text = codeDiv.nodes[0]; | |
| 46 text.insertData(offset, newText); | |
| 47 selection.setPosition(text, offset + newText.length); | |
| 48 } | |
| 49 | |
| 50 // This prevents creating a new div element when hitting enter. | |
| 51 codeDiv.on.keyPress.add((Event e) { | |
| 52 if (e.keyIdentifier == 'Enter') { | |
| 53 // TODO(ahe): Is 'Enter' portable? | |
| 54 insertTextAtPoint(e, '\n'); | |
| 55 } | |
| 56 }); | |
| 57 | |
| 58 // Override tab key. | |
| 59 codeDiv.on.keyDown.add((Event e) { | |
| 60 if (e.keyIdentifier == 'U+0009') { | |
| 61 // TODO(ahe): Find better way to detect tab key. | |
| 62 insertTextAtPoint(e, ' '); | |
| 63 } | |
| 64 }); | |
| 65 | |
| 66 // Called on keyUp and mouseUp to display a marker at the current | |
| 67 // insertion point (the selection's first range). This probably | |
| 68 // needs more work before it works really well, but seems to be good | |
| 69 // enough for now. | |
| 70 handleUp(Event e) { | |
| 71 var selection = html.window.getSelection(); | |
| 72 var range = selection.getRangeAt(0); | |
| 73 var rects = range.getClientRects(); | |
| 74 if (rects.length < 1) { | |
| 75 hide(popup); | |
| 76 return; | |
| 77 } | |
| 78 html.ClientRect rect = rects.item(rects.length - 1); | |
| 79 if (rect.width.toInt() != 0) { | |
| 80 // This is a selection of multiple characters, not a single | |
| 81 // point of insertion. | |
| 82 hide(popup); | |
| 83 return; | |
| 84 } | |
| 85 popup.style.top = "${rect.bottom.toInt()}px"; | |
| 86 popup.style.left = "${rect.right.toInt()}px"; | |
| 87 // Instead of displaying this immediately, we could set up a timer | |
| 88 // event and display it later. This is a matter of getting the UX | |
| 89 // just right, for now it simply demonstrates that we know where | |
| 90 // the insertion point is (in pixels) and what the character | |
| 91 // offset is. | |
| 92 show(popup); | |
| 93 popup.text = '''Code completion here... | |
| 94 Current character offset: ${range.startOffset}'''; | |
| 95 // TODO(ahe): Better detection of when [codeDiv] has changed. | |
| 96 update(); | |
| 97 } | |
| 98 | |
| 99 codeDiv.on.keyUp.add(handleUp); | |
| 100 codeDiv.on.mouseUp.add(handleUp); | |
| 101 | |
| 102 codeDiv.on.blur.add((Event e) => hide(popup)); | |
| 103 | |
| 104 // The event handlers are now set up. Allow editing. | |
| 105 codeDiv.contentEditable = "true"; | |
| 106 | |
| 107 // Creates a compiler isolate in its own iframe. This should prevent | |
| 108 // the compiler from blocking the UI. | |
| 109 spawnDomIsolate(html.document.query('#isolate').contentWindow, | |
| 110 'compilerIsolate').then((sendPort) { | |
| 111 // The compiler isolate is now ready to talk. Store the port so | |
| 112 // that the update function starts requesting outlines whenever | |
| 113 // [codeDiv] changes. | |
| 114 port = sendPort; | |
| 115 // Make sure that we get an initial outline. | |
| 116 update(); | |
| 117 html.document.query('#status').innerHTML = 'Ready'; | |
| 118 }); | |
| 119 } | |
| 120 | |
| 121 class Runner { | |
| 122 final LeapCompiler compiler; | |
| 123 | |
| 124 Runner() : compiler = new LeapCompiler(); | |
| 125 | |
| 126 String init() { | |
| 127 Stopwatch sw = new Stopwatch()..start(); | |
| 128 compiler.scanBuiltinLibraries(); | |
| 129 sw.stop(); | |
| 130 return 'Scanned core libraries in ${sw.elapsedInMs()}ms'; | |
| 131 } | |
| 132 | |
| 133 String update(String codeText) { | |
| 134 StringBuffer sb = new StringBuffer(); | |
| 135 | |
| 136 Stopwatch sw = new Stopwatch()..start(); | |
| 137 | |
| 138 LibraryElement e = compile(new LeapScript(codeText)); | |
| 139 | |
| 140 void printFunction(FunctionElement fe, [String indentation = ""]) { | |
| 141 var paramAcc = []; | |
| 142 | |
| 143 FunctionType ft = fe.computeType(compiler); | |
| 144 | |
| 145 sb.add("<div>${indentation}"); | |
| 146 ft.returnType.name.printOn(sb); | |
| 147 sb.add(" "); | |
| 148 fe.name.printOn(sb); | |
| 149 sb.add("("); | |
| 150 ft.parameterTypes.printOn(sb, ", "); | |
| 151 sb.add(");</div>"); | |
| 152 | |
| 153 } | |
| 154 | |
| 155 void printField(FieldElement fe, [String indentation = ""]) { | |
| 156 sb.add("<div>${indentation}var "); | |
| 157 fe.name.printOn(sb); | |
| 158 sb.add(";</div>"); | |
| 159 } | |
| 160 | |
| 161 void printClass(ClassElement ce) { | |
| 162 ce.parseNode(compiler); | |
| 163 | |
| 164 sb.add("<div>class "); | |
| 165 ce.name.printOn(sb); | |
| 166 sb.add(" {"); | |
| 167 | |
| 168 for (Element e in ce.members.reverse()) { | |
| 169 switch(e.kind) { | |
| 170 case ElementKind.FUNCTION: | |
| 171 | |
| 172 printFunction(e, " "); | |
| 173 break; | |
| 174 | |
| 175 case ElementKind.FIELD: | |
| 176 | |
| 177 printField(e, " "); | |
| 178 break; | |
| 179 } | |
| 180 } | |
| 181 sb.add("}</div>"); | |
| 182 } | |
| 183 | |
| 184 for (Element c in e.topLevelElements.reverse()) { | |
| 185 switch (c.kind) { | |
| 186 case ElementKind.FUNCTION: | |
| 187 printFunction (c); | |
| 188 break; | |
| 189 | |
| 190 case ElementKind.CLASS: | |
| 191 printClass(c); | |
| 192 break; | |
| 193 | |
| 194 case ElementKind.FIELD: | |
| 195 printField (c); | |
| 196 break; | |
| 197 } | |
| 198 } | |
| 199 | |
| 200 compiler.log("Outline ${sw.elapsedInMs()}"); | |
| 201 return sb.toString(); | |
| 202 } | |
| 203 | |
| 204 Element compile(String script) { | |
| 205 return compiler.runSelective(script); | |
| 206 } | |
| 207 } | |
| 208 | |
| 209 class LeapCompiler extends Compiler { | |
| 210 HttpRequestCache cache; | |
| 211 | |
| 212 final bool throwOnError = false; | |
| 213 | |
| 214 final libDir = "../.."; | |
| 215 | |
| 216 LeapCompiler() : cache = new HttpRequestCache(), super() { | |
| 217 tasks = [scanner, dietParser, parser, resolver, checker]; | |
| 218 } | |
| 219 | |
| 220 void log(message) { print(message); } | |
| 221 | |
| 222 String get legDirectory => libDir; | |
| 223 | |
| 224 LibraryElement scanBuiltinLibrary(String path) { | |
| 225 Uri base = new Uri.fromString(html.window.location.toString()); | |
| 226 Uri libraryRoot = base.resolve(libDir); | |
| 227 Uri resolved = libraryRoot.resolve(DART2JS_LIBRARY_MAP[path]); | |
| 228 LibraryElement library = scanner.loadLibrary(resolved, null); | |
| 229 return library; | |
| 230 } | |
| 231 | |
| 232 currentScript() { | |
| 233 if (currentElement === null) return null; | |
| 234 CompilationUnitElement compilationUnit = | |
| 235 currentElement.getCompilationUnit(); | |
| 236 if (compilationUnit === null) return null; | |
| 237 return compilationUnit.script; | |
| 238 } | |
| 239 | |
| 240 Script readScript(Uri uri, [ScriptTag node]) { | |
| 241 String text = ""; | |
| 242 try { | |
| 243 text = cache.readAll(uri.path.toString()); | |
| 244 } catch (exception) { | |
| 245 cancel("${uri.path}: $exception", node: node); | |
| 246 } | |
| 247 SourceFile sourceFile = new SourceFile(uri.toString(), text); | |
| 248 return new Script(uri, sourceFile); | |
| 249 } | |
| 250 | |
| 251 reportWarning(Node node, var message) { | |
| 252 print(message); | |
| 253 } | |
| 254 | |
| 255 reportError(Node node, var message) { | |
| 256 cancel(message.toString(), node); | |
| 257 } | |
| 258 | |
| 259 void cancel([String reason, Node node, token, instruction, element]) { | |
| 260 print(reason); | |
| 261 } | |
| 262 | |
| 263 Element runSelective(Script script) { | |
| 264 Stopwatch sw = new Stopwatch()..start(); | |
| 265 Element e; | |
| 266 try { | |
| 267 e = runCompilerSelective(script); | |
| 268 } on CompilerCancelledException catch (exception) { | |
| 269 log(exception.toString()); | |
| 270 log('compilation failed'); | |
| 271 return null; | |
| 272 } | |
| 273 log('compilation succeeded: ${sw.elapsedInMs()}ms'); | |
| 274 return e; | |
| 275 } | |
| 276 | |
| 277 LibraryElement runCompilerSelective(Script script) { | |
| 278 mainApp = new LibraryElement(script); | |
| 279 | |
| 280 universe.libraries.remove(script.uri.toString()); | |
| 281 Element element; | |
| 282 withCurrentElement(mainApp, () { | |
| 283 scanner.scan(mainApp); | |
| 284 }); | |
| 285 return mainApp; | |
| 286 } | |
| 287 } | |
| OLD | NEW |