Skip to content

Provide pester functions (it, context, describe) in document symbols #490

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Jun 5, 2017
Merged
Show file tree
Hide file tree
Changes from all 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
48 changes: 20 additions & 28 deletions src/PowerShellEditorServices/Language/AstOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -243,41 +243,33 @@ static public IEnumerable<SymbolReference> FindSymbolsInDocument(Ast scriptAst,

// TODO: Restore this when we figure out how to support multiple
// PS versions in the new PSES-as-a-module world (issue #276)
// if (powerShellVersion >= new Version(5,0))
// {
//#if PowerShellv5
// FindSymbolsVisitor2 findSymbolsVisitor = new FindSymbolsVisitor2();
// scriptAst.Visit(findSymbolsVisitor);
// symbolReferences = findSymbolsVisitor.SymbolReferences;
//#endif
// }
// else
{
if (IsPowerShellDataFileAst(scriptAst))
{
var findHashtableSymbolsVisitor = new FindHashtableSymbolsVisitor();
scriptAst.Visit(findHashtableSymbolsVisitor);
symbolReferences = findHashtableSymbolsVisitor.SymbolReferences;
}
else
{
FindSymbolsVisitor findSymbolsVisitor = new FindSymbolsVisitor();
scriptAst.Visit(findSymbolsVisitor);
symbolReferences = findSymbolsVisitor.SymbolReferences;
}
}

// if (powerShellVersion >= new Version(5,0))
// {
//#if PowerShellv5
// FindSymbolsVisitor2 findSymbolsVisitor = new FindSymbolsVisitor2();
// scriptAst.Visit(findSymbolsVisitor);
// symbolReferences = findSymbolsVisitor.SymbolReferences;
//#endif
// }
// else

FindSymbolsVisitor findSymbolsVisitor = new FindSymbolsVisitor();
scriptAst.Visit(findSymbolsVisitor);
symbolReferences = findSymbolsVisitor.SymbolReferences;
return symbolReferences;
}

