From a979eda983da8df924924bef8ca5ef254906df8c Mon Sep 17 00:00:00 2001 From: Saransh Saini <66969478+saranshsaini@users.noreply.github.com> Date: Tue, 21 Jan 2025 23:22:04 -0800 Subject: [PATCH] Improve Project Parsing Logic (#157) Improved Logic --- CodeiumVS/CodeiumVS.csproj | 1 + CodeiumVS/LanguageServer/LanguageServer.cs | 113 ++++++++++++++++----- CodeiumVS/SettingsPage.cs | 2 +- CodeiumVS/source.extension.vsixmanifest | 2 +- 4 files changed, 90 insertions(+), 28 deletions(-) diff --git a/CodeiumVS/CodeiumVS.csproj b/CodeiumVS/CodeiumVS.csproj index e9714b0..88ef9f3 100644 --- a/CodeiumVS/CodeiumVS.csproj +++ b/CodeiumVS/CodeiumVS.csproj @@ -176,6 +176,7 @@ + diff --git a/CodeiumVS/LanguageServer/LanguageServer.cs b/CodeiumVS/LanguageServer/LanguageServer.cs index 7e570bd..fdb5cd3 100644 --- a/CodeiumVS/LanguageServer/LanguageServer.cs +++ b/CodeiumVS/LanguageServer/LanguageServer.cs @@ -1,4 +1,4 @@ -using CodeiumVS.Packets; +using CodeiumVS.Packets; using EnvDTE; using Microsoft.VisualStudio; using Microsoft.VisualStudio.Imaging; @@ -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; @@ -20,6 +21,7 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using System.Xml.Linq; namespace CodeiumVS; @@ -753,7 +755,7 @@ private async Task InitializeTrackedWorkspaceAsync() } } - var inputProjectsToIndex = new HashSet(StringComparer.OrdinalIgnoreCase); + var inputFilesToIndex = new HashSet(StringComparer.OrdinalIgnoreCase); string projectListPath = _package.SettingsPage.IndexingFilesListPath.Trim(); try { @@ -765,10 +767,13 @@ 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) @@ -776,7 +781,8 @@ private async Task InitializeTrackedWorkspaceAsync() await _package.LogAsync($"Error reading project list: {ex.Message}"); } - List projectsToIndex = await GetFilesToIndex(inputProjectsToIndex, openFilePaths, dte); + List projectsToIndex = new List(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++) @@ -797,17 +803,16 @@ private async Task InitializeTrackedWorkspaceAsync() } } - private async Task> GetFilesToIndex(HashSet inputProjectsToIndex, HashSet openFilePaths, DTE dte) + private async Task> GetFilesToIndex(HashSet openFilePaths, DTE dte) { await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); int maxToIndex = 15; - HashSet specifiedProjectsToIndexPath = new HashSet(); HashSet openFilesProjectsToIndexPath = new HashSet(); HashSet remainingProjectsToIndexPath = new HashSet(); HashSet processedProjects = new HashSet(); 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; } @@ -816,38 +821,95 @@ async Task AddFilesToIndexLists(EnvDTE.Project project) if (!string.IsNullOrEmpty(projectFullName) && !processedProjects.Contains(projectFullName)) { string projectDir = Path.GetDirectoryName(projectFullName); + HashSet sourceDirectories = new HashSet { 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 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(); + 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 matchingFiles = new List(); 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); } @@ -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 result = new List(); - result.AddRange(specifiedProjectsToIndexPath); result.AddRange(openFilesProjectsToIndexPath); result.AddRange(remainingProjectsToIndexPath); return result; diff --git a/CodeiumVS/SettingsPage.cs b/CodeiumVS/SettingsPage.cs index ef2322e..f4b80e0 100644 --- a/CodeiumVS/SettingsPage.cs +++ b/CodeiumVS/SettingsPage.cs @@ -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 diff --git a/CodeiumVS/source.extension.vsixmanifest b/CodeiumVS/source.extension.vsixmanifest index abe6cb9..68615b1 100644 --- a/CodeiumVS/source.extension.vsixmanifest +++ b/CodeiumVS/source.extension.vsixmanifest @@ -1,7 +1,7 @@ - + Codeium The modern coding superpower: free AI code acceleration plugin for your favorite languages. Type less. Code more. Ship faster. https://www.codeium.com