Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add JSDoc example #8533

Draft
wants to merge 19 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ node_modules/
/playwright-report/
/playwright/.cache/
$__StoryList.tid
types/core/
62 changes: 62 additions & 0 deletions core/modules/parsers/base.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*\
title: $:/core/modules/parsers/base.js
type: application/javascript
module-type: library
\*/

/**
* Represents an attribute in a parse tree node
*
* @typedef {Object} ParseTreeAttribute
* @property {number} [end] - End position of attribute in source text
* @property {string} [name] - Name of attribute
* @property {number} [start] - Start position of attribute in source text
* @property {'string' | 'number' | 'bigint' | 'boolean' | 'macro' | 'macro-parameter'} type - Type of attribute
* @property {string | IMacroCallParseTreeNode} value - Actual value of attribute
*/

/**
* Base structure for a parse node
*
* @typedef {Object} ParseTreeNode
* @property {string} type - Type of widget that will render this node
* @property {string} rule - Parse rule that generated this node. One rule can generate multiple types of nodes
* @property {number} start - Rule start marker in source text
* @property {number} end - Rule end marker in source text
* @property {Record<string,ParseTreeAttribute>} [attributes] - Attributes of widget
* @property {ParseTreeNode[]} [children] - Array of child parse nodes
*/

/**
* Base class for parsers. This only provides typing
*
* @class
* @param {string} type - Content type of text to be parsed
* @param {string} text - Text to be parsed
* @param {Object} options - Parser options
* @param {boolean} [options.parseAsInline=false] - If true, text will be parsed as an inline run
* @param {Object} options.wiki - Reference to wiki store in use
* @param {string} [options._canonical_uri] - Optional URI of content if text is missing or empty
* @param {boolean} [options.configTrimWhiteSpace=false] - If true, parser trims white space
*/
function Parser(type, text, options) {
/**
* Result AST
* @type {ParseTreeNode[]}
*/
this.tree = [];

/**
* Original text without modifications
* @type {string}
*/
this.source = text;

/**
* Source content type in MIME format
* @type {string}
*/
this.type = type;
}

exports.Parser = Parser;
27 changes: 27 additions & 0 deletions core/modules/parsers/wikiparser/rules/codeblock.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,27 @@ Wiki text rule for code blocks. For example:
```

\*/

/**
* @typedef {import("$:/core/modules/parsers/base.js").ParseTreeAttribute} ParseTreeAttribute
* @typedef {import('../wikirulebase.js').WikiRuleBase} WikiRuleBase
* @typedef {import('../../base.js').Parser} Parser
* @typedef {typeof exports & WikiRuleBase} ThisRule
*/

/**
* Represents the `codeblock` rule.
*
* @typedef {Object} ParseTreeCodeblockNode
* @property {"codeblock"} rule
* @property {"codeblock"} type
* @property {number} start
* @property {number} end
* @property {Object} attributes
* @property {ParseTreeAttribute} attributes.code
* @property {ParseTreeAttribute} attributes.language
*/

Copy link
Member

@pmario pmario Sep 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO having @property {string} type and then - The type of the widget, which is "codeblock". is the same thing. Both elements describe: "What it is". -- I think the second part should describe: "What it does", otherwise we can skip it.

  • @Property does describe -- What it is
  • Text describes -- What it does

eg:

/**
 * Represents the parser `codeblock` rule.
 * 
 * @typedef {Object} CodeblockNode
 * @property {string} rule - Always "codeblock"
 * @property {string} type - Always "codeblock"
 * @property {number} start - Rule start marker in source text
 * @property {number} end - Rule end marker in source text
 * @property {Object} attributes - Contains "code" and "language" objects
 * @property {Object} attributes.code - The code attribute object
 * @property {string} attributes.code.type - Always "string"
 * @property {string} attributes.code.value - Actual code
 * @property {number} attributes.code.start - Start position of code in source text
 * @property {number} attributes.code.end - End position of code in source text
 * @property {Object} attributes.language - The language attribute object
 * @property {string} attributes.language.type - Always "string"
 * @property {string} attributes.language.value - Language specified after the triple backticks, if any
 * @property {number} attributes.language.start - Start position of the string in source text
 * @property {number} attributes.language.end - End position of the string in source text
 */

Should be as short as possible and still valid. Especially see type, rule, start, end which are properties of "CodeblockNode"

I did remove the "full stops" from all @ elements. They are not needed
The first intro sentence has a full stop.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I'd copy that.

I have to admit, all jsdoc are generated by github copilot. I don't have time for this detail, I will leave it for future refinement. I'm still debugging the type when I'm using it in my plugin project.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have to admit, all jsdoc are generated by github copilot. I don't have time for this detail, I will leave it for future refinement. I'm still debugging the type when I'm using it in my plugin project.

The new type information will significantly increase the TW code-size. So if there is redundant information it should be removed.

And even more important it has to be valid. So if the co-pilot only adds comments in a more verbose form than the parameters are. There should not be any comments at all -- They have no value.

So if there are no comments, we actually know, that we have to add them manually. IMO for the tooltips it would be OK to start with the type info.

Copy link
Contributor Author

@linonetwo linonetwo Sep 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will significantly increase the TW code-size

we must remove comments before publishing HTML wiki. I think these JSDoc will basically double the size.

it has to be valid

This won't be a big problem, because 1. I use the method body as input too. And 2. we can auto merge "comment" type of PR based on #7542 , so people can update comment quickly. I find this is quite frequent when maintaining https://github.com/tiddly-gittly/TW5-Typed

(function(){

/*jslint node: true, browser: true */
Expand All @@ -27,6 +48,12 @@ exports.init = function(parser) {
this.matchRegExp = /```([\w-]*)\r?\n/mg;
};

