jacobslusser / ScintillaNET

A Windows Forms control, wrapper, and bindings for the Scintilla text editor.
MIT License
963 stars 243 forks source link

Can't do indent guide highlighting without brace matching #472

Closed lafrank closed 4 years ago

lafrank commented 4 years ago

For Python there should be no brace matching to perform indentation guide highlighting, but I caould not find a way to enable the latter when brace matching is disabled.

That is, the code below has no effect whatever column number is set for HighLighGuide: scintilla.BraceHighlight(Scintilla.InvalidPosition, Scintilla.InvalidPosition); scintilla.HighlightGuide = caretPos;

xv commented 4 years ago

The property HighlightGuide is strictly tied to brace matching.

This is the code for HighLightGuide from Scintilla.cs:

public int HighlightGuide
{
    get
    {
        return DirectMessage(NativeMethods.SCI_GETHIGHLIGHTGUIDE).ToInt32();
    }
    set
    {
        value = Helpers.ClampMin(value, 0);
        DirectMessage(NativeMethods.SCI_SETHIGHLIGHTGUIDE, new IntPtr(value));
    }
}

Here's the documentation for SCI_GETHIGHLIGHTGUIDE & SCI_SETHIGHLIGHTGUIDE:

SCI_SETHIGHLIGHTGUIDE(position column)
SCI_GETHIGHLIGHTGUIDE → position
When brace highlighting occurs, the indentation guide corresponding to the braces may be highlighted with the brace highlighting style, STYLE_BRACELIGHT (34). Set column to 0 to cancel this highlight.

AFAIK, there's no built-in way to highlight guides without matching braces.

lafrank commented 4 years ago

So how do I provide indent guide highlighting for Python ?

That would be extremely useful since Python is heavily depending on indentation but brace-matching has no added value here due to language syntax. Just look at below example and imagine one has to find the right position for the next line :

image

Any idea how to resolve this ? Thank you.

xv commented 4 years ago

Looking at SciTE's (Scintilla's demonstrator editor) way of highlighting Python indent guides, it seems based on a combination of two things:

  1. Having the caret placed on colons; and
  2. Having the lexer folding enabled.

The following method is a very rough translation of SciTE's code to highlight Python's indent guides:

static bool IsPythonColon(Scintilla sci, int pos)
{
    return sci.Lexer == Lexer.Python &&
           sci.GetStyleAt(pos) == sci.Styles[Style.Python.Operator].Index &&
           sci.GetCharAt(pos) == ':';
}

static void HighlightPyIndentGuide(Scintilla sci)
{
    var caretPos = sci.CurrentPosition;
    var braceAtCaret = -1;

    // Check if caret is before or after the colon and set pos
    if (caretPos > 0 && IsPythonColon(sci, caretPos - 1))
        braceAtCaret = caretPos - 1;
    else if (IsPythonColon(sci, caretPos))
        braceAtCaret = caretPos;

    // Colon exists
    if (braceAtCaret >= 0)
    {
        var lineStart = sci.LineFromPosition(braceAtCaret);
        var lineMaxSubord = sci.Lines[lineStart].GetLastChild(-1);
        var braceOpposite = sci.Lines[lineMaxSubord].EndPosition;

        var indentPos = sci.Lines[lineStart].IndentPosition;
        var indentPosNext = sci.Lines[lineStart + 1].IndentPosition;

        var columnAtCaret = sci.GetColumn(indentPos);
        var columnOpposite = sci.GetColumn(braceOpposite);

        // Don't highlight colon if at a parent-level indent (no guides)
        if (columnAtCaret != 0)
            sci.BraceHighlight(braceAtCaret, braceOpposite);

        var caretColumnNext = sci.GetColumn(indentPosNext);
        var indentSize = sci.IndentWidth;

        if ((caretColumnNext - indentSize) > 1)
            columnAtCaret = caretColumnNext - indentSize;

        if (columnOpposite == 0)
            columnOpposite = columnAtCaret;

        sci.HighlightGuide = Math.Min(columnAtCaret, columnOpposite);
    }
    else
    {
        sci.BraceHighlight(Scintilla.InvalidPosition, Scintilla.InvalidPosition);
        sci.HighlightGuide = 0;
    }
}

The method relies on lineMaxSubord to find the line where the indent guide ends, which in turn relies on the folding mechanism of the lexer to provide the correct folding levels. Therefore, you should only use it when folding is enabled, otherwise you'll observe incorrect highlighting behaviour.

You should carefully examine SciTE's code to learn form. This translation is buggy and it's merely just a push-start. It doesn't take into account colons that aren't used to start an indent suite, such as:

    # setting the caret on the colons would result in incorrectly
    # highlighting the indent guide
    dict = {
        'brand': "prada",
        'price': "treefiddy",
    }
lafrank commented 4 years ago

Thank you xv, much appreciate your help with this !