Skip to content

Commit

Permalink
Switch to Human Readable Json Property Names, use compression
Browse files Browse the repository at this point in the history
  • Loading branch information
chidozieononiwu committed Jun 13, 2024
1 parent fa47aa7 commit 044e547
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 146 deletions.
98 changes: 42 additions & 56 deletions src/dotnet/APIView/APIView/Model/TokenTreeModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,36 +22,30 @@ public enum StructuredTokenKind

public class StructuredToken
{
[JsonPropertyName("t")]
public HashSet<string> _tagsForSerializer
public HashSet<string> Tags
{
get { return Tags.Count > 0 ? Tags : null; }
set { Tags = value ?? new HashSet<string>(); }
get { return TagsObj.Count > 0 ? TagsObj : null; }
set { TagsObj = value ?? new HashSet<string>(); }
}
[JsonPropertyName("p")]
public Dictionary<string, string> _propertiesForSerializer
public Dictionary<string, string> Properties
{
get { return Properties.Count > 0 ? Properties : null; }
set { Properties = value ?? new Dictionary<string, string>(); }
get { return PropertiesObj.Count > 0 ? PropertiesObj : null; }
set { PropertiesObj = value ?? new Dictionary<string, string>(); }
}
[JsonPropertyName("rc")]
public HashSet<string> _renderClassesForSerializer
public HashSet<string> RenderClasses
{
get { return RenderClasses.Count > 0 ? RenderClasses : null; }
set { RenderClasses = value ?? new HashSet<string>(); }
get { return RenderClassesObj.Count > 0 ? RenderClassesObj : null; }
set { RenderClassesObj = value ?? new HashSet<string>(); }
}
[JsonPropertyName("v")]
public string Value { get; set; } = string.Empty;
[JsonPropertyName("i")]
public string Id { get; set; }
[JsonPropertyName("k")]
public StructuredTokenKind Kind { get; set; }
[JsonIgnore]
public HashSet<string> Tags { get; set; } = new HashSet<string>();
public HashSet<string> TagsObj { get; set; } = new HashSet<string>();
[JsonIgnore]
public Dictionary<string, string> Properties { get; set; } = new Dictionary<string, string>();
public Dictionary<string, string> PropertiesObj { get; set; } = new Dictionary<string, string>();
[JsonIgnore]
public HashSet<string> RenderClasses { get; set; } = new HashSet<string>();
public HashSet<string> RenderClassesObj { get; set; } = new HashSet<string>();

public StructuredToken()
{
Expand Down Expand Up @@ -100,14 +94,14 @@ public static StructuredToken CreateTextToken(string value, string id = null)
{
token.Id = id;
}
token.RenderClasses.Add("text");
token.RenderClassesObj.Add("text");
return token;
}

public static StructuredToken CreateKeywordToken(string value)
{
var token = new StructuredToken(value);
token.RenderClasses.Add("keyword");
token.RenderClassesObj.Add("keyword");
return token;
}

Expand All @@ -124,7 +118,7 @@ public static StructuredToken CreateKeywordToken(Accessibility accessibility)
public static StructuredToken CreatePunctuationToken(string value)
{
var token = new StructuredToken(value);
token.RenderClasses.Add("punc");
token.RenderClassesObj.Add("punc");
return token;
}

Expand All @@ -136,81 +130,73 @@ public static StructuredToken CreatePunctuationToken(SyntaxKind syntaxKind)
public static StructuredToken CreateTypeNameToken(string value)
{
var token = new StructuredToken(value);
token.RenderClasses.Add("tname");
token.RenderClassesObj.Add("tname");
return token;
}

public static StructuredToken CreateMemberNameToken(string value)
{
var token = new StructuredToken(value);
token.RenderClasses.Add("mname");
token.RenderClassesObj.Add("mname");
return token;
}

public static StructuredToken CreateLiteralToken(string value)
{
var token = new StructuredToken(value);
token.RenderClasses.Add("literal");
token.RenderClassesObj.Add("literal");
return token;
}

public static StructuredToken CreateStringLiteralToken(string value)
{
var token = new StructuredToken(value);
token.RenderClasses.Add("sliteral");
token.RenderClassesObj.Add("sliteral");
return token;
}
}

