From cbb8cbbff6a2aebc68e8fd6d7bd14df3a018db23 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 16 Mar 2012 20:02:38 +0100 Subject: [PATCH] fix XML code completion weirdnesses and adjust unit tests --- .../Project/Src/XmlCodeCompletionBinding.cs | 75 +++++++++---------- .../Project/Src/XmlCompletionItem.cs | 4 - .../Src/XmlCompletionItemCollection.cs | 2 +- .../XmlEditor/Project/Src/XmlParser.cs | 21 ++++-- .../Src/XmlSchemaCompletionCollection.cs | 3 +- .../AttributeCompletionWindowTestFixture.cs | 8 +- ...paceAttributeValueCompletionTestFixture.cs | 7 -- .../ElementCompletionWindowTests.cs | 2 +- .../NamespaceCompletionWindowTestFixture.cs | 57 -------------- .../XmlEditor/Test/Utils/MockDocument.cs | 8 +- .../XmlEditor/Test/Utils/MockTextEditor.cs | 2 + .../XmlEditor/Test/XmlEditor.Tests.csproj | 1 - 12 files changed, 65 insertions(+), 125 deletions(-) delete mode 100644 src/AddIns/DisplayBindings/XmlEditor/Test/Completion/NamespaceCompletionWindowTestFixture.cs diff --git a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlCodeCompletionBinding.cs b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlCodeCompletionBinding.cs index b6136f5231..168b49ad08 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlCodeCompletionBinding.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlCodeCompletionBinding.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.IO; - +using System.Linq; using ICSharpCode.Core; using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Editor.CodeCompletion; @@ -12,7 +12,7 @@ using ICSharpCode.SharpDevelop.Editor.CodeCompletion; namespace ICSharpCode.XmlEditor { public class XmlCodeCompletionBinding : ICodeCompletionBinding - { + { XmlSchemaFileAssociations schemaFileAssociations; XmlSchemaCompletionCollection schemas; @@ -27,42 +27,37 @@ namespace ICSharpCode.XmlEditor this.schemas = schemaFileAssociations.Schemas; } + char[] ignoredChars = new[] { '\\', '/', '"', '\'', '=', '>' }; + public CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch) { - XmlSchemaCompletion defaultSchema = schemaFileAssociations.GetSchemaCompletion(editor.FileName); - XmlCompletionItemCollection completionItems = GetCompletionItems(editor, ch, defaultSchema); - if (completionItems.HasItems) { - completionItems.Sort(); - ICompletionListWindow completionWindow = editor.ShowCompletionWindow(completionItems); - if (completionWindow != null) { - SetCompletionWindowWidth(completionWindow, completionItems); - } - } - - if ((ch == '<') || (ch == ' ') || (ch == '=')) { - return CodeCompletionKeyPressResult.Completed; - } - return CodeCompletionKeyPressResult.None; + if (char.IsWhiteSpace(ch) || editor.SelectionLength > 0) + return CodeCompletionKeyPressResult.None; + if (ignoredChars.Contains(ch)) + return CodeCompletionKeyPressResult.None; + if (XmlParser.GetXmlIdentifierBeforeIndex(editor.Document, editor.Caret.Offset).Length > 0) + return CodeCompletionKeyPressResult.None; + editor.Document.Insert(editor.Caret.Offset, ch.ToString()); + CtrlSpace(editor); + return CodeCompletionKeyPressResult.EatKey; } - XmlCompletionItemCollection GetCompletionItems(ITextEditor editor, char characterTyped, XmlSchemaCompletion defaultSchema) + XmlCompletionItemCollection GetCompletionItems(ITextEditor editor, XmlSchemaCompletion defaultSchema) { - string textUpToCursor = GetTextUpToCursor(editor, characterTyped); + int offset = editor.Caret.Offset; + string textUpToCursor = editor.Document.GetText(0, offset); - switch (characterTyped) { - case '=': - return schemas.GetNamespaceCompletion(textUpToCursor); - case '<': - return schemas.GetElementCompletion(textUpToCursor, defaultSchema); - case ' ': - return schemas.GetAttributeCompletion(textUpToCursor, defaultSchema); - } - return schemas.GetAttributeValueCompletion(characterTyped, textUpToCursor, defaultSchema); - } - - string GetTextUpToCursor(ITextEditor editor, char characterTyped) - { - return editor.Document.GetText(0, editor.Caret.Offset) + characterTyped; + XmlCompletionItemCollection items = new XmlCompletionItemCollection(); + if (XmlParser.IsInsideAttributeValue(textUpToCursor, offset)) { + items = schemas.GetNamespaceCompletion(textUpToCursor); + if (items.Count == 0) + items = schemas.GetAttributeValueCompletion(textUpToCursor, editor.Caret.Offset, defaultSchema); + } else { + items = schemas.GetAttributeCompletion(textUpToCursor, defaultSchema); + if (items.Count == 0) + items = schemas.GetElementCompletion(textUpToCursor, defaultSchema); + } + return items; } void SetCompletionWindowWidth(ICompletionListWindow completionWindow, XmlCompletionItemCollection completionItems) @@ -74,14 +69,18 @@ namespace ICSharpCode.XmlEditor } public bool CtrlSpace(ITextEditor editor) - { - string text = editor.Document.Text; - int offset = editor.Caret.Offset; - + { XmlSchemaCompletion defaultSchema = schemaFileAssociations.GetSchemaCompletion(editor.FileName); - XmlCompletionItemCollection completionItems = schemas.GetAttributeValueCompletion(text, offset, defaultSchema); + + XmlCompletionItemCollection completionItems = GetCompletionItems(editor, defaultSchema); if (completionItems.HasItems) { - editor.ShowCompletionWindow(completionItems); + completionItems.Sort(); + string identifier = XmlParser.GetXmlIdentifierBeforeIndex(editor.Document, editor.Caret.Offset); + completionItems.PreselectionLength = identifier.Length; + ICompletionListWindow completionWindow = editor.ShowCompletionWindow(completionItems); + if (completionWindow != null) { + SetCompletionWindowWidth(completionWindow, completionItems); + } return true; } return false; diff --git a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlCompletionItem.cs b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlCompletionItem.cs index cf0f206047..eda9d42360 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlCompletionItem.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlCompletionItem.cs @@ -65,10 +65,6 @@ namespace ICSharpCode.XmlEditor base.Complete(context); switch (dataType) { - case XmlCompletionItemType.NamespaceUri: - context.Editor.Document.Insert(context.StartOffset, "\""); - context.Editor.Document.Insert(context.EndOffset + 1, "\""); - break; case XmlCompletionItemType.XmlAttribute: context.Editor.Document.Insert(context.EndOffset, "=\"\""); context.Editor.Caret.Offset--; diff --git a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlCompletionItemCollection.cs b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlCompletionItemCollection.cs index c97486c725..f629407eb8 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlCompletionItemCollection.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlCompletionItemCollection.cs @@ -134,7 +134,7 @@ namespace ICSharpCode.XmlEditor public ICompletionItem SuggestedItem { get { - if (HasItems) { + if (HasItems && PreselectionLength == 0) { return this[0]; } return null; diff --git a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlParser.cs b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlParser.cs index 86f46c8d32..2f4ee1934d 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlParser.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlParser.cs @@ -7,6 +7,7 @@ using System.IO; using System.Text; using System.Text.RegularExpressions; using System.Xml; +using ICSharpCode.SharpDevelop; namespace ICSharpCode.XmlEditor { @@ -19,14 +20,10 @@ namespace ICSharpCode.XmlEditor /// since we are interested in the complete path or tree to the /// currently active element. /// - public sealed class XmlParser + public static class XmlParser { static readonly char[] whitespaceCharacters = new char[] {' ', '\n', '\t', '\r'}; - XmlParser() - { - } - /// /// Gets path of the xml element start tag that the specified /// is currently inside. @@ -687,9 +684,21 @@ namespace ICSharpCode.XmlEditor return path; } - static bool IsQuoteChar(char ch) + public static bool IsQuoteChar(char ch) { return (ch == '\"') || (ch == '\''); } + + public static string GetXmlIdentifierBeforeIndex(ITextBuffer document, int index) + { + if (document == null) + throw new ArgumentNullException("document"); + if (index < 0 || index > document.TextLength) + throw new ArgumentOutOfRangeException("index", index, "Value must be between 0 and " + document.TextLength); + int i = index - 1; + while (i >= 0 && IsXmlNameChar(document.GetCharAt(i)) && document.GetCharAt(i) != '/') + i--; + return document.GetText(i + 1, index - i - 1); + } } } diff --git a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlSchemaCompletionCollection.cs b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlSchemaCompletionCollection.cs index 253f6ecf84..0fdc343294 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlSchemaCompletionCollection.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Project/Src/XmlSchemaCompletionCollection.cs @@ -26,7 +26,8 @@ namespace ICSharpCode.XmlEditor public XmlCompletionItemCollection GetNamespaceCompletion(string textUpToCursor) { - if (XmlParser.IsNamespaceDeclaration(textUpToCursor, textUpToCursor.Length)) { + string attrName = XmlParser.GetAttributeNameAtIndex(textUpToCursor, textUpToCursor.Length); + if (attrName == "xmlns" || attrName.StartsWith("xmlns:")) { return GetNamespaceCompletion(); } return new XmlCompletionItemCollection(); diff --git a/src/AddIns/DisplayBindings/XmlEditor/Test/Completion/AttributeCompletionWindowTestFixture.cs b/src/AddIns/DisplayBindings/XmlEditor/Test/Completion/AttributeCompletionWindowTestFixture.cs index ce23113403..fa1f524bc9 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Test/Completion/AttributeCompletionWindowTestFixture.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Test/Completion/AttributeCompletionWindowTestFixture.cs @@ -40,15 +40,15 @@ namespace XmlEditor.Tests.Completion } [Test] - public void KeyPressResultIsCompletedAfterPressingSpaceBar() + public void KeyPressResultIsNotCompletedAfterPressingSpaceBar() { - Assert.AreEqual(CodeCompletionKeyPressResult.Completed, keyPressResult); + Assert.AreEqual(CodeCompletionKeyPressResult.None, keyPressResult); } [Test] - public void CompletionListUsedInShowCompletionWindowHasItems() + public void CompletionListUsedInShowCompletionWindowHasNoItems() { - Assert.IsTrue(textEditor.CompletionItemsDisplayedToArray().Length > 0); + Assert.IsTrue(textEditor.CompletionItemsDisplayedToArray().Length == 0); } } } diff --git a/src/AddIns/DisplayBindings/XmlEditor/Test/Completion/CtrlSpaceAttributeValueCompletionTestFixture.cs b/src/AddIns/DisplayBindings/XmlEditor/Test/Completion/CtrlSpaceAttributeValueCompletionTestFixture.cs index f12ab955f1..78d309b59d 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Test/Completion/CtrlSpaceAttributeValueCompletionTestFixture.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Test/Completion/CtrlSpaceAttributeValueCompletionTestFixture.cs @@ -46,13 +46,6 @@ namespace XmlEditor.Tests.Completion Assert.IsTrue(result); } - [Test] - public void CtrlSpaceMethodResultIsFalseWhenCursorIsOutsideAttributeValue() - { - textEditor.Caret.Offset = 0; - Assert.IsFalse(completionBinding.CtrlSpace(textEditor)); - } - [Test] public void ShowCompletionWindowCalledWithCompletionItems() { diff --git a/src/AddIns/DisplayBindings/XmlEditor/Test/Completion/ElementCompletionWindowTests.cs b/src/AddIns/DisplayBindings/XmlEditor/Test/Completion/ElementCompletionWindowTests.cs index d61da5e212..3358832559 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Test/Completion/ElementCompletionWindowTests.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Test/Completion/ElementCompletionWindowTests.cs @@ -42,7 +42,7 @@ namespace XmlEditor.Tests.Completion public void HandleKeyPress_LessThanKeyPressed_KeyPressResultIsCompletedAfterPressingLessThanSign() { keyPressResult = completionBinding.HandleKeyPress(textEditor, '<'); - Assert.AreEqual(CodeCompletionKeyPressResult.Completed, keyPressResult); + Assert.AreEqual(CodeCompletionKeyPressResult.EatKey, keyPressResult); } [Test] diff --git a/src/AddIns/DisplayBindings/XmlEditor/Test/Completion/NamespaceCompletionWindowTestFixture.cs b/src/AddIns/DisplayBindings/XmlEditor/Test/Completion/NamespaceCompletionWindowTestFixture.cs deleted file mode 100644 index 643a1e0dfc..0000000000 --- a/src/AddIns/DisplayBindings/XmlEditor/Test/Completion/NamespaceCompletionWindowTestFixture.cs +++ /dev/null @@ -1,57 +0,0 @@ -// 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 System.IO; -using ICSharpCode.Core; -using ICSharpCode.SharpDevelop.Editor.CodeCompletion; -using ICSharpCode.XmlEditor; -using NUnit.Framework; -using XmlEditor.Tests.Utils; - -namespace XmlEditor.Tests.Completion -{ - [TestFixture] - public class NamespaceCompletionWindowTestFixture : NamespaceCompletionWindowTestFixtureBase - { - [SetUp] - public void Init() - { - base.InitBase(); - - XmlCodeCompletionBinding completionBinding = new XmlCodeCompletionBinding(associations); - keyPressResult = completionBinding.HandleKeyPress(textEditor, '='); - } - - [Test] - public void KeyPressResultIsCompletedAfterPressingEqualsSign() - { - Assert.AreEqual(CodeCompletionKeyPressResult.Completed, keyPressResult); - } - - [Test] - public void CompletionWindowWidthSetToMatchLongestNamespaceTextWidth() - { - Assert.AreEqual(double.NaN, textEditor.CompletionWindowDisplayed.Width); - } - - [Test] - public void ExpectedCompletionDataItems() - { - XmlCompletionItemCollection expectedItems = new XmlCompletionItemCollection(); - expectedItems.Add(new XmlCompletionItem("a", XmlCompletionItemType.NamespaceUri)); - expectedItems.Add(new XmlCompletionItem("b", XmlCompletionItemType.NamespaceUri)); - expectedItems.Add(new XmlCompletionItem("c", XmlCompletionItemType.NamespaceUri)); - - Assert.AreEqual(expectedItems.ToArray(), textEditor.CompletionItemsDisplayedToArray()); - } - - [Test] - public void TextEditorDocumentGetTextCalledWithOffsetAndLength() - { - TextSection expectedTextSection = new TextSection(0, 8); - Assert.AreEqual(expectedTextSection, textEditor.MockDocument.GetTextSectionUsedWithGetTextMethod()); - } - } -} diff --git a/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/MockDocument.cs b/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/MockDocument.cs index c3e553f6fd..3763fbbfd5 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/MockDocument.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/MockDocument.cs @@ -49,9 +49,7 @@ namespace XmlEditor.Tests.Utils } public int TextLength { - get { - throw new NotImplementedException(); - } + get { return text.Length; } } public IDocumentLine GetLine(int lineNumber) @@ -78,7 +76,7 @@ namespace XmlEditor.Tests.Utils public void Insert(int offset, string text) { - throw new NotImplementedException(); + this.text = this.text.Insert(offset, text); } public void Insert(int offset, string text, AnchorMovementType defaultAnchorMovementType) @@ -138,7 +136,7 @@ namespace XmlEditor.Tests.Utils public char GetCharAt(int offset) { - throw new NotImplementedException(); + return text[offset]; } public string GetText(int offset, int length) diff --git a/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/MockTextEditor.cs b/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/MockTextEditor.cs index d7ce998abc..8774503ce1 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/MockTextEditor.cs +++ b/src/AddIns/DisplayBindings/XmlEditor/Test/Utils/MockTextEditor.cs @@ -156,6 +156,8 @@ namespace XmlEditor.Tests.Utils public ICompletionItem[] CompletionItemsDisplayedToArray() { List items = new List(); + if (completionItemsDisplayed == null) + return items.ToArray(); foreach (ICompletionItem item in completionItemsDisplayed.Items) { items.Add(item); } diff --git a/src/AddIns/DisplayBindings/XmlEditor/Test/XmlEditor.Tests.csproj b/src/AddIns/DisplayBindings/XmlEditor/Test/XmlEditor.Tests.csproj index 20c2779bed..7aed7b41dd 100644 --- a/src/AddIns/DisplayBindings/XmlEditor/Test/XmlEditor.Tests.csproj +++ b/src/AddIns/DisplayBindings/XmlEditor/Test/XmlEditor.Tests.csproj @@ -82,7 +82,6 @@ -