Multi instance, tab handling

This commit is contained in:
2023-08-29 18:03:22 +02:00
parent 85488c293d
commit 28640e5dd2
33 changed files with 1430 additions and 436 deletions

View File

@@ -0,0 +1,7 @@
using FileTime.Core.Models;
namespace FileTime.App.Core.Models;
public record TabToOpen(int? TabNumber, NativePath Path);
public record TabsToOpenOnStart(List<TabToOpen> TabsToOpen);

View File

@@ -0,0 +1,27 @@
using FileTime.Core.Models;
namespace FileTime.App.Core.UserCommand;
public class NewTabCommand : IUserCommand
{
public NewTabCommand(FullName? path)
{
Path = path;
}
public FullName? Path { get; }
public bool Open { get; init; } = true;
}
public class IdentifiableNewTabCommand : NewTabCommand, IIdentifiableUserCommand
{
public const string CommandName = "new_tab";
public static IdentifiableNewTabCommand Instance { get; } = new();
private IdentifiableNewTabCommand():base(null)
{
}
public string UserCommandID => CommandName;
public string Title => "New tab";
}

View File

@@ -0,0 +1,15 @@
namespace FileTime.App.Core.UserCommand;
public class SelectNextTabCommand : IIdentifiableUserCommand
{
public const string CommandName = "next_tab";
public static SelectNextTabCommand Instance { get; } = new();
private SelectNextTabCommand()
{
}
public string UserCommandID => CommandName;
public string Title => "Next tab";
}

View File

@@ -0,0 +1,15 @@
namespace FileTime.App.Core.UserCommand;
public class SelectPreviousTabCommand : IIdentifiableUserCommand
{
public const string CommandName = "previous_tab";
public static SelectPreviousTabCommand Instance { get; } = new();
private SelectPreviousTabCommand()
{
}
public string UserCommandID => CommandName;
public string Title => "New tab";
}

View File

@@ -47,19 +47,15 @@ public class MainConfiguration
private static List<CommandBindingConfiguration> InitDefaultKeyBindings() =>
new List<CommandBindingConfiguration>
{
//new CommandBindingConfiguration(ConfigCommand.AutoRefresh, new KeyConfig(Keys.R, shift: true)),
//new CommandBindingConfiguration(ConfigCommand.ChangeTimelineMode, new[] { Keys.T, Keys.M }),
new(CloseTabCommand.CommandName, Keys.Q),
//new CommandBindingConfiguration(ConfigCommand.Compress, new[] { Keys.Y, Keys.C }),
new(CopyBase64Command.CommandName, new[] {Keys.C, Keys.B}),
new(CopyCommand.CommandName, new[] {Keys.Y, Keys.Y}),
//new CommandBindingConfiguration(ConfigCommand.CopyHash, new[] { Keys.C, Keys.H }),
new(CopyNativePathCommand.CommandName, new[] {Keys.C, Keys.P}),
new(CopyFilesToClipboardCommand.CommandName, new[] {Keys.Y, Keys.C}),
new(CreateContainer.CommandName, Keys.F7),
new(CreateContainer.CommandName, new[] {Keys.C, Keys.C}),
new(CreateElementCommand.CommandName, new[] {Keys.C, Keys.E}),
//new CommandBindingConfiguration(ConfigCommand.Cut, new[] { Keys.D, Keys.D }),
new(EditCommand.CommandName, new[] {Keys.F4}),
new(EnterRapidTravelCommand.CommandName, new KeyConfig(Keys.Comma, shift: true)),
new(EnterRapidTravelCommand.CommandName, new KeyConfig(Keys.Question, shift: true)),
@@ -80,6 +76,7 @@ public class MainConfiguration
new(MoveCursorDownCommand.CommandName, Keys.Down),
new(MoveCursorUpPageCommand.CommandName, Keys.PageUp),
new(MoveCursorDownPageCommand.CommandName, Keys.PageDown),
new(IdentifiableNewTabCommand.CommandName, new[] {new KeyConfig(Keys.T, ctrl: true)}),
//new CommandBindingConfiguration(ConfigCommand.NextTimelineBlock, Keys.L ),
//new CommandBindingConfiguration(ConfigCommand.NextTimelineCommand, Keys.J ),
new(OpenSelectedCommand.CommandName, Keys.Right),
@@ -91,7 +88,6 @@ public class MainConfiguration
new(PasteCommand.PasteSkipCommandName, new[] {Keys.P, Keys.S}),
new(PasteFilesFromClipboardCommand.PasteMergeCommandName, new[] {new KeyConfig(Keys.V, ctrl: true)}),
new(PasteFilesFromClipboardCommand.PasteOverwriteCommandName, new[] {new KeyConfig(Keys.V, ctrl: true, shift: true)}),
//new CommandBindingConfiguration(ConfigCommand.PinFavorite, new[] { Keys.F, Keys.P }),
//new CommandBindingConfiguration(ConfigCommand.PreviousTimelineBlock, Keys.H ),
//new CommandBindingConfiguration(ConfigCommand.PreviousTimelineCommand, Keys.K ),
new(RefreshCommand.CommandName, Keys.R),
@@ -99,10 +95,10 @@ public class MainConfiguration
new(RenameCommand.CommandName, new[] {Keys.C, Keys.W}),
new(IdentifiableRunOrOpenCommand.CommandName, Keys.Enter),
//new CommandBindingConfiguration(ConfigCommand.RunCommand, new KeyConfig(Keys.D4, shift: true)),
//new CommandBindingConfiguration(ConfigCommand.ScanContainerSize, new[] { Keys.C, Keys.S }),
//new CommandBindingConfiguration(ConfigCommand.ShowAllShortcut, Keys.F1),
new(DeleteCommand.SoftDeleteCommandName, new[] {new KeyConfig(Keys.D), new KeyConfig(Keys.D, shift: true)}),
new(IdentifiableSearchCommand.SearchByNameContainsCommandName, new[] {Keys.S, Keys.N}),
new(SelectNextTabCommand.CommandName, new[] {new KeyConfig(Keys.Tab, ctrl: true)}),
new(SelectPreviousTabCommand.CommandName, new[] {new KeyConfig(Keys.Tab, ctrl: true, shift: true)}),
new(SwitchToTabCommand.SwitchToLastTabCommandName, new[] {new KeyConfig(Keys.Num9, alt: true)}),
new(SwitchToTabCommand.SwitchToTab1CommandName, new[] {new KeyConfig(Keys.Num1, alt: true)}),
new(SwitchToTabCommand.SwitchToTab2CommandName, new[] {new KeyConfig(Keys.Num2, alt: true)}),

View File

@@ -36,6 +36,7 @@ public class TabPersistenceService : ITabPersistenceService
private readonly IServiceProvider _serviceProvider;
private readonly ILocalContentProvider _localContentProvider;
private readonly TabPersistenceSettings _tabPersistenceSettings;
private readonly TabsToOpenOnStart _tabsToOpen;
public TabPersistenceService(
IApplicationSettings applicationSettings,
@@ -44,6 +45,7 @@ public class TabPersistenceService : ITabPersistenceService
IServiceProvider serviceProvider,
ILocalContentProvider localContentProvider,
TabPersistenceSettings tabPersistenceSettings,
TabsToOpenOnStart tabsToOpen,
ILogger<TabPersistenceService> logger)
{
_appState = appState;
@@ -53,6 +55,7 @@ public class TabPersistenceService : ITabPersistenceService
_serviceProvider = serviceProvider;
_localContentProvider = localContentProvider;
_tabPersistenceSettings = tabPersistenceSettings;
_tabsToOpen = tabsToOpen;
_jsonOptions = new JsonSerializerOptions
{
@@ -62,22 +65,63 @@ public class TabPersistenceService : ITabPersistenceService
}
public async Task InitAsync()
=> await LoadStatesAsync();
{
var containers = new List<(int? TabNumber, IContainer Container)>();
foreach (var (requestedTabNumber, nativePath) in _tabsToOpen.TabsToOpen)
{
if (await _timelessContentProvider.GetItemByNativePathAsync(nativePath) is not IContainer container) continue;
containers.Add((requestedTabNumber, container));
}
var loadedTabViewModels = await LoadStatesAsync(containers.Count == 0);
var tabViewModels = new List<ITabViewModel>();
foreach (var (requestedTabNumber, container) in containers)
{
var tabNumber = requestedTabNumber ?? 1;
if (tabNumber < 1) tabNumber = 1;
var tabNumbers = _appState.Tabs.Select(t => t.TabNumber).ToHashSet();
while (tabNumbers.Contains(tabNumber))
{
tabNumber++;
}
var tab = await _serviceProvider.GetAsyncInitableResolver(container)
.GetRequiredServiceAsync<ITab>();
var tabViewModel = _serviceProvider.GetInitableResolver(tab, tabNumber).GetRequiredService<ITabViewModel>();
_appState.AddTab(tabViewModel);
tabViewModels.Add(tabViewModel);
}
tabViewModels.Reverse();
await _appState.SetSelectedTabAsync(tabViewModels.Concat(loadedTabViewModels).First());
}
public Task ExitAsync(CancellationToken token = default)
{
if(!_tabPersistenceSettings.SaveState) return Task.CompletedTask;
if (!_tabPersistenceSettings.SaveState) return Task.CompletedTask;
SaveStates(token);
return Task.CompletedTask;
}
private async Task LoadStatesAsync(CancellationToken token = default)
private async Task<IEnumerable<ITabViewModel>> LoadStatesAsync(bool createEmptyIfNecessary, CancellationToken token = default)
{
if (!File.Exists(_settingsPath) || !_tabPersistenceSettings.LoadState)
{
await CreateEmptyTab();
return;
if (createEmptyIfNecessary)
{
var tabViewModel = await CreateEmptyTab();
return new[] {tabViewModel};
}
return Enumerable.Empty<ITabViewModel>();
}
try
@@ -86,7 +130,8 @@ public class TabPersistenceService : ITabPersistenceService
var state = await JsonSerializer.DeserializeAsync<PersistenceRoot>(stateReader, cancellationToken: token);
if (state != null)
{
if (await RestoreTabs(state.TabStates)) return;
var (success, tabViewModels) = await RestoreTabs(state.TabStates);
if (success) return tabViewModels;
}
}
catch (Exception e)
@@ -94,9 +139,15 @@ public class TabPersistenceService : ITabPersistenceService
_logger.LogError(e, "Unknown exception while restoring app state");
}
await CreateEmptyTab();
if (createEmptyIfNecessary)
{
var tabViewModel = await CreateEmptyTab();
return new[] {tabViewModel};
}
async Task CreateEmptyTab()
return Enumerable.Empty<ITabViewModel>();
async Task<ITabViewModel> CreateEmptyTab()
{
IContainer? currentDirectory = null;
try
@@ -116,81 +167,75 @@ public class TabPersistenceService : ITabPersistenceService
var tabViewModel = _serviceProvider.GetInitableResolver(tab, 1).GetRequiredService<ITabViewModel>();
_appState.AddTab(tabViewModel);
return tabViewModel;
}
}
private async Task<bool> RestoreTabs(TabStates? tabStates)
private async Task<(bool Success, IEnumerable<ITabViewModel>)> RestoreTabs(TabStates? tabStates)
{
if (tabStates == null
|| tabStates.Tabs == null)
{
return false;
return (false, Enumerable.Empty<ITabViewModel>());
}
try
foreach (var tab in tabStates.Tabs)
{
foreach (var tab in tabStates.Tabs)
try
{
try
if (tab.Path == null) continue;
if (_contentProvidersNotToRestore.Any(p => tab.Path.StartsWith(p))) continue;
IContainer? container = null;
var path = FullName.CreateSafe(tab.Path);
while (true)
{
if (tab.Path == null) continue;
if (_contentProvidersNotToRestore.Any(p => tab.Path.StartsWith(p))) continue;
IContainer? container = null;
var path = FullName.CreateSafe(tab.Path);
while (true)
try
{
try
{
var pathItem =
await _timelessContentProvider.GetItemByFullNameAsync(path, PointInTime.Present);
var pathItem =
await _timelessContentProvider.GetItemByFullNameAsync(path, PointInTime.Present);
container = pathItem switch
{
IContainer c => c,
IElement e =>
e.Parent is null
? null
: await e.Parent.ResolveAsync() as IContainer,
_ => null
};
break;
}
catch
container = pathItem switch
{
path = path?.GetParent();
if (path == null)
{
throw new Exception($"Could not find an initializable path along {tab.Path}");
}
IContainer c => c,
IElement e =>
e.Parent is null
? null
: await e.Parent.ResolveAsync() as IContainer,
_ => null
};
break;
}
catch
{
path = path?.GetParent();
if (path == null)
{
throw new Exception($"Could not find an initializable path along {tab.Path}");
}
}
if (container == null) continue;
if (_contentProvidersNotToRestore.Contains(container.Provider.Name)) continue;
var tabToLoad = await _serviceProvider.GetAsyncInitableResolver(container)
.GetRequiredServiceAsync<ITab>();
var tabViewModel = _serviceProvider.GetInitableResolver(tabToLoad, tab.Number)
.GetRequiredService<ITabViewModel>();
_appState.AddTab(tabViewModel);
}
catch (Exception e)
{
_logger.LogError(e, "Unknown exception while restoring tab. {TabState}",
JsonSerializer.Serialize(tab, _jsonOptions));
}
if (container == null) continue;
if (_contentProvidersNotToRestore.Contains(container.Provider.Name)) continue;
var tabToLoad = await _serviceProvider.GetAsyncInitableResolver(container)
.GetRequiredServiceAsync<ITab>();
var tabViewModel = _serviceProvider.GetInitableResolver(tabToLoad, tab.Number)
.GetRequiredService<ITabViewModel>();
_appState.AddTab(tabViewModel);
}
catch (Exception e)
{
_logger.LogError(e, "Unknown exception while restoring tab. {TabState}",
JsonSerializer.Serialize(tab, _jsonOptions));
}
}
catch (Exception e)
{
_logger.LogError(e, "Unknown exception while restoring tabs");
return false;
}
if (_appState.Tabs.Count == 0) return false;
if (_appState.Tabs.Count == 0) return (false, Enumerable.Empty<ITabViewModel>());
var optimalTabs = _appState
.Tabs
@@ -200,10 +245,7 @@ public class TabPersistenceService : ITabPersistenceService
.Tabs
.SkipWhile(t => t.TabNumber <= tabStates.ActiveTabNumber);
var tabToActivate = optimalTabs.Concat(suboptimalTabs).FirstOrDefault();
if (tabToActivate is not null) await _appState.SetSelectedTabAsync(tabToActivate);
return true;
return (true, optimalTabs.Concat(suboptimalTabs));
}
public void SaveStates(CancellationToken token = default)