/**
* Parses the code block and returns an array of `codeblock` widgets.
*
* @this {ThisRule}
* @returns {ParseTreeCodeblockNode[]} An array containing a single codeblock widget object.
*/
exports.parse = function() {
var reEnd = /(\r?\n```$)/mg;
var languageStart = this.parser.pos + 3,
Expand Down
58 changes: 54 additions & 4 deletions core/modules/parsers/wikiparser/rules/html.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,34 @@ This is a widget invocation
}}}

\*/


/**
* @typedef {import("$:/core/modules/parsers/base.js").ParseTreeAttribute} ParseTreeAttribute
* @typedef {import('../wikirulebase.js').WikiRuleBase} WikiRuleBase
* @typedef {import('../../base.js').Parser} Parser
* @typedef {typeof exports & WikiRuleBase & {nextTag?:ParseTreeHtmlNode;}} ThisRule
*/

/**
* Represents the parser `html` rule
*
* @typedef {Object} ParseTreeHtmlNode
* @property {"html"} rule
* @property {"element"} type
* @property {keyof HTMLElementTagNameMap} tag
* @property {number} start
* @property {number} end
* @property {Record<string,ParseTreeAttribute>} attributes - Contains attributes of HTML element
* @property {boolean} isSelfClosing - If tag is self-closing
* @property {boolean} isBlock - If tag is a block element
* @property {number} openTagStart
* @property {number} openTagEnd
* @property {number} closeTagStart
* @property {number} closeTagEnd
* @property {ParseTreeHtmlNode[]} [children]
*/

(function(){

/*jslint node: true, browser: true */
Expand All @@ -26,10 +54,18 @@ This is a widget invocation
exports.name = "html";
exports.types = {inline: true, block: true};

/**
* @param {Parser} parser
*/
exports.init = function(parser) {
this.parser = parser;
};

/**
* @this {ThisRule}
* @param {number} startPos
* @returns {number | undefined} Start position of next HTML tag
*/
exports.findNextMatch = function(startPos) {
// Find the next tag
this.nextTag = this.findNextTag(this.parser.source,startPos,{
Expand All @@ -38,11 +74,16 @@ exports.findNextMatch = function(startPos) {
return this.nextTag ? this.nextTag.start : undefined;
};

/*
Parse the most recent match
*/
/**
* Parse most recent match
* @this {ThisRule}
* @returns {ParseTreeHtmlNode[]} Array containing parsed HTML tag object
*/
exports.parse = function() {
// Retrieve the most recent match so that recursive calls don't overwrite it
/**
* @type {ParseTreeHtmlNode}
* Retrieve the most recent match so that recursive calls don't overwrite it
*/
var tag = this.nextTag;
if (!tag.isSelfClosing) {
tag.openTagStart = tag.start;
Expand Down Expand Up @@ -161,6 +202,15 @@ exports.parseTag = function(source,pos,options) {
return node;
};

/**
* Find the next HTML tag in the source
*
* @this {ThisRule}
* @param {string} source
* @param {number} pos - Position to start searching from
* @param {Object} options
* @returns {Object|null} Parsed tag object or null if no valid tag is found
*/
exports.findNextTag = function(source,pos,options) {
// A regexp for finding candidate HTML tags
var reLookahead = /<([a-zA-Z\-\$\.]+)/g;
Expand Down
29 changes: 17 additions & 12 deletions core/modules/parsers/wikiparser/wikiparser.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,18 @@ Attributes are stored as hashmaps of the following objects:
/*global $tw: false */
"use strict";

/*
type: content type of text
text: text to be parsed
options: see below:
parseAsInline: true to parse text as inline instead of block
wiki: reference to wiki to use
_canonical_uri: optional URI of content if text is missing or empty
configTrimWhiteSpace: true to trim whitespace
*/
var WikiParser = function(type,text,options) {
/**
* @typedef {import('../base').Parser} Parser
*/

/**
* WikiParser class for parsing text of a specified MIME type.
*
* @class
* @extends {Parser}
* @constructor
*/
function WikiParser(type,text,options) {
this.wiki = options.wiki;
var self = this;
// Check for an externally linked tiddler
Expand Down Expand Up @@ -99,8 +101,11 @@ var WikiParser = function(type,text,options) {
// Return the parse tree
};

/*
*/
/**
* Load a remote tiddler from a given URL.
*
* @param {string} url - The URL of the remote tiddler to load.
*/
WikiParser.prototype.loadRemoteTiddler = function(url) {
var self = this;
$tw.utils.httpRequest({
Expand Down
44 changes: 34 additions & 10 deletions core/modules/parsers/wikiparser/wikirulebase.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,46 @@ Base class for wiki parser rules
/*global $tw: false */
"use strict";

/*
This constructor is always overridden with a blank constructor, and so shouldn't be used
*/
var WikiRuleBase = function() {
/**
* @typedef {import('../base').Parser} Parser
*/

/**
* Base class for wiki rules.
* This constructor is always overridden with a blank constructor, and so shouldn't be used
*
* @class
* @constructor
*/
function WikiRuleBase() {
/**
* Inject by parser
* @type {Record<"pragma"|"block"|"inline", boolean>}
*/
this.is = {};
/**
* @type {RegExp}
*/
this.matchRegExp;
};

/*
To be overridden by individual rules
*/
/**
* Initialize rule with given parser instance
* To be overridden by individual rules
*
* @param {Parser} parser - Parser instance to initialize with
*/
WikiRuleBase.prototype.init = function(parser) {
this.parser = parser;
};

/*
Default implementation of findNextMatch uses RegExp matching
*/
/**
* Default implementation of findNextMatch uses RegExp matching
* Find next match in source starting from given position using RegExp matching
*
* @param {number} startPos - Position to start searching from
* @returns {number|undefined} Index of next match or undefined if no match is found
*/
WikiRuleBase.prototype.findNextMatch = function(startPos) {
this.matchRegExp.lastIndex = startPos;
this.match = this.matchRegExp.exec(this.parser.source);
Expand Down
43 changes: 27 additions & 16 deletions core/modules/widgets/widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,30 @@ Widget base class
/*global $tw: false */
"use strict";

/*
Create a widget object for a parse tree node
parseTreeNode: reference to the parse tree node to be rendered
options: see below
Options include:
wiki: mandatory reference to wiki associated with this render tree
parentWidget: optional reference to a parent renderer node for the context chain
document: optional document object to use instead of global document
*/
var Widget = function(parseTreeNode,options) {
/**
* Widget class for creating a widget object for a parse tree node.
*
* @class
* @constructor
* @param {Object} parseTreeNode - Reference to the parse tree node to be rendered.
* @param {Object} options - Options for the widget.
* @param {Object} options.wiki - Mandatory reference to the wiki associated with this render tree.
* @param {Widget} [options.parentWidget] - Optional reference to a parent renderer node for the context chain.
* @param {Document} [options.document] - Optional document object to use instead of the global document.
*/
function Widget(parseTreeNode,options) {
this.initialise(parseTreeNode,options);
};

/*
Initialise widget properties. These steps are pulled out of the constructor so that we can reuse them in subclasses
*/
/**
* Initialise widget properties. These steps are pulled out of the constructor so that we can reuse them in subclasses.
*
* @param {Object} parseTreeNode - Reference to the parse tree node to be rendered.
* @param {Object} options - Options for the widget.
* @param {Object} options.wiki - Mandatory reference to the wiki associated with this render tree.
* @param {Widget} [options.parentWidget] - Optional reference to a parent renderer node for the context chain.
* @param {Document} [options.document] - Optional document object to use instead of the global document.
*/
Widget.prototype.initialise = function(parseTreeNode,options) {
// Bail if parseTreeNode is undefined, meaning that the widget constructor was called without any arguments so that it can be subclassed
if(parseTreeNode === undefined) {
Expand Down Expand Up @@ -64,9 +72,12 @@ Widget.prototype.initialise = function(parseTreeNode,options) {
}
};

/*
Render this widget into the DOM
*/
/**
* Render this widget into the DOM.
*
* @param {Element} parent - The parent DOM node to render into.
* @param {Element} nextSibling - The next sibling DOM node to render before.
*/
Widget.prototype.render = function(parent,nextSibling) {
this.parentDomNode = parent;
this.execute();
Expand Down
Loading
Loading