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

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

Issue 265063002: Better handling of large files. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge
Patch Set: Reviewable Created 6 years, 7 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
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 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
85 85
86 // Implementation note: The state machine is actually implemented by 86 // Implementation note: The state machine is actually implemented by
87 // [InteractionContext], this class represents public event handlers. 87 // [InteractionContext], this class represents public event handlers.
88 88
89 factory InteractionManager() => new InteractionContext(); 89 factory InteractionManager() => new InteractionContext();
90 90
91 InteractionManager.internal(); 91 InteractionManager.internal();
92 92
93 void onInput(Event event); 93 void onInput(Event event);
94 94
95 // TODO(ahe): Rename to onKeyDown (as it is called in response to keydown
96 // event).
95 void onKeyUp(KeyboardEvent event); 97 void onKeyUp(KeyboardEvent event);
96 98
97 void onMutation(List<MutationRecord> mutations, MutationObserver observer); 99 void onMutation(List<MutationRecord> mutations, MutationObserver observer);
98 100
99 void onSelectionChange(Event event); 101 void onSelectionChange(Event event);
100 102
101 /// Called when the content of a CompilationUnit changed. 103 /// Called when the content of a CompilationUnit changed.
102 void onCompilationUnitChanged(CompilationUnit unit); 104 void onCompilationUnitChanged(CompilationUnit unit);
103 105
104 Future<List<String>> projectFileNames(); 106 Future<List<String>> projectFileNames();
(...skipping 24 matching lines...) Expand all
129 state = new InitialState(this); 131 state = new InitialState(this);
130 } 132 }
131 133
132 void onInput(Event event) => state.onInput(event); 134 void onInput(Event event) => state.onInput(event);
133 135
134 void onKeyUp(KeyboardEvent event) => state.onKeyUp(event); 136 void onKeyUp(KeyboardEvent event) => state.onKeyUp(event);
135 137
136 void onMutation(List<MutationRecord> mutations, MutationObserver observer) { 138 void onMutation(List<MutationRecord> mutations, MutationObserver observer) {
137 try { 139 try {
138 try { 140 try {
139 return state.onMutation(mutations, observer); 141 Stopwatch sw = new Stopwatch()..start();
142 state.onMutation(mutations, observer);
143 sw.stop();
144 print('onMutation took ${sw.elapsedMilliseconds}ms');
kasperl 2014/05/06 04:51:06 Do you always want to do this?
ahe 2014/05/06 12:54:02 Removed.
140 } finally { 145 } finally {
141 // Discard any mutations during the observer, as these can lead to 146 // Discard any mutations during the observer, as these can lead to
142 // infinite loop. 147 // infinite loop.
143 observer.takeRecords(); 148 observer.takeRecords();
144 } 149 }
145 } catch (error, stackTrace) { 150 } catch (error, stackTrace) {
146 try { 151 try {
147 editor.isMalformedInput = true; 152 editor.isMalformedInput = true;
148 outputDiv 153 outputDiv
149 ..nodes.clear() 154 ..nodes.clear()
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
221 onUnmodifiedKeyUp(event); 226 onUnmodifiedKeyUp(event);
222 } 227 }
223 } 228 }
224 229
225 void onModifiedKeyUp(KeyboardEvent event) { 230 void onModifiedKeyUp(KeyboardEvent event) {
226 } 231 }
227 232
228 void onUnmodifiedKeyUp(KeyboardEvent event) { 233 void onUnmodifiedKeyUp(KeyboardEvent event) {
229 switch (event.keyCode) { 234 switch (event.keyCode) {
230 case KeyCode.ENTER: { 235 case KeyCode.ENTER: {
231 event.preventDefault();
232 Selection selection = window.getSelection(); 236 Selection selection = window.getSelection();
233 if (isCollapsed(selection) && selection.anchorNode is Text) { 237 if (isCollapsed(selection)) {
234 Text text = selection.anchorNode; 238 event.preventDefault();
235 int offset = selection.anchorOffset; 239 Node node = selection.anchorNode;
236 text.insertData(offset, '\n'); 240 if (node is Text) {
237 selection.collapse(text, offset + 1); 241 Text text = node;
242 int offset = selection.anchorOffset;
243 // If at end-of-file, insert an extra newline.
244 String newline = isAtEndOfFile(text, offset) ? '\n\n' : '\n';
245 text.insertData(offset, newline);
246 selection.collapse(text, offset + 1);
247 } else if (node is Element) {
248 node.appendText('\n\n');
kasperl 2014/05/06 04:51:06 A general comment that explains why you're adding
ahe 2014/05/06 12:54:02 Done.
249 selection.collapse(node.firstChild, 1);
250 } else {
251 window.console.error('Unexpected node');
252 window.console.dir(node);
kasperl 2014/05/06 04:51:06 Use cascades?
ahe 2014/05/06 12:54:02 Done.
253 }
238 } 254 }
239 break; 255 break;
240 } 256 }
241 } 257 }
242 258
243 // editor.scheduleRemoveCodeCompletion(); 259 // editor.scheduleRemoveCodeCompletion();
244 260
245 // This is a hack to get Safari (iOS) to send mutation events on 261 // This is a hack to get Safari (iOS) to send mutation events on
246 // contenteditable. 262 // contenteditable.
247 // TODO(ahe): Move to onInput? 263 // TODO(ahe): Move to onInput?
248 var newDiv = new DivElement(); 264 var newDiv = new DivElement();
249 hackDiv.replaceWith(newDiv); 265 hackDiv.replaceWith(newDiv);
250 hackDiv = newDiv; 266 hackDiv = newDiv;
251 } 267 }
252 268
253 void onMutation(List<MutationRecord> mutations, MutationObserver observer) { 269 void onMutation(List<MutationRecord> mutations, MutationObserver observer) {
254 print('onMutation'); 270 print('onMutation');
255 271
256 List<Node> highlighting = mainEditorPane.querySelectorAll( 272 List<Node> highlighting = mainEditorPane.querySelectorAll(
257 'a.diagnostic>span, .dart-code-completion, .hazed-suggestion'); 273 'a.diagnostic>span, .dart-code-completion, .hazed-suggestion');
258 for (Element element in highlighting) { 274 for (Element element in highlighting) {
259 element.remove(); 275 element.remove();
260 } 276 }
261 277
262 Selection selection = window.getSelection(); 278 Selection selection = window.getSelection();
263 TrySelection trySelection = new TrySelection(mainEditorPane, selection); 279 TrySelection trySelection = new TrySelection(mainEditorPane, selection);
264 280
281 Set<Node> normalizedNodes = new Set<Node>();
265 for (MutationRecord record in mutations) { 282 for (MutationRecord record in mutations) {
266 normalizeMutationRecord(record, trySelection); 283 normalizeMutationRecord(record, trySelection, normalizedNodes);
284 }
285
286 if (normalizedNodes.length == 1) {
287 Node node = normalizedNodes.single;
288 if (node is Element && node.classes.contains('lineNumber')) {
289 print('Single line change: ${node.outerHtml}');
kasperl 2014/05/06 04:51:06 Do you want to keep the printing in here?
ahe 2014/05/06 12:54:02 For now, yes.
290
291 String currentText = node.text;
292
293 trySelection = new TrySelection(node, selection);
294 trySelection.updateText(currentText);
295
296 editor.isMalformedInput = false;
297 int offset = 0;
298 List<Node> nodes = <Node>[];
299
300 String state = '';
301 Element previousLine = node.previousElementSibling;
302 if (previousLine != null) {
303 state = previousLine.getAttribute('dart-state');
304 }
305 for (String line in currentText.split(new RegExp('^', multiLine: true))) {
kasperl 2014/05/06 04:51:06 Long line. Consider adding a local for the result
ahe 2014/05/06 12:54:02 I created a method for splitting text into lines.
306 List<Node> lineNodes = <Node>[];
307 state =
308 tokenizeAndHighlight(line, state, offset, trySelection, lineNodes) ;
kasperl 2014/05/06 04:51:06 Long line.
ahe 2014/05/06 12:54:02 Done.
309 offset += line.length;
310 nodes.add(makeLine(lineNodes, state));
311 }
312
313 node.parent.insertAllBefore(nodes, node);
314 node.remove();
315 trySelection.adjust(selection);
316
317 // Discard highlighting mutations.
318 observer.takeRecords();
319 return;
320 }
267 } 321 }
268 322
269 String currentText = mainEditorPane.text; 323 String currentText = mainEditorPane.text;
270 trySelection.updateText(currentText); 324 trySelection.updateText(currentText);
271 325
272 context.currentCompilationUnit.content = currentText; 326 context.currentCompilationUnit.content = currentText;
273 327
274 editor.seenIdentifiers = new Set<String>.from(mock.identifiers); 328 editor.seenIdentifiers = new Set<String>.from(mock.identifiers);
275 329
276 editor.isMalformedInput = false; 330 editor.isMalformedInput = false;
277 int offset = 0; 331 int offset = 0;
278 List<Node> nodes = <Node>[]; 332 List<Node> nodes = <Node>[];
279 333
280 String state = ''; 334 String state = '';
281 for (String line in currentText.split(new RegExp('^', multiLine: true))) { 335 for (String line in currentText.split(new RegExp('^', multiLine: true))) {
282 List<Node> lineNodes = <Node>[]; 336 List<Node> lineNodes = <Node>[];
283 state = 337 state =
284 tokenizeAndHighlight(line, state, offset, trySelection, lineNodes); 338 tokenizeAndHighlight(line, state, offset, trySelection, lineNodes);
285 offset += line.length; 339 offset += line.length;
286 nodes.add(new SpanElement() 340 nodes.add(makeLine(lineNodes, state));
287 ..nodes.addAll(lineNodes)
288 ..classes.add('lineNumber'));
289 } 341 }
290 342
291 mainEditorPane 343 mainEditorPane
292 ..nodes.clear() 344 ..nodes.clear()
293 ..nodes.addAll(nodes); 345 ..nodes.addAll(nodes);
294 trySelection.adjust(selection); 346 trySelection.adjust(selection);
295 347
296 // Discard highlighting mutations. 348 // Discard highlighting mutations.
297 observer.takeRecords(); 349 observer.takeRecords();
298 } 350 }
(...skipping 479 matching lines...) Expand 10 before | Expand all | Expand 10 after
778 830
779 bool isUnterminatedMultiLineToken(UnterminatedToken token) { 831 bool isUnterminatedMultiLineToken(UnterminatedToken token) {
780 return 832 return
781 token.start == '/*' || 833 token.start == '/*' ||
782 token.start == "'''" || 834 token.start == "'''" ||
783 token.start == '"""' || 835 token.start == '"""' ||
784 token.start == "r'''" || 836 token.start == "r'''" ||
785 token.start == 'r"""'; 837 token.start == 'r"""';
786 } 838 }
787 839
788 void normalizeMutationRecord(MutationRecord record, TrySelection selection) { 840 void normalizeMutationRecord(MutationRecord record,
789 if (record.addedNodes.isEmpty) return; 841 TrySelection selection,
842 Set<Node> normalizedNodes) {
790 for (Node node in record.addedNodes) { 843 for (Node node in record.addedNodes) {
791 if (node.parent == null) continue; 844 if (node.parent == null) continue;
792 StringBuffer buffer = new StringBuffer(); 845 StringBuffer buffer = new StringBuffer();
793 int selectionOffset = htmlToText(node, buffer, selection); 846 int selectionOffset = htmlToText(node, buffer, selection);
794 Text newNode = new Text('$buffer'); 847 Text newNode = new Text('$buffer');
795 node.replaceWith(newNode); 848 node.replaceWith(newNode);
849 normalizedNodes.add(findLine(newNode));
796 if (selectionOffset != -1) { 850 if (selectionOffset != -1) {
797 selection.anchorNode = newNode; 851 selection.anchorNode = newNode;
798 selection.anchorOffset = selectionOffset; 852 selection.anchorOffset = selectionOffset;
799 } 853 }
800 } 854 }
855 if (!record.removedNodes.isEmpty) {
856 normalizedNodes.add(findLine(record.target));
857 }
858 if (record.type == "characterData") {
859 normalizedNodes.add(findLine(record.target));
860 }
801 } 861 }
862
863 // Finds the line of [node] (a parent node with CSS class 'lineNumber').
864 // If no such parent exists, return mainEditorPane if it is a parent.
865 // Otherwise return [node].
866 Node findLine(Node node) {
867 for (Node n = node; n != null; n = n.parent) {
868 if (n is Element && n.classes.contains('lineNumber')) return n;
869 if (n == mainEditorPane) return n;
870 }
871 return node;
872 }
873
874 Element makeLine(List<Node> lineNodes, String state) {
875 // Using a div element here (anything with display=block) generally messes up
876 // editing and navigation. We would like to use a block element here so
877 // error messages show as expected. But no such luck. Fortunately, there
878 // are strong indications that the current solution for displaying errors
879 // isn't good enough anyways.
880 return new SpanElement()
881 ..setAttribute('dart-state', state)
882 ..nodes.addAll(lineNodes)
883 ..classes.add('lineNumber');
884 }
885
886 bool isAtEndOfFile(Text text, int offset) {
887 // return false;
kasperl 2014/05/06 04:51:06 Remove this comment.
ahe 2014/05/06 12:54:02 Done.
888 Node line = findLine(text);
889 return
890 line.nextNode == null &&
891 text.parent.nextNode == null &&
892 offset == text.length;
893 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698