Ejemplo n.º 1
0
        /// <summary>
        /// Gets the <see cref="IVisualStudioHostDocument"/> for the file at the given filePath.
        /// If we are on the foreground thread and this document is already open in the editor,
        /// then we also attempt to associate the text buffer with it.
        /// Otherwise, if we are on a background thread, then this text buffer association will happen on a scheduled task
        /// whenever <see cref="NotifyDocumentRegisteredToProjectAndStartToRaiseEvents"/> is invoked for the returned document.
        /// </summary>
        public IVisualStudioHostDocument TryGetDocumentForFile(
            IVisualStudioHostProject hostProject,
            string filePath,
            SourceCodeKind sourceCodeKind,
            Func <ITextBuffer, bool> canUseTextBuffer,
            Func <uint, IReadOnlyList <string> > getFolderNames,
            EventHandler updatedOnDiskHandler  = null,
            EventHandler <bool> openedHandler  = null,
            EventHandler <bool> closingHandler = null)
        {
            var documentKey = new DocumentKey(hostProject, filePath);
            StandardTextDocument document;

            lock (_gate)
            {
                if (_documentMap.TryGetValue(documentKey, out document))
                {
                    return(document);
                }
            }

            ITextBuffer openTextBuffer = null;
            uint        foundCookie    = VSConstants.VSCOOKIE_NIL;

            if (IsForeground())
            {
                // If we are on the foreground thread and this document is already open in the editor we want to associate the text buffer with it.
                // Otherwise, we are on a background thread, and this text buffer association will happen on a scheduled task
                // whenever NotifyDocumentRegisteredToProjectAndStartToRaiseEvents is invoked for the returned document.
                // However, determining if a document is already open is a bit complicated. With the introduction
                // of the lazy tabs feature in Dev12, a document may be open (i.e. it has a tab in the shell) but not
                // actually initialized (no data has been loaded for it because its contents have not actually been
                // viewed or used). We only care about documents that are open AND initialized.
                // That means we can't call IVsRunningDocumentTable::FindAndLockDocument to find the document; if the
                // document is open but not initialized, the call will force initialization. This is bad for two
                // reasons:
                //   1.) It circumvents lazy tabs for any document that is part of a VB or C# project.
                //   2.) Initialization may cause a whole host of other code to run synchronously, such as taggers.
                // Instead, we check if the document is already initialized, and avoid asking for the doc data and
                // hierarchy if it is not.
                if (_runningDocumentTable.TryGetCookieForInitializedDocument(documentKey.Moniker, out foundCookie))
                {
                    object foundDocData = _runningDocumentTable.GetDocumentData(foundCookie);
                    openTextBuffer = TryGetTextBufferFromDocData(foundDocData);
                    if (openTextBuffer == null)
                    {
                        // We're open but not open as a normal text buffer. This can happen if the
                        // project system (say in ASP.NET cases) is telling us to add a file which
                        // actually isn't a normal text file at all.
                        return(null);
                    }

                    if (!canUseTextBuffer(openTextBuffer))
                    {
                        return(null);
                    }
                }
            }

            lock (_gate)
            {
                // If this is being added through a public call to Workspace.AddDocument (say, ApplyChanges) then we might
                // already have a document ID that we should be using here.
                _documentIdHints.TryGetValue(filePath, out var id);

                document = new StandardTextDocument(
                    this,
                    hostProject,
                    documentKey,
                    getFolderNames,
                    sourceCodeKind,
                    _textUndoHistoryRegistry,
                    _fileChangeService,
                    openTextBuffer,
                    id,
                    updatedOnDiskHandler,
                    openedHandler,
                    closingHandler);

                // Add this to our document map
                _documentMap.Add(documentKey, document);

                if (openTextBuffer != null)
                {
                    AddCookieOpenDocumentPair_NoLock(foundCookie, documentKey);
                }
            }

            return(document);
        }
