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 210 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 221 : super.internal() { | 221 : super.internal() { |
| 222 state = new InitialState(this); | 222 state = new InitialState(this); |
| 223 heartbeat = new Timer.periodic(HEARTBEAT_INTERVAL, onHeartbeat); | 223 heartbeat = new Timer.periodic(HEARTBEAT_INTERVAL, onHeartbeat); |
| 224 } | 224 } |
| 225 | 225 |
| 226 void onInput(Event event) => state.onInput(event); | 226 void onInput(Event event) => state.onInput(event); |
| 227 | 227 |
| 228 void onKeyUp(KeyboardEvent event) => state.onKeyUp(event); | 228 void onKeyUp(KeyboardEvent event) => state.onKeyUp(event); |
| 229 | 229 |
| 230 void onMutation(List<MutationRecord> mutations, MutationObserver observer) { | 230 void onMutation(List<MutationRecord> mutations, MutationObserver observer) { |
| 231 workAroundFirefoxBug(); | |
| 231 try { | 232 try { |
| 232 try { | 233 try { |
| 233 return state.onMutation(mutations, observer); | 234 return state.onMutation(mutations, observer); |
| 234 } finally { | 235 } finally { |
| 235 // Discard any mutations during the observer, as these can lead to | 236 // Discard any mutations during the observer, as these can lead to |
| 236 // infinite loop. | 237 // infinite loop. |
| 237 observer.takeRecords(); | 238 observer.takeRecords(); |
| 238 } | 239 } |
| 239 } catch (error, stackTrace) { | 240 } catch (error, stackTrace) { |
| 240 try { | 241 try { |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 298 InteractionContext get context; | 299 InteractionContext get context; |
| 299 | 300 |
| 300 // TODO(ahe): Remove this. | 301 // TODO(ahe): Remove this. |
| 301 Set<AnchorElement> get oldDiagnostics { | 302 Set<AnchorElement> get oldDiagnostics { |
| 302 throw 'Use context.oldDiagnostics instead'; | 303 throw 'Use context.oldDiagnostics instead'; |
| 303 } | 304 } |
| 304 | 305 |
| 305 void set state(InteractionState newState); | 306 void set state(InteractionState newState); |
| 306 | 307 |
| 307 void onStateChanged(InteractionState previous) { | 308 void onStateChanged(InteractionState previous) { |
| 308 print('State change ${previous.runtimeType} -> ${runtimeType}.'); | |
| 309 } | 309 } |
| 310 | 310 |
| 311 void transitionToInitialState() { | 311 void transitionToInitialState() { |
| 312 state = new InitialState(context); | 312 state = new InitialState(context); |
| 313 } | 313 } |
| 314 } | 314 } |
| 315 | 315 |
| 316 class InitialState extends InteractionState { | 316 class InitialState extends InteractionState { |
| 317 final InteractionContext context; | 317 final InteractionContext context; |
| 318 bool requestCodeCompletion = false; | 318 bool requestCodeCompletion = false; |
| 319 | 319 |
| 320 InitialState(this.context); | 320 InitialState(this.context); |
| 321 | 321 |
| 322 void set state(InteractionState state) { | 322 void set state(InteractionState state) { |
| 323 InteractionState previous = context.state; | 323 InteractionState previous = context.state; |
| 324 if (previous != state) { | 324 if (previous != state) { |
| 325 context.state = state; | 325 context.state = state; |
| 326 state.onStateChanged(previous); | 326 state.onStateChanged(previous); |
| 327 } | 327 } |
| 328 } | 328 } |
| 329 | 329 |
| 330 void onInput(Event event) { | 330 void onInput(Event event) { |
| 331 state = new PendingInputState(context); | 331 state = new PendingInputState(context); |
| 332 } | 332 } |
| 333 | 333 |
| 334 void onKeyUp(KeyboardEvent event) { | 334 void onKeyUp(KeyboardEvent event) { |
| 335 if (computeHasModifier(event)) { | 335 if (computeHasModifier(event)) { |
| 336 print('onKeyUp (modified)'); | |
| 337 onModifiedKeyUp(event); | 336 onModifiedKeyUp(event); |
| 338 } else { | 337 } else { |
| 339 print('onKeyUp (unmodified)'); | |
| 340 onUnmodifiedKeyUp(event); | 338 onUnmodifiedKeyUp(event); |
| 341 } | 339 } |
| 342 } | 340 } |
| 343 | 341 |
| 344 void onModifiedKeyUp(KeyboardEvent event) { | 342 void onModifiedKeyUp(KeyboardEvent event) { |
| 345 } | 343 } |
| 346 | 344 |
| 347 void onUnmodifiedKeyUp(KeyboardEvent event) { | 345 void onUnmodifiedKeyUp(KeyboardEvent event) { |
| 348 switch (event.keyCode) { | 346 switch (event.keyCode) { |
| 349 case KeyCode.ENTER: { | 347 case KeyCode.ENTER: { |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 376 | 374 |
| 377 // This is a hack to get Safari (iOS) to send mutation events on | 375 // This is a hack to get Safari (iOS) to send mutation events on |
| 378 // contenteditable. | 376 // contenteditable. |
| 379 // TODO(ahe): Move to onInput? | 377 // TODO(ahe): Move to onInput? |
| 380 var newDiv = new DivElement(); | 378 var newDiv = new DivElement(); |
| 381 hackDiv.replaceWith(newDiv); | 379 hackDiv.replaceWith(newDiv); |
| 382 hackDiv = newDiv; | 380 hackDiv = newDiv; |
| 383 } | 381 } |
| 384 | 382 |
| 385 void onMutation(List<MutationRecord> mutations, MutationObserver observer) { | 383 void onMutation(List<MutationRecord> mutations, MutationObserver observer) { |
| 386 print('onMutation'); | |
| 387 | |
| 388 removeCodeCompletion(); | 384 removeCodeCompletion(); |
| 389 | 385 |
| 390 Selection selection = window.getSelection(); | 386 Selection selection = window.getSelection(); |
| 391 TrySelection trySelection = new TrySelection(mainEditorPane, selection); | 387 TrySelection trySelection = new TrySelection(mainEditorPane, selection); |
| 392 | 388 |
| 393 Set<Node> normalizedNodes = new Set<Node>(); | 389 Set<Node> normalizedNodes = new Set<Node>(); |
| 394 for (MutationRecord record in mutations) { | 390 for (MutationRecord record in mutations) { |
| 395 normalizeMutationRecord(record, trySelection, normalizedNodes); | 391 normalizeMutationRecord(record, trySelection, normalizedNodes); |
| 396 } | 392 } |
| 397 | 393 |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 419 for (String line in splitLines(currentText)) { | 415 for (String line in splitLines(currentText)) { |
| 420 List<Node> lineNodes = <Node>[]; | 416 List<Node> lineNodes = <Node>[]; |
| 421 state = tokenizeAndHighlight( | 417 state = tokenizeAndHighlight( |
| 422 line, state, offset, trySelection, lineNodes); | 418 line, state, offset, trySelection, lineNodes); |
| 423 offset += line.length; | 419 offset += line.length; |
| 424 nodes.add(makeLine(lineNodes, state)); | 420 nodes.add(makeLine(lineNodes, state)); |
| 425 } | 421 } |
| 426 | 422 |
| 427 node.parent.insertAllBefore(nodes, node); | 423 node.parent.insertAllBefore(nodes, node); |
| 428 node.remove(); | 424 node.remove(); |
| 429 trySelection.adjust(selection); | 425 if (mainEditorPane.contains(trySelection.anchorNode)) { |
|
ahe
2014/06/27 11:23:28
This sometimes happens on Firefox. It is related t
Johnni Winther
2014/07/02 08:40:54
Put this in a comment.
ahe
2014/07/04 13:52:00
Done.
| |
| 426 trySelection.adjust(selection); | |
| 427 } | |
| 430 | 428 |
| 431 // TODO(ahe): We know almost exactly what has changed. It could be | 429 // TODO(ahe): We know almost exactly what has changed. It could be |
| 432 // more efficient to only communicate what changed. | 430 // more efficient to only communicate what changed. |
| 433 context.currentCompilationUnit.content = getText(mainEditorPane); | 431 context.currentCompilationUnit.content = getText(mainEditorPane); |
| 434 | 432 |
| 435 // Discard highlighting mutations. | 433 // Discard highlighting mutations. |
| 436 observer.takeRecords(); | 434 observer.takeRecords(); |
| 437 return; | 435 return; |
| 438 } | 436 } |
| 439 } | 437 } |
| (...skipping 703 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1143 Text newNode = new Text('$buffer'); | 1141 Text newNode = new Text('$buffer'); |
| 1144 node.replaceWith(newNode); | 1142 node.replaceWith(newNode); |
| 1145 if (selectionOffset != -1) { | 1143 if (selectionOffset != -1) { |
| 1146 selection.anchorNode = newNode; | 1144 selection.anchorNode = newNode; |
| 1147 selection.anchorOffset = selectionOffset; | 1145 selection.anchorOffset = selectionOffset; |
| 1148 } | 1146 } |
| 1149 } | 1147 } |
| 1150 if (!record.removedNodes.isEmpty) { | 1148 if (!record.removedNodes.isEmpty) { |
| 1151 normalizedNodes.add(findLine(record.target)); | 1149 normalizedNodes.add(findLine(record.target)); |
| 1152 } | 1150 } |
| 1153 if (record.type == "characterData") { | 1151 if (record.type == "characterData" && record.target.parent != null) { |
|
ahe
2014/06/27 11:23:28
Firefox sends two records when the last character
Johnni Winther
2014/07/02 08:40:54
Put this in a comment.
ahe
2014/07/04 13:52:00
Done.
| |
| 1154 normalizedNodes.add(findLine(record.target)); | 1152 normalizedNodes.add(findLine(record.target)); |
| 1155 } | 1153 } |
| 1156 } | 1154 } |
| 1157 | 1155 |
| 1158 // Finds the line of [node] (a parent node with CSS class 'lineNumber'). | 1156 // Finds the line of [node] (a parent node with CSS class 'lineNumber'). |
| 1159 // If no such parent exists, return mainEditorPane if it is a parent. | 1157 // If no such parent exists, return mainEditorPane if it is a parent. |
| 1160 // Otherwise return [node]. | 1158 // Otherwise return [node]. |
| 1161 Node findLine(Node node) { | 1159 Node findLine(Node node) { |
| 1162 for (Node n = node; n != null; n = n.parent) { | 1160 for (Node n = node; n != null; n = n.parent) { |
| 1163 if (n is Element && n.classes.contains('lineNumber')) return n; | 1161 if (n is Element && n.classes.contains('lineNumber')) return n; |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1196 bool isCompilerStageMarker(String message) { | 1194 bool isCompilerStageMarker(String message) { |
| 1197 return | 1195 return |
| 1198 message.startsWith('Package root is ') || | 1196 message.startsWith('Package root is ') || |
| 1199 message.startsWith('Compiling ') || | 1197 message.startsWith('Compiling ') || |
| 1200 message == "Resolving..." || | 1198 message == "Resolving..." || |
| 1201 message.startsWith('Resolved ') || | 1199 message.startsWith('Resolved ') || |
| 1202 message == "Inferring types..." || | 1200 message == "Inferring types..." || |
| 1203 message == "Compiling..." || | 1201 message == "Compiling..." || |
| 1204 message.startsWith('Compiled '); | 1202 message.startsWith('Compiled '); |
| 1205 } | 1203 } |
| 1204 | |
| 1205 void workAroundFirefoxBug() { | |
| 1206 Selection selection = window.getSelection(); | |
| 1207 if (!isCollapsed(selection)) return; | |
| 1208 Node node = selection.anchorNode; | |
| 1209 int offset = selection.anchorOffset; | |
| 1210 if (selection.anchorNode is Element && selection.anchorOffset != 0) { | |
|
ahe
2014/06/27 11:23:28
In some cases, Firefox reports the wrong anchorOff
Johnni Winther
2014/07/02 08:40:54
Again, put this in a comment.
ahe
2014/07/04 13:52:00
Done.
| |
| 1211 selection | |
| 1212 ..modify('move', 'backward', 'character') | |
| 1213 ..modify('move', 'forward', 'character'); | |
| 1214 print('Worked around Firefox selection bug $node@$offset -> ' | |
| 1215 '${selection.anchorNode}@${selection.anchorOffset}.'); | |
| 1216 } | |
| 1217 } | |
| OLD | NEW |