@@ -107,6 +107,7 @@ impl ParserStateMachine {
107107
108108#[ derive( Debug ,  Clone ) ]  
109109pub  struct  Article  { 
110+     pub  order :  i16 , 
110111    pub  topic :  String , 
111112    pub  content :  String , 
112113    pub  path :  String , 
@@ -150,6 +151,27 @@ enum Keyword {
150151     * ``` 
151152     */ 
152153    Article , 
154+     /** 
155+      * @Article Syntax 
156+      * `@Order <Order of the Article>` is for controlling the order of the article sections. 
157+      * 
158+      * Example: 
159+      * 
160+      * ```rust 
161+      * /** 
162+      *  * @Article Usage example 
163+      *  * @Order 2 
164+      *  * ## Header2 
165+      *  */ 
166+      * 
167+      *   /** 
168+      *  * @Article Usage example 
169+      *  * @Order 1 
170+      *  * # Header1 
171+      *  */ 
172+      * ``` 
173+      */ 
174+     Order , 
153175    /** 
154176    * @Article Syntax 
155177    * `@FileArticle` allows you to mark a whole file is a source of documentation for a specified 
@@ -229,6 +251,7 @@ enum Keyword {
229251impl  Keyword  { 
230252    fn  as_str ( & self )  -> & ' static  str  { 
231253        match  * self  { 
254+             Keyword :: Order  => "@Order" , 
232255            Keyword :: Article  => "@Article" , 
233256            Keyword :: FileArticle  => "@FileArticle" , 
234257            Keyword :: Ignore  => "@Ignore" , 
@@ -262,6 +285,7 @@ impl Parser {
262285
263286        let  articles:  Vec < Article >  = vec ! [ ] ; 
264287        let  current_article = Article  { 
288+             order :  0 , 
265289            topic :  String :: from ( "" ) , 
266290            content :  String :: from ( "" ) , 
267291            path :  String :: from ( "" ) , 
@@ -327,6 +351,14 @@ impl Parser {
327351        result
328352    } 
329353
354+     fn  parse_article_order ( & self ,  order_block_line :  String )  -> i16  { 
355+         order_block_line
356+                 . replace ( Keyword :: Order . as_str ( ) ,  "" ) 
357+                 . trim ( ) 
358+                 . parse ( ) 
359+                 . unwrap_or ( 0 ) 
360+     } 
361+ 
330362    fn  trim_article_line ( & self ,  line :  String )  -> String  { 
331363        line. trim_start ( ) 
332364            . trim_start_matches ( self . comment_symbol ) 
@@ -336,6 +368,7 @@ impl Parser {
336368
337369    fn  new_article ( & self )  -> Article  { 
338370        Article  { 
371+             order :  0 , 
339372            topic :  String :: from ( "" ) , 
340373            content :  String :: from ( "" ) , 
341374            path :  String :: from ( "" ) , 
@@ -350,6 +383,7 @@ impl Parser {
350383        let  topic = name_chunks[ 2 ..] . join ( "." ) ; 
351384
352385        vec ! [ Article  { 
386+             order:  0 , 
353387            topic, 
354388            content:  String :: from( file_content) , 
355389            path:  String :: from( file_path) , 
@@ -429,6 +463,13 @@ impl Parser {
429463            self . current_article . topic  = self . trim_article_line ( topic) ; 
430464            self . current_article . start_line  = line_number; 
431465            self . state_machine . to_article_mut ( ) ; 
466+         }  else  if  trimmed_line. starts_with ( Keyword :: Order . as_str ( ) ) 
467+             && self . state_machine . is_in ( ParserState :: ArticleParsing ) 
468+         { 
469+             let  parsed_order = self . parse_article_order ( trimmed_line) ; 
470+ 
471+             self . current_article . order  = parsed_order; 
472+             self . current_article . start_line  = line_number; 
432473        }  else  if  trimmed_line. starts_with ( Keyword :: Ignore . as_str ( ) )  { 
433474            self . state_machine . to_skippintg_mut ( ) ; 
434475            self . current_article  = self . new_article ( ) ; 
@@ -484,6 +525,7 @@ impl Parser {
484525            line_number += 1 ; 
485526        } 
486527
528+         self . articles . sort_by_key ( |a| a. order ) ; 
487529        self . articles . clone ( ) 
488530    } 
489531
@@ -559,6 +601,7 @@ pub fn test () {}
559601
560602    let  articles = parser. parse_file ( file_content,  "" ) ; 
561603    let  expected_result = vec ! [ Article  { 
604+         order:  0 , 
562605        topic:  String :: from( "Test article" ) , 
563606        content:  String :: from( "some text" ) , 
564607        path:  "" . to_string( ) , 
@@ -569,6 +612,48 @@ pub fn test () {}
569612    assert_eq ! ( articles,  expected_result) ; 
570613} 
571614
615+ #[ test]  
616+ fn  parse_articles_with_custom_order ( )  { 
617+     let  mut  parser = Parser :: new ( get_test_config ( ) ) ; 
618+     let  file_content = " 
619+ /** 
620+  * @Article Test article3 
621+  * @Order 3 
622+  * some text3 
623+  */ 
624+ pub fn test () {} 
625+ 
626+ /** 
627+  * @Article Test article1 
628+  * @Order 1 
629+  * some text 
630+  */ 
631+ pub fn test2 () {} 
632+   " ; 
633+ 
634+     let  articles = parser. parse_file ( file_content,  "" ) ; 
635+     let  expected_result = vec ! [ 
636+         Article  { 
637+             order:  1 , 
638+             topic:  String :: from( "Test article1" ) , 
639+             content:  String :: from( "some text" ) , 
640+             path:  "" . to_string( ) , 
641+             start_line:  11 , 
642+             end_line:  12 , 
643+         } , 
644+         Article  { 
645+             order:  3 , 
646+             topic:  String :: from( "Test article3" ) , 
647+             content:  String :: from( "some text3" ) , 
648+             path:  "" . to_string( ) , 
649+             start_line:  4 , 
650+             end_line:  5 , 
651+         } , 
652+     ] ; 
653+ 
654+     assert_eq ! ( articles,  expected_result) ; 
655+ } 
656+ 
572657#[ test]  
573658fn  ignore_comments_with_ignore_mark ( )  { 
574659    let  mut  parser = Parser :: new ( get_test_config ( ) ) ; 
@@ -604,6 +689,7 @@ pub fn test () {}
604689
605690    let  articles = parser. parse_file ( file_content,  "" ) ; 
606691    let  expected_result = vec ! [ Article  { 
692+         order:  0 , 
607693        topic:  String :: from( "Test article" ) , 
608694        content:  String :: from( "some multiline\n awesome text" ) , 
609695        path:  "" . to_string( ) , 
@@ -650,6 +736,7 @@ pub fn test () {}
650736
651737    let  articles = parser. parse_file ( file_content,  "" ) ; 
652738    let  expected_result = vec ! [ Article  { 
739+         order:  0 , 
653740        topic:  String :: from( "Test article" ) , 
654741        content:  String :: from( "```rust\n fn main() {\n     println!(\" Hello world!\" );\n }\n ```\n \n ```rust\n fn test() {\n     println!(\" Hello world!\" );\n }\n ```" ) , 
655742        path:  "" . to_string( ) , 
@@ -684,6 +771,7 @@ fn parse_documentation_with_indentation_before_comments() {
684771
685772    let  articles = parser. parse_file ( file_content,  "" ) ; 
686773    let  expected_result = vec ! [ Article  { 
774+         order:  0 , 
687775        topic:  String :: from( "Test article" ) , 
688776        content:  String :: from( "#### [no-implicit-coercion](https://eslint.org/docs/rules/no-implicit-coercion)\n All implicit coercions except `!!` are disallowed:\n ```js\n // Fail\n +foo\n 1 * foo\n \' \'  + foo\n `${foo}`\n ~foo.indexOf(bar)\n \n // Pass\n !!foo\n ```" ) , 
689777        path:  "" . to_string( ) , 
@@ -714,6 +802,7 @@ pub fn test () {}
714802
715803    let  articles = parser. parse_file ( file_content,  "" ) ; 
716804    let  expected_result = vec ! [ Article  { 
805+         order:  0 , 
717806        topic:  String :: from( "Test article" ) , 
718807        content:  String :: from( "List:\n * Item 1\n * Item 2\n \n   Item 2 subtext\n * Item 3" ) , 
719808        path:  "" . to_string( ) , 
@@ -738,6 +827,7 @@ use std::io::prelude::*;
738827
739828    let  articles = parser. parse_file ( file_content,  "" ) ; 
740829    let  expected_result = vec ! [ Article  { 
830+         order:  0 , 
741831        topic:  String :: from( "Test article" ) , 
742832        content:  String :: from( "" ) , 
743833        path:  "" . to_string( ) , 
760850
761851    let  articles = parser. parse_file ( file_content,  "" ) ; 
762852    let  expected_result = vec ! [ Article  { 
853+         order:  0 , 
763854        topic:  String :: from( "Test article" ) , 
764855        content:  String :: from( "test" ) , 
765856        path:  "" . to_string( ) , 
@@ -784,6 +875,7 @@ const b = 2
784875
785876    let  articles = parser. parse_file ( file_content,  "" ) ; 
786877    let  expected_result = vec ! [ Article  { 
878+         order:  0 , 
787879        topic:  String :: from( "Test article" ) , 
788880        content:  String :: from( "test" ) , 
789881        path:  "" . to_string( ) , 
@@ -816,13 +908,15 @@ fn use_global_article_attribute() {
816908    let  articles = parser. parse_file ( file_content,  "" ) ; 
817909    let  expected_result = vec ! [ 
818910        Article  { 
911+             order:  0 , 
819912            topic:  String :: from( "Test article" ) , 
820913            content:  String :: from( "test" ) , 
821914            path:  "" . to_string( ) , 
822915            start_line:  6 , 
823916            end_line:  7 , 
824917        } , 
825918        Article  { 
919+             order:  0 , 
826920            topic:  String :: from( "Test article" ) , 
827921            content:  String :: from( "test" ) , 
828922            path:  "" . to_string( ) , 
@@ -856,6 +950,7 @@ fn ignore_sections_in_case_of_global_article() {
856950
857951    let  articles = parser. parse_file ( file_content,  "" ) ; 
858952    let  expected_result = vec ! [ Article  { 
953+         order:  0 , 
859954        topic:  String :: from( "Test article" ) , 
860955        content:  String :: from( "test" ) , 
861956        path:  "" . to_string( ) , 
@@ -881,6 +976,7 @@ const TIMEOUT = 3000
881976
882977    let  articles = parser. parse_file ( file_content,  "" ) ; 
883978    let  expected_result = vec ! [ Article  { 
979+         order:  0 , 
884980        topic:  String :: from( "Test article" ) , 
885981        content:  String :: from( "Request timeout:\n ```js/\n const TIMEOUT = 3000\n ```" ) , 
886982        path:  "" . to_string( ) , 
@@ -908,6 +1004,7 @@ const TIMEOUT = 3000
9081004
9091005    let  articles = parser. parse_file ( file_content,  "" ) ; 
9101006    let  expected_result = vec ! [ Article  { 
1007+         order:  0 , 
9111008        topic:  String :: from( "Test article" ) , 
9121009        content:  String :: from( "Request timeout:\n ```js/\n const TIMEOUT = 3000\n ```" ) , 
9131010        path:  "" . to_string( ) , 
@@ -933,6 +1030,7 @@ fn parse_code_block_attribute_from_ending_comment_only() {
9331030
9341031    let  articles = parser. parse_file ( file_content,  "" ) ; 
9351032    let  expected_result = vec ! [ Article  { 
1033+         order:  0 , 
9361034        topic:  String :: from( "Test article" ) , 
9371035        content:  String :: from( "Should ignore @CodeBlockEnd in a text block\n ```rust/\n ...\n ```" ) , 
9381036        path:  "" . to_string( ) , 
@@ -960,6 +1058,7 @@ fn parse_nested_commends() {
9601058
9611059    let  articles = parser. parse_file ( file_content,  "" ) ; 
9621060    let  expected_result = vec ! [ Article  { 
1061+         order:  0 , 
9631062        topic:  String :: from( "Test article" ) , 
9641063        content:  String :: from( "Example:\n /**\n * @Article Example article\n * Example\n */\n test" ) , 
9651064        path:  "" . to_string( ) , 
@@ -998,6 +1097,7 @@ fn parse_fdoc_file_check() {
9981097    let  parser = Parser :: new ( get_test_config ( ) ) ; 
9991098    let  result = parser. parse_fdoc_file ( "test" ,  "/some/long/path/to/file.fdoc.md" ) ; 
10001099    let  expected_result = vec ! [ Article  { 
1100+         order:  0 , 
10011101        topic:  String :: from( "file" ) , 
10021102        content:  String :: from( "test" ) , 
10031103        path:  "/some/long/path/to/file.fdoc.md" . to_string( ) , 
0 commit comments