OLD | NEW |
---|---|
(Empty) | |
1 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
hirono
2014/10/03 10:10:42
nit: 2014?
| |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 """ | |
5 Generator that produces an externs file for the Closure Compiler. | |
6 | |
7 See https://developers.google.com/closure/compiler/docs/api-tutorial3#externs | |
8 """ | |
9 | |
10 from code import Code | |
11 from model import * | |
12 from schema_util import * | |
13 | |
14 import os | |
15 from datetime import datetime | |
16 | |
17 LICENSE = ("""// Copyright %s The Chromium Authors. All rights reserved. | |
18 // Use of this source code is governed by a BSD-style license that can be | |
19 // found in the LICENSE file. | |
20 """ % datetime.now().year) | |
21 | |
22 class JsExternsGenerator(object): | |
23 def __init__(self): | |
24 pass | |
25 | |
26 def Generate(self, namespace): | |
27 return _Generator(namespace).Generate() | |
28 | |
29 class _Generator(object): | |
30 def __init__(self, namespace): | |
31 self._namespace = namespace | |
32 | |
33 def Generate(self): | |
34 """Generates a Code object with the .dart for the entire namespace. | |
35 """ | |
36 c = Code() | |
37 (c.Append(LICENSE) | |
38 .Append() | |
39 .Append('/** @fileoverview Externs generated from namespace: %s */' % | |
40 self._namespace.name) | |
41 .Append()) | |
42 | |
43 for type_ in self._namespace.types.values(): | |
44 c.Cblock(self._GenerateType(type_)) | |
45 | |
46 c.Cblock(self._GenerateNamespaceObject()) | |
47 | |
48 for function in self._namespace.functions.values(): | |
49 c.Cblock(self._GenerateFunction(function)) | |
50 | |
51 for event in self._namespace.events.values(): | |
52 c.Cblock(self._GenerateEvent(event)) | |
53 | |
54 return c | |
55 | |
56 def _GenerateType(self, type_): | |
57 """Given a Type object, returns the Code for this type's definition. | |
58 | |
59 """ | |
60 c = Code() | |
61 | |
62 # Since enums are just treated as strings for now, don't generate their | |
63 # type. | |
64 if type_.property_type is PropertyType.ENUM: | |
65 return c | |
66 | |
67 c.Concat(self._GenerateTypeJsDoc(type_)) | |
68 | |
69 if self._IsTypeConstructor(type_): | |
70 c.Append('var ' + type_.simple_name + ' = function();') | |
hirono
2014/10/03 10:10:42
Is it OK dropping arguments of constructor?
Vitaly Pavlenko
2014/10/03 23:33:44
You're right, that's a mistake. By the way, will y
| |
71 else: | |
72 c.Append('var ' + type_.simple_name + ';') | |
73 | |
74 return c | |
75 | |
76 def _IsTypeConstructor(self, type_): | |
77 """Returns true if the given type should be a @constructor. If this returns | |
78 false, the type is a typedef. | |
79 """ | |
80 return any(prop.type_.property_type is PropertyType.FUNCTION | |
81 for prop in type_.properties.values()) | |
82 | |
83 def _GenerateTypeJsDoc(self, type_): | |
84 """Generates the documentation for a type as a Code. | |
85 | |
86 Returns an empty code object if the object has no documentation. | |
87 """ | |
88 c = Code() | |
89 c.Append('/**') | |
90 | |
91 if type_.description: | |
92 for line in type_.description.split('\n'): | |
93 c.Comment(line, comment_prefix=' * ') | |
94 | |
95 if self._IsTypeConstructor(type_): | |
96 c.Comment('@constructor', comment_prefix = ' * ') | |
97 else: | |
98 c.Concat(self._GenerateTypedef(type_.properties)) | |
99 | |
100 c.Append(' */') | |
101 return c | |
102 | |
103 def _GenerateTypedef(self, properties): | |
104 """Given an OrderedDict of properties, returns a Code containing a @typedef. | |
105 """ | |
106 if not properties: return Code() | |
107 | |
108 lines = [] | |
109 lines.append('@typedef {{') | |
110 for field, property_ in properties.items(): | |
111 js_type = self._TypeToJsType(property_.type_) | |
112 if property_.optional: | |
113 js_type = '(%s|undefined)' % js_type | |
114 lines.append(' %s: %s,' % (field, js_type)) | |
115 | |
116 # remove last trailing comma | |
117 lines[-1] = lines[-1][:-1] | |
118 lines.append('}}') | |
119 # TODO(tbreisacher): Add '@see <link to documentation>'. | |
120 | |
121 lines = [' * ' + line for line in lines] | |
122 codeString = '\n'.join(lines) | |
Tyler Breisacher (Chromium)
2015/02/25 00:22:05
Can probably inline this?
codeString = '\n'.join(
| |
123 c = Code() | |
124 c.Append(codeString) | |
125 return c | |
126 | |
127 def _GenerateFunctionJsDoc(self, function): | |
128 """Generates the documentation for a function as a Code. | |
129 | |
130 Returns an empty code object if the object has no documentation. | |
131 """ | |
132 c = Code() | |
133 c.Append('/**') | |
134 | |
135 if function.description: | |
136 for line in function.description.split('\n'): | |
137 c.Comment(line, comment_prefix=' * ') | |
138 | |
139 for param in function.params: | |
140 js_type = self._TypeToJsType(param.type_) | |
141 | |
142 if param.optional: | |
143 js_type += '=' | |
144 | |
145 param_doc = '@param {%s} %s %s' % (js_type, | |
146 param.name, | |
147 param.description or '') | |
148 c.Comment(param_doc, comment_prefix=' * ') | |
149 | |
150 if function.callback: | |
151 # TODO(tbreisacher): Convert Function to function() for better | |
152 # typechecking. | |
153 js_type = 'Function' | |
154 if function.callback.optional: | |
155 js_type += '=' | |
156 param_doc = '@param {%s} %s %s' % (js_type, | |
157 function.callback.name, | |
158 function.callback.description or '') | |
159 c.Comment(param_doc, comment_prefix=' * ') | |
160 | |
161 if function.returns: | |
162 return_doc = '@return {%s} %s' % (self._TypeToJsType(function.returns), | |
163 function.returns.description) | |
164 c.Comment(return_doc, comment_prefix=' * ') | |
165 | |
166 c.Append(' */') | |
167 return c | |
168 | |
169 def _TypeToJsType(self, type_): | |
170 """Converts a model.Type to a JS type (number, Array, etc.)""" | |
171 if type_.property_type in (PropertyType.INTEGER, PropertyType.DOUBLE): | |
172 return 'number' | |
173 elif type_.property_type is PropertyType.OBJECT: | |
174 return 'Object' | |
175 elif type_.property_type is PropertyType.ARRAY: | |
176 return 'Array' | |
177 elif type_.property_type is PropertyType.REF: | |
178 return type_.ref_type | |
179 elif type_.property_type.is_fundamental: | |
180 return type_.property_type.name | |
181 else: | |
182 return '?' # TODO make this more specific | |
Tyler Breisacher (Chromium)
2015/02/25 00:22:05
put someone's name on this TODO. I reluctantly vol
| |
183 | |
184 def _GenerateFunction(self, function): | |
185 """Generates the code representing a function, including its documentation. | |
186 For example: | |
187 | |
188 /** | |
189 * @param {string} title The new title. | |
190 */ | |
191 chrome.window.setTitle = function(title) {}; | |
192 """ | |
193 c = Code() | |
194 params = self._GenerateFunctionParams(function) | |
195 (c.Concat(self._GenerateFunctionJsDoc(function)) | |
196 .Append('chrome.%s.%s = function(%s) {};' % (self._namespace.name, | |
197 function.name, | |
198 params)) | |
199 ) | |
200 return c | |
201 | |
202 def _GenerateEvent(self, event): | |
203 """Generates the code representing an event. | |
204 For example: | |
205 | |
206 /** @type {!ChromeEvent} */ | |
207 chrome.bookmarks.onChildrenReordered; | |
208 """ | |
209 c = Code() | |
210 (c.Append('/** @type {!ChromeEvent} */') | |
211 .Append('chrome.%s.%s;' % (self._namespace.name, event.name))) | |
212 return c | |
213 | |
214 def _GenerateNamespaceObject(self): | |
215 """Generates the code creating namespace object. | |
216 For example: | |
217 | |
218 /** | |
219 * @const | |
220 */ | |
221 chrome.bookmarks = {}; | |
222 """ | |
223 c = Code() | |
224 (c.Append("""/** | |
225 * @const | |
226 */""") | |
227 .Append('chrome.%s = {};' % self._namespace.name)) | |
228 return c | |
229 | |
230 def _GenerateFunctionParams(self, function): | |
231 params = function.params[:] | |
232 if function.callback: | |
233 params.append(function.callback) | |
234 return ', '.join(param.name for param in params) | |
OLD | NEW |