// catcodes
//
-mode_type asMode(mode_type oldmode, string const & str)
-{
- if (str == "mathmode")
- return MATH_MODE;
- if (str == "textmode" || str == "forcetext")
- return TEXT_MODE;
- return oldmode;
-}
-
-
CatCode catcode(unsigned char c)
{
return theCatcode[c];
}
+bool Parser::isParagraph() const
+{
+ // A new paragraph in TeX ist started
+ // - either by a newline, following any amount of whitespace
+ // characters (including zero), and another newline
+ // - or the token \par
+ if (curr_token().cat() == catNewline &&
+ (curr_token().cs().size() > 1 ||
+ (next_token().cat() == catSpace &&
+ pos_ < tokens_.size() - 1 &&
+ tokens_[pos_ + 1].cat() == catNewline)))
+ return true;
+ if (curr_token().cat() == catEscape && curr_token().cs() == "par")
+ return true;
+ return false;
+}
+
+
void Parser::skip_spaces(bool skip_comments)
{
// We just silently return if we have no more tokens.
// skip_spaces() should be callable at any time,
// the caller must check p::good() anyway.
while (good()) {
- if ( next_token().cat() == catSpace ||
- (next_token().cat() == catNewline && next_token().cs().size() == 1) ||
- next_token().cat() == catComment && next_token().cs().empty())
- get_token();
- else if (skip_comments && next_token().cat() == catComment)
- cerr << " Ignoring comment: " << get_token().asInput();
- else
+ get_token();
+ if (isParagraph()) {
+ putback();
break;
+ }
+ if ( curr_token().cat() == catSpace ||
+ curr_token().cat() == catNewline ||
+ (curr_token().cat() == catComment && curr_token().cs().empty()))
+ continue;
+ if (skip_comments && curr_token().cat() == catComment)
+ cerr << " Ignoring comment: " << curr_token().asInput();
+ else {
+ putback();
+ break;
+ }
}
}
if (!curr_token().cs().empty())
cerr << "Ignoring comment: " << curr_token().asInput();
}
- else if (curr_token().cat() == catSpace || curr_token().cat() == catNewline)
- result += curr_token().cs();
else
- result += c;
+ result += curr_token().asInput();
}
return result;
///
std::string const & cs() const { return cs_; }
- ///
+ /// Returns the catcode of the token
CatCode cat() const { return cat_; }
///
char character() const { return char_; }
- ///
+ /// Returns the token as string
std::string asString() const;
- ///
+ /// Returns the token verbatim
std::string asInput() const;
private:
std::string getArg(char left, char right);
/// getArg('[', ']') including the brackets
std::string getOpt();
- ///
+ /// Returns the character of the current token and increments the token position.
char getChar();
///
void error(std::string const & msg);
- ///
+ /// Parses \p is into tokens
void tokenize(std::istream & is);
///
void push_back(Token const & t);
///
void pop_back();
- ///
+ /// The previous token.
Token const & prev_token() const;
- ///
+ /// The current token.
Token const & curr_token() const;
- ///
+ /// The next token.
Token const & next_token() const;
/// Make the next token current and return that.
Token const & get_token();
- /// skips spaces (and comments if \param skip_comments is true)
+ /// \return whether the current token starts a new paragraph
+ bool isParagraph() const;
+ /// skips spaces (and comments if \p skip_comments is true)
void skip_spaces(bool skip_comments = false);
- /// puts back spaces (and comments if \param skip_comments is true)
+ /// puts back spaces (and comments if \p skip_comments is true)
void unskip_spaces(bool skip_comments = false);
///
void lex(std::string const & s);
}
-// A simple function to translate a latex length to something lyx can
-// understand. Not perfect, but rather best-effort.
+/// A simple function to translate a latex length to something lyx can
+/// understand. Not perfect, but rather best-effort.
bool translate_len(string const & length, string & valstring, string & unit)
{
if (!splitLatexLength(length, valstring, unit))
}
+void eat_whitespace(Parser &, ostream &, Context &, bool);
+
+
void output_command_layout(ostream & os, Parser & p, bool outer,
Context & parent_context,
LyXLayout_ptr newlayout)
context.check_deeper(os);
context.check_layout(os);
if (context.layout->optionalargs > 0) {
- p.skip_spaces();
+ eat_whitespace(p, os, context, false);
if (p.next_token().character() == '[') {
p.get_token(); // eat '['
begin_inset(os, "OptArg\n");
os << "status collapsed\n\n";
parse_text_in_inset(p, os, FLAG_BRACK_LAST, outer, context);
end_inset(os);
+ eat_whitespace(p, os, context, false);
}
}
parse_text_snippet(p, os, FLAG_ITEM, outer, context);
/*!
- * Check wether \param command is a known command. If yes,
+ * Check whether \p command is a known command. If yes,
* handle the command with all arguments.
* \return true if the command was parsed, false otherwise.
*/
string const name = p.getArg('{', '}');
const bool is_starred = suffixIs(name, '*');
string const unstarred_name = rtrim(name, "*");
+ eat_whitespace(p, os, parent_context, false);
active_environments.push_back(name);
- p.skip_spaces();
if (is_math_env(name)) {
parent_context.check_layout(os);
p.skip_spaces();
}
-} // anonymous namespace
+/// parses a comment and outputs it to \p os.
+void parse_comment(Parser & p, ostream & os, Token const & t, Context & context)
+{
+ BOOST_ASSERT(t.cat() == catComment);
+ context.check_layout(os);
+ if (!t.cs().empty()) {
+ handle_comment(os, '%' + t.cs(), context);
+ if (p.next_token().cat() == catNewline) {
+ // A newline after a comment line starts a new
+ // paragraph
+ if(!context.atParagraphStart()) {
+ // Only start a new paragraph if not already
+ // done (we might get called recursively)
+ context.new_paragraph(os);
+ }
+ eat_whitespace(p, os, context, true);
+ }
+ } else {
+ // "%\n" combination
+ p.skip_spaces();
+ }
+}
+/*!
+ * Reads spaces and comments until the first non-space, non-comment token.
+ * New paragraphs (double newlines or \\par) are handled like simple spaces
+ * if \p eatParagraph is true.
+ * Spaces are skipped, but comments are written to \p os.
+ */
+void eat_whitespace(Parser & p, ostream & os, Context & context,
+ bool eatParagraph)
+{
+ while (p.good()) {
+ Token const & t = p.get_token();
+ if (t.cat() == catComment)
+ parse_comment(p, os, t, context);
+ else if ((! eatParagraph && p.isParagraph()) ||
+ (t.cat() != catSpace && t.cat() != catNewline)) {
+ p.putback();
+ return;
+ }
+ }
+}
+
+} // anonymous namespace
void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
skip_braces(p);
}
- else if (t.cat() == catSpace || (t.cat() == catNewline && t.cs().size() == 1))
+ else if (t.cat() == catSpace || (t.cat() == catNewline && ! p.isParagraph()))
check_space(p, os, context);
else if (t.cat() == catLetter ||
os << t.character();
}
- else if (t.cat() == catNewline || (t.cat() == catEscape && t.cs() == "par")) {
- p.skip_spaces();
+ else if (p.isParagraph()) {
context.new_paragraph(os);
+ eat_whitespace(p, os, context, true);
}
else if (t.cat() == catActive) {
handle_ert(os, "}", context);
}
- else if (t.cat() == catComment) {
- context.check_layout(os);
- if (!t.cs().empty()) {
- handle_comment(os, '%' + t.cs(), context);
- if (p.next_token().cat() == catNewline) {
- // A newline after a comment line starts a new paragraph
- context.new_paragraph(os);
- p.skip_spaces();
- }
- } else {
- // "%\n" combination
- p.skip_spaces();
- }
- }
+ else if (t.cat() == catComment)
+ parse_comment(p, os, t, context);
//
// control sequences
} else if (!s.empty()) {
// The space is needed to separate the item from the rest of the sentence.
os << s << ' ';
- p.skip_spaces();
+ eat_whitespace(p, os, context, false);
}
}
}
}
else if (t.cs() == "def") {
- p.skip_spaces();
context.check_layout(os);
+ eat_whitespace(p, os, context, false);
string name = p.get_token().cs();
while (p.next_token().cat() != catBegin)
name += p.get_token().asString();
}
// TODO: Handle the unknown settings better.
// Warn about invalid options.
- // Check wether some option was given twice.
+ // Check whether some option was given twice.
end_inset(os);
}
else if (t.cs() == "hfill") {
context.check_layout(os);
- os << "\n\\hfill\n";
+ os << "\n\\hfill \n";
skip_braces(p);
p.skip_spaces();
}
begin_inset(os, "Quotes ");
os << known_coded_quotes[where - known_quotes];
end_inset(os);
+ // LyX adds {} after the quote, so we have to eat
+ // spaces here if there are any before a possible
+ // {} pair.
+ eat_whitespace(p, os, context, false);
skip_braces(p);
}
char const ** where = is_known(t.cs(), known_sizes);
context.check_layout(os);
os << "\n\\size " << known_coded_sizes[where - known_sizes] << "\n";
- p.skip_spaces();
+ eat_whitespace(p, os, context, false);
}
else if (t.cs() == "LyX" || t.cs() == "TeX"
begin_inset(os, "VSpace ");
os << t.cs();
end_inset(os);
+ skip_braces(p);
}
else if (t.cs() == "vspace") {