void ICompletionSource.AugmentCompletionSession(ICompletionSession session, IList <CompletionSet> completionSets)
        {
            ObjectTree objectTree = new ObjectTree(this.language, this.textBuffer.CurrentSnapshot);

            // In the case of virtual spaces, VS sends SnapshotPoint of the last physical
            // location in the line.  Unfortunately, we really need to know the virtual
            // location!  As far as I know, there's no way to actually get this.  Sadly,
            // that means that we might provide the wrong context.
            SnapshotPoint point = session.GetTriggerPoint(this.textBuffer.CurrentSnapshot).Value;
            Position      pos   = point.ToSwixPosition();

            StatementNode currentNode;
            StatementNode parentNode;

            if (!objectTree.TryFindContextNodes(pos, out currentNode, out parentNode))
            {
                return;
            }

            // Get compilation context for value completions (IDs, file names, etc.)
            IEnumerable <PackageItem> contextItems = SourceFileCompiler.Instance.GetCompilationContext(this.textBuffer);

            List <Completion> completions = this.CreateCompletions(objectTree, currentNode, parentNode, null, pos, contextItems);

            ITrackingSpan applicableSpan = this.FindApplicableSpan(session);

            completionSets.Add(new CompletionSet(
                                   "Swix Terms", // the non-localized title of the tab
                                   "Swix Terms", // the display title of the tab
                                   applicableSpan,
                                   completions,
                                   null));
        }
        private void AddObjectCompletions(ObjectTree objectTree, StatementNode parentNode, List <Completion> completions)
        {
            SpewDiagnostics("----- Adding Objects for {0}", parentNode.Statement);

            var parentOit = this.GetTypeFromObjectStatement(parentNode);

            if (parentOit == null)
            {
                return;
            }

            // Add all of the types that are allowable as children of the parent object.
            foreach (var oit in this.typeCache.GetTypes().Where(t => t.Type.IsOrDerivesFrom(parentOit.GetChildrenTypes())))
            {
                var type = oit.Type;

                // Check for [Obsolete] objects...
                object[] attributes = type.GetCustomAttributes(typeof(ObsoleteAttribute), true);

                // Some attributes have a enabled/disabled flag, but [Obsolete] is always set if present.
                if (attributes == null || attributes.Length == 0)
                {
                    var prefix = this.typeCache.GetPrefixForClrNamespace(type.Namespace);

                    if (!string.IsNullOrEmpty(prefix))
                    {
                        prefix = string.Concat(prefix, this.prefixSeparator);
                    }

                    string objectName  = this.FormatName(type.Name);
                    string replacement = string.Concat(prefix, objectName);

                    completions.Add(new Completion(
                                        replacement,
                                        string.Concat(replacement, " "), // added space makes continued completions worthwhile...
                                        string.Format("{0}\nobject", type.Name),
                                        this.GetGlyph(StandardGlyphGroup.GlyphGroupClass, StandardGlyphItem.GlyphItemPublic),
                                        ""));
                }
            }
        }
        private void AddValueCompletions(ObjectTree objectTree, StatementNode currentNode, Token <ParserTokenType> token, IEnumerable <PackageItem> contextItems, List <Completion> completions)
        {
            if (token == null)
            {
                return;
            }

            SpewDiagnostics("----- Adding Values for {0}", currentNode.Statement);

            var oit = this.GetTypeFromObjectStatement(currentNode);

            if (oit == null)
            {
                return;
            }

            // Determine the type of the property or attached property we need...
            // Look at the tokens prior to the context token.
            var tokens = currentNode.Statement.AllTokens;
            var lookup = tokens.Take(tokens.IndexOf(token)).Reverse().ToList();

            var propToken = lookup.FirstOrDefault(t => t.TokenType == ParserTokenType.PropertyName);

            if (propToken == null)
            {
                return;
            }

            string propName           = propToken.Value;
            string attachedObjectName = string.Empty;
            string prefix             = string.Empty;

            var propIndex           = lookup.IndexOf(propToken);
            var attachedObjectToken = lookup.Skip(propIndex).ElementAtOrDefault(2);

            if (attachedObjectToken != null && attachedObjectToken.TokenType == ParserTokenType.AttachedPropertyObject)
            {
                attachedObjectName = attachedObjectToken.Value;

                var prefixToken = lookup.Skip(propIndex).ElementAtOrDefault(4);

                if (prefixToken != null && prefixToken.TokenType == ParserTokenType.NamespacePrefix)
                {
                    prefix = prefixToken.Value;
                }
            }

            Type type = null;

            if (string.IsNullOrEmpty(attachedObjectName))
            {
                // normal property...
                PropertyInstanceType prop;
                if (oit.TryGetProperty(propName, out prop))
                {
                    type = prop.ValueType;
                }
            }
            else
            {
                // attached property...
                string clrNamespace;
                AttachedPropertyObjectType attachedObj;
                AttachedPropertySetterType setter;
                if (this.typeCache.TryGetNamespaceByPrefix(prefix, out clrNamespace) &&
                    this.typeCache.TryGetAttachedPropertyType(clrNamespace, attachedObjectName, out attachedObj) &&
                    attachedObj.TryGetPropertySetter(propName, out setter))
                {
                    type = setter.ValueType;
                }
            }

            if (type == null)
            {
                return;
            }

            if (type == typeof(string))
            {
                // We don't show any completions for strings... we can't!
            }
            else if (type.IsValueType)
            {
                if (type.IsEnum)
                {
                    foreach (string name in type.GetEnumNames())
                    {
                        string valueName = this.FormatName(name);

                        completions.Add(new Completion(
                                            valueName,
                                            valueName,
                                            string.Format("{0}\n{1} enumeration value", name, type.Name),
                                            this.GetGlyph(StandardGlyphGroup.GlyphGroupValueType, StandardGlyphItem.GlyphItemPublic),
                                            ""));
                    }
                }
                else
                {
                    // simple type...
                    if (type == typeof(bool))
                    {
                        completions.Add(new Completion("true", "true", null, this.GetGlyph(StandardGlyphGroup.GlyphGroupValueType, StandardGlyphItem.GlyphItemPublic), ""));
                        completions.Add(new Completion("false", "false", null, this.GetGlyph(StandardGlyphGroup.GlyphGroupValueType, StandardGlyphItem.GlyphItemPublic), ""));
                    }
                    else
                    {
                        // ?? non-class value types / strings??
                        // We have to have more than one placeholder, or else VS will auto-fill for us!
                        string info = string.Format("TODO: Determine values for {0}", type.FullName);
                        completions.Add(new Completion("fake 1", "???", info, this.GetGlyph(StandardGlyphGroup.GlyphGroupValueType, StandardGlyphItem.GlyphItemPublic), ""));
                        completions.Add(new Completion("fake 2", "???", info, this.GetGlyph(StandardGlyphGroup.GlyphGroupValueType, StandardGlyphItem.GlyphItemPublic), ""));
                    }
                }
            }
            else
            {
                // Handle other classes/interfaces...
                // Find all the context items that could fulfill this type...
                List <string> usedValues = new List <string>();
                foreach (PackageItem item in contextItems)
                {
                    var itemType = item.GetType();

                    if (itemType.IsOrDerivesFrom(type))
                    {
                        // By convention, we know that the first entry is the shortest possible reference,
                        // and the last one is the longest/most-explicit.
                        List <string> referenceNames = item.GetReferenceNames().ToList();

                        // While editing, it's possible to end up with incomplete items that have no names
                        // at all.  In that case, just skip them!
                        if (referenceNames.Count == 0)
                        {
                            continue;
                        }

                        string shortest = referenceNames[0];
                        string longest  = referenceNames[referenceNames.Count - 1];

                        string info = string.Format(
                            "Reference to {0} object\nshortest reference: {1}\nlongest reference: {2}",
                            this.FormatName(itemType.Name),
                            shortest,
                            longest);

                        foreach (string referenceName in referenceNames)
                        {
                            if (!usedValues.Contains(referenceName))
                            {
                                usedValues.Add(referenceName);

                                // Add quotes if there are any non-token characters in the value.
                                string insertionText = referenceName;
                                if (!referenceName.All(c => CompletionSource.IsTokenTermCharacter(c) && c != '"'))
                                {
                                    insertionText = string.Concat('"', referenceName, '"');
                                }

                                completions.Add(new Completion(
                                                    referenceName,
                                                    insertionText,
                                                    info,
                                                    this.GetGlyph(StandardGlyphGroup.GlyphGroupValueType, StandardGlyphItem.GlyphItemPublic),
                                                    ""));
                            }
                        }
                    }
                }
            }
        }
        private void AddMemberCompletions(ObjectTree objectTree, StatementNode currentNode, string currentText, List <Completion> completions)
        {
            SpewDiagnostics("----- Adding Members for {0}", currentNode.Statement);

            var oit = this.GetTypeFromObjectStatement(currentNode);

            if (oit == null)
            {
                return;
            }

            // We want to add all the members that are publically writable, and aren't already used
            // on the object *unless* it's the current token context.
            var propsUsed      = currentNode.Statement.Tokens.Where(t => t.TokenType == ParserTokenType.PropertyName);
            var propsToExclude = propsUsed.Select(t => t.Value).Where(s => !string.Equals(s, currentText)).ToList();

            foreach (var prop in oit.Properties)
            {
                // Check for [Obsolete] members...
                object[] attributes = prop.PropertyInfo.GetCustomAttributes(typeof(ObsoleteAttribute), true);

                // Some attributes have a enabled/disabled flag, but [Obsolete] is always set if present.
                if (attributes == null || attributes.Length == 0)
                {
                    string propName = this.FormatName(prop.Name);

                    if (propsToExclude.Contains(propName))
                    {
                        continue;
                    }

                    completions.Add(new Completion(
                                        propName,
                                        string.Concat(propName, "="),
                                        string.Format("{0}.{1}\nattribute", oit.Type.Name, prop.Name),
                                        this.GetGlyph(StandardGlyphGroup.GlyphGroupField, StandardGlyphItem.GlyphItemPublic),
                                        ""));
                }
            }

            // In addition to any properties on the object itself, we also need to list any
            // attached properties that work for this object.
            foreach (var prop in this.typeCache.GetAttachedProperties())
            {
                var prefix = this.typeCache.GetPrefixForClrNamespace(prop.Type.Namespace);

                if (!string.IsNullOrEmpty(prefix))
                {
                    prefix = string.Concat(prefix, this.prefixSeparator);
                }

                var attachedObjectName = this.FormatName(prop.Type.Name);

                foreach (var setter in prop.Setters)
                {
                    // TODO: Check for [Obsolete] members...
                    ////object[] attributes = setter.GetCustomAttributes(typeof(ObsoleteAttribute), true);

                    ////// Some attributes have a enabled/disabled flag, but [Obsolete] is always set if present.
                    ////if (attributes == null || attributes.Length == 0)
                    ////{
                    if (oit.Type.IsOrDerivesFrom(setter.ObjectType))
                    {
                        var propName    = this.FormatName(setter.Name);
                        var replacement = string.Concat(prefix, attachedObjectName, ".", propName);

                        completions.Add(new Completion(
                                            replacement,
                                            string.Concat(replacement, "="),
                                            string.Format("{0}.{1}\nextension attribute\nfrom \"{2}\"", prop.Type.Name, setter.Name, prop.Type.Namespace),
                                            this.GetGlyph(StandardGlyphGroup.GlyphExtensionMethod, StandardGlyphItem.GlyphItemPublic),
                                            ""));
                    }
                    ////}
                }
            }
        }
        private List <Completion> CreateCompletions(ObjectTree objectTree, StatementNode currentNode, StatementNode parentNode, string filterText, Position position, IEnumerable <PackageItem> contextItems)
        {
            List <Completion> completions = new List <Completion>();

            // Figure out what kind of completion we need...
            bool   addObjectCompletions   = false;
            bool   addMemberCompletions   = false;
            bool   addValueCompletions    = false;
            string currentText            = string.Empty;
            Token <ParserTokenType> token = null;

            if (currentNode == null)
            {
                addObjectCompletions = true;
            }
            else
            {
                // Determine the completions based on the node!
                token = currentNode.Statement.AllTokens.FirstOrDefault(t => t.Range.End >= position);

                SpewDiagnostics("----- Finding completions for statement: [{0}]", currentNode.Statement);
                SpewDiagnostics("----- Position: {0}", position);
                SpewDiagnostics("----- Token: {0}", token);

                var tokenType = ParserTokenType.Unknown;

                if (token == null)
                {
                    // Need to find a surrogate token?
                }
                else
                {
                    if (token.Range.End < position)
                    {
                        // we're actually *after* that token, so figure out what that means...
                    }

                    tokenType   = token.TokenType;
                    currentText = token.Value;
                }

                switch (tokenType)
                {
                ////case ParserTokenType.Unknown:
                ////    break;
                case ParserTokenType.Whitespace:
                    // Depends on what the previous/next tokens are...
                    addMemberCompletions = true;
                    break;

                ////case ParserTokenType.Comment:
                ////    break;
                case ParserTokenType.UseKeyword:
                    break;

                case ParserTokenType.Object:
                    addObjectCompletions = true;
                    break;

                case ParserTokenType.PropertyName:
                    addMemberCompletions = true;
                    break;

                case ParserTokenType.Equals:
                    addValueCompletions = true;
                    break;

                case ParserTokenType.PropertyValue:
                    addValueCompletions = true;
                    break;

                case ParserTokenType.DoubleQuote:
                    addValueCompletions = true;
                    break;

                case ParserTokenType.SingleQuote:
                    addValueCompletions = true;
                    break;

                case ParserTokenType.Period:
                    break;

                case ParserTokenType.NamespacePrefix:
                    break;

                case ParserTokenType.NamespacePrefixDeclaration:
                    break;

                case ParserTokenType.NamespaceDeclaration:
                    break;

                case ParserTokenType.AttachedPropertyObject:
                    break;

                default:
                    break;
                }
            }

            if (addObjectCompletions)
            {
                this.AddObjectCompletions(objectTree, parentNode, completions);
            }

            if (addMemberCompletions)
            {
                this.AddMemberCompletions(objectTree, currentNode, currentText, completions);
            }

            if (addValueCompletions)
            {
                this.AddValueCompletions(objectTree, currentNode, token, contextItems, completions);
            }

            // sort the completions...
            completions.Sort((a, b) =>
            {
                return(a.DisplayText.CompareTo(b.DisplayText));
            });

            return(completions);
        }