tfiala updated this revision to Diff 71996.
tfiala added a comment.

This change addresses Jim's and Greg's suggestions.

- stop-show-column now is a quad-state:
  - ansi-or-caret (default):  Shows ANSI if color is enabled; otherwise, uses 
the text-based caret.
  - ansi-only:  ANSI highlighting is used if color is enabled; otherwise, no 
column marking.
  - caret-only: text-based caret on the following line will always be used when 
showing stop column.
  - none: no column marking is done.

- stop-show-column-decorator-pre: this is a format entity (with tab completion 
for ${ansi.*} codes).  It defaults to the start code for underlining 
(${ansi.underline}), the default column stop marker display style when using 
ANSI highlighting.
- stop-show-column-decorator-post: this is the format entity for disabling 
whatever was enabled in the "-pre" setting.  This defaults to ${ansi.stop}.  
This would need to be changed if color codes were used for 
background/foreground of anything other than inverse.

- other minor odds and ends, like changing the constructor of 
SourceManager::File to either take a Target or a Debugger, but not both.


https://reviews.llvm.org/D20835

Files:
  include/lldb/API/SBSourceManager.h
  include/lldb/Core/Debugger.h
  include/lldb/Core/Disassembler.h
  include/lldb/Core/SourceManager.h
  include/lldb/lldb-enumerations.h
  packages/Python/lldbsuite/test/functionalities/load_unload/TestLoadUnload.py
  packages/Python/lldbsuite/test/lldbtest.py
  packages/Python/lldbsuite/test/settings/TestSettings.py
  packages/Python/lldbsuite/test/source-manager/TestSourceManager.py
  scripts/interface/SBSourceManager.i
  source/API/SBSourceManager.cpp
  source/Commands/CommandObjectSource.cpp
  source/Core/Debugger.cpp
  source/Core/Disassembler.cpp
  source/Core/SourceManager.cpp
  source/Target/StackFrame.cpp

Index: source/Target/StackFrame.cpp
===================================================================
--- source/Target/StackFrame.cpp
+++ source/Target/StackFrame.cpp
@@ -1855,7 +1855,8 @@
           size_t num_lines =
               target->GetSourceManager().DisplaySourceLinesWithLineNumbers(
                   m_sc.line_entry.file, m_sc.line_entry.line,
-                  source_lines_before, source_lines_after, "->", &strm);
+                  m_sc.line_entry.column, source_lines_before,
+                  source_lines_after, "->", &strm);
           if (num_lines != 0)
             have_source = true;
           // TODO: Give here a one time warning if source file is missing.
Index: source/Core/SourceManager.cpp
===================================================================
--- source/Core/SourceManager.cpp
+++ source/Core/SourceManager.cpp
@@ -22,6 +22,7 @@
 #include "lldb/Symbol/Function.h"
 #include "lldb/Symbol/SymbolContext.h"
 #include "lldb/Target/Target.h"
+#include "lldb/Utility/AnsiTerminal.h"
 
 using namespace lldb;
 using namespace lldb_private;
@@ -71,16 +72,55 @@
 
   // If file_sp is no good or it points to a non-existent file, reset it.
   if (!file_sp || !file_sp->GetFileSpec().Exists()) {
-    file_sp.reset(new File(file_spec, target_sp.get()));
+    if (target_sp)
+      file_sp.reset(new File(file_spec, target_sp.get()));
+    else
+      file_sp.reset(new File(file_spec, debugger_sp));
 
     if (debugger_sp)
       debugger_sp->GetSourceFileCache().AddSourceFile(file_sp);
   }
   return file_sp;
 }
 
