diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataHelper.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataHelper.cs index 4ac0c58022..cefd3f59aa 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataHelper.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataHelper.cs @@ -51,38 +51,38 @@ namespace ICSharpCode.XamlBinding string attributeValue = XmlParser.GetAttributeValueAtIndex(text, offset); bool inAttributeValue = XmlParser.IsInsideAttributeValue(text, offset); int offsetFromValueStart = Utils.GetOffsetFromValueStart(text, offset); - AttributeValue value = null; - - value = MarkupExtensionParser.ParseValue(attributeValue); - - XamlContextDescription description = XamlContextDescription.InTag; + int elementStartIndex = XmlParser.GetActiveElementStartIndex(text, offset); + AttributeValue value = MarkupExtensionParser.ParseValue(attributeValue); + XamlContextDescription description = XamlContextDescription.None; if (path == null || path.Elements.Count == 0) { - description = XamlContextDescription.None; path = XmlParser.GetParentElementPath(text.Substring(0, offset)); } else { - int ltOffset = XmlParser.GetActiveElementStartIndex(text, offset); - if (ltOffset == -1) - description = XamlContextDescription.AtTag; - else { - string space = text.Substring(ltOffset + 1, offset - ltOffset - 1); - var last = path.Elements.LastOrDefault(); - if (last != null && last.ToString().StartsWith(space, StringComparison.Ordinal)) - description = XamlContextDescription.AtTag; - } + description = XamlContextDescription.AtTag; } - if (inAttributeValue) - description = XamlContextDescription.InAttributeValue; + if (text[offset] == '>') + description = XamlContextDescription.None; - if (value != null && !value.IsString) - description = XamlContextDescription.InMarkupExtension; + if (!string.IsNullOrEmpty(attribute) || (elementStartIndex > -1 && offset > 0 && char.IsWhiteSpace(text[offset - 1]))) + description = XamlContextDescription.InTag; + + if (inAttributeValue) { + description = XamlContextDescription.InAttributeValue; + + if (value != null && !value.IsString) + description = XamlContextDescription.InMarkupExtension; + + if (attributeValue.StartsWith("{}") && attributeValue.Length > 2) + description = XamlContextDescription.InAttributeValue; + } if (Utils.IsInsideXmlComment(text, offset)) description = XamlContextDescription.InComment; Dictionary xmlnsDefs = new Dictionary(); + // TODO : does not work when trying to resolve , if xmlns declarartion is in using (XmlTextReader reader = Utils.CreateReaderAtTarget(text, line, col)) { xmlnsDefs.AddRange(reader.GetNamespacesInScope(XmlNamespaceScope.All)); } @@ -98,66 +98,15 @@ namespace ICSharpCode.XamlBinding ParseInformation = info }; + LoggingService.Debug(context); + return context; } public static XamlCompletionContext ResolveCompletionContext(ITextEditor editor, char typedValue) { - string text = editor.Document.Text; - int offset = editor.Caret.Offset; - - ParseInformation info = ParserService.GetParseInformation(editor.FileName); - XmlElementPath path = XmlParser.GetActiveElementStartPathAtIndex(text, offset); - string attribute = XmlParser.GetAttributeNameAtIndex(text, offset); - string attributeValue = XmlParser.GetAttributeValueAtIndex(text, offset); - bool inAttributeValue = XmlParser.IsInsideAttributeValue(text, offset); - int offsetFromValueStart = Utils.GetOffsetFromValueStart(text, offset); - AttributeValue value = null; - - value = MarkupExtensionParser.ParseValue(attributeValue); - - XamlContextDescription description = XamlContextDescription.InTag; - - if (path == null || path.Elements.Count == 0) { - description = XamlContextDescription.None; - path = XmlParser.GetParentElementPath(text.Substring(0, offset)); - } else { - int ltOffset = XmlParser.GetActiveElementStartIndex(text, offset); - if (ltOffset == -1) - description = XamlContextDescription.AtTag; - else { - string space = text.Substring(ltOffset + 1, offset - ltOffset - 1); - var last = path.Elements.LastOrDefault(); - if (last != null && last.ToString().StartsWith(space, StringComparison.Ordinal)) - description = XamlContextDescription.AtTag; - } - } - - if (inAttributeValue) - description = XamlContextDescription.InAttributeValue; - - if (value != null && !value.IsString) - description = XamlContextDescription.InMarkupExtension; - - if (Utils.IsInsideXmlComment(text, offset)) - description = XamlContextDescription.InComment; - - Dictionary xmlnsDefs = new Dictionary(); - - using (XmlTextReader reader = Utils.CreateReaderAtTarget(text, editor.Caret.Line, editor.Caret.Column)) { - xmlnsDefs.AddRange(reader.GetNamespacesInScope(XmlNamespaceScope.All)); - } - - var context = new XamlCompletionContext() { + var context = new XamlCompletionContext(ResolveContext(editor.Document.Text, editor.FileName, editor.Caret.Line, editor.Caret.Column)) { PressedKey = typedValue, - Description = description, - AttributeName = attribute, - AttributeValue = value, - RawAttributeValue = attributeValue, - ValueStartOffset = offsetFromValueStart, - Path = (path == null || path.Elements.Count == 0) ? null : path, - XmlnsDefinitions = xmlnsDefs, - ParseInformation = info, Editor = editor }; @@ -208,8 +157,12 @@ namespace ICSharpCode.XamlBinding } foreach (string @namespace in content.NamespaceNames) { - if (!string.IsNullOrEmpty(@namespace)) - list.Add(new XmlnsCompletionItem(@namespace, content.AssemblyName)); + if (!string.IsNullOrEmpty(@namespace)) { + if (string.IsNullOrEmpty(content.AssemblyName)) + list.Add(new XmlnsCompletionItem(@namespace, false)); + else + list.Add(new XmlnsCompletionItem(@namespace, content.AssemblyName)); + } } } @@ -314,13 +267,14 @@ namespace ICSharpCode.XamlBinding } break; case XamlContextDescription.AtTag: - list.Items.AddRange(standardElements); if (editor.Document.GetCharAt(editor.Caret.Offset - 1) == '.' || context.PressedKey == '.') { var loc = editor.Document.OffsetToPosition(Utils.GetParentElementStart(editor)); var existing = Utils.GetListOfExistingAttributeNames(editor.Document.Text, loc.Line, loc.Column); list.Items.AddRange(CreateListForAttributeName(context, existing).RemoveEvents()); - } else + } else { + list.Items.AddRange(standardElements); list.Items.AddRange(CreateListForElement(info, editor.Document.Text, editor.Caret.Line, editor.Caret.Column, false)); + } break; case XamlContextDescription.InTag: var existingAttribs = Utils.GetListOfExistingAttributeNames(editor.Document.Text, editor.Caret.Line, editor.Caret.Column); @@ -363,9 +317,9 @@ namespace ICSharpCode.XamlBinding return false; } - public static IEnumerable CreateMarkupExtensionInsight(XamlCompletionContext context, ParseInformation info, ITextEditor editor) + public static IEnumerable CreateMarkupExtensionInsight(XamlCompletionContext context) { - var markup = Utils.GetInnermostMarkup(context.AttributeValue.ExtensionValue); + var markup = Utils.GetMarkupExtensionAtPosition(context.AttributeValue.ExtensionValue, context.Editor.Caret.Offset); var trr = ResolveMarkupExtensionType(markup, context); if (trr != null) { @@ -381,23 +335,21 @@ namespace ICSharpCode.XamlBinding } } - public static ICompletionItemList CreateMarkupExtensionCompletion(XamlCompletionContext context, ParseInformation info, ITextEditor editor) + public static ICompletionItemList CreateMarkupExtensionCompletion(XamlCompletionContext context) { var list = new XamlCompletionItemList(); - var path = XmlParser.GetActiveElementStartPathAtIndex(editor.Document.Text, editor.Caret.Offset); - - var markup = Utils.GetInnermostMarkup(context.AttributeValue.ExtensionValue); - + var path = XmlParser.GetActiveElementStartPathAtIndex(context.Editor.Document.Text, context.Editor.Caret.Offset); + var markup = Utils.GetMarkupExtensionAtPosition(context.AttributeValue.ExtensionValue, context.Editor.Caret.Offset); var trr = ResolveMarkupExtensionType(markup, context); if (trr == null) { - list.Items.AddRange(CreateListOfMarkupExtensions(info, editor.Document.Text, editor.Caret.Line, editor.Caret.Column)); + list.Items.AddRange(CreateListOfMarkupExtensions(context.ParseInformation, context.Editor.Document.Text, context.Editor.Caret.Line, context.Editor.Caret.Column)); list.PreselectionLength = markup.ExtensionType.Length; } else { if (trr.ResolvedType != null) { if (markup.NamedArguments.Count == 0) { - DoPositionalArgsCompletion(list, context, trr, info, editor); - DoNamedArgsCompletion(list, trr, markup); + if (DoPositionalArgsCompletion(list, context, trr, context.ParseInformation, context.Editor)) + DoNamedArgsCompletion(list, trr, markup); } else DoNamedArgsCompletion(list, trr, markup); } @@ -416,7 +368,8 @@ namespace ICSharpCode.XamlBinding } } - static void DoPositionalArgsCompletion(XamlCompletionItemList list, XamlCompletionContext context, TypeResolveResult trr, ParseInformation info, ITextEditor editor) + /// returns true if elements from named args completion should be added afterwards. + static bool DoPositionalArgsCompletion(XamlCompletionItemList list, XamlCompletionContext context, TypeResolveResult trr, ParseInformation info, ITextEditor editor) { switch (trr.ResolvedType.FullyQualifiedName) { case "System.Windows.Markup.ArrayExtension": @@ -424,21 +377,17 @@ namespace ICSharpCode.XamlBinding // x:Null/x:Array does not need completion, ignore it break; case "System.Windows.Markup.StaticExtension": - if (context.AttributeValue.ExtensionValue.PositionalArguments.Count == 1 && context.PressedKey == ' ') break; - if (context.AttributeValue.ExtensionValue.PositionalArguments.Count <= 1) DoStaticExtensionCompletion(list, context); + if (context.AttributeValue.ExtensionValue.PositionalArguments.Count <= 1) + return DoStaticExtensionCompletion(list, context); break; case "System.Windows.Markup.TypeExtension": - if (context.AttributeValue.ExtensionValue.PositionalArguments.Count == 1 && context.PressedKey == ' ') break; if (context.AttributeValue.ExtensionValue.PositionalArguments.Count <= 1) { list.Items.AddRange(CreateListForElement(info, editor.Document.Text, editor.Caret.Line, editor.Caret.Column, false)); AttributeValue selItem = context.AttributeValue.ExtensionValue.PositionalArguments.LastOrDefault(); if (selItem != null && selItem.IsString) { - string s = selItem.StringValue; - list.PreselectionLength = s.Length; - list.SuggestedItem = list.Items.FirstOrDefault(item => item.Text.StartsWith(s, StringComparison.OrdinalIgnoreCase)); + list.PreselectionLength = selItem.StringValue.Length; } } - break; default: // var ctors = trr.ResolvedType @@ -449,6 +398,8 @@ namespace ICSharpCode.XamlBinding // //var ctor = FindCompletableCtor(ctors, markup.PositionalArguments.Count) break; } + + return true; } public static IEnumerable MemberInsight(MemberResolveResult result) @@ -471,7 +422,7 @@ namespace ICSharpCode.XamlBinding } } - public static IEnumerable MemberCompletion(XamlCompletionContext context, IReturnType type) + public static IEnumerable MemberCompletion(XamlCompletionContext context, IReturnType type, string textPrefix) { if (type == null || type.GetUnderlyingClass() == null) yield break; @@ -479,9 +430,19 @@ namespace ICSharpCode.XamlBinding var c = type.GetUnderlyingClass(); switch (c.ClassType) { + case ClassType.Class: + if (context.Description == XamlContextDescription.InMarkupExtension) { + foreach (IField f in c.Fields) + yield return new XamlCodeCompletionItem(f, textPrefix + f.Name); + foreach (IProperty p in c.Properties.Where(pr => pr.IsPublic && pr.IsStatic && pr.CanGet)) + yield return new XamlCodeCompletionItem(p, textPrefix + p.Name); + } + break; case ClassType.Enum: foreach (IField f in c.Fields) - yield return new XamlCodeCompletionItem(f); + yield return new XamlCodeCompletionItem(f, textPrefix + f.Name); + foreach (IProperty p in c.Properties.Where(pr => pr.IsPublic && pr.IsStatic && pr.CanGet)) + yield return new XamlCodeCompletionItem(p, textPrefix + p.Name); break; case ClassType.Struct: if (c.FullyQualifiedName == "System.Boolean") { @@ -519,17 +480,14 @@ namespace ICSharpCode.XamlBinding break; } - switch (c.FullyQualifiedName) { - case "System.Windows.Media.Brush": - foreach (var item in typeof(System.Windows.Media.Brushes).GetProperties()) { - yield return new DefaultCompletionItem(item.Name); - } - break; - case "System.Windows.Media.Color": - foreach (var item in typeof(System.Windows.Media.Colors).GetProperties()) { - yield return new DefaultCompletionItem(item.Name); - } - break; + var classes = c.ProjectContent.Classes.Where( + cla => (cla.FullyQualifiedName == c.FullyQualifiedName + "s" || + cla.FullyQualifiedName == c.FullyQualifiedName + "es")); + foreach (var coll in classes) { + foreach (var item in coll.Properties) + yield return new DefaultCompletionItem(item.Name); + foreach (var item in coll.Fields.Where(f => f.IsPublic && f.IsStatic && f.ReturnType.FullyQualifiedName == c.FullyQualifiedName)) + yield return new DefaultCompletionItem(item.Name); } } @@ -542,14 +500,15 @@ namespace ICSharpCode.XamlBinding return mrr.ResolvedMember; } - static void DoStaticExtensionCompletion(XamlCompletionItemList list, XamlCompletionContext context) + static bool DoStaticExtensionCompletion(XamlCompletionItemList list, XamlCompletionContext context) { AttributeValue selItem = context.AttributeValue.ExtensionValue.PositionalArguments.LastOrDefault(); if (context.PressedKey == '.') { if (selItem != null && selItem.IsString) { var rr = ResolveStringValue(selItem.StringValue, context) as TypeResolveResult; if (rr != null) - list.Items.AddRange(MemberCompletion(context, rr.ResolvedType)); + list.Items.AddRange(MemberCompletion(context, rr.ResolvedType, string.Empty)); + return false; } } else { if (selItem != null && selItem.IsString) { @@ -557,16 +516,19 @@ namespace ICSharpCode.XamlBinding string s = (index > -1) ? selItem.StringValue.Substring(0, index) : selItem.StringValue; var rr = ResolveStringValue(s, context) as TypeResolveResult; if (rr != null) { - list.Items.AddRange(MemberCompletion(context, rr.ResolvedType)); + list.Items.AddRange(MemberCompletion(context, rr.ResolvedType, (index == -1) ? "." : string.Empty)); + + list.PreselectionLength = (index > -1) ? selItem.StringValue.Length - index - 1 : 0; - list.PreselectionLength = selItem.StringValue.Length - index - 1; - list.SuggestedItem = list.Items.FirstOrDefault(item => item.Text.StartsWith(selItem.StringValue.Substring(index + 1), StringComparison.OrdinalIgnoreCase)); + return false; } else DoStaticTypeCompletion(selItem, list, context.ParseInformation, context.Editor); } else { DoStaticTypeCompletion(selItem, list, context.ParseInformation, context.Editor); } } + + return true; } static void DoStaticTypeCompletion(AttributeValue selItem, XamlCompletionItemList list, ParseInformation info, ITextEditor editor) @@ -578,9 +540,7 @@ namespace ICSharpCode.XamlBinding .Cast()); } if (selItem != null && selItem.IsString) { - string s = selItem.StringValue; - list.PreselectionLength = s.Length; - list.SuggestedItem = list.Items.FirstOrDefault(item => item.Text.StartsWith(s, StringComparison.OrdinalIgnoreCase)); + list.PreselectionLength = selItem.StringValue.Length; } } @@ -599,16 +559,32 @@ namespace ICSharpCode.XamlBinding public static TypeResolveResult ResolveMarkupExtensionType(MarkupExtensionInfo markup, XamlCompletionContext context) { XamlResolver resolver = new XamlResolver(); + XamlContextDescription desc = context.Description; + context.Description = XamlContextDescription.AtTag; TypeResolveResult trr = resolver.Resolve(new ExpressionResult(markup.ExtensionType, context), context.ParseInformation, context.Editor.Document.Text) as TypeResolveResult; if (trr == null) trr = resolver.Resolve(new ExpressionResult(markup.ExtensionType + "Extension", context), context.ParseInformation, context.Editor.Document.Text) as TypeResolveResult; - + context.Description = desc; return trr; } - public static TypeResolveResult ResolveType(string name, XamlCompletionContext context) + public static IReturnType ResolveType(string name, XamlCompletionContext context) { - return new XamlResolver() - .Resolve(new ExpressionResult(name, context), context.ParseInformation, context.Editor.Document.Text) as TypeResolveResult; + XamlCompilationUnit cu = context.ParseInformation.BestCompilationUnit as XamlCompilationUnit; + if (cu == null) + return null; + string prefix = ""; + int len = name.IndexOf(':'); + if (len > 0) { + prefix = name.Substring(0, len); + name = name.Substring(len + 1, name.Length - len - 1); + } + string namespaceName = ""; + if (context.XmlnsDefinitions.TryGetValue(prefix, out namespaceName)) { + IReturnType rt = cu.CreateType(namespaceName, name); + if (rt != null) + return rt; + } + return null; } public static IEnumerable AddMatchingEventHandlers(ITextEditor editor, IMethod delegateInvoker) diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionInfo.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionInfo.cs index 41a25a0bc1..18c4965086 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionInfo.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionInfo.cs @@ -22,7 +22,7 @@ namespace ICSharpCode.XamlBinding public IDictionary NamedArguments { get; private set; } public int StartOffset { get; set; } - + public MarkupExtensionInfo() : this(string.Empty, new List(), new Dictionary(StringComparer.OrdinalIgnoreCase)) { diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionParser.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionParser.cs index cb3dc2d3f8..a7d067de11 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionParser.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionParser.cs @@ -31,13 +31,13 @@ namespace ICSharpCode.XamlBinding 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)); + info.TryAddNamedArgument(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, namedArgsStart)); + info.TryAddNamedArgument(argumentName, ParseValue(token.Value, namedArgsStart)); argumentName = null; } else { info.PositionalArguments.Add(ParseValue(token.Value, token.StartOffset)); @@ -53,6 +53,13 @@ namespace ICSharpCode.XamlBinding return info; } + static void TryAddNamedArgument(this MarkupExtensionInfo info, string name, AttributeValue value) + { + if (!info.NamedArguments.ContainsKey(name)) { + info.NamedArguments.Add(name, value); + } + } + public static AttributeValue ParseValue(string text) { return ParseValue(text, 0); diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionTokenizer.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionTokenizer.cs index ca83d09b45..dfc083a364 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionTokenizer.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionTokenizer.cs @@ -143,7 +143,15 @@ namespace ICSharpCode.XamlBinding } string valueText = b.ToString(); if (pos < text.Length && text[pos] == '=') { - AddToken(MarkupExtensionTokenKind.MemberName, valueText.Trim()); + int splitPos = valueText.LastIndexOf(' '); + if (splitPos > -1) { + string valueString = valueText.Substring(0, splitPos).Trim(); + string memberString = valueText.Substring(splitPos).Trim(); + AddToken(MarkupExtensionTokenKind.String, valueString); + startPos += splitPos; + AddToken(MarkupExtensionTokenKind.MemberName, memberString); + } else + AddToken(MarkupExtensionTokenKind.MemberName, valueText.Trim()); } else { AddToken(MarkupExtensionTokenKind.String, valueText); } diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Options/CodeCompletion.xaml b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Options/CodeCompletion.xaml new file mode 100644 index 0000000000..b89c85103a --- /dev/null +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Options/CodeCompletion.xaml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Options/CodeCompletion.xaml.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Options/CodeCompletion.xaml.cs new file mode 100644 index 0000000000..8a088d4d13 --- /dev/null +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Options/CodeCompletion.xaml.cs @@ -0,0 +1,32 @@ +/* + * Created by SharpDevelop. + * User: Siegfried + * Date: 19.06.2009 + * Time: 20:42 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ +using ICSharpCode.SharpDevelop.Gui; +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; + +namespace ICSharpCode.XamlBinding.Options +{ + /// + /// Interaction logic for CodeCompletion.xaml + /// + public partial class CodeCompletion : OptionPanel + { + public CodeCompletion() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Options/XamlBindingOptions.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Options/XamlBindingOptions.cs new file mode 100644 index 0000000000..5b7862c053 --- /dev/null +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Options/XamlBindingOptions.cs @@ -0,0 +1,17 @@ +/* + * Created by SharpDevelop. + * User: Siegfried + * Date: 19.06.2009 + * Time: 22:20 + * + * To change this template use Tools | Options | Coding | Edit Standard Headers. + */ +using System; + +namespace ICSharpCode.XamlBinding.Options +{ + public static class XamlBindingOptions + { + public static bool UseExtensionCompletion { get; set; } + } +} diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Utils.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Utils.cs index 6b6e69d520..d01735fb4f 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Utils.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Utils.cs @@ -36,16 +36,24 @@ namespace ICSharpCode.XamlBinding public static string GetAttributeValue(string text, int line, int col, string name) { - try { - XmlTextReader reader = new XmlTextReader(new StringReader(text)); - reader.XmlResolver = null; + try { + XmlReader reader = CreateReaderAtTarget(text, line, col); - while (reader.Read() && !IsReaderAtTarget(reader, line, col)) { } - - - if (!reader.MoveToFirstAttribute()) - return null; + if (!reader.MoveToFirstAttribute()) { + /* int offset = GetOffsetFromFilePos(text, line, col) + 1; + if (XmlParser.IsInsideAttributeValue(text, offset)) + text = text.Substring(0, offset) + "\">"; + else { + if (!string.IsNullOrEmpty(XmlParser.GetAttributeNameAtIndex(text, offset))) + text = text.Substring(0, offset) + "=\"\">"; + else + text = text.Substring(0, offset) + ">"; + } + reader = CreateReaderAtTarget(text, line, col); + if (!reader.MoveToFirstAttribute()) */ + return null; + } do { LoggingService.Debug("name: " + reader.Name + " value: " + reader.Value); string plainName = reader.Name.ToUpperInvariant(); @@ -207,23 +215,6 @@ 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. /// @@ -238,8 +229,6 @@ namespace ICSharpCode.XamlBinding { object previous = info.ExtensionType; - Debug.Print("offset: " + offset); - foreach (var item in info.PositionalArguments) { if (item.StartOffset > offset) break; @@ -249,10 +238,29 @@ namespace ICSharpCode.XamlBinding 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); + previous = pair.Value.IsString ? pair : GetMarkupDataAtPosition(pair.Value.ExtensionValue, offset - pair.Value.StartOffset); } return previous; } + + public static MarkupExtensionInfo GetMarkupExtensionAtPosition(MarkupExtensionInfo info, int offset) + { + MarkupExtensionInfo tmp = info; + + foreach (var item in info.PositionalArguments) { + if (item.StartOffset > offset) + break; + tmp = item.IsString ? tmp : GetMarkupExtensionAtPosition(item.ExtensionValue, offset - item.StartOffset); + } + + foreach (var pair in info.NamedArguments) { + if (pair.Value.StartOffset > offset) + break; + tmp = pair.Value.IsString ? tmp : GetMarkupExtensionAtPosition(pair.Value.ExtensionValue, offset - pair.Value.StartOffset); + } + + return tmp; + } } } diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlBinding.addin b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlBinding.addin index 28283773dd..493f267cfc 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlBinding.addin +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlBinding.addin @@ -45,4 +45,12 @@ + + + + + diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlBinding.csproj b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlBinding.csproj index 12f0708e27..815043fb90 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlBinding.csproj +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlBinding.csproj @@ -65,9 +65,17 @@ - - - + + + + + + CodeCompletion.xaml + Code + + + + @@ -82,7 +90,8 @@ - + + @@ -105,6 +114,11 @@ ICSharpCode.Core False + + {7E4A7172-7FF5-48D0-B719-7CD959DD1AC9} + ICSharpCode.Core.Presentation + False + {924EE450-603D-49C1-A8E5-4AFAA31CE6F3} ICSharpCode.SharpDevelop.Dom @@ -125,4 +139,10 @@ False + + + + + + \ No newline at end of file diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCodeCompletionBinding.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCodeCompletionBinding.cs index 55d39d26e2..cc4e2a59d7 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCodeCompletionBinding.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCodeCompletionBinding.cs @@ -64,15 +64,14 @@ namespace ICSharpCode.XamlBinding } break; case '{': // starting point for Markup Extension Completion - if (!string.IsNullOrEmpty(context.AttributeName) && XmlParser.IsInsideAttributeValue(editor.Document.Text, editor.Caret.Offset)) { - string valueBeforeCaret = context.RawAttributeValue.Substring(0, context.ValueStartOffset); - if (valueBeforeCaret.StartsWith("{}", StringComparison.OrdinalIgnoreCase)) - return CodeCompletionKeyPressResult.None; - + if (!string.IsNullOrEmpty(context.AttributeName) + && XmlParser.IsInsideAttributeValue(editor.Document.Text, editor.Caret.Offset) + && !(context.RawAttributeValue.StartsWith("{}") && context.RawAttributeValue.Length != 2) + ) { editor.Document.Insert(editor.Caret.Offset, "{}"); editor.Caret.Offset--; - DoMarkupExtensionCompletion(editor, info, CompletionDataHelper.ResolveCompletionContext(editor, '{')); + DoMarkupExtensionCompletion(CompletionDataHelper.ResolveCompletionContext(editor, '{')); return CodeCompletionKeyPressResult.EatKey; } break; @@ -82,6 +81,9 @@ namespace ICSharpCode.XamlBinding list = CompletionDataHelper.CreateListForContext(editor, context); editor.ShowCompletionWindow(list); return CodeCompletionKeyPressResult.Completed; + } else if (context.Description == XamlContextDescription.InMarkupExtension) { + if (DoMarkupExtensionCompletion(context)) + return CodeCompletionKeyPressResult.Completed; } break; case ':': @@ -134,10 +136,9 @@ namespace ICSharpCode.XamlBinding { XamlCompletionContext context = CompletionDataHelper.ResolveCompletionContext(editor, ' '); context.Forced = true; - - var info = ParserService.GetParseInformation(editor.FileName); + Core.LoggingService.Debug(context); if (context.Path != null) { - if (!XmlParser.IsInsideAttributeValue(editor.Document.Text, editor.Caret.Offset)) { + if (!XmlParser.IsInsideAttributeValue(editor.Document.Text, editor.Caret.Offset) && context.Description != XamlContextDescription.InAttributeValue) { var list = CompletionDataHelper.CreateListForContext(editor, context) as XamlCompletionItemList; string starter = editor.GetWordBeforeCaret().Trim('<', '>'); if (!string.IsNullOrEmpty(starter) && !starter.EndsWith(StringComparison.Ordinal, ' ', '\t', '\n', '\r')) @@ -145,13 +146,9 @@ namespace ICSharpCode.XamlBinding editor.ShowCompletionWindow(list); return true; } else { - // DO NOT USE CompletionDataHelper.CreateListForContext here!!! might result in endless recursion!!!! + // DO NOT USE CompletionDataHelper.CreateListForContext here!!! results in endless recursion!!!! if (!string.IsNullOrEmpty(context.AttributeName)) { - string valueBeforeCaret = context.RawAttributeValue.Substring(0, context.ValueStartOffset); - if (valueBeforeCaret.StartsWith("{}", StringComparison.OrdinalIgnoreCase)) - return false; - - if (!DoMarkupExtensionCompletion(editor, info, context)) { + if (!DoMarkupExtensionCompletion(context)) { XamlResolver resolver = new XamlResolver(); var completionList = new XamlCompletionItemList(); @@ -160,51 +157,74 @@ namespace ICSharpCode.XamlBinding if (mrr != null && mrr.ResolvedType != null) { var c = mrr.ResolvedType.GetUnderlyingClass(); - completionList.Items.AddRange(CompletionDataHelper.MemberCompletion(context, mrr.ResolvedType)); + completionList.Items.AddRange(CompletionDataHelper.MemberCompletion(context, mrr.ResolvedType, string.Empty)); editor.ShowInsightWindow(CompletionDataHelper.MemberInsight(mrr)); } - if (context.AttributeName == "Property" && context.Path.Elements.LastOrDefault().Name == "Setter") { - int offset = Utils.GetParentElementStart(editor); - var loc = editor.Document.OffsetToPosition(offset); - string[] attributes = Utils.GetListOfExistingAttributeNames(editor.Document.Text, loc.Line, loc.Column); - - if (attributes.Contains("TargetType")) { - AttributeValue value = MarkupExtensionParser.ParseValue(Utils.GetAttributeValue(editor.Document.Text, loc.Line, loc.Column, "TargetType")); - if (!value.IsString) { - TypeResolveResult trr = CompletionDataHelper.ResolveMarkupExtensionType(value.ExtensionValue, context); - - var typeName = CompletionDataHelper.ResolveType(GetTypeNameFromTypeExtension(value.ExtensionValue), context); - - if (trr != null && trr.ResolvedClass != null && trr.ResolvedClass.FullyQualifiedName == "System.Windows.Markup.TypeExtension" - && typeName != null && typeName != null) { - completionList.Items.AddRange( - typeName.ResolvedClass.Properties - .Where(p => p.IsPublic && p.CanSet) - .Select(prop => new DefaultCompletionItem(prop.Name)) - .Cast() - ); - } - } - } - } + if (context.Path.Elements.LastOrDefault().Name == "Setter") + DoSetterCompletion(context, completionList); + + completionList.SortItems(); if (context.AttributeName.StartsWith("xmlns")) - completionList.Items.AddRange(CompletionDataHelper.CreateListForXmlnsCompletion(info.BestCompilationUnit.ProjectContent)); + completionList.Items.AddRange(CompletionDataHelper.CreateListForXmlnsCompletion(context.ParseInformation.BestCompilationUnit.ProjectContent)); ICompletionListWindow window = editor.ShowCompletionWindow(completionList); if (context.AttributeName.StartsWith("xmlns")) window.Width = double.NaN; - - return true; } + return true; } } } return false; } + static void DoSetterCompletion(XamlCompletionContext context, XamlCompletionItemList completionList) { + int offset = Utils.GetParentElementStart(context.Editor); + var loc = context.Editor.Document.OffsetToPosition(offset); + + AttributeValue value = MarkupExtensionParser.ParseValue(Utils.GetAttributeValue(context.Editor.Document.Text, loc.Line, loc.Column, "TargetType") ?? string.Empty); + if (!value.IsString) { + TypeResolveResult trr = CompletionDataHelper.ResolveMarkupExtensionType(value.ExtensionValue, context); + var typeName = CompletionDataHelper.ResolveType(GetTypeNameFromTypeExtension(value.ExtensionValue), context); + + if (trr != null && trr.ResolvedClass != null && trr.ResolvedClass.FullyQualifiedName == "System.Windows.Markup.TypeExtension" + && typeName != null) { + switch (context.AttributeName) { + case "Value": + var loc2 = context.Editor.Document.OffsetToPosition(XmlParser.GetActiveElementStartIndex(context.Editor.Document.Text, context.Editor.Caret.Offset)); + AttributeValue propType = MarkupExtensionParser.ParseValue( + Utils.GetAttributeValue(context.Editor.Document.Text, loc2.Line, + loc2.Column, "Property")); + if (!propType.IsString) + break; + + XamlResolver resolver = new XamlResolver(); + var member = resolver.Resolve(new ExpressionResult(GetTypeNameFromTypeExtension(value.ExtensionValue) + "." + propType.StringValue, context), + context.ParseInformation, context.Editor.Document.Text) as MemberResolveResult; + + if (member == null || member.ResolvedMember == null) + break; + + completionList.Items.AddRange( + CompletionDataHelper.MemberCompletion(context, member.ResolvedMember.ReturnType, string.Empty) + ); + break; + case "Property": + completionList.Items.AddRange( + typeName.GetProperties() + .Where(p => p.IsPublic && p.CanSet) + .Select(prop => new DefaultCompletionItem(prop.Name)) + .Cast() + ); + break; + } + } + } + } + static string GetTypeNameFromTypeExtension(MarkupExtensionInfo info) { var item = info.PositionalArguments.FirstOrDefault(); @@ -220,12 +240,12 @@ namespace ICSharpCode.XamlBinding return string.Empty; } - static bool DoMarkupExtensionCompletion(ITextEditor editor, ParseInformation info, XamlCompletionContext context) + static bool DoMarkupExtensionCompletion(XamlCompletionContext context) { - if (context.AttributeValue != null && !context.AttributeValue.IsString) { - var completionList = CompletionDataHelper.CreateMarkupExtensionCompletion(context, info, editor); - editor.ShowCompletionWindow(completionList); - var insightList = CompletionDataHelper.CreateMarkupExtensionInsight(context, info, editor); + if (context.Description == XamlContextDescription.InMarkupExtension && context.AttributeValue != null && !context.AttributeValue.IsString) { + var completionList = CompletionDataHelper.CreateMarkupExtensionCompletion(context); + context.Editor.ShowCompletionWindow(completionList); + var insightList = CompletionDataHelper.CreateMarkupExtensionInsight(context); //editor.ShowInsightWindow(insightList); return true; } diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCompletionItemList.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCompletionItemList.cs index 633a0ae977..75aa2da930 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCompletionItemList.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlCompletionItemList.cs @@ -32,6 +32,22 @@ namespace ICSharpCode.XamlBinding return base.ProcessInput(key); } + public static int CountWhiteSpacesAtEnd(string text) + { + if (string.IsNullOrEmpty(text)) + return 0; + + int i = text.Length - 1; + + while (i >= 0) { + if (!char.IsWhiteSpace(text[i])) + break; + i--; + } + + return text.Length - i - 1; + } + public override void Complete(CompletionContext context, ICompletionItem item) { base.Complete(context, item); @@ -51,15 +67,17 @@ namespace ICSharpCode.XamlBinding AttributeValue value = MarkupExtensionParser.ParseValue(valuePart); if (value != null && !value.IsString) { - var markup = Utils.GetInnermostMarkup(value.ExtensionValue); + var markup = Utils.GetMarkupExtensionAtPosition(value.ExtensionValue, context.Editor.Caret.Offset); if (markup.NamedArguments.Count > 0 || markup.PositionalArguments.Count > 0) { int oldOffset = context.Editor.Caret.Offset; context.Editor.Caret.Offset = context.StartOffset; - string word = context.Editor.GetWordBeforeCaret().Trim(); + string word = context.Editor.GetWordBeforeCaret().TrimEnd(); + int spaces = CountWhiteSpacesAtEnd(context.Editor.GetWordBeforeCaret()); + int typeNameStart = markup.ExtensionType.IndexOf(':') + 1; - if (!word.EndsWith(",") && markup.ExtensionType != word) { - context.Editor.Document.Insert(context.Editor.Caret.Offset, ", "); - oldOffset += 2; + if (!word.EndsWith(",") && markup.ExtensionType.Substring(typeNameStart, markup.ExtensionType.Length - typeNameStart) != word) { + context.Editor.Document.Replace(context.Editor.Caret.Offset - spaces, spaces, ", "); + oldOffset += (2 - spaces); } context.Editor.Caret.Offset = oldOffset; diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlContext.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlContext.cs index 36e8675163..3eee87e513 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlContext.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlContext.cs @@ -47,6 +47,20 @@ namespace ICSharpCode.XamlBinding } public class XamlCompletionContext : XamlContext { + public XamlCompletionContext() { } + + public XamlCompletionContext(XamlContext context) + { + this.AttributeName = context.AttributeName; + this.AttributeValue = context.AttributeValue; + this.Description = context.Description; + this.ParseInformation = context.ParseInformation; + this.Path = context.Path; + this.RawAttributeValue = context.RawAttributeValue; + this.ValueStartOffset = context.ValueStartOffset; + this.XmlnsDefinitions = context.XmlnsDefinitions; + } + public char PressedKey { get; set; } public bool Forced { get; set; } public ITextEditor Editor { get; set; } diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlResolver.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlResolver.cs index f3d0d7475b..1d1598fb84 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlResolver.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlResolver.cs @@ -5,15 +5,17 @@ // $Revision: 3539 $ // -using ICSharpCode.XmlEditor; using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Xml; + using ICSharpCode.Core; using ICSharpCode.SharpDevelop.Dom; +using ICSharpCode.XmlEditor; namespace ICSharpCode.XamlBinding { @@ -66,30 +68,75 @@ namespace ICSharpCode.XamlBinding 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); + MarkupExtensionInfo info = Utils.GetMarkupExtensionAtPosition(context.AttributeValue.ExtensionValue, context.ValueStartOffset); + TypeResolveResult extensionType = (ResolveElementName(info.ExtensionType + "Extension") ?? ResolveElementName(info.ExtensionType)) as TypeResolveResult; + // TODO : hardcode x:Type and x:Static + if (extensionType != null && extensionType.ResolvedType != null) { + var value = data as AttributeValue; + + switch (extensionType.ResolvedType.FullyQualifiedName) { + case "System.Windows.Markup.StaticExtension": + if (value != null && value.IsString) { + return ResolveElementName(expression) ?? ResolveAttribute(expression); + } + + if (data is KeyValuePair) { + var pair = (KeyValuePair)data; + var member = ResolveNamedAttribute(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) ?? ResolveElementName(expression); + } + } + break; + case "System.Windows.Markup.TypeExtension": + + + if (value != null && value.IsString) { + return ResolveElementName(expression) ?? ResolveAttribute(expression); + } + + if (data is KeyValuePair) { + var pair = (KeyValuePair)data; + var member = ResolveNamedAttribute(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) ?? ResolveElementName(expression); + } + } + break; } } return null; } } + + MemberResolveResult ResolveNamedAttribute(string expression) + { + MarkupExtensionInfo info = Utils.GetMarkupExtensionAtPosition(context.AttributeValue.ExtensionValue, context.ValueStartOffset); + TypeResolveResult extensionType = (ResolveElementName(info.ExtensionType + "Extension") ?? ResolveElementName(info.ExtensionType)) as TypeResolveResult; + + if (extensionType != null && extensionType.ResolvedType != null) { + return ResolvePropertyName(extensionType.ResolvedType, expression, false); + } + + return null; + } ResolveResult ResolveElementName(string exp) { string xmlNamespace; string name; + + foreach (var s in context.XmlnsDefinitions) { + Debug.Print(s.Key + " " + s.Value); + } + this.resolveExpression = exp; if (resolveExpression.Contains(":")) { string prefix = resolveExpression.Substring(0, resolveExpression.IndexOf(':')); @@ -125,31 +172,37 @@ namespace ICSharpCode.XamlBinding IProjectContent pc = context.ParseInformation.BestCompilationUnit.ProjectContent; IReturnType resolvedType = XamlCompilationUnit.FindType(pc, xmlNamespace, className); if (resolvedType != null && resolvedType.GetUnderlyingClass() != null) { - IMember member = resolvedType.GetProperties().Find(delegate(IProperty p) { return p.Name == propertyName; }); - if (member == null) { - member = resolvedType.GetEvents().Find(delegate(IEvent p) { return p.Name == propertyName; }); - } - if (member == null && allowAttached) { - IMethod method = resolvedType.GetMethods().Find( + return ResolvePropertyName(resolvedType, propertyName, allowAttached); + } + return null; + } + + MemberResolveResult ResolvePropertyName(IReturnType resolvedType, string propertyName, bool allowAttached) + { + IMember member = resolvedType.GetProperties().Find(delegate(IProperty p) { return p.Name == propertyName; }); + if (member == null) { + member = resolvedType.GetEvents().Find(delegate(IEvent p) { return p.Name == propertyName; }); + } + if (member == null && allowAttached) { + IMethod method = resolvedType.GetMethods().Find( + delegate(IMethod p) { + return p.IsStatic && p.Parameters.Count == 1 && p.Name == "Get" + propertyName; + }); + member = method; + if (member != null) { + member = new DefaultProperty(resolvedType.GetUnderlyingClass(), propertyName) { ReturnType = method.ReturnType }; + } else { + IMethod m = resolvedType.GetMethods().Find( delegate(IMethod p) { - return p.IsStatic && p.Parameters.Count == 1 && p.Name == "Get" + propertyName; + return p.IsPublic && p.IsStatic && p.Parameters.Count == 2 && (p.Name == "Add" + propertyName + "Handler" || p.Name == "Remove" + propertyName + "Handler"); }); - member = method; - if (member != null) { - member = new DefaultProperty(resolvedType.GetUnderlyingClass(), propertyName) { ReturnType = method.ReturnType }; - } else { - IMethod m = resolvedType.GetMethods().Find( - delegate(IMethod p) { - return p.IsPublic && p.IsStatic && p.Parameters.Count == 2 && (p.Name == "Add" + propertyName + "Handler" || p.Name == "Remove" + propertyName + "Handler"); - }); - member = m; - if (member != null) - member = new DefaultEvent(resolvedType.GetUnderlyingClass(), propertyName) { ReturnType = m.Parameters[1].ReturnType }; - } + member = m; + if (member != null) + member = new DefaultEvent(resolvedType.GetUnderlyingClass(), propertyName) { ReturnType = m.Parameters[1].ReturnType }; } - if (member != null) - return new MemberResolveResult(callingClass, null, member); } + if (member != null) + return new MemberResolveResult(callingClass, null, member); return null; }