Skip to content

Commit 92e8be4

Browse files
Load .wt.json snippets from parent directories (#18904)
## Summary of the Pull Request Adds logic such that local snippets from .wt.json files are imported not only at the current directory, but also in the parent directories. ## References and Relevant Issues Spec: https://github.com/microsoft/terminal/blob/main/doc/specs/%231595%20-%20Suggestions%20UI/Snippets.md ## Validation Steps Performed `D:\test\.wt.json`: contains `Hello --> hello (parent)` `D:\test\inner_dir\.wt.json`: contains `Hello --> hello` - In `D:\test\`... - ✅ `Hello` command outputs `hello (parent)` - ✅ commands from `inner_dir` not shown - In `D:\test\inner_dir\`... - ✅ `Hello` command outputs `hello` - ✅ commands from `D:\test` are shown ## PR Checklist Closes #17805
1 parent 4d67453 commit 92e8be4

File tree

2 files changed

+76
-22
lines changed

2 files changed

+76
-22
lines changed

src/cascadia/TerminalSettingsModel/ActionMap.cpp

Lines changed: 74 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -916,14 +916,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
916916
AddAction(*cmd, keys);
917917
}
918918

919-
// Update ActionMap's cache of actions for this directory. We'll look for a
920-
// .wt.json in this directory. If it exists, we'll read it, parse it's JSON,
921-
// then take all the sendInput actions in it and store them in our
922-
// _cwdLocalSnippetsCache
923-
std::vector<Model::Command> ActionMap::_updateLocalSnippetCache(winrt::hstring currentWorkingDirectory)
919+
// Look for a .wt.json file in the given directory. If it exists,
920+
// read it, parse it's JSON, and retrieve all the sendInput actions.
921+
std::unordered_map<hstring, Model::Command> ActionMap::_loadLocalSnippets(const std::filesystem::path& currentWorkingDirectory)
924922
{
925923
// This returns an empty string if we fail to load the file.
926-
std::filesystem::path localSnippetsPath{ std::wstring_view{ currentWorkingDirectory + L"\\.wt.json" } };
924+
std::filesystem::path localSnippetsPath = currentWorkingDirectory / std::filesystem::path{ ".wt.json" };
927925
const auto data = til::io::read_file_as_utf8_string_if_exists(localSnippetsPath);
928926
if (data.empty())
929927
{
@@ -943,12 +941,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
943941
return {};
944942
}
945943

946-
auto result = std::vector<Model::Command>();
944+
std::unordered_map<hstring, Model::Command> result;
947945
if (auto actions{ root[JsonKey("snippets")] })
948946
{
949947
for (const auto& json : actions)
950948
{
951-
result.push_back(*Command::FromSnippetJson(json));
949+
const auto snippet = Command::FromSnippetJson(json);
950+
result.insert_or_assign(snippet->Name(), *snippet);
952951
}
953952
}
954953
return result;
@@ -958,34 +957,89 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
958957
winrt::hstring currentCommandline,
959958
winrt::hstring currentWorkingDirectory)
960959
{
960+
// enumerate all the parent directories we want to import snippets from
961+
std::filesystem::path directory{ std::wstring_view{ currentWorkingDirectory } };
962+
std::vector<std::filesystem::path> directories;
963+
while (!directory.empty())
961964
{
962-
// Check if there are any cached commands in this directory.
963-
const auto& cache{ _cwdLocalSnippetsCache.lock_shared() };
965+
directories.push_back(directory);
966+
auto parentPath = directory.parent_path();
967+
if (directory == parentPath)
968+
{
969+
break;
970+
}
971+
directory = std::move(parentPath);
972+
}
964973

965-
const auto cacheIterator = cache->find(currentWorkingDirectory);
966-
if (cacheIterator != cache->end())
974+
{
975+
// Check if all the directories are already in the cache
976+
const auto& cache{ _cwdLocalSnippetsCache.lock_shared() };
977+
if (std::ranges::all_of(directories, [&](auto&& dir) { return cache->contains(dir); }))
967978
{
968-
// We found something in the cache! return it.
979+
// Load snippets from directories in reverse order.
980+
// This ensures that we prioritize snippets closer to the cwd.
981+
// The map makes it easy to avoid duplicates.
982+
std::unordered_map<hstring, Model::Command> localSnippetsMap;
983+
for (auto rit = directories.rbegin(); rit != directories.rend(); ++rit)
984+
{
985+
// register snippets from cache
986+
for (const auto& [name, snippet] : cache->at(*rit))
987+
{
988+
localSnippetsMap.insert_or_assign(name, snippet);
989+
}
990+
}
991+
992+
std::vector<Model::Command> localSnippets;
993+
localSnippets.reserve(localSnippetsMap.size());
994+
std::ranges::transform(localSnippetsMap,
995+
std::back_inserter(localSnippets),
996+
[](const auto& kvPair) { return kvPair.second; });
969997
co_return winrt::single_threaded_vector<Model::Command>(_filterToSnippets(NameMap(),
970998
currentCommandline,
971-
cacheIterator->second));
999+
localSnippets));
9721000
}
9731001
} // release the lock on the cache
9741002

