@@ -12,14 +12,18 @@ type Detail = {
1212
1313export type FileOptions = {
1414 /**
15- * Patterns to include files matching specific paths.
15+ * A function to filter files based on their paths.
16+ *
17+ * If the function returns `false`, the file is excluded.
1618 */
17- includes ?: RegExp [ ] ;
19+ filterFile ?: ( path : string ) => boolean ;
1820
1921 /**
20- * Patterns to exclude files matching specific paths.
22+ * A function to filter directories based on their paths.
23+ *
24+ * If the function returns `false`, the directory is excluded.
2125 */
22- excludes ?: RegExp [ ] ;
26+ filterDirectory ?: ( path : string ) => boolean ;
2327} ;
2428
2529/**
@@ -32,7 +36,11 @@ export type FileOptions = {
3236 * @returns A Source that generates items representing filtered files.
3337 */
3438export function file ( options : Readonly < FileOptions > = { } ) : Source < Detail > {
35- const { includes, excludes } = options ;
39+ const {
40+ filterFile = ( ) => true ,
41+ filterDirectory = ( ) => true ,
42+ } = options ;
43+
3644 return defineSource ( async function * ( denops , { args } , { signal } ) {
3745 const root = removeTrailingSeparator (
3846 await denops . eval (
@@ -42,17 +50,12 @@ export function file(options: Readonly<FileOptions> = {}): Source<Detail> {
4250 ) ;
4351 signal ?. throwIfAborted ( ) ;
4452
45- const filter = ( path : string ) => {
46- if ( includes && ! includes . some ( ( p ) => p . test ( path ) ) ) {
47- return false ;
48- } else if ( excludes && excludes . some ( ( p ) => p . test ( path ) ) ) {
49- return false ;
50- }
51- return true ;
52- } ;
53-
5453 // Enumerate files and apply filters
55- for await ( const [ id , path ] of enumerate ( walk ( root , filter , signal ) ) ) {
54+ for await (
55+ const [ id , path ] of enumerate (
56+ walk ( root , filterFile , filterDirectory , signal ) ,
57+ )
58+ ) {
5659 yield {
5760 id,
5861 value : path ,
@@ -64,13 +67,12 @@ export function file(options: Readonly<FileOptions> = {}): Source<Detail> {
6467
6568async function * walk (
6669 root : string ,
67- filter : ( path : string ) => boolean ,
70+ filterFile : ( path : string ) => boolean ,
71+ filterDirectory : ( path : string ) => boolean ,
6872 signal ?: AbortSignal ,
6973) : AsyncIterableIterator < string > {
7074 for await ( const entry of Deno . readDir ( root ) ) {
7175 const path = `${ root } ${ SEPARATOR } ${ entry . name } ` ;
72- // Skip files that do not match the filter
73- if ( ! filter ( path ) ) continue ;
7476 // Follow symbolic links to recursively yield files
7577 let isDirectory = entry . isDirectory ;
7678 if ( entry . isSymlink ) {
@@ -87,9 +89,13 @@ async function* walk(
8789 }
8890 // Recursively yield files from directories, or yield file details
8991 if ( isDirectory ) {
90- yield * walk ( path , filter , signal ) ;
92+ if ( filterDirectory ( path ) ) {
93+ yield * walk ( path , filterFile , filterDirectory , signal ) ;
94+ }
9195 } else {
92- yield path ;
96+ if ( filterFile ( path ) ) {
97+ yield path ;
98+ }
9399 }
94100 }
95101}
0 commit comments