// // // // // $Revision$ // using System; using System.Diagnostics; using System.Drawing; using System.Text; using ICSharpCode.TextEditor.Document; namespace ICSharpCode.TextEditor.Actions { public class Tab : AbstractEditAction { public static string GetIndentationString(IDocument document) { return GetIndentationString(document, null); } public static string GetIndentationString(IDocument document, TextArea textArea) { StringBuilder indent = new StringBuilder(); if (document.TextEditorProperties.ConvertTabsToSpaces) { int tabIndent = document.TextEditorProperties.IndentationSize; if (textArea != null) { int column = textArea.TextView.GetVisualColumn(textArea.Caret.Line, textArea.Caret.Column); indent.Append(new String(' ', tabIndent - column % tabIndent)); } else { indent.Append(new String(' ', tabIndent)); } } else { indent.Append('\t'); } return indent.ToString(); } void InsertTabs(IDocument document, ISelection selection, int y1, int y2) { string indentationString = GetIndentationString(document); for (int i = y2; i >= y1; --i) { LineSegment line = document.GetLineSegment(i); if (i == y2 && i == selection.EndPosition.Y && selection.EndPosition.X == 0) { continue; } // this bit is optional - but useful if you are using block tabbing to sort out // a source file with a mixture of tabs and spaces // string newLine = document.GetText(line.Offset,line.Length); // document.Replace(line.Offset,line.Length,newLine); document.Insert(line.Offset, indentationString); } } void InsertTabAtCaretPosition(TextArea textArea) { switch (textArea.Caret.CaretMode) { case CaretMode.InsertMode: textArea.InsertString(GetIndentationString(textArea.Document, textArea)); break; case CaretMode.OverwriteMode: string indentStr = GetIndentationString(textArea.Document, textArea); textArea.ReplaceChar(indentStr[0]); if (indentStr.Length > 1) { textArea.InsertString(indentStr.Substring(1)); } break; } textArea.SetDesiredColumn(); } /// /// Executes this edit action /// /// The which is used for callback purposes public override void Execute(TextArea textArea) { if (textArea.SelectionManager.SelectionIsReadonly) { return; } textArea.Document.UndoStack.StartUndoGroup(); if (textArea.SelectionManager.HasSomethingSelected) { foreach (ISelection selection in textArea.SelectionManager.SelectionCollection) { int startLine = selection.StartPosition.Y; int endLine = selection.EndPosition.Y; if (startLine != endLine) { textArea.BeginUpdate(); InsertTabs(textArea.Document, selection, startLine, endLine); textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.LinesBetween, startLine, endLine)); textArea.EndUpdate(); } else { InsertTabAtCaretPosition(textArea); break; } } textArea.Document.CommitUpdate(); textArea.AutoClearSelection = false; } else { InsertTabAtCaretPosition(textArea); } textArea.Document.UndoStack.EndUndoGroup(); } } public class ShiftTab : AbstractEditAction { void RemoveTabs(IDocument document, ISelection selection, int y1, int y2) { document.UndoStack.StartUndoGroup(); for (int i = y2; i >= y1; --i) { LineSegment line = document.GetLineSegment(i); if (i == y2 && line.Offset == selection.EndOffset) { continue; } if (line.Length > 0) { /**** TextPad Strategy: /// first convert leading whitespace to tabs (controversial! - not all editors work like this) string newLine = TextUtilities.LeadingWhiteSpaceToTabs(document.GetText(line.Offset,line.Length),document.Properties.Get("TabIndent", 4)); if(newLine.Length > 0 && newLine[0] == '\t') { document.Replace(line.Offset,line.Length,newLine.Substring(1)); ++redocounter; } else if(newLine.Length > 0 && newLine[0] == ' ') { /// there were just some leading spaces but less than TabIndent of them int leadingSpaces = 1; for(leadingSpaces = 1; leadingSpaces < newLine.Length && newLine[leadingSpaces] == ' '; leadingSpaces++) { /// deliberately empty } document.Replace(line.Offset,line.Length,newLine.Substring(leadingSpaces)); ++redocounter; } /// else /// there were no leading tabs or spaces on this line so do nothing /// MS Visual Studio 6 strategy: ****/ // string temp = document.GetText(line.Offset,line.Length); if (line.Length > 0) { int charactersToRemove = 0; if(document.GetCharAt(line.Offset) == '\t') { // first character is a tab - just remove it charactersToRemove = 1; } else if(document.GetCharAt(line.Offset) == ' ') { int leadingSpaces = 1; int tabIndent = document.TextEditorProperties.IndentationSize; for (leadingSpaces = 1; leadingSpaces < line.Length && document.GetCharAt(line.Offset + leadingSpaces) == ' '; leadingSpaces++) { // deliberately empty } if(leadingSpaces >= tabIndent) { // just remove tabIndent charactersToRemove = tabIndent; } else if(line.Length > leadingSpaces && document.GetCharAt(line.Offset + leadingSpaces) == '\t') { // remove the leading spaces and the following tab as they add up // to just one tab stop charactersToRemove = leadingSpaces+1; } else { // just remove the leading spaces charactersToRemove = leadingSpaces; } } if (charactersToRemove > 0) { document.Remove(line.Offset,charactersToRemove); } } } } document.UndoStack.EndUndoGroup(); } /// /// Executes this edit action /// /// The which is used for callback purposes public override void Execute(TextArea textArea) { if (textArea.SelectionManager.HasSomethingSelected) { foreach (ISelection selection in textArea.SelectionManager.SelectionCollection) { int startLine = selection.StartPosition.Y; int endLine = selection.EndPosition.Y; textArea.BeginUpdate(); RemoveTabs(textArea.Document, selection, startLine, endLine); textArea.Document.UpdateQueue.Clear(); textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.LinesBetween, startLine, endLine)); textArea.EndUpdate(); } textArea.AutoClearSelection = false; } else { // Pressing Shift-Tab with nothing selected the cursor will move back to the // previous tab stop. It will stop at the beginning of the line. Also, the desired // column is updated to that column. LineSegment line = textArea.Document.GetLineSegmentForOffset(textArea.Caret.Offset); string startOfLine = textArea.Document.GetText(line.Offset,textArea.Caret.Offset - line.Offset); int tabIndent = textArea.Document.TextEditorProperties.IndentationSize; int currentColumn = textArea.Caret.Column; int remainder = currentColumn % tabIndent; if (remainder == 0) { textArea.Caret.DesiredColumn = Math.Max(0, currentColumn - tabIndent); } else { textArea.Caret.DesiredColumn = Math.Max(0, currentColumn - remainder); } textArea.SetCaretToDesiredColumn(); } } } 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; void RemoveCommentAt(IDocument document, string comment, ISelection selection, int y1, int y2) { firstLine = y1; lastLine = y2; for (int i = y2; i >= y1; --i) { LineSegment line = document.GetLineSegment(i); if (selection != null && i == y2 && line.Offset == selection.Offset + selection.Length) { --lastLine; continue; } string lineText = document.GetText(line.Offset, line.Length); if (lineText.Trim().StartsWith(comment)) { document.Remove(line.Offset + lineText.IndexOf(comment), comment.Length); } } } void SetCommentAt(IDocument document, string comment, ISelection selection, int y1, int y2) { firstLine = y1; lastLine = y2; for (int i = y2; i >= y1; --i) { LineSegment line = document.GetLineSegment(i); if (selection != null && i == y2 && line.Offset == selection.Offset + selection.Length) { --lastLine; continue; } string lineText = document.GetText(line.Offset, line.Length); document.Insert(line.Offset, comment); } } bool ShouldComment(IDocument document, string comment, ISelection selection, int startLine, int endLine) { for (int i = endLine; i >= startLine; --i) { LineSegment line = document.GetLineSegment(i); if (selection != null && i == endLine && line.Offset == selection.Offset + selection.Length) { --lastLine; continue; } string lineText = document.GetText(line.Offset, line.Length); if (!lineText.Trim().StartsWith(comment)) { return true; } } return false; } /// /// Executes this edit action /// /// The which is used for callback purposes public override void Execute(TextArea textArea) { if (textArea.Document.ReadOnly) { return; } string comment = null; if (textArea.Document.HighlightingStrategy.Properties.ContainsKey("LineComment")) { comment = textArea.Document.HighlightingStrategy.Properties["LineComment"].ToString(); } if (comment == null || comment.Length == 0) { return; } textArea.Document.UndoStack.StartUndoGroup(); if (textArea.SelectionManager.HasSomethingSelected) { bool shouldComment = true; foreach (ISelection selection in textArea.SelectionManager.SelectionCollection) { if (!ShouldComment(textArea.Document, comment, selection, selection.StartPosition.Y, selection.EndPosition.Y)) { shouldComment = false; break; } } foreach (ISelection selection in textArea.SelectionManager.SelectionCollection) { textArea.BeginUpdate(); if (shouldComment) { SetCommentAt(textArea.Document, comment, selection, selection.StartPosition.Y, selection.EndPosition.Y); } else { RemoveCommentAt(textArea.Document, comment, selection, selection.StartPosition.Y, selection.EndPosition.Y); } textArea.Document.UpdateQueue.Clear(); textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.LinesBetween, firstLine, lastLine)); textArea.EndUpdate(); } textArea.Document.CommitUpdate(); textArea.AutoClearSelection = false; } else { textArea.BeginUpdate(); int caretLine = textArea.Caret.Line; if (ShouldComment(textArea.Document, comment, null, caretLine, caretLine)) { SetCommentAt(textArea.Document, comment, null, caretLine, caretLine); } else { RemoveCommentAt(textArea.Document, comment, null, caretLine, caretLine); } textArea.Document.UpdateQueue.Clear(); textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, caretLine)); textArea.EndUpdate(); } textArea.Document.UndoStack.EndUndoGroup(); } } 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); textArea.Document.UndoStack.StartUndoGroup(); if (commentRegion != null) { RemoveComment(textArea.Document, commentRegion); } else if (textArea.SelectionManager.HasSomethingSelected) { SetCommentAt(textArea.Document, selectionStartOffset, selectionEndOffset, commentStart, commentEnd); } textArea.Document.UndoStack.EndUndoGroup(); 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 or partially inside the // selected text. int commentEndBeforeStartOffset = -1; 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); if (commentStartOffset >= 0) { // Find end of comment before comment start. commentEndBeforeStartOffset = text.IndexOf(commentEnd, commentStartOffset, selectionStartOffset - commentStartOffset); if (commentEndBeforeStartOffset > commentStartOffset) { commentStartOffset = -1; } } } // Find end of comment after or partially after the // 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); } void RemoveComment(IDocument document, BlockCommentRegion commentRegion) { document.Remove(commentRegion.EndOffset, commentRegion.CommentEnd.Length); document.Remove(commentRegion.StartOffset, commentRegion.CommentStart.Length); } } 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 int GetHashCode() { int hashCode = 0; unchecked { if (commentStart != null) hashCode += 1000000007 * commentStart.GetHashCode(); if (commentEnd != null) hashCode += 1000000009 * commentEnd.GetHashCode(); hashCode += 1000000021 * startOffset.GetHashCode(); hashCode += 1000000033 * endOffset.GetHashCode(); } return hashCode; } public override bool Equals(object obj) { BlockCommentRegion other = obj as BlockCommentRegion; if (other == null) return false; return this.commentStart == other.commentStart && this.commentEnd == other.commentEnd && this.startOffset == other.startOffset && this.endOffset == other.endOffset; } } public class Backspace : AbstractEditAction { /// /// Executes this edit action /// /// The which is used for callback purposes public override void Execute(TextArea textArea) { if (textArea.SelectionManager.HasSomethingSelected) { Delete.DeleteSelection(textArea); } else { if (textArea.Caret.Offset > 0 && !textArea.IsReadOnly(textArea.Caret.Offset - 1)) { textArea.BeginUpdate(); int curLineNr = textArea.Document.GetLineNumberForOffset(textArea.Caret.Offset); int curLineOffset = textArea.Document.GetLineSegment(curLineNr).Offset; if (curLineOffset == textArea.Caret.Offset) { LineSegment line = textArea.Document.GetLineSegment(curLineNr - 1); bool lastLine = curLineNr == textArea.Document.TotalNumberOfLines; int lineEndOffset = line.Offset + line.Length; int lineLength = line.Length; textArea.Document.Remove(lineEndOffset, curLineOffset - lineEndOffset); textArea.Caret.Position = new TextLocation(lineLength, curLineNr - 1); textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.PositionToEnd, new TextLocation(0, curLineNr - 1))); } else { int caretOffset = textArea.Caret.Offset - 1; textArea.Caret.Position = textArea.Document.OffsetToPosition(caretOffset); textArea.Document.Remove(caretOffset, 1); textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.PositionToLineEnd, new TextLocation(textArea.Caret.Offset - textArea.Document.GetLineSegment(curLineNr).Offset, curLineNr))); } textArea.EndUpdate(); } } } } public class Delete : AbstractEditAction { internal static void DeleteSelection(TextArea textArea) { Debug.Assert(textArea.SelectionManager.HasSomethingSelected); if (textArea.SelectionManager.SelectionIsReadonly) return; textArea.BeginUpdate(); textArea.Caret.Position = textArea.SelectionManager.SelectionCollection[0].StartPosition; textArea.SelectionManager.RemoveSelectedText(); textArea.ScrollToCaret(); textArea.EndUpdate(); } /// /// Executes this edit action /// /// The which is used for callback purposes public override void Execute(TextArea textArea) { if (textArea.SelectionManager.HasSomethingSelected) { DeleteSelection(textArea); } else { if (textArea.IsReadOnly(textArea.Caret.Offset)) return; if (textArea.Caret.Offset < textArea.Document.TextLength) { textArea.BeginUpdate(); int curLineNr = textArea.Document.GetLineNumberForOffset(textArea.Caret.Offset); LineSegment curLine = textArea.Document.GetLineSegment(curLineNr); if (curLine.Offset + curLine.Length == textArea.Caret.Offset) { if (curLineNr + 1 < textArea.Document.TotalNumberOfLines) { LineSegment nextLine = textArea.Document.GetLineSegment(curLineNr + 1); textArea.Document.Remove(textArea.Caret.Offset, nextLine.Offset - textArea.Caret.Offset); textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.PositionToEnd, new TextLocation(0, curLineNr))); } } else { textArea.Document.Remove(textArea.Caret.Offset, 1); // textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.PositionToLineEnd, new TextLocation(textArea.Caret.Offset - textArea.Document.GetLineSegment(curLineNr).Offset, curLineNr))); } textArea.UpdateMatchingBracket(); textArea.EndUpdate(); } } } } public class MovePageDown : AbstractEditAction { /// /// Executes this edit action /// /// The which is used for callback purposes public override void Execute(TextArea textArea) { int curLineNr = textArea.Caret.Line; int requestedLineNumber = Math.Min(textArea.Document.GetNextVisibleLineAbove(curLineNr, textArea.TextView.VisibleLineCount), textArea.Document.TotalNumberOfLines - 1); if (curLineNr != requestedLineNumber) { textArea.Caret.Position = new TextLocation(0, requestedLineNumber); textArea.SetCaretToDesiredColumn(); } } } public class MovePageUp : AbstractEditAction { /// /// Executes this edit action /// /// The which is used for callback purposes public override void Execute(TextArea textArea) { int curLineNr = textArea.Caret.Line; int requestedLineNumber = Math.Max(textArea.Document.GetNextVisibleLineBelow(curLineNr, textArea.TextView.VisibleLineCount), 0); if (curLineNr != requestedLineNumber) { textArea.Caret.Position = new TextLocation(0, requestedLineNumber); textArea.SetCaretToDesiredColumn(); } } } public class Return : AbstractEditAction { /// /// Executes this edit action /// /// The which is used for callback purposes public override void Execute(TextArea textArea) { if (textArea.Document.ReadOnly) { return; } textArea.BeginUpdate(); textArea.Document.UndoStack.StartUndoGroup(); try { if (textArea.HandleKeyPress('\n')) return; textArea.InsertString(Environment.NewLine); int curLineNr = textArea.Caret.Line; textArea.Document.FormattingStrategy.FormatLine(textArea, curLineNr, textArea.Caret.Offset, '\n'); textArea.SetDesiredColumn(); textArea.Document.UpdateQueue.Clear(); textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.PositionToEnd, new TextLocation(0, curLineNr - 1))); } finally { textArea.Document.UndoStack.EndUndoGroup(); textArea.EndUpdate(); } } } public class ToggleEditMode : AbstractEditAction { /// /// Executes this edit action /// /// The which is used for callback purposes public override void Execute(TextArea textArea) { if (textArea.Document.ReadOnly) { return; } switch (textArea.Caret.CaretMode) { case CaretMode.InsertMode: textArea.Caret.CaretMode = CaretMode.OverwriteMode; break; case CaretMode.OverwriteMode: textArea.Caret.CaretMode = CaretMode.InsertMode; break; } } } public class Undo : AbstractEditAction { /// /// Executes this edit action /// /// The which is used for callback purposes public override void Execute(TextArea textArea) { textArea.MotherTextEditorControl.Undo(); } } public class Redo : AbstractEditAction { /// /// Executes this edit action /// /// The which is used for callback purposes public override void Execute(TextArea textArea) { textArea.MotherTextEditorControl.Redo(); } } /// /// handles the ctrl-backspace key /// functionality attempts to roughly mimic MS Developer studio /// I will implement this as deleting back to the point that ctrl-leftarrow would /// take you to /// public class WordBackspace : AbstractEditAction { /// /// Executes this edit action /// /// The which is used for callback purposes public override void Execute(TextArea textArea) { // if anything is selected we will just delete it first if (textArea.SelectionManager.HasSomethingSelected) { Delete.DeleteSelection(textArea); return; } textArea.BeginUpdate(); // now delete from the caret to the beginning of the word LineSegment line = textArea.Document.GetLineSegmentForOffset(textArea.Caret.Offset); // if we are not at the beginning of a line if (textArea.Caret.Offset > line.Offset) { int prevWordStart = TextUtilities.FindPrevWordStart(textArea.Document, textArea.Caret.Offset); if (prevWordStart < textArea.Caret.Offset) { if (!textArea.IsReadOnly(prevWordStart, textArea.Caret.Offset - prevWordStart)) { textArea.Document.Remove(prevWordStart, textArea.Caret.Offset - prevWordStart); textArea.Caret.Position = textArea.Document.OffsetToPosition(prevWordStart); } } } // if we are now at the beginning of a line if (textArea.Caret.Offset == line.Offset) { // if we are not on the first line int curLineNr = textArea.Document.GetLineNumberForOffset(textArea.Caret.Offset); if (curLineNr > 0) { // move to the end of the line above LineSegment lineAbove = textArea.Document.GetLineSegment(curLineNr - 1); int endOfLineAbove = lineAbove.Offset + lineAbove.Length; int charsToDelete = textArea.Caret.Offset - endOfLineAbove; if (!textArea.IsReadOnly(endOfLineAbove, charsToDelete)) { textArea.Document.Remove(endOfLineAbove, charsToDelete); textArea.Caret.Position = textArea.Document.OffsetToPosition(endOfLineAbove); } } } textArea.SetDesiredColumn(); textArea.EndUpdate(); // if there are now less lines, we need this or there are redraw problems textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.PositionToEnd, new TextLocation(0, textArea.Document.GetLineNumberForOffset(textArea.Caret.Offset)))); textArea.Document.CommitUpdate(); } } /// /// handles the ctrl-delete key /// functionality attempts to mimic MS Developer studio /// I will implement this as deleting forwardto the point that /// ctrl-leftarrow would take you to /// public class DeleteWord : Delete { /// /// Executes this edit action /// /// The which is used for callback purposes public override void Execute(TextArea textArea) { if (textArea.SelectionManager.HasSomethingSelected) { DeleteSelection(textArea); return; } // if anything is selected we will just delete it first textArea.BeginUpdate(); // now delete from the caret to the beginning of the word LineSegment line = textArea.Document.GetLineSegmentForOffset(textArea.Caret.Offset); if(textArea.Caret.Offset == line.Offset + line.Length) { // if we are at the end of a line base.Execute(textArea); } else { int nextWordStart = TextUtilities.FindNextWordStart(textArea.Document, textArea.Caret.Offset); if(nextWordStart > textArea.Caret.Offset) { if (!textArea.IsReadOnly(textArea.Caret.Offset, nextWordStart - textArea.Caret.Offset)) { textArea.Document.Remove(textArea.Caret.Offset, nextWordStart - textArea.Caret.Offset); // cursor never moves with this command } } } textArea.UpdateMatchingBracket(); textArea.EndUpdate(); // if there are now less lines, we need this or there are redraw problems textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.PositionToEnd, new TextLocation(0, textArea.Document.GetLineNumberForOffset(textArea.Caret.Offset)))); textArea.Document.CommitUpdate(); } } public class DeleteLine : AbstractEditAction { public override void Execute(TextArea textArea) { int lineNr = textArea.Caret.Line; LineSegment line = textArea.Document.GetLineSegment(lineNr); if (textArea.IsReadOnly(line.Offset, line.Length)) return; textArea.Document.Remove(line.Offset, line.TotalLength); textArea.Caret.Position = textArea.Document.OffsetToPosition(line.Offset); textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.PositionToEnd, new TextLocation(0, lineNr))); textArea.UpdateMatchingBracket(); textArea.Document.CommitUpdate(); } } public class DeleteToLineEnd : AbstractEditAction { public override void Execute(TextArea textArea) { int lineNr = textArea.Caret.Line; LineSegment line = textArea.Document.GetLineSegment(lineNr); int numRemove = (line.Offset + line.Length) - textArea.Caret.Offset; if (numRemove > 0 && !textArea.IsReadOnly(textArea.Caret.Offset, numRemove)) { textArea.Document.Remove(textArea.Caret.Offset, numRemove); textArea.Document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, new TextLocation(0, lineNr))); textArea.Document.CommitUpdate(); } } } public class GotoMatchingBrace : AbstractEditAction { public override void Execute(TextArea textArea) { Highlight highlight = textArea.FindMatchingBracketHighlight(); if (highlight != null) { TextLocation p1 = new TextLocation(highlight.CloseBrace.X + 1, highlight.CloseBrace.Y); TextLocation p2 = new TextLocation(highlight.OpenBrace.X + 1, highlight.OpenBrace.Y); if (p1 == textArea.Caret.Position) { if (textArea.Document.TextEditorProperties.BracketMatchingStyle == BracketMatchingStyle.After) { textArea.Caret.Position = p2; } else { textArea.Caret.Position = new TextLocation(p2.X - 1, p2.Y); } } else { if (textArea.Document.TextEditorProperties.BracketMatchingStyle == BracketMatchingStyle.After) { textArea.Caret.Position = p1; } else { textArea.Caret.Position = new TextLocation(p1.X - 1, p1.Y); } } textArea.SetDesiredColumn(); } } } }