/// <summary> /// Get the text changes between this document and a prior version of the same document. /// The changes, when applied to the text of the old document, will produce the text of the current document. /// </summary> public async Task <IEnumerable <TextChange> > GetTextChangesAsync(Document oldDocument, CancellationToken cancellationToken = default(CancellationToken)) { try { using (Logger.LogBlock(FeatureId.Document, FunctionId.Document_GetTextChanges, this.Name, cancellationToken)) { if (oldDocument == this) { // no changes return(SpecializedCollections.EmptyEnumerable <TextChange>()); } if (this.Id != oldDocument.Id) { throw new ArgumentException(WorkspacesResources.DocumentVersionIsDifferent); } // first try to see if text already knows its changes IList <TextChange> textChanges = null; SourceText text; SourceText oldText; if (this.TryGetText(out text) && oldDocument.TryGetText(out oldText)) { if (text == oldText) { return(SpecializedCollections.EmptyEnumerable <TextChange>()); } var container = text.Container; if (container != null) { textChanges = text.GetTextChanges(oldText).ToList(); // if changes are significant (not the whole document being replaced) then use these changes if (textChanges.Count > 1 || (textChanges.Count == 1 && textChanges[0].Span != new TextSpan(0, oldText.Length))) { return(textChanges); } } } // get changes by diffing the trees SyntaxTree tree = await this.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); SyntaxTree oldTree = await oldDocument.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); return(tree.GetChanges(oldTree)); } } catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }
/// <summary> /// Get the text changes between this document and a prior version of the same document. /// The changes, when applied to the text of the old document, will produce the text of the current document. /// </summary> public async Task<IEnumerable<TextChange>> GetTextChangesAsync(Document oldDocument, CancellationToken cancellationToken = default(CancellationToken)) { try { using (Logger.LogBlock(FunctionId.Workspace_Document_GetTextChanges, this.Name, cancellationToken)) { if (oldDocument == this) { // no changes return SpecializedCollections.EmptyEnumerable<TextChange>(); } if (this.Id != oldDocument.Id) { throw new ArgumentException(WorkspacesResources.DocumentVersionIsDifferent); } // first try to see if text already knows its changes IList<TextChange> textChanges = null; SourceText text; SourceText oldText; if (this.TryGetText(out text) && oldDocument.TryGetText(out oldText)) { if (text == oldText) { return SpecializedCollections.EmptyEnumerable<TextChange>(); } var container = text.Container; if (container != null) { textChanges = text.GetTextChanges(oldText).ToList(); // if changes are significant (not the whole document being replaced) then use these changes if (textChanges.Count > 1 || (textChanges.Count == 1 && textChanges[0].Span != new TextSpan(0, oldText.Length))) { return textChanges; } } } // get changes by diffing the trees if (this.SupportsSyntaxTree) { var tree = await this.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var oldTree = await oldDocument.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); return tree.GetChanges(oldTree); } text = await this.GetTextAsync(cancellationToken).ConfigureAwait(false); oldText = await oldDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); return text.GetTextChanges(oldText).ToList(); } } catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }
/// <summary> /// Get the text changes between this document and a prior version of the same document. /// The changes, when applied to the text of the old document, will produce the text of the current document. /// </summary> public async Task<IEnumerable<TextChange>> GetTextChangesAsync(Document oldDocument, CancellationToken cancellationToken = default(CancellationToken)) { try { using (Logger.LogBlock(FunctionId.Workspace_Document_GetTextChanges, this.Name, cancellationToken)) { if (oldDocument == this) { // no changes return SpecializedCollections.EmptyEnumerable<TextChange>(); } if (this.Id != oldDocument.Id) { throw new ArgumentException(WorkspacesResources.DocumentVersionIsDifferent); } // first try to see if text already knows its changes IList<TextChange> textChanges = null; SourceText text; SourceText oldText; if (this.TryGetText(out text) && oldDocument.TryGetText(out oldText)) { if (text == oldText) { return SpecializedCollections.EmptyEnumerable<TextChange>(); } var container = text.Container; if (container != null) { textChanges = text.GetTextChanges(oldText).ToList(); // if changes are significant (not the whole document being replaced) then use these changes if (textChanges.Count > 1 || (textChanges.Count == 1 && textChanges[0].Span != new TextSpan(0, oldText.Length))) { return textChanges; } } } // get changes by diffing the trees if (this.SupportsSyntaxTree) { var tree = await this.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var oldTree = await oldDocument.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); return tree.GetChanges(oldTree); } text = await this.GetTextAsync(cancellationToken).ConfigureAwait(false); oldText = await oldDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); return text.GetTextChanges(oldText).ToList(); } } catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } } /// <summary> /// Gets the list of <see cref="DocumentId"/>s that are linked to this /// <see cref="Document" />. <see cref="Document"/>s are considered to be linked if they /// share the same <see cref="TextDocument.FilePath" />. This <see cref="DocumentId"/> is excluded from the /// result. /// </summary> public ImmutableArray<DocumentId> GetLinkedDocumentIds() { var documentIdsWithPath = this.Project.Solution.GetDocumentIdsWithFilePath(this.FilePath); return documentIdsWithPath.Remove(this.Id); }
/// <summary> /// Get the text changes between this document and a prior version of the same document. /// The changes, when applied to the text of the old document, will produce the text of the current document. /// </summary> public async Task<IEnumerable<TextChange>> GetTextChangesAsync(Document oldDocument, CancellationToken cancellationToken = default(CancellationToken)) { try { using (Logger.LogBlock(FeatureId.Document, FunctionId.Document_GetTextChanges, this.Name, cancellationToken)) { if (oldDocument == this) { // no changes return SpecializedCollections.EmptyEnumerable<TextChange>(); } if (this.Id != oldDocument.Id) { throw new ArgumentException(WorkspacesResources.DocumentVersionIsDifferent); } // first try to see if text already knows its changes IList<TextChange> textChanges = null; SourceText text; SourceText oldText; if (this.TryGetText(out text) && oldDocument.TryGetText(out oldText)) { if (text == oldText) { return SpecializedCollections.EmptyEnumerable<TextChange>(); } var container = text.Container; if (container != null) { textChanges = text.GetTextChanges(oldText).ToList(); // if changes are significant (not the whole document being replaced) then use these changes if (textChanges.Count > 1 || (textChanges.Count == 1 && textChanges[0].Span != new TextSpan(0, oldText.Length))) { return textChanges; } } } // get changes by diffing the trees if (this.SupportsSyntaxTree) { var tree = await this.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var oldTree = await oldDocument.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); return tree.GetChanges(oldTree); } text = await this.GetTextAsync(cancellationToken).ConfigureAwait(false); oldText = await oldDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); return text.GetTextChanges(oldText).ToList(); } } catch (Exception e) if (ExceptionHelpers.CrashUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } } /// <summary> /// Creates a branched version of this document that has its semantic model frozen in whatever state it is available at the time, /// assuming a background process is constructing the semantics asynchronously. Repeated calls to this method may return /// documents with increasingly more complete semantics. /// /// Use this method to gain access to potentially incomplete semantics quickly. /// </summary> internal async Task<Document> WithFrozenPartialSemanticsAsync(CancellationToken cancellationToken) { var solution = this.Project.Solution; var workspace = solution.Workspace; // only produce doc with frozen semantics if this document is part of the workspace's // primary branch and there is actual background compilation going on, since w/o // background compilation the semantics won't be moving toward completeness. Also, // ensure that the project that this document is part of actually supports compilations, // as partial semantics don't make sense otherwise. if (solution.BranchId == workspace.PrimaryBranchId && workspace.PartialSemanticsEnabled && this.Project.SupportsCompilation) { var newSolution = await this.Project.Solution.WithFrozenPartialCompilationIncludingSpecificDocumentAsync(this.Id, cancellationToken).ConfigureAwait(false); return newSolution.GetDocument(this.Id); } else { return this; } }