Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ private async Task<string> GetDotnetSdkFolderFromDotnetExecutableAsync(string pr

public Process Run(string[] arguments, string? workingDirectory, bool shouldLocalizeOutput, bool redirectStandardInput = false)
{
_logger.LogDebug($"Running dotnet CLI command at {_dotnetExecutablePath.Value} in directory {workingDirectory} with arguments {arguments}");
_logger.LogDebug($"Running dotnet CLI command at {_dotnetExecutablePath.Value} in directory {workingDirectory} with arguments '{string.Join(' ', arguments)}'");

var startInfo = new ProcessStartInfo(_dotnetExecutablePath.Value)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.LanguageServer.Handler.DebugConfiguration;
using Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.FileWatching;
using Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.ProjectTelemetry;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.CodeAnalysis.ProjectSystem;
Expand All @@ -25,7 +26,8 @@ internal sealed class LoadedProject : IDisposable

private readonly ProjectSystemProject _projectSystemProject;
private readonly ProjectSystemProjectOptionsProcessor _optionsProcessor;
private readonly IFileChangeContext _fileChangeContext;
private readonly IFileChangeContext _sourceFileChangeContext;
private readonly IFileChangeContext _projectFileChangeContext;
private readonly ProjectTargetFrameworkManager _targetFrameworkManager;

/// <summary>
Expand Down Expand Up @@ -53,22 +55,16 @@ public LoadedProject(ProjectSystemProject projectSystemProject, SolutionServices
// TODO: we only should listen for add/removals here, but we can't specify such a filter now
_projectDirectory = Path.GetDirectoryName(_projectFilePath)!;

_fileChangeContext = fileWatcher.CreateContext([new(_projectDirectory, [".cs", ".cshtml", ".razor"])]);
_fileChangeContext.FileChanged += FileChangedContext_FileChanged;
_sourceFileChangeContext = fileWatcher.CreateContext([new(_projectDirectory, [".cs", ".cshtml", ".razor"])]);
_sourceFileChangeContext.FileChanged += SourceFileChangeContext_FileChanged;

// Start watching for file changes for the project file as well
_fileChangeContext.EnqueueWatchingFile(_projectFilePath);
_projectFileChangeContext = fileWatcher.CreateContext([]);
_projectFileChangeContext.FileChanged += ProjectFileChangeContext_FileChanged;
_projectFileChangeContext.EnqueueWatchingFile(_projectFilePath);
}

private void FileChangedContext_FileChanged(object? sender, string filePath)
private void SourceFileChangeContext_FileChanged(object? sender, string filePath)
{
// If the project file itself changed, we almost certainly need to reload the project.
if (string.Equals(filePath, _projectFilePath, StringComparison.OrdinalIgnoreCase))
{
NeedsReload?.Invoke(this, EventArgs.Empty);
return;
}

var matchers = _mostRecentFileMatchers?.Value;
if (matchers is null)
{
Expand All @@ -92,6 +88,11 @@ private void FileChangedContext_FileChanged(object? sender, string filePath)
}
}

private void ProjectFileChangeContext_FileChanged(object? sender, string filePath)
{
NeedsReload?.Invoke(this, EventArgs.Empty);
}

public event EventHandler? NeedsReload;

public string? GetTargetFramework()
Expand All @@ -105,7 +106,8 @@ private void FileChangedContext_FileChanged(object? sender, string filePath)
/// </summary>
public void Dispose()
{
_fileChangeContext.Dispose();
_sourceFileChangeContext.Dispose();
_projectFileChangeContext.Dispose();
_optionsProcessor.Dispose();
_projectSystemProject.RemoveFromWorkspace();
}
Expand Down Expand Up @@ -221,7 +223,7 @@ public void Dispose()
document => _projectSystemProject.RemoveDynamicSourceFile(document.FilePath),
"Project {0} now has {1} dynamic file(s).");

WatchProjectAssetsFile(newProjectInfo, _fileChangeContext);
WatchProjectAssetsFile(newProjectInfo);

var needsRestore = ProjectDependencyHelper.NeedsRestore(newProjectInfo, _mostRecentFileInfo, logger);

Expand Down Expand Up @@ -272,7 +274,7 @@ void UpdateProjectSystemProjectCollection<T>(IEnumerable<T> loadedCollection, IE
logger.LogTrace(logMessage, projectFullPathWithTargetFramework, newItems.Count);
}

void WatchProjectAssetsFile(ProjectFileInfo currentProjectInfo, IFileChangeContext fileChangeContext)
void WatchProjectAssetsFile(ProjectFileInfo currentProjectInfo)
{
if (_mostRecentFileInfo?.ProjectAssetsFilePath == currentProjectInfo.ProjectAssetsFilePath)
{
Expand All @@ -282,14 +284,9 @@ void WatchProjectAssetsFile(ProjectFileInfo currentProjectInfo, IFileChangeConte

// Dispose of the last once since we're changing the file we're watching.
_mostRecentProjectAssetsFileWatcher?.Dispose();

IWatchedFile? currentWatcher = null;
if (currentProjectInfo.ProjectAssetsFilePath != null)
{
currentWatcher = fileChangeContext.EnqueueWatchingFile(currentProjectInfo.ProjectAssetsFilePath);
}

_mostRecentProjectAssetsFileWatcher = currentWatcher;
_mostRecentProjectAssetsFileWatcher = currentProjectInfo.ProjectAssetsFilePath is { } assetsFilePath
? _projectFileChangeContext.EnqueueWatchingFile(assetsFilePath)
: null;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Immutable;
using System.Composition;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.Extensions.Logging;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.LanguageServer.Handler;
Expand All @@ -17,14 +18,16 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler;
[Method(MethodName)]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class RestoreHandler(DotnetCliHelper dotnetCliHelper) : ILspServiceRequestHandler<RestoreParams, RestorePartialResult[]>
internal sealed class RestoreHandler(DotnetCliHelper dotnetCliHelper, ILoggerFactory loggerFactory) : ILspServiceRequestHandler<RestoreParams, RestorePartialResult[]>
{
internal const string MethodName = "workspace/_roslyn_restore";

public bool MutatesSolutionState => false;

public bool RequiresLSPSolution => true;

private readonly ILogger<RestoreHandler> _logger = loggerFactory.CreateLogger<RestoreHandler>();

public async Task<RestorePartialResult[]> HandleRequestAsync(RestoreParams request, RequestContext context, CancellationToken cancellationToken)
{
Contract.ThrowIfNull(context.Solution);
Expand All @@ -35,18 +38,31 @@ public async Task<RestorePartialResult[]> HandleRequestAsync(RestoreParams reque
var restorePaths = GetRestorePaths(request, context.Solution, context);
if (restorePaths.IsEmpty)
{
_logger.LogDebug($"Restore was requested but no paths were provided.");
progress.Report(new RestorePartialResult(LanguageServerResources.Restore, LanguageServerResources.Nothing_found_to_restore));
return progress.GetValues() ?? [];
}

await RestoreAsync(restorePaths, progress, cancellationToken);
_logger.LogDebug($"Running restore on {restorePaths.Length} paths, starting with '{restorePaths.First()}'.");
bool success = await RestoreAsync(restorePaths, progress, cancellationToken);

progress.Report(new RestorePartialResult(LanguageServerResources.Restore, $"{LanguageServerResources.Restore_complete}{Environment.NewLine}"));
if (success)
{
_logger.LogDebug($"Restore completed successfully.");
}
else
{
_logger.LogError($"Restore completed with errors. See '.NET NuGet Restore' output window for more details.");
}

return progress.GetValues() ?? [];
}

private async Task RestoreAsync(ImmutableArray<string> pathsToRestore, BufferedProgress<RestorePartialResult> progress, CancellationToken cancellationToken)
/// <returns>True if all restore invocations exited with code 0. Otherwise, false.</returns>
private async Task<bool> RestoreAsync(ImmutableArray<string> pathsToRestore, BufferedProgress<RestorePartialResult> progress, CancellationToken cancellationToken)
{
bool success = true;
foreach (var path in pathsToRestore)
{
var arguments = new string[] { "restore", path };
Expand All @@ -71,9 +87,12 @@ private async Task RestoreAsync(ImmutableArray<string> pathsToRestore, BufferedP
if (process.ExitCode != 0)
{
ReportProgress(progress, stageName, string.Format(LanguageServerResources.Failed_to_run_restore_on_0, path));
success = false;
}
}

return success;

static void ReportProgress(BufferedProgress<RestorePartialResult> progress, string stage, string? restoreOutput)
{
if (restoreOutput != null)
Expand Down
Loading