Browse Source
Allow using 'command' attribute on <MenuItem> with custom routed commands defined in AddIns. Implemented offset mapping in AvalonEdit. This allows replacing text in the document without removing all text markers from the replaced region. git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4191 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61shortcuts
23 changed files with 883 additions and 190 deletions
@ -0,0 +1,91 @@ |
|||||||
|
// <file>
|
||||||
|
// <copyright see="prj:///doc/copyright.txt"/>
|
||||||
|
// <license see="prj:///doc/license.txt"/>
|
||||||
|
// <owner name="Daniel Grunwald"/>
|
||||||
|
// <version>$Revision$</version>
|
||||||
|
// </file>
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Windows.Input; |
||||||
|
|
||||||
|
namespace ICSharpCode.AvalonEdit |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Custom commands for AvalonEdit.
|
||||||
|
/// </summary>
|
||||||
|
public static class AvalonEditCommands |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Deletes the current line.
|
||||||
|
/// The default shortcut is Ctrl+D.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly RoutedCommand DeleteLine = new RoutedCommand( |
||||||
|
"DeleteLine", typeof(TextEditor), |
||||||
|
new InputGestureCollection { |
||||||
|
new KeyGesture(Key.D, ModifierKeys.Control) |
||||||
|
}); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes leading whitespace from the selected lines (or the whole document if the selection is empty).
|
||||||
|
/// </summary>
|
||||||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "Whitespace", |
||||||
|
Justification = "WPF uses 'Whitespace'")] |
||||||
|
public static readonly RoutedCommand RemoveLeadingWhitespace = new RoutedCommand("RemoveLeadingWhitespace", typeof(TextEditor)); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes trailing whitespace from the selected lines (or the whole document if the selection is empty).
|
||||||
|
/// </summary>
|
||||||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "Whitespace", |
||||||
|
Justification = "WPF uses 'Whitespace'")] |
||||||
|
public static readonly RoutedCommand RemoveTrailingWhitespace = new RoutedCommand("RemoveTrailingWhitespace", typeof(TextEditor)); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the selected text to upper case.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly RoutedCommand ConvertToUppercase = new RoutedCommand("ConvertToUppercase", typeof(TextEditor)); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the selected text to lower case.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly RoutedCommand ConvertToLowercase = new RoutedCommand("ConvertToLowercase", typeof(TextEditor)); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the selected text to title case.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly RoutedCommand ConvertToTitleCase = new RoutedCommand("ConvertToTitleCase", typeof(TextEditor)); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Inverts the case of the selected text.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly RoutedCommand InvertCase = new RoutedCommand("InvertCase", typeof(TextEditor)); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts tabs to spaces in the selected text.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly RoutedCommand ConvertTabsToSpaces = new RoutedCommand("ConvertTabsToSpaces", typeof(TextEditor)); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts spaces to tabs in the selected text.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly RoutedCommand ConvertSpacesToTabs = new RoutedCommand("ConvertSpacesToTabs", typeof(TextEditor)); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts leading tabs to spaces in the selected lines (or the whole document if the selection is empty).
|
||||||
|
/// </summary>
|
||||||
|
public static readonly RoutedCommand ConvertLeadingTabsToSpaces = new RoutedCommand("ConvertLeadingTabsToSpaces", typeof(TextEditor)); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts leading spaces to tabs in the selected lines (or the whole document if the selection is empty).
|
||||||
|
/// </summary>
|
||||||
|
public static readonly RoutedCommand ConvertLeadingSpacesToTabs = new RoutedCommand("ConvertLeadingSpacesToTabs", typeof(TextEditor)); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Runs the IIndentationStrategy on the selected lines (or the whole document if the selection is empty).
|
||||||
|
/// </summary>
|
||||||
|
public static readonly RoutedCommand IndentSelection = new RoutedCommand( |
||||||
|
"IndentSelection", typeof(TextEditor), |
||||||
|
new InputGestureCollection { |
||||||
|
new KeyGesture(Key.I, ModifierKeys.Control) |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,177 @@ |
|||||||
|
// <file>
|
||||||
|
// <copyright see="prj:///doc/copyright.txt"/>
|
||||||
|
// <license see="prj:///doc/license.txt"/>
|
||||||
|
// <owner name="Daniel Grunwald"/>
|
||||||
|
// <version>$Revision$</version>
|
||||||
|
// </file>
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Collections.ObjectModel; |
||||||
|
|
||||||
|
namespace ICSharpCode.AvalonEdit.Document |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Contains predefined offset change mapping types.
|
||||||
|
/// </summary>
|
||||||
|
public enum OffsetChangeMappingType |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// First the old text is removed, then the new text is inserted.
|
||||||
|
/// </summary>
|
||||||
|
RemoveAndInsert, |
||||||
|
/// <summary>
|
||||||
|
/// The text is replaced character-by-character.
|
||||||
|
/// If the new text is longer than the old text, a single insertion at the end is used to account for the difference.
|
||||||
|
/// If the new text is shorter than the old text, a single deletion at the end is used to account for the difference.
|
||||||
|
/// </summary>
|
||||||
|
CharacterReplace |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Describes a series of offset changes.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable] |
||||||
|
public sealed class OffsetChangeMap : Collection<OffsetChangeMapEntry> |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Immutable OffsetChangeMap that is empty.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly OffsetChangeMap Empty = new OffsetChangeMap(Utils.Empty<OffsetChangeMapEntry>.ReadOnlyCollection); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new OffsetChangeMap instance.
|
||||||
|
/// </summary>
|
||||||
|
public OffsetChangeMap() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new OffsetChangeMap instance.
|
||||||
|
/// </summary>
|
||||||
|
public OffsetChangeMap(int capacity) |
||||||
|
: base(new List<OffsetChangeMapEntry>(capacity)) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Private constructor for immutable 'Empty' instance.
|
||||||
|
/// </summary>
|
||||||
|
private OffsetChangeMap(IList<OffsetChangeMapEntry> entries) |
||||||
|
: base(entries) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the new offset where the specified offset moves after this document change.
|
||||||
|
/// </summary>
|
||||||
|
public int GetNewOffset(int offset, AnchorMovementType movementType) |
||||||
|
{ |
||||||
|
foreach (OffsetChangeMapEntry entry in this) { |
||||||
|
offset = entry.GetNewOffset(offset, movementType); |
||||||
|
} |
||||||
|
return offset; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets whether this OffsetChangeMap is a valid explanation for the specified document change.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsValidForDocumentChange(int offset, int removalLength, int insertionLength) |
||||||
|
{ |
||||||
|
int endOffset = offset + removalLength; |
||||||
|
foreach (OffsetChangeMapEntry entry in this) { |
||||||
|
// check that ChangeMapEntry is in valid range for this document change
|
||||||
|
if (entry.Offset < offset || entry.Offset + entry.RemovalLength > endOffset) |
||||||
|
return false; |
||||||
|
endOffset += entry.Delta; |
||||||
|
} |
||||||
|
// check that the total delta matches
|
||||||
|
return endOffset == offset + insertionLength; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the inverted OffsetChangeMap (used for the undo operation).
|
||||||
|
/// </summary>
|
||||||
|
public OffsetChangeMap Invert() |
||||||
|
{ |
||||||
|
if (this == Empty) |
||||||
|
return this; |
||||||
|
OffsetChangeMap newMap = new OffsetChangeMap(this.Count); |
||||||
|
for (int i = this.Count - 1; i >= 0; i--) { |
||||||
|
OffsetChangeMapEntry entry = this[i]; |
||||||
|
newMap.Add(new OffsetChangeMapEntry(entry.Offset, -entry.Delta)); |
||||||
|
} |
||||||
|
return newMap; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An entry in the OffsetChangeMap.
|
||||||
|
/// This represents the offset of a document change (either insertion or removal, not both at once).
|
||||||
|
/// </summary>
|
||||||
|
[Serializable] |
||||||
|
public struct OffsetChangeMapEntry |
||||||
|
{ |
||||||
|
readonly int offset; |
||||||
|
readonly int delta; |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The offset at which the change occurs.
|
||||||
|
/// </summary>
|
||||||
|
public int Offset { |
||||||
|
get { return offset; } |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The change delta. If positive, it is equal to InsertionLength; if negative, it is equal to RemovalLength.
|
||||||
|
/// </summary>
|
||||||
|
public int Delta { |
||||||
|
get { return delta; } |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number of characters removed.
|
||||||
|
/// Returns 0 if this entry represents an insertion.
|
||||||
|
/// </summary>
|
||||||
|
public int RemovalLength { |
||||||
|
get { |
||||||
|
return delta < 0 ? -delta : 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number of characters inserted.
|
||||||
|
/// Returns 0 if this entry represents a removal.
|
||||||
|
/// </summary>
|
||||||
|
public int InsertionLength { |
||||||
|
get { |
||||||
|
return delta > 0 ? delta : 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the new offset where the specified offset moves after this document change.
|
||||||
|
/// </summary>
|
||||||
|
public int GetNewOffset(int oldOffset, AnchorMovementType movementType) |
||||||
|
{ |
||||||
|
if (oldOffset < this.Offset) |
||||||
|
return oldOffset; |
||||||
|
if (oldOffset > this.Offset + this.RemovalLength) |
||||||
|
return oldOffset + this.Delta; |
||||||
|
// offset is inside removed region
|
||||||
|
if (movementType == AnchorMovementType.AfterInsertion) |
||||||
|
return this.Offset + this.InsertionLength; |
||||||
|
else |
||||||
|
return this.Offset; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new OffsetChangeMapEntry instance.
|
||||||
|
/// </summary>
|
||||||
|
public OffsetChangeMapEntry(int offset, int delta) |
||||||
|
{ |
||||||
|
this.offset = offset; |
||||||
|
this.delta = delta; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue