Teach CommandPalette to persist recent command lines (#11030)

Closes #11026
This commit is contained in:
Don-Vito 2021-08-26 22:04:35 +03:00 committed by GitHub
parent 7423734a48
commit 7112f4e081
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 102 additions and 18 deletions

View File

@ -36,7 +36,6 @@ namespace winrt::TerminalApp::implementation
_allCommands = winrt::single_threaded_vector<winrt::TerminalApp::FilteredCommand>();
_tabActions = winrt::single_threaded_vector<winrt::TerminalApp::FilteredCommand>();
_mruTabActions = winrt::single_threaded_vector<winrt::TerminalApp::FilteredCommand>();
_commandLineHistory = winrt::single_threaded_vector<winrt::TerminalApp::FilteredCommand>();
_switchToMode(CommandPaletteMode::ActionMode);
@ -587,7 +586,7 @@ namespace winrt::TerminalApp::implementation
case CommandPaletteMode::TabSwitchMode:
return _tabSwitcherMode == TabSwitcherMode::MostRecentlyUsed ? _mruTabActions : _tabActions;
case CommandPaletteMode::CommandlineMode:
return _commandLineHistory;
return _loadRecentCommands();
default:
return _allCommands;
}
@ -720,14 +719,10 @@ namespace winrt::TerminalApp::implementation
// - <none>
void CommandPalette::_dispatchCommandline(winrt::TerminalApp::FilteredCommand const& command)
{
const auto filteredCommand = command ? command : _buildCommandLineCommand(_getTrimmedInput());
const auto filteredCommand = command ? command : _buildCommandLineCommand(winrt::hstring(_getTrimmedInput()));
if (filteredCommand.has_value())
{
if (_commandLineHistory.Size() == CommandLineHistoryLength)
{
_commandLineHistory.RemoveAtEnd();
}
_commandLineHistory.InsertAt(0, filteredCommand.value());
_updateRecentCommands(filteredCommand.value().Item().Name());
TraceLoggingWrite(
g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
@ -744,15 +739,14 @@ namespace winrt::TerminalApp::implementation
}
}
std::optional<winrt::TerminalApp::FilteredCommand> CommandPalette::_buildCommandLineCommand(std::wstring const& commandLine)
std::optional<TerminalApp::FilteredCommand> CommandPalette::_buildCommandLineCommand(const hstring& commandLine)
{
if (commandLine.empty())
{
return std::nullopt;
}
winrt::hstring cl{ commandLine };
auto commandLinePaletteItem{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(cl) };
auto commandLinePaletteItem{ winrt::make<CommandLinePaletteItem>(commandLine) };
return winrt::make<FilteredCommand>(commandLinePaletteItem);
}
@ -1217,4 +1211,48 @@ namespace winrt::TerminalApp::implementation
itemContainer.DataContext(args.Item());
}
}
// Method Description:
// - Reads the list of recent commands from the persistent application state
// Return Value:
// - The list of FilteredCommand representing the ones stored in the state
IVector<TerminalApp::FilteredCommand> CommandPalette::_loadRecentCommands()
{
const auto recentCommands = ApplicationState::SharedInstance().RecentCommands();
std::vector<TerminalApp::FilteredCommand> parsedCommands;
parsedCommands.reserve(std::min(recentCommands.Size(), CommandLineHistoryLength));
for (const auto& c : recentCommands)
{
if (parsedCommands.size() >= CommandLineHistoryLength)
{
// Don't load more than CommandLineHistoryLength commands
break;
}
if (const auto parsedCommand = _buildCommandLineCommand(c))
{
parsedCommands.push_back(*parsedCommand);
}
}
return single_threaded_vector(std::move(parsedCommands));
}
// Method Description:
// - Update recent commands by putting the provided command as most recent.
// Upon race condition might override an update made by another window.
// Return Value:
// - <none>
void CommandPalette::_updateRecentCommands(const hstring& command)
{
const auto recentCommands = ApplicationState::SharedInstance().RecentCommands();
const auto countToCopy = std::min(recentCommands.Size(), CommandLineHistoryLength - 1);
std::vector<hstring> newRecentCommands{ countToCopy + 1 };
til::at(newRecentCommands, 0) = command;
if (countToCopy)
{
recentCommands.GetMany(0, { newRecentCommands.data() + 1, countToCopy });
}
ApplicationState::SharedInstance().RecentCommands(single_threaded_vector(std::move(newRecentCommands)));
}
}

