Chromium Code Reviews| OLD | NEW |
|---|---|
| 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; |
| 11 | 11 |
| 12 import 'dart:math' show | 12 import 'dart:math' show |
| 13 max, | 13 max, |
| 14 min; | 14 min; |
| 15 | 15 |
| 16 import 'dart:async' show | 16 import 'dart:async' show |
| 17 Completer, | 17 Completer, |
| 18 Future, | 18 Future, |
| 19 Timer; | 19 Timer; |
| 20 | 20 |
| 21 import 'dart:collection' show | 21 import 'dart:collection' show |
| 22 Queue; | 22 Queue; |
| 23 | 23 |
| 24 import 'dart:js' as hack; | |
| 25 | |
| 24 import 'package:compiler/implementation/scanner/scannerlib.dart' show | 26 import 'package:compiler/implementation/scanner/scannerlib.dart' show |
| 25 BeginGroupToken, | 27 BeginGroupToken, |
| 26 EOF_TOKEN, | 28 EOF_TOKEN, |
| 27 ErrorToken, | 29 ErrorToken, |
| 28 STRING_INTERPOLATION_IDENTIFIER_TOKEN, | 30 STRING_INTERPOLATION_IDENTIFIER_TOKEN, |
| 29 STRING_INTERPOLATION_TOKEN, | 31 STRING_INTERPOLATION_TOKEN, |
| 30 STRING_TOKEN, | 32 STRING_TOKEN, |
| 31 StringScanner, | 33 StringScanner, |
| 32 Token, | 34 Token, |
| 33 UnmatchedToken, | 35 UnmatchedToken, |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 75 | 77 |
| 76 import 'editor.dart' as editor; | 78 import 'editor.dart' as editor; |
| 77 | 79 |
| 78 import 'mock.dart' as mock; | 80 import 'mock.dart' as mock; |
| 79 | 81 |
| 80 import 'settings.dart' as settings; | 82 import 'settings.dart' as settings; |
| 81 | 83 |
| 82 import 'shadow_root.dart' show | 84 import 'shadow_root.dart' show |
| 83 getShadowRoot, | 85 getShadowRoot, |
| 84 getText, | 86 getText, |
| 85 setShadowRoot; | 87 setShadowRoot, |
| 88 containsNode; | |
| 86 | 89 |
| 87 import 'iframe_error_handler.dart' show | 90 import 'iframe_error_handler.dart' show |
| 88 ErrorMessage; | 91 ErrorMessage; |
| 89 | 92 |
| 90 const String TRY_DART_NEW_DEFECT = | 93 const String TRY_DART_NEW_DEFECT = |
| 91 'https://code.google.com/p/dart/issues/entry' | 94 'https://code.google.com/p/dart/issues/entry' |
| 92 '?template=Try+Dart+Internal+Error'; | 95 '?template=Try+Dart+Internal+Error'; |
| 93 | 96 |
| 94 /// How frequently [InteractionManager.onHeartbeat] is called. | 97 /// How frequently [InteractionManager.onHeartbeat] is called. |
| 95 const Duration HEARTBEAT_INTERVAL = const Duration(milliseconds: 50); | 98 const Duration HEARTBEAT_INTERVAL = const Duration(milliseconds: 50); |
| (...skipping 356 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 452 String state = ''; | 455 String state = ''; |
| 453 String currentText = getText(node); | 456 String currentText = getText(node); |
| 454 if (root != null) { | 457 if (root != null) { |
| 455 // Single line change. | 458 // Single line change. |
| 456 trySelection = trySelection.copyWithRoot(node); | 459 trySelection = trySelection.copyWithRoot(node); |
| 457 Element previousLine = node.previousElementSibling; | 460 Element previousLine = node.previousElementSibling; |
| 458 if (previousLine != null) { | 461 if (previousLine != null) { |
| 459 state = previousLine.getAttribute('dart-state'); | 462 state = previousLine.getAttribute('dart-state'); |
| 460 } | 463 } |
| 461 | 464 |
| 462 node.parent.insertAllBefore( | 465 node.parentNode.insertAllBefore( |
| 463 createHighlightedNodes(trySelection, currentText, state), | 466 createHighlightedNodes(trySelection, currentText, state), |
| 464 node); | 467 node); |
| 465 node.remove(); | 468 node.remove(); |
| 466 } else { | 469 } else { |
| 467 root = node; | 470 root = node; |
| 468 editor.seenIdentifiers = new Set<String>.from(mock.identifiers); | 471 editor.seenIdentifiers = new Set<String>.from(mock.identifiers); |
| 469 | 472 |
| 470 // Fail safe: new [nodes] are computed before clearing old nodes. | 473 // Fail safe: new [nodes] are computed before clearing old nodes. |
| 471 List<Node> nodes = | 474 List<Node> nodes = |
| 472 createHighlightedNodes(trySelection, currentText, state); | 475 createHighlightedNodes(trySelection, currentText, state); |
| 473 | 476 |
| 474 node.nodes | 477 node.nodes |
| 475 ..clear() | 478 ..clear() |
| 476 ..addAll(nodes); | 479 ..addAll(nodes); |
| 477 } | 480 } |
| 478 | 481 |
| 479 if (mainEditorPane.contains(trySelection.anchorNode)) { | 482 if (containsNode(mainEditorPane, trySelection.anchorNode)) { |
| 480 // Sometimes the anchor node is removed by the above call. This has | 483 // Sometimes the anchor node is removed by the above call. This has |
| 481 // only been observed in Firefox, and is hard to reproduce. | 484 // only been observed in Firefox, and is hard to reproduce. |
| 482 trySelection.adjust(selection); | 485 trySelection.adjust(selection); |
| 483 } | 486 } |
| 484 | 487 |
| 485 // TODO(ahe): We know almost exactly what has changed. It could be | 488 // TODO(ahe): We know almost exactly what has changed. It could be |
| 486 // more efficient to only communicate what changed. | 489 // more efficient to only communicate what changed. |
| 487 context.currentCompilationUnit.content = getText(root); | 490 context.currentCompilationUnit.content = getText(root); |
| 488 | 491 |
| 489 // Discard highlighting mutations. | 492 // Discard highlighting mutations. |
| (...skipping 720 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1210 token.start == "'''" || | 1213 token.start == "'''" || |
| 1211 token.start == '"""' || | 1214 token.start == '"""' || |
| 1212 token.start == "r'''" || | 1215 token.start == "r'''" || |
| 1213 token.start == 'r"""'; | 1216 token.start == 'r"""'; |
| 1214 } | 1217 } |
| 1215 | 1218 |
| 1216 void normalizeMutationRecord(MutationRecord record, | 1219 void normalizeMutationRecord(MutationRecord record, |
| 1217 TrySelection selection, | 1220 TrySelection selection, |
| 1218 Set<Node> normalizedNodes) { | 1221 Set<Node> normalizedNodes) { |
| 1219 for (Node node in record.addedNodes) { | 1222 for (Node node in record.addedNodes) { |
| 1220 if (node.parent == null) continue; | 1223 if (node.parentNode == null) continue; |
| 1221 normalizedNodes.add(findLine(node)); | 1224 normalizedNodes.add(findLine(node)); |
| 1222 if (node is Text) continue; | 1225 if (node is Text) continue; |
| 1223 StringBuffer buffer = new StringBuffer(); | 1226 StringBuffer buffer = new StringBuffer(); |
| 1224 int selectionOffset = htmlToText(node, buffer, selection); | 1227 int selectionOffset = htmlToText(node, buffer, selection); |
| 1225 Text newNode = new Text('$buffer'); | 1228 Text newNode = new Text('$buffer'); |
| 1226 node.replaceWith(newNode); | 1229 node.replaceWith(newNode); |
| 1227 if (selectionOffset != -1) { | 1230 if (selectionOffset != -1) { |
| 1228 selection.anchorNode = newNode; | 1231 selection.anchorNode = newNode; |
| 1229 selection.anchorOffset = selectionOffset; | 1232 selection.anchorOffset = selectionOffset; |
| 1230 } | 1233 } |
| 1231 } | 1234 } |
| 1232 if (!record.removedNodes.isEmpty) { | 1235 if (!record.removedNodes.isEmpty) { |
| 1233 var first = record.removedNodes.first; | 1236 var first = record.removedNodes.first; |
| 1234 var line = findLine(record.target); | 1237 var line = findLine(record.target); |
| 1235 | 1238 |
| 1236 if (first is Text && first.data=="\n" && line.nextNode != null) { | 1239 if (first is Text && first.data=="\n" && line.nextNode != null) { |
| 1237 normalizedNodes.add(line.nextNode); | 1240 normalizedNodes.add(line.nextNode); |
| 1238 } | 1241 } |
| 1239 normalizedNodes.add(line); | 1242 normalizedNodes.add(line); |
| 1240 } | 1243 } |
| 1241 if (record.type == "characterData" && record.target.parent != null) { | 1244 if (record.type == "characterData" && record.target.parentNode != null) { |
| 1242 // At least Firefox sends a "characterData" record whose target is the | 1245 // At least Firefox sends a "characterData" record whose target is the |
| 1243 // deleted text node. It also sends a record where "removedNodes" isn't | 1246 // deleted text node. It also sends a record where "removedNodes" isn't |
| 1244 // empty whose target is the parent (which we are interested in). | 1247 // empty whose target is the parent (which we are interested in). |
| 1245 normalizedNodes.add(findLine(record.target)); | 1248 normalizedNodes.add(findLine(record.target)); |
| 1246 } | 1249 } |
| 1247 } | 1250 } |
| 1248 | 1251 |
| 1249 // Finds the line of [node] (a parent node with CSS class 'lineNumber'). | 1252 // Finds the line of [node] (a parent node with CSS class 'lineNumber'). |
| 1250 // If no such parent exists, return mainEditorPane if it is a parent. | 1253 // If no such parent exists, return mainEditorPane if it is a parent. |
| 1251 // Otherwise return [node]. | 1254 // Otherwise return [node]. |
| 1252 Node findLine(Node node) { | 1255 Node findLine(Node node) { |
| 1253 for (Node n = node; n != null; n = n.parent) { | 1256 for (Node n = node; n != null; n = n.parentNode) { |
| 1254 if (n is Element && n.classes.contains('lineNumber')) return n; | 1257 if (n is Element && n.classes.contains('lineNumber')) return n; |
| 1255 if (n == mainEditorPane) return n; | 1258 if (n == mainEditorPane) return n; |
| 1256 } | 1259 } |
| 1257 return node; | 1260 return node; |
| 1258 } | 1261 } |
| 1259 | 1262 |
| 1260 Element makeLine(List<Node> lineNodes, String state) { | 1263 Element makeLine(List<Node> lineNodes, String state) { |
| 1261 return new SpanElement() | 1264 return new SpanElement() |
| 1262 ..setAttribute('dart-state', state) | 1265 ..setAttribute('dart-state', state) |
| 1263 ..nodes.addAll(lineNodes) | 1266 ..nodes.addAll(lineNodes) |
| 1264 ..classes.add('lineNumber'); | 1267 ..classes.add('lineNumber'); |
| 1265 } | 1268 } |
| 1266 | 1269 |
| 1267 bool isAtEndOfFile(Text text, int offset) { | 1270 bool isAtEndOfFile(Text text, int offset) { |
| 1268 Node line = findLine(text); | 1271 Node line = findLine(text); |
| 1269 return | 1272 return |
| 1270 line.nextNode == null && | 1273 line.nextNode == null && |
| 1271 text.parent.nextNode == null && | 1274 text.parentNode.nextNode == null && |
| 1272 offset == text.length; | 1275 offset == text.length; |
| 1273 } | 1276 } |
| 1274 | 1277 |
| 1275 List<String> splitLines(String text) { | 1278 List<String> splitLines(String text) { |
| 1276 return text.split(new RegExp('^', multiLine: true)); | 1279 return text.split(new RegExp('^', multiLine: true)); |
| 1277 } | 1280 } |
| 1278 | 1281 |
| 1279 void removeCodeCompletion() { | 1282 void removeCodeCompletion() { |
| 1280 List<Node> highlighting = | 1283 List<Node> highlighting = |
| 1281 mainEditorPane.querySelectorAll('.dart-code-completion'); | 1284 mainEditorPane.querySelectorAll('.dart-code-completion'); |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 1300 if (!isCollapsed(selection)) return; | 1303 if (!isCollapsed(selection)) return; |
| 1301 Node node = selection.anchorNode; | 1304 Node node = selection.anchorNode; |
| 1302 int offset = selection.anchorOffset; | 1305 int offset = selection.anchorOffset; |
| 1303 if (selection.anchorNode is Element && selection.anchorOffset != 0) { | 1306 if (selection.anchorNode is Element && selection.anchorOffset != 0) { |
| 1304 // In some cases, Firefox reports the wrong anchorOffset (always seems to | 1307 // In some cases, Firefox reports the wrong anchorOffset (always seems to |
| 1305 // be 6) when anchorNode is an Element. Moving the cursor back and forth | 1308 // be 6) when anchorNode is an Element. Moving the cursor back and forth |
| 1306 // adjusts the anchorOffset. | 1309 // adjusts the anchorOffset. |
| 1307 // Safari can also reach this code, but the offset isn't wrong, just | 1310 // Safari can also reach this code, but the offset isn't wrong, just |
| 1308 // inconsistent. After moving the cursor back and forth, Safari will make | 1311 // inconsistent. After moving the cursor back and forth, Safari will make |
| 1309 // the offset relative to a text node. | 1312 // the offset relative to a text node. |
| 1310 selection | 1313 var selectionProxy = new hack.JsObject.fromBrowserObject(selection); |
|
ahe
2014/08/19 09:22:30
Perhaps we can find a better way to encapsulate th
aam-me
2014/08/19 12:09:12
Done.
| |
| 1311 ..modify('move', 'backward', 'character') | 1314 var modify = selectionProxy['modify']; |
| 1312 ..modify('move', 'forward', 'character'); | 1315 if (modify != null) { |
| 1313 print('Selection adjusted $node@$offset -> ' | 1316 // IE doesn't support selection.modify, but it's okay since the code |
| 1314 '${selection.anchorNode}@${selection.anchorOffset}.'); | 1317 // above is for Firefox, IE doesn't have problems with anchorOffset. |
| 1318 selection | |
| 1319 ..modify('move', 'backward', 'character') | |
| 1320 ..modify('move', 'forward', 'character'); | |
| 1321 print('Selection adjusted $node@$offset -> ' | |
| 1322 '${selection.anchorNode}@${selection.anchorOffset}.'); | |
| 1323 } | |
| 1315 } | 1324 } |
| 1316 } | 1325 } |
| 1317 | 1326 |
| 1318 /// Compute the token following a string. Compare to parseSingleLiteralString | 1327 /// Compute the token following a string. Compare to parseSingleLiteralString |
| 1319 /// in parser.dart. | 1328 /// in parser.dart. |
| 1320 Token followString(Token token) { | 1329 Token followString(Token token) { |
| 1321 // TODO(ahe): I should be able to get rid of this if I change the scanner to | 1330 // TODO(ahe): I should be able to get rid of this if I change the scanner to |
| 1322 // create BeginGroupToken for strings. | 1331 // create BeginGroupToken for strings. |
| 1323 int kind = token.kind; | 1332 int kind = token.kind; |
| 1324 while (kind != EOF_TOKEN) { | 1333 while (kind != EOF_TOKEN) { |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 1337 token = token.next; | 1346 token = token.next; |
| 1338 kind = token.kind; | 1347 kind = token.kind; |
| 1339 } | 1348 } |
| 1340 return token; | 1349 return token; |
| 1341 } | 1350 } |
| 1342 | 1351 |
| 1343 String extractQuote(String string) { | 1352 String extractQuote(String string) { |
| 1344 StringQuoting q = StringValidator.quotingFromString(string); | 1353 StringQuoting q = StringValidator.quotingFromString(string); |
| 1345 return (q.raw ? 'r' : '') + (q.quoteChar * q.leftQuoteLength); | 1354 return (q.raw ? 'r' : '') + (q.quoteChar * q.leftQuoteLength); |
| 1346 } | 1355 } |
| OLD | NEW |