diff --git a/doc/1_6.qbk b/doc/1_6.qbk index 653508d..370b198 100644 --- a/doc/1_6.qbk +++ b/doc/1_6.qbk @@ -330,4 +330,13 @@ and lists. I'm not sure if it would be a good idea. [endsect] +[section:callouts Callouts in code block] + +Currently callouts can only be used in code snippets. 1.7 add +support in normal code blocks. The same syntax is used as in +code snippets, the callout descriptions appear immediately +after the code block. + +[endsect] + [endsect] [/ Quickbok 1.7] diff --git a/src/actions.cpp b/src/actions.cpp index 63d443a..4332ed4 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -536,6 +536,83 @@ namespace quickbook out << ""; } + namespace + { + bool parse_template(value const&, quickbook::actions& actions); + } + + void actions::start_callouts() + { + ++callout_depth; + } + + std::string actions::add_callout(value v) + { + std::string callout_id1 = ids.add_id("c", id_category::numbered); + std::string callout_id2 = ids.add_id("c", id_category::numbered); + + callouts.insert(encoded_value(callout_id1)); + callouts.insert(encoded_value(callout_id2)); + callouts.insert(v); + + std::string code; + code += ""; + + return code; + } + + std::string actions::end_callouts() + { + assert(callout_depth > 0); + std::string block; + + --callout_depth; + if (callout_depth > 0) return block; + + value_consumer c = callouts.release(); + if (!c.check()) return block; + + block += ""; + while (c.check()) + { + std::string callout_id1 = c.consume().get_encoded(); + std::string callout_id2 = c.consume().get_encoded(); + value callout_body = c.consume(); + + std::string callout_value; + + { + template_state state(*this); + ++template_depth; + + bool r = parse_template(callout_body, *this); + + if(!r) + { + detail::outerr(callout_body.get_file(), callout_body.get_position()) + << "Expanding callout." << std::endl + << "------------------begin------------------" << std::endl + << detail::utf8(callout_body.get_quickbook()) + << std::endl + << "------------------end--------------------" << std::endl + ; + ++error_count; + } + + out.swap(callout_value); + } + + block += ""; + block += callout_value; + block += ""; + } + block += ""; + + return block; + } + void explicit_list_action(quickbook::actions& actions, value list) { write_anchors(actions, actions.out); @@ -645,6 +722,8 @@ namespace quickbook if (f->source.empty()) return; // Nothing left to do here. The program is empty. + if (qbk_version_n >= 107u) actions.start_callouts(); + parse_iterator first_(f->source.begin()); parse_iterator last_(f->source.end()); @@ -653,7 +732,7 @@ namespace quickbook // print the code with syntax coloring std::string str = syntax_highlight(first_, last_, actions, - source_mode); + source_mode, block); boost::swap(actions.current_file, saved_file); @@ -665,12 +744,14 @@ namespace quickbook output << ""; output << str; output << "\n"; + + if (qbk_version_n >= 107u) output << actions.end_callouts(); } else { parse_iterator first_(code_value.begin()); parse_iterator last_(code_value.end()); std::string str = syntax_highlight(first_, last_, actions, - source_mode); + source_mode, block); actions.phrase << ""; actions.phrase << str; @@ -1118,9 +1199,9 @@ namespace quickbook parse_iterator last(source.end()); bool r = cl::parse(first, last, - content.get_tag() == template_tags::block ? - actions.grammar().block : - actions.grammar().inline_phrase + content.get_tag() == template_tags::phrase ? + actions.grammar().inline_phrase : + actions.grammar().block ).full; boost::swap(actions.current_file, saved_current_file); @@ -1134,12 +1215,14 @@ namespace quickbook std::vector const& args, string_iterator first) { + bool is_block = symbol->content.get_tag() != template_tags::phrase; + // If this template contains already encoded text, then just // write it out, without going through any of the rigamarole. if (symbol->content.is_encoded()) { - if (symbol->content.get_tag() == template_tags::block) + if (is_block) { actions.paragraph(); actions.out << symbol->content.get_encoded(); @@ -1200,7 +1283,7 @@ namespace quickbook { detail::outerr(actions.current_file, first) << "Expanding " - << (symbol->content.get_tag() == template_tags::block ? "block" : "phrase") + << (is_block ? "block" : "phrase") << " template: " << detail::utf8(symbol->identifier) << std::endl << std::endl << "------------------begin------------------" << std::endl @@ -1225,7 +1308,7 @@ namespace quickbook actions.phrase.swap(phrase); } - if(symbol->content.get_tag() == template_tags::block || !block.empty()) { + if(is_block || !block.empty()) { actions.paragraph(); // For paragraphs before the template call. actions.out << block; actions.phrase << phrase; @@ -1240,81 +1323,19 @@ namespace quickbook template_symbol const* symbol, string_iterator first) { - value_consumer values = symbol->content; - value content = values.consume(template_tags::block); - value callouts = values.consume(); - values.finish(); - - std::vector callout_ids; + assert(symbol->params.size() == 0); std::vector args; - unsigned int size = symbol->params.size(); - std::string callout_base("c"); - - for(unsigned int i = 0; i < size; ++i) - { - std::string callout_id1 = actions.ids.add_id(callout_base, id_category::numbered); - std::string callout_id2 = actions.ids.add_id(callout_base, id_category::numbered); - - std::string code; - code += ""; - - args.push_back(encoded_value(code, template_tags::phrase)); - callout_ids.push_back(callout_id1); - callout_ids.push_back(callout_id2); - } // Create a fake symbol for call_template template_symbol t( symbol->identifier, symbol->params, - content, + symbol->content, symbol->lexical_parent); + + actions.start_callouts(); call_template(actions, &t, args, first); - - std::string block; - - if(!callouts.empty()) - { - block += ""; - int i = 0; - BOOST_FOREACH(value c, callouts) - { - std::string callout_id1 = callout_ids[i++]; - std::string callout_id2 = callout_ids[i++]; - - std::string callout_value; - { - template_state state(actions); - ++actions.template_depth; - - bool r = parse_template(c, actions); - - if(!r) - { - detail::outerr(c.get_file(), c.get_position()) - << "Expanding callout." << std::endl - << "------------------begin------------------" << std::endl - << detail::utf8(c.get_quickbook()) - << std::endl - << "------------------end--------------------" << std::endl - ; - ++actions.error_count; - return; - } - - actions.out.swap(callout_value); - } - - block += ""; - block += callout_value; - block += ""; - } - block += ""; - } - - actions.out << block; + actions.out << actions.end_callouts(); } void do_template_action(quickbook::actions& actions, value template_list, diff --git a/src/actions.hpp b/src/actions.hpp index 409bbb8..3fce5ce 100644 --- a/src/actions.hpp +++ b/src/actions.hpp @@ -62,7 +62,8 @@ namespace quickbook std::string syntax_highlight( parse_iterator first, parse_iterator last, actions& escape_actions, - std::string const& source_mode); + std::string const& source_mode, + bool is_block); struct xinclude_path { xinclude_path(fs::path const& path, std::string const& uri) : diff --git a/src/actions_class.cpp b/src/actions_class.cpp index ac54c20..84bb99f 100644 --- a/src/actions_class.cpp +++ b/src/actions_class.cpp @@ -32,6 +32,8 @@ namespace quickbook , warned_about_breaks(false) , conditional(true) , ids(ids) + , callouts() + , callout_depth(0) , imported(false) , macro() diff --git a/src/actions_class.hpp b/src/actions_class.hpp index 1ad3843..75498ec 100644 --- a/src/actions_class.hpp +++ b/src/actions_class.hpp @@ -46,6 +46,8 @@ namespace quickbook bool warned_about_breaks; bool conditional; id_manager& ids; + value_builder callouts; // callouts are global as + int callout_depth; // they don't nest. // state saved for files and templates. bool imported; @@ -77,6 +79,10 @@ namespace quickbook void start_list_item(); void end_list_item(); + void start_callouts(); + std::string add_callout(value); + std::string end_callouts(); + scoped_parser to_value; scoped_parser diff --git a/src/code_snippet.cpp b/src/code_snippet.cpp index 8d9484c..b7af1b7 100644 --- a/src/code_snippet.cpp +++ b/src/code_snippet.cpp @@ -31,13 +31,13 @@ namespace quickbook char const* source_type) : last_code_pos(source_file->source.begin()) , in_code(false) - , callout_id(0) , snippet_stack() , storage(storage) , source_file(source_file) , source_type(source_type) , error_count(0) { + source_file->is_code_snippets = true; content.start(source_file); } @@ -48,7 +48,6 @@ namespace quickbook void start_snippet_impl(std::string const&, string_iterator); void end_snippet(string_iterator first, string_iterator last); void end_snippet_impl(string_iterator); - void callout(string_iterator first, string_iterator last); void end_file(string_iterator, string_iterator); void append_code(string_iterator first, string_iterator last); @@ -56,26 +55,22 @@ namespace quickbook struct snippet_data { - snippet_data(std::string const& id, int callout_base_id) + snippet_data(std::string const& id) : id(id) - , callout_base_id(callout_base_id) , start_code(false) {} std::string id; - int callout_base_id; bool start_code; std::string::const_iterator source_pos; mapped_file_builder::pos start_pos; - value_builder callouts; boost::shared_ptr next; }; - void push_snippet_data(std::string const& id, int callout_base_id, + void push_snippet_data(std::string const& id, std::string::const_iterator pos) { - boost::shared_ptr new_snippet( - new snippet_data(id, callout_base_id)); + boost::shared_ptr new_snippet(new snippet_data(id)); new_snippet->next = snippet_stack; snippet_stack = new_snippet; snippet_stack->start_code = in_code; @@ -95,7 +90,6 @@ namespace quickbook std::string::const_iterator mark_begin, mark_end; std::string::const_iterator last_code_pos; bool in_code; - int callout_id; boost::shared_ptr snippet_stack; std::vector& storage; file_ptr source_file; @@ -240,8 +234,6 @@ namespace quickbook | escaped_comment [boost::bind(&actions_type::escaped_comment, &actions, _1, _2)] | ignore [boost::bind(&actions_type::append_code, &actions, _1, _2)] | pass_thru_comment [boost::bind(&actions_type::pass_thru, &actions, _1, _2)] - | line_callout [boost::bind(&actions_type::callout, &actions, _1, _2)] - | inline_callout [boost::bind(&actions_type::callout, &actions, _1, _2)] | cl::anychar_p ; @@ -287,23 +279,6 @@ namespace quickbook "/*[*/" ; - inline_callout - = cl::confix_p( - "/*<" >> *cl::space_p, - (*cl::anychar_p) [boost::bind(&actions_type::mark, &actions, _1, _2)], - ">*/" - ) - ; - - line_callout - = cl::confix_p( - "/*<<" >> *cl::space_p, - (*cl::anychar_p) [boost::bind(&actions_type::mark, &actions, _1, _2)], - ">>*/" - ) - >> *cl::space_p - ; - ignore = cl::confix_p( *cl::blank_p >> "//<-", @@ -354,7 +329,7 @@ namespace quickbook cl::rule start_, identifier, code_elements, start_snippet, end_snippet, - escaped_comment, pass_thru_comment, inline_callout, line_callout, ignore; + escaped_comment, pass_thru_comment, ignore; cl::rule const& start() const { return start_; } @@ -447,27 +422,6 @@ namespace quickbook content.add(mark_begin, mark_end); } - void code_snippet_actions::callout(string_iterator first, string_iterator last) - { - if(!snippet_stack) return; - append_code(first, last); - - if (!in_code) - { - content.add("\n\n", first); - content.add(source_type, first); - content.add("```\n", first); - in_code = true; - } - - content.add( - "``[[callout" + boost::lexical_cast(callout_id) + "]]``", - first); - - snippet_stack->callouts.insert(qbk_value(source_file, mark_begin, mark_end, template_tags::block)); - ++callout_id; - } - void code_snippet_actions::escaped_comment(string_iterator first, string_iterator last) { append_code(first, last); @@ -549,7 +503,7 @@ namespace quickbook void code_snippet_actions::start_snippet_impl(std::string const& id, string_iterator position) { - push_snippet_data(id, callout_id, position); + push_snippet_data(id, position); } void code_snippet_actions::end_snippet_impl(string_iterator position) @@ -557,7 +511,6 @@ namespace quickbook assert(snippet_stack); boost::shared_ptr snippet = pop_snippet_data(); - value callouts = snippet->callouts.release(); mapped_file_builder f; f.start(source_file); @@ -572,29 +525,11 @@ namespace quickbook } std::vector params; - int i = 0; - for(value::iterator it = callouts.begin(); it != callouts.end(); ++it) - { - params.push_back("[callout" + boost::lexical_cast(snippet->callout_base_id + i) + "]"); - ++i; - } file_ptr body = f.release(); - value_builder builder; - builder.set_tag(template_tags::snippet); - builder.insert(qbk_value(body, body->source.begin(), body->source.end(), - template_tags::block)); - builder.insert(callouts); - - template_symbol symbol(snippet->id, params, builder.release()); - storage.push_back(symbol); - - // Copy the snippet's callouts to its parent - - if(snippet_stack) - { - snippet_stack->callouts.extend(callouts); - } + storage.push_back(template_symbol(snippet->id, params, + qbk_value(body, body->source.begin(), body->source.end(), + template_tags::snippet))); } } diff --git a/src/files.cpp b/src/files.cpp index ce63c84..02ada8b 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -285,8 +285,9 @@ namespace quickbook struct mapped_file : file { mapped_file(file_ptr original) : - file(original->path, std::string(), original->version()), - original(original), mapped_sections() {} + file(*original, std::string()), + original(original), mapped_sections() + {} file_ptr original; std::vector mapped_sections; diff --git a/src/files.hpp b/src/files.hpp index 1b0ef28..7475c10 100644 --- a/src/files.hpp +++ b/src/files.hpp @@ -38,13 +38,20 @@ namespace quickbook { { fs::path const path; std::string source; + bool is_code_snippets; private: unsigned qbk_version; public: file(fs::path const& path, std::string const& source, unsigned qbk_version) : - path(path), source(source), qbk_version(qbk_version) + path(path), source(source), is_code_snippets(false), + qbk_version(qbk_version) + {} + + file(file const& f, std::string const& source) : + path(f.path), source(source), is_code_snippets(f.is_code_snippets), + qbk_version(f.qbk_version) {} virtual ~file() {} diff --git a/src/syntax_highlight.cpp b/src/syntax_highlight.cpp index 8eecde7..a0a3bda 100644 --- a/src/syntax_highlight.cpp +++ b/src/syntax_highlight.cpp @@ -93,9 +93,17 @@ namespace quickbook quickbook::actions& escape_actions; do_macro_action do_macro_impl; - syntax_highlight_actions(quickbook::actions& escape_actions) : + // State + bool support_callouts; + string_ref marked_text; + + syntax_highlight_actions(quickbook::actions& escape_actions, + bool is_block) : out(), escape_actions(escape_actions), - do_macro_impl(out, escape_actions) + do_macro_impl(out, escape_actions), + support_callouts(is_block && (qbk_version_n >= 107u || + escape_actions.current_file->is_code_snippets)), + marked_text() {} void span(parse_iterator, parse_iterator, char const*); @@ -106,6 +114,9 @@ namespace quickbook void pre_escape_back(parse_iterator, parse_iterator); void post_escape_back(parse_iterator, parse_iterator); void do_macro(std::string const&); + + void mark_text(parse_iterator, parse_iterator); + void callout(parse_iterator, parse_iterator); }; void syntax_highlight_actions::span(parse_iterator first, @@ -175,6 +186,19 @@ namespace quickbook do_macro_impl(v); } + void syntax_highlight_actions::mark_text(parse_iterator first, + parse_iterator last) + { + marked_text = string_ref(first.base(), last.base()); + } + + void syntax_highlight_actions::callout(parse_iterator, parse_iterator) + { + out << escape_actions.add_callout(qbk_value(escape_actions.current_file, + marked_text.begin(), marked_text.end())); + marked_text.clear(); + } + // Syntax struct keywords_holder @@ -242,7 +266,9 @@ namespace quickbook unexpected_char(self.actions, &syntax_highlight_actions::unexpected_char), plain_char(self.actions, &syntax_highlight_actions::plain_char), pre_escape_back(self.actions, &syntax_highlight_actions::pre_escape_back), - post_escape_back(self.actions, &syntax_highlight_actions::post_escape_back); + post_escape_back(self.actions, &syntax_highlight_actions::post_escape_back), + mark_text(self.actions, &syntax_highlight_actions::mark_text), + callout(self.actions, &syntax_highlight_actions::callout); member_action_value do_macro(self.actions, &syntax_highlight_actions::do_macro); @@ -252,6 +278,10 @@ namespace quickbook | macro | escape | preprocessor [span("preprocessor")] + | cl::eps_p(ph::var(self.actions.support_callouts)) + >> ( line_callout [callout] + | inline_callout [callout] + ) | comment | keyword [span("keyword")] | identifier [span("identifier")] @@ -294,6 +324,23 @@ namespace quickbook = '#' >> *cl::space_p >> ((cl::alpha_p | '_') >> *(cl::alnum_p | '_')) ; + inline_callout + = cl::confix_p( + "/*<" >> *cl::space_p, + (*cl::anychar_p) [mark_text], + ">*/" + ) + ; + + line_callout + = cl::confix_p( + "/*<<" >> *cl::space_p, + (*cl::anychar_p) [mark_text], + ">>*/" + ) + >> *cl::space_p + ; + comment = cl::str_p("//") [span_start("comment")] >> *( escape @@ -342,7 +389,9 @@ namespace quickbook } cl::rule - program, macro, preprocessor, comment, special, string_, + program, macro, preprocessor, + inline_callout, line_callout, comment, + special, string_, char_, number, identifier, keyword, escape, string_char; @@ -377,7 +426,9 @@ namespace quickbook unexpected_char(self.actions, &syntax_highlight_actions::unexpected_char), plain_char(self.actions, &syntax_highlight_actions::plain_char), pre_escape_back(self.actions, &syntax_highlight_actions::pre_escape_back), - post_escape_back(self.actions, &syntax_highlight_actions::post_escape_back); + post_escape_back(self.actions, &syntax_highlight_actions::post_escape_back), + mark_text(self.actions, &syntax_highlight_actions::mark_text), + callout(self.actions, &syntax_highlight_actions::callout); member_action_value do_macro(self.actions, &syntax_highlight_actions::do_macro); @@ -560,9 +611,10 @@ namespace quickbook parse_iterator first, parse_iterator last, actions& escape_actions, - std::string const& source_mode) + std::string const& source_mode, + bool is_block) { - syntax_highlight_actions syn_actions(escape_actions); + syntax_highlight_actions syn_actions(escape_actions, is_block); // print the code with syntax coloring if (source_mode == "c++") diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 6d8b11a..ef9e068 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -26,6 +26,7 @@ test-suite quickbook.test : [ quickbook-test anchor-1_6 ] [ quickbook-test blocks-1_5 ] [ quickbook-test callouts-1_5 ] + [ quickbook-test callouts-1_7 ] [ quickbook-test code-1_1 ] [ quickbook-test code-1_5 ] [ quickbook-test code_cpp-1_5 ] diff --git a/test/callouts-1_7.gold b/test/callouts-1_7.gold new file mode 100644 index 0000000..5cf0dc5 --- /dev/null +++ b/test/callouts-1_7.gold @@ -0,0 +1,247 @@ + + +
+ Callout Tests + + Example 1: + + + Now we can define a function that simulates an ordinary six-sided die. + +int roll_die() { + boost::uniform_int<> dist(1, 6); +} + + + + + create a uniform_int distribution + + + + + Example 2: + +int roll_die() { + boost::variate_generator<boost::mt19937&, boost::uniform_int<> > die(gen, dist); +} + + + + + + test + + + + + + Example 3: + +int roll_die() { + boost::variate_generator<boost::mt19937&, boost::uniform_int<> > die(gen, dist); +} + + + + + + test + + + + + + Example 3 (again!): + +int roll_die() { + boost::variate_generator<boost::mt19937&, boost::uniform_int<> > die(gen, dist); +} + + + + + + test + + + + + + Example 4: + +int roll_die() { + boost::variate_generator<boost::mt19937&, boost::uniform_int<> > die(gen, dist); + boost::uniform_int<> dist(1, 6); +} + + + + + callout 1 + + + + + callout 2 + + + + + create a uniform_int distribution + + + +boost::uniform_int<> dist(1, 6); + + + + + callout 2 + + + + + create a uniform_int distribution + + + +
+ <link linkend="callout_tests.test_section">Try callouts in a section</link> + + Example 1: + + + Now we can define a function that simulates an ordinary six-sided die. + +int roll_die() { + boost::uniform_int<> dist(1, 6); +} + + + + + create a uniform_int distribution + + + + + Example 2: + +int roll_die() { + boost::variate_generator<boost::mt19937&, boost::uniform_int<> > die(gen, dist); +} + + + + + + test + + + + + + Example 3: + +int roll_die() { + boost::variate_generator<boost::mt19937&, boost::uniform_int<> > die(gen, dist); +} + + + + + + test + + + + + + Example 3 (again!): + +int roll_die() { + boost::variate_generator<boost::mt19937&, boost::uniform_int<> > die(gen, dist); +} + + + + + + test + + + + + + Example 4: + +int roll_die() { + boost::variate_generator<boost::mt19937&, boost::uniform_int<> > die(gen, dist); + boost::uniform_int<> dist(1, 6); +} + + + + + callout 1 + + + + + callout 2 + + + + + create a uniform_int distribution + + + +boost::uniform_int<> dist(1, 6); + + + + + callout 2 + + + + + create a uniform_int distribution + + + +
+
+ <link linkend="callout_tests.blocks">Callouts in code blocks</link> +int roll_die() { + boost::uniform_int<> dist(1, 6); +} + + + + + create a uniform_int distribution + + + +int roll_die() { + boost::variate_generator<boost::mt19937&, boost::uniform_int<> > die(gen, dist); +} + + + + + + test + + + + + + /*< This shouldn't be a callout >*/ + +
+
diff --git a/test/callouts-1_7.quickbook b/test/callouts-1_7.quickbook new file mode 100644 index 0000000..fd3d9ee --- /dev/null +++ b/test/callouts-1_7.quickbook @@ -0,0 +1,68 @@ +[article Callout Tests + [quickbook 1.7] +] + +[import callouts.cpp] + +Example 1: + +[example1] + +Example 2: + +[example2] + +Example 3: + +[example3] + +Example 3 (again!): + +[example3] + +Example 4: + +[example4] +[example4a] + +[section:test_section Try callouts in a section] + +Example 1: + +[example1] + +Example 2: + +[example2] + +Example 3: + +[example3] + +Example 3 (again!): + +[example3] + +Example 4: + +[example4] +[example4a] + +[endsect] + +[section:blocks Callouts in code blocks] + + int roll_die() { + boost::uniform_int<> dist(1, 6); /*< create a uniform_int distribution >*/ + } + +``` +int roll_die() { + /*<< [important test] >>*/ + boost::variate_generator > die(gen, dist); +} +``` + +`/*< This shouldn't be a callout >*/` + +[endsect] \ No newline at end of file