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

Side by Side Diff: pkg/dartdoc/search.dart

Issue 10829361: 'Find-as-you-type'-search in dartdoc/apidoc. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Rebased Created 8 years, 3 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 | « pkg/dartdoc/nav.dart ('k') | pkg/dartdoc/static/styles.css » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012, 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 /**
6 * [SearchText] represent the search field text. The text is viewed in three
7 * ways: [text] holds the original search text, used for performing
8 * case-sensitive matches, [lowerCase] holds the lower-case search text, used
9 * for performing case-insenstive matches, [camelCase] holds a camel-case
10 * interpretation of the search text, used to order matches in camel-case.
11 */
12 class SearchText {
13 final String text;
14 final String lowerCase;
15 final String camelCase;
16
17 SearchText(String searchText)
18 : text = searchText,
19 lowerCase = searchText.toLowerCase(),
20 camelCase = searchText.isEmpty() ? ''
21 : '${searchText.substring(0, 1).toUpperCase()}'
22 '${searchText.substring(1)}';
23
24 int get length() => text.length;
25
26 bool isEmpty() => length == 0;
27 }
28
29 /**
30 * [StringMatch] represents the case-insensitive matching of [searchText] as a
31 * substring within a [text].
32 */
33 class StringMatch {
34 final SearchText searchText;
35 final String text;
36 final int matchOffset;
37 final int matchEnd;
38
39 StringMatch(this.searchText,
40 this.text, this.matchOffset, this.matchEnd);
41
42 /**
43 * Returns the HTML representation of the match.
44 */
45 String toHtml() {
46 return '${text.substring(0, matchOffset)}'
47 '<span class="drop-down-link-highlight">$matchText</span>'
48 '${text.substring(matchEnd)}';
49 }
50
51 String get matchText() =>
52 text.substring(matchOffset, matchEnd);
53
54 /**
55 * Is [:true:] iff [searchText] matches the full [text] case-sensitively.
56 */
57 bool get isFullMatch() => text == searchText.text;
58
59 /**
60 * Is [:true:] iff [searchText] matches a substring of [text]
61 * case-sensitively.
62 */
63 bool get isExactMatch() => matchText == searchText.text;
64
65 /**
66 * Is [:true:] iff [searchText] matches a substring of [text] when
67 * [searchText] is interpreted as camel case.
68 */
69 bool get isCamelCaseMatch() => matchText == searchText.camelCase;
70 }
71
72 /**
73 * [Result] represents a match of the search text on a library, type or member.
74 */
75 class Result {
76 final StringMatch prefix;
77 final StringMatch match;
78
79 final String library;
80 final String type;
81 final String args;
82 final String kind;
83 final String url;
84
85 TableRowElement row;
86
87 Result(this.match, this.kind, this.url,
88 [this.library, this.type, String args, this.prefix])
89 : this.args = args != null ? '&lt;$args&gt;' : '';
90
91 bool get isTopLevel() => prefix == null && type == null;
92
93 void addRow(TableElement table) {
94 if (row != null) return;
95
96 clickHandler(Event event) {
97 window.location.href = url;
98 hideDropDown();
99 }
100
101 row = table.insertRow(table.rows.length);
102 row.classes.add('drop-down-link-tr');
103 row.on.mouseDown.add((event) => hideDropDownSuspend = true);
104 row.on.click.add(clickHandler);
105 row.on.mouseUp.add((event) => hideDropDownSuspend = false);
106 var sb = new StringBuffer();
107 sb.add('<td class="drop-down-link-td">');
108 sb.add('<table class="drop-down-table"><tr><td colspan="2">');
109 if (kind == GETTER) {
110 sb.add('get ');
111 } else if (kind == SETTER) {
112 sb.add('set ');
113 }
114 sb.add(match.toHtml());
115 if (kind == CLASS || kind == INTERFACE || kind == TYPEDEF) {
116 sb.add(args);
117 } else if (kind == CONSTRUCTOR || kind == METHOD) {
118 sb.add('(...)');
119 }
120 sb.add('</td></tr><tr><td class="drop-down-link-kind">');
121 sb.add(kindToString(kind));
122 if (prefix != null) {
123 sb.add(' in ');
124 sb.add(prefix.toHtml());
125 sb.add(args);
126 } else if (type != null) {
127 sb.add(' in ');
128 sb.add(type);
129 sb.add(args);
130 }
131
132 sb.add('</td><td class="drop-down-link-library">');
133 if (library != null) {
134 sb.add('library $library');
135 }
136 sb.add('</td></tr></table></td>');
137 row.innerHTML = sb.toString();
138 }
139 }
140
141 /**
142 * Creates a [StringMatch] object for [text] if a substring matches
143 * [searchText], or returns [: null :] if no match is found.
144 */
145 StringMatch obtainMatch(SearchText searchText, String text) {
146 if (searchText.isEmpty()) {
147 return new StringMatch(searchText, text, 0, 0);
148 }
149 int offset = text.toLowerCase().indexOf(searchText.lowerCase);
150 if (offset != -1) {
151 return new StringMatch(searchText, text,
152 offset, offset + searchText.length);
153 }
154 return null;
155 }
156
157 /**
158 * Compares [a] and [b], regarding [:true:] smaller than [:false:].
159 *
160 * [:null:]-values are not handled.
161 */
162 int compareBools(bool a, bool b) {
163 if (a == b) return 0;
164 return a ? -1 : 1;
165 }
166
167 /**
168 * Used to sort the search results heuristically to show the more relevant match
169 * in the top of the dropdown.
170 */
171 int resultComparator(Result a, Result b) {
172 // Favor top level entities.
173 int result = compareBools(a.isTopLevel, b.isTopLevel);
174 if (result != 0) return result;
175
176 if (a.prefix != null && b.prefix != null) {
177 // Favor full prefix matches.
178 result = compareBools(a.prefix.isFullMatch, b.prefix.isFullMatch);
179 if (result != 0) return result;
180 }
181
182 // Favor matches in the start.
183 result = compareBools(a.match.matchOffset == 0,
184 b.match.matchOffset == 0);
185 if (result != 0) return result;
186
187 // Favor matches to the end. For example, prefer 'cancel' over 'cancelable'
188 result = compareBools(a.match.matchEnd == a.match.text.length,
189 b.match.matchEnd == b.match.text.length);
190 if (result != 0) return result;
191
192 // Favor exact case-sensitive matches.
193 result = compareBools(a.match.isExactMatch, b.match.isExactMatch);
194 if (result != 0) return result;
195
196 // Favor matches that do not break camel-case.
197 result = compareBools(a.match.isCamelCaseMatch, b.match.isCamelCaseMatch);
198 if (result != 0) return result;
199
200 // Favor matches close to the begining.
201 result = a.match.matchOffset.compareTo(b.match.matchOffset);
202 if (result != 0) return result;
203
204 if (a.type != null && b.type != null) {
205 // Favor short type names over long.
206 result = a.type.length.compareTo(b.type.length);
207 if (result != 0) return result;
208
209 // Sort type alphabetically.
210 // TODO(4805): Use [:type.compareToIgnoreCase] when supported.
211 result = a.type.toLowerCase().compareTo(b.type.toLowerCase());
212 if (result != 0) return result;
213 }
214
215 // Sort match alphabetically.
216 // TODO(4805): Use [:text.compareToIgnoreCase] when supported.
217 return a.match.text.toLowerCase().compareTo(b.match.text.toLowerCase());
218 }
OLDNEW
« no previous file with comments | « pkg/dartdoc/nav.dart ('k') | pkg/dartdoc/static/styles.css » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698