Introduce SimulatorBuilder
[gromacs.git] / src / gromacs / onlinehelp / helpformat.cpp
blob4d9ce48aebc321685a328b869def2d69c7f3e169
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013,2014,2016,2017,2019, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
35 /*! \internal \file
36 * \brief
37 * Implements functions in helpformat.h.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_onlinehelp
42 #include "gmxpre.h"
44 #include "helpformat.h"
46 #include <algorithm>
47 #include <string>
48 #include <vector>
50 #include "gromacs/onlinehelp/helpwritercontext.h"
51 #include "gromacs/utility/gmxassert.h"
52 #include "gromacs/utility/stringutil.h"
54 namespace gmx
57 /********************************************************************
58 * TextTableFormatter::Impl
61 /*! \internal \brief
62 * Private implementation class for TextTableFormatter.
64 * \ingroup module_onlinehelp
66 class TextTableFormatter::Impl
68 public:
69 /*! \internal \brief
70 * Manages a single column for TextTableFormatter.
72 * \ingroup module_onlinehelp
74 struct ColumnData
76 //! Initializes a text table column with given values.
77 ColumnData(const char *title, int width, bool bWrap)
78 : title_(title != nullptr ? title : ""),
79 width_(width), bWrap_(bWrap), firstLine_(0),
80 nextLineIndex_(0), nextLineOffset_(0)
82 GMX_ASSERT(width >= 0, "Negative width not possible");
83 GMX_ASSERT(title_.length() <= static_cast<size_t>(width),
84 "Title too long for column width");
87 //! Returns the title of the column.
88 const std::string &title() const { return title_; }
89 //! Returns the width of the column.
90 int width() const { return width_; }
91 /*! \brief
92 * Returns the first line offset for the current row.
94 * Note that the return value may be outside the printed lines if
95 * there is no text.
97 int firstLine() const { return firstLine_; }
99 /*! \brief
100 * Resets the formatting state.
102 * After this call, textForNextLine() and hasLinesRemaining() can
103 * be used to format the lines for the column.
105 void startFormatting()
107 nextLineIndex_ = (!lines_.empty() ? -firstLine_ : 0);
108 nextLineOffset_ = 0;
110 //! Whether there are lines remaining for textForNextLine().
111 bool hasLinesRemaining() const
113 return nextLineIndex_ < ssize(lines_);
115 /*! \brief
116 * Returns the text for the next line.
118 * \param[in] columnWidth Width to wrap the text to.
119 * \returns Text for the next line, or empty string if there is
120 * no text for this column.
122 std::string textForNextLine(int columnWidth)
124 if (nextLineIndex_ < 0 || !hasLinesRemaining())
126 ++nextLineIndex_;
127 return std::string();
129 if (bWrap_)
131 TextLineWrapperSettings settings;
132 settings.setLineLength(columnWidth);
133 TextLineWrapper wrapper(settings);
134 const std::string &currentLine = lines_[nextLineIndex_];
135 const size_t prevOffset = nextLineOffset_;
136 const size_t nextOffset
137 = wrapper.findNextLine(currentLine, prevOffset);
138 if (nextOffset >= currentLine.size())
140 ++nextLineIndex_;
141 nextLineOffset_ = 0;
143 else
145 nextLineOffset_ = nextOffset;
147 return wrapper.formatLine(currentLine, prevOffset, nextOffset);
149 else
151 return lines_[nextLineIndex_++];
155 //! Statit data: title of the column.
156 std::string title_;
157 //! Static data: width of the column.
158 int width_;
159 //! Static data: whether to automatically wrap input text.
160 bool bWrap_;
161 //! First line offset for the current row.
162 int firstLine_;
163 //! Text lines for the current row.
164 std::vector<std::string> lines_;
165 //! Formatting state: index in `lines_` for the next line.
166 int nextLineIndex_;
167 //! Formatting state: offset within line `nextLineIndex_` for the next line.
168 size_t nextLineOffset_;
171 //! Container type for column data.
172 typedef std::vector<ColumnData> ColumnList;
174 //! Initializes data for an empty formatter.
175 Impl();
177 /*! \brief
178 * Convenience method for checked access to data for a column.
180 * \param[in] index Zero-based column index.
181 * \returns \c columns_[index]
183 ColumnData &columnData(int index)
185 GMX_ASSERT(index >= 0 && index < ssize(columns_),
186 "Invalid column index");
187 return columns_[index];
189 //! \copydoc columnData()
190 const ColumnData &columnData(int index) const
192 return const_cast<Impl *>(this)->columnData(index);
195 //! Container for column data.
196 ColumnList columns_;
197 //! Indentation before the first column.
198 int firstColumnIndent_;
199 //! Indentation before the last column if folded.
200 int foldLastColumnToNextLineIndent_;
201 //! If true, no output has yet been produced.
202 bool bFirstRow_;
203 //! If true, a header will be printed before the first row.
204 bool bPrintHeader_;
207 TextTableFormatter::Impl::Impl()
208 : firstColumnIndent_(0), foldLastColumnToNextLineIndent_(-1),
209 bFirstRow_(true), bPrintHeader_(false)
213 /********************************************************************
214 * TextTableFormatter
217 TextTableFormatter::TextTableFormatter()
218 : impl_(new Impl)
222 TextTableFormatter::~TextTableFormatter()
226 void TextTableFormatter::addColumn(const char *title, int width, bool bWrap)
228 if (title != nullptr && title[0] != '\0')
230 impl_->bPrintHeader_ = true;
232 impl_->columns_.emplace_back(title, width, bWrap);
235 void TextTableFormatter::setFirstColumnIndent(int indent)
237 GMX_RELEASE_ASSERT(indent >= 0, "Negative indentation not allowed");
238 impl_->firstColumnIndent_ = indent;
241 void TextTableFormatter::setFoldLastColumnToNextLine(int indent)
243 impl_->foldLastColumnToNextLineIndent_ = indent;
246 bool TextTableFormatter::didOutput() const
248 return !impl_->bFirstRow_;
251 void TextTableFormatter::clear()
253 Impl::ColumnList::iterator i;
254 for (i = impl_->columns_.begin(); i != impl_->columns_.end(); ++i)
256 i->firstLine_ = 0;
257 i->lines_.clear();
261 void TextTableFormatter::addColumnLine(int index, const std::string &text)
263 Impl::ColumnData &column = impl_->columnData(index);
264 TextLineWrapper wrapper;
265 std::vector<std::string> lines(wrapper.wrapToVector(text));
266 column.lines_.insert(column.lines_.end(), lines.begin(), lines.end());
269 void TextTableFormatter::addColumnHelpTextBlock(
270 int index, const HelpWriterContext &context, const std::string &text)
272 Impl::ColumnData &column = impl_->columnData(index);
273 TextLineWrapperSettings settings;
274 // TODO: If in the future, there is actually a coupling between the markup
275 // and the wrapping, this must be postponed into formatRow(), where we do
276 // the actual line wrapping.
277 std::vector<std::string> lines(
278 context.substituteMarkupAndWrapToVector(settings, text));
279 column.lines_.insert(column.lines_.end(), lines.begin(), lines.end());
282 void TextTableFormatter::setColumnFirstLineOffset(int index, int firstLine)
284 GMX_ASSERT(firstLine >= 0, "Invalid first line");
285 Impl::ColumnData &column = impl_->columnData(index);
286 column.firstLine_ = firstLine;
289 std::string TextTableFormatter::formatRow()
291 std::string result;
292 Impl::ColumnList::iterator column;
293 // Print a header if this is the first line.
294 if (impl_->bPrintHeader_ && impl_->bFirstRow_)
296 size_t totalWidth = 0;
297 result.append(impl_->firstColumnIndent_, ' ');
298 for (column = impl_->columns_.begin();
299 column != impl_->columns_.end();
300 ++column)
302 std::string title(column->title());
303 if (column != impl_->columns_.end() - 1)
305 title.resize(column->width() + 1, ' ');
306 totalWidth += title.length();
308 else
310 totalWidth += std::min(column->width(),
311 static_cast<int>(title.length() + 13));
313 result.append(title);
315 result.append("\n");
316 result.append(impl_->firstColumnIndent_, ' ');
317 result.append(totalWidth, '-');
318 result.append("\n");
321 // Format all the lines, one column at a time.
322 std::vector<std::string> lines;
323 std::vector<std::string> columnLines;
324 int currentWidth = 0;
325 bool bFoldLastColumn = false;
326 for (column = impl_->columns_.begin();
327 column != impl_->columns_.end();
328 ++column)
330 // Format the column into columnLines.
331 column->startFormatting();
332 columnLines.clear();
333 columnLines.reserve(lines.size());
334 for (size_t line = 0; column->hasLinesRemaining(); ++line)
336 int columnWidth = column->width();
337 if (line < lines.size())
339 const int overflow = static_cast<int>(lines[line].length()) - currentWidth;
340 if (overflow > 0)
342 if (overflow > columnWidth && column->bWrap_)
344 columnLines.emplace_back();
345 continue;
347 columnWidth -= overflow;
350 columnLines.push_back(column->textForNextLine(columnWidth));
352 if (column == impl_->columns_.end() - 1
353 && impl_->foldLastColumnToNextLineIndent_ >= 0
354 && columnLines.size() >= lines.size() + column->lines_.size())
356 bFoldLastColumn = true;
357 currentWidth += column->width();
358 break;
360 // Add columnLines into lines.
361 if (lines.size() < columnLines.size())
363 lines.resize(columnLines.size());
365 for (size_t line = 0; line < columnLines.size(); ++line)
367 if (column != impl_->columns_.begin() && !columnLines[line].empty())
369 lines[line].append(" ");
370 if (static_cast<int>(lines[line].length()) < currentWidth)
372 lines[line].resize(currentWidth, ' ');
375 lines[line].append(columnLines[line]);
377 currentWidth += column->width() + 1;
380 // Construct the result by concatenating all the lines.
381 std::vector<std::string>::const_iterator line;
382 for (line = lines.begin(); line != lines.end(); ++line)
384 result.append(impl_->firstColumnIndent_, ' ');
385 result.append(*line);
386 result.append("\n");
389 if (bFoldLastColumn)
391 Impl::ColumnList::reference lastColumn = impl_->columns_.back();
392 const int totalIndent
393 = impl_->firstColumnIndent_ + impl_->foldLastColumnToNextLineIndent_;
394 lastColumn.startFormatting();
395 currentWidth -= impl_->foldLastColumnToNextLineIndent_;
396 while (lastColumn.hasLinesRemaining())
398 result.append(totalIndent, ' ');
399 result.append(lastColumn.textForNextLine(currentWidth));
400 result.append("\n");
404 impl_->bFirstRow_ = false;
405 clear();
406 return result;
409 } // namespace gmx