Save plain text in the source cache
[binutils-gdb.git] / gdb / source-cache.c
1 /* Cache of styled source file text
2 Copyright (C) 2018-2019 Free Software Foundation, Inc.
3
4 This file is part of GDB.
5
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.
10
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.
15
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/>. */
18
19 #include "defs.h"
20 #include "source-cache.h"
21 #include "gdbsupport/scoped_fd.h"
22 #include "source.h"
23 #include "cli/cli-style.h"
24 #include "symtab.h"
25 #include "gdbsupport/selftest.h"
26
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. */
32 #undef open
33 #undef close
34 #include <sstream>
35 #include <srchilite/sourcehighlight.h>
36 #include <srchilite/langmap.h>
37 #endif
38
39 /* The number of source files we'll cache. */
40
41 #define MAX_ENTRIES 5
42
43 /* See source-cache.h. */
44
45 source_cache g_source_cache;
46
47 /* See source-cache.h. */
48
49 bool
50 source_cache::get_plain_source_lines (struct symtab *s, std::string *lines)
51 {
52 scoped_fd desc (open_source_file_with_line_charpos (s));
53 if (desc.get () < 0)
54 return false;
55
56 struct stat st;
57
58 if (fstat (desc.get (), &st) < 0)
59 perror_with_name (symtab_to_filename_for_display (s));
60
61 /* We could cache this in line_charpos... */
62 lines->resize (st.st_size);
63 if (myread (desc.get (), &(*lines)[0], lines->size ()) < 0)
64 perror_with_name (symtab_to_filename_for_display (s));
65
66 return true;
67 }
68
69 /* A helper function for get_plain_source_lines that extracts the
70 desired source lines from TEXT, putting them into LINES_OUT. The
71 arguments are as for get_source_lines. The return value is the
72 desired lines. */
73 static std::string
74 extract_lines (const std::string &text, int first_line, int last_line)
75 {
76 int lineno = 1;
77 std::string::size_type pos = 0;
78 std::string::size_type first_pos = std::string::npos;
79
80 while (pos != std::string::npos && lineno <= last_line)
81 {
82 std::string::size_type new_pos = text.find ('\n', pos);
83
84 if (lineno == first_line)
85 first_pos = pos;
86
87 pos = new_pos;
88 if (lineno == last_line || pos == std::string::npos)
89 {
90 if (first_pos == std::string::npos)
91 return {};
92 if (pos == std::string::npos)
93 pos = text.size ();
94 else
95 ++pos;
96 return text.substr (first_pos, pos - first_pos);
97 }
98 ++lineno;
99 ++pos;
100 }
101
102 return {};
103 }
104
105 #ifdef HAVE_SOURCE_HIGHLIGHT
106
107 /* Return the Source Highlight language name, given a gdb language
108 LANG. Returns NULL if the language is not known. */
109
110 static const char *
111 get_language_name (enum language lang)
112 {
113 switch (lang)
114 {
115 case language_c:
116 case language_objc:
117 return "c.lang";
118
119 case language_cplus:
120 return "cpp.lang";
121
122 case language_d:
123 return "d.lang";
124
125 case language_go:
126 return "go.lang";
127
128 case language_fortran:
129 return "fortran.lang";
130
131 case language_m2:
132 /* Not handled by Source Highlight. */
133 break;
134
135 case language_asm:
136 return "asm.lang";
137
138 case language_pascal:
139 return "pascal.lang";
140
141 case language_opencl:
142 /* Not handled by Source Highlight. */
143 break;
144
145 case language_rust:
146 /* Not handled by Source Highlight. */
147 break;
148
149 case language_ada:
150 return "ada.lang";
151
152 default:
153 break;
154 }
155
156 return nullptr;
157 }
158
159 #endif /* HAVE_SOURCE_HIGHLIGHT */
160
161 /* See source-cache.h. */
162
163 bool
164 source_cache::get_source_lines (struct symtab *s, int first_line,
165 int last_line, std::string *lines)
166 {
167 if (first_line < 1 || last_line < 1 || first_line > last_line)
168 return false;
169
170 std::string fullname = symtab_to_fullname (s);
171
172 for (const auto &item : m_source_map)
173 {
174 if (item.fullname == fullname)
175 {
176 *lines = extract_lines (item.contents, first_line, last_line);
177 return true;
178 }
179 }
180
181 std::string contents;
182 if (!get_plain_source_lines (s, &contents))
183 return false;
184
185 #ifdef HAVE_SOURCE_HIGHLIGHT
186 if (source_styling && gdb_stdout->can_emit_style_escape ())
187 {
188 const char *lang_name = get_language_name (SYMTAB_LANGUAGE (s));
189 if (lang_name != nullptr)
190 {
191 /* The global source highlight object, or null if one was
192 never constructed. This is stored here rather than in
193 the class so that we don't need to include anything or do
194 conditional compilation in source-cache.h. */
195 static srchilite::SourceHighlight *highlighter;
196
197 if (highlighter == nullptr)
198 {
199 highlighter = new srchilite::SourceHighlight ("esc.outlang");
200 highlighter->setStyleFile ("esc.style");
201 }
202
203 std::istringstream input (contents);
204 std::ostringstream output;
205 highlighter->highlight (input, output, lang_name, fullname);
206
207 contents = output.str ();
208 }
209 }
210 #endif /* HAVE_SOURCE_HIGHLIGHT */
211
212 source_text result = { std::move (fullname), std::move (contents) };
213 m_source_map.push_back (std::move (result));
214
215 if (m_source_map.size () > MAX_ENTRIES)
216 m_source_map.erase (m_source_map.begin ());
217
218 *lines = extract_lines (m_source_map.back ().contents,
219 first_line, last_line);
220 return true;
221 }
222
223 #if GDB_SELF_TEST
224 namespace selftests
225 {
226 static void extract_lines_test ()
227 {
228 std::string input_text = "abc\ndef\nghi\njkl\n";
229
230 SELF_CHECK (extract_lines (input_text, 1, 1) == "abc\n");
231 SELF_CHECK (extract_lines (input_text, 2, 1) == "");
232 SELF_CHECK (extract_lines (input_text, 1, 2) == "abc\ndef\n");
233 SELF_CHECK (extract_lines ("abc", 1, 1) == "abc");
234 }
235 }
236 #endif
237
238 void
239 _initialize_source_cache ()
240 {
241 #if GDB_SELF_TEST
242 selftests::register_test ("source-cache", selftests::extract_lines_test);
243 #endif
244 }