@@ -223,6 +223,31 @@ struct PendingBlockDirective {
223
223
}
224
224
}
225
225
226
+ struct PendingDoxygenCommand {
227
+ enum CommandKind {
228
+ case param( name: Substring )
229
+
230
+ var debugDescription : String {
231
+ switch self {
232
+ case . param( name: let name) :
233
+ return " 'param' Argument: ' \( name) ' "
234
+ }
235
+ }
236
+ }
237
+
238
+ var atLocation : SourceLocation
239
+
240
+ var nameLocation : SourceLocation
241
+
242
+ var kind : CommandKind
243
+
244
+ var endLocation : SourceLocation
245
+
246
+ mutating func addLine( _ line: TrimmedLine ) {
247
+ endLocation = SourceLocation ( line: line. lineNumber ?? 0 , column: line. untrimmedText. count + 1 , source: line. source)
248
+ }
249
+ }
250
+
226
251
struct TrimmedLine {
227
252
/// A successful result of scanning for a prefix on a ``TrimmedLine``.
228
253
struct Lex : Equatable {
@@ -459,8 +484,11 @@ private enum ParseContainer: CustomStringConvertible {
459
484
/// A block directive container, which can contain other block directives or runs of lines.
460
485
case blockDirective( PendingBlockDirective , [ ParseContainer ] )
461
486
462
- init < TrimmedLines: Sequence > ( parsingHierarchyFrom trimmedLines: TrimmedLines ) where TrimmedLines. Element == TrimmedLine {
463
- self = ParseContainerStack ( parsingHierarchyFrom: trimmedLines) . top
487
+ /// A Doxygen command, which can contain arbitrary markup (but not block directives).
488
+ case doxygenCommand( PendingDoxygenCommand , [ TrimmedLine ] )
489
+
490
+ init < TrimmedLines: Sequence > ( parsingHierarchyFrom trimmedLines: TrimmedLines , options: ParseOptions ) where TrimmedLines. Element == TrimmedLine {
491
+ self = ParseContainerStack ( parsingHierarchyFrom: trimmedLines, options: options) . top
464
492
}
465
493
466
494
var children : [ ParseContainer ] {
@@ -471,6 +499,8 @@ private enum ParseContainer: CustomStringConvertible {
471
499
return children
472
500
case . lineRun:
473
501
return [ ]
502
+ case . doxygenCommand:
503
+ return [ ]
474
504
}
475
505
}
476
506
@@ -545,6 +575,15 @@ private enum ParseContainer: CustomStringConvertible {
545
575
indent -= 4
546
576
}
547
577
print ( children: children)
578
+ case . doxygenCommand( let pendingDoxygenCommand, let lines) :
579
+ print ( " * Doxygen command \( pendingDoxygenCommand. kind. debugDescription) " )
580
+ queueNewline ( )
581
+ indent += 4
582
+ for line in lines {
583
+ print ( line. text. debugDescription)
584
+ queueNewline ( )
585
+ }
586
+ indent -= 4
548
587
}
549
588
}
550
589
}
@@ -565,6 +604,9 @@ private enum ParseContainer: CustomStringConvertible {
565
604
case . blockDirective( var pendingBlockDirective, let children) :
566
605
pendingBlockDirective. updateIndentation ( for: line)
567
606
self = . blockDirective( pendingBlockDirective, children)
607
+ case . doxygenCommand:
608
+ var newParent : ParseContainer ? = nil
609
+ parent? . updateIndentation ( under: & newParent, for: line)
568
610
}
569
611
}
570
612
@@ -609,6 +651,8 @@ private enum ParseContainer: CustomStringConvertible {
609
651
return parent? . indentationAdjustment ( under: nil ) ?? 0
610
652
case . blockDirective( let pendingBlockDirective, _) :
611
653
return pendingBlockDirective. indentationColumnCount
654
+ case . doxygenCommand:
655
+ return parent? . indentationAdjustment ( under: nil ) ?? 0
612
656
}
613
657
}
614
658
@@ -677,6 +721,15 @@ private enum ParseContainer: CustomStringConvertible {
677
721
} ) ,
678
722
parsedRange: pendingBlockDirective. atLocation..< pendingBlockDirective. endLocation,
679
723
children) ]
724
+ case let . doxygenCommand( pendingDoxygenCommand, lines) :
725
+ let range = pendingDoxygenCommand. atLocation..< pendingDoxygenCommand. endLocation
726
+ ranges. add ( range)
727
+ let children = ParseContainer . lineRun ( lines, isInCodeFence: false )
728
+ . convertToRawMarkup ( ranges: & ranges, parent: self , options: options)
729
+ switch pendingDoxygenCommand. kind {
730
+ case . param( let name) :
731
+ return [ . doxygenParam( name: String ( name) , parsedRange: range, children) ]
732
+ }
680
733
}
681
734
}
682
735
}
@@ -689,8 +742,11 @@ struct ParseContainerStack {
689
742
/// The stack of containers to be incrementally folded into a hierarchy.
690
743
private var stack : [ ParseContainer ]
691
744
692
- init < TrimmedLines: Sequence > ( parsingHierarchyFrom trimmedLines: TrimmedLines ) where TrimmedLines. Element == TrimmedLine {
745
+ private let options : ParseOptions
746
+
747
+ init < TrimmedLines: Sequence > ( parsingHierarchyFrom trimmedLines: TrimmedLines , options: ParseOptions ) where TrimmedLines. Element == TrimmedLine {
693
748
self . stack = [ . root( [ ] ) ]
749
+ self . options = options
694
750
for line in trimmedLines {
695
751
accept ( line)
696
752
}
@@ -708,6 +764,20 @@ struct ParseContainerStack {
708
764
} != nil
709
765
}
710
766
767
+ private var canParseDoxygenCommand : Bool {
768
+ guard options. contains ( . parseMinimalDoxygen) else { return false }
769
+
770
+ guard !isInBlockDirective else { return false }
771
+
772
+ if case . blockDirective = top {
773
+ return false
774
+ } else if case . lineRun( _, isInCodeFence: let codeFence) = top {
775
+ return !codeFence
776
+ } else {
777
+ return true
778
+ }
779
+ }
780
+
711
781
private func isCodeFenceOrIndentedCodeBlock( on line: TrimmedLine ) -> Bool {
712
782
// Check if this line is indented 4 or more spaces relative to the current
713
783
// indentation adjustment.
@@ -760,23 +830,85 @@ struct ParseContainerStack {
760
830
return pendingBlockDirective
761
831
}
762
832
833
+ private func parseDoxygenCommandOpening( on line: TrimmedLine ) -> ( pendingCommand: PendingDoxygenCommand , remainder: TrimmedLine ) ? {
834
+ guard canParseDoxygenCommand else { return nil }
835
+ guard !isCodeFenceOrIndentedCodeBlock( on: line) else { return nil }
836
+
837
+ var remainder = line
838
+ guard let at = remainder. lex ( until: { ch in
839
+ switch ch {
840
+ case " @ " , " \\ " :
841
+ return . continue
842
+ default :
843
+ return . stop
844
+ }
845
+ } ) else { return nil }
846
+ guard let name = remainder. lex ( until: { ch in
847
+ if ch. isWhitespace {
848
+ return . stop
849
+ } else {
850
+ return . continue
851
+ }
852
+ } ) else { return nil }
853
+ remainder. lexWhitespace ( )
854
+
855
+ switch name. text. lowercased ( ) {
856
+ case " param " :
857
+ guard let paramName = remainder. lex ( until: { ch in
858
+ if ch. isWhitespace {
859
+ return . stop
860
+ } else {
861
+ return . continue
862
+ }
863
+ } ) else { return nil }
864
+ remainder. lexWhitespace ( )
865
+ var pendingCommand = PendingDoxygenCommand (
866
+ atLocation: at. range!. lowerBound,
867
+ nameLocation: name. range!. lowerBound,
868
+ kind: . param( name: paramName. text) ,
869
+ endLocation: name. range!. upperBound)
870
+ pendingCommand. addLine ( remainder)
871
+ return ( pendingCommand, remainder)
872
+ default :
873
+ return nil
874
+ }
875
+ }
876
+
763
877
/// Accept a trimmed line, opening new block directives as indicated by the source,
764
878
/// closing a block directive if applicable, or adding the line to a run of lines to be parsed
765
879
/// as Markdown later.
766
880
private mutating func accept( _ line: TrimmedLine ) {
767
- if line. isEmptyOrAllWhitespace,
768
- case let . blockDirective( pendingBlockDirective, _) = top {
769
- switch pendingBlockDirective. parseState {
770
- case . argumentsStart,
771
- . contentsStart,
772
- . done:
773
- closeTop ( )
881
+ if line. isEmptyOrAllWhitespace {
882
+ switch top {
883
+ case let . blockDirective( pendingBlockDirective, _) :
884
+ switch pendingBlockDirective. parseState {
885
+ case . argumentsStart,
886
+ . contentsStart,
887
+ . done:
888
+ closeTop ( )
774
889
890
+ default :
891
+ break
892
+ }
893
+ case . doxygenCommand:
894
+ closeTop ( )
775
895
default :
776
896
break
777
897
}
778
898
}
779
899
900
+ // If we can parse a Doxygen command from this line, start one and skip everything else.
901
+ if let result = parseDoxygenCommandOpening ( on: line) {
902
+ switch top {
903
+ case . root:
904
+ break
905
+ default :
906
+ closeTop ( )
907
+ }
908
+ push ( . doxygenCommand( result. pendingCommand, [ result. remainder] ) )
909
+ return
910
+ }
911
+
780
912
// If we're inside a block directive, check to see whether we need to update its
781
913
// indentation calculation to account for its content.
782
914
updateIndentation ( for: line)
@@ -822,7 +954,7 @@ struct ParseContainerStack {
822
954
switch top {
823
955
case . root:
824
956
push ( . blockDirective( newBlockDirective, [ ] ) )
825
- case . lineRun:
957
+ case . lineRun, . doxygenCommand :
826
958
closeTop ( )
827
959
push ( . blockDirective( newBlockDirective, [ ] ) )
828
960
case . blockDirective( let previousBlockDirective, _) :
@@ -848,11 +980,16 @@ struct ParseContainerStack {
848
980
} else {
849
981
switch top {
850
982
case . root:
851
- push ( . lineRun( [ line] , isInCodeFence: false ) )
983
+ push ( . lineRun( [ line] , isInCodeFence: line . isProbablyCodeFence ) )
852
984
case . lineRun( var lines, let isInCodeFence) :
853
985
pop ( )
854
986
lines. append ( line)
855
987
push ( . lineRun( lines, isInCodeFence: isInCodeFence != line. isProbablyCodeFence) )
988
+ case . doxygenCommand( var pendingDoxygenCommand, var lines) :
989
+ pop ( )
990
+ lines. append ( line)
991
+ pendingDoxygenCommand. addLine ( line)
992
+ push ( . doxygenCommand( pendingDoxygenCommand, lines) )
856
993
case . blockDirective( var pendingBlockDirective, let children) :
857
994
// A pending block directive can accept this line if it is in the middle of
858
995
// parsing arguments text (to allow indentation to align arguments) or
@@ -923,6 +1060,8 @@ struct ParseContainerStack {
923
1060
push ( . blockDirective( pendingBlockDirective, children) )
924
1061
case . lineRun:
925
1062
fatalError ( " Line runs cannot have children " )
1063
+ case . doxygenCommand:
1064
+ fatalError ( " Doxygen commands cannot have children " )
926
1065
}
927
1066
}
928
1067
@@ -985,7 +1124,7 @@ struct BlockDirectiveParser {
985
1124
// Phase 1: Categorize the lines into a hierarchy of block containers by parsing the prefix
986
1125
// of the line, opening and closing block directives appropriately, and folding elements
987
1126
// into a root document.
988
- let rootContainer = ParseContainer ( parsingHierarchyFrom: trimmedLines)
1127
+ let rootContainer = ParseContainer ( parsingHierarchyFrom: trimmedLines, options : options )
989
1128
990
1129
// Phase 2: Convert the hierarchy of block containers into a real ``Document``.
991
1130
// This is where the CommonMark parser is called upon to parse runs of lines of content,
0 commit comments