Skip to content

Commit b5d8c9f

Browse files
committed
feat: format indented code block
issue: #32
1 parent f755db1 commit b5d8c9f

File tree

8 files changed

+202
-64
lines changed

8 files changed

+202
-64
lines changed

src/descriptionFormatter.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { format } from "prettier";
22
import { DESCRIPTION, EXAMPLE, TODO } from "./tags";
33
import { AllOptions } from "./types";
4-
import { capitalizer } from "./utils";
4+
import { capitalizer, formatCode } from "./utils";
55

66
const EMPTY_LINE_SIGNATURE = "2@^5!~#sdE!_EMPTY_LINE_SIGNATURE";
77
const NEW_LINE_START_WITH_DASH = "2@^5!~#sdE!_NEW_LINE_START_WITH_DASH";
@@ -12,6 +12,7 @@ const NEW_PARAGRAPH_START_WITH_DASH =
1212
const NEW_PARAGRAPH_START_THREE_SPACE_SIGNATURE =
1313
"2@^5!~#sdE!_NEW_PARAGRAPH_START_THREE_SPACE_SIGNATURE";
1414
const CODE = "2@^5!~#sdE!_CODE";
15+
const CODE_INDENTED = "2@^5!~#sdE!_CODE_INDENTED";
1516

1617
interface DescriptionEndLineParams {
1718
description: string;
@@ -63,6 +64,17 @@ function formatDescription(
6364
text = text.replace(code, `\n\n${CODE}\n\n`);
6465
});
6566
}
67+
const intendedCodes: string[] = [];
68+
text = text.replace(
69+
/(\n\n[^\S\r\n]{4}[\s\S]*?)((\n[\S])|$)/g,
70+
(code, _1, _2, _3) => {
71+
code = _3 ? code.slice(0, -1) : code;
72+
73+
intendedCodes.push(code);
74+
return `\n\n${CODE_INDENTED}\n\n${_3 ? _3.slice(1) : ""}`;
75+
},
76+
);
77+
6678
/**
6779
* Description
6880
*
@@ -194,6 +206,19 @@ function formatDescription(
194206
}, "");
195207
}
196208

209+
if (intendedCodes.length > 0) {
210+
text = text.split(CODE_INDENTED).reduce((pre, cur, index) => {
211+
let result = intendedCodes?.[index] || "";
212+
const beginningSpace = " ".repeat(4);
213+
214+
if (result) {
215+
// Remove two space from lines, maybe added previous format
216+
result = formatCode(result, beginningSpace, options).trim();
217+
}
218+
return `${pre}${cur.trim()}${result ? `\n\n ${result}\n\n` : ""}`;
219+
}, "");
220+
}
221+
197222
text = text.trim().slice(tagStringLength);
198223

199224
return text;
@@ -209,6 +234,7 @@ function breakDescriptionToLines(
209234
if (!str) {
210235
return str;
211236
}
237+
212238
const extraLastLineWidth = 10;
213239
let result = "";
214240
while (str.length > maxWidth + extraLastLineWidth) {

src/stringify.ts

Lines changed: 5 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { Spec } from "comment-parser/src/primitives";
2-
import { format } from "prettier";
32
import { formatDescription, descriptionEndLine } from "./descriptionFormatter";
43
import { DESCRIPTION, EXAMPLE, SPACE_TAG_DATA } from "./tags";
54
import {
@@ -8,6 +7,7 @@ import {
87
TAGS_VERTICALLY_ALIGN_ABLE,
98
} from "./roles";
109
import { AllOptions } from "./types";
10+
import { formatCode } from "./utils";
1111

1212
const stringify = (
1313
{ name, description, type, tag }: Spec,
@@ -29,7 +29,6 @@ const stringify = (
2929
jsdocSpaces,
3030
jsdocVerticalAlignment,
3131
jsdocDescriptionTag,
32-
jsdocKeepUnParseAbleExampleIndent,
3332
useTabs,
3433
tabWidth,
3534
} = options;
@@ -96,47 +95,10 @@ const stringify = (
9695
}
9796

9897
const beginningSpace = useTabs ? "\t" : " ".repeat(tabWidth);
99-
100-
// Remove two space from lines, maybe added previous format
101-
if (
102-
description
103-
.split("\n")
104-
.slice(1)
105-
.every((v) => !v.trim() || v.startsWith(beginningSpace))
106-
) {
107-
description = description.replace(
108-
new RegExp(`\n${useTabs ? "[\t]" : `[^S\r\n]{${tabWidth}}`}`, "g"),
109-
"\n",
110-
);
111-
}
112-
113-
try {
114-
let formattedExample = "";
115-
const examplePrintWith = printWidth - tabWidth;
116-
117-
// If example is a json
118-
if (description.trim().startsWith("{")) {
119-
formattedExample = format(description || "", {
120-
...options,
121-
parser: "json",
122-
printWidth: examplePrintWith,
123-
});
124-
} else {
125-
formattedExample = format(description || "", {
126-
...options,
127-
printWidth: examplePrintWith,
128-
});
129-
}
130-
131-
tagString += formattedExample.replace(/(^|\n)/g, "\n" + beginningSpace); // Add tow space to start of lines
132-
tagString = tagString.slice(0, tagString.length - 3);
133-
} catch (err) {
134-
tagString += "\n";
135-
tagString += description
136-
.split("\n")
137-
.map((l) => ` ${jsdocKeepUnParseAbleExampleIndent ? l : l.trim()}`)
138-
.join("\n");
139-
}
98+
const formattedExample = formatCode(description, beginningSpace, options);
99+
tagString += formattedExample.startsWith("\n")
100+
? formattedExample.trimEnd()
101+
: "\n" + formattedExample;
140102
}
141103

142104
// Add empty line after some tags if there is something below

src/utils.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,11 +207,70 @@ function findTokenIndex(tokens: Token[], token: Token): number {
207207
});
208208
}
209209

210+
function formatCode(
211+
result: string,
212+
beginningSpace: string,
213+
options: AllOptions,
214+
): string {
215+
const { printWidth, jsdocKeepUnParseAbleExampleIndent } = options;
216+
217+
if (
218+
result
219+
.split("\n")
220+
.slice(1)
221+
.every((v) => !v.trim() || v.startsWith(beginningSpace))
222+
) {
223+
result = result.replace(
224+
new RegExp(
225+
`\n${beginningSpace
226+
.replace(/[\t]/g, "[\\t]")
227+
.replace(/[^S\r\n]/g, "[^S\\r\\n]")}`,
228+
"g",
229+
),
230+
"\n",
231+
);
232+
}
233+
234+
try {
235+
let formattedExample = "";
236+
const examplePrintWith = printWidth - 4;
237+
238+
// If example is a json
239+
if (result.trim().startsWith("{")) {
240+
formattedExample = format(result || "", {
241+
...options,
242+
parser: "json",
243+
printWidth: examplePrintWith,
244+
});
245+
} else {
246+
formattedExample = format(result || "", {
247+
...options,
248+
printWidth: examplePrintWith,
249+
});
250+
}
251+
252+
result = formattedExample.replace(/(^|\n)/g, "\n" + beginningSpace); // Add spaces to start of lines
253+
} catch (err) {
254+
result = result
255+
.split("\n")
256+
.map(
257+
(l) =>
258+
`${beginningSpace}${
259+
jsdocKeepUnParseAbleExampleIndent ? l : l.trim()
260+
}`,
261+
)
262+
.join("\n");
263+
}
264+
265+
return result;
266+
}
267+
210268
export {
211269
convertToModernType,
212270
formatType,
213271
addStarsToTheBeginningOfTheLines,
214272
capitalizer,
215273
detectEndOfLine,
216274
findTokenIndex,
275+
formatCode,
217276
};

tests/__snapshots__/files/prism-core.js.shot

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ var Prism = (function (_self) {
8383
* type([1, 2]) === 'Array';
8484
* type({}) === 'Object';
8585
* type(String) === 'Function';
86-
* type(/abc+/) === 'RegExp'
86+
* type(/abc+/) === 'RegExp';
8787
*
8888
* @param {any} o
8989
* @returns {string}
@@ -275,13 +275,13 @@ var Prism = (function (_self) {
275275
* Furthermore, all non-overwriting tokens should be placed after the overwriting ones.
276276
*
277277
* @example
278-
* Prism.languages['css-with-colors'] = Prism.languages.extend('css', {
279-
* // Prism.languages.css already has a 'comment' token, so this token will overwrite CSS' 'comment' token
280-
* // at its original position
281-
* 'comment': { ... },
282-
* // CSS doesn't have a 'color' token, so this token will be appended
283-
* 'color': /\\\\b(?:red|green|blue)\\\\b/
284-
* });
278+
* Prism.languages['css-with-colors'] = Prism.languages.extend('css', {
279+
* // Prism.languages.css already has a 'comment' token, so this token will overwrite CSS' 'comment' token
280+
* // at its original position
281+
* 'comment': { ... },
282+
* // CSS doesn't have a 'color' token, so this token will be appended
283+
* 'color': /\\\\b(?:red|green|blue)\\\\b/
284+
* });
285285
*
286286
* @param {string} id The id of the language to extend. This has to be a key in \`Prism.languages\`.
287287
* @param {Grammar} redef The new tokens to append.
@@ -593,7 +593,7 @@ var Prism = (function (_self) {
593593
*
594594
* @memberof Prism
595595
* @example
596-
* Prism.highlight('var foo = true;', Prism.languages.javascript, 'javascript')
596+
* Prism.highlight('var foo = true;', Prism.languages.javascript, 'javascript');
597597
*
598598
* @param {string} text A string with the code to be highlighted.
599599
* @param {Grammar} grammar An object containing the tokens to use.
@@ -631,7 +631,7 @@ var Prism = (function (_self) {
631631
* if (token instanceof Prism.Token && token.type === 'number') {
632632
* console.log(\`Found numeric literal: \${token.content}\`);
633633
* }
634-
* })
634+
* });
635635
*
636636
* @param {string} text A string with the code to be highlighted.
637637
* @param {Grammar} grammar An object containing the tokens to use.

tests/__snapshots__/files/types.ts.shot

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,51 @@
33
exports[`File: types.ts Options: {} 1`] = `
44
"/** @type {((element: HTMLElement) => boolean)[]} */
55
var filters = [];
6+
7+
/**
8+
* Returns a slice of the first array that doesn't contain the leading and
9+
* trailing elements the both arrays have in common.
10+
*
11+
* Examples:
12+
*
13+
* trimCommon([1,2,3,4], [1,3,2,4]) => [2,3]
14+
* trimCommon([1,2,3,4], [1,2,3,4]) => []
15+
* trimCommon([1,2,0,0,3,4], [1,2,3,4]) => [0,0]
16+
* trimCommon([1,2,3,4], [1,2,0,0,3,4]) => []
17+
*
18+
*
19+
*
20+
* trimCommon([1,2,3,4],[1,3,2,4])
21+
* trimCommon([1,2,3,4 ], [1,2,3,4])
22+
* trimCommon([1,2,0,0, 3,4], [1,2,3,4])
23+
* trimCommon([1,2,3,4],[1,2,0,0,3,4])
24+
*
25+
* @template T
26+
* @param {readonly T[]} a1
27+
* @param {readonly T[]} a2
28+
* @returns {T[]}
29+
*/
30+
31+
/**
32+
* Returns a slice of the first array that doesn't contain the leading and
33+
* trailing elements the both arrays have in common.
34+
*
35+
* Examples:
36+
*
37+
* trimCommon([1,2,3,4], [1,3,2,4]) => [2,3]
38+
* trimCommon([1,2,3,4], [1,2,3,4]) => []
39+
* trimCommon([1,2,0,0,3,4], [1,2,3,4]) => [0,0]
40+
* trimCommon([1,2,3,4], [1,2,0,0,3,4]) => []
41+
*
42+
* trimCommon([1,2,3,4],[1,3,2,4])
43+
* trimCommon([1,2,3,4 ], [1,2,3,4])
44+
* trimCommon([1,2,0,0, 3,4], [1,2,3,4])
45+
* trimCommon([1,2,3,4],[1,2,0,0,3,4])
46+
*
47+
* @template T
48+
* @param {readonly T[]} a1
49+
* @param {readonly T[]} a2
50+
* @returns {T[]}
51+
*/
652
"
753
`;

tests/__snapshots__/paragraph.test.ts.snap

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -354,17 +354,16 @@ exports[`description new line with dash 3`] = `
354354
*
355355
* The test case file can either consist of two parts:
356356
*
357-
* {source code}
358-
* -------------------------------------
359-
* {expected token stream}
357+
* const a = \\"\\";
358+
* const b = { c: [] };
360359
*
361360
* Or of three parts:
362361
*
363362
* {source code}
364-
* -------------------------------------
365-
* {expected token stream}
366-
* -------------------------------------
367-
* {text comment explaining the test case}
363+
* ----
364+
* {expected token stream}
365+
* ----
366+
* {text comment explaining the test case}
368367
*
369368
* If the file contains more than three parts, the remaining parts are just
370369
* ignored. If the file however does not contain at least two parts (so no

tests/files/types.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,50 @@
22
* @type {Array<(element: HTMLElement) => boolean>}
33
*/
44
var filters = [];
5+
6+
/**
7+
* Returns a slice of the first array that doesn't contain the leading and trailing elements the both arrays have in
8+
* common.
9+
*
10+
* Examples:
11+
*
12+
* trimCommon([1,2,3,4], [1,3,2,4]) => [2,3]
13+
* trimCommon([1,2,3,4], [1,2,3,4]) => []
14+
* trimCommon([1,2,0,0,3,4], [1,2,3,4]) => [0,0]
15+
* trimCommon([1,2,3,4], [1,2,0,0,3,4]) => []
16+
*
17+
*
18+
*
19+
* trimCommon([1,2,3,4],[1,3,2,4])
20+
* trimCommon([1,2,3,4 ], [1,2,3,4])
21+
* trimCommon([1,2,0,0, 3,4], [1,2,3,4])
22+
* trimCommon([1,2,3,4],[1,2,0,0,3,4])
23+
*
24+
* @param {readonly T[]} a1
25+
* @param {readonly T[]} a2
26+
* @returns {T[]}
27+
* @template T
28+
*/
29+
30+
31+
/**
32+
* Returns a slice of the first array that doesn't contain the leading and trailing elements the both arrays have in
33+
* common.
34+
*
35+
* Examples:
36+
*
37+
* trimCommon([1,2,3,4], [1,3,2,4]) => [2,3]
38+
* trimCommon([1,2,3,4], [1,2,3,4]) => []
39+
* trimCommon([1,2,0,0,3,4], [1,2,3,4]) => [0,0]
40+
* trimCommon([1,2,3,4], [1,2,0,0,3,4]) => []
41+
*
42+
* trimCommon([1,2,3,4],[1,3,2,4])
43+
* trimCommon([1,2,3,4 ], [1,2,3,4])
44+
* trimCommon([1,2,0,0, 3,4], [1,2,3,4])
45+
* trimCommon([1,2,3,4],[1,2,0,0,3,4])
46+
*
47+
* @param {readonly T[]} a1
48+
* @param {readonly T[]} a2
49+
* @returns {T[]}
50+
* @template T
51+
*/

tests/paragraph.test.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,8 @@ test("description new line with dash", () => {
154154
*
155155
* The test case file can either consist of two parts:
156156
*
157-
* {source code}
158-
* ----
159-
* {expected token stream}
157+
* const a=''
158+
* const b={c:[]}
160159
*
161160
*
162161
* or of three parts:

0 commit comments

Comments
 (0)