[WPF] Some extension methods to simplify the use of AvalonEdit

November 4th, 2011 | Posted by Tom in .NET | Article | AvalonEdit | Read: 5,461

AvalonEdit, a core component of SharpDevelop 4.0,  is a very useful component for anyone who want to re-create, in its application, a code/text editor. It provides some great features like:

  1. Autocompletion
  2. Tooltip
  3. Text highlighting
  4. An much more !

Sample Image

But if you need/want to create your own code editor, there are some steps that you’ll need to do and that be a bit hard to perform. I think, for example, to the following case:

  1. Find the word under the mouse to display it’s documentation on the tooltip
  2. Find the word before a dot (“.”) to display the correct values in the autocompletion box
  3. Add a custom highlighting

To help you in these tasks, I’ve create some extension methods that you can use directly:

public static class AvalonEditExtensions
{
    public static IHighlightingDefinition AddCustomHighlighting(this TextEditor textEditor, Stream xshdStream)
    {
        if (xshdStream == null)
            throw new InvalidOperationException("Could not find embedded resource");

        IHighlightingDefinition customHighlighting;

        // Load our custom highlighting definition
        using (XmlReader reader = new XmlTextReader(xshdStream))
        {
            customHighlighting = HighlightingLoader.Load(reader, HighlightingManager.Instance);
        }

        // And register it in the HighlightingManager
        HighlightingManager.Instance.RegisterHighlighting("Custom Highlighting", null, customHighlighting);

        return customHighlighting;
    }

    public static IHighlightingDefinition AddCustomHighlighting(this TextEditor textEditor, Stream xshdStream, string[] extensions)
    {
        if (xshdStream == null)
             throw new InvalidOperationException("Could not find embedded resource");

        IHighlightingDefinition customHighlighting;

        // Load our custom highlighting definition
        using (XmlReader reader = new XmlTextReader(xshdStream))
        {
            customHighlighting = HighlightingLoader.Load(reader, HighlightingManager.Instance);
        }

        // And register it in the HighlightingManager
        HighlightingManager.Instance.RegisterHighlighting("Custom Highlighting", extensions, customHighlighting);

        return customHighlighting;
    }

    public static string GetWordUnderMouse(this TextDocument document, TextViewPosition position)
    {
        string wordHovered = string.Empty;

        var line = position.Line;
        var column = position.Column;

        var offset = document.GetOffset(line, column);
        if (offset >= document.TextLength)
            offset--;

        var textAtOffset = document.GetText(offset, 1);

        // Get text backward of the mouse position, until the first space
        while (!string.IsNullOrWhiteSpace(textAtOffset))
        {
            wordHovered = textAtOffset + wordHovered;

            offset--;

            if (offset < 0)
                break;

            textAtOffset = document.GetText(offset, 1);
        }

        // Get text forward the mouse position, until the first space
        offset = document.GetOffset(line, column);
        if (offset < document.TextLength - 1)
        {
            offset++;

            textAtOffset = document.GetText(offset, 1);

            while (!string.IsNullOrWhiteSpace(textAtOffset))
            {
                wordHovered = wordHovered + textAtOffset;

                offset++;

                if (offset >= document.TextLength)
                    break;

                textAtOffset = document.GetText(offset, 1);
            }
        }

        return wordHovered;
    }

    public static string GetWordBeforeDot(this TextEditor textEditor)
    {
        var wordBeforeDot = string.Empty;

        var caretPosition = textEditor.CaretOffset - 2;

        var lineOffset = textEditor.Document.GetOffset(textEditor.Document.GetLocation(caretPosition));

        string text = textEditor.Document.GetText(lineOffset, 1);

        // Get text backward of the mouse position, until the first space
        while (!string.IsNullOrWhiteSpace(text) && text.CompareTo(".") > 0)
        {
            wordBeforeDot = text + wordBeforeDot;

            if (caretPosition == 0)
                break;

            lineOffset = textEditor.Document.GetOffset(textEditor.Document.GetLocation(--caretPosition));

            text = textEditor.Document.GetText(lineOffset, 1);
        }

        return wordBeforeDot;
    }
}

To use these methods, it’s really simple:

private void LoadCustomHighlighting()
{
    using (Stream s = typeof(MainWindow).Assembly.GetManifestResourceStream("CustomHighlighting.xshd"))
    {
        textEditor.SyntaxHighlighting = textEditor.AddCustomHighlighting(s);
    }
}

private void textEditor_MouseHover(object sender, MouseEventArgs e)
{
    var pos = textEditor.GetPositionFromPoint(e.GetPosition(textEditor));
    if (pos != null)
    {
        string wordHovered = textEditor.Document.GetWordUnderMouse(pos.Value);

        e.Handled = true;
    }
}

void textEditor_TextArea_TextEntered(object sender, TextCompositionEventArgs e)
{
    if (e.Text == ".")
    {
        var previousWord = textEditor.GetWordBeforeDot();
    }
}

As you can see, the whole difficult part is already managed by the extension methods. Of course, the code can be a bit ugly but, at least, it’s working fine. But feel free to post a comment if you have ideas to improve it !

 

Thanks and happy coding!

You can follow any responses to this entry through the RSS 2.0 You can leave a response, or trackback.

One Response



Add Comment Register



Leave a Reply