Skip to content

Commit

Permalink
auto schema detection of js in lsp (#459)
Browse files Browse the repository at this point in the history
  • Loading branch information
ChristopherHX authored Dec 2, 2024
1 parent ab98200 commit 122f991
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 12 deletions.
2 changes: 1 addition & 1 deletion src/Runner.Language.Server/AutoCompleter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public async Task<CompletionList> Handle(CompletionParams request, CancellationT
}
} else {
schema = AzureDevops.LoadSchema();
var template = await AzureDevops.ParseTemplate(context, currentFileName, null, true);
var template = await AzureDevops.ParseTemplate(context, currentFileName, this.data.Schema[request.TextDocument.Uri], true);
_ = template;
}
} catch
Expand Down
2 changes: 1 addition & 1 deletion src/Runner.Language.Server/HoverProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ private static TemplateContext CreateTemplateContext(GitHub.DistributedTask.Obje

templateContext.Errors.Check();
} else {
var template = await AzureDevops.ParseTemplate(context, currentFileName, null, true);
var template = await AzureDevops.ParseTemplate(context, currentFileName, this.data.Schema[request.TextDocument.Uri], true);
_ = template;
}
} catch
Expand Down
2 changes: 1 addition & 1 deletion src/Runner.Language.Server/SemanticTokenHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ private static TemplateContext CreateTemplateContext(GitHub.DistributedTask.Obj


} else {
var template = await AzureDevops.ParseTemplate(context, currentFileName, null, true);
var template = await AzureDevops.ParseTemplate(context, currentFileName, this.data.Schema[request.TextDocument.Uri], true);
token = template.Item2;
}
} catch
Expand Down
4 changes: 4 additions & 0 deletions src/Runner.Language.Server/SharedData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,9 @@ public class SharedData

public Dictionary<DocumentUri, string> Content { get; } = [];

public Dictionary<DocumentUri, string> Types { get; } = [];

public Dictionary<DocumentUri, string> Schema { get; } = [];

public OmniSharp.Extensions.LanguageServer.Protocol.Server.ILanguageServer? Server { get; set; }
}
61 changes: 52 additions & 9 deletions src/Runner.Language.Server/TextDocumentSyncHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ public override TextDocumentAttributes GetTextDocumentAttributes(DocumentUri uri
return new TextDocumentAttributes(uri, "yaml");
}

private async Task<bool> ShouldHandle(TextDocumentIdentifier doc, string content, string? langID) {
var known = langID == "azure-pipelines" || doc.Uri.Path.Contains("/.github/workflows/") || doc.Uri.Path.EndsWith("/action.yml") || doc.Uri.Path.EndsWith("/azure-pipeline.yml");
private bool ShouldHandle(TextDocumentIdentifier doc, string content, string? langID, out string schema) {
schema = null;

Check warning on line 36 in src/Runner.Language.Server/TextDocumentSyncHelper.cs

View workflow job for this annotation

GitHub Actions / deploy

Cannot convert null literal to non-nullable reference type.

Check warning on line 36 in src/Runner.Language.Server/TextDocumentSyncHelper.cs

View workflow job for this annotation

GitHub Actions / deploy

Cannot convert null literal to non-nullable reference type.
var known = langID == "azure-pipelines" || doc.Uri.Path.Contains("/.github/workflows/") || doc.Uri.Path.EndsWith("/action.yml") || doc.Uri.Path.EndsWith("/action.yaml") || doc.Uri.Path.EndsWith("/azure-pipeline.yml") || doc.Uri.Path.EndsWith("/azure-pipeline.yaml");

if(known) {
return true;
Expand All @@ -44,6 +45,9 @@ private async Task<bool> ShouldHandle(TextDocumentIdentifier doc, string content
yamlStream.Load(input);
var rootNode = (YamlMappingNode)yamlStream.Documents[0].RootNode;
var isPipeline = CheckIsPipeline(rootNode);
if(isPipeline != null) {
schema = ExtractSchema(rootNode);
}
return isPipeline != null;
} catch {

Expand Down Expand Up @@ -106,14 +110,52 @@ static YamlMappingNode CheckIsPipeline(YamlMappingNode obj)
return null;

Check warning on line 110 in src/Runner.Language.Server/TextDocumentSyncHelper.cs

View workflow job for this annotation

GitHub Actions / deploy

Possible null reference return.
}
}
}

static string ExtractSchema(YamlMappingNode obj)
{
var variables = obj.Children.ContainsKey(new YamlScalarNode("variables")) ? obj.Children[new YamlScalarNode("variables")] : null;
var stages = obj.Children.ContainsKey(new YamlScalarNode("stages")) ? obj.Children[new YamlScalarNode("stages")] as YamlSequenceNode : null;
var jobs = obj.Children.ContainsKey(new YamlScalarNode("jobs")) ? obj.Children[new YamlScalarNode("jobs")] as YamlSequenceNode : null;
var steps = obj.Children.ContainsKey(new YamlScalarNode("steps")) ? obj.Children[new YamlScalarNode("steps")] as YamlSequenceNode : null;
bool isTypedTemplate = false;
var parameters = obj.Children.ContainsKey(new YamlScalarNode("parameters")) ? obj.Children[new YamlScalarNode("parameters")] : null;
bool mustBeTemplate = parameters != null && (!(isTypedTemplate = parameters is YamlSequenceNode) || ((YamlSequenceNode)parameters).Children.Any(x => x is YamlMappingNode param && param.Children.ContainsKey(new YamlScalarNode("type")) && param.Children[new YamlScalarNode("type")].ToString() == "legacyObject"));
string schema = null;

Check warning on line 123 in src/Runner.Language.Server/TextDocumentSyncHelper.cs

View workflow job for this annotation

GitHub Actions / deploy

Converting null literal or possible null value to non-nullable type.

if (mustBeTemplate && isTypedTemplate && obj.Children.ContainsKey(new YamlScalarNode("extends")))
{
schema = "extend-template-root";
}
else if (mustBeTemplate && variables == null && stages != null && jobs == null && steps == null)
{
schema = "stage-template-root";
}
else if (mustBeTemplate && variables == null && stages == null && jobs != null && steps == null)
{
schema = "job-template-root";
}
else if (mustBeTemplate && variables == null && stages == null && jobs == null && steps != null)
{
schema = "step-template-root";
}
else if ((mustBeTemplate || !obj.Children.ContainsKey(new YamlScalarNode("extends"))) && variables != null && stages == null && jobs == null && steps == null)
{
schema = "variable-template-root";
}

return schema;

Check warning on line 146 in src/Runner.Language.Server/TextDocumentSyncHelper.cs

View workflow job for this annotation

GitHub Actions / deploy

Possible null reference return.
}
}

public override async Task<Unit> Handle(DidOpenTextDocumentParams request, CancellationToken cancellationToken)
{
if(await ShouldHandle(request.TextDocument, request.TextDocument.Text, request.TextDocument.LanguageId)) {
if(ShouldHandle(request.TextDocument, request.TextDocument.Text, request.TextDocument.LanguageId, out var schema)) {
this.data.Types[request.TextDocument.Uri] = request.TextDocument.LanguageId;
this.data.Schema[request.TextDocument.Uri] = schema;
this.data.Content[request.TextDocument.Uri] = request.TextDocument.Text;
await ValidateSyntaxAsync(request.TextDocument.Uri);
await ValidateSyntaxAsync(request.TextDocument.Uri, schema);
} else {
this.data.Types[request.TextDocument.Uri] = request.TextDocument.LanguageId;
this.data.Content.Remove(request.TextDocument.Uri);
SendDiagnostics(request.TextDocument.Uri, new List<string>());
}
Expand All @@ -122,9 +164,10 @@ public override async Task<Unit> Handle(DidOpenTextDocumentParams request, Cance

public override async Task<Unit> Handle(DidChangeTextDocumentParams request, CancellationToken cancellationToken)
{
if(await ShouldHandle(request.TextDocument, request.ContentChanges.FirstOrDefault()?.Text, null)) {
if(ShouldHandle(request.TextDocument, request.ContentChanges.FirstOrDefault()?.Text, null, out var schema)) {

Check warning on line 167 in src/Runner.Language.Server/TextDocumentSyncHelper.cs

View workflow job for this annotation

GitHub Actions / deploy

Possible null reference argument for parameter 'content' in 'bool TextDocumentSyncHelper.ShouldHandle(TextDocumentIdentifier doc, string content, string? langID, out string schema)'.
this.data.Schema[request.TextDocument.Uri] = schema;
this.data.Content[request.TextDocument.Uri] = request.ContentChanges.FirstOrDefault()?.Text ?? "";
await ValidateSyntaxAsync(request.TextDocument.Uri);
await ValidateSyntaxAsync(request.TextDocument.Uri, schema);
} else {
this.data.Content.Remove(request.TextDocument.Uri);
SendDiagnostics(request.TextDocument.Uri, new List<string>());
Expand All @@ -149,7 +192,7 @@ private static TemplateContext CreateTemplateContext(GitHub.DistributedTask.Obje
return templateContext;
}

private async Task ValidateSyntaxAsync(DocumentUri uri)
private async Task ValidateSyntaxAsync(DocumentUri uri, string? schema)
{
var content = this.data.Content[uri];
var currentFileName = "t.yml";
Expand Down Expand Up @@ -180,7 +223,7 @@ private async Task ValidateSyntaxAsync(DocumentUri uri)

templateContext.Errors.Check();
} else {
var template = await AzureDevops.ParseTemplate(context, currentFileName, null, true);
var template = await AzureDevops.ParseTemplate(context, currentFileName, schema, true);
_ = template;
}
SendDiagnostics(uri, new List<string>());
Expand Down

0 comments on commit 122f991

Please sign in to comment.