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

Side by Side Diff: server/static/rpcexplorer/rpc-completer.html

Issue 1695893004: RPC Explorer (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/luci-go@rpcepxlorer-deps
Patch Set: 80 chars Created 4 years, 10 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
OLDNEW
(Empty)
1 <!--
2 Copyright 2016 The Chromium Authors. All rights reserved.
3 Use of this source code is governed by a BSD-style license that can be
4 found in the LICENSE file.
5 -->
6
7 <link rel="import" href="../bower_components/polymer/polymer.html">
8
9 <link rel="import" href="rpc-descriptor-util.html">
10
11 <!--
12 The `rpc-completer` element implements Ace editor completer interface
13 based on a protobuf message descriptors.
14 -->
15 <script>
16 'use strict';
17
18 Polymer({
19 is: 'rpc-completer',
20
21 properties: {
22 /** @type {FileDescriptorSet} */
23 description: Object,
24
25 rootTypeName: String,
26
27 /** @type {DescriptorProto} */
28 rootType: {
29 type: Object,
30 computed: '_resolveType(description, rootTypeName)'
31 }
32 },
33
34
35 /**
36 * Returns elements to display in autocomplete.
37 */
38 getCompletions: function(editor, session, pos, prefix, callback) {
39 if (!this.rootType) {
40 return;
41 }
42
43 // Get all text left to the current selection.
44 var beforePos = {
45 start: {row: 0, col: 0},
46 end: session.selection.getRange().start
47 };
48 var text = session.getTextRange(beforePos);
49 var completions = this.getCompletionsForText(this.rootType, text);
50 if (completions) {
51 callback(null, completions);
52 }
53 },
54
55 /**
56 * Returns leading comments of a completion.
57 * The result is displayed to the right of the selected completion.
58 */
59 getDocTooltip: function(completion) {
60 return completion.docTooltip;
61 },
62
63 getCompletionsForText: function(type, text) {
64 // Resolve path.
65 var path = this.getCurrentPath(text);
66 if (path == null) {
67 return [];
68 }
69
70 // Resolve type.
71 var util = rpcExplorer.descUtil;
72 for (var i = 0; i < path.length; i++) {
73 if (type.type != 'messageType') {
74 return [];
75 }
76 var field = util.findByName(type.desc.field, path[i]);
77 if (!field) {
78 console.log('Field ' + path[i] + ' not found')
79 return [];
80 }
81 var typeName = field.type_name;
82 if (typeof typeName != 'string') {
83 console.log('Field ' + path[i] + ' is not a message or enum')
84 return [];
85 }
86 // Referenced types are often prefixed with '.'.
87 typeName = util.trimPrefixDot(typeName);
88 type = util.resolve(this.description, typeName);
89 if (!type) {
90 return [];
91 }
92 }
93
94 // Automatically surround with quotes.
95 var quoteCount = (text.match(/"/g) || []).length;
96 var shouldQuote = quoteCount % 2 === 0;
97
98 function docTooltip(desc) {
99 var info = desc.source_code_info;
100 return info && info.leading_comments || '';
101 }
102
103 var completions = [];
104 switch (type.type) {
105 case 'messageType':
106 if (!type.desc.field) {
107 break;
108 }
109 for (var i = 0; i < type.desc.field.length; i++) {
110 var field = type.desc.field[i];
111 var meta = this.fieldTypeName(field);
112 if (field.label === 'LABEL_REPEATED') {
113 meta = 'repeated ' + meta;
114 }
115 completions.push({
116 caption: field.name,
117 snippet: this.snippetForField(field, shouldQuote),
118 meta: meta,
119 docTooltip: docTooltip(field)
120 });
121 }
122 break;
123
124 case 'enumType':
125 for (var i = 0; i < type.desc.value.length; i++) {
126 var value = type.desc.value[i];
127 var snippet = value.name;
128 if (shouldQuote) {
129 snippet = '"' + snippet + '"';
130 }
131 completions.push({
132 caption: value.name,
133 snippet: snippet,
134 meta: '' + value.number,
135 docTooltip: docTooltip(value)
136 });
137 }
138 break;
139 }
140 return completions;
141 },
142
143 snippetForField: function(field, shouldQuote) {
144 // snippet docs:
145 // https://cloud9-sdk.readme.io/docs/snippets
146 var snippet = field.name;
147 if (shouldQuote) {
148 snippet = '"' + snippet + '"';
149 }
150 if (!shouldQuote) {
151 return snippet;
152 }
153
154 snippet += ': ';
155
156 var open = '';
157 var close = '';
158 if (field.label === 'LABEL_REPEATED') {
159 open += '[';
160 close = ']' + close;
161 }
162
163 switch (field.type) {
164 case 'TYPE_MESSAGE':
165 open += '{';
166 close = '}' + close;
167 break;
168 case 'TYPE_STRING':
169 case 'TYPE_ENUM':
170 open += '"';
171 close = '"' + close;
172 break;
173 }
174
175 // ${0} is the position of cursor after insertion.
176 snippet += open + '${0}' + close;
177 return snippet;
178 },
179
180 /**
181 * Resolves path at the end of text, best effort.
182 * e.g. for text '{ "a": { "b": [' returns ['a', 'b']
183 * For '{ "a": {}, "b": {' returns ['b'].
184 * For '{ "a":' returns ['a'].
185 */
186 getCurrentPath: function(text) {
187 var path = [];
188 for (var i = 0; i < text.length;) {
189 i = text.indexOf(':', i);
190 if (i === -1) {
191 break;
192 }
193 var colon = i;
194
195 i++;
196 i = this._skipWhitespace(text, i);
197
198 if (i === text.length ||
199 text.charAt(i) === '"' && i+1 === text.length) {
200 // the path is a field.
201 } else if (text.charAt(i) in {'{':0, '[': 0}) {
202 // there is an array or object after the colon
203 var closingIndex = this.findMatching(text, i);
204 if (closingIndex !== -1) {
205 // Not an object/array or closed. Ignore.
206 continue;
207 }
208 } else {
209 continue
210 }
211
212 // read the name to the left of colon.
213 var secondQuote = text.lastIndexOf('"', colon);
214 if (secondQuote === -1) {
215 return null;
216 }
217
218 var firstQuote = text.lastIndexOf('"', secondQuote - 1);
219 if (firstQuote === -1) {
220 return null;
221 }
222
223 path.push(text.substring(firstQuote + 1, secondQuote));
224 }
225 return path;
226 },
227
228 /** Finds index of the matching brace. */
229 findMatching: function(text, i) {
230 var level = 0;
231 var open = text.charAt(i);
232 var close;
233 switch (open) {
234 case '{':
235 close = '}';
236 break;
237
238 case '[':
239 close = ']';
240 break;
241
242 default:
243 throw Error('Unexpected brace: ' + open);
244 }
245
246 for (; i < text.length; i++) {
247 switch (text.charAt(i)) {
248 case open:
249 level++;
250 break;
251 case close:
252 level--;
253 if (level === 0) {
254 return i;
255 }
256 break;
257 }
258 }
259 return -1;
260 },
261
262 _resolveType: function(desc, name) {
263 return rpcExplorer.descUtil.resolve(desc, name);
264 },
265
266 _scalarTypeNames: {
267 TYPE_DOUBLE: 'double',
268 TYPE_FLOAT: 'float',
269 TYPE_INT64: 'int64',
270 TYPE_UINT64: 'uint64',
271 TYPE_INT32: 'int32',
272 TYPE_FIXED64: 'fixed64',
273 TYPE_FIXED32: 'fixed32',
274 TYPE_BOOL: 'bool',
275 TYPE_STRING: 'string',
276 TYPE_BYTES: 'bytes',
277 TYPE_UINT32: 'uint32',
278 TYPE_SFIXED32: 'sfixed32',
279 TYPE_SFIXED64: 'sfixed64',
280 TYPE_SINT32: 'sint32',
281 TYPE_SINT64: 'sint64',
282 },
283
284 fieldTypeName: function(field) {
285 var name = this._scalarTypeNames[field.type];
286 if (!name) {
287 name = rpcExplorer.descUtil.trimPrefixDot(field.type_name);
288 }
289 return name;
290 },
291
292 _skipWhitespace: function(text, i) {
293 var space = {
294 ' ': 1,
295 '\n': 1,
296 '\r': 1,
297 '\t': 1
298 };
299 while (space[text.charAt(i)]) {
300 i++;
301 }
302 return i;
303 }
304 });
305 </script>
OLDNEW
« no previous file with comments | « server/static/rpcexplorer/index.html ('k') | server/static/rpcexplorer/rpc-descriptor-util.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698