Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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 @@ -35,6 +35,7 @@ parser/.env*

# Parsed
builder/src/parsed.json
parser/parsed.json

# build deno files
*.exe
Expand Down
11 changes: 11 additions & 0 deletions parser/examples/wikis/test_wiki.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-- @wiki Example UI: 3D2D Test UI
-- This example shows how to create and populate a 3D2D CNUI panel infront of the player.
--
-- @header CNUI Example: 3D2D Test UI
-- @text This example shows how to create and populate a 3D2D CNUI panel infront of the player.
--
-- @code
concommand.Add("cnui_test_3d2d", function()

end)
-- @code
23 changes: 19 additions & 4 deletions parser/src/classes/Parser/Tags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,13 +188,28 @@ export default class Tags {
);
}

const rich = (this.tags[tag_name] || this.alias[tag_name] || this.multiBlockAlias[tag_name]).rich;
const tagDef = this.tags[tag_name] || this.alias[tag_name] || this.multiBlockAlias[tag_name];
const rich = tagDef.rich;
// For alias tags (including multiBlockAlias), preserve newlines so the Tag.process
// regex can correctly extract only the first line as the argument
const isAlias = tagDef.an_alias;

let match: string;
if (rich) {
match = tag_match[0].replace(rich_trim_re, "");
} else if (isAlias) {
// For alias tags, only trim left whitespace but preserve newlines
// The Tag.process regex will extract only the content on the first line
match = tag_match[0].replace(trim_left_re, "");
} else {
match = tag_match[0]
.replace(trim_left_re, "")
.replace(trim_right_re, "$1 $2");
}

const tag_info: TagInfo = {
line: line,
match: rich ? tag_match[0].replace(rich_trim_re, "") : tag_match[0]
.replace(trim_left_re, "")
.replace(trim_right_re, "$1 $2"),
match: match,
from_alias: from_alias,
};

Expand Down
8 changes: 5 additions & 3 deletions parser/src/classes/Project/CategoryProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import SelectorTag from "../Tags/SelectorTag.ts";
import Tag from "../Tags/Tag.ts";
import BooleanTag from "../Tags/BooleanTag.ts";
import BlockTag from "../Tags/BlockTag.ts";
import WikiAliasTag from "../Tags/WikiAliasTag.ts";

export interface ProjectStructure {
[key: string]: Category;
Expand Down Expand Up @@ -90,13 +91,14 @@ export default class CategoryProject {
false,
true,
);
// Wikis category - don't pass tag_name to avoid standard alias creation
// We use WikiAliasTag which handles name and description extraction
this.add_category(
new Category("Wikis", "wiki"),
false,
"wiki",
false,
true,
);
// Add custom wiki alias that extracts both name and description
this.add_tag(new WikiAliasTag());

/* Default categories that define types. */
this.add_category_type("Classes");
Expand Down
11 changes: 9 additions & 2 deletions parser/src/classes/Tags/Tag.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { escape_regex } from "../../utils/regex.ts";

// Pattern for capturing the rest of the line including multiline content (used with s flag for rich tags)
const restRe = "(?: |\\t)+(.+)$";
const optionalRestRe = "(?:(?: |\\t)+(.+))?$";
// Pattern for capturing only single-line content (does not match newlines)
const restReSingleLine = "(?: |\\t)+([^\\n\\r\\u2028\\u2029]+)";
const optionalRestReSingleLine = "(?:(?: |\\t)+([^\\n\\r\\u2028\\u2029]+))?";
const middleArgRe =
"(?:(?:(?: |\\t)+\"((?:[^\\\\]|\\\\.)*?)\")|(?:(?: |\\t)+'((?:[^\\\\]|\\\\.)*?)')|(?: |\\t)+([^ \\t]+))";
export default class Tag {
Expand All @@ -16,13 +20,16 @@ export default class Tag {
public readonly optionalLastArg: boolean = false,
) {
const tagRe = `^(?: |\\t)*@${escape_regex(name)}`;
// Use single-line patterns for non-rich tags to prevent capturing multiline content as the argument
const useRestRe = rich ? restRe : restReSingleLine;
const useOptionalRestRe = rich ? optionalRestRe : optionalRestReSingleLine;
if (argsCount == 0) {
this.re = RegExp(tagRe + "$", "s");
} else if (argsCount == 1) {
this.re = RegExp(tagRe + (optionalLastArg ? optionalRestRe : restRe), "s");
this.re = RegExp(tagRe + (optionalLastArg ? useOptionalRestRe : useRestRe), "s");
} else {
this.re = RegExp(
tagRe + middleArgRe.repeat(argsCount - 1) + (optionalLastArg ? optionalRestRe : restRe),
tagRe + middleArgRe.repeat(argsCount - 1) + (optionalLastArg ? useOptionalRestRe : useRestRe),
"s",
);
}
Expand Down
38 changes: 38 additions & 0 deletions parser/src/classes/Tags/WikiAliasTag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import AliasTag from "./AliasTag.ts";

/**
* A specialized alias tag for wiki pages that extracts both the name
* (from the first line) and description (from subsequent lines).
*/
export default class WikiAliasTag extends AliasTag {
constructor() {
// Use argsCount=1 to extract just the name from the first line
// The description will be extracted in the convert method
// unique=true means only one @wiki tag per block is allowed
super("wiki", /* argsCount */ 1, /* callback */ [], /* unique */ true);
}

convert(string: string): string[] {
// Extract name using parent's process method (only gets first line content)
const args = super.process(string);
const name = args[0] || "";

// Extract description from content after the first line
// The string is in format: "@wiki Name\nDescription line 1\nDescription line 2..."
const lines = string.split(/[\n\r\u2028\u2029]/);
// Filter out all empty lines from the description
const descriptionLines = lines.slice(1).filter(line => {
const trimmed = line.trim();
return trimmed.length > 0;
});
const description = descriptionLines.join("\n").trim();

const result = [`@category wikis`, `@name ${name}`];
if (description) {
// Output description without @description prefix - the parser's tags_re regex
// will capture unprefixed content and process it as a description tag
result.push(description);
}
return result;
}
}