OLD | NEW |
---|---|
(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 | |
8 <link rel="import" href="/static/common/third_party/polymer/polymer.html"> | |
9 | |
10 <link rel="import" href="rpc-descriptor-util.html"> | |
11 | |
12 <!-- The `rpc-completer` element implements Ace editor completer interface | |
Bons
2016/02/13 17:18:25
newline after <!-- ?
nodir
2016/02/17 02:02:13
Done.
| |
13 based on a protobuf message descriptors. | |
14 --> | |
15 <script> | |
16 'use strict'; | |
17 | |
18 Polymer({ | |
19 is: 'rpc-completer', | |
20 | |
21 properties: { | |
22 description: Object, // FileDescriptorSet message | |
23 rootTypeName: String, | |
24 rootType: { | |
25 type: Object, | |
26 computed: '_resolveType(description, rootTypeName)' | |
27 } | |
28 }, | |
29 | |
30 _resolveType: function(desc, name) { | |
31 return rpcExplorer.descUtil.resolve(desc, name); | |
32 }, | |
33 | |
34 skipWhitespace: function(text, i) { | |
35 var space = { | |
36 ' ': 1, | |
37 '\n': 1, | |
38 '\r': 1, | |
39 '\t': 1 | |
40 }; | |
41 while (space[text.charAt(i)]) { | |
42 i++; | |
43 } | |
44 return i; | |
45 }, | |
46 | |
47 findMatching: function(text, i, open, close) { | |
48 var level = 1; | |
49 for (; i < text.length; i++) { | |
50 switch (text.charAt(i)) { | |
51 case open: | |
52 level++; | |
53 break; | |
54 case close: | |
55 level--; | |
56 if (level == 0) { | |
57 return i; | |
58 } | |
59 break; | |
60 } | |
61 } | |
62 return -1; | |
63 }, | |
64 | |
65 // resolves path at the end of text, best effort. | |
66 // e.g. for text '{ "a": { "b": [' returns ['a', 'b'] | |
67 // For '{ "a": {}, "b": {' returns ['b']. | |
68 // For '{ "a":' returns ['a']. | |
69 getCurrentPath: function(text) { | |
70 var path = []; | |
71 for (var i = 0; i < text.length;) { | |
72 // find colon. | |
Bons
2016/02/13 17:18:25
this comment is too short and doesn't really tell
nodir
2016/02/17 02:02:13
removed
| |
73 i = text.indexOf(':', i); | |
74 if (i == -1) { | |
75 break; | |
76 } | |
77 var colon = i; | |
78 | |
79 i++; | |
80 i = this.skipWhitespace(text, i); | |
81 | |
82 if (i == text.length || text.charAt(i) == '"' && i + 1 == text.length) { | |
83 // the path is a field. | |
84 } else { | |
85 // is there an array or object after the colon? | |
86 var closingIndex = null; | |
87 switch (text.charAt(i)) { | |
88 case '{': | |
89 closingIndex = this.findMatching(text, i + 1, '{', '}'); | |
90 break; | |
91 case '[': | |
92 closingIndex = this.findMatching(text, i + 1, '[', ']'); | |
93 break; | |
94 } | |
95 if (closingIndex == null || closingIndex != -1) { | |
96 // Not an object/array or closed. Ignore. | |
97 continue; | |
98 } | |
99 } | |
100 | |
101 // read the name to the left of colon. | |
102 var secondQuote = text.lastIndexOf('"', colon); | |
103 if (secondQuote == -1) { | |
104 return null; | |
105 } | |
106 | |
107 var firstQuote = text.lastIndexOf('"', secondQuote - 1); | |
108 if (firstQuote == -1) { | |
109 return null; | |
110 } | |
111 | |
112 path.push(text.substring(firstQuote + 1, secondQuote)); | |
113 } | |
114 return path; | |
115 }, | |
116 | |
117 // returns elements to display in autocomplete. | |
118 getCompletions: function(editor, session, pos, prefix, callback) { | |
119 if (!this.rootType) { | |
120 return; | |
121 } | |
122 | |
123 // Get all text left to the current selection. | |
124 var beforePos = { | |
125 start: {row: 0, col: 0 }, | |
126 end: session.selection.getRange().start | |
127 }; | |
128 var text = session.getTextRange(beforePos); | |
129 // Resolve path. | |
130 var path = this.getCurrentPath(text); | |
131 console.log('completion: current path: ', path); | |
132 | |
133 // Resolve type. | |
134 var type = this.rootType; | |
135 var util = rpcExplorer.descUtil; | |
136 for (var i = 0; i < path.length; i++) { | |
137 if (type.type != 'message') { | |
138 return; | |
139 } | |
140 var field = util.findByName(type.desc.field, path[i]); | |
141 if (!field) { | |
142 console.log('Field ' + path[i] + ' not found') | |
143 return; | |
144 } | |
145 var typeName = field.type_name; | |
146 if (typeof typeName != 'string') { | |
147 console.log('Field ' + path[i] + ' is not a message or enum') | |
148 return; | |
149 } | |
150 // referenced types are often prefixed with '.' | |
151 typeName = util.trimPrefixDot(typeName); | |
152 type = util.resolve(this.description, typeName); | |
153 if (!type) { | |
154 return; | |
155 } | |
156 } | |
157 | |
158 // Automatically surround with quotes. | |
159 var quoteCount = (text.match(/"/g) || []).length; | |
160 var shouldQuote = quoteCount % 2 == 0; | |
161 | |
162 var completions = []; | |
163 switch (type.type) { | |
164 case 'message': | |
165 if (!type.desc.field) { | |
166 return; | |
167 } | |
168 for (var i = 0; i < type.desc.field.length; i++) { | |
169 var field = type.desc.field[i]; | |
170 var meta = this.fieldTypeName(field); | |
171 if (field.label == 'LABEL_REPEATED') { | |
172 meta = 'repeated ' + meta; | |
173 } | |
174 completions.push({ | |
175 desc: field, | |
176 caption: field.name, | |
177 snippet: this.snippetForField(field, shouldQuote), | |
178 meta: meta | |
179 }); | |
180 } | |
181 break; | |
182 | |
183 case 'enum': | |
184 for (var i = 0; i < type.desc.value.length; i++) { | |
185 var value = type.desc.value[i]; | |
186 var snippet = value.name; | |
187 if (shouldQuote) { | |
188 snippet = '"' + snippet + '"'; | |
189 } | |
190 completions.push({ | |
191 desc: value, | |
192 caption: value.name, | |
193 snippet: snippet, | |
194 meta: '' + value.number | |
195 }) | |
196 } | |
197 break; | |
198 } | |
199 | |
200 callback(null, completions); | |
201 }, | |
202 | |
203 snippetForField: function (field, shouldQuote) { | |
204 // snippet docs: | |
205 // https://cloud9-sdk.readme.io/docs/snippets | |
206 var snippet = field.name; | |
207 if (shouldQuote) { | |
208 snippet = '"' + snippet + '"'; | |
209 } | |
210 if (!shouldQuote) { | |
211 return snippet; | |
212 } | |
213 | |
214 snippet += ': '; | |
215 | |
216 var open = ''; | |
217 var close = ''; | |
218 if (field.label == 'LABEL_REPEATED') { | |
219 open += '['; | |
220 close = ']' + close; | |
221 } | |
222 | |
223 switch (field.type) { | |
224 case 'TYPE_MESSAGE': | |
225 open += '{'; | |
226 close = '}' + close; | |
227 break; | |
228 case 'TYPE_STRING': | |
229 case 'TYPE_ENUM': | |
230 open += '"'; | |
231 close = '"' + close; | |
232 break; | |
233 } | |
234 | |
235 // ${0} is the position of cursor after insertion. | |
236 snippet += open + '${0}' + close; | |
237 return snippet; | |
238 }, | |
239 | |
240 // Returns leading comments of a completion. | |
241 // The result is displayed to the right of the selected completion. | |
242 getDocTooltip: function(completion) { | |
243 return completion.desc.source_code_info && completion.desc.source_code_info. leading_comments; | |
Bons
2016/02/13 17:18:25
80 chars
nodir
2016/02/17 02:02:12
Done.
| |
244 }, | |
245 | |
246 scalarTypeNames: { | |
247 TYPE_DOUBLE: 'double', | |
248 TYPE_FLOAT: 'float', | |
249 TYPE_INT64: 'int64', | |
250 TYPE_UINT64: 'uint64', | |
251 TYPE_INT32: 'int32', | |
252 TYPE_FIXED64: 'fixed64', | |
253 TYPE_FIXED32: 'fixed32', | |
254 TYPE_BOOL: 'bool', | |
255 TYPE_STRING: 'string', | |
256 TYPE_BYTES: 'bytes', | |
257 TYPE_UINT32: 'uint32', | |
258 TYPE_SFIXED32: 'sfixed32', | |
259 TYPE_SFIXED64: 'sfixed64', | |
260 TYPE_SINT32: 'sint32', | |
261 TYPE_SINT64: 'sint64', | |
262 }, | |
263 | |
264 fieldTypeName: function(field) { | |
265 var name = this.scalarTypeNames[field.type]; | |
266 if (!name) { | |
267 name = rpcExplorer.descUtil.trimPrefixDot(field.type_name); | |
268 } | |
269 return name; | |
270 } | |
271 | |
272 }) | |
Bons
2016/02/13 17:18:25
semicolon
nodir
2016/02/17 02:02:13
Done.
| |
273 </script> | |
OLD | NEW |