View File

@@ -86,11 +86,14 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
new TypeUserCommandHandler<MoveCursorToLastCommand>(MoveCursorToLast),
new TypeUserCommandHandler<MoveCursorUpCommand>(MoveCursorUp),
new TypeUserCommandHandler<MoveCursorUpPageCommand>(MoveCursorUpPage),
new TypeUserCommandHandler<NewTabCommand>(NewTabAsync),
new TypeUserCommandHandler<OpenCommandPaletteCommand>(OpenCommandPalette),
new TypeUserCommandHandler<OpenContainerCommand>(OpenContainer),
new TypeUserCommandHandler<OpenSelectedCommand>(OpenSelected),
new TypeUserCommandHandler<RunOrOpenCommand>(RunOrOpen),
new TypeUserCommandHandler<RefreshCommand>(Refresh),
new TypeUserCommandHandler<SelectNextTabCommand>(SelectNextTab),
new TypeUserCommandHandler<SelectPreviousTabCommand>(SelectPreviousTab),
new TypeUserCommandHandler<SwitchToTabCommand>(SwitchToTab),
});
}
@@ -346,38 +349,19 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
private async Task SwitchToTab(SwitchToTabCommand command)
{
var number = command.TabNumber;
var tabViewModel = _appState.Tabs.FirstOrDefault(t => t.TabNumber == number);
var tabNumber = command.TabNumber;
var tabViewModel = _appState.Tabs.FirstOrDefault(t => t.TabNumber == tabNumber);
if (number == -1)
if (tabNumber == -1)
{
var greatestNumber = _appState.Tabs.Max(t => t.TabNumber);
tabViewModel = _appState.Tabs.FirstOrDefault(t => t.TabNumber == greatestNumber);
}
else if (tabViewModel == null)
if (tabViewModel == null)
{
IContainer? newLocation = null;
try
{
newLocation = _currentLocation?.Value?.FullName is { } fullName
? (IContainer) await _timelessContentProvider.GetItemByFullNameAsync(fullName, PointInTime.Present)
: _localContentProvider;
}
catch (Exception ex)
{
var fullName = _currentLocation?.Value?.FullName?.Path ?? "unknown";
_logger.LogError(ex, "Could not resolve container while switching to tab {TabNumber} to path {FullName}", number, fullName);
}
newLocation ??= _localContentProvider;
var tab = await _serviceProvider.GetAsyncInitableResolver(newLocation)
.GetRequiredServiceAsync<ITab>();
var newTabViewModel = _serviceProvider.GetInitableResolver(tab, number).GetRequiredService<ITabViewModel>();
_appState.AddTab(newTabViewModel);
tabViewModel = newTabViewModel;
var newLocation = await GetLocationForNewTabAsync(tabNumber);
tabViewModel = await CreateTabAsync(newLocation, tabNumber);
}
if (_viewMode == ViewMode.RapidTravel)
@@ -385,9 +369,96 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
await _userCommandHandlerService.HandleCommandAsync(ExitRapidTravelCommand.Instance);
}
await _appState.SetSelectedTabAsync(tabViewModel);
}
private async Task NewTabAsync(NewTabCommand command)
{
var numbers = _appState.Tabs.Select(t => t.TabNumber).ToHashSet();
var tabNumber = 1;
while (numbers.Contains(tabNumber))
{
tabNumber++;
}
IContainer? newLocation = null;
if (command.Path is { } path)
{
newLocation = await _timelessContentProvider.GetItemByFullNameAsync(path, PointInTime.Present) as IContainer;
}
newLocation ??= await GetLocationForNewTabAsync(tabNumber);
var tabViewModel = await CreateTabAsync(newLocation, tabNumber);
if (_viewMode == ViewMode.RapidTravel)
{
await _userCommandHandlerService.HandleCommandAsync(ExitRapidTravelCommand.Instance);
}
if (command.Open)
{
await _appState.SetSelectedTabAsync(tabViewModel);
}
}
private async Task SelectNextTab()
{
var currentTabNumber = _appState.SelectedTab.Value?.TabNumber;
var nextTabNumbers = _appState.Tabs.Select(t => t.TabNumber).Order().SkipWhile(n => n <= currentTabNumber).ToArray();
if (nextTabNumbers.Length == 0) return;
var nextTabNumber = nextTabNumbers[0];
var tabViewModel = _appState.Tabs.FirstOrDefault(t => t.TabNumber == nextTabNumber);
await _appState.SetSelectedTabAsync(tabViewModel!);
}
private async Task SelectPreviousTab()
{
var currentTabNumber = _appState.SelectedTab.Value?.TabNumber;
var nextTabNumbers = _appState.Tabs.Select(t => t.TabNumber).Order().TakeWhile(n => n < currentTabNumber).ToArray();
if (nextTabNumbers.Length == 0) return;
var nextTabNumber = nextTabNumbers[^1];
var tabViewModel = _appState.Tabs.FirstOrDefault(t => t.TabNumber == nextTabNumber);
await _appState.SetSelectedTabAsync(tabViewModel!);
}
private async Task<IContainer> GetLocationForNewTabAsync(int tabNumber)
{
try
{
var newLocation = _currentLocation?.Value?.FullName is { } fullName
? (IContainer) await _timelessContentProvider.GetItemByFullNameAsync(fullName, PointInTime.Present)
: _localContentProvider;
return newLocation;
}
catch (Exception ex)
{
var fullName = _currentLocation?.Value?.FullName?.Path ?? "unknown";
_logger.LogError(ex, "Could not resolve container while switching to tab {TabNumber} to path {FullName}", tabNumber, fullName);
}
return _localContentProvider;
}
private async Task<ITabViewModel> CreateTabAsync(IContainer newLocation, int tabNumber)
{
var tab = await _serviceProvider.GetAsyncInitableResolver(newLocation)
.GetRequiredServiceAsync<ITab>();
var newTabViewModel = _serviceProvider.GetInitableResolver(tab, tabNumber).GetRequiredService<ITabViewModel>();
_appState.AddTab(newTabViewModel);
return newTabViewModel;
}
private Task CloseTab()
{
if ((!_applicationConfiguration.AllowCloseLastTab && _appState.Tabs.Count < 2) || _selectedTab == null) return Task.CompletedTask;

View File

@@ -1,4 +1,5 @@
using FileTime.App.Core.Configuration;
using FileTime.App.Core.Models;
using FileTime.App.Core.Services;
using FileTime.App.Core.Services.UserCommandHandler;
using FileTime.App.Core.StartupServices;
@@ -36,6 +37,7 @@ public static class Startup
serviceCollection.TryAddSingleton<IPossibleCommandsService, PossibleCommandsService>();
serviceCollection.TryAddSingleton<IPossibleCommandsViewModel, PossibleCommandsViewModel>();
serviceCollection.TryAddSingleton<IProgramsService, ProgramsService>();
serviceCollection.TryAddSingleton(new TabsToOpenOnStart(new List<TabToOpen>()));
return serviceCollection
.AddCommandHandlers()

View File

@@ -13,8 +13,8 @@ public class DefaultIdentifiableCommandHandlerRegister : IStartupHandler
AddUserCommand(AddRemoteContentProviderCommand.Instance);
AddUserCommand(CloseTabCommand.Instance);
AddUserCommand(CopyCommand.Instance);
AddUserCommand(CopyBase64Command.Instance);
AddUserCommand(CopyCommand.Instance);
AddUserCommand(CopyFilesToClipboardCommand.Instance);
AddUserCommand(CopyNativePathCommand.Instance);
AddUserCommand(CreateContainer.Instance);
@@ -32,6 +32,9 @@ public class DefaultIdentifiableCommandHandlerRegister : IStartupHandler
AddUserCommand(GoToProviderCommand.Instance);
AddUserCommand(GoToRootCommand.Instance);
AddUserCommand(GoUpCommand.Instance);
AddUserCommand(IdentifiableRunOrOpenCommand.Instance);
AddUserCommand(IdentifiableSearchCommand.SearchByNameContains);
AddUserCommand(IdentifiableSearchCommand.SearchByRegex);
AddUserCommand(MarkCommand.Instance);
AddUserCommand(MoveCursorDownCommand.Instance);
AddUserCommand(MoveCursorDownPageCommand.Instance);
@@ -39,6 +42,7 @@ public class DefaultIdentifiableCommandHandlerRegister : IStartupHandler
AddUserCommand(MoveCursorToLastCommand.Instance);
AddUserCommand(MoveCursorUpCommand.Instance);
AddUserCommand(MoveCursorUpPageCommand.Instance);
AddUserCommand(IdentifiableNewTabCommand.Instance);
AddUserCommand(OpenCommandPaletteCommand.Instance);
AddUserCommand(OpenInDefaultFileExplorerCommand.Instance);
AddUserCommand(OpenSelectedCommand.Instance);
@@ -51,19 +55,18 @@ public class DefaultIdentifiableCommandHandlerRegister : IStartupHandler
AddUserCommand(PauseCommandSchedulerCommand.Instance);
AddUserCommand(RefreshCommand.Instance);
AddUserCommand(RenameCommand.Instance);
AddUserCommand(IdentifiableRunOrOpenCommand.Instance);
AddUserCommand(ScanSizeCommand.Instance);
AddUserCommand(StartCommandSchedulerCommand.Instance);
AddUserCommand(SortItemsCommand.OrderByNameCommand);
AddUserCommand(SortItemsCommand.OrderByNameDescCommand);
AddUserCommand(SelectNextTabCommand.Instance);
AddUserCommand(SelectPreviousTabCommand.Instance);
AddUserCommand(SortItemsCommand.OrderByCreatedAtCommand);
AddUserCommand(SortItemsCommand.OrderByCreatedAtDescCommand);
AddUserCommand(SortItemsCommand.OrderByLastModifiedCommand);
AddUserCommand(SortItemsCommand.OrderByLastModifiedDescCommand);
AddUserCommand(SortItemsCommand.OrderByNameCommand);
AddUserCommand(SortItemsCommand.OrderByNameDescCommand);
AddUserCommand(SortItemsCommand.OrderBySizeCommand);
AddUserCommand(SortItemsCommand.OrderBySizeDescCommand);
AddUserCommand(IdentifiableSearchCommand.SearchByNameContains);
AddUserCommand(IdentifiableSearchCommand.SearchByRegex);
AddUserCommand(StartCommandSchedulerCommand.Instance);
AddUserCommand(SwitchToTabCommand.SwitchToLastTab);
AddUserCommand(SwitchToTabCommand.SwitchToTab1);
AddUserCommand(SwitchToTabCommand.SwitchToTab2);