@@ -262,6 +262,67 @@ function loadConfigurationFile(configFile: string): HtmlHintConfig | null {
262262return ruleset ;
263263}
264264
265+ function isHtmlHintRuleEnabled ( value :unknown ) :boolean {
266+ if ( value === undefined || value === null ) {
267+ return false ;
268+ }
269+
270+ if ( Array . isArray ( value ) ) {
271+ if ( value . length === 0 ) {
272+ return false ;
273+ }
274+ return isHtmlHintRuleEnabled ( value [ 0 ] ) ;
275+ }
276+
277+ if ( typeof value === "boolean" ) {
278+ return value ;
279+ }
280+
281+ if ( typeof value === "number" ) {
282+ return value !== 0 ;
283+ }
284+
285+ if ( typeof value === "string" ) {
286+ const normalized = value . trim ( ) . toLowerCase ( ) ;
287+ return normalized !== "false" && normalized !== "0" && normalized !== "" ;
288+ }
289+
290+ if ( typeof value === "object" ) {
291+ return true ;
292+ }
293+
294+ return false ;
295+ }
296+
297+ function isRuleEnabledForDocument (
298+ document :TextDocument ,
299+ ruleId :string ,
300+ ) :boolean {
301+ try {
302+ const parsedUri = URI . parse ( document . uri ) ;
303+
304+ if ( parsedUri . scheme !== "file" ) {
305+ trace (
306+ `[DEBUG] isRuleEnabledForDocument: Non-file scheme for${ document . uri } , rule${ ruleId } treated as disabled` ,
307+ ) ;
308+ return false ;
309+ }
310+
311+ const config = getConfiguration ( parsedUri . fsPath ) ;
312+ const ruleValue = ( config as Record < string , unknown > ) [ ruleId ] ;
313+ const enabled = isHtmlHintRuleEnabled ( ruleValue ) ;
314+ trace (
315+ `[DEBUG] isRuleEnabledForDocument: Rule${ ruleId } enabled=${ enabled } for${ document . uri } ` ,
316+ ) ;
317+ return enabled ;
318+ } catch ( error ) {
319+ trace (
320+ `[DEBUG] isRuleEnabledForDocument: Failed to determine rule${ ruleId } for${ document . uri } :${ error } ` ,
321+ ) ;
322+ return false ;
323+ }
324+ }
325+
265326function isErrorWithMessage ( err :unknown ) :err is{ message :string } {
266327return (
267328typeof err === "object" &&
@@ -789,7 +850,9 @@ function createMetaCharsetRequireFix(
789850// Insert charset meta tag at the beginning of head (right after <head>)
790851const headStart = headMatch . index ! + headMatch [ 0 ] . indexOf ( ">" ) + 1 ;
791852const insertPosition = headStart ;
792- const newText = '\n <meta charset="UTF-8">' ;
853+ const shouldSelfClose = isRuleEnabledForDocument ( document , "tag-self-close" ) ;
854+ const newText =
855+ '\n <meta charset="UTF-8"' + ( shouldSelfClose ?" />" :">" ) ;
793856
794857const edit :TextEdit = {
795858range :{
@@ -846,27 +909,28 @@ function createMetaViewportRequireFix(
846909/ < m e t a \s + c h a r s e t \s * = \s * [ " ' ] [ ^ " ' ] * [ " ' ] [ ^ > ] * > / i,
847910) ;
848911
912+ const shouldSelfClose = isRuleEnabledForDocument ( document , "tag-self-close" ) ;
913+ trace (
914+ `[DEBUG] createMetaViewportRequireFix: tag-self-close enabled=${ shouldSelfClose } ` ,
915+ ) ;
916+ const viewportSnippet = `\n <meta name="viewport" content="width=device-width, initial-scale=1.0"${ shouldSelfClose ?" />" :">" } ` ;
917+
849918let insertPosition :number ;
850- let newText :string ;
851919
852920if ( metaCharsetMatch ) {
853921const metaCharsetEnd =
854922headStart + metaCharsetMatch . index ! + metaCharsetMatch [ 0 ] . length ;
855923insertPosition = metaCharsetEnd ;
856- newText =
857- '\n <meta name="viewport" content="width=device-width, initial-scale=1.0">' ;
858924} else {
859925insertPosition = headStart ;
860- newText =
861- '\n <meta name="viewport" content="width=device-width, initial-scale=1.0">' ;
862926}
863927
864928const edit :TextEdit = {
865929range :{
866930start :document . positionAt ( insertPosition ) ,
867931end :document . positionAt ( insertPosition ) ,
868932} ,
869- newText :newText ,
933+ newText :viewportSnippet ,
870934} ;
871935
872936const workspaceEdit :WorkspaceEdit = {
@@ -923,32 +987,32 @@ function createMetaDescriptionRequireFix(
923987) ;
924988
925989let insertPosition :number ;
926- let newText :string ;
990+ const shouldSelfClose = isRuleEnabledForDocument ( document , "tag-self-close" ) ;
991+ const descriptionSnippet =
992+ '\n <meta name="description" content=""' +
993+ ( shouldSelfClose ?" />" :">" ) ;
927994
928995if ( metaViewportMatch ) {
929996// Insert after viewport meta tag
930997const metaViewportEnd =
931998headStart + metaViewportMatch . index ! + metaViewportMatch [ 0 ] . length ;
932999insertPosition = metaViewportEnd ;
933- newText = '\n <meta name="description" content="">' ;
9341000} else if ( metaCharsetMatch ) {
9351001// Insert after charset meta tag
9361002const metaCharsetEnd =
9371003headStart + metaCharsetMatch . index ! + metaCharsetMatch [ 0 ] . length ;
9381004insertPosition = metaCharsetEnd ;
939- newText = '\n <meta name="description" content="">' ;
9401005} else {
9411006// Insert at the beginning of head
9421007insertPosition = headStart ;
943- newText = '\n <meta name="description" content="">' ;
9441008}
9451009
9461010const edit :TextEdit = {
9471011range :{
9481012start :document . positionAt ( insertPosition ) ,
9491013end :document . positionAt ( insertPosition ) ,
9501014} ,
951- newText :newText ,
1015+ newText :descriptionSnippet ,
9521016} ;
9531017
9541018const workspaceEdit :WorkspaceEdit = {
@@ -1977,7 +2041,10 @@ function createAttrValueNoDuplicationFix(
19772041`[DEBUG] createAttrValueNoDuplicationFix called with diagnostic:${ JSON . stringify ( diagnostic ) } ` ,
19782042) ;
19792043
1980- if ( ! diagnostic . data || diagnostic . data . ruleId !== "attr-value-no-duplication" ) {
2044+ if (
2045+ ! diagnostic . data ||
2046+ diagnostic . data . ruleId !== "attr-value-no-duplication"
2047+ ) {
19812048trace (
19822049`[DEBUG] createAttrValueNoDuplicationFix: Invalid diagnostic data or ruleId` ,
19832050) ;
@@ -1990,7 +2057,9 @@ function createAttrValueNoDuplicationFix(
19902057// Use robust tag boundary detection
19912058const tagBoundaries = findTagBoundaries ( text , diagnosticOffset ) ;
19922059if ( ! tagBoundaries ) {
1993- trace ( `[DEBUG] createAttrValueNoDuplicationFix: Could not find tag boundaries` ) ;
2060+ trace (
2061+ `[DEBUG] createAttrValueNoDuplicationFix: Could not find tag boundaries` ,
2062+ ) ;
19942063return null ;
19952064}
19962065
@@ -2017,8 +2086,9 @@ function createAttrValueNoDuplicationFix(
20172086
20182087if ( values . length !== uniqueValues . length ) {
20192088// Found duplicates, create an edit to fix them
2020- const newAttrValue = uniqueValues . join ( ' ' ) ;
2021- const quote = match [ 2 ] !== undefined ?'"' :match [ 3 ] !== undefined ?"'" :'' ;
2089+ const newAttrValue = uniqueValues . join ( " " ) ;
2090+ const quote =
2091+ match [ 2 ] !== undefined ?'"' :match [ 3 ] !== undefined ?"'" :"" ;
20222092const newFullMatch = quote
20232093 ?`${ attrName } =${ quote } ${ newAttrValue } ${ quote } `
20242094 :`${ attrName } =${ newAttrValue } ` ;