icsharpcode / AvalonEdit

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

Autocompletion with indentation not working #403

Open Dangujba opened 1 year ago

Dangujba commented 1 year ago

I am to new avalonEdit and using it for my custom language, the lanuguage does not use { } for block instead it uses some keyword use as end. So i tried to implement feature that will automatically complete block for user and indent cursor, now the completion is working but indentation not working.

When user type in

function name()

and press enter i want to have

function name()
    |
end function

instead i am getting

function name()
|
end function

| representing cursor

Below is implementation the insertion working perfectly

using System;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.Document;

public class AutocompleteHandler
{
    private TextEditor editor;
    private const string TriggerWord = "function";
    private const string ClosingBlock = "end function";

    public AutocompleteHandler(TextEditor editor)
    {
        this.editor = editor;
        this.editor.TextArea.PreviewKeyDown += TextArea_PreviewKeyDown;
    }

    private void TextArea_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Enter)
        {
            int currentOffset = editor.CaretOffset;
            TextDocument document = editor.Document;

            // Get the current line
            DocumentLine currentLine = document.GetLineByOffset(currentOffset);
            int lineOffset = currentLine.Offset;
            string lineText = document.GetText(currentLine);

            // Check if the line contains the trigger word followed by a valid function declaration
            if (IsValidFunctionDeclaration(lineText))
            {
                // Insert the closing block
                string indentation = GetLineIndentation(lineText);
                string closingBlockText = $"{indentation}{ClosingBlock}";

                // Update the content of the text in the editor

                document.Insert(currentOffset,  $"\n\n{closingBlockText}");

                // Adjust cursor position
                int cursorLine = currentLine.LineNumber + 1; // Line number of the inserted closing block
                int cursorColumn = indentation.Length + 4; // Cursor column position in the middle of the block

                editor.TextArea.Caret.Line = cursorLine;
                editor.TextArea.Caret.Column = cursorColumn;
                editor.TextArea.Caret.BringCaretToView();

                // Mark the event as handled
                e.Handled = true;
            }   
        }
    }

    private bool IsValidFunctionDeclaration(string lineText)
    {
        // Remove leading and trailing whitespaces
        lineText = lineText.Trim();

        // Check if the line starts with the trigger word "function"
        if (!lineText.StartsWith(TriggerWord, StringComparison.OrdinalIgnoreCase))
            return false;

        // Check if the line ends with either an empty parameter list "()"
        // or a parameter list with at least one character inside
        return lineText.EndsWith("()") || lineText.EndsWith(")");
    }

    private string GetLineIndentation(string lineText)
    {
        int indentationLength = lineText.TakeWhile(char.IsWhiteSpace).Count();
        return lineText.Substring(0, indentationLength);
    }

}