diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Project/PythonBinding.csproj b/src/AddIns/BackendBindings/Python/PythonBinding/Project/PythonBinding.csproj
index f46591e5c8..cf40f49d34 100644
--- a/src/AddIns/BackendBindings/Python/PythonBinding/Project/PythonBinding.csproj
+++ b/src/AddIns/BackendBindings/Python/PythonBinding/Project/PythonBinding.csproj
@@ -103,6 +103,7 @@
+
diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonCodeCompletionBinding.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonCodeCompletionBinding.cs
index 55dd92c2ac..413947328a 100644
--- a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonCodeCompletionBinding.cs
+++ b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonCodeCompletionBinding.cs
@@ -12,6 +12,11 @@ namespace ICSharpCode.PythonBinding
{
public class PythonCodeCompletionBinding : DefaultCodeCompletionBinding
{
+ public PythonCodeCompletionBinding()
+ {
+ base.insightHandler = new PythonInsightWindowHandler();
+ }
+
///
/// Shows the code completion window if the keyword is handled.
///
diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonInsightWindowHandler.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonInsightWindowHandler.cs
new file mode 100644
index 0000000000..62689cc1e4
--- /dev/null
+++ b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonInsightWindowHandler.cs
@@ -0,0 +1,77 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using ICSharpCode.SharpDevelop.Editor;
+using ICSharpCode.SharpDevelop.Editor.CodeCompletion;
+
+namespace ICSharpCode.PythonBinding
+{
+ public class PythonInsightWindowHandler : IInsightWindowHandler
+ {
+ ITextEditor editor;
+ IInsightWindow insightWindow;
+
+ public void InitializeOpenedInsightWindow(ITextEditor editor, IInsightWindow insightWindow)
+ {
+ this.editor = editor;
+ this.insightWindow = insightWindow;
+ int offset = insightWindow.StartOffset;
+ insightWindow.DocumentChanged += DocumentChanged;
+ }
+
+ void DocumentChanged(object sender, TextChangeEventArgs e)
+ {
+ if (IsOutsideMethodCall()) {
+ insightWindow.Close();
+ }
+ }
+
+ bool IsOutsideMethodCall()
+ {
+ string text = GetTextInsideMethodCallUpToCursor();
+ return TextContainsClosingBracketForMethod(text);
+ }
+
+ string GetTextInsideMethodCallUpToCursor()
+ {
+ int insightStartOffset = insightWindow.StartOffset;
+ int currentOffset = editor.Caret.Offset;
+ int length = currentOffset - insightStartOffset;
+ if (length < 0) {
+ // Force completion window to close by returning the close bracket.
+ return ")";
+ }
+ return editor.Document.GetText(insightStartOffset, length);
+ }
+
+ bool TextContainsClosingBracketForMethod(string text)
+ {
+ int bracketCount = 1;
+ foreach (char ch in text) {
+ switch (ch) {
+ case '(':
+ bracketCount++;
+ break;
+ case ')':
+ bracketCount--;
+ if (bracketCount == 0) {
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+ }
+
+ public bool InsightRefreshOnComma(ITextEditor editor, char ch, out IInsightWindow insightWindow)
+ {
+ insightWindow = null;
+ return false;
+ }
+
+ public void HighlightParameter(IInsightWindow window, int index)
+ {
+ }
+ }
+}
diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Completion/PythonCodeCompletionBindingTests.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Completion/PythonCodeCompletionBindingTests.cs
new file mode 100644
index 0000000000..5f6725677b
--- /dev/null
+++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Completion/PythonCodeCompletionBindingTests.cs
@@ -0,0 +1,29 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using ICSharpCode.PythonBinding;
+using NUnit.Framework;
+using PythonBinding.Tests.Utils;
+
+namespace PythonBinding.Tests.Completion
+{
+ [TestFixture]
+ public class PythonCodeCompletionBindingTests
+ {
+ TestablePythonCodeCompletionBinding completionBinding;
+
+ void CreatePythonCodeCompletionBinding()
+ {
+ completionBinding = new TestablePythonCodeCompletionBinding();
+ }
+
+ [Test]
+ public void InsightHandler_CreateNewCodecompletionBindingInstance_IsSetToPythonInsightWindowHandlerInstance()
+ {
+ CreatePythonCodeCompletionBinding();
+ PythonInsightWindowHandler handler = completionBinding.PythonInsightWindowHandler;
+ Assert.IsNotNull(handler);
+ }
+ }
+}
diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Completion/PythonInsightWindowHandlerTests.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Completion/PythonInsightWindowHandlerTests.cs
new file mode 100644
index 0000000000..c920141ab0
--- /dev/null
+++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Completion/PythonInsightWindowHandlerTests.cs
@@ -0,0 +1,126 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using ICSharpCode.PythonBinding;
+using ICSharpCode.Scripting.Tests.Utils;
+using ICSharpCode.SharpDevelop.Editor;
+using NUnit.Framework;
+using PythonBinding.Tests.Utils;
+
+namespace PythonBinding.Tests.Completion
+{
+ [TestFixture]
+ public class PythonInsightWindowHandlerTests
+ {
+ PythonInsightWindowHandler handler;
+ MockTextEditor fakeTextEditor;
+ FakeInsightWindow fakeInsightWindow;
+
+ void CreatePythonInsightWindowHandler()
+ {
+ fakeTextEditor = new MockTextEditor();
+ fakeInsightWindow = new FakeInsightWindow();
+ handler = new PythonInsightWindowHandler();
+ }
+
+ void InitializePythonInsightWindowHandler()
+ {
+ handler.InitializeOpenedInsightWindow(fakeTextEditor, fakeInsightWindow);
+ }
+
+ TextChangeEventArgs CreateInsertedTextChangeEventArgs(int offset, string insertedText)
+ {
+ return new TextChangeEventArgs(offset, String.Empty, insertedText);
+ }
+
+ [Test]
+ public void InitializeOpenedInsightWindow_CloseParenthesisCharacterAddedToDocument_InsightWindowClosed()
+ {
+ CreatePythonInsightWindowHandler();
+ fakeTextEditor.FakeDocument.Text = "method(";
+ fakeTextEditor.FakeCaret.Offset = 7;
+ fakeInsightWindow.StartOffset = 7;
+ InitializePythonInsightWindowHandler();
+
+ int newCaretOffset = 8;
+ fakeTextEditor.FakeCaret.Offset = newCaretOffset;
+ fakeTextEditor.FakeDocument.Text = "method()";
+ TextChangeEventArgs e = CreateInsertedTextChangeEventArgs(newCaretOffset, ")");
+ fakeInsightWindow.FireDocumentChangedEvent(e);
+
+ bool closed = fakeInsightWindow.IsClosed;
+ Assert.IsTrue(closed);
+ }
+
+ [Test]
+ public void InitializeOpenedInsightWindow_MethodCallWithCursorAtOpenBracket_InsightWindowIsClosedBeforeDocumentIsChanged()
+ {
+ CreatePythonInsightWindowHandler();
+ fakeTextEditor.FakeDocument.Text = "method(";
+ fakeTextEditor.FakeCaret.Offset = 7;
+ fakeInsightWindow.StartOffset = 7;
+ InitializePythonInsightWindowHandler();
+
+ bool closed = fakeInsightWindow.IsClosed;
+ Assert.IsFalse(closed);
+ }
+
+ [Test]
+ public void InitializeOpenedInsightWindow_SingleCharacterAddedToDocumentAfterOpenParenthesis_InsightWindowIsNotClosed()
+ {
+ CreatePythonInsightWindowHandler();
+ fakeTextEditor.FakeDocument.Text = "method(";
+ fakeTextEditor.FakeCaret.Offset = 7;
+ fakeInsightWindow.StartOffset = 7;
+ InitializePythonInsightWindowHandler();
+
+ int newCaretOffset = 8;
+ fakeTextEditor.FakeCaret.Offset = newCaretOffset;
+ fakeTextEditor.FakeDocument.Text = "method(a";
+ TextChangeEventArgs e = CreateInsertedTextChangeEventArgs(newCaretOffset, "a");
+ fakeInsightWindow.FireDocumentChangedEvent(e);
+
+ bool closed = fakeInsightWindow.IsClosed;
+ Assert.IsFalse(closed);
+ }
+
+ [Test]
+ public void InitializeOpenedInsightWindow_MethodCallInsideMethodCallAndCloseParenthesisCharacterAddedToDocument_InsightWindowIsNotClosed()
+ {
+ CreatePythonInsightWindowHandler();
+ fakeTextEditor.FakeDocument.Text = "method(a(";
+ fakeTextEditor.FakeCaret.Offset = 9;
+ fakeInsightWindow.StartOffset = 7;
+ InitializePythonInsightWindowHandler();
+
+ int newCaretOffset = 10;
+ fakeTextEditor.FakeCaret.Offset = newCaretOffset;
+ fakeTextEditor.FakeDocument.Text = "method(a()";
+ TextChangeEventArgs e = CreateInsertedTextChangeEventArgs(newCaretOffset, ")");
+ fakeInsightWindow.FireDocumentChangedEvent(e);
+
+ bool closed = fakeInsightWindow.IsClosed;
+ Assert.IsFalse(closed);
+ }
+
+ [Test]
+ public void InitializeOpenedInsightWindow_CharacterAddedToDocumentBeforeStartOfInsightWindow_InsightWindowClosed()
+ {
+ CreatePythonInsightWindowHandler();
+ fakeTextEditor.FakeDocument.Text = "method(";
+ fakeTextEditor.FakeCaret.Offset = 7;
+ fakeInsightWindow.StartOffset = 7;
+ InitializePythonInsightWindowHandler();
+
+ int newCaretOffset = 1;
+ fakeTextEditor.FakeCaret.Offset = newCaretOffset;
+ fakeTextEditor.FakeDocument.Text = "aethod(";
+ TextChangeEventArgs e = CreateInsertedTextChangeEventArgs(newCaretOffset, "a");
+ fakeInsightWindow.FireDocumentChangedEvent(e);
+
+ bool closed = fakeInsightWindow.IsClosed;
+ Assert.IsTrue(closed);
+ }
+ }
+}
diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/PythonBinding.Tests.csproj b/src/AddIns/BackendBindings/Python/PythonBinding/Test/PythonBinding.Tests.csproj
index c887f2fc3a..308b86f889 100644
--- a/src/AddIns/BackendBindings/Python/PythonBinding/Test/PythonBinding.Tests.csproj
+++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/PythonBinding.Tests.csproj
@@ -97,9 +97,11 @@
+
+
@@ -417,6 +419,7 @@
+
diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/FakeInsightWindow.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/FakeInsightWindow.cs
new file mode 100644
index 0000000000..3f13c700ae
--- /dev/null
+++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/FakeInsightWindow.cs
@@ -0,0 +1,87 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using ICSharpCode.SharpDevelop.Editor;
+using ICSharpCode.SharpDevelop.Editor.CodeCompletion;
+
+namespace PythonBinding.Tests.Utils
+{
+ public class FakeInsightWindow : IInsightWindow
+ {
+ #pragma warning disable 67
+ public event EventHandler DocumentChanged;
+ public event EventHandler SelectedItemChanged;
+ public event EventHandler CaretPositionChanged;
+ public event EventHandler Closed;
+ #pragma warning restore 67
+
+ public bool IsClosed;
+
+ public IList Items {
+ get {
+ throw new NotImplementedException();
+ }
+ }
+
+ public IInsightItem SelectedItem {
+ get {
+ throw new NotImplementedException();
+ }
+ set {
+ throw new NotImplementedException();
+ }
+ }
+
+ public double Width {
+ get {
+ throw new NotImplementedException();
+ }
+ set {
+ throw new NotImplementedException();
+ }
+ }
+
+ public double Height {
+ get {
+ throw new NotImplementedException();
+ }
+ set {
+ throw new NotImplementedException();
+ }
+ }
+
+ public bool CloseAutomatically {
+ get {
+ throw new NotImplementedException();
+ }
+ set {
+ throw new NotImplementedException();
+ }
+ }
+
+ public int StartOffset { get; set; }
+
+ public int EndOffset {
+ get {
+ throw new NotImplementedException();
+ }
+ set {
+ throw new NotImplementedException();
+ }
+ }
+
+ public void Close()
+ {
+ IsClosed = true;
+ }
+
+ public void FireDocumentChangedEvent(TextChangeEventArgs e)
+ {
+ if (DocumentChanged != null) {
+ DocumentChanged(this, e);
+ }
+ }
+ }
+}
diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/TestablePythonCodeCompletionBinding.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/TestablePythonCodeCompletionBinding.cs
index db0e56fb4a..c1e140899b 100644
--- a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/TestablePythonCodeCompletionBinding.cs
+++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/TestablePythonCodeCompletionBinding.cs
@@ -47,5 +47,9 @@ namespace PythonBinding.Tests.Utils
IsCodeCompletionWindowDisplayed = true;
CompletionItemProviderUsedWhenDisplayingCodeCompletionWindow = completionItemProvider;
}
+
+ public PythonInsightWindowHandler PythonInsightWindowHandler {
+ get { return base.insightHandler as PythonInsightWindowHandler; }
+ }
}
}
diff --git a/src/AddIns/BackendBindings/Scripting/Test/Utils/FakeCaret.cs b/src/AddIns/BackendBindings/Scripting/Test/Utils/FakeCaret.cs
index 0d50e42fc8..d0e1c694ad 100644
--- a/src/AddIns/BackendBindings/Scripting/Test/Utils/FakeCaret.cs
+++ b/src/AddIns/BackendBindings/Scripting/Test/Utils/FakeCaret.cs
@@ -2,6 +2,7 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
+using ICSharpCode.NRefactory;
using ICSharpCode.SharpDevelop.Editor;
namespace ICSharpCode.Scripting.Tests.Utils
@@ -17,15 +18,7 @@ namespace ICSharpCode.Scripting.Tests.Utils
}
}
- public int Offset {
- get {
- throw new NotImplementedException();
- }
- set {
- throw new NotImplementedException();
- }
- }
-
+ public int Offset { get; set; }
public int Line { get; set; }
public int Column {
@@ -37,7 +30,7 @@ namespace ICSharpCode.Scripting.Tests.Utils
}
}
- public ICSharpCode.NRefactory.Location Position {
+ public Location Position {
get {
throw new NotImplementedException();
}
diff --git a/src/AddIns/BackendBindings/Scripting/Test/Utils/FakeDocument.cs b/src/AddIns/BackendBindings/Scripting/Test/Utils/FakeDocument.cs
index 5d5d65dd59..a3adb7c6f3 100644
--- a/src/AddIns/BackendBindings/Scripting/Test/Utils/FakeDocument.cs
+++ b/src/AddIns/BackendBindings/Scripting/Test/Utils/FakeDocument.cs
@@ -124,7 +124,7 @@ namespace ICSharpCode.Scripting.Tests.Utils
public string GetText(int offset, int length)
{
- throw new NotImplementedException();
+ return Text.Substring(offset, length);
}
public object GetService(Type serviceType)