为什么修改本地数据时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());
}
}
我正在编写 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());
}
}