locked
VS2010 MPF: LibraryManager Lazy Update RRS feed

  • Question

  • Good morning,

    I've had class library functionality working for some time in my language service.  However, I am currently using code similar to the IronPython example, which is shown below:

             protected void FileParsed(LibraryTask task, IScopeNode scope) {
                LibraryNode module = new LibraryNode(
                    System.IO.Path.GetFileName(task.FileName),
                    LibraryNode.LibraryNodeType.PhysicalContainer
                );
    
                // TODO: Creating the module tree should be done lazily as needed
                // Currently we replace the entire tree and rely upon the libraries
                // update count to invalidate the whole thing.  We could do this
                // finer grained and only update the changed nodes.  But then we
                // need to make sure we're not mutating lists which are handed out.
                CreateModuleTree(module, module, scope, "", task.ModuleID);
    
                if (null != task.ModuleID) {
                    LibraryNode previousItem = null;
                    lock (_files) {
                        if (_files.TryGetValue(task.ModuleID, out previousItem)) {
                            _files.Remove(task.ModuleID);
                        }
                    }
                    _library.RemoveNode(previousItem);
                }
                _library.AddNode(module);
                if (null != task.ModuleID) {
                    lock (_files) {
                        _files.Add(task.ModuleID, module);
                    }
                }            
             }}

    The problem with this method is that it will clear the Node that changed, and then replace it with the updated Node.  This will cause the class view to collapse (if this node was open).  I would like to implement their TODO, which is to only update the nodes that change.  I've done this by writing a 'MergeNodes' method, which will only update nodes that have been affected.  However, it looks like Visual Studio is caching lists that are open (or not allowing them to be mutated) resulting in the updates only being applied to Nodes that haven't been investigated.  Is there a way to ask Visual Studio to clear the cache and to update what is currently displayed?

    In the end, I would like to achieve similar functionality to what the C# class view is currently capable of, where the class view will maintain state and update even while code is being written.

    Thanks,

    Giawa

    • Edited by Giawa Tuesday, February 21, 2012 9:58 PM
    Tuesday, February 21, 2012 7:03 PM

Answers

  • Hi all,

    Ok, after some head scratching I figured it out this afternoon.  Ed pointed me in the correct direction, but there were still some snags to work out.  Here's how the whole deal works.

    When Visual Studio needs to fill the member view or class view it will call GetList2.  The LibraryNode will normally filter its children based on what has been requested.  The type of information that Visual Studio is requesting is stored in the 'ListType' variable, and is a LibraryNodeType (something like Hierarchy, Namespace, Class, Member, Package, etc).  This is where strange things begin.  Visual Studio will clone the LibraryNode and then remove whichever children do not match the 'ListType'.  This will result in a cloned node for each type of list you are supporting.  In my case, I have two lists (the class view and then the member view).  So I would have what I called the 'original' node (which was correctly being updated upon a file change) and then cloned nodes, which are what actually populate the class view once Visual Studio has called GetList2.

    Visual Studio will now intermittently call UpdateCounter, which returns the update count.  Unfortunately, UpdateCounter is being called on cloned objects, and not the original.  So there's no way for Visual Studio to know that new data exists in the original LibraryNode, and has no way to merge the children of the original node with the cloned nodes.  So, here's what I've done.  Each LibraryNode has an 'Original' property, which points to the original node that gets regular updates via MergeNodes (from the LibraryManager).  It's also important to store the type of filtering being done so that when you go to merge the cloned node with the original node you can apply the same filter.  Make sure you set your LibraryNode FilterType in your GetList2 method (just after you clone your LibraryNode).

    private LibraryNode original; protected LibraryNode Original { get { return original; } set { original = value; } } protected virtual LibraryNode Clone() { LibraryNode clonedNode = new LibraryNode(this); clonedNode.Original = (this.original == null ? this : this.original); return clonedNode; }

            public LibraryNodeType FilterType { get; set; }

    The next step is to modify the UpdateCounter method to ensure that the cloned updateCount and the original updateCount match.  If they do not match, then the cloned LibraryNode must be updated with new filtered data.

            int IVsSimpleObjectList2.UpdateCounter(out uint pCurUpdate)
            {
                if (original != null)
                {
                    if (updateCount != original.updateCount)
                    {
                        updateCount = original.updateCount;
                        this.children.Clear();
                        foreach (var child in original.GetChildren())
                        {
                            if ((child.type & this.FilterType) != 0) this.children.Add(child);
                        }
                    }
                }
    
                pCurUpdate = updateCount;
                return VSConstants.S_OK;
            }

    Now Visual Studio can reload the list, since the updateCount has incremented for the cloned objects (that were effectively being cached).  This is done gracefully without collapsing the class view, which is what I was hoping for.

    Hopefully this helps somebody else out,

    Giawa

    • Marked as answer by Giawa Wednesday, February 29, 2012 10:06 PM
    • Edited by Giawa Wednesday, February 29, 2012 10:26 PM
    Wednesday, February 29, 2012 10:06 PM

