Skip to content

Conversation

@helin24
Copy link
Owner

@helin24 helin24 commented Nov 5, 2025

No description provided.

@helin24
Copy link
Owner Author

helin24 commented Nov 5, 2025

/gemini

@gemini-code-assist
Copy link

Summary of Changes

Hello @helin24, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant enhancement to the Flutter IntelliJ plugin by enabling the monitoring and reporting of the user's active editor location to the Dart Tooling Daemon (DTD). This capability allows for more dynamic and context-aware interactions between the IDE and external Dart/Flutter tools, potentially leading to richer development experiences. The changes involve integrating with IntelliJ's core editor event system, defining a new DTD service, and ensuring efficient, debounced event communication.

Highlights

  • Editor Location Tracking: Implemented new functionality to track the active editor's file and caret/selection positions within the IDE.
  • DTD Service Registration: Registered a new Dart Tooling Daemon (DTD) service method, 'getActiveLocation', under the 'Editor' stream to expose real-time editor location data.
  • Event Notification System: Set up listeners for editor selection and caret position changes, which trigger debounced 'activeLocationChanged' events to be sent to the DTD.
  • Code Refactoring for DTD Events: Introduced a generic sendDtdEvent helper method to streamline and reduce duplication in the process of dispatching DTD events, also refactoring the existing theme change event.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request adds a new service to the Dart Tooling Daemon (DTD) for tracking the active editor location. It includes registering a new Editor.getActiveLocation service, setting up listeners for editor and caret changes, and sending activeLocationChanged events to DTD. The changes also include a refactoring to centralize DTD event sending logic.

My review focuses on potential memory leaks, code duplication, and performance considerations. I've identified a potential memory leak in how message bus connections are handled and significant code duplication in the event listener setup. I've also suggested a shorter debounce interval for location change events to improve responsiveness. The implementation of getActiveLocation appears to be a stub, which is noted for future work.

Comment on lines +274 to +325
private void setUpLocationChangeNotifications(@NotNull Project project) {
project.getMessageBus().connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, new FileEditorManagerListener() {
@Override
public void selectionChanged(@NotNull FileEditorManagerEvent event) {
if (lastEditor != null && caretListener != null) {
lastEditor.getCaretModel().removeCaretListener(caretListener);
}

final FileEditor newEditor = event.getNewEditor();
if (newEditor instanceof TextEditor) {
lastEditor = ((TextEditor)newEditor).getEditor();
if (caretListener == null) {
caretListener = new CaretListener() {
@Override
public void caretPositionChanged(@NotNull CaretEvent event) {
OpenApiUtils.safeInvokeLater(() -> {
List<CaretState> selections = event.getCaret().getCaretModel().getCaretsAndSelections();
sendActiveLocationChangedEvent(project, event.getEditor(), selections);
});
}
};
}
lastEditor.getCaretModel().addCaretListener(caretListener);
} else {
lastEditor = null;
}
}
});

// Handle the initially selected editor
final FileEditorManager fileEditorManager = FileEditorManager.getInstance(project);
final FileEditor[] selectedEditors = fileEditorManager.getSelectedEditors();
if (selectedEditors.length > 0) {
final FileEditor firstEditor = selectedEditors[0];
if (firstEditor instanceof TextEditor) {
if (caretListener == null) {
caretListener = new CaretListener() {
@Override
public void caretPositionChanged(@NotNull CaretEvent event) {
OpenApiUtils.safeInvokeLater(() -> {
List<CaretState> selections = event.getCaret().getCaretModel().getCaretsAndSelections();
sendActiveLocationChangedEvent(project, event.getEditor(), selections);
});
}
};
}
lastEditor = ((TextEditor)firstEditor).getEditor();
lastEditor.getCaretModel().addCaretListener(caretListener);
//sendActiveLocationChangedEvent(project, lastEditor, lastEditor.getCaretModel().getCaretsAndSelections());
}
}
}

Choose a reason for hiding this comment

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

high

