| # Code completion | |
| This document describes how code completion is implemented and how to fix bugs | |
| and extend it. | |
| ## Basic operation | |
| Code completion begins with the receipt of either a `completion.getSuggestions2` | |
| request (from the legacy protocol) or a `textDocument/completion` request (from | |
| LSP). When the request is received the appropriate handler is invoked. The | |
| handler will compute a list of completion suggestions and will then translate | |
| the suggestions into the form required by the protocol. | |
| Code completion is supported in `.dart` files as well as in the `pubspec.yaml`, | |
| `analysis_options.yaml`, and `fix_data.yaml` files. | |
| Dart completion suggestions are computed using the `DartCompletionManager` by | |
| invoking the method `computeFinalizedCandidateSuggestions`. | |
| ### Completion passes | |
| The completion manager computes suggestions in two "passes". | |
| - The `InScopeCompletionPass` computes suggestions for every appropriate element | |
| whose name is visible in the name scope surrounding the completion location. | |
| - The `NotImportedCompletionPass` computes suggestions for elements that are not | |
| yet visible in the name scope surrounding the completion location. This pass | |
| is skipped if there isn't time left in the `budget` or if there are already | |
| enough suggestions that the not-yet-imported elements wouldn't be sent anyway. | |
| ### The collector | |
| Both passes add `CandidateSuggestion`s to a `SuggestionCollector`. The collector | |
| keeps a list of candidates and uses an insertion sort to keep the list sorted. | |
| The sorting is done based on how well the suggestion matches the completion | |
| prefix (as computed by the `FuzzyMatcher`). | |
| For performance reasons, there is a limit to the number of suggestions that are | |
| passed back to the client. When there are more candidates in the list than what | |
| will be sent back, then any candidates whose match score is less than the score | |
| of any candidates within the window will be dropped from the list. | |
| ### Ranking | |
| After the list of candidates has been computed, the candidates are ranked by the | |
| `RelevanceComputer`. They are then re-sorted based on the relevance and | |
| truncated to the maximum number of suggestions to be returned. | |
| The relevance score is computed as follows. First, the `FeatureComputer` is used | |
| to measure certain features related to both the completion location and the | |
| suggestion. The features include such things as whether the type of the | |
| suggestion matches the context type, or how far from the completion location a | |
| local variable declaration is. Each feature is represented as a `double` between | |
| `-1.0` and `1.0` inclusive. The feature values are combined using a weighted | |
| average and then adjusted to be between `0` and `1000` inclusive. | |
| The set of features used are selected based on a statistical analysis of | |
| representative Dart code. It is easy to find specific cases where the existing | |
| ranking algorithm does a poor job, but the requirement is that the ranking | |
| algorithm must be optimized across all use cases, not just one use case. This | |
| sometimes results in relevance scores that seem counter-intuitive. When that | |
| happens, one possible path to explore is to add a new feature to the mix. | |
| Changes to either the set of features or the computation of a specific feature | |
| should only be done if a statistical analysis indicates that the change will | |
| produce a better overall ranking (as measured by the MRR of the suggestion | |
| compared to the actual selection). The tool in | |
| `analysis_server/tool/code_completion/completion_metrics.dart` can be used to | |
| compare the quality of one or more experiments compared to the current | |
| implementation. | |
| ## Maintaining and improving | |
| The easiest way to fix bugs or add support for new features is to start by | |
| writing a test in the directory `test/services/completion/dart/location`. These | |
| tests are grouped based on the location in the grammar at which completion is | |
| being requested. | |
| When you have a test that's failing, add a breakpoint to | |
| `InScopeCompletionPass.computeSuggestions`. When the debugger stops at the | |
| breakpoint, hover over `_completionNode` to see what kind of node will be | |
| visited. Add a breakpoint in the corresponding visit method and resume | |
| execution. (If the visit method doesn't already exist, then add it and restart | |
| the debugger). | |
| ## New language features | |
| If a new language feature is being introduced that adds new syntax, then code | |
| completion support will need to be updated. | |
| If the changes are limited to updating an existing node then you should be able | |
| to use the method above to update the corresponding visit method. If the changes | |
| required the addition of some new subclasses of `AstNode`, then you'll likely | |
| need to add a new visit method for the added nodes. | |
| If the changes introduce a new kind of element then you might need to add a new | |
| subclass of `CandidateSuggestion` and update the `DeclarationHelper` to produce | |
| the suggestion under the appropriate conditions. | |