+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.
+  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;
+
+  // 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.
+  const auto value = debugger_sp->GetStopShowColumn();
+  return ((value == eStopShowColumnAnsiOrCaret) ||
+          (value == eStopShowColumnAnsiOnly));
+}
+
+static bool should_show_stop_column_with_caret(DebuggerSP debugger_sp) {
+  // We don't use text-based stop column formatting if we can't lookup values
+  // from the debugger.
+  if (!debugger_sp)
+    return false;
+
+  // If we're asked to show the first available of ANSI or caret, then
+  // we do show the caret when ANSI is not available.
+  const auto value = debugger_sp->GetStopShowColumn();
+  if ((value == eStopShowColumnAnsiOrCaret) && !debugger_sp->GetUseColor())
+    return true;
+
+  // The only other time we use caret is if we're explicitly asked to show
+  // caret.
+  return value == eStopShowColumnCaretOnly;
+}
+
 size_t SourceManager::DisplaySourceLinesWithLineNumbersUsingLastFile(
-    uint32_t start_line, uint32_t count, uint32_t curr_line,
+    uint32_t start_line, uint32_t count, uint32_t curr_line, uint32_t column,
     const char *current_line_cstr, Stream *s,
     const SymbolContextList *bp_locs) {
   if (count == 0)
@@ -123,7 +163,20 @@
       return_value +=
           s->Printf("%s%2.2s %-4u\t", prefix,
                     line == curr_line ? current_line_cstr : "", line);
-      size_t this_line_size = m_last_file_sp->DisplaySourceLines(line, 0, 0, s);
+      size_t this_line_size = m_last_file_sp->DisplaySourceLines(
+          line, line == curr_line ? column : 0, 0, 0, s);
+      if (column != 0 && line == curr_line &&
+          should_show_stop_column_with_caret(m_debugger_wp.lock())) {
+        // Display caret cursor.
+        std::string src_line;
+        m_last_file_sp->GetLine(line, src_line);
+        return_value += s->Printf("    \t");
+        // Insert a space for every non-tab character in the source line.
+        for (int i = 0; i < column - 1 && i < src_line.length(); ++i)
+          return_value += s->PutChar(src_line[i] == '\t' ? '\t' : ' ');
+        // Now add the caret.
+        return_value += s->Printf("^\n");
+      }
       if (this_line_size == 0) {
         m_last_line = UINT32_MAX;
         break;
@@ -135,8 +188,9 @@
 }
 
 size_t SourceManager::DisplaySourceLinesWithLineNumbers(
-    const FileSpec &file_spec, uint32_t line, uint32_t context_before,
-    uint32_t context_after, const char *current_line_cstr, Stream *s,
+    const FileSpec &file_spec, uint32_t line, uint32_t column,
+    uint32_t context_before, uint32_t context_after,
+    const char *current_line_cstr, Stream *s,
     const SymbolContextList *bp_locs) {
   FileSP file_sp(GetFile(file_spec));
 
@@ -153,7 +207,7 @@
     m_last_file_sp = file_sp;
   }
   return DisplaySourceLinesWithLineNumbersUsingLastFile(
-      start_line, count, line, current_line_cstr, s, bp_locs);
+      start_line, count, line, column, current_line_cstr, s, bp_locs);
 }
 
 size_t SourceManager::DisplayMoreWithLineNumbers(
@@ -193,8 +247,9 @@
     } else
       m_last_line = 1;
 
+    const uint32_t column = 0;
     return DisplaySourceLinesWithLineNumbersUsingLastFile(
-        m_last_line, m_last_count, UINT32_MAX, "", s, bp_locs);
+        m_last_line, m_last_count, UINT32_MAX, column, "", s, bp_locs);
   }
   return 0;
 }
@@ -272,10 +327,28 @@
                                          match_lines);
 }
 
+SourceManager::File::File(const FileSpec &file_spec,
+                          lldb::DebuggerSP debugger_sp)
+    : m_file_spec_orig(file_spec), m_file_spec(file_spec),
+      m_mod_time(file_spec.GetModificationTime()), m_source_map_mod_id(0),
+      m_data_sp(), m_offsets(),
+      m_debugger_wp(debugger_sp) {
+  CommonInitializer(file_spec, nullptr);
+}
+
 SourceManager::File::File(const FileSpec &file_spec, Target *target)
     : m_file_spec_orig(file_spec), m_file_spec(file_spec),
       m_mod_time(file_spec.GetModificationTime()), m_source_map_mod_id(0),
-      m_data_sp(), m_offsets() {
+      m_data_sp(), m_offsets(),
+      m_debugger_wp(target ? target->GetDebugger().shared_from_this() :
+                    DebuggerSP()) {
+  CommonInitializer(file_spec, target);
+}
+
+SourceManager::File::~File() {}
+
+void SourceManager::File::CommonInitializer(const FileSpec &file_spec,
+                                            Target *target) {
   if (!m_mod_time.IsValid()) {
     if (target) {
       m_source_map_mod_id = target->GetSourcePathMap().GetModificationID();
@@ -286,10 +359,10 @@
         bool check_inlines = false;
         SymbolContextList sc_list;
         size_t num_matches =
-            target->GetImages().ResolveSymbolContextForFilePath(
-                file_spec.GetFilename().AsCString(), 0, check_inlines,
-                lldb::eSymbolContextModule | lldb::eSymbolContextCompUnit,
-                sc_list);
+        target->GetImages().ResolveSymbolContextForFilePath(
+                                                            file_spec.GetFilename().AsCString(), 0, check_inlines,
+                                                            lldb::eSymbolContextModule | lldb::eSymbolContextCompUnit,
+                                                            sc_list);
         bool got_multiple = false;
         if (num_matches != 0) {
           if (num_matches > 1) {
@@ -332,13 +405,11 @@
       }
     }
   }
-
+  
   if (m_mod_time.IsValid())
     m_data_sp = m_file_spec.ReadFileContents();
 }
 
-SourceManager::File::~File() {}
-
 uint32_t SourceManager::File::GetLineOffset(uint32_t line) {
   if (line == 0)
     return UINT32_MAX;
@@ -418,7 +489,7 @@
   }
 }
 
-size_t SourceManager::File::DisplaySourceLines(uint32_t line,
+size_t SourceManager::File::DisplaySourceLines(uint32_t line, uint32_t column,
                                                uint32_t context_before,
                                                uint32_t context_after,
                                                Stream *s) {
@@ -440,7 +511,56 @@
     if (start_line_offset < end_line_offset) {
       size_t count = end_line_offset - start_line_offset;
       const uint8_t *cstr = m_data_sp->GetBytes() + start_line_offset;
-      bytes_written = s->Write(cstr, count);
+
+      bool displayed_line = false;
+
+      if (column && (column < count)) {
+        auto debugger_sp = m_debugger_wp.lock();
+        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.
+          std::string format_entry_pre =
+              debugger_sp->GetStopShowColumnDecoratorPre();
+          std::string format_entry_post =
+              debugger_sp->GetStopShowColumnDecoratorPost();
+
+          // We only bother breaking up the line to format the marked column if
+          // there is any marking specified on both sides of the marked column.
+          // In ANSI-terminal-sequence land, there must be a post if there is a
+          // pre format, and vice versa.
+          if (!format_entry_pre.empty() && !format_entry_post.empty()) {
+            // Mark the current column with the desired escape sequence for
+            // formatting the column (e.g. underline, inverse, etc.)
+
+            // First print the part before the column to mark.
+            bytes_written = s->Write(cstr, column - 1);
+
+            // Write the pre escape sequence.
+            bytes_written += s->Write(format_entry_pre.c_str(),
+                                      format_entry_pre.length());
+
+            // Write the marked column.
+            bytes_written += s->Write(cstr + column - 1, 1);
+
+            // Write the post escape sequence.
+            bytes_written += s->Write(format_entry_post.c_str(),
+                                      format_entry_post.length());
+
+            // And finish up with the rest of the line.
+            bytes_written += s->Write(cstr + column, count - column);
+
+            // Keep track of the fact that we just wrote the line.
+            displayed_line = true;
+          }
+        }
+      }
+
+      // 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);
+
+      // Ensure we get an end of line character one way or another.
       if (!is_newline_char(cstr[count - 1]))
         bytes_written += s->EOL();
     }
Index: source/Core/Disassembler.cpp
===================================================================
--- source/Core/Disassembler.cpp
+++ source/Core/Disassembler.cpp
@@ -289,6 +289,9 @@
         func_decl_file == prologue_end_line.original_file) {
       decl_line.file = func_decl_file;
       decl_line.line = func_decl_line;
+      // TODO do we care about column on these entries?  If so, we need to
+      // plumb that through GetStartLineSourceInfo.
+      decl_line.column = 0;
     }
   }
   return decl_line;
@@ -448,6 +451,7 @@
               SourceLine this_line;
               this_line.file = sc.line_entry.file;
               this_line.line = sc.line_entry.line;
+              this_line.column = sc.line_entry.column;
               if (ElideMixedSourceAndDisassemblyLine(exe_ctx, sc, this_line) ==
                   false)
                 AddLineToSourceLineTables(this_line, source_lines_seen);
@@ -613,7 +617,7 @@
             line_highlight = "**";
           }
           source_manager.DisplaySourceLinesWithLineNumbers(
-              ln.file, ln.line, 0, 0, line_highlight, &strm);
+              ln.file, ln.line, ln.column, 0, 0, line_highlight, &strm);
         }
         if (source_lines_to_display.print_source_context_end_eol)
           strm.EOL();
