Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(155)

Side by Side Diff: dart/site/try/src/interaction_manager.dart

Issue 340343006: Send console messages through interaction manager. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge
Patch Set: Add comments. Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « dart/site/try/src/compilation.dart ('k') | dart/site/try/src/ui.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library trydart.interaction_manager; 5 library trydart.interaction_manager;
6 6
7 import 'dart:html'; 7 import 'dart:html';
8 8
9 import 'dart:convert' show 9 import 'dart:convert' show
10 JSON; 10 JSON;
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
73 import 'shadow_root.dart' show 73 import 'shadow_root.dart' show
74 getShadowRoot, 74 getShadowRoot,
75 getText, 75 getText,
76 removeShadowRootPolyfill, 76 removeShadowRootPolyfill,
77 setShadowRoot; 77 setShadowRoot;
78 78
79 const String TRY_DART_NEW_DEFECT = 79 const String TRY_DART_NEW_DEFECT =
80 'https://code.google.com/p/dart/issues/entry' 80 'https://code.google.com/p/dart/issues/entry'
81 '?template=Try+Dart+Internal+Error'; 81 '?template=Try+Dart+Internal+Error';
82 82
83 const Duration HEARTBEAT_INTERVAL = const Duration(milliseconds: 500); 83 /// How frequently [InteractionManager.onHeartbeat] is called.
84 const Duration HEARTBEAT_INTERVAL = const Duration(milliseconds: 50);
84 85
86 /// Determines how frequently "project" files are saved. The time is measured
87 /// from the time of last modification.
85 const Duration SAVE_INTERVAL = const Duration(seconds: 5); 88 const Duration SAVE_INTERVAL = const Duration(seconds: 5);
86 89
90 /// Determines how frequently the compiler is invoked. The time is measured
91 /// from the time of last modification.
87 const Duration COMPILE_INTERVAL = const Duration(seconds: 1); 92 const Duration COMPILE_INTERVAL = const Duration(seconds: 1);
88 93
94 /// Determines if a compilation is slow. The time is measured from the last
95 /// compilation started. If a compilation is slow, progress information is
96 /// displayed to the user, but the console is untouched if the compilation
97 /// finished quickly. The purpose is to reduce flicker in the UI.
98 const Duration SLOW_COMPILE = const Duration(seconds: 1);
99
89 /** 100 /**
90 * UI interaction manager for the entire application. 101 * UI interaction manager for the entire application.
91 */ 102 */
92 abstract class InteractionManager { 103 abstract class InteractionManager {
93 // Design note: All UI interactions go through one instance of this 104 // Design note: All UI interactions go through one instance of this
94 // class. This is by design. 105 // class. This is by design.
95 // 106 //
96 // Simplicity in UI is in the eye of the beholder, not the implementor. Great 107 // Simplicity in UI is in the eye of the beholder, not the implementor. Great
97 // 'natural UI' is usually achieved with substantial implementation 108 // 'natural UI' is usually achieved with substantial implementation
98 // complexity that doesn't modularize well and has nasty complicated state 109 // complexity that doesn't modularize well and has nasty complicated state
(...skipping 26 matching lines...) Expand all
125 void onCompilationUnitChanged(CompilationUnit unit); 136 void onCompilationUnitChanged(CompilationUnit unit);
126 137
127 Future<List<String>> projectFileNames(); 138 Future<List<String>> projectFileNames();
128 139
129 /// Called when the user selected a new project file. 140 /// Called when the user selected a new project file.
130 void onProjectFileSelected(String projectFile); 141 void onProjectFileSelected(String projectFile);
131 142
132 /// Called when notified about a project file changed (on the server). 143 /// Called when notified about a project file changed (on the server).
133 void onProjectFileFsEvent(MessageEvent e); 144 void onProjectFileFsEvent(MessageEvent e);
134 145
135 /// Called every 500ms. 146 /// Called every [HEARTBEAT_INTERVAL].
136 void onHeartbeat(Timer timer); 147 void onHeartbeat(Timer timer);
137 148
138 /// Called by [:window.onMessage.listen:]. 149 /// Called by [:window.onMessage.listen:].
139 void onMessage(MessageEvent event); 150 void onWindowMessage(MessageEvent event);
151
152 void onCompilationFailed();
140 153
141 void onCompilationDone(); 154 void onCompilationDone();
142 155
143 /// Called when a compilation is starting, but just before sending the 156 /// Called when a compilation is starting, but just before sending the
144 /// initiating message to the compiler isolate. 157 /// initiating message to the compiler isolate.
145 void compilationStarting(); 158 void compilationStarting();
159
160 // TODO(ahe): Remove this from InteractionManager, but not from InitialState.
161 void consolePrintLine(line);
162
163 void verboseCompilerMessage(String message);
146 } 164 }
147 165
148 /** 166 /**
149 * State machine for UI interactions. 167 * State machine for UI interactions.
150 */ 168 */
151 class InteractionContext extends InteractionManager { 169 class InteractionContext extends InteractionManager {
152 InteractionState state; 170 InteractionState state;
153 171
154 final Map<String, CompilationUnit> projectFiles = <String, CompilationUnit>{}; 172 final Map<String, CompilationUnit> projectFiles = <String, CompilationUnit>{};
155 173
156 final Set<CompilationUnit> modifiedUnits = new Set<CompilationUnit>(); 174 final Set<CompilationUnit> modifiedUnits = new Set<CompilationUnit>();
157 175
158 final Queue<CompilationUnit> unitsToSave = new Queue<CompilationUnit>(); 176 final Queue<CompilationUnit> unitsToSave = new Queue<CompilationUnit>();
159 177
178 /// Tracks time since last modification of a "project" file.
160 final Stopwatch saveTimer = new Stopwatch(); 179 final Stopwatch saveTimer = new Stopwatch();
161 180
181 /// Tracks time since last modification.
162 final Stopwatch compileTimer = new Stopwatch(); 182 final Stopwatch compileTimer = new Stopwatch();
163 183
184 /// Tracks elapsed time of current compilation.
185 final Stopwatch elapsedCompilationTime = new Stopwatch();
186
164 CompilationUnit currentCompilationUnit = 187 CompilationUnit currentCompilationUnit =
165 // TODO(ahe): Don't use a fake unit. 188 // TODO(ahe): Don't use a fake unit.
166 new CompilationUnit('fake', ''); 189 new CompilationUnit('fake', '');
167 190
168 Timer heartbeat; 191 Timer heartbeat;
169 192
170 Completer<String> completeSaveOperation; 193 Completer<String> completeSaveOperation;
171 194
195 bool shouldClearConsole = false;
196
197 Element compilerConsole;
198
199 bool isFirstCompile = true;
200
172 final Set<AnchorElement> oldDiagnostics = new Set<AnchorElement>(); 201 final Set<AnchorElement> oldDiagnostics = new Set<AnchorElement>();
173 202
174 InteractionContext() 203 InteractionContext()
175 : super.internal() { 204 : super.internal() {
176 state = new InitialState(this); 205 state = new InitialState(this);
177 heartbeat = new Timer.periodic(HEARTBEAT_INTERVAL, onHeartbeat); 206 heartbeat = new Timer.periodic(HEARTBEAT_INTERVAL, onHeartbeat);
178 } 207 }
179 208
180 void onInput(Event event) => state.onInput(event); 209 void onInput(Event event) => state.onInput(event);
181 210
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
222 void onProjectFileSelected(String projectFile) { 251 void onProjectFileSelected(String projectFile) {
223 return state.onProjectFileSelected(projectFile); 252 return state.onProjectFileSelected(projectFile);
224 } 253 }
225 254
226 void onProjectFileFsEvent(MessageEvent e) { 255 void onProjectFileFsEvent(MessageEvent e) {
227 return state.onProjectFileFsEvent(e); 256 return state.onProjectFileFsEvent(e);
228 } 257 }
229 258
230 void onHeartbeat(Timer timer) => state.onHeartbeat(timer); 259 void onHeartbeat(Timer timer) => state.onHeartbeat(timer);
231 260
232 void onMessage(MessageEvent event) => state.onMessage(event); 261 void onWindowMessage(MessageEvent event) => state.onWindowMessage(event);
262
263 void onCompilationFailed() => state.onCompilationFailed();
233 264
234 void onCompilationDone() => state.onCompilationDone(); 265 void onCompilationDone() => state.onCompilationDone();
235 266
236 void compilationStarting() => state.compilationStarting(); 267 void compilationStarting() => state.compilationStarting();
268
269 void consolePrintLine(line) => state.consolePrintLine(line);
270
271 void verboseCompilerMessage(String message) {
272 return state.verboseCompilerMessage(message);
273 }
237 } 274 }
238 275
239 abstract class InteractionState implements InteractionManager { 276 abstract class InteractionState implements InteractionManager {
240 InteractionContext get context; 277 InteractionContext get context;
241 278
242 // TODO(ahe): Remove this. 279 // TODO(ahe): Remove this.
243 Set<AnchorElement> get oldDiagnostics { 280 Set<AnchorElement> get oldDiagnostics {
244 throw 'Use context.oldDiagnostics instead'; 281 throw 'Use context.oldDiagnostics instead';
245 } 282 }
246 283
(...skipping 272 matching lines...) Expand 10 before | Expand all | Expand 10 after
519 saveUnits(); 556 saveUnits();
520 } 557 }
521 if (!settings.compilationPaused && 558 if (!settings.compilationPaused &&
522 context.compileTimer.elapsed > COMPILE_INTERVAL) { 559 context.compileTimer.elapsed > COMPILE_INTERVAL) {
523 if (startCompilation()) { 560 if (startCompilation()) {
524 context.compileTimer 561 context.compileTimer
525 ..stop() 562 ..stop()
526 ..reset(); 563 ..reset();
527 } 564 }
528 } 565 }
566
567 if (context.elapsedCompilationTime.elapsed > SLOW_COMPILE) {
568 if (context.compilerConsole.parent == null) {
569 outputDiv.append(context.compilerConsole);
570 }
571 }
529 } 572 }
530 573
531 void saveUnits() { 574 void saveUnits() {
532 if (context.unitsToSave.isEmpty) return; 575 if (context.unitsToSave.isEmpty) return;
533 CompilationUnit unit = context.unitsToSave.removeFirst(); 576 CompilationUnit unit = context.unitsToSave.removeFirst();
534 onError(ProgressEvent event) { 577 onError(ProgressEvent event) {
535 HttpRequest request = event.target; 578 HttpRequest request = event.target;
536 statusDiv.text = "Couldn't save '${unit.name}': ${request.responseText}"; 579 statusDiv.text = "Couldn't save '${unit.name}': ${request.responseText}";
537 context.completeSaveOperation.complete(unit.name); 580 context.completeSaveOperation.complete(unit.name);
538 } 581 }
539 new HttpRequest() 582 new HttpRequest()
540 ..open("POST", "/project/${unit.name}") 583 ..open("POST", "/project/${unit.name}")
541 ..onError.listen(onError) 584 ..onError.listen(onError)
542 ..send(unit.content); 585 ..send(unit.content);
543 void setupCompleter() { 586 void setupCompleter() {
544 context.completeSaveOperation = new Completer<String>.sync(); 587 context.completeSaveOperation = new Completer<String>.sync();
545 context.completeSaveOperation.future.then((String name) { 588 context.completeSaveOperation.future.then((String name) {
546 if (name == unit.name) { 589 if (name == unit.name) {
547 print("Saved source of '$name'"); 590 print("Saved source of '$name'");
548 saveUnits(); 591 saveUnits();
549 } else { 592 } else {
550 setupCompleter(); 593 setupCompleter();
551 } 594 }
552 }); 595 });
553 } 596 }
554 setupCompleter(); 597 setupCompleter();
555 } 598 }
556 599
557 void onMessage(MessageEvent event) { 600 void onWindowMessage(MessageEvent event) {
558 if (event.source is! WindowBase || event.source == window) { 601 if (event.source is! WindowBase || event.source == window) {
559 return onBadMessage(event); 602 return onBadMessage(event);
560 } 603 }
561 if (event.data is List) { 604 if (event.data is List) {
562 List message = event.data; 605 List message = event.data;
563 if (message.length > 0) { 606 if (message.length > 0) {
564 switch (message[0]) { 607 switch (message[0]) {
565 case 'error': 608 case 'error':
566 return onErrorMessage(message[1]['url'], message[1]['message']); 609 return onErrorMessage(message[1]['url'], message[1]['message']);
567 case 'scrollHeight': 610 case 'scrollHeight':
(...skipping 26 matching lines...) Expand all
594 } 637 }
595 638
596 void onBadMessage(MessageEvent event) { 639 void onBadMessage(MessageEvent event) {
597 window.console 640 window.console
598 ..groupCollapsed('Bad message') 641 ..groupCollapsed('Bad message')
599 ..dir(event) 642 ..dir(event)
600 ..log(event.source.runtimeType) 643 ..log(event.source.runtimeType)
601 ..groupEnd(); 644 ..groupEnd();
602 } 645 }
603 646
604 void consolePrintLine(data) { 647 void consolePrintLine(line) {
605 outputDiv.appendText('$data\n'); 648 if (context.shouldClearConsole) {
649 context.shouldClearConsole = false;
650 outputDiv.nodes.clear();
651 }
652 if (window.parent != window) {
653 // Test support.
654 // TODO(ahe): Use '/' instead of '*' when Firefox is upgraded to version
655 // 30 across build bots. Support for '/' was added in version 29, and we
656 // support the two most recent versions.
657 window.parent.postMessage('$line\n', '*');
658 }
659 outputDiv.appendText('$line\n');
660 }
661
662 void onCompilationFailed() {
606 } 663 }
607 664
608 void onCompilationDone() { 665 void onCompilationDone() {
666 context.isFirstCompile = false;
667 context.elapsedCompilationTime.stop();
668 Duration compilationDuration = context.elapsedCompilationTime.elapsed;
669 context.elapsedCompilationTime.reset();
670 print('Compilation took $compilationDuration.');
671 if (context.compilerConsole.parent != null) {
672 context.compilerConsole.remove();
673 }
609 for (AnchorElement diagnostic in context.oldDiagnostics) { 674 for (AnchorElement diagnostic in context.oldDiagnostics) {
610 if (diagnostic.parent != null) { 675 if (diagnostic.parent != null) {
611 // Problem fixed, remove the diagnostic. 676 // Problem fixed, remove the diagnostic.
612 diagnostic.replaceWith(new Text(getText(diagnostic))); 677 diagnostic.replaceWith(new Text(getText(diagnostic)));
613 } 678 }
614 } 679 }
615 context.oldDiagnostics.clear(); 680 context.oldDiagnostics.clear();
616 observer.takeRecords(); // Discard mutations. 681 observer.takeRecords(); // Discard mutations.
617 } 682 }
618 683
619 void compilationStarting() { 684 void compilationStarting() {
685 var progress = new SpanElement()
686 ..appendHtml('<i class="icon-spinner icon-spin"></i>')
687 ..appendText(' Compiling Dart program.');
688 if (settings.verboseCompiler) {
689 progress.appendText('..');
690 }
691 context.compilerConsole = new SpanElement()
692 ..append(progress)
693 ..appendText('\n');
694 context.shouldClearConsole = true;
695 context.elapsedCompilationTime
696 ..start()
697 ..reset();
698 if (context.isFirstCompile) {
699 outputDiv.append(context.compilerConsole);
700 }
620 context.oldDiagnostics 701 context.oldDiagnostics
621 ..clear() 702 ..clear()
622 ..addAll(mainEditorPane.querySelectorAll('a.diagnostic')); 703 ..addAll(mainEditorPane.querySelectorAll('a.diagnostic'));
623 } 704 }
705
706 void verboseCompilerMessage(String message) {
707 if (settings.verboseCompiler) {
708 context.compilerConsole.appendText('$message\n');
709 } else {
710 if (isCompilerStageMarker(message)) {
711 Element progress = context.compilerConsole.firstChild;
712 progress.appendText('.');
713 }
714 }
715 }
624 } 716 }
625 717
626 Future<String> getString(uri) { 718 Future<String> getString(uri) {
627 return new Future<String>.sync(() => HttpRequest.getString('$uri')); 719 return new Future<String>.sync(() => HttpRequest.getString('$uri'));
628 } 720 }
629 721
630 class PendingInputState extends InitialState { 722 class PendingInputState extends InitialState {
631 PendingInputState(InteractionContext context) 723 PendingInputState(InteractionContext context)
632 : super(context); 724 : super(context);
633 725
(...skipping 429 matching lines...) Expand 10 before | Expand all | Expand 10 after
1063 return text.split(new RegExp('^', multiLine: true)); 1155 return text.split(new RegExp('^', multiLine: true));
1064 } 1156 }
1065 1157
1066 void removeCodeCompletion() { 1158 void removeCodeCompletion() {
1067 List<Node> highlighting = 1159 List<Node> highlighting =
1068 mainEditorPane.querySelectorAll('.dart-code-completion'); 1160 mainEditorPane.querySelectorAll('.dart-code-completion');
1069 for (Element element in highlighting) { 1161 for (Element element in highlighting) {
1070 element.remove(); 1162 element.remove();
1071 } 1163 }
1072 } 1164 }
1165
1166 bool isCompilerStageMarker(String message) {
1167 return
1168 message.startsWith('Package root is ') ||
1169 message.startsWith('Compiling ') ||
1170 message == "Resolving..." ||
1171 message.startsWith('Resolved ') ||
1172 message == "Inferring types..." ||
1173 message == "Compiling..." ||
1174 message.startsWith('Compiled ');
1175 }
OLDNEW
« no previous file with comments | « dart/site/try/src/compilation.dart ('k') | dart/site/try/src/ui.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698