| OLD | NEW | 
|---|
|  | (Empty) | 
| 1 // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file |  | 
| 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. |  | 
| 4 |  | 
| 5 library trydart.editor; |  | 
| 6 |  | 
| 7 import 'dart:html'; |  | 
| 8 |  | 
| 9 import 'package:compiler/src/scanner/string_scanner.dart' show |  | 
| 10     StringScanner; |  | 
| 11 |  | 
| 12 import 'package:compiler/src/tokens/token.dart' show |  | 
| 13     ErrorToken, |  | 
| 14     Token; |  | 
| 15 |  | 
| 16 import 'package:compiler/src/tokens/token_constants.dart' show |  | 
| 17     EOF_TOKEN; |  | 
| 18 |  | 
| 19 import 'ui.dart' show |  | 
| 20     currentTheme, |  | 
| 21     hackDiv, |  | 
| 22     interaction, |  | 
| 23     mainEditorPane, |  | 
| 24     observer, |  | 
| 25     outputDiv; |  | 
| 26 |  | 
| 27 import 'decoration.dart' show |  | 
| 28     CodeCompletionDecoration, |  | 
| 29     Decoration, |  | 
| 30     DiagnosticDecoration, |  | 
| 31     error, |  | 
| 32     info, |  | 
| 33     warning; |  | 
| 34 |  | 
| 35 import 'selection.dart' show |  | 
| 36     isCollapsed; |  | 
| 37 |  | 
| 38 import 'shadow_root.dart' show |  | 
| 39     getShadowRoot; |  | 
| 40 |  | 
| 41 import 'settings.dart' as settings; |  | 
| 42 |  | 
| 43 const String INDENT = '\u{a0}\u{a0}'; |  | 
| 44 |  | 
| 45 Set<String> seenIdentifiers; |  | 
| 46 |  | 
| 47 Element moveActive(int distance, Node ui) { |  | 
| 48   var /* ShadowRoot or Element */ root = getShadowRoot(ui); |  | 
| 49   List<Element> entries = root.querySelectorAll('.dart-static>.dart-entry'); |  | 
| 50   int activeIndex = -1; |  | 
| 51   for (var i = 0; i < entries.length; i++) { |  | 
| 52     if (entries[i].classes.contains('activeEntry')) { |  | 
| 53       activeIndex = i; |  | 
| 54       break; |  | 
| 55     } |  | 
| 56   } |  | 
| 57   int newIndex = activeIndex + distance; |  | 
| 58   Element currentEntry; |  | 
| 59   if (0 <= newIndex && newIndex < entries.length) { |  | 
| 60     currentEntry = entries[newIndex]; |  | 
| 61   } |  | 
| 62   if (currentEntry == null) return null; |  | 
| 63   if (0 <= newIndex && activeIndex != -1) { |  | 
| 64     entries[activeIndex].classes.remove('activeEntry'); |  | 
| 65   } |  | 
| 66   Element staticNode = root.querySelector('.dart-static'); |  | 
| 67   String visibility = computeVisibility(currentEntry, staticNode); |  | 
| 68   print(visibility); |  | 
| 69   var serverResults = root.querySelectorAll('.dart-server>.dart-entry'); |  | 
| 70   var serverResultCount = serverResults.length; |  | 
| 71   if (serverResultCount > 0) { |  | 
| 72     switch (visibility) { |  | 
| 73       case obscured: |  | 
| 74       case hidden: { |  | 
| 75         Rectangle cr = currentEntry.getBoundingClientRect(); |  | 
| 76         Rectangle sr = staticNode.getBoundingClientRect(); |  | 
| 77         Element entry = serverResults[0]; |  | 
| 78         entry.remove(); |  | 
| 79         currentEntry.parentNode.insertBefore(entry, currentEntry); |  | 
| 80         currentEntry = entry; |  | 
| 81         serverResultCount--; |  | 
| 82 |  | 
| 83         staticNode.style.maxHeight = '${sr.boundingBox(cr).height}px'; |  | 
| 84       } |  | 
| 85     } |  | 
| 86   } else { |  | 
| 87     currentEntry.scrollIntoView(); |  | 
| 88   } |  | 
| 89   if (serverResultCount == 0) { |  | 
| 90     root.querySelector('.dart-server').style.display = 'none'; |  | 
| 91   } |  | 
| 92   if (currentEntry != null) { |  | 
| 93     currentEntry.classes.add('activeEntry'); |  | 
| 94   } |  | 
| 95   // Discard mutations. |  | 
| 96   observer.takeRecords(); |  | 
| 97   return currentEntry; |  | 
| 98 } |  | 
| 99 |  | 
| 100 const visible = 'visible'; |  | 
| 101 const obscured = 'obscured'; |  | 
| 102 const hidden = 'hidden'; |  | 
| 103 |  | 
| 104 String computeVisibility(Element node, [Element parent]) { |  | 
| 105   Rectangle nr = node.getBoundingClientRect(); |  | 
| 106   if (parent == null) parent = node.parentNode; |  | 
| 107   Rectangle pr = parent.getBoundingClientRect(); |  | 
| 108 |  | 
| 109   if (pr.containsRectangle(nr)) return visible; |  | 
| 110 |  | 
| 111   if (pr.intersects(nr)) return obscured; |  | 
| 112 |  | 
| 113   return hidden; |  | 
| 114 } |  | 
| 115 |  | 
| 116 var activeCompletion; |  | 
| 117 num minSuggestionWidth = 0; |  | 
| 118 |  | 
| 119 /// Returns the [Element] which encloses the current collapsed selection, if it |  | 
| 120 /// exists. |  | 
| 121 Element getElementAtSelection() { |  | 
| 122   Selection selection = window.getSelection(); |  | 
| 123   if (!isCollapsed(selection)) return null; |  | 
| 124   var anchorNode = selection.anchorNode; |  | 
| 125   if (!mainEditorPane.contains(anchorNode)) return null; |  | 
| 126   if (mainEditorPane == anchorNode) return null; |  | 
| 127   int type = anchorNode.nodeType; |  | 
| 128   if (type != Node.TEXT_NODE) return null; |  | 
| 129   Text text = anchorNode; |  | 
| 130   var parent = text.parent; |  | 
| 131   if (parent is! Element) return null; |  | 
| 132   if (mainEditorPane == parent) return null; |  | 
| 133   return parent; |  | 
| 134 } |  | 
| 135 |  | 
| 136 bool isMalformedInput = false; |  | 
| 137 |  | 
| 138 addDiagnostic(String kind, String message, int begin, int end) { |  | 
| 139   observer.disconnect(); |  | 
| 140   Selection selection = window.getSelection(); |  | 
| 141   int offset = 0; |  | 
| 142   int anchorOffset = 0; |  | 
| 143   bool hasSelection = false; |  | 
| 144   Node anchorNode = selection.anchorNode; |  | 
| 145   bool foundNode = false; |  | 
| 146   void walk4(Node node) { |  | 
| 147     // TODO(ahe): Use TreeWalker when that is exposed. |  | 
| 148     int type = node.nodeType; |  | 
| 149     if (type == Node.TEXT_NODE || type == Node.CDATA_SECTION_NODE) { |  | 
| 150       CharacterData cdata = node; |  | 
| 151       // print('walking: ${node.data}'); |  | 
| 152       if (anchorNode == node) { |  | 
| 153         hasSelection = true; |  | 
| 154         anchorOffset = selection.anchorOffset + offset; |  | 
| 155       } |  | 
| 156       int newOffset = offset + cdata.length; |  | 
| 157       if (offset <= begin && begin < newOffset) { |  | 
| 158         hasSelection = node == anchorNode; |  | 
| 159         anchorOffset = selection.anchorOffset; |  | 
| 160         var alert; |  | 
| 161         if (kind == 'error') { |  | 
| 162           alert = error(message); |  | 
| 163         } else if (kind == 'warning') { |  | 
| 164           alert = warning(message); |  | 
| 165         } else { |  | 
| 166           alert = info(message); |  | 
| 167         } |  | 
| 168         Element parent = node.parentNode; |  | 
| 169         if (parent.classes.contains("diagnostic") && |  | 
| 170             !interaction.oldDiagnostics.contains(parent)) { |  | 
| 171           Element other = parent.firstChild; |  | 
| 172           other.remove(); |  | 
| 173           SpanElement wrapper = new SpanElement() |  | 
| 174             ..classes.add('diagnostic') |  | 
| 175             ..style.fontWeight = 'normal'; |  | 
| 176 |  | 
| 177           var root = getShadowRoot(wrapper); |  | 
| 178           if (root is ShadowRoot) { |  | 
| 179             // When https://code.google.com/p/chromium/issues/detail?id=313458 |  | 
| 180             // is fixed: |  | 
| 181             // var link = new LinkElement() |  | 
| 182             //     ..rel = "stylesheet" |  | 
| 183             //     ..type = "text/css" |  | 
| 184             //     ..href = "dartlang-style.css"; |  | 
| 185             // root.append(link); |  | 
| 186             root.append( |  | 
| 187                 new StyleElement()..text = '@import url(dartlang-style.css)'); |  | 
| 188           } |  | 
| 189           root |  | 
| 190               ..append(other) |  | 
| 191               ..append(alert); |  | 
| 192           other.style.display = 'block'; |  | 
| 193           alert.style.display = 'block'; |  | 
| 194           parent.append(wrapper); |  | 
| 195         } else { |  | 
| 196           if (interaction.oldDiagnostics.contains(parent)) { |  | 
| 197             node.remove(); |  | 
| 198             parent.replaceWith(node); |  | 
| 199           } |  | 
| 200           Node marker = new Text(""); |  | 
| 201           node.replaceWith(marker); |  | 
| 202           // TODO(ahe): Don't highlight everything in the node.  Find the |  | 
| 203           // relevant token (works for now as we create a node for each token, |  | 
| 204           // which is probably not great for performance). |  | 
| 205           marker.replaceWith(diagnostic(node, alert)); |  | 
| 206           if (hasSelection) { |  | 
| 207             selection.collapse(node, anchorOffset); |  | 
| 208           } |  | 
| 209         } |  | 
| 210         foundNode = true; |  | 
| 211         return; |  | 
| 212       } |  | 
| 213       offset = newOffset; |  | 
| 214     } else if (type == Node.ELEMENT_NODE) { |  | 
| 215       Element element = node; |  | 
| 216       CssClassSet classes = element.classes; |  | 
| 217       if (classes.contains('alert') || |  | 
| 218           classes.contains('dart-code-completion')) { |  | 
| 219         return; |  | 
| 220       } |  | 
| 221     } |  | 
| 222 |  | 
| 223     var child = node.firstChild; |  | 
| 224     while(child != null && !foundNode) { |  | 
| 225       walk4(child); |  | 
| 226       child = child.nextNode; |  | 
| 227     } |  | 
| 228   } |  | 
| 229   walk4(mainEditorPane); |  | 
| 230 |  | 
| 231   if (!foundNode) { |  | 
| 232     outputDiv.appendText('$message\n'); |  | 
| 233   } |  | 
| 234 |  | 
| 235   observer.takeRecords(); |  | 
| 236   observer.observe( |  | 
| 237       mainEditorPane, childList: true, characterData: true, subtree: true); |  | 
| 238 } |  | 
| 239 |  | 
| 240 Decoration getDecoration(Token token) { |  | 
| 241   if (token is ErrorToken) { |  | 
| 242     // TODO(ahe): Remove side effects from this method. It only leads to |  | 
| 243     // confusion. |  | 
| 244     isMalformedInput = true; |  | 
| 245     return new DiagnosticDecoration('error', token.assertionMessage); |  | 
| 246   } |  | 
| 247   String tokenValue = token.value; |  | 
| 248   String tokenInfo = token.info.value; |  | 
| 249   if (tokenInfo == 'string') return currentTheme.string; |  | 
| 250   if (tokenInfo == 'identifier') { |  | 
| 251     seenIdentifiers.add(tokenValue); |  | 
| 252     Decoration decoration = currentTheme.foreground; |  | 
| 253     if (settings.enableCodeCompletion.value) { |  | 
| 254       decoration = CodeCompletionDecoration.from(decoration); |  | 
| 255     } |  | 
| 256     return decoration; |  | 
| 257   } |  | 
| 258   if (tokenInfo == 'keyword') return currentTheme.keyword; |  | 
| 259   if (tokenInfo == 'comment') return currentTheme.singleLineComment; |  | 
| 260   if (tokenInfo == 'malformed input') { |  | 
| 261     // TODO(ahe): Remove side effects from this method. It only leads to |  | 
| 262     // confusion. |  | 
| 263     isMalformedInput = true; |  | 
| 264     return new DiagnosticDecoration('error', tokenValue); |  | 
| 265   } |  | 
| 266   return currentTheme.foreground; |  | 
| 267 } |  | 
| 268 |  | 
| 269 diagnostic(content, tip) { |  | 
| 270   if (content is String) { |  | 
| 271     content = new Text(content); |  | 
| 272   } |  | 
| 273   if (content is! List) { |  | 
| 274     content = [content]; |  | 
| 275   } |  | 
| 276   return new AnchorElement() |  | 
| 277       ..classes.add('diagnostic') |  | 
| 278       ..append(tip) // Should be first for better Firefox editing. |  | 
| 279       ..nodes.addAll(content); |  | 
| 280 } |  | 
| OLD | NEW | 
|---|