Index: source/Core/Debugger.cpp
===================================================================
--- source/Core/Debugger.cpp
+++ source/Core/Debugger.cpp
@@ -140,6 +140,25 @@
 // {${function.initial-function}{${module.file.basename}`}{${function.name-without-args}}:\n}{${function.changed}\n{${module.file.basename}`}{${function.name-without-args}}:\n}{${current-pc-arrow}
 // }{${addr-file-or-load}}:
 
+#define DEFAULT_STOP_COLUMN_FORMAT_PRE "${ansi.underline}"
+#define DEFAULT_STOP_COLUMN_FORMAT_POST "${ansi.normal}"
+
+static OptionEnumValueElement s_stop_show_column_values[] = {
+  {eStopShowColumnAnsiOrCaret, "ansi-or-caret",
+   "Highlight the stop column with ANSI terminal codes when color/ANSI mode "
+   "is enabled; otherwise, fall back to using a text-only caret (^) as if "
+   "\"caret-only\" mode was selected."},
+  {eStopShowColumnAnsiOnly, "ansi-only",
+   "Highlight the stop column with ANSI terminal codes when running LLDB with "
+   "color/ANSI enabled."},
+  {eStopShowColumnCaretOnly, "caret-only",
+   "Highlight the stop column with a caret character (^) underneath the stop "
+   "column.  This method introduces a new line in source listings that display "
+   "thread stop locations."},
+  {eStopShowColumnNone, "none",
+   "Do not highlight the stop column."},
+  {0, nullptr, nullptr}};
+
 static PropertyDefinition g_properties[] = {
     {"auto-confirm", OptionValue::eTypeBoolean, true, false, nullptr, nullptr,
      "If true all confirmation prompts will receive their default reply."},
@@ -173,6 +192,20 @@
     {"stop-line-count-before", OptionValue::eTypeSInt64, true, 3, nullptr,
      nullptr, "The number of sources lines to display that come before the "
               "current source line when displaying a stopped context."},
+    {"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 "
+     "mark the current position when displaying a stopped context."},
+    {"stop-show-column-decorator-pre", OptionValue::eTypeFormatEntity, true, 0,
+     DEFAULT_STOP_COLUMN_FORMAT_PRE, nullptr,
+     "When displaying the column marker in a color-enabled (i.e. ANSI) "
+     "terminal, use the ANSI terminal code specified in this format at the "
+     "immediately before the column to be marked."},
+    {"stop-show-column-decorator-post", OptionValue::eTypeFormatEntity, true, 0,
+     DEFAULT_STOP_COLUMN_FORMAT_POST, nullptr,
+     "When displaying the column marker in a color-enabled (i.e. ANSI) "
+     "terminal, use the ANSI terminal code specified in this format "
+     "immediately after the column to be marked."},
     {"term-width", OptionValue::eTypeSInt64, true, 80, nullptr, nullptr,
      "The maximum number of columns to use for displaying text."},
     {"thread-format", OptionValue::eTypeFormatEntity, true, 0,
@@ -210,6 +243,9 @@
   ePropertyStopDisassemblyDisplay,
   ePropertyStopLineCountAfter,
   ePropertyStopLineCountBefore,
+  ePropertyStopShowColumn,
+  ePropertyStopShowColumnDecoratorPre,
+  ePropertyStopShowColumnDecoratorPost,
   ePropertyTerminalWidth,
   ePropertyThreadFormat,
   ePropertyUseExternalEditor,
@@ -371,6 +407,33 @@
   return ret;
 }
 
+StopShowColumn Debugger::GetStopShowColumn() const {
+  const uint32_t idx = ePropertyStopShowColumn;
+  return (lldb::StopShowColumn)
+  m_collection_sp->GetPropertyAtIndexAsEnumeration(
+      nullptr, idx, g_properties[idx].default_uint_value);
+}
+
+const char *Debugger::GetStopShowColumnDecoratorPre() const {
+  const uint32_t idx = ePropertyStopShowColumnDecoratorPre;
+  auto entry = m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx);
+  if (!entry || entry->children.empty())
+    return nullptr;
+
+  auto format_entry = entry->children[0];
+  return format_entry.string.c_str();
+}
+
+const char *Debugger::GetStopShowColumnDecoratorPost() const {
+  const uint32_t idx = ePropertyStopShowColumnDecoratorPost;
+  auto entry = m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx);
+  if (!entry || entry->children.empty())
+    return nullptr;
+
+  auto format_entry = entry->children[0];
+  return format_entry.string.c_str();
+}
+
 uint32_t Debugger::GetStopSourceLineCount(bool before) const {
   const uint32_t idx =
       before ? ePropertyStopLineCountBefore : ePropertyStopLineCountAfter;
Index: source/Commands/CommandObjectSource.cpp
===================================================================
--- source/Commands/CommandObjectSource.cpp
+++ source/Commands/CommandObjectSource.cpp
@@ -896,8 +896,10 @@
 
       result.AppendMessageWithFormat("File: %s\n",
                                      start_file.GetPath().c_str());
+      // We don't care about the column here.
+      const uint32_t column = 0;
       return target->GetSourceManager().DisplaySourceLinesWithLineNumbers(
-          start_file, line_no, 0, m_options.num_lines, "",
+          start_file, line_no, 0, m_options.num_lines, column, "",
           &result.GetOutputStream(), GetBreakpointLocations());
     } else {
       result.AppendErrorWithFormat(
@@ -1150,8 +1152,13 @@
           size_t lines_to_back_up =
               m_options.num_lines >= 10 ? 5 : m_options.num_lines / 2;
 
+          const uint32_t column =
+              (m_interpreter.GetDebugger().GetStopShowColumn() !=
+               eStopShowColumnNone)
+                  ? sc.line_entry.column
+                  : 0;
           target->GetSourceManager().DisplaySourceLinesWithLineNumbers(
-              sc.comp_unit, sc.line_entry.line, lines_to_back_up,
+              sc.comp_unit, sc.line_entry.line, lines_to_back_up, column,
               m_options.num_lines - lines_to_back_up, "->",
               &result.GetOutputStream(), GetBreakpointLocations());
           result.SetStatus(eReturnStatusSuccessFinishResult);
@@ -1187,12 +1194,14 @@
         } else
           m_breakpoint_locations.Clear();
 
+        const uint32_t column = 0;
         if (target->GetSourceManager()
                 .DisplaySourceLinesWithLineNumbersUsingLastFile(
                     m_options.start_line, // Line to display
                     m_options.num_lines,  // Lines after line to
                     UINT32_MAX,           // Don't mark "line"
-                    "",                   // Don't mark "line"
+                    column,
+                    "", // Don't mark "line"
                     &result.GetOutputStream(), GetBreakpointLocations())) {
           result.SetStatus(eReturnStatusSuccessFinishResult);
         }
@@ -1269,10 +1278,10 @@
 
           if (m_options.num_lines == 0)
             m_options.num_lines = 10;
-
+          const uint32_t column = 0;
           target->GetSourceManager().DisplaySourceLinesWithLineNumbers(
-              sc.comp_unit, m_options.start_line, 0, m_options.num_lines, "",
-              &result.GetOutputStream(), GetBreakpointLocations());
+              sc.comp_unit, m_options.start_line, 0, m_options.num_lines,
+              column, "", &result.GetOutputStream(), GetBreakpointLocations());
 
           result.SetStatus(eReturnStatusSuccessFinishResult);
         } else {
Index: source/API/SBSourceManager.cpp
===================================================================
--- source/API/SBSourceManager.cpp
+++ source/API/SBSourceManager.cpp
@@ -37,7 +37,7 @@
   }
 
   size_t DisplaySourceLinesWithLineNumbers(const lldb_private::FileSpec &file,
-                                           uint32_t line,
+                                           uint32_t line, uint32_t column,
                                            uint32_t context_before,
                                            uint32_t context_after,
                                            const char *current_line_cstr,
@@ -48,14 +48,15 @@
     lldb::TargetSP target_sp(m_target_wp.lock());
     if (target_sp) {
       return target_sp->GetSourceManager().DisplaySourceLinesWithLineNumbers(
-          file, line, context_before, context_after, current_line_cstr, s);
+          file, line, column, context_before, context_after, current_line_cstr,
+          s);
     } else {
       lldb::DebuggerSP debugger_sp(m_debugger_wp.lock());
       if (debugger_sp) {
         return debugger_sp->GetSourceManager()
-            .DisplaySourceLinesWithLineNumbers(file, line, context_before,
-                                               context_after, current_line_cstr,
-                                               s);
+            .DisplaySourceLinesWithLineNumbers(file, line, column,
+                                               context_before, context_after,
+                                               current_line_cstr, s);
       }
     }
     return 0;
@@ -96,10 +97,20 @@
 size_t SBSourceManager::DisplaySourceLinesWithLineNumbers(
     const SBFileSpec &file, uint32_t line, uint32_t context_before,
     uint32_t context_after, const char *current_line_cstr, SBStream &s) {
+  const uint32_t column = 0;
+  return DisplaySourceLinesWithLineNumbersAndColumn(
+      file.ref(), line, column, context_before, context_after,
+      current_line_cstr, s);
+}
+
+size_t SBSourceManager::DisplaySourceLinesWithLineNumbersAndColumn(
+    const SBFileSpec &file, uint32_t line, uint32_t column,
+    uint32_t context_before, uint32_t context_after,
+    const char *current_line_cstr, SBStream &s) {
   if (m_opaque_ap.get() == NULL)
     return 0;
 
   return m_opaque_ap->DisplaySourceLinesWithLineNumbers(
-      file.ref(), line, context_before, context_after, current_line_cstr,
-      s.get());
+      file.ref(), line, column, context_before, context_after,
+      current_line_cstr, s.get());
 }
Index: scripts/interface/SBSourceManager.i
===================================================================
--- scripts/interface/SBSourceManager.i
+++ scripts/interface/SBSourceManager.i
@@ -49,6 +49,13 @@
                                        uint32_t context_after,
                                        const char* current_line_cstr,
                                        lldb::SBStream &s);
