-
Notifications
You must be signed in to change notification settings - Fork 240
Custom member types
There are two broad categories of member types we can create:
-
:method_like
- such members have parameters and possibly a return value. Things like methods and events. -
:property_like
- such members have a type and possibly some kind of (default) value. Things like properties and configs.
Method-like members are way easier to implement, so we start with them.
Lets implement a @hook
tag to describe a special class of functions
that will provide a way to hook into the internals of a class and
extend it. For example:
/**
* @hook before
* Runs before the component is initialized.
* @param {Object} self The component itself.
* @param {Object} cfg Config options passed in constructor.
*/
An implementation for this is quite simple:
require "jsduck/tag/tag"
class Hook < JsDuck::Tag::Tag
def initialize
@tagname = :hook
@pattern = "hook"
@member_type = {
:name => :hook,
:category => :method_like,
:title => "Hooks",
:position => MEMBER_POS_CFG - 0.1,
}
end
def parse_doc(scanner)
return {
:tagname => :hook,
:name => scanner.ident,
}
end
def process_doc(context, hook_tags, position)
context[:name] = hook_tags[0][:name]
end
end
The thing that turns this tag into a new member type is the setting of
@member_type
variable. There we give a :name
to our new type,
specify our chosen :category
, and set a few rendering options.
:position
defines the ordering of member sections in the final
page. Here we position hooks at the very top - before configs. The
title is shown at the top of each members section and is also used as
a title for toolbar button.
parse_doc
and process_doc
are fairly trivial. We parse the name
of the hook using builtin scanner.ident
method, and assign the value
to :name
field in context
hash. The :name
field is a special
field that every member type must have - it gets further processed
internally for JSDuck.
The result will look like this:
Now let's tackle a property-like member type. We'll define a
@constant
tag which indeed will work just like a @property
, but
with the semantic difference of documenting unchangable values. For
example:
/**
* @constant {Number} [GRAVITY=9.80665]
* Acceleration of objects in Earth's gravitational field.
*/
That's really exactly the same syntax that builtin @cfg
and
@property
use. Here's our implementation:
require "jsduck/tag/tag"
class Constant < JsDuck::Tag::Tag
def initialize
@tagname = :constant
@pattern = "constant"
@member_type = {
:name => :constant,
:category => :property_like,
:title => "Constants",
:position => MEMBER_POS_CFG - 0.1,
}
end
def parse_doc(scanner)
scanner.standard_tag({:tagname => :constant, :type => true, :name => true})
end
def process_doc(h, tags, pos)
p = tags[0]
h[:name] = p[:name]
h[:type] = p[:type]
h[:default] = p[:default]
end
end
Now, instead of implementing all the complex parsing logic for
extracting the type, name and value, we simply reuse the builtin
mechanism form parsing such tags by calling the scanner.standard_tag
method, which will parse the type and name parts of our tag and return
the following hash:
{
:tagname => :constant,
:type => "Number",
:name => "GRAVITY",
:default => "9.80665"
}
In process_doc
we assign those values separately to the context
hash and JSDuck will already know how to treat them.
The result looks like this: