diff --git a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/BooProject.cs b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/BooProject.cs
index 8905c4013f..fb1d739860 100644
--- a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/BooProject.cs
+++ b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/BooProject.cs
@@ -102,5 +102,12 @@ namespace Grunwald.BooBinding
return BooAmbience.Instance;
}
}
+
+ [Browsable(false)]
+ public bool Ducky {
+ get {
+ return GetProperty("Ducky", false);
+ }
+ }
}
}
diff --git a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/BooResolver.cs b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/BooResolver.cs
index ac706d6028..317bd23394 100644
--- a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/BooResolver.cs
+++ b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/BooResolver.cs
@@ -63,6 +63,19 @@ namespace Grunwald.BooBinding.CodeCompletion
return cu;
}
}
+
+ ///
+ /// Gets if duck typing is enabled for the Boo project.
+ ///
+ public bool IsDucky {
+ get {
+ BooProject p = pc.Project as BooProject;
+ if (p != null)
+ return p.Ducky;
+ else
+ return false;
+ }
+ }
#endregion
#region Initialization
diff --git a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/VariableLookupVisitor.cs b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/VariableLookupVisitor.cs
index fe3d985e40..2125022648 100644
--- a/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/VariableLookupVisitor.cs
+++ b/src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/CodeCompletion/VariableLookupVisitor.cs
@@ -26,25 +26,27 @@ namespace Grunwald.BooBinding.CodeCompletion
DeclarationFound(node.Declaration.Name, node.Declaration.Type, node.Initializer, node.LexicalInfo);
}
- LexicalInfo GetEndSourceLocation(Node node)
+ SourceLocation GetEndSourceLocation(Node node)
{
- if (node.LexicalInfo.IsValid) return node.LexicalInfo;
+ if (node.EndSourceLocation.IsValid) return node.EndSourceLocation;
if (node is CallableBlockExpression) {
return GetEndSourceLocation((node as CallableBlockExpression).Body);
+ } else if (node is ForStatement) {
+ return GetEndSourceLocation((node as ForStatement).Block);
} else if (node is Block) {
StatementCollection st = (node as Block).Statements;
if (st.Count > 0) {
return GetEndSourceLocation(st[st.Count - 1]);
}
}
- return node.LexicalInfo;
+ return node.EndSourceLocation;
}
public override void OnCallableBlockExpression(CallableBlockExpression node)
{
if (node.LexicalInfo.Line <= resolver.CaretLine && GetEndSourceLocation(node).Line >= resolver.CaretLine - 1) {
foreach (ParameterDeclaration param in node.Parameters) {
- DeclarationFound(param.Name, param.Type, null, param.LexicalInfo);
+ DeclarationFound(param.Name, param.Type ?? (resolver.IsDucky ? new SimpleTypeReference("duck") : new SimpleTypeReference("object")), null, param.LexicalInfo);
}
base.OnCallableBlockExpression(node);
}
@@ -225,7 +227,9 @@ namespace Grunwald.BooBinding.CodeCompletion
if (declarationType != null) {
Add(declarationName, declarationType);
} else if (initializer != null) {
- Add(declarationName, initializer, false);
+ if (!knownVariableNames.Contains(declarationName)) {
+ Add(declarationName, initializer, false);
+ }
}
}
diff --git a/src/AddIns/BackendBindings/Boo/BooBinding/Test/ResolverTests.cs b/src/AddIns/BackendBindings/Boo/BooBinding/Test/ResolverTests.cs
index a453a028b4..f1acccad45 100644
--- a/src/AddIns/BackendBindings/Boo/BooBinding/Test/ResolverTests.cs
+++ b/src/AddIns/BackendBindings/Boo/BooBinding/Test/ResolverTests.cs
@@ -52,9 +52,10 @@ namespace Grunwald.BooBinding.Tests
booLangPC.ReferencedContents.Add(ProjectContentRegistry.Mscorlib);
}
- ResolveResult Resolve(string prog, ExpressionResult er, string marker)
+ const string fileName = "tempFile.boo";
+
+ void Register(string prog)
{
- const string fileName = "tempFile.boo";
DefaultProjectContent pc = new DefaultProjectContent();
ParserService.ForceProjectContent(pc);
pc.ReferencedContents.Add(ProjectContentRegistry.Mscorlib);
@@ -63,10 +64,13 @@ namespace Grunwald.BooBinding.Tests
ICompilationUnit cu = new BooParser().Parse(pc, fileName, prog);
ParserService.UpdateParseInformation(cu, fileName, false, false);
cu.Classes.ForEach(pc.AddClassToNamespaceList);
-
+ }
+
+ void GetPos(string prog, string marker, out int line, out int column)
+ {
int index = prog.IndexOf(marker);
- int line = 1;
- int column = 0;
+ line = 1;
+ column = 0;
for (int i = 0; i < index; i++) {
column++;
if (prog[i]=='\n') {
@@ -74,6 +78,13 @@ namespace Grunwald.BooBinding.Tests
column = 0;
}
}
+ }
+
+ ResolveResult Resolve(string prog, ExpressionResult er, string marker)
+ {
+ Register(prog);
+ int line, column;
+ GetPos(prog, marker, out line, out column);
BooResolver r = new BooResolver();
return r.Resolve(er, line, column, fileName, prog);
@@ -91,8 +102,8 @@ namespace Grunwald.BooBinding.Tests
"\t/*2*/\n" +
"\tclosure2 = def(e as DateTime):\n" +
"\t\treturn e.Year\n" +
- "\trecursiveClosure = def():\n" +
- "\t\treturn recursiveClosure()\n" +
+ "\trecursiveClosure = def(myObject):/*inRecursiveClosure*/\n" +
+ "\t\treturn recursiveClosure(myObject)\n" +
"\t/*3*/\n";
[Test]
@@ -162,7 +173,14 @@ namespace Grunwald.BooBinding.Tests
// preventing the StackOverflow.
LocalResolveResult rr = Resolve("recursiveClosure", "/*3*/");
Assert.IsFalse(rr.IsParameter);
- Assert.AreEqual("delegate():?", rr.ResolvedType.FullyQualifiedName);
+ Assert.AreEqual("delegate(myObject:Object):?", rr.ResolvedType.FullyQualifiedName);
+ }
+
+ [Test]
+ public void ClosureTypelessArgument()
+ {
+ LocalResolveResult rr = Resolve("myObject", "/*inRecursiveClosure*/");
+ Assert.AreEqual("System.Object", rr.ResolvedType.FullyQualifiedName);
}
#endregion
@@ -196,5 +214,89 @@ namespace Grunwald.BooBinding.Tests
Assert.IsNull(Resolve(regressionProg, new ExpressionResult("boo640a"), "/*1*/"));
}
#endregion
+
+ #region CtrlSpace
+ void CtrlSpace(string prog, params string[] expected)
+ {
+ CtrlSpace(new string[0], prog, expected);
+ }
+
+ void CtrlSpace(string[] unExpected, string prog, params string[] expected)
+ {
+ Register(prog);
+ int line, column;
+ GetPos(prog, "/*mark*/", out line, out column);
+ BooResolver r = new BooResolver();
+ System.Collections.ArrayList ar;
+ ar = r.CtrlSpace(line, column, fileName, prog, ExpressionContext.Default);
+ foreach (string e in unExpected) {
+ foreach (object o in ar) {
+ if (e.Equals(o))
+ Assert.Fail("Didn't expect " + e);
+ if (o is IMember && (o as IMember).Name == e) {
+ Assert.Fail("Didn't expect " + e);
+ }
+ if (o is IClass && (o as IClass).Name == e) {
+ Assert.Fail("Didn't expect " + e);
+ }
+ }
+ }
+ foreach (string e in expected) {
+ bool ok = false;
+ foreach (object o in ar) {
+ if (e.Equals(o)) {
+ if (ok) Assert.Fail("double entry " + e);
+ ok = true;
+ }
+ if (o is IMember && (o as IMember).Name == e) {
+ if (ok) Assert.Fail("double entry " + e);
+ ok = true;
+ }
+ if (o is IClass && (o as IClass).Name == e) {
+ if (ok) Assert.Fail("double entry " + e);
+ ok = true;
+ }
+ }
+ if (!ok)
+ Assert.Fail("Expected " + e);
+ }
+ }
+
+ [Test]
+ public void CtrlSpaceScopeExtension()
+ {
+ string prog =
+ "def Foo():\n" +
+ "\tbar = def():\n" +
+ "\t\tx = 0\n" +
+ "\t\t/*mark*/\n";
+ CtrlSpace(prog, "bar", "x");
+ }
+
+ [Test]
+ public void DoubleEntryTest()
+ {
+ string prog =
+ "class MyClass:\n" +
+ "\t_myInt = 0\n" +
+ "\tdef Foo():\n" +
+ "\t\t_myInt = 5\n" +
+ "\t\t/*mark*/\n";
+ CtrlSpace(prog, "_myInt");
+ }
+
+ [Test]
+ public void LoopInClosureTest()
+ {
+ string prog =
+ "def Foo():\n" +
+ "\tfor i in range(5):\n" +
+ "\t\tbar = def():\n" +
+ "\t\t\tx = 0\n" +
+ "\t\t\t/*mark*/\n" +
+ "\t\t\tprint x";
+ CtrlSpace(prog, "x", "bar", "i");
+ }
+ #endregion
}
}