mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-29 06:13:20 +00:00
Build out support for multiple file formats in uvars
This is in preparation for adjusting the file format to support path variables.
This commit is contained in:
parent
ce1463bde6
commit
11d523e61a
5 changed files with 119 additions and 6 deletions
|
@ -1744,8 +1744,7 @@ int common_get_width() { return get_current_winsize().ws_col; }
|
|||
int common_get_height() { return get_current_winsize().ws_row; }
|
||||
|
||||
bool string_prefixes_string(const wchar_t *proposed_prefix, const wcstring &value) {
|
||||
size_t prefix_size = wcslen(proposed_prefix);
|
||||
return prefix_size <= value.size() && value.compare(0, prefix_size, proposed_prefix) == 0;
|
||||
return string_prefixes_string(proposed_prefix, value.c_str());
|
||||
}
|
||||
|
||||
bool string_prefixes_string(const wcstring &proposed_prefix, const wcstring &value) {
|
||||
|
@ -1763,6 +1762,17 @@ bool string_prefixes_string(const wchar_t *proposed_prefix, const wchar_t *value
|
|||
return true;
|
||||
}
|
||||
|
||||
bool string_prefixes_string(const char *proposed_prefix, const std::string &value) {
|
||||
return string_prefixes_string(proposed_prefix, value.c_str());
|
||||
}
|
||||
|
||||
bool string_prefixes_string(const char *proposed_prefix, const char *value) {
|
||||
for (size_t idx = 0; proposed_prefix[idx] != L'\0'; idx++) {
|
||||
if (proposed_prefix[idx] != value[idx]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool string_prefixes_string_case_insensitive(const wcstring &proposed_prefix,
|
||||
const wcstring &value) {
|
||||
size_t prefix_size = proposed_prefix.size();
|
||||
|
|
|
@ -342,6 +342,8 @@ std::string wcs2string(const wcstring &input);
|
|||
bool string_prefixes_string(const wcstring &proposed_prefix, const wcstring &value);
|
||||
bool string_prefixes_string(const wchar_t *proposed_prefix, const wcstring &value);
|
||||
bool string_prefixes_string(const wchar_t *proposed_prefix, const wchar_t *value);
|
||||
bool string_prefixes_string(const char *proposed_prefix, const std::string &value);
|
||||
bool string_prefixes_string(const char *proposed_prefix, const char *value);
|
||||
|
||||
/// Test if a string is a suffix of another.
|
||||
bool string_suffixes_string(const wcstring &proposed_suffix, const wcstring &value);
|
||||
|
|
|
@ -74,6 +74,9 @@
|
|||
/// Small note about not editing ~/.fishd manually. Inserted at the top of all .fishd files.
|
||||
#define SAVE_MSG "# This file contains fish universal variable definitions.\n"
|
||||
|
||||
/// Version for fish 3.0
|
||||
#define UVARS_VERSION_3_0 "3.0"
|
||||
|
||||
/// The different types of messages found in the fishd file.
|
||||
enum class uvar_message_type_t { set, set_export };
|
||||
|
||||
|
@ -764,7 +767,7 @@ var_table_t env_universal_t::read_message_internal(int fd) {
|
|||
// Process it if it's a newline (which is true if we are before the end of the buffer).
|
||||
if (cursor < bufflen && !line.empty()) {
|
||||
if (utf8_to_wchar(line.data(), line.size(), &wide_line, 0)) {
|
||||
env_universal_t::parse_message_internal(wide_line, &result, &storage);
|
||||
env_universal_t::parse_message_2x_internal(wide_line, &result, &storage);
|
||||
}
|
||||
line.clear();
|
||||
}
|
||||
|
@ -778,8 +781,70 @@ var_table_t env_universal_t::read_message_internal(int fd) {
|
|||
return result;
|
||||
}
|
||||
|
||||
/// Parse message msg/
|
||||
void env_universal_t::parse_message_internal(const wcstring &msgstr, var_table_t *vars,
|
||||
/// \return the format corresponding to file contents \p s.
|
||||
uvar_format_t env_universal_t::format_for_contents(const std::string &s) {
|
||||
// Walk over leading comments, looking for one like '# version'
|
||||
line_iterator_t<std::string> iter{s};
|
||||
while (iter.next()) {
|
||||
const std::string &line = iter.line();
|
||||
if (line.empty()) continue;
|
||||
if (line.front() != L'#') {
|
||||
// Exhausted leading comments.
|
||||
break;
|
||||
}
|
||||
// Note scanf %s is max characters to write; add 1 for null terminator.
|
||||
char versionbuf[64 + 1];
|
||||
if (sscanf(line.c_str(), "# VERSION: %64s", versionbuf) != 1) continue;
|
||||
|
||||
// Try reading the version.
|
||||
if (!strcmp(versionbuf, UVARS_VERSION_3_0)) {
|
||||
return uvar_format_t::fish_3_0;
|
||||
} else {
|
||||
// Unknown future version.
|
||||
return uvar_format_t::future;
|
||||
}
|
||||
}
|
||||
// No version found, assume 2.x
|
||||
return uvar_format_t::fish_2_x;
|
||||
}
|
||||
|
||||
void env_universal_t::populate_variables(const std::string &s, var_table_t *out_vars) {
|
||||
// Decide on the format.
|
||||
const uvar_format_t format = format_for_contents(s);
|
||||
|
||||
line_iterator_t<std::string> iter{s};
|
||||
wcstring wide_line;
|
||||
wcstring storage;
|
||||
while (iter.next()) {
|
||||
const std::string &line = iter.line();
|
||||
// Skip empties and constants.
|
||||
if (line.empty() || line.front() == L'#') continue;
|
||||
|
||||
// Convert to UTF8.
|
||||
wide_line.clear();
|
||||
if (!utf8_to_wchar(line.data(), line.size(), &wide_line, 0)) continue;
|
||||
|
||||
switch (format) {
|
||||
case uvar_format_t::fish_2_x:
|
||||
env_universal_t::parse_message_2x_internal(wide_line, out_vars, &storage);
|
||||
break;
|
||||
case uvar_format_t::fish_3_0:
|
||||
// For future formats, just try with the most recent one.
|
||||
case uvar_format_t::future:
|
||||
env_universal_t::parse_message_30_internal(wide_line, out_vars, &storage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse message msg per fish 3.0 format.
|
||||
void env_universal_t::parse_message_30_internal(const wcstring &msgstr, var_table_t *vars,
|
||||
wcstring *storage) {
|
||||
// TODO.
|
||||
}
|
||||
|
||||
/// Parse message msg per fish 2.x format.
|
||||
void env_universal_t::parse_message_2x_internal(const wcstring &msgstr, var_table_t *vars,
|
||||
wcstring *storage) {
|
||||
const wchar_t *msg = msgstr.c_str();
|
||||
|
||||
|
|
|
@ -30,6 +30,10 @@ struct callback_data_t {
|
|||
|
||||
typedef std::vector<struct callback_data_t> callback_data_list_t;
|
||||
|
||||
// List of fish universal variable formats.
|
||||
// This is exposed for testing.
|
||||
enum class uvar_format_t { fish_2_x, fish_3_0, future };
|
||||
|
||||
bool get_hostname_identifier(wcstring &result);
|
||||
/// Class representing universal variables.
|
||||
class env_universal_t {
|
||||
|
@ -68,7 +72,10 @@ class env_universal_t {
|
|||
// vars_to_acquire.
|
||||
void acquire_variables(var_table_t &vars_to_acquire);
|
||||
|
||||
static void parse_message_internal(const wcstring &msg, var_table_t *vars, wcstring *storage);
|
||||
static void parse_message_2x_internal(const wcstring &msg, var_table_t *vars,
|
||||
wcstring *storage);
|
||||
static void parse_message_30_internal(const wcstring &msg, var_table_t *vars,
|
||||
wcstring *storage);
|
||||
static var_table_t read_message_internal(int fd);
|
||||
|
||||
public:
|
||||
|
@ -95,6 +102,13 @@ class env_universal_t {
|
|||
/// Reads and writes variables at the correct path. Returns true if modified variables were
|
||||
/// written.
|
||||
bool sync(callback_data_list_t &callbacks);
|
||||
|
||||
/// Populate a variable table \p out_vars from a \p s string.
|
||||
/// This is exposed for testing only.
|
||||
static void populate_variables(const std::string &s, var_table_t *out_vars);
|
||||
|
||||
/// Guess a file format. Exposed for testing only.
|
||||
static uvar_format_t format_for_contents(const std::string &s);
|
||||
};
|
||||
|
||||
/// The "universal notifier" is an object responsible for broadcasting and receiving universal
|
||||
|
|
|
@ -2940,6 +2940,27 @@ static void test_universal_callbacks() {
|
|||
(void)system("rm -Rf test/fish_uvars_test/");
|
||||
}
|
||||
|
||||
static void test_universal_formats() {
|
||||
say(L"Testing universal format detection");
|
||||
const struct {
|
||||
const char *str;
|
||||
uvar_format_t format;
|
||||
} tests[] = {
|
||||
{"# VERSION: 3.0", uvar_format_t::fish_3_0},
|
||||
{"# version: 3.0", uvar_format_t::fish_2_x},
|
||||
{"# blah blahVERSION: 3.0", uvar_format_t::fish_2_x},
|
||||
{"stuff\n# blah blahVERSION: 3.0", uvar_format_t::fish_2_x},
|
||||
{"# blah\n# VERSION: 3.0", uvar_format_t::fish_3_0},
|
||||
{"# blah\n#VERSION: 3.0", uvar_format_t::fish_3_0},
|
||||
{"# blah\n#VERSION:3.0", uvar_format_t::fish_3_0},
|
||||
{"# blah\n#VERSION:3.1", uvar_format_t::future},
|
||||
};
|
||||
for (const auto &test : tests) {
|
||||
uvar_format_t format = env_universal_t::format_for_contents(test.str);
|
||||
do_test(format == test.format);
|
||||
}
|
||||
}
|
||||
|
||||
bool poll_notifier(const std::unique_ptr<universal_notifier_t> ¬e) {
|
||||
bool result = false;
|
||||
if (note->usec_delay_between_polls() > 0) {
|
||||
|
@ -4824,6 +4845,7 @@ int main(int argc, char **argv) {
|
|||
if (should_test_function("line_iterator")) test_line_iterator();
|
||||
if (should_test_function("universal")) test_universal();
|
||||
if (should_test_function("universal")) test_universal_callbacks();
|
||||
if (should_test_function("universal")) test_universal_formats();
|
||||
if (should_test_function("notifiers")) test_universal_notifiers();
|
||||
if (should_test_function("completion_insertions")) test_completion_insertions();
|
||||
if (should_test_function("autosuggestion_ignores")) test_autosuggestion_ignores();
|
||||
|
|
Loading…
Reference in a new issue