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>(); _allCommands = winrt::single_threaded_vector<winrt::TerminalApp::FilteredCommand>();
_tabActions = winrt::single_threaded_vector<winrt::TerminalApp::FilteredCommand>(); _tabActions = winrt::single_threaded_vector<winrt::TerminalApp::FilteredCommand>();
_mruTabActions = 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); _switchToMode(CommandPaletteMode::ActionMode);
@ -587,7 +586,7 @@ namespace winrt::TerminalApp::implementation
case CommandPaletteMode::TabSwitchMode: case CommandPaletteMode::TabSwitchMode:
return _tabSwitcherMode == TabSwitcherMode::MostRecentlyUsed ? _mruTabActions : _tabActions; return _tabSwitcherMode == TabSwitcherMode::MostRecentlyUsed ? _mruTabActions : _tabActions;
case CommandPaletteMode::CommandlineMode: case CommandPaletteMode::CommandlineMode:
return _commandLineHistory; return _loadRecentCommands();
default: default:
return _allCommands; return _allCommands;
} }
@ -720,14 +719,10 @@ namespace winrt::TerminalApp::implementation
// - <none> // - <none>
void CommandPalette::_dispatchCommandline(winrt::TerminalApp::FilteredCommand const& command) 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 (filteredCommand.has_value())
{ {
if (_commandLineHistory.Size() == CommandLineHistoryLength) _updateRecentCommands(filteredCommand.value().Item().Name());
{
_commandLineHistory.RemoveAtEnd();
}
_commandLineHistory.InsertAt(0, filteredCommand.value());
TraceLoggingWrite( TraceLoggingWrite(
g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider 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()) if (commandLine.empty())
{ {
return std::nullopt; return std::nullopt;
} }
winrt::hstring cl{ commandLine }; auto commandLinePaletteItem{ winrt::make<CommandLinePaletteItem>(commandLine) };
auto commandLinePaletteItem{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(cl) };
return winrt::make<FilteredCommand>(commandLinePaletteItem); return winrt::make<FilteredCommand>(commandLinePaletteItem);
} }
@ -1217,4 +1211,48 @@ namespace winrt::TerminalApp::implementation
itemContainer.DataContext(args.Item()); 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 _dispatchCommand(winrt::TerminalApp::FilteredCommand const& command);
void _dispatchCommandline(winrt::TerminalApp::FilteredCommand const& command); void _dispatchCommandline(winrt::TerminalApp::FilteredCommand const& command);
void _switchToTab(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 _dismissPalette();
void _scrollToIndex(uint32_t index); void _scrollToIndex(uint32_t index);
uint32_t _getNumVisibleItems(); uint32_t _getNumVisibleItems();
static constexpr int CommandLineHistoryLength = 10; static constexpr uint32_t CommandLineHistoryLength = 20;
Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _commandLineHistory{ nullptr }; static Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _loadRecentCommands();
static void _updateRecentCommands(const winrt::hstring& command);
::TerminalApp::AppCommandlineArgs _appArgs; ::TerminalApp::AppCommandlineArgs _appArgs;
void _choosingItemContainer(Windows::UI::Xaml::Controls::ListViewBase const& sender, Windows::UI::Xaml::Controls::ChoosingItemContainerEventArgs const& args); 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()); 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; using namespace ::Microsoft::Terminal::Settings::Model;

View file

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

View file

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