1 /* Cache of styled source file text
2 Copyright (C) 2018-2019 Free Software Foundation, Inc.
4 This file is part of GDB.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20 #include "source-cache.h"
21 #include "gdbsupport/scoped_fd.h"
23 #include "cli/cli-style.h"
25 #include "gdbsupport/selftest.h"
27 #ifdef HAVE_SOURCE_HIGHLIGHT
28 /* If Gnulib redirects 'open' and 'close' to its replacements
29 'rpl_open' and 'rpl_close' via cpp macros, including <fstream>
30 below with those macros in effect will cause unresolved externals
31 when GDB is linked. Happens, e.g., in the MinGW build. */
36 #include <srchilite/sourcehighlight.h>
37 #include <srchilite/langmap.h>
40 /* The number of source files we'll cache. */
44 /* See source-cache.h. */
46 source_cache g_source_cache
;
48 /* See source-cache.h. */
51 source_cache::get_plain_source_lines (struct symtab
*s
, int first_line
,
52 int last_line
, std::string
*lines
)
54 scoped_fd
desc (open_source_file_with_line_charpos (s
));
58 if (first_line
< 1 || first_line
> s
->nlines
|| last_line
< 1)
61 if (lseek (desc
.get (), s
->line_charpos
[first_line
- 1], SEEK_SET
) < 0)
62 perror_with_name (symtab_to_filename_for_display (s
));
65 if (last_line
>= s
->nlines
)
69 if (fstat (desc
.get (), &st
) < 0)
70 perror_with_name (symtab_to_filename_for_display (s
));
71 /* We could cache this in line_charpos... */
72 last_charpos
= st
.st_size
;
75 last_charpos
= s
->line_charpos
[last_line
];
77 lines
->resize (last_charpos
- s
->line_charpos
[first_line
- 1]);
78 if (myread (desc
.get (), &(*lines
)[0], lines
->size ()) < 0)
79 perror_with_name (symtab_to_filename_for_display (s
));
84 /* A helper function for get_plain_source_lines that extracts the
85 desired source lines from TEXT, putting them into LINES_OUT. The
86 arguments are as for get_source_lines. The return value is the
89 extract_lines (const std::string
&text
, int first_line
, int last_line
)
92 std::string::size_type pos
= 0;
93 std::string::size_type first_pos
= std::string::npos
;
95 while (pos
!= std::string::npos
&& lineno
<= last_line
)
97 std::string::size_type new_pos
= text
.find ('\n', pos
);
99 if (lineno
== first_line
)
103 if (lineno
== last_line
|| pos
== std::string::npos
)
105 if (first_pos
== std::string::npos
)
107 if (pos
== std::string::npos
)
111 return text
.substr (first_pos
, pos
- first_pos
);
120 #ifdef HAVE_SOURCE_HIGHLIGHT
122 /* Return the Source Highlight language name, given a gdb language
123 LANG. Returns NULL if the language is not known. */
126 get_language_name (enum language lang
)
143 case language_fortran
:
144 return "fortran.lang";
147 /* Not handled by Source Highlight. */
153 case language_pascal
:
154 return "pascal.lang";
156 case language_opencl
:
157 /* Not handled by Source Highlight. */
161 /* Not handled by Source Highlight. */
174 #endif /* HAVE_SOURCE_HIGHLIGHT */
176 /* See source-cache.h. */
179 source_cache::get_source_lines (struct symtab
*s
, int first_line
,
180 int last_line
, std::string
*lines
)
182 if (first_line
< 1 || last_line
< 1 || first_line
> last_line
)
185 #ifdef HAVE_SOURCE_HIGHLIGHT
186 if (source_styling
&& gdb_stdout
->can_emit_style_escape ())
188 const char *fullname
= symtab_to_fullname (s
);
190 for (const auto &item
: m_source_map
)
192 if (item
.fullname
== fullname
)
194 *lines
= extract_lines (item
.contents
, first_line
, last_line
);
199 const char *lang_name
= get_language_name (SYMTAB_LANGUAGE (s
));
200 if (lang_name
!= nullptr)
202 std::ifstream
input (fullname
);
203 if (input
.is_open ())
205 /* The global source highlight object, or null if one
206 was never constructed. This is stored here rather
207 than in the class so that we don't need to include
208 anything or do conditional compilation in
210 static srchilite::SourceHighlight
*highlighter
;
212 if (s
->line_charpos
== 0)
214 scoped_fd
desc (open_source_file_with_line_charpos (s
));
218 /* FULLNAME points to a value owned by the symtab
219 (symtab::fullname). Calling open_source_file reallocates
220 that value, so we must refresh FULLNAME to avoid a
222 fullname
= symtab_to_fullname (s
);
225 if (highlighter
== nullptr)
227 highlighter
= new srchilite::SourceHighlight ("esc.outlang");
228 highlighter
->setStyleFile ("esc.style");
231 std::ostringstream output
;
232 highlighter
->highlight (input
, output
, lang_name
, fullname
);
234 source_text result
= { fullname
, output
.str () };
235 m_source_map
.push_back (std::move (result
));
237 if (m_source_map
.size () > MAX_ENTRIES
)
238 m_source_map
.erase (m_source_map
.begin ());
240 *lines
= extract_lines (m_source_map
.back ().contents
,
241 first_line
, last_line
);
246 #endif /* HAVE_SOURCE_HIGHLIGHT */
248 return get_plain_source_lines (s
, first_line
, last_line
, lines
);
254 static void extract_lines_test ()
256 std::string input_text
= "abc\ndef\nghi\njkl\n";
258 SELF_CHECK (extract_lines (input_text
, 1, 1) == "abc\n");
259 SELF_CHECK (extract_lines (input_text
, 2, 1) == "");
260 SELF_CHECK (extract_lines (input_text
, 1, 2) == "abc\ndef\n");
261 SELF_CHECK (extract_lines ("abc", 1, 1) == "abc");
267 _initialize_source_cache ()
270 selftests::register_test ("source-cache", selftests::extract_lines_test
);