private ITrackingSpan FindApplicableSpan(ICompletionSession session)
        {
            // We eventually want to use an ITextStructureNavigator to expand the current point, but
            // first we have to teach it what out structure is.  For now, we just understand the Rtype
            // syntax directly.
            ////ITextStructureNavigator navigator = this.sourceProvider.NavigatorService.GetTextStructureNavigator(this.textBuffer);

            ITextSnapshot  snapshot     = session.TextView.TextSnapshot;
            ITrackingPoint triggerPoint = session.GetTriggerPoint(session.TextView.TextBuffer);

            // Look left and right until we're at a contextual break.
            SnapshotPoint initialPoint = triggerPoint.GetPoint(snapshot);
            SnapshotPoint leftPoint    = initialPoint;
            SnapshotPoint rightPoint   = initialPoint;

            ITextSnapshotLine line = leftPoint.GetContainingLine();

            // look left...
            if (leftPoint > line.Start)
            {
                leftPoint -= 1;
                while (leftPoint > line.Start && CompletionSource.IsTokenTermCharacter(leftPoint.GetChar()))
                {
                    leftPoint -= 1;
                }

                // If we bailed because of the character, advance back to the right.
                if (!CompletionSource.IsTokenTermCharacter(leftPoint.GetChar()))
                {
                    leftPoint += 1;
                }
            }

            // look right...
            // In theory, we might need to include spaces that are inside a quoted region, but we don't know if we've
            // just added the start-quote and might over-extend the selection.  It's safer to be conservative even if
            // it means leaving cruft in the buffer... it's easier for the user to delete the extra than to recover it if
            // we removed it.
            while (rightPoint < line.End && CompletionSource.IsTokenTermCharacter(rightPoint.GetChar()))
            {
                rightPoint += 1;
            }

            ITrackingSpan applicableSpan = snapshot.CreateTrackingSpan(
                leftPoint,
                rightPoint - leftPoint,
                SpanTrackingMode.EdgeInclusive);

            ////System.Diagnostics.Debug.WriteLine("**FindApplicableSpan: final span={0} ('{1}')", applicableSpan, applicableSpan.GetText(leftPoint.Snapshot));

            return(applicableSpan);
        }
Beispiel #2
0
        private int HandleTyping(CommandID command, uint options, IntPtr pvIn, IntPtr pvOut)
        {
            // If we're in an automation function, *don't* trigger anything as a result of typing!
            if (VsShellUtilities.IsInAutomationFunction(this.provider.ServiceProvider))
            {
                return(this.PassThrough(command, options, pvIn, pvOut));
            }

            System.Diagnostics.Debug.Assert(command.Guid == VSConstants.VSStd2K);
            System.Diagnostics.Debug.Assert(command.ID == (int)VSConstants.VSStd2KCmdID.TYPECHAR ||
                                            command.ID == (int)VSConstants.VSStd2KCmdID.RETURN ||
                                            command.ID == (int)VSConstants.VSStd2KCmdID.TAB ||
                                            command.ID == (int)VSConstants.VSStd2KCmdID.BACKSPACE ||
                                            command.ID == (int)VSConstants.VSStd2KCmdID.DELETE);

            VSConstants.VSStd2KCmdID commandId = (VSConstants.VSStd2KCmdID)command.ID;
            char typedChar = char.MinValue;

            // Make sure the input is a typed character before getting it.
            if (commandId == VSConstants.VSStd2KCmdID.TYPECHAR)
            {
                typedChar = (char)(ushort)Marshal.GetObjectForNativeVariant(pvIn);
            }

            // Check for a commit character, and possibly commit a selection.
            if (commandId == VSConstants.VSStd2KCmdID.RETURN ||
                commandId == VSConstants.VSStd2KCmdID.TAB ||
                (commandId == VSConstants.VSStd2KCmdID.TYPECHAR && !CompletionSource.IsTokenTermCharacter(typedChar)))
            {
                if (this.session != null && !this.session.IsDismissed)
                {
                    // if the selection is fully selected, commit the current session
                    if (this.session.SelectedCompletionSet.SelectionStatus.IsSelected)
                    {
                        ITrackingSpan applicableSpan = this.session.SelectedCompletionSet.ApplicableTo;
                        this.session.Commit();

                        // If we ended with whitespace or '=', trigger another session!
                        char ch = (applicableSpan.GetEndPoint(applicableSpan.TextBuffer.CurrentSnapshot) - 1).GetChar();
                        if (char.IsWhiteSpace(ch) || ch == '=')
                        {
                            this.TriggerCompletion();
                        }

                        return(VSConstants.S_OK);    // don't add the commit character to the buffer
                    }
                    else
                    {
                        // if there is no selection, dismiss the session
                        this.session.Dismiss();
                    }
                }
            }

            // pass the command on through to the buffer to allow typing...
            int ret = this.PassThrough(command, options, pvIn, pvOut);

            if (commandId == VSConstants.VSStd2KCmdID.TYPECHAR && !typedChar.Equals(char.MinValue))
            {
                // If there is no active session, bring up completion
                if (this.session == null || this.session.IsDismissed)
                {
                    this.TriggerCompletion();
                }

                if (this.session != null && !this.session.IsDismissed)
                {
                    // the completion session is already active, so just filter
                    this.session.Filter();
                }
            }
            // else if BACKSPACE or DELETE, redo the filter...
            else if (commandId == VSConstants.VSStd2KCmdID.BACKSPACE ||
                     commandId == VSConstants.VSStd2KCmdID.DELETE)
            {
                if (this.session != null && !this.session.IsDismissed)
                {
                    this.session.Filter();
                }
            }

            return(ret);
        }
        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),
                                                    ""));
                            }
                        }
                    }
                }
            }
        }