Public Access
Shell: Tab completes every word of a command, and * and ? stand for several files (#67)
Tab used to complete a command's first word only. It now follows the help text word by word: `lora st` gives `lora status`, `gnss track ` lists `start stop`. The words are read from the help text as written, so a new command completes with no table to keep; the Shell's own words are added in the same notation. `*` and `?` in the last part of a path, for ls, du, rm, cp and mv, from the Shell and both consoles. The command runs once for each name matched, lined up and run by the main loop as each finishes; `cancel` empties the line-up. 64 matches at most, refused whole past that. In the Shell, rm with a pattern asks once, with the count. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
This commit is contained in:
@@ -128,6 +128,43 @@ void test_rm_takes_its_switches_like_unix() {
|
||||
TEST_ASSERT_EQUAL_STRING("", parseRm("-rf").path.c_str());
|
||||
}
|
||||
|
||||
void test_a_pattern_matches_names() {
|
||||
TEST_ASSERT_TRUE(globMatch("*.txt", "notes.txt"));
|
||||
TEST_ASSERT_TRUE(globMatch("*.TXT", "notes.txt")); // the card doesn't tell cases apart
|
||||
TEST_ASSERT_FALSE(globMatch("*.txt", "notes.txt.bak"));
|
||||
TEST_ASSERT_TRUE(globMatch("*", "anything"));
|
||||
TEST_ASSERT_TRUE(globMatch("*", ""));
|
||||
TEST_ASSERT_TRUE(globMatch("a?c", "abc"));
|
||||
TEST_ASSERT_FALSE(globMatch("a?c", "ac"));
|
||||
TEST_ASSERT_FALSE(globMatch("a?c", "abbc"));
|
||||
TEST_ASSERT_TRUE(globMatch("2026-10-0?.log", "2026-10-07.log"));
|
||||
TEST_ASSERT_TRUE(globMatch("a*b*c", "a-then-b-then-c"));
|
||||
TEST_ASSERT_TRUE(globMatch("a*b*c", "abbbc"));
|
||||
TEST_ASSERT_FALSE(globMatch("a*b*c", "a-then-c"));
|
||||
TEST_ASSERT_TRUE(globMatch("*a", "banana")); // the star has to give back and try again
|
||||
TEST_ASSERT_FALSE(globMatch("*ab", "banana"));
|
||||
TEST_ASSERT_TRUE(globMatch("file-000?.txt", "file-0001.txt"));
|
||||
TEST_ASSERT_TRUE(globMatch("exact", "EXACT"));
|
||||
TEST_ASSERT_FALSE(globMatch("", "x"));
|
||||
TEST_ASSERT_TRUE(globMatch("**", "x"));
|
||||
}
|
||||
|
||||
void test_a_path_with_a_pattern_is_taken_apart() {
|
||||
std::string folder, pattern;
|
||||
TEST_ASSERT_TRUE(splitGlob("/notes/*.txt", folder, pattern));
|
||||
TEST_ASSERT_EQUAL_STRING("/notes", folder.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("*.txt", pattern.c_str());
|
||||
TEST_ASSERT_TRUE(splitGlob("/*", folder, pattern));
|
||||
TEST_ASSERT_EQUAL_STRING("/", folder.c_str());
|
||||
TEST_ASSERT_EQUAL_STRING("*", pattern.c_str());
|
||||
TEST_ASSERT_FALSE(splitGlob("/notes/a.txt", folder, pattern)); // no pattern
|
||||
TEST_ASSERT_FALSE(splitGlob("/*/a.txt", folder, pattern)); // not in a folder's name
|
||||
TEST_ASSERT_FALSE(splitGlob("/n*/a?.txt", folder, pattern));
|
||||
TEST_ASSERT_FALSE(splitGlob("*.txt", folder, pattern)); // a path starts at the top
|
||||
TEST_ASSERT_TRUE(hasGlob("a?"));
|
||||
TEST_ASSERT_FALSE(hasGlob("/plain/path"));
|
||||
}
|
||||
|
||||
int main() {
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_path_parts);
|
||||
@@ -139,5 +176,7 @@ int main() {
|
||||
RUN_TEST(test_kinds);
|
||||
RUN_TEST(test_looks_like_text);
|
||||
RUN_TEST(test_rm_takes_its_switches_like_unix);
|
||||
RUN_TEST(test_a_pattern_matches_names);
|
||||
RUN_TEST(test_a_path_with_a_pattern_is_taken_apart);
|
||||
return UNITY_END();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user