Teach CommandPalette to persist recent command lines (#11030)
Closes #11026
This commit is contained in:
parent
7423734a48
commit
7112f4e081
|
@ -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)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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; };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in a new issue