+2019-08-06  Tom Tromey  <tromey@adacore.com>
+
+       * source-cache.c (extract_lines): No longer a method.
+       Changed type of parameter.  Include final newline.
+       (selftests::extract_lines_test): New function.
+       (_initialize_source_cache): Likewise.
+       * source-cache.h (class source_cache)
+       <extract_lines>: Don't declare.
+
 2019-08-06  Tom Tromey  <tromey@adacore.com>
 
        * breakpoint.c (init_breakpoint_sal): Update.
 
 #include "source.h"
 #include "cli/cli-style.h"
 #include "symtab.h"
+#include "gdbsupport/selftest.h"
 
 #ifdef HAVE_SOURCE_HIGHLIGHT
 /* If Gnulib redirects 'open' and 'close' to its replacements
   return true;
 }
 
-/* See source-cache.h.  */
-
-std::string
-source_cache::extract_lines (const struct source_text &text, int first_line,
-                            int last_line)
+/* A helper function for get_plain_source_lines that extracts the
+   desired source lines from TEXT, putting them into LINES_OUT.  The
+   arguments are as for get_source_lines.  The return value is the
+   desired lines.  */
+static std::string
+extract_lines (const std::string &text, int first_line, int last_line)
 {
   int lineno = 1;
   std::string::size_type pos = 0;
 
   while (pos != std::string::npos && lineno <= last_line)
     {
-      std::string::size_type new_pos = text.contents.find ('\n', pos);
+      std::string::size_type new_pos = text.find ('\n', pos);
 
       if (lineno == first_line)
        first_pos = pos;
          if (first_pos == std::string::npos)
            return {};
          if (pos == std::string::npos)
-           pos = text.contents.size ();
-         return text.contents.substr (first_pos, pos - first_pos);
+           pos = text.size ();
+         else
+           ++pos;
+         return text.substr (first_pos, pos - first_pos);
        }
       ++lineno;
       ++pos;
        {
          if (item.fullname == fullname)
            {
-             *lines = extract_lines (item, first_line, last_line);
+             *lines = extract_lines (item.contents, first_line, last_line);
              return true;
            }
        }
              if (m_source_map.size () > MAX_ENTRIES)
                m_source_map.erase (m_source_map.begin ());
 
-             *lines = extract_lines (m_source_map.back (), first_line,
-                                     last_line);
+             *lines = extract_lines (m_source_map.back ().contents,
+                                     first_line, last_line);
              return true;
            }
        }
 
   return get_plain_source_lines (s, first_line, last_line, lines);
 }
+
+#if GDB_SELF_TEST
+namespace selftests
+{
+static void extract_lines_test ()
+{
+  std::string input_text = "abc\ndef\nghi\njkl\n";
+
+  SELF_CHECK (extract_lines (input_text, 1, 1) == "abc\n");
+  SELF_CHECK (extract_lines (input_text, 2, 1) == "");
+  SELF_CHECK (extract_lines (input_text, 1, 2) == "abc\ndef\n");
+  SELF_CHECK (extract_lines ("abc", 1, 1) == "abc");
+}
+}
+#endif
+
+void
+_initialize_source_cache ()
+{
+#if GDB_SELF_TEST
+  selftests::register_test ("source-cache", selftests::extract_lines_test);
+#endif
+}
 
      are as for get_source_lines.  */
   bool get_plain_source_lines (struct symtab *s, int first_line,
                               int last_line, std::string *lines_out);
-  /* A helper function for get_plain_source_lines that extracts the
-     desired source lines from TEXT, putting them into LINES_OUT.  The
-     arguments are as for get_source_lines.  The return value is the
-     desired lines.  */
-  std::string extract_lines (const struct source_text &text, int first_line,
-                            int last_line);
 
   /* The contents of the cache.  */
   std::vector<source_text> m_source_map;