Skip to content

Commit b6d0b88

Browse files
Fix Tree and node view diff and add more test cases for parser (#8880)
* Fix Tree and node view diff and add more test cases for parser
1 parent 4f63562 commit b6d0b88

9 files changed

Lines changed: 194 additions & 69 deletions

File tree

src/dotnet/APIView/APIView/Model/V2/ReviewLine.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ public class ReviewLine
3939
/// This is set if a line is end of context. For e.g. end of a class or name space line "}"
4040
/// </summary>
4141
public bool? IsContextEndLine { get; set; }
42+
/// <summary>
43+
/// This is to set a line as related to another line. So when a related line is hidden in node or tree view then current line will also be hidden
44+
/// for e.g. an attribute line or notation line will be set as related to that API or class line.
45+
/// </summary>
46+
public string RelatedToLine { get; set; }
4247
// Following properties are helper methods that's used to render review lines to UI required format.
4348
[JsonIgnore]
4449
public DiffKind DiffKind { get; set; } = DiffKind.NoneDiff;

src/dotnet/APIView/APIViewWeb/Helpers/CodeFileHelpers.cs

Lines changed: 37 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -21,32 +21,27 @@ public static async Task<CodePanelData> GenerateCodePanelDataAsync(CodePanelRawD
2121

2222
// Create root node
2323
var rootNodeId = "root";
24-
if (!codePanelData.NodeMetaDataObj.ContainsKey(rootNodeId))
25-
{
26-
codePanelData.NodeMetaDataObj.TryAdd(rootNodeId, new CodePanelNodeMetaData());
27-
}
24+
codePanelData.NodeMetaDataObj[rootNodeId] = new CodePanelNodeMetaData();
2825
var codeFile = codePanelRawData.activeRevisionCodeFile;
29-
codePanelData.NodeMetaDataObj[rootNodeId].NavigationTreeNode = CreateRootNode($"{codeFile.PackageName} {codeFile.PackageVersion}", rootNodeId);
26+
codePanelData.AddNavigation(rootNodeId, CreateRootNode($"{codeFile.PackageName} {codeFile.PackageVersion}", rootNodeId));
3027

3128
//Collect documentation lines from active revision
32-
Dictionary<string, List<CodePanelRowData>> documentationMap = new Dictionary<string, List<CodePanelRowData>>();
33-
Dictionary<string, List<CodePanelRowData>> diffDdocumentationMap = new Dictionary<string, List<CodePanelRowData>>();
34-
CollectDocumentationLines(codeFile.ReviewLines, documentationMap, 1, "root");
29+
CollectDocumentationLines(codeFile.ReviewLines, codePanelData.ActiveDocumentationMap, 1, "root");
3530

3631
//Calculate the diff if diff revision code file is present
3732
if (codePanelRawData.diffRevisionCodeFile != null)
3833
{
3934
var diffLines = codePanelRawData.diffRevisionCodeFile.ReviewLines;
40-
CollectDocumentationLines(diffLines, diffDdocumentationMap, 1, "root", true);
35+
CollectDocumentationLines(diffLines, codePanelData.DiffDocumentationMap, 1, "root", true);
4136
// Check if diff is required for active revision and diff revision to avoid unnecessary diff calculation
4237
bool hasSameApis = AreCodeFilesSame(codePanelRawData.activeRevisionCodeFile, codePanelRawData.diffRevisionCodeFile);
4338
if(!hasSameApis)
4439
{
4540
reviewLines = FindDiff(reviewLines, codePanelRawData.diffRevisionCodeFile.ReviewLines);
4641
// Remap nodeIdHashed for documentation to diff adjusted nodeIdHashed so that documentation is correctly listed on review.
47-
RemapDocumentationLines(reviewLines, documentationMap);
42+
RemapDocumentationLines(reviewLines, codePanelData.ActiveDocumentationMap);
4843
// Remap documentation is diff revision using node hash ID for active revision. We don't need to show documentation if it's node itself is not present in active revision.
49-
RemapDocumentationLines(reviewLines, diffDdocumentationMap);
44+
RemapDocumentationLines(reviewLines, codePanelData.DiffDocumentationMap);
5045
codePanelData.HasDiff = true;
5146
}
5247
else
@@ -56,56 +51,60 @@ public static async Task<CodePanelData> GenerateCodePanelDataAsync(CodePanelRawD
5651
}
5752

5853
int idx = 0;
59-
string previousNodeHashId = "";
60-
foreach(var reviewLine in reviewLines)
54+
string nodeHashId = "";
55+
Dictionary<string,string> relatedLineMap = new Dictionary<string, string>();
56+
foreach (var reviewLine in reviewLines)
6157
{
6258
if (reviewLine.IsDocumentation) continue;
63-
previousNodeHashId = await BuildAPITree(codePanelData: codePanelData, codePanelRawData: codePanelRawData, reviewLine: reviewLines[idx],
64-
parentNodeIdHashed: rootNodeId, nodePositionAtLevel: idx, documentationMap: documentationMap, diffDocumentationMap: diffDdocumentationMap, prevNodeHashId: previousNodeHashId);
65-
idx++;
59+
nodeHashId = await BuildAPITree(codePanelData: codePanelData, codePanelRawData: codePanelRawData, reviewLine: reviewLines[idx],
60+
parentNodeIdHashed: rootNodeId, nodePositionAtLevel: idx, prevNodeHashId: nodeHashId, relatedLineMap: relatedLineMap);
61+
idx++;
6662
}
67-
return codePanelData;
68-
}
69-
7063

71-
// Creates tree reference for code line nodes in the review. This tree helps to render the code panel in the UI.
72-
private static void ConnectNodeToParent(CodePanelData codePanelData, string nodeIdHashed, string parentNodeIdHashed, int nodePosition)
73-
{
74-
if (!codePanelData.NodeMetaDataObj.ContainsKey(nodeIdHashed))
64+
//Set related line's node ID hashed in tree metadata
65+
foreach(var key in relatedLineMap.Keys)
7566
{
76-
codePanelData.NodeMetaDataObj.TryAdd(nodeIdHashed, new CodePanelNodeMetaData());
67+
codePanelData.SetLineAsRelated(key, relatedLineMap[key]);
7768
}
78-
codePanelData.NodeMetaDataObj[nodeIdHashed].ParentNodeIdHashed = parentNodeIdHashed;
79-
codePanelData.NodeMetaDataObj[parentNodeIdHashed].ChildrenNodeIdsInOrderObj.TryAdd(nodePosition, nodeIdHashed);
69+
return codePanelData;
8070
}
8171

72+
8273
private static async Task<string> BuildAPITree(CodePanelData codePanelData, CodePanelRawData codePanelRawData, ReviewLine reviewLine, string parentNodeIdHashed, int nodePositionAtLevel,
83-
Dictionary<string, List<CodePanelRowData>> documentationMap, Dictionary<string, List<CodePanelRowData>> diffDocumentationMap, string prevNodeHashId, int indent = 1)
74+
string prevNodeHashId, Dictionary<string, string> relatedLineMap, int indent = 1)
8475
{
8576
//Create hashed node ID for current review line(node)
8677
var nodeIdHashed = reviewLine.GetTokenNodeIdHash(parentNodeIdHashed, nodePositionAtLevel);
78+
codePanelData.AddLineIdNodeHashMapping(reviewLine.LineId, nodeIdHashed);
8779
//Create parent and child tree reference map
88-
ConnectNodeToParent(codePanelData, nodeIdHashed, parentNodeIdHashed, nodePositionAtLevel);
89-
80+
codePanelData.ConnectNodeToParent(nodeIdHashed, parentNodeIdHashed, nodePositionAtLevel);
81+
82+
//Populate the map of nodeHashId to it's related line ID
83+
// This is later used to set related line's node ID hashed in tree metadata since related tree node is built after current node.
84+
if (!string.IsNullOrEmpty(reviewLine.RelatedToLine))
85+
{
86+
relatedLineMap[nodeIdHashed] = reviewLine.RelatedToLine;
87+
}
88+
9089
// Build current code line node
91-
BuildNodeTokens(codePanelData, codePanelRawData, reviewLine, nodeIdHashed, indent, documentationMap, diffDocumentationMap);
90+
BuildNodeTokens(codePanelData, codePanelRawData, reviewLine, nodeIdHashed, indent);
9291

9392
//Create navigation node for current line
9493
var navTreeNode = CreateNavigationNode(reviewLine, nodeIdHashed);
9594
if (navTreeNode != null)
9695
{
97-
codePanelData.NodeMetaDataObj[nodeIdHashed].NavigationTreeNode = navTreeNode;
96+
codePanelData.AddNavigation(nodeIdHashed, navTreeNode);
9897
}
9998

10099
// Process all child lines
101100
int idx = 0;
102-
string prevChildNodeHashId = "";
101+
string childNodeHashId = "";
103102
foreach (var childLine in reviewLine.Children)
104103
{
105104
if (childLine.IsDocumentation) continue;
106105

107-
prevChildNodeHashId = await BuildAPITree(codePanelData: codePanelData, codePanelRawData: codePanelRawData, reviewLine: childLine,
108-
parentNodeIdHashed: nodeIdHashed, nodePositionAtLevel: idx, documentationMap, diffDocumentationMap, prevNodeHashId: prevChildNodeHashId, indent: indent + 1);
106+
childNodeHashId = await BuildAPITree(codePanelData: codePanelData, codePanelRawData: codePanelRawData, reviewLine: childLine,
107+
parentNodeIdHashed: nodeIdHashed, nodePositionAtLevel: idx, prevNodeHashId: childNodeHashId, relatedLineMap: relatedLineMap, indent: indent + 1);
109108
idx++;
110109
};
111110

@@ -173,17 +172,16 @@ private static NavigationTreeNode CreateRootNode(string rootName, string nodeIdH
173172
return navTreeNode;
174173
}
175174

176-
private static void BuildNodeTokens(CodePanelData codePanelData, CodePanelRawData codePanelRawData, ReviewLine reviewLine, string nodeIdHashed, int indent,
177-
Dictionary<string, List<CodePanelRowData>> documentationMap, Dictionary<string, List<CodePanelRowData>> diffDocumentationMap)
175+
private static void BuildNodeTokens(CodePanelData codePanelData, CodePanelRawData codePanelRawData, ReviewLine reviewLine, string nodeIdHashed, int indent)
178176
{
179177
// Generate code line row
180178
var codePanelRow = GetCodePanelRowData(reviewLine, nodeIdHashed, indent);
181179

182180
// Add documentation rows to code panel data
183-
if (documentationMap.ContainsKey(nodeIdHashed))
181+
if (codePanelData.ActiveDocumentationMap.ContainsKey(nodeIdHashed))
184182
{
185-
var activeDocLines = documentationMap[nodeIdHashed];
186-
var diffDocLines = diffDocumentationMap.ContainsKey(nodeIdHashed) ? diffDocumentationMap[nodeIdHashed] : new List<CodePanelRowData>();
183+
var activeDocLines = codePanelData.ActiveDocumentationMap[nodeIdHashed];
184+
var diffDocLines = codePanelData.DiffDocumentationMap.ContainsKey(nodeIdHashed) ? codePanelData.DiffDocumentationMap[nodeIdHashed] : new List<CodePanelRowData>();
187185
var docLines = activeDocLines.InterleavedUnion(diffDocLines);
188186
var docsIntersect = new HashSet<CodePanelRowData>(diffDocLines.Intersect(activeDocLines));
189187
var activeDocs = new HashSet<CodePanelRowData>(activeDocLines);

src/dotnet/APIView/APIViewWeb/LeanModels/CodePanelModels.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ public class CodePanelNodeMetaData
9090
public bool IsNodeWithDiffInDescendants { get; set; }
9191
public bool IsNodeWithNoneDocDiffInDescendants { get; set; }
9292
public string BottomTokenNodeIdHash { get; set; }
93+
public string RelatedNodeIdHash { get; set; }
9394
}
9495

9596
public class CodePanelData
@@ -98,5 +99,55 @@ public class CodePanelData
9899
public Dictionary<string, CodePanelNodeMetaData> NodeMetaDataObj { get; set; } = new Dictionary<string, CodePanelNodeMetaData>();
99100
public Dictionary<string, CodePanelNodeMetaData> NodeMetaData => NodeMetaDataObj.Count > 0 ? NodeMetaDataObj : null;
100101
public bool HasDiff { get; set; } = false;
102+
[JsonIgnore]
103+
public Dictionary<string, string> LineIdToNodeIdHashed { get; set; } = new Dictionary<string, string>();
104+
[JsonIgnore]
105+
public Dictionary<string, List<CodePanelRowData>> ActiveDocumentationMap { get; set; } = new Dictionary<string, List<CodePanelRowData>>();
106+
[JsonIgnore]
107+
public Dictionary<string, List<CodePanelRowData>> DiffDocumentationMap { get; set; } = new Dictionary<string, List<CodePanelRowData>>();
108+
109+
public void AddLineIdNodeHashMapping(string lineId, string nodeId)
110+
{
111+
if (!string.IsNullOrEmpty(lineId) && !string.IsNullOrEmpty(nodeId))
112+
{
113+
LineIdToNodeIdHashed[lineId] = nodeId;
114+
}
115+
}
116+
117+
public string GetNodeIdHashFromLineId(string lineId)
118+
{
119+
if (!string.IsNullOrEmpty(lineId) && LineIdToNodeIdHashed.ContainsKey(lineId))
120+
{
121+
return LineIdToNodeIdHashed[lineId];
122+
}
123+
return string.Empty;
124+
}
125+
126+
public void SetLineAsRelated(string nodeIdHashed, string relatedLine)
127+
{
128+
var relatedNodeHashId = GetNodeIdHashFromLineId(relatedLine);
129+
if (!string.IsNullOrEmpty(relatedNodeHashId) && NodeMetaDataObj.ContainsKey(nodeIdHashed))
130+
{
131+
NodeMetaDataObj[nodeIdHashed].RelatedNodeIdHash = relatedNodeHashId;
132+
}
133+
}
134+
135+
public void AddNavigation(string nodeIdHash, NavigationTreeNode node)
136+
{
137+
if (!string.IsNullOrEmpty(nodeIdHash) && node != null)
138+
{
139+
NodeMetaDataObj[nodeIdHash].NavigationTreeNode = node;
140+
}
141+
}
142+
143+
public void ConnectNodeToParent(string nodeIdHashed, string parentNodeIdHashed, int nodePosition)
144+
{
145+
if (!NodeMetaDataObj.ContainsKey(nodeIdHashed))
146+
{
147+
NodeMetaDataObj.TryAdd(nodeIdHashed, new CodePanelNodeMetaData());
148+
}
149+
NodeMetaDataObj[nodeIdHashed].ParentNodeIdHashed = parentNodeIdHashed;
150+
NodeMetaDataObj[parentNodeIdHashed].ChildrenNodeIdsInOrderObj.TryAdd(nodePosition, nodeIdHashed);
151+
}
101152
}
102153
}

src/dotnet/APIView/ClientSPA/src/app/_models/codePanelModels.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export class CodePanelNodeMetaData {
7272
isNodeWithNoneDocDiffInDescendants : boolean;
7373
bottomTokenNodeIdHash: string;
7474
isProcessed: boolean;
75+
relatedNodeIdHash: string;
7576

7677
constructor() {
7778
this.documentation = [];
@@ -86,5 +87,6 @@ export class CodePanelNodeMetaData {
8687
this.isNodeWithNoneDocDiffInDescendants = false;
8788
this.bottomTokenNodeIdHash = '';
8889
this.isProcessed = false;
90+
this.relatedNodeIdHash = '';
8991
}
9092
}

src/dotnet/APIView/ClientSPA/src/app/_workers/apitree-builder.worker.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ describe('API Tree Builder', () => {
6464
apiTreeBuilder.onmessage = ({ data }) => {
6565
if (data.directive === ReviewPageWorkerMessageDirective.UpdateCodePanelRowData) {
6666
const codePanelRowData = data.payload as CodePanelRowData[];
67-
expect(codePanelRowData.length).toBe(21);
67+
expect(codePanelRowData.length).toBe(24);
6868
const linesWithDiff = codePanelRowData.filter(row => row.diffKind === 'removed' || row.diffKind === 'added');
6969
expect(linesWithDiff.length).toBe(6);
7070
}
@@ -95,7 +95,7 @@ describe('API Tree Builder', () => {
9595
apiTreeBuilder.onmessage = ({ data }) => {
9696
if (data.directive === ReviewPageWorkerMessageDirective.UpdateCodePanelRowData) {
9797
const codePanelRowData = data.payload as CodePanelRowData[];
98-
expect(codePanelRowData.length).toBe(171);
98+
expect(codePanelRowData.length).toBe(174);
9999
const linesWithDiff = codePanelRowData.filter(row => row.diffKind === 'removed' || row.diffKind === 'added');
100100
expect(linesWithDiff.length).toBe(152);
101101
}
@@ -312,7 +312,7 @@ describe('API Tree Builder', () => {
312312
apiTreeBuilder.onmessage = ({ data }) => {
313313
if (data.directive === ReviewPageWorkerMessageDirective.UpdateCodePanelRowData) {
314314
const codePanelRowData = data.payload as CodePanelRowData[];
315-
expect(codePanelRowData.length).toBe(20);
315+
expect(codePanelRowData.length).toBe(21);
316316
const linesWithDiff = codePanelRowData.filter(row => row.diffKind === 'removed' || row.diffKind === 'added');
317317
expect(linesWithDiff.length).toBe(9);
318318
}

src/dotnet/APIView/ClientSPA/src/app/_workers/apitree-builder.worker.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ let lineNumber: number = 0;
1515
let diffLineNumber: number = 0;
1616
let toggleDocumentationClassPart = "bi-arrow-up-square";
1717
let hasHiddenAPI: boolean = false;
18+
let visibleNodes: Set<string> = new Set<string>();
19+
let addPostDiffContext: boolean = false;
1820

1921
addEventListener('message', ({ data }) => {
2022
if (data instanceof ArrayBuffer) {
@@ -56,6 +58,8 @@ addEventListener('message', ({ data }) => {
5658
navigationTree = [];
5759
diffBuffer = [];
5860
apiTreeBuilderData = null;
61+
visibleNodes = new Set<string>();
62+
addPostDiffContext = false;
5963
}
6064
else {
6165
apiTreeBuilderData = data;
@@ -71,6 +75,17 @@ function buildCodePanelRows(nodeIdHashed: string, navigationTree: NavigationTree
7175
if(node.isProcessed)
7276
return;
7377

78+
//If current node is related line attribute and then related node is not modified then skip current node in tree and node view
79+
if (node.relatedNodeIdHash && !node.isNodeWithDiff && !node.isNodeWithDiffInDescendants &&
80+
(apiTreeBuilderData?.diffStyle == TREE_DIFF_STYLE || apiTreeBuilderData?.diffStyle == NODE_DIFF_STYLE))
81+
{
82+
let relatedNode = codePanelData?.nodeMetaData[node.relatedNodeIdHash]!;
83+
if (!relatedNode.isNodeWithDiff && !node.isNodeWithDiffInDescendants && !visibleNodes.has(node.relatedNodeIdHash))
84+
{
85+
return;
86+
}
87+
}
88+
7489
let buildNode = true;
7590
let buildChildren = true;
7691
let addNodeToBuffer = false
@@ -80,7 +95,7 @@ function buildCodePanelRows(nodeIdHashed: string, navigationTree: NavigationTree
8095
buildNode = false;
8196
buildChildren = false;
8297
}
83-
98+
8499
if (!buildNode && (!node.childrenNodeIdsInOrder || Object.keys(node.childrenNodeIdsInOrder).length === 0) &&
85100
(apiTreeBuilderData?.diffStyle !== NODE_DIFF_STYLE || node.isNodeWithDiff)) {
86101
buildNode = true;
@@ -100,6 +115,7 @@ function buildCodePanelRows(nodeIdHashed: string, navigationTree: NavigationTree
100115

101116
if ((!node.childrenNodeIdsInOrder || Object.keys(node.childrenNodeIdsInOrder).length === 0) && node.isNodeWithDiff) {
102117
codePanelRowData.push(...diffBuffer);
118+
diffBuffer.map(row => visibleNodes.add(row.nodeIdHashed));
103119
diffBuffer = [];
104120
}
105121

@@ -127,8 +143,18 @@ function buildCodePanelRows(nodeIdHashed: string, navigationTree: NavigationTree
127143
setLineNumber(codeLine);
128144
if (buildNode) {
129145
codePanelRowData.push(codeLine);
146+
visibleNodes.add(nodeIdHashed);
147+
addPostDiffContext = true;
130148
}
131149
if (addNodeToBuffer) {
150+
// We should add immediate 3 lines as context post a changed line
151+
if (addPostDiffContext && diffBuffer.length === 3)
152+
{
153+
codePanelRowData.push(...diffBuffer);
154+
diffBuffer.map(row => visibleNodes.add(row.nodeIdHashed));
155+
diffBuffer = [];
156+
addPostDiffContext = false;
157+
}
132158
diffBuffer.push(codeLine);
133159
addJustDiffBuffer();
134160
}
@@ -181,6 +207,7 @@ function buildCodePanelRows(nodeIdHashed: string, navigationTree: NavigationTree
181207
setLineNumber(codeLine);
182208
if (buildNode) {
183209
codePanelRowData.push(codeLine);
210+
visibleNodes.add(codeLine.nodeIdHashed);
184211
}
185212
});
186213
}

0 commit comments

Comments
 (0)