Commit | Line | Data |
---|---|---|
629a09ae DV |
1 | if (typeof JSDOC == "undefined") JSDOC = {}; |
2 | ||
3 | /** | |
4 | @constructor | |
5 | */ | |
6 | JSDOC.DocTag = function(src) { | |
7 | this.init(); | |
8 | if (typeof src != "undefined") { | |
9 | this.parse(src); | |
10 | } | |
11 | } | |
12 | ||
13 | /** | |
14 | Create and initialize the properties of this. | |
15 | */ | |
16 | JSDOC.DocTag.prototype.init = function() { | |
17 | this.title = ""; | |
18 | this.type = ""; | |
19 | this.name = ""; | |
20 | this.isOptional = false; | |
21 | this.defaultValue = ""; | |
22 | this.desc = ""; | |
23 | ||
24 | return this; | |
25 | } | |
26 | ||
27 | /** | |
28 | Populate the properties of this from the given tag src. | |
29 | @param {string} src | |
30 | */ | |
31 | JSDOC.DocTag.prototype.parse = function(src) { | |
32 | if (typeof src != "string") throw "src must be a string not "+(typeof src); | |
33 | ||
34 | try { | |
35 | src = this.nibbleTitle(src); | |
36 | if (JSDOC.PluginManager) { | |
37 | JSDOC.PluginManager.run("onDocTagSynonym", this); | |
38 | } | |
39 | ||
40 | src = this.nibbleType(src); | |
41 | ||
42 | // only some tags are allowed to have names. | |
43 | if (this.title == "param" || this.title == "property" || this.title == "config") { // @config is deprecated | |
44 | src = this.nibbleName(src); | |
45 | } | |
46 | } | |
47 | catch(e) { | |
48 | if (LOG) LOG.warn(e); | |
49 | else throw e; | |
50 | } | |
51 | this.desc = src; // whatever is left | |
52 | ||
53 | // example tags need to have whitespace preserved | |
54 | if (this.title != "example") this.desc = this.desc.trim(); | |
55 | ||
56 | if (JSDOC.PluginManager) { | |
57 | JSDOC.PluginManager.run("onDocTag", this); | |
58 | } | |
59 | } | |
60 | ||
61 | /** | |
62 | Automatically called when this is stringified. | |
63 | */ | |
64 | JSDOC.DocTag.prototype.toString = function() { | |
65 | return this.desc; | |
66 | } | |
67 | ||
68 | /*t: | |
69 | plan(1, "testing JSDOC.DocTag#toString"); | |
70 | ||
71 | var tag = new JSDOC.DocTag("param {object} date A valid date."); | |
72 | is(""+tag, "A valid date.", "stringifying a tag returns the desc."); | |
73 | */ | |
74 | ||
75 | /** | |
76 | Find and shift off the title of a tag. | |
77 | @param {string} src | |
78 | @return src | |
79 | */ | |
80 | JSDOC.DocTag.prototype.nibbleTitle = function(src) { | |
81 | if (typeof src != "string") throw "src must be a string not "+(typeof src); | |
82 | ||
83 | var parts = src.match(/^\s*(\S+)(?:\s([\s\S]*))?$/); | |
84 | ||
85 | if (parts && parts[1]) this.title = parts[1]; | |
86 | if (parts && parts[2]) src = parts[2]; | |
87 | else src = ""; | |
88 | ||
89 | return src; | |
90 | } | |
91 | ||
92 | /*t: | |
93 | plan(8, "testing JSDOC.DocTag#nibbleTitle"); | |
94 | ||
95 | var tag = new JSDOC.DocTag(); | |
96 | ||
97 | tag.init().nibbleTitle("aTitleGoesHere"); | |
98 | is(tag.title, "aTitleGoesHere", "a title can be found in a single-word string."); | |
99 | ||
100 | var src = tag.init().nibbleTitle("aTitleGoesHere and the rest"); | |
101 | is(tag.title, "aTitleGoesHere", "a title can be found in a multi-word string."); | |
102 | is(src, "and the rest", "the rest is returned when the title is nibbled off."); | |
103 | ||
104 | src = tag.init().nibbleTitle(""); | |
105 | is(tag.title, "", "given an empty string the title is empty."); | |
106 | is(src, "", "the rest is empty when the tag is empty."); | |
107 | ||
108 | var src = tag.init().nibbleTitle(" aTitleGoesHere\n a description"); | |
109 | is(tag.title, "aTitleGoesHere", "leading and trailing spaces are not part of the title."); | |
110 | is(src, " a description", "leading spaces (less one) are part of the description."); | |
111 | ||
112 | tag.init().nibbleTitle("a.Title::Goes_Here foo"); | |
113 | is(tag.title, "a.Title::Goes_Here", "titles with punctuation are allowed."); | |
114 | */ | |
115 | ||
116 | /** | |
117 | Find and shift off the type of a tag. | |
118 | @requires frame/String.js | |
119 | @param {string} src | |
120 | @return src | |
121 | */ | |
122 | JSDOC.DocTag.prototype.nibbleType = function(src) { | |
123 | if (typeof src != "string") throw "src must be a string not "+(typeof src); | |
124 | ||
125 | if (src.match(/^\s*\{/)) { | |
126 | var typeRange = src.balance("{", "}"); | |
127 | if (typeRange[1] == -1) { | |
128 | throw "Malformed comment tag ignored. Tag type requires an opening { and a closing }: "+src; | |
129 | } | |
130 | this.type = src.substring(typeRange[0]+1, typeRange[1]).trim(); | |
131 | this.type = this.type.replace(/\s*,\s*/g, "|"); // multiples can be separated by , or | | |
132 | src = src.substring(typeRange[1]+1); | |
133 | } | |
134 | ||
135 | return src; | |
136 | } | |
137 | ||
138 | /*t: | |
139 | plan(5, "testing JSDOC.DocTag.parser.nibbleType"); | |
140 | requires("../frame/String.js"); | |
141 | ||
142 | var tag = new JSDOC.DocTag(); | |
143 | ||
144 | tag.init().nibbleType("{String[]} aliases"); | |
145 | is(tag.type, "String[]", "type can have non-alpha characters."); | |
146 | ||
147 | tag.init().nibbleType("{ aTypeGoesHere } etc etc"); | |
148 | is(tag.type, "aTypeGoesHere", "type is trimmed."); | |
149 | ||
150 | tag.init().nibbleType("{ oneType, twoType ,\n threeType } etc etc"); | |
151 | is(tag.type, "oneType|twoType|threeType", "multiple types can be separated by commas."); | |
152 | ||
153 | var error; | |
154 | try { tag.init().nibbleType("{widget foo"); } | |
155 | catch(e) { error = e; } | |
156 | is(typeof error, "string", "malformed tag type throws error."); | |
157 | isnt(error.indexOf("Malformed"), -1, "error message tells tag is malformed."); | |
158 | */ | |
159 | ||
160 | /** | |
161 | Find and shift off the name of a tag. | |
162 | @requires frame/String.js | |
163 | @param {string} src | |
164 | @return src | |
165 | */ | |
166 | JSDOC.DocTag.prototype.nibbleName = function(src) { | |
167 | if (typeof src != "string") throw "src must be a string not "+(typeof src); | |
168 | ||
169 | src = src.trim(); | |
170 | ||
171 | // is optional? | |
172 | if (src.charAt(0) == "[") { | |
173 | var nameRange = src.balance("[", "]"); | |
174 | if (nameRange[1] == -1) { | |
175 | throw "Malformed comment tag ignored. Tag optional name requires an opening [ and a closing ]: "+src; | |
176 | } | |
177 | this.name = src.substring(nameRange[0]+1, nameRange[1]).trim(); | |
178 | this.isOptional = true; | |
179 | ||
180 | src = src.substring(nameRange[1]+1); | |
181 | ||
182 | // has default value? | |
183 | var nameAndValue = this.name.split("="); | |
184 | if (nameAndValue.length) { | |
185 | this.name = nameAndValue.shift().trim(); | |
186 | this.defaultValue = nameAndValue.join("="); | |
187 | } | |
188 | } | |
189 | else { | |
190 | var parts = src.match(/^(\S+)(?:\s([\s\S]*))?$/); | |
191 | if (parts) { | |
192 | if (parts[1]) this.name = parts[1]; | |
193 | if (parts[2]) src = parts[2].trim(); | |
194 | else src = ""; | |
195 | } | |
196 | } | |
197 | ||
198 | return src; | |
199 | } | |
200 | ||
201 | /*t: | |
202 | requires("../frame/String.js"); | |
203 | plan(9, "testing JSDOC.DocTag.parser.nibbleName"); | |
204 | ||
205 | var tag = new JSDOC.DocTag(); | |
206 | ||
207 | tag.init().nibbleName("[foo] This is a description."); | |
208 | is(tag.isOptional, true, "isOptional syntax is detected."); | |
209 | is(tag.name, "foo", "optional param name is found."); | |
210 | ||
211 | tag.init().nibbleName("[foo] This is a description."); | |
212 | is(tag.isOptional, true, "isOptional syntax is detected when no type."); | |
213 | is(tag.name, "foo", "optional param name is found when no type."); | |
214 | ||
215 | tag.init().nibbleName("[foo=7] This is a description."); | |
216 | is(tag.name, "foo", "optional param name is found when default value."); | |
217 | is(tag.defaultValue, 7, "optional param default value is found when default value."); | |
218 | ||
219 | //tag.init().nibbleName("[foo= a value] This is a description."); | |
220 | //is(tag.defaultValue, " a value", "optional param default value is found when default value has spaces (issue #112)."); | |
221 | ||
222 | tag.init().nibbleName("[foo=[]] This is a description."); | |
223 | is(tag.defaultValue, "[]", "optional param default value is found when default value is [] (issue #95)."); | |
224 | ||
225 | tag.init().nibbleName("[foo=a=b] This is a description."); | |
226 | is(tag.name, "foo", "optional param name is found when default value is a=b."); | |
227 | is(tag.defaultValue, "a=b", "optional param default value is found when default value is a=b.") | |
228 | */ | |
229 | ||
230 | /*t: | |
231 | plan(32, "Testing JSDOC.DocTag.parser."); | |
232 | requires("../frame/String.js"); | |
233 | ||
234 | var tag = new JSDOC.DocTag(); | |
235 | ||
236 | is(typeof tag, "object", "JSDOC.DocTag.parser with an empty string returns an object."); | |
237 | is(typeof tag.title, "string", "returned object has a string property 'title'."); | |
238 | is(typeof tag.type, "string", "returned object has a string property 'type'."); | |
239 | is(typeof tag.name, "string", "returned object has a string property 'name'."); | |
240 | is(typeof tag.defaultValue, "string", "returned object has a string property 'defaultValue'."); | |
241 | is(typeof tag.isOptional, "boolean", "returned object has a boolean property 'isOptional'."); | |
242 | is(typeof tag.desc, "string", "returned object has a string property 'desc'."); | |
243 | ||
244 | tag = new JSDOC.DocTag("param {widget} foo"); | |
245 | is(tag.title, "param", "param title is found."); | |
246 | is(tag.name, "foo", "param name is found when desc is missing."); | |
247 | is(tag.desc, "", "param desc is empty when missing."); | |
248 | ||
249 | tag = new JSDOC.DocTag("param {object} date A valid date."); | |
250 | is(tag.name, "date", "param name is found with a type."); | |
251 | is(tag.type, "object", "param type is found."); | |
252 | is(tag.desc, "A valid date.", "param desc is found with a type."); | |
253 | ||
254 | tag = new JSDOC.DocTag("param aName a description goes\n here."); | |
255 | is(tag.name, "aName", "param name is found without a type."); | |
256 | is(tag.desc, "a description goes\n here.", "param desc is found without a type."); | |
257 | ||
258 | tag = new JSDOC.DocTag("param {widget}"); | |
259 | is(tag.name, "", "param name is empty when it is not given."); | |
260 | ||
261 | tag = new JSDOC.DocTag("param {widget} [foo] This is a description."); | |
262 | is(tag.name, "foo", "optional param name is found."); | |
263 | ||
264 | tag = new JSDOC.DocTag("return {aType} This is a description."); | |
265 | is(tag.type, "aType", "when return tag has no name, type is found."); | |
266 | is(tag.desc, "This is a description.", "when return tag has no name, desc is found."); | |
267 | ||
268 | tag = new JSDOC.DocTag("author Joe Coder <jcoder@example.com>"); | |
269 | is(tag.title, "author", "author tag has a title."); | |
270 | is(tag.type, "", "the author tag has no type."); | |
271 | is(tag.name, "", "the author tag has no name."); | |
272 | is(tag.desc, "Joe Coder <jcoder@example.com>", "author tag has desc."); | |
273 | ||
274 | tag = new JSDOC.DocTag("private \t\n "); | |
275 | is(tag.title, "private", "private tag has a title."); | |
276 | is(tag.type, "", "the private tag has no type."); | |
277 | is(tag.name, "", "the private tag has no name."); | |
278 | is(tag.desc, "", "private tag has no desc."); | |
279 | ||
280 | tag = new JSDOC.DocTag("example\n example(code);\n more();"); | |
281 | is(tag.desc, " example(code);\n more();", "leading whitespace (less one) in examples code is preserved."); | |
282 | ||
283 | tag = new JSDOC.DocTag("param theName \n"); | |
284 | is(tag.name, "theName", "name only is found."); | |
285 | ||
286 | tag = new JSDOC.DocTag("type theDesc \n"); | |
287 | is(tag.desc, "theDesc", "desc only is found."); | |
288 | ||
289 | tag = new JSDOC.DocTag("type {theType} \n"); | |
290 | is(tag.type, "theType", "type only is found."); | |
291 | ||
292 | tag = new JSDOC.DocTag(""); | |
293 | is(tag.title, "", "title is empty when tag is empty."); | |
294 | */ |