This method has some areas for improvement:

  1. Potential Memory Leak: The message bus connection is created with project.getMessageBus().connect() without providing a Disposable parent. This can lead to a memory leak as the connection might not be disposed of when the project closes. You should pass a disposable parent, for example FlutterDartAnalysisServer.getInstance(project), to the connect() method.

  2. Code Duplication: The logic for creating the CaretListener and attaching it to an editor is duplicated for handling selection changes and for the initially selected editor. This can be refactored to improve maintainability and reduce redundancy.

Here's a suggested refactoring that addresses both points and simplifies the logic:

  private void setUpLocationChangeNotifications(@NotNull Project project) {
    if (caretListener == null) {
      caretListener = new CaretListener() {
        @Override
        public void caretPositionChanged(@NotNull CaretEvent event) {
          OpenApiUtils.safeInvokeLater(() -> {
            List<CaretState> selections = event.getCaret().getCaretModel().getCaretsAndSelections();
            sendActiveLocationChangedEvent(project, event.getEditor(), selections);
          });
        }
      };
    }

    project.getMessageBus().connect(FlutterDartAnalysisServer.getInstance(project)).subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, new FileEditorManagerListener() {
      @Override
      public void selectionChanged(@NotNull FileEditorManagerEvent event) {
        if (lastEditor != null) {
          lastEditor.getCaretModel().removeCaretListener(caretListener);
        }

        final FileEditor newEditor = event.getNewEditor();
        if (newEditor instanceof TextEditor) {
          lastEditor = ((TextEditor)newEditor).getEditor();
          lastEditor.getCaretModel().addCaretListener(caretListener);
        } else {
          lastEditor = null;
        }
      }
    });

    // Handle the initially selected editor
    final FileEditorManager fileEditorManager = FileEditorManager.getInstance(project);
    final FileEditor[] selectedEditors = fileEditorManager.getSelectedEditors();
    if (selectedEditors.length > 0) {
      final FileEditor firstEditor = selectedEditors[0];
      if (firstEditor instanceof TextEditor) {
        lastEditor = ((TextEditor)firstEditor).getEditor();
        lastEditor.getCaretModel().addCaretListener(caretListener);
        //sendActiveLocationChangedEvent(project, lastEditor, lastEditor.getCaretModel().getCaretsAndSelections());
      }
    }
  }

Comment on lines +253 to +255
dtdService.registerServiceMethod("Editor", "getActiveLocation", new JsonObject(), request -> {
return new DartToolingDaemonResponse(new JsonObject(), null);
});

Choose a reason for hiding this comment

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

medium

The implementation for the getActiveLocation service method is currently a stub that returns an empty JsonObject. While this might be intentional for now, it should be updated to return the actual active editor location to be fully functional. This is likely a placeholder for future work, but it's important to track.

log().info("Sending activeLocationChanged event");
//log().info(params);
sendDtdEvent(project, "activeLocationChanged", params);
}, 1, TimeUnit.SECONDS);

Choose a reason for hiding this comment

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

medium

A debounce time of 1 second for active location changes might feel unresponsive to the user. Consider reducing this to a smaller value, like 200-500 milliseconds, to provide more immediate feedback while still preventing excessive event traffic.

    }, 500, TimeUnit.MILLISECONDS);

}

private void registerDtdServices(@NotNull Project project) {
new DtdUtils().readyDtdService(project).thenAccept(dtdService -> {
Copy link
Owner Author

Choose a reason for hiding this comment

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

@gemini-code-assist what happens if it takes DTD too long to respond? Will it prevent setting up listening for theme changes?

Choose a reason for hiding this comment

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

That's a great question! The registerDtdServices method uses a CompletableFuture with thenAccept, which means the DTD service registration happens asynchronously. This design ensures that the main thread isn't blocked while waiting for the DTD service to become ready.Therefore, if the DTD service takes a long time to respond or fails to initialize, it will not prevent the setUpThemeChangeNotifications method from being called and setting up listening for theme changes. The theme change notifications are initialized independently of the DTD service registration's completion.

@gemini-code-assist
Copy link

I have already provided a summary of the pull request in my previous comment. Please let me know if you have any specific questions or if there's anything else I can assist you with regarding this pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants