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