Changeset View
Changeset View
Standalone View
Standalone View
src/document/katedocument.cpp
Show First 20 Lines • Show All 91 Lines • ▼ Show 20 Line(s) | 3018 | if (view->config()->autoBrackets() && chars.size() == 1) { | |||
---|---|---|---|---|---|
3029 | if (m_currentAutobraceClosingChar == typedChar && m_currentAutobraceRange) { | 3029 | if (m_currentAutobraceClosingChar == typedChar && m_currentAutobraceRange) { | ||
3030 | // do nothing | 3030 | // do nothing | ||
3031 | m_currentAutobraceRange.reset(nullptr); | 3031 | m_currentAutobraceRange.reset(nullptr); | ||
3032 | view->cursorRight(); | 3032 | view->cursorRight(); | ||
3033 | return true; | 3033 | return true; | ||
3034 | } | 3034 | } | ||
3035 | } | 3035 | } | ||
3036 | 3036 | | |||
3037 | editStart(); | ||||
3038 | | ||||
3037 | /** | 3039 | /** | ||
3038 | * selection around => special handling if we want to add auto brackets | 3040 | * special handling if we want to add auto brackets to a selection | ||
3039 | */ | 3041 | */ | ||
3040 | if (view->selection() && !closingBracket.isNull()) { | 3042 | if (view->selection() && !closingBracket.isNull()) { | ||
3041 | /** | 3043 | std::unique_ptr<KTextEditor::MovingRange> selectionRange(newMovingRange(view->selectionRange())); | ||
3042 | * add bracket at start + end of the selection | 3044 | const int startLine = qMax(0, selectionRange->start().line()); | ||
3043 | */ | 3045 | const int endLine = qMin(selectionRange->end().line(), lastLine()); | ||
3044 | KTextEditor::Cursor oldCur = view->cursorPosition(); | 3046 | const bool blockMode = view->blockSelection() && (startLine != endLine); | ||
3045 | insertText(view->selectionRange().start(), chars); | 3047 | if (blockMode) { | ||
3046 | view->slotTextInserted(view, oldCur, chars); | 3048 | // Add brackets to each line of the block | ||
3049 | const KTextEditor::Range workingRange = view->selectionRange(); | ||||
3050 | for (int line = startLine; line <= endLine; ++line) { | ||||
3051 | const KTextEditor::Range r(rangeOnLine(workingRange, line)); | ||||
3052 | insertText(r.end(), QString(closingBracket)); | ||||
3053 | insertText(r.start(), chars); | ||||
3054 | } | ||||
3055 | } else { | ||||
3056 | // No block, just add to start & end of selection | ||||
3057 | insertText(selectionRange->end(), QString(closingBracket)); | ||||
3058 | insertText(selectionRange->start(), chars); | ||||
3059 | } | ||||
3047 | 3060 | | |||
3048 | view->setCursorPosition(view->selectionRange().end()); | 3061 | // Refesh selection | ||
3049 | oldCur = view->cursorPosition(); | 3062 | view->setSelection(selectionRange->toRange()); | ||
3050 | insertText(view->selectionRange().end(), QString(closingBracket)); | 3063 | view->setCursorPosition(selectionRange->end()); | ||
3051 | view->slotTextInserted(view, oldCur, QString(closingBracket)); | | |||
3052 | 3064 | | |||
3053 | /** | 3065 | editEnd(); | ||
3054 | * expand selection | 3066 | return true; | ||
3055 | */ | | |||
3056 | view->setSelection(KTextEditor::Range(view->selectionRange().start() + Cursor{0, 1}, | | |||
3057 | view->cursorPosition() - Cursor{0, 1})); | | |||
3058 | view->setCursorPosition(view->selectionRange().start()); | | |||
3059 | } | 3067 | } | ||
3060 | 3068 | | |||
3061 | /** | 3069 | /** | ||
3062 | * else normal handling | 3070 | * normal handling | ||
3063 | */ | 3071 | */ | ||
3064 | else { | 3072 | if (!view->config()->persistentSelection() && view->selection()) { | ||
3065 | editStart(); | 3073 | view->removeSelectedText(); | ||
3066 | 3074 | } | |||
3067 | if (!view->config()->persistentSelection() && view->selection()) { | | |||
3068 | view->removeSelectedText(); | | |||
3069 | } | | |||
3070 | 3075 | | |||
3071 | const KTextEditor::Cursor oldCur(view->cursorPosition()); | 3076 | const KTextEditor::Cursor oldCur(view->cursorPosition()); | ||
3072 | | ||||
3073 | const bool multiLineBlockMode = view->blockSelection() && view->selection(); | | |||
3074 | if (view->currentInputMode()->overwrite()) { | | |||
3075 | // blockmode multiline selection case: remove chars in every line | | |||
3076 | const KTextEditor::Range selectionRange = view->selectionRange(); | | |||
3077 | const int startLine = multiLineBlockMode ? qMax(0, selectionRange.start().line()) : view->cursorPosition().line(); | | |||
3078 | const int endLine = multiLineBlockMode ? qMin(selectionRange.end().line(), lastLine()) : startLine; | | |||
3079 | const int virtualColumn = toVirtualColumn(multiLineBlockMode ? selectionRange.end() : view->cursorPosition()); | | |||
3080 | | ||||
3081 | for (int line = endLine; line >= startLine; --line) { | | |||
3082 | Kate::TextLine textLine = m_buffer->plainLine(line); | | |||
3083 | Q_ASSERT(textLine); | | |||
3084 | const int column = fromVirtualColumn(line, virtualColumn); | | |||
3085 | KTextEditor::Range r = KTextEditor::Range(KTextEditor::Cursor(line, column), qMin(chars.length(), | | |||
3086 | textLine->length() - column)); | | |||
3087 | | ||||
3088 | // replace mode needs to know what was removed so it can be restored with backspace | | |||
3089 | if (oldCur.column() < lineLength(line)) { | | |||
3090 | QChar removed = characterAt(KTextEditor::Cursor(line, column)); | | |||
3091 | view->currentInputMode()->overwrittenChar(removed); | | |||
3092 | } | | |||
3093 | 3077 | | |||
3094 | removeText(r); | 3078 | const bool multiLineBlockMode = view->blockSelection() && view->selection(); | ||
3095 | } | 3079 | if (view->currentInputMode()->overwrite()) { | ||
3096 | } | 3080 | // blockmode multiline selection case: remove chars in every line | ||
3081 | const KTextEditor::Range selectionRange = view->selectionRange(); | ||||
3082 | const int startLine = multiLineBlockMode ? qMax(0, selectionRange.start().line()) : view->cursorPosition().line(); | ||||
3083 | const int endLine = multiLineBlockMode ? qMin(selectionRange.end().line(), lastLine()) : startLine; | ||||
3084 | const int virtualColumn = toVirtualColumn(multiLineBlockMode ? selectionRange.end() : view->cursorPosition()); | ||||
3097 | 3085 | | |||
3098 | if (multiLineBlockMode) { | 3086 | for (int line = endLine; line >= startLine; --line) { | ||
3099 | KTextEditor::Range selectionRange = view->selectionRange(); | 3087 | Kate::TextLine textLine = m_buffer->plainLine(line); | ||
3100 | const int startLine = qMax(0, selectionRange.start().line()); | 3088 | Q_ASSERT(textLine); | ||
3101 | const int endLine = qMin(selectionRange.end().line(), lastLine()); | 3089 | const int column = fromVirtualColumn(line, virtualColumn); | ||
3102 | const int column = toVirtualColumn(selectionRange.end()); | 3090 | KTextEditor::Range r = KTextEditor::Range(KTextEditor::Cursor(line, column), qMin(chars.length(), | ||
3103 | for (int line = endLine; line >= startLine; --line) { | 3091 | textLine->length() - column)); | ||
3104 | editInsertText(line, fromVirtualColumn(line, column), chars); | 3092 | | ||
3093 | // replace mode needs to know what was removed so it can be restored with backspace | ||||
3094 | if (oldCur.column() < lineLength(line)) { | ||||
3095 | QChar removed = characterAt(KTextEditor::Cursor(line, column)); | ||||
3096 | view->currentInputMode()->overwrittenChar(removed); | ||||
3105 | } | 3097 | } | ||
3106 | int newSelectionColumn = toVirtualColumn(view->cursorPosition()); | | |||
3107 | selectionRange.setRange(KTextEditor::Cursor(selectionRange.start().line(), fromVirtualColumn(selectionRange.start().line(), newSelectionColumn)) | | |||
3108 | , KTextEditor::Cursor(selectionRange.end().line(), fromVirtualColumn(selectionRange.end().line(), newSelectionColumn))); | | |||
3109 | view->setSelection(selectionRange); | | |||
3110 | } else { | | |||
3111 | chars = eventuallyReplaceTabs(view->cursorPosition(), chars); | | |||
3112 | insertText(view->cursorPosition(), chars); | | |||
3113 | } | | |||
3114 | 3098 | | |||
3115 | /** | 3099 | removeText(r); | ||
3116 | * auto bracket handling for newly inserted text | | |||
3117 | * we inserted a bracket? | | |||
3118 | * => add the matching closing one to the view + input chars | | |||
3119 | * try to preserve the cursor position | | |||
3120 | */ | | |||
3121 | bool skipAutobrace = closingBracket == QLatin1Char('\''); | | |||
3122 | if (highlight() && skipAutobrace) { | | |||
3123 | // skip adding ' in spellchecked areas, because those are text | | |||
3124 | skipAutobrace = highlight()->spellCheckingRequiredForLocation(this, view->cursorPosition() - Cursor{0, 1}); | | |||
3125 | } | 3100 | } | ||
3101 | } | ||||
3126 | 3102 | | |||
3127 | const auto cursorPos(view->cursorPosition()); | 3103 | if (multiLineBlockMode) { | ||
3128 | if (!skipAutobrace && (closingBracket == QLatin1Char('\''))) { | 3104 | KTextEditor::Range selectionRange = view->selectionRange(); | ||
3129 | // skip auto quotes when these looks already balanced, bug 405089 | 3105 | const int startLine = qMax(0, selectionRange.start().line()); | ||
3130 | Kate::TextLine textLine = m_buffer->plainLine(cursorPos.line()); | 3106 | const int endLine = qMin(selectionRange.end().line(), lastLine()); | ||
3131 | // RegEx match quote, but not excaped quote, thanks to https://stackoverflow.com/a/11819111 | 3107 | const int column = toVirtualColumn(selectionRange.end()); | ||
3132 | const int count = textLine->text().left(cursorPos.column()).count(QRegularExpression(QStringLiteral("(?<!\\\\)(?:\\\\\\\\)*\\\'"))); | 3108 | for (int line = endLine; line >= startLine; --line) { | ||
3133 | skipAutobrace = (count % 2 == 0) ? true : false; | 3109 | editInsertText(line, fromVirtualColumn(line, column), chars); | ||
3134 | } | | |||
3135 | if (!skipAutobrace && (closingBracket == QLatin1Char('\"'))) { | | |||
3136 | // ...same trick for double quotes | | |||
3137 | Kate::TextLine textLine = m_buffer->plainLine(cursorPos.line()); | | |||
3138 | const int count = textLine->text().left(cursorPos.column()).count(QRegularExpression(QStringLiteral("(?<!\\\\)(?:\\\\\\\\)*\\\""))); | | |||
3139 | skipAutobrace = (count % 2 == 0) ? true : false; | | |||
3140 | } | 3110 | } | ||
3111 | int newSelectionColumn = toVirtualColumn(view->cursorPosition()); | ||||
3112 | selectionRange.setRange(KTextEditor::Cursor(selectionRange.start().line(), fromVirtualColumn(selectionRange.start().line(), newSelectionColumn)) | ||||
3113 | , KTextEditor::Cursor(selectionRange.end().line(), fromVirtualColumn(selectionRange.end().line(), newSelectionColumn))); | ||||
3114 | view->setSelection(selectionRange); | ||||
3115 | } else { | ||||
3116 | chars = eventuallyReplaceTabs(view->cursorPosition(), chars); | ||||
3117 | insertText(view->cursorPosition(), chars); | ||||
3118 | } | ||||
3141 | 3119 | | |||
3142 | if (!closingBracket.isNull() && !skipAutobrace) { | 3120 | /** | ||
3143 | // add bracket to the view | 3121 | * auto bracket handling for newly inserted text | ||
3144 | const auto nextChar = view->document()->text({cursorPos, cursorPos + Cursor{0, 1}}).trimmed(); | 3122 | * we inserted a bracket? | ||
3145 | if (nextChar.isEmpty() || !nextChar.at(0).isLetterOrNumber()) { | 3123 | * => add the matching closing one to the view + input chars | ||
3146 | insertText(view->cursorPosition(), QString(closingBracket)); | 3124 | * try to preserve the cursor position | ||
3147 | const auto insertedAt(view->cursorPosition()); | 3125 | */ | ||
3148 | view->setCursorPosition(cursorPos); | 3126 | bool skipAutobrace = closingBracket == QLatin1Char('\''); | ||
3149 | m_currentAutobraceRange.reset(newMovingRange({cursorPos - Cursor{0, 1}, insertedAt}, | 3127 | if (highlight() && skipAutobrace) { | ||
3150 | KTextEditor::MovingRange::DoNotExpand)); | 3128 | // skip adding ' in spellchecked areas, because those are text | ||
3151 | connect(view, &View::cursorPositionChanged, | 3129 | skipAutobrace = highlight()->spellCheckingRequiredForLocation(this, view->cursorPosition() - Cursor{0, 1}); | ||
3152 | this, &DocumentPrivate::checkCursorForAutobrace, Qt::UniqueConnection); | 3130 | } | ||
3153 | 3131 | | |||
3154 | // add bracket to chars inserted! needed for correct signals + indent | 3132 | const auto cursorPos(view->cursorPosition()); | ||
3155 | chars.append(closingBracket); | 3133 | if (!skipAutobrace && (closingBracket == QLatin1Char('\''))) { | ||
3156 | } | 3134 | // skip auto quotes when these looks already balanced, bug 405089 | ||
3157 | m_currentAutobraceClosingChar = closingBracket; | 3135 | Kate::TextLine textLine = m_buffer->plainLine(cursorPos.line()); | ||
3136 | // RegEx match quote, but not excaped quote, thanks to https://stackoverflow.com/a/11819111 | ||||
3137 | const int count = textLine->text().left(cursorPos.column()).count(QRegularExpression(QStringLiteral("(?<!\\\\)(?:\\\\\\\\)*\\\'"))); | ||||
3138 | skipAutobrace = (count % 2 == 0) ? true : false; | ||||
3139 | } | ||||
3140 | if (!skipAutobrace && (closingBracket == QLatin1Char('\"'))) { | ||||
3141 | // ...same trick for double quotes | ||||
3142 | Kate::TextLine textLine = m_buffer->plainLine(cursorPos.line()); | ||||
3143 | const int count = textLine->text().left(cursorPos.column()).count(QRegularExpression(QStringLiteral("(?<!\\\\)(?:\\\\\\\\)*\\\""))); | ||||
3144 | skipAutobrace = (count % 2 == 0) ? true : false; | ||||
3145 | } | ||||
3146 | | ||||
3147 | if (!closingBracket.isNull() && !skipAutobrace ) { | ||||
3148 | // add bracket to the view | ||||
3149 | const auto nextChar = view->document()->text({cursorPos, cursorPos + Cursor{0, 1}}).trimmed(); | ||||
3150 | if (nextChar.isEmpty() || !nextChar.at(0).isLetterOrNumber()) { | ||||
3151 | insertText(view->cursorPosition(), QString(closingBracket)); | ||||
3152 | const auto insertedAt(view->cursorPosition()); | ||||
3153 | view->setCursorPosition(cursorPos); | ||||
3154 | m_currentAutobraceRange.reset(newMovingRange({cursorPos - Cursor{0, 1}, insertedAt}, | ||||
3155 | KTextEditor::MovingRange::DoNotExpand)); | ||||
3156 | connect(view, &View::cursorPositionChanged, | ||||
3157 | this, &DocumentPrivate::checkCursorForAutobrace, Qt::UniqueConnection); | ||||
3158 | | ||||
3159 | // add bracket to chars inserted! needed for correct signals + indent | ||||
3160 | chars.append(closingBracket); | ||||
3158 | } | 3161 | } | ||
3162 | m_currentAutobraceClosingChar = closingBracket; | ||||
3163 | } | ||||
3159 | 3164 | | |||
3160 | // end edit session here, to have updated HL in userTypedChar! | 3165 | // end edit session here, to have updated HL in userTypedChar! | ||
3161 | editEnd(); | 3166 | editEnd(); | ||
3162 | | ||||
3163 | // trigger indentation | | |||
3164 | KTextEditor::Cursor b(view->cursorPosition()); | | |||
3165 | m_indenter->userTypedChar(view, b, chars.isEmpty() ? QChar() : chars.at(chars.length() - 1)); | | |||
3166 | 3167 | | |||
3167 | /** | 3168 | // trigger indentation | ||
3168 | * inform the view about the original inserted chars | 3169 | KTextEditor::Cursor b(view->cursorPosition()); | ||
3169 | */ | 3170 | m_indenter->userTypedChar(view, b, chars.isEmpty() ? QChar() : chars.at(chars.length() - 1)); | ||
3170 | view->slotTextInserted(view, oldCur, chars); | | |||
3171 | } | | |||
3172 | 3171 | | |||
3173 | /** | 3172 | /** | ||
3174 | * be done | 3173 | * inform the view about the original inserted chars | ||
3175 | */ | 3174 | */ | ||
3175 | view->slotTextInserted(view, oldCur, chars); | ||||
3176 | | ||||
3176 | return true; | 3177 | return true; | ||
3177 | } | 3178 | } | ||
3178 | 3179 | | |||
3179 | void KTextEditor::DocumentPrivate::checkCursorForAutobrace(KTextEditor::View*, const KTextEditor::Cursor& newPos) { | 3180 | void KTextEditor::DocumentPrivate::checkCursorForAutobrace(KTextEditor::View*, const KTextEditor::Cursor& newPos) { | ||
3180 | if ( m_currentAutobraceRange && ! m_currentAutobraceRange->toRange().contains(newPos) ) { | 3181 | if ( m_currentAutobraceRange && ! m_currentAutobraceRange->toRange().contains(newPos) ) { | ||
3181 | m_currentAutobraceRange.clear(); | 3182 | m_currentAutobraceRange.clear(); | ||
3182 | } | 3183 | } | ||
3183 | } | 3184 | } | ||
▲ Show 20 Lines • Show All 91 Lines • Show Last 20 Lines |