Ejemplo n.º 2
0
        private void TryProcessOpenForDocCookie_NoLock(uint docCookie)
        {
            string moniker = _runningDocumentTable.GetDocumentMoniker(docCookie);

            _runningDocumentTable.GetDocumentHierarchyItem(docCookie, out var hierarchy, out var itemid);

            var shimTextBuffer = _runningDocumentTable.GetDocumentData(docCookie) as IVsTextBuffer;

            if (shimTextBuffer != null)
            {
                var hasAssociatedRoslynDocument = false;
                foreach (var project in _projectContainer.GetProjects())
                {
                    var documentKey = new DocumentKey(project, moniker);

                    if (_documentMap.ContainsKey(documentKey))
                    {
                        hasAssociatedRoslynDocument = true;
                        var textBuffer = _editorAdaptersFactoryService.GetDocumentBuffer(shimTextBuffer);

                        // If we already have an ITextBuffer for this document, then we can open it now.
                        // Otherwise, setup an event handler that will do it when the buffer loads.
                        if (textBuffer != null)
                        {
                            // We might already have this docCookie marked as open an older document. This can happen
                            // if we're in the middle of a rename but this class hasn't gotten the notification yet but
                            // another listener for RDT events got it
                            if (_docCookiesToOpenDocumentKeys.ContainsKey(docCookie))
                            {
                                CloseDocuments_NoLock(docCookie, monikerToKeep: moniker);
                            }

                            if (hierarchy == project.Hierarchy)
                            {
                                // This is the current context
                                NewBufferOpened_NoLock(docCookie, textBuffer, documentKey, isCurrentContext: true);
                            }
                            else
                            {
                                // This is a non-current linked context
                                NewBufferOpened_NoLock(docCookie, textBuffer, documentKey, isCurrentContext: false);
                            }
                        }
                        else
                        {
                            TextBufferDataEventsSink.HookupHandler(shimTextBuffer, onDocumentLoadCompleted: () => OnDocumentLoadCompleted(shimTextBuffer, documentKey, moniker));
                        }
                    }
                }

                if (!hasAssociatedRoslynDocument && this._documentTrackingServiceOpt != null && !_docCookiesToNonRoslynDocumentBuffers.ContainsKey(docCookie))
                {
                    // Non-Roslyn document opened.
                    var textBuffer = _editorAdaptersFactoryService.GetDocumentBuffer(shimTextBuffer);
                    if (textBuffer != null)
                    {
                        OnNonRoslynBufferOpened_NoLock(textBuffer, docCookie);
                    }
                    else
                    {
                        TextBufferDataEventsSink.HookupHandler(shimTextBuffer, onDocumentLoadCompleted: () => OnDocumentLoadCompleted(shimTextBuffer, documentKeyOpt: null, moniker: moniker));
                    }
                }
            }
            else
            {
                // This is opening some other designer or property page. If it's tied to our IVsHierarchy, we should
                // let the workspace know
                foreach (var project in _projectContainer.GetProjects())
                {
                    if (hierarchy == project.Hierarchy)
                    {
                        _projectContainer.NotifyNonDocumentOpenedForProject(project);
                    }
                }
            }
        }
Ejemplo n.º 3
0
            /// <summary>
            /// Creates a <see cref="StandardTextDocument"/>.
            /// <para>Note: getFolderNames maps from a VSITEMID to the folders this document should be contained in.</para>
            /// </summary>
            public StandardTextDocument(
                DocumentProvider documentProvider,
                IVisualStudioHostProject project,
                DocumentKey documentKey,
                Func <uint, IReadOnlyList <string> > getFolderNames,
                SourceCodeKind sourceCodeKind,
                ITextUndoHistoryRegistry textUndoHistoryRegistry,
                IVsFileChangeEx fileChangeService,
                ITextBuffer openTextBuffer,
                DocumentId id,
                EventHandler updatedOnDiskHandler,
                EventHandler <bool> openedHandler,
                EventHandler <bool> closingHandler)
            {
                Contract.ThrowIfNull(documentProvider);

                this.Project = project;
                this.Id      = id ?? DocumentId.CreateNewId(project.Id, documentKey.Moniker);
                _itemMoniker = documentKey.Moniker;

                var itemid = this.GetItemId();

                this.Folders = itemid == (uint)VSConstants.VSITEMID.Nil
                    ? SpecializedCollections.EmptyReadOnlyList <string>()
                    : getFolderNames(itemid);

                _documentProvider = documentProvider;

                this.Key                          = documentKey;
                this.SourceCodeKind               = sourceCodeKind;
                _textUndoHistoryRegistry          = textUndoHistoryRegistry;
                _fileChangeTracker                = new FileChangeTracker(fileChangeService, this.FilePath);
                _fileChangeTracker.UpdatedOnDisk += OnUpdatedOnDisk;

                _openTextBuffer  = openTextBuffer;
                _snapshotTracker = new ReiteratedVersionSnapshotTracker(openTextBuffer);

                // The project system does not tell us the CodePage specified in the proj file, so
                // we use null to auto-detect.
                _doNotAccessDirectlyLoader = new FileTextLoader(documentKey.Moniker, defaultEncoding: null);

                // If we aren't already open in the editor, then we should create a file change notification
                if (openTextBuffer == null)
                {
                    _fileChangeTracker.StartFileChangeListeningAsync();
                }

                if (updatedOnDiskHandler != null)
                {
                    UpdatedOnDisk += updatedOnDiskHandler;
                }

                if (openedHandler != null)
                {
                    Opened += openedHandler;
                }

                if (closingHandler != null)
                {
                    Closing += closingHandler;
                }
            }
 private TextBufferDataEventsSink(DocumentProvider documentProvider, IVsTextBuffer textBuffer, DocumentKey documentKey)
 {
     _documentProvider = documentProvider;
     _textBuffer       = textBuffer;
     _documentKey      = documentKey;
 }
            /// <summary>
            /// Helper method for creating and hooking up a <c>TextBufferDataEventsSink</c>.
            /// </summary>
            public static void HookupHandler(DocumentProvider documentProvider, IVsTextBuffer textBuffer, DocumentKey documentKey)
            {
                var eventHandler = new TextBufferDataEventsSink(documentProvider, textBuffer, documentKey);

                eventHandler._sink = ComEventSink.Advise <IVsTextBufferDataEvents>(textBuffer, eventHandler);
            }
Ejemplo n.º 6
0
 private bool IsCurrentContext(DocumentKey documentKey)
 {
     AssertIsForeground();
     var document = documentKey.HostProject.GetCurrentDocumentFromPath(documentKey.Moniker);
     return document != null && LinkedFileUtilities.IsCurrentContextHierarchy(document, _runningDocumentTable);
 }