| Index: dart/site/try/src/editor.dart
|
| diff --git a/dart/site/try/src/editor.dart b/dart/site/try/src/editor.dart
|
| index 885295de9c167a69860620832964cdb048f71f0c..f360e76bcca15b5edf1e66f9747407ccb8e3f1ab 100644
|
| --- a/dart/site/try/src/editor.dart
|
| +++ b/dart/site/try/src/editor.dart
|
| @@ -6,18 +6,12 @@ library trydart.editor;
|
|
|
| import 'dart:html';
|
|
|
| -import '../../../sdk/lib/_internal/compiler/implementation/scanner/scannerlib.dart'
|
| +import 'package:compiler/implementation/scanner/scannerlib.dart'
|
| show
|
| EOF_TOKEN,
|
| StringScanner,
|
| Token;
|
|
|
| -import '../../../sdk/lib/_internal/compiler/implementation/source_file.dart' show
|
| - StringSourceFile;
|
| -
|
| -import 'compilation.dart' show
|
| - scheduleCompilation;
|
| -
|
| import 'ui.dart' show
|
| currentTheme,
|
| hackDiv,
|
| @@ -26,6 +20,7 @@ import 'ui.dart' show
|
| outputDiv;
|
|
|
| import 'decoration.dart' show
|
| + CodeCompletionDecoration,
|
| Decoration,
|
| DiagnosticDecoration,
|
| error,
|
| @@ -34,189 +29,99 @@ import 'decoration.dart' show
|
|
|
| const String INDENT = '\u{a0}\u{a0}';
|
|
|
| -onKeyUp(KeyboardEvent e) {
|
| - if (e.keyCode == 13) {
|
| - e.preventDefault();
|
| - Selection selection = window.getSelection();
|
| - if (selection.isCollapsed && selection.anchorNode is Text) {
|
| - Text text = selection.anchorNode;
|
| - int offset = selection.anchorOffset;
|
| - text.insertData(offset, '\n');
|
| - selection.collapse(text, offset + 1);
|
| - }
|
| - }
|
| - // This is a hack to get Safari to send mutation events on contenteditable.
|
| - var newDiv = new DivElement();
|
| - hackDiv.replaceWith(newDiv);
|
| - hackDiv = newDiv;
|
| -}
|
| -
|
| -bool isMalformedInput = false;
|
| -String currentSource = "";
|
| -
|
| -// TODO(ahe): This method should be cleaned up. It is too large.
|
| -onMutation(List<MutationRecord> mutations, MutationObserver observer) {
|
| - scheduleCompilation();
|
| -
|
| - for (Element element in inputPre.queryAll('a[class="diagnostic"]>span')) {
|
| - element.remove();
|
| - }
|
| - // Discard clean-up mutations.
|
| - observer.takeRecords();
|
| -
|
| - Selection selection = window.getSelection();
|
| -
|
| - while (!mutations.isEmpty) {
|
| - for (MutationRecord record in mutations) {
|
| - String type = record.type;
|
| - switch (type) {
|
| -
|
| - case 'characterData':
|
| +Set<String> seenIdentifiers;
|
|
|
| - bool hasSelection = false;
|
| - int offset = selection.anchorOffset;
|
| - if (selection.isCollapsed && selection.anchorNode == record.target) {
|
| - hasSelection = true;
|
| - }
|
| - var parent = record.target.parentNode;
|
| - if (parent != inputPre) {
|
| - inlineChildren(parent);
|
| - }
|
| - if (hasSelection) {
|
| - selection.collapse(record.target, offset);
|
| - }
|
| - break;
|
| -
|
| - default:
|
| - if (!record.addedNodes.isEmpty) {
|
| - for (var node in record.addedNodes) {
|
| -
|
| - if (node.nodeType != Node.ELEMENT_NODE) continue;
|
| -
|
| - if (node is BRElement) {
|
| - if (selection.anchorNode != node) {
|
| - node.replaceWith(new Text('\n'));
|
| - }
|
| - } else {
|
| - var parent = node.parentNode;
|
| - if (parent == null) continue;
|
| - var nodes = new List.from(node.nodes);
|
| - var style = node.getComputedStyle();
|
| - if (style.display != 'inline') {
|
| - var previous = node.previousNode;
|
| - if (previous is Text) {
|
| - previous.appendData('\n');
|
| - } else {
|
| - parent.insertBefore(new Text('\n'), node);
|
| - }
|
| - }
|
| - for (Node child in nodes) {
|
| - child.remove();
|
| - parent.insertBefore(child, node);
|
| - }
|
| - node.remove();
|
| - }
|
| - }
|
| - }
|
| - }
|
| +Element moveActive(int distance) {
|
| + List<Element> entries = document.querySelectorAll('.dart-static>.dart-entry');
|
| + int activeIndex = -1;
|
| + for (var i = 0; i < entries.length; i++) {
|
| + if (entries[i].classes.contains('activeEntry')) {
|
| + activeIndex = i;
|
| + break;
|
| }
|
| - mutations = observer.takeRecords();
|
| }
|
| -
|
| - if (!inputPre.nodes.isEmpty && inputPre.nodes.last is Text) {
|
| - Text text = inputPre.nodes.last;
|
| - if (!text.text.endsWith('\n')) {
|
| - text.appendData('\n');
|
| - }
|
| + int newIndex = activeIndex + distance;
|
| + Element currentEntry;
|
| + if (0 <= newIndex && newIndex < entries.length) {
|
| + currentEntry = entries[newIndex];
|
| }
|
| -
|
| - int offset = 0;
|
| - int anchorOffset = 0;
|
| - bool hasSelection = false;
|
| - Node anchorNode = selection.anchorNode;
|
| - // TODO(ahe): Try to share walk4 methods.
|
| - void walk4(Node node) {
|
| - // TODO(ahe): Use TreeWalker when that is exposed.
|
| - // function textNodesUnder(root){
|
| - // var n, a=[], walk=document.createTreeWalker(
|
| - // root,NodeFilter.SHOW_TEXT,null,false);
|
| - // while(n=walk.nextNode()) a.push(n);
|
| - // return a;
|
| - // }
|
| - int type = node.nodeType;
|
| - if (type == Node.TEXT_NODE || type == Node.CDATA_SECTION_NODE) {
|
| - CharacterData text = node;
|
| - if (anchorNode == node) {
|
| - hasSelection = true;
|
| - anchorOffset = selection.anchorOffset + offset;
|
| - return;
|
| + if (currentEntry == null) return null;
|
| + if (0 <= newIndex && activeIndex != -1) {
|
| + entries[activeIndex].classes.remove('activeEntry');
|
| + }
|
| + Element staticNode = document.querySelector('.dart-static');
|
| + String visibility = computeVisibility(currentEntry, staticNode);
|
| + print(visibility);
|
| + var serverResults = document.querySelectorAll('.dart-server>.dart-entry');
|
| + var serverResultCount = serverResults.length;
|
| + if (serverResultCount > 0) {
|
| + switch (visibility) {
|
| + case obscured:
|
| + case hidden: {
|
| + Rectangle cr = currentEntry.getBoundingClientRect();
|
| + Rectangle sr = staticNode.getBoundingClientRect();
|
| + Element entry = serverResults[0];
|
| + entry.remove();
|
| + currentEntry.parentNode.insertBefore(entry, currentEntry);
|
| + currentEntry = entry;
|
| + serverResultCount--;
|
| +
|
| + staticNode.style.maxHeight = '${sr.boundingBox(cr).height}px';
|
| }
|
| - offset += text.length;
|
| - }
|
| -
|
| - var child = node.firstChild;
|
| - while (child != null) {
|
| - walk4(child);
|
| - if (hasSelection) return;
|
| - child = child.nextNode;
|
| }
|
| + } else {
|
| + currentEntry.scrollIntoView(ScrollAlignment.BOTTOM);
|
| }
|
| - if (selection.isCollapsed) {
|
| - walk4(inputPre);
|
| + if (serverResultCount == 0) {
|
| + document.querySelector('.dart-server').style.display = 'none';
|
| }
|
| -
|
| - currentSource = inputPre.text;
|
| - inputPre.nodes.clear();
|
| - inputPre.appendText(currentSource);
|
| - if (hasSelection) {
|
| - selection.collapse(inputPre.firstChild, anchorOffset);
|
| + if (currentEntry != null) {
|
| + currentEntry.classes.add('activeEntry');
|
| }
|
| + // Discard mutations.
|
| + observer.takeRecords();
|
| + return currentEntry;
|
| +}
|
|
|
| - isMalformedInput = false;
|
| - for (var n in new List.from(inputPre.nodes)) {
|
| - if (n is! Text) continue;
|
| - Text node = n;
|
| - String text = node.text;
|
| +const visible = 'visible';
|
| +const obscured = 'obscured';
|
| +const hidden = 'hidden';
|
|
|
| - Token token = new StringScanner(
|
| - new StringSourceFile('', text), includeComments: true).tokenize();
|
| - int offset = 0;
|
| - for (;token.kind != EOF_TOKEN; token = token.next) {
|
| - Decoration decoration = getDecoration(token);
|
| - if (decoration == null) continue;
|
| - bool hasSelection = false;
|
| - int selectionOffset = selection.anchorOffset;
|
| +String computeVisibility(Element node, [Element parent]) {
|
| + Rectangle nr = node.getBoundingClientRect();
|
| + if (parent == null) parent = node.parentNode;
|
| + Rectangle pr = parent.getBoundingClientRect();
|
|
|
| - if (selection.isCollapsed && selection.anchorNode == node) {
|
| - hasSelection = true;
|
| - selectionOffset = selection.anchorOffset;
|
| - }
|
| - int splitPoint = token.charOffset - offset;
|
| - Text str = node.splitText(splitPoint);
|
| - Text after = str.splitText(token.charCount);
|
| - offset += splitPoint + token.charCount;
|
| - inputPre.insertBefore(after, node.nextNode);
|
| - inputPre.insertBefore(decoration.applyTo(str), after);
|
| + if (pr.containsRectangle(nr)) return visible;
|
|
|
| - if (hasSelection && selectionOffset > node.length) {
|
| - selectionOffset -= node.length;
|
| - if (selectionOffset > str.length) {
|
| - selectionOffset -= str.length;
|
| - selection.collapse(after, selectionOffset);
|
| - } else {
|
| - selection.collapse(str, selectionOffset);
|
| - }
|
| - }
|
| - node = after;
|
| - }
|
| - }
|
| + if (pr.intersects(nr)) return obscured;
|
| +
|
| + return hidden;
|
| +}
|
|
|
| - window.localStorage['currentSource'] = currentSource;
|
| +var activeCompletion;
|
| +num minSuggestionWidth = 0;
|
|
|
| - // Discard highlighting mutations.
|
| - observer.takeRecords();
|
| +/// Returns the [Element] which encloses the current collapsed selection, if it
|
| +/// exists.
|
| +Element getElementAtSelection() {
|
| + Selection selection = window.getSelection();
|
| + if (!selection.isCollapsed) return null;
|
| + var anchorNode = selection.anchorNode;
|
| + if (!inputPre.contains(anchorNode)) return null;
|
| + if (inputPre == anchorNode) return null;
|
| + int type = anchorNode.nodeType;
|
| + if (type != Node.TEXT_NODE) return null;
|
| + Text text = anchorNode;
|
| + var parent = text.parent;
|
| + if (parent is! Element) return null;
|
| + if (inputPre == parent) return null;
|
| + return parent;
|
| }
|
|
|
| +bool isMalformedInput = false;
|
| +String currentSource = "";
|
| +
|
| addDiagnostic(String kind, String message, int begin, int end) {
|
| observer.disconnect();
|
| Selection selection = window.getSelection();
|
| @@ -260,7 +165,11 @@ addDiagnostic(String kind, String message, int begin, int end) {
|
| offset = newOffset;
|
| } else if (type == Node.ELEMENT_NODE) {
|
| Element element = node;
|
| - if (element.classes.contains('alert')) return;
|
| + CssClassSet classes = element.classes;
|
| + if (classes.contains('alert') ||
|
| + classes.contains('dart-code-completion')) {
|
| + return;
|
| + }
|
| }
|
|
|
| var child = node.firstChild;
|
| @@ -295,7 +204,10 @@ Decoration getDecoration(Token token) {
|
| String tokenValue = token.value;
|
| String tokenInfo = token.info.value;
|
| if (tokenInfo == 'string') return currentTheme.string;
|
| - // if (tokenInfo == 'identifier') return identifier;
|
| + if (tokenInfo == 'identifier') {
|
| + seenIdentifiers.add(tokenValue);
|
| + return CodeCompletionDecoration.from(currentTheme.foreground);
|
| + }
|
| if (tokenInfo == 'keyword') return currentTheme.keyword;
|
| if (tokenInfo == 'comment') return currentTheme.singleLineComment;
|
| if (tokenInfo == 'malformed input') {
|
|
|