@@ -120,9 +120,7 @@ export class MarkdownCommand extends MarkdownBase {
120120 if ( this . help . length > 0 ) {
121121 lines . push ( `## Description for ${ this . commandName } ` ) ;
122122 lines . push ( '' ) ;
123- for ( const paragraph of convertHyphenListsToMarkdown (
124- this . help . map ( ( p ) => applyCodeFormatting ( escapeAngleBrackets ( p ) ) )
125- ) ) {
123+ for ( const paragraph of convertHyphenListsToMarkdown ( this . help . map ( ( p ) => escapeForMarkdown ( p ) ) ) ) {
126124 lines . push ( paragraph ) ;
127125 lines . push ( '' ) ;
128126 }
@@ -172,111 +170,19 @@ export class MarkdownCommand extends MarkdownBase {
172170 }
173171}
174172
175- function escapeAngleBrackets ( text : string ) : string {
176- return text . replace ( / < / g, '<' ) . replace ( / > / g, '>' ) ;
177- }
178-
179- function applyCodeFormatting ( text : string ) : string {
180- // First, wrap JSON-like structures (must run before other code formatting to avoid double-wrapping)
181- let result = wrapJsonInCode ( text ) ;
182- // Wrap --flag-name tokens (not already in backticks)
183- result = result . replace ( / (?< ! ` ) - - ( [ \w - ] + ) (? ! ` ) / g, '`--$1`' ) ;
184- // Wrap glob patterns like *.cls, *.trigger (not already in backticks)
185- result = result . replace ( / (?< ! ` ) \* ( \. \w + ) (? ! ` ) / g, '`*$1`' ) ;
186- // Wrap filenames/extensions with known doc-related extensions (not already in backticks)
187- // Must run compound extensions (.sarif.json) before simple ones (.sarif, .json)
188- result = result . replace ( / (?< ! [ \w ` ] ) ( \. s a r i f \. j s o n ) (? ! [ \w ` ] ) / g, '`$1`' ) ;
189- result = result . replace ( / (?< ! [ \w ` ] ) ( \w [ \w . - ] * \. (?: x m l | h t m l ? | j s o n | s a r i f | c s v ) ) (? ! [ \w ` ] ) / g, '`$1`' ) ;
190- result = result . replace ( / (?< ! [ \w ` ] ) ( \. (?: x m l | h t m l ? | j s o n | s a r i f | c s v ) ) (? ! [ \w ` ] ) / g, '`$1`' ) ;
191- // Wrap file/directory paths: must be preceded by whitespace or opening punctuation (not part of a URL)
192- // Matches: ./foo/bar, ../foo, foo/bar/baz — but not https://foo/bar
193- result = result . replace ( / ( ^ | (?< = [ \s ( [ " ] ) ) (? ! h t t p s ? : \/ \/ ) ( (?: \. { 1 , 2 } \/ | [ \w ] [ \w - ] * \/ ) [ \w . / - ] + ) / g, '$1`$2`' ) ;
194- return result ;
195- }
196-
197- function wrapJsonInCode ( text : string ) : string {
198- // Strategy: Look for JSON-like patterns and validate them before wrapping
199- // Match objects like {"key": "value"} or {"key": 123, "key2": true}
200- // Match arrays like ["value1", "value2"] or [{"key": "value"}]
201-
202- let result = text ;
203- const matches : Array < { start : number ; end : number ; content : string } > = [ ] ;
204-
205- // Find potential JSON objects and arrays
206- let i = 0 ;
207- while ( i < text . length ) {
208- if ( text [ i ] === '{' || text [ i ] === '[' ) {
209- const openChar = text [ i ] ;
210- const closeChar = openChar === '{' ? '}' : ']' ;
211- let depth = 1 ;
212- let j = i + 1 ;
213- let inString = false ;
214- let escape = false ;
215-
216- // Find matching closing bracket
217- while ( j < text . length && depth > 0 ) {
218- if ( escape ) {
219- escape = false ;
220- j ++ ;
221- continue ;
222- }
173+ function escapeForMarkdown ( text : string ) : string {
174+ // Escape HTML entities for markdown safety
175+ let result = text . replace ( / < / g, '<' ) . replace ( / > / g, '>' ) ;
223176
224- if ( text [ j ] === '\\' ) {
225- escape = true ;
226- j ++ ;
227- continue ;
228- }
177+ // Normalize whitespace: collapse multiple spaces/tabs/newlines to single space
178+ result = result . replace ( / \s + / g, ' ' ) ;
229179
230- if ( text [ j ] === '"' ) {
231- inString = ! inString ;
232- } else if ( ! inString ) {
233- if ( text [ j ] === openChar ) {
234- depth ++ ;
235- } else if ( text [ j ] === closeChar ) {
236- depth -- ;
237- }
238- }
239- j ++ ;
240- }
241-
242- if ( depth === 0 ) {
243- const content = text . substring ( i , j ) ;
244- // Check if this looks like JSON: must have quotes around keys and colons
245- if ( looksLikeJson ( content ) ) {
246- matches . push ( { start : i , end : j , content } ) ;
247- }
248- i = j ;
249- } else {
250- i ++ ;
251- }
252- } else {
253- i ++ ;
254- }
255- }
256-
257- // Apply wrapping in reverse order to preserve indices
258- for ( let k = matches . length - 1 ; k >= 0 ; k -- ) {
259- const { start, end, content } = matches [ k ] ;
260- // Check if already in backticks
261- const before = text . substring ( Math . max ( 0 , start - 1 ) , start ) ;
262- const after = text . substring ( end , Math . min ( text . length , end + 1 ) ) ;
263- if ( before !== '`' && after !== '`' ) {
264- result = result . substring ( 0 , start ) + `\`${ content } \`` + result . substring ( end ) ;
265- }
266- }
180+ // Trim leading/trailing whitespace
181+ result = result . trim ( ) ;
267182
268183 return result ;
269184}
270185
271- function looksLikeJson ( text : string ) : boolean {
272- // Must contain at least one quoted key-value pair with colon, or be an array with quoted strings
273- const hasKeyValue = / " [ ^ " ] + " \s * : \s * / . test ( text ) ;
274- const isArray = text . trim ( ) . startsWith ( '[' ) && text . trim ( ) . endsWith ( ']' ) ;
275- const hasQuotedString = / " [ ^ " ] + " \s * / . test ( text ) ;
276-
277- return hasKeyValue || ( isArray && hasQuotedString ) ;
278- }
279-
280186function convertHyphenListsToMarkdown ( paragraphs : string [ ] ) : string [ ] {
281187 const result : string [ ] = [ ] ;
282188 let i = 0 ;
@@ -360,7 +266,7 @@ function renderFlagDescription(param: CommandParameterData): string {
360266 if ( param . defaultFlagValue ) metadataParts . push ( `**Default value:** \`${ param . defaultFlagValue } \`` ) ;
361267
362268 const desc = convertHyphenListsToMarkdown (
363- param . description . map ( ( p ) => applyCodeFormatting ( escapeAngleBrackets ( p . replace ( / \| / g, '|' ) ) ) )
269+ param . description . map ( ( p ) => escapeForMarkdown ( p . replace ( / \| / g, '|' ) ) )
364270 ) . join ( '<br><br>' ) ;
365271
366272 const parts : string [ ] = [ metadataParts . join ( '<br>' ) ] ;
0 commit comments