9751003
// Don't do I/O on the main thread
9761004
co_await winrt::resume_background();
9771005

978-
auto result = _updateLocalSnippetCache(currentWorkingDirectory);
979-
if (!result.empty())
1006+
// Load snippets from directories in reverse order.
1007+
// This ensures that we prioritize snippets closer to the cwd.
1008+
// The map makes it easy to avoid duplicates.
1009+
const auto& cache{ _cwdLocalSnippetsCache.lock() };
1010+
std::unordered_map<hstring, Model::Command> localSnippetsMap;
1011+
for (auto rit = directories.rbegin(); rit != directories.rend(); ++rit)
9801012
{
981-
// We found something! Add it to the cache
982-
auto cache{ _cwdLocalSnippetsCache.lock() };
983-
cache->insert_or_assign(currentWorkingDirectory, result);
1013+
const auto& dir = *rit;
1014+
if (const auto cacheIterator = cache->find(dir); cacheIterator != cache->end())
1015+
{
1016+
// register snippets from cache
1017+
for (const auto& [name, snippet] : cache->at(*rit))
1018+
{
1019+
localSnippetsMap.insert_or_assign(name, snippet);
1020+
}
1021+
}
1022+
else
1023+
{
1024+
// we don't have this directory in the cache, so we need to load it
1025+
auto result = _loadLocalSnippets(dir);
1026+
cache->insert_or_assign(dir, result);
1027+
1028+
// register snippets from cache
1029+
std::ranges::for_each(result, [&localSnippetsMap](const auto& kvPair) {
1030+
localSnippetsMap.insert_or_assign(kvPair.first, kvPair.second);
1031+
});
1032+
}
9841033
}
9851034

1035+
std::vector<Model::Command> localSnippets;
1036+
localSnippets.reserve(localSnippetsMap.size());
1037+
std::ranges::transform(localSnippetsMap,
1038+
std::back_inserter(localSnippets),
1039+
[](const auto& kvPair) { return kvPair.second; });
9861040
co_return winrt::single_threaded_vector<Model::Command>(_filterToSnippets(NameMap(),
9871041
currentCommandline,
988-
result));
1042+
localSnippets));
9891043
}
9901044
#pragma endregion
9911045
}

src/cascadia/TerminalSettingsModel/ActionMap.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
103103
void _TryUpdateActionMap(const Model::Command& cmd);
104104
void _TryUpdateKeyChord(const Model::Command& cmd, const Control::KeyChord& keys);
105105

106-
std::vector<Model::Command> _updateLocalSnippetCache(winrt::hstring currentWorkingDirectory);
106+
static std::unordered_map<hstring, Model::Command> _loadLocalSnippets(const std::filesystem::path& currentWorkingDirectory);
107107

108108
Windows::Foundation::Collections::IMap<hstring, Model::ActionAndArgs> _AvailableActionsCache{ nullptr };
109109
Windows::Foundation::Collections::IMap<hstring, Model::Command> _NameMapCache{ nullptr };
@@ -137,7 +137,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
137137
// we can give the SUI a view of the key chords and the commands they map to
138138
Windows::Foundation::Collections::IMap<Control::KeyChord, Model::Command> _ResolvedKeyToActionMapCache{ nullptr };
139139

140-
til::shared_mutex<std::unordered_map<hstring, std::vector<Model::Command>>> _cwdLocalSnippetsCache{};
140+
til::shared_mutex<std::unordered_map<std::filesystem::path, std::unordered_map<hstring, Model::Command>>> _cwdLocalSnippetsCache{};
141141

142142
std::set<std::string> _changeLog;
143143

0 commit comments

Comments
 (0)