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

Side by Side Diff: client/web/model.dart

Issue 11636011: Web components based app to view dart docs. Still has rough edges. (Closed) Base URL: https://github.com/dart-lang/dart-api-app.git@master
Patch Set: more fixes Created 7 years, 11 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
« no previous file with comments | « client/web/markdown.dart ('k') | client/web/resources.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
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
1 library apidoc_model; 5 library apidoc_model;
2 6
3 import 'package:web_ui/watcher.dart' as watchers; 7 import 'package:web_ui/watcher.dart' as watchers;
4 import 'package:web_ui/safe_html.dart'; 8 import 'package:web_ui/safe_html.dart';
5 import 'markdown.dart' as md; 9 import 'markdown.dart' as md;
6 import 'dart:html' as html; 10 import 'dart:html' as html;
7 import 'dart:json'; 11 import 'dart:json';
8 import 'ast.dart'; 12 import 'ast.dart';
9 13
14 // TODO(jacobr): specify the version # in the JSON file.
10 String svnRevisionNumber = "15605"; 15 String svnRevisionNumber = "15605";
11 16
12 String _activeReferenceId; 17 /**
18 * Reference id of [currentElement].
19 *
20 * Stored in addition to [currentElement] as [currentElement] may
21 * not yet be available if the data model for the library it is part of has
22 * not yet been loaded.
23 */
24 String _currentReferenceId;
13 25
14 /// Current state of the application. 26 /**
27 * Current library the user is browsing if any.
28 */
15 LibraryElement currentLibrary; 29 LibraryElement currentLibrary;
30
31 /**
32 * Current type the user is viewing if any.
33 * Should be either a [ClassElement] or a [TypedefElement].
34 */
16 Element currentType; 35 Element currentType;
36 /**
37 * Current member of either [currentLibrary] or [currentType] that the user is
38 * viewing.
39 */
17 Element currentMember; 40 Element currentMember;
41
42 /**
43 * Element corresponding to [_currentReferenceId].
44 * The most specific element of [currentLibrary]. [currentType], and
45 * [currentMember].
46 */
18 Element currentElement; 47 Element currentElement;
19 48
49 /**
50 * Recomputes the Elements part of the current active state from the data model.
51 *
52 * This method should be invoked after additional libraries are loaded from the
53 * server or after the user navigates to a different element in the UI.
54 */
20 void _recomputeActiveState() { 55 void _recomputeActiveState() {
21 currentLibrary = null; 56 currentLibrary = null;
22 currentType = null; 57 currentType = null;
23 currentMember = null; 58 currentMember = null;
24 currentElement = null; 59 currentElement = null;
25 if (_activeReferenceId != null) { 60 if (_currentReferenceId != null) {
26 var path = lookupReferenceId(_activeReferenceId).path; 61 var referenceElement = lookupReferenceId(_currentReferenceId);
27 62 if (referenceElement != null) {
28 if (path.length > 0) { 63 var path = referenceElement.path;
29 currentLibrary = path[0]; 64 if (path.length > 0) {
30 } 65 currentLibrary = path[0];
31 if (path.length > 1) { 66 }
32 if (path[1] is ClassElement || path[1] is TypedefElement) { 67 if (path.length > 1) {
33 currentType = path[1]; 68 if (path[1] is ClassElement || path[1] is TypedefElement) {
34 if (path.length > 2) { 69 currentType = path[1];
35 currentMember = path[2]; 70 if (path.length > 2) {
71 currentMember = path[2];
72 }
73 } else {
74 currentMember = path[1];
36 } 75 }
76 }
77 if (currentMember != null) {
78 currentElement = currentMember;
79 } else if (currentType != null) {
80 currentElement = currentType;
37 } else { 81 } else {
38 currentMember = path[1]; 82 currentElement = currentLibrary;
39 } 83 }
40 } 84 }
41 if (currentMember != null) {
42 currentElement = currentMember;
43 } else if (currentType != null) {
44 currentElement = currentType;
45 } else {
46 currentElement = currentLibrary;
47 }
48 } 85 }
49 } 86 }
50 87
88 /**
89 * Scrolls the [currentElement] into view.
90 */
51 void scrollIntoView() { 91 void scrollIntoView() {
52 // TODO(jacobr): there should be a cleaner way to run code that executes 92 // TODO(jacobr): there should be a cleaner way to run code that executes
53 // after the UI updates. 93 // after the UI updates. https://github.com/dart-lang/web-ui/issues/188
54 html.window.setTimeout(() { 94 html.window.setTimeout(() {
55 if (currentElement != null) { 95 if (currentElement != null) {
56 for (var e in html.queryAll('[data-id="${currentElement.id}"]')) { 96 for (var e in html.queryAll('[data-id="${currentElement.id}"]')) {
57 e.scrollIntoView(false); 97 e.scrollIntoView(false);
58 } 98 }
59 } 99 }
60 }, 0); 100 }, 0);
61 } 101 }
62 102
63 onDataModelChanged() { 103 /**
104 * Invoke every time the data model changes.
105 */
106 void _onDataModelChanged() {
64 _recomputeActiveState(); 107 _recomputeActiveState();
65 scrollIntoView(); 108 scrollIntoView();
66 } 109 }
67 110
68 /** 111 /**
69 * Generate a CSS class given an element that may be a class, member, method, 112 * Generate a CSS class given an element that may be a class, member, method,
70 * etc. 113 * etc.
71 */ 114 */
72 String kindCssClass(Element element) { 115 String kindCssClass(Element element) {
73 String classes = 'kind kind-${normalizedKind(element)}'; 116 String classes = 'kind kind-${_normalizedKind(element)}';
74 if (element.isPrivate == true) { 117 if (element.isPrivate == true) {
75 classes = '$classes private'; 118 classes = '$classes private';
76 } else if (element is MethodElementBase && element.isStatic) { 119 } else if (element is MethodLikeElement && element.isStatic) {
77 classes = '$classes static'; 120 classes = '$classes static';
78 } 121 }
79 122
80 // Setters are viewed as methods by the AST. 123 // Setters are viewed as methods by the AST.
81 if (element is PropertyElement) { 124 if (element is PropertyElement) {
82 classes = '$classes getter'; 125 classes = '$classes getter';
83 } 126 }
84 127
85 if (element is MethodElementBase && element.isSetter) { 128 if (element is MethodLikeElement && element.isSetter) {
86 classes = '$classes setter'; 129 classes = '$classes setter';
87 } 130 }
88 131
89 return classes; 132 return classes;
90 } 133 }
91 134
92 String normalizedKind(obj) { 135 String _normalizedKind(Element element) {
93 if (obj is Element) return normalizedKindFromElement(obj);
94 return obj;
95 }
96
97 String normalizedKindFromElement(Element element) {
98 var kind = element.kind; 136 var kind = element.kind;
99 var name = element.name; 137 var name = element.name;
100 if (kind == 'method' && element.isOperator) { 138 if (kind == 'method' && element.isOperator) {
101 kind = 'operator'; 139 kind = 'operator';
102 } 140 }
103 // TODO(jacobr): this is horrible but matches what DartDoc does 141 // TODO(jacobr): this is horrible but matches what DartDoc does
104 if (kind == 'class' && name.endsWith('Exception')) { 142 if (kind == 'class' && name.endsWith('Exception')) {
105 kind = 'exception'; 143 kind = 'exception';
106 } 144 }
107 return kind; 145 return kind;
108 } 146 }
109 147
110 String toUserVisibleKind(Element element) { 148 String toUserVisibleKind(Element element) {
111 return KIND_TITLES[normalizedKind(element)]; 149 return KIND_TITLES[_normalizedKind(element)];
112 } 150 }
113 151
114 /** 152 /**
115 * [obj] shoudl be a [Reference] or [Element]. 153 * [obj] shoudl be a [Reference] or [Element].
116 */ 154 */
117 String permalink(var obj) { 155 String permalink(var obj) {
118 var data = {'id': obj.refId}; 156 var data = {'id': obj.refId};
157 // TODO(jacobr): evaluate whether the persistent UI state will stay just a
158 // single reference ID in which case this is overkill.
119 return "#!${JSON.stringify(data)}"; 159 return "#!${JSON.stringify(data)}";
120 } 160 }
121 161
122 void loadStateFromUrl() { 162 void loadStateFromUrl() {
123 String link = html.window.location.hash; 163 String link = html.window.location.hash;
124 var data = {}; 164 var data = {};
125 if (link.length > 2) { 165 if (link.length > 2) {
126 try { 166 try {
127 // strip #! and parse json. 167 // strip #! and parse json.
128 data = JSON.parse(link.substring(2)); 168 data = JSON.parse(link.substring(2));
129 } catch (e) { 169 } catch (e) {
130 html.window.console.error("Invalid link url"); 170 html.window.console.error("Invalid link url");
131 // TODO(jacobr): redirect to default page or better yet attempt to fixup. 171 // TODO(jacobr): redirect to default page or better yet attempt to fixup.
132 } 172 }
133 } 173 }
134 _activeReferenceId = data['id']; 174 _currentReferenceId = data['id'];
135 _recomputeActiveState(); 175 _recomputeActiveState();
136 scrollIntoView(); 176 scrollIntoView();
137 } 177 }
138 178
139 Future loadModel() { 179 Future loadModel() {
140 html.window.on.popState.add((e) { 180 // Note: listen on both popState and hashChange, because IE9 doens't support
181 // history API, and it doesn't work properly on Opera 12.
182 // See http://dartbug.com/5483
183 updateState(e) {
141 loadStateFromUrl(); 184 loadStateFromUrl();
142 watchers.dispatch(); 185 watchers.dispatch();
143 }); 186 }
187 html.window.on
188 ..popState.add(updateState)
189 ..hashChange.add(updateState);
144 190
145 // Patch in support for [:...:]-style code to the markdown parser. 191 // Patch in support for [:...:]-style code to the markdown parser.
146 // TODO(rnystrom): Markdown already has syntax for this. Phase this out? 192 // TODO(rnystrom): Markdown already has syntax for this. Phase this out?
147 md.InlineParser.syntaxes.insertRange(0, 1, 193 md.InlineParser.syntaxes.insertRange(0, 1,
148 new md.CodeSyntax(r'\[\:((?:.|\n)*?)\:\]')); 194 new md.CodeSyntax(r'\[\:((?:.|\n)*?)\:\]'));
149 195
150 md.setImplicitLinkResolver(_resolveNameReference); 196 md.setImplicitLinkResolver(_resolveNameReference);
151 var completer = new Completer(); 197 var completer = new Completer();
152 // TODO(jacobr): shouldn't have to get this from the parent directory. 198 // TODO(jacobr): shouldn't have to get this from the parent directory.
153 new html.HttpRequest.get('../static/apidoc.json', onSuccess(html.HttpRequest r eq) { 199 new html.HttpRequest.get('../static/apidoc.json', (req) {
154 for (var libraryJson in JSON.parse(req.responseText)) { 200 for (var libraryJson in JSON.parse(req.responseText)) {
155 var library = new LibraryElement(libraryJson, null); 201 var library = new LibraryElement(libraryJson, null);
156 libraries[library.id] = library; 202 libraries[library.id] = library;
157 } 203 }
158 onDataModelChanged(); 204 _onDataModelChanged();
159 completer.complete(true); 205 completer.complete(true);
160 }); 206 });
161 return completer.future; 207 return completer.future;
162 } 208 }
163 209
164 /** XXX NOT USED TODO(jacobr) remove. 210 // TODO(jacobr): remove this method and resolve refences to types in the json
165 class DocComment { 211 // generation. That way the existing correct logic in Dart2Js can be used rather
166 final String text; 212 // than this rather busted logic.
167
168 /**
169 * Non-null if the comment is inherited from another declaration.
170 */
171 final inheritedFrom; // InterfaceMirror?
172
173 DocComment(this.text, [this.inheritedFrom = null]) {
174 assert(text != null && !text.trim().isEmpty);
175 }
176
177 SafeHtml get html => new SafeHtml.unsafe(md.markdownToHtml(text));
178
179 String toString() => text;
180 }
181
182 */
183
184 /** 213 /**
185 * This will be called whenever a doc comment hits a `[name]` in square 214 * This will be called whenever a doc comment hits a `[name]` in square
186 * brackets. It will try to figure out what the name refers to and link or 215 * brackets. It will try to figure out what the name refers to and link or
187 * style it appropriately. 216 * style it appropriately.
188 */ 217 */
189 md.Node _resolveNameReference(String name) { 218 md.Node _resolveNameReference(String name) {
190 // TODO(jacobr): this isn't right yet and we have made this code quite ugly 219 // TODO(jacobr): this isn't right yet and we have made this code quite ugly
191 // by using the verbose universal permalink member even though library is 220 // by using the verbose universal permalink member even though library is
192 // always currentLibrary. 221 // always currentLibrary.
193 makeLink(String href) { 222 makeLink(String href) {
194 return new md.Element.text('a', name) 223 return new md.Element.text('a', name)
195 ..attributes['href'] = href 224 ..attributes['href'] = href
196 ..attributes['class'] = 'crossref'; 225 ..attributes['class'] = 'crossref';
197 } 226 }
198 227
199 // See if it's a parameter of the current method. 228 // See if it's a parameter of the current method.
200 if (currentMember != null && currentMember.kind == 'method') { 229 if (currentMember != null && currentMember.kind == 'method') {
201 var parameters = currentMember.children; 230 var parameters = currentMember.children;
202 for (final parameter in parameters) { 231 for (final parameter in parameters) {
203 if (parameter.name == name) { 232 if (parameter.name == name) {
204 final element = new md.Element.text('span', name); 233 final element = new md.Element.text('span', name);
205 element.attributes['class'] = 'param'; 234 element.attributes['class'] = 'param';
206 return element; 235 return element;
207 } 236 }
208 } 237 }
209 } 238 }
210 239
211 // See if it's another member of the current type. 240 // See if it's another member of the current type.
212 // TODO(jacobr): fixme. this is wrong... members are by id now not simple stri ng name... 241 // TODO(jacobr): fixme. this is wrong... members are by id now not simple
242 // string name...
213 if (currentType != null) { 243 if (currentType != null) {
214 final foundMember = currentType.members[name]; 244 // TODO(jacobr): this should be foundMember = currentType.members[name];
245 final foundMember = null;
215 if (foundMember != null) { 246 if (foundMember != null) {
216 return makeLink(permalink(foundMember)); 247 return makeLink(permalink(foundMember));
217 } 248 }
218 } 249 }
219 250
220 // See if it's another type or a member of another type in the current 251 // See if it's another type or a member of another type in the current
221 // library. 252 // library.
222 if (currentLibrary != null) { 253 if (currentLibrary != null) {
223 // See if it's a constructor 254 // See if it's a constructor
224 final constructorLink = (() { 255 final constructorLink = (() {
(...skipping 11 matching lines...) Expand all
236 return makeLink(permalink(constructor)); 267 return makeLink(permalink(constructor));
237 })(); 268 })();
238 if (constructorLink != null) return constructorLink; 269 if (constructorLink != null) return constructorLink;
239 270
240 // See if it's a member of another type 271 // See if it's a member of another type
241 final foreignMemberLink = (() { 272 final foreignMemberLink = (() {
242 final match = new RegExp(r'([\w$]+)\.([\w$]+)').firstMatch(name); 273 final match = new RegExp(r'([\w$]+)\.([\w$]+)').firstMatch(name);
243 if (match == null) return null; 274 if (match == null) return null;
244 var foundType = currentLibrary.classes[match[1]]; 275 var foundType = currentLibrary.classes[match[1]];
245 if (foundType == null) return null; 276 if (foundType == null) return null;
246 var foundMember = foundType.members[match[2]]; 277 // TODO(jacobr): should be foundMember = foundType.members[match[2]];
278 var foundMember = null;
247 if (foundMember == null) return null; 279 if (foundMember == null) return null;
248 return makeLink(permalink(foundMember)); 280 return makeLink(permalink(foundMember));
249 })(); 281 })();
250 if (foreignMemberLink != null) return foreignMemberLink; 282 if (foreignMemberLink != null) return foreignMemberLink;
251 283
252 var foundType = currentLibrary.classes[name]; 284 var foundType = currentLibrary.classes[name];
253 if (foundType != null) { 285 if (foundType != null) {
254 return makeLink(permalink(foundType)); 286 return makeLink(permalink(foundType));
255 } 287 }
256 288
257 // See if it's a top-level member in the current library. 289 // See if it's a top-level member in the current library.
258 var foundMember = currentLibrary.members[name]; 290 // TODO(jacobr): should be foundMember = currentLibrary.members[name];
291 var foundMember = null;
259 if (foundMember != null) { 292 if (foundMember != null) {
260 return makeLink(permalink(foundMember)); 293 return makeLink(permalink(foundMember));
261 } 294 }
262 } 295 }
263 296
264 // TODO(jacobr): Should also consider: 297 // TODO(jacobr): Should also consider:
265 // * Names imported by libraries this library imports. Don't think we even 298 // * Names imported by libraries this library imports. Don't think we even
266 // store this in the AST. 299 // store this in the AST.
267 // * Type parameters of the enclosing type. 300 // * Type parameters of the enclosing type.
268 301
269 return new md.Element.text('code', name); 302 return new md.Element.text('code', name);
270 } 303 }
OLDNEW
« no previous file with comments | « client/web/markdown.dart ('k') | client/web/resources.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698