icsharpcode / AvalonEdit

The WPF-based text editor component used in SharpDevelop
http://avalonedit.net/
MIT License
1.85k stars 469 forks source link

The shortcut key Ctrl+K,Ctrl+D can format all the code. #423

Open Philorrine opened 6 months ago

Philorrine commented 6 months ago

When we use Visual Studio and press Ctrl+K, Ctrl+D, the code will be formatted. And how can we achieve this functionality in avalonedit? And when we press Ctrl+D after Ctrl+K, the line where the cursor is located will be deleted.

blakepell commented 3 weeks ago

I can answer half the question. I don't think AvalonEdit (or WPF) supports "chords" like Visual Studio out of the box (or if I does I'm unaware). To listen for Chords with AvalonEdit I use this code (you can use this for WPF in general). Here is an example to comment and uncomment code with Ctrl+K Ctrl+C and Ctrl+K Ctrl+U (I've gotten a lot of mileage out of this class).

public class KeyChordGesture : KeyGesture
{
    private readonly Key _key;
    private bool _gotFirstGesture;
    private readonly InputGesture _firstGesture;

    public KeyChordGesture(ModifierKeys modifier, Key firstKey, Key secondKey) : base(Key.None)
    {
        _firstGesture = new KeyGesture(firstKey, modifier);
        _key = secondKey;
    }

    public override bool Matches(object obj, InputEventArgs inputEventArgs)
    {
        var keyArgs = inputEventArgs as KeyEventArgs;

        if (keyArgs == null || keyArgs.IsRepeat)
        {
            return false;
        }

        if (_gotFirstGesture)
        {
            _gotFirstGesture = false;

            if (keyArgs.Key == _key)
            {
                inputEventArgs.Handled = true;
            }

            return keyArgs.Key == _key;
        }

        _gotFirstGesture = _firstGesture.Matches(null, inputEventArgs);

        if (_gotFirstGesture)
        {
            inputEventArgs.Handled = true;
        }

        return false;
    }
}

Then you would setup your key bindings like this (I pass it two relay commands in this example):

// Comment command
Editor.TextArea.InputBindings.Add(new KeyBinding(this.ViewModel.CommentCommand,
    new KeyChordGesture(ModifierKeys.Control, Key.K, Key.C)));

// Uncomment command.
Editor.TextArea.InputBindings.Add(new KeyBinding(this.ViewModel.UncommentCommand,
    new KeyChordGesture(ModifierKeys.Control, Key.K, Key.U)));

Then here is the relay commands those calls (these might need a little work but they mostly work):

/// <summary>
/// Comments out the selected section of code.
/// </summary>
public void Comment()
{
    if (this.Editor.SelectionLength <= 1)
    {
        return;
    }

    var sb = StringBuilderPool.Take();
    var lines = this.Editor.SelectedText.Split('\n');

    foreach (var line in lines)
    {
        string codeLine = line.TrimEnd();

        if (codeLine.StartsWith("//"))
        {
            sb.AppendLine(codeLine);
        }
        else
        {
            sb.Append("//").Append(codeLine).Append("\r\n");
        }
    }

    this.Editor.SelectedText = sb.ToString().TrimEnd('\r', '\n').TrimEnd("//");

    StringBuilderPool.Return(sb);
}

/// <summary>
/// Uncomment out the selected section of code.
/// </summary>
public ICommand UncommentCommand => new RelayCommand(Uncomment);

/// <summary>
/// Uncomment out the selected section of code.
/// </summary>
public void Uncomment()
{
    if (this.Editor.SelectionLength <= 1)
    {
        return;
    }

    var sb = StringBuilderPool.Take();
    var lines = this.Editor.SelectedText.Split('\n');

    foreach (var line in lines)
    {
        string codeLine = line.TrimEnd();

        if (codeLine.StartsWith("//"))
        {
            sb.AppendLine(codeLine.Substring(2));
        }
        else
        {
            sb.Append(codeLine).Append("\r\n");
        }
    }

    this.Editor.SelectedText = sb.ToString().TrimEnd('\r', '\n');

    StringBuilderPool.Return(sb);
}