jagregory / boolangstudio forked from olsonjeffery/boolangstudio

Boo language integration for Visual Studio 2008

boolangstudio / Source / BooLangService / HierarchyListener.cs
100644 207 lines (165 sloc) 6.627 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
using System;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell.Interop;
 
namespace Boo.BooLangProject
{
    internal class HierarchyListener : IVsHierarchyEvents
    {
        // friendlier named constants
        private const uint NullItem = VSConstants.VSITEMID_NIL;
        private const uint RootItem = VSConstants.VSITEMID_ROOT;
        private const int NextSibling = (int)__VSHPROPID.VSHPROPID_NextSibling;
        private const int FirstChild = (int)__VSHPROPID.VSHPROPID_FirstChild;
        private const int TypeGuid = (int)__VSHPROPID.VSHPROPID_TypeGuid;
        private const int HasEnumerationSideEffects = (int)__VSHPROPID.VSHPROPID_HasEnumerationSideEffects;
        private readonly Guid PhysicalFile = VSConstants.GUID_ItemType_PhysicalFile;
        private const int Ok = VSConstants.S_OK;
 
        private readonly IVsHierarchy hierarchy;
 
        public HierarchyListener(IVsHierarchy hierarchy)
        {
            this.hierarchy = hierarchy;
        }
 
        public void StartListening()
        {
            Scan(RootItem);
 
            RaiseCompleteEvent();
        }
 
        /// <summary>
        /// Scans an existing hierarchy, raising events for each file it encounters.
        /// </summary>
        /// <param name="id">Item to start the scan from.</param>
        private void Scan(uint id)
        {
            uint currentItem = id;
 
            while (currentItem != NullItem)
            {
                RaiseItemAddedEvent(currentItem);
 
                if (CanRecurseInto(currentItem))
                {
                    object child = GetFirstChild(currentItem);
 
                    if (child != null)
                        Scan(GetItemID(child));
                }
 
                currentItem = MoveNext(currentItem);
            }
        }
 
        /// <summary>
        /// Raises the ItemAdded event for the specified item, if it's appropriate to do so.
        /// </summary>
        /// <param name="item">Item to raise the event for.</param>
        private void RaiseItemAddedEvent(uint item)
        {
            string itemName;
 
            if (ItemAdded == null || !IsBooFile(item, out itemName)) return;
 
            ItemAdded(hierarchy, new HierarchyEventArgs(item, itemName));
        }
 
        private void RaiseCompleteEvent()
        {
            if (Complete == null) return;
 
            Complete(hierarchy, EventArgs.Empty);
        }
 
        /// <summary>
        /// Determines whether an item's children can be recursed into.
        /// </summary>
        /// <param name="item">Item whose children are to be recursed.</param>
        /// <returns></returns>
        private bool CanRecurseInto(uint item)
        {
            object propertyValue;
            int result = hierarchy.GetProperty(item, HasEnumerationSideEffects, out propertyValue);
 
            if (result == Ok && propertyValue is bool)
                return !(bool)propertyValue;
            
            return true;
        }
 
        /// <summary>
        /// Gets the first child of an item.
        /// </summary>
        /// <param name="item">Item to get the first child of.</param>
        /// <returns></returns>
        private object GetFirstChild(uint item)
        {
            object child;
            int result = hierarchy.GetProperty(item, FirstChild, out child);
 
            return result == Ok ? child : null;
        }
 
        /// <summary>
        /// Moves to the next sibling in the tree.
        /// </summary>
        /// <param name="currentItem">Current item.</param>
        /// <returns>Next item.</returns>
        private uint MoveNext(uint currentItem)
        {
            object sibling;
            int result = hierarchy.GetProperty(currentItem, NextSibling, out sibling);
 
            return result != Ok ? NullItem : GetItemID(sibling);
        }
 
        /// <summary>
        /// Checks if a file is a valid Boo document.
        /// </summary>
        /// <param name="item">Item to check.</param>
        /// <param name="canonicalName">Name of the file.</param>
        /// <returns>If a file is a valid Boo document.</returns>
        private bool IsBooFile(uint item, out string canonicalName)
        {
            canonicalName = null;
 
            if (!IsFile(item)) return false;
 
            int result = hierarchy.GetCanonicalName(item, out canonicalName);
 
            return !ErrorHandler.Failed(result) && canonicalName.EndsWith(".boo"); // todo: use a file extension list
        }
 
        /// <summary>
        /// Checks if an item is a file.
        /// </summary>
        /// <param name="item">Item to check</param>
        /// <returns>If item is a file.</returns>
        private bool IsFile(uint item)
        {
            Guid typeGuid;
            int result = hierarchy.GetGuidProperty(item, TypeGuid, out typeGuid);
 
            return !ErrorHandler.Failed(result) && typeGuid == PhysicalFile;
        }
 
        /// <summary>
        /// Gets the item ID of a variant object.
        /// </summary>
        /// <param name="variant">VARIANT holding an itemid.</param>
        /// <returns>Item Id of the concerned node</returns>
        static uint GetItemID(object variant)
        {
            if (variant == null) return NullItem;
 
            if (variant is int) return (uint)(int)variant;
            if (variant is uint) return (uint)variant;
            if (variant is short) return (uint)(short)variant;
            if (variant is ushort) return (ushort)variant;
            if (variant is long) return (uint)(long)variant;
 
            return NullItem;
        }
 
 
        #region IVsHierarchyEvents Members
 
        public int OnItemAdded(uint itemidParent, uint itemidSiblingPrev, uint itemidAdded)
        {
            RaiseItemAddedEvent(itemidAdded);
 
            return Ok;
        }
 
        public int OnItemsAppended(uint itemidParent)
        {
            throw new NotImplementedException();
        }
 
        public int OnItemDeleted(uint itemid)
        {
            throw new NotImplementedException();
        }
 
        public int OnPropertyChanged(uint itemid, int propid, uint flags)
        {
            throw new NotImplementedException();
        }
 
        public int OnInvalidateItems(uint itemidParent)
        {
            throw new NotImplementedException();
        }
 
        public int OnInvalidateIcon(IntPtr hicon)
        {
            throw new NotImplementedException();
        }
 
        #endregion
 
        public event EventHandler<HierarchyEventArgs> ItemAdded;
        public event EventHandler<EventArgs> Complete;
    }
}