提交 8ee61d63 编写于 作者: C CyrusNajmabadi

Provide a synchronous path for outlining so it won't block hte UI thread...

Provide a synchronous path for outlining so it won't block hte UI thread waiting for a threadpool thread.
上级 71c56836
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
namespace Microsoft.CodeAnalysis.Editor.Implementation.Outlining namespace Microsoft.CodeAnalysis.Editor.Implementation.Outlining
{ {
internal abstract class AbstractOutliningService : IOutliningService internal abstract class AbstractOutliningService : ISynchronousOutliningService
{ {
private readonly ImmutableDictionary<Type, ImmutableArray<AbstractSyntaxOutliner>> _nodeOutlinerMap; private readonly ImmutableDictionary<Type, ImmutableArray<AbstractSyntaxOutliner>> _nodeOutlinerMap;
private readonly ImmutableDictionary<int, ImmutableArray<AbstractSyntaxOutliner>> _triviaOutlinerMap; private readonly ImmutableDictionary<int, ImmutableArray<AbstractSyntaxOutliner>> _triviaOutlinerMap;
...@@ -23,16 +23,40 @@ internal abstract class AbstractOutliningService : IOutliningService ...@@ -23,16 +23,40 @@ internal abstract class AbstractOutliningService : IOutliningService
_triviaOutlinerMap = defaultTriviaOutlinerMap; _triviaOutlinerMap = defaultTriviaOutlinerMap;
} }
public async Task<IList<OutliningSpan>> GetOutliningSpansAsync(Document document, CancellationToken cancellationToken) /// <summary>
/// Keep in sync with <see cref="GetOutliningSpansAsync"/>
/// </summary>
public IList<OutliningSpan> GetOutliningSpans(
Document document, CancellationToken cancellationToken)
{ {
try try
{ {
var syntaxDocument = await SyntacticDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false); var syntaxRoot = document.GetSyntaxRootSynchronously(cancellationToken);
// change this to shared pool once RI // change this to shared pool once RI
var regions = new List<OutliningSpan>(); var regions = new List<OutliningSpan>();
RegionCollector.CollectOutliningSpans(syntaxDocument, _nodeOutlinerMap, _triviaOutlinerMap, regions, cancellationToken); RegionCollector.CollectOutliningSpans(document, syntaxRoot, _nodeOutlinerMap, _triviaOutlinerMap, regions, cancellationToken);
return regions;
}
catch (Exception e) when (FatalError.ReportUnlessCanceled(e))
{
throw ExceptionUtilities.Unreachable;
}
}
/// <summary>
/// Keep in sync with <see cref="GetOutliningSpans"/>
/// </summary>
public async Task<IList<OutliningSpan>> GetOutliningSpansAsync(
Document document, CancellationToken cancellationToken)
{
try
{
var syntaxRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
// change this to shared pool once RI
var regions = new List<OutliningSpan>();
RegionCollector.CollectOutliningSpans(document, syntaxRoot, _nodeOutlinerMap, _triviaOutlinerMap, regions, cancellationToken);
return regions; return regions;
} }
catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) catch (Exception e) when (FatalError.ReportUnlessCanceled(e))
......
...@@ -11,4 +11,9 @@ internal interface IOutliningService : ILanguageService ...@@ -11,4 +11,9 @@ internal interface IOutliningService : ILanguageService
{ {
Task<IList<OutliningSpan>> GetOutliningSpansAsync(Document document, CancellationToken cancellationToken); Task<IList<OutliningSpan>> GetOutliningSpansAsync(Document document, CancellationToken cancellationToken);
} }
internal interface ISynchronousOutliningService : IOutliningService
{
IList<OutliningSpan> GetOutliningSpans(Document document, CancellationToken cancellationToken);
}
} }
...@@ -92,49 +92,51 @@ protected override ITaggerEventSource CreateEventSource(ITextView textViewOpt, I ...@@ -92,49 +92,51 @@ protected override ITaggerEventSource CreateEventSource(ITextView textViewOpt, I
TaggerEventSources.OnWorkspaceRegistrationChanged(subjectBuffer, TaggerDelay.OnIdle)); TaggerEventSources.OnWorkspaceRegistrationChanged(subjectBuffer, TaggerDelay.OnIdle));
} }
protected override async Task ProduceTagsAsync(TaggerContext<IOutliningRegionTag> context, DocumentSnapshotSpan documentSnapshotSpan, int? caretPosition) /// <summary>
/// Keep this in sync with <see cref="ProduceTagsSynchronously"/>
/// </summary>
protected override async Task ProduceTagsAsync(
TaggerContext<IOutliningRegionTag> context, DocumentSnapshotSpan documentSnapshotSpan, int? caretPosition)
{ {
try try
{ {
var cancellationToken = context.CancellationToken; var outliningService = TryGetOutliningService(context, documentSnapshotSpan);
using (Logger.LogBlock(FunctionId.Tagger_Outlining_TagProducer_ProduceTags, cancellationToken)) if (outliningService != null)
{
var regions = await outliningService.GetOutliningSpansAsync(
documentSnapshotSpan.Document, context.CancellationToken).ConfigureAwait(false);
ProcessOutliningSpans(context, documentSnapshotSpan.SnapshotSpan, outliningService, regions);
}
}
catch (Exception e) when (FatalError.ReportUnlessCanceled(e))
{
throw ExceptionUtilities.Unreachable;
}
}
/// <summary>
/// Keep this in sync with <see cref="ProduceTagsAsync"/>
/// </summary>
protected override void ProduceTagsSynchronously(
TaggerContext<IOutliningRegionTag> context, DocumentSnapshotSpan documentSnapshotSpan, int? caretPosition)
{
try
{
var outliningService = TryGetOutliningService(context, documentSnapshotSpan);
if (outliningService != null)
{ {
var document = documentSnapshotSpan.Document; var document = documentSnapshotSpan.Document;
var snapshotSpan = documentSnapshotSpan.SnapshotSpan; var cancellationToken = context.CancellationToken;
var snapshot = snapshotSpan.Snapshot;
if (document != null) // Try to call through the synchronous service if possible. Otherwise, fallback
{ // and make a blocking call against the async service.
var outliningService = document.Project.LanguageServices.GetService<IOutliningService>(); var synchronousOutliningService = outliningService as ISynchronousOutliningService;
if (outliningService != null)
{ var regions = synchronousOutliningService != null
var regions = await outliningService.GetOutliningSpansAsync(document, cancellationToken).ConfigureAwait(false); ? synchronousOutliningService.GetOutliningSpans(document, cancellationToken)
if (regions != null) : outliningService.GetOutliningSpansAsync(document, cancellationToken).WaitAndGetResult(cancellationToken);
{
regions = GetMultiLineRegions(outliningService, regions, snapshotSpan.Snapshot); ProcessOutliningSpans(context, documentSnapshotSpan.SnapshotSpan, outliningService, regions);
// Create the outlining tags.
var tagSpans =
from region in regions
let spanToCollapse = new SnapshotSpan(snapshot, region.TextSpan.ToSpan())
let hintSpan = new SnapshotSpan(snapshot, region.HintSpan.ToSpan())
let tag = new Tag(snapshot.TextBuffer,
region.BannerText,
hintSpan,
region.AutoCollapse,
region.IsDefaultCollapsed,
_textEditorFactoryService,
_projectionBufferFactoryService,
_editorOptionsFactoryService)
select new TagSpan<IOutliningRegionTag>(spanToCollapse, tag);
foreach (var tagSpan in tagSpans)
{
context.AddTag(tagSpan);
}
}
}
}
} }
} }
catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) catch (Exception e) when (FatalError.ReportUnlessCanceled(e))
...@@ -143,6 +145,56 @@ protected override async Task ProduceTagsAsync(TaggerContext<IOutliningRegionTag ...@@ -143,6 +145,56 @@ protected override async Task ProduceTagsAsync(TaggerContext<IOutliningRegionTag
} }
} }
private IOutliningService TryGetOutliningService(
TaggerContext<IOutliningRegionTag> context,
DocumentSnapshotSpan documentSnapshotSpan)
{
var cancellationToken = context.CancellationToken;
using (Logger.LogBlock(FunctionId.Tagger_Outlining_TagProducer_ProduceTags, cancellationToken))
{
var document = documentSnapshotSpan.Document;
var snapshotSpan = documentSnapshotSpan.SnapshotSpan;
var snapshot = snapshotSpan.Snapshot;
if (document != null)
{
return document.Project.LanguageServices.GetService<IOutliningService>();
}
}
return null;
}
private void ProcessOutliningSpans(
TaggerContext<IOutliningRegionTag> context, SnapshotSpan snapshotSpan, IOutliningService outliningService, IList<OutliningSpan> regions)
{
if (regions != null)
{
var snapshot = snapshotSpan.Snapshot;
regions = GetMultiLineRegions(outliningService, regions, snapshot);
// Create the outlining tags.
var tagSpans =
from region in regions
let spanToCollapse = new SnapshotSpan(snapshot, region.TextSpan.ToSpan())
let hintSpan = new SnapshotSpan(snapshot, region.HintSpan.ToSpan())
let tag = new Tag(snapshot.TextBuffer,
region.BannerText,
hintSpan,
region.AutoCollapse,
region.IsDefaultCollapsed,
_textEditorFactoryService,
_projectionBufferFactoryService,
_editorOptionsFactoryService)
select new TagSpan<IOutliningRegionTag>(spanToCollapse, tag);
foreach (var tagSpan in tagSpans)
{
context.AddTag(tagSpan);
}
}
}
private static bool s_exceptionReported = false; private static bool s_exceptionReported = false;
private IList<OutliningSpan> GetMultiLineRegions(IOutliningService service, IList<OutliningSpan> regions, ITextSnapshot snapshot) private IList<OutliningSpan> GetMultiLineRegions(IOutliningService service, IList<OutliningSpan> regions, ITextSnapshot snapshot)
......
...@@ -9,14 +9,14 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Outlining ...@@ -9,14 +9,14 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Outlining
{ {
internal class RegionCollector internal class RegionCollector
{ {
private readonly SyntacticDocument _document; private readonly Document _document;
private readonly ImmutableDictionary<Type, ImmutableArray<AbstractSyntaxOutliner>> _nodeOutlinerMap; private readonly ImmutableDictionary<Type, ImmutableArray<AbstractSyntaxOutliner>> _nodeOutlinerMap;
private readonly ImmutableDictionary<int, ImmutableArray<AbstractSyntaxOutliner>> _triviaOutlinerMap; private readonly ImmutableDictionary<int, ImmutableArray<AbstractSyntaxOutliner>> _triviaOutlinerMap;
private readonly List<OutliningSpan> _regions; private readonly List<OutliningSpan> _regions;
private readonly CancellationToken _cancellationToken; private readonly CancellationToken _cancellationToken;
private RegionCollector( private RegionCollector(
SyntacticDocument document, Document document,
ImmutableDictionary<Type, ImmutableArray<AbstractSyntaxOutliner>> nodeOutlinerMap, ImmutableDictionary<Type, ImmutableArray<AbstractSyntaxOutliner>> nodeOutlinerMap,
ImmutableDictionary<int, ImmutableArray<AbstractSyntaxOutliner>> triviaOutlinerMap, ImmutableDictionary<int, ImmutableArray<AbstractSyntaxOutliner>> triviaOutlinerMap,
List<OutliningSpan> spans, List<OutliningSpan> spans,
...@@ -30,14 +30,15 @@ internal class RegionCollector ...@@ -30,14 +30,15 @@ internal class RegionCollector
} }
public static void CollectOutliningSpans( public static void CollectOutliningSpans(
SyntacticDocument document, Document document,
SyntaxNode syntaxRoot,
ImmutableDictionary<Type, ImmutableArray<AbstractSyntaxOutliner>> nodeOutlinerMap, ImmutableDictionary<Type, ImmutableArray<AbstractSyntaxOutliner>> nodeOutlinerMap,
ImmutableDictionary<int, ImmutableArray<AbstractSyntaxOutliner>> triviaOutlinerMap, ImmutableDictionary<int, ImmutableArray<AbstractSyntaxOutliner>> triviaOutlinerMap,
List<OutliningSpan> spans, List<OutliningSpan> spans,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var collector = new RegionCollector(document, nodeOutlinerMap, triviaOutlinerMap, spans, cancellationToken); var collector = new RegionCollector(document, nodeOutlinerMap, triviaOutlinerMap, spans, cancellationToken);
collector.Collect(document.Root); collector.Collect(syntaxRoot);
} }
private void Collect(SyntaxNode root) private void Collect(SyntaxNode root)
...@@ -66,7 +67,7 @@ private void GetOutliningSpans(SyntaxNode node) ...@@ -66,7 +67,7 @@ private void GetOutliningSpans(SyntaxNode node)
{ {
_cancellationToken.ThrowIfCancellationRequested(); _cancellationToken.ThrowIfCancellationRequested();
outliner.CollectOutliningSpans(_document.Document, node, _regions, _cancellationToken); outliner.CollectOutliningSpans(_document, node, _regions, _cancellationToken);
} }
} }
} }
...@@ -90,7 +91,7 @@ private void GetOutliningSpans(SyntaxTriviaList triviaList) ...@@ -90,7 +91,7 @@ private void GetOutliningSpans(SyntaxTriviaList triviaList)
{ {
_cancellationToken.ThrowIfCancellationRequested(); _cancellationToken.ThrowIfCancellationRequested();
outliner.CollectOutliningSpans(_document.Document, trivia, _regions, _cancellationToken); outliner.CollectOutliningSpans(_document, trivia, _regions, _cancellationToken);
} }
} }
} }
......
...@@ -556,13 +556,18 @@ private IEnumerable<ITagSpan<TTag>> GetNonIntersectingTagSpans(IEnumerable<Snaps ...@@ -556,13 +556,18 @@ private IEnumerable<ITagSpan<TTag>> GetNonIntersectingTagSpans(IEnumerable<Snaps
ProcessContext(spansToTag, oldTagTrees, context); ProcessContext(spansToTag, oldTagTrees, context);
} }
private Task ProduceTagsAsync(TaggerContext<TTag> context) private bool ShouldSkipTagProduction()
{ {
var options = _dataSource.Options ?? SpecializedCollections.EmptyEnumerable<Option<bool>>(); var options = _dataSource.Options ?? SpecializedCollections.EmptyEnumerable<Option<bool>>();
var perLanguageOptions = _dataSource.PerLanguageOptions ?? SpecializedCollections.EmptyEnumerable<PerLanguageOption<bool>>(); var perLanguageOptions = _dataSource.PerLanguageOptions ?? SpecializedCollections.EmptyEnumerable<PerLanguageOption<bool>>();
if (options.Any(option => !_subjectBuffer.GetOption(option)) || return options.Any(option => !_subjectBuffer.GetOption(option)) ||
perLanguageOptions.Any(option => !_subjectBuffer.GetOption(option))) perLanguageOptions.Any(option => !_subjectBuffer.GetOption(option));
}
private Task ProduceTagsAsync(TaggerContext<TTag> context)
{
if (ShouldSkipTagProduction())
{ {
// If the feature is disabled, then just produce no tags. // If the feature is disabled, then just produce no tags.
return SpecializedTasks.EmptyTask; return SpecializedTasks.EmptyTask;
...@@ -571,6 +576,16 @@ private Task ProduceTagsAsync(TaggerContext<TTag> context) ...@@ -571,6 +576,16 @@ private Task ProduceTagsAsync(TaggerContext<TTag> context)
return _dataSource.ProduceTagsAsync(context); return _dataSource.ProduceTagsAsync(context);
} }
private void ProduceTagsSynchronously(TaggerContext<TTag> context)
{
if (ShouldSkipTagProduction())
{
return;
}
_dataSource.ProduceTagsSynchronously(context);
}
private void ProcessContext( private void ProcessContext(
List<DocumentSnapshotSpan> spansToTag, List<DocumentSnapshotSpan> spansToTag,
ImmutableDictionary<ITextBuffer, TagSpanIntervalTree<TTag>> oldTagTrees, ImmutableDictionary<ITextBuffer, TagSpanIntervalTree<TTag>> oldTagTrees,
...@@ -724,7 +739,8 @@ public TagSpanIntervalTree<TTag> GetAccurateTagIntervalTreeForBuffer(ITextBuffer ...@@ -724,7 +739,8 @@ public TagSpanIntervalTree<TTag> GetAccurateTagIntervalTreeForBuffer(ITextBuffer
var context = new TaggerContext<TTag>( var context = new TaggerContext<TTag>(
this.State, spansToTag, caretPoint, this.AccumulatedTextChanges, oldTagTrees, cancellationToken); this.State, spansToTag, caretPoint, this.AccumulatedTextChanges, oldTagTrees, cancellationToken);
ProduceTagsAsync(context).Wait(cancellationToken);
ProduceTagsSynchronously(context);
ProcessContext(spansToTag, oldTagTrees, context); ProcessContext(spansToTag, oldTagTrees, context);
} }
......
...@@ -202,13 +202,31 @@ internal Task ProduceTagsAsync_ForTestingPurposesOnly(TaggerContext<TTag> contex ...@@ -202,13 +202,31 @@ internal Task ProduceTagsAsync_ForTestingPurposesOnly(TaggerContext<TTag> contex
/// <summary> /// <summary>
/// Produce tags for the given context. /// Produce tags for the given context.
/// Keep in sync with <see cref="ProduceTagsSynchronously(TaggerContext{TTag})"/>
/// </summary> /// </summary>
protected virtual async Task ProduceTagsAsync(TaggerContext<TTag> context) protected virtual async Task ProduceTagsAsync(TaggerContext<TTag> context)
{ {
foreach (var spanToTag in context.SpansToTag) foreach (var spanToTag in context.SpansToTag)
{ {
context.CancellationToken.ThrowIfCancellationRequested(); context.CancellationToken.ThrowIfCancellationRequested();
await ProduceTagsAsync(context, spanToTag, GetCaretPosition(context.CaretPosition, spanToTag.SnapshotSpan)).ConfigureAwait(false); await ProduceTagsAsync(
context, spanToTag,
GetCaretPosition(context.CaretPosition, spanToTag.SnapshotSpan)).ConfigureAwait(false);
}
}
/// <summary>
/// Produce tags for the given context.
/// Keep in sync with <see cref="ProduceTagsAsync(TaggerContext{TTag})"/>
/// </summary>
protected void ProduceTagsSynchronously(TaggerContext<TTag> context)
{
foreach (var spanToTag in context.SpansToTag)
{
context.CancellationToken.ThrowIfCancellationRequested();
ProduceTagsSynchronously(
context, spanToTag,
GetCaretPosition(context.CaretPosition, spanToTag.SnapshotSpan));
} }
} }
...@@ -223,6 +241,21 @@ protected virtual Task ProduceTagsAsync(TaggerContext<TTag> context, DocumentSna ...@@ -223,6 +241,21 @@ protected virtual Task ProduceTagsAsync(TaggerContext<TTag> context, DocumentSna
return SpecializedTasks.EmptyTask; return SpecializedTasks.EmptyTask;
} }
protected virtual void ProduceTagsSynchronously(TaggerContext<TTag> context, DocumentSnapshotSpan spanToTag, int? caretPosition)
{
// By default we implement the sync version of this by blocking on the async version.
//
// The benefit of this is that all taggers can implicitly be used as IAccurateTaggers
// without any code changes.
//
// However, the drawback is that it means the UI thread might be blocked waiting for
// tasks to be scheduled and run on the threadpool.
//
// Taggers that need to be called accurately should override this method to produce
// results quickly if possible.
ProduceTagsAsync(context, spanToTag, caretPosition).Wait(context.CancellationToken);
}
private struct DiffResult private struct DiffResult
{ {
public NormalizedSnapshotSpanCollection Added { get; } public NormalizedSnapshotSpanCollection Added { get; }
......
...@@ -154,18 +154,8 @@ public bool SupportsSemanticModel ...@@ -154,18 +154,8 @@ public bool SupportsSemanticModel
} }
} }
/// <summary> private Task<SyntaxTree> GetExistingSyntaxTreeTask()
/// Gets the <see cref="SyntaxTree" /> for this document asynchronously.
/// </summary>
public Task<SyntaxTree> GetSyntaxTreeAsync(CancellationToken cancellationToken = default(CancellationToken))
{ {
// If the language doesn't support getting syntax trees for a document, then bail out
// immediately.
if (!this.SupportsSyntaxTree)
{
return SpecializedTasks.Default<SyntaxTree>();
}
if (_syntaxTreeResultTask != null) if (_syntaxTreeResultTask != null)
{ {
return _syntaxTreeResultTask; return _syntaxTreeResultTask;
...@@ -195,11 +185,51 @@ public Task<SyntaxTree> GetSyntaxTreeAsync(CancellationToken cancellationToken = ...@@ -195,11 +185,51 @@ public Task<SyntaxTree> GetSyntaxTreeAsync(CancellationToken cancellationToken =
return _syntaxTreeResultTask; return _syntaxTreeResultTask;
} }
return null;
}
/// <summary>
/// Gets the <see cref="SyntaxTree" /> for this document asynchronously.
/// </summary>
public Task<SyntaxTree> GetSyntaxTreeAsync(CancellationToken cancellationToken = default(CancellationToken))
{
// If the language doesn't support getting syntax trees for a document, then bail out
// immediately.
if (!this.SupportsSyntaxTree)
{
return SpecializedTasks.Default<SyntaxTree>();
}
var syntaxTreeTask = GetExistingSyntaxTreeTask();
if (syntaxTreeTask != null)
{
return syntaxTreeTask;
}
// we can't cache this result, since internally it uses AsyncLazy which // we can't cache this result, since internally it uses AsyncLazy which
// care about cancellation token // care about cancellation token
return _state.GetSyntaxTreeAsync(cancellationToken); return _state.GetSyntaxTreeAsync(cancellationToken);
} }
private SyntaxTree GetSyntaxTree(CancellationToken cancellationToken)
{
if (!this.SupportsSyntaxTree)
{
return null;
}
// if we already have a stask for getting this syntax tree, and the task
// has completed, then we can just return that value.
var syntaxTreeTask = GetExistingSyntaxTreeTask();
if (syntaxTreeTask?.Status == TaskStatus.RanToCompletion)
{
return syntaxTreeTask.Result;
}
// Otherwise defer to our state to get this value.
return _state.GetSyntaxTree(cancellationToken);
}
/// <summary> /// <summary>
/// Gets the root node of the current syntax tree if the syntax tree has already been parsed and the tree is still cached. /// Gets the root node of the current syntax tree if the syntax tree has already been parsed and the tree is still cached.
/// In almost all cases, you should call <see cref="GetSyntaxRootAsync"/> to fetch the root node, which will parse /// In almost all cases, you should call <see cref="GetSyntaxRootAsync"/> to fetch the root node, which will parse
...@@ -226,6 +256,22 @@ public async Task<SyntaxNode> GetSyntaxRootAsync(CancellationToken cancellationT ...@@ -226,6 +256,22 @@ public async Task<SyntaxNode> GetSyntaxRootAsync(CancellationToken cancellationT
return await tree.GetRootAsync(cancellationToken).ConfigureAwait(false); return await tree.GetRootAsync(cancellationToken).ConfigureAwait(false);
} }
/// <summary>
/// Only for features that absolutely must run synchronously (probably because they're
/// on the UI thread). Right now, the only feature this is for is Outlining as VS will
/// block on that feature from the UI thread when a document is opened.
/// </summary>
internal SyntaxNode GetSyntaxRootSynchronously(CancellationToken cancellationToken)
{
if (!this.SupportsSyntaxTree)
{
return null;
}
var tree = this.GetSyntaxTree(cancellationToken);
return tree.GetRoot(cancellationToken);
}
/// <summary> /// <summary>
/// Gets the current semantic model for this document if the model is already computed and still cached. /// Gets the current semantic model for this document if the model is already computed and still cached.
/// In almost all cases, you should call <see cref="GetSemanticModelAsync"/>, which will compute the semantic model /// In almost all cases, you should call <see cref="GetSemanticModelAsync"/>, which will compute the semantic model
......
...@@ -559,6 +559,15 @@ public async Task<SyntaxTree> GetSyntaxTreeAsync(CancellationToken cancellationT ...@@ -559,6 +559,15 @@ public async Task<SyntaxTree> GetSyntaxTreeAsync(CancellationToken cancellationT
return treeAndVersion.Tree; return treeAndVersion.Tree;
} }
internal SyntaxTree GetSyntaxTree(CancellationToken cancellationToken)
{
var treeAndVersion = _treeSource.GetValue(cancellationToken);
// make sure there is an association between this tree and this doc id before handing it out
BindSyntaxTreeToId(treeAndVersion.Tree, this.Id);
return treeAndVersion.Tree;
}
public bool TryGetTopLevelChangeTextVersion(out VersionStamp version) public bool TryGetTopLevelChangeTextVersion(out VersionStamp version)
{ {
TreeAndVersion treeAndVersion; TreeAndVersion treeAndVersion;
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis namespace Microsoft.CodeAnalysis
{ {
...@@ -24,16 +21,14 @@ protected SyntacticDocument(Document document, SourceText text, SyntaxTree tree, ...@@ -24,16 +21,14 @@ protected SyntacticDocument(Document document, SourceText text, SyntaxTree tree,
this.Root = root; this.Root = root;
} }
public Project Project public Project Project => this.Document.Project;
{
get { return this.Document.Project; }
}
public static async Task<SyntacticDocument> CreateAsync(Document document, CancellationToken cancellationToken) public static async Task<SyntacticDocument> CreateAsync(
Document document, CancellationToken cancellationToken)
{ {
var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
return new SyntacticDocument(document, text, root.SyntaxTree, root); return new SyntacticDocument(document, text, root.SyntaxTree, root);
} }
} }
} }
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册