diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/UtilsTests.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/UtilsTests.cs index 912c751015..62bb433407 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/UtilsTests.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/UtilsTests.cs @@ -18,117 +18,134 @@ namespace ICSharpCode.XamlBinding.Tests public class UtilsTests { [Test] - public void XmlNamespacesForOffsetSimple() + public void DiffTestSimple() { - string xaml = File.ReadAllText("Test1.xaml"); - int offset = xaml.IndexOf("CheckBox") + "CheckBox ".Length; + string xaml = ""; + int offset = " { - {"xmlns", "http://schemas.microsoft.com/netfx/2007/xaml/presentation"}, - {"xmlns:x", "http://schemas.microsoft.com/winfx/2006/xaml"} - }; + int actualResult = Utils.GetOffsetFromValueStart(xaml, offset); - var result = Utils.GetXmlNamespacesForOffset(xaml, offset); + Assert.AreEqual(expectedResult, actualResult); + } + + [Test] + public void DiffTestSimple2() + { + string xaml = ""; + int offset = " { - {"xmlns", "http://schemas.microsoft.com/netfx/2007/xaml/presentation"}, - {"xmlns:x", "http://schemas.microsoft.com/winfx/2006/xaml"}, - {"xmlns:y", "clr-namespace:ICSharpCode.Profiler.Controls;assembly=ICSharpCode.Profiler.Controls"} - }; + string text = @"SharpDevelop uses the MSBuild +libraries for compilation. But when you compile a project +inside SharpDevelop, there's more going on than a +simple call to MSBuild."; - var result = Utils.GetXmlNamespacesForOffset(xaml, offset); + int expected = 0; + int line = 1; + int col = 1; - foreach (var p in result) - Debug.Print(p.Key + " " + p.Value); + int result = Utils.GetOffsetFromFilePos(text, line, col); - Assert.AreEqual(expectedResult, result, "Is not equal"); + Assert.AreEqual(expected, result); } [Test] - public void XmlNamespacesForOffsetComplex() + public void GetOffsetTest2() { - string xaml = File.ReadAllText("Test3.xaml"); - int offset = xaml.IndexOf("CheckBox") + "CheckBox ".Length; + string text = @"SharpDevelop uses the MSBuild +libraries for compilation. But when you compile a project +inside SharpDevelop, there's more going on than a +simple call to MSBuild."; - var expectedResult = new Dictionary { - {"xmlns", "http://schemas.microsoft.com/netfx/2007/xaml/presentation"}, - {"xmlns:x", "clr-namespace:ICSharpCode.Profiler.Controls;assembly=ICSharpCode.Profiler.Controls"} - }; + int expected = 4; + int line = 1; + int col = 5; - var result = Utils.GetXmlNamespacesForOffset(xaml, offset); + int result = Utils.GetOffsetFromFilePos(text, line, col); - foreach (var p in result) - Debug.Print(p.Key + " " + p.Value); - - Assert.AreEqual(expectedResult, result, "Is not equal"); + Assert.AreEqual(expected, result); } [Test] - public void DiffTestSimple() + public void GetOffsetTest3() { - string xaml = ""; - int offset = ""; - int offset = " { "xmlns", "xmlns:x", "xmlns:toolkit", "xmlns:chartingToolkit" }; + int result = Utils.GetOffsetFromFilePos(text, line, col); - Assert.AreEqual(expected.Count, list.Length, "Wrong count!"); - Assert.AreEqual(list, expected, "Wrong elements!"); + Assert.AreEqual(expected, result); } [Test] - public void ExistingAttributesWithInvalidSyntaxTest() + public void GetOffsetTest6() { - string xaml = " { "xmlns", "xmlns:x", "xmlns:toolkit", "xmlns:chartingToolkit" }; + int result = Utils.GetOffsetFromFilePos(text, line, col); - Assert.AreEqual(expected.Count, list.Length, "Wrong count!"); - Assert.AreEqual(list, expected, "Wrong elements!"); + Assert.AreEqual(expected, result); } } } diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataHelper.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataHelper.cs index e1c76934ca..12de96b84b 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataHelper.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataHelper.cs @@ -41,27 +41,77 @@ namespace ICSharpCode.XamlBinding public const string XamlNamespace = "http://schemas.microsoft.com/winfx/2006/xaml"; - public static XamlContext ResolveContext(ITextEditor editor, char typedValue) + public static XamlContext ResolveContext(string text, string fileName, int line, int col) { - string text = editor.Document.Text; - int offset = editor.Caret.Offset; - XamlResolver resolver = new XamlResolver(); + int offset = Utils.GetOffsetFromFilePos(text, line, col); - ParseInformation info = ParserService.GetParseInformation(editor.FileName); + ParseInformation info = ParserService.GetParseInformation(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); - ResolveResult rr = null; AttributeValue value = null; - XamlExpressionContext cxt = new XamlExpressionContext(path, attribute, inAttributeValue); + 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; - if (!string.IsNullOrEmpty(attribute)) { - rr = resolver.Resolve(new ExpressionResult(attribute, cxt) { Region = new DomRegion(1,1) }, info, text); + Dictionary xmlnsDefs = new Dictionary(); + + using (XmlTextReader reader = Utils.CreateReaderAtTarget(text, line, col)) { + xmlnsDefs.AddRange(reader.GetNamespacesInScope(XmlNamespaceScope.All)); } + var context = new XamlContext() { + Description = description, + AttributeName = attribute, + AttributeValue = value, + RawAttributeValue = attributeValue, + ValueStartOffset = offsetFromValueStart, + Path = (path == null || path.Elements.Count == 0) ? null : path, + XmlnsDefinitions = xmlnsDefs, + ParseInformation = info + }; + + 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; @@ -92,33 +142,30 @@ namespace ICSharpCode.XamlBinding Dictionary xmlnsDefs = new Dictionary(); - using (XmlTextReader reader = CreateReaderAtTarget(editor.Document.Text, editor.Caret.Line, editor.Caret.Column)) { - + using (XmlTextReader reader = Utils.CreateReaderAtTarget(text, editor.Caret.Line, editor.Caret.Column)) { + xmlnsDefs.AddRange(reader.GetNamespacesInScope(XmlNamespaceScope.All)); } - var context = new XamlContext() { + var context = new XamlCompletionContext() { PressedKey = typedValue, Description = description, - ResolvedExpression = rr, AttributeName = attribute, AttributeValue = value, RawAttributeValue = attributeValue, ValueStartOffset = offsetFromValueStart, Path = (path == null || path.Elements.Count == 0) ? null : path, - XmlnsDefinitions = xmlnsDefs + XmlnsDefinitions = xmlnsDefs, + ParseInformation = info, + Editor = editor }; - LoggingService.Debug(context); - return context; } - static List CreateListForAttributeName(ITextEditor editor, ParseInformation parseInfo, XamlExpressionContext context, string[] existingItems) + static List CreateListForAttributeName(XamlCompletionContext context, string[] existingItems) { - if (context.ElementPath.Elements.Count == 0) - return null; - QualifiedName lastElement = context.ElementPath.Elements.LastOrDefault(); - XamlCompilationUnit cu = parseInfo.BestCompilationUnit as XamlCompilationUnit; + QualifiedName lastElement = context.Path.Elements.LastOrDefault(); + XamlCompilationUnit cu = context.ParseInformation.BestCompilationUnit as XamlCompilationUnit; if (cu == null) return null; IReturnType rt = cu.CreateType(lastElement.Namespace, lastElement.Name.Trim('.')); @@ -126,7 +173,7 @@ namespace ICSharpCode.XamlBinding return null; var list = new List(); - string xamlPrefix = Utils.GetXamlNamespacePrefix(editor.Document.Text, editor.Caret.Offset); + string xamlPrefix = Utils.GetXamlNamespacePrefix(context); foreach (string item in xamlNamespaceAttributes) { if (!existingItems.Contains(xamlPrefix + ":" + item)) @@ -252,7 +299,7 @@ namespace ICSharpCode.XamlBinding return neededItems.ToList(); } - public static ICompletionItemList CreateListForContext(ITextEditor editor, XamlContext context) + public static ICompletionItemList CreateListForContext(ITextEditor editor, XamlCompletionContext context) { XamlCompletionItemList list = new XamlCompletionItemList(); ParseInformation info = ParserService.GetParseInformation(editor.FileName); @@ -267,18 +314,19 @@ namespace ICSharpCode.XamlBinding case XamlContextDescription.AtTag: list.Items.AddRange(standardElements); if (editor.Document.GetCharAt(editor.Caret.Offset - 1) == '.' || context.PressedKey == '.') { - var existing = Utils.GetListOfExistingAttributeNames(editor.Document.Text, Utils.GetParentElementStart(editor)); - list.Items.AddRange(CreateListForAttributeName(editor, info, new XamlExpressionContext(context.Path, null, false), existing).RemoveEvents()); + 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 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.Offset); - list.Items.AddRange(CreateListForAttributeName(editor, info, new XamlExpressionContext(context.Path, null, false), existingAttribs)); + var existingAttribs = Utils.GetListOfExistingAttributeNames(editor.Document.Text, editor.Caret.Line, editor.Caret.Column); + list.Items.AddRange(CreateListForAttributeName(context, existingAttribs)); QualifiedName last = context.Path.Elements[context.Path.Elements.Count - 1]; - TypeResolveResult trr = new XamlResolver().Resolve(new ExpressionResult(last.Name, new XamlExpressionContext(context.Path, null, false)), info, editor.Document.Text) as TypeResolveResult; + TypeResolveResult trr = new XamlResolver().Resolve(new ExpressionResult(last.Name, context), info, editor.Document.Text) as TypeResolveResult; if (trr != null && trr.ResolvedType != null && trr.ResolvedType.GetUnderlyingClass() != null) { if (trr.ResolvedType.GetUnderlyingClass().ClassInheritanceTree.Any(i => i.FullyQualifiedName == "System.Windows.DependencyObject")) { @@ -313,10 +361,10 @@ namespace ICSharpCode.XamlBinding return false; } - public static IEnumerable CreateMarkupExtensionInsight(XamlContext context, ParseInformation info, ITextEditor editor) + public static IEnumerable CreateMarkupExtensionInsight(XamlCompletionContext context, ParseInformation info, ITextEditor editor) { var markup = GetInnermostMarkup(context.AttributeValue.ExtensionValue); - var trr = ResolveMarkupExtensionType(markup, info, editor, context.Path); + var trr = ResolveMarkupExtensionType(markup, context); if (trr != null) { var ctors = trr.ResolvedType @@ -331,14 +379,14 @@ namespace ICSharpCode.XamlBinding } } - public static ICompletionItemList CreateMarkupExtensionCompletion(XamlContext context, ParseInformation info, ITextEditor editor) + public static ICompletionItemList CreateMarkupExtensionCompletion(XamlCompletionContext context, ParseInformation info, ITextEditor editor) { var list = new XamlCompletionItemList(); var path = XmlParser.GetActiveElementStartPathAtIndex(editor.Document.Text, editor.Caret.Offset); var markup = GetInnermostMarkup(context.AttributeValue.ExtensionValue); - var trr = ResolveMarkupExtensionType(markup, info, editor, path); + var trr = ResolveMarkupExtensionType(markup, context); if (trr == null) { list.Items.AddRange(CreateListOfMarkupExtensions(info, editor.Document.Text, editor.Caret.Line, editor.Caret.Column)); @@ -366,7 +414,7 @@ namespace ICSharpCode.XamlBinding } } - static void DoPositionalArgsCompletion(XamlCompletionItemList list, XamlContext context, TypeResolveResult trr, ParseInformation info, ITextEditor editor) + static void DoPositionalArgsCompletion(XamlCompletionItemList list, XamlCompletionContext context, TypeResolveResult trr, ParseInformation info, ITextEditor editor) { switch (trr.ResolvedType.FullyQualifiedName) { case "System.Windows.Markup.ArrayExtension": @@ -375,7 +423,7 @@ namespace ICSharpCode.XamlBinding 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, info, editor); + if (context.AttributeValue.ExtensionValue.PositionalArguments.Count <= 1) DoStaticExtensionCompletion(list, context); break; case "System.Windows.Markup.TypeExtension": if (context.AttributeValue.ExtensionValue.PositionalArguments.Count == 1 && context.PressedKey == ' ') break; @@ -421,7 +469,7 @@ namespace ICSharpCode.XamlBinding } } - public static IEnumerable MemberCompletion(ITextEditor editor, IReturnType type) + public static IEnumerable MemberCompletion(XamlCompletionContext context, IReturnType type) { if (type == null || type.GetUnderlyingClass() == null) yield break; @@ -442,24 +490,27 @@ namespace ICSharpCode.XamlBinding case ClassType.Delegate: IMethod invoker = c.Methods.Where(method => method.Name == "Invoke").FirstOrDefault(); if (invoker != null) { - var path = XmlParser.GetActiveElementStartPathAtIndex(editor.Document.Text, editor.Caret.Offset); - if (path != null && path.Elements.Count > 0) { - var item = path.Elements[path.Elements.Count - 1]; - string attribute = XmlParser.GetAttributeNameAtIndex(editor.Document.Text, editor.Caret.Offset); - var evt = ResolveAttribute(attribute, editor) as IEvent; + if (context.Path != null) { + var item = context.Path.Elements.LastOrDefault(); + var evt = ResolveAttribute(context.AttributeName, context) as IEvent; if (evt == null) break; - string prefix = Utils.GetXamlNamespacePrefix(editor.Document.Text, editor.Caret.Offset); - string name = Utils.GetAttributeValue(editor.Document.Text, editor.Caret.Offset, "name"); - if (string.IsNullOrEmpty(name)) - name = Utils.GetAttributeValue(editor.Document.Text, editor.Caret.Offset, (string.IsNullOrEmpty(prefix) ? "" : prefix + ":") + "name"); + int offset = XmlParser.GetActiveElementStartIndex(context.Editor.Document.Text, context.Editor.Caret.Offset); + + if (offset == -1) + break; + + var loc = context.Editor.Document.OffsetToPosition(offset); - Debug.Print("prefix: " + prefix + " name: " + name); + string prefix = Utils.GetXamlNamespacePrefix(context); + string name = Utils.GetAttributeValue(context.Editor.Document.Text, loc.Line, loc.Column + 1, "name"); + if (string.IsNullOrEmpty(name)) + name = Utils.GetAttributeValue(context.Editor.Document.Text, loc.Line, loc.Column + 1, (string.IsNullOrEmpty(prefix) ? "" : prefix + ":") + "name"); yield return new NewEventCompletionItem(evt, (string.IsNullOrEmpty(name)) ? item.Name : name); - foreach (var eventItem in CompletionDataHelper.AddMatchingEventHandlers(editor, invoker)) + foreach (var eventItem in CompletionDataHelper.AddMatchingEventHandlers(context.Editor, invoker)) yield return eventItem; } } @@ -480,42 +531,38 @@ namespace ICSharpCode.XamlBinding } } - static IEntity ResolveAttribute(string attribute, ITextEditor editor) + static IEntity ResolveAttribute(string attribute, XamlCompletionContext context) { XamlResolver resolver = new XamlResolver(); - - var path = XmlParser.GetActiveElementStartPathAtIndex(editor.Document.Text, editor.Caret.Offset); - var exp = new ExpressionResult(attribute, new XamlExpressionContext(path, attribute, false)); - var info = ParserService.GetParseInformation(editor.FileName); - - var mrr = resolver.Resolve(exp, info, editor.Document.Text) as MemberResolveResult; + var exp = new ExpressionResult(attribute, context); + var mrr = resolver.Resolve(exp, context.ParseInformation, context.Editor.Document.Text) as MemberResolveResult; return mrr.ResolvedMember; } - static void DoStaticExtensionCompletion(XamlCompletionItemList list, XamlContext context, ParseInformation info, ITextEditor editor) + static void 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.Path, info, editor) as TypeResolveResult; + var rr = ResolveStringValue(selItem.StringValue, context) as TypeResolveResult; if (rr != null) - list.Items.AddRange(MemberCompletion(editor, rr.ResolvedType)); + list.Items.AddRange(MemberCompletion(context, rr.ResolvedType)); } } else { if (selItem != null && selItem.IsString) { int index = selItem.StringValue.IndexOf('.'); string s = (index > -1) ? selItem.StringValue.Substring(0, index) : selItem.StringValue; - var rr = ResolveStringValue(s, context.Path, info, editor) as TypeResolveResult; + var rr = ResolveStringValue(s, context) as TypeResolveResult; if (rr != null) { - list.Items.AddRange(MemberCompletion(editor, rr.ResolvedType)); + list.Items.AddRange(MemberCompletion(context, rr.ResolvedType)); list.PreselectionLength = selItem.StringValue.Length - index - 1; list.SuggestedItem = list.Items.FirstOrDefault(item => item.Text.StartsWith(selItem.StringValue.Substring(index + 1), StringComparison.OrdinalIgnoreCase)); } else - DoStaticTypeCompletion(selItem, list, info, editor); + DoStaticTypeCompletion(selItem, list, context.ParseInformation, context.Editor); } else { - DoStaticTypeCompletion(selItem, list, info, editor); + DoStaticTypeCompletion(selItem, list, context.ParseInformation, context.Editor); } } } @@ -535,10 +582,10 @@ namespace ICSharpCode.XamlBinding } } - static ResolveResult ResolveStringValue(string value, XmlElementPath path, ParseInformation info, ITextEditor editor) + static ResolveResult ResolveStringValue(string value, XamlCompletionContext context) { var resolver = new XamlResolver(); - var rr = resolver.Resolve(new ExpressionResult(value, new XamlExpressionContext(path, string.Empty, false)), info, editor.Document.Text); + var rr = resolver.Resolve(new ExpressionResult(value, context), context.ParseInformation, context.Editor.Document.Text); return rr; } @@ -563,20 +610,19 @@ namespace ICSharpCode.XamlBinding return markup; } - public static TypeResolveResult ResolveMarkupExtensionType(MarkupExtensionInfo markup, ParseInformation info, ITextEditor editor, XmlElementPath path) + public static TypeResolveResult ResolveMarkupExtensionType(MarkupExtensionInfo markup, XamlCompletionContext context) { XamlResolver resolver = new XamlResolver(); - TypeResolveResult trr = resolver.Resolve(new ExpressionResult(markup.ExtensionType, new XamlExpressionContext(path, null, false)), info, editor.Document.Text) as TypeResolveResult; - if (trr == null) trr = resolver.Resolve(new ExpressionResult(markup.ExtensionType + "Extension", new XamlExpressionContext(path, null, false)), info, editor.Document.Text) as TypeResolveResult; + 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; return trr; } - public static TypeResolveResult ResolveType(string name, XamlContext context, ITextEditor editor) + public static TypeResolveResult ResolveType(string name, XamlCompletionContext context) { return new XamlResolver() - .Resolve(new ExpressionResult(name, new XamlExpressionContext(context.Path, null, false)), - ParserService.GetParseInformation(editor.FileName), editor.Document.Text) as TypeResolveResult; + .Resolve(new ExpressionResult(name, context), context.ParseInformation, context.Editor.Document.Text) as TypeResolveResult; } public static IEnumerable AddMatchingEventHandlers(ITextEditor editor, IMethod delegateInvoker) @@ -622,22 +668,9 @@ namespace ICSharpCode.XamlBinding return result; } - static XmlTextReader CreateReaderAtTarget(string fileContent, int caretLine, int caretColumn) - { - XmlTextReader r = new XmlTextReader(new StringReader(fileContent)); - - try { - r.WhitespaceHandling = WhitespaceHandling.Significant; - // move reader to correct position - while (r.Read() && !IsReaderAtTarget(r, caretLine, caretColumn)) { } - } catch (XmlException) {} - - return r; - } - static string GetPrefixForNamespace(string @namespace, string fileContent, int caretLine, int caretColumn) { - using (XmlTextReader r = CreateReaderAtTarget(fileContent, caretLine, caretColumn)) { + using (XmlTextReader r = Utils.CreateReaderAtTarget(fileContent, caretLine, caretColumn)) { foreach (var item in r.GetNamespacesInScope(XmlNamespaceScope.ExcludeXml)) { if (item.Value == @namespace) { return item.Key; @@ -650,7 +683,7 @@ namespace ICSharpCode.XamlBinding static IDictionary> GetClassesFromContext(ParseInformation parseInfo, string fileContent, int caretLine, int caretColumn) { - using (XmlTextReader r = CreateReaderAtTarget(fileContent, caretLine, caretColumn)) { + using (XmlTextReader r = Utils.CreateReaderAtTarget(fileContent, caretLine, caretColumn)) { IProjectContent pc = parseInfo.BestCompilationUnit.ProjectContent; var result = new Dictionary>(); @@ -665,7 +698,7 @@ namespace ICSharpCode.XamlBinding static List GetListOfAttachedProperties(ParseInformation parseInfo, string fileContent, int caretLine, int caretColumn, string[] existingItems) { - using (XmlTextReader r = CreateReaderAtTarget(fileContent, caretLine, caretColumn)) { + using (XmlTextReader r = Utils.CreateReaderAtTarget(fileContent, caretLine, caretColumn)) { List result = new List(); IProjectContent pc = parseInfo.BestCompilationUnit.ProjectContent; diff --git a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Utils.cs b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Utils.cs index daf78d900c..65a82f1c6e 100644 --- a/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Utils.cs +++ b/src/AddIns/BackendBindings/XamlBinding/XamlBinding/Utils.cs @@ -24,54 +24,25 @@ namespace ICSharpCode.XamlBinding /// public static class Utils { - public static bool HasMatchingEndTag(string tagname, string text, int offset) + internal static bool IsReaderAtTarget(XmlTextReader r, int line, int col) { - int index = XmlParser.GetActiveElementStartIndex(text, offset); - if (index == -1) + if (r.LineNumber > line) + return true; + else if (r.LineNumber == line) + return r.LinePosition >= col; + else return false; - - text = text.Substring(index); - - var path = XmlParser.GetActiveElementStartPathAtIndex(text, index); - - XmlReader reader = XmlTextReader.Create(new StringReader(text)); - int startTags = 0; - try { - while (reader.Read()) { - switch (reader.NodeType) { - case XmlNodeType.Element: - if (!reader.IsEmptyElement) - startTags++; - break; - case XmlNodeType.EndElement: - startTags--; - if (startTags == 0 && tagname == reader.Name) { - return true; - } - break; - } - } - } catch (XmlException e) { - Debug.Print(e.ToString()); - return false; - } - - return false; } - public static string GetAttributeValue(string text, int offset, string name) + public static string GetAttributeValue(string text, int line, int col, string name) { - text = SimplifyToSingleElement(text, offset, "Test"); - - if (text == null) - return null; - - XmlTextReader reader = new XmlTextReader(new StringReader(text)); - reader.XmlResolver = null; - - try { - reader.ReadToFollowing("Test"); + try { + XmlTextReader reader = new XmlTextReader(new StringReader(text)); + reader.XmlResolver = null; + while (reader.Read() && !IsReaderAtTarget(reader, line, col)) { } + + if (!reader.MoveToFirstAttribute()) return null; @@ -89,68 +60,6 @@ namespace ICSharpCode.XamlBinding return null; } - public static Dictionary GetXmlNamespacesForOffset(string fileContent, int offset) - { - if (fileContent == null) - throw new ArgumentNullException("fileContent"); - if (offset < 0 || offset > fileContent.Length) - throw new ArgumentOutOfRangeException("offset", offset, "Value must be between 0 and " + fileContent.Length); - - var map = new Dictionary(); - - int endIndex = fileContent.IndexOfAny(new char[] {'<', '>'}, offset); - if (endIndex > -1) - fileContent = fileContent.Substring(0, endIndex + 1); - - int lastWhiteSpacePos = fileContent.LastIndexOfAny(new char[] {' ', '\t', '\n', '\r'}); - - bool inDouble = false, inSingle = false; - - for (int i = 0; i < fileContent.Length; i++) { - if (fileContent[i] == '"' && !inSingle) - inDouble = !inDouble; - if (fileContent[i] == '\'' && !inDouble) - inSingle = !inSingle; - if (fileContent[i] == '>') { - int lastDelimiterPos = fileContent.Substring(0, i + 1).LastIndexOfAny(new char[] {'>', '/'}); - if (inDouble) { - fileContent.Insert(lastDelimiterPos, "\""); - i++; - inDouble = false; - } - if (inSingle) { - fileContent.Insert(lastDelimiterPos, "'"); - inSingle = false; - i++; - } - } - } - - fileContent = fileContent.Replace("", " ").Replace("<", " ").Replace("/>", " ").Replace(">", " ").Replace("\n", " ").Replace("\r", " ").Replace("\t", " "); - while (fileContent.Contains(" ")) - fileContent = fileContent.Replace(" ", " "); - - fileContent = fileContent.Replace("= \"", "=\""); - fileContent = fileContent.Replace(" =\"", "=\""); - - Debug.Print(fileContent); - - string[] data = fileContent.Split(' '); - - var filter1 = data.Where(s => s.StartsWith("xmlns")); - - foreach (string item in filter1) { - string[] parts = item.Split(new char[] {'='}, 2); - if (parts.Length == 2) { - if (map.ContainsKey(parts[0])) // replace namespace with new one - map.Remove(parts[0]); - map.Add(parts[0], parts[1].Trim('"', '\'')); - } - } - - return map; - } - public static int GetOffsetFromValueStart(string xaml, int offset) { if (xaml == null) @@ -166,29 +75,25 @@ namespace ICSharpCode.XamlBinding return offset - start - 1; } - public static string[] GetListOfExistingAttributeNames(string text, int offset) + public static string[] GetListOfExistingAttributeNames(string text, int line, int col) { List list = new List(); - text = SimplifyToSingleElement(text, offset, "Test"); - if (text == null) return list.ToArray(); - XmlReader reader = XmlTextReader.Create(new StringReader(text)); - - try { - reader.ReadToFollowing("Test"); - - if (!reader.MoveToFirstAttribute()) - return list.ToArray(); - - do { - LoggingService.Debug("name: " + reader.Name + " value: " + reader.Value); - list.Add(reader.Name); - } while (reader.MoveToNextAttribute()); - } catch (XmlException e) { - Debug.Print(e.ToString()); + using (XmlReader reader = CreateReaderAtTarget(text, line, col)) { + try { + if (!reader.MoveToFirstAttribute()) + return list.ToArray(); + + do { + LoggingService.Debug("name: " + reader.Name + " value: " + reader.Value); + list.Add(reader.Name); + } while (reader.MoveToNextAttribute()); + } catch (XmlException e) { + Debug.Print(e.ToString()); + } } foreach (var item in list) @@ -214,44 +119,13 @@ namespace ICSharpCode.XamlBinding } static char[] whitespace = new char[] {' ', '\t', '\n', '\r'}; - - static string SimplifyToSingleElement(string text, int offset, string name) - { - int index = XmlParser.GetActiveElementStartIndex(text, offset); - if (index == -1) return null; - index = text.IndexOfAny(whitespace, index); - if (index == -1) return null; - string newText = text.Substring(index); - int endIndex = newText.IndexOfAny(new char[] { '<', '>' }); - if (endIndex == -1) - endIndex = newText.Length; - newText = newText.Substring(0, endIndex).Trim(' ', '\t', '\n', '\r', '/'); - LoggingService.Debug("text: '" + newText + "'"); - if (!newText.EndsWith("\"") && newText.LastIndexOfAny(whitespace) > -1) { - newText = newText.Substring(0, newText.LastIndexOfAny(whitespace)); - } - - string namespaceDecls = ""; - - var list = Utils.GetXmlNamespacesForOffset(text, offset); - - foreach (var item in list) { - if (!Regex.IsMatch(text, item.Key + "\\s*=\\s*\"")) - namespaceDecls += item.Key + "=\"" + item.Value + "\" "; - } - - text = "<" + name + " " + newText + " " + namespaceDecls + " />"; - - return text; - } - public static string GetXamlNamespacePrefix(string xaml, int offset) + public static string GetXamlNamespacePrefix(XamlContext context) { - var list = Utils.GetXmlNamespacesForOffset(xaml, offset); - var item = list.FirstOrDefault(i => i.Value == CompletionDataHelper.XamlNamespace); - - if (item.Key.StartsWith("xmlns:", StringComparison.OrdinalIgnoreCase)) - return item.Key.Substring("xmlns:".Length); + var item = context.XmlnsDefinitions.FirstOrDefault(i => i.Value == CompletionDataHelper.XamlNamespace); + + if (item.Key != null) + return item.Key; return string.Empty; } @@ -273,6 +147,28 @@ namespace ICSharpCode.XamlBinding return interestingPart.LastIndexOf("