From b74fdd6d54fff4ee49578b4b4e6f16aa152c0450 Mon Sep 17 00:00:00 2001 From: "vyacheslav.popov" Date: Mon, 3 Apr 2023 11:53:30 +0400 Subject: [PATCH] feat: add the ability to order articles --- src/parser.rs | 100 +++++++++++++++++++++++++++++++++++++++++++++++++ src/plugins.rs | 4 +- 2 files changed, 102 insertions(+), 2 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index eb19bac..2a9ac77 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -107,6 +107,7 @@ impl ParserStateMachine { #[derive(Debug, Clone)] pub struct Article { + pub order: i16, pub topic: String, pub content: String, pub path: String, @@ -150,6 +151,27 @@ enum Keyword { * ``` */ Article, + /** + * @Article Syntax + * `@Order ` is for controlling the order of the article sections. + * + * Example: + * + * ```rust + * /** + * * @Article Usage example + * * @Order 2 + * * ## Header2 + * */ + * + * /** + * * @Article Usage example + * * @Order 1 + * * # Header1 + * */ + * ``` + */ + Order, /** * @Article Syntax * `@FileArticle` allows you to mark a whole file is a source of documentation for a specified @@ -229,6 +251,7 @@ enum Keyword { impl Keyword { fn as_str(&self) -> &'static str { match *self { + Keyword::Order => "@Order", Keyword::Article => "@Article", Keyword::FileArticle => "@FileArticle", Keyword::Ignore => "@Ignore", @@ -262,6 +285,7 @@ impl Parser { let articles: Vec
= vec![]; let current_article = Article { + order: 0, topic: String::from(""), content: String::from(""), path: String::from(""), @@ -327,6 +351,14 @@ impl Parser { result } + fn parse_article_order(&self, order_block_line: String) -> i16 { + order_block_line + .replace(Keyword::Order.as_str(), "") + .trim() + .parse() + .unwrap_or(0) + } + fn trim_article_line(&self, line: String) -> String { line.trim_start() .trim_start_matches(self.comment_symbol) @@ -336,6 +368,7 @@ impl Parser { fn new_article(&self) -> Article { Article { + order: 0, topic: String::from(""), content: String::from(""), path: String::from(""), @@ -350,6 +383,7 @@ impl Parser { let topic = name_chunks[2..].join("."); vec![Article { + order: 0, topic, content: String::from(file_content), path: String::from(file_path), @@ -429,6 +463,13 @@ impl Parser { self.current_article.topic = self.trim_article_line(topic); self.current_article.start_line = line_number; self.state_machine.to_article_mut(); + } else if trimmed_line.starts_with(Keyword::Order.as_str()) + && self.state_machine.is_in(ParserState::ArticleParsing) + { + let parsed_order = self.parse_article_order(trimmed_line); + + self.current_article.order = parsed_order; + self.current_article.start_line = line_number; } else if trimmed_line.starts_with(Keyword::Ignore.as_str()) { self.state_machine.to_skippintg_mut(); self.current_article = self.new_article(); @@ -484,6 +525,7 @@ impl Parser { line_number += 1; } + self.articles.sort_by_key(|a| a.order); self.articles.clone() } @@ -559,6 +601,7 @@ pub fn test () {} let articles = parser.parse_file(file_content, ""); let expected_result = vec![Article { + order: 0, topic: String::from("Test article"), content: String::from("some text"), path: "".to_string(), @@ -569,6 +612,48 @@ pub fn test () {} assert_eq!(articles, expected_result); } +#[test] +fn parse_articles_with_custom_order() { + let mut parser = Parser::new(get_test_config()); + let file_content = " +/** + * @Article Test article3 + * @Order 3 + * some text3 + */ +pub fn test () {} + +/** + * @Article Test article1 + * @Order 1 + * some text + */ +pub fn test2 () {} + "; + + let articles = parser.parse_file(file_content, ""); + let expected_result = vec![ + Article { + order: 1, + topic: String::from("Test article1"), + content: String::from("some text"), + path: "".to_string(), + start_line: 11, + end_line: 12, + }, + Article { + order: 3, + topic: String::from("Test article3"), + content: String::from("some text3"), + path: "".to_string(), + start_line: 4, + end_line: 5, + }, + ]; + + assert_eq!(articles, expected_result); +} + #[test] fn ignore_comments_with_ignore_mark() { let mut parser = Parser::new(get_test_config()); @@ -604,6 +689,7 @@ pub fn test () {} let articles = parser.parse_file(file_content, ""); let expected_result = vec![Article { + order: 0, topic: String::from("Test article"), content: String::from("some multiline\nawesome text"), path: "".to_string(), @@ -650,6 +736,7 @@ pub fn test () {} let articles = parser.parse_file(file_content, ""); let expected_result = vec![Article { + order: 0, topic: String::from("Test article"), content: String::from("```rust\nfn main() {\n println!(\"Hello world!\");\n}\n```\n\n```rust\nfn test() {\n println!(\"Hello world!\");\n}\n```"), path: "".to_string(), @@ -684,6 +771,7 @@ fn parse_documentation_with_indentation_before_comments() { let articles = parser.parse_file(file_content, ""); let expected_result = vec![Article { + order: 0, topic: String::from("Test article"), content: String::from("#### [no-implicit-coercion](https://eslint.org/docs/rules/no-implicit-coercion)\nAll implicit coercions except `!!` are disallowed:\n```js\n// Fail\n+foo\n1 * foo\n\'\' + foo\n`${foo}`\n~foo.indexOf(bar)\n\n// Pass\n!!foo\n```"), path: "".to_string(), @@ -714,6 +802,7 @@ pub fn test () {} let articles = parser.parse_file(file_content, ""); let expected_result = vec![Article { + order: 0, topic: String::from("Test article"), content: String::from("List:\n* Item 1\n* Item 2\n\n Item 2 subtext\n* Item 3"), path: "".to_string(), @@ -738,6 +827,7 @@ use std::io::prelude::*; let articles = parser.parse_file(file_content, ""); let expected_result = vec![Article { + order: 0, topic: String::from("Test article"), content: String::from(""), path: "".to_string(), @@ -760,6 +850,7 @@ test let articles = parser.parse_file(file_content, ""); let expected_result = vec![Article { + order: 0, topic: String::from("Test article"), content: String::from("test"), path: "".to_string(), @@ -784,6 +875,7 @@ const b = 2 let articles = parser.parse_file(file_content, ""); let expected_result = vec![Article { + order: 0, topic: String::from("Test article"), content: String::from("test"), path: "".to_string(), @@ -816,6 +908,7 @@ fn use_global_article_attribute() { let articles = parser.parse_file(file_content, ""); let expected_result = vec![ Article { + order: 0, topic: String::from("Test article"), content: String::from("test"), path: "".to_string(), @@ -823,6 +916,7 @@ fn use_global_article_attribute() { end_line: 7, }, Article { + order: 0, topic: String::from("Test article"), content: String::from("test"), path: "".to_string(), @@ -856,6 +950,7 @@ fn ignore_sections_in_case_of_global_article() { let articles = parser.parse_file(file_content, ""); let expected_result = vec![Article { + order: 0, topic: String::from("Test article"), content: String::from("test"), path: "".to_string(), @@ -881,6 +976,7 @@ const TIMEOUT = 3000 let articles = parser.parse_file(file_content, ""); let expected_result = vec![Article { + order: 0, topic: String::from("Test article"), content: String::from("Request timeout:\n```js/\nconst TIMEOUT = 3000\n```"), path: "".to_string(), @@ -908,6 +1004,7 @@ const TIMEOUT = 3000 let articles = parser.parse_file(file_content, ""); let expected_result = vec![Article { + order: 0, topic: String::from("Test article"), content: String::from("Request timeout:\n```js/\nconst TIMEOUT = 3000\n```"), path: "".to_string(), @@ -933,6 +1030,7 @@ fn parse_code_block_attribute_from_ending_comment_only() { let articles = parser.parse_file(file_content, ""); let expected_result = vec![Article { + order: 0, topic: String::from("Test article"), content: String::from("Should ignore @CodeBlockEnd in a text block\n```rust/\n...\n```"), path: "".to_string(), @@ -960,6 +1058,7 @@ fn parse_nested_commends() { let articles = parser.parse_file(file_content, ""); let expected_result = vec![Article { + order: 0, topic: String::from("Test article"), content: String::from("Example:\n/**\n* @Article Example article\n* Example\n*/\ntest"), path: "".to_string(), @@ -998,6 +1097,7 @@ fn parse_fdoc_file_check() { let parser = Parser::new(get_test_config()); let result = parser.parse_fdoc_file("test", "/some/long/path/to/file.fdoc.md"); let expected_result = vec![Article { + order: 0, topic: String::from("file"), content: String::from("test"), path: "/some/long/path/to/file.fdoc.md".to_string(), diff --git a/src/plugins.rs b/src/plugins.rs index 8930347..9fb884e 100644 --- a/src/plugins.rs +++ b/src/plugins.rs @@ -28,7 +28,7 @@ pub struct Plugins { * {{ #your-plugin-name * any text here * }} - * + * * ``` * * To create a plugin for parsing these blocks, you should add a file called `your-plugin-name.html.lua` into the plugins folder. By default, it's `./plugins`, but it's possible to change it in the config file. @@ -44,7 +44,7 @@ pub struct Plugins { * * ```lua * function transform(text) - * result = 'transformted text' + * result = 'transformted text' * end * ``` *