Rework file persistence
This commit is contained in:
@@ -5,6 +5,7 @@ using Ardalis.GuardClauses;
|
|||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.Classification;
|
using Microsoft.CodeAnalysis.Classification;
|
||||||
|
using Microsoft.CodeAnalysis.CodeActions;
|
||||||
using Microsoft.CodeAnalysis.CodeFixes;
|
using Microsoft.CodeAnalysis.CodeFixes;
|
||||||
using Microsoft.CodeAnalysis.CodeRefactorings;
|
using Microsoft.CodeAnalysis.CodeRefactorings;
|
||||||
using Microsoft.CodeAnalysis.Completion;
|
using Microsoft.CodeAnalysis.Completion;
|
||||||
@@ -29,7 +30,8 @@ namespace SharpIDE.Application.Features.Analysis;
|
|||||||
|
|
||||||
public static class RoslynAnalysis
|
public static class RoslynAnalysis
|
||||||
{
|
{
|
||||||
public static MSBuildWorkspace? _workspace;
|
public static AdhocWorkspace? _workspace;
|
||||||
|
private static MSBuildProjectLoader? _msBuildProjectLoader;
|
||||||
private static RemoteSnapshotManager? _snapshotManager;
|
private static RemoteSnapshotManager? _snapshotManager;
|
||||||
private static RemoteSemanticTokensLegendService? _semanticTokensLegendService;
|
private static RemoteSemanticTokensLegendService? _semanticTokensLegendService;
|
||||||
private static SharpIdeSolutionModel? _sharpIdeSolutionModel;
|
private static SharpIdeSolutionModel? _sharpIdeSolutionModel;
|
||||||
@@ -68,7 +70,7 @@ public static class RoslynAnalysis
|
|||||||
var container = configuration.CreateContainer();
|
var container = configuration.CreateContainer();
|
||||||
|
|
||||||
var host = MefHostServices.Create(container);
|
var host = MefHostServices.Create(container);
|
||||||
_workspace = MSBuildWorkspace.Create(host);
|
_workspace = new AdhocWorkspace(host);
|
||||||
_workspace.RegisterWorkspaceFailedHandler(o => throw new InvalidOperationException($"Workspace failed: {o.Diagnostic.Message}"));
|
_workspace.RegisterWorkspaceFailedHandler(o => throw new InvalidOperationException($"Workspace failed: {o.Diagnostic.Message}"));
|
||||||
|
|
||||||
var snapshotManager = container.GetExports<RemoteSnapshotManager>().FirstOrDefault();
|
var snapshotManager = container.GetExports<RemoteSnapshotManager>().FirstOrDefault();
|
||||||
@@ -76,10 +78,14 @@ public static class RoslynAnalysis
|
|||||||
|
|
||||||
_semanticTokensLegendService = container.GetExports<RemoteSemanticTokensLegendService>().FirstOrDefault();
|
_semanticTokensLegendService = container.GetExports<RemoteSemanticTokensLegendService>().FirstOrDefault();
|
||||||
_semanticTokensLegendService!.SetLegend(TokenTypeProvider.ConstructTokenTypes(false), TokenTypeProvider.ConstructTokenModifiers());
|
_semanticTokensLegendService!.SetLegend(TokenTypeProvider.ConstructTokenTypes(false), TokenTypeProvider.ConstructTokenModifiers());
|
||||||
|
|
||||||
|
_msBuildProjectLoader = new MSBuildProjectLoader(_workspace);
|
||||||
}
|
}
|
||||||
using (var ___ = SharpIdeOtel.Source.StartActivity("OpenSolution"))
|
using (var ___ = SharpIdeOtel.Source.StartActivity("OpenSolution"))
|
||||||
{
|
{
|
||||||
var solution = await _workspace.OpenSolutionAsync(_sharpIdeSolutionModel.FilePath, new Progress());
|
var solutionInfo = await _msBuildProjectLoader!.LoadSolutionInfoAsync(_sharpIdeSolutionModel.FilePath);
|
||||||
|
_workspace.ClearSolution();
|
||||||
|
var solution = _workspace.AddSolution(solutionInfo);
|
||||||
}
|
}
|
||||||
timer.Stop();
|
timer.Stop();
|
||||||
Console.WriteLine($"RoslynAnalysis: Solution loaded in {timer.ElapsedMilliseconds}ms");
|
Console.WriteLine($"RoslynAnalysis: Solution loaded in {timer.ElapsedMilliseconds}ms");
|
||||||
@@ -427,27 +433,49 @@ public static class RoslynAnalysis
|
|||||||
return completions;
|
return completions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task ApplyCodeActionAsync(CodeAction codeAction)
|
/// Returns the list of files modified by applying the code action
|
||||||
|
public static async Task<List<(SharpIdeFile File, string UpdatedText)>> ApplyCodeActionAsync(CodeAction codeAction)
|
||||||
{
|
{
|
||||||
using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(ApplyCodeActionAsync)}");
|
using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(ApplyCodeActionAsync)}");
|
||||||
var cancellationToken = CancellationToken.None;
|
var cancellationToken = CancellationToken.None;
|
||||||
var operations = await codeAction.GetOperationsAsync(cancellationToken);
|
var operations = await codeAction.GetOperationsAsync(cancellationToken);
|
||||||
|
var changedDocumentIds = new List<DocumentId>();
|
||||||
foreach (var operation in operations)
|
foreach (var operation in operations)
|
||||||
{
|
{
|
||||||
operation.Apply(_workspace!, cancellationToken);
|
if (operation is ApplyChangesOperation applyChangesOperation)
|
||||||
// if (operation is ApplyChangesOperation applyChangesOperation)
|
{
|
||||||
// {
|
var newSolution = applyChangesOperation.ChangedSolution;
|
||||||
// var newSolution = applyChangesOperation.ChangedSolution;
|
var changedDocIds = newSolution
|
||||||
// _workspace.TryApplyChanges(newSolution);
|
.GetChanges(_workspace!.CurrentSolution)
|
||||||
// }
|
.GetProjectChanges()
|
||||||
// else
|
.SelectMany(s => s.GetChangedDocuments());
|
||||||
// {
|
changedDocumentIds.AddRange(changedDocIds);
|
||||||
// throw new NotSupportedException($"Unsupported operation type: {operation.GetType().Name}");
|
|
||||||
// }
|
_workspace.TryApplyChanges(newSolution);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new NotSupportedException($"Unsupported operation type: {operation.GetType().Name}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var changedFilesWithText = await changedDocumentIds
|
||||||
|
.DistinctBy(s => s.Id)
|
||||||
|
.Select(id => _workspace!.CurrentSolution.GetDocument(id))
|
||||||
|
.Where(d => d is not null)
|
||||||
|
.OfType<Document>() // ensures non-null
|
||||||
|
.ToAsyncEnumerable()
|
||||||
|
.Select(async (Document doc, CancellationToken ct) =>
|
||||||
|
{
|
||||||
|
var text = await doc.GetTextAsync(ct);
|
||||||
|
var sharpFile = _sharpIdeSolutionModel!.AllFiles.Single(f => f.Path == doc.FilePath);
|
||||||
|
return (sharpFile, text.ToString());
|
||||||
|
})
|
||||||
|
.ToListAsync(cancellationToken);
|
||||||
|
|
||||||
|
return changedFilesWithText;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use AdhocWorkspace or something else, to avoid writing to disk on every change
|
|
||||||
public static void UpdateDocument(SharpIdeFile fileModel, string newContent)
|
public static void UpdateDocument(SharpIdeFile fileModel, string newContent)
|
||||||
{
|
{
|
||||||
Guard.Against.Null(fileModel, nameof(fileModel));
|
Guard.Against.Null(fileModel, nameof(fileModel));
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using SharpIDE.Application.Features.Analysis;
|
||||||
|
using SharpIDE.Application.Features.SolutionDiscovery;
|
||||||
|
|
||||||
|
namespace SharpIDE.Application.Features.FilePersistence;
|
||||||
|
#pragma warning disable VSTHRD011
|
||||||
|
|
||||||
|
/// Holds the in memory copies of files, and manages saving/loading them to/from disk.
|
||||||
|
public class IdeFileManager
|
||||||
|
{
|
||||||
|
private ConcurrentDictionary<SharpIdeFile, Lazy<Task<string>>> _openFiles = new();
|
||||||
|
|
||||||
|
/// Implicitly 'opens' a file if not already open, and returns the text.
|
||||||
|
public async Task<string> GetFileTextAsync(SharpIdeFile file)
|
||||||
|
{
|
||||||
|
var textTaskLazy = _openFiles.GetOrAdd(file, f =>
|
||||||
|
{
|
||||||
|
var lazy = new Lazy<Task<string>>(Task<string> () => File.ReadAllTextAsync(f.Path));
|
||||||
|
return lazy;
|
||||||
|
});
|
||||||
|
var textTask = textTaskLazy.Value;
|
||||||
|
var text = await textTask;
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calling this assumes that the file is already open - may need to be revisited for code fixes and refactorings. I think all files involved in a multi-file fix/refactor shall just be saved to disk immediately.
|
||||||
|
public void UpdateFileTextInMemory(SharpIdeFile file, string newText)
|
||||||
|
{
|
||||||
|
if (!_openFiles.ContainsKey(file)) throw new InvalidOperationException("File is not open in memory.");
|
||||||
|
|
||||||
|
var newLazyTask = new Lazy<Task<string>>(() => Task.FromResult(newText));
|
||||||
|
_openFiles[file] = newLazyTask;
|
||||||
|
// Potentially should be event based?
|
||||||
|
if (file.IsRoslynWorkspaceFile)
|
||||||
|
{
|
||||||
|
RoslynAnalysis.UpdateDocument(file, newText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SaveFileAsync(SharpIdeFile file)
|
||||||
|
{
|
||||||
|
if (!_openFiles.ContainsKey(file)) throw new InvalidOperationException("File is not open in memory.");
|
||||||
|
|
||||||
|
var text = await GetFileTextAsync(file);
|
||||||
|
await File.WriteAllTextAsync(file.Path, text);
|
||||||
|
file.IsDirty.Value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateInMemoryIfOpenAndSaveAsync(SharpIdeFile file, string newText)
|
||||||
|
{
|
||||||
|
if (_openFiles.ContainsKey(file))
|
||||||
|
{
|
||||||
|
UpdateFileTextInMemory(file, newText);
|
||||||
|
await SaveFileAsync(file);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await File.WriteAllTextAsync(file.Path, newText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SaveAllOpenFilesAsync()
|
||||||
|
{
|
||||||
|
foreach (var file in _openFiles.Keys)
|
||||||
|
{
|
||||||
|
await SaveFileAsync(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma warning restore VSTHRD011
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using R3;
|
||||||
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
|
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
|
||||||
|
|
||||||
namespace SharpIDE.Application.Features.SolutionDiscovery;
|
namespace SharpIDE.Application.Features.SolutionDiscovery;
|
||||||
@@ -10,6 +11,10 @@ public class SharpIdeFile : ISharpIdeNode, IChildSharpIdeNode
|
|||||||
public required string Path { get; set; }
|
public required string Path { get; set; }
|
||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
public bool IsRazorFile => Path.EndsWith(".razor", StringComparison.OrdinalIgnoreCase);
|
public bool IsRazorFile => Path.EndsWith(".razor", StringComparison.OrdinalIgnoreCase);
|
||||||
|
public bool IsCshtmlFile => Path.EndsWith(".cshtml", StringComparison.OrdinalIgnoreCase);
|
||||||
|
public bool IsCsharpFile => Path.EndsWith(".cs", StringComparison.OrdinalIgnoreCase);
|
||||||
|
public bool IsRoslynWorkspaceFile => IsCsharpFile || IsRazorFile || IsCshtmlFile;
|
||||||
|
public required ReactiveProperty<bool> IsDirty { get; set; }
|
||||||
|
|
||||||
[SetsRequiredMembers]
|
[SetsRequiredMembers]
|
||||||
internal SharpIdeFile(string fullPath, string name, IExpandableSharpIdeNode parent, ConcurrentBag<SharpIdeFile> allFiles)
|
internal SharpIdeFile(string fullPath, string name, IExpandableSharpIdeNode parent, ConcurrentBag<SharpIdeFile> allFiles)
|
||||||
@@ -17,6 +22,7 @@ public class SharpIdeFile : ISharpIdeNode, IChildSharpIdeNode
|
|||||||
Path = fullPath;
|
Path = fullPath;
|
||||||
Name = name;
|
Name = name;
|
||||||
Parent = parent;
|
Parent = parent;
|
||||||
|
IsDirty = new ReactiveProperty<bool>(false);
|
||||||
allFiles.Add(this);
|
allFiles.Add(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,7 @@
|
|||||||
<PackageReference Include="Microsoft.VisualStudio.SolutionPersistence" />
|
<PackageReference Include="Microsoft.VisualStudio.SolutionPersistence" />
|
||||||
<PackageReference Include="NuGet.Protocol" />
|
<PackageReference Include="NuGet.Protocol" />
|
||||||
<PackageReference Include="ObservableCollections" />
|
<PackageReference Include="ObservableCollections" />
|
||||||
|
<PackageReference Include="R3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://dyoci88kqk2r1
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using Ardalis.GuardClauses;
|
using Ardalis.GuardClauses;
|
||||||
using Godot;
|
using Godot;
|
||||||
|
using R3;
|
||||||
using SharpIDE.Application.Features.Analysis;
|
using SharpIDE.Application.Features.Analysis;
|
||||||
using SharpIDE.Application.Features.Debugging;
|
using SharpIDE.Application.Features.Debugging;
|
||||||
using SharpIDE.Application.Features.Events;
|
using SharpIDE.Application.Features.Events;
|
||||||
@@ -74,6 +75,16 @@ public partial class CodeEditorPanel : MarginContainer
|
|||||||
_tabContainer.SetTabTooltip(newTabIndex, file.Path);
|
_tabContainer.SetTabTooltip(newTabIndex, file.Path);
|
||||||
_tabContainer.CurrentTab = newTabIndex;
|
_tabContainer.CurrentTab = newTabIndex;
|
||||||
});
|
});
|
||||||
|
file.IsDirty.Skip(1).SubscribeOnThreadPool().SubscribeAwait(async (isDirty, ct) =>
|
||||||
|
{
|
||||||
|
//GD.Print($"File dirty state changed: {file.Path} is now {(isDirty ? "dirty" : "clean")}");
|
||||||
|
await this.InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
var tabIndex = newTab.GetIndex();
|
||||||
|
var title = file.Name + (isDirty ? " (*)" : "");
|
||||||
|
_tabContainer.SetTabTitle(tabIndex, title);
|
||||||
|
});
|
||||||
|
});
|
||||||
await newTab.SetSharpIdeFile(file);
|
await newTab.SetSharpIdeFile(file);
|
||||||
if (fileLinePosition is not null) await this.InvokeAsync(() => newTab.SetFileLinePosition(fileLinePosition.Value));
|
if (fileLinePosition is not null) await this.InvokeAsync(() => newTab.SetFileLinePosition(fileLinePosition.Value));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using Ardalis.GuardClauses;
|
|
||||||
using Godot;
|
using Godot;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.Classification;
|
using Microsoft.CodeAnalysis.Classification;
|
||||||
@@ -93,10 +92,11 @@ public partial class SharpIdeCodeEdit : CodeEdit
|
|||||||
GD.Print($"Selection changed to line {_currentLine}, start {_selectionStartCol}, end {_selectionEndCol}");
|
GD.Print($"Selection changed to line {_currentLine}, start {_selectionStartCol}, end {_selectionEndCol}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTextChanged()
|
private async void OnTextChanged()
|
||||||
{
|
{
|
||||||
// update the MSBuildWorkspace
|
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
|
||||||
RoslynAnalysis.UpdateDocument(_currentFile, Text);
|
_currentFile.IsDirty.Value = true;
|
||||||
|
Singletons.FileManager.UpdateFileTextInMemory(_currentFile, Text);
|
||||||
_ = Task.GodotRun(async () =>
|
_ = Task.GodotRun(async () =>
|
||||||
{
|
{
|
||||||
var syntaxHighlighting = RoslynAnalysis.GetDocumentSyntaxHighlighting(_currentFile);
|
var syntaxHighlighting = RoslynAnalysis.GetDocumentSyntaxHighlighting(_currentFile);
|
||||||
@@ -122,8 +122,12 @@ public partial class SharpIdeCodeEdit : CodeEdit
|
|||||||
var vScroll = GetVScroll();
|
var vScroll = GetVScroll();
|
||||||
_ = Task.GodotRun(async () =>
|
_ = Task.GodotRun(async () =>
|
||||||
{
|
{
|
||||||
await RoslynAnalysis.ApplyCodeActionAsync(codeAction);
|
var affectedFiles = await RoslynAnalysis.ApplyCodeActionAsync(codeAction);
|
||||||
var fileContents = await File.ReadAllTextAsync(_currentFile.Path);
|
foreach (var (affectedFile, updatedText) in affectedFiles)
|
||||||
|
{
|
||||||
|
await Singletons.FileManager.UpdateInMemoryIfOpenAndSaveAsync(affectedFile, updatedText);
|
||||||
|
}
|
||||||
|
var fileContents = await Singletons.FileManager.GetFileTextAsync(_currentFile);
|
||||||
var syntaxHighlighting = await RoslynAnalysis.GetDocumentSyntaxHighlighting(_currentFile);
|
var syntaxHighlighting = await RoslynAnalysis.GetDocumentSyntaxHighlighting(_currentFile);
|
||||||
var razorSyntaxHighlighting = await RoslynAnalysis.GetRazorDocumentSyntaxHighlighting(_currentFile);
|
var razorSyntaxHighlighting = await RoslynAnalysis.GetRazorDocumentSyntaxHighlighting(_currentFile);
|
||||||
var diagnostics = await RoslynAnalysis.GetDocumentDiagnostics(_currentFile);
|
var diagnostics = await RoslynAnalysis.GetDocumentDiagnostics(_currentFile);
|
||||||
@@ -156,7 +160,7 @@ public partial class SharpIdeCodeEdit : CodeEdit
|
|||||||
{
|
{
|
||||||
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding); // get off the UI thread
|
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding); // get off the UI thread
|
||||||
_currentFile = file;
|
_currentFile = file;
|
||||||
var readFileTask = File.ReadAllTextAsync(_currentFile.Path);
|
var readFileTask = Singletons.FileManager.GetFileTextAsync(file);
|
||||||
|
|
||||||
var syntaxHighlighting = RoslynAnalysis.GetDocumentSyntaxHighlighting(_currentFile);
|
var syntaxHighlighting = RoslynAnalysis.GetDocumentSyntaxHighlighting(_currentFile);
|
||||||
var razorSyntaxHighlighting = RoslynAnalysis.GetRazorDocumentSyntaxHighlighting(_currentFile);
|
var razorSyntaxHighlighting = RoslynAnalysis.GetRazorDocumentSyntaxHighlighting(_currentFile);
|
||||||
@@ -234,6 +238,20 @@ public partial class SharpIdeCodeEdit : CodeEdit
|
|||||||
{
|
{
|
||||||
EmitSignalCodeFixesRequested();
|
EmitSignalCodeFixesRequested();
|
||||||
}
|
}
|
||||||
|
else if (@event.IsActionPressed(InputStringNames.SaveAllFiles))
|
||||||
|
{
|
||||||
|
_ = Task.GodotRun(async () =>
|
||||||
|
{
|
||||||
|
await Singletons.FileManager.SaveAllOpenFilesAsync();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (@event.IsActionPressed(InputStringNames.SaveFile))
|
||||||
|
{
|
||||||
|
_ = Task.GodotRun(async () =>
|
||||||
|
{
|
||||||
|
await Singletons.FileManager.SaveFileAsync(_currentFile);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ public partial class SolutionExplorerPanel : MarginContainer
|
|||||||
|
|
||||||
foreach (var sharpIdeFolder in project.Folders)
|
foreach (var sharpIdeFolder in project.Folders)
|
||||||
{
|
{
|
||||||
AddFoldertoTree(projectItem, sharpIdeFolder);
|
AddFolderToTree(projectItem, sharpIdeFolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var file in project.Files)
|
foreach (var file in project.Files)
|
||||||
@@ -139,7 +139,7 @@ public partial class SolutionExplorerPanel : MarginContainer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddFoldertoTree(TreeItem projectItem, SharpIdeFolder sharpIdeFolder)
|
private void AddFolderToTree(TreeItem projectItem, SharpIdeFolder sharpIdeFolder)
|
||||||
{
|
{
|
||||||
var folderItem = _tree.CreateItem(projectItem);
|
var folderItem = _tree.CreateItem(projectItem);
|
||||||
folderItem.SetText(0, sharpIdeFolder.Name);
|
folderItem.SetText(0, sharpIdeFolder.Name);
|
||||||
@@ -147,7 +147,7 @@ public partial class SolutionExplorerPanel : MarginContainer
|
|||||||
|
|
||||||
foreach (var subFolder in sharpIdeFolder.Folders)
|
foreach (var subFolder in sharpIdeFolder.Folders)
|
||||||
{
|
{
|
||||||
AddFoldertoTree(folderItem, subFolder); // recursion
|
AddFolderToTree(folderItem, subFolder); // recursion
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var file in sharpIdeFolder.Files)
|
foreach (var file in sharpIdeFolder.Files)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Microsoft.Extensions.Hosting;
|
|||||||
using SharpIDE.Application.Features.Analysis;
|
using SharpIDE.Application.Features.Analysis;
|
||||||
using SharpIDE.Application.Features.Build;
|
using SharpIDE.Application.Features.Build;
|
||||||
using SharpIDE.Application.Features.Events;
|
using SharpIDE.Application.Features.Events;
|
||||||
|
using SharpIDE.Application.Features.FilePersistence;
|
||||||
using SharpIDE.Application.Features.FileWatching;
|
using SharpIDE.Application.Features.FileWatching;
|
||||||
using SharpIDE.Application.Features.Run;
|
using SharpIDE.Application.Features.Run;
|
||||||
using SharpIDE.Application.Features.SolutionDiscovery;
|
using SharpIDE.Application.Features.SolutionDiscovery;
|
||||||
@@ -45,6 +46,7 @@ public partial class IdeRoot : Control
|
|||||||
Singletons.BuildService = new BuildService();
|
Singletons.BuildService = new BuildService();
|
||||||
Singletons.FileWatcher?.Dispose();
|
Singletons.FileWatcher?.Dispose();
|
||||||
Singletons.FileWatcher = new IdeFileWatcher();
|
Singletons.FileWatcher = new IdeFileWatcher();
|
||||||
|
Singletons.FileManager = new IdeFileManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
|
|||||||
@@ -7,4 +7,6 @@ public static class InputStringNames
|
|||||||
public static readonly StringName CodeFixes = "CodeFixes";
|
public static readonly StringName CodeFixes = "CodeFixes";
|
||||||
public static readonly StringName StepOver = "StepOver";
|
public static readonly StringName StepOver = "StepOver";
|
||||||
public static readonly StringName FindInFiles = nameof(FindInFiles);
|
public static readonly StringName FindInFiles = nameof(FindInFiles);
|
||||||
|
public static readonly StringName SaveFile = nameof(SaveFile);
|
||||||
|
public static readonly StringName SaveAllFiles = nameof(SaveAllFiles);
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using SharpIDE.Application.Features.Build;
|
using SharpIDE.Application.Features.Build;
|
||||||
|
using SharpIDE.Application.Features.FilePersistence;
|
||||||
using SharpIDE.Application.Features.FileWatching;
|
using SharpIDE.Application.Features.FileWatching;
|
||||||
using SharpIDE.Application.Features.Run;
|
using SharpIDE.Application.Features.Run;
|
||||||
using SharpIDE.Godot.Features.IdeSettings;
|
using SharpIDE.Godot.Features.IdeSettings;
|
||||||
@@ -10,5 +11,6 @@ public static class Singletons
|
|||||||
public static RunService RunService { get; set; } = null!;
|
public static RunService RunService { get; set; } = null!;
|
||||||
public static BuildService BuildService { get; set; } = null!;
|
public static BuildService BuildService { get; set; } = null!;
|
||||||
public static IdeFileWatcher FileWatcher { get; set; } = null!;
|
public static IdeFileWatcher FileWatcher { get; set; } = null!;
|
||||||
|
public static IdeFileManager FileManager { get; set; } = null!;
|
||||||
public static AppState AppState { get; set; } = null!;
|
public static AppState AppState { get; set; } = null!;
|
||||||
}
|
}
|
||||||
@@ -51,3 +51,13 @@ FindInFiles={
|
|||||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":70,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":70,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
SaveFile={
|
||||||
|
"deadzone": 0.2,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
SaveAllFiles={
|
||||||
|
"deadzone": 0.2,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user