键入时使用 Roslyn 的 CompletionSevice 的最有效方法是什么?

What's the most efficient way to use Roslyn's CompletionSevice when typing?

我正在查看 Roslyn 的 CompletionServiceShouldTriggerCompletion 定义为

public virtual bool ShouldTriggerCompletion(
    SourceText text,
    int caretPosition,
    CompletionTrigger trigger,
    ImmutableHashSet<string> roles = null,
    OptionSet options = null
) { … }

其中 CompletionTrigger 是单个 char Character.

的包装器

这似乎意味着我应该在键入时在每个字符上调用 ShouldTriggerCompletion — 但这意味着我需要在每个字符上更新 SourceText,它分配一个 TextChange 的数组,一个新的 SourceText 和可能的其他东西,这取决于它的内部结构。

我对这个 API 的理解正确吗?打字时最有效的使用方法是什么?


编辑: 澄清一下,我知道我可以事后猜测它并且只调用它让我们说 .。但我的目标是按照预期的方式使用 API,除了已经提供的优化之外,不进行任何优化。

这取决于您要做什么。

如果您确定知道文本的完成触发器,则可以通过在您认为合适的时候自行触发完成来避免调用 ShouldTriggerCompletion。但是过早的优化并不是您自己重新实现它的好理由。 (您将破坏与在 Roslyn 沙箱中运行的其他程序集的兼容性;您可能会错过边缘情况;语言升级时您不会获得升级;等等)


此外,如果您是调用者,您可能不应该直接调用 ShouldTriggerCompletionGetCompletionsAsync 似乎更可能是您想要的。

however that would mean I need to update SourceText on each char, which allocates an array of TextChange, a new SourceText and potentially other things depending on its internal structure

是的,在 Visual Studio 中的每次击键我们都会创建一个新的 SourceText 和一个新的 Document/Project/Solution 快照。我们必须这样做,因为 所有 功能都取决于现有的文档实例。

对于我们来说,这样的操作是便宜的。 Visual Studio 中的文本编辑器组件已经为每次编辑创建了一个便宜的 ITextSnapshot,它通过非常奇特的数据结构(想想字符串片段的二叉树)来实现,所以这是尽可能便宜的。当我们在编辑器中为文件创建 SourceText 时,我们创建了我们的 own derived type of SourceText 并且我们只是将数据请求转发给编辑器 ITextSnapshot API。 SourceText 实际上拥有 ITextSnapshot 成员的子集并非巧合,因为我们正是为这种模式设计的!