/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/VisualStudio/Xaml/Impl/Implementation/XamlProjectService.cs
277 строк
10 KB
Cyrus Najmabadi
Use implicit object creation expressions
13 сен 2025, 01:48
13 сен 2025, 01:48
4c6a36a
Код
Авторство
О чём код?
// 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.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel.Composition; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Editor.Xaml; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Workspaces.ProjectSystem; using Microsoft.CodeAnalysis.Xaml.Diagnostics.Analyzers; using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.TextManager.Interop; namespace Microsoft.VisualStudio.LanguageServices.Xaml; [Export] internal sealed partial class XamlProjectService : IDisposable { private readonly IServiceProvider _serviceProvider; private readonly Workspace _workspace; private readonly VisualStudioProjectFactory _visualStudioProjectFactory; private readonly IVsEditorAdaptersFactoryService _editorAdaptersFactory; private readonly IThreadingContext _threadingContext; private readonly Dictionary<IVsHierarchy, ProjectSystemProject> _xamlProjects = []; private readonly ConcurrentDictionary<string, DocumentId> _documentIds = new(StringComparer.OrdinalIgnoreCase); private readonly WorkspaceEventRegistration _documentClosedHandlerDisposer; private RunningDocumentTable? _rdt; private IVsSolution? _vsSolution; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public XamlProjectService( [Import(typeof(Shell.SVsServiceProvider))] IServiceProvider serviceProvider, IVsEditorAdaptersFactoryService editorAdaptersFactoryService, VisualStudioProjectFactory visualStudioProjectFactory, VisualStudioWorkspace workspace, IXamlDocumentAnalyzerService analyzerService, IThreadingContext threadingContext) { _serviceProvider = serviceProvider; _editorAdaptersFactory = editorAdaptersFactoryService; _visualStudioProjectFactory = visualStudioProjectFactory; _workspace = workspace; _threadingContext = threadingContext; AnalyzerService = analyzerService; // Require main thread on the callback as OnDocumentClosed is not thread safe. _documentClosedHandlerDisposer = _workspace.RegisterDocumentClosedHandler(OnDocumentClosed, WorkspaceEventOptions.RequiresMainThreadOptions); } public void Dispose() => _documentClosedHandlerDisposer.Dispose(); public static IXamlDocumentAnalyzerService? AnalyzerService { get; private set; } public DocumentId? TrackOpenDocument(string filePath) { if (string.IsNullOrEmpty(filePath)) { // Can't track anything without a path (can happen while diffing) return null; } if (_documentIds.TryGetValue(filePath, out var documentId)) { return documentId; } documentId = GetDocumentId(filePath); if (documentId != null) { _documentIds.TryAdd(filePath, documentId); } return documentId; DocumentId? GetDocumentId(string path) { if (_threadingContext.JoinableTaskContext.IsOnMainThread) { return EnsureDocument(filePath); } else { return _threadingContext.JoinableTaskFactory.Run(async () => { await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(); return EnsureDocument(filePath); }); } } } private DocumentId? EnsureDocument(string filePath) { if (_rdt == null) { _rdt = new RunningDocumentTable(_serviceProvider); _rdt.Advise(this); } if (_vsSolution == null) { _vsSolution = (IVsSolution)_serviceProvider.GetService(typeof(SVsSolution)); _vsSolution.AdviseSolutionEvents(this, out _); } IVsHierarchy? hierarchy = null; uint docCookie = 0; try { _rdt.FindDocument(filePath, out hierarchy, out _, out docCookie); } catch (ArgumentException) { // We only support open documents that are in the RDT already return null; } if (hierarchy == null || docCookie == 0) { return null; } if (!_xamlProjects.TryGetValue(hierarchy, out var project)) { if (!hierarchy.TryGetName(out var name)) { return null; } if (!hierarchy.TryGetGuidProperty(__VSHPROPID.VSHPROPID_ProjectIDGuid, out var projectGuid)) { return null; } var projectInfo = new VisualStudioProjectCreationInfo { Hierarchy = hierarchy, FilePath = hierarchy.TryGetProjectFilePath(), ProjectGuid = projectGuid }; project = _threadingContext.JoinableTaskFactory.Run(() => _visualStudioProjectFactory.CreateAndAddToWorkspaceAsync( name, StringConstants.XamlLanguageName, projectInfo, CancellationToken.None)); _xamlProjects.Add(hierarchy, project); } if (!project.ContainsSourceFile(filePath)) { project.AddSourceFile(filePath); var documentId = _workspace.CurrentSolution.GetDocumentIdsWithFilePath(filePath).Single(d => d.ProjectId == project.Id); _documentIds[filePath] = documentId; // Remove the following when https://github.com/dotnet/roslyn/issues/49879 is fixed var document = _workspace.CurrentSolution.GetRequiredDocument(documentId); var hasText = document.TryGetText(out var text); if (!hasText || text?.Container.TryGetTextBuffer() == null) { var docInfo = _rdt.GetDocumentInfo(docCookie); var textBuffer = TryGetTextBufferFromDocData(docInfo.DocData); var textContainer = textBuffer?.AsTextContainer(); if (textContainer != null) { _workspace.OnDocumentTextChanged(documentId, textContainer.CurrentText, PreservationMode.PreserveIdentity); } } } if (_documentIds.TryGetValue(filePath, out var docId)) { return docId; } return null; } private void OnDocumentClosed(DocumentEventArgs e) { var filePath = e.Document.FilePath; if (filePath == null) return; if (_documentIds.TryGetValue(filePath, out var documentId)) { var document = _workspace.CurrentSolution.GetDocument(documentId); if (document?.FilePath != null) { var project = _xamlProjects.Values.SingleOrDefault(p => p.Id == document.Project.Id); project?.RemoveSourceFile(document.FilePath); } _documentIds.TryRemove(filePath, out _); } } private void OnProjectClosing(IVsHierarchy hierarchy) { if (_xamlProjects.TryGetValue(hierarchy, out var project)) { project.RemoveFromWorkspace(); _xamlProjects.Remove(hierarchy); } } private void OnDocumentMonikerChanged(uint docCookie, IVsHierarchy hierarchy, string oldMoniker, string newMoniker) { // If the moniker change only involves casing differences then the project system will // not remove & add the file again with the new name, so we should not clear any state. // Leaving the old casing in the DocumentKey is safe because DocumentKey equality // checks ignore the casing of the moniker. if (oldMoniker.Equals(newMoniker, StringComparison.OrdinalIgnoreCase)) { return; } // If the moniker change only involves a non-XAML project then ignore it. if (!_xamlProjects.TryGetValue(hierarchy, out var project)) { return; } var info = _rdt?.GetDocumentInfo(docCookie); var buffer = TryGetTextBufferFromDocData(info?.DocData); var isXaml = buffer?.ContentType.IsOfType(ContentTypeNames.XamlContentType) == true; // Managed languages rely on the msbuild host object to add and remove documents during rename. // For XAML we have to do that ourselves. if (project.ContainsSourceFile(oldMoniker)) { project.RemoveSourceFile(oldMoniker); } _documentIds.TryRemove(oldMoniker, out _); if (isXaml) { project.AddSourceFile(newMoniker); var documentId = _workspace.CurrentSolution.GetDocumentIdsWithFilePath(newMoniker).Single(d => d.ProjectId == project.Id); _documentIds[newMoniker] = documentId; } } /// <summary> /// Tries to return an ITextBuffer representing the document from the document's DocData. /// </summary> /// <param name="docData">The DocData from the running document table.</param> /// <returns>The ITextBuffer. If one could not be found, this returns null.</returns> private ITextBuffer? TryGetTextBufferFromDocData(object? docData) { if (docData is IVsTextBuffer vsTestBuffer) { return _editorAdaptersFactory.GetDocumentBuffer(vsTestBuffer); } return null; } }