better code completion apply
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
using Microsoft.CodeAnalysis.Completion;
|
||||
using SharpIDE.Application.Features.FileWatching;
|
||||
using SharpIDE.Application.Features.SolutionDiscovery;
|
||||
|
||||
namespace SharpIDE.Application.Features.Analysis;
|
||||
|
||||
public class IdeCompletionService(RoslynAnalysis roslynAnalysis, FileChangedService fileChangedService)
|
||||
{
|
||||
private readonly RoslynAnalysis _roslynAnalysis = roslynAnalysis;
|
||||
private readonly FileChangedService _fileChangedService = fileChangedService;
|
||||
|
||||
public async Task ApplyCompletion(SharpIdeFile file, CompletionItem completionItem)
|
||||
{
|
||||
var (updatedDocumentText, newLinePosition) = await _roslynAnalysis.GetCompletionApplyChanges(file, completionItem);
|
||||
await _fileChangedService.SharpIdeFileChanged(file, updatedDocumentText, FileChangeType.CompletionChange, newLinePosition);
|
||||
}
|
||||
}
|
||||
@@ -575,6 +575,37 @@ public class RoslynAnalysis
|
||||
return completions;
|
||||
}
|
||||
|
||||
public async Task<(string updatedText, SharpIdeFileLinePosition sharpIdeFileLinePosition)> GetCompletionApplyChanges(SharpIdeFile file, CompletionItem completionItem, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var documentId = _workspace!.CurrentSolution.GetDocumentIdsWithFilePath(file.Path).Single();
|
||||
var document = _workspace.CurrentSolution.GetRequiredDocument(documentId);
|
||||
var completionService = CompletionService.GetService(document) ?? throw new InvalidOperationException("Completion service is not available for the document.");
|
||||
|
||||
var completionChange = await completionService.GetChangeAsync(document, completionItem, commitCharacter: '.', cancellationToken: cancellationToken);
|
||||
var sourceText = await document.GetTextAsync(cancellationToken);
|
||||
var newText = sourceText.WithChanges(completionChange.TextChange);
|
||||
var newCaretPosition = completionChange.NewPosition ?? NewCaretPosition();
|
||||
var linePosition = newText.Lines.GetLinePosition(newCaretPosition);
|
||||
var sharpIdeFileLinePosition = new SharpIdeFileLinePosition
|
||||
{
|
||||
Line = linePosition.Line,
|
||||
Column = linePosition.Character
|
||||
};
|
||||
|
||||
return (newText.ToString(), sharpIdeFileLinePosition);
|
||||
|
||||
int NewCaretPosition()
|
||||
{
|
||||
var caretPosition = completionChange.TextChange.Span.Start + completionChange.TextChange.NewText!.Length;
|
||||
// if change ends with (), place caret between the parentheses
|
||||
if (completionChange.TextChange.NewText!.EndsWith("()"))
|
||||
{
|
||||
caretPosition -= 1;
|
||||
}
|
||||
return caretPosition;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the list of files that would be modified by applying the code action. Does not apply the changes to the workspace sln
|
||||
public async Task<List<(SharpIdeFile File, string UpdatedText)>> GetCodeActionApplyChanges(CodeAction codeAction)
|
||||
{
|
||||
|
||||
@@ -13,7 +13,8 @@ public enum FileChangeType
|
||||
IdeSaveToDisk, // Apply to disk
|
||||
IdeUnsavedChange, // Apply only in memory
|
||||
ExternalChange, // Apply to disk, as well as in memory
|
||||
CodeActionChange // Apply to disk, as well as in memory
|
||||
CodeActionChange, // Apply to disk, as well as in memory
|
||||
CompletionChange // Apply only in memory, as well as notify tabs of new content
|
||||
}
|
||||
|
||||
public class FileChangedService(RoslynAnalysis roslynAnalysis, IdeOpenTabsFileManager openTabsFileManager)
|
||||
@@ -59,7 +60,7 @@ public class FileChangedService(RoslynAnalysis roslynAnalysis, IdeOpenTabsFileMa
|
||||
}
|
||||
|
||||
// All file changes should go via this service
|
||||
public async Task SharpIdeFileChanged(SharpIdeFile file, string newContents, FileChangeType changeType)
|
||||
public async Task SharpIdeFileChanged(SharpIdeFile file, string newContents, FileChangeType changeType, SharpIdeFileLinePosition? linePosition = null)
|
||||
{
|
||||
if (changeType is FileChangeType.ExternalChange)
|
||||
{
|
||||
@@ -67,13 +68,19 @@ public class FileChangedService(RoslynAnalysis roslynAnalysis, IdeOpenTabsFileMa
|
||||
// Update any open tabs
|
||||
// update in memory
|
||||
await _openTabsFileManager.UpdateFileTextInMemory(file, newContents);
|
||||
file.FileContentsChangedExternally.InvokeParallelFireAndForget();
|
||||
file.FileContentsChangedExternally.InvokeParallelFireAndForget(linePosition);
|
||||
}
|
||||
else if (changeType is FileChangeType.CodeActionChange)
|
||||
{
|
||||
// update in memory, tabs and save to disk
|
||||
await _openTabsFileManager.UpdateInMemoryIfOpenAndSaveAsync(file, newContents);
|
||||
file.FileContentsChangedExternally.InvokeParallelFireAndForget();
|
||||
file.FileContentsChangedExternally.InvokeParallelFireAndForget(linePosition);
|
||||
}
|
||||
else if (changeType is FileChangeType.CompletionChange)
|
||||
{
|
||||
// update in memory, tabs
|
||||
await _openTabsFileManager.UpdateFileTextInMemory(file, newContents);
|
||||
file.FileContentsChangedExternally.InvokeParallelFireAndForget(linePosition);
|
||||
}
|
||||
else if (changeType is FileChangeType.IdeSaveToDisk)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using R3;
|
||||
using SharpIDE.Application.Features.Analysis;
|
||||
using SharpIDE.Application.Features.Events;
|
||||
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
|
||||
|
||||
@@ -19,7 +20,7 @@ public class SharpIdeFile : ISharpIdeNode, IChildSharpIdeNode, IFileOrFolder
|
||||
public required ReactiveProperty<bool> IsDirty { get; init; }
|
||||
public required bool SuppressDiskChangeEvents { get; set; } // probably has concurrency issues
|
||||
public required DateTimeOffset? LastIdeWriteTime { get; set; }
|
||||
public EventWrapper<Task> FileContentsChangedExternally { get; } = new(() => Task.CompletedTask);
|
||||
public EventWrapper<SharpIdeFileLinePosition?, Task> FileContentsChangedExternally { get; } = new((_) => Task.CompletedTask);
|
||||
|
||||
[SetsRequiredMembers]
|
||||
internal SharpIdeFile(string fullPath, string name, IExpandableSharpIdeNode parent, ConcurrentBag<SharpIdeFile> allFiles)
|
||||
|
||||
Reference in New Issue
Block a user