104 std::string_view option,
105 const std::vector<std::string>& mutually_exclusive_group) {
107 [](
const std::string& line) {
108 return trim(line).starts_with(
"\\documentclass");
110 "a unique \\documentclass line");
112 const std::string& line = lines_[index];
113 const std::size_t brace_pos = line.find(
'{');
114 if (brace_pos == std::string::npos) {
116 path_.string() +
"': missing '{'.");
119 const std::size_t bracket_start = line.find(
'[');
122 std::vector<std::string> options;
124 if (bracket_start != std::string::npos && bracket_start < brace_pos) {
125 const std::size_t bracket_end = line.find(
']', bracket_start);
126 if (bracket_end == std::string::npos || bracket_end > brace_pos) {
128 path_.string() +
"': unmatched '['.");
130 prefix = line.substr(0, bracket_start);
131 suffix = line.substr(bracket_end + 1);
133 std::string_view remaining =
134 std::string_view(line).substr(bracket_start + 1,
135 bracket_end - bracket_start - 1);
136 while (!remaining.empty()) {
137 const std::size_t comma = remaining.find(
',');
138 const std::string_view token =
139 trim(remaining.substr(0, comma));
140 if (!token.empty()) options.emplace_back(token);
141 if (comma == std::string_view::npos)
break;
142 remaining = remaining.substr(comma + 1);
145 prefix = line.substr(0, brace_pos);
146 suffix = line.substr(brace_pos);
149 std::vector<std::string> rewritten;
150 rewritten.emplace_back(option);
151 for (
const std::string& existing : options) {
152 const bool excluded =
153 existing == option ||
154 std::find(mutually_exclusive_group.begin(),
155 mutually_exclusive_group.end(),
156 existing) != mutually_exclusive_group.end();
157 if (!excluded) rewritten.push_back(existing);
161 for (std::size_t i = 0; i < rewritten.size(); ++i) {
162 if (i != 0) joined +=
", ";
163 joined += rewritten[i];
166 lines_[index] = prefix +
"[" + joined +
"]" + suffix;
170 const std::filesystem::path temp_path =
171 path_.string() +
".notex.tmp";
173 PLOG_DEBUG <<
"Atomically writing '" << path_.string() <<
"' via '"
174 << temp_path.string() <<
"'.";
176 std::ofstream stream(temp_path, std::ios::binary);
181 for (
const std::string& line : lines_) { stream << line <<
'\n'; }
185 std::filesystem::rename(temp_path, path_, ec);
187 std::error_code ignored;
188 std::filesystem::remove(temp_path, ignored);
190 "': " + ec.message());
192 PLOG_DEBUG <<
"Saved '" << path_.string() <<
"'.";
void insert_lines(std::size_t index, std::vector< std::string > new_lines)
Inserts new_lines, in order, before the current line index.