Skip to content

Commit

Permalink
Merge pull request #96 from retailcoder/pipeline/state
Browse files Browse the repository at this point in the history
Settings UI enhancements, LSP comms on document change (foldings, diagnostics).
  • Loading branch information
retailcoder authored Mar 27, 2024
2 parents eb372a5 + 6627c6a commit 5b3caf6
Show file tree
Hide file tree
Showing 247 changed files with 7,760 additions and 2,217 deletions.
50 changes: 35 additions & 15 deletions Client/Rubberduck.Editor/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Dragablz;
using AsyncAwaitBestPractices;
using Dragablz;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NLog;
Expand Down Expand Up @@ -98,8 +99,6 @@ protected override async void OnStartup(StartupEventArgs e)
_ = UiContextProvider.Instance(); // we MUST do this while we KNOW we're on the main thread.
try
{
ShutdownMode = ShutdownMode.OnLastWindowClose;

var args = e.Args;
_options = await ServerArgs.ParseAsync(args);
_tokenSource = new CancellationTokenSource();
Expand All @@ -119,7 +118,10 @@ protected override async void OnStartup(StartupEventArgs e)
_settings = _serviceProvider.GetRequiredService<RubberduckSettingsProvider>();

var splash = _serviceProvider.GetRequiredService<SplashService>();
splash.Show(WindowSize.Splash);
if (_settings.Settings.GeneralSettings.ShowSplash)
{
splash.Show(WindowSize.Splash);
}

splash.UpdateStatus("Loading configuration...");
_settings.ClearCache();
Expand Down Expand Up @@ -161,6 +163,10 @@ private async Task ShowEditorAsync()
var model = _serviceProvider.GetRequiredService<IShellWindowViewModel>();

var view = _shell ??= new ShellWindow() { DataContext = model };

ShutdownMode = ShutdownMode.OnMainWindowClose;
MainWindow = view;

view.Show();

ShowStartupToolwindows(settings);
Expand Down Expand Up @@ -199,14 +205,19 @@ private void ShowStartupToolwindows(EditorSettings settings)
private async Task LoadWelcomeTabAsync(IShellWindowViewModel model)
{
var fileSystem = _serviceProvider.GetRequiredService<IFileSystem>();
var path = fileSystem.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Rubberduck", "Templates", "Welcome.md");
var folder = fileSystem.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Rubberduck", "Templates");
var filename = "Welcome.md";
var path = fileSystem.Path.Combine(folder, filename);
var content = await fileSystem.File.ReadAllTextAsync(path);

var rootUri = new Uri(folder);
var fileUri = new WorkspaceFileUri(filename, rootUri);

var showSettingsCommand = _serviceProvider.GetRequiredService<ShowRubberduckSettingsCommand>();
var closeToolWindowCommand = _serviceProvider.GetRequiredService<CloseToolWindowCommand>();
var activeDocumentStatus = _serviceProvider.GetRequiredService<IDocumentStatusViewModel>();
var welcome = new MarkdownDocumentTabViewModel(new Uri(path), "Welcome", content, isReadOnly: true, showSettingsCommand, closeToolWindowCommand, activeDocumentStatus);

var documentState = new DocumentState(fileUri, content, isOpened: true);
var welcome = new MarkdownDocumentTabViewModel(documentState, isReadOnly: true, showSettingsCommand, closeToolWindowCommand, activeDocumentStatus);
var welcomeTabContent = new MarkdownEditorControl() { DataContext = welcome };
welcome.ContentControl = welcomeTabContent;
welcome.IsSelected = true;
Expand All @@ -219,11 +230,11 @@ protected override void OnExit(ExitEventArgs e)
{
var level = _settings.Settings.LoggerSettings.TraceLevel.ToTraceLevel();
var delay = _settings.Settings.LanguageClientSettings.ExitNotificationDelay;
e.ApplicationExitCode = 0;

if (TimedAction.TryRun(() =>
{
_languageClient?.ExitAsync().ConfigureAwait(false).GetAwaiter().GetResult();
base.OnExit(e);
_languageClient?.ExitAsync().SafeFireAndForget();
}, out var elapsed, out var exception))
{
_logger.LogPerformance(level, $"Notified language server to shutdown and exit (delay: {delay.TotalMilliseconds}ms).", elapsed);
Expand All @@ -232,6 +243,16 @@ protected override void OnExit(ExitEventArgs e)
{
_logger.LogError(level, exception, "Error sending shutdown/exit notifications.");
}

if (_editorServer != null)
{
if (!_editorServer.ServerState.IsCleanExit)
{
e.ApplicationExitCode = 1;
}
}

base.OnExit(e);
}

private void ConfigureLogging(ILoggingBuilder builder)
Expand All @@ -257,8 +278,7 @@ private void ConfigureLogging(ILoggingBuilder builder)

private void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<Func<ILanguageServer>>(provider => () => _editorServer.Server);
services.AddSingleton<Func<ILanguageClient?>>(provider => () => _languageClient.LanguageClient);
services.AddSingleton<Func<ILanguageClient>>(provider => () => _languageClient.LanguageClient!);
services.AddSingleton<LanguageClientApp>(provider => _languageClient);
services.AddSingleton<ILanguageClientService, LanguageClientService>();
services.AddSingleton<ILanguageServerConnectionStatusProvider, LanguageClientService>(provider => (LanguageClientService)provider.GetRequiredService<ILanguageClientService>());
Expand Down Expand Up @@ -301,7 +321,7 @@ private void ConfigureServices(IServiceCollection services)
services.AddSingleton<ShellProvider>();
services.AddSingleton<IShellWindowViewModel, ShellWindowViewModel>();
services.AddSingleton<IShellStatusBarViewModel, ShellStatusBarViewModel>();
services.AddSingleton<IWindowChromeViewModel, WindowChromeViewModel>();
services.AddTransient<IWindowChromeViewModel, WindowChromeViewModel>();

services.AddSingleton<InterTabClient>();
services.AddSingleton<InterToolTabClient>();
Expand All @@ -311,7 +331,7 @@ private void ConfigureServices(IServiceCollection services)

services.AddSingleton<MessageActionsProvider>();
services.AddSingleton<IMessageWindowFactory, MessageWindowFactory>();
services.AddSingleton<IMessageService, MessageService>();
services.AddSingleton<IMessageService, OokiiMessageService>();
services.AddSingleton<ShowMessageHandler>();
services.AddSingleton<ShowMessageRequestHandler>();

Expand All @@ -323,10 +343,10 @@ private void ConfigureServices(IServiceCollection services)
services.AddSingleton<IWindowFactory<SettingsWindow, SettingsWindowViewModel>, SettingsWindowFactory>();
services.AddSingleton<ISettingViewModelFactory, SettingViewModelFactory>();

services.AddSingleton<IWorkspaceService, WorkspaceClientService>();
services.AddSingleton<IAppWorkspacesService, WorkspaceClientService>();
services.AddSingleton<ILanguageServerTraceViewModel, LanguageServerTraceViewModel>();
services.AddSingleton<IWorkspaceExplorerViewModel, WorkspaceExplorerViewModel>();
services.AddSingleton<IWorkspaceStateManager, WorkspaceStateManager>();
services.AddSingleton<IAppWorkspacesStateManager, WorkspaceStateManager>();
services.AddSingleton<DocumentContentStore>();
services.AddSingleton<OpenDocumentCommand>();

Expand Down
4 changes: 2 additions & 2 deletions Client/Rubberduck.Editor/Commands/CloseAllDocumentsCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ namespace Rubberduck.Editor.Commands
{
public class CloseAllDocumentsCommand : CommandBase
{
private readonly IWorkspaceService _workspace;
private readonly IAppWorkspacesService _workspace;

public CloseAllDocumentsCommand(UIServiceHelper service,
IWorkspaceService workspace)
IAppWorkspacesService workspace)
: base(service)
{
_workspace = workspace;
Expand Down
22 changes: 15 additions & 7 deletions Client/Rubberduck.Editor/Commands/CloseDocumentCommand.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Rubberduck.InternalApi.Extensions;
using OmniSharp.Extensions.LanguageServer.Protocol.Client;
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
using Rubberduck.InternalApi.Extensions;
using Rubberduck.InternalApi.Services;
using Rubberduck.UI.Command.Abstract;
using Rubberduck.UI.Services;
Expand All @@ -9,24 +11,30 @@ namespace Rubberduck.Editor.Commands
{
public class CloseDocumentCommand : CommandBase
{
private readonly IWorkspaceService _workspace;
private readonly IAppWorkspacesService _workspace;
private readonly Func<ILanguageClient> _lsp;


public CloseDocumentCommand(UIServiceHelper service,
IWorkspaceService workspace)
IAppWorkspacesService workspace,
Func<ILanguageClient> lsp)
: base(service)
{
_workspace = workspace;
_lsp = lsp;
}

protected async override Task OnExecuteAsync(object? parameter)
{
if (parameter is WorkspaceFileUri uri)
{
_workspace.CloseFile(uri);
return;
if (_workspace.Workspaces.ActiveWorkspace?.TryGetWorkspaceFile(uri, out var document) ?? false)
{
var server = _lsp();
_workspace.CloseFile(uri);
server?.TextDocument.DidCloseTextDocument(new() { TextDocument = new() { Uri = uri.AbsoluteLocation.AbsoluteUri } });
}
}

// TODO once there's a document state manager, grab the ActiveDocument here
}
}
}
4 changes: 2 additions & 2 deletions Client/Rubberduck.Editor/Commands/CloseWorkspaceCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ namespace Rubberduck.Editor.Commands
{
public class CloseWorkspaceCommand : CommandBase
{
private readonly IWorkspaceService _workspace;
private readonly IAppWorkspacesService _workspace;

public CloseWorkspaceCommand(UIServiceHelper service,
IWorkspaceService workspace)
IAppWorkspacesService workspace)
: base(service)
{
_workspace = workspace;
Expand Down
4 changes: 2 additions & 2 deletions Client/Rubberduck.Editor/Commands/ExitCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ namespace Rubberduck.Editor.Commands
{
public class ExitCommand : CommandBase
{
private readonly IWorkspaceService _workspace;
private readonly IAppWorkspacesService _workspace;

public ExitCommand(UIServiceHelper service, IWorkspaceService workspace)
public ExitCommand(UIServiceHelper service, IAppWorkspacesService workspace)
: base(service)
{
_workspace = workspace;
Expand Down
99 changes: 63 additions & 36 deletions Client/Rubberduck.Editor/Commands/OpenDocumentCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using Rubberduck.Editor.Shell.Document;
using Rubberduck.InternalApi.Extensions;
using Rubberduck.InternalApi.Model.Workspace;
using Rubberduck.InternalApi.ServerPlatform.LanguageServer;
using Rubberduck.InternalApi.Services;
using Rubberduck.UI.Command;
Expand All @@ -13,28 +12,31 @@
using Rubberduck.UI.Shell.Document;
using Rubberduck.UI.Shell.StatusBar;
using System;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Controls;

namespace Rubberduck.Editor.Commands
{
public class OpenDocumentCommand : CommandBase
{
private readonly IWorkspaceStateManager _workspaces;
private readonly IAppWorkspacesStateManager _workspaces;
private readonly ShellProvider _shell;

private readonly ShowRubberduckSettingsCommand _showSettingsCommand;
private readonly CloseToolWindowCommand _closeToolWindowCommand;
private readonly IDocumentStatusViewModel _activeDocumentStatus;

private readonly Func<ILanguageClient?> _lsp;
private readonly Func<ILanguageClient> _lsp;
private readonly Lazy<ILanguageClient> _languageClient;
private ILanguageClient LanguageClient => _languageClient.Value;

public OpenDocumentCommand(UIServiceHelper service,
IWorkspaceStateManager workspaces,
IAppWorkspacesStateManager workspaces,
ShellProvider shell,
ShowRubberduckSettingsCommand showSettingsCommand,
CloseToolWindowCommand closeToolWindow,
IDocumentStatusViewModel activeDocumentStatus, Func<ILanguageClient?> lsp)
IDocumentStatusViewModel activeDocumentStatus, Func<ILanguageClient> lsp)
: base(service)
{
_workspaces = workspaces;
Expand All @@ -45,54 +47,83 @@ public OpenDocumentCommand(UIServiceHelper service,
AddToCanExecuteEvaluation(e => true);

_lsp = lsp;
_languageClient = new Lazy<ILanguageClient>(() => lsp.Invoke(), isThreadSafe: true);
}

protected async override Task OnExecuteAsync(object? parameter)
{
var workspace = _workspaces.ActiveWorkspace;
if (workspace != null && parameter is WorkspaceFileUri uri)
if (LanguageClient != null && workspace != null && parameter is WorkspaceFileUri uri)
{
var rootUri = workspace.WorkspaceRoot;
//var root = _workspaces.ActiveWorkspace?.WorkspaceRoot?.LocalPath ?? throw new InvalidOperationException();
//var srcRoot = System.IO.Path.Combine(root, ProjectFile.SourceRoot);
//var relativeUri = uri.OriginalString[1..][srcRoot.Length..];

if (rootUri != null && workspace.TryGetWorkspaceFile(uri, out var file) && file != null && !file.IsMissing && !file.IsLoadError)
if (workspace.WorkspaceRoot != null && workspace.TryGetWorkspaceFile(uri, out var file) && file != null)
{
//&& !file.IsMissing && !file.IsLoadError

UserControl view;
IDocumentTabViewModel document;

if (file is SourceFileDocumentState)
if (file is DocumentState state)
{
document = new VBACodeDocumentTabViewModel(uri, file.Name, file.Text, isReadOnly: false, _showSettingsCommand, _closeToolWindowCommand, _activeDocumentStatus);
view = new SourceCodeEditorControl() { DataContext = document };
if (state is CodeDocumentState codeDocumentState)
{
if (codeDocumentState.Language.Id == SupportedLanguage.VBA.Id)
{
document = new VBACodeDocumentTabViewModel(codeDocumentState, isReadOnly: false, _showSettingsCommand, _closeToolWindowCommand, _activeDocumentStatus, _lsp, Service);
view = new SourceCodeEditorControl() { DataContext = document };
}
else if (codeDocumentState.Language.Id == SupportedLanguage.VB6.Id)
{
document = new VB6CodeDocumentTabViewModel(codeDocumentState, isReadOnly: false, _showSettingsCommand, _closeToolWindowCommand, _activeDocumentStatus, _lsp, Service);
view = new SourceCodeEditorControl() { DataContext = document };
}
else
{
throw new NotSupportedException();
}
}
else
{
switch (file.FileExtension)
{
case "md":
document = new MarkdownDocumentTabViewModel(state, isReadOnly: false, _showSettingsCommand, _closeToolWindowCommand, _activeDocumentStatus);
view = new MarkdownEditorControl() { DataContext = document };
break;
case "rdproj":
document = new RubberduckProjectDocumentTabViewModel(state, isReadOnly: false, _showSettingsCommand, _closeToolWindowCommand, _activeDocumentStatus);
view = new SourceCodeEditorControl() { DataContext = document }; // TODO understand json as a different "language" / make a json language server
break;
default:
document = new TextDocumentTabViewModel(state, isReadOnly: false, _showSettingsCommand, _closeToolWindowCommand, _activeDocumentStatus);
view = new TextEditorControl() { DataContext = document };
break;
}
}

workspace.WorkspaceFileStateChanged += (sender, args) =>
{
if (args.Uri == state.Uri && workspace.TryGetWorkspaceFile(args.Uri, out var updated) && updated != null)
{
document.DocumentState = updated;
}
};
}
else
{
switch (file.FileExtension)
{
case "md":
document = new MarkdownDocumentTabViewModel(uri, file.Name, file.Text, isReadOnly: false, _showSettingsCommand, _closeToolWindowCommand, _activeDocumentStatus);
view = new MarkdownEditorControl() { DataContext = document };
break;
case "rdproj":
document = new RubberduckProjectDocumentTabViewModel(uri, workspace.ProjectName, file.Text, isReadOnly: false, _showSettingsCommand, _closeToolWindowCommand, _activeDocumentStatus);
view = new SourceCodeEditorControl() { DataContext = document }; // TODO understand json as a different "language"
break;
default:
document = new TextDocumentTabViewModel(uri, file.Name, file.Text, isReadOnly: false, _showSettingsCommand, _closeToolWindowCommand, _activeDocumentStatus);
view = new TextEditorControl() { DataContext = document };
break;
}
throw new InvalidOperationException("document state was unexpectedly null.");
}

file = file.WithOpened(true);
file = file with { IsOpened = true };
NotifyLanguageServer(file);

document.ContentControl = view;
_shell.ViewModel.DocumentWindows.Add(document);
_shell.ViewModel.ActiveDocumentTab = document;
}
else
{
throw new FileNotFoundException($"File '{uri}' is present in the workspace folder, but not included in this workspace. Include it in the project?");
}
}

await Task.CompletedTask;
Expand All @@ -109,15 +140,11 @@ private void NotifyLanguageServer(DocumentState file)

var absoluteUri = file.Uri.AbsoluteLocation;

var languageId = file is SourceFileDocumentState
? SupportedLanguage.VBA.Id
: "none";

var textDocumentItem = new TextDocumentItem
{
Uri = absoluteUri,
Version = 1,
LanguageId = languageId,
LanguageId = SupportedLanguage.VBA.Id,
Text = file.Text,
};
lsp.TextDocument.DidOpenTextDocument(new() { TextDocument = textDocumentItem });
Expand Down
Loading

0 comments on commit 5b3caf6

Please sign in to comment.