1919 */
2020
2121import * as path from "path" ;
22+ import * as fs from "fs" ;
2223import {
2324 createConnection ,
2425 TextDocuments ,
2526 Diagnostic ,
2627 DiagnosticSeverity ,
27- ProposedFeatures ,
2828 InitializeParams ,
2929 TextDocumentSyncKind ,
3030 InitializeResult ,
3131 Connection ,
32- ErrorMessageTracker ,
3332 CancellationToken ,
3433 CodeActionParams ,
35- Command ,
3634 CodeAction ,
3735 CodeActionKind ,
3836 TextEdit ,
3937 WorkspaceEdit ,
40- CodeDescription ,
4138} from "vscode-languageserver/node" ;
4239import { TextDocument } from "vscode-languageserver-textdocument" ;
4340import * as htmlhint from "htmlhint" ;
44- import fs = require( "fs" ) ;
4541import { URI } from "vscode-uri" ;
4642import ignore from "ignore" ;
47- let stripJsonComments : any = require ( "strip-json-comments" ) ;
43+ import stripJsonComments from "strip-json-comments" ;
4844
4945// Cache for gitignore patterns to avoid repeatedly parsing .gitignore files
50- let gitignoreCache : Map < string , any > = new Map ( ) ;
46+ let gitignoreCache : Map < string , ReturnType < typeof ignore > > = new Map ( ) ;
5147
5248// Cache for workspace root detection to avoid repeated filesystem calls
5349let workspaceRootCache : Map < string , string | null > = new Map ( ) ;
5450
51+ interface HtmlHintSettings {
52+ configFile : string ;
53+ enable : boolean ;
54+ options : Record < string , unknown > ;
55+ optionsFile : string ;
56+ ignoreGitignore : boolean ;
57+ }
58+
5559interface Settings {
56- htmlhint : {
57- configFile : string ;
58- enable : boolean ;
59- options : any ;
60- optionsFile : string ;
61- ignoreGitignore : boolean ;
62- } ;
63- [ key : string ] : any ;
60+ htmlhint : HtmlHintSettings ;
61+ [ key : string ] : unknown ;
62+ }
63+
64+ interface HtmlHintConfig {
65+ [ key : string ] : unknown ;
6466}
6567
6668let settings : Settings | null = null ;
67- let linter : any = null ;
69+ let linter : {
70+ verify : ( text : string , config ?: HtmlHintConfig ) => htmlhint . Error [ ] ;
71+ } | null = null ;
6872
6973/**
7074 * This variable is used to cache loaded htmlhintrc objects. It is a dictionary from path -> config object.
7175 * A value of null means a .htmlhintrc object didn't exist at the given path.
7276 * A value of undefined means the file at this path hasn't been loaded yet, or should be reloaded because it changed
7377 */
74- let htmlhintrcOptions : any = { } ;
75-
76- /**
77- * Given an htmlhint Error object, approximate the text range highlight
78- */
79- function getRange ( error : htmlhint . Error , lines : string [ ] ) : any {
80- let line = lines [ error . line - 1 ] ;
81- if ( ! line ) {
82- // Fallback if line doesn't exist
83- return {
84- start : {
85- line : error . line - 1 ,
86- character : error . col - 1 ,
87- } ,
88- end : {
89- line : error . line - 1 ,
90- character : error . col - 1 ,
91- } ,
92- } ;
93- }
94-
95- let isWhitespace = false ;
96- let curr = error . col ;
97- while ( curr < line . length && ! isWhitespace ) {
98- let char = line [ curr ] ;
99- isWhitespace =
100- char === " " ||
101- char === "\t" ||
102- char === "\n" ||
103- char === "\r" ||
104- char === "<" ;
105- ++ curr ;
106- }
107-
108- if ( isWhitespace ) {
109- -- curr ;
110- }
111-
112- return {
113- start : {
114- line : error . line - 1 , // Html-hint line numbers are 1-based.
115- character : error . col - 1 ,
116- } ,
117- end : {
118- line : error . line - 1 ,
119- character : curr ,
120- } ,
121- } ;
122- }
78+ let htmlhintrcOptions : Record < string , HtmlHintConfig | null | undefined > = { } ;
12379
12480/**
12581 * Given an htmlhint.Error type return a VS Code server Diagnostic object
@@ -157,8 +113,8 @@ function makeDiagnostic(
157113 * Get the HTMLHint configuration settings for the given HTML file. This method will take care of whether to use
158114 * VS Code settings, or to use a .htmlhintrc file.
159115 */
160- function getConfiguration ( filePath : string ) : any {
161- let options : any ;
116+ function getConfiguration ( filePath : string ) : HtmlHintConfig {
117+ let options : HtmlHintConfig | undefined ;
162118
163119 trace ( `[HTMLHint Debug] Getting configuration for file: ${ filePath } ` ) ;
164120 trace ( `[HTMLHint Debug] Current settings: ${ JSON . stringify ( settings ) } ` ) ;
@@ -228,8 +184,8 @@ function getConfiguration(filePath: string): any {
228184 * Given the path of an HTML file, this function will look in current directory & parent directories
229185 * to find a .htmlhintrc file to use as the linter configuration. The settings are
230186 */
231- function findConfigForHtmlFile ( base : string ) {
232- let options : any ;
187+ function findConfigForHtmlFile ( base : string ) : HtmlHintConfig | undefined {
188+ let options : HtmlHintConfig | undefined ;
233189 trace ( `[HTMLHint Debug] Looking for config starting from: ${ base } ` ) ;
234190
235191 if ( fs . existsSync ( base ) ) {
@@ -285,8 +241,8 @@ function findConfigForHtmlFile(base: string) {
285241/**
286242 * Given a path to a .htmlhintrc file, load it into a javascript object and return it.
287243 */
288- function loadConfigurationFile ( configFile : string ) : any {
289- let ruleset : any = null ;
244+ function loadConfigurationFile ( configFile : string ) : HtmlHintConfig | null {
245+ let ruleset : HtmlHintConfig | null = null ;
290246 trace ( `[HTMLHint Debug] Attempting to load config file: ${ configFile } ` ) ;
291247 if ( fs . existsSync ( configFile ) ) {
292248 trace ( `[HTMLHint Debug] Config file exists, reading: ${ configFile } ` ) ;
@@ -339,16 +295,22 @@ function validateAllTextDocuments(
339295 return ;
340296 }
341297
342- let tracker = new ErrorMessageTracker ( ) ;
298+ // Collect all errors and send them together instead of using ErrorMessageTracker
299+ const errors : string [ ] = [ ] ;
343300 documents . forEach ( ( document ) => {
344301 try {
345302 trace ( `[DEBUG] Revalidating document: ${ document . uri } ` ) ;
346303 validateTextDocument ( connection , document ) ;
347304 } catch ( err ) {
348- tracker . add ( getErrorMessage ( err , document ) ) ;
305+ errors . push ( getErrorMessage ( err , document ) ) ;
349306 }
350307 } ) ;
351- tracker . sendErrors ( connection ) ;
308+
309+ // Send all errors at once if there are any
310+ if ( errors . length > 0 ) {
311+ connection . window . showErrorMessage ( errors . join ( "\n" ) ) ;
312+ }
313+
352314 trace ( `[DEBUG] validateAllTextDocuments completed` ) ;
353315}
354316
@@ -363,7 +325,7 @@ function validateTextDocument(
363325 }
364326}
365327
366- let connection : Connection = createConnection ( ProposedFeatures . all ) ;
328+ let connection : Connection = createConnection ( ) ;
367329let documents : TextDocuments < TextDocument > = new TextDocuments ( TextDocument ) ;
368330documents . listen ( connection ) ;
369331
@@ -1914,7 +1876,9 @@ connection.onInitialize(
19141876 : undefined ;
19151877
19161878 // Since Files API is no longer available, we'll use embedded htmlhint directly
1917- linter = htmlhint . default || htmlhint . HTMLHint || htmlhint ;
1879+ linter = ( htmlhint . default ||
1880+ htmlhint . HTMLHint ||
1881+ htmlhint ) as typeof linter ;
19181882
19191883 let result : InitializeResult = {
19201884 capabilities : {
0 commit comments