/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs
188 строк
8 KB
Cyrus Najmabadi
Remove more dependencies
28 апр 2025, 22:57
28 апр 2025, 22:57
163e9f6
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Runtime.InteropServices; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Workspaces.ProjectSystem; using Microsoft.VisualStudio.ComponentModelHost; using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Tagging; using Microsoft.VisualStudio.TextManager.Interop; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Venus; internal partial class ContainedLanguage { private readonly IVsEditorAdaptersFactoryService _editorAdaptersFactoryService; private readonly IDiagnosticAnalyzerService _diagnosticAnalyzerService; private readonly Guid _languageServiceGuid; protected readonly Workspace Workspace; protected readonly IComponentModel ComponentModel; public ProjectSystemProject? Project { get; } protected readonly ContainedDocument ContainedDocument; public IVsTextBufferCoordinator BufferCoordinator { get; protected set; } /// <summary> /// The subject (secondary) buffer that contains the C# or VB code. /// </summary> public ITextBuffer SubjectBuffer { get; } /// <summary> /// The underlying buffer that contains C# or VB code. NOTE: This is NOT the "document" buffer /// that is saved to disk. Instead it is the view that the user sees. The normal buffer graph /// in Venus includes 4 buffers: /// <code> /// SurfaceBuffer/Databuffer (projection) /// / | /// Subject Buffer (C#/VB projection) | /// | | /// Inert (generated) C#/VB Buffer Document (aspx) buffer /// </code> /// In normal circumstance, the Subject and Inert C# buffer are identical in content, and the /// Surface and Document are also identical. The Subject Buffer is the one that is part of the /// workspace, that most language operations deal with. The surface buffer is the one that the /// view is created over, and the Document buffer is the one that is saved to disk. /// </summary> public ITextBuffer DataBuffer { get; } // Set when a TextViewFilter is set. We hold onto this to keep our TagSource objects alive even if Venus // disconnects the subject buffer from the view temporarily (which they do frequently). Otherwise, we have to // re-compute all of the tag data when they re-connect it, and this causes issues like classification // flickering. private readonly ITagAggregator<ITag> _bufferTagAggregator; internal ContainedLanguage( IVsTextBufferCoordinator bufferCoordinator, IComponentModel componentModel, Workspace workspace, ProjectId projectId, ProjectSystemProject? project, Guid languageServiceGuid, AbstractFormattingRule? vbHelperFormattingRule = null) { BufferCoordinator = bufferCoordinator; ComponentModel = componentModel; Project = project; _languageServiceGuid = languageServiceGuid; Workspace = workspace; _editorAdaptersFactoryService = componentModel.GetService<IVsEditorAdaptersFactoryService>(); _diagnosticAnalyzerService = workspace.Services.GetRequiredService<IDiagnosticAnalyzerService>(); // Get the ITextBuffer for the secondary buffer Marshal.ThrowExceptionForHR(bufferCoordinator.GetSecondaryBuffer(out var secondaryTextLines)); SubjectBuffer = _editorAdaptersFactoryService.GetDocumentBuffer(secondaryTextLines)!; // Get the ITextBuffer for the primary buffer Marshal.ThrowExceptionForHR(bufferCoordinator.GetPrimaryBuffer(out var primaryTextLines)); DataBuffer = _editorAdaptersFactoryService.GetDataBuffer(primaryTextLines)!; // Create our tagger var bufferTagAggregatorFactory = ComponentModel.GetService<IBufferTagAggregatorFactoryService>(); _bufferTagAggregator = bufferTagAggregatorFactory.CreateTagAggregator<ITag>(SubjectBuffer); var filePath = GetFilePathFromBuffers(); DocumentId documentId; if (this.Project != null) { documentId = this.Project.AddSourceTextContainer( SubjectBuffer.AsTextContainer(), filePath, sourceCodeKind: SourceCodeKind.Regular, folders: default, designTimeOnly: true, documentServiceProvider: new ContainedDocument.DocumentServiceProvider(DataBuffer)); } else { documentId = DocumentId.CreateNewId(projectId, $"{nameof(ContainedDocument)}: {filePath}"); // We must jam a document into an existing workspace, which we'll assume is safe to do with OnDocumentAdded Workspace.OnDocumentAdded(DocumentInfo.Create( documentId, name: filePath, loader: null, filePath: filePath)); Workspace.OnDocumentOpened(documentId, SubjectBuffer.AsTextContainer()); } ContainedDocument = new ContainedDocument( documentId, subjectBuffer: SubjectBuffer, dataBuffer: DataBuffer, BufferCoordinator, Workspace, Project, ComponentModel, vbHelperFormattingRule); // link subject buffer back to the ContainedDocument, so that we can find it given just the buffer: SubjectBuffer.Properties.AddProperty(typeof(IContainedDocument), ContainedDocument); // TODO: Can contained documents be linked or shared? this.DataBuffer.Changed += OnDataBufferChanged; } private void OnDisconnect() { this.DataBuffer.Changed -= OnDataBufferChanged; if (this.Project != null) { this.Project.RemoveSourceTextContainer(SubjectBuffer.AsTextContainer()); } else { // It's possible the host of the workspace might have already removed the entire project if (Workspace.CurrentSolution.ContainsDocument(ContainedDocument.Id)) { Workspace.OnDocumentRemoved(ContainedDocument.Id); } } this.ContainedDocument.Dispose(); _bufferTagAggregator?.Dispose(); } private void OnDataBufferChanged(object sender, TextContentChangedEventArgs e) { // we don't actually care what has changed in primary buffer. we just want to re-analyze secondary buffer // when primary buffer has changed to update diagnostic positions. _diagnosticAnalyzerService.RequestDiagnosticRefresh(); } public string GetFilePathFromBuffers() { var textDocumentFactoryService = ComponentModel.GetService<ITextDocumentFactoryService>(); // Try to get the file path from the secondary buffer if (!textDocumentFactoryService.TryGetTextDocument(SubjectBuffer, out var document)) { // Fallback to the primary buffer textDocumentFactoryService.TryGetTextDocument(DataBuffer, out document); } if (document == null) { FatalError.ReportAndPropagate(new InvalidOperationException("Failed to get an ITextDocument for a contained document")); } return document?.FilePath ?? string.Empty; } }