+    size_t
+    DisplaySourceLinesWithLineNumbersAndColumn (const lldb::SBFileSpec &file,
+                                                uint32_t line, uint32_t column,
+                                                uint32_t context_before,
+                                                uint32_t context_after,
+                                                const char* current_line_cstr,
+                                                lldb::SBStream &s);
 };
 
 } // namespace lldb
Index: packages/Python/lldbsuite/test/source-manager/TestSourceManager.py
===================================================================
--- packages/Python/lldbsuite/test/source-manager/TestSourceManager.py
+++ packages/Python/lldbsuite/test/source-manager/TestSourceManager.py
@@ -10,67 +10,109 @@
 """
 
 from __future__ import print_function
-
+import re
 
 import lldb
 from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
 from lldbsuite.test import lldbutil
 
 
+def ansi_underline_surround_regex(inner_regex_text):
+    # return re.compile(r"\[4m%s\[0m" % inner_regex_text)
+    return "4.+\033\\[4m%s\033\\[0m" % inner_regex_text
+
+
 class SourceManagerTestCase(TestBase):
 
     mydir = TestBase.compute_mydir(__file__)
 
+    SOURCE_FILE = 'main.c'
+
+    NO_DEBUG_INFO_TESTCASE = True
+
     def setUp(self):
         # Call super's setUp().
         TestBase.setUp(self)
         # Find the line number to break inside main().
-        self.line = line_number('main.c', '// Set break point at this line.')
-
-    @add_test_categories(['pyapi'])
-    def test_display_source_python(self):
-        """Test display of source using the SBSourceManager API."""
+        self.line = line_number(self.SOURCE_FILE, '// Set break point at this line.')
+
+    def get_expected_stop_column_number(self):
+        """Return the 1-based column number of the first non-whitespace
+        character in the breakpoint source line."""
+        stop_line = get_line(self.SOURCE_FILE, self.line)
+        # The number of spaces that must be skipped to get to the first non-
+        # whitespace character --- where we expect the debugger breakpoint
+        # column to be --- is equal to the number of characters that get
+        # stripped off the front when we lstrip it, plus one to specify
+        # the character column after the initial whitespace.
+        return len(stop_line) - len(stop_line.lstrip()) + 1
+
+    def do_display_source_python_api(self, use_color, column_marker_regex):
         self.build()
         exe = os.path.join(os.getcwd(), "a.out")
         self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
 
         target = self.dbg.CreateTarget(exe)
         self.assertTrue(target, VALID_TARGET)
 
         # Launch the process, and do not stop at the entry point.
+        args = None
+        envp = None
         process = target.LaunchSimple(
-            None, None, self.get_process_working_directory())
+            args, envp, self.get_process_working_directory())
+        self.assertIsNotNone(process)
 
         #
         # Exercise Python APIs to display source lines.
         #
 
+        # Setup whether we should use ansi escape sequences, including color
+        # and styles such as underline.
+        self.dbg.SetUseColor(use_color)
+
         # Create the filespec for 'main.c'.
         filespec = lldb.SBFileSpec('main.c', False)
         source_mgr = self.dbg.GetSourceManager()
         # Use a string stream as the destination.
         stream = lldb.SBStream()
-        source_mgr.DisplaySourceLinesWithLineNumbers(filespec,
-                                                     self.line,
-                                                     2,  # context before
-                                                     2,  # context after
-                                                     "=>",  # prefix for current line
-                                                     stream)
+        column = self.get_expected_stop_column_number()
+        context_before = 2
+        context_after = 2
+        current_line_prefix = "=>"
+        source_mgr.DisplaySourceLinesWithLineNumbersAndColumn(
+            filespec, self.line, column, context_before, context_after,
+            current_line_prefix, stream)
 
         #    2
         #    3    int main(int argc, char const *argv[]) {
         # => 4        printf("Hello world.\n"); // Set break point at this line.
         #    5        return 0;
         #    6    }
         self.expect(stream.GetData(), "Source code displayed correctly",
                     exe=False,
-                    patterns=['=> %d.*Hello world' % self.line])
+                    patterns=['=> %d.*Hello world' % self.line,
+                              column_marker_regex])
 
         # Boundary condition testings for SBStream().  LLDB should not crash!
         stream.Print(None)
         stream.RedirectToFile(None, True)
 
+    @add_test_categories(['pyapi'])
+    def test_display_source_python_dumb_terminal(self):
+        """Test display of source using the SBSourceManager API, using a
+        dumb terminal and thus no color support (the default)."""
+        use_color = False
+        self.do_display_source_python_api(use_color, r"\s+\^")
+
+    @add_test_categories(['pyapi'])
+    def test_display_source_python_ansi_terminal(self):
+        """Test display of source using the SBSourceManager API, using a
+        dumb terminal and thus no color support (the default)."""
+        use_color = True
+        underline_regex = ansi_underline_surround_regex(r".")
+        self.do_display_source_python_api(use_color, underline_regex)
+
     def test_move_and_then_display_source(self):
         """Test that target.source-map settings work by moving main.c to hidden/main.c."""
         self.build()
Index: packages/Python/lldbsuite/test/settings/TestSettings.py
===================================================================
--- packages/Python/lldbsuite/test/settings/TestSettings.py
+++ packages/Python/lldbsuite/test/settings/TestSettings.py
@@ -527,6 +527,7 @@
                              "stop-disassembly-display",
                              "stop-line-count-after",
                              "stop-line-count-before",
+                             "stop-show-column",
                              "term-width",
                              "thread-format",
                              "use-external-editor",
Index: packages/Python/lldbsuite/test/lldbtest.py
===================================================================
--- packages/Python/lldbsuite/test/lldbtest.py
+++ packages/Python/lldbsuite/test/lldbtest.py
@@ -228,6 +228,10 @@
         "Unable to find '%s' within file %s" %
         (string_to_match, filename))
 
+def get_line(filename, line_number):
+    """Return the text of the line at the 1-based line number."""
+    with io.open(filename, mode='r', encoding="utf-8") as f:
+        return f.readlines()[line_number - 1]
 
 def pointer_size():
     """Return the pointer size of the host system."""
Index: packages/Python/lldbsuite/test/functionalities/load_unload/TestLoadUnload.py
===================================================================
--- packages/Python/lldbsuite/test/functionalities/load_unload/TestLoadUnload.py
+++ packages/Python/lldbsuite/test/functionalities/load_unload/TestLoadUnload.py
@@ -158,6 +158,10 @@
         exe = os.path.join(os.getcwd(), "a.out")
         self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
 
+        # Shut off ANSI color usage so we don't get ANSI escape sequences
+        # mixed in with stop locations.
+        self.dbg.SetUseColor(False)
+
         if self.platformIsDarwin():
             dylibName = 'libloadunload_d.dylib'
             dsymName = 'libloadunload_d.dylib.dSYM'
Index: include/lldb/lldb-enumerations.h
===================================================================
--- include/lldb/lldb-enumerations.h
+++ include/lldb/lldb-enumerations.h
@@ -462,6 +462,13 @@
   eDynamicDontRunTarget = 2
 };
 
+enum StopShowColumn {
+  eStopShowColumnAnsiOrCaret = 0,
+  eStopShowColumnAnsiOnly = 1,
+  eStopShowColumnCaretOnly = 2,
+  eStopShowColumnNone = 3
+};
+
 enum AccessType {
   eAccessNone,
   eAccessPublic,
Index: include/lldb/Core/SourceManager.h
===================================================================
--- include/lldb/Core/SourceManager.h
+++ include/lldb/Core/SourceManager.h
@@ -32,12 +32,14 @@
 
   public:
     File(const FileSpec &file_spec, Target *target);
+    File(const FileSpec &file_spec, lldb::DebuggerSP debugger_sp);
     ~File();
 
     void UpdateIfNeeded();
 
-    size_t DisplaySourceLines(uint32_t line, uint32_t context_before,
-                              uint32_t context_after, Stream *s);
+    size_t DisplaySourceLines(uint32_t line, uint32_t column,
+                              uint32_t context_before, uint32_t context_after,
+                              Stream *s);
     void FindLinesMatchingRegex(RegularExpression &regex, uint32_t start_line,
                                 uint32_t end_line,
                                 std::vector<uint32_t> &match_lines);
@@ -76,6 +78,12 @@
     lldb::DataBufferSP m_data_sp;
     typedef std::vector<uint32_t> LineOffsets;
     LineOffsets m_offsets;
+    lldb::DebuggerWP m_debugger_wp;
+
+  private:
+
+    void CommonInitializer(const FileSpec &file_spec, Target *target);
+
   };
 #endif // SWIG
 
@@ -114,14 +122,16 @@
 
   FileSP GetLastFile() { return m_last_file_sp; }
 
-  size_t DisplaySourceLinesWithLineNumbers(
-      const FileSpec &file, uint32_t line, uint32_t context_before,
-      uint32_t context_after, const char *current_line_cstr, Stream *s,
-      const SymbolContextList *bp_locs = nullptr);
+  size_t
+  DisplaySourceLinesWithLineNumbers(const FileSpec &file, uint32_t line,
+                                    uint32_t column, uint32_t context_before,
+                                    uint32_t context_after,
+                                    const char *current_line_cstr, Stream *s,
+                                    const SymbolContextList *bp_locs = nullptr);
 
   // This variant uses the last file we visited.
   size_t DisplaySourceLinesWithLineNumbersUsingLastFile(
-      uint32_t start_line, uint32_t count, uint32_t curr_line,
+      uint32_t start_line, uint32_t count, uint32_t curr_line, uint32_t column,
       const char *current_line_cstr, Stream *s,
       const SymbolContextList *bp_locs = nullptr);
 
Index: include/lldb/Core/Disassembler.h
===================================================================
--- include/lldb/Core/Disassembler.h
+++ include/lldb/Core/Disassembler.h
@@ -428,15 +428,16 @@
   struct SourceLine {
     FileSpec file;
     uint32_t line;
+    uint32_t column;
 
-    SourceLine() : file(), line(LLDB_INVALID_LINE_NUMBER) {}
+    SourceLine() : file(), line(LLDB_INVALID_LINE_NUMBER), column(0) {}
 
     bool operator==(const SourceLine &rhs) const {
-      return file == rhs.file && line == rhs.line;
+      return file == rhs.file && line == rhs.line && rhs.column == column;
     }
 
     bool operator!=(const SourceLine &rhs) const {
-      return file != rhs.file || line != rhs.line;
+      return file != rhs.file || line != rhs.line || column != rhs.column;
     }
 
     bool IsValid() const { return line != LLDB_INVALID_LINE_NUMBER; }
@@ -486,6 +487,7 @@
     SourceLine sl;
     sl.file = line.file;
     sl.line = line.line;
+    sl.column = line.column;
     return ElideMixedSourceAndDisassemblyLine(exe_ctx, sc, sl);
   };
 
Index: include/lldb/Core/Debugger.h
===================================================================
--- include/lldb/Core/Debugger.h
+++ include/lldb/Core/Debugger.h
@@ -238,6 +238,12 @@
 
   bool SetUseColor(bool use_color);
 
+  lldb::StopShowColumn GetStopShowColumn() const;
+
+  const char *GetStopShowColumnDecoratorPre() const;
+
+  const char *GetStopShowColumnDecoratorPost() const;
+
   uint32_t GetStopSourceLineCount(bool before) const;
 
   StopDisassemblyType GetStopDisassemblyDisplay() const;
Index: include/lldb/API/SBSourceManager.h
===================================================================
--- include/lldb/API/SBSourceManager.h
+++ include/lldb/API/SBSourceManager.h
@@ -30,6 +30,11 @@
       const lldb::SBFileSpec &file, uint32_t line, uint32_t context_before,
       uint32_t context_after, const char *current_line_cstr, lldb::SBStream &s);
 
+  size_t DisplaySourceLinesWithLineNumbersAndColumn(
+      const lldb::SBFileSpec &file, uint32_t line, uint32_t column,
+      uint32_t context_before, uint32_t context_after,
+      const char *current_line_cstr, lldb::SBStream &s);
+
 protected:
   friend class SBCommandInterpreter;
   friend class SBDebugger;
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to