Skip to content

Commit 510f596

Browse files
committed
Optimize CodeLens providers
Removing the timer and unnecessary allocations.
1 parent f087363 commit 510f596

File tree

4 files changed

+40
-56
lines changed

4 files changed

+40
-56
lines changed

src/PowerShellEditorServices/Services/CodeLens/ICodeLensProvider.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4+
using System.Collections.Generic;
45
using System.Threading;
56
using System.Threading.Tasks;
67
using Microsoft.PowerShell.EditorServices.Services.TextDocument;
@@ -27,8 +28,8 @@ internal interface ICodeLensProvider
2728
/// The document for which CodeLenses should be provided.
2829
/// </param>
2930
/// <param name="cancellationToken"></param>
30-
/// <returns>An array of CodeLenses.</returns>
31-
CodeLens[] ProvideCodeLenses(ScriptFile scriptFile, CancellationToken cancellationToken);
31+
/// <returns>An IEnumerable of CodeLenses.</returns>
32+
IEnumerable<CodeLens> ProvideCodeLenses(ScriptFile scriptFile, CancellationToken cancellationToken);
3233

3334
/// <summary>
3435
/// Resolves a CodeLens that was created without a Command.

src/PowerShellEditorServices/Services/CodeLens/PesterCodeLensProvider.cs

