diff --git a/lldb/include/lldb/Expression/DiagnosticManager.h b/lldb/include/lldb/Expression/DiagnosticManager.h index fc49349d1b7c3..c7e02d80cf750 100644 --- a/lldb/include/lldb/Expression/DiagnosticManager.h +++ b/lldb/include/lldb/Expression/DiagnosticManager.h @@ -12,7 +12,7 @@ #include "lldb/lldb-defines.h" #include "lldb/lldb-types.h" -#include "lldb/Utility/DiagnosticsRendering.h" +#include "lldb/Host/common/DiagnosticsRendering.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/Status.h" diff --git a/lldb/include/lldb/Host/Terminal.h b/lldb/include/lldb/Host/Terminal.h index da0d05e8bd265..3d66515c18812 100644 --- a/lldb/include/lldb/Host/Terminal.h +++ b/lldb/include/lldb/Host/Terminal.h @@ -68,6 +68,18 @@ class Terminal { llvm::Error SetHardwareFlowControl(bool enabled); + /// Returns whether or not the current terminal supports Unicode rendering. + /// + /// The value is cached after the first computation. + /// + /// On POSIX systems, we check if the LANG environment variable contains the + /// substring "UTF-8", case insensitive. + /// + /// On Windows, we always return true since we use the `WriteConsoleW` API + /// internally. Note that the default Windows codepage (437) does not support + /// all Unicode characters. This function does not check the codepage. + static bool SupportsUnicode(); + protected: struct Data; diff --git a/lldb/include/lldb/Utility/DiagnosticsRendering.h b/lldb/include/lldb/Host/common/DiagnosticsRendering.h similarity index 82% rename from lldb/include/lldb/Utility/DiagnosticsRendering.h rename to lldb/include/lldb/Host/common/DiagnosticsRendering.h index dd33d671c24a5..3eea0647da37e 100644 --- a/lldb/include/lldb/Utility/DiagnosticsRendering.h +++ b/lldb/include/lldb/Host/common/DiagnosticsRendering.h @@ -59,10 +59,27 @@ struct DiagnosticDetail { StructuredData::ObjectSP Serialize(llvm::ArrayRef details); +/// Renders an array of DiagnosticDetail instances. +/// +/// \param[in] stream +/// The stream to render the diagnostics to. +/// \param offset_in_command +/// An optional offset to the column position of the diagnostic in the +/// source. +/// \param show_inline +/// Whether to show the diagnostics inline. +/// \param details +/// The array of DiagnosticsDetail to render. +/// \param force_ascii +/// Whether to force ascii rendering. If false, Unicode characters will be +/// used if the output file supports them. +/// +/// \see lldb_private::Terminal::SupportsUnicode void RenderDiagnosticDetails(Stream &stream, std::optional offset_in_command, bool show_inline, - llvm::ArrayRef details); + llvm::ArrayRef details, + bool force_ascii = false); class DiagnosticError : public llvm::ErrorInfo { diff --git a/lldb/include/lldb/Interpreter/CommandReturnObject.h b/lldb/include/lldb/Interpreter/CommandReturnObject.h index d53aeb81be2ba..0742f1b836f5e 100644 --- a/lldb/include/lldb/Interpreter/CommandReturnObject.h +++ b/lldb/include/lldb/Interpreter/CommandReturnObject.h @@ -10,7 +10,7 @@ #define LLDB_INTERPRETER_COMMANDRETURNOBJECT_H #include "lldb/Host/StreamFile.h" -#include "lldb/Utility/DiagnosticsRendering.h" +#include "lldb/Host/common/DiagnosticsRendering.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/StreamTee.h" #include "lldb/Utility/StructuredData.h" diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h index 9eda7bac4a364..498f710c5e845 100644 --- a/lldb/include/lldb/ValueObject/DILParser.h +++ b/lldb/include/lldb/ValueObject/DILParser.h @@ -9,8 +9,8 @@ #ifndef LLDB_VALUEOBJECT_DILPARSER_H #define LLDB_VALUEOBJECT_DILPARSER_H +#include "lldb/Host/common/DiagnosticsRendering.h" #include "lldb/Target/ExecutionContextScope.h" -#include "lldb/Utility/DiagnosticsRendering.h" #include "lldb/Utility/Status.h" #include "lldb/ValueObject/DILAST.h" #include "lldb/ValueObject/DILLexer.h" diff --git a/lldb/source/Commands/CommandObjectExpression.cpp b/lldb/source/Commands/CommandObjectExpression.cpp index 20727698b2501..84757b1a4ce3a 100644 --- a/lldb/source/Commands/CommandObjectExpression.cpp +++ b/lldb/source/Commands/CommandObjectExpression.cpp @@ -13,6 +13,7 @@ #include "lldb/Expression/UserExpression.h" #include "lldb/Host/OptionParser.h" #include "lldb/Host/StreamFile.h" +#include "lldb/Host/common/DiagnosticsRendering.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandOptionArgumentTable.h" #include "lldb/Interpreter/CommandReturnObject.h" @@ -21,7 +22,6 @@ #include "lldb/Target/Process.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" -#include "lldb/Utility/DiagnosticsRendering.h" #include "lldb/lldb-enumerations.h" #include "lldb/lldb-forward.h" #include "lldb/lldb-private-enumerations.h" diff --git a/lldb/source/Host/CMakeLists.txt b/lldb/source/Host/CMakeLists.txt index 3a7a9a7d2488c..5fc3edd979d64 100644 --- a/lldb/source/Host/CMakeLists.txt +++ b/lldb/source/Host/CMakeLists.txt @@ -17,6 +17,7 @@ macro(add_host_subdirectory group) endmacro() add_host_subdirectory(common + common/DiagnosticsRendering.cpp common/FileAction.cpp common/FileCache.cpp common/File.cpp diff --git a/lldb/source/Utility/DiagnosticsRendering.cpp b/lldb/source/Host/common/DiagnosticsRendering.cpp similarity index 95% rename from lldb/source/Utility/DiagnosticsRendering.cpp rename to lldb/source/Host/common/DiagnosticsRendering.cpp index 8c21e661ce764..2c9d33a6c325c 100644 --- a/lldb/source/Utility/DiagnosticsRendering.cpp +++ b/lldb/source/Host/common/DiagnosticsRendering.cpp @@ -6,7 +6,9 @@ // //===----------------------------------------------------------------------===// -#include "lldb/Utility/DiagnosticsRendering.h" +#include "lldb/Host/common/DiagnosticsRendering.h" +#include "lldb/Host/Terminal.h" + #include using namespace lldb_private; @@ -85,7 +87,8 @@ static llvm::raw_ostream &PrintSeverity(Stream &stream, void RenderDiagnosticDetails(Stream &stream, std::optional offset_in_command, bool show_inline, - llvm::ArrayRef details) { + llvm::ArrayRef details, + bool force_ascii) { if (details.empty()) return; @@ -97,12 +100,8 @@ void RenderDiagnosticDetails(Stream &stream, return; } - // Since there is no other way to find this out, use the color - // attribute as a proxy for whether the terminal supports Unicode - // characters. In the future it might make sense to move this into - // Host so it can be customized for a specific platform. llvm::StringRef cursor, underline, vbar, joint, hbar, spacer; - if (stream.AsRawOstream().colors_enabled()) { + if (Terminal::SupportsUnicode() && !force_ascii) { cursor = "˄"; underline = "˜"; vbar = "│"; diff --git a/lldb/source/Host/common/Terminal.cpp b/lldb/source/Host/common/Terminal.cpp index 436dfd8130d9b..b6d09425e956e 100644 --- a/lldb/source/Host/common/Terminal.cpp +++ b/lldb/source/Host/common/Terminal.cpp @@ -400,6 +400,23 @@ llvm::Error Terminal::SetHardwareFlowControl(bool enabled) { #endif // LLDB_ENABLE_TERMIOS } +bool Terminal::SupportsUnicode() { +#ifdef _WIN32 + return true; +#else + static std::optional g_result; + if (g_result) + return g_result.value(); + + const char *lang_var = std::getenv("LANG"); + if (!lang_var) + return false; + g_result = + llvm::StringRef(lang_var).lower().find("utf-8") != std::string::npos; + return g_result.value(); +#endif +} + TerminalState::TerminalState(Terminal term, bool save_process_group) : m_tty(term) { Save(term, save_process_group); diff --git a/lldb/source/Interpreter/CommandReturnObject.cpp b/lldb/source/Interpreter/CommandReturnObject.cpp index 0a2948e8e6ca4..ef5bfae1bd1bd 100644 --- a/lldb/source/Interpreter/CommandReturnObject.cpp +++ b/lldb/source/Interpreter/CommandReturnObject.cpp @@ -8,7 +8,7 @@ #include "lldb/Interpreter/CommandReturnObject.h" -#include "lldb/Utility/DiagnosticsRendering.h" +#include "lldb/Host/common/DiagnosticsRendering.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/StreamString.h" diff --git a/lldb/source/Interpreter/Options.cpp b/lldb/source/Interpreter/Options.cpp index 4cf68db466158..ea2213ede78a7 100644 --- a/lldb/source/Interpreter/Options.cpp +++ b/lldb/source/Interpreter/Options.cpp @@ -14,12 +14,13 @@ #include #include "lldb/Host/OptionParser.h" +#include "lldb/Host/common/DiagnosticsRendering.h" #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandObject.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Target/Target.h" -#include "lldb/Utility/DiagnosticsRendering.h" +#include "lldb/Utility/AnsiTerminal.h" #include "lldb/Utility/StreamString.h" #include "llvm/ADT/STLExtras.h" diff --git a/lldb/source/Utility/CMakeLists.txt b/lldb/source/Utility/CMakeLists.txt index 4696ed4690d37..80b53f8c098d2 100644 --- a/lldb/source/Utility/CMakeLists.txt +++ b/lldb/source/Utility/CMakeLists.txt @@ -38,7 +38,6 @@ add_lldb_library(lldbUtility NO_INTERNAL_DEPENDENCIES DataEncoder.cpp DataExtractor.cpp Diagnostics.cpp - DiagnosticsRendering.cpp Environment.cpp ErrorMessages.cpp Event.cpp diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp index eac41fab90763..99afa57218a8e 100644 --- a/lldb/source/ValueObject/DILParser.cpp +++ b/lldb/source/ValueObject/DILParser.cpp @@ -12,8 +12,8 @@ //===----------------------------------------------------------------------===// #include "lldb/ValueObject/DILParser.h" +#include "lldb/Host/common/DiagnosticsRendering.h" #include "lldb/Target/ExecutionContextScope.h" -#include "lldb/Utility/DiagnosticsRendering.h" #include "lldb/ValueObject/DILAST.h" #include "lldb/ValueObject/DILEval.h" #include "llvm/ADT/StringRef.h" diff --git a/lldb/test/Shell/Commands/command-dwim-print.test b/lldb/test/Shell/Commands/command-dwim-print.test index 9153edbd21791..8c2697d8ebf8c 100644 --- a/lldb/test/Shell/Commands/command-dwim-print.test +++ b/lldb/test/Shell/Commands/command-dwim-print.test @@ -1,16 +1,16 @@ # RUN: echo quit | %lldb -o "dwim-print a" \ # RUN: | FileCheck %s --strict-whitespace --check-prefix=CHECK1 # (lldb) dwim-print a -# CHECK1:{{^ \^}} -# CHECK1: {{^ error: use of undeclared identifier 'a'}} +# CHECK1:{{^ (\^|˄)}} +# CHECK1: {{^ (╰─ )?error: use of undeclared identifier 'a'}} # RUN: echo quit | %lldb -o "p a" \ # RUN: | FileCheck %s --strict-whitespace --check-prefix=CHECK2 # (lldb) p a -# CHECK2:{{^ \^}} +# CHECK2:{{^ (\^|˄)}} # RUN: echo quit | %lldb -o "dwim-print -- a" \ # RUN: | FileCheck %s --strict-whitespace --check-prefix=CHECK3 # (lldb) dwim-print -- a -# CHECK3:{{^ \^}} +# CHECK3:{{^ (\^|˄)}} # RUN: echo quit | %lldb -o "settings set show-inline-diagnostics false" \ # RUN: -o "dwim-print a" 2>&1 | FileCheck %s --check-prefix=CHECK4 # CHECK4: error: :1:1: use of undeclared identifier diff --git a/lldb/test/Shell/Commands/command-expr-diagnostics.test b/lldb/test/Shell/Commands/command-expr-diagnostics.test index 3c827fb4516ec..3695312ca1684 100644 --- a/lldb/test/Shell/Commands/command-expr-diagnostics.test +++ b/lldb/test/Shell/Commands/command-expr-diagnostics.test @@ -2,20 +2,20 @@ # RUN: echo quit | %lldb -o "expression a+b" \ # RUN: | FileCheck %s --strict-whitespace --check-prefix=CHECK1 # (lldb) expression a+b -# CHECK1:{{^ \^ \^}} -# CHECK1: {{^ | error: use of undeclared identifier 'b'}} -# CHECK1: {{^ error: use of undeclared identifier 'a'}} +# CHECK1:{{^ (\^|˄) (\^|˄)}} +# CHECK1: {{^ (\||│) (╰─ )?error: use of undeclared identifier 'b'}} +# CHECK1: {{^ (╰─ )?error: use of undeclared identifier 'a'}} # RUN: echo quit | %lldb -o "expr a" \ # RUN: | FileCheck %s --strict-whitespace --check-prefix=CHECK2 # (lldb) expr a -# CHECK2:{{^ \^}} +# CHECK2:{{^ (\^|˄)}} # RUN: echo quit | %lldb -o "expr -i 0 -o 0 -- a" \ # RUN: | FileCheck %s --strict-whitespace --check-prefix=CHECK3 # (lldb) expr -i 0 -o 0 -- a -# CHECK3:{{^ \^}} -# CHECK3: {{^ error: use of undeclared identifier 'a'}} +# CHECK3:{{^ (\^|˄)}} +# CHECK3: {{^ (╰─ )?error: use of undeclared identifier 'a'}} # RUN: echo "int main(){return 0;}">%t.c # RUN: %clang_host %t.c -o %t.exe @@ -23,8 +23,8 @@ # RUN: "expr --top-level -- template T FOO(T x) { return x/2;}" -o \ # RUN: "expression -- FOO(\"\")" 2>&1 | FileCheck %s --check-prefix=CHECK4 # (lldb) expression -- FOO("") -# CHECK4:{{^ \^}} -# CHECK4: {{^ note: in instantiation of function template}} +# CHECK4:{{^ (\^|˄)}} +# CHECK4: {{^ (╰─ )?note: in instantiation of function template}} # CHECK4: error: details) { StreamString stream; - RenderDiagnosticDetails(stream, 0, true, details); + RenderDiagnosticDetails(stream, 0, true, details, /*force_ascii=*/true); return stream.GetData(); } } // namespace diff --git a/lldb/unittests/Utility/CMakeLists.txt b/lldb/unittests/Utility/CMakeLists.txt index 77b52079cf32b..4cbe15bb5b073 100644 --- a/lldb/unittests/Utility/CMakeLists.txt +++ b/lldb/unittests/Utility/CMakeLists.txt @@ -10,7 +10,6 @@ add_lldb_unittest(UtilityTests DataBufferTest.cpp DataEncoderTest.cpp DataExtractorTest.cpp - DiagnosticsRenderingTest.cpp EnvironmentTest.cpp EventTest.cpp FileSpecListTest.cpp diff --git a/llvm/utils/gn/secondary/lldb/source/Host/BUILD.gn b/llvm/utils/gn/secondary/lldb/source/Host/BUILD.gn index b00442d8e1ebb..7fa38e81061b9 100644 --- a/llvm/utils/gn/secondary/lldb/source/Host/BUILD.gn +++ b/llvm/utils/gn/secondary/lldb/source/Host/BUILD.gn @@ -17,6 +17,7 @@ static_library("Host") { public_deps = [ "//llvm/utils/gn/build/libs/xml" ] sources = [ "aix/Support.cpp", + "common/DiagnosticsRendering.cpp", "common/File.cpp", "common/FileAction.cpp", "common/FileCache.cpp", diff --git a/llvm/utils/gn/secondary/lldb/source/Utility/BUILD.gn b/llvm/utils/gn/secondary/lldb/source/Utility/BUILD.gn index 4221fab1e237b..5faa365bb7bdb 100644 --- a/llvm/utils/gn/secondary/lldb/source/Utility/BUILD.gn +++ b/llvm/utils/gn/secondary/lldb/source/Utility/BUILD.gn @@ -21,7 +21,6 @@ static_library("Utility") { "DataEncoder.cpp", "DataExtractor.cpp", "Diagnostics.cpp", - "DiagnosticsRendering.cpp", "Environment.cpp", "ErrorMessages.cpp", "Event.cpp",