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