Skip to content

Commit e06f52c

Browse files
authored
implements file type filtering for Explorer (#2103) (#2104)
* implements file type filtering for Explorer (#2103) * updates to file type filtering (#2103) * refactoring to file type filtering (#2103)
1 parent 464db02 commit e06f52c

File tree

1 file changed

+140
-12
lines changed

1 file changed

+140
-12
lines changed

src/Components/SolutionExplorer.fs

Lines changed: 140 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -146,15 +146,71 @@ module SolutionExplorer =
146146

147147
entry.Children |> Seq.rev |> Seq.map (toModel projPath) |> Seq.toList
148148

149+
// File extension to item type mapping
150+
let getItemTypeForFile (filePath: string) =
151+
let ext = node.path.extname(filePath).ToLowerInvariant()
152+
153+
match ext with
154+
| ".fs"
155+
| ".fsi" -> "Compile"
156+
| ".fsx" -> "Compile"
157+
| ".fsl" -> "FsLex"
158+
| ".fsy" -> "FsYacc"
159+
| ".txt"
160+
| ".md"
161+
| ".json"
162+
| ".xml"
163+
| ".config" -> "Content"
164+
| ".resx" -> "EmbeddedResource"
165+
| ".dll"
166+
| ".exe" -> "Reference"
167+
| _ -> "Content" // Default fallback
168+
169+
// Check if a folder should be included in the tree view
170+
let isSpecialFolder (folderName: string) =
171+
folderName = ".deps"
172+
|| folderName = "bin"
173+
|| folderName = "obj"
174+
|| folderName = "packages"
175+
|| folderName = "docs"
176+
|| folderName = "scripts"
177+
|| folderName = "tools"
178+
149179
let private getProjectModel (proj: Project) =
150180
let projects = Project.getLoaded () |> Seq.toArray
151181

152-
let files =
153-
proj.Items
154-
|> Seq.filter (fun p -> p.Name = "Compile")
155-
|> Seq.map (fun p -> p.VirtualPath, p.FilePath)
156-
|> Seq.toList
157-
|> buildTree proj.Project
182+
183+
// Get project files from FSAC
184+
let projectFiles =
185+
proj.Items |> Seq.map (fun p -> p.VirtualPath, p.FilePath) |> Seq.toList
186+
187+
// Add additional folders that aren't in the project file
188+
let projectDir = node.path.dirname proj.Project
189+
190+
let additionalFolders =
191+
try
192+
let dirContents = node.fs.readdirSync (U2.Case1 projectDir)
193+
194+
dirContents
195+
|> Seq.filter (fun item ->
196+
let fullPath = node.path.join (projectDir, item)
197+
let stats = node.fs.statSync (U2.Case1 fullPath)
198+
let isDir = stats.isDirectory ()
199+
200+
isDir && isSpecialFolder item)
201+
|> Seq.map (fun folder ->
202+
let fullPath = node.path.join (projectDir, folder)
203+
(folder, fullPath))
204+
|> Seq.toList
205+
with ex ->
206+
[]
207+
208+
// Combine project files with additional folders
209+
let allItems =
210+
projectFiles
211+
@ (additionalFolders |> List.map (fun (name, path) -> (name, path)))
212+
213+
let files = buildTree proj.Project allItems
158214

159215
let packageRefs =
160216
proj.PackageReferences
@@ -245,13 +301,85 @@ module SolutionExplorer =
245301

246302
match ws with
247303
| WorkspacePeekFound.Solution sln ->
304+
// Get solution items from the solution file
305+
let solutionItems = sln.Items |> Array.map getItem |> List.ofArray
306+
307+
// Add additional folders that aren't in the solution file
308+
let solutionDir = node.path.dirname sln.Path
309+
310+
// Get existing folder names from solution items to avoid duplicates
311+
let existingFolderNames =
312+
solutionItems
313+
|> List.collect (fun item ->
314+
match item with
315+
| WorkspaceFolder(_, name, _) -> [ name ]
316+
| _ -> [])
317+
318+
let additionalSolutionFolders =
319+
try
320+
let dirContents = node.fs.readdirSync (U2.Case1 solutionDir)
321+
322+
dirContents
323+
|> Seq.filter (fun item ->
324+
let fullPath = node.path.join (solutionDir, item)
325+
let stats = node.fs.statSync (U2.Case1 fullPath)
326+
let isDir = stats.isDirectory ()
327+
328+
isDir && isSpecialFolder item && not (existingFolderNames |> List.contains item))
329+
|> Seq.map (fun folder ->
330+
let fullPath = node.path.join (solutionDir, folder)
331+
332+
// Recursively scan the contents of this folder
333+
let rec scanFolderContents (currentPath: string) (depth: int) =
334+
// Helper function to determine if an item should be hidden
335+
let shouldHideItem (item: string) (depth: int) =
336+
item.StartsWith(".") && item <> ".deps"
337+
|| item = "node_modules"
338+
|| item = ".git"
339+
|| item = ".vs"
340+
|| (item = "packages" && depth > 0)
341+
342+
try
343+
let contents = node.fs.readdirSync (U2.Case1 currentPath)
344+
345+
contents
346+
|> Seq.filter (fun item -> not (shouldHideItem item depth))
347+
|> Seq.map (fun item ->
348+
let itemPath = node.path.join (currentPath, item)
349+
let stats = node.fs.statSync (U2.Case1 itemPath)
350+
351+
if stats.isDirectory () then
352+
// Recursively scan subdirectories (limit depth to avoid infinite recursion)
353+
if depth < 6 then
354+
let subContents = scanFolderContents itemPath (depth + 1)
355+
Model.Folder(ref None, item, itemPath, subContents, "")
356+
else
357+
// For very deep directories, just show as empty folder
358+
Model.Folder(ref None, item, itemPath, [], "")
359+
else
360+
// Use the same file type mapping as project level
361+
let itemType = getItemTypeForFile item
362+
363+
// For files, create a file representation
364+
Model.File(ref None, itemPath, item, None, ""))
365+
|> Seq.toList
366+
with ex ->
367+
[]
368+
369+
let folderContents = scanFolderContents fullPath 0
370+
371+
372+
// Create a WorkspaceFolder with the scanned contents
373+
Model.WorkspaceFolder(ref None, folder, folderContents))
374+
|> Seq.toList
375+
with ex ->
376+
[]
377+
378+
// Combine solution items with additional folders
379+
let allSolutionItems = solutionItems @ additionalSolutionFolders
380+
248381
let s =
249-
Solution(
250-
ref None,
251-
sln.Path,
252-
(node.path.basename sln.Path),
253-
(sln.Items |> Array.map getItem |> List.ofArray)
254-
)
382+
Solution(ref None, sln.Path, (node.path.basename sln.Path), allSolutionItems)
255383

256384
let result = Workspace [ s ]
257385
setParentRef s result

0 commit comments

Comments
 (0)