Repository :
https://github.com/FarGroup/FarManager
On branch : master
Link :
https://github.com/FarGroup/FarManager/commit/820f217d2526d97cc5ad4ec83b2cf0b1b05450be
>---------------------------------------------------------------
commit 820f217d2526d97cc5ad4ec83b2cf0b1b05450be
Author: Alex Alabuzhev <
alab...@gmail.com>
Date: Fri May 29 22:47:21 2026 +0100
Refactoring, tests
>---------------------------------------------------------------
820f217d2526d97cc5ad4ec83b2cf0b1b05450be
far/changelog | 5 +++
far/color_picker.cpp | 11 ++-----
far/exception.cpp | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++--
far/execute.cpp | 2 +-
far/platform.cpp | 74 ++++++++++++++++++++++++++++++++++++++++--
far/platform.hpp | 1 +
far/plugsettings.cpp | 2 +-
far/sqlitedb.cpp | 32 +++++++++++-------
far/vbuild.m4 | 2 +-
far/wm_listener.cpp | 1 -
10 files changed, 192 insertions(+), 29 deletions(-)
diff --git a/far/changelog b/far/changelog
index fd51a7d67..5af22f384 100644
--- a/far/changelog
+++ b/far/changelog
@@ -1,3 +1,8 @@
+--------------------------------------------------------------------------------
+drkns 2026-05-29 22:43:21+01:00 - build 6690
+
+1. Refactoring, tests.
+
--------------------------------------------------------------------------------
w17 2026-05-29 02:00:15+03:00 - build 6689
diff --git a/far/color_picker.cpp b/far/color_picker.cpp
index fb9180e03..4eb44b999 100644
--- a/far/color_picker.cpp
+++ b/far/color_picker.cpp
@@ -609,15 +609,8 @@ static bool pick_color_single(colors::single_color& Color, std::array<COLORREF,
if (!UndelineColorControlActivated)
ColorDlg[scd_item(cb::colorcode_edit)].Flags |= DIF_FOCUS;
- if constexpr ((true))
- {
- ColorDlg[scd_item(cb::color_text)].Flags |= DIF_HIDDEN | DIF_DISABLE;
- disable_if_needed(Color.Value, ColorDlg, scd::color_first);
- }
- else
- {
- ColorDlg[scd_item(cb::color_active_checkbox)].Flags |= DIF_HIDDEN | DIF_DISABLE;
- }
+ ColorDlg[scd_item(cb::color_text)].Flags |= DIF_HIDDEN | DIF_DISABLE;
+ disable_if_needed(Color.Value, ColorDlg, scd::color_first);
const auto Dlg = Dialog::create(ColorDlg, std::bind_front(&single_color_state::GetSingleColorDlgProc, &ColorState));
diff --git a/far/exception.cpp b/far/exception.cpp
index 12650abf8..cad3365d5 100644
--- a/far/exception.cpp
+++ b/far/exception.cpp
@@ -135,9 +135,9 @@ string error_state_ex::to_string() const
{
if (any())
{
- auto Str = error_state::to_string();
+ auto Str = to_string_base();
if (Errno)
- Str = concat(ErrnoStr(), L", "sv, Str);
+ Str = concat(L"errno: "sv, ErrnoStr(), L", "sv, Str);
return far::format(L"Message: {}, Error: {{{}}} ({})"sv, What, Str, source_location_to_string(Location));
}
@@ -162,3 +162,90 @@ void throw_far_exception(std::string_view const Message, source_location const&
{
throw far_exception(encoding::utf8::get_chars(Message), true, Location);
}
+
+#ifdef ENABLE_TESTS
+
+#include "testing.hpp"
+
+TEST_CASE("error_state_ex")
+{
+ os::error_state const ErrorState{ ERROR_ARENA_TRASHED, STATUS_GUARD_PAGE_VIOLATION, source_location::current() };
+
+ static const struct
+ {
+ string_view What;
+ int Errno;
+ }
+ Tests[]
+ {
+ { {}, {} },
+ { {}, ENOMEM },
+ { L"Whoopsie"sv, {} },
+ { L"Whoopsie"sv, ENOMEM },
+ };
+
+ for (const auto& i: Tests)
+ {
+ error_state_ex const ErrorStateEx{ ErrorState, i.What, i.Errno };
+
+ REQUIRE(ErrorStateEx.any());
+ REQUIRE(ErrorStateEx.What == i.What);
+ REQUIRE(ErrorStateEx.Errno == i.Errno);
+ REQUIRE(ErrorStateEx.Win32Error == ErrorState.Win32Error);
+ REQUIRE(ErrorStateEx.NtError == ErrorState.NtError);
+ REQUIRE(ErrorStateEx.Location.file_name() == ErrorState.Location.file_name());
+ REQUIRE(ErrorStateEx.Location.function_name() == ErrorState.Location.function_name());
+ REQUIRE(ErrorStateEx.Location.line() == ErrorState.Location.line());
+
+ const auto ErrnoStr = os::format_errno(i.Errno);
+ REQUIRE(ErrorStateEx.ErrnoStr() == ErrnoStr);
+
+ string const ErrorStrings[]
+ {
+ i.Errno? far::format(L"errno: {}"sv, ErrnoStr) : L""s,
+ ErrorState.any()? ErrorState.to_string_base() : L""s,
+ };
+
+ const auto FullStr = join(L", "sv, ErrorStrings | std::views::filter([](string const& Str) { return !Str.empty(); }));
+
+ REQUIRE(far::format(L"{}"sv, ErrorStateEx) == far::format(
+ L"Message: {}, Error: {{{}}} ({})"sv,
+ i.What,
+ FullStr,
+ source_location_to_string(ErrorState.Location))
+ );
+ }
+}
+
+TEST_CASE("far_exception")
+{
+ const auto Message = L"Run away! Run away!"sv;
+ const auto Errno = ENOTRECOVERABLE;
+ const auto Win32Error = ERROR_ARENA_TRASHED;
+ const auto NtError = STATUS_GUARD_PAGE_VIOLATION;
+ const auto Location = source_location::current();
+
+ errno = Errno;
+ SetLastError(Win32Error);
+ os::set_last_nt_status(NtError);
+ far_exception e(Message, true, Location);
+
+ REQUIRE(e.What == Message);
+ REQUIRE(e.Errno == Errno);
+ REQUIRE(e.Win32Error == Win32Error);
+ REQUIRE(e.NtError == NtError);
+ REQUIRE(e.Location.file_name() == Location.file_name());
+ REQUIRE(e.Location.function_name() == Location.function_name());
+ REQUIRE(e.Location.line() == Location.line());
+ REQUIRE(e.message() == Message);
+
+ REQUIRE(far::format(L"{}"sv, e) == far::format(
+ L"far_base_exception: {{Message: {}, Error: {{errno: {}, LastError: {}, NTSTATUS: {}}} ({})}}"sv,
+ Message,
+ os::format_errno(Errno),
+ os::format_error(Win32Error),
+ os::format_ntstatus(NtError),
+ source_location_to_string(Location))
+ );
+}
+#endif
diff --git a/far/execute.cpp b/far/execute.cpp
index 9ae53eeb7..9988104b1 100644
--- a/far/execute.cpp
+++ b/far/execute.cpp
@@ -505,7 +505,7 @@ static void log_process_exit_code(execute_info const& Info, os::handle const& Pr
ExitCode,
ExitCode == EXIT_FAILURE?
L""sv :
- far::format(L", {}"sv, os::error_state{ExitCode, static_cast<NTSTATUS>(ExitCode)}.to_string())
+ far::format(L", {}"sv, os::error_state{ExitCode, static_cast<NTSTATUS>(ExitCode)})
);
console.command_finished(ExitCode);
diff --git a/far/platform.cpp b/far/platform.cpp
index 3db0c4ec4..80aaff3aa 100644
--- a/far/platform.cpp
+++ b/far/platform.cpp
@@ -349,7 +349,7 @@ static string format_error_impl(unsigned const ErrorCode, bool const Nt)
format_message_error_context const Context{ Nt, ErrorCode };
const auto ErrorStr = errors_to_string_impl(LastError.Win32Error, LastError.NtError, &Context);
- LOGERROR(L"FormatMessage<{}>(0x{:08X}): {}"sv, Nt? L"NT"sv : L"Win32"sv, ErrorCode, ErrorStr);
+ LOGERROR(L"FormatMessage<{}>(0x{:08X}): Error: {{{}}} ({})"sv, Nt? L"NT"sv : L"Win32"sv, ErrorCode, ErrorStr, source_location_to_string(LastError.Location));
return {};
}
@@ -420,11 +420,16 @@ string error_state::NtErrorStr() const
return format_ntstatus(NtError);
}
-string error_state::to_string() const
+string error_state::to_string_base() const
{
return errors_to_string_impl(Win32Error, NtError);
}
+string error_state::to_string() const
+{
+ return far::format(L"Error: {{{}}} ({})"sv, to_string_base(), source_location_to_string(Location));
+}
+
error_state last_error(source_location const& Location)
{
return
@@ -1158,4 +1163,69 @@ TEST_CASE("platform.rtdl")
}
}
+TEST_CASE("postprocess_error_string")
+{
+ static const struct
+ {
+ DWORD Error;
+ string_view Str, Expected;
+ }
+ Tests[]
+ {
+ { 0, L""sv, L"0x00000000 - Unknown error"sv },
+ { 42, L"1\n2\n3 "sv, L"0x0000002A - 1 2 3"sv },
+ };
+
+ for (const auto& i: Tests)
+ {
+ REQUIRE(os::postprocess_error_string(i.Error, string(i.Str)) == i.Expected);
+ }
+}
+
+TEST_CASE("error_state")
+{
+ static const struct
+ {
+ DWORD Win32Error;
+ NTSTATUS NtError;
+ }
+ Tests[]
+ {
+ { ERROR_SUCCESS, STATUS_SUCCESS },
+ { ERROR_SUCCESS, STATUS_GUARD_PAGE_VIOLATION },
+ { ERROR_ARENA_TRASHED, STATUS_SUCCESS },
+ { ERROR_ARENA_TRASHED, STATUS_GUARD_PAGE_VIOLATION },
+ };
+
+ for (const auto& i: Tests)
+ {
+ const auto Location = source_location::current();
+ const auto ErrorState = os::error_state(i.Win32Error, i.NtError, Location);
+
+ const auto Any = i.Win32Error != ERROR_SUCCESS || i.NtError != STATUS_SUCCESS;
+
+ REQUIRE(ErrorState.any() == Any);
+ REQUIRE(ErrorState.Win32Error == i.Win32Error);
+ REQUIRE(ErrorState.NtError == i.NtError);
+ REQUIRE(ErrorState.Location.file_name() == Location.file_name());
+ REQUIRE(ErrorState.Location.function_name() == Location.function_name());
+ REQUIRE(ErrorState.Location.line() == Location.line());
+
+ const auto Win32ErrorStr = os::format_error(i.Win32Error);
+ REQUIRE(ErrorState.Win32ErrorStr() == Win32ErrorStr);
+
+ const auto NtErrorStr = os::format_ntstatus(i.NtError);
+ REQUIRE(ErrorState.NtErrorStr() == NtErrorStr);
+
+ string const ErrorStrings[]
+ {
+ i.Win32Error? far::format(L"LastError: {}"sv, Win32ErrorStr) : L""s,
+ i.NtError? far::format(L"NTSTATUS: {}"sv, NtErrorStr) : L""s,
+ };
+
+ const auto FullStr = join(L", "sv, ErrorStrings | std::views::filter([](string const& Str) { return !Str.empty(); }));
+
+ REQUIRE(far::format(L"{}"sv, ErrorState) == far::format(L"Error: {{{}}} ({})"sv, FullStr, source_location_to_string(Location)));
+ }
+}
#endif
diff --git a/far/platform.hpp b/far/platform.hpp
index 62c0181c3..263702848 100644
--- a/far/platform.hpp
+++ b/far/platform.hpp
@@ -267,6 +267,7 @@ namespace os
[[nodiscard]] string Win32ErrorStr() const;
[[nodiscard]] string NtErrorStr() const;
+ [[nodiscard]] string to_string_base() const;
[[nodiscard]] string to_string() const;
};
diff --git a/far/plugsettings.cpp b/far/plugsettings.cpp
index 691586bda..841c52553 100644
--- a/far/plugsettings.cpp
+++ b/far/plugsettings.cpp
@@ -358,7 +358,7 @@ bool PluginSettings::Delete(const FarSettingsValue& Value)
int PluginSettings::SubKey(const FarSettingsValue& Value, bool bCreate)
{
//Don't allow illegal key names - empty names or with backslashes
- if (Value.Root >= m_Keys.size() || !Value.Value || !*Value.Value || contains(Value.Value, '\\'))
+ if (Value.Root >= m_Keys.size() || !Value.Value || !*Value.Value || contains(Value.Value, L'\\'))
return 0;
const auto root = bCreate? PluginsCfg->CreateKey(m_Keys[Value.Root], Value.Value) : PluginsCfg->FindByName(m_Keys[Value.Root], Value.Value);
diff --git a/far/sqlitedb.cpp b/far/sqlitedb.cpp
index 7ccbd03e6..907503386 100644
--- a/far/sqlitedb.cpp
+++ b/far/sqlitedb.cpp
@@ -196,6 +196,11 @@ namespace
return major * 1000000 + minor * 1000 + patch;
}
+ string to_string() const
+ {
+ return far::format(L"{}.{}.{}"sv, major, minor, patch);
+ }
+
unsigned major;
unsigned minor;
unsigned patch;
@@ -207,11 +212,14 @@ namespace
ApiVersion{ SQLITE_VERSION_NUMBER },
LibVersion{ sqlite::sqlite3_libversion_number() };
- return components::info{ L"SQLite"sv, far::format(
- L"{}.{}.{} (API) / {}.{}.{} (library)"sv,
- ApiVersion.major, ApiVersion.minor, ApiVersion.patch,
- LibVersion.major, LibVersion.minor, LibVersion.patch
- )};
+ return components::info{ L"SQLite"sv,
+ far::format(L"{}{}"sv,
+ ApiVersion,
+ LibVersion == ApiVersion?
+ L""sv :
+ far::format(L" (API) / {} (library)"sv, LibVersion)
+ )
+ };
});
}
@@ -255,16 +263,16 @@ static void check_version()
throw far_known_exception(far::format(
L""
"SQLite library is too old\n\n"
- "Loaded version: {}.{}.{}\n"
- "Minimum version: {}.{}.{}\n"
- "Expected version: {}.{}.{}"sv,
- LibVersion.major, LibVersion.minor, LibVersion.patch,
- MinVersion.major, MinVersion.minor, MinVersion.patch,
- ApiVersion.major, ApiVersion.minor, ApiVersion.patch
+ "Loaded version: {}\n"
+ "Minimum version: {}\n"
+ "Expected version: {}"sv,
+ LibVersion,
+ MinVersion,
+ ApiVersion
));
}
- LOGINFO(L"SQLite {}.{}.{} loaded"sv, LibVersion.major, LibVersion.minor, LibVersion.patch);
+ LOGINFO(L"SQLite {} loaded"sv, LibVersion);
}
void SQLiteDb::library_load()
diff --git a/far/vbuild.m4 b/far/vbuild.m4
index 87349c6b6..d99a176aa 100644
--- a/far/vbuild.m4
+++ b/far/vbuild.m4
@@ -1 +1 @@
-6689
+6690
diff --git a/far/wm_listener.cpp b/far/wm_listener.cpp
index b03be1fc2..b30d60743 100644
--- a/far/wm_listener.cpp
+++ b/far/wm_listener.cpp
@@ -248,7 +248,6 @@ void wm_listener::WindowThreadRoutine(const os::event& ReadyEvent)
break;
}
- TranslateMessage(&Msg);
DispatchMessage(&Msg);
}