+10-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
3-
using System;
43
using System.Collections.Generic;
54
using System.Threading;
65
using System.Threading.Tasks;
@@ -99,18 +98,20 @@ private static CodeLens[] GetPesterLens(PesterSymbolReference pesterSymbol, Scri
9998
/// <param name="scriptFile">The script file to get Pester CodeLenses for.</param>
10099
/// <param name="cancellationToken"></param>
101100
/// <returns>All Pester CodeLenses for the given script file.</returns>
102-
public CodeLens[] ProvideCodeLenses(ScriptFile scriptFile, CancellationToken cancellationToken)
101+
public IEnumerable<CodeLens> ProvideCodeLenses(ScriptFile scriptFile, CancellationToken cancellationToken)
103102
{
104103
// Don't return anything if codelens setting is disabled
105104
if (!_configurationService.CurrentSettings.Pester.CodeLens)
106105
{
107-
return Array.Empty<CodeLens>();
106+
yield break;
108107
}
109108

110-
List<CodeLens> lenses = new();
111109
foreach (SymbolReference symbol in _symbolProvider.ProvideDocumentSymbols(scriptFile))
112110
{
113-
cancellationToken.ThrowIfCancellationRequested();
111+
if (cancellationToken.IsCancellationRequested)
112+
{
113+
yield break;
114+
}
114115

115116
if (symbol is not PesterSymbolReference pesterSymbol)
116117
{
@@ -129,10 +130,11 @@ public CodeLens[] ProvideCodeLenses(ScriptFile scriptFile, CancellationToken can
129130
continue;
130131
}
131132

132-
lenses.AddRange(GetPesterLens(pesterSymbol, scriptFile));
133+
foreach (CodeLens codeLens in GetPesterLens(pesterSymbol, scriptFile))
134+
{
135+
yield return codeLens;
136+
}
133137
}
134-
135-
return lenses.ToArray();
136138
}
137139

138140
/// <summary>

src/PowerShellEditorServices/Services/CodeLens/ReferencesCodeLensProvider.cs

+9-8
Original file line numberDiff line numberDiff line change
@@ -53,33 +53,34 @@ public ReferencesCodeLensProvider(WorkspaceService workspaceService, SymbolsServ
5353
/// </summary>
5454
/// <param name="scriptFile">The PowerShell script file to get code lenses for.</param>
5555
/// <param name="cancellationToken"></param>
56-
/// <returns>An array of CodeLenses describing all functions, classes and enums in the given script file.</returns>
57-
public CodeLens[] ProvideCodeLenses(ScriptFile scriptFile, CancellationToken cancellationToken)
56+
/// <returns>An IEnumerable of CodeLenses describing all functions, classes and enums in the given script file.</returns>
57+
public IEnumerable<CodeLens> ProvideCodeLenses(ScriptFile scriptFile, CancellationToken cancellationToken)
5858
{
59-
List<CodeLens> acc = new();
6059
foreach (SymbolReference symbol in _symbolProvider.ProvideDocumentSymbols(scriptFile))
6160
{
62-
cancellationToken.ThrowIfCancellationRequested();
61+
if (cancellationToken.IsCancellationRequested)
62+
{
63+
yield break;
64+
}
65+
6366
// TODO: Can we support more here?
6467
if (symbol.IsDeclaration &&
6568
symbol.Type is
6669
SymbolType.Function or
6770
SymbolType.Class or
6871
SymbolType.Enum)
6972
{
70-
acc.Add(new CodeLens
73+
yield return new CodeLens
7174
{
7275
Data = JToken.FromObject(new
7376
{
7477
Uri = scriptFile.DocumentUri,
7578
ProviderId = nameof(ReferencesCodeLensProvider)
7679
}, LspSerializer.Instance.JsonSerializer),
7780
Range = symbol.NameRegion.ToRange(),
78-
});
81+
};
7982
}
8083
}
81-
82-
return acc.ToArray();
8384
}
8485

8586
/// <summary>

src/PowerShellEditorServices/Services/TextDocument/Handlers/CodeLensHandlers.cs

+18-38
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,11 @@
33

44
using System;
55
using System.Collections.Generic;
6-
using System.Diagnostics;
76
using System.Linq;
87
using System.Threading;
98
using System.Threading.Tasks;
109
using Microsoft.Extensions.Logging;
1110
using Microsoft.PowerShell.EditorServices.CodeLenses;
12-
using Microsoft.PowerShell.EditorServices.Logging;
1311
using Microsoft.PowerShell.EditorServices.Services;
1412
using Microsoft.PowerShell.EditorServices.Services.TextDocument;
1513
using Microsoft.PowerShell.EditorServices.Utility;
@@ -21,6 +19,7 @@ namespace Microsoft.PowerShell.EditorServices.Handlers
2119
{
2220
internal class PsesCodeLensHandlers : CodeLensHandlerBase
2321
{
22+
private static readonly CodeLensContainer s_emptyCodeLensContainer = new();
2423
private readonly ILogger _logger;
2524
private readonly SymbolsService _symbolsService;
2625
private readonly WorkspaceService _workspaceService;
@@ -40,12 +39,17 @@ public PsesCodeLensHandlers(ILoggerFactory factory, SymbolsService symbolsServic
4039

4140
public override Task<CodeLensContainer> Handle(CodeLensParams request, CancellationToken cancellationToken)
4241
{
42+
_logger.LogDebug($"Handling code lens request for {request.TextDocument.Uri}");
43+
4344
ScriptFile scriptFile = _workspaceService.GetFile(request.TextDocument.Uri);
44-
CodeLens[] codeLensResults = ProvideCodeLenses(scriptFile, cancellationToken);
45-
return Task.FromResult(new CodeLensContainer(codeLensResults));
45+
IEnumerable<CodeLens> codeLensResults = ProvideCodeLenses(scriptFile, cancellationToken);
46+
47+
return !codeLensResults.Any()
48+
? Task.FromResult(s_emptyCodeLensContainer)
49+
: Task.FromResult(new CodeLensContainer(codeLensResults));
4650
}
4751

48-
public override async Task<CodeLens> Handle(CodeLens request, CancellationToken cancellationToken)
52+
public override Task<CodeLens> Handle(CodeLens request, CancellationToken cancellationToken)
4953
{
5054
// TODO: Catch deserialization exception on bad object
5155
CodeLensData codeLensData = request.Data.ToObject<CodeLensData>();
@@ -55,8 +59,7 @@ public override async Task<CodeLens> Handle(CodeLens request, CancellationToken
5559
.FirstOrDefault(provider => provider.ProviderId.Equals(codeLensData.ProviderId, StringComparison.Ordinal));
5660

5761
ScriptFile scriptFile = _workspaceService.GetFile(codeLensData.Uri);
58-
return await originalProvider.ResolveCodeLens(request, scriptFile, cancellationToken)
59-
.ConfigureAwait(false);
62+
return originalProvider.ResolveCodeLens(request, scriptFile, cancellationToken);
6063
}
6164

6265
/// <summary>
@@ -65,43 +68,20 @@ public override async Task<CodeLens> Handle(CodeLens request, CancellationToken
6568
/// <param name="scriptFile">The PowerShell script file to get CodeLenses for.</param>
6669
/// <param name="cancellationToken"></param>
6770
/// <returns>All generated CodeLenses for the given script file.</returns>
68-
private CodeLens[] ProvideCodeLenses(ScriptFile scriptFile, CancellationToken cancellationToken)
69-
{
70-
return InvokeProviders(provider => provider.ProvideCodeLenses(scriptFile, cancellationToken))
71-
.SelectMany(codeLens => codeLens)
72-
.ToArray();
73-
}
74-
75-
/// <summary>
76-
/// Invokes the given function synchronously against all
77-
/// registered providers.
78-
/// </summary>
79-
/// <param name="invokeFunc">The function to be invoked.</param>
80-
/// <returns>
81-
/// An IEnumerable containing the results of all providers
82-
/// that were invoked successfully.
83-
/// </returns>
84-
private IEnumerable<TResult> InvokeProviders<TResult>(Func<ICodeLensProvider, TResult> invokeFunc)
71+
private IEnumerable<CodeLens> ProvideCodeLenses(ScriptFile scriptFile, CancellationToken cancellationToken)
8572
{
86-
Stopwatch invokeTimer = new();
87-
List<TResult> providerResults = new();
88-
8973
foreach (ICodeLensProvider provider in _symbolsService.GetCodeLensProviders())
9074
{
91-
try
92-
{
93-
invokeTimer.Restart();
94-
providerResults.Add(invokeFunc(provider));
95-
invokeTimer.Stop();
96-
_logger.LogTrace($"Invocation of provider '{provider.GetType().Name}' completed in {invokeTimer.ElapsedMilliseconds}ms.");
97-
}
98-
catch (Exception e)
75+
foreach (CodeLens codeLens in provider.ProvideCodeLenses(scriptFile, cancellationToken))
9976
{
100-
_logger.LogException($"Exception caught while invoking provider {provider.GetType().Name}:", e);
77+
if (cancellationToken.IsCancellationRequested)
78+
{
79+
yield break;
80+
}
81+
82+
yield return codeLens;
10183
}
10284
}
103-
104-
return providerResults;
10585
}
10686
}
10787
}

0 commit comments

Comments
 (0)