为什么修改本地数据时yield return不起作用?

Why yield return does not work when modifying local data?

我正在编写 Visual Studio 扩展,目前正在向编辑器添加边距字形。我从 MS 文档中的演练示例开始:https://docs.microsoft.com/en-us/visualstudio/extensibility/walkthrough-creating-a-margin-glyph?view=vs-2017.

我的逻辑与示例有点不同,因为我没有分析 spans 参数给出的文件内容:我之前已经从分析过程中得到了一组包含缺陷位置的结果。其基本原理是当且仅当尚未呈现标签时才生成标签。

的确,如果用户在现有标志之前的行中添加新行,我想避免错误地呈现新字形。示例:如果第 42 行有一个字形,光标位于第 41 行,并且用户键入了一个新行,则在第 42 行生成一个新的字形(因为再次调用了 GetTags 方法),同时将先前的字形移动到行43.

我的代码是:

internal class MyDefectTagger : ITagger<MyDefectTag>
{
    private IClassifier m_classifier;
    private ITextBuffer m_buffer;

    internal MyDefectTagger(IClassifier classifier, ITextBuffer buffer)
    {
        m_classifier = classifier;
        m_buffer = buffer;
    }

    IEnumerable<ITagSpan<MyDefectTag>>
        ITagger<MyDefectTag>.GetTags(NormalizedSnapshotSpanCollection spans)
    {
        var filename = GetFileName(m_buffer);

        if (MyModel.Instance == null || 
            MyModel.Instance.defectsLocation == null || 
            !MyModel.Instance.defectsLocation.ContainsKey(filename))
        {
            yield break;
        }

        foreach (SnapshotSpan span in spans)
        {
            ITextSnapshot textSnapshot = span.Snapshot;

            foreach (ITextSnapshotLine textSnapshotLine in textSnapshot.Lines)
            {
                var line = textSnapshotLine.LineNumber + 1; // Lines start at 1 in VS Editor

                if (MyModel.Instance.defectsLocation[filename].ContainsKey(line) &&
                    !MyModel.Instance.defectsLocation[filename][line].rendered)
                {
                    MyModel.Instance.defectsLocation[filename][line].rendered = true; // YIELD WORKS IF THIS LINE IS COMMENTED OUT
                    yield return new TagSpan<MyDefectTag>(new SnapshotSpan(textSnapshotLine.Start, 0), new MyDefectTag());
                }
            }
        }
    }
}

        public event EventHandler<SnapshotSpanEventArgs> TagsChanged;

        private string GetFileName(ITextBuffer buffer)
        {
            buffer.Properties.TryGetProperty(typeof(ITextDocument), out ITextDocument document);

            return document?.FilePath;
        }
    }

奇怪的是,当 "rendered = true" 行被注释掉时,通常会产生 returns(尽管我得到了错误的新字形)。当 rendered = true 行为 运行 时,yield 似乎被阻塞(而调试器仍在执行 yield 指令)。

有什么我遗漏的吗?使用yieldreturn时是否禁止修改本地数据?是否存在一些隐藏的并发问题?感谢您的帮助!

我终于发现我没有正确扫描 NormalizedSnapshotSpanCollection 跨度给出的行。我错误地 re-iterating 遍历了每个跨度的所有行 foreach (ITextSnapshotLine textSnapshotLine in textSnapshot.Lines)。事实上,我需要获取与给定跨度关联的行号,现在它可以工作了:

  • 我在每一行上都得到了适当的边距字形我的分析给我一个缺陷。
  • 在具有边距字形的行之前添加新行时,我没有再得到任何重复项。

因此我的解决方案是:

        foreach (SnapshotSpan span in spans)
        {
            var lineNumber= span.Snapshot.GetLineNumberFromPosition(span.Start.Position) + 1;

            if (MyModel.Instance.defectsLocation[filename].ContainsKey(lineNumber) &&
                !MyModel.Instance.defectsLocation[filename][lineNumber].rendered)
            {
                //MyModel.Instance.defectsLocation[filename][lineNumber].rendered = true;//Strangely no getting any MyDefectTag displayed when I uncomment this line...
                yield return new TagSpan<MyDefectTag>(new SnapshotSpan(span.Start, 0), new MyDefectTag());
            }
        }