Skip to content

Commit bee16cd

Browse files
committed
Fix #67 Handle markdown tables by putting them in long comments + unindent descriptions
1 parent 0c8cac8 commit bee16cd

File tree

5 files changed

+227
-120
lines changed

5 files changed

+227
-120
lines changed

__tests__/test-data/offline-sites/gmod-wiki/panel-slider.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,7 @@ end
3737
</example>`;
3838

3939
export const apiDefinition =
40-
`---
41-
---
42-
---
43-
--- A simple slider featuring an numeric display.
44-
---
40+
`--- A simple slider featuring an numeric display.
4541
---@deprecated Panel:SetActionFunction and Panel:PostMessage. Use DNumSlider instead.
4642
---@class Slider : Panel
4743
local Slider = {}\n\n`;

__tests__/test-data/offline-sites/gmod-wiki/struct-custom-entity-fields.ts

Lines changed: 91 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,10 @@ This can also be set from map, see <page>Sandbox Specific Mapping</page></item>
4747
</structure>`;
4848

4949
export const apiDefinition =
50-
`---
51-
--- Information about custom fields **all** entities can have.
50+
`--- Information about custom fields **all** entities can have.
5251
---
5352
--- See also [Structures/ENT](https://wiki.facepunch.com/gmod/Structures/ENT)
5453
---
55-
---
5654
---[View wiki](https://wiki.facepunch.com/gmod/Custom_Entity_Fields)
5755
---@class Custom_Entity_Fields
5856
local Custom_Entity_Fields = {}
@@ -136,94 +134,94 @@ Custom_Entity_Fields.m_RenderOrigin = nil
136134
Custom_Entity_Fields.m_RenderAngles = nil\n\n`;
137135

138136
export const json = {
139-
name: 'Custom_Entity_Fields',
140-
address: 'Custom_Entity_Fields',
141-
type: 'struct',
142-
fields: [
143-
{
144-
name: 'GetEntityDriveMode',
145-
type: 'function',
146-
description: '`Serverside`, Sandbox and Sandbox derived only.\n\nCalled by the Drive property to override the default drive type, which is `drive_sandbox`.',
147-
},
148-
{
149-
name: 'OnEntityCopyTableFinish',
150-
type: 'function',
151-
description: 'Documented at ENTITY:OnEntityCopyTableFinish.',
152-
},
153-
{
154-
name: 'PostEntityCopy',
155-
type: 'function',
156-
description: 'Documented at ENTITY:PostEntityCopy.',
157-
},
158-
{
159-
name: 'PostEntityPaste',
160-
type: 'function',
161-
description: 'Documented at ENTITY:PostEntityPaste.',
162-
},
163-
{
164-
name: 'PreEntityCopy',
165-
type: 'function',
166-
description: 'Documented at ENTITY:PreEntityCopy.',
167-
},
168-
{
169-
name: 'OnDuplicated',
170-
type: 'function',
171-
description: 'Documented at ENTITY:OnDuplicated.',
172-
},
173-
{
174-
name: 'PhysgunDisabled',
175-
type: 'boolean',
176-
description: '`Shared`, Sandbox or Sandbox derived only.\n\nIf set to `true`, physgun will not be able to pick this entity up. This can also be set from map, see Sandbox Specific Mapping',
177-
},
178-
{
179-
name: 'PhysgunPickup',
180-
type: 'function',
181-
description: '`Shared`, Sandbox or Sandbox derived only.\n\nCalled from GM:PhysgunPickup, overrides `PhysgunDisabled`',
182-
},
183-
{
184-
name: 'm_tblToolsAllowed',
185-
type: 'table',
186-
description: '`Shared`, Sandbox or Sandbox derived only.\n\nControls which tools **and** properties can be used on this entity. Format is a list of strings where each string is the tool or property classname.\n\nThis can also be set from map, see Sandbox Specific Mapping',
187-
},
188-
{
189-
name: 'GravGunPickupAllowed',
190-
type: 'function',
191-
description: 'Documented at ENTITY:GravGunPickupAllowed.',
192-
},
193-
{
194-
name: 'GravGunPunt',
195-
type: 'function',
196-
description: 'Documented at ENTITY:GravGunPunt.',
197-
},
198-
{
199-
name: 'CanProperty',
200-
type: 'function',
201-
description: 'Documented at ENTITY:CanProperty.',
202-
},
203-
{
204-
name: 'CanTool',
205-
type: 'function',
206-
description: 'Documented at ENTITY:CanTool.',
207-
},
208-
{
209-
name: 'CalcAbsolutePosition',
210-
type: 'function',
211-
description: 'Documented at ENTITY:CalcAbsolutePosition.',
212-
},
213-
{
214-
name: 'RenderOverride',
215-
type: 'function',
216-
description: 'Documented at ENTITY:RenderOverride.',
217-
},
218-
{
219-
name: 'm_RenderOrigin',
220-
type: 'Vector',
221-
description: '(Clientside) Do not use.',
222-
},
223-
{
224-
name: 'm_RenderAngles',
225-
type: 'Angle',
226-
description: '(Clientside) Do not use.',
227-
},
228-
],
137+
name: 'Custom_Entity_Fields',
138+
address: 'Custom_Entity_Fields',
139+
type: 'struct',
140+
fields: [
141+
{
142+
name: 'GetEntityDriveMode',
143+
type: 'function',
144+
description: '`Serverside`, Sandbox and Sandbox derived only.\n\nCalled by the Drive property to override the default drive type, which is `drive_sandbox`.',
145+
},
146+
{
147+
name: 'OnEntityCopyTableFinish',
148+
type: 'function',
149+
description: 'Documented at ENTITY:OnEntityCopyTableFinish.',
150+
},
151+
{
152+
name: 'PostEntityCopy',
153+
type: 'function',
154+
description: 'Documented at ENTITY:PostEntityCopy.',
155+
},
156+
{
157+
name: 'PostEntityPaste',
158+
type: 'function',
159+
description: 'Documented at ENTITY:PostEntityPaste.',
160+
},
161+
{
162+
name: 'PreEntityCopy',
163+
type: 'function',
164+
description: 'Documented at ENTITY:PreEntityCopy.',
165+
},
166+
{
167+
name: 'OnDuplicated',
168+
type: 'function',
169+
description: 'Documented at ENTITY:OnDuplicated.',
170+
},
171+
{
172+
name: 'PhysgunDisabled',
173+
type: 'boolean',
174+
description: '`Shared`, Sandbox or Sandbox derived only.\n\nIf set to `true`, physgun will not be able to pick this entity up. This can also be set from map, see Sandbox Specific Mapping',
175+
},
176+
{
177+
name: 'PhysgunPickup',
178+
type: 'function',
179+
description: '`Shared`, Sandbox or Sandbox derived only.\n\nCalled from GM:PhysgunPickup, overrides `PhysgunDisabled`',
180+
},
181+
{
182+
name: 'm_tblToolsAllowed',
183+
type: 'table',
184+
description: '`Shared`, Sandbox or Sandbox derived only.\n\nControls which tools **and** properties can be used on this entity. Format is a list of strings where each string is the tool or property classname.\n\nThis can also be set from map, see Sandbox Specific Mapping',
185+
},
186+
{
187+
name: 'GravGunPickupAllowed',
188+
type: 'function',
189+
description: 'Documented at ENTITY:GravGunPickupAllowed.',
190+
},
191+
{
192+
name: 'GravGunPunt',
193+
type: 'function',
194+
description: 'Documented at ENTITY:GravGunPunt.',
195+
},
196+
{
197+
name: 'CanProperty',
198+
type: 'function',
199+
description: 'Documented at ENTITY:CanProperty.',
200+
},
201+
{
202+
name: 'CanTool',
203+
type: 'function',
204+
description: 'Documented at ENTITY:CanTool.',
205+
},
206+
{
207+
name: 'CalcAbsolutePosition',
208+
type: 'function',
209+
description: 'Documented at ENTITY:CalcAbsolutePosition.',
210+
},
211+
{
212+
name: 'RenderOverride',
213+
type: 'function',
214+
description: 'Documented at ENTITY:RenderOverride.',
215+
},
216+
{
217+
name: 'm_RenderOrigin',
218+
type: 'Vector',
219+
description: '(Clientside) Do not use.',
220+
},
221+
{
222+
name: 'm_RenderAngles',
223+
type: 'Angle',
224+
description: '(Clientside) Do not use.',
225+
},
226+
],
229227
};

__tests__/utils/string.spec.ts

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { removeNewlines, toLowerCamelCase, putCommentBeforeEachLine, safeFileName } from '../../src/utils/string';
1+
import { removeNewlines, toLowerCamelCase, wrapInComment, safeFileName, unindentText } from '../../src/utils/string';
22

