File indexing completed on 2024-05-12 15:46:28
0001 /* 0002 SPDX-FileCopyrightText: 2008 Erlend Hamberg <ehamberg@gmail.com> 0003 SPDX-FileCopyrightText: 2011 Svyatoslav Kuzmich <svatoslav1@gmail.com> 0004 SPDX-FileCopyrightText: 2012-2013 Simon St James <kdedevel@etotheipiplusone.com> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include <document/katedocument.h> 0010 #include <inputmode/kateviinputmode.h> 0011 0012 #include <vimode/inputmodemanager.h> 0013 #include <vimode/marks.h> 0014 #include <vimode/modes/visualvimode.h> 0015 #include <vimode/motion.h> 0016 #include <vimode/range.h> 0017 0018 using namespace KateVi; 0019 0020 VisualViMode::VisualViMode(InputModeManager *viInputModeManager, KTextEditor::ViewPrivate *view, KateViewInternal *viewInternal) 0021 : NormalViMode(viInputModeManager, view, viewInternal) 0022 { 0023 m_start.setPosition(-1, -1); 0024 m_mode = ViMode::VisualMode; 0025 0026 connect(m_view, &KTextEditor::ViewPrivate::selectionChanged, this, &VisualViMode::updateSelection); 0027 } 0028 0029 void VisualViMode::selectInclusive(const KTextEditor::Cursor c1, const KTextEditor::Cursor c2) 0030 { 0031 if (c1 >= c2) { 0032 m_view->setSelection(KTextEditor::Range(c1.line(), c1.column() + 1, c2.line(), c2.column())); 0033 } else { 0034 m_view->setSelection(KTextEditor::Range(c1.line(), c1.column(), c2.line(), c2.column() + 1)); 0035 } 0036 } 0037 0038 void VisualViMode::selectBlockInclusive(const KTextEditor::Cursor c1, const KTextEditor::Cursor c2) 0039 { 0040 m_view->setBlockSelection(true); 0041 0042 if (c1.column() >= c2.column()) { 0043 m_view->setSelection(KTextEditor::Range(c1.line(), c1.column() + 1, c2.line(), c2.column())); 0044 } else { 0045 m_view->setSelection(KTextEditor::Range(c1.line(), c1.column(), c2.line(), c2.column() + 1)); 0046 } 0047 } 0048 0049 void VisualViMode::selectLines(KTextEditor::Range range) 0050 { 0051 int sline = qMin(range.start().line(), range.end().line()); 0052 int eline = qMax(range.start().line(), range.end().line()); 0053 int ecol = m_view->doc()->lineLength(eline) + 1; 0054 0055 m_view->setSelection(KTextEditor::Range(KTextEditor::Cursor(sline, 0), KTextEditor::Cursor(eline, ecol))); 0056 } 0057 0058 void VisualViMode::goToPos(const Range &r) 0059 { 0060 KTextEditor::Cursor c = m_view->cursorPosition(); 0061 0062 if (r.startLine != -1 && r.startColumn != -1 && c == m_start) { 0063 m_start.setLine(r.startLine); 0064 m_start.setColumn(r.startColumn); 0065 c.setLine(r.endLine); 0066 c.setColumn(r.endColumn); 0067 } else if (r.startLine != -1 && r.startColumn != -1 && m_motionCanChangeWholeVisualModeSelection) { 0068 const KTextEditor::Cursor textObjectBegin(r.startLine, r.startColumn); 0069 if (textObjectBegin < m_start) { 0070 m_start.setLine(r.startLine); 0071 m_start.setColumn(r.startColumn); 0072 c.setLine(r.endLine); 0073 c.setColumn(r.endColumn); 0074 } 0075 } else { 0076 c.setLine(r.endLine); 0077 c.setColumn(r.endColumn); 0078 } 0079 0080 if (c.line() >= doc()->lines()) { 0081 c.setLine(doc()->lines() - 1); 0082 } 0083 0084 updateCursor(c); 0085 0086 // Setting range for a command 0087 m_commandRange = Range(m_start, c, m_commandRange.motionType); 0088 0089 // If visual mode is blockwise 0090 if (isVisualBlock()) { 0091 selectBlockInclusive(m_start, c); 0092 0093 // Need to correct command range to make it inclusive. 0094 if ((c.line() < m_start.line() && c.column() > m_start.column()) || (c.line() > m_start.line() && c.column() < m_start.column())) { 0095 qSwap(m_commandRange.endColumn, m_commandRange.startColumn); 0096 } 0097 return; 0098 } else { 0099 m_view->setBlockSelection(false); 0100 } 0101 0102 // If visual mode is linewise 0103 if (isVisualLine()) { 0104 selectLines(KTextEditor::Range(m_start, c)); 0105 return; 0106 } 0107 0108 // If visual mode is charwise 0109 selectInclusive(m_start, c); 0110 } 0111 0112 void VisualViMode::reset() 0113 { 0114 m_mode = ViMode::VisualMode; 0115 0116 // only switch to normal mode if still in visual mode. commands like c, s, ... 0117 // can have switched to insert mode 0118 if (m_viInputModeManager->isAnyVisualMode()) { 0119 saveRangeMarks(); 0120 m_lastVisualMode = m_viInputModeManager->getCurrentViMode(); 0121 0122 // Return the cursor back to start of selection after. 0123 if (!m_pendingResetIsDueToExit) { 0124 KTextEditor::Cursor c = m_view->cursorPosition(); 0125 if (m_start.line() != -1 && m_start.column() != -1) { 0126 if (m_viInputModeManager->getCurrentViMode() == ViMode::VisualLineMode) { 0127 if (m_start.line() < c.line()) { 0128 updateCursor(KTextEditor::Cursor(m_start.line(), 0)); 0129 m_stickyColumn = -1; 0130 } 0131 } else { 0132 updateCursor(qMin(m_start, c)); 0133 m_stickyColumn = -1; 0134 } 0135 } 0136 } 0137 0138 if (m_viInputModeManager->getPreviousViMode() == ViMode::InsertMode) { 0139 startInsertMode(); 0140 } else { 0141 startNormalMode(); 0142 } 0143 } 0144 0145 if (!m_commandShouldKeepSelection) { 0146 m_view->removeSelection(); 0147 } else { 0148 m_commandShouldKeepSelection = false; 0149 } 0150 0151 m_start.setPosition(-1, -1); 0152 m_pendingResetIsDueToExit = false; 0153 } 0154 0155 void VisualViMode::saveRangeMarks() 0156 { 0157 // DO NOT save these marks if the 0158 // action that exited visual mode deleted the selection 0159 if (m_deleteCommand == false) { 0160 m_viInputModeManager->marks()->setSelectionStart(m_start); 0161 m_viInputModeManager->marks()->setSelectionFinish(m_view->cursorPosition()); 0162 } 0163 } 0164 0165 void VisualViMode::init() 0166 { 0167 // when using "gv" we already have a start position 0168 if (!m_start.isValid()) { 0169 m_start = m_view->cursorPosition(); 0170 } 0171 0172 if (isVisualLine()) { 0173 KTextEditor::Cursor c = m_view->cursorPosition(); 0174 selectLines(KTextEditor::Range(c, c)); 0175 } 0176 0177 m_commandRange = Range(m_start, m_start, m_commandRange.motionType); 0178 } 0179 0180 void VisualViMode::setVisualModeType(ViMode mode) 0181 { 0182 Q_ASSERT(mode == ViMode::VisualMode || mode == ViMode::VisualLineMode || mode == ViMode::VisualBlockMode); 0183 m_mode = mode; 0184 } 0185 0186 void VisualViMode::switchStartEnd() 0187 { 0188 KTextEditor::Cursor c = m_start; 0189 m_start = m_view->cursorPosition(); 0190 0191 updateCursor(c); 0192 0193 m_stickyColumn = -1; 0194 } 0195 0196 void VisualViMode::goToPos(const KTextEditor::Cursor c) 0197 { 0198 Range r(c, InclusiveMotion); 0199 goToPos(r); 0200 } 0201 0202 void VisualViMode::updateSelection() 0203 { 0204 if (!m_viInputModeManager->inputAdapter()->isActive()) { 0205 return; 0206 } 0207 if (m_viInputModeManager->isHandlingKeypress() && !m_isUndo) { 0208 return; 0209 } 0210 0211 // If we are there it's already not VisualBlock mode. 0212 m_view->setBlockSelection(false); 0213 0214 // If not valid going back to normal mode 0215 KTextEditor::Range r = m_view->selectionRange(); 0216 if (!r.isValid()) { 0217 // Don't screw up the cursor's position. See BUG #337286. 0218 m_pendingResetIsDueToExit = true; 0219 reset(); 0220 return; 0221 } 0222 0223 // If already not in visual mode, it's time to go there. 0224 if (m_viInputModeManager->getCurrentViMode() != ViMode::VisualMode) { 0225 commandEnterVisualMode(); 0226 } 0227 0228 // Set range for commands 0229 m_start = (m_view->cursorPosition() == r.start()) ? r.end() : r.start(); 0230 m_commandRange = Range(r.start(), r.end(), m_commandRange.motionType); 0231 // The end of the range seems to be one space forward of where it should be. 0232 m_commandRange.endColumn--; 0233 } 0234 0235 #define ADDCMD(STR, FUNC, FLGS) Command(QStringLiteral(STR), &NormalViMode::FUNC, FLGS) 0236 0237 #define ADDMOTION(STR, FUNC, FLGS) Motion(QStringLiteral(STR), &NormalViMode::FUNC, FLGS) 0238 0239 const std::vector<Command> &VisualViMode::commands() 0240 { 0241 // init once, is expensive 0242 static std::vector<Command> global{ 0243 ADDCMD("J", commandJoinLines, IS_CHANGE), 0244 ADDCMD("c", commandChange, IS_CHANGE), 0245 ADDCMD("s", commandChange, IS_CHANGE), 0246 ADDCMD("C", commandChangeToEOL, IS_CHANGE), 0247 ADDCMD("S", commandChangeToEOL, IS_CHANGE), 0248 ADDCMD("d", commandDelete, IS_CHANGE), 0249 ADDCMD("<delete>", commandDelete, IS_CHANGE), 0250 ADDCMD("D", commandDeleteToEOL, IS_CHANGE), 0251 ADDCMD("x", commandDeleteChar, IS_CHANGE), 0252 ADDCMD("X", commandDeleteCharBackward, IS_CHANGE), 0253 ADDCMD("gu", commandMakeLowercase, IS_CHANGE), 0254 ADDCMD("u", commandMakeLowercase, IS_CHANGE), 0255 ADDCMD("gU", commandMakeUppercase, IS_CHANGE), 0256 ADDCMD("g~", commandChangeCaseRange, IS_CHANGE), 0257 ADDCMD("U", commandMakeUppercase, IS_CHANGE), 0258 ADDCMD("y", commandYank, 0), 0259 ADDCMD("Y", commandYankToEOL, 0), 0260 ADDCMD("p", commandPaste, IS_CHANGE), 0261 ADDCMD("P", commandPasteBefore, IS_CHANGE), 0262 ADDCMD("r.", commandReplaceCharacter, IS_CHANGE | REGEX_PATTERN), 0263 ADDCMD(":", commandSwitchToCmdLine, SHOULD_NOT_RESET), 0264 ADDCMD("m.", commandSetMark, REGEX_PATTERN | SHOULD_NOT_RESET), 0265 ADDCMD(">", commandIndentLines, IS_CHANGE), 0266 ADDCMD("<", commandUnindentLines, IS_CHANGE), 0267 ADDCMD("<c-c>", commandAbort, 0), 0268 ADDCMD("<c-[>", commandAbort, 0), 0269 ADDCMD("ga", commandPrintCharacterCode, SHOULD_NOT_RESET), 0270 ADDCMD("v", commandEnterVisualMode, SHOULD_NOT_RESET), 0271 ADDCMD("V", commandEnterVisualLineMode, SHOULD_NOT_RESET), 0272 ADDCMD("o", commandToOtherEnd, SHOULD_NOT_RESET | CAN_LAND_INSIDE_FOLDING_RANGE), 0273 ADDCMD("=", commandAlignLines, SHOULD_NOT_RESET), 0274 ADDCMD("~", commandChangeCase, IS_CHANGE), 0275 ADDCMD("I", commandPrependToBlock, IS_CHANGE), 0276 ADDCMD("A", commandAppendToBlock, IS_CHANGE), 0277 ADDCMD("gq", commandFormatLines, IS_CHANGE), 0278 ADDCMD("q.", commandStartRecordingMacro, REGEX_PATTERN | SHOULD_NOT_RESET), 0279 ADDCMD("@.", commandReplayMacro, REGEX_PATTERN | SHOULD_NOT_RESET), 0280 ADDCMD("z.", commandCenterViewOnNonBlank, 0), 0281 ADDCMD("zz", commandCenterViewOnCursor, 0), 0282 ADDCMD("z<return>", commandTopViewOnNonBlank, 0), 0283 ADDCMD("zt", commandTopViewOnCursor, 0), 0284 ADDCMD("z-", commandBottomViewOnNonBlank, 0), 0285 ADDCMD("zb", commandBottomViewOnCursor, 0), 0286 }; 0287 return global; 0288 } 0289 0290 const std::vector<Motion> &VisualViMode::motions() 0291 { 0292 // init once, is expensive 0293 static std::vector<Motion> global{ 0294 // regular motions 0295 ADDMOTION("h", motionLeft, 0), 0296 ADDMOTION("<left>", motionLeft, 0), 0297 ADDMOTION("<backspace>", motionLeft, 0), 0298 ADDMOTION("j", motionDown, 0), 0299 ADDMOTION("<down>", motionDown, 0), 0300 ADDMOTION("k", motionUp, 0), 0301 ADDMOTION("<up>", motionUp, 0), 0302 ADDMOTION("l", motionRight, 0), 0303 ADDMOTION("<right>", motionRight, 0), 0304 ADDMOTION(" ", motionRight, 0), 0305 ADDMOTION("$", motionToEOL, 0), 0306 ADDMOTION("<end>", motionToEOL, 0), 0307 ADDMOTION("0", motionToColumn0, 0), 0308 ADDMOTION("<home>", motionToColumn0, 0), 0309 ADDMOTION("^", motionToFirstCharacterOfLine, 0), 0310 ADDMOTION("f.", motionFindChar, REGEX_PATTERN | CAN_LAND_INSIDE_FOLDING_RANGE), 0311 ADDMOTION("F.", motionFindCharBackward, REGEX_PATTERN | CAN_LAND_INSIDE_FOLDING_RANGE), 0312 ADDMOTION("t.", motionToChar, REGEX_PATTERN | CAN_LAND_INSIDE_FOLDING_RANGE), 0313 ADDMOTION("T.", motionToCharBackward, REGEX_PATTERN | CAN_LAND_INSIDE_FOLDING_RANGE), 0314 ADDMOTION(";", motionRepeatlastTF, CAN_LAND_INSIDE_FOLDING_RANGE), 0315 ADDMOTION(",", motionRepeatlastTFBackward, CAN_LAND_INSIDE_FOLDING_RANGE), 0316 ADDMOTION("n", motionFindNext, CAN_LAND_INSIDE_FOLDING_RANGE), 0317 ADDMOTION("N", motionFindPrev, CAN_LAND_INSIDE_FOLDING_RANGE), 0318 ADDMOTION("gg", motionToLineFirst, 0), 0319 ADDMOTION("G", motionToLineLast, 0), 0320 ADDMOTION("w", motionWordForward, CAN_LAND_INSIDE_FOLDING_RANGE), 0321 ADDMOTION("W", motionWORDForward, CAN_LAND_INSIDE_FOLDING_RANGE), 0322 ADDMOTION("<c-right>", motionWordForward, IS_NOT_LINEWISE | CAN_LAND_INSIDE_FOLDING_RANGE), 0323 ADDMOTION("<c-left>", motionWordBackward, IS_NOT_LINEWISE | CAN_LAND_INSIDE_FOLDING_RANGE), 0324 ADDMOTION("b", motionWordBackward, CAN_LAND_INSIDE_FOLDING_RANGE), 0325 ADDMOTION("B", motionWORDBackward, CAN_LAND_INSIDE_FOLDING_RANGE), 0326 ADDMOTION("e", motionToEndOfWord, CAN_LAND_INSIDE_FOLDING_RANGE), 0327 ADDMOTION("E", motionToEndOfWORD, CAN_LAND_INSIDE_FOLDING_RANGE), 0328 ADDMOTION("ge", motionToEndOfPrevWord, CAN_LAND_INSIDE_FOLDING_RANGE), 0329 ADDMOTION("gE", motionToEndOfPrevWORD, CAN_LAND_INSIDE_FOLDING_RANGE), 0330 ADDMOTION("|", motionToScreenColumn, 0), 0331 ADDMOTION("%", motionToMatchingItem, CAN_LAND_INSIDE_FOLDING_RANGE), 0332 ADDMOTION("`.", motionToMark, REGEX_PATTERN | CAN_LAND_INSIDE_FOLDING_RANGE), 0333 ADDMOTION("'.", motionToMarkLine, REGEX_PATTERN | CAN_LAND_INSIDE_FOLDING_RANGE), 0334 ADDMOTION("[[", motionToPreviousBraceBlockStart, CAN_LAND_INSIDE_FOLDING_RANGE), 0335 ADDMOTION("]]", motionToNextBraceBlockStart, CAN_LAND_INSIDE_FOLDING_RANGE), 0336 ADDMOTION("[]", motionToPreviousBraceBlockEnd, CAN_LAND_INSIDE_FOLDING_RANGE), 0337 ADDMOTION("][", motionToNextBraceBlockEnd, CAN_LAND_INSIDE_FOLDING_RANGE), 0338 ADDMOTION("*", motionToNextOccurrence, CAN_LAND_INSIDE_FOLDING_RANGE), 0339 ADDMOTION("#", motionToPrevOccurrence, CAN_LAND_INSIDE_FOLDING_RANGE), 0340 ADDMOTION("<c-f>", motionPageDown, 0), 0341 ADDMOTION("<pagedown>", motionPageDown, 0), 0342 ADDMOTION("<c-b>", motionPageUp, 0), 0343 ADDMOTION("<pageup>", motionPageUp, 0), 0344 ADDMOTION("gj", motionToNextVisualLine, 0), 0345 ADDMOTION("g<down>", motionToNextVisualLine, 0), 0346 ADDMOTION("gk", motionToPrevVisualLine, 0), 0347 ADDMOTION("g<up>", motionToPrevVisualLine, 0), 0348 ADDMOTION("(", motionToPreviousSentence, CAN_LAND_INSIDE_FOLDING_RANGE), 0349 ADDMOTION(")", motionToNextSentence, CAN_LAND_INSIDE_FOLDING_RANGE), 0350 ADDMOTION("{", motionToBeforeParagraph, CAN_LAND_INSIDE_FOLDING_RANGE), 0351 ADDMOTION("}", motionToAfterParagraph, CAN_LAND_INSIDE_FOLDING_RANGE), 0352 ADDMOTION("<c-u>", motionHalfPageUp, 0), 0353 ADDMOTION("<c-d>", motionHalfPageDown, 0), 0354 0355 // text objects 0356 ADDMOTION("iw", textObjectInnerWord, 0), 0357 ADDMOTION("aw", textObjectAWord, 0), 0358 ADDMOTION("iW", textObjectInnerWORD, 0), 0359 ADDMOTION("aW", textObjectAWORD, IS_NOT_LINEWISE), 0360 ADDMOTION("is", textObjectInnerSentence, IS_NOT_LINEWISE | CAN_CHANGE_WHOLE_VISUAL_MODE_SELECTION | CAN_LAND_INSIDE_FOLDING_RANGE), 0361 ADDMOTION("as", textObjectASentence, IS_NOT_LINEWISE | CAN_LAND_INSIDE_FOLDING_RANGE | CAN_LAND_INSIDE_FOLDING_RANGE), 0362 ADDMOTION("ip", textObjectInnerParagraph, IS_NOT_LINEWISE | CAN_CHANGE_WHOLE_VISUAL_MODE_SELECTION | CAN_LAND_INSIDE_FOLDING_RANGE), 0363 ADDMOTION("ap", textObjectAParagraph, IS_NOT_LINEWISE | CAN_CHANGE_WHOLE_VISUAL_MODE_SELECTION | CAN_LAND_INSIDE_FOLDING_RANGE), 0364 ADDMOTION("i\"", textObjectInnerQuoteDouble, CAN_CHANGE_WHOLE_VISUAL_MODE_SELECTION | CAN_LAND_INSIDE_FOLDING_RANGE), 0365 ADDMOTION("a\"", textObjectAQuoteDouble, CAN_LAND_INSIDE_FOLDING_RANGE), 0366 ADDMOTION("i'", textObjectInnerQuoteSingle, CAN_CHANGE_WHOLE_VISUAL_MODE_SELECTION | CAN_LAND_INSIDE_FOLDING_RANGE), 0367 ADDMOTION("a'", textObjectAQuoteSingle, CAN_LAND_INSIDE_FOLDING_RANGE), 0368 ADDMOTION("i[()b]", textObjectInnerParen, REGEX_PATTERN | CAN_CHANGE_WHOLE_VISUAL_MODE_SELECTION | CAN_LAND_INSIDE_FOLDING_RANGE), 0369 ADDMOTION("a[()b]", textObjectAParen, REGEX_PATTERN | CAN_LAND_INSIDE_FOLDING_RANGE), 0370 ADDMOTION("i[{}B]", 0371 textObjectInnerCurlyBracket, 0372 REGEX_PATTERN | IS_NOT_LINEWISE | CAN_CHANGE_WHOLE_VISUAL_MODE_SELECTION | CAN_LAND_INSIDE_FOLDING_RANGE), 0373 ADDMOTION("a[{}B]", textObjectACurlyBracket, REGEX_PATTERN | IS_NOT_LINEWISE | CAN_LAND_INSIDE_FOLDING_RANGE), 0374 ADDMOTION("i[><]", 0375 textObjectInnerInequalitySign, 0376 REGEX_PATTERN | IS_NOT_LINEWISE | CAN_CHANGE_WHOLE_VISUAL_MODE_SELECTION | CAN_LAND_INSIDE_FOLDING_RANGE), 0377 ADDMOTION("i[\\[\\]]", textObjectInnerBracket, REGEX_PATTERN | CAN_CHANGE_WHOLE_VISUAL_MODE_SELECTION | CAN_LAND_INSIDE_FOLDING_RANGE), 0378 ADDMOTION("a[\\[\\]]", textObjectABracket, REGEX_PATTERN | CAN_LAND_INSIDE_FOLDING_RANGE), 0379 ADDMOTION("i,", textObjectInnerComma, CAN_LAND_INSIDE_FOLDING_RANGE), 0380 ADDMOTION("a,", textObjectAComma, CAN_LAND_INSIDE_FOLDING_RANGE), 0381 0382 ADDMOTION("/<enter>", motionToIncrementalSearchMatch, CAN_LAND_INSIDE_FOLDING_RANGE), 0383 ADDMOTION("?<enter>", motionToIncrementalSearchMatch, CAN_LAND_INSIDE_FOLDING_RANGE), 0384 }; 0385 return global; 0386 } 0387 0388 #include "moc_visualvimode.cpp"