Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/dotnet/APIView/APIView/Model/V2/ReviewLine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ public class ReviewLine
/// This is set if a line is end of context. For e.g. end of a class or name space line "}"
/// </summary>
public bool? IsContextEndLine { get; set; }
/// <summary>
/// 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
/// for e.g. an attribute line or notation line will be set as related to that API or class line.
/// </summary>
public string RelatedToLine { get; set; }
Comment thread
praveenkuttappan marked this conversation as resolved.
// Following properties are helper methods that's used to render review lines to UI required format.
[JsonIgnore]
public DiffKind DiffKind { get; set; } = DiffKind.NoneDiff;
Expand Down
76 changes: 37 additions & 39 deletions src/dotnet/APIView/APIViewWeb/Helpers/CodeFileHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,32 +21,27 @@ public static async Task<CodePanelData> GenerateCodePanelDataAsync(CodePanelRawD

// Create root node
var rootNodeId = "root";
if (!codePanelData.NodeMetaDataObj.ContainsKey(rootNodeId))
{
codePanelData.NodeMetaDataObj.TryAdd(rootNodeId, new CodePanelNodeMetaData());
}
codePanelData.NodeMetaDataObj[rootNodeId] = new CodePanelNodeMetaData();
var codeFile = codePanelRawData.activeRevisionCodeFile;
codePanelData.NodeMetaDataObj[rootNodeId].NavigationTreeNode = CreateRootNode($"{codeFile.PackageName} {codeFile.PackageVersion}", rootNodeId);
codePanelData.AddNavigation(rootNodeId, CreateRootNode($"{codeFile.PackageName} {codeFile.PackageVersion}", rootNodeId));

//Collect documentation lines from active revision
Dictionary<string, List<CodePanelRowData>> documentationMap = new Dictionary<string, List<CodePanelRowData>>();
Dictionary<string, List<CodePanelRowData>> diffDdocumentationMap = new Dictionary<string, List<CodePanelRowData>>();
CollectDocumentationLines(codeFile.ReviewLines, documentationMap, 1, "root");
CollectDocumentationLines(codeFile.ReviewLines, codePanelData.ActiveDocumentationMap, 1, "root");

//Calculate the diff if diff revision code file is present
if (codePanelRawData.diffRevisionCodeFile != null)
{
var diffLines = codePanelRawData.diffRevisionCodeFile.ReviewLines;
CollectDocumentationLines(diffLines, diffDdocumentationMap, 1, "root", true);
CollectDocumentationLines(diffLines, codePanelData.DiffDocumentationMap, 1, "root", true);
// Check if diff is required for active revision and diff revision to avoid unnecessary diff calculation
bool hasSameApis = AreCodeFilesSame(codePanelRawData.activeRevisionCodeFile, codePanelRawData.diffRevisionCodeFile);
if(!hasSameApis)
{
reviewLines = FindDiff(reviewLines, codePanelRawData.diffRevisionCodeFile.ReviewLines);
// Remap nodeIdHashed for documentation to diff adjusted nodeIdHashed so that documentation is correctly listed on review.
RemapDocumentationLines(reviewLines, documentationMap);
RemapDocumentationLines(reviewLines, codePanelData.ActiveDocumentationMap);
// 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.
RemapDocumentationLines(reviewLines, diffDdocumentationMap);
RemapDocumentationLines(reviewLines, codePanelData.DiffDocumentationMap);
codePanelData.HasDiff = true;
}
else
Expand All @@ -56,56 +51,60 @@ public static async Task<CodePanelData> GenerateCodePanelDataAsync(CodePanelRawD
}

int idx = 0;
string previousNodeHashId = "";
foreach(var reviewLine in reviewLines)
string nodeHashId = "";
Dictionary<string,string> relatedLineMap = new Dictionary<string, string>();
foreach (var reviewLine in reviewLines)
{
if (reviewLine.IsDocumentation) continue;
previousNodeHashId = await BuildAPITree(codePanelData: codePanelData, codePanelRawData: codePanelRawData, reviewLine: reviewLines[idx],
parentNodeIdHashed: rootNodeId, nodePositionAtLevel: idx, documentationMap: documentationMap, diffDocumentationMap: diffDdocumentationMap, prevNodeHashId: previousNodeHashId);
idx++;
nodeHashId = await BuildAPITree(codePanelData: codePanelData, codePanelRawData: codePanelRawData, reviewLine: reviewLines[idx],
parentNodeIdHashed: rootNodeId, nodePositionAtLevel: idx, prevNodeHashId: nodeHashId, relatedLineMap: relatedLineMap);
idx++;
}
return codePanelData;
}


// Creates tree reference for code line nodes in the review. This tree helps to render the code panel in the UI.
private static void ConnectNodeToParent(CodePanelData codePanelData, string nodeIdHashed, string parentNodeIdHashed, int nodePosition)
{
if (!codePanelData.NodeMetaDataObj.ContainsKey(nodeIdHashed))
//Set related line's node ID hashed in tree metadata
foreach(var key in relatedLineMap.Keys)
{
codePanelData.NodeMetaDataObj.TryAdd(nodeIdHashed, new CodePanelNodeMetaData());
codePanelData.SetLineAsRelated(key, relatedLineMap[key]);
}
codePanelData.NodeMetaDataObj[nodeIdHashed].ParentNodeIdHashed = parentNodeIdHashed;
codePanelData.NodeMetaDataObj[parentNodeIdHashed].ChildrenNodeIdsInOrderObj.TryAdd(nodePosition, nodeIdHashed);
return codePanelData;
}


private static async Task<string> BuildAPITree(CodePanelData codePanelData, CodePanelRawData codePanelRawData, ReviewLine reviewLine, string parentNodeIdHashed, int nodePositionAtLevel,
Dictionary<string, List<CodePanelRowData>> documentationMap, Dictionary<string, List<CodePanelRowData>> diffDocumentationMap, string prevNodeHashId, int indent = 1)
string prevNodeHashId, Dictionary<string, string> relatedLineMap, int indent = 1)
{
//Create hashed node ID for current review line(node)
var nodeIdHashed = reviewLine.GetTokenNodeIdHash(parentNodeIdHashed, nodePositionAtLevel);
codePanelData.AddLineIdNodeHashMapping(reviewLine.LineId, nodeIdHashed);
//Create parent and child tree reference map
ConnectNodeToParent(codePanelData, nodeIdHashed, parentNodeIdHashed, nodePositionAtLevel);

codePanelData.ConnectNodeToParent(nodeIdHashed, parentNodeIdHashed, nodePositionAtLevel);

//Populate the map of nodeHashId to it's related line ID
// This is later used to set related line's node ID hashed in tree metadata since related tree node is built after current node.
if (!string.IsNullOrEmpty(reviewLine.RelatedToLine))
{
relatedLineMap[nodeIdHashed] = reviewLine.RelatedToLine;
}

// Build current code line node
BuildNodeTokens(codePanelData, codePanelRawData, reviewLine, nodeIdHashed, indent, documentationMap, diffDocumentationMap);
BuildNodeTokens(codePanelData, codePanelRawData, reviewLine, nodeIdHashed, indent);

//Create navigation node for current line
var navTreeNode = CreateNavigationNode(reviewLine, nodeIdHashed);
if (navTreeNode != null)
{
codePanelData.NodeMetaDataObj[nodeIdHashed].NavigationTreeNode = navTreeNode;
codePanelData.AddNavigation(nodeIdHashed, navTreeNode);
}

// Process all child lines
int idx = 0;
string prevChildNodeHashId = "";
string childNodeHashId = "";
foreach (var childLine in reviewLine.Children)
{
if (childLine.IsDocumentation) continue;

prevChildNodeHashId = await BuildAPITree(codePanelData: codePanelData, codePanelRawData: codePanelRawData, reviewLine: childLine,
parentNodeIdHashed: nodeIdHashed, nodePositionAtLevel: idx, documentationMap, diffDocumentationMap, prevNodeHashId: prevChildNodeHashId, indent: indent + 1);
childNodeHashId = await BuildAPITree(codePanelData: codePanelData, codePanelRawData: codePanelRawData, reviewLine: childLine,
parentNodeIdHashed: nodeIdHashed, nodePositionAtLevel: idx, prevNodeHashId: childNodeHashId, relatedLineMap: relatedLineMap, indent: indent + 1);
idx++;
};

Expand Down Expand Up @@ -173,17 +172,16 @@ private static NavigationTreeNode CreateRootNode(string rootName, string nodeIdH
return navTreeNode;
}

private static void BuildNodeTokens(CodePanelData codePanelData, CodePanelRawData codePanelRawData, ReviewLine reviewLine, string nodeIdHashed, int indent,
Dictionary<string, List<CodePanelRowData>> documentationMap, Dictionary<string, List<CodePanelRowData>> diffDocumentationMap)
private static void BuildNodeTokens(CodePanelData codePanelData, CodePanelRawData codePanelRawData, ReviewLine reviewLine, string nodeIdHashed, int indent)
{
// Generate code line row
var codePanelRow = GetCodePanelRowData(reviewLine, nodeIdHashed, indent);

// Add documentation rows to code panel data
if (documentationMap.ContainsKey(nodeIdHashed))
if (codePanelData.ActiveDocumentationMap.ContainsKey(nodeIdHashed))
{
var activeDocLines = documentationMap[nodeIdHashed];
var diffDocLines = diffDocumentationMap.ContainsKey(nodeIdHashed) ? diffDocumentationMap[nodeIdHashed] : new List<CodePanelRowData>();
var activeDocLines = codePanelData.ActiveDocumentationMap[nodeIdHashed];
var diffDocLines = codePanelData.DiffDocumentationMap.ContainsKey(nodeIdHashed) ? codePanelData.DiffDocumentationMap[nodeIdHashed] : new List<CodePanelRowData>();
var docLines = activeDocLines.InterleavedUnion(diffDocLines);
var docsIntersect = new HashSet<CodePanelRowData>(diffDocLines.Intersect(activeDocLines));
var activeDocs = new HashSet<CodePanelRowData>(activeDocLines);
Expand Down
51 changes: 51 additions & 0 deletions src/dotnet/APIView/APIViewWeb/LeanModels/CodePanelModels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ public class CodePanelNodeMetaData
public bool IsNodeWithDiffInDescendants { get; set; }
public bool IsNodeWithNoneDocDiffInDescendants { get; set; }
public string BottomTokenNodeIdHash { get; set; }
public string RelatedNodeIdHash { get; set; }
}

public class CodePanelData
Expand All @@ -98,5 +99,55 @@ public class CodePanelData
public Dictionary<string, CodePanelNodeMetaData> NodeMetaDataObj { get; set; } = new Dictionary<string, CodePanelNodeMetaData>();
public Dictionary<string, CodePanelNodeMetaData> NodeMetaData => NodeMetaDataObj.Count > 0 ? NodeMetaDataObj : null;
public bool HasDiff { get; set; } = false;
[JsonIgnore]
public Dictionary<string, string> LineIdToNodeIdHashed { get; set; } = new Dictionary<string, string>();
[JsonIgnore]
public Dictionary<string, List<CodePanelRowData>> ActiveDocumentationMap { get; set; } = new Dictionary<string, List<CodePanelRowData>>();
[JsonIgnore]
public Dictionary<string, List<CodePanelRowData>> DiffDocumentationMap { get; set; } = new Dictionary<string, List<CodePanelRowData>>();

public void AddLineIdNodeHashMapping(string lineId, string nodeId)
{
if (!string.IsNullOrEmpty(lineId) && !string.IsNullOrEmpty(nodeId))
{
LineIdToNodeIdHashed[lineId] = nodeId;
}
}

public string GetNodeIdHashFromLineId(string lineId)
{
if (!string.IsNullOrEmpty(lineId) && LineIdToNodeIdHashed.ContainsKey(lineId))
{
return LineIdToNodeIdHashed[lineId];
}
return string.Empty;
}

public void SetLineAsRelated(string nodeIdHashed, string relatedLine)
{
var relatedNodeHashId = GetNodeIdHashFromLineId(relatedLine);
if (!string.IsNullOrEmpty(relatedNodeHashId) && NodeMetaDataObj.ContainsKey(nodeIdHashed))
{
NodeMetaDataObj[nodeIdHashed].RelatedNodeIdHash = relatedNodeHashId;
}
}

public void AddNavigation(string nodeIdHash, NavigationTreeNode node)
{
if (!string.IsNullOrEmpty(nodeIdHash) && node != null)
{
NodeMetaDataObj[nodeIdHash].NavigationTreeNode = node;
}
}

public void ConnectNodeToParent(string nodeIdHashed, string parentNodeIdHashed, int nodePosition)
{
if (!NodeMetaDataObj.ContainsKey(nodeIdHashed))
{
NodeMetaDataObj.TryAdd(nodeIdHashed, new CodePanelNodeMetaData());
}
NodeMetaDataObj[nodeIdHashed].ParentNodeIdHashed = parentNodeIdHashed;
NodeMetaDataObj[parentNodeIdHashed].ChildrenNodeIdsInOrderObj.TryAdd(nodePosition, nodeIdHashed);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export class CodePanelNodeMetaData {
isNodeWithNoneDocDiffInDescendants : boolean;
bottomTokenNodeIdHash: string;
isProcessed: boolean;
relatedNodeIdHash: string;

constructor() {
this.documentation = [];
Expand All @@ -86,5 +87,6 @@ export class CodePanelNodeMetaData {
this.isNodeWithNoneDocDiffInDescendants = false;
this.bottomTokenNodeIdHash = '';
this.isProcessed = false;
this.relatedNodeIdHash = '';
Comment thread
praveenkuttappan marked this conversation as resolved.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ describe('API Tree Builder', () => {
apiTreeBuilder.onmessage = ({ data }) => {
if (data.directive === ReviewPageWorkerMessageDirective.UpdateCodePanelRowData) {
const codePanelRowData = data.payload as CodePanelRowData[];
expect(codePanelRowData.length).toBe(21);
expect(codePanelRowData.length).toBe(24);
const linesWithDiff = codePanelRowData.filter(row => row.diffKind === 'removed' || row.diffKind === 'added');
expect(linesWithDiff.length).toBe(6);
}
Expand Down Expand Up @@ -95,7 +95,7 @@ describe('API Tree Builder', () => {
apiTreeBuilder.onmessage = ({ data }) => {
if (data.directive === ReviewPageWorkerMessageDirective.UpdateCodePanelRowData) {
const codePanelRowData = data.payload as CodePanelRowData[];
expect(codePanelRowData.length).toBe(171);
expect(codePanelRowData.length).toBe(174);
const linesWithDiff = codePanelRowData.filter(row => row.diffKind === 'removed' || row.diffKind === 'added');
expect(linesWithDiff.length).toBe(152);
}
Expand Down Expand Up @@ -312,7 +312,7 @@ describe('API Tree Builder', () => {
apiTreeBuilder.onmessage = ({ data }) => {
if (data.directive === ReviewPageWorkerMessageDirective.UpdateCodePanelRowData) {
const codePanelRowData = data.payload as CodePanelRowData[];
expect(codePanelRowData.length).toBe(20);
expect(codePanelRowData.length).toBe(21);
const linesWithDiff = codePanelRowData.filter(row => row.diffKind === 'removed' || row.diffKind === 'added');
expect(linesWithDiff.length).toBe(9);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ let lineNumber: number = 0;
let diffLineNumber: number = 0;
let toggleDocumentationClassPart = "bi-arrow-up-square";
let hasHiddenAPI: boolean = false;
let visibleNodes: Set<string> = new Set<string>();
let addPostDiffContext: boolean = false;

addEventListener('message', ({ data }) => {
if (data instanceof ArrayBuffer) {
Expand Down Expand Up @@ -56,6 +58,8 @@ addEventListener('message', ({ data }) => {
navigationTree = [];
diffBuffer = [];
apiTreeBuilderData = null;
visibleNodes = new Set<string>();
addPostDiffContext = false;
}
else {
apiTreeBuilderData = data;
Expand All @@ -71,6 +75,17 @@ function buildCodePanelRows(nodeIdHashed: string, navigationTree: NavigationTree
if(node.isProcessed)
return;

//If current node is related line attribute and then related node is not modified then skip current node in tree and node view
if (node.relatedNodeIdHash && !node.isNodeWithDiff && !node.isNodeWithDiffInDescendants &&
(apiTreeBuilderData?.diffStyle == TREE_DIFF_STYLE || apiTreeBuilderData?.diffStyle == NODE_DIFF_STYLE))
{
let relatedNode = codePanelData?.nodeMetaData[node.relatedNodeIdHash]!;
if (!relatedNode.isNodeWithDiff && !node.isNodeWithDiffInDescendants && !visibleNodes.has(node.relatedNodeIdHash))
{
return;
}
}

let buildNode = true;
let buildChildren = true;
let addNodeToBuffer = false
Expand All @@ -80,7 +95,7 @@ function buildCodePanelRows(nodeIdHashed: string, navigationTree: NavigationTree
buildNode = false;
buildChildren = false;
}

if (!buildNode && (!node.childrenNodeIdsInOrder || Object.keys(node.childrenNodeIdsInOrder).length === 0) &&
(apiTreeBuilderData?.diffStyle !== NODE_DIFF_STYLE || node.isNodeWithDiff)) {
buildNode = true;
Expand All @@ -100,6 +115,7 @@ function buildCodePanelRows(nodeIdHashed: string, navigationTree: NavigationTree

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

Expand Down Expand Up @@ -127,8 +143,18 @@ function buildCodePanelRows(nodeIdHashed: string, navigationTree: NavigationTree
setLineNumber(codeLine);
if (buildNode) {
codePanelRowData.push(codeLine);
visibleNodes.add(nodeIdHashed);
addPostDiffContext = true;
}
if (addNodeToBuffer) {
// We should add immediate 3 lines as context post a changed line
if (addPostDiffContext && diffBuffer.length === 3)
{
codePanelRowData.push(...diffBuffer);
diffBuffer.map(row => visibleNodes.add(row.nodeIdHashed));
diffBuffer = [];
addPostDiffContext = false;
}
diffBuffer.push(codeLine);
addJustDiffBuffer();
}
Expand Down Expand Up @@ -181,6 +207,7 @@ function buildCodePanelRows(nodeIdHashed: string, navigationTree: NavigationTree
setLineNumber(codeLine);
if (buildNode) {
codePanelRowData.push(codeLine);
visibleNodes.add(codeLine.nodeIdHashed);
}
});
}
Expand Down
Loading