diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataHelper.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataHelper.cs index 12de96b84b..4ac0c58022 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataHelper.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataHelper.cs @@ -53,6 +53,8 @@ namespace ICSharpCode.XamlBinding int offsetFromValueStart = Utils.GetOffsetFromValueStart(text, offset); AttributeValue value = null; + value = MarkupExtensionParser.ParseValue(attributeValue); + XamlContextDescription description = XamlContextDescription.InTag; if (path == null || path.Elements.Count == 0) { @@ -363,7 +365,7 @@ namespace ICSharpCode.XamlBinding public static IEnumerable CreateMarkupExtensionInsight(XamlCompletionContext context, ParseInformation info, ITextEditor editor) { - var markup = GetInnermostMarkup(context.AttributeValue.ExtensionValue); + var markup = Utils.GetInnermostMarkup(context.AttributeValue.ExtensionValue); var trr = ResolveMarkupExtensionType(markup, context); if (trr != null) { @@ -384,7 +386,7 @@ namespace ICSharpCode.XamlBinding var list = new XamlCompletionItemList(); var path = XmlParser.GetActiveElementStartPathAtIndex(editor.Document.Text, editor.Caret.Offset); - var markup = GetInnermostMarkup(context.AttributeValue.ExtensionValue); + var markup = Utils.GetInnermostMarkup(context.AttributeValue.ExtensionValue); var trr = ResolveMarkupExtensionType(markup, context); @@ -593,22 +595,6 @@ namespace ICSharpCode.XamlBinding { return null; } - - public static MarkupExtensionInfo GetInnermostMarkup(MarkupExtensionInfo markup) - { - var last = markup.PositionalArguments.LastOrDefault(); - - if (markup.NamedArguments.Count > 0) - last = markup.NamedArguments.LastOrDefault().Value; - - if (last != null) { - if (!last.IsString) { - return GetInnermostMarkup(last.ExtensionValue); - } - } - - return markup; - } public static TypeResolveResult ResolveMarkupExtensionType(MarkupExtensionInfo markup, XamlCompletionContext context) { diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionInfo.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionInfo.cs index d82e2969d5..41a25a0bc1 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionInfo.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionInfo.cs @@ -20,6 +20,8 @@ namespace ICSharpCode.XamlBinding public string ExtensionType { get; set; } public IList PositionalArguments { get; private set; } public IDictionary NamedArguments { get; private set; } + + public int StartOffset { get; set; } public MarkupExtensionInfo() : this(string.Empty, new List(), new Dictionary(StringComparer.OrdinalIgnoreCase)) @@ -39,6 +41,8 @@ namespace ICSharpCode.XamlBinding string stringValue; MarkupExtensionInfo extensionValue; + public int StartOffset { get; set; } + public bool IsString { get { return stringValue != null; } } diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionParser.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionParser.cs index 9051b4f0e8..cb3dc2d3f8 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionParser.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionParser.cs @@ -19,25 +19,28 @@ namespace ICSharpCode.XamlBinding var tokenizer = new MarkupExtensionTokenizer(text); string argumentName = null; + int namedArgsStart = 0; var token = tokenizer.NextToken(); while (token.Kind != MarkupExtensionTokenKind.EndOfFile) { switch (token.Kind) { case MarkupExtensionTokenKind.TypeName: info.ExtensionType = token.Value; + info.StartOffset = token.StartOffset; break; case MarkupExtensionTokenKind.MemberName: // if there is an open member without a value add the member name if (argumentName != null) info.NamedArguments.Add(argumentName, new AttributeValue(string.Empty)); argumentName = token.Value; + namedArgsStart = token.StartOffset; break; case MarkupExtensionTokenKind.String: if (argumentName != null) { - info.NamedArguments.Add(argumentName, ParseValue(token.Value)); + info.NamedArguments.Add(argumentName, ParseValue(token.Value, namedArgsStart)); argumentName = null; } else { - info.PositionalArguments.Add(ParseValue(token.Value)); + info.PositionalArguments.Add(ParseValue(token.Value, token.StartOffset)); } break; } @@ -51,14 +54,19 @@ namespace ICSharpCode.XamlBinding } public static AttributeValue ParseValue(string text) + { + return ParseValue(text, 0); + } + + public static AttributeValue ParseValue(string text, int offset) { if (string.IsNullOrEmpty(text)) - return new AttributeValue(string.Empty); + return new AttributeValue(string.Empty) { StartOffset = offset }; if (text.StartsWith("{", StringComparison.OrdinalIgnoreCase)) - return new AttributeValue(Parse(text)); + return new AttributeValue(Parse(text)) { StartOffset = offset }; else - return new AttributeValue(text); + return new AttributeValue(text) { StartOffset = offset }; } } } diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionToken.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionToken.cs index a5140fb529..97a02b18e7 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionToken.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionToken.cs @@ -14,6 +14,8 @@ namespace ICSharpCode.XamlBinding public MarkupExtensionTokenKind Kind { get; private set; } public string Value { get; private set; } + public int StartOffset { get; set; } + public MarkupExtensionToken(MarkupExtensionTokenKind kind, string value) { this.Kind = kind; diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionTokenizer.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionTokenizer.cs index 1caf874500..ca83d09b45 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionTokenizer.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionTokenizer.cs @@ -27,6 +27,8 @@ namespace ICSharpCode.XamlBinding string text; int pos; + int startPos; + Queue tokens = new Queue(); /// @@ -51,7 +53,7 @@ namespace ICSharpCode.XamlBinding void AddToken(MarkupExtensionTokenKind kind, string val) { - tokens.Enqueue(new MarkupExtensionToken(kind, val)); + tokens.Enqueue(new MarkupExtensionToken(kind, val) { StartOffset = startPos }); } void ParseBeginning() @@ -75,16 +77,20 @@ namespace ICSharpCode.XamlBinding case '}': AddToken(MarkupExtensionTokenKind.CloseBrace, "}"); pos++; + startPos = pos; break; case '=': AddToken(MarkupExtensionTokenKind.Equals, "="); pos++; + startPos = pos; break; case ',': AddToken(MarkupExtensionTokenKind.Comma, ","); pos++; + startPos = pos; break; default: + startPos = pos; MembernameOrString(); break; } diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Utils.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Utils.cs index 65a82f1c6e..6b6e69d520 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Utils.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Utils.cs @@ -206,5 +206,53 @@ namespace ICSharpCode.XamlBinding return r; } + + + public static MarkupExtensionInfo GetInnermostMarkup(MarkupExtensionInfo markup) + { + var last = markup.PositionalArguments.LastOrDefault(); + + if (markup.NamedArguments.Count > 0) + last = markup.NamedArguments.LastOrDefault().Value; + + if (last != null) { + if (!last.IsString) { + return GetInnermostMarkup(last.ExtensionValue); + } + } + + return markup; + } + + /// + /// Gets the of a markup extension at the given position. + /// + /// The markup extension data to parse. + /// The offset to look at. + /// + /// A string, if the at offset is the extension type.
+ /// An AttributeValue, if at the offset is a positional argument.
+ /// A KeyValuePair<string, AttributeValue>, if at the offset is a named argument. + ///
+ public static object GetMarkupDataAtPosition(MarkupExtensionInfo info, int offset) + { + object previous = info.ExtensionType; + + Debug.Print("offset: " + offset); + + foreach (var item in info.PositionalArguments) { + if (item.StartOffset > offset) + break; + previous = item.IsString ? item : GetMarkupDataAtPosition(item.ExtensionValue, offset - item.StartOffset); + } + + foreach (var pair in info.NamedArguments) { + if (pair.Value.StartOffset > offset) + break; + previous = pair.Value.IsString ? pair.Value : GetMarkupDataAtPosition(pair.Value.ExtensionValue, offset - pair.Value.StartOffset); + } + + return previous; + } } } diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCompletionItemList.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCompletionItemList.cs index 143a67b6b0..633a0ae977 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCompletionItemList.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCompletionItemList.cs @@ -51,7 +51,7 @@ namespace ICSharpCode.XamlBinding AttributeValue value = MarkupExtensionParser.ParseValue(valuePart); if (value != null && !value.IsString) { - var markup = CompletionDataHelper.GetInnermostMarkup(value.ExtensionValue); + var markup = Utils.GetInnermostMarkup(value.ExtensionValue); if (markup.NamedArguments.Count > 0 || markup.PositionalArguments.Count > 0) { int oldOffset = context.Editor.Caret.Offset; context.Editor.Caret.Offset = context.StartOffset; diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlResolver.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlResolver.cs index b32d9d89f3..f3d0d7475b 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlResolver.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlResolver.cs @@ -23,6 +23,7 @@ namespace ICSharpCode.XamlBinding public class XamlResolver : IResolver { IClass callingClass; + string fileContent; string resolveExpression; int caretLine, caretColumn; XamlContext context; @@ -32,26 +33,58 @@ namespace ICSharpCode.XamlBinding this.resolveExpression = expressionResult.Expression; this.caretLine = expressionResult.Region.BeginLine; this.caretColumn = expressionResult.Region.BeginColumn; + this.fileContent = fileContent; this.callingClass = parseInfo.BestCompilationUnit.GetInnermostClass(caretLine, caretColumn); - this.context = CompletionDataHelper.ResolveContext(fileContent, parseInfo.MostRecentCompilationUnit.FileName, caretLine, caretColumn); - if (context == null) - return null; - if (string.IsNullOrEmpty(context.AttributeName)) { - return ResolveElementName(expressionResult.Expression); - } - else if (context.Description == XamlContextDescription.InAttributeValue) { - MemberResolveResult mrr = ResolveAttribute(context.AttributeName); - if (mrr != null) { - return ResolveAttributeValue(mrr.ResolvedMember, resolveExpression); - } - } - else { - // in attribute name - return ResolveAttribute(resolveExpression); + this.context = expressionResult.Context as XamlContext ?? CompletionDataHelper.ResolveContext(fileContent, parseInfo.MostRecentCompilationUnit.FileName, caretLine, caretColumn); + + switch (this.context.Description) { + case XamlContextDescription.AtTag: + return ResolveElementName(resolveExpression); + case XamlContextDescription.InTag: + return ResolveAttribute(resolveExpression); + case XamlContextDescription.InAttributeValue: + MemberResolveResult mrr = ResolveAttribute(context.AttributeName); + if (mrr != null) { + return ResolveAttributeValue(mrr.ResolvedMember, resolveExpression) ?? mrr; + } + break; + case XamlContextDescription.InMarkupExtension: + return ResolveMarkupExtension(resolveExpression); } return null; } + + ResolveResult ResolveMarkupExtension(string expression) + { + if (context.AttributeValue.IsString) + return null; + + object data = Utils.GetMarkupDataAtPosition(context.AttributeValue.ExtensionValue, context.ValueStartOffset); + + // resolve markup extension type + if ((data as string) == expression) { + return ResolveElementName(expression + "Extension") ?? ResolveElementName(expression); + } else { + var value = data as AttributeValue; + if (value != null && value.IsString) { + return ResolveElementName(expression) ?? ResolveAttribute(expression); + } + + if (data is KeyValuePair) { + var pair = (KeyValuePair)data; + var member = ResolveAttribute(pair.Key); + if (pair.Value.StartOffset + pair.Key.Length >= context.ValueStartOffset) { + return member; + } else { + if (pair.Value.IsString && member != null) + return ResolveAttributeValue(member.ResolvedMember, expression); + } + } + + return null; + } + } ResolveResult ResolveElementName(string exp) {