protected override CompletionDataList GetAttributeValueCompletions(S.IAttributedXObject ob, S.XAttribute att) { var list = base.GetAttributeValueCompletions(ob, att) ?? new CompletionDataList(); if (ob is S.XElement) { if (ob.Name.HasPrefix) { S.XAttribute idAtt = ob.Attributes[new S.XName("id")]; string id = idAtt == null? null : idAtt.Value; if (string.IsNullOrEmpty(id) || string.IsNullOrEmpty(id.Trim())) { id = null; } AddAspAttributeValueCompletionData(list, ob.Name, att.Name, id); } } else if (ob is AspNetDirective) { return(DirectiveCompletion.GetAttributeValues(project, Document.FileName, ob.Name.FullName, att.Name.FullName)); } return(list.Count > 0? list : null); }
protected virtual void AddAttributeValueCompletionData(CodeCompletionDataProvider cp, S.XElement element, S.XAttribute attribute) { }
ICompletionDataProvider HandleCodeCompletion( CodeCompletionContext completionContext, bool forced, ref int triggerWordLength) { tracker.UpdateEngine(); //FIXME: lines in completionContext are zero-indexed, but ILocation and buffer are 1-indexed. //This could easily cause bugs. int line = completionContext.TriggerLine + 1, col = completionContext.TriggerLineOffset; ITextBuffer buf = this.Buffer; // completionChar may be a space even if the current char isn't, when ctrl-space is fired t int currentPosition = buf.CursorPosition - 1; char currentChar = buf.GetCharAt(currentPosition); char previousChar = buf.GetCharAt(currentPosition - 1); //decide whether completion will be auto-activated, to avoid unnecessary //parsing, which hurts editor responsiveness if (!forced) { // if (tracker.Engine.CurrentState is S.XmlFreeState && !(currentChar == '<' || currentChar == '>')) { return(null); } if (tracker.Engine.CurrentState is S.XmlNameState && tracker.Engine.CurrentState.Parent is S.XmlAttributeState && previousChar != ' ') { return(null); } if (tracker.Engine.CurrentState is S.XmlAttributeValueState && !(previousChar == '\'' || previousChar == '"' || currentChar == '\'' || currentChar == '"')) { return(null); } } //tag completion if (currentChar == '<') { CodeCompletionDataProvider cp = new CodeCompletionDataProvider(null, GetAmbience()); if (tracker.Engine.CurrentState is S.XmlFreeState) { S.XElement el = tracker.Engine.Nodes.Peek() as S.XElement; AddTagCompletionData(cp, el); } return(cp); } //closing tag completion if (tracker.Engine.CurrentState is S.XmlFreeState && currentPosition - 1 > 0 && currentChar == '>') { //get name of current node in document that's being ended S.XElement el = tracker.Engine.Nodes.Peek() as S.XElement; if (el != null && el.Position.End >= currentPosition && !el.IsClosed && el.IsNamed) { CodeCompletionDataProvider cp = new CodeCompletionDataProvider(null, GetAmbience()); cp.AddCompletionData( new MonoDevelop.XmlEditor.Completion.XmlTagCompletionData( String.Concat("</", el.Name.FullName, ">"), 0, true) ); return(cp); } } //attributes names within tags if (tracker.Engine.CurrentState is S.XmlTagState && forced || (tracker.Engine.CurrentState is S.XmlNameState && tracker.Engine.CurrentState.Parent is S.XmlAttributeState && tracker.Engine.CurrentStateLength == 1) ) { int peekp = (tracker.Engine.CurrentState is S.XmlTagState) ? 0 : 1; S.XElement el = (S.XElement)tracker.Engine.Nodes.Peek(peekp); // HACK S.XElement pel = tracker.Engine.Nodes.Peek(peekp + 1) as S.XElement; if (el.Parent == null && pel != null) { pel.AddChildNode(el); } //attributes if (el != null && el.Name.IsValid && (forced || char.IsWhiteSpace(currentChar) || (char.IsWhiteSpace(previousChar) && char.IsLetter(currentChar)))) { CodeCompletionDataProvider cp = new CodeCompletionDataProvider(null, GetAmbience()); if (!forced) { triggerWordLength = 1; } AddAttributeCompletionData(cp, el); return(cp); } } //attribute values //determine whether to trigger completion within attribute values quotes if ((tracker.Engine.CurrentState is S.XmlDoubleQuotedAttributeValueState || tracker.Engine.CurrentState is S.XmlSingleQuotedAttributeValueState) //trigger on the opening quote && (tracker.Engine.CurrentStateLength == 0 //or trigger on first letter of value, if unforced || (!forced && tracker.Engine.CurrentStateLength == 1)) ) { S.XAttribute att = (S.XAttribute)tracker.Engine.Nodes.Peek(); if (att.IsNamed) { S.XElement el = (S.XElement)tracker.Engine.Nodes.Peek(1); // HACK S.XElement pel = tracker.Engine.Nodes.Peek(2) as S.XElement; if (el.Parent == null && pel != null) { pel.AddChildNode(el); } char next = ' '; if (currentPosition + 1 < buf.Length) { next = buf.GetCharAt(currentPosition + 1); } char compareChar = (tracker.Engine.CurrentStateLength == 0)? currentChar : previousChar; Console.WriteLine("ppa: " + att.Value); if ((compareChar == '"' || compareChar == '\'') && (next == compareChar || char.IsWhiteSpace(next)) ) { //if triggered by first letter of value, grab that letter if (tracker.Engine.CurrentStateLength == 1) { triggerWordLength = 1; } CodeCompletionDataProvider cp = new CodeCompletionDataProvider(null, GetAmbience()); AddAttributeValueCompletionData(cp, el, att); return(cp); } } } return(null); }
public override State PushChar(char c, IParseContext context, ref string rollback) { XAttribute att = context.Nodes.Peek() as XAttribute; if (c == '<') { //parent handles message if (att != null) { context.Nodes.Pop(); } rollback = string.Empty; return(Parent); } //state has just been entered if (context.CurrentStateLength == 1) { if (context.PreviousState is XmlNameState) { Debug.Assert(att.IsNamed); context.StateTag = GETTINGEQ; } else if (context.PreviousState is XmlAttributeValueState) { //Got value, so end attribute context.Nodes.Pop(); att.End(context.LocationMinus(1)); IAttributedXObject element = (IAttributedXObject)context.Nodes.Peek(); element.Attributes.AddAttribute(att); rollback = string.Empty; return(Parent); } else { //starting a new attribute Debug.Assert(att == null); Debug.Assert(context.StateTag == NAMING); att = new XAttribute(context.LocationMinus(1)); context.Nodes.Push(att); rollback = string.Empty; return(XmlNameState); } } if (c == '>') { context.LogWarning("Attribute ended unexpectedly with '>' character."); if (att != null) { context.Nodes.Pop(); } rollback = string.Empty; return(Parent); } if (context.StateTag == GETTINGEQ) { if (char.IsWhiteSpace(c)) { return(null); } else if (c == '=') { context.StateTag = GETTINGVAL; return(null); } } else if (context.StateTag == GETTINGVAL) { if (char.IsWhiteSpace(c)) { return(null); } else if (c == '"') { return(DoubleQuotedAttributeValueState); } else if (c == '\'') { return(SingleQuotedAttributeValueState); } else if (char.IsLetterOrDigit(c)) { rollback = string.Empty; return(UnquotedAttributeValueState); } } if (Char.IsLetterOrDigit(c) || char.IsPunctuation(c) || char.IsWhiteSpace(c)) { string err; if (context.StateTag == GETTINGEQ) { context.LogError("Expecting = in attribute, got " + c + "."); } else if (context.StateTag == GETTINGVAL) { context.LogError("Expecting attribute value, got " + c + "."); } else { context.LogError("Unexpected character '" + c + "' in attribute."); } if (att != null) { context.Nodes.Pop(); } rollback = string.Empty; return(Parent); } rollback = string.Empty; return(Parent); }