diff --git a/src/Libraries/ICSharpCode.TextEditor/Project/Resources/XML-Mode.xshd b/src/Libraries/ICSharpCode.TextEditor/Project/Resources/XML-Mode.xshd index a4867f5f16..7571aa5dff 100644 --- a/src/Libraries/ICSharpCode.TextEditor/Project/Resources/XML-Mode.xshd +++ b/src/Libraries/ICSharpCode.TextEditor/Project/Resources/XML-Mode.xshd @@ -2,6 +2,10 @@ + + + + diff --git a/src/Libraries/ICSharpCode.TextEditor/Project/Src/Actions/MiscActions.cs b/src/Libraries/ICSharpCode.TextEditor/Project/Src/Actions/MiscActions.cs index 9f8ceca69c..3e8fa5cf1e 100644 --- a/src/Libraries/ICSharpCode.TextEditor/Project/Src/Actions/MiscActions.cs +++ b/src/Libraries/ICSharpCode.TextEditor/Project/Src/Actions/MiscActions.cs @@ -208,6 +208,27 @@ namespace ICSharpCode.TextEditor.Actions } public class ToggleComment : AbstractEditAction + { + /// + /// Executes this edit action + /// + /// The which is used for callback purposes + public override void Execute(TextArea textArea) + { + if (textArea.Document.ReadOnly) { + return; + } + + if (textArea.Document.HighlightingStrategy.Properties.ContainsKey("LineComment")) { + new ToggleLineComment().Execute(textArea); + } else if (textArea.Document.HighlightingStrategy.Properties.ContainsKey("BlockCommentBegin") && + textArea.Document.HighlightingStrategy.Properties.ContainsKey("BlockCommentBegin")) { + new ToggleBlockComment().Execute(textArea); + } + } + } + + public class ToggleLineComment : AbstractEditAction { int firstLine; int lastLine; @@ -332,6 +353,194 @@ namespace ICSharpCode.TextEditor.Actions } } + public class ToggleBlockComment : AbstractEditAction + { + /// + /// Executes this edit action + /// + /// The which is used for callback purposes + public override void Execute(TextArea textArea) + { + if (textArea.Document.ReadOnly) { + return; + } + + string commentStart = null; + if (textArea.Document.HighlightingStrategy.Properties.ContainsKey("BlockCommentBegin")) { + commentStart = textArea.Document.HighlightingStrategy.Properties["BlockCommentBegin"].ToString(); + } + + string commentEnd = null; + if (textArea.Document.HighlightingStrategy.Properties.ContainsKey("BlockCommentEnd")) { + commentEnd = textArea.Document.HighlightingStrategy.Properties["BlockCommentEnd"].ToString(); + } + + if (commentStart == null || commentStart.Length == 0 || commentEnd == null || commentEnd.Length == 0) { + return; + } + + int selectionStartOffset; + int selectionEndOffset; + + if (textArea.SelectionManager.HasSomethingSelected) { + selectionStartOffset = textArea.SelectionManager.SelectionCollection[0].Offset; + selectionEndOffset = textArea.SelectionManager.SelectionCollection[textArea.SelectionManager.SelectionCollection.Count - 1].EndOffset; + } else { + selectionStartOffset = textArea.Caret.Offset; + selectionEndOffset = selectionStartOffset; + } + + BlockCommentRegion commentRegion = FindSelectedCommentRegion(textArea.Document, commentStart, commentEnd, selectionStartOffset, selectionEndOffset); + + if (commentRegion != null) { + RemoveComment(textArea.Document, commentRegion); + } else if (textArea.SelectionManager.HasSomethingSelected) { + SetCommentAt(textArea.Document, selectionStartOffset, selectionEndOffset, commentStart, commentEnd); + } + + textArea.Document.CommitUpdate(); + textArea.AutoClearSelection = false; + } + + public static BlockCommentRegion FindSelectedCommentRegion(IDocument document, string commentStart, string commentEnd, int selectionStartOffset, int selectionEndOffset) + { + if (document.TextLength == 0) { + return null; + } + + // Find start of comment in selected text. + + int commentEndOffset = -1; + string selectedText = document.GetText(selectionStartOffset, selectionEndOffset - selectionStartOffset); + + int commentStartOffset = selectedText.IndexOf(commentStart); + if (commentStartOffset >= 0) { + commentStartOffset += selectionStartOffset; + } + + // Find end of comment in selected text. + + if (commentStartOffset >= 0) { + commentEndOffset = selectedText.IndexOf(commentEnd, commentStartOffset + commentStart.Length - selectionStartOffset); + } else { + commentEndOffset = selectedText.IndexOf(commentEnd); + } + + if (commentEndOffset >= 0) { + commentEndOffset += selectionStartOffset; + } + + // Find start of comment before selected text. + + if (commentStartOffset == -1) { + int offset = selectionEndOffset + commentStart.Length - 1; + if (offset > document.TextLength) { + offset = document.TextLength; + } + string text = document.GetText(0, offset); + commentStartOffset = text.LastIndexOf(commentStart); + } + + // Find end of comment after selected text. + + if (commentEndOffset == -1) { + int offset = selectionStartOffset + 1 - commentEnd.Length; + if (offset < 0) { + offset = selectionStartOffset; + } + string text = document.GetText(offset, document.TextLength - offset); + commentEndOffset = text.IndexOf(commentEnd); + if (commentEndOffset >= 0) { + commentEndOffset += offset; + } + } + + if (commentStartOffset != -1 && commentEndOffset != -1) { + return new BlockCommentRegion(commentStart, commentEnd, commentStartOffset, commentEndOffset); + } + + return null; + } + + + void SetCommentAt(IDocument document, int offsetStart, int offsetEnd, string commentStart, string commentEnd) + { + document.Insert(offsetEnd, commentEnd); + document.Insert(offsetStart, commentStart); + document.UndoStack.UndoLast(2); + } + + void RemoveComment(IDocument document, BlockCommentRegion commentRegion) + { + document.Remove(commentRegion.EndOffset, commentRegion.CommentEnd.Length); + document.Remove(commentRegion.StartOffset, commentRegion.CommentStart.Length); + document.UndoStack.UndoLast(2); + } + } + + public class BlockCommentRegion + { + string commentStart = String.Empty; + string commentEnd = String.Empty; + int startOffset = -1; + int endOffset = -1; + + /// + /// The end offset is the offset where the comment end string starts from. + /// + public BlockCommentRegion(string commentStart, string commentEnd, int startOffset, int endOffset) + { + this.commentStart = commentStart; + this.commentEnd = commentEnd; + this.startOffset = startOffset; + this.endOffset = endOffset; + } + + public string CommentStart { + get { + return commentStart; + } + } + + public string CommentEnd { + get { + return commentEnd; + } + } + + public int StartOffset { + get { + return startOffset; + } + } + + public int EndOffset { + get { + return endOffset; + } + } + + public override bool Equals(object obj) + { + BlockCommentRegion commentRegion = obj as BlockCommentRegion; + if (commentRegion != null) { + if (commentRegion.commentStart == commentStart && + commentRegion.commentEnd == commentEnd && + commentRegion.startOffset == startOffset && + commentRegion.endOffset == endOffset) { + return true; + } + } + + return false; + } + + public override int GetHashCode() + { + return commentStart.GetHashCode() & commentEnd.GetHashCode() & startOffset.GetHashCode() & endOffset.GetHashCode(); + } + } + public class IndentSelection : AbstractEditAction { /// diff --git a/src/Libraries/ICSharpCode.TextEditor/Test/BlockCommentTests.cs b/src/Libraries/ICSharpCode.TextEditor/Test/BlockCommentTests.cs new file mode 100644 index 0000000000..0f63b2aee7 --- /dev/null +++ b/src/Libraries/ICSharpCode.TextEditor/Test/BlockCommentTests.cs @@ -0,0 +1,147 @@ +// +// 2002-2005 AlphaSierraPapa +// GNU General Public License +// +// $Revision$ +// + +using ICSharpCode.TextEditor; +using ICSharpCode.TextEditor.Actions; +using ICSharpCode.TextEditor.Document; +using NUnit.Framework; +using System; + +namespace ICSharpCode.TextEditor.Tests +{ + [TestFixture] + public class BlockCommentTests + { + IDocument document = null; + string commentStart = ""; + + [SetUp] + public void Init() + { + document = new DocumentFactory().CreateDocument(); + document.HighlightingStrategy = HighlightingManager.Manager.FindHighlighter("XML"); + } + + [Test] + public void NoTextSelected() + { + document.TextContent = String.Empty; + int selectionStartOffset = 0; + int selectionEndOffset = 0; + + BlockCommentRegion commentRegion = ToggleBlockComment.FindSelectedCommentRegion(document, commentStart, commentEnd, selectionStartOffset, selectionEndOffset); + Assert.IsNull(commentRegion, "Should not be a comment region for an empty document"); + } + + [Test] + public void EntireCommentSelected() + { + document.TextContent = ""; + int selectionStartOffset = 0; + int selectionEndOffset = 7; + BlockCommentRegion expectedCommentRegion = new BlockCommentRegion(commentStart, commentEnd, 0, 4); + + BlockCommentRegion commentRegion = ToggleBlockComment.FindSelectedCommentRegion(document, commentStart, commentEnd, selectionStartOffset, selectionEndOffset); + Assert.AreEqual(expectedCommentRegion, commentRegion); + } + + [Test] + public void EntireCommentAndExtraTextSelected() + { + document.TextContent = "a"; + int selectionStartOffset = 0; + int selectionEndOffset = 9; + BlockCommentRegion expectedCommentRegion = new BlockCommentRegion(commentStart, commentEnd, 1, 6); + + BlockCommentRegion commentRegion = ToggleBlockComment.FindSelectedCommentRegion(document, commentStart, commentEnd, selectionStartOffset, selectionEndOffset); + Assert.AreEqual(expectedCommentRegion, commentRegion); + } + + [Test] + public void OnlyCommentStartSelected() + { + document.TextContent = ""; + int selectionStartOffset = 0; + int selectionEndOffset = 4; + BlockCommentRegion expectedCommentRegion = new BlockCommentRegion(commentStart, commentEnd, 0, 5); + + BlockCommentRegion commentRegion = ToggleBlockComment.FindSelectedCommentRegion(document, commentStart, commentEnd, selectionStartOffset, selectionEndOffset); + Assert.AreEqual(expectedCommentRegion, commentRegion); + } + + [Test] + public void OnlyCommentEndSelected() + { + document.TextContent = ""; + int selectionStartOffset = 5; + int selectionEndOffset = 8; + BlockCommentRegion expectedCommentRegion = new BlockCommentRegion(commentStart, commentEnd, 0, 5); + + BlockCommentRegion commentRegion = ToggleBlockComment.FindSelectedCommentRegion(document, commentStart, commentEnd, selectionStartOffset, selectionEndOffset); + Assert.AreEqual(expectedCommentRegion, commentRegion); + } + + [Test] + public void LastCharacterOfCommentEndSelected() + { + document.TextContent = ""; + int selectionStartOffset = 7; + int selectionEndOffset = 8; + BlockCommentRegion expectedCommentRegion = new BlockCommentRegion(commentStart, commentEnd, 0, 5); + + BlockCommentRegion commentRegion = ToggleBlockComment.FindSelectedCommentRegion(document, commentStart, commentEnd, selectionStartOffset, selectionEndOffset); + Assert.AreEqual(expectedCommentRegion, commentRegion); + } + + [Test] + public void CaretInsideCommentButNoSelectedText() + { + document.TextContent = ""; + int selectionStartOffset = 4; + int selectionEndOffset = 4; + BlockCommentRegion expectedCommentRegion = new BlockCommentRegion(commentStart, commentEnd, 0, 4); + + BlockCommentRegion commentRegion = ToggleBlockComment.FindSelectedCommentRegion(document, commentStart, commentEnd, selectionStartOffset, selectionEndOffset); + Assert.AreEqual(expectedCommentRegion, commentRegion); + } + + [Test] + public void FirstCharacterOfCommentStartSelected() + { + document.TextContent = ""; + int selectionStartOffset = 0; + int selectionEndOffset = 1; + BlockCommentRegion expectedCommentRegion = new BlockCommentRegion(commentStart, commentEnd, 0, 5); + + BlockCommentRegion commentRegion = ToggleBlockComment.FindSelectedCommentRegion(document, commentStart, commentEnd, selectionStartOffset, selectionEndOffset); + Assert.AreEqual(expectedCommentRegion, commentRegion); + } + + [Test] + public void CursorJustOutsideCommentStart() + { + document.TextContent = ""; + int selectionStartOffset = 0; + int selectionEndOffset = 0; + + BlockCommentRegion commentRegion = ToggleBlockComment.FindSelectedCommentRegion(document, commentStart, commentEnd, selectionStartOffset, selectionEndOffset); + Assert.IsNull(commentRegion); + } + + [Test] + public void CursorJustOutsideCommentEnd() + { + document.TextContent = ""; + int selectionStartOffset = 8; + int selectionEndOffset = 8; + + BlockCommentRegion commentRegion = ToggleBlockComment.FindSelectedCommentRegion(document, commentStart, commentEnd, selectionStartOffset, selectionEndOffset); + Assert.IsNull(commentRegion); + } + } +} diff --git a/src/Libraries/ICSharpCode.TextEditor/Test/ICSharpCode.TextEditor.Test.csproj b/src/Libraries/ICSharpCode.TextEditor/Test/ICSharpCode.TextEditor.Test.csproj index 49b45aaf07..327ba55119 100644 --- a/src/Libraries/ICSharpCode.TextEditor/Test/ICSharpCode.TextEditor.Test.csproj +++ b/src/Libraries/ICSharpCode.TextEditor/Test/ICSharpCode.TextEditor.Test.csproj @@ -1,4 +1,4 @@ - + Debug AnyCPU @@ -8,20 +8,10 @@ ICSharpCode.TextEditor.Test ICSharpCode.TextEditor.Test Library - - 4 - - - - False False OnSuccessfulBuild - - - - Library @@ -29,8 +19,6 @@ False False True - - ..\..\..\..\bin\ True @@ -39,22 +27,16 @@ True False False - - ..\..\..\..\bin\ True - - - - - - - - + + + + @@ -62,10 +44,7 @@ {00000000-0000-0000-0000-000000000000} ICSharpCode.TextEditor - - - - + \ No newline at end of file diff --git a/src/SharpDevelop.WithTests.sln b/src/SharpDevelop.WithTests.sln index b70a26e7ba..25eef3ddde 100644 --- a/src/SharpDevelop.WithTests.sln +++ b/src/SharpDevelop.WithTests.sln @@ -1,5 +1,5 @@ Microsoft Visual Studio Solution File, Format Version 9.00 -# SharpDevelop 2.0.0.539 +# SharpDevelop 2.0.0.546 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AddIns", "AddIns", "{14A277EE-7DF1-4529-B639-7D1EF334C1C5}" ProjectSection(SolutionItems) = postProject EndProjectSection @@ -64,6 +64,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{ ProjectSection(SolutionItems) = postProject EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.TextEditor.Test", "Libraries\ICSharpCode.TextEditor\Test\ICSharpCode.TextEditor.Test.csproj", "{6259D767-BA7C-484D-9472-68F350A20086}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.TextEditor", "Libraries\ICSharpCode.TextEditor\Project\ICSharpCode.TextEditor.csproj", "{2D18BE89-D210-49EB-A9DD-2246FBB3DF6D}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "nunit.framework.dll", "Libraries\NUnit.Framework\nunit.framework.dll.csproj", "{83DD7E12-A705-4DBA-9D71-09C8973D9382}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NRefactoryTests", "Libraries\NRefactory\Test\NRefactoryTests.csproj", "{870115DD-960A-4406-A6B9-600BCDC36A03}" @@ -72,8 +76,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Build.Tasks", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinFormsUI", "Libraries\DockPanel_Src\WinFormsUI\WinFormsUI.csproj", "{D3C782BA-178E-4235-A3BA-8C11DEBB6BEE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.TextEditor", "Libraries\ICSharpCode.TextEditor\Project\ICSharpCode.TextEditor.csproj", "{2D18BE89-D210-49EB-A9DD-2246FBB3DF6D}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NRefactory", "Libraries\NRefactory\Project\NRefactory.csproj", "{3A9AE6AA-BC07-4A2F-972C-581E3AE2F195}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Main", "Main", "{5A3EBEBA-0560-41C1-966B-23F7D03A5486}" @@ -198,11 +200,12 @@ Global {EC06F96A-AEEC-49D6-B03D-AB87C6EB674C} = {6604365C-C702-4C10-9BA8-637F1E3D4D0D} {A4C858C8-51B6-4265-A695-A20FCEBA1D19} = {6604365C-C702-4C10-9BA8-637F1E3D4D0D} {3A9AE6AA-BC07-4A2F-972C-581E3AE2F195} = {9421EDF4-9769-4BE9-B5A6-C87DE221D73C} - {2D18BE89-D210-49EB-A9DD-2246FBB3DF6D} = {9421EDF4-9769-4BE9-B5A6-C87DE221D73C} {D3C782BA-178E-4235-A3BA-8C11DEBB6BEE} = {9421EDF4-9769-4BE9-B5A6-C87DE221D73C} {4139CCF6-FB49-4A9D-B2CF-331E9EA3198D} = {9421EDF4-9769-4BE9-B5A6-C87DE221D73C} {870115DD-960A-4406-A6B9-600BCDC36A03} = {9421EDF4-9769-4BE9-B5A6-C87DE221D73C} {83DD7E12-A705-4DBA-9D71-09C8973D9382} = {9421EDF4-9769-4BE9-B5A6-C87DE221D73C} + {2D18BE89-D210-49EB-A9DD-2246FBB3DF6D} = {9421EDF4-9769-4BE9-B5A6-C87DE221D73C} + {6259D767-BA7C-484D-9472-68F350A20086} = {9421EDF4-9769-4BE9-B5A6-C87DE221D73C} {1152B71B-3C05-4598-B20D-823B5D40559E} = {5A3EBEBA-0560-41C1-966B-23F7D03A5486} {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {5A3EBEBA-0560-41C1-966B-23F7D03A5486} {2748AD25-9C63-4E12-877B-4DCE96FBED54} = {5A3EBEBA-0560-41C1-966B-23F7D03A5486}