Skip to content
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

Pylance will request PTVS to listen for "**/*" under root but we dont… #8059

Closed
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ public void AddPatterns(FileSystemWatcher[] patterns) {
Array.ForEach(patterns, p => AddPattern(p));
}

public void AddExclude(FileSystemWatcher pattern) {
_matcher.AddExclude(pattern.GlobPattern);
}
private void AddPattern(FileSystemWatcher pattern) {
// Add to our matcher
_matcher.AddInclude(pattern.GlobPattern);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ internal sealed class PythonLanguageClient : ILanguageClient, ILanguageClientCus
private List<IPythonLanguageClientContext> _clientContexts = new List<IPythonLanguageClientContext>();
private PythonAnalysisOptions _analysisOptions;
private PythonAdvancedEditorOptions _advancedEditorOptions;
private ITaskList _taskListService;
private ITaskList _taskListService;
private LanguageServer _server;
private JsonRpc _rpc;
private bool _workspaceFoldersSupported = false;
Expand All @@ -99,7 +99,7 @@ internal sealed class PythonLanguageClient : ILanguageClient, ILanguageClientCus
private static TaskCompletionSource<int> _readyTcs = new TaskCompletionSource<int>();
private bool _loaded = false;
private Timer _deferredSettingsChangedTimer;
private const int _defaultSettingsDelayMS = 2000;
private const int _defaultSettingsDelayMS = 5000;
Copy link
Member Author

Choose a reason for hiding this comment

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

increasing settings delay helps delay analysis on while installing new packages


public PythonLanguageClient() {
_disposables = new Common.Core.Disposables.DisposableBag(GetType().Name);
Expand Down Expand Up @@ -152,10 +152,10 @@ public async Task<Connection> ActivateAsync(CancellationToken token) {

_disposables.Add(() => {
_clientContexts.ForEach(c => {
c.InterpreterChanged -= OnSettingsChanged;
c.InterpreterChanged -= OnInterpreterChanged;
c.SearchPathsChanged -= OnSettingsChanged;
c.ReanalyzeChanged -= OnReanalyzeChanged;
});
});
_analysisOptions.Changed -= OnSettingsChanged;
_advancedEditorOptions.Changed -= OnSettingsChanged;

Expand All @@ -164,7 +164,7 @@ public async Task<Connection> ActivateAsync(CancellationToken token) {
taskListService.PropertyChanged -= OnSettingsChanged;
} catch (ServiceUnavailableException) {
}

_clientContexts.ForEach(c => c.Dispose());
_clientContexts.Clear();
solutionEvents.Opened -= OnSolutionOpened;
Expand Down Expand Up @@ -215,7 +215,7 @@ private async Task OnWorkspaceConfiguration(object sender, WorkspaceConfiguratio

var pythonSetting = GetSettings(item.scopeUri);

if(pythonSetting == null) {
if (pythonSetting == null) {
continue;
}

Expand Down Expand Up @@ -256,7 +256,7 @@ public Task OnServerInitializeFailedAsync(Exception e) {
}

public Task AttachForCustomMessageAsync(JsonRpc rpc) {

_rpc = rpc;

// This is a workaround until we have proper API from ILanguageClient for now.
Expand All @@ -269,7 +269,6 @@ public Task AttachForCustomMessageAsync(JsonRpc rpc) {
_fileListener = null;
_fileListener = new FileWatcher.Listener(_rpc, WorkspaceService, Site);
_disposables.Add(_fileListener);

// We also need to switch the order on handlers for all of the rpc targets. Until
// the VSSDK gives us a way to do this, use reflection.
try {
Expand Down Expand Up @@ -301,15 +300,15 @@ public Task AttachForCustomMessageAsync(JsonRpc rpc) {
} catch {
// Any exceptions, just skip this part
}

return Task.CompletedTask;
}

public void Dispose() => _disposables.TryDispose();

public void AddClientContext(IPythonLanguageClientContext context) {
_clientContexts.Add(context);
context.InterpreterChanged += OnSettingsChanged;
context.InterpreterChanged += OnInterpreterChanged;
context.SearchPathsChanged += OnSettingsChanged;
context.ReanalyzeChanged += OnReanalyzeChanged;
}
Expand Down Expand Up @@ -367,16 +366,15 @@ private async Task NotifyWithParametersAsync(string request, object parameters)
await _rpc.NotifyWithParameterObjectAsync(request, parameters).ConfigureAwait(false);
}

private LanguageServerSettings.PythonSettings GetSettings(Uri scopeUri = null)
{
private LanguageServerSettings.PythonSettings GetSettings(Uri scopeUri = null) {
IPythonLanguageClientContext context = null;
if (scopeUri == null) {
// REPL context has null RootPath
context = _clientContexts.Find(c => c.RootPath == null);
if (context == null) {
if (context == null) {
return null;
}
}else {
} else {
var pathFromScopeUri = CommonUtils.NormalizeDirectoryPath(scopeUri.LocalPath).ToLower().TrimStart('\\');
// Find the matching context for the item, but ignore interactive window where "RootPath" is null.
context = _clientContexts.Find(c => scopeUri != null && c.RootPath != null && PathUtils.IsSamePath(c.RootPath.ToLower(), pathFromScopeUri));
Expand Down Expand Up @@ -408,12 +406,9 @@ private LanguageServerSettings.PythonSettings GetSettings(Uri scopeUri = null)
// get task list tokens from options
var taskListTokens = new List<LanguageServerSettings.PythonSettings.PythonAnalysisSettings.TaskListToken>();
var taskListService = Site.GetService<SVsTaskList, ITaskList>();
if (taskListService != null)
{
foreach (var commentToken in taskListService.CommentTokens)
{
taskListTokens.Add(new LanguageServerSettings.PythonSettings.PythonAnalysisSettings.TaskListToken()
{
if (taskListService != null) {
foreach (var commentToken in taskListService.CommentTokens) {
taskListTokens.Add(new LanguageServerSettings.PythonSettings.PythonAnalysisSettings.TaskListToken() {
text = commentToken.Text,
priority = commentToken.Priority.ToString()
});
Expand Down Expand Up @@ -449,6 +444,21 @@ private LanguageServerSettings.PythonSettings GetSettings(Uri scopeUri = null)
return settings;

}
private void OnInterpreterChanged(object sender, EventArgs e) {

// By default Pylance will tell us to watch everything under the workspace with pattern "**/*"
// we can exclude the interpreter directory because we already have package managers listening to them
this._clientContexts.ForEach(context => {

if (PathUtils.IsSubpathOf(context.RootPath, context.InterpreterConfiguration.InterpreterPath)) {
var pattern = CommonUtils.GetRelativeFilePath(context.RootPath, context.InterpreterConfiguration.GetPrefixPath()) + "/**/*";
var watcher = new FileSystemWatcher() { GlobPattern = pattern };
this._fileListener.AddExclude(watcher);
Copy link
Member Author

Choose a reason for hiding this comment

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

this is the fix. just exclude files under the interpreter if the interpreter is under the root workspace

}

});
OnSettingsChanged(sender, e);
}

private void OnSettingsChanged(object sender, EventArgs e) {
try {
Expand All @@ -470,7 +480,7 @@ private void OnReanalyzeChanged(object sender, EventArgs e) {
} catch (ObjectDisposedException) {
}
}

private bool TryGetOpenedDocumentData(RunningDocumentInfo info, out ITextBuffer textBuffer, out string filePath) {
textBuffer = null;
filePath = string.Empty;
Expand Down Expand Up @@ -514,8 +524,16 @@ private void OnWorkspaceFolderWatched(object sender, EventArgs e) {
}

private void WatchedFilesRegistered(object sender, DidChangeWatchedFilesRegistrationOptions e) {
// Add the file globs to our listener. It will listen to the globs
_fileListener?.AddPatterns(e.Watchers);

//this._clientContexts.ForEach(context => {

// if (PathUtils.IsSubpathOf(context.RootPath, context.InterpreterConfiguration.InterpreterPath)) {
// var pattern = CommonUtils.GetRelativeFilePath(context.RootPath, context.InterpreterConfiguration.GetPrefixPath()) + "/**/*";
// var watcher = new FileSystemWatcher() { GlobPattern = pattern };
// this._fileListener.AddExclude(watcher);
// }

//});
}

private void CreateClientContexts() {
Expand Down Expand Up @@ -549,7 +567,7 @@ private Tuple<StreamData, bool> OnSendToServer(StreamData data) {
capabilities["workspace"]["workspaceFolders"] = true;
capabilities["workspace"]["didChangeWatchedFiles"]["dynamicRegistration"] = true;
capabilities["workspace"]["configuration"] = true;

var folders = GetFolders();
Debug.Assert(folders.Any(), "no workspace or projects found");
messageParams["workspaceFolders"] = JToken.FromObject(folders.ToArray());
Expand All @@ -569,15 +587,15 @@ private Tuple<StreamData, bool> OnSendToServer(StreamData data) {
}
} else if (message.Value<string>("method") == "textDocument/codeAction") {
if (message.TryGetValue("params", out JToken messageParams)) {
if (messageParams != null && messageParams["range"] != null
&& messageParams["context"] != null
if (messageParams != null && messageParams["range"] != null
&& messageParams["context"] != null
&& messageParams["context"]["_vs_selectionRange"] != null) {
var selectionRange = messageParams["context"]["_vs_selectionRange"];
messageParams["range"]["start"] = selectionRange["start"];
messageParams["range"]["end"] = selectionRange["end"];
return Tuple.Create(MessageParser.Serialize(message), true);
}

}
}
} catch {
Expand All @@ -596,7 +614,7 @@ private List<WorkspaceFolder> GetFolders() {
return [folder];
} else {
var folders = from n in this.ProjectContextProvider.ProjectNodes
select new WorkspaceFolder { uri = new System.Uri(n.BaseURI.Directory), name = n.Name };
select new WorkspaceFolder { uri = new System.Uri(n.BaseURI.Directory), name = n.Name };
return folders.ToList();
}
}
Expand All @@ -607,11 +625,11 @@ private async Task OnWorkspaceOpening(object sender, EventArgs e) {
// Send just this workspace folder. Assumption here is that the language client will be destroyed/recreated on
// each workspace open
var folders = GetFolders();
this._workspaceFolders= folders.ToList();
this._workspaceFolders = folders.ToList();
await InvokeDidChangeWorkspaceFoldersAsync(folders.ToArray(), new WorkspaceFolder[0]);
}
}

// Note this is called for both openFolder and openProject modes
private void OnSolutionClosing() {
this._clientContexts.Clear();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ internal set {
_activePackageManagers = null;
foreach (var pm in oldPms.MaybeEnumerate()) {
pm.InstalledFilesChanged -= PackageManager_InstalledFilesChanged;
pm.DisableNotifications();
}

lock (_validFactories) {
Expand All @@ -258,15 +259,6 @@ internal set {
}
}

// start listening for package changes on the active interpreter again if interpreter is outside our workspace.
_activePackageManagers = InterpreterOptions.GetPackageManagers(_active).ToArray();
if (_active != null && !PathUtils.IsSubpathOf(ProjectHome, _active.Configuration.InterpreterPath)) {
foreach (var pm in _activePackageManagers) {
pm.InstalledFilesChanged += PackageManager_InstalledFilesChanged;
pm.EnableNotifications();
}
}

// update the InterpreterId element in the pyproj with the new active interpreter
if (_active != oldActive) {
if (_active != null) {
Expand Down Expand Up @@ -296,6 +288,15 @@ internal set {
ActiveInterpreterChanged?.Invoke(this, EventArgs.Empty);
}
BoldActiveEnvironment();

// start listening for package changes on the active interpreter again if interpreter is outside our workspace.
_activePackageManagers = InterpreterOptions.GetPackageManagers(_active).ToArray();
if (_active != null) {
foreach (var pm in _activePackageManagers) {
pm.InstalledFilesChanged += PackageManager_InstalledFilesChanged;
pm.EnableNotifications();
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ sealed class PythonWorkspaceContext : IPythonWorkspaceContext {
private const string TestFrameworkProperty = "TestFramework";
private const string UnitTestRootDirectoryProperty = "UnitTestRootDirectory";
private const string UnitTestPatternProperty = "UnitTestPattern";
private const int DebounceDelayMS = 10000;

private readonly IWorkspace _workspace;
private readonly IPropertyEvaluatorService _propertyEvaluatorService;
Expand Down Expand Up @@ -151,13 +152,12 @@ public void Initialize() {
_registryService.InterpretersChanged += OnInterpretersChanged;

_activePackageManagers = _optionsService.GetPackageManagers(_factory).ToArray();

if (!PathUtils.IsSubpathOf(_workspace.Location, _factory.Configuration.InterpreterPath)) {
foreach (var pm in _activePackageManagers) {
pm.InstalledFilesChanged += PackageManager_InstalledFilesChanged;
pm.EnableNotifications();
}

foreach (var pm in _activePackageManagers) {
pm.InstalledFilesChanged += PackageManager_InstalledFilesChanged;
pm.EnableNotifications();
}

}

public void Dispose() {
Expand Down Expand Up @@ -357,6 +357,7 @@ private void ReloadInterpreterSetting() {

foreach (var pm in EnumerableExtensions.MaybeEnumerate(oldPms)) {
pm.InstalledFilesChanged -= PackageManager_InstalledFilesChanged;
pm.DisableNotifications();
}

RefreshCurrentFactory();
Expand All @@ -366,19 +367,17 @@ private void ReloadInterpreterSetting() {
newFactory = _factory;
}


if (oldFactory?.Configuration.Id != newFactory?.Configuration.Id) {
ActiveInterpreterChanged?.Invoke(this, EventArgs.Empty);
}

_activePackageManagers = _optionsService.GetPackageManagers(newFactory).ToArray();
foreach (var pm in _activePackageManagers) {
if (!PathUtils.IsSubpathOf(_workspace.Location, newFactory.Configuration.InterpreterPath)) {
pm.InstalledFilesChanged += PackageManager_InstalledFilesChanged;
pm.EnableNotifications();
}
}


if (oldFactory?.Configuration.Id != newFactory?.Configuration.Id) {
ActiveInterpreterChanged?.Invoke(this, EventArgs.Empty);
}
}

private Task OnSettingsChanged(object sender, EventArgs e) {
Expand Down Expand Up @@ -425,6 +424,7 @@ private Task OnSettingsChanged(object sender, EventArgs e) {
_registryService.InterpretersChanged -= OnInterpretersChanged;
foreach (var pm in EnumerableExtensions.MaybeEnumerate(_activePackageManagers)) {
pm.InstalledFilesChanged -= PackageManager_InstalledFilesChanged;
pm.DisableNotifications();
}

try {
Expand All @@ -437,17 +437,17 @@ private Task OnSettingsChanged(object sender, EventArgs e) {
var oldFactory = CurrentFactory;
RefreshCurrentFactory();

if (oldFactory != CurrentFactory) {
ActiveInterpreterChanged?.Invoke(this, EventArgs.Empty);
}

_activePackageManagers = _optionsService.GetPackageManagers(_factory).ToArray();
if (!PathUtils.IsSubpathOf(_workspace.Location, _factory.Configuration.InterpreterPath)) {
foreach (var pm in _activePackageManagers) {
pm.InstalledFilesChanged += PackageManager_InstalledFilesChanged;
pm.EnableNotifications();
}
}

if (oldFactory != CurrentFactory) {
ActiveInterpreterChanged?.Invoke(this, EventArgs.Empty);
}
}

if (searchPathsChanged) {
Expand All @@ -472,7 +472,7 @@ public void AddActionOnClose(object key, Action<object> action) {

private void PackageManager_InstalledFilesChanged(object sender, EventArgs e) {
try {
_reanalyzeWorkspaceNotification.Change(500, Timeout.Infinite);
_reanalyzeWorkspaceNotification.Change(DebounceDelayMS, Timeout.Infinite);
} catch (ObjectDisposedException) {
}
}
Expand Down