1- module FSharpLint.Console.Program
1+ module FSharpLint.Console.Program
22
33open Argu
44open System
@@ -18,6 +18,7 @@ type internal FileType =
1818 | Solution = 2
1919 | File = 3
2020 | Source = 4
21+ | Wildcard = 5
2122
2223// Allowing underscores in union case names for proper Argu command line option formatting.
2324// fsharplint:disable UnionCasesNames
4950 | Lint_ Config _ -> " Path to the config for the lint."
5051// fsharplint:enable UnionCasesNames
5152
53+ /// Expands a wildcard pattern to a list of matching files.
54+ /// Supports recursive search using ** (e.g., "**/*.fs" or "src/**/*.fs")
55+ let internal expandWildcard ( pattern : string ) =
56+ let isFSharpFile ( filePath : string ) =
57+ filePath.EndsWith " .fs" || filePath.EndsWith " .fsx"
58+
59+ let normalizedPattern = pattern.Replace( '\\' , '/' )
60+
61+ let directory , searchPattern , searchOption =
62+ match normalizedPattern.IndexOf " **/" with
63+ | - 1 ->
64+ // Non-recursive pattern
65+ match normalizedPattern.LastIndexOf '/' with
66+ | - 1 -> ( " ." , normalizedPattern, SearchOption.TopDirectoryOnly)
67+ | lastSeparator ->
68+ let dir = normalizedPattern.Substring( 0 , lastSeparator)
69+ let pat = normalizedPattern.Substring( lastSeparator + 1 )
70+ (( if String.IsNullOrEmpty dir then " ." else dir), pat, SearchOption.TopDirectoryOnly)
71+ | 0 ->
72+ // Pattern starts with **/
73+ let pat = normalizedPattern.Substring 3
74+ ( " ." , pat, SearchOption.AllDirectories)
75+ | doubleStarIndex ->
76+ // Pattern has **/ in the middle
77+ let dir = normalizedPattern.Substring( 0 , doubleStarIndex) .TrimEnd '/'
78+ let pat = normalizedPattern.Substring( doubleStarIndex + 3 )
79+ ( dir, pat, SearchOption.AllDirectories)
80+
81+ let fullDirectory = Path.GetFullPath directory
82+ if Directory.Exists fullDirectory then
83+ Directory.GetFiles( fullDirectory, searchPattern, searchOption)
84+ |> Array.filter isFSharpFile
85+ |> Array.toList
86+ else
87+ List.empty
88+
5289let private parserProgress ( output : Output.IOutput ) = function
5390 | Starting file ->
5491 String.Format( Resources.GetString( " ConsoleStartingFile" ), file) |> output.WriteInfo
@@ -59,9 +96,15 @@ let private parserProgress (output:Output.IOutput) = function
5996 output.WriteError
6097 $" Exception Message:{Environment.NewLine}{parseException.Message}{Environment.NewLine}Exception Stack Trace:{Environment.NewLine}{parseException.StackTrace}{Environment.NewLine}"
6198
99+ /// Checks if a string contains wildcard characters.
100+ let internal containsWildcard ( target : string ) =
101+ target.Contains( " *" ) || target.Contains( " ?" )
102+
62103/// Infers the file type of the target based on its file extension.
63104let internal inferFileType ( target : string ) =
64- if target.EndsWith " .fs" || target.EndsWith " .fsx" then
105+ if containsWildcard target then
106+ FileType.Wildcard
107+ else if target.EndsWith " .fs" || target.EndsWith " .fsx" then
65108 FileType.File
66109 else if target.EndsWith " .fsproj" then
67110 FileType.Project
@@ -125,6 +168,15 @@ let private start (arguments:ParseResults<ToolArgs>) (toolsPath:Ionide.ProjInfo.
125168 | FileType.File -> Lint.lintFile lintParams target
126169 | FileType.Source -> Lint.lintSource lintParams target
127170 | FileType.Solution -> Lint.lintSolution lintParams target toolsPath
171+ | FileType.Wildcard ->
172+ output.WriteInfo $" Wildcard detected, but not recommended. Using a project (slnx/sln/fsproj) can detect more issues."
173+ let files = expandWildcard target
174+ if List.isEmpty files then
175+ output.WriteInfo $" No files matching pattern '%s {target}' were found."
176+ LintResult.Success List.empty
177+ else
178+ output.WriteInfo $" Found %d {List.length files} file(s) matching pattern '%s {target}'."
179+ Lint.lintFiles lintParams files
128180 | FileType.Project
129181 | _ -> Lint.lintProject lintParams target toolsPath
130182 handleLintResult lintResult
0 commit comments