View File

@ -123,15 +123,16 @@ namespace winrt::TerminalApp::implementation
void _dispatchCommand(winrt::TerminalApp::FilteredCommand const& command);
void _dispatchCommandline(winrt::TerminalApp::FilteredCommand const& command);
void _switchToTab(winrt::TerminalApp::FilteredCommand const& command);
std::optional<winrt::TerminalApp::FilteredCommand> _buildCommandLineCommand(std::wstring const& commandLine);
static std::optional<winrt::TerminalApp::FilteredCommand> _buildCommandLineCommand(const winrt::hstring& commandLine);
void _dismissPalette();
void _scrollToIndex(uint32_t index);
uint32_t _getNumVisibleItems();
static constexpr int CommandLineHistoryLength = 10;
Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _commandLineHistory{ nullptr };
static constexpr uint32_t CommandLineHistoryLength = 20;
static Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _loadRecentCommands();
static void _updateRecentCommands(const winrt::hstring& command);
::TerminalApp::AppCommandlineArgs _appArgs;
void _choosingItemContainer(Windows::UI::Xaml::Controls::ListViewBase const& sender, Windows::UI::Xaml::Controls::ChoosingItemContainerEventArgs const& args);

View File

@ -55,6 +55,48 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
return fmt::format("{}[]", ConversionTrait<GUID>{}.TypeDescription());
}
};
template<typename T>
struct ConversionTrait<winrt::Windows::Foundation::Collections::IVector<T>>
{
winrt::Windows::Foundation::Collections::IVector<T> FromJson(const Json::Value& json) const
{
ConversionTrait<T> trait;
std::vector<T> val;
val.reserve(json.size());
for (const auto& element : json)
{
val.push_back(trait.FromJson(element));
}
return winrt::single_threaded_vector(move(val));
}
bool CanConvert(const Json::Value& json) const
{
ConversionTrait<T> trait;
return json.isArray() && std::all_of(json.begin(), json.end(), [trait](const auto& json) -> bool { return trait.CanConvert(json); });
}
Json::Value ToJson(const winrt::Windows::Foundation::Collections::IVector<T>& val)
{
ConversionTrait<T> trait;
Json::Value json{ Json::arrayValue };
for (const auto& key : val)
{
json.append(trait.ToJson(key));
}
return json;
}
std::string TypeDescription() const
{
return fmt::format("vector ({})", ConversionTrait<T>{}.TypeDescription());
}
};
}
using namespace ::Microsoft::Terminal::Settings::Model;

View File

@ -21,8 +21,9 @@ Abstract:
// This macro generates all getters and setters for ApplicationState.
// It provides X with the following arguments:
// (type, function name, JSON key, ...variadic construction arguments)
#define MTSM_APPLICATION_STATE_FIELDS(X) \
X(std::unordered_set<winrt::guid>, GeneratedProfiles, "generatedProfiles")
#define MTSM_APPLICATION_STATE_FIELDS(X) \
X(std::unordered_set<winrt::guid>, GeneratedProfiles, "generatedProfiles") \
X(Windows::Foundation::Collections::IVector<hstring>, RecentCommands, "recentCommands")
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{

View File

@ -9,5 +9,7 @@ namespace Microsoft.Terminal.Settings.Model
void Reload();
String FilePath { get; };
Windows.Foundation.Collections.IVector<String> RecentCommands { get; set; };
}
}

View File

@ -161,7 +161,7 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
return til::u8u16(Detail::GetStringView(json));
}
bool CanConvert(const Json::Value& json)
bool CanConvert(const Json::Value& json) const
{
return json.isString();
}
@ -252,7 +252,7 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
return til::u16u8(val);
}
bool CanConvert(const Json::Value& json)
bool CanConvert(const Json::Value& json) const
{
// hstring has a specific behavior for null, so it can convert it
return ConversionTrait<std::wstring>::CanConvert(json) || json.isNull();