void SmartIndentCpp::OnEditorHook(cbEditor* ed, wxScintillaEvent& event) const
{

    // check if smart indent is enabled
    // check the event type and the currently set language
    // if it is not a CharAdded event or the language is not C/C++, D, or Java return

    if (!ed)
        return;

    if ( !SmartIndentEnabled() )
        return;

    wxEventType type = event.GetEventType();
    if ( type != wxEVT_SCI_CHARADDED )
        return;

    cbStyledTextCtrl* stc = ed->GetControl();
    if (!stc)
        return;

    wxString langname = Manager::Get()->GetEditorManager()->GetColourSet()->GetLanguageName(ed->GetLanguage());
    if ( langname != wxT("D") && (stc->GetLexer() != wxSCI_LEX_CPP || langname == wxT("Hitachi asm"))) return;

    ed->AutoIndentDone(); // we are responsible.

    const int pos = stc->GetCurrentPos();
    int currLine = stc->LineFromPosition(pos);

    if (currLine == 0)
        return;

    const wxChar ch = event.GetKey();

    if ( SelectionBraceCompletionEnabled() || stc->IsBraceShortcutActive() )
        DoSelectionBraceCompletion(stc, ch);

    DoSmartIndent(ed, ch);

    if ( BraceCompletionEnabled() )
        DoBraceCompletion(stc, ch);
}
void SmartIndentCpp::DoSmartIndent(cbEditor* ed, const wxChar &ch)const
{
    static bool autoIndentStart = false;
    static bool autoIndentDone = true;
    static int autoIndentLine = -1;
    static int autoIndentLineIndent = -1;

    static bool autoUnIndent = false; // for public: / case: / etc..
    static int autoUnIndentValue = -1;
    static int autoUnIndentLine = -1;

    cbStyledTextCtrl* stc = ed->GetControl();

    const int pos = stc->GetCurrentPos();
    // indent
    if ( (ch == _T('\n')) || ( (stc->GetEOLMode() == wxSCI_EOL_CR) && (ch == _T('\r')) ) )
    {
        stc->BeginUndoAction();
        // new-line: adjust indentation
        bool autoIndent = AutoIndentEnabled();
        bool smartIndent = SmartIndentEnabled();
        int currLine = stc->LineFromPosition(pos);
        if (autoIndent && currLine > 0)
        {
            wxString indent;
            if (stc->GetCurLine().Trim().IsEmpty())
            {
                // copy the indentation of the last non-empty line
                for (int i = currLine - 1; i >= 0; --i)
                {
                    const wxString& prevLineStr = stc->GetLine(i);
                    if (!(prevLineStr.IsEmpty() || prevLineStr[0] == _T('\n') || prevLineStr[0] == _T('\r')))
                    {
                        indent = ed->GetLineIndentString(i);
                        break;
                    }
                }
            }
            else
                indent = ed->GetLineIndentString(currLine - 1);
            if (smartIndent)
            {
                wxChar b = GetLastNonWhitespaceChar(ed);
                // if the last entered char before newline was an opening curly brace,
                // increase indentation level (the closing brace is handled in another block)

                if (!BraceIndent(stc, indent))
                {
                    if (b == _T('{'))
                    {
                        int nonblankpos;
                        wxChar c = GetNextNonWhitespaceCharOfLine(stc, pos, &nonblankpos);

                        if ( c != _T('}') )
                            Indent(stc, indent);
                        else
                        {
                            if ( pos != nonblankpos )
                            {
                                stc->SetCurrentPos(nonblankpos);
                                stc->DeleteBack();
                            }
                        }
                    }
                    else if (b == _T(':'))
                        Indent(stc, indent);
                }

            }
            stc->InsertText(pos, indent);
            stc->GotoPos(pos + indent.Length());
            stc->ChooseCaretX();
        }

        // smart indent
        if (smartIndent && currLine > 0)
        {
            if (!autoIndentDone)
            {
                bool valid = true;
                int line = stc->GetCurrentLine();
                if (line < autoIndentLine)
                    valid = false;
                else
                {
                    while (--line > autoIndentLine)
                    {
                        if (stc->GetLineIndentation(line) < autoIndentLineIndent)
                        {
                            valid = false;
                            break;
                        }
                    }
                }

                if (!valid)
                {
                    autoIndentStart = false;
                    autoIndentDone = true;
                    autoIndentLine = -1;
                    autoIndentLineIndent = -1;
                }
            }

            if (autoIndentDone)
            {
                const int ind_pos = stc->GetLineIndentPosition(currLine - 1);
                const wxString text = stc->GetTextRange(ind_pos, stc->WordEndPosition(ind_pos, true));
                if (   text == _T("if")
                    || text == _T("else")
                    || text == _T("for")
                    || text == _T("while")
                    || text == _T("do") )
                {
                    const wxChar non_ws_ch = GetLastNonWhitespaceChar(ed);
                    if (non_ws_ch != _T(';') && non_ws_ch != _T('}'))
                    {
                        autoIndentDone = false;
                        autoIndentLine = currLine - 1;
                        autoIndentLineIndent = stc->GetLineIndentation(currLine - 1);
                    }
                }
            }

            if (!autoIndentDone)
            {
                if (autoIndentStart)
                {
                    const wxChar non_ws_ch = GetLastNonWhitespaceChar(ed);
                    if (non_ws_ch == _T(';') || non_ws_ch == _T('}'))
                    {
                        stc->SetLineIndentation(currLine, autoIndentLineIndent);
                        stc->GotoPos(stc->GetLineEndPosition(currLine));

                        autoIndentStart = false;
                        autoIndentDone = true;
                        autoIndentLine = -1;
                        autoIndentLineIndent = -1;
                    }
                }
                else
                {
                    int lastLine = currLine;
                    while (--lastLine >= 0)
                    {
                        const int lineIndentPos = stc->GetLineIndentPosition(lastLine);
                        const int start = stc->WordStartPosition(lineIndentPos, true);
                        const int end = stc->WordEndPosition(lineIndentPos, true);
                        const wxString last = stc->GetTextRange(start, end);

                        if (   last == _T("if")
                            || last == _T("else")
                            || last == _T("for")
                            || last == _T("while")
                            || last == _T("do") )
                        {
                            const wxString text = stc->GetTextRange(lineIndentPos + last.Len(), pos);
                            int level = 0;
                            for (size_t i = 0; i < text.Len(); ++i)
                            {
                                if (text[i] == _T('('))
                                {
                                    const int style = stc->GetStyleAt(pos - text.Len() + i);
                                    if (   stc->IsString(style)
                                        || stc->IsCharacter(style)
                                        || stc->IsComment(style) )
                                    {
                                        continue;
                                    }
                                    ++level;
                                }
                                else if (text[i] == _T(')'))
                                {
                                    const int style = stc->GetStyleAt(pos - text.Len() + i);
                                    if (   stc->IsString(style)
                                        || stc->IsCharacter(style)
                                        || stc->IsComment(style) )
                                    {
                                        continue;
                                    }
                                    --level;
                                }
                            }

                            if (!level)
                            {
                                autoIndentStart = true;

                                int nonblankpos;
                                wxChar c = GetNextNonWhitespaceCharOfLine(stc, pos, &nonblankpos);
                                if (c == _T('}') && currLine == stc->LineFromPosition(nonblankpos))
                                {
                                    stc->NewLine();
                                    stc->GotoPos(pos);

                                    autoIndentStart = false;
                                    autoIndentDone = true;
                                    autoIndentLine = -1;
                                    autoIndentLineIndent = -1;
                                }

                                stc->Tab();
                            }

                            break;
                        }
                    }
                }
            }

            // smart un-indent
            if (autoUnIndent)
            {
                if ( GetLastNonWhitespaceChar(ed) == _T(':'))
                {
                    stc->SetLineIndentation(autoUnIndentLine, autoUnIndentValue);
                    stc->SetLineIndentation(currLine, autoUnIndentValue);
                    stc->Tab();
                }

                autoUnIndent = false;
                autoUnIndentValue = -1;
                autoUnIndentLine = -1;
            }
        }

        stc->EndUndoAction();
    }

    // unindent
    else if (ch == _T('{'))
    {
        if (autoIndentStart)
        {
            bool valid = true;
            const int start = stc->PositionFromLine(autoIndentLine);
            const wxString text = stc->GetTextRange(start, pos);
            if (text.Find(_T('{')) != int(text.Len() - 1))
                valid = false;
            else
            {
                int line = stc->GetCurrentLine();
                if (line < autoIndentLine)
                    valid = false;
                else
                {
                    while (--line > autoIndentLine)
                    {
                        if (stc->GetLineIndentation(line) < autoIndentLineIndent)
                        {
                            valid = false;
                            break;
                        }
                    }
                }
            }

            if (valid)
            {
                stc->BeginUndoAction();
                stc->SetLineIndentation(stc->GetCurrentLine(), autoIndentLineIndent);
                stc->EndUndoAction();
            }

            autoIndentStart = false;
            autoIndentDone = true;
            autoIndentLine = -1;
            autoIndentLineIndent = -1;
        }
    }

    // unindent
    else if (ch == _T('}'))
    {
        bool smartIndent = Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/smart_indent"), true);
        if ( smartIndent && ( (stc->GetLexer() == wxSCI_LEX_CPP) || (stc->GetLexer() == wxSCI_LEX_D) ) )
        {
            stc->BeginUndoAction();
            // undo block indentation, if needed
            wxString str = stc->GetLine(stc->GetCurrentLine());
            str.Trim(false);
            str.Trim(true);
            if (str.Matches(_T("}")))
            {
                // just the brace here; unindent
                // find opening brace (skipping nested blocks)
                int cur_pos = stc->GetCurrentPos() - 2;
                cur_pos = FindBlockStart(stc, cur_pos, _T('{'), _T('}'));
                if (cur_pos != -1)
                {
                    wxString indent = ed->GetLineIndentString(stc->LineFromPosition(cur_pos));
                    indent << _T('}');
                    stc->DelLineLeft();
                    stc->DelLineRight();
                    cur_pos = stc->GetCurrentPos();
                    stc->InsertText(cur_pos, indent);
                    stc->GotoPos(cur_pos + indent.Length());
                    stc->ChooseCaretX();
                }
            }
            stc->EndUndoAction();
        }
    }

    // unindent
    else if (ch == _T(':'))
    {
        bool smartIndent = Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/smart_indent"), true);
        if (smartIndent && stc->GetLexer() == wxSCI_LEX_CPP && !autoUnIndent)
        {
            const int curLine = stc->GetCurrentLine();
            const int li_pos = stc->GetLineIndentPosition(curLine);
            const wxString text = stc->GetTextRange(li_pos, stc->WordEndPosition(li_pos, true));
            if (   text == _T("public")
                || text == _T("protected")
                || text == _T("private")
                || text == _T("case")
                || text == _T("default") )
            {
                const bool isSwitch = (text == _T("case") || text == _T("default"));
                int lastLine = curLine;
                int lastLineIndent = -1;
                while (--lastLine >= 0)
                {
                    const int lineIndentPos = stc->GetLineIndentPosition(lastLine);
                    const int start = stc->WordStartPosition(lineIndentPos, true);
                    const int end = stc->WordEndPosition(lineIndentPos, true);

                    const wxString last = stc->GetTextRange(start, end);
                    if (last.IsEmpty())
                        continue;

                    if (isSwitch)
                    {
                        if (last == _T("case"))
                        {
                            lastLineIndent = stc->GetLineIndentation(lastLine);
                            break;
                        }
                        else if (last == _T("switch"))
                            break;
                    }
                    else
                    {
                        if (   last == _T("public")
                            || last == _T("protected")
                            || last == _T("private") )
                        {
                            lastLineIndent = stc->GetLineIndentation(lastLine);
                            break;
                        }
                        else if (last == _T("class"))
                            break;
                    }
                }

                if (lastLineIndent != -1)
                {
                    autoUnIndent = true;
                    autoUnIndentValue = lastLineIndent;
                    autoUnIndentLine = curLine;
                }
                else
                {
                    const int curLineIndent = stc->GetLineIndentation(curLine);
                    const int tabWidth = stc->GetTabWidth();
                    if (curLineIndent >= tabWidth)
                    {
                        autoUnIndent = true;
                        autoUnIndentValue = curLineIndent - tabWidth;
                        autoUnIndentLine = curLine;
                    }
                }
            }
        }
    }

}
void SmartIndentFortran::OnEditorHook(cbEditor* ed, wxScintillaEvent& event) const
{
    // check if smart indent is enabled
    // check the event type and the currently set language
    // if it is not a CharAdded event or the language is not Fortran return

    if (!ed)
        return;

    if ( !SmartIndentEnabled() )
        return;

    wxEventType type = event.GetEventType();
    if ( type != wxEVT_SCI_CHARADDED )
        return;

    cbStyledTextCtrl* stc = ed->GetControl();
    if (!stc)
        return;

    wxString langname = Manager::Get()->GetEditorManager()->GetColourSet()->GetLanguageName(ed->GetLanguage());
    if ( langname != wxT("Fortran") && langname != wxT("Fortran77") ) return;

    ed->AutoIndentDone(); // we are responsible.

    const int pos = stc->GetCurrentPos();
    wxChar ch = event.GetKey();

    if ( (ch == _T('\n')) || ( (stc->GetEOLMode() == wxSCI_EOL_CR) && (ch == _T('\r')) ) )
    {
        const bool autoIndent = AutoIndentEnabled();

        stc->BeginUndoAction();

        int currLine = stc->LineFromPosition(pos);

        // auto indent
        if (autoIndent && currLine > 0)
        {
            wxString indent = ed->GetLineIndentString(currLine - 1);
            stc->InsertText(pos, indent);
            stc->GotoPos(pos + indent.Length());
            stc->ChooseCaretX();
        }

        // smart indent
        bool smartIndent = SmartIndentEnabled();
        if (smartIndent && currLine > 0)
        {
            int start = stc->GetLineIndentPosition(currLine - 1);
            int endt  = stc->WordEndPosition(start, true);
            wxString text = stc->GetTextRange(start, endt).Lower();
            wxString lineText = stc->GetLine(currLine - 1).BeforeFirst('!').Lower();
            wxString lastText = lineText.AfterLast(')').Trim().Trim(false);
            wxString secText = lineText.Trim(false).Mid(text.Length()).Trim(false);
            if (  (text == _T("if") && lastText == _T("then"))
                || text == _T("else")
                || text == _T("elseif")
                || text == _T("enum")
                ||(text == _T("where") && lastText.IsEmpty())
                || text == _T("elsewhere")
                || text == _T("block")
                || text == _T("blockdata")
                ||(text == _T("forall") && lastText.IsEmpty())
                || text == _T("while")
                || text == _T("case")
                || text == _T("associate")
                || text == _T("block")
                || text == _T("critical")
                || text == _T("do")
                ||(text == _T("type") && !secText.StartsWith(_T("(")))
                || text == _T("program")
                || text == _T("function")
                || text == _T("subroutine")
                || text == _T("interface")
                ||(    text == _T("module")
                   && !secText.StartsWith(_T("procedure "))
                   && !secText.StartsWith(_T("procedure:")) ) )
            {
                stc->Tab();
            }
        }

        stc->EndUndoAction();
    }

    bool braceCompleted = false;
    if ( SelectionBraceCompletionEnabled() || stc->IsBraceShortcutActive() )
        braceCompleted = stc->DoSelectionBraceCompletion(ch);
    if (!braceCompleted && BraceCompletionEnabled())
        stc->DoBraceCompletion(ch);
}