Skip to content

Commit f8cb678

Browse files
authored
put mdfrag componets together (dotnet#2436)
* put mdfrag componets together * resolve comments
1 parent e399eb0 commit f8cb678

File tree

13 files changed

+317
-74
lines changed

13 files changed

+317
-74
lines changed

src/Microsoft.DocAsCode.Build.OverwriteDocuments/OverwriteDocumentModelCreater.cs

+25-15
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,14 @@ namespace Microsoft.DocAsCode.Build.OverwriteDocuments
1515

1616
public class OverwriteDocumentModelCreater
1717
{
18-
public static OverwriteDocumentModel Create(MarkdownFragmentModel model)
18+
string _file;
19+
20+
public OverwriteDocumentModelCreater(string file)
21+
{
22+
_file = file ?? throw new ArgumentNullException(nameof(file));
23+
}
24+
25+
public OverwriteDocumentModel Create(MarkdownFragmentModel model)
1926
{
2027
// TODO: support multi-layer yaml
2128
var yamlCodeBlockMetadata = ConvertYamlCodeBlock(model.YamlCodeBlock, model.YamlCodeBlockSource);
@@ -51,7 +58,7 @@ internal static Dictionary<string, object> ConvertYamlCodeBlock(string yamlCodeB
5158
}
5259
}
5360

54-
internal static Dictionary<string, object> ConvertContents(List<MarkdownPropertyModel> contents)
61+
internal Dictionary<string, object> ConvertContents(List<MarkdownPropertyModel> contents)
5562
{
5663
var contentsMetadata = new Dictionary<string, object>();
5764
List<OPathSegment> OPathSegments;
@@ -76,8 +83,8 @@ internal static Dictionary<string, object> MergeYamlCodeMetadataWithContentsMeta
7683
{
7784
var metadata = yamlCodeMetadata.Concat(contentsMetadata).GroupBy(p => p.Key);
7885
var metadatasWithSameOPath = from meta in metadata
79-
where meta.Count() > 1
80-
select meta;
86+
where meta.Count() > 1
87+
select meta;
8188
foreach (var meta in metadatasWithSameOPath)
8289
{
8390
Logger.LogWarning(
@@ -88,13 +95,13 @@ where meta.Count() > 1
8895
return metadata.ToDictionary(g => g.Key, g => g.Last().Value);
8996
}
9097

91-
private static void AppendNewObject(List<OPathSegment> OPathSegments, Block codeHeaderBlock, List<Block> propertyValue, Dictionary<string, object> contentsMetadata)
98+
private void AppendNewObject(List<OPathSegment> OPathSegments, Block codeHeaderBlock, List<Block> propertyValue, Dictionary<string, object> contentsMetadata)
9299
{
93100
FindOrCreateObject(contentsMetadata, codeHeaderBlock, OPathSegments, 0, propertyValue,
94101
string.Join("/", OPathSegments.Select(o => o.OriginalSegmentString)));
95102
}
96103

97-
private static void FindOrCreateObject(Dictionary<string, object> currentObject, Block codeHeaderBlock, List<OPathSegment> OPathSegments, int index, List<Block> propertyValue, string originalOPathString)
104+
private void FindOrCreateObject(Dictionary<string, object> currentObject, Block codeHeaderBlock, List<OPathSegment> OPathSegments, int index, List<Block> propertyValue, string originalOPathString)
98105
{
99106
var segment = OPathSegments[index];
100107
if (index == OPathSegments.Count - 1)
@@ -122,20 +129,22 @@ private static void FindOrCreateObject(Dictionary<string, object> currentObject,
122129
}
123130
else
124131
{
125-
var listObject = childObject as List<Dictionary<string, object>>;
132+
var listObject = childObject as List<object>;
126133
if (listObject != null)
127134
{
128135
object value;
129136
var goodItems = (from item in listObject
130-
where item.TryGetValue(segment.Key, out value) && (value as string).Equals(segment.Value)
131-
select item).ToList();
137+
where item is Dictionary<string, object>
138+
&& ((Dictionary<string, object>)item).TryGetValue(segment.Key, out value)
139+
&& ((string)value).Equals(segment.Value)
140+
select (Dictionary<string, object>)item).ToList();
132141
if (goodItems.Count > 0)
133142
{
134143
FindOrCreateObject(goodItems[0], codeHeaderBlock, OPathSegments, ++index, propertyValue, originalOPathString);
135144
}
136145
else
137146
{
138-
nextObject = CreateDictionaryArrayObject(segment)[0];
147+
nextObject = (Dictionary<string, object>)CreateDictionaryArrayObject(segment)[0];
139148
listObject.Add(nextObject);
140149
FindOrCreateObject(nextObject, codeHeaderBlock, OPathSegments, ++index, propertyValue, originalOPathString);
141150
}
@@ -158,15 +167,15 @@ where item.TryGetValue(segment.Key, out value) && (value as string).Equals(segme
158167
else
159168
{
160169
var newObject = CreateDictionaryArrayObject(segment);
161-
nextObject = newObject[0];
170+
nextObject = (Dictionary<string, object>)newObject[0];
162171
currentObject[segment.SegmentName] = newObject;
163172
}
164173

165174
FindOrCreateObject(nextObject, codeHeaderBlock, OPathSegments, ++index, propertyValue, originalOPathString);
166175
}
167176
}
168177

169-
private static void CreateCoreObject(OPathSegment lastSegment, Block codeHeaderBlock, Dictionary<string, object> currentObject, List<Block> propertyValue, string originalOPathString)
178+
private void CreateCoreObject(OPathSegment lastSegment, Block codeHeaderBlock, Dictionary<string, object> currentObject, List<Block> propertyValue, string originalOPathString)
170179
{
171180
if (currentObject.TryGetValue(lastSegment.SegmentName, out object value))
172181
{
@@ -189,14 +198,15 @@ private static void CreateCoreObject(OPathSegment lastSegment, Block codeHeaderB
189198
currentObject[lastSegment.SegmentName] = CreateDocument(propertyValue);
190199
}
191200

192-
private static MarkdownDocument CreateDocument(List<Block> blocks)
201+
private MarkdownDocument CreateDocument(List<Block> blocks)
193202
{
194203
var result = new MarkdownDocument();
195204
foreach (var block in blocks)
196205
{
197206
block.Parent?.Remove(block);
198207
result.Add(block);
199208
}
209+
result.SetData("filePath", _file);
200210
return result;
201211
}
202212

@@ -205,9 +215,9 @@ private static Dictionary<string, object> CreateDictionaryObject(OPathSegment se
205215
return new Dictionary<string, object>();
206216
}
207217

208-
private static List<Dictionary<string, object>> CreateDictionaryArrayObject(OPathSegment segment)
218+
private static List<object> CreateDictionaryArrayObject(OPathSegment segment)
209219
{
210-
var newObject = new List<Dictionary<string, object>>
220+
var newObject = new List<object>
211221
{
212222
new Dictionary<string, object>
213223
{

src/Microsoft.DocAsCode.Build.SchemaDriven/ApplyOverwriteFragments.cs

+91-31
Original file line numberDiff line numberDiff line change
@@ -3,66 +3,126 @@
33

44
namespace Microsoft.DocAsCode.Build.SchemaDriven
55
{
6+
using System;
67
using System.Collections.Generic;
78
using System.Composition;
89
using System.Linq;
910

1011
using Microsoft.DocAsCode.Build.Common;
12+
using Microsoft.DocAsCode.Build.OverwriteDocuments;
1113
using Microsoft.DocAsCode.Common;
14+
using Microsoft.DocAsCode.Exceptions;
15+
using Microsoft.DocAsCode.MarkdigEngine;
1216
using Microsoft.DocAsCode.Plugins;
1317

14-
[Export(nameof(SchemaDrivenDocumentProcessor), typeof(IDocumentBuildStep))]
18+
// [Export(nameof(SchemaDrivenDocumentProcessor), typeof(IDocumentBuildStep))]
19+
// TODO: export to the entire SchemaDrivenDocumentProcessor when incremental is ready
20+
[Export("SchemaDrivenDocumentProcessor.RESTComponentV3", typeof(IDocumentBuildStep))]
21+
[Export("SchemaDrivenDocumentProcessor.RESTOperationV3", typeof(IDocumentBuildStep))]
22+
[Export("SchemaDrivenDocumentProcessor.RESTOperationGroupV3", typeof(IDocumentBuildStep))]
1523
public class ApplyOverwriteFragments : BaseDocumentBuildStep, ISupportIncrementalBuildStep
1624
{
1725
public override string Name => nameof(ApplyOverwriteFragments);
1826

1927
public override int BuildOrder => 0x08;
2028

21-
public virtual void Build(FileModel model, IHostService host)
29+
public override void Build(FileModel model, IHostService host)
2230
{
23-
var overwriteApplier = new OverwriteApplier(host, OverwriteModelType.MarkdownFragments);
24-
25-
var overwriteDocumentModels = model.MarkdownFragmentsModel?.Content as List<OverwriteDocumentModel>;
26-
if (overwriteDocumentModels == null)
31+
if (model.MarkdownFragmentsModel == null)
2732
{
2833
return;
2934
}
35+
if (!(model.MarkdownFragmentsModel.Content is string))
36+
{
37+
var message = "Unable to parse markdown fragments. Expect string content.";
38+
Logger.LogError(message);
39+
throw new DocfxException(message);
40+
}
41+
if (model.MarkdownFragmentsModel.Properties.MarkdigMarkdownService == null || !(model.MarkdownFragmentsModel.Properties.MarkdigMarkdownService is MarkdigMarkdownService))
42+
{
43+
var message = "Unable to find markdig markdown service in file model.";
44+
Logger.LogError(message);
45+
throw new DocfxException(message);
46+
}
47+
if (!(model.Properties.Schema is DocumentSchema))
48+
{
49+
var message = "Unable to find schema in file model.";
50+
Logger.LogError(message);
51+
throw new DocfxException(message);
52+
}
3053

31-
var schema = model.Properties.Schema as DocumentSchema;
32-
using (new LoggerFileScope(model.LocalPathFromRoot))
54+
using (new LoggerFileScope(model.MarkdownFragmentsModel.LocalPathFromRoot))
3355
{
34-
foreach (var overwriteDocumentModel in overwriteDocumentModels)
56+
try
57+
{
58+
BuildCore(model, host);
59+
}
60+
catch (MarkdownFragmentsException ex)
3561
{
36-
var uidDefiniton = model.Uids.Where(s => s.Name == overwriteDocumentModel.Uid).ToList();
37-
if (uidDefiniton.Count == 0)
38-
{
39-
Logger.LogWarning($"Unable to find UidDefinition for Uid {overwriteDocumentModel.Uid}");
40-
}
41-
42-
if (uidDefiniton.Count > 1)
43-
{
44-
Logger.LogWarning($"There are more than one UidDefinitions found for Uid {overwriteDocumentModel.Uid} in lines {string.Join(", ", uidDefiniton.Select(uid => uid.Line).ToList())}");
45-
}
46-
47-
var ud = uidDefiniton[0];
48-
var jsonPointer = new JsonPointer(ud.Path).GetParentPointer();
49-
var schemaForCurrentUid = jsonPointer.FindSchema(schema);
50-
var source = jsonPointer.GetValue(model.Content);
51-
52-
overwriteApplier.MergeContentWithOverwrite(ref source, overwriteDocumentModel.Metadata, ud.Name, string.Empty, schemaForCurrentUid);
62+
Logger.LogWarning(
63+
$"Unable to parse markdown fragments: {ex.Message}",
64+
line: ex.Position == -1 ? null : ex.Position.ToString(),
65+
code: WarningCodes.Overwrite.InvalidMarkdownFragments);
66+
return;
5367
}
68+
}
69+
}
70+
71+
private void BuildCore(FileModel model, IHostService host)
72+
{
73+
var markdownService = (MarkdigMarkdownService)model.MarkdownFragmentsModel.Properties.MarkdigMarkdownService;
74+
var overwriteDocumentModelCreater = new OverwriteDocumentModelCreater(model.File);
75+
var overwriteApplier = new OverwriteApplier(host, OverwriteModelType.MarkdownFragments);
76+
var schema = model.Properties.Schema as DocumentSchema;
77+
List<OverwriteDocumentModel> overwriteDocumentModels;
78+
79+
// 1. string => AST(MarkdownDocument)
80+
var ast = markdownService.Parse((string)model.MarkdownFragmentsModel.Content, model.File);
5481

55-
// 1. Validate schema after the merge
56-
((SchemaDrivenDocumentProcessor)host.Processor).SchemaValidator.Validate(model.Content);
82+
// 2 AST(MarkdownDocument) => MarkdownFragmentModel
83+
var fragments = new MarkdownFragmentsCreater().Create(ast);
84+
85+
// 3. MarkdownFragmentModel => OverwriteDocument
86+
overwriteDocumentModels = fragments.Select(overwriteDocumentModelCreater.Create).ToList();
87+
88+
// 4. Apply schema to OverwriteDocument, and merge with skeyleton YAML object
89+
foreach (var overwriteDocumentModel in overwriteDocumentModels)
90+
{
91+
var uidDefinitons = model.Uids.Where(s => s.Name == overwriteDocumentModel.Uid).ToList();
92+
if (uidDefinitons.Count == 0)
93+
{
94+
throw new DocfxException($"Unable to find UidDefinition for Uid {overwriteDocumentModel.Uid}");
95+
}
96+
if (uidDefinitons.Count > 1)
97+
{
98+
Logger.LogWarning($"There are more than one UidDefinitions found for Uid {overwriteDocumentModel.Uid} in lines {string.Join(", ", uidDefinitons.Select(uid => uid.Line).ToList())}");
99+
}
57100

58-
// 2. Re-export xrefspec after the merge
59-
overwriteApplier.UpdateXrefSpec(model, schema);
101+
var ud = uidDefinitons[0];
102+
var jsonPointer = new JsonPointer(ud.Path).GetParentPointer();
103+
var schemaForCurrentUid = jsonPointer.FindSchema(schema);
104+
var source = jsonPointer.GetValue(model.Content);
105+
overwriteApplier.BuildOverwriteWithSchema(model.MarkdownFragmentsModel, overwriteDocumentModel, schema);
106+
overwriteApplier.MergeContentWithOverwrite(ref source, overwriteDocumentModel.Metadata, ud.Name, string.Empty, schemaForCurrentUid);
60107
}
108+
109+
// 5. Validate schema after the merge
110+
((SchemaDrivenDocumentProcessor)host.Processor).SchemaValidator.Validate(model.Content);
111+
112+
// 6. Re-export xrefspec after the merge
113+
overwriteApplier.UpdateXrefSpec(model, schema);
114+
115+
model.LinkToUids = model.LinkToUids.Union(model.MarkdownFragmentsModel.LinkToUids);
116+
model.LinkToFiles = model.LinkToFiles.Union(model.MarkdownFragmentsModel.LinkToFiles);
117+
model.FileLinkSources = model.FileLinkSources.Merge(model.MarkdownFragmentsModel.FileLinkSources);
118+
model.UidLinkSources = model.UidLinkSources.Merge(model.MarkdownFragmentsModel.UidLinkSources);
119+
model.MarkdownFragmentsModel.Content = overwriteDocumentModels;
61120
}
62121

63122
#region ISupportIncrementalBuildStep Members
64123

65-
public bool CanIncrementalBuild(FileAndType fileAndType) => true;
124+
// TODO: support incremental build
125+
public bool CanIncrementalBuild(FileAndType fileAndType) => false;
66126

67127
public string GetIncrementalContextHash() => null;
68128

src/Microsoft.DocAsCode.Build.SchemaDriven/Microsoft.DocAsCode.Build.SchemaDriven.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<EmbeddedResource Include="..\..\schemas\v1.0\schema.json" Link="schemas\v1.0\schema.json" />
88
</ItemGroup>
99
<ItemGroup>
10+
<ProjectReference Include="..\Microsoft.DocAsCode.Build.OverwriteDocuments\Microsoft.DocAsCode.Build.OverwriteDocuments.csproj" />
1011
<ProjectReference Include="..\Microsoft.DocAsCode.MarkdigEngine\Microsoft.DocAsCode.MarkdigEngine.csproj" />
1112
<ProjectReference Include="..\Microsoft.DocAsCode.MarkdownLite\Microsoft.DocAsCode.MarkdownLite.csproj" />
1213
<ProjectReference Include="..\Microsoft.DocAsCode.Build.Common\Microsoft.DocAsCode.Build.Common.csproj" />

src/Microsoft.DocAsCode.Build.SchemaDriven/OverwriteApplier.cs

+17-12
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ namespace Microsoft.DocAsCode.Build.SchemaDriven
1010
using Microsoft.DocAsCode.Build.SchemaDriven.Processors;
1111
using Microsoft.DocAsCode.Common;
1212
using Microsoft.DocAsCode.DataContracts.Common;
13+
using Microsoft.DocAsCode.MarkdigEngine;
1314
using Microsoft.DocAsCode.Plugins;
1415

1516
public class OverwriteApplier
@@ -85,8 +86,9 @@ public object BuildOverwriteWithSchema(FileModel owModel, OverwriteDocumentModel
8586
dynamic overwriteObject = ConvertToObjectHelper.ConvertToDynamic(overwrite.Metadata);
8687
overwriteObject.uid = overwrite.Uid;
8788
var overwriteModel = new FileModel(owModel.FileAndType, overwriteObject, owModel.OriginalFileAndType);
88-
89-
var context = new ProcessContext(_host, overwriteModel);
89+
var context = (((IDictionary<string, object>)(owModel.Properties)).TryGetValue("MarkdigMarkdownService", out var service))
90+
? new ProcessContext(_host, overwriteModel, null, (MarkdigMarkdownService)service)
91+
: new ProcessContext(_host, overwriteModel);
9092
if (_overwriteModelType == OverwriteModelType.Classic)
9193
{
9294
context.ContentAnchorParser = new ContentAnchorParser(overwrite.Conceptual);
@@ -99,18 +101,21 @@ public object BuildOverwriteWithSchema(FileModel owModel, OverwriteDocumentModel
99101
}
100102

101103
// add SouceDetail back to transformed, in week type
102-
transformed[Constants.PropertyName.Documentation] = new Dictionary<string, object>
104+
if (overwrite.Documentation != null)
103105
{
104-
["remote"] = overwrite.Documentation.Remote == null ? null : new Dictionary<string, object>
106+
transformed[Constants.PropertyName.Documentation] = new Dictionary<string, object>
105107
{
106-
["path"] = overwrite.Documentation.Remote.RelativePath,
107-
["branch"] = overwrite.Documentation.Remote.RemoteBranch,
108-
["repo"] = overwrite.Documentation.Remote.RemoteRepositoryUrl,
109-
}
110-
["path"] = overwrite.Documentation?.Path,
111-
["startLine"] = overwrite.Documentation?.StartLine ?? 0,
112-
["endLine"] = overwrite.Documentation?.EndLine ?? 0,
113-
};
108+
["remote"] = overwrite.Documentation.Remote == null ? null : new Dictionary<string, object>
109+
{
110+
["path"] = overwrite.Documentation.Remote.RelativePath,
111+
["branch"] = overwrite.Documentation.Remote.RemoteBranch,
112+
["repo"] = overwrite.Documentation.Remote.RemoteRepositoryUrl,
113+
}
114+
["path"] = overwrite.Documentation?.Path,
115+
["startLine"] = overwrite.Documentation?.StartLine ?? 0,
116+
["endLine"] = overwrite.Documentation?.EndLine ?? 0,
117+
};
118+
}
114119

115120
owModel.LinkToUids = owModel.LinkToUids.Union((context.UidLinkSources).Keys);
116121
owModel.LinkToFiles = owModel.LinkToFiles.Union((context.FileLinkSources).Keys);

src/Microsoft.DocAsCode.Build.SchemaDriven/Processors/ProcessContext.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ public T GetModel<T>()
4545
}
4646

4747
public ProcessContext(IHostService hs, FileModel fm, IDocumentBuildContext bc = null)
48+
: this(hs, fm, bc, null) { }
49+
50+
public ProcessContext(IHostService hs, FileModel fm, IDocumentBuildContext bc, MarkdigMarkdownService markdigMarkdownService = null)
4851
{
4952
_model = fm;
5053
OriginalFileAndType = fm.OriginalFileAndType;
@@ -67,10 +70,7 @@ public ProcessContext(IHostService hs, FileModel fm, IDocumentBuildContext bc =
6770

6871
Host = hs;
6972
BuildContext = bc;
70-
if (((IDictionary<string, object>)(fm.Properties)).TryGetValue("MarkdigMarkdownService", out var service))
71-
{
72-
MarkdigMarkdownService = (MarkdigMarkdownService)service;
73-
}
73+
MarkdigMarkdownService = markdigMarkdownService;
7474
}
7575
}
7676
}

0 commit comments

Comments
 (0)