Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix project.json search in DotNetProjectSystem #673

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 5 additions & 49 deletions src/OmniSharp.DotNet/DotNetWorkspace.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Graph;
using OmniSharp.DotNet.Projects;

namespace OmniSharp.DotNet
{

public class DotNetWorkspace : Workspace
{
private readonly HashSet<string> _projects = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
Expand All @@ -16,13 +15,9 @@ public class DotNetWorkspace : Workspace

public DotNetWorkspace(string initialPath) : base(ProjectReaderSettings.ReadFromEnvironment(), true)
{
var paths = ResolveProjectPath(initialPath);
if (paths != null && paths.Any())
foreach (var path in ProjectSearcher.Search(initialPath))
{
foreach (var path in paths)
{
AddProject(path);
}
AddProject(path);
}
}

Expand Down Expand Up @@ -50,7 +45,7 @@ public IReadOnlyList<string> GetAllProjects()
public IReadOnlyList<ProjectContext> GetProjectContexts(string projectPath)
{
return (IReadOnlyList<ProjectContext>)GetProjectContextCollection(projectPath)?.ProjectContexts.AsReadOnly() ??
Enumerable.Empty<ProjectContext>().ToList().AsReadOnly();
Array.Empty<ProjectContext>();
}

/// <summary>
Expand Down Expand Up @@ -103,45 +98,6 @@ protected override IEnumerable<ProjectContext> BuildProjectContexts(Project proj
}
}

private static List<string> ResolveProjectPath(string projectPath)
{
if (File.Exists(projectPath))
{
var filename = Path.GetFileName(projectPath);
if (!Project.FileName.Equals(filename, StringComparison.OrdinalIgnoreCase) &&
!GlobalSettings.FileName.Equals(filename, StringComparison.OrdinalIgnoreCase))
{
return null;
}

projectPath = Path.GetDirectoryName(projectPath);
}

if (File.Exists(Path.Combine(projectPath, Project.FileName)))
{
return new List<string> { projectPath };
}

if (File.Exists(Path.Combine(projectPath, GlobalSettings.FileName)))
{
var root = ProjectRootResolver.ResolveRootDirectory(projectPath);
GlobalSettings globalSettings;
if (GlobalSettings.TryGetGlobalSettings(projectPath, out globalSettings))
{
return globalSettings.ProjectSearchPaths
.Select(searchPath => Path.Combine(globalSettings.DirectoryPath, searchPath))
.Where(actualPath => Directory.Exists(actualPath))
.SelectMany(actualPath => Directory.GetDirectories(actualPath))
.Where(actualPath => File.Exists(Path.Combine(actualPath, Project.FileName)))
.Select(path => Path.GetFullPath(path))
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
}
}

return null;
}

private static IEnumerable<ProjectDescription> GetProjectReferences(ProjectContext context)
{
var projectDescriptions = context.LibraryManager
Expand All @@ -156,7 +112,7 @@ private static IEnumerable<ProjectDescription> GetProjectReferences(ProjectConte
continue;
}

// if this is an assembly reference then don't threat it as project reference
// if this is an assembly reference then don't treat it as project reference
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol

if (!string.IsNullOrEmpty(description.TargetFrameworkInfo?.AssemblyPath))
{
continue;
Expand Down
117 changes: 78 additions & 39 deletions src/OmniSharp.DotNet/Projects/ProjectSearcher.cs
Original file line number Diff line number Diff line change
@@ -1,66 +1,86 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.DotNet.ProjectModel;

namespace OmniSharp.DotNet.Projects
{
public class ProjectSearcher
{
public static IEnumerable<string> Search(string solutionRoot)
public static IEnumerable<string> Search(string directory)
{
return Search(solutionRoot, maxDepth: 5);
return Search(directory, maxDepth: 5);
}

public static IEnumerable<string> Search(string solutionRoot, int maxDepth)
public static IEnumerable<string> Search(string directory, int maxDepth)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this function was inconsistent before my changes. Sometimes it would return a directory path to a project.json files, and sometimes it would return multiple file paths to project.json files. Now, it just returns file paths to project.json files.

{
var dir = new DirectoryInfo(solutionRoot);
if (!dir.Exists)
if (!Directory.Exists(directory))
{
return Enumerable.Empty<string>();
return Array.Empty<string>();
}

if (File.Exists(Path.Combine(solutionRoot, Project.FileName)))
// Is there a project.json file in this directory? If so, return it.
var projectFilePath = Path.Combine(directory, Project.FileName);
if (File.Exists(projectFilePath))
{
return new string[] { solutionRoot };
return new string[] { projectFilePath };
}
else if (File.Exists(Path.Combine(solutionRoot, GlobalSettings.FileName)))

// Is there a global.json file in this directory? If so, use that to search.
if (File.Exists(Path.Combine(directory, GlobalSettings.FileName)))
{
return FindProjectsThroughGlobalJson(solutionRoot);
return FindProjectsThroughGlobalJson(directory);
}
else

// Otherwise, perform a general search through the file system.
return FindProjects(directory, maxDepth);
}

// TODO: Replace with proper tuple when we move to C# 7
private struct DirectoryAndDepth
{
public readonly string Directory;
public readonly int Depth;

private DirectoryAndDepth(string directory, int depth)
{
return FindProjects(solutionRoot, maxDepth);
this.Directory = directory;
this.Depth = depth;
}

public static DirectoryAndDepth Create(string directory, int depth)
=> new DirectoryAndDepth(directory, depth);
}

private static IEnumerable<string> FindProjects(string root, int maxDepth)
private static IEnumerable<string> FindProjects(string rootDirectory, int maxDepth)
{
var result = new List<string>();
var stack = new Stack<Tuple<DirectoryInfo, int>>();
var result = new SortedSet<string>(StringComparer.OrdinalIgnoreCase);
var stack = new Stack<DirectoryAndDepth>();

stack.Push(Tuple.Create(new DirectoryInfo(root), 0));
stack.Push(DirectoryAndDepth.Create(rootDirectory, 0));

while (stack.Any())
while (stack.Count > 0)
{
var next = stack.Pop();
var currentFolder = next.Item1;
var depth = next.Item2;
var current = stack.Pop();

if (!currentFolder.Exists)
if (!Directory.Exists(current.Directory))
{
continue;
}
else if (currentFolder.GetFiles(Project.FileName).Any())

// Did we find a project.json?
var projectFilePath = Path.Combine(current.Directory, Project.FileName);
if (File.Exists(projectFilePath))
{
result.Add(Path.Combine(currentFolder.FullName, Project.FileName));
result.Add(projectFilePath);
}
else if (depth < maxDepth)

// If we're not already at maximum depth, go ahead and search child directories.
if (current.Depth < maxDepth)
{
foreach (var sub in currentFolder.GetDirectories())
foreach (var childDirectory in Directory.GetDirectories(current.Directory))
{
stack.Push(Tuple.Create(sub, depth + 1));
stack.Push(DirectoryAndDepth.Create(childDirectory, current.Depth + 1));
}
}
}
Expand All @@ -73,19 +93,38 @@ private static IEnumerable<string> FindProjectsThroughGlobalJson(string root)
GlobalSettings globalSettings;
if (GlobalSettings.TryGetGlobalSettings(root, out globalSettings))
{
return globalSettings.ProjectSearchPaths
.Select(searchPath => Path.Combine(globalSettings.DirectoryPath, searchPath))
.Where(actualPath => Directory.Exists(actualPath))
.SelectMany(actualPath => Directory.GetDirectories(actualPath))
.Where(actualPath => File.Exists(Path.Combine(actualPath, Project.FileName)))
.Select(path => Path.GetFullPath(path))
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
}
else
{
return Enumerable.Empty<string>();
var projectPaths = new SortedSet<string>(StringComparer.OrdinalIgnoreCase);
var searchDirectories = new Queue<string>();

// Look in global.json 'projects' search paths and their immediate children
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For my sanity (because project.json is starting to fade with csproj out soon 😜).

global.json never supported wild cards, but instead just looked to see if it a any project.json in the folder or it's children. ie the common case was projects: ["src", "test"].

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Were you looking for a change here?

foreach (var searchPath in globalSettings.ProjectSearchPaths)
{
var searchDirectory = Path.Combine(globalSettings.DirectoryPath, searchPath);
if (Directory.Exists(searchDirectory))
{
searchDirectories.Enqueue(searchDirectory);

foreach (var childDirectory in Directory.GetDirectories(searchDirectory))
{
searchDirectories.Enqueue(childDirectory);
}
}
}

while (searchDirectories.Count > 0)
{
var searchDirectory = searchDirectories.Dequeue();
var projectFilePath = Path.Combine(searchDirectory, Project.FileName);
if (File.Exists(projectFilePath))
{
projectPaths.Add(Path.GetFullPath(projectFilePath));
}
}

return projectPaths;
}

return Array.Empty<string>();
}
}
}
62 changes: 0 additions & 62 deletions tests/OmniSharp.DotNet.Tests/ProjectSearcherTest.cs

This file was deleted.

Loading