All replies

  • Hi Giawa,

    Thank you for your question.

    I am trying to involve someone familiar with this topic to further look at this issue. There might be some time delay. Appreciate your patience.

    Thank you for your understanding and support.


    Lucy Liu [MSFT]
    MSDN Community Support | Feedback to us

    Friday, February 24, 2012 8:37 AM
  • Hi Lucy,

    Thanks for getting back to me.  I look forward to a resolution.

    Giawa

    Saturday, February 25, 2012 12:45 AM
  • Hi Giawa,

    It's been a while since I've played in this space, but I think this is controlled in part by your implementation of IVsObjectList.UpdateCounter. The C# integration doesn't leverage those MPF classes. The C# language service, it's project system, and classview support was implemented in C++ years ago, and the constructs they use under the hood, don't really map to the MPF sources, so its not really easy to guess at a glance.

    But you may want to take a closer look at your UpdateCounter method, and see if/how that's being factored into your classview nodes not being updated.

    This ask falls outside of the bounds of forum support for us. But if you hit a dead end, I would encourage you to open a paid support incident via your MSDN or phone.

    Sincerely,


    Ed Dore

    • Proposed as answer by Ed DoreMicrosoft employee Tuesday, February 28, 2012 10:40 PM
    • Marked as answer by Giawa Wednesday, February 29, 2012 5:50 PM
    • Unmarked as answer by Giawa Wednesday, February 29, 2012 10:08 PM
    Tuesday, February 28, 2012 10:40 PM
  • Hi Ed,

    Thanks for getting back to me.  Fair enough, I realize some of my questions are a bit beyond the scope of what is normally discussed in the forum here.  I'll bring that code back up to take another look.  I put it on the backburner while I was awaiting a reply.  I'll post my results here if I am able to get that particular code working.

    It does look like the update counter is being updated appropriately, but it looks like Visual Studio has troubles with updating lists that are currently open (or have been recently opened).  So I'll try to look into that.  In the meantime, I've marked your response as the answer.

    Thanks,

    Giawa

    Wednesday, February 29, 2012 5:49 PM
  • Hi all,

    Ok, after some head scratching I figured it out this afternoon.  Ed pointed me in the correct direction, but there were still some snags to work out.  Here's how the whole deal works.

    When Visual Studio needs to fill the member view or class view it will call GetList2.  The LibraryNode will normally filter its children based on what has been requested.  The type of information that Visual Studio is requesting is stored in the 'ListType' variable, and is a LibraryNodeType (something like Hierarchy, Namespace, Class, Member, Package, etc).  This is where strange things begin.  Visual Studio will clone the LibraryNode and then remove whichever children do not match the 'ListType'.  This will result in a cloned node for each type of list you are supporting.  In my case, I have two lists (the class view and then the member view).  So I would have what I called the 'original' node (which was correctly being updated upon a file change) and then cloned nodes, which are what actually populate the class view once Visual Studio has called GetList2.

    Visual Studio will now intermittently call UpdateCounter, which returns the update count.  Unfortunately, UpdateCounter is being called on cloned objects, and not the original.  So there's no way for Visual Studio to know that new data exists in the original LibraryNode, and has no way to merge the children of the original node with the cloned nodes.  So, here's what I've done.  Each LibraryNode has an 'Original' property, which points to the original node that gets regular updates via MergeNodes (from the LibraryManager).  It's also important to store the type of filtering being done so that when you go to merge the cloned node with the original node you can apply the same filter.  Make sure you set your LibraryNode FilterType in your GetList2 method (just after you clone your LibraryNode).

    private LibraryNode original; protected LibraryNode Original { get { return original; } set { original = value; } } protected virtual LibraryNode Clone() { LibraryNode clonedNode = new LibraryNode(this); clonedNode.Original = (this.original == null ? this : this.original); return clonedNode; }

            public LibraryNodeType FilterType { get; set; }

    The next step is to modify the UpdateCounter method to ensure that the cloned updateCount and the original updateCount match.  If they do not match, then the cloned LibraryNode must be updated with new filtered data.

            int IVsSimpleObjectList2.UpdateCounter(out uint pCurUpdate)
            {
                if (original != null)
                {
                    if (updateCount != original.updateCount)
                    {
                        updateCount = original.updateCount;
                        this.children.Clear();
                        foreach (var child in original.GetChildren())
                        {
                            if ((child.type & this.FilterType) != 0) this.children.Add(child);
                        }
                    }
                }
    
                pCurUpdate = updateCount;
                return VSConstants.S_OK;
            }

    Now Visual Studio can reload the list, since the updateCount has incremented for the cloned objects (that were effectively being cached).  This is done gracefully without collapsing the class view, which is what I was hoping for.

    Hopefully this helps somebody else out,

    Giawa

    • Marked as answer by Giawa Wednesday, February 29, 2012 10:06 PM
    • Edited by Giawa Wednesday, February 29, 2012 10:26 PM
    Wednesday, February 29, 2012 10:06 PM
  • Wow! Thanks for posting the resolution. I'm pretty certain it would have taken me weeks (if not a months) to come up with something like that.

    Giawa, if you think this is something that can worked into the underlying MPF source, please feel free to submit a new issue or discussion item on the codeplex site : http://mpfproj10.codeplex.com/.

    Thanks,


    Ed Dore

    Friday, March 2, 2012 6:14 PM