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