Browse Source

- Fixed bugs in XAML code completion

- Added Unit Tests

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@5006 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Siegfried Pammer 16 years ago
parent
commit
41471d9cc1
  1. 77
      src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/CodeCompletionTests.cs
  2. 323
      src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/GetMarkupExtensionAtPositionTests.cs
  3. 1
      src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/XamlBinding.Tests.csproj
  4. 24
      src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataHelper.cs
  5. 181
      src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionInfo.cs
  6. 29
      src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionParser.cs
  7. 1
      src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionToken.cs
  8. 9
      src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionTokenizer.cs
  9. 51
      src/AddIns/BackendBindings/XamlBinding/XamlBinding/Utils.cs
  10. 30
      src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlColorizer.cs

77
src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/CodeCompletionTests.cs

@ -19,6 +19,7 @@ namespace ICSharpCode.XamlBinding.Tests @@ -19,6 +19,7 @@ namespace ICSharpCode.XamlBinding.Tests
[RequiresSTA]
public class CodeCompletionTests : TextEditorBasedTests
{
#region CtrlSpace
[Test]
public void CtrlSpaceTest01()
{
@ -545,14 +546,14 @@ namespace ICSharpCode.XamlBinding.Tests @@ -545,14 +546,14 @@ namespace ICSharpCode.XamlBinding.Tests
}
[Test]
public void CtrlSpaceTest25()
public void CtrlSpaceTest23()
{
string fileHeader = @"<Window x:Class='ICSharpCode.XamlBinding.Tests.CompletionTestsBase'
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Window.Resources>
<Style TargetType='{x:Type Button}'>
<Setter Property='AllowDrop' Value'";
<Style TargetType='Button'>
<Setter Property='";
string fileFooter = @"'
</Style>
</Window.Resources>
@ -570,14 +571,14 @@ namespace ICSharpCode.XamlBinding.Tests @@ -570,14 +571,14 @@ namespace ICSharpCode.XamlBinding.Tests
}
[Test]
public void CtrlSpaceTest23()
public void CtrlSpaceTest24()
{
string fileHeader = @"<Window x:Class='ICSharpCode.XamlBinding.Tests.CompletionTestsBase'
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Window.Resources>
<Style TargetType='Button'>
<Setter Property='";
<Setter Property='AllowDrop' Value='";
string fileFooter = @"'
</Style>
</Window.Resources>
@ -595,14 +596,14 @@ namespace ICSharpCode.XamlBinding.Tests @@ -595,14 +596,14 @@ namespace ICSharpCode.XamlBinding.Tests
}
[Test]
public void CtrlSpaceTest24()
public void CtrlSpaceTest25()
{
string fileHeader = @"<Window x:Class='ICSharpCode.XamlBinding.Tests.CompletionTestsBase'
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Window.Resources>
<Style TargetType='Button'>
<Setter Property='AllowDrop' Value='";
<Style TargetType='{x:Type Button}'>
<Setter Property='AllowDrop' Value'";
string fileFooter = @"'
</Style>
</Window.Resources>
@ -619,6 +620,35 @@ namespace ICSharpCode.XamlBinding.Tests @@ -619,6 +620,35 @@ namespace ICSharpCode.XamlBinding.Tests
});
}
[Test]
public void CtrlSpaceTest26()
{
string fileHeader = @"<Window x:Class='ICSharpCode.XamlBinding.Tests.CompletionTestsBase'
xmlns='clr-namespace:System.Windows;assembly=PresentationFramework'
xmlns:c='clr-namespace:System.Windows.Controls;assembly=PresentationFramework'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
Title='Test' Height='300' Width='300'>
<c:Grid>
<c:Button AllowDrop='True' c:Grid.";
string fileFooter = @" />
</c:Grid>
</Window>";
TestCtrlSpace(fileHeader, fileFooter, true,
list => {
Assert.AreEqual(0, list.PreselectionLength);
Assert.IsNull(list.SuggestedItem);
Assert.IsTrue(list.Items.Any());
var items = list.Items.Select(item => item.Text).ToArray();
Assert.Contains("Column", items);
Assert.Contains("Row", items);
Assert.Contains("RowSpan", items);
Assert.Contains("ColumnSpan", items);
});
}
#endregion
#region KeyPress
[Test]
public void TypeAtValueEndingInSpace()
{
@ -722,7 +752,7 @@ namespace ICSharpCode.XamlBinding.Tests @@ -722,7 +752,7 @@ namespace ICSharpCode.XamlBinding.Tests
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Grid>
<Button ";
<Button";
string fileFooter = @"
</Grid>
</Window>";
@ -735,6 +765,32 @@ namespace ICSharpCode.XamlBinding.Tests @@ -735,6 +765,32 @@ namespace ICSharpCode.XamlBinding.Tests
});
}
[Test]
public void ElementAttributeDotPressTest05()
{
string fileHeader = @"<Window x:Class='ICSharpCode.XamlBinding.Tests.CompletionTestsBase'
xmlns='clr-namespace:System.Windows;assembly=PresentationFramework'
xmlns:c='clr-namespace:System.Windows.Controls;assembly=PresentationFramework'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
Title='Test' Height='300' Width='300'>
<c:Grid>
<c:Button AllowDrop='True' c:Grid";
string fileFooter = @" />
</c:Grid>
</Window>";
TestKeyPress(fileHeader, fileFooter, '.', CodeCompletionKeyPressResult.Completed,
list => {
Assert.AreEqual(0, list.PreselectionLength);
Assert.IsNull(list.SuggestedItem);
Assert.IsTrue(list.Items.Any());
var items = list.Items.Select(item => item.Text).ToArray();
Assert.Contains("Column", items);
Assert.Contains("ColumnSpan", items);
Assert.Contains("Row", items);
});
}
[Test]
public void LowerThanPressedTest()
{
@ -817,7 +873,6 @@ namespace ICSharpCode.XamlBinding.Tests @@ -817,7 +873,6 @@ namespace ICSharpCode.XamlBinding.Tests
TestKeyPress(fileHeader, fileFooter, 'a', CodeCompletionKeyPressResult.None, list => {});
TestKeyPress(fileHeader, fileFooter, '<', CodeCompletionKeyPressResult.Completed, list => {});
}
#endregion
}
}

323
src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/GetMarkupExtensionAtPositionTests.cs

@ -0,0 +1,323 @@ @@ -0,0 +1,323 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Siegfried Pammer" email="siegfriedpammer@gmail.com" />
// <version>$Revision$</version>
// </file>
using System;
using NUnit.Framework;
namespace ICSharpCode.XamlBinding.Tests
{
[TestFixture]
public class GetMarkupExtensionAtPositionTests
{
[Test]
public void EmptyTest()
{
string markup = "{x:Type}";
MarkupExtensionInfo info = new MarkupExtensionInfo() {
ExtensionType = "x:Type",
StartOffset = 0,
EndOffset = 8
};
RunParseTest(markup, info);
RunDetectionTest(markup, 5, info);
}
[Test]
public void SimpleTest()
{
string markup = "{x:Type Button}";
MarkupExtensionInfo info = new MarkupExtensionInfo() {
ExtensionType = "x:Type",
StartOffset = 0,
EndOffset = 15
};
info.PositionalArguments.Add(new AttributeValue("Button") { StartOffset = 8 });
RunParseTest(markup, info);
RunDetectionTest(markup, 10, info);
}
[Test]
public void SimplePosArgTest()
{
string markup = "{x:Type Button, Button2}";
MarkupExtensionInfo info = new MarkupExtensionInfo() {
ExtensionType = "x:Type",
StartOffset = 0,
EndOffset = 24
};
info.PositionalArguments.Add(new AttributeValue("Button") { StartOffset = 8 });
info.PositionalArguments.Add(new AttributeValue("Button2") { StartOffset = 16 });
RunParseTest(markup, info);
RunDetectionTest(markup, 23, info);
}
[Test]
public void SimpleNamedArgTest()
{
string markup = "{x:Type Type=Button}";
MarkupExtensionInfo info = new MarkupExtensionInfo() {
ExtensionType = "x:Type",
StartOffset = 0,
EndOffset = 20
};
info.NamedArguments.Add("Type", new AttributeValue("Button") { StartOffset = 13 });
RunParseTest(markup, info);
RunDetectionTest(markup, 0, info);
}
[Test]
public void SimpleNamedArgTest1()
{
string markup = "{x:Type Type=Button, a=b}";
MarkupExtensionInfo info = new MarkupExtensionInfo() {
ExtensionType = "x:Type",
StartOffset = 0,
EndOffset = 25
};
info.NamedArguments.Add("Type", new AttributeValue("Button") { StartOffset = 13 });
info.NamedArguments.Add("a", new AttributeValue("b") { StartOffset = 23 });
RunParseTest(markup, info);
RunDetectionTest(markup, 20, info);
}
[Test]
public void PosAndNamedArgTest()
{
string markup = "{x:Type xy, Type=Button, a=b}";
MarkupExtensionInfo info = new MarkupExtensionInfo() {
ExtensionType = "x:Type",
StartOffset = 0,
EndOffset = 29
};
info.PositionalArguments.Add(new AttributeValue("xy") { StartOffset = 8 });
info.NamedArguments.Add("Type", new AttributeValue("Button") { StartOffset = 17 });
info.NamedArguments.Add("a", new AttributeValue("b") { StartOffset = 27 });
RunParseTest(markup, info);
RunDetectionTest(markup, 29, info);
}
[Test]
public void MissingColonTest()
{
string markup = "{x:Type xy Type=Button, a=b}";
MarkupExtensionInfo info = new MarkupExtensionInfo() {
ExtensionType = "x:Type",
StartOffset = 0,
EndOffset = 28
};
info.PositionalArguments.Add(new AttributeValue("xy") { StartOffset = 8 });
info.NamedArguments.Add("Type", new AttributeValue("Button") { StartOffset = 16 });
info.NamedArguments.Add("a", new AttributeValue("b") { StartOffset = 26 });
RunParseTest(markup, info);
}
[Test]
public void MissingColonTest2()
{
string markup = "{x:Type xy Type=Button a=b}";
MarkupExtensionInfo info = new MarkupExtensionInfo() {
ExtensionType = "x:Type",
StartOffset = 0,
EndOffset = 27
};
info.PositionalArguments.Add(new AttributeValue("xy") { StartOffset = 8 });
info.NamedArguments.Add("Type", new AttributeValue("Button") { StartOffset = 16 });
info.NamedArguments.Add("a", new AttributeValue("b") { StartOffset = 25 });
RunParseTest(markup, info);
}
[Test]
public void NestedTest1()
{
string markup = "{bla {bla2}}";
MarkupExtensionInfo info = new MarkupExtensionInfo() {
ExtensionType = "bla",
StartOffset = 0,
EndOffset = 12
};
info.PositionalArguments.Add(new AttributeValue(new MarkupExtensionInfo() { ExtensionType = "bla2", StartOffset = 5, EndOffset = 11 }) { StartOffset = 5 });
RunParseTest(markup, info);
}
[Test]
public void NestedTest2()
{
string markup = "{bla a, {bla2}}";
MarkupExtensionInfo info = new MarkupExtensionInfo() {
ExtensionType = "bla",
StartOffset = 0,
EndOffset = 15
};
info.PositionalArguments.Add(new AttributeValue("a") { StartOffset = 5 });
info.PositionalArguments.Add(
new AttributeValue(
new MarkupExtensionInfo() {
ExtensionType = "bla2",
StartOffset = 8,
EndOffset = 14
}
) {
StartOffset = 8
}
);
RunParseTest(markup, info);
}
[Test]
public void NestedTest3()
{
string markup = "{bla a, {bla2}, b, {bla3 {a}, b}}";
MarkupExtensionInfo info = new MarkupExtensionInfo() {
ExtensionType = "bla",
StartOffset = 0,
EndOffset = 33
};
info.PositionalArguments.Add(new AttributeValue("a") { StartOffset = 5 });
info.PositionalArguments.Add(
new AttributeValue(
new MarkupExtensionInfo() {
ExtensionType = "bla2",
StartOffset = 8,
EndOffset = 14
}
) {
StartOffset = 8
}
);
info.PositionalArguments.Add(new AttributeValue("b") { StartOffset = 16 });
var nested2 = new MarkupExtensionInfo() {
ExtensionType = "bla3",
StartOffset = 19,
EndOffset = 32
};
nested2.PositionalArguments.Add(
new AttributeValue(
new MarkupExtensionInfo() {
ExtensionType = "a",
StartOffset = 25,
EndOffset = 28
}
) {
StartOffset = 25
}
);
nested2.PositionalArguments.Add(new AttributeValue("b") { StartOffset = 30 });
info.PositionalArguments.Add(
new AttributeValue(nested2) {
StartOffset = 19
}
);
RunParseTest(markup, info);
}
[Test]
public void NestedTest4()
{
string markup = "{bla a, x={bla3 {a}, b={c}, d=e}, y=z}";
MarkupExtensionInfo info = new MarkupExtensionInfo() {
ExtensionType = "bla",
StartOffset = 0,
EndOffset = 38
};
info.PositionalArguments.Add(new AttributeValue("a") { StartOffset = 5 });
//x={bla3 {a}, b={c}, d=e}
MarkupExtensionInfo inner = new MarkupExtensionInfo() {
ExtensionType = "bla3",
StartOffset = 10,
EndOffset = 32
};
MarkupExtensionInfo smallInner = new MarkupExtensionInfo() {
ExtensionType = "a",
StartOffset = 16,
EndOffset = 19
};
inner.PositionalArguments.Add(new AttributeValue(smallInner) { StartOffset = 16 });
MarkupExtensionInfo smallInner2 = new MarkupExtensionInfo() {
ExtensionType = "c",
StartOffset = 23,
EndOffset = 26
};
inner.NamedArguments.Add("b", new AttributeValue(smallInner2) { StartOffset = 23 });
inner.NamedArguments.Add("d", new AttributeValue("e") { StartOffset = 30 });
info.NamedArguments.Add("x", new AttributeValue(inner) { StartOffset = 10 });
info.NamedArguments.Add("y", new AttributeValue("z") { StartOffset = 36 });
RunParseTest(markup, info);
RunDetectionTest(markup, 9, info);
RunDetectionTest(markup, 11, inner);
RunDetectionTest(markup, 17, smallInner);
RunDetectionTest(markup, 19, inner);
RunDetectionTest(markup, 32, info);
}
static void RunDetectionTest(string markup, int offset, MarkupExtensionInfo expectedResult)
{
MarkupExtensionInfo data = MarkupExtensionParser.Parse(markup);
MarkupExtensionInfo result = Utils.GetMarkupExtensionAtPosition(data, offset);
Assert.AreEqual(expectedResult, result);
}
static void RunParseTest(string markup, MarkupExtensionInfo expectedResult)
{
MarkupExtensionInfo data = MarkupExtensionParser.Parse(markup);
Assert.AreEqual(expectedResult, data);
}
}
}

1
src/AddIns/BackendBindings/XamlBinding/XamlBinding.Tests/XamlBinding.Tests.csproj

@ -71,6 +71,7 @@ @@ -71,6 +71,7 @@
<SubType>Code</SubType>
</Compile>
<Compile Include="ExtensionsTests.cs" />
<Compile Include="GetMarkupExtensionAtPositionTests.cs" />
<Compile Include="MockTextEditor.cs" />
<Compile Include="ParserTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

24
src/AddIns/BackendBindings/XamlBinding/XamlBinding/CompletionDataHelper.cs

@ -502,15 +502,17 @@ namespace ICSharpCode.XamlBinding @@ -502,15 +502,17 @@ namespace ICSharpCode.XamlBinding
ns = word.Substring(0, pos);
string element = word.Substring(pos + 1, word.Length - pos - 1);
string className = element;
string className = word;
int propertyStart = element.IndexOf('.');
if (propertyStart != -1)
className = element.Substring(0, propertyStart).TrimEnd('.');
if (propertyStart != -1) {
element = element.Substring(0, propertyStart).TrimEnd('.');
className = className.Substring(0, propertyStart + pos + 1).TrimEnd('.');
}
TypeResolveResult trr = XamlResolver.Resolve(className, context) as TypeResolveResult;
IClass typeClass = (trr != null && trr.ResolvedType != null) ? trr.ResolvedType.GetUnderlyingClass() : null;
if (typeClass != null && typeClass.HasAttached(true, true))
list.Items.AddRange(GetListOfAttached(context, className, ns, true, true));
list.Items.AddRange(GetListOfAttached(context, element, ns, true, true));
} else {
QualifiedNameWithLocation last = context.ActiveElement.ToQualifiedName();
TypeResolveResult trr = XamlResolver.Resolve(last.Name, context) as TypeResolveResult;
@ -552,7 +554,7 @@ namespace ICSharpCode.XamlBinding @@ -552,7 +554,7 @@ namespace ICSharpCode.XamlBinding
public static IEnumerable<IInsightItem> CreateMarkupExtensionInsight(XamlCompletionContext context)
{
var markup = Utils.GetMarkupExtensionAtPosition(context.AttributeValue.ExtensionValue, context.Editor.Caret.Offset);
var markup = Utils.GetMarkupExtensionAtPosition(context.AttributeValue.ExtensionValue, context.ValueStartOffset);
var type = ResolveType(markup.ExtensionType, context) ?? ResolveType(markup.ExtensionType + "Extension", context);
if (type != null) {
@ -568,12 +570,12 @@ namespace ICSharpCode.XamlBinding @@ -568,12 +570,12 @@ namespace ICSharpCode.XamlBinding
public static ICompletionItemList CreateMarkupExtensionCompletion(XamlCompletionContext context)
{
var list = new XamlCompletionItemList(context);
string visibleValue = context.RawAttributeValue.Substring(0, context.ValueStartOffset + 1);
string visibleValue = context.RawAttributeValue.Substring(0, Utils.MinMax(context.ValueStartOffset, 0, context.RawAttributeValue.Length));
if (context.PressedKey == '=')
visibleValue += "=";
context.RawAttributeValue = visibleValue;
context.AttributeValue = MarkupExtensionParser.ParseValue(visibleValue);
var markup = Utils.GetInnermostMarkupExtensionInfo(context.AttributeValue.ExtensionValue);
// context.RawAttributeValue = visibleValue;
// context.AttributeValue = MarkupExtensionParser.ParseValue(visibleValue);
var markup = Utils.GetMarkupExtensionAtPosition(context.AttributeValue.ExtensionValue, context.ValueStartOffset);
var type = ResolveType(markup.ExtensionType, context) ?? ResolveType(markup.ExtensionType + "Extension", context);
if (type == null) {
@ -627,7 +629,7 @@ namespace ICSharpCode.XamlBinding @@ -627,7 +629,7 @@ namespace ICSharpCode.XamlBinding
case "System.Windows.Markup.TypeExtension":
if (context.AttributeValue.ExtensionValue.PositionalArguments.Count <= 1) {
list.Items.AddRange(GetClassesFromContext(context).FlattenToList());
AttributeValue selItem = Utils.GetInnermostMarkupExtensionInfo(context.AttributeValue.ExtensionValue)
AttributeValue selItem = Utils.GetMarkupExtensionAtPosition(context.AttributeValue.ExtensionValue, context.ValueStartOffset)
.PositionalArguments.LastOrDefault();
string word = context.Editor.GetWordBeforeCaret().TrimEnd();
if (selItem != null && selItem.IsString && word == selItem.StringValue) {
@ -1058,7 +1060,7 @@ namespace ICSharpCode.XamlBinding @@ -1058,7 +1060,7 @@ namespace ICSharpCode.XamlBinding
static bool DoStaticExtensionCompletion(XamlCompletionItemList list, XamlCompletionContext context)
{
AttributeValue selItem = Utils.GetInnermostMarkupExtensionInfo(context.AttributeValue.ExtensionValue)
AttributeValue selItem = Utils.GetMarkupExtensionAtPosition(context.AttributeValue.ExtensionValue, context.ValueStartOffset)
.PositionalArguments.LastOrDefault();
if (context.PressedKey == '.') {
if (selItem != null && selItem.IsString) {

181
src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionInfo.cs

@ -12,16 +12,18 @@ using System.Linq; @@ -12,16 +12,18 @@ using System.Linq;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.XmlEditor;
using System.Text;
namespace ICSharpCode.XamlBinding
{
public class MarkupExtensionInfo
public class MarkupExtensionInfo : IEquatable<MarkupExtensionInfo>
{
public string ExtensionType { get; set; }
public IList<AttributeValue> PositionalArguments { get; private set; }
public IDictionary<string, AttributeValue> NamedArguments { get; private set; }
public int StartOffset { get; set; }
public int EndOffset { get; set; }
public MarkupExtensionInfo()
: this(string.Empty, new List<AttributeValue>(), new Dictionary<string, AttributeValue>(StringComparer.OrdinalIgnoreCase))
@ -34,15 +36,125 @@ namespace ICSharpCode.XamlBinding @@ -34,15 +36,125 @@ namespace ICSharpCode.XamlBinding
this.PositionalArguments = posArgs;
this.NamedArguments = namedArgs;
}
public override bool Equals(object obj)
{
return Equals(obj as MarkupExtensionInfo);
}
public override int GetHashCode()
{
unchecked {
int hash = ExtensionType.GetHashCode() ^
StartOffset.GetHashCode() ^
EndOffset.GetHashCode();
foreach (var value in PositionalArguments)
hash ^= value.GetHashCode();
foreach (var pair in NamedArguments)
hash = hash ^ pair.Key.GetHashCode() ^ pair.Value.GetHashCode();
return hash;
}
}
public bool Equals(MarkupExtensionInfo other)
{
if (ReferenceEquals(other, null))
return false;
if (other.ExtensionType != ExtensionType)
return false;
if (other.StartOffset != StartOffset)
return false;
if (other.EndOffset != EndOffset)
return false;
if (other.NamedArguments.Count != NamedArguments.Count)
return false;
if (other.PositionalArguments.Count != PositionalArguments.Count)
return false;
for (int i = 0; i < PositionalArguments.Count; i++) {
if (!PositionalArguments[i].Equals(other.PositionalArguments[i]))
return false;
}
List<KeyValuePair<string, AttributeValue>> myItems = NamedArguments.ToList();
List<KeyValuePair<string, AttributeValue>> otherItems = other.NamedArguments.ToList();
for (int i = 0; i < myItems.Count; i++) {
if (myItems[i].Key != otherItems[i].Key)
return false;
if (!myItems[i].Value.Equals(otherItems[i].Value))
return false;
}
return true;
}
public static bool operator ==(MarkupExtensionInfo lhs, MarkupExtensionInfo rhs)
{
if (object.ReferenceEquals(lhs, rhs))
return true;
if (((object)lhs) == null)
return false;
return lhs.Equals(rhs);
}
public static bool operator !=(MarkupExtensionInfo lhs, MarkupExtensionInfo rhs)
{
return !(lhs == rhs);
}
public override string ToString()
{
StringBuilder builder = new StringBuilder("{");
foreach (var pos in PositionalArguments)
builder.Append(pos + ",");
builder.Append("}");
string posArgs = builder.ToString();
builder = new StringBuilder("{");
foreach (var pair in NamedArguments)
builder.Append(pair.Key + "=" + pair.Value + ",");
builder.Append("}");
string namedArgs = builder.ToString();
return string.Format("[MarkupExtensionInfo Type={0}, Start={1}, End={2}, Pos={3}, Named={4}]",
ExtensionType, StartOffset, EndOffset, posArgs, namedArgs);
}
}
public class AttributeValue
public class AttributeValue : IEquatable<AttributeValue>
{
string stringValue;
MarkupExtensionInfo extensionValue;
public int StartOffset { get; set; }
public int EndOffset {
get {
if (IsString)
return StartOffset + StringValue.Length;
return extensionValue.EndOffset;
}
}
public bool IsString {
get { return stringValue != null; }
}
@ -70,5 +182,70 @@ namespace ICSharpCode.XamlBinding @@ -70,5 +182,70 @@ namespace ICSharpCode.XamlBinding
this.extensionValue = value;
}
public override bool Equals(object obj)
{
if (obj is string)
return Equals(obj as string);
if (obj is AttributeValue)
return Equals(obj as AttributeValue);
return false;
}
public override int GetHashCode()
{
int hash = StartOffset.GetHashCode() ^ EndOffset.GetHashCode();
if (IsString)
return hash ^ StringValue.GetHashCode();
return hash ^ ExtensionValue.GetHashCode();
}
public bool Equals(AttributeValue other)
{
if (ReferenceEquals(other, null))
return false;
if (IsString != other.IsString)
return false;
if (StartOffset != other.StartOffset)
return false;
if (EndOffset != other.EndOffset)
return false;
if (IsString)
return other.StringValue == StringValue;
else
return other.ExtensionValue == extensionValue;
}
public static bool operator ==(AttributeValue lhs, AttributeValue rhs)
{
if (object.ReferenceEquals(lhs, rhs))
return true;
if (((object)rhs) == null)
return false;
return rhs.Equals(lhs);
}
public static bool operator !=(AttributeValue lhs, AttributeValue rhs)
{
return !(lhs == rhs);
}
public override string ToString()
{
if (IsString)
return string.Format("[AttributeValue Start={1}, End={2}, String={0}]", StringValue, StartOffset, EndOffset);
return string.Format("[AttributeValue Start={1}, End={2}, Extension={0}]", ExtensionValue, StartOffset, EndOffset);
}
}
}

29
src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionParser.cs

@ -12,10 +12,14 @@ namespace ICSharpCode.XamlBinding @@ -12,10 +12,14 @@ namespace ICSharpCode.XamlBinding
public static class MarkupExtensionParser
{
public static MarkupExtensionInfo Parse(string text)
{
return Parse(text, 0);
}
static MarkupExtensionInfo Parse(string text, int offset)
{
var info = new MarkupExtensionInfo();
string argumentName = null;
int namedArgsStart = 0;
MarkupExtensionTokenizer tokenizer = new MarkupExtensionTokenizer(text);
MarkupExtensionToken token = null;
@ -25,36 +29,41 @@ namespace ICSharpCode.XamlBinding @@ -25,36 +29,41 @@ namespace ICSharpCode.XamlBinding
while (token.Kind != MarkupExtensionTokenKind.EndOfFile) {
switch (token.Kind) {
case MarkupExtensionTokenKind.OpenBrace:
info.StartOffset = token.StartOffset + offset;
break;
case MarkupExtensionTokenKind.CloseBrace:
info.EndOffset = token.EndOffset + offset;
break;
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.TryAddNamedArgument(argumentName, new AttributeValue(string.Empty));
info.TryAddNamedArgument(argumentName, ParseValue("", token.EndOffset + offset));
argumentName = token.Value;
namedArgsStart = token.StartOffset;
break;
case MarkupExtensionTokenKind.String:
if (argumentName != null) {
info.TryAddNamedArgument(argumentName, ParseValue(token.Value, namedArgsStart));
info.TryAddNamedArgument(argumentName, ParseValue(token.Value, token.StartOffset + offset));
argumentName = null;
} else {
info.PositionalArguments.Add(ParseValue(token.Value, token.StartOffset));
info.PositionalArguments.Add(ParseValue(token.Value, token.StartOffset + offset));
}
break;
}
token = tokenizer.NextToken();
}
} catch (MarkupExtensionParseException) {
// ignore parser errors
} finally {
if (token != null && argumentName != null)
info.TryAddNamedArgument(argumentName, ParseValue(token.Value, namedArgsStart));
info.TryAddNamedArgument(argumentName, ParseValue(token.Value, token.StartOffset + offset));
}
return info;
return info;
}
static void TryAddNamedArgument(this MarkupExtensionInfo info, string name, AttributeValue value)
@ -69,13 +78,13 @@ namespace ICSharpCode.XamlBinding @@ -69,13 +78,13 @@ namespace ICSharpCode.XamlBinding
return ParseValue(text, 0);
}
public static AttributeValue ParseValue(string text, int offset)
static AttributeValue ParseValue(string text, int offset)
{
if (string.IsNullOrEmpty(text))
return new AttributeValue(string.Empty) { StartOffset = offset };
if (text.StartsWith("{", StringComparison.OrdinalIgnoreCase))
return new AttributeValue(Parse(text)) { StartOffset = offset };
return new AttributeValue(Parse(text, offset)) { StartOffset = offset };
else
return new AttributeValue(text) { StartOffset = offset };
}

1
src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionToken.cs

@ -15,6 +15,7 @@ namespace ICSharpCode.XamlBinding @@ -15,6 +15,7 @@ namespace ICSharpCode.XamlBinding
public string Value { get; private set; }
public int StartOffset { get; set; }
public int EndOffset { get; set; }
public MarkupExtensionToken(MarkupExtensionTokenKind kind, string value)
{

9
src/AddIns/BackendBindings/XamlBinding/XamlBinding/MarkupExtensionTokenizer.cs

@ -48,12 +48,12 @@ namespace ICSharpCode.XamlBinding @@ -48,12 +48,12 @@ namespace ICSharpCode.XamlBinding
if (tokens.Count > 0)
return tokens.Dequeue();
else
return new MarkupExtensionToken(MarkupExtensionTokenKind.EndOfFile, "");
return new MarkupExtensionToken(MarkupExtensionTokenKind.EndOfFile, "") { StartOffset = pos, EndOffset = pos };
}
void AddToken(MarkupExtensionTokenKind kind, string val)
{
tokens.Enqueue(new MarkupExtensionToken(kind, val) { StartOffset = startPos });
tokens.Enqueue(new MarkupExtensionToken(kind, val) { StartOffset = startPos, EndOffset = startPos + val.Length });
}
void ParseBeginning()
@ -73,24 +73,21 @@ namespace ICSharpCode.XamlBinding @@ -73,24 +73,21 @@ namespace ICSharpCode.XamlBinding
{
ConsumeWhitespace();
if (pos < text.Length) {
startPos = pos;
switch (text[pos]) {
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;
}

51
src/AddIns/BackendBindings/XamlBinding/XamlBinding/Utils.cs

@ -24,28 +24,6 @@ namespace ICSharpCode.XamlBinding @@ -24,28 +24,6 @@ namespace ICSharpCode.XamlBinding
/// </summary>
public static class Utils
{
public static MarkupExtensionInfo GetInnermostMarkupExtensionInfo(MarkupExtensionInfo info)
{
var lastNamed = info.NamedArguments.LastOrDefault();
var lastPositional = info.PositionalArguments.LastOrDefault();
if (lastNamed.Value != null) {
if (lastNamed.Value.IsString)
return info;
return GetInnermostMarkupExtensionInfo(lastNamed.Value.ExtensionValue);
} else {
if (lastPositional != null) {
if (lastPositional.IsString)
return info;
return GetInnermostMarkupExtensionInfo(lastPositional.ExtensionValue);
}
}
return info;
}
public static int MinMax(int value, int lower, int upper)
{
return Math.Min(Math.Max(value, lower), upper);
@ -102,39 +80,44 @@ namespace ICSharpCode.XamlBinding @@ -102,39 +80,44 @@ namespace ICSharpCode.XamlBinding
/// An AttributeValue, if at the offset is a positional argument. <br />
/// A KeyValuePair&lt;string, AttributeValue>, if at the offset is a named argument.
/// </returns>
/// <remarks>offset != Caret.Offset, but offset == ValueStartOffset</remarks>
public static object GetMarkupDataAtPosition(MarkupExtensionInfo info, int offset)
{
object previous = info.ExtensionType;
int endOffset = info.StartOffset + info.ExtensionType.Length;
foreach (var item in info.PositionalArguments) {
if (item.StartOffset > offset)
break;
previous = item.IsString ? item : GetMarkupDataAtPosition(item.ExtensionValue, offset - item.StartOffset);
if (item.StartOffset <= offset && offset <= item.EndOffset)
previous = item.IsString ? item : GetMarkupDataAtPosition(item.ExtensionValue, offset);
endOffset = item.EndOffset;
}
foreach (var pair in info.NamedArguments) {
if (pair.Value.StartOffset > offset)
break;
previous = pair.Value.IsString ? pair : GetMarkupDataAtPosition(pair.Value.ExtensionValue, offset - pair.Value.StartOffset);
if (pair.Value.StartOffset <= offset && offset <= pair.Value.EndOffset)
previous = pair.Value.IsString ? pair : GetMarkupDataAtPosition(pair.Value.ExtensionValue, offset);
else if (endOffset <= offset && offset <= pair.Value.StartOffset)
previous = pair;
endOffset = pair.Value.EndOffset;
}
return previous;
}
/// <remarks>offset != Caret.Offset, but offset == ValueStartOffset</remarks>
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);
if (item.StartOffset < offset && offset < item.EndOffset)
tmp = item.IsString ? tmp : GetMarkupExtensionAtPosition(item.ExtensionValue, offset);
}
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);
if (pair.Value.StartOffset < offset && offset < pair.Value.EndOffset)
tmp = pair.Value.IsString ? tmp : GetMarkupExtensionAtPosition(pair.Value.ExtensionValue, offset);
}
return tmp;

30
src/AddIns/BackendBindings/XamlBinding/XamlBinding/XamlColorizer.cs

@ -35,22 +35,20 @@ namespace ICSharpCode.XamlBinding @@ -35,22 +35,20 @@ namespace ICSharpCode.XamlBinding
public sealed class HighlightTask {
// input
public string FileName { get; private set; }
public string LineText { get; private set; }
public int LineNumber { get; private set; }
public int Offset { get; private set; }
readonly string fileName;
readonly string lineText;
readonly int offset;
TextView textView;
ITextBuffer snapshot;
public HighlightTask(ITextEditor editor, DocumentLine currentLine, TextView textView)
{
this.FileName = editor.FileName;
this.fileName = editor.FileName;
this.textView = textView;
this.snapshot = editor.Document.CreateSnapshot();
this.LineNumber = currentLine.LineNumber;
this.LineText = currentLine.Text;
this.Offset = currentLine.Offset;
this.lineText = textView.Document.GetText(currentLine);
this.offset = currentLine.Offset;
this.task = new Task(Process);
}
@ -120,7 +118,7 @@ namespace ICSharpCode.XamlBinding @@ -120,7 +118,7 @@ namespace ICSharpCode.XamlBinding
void InvokeRedraw()
{
task.Wait();
textView.Redraw(this.Offset, this.LineText.Length, DispatcherPriority.Background);
textView.Redraw(this.offset, this.lineText.Length, DispatcherPriority.Background);
}
IEnumerable<HighlightingInfo> GetInfo()
@ -129,12 +127,12 @@ namespace ICSharpCode.XamlBinding @@ -129,12 +127,12 @@ namespace ICSharpCode.XamlBinding
XamlContext context = null;
do {
if (index + 1 >= LineText.Length)
if (index + 1 >= lineText.Length)
break;
index = LineText.IndexOfAny(index + 1, '=', '.');
index = lineText.IndexOfAny(index + 1, '=', '.');
if (index > -1) {
context = CompletionDataHelper.ResolveContext(snapshot, FileName, Offset + index);
context = CompletionDataHelper.ResolveContext(snapshot, fileName, offset + index);
if (context.ActiveElement == null || context.InAttributeValueOrMarkupExtension || context.InCommentOrCData)
continue;
@ -152,21 +150,21 @@ namespace ICSharpCode.XamlBinding @@ -152,21 +150,21 @@ namespace ICSharpCode.XamlBinding
continue;
propertyName = token.Substring(propertyNameIndex + 1);
startIndex = LineText.IndexOf(propertyName, index, StringComparison.Ordinal);
startIndex = lineText.IndexOf(propertyName, index, StringComparison.Ordinal);
break;
case XamlContextDescription.InTag:
if (LineText[index] == '.' || context.Attribute == null)
if (lineText[index] == '.' || context.Attribute == null)
continue;
token = propertyName = context.Attribute.Name;
startIndex = LineText.LastIndexOf(propertyName, index, StringComparison.Ordinal);
startIndex = lineText.LastIndexOf(propertyName, index, StringComparison.Ordinal);
break;
default:
continue;
}
if (startIndex > -1) {
yield return new HighlightingInfo(token, startIndex, startIndex + propertyName.Length, Offset, context);
yield return new HighlightingInfo(token, startIndex, startIndex + propertyName.Length, offset, context);
}
}
} while (index > -1);

Loading…
Cancel
Save