[LLDB] Added syntax highlighting support
Summary:
This patch adds syntax highlighting support to LLDB. When enabled (and lldb is allowed
to use colors), printed source code is annotated with the ANSI color escape sequences.
So far we have only one highlighter which is based on Clang and is responsible for all
languages that are supported by Clang. It essentially just runs the raw lexer over the input
and then surrounds the specific tokens with the configured escape sequences.
Reviewers: zturner, davide
Reviewed By: davide
Subscribers: labath, teemperor, llvm-commits, mgorny, lldb-commits
Tags: #lldb
Differential Revision: https://reviews.llvm.org/D49334
llvm-svn: 338662
diff --git a/lldb/source/Core/CMakeLists.txt b/lldb/source/Core/CMakeLists.txt
index 927a0dc..bae5b49 100644
--- a/lldb/source/Core/CMakeLists.txt
+++ b/lldb/source/Core/CMakeLists.txt
@@ -25,6 +25,7 @@
FileLineResolver.cpp
FileSpecList.cpp
FormatEntity.cpp
+ Highlighter.cpp
IOHandler.cpp
Listener.cpp
Mangled.cpp
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index 972d0bc..b29f356 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -233,6 +233,8 @@
nullptr,
"The number of sources lines to display that come before the "
"current source line when displaying a stopped context."},
+ {"highlight-source", OptionValue::eTypeBoolean, true, true, nullptr,
+ nullptr, "If true, LLDB will highlight the displayed source code."},
{"stop-show-column", OptionValue::eTypeEnum, false,
eStopShowColumnAnsiOrCaret, nullptr, s_stop_show_column_values,
"If true, LLDB will use the column information from the debug info to "
@@ -296,6 +298,7 @@
ePropertyStopDisassemblyDisplay,
ePropertyStopLineCountAfter,
ePropertyStopLineCountBefore,
+ ePropertyHighlightSource,
ePropertyStopShowColumn,
ePropertyStopShowColumnAnsiPrefix,
ePropertyStopShowColumnAnsiSuffix,
@@ -470,6 +473,12 @@
return ret;
}
+bool Debugger::GetHighlightSource() const {
+ const uint32_t idx = ePropertyHighlightSource;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(
+ nullptr, idx, g_properties[idx].default_uint_value);
+}
+
StopShowColumn Debugger::GetStopShowColumn() const {
const uint32_t idx = ePropertyStopShowColumn;
return (lldb::StopShowColumn)m_collection_sp->GetPropertyAtIndexAsEnumeration(
diff --git a/lldb/source/Core/Highlighter.cpp b/lldb/source/Core/Highlighter.cpp
new file mode 100644
index 0000000..2ef99c8
--- /dev/null
+++ b/lldb/source/Core/Highlighter.cpp
@@ -0,0 +1,68 @@
+//===-- Highlighter.cpp -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Highlighter.h"
+
+#include "lldb/Target/Language.h"
+#include "lldb/Utility/AnsiTerminal.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb_private;
+
+std::size_t HighlightStyle::ColorStyle::Apply(Stream &s,
+ llvm::StringRef value) const {
+ s << m_prefix << value << m_suffix;
+ // Calculate how many bytes we have written.
+ return m_prefix.size() + value.size() + m_suffix.size();
+}
+
+void HighlightStyle::ColorStyle::Set(llvm::StringRef prefix,
+ llvm::StringRef suffix) {
+ m_prefix = lldb_utility::ansi::FormatAnsiTerminalCodes(prefix);
+ m_suffix = lldb_utility::ansi::FormatAnsiTerminalCodes(suffix);
+}
+
+std::size_t NoHighlighter::Highlight(const HighlightStyle &options,
+ llvm::StringRef line,
+ llvm::StringRef previous_lines,
+ Stream &s) const {
+ // We just forward the input to the output and do no highlighting.
+ s << line;
+ return line.size();
+}
+
+static HighlightStyle::ColorStyle GetColor(const char *c) {
+ return HighlightStyle::ColorStyle(c, "${ansi.normal}");
+}
+
+HighlightStyle HighlightStyle::MakeVimStyle() {
+ HighlightStyle result;
+ result.comment = GetColor("${ansi.fg.purple}");
+ result.scalar_literal = GetColor("${ansi.fg.red}");
+ result.keyword = GetColor("${ansi.fg.green}");
+ return result;
+}
+
+const Highlighter &
+HighlighterManager::getHighlighterFor(lldb::LanguageType language_type,
+ llvm::StringRef path) const {
+ Language *language = lldb_private::Language::FindPlugin(language_type, path);
+ if (language && language->GetHighlighter())
+ return *language->GetHighlighter();
+ return m_no_highlighter;
+}
+
+std::string Highlighter::Highlight(const HighlightStyle &options,
+ llvm::StringRef line,
+ llvm::StringRef previous_lines) const {
+ StreamString s;
+ Highlight(options, line, previous_lines, s);
+ s.Flush();
+ return s.GetString().str();
+}
diff --git a/lldb/source/Core/SourceManager.cpp b/lldb/source/Core/SourceManager.cpp
index a6afd64..0ae1d9b 100644
--- a/lldb/source/Core/SourceManager.cpp
+++ b/lldb/source/Core/SourceManager.cpp
@@ -13,6 +13,7 @@
#include "lldb/Core/AddressRange.h" // for AddressRange
#include "lldb/Core/Debugger.h"
#include "lldb/Core/FormatEntity.h" // for FormatEntity
+#include "lldb/Core/Highlighter.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/ModuleList.h" // for ModuleList
#include "lldb/Host/FileSystem.h"
@@ -103,6 +104,18 @@
return file_sp;
}
+static bool should_highlight_source(DebuggerSP debugger_sp) {
+ if (!debugger_sp)
+ return false;
+
+ // We don't use ANSI stop column formatting if the debugger doesn't think it
+ // should be using color.
+ if (!debugger_sp->GetUseColor())
+ return false;
+
+ return debugger_sp->GetHighlightSource();
+}
+
static bool should_show_stop_column_with_ansi(DebuggerSP debugger_sp) {
// We don't use ANSI stop column formatting if we can't lookup values from
// the debugger.
@@ -114,6 +127,11 @@
if (!debugger_sp->GetUseColor())
return false;
+ // Don't use terminal attributes when we have highlighting enabled. This
+ // can mess up the command line.
+ if (debugger_sp->GetHighlightSource())
+ return false;
+
// We only use ANSI stop column formatting if we're either supposed to show
// ANSI where available (which we know we have when we get to this point), or
// if we're only supposed to use ANSI.
@@ -515,6 +533,16 @@
if (!m_data_sp)
return 0;
+ std::string previous_content;
+
+ HighlightStyle style = HighlightStyle::MakeVimStyle();
+ HighlighterManager mgr;
+ std::string path = GetFileSpec().GetPath(/*denormalize*/ false);
+ // FIXME: Find a way to get the definitive language this file was written in
+ // and pass it to the highlighter.
+ auto &highlighter =
+ mgr.getHighlighterFor(lldb::LanguageType::eLanguageTypeUnknown, path);
+
const uint32_t start_line =
line <= context_before ? 1 : line - context_before;
const uint32_t start_line_offset = GetLineOffset(start_line);
@@ -530,10 +558,19 @@
size_t count = end_line_offset - start_line_offset;
const uint8_t *cstr = m_data_sp->GetBytes() + start_line_offset;
+ auto ref = llvm::StringRef(reinterpret_cast<const char *>(cstr), count);
bool displayed_line = false;
- if (column && (column < count)) {
- auto debugger_sp = m_debugger_wp.lock();
+ auto debugger_sp = m_debugger_wp.lock();
+ if (should_highlight_source(debugger_sp)) {
+ bytes_written +=
+ highlighter.Highlight(style, ref, previous_content, *s);
+ displayed_line = true;
+ // Add the new line to the previous lines.
+ previous_content += ref.str();
+ }
+
+ if (!displayed_line && column && (column < count)) {
if (should_show_stop_column_with_ansi(debugger_sp) && debugger_sp) {
// Check if we have any ANSI codes with which to mark this column. If
// not, no need to do this work.
@@ -581,10 +618,10 @@
// If we didn't end up displaying the line with ANSI codes for whatever
// reason, display it now sans codes.
if (!displayed_line)
- bytes_written = s->Write(cstr, count);
+ bytes_written = s->PutCString(ref);
// Ensure we get an end of line character one way or another.
- if (!is_newline_char(cstr[count - 1]))
+ if (!is_newline_char(ref.back()))
bytes_written += s->EOL();
}
return bytes_written;
diff --git a/lldb/source/Plugins/Language/CMakeLists.txt b/lldb/source/Plugins/Language/CMakeLists.txt
index 4b92a8e..723cf13 100644
--- a/lldb/source/Plugins/Language/CMakeLists.txt
+++ b/lldb/source/Plugins/Language/CMakeLists.txt
@@ -1,3 +1,4 @@
+add_subdirectory(ClangCommon)
add_subdirectory(CPlusPlus)
add_subdirectory(Go)
add_subdirectory(Java)
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
index 180440a..e7faee8 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
+++ b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
@@ -24,6 +24,8 @@
lldbSymbol
lldbTarget
lldbUtility
+ lldbPluginClangCommon
+
LINK_COMPONENTS
Support
)
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index 2c63e64..9df8853 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -1002,3 +1002,16 @@
return g_formatters;
}
+
+bool CPlusPlusLanguage::IsSourceFile(llvm::StringRef file_path) const {
+ const auto suffixes = {".cpp", ".cxx", ".c++", ".cc", ".c",
+ ".h", ".hh", ".hpp", ".hxx", ".h++"};
+ for (auto suffix : suffixes) {
+ if (file_path.endswith_lower(suffix))
+ return true;
+ }
+
+ // Check if we're in a STL path (where the files usually have no extension
+ // that we could check for.
+ return file_path.contains("/usr/include/c++/");
+}
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
index 7380ef3..505ebb2 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
@@ -19,6 +19,7 @@
#include "llvm/ADT/StringRef.h"
// Project includes
+#include "Plugins/Language/ClangCommon/ClangHighlighter.h"
#include "lldb/Target/Language.h"
#include "lldb/Utility/ConstString.h"
#include "lldb/lldb-private.h"
@@ -26,6 +27,8 @@
namespace lldb_private {
class CPlusPlusLanguage : public Language {
+ ClangHighlighter m_highlighter;
+
public:
class MethodName {
public:
@@ -90,6 +93,10 @@
HardcodedFormatters::HardcodedSyntheticFinder
GetHardcodedSynthetics() override;
+ bool IsSourceFile(llvm::StringRef file_path) const override;
+
+ const Highlighter *GetHighlighter() const override { return &m_highlighter; }
+
//------------------------------------------------------------------
// Static Functions
//------------------------------------------------------------------
diff --git a/lldb/source/Plugins/Language/ClangCommon/CMakeLists.txt b/lldb/source/Plugins/Language/ClangCommon/CMakeLists.txt
new file mode 100644
index 0000000..854320d
--- /dev/null
+++ b/lldb/source/Plugins/Language/ClangCommon/CMakeLists.txt
@@ -0,0 +1,9 @@
+add_lldb_library(lldbPluginClangCommon PLUGIN
+ ClangHighlighter.cpp
+
+ LINK_LIBS
+ lldbCore
+ lldbUtility
+ LINK_COMPONENTS
+ Support
+)
diff --git a/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.cpp b/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.cpp
new file mode 100644
index 0000000..29da03d
--- /dev/null
+++ b/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.cpp
@@ -0,0 +1,227 @@
+//===-- ClangHighlighter.cpp ------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangHighlighter.h"
+
+#include "lldb/Target/Language.h"
+#include "lldb/Utility/AnsiTerminal.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+using namespace lldb_private;
+
+bool ClangHighlighter::isKeyword(llvm::StringRef token) const {
+ return keywords.find(token) != keywords.end();
+}
+
+ClangHighlighter::ClangHighlighter() {
+#define KEYWORD(X, N) keywords.insert(#X);
+#include "clang/Basic/TokenKinds.def"
+}
+
+/// Determines which style should be applied to the given token.
+/// \param highlighter
+/// The current highlighter that should use the style.
+/// \param token
+/// The current token.
+/// \param tok_str
+/// The string in the source code the token represents.
+/// \param options
+/// The style we use for coloring the source code.
+/// \param in_pp_directive
+/// If we are currently in a preprocessor directive. NOTE: This is
+/// passed by reference and will be updated if the current token starts
+/// or ends a preprocessor directive.
+/// \return
+/// The ColorStyle that should be applied to the token.
+static HighlightStyle::ColorStyle
+determineClangStyle(const ClangHighlighter &highlighter,
+ const clang::Token &token, llvm::StringRef tok_str,
+ const HighlightStyle &options, bool &in_pp_directive) {
+ using namespace clang;
+
+ if (token.is(tok::comment)) {
+ // If we were in a preprocessor directive before, we now left it.
+ in_pp_directive = false;
+ return options.comment;
+ } else if (in_pp_directive || token.getKind() == tok::hash) {
+ // Let's assume that the rest of the line is a PP directive.
+ in_pp_directive = true;
+ // Preprocessor directives are hard to match, so we have to hack this in.
+ return options.pp_directive;
+ } else if (tok::isStringLiteral(token.getKind()))
+ return options.string_literal;
+ else if (tok::isLiteral(token.getKind()))
+ return options.scalar_literal;
+ else if (highlighter.isKeyword(tok_str))
+ return options.keyword;
+ else
+ switch (token.getKind()) {
+ case tok::raw_identifier:
+ case tok::identifier:
+ return options.identifier;
+ case tok::l_brace:
+ case tok::r_brace:
+ return options.braces;
+ case tok::l_square:
+ case tok::r_square:
+ return options.square_brackets;
+ case tok::l_paren:
+ case tok::r_paren:
+ return options.parentheses;
+ case tok::comma:
+ return options.comma;
+ case tok::coloncolon:
+ case tok::colon:
+ return options.colon;
+
+ case tok::amp:
+ case tok::ampamp:
+ case tok::ampequal:
+ case tok::star:
+ case tok::starequal:
+ case tok::plus:
+ case tok::plusplus:
+ case tok::plusequal:
+ case tok::minus:
+ case tok::arrow:
+ case tok::minusminus:
+ case tok::minusequal:
+ case tok::tilde:
+ case tok::exclaim:
+ case tok::exclaimequal:
+ case tok::slash:
+ case tok::slashequal:
+ case tok::percent:
+ case tok::percentequal:
+ case tok::less:
+ case tok::lessless:
+ case tok::lessequal:
+ case tok::lesslessequal:
+ case tok::spaceship:
+ case tok::greater:
+ case tok::greatergreater:
+ case tok::greaterequal:
+ case tok::greatergreaterequal:
+ case tok::caret:
+ case tok::caretequal:
+ case tok::pipe:
+ case tok::pipepipe:
+ case tok::pipeequal:
+ case tok::question:
+ case tok::equal:
+ case tok::equalequal:
+ return options.operators;
+ default:
+ break;
+ }
+ return HighlightStyle::ColorStyle();
+}
+
+std::size_t ClangHighlighter::Highlight(const HighlightStyle &options,
+ llvm::StringRef line,
+ llvm::StringRef previous_lines,
+ Stream &result) const {
+ using namespace clang;
+
+ std::size_t written_bytes = 0;
+
+ FileSystemOptions file_opts;
+ FileManager file_mgr(file_opts);
+
+ unsigned line_number = previous_lines.count('\n') + 1U;
+
+ // Let's build the actual source code Clang needs and setup some utility
+ // objects.
+ std::string full_source = previous_lines.str() + line.str();
+ llvm::IntrusiveRefCntPtr<DiagnosticIDs> diag_ids(new DiagnosticIDs());
+ llvm::IntrusiveRefCntPtr<DiagnosticOptions> diags_opts(
+ new DiagnosticOptions());
+ DiagnosticsEngine diags(diag_ids, diags_opts);
+ clang::SourceManager SM(diags, file_mgr);
+ auto buf = llvm::MemoryBuffer::getMemBuffer(full_source);
+
+ FileID FID = SM.createFileID(clang::SourceManager::Unowned, buf.get());
+
+ // Let's just enable the latest ObjC and C++ which should get most tokens
+ // right.
+ LangOptions Opts;
+ Opts.ObjC2 = true;
+ Opts.CPlusPlus17 = true;
+ Opts.LineComment = true;
+
+ Lexer lex(FID, buf.get(), SM, Opts);
+ // The lexer should keep whitespace around.
+ lex.SetKeepWhitespaceMode(true);
+
+ // Keeps track if we have entered a PP directive.
+ bool in_pp_directive = false;
+
+ // True once we actually lexed the user provided line.
+ bool found_user_line = false;
+
+ Token token;
+ bool exit = false;
+ while (!exit) {
+ // Returns true if this is the last token we get from the lexer.
+ exit = lex.LexFromRawLexer(token);
+
+ bool invalid = false;
+ unsigned current_line_number =
+ SM.getSpellingLineNumber(token.getLocation(), &invalid);
+ if (current_line_number != line_number)
+ continue;
+ found_user_line = true;
+
+ // We don't need to print any tokens without a spelling line number.
+ if (invalid)
+ continue;
+
+ // Same as above but with the column number.
+ invalid = false;
+ unsigned start = SM.getSpellingColumnNumber(token.getLocation(), &invalid);
+ if (invalid)
+ continue;
+ // Column numbers start at 1, but indexes in our string start at 0.
+ --start;
+
+ // Annotations don't have a length, so let's skip them.
+ if (token.isAnnotation())
+ continue;
+
+ // Extract the token string from our source code.
+ llvm::StringRef tok_str = line.substr(start, token.getLength());
+
+ // If the token is just an empty string, we can skip all the work below.
+ if (tok_str.empty())
+ continue;
+
+ // See how we are supposed to highlight this token.
+ HighlightStyle::ColorStyle color =
+ determineClangStyle(*this, token, tok_str, options, in_pp_directive);
+
+ written_bytes += color.Apply(result, tok_str);
+ }
+
+ // If we went over the whole file but couldn't find our own file, then
+ // somehow our setup was wrong. When we're in release mode we just give the
+ // user the normal line and pretend we don't know how to highlight it. In
+ // debug mode we bail out with an assert as this should never happen.
+ if (!found_user_line) {
+ result << line;
+ written_bytes += line.size();
+ assert(false && "We couldn't find the user line in the input file?");
+ }
+
+ return written_bytes;
+}
diff --git a/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.h b/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.h
new file mode 100644
index 0000000..4eb239b
--- /dev/null
+++ b/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.h
@@ -0,0 +1,42 @@
+//===-- ClangHighlighter.h --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ClangHighlighter_h_
+#define liblldb_ClangHighlighter_h_
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+#include "lldb/Utility/Stream.h"
+#include "llvm/ADT/StringSet.h"
+
+// Project includes
+#include "lldb/Core/Highlighter.h"
+
+namespace lldb_private {
+
+class ClangHighlighter : public Highlighter {
+ llvm::StringSet<> keywords;
+
+public:
+ ClangHighlighter();
+ llvm::StringRef GetName() const override { return "clang"; }
+
+ std::size_t Highlight(const HighlightStyle &options, llvm::StringRef line,
+ llvm::StringRef previous_lines,
+ Stream &s) const override;
+
+ /// Returns true if the given string represents a keywords in any Clang
+ /// supported language.
+ bool isKeyword(llvm::StringRef token) const;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ClangHighlighter_h_
diff --git a/lldb/source/Plugins/Language/Go/GoLanguage.cpp b/lldb/source/Plugins/Language/Go/GoLanguage.cpp
index 66b4530..ed99b92 100644
--- a/lldb/source/Plugins/Language/Go/GoLanguage.cpp
+++ b/lldb/source/Plugins/Language/Go/GoLanguage.cpp
@@ -125,3 +125,7 @@
return g_formatters;
}
+
+bool GoLanguage::IsSourceFile(llvm::StringRef file_path) const {
+ return file_path.endswith(".go");
+}
diff --git a/lldb/source/Plugins/Language/Go/GoLanguage.h b/lldb/source/Plugins/Language/Go/GoLanguage.h
index ebec1d7..ee21c56 100644
--- a/lldb/source/Plugins/Language/Go/GoLanguage.h
+++ b/lldb/source/Plugins/Language/Go/GoLanguage.h
@@ -39,6 +39,8 @@
HardcodedFormatters::HardcodedSyntheticFinder
GetHardcodedSynthetics() override;
+ bool IsSourceFile(llvm::StringRef file_path) const override;
+
//------------------------------------------------------------------
// Static Functions
//------------------------------------------------------------------
diff --git a/lldb/source/Plugins/Language/Java/JavaLanguage.cpp b/lldb/source/Plugins/Language/Java/JavaLanguage.cpp
index b17862f..634ae4c 100644
--- a/lldb/source/Plugins/Language/Java/JavaLanguage.cpp
+++ b/lldb/source/Plugins/Language/Java/JavaLanguage.cpp
@@ -99,3 +99,7 @@
});
return g_category;
}
+
+bool JavaLanguage::IsSourceFile(llvm::StringRef file_path) const {
+ return file_path.endswith(".java");
+}
diff --git a/lldb/source/Plugins/Language/Java/JavaLanguage.h b/lldb/source/Plugins/Language/Java/JavaLanguage.h
index 5b65250..a16ea99 100644
--- a/lldb/source/Plugins/Language/Java/JavaLanguage.h
+++ b/lldb/source/Plugins/Language/Java/JavaLanguage.h
@@ -45,6 +45,8 @@
bool IsNilReference(ValueObject &valobj) override;
lldb::TypeCategoryImplSP GetFormatters() override;
+
+ bool IsSourceFile(llvm::StringRef file_path) const override;
};
} // namespace lldb_private
diff --git a/lldb/source/Plugins/Language/OCaml/OCamlLanguage.cpp b/lldb/source/Plugins/Language/OCaml/OCamlLanguage.cpp
index ec24a36..b294f84 100644
--- a/lldb/source/Plugins/Language/OCaml/OCamlLanguage.cpp
+++ b/lldb/source/Plugins/Language/OCaml/OCamlLanguage.cpp
@@ -28,6 +28,15 @@
using namespace lldb;
using namespace lldb_private;
+bool OCamlLanguage::IsSourceFile(llvm::StringRef file_path) const {
+ const auto suffixes = {".ml", ".mli"};
+ for (auto suffix : suffixes) {
+ if (file_path.endswith_lower(suffix))
+ return true;
+ }
+ return false;
+}
+
void OCamlLanguage::Initialize() {
PluginManager::RegisterPlugin(GetPluginNameStatic(), "OCaml Language",
CreateInstance);
diff --git a/lldb/source/Plugins/Language/OCaml/OCamlLanguage.h b/lldb/source/Plugins/Language/OCaml/OCamlLanguage.h
index 21837fe..c4a75db 100644
--- a/lldb/source/Plugins/Language/OCaml/OCamlLanguage.h
+++ b/lldb/source/Plugins/Language/OCaml/OCamlLanguage.h
@@ -31,6 +31,8 @@
return lldb::eLanguageTypeOCaml;
}
+ bool IsSourceFile(llvm::StringRef file_path) const override;
+
static void Initialize();
static void Terminate();
diff --git a/lldb/source/Plugins/Language/ObjC/CMakeLists.txt b/lldb/source/Plugins/Language/ObjC/CMakeLists.txt
index 95ace3a..afb68d4 100644
--- a/lldb/source/Plugins/Language/ObjC/CMakeLists.txt
+++ b/lldb/source/Plugins/Language/ObjC/CMakeLists.txt
@@ -31,6 +31,7 @@
lldbTarget
lldbUtility
lldbPluginAppleObjCRuntime
+ lldbPluginClangCommon
EXTRA_CXXFLAGS ${EXTRA_CXXFLAGS}
)
diff --git a/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp b/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp
index 47874b3..d57b999 100644
--- a/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp
+++ b/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp
@@ -1102,3 +1102,12 @@
bool isZero = valobj.GetValueAsUnsigned(0, &canReadValue) == 0;
return canReadValue && isZero;
}
+
+bool ObjCLanguage::IsSourceFile(llvm::StringRef file_path) const {
+ const auto suffixes = {".h", ".m", ".M"};
+ for (auto suffix : suffixes) {
+ if (file_path.endswith_lower(suffix))
+ return true;
+ }
+ return false;
+}
diff --git a/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h b/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h
index 9782c5d..10c30b3 100644
--- a/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h
+++ b/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h
@@ -17,6 +17,7 @@
// Other libraries and framework includes
// Project includes
+#include "Plugins/Language/ClangCommon/ClangHighlighter.h"
#include "lldb/Target/Language.h"
#include "lldb/Utility/ConstString.h"
#include "lldb/lldb-private.h"
@@ -24,6 +25,8 @@
namespace lldb_private {
class ObjCLanguage : public Language {
+ ClangHighlighter m_highlighter;
+
public:
class MethodName {
public:
@@ -121,6 +124,10 @@
bool IsNilReference(ValueObject &valobj) override;
+ bool IsSourceFile(llvm::StringRef file_path) const override;
+
+ const Highlighter *GetHighlighter() const override { return &m_highlighter; }
+
//------------------------------------------------------------------
// Static Functions
//------------------------------------------------------------------
diff --git a/lldb/source/Plugins/Language/ObjCPlusPlus/CMakeLists.txt b/lldb/source/Plugins/Language/ObjCPlusPlus/CMakeLists.txt
index 75df979..1aa5cc1 100644
--- a/lldb/source/Plugins/Language/ObjCPlusPlus/CMakeLists.txt
+++ b/lldb/source/Plugins/Language/ObjCPlusPlus/CMakeLists.txt
@@ -1,7 +1,8 @@
add_lldb_library(lldbPluginObjCPlusPlusLanguage PLUGIN
ObjCPlusPlusLanguage.cpp
-
+
LINK_LIBS
lldbCore
lldbTarget
+ lldbPluginClangCommon
)
diff --git a/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.cpp
index bfc22c9..5e6d86e 100644
--- a/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.cpp
@@ -16,6 +16,15 @@
using namespace lldb;
using namespace lldb_private;
+bool ObjCPlusPlusLanguage::IsSourceFile(llvm::StringRef file_path) const {
+ const auto suffixes = {".h", ".mm"};
+ for (auto suffix : suffixes) {
+ if (file_path.endswith_lower(suffix))
+ return true;
+ }
+ return false;
+}
+
void ObjCPlusPlusLanguage::Initialize() {
PluginManager::RegisterPlugin(GetPluginNameStatic(), "Objective-C++ Language",
CreateInstance);
diff --git a/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h b/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h
index 588b522..b1825c9 100644
--- a/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h
+++ b/lldb/source/Plugins/Language/ObjCPlusPlus/ObjCPlusPlusLanguage.h
@@ -14,12 +14,15 @@
// C++ Includes
// Other libraries and framework includes
// Project includes
+#include "Plugins/Language/ClangCommon/ClangHighlighter.h"
#include "lldb/Target/Language.h"
#include "lldb/lldb-private.h"
namespace lldb_private {
class ObjCPlusPlusLanguage : public Language {
+ ClangHighlighter m_highlighter;
+
public:
ObjCPlusPlusLanguage() = default;
@@ -29,6 +32,10 @@
return lldb::eLanguageTypeObjC_plus_plus;
}
+ bool IsSourceFile(llvm::StringRef file_path) const override;
+
+ const Highlighter *GetHighlighter() const override { return &m_highlighter; }
+
//------------------------------------------------------------------
// Static Functions
//------------------------------------------------------------------
diff --git a/lldb/source/Target/Language.cpp b/lldb/source/Target/Language.cpp
index cde6f86..599b442 100644
--- a/lldb/source/Target/Language.cpp
+++ b/lldb/source/Target/Language.cpp
@@ -77,7 +77,39 @@
return nullptr;
}
+Language *Language::FindPlugin(llvm::StringRef file_path) {
+ Language *result = nullptr;
+ ForEach([&result, file_path](Language *language) {
+ if (language->IsSourceFile(file_path)) {
+ result = language;
+ return false;
+ }
+ return true;
+ });
+ return result;
+}
+
+Language *Language::FindPlugin(LanguageType language,
+ llvm::StringRef file_path) {
+ Language *result = FindPlugin(language);
+ // Finding a language by file path is slower, we so we use this as the
+ // fallback.
+ if (!result)
+ result = FindPlugin(file_path);
+ return result;
+}
+
void Language::ForEach(std::function<bool(Language *)> callback) {
+ // If we want to iterate over all languages, we first have to complete the
+ // LanguagesMap.
+ static llvm::once_flag g_initialize;
+ llvm::call_once(g_initialize, [] {
+ for (unsigned lang = eLanguageTypeUnknown; lang < eNumLanguageTypes;
+ ++lang) {
+ FindPlugin(static_cast<lldb::LanguageType>(lang));
+ }
+ });
+
std::lock_guard<std::mutex> guard(GetLanguagesMutex());
LanguagesMap &map(GetLanguagesMap());
for (const auto &entry : map) {