30
30
31
31
using Microsoft . VisualStudio . Services . WebApi ;
32
32
using Process = System . Diagnostics . Process ;
33
+ using System . Runtime . CompilerServices ;
34
+
35
+ [ assembly: InternalsVisibleTo ( "AzureDevOps.WikiPDFExport.Test" ) ]
33
36
34
37
namespace azuredevops_export_wiki
35
38
{
@@ -100,7 +103,6 @@ public WikiPDFExporter(Options options)
100
103
{ "icon_pull_request" , "bowtie-tfvc-pull-request" } ,
101
104
{ "icon_github_issue" , "bowtie-status-error-outline" } ,
102
105
} ;
103
-
104
106
}
105
107
106
108
public async Task Export ( )
@@ -416,31 +418,6 @@ private string ConvertMarkdownToHTML(List<MarkdownFile> files)
416
418
pipelineBuilder = pipelineBuilder . UseMermaidContainers ( ) ;
417
419
}
418
420
419
- var firstMDFileInfo = new FileInfo ( files [ 0 ] . AbsolutePath ) ;
420
- var singleMDFilePath = firstMDFileInfo . DirectoryName ;
421
- if ( _options . SingleTOC )
422
- {
423
- var completeMarkdown = "[TOC]\n " ;
424
- for ( var i = 0 ; i < files . Count ; i ++ )
425
- {
426
- var file = new FileInfo ( files [ i ] . AbsolutePath ) ;
427
- var md = File . ReadAllText ( file . FullName ) ;
428
- completeMarkdown += md ;
429
- }
430
-
431
- var directoryName = firstMDFileInfo . Directory . Name ;
432
- var relativePath = "/" + directoryName + ".md" ;
433
- singleMDFilePath = new FileInfo ( files [ 0 ] . AbsolutePath ) . DirectoryName + relativePath ;
434
- if ( File . Exists ( singleMDFilePath ) )
435
- {
436
- Log ( $ "File { singleMDFilePath } can't be used as a single md!", LogLevel . Error , 1 ) ;
437
- return null ;
438
- }
439
- File . WriteAllText ( singleMDFilePath , completeMarkdown ) ;
440
- files = new List < MarkdownFile > { new MarkdownFile { AbsolutePath = singleMDFilePath , Level = 0 , RelativePath = relativePath } } ;
441
- }
442
-
443
-
444
421
for ( var i = 0 ; i < files . Count ; i ++ )
445
422
{
446
423
var mf = files [ i ] ;
@@ -455,14 +432,37 @@ private string ConvertMarkdownToHTML(List<MarkdownFile> files)
455
432
continue ;
456
433
}
457
434
458
- var md = File . ReadAllText ( file . FullName ) ;
435
+ var markdownContent = File . ReadAllText ( file . FullName ) ;
436
+ files [ i ] . Content = markdownContent ;
437
+ }
459
438
460
- //rename TOC tags to fit to MarkdigToc
461
- if ( _options . SingleTOC )
462
- md = md . Replace ( "[[_TOC_]]" , "" ) ;
463
- else
464
- md = RenameTableOfContent ( md ) ;
439
+ if ( ! string . IsNullOrEmpty ( _options . GlobalTOC ) )
440
+ {
441
+ var firstMDFileInfo = new FileInfo ( files [ 0 ] . AbsolutePath ) ;
442
+ var directoryName = firstMDFileInfo . Directory . Name ;
443
+ var tocName = _options . GlobalTOC == "" ? directoryName : _options . GlobalTOC ;
444
+ var relativePath = "/" + tocName + ".md" ;
445
+ var tocMDFilePath = new FileInfo ( files [ 0 ] . AbsolutePath ) . DirectoryName + relativePath ;
446
+
447
+ var contents = files . Select ( x => x . Content ) . ToList ( ) ;
448
+ var tocContent = CreateGlobalTableOfContent ( contents ) ;
449
+ var tocString = string . Join ( "\n " , tocContent ) ;
450
+ var tocMarkdownFile = new MarkdownFile { AbsolutePath = tocMDFilePath , Level = 0 , RelativePath = relativePath , Content = tocString } ;
451
+ files . Insert ( 0 , tocMarkdownFile ) ;
452
+ }
465
453
454
+ for ( var i = 0 ; i < files . Count ; i ++ )
455
+ {
456
+ var mf = files [ i ] ;
457
+ var file = new FileInfo ( files [ i ] . AbsolutePath ) ;
458
+
459
+ Log ( $ "{ file . Name } ", LogLevel . Information , 1 ) ;
460
+
461
+ var md = mf . Content ;
462
+
463
+ //rename TOC tags to fit to MarkdigToc or delete them from each markdown document
464
+ var newTOCString = _options . GlobalTOC != null ? "" : "[TOC]" ;
465
+ md = md . Replace ( "[[_TOC_]]" , newTOCString ) ;
466
466
467
467
// remove scalings from image links, width & height: file.png =600x500
468
468
var regexImageScalings = @"\((.[^\)]*?[png|jpg|jpeg]) =(\d+)x(\d+)\)" ;
@@ -485,7 +485,7 @@ private string ConvertMarkdownToHTML(List<MarkdownFile> files)
485
485
var pipeline = pipelineBuilder . Build ( ) ;
486
486
487
487
//parse the markdown document so we can alter it later
488
- var document = ( MarkdownDocument ) Markdown . Parse ( md , pipeline ) ;
488
+ var document = Markdown . Parse ( md , pipeline ) ;
489
489
490
490
if ( _options . NoFrontmatter )
491
491
{
@@ -520,6 +520,12 @@ private string ConvertMarkdownToHTML(List<MarkdownFile> files)
520
520
}
521
521
html = builder . ToString ( ) ;
522
522
523
+ if ( ! string . IsNullOrEmpty ( _options . GlobalTOC ) && i == 0 )
524
+ {
525
+ html = RemoveDuplicatedHeadersFromGlobalTOC ( html ) ;
526
+ Log ( $ "Removed duplicated headers from toc html", LogLevel . Information , 1 ) ;
527
+ }
528
+
523
529
//add html anchor
524
530
var anchorPath = file . FullName . Substring ( _path . Length ) ;
525
531
anchorPath = anchorPath . Replace ( "\\ " , "" ) ;
@@ -588,14 +594,35 @@ private string ConvertMarkdownToHTML(List<MarkdownFile> files)
588
594
sb . Append ( html ) ;
589
595
}
590
596
591
- if ( _options . SingleTOC && File . Exists ( singleMDFilePath ) )
592
- File . Delete ( singleMDFilePath ) ;
593
-
594
597
var result = sb . ToString ( ) ;
595
598
596
599
return result ;
597
600
}
598
601
602
+ internal string RemoveDuplicatedHeadersFromGlobalTOC ( string html )
603
+ {
604
+ var result = Regex . Replace ( html , @"^ *<h[123456].*>.*<\/h[123456]> *\n?$" , "" , RegexOptions . Multiline ) ;
605
+ result = result . Trim ( '\n ' ) ;
606
+ return result ;
607
+ }
608
+
609
+ internal List < string > CreateGlobalTableOfContent ( List < string > contents )
610
+ {
611
+ var headers = new List < string > ( ) ;
612
+ foreach ( var content in contents )
613
+ {
614
+ var headerMatches = Regex . Matches ( content , "^ *#+ ?.*$" , RegexOptions . Multiline ) ;
615
+ headers . AddRange ( headerMatches . Select ( x => x . Value . Trim ( ) ) ) ;
616
+ }
617
+
618
+ if ( ! headers . Any ( ) )
619
+ return new List < string > ( ) ; // no header -> no toc
620
+
621
+ var tocContent = new List < string > { "[TOC]" } ; // MarkdigToc style
622
+ tocContent . AddRange ( headers ) ;
623
+ return tocContent ;
624
+ }
625
+
599
626
private MarkdownDocument RemoveFrontmatter ( MarkdownDocument document )
600
627
{
601
628
var frontmatter = document . Descendants < YamlFrontMatterBlock > ( ) . FirstOrDefault ( ) ;
@@ -889,6 +916,7 @@ public class MarkdownFile
889
916
public string AbsolutePath ;
890
917
public string RelativePath ;
891
918
public int Level ;
919
+ public string Content ;
892
920
893
921
public override string ToString ( )
894
922
{
0 commit comments