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

Improve Project Parsing Logic #157

Merged
merged 2 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions CodeiumVS/CodeiumVS.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@
<Reference Include="System.Net.Http" />
<Reference Include="System.Xaml" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
Expand Down
113 changes: 87 additions & 26 deletions CodeiumVS/LanguageServer/LanguageServer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using CodeiumVS.Packets;
using CodeiumVS.Packets;
using EnvDTE;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Imaging;
Expand All @@ -12,6 +12,7 @@
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography;
Expand All @@ -20,6 +21,7 @@
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace CodeiumVS;

Expand Down Expand Up @@ -753,7 +755,7 @@ private async Task InitializeTrackedWorkspaceAsync()
}
}

var inputProjectsToIndex = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var inputFilesToIndex = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
string projectListPath = _package.SettingsPage.IndexingFilesListPath.Trim();
try
{
Expand All @@ -765,18 +767,22 @@ private async Task InitializeTrackedWorkspaceAsync()
string trimmedLine = line.Trim();
if (!string.IsNullOrEmpty(trimmedLine))
{
inputProjectsToIndex.Add(trimmedLine);
if (Path.IsPathRooted(trimmedLine))
{
inputFilesToIndex.Add(trimmedLine);
}
}
}
await _package.LogAsync($"Number of Projects loaded from {projectListPath}: {inputProjectsToIndex.Count}");
await _package.LogAsync($"Loaded from {inputFilesToIndex.Count} files");
}
}
catch (Exception ex)
{
await _package.LogAsync($"Error reading project list: {ex.Message}");
}

List<string> projectsToIndex = await GetFilesToIndex(inputProjectsToIndex, openFilePaths, dte);
List<string> projectsToIndex = new List<string>(inputFilesToIndex);
projectsToIndex.AddRange(await GetFilesToIndex(openFilePaths, dte));
await _package.LogAsync($"Number of projects to index: {projectsToIndex.Count}");

for (int i = 0; i < projectsToIndex.Count; i++)
Expand All @@ -797,17 +803,16 @@ private async Task InitializeTrackedWorkspaceAsync()
}
}

private async Task<List<string>> GetFilesToIndex(HashSet<string> inputProjectsToIndex, HashSet<string> openFilePaths, DTE dte)
private async Task<List<string>> GetFilesToIndex(HashSet<string> openFilePaths, DTE dte)
{
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
int maxToIndex = 15;
HashSet<string> specifiedProjectsToIndexPath = new HashSet<string>();
HashSet<string> openFilesProjectsToIndexPath = new HashSet<string>();
HashSet<string> remainingProjectsToIndexPath = new HashSet<string>();
HashSet<string> processedProjects = new HashSet<string>();
async Task AddFilesToIndexLists(EnvDTE.Project project)
{
if (specifiedProjectsToIndexPath.Count == inputProjectsToIndex.Count && openFilePaths.Count == 0 && (specifiedProjectsToIndexPath.Count + remainingProjectsToIndexPath.Count + openFilesProjectsToIndexPath.Count) >= maxToIndex)
if (openFilePaths.Count == 0 && (openFilesProjectsToIndexPath.Count + remainingProjectsToIndexPath.Count) >= maxToIndex)
{
return;
}
Expand All @@ -816,38 +821,95 @@ async Task AddFilesToIndexLists(EnvDTE.Project project)
if (!string.IsNullOrEmpty(projectFullName) && !processedProjects.Contains(projectFullName))
{
string projectDir = Path.GetDirectoryName(projectFullName);
HashSet<string> sourceDirectories = new HashSet<string> { projectDir };

// There are three cases.
// 1. The project is in the list of projects to index passed in by the user. These take priority. The entire solution is searched until all are found.
// 2. Find the project the open file is a member of. Not sure if nested projects could match the same file multiple times so delete from the set when found.
// 3. Any other project. Tops it up to the max amount to index if the previous two cases didnt.
if (inputProjectsToIndex.Contains(projectName))
// Parse the csproj file to find all source directories
if (File.Exists(projectFullName) && (projectFullName.EndsWith(".csproj", StringComparison.OrdinalIgnoreCase) || projectFullName.EndsWith(".vcxproj", StringComparison.OrdinalIgnoreCase)))
{
await _package.LogAsync($"Found in input list {projectName}");
specifiedProjectsToIndexPath.Add(projectDir);
try
{
XDocument projDoc = XDocument.Load(projectFullName);
IEnumerable<string> compileItems;
if (projectFullName.EndsWith(".vcxproj", StringComparison.OrdinalIgnoreCase))
{
// Handle C++ project files
compileItems = projDoc.Descendants()
.Where(x => x.Name.LocalName == "ClCompile" || x.Name.LocalName == "ClInclude")
.Select(x => x.Attribute("Include")?.Value)
.Where(x => !string.IsNullOrEmpty(x));
}
else
{
// Handle C# project files
compileItems = projDoc.Descendants()
.Where(x => x.Name.LocalName == "Compile" || x.Name.LocalName == "Content")
.Select(x => x.Attribute("Include")?.Value)
.Where(x => !string.IsNullOrEmpty(x));
}

var fullPaths = new List<string>();
foreach (var item in compileItems)
{
string fullPath = Path.GetFullPath(Path.Combine(projectDir, item));
fullPaths.Add(fullPath);
}

if (fullPaths.Count > 0)
{
// Find the common root directory
string commonRoot = Path.GetDirectoryName(fullPaths[0]);
foreach (var path in fullPaths.Skip(1))
{
string directory = Path.GetDirectoryName(path);
while (!directory.StartsWith(commonRoot, StringComparison.OrdinalIgnoreCase) && commonRoot.Length > 3)
{
commonRoot = Path.GetDirectoryName(commonRoot);
}
}

await _package.LogAsync($"Common root directory: {commonRoot}");
if (Directory.Exists(commonRoot))
{
sourceDirectories.Add(commonRoot);
}
}
}
catch (Exception ex)
{
await _package.LogAsync($"Failed to parse project file {projectFullName}: {ex.Message}");
}
}
else if (openFilePaths.Count != 0)

if (openFilePaths.Count != 0)
{
string matchingFile = null;
List<string> matchingFiles = new List<string>();
foreach (var filePath in openFilePaths)
{
if (filePath.StartsWith(projectDir, StringComparison.OrdinalIgnoreCase))
if (sourceDirectories.Any(dir => filePath.StartsWith(dir, StringComparison.OrdinalIgnoreCase)))
{
await _package.LogAsync($"Found in open files {filePath}");
matchingFile = filePath;
break;
matchingFiles.Add(filePath);
}
}
if (!string.IsNullOrEmpty(matchingFile))
if (matchingFiles.Count > 0)
{
openFilesProjectsToIndexPath.Add(projectDir);
openFilePaths.Remove(matchingFile);
foreach (var dir in sourceDirectories)
{
openFilesProjectsToIndexPath.Add(dir);
}
foreach (var file in matchingFiles)
{
openFilePaths.Remove(file);
}
}
}
else
{
await _package.LogAsync($"Found in remaining {projectName}");
remainingProjectsToIndexPath.Add(projectDir);
foreach (var dir in sourceDirectories)
{
remainingProjectsToIndexPath.Add(dir);
}
}
processedProjects.Add(projectFullName);
}
Expand Down Expand Up @@ -875,14 +937,13 @@ async Task AddFilesToIndexLists(EnvDTE.Project project)
{
await AddFilesToIndexLists(project);
}
catch (Exception ex)
catch (Exception ex)
{
await _package.LogAsync($"Failed to process project: {ex.Message}");
continue;
}
}
List<string> result = new List<string>();
result.AddRange(specifiedProjectsToIndexPath);
result.AddRange(openFilesProjectsToIndexPath);
result.AddRange(remainingProjectsToIndexPath);
return result;
Expand Down
2 changes: 1 addition & 1 deletion CodeiumVS/SettingsPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class SettingsPage : DialogPage
private bool indexOpenFiles = true;

[Category("Codeium")]
[DisplayName("Enterprise Mode")]
[DisplayName("Self-Hosted Enterprise Mode")]
[Description(
"Set this to True if using Visual Studio with Codeium Enterprise. Requires restart.")]
public bool EnterpriseMode
Expand Down
2 changes: 1 addition & 1 deletion CodeiumVS/source.extension.vsixmanifest
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<Identity Id="Codeium.VisualStudio" Version="1.8.95" Language="en-US" Publisher="Codeium" />
<Identity Id="Codeium.VisualStudio" Version="1.8.96" Language="en-US" Publisher="Codeium" />
<DisplayName>Codeium</DisplayName>
<Description xml:space="preserve">The modern coding superpower: free AI code acceleration plugin for your favorite languages. Type less. Code more. Ship faster.</Description>
<MoreInfo>https://www.codeium.com</MoreInfo>
Expand Down