Skip to content

Commit 0696b81

Browse files
committed
- Add nuspec
- Fix microsoft#501: PTVS-LS Integration: Fix LS hanging during file changes - Fix microsoft#502: PTVS-LS Integration: Add required *.py files to the vsix
1 parent 1a0f2ba commit 0696b81

File tree

8 files changed

+142
-58
lines changed

8 files changed

+142
-58
lines changed

src/Analysis/Engine/Impl/Definitions/IGroupableAnalysisProjectEntry.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ namespace Microsoft.PythonTools.Analysis {
2222
/// more efficient analysis.
2323
///
2424
/// To analyze the full group you call Analyze(true) on all the items in the same group (determined
25-
/// by looking at the identity of the AnalysGroup object). Then you call AnalyzeQueuedEntries on the
25+
/// by looking at the identity of the AnalysisGroup object). Then you call AnalyzeQueuedEntries on the
2626
/// group.
2727
/// </summary>
2828
public interface IGroupableAnalysisProjectEntry {
2929
/// <summary>
3030
/// Analyzes this project entry optionally just adding it to the queue shared by the project.
3131
/// </summary>
32-
void Analyze(CancellationToken cancel, bool enqueueOnly);
32+
void PreAnalyze();
3333

3434
IGroupableAnalysisProject AnalysisGroup { get; }
3535
}

src/Analysis/Engine/Impl/Intellisense/AnalysisQueue.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ public void Enqueue(IAnalyzable item, AnalysisPriority priority) {
137137
}
138138

139139
private async Task HandleAnalyzable(IAnalyzable item, AnalysisPriority priority, CancellationToken cancellationToken) {
140+
cancellationToken.ThrowIfCancellationRequested();
141+
140142
if (item is IGroupableAnalysisProjectEntry groupable) {
141143
var added = _enqueuedGroups.Add(groupable.AnalysisGroup);
142144
if (added) {
@@ -147,7 +149,7 @@ private async Task HandleAnalyzable(IAnalyzable item, AnalysisPriority priority,
147149
}
148150
}
149151

150-
groupable.Analyze(cancellationToken, true);
152+
groupable.PreAnalyze();
151153
} else {
152154
item.Analyze(cancellationToken);
153155
}

src/Analysis/Engine/Impl/ProjectEntry.cs

Lines changed: 90 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
using System.Diagnostics.CodeAnalysis;
2222
using System.IO;
2323
using System.Linq;
24+
using System.Runtime.CompilerServices;
2425
using System.Text;
2526
using System.Threading;
2627
using System.Threading.Tasks;
@@ -45,11 +46,10 @@ internal sealed class ProjectEntry : IPythonProjectEntry, IAggregateableProjectE
4546
private readonly ConcurrentQueue<WeakReference<ReferenceDict>> _backReferences = new ConcurrentQueue<WeakReference<ReferenceDict>>();
4647
private readonly HashSet<AggregateProjectEntry> _aggregates = new HashSet<AggregateProjectEntry>();
4748

48-
private TaskCompletionSource<IModuleAnalysis> _analysisTcs = new TaskCompletionSource<IModuleAnalysis>();
49+
private AnalysisCompletionToken _analysisCompletionToken;
4950
private AnalysisUnit _unit;
5051
private readonly ManualResetEventSlim _pendingParse = new ManualResetEventSlim(true);
5152
private long _expectedParseVersion;
52-
private long _expectedAnalysisVersion;
5353

5454
internal ProjectEntry(
5555
PythonAnalyzer state,
@@ -66,6 +66,7 @@ IAnalysisCookie cookie
6666

6767
MyScope = new ModuleInfo(ModuleName, this, state.Interpreter.CreateModuleContext());
6868
_unit = new AnalysisUnit(null, MyScope.Scope);
69+
_analysisCompletionToken = AnalysisCompletionToken.Default;
6970

7071
_buffers = new SortedDictionary<int, DocumentBuffer> { [0] = new DocumentBuffer() };
7172
if (Cookie is InitialContentCookie c) {
@@ -137,10 +138,10 @@ public IPythonParse GetCurrentParse() {
137138
}
138139
}
139140

140-
internal Task<IModuleAnalysis> GetAnalysisAsync(int waitingTimeout = -1, CancellationToken cancellationToken = default(CancellationToken)) {
141+
internal Task<IModuleAnalysis> GetAnalysisAsync(int waitingTimeout = -1, CancellationToken cancellationToken = default) {
141142
Task<IModuleAnalysis> task;
142143
lock (this) {
143-
task = _analysisTcs.Task;
144+
task = _analysisCompletionToken.Task;
144145
}
145146

146147
if (task.IsCompleted || waitingTimeout == -1 && !cancellationToken.CanBeCanceled) {
@@ -158,22 +159,15 @@ public IPythonParse GetCurrentParse() {
158159

159160
internal void SetCompleteAnalysis() {
160161
lock (this) {
161-
if (_expectedAnalysisVersion != Analysis.Version) {
162-
return;
163-
}
164-
_analysisTcs.TrySetResult(Analysis);
162+
_analysisCompletionToken.TrySetAnalysis(Analysis);
165163
}
166164
RaiseNewAnalysis();
167165
}
168166

169-
internal void ResetCompleteAnalysis() {
170-
TaskCompletionSource<IModuleAnalysis> analysisTcs = null;
167+
internal void NewAnalysisAwaitableOnParse() {
171168
lock (this) {
172-
_expectedAnalysisVersion = AnalysisVersion + 1;
173-
analysisTcs = _analysisTcs;
174-
_analysisTcs = new TaskCompletionSource<IModuleAnalysis>(TaskCreationOptions.RunContinuationsAsynchronously);
169+
_analysisCompletionToken = _analysisCompletionToken.NewParse();
175170
}
176-
analysisTcs?.TrySetCanceled();
177171
}
178172

179173
public void SetCurrentParse(PythonAst tree, IAnalysisCookie cookie, bool notify = true) {
@@ -195,38 +189,58 @@ public void SetCurrentParse(PythonAst tree, IAnalysisCookie cookie, bool notify
195189

196190
internal bool IsVisible(ProjectEntry assigningScope) => true;
197191

198-
public void Analyze(CancellationToken cancel) => Analyze(cancel, false);
199-
200-
public void Analyze(CancellationToken cancel, bool enqueueOnly) {
192+
public void Analyze(CancellationToken cancel) {
201193
if (cancel.IsCancellationRequested) {
202194
return;
203195
}
204196

205197
lock (this) {
206-
AnalysisVersion++;
198+
PrepareForAnalysis();
207199

208-
foreach (var aggregate in _aggregates) {
209-
aggregate?.BumpVersion();
210-
}
200+
ProjectState.AnalyzeQueuedEntries(cancel);
211201

212-
Parse(enqueueOnly, cancel);
202+
// publish the analysis now that it's complete/running
203+
Analysis = new ModuleAnalysis(
204+
_unit,
205+
((ModuleScope)_unit.Scope).CloneForPublish(),
206+
DocumentUri,
207+
AnalysisVersion
208+
);
213209
}
214210

215-
if (!enqueueOnly) {
216-
RaiseNewAnalysis();
211+
RaiseNewAnalysis();
212+
}
213+
214+
public void PreAnalyze() {
215+
lock (this) {
216+
PrepareForAnalysis();
217+
218+
// publish the analysis now that it's complete/running
219+
Analysis = new ModuleAnalysis(
220+
_unit,
221+
((ModuleScope)_unit.Scope).CloneForPublish(),
222+
DocumentUri,
223+
AnalysisVersion
224+
);
217225
}
218226
}
219227

220228
private void RaiseNewAnalysis() => NewAnalysis?.Invoke(this, EventArgs.Empty);
221229

222-
public int AnalysisVersion { get; private set; }
230+
public int AnalysisVersion => _analysisCompletionToken.Version;
223231

224232
public bool IsAnalyzed => Analysis != null;
225233

226-
private void Parse(bool enqueueOnly, CancellationToken cancel) {
234+
private void PrepareForAnalysis() {
227235
#if DEBUG
228236
Debug.Assert(Monitor.IsEntered(this));
229237
#endif
238+
_analysisCompletionToken = _analysisCompletionToken.NewAnalysis();
239+
240+
foreach (var aggregate in _aggregates) {
241+
aggregate?.BumpVersion();
242+
}
243+
230244
var parse = GetCurrentParse();
231245
var tree = parse?.Tree;
232246
var cookie = parse?.Cookie;
@@ -306,18 +320,6 @@ where lastDot > 0
306320
}
307321

308322
_unit.Enqueue();
309-
310-
if (!enqueueOnly) {
311-
ProjectState.AnalyzeQueuedEntries(cancel);
312-
}
313-
314-
// publish the analysis now that it's complete/running
315-
Analysis = new ModuleAnalysis(
316-
_unit,
317-
((ModuleScope)_unit.Scope).CloneForPublish(),
318-
DocumentUri,
319-
AnalysisVersion
320-
);
321323
}
322324

323325
public IGroupableAnalysisProject AnalysisGroup => ProjectState;
@@ -348,7 +350,7 @@ where lastDot > 0
348350

349351
public void Dispose() {
350352
lock (this) {
351-
AnalysisVersion = -1;
353+
_analysisCompletionToken = AnalysisCompletionToken.Disposed;
352354

353355
var state = ProjectState;
354356
foreach (var aggregatedInto in _aggregates) {
@@ -494,6 +496,55 @@ public void ResetDocument(int version, string content) {
494496
public void AddBackReference(ReferenceDict referenceDict) {
495497
_backReferences.Enqueue(new WeakReference<ReferenceDict>(referenceDict));
496498
}
499+
500+
private struct AnalysisCompletionToken {
501+
public static readonly AnalysisCompletionToken Default;
502+
public static readonly AnalysisCompletionToken Disposed;
503+
504+
static AnalysisCompletionToken() {
505+
var cancelledTcs = CreateTcs();
506+
cancelledTcs.SetCanceled();
507+
508+
Default = new AnalysisCompletionToken(CreateTcs(), -1, false);
509+
Disposed = new AnalysisCompletionToken(cancelledTcs, -1, true);
510+
}
511+
512+
private readonly bool _isParse;
513+
private readonly TaskCompletionSource<IModuleAnalysis> _tcs;
514+
515+
public int Version { get; }
516+
public Task<IModuleAnalysis> Task => _tcs.Task;
517+
518+
public AnalysisCompletionToken NewParse() {
519+
if (_isParse) {
520+
return this;
521+
}
522+
523+
var tcs = CreateTcs();
524+
_tcs.TrySetCanceled();
525+
return new AnalysisCompletionToken(tcs, Version + 1, true);
526+
}
527+
528+
public AnalysisCompletionToken NewAnalysis() {
529+
var tcs = _tcs.Task.IsCompleted ? CreateTcs() : _tcs;
530+
return new AnalysisCompletionToken(tcs, _isParse ? Version : Version + 1, false);
531+
}
532+
533+
private AnalysisCompletionToken(TaskCompletionSource<IModuleAnalysis> tcs, int version, bool isParse) {
534+
_tcs = tcs;
535+
Version = version;
536+
_isParse = isParse;
537+
}
538+
539+
public void TrySetAnalysis(IModuleAnalysis analysis) {
540+
if (Version == analysis.Version) {
541+
_tcs.TrySetResult(analysis);
542+
}
543+
}
544+
545+
private static TaskCompletionSource<IModuleAnalysis> CreateTcs()
546+
=> new TaskCompletionSource<IModuleAnalysis>(TaskCreationOptions.RunContinuationsAsynchronously);
547+
}
497548
}
498549

499550
class InitialContentCookie : IAnalysisCookie {

src/Analysis/Engine/Impl/PythonAnalyzer.cs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -201,9 +201,7 @@ public IPythonProjectEntry AddModule(string moduleName, string filePath, Uri doc
201201
}
202202
return entry;
203203
}
204-
205-
public void RemoveModule(IProjectEntry entry) => RemoveModule(entry, null);
206-
204+
207205
/// <summary>
208206
/// Removes the specified project entry from the current analysis.
209207
///
@@ -213,9 +211,8 @@ public IPythonProjectEntry AddModule(string moduleName, string filePath, Uri doc
213211
/// <param name="onImporter">Action to perform on each module that
214212
/// had imported the one being removed.</param>
215213
public void RemoveModule(IProjectEntry entry, Action<IPythonProjectEntry> onImporter) {
216-
if (entry == null) {
217-
throw new ArgumentNullException(nameof(entry));
218-
}
214+
Check.ArgumentNotNull(nameof(entry), entry);
215+
Check.ArgumentNotNull(nameof(onImporter), onImporter);
219216
Contract.EndContractBlock();
220217

221218
var pyEntry = entry as IPythonProjectEntry;
@@ -237,9 +234,6 @@ public void RemoveModule(IProjectEntry entry, Action<IPythonProjectEntry> onImpo
237234
entry.Dispose();
238235
ClearDiagnostics(entry);
239236

240-
if (onImporter == null) {
241-
onImporter = e => e.Analyze(CancellationToken.None, enqueueOnly: true);
242-
}
243237

244238
if (!string.IsNullOrEmpty(pyEntry?.ModuleName)) {
245239
Modules.TryRemove(pyEntry.ModuleName, out _);

src/Analysis/Engine/Test/ThreadingTest.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,11 @@ class MyClass:
131131

132132
// One analysis before we start
133133
foreach (var e in entries) {
134-
e.Analyze(cancel, true);
134+
e.PreAnalyze();
135135
}
136136
state.AnalyzeQueuedEntries(cancel);
137137

138-
// Repeatedly re-analyse the code
138+
// Repeatedly re-analyze the code
139139
yield return Task.Run(() => {
140140
var rnd = new Random();
141141
while (!cancel.IsCancellationRequested) {
@@ -146,7 +146,7 @@ class MyClass:
146146
.Select(t => t.Item2)
147147
.ToList();
148148
foreach (var e in shufEntries) {
149-
e.Analyze(cancel, true);
149+
e.PreAnalyze();
150150
}
151151

152152
state.AnalyzeQueuedEntries(cancel);

src/LanguageServer/Core/Impl/Implementation/Server.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ internal Task EnqueueItemAsync(IDocument doc, AnalysisPriority priority = Analys
583583
var pending = _pendingAnalysisEnqueue.Incremented();
584584
try {
585585
var entry = doc as ProjectEntry;
586-
entry?.ResetCompleteAnalysis();
586+
entry?.NewAnalysisAwaitableOnParse();
587587

588588
// If we don't need to parse, use null cookie
589589
var cookieTask = Task.FromResult<IAnalysisCookie>(null);

src/PTVS/Microsoft.PythonTools.Analyzer/Impl/Microsoft.PythonTools.Analyzer.csproj

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44
<RootNamespace>Microsoft.PythonTools.Analysis</RootNamespace>
55
<AssemblyTitle>Visual Studio - Python background analyzer</AssemblyTitle>
66
<AssemblyName>Microsoft.PythonTools.Analyzer</AssemblyName>
7+
<NuspecFile>python-language-server-ptvs.nuspec</NuspecFile>
8+
<NuspecProperties>version=$(NugetVersion)</NuspecProperties>
79
</PropertyGroup>
810
<Import Project="..\..\..\..\build\NetStandard.settings" />
911
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
1012
<PropertyGroup>
11-
<OutputType>Exe</OutputType>
12-
<DebugType>portable</DebugType>
13-
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
13+
<OutputType>Exe</OutputType>
14+
<DebugType>portable</DebugType>
15+
<NuspecBasePath>$(OutputPath)</NuspecBasePath>
16+
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
1417
</PropertyGroup>
1518
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
1619
<CodeAnalysisRuleSet>..\..\..\PLS.ruleset</CodeAnalysisRuleSet>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?xml version="1.0"?>
2+
<package >
3+
<metadata>
4+
<id>python-language-server-ptvs$os$</id>
5+
<version>$version$</version>
6+
<title>Python Language Server for PTVS</title>
7+
<authors>Microsoft</authors>
8+
<owners>Microsoft</owners>
9+
<license type="expression">Apache-2.0</license>
10+
<projectUrl>https://github.com/Microsoft/PTVS</projectUrl>
11+
<requireLicenseAcceptance>false</requireLicenseAcceptance>
12+
<description>Microsoft Python Language Server for PTVS</description>
13+
<copyright>Copyright (c) 2018</copyright>
14+
<tags>Python;VisualStudio</tags>
15+
</metadata>
16+
<files>
17+
<file src="Microsoft.Python.Analysis.Engine.dll" target="lib/vs16" />
18+
<file src="Microsoft.Python.Analysis.Engine.pdb" target="lib/vs16" />
19+
<file src="Microsoft.Python.LanguageServer.Core.dll" target="lib/vs16" />
20+
<file src="Microsoft.Python.LanguageServer.Core.pdb" target="lib/vs16" />
21+
<file src="Microsoft.PythonTools.Ipc.Json.dll" target="lib/vs16" />
22+
<file src="Microsoft.PythonTools.Ipc.Json.pdb" target="lib/vs16" />
23+
<file src="Microsoft.PythonTools.Analyzer.exe" target="lib/vs16" />
24+
<file src="Microsoft.PythonTools.Analyzer.exe.config" target="lib/vs16" />
25+
<file src="Microsoft.PythonTools.Analyzer.pdb" target="lib/vs16" />
26+
<file src="PythonScraper.py" target="lib/vs16" />
27+
<file src="BuiltinScraper.py" target="lib/vs16" />
28+
<file src="ExtensionScraper.py" target="lib/vs16" />
29+
<file src="IronPythonScraper.py" target="lib/vs16" />
30+
<file src="scrape_module.py" target="lib/vs16" />
31+
<file src="get_search_paths.py" target="lib/vs16" />
32+
<file src="typing-stub.pyi" target="lib/vs16" />
33+
</files>
34+
</package>

0 commit comments

Comments
 (0)