Menu

A quick auto-indent/smart-indent hack

Monty
2006-10-05
2012-11-13
  • Monty

    Monty - 2006-10-05

    Hi all,

    I've never modified on an open source project before, but Notepad++ was pretty easy to get working with the Visual Studio files. The code is quite easy to read, even with minimal comments. Good job!

    Anyway, I loved npp as a text editor, but the only thing missing for me was smart-indentation for programming. So I made a few modifications and it seems to work quite well. It only works with braces (so it won't work for def in Python, if/for/while statements without a {} block in C, html tags, etc), but for Perl and most cases in C/C++, Java, LISP, etc it works quite well.

    Now I have a smart-indenting, code-folding, MDI text editor that's perfect for Perl scripts. UltraEdit had a few quirks in the syntax highlighting, and isn't freeware either, let alone open source. Thank you npp!

    As you can probably tell, I leveraged the brace-matching code to get this to work. It's not rigourously tested, but for anyone else who's interested, here's what you need to do:

    1. Add the private variable "bool _checkIndent;" to the Notepad_plus class

    2. Modify the "MaintainIndentation(char ch)" function as follows:

    void Notepad_plus::MaintainIndentation(char ch)
    {
        int eolMode = int(_pEditView->execute(SCI_GETEOLMODE));
        int curLine = int(_pEditView->getCurrentLineNumber());
        int lastLine = curLine - 1;
        int indentAmount = 0;

        // Flag the line to be checked for smart-indentation if this
        // character closes a block and has no other text on the line
        if (strchr("])}", ch)) {
            int len = _pEditView->getLineLength(curLine) + 1;
            char *line = new char[len];
            _pEditView->execute(SCI_GETLINE, curLine, reinterpret_cast<LPARAM>(line));

            _checkIndent = true;
            for (int a = 0; a < len; a++)
                if (strchr("\0\r\n\t ])}", line[a]) != NULL)
                    _checkIndent = false;
        }

        if (((eolMode == SC_EOL_CRLF || eolMode == SC_EOL_LF) && ch == '\n') ||
                (eolMode == SC_EOL_CR && ch == '\r'))
        {
            while (lastLine >= 0 && _pEditView->getLineLength(lastLine) == 0)
                lastLine--;

            if (lastLine >= 0) {
                indentAmount = _pEditView->getLineIndent(lastLine);
            }

            // Find the position of the previous character
            int pos = _pEditView->execute(SCI_GETCURRENTPOS) - ((eolMode == SC_EOL_CRLF) ? 3 : 2);
            // If an opening brace preceded the newline, smart-indent outwards
            if ((pos > 0) && strchr("[({", ch = char(_pEditView->execute(SCI_GETCHARAT, pos))))
                indentAmount += int(_pEditView->execute(SCI_GETTABWIDTH));

            if (indentAmount > 0) {
                _pEditView->setLineIndent(curLine, indentAmount);
            }
        }
    }

    3. Modify the "braceMatch()" function as follows:

    void Notepad_plus::braceMatch()
    {
        int braceAtCaret = -1;
        int braceOpposite = -1;
        findMatchingBracePos(braceAtCaret, braceOpposite);

        if ((braceAtCaret != -1) && (braceOpposite == -1))
        {
            _pEditView->execute(SCI_BRACEBADLIGHT, braceAtCaret);
            _pEditView->execute(SCI_SETHIGHLIGHTGUIDE);
        }
        else
        {
            _pEditView->execute(SCI_BRACEHIGHLIGHT, braceAtCaret, braceOpposite);

            // If we're flagged as closing a block, adjust the indentation
            // to match that of where the block opened
            if (_checkIndent && (braceOpposite >= 0)) {
                int curLine = int(_pEditView->getCurrentLineNumber());
                int prevLine = int(_pEditView->execute(SCI_LINEFROMPOSITION, braceOpposite));
                int indentAmount = _pEditView->getLineIndent(prevLine);
                _pEditView->setLineIndent(curLine, indentAmount);
            }
            _checkIndent = false;

            if (_pEditView->isShownIndentGuide())
            {
                int columnAtCaret = int(_pEditView->execute(SCI_GETCOLUMN, braceAtCaret));
                int columnOpposite = int(_pEditView->execute(SCI_GETCOLUMN, braceOpposite));
                _pEditView->execute(SCI_SETHIGHLIGHTGUIDE, (columnAtCaret < columnOpposite)?columnAtCaret:columnOpposite);
            }
        }

        enableCommand(IDM_SEARCH_GOTOMATCHINGBRACE, (braceAtCaret != -1) && (braceOpposite != -1), MENU | TOOLBAR);
    }

    It might be useful to put a separate "Smart-Indentation" checkbox in the settings dialog, but I couldn't be bothered. Hopefully some of you will find this useful. I'll be happy to send a binary to anyone who wants it.

    Enjoy!

     
    • Don HO

      Don HO - 2006-10-06

      I just tested your modification, the smart indentatation *for brace* works well. This feature will be included in the next release, and perhaps, I'll enhance it for "if", "while"... statement if I have the time.

      Thank you.

      Don

       
      • Nobody/Anonymous

        Hi there,

        is there now a way to activate smart-indent?

        Andreas

         
      • mcfang

        mcfang - 2008-08-13

        Is this feature included? I can't seem to find anything about it.

         
    • Harry

      Harry - 2008-08-22

      Im trying to write a plugin that does this, which shouldnt take too long to make usable

       
    • Monty

      Monty - 2006-10-05

      One little correction:

      You need "delete[] line;" in the "MaintainIndentation()" function, or there will be a memory leak. (Oops.)

       
    • Monty

      Monty - 2006-10-15

      You're welcome, Don.

      I'm actually working on a plugin to handle all this so it may not be necessary to include it, and it'll include language specific stuff as well.

      I've been really busy for the past couple of weeks so I couldn't finish it, but I'll try again soon. I want to make a good framework that works for most languages and even identifies and handles multiline statements correctly. A bit tricky, but I think it's doable.

      (BTW, isn't it a shame that the SourceForge forums don't supports tabs or fixed width font for code fragments?)

       
    • Fotis Gimian

      Fotis Gimian - 2008-08-22

      I have just been goin through the code and I'm afraid it is not there as of 5.0.3.

      Cheers
      Fots

       
MongoDB Logo MongoDB