Skip to content

"flutter.setSubscriptions" call never forgets a file (aka will ask about an ever growing number of files) #7980

Closed
@jensjoha

Description

@jensjoha

In IntelliJ, opening a project with flutter in the environment in pubspec.yaml, e.g. something like

name: issue_whatnot
publish_to: "none"

environment:
  flutter: ">=3.29.0 <4.0.0"
  sdk: ">=3.7.0 <4.0.0"

opening a file sends a flutter.setSubscriptions request looking something like this:

{
  "id": "42",
  "method": "flutter.setSubscriptions",
  "params": {
    "subscriptions": {
      "OUTLINE": [
        "file:///path/to/file_1.dart"
      ]
    }
  }
}

Say we close the file and open another, it now sends something like this:

{
  "id": "42",
  "method": "flutter.setSubscriptions",
  "params": {
    "subscriptions": {
      "OUTLINE": [
        "file:///path/to/file_1.dart",
        "file:///path/to/file_2.dart"
      ]
    }
  }
}

closing that and opening a third, closing that and opening a forth etc a bunch of times and you'll end up with something like

{
  "id": "42",
  "method": "flutter.setSubscriptions",
  "params": {
    "subscriptions": {
      "OUTLINE": [
        "file:///path/to/file_1.dart",
        "file:///path/to/file_2.dart",
        ...
        "file:///path/to/file_n.dart",
      ]
    }
  }
}

at which point the analyzer will start to become slow:

5 ms (+ 22045 ms)	completion.getSuggestions2
23121 ms (+ 100 ms)	edit.getAssists

Notice that if I open a file I've already opened once it doesn't send the request, I believe because of this:

private void addSubscription(@NotNull final String service, @NotNull final String filePath) {
if(!isServerConnected()) {
return;
}
final List<String> files = subscriptions.computeIfAbsent(service, k -> new ArrayList<>());
final String filePathOrUri = getAnalysisService().getLocalFileUri(filePath);
if (!files.contains(filePathOrUri)) {
files.add(filePathOrUri);
sendSubscriptions();
}
}
.

(i.e. sendSubscriptions is only called i it actually adds the path)

I'm guessing something goes wrong in these lines:

final List<String> obsoletePaths = new ArrayList<>();
synchronized (outlineListeners) {
for (final String path : outlineListeners.keySet()) {
if (!newPaths.contains(path)) {
obsoletePaths.add(path);
}
}
for (final String path : obsoletePaths) {
final String filePathOrUri = getAnalysisServer().getAnalysisService().getLocalFileUri(path);
final FlutterOutlineListener listener = outlineListeners.remove(filePathOrUri);
if (listener != null) {
getAnalysisServer().removeOutlineListener(FileUtil.toSystemDependentName(path), listener);
}
}
// Register new outline listeners.
for (final String path : newPaths) {
final String filePathOrUri = getAnalysisServer().getAnalysisService().getLocalFileUri(path);
if (outlineListeners.containsKey(filePathOrUri)) {
continue;
}
final FlutterOutlineListener listener = new OutlineListener(filePathOrUri);
outlineListeners.put(filePathOrUri, listener);
getAnalysisServer().addOutlineListener(FileUtil.toSystemDependentName(path), listener);
}
}

Without having looked into it it seems suspect that it takes a key from outlineListeners (final String path : outlineListeners.keySet()) and then does something to it before trying to remove it again (final String filePathOrUri = getAnalysisServer().getAnalysisService().getLocalFileUri(path);, final FlutterOutlineListener listener = outlineListeners.remove(filePathOrUri);), but I'm guessing.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions