@@ -14,7 +14,7 @@ export interface FileType {
1414 * Built-in file type definitions
1515 * Use `rg --type-list` to see all types in real ripgrep
1616 */
17- export const FILE_TYPES : Record < string , FileType > = {
17+ const FILE_TYPES : Record < string , FileType > = {
1818 // Web languages
1919 js : { extensions : [ ".js" , ".mjs" , ".cjs" , ".jsx" ] , globs : [ ] } ,
2020 ts : { extensions : [ ".ts" , ".tsx" , ".mts" , ".cts" ] , globs : [ ] } ,
@@ -86,37 +86,157 @@ export const FILE_TYPES: Record<string, FileType> = {
8686} ;
8787
8888/**
89- * Check if a filename matches any of the specified types
89+ * Mutable file type registry for runtime type modifications
90+ * Supports --type-add and --type-clear flags
9091 */
91- export function matchesType ( filename : string , types : string [ ] ) : boolean {
92- const lowerFilename = filename . toLowerCase ( ) ;
92+ export class FileTypeRegistry {
93+ private types : Map < string , FileType > ;
9394
94- for ( const typeName of types ) {
95- const fileType = FILE_TYPES [ typeName ] ;
96- if ( ! fileType ) continue ;
95+ constructor ( ) {
96+ // Clone default types
97+ this . types = new Map (
98+ Object . entries ( FILE_TYPES ) . map ( ( [ name , type ] ) => [
99+ name ,
100+ { extensions : [ ...type . extensions ] , globs : [ ...type . globs ] } ,
101+ ] ) ,
102+ ) ;
103+ }
104+
105+ /**
106+ * Add a type definition
107+ * Format: "name:pattern" where pattern can be:
108+ * - "*.ext" - glob pattern
109+ * - "include:other" - include patterns from another type
110+ */
111+ addType ( spec : string ) : void {
112+ const colonIdx = spec . indexOf ( ":" ) ;
113+ if ( colonIdx === - 1 ) return ;
114+
115+ const name = spec . slice ( 0 , colonIdx ) ;
116+ const pattern = spec . slice ( colonIdx + 1 ) ;
97117
98- // Check extensions
99- for ( const ext of fileType . extensions ) {
100- if ( lowerFilename . endsWith ( ext ) ) {
101- return true ;
118+ if ( pattern . startsWith ( "include:" ) ) {
119+ // Include patterns from another type
120+ const otherName = pattern . slice ( 8 ) ;
121+ const other = this . types . get ( otherName ) ;
122+ if ( other ) {
123+ const existing = this . types . get ( name ) || { extensions : [ ] , globs : [ ] } ;
124+ existing . extensions . push ( ...other . extensions ) ;
125+ existing . globs . push ( ...other . globs ) ;
126+ this . types . set ( name , existing ) ;
102127 }
128+ } else {
129+ // Add glob pattern
130+ const existing = this . types . get ( name ) || { extensions : [ ] , globs : [ ] } ;
131+ // If pattern is like "*.ext", add to extensions
132+ if ( pattern . startsWith ( "*." ) && ! pattern . slice ( 2 ) . includes ( "*" ) ) {
133+ const ext = pattern . slice ( 1 ) ; // Keep the dot
134+ if ( ! existing . extensions . includes ( ext ) ) {
135+ existing . extensions . push ( ext ) ;
136+ }
137+ } else {
138+ // Add as glob pattern
139+ if ( ! existing . globs . includes ( pattern ) ) {
140+ existing . globs . push ( pattern ) ;
141+ }
142+ }
143+ this . types . set ( name , existing ) ;
144+ }
145+ }
146+
147+ /**
148+ * Clear all patterns from a type
149+ */
150+ clearType ( name : string ) : void {
151+ const existing = this . types . get ( name ) ;
152+ if ( existing ) {
153+ existing . extensions = [ ] ;
154+ existing . globs = [ ] ;
103155 }
156+ }
157+
158+ /**
159+ * Get a type by name
160+ */
161+ getType ( name : string ) : FileType | undefined {
162+ return this . types . get ( name ) ;
163+ }
164+
165+ /**
166+ * Get all type names
167+ */
168+ getAllTypes ( ) : Map < string , FileType > {
169+ return this . types ;
170+ }
171+
172+ /**
173+ * Check if a filename matches any of the specified types
174+ */
175+ matchesType ( filename : string , typeNames : string [ ] ) : boolean {
176+ const lowerFilename = filename . toLowerCase ( ) ;
177+
178+ for ( const typeName of typeNames ) {
179+ // Special case: 'all' matches any file with a recognized type
180+ if ( typeName === "all" ) {
181+ if ( this . matchesAnyType ( filename ) ) {
182+ return true ;
183+ }
184+ continue ;
185+ }
186+
187+ const fileType = this . types . get ( typeName ) ;
188+ if ( ! fileType ) continue ;
104189
105- // Check globs (simple exact match for now)
106- for ( const glob of fileType . globs ) {
107- if ( glob . includes ( "*" ) ) {
108- // Simple glob matching
109- const pattern = glob . replace ( / \. / g, "\\." ) . replace ( / \* / g, ".*" ) ;
110- if ( new RegExp ( `^${ pattern } $` , "i" ) . test ( filename ) ) {
190+ // Check extensions
191+ for ( const ext of fileType . extensions ) {
192+ if ( lowerFilename . endsWith ( ext ) ) {
193+ return true ;
194+ }
195+ }
196+
197+ // Check globs
198+ for ( const glob of fileType . globs ) {
199+ if ( glob . includes ( "*" ) ) {
200+ const pattern = glob . replace ( / \. / g, "\\." ) . replace ( / \* / g, ".*" ) ;
201+ if ( new RegExp ( `^${ pattern } $` , "i" ) . test ( filename ) ) {
202+ return true ;
203+ }
204+ } else if ( lowerFilename === glob . toLowerCase ( ) ) {
111205 return true ;
112206 }
113- } else if ( lowerFilename === glob . toLowerCase ( ) ) {
114- return true ;
115207 }
116208 }
209+
210+ return false ;
117211 }
118212
119- return false ;
213+ /**
214+ * Check if a filename matches any recognized type
215+ */
216+ private matchesAnyType ( filename : string ) : boolean {
217+ const lowerFilename = filename . toLowerCase ( ) ;
218+
219+ for ( const fileType of this . types . values ( ) ) {
220+ for ( const ext of fileType . extensions ) {
221+ if ( lowerFilename . endsWith ( ext ) ) {
222+ return true ;
223+ }
224+ }
225+
226+ for ( const glob of fileType . globs ) {
227+ if ( glob . includes ( "*" ) ) {
228+ const pattern = glob . replace ( / \. / g, "\\." ) . replace ( / \* / g, ".*" ) ;
229+ if ( new RegExp ( `^${ pattern } $` , "i" ) . test ( filename ) ) {
230+ return true ;
231+ }
232+ } else if ( lowerFilename === glob . toLowerCase ( ) ) {
233+ return true ;
234+ }
235+ }
236+ }
237+
238+ return false ;
239+ }
120240}
121241
122242/**
0 commit comments