33
describe('toLowerCamelCase', () => {
44
it('should convert a string to lowerCamelCase', () => {
@@ -35,21 +35,74 @@ describe('putCommentBeforeEachLine', () => {
3535
['hello\n\n\n\nworld', '--- hello\n---\n--- world'],
3636
['hello\r\n\r\n\r\n\r\nworld', '--- hello\n---\n--- world'],
3737
])('should put a comment before each line', (input, expected, skipFirstLine = false) => {
38-
expect(putCommentBeforeEachLine(input, skipFirstLine)).toBe(expected);
38+
expect(wrapInComment(input, skipFirstLine)).toBe(expected);
39+
});
40+
});
41+
42+
describe('wrapInComment', () => {
43+
it.each([
44+
[
45+
`The following specifiers are exclusive to LuaJIT:
46+
47+
| Format | Description | Example of the output |
48+
|:------:|:-----------:|:---------------------:|
49+
| %p | Returns pointer to supplied structure (table/function) | \`0xf20a8968\` |
50+
| %q | Formats a string between double quotes, using escape sequences when necessary to ensure that it can safely be read back by the Lua interpreter | \`"test\\1\\2test"\` |`,
51+
`The following specifiers are exclusive to LuaJIT:
52+
--[[
53+
54+
| Format | Description | Example of the output |
55+
|:------:|:-----------:|:---------------------:|
56+
| %p | Returns pointer to supplied structure (table/function) | \`0xf20a8968\` |
57+
| %q | Formats a string between double quotes, using escape sequences when necessary to ensure that it can safely be read back by the Lua interpreter | \`"test\\1\\2test"\` |
58+
--]]`,
59+
true,
60+
],
61+
[
62+
`The following specifiers are exclusive to LuaJIT:
63+
64+
| Format | Description | Example of the output |
65+
| ------ | ----------- | --------------------- |
66+
| %p | Returns pointer to supplied structure (table/function) | \`0xf20a8968\` |
67+
| %q | Formats a string between double quotes, using escape sequences when necessary to ensure that it can safely be read back by the Lua interpreter | \`"test\\1\\2test"\` |`,
68+
`--[[
69+
The following specifiers are exclusive to LuaJIT:
70+
71+
| Format | Description | Example of the output |
72+
| ------ | ----------- | --------------------- |
73+
| %p | Returns pointer to supplied structure (table/function) | \`0xf20a8968\` |
74+
| %q | Formats a string between double quotes, using escape sequences when necessary to ensure that it can safely be read back by the Lua interpreter | \`"test\\1\\2test"\` |
75+
--]]`,
76+
],
77+
])('should put a comment before each line', (input, expected, skipFirstLine = false) => {
78+
expect(wrapInComment(input, skipFirstLine)).toBe(expected);
3979
});
4080
});
4181

4282
describe('safeFileName', () => {
4383
it.each([
44-
['hello world','hello world'],
84+
['hello world', 'hello world'],
4585
['hello:World', 'hello_World'],
4686
['hello:World', 'hello-World', '-'],
4787
['hello:World', 'helloWorld', ''],
4888
['hello:World', 'hello World', ' '],
49-
])('should make a string "%s" safe ("%s") for use as a file name', (input: string, expected: string, replacement: string|undefined = undefined) => {
89+
])('should make a string "%s" safe ("%s") for use as a file name', (input: string, expected: string, replacement: string | undefined = undefined) => {
5090
if (replacement !== undefined)
5191
expect(safeFileName(input, replacement)).toBe(expected);
5292
else
5393
expect(safeFileName(input)).toBe(expected);
5494
});
5595
});
96+
97+
describe('unindentText', () => {
98+
it.each([
99+
['hello\nworld', 'hello\nworld'],
100+
[' hello\n world', 'hello\nworld'],
101+
[' hello\n world', 'hello\nworld'],
102+
['\thello\n\tworld', 'hello\nworld'],
103+
['\thello\n\t world', 'hello\n world'],
104+
['\t\thello\n\t\tworld', 'hello\nworld'],
105+
])('should unindent text by the given amount', (input, expected) => {
106+
expect(unindentText(input)).toBe(expected);
107+
});
108+
});

src/api-writer/glua-api-writer.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ClassFunction, Enum, Function, HookFunction, LibraryFunction, TypePage, Panel, PanelFunction, Realm, Struct, WikiPage, isPanel, FunctionArgument, FunctionCallback } from '../scrapers/wiki-page-markup-scraper.js';
2-
import { escapeSingleQuotes, indentText, putCommentBeforeEachLine, removeNewlines, safeFileName, toLowerCamelCase } from '../utils/string.js';
2+
import { escapeSingleQuotes, indentText, wrapInComment, removeNewlines, safeFileName, toLowerCamelCase } from '../utils/string.js';
33
import {
44
isClassFunction,
55
isHookFunction,
@@ -117,9 +117,9 @@ export class GluaApiWriter {
117117
api += this.pageOverrides.get(classOverride)!.replace(/\n$/g, '') + '\n\n';
118118
} else {
119119
if (realm) {
120-
api += `---${this.formatRealm(realm)} ${description ? `${putCommentBeforeEachLine(description)}\n` : ''}\n`;
120+
api += `---${this.formatRealm(realm)} ${description ? `${wrapInComment(description)}\n` : ''}\n`;
121121
} else {
122-
api += description ? `${putCommentBeforeEachLine(description, false)}\n` : '';
122+
api += description ? `${wrapInComment(description, false)}\n` : '';
123123
}
124124

125125
if (url) {
@@ -166,7 +166,7 @@ export class GluaApiWriter {
166166
if (!this.writtenLibraryGlobals.has(page.name)) {
167167
let api = '';
168168

169-
api += page.description ? `${putCommentBeforeEachLine(page.description.trim(), false)}\n` : '';
169+
api += page.description ? `${wrapInComment(page.description, false)}\n` : '';
170170

171171
if (page.deprecated)
172172
api += `---@deprecated ${removeNewlines(page.deprecated)}\n`;
@@ -244,7 +244,7 @@ export class GluaApiWriter {
244244
api += `---@deprecated ${removeNewlines(_enum.deprecated)}\n`;
245245

246246
if (isContainedInTable) {
247-
api += `---${this.formatRealm(_enum.realm)} ${_enum.description ? `${putCommentBeforeEachLine(_enum.description.trim())}` : ''}\n`;
247+
api += `---${this.formatRealm(_enum.realm)} ${_enum.description ? `${wrapInComment(_enum.description)}` : ''}\n`;
248248
api += `---@enum ${_enum.name}\n`;
249249
api += `${_enum.name} = {\n`;
250250
}
@@ -265,12 +265,12 @@ export class GluaApiWriter {
265265
key = key.split('.')[1];
266266

267267
if (item.description?.trim()) {
268-
api += `${indentText(putCommentBeforeEachLine(item.description.trim(), false), 2)}\n`;
268+
api += `${indentText(wrapInComment(item.description, false), 2)}\n`;
269269
}
270270

271271
api += ` ${key} = ${item.value},\n`;
272272
} else {
273-
api += item.description ? `${putCommentBeforeEachLine(item.description.trim(), false)}\n` : '';
273+
api += item.description ? `${wrapInComment(item.description, false)}\n` : '';
274274
if (item.deprecated)
275275
api += `---@deprecated ${removeNewlines(item.deprecated)}\n`;
276276
api += `${key} = ${item.value}\n`;
@@ -287,7 +287,7 @@ export class GluaApiWriter {
287287
// Until LuaLS supports global enumerations (https://github.com/LuaLS/lua-language-server/issues/2721) we
288288
// will use @alias as a workaround.
289289
// LuaLS doesn't nicely display annotations for aliasses, hence this is commented
290-
//api += `\n---${this.formatRealm(_enum.realm)} ${_enum.description ? `${putCommentBeforeEachLine(_enum.description.trim())}` : ''}\n`;
290+
//api += `\n---${this.formatRealm(_enum.realm)} ${_enum.description ? `${wrapInComment(_enum.description)}` : ''}\n`;
291291
api += `\n---@alias ${_enum.name}\n`;
292292

293293
for (const item of _enum.items) {
@@ -319,7 +319,7 @@ export class GluaApiWriter {
319319
if (field.deprecated)
320320
api += `---@deprecated ${removeNewlines(field.deprecated)}\n`;
321321

322-
api += `---${putCommentBeforeEachLine(field.description.trim())}\n`;
322+
api += `---${wrapInComment(field.description)}\n`;
323323

324324
const type = GluaApiWriter.transformType(field.type, field.callback);
325325
api += `---@type ${type}\n`;
@@ -480,7 +480,7 @@ export class GluaApiWriter {
480480
}
481481

482482
private writeFunctionLuaDocComment(func: Function, args: FunctionArgument[] | undefined, realm: Realm) {
483-
let luaDocComment = `---${this.formatRealm(realm)} ${putCommentBeforeEachLine(func.description!.trim())}\n`;
483+
let luaDocComment = `---${this.formatRealm(realm)} ${wrapInComment(func.description!)}\n`;
484484
luaDocComment += `---\n---[View wiki](${func.url})\n`;
485485

486486
if (args) {
@@ -504,13 +504,13 @@ export class GluaApiWriter {
504504
let typesString = types.map(type => GluaApiWriter.transformType(type, arg.callback))
505505
.join('|');
506506

507-
luaDocComment += `---@param ${GluaApiWriter.safeName(arg.name)}${arg.default !== undefined ? `?` : ''} ${typesString} ${putCommentBeforeEachLine(arg.description!.trim())}\n`;
507+
luaDocComment += `---@param ${GluaApiWriter.safeName(arg.name)}${arg.default !== undefined ? `?` : ''} ${typesString} ${wrapInComment(arg.description!)}\n`;
508508
});
509509
}
510510

511511
if (func.returns) {
512512
func.returns.forEach(ret => {
513-
const description = putCommentBeforeEachLine(ret.description!.trim());
513+
const description = wrapInComment(ret.description!);
514514

515515
luaDocComment += `---@return `;
516516

0 commit comments

Comments
 (0)