public class APITreeNode
{
[JsonPropertyName("t")]
public HashSet<string> _tagsForSerializer
public HashSet<string> Tags
{
get { return Tags.Count > 0 ? Tags : null; }
set { Tags = value ?? new HashSet<string>(); }
get { return TagsObj.Count > 0 ? TagsObj : null; }
set { TagsObj = value ?? new HashSet<string>(); }
}
[JsonPropertyName("p")]
public Dictionary<string, string> _propertiesForSerializer
public Dictionary<string, string> Properties
{
get { return Properties.Count > 0 ? Properties : null; }
set { Properties = value ?? new Dictionary<string, string>(); }
get { return PropertiesObj.Count > 0 ? PropertiesObj : null; }
set { PropertiesObj = value ?? new Dictionary<string, string>(); }
}
[JsonPropertyName("tt")]
public List<StructuredToken> _topTokensForSerializer
public List<StructuredToken> TopTokens
{
get { return TopTokens.Count > 0 ? TopTokens : null; }
set { TopTokens = value ?? new List<StructuredToken>(); }
get { return TopTokensObj.Count > 0 ? TopTokensObj : null; }
set { TopTokensObj = value ?? new List<StructuredToken>(); }
}
[JsonPropertyName("bt")]
public List<StructuredToken> _bottomTokensForSerializer
public List<StructuredToken> BottomTokens
{
get { return BottomTokens.Count > 0 ? BottomTokens : null; }
set { BottomTokens = value ?? new List<StructuredToken>(); }
get { return BottomTokensObj.Count > 0 ? BottomTokensObj : null; }
set { BottomTokensObj = value ?? new List<StructuredToken>(); }
}
[JsonPropertyName("c")]
public List<APITreeNode> _childrenForSerializer
public List<APITreeNode> Children
{
get { return Children.Count > 0 ? Children : null; }
set { Children = value ?? new List<APITreeNode>(); }
get { return ChildrenObj.Count > 0 ? ChildrenObj : null; }
set { ChildrenObj = value ?? new List<APITreeNode>(); }
}

[JsonPropertyName("n")]
public string Name { get; set; }
[JsonPropertyName("i")]
public string Id { get; set; }
[JsonPropertyName("k")]
public string Kind { get; set; }
[JsonIgnore]
public HashSet<string> Tags { get; set; } = new HashSet<string>();
public HashSet<string> TagsObj { get; set; } = new HashSet<string>();
[JsonIgnore]
public Dictionary<string, string> Properties { get; set; } = new Dictionary<string, string>();
public Dictionary<string, string> PropertiesObj { get; set; } = new Dictionary<string, string>();
[JsonIgnore]
public List<StructuredToken> TopTokens { get; set; } = new List<StructuredToken>();
public List<StructuredToken> TopTokensObj { get; set; } = new List<StructuredToken>();
[JsonIgnore]
public List<StructuredToken> BottomTokens { get; set; } = new List<StructuredToken>();
public List<StructuredToken> BottomTokensObj { get; set; } = new List<StructuredToken>();
[JsonIgnore]
public List<APITreeNode> Children { get; set; } = new List<APITreeNode>();
public List<APITreeNode> ChildrenObj { get; set; } = new List<APITreeNode>();

public override int GetHashCode()
{
Expand All @@ -234,13 +220,13 @@ public override bool Equals(object obj)

public void SortChildren()
{
if (Children != null)
if (ChildrenObj != null)
{
if (Kind.Equals("Namespace") || Kind.Equals("Type") || Kind.Equals("Member"))
{
Children.Sort((x, y) => x.Name.CompareTo(y.Name));
ChildrenObj.Sort((x, y) => x.Name.CompareTo(y.Name));
}
foreach (var child in Children)
foreach (var child in ChildrenObj)
{
child.SortChildren();
}
Expand Down
32 changes: 15 additions & 17 deletions tools/apiview/parsers/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,48 +45,46 @@ Sort each node at each level of the tree by your desired property, this is to en

Ensure each node has an Id. The combination of `Id`, `Kind` and `SubKind` should make the node unique across all nodes in the tree. This is very important. For example a class and a method can potentally have the same Id, but the kind should diffrentiate them from each other.

- `Name (n)` : The name of the tree node which will be used as label for the API Navigation. Generally use the name of the module (class, method)
- `Id (i)` : Id of the node, which should be unique at the node level. i.e. unique among its siblings. Use whatever existing parser is assigning to DefinitionId for the main Token of the node. Each node must have an Id.
- `Kind (k)` : What kind of node is it. Please ensure you set a Kind for each node. Using any of the following `assembly`, `class`, `delegate`, `enum`, `interface`, `method` , `namespace`, `package`, `struct`, `type` will get you the corresponding default icons for the page navigation but feel free to use something language specific.
- `Tags (t)` : Use this for opt in or opt out boolean properties. The currently supported tags are
- `Name` : The name of the tree node which will be used as label for the API Navigation. Generally use the name of the module (class, method)
- `Id` : Id of the node, which should be unique at the node level. i.e. unique among its siblings. Use whatever existing parser is assigning to DefinitionId for the main Token of the node. Each node must have an Id.
- `Kind` : What kind of node is it. Please ensure you set a Kind for each node. Using any of the following `assembly`, `class`, `delegate`, `enum`, `interface`, `method` , `namespace`, `package`, `struct`, `type` will get you the corresponding default icons for the page navigation but feel free to use something language specific.
- `Tags` : Use this for opt in or opt out boolean properties. The currently supported tags are
- `Deprecated` Mark a node as deprecated
- `Hidden` Mark a node as Hidden
- `HideFromNavigation` Indicate that anode should be hidden from the page navigation.
- `SkipDiff` Indicate that a node should not be used in computatation of diff.
- `CrossLandDefId` The cross language definitionId for the node.
- `Properties (p)` : Use this for other properties of the node. The currently supported tags
- `Properties` : Use this for other properties of the node. The currently supported tags
- `SubKind` Similar to kind, use this to make the node more specific. e.g. `Kind = 'Type'`, and `SubKind = 'class'` or somethign specific to you language. We also use this to make the navigation icon it will overide kind.
- `IconName` Use this only if you are looking to add a custom icon different from language wide defaults.
- `TopTokens (tt)` : The main data of the node. This is all the tokens that actually define the node. e.g. For a class this would include the access modifier, the class name, any attributes or decorators e.t.c. For a method this would include the return type, method name, parameters e.t.c. See StructureTokesn description below for more info.
- `BottomToken (bt)` : Data that closes out the node. Depending on the language this would include the closing curly brace and/or empty lines. You can simulate an empty line by adding an empty token (content token with no value) and a lineBreak token.
- `Children (c)` : The nodes immediate children. For a namespace this would be classes, for a class this would be the class constructors and methods.
- `TopTokens` : The main data of the node. This is all the tokens that actually define the node. e.g. For a class this would include the access modifier, the class name, any attributes or decorators e.t.c. For a method this would include the return type, method name, parameters e.t.c. See StructureTokesn description below for more info.
- `BottomToken` : Data that closes out the node. Depending on the language this would include the closing curly brace and/or empty lines. You can simulate an empty line by adding an empty token (content token with no value) and a lineBreak token.
- `Children` : The nodes immediate children. For a namespace this would be classes, for a class this would be the class constructors and methods.

### StructuredToken
- `Value (v)` : The token value which will be dispalyed. Can be null. Spacing tokens don't need to have value.
- `Id (i)` : This is essentially whatever existing parser was asigning to the token DefinitionId. You dont have to assign a tokenId.
- `Kind (k)` : An enum
- `Value` : The token value which will be dispalyed. Can be null. Spacing tokens don't need to have value.
- `Id` : This is essentially whatever existing parser was asigning to the token DefinitionId. You dont have to assign a tokenId.
- `Kind` : An enum
- `Content` Specifies that the token is content
- `LineBreak` Space token indicating switch to new line.
- `NoneBreakingSpace` Regular single space
- `TabSpace` 4 NoneBreakingSpaces
- `ParameterSeparator` Use this between method parameters. Depending on user setting this would result in a singlespace or new line
- `Url` A url token should have `LinkText` property and the url/link should be the token value.
All tokens should be content except for spacing tokens and url. ParameterSeparator should be used between method or function parameters.
- `Tags (t)` : Use this for opt in or opt out boolean properties.The currently supported tags are
- `Tags` : Use this for opt in or opt out boolean properties.The currently supported tags are
- `SkippDiff` Indicate that a token should not be used in computatation of diff.
- `Deprecated` Mark a token as deprecated
- `Properties (p)` : Properties of the token.
- `Properties` : Properties of the token.
- `GroupId` : `doc` to group consecutive comment tokens as documentation.
- `NavigateToId` Id for navigating to where the node where the token is defined. Should match the definitionId of the node.
- `RenderClasses (rc)` : Add css classes for how the tokens will be rendred. Classes currently being used are `text` `keyword` `punc` `tname` `mname` `literal` `sliteral` `comment` Feel free to add your own custom class. Whatever custom classes you use please provide us the appriopriate css for the class so we can update APIView.

Json property names for each property is shown in brackets.
- `RenderClasses` : Add css classes for how the tokens will be rendred. Classes currently being used are `text` `keyword` `punc` `tname` `mname` `literal` `sliteral` `comment` Feel free to add your own custom class. Whatever custom classes you use please provide us the appriopriate css for the class so we can update APIView.

Dont worry about indentation that will be handeled by the tree structure, unless you want to have indentation between the tokens then use `TabSpace` token kind.

If your packages contains multiple assemblies then you will have multiple trees with multiple roots. Assign the final parsed value to a `List<APITreeNode> APIForest` property of the `CodeFile`.

Serialize the generated code file to JSON. Try to make the json as small as possible by ignoring null values and empty collections, and using the abbreviated names of theproperties as the Json property name
Serialize the generated code file to JSON them compress the file using Gzip compression. Try to make the json as small as possible by ignoring null values and empty collections.

## How to handle commons Scenarios
- TEXT, KEYWORD, COMMENT : Add `text`, `keyword`, `comment` to RenderClasses of the token
Expand Down
11 changes: 3 additions & 8 deletions tools/apiview/parsers/csharp-api-parser/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,26 +102,21 @@ static void HandlePackageFileParsing(Stream stream, FileInfo packageFilePath, Di
}
var parsedFileName = string.IsNullOrEmpty(outputFileName) ? assemblySymbol.Name : outputFileName;
var treeTokenCodeFile = new CSharpAPIParser.TreeToken.CodeFileBuilder().Build(assemblySymbol, runAnalysis, dependencies);
var jsonTokenFilePath = Path.Combine(OutputDirectory.FullName, $"{parsedFileName}");
var gzipJsonTokenFilePath = Path.Combine(OutputDirectory.FullName, $"{parsedFileName}.gzip");
var gzipJsonTokenFilePath = Path.Combine(OutputDirectory.FullName, $"{parsedFileName}");


var options = new JsonSerializerOptions()
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};

{
using FileStream fileStream = new FileStream(jsonTokenFilePath, FileMode.Create, FileAccess.Write);
JsonSerializer.Serialize(fileStream, treeTokenCodeFile, options);

using FileStream gzipFileStream = new FileStream(gzipJsonTokenFilePath, FileMode.Create, FileAccess.Write);
using GZipStream gZipStream = new GZipStream(gzipFileStream, CompressionLevel.Optimal);
JsonSerializer.Serialize(new Utf8JsonWriter(gZipStream, new JsonWriterOptions { Indented = false }), treeTokenCodeFile, options);
}

Console.WriteLine($"TokenCodeFile File {jsonTokenFilePath} Generated Successfully.");
Console.WriteLine($"Compressed TokenCodeFile File {gzipJsonTokenFilePath} Generated Successfully.");
Console.WriteLine($"TokenCodeFile File {gzipJsonTokenFilePath} Generated Successfully.");
Console.WriteLine();
}
catch (Exception ex)
Expand Down
Loading

0 comments on commit 044e547

Please sign in to comment.