//FIXME: the length should do a readahead to capture the whole token public static (XmlCompletionTrigger kind, int length) GetTrigger(XmlSpineParser parser, XmlTriggerReason reason, char typedCharacter) { bool isExplicit = reason == XmlTriggerReason.Invocation; bool isTypedChar = reason == XmlTriggerReason.TypedChar; bool isBackspace = reason == XmlTriggerReason.Backspace; if (isTypedChar) { Debug.Assert(typedCharacter != '\0'); } var context = parser.GetContext(); // explicit invocation in element name if (isExplicit && context.CurrentState is XmlNameState && context.CurrentState.Parent is XmlTagState) { int length = context.CurrentStateLength; return(XmlCompletionTrigger.Element, length); } //auto trigger after < in free space if ((isTypedChar || isBackspace) && XmlRootState.MaybeTag(context)) { return(XmlCompletionTrigger.Element, 0); } //auto trigger after typing first char after < or fist char of attribute if (isTypedChar && context.CurrentStateLength == 1 && context.CurrentState is XmlNameState && XmlChar.IsFirstNameChar(typedCharacter)) { if (context.CurrentState.Parent is XmlTagState) { return(XmlCompletionTrigger.Element, 1); } if (context.CurrentState.Parent is XmlAttributeState) { return(XmlCompletionTrigger.Attribute, 1); } } // trigger on explicit invocation after < if (isExplicit && XmlRootState.MaybeTag(context)) { return(XmlCompletionTrigger.Element, 0); } //doctype/cdata completion, explicit trigger after <! or type ! after < if ((isExplicit || typedCharacter == '!') && XmlRootState.MaybeCDataOrCommentOrDocType(context)) { return(XmlCompletionTrigger.DeclarationOrCDataOrComment, 2); } //explicit trigger in existing doctype if (isExplicit && (XmlRootState.MaybeDocType(context) || context.Nodes.Peek() is XDocType)) { int length = context.CurrentState is XmlRootState ? context.CurrentStateLength : context.Position - ((XDocType)context.Nodes.Peek()).Span.Start; return(XmlCompletionTrigger.DocType, length); } //explicit trigger in attribute name if (isExplicit && context.CurrentState is XmlNameState && context.CurrentState.Parent is XmlAttributeState) { return(XmlCompletionTrigger.Attribute, context.CurrentStateLength); } //typed space or explicit trigger in tag if ((isExplicit || typedCharacter == ' ') && XmlTagState.IsFree(context)) { return(XmlCompletionTrigger.Attribute, 0); } //attribute value completion if (XmlAttributeValueState.GetDelimiterChar(context).HasValue) { //auto trigger on quote regardless if (context.CurrentStateLength == 1) { return(XmlCompletionTrigger.AttributeValue, 0); } if (isExplicit) { return(XmlCompletionTrigger.AttributeValue, context.CurrentStateLength - 1); } } //entity completion if (context.CurrentState is XmlTextState || context.CurrentState is XmlAttributeValueState) { if (typedCharacter == '&') { return(XmlCompletionTrigger.Entity, 0); } var text = parser.GetContext().KeywordBuilder; if (isBackspace && text[text.Length - 1] == '&') { return(XmlCompletionTrigger.Entity, 0); } if (isExplicit) { for (int i = 0; i < text.Length; i++) { var c = text[text.Length - i - 1]; if (c == '&') { return(XmlCompletionTrigger.Entity, i); } if (!XmlChar.IsNameChar(c)) { break; } } } } //explicit invocation in free space if (isExplicit && ( context.CurrentState is XmlTextState || XmlRootState.IsFree(context) )) { return(XmlCompletionTrigger.ElementWithBracket, 0); } return(XmlCompletionTrigger.None, 0); }
//FIXME: the length should do a readahead to capture the whole token public static (XmlCompletionTrigger kind, int length) GetTrigger(XmlParser spine, XmlTriggerReason reason, char typedCharacter) { int stateTag = ((IXmlParserContext)spine).StateTag; bool isExplicit = reason == XmlTriggerReason.Invocation; bool isTypedChar = reason == XmlTriggerReason.TypedChar; bool isBackspace = reason == XmlTriggerReason.Backspace; Debug.Assert(!isTypedChar || typedCharacter == '\0'); // explicit invocation in element name if (isExplicit && spine.CurrentState is XmlNameState && spine.Nodes.Peek() is XElement el && !el.IsNamed) { int length = spine.CurrentStateLength; return(XmlCompletionTrigger.Element, length); } //auto trigger after < in free space if (spine.CurrentState is XmlRootState && stateTag == XmlRootState.BRACKET) { return(XmlCompletionTrigger.Element, 0); } // trigger on explicit invocation after < if (isExplicit && spine.CurrentState is XmlRootState && stateTag == XmlRootState.BRACKET) { return(XmlCompletionTrigger.Element, 0); } //doctype/cdata completion, explicit trigger after <! or type ! after < if ((isExplicit || typedCharacter == '!') && spine.CurrentState is XmlRootState && stateTag == XmlRootState.BRACKET_EXCLAM) { return(XmlCompletionTrigger.DeclarationOrCDataOrComment, 2); } //explicit trigger in existing declaration if (isExplicit && ((spine.CurrentState is XmlRootState && stateTag == XmlRootState.DOCTYPE) || spine.Nodes.Peek() is XDocType)) { int length = spine.CurrentState is XmlRootState ? spine.CurrentStateLength : spine.Position - ((XDocType)spine.Nodes.Peek()).Span.Start; return(XmlCompletionTrigger.DocType, length); } //explicit trigger in attribute name if (isExplicit && spine.CurrentState is XmlNameState && spine.CurrentState.Parent is XmlAttributeState) { return(XmlCompletionTrigger.Attribute, spine.CurrentStateLength); } //typed space or explicit trigger in tag if ((isExplicit || typedCharacter == ' ') && spine.CurrentState is XmlTagState && stateTag == XmlTagState.FREE) { return(XmlCompletionTrigger.Attribute, 0); } //attribute value completion if (spine.CurrentState is XmlAttributeValueState) { var kind = stateTag & XmlAttributeValueState.TagMask; if (kind == XmlAttributeValueState.DOUBLEQUOTE || kind == XmlAttributeValueState.SINGLEQUOTE) { //auto trigger on quote regardless if (spine.CurrentStateLength == 1) { return(XmlCompletionTrigger.AttributeValue, 0); } if (isExplicit) { return(XmlCompletionTrigger.AttributeValue, spine.CurrentStateLength - 1); } } } //entity completion if (spine.CurrentState is XmlTextState || spine.CurrentState is XmlAttributeValueState) { if (typedCharacter == '&') { return(XmlCompletionTrigger.Entity, 0); } var text = ((IXmlParserContext)spine).KeywordBuilder; if (isBackspace && text[text.Length - 1] == '&') { return(XmlCompletionTrigger.Entity, 0); } if (isExplicit) { for (int i = 0; i < text.Length; i++) { var c = text[text.Length - i - 1]; if (c == '&') { return(XmlCompletionTrigger.Entity, i); } if (!XmlChar.IsNameChar(c)) { break; } } } } //explicit invocation in free space if (isExplicit && ( spine.CurrentState is XmlTextState || (spine.CurrentState is XmlRootState && stateTag == XmlRootState.FREE) )) { return(XmlCompletionTrigger.ElementWithBracket, 0); } return(XmlCompletionTrigger.None, 0); }