Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(250)

Side by Side Diff: dart/site/try/src/editor.dart

Issue 197923002: Mock up code completion. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge
Patch Set: Merged with r34309. Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « dart/site/try/src/decoration.dart ('k') | dart/site/try/src/interaction_manager.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 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 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.editor; 5 library trydart.editor;
6 6
7 import 'dart:html'; 7 import 'dart:html';
8 8
9 import '../../../sdk/lib/_internal/compiler/implementation/scanner/scannerlib.da rt' 9 import 'package:compiler/implementation/scanner/scannerlib.dart'
10 show 10 show
11 EOF_TOKEN, 11 EOF_TOKEN,
12 StringScanner, 12 StringScanner,
13 Token; 13 Token;
14 14
15 import '../../../sdk/lib/_internal/compiler/implementation/source_file.dart' sho w
16 StringSourceFile;
17
18 import 'compilation.dart' show
19 scheduleCompilation;
20
21 import 'ui.dart' show 15 import 'ui.dart' show
22 currentTheme, 16 currentTheme,
23 hackDiv, 17 hackDiv,
24 inputPre, 18 inputPre,
25 observer, 19 observer,
26 outputDiv; 20 outputDiv;
27 21
28 import 'decoration.dart' show 22 import 'decoration.dart' show
23 CodeCompletionDecoration,
29 Decoration, 24 Decoration,
30 DiagnosticDecoration, 25 DiagnosticDecoration,
31 error, 26 error,
32 info, 27 info,
33 warning; 28 warning;
34 29
35 const String INDENT = '\u{a0}\u{a0}'; 30 const String INDENT = '\u{a0}\u{a0}';
36 31
37 onKeyUp(KeyboardEvent e) { 32 Set<String> seenIdentifiers;
38 if (e.keyCode == 13) { 33
39 e.preventDefault(); 34 Element moveActive(int distance) {
40 Selection selection = window.getSelection(); 35 List<Element> entries = document.querySelectorAll('.dart-static>.dart-entry');
41 if (selection.isCollapsed && selection.anchorNode is Text) { 36 int activeIndex = -1;
42 Text text = selection.anchorNode; 37 for (var i = 0; i < entries.length; i++) {
43 int offset = selection.anchorOffset; 38 if (entries[i].classes.contains('activeEntry')) {
44 text.insertData(offset, '\n'); 39 activeIndex = i;
45 selection.collapse(text, offset + 1); 40 break;
46 } 41 }
47 } 42 }
48 // This is a hack to get Safari to send mutation events on contenteditable. 43 int newIndex = activeIndex + distance;
49 var newDiv = new DivElement(); 44 Element currentEntry;
50 hackDiv.replaceWith(newDiv); 45 if (0 <= newIndex && newIndex < entries.length) {
51 hackDiv = newDiv; 46 currentEntry = entries[newIndex];
47 }
48 if (currentEntry == null) return null;
49 if (0 <= newIndex && activeIndex != -1) {
50 entries[activeIndex].classes.remove('activeEntry');
51 }
52 Element staticNode = document.querySelector('.dart-static');
53 String visibility = computeVisibility(currentEntry, staticNode);
54 print(visibility);
55 var serverResults = document.querySelectorAll('.dart-server>.dart-entry');
56 var serverResultCount = serverResults.length;
57 if (serverResultCount > 0) {
58 switch (visibility) {
59 case obscured:
60 case hidden: {
61 Rectangle cr = currentEntry.getBoundingClientRect();
62 Rectangle sr = staticNode.getBoundingClientRect();
63 Element entry = serverResults[0];
64 entry.remove();
65 currentEntry.parentNode.insertBefore(entry, currentEntry);
66 currentEntry = entry;
67 serverResultCount--;
68
69 staticNode.style.maxHeight = '${sr.boundingBox(cr).height}px';
70 }
71 }
72 } else {
73 currentEntry.scrollIntoView(ScrollAlignment.BOTTOM);
74 }
75 if (serverResultCount == 0) {
76 document.querySelector('.dart-server').style.display = 'none';
77 }
78 if (currentEntry != null) {
79 currentEntry.classes.add('activeEntry');
80 }
81 // Discard mutations.
82 observer.takeRecords();
83 return currentEntry;
84 }
85
86 const visible = 'visible';
87 const obscured = 'obscured';
88 const hidden = 'hidden';
89
90 String computeVisibility(Element node, [Element parent]) {
91 Rectangle nr = node.getBoundingClientRect();
92 if (parent == null) parent = node.parentNode;
93 Rectangle pr = parent.getBoundingClientRect();
94
95 if (pr.containsRectangle(nr)) return visible;
96
97 if (pr.intersects(nr)) return obscured;
98
99 return hidden;
100 }
101
102 var activeCompletion;
103 num minSuggestionWidth = 0;
104
105 /// Returns the [Element] which encloses the current collapsed selection, if it
106 /// exists.
107 Element getElementAtSelection() {
108 Selection selection = window.getSelection();
109 if (!selection.isCollapsed) return null;
110 var anchorNode = selection.anchorNode;
111 if (!inputPre.contains(anchorNode)) return null;
112 if (inputPre == anchorNode) return null;
113 int type = anchorNode.nodeType;
114 if (type != Node.TEXT_NODE) return null;
115 Text text = anchorNode;
116 var parent = text.parent;
117 if (parent is! Element) return null;
118 if (inputPre == parent) return null;
119 return parent;
52 } 120 }
53 121
54 bool isMalformedInput = false; 122 bool isMalformedInput = false;
55 String currentSource = ""; 123 String currentSource = "";
56 124
57 // TODO(ahe): This method should be cleaned up. It is too large.
58 onMutation(List<MutationRecord> mutations, MutationObserver observer) {
59 scheduleCompilation();
60
61 for (Element element in inputPre.queryAll('a[class="diagnostic"]>span')) {
62 element.remove();
63 }
64 // Discard clean-up mutations.
65 observer.takeRecords();
66
67 Selection selection = window.getSelection();
68
69 while (!mutations.isEmpty) {
70 for (MutationRecord record in mutations) {
71 String type = record.type;
72 switch (type) {
73
74 case 'characterData':
75
76 bool hasSelection = false;
77 int offset = selection.anchorOffset;
78 if (selection.isCollapsed && selection.anchorNode == record.target) {
79 hasSelection = true;
80 }
81 var parent = record.target.parentNode;
82 if (parent != inputPre) {
83 inlineChildren(parent);
84 }
85 if (hasSelection) {
86 selection.collapse(record.target, offset);
87 }
88 break;
89
90 default:
91 if (!record.addedNodes.isEmpty) {
92 for (var node in record.addedNodes) {
93
94 if (node.nodeType != Node.ELEMENT_NODE) continue;
95
96 if (node is BRElement) {
97 if (selection.anchorNode != node) {
98 node.replaceWith(new Text('\n'));
99 }
100 } else {
101 var parent = node.parentNode;
102 if (parent == null) continue;
103 var nodes = new List.from(node.nodes);
104 var style = node.getComputedStyle();
105 if (style.display != 'inline') {
106 var previous = node.previousNode;
107 if (previous is Text) {
108 previous.appendData('\n');
109 } else {
110 parent.insertBefore(new Text('\n'), node);
111 }
112 }
113 for (Node child in nodes) {
114 child.remove();
115 parent.insertBefore(child, node);
116 }
117 node.remove();
118 }
119 }
120 }
121 }
122 }
123 mutations = observer.takeRecords();
124 }
125
126 if (!inputPre.nodes.isEmpty && inputPre.nodes.last is Text) {
127 Text text = inputPre.nodes.last;
128 if (!text.text.endsWith('\n')) {
129 text.appendData('\n');
130 }
131 }
132
133 int offset = 0;
134 int anchorOffset = 0;
135 bool hasSelection = false;
136 Node anchorNode = selection.anchorNode;
137 // TODO(ahe): Try to share walk4 methods.
138 void walk4(Node node) {
139 // TODO(ahe): Use TreeWalker when that is exposed.
140 // function textNodesUnder(root){
141 // var n, a=[], walk=document.createTreeWalker(
142 // root,NodeFilter.SHOW_TEXT,null,false);
143 // while(n=walk.nextNode()) a.push(n);
144 // return a;
145 // }
146 int type = node.nodeType;
147 if (type == Node.TEXT_NODE || type == Node.CDATA_SECTION_NODE) {
148 CharacterData text = node;
149 if (anchorNode == node) {
150 hasSelection = true;
151 anchorOffset = selection.anchorOffset + offset;
152 return;
153 }
154 offset += text.length;
155 }
156
157 var child = node.firstChild;
158 while (child != null) {
159 walk4(child);
160 if (hasSelection) return;
161 child = child.nextNode;
162 }
163 }
164 if (selection.isCollapsed) {
165 walk4(inputPre);
166 }
167
168 currentSource = inputPre.text;
169 inputPre.nodes.clear();
170 inputPre.appendText(currentSource);
171 if (hasSelection) {
172 selection.collapse(inputPre.firstChild, anchorOffset);
173 }
174
175 isMalformedInput = false;
176 for (var n in new List.from(inputPre.nodes)) {
177 if (n is! Text) continue;
178 Text node = n;
179 String text = node.text;
180
181 Token token = new StringScanner(
182 new StringSourceFile('', text), includeComments: true).tokenize();
183 int offset = 0;
184 for (;token.kind != EOF_TOKEN; token = token.next) {
185 Decoration decoration = getDecoration(token);
186 if (decoration == null) continue;
187 bool hasSelection = false;
188 int selectionOffset = selection.anchorOffset;
189
190 if (selection.isCollapsed && selection.anchorNode == node) {
191 hasSelection = true;
192 selectionOffset = selection.anchorOffset;
193 }
194 int splitPoint = token.charOffset - offset;
195 Text str = node.splitText(splitPoint);
196 Text after = str.splitText(token.charCount);
197 offset += splitPoint + token.charCount;
198 inputPre.insertBefore(after, node.nextNode);
199 inputPre.insertBefore(decoration.applyTo(str), after);
200
201 if (hasSelection && selectionOffset > node.length) {
202 selectionOffset -= node.length;
203 if (selectionOffset > str.length) {
204 selectionOffset -= str.length;
205 selection.collapse(after, selectionOffset);
206 } else {
207 selection.collapse(str, selectionOffset);
208 }
209 }
210 node = after;
211 }
212 }
213
214 window.localStorage['currentSource'] = currentSource;
215
216 // Discard highlighting mutations.
217 observer.takeRecords();
218 }
219
220 addDiagnostic(String kind, String message, int begin, int end) { 125 addDiagnostic(String kind, String message, int begin, int end) {
221 observer.disconnect(); 126 observer.disconnect();
222 Selection selection = window.getSelection(); 127 Selection selection = window.getSelection();
223 int offset = 0; 128 int offset = 0;
224 int anchorOffset = 0; 129 int anchorOffset = 0;
225 bool hasSelection = false; 130 bool hasSelection = false;
226 Node anchorNode = selection.anchorNode; 131 Node anchorNode = selection.anchorNode;
227 bool foundNode = false; 132 bool foundNode = false;
228 void walk4(Node node) { 133 void walk4(Node node) {
229 // TODO(ahe): Use TreeWalker when that is exposed. 134 // TODO(ahe): Use TreeWalker when that is exposed.
(...skipping 23 matching lines...) Expand all
253 } 158 }
254 if (hasSelection) { 159 if (hasSelection) {
255 selection.collapse(node, anchorOffset); 160 selection.collapse(node, anchorOffset);
256 } 161 }
257 foundNode = true; 162 foundNode = true;
258 return; 163 return;
259 } 164 }
260 offset = newOffset; 165 offset = newOffset;
261 } else if (type == Node.ELEMENT_NODE) { 166 } else if (type == Node.ELEMENT_NODE) {
262 Element element = node; 167 Element element = node;
263 if (element.classes.contains('alert')) return; 168 CssClassSet classes = element.classes;
169 if (classes.contains('alert') ||
170 classes.contains('dart-code-completion')) {
171 return;
172 }
264 } 173 }
265 174
266 var child = node.firstChild; 175 var child = node.firstChild;
267 while(child != null && !foundNode) { 176 while(child != null && !foundNode) {
268 walk4(child); 177 walk4(child);
269 child = child.nextNode; 178 child = child.nextNode;
270 } 179 }
271 } 180 }
272 walk4(inputPre); 181 walk4(inputPre);
273 182
(...skipping 14 matching lines...) Expand all
288 child.remove(); 197 child.remove();
289 parent.insertBefore(child, element); 198 parent.insertBefore(child, element);
290 } 199 }
291 element.remove(); 200 element.remove();
292 } 201 }
293 202
294 Decoration getDecoration(Token token) { 203 Decoration getDecoration(Token token) {
295 String tokenValue = token.value; 204 String tokenValue = token.value;
296 String tokenInfo = token.info.value; 205 String tokenInfo = token.info.value;
297 if (tokenInfo == 'string') return currentTheme.string; 206 if (tokenInfo == 'string') return currentTheme.string;
298 // if (tokenInfo == 'identifier') return identifier; 207 if (tokenInfo == 'identifier') {
208 seenIdentifiers.add(tokenValue);
209 return CodeCompletionDecoration.from(currentTheme.foreground);
210 }
299 if (tokenInfo == 'keyword') return currentTheme.keyword; 211 if (tokenInfo == 'keyword') return currentTheme.keyword;
300 if (tokenInfo == 'comment') return currentTheme.singleLineComment; 212 if (tokenInfo == 'comment') return currentTheme.singleLineComment;
301 if (tokenInfo == 'malformed input') { 213 if (tokenInfo == 'malformed input') {
302 isMalformedInput = true; 214 isMalformedInput = true;
303 return new DiagnosticDecoration('error', tokenValue); 215 return new DiagnosticDecoration('error', tokenValue);
304 } 216 }
305 return currentTheme.foreground; 217 return currentTheme.foreground;
306 } 218 }
307 219
308 diagnostic(text, tip) { 220 diagnostic(text, tip) {
309 if (text is String) { 221 if (text is String) {
310 text = new Text(text); 222 text = new Text(text);
311 } 223 }
312 return new AnchorElement() 224 return new AnchorElement()
313 ..classes.add('diagnostic') 225 ..classes.add('diagnostic')
314 ..append(text) 226 ..append(text)
315 ..append(tip); 227 ..append(tip);
316 } 228 }
OLDNEW
« no previous file with comments | « dart/site/try/src/decoration.dart ('k') | dart/site/try/src/interaction_manager.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698