static private bool IsPowerShellDataFileAst(Ast ast)
/// <summary>
/// Checks if a given ast represents the root node of a *.psd1 file.
/// </summary>
/// <param name="ast">The abstract syntax tree of the given script</param>
/// <returns>true if the AST represts a *.psd1 file, otherwise false</returns>
static public bool IsPowerShellDataFileAst(Ast ast)
{
// sometimes we don't have reliable access to the filename
// so we employ heuristics to check if the contents are
// part of a psd1 file.
return (ast.Extent.File != null
&& ast.Extent.File.EndsWith(".psd1", StringComparison.OrdinalIgnoreCase))
|| IsPowerShellDataFileAstNode(
return IsPowerShellDataFileAstNode(
new { Item = ast, Children = new List<dynamic>() },
new Type[] {
typeof(ScriptBlockAst),
Expand Down
23 changes: 23 additions & 0 deletions src/PowerShellEditorServices/Language/DocumentSymbolProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace Microsoft.PowerShell.EditorServices
{
internal abstract class DocumentSymbolProvider
{
public IEnumerable<SymbolReference> GetSymbols(ScriptFile scriptFile, Version psVersion = null)
{
if (CanProvideFor(scriptFile))
{
return GetSymbolsImpl(scriptFile, psVersion);
}

return Enumerable.Empty<SymbolReference>();
}

protected abstract IEnumerable<SymbolReference> GetSymbolsImpl(ScriptFile scriptFile, Version psVersion);

protected abstract bool CanProvideFor(ScriptFile scriptFile);
}
}
27 changes: 14 additions & 13 deletions src/PowerShellEditorServices/Language/LanguageService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public class LanguageService
private string mostRecentRequestFile;
private Dictionary<String, List<String>> CmdletToAliasDictionary;
private Dictionary<String, String> AliasToCmdletDictionary;
private DocumentSymbolProvider[] documentSymbolProviders;

const int DefaultWaitTimeoutMilliseconds = 5000;

Expand All @@ -54,6 +55,12 @@ public LanguageService(PowerShellContext powerShellContext)

this.CmdletToAliasDictionary = new Dictionary<String, List<String>>(StringComparer.OrdinalIgnoreCase);
this.AliasToCmdletDictionary = new Dictionary<String, String>(StringComparer.OrdinalIgnoreCase);
this.documentSymbolProviders = new DocumentSymbolProvider[]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you reading my mind? I was just about to implement a generic "feature provider" model in certain parts of PSES :)

{
new ScriptDocumentSymbolProvider(),
new PSDDocumentSymbolProvider(),
new PesterDocumentSymbolProvider()
};
}

#endregion
Expand Down Expand Up @@ -226,24 +233,18 @@ await SymbolDetails.Create(
public FindOccurrencesResult FindSymbolsInFile(ScriptFile scriptFile)
{
Validate.IsNotNull("scriptFile", scriptFile);

IEnumerable<SymbolReference> symbolReferencesinFile =
AstOperations
.FindSymbolsInDocument(scriptFile.ScriptAst, this.powerShellContext.LocalPowerShellVersion.Version)
.Select(
reference =>
return new FindOccurrencesResult
{
FoundOccurrences = documentSymbolProviders
.SelectMany(p => p.GetSymbols(scriptFile, powerShellContext.LocalPowerShellVersion.Version))
.Select(reference =>
{
reference.SourceLine =
scriptFile.GetLine(reference.ScriptRegion.StartLineNumber);
reference.FilePath = scriptFile.FilePath;
return reference;
});

return
new FindOccurrencesResult
{
FoundOccurrences = symbolReferencesinFile
};
})
};
}

/// <summary>
Expand Down
21 changes: 21 additions & 0 deletions src/PowerShellEditorServices/Language/PSDDocumentSymbolProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;

namespace Microsoft.PowerShell.EditorServices
{
internal class PSDDocumentSymbolProvider : DocumentSymbolProvider
{
protected override bool CanProvideFor(ScriptFile scriptFile)
{
return (scriptFile.FilePath != null &&
scriptFile.FilePath.EndsWith(".psd1", StringComparison.OrdinalIgnoreCase)) ||
AstOperations.IsPowerShellDataFileAst(scriptFile.ScriptAst);
}
protected override IEnumerable<SymbolReference> GetSymbolsImpl(ScriptFile scriptFile, Version psVersion)
{
var findHashtableSymbolsVisitor = new FindHashtableSymbolsVisitor();
scriptFile.ScriptAst.Visit(findHashtableSymbolsVisitor);
return findHashtableSymbolsVisitor.SymbolReferences;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation.Language;

namespace Microsoft.PowerShell.EditorServices
{
internal class PesterDocumentSymbolProvider : DocumentSymbolProvider
{
protected override bool CanProvideFor(ScriptFile scriptFile)
{
return scriptFile.FilePath.EndsWith("tests.ps1", StringComparison.OrdinalIgnoreCase);
}

protected override IEnumerable<SymbolReference> GetSymbolsImpl(ScriptFile scriptFile, Version psVersion)
{
var commandAsts = scriptFile.ScriptAst.FindAll(ast =>
{
switch ((ast as CommandAst)?.GetCommandName().ToLower())
{
case "describe":
case "context":
case "it":
return true;

default:
return false;
}
},
true);

return commandAsts.Select(ast => new SymbolReference(
SymbolType.Function,
ast.Extent,
scriptFile.FilePath,
scriptFile.GetLine(ast.Extent.StartLineNumber)));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;

namespace Microsoft.PowerShell.EditorServices
{
internal class ScriptDocumentSymbolProvider : DocumentSymbolProvider
{
protected override bool CanProvideFor(ScriptFile scriptFile)
{
return scriptFile != null &&
scriptFile.FilePath != null &&
(scriptFile.FilePath.EndsWith(".ps1", StringComparison.OrdinalIgnoreCase) ||
scriptFile.FilePath.EndsWith(".psm1", StringComparison.OrdinalIgnoreCase));
}

protected override IEnumerable<SymbolReference> GetSymbolsImpl(
ScriptFile scriptFile,
Version psVersion)
{
return AstOperations.FindSymbolsInDocument(
scriptFile.ScriptAst,
psVersion);

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

namespace Microsoft.PowerShell.EditorServices.Test.Shared.Symbols
{
public class FindSymbolsInPSDFile
{
public static readonly ScriptRegion SourceDetails =
new ScriptRegion
{
File = @"Symbols\PowerShellDataFile.psd1"
};
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

namespace Microsoft.PowerShell.EditorServices.Test.Shared.Symbols
{
public class FindSymbolsInPesterFile
{
public static readonly ScriptRegion SourceDetails =
new ScriptRegion
{
File = @"Symbols\PesterFile.tests.ps1"
};
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Describe "A dummy test" {
Context "When a pester file is given" {
It "Should return it symbols" {

}

It "Should return context symbols" {

}

It "Should return describe symbols" {

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@{
property1 = "value1"
property2 = "value2"
property3 = "value3"
}
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,20 @@ public void LanguageServiceFindsSymbolsInFile()
//Assert.Equal(1, firstConfigurationSymbol.ScriptRegion.StartColumnNumber);
}

[Fact]
public void LanguageServiceFindsSymbolsInPesterFile()
{
var symbolsResult = this.FindSymbolsInFile(FindSymbolsInPesterFile.SourceDetails);
Assert.Equal(5, symbolsResult.FoundOccurrences.Count());
}

[Fact]
public void LangServerFindsSymbolsInPSDFile()
{
var symbolsResult = this.FindSymbolsInFile(FindSymbolsInPSDFile.SourceDetails);
Assert.Equal(3, symbolsResult.FoundOccurrences.Count());
}

[Fact]
public void LanguageServiceFindsSymbolsInNoSymbolsFile()
{
Expand All @@ -310,7 +324,6 @@ public void LanguageServiceFindsSymbolsInNoSymbolsFile()
Assert.Equal(0, symbolsResult.FoundOccurrences.Count());
}


private ScriptFile GetScriptFile(ScriptRegion scriptRegion)
{
string resolvedPath =
Expand Down