Chromium Code Reviews| Index: dart/site/try/src/interaction_manager.dart |
| diff --git a/dart/site/try/src/interaction_manager.dart b/dart/site/try/src/interaction_manager.dart |
| index 5b309a96cbcbe2fff1d870ac2d033120d9539b94..46cac9da3df5dc709ee1d76dd55d219a927e8e90 100644 |
| --- a/dart/site/try/src/interaction_manager.dart |
| +++ b/dart/site/try/src/interaction_manager.dart |
| @@ -92,6 +92,8 @@ abstract class InteractionManager { |
| void onInput(Event event); |
| + // TODO(ahe): Rename to onKeyDown (as it is called in response to keydown |
| + // event). |
| void onKeyUp(KeyboardEvent event); |
| void onMutation(List<MutationRecord> mutations, MutationObserver observer); |
| @@ -136,7 +138,10 @@ class InteractionContext extends InteractionManager { |
| void onMutation(List<MutationRecord> mutations, MutationObserver observer) { |
| try { |
| try { |
| - return state.onMutation(mutations, observer); |
| + Stopwatch sw = new Stopwatch()..start(); |
| + state.onMutation(mutations, observer); |
| + sw.stop(); |
| + 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.
|
| } finally { |
| // Discard any mutations during the observer, as these can lead to |
| // infinite loop. |
| @@ -228,13 +233,24 @@ class InitialState extends InteractionState { |
| void onUnmodifiedKeyUp(KeyboardEvent event) { |
| switch (event.keyCode) { |
| case KeyCode.ENTER: { |
| - event.preventDefault(); |
| Selection selection = window.getSelection(); |
| - if (isCollapsed(selection) && selection.anchorNode is Text) { |
| - Text text = selection.anchorNode; |
| - int offset = selection.anchorOffset; |
| - text.insertData(offset, '\n'); |
| - selection.collapse(text, offset + 1); |
| + if (isCollapsed(selection)) { |
| + event.preventDefault(); |
| + Node node = selection.anchorNode; |
| + if (node is Text) { |
| + Text text = node; |
| + int offset = selection.anchorOffset; |
| + // If at end-of-file, insert an extra newline. |
| + String newline = isAtEndOfFile(text, offset) ? '\n\n' : '\n'; |
| + text.insertData(offset, newline); |
| + selection.collapse(text, offset + 1); |
| + } else if (node is Element) { |
| + 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.
|
| + selection.collapse(node.firstChild, 1); |
| + } else { |
| + window.console.error('Unexpected node'); |
| + window.console.dir(node); |
|
kasperl
2014/05/06 04:51:06
Use cascades?
ahe
2014/05/06 12:54:02
Done.
|
| + } |
| } |
| break; |
| } |
| @@ -262,8 +278,46 @@ class InitialState extends InteractionState { |
| Selection selection = window.getSelection(); |
| TrySelection trySelection = new TrySelection(mainEditorPane, selection); |
| + Set<Node> normalizedNodes = new Set<Node>(); |
| for (MutationRecord record in mutations) { |
| - normalizeMutationRecord(record, trySelection); |
| + normalizeMutationRecord(record, trySelection, normalizedNodes); |
| + } |
| + |
| + if (normalizedNodes.length == 1) { |
| + Node node = normalizedNodes.single; |
| + if (node is Element && node.classes.contains('lineNumber')) { |
| + 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.
|
| + |
| + String currentText = node.text; |
| + |
| + trySelection = new TrySelection(node, selection); |
| + trySelection.updateText(currentText); |
| + |
| + editor.isMalformedInput = false; |
| + int offset = 0; |
| + List<Node> nodes = <Node>[]; |
| + |
| + String state = ''; |
| + Element previousLine = node.previousElementSibling; |
| + if (previousLine != null) { |
| + state = previousLine.getAttribute('dart-state'); |
| + } |
| + 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.
|
| + List<Node> lineNodes = <Node>[]; |
| + state = |
| + tokenizeAndHighlight(line, state, offset, trySelection, lineNodes); |
|
kasperl
2014/05/06 04:51:06
Long line.
ahe
2014/05/06 12:54:02
Done.
|
| + offset += line.length; |
| + nodes.add(makeLine(lineNodes, state)); |
| + } |
| + |
| + node.parent.insertAllBefore(nodes, node); |
| + node.remove(); |
| + trySelection.adjust(selection); |
| + |
| + // Discard highlighting mutations. |
| + observer.takeRecords(); |
| + return; |
| + } |
| } |
| String currentText = mainEditorPane.text; |
| @@ -283,9 +337,7 @@ class InitialState extends InteractionState { |
| state = |
| tokenizeAndHighlight(line, state, offset, trySelection, lineNodes); |
| offset += line.length; |
| - nodes.add(new SpanElement() |
| - ..nodes.addAll(lineNodes) |
| - ..classes.add('lineNumber')); |
| + nodes.add(makeLine(lineNodes, state)); |
| } |
| mainEditorPane |
| @@ -785,17 +837,57 @@ bool isUnterminatedMultiLineToken(UnterminatedToken token) { |
| token.start == 'r"""'; |
| } |
| -void normalizeMutationRecord(MutationRecord record, TrySelection selection) { |
| - if (record.addedNodes.isEmpty) return; |
| +void normalizeMutationRecord(MutationRecord record, |
| + TrySelection selection, |
| + Set<Node> normalizedNodes) { |
| for (Node node in record.addedNodes) { |
| if (node.parent == null) continue; |
| StringBuffer buffer = new StringBuffer(); |
| int selectionOffset = htmlToText(node, buffer, selection); |
| Text newNode = new Text('$buffer'); |
| node.replaceWith(newNode); |
| + normalizedNodes.add(findLine(newNode)); |
| if (selectionOffset != -1) { |
| selection.anchorNode = newNode; |
| selection.anchorOffset = selectionOffset; |
| } |
| } |
| + if (!record.removedNodes.isEmpty) { |
| + normalizedNodes.add(findLine(record.target)); |
| + } |
| + if (record.type == "characterData") { |
| + normalizedNodes.add(findLine(record.target)); |
| + } |
| +} |
| + |
| +// Finds the line of [node] (a parent node with CSS class 'lineNumber'). |
| +// If no such parent exists, return mainEditorPane if it is a parent. |
| +// Otherwise return [node]. |
| +Node findLine(Node node) { |
| + for (Node n = node; n != null; n = n.parent) { |
| + if (n is Element && n.classes.contains('lineNumber')) return n; |
| + if (n == mainEditorPane) return n; |
| + } |
| + return node; |
| +} |
| + |
| +Element makeLine(List<Node> lineNodes, String state) { |
| + // Using a div element here (anything with display=block) generally messes up |
| + // editing and navigation. We would like to use a block element here so |
| + // error messages show as expected. But no such luck. Fortunately, there |
| + // are strong indications that the current solution for displaying errors |
| + // isn't good enough anyways. |
| + return new SpanElement() |
| + ..setAttribute('dart-state', state) |
| + ..nodes.addAll(lineNodes) |
| + ..classes.add('lineNumber'); |
| +} |
| + |
| +bool isAtEndOfFile(Text text, int offset) { |
| + // return false; |
|
kasperl
2014/05/06 04:51:06
Remove this comment.
ahe
2014/05/06 12:54:02
Done.
|
| + Node line = findLine(text); |
| + return |
| + line.nextNode == null && |
| + text.parent.nextNode == null && |
| + offset == text.length; |
| } |