15#include "katedocument.h"
17#include "kateabstractinputmode.h"
18#include "kateautoindent.h"
19#include "katebuffer.h"
20#include "katecompletionwidget.h"
21#include "kateconfig.h"
22#include "katedialogs.h"
23#include "kateglobal.h"
24#include "katehighlight.h"
25#include "kateindentdetecter.h"
26#include "katemodemanager.h"
27#include "katepartdebug.h"
28#include "kateplaintextsearch.h"
29#include "kateregexpsearch.h"
30#include "katerenderer.h"
31#include "katescriptmanager.h"
32#include "kateswapfile.h"
33#include "katesyntaxmanager.h"
34#include "katetemplatehandler.h"
35#include "kateundomanager.h"
36#include "katevariableexpansionmanager.h"
38#include "printing/kateprinter.h"
39#include "spellcheck/ontheflycheck.h"
40#include "spellcheck/prefixstore.h"
41#include "spellcheck/spellcheck.h"
46#include "editorconfig.h"
49#include <KTextEditor/Attribute>
50#include <KTextEditor/DocumentCursor>
51#include <ktexteditor/message.h>
53#include <KConfigGroup>
56#include <KIO/FileCopyJob>
57#include <KIO/JobUiDelegate>
62#include <KNetworkMounts>
63#include <KParts/OpenUrlArguments>
64#include <KStandardAction>
65#include <KStringHandler>
66#include <KToggleAction>
67#include <KXMLGUIFactory>
69#include <QApplication>
71#include <QCryptographicHash>
75#include <QMimeDatabase>
77#include <QRegularExpression>
78#include <QStandardPaths>
79#include <QTemporaryFile>
87#define EDIT_DEBUG qCDebug(LOG_KTE)
94template<
class C,
class E>
95static int indexOf(
const std::initializer_list<C> &list,
const E &entry)
101template<
class C,
class E>
102static bool contains(
const std::initializer_list<C> &list,
const E &entry)
104 return indexOf(list, entry) >= 0;
107static inline QChar matchingStartBracket(
const QChar c)
120static inline QChar matchingEndBracket(
const QChar c,
bool withQuotes =
true)
137static inline QChar matchingBracket(
const QChar c)
139 QChar bracket = matchingStartBracket(c);
141 bracket = matchingEndBracket(c,
false);
146static inline bool isStartBracket(
const QChar c)
148 return !matchingEndBracket(c,
false).
isNull();
151static inline bool isEndBracket(
const QChar c)
153 return !matchingStartBracket(c).
isNull();
156static inline bool isBracket(
const QChar c)
158 return isStartBracket(c) || isEndBracket(c);
165KTextEditor::DocumentPrivate::DocumentPrivate(
const KPluginMetaData &data,
bool bSingleViewMode,
bool bReadOnly,
QWidget *parentWidget,
QObject *parent)
167 , m_bSingleViewMode(bSingleViewMode)
168 , m_bReadOnly(bReadOnly)
178 m_docName(QStringLiteral(
"need init"))
181 m_fileType(QStringLiteral(
"Normal"))
184 m_config(new KateDocumentConfig(this))
188 const auto &aboutData = EditorPrivate::self()->aboutData();
189 setComponentName(aboutData.componentName(), aboutData.displayName());
193 setProgressInfoEnabled(
false);
199 m_buffer->setHighlight(0);
202 m_swapfile = (config()->swapFileMode() == KateDocumentConfig::DisableSwapFile) ?
nullptr : new Kate::SwapFile(this);
208 connect(KateHlManager::self(), &KateHlManager::changed,
this, &KTextEditor::DocumentPrivate::internalHlChanged);
218 m_modOnHdTimer.setSingleShot(
true);
219 m_modOnHdTimer.setInterval(200);
220 connect(&m_modOnHdTimer, &
QTimer::timeout,
this, &KTextEditor::DocumentPrivate::slotDelayedHandleModOnHd);
224 m_autoReloadMode->setWhatsThis(
i18n(
"Automatic reload the document when it was changed on disk"));
227 m_autoReloadThrottle.setSingleShot(
true);
235 connect(
this, &KTextEditor::DocumentPrivate::started,
this, &KTextEditor::DocumentPrivate::slotStarted);
236 connect(
this, qOverload<>(&KTextEditor::DocumentPrivate::completed),
this, &KTextEditor::DocumentPrivate::slotCompleted);
237 connect(
this, &KTextEditor::DocumentPrivate::canceled,
this, &KTextEditor::DocumentPrivate::slotCanceled);
246 if (m_bSingleViewMode && parentWidget) {
248 insertChildClient(view);
253 connect(m_undoManager, &KateUndoManager::undoChanged,
this, &KTextEditor::DocumentPrivate::undoChanged);
254 connect(m_undoManager, &KateUndoManager::undoStart,
this, &KTextEditor::DocumentPrivate::editingStarted);
255 connect(m_undoManager, &KateUndoManager::undoEnd,
this, &KTextEditor::DocumentPrivate::editingFinished);
256 connect(m_undoManager, &KateUndoManager::redoStart,
this, &KTextEditor::DocumentPrivate::editingStarted);
257 connect(m_undoManager, &KateUndoManager::redoEnd,
this, &KTextEditor::DocumentPrivate::editingFinished);
259 connect(
this, &KTextEditor::DocumentPrivate::sigQueryClose,
this, &KTextEditor::DocumentPrivate::slotQueryClose_save);
261 connect(
this, &KTextEditor::DocumentPrivate::aboutToInvalidateMovingInterfaceContent,
this, &KTextEditor::DocumentPrivate::clearEditingPosStack);
262 onTheFlySpellCheckingEnabled(config()->onTheFlySpellCheck());
267 m_autoSaveTimer.setSingleShot(
true);
269 if (isModified() && url().isLocalFile()) {
278KTextEditor::DocumentPrivate::~DocumentPrivate()
285 delete m_modOnHdHandler;
288 Q_EMIT aboutToDeleteMovingInterfaceContent(
this);
291 delete m_onTheFlyChecker;
292 m_onTheFlyChecker =
nullptr;
294 clearDictionaryRanges();
299 Q_EMIT aboutToClose(
this);
302 deactivateDirWatch();
305 setAutoDeleteWidget(
false);
306 setAutoDeletePart(
false);
313 for (
auto &mark :
std::as_const(m_marks)) {
328 if (m_editingStackPosition != m_editingStack.size() - 1) {
329 m_editingStack.resize(m_editingStackPosition);
333 std::shared_ptr<KTextEditor::MovingCursor> mc;
336 if (!m_editingStack.isEmpty() && cursor.
line() == m_editingStack.top()->line()) {
337 mc = m_editingStack.pop();
342 const int editingStackSizeLimit = 32;
343 if (m_editingStack.size() >= editingStackSizeLimit) {
345 m_editingStack.removeFirst();
347 mc = m_editingStack.takeFirst();
353 mc->setPosition(cursor);
355 mc = std::shared_ptr<KTextEditor::MovingCursor>(newMovingCursor(cursor));
359 m_editingStack.push(mc);
360 m_editingStackPosition = m_editingStack.size() - 1;
365 if (m_editingStack.isEmpty()) {
368 auto targetPos = m_editingStack.at(m_editingStackPosition)->toCursor();
369 if (targetPos == currentCursor) {
370 if (nextOrPrev == Previous) {
371 m_editingStackPosition--;
373 m_editingStackPosition++;
375 m_editingStackPosition = qBound(0, m_editingStackPosition, m_editingStack.size() - 1);
377 return m_editingStack.at(m_editingStackPosition)->toCursor();
380void KTextEditor::DocumentPrivate::clearEditingPosStack()
382 m_editingStack.clear();
383 m_editingStackPosition = -1;
387QWidget *KTextEditor::DocumentPrivate::widget()
390 if (!singleViewMode()) {
401 insertChildClient(view);
411 KTextEditor::ViewPrivate *newView =
new KTextEditor::ViewPrivate(
this, parent, mainWindow);
413 if (m_fileChangedDialogsActivated) {
417 Q_EMIT viewCreated(
this, newView);
420 const auto keys = m_messageHash.keys();
422 if (!message->view()) {
423 newView->postMessage(message, m_messageHash[message]);
432 const int col1 = toVirtualColumn(range.
start());
433 const int col2 = toVirtualColumn(range.
end());
434 return KTextEditor::Range(line, fromVirtualColumn(line, col1), line, fromVirtualColumn(line, col2));
439bool KTextEditor::DocumentPrivate::isEditingTransactionRunning()
const
441 return editSessionNumber > 0;
444QString KTextEditor::DocumentPrivate::text()
const
446 return m_buffer->text();
452 qCWarning(LOG_KTE) <<
"Text requested for invalid range" << range;
466 for (
int i = range.
start().
line(); (i <= range.
end().
line()) && (i < m_buffer->lines()); ++i) {
471 }
else if (i == range.
end().
line()) {
493 return textLine.
at(position.
column());
498 return text(wordRangeAt(cursor));
504 const int line = cursor.
line();
508 const int lineLenth = textLine.
length();
509 if (cursor.
column() > lineLenth) {
519 while (end < lineLenth && highlight()->isInWord(textLine.
at(end), textLine.
attribute(end))) {
528 const int ln = cursor.
line();
529 const int col = cursor.
column();
531 if (ln < 0 || col < 0 || ln >= lines() || col > lineLength(ln)) {
536 Q_ASSERT(str.
length() >= col);
539 const int len = lineLength(ln);
540 if (col == 0 || col == len) {
553 qCWarning(LOG_KTE) <<
"Text requested for invalid range" << range;
562 Q_ASSERT(range.
start() <= range.
end());
567 for (
int i = range.
start().
line(); (i <= range.
end().
line()) && (i < m_buffer->lines()); ++i) {
572 }
else if (i == range.
end().
line()) {
575 ret << textLine.
text();
587QString KTextEditor::DocumentPrivate::line(
int line)
const
593bool KTextEditor::DocumentPrivate::setText(
const QString &s)
595 if (!isReadWrite()) {
599 std::vector<KTextEditor::Mark> msave;
601 std::transform(m_marks.cbegin(), m_marks.cend(), std::back_inserter(msave), [](
KTextEditor::Mark *mark) {
605 for (
auto v :
std::as_const(m_views)) {
606 static_cast<KTextEditor::ViewPrivate *
>(v)->completionWidget()->setIgnoreBufferSignals(
true);
619 for (
auto v :
std::as_const(m_views)) {
620 static_cast<KTextEditor::ViewPrivate *
>(v)->completionWidget()->setIgnoreBufferSignals(
false);
624 setMark(mark.line, mark.type);
630bool KTextEditor::DocumentPrivate::setText(
const QStringList &text)
632 if (!isReadWrite()) {
636 std::vector<KTextEditor::Mark> msave;
637 msave.reserve(m_marks.size());
638 std::transform(m_marks.cbegin(), m_marks.cend(), std::back_inserter(msave), [](
KTextEditor::Mark *mark) {
642 for (
auto v :
std::as_const(m_views)) {
643 static_cast<KTextEditor::ViewPrivate *
>(v)->completionWidget()->setIgnoreBufferSignals(
true);
656 for (
auto v :
std::as_const(m_views)) {
657 static_cast<KTextEditor::ViewPrivate *
>(v)->completionWidget()->setIgnoreBufferSignals(
false);
661 setMark(mark.line, mark.type);
667bool KTextEditor::DocumentPrivate::clear()
669 if (!isReadWrite()) {
673 for (
auto view :
std::as_const(m_views)) {
674 static_cast<ViewPrivate *
>(view)->
clear();
675 static_cast<ViewPrivate *
>(view)->tagAll();
681 Q_EMIT aboutToInvalidateMovingInterfaceContent(
this);
682 m_buffer->invalidateRanges();
684 Q_EMIT aboutToRemoveText(documentRange());
686 return editRemoveLines(0, lastLine());
691 if (!isReadWrite()) {
704 int currentLine = position.
line();
705 int currentLineStart = 0;
706 const int totalLength = text.
length();
707 int insertColumn = position.
column();
710 if (position.
line() > lines()) {
712 while (line <= position.
line()) {
713 editInsertLine(line,
QString(),
false);
719 int positionColumnExpanded = insertColumn;
720 const int tabWidth = config()->tabWidth();
722 if (currentLine < lines()) {
723 positionColumnExpanded = plainKateTextLine(currentLine).toVirtualColumn(insertColumn, tabWidth);
729 for (; pos < totalLength; pos++) {
730 const QChar &ch = text.
at(pos);
734 if (currentLineStart < pos) {
735 editInsertText(currentLine, insertColumn, text.
mid(currentLineStart, pos - currentLineStart), notify);
736 endCol = insertColumn + (pos - currentLineStart);
741 const auto wrapColumn = insertColumn + pos - currentLineStart;
742 const auto currentLineLength = lineLength(currentLine);
743 if (wrapColumn > currentLineLength) {
744 editInsertText(currentLine, currentLineLength,
QString(wrapColumn - currentLineLength,
QLatin1Char(
' ')), notify);
748 editWrapLine(currentLine, wrapColumn,
true,
nullptr, notify);
756 auto l = currentLine < lines();
757 if (currentLine == lastLine() + 1) {
758 editInsertLine(currentLine,
QString(), notify);
761 insertColumn = positionColumnExpanded;
763 insertColumn = plainKateTextLine(currentLine).fromVirtualColumn(insertColumn, tabWidth);
767 currentLineStart = pos + 1;
772 if (currentLineStart < pos) {
773 editInsertText(currentLine, insertColumn, text.
mid(currentLineStart, pos - currentLineStart), notify);
774 endCol = insertColumn + (pos - currentLineStart);
779 Q_EMIT textInsertedRange(
this, insertedRange);
787 if (!isReadWrite()) {
799 if (!isReadWrite()) {
811 Q_EMIT aboutToRemoveText(range);
817 if (range.
end().
line() > lastLine()) {
828 if (to <= lastLine()) {
829 editRemoveText(to, 0, range.
end().
column());
838 editRemoveLines(from + 1, to - 1);
842 editRemoveText(from, range.
start().
column(), m_buffer->plainLine(from).length() - range.
start().
column());
843 editUnWrapLine(from);
848 int startLine = qMax(0, range.
start().
line());
849 int vc1 = toVirtualColumn(range.
start());
850 int vc2 = toVirtualColumn(range.
end());
851 for (
int line = qMin(range.
end().
line(), lastLine()); line >= startLine; --line) {
852 int col1 = fromVirtualColumn(line, vc1);
853 int col2 = fromVirtualColumn(line, vc2);
854 editRemoveText(line, qMin(col1, col2), qAbs(col2 - col1));
862bool KTextEditor::DocumentPrivate::insertLine(
int l,
const QString &str)
864 if (!isReadWrite()) {
868 if (l < 0 || l > lines()) {
872 return editInsertLine(l, str);
875bool KTextEditor::DocumentPrivate::insertLines(
int line,
const QStringList &text)
877 if (!isReadWrite()) {
881 if (line < 0 || line > lines()) {
886 for (
const QString &
string : text) {
887 success &= editInsertLine(line++,
string);
893bool KTextEditor::DocumentPrivate::removeLine(
int line)
895 if (!isReadWrite()) {
899 if (line < 0 || line > lastLine()) {
903 return editRemoveLine(line);
906qsizetype KTextEditor::DocumentPrivate::totalCharacters()
const
909 for (
int i = 0; i < m_buffer->lines(); ++i) {
910 l += m_buffer->lineLength(i);
915int KTextEditor::DocumentPrivate::lines()
const
917 return m_buffer->lines();
920int KTextEditor::DocumentPrivate::lineLength(
int line)
const
922 return m_buffer->lineLength(line);
927 return m_buffer->cursorToOffset(c);
932 return m_buffer->offsetToCursor(offset);
935bool KTextEditor::DocumentPrivate::isLineModified(
int line)
const
937 if (line < 0 || line >= lines()) {
942 return l.markedAsModified();
945bool KTextEditor::DocumentPrivate::isLineSaved(
int line)
const
947 if (line < 0 || line >= lines()) {
952 return l.markedAsSavedOnDisk();
955bool KTextEditor::DocumentPrivate::isLineTouched(
int line)
const
957 if (line < 0 || line >= lines()) {
962 return l.markedAsModified() || l.markedAsSavedOnDisk();
970bool KTextEditor::DocumentPrivate::editStart()
974 if (editSessionNumber > 1) {
978 editIsRunning =
true;
983 m_undoManager->editStart();
985 for (
auto view :
std::as_const(m_views)) {
986 static_cast<ViewPrivate *
>(view)->editStart();
989 m_buffer->editStart();
996bool KTextEditor::DocumentPrivate::editEnd()
998 if (editSessionNumber == 0) {
1004 if (m_buffer->editChanged() && (editSessionNumber == 1)) {
1005 if (m_undoManager->isActive() && config()->wordWrap()) {
1006 wrapText(m_buffer->editTagStart(), m_buffer->editTagEnd());
1010 editSessionNumber--;
1012 if (editSessionNumber > 0) {
1018 m_buffer->editEnd();
1020 m_undoManager->editEnd();
1023 for (
auto view :
std::as_const(m_views)) {
1024 static_cast<ViewPrivate *
>(view)->editEnd(m_buffer->editTagStart(), m_buffer->editTagEnd(), m_buffer->editTagFrom());
1027 if (m_buffer->editChanged()) {
1029 Q_EMIT textChanged(
this);
1035 if (m_editLastChangeStartCursor.isValid()) {
1036 saveEditingPositions(m_editLastChangeStartCursor);
1039 if (config()->autoSave() && config()->autoSaveInterval() > 0) {
1040 m_autoSaveTimer.start();
1043 editIsRunning =
false;
1047void KTextEditor::DocumentPrivate::pushEditState()
1049 editStateStack.push(editSessionNumber);
1052void KTextEditor::DocumentPrivate::popEditState()
1054 if (editStateStack.isEmpty()) {
1058 int count = editStateStack.pop() - editSessionNumber;
1069void KTextEditor::DocumentPrivate::inputMethodStart()
1071 m_undoManager->inputMethodStart();
1074void KTextEditor::DocumentPrivate::inputMethodEnd()
1076 m_undoManager->inputMethodEnd();
1079bool KTextEditor::DocumentPrivate::wrapText(
int startLine,
int endLine)
1081 if (startLine < 0 || endLine < 0) {
1085 if (!isReadWrite()) {
1089 int col = config()->wordWrapAt();
1097 for (
int line = startLine; (line <= endLine) && (line < lines()); line++) {
1103 bool nextlValid = line + 1 < lines();
1108 int eolPosition = l.
length() - 1;
1114 for (; z2 < l.
length(); z2++) {
1116 if (t.
at(z2) == tabChar) {
1117 x += m_buffer->tabWidth() - (x % m_buffer->tabWidth());
1127 const int colInChars = qMin(z2, l.
length() - 1);
1128 int searchStart = colInChars;
1132 if (searchStart == eolPosition && t.
at(searchStart).
isSpace()) {
1144 for (z = searchStart; z >= 0; z--) {
1148 if ((nw < 0) && highlight()->canBreakAt(t.
at(z), l.
attribute(z))) {
1164 if ((nw >= 0) && nw < colInChars) {
1167 z = (nw >= 0) ? nw : colInChars;
1171 editWrapLine(line, z,
true);
1172 editMarkLineAutoWrapped(line + 1,
true);
1177 editInsertText(line + 1, 0, QStringLiteral(
" "));
1180 bool newLineAdded =
false;
1181 editWrapLine(line, z,
false, &newLineAdded);
1183 editMarkLineAutoWrapped(line + 1,
true);
1195bool KTextEditor::DocumentPrivate::wrapParagraph(
int first,
int last)
1197 if (first == last) {
1198 return wrapText(first, last);
1201 if (first < 0 || last < first) {
1205 if (last >= lines() || first > last) {
1209 if (!isReadWrite()) {
1216 std::unique_ptr<KTextEditor::MovingRange> range(newMovingRange(
KTextEditor::Range(first, 0, last, 0)));
1220 for (
int line = first; line <= range->
end().line(); ++line) {
1222 if (plainKateTextLine(first).firstChar() < 0) {
1225 curr->setPosition(curr->line() + 1, 0);
1230 if (plainKateTextLine(line).firstChar() < 0) {
1231 curr->setPosition(line, 0);
1232 joinLines(first, line - 1);
1235 wrapText(first, first);
1237 first = curr->line() + 1;
1243 bool needWrap = (curr->line() != range->
end().
line());
1244 if (needWrap && plainKateTextLine(first).firstChar() != -1) {
1245 joinLines(first, range->
end().
line());
1248 wrapText(first, first);
1256bool KTextEditor::DocumentPrivate::editInsertText(
int line,
int col,
const QString &s,
bool notify)
1259 EDIT_DEBUG <<
"editInsertText" << line << col << s;
1261 if (line < 0 || col < 0) {
1270 if (!isReadWrite()) {
1274 auto l = plainKateTextLine(line);
1284 if (col2 > length) {
1289 m_undoManager->slotTextInserted(line, col2, s2, l);
1295 m_buffer->insertText(m_editLastChangeStartCursor, s2);
1305bool KTextEditor::DocumentPrivate::editRemoveText(
int line,
int col,
int len)
1308 EDIT_DEBUG <<
"editRemoveText" << line << col << len;
1310 if (line < 0 || line >= lines() || col < 0 || len < 0) {
1314 if (!isReadWrite()) {
1331 len = qMin(len, l.
text().
size() - col);
1337 m_undoManager->slotTextRemoved(line, col, oldText, l);
1352bool KTextEditor::DocumentPrivate::editMarkLineAutoWrapped(
int line,
bool autowrapped)
1355 EDIT_DEBUG <<
"editMarkLineAutoWrapped" << line << autowrapped;
1357 if (line < 0 || line >= lines()) {
1361 if (!isReadWrite()) {
1367 m_undoManager->slotMarkLineAutoWrapped(line, autowrapped);
1371 m_buffer->setLineMetaData(line, l);
1378bool KTextEditor::DocumentPrivate::editWrapLine(
int line,
int col,
bool newLine,
bool *newLineAdded,
bool notify)
1381 EDIT_DEBUG <<
"editWrapLine" << line << col << newLine;
1383 if (line < 0 || line >= lines() || col < 0) {
1387 if (!isReadWrite()) {
1391 const auto tl = plainKateTextLine(line);
1395 const bool nextLineValid = lineLength(line + 1) >= 0;
1397 m_undoManager->slotLineWrapped(line, col, tl.length() - col, (!nextLineValid || newLine), tl);
1399 if (!nextLineValid || newLine) {
1403 for (
const auto &mark :
std::as_const(m_marks)) {
1404 if (mark->line >= line) {
1405 if ((col == 0) || (mark->line > line)) {
1411 for (
const auto &mark :
list) {
1412 m_marks.take(mark->line);
1415 for (
const auto &mark :
list) {
1417 m_marks.insert(mark->line, mark);
1421 Q_EMIT marksChanged(
this);
1426 (*newLineAdded) =
true;
1430 m_buffer->unwrapLine(line + 2);
1434 (*newLineAdded) =
false;
1450bool KTextEditor::DocumentPrivate::editUnWrapLine(
int line,
bool removeLine,
int length)
1453 EDIT_DEBUG <<
"editUnWrapLine" << line << removeLine << length;
1455 if (line < 0 || line >= lines() || line + 1 >= lines() || length < 0) {
1459 if (!isReadWrite()) {
1469 m_undoManager->slotLineUnWrapped(line, col, length, removeLine, tl, nextLine);
1472 m_buffer->unwrapLine(line + 1);
1475 m_buffer->unwrapLine(line + 1);
1479 for (
const auto &mark :
std::as_const(m_marks)) {
1480 if (mark->line >= line + 1) {
1484 if (mark->line == line + 1) {
1485 auto m = m_marks.take(line);
1487 mark->type |= m->type;
1493 for (
const auto &mark :
list) {
1494 m_marks.take(mark->line);
1497 for (
const auto &mark :
list) {
1499 m_marks.insert(mark->line, mark);
1503 Q_EMIT marksChanged(
this);
1509 Q_EMIT textRemoved(
this,
KTextEditor::Range(line, col, line + 1, 0), QStringLiteral(
"\n"));
1516bool KTextEditor::DocumentPrivate::editInsertLine(
int line,
const QString &s,
bool notify)
1519 EDIT_DEBUG <<
"editInsertLine" << line << s;
1525 if (!isReadWrite()) {
1529 if (line > lines()) {
1535 m_undoManager->slotLineInserted(line, s);
1549 for (
const auto &mark :
std::as_const(m_marks)) {
1550 if (mark->line >= line) {
1555 for (
const auto &mark :
list) {
1556 m_marks.take(mark->line);
1559 for (
const auto &mark :
list) {
1561 m_marks.insert(mark->line, mark);
1565 Q_EMIT marksChanged(
this);
1571 int prevLineLength = lineLength(line - 1);
1578 m_editLastChangeStartCursor = rangeInserted.start();
1581 Q_EMIT textInsertedRange(
this, rangeInserted);
1589bool KTextEditor::DocumentPrivate::editRemoveLine(
int line)
1591 return editRemoveLines(line, line);
1594bool KTextEditor::DocumentPrivate::editRemoveLines(
int from,
int to)
1597 EDIT_DEBUG <<
"editRemoveLines" << from << to;
1599 if (to < from || from < 0 || to > lastLine()) {
1603 if (!isReadWrite()) {
1608 return editRemoveText(0, 0, lineLength(0));
1615 for (
int line = to; line >= from; --line) {
1618 m_undoManager->slotLineRemoved(line, l.
text(), l);
1624 for (
int line = to; line >= from; --line) {
1626 if (line + 1 < m_buffer->lines()) {
1627 m_buffer->unwrapLine(line + 1);
1629 m_buffer->unwrapLine(line);
1637 int line = mark->line;
1640 }
else if (line >= from) {
1645 for (
int line : rmark) {
1646 delete m_marks.take(line);
1649 for (
auto mark :
list) {
1650 m_marks.take(mark->line);
1653 for (
auto mark :
list) {
1654 mark->line -= to - from + 1;
1655 m_marks.insert(mark->line, mark);
1659 Q_EMIT marksChanged(
this);
1664 if (to == lastLine() + to - from + 1) {
1667 int prevLineLength = lineLength(from - 1);
1673 m_editLastChangeStartCursor = rangeRemoved.start();
1684uint KTextEditor::DocumentPrivate::undoCount()
const
1686 return m_undoManager->undoCount();
1689uint KTextEditor::DocumentPrivate::redoCount()
const
1691 return m_undoManager->redoCount();
1694void KTextEditor::DocumentPrivate::undo()
1696 m_undoManager->undo();
1699void KTextEditor::DocumentPrivate::redo()
1701 m_undoManager->redo();
1707KTextEditor::DocumentPrivate::searchText(
KTextEditor::Range range,
const QString &pattern,
const KTextEditor::SearchOptions options)
const
1709 const bool escapeSequences = options.testFlag(KTextEditor::EscapeSequences);
1710 const bool regexMode = options.testFlag(KTextEditor::Regex);
1711 const bool backwards = options.testFlag(KTextEditor::Backwards);
1712 const bool wholeWords = options.testFlag(KTextEditor::WholeWords);
1723 return searcher.search(pattern, range, backwards, patternOptions);
1726 if (escapeSequences) {
1746QWidget *KTextEditor::DocumentPrivate::dialogParent()
1761QUrl KTextEditor::DocumentPrivate::getSaveFileUrl(
const QString &dialogTitle)
1764 QUrl startUrl = url();
1774 const auto views = mainWindow->
views();
1775 for (
auto view : views) {
1789bool KTextEditor::DocumentPrivate::setMode(
const QString &name)
1791 return updateFileType(name);
1796 return const_cast<KTextEditor::DocumentPrivate *
>(
this)->defStyleNum(position.
line(), position.
column());
1799QString KTextEditor::DocumentPrivate::mode()
const
1804QStringList KTextEditor::DocumentPrivate::modes()
const
1810 for (KateFileType *type : modeList) {
1817bool KTextEditor::DocumentPrivate::setHighlightingMode(
const QString &name)
1819 int mode = KateHlManager::self()->nameFind(name);
1823 m_buffer->setHighlight(mode);
1827QString KTextEditor::DocumentPrivate::highlightingMode()
const
1829 return highlight()->name();
1832QStringList KTextEditor::DocumentPrivate::highlightingModes()
const
1834 const auto modeList = KateHlManager::self()->modeList();
1837 for (
const auto &hl : modeList) {
1843QString KTextEditor::DocumentPrivate::highlightingModeSection(
int index)
const
1845 return KateHlManager::self()->modeList().at(index).section();
1848QString KTextEditor::DocumentPrivate::modeSection(
int index)
const
1853void KTextEditor::DocumentPrivate::bufferHlChanged()
1859 m_indenter->checkRequiredStyle();
1861 Q_EMIT highlightingModeChanged(
this);
1864void KTextEditor::DocumentPrivate::setDontChangeHlOnSave()
1866 m_hlSetByUser =
true;
1869void KTextEditor::DocumentPrivate::bomSetByUser()
1871 m_bomSetByUser =
true;
1878 if (!flags.
contains(QStringLiteral(
"SkipEncoding"))) {
1881 if (!tmpenc.
isEmpty() && (tmpenc != encoding())) {
1882 setEncoding(tmpenc);
1886 if (!flags.
contains(QStringLiteral(
"SkipUrl"))) {
1891 if (!url.isEmpty() && url.isValid()) {
1900 if (!flags.
contains(QStringLiteral(
"SkipMode"))) {
1904 if (kconfig.
hasKey(
"Mode Set By User")) {
1906 m_fileTypeSetByUser =
true;
1907 updateFileType(kconfig.
readEntry(
"Mode"));
1911 if (!flags.
contains(QStringLiteral(
"SkipHighlighting"))) {
1913 if (kconfig.
hasKey(
"Highlighting Set By User")) {
1914 const int mode = KateHlManager::self()->nameFind(kconfig.
readEntry(
"Highlighting"));
1915 m_hlSetByUser =
true;
1918 m_buffer->setHighlight(mode);
1925 if (!userSetIndentMode.
isEmpty()) {
1926 config()->setIndentationMode(userSetIndentMode);
1931 for (
int i = 0; i < marks.
count(); i++) {
1932 addMark(marks.
at(i), KTextEditor::DocumentPrivate::markType01);
1938 if (this->url().isLocalFile()) {
1945 if (!flags.
contains(QStringLiteral(
"SkipUrl"))) {
1956 if (m_fileTypeSetByUser && !flags.
contains(QStringLiteral(
"SkipMode"))) {
1960 kconfig.
writeEntry(
"Mode Set By User", m_fileTypeSetByUser);
1963 if (m_hlSetByUser && !flags.
contains(QStringLiteral(
"SkipHighlighting"))) {
1968 kconfig.
writeEntry(
"Highlighting Set By User", m_hlSetByUser);
1972 if (m_indenterSetByUser) {
1973 kconfig.
writeEntry(
"Indentation Mode", config()->indentationMode());
1978 for (
const auto &mark :
std::as_const(m_marks)) {
1991uint KTextEditor::DocumentPrivate::mark(
int line)
2001void KTextEditor::DocumentPrivate::setMark(
int line, uint markType)
2004 addMark(line, markType);
2007void KTextEditor::DocumentPrivate::clearMark(
int line)
2009 if (line < 0 || line > lastLine()) {
2013 if (
auto mark = m_marks.take(line)) {
2014 Q_EMIT markChanged(
this, *mark, MarkRemoved);
2015 Q_EMIT marksChanged(
this);
2022void KTextEditor::DocumentPrivate::addMark(
int line, uint markType)
2026 if (line < 0 || line > lastLine()) {
2030 if (markType == 0) {
2034 if ((mark = m_marks.value(line))) {
2036 markType &= ~mark->type;
2038 if (markType == 0) {
2043 mark->
type |= markType;
2047 mark->
type = markType;
2048 m_marks.insert(line, mark);
2054 temp.
type = markType;
2055 Q_EMIT markChanged(
this, temp, MarkAdded);
2057 Q_EMIT marksChanged(
this);
2062void KTextEditor::DocumentPrivate::removeMark(
int line, uint markType)
2064 if (line < 0 || line > lastLine()) {
2068 auto it = m_marks.find(line);
2069 if (it == m_marks.end()) {
2075 markType &= mark->
type;
2077 if (markType == 0) {
2082 mark->
type &= ~markType;
2087 temp.
type = markType;
2088 Q_EMIT markChanged(
this, temp, MarkRemoved);
2090 if (mark->
type == 0) {
2095 Q_EMIT marksChanged(
this);
2105void KTextEditor::DocumentPrivate::requestMarkTooltip(
int line,
QPoint position)
2112 bool handled =
false;
2113 Q_EMIT markToolTipRequested(
this, *mark, position, handled);
2116bool KTextEditor::DocumentPrivate::handleMarkClick(
int line)
2118 bool handled =
false;
2123 Q_EMIT markClicked(
this, *mark, handled);
2129bool KTextEditor::DocumentPrivate::handleMarkContextMenu(
int line,
QPoint position)
2131 bool handled =
false;
2134 Q_EMIT markContextMenuRequested(
this,
KTextEditor::Mark{line, 0}, position, handled);
2136 Q_EMIT markContextMenuRequested(
this, *mark, position, handled);
2142void KTextEditor::DocumentPrivate::clearMarks()
2151 for (
const auto &m : marksCopy) {
2152 Q_EMIT markChanged(
this, *m, MarkRemoved);
2157 Q_EMIT marksChanged(
this);
2161void KTextEditor::DocumentPrivate::setMarkDescription(Document::MarkTypes type,
const QString &description)
2163 m_markDescriptions.insert(type, description);
2166QColor KTextEditor::DocumentPrivate::markColor(Document::MarkTypes type)
const
2169 if ((uint)
type >= (uint)markType01 && (uint)
type <= reserved) {
2170 return KateRendererConfig::global()->lineMarkerColor(type);
2176QString KTextEditor::DocumentPrivate::markDescription(Document::MarkTypes type)
const
2178 return m_markDescriptions.value(type,
QString());
2181void KTextEditor::DocumentPrivate::setEditableMarks(uint markMask)
2183 m_editableMarks = markMask;
2186uint KTextEditor::DocumentPrivate::editableMarks()
const
2188 return m_editableMarks;
2192void KTextEditor::DocumentPrivate::setMarkIcon(Document::MarkTypes markType,
const QIcon &icon)
2194 m_markIcons.insert(markType, icon);
2197QIcon KTextEditor::DocumentPrivate::markIcon(Document::MarkTypes markType)
const
2199 return m_markIcons.value(markType,
QIcon());
2203bool KTextEditor::DocumentPrivate::print()
2205 return KatePrinter::print(
this);
2208void KTextEditor::DocumentPrivate::printPreview()
2210 KatePrinter::printPreview(
this);
2215QString KTextEditor::DocumentPrivate::mimeType()
2217 if (!m_modOnHd && url().isLocalFile()) {
2225 for (
int i = 0; (i < lines()) && (buf.
size() <= 4096); ++i) {
2226 buf.
append(line(i).toUtf8());
2231 if (!url().path().isEmpty()) {
2241void KTextEditor::DocumentPrivate::showAndSetOpeningErrorAccess()
2244 i18n(
"The file %1 could not be loaded, as it was not possible to read from it.<br />Check if you have read access to this file.",
2247 message->setWordWrap(
true);
2249 i18nc(
"translators: you can also translate 'Try Again' with 'Reload'",
"Try Again"),
2254 closeAction->
setToolTip(
i18nc(
"Close the message being displayed",
"Close message"));
2257 message->addAction(tryAgainAction);
2258 message->addAction(closeAction);
2261 postMessage(message);
2264 m_openingError =
true;
2268void KTextEditor::DocumentPrivate::openWithLineLengthLimitOverride()
2271 const int longestLine = m_buffer->longestLineLoaded();
2272 int newLimit = pow(2, ceil(log2(longestLine)));
2273 if (newLimit <= longestLine) {
2278 config()->setLineLengthLimit(newLimit);
2283 if (!m_openingError) {
2285 m_readWriteStateBeforeLoading =
true;
2289int KTextEditor::DocumentPrivate::lineLengthLimit()
const
2291 return config()->lineLengthLimit();
2295bool KTextEditor::DocumentPrivate::openFile()
2298 Q_EMIT aboutToInvalidateMovingInterfaceContent(
this);
2301 m_openingError =
false;
2307 QString currentEncoding = encoding();
2314 if (pos != -1 && !(m_reloading && m_userSetEncodingForNextReload)) {
2326 if (m_reloading && m_userSetEncodingForNextReload && (currentEncoding != encoding())) {
2327 setEncoding(currentEncoding);
2330 bool success = m_buffer->openFile(localFilePath(), (m_reloading && m_userSetEncodingForNextReload));
2343 for (
auto view :
std::as_const(m_views)) {
2346 static_cast<ViewPrivate *
>(view)->updateView(
true);
2350 Q_EMIT textChanged(
this);
2351 Q_EMIT loaded(
this);
2358 m_modOnHdReason = OnDiskUnmodified;
2359 m_prevModOnHdReason = OnDiskUnmodified;
2360 Q_EMIT modifiedOnDisk(
this, m_modOnHd, m_modOnHdReason);
2365 if (!isEmpty() && config()->autoDetectIndent() && !config()->isSet(KateDocumentConfig::IndentationWidth)
2366 && !config()->isSet(KateDocumentConfig::ReplaceTabsWithSpaces)) {
2368 auto result = detecter.detect(config()->indentationWidth(), config()->replaceTabsDyn());
2369 config()->setIndentationWidth(result.indentWidth);
2370 config()->setReplaceTabsDyn(result.indentUsingSpaces);
2377 showAndSetOpeningErrorAccess();
2381 if (m_buffer->brokenEncoding()) {
2383 setReadWrite(
false);
2384 m_readWriteStateBeforeLoading =
false;
2386 i18n(
"The file %1 was opened with %2 encoding but contained invalid characters.<br />"
2387 "It is set to read-only mode, as saving might destroy its content.<br />"
2388 "Either reopen the file with the correct encoding chosen or enable the read-write mode again in the tools menu to be able to edit it.",
2390 m_buffer->textCodec()),
2392 message->setWordWrap(
true);
2393 postMessage(message);
2396 m_openingError =
true;
2400 if (m_buffer->tooLongLinesWrapped()) {
2402 setReadWrite(
false);
2403 m_readWriteStateBeforeLoading =
false;
2405 new KTextEditor::Message(
i18n(
"The file %1 was opened and contained lines longer than the configured Line Length Limit (%2 characters).<br />"
2406 "The longest of those lines was %3 characters long<br/>"
2407 "Those lines were wrapped and the document is set to read-only mode, as saving will modify its content.",
2409 config()->lineLengthLimit(),
2410 m_buffer->longestLineLoaded()),
2412 QAction *increaseAndReload =
new QAction(
i18n(
"Temporarily raise limit and reload file"), message);
2414 message->addAction(increaseAndReload,
true);
2415 message->addAction(
new QAction(
i18n(
"Close"), message),
true);
2416 message->setWordWrap(
true);
2417 postMessage(message);
2420 m_openingError =
true;
2429bool KTextEditor::DocumentPrivate::saveFile()
2432 delete m_modOnHdHandler;
2435 if (!url().isEmpty()) {
2436 if (m_fileChangedDialogsActivated && m_modOnHd) {
2439 if (!isModified()) {
2442 str +
i18n(
"Do you really want to save this unmodified file? You could overwrite changed data in the file on disk."),
2443 i18n(
"Trying to Save Unmodified File"),
2453 "Do you really want to save this file? Both your open file and the file on disk were changed. There could be some data lost."),
2454 i18n(
"Possible Data Loss"),
2466 if (!m_buffer->canEncode()
2468 i18n(
"The selected encoding cannot encode every Unicode character in this document. Do you really want to save "
2469 "it? There could be some data lost."),
2470 i18n(
"Possible Data Loss"),
2478 if (!createBackupFile()) {
2483 QString oldPath = m_dirWatchFile;
2486 if (oldPath != localFilePath()) {
2489 if (url().isLocalFile()) {
2496 const bool variablesWereRead = readVariables();
2502 if (!variablesWereRead) {
2503 for (
auto *view :
std::as_const(m_views)) {
2504 auto v =
static_cast<ViewPrivate *
>(view);
2505 if (v->isVisible()) {
2506 const auto range = v->visibleRange();
2508 bool repaint =
false;
2510 if (isLineModified(i)) {
2517 v->updateView(
true);
2524 deactivateDirWatch();
2529 removeTrailingSpacesAndAddNewLineAtEof();
2534 if (!m_buffer->saveFile(localFilePath())) {
2536 activateDirWatch(oldPath);
2538 i18n(
"The document could not be saved, as it was not possible to write to %1.\nCheck that you have write access to this file or "
2539 "that enough disk space is available.\nThe original file may be lost or damaged. "
2540 "Don't quit the application until the file is successfully written.",
2556 m_modOnHdReason = OnDiskUnmodified;
2557 m_prevModOnHdReason = OnDiskUnmodified;
2558 Q_EMIT modifiedOnDisk(
this, m_modOnHd, m_modOnHdReason);
2563 m_undoManager->undoSafePoint();
2564 m_undoManager->updateLineModifications();
2572bool KTextEditor::DocumentPrivate::createBackupFile()
2575 const bool backupLocalFiles = config()->backupOnSaveLocal();
2576 const bool backupRemoteFiles = config()->backupOnSaveRemote();
2580 if (!backupLocalFiles && !backupRemoteFiles) {
2587 bool needBackup = backupLocalFiles && backupRemoteFiles;
2589 bool slowOrRemoteFile = !u.isLocalFile();
2590 if (!slowOrRemoteFile) {
2594 slowOrRemoteFile = (mountPoint && mountPoint->probablySlow());
2596 needBackup = (!slowOrRemoteFile && backupLocalFiles) || (slowOrRemoteFile && backupRemoteFiles);
2607 if (backupPrefix.isEmpty() && backupSuffix.isEmpty()) {
2614 u.setPath(backupPrefix + u.fileName() + backupSuffix);
2617 const QString fileName = u.fileName();
2619 u.setPath(u.path() + backupPrefix + fileName + backupSuffix);
2622 qCDebug(LOG_KTE) <<
"backup src file name: " << url();
2623 qCDebug(LOG_KTE) <<
"backup dst file name: " << u;
2626 bool backupSuccess =
false;
2629 if (u.isLocalFile()) {
2632 QFile backupFile(u.toLocalFile());
2633 if (backupFile.exists()) {
2634 backupFile.remove();
2637 backupSuccess =
QFile::copy(url().toLocalFile(), u.toLocalFile());
2639 backupSuccess =
true;
2645 if (statJob->
exec()) {
2650 backupSuccess = job->
exec();
2652 backupSuccess =
true;
2659 i18n(
"For file %1 no backup copy could be created before saving."
2660 " If an error occurs while saving, you might lose the data of this file."
2661 " A reason could be that the media you write to is full or the directory of the file is read-only for you.",
2663 i18n(
"Failed to create backup copy."),
2666 QStringLiteral(
"Backup Failed Warning"))
2674void KTextEditor::DocumentPrivate::readDirConfig()
2684 while (!seenDirectories.
contains(
dir.absolutePath())) {
2686 seenDirectories.
insert(
dir.absolutePath());
2694 QString line = stream.readLine();
2695 while ((linesRead < 32) && !line.
isNull()) {
2696 readVariableLine(line);
2698 line = stream.readLine();
2712#if EDITORCONFIG_FOUND
2716 EditorConfig editorConfig(
this);
2717 editorConfig.parse();
2721void KTextEditor::DocumentPrivate::activateDirWatch(
const QString &useFileName)
2723 QString fileToUse = useFileName;
2725 fileToUse = localFilePath();
2739 if (fileToUse == m_dirWatchFile) {
2744 deactivateDirWatch();
2747 if (url().isLocalFile() && !fileToUse.
isEmpty()) {
2749 m_dirWatchFile = fileToUse;
2753void KTextEditor::DocumentPrivate::deactivateDirWatch()
2755 if (!m_dirWatchFile.isEmpty()) {
2759 m_dirWatchFile.clear();
2762bool KTextEditor::DocumentPrivate::openUrl(
const QUrl &url)
2766 m_fileTypeSetByUser =
false;
2773bool KTextEditor::DocumentPrivate::closeUrl()
2778 if (!m_reloading && !url().isEmpty()) {
2779 if (m_fileChangedDialogsActivated && m_modOnHd) {
2781 delete m_modOnHdHandler;
2783 QWidget *parentWidget(dialogParent());
2786 +
i18n(
"Do you really want to continue to close this file? Data loss may occur."),
2787 i18n(
"Possible Data Loss"),
2790 QStringLiteral(
"kate_close_modonhd_%1").arg(m_modOnHdReason))
2793 m_reloading =
false;
2804 m_reloading =
false;
2810 Q_EMIT aboutToClose(
this);
2814 if (!m_messageHash.isEmpty()) {
2815 const auto keys = m_messageHash.keys();
2822 Q_EMIT aboutToInvalidateMovingInterfaceContent(
this);
2825 deactivateDirWatch();
2836 m_modOnHdReason = OnDiskUnmodified;
2837 m_prevModOnHdReason = OnDiskUnmodified;
2838 Q_EMIT modifiedOnDisk(
this, m_modOnHd, m_modOnHdReason);
2848 m_undoManager->clearUndo();
2849 m_undoManager->clearRedo();
2855 m_buffer->setHighlight(0);
2858 for (
auto view :
std::as_const(m_views)) {
2859 static_cast<ViewPrivate *
>(view)->clearSelection();
2860 static_cast<ViewPrivate *
>(view)->
clear();
2865 m_swapfile->fileClosed();
2872bool KTextEditor::DocumentPrivate::isDataRecoveryAvailable()
const
2874 return m_swapfile && m_swapfile->shouldRecover();
2877void KTextEditor::DocumentPrivate::recoverData()
2879 if (isDataRecoveryAvailable()) {
2880 m_swapfile->recover();
2884void KTextEditor::DocumentPrivate::discardDataRecovery()
2886 if (isDataRecoveryAvailable()) {
2887 m_swapfile->discard();
2891void KTextEditor::DocumentPrivate::setReadWrite(
bool rw)
2893 if (isReadWrite() == rw) {
2899 for (
auto v :
std::as_const(m_views)) {
2900 auto view =
static_cast<ViewPrivate *
>(v);
2901 view->slotUpdateUndo();
2902 view->slotReadWriteChanged();
2905 Q_EMIT readWriteChanged(
this);
2908void KTextEditor::DocumentPrivate::setModified(
bool m)
2910 if (isModified() != m) {
2913 for (
auto view :
std::as_const(m_views)) {
2914 static_cast<ViewPrivate *
>(view)->slotUpdateUndo();
2917 Q_EMIT modifiedChanged(
this);
2920 m_undoManager->setModified(m);
2926void KTextEditor::DocumentPrivate::makeAttribs(
bool needInvalidate)
2928 for (
auto view :
std::as_const(m_views)) {
2929 static_cast<ViewPrivate *
>(view)->renderer()->updateAttributes();
2932 if (needInvalidate) {
2933 m_buffer->invalidateHighlighting();
2936 for (
auto v :
std::as_const(m_views)) {
2937 auto view =
static_cast<ViewPrivate *
>(v);
2939 view->updateView(
true);
2944void KTextEditor::DocumentPrivate::internalHlChanged()
2951 Q_ASSERT(!m_views.contains(view));
2952 m_views.append(view);
2955 if (!m_fileType.isEmpty()) {
2960 readVariables(
true);
2962 setActiveView(view);
2967 Q_ASSERT(m_views.contains(view));
2968 m_views.removeAll(view);
2970 if (activeView() == view) {
2971 setActiveView(
nullptr);
2977 if (m_activeView == view) {
2981 m_activeView =
static_cast<KTextEditor::ViewPrivate *
>(view);
2984bool KTextEditor::DocumentPrivate::ownedView(KTextEditor::ViewPrivate *view)
2987 return (m_views.contains(view));
2990int KTextEditor::DocumentPrivate::toVirtualColumn(
int line,
int column)
const
2998 return toVirtualColumn(cursor.
line(), cursor.
column());
3001int KTextEditor::DocumentPrivate::fromVirtualColumn(
int line,
int column)
const
3007int KTextEditor::DocumentPrivate::fromVirtualColumn(
const KTextEditor::Cursor cursor)
const
3009 return fromVirtualColumn(cursor.
line(), cursor.
column());
3018 bool skipAutobrace = closingBracket ==
QLatin1Char(
'\'');
3019 if (highlight() && skipAutobrace) {
3021 skipAutobrace = highlight()->spellCheckingRequiredForLocation(
this, pos - Cursor{0, 1});
3024 if (!skipAutobrace && (closingBracket ==
QLatin1Char(
'\''))) {
3030 skipAutobrace = (count % 2 == 0) ?
true : false;
3032 if (!skipAutobrace && (closingBracket ==
QLatin1Char(
'\"'))) {
3037 skipAutobrace = (count % 2 == 0) ?
true : false;
3039 return skipAutobrace;
3042void KTextEditor::DocumentPrivate::typeChars(KTextEditor::ViewPrivate *view,
QString chars)
3050 QChar closingBracket;
3051 if (view->config()->autoBrackets()) {
3053 const QChar typedChar = chars.
at(0);
3054 const QChar openBracket = matchingStartBracket(typedChar);
3055 if (!openBracket.
isNull()) {
3057 if ((characterAt(curPos) == typedChar) && findMatchingBracket(curPos, 123 ).
isValid()) {
3059 view->cursorRight();
3065 if (chars.
size() == 1) {
3067 closingBracket = matchingEndBracket(typedChar);
3070 if (m_currentAutobraceClosingChar == typedChar && m_currentAutobraceRange) {
3072 m_currentAutobraceRange.reset(
nullptr);
3073 view->cursorRight();
3080 if (view->selection() && closingBracket.
isNull() && view->config()->encloseSelectionInChars()) {
3081 const QChar typedChar = chars.
at(0);
3082 if (view->config()->charsToEncloseSelection().
contains(typedChar)) {
3091 if (view->selection() && !closingBracket.
isNull()) {
3092 std::unique_ptr<KTextEditor::MovingRange> selectionRange(newMovingRange(view->selectionRange()));
3093 const int startLine = qMax(0, selectionRange->start().line());
3094 const int endLine = qMin(selectionRange->end().line(), lastLine());
3095 const bool blockMode = view->blockSelection() && (startLine != endLine);
3097 if (selectionRange->start().column() > selectionRange->end().column()) {
3100 selectionRange->setInsertBehaviors(MovingRange::ExpandLeft | MovingRange::ExpandRight);
3103 const int startColumn = qMin(selectionRange->start().column(), selectionRange->end().column());
3104 const int endColumn = qMax(selectionRange->start().column(), selectionRange->end().column());
3106 for (
int line = startLine; line <= endLine; ++line) {
3108 insertText(r.end(),
QString(closingBracket));
3109 view->slotTextInserted(view, r.end(),
QString(closingBracket));
3110 insertText(r.start(), chars);
3111 view->slotTextInserted(view, r.start(), chars);
3115 for (
const auto &cursor : view->secondaryCursors()) {
3116 if (!cursor.range) {
3119 const auto &currSelectionRange = cursor.range;
3120 auto expandBehaviour = currSelectionRange->insertBehaviors();
3122 insertText(currSelectionRange->end(),
QString(closingBracket));
3123 insertText(currSelectionRange->start(), chars);
3124 currSelectionRange->setInsertBehaviors(expandBehaviour);
3125 cursor.pos->
setPosition(currSelectionRange->end());
3126 auto mutableCursor =
const_cast<KTextEditor::ViewPrivate::SecondaryCursor *
>(&cursor);
3127 mutableCursor->anchor = currSelectionRange->start().toCursor();
3131 insertText(selectionRange->end(),
QString(closingBracket));
3132 view->slotTextInserted(view, selectionRange->end(),
QString(closingBracket));
3133 insertText(selectionRange->start(), chars);
3134 view->slotTextInserted(view, selectionRange->start(), chars);
3138 view->setSelection(selectionRange->toRange());
3139 view->setCursorPosition(selectionRange->end());
3146 if (!view->config()->persistentSelection() && view->selection()) {
3147 view->removeSelectedText();
3152 const bool multiLineBlockMode = view->blockSelection() && view->selection();
3153 if (view->currentInputMode()->overwrite()) {
3156 const int startLine = multiLineBlockMode ? qMax(0, selectionRange.
start().
line()) : view->cursorPosition().line();
3157 const int endLine = multiLineBlockMode ? qMin(selectionRange.
end().
line(), lastLine()) : startLine;
3158 const int virtualColumn = toVirtualColumn(multiLineBlockMode ? selectionRange.
end() : view->cursorPosition());
3160 for (
int line = endLine; line >= startLine; --line) {
3162 const int column = fromVirtualColumn(line, virtualColumn);
3166 if (oldCur.column() < lineLength(line)) {
3168 view->currentInputMode()->overwrittenChar(removed);
3175 chars = eventuallyReplaceTabs(view->cursorPosition(), chars);
3177 if (multiLineBlockMode) {
3179 const int startLine = qMax(0, selectionRange.
start().
line());
3180 const int endLine = qMin(selectionRange.
end().
line(), lastLine());
3181 const int column = toVirtualColumn(selectionRange.
end());
3182 for (
int line = endLine; line >= startLine; --line) {
3183 editInsertText(line, fromVirtualColumn(line, column), chars);
3185 int newSelectionColumn = toVirtualColumn(view->cursorPosition());
3188 view->setSelection(selectionRange);
3193 view->completionWidget()->setIgnoreBufferSignals(
true);
3194 const auto &sc = view->secondaryCursors();
3195 const bool hasClosingBracket = !closingBracket.
isNull();
3196 const QString closingChar = closingBracket;
3197 for (
const auto &c : sc) {
3198 insertText(c.cursor(), chars);
3199 const auto pos = c.cursor();
3200 const auto nextChar = view->document()->
text({pos, pos + Cursor{0, 1}}).trimmed();
3201 if (hasClosingBracket && !skipAutoBrace(closingBracket, pos) && (nextChar.isEmpty() || !nextChar.at(0).isLetterOrNumber())) {
3202 insertText(c.cursor(), closingChar);
3206 view->completionWidget()->setIgnoreBufferSignals(
false);
3208 insertText(view->cursorPosition(), chars);
3215 if (!closingBracket.
isNull() && !skipAutoBrace(closingBracket, view->cursorPosition())) {
3217 const auto cursorPos = view->cursorPosition();
3218 const auto nextChar = view->document()->
text({cursorPos, cursorPos + Cursor{0, 1}}).trimmed();
3219 if (nextChar.isEmpty() || !nextChar.at(0).isLetterOrNumber()) {
3220 insertText(view->cursorPosition(),
QString(closingBracket));
3221 const auto insertedAt(view->cursorPosition());
3222 view->setCursorPosition(cursorPos);
3227 chars.
append(closingBracket);
3229 m_currentAutobraceClosingChar = closingBracket;
3236 const auto &secondaryCursors = view->secondaryCursors();
3237 for (
const auto &c : secondaryCursors) {
3238 m_indenter->userTypedChar(view, c.cursor(), chars.
isEmpty() ?
QChar() : chars.at(chars.length() - 1));
3243 m_indenter->userTypedChar(view, b, chars.
isEmpty() ?
QChar() : chars.at(chars.length() - 1));
3246 view->slotTextInserted(view, oldCur, chars);
3251 if (m_currentAutobraceRange && !m_currentAutobraceRange->toRange().contains(newPos)) {
3252 m_currentAutobraceRange.reset();
3256void KTextEditor::DocumentPrivate::newLine(KTextEditor::ViewPrivate *v, KTextEditor::DocumentPrivate::NewLineIndent indent, NewLinePos newLinePos)
3260 if (!v->config()->persistentSelection() && v->selection()) {
3261 v->removeSelectedText();
3262 v->clearSelection();
3266 if (c.
line() > lastLine()) {
3276 int len = lineLength(ln);
3286 m_buffer->updateHighlighting();
3293 bool moveCursorToTop =
false;
3294 if (newLinePos == Above) {
3295 if (pos.
line() <= 0) {
3298 moveCursorToTop =
true;
3303 }
else if (newLinePos == Below) {
3304 int lastCol = lineLength(pos.
line());
3307 return std::pair{pos, moveCursorToTop};
3311 const auto &secondaryCursors = v->secondaryCursors();
3312 if (!secondaryCursors.empty()) {
3315 for (
const auto &c : secondaryCursors) {
3316 const auto [newPos, moveCursorToTop] = adjustCusorPos(c.cursor());
3318 insertNewLine(c.cursor());
3319 if (moveCursorToTop) {
3323 if (indent == KTextEditor::DocumentPrivate::Indent) {
3327 v->setCursorPosition(c.cursor());
3328 m_indenter->userTypedChar(v, c.cursor(),
QLatin1Char(
'\n'));
3334 v->setCursorPosition(savedPrimary.toCursor());
3337 const auto [newPos, moveCursorToTop] = adjustCusorPos(v->cursorPosition());
3338 v->setCursorPosition(newPos);
3339 insertNewLine(v->cursorPosition());
3340 if (moveCursorToTop) {
3341 v->setCursorPosition({0, 0});
3344 if (indent == KTextEditor::DocumentPrivate::Indent) {
3345 m_indenter->userTypedChar(v, v->cursorPosition(),
QLatin1Char(
'\n'));
3354 if (textLine.
length() < 2) {
3358 uint col = cursor.
column();
3364 if ((textLine.
length() - col) < 2) {
3368 uint line = cursor.
line();
3379 editRemoveText(line, col, 2);
3380 editInsertText(line, col, s);
3387 Q_ASSERT(!firstWord.
overlaps(secondWord));
3395 const QString tempString = text(secondWord);
3398 replaceText(secondWord, text(firstWord));
3399 replaceText(firstWord, tempString);
3405 int col = qMax(c.
column(), 0);
3406 int line = qMax(c.
line(), 0);
3407 if ((col == 0) && (line == 0)) {
3410 if (line >= lines()) {
3417 bool useNextBlock =
false;
3418 if (config()->backspaceIndents()) {
3425 if (pos < 0 || pos >= (
int)colX) {
3427 if ((
int)col > textLine.
length()) {
3433 useNextBlock =
true;
3436 if (!config()->backspaceIndents() || useNextBlock) {
3439 if (!view->config()->backspaceRemoveComposed()) {
3440 beginCursor.setColumn(col - 1);
3442 if (!isValidTextPosition(beginCursor)) {
3444 beginCursor.setColumn(col - 2);
3447 if (
auto l = view->textLayout(c)) {
3448 beginCursor.setColumn(l->previousCursorPosition(c.
column()));
3463 if (config()->wordWrap() && textLine.
endsWith(QStringLiteral(
" "))) {
3476void KTextEditor::DocumentPrivate::backspace(KTextEditor::ViewPrivate *view)
3478 if (!view->config()->persistentSelection() && view->hasSelections()) {
3482 if (view->blockSelection() && view->selection() && range.
start().
column() > 0 && toVirtualColumn(range.
start()) == toVirtualColumn(range.
end())) {
3485 view->setSelection(range);
3487 view->removeSelectedText();
3488 view->ensureUniqueCursors();
3496 const auto &multiCursors = view->secondaryCursors();
3497 view->completionWidget()->setIgnoreBufferSignals(
true);
3498 for (
const auto &c : multiCursors) {
3499 const auto newPos = backspaceAtCursor(view, c.cursor());
3504 view->completionWidget()->setIgnoreBufferSignals(
false);
3507 auto newPos = backspaceAtCursor(view, view->cursorPosition());
3509 view->setCursorPosition(newPos);
3512 view->ensureUniqueCursors();
3517 if (m_currentAutobraceRange) {
3518 const auto r = m_currentAutobraceRange->toRange();
3521 del(view, view->cursorPosition());
3522 m_currentAutobraceRange.reset();
3527void KTextEditor::DocumentPrivate::del(KTextEditor::ViewPrivate *view,
const KTextEditor::Cursor c)
3529 if (!view->config()->persistentSelection() && view->selection()) {
3532 if (view->blockSelection() && toVirtualColumn(range.
start()) == toVirtualColumn(range.
end())) {
3535 view->setSelection(range);
3537 view->removeSelectedText();
3542 if (c.
column() < m_buffer->lineLength(c.
line())) {
3545 }
else if (c.
line() < lastLine()) {
3550bool KTextEditor::DocumentPrivate::multiPaste(KTextEditor::ViewPrivate *view,
const QStringList &texts)
3552 if (texts.
isEmpty() || view->isMulticursorNotAllowed() || view->secondaryCursors().size() + 1 != (
size_t)texts.
size()) {
3556 m_undoManager->undoSafePoint();
3559 if (view->selection()) {
3560 view->removeSelectedText();
3563 auto plainSecondaryCursors = view->plainSecondaryCursors();
3564 KTextEditor::ViewPrivate::PlainSecondaryCursor primary;
3565 primary.pos = view->cursorPosition();
3566 primary.range = view->selectionRange();
3567 plainSecondaryCursors.append(primary);
3568 std::sort(plainSecondaryCursors.begin(), plainSecondaryCursors.end());
3572 for (
int i = texts.
size() - 1; i >= 0; --i) {
3574 text.
replace(re, QStringLiteral(
"\n"));
3577 insertText(pos, text,
false);
3585void KTextEditor::DocumentPrivate::paste(KTextEditor::ViewPrivate *view,
const QString &text)
3598 const bool isSingleLine = lines == 0;
3600 m_undoManager->undoSafePoint();
3606 bool skipIndentOnPaste =
false;
3608 const int length = lineLength(pos.
line());
3610 skipIndentOnPaste = length > 0;
3613 if (!view->config()->persistentSelection() && view->selection()) {
3614 pos = view->selectionRange().
start();
3615 if (view->blockSelection()) {
3616 pos = rangeOnLine(view->selectionRange(), pos.
line()).
start();
3623 view->removeSelectedText();
3626 if (config()->ovr()) {
3629 if (!view->blockSelection()) {
3630 int endColumn = (pasteLines.count() == 1 ? pos.
column() : 0) + pasteLines.last().length();
3633 int maxi = qMin(pos.
line() + pasteLines.count(), this->lines());
3635 for (
int i = pos.
line(); i < maxi; ++i) {
3636 int pasteLength = pasteLines.at(i - pos.
line()).length();
3642 insertText(pos, s, view->blockSelection());
3649 if (view->blockSelection()) {
3650 view->setCursorPositionInternal(pos);
3653 if (config()->indentPastedText()) {
3655 if (!skipIndentOnPaste) {
3656 m_indenter->indent(view, range);
3660 if (!view->blockSelection()) {
3661 Q_EMIT charactersSemiInteractivelyInserted(pos, s);
3663 m_undoManager->undoSafePoint();
3668 if (!isReadWrite()) {
3673 m_indenter->changeIndent(range, change);
3677void KTextEditor::DocumentPrivate::align(KTextEditor::ViewPrivate *view,
KTextEditor::Range range)
3679 m_indenter->indent(view, range);
3686 if (lines.
size() < 2) {
3692 int selectionStartColumn = range.
start().
column();
3694 for (
const auto &line : lines) {
3696 if (!
match.hasMatch()) {
3697 patternStartColumns.
append(-1);
3698 }
else if (
match.lastCapturedIndex() == 0) {
3699 patternStartColumns.
append(
match.capturedStart(0) + (blockwise ? selectionStartColumn : 0));
3701 patternStartColumns.
append(
match.capturedStart(1) + (blockwise ? selectionStartColumn : 0));
3704 if (!blockwise && patternStartColumns[0] != -1) {
3705 patternStartColumns[0] += selectionStartColumn;
3708 int maxColumn = *std::max_element(patternStartColumns.
cbegin(), patternStartColumns.
cend());
3711 for (
int i = 0; i < lines.
size(); ++i) {
3712 if (patternStartColumns[i] != -1) {
3719void KTextEditor::DocumentPrivate::insertTab(KTextEditor::ViewPrivate *view,
const KTextEditor::Cursor)
3721 if (!isReadWrite()) {
3725 int lineLen = line(view->cursorPosition().
line()).length();
3730 if (!view->config()->persistentSelection() && view->selection()) {
3731 view->removeSelectedText();
3732 }
else if (view->currentInputMode()->overwrite() && c.
column() < lineLen) {
3737 view->currentInputMode()->overwrittenChar(removed);
3741 c = view->cursorPosition();
3742 editInsertText(c.
line(), c.
column(), QStringLiteral(
"\t"));
3751bool KTextEditor::DocumentPrivate::removeStringFromBeginning(
int line,
const QString &str)
3775bool KTextEditor::DocumentPrivate::removeStringFromEnd(
int line,
const QString &str)
3780 bool there = textline.
endsWith(str);
3802 const bool replacetabs = config()->replaceTabsDyn();
3806 const int indentWidth = config()->indentationWidth();
3809 int column = cursorPos.
column();
3815 for (
const QChar ch : str) {
3816 if (ch == tabChar) {
3819 int spacesToInsert = indentWidth - (column % indentWidth);
3821 column += spacesToInsert;
3834void KTextEditor::DocumentPrivate::addStartLineCommentToSingleLine(
int line,
int attrib)
3836 const QString commentLineMark = highlight()->getCommentSingleLineStart(attrib) +
QLatin1Char(
' ');
3839 if (highlight()->getCommentSingleLinePosition(attrib) == KSyntaxHighlighting::CommentPosition::AfterWhitespace) {
3850bool KTextEditor::DocumentPrivate::removeStartLineCommentFromSingleLine(
int line,
int attrib)
3852 const QString shortCommentMark = highlight()->getCommentSingleLineStart(attrib);
3858 bool removed = (removeStringFromBeginning(line, longCommentMark) || removeStringFromBeginning(line, shortCommentMark));
3869void KTextEditor::DocumentPrivate::addStartStopCommentToSingleLine(
int line,
int attrib)
3871 const QString startCommentMark = highlight()->getCommentStart(attrib) +
QLatin1Char(
' ');
3872 const QString stopCommentMark =
QLatin1Char(
' ') + highlight()->getCommentEnd(attrib);
3880 const int col = m_buffer->lineLength(line);
3892bool KTextEditor::DocumentPrivate::removeStartStopCommentFromSingleLine(
int line,
int attrib)
3894 const QString shortStartCommentMark = highlight()->getCommentStart(attrib);
3896 const QString shortStopCommentMark = highlight()->getCommentEnd(attrib);
3902 const bool removedStart = (removeStringFromBeginning(line, longStartCommentMark) || removeStringFromBeginning(line, shortStartCommentMark));
3905 const bool removedStop = removedStart && (removeStringFromEnd(line, longStopCommentMark) || removeStringFromEnd(line, shortStopCommentMark));
3909 return (removedStart || removedStop);
3916void KTextEditor::DocumentPrivate::addStartStopCommentToSelection(
KTextEditor::Range selection,
bool blockSelection,
int attrib)
3918 const QString startComment = highlight()->getCommentStart(attrib);
3919 const QString endComment = highlight()->getCommentEnd(attrib);
3929 if (!blockSelection) {
3930 insertText(range.
end(), endComment);
3931 insertText(range.
start(), startComment);
3933 for (
int line = range.
start().
line(); line <= range.
end().
line(); line++) {
3935 insertText(subRange.
end(), endComment);
3936 insertText(subRange.
start(), startComment);
3947void KTextEditor::DocumentPrivate::addStartLineCommentToSelection(
KTextEditor::Range selection,
int attrib)
3950 int el = selection.
end().
line();
3953 if ((selection.
end().
column() == 0) && (el > 0)) {
3957 if (sl < 0 || el < 0 || sl >= lines() || el >= lines()) {
3963 const QString commentLineMark = highlight()->getCommentSingleLineStart(attrib) +
QLatin1Char(
' ');
3966 if (highlight()->getCommentSingleLinePosition(attrib) == KSyntaxHighlighting::CommentPosition::AfterWhitespace) {
3974 col = std::numeric_limits<int>::max();
3976 for (
int l = el; l >= sl; l--) {
3977 const auto line = plainKateTextLine(l);
3978 if (line.length() == 0) {
3981 col = qMin(col, qMax(0, line.firstChar()));
3988 if (col == std::numeric_limits<int>::max()) {
3995 for (
int l = el; l >= sl; l--) {
4002bool KTextEditor::DocumentPrivate::nextNonSpaceCharPos(
int &line,
int &col)
4004 for (; line >= 0 && line < m_buffer->lines(); line++) {
4018bool KTextEditor::DocumentPrivate::previousNonSpaceCharPos(
int &line,
int &col)
4020 while (line >= 0 && line < m_buffer->lines()) {
4042bool KTextEditor::DocumentPrivate::removeStartStopCommentFromSelection(
KTextEditor::Range selection,
int attrib)
4044 const QString startComment = highlight()->getCommentStart(attrib);
4045 const QString endComment = highlight()->getCommentEnd(attrib);
4047 int sl = qMax<int>(0, selection.
start().
line());
4048 int el = qMin<int>(selection.
end().
line(), lastLine());
4055 }
else if (el > 0) {
4057 ec = m_buffer->lineLength(el) - 1;
4060 const int startCommentLen = startComment.
length();
4061 const int endCommentLen = endComment.
length();
4065 bool remove = nextNonSpaceCharPos(sl, sc) && m_buffer->plainLine(sl).matchesAt(sc, startComment) && previousNonSpaceCharPos(el, ec)
4066 && ((ec - endCommentLen + 1) >= 0) && m_buffer->plainLine(el).matchesAt(ec - endCommentLen + 1, endComment);
4083 const QString startComment = highlight()->getCommentStart(attrib);
4084 const QString endComment = highlight()->getCommentEnd(attrib);
4085 const int startCommentLen = startComment.
length();
4086 const int endCommentLen = endComment.
length();
4088 const bool remove = m_buffer->plainLine(
start.line()).matchesAt(
start.column(), startComment)
4089 && m_buffer->plainLine(
end.line()).matchesAt(
end.column() - endCommentLen, endComment);
4103bool KTextEditor::DocumentPrivate::removeStartLineCommentFromSelection(
KTextEditor::Range selection,
int attrib,
bool toggleComment)
4105 const QString shortCommentMark = highlight()->getCommentSingleLineStart(attrib);
4108 const int startLine = selection.
start().
line();
4109 int endLine = selection.
end().
line();
4111 if ((selection.
end().
column() == 0) && (endLine > 0)) {
4115 bool removed =
false;
4121 if (toggleComment) {
4122 bool allLinesAreCommented =
true;
4123 for (
int line = endLine; line >= startLine; line--) {
4124 const auto ln = m_buffer->plainLine(line);
4125 const QString &text = ln.text();
4132 textView = textView.trimmed();
4133 if (!textView.startsWith(shortCommentMark) && !textView.startsWith(longCommentMark)) {
4134 allLinesAreCommented =
false;
4138 if (!allLinesAreCommented) {
4146 for (
int z = endLine; z >= startLine; z--) {
4148 removed = (removeStringFromBeginning(z, longCommentMark) || removeStringFromBeginning(z, shortCommentMark) || removed);
4159 const bool hasSelection = !selection.
isEmpty();
4160 int selectionCol = 0;
4165 const int line = c.
line();
4167 int startAttrib = 0;
4170 if (selectionCol < ln.
length()) {
4171 startAttrib = ln.
attribute(selectionCol);
4176 bool hasStartLineCommentMark = !(highlight()->getCommentSingleLineStart(startAttrib).isEmpty());
4177 bool hasStartStopCommentMark = (!(highlight()->getCommentStart(startAttrib).isEmpty()) && !(highlight()->getCommentEnd(startAttrib).isEmpty()));
4179 if (changeType == Comment) {
4180 if (!hasSelection) {
4181 if (hasStartLineCommentMark) {
4182 addStartLineCommentToSingleLine(line, startAttrib);
4183 }
else if (hasStartStopCommentMark) {
4184 addStartStopCommentToSingleLine(line, startAttrib);
4195 if (hasStartStopCommentMark
4196 && (!hasStartLineCommentMark
4199 addStartStopCommentToSelection(selection, blockSelect, startAttrib);
4200 }
else if (hasStartLineCommentMark) {
4201 addStartLineCommentToSelection(selection, startAttrib);
4205 bool removed =
false;
4206 const bool toggleComment = changeType == ToggleComment;
4207 if (!hasSelection) {
4208 removed = (hasStartLineCommentMark && removeStartLineCommentFromSingleLine(line, startAttrib))
4209 || (hasStartStopCommentMark && removeStartStopCommentFromSingleLine(line, startAttrib));
4212 removed = (hasStartStopCommentMark && removeStartStopCommentFromSelection(selection, startAttrib))
4213 || (hasStartLineCommentMark && removeStartLineCommentFromSelection(selection, startAttrib, toggleComment));
4217 if (!removed && toggleComment) {
4218 commentSelection(selection, c, blockSelect, Comment);
4227void KTextEditor::DocumentPrivate::comment(KTextEditor::ViewPrivate *v, uint line, uint column, CommentType change)
4230 const bool skipWordWrap = wordWrap();
4237 if (v->selection()) {
4238 const auto &cursors = v->secondaryCursors();
4239 for (
const auto &c : cursors) {
4243 commentSelection(c.range->toRange(), c.cursor(),
false, change);
4246 commentSelection(v->selectionRange(), c, v->blockSelection(), change);
4248 const auto &cursors = v->secondaryCursors();
4249 for (
const auto &c : cursors) {
4250 commentSelection({}, c.cursor(),
false, change);
4262void KTextEditor::DocumentPrivate::transformCursorOrRange(KTextEditor::ViewPrivate *v,
4265 KTextEditor::DocumentPrivate::TextTransform t)
4267 if (v->selection()) {
4279 if (range.
start().
line() == selection.
end().
line() || v->blockSelection()) {
4284 int swapCol =
start;
4294 if (t == Uppercase) {
4297 }
else if (t == Lowercase) {
4310 && !highlight()->isInWord(l.
at(range.
start().
column() - 1)))
4311 || (p && !highlight()->isInWord(s.
at(p - 1)))) {
4320 insertText(range.
start(), s);
4355 insertText(cursor, s);
4361void KTextEditor::DocumentPrivate::transform(KTextEditor::ViewPrivate *v,
const KTextEditor::Cursor c, KTextEditor::DocumentPrivate::TextTransform t)
4365 if (v->selection()) {
4366 const auto &cursors = v->secondaryCursors();
4367 for (
const auto &c : cursors) {
4371 auto pos = c.pos->toCursor();
4372 transformCursorOrRange(v, c.anchor, c.range->toRange(), t);
4376 const auto selRange = v->selectionRange();
4377 transformCursorOrRange(v, c, v->selectionRange(), t);
4378 v->setSelection(selRange);
4379 v->setCursorPosition(c);
4381 const auto &secondaryCursors = v->secondaryCursors();
4382 for (
const auto &c : secondaryCursors) {
4383 transformCursorOrRange(v, c.cursor(), {}, t);
4385 transformCursorOrRange(v, c, {}, t);
4391void KTextEditor::DocumentPrivate::joinLines(uint first, uint last)
4396 while (first < last) {
4397 if (line >= lines() || line + 1 >= lines()) {
4413 editRemoveText(line + 1, 0, pos);
4416 editInsertText(line + 1, 0, QStringLiteral(
" "));
4420 editRemoveText(line + 1, 0, tl.
length());
4423 editUnWrapLine(line);
4431 for (
auto view :
std::as_const(m_views)) {
4432 static_cast<ViewPrivate *
>(view)->tagLines(lineRange,
true);
4436void KTextEditor::DocumentPrivate::tagLine(
int line)
4438 tagLines({line, line});
4441void KTextEditor::DocumentPrivate::repaintViews(
bool paintOnlyDirty)
4443 for (
auto view :
std::as_const(m_views)) {
4444 static_cast<ViewPrivate *
>(view)->repaintText(paintOnlyDirty);
4457 if (maxLines < 0 ||
start.line() < 0 ||
start.line() >= lines()) {
4467 if (config()->ovr()) {
4468 if (isBracket(right)) {
4473 }
else if (isBracket(right)) {
4475 }
else if (isBracket(left)) {
4482 const QChar opposite = matchingBracket(bracket);
4487 const int searchDir = isStartBracket(bracket) ? 1 : -1;
4490 const int minLine = qMax(range.
start().
line() - maxLines, 0);
4491 const int maxLine = qMin(range.
start().
line() + maxLines, documentEnd().line());
4496 int validAttr = kateTextLine(cursor.
line()).attribute(cursor.
column());
4498 while (cursor.
line() >= minLine && cursor.
line() <= maxLine) {
4499 if (!cursor.move(searchDir)) {
4507 if (c == opposite) {
4509 if (searchDir > 0) {
4510 range.
setEnd(cursor.toCursor());
4517 }
else if (c == bracket) {
4533void KTextEditor::DocumentPrivate::updateDocName()
4536 if (!url().isEmpty() && (m_docName == removeNewLines(url().fileName()) || m_docName.
startsWith(removeNewLines(url().fileName()) +
QLatin1String(
" (")))) {
4542 std::vector<KTextEditor::DocumentPrivate *> docsWithSameName;
4546 auto doc =
static_cast<KTextEditor::DocumentPrivate *
>(kteDoc);
4547 if ((doc !=
this) && (doc->url().fileName() == url().
fileName())) {
4548 if (doc->m_docNameNumber > count) {
4549 count = doc->m_docNameNumber;
4551 docsWithSameName.push_back(doc);
4555 m_docNameNumber = count + 1;
4558 m_docName = removeNewLines(url().fileName());
4560 m_isUntitled = m_docName.isEmpty();
4562 if (!m_isUntitled && !docsWithSameName.empty()) {
4563 docsWithSameName.push_back(
this);
4564 uniquifyDocNames(docsWithSameName);
4569 m_docName =
i18n(
"Untitled");
4572 if (m_docNameNumber > 0) {
4577 if (oldName != m_docName) {
4578 Q_EMIT documentNameChanged(
this);
4593static QString shortestPrefix(
const std::vector<QString> &urls, KTextEditor::DocumentPrivate *doc)
4596 int lastSlash = url.lastIndexOf(
QLatin1Char(
'/'));
4597 if (lastSlash == -1) {
4601 int fileNameStart = lastSlash;
4604 lastSlash = url.lastIndexOf(
QLatin1Char(
'/'), lastSlash);
4605 if (lastSlash == -1) {
4608 return url.mid(lastSlash, fileNameStart);
4613 urlv = urlv.
mid(lastSlash);
4615 for (
size_t i = 0; i < urls.size(); ++i) {
4616 if (urls[i] == url) {
4620 if (urls[i].endsWith(urlv)) {
4621 lastSlash = url.lastIndexOf(
QLatin1Char(
'/'), lastSlash - 1);
4622 if (lastSlash <= 0) {
4624 return url.mid(0, fileNameStart);
4627 urlv = urlView.
mid(lastSlash);
4632 return url.mid(lastSlash + 1, fileNameStart - (lastSlash + 1));
4635void KTextEditor::DocumentPrivate::uniquifyDocNames(
const std::vector<KTextEditor::DocumentPrivate *> &docs)
4637 std::vector<QString> paths;
4638 paths.reserve(docs.size());
4639 std::transform(docs.begin(), docs.end(), std::back_inserter(paths), [](
const KTextEditor::DocumentPrivate *d) {
4640 return d->url().toString(QUrl::NormalizePathSegments | QUrl::PreferLocalFile);
4643 for (
const auto doc : docs) {
4644 const QString prefix = shortestPrefix(paths, doc);
4645 const QString fileName = doc->url().fileName();
4646 const QString oldName = doc->m_docName;
4649 doc->m_docName = fileName + QStringLiteral(
" - ") + prefix;
4651 doc->m_docName = fileName;
4654 if (doc->m_docName != oldName) {
4655 Q_EMIT doc->documentNameChanged(doc);
4662 if (url().isEmpty() || !m_modOnHd) {
4666 if (!isModified() && isAutoReload()) {
4667 onModOnHdAutoReload();
4671 if (!m_fileChangedDialogsActivated || m_modOnHdHandler) {
4676 if (m_modOnHdReason == m_prevModOnHdReason) {
4679 m_prevModOnHdReason = m_modOnHdReason;
4681 m_modOnHdHandler =
new KateModOnHdPrompt(
this, m_modOnHdReason, reasonedMOHString());
4682 connect(m_modOnHdHandler.data(), &KateModOnHdPrompt::saveAsTriggered,
this, &DocumentPrivate::onModOnHdSaveAs);
4683 connect(m_modOnHdHandler.data(), &KateModOnHdPrompt::closeTriggered,
this, &DocumentPrivate::onModOnHdClose);
4684 connect(m_modOnHdHandler.data(), &KateModOnHdPrompt::reloadTriggered,
this, &DocumentPrivate::onModOnHdReload);
4685 connect(m_modOnHdHandler.data(), &KateModOnHdPrompt::autoReloadTriggered,
this, &DocumentPrivate::onModOnHdAutoReload);
4686 connect(m_modOnHdHandler.data(), &KateModOnHdPrompt::ignoreTriggered,
this, &DocumentPrivate::onModOnHdIgnore);
4689void KTextEditor::DocumentPrivate::onModOnHdSaveAs()
4692 const QUrl res = getSaveFileUrl(
i18n(
"Save File"));
4698 delete m_modOnHdHandler;
4699 m_prevModOnHdReason = OnDiskUnmodified;
4700 Q_EMIT modifiedOnDisk(
this,
false, OnDiskUnmodified);
4707void KTextEditor::DocumentPrivate::onModOnHdClose()
4710 m_fileChangedDialogsActivated =
false;
4723void KTextEditor::DocumentPrivate::onModOnHdReload()
4726 m_prevModOnHdReason = OnDiskUnmodified;
4727 Q_EMIT modifiedOnDisk(
this,
false, OnDiskUnmodified);
4733 m_undoManager->clearUndo();
4734 m_undoManager->clearRedo();
4737 delete m_modOnHdHandler;
4740void KTextEditor::DocumentPrivate::autoReloadToggled(
bool b)
4742 m_autoReloadMode->setChecked(b);
4746 disconnect(&m_modOnHdTimer, &
QTimer::timeout,
this, &DocumentPrivate::onModOnHdAutoReload);
4750bool KTextEditor::DocumentPrivate::isAutoReload()
4752 return m_autoReloadMode->isChecked();
4755void KTextEditor::DocumentPrivate::delayAutoReload()
4757 if (isAutoReload()) {
4758 m_autoReloadThrottle.start();
4762void KTextEditor::DocumentPrivate::onModOnHdAutoReload()
4764 if (m_modOnHdHandler) {
4765 delete m_modOnHdHandler;
4766 autoReloadToggled(
true);
4769 if (!isAutoReload()) {
4773 if (m_modOnHd && !m_reloading && !m_autoReloadThrottle.isActive()) {
4775 m_prevModOnHdReason = OnDiskUnmodified;
4776 Q_EMIT modifiedOnDisk(
this,
false, OnDiskUnmodified);
4780 m_undoManager->clearUndo();
4781 m_undoManager->clearRedo();
4784 m_autoReloadThrottle.start();
4788void KTextEditor::DocumentPrivate::onModOnHdIgnore()
4791 delete m_modOnHdHandler;
4794void KTextEditor::DocumentPrivate::setModifiedOnDisk(ModifiedOnDiskReason reason)
4796 m_modOnHdReason = reason;
4797 m_modOnHd = (reason != OnDiskUnmodified);
4798 Q_EMIT modifiedOnDisk(
this, (reason != OnDiskUnmodified), reason);
4801class KateDocumentTmpMark
4808void KTextEditor::DocumentPrivate::setModifiedOnDiskWarning(
bool on)
4810 m_fileChangedDialogsActivated = on;
4813bool KTextEditor::DocumentPrivate::documentReload()
4815 if (url().isEmpty()) {
4827 m_undoManager->clearUndo();
4828 m_undoManager->clearRedo();
4833 delete m_modOnHdHandler;
4835 Q_EMIT aboutToReload(
this);
4839 std::transform(m_marks.cbegin(), m_marks.cend(), std::back_inserter(tmp), [
this](
KTextEditor::Mark *mark) {
4840 return KateDocumentTmpMark{line(mark->line), *mark};
4844 const QString oldMode = mode();
4845 const bool modeByUser = m_fileTypeSetByUser;
4846 const QString oldHlMode = highlightingMode();
4847 const bool hlByUser = m_hlSetByUser;
4849 m_storedVariables.clear();
4853 std::transform(m_views.cbegin(), m_views.cend(), std::back_inserter(cursorPositions), [](
KTextEditor::View *v) {
4854 return std::pair<KTextEditor::ViewPrivate *, KTextEditor::Cursor>(static_cast<ViewPrivate *>(v), v->cursorPosition());
4859 for (
auto *view : m_views) {
4860 static_cast<ViewPrivate *
>(view)->clearSecondaryCursors();
4863 static_cast<ViewPrivate *
>(view)->clearFoldingState();
4868 KTextEditor::DocumentPrivate::openUrl(url());
4871 m_userSetEncodingForNextReload =
false;
4874 for (
auto v :
std::as_const(m_views)) {
4876 auto it = std::find_if(cursorPositions.
cbegin(), cursorPositions.
cend(), [v](
const std::pair<KTextEditor::ViewPrivate *, KTextEditor::Cursor> &p) {
4877 return p.first == v;
4879 v->setCursorPosition(it->second);
4885 const int lines = this->lines();
4886 for (
const auto &tmpMark : tmp) {
4887 if (tmpMark.mark.line < lines) {
4888 if (tmpMark.line == line(tmpMark.mark.line)) {
4889 setMark(tmpMark.mark.line, tmpMark.mark.type);
4896 updateFileType(oldMode,
true);
4899 setHighlightingMode(oldHlMode);
4902 Q_EMIT reloaded(
this);
4907bool KTextEditor::DocumentPrivate::documentSave()
4909 if (!url().
isValid() || !isReadWrite()) {
4910 return documentSaveAs();
4916bool KTextEditor::DocumentPrivate::documentSaveAs()
4918 const QUrl saveUrl = getSaveFileUrl(
i18n(
"Save File"));
4926bool KTextEditor::DocumentPrivate::documentSaveAsWithEncoding(
const QString &encoding)
4928 const QUrl saveUrl = getSaveFileUrl(
i18n(
"Save File"));
4933 setEncoding(encoding);
4937void KTextEditor::DocumentPrivate::documentSaveCopyAs()
4939 const QUrl saveUrl = getSaveFileUrl(
i18n(
"Save Copy of File"));
4945 if (!file->
open()) {
4949 if (!m_buffer->saveFile(file->
fileName())) {
4951 i18n(
"The document could not be saved, as it was not possible to write to %1.\n\nCheck that you have write access to this file or "
4952 "that enough disk space is available.",
4960 const auto url = this->url();
4962 if (
auto sj = qobject_cast<KIO::StatJob *>(j)) {
4973void KTextEditor::DocumentPrivate::setWordWrap(
bool on)
4975 config()->setWordWrap(on);
4978bool KTextEditor::DocumentPrivate::wordWrap()
const
4980 return config()->wordWrap();
4983void KTextEditor::DocumentPrivate::setWordWrapAt(uint col)
4985 config()->setWordWrapAt(col);
4988unsigned int KTextEditor::DocumentPrivate::wordWrapAt()
const
4990 return config()->wordWrapAt();
4993void KTextEditor::DocumentPrivate::setPageUpDownMovesCursor(
bool on)
4995 config()->setPageUpDownMovesCursor(on);
4998bool KTextEditor::DocumentPrivate::pageUpDownMovesCursor()
const
5000 return config()->pageUpDownMovesCursor();
5004bool KTextEditor::DocumentPrivate::setEncoding(
const QString &e)
5006 return m_config->setEncoding(e);
5009QString KTextEditor::DocumentPrivate::encoding()
const
5011 return m_config->encoding();
5014void KTextEditor::DocumentPrivate::updateConfig()
5016 m_undoManager->updateConfig();
5019 m_indenter->setMode(m_config->indentationMode());
5020 m_indenter->updateConfig();
5023 m_buffer->setTabWidth(config()->tabWidth());
5026 for (
auto view :
std::as_const(m_views)) {
5027 static_cast<ViewPrivate *
>(view)->updateDocumentConfig();
5031 if (m_onTheFlyChecker) {
5032 m_onTheFlyChecker->updateConfig();
5035 if (config()->autoSave()) {
5036 int interval = config()->autoSaveInterval();
5037 if (interval == 0) {
5038 m_autoSaveTimer.stop();
5040 m_autoSaveTimer.setInterval(interval * 1000);
5042 m_autoSaveTimer.start();
5047 Q_EMIT configChanged(
this);
5057bool KTextEditor::DocumentPrivate::readVariables(
bool onlyViewAndRenderer)
5059 const bool hasVariableline = [
this] {
5062 for (
int i = qMax(10, lines() - 10); i < lines(); ++i) {
5063 if (line(i).contains(s)) {
5068 for (
int i = 0; i < qMin(9, lines()); ++i) {
5069 if (line(i).contains(s)) {
5075 if (!hasVariableline) {
5079 if (!onlyViewAndRenderer) {
5080 m_config->configStart();
5084 for (
auto view :
std::as_const(m_views)) {
5085 auto v =
static_cast<ViewPrivate *
>(view);
5090 for (
int i = 0; i < qMin(9, lines()); ++i) {
5091 readVariableLine(line(i), onlyViewAndRenderer);
5094 for (
int i = qMax(10, lines() - 10); i < lines(); i++) {
5095 readVariableLine(line(i), onlyViewAndRenderer);
5099 if (!onlyViewAndRenderer) {
5100 m_config->configEnd();
5103 for (
auto view :
std::as_const(m_views)) {
5104 auto v =
static_cast<ViewPrivate *
>(view);
5111void KTextEditor::DocumentPrivate::readVariableLine(
const QString &t,
bool onlyViewAndRenderer)
5114 static const QRegularExpression kvLineWildcard(QStringLiteral(
"kate-wildcard\\((.*)\\):(.*)"));
5115 static const QRegularExpression kvLineMime(QStringLiteral(
"kate-mimetype\\((.*)\\):(.*)"));
5128 auto match = kvLine.match(t);
5129 if (
match.hasMatch()) {
5130 s =
match.captured(1);
5133 }
else if ((match = kvLineWildcard.match(t)).hasMatch()) {
5139 for (
const QString &pattern : wildcards) {
5146 found = wildcard.
match(matchPath ? pathOfFile : nameOfFile).hasMatch();
5157 s =
match.captured(2);
5160 }
else if ((match = kvLineMime.match(t)).hasMatch()) {
5168 s =
match.captured(2);
5176 static const auto vvl = {
5200 int spaceIndent = -1;
5201 bool replaceTabsSet =
false;
5206 while ((match = kvVar.match(s, startPos)).hasMatch()) {
5207 startPos =
match.capturedEnd(0);
5208 var =
match.captured(1);
5209 val =
match.captured(2).trimmed();
5214 if (onlyViewAndRenderer) {
5215 if (contains(vvl, var)) {
5216 setViewVariable(var, val);
5220 if (var ==
QLatin1String(
"word-wrap") && checkBoolValue(val, &state)) {
5225 else if (var ==
QLatin1String(
"backspace-indents") && checkBoolValue(val, &state)) {
5226 m_config->setBackspaceIndents(state);
5227 }
else if (var ==
QLatin1String(
"indent-pasted-text") && checkBoolValue(val, &state)) {
5228 m_config->setIndentPastedText(state);
5229 }
else if (var ==
QLatin1String(
"replace-tabs") && checkBoolValue(val, &state)) {
5230 m_config->setReplaceTabsDyn(state);
5231 replaceTabsSet =
true;
5232 }
else if (var ==
QLatin1String(
"remove-trailing-space") && checkBoolValue(val, &state)) {
5233 qCWarning(LOG_KTE) <<
i18n(
5234 "Using deprecated modeline 'remove-trailing-space'. "
5235 "Please replace with 'remove-trailing-spaces modified;', see "
5236 "https://docs.kde.org/?application=katepart&branch=stable5&path=config-variables.html#variable-remove-trailing-spaces");
5237 m_config->setRemoveSpaces(state ? 1 : 0);
5238 }
else if (var ==
QLatin1String(
"replace-trailing-space-save") && checkBoolValue(val, &state)) {
5239 qCWarning(LOG_KTE) <<
i18n(
5240 "Using deprecated modeline 'replace-trailing-space-save'. "
5241 "Please replace with 'remove-trailing-spaces all;', see "
5242 "https://docs.kde.org/?application=katepart&branch=stable5&path=config-variables.html#variable-remove-trailing-spaces");
5243 m_config->setRemoveSpaces(state ? 2 : 0);
5244 }
else if (var ==
QLatin1String(
"overwrite-mode") && checkBoolValue(val, &state)) {
5245 m_config->setOvr(state);
5246 }
else if (var ==
QLatin1String(
"keep-extra-spaces") && checkBoolValue(val, &state)) {
5247 m_config->setKeepExtraSpaces(state);
5248 }
else if (var ==
QLatin1String(
"tab-indents") && checkBoolValue(val, &state)) {
5249 m_config->setTabIndents(state);
5250 }
else if (var ==
QLatin1String(
"show-tabs") && checkBoolValue(val, &state)) {
5251 m_config->setShowTabs(state);
5252 }
else if (var ==
QLatin1String(
"show-trailing-spaces") && checkBoolValue(val, &state)) {
5253 m_config->setShowSpaces(state ? KateDocumentConfig::Trailing : KateDocumentConfig::
None);
5254 }
else if (var ==
QLatin1String(
"space-indent") && checkBoolValue(val, &state)) {
5256 spaceIndent = state;
5257 }
else if (var ==
QLatin1String(
"smart-home") && checkBoolValue(val, &state)) {
5258 m_config->setSmartHome(state);
5259 }
else if (var ==
QLatin1String(
"newline-at-eof") && checkBoolValue(val, &state)) {
5260 m_config->setNewLineAtEof(state);
5264 else if (var ==
QLatin1String(
"tab-width") && checkIntValue(val, &n)) {
5265 m_config->setTabWidth(n);
5266 }
else if (var ==
QLatin1String(
"indent-width") && checkIntValue(val, &n)) {
5267 m_config->setIndentationWidth(n);
5269 m_config->setIndentationMode(val);
5270 }
else if (var ==
QLatin1String(
"word-wrap-column") && checkIntValue(val, &n) && n > 0) {
5271 m_config->setWordWrapAt(n);
5277 if ((n = indexOf(l, val.
toLower())) != -1) {
5280 m_config->setEol(n);
5281 m_config->setAllowEolDetection(
false);
5284 if (checkBoolValue(val, &state)) {
5285 m_config->setBom(state);
5287 }
else if (var ==
QLatin1String(
"remove-trailing-spaces")) {
5290 m_config->setRemoveSpaces(1);
5292 m_config->setRemoveSpaces(2);
5294 m_config->setRemoveSpaces(0);
5297 setHighlightingMode(val);
5303 setDefaultDictionary(val);
5304 }
else if (var ==
QLatin1String(
"automatic-spell-checking") && checkBoolValue(val, &state)) {
5305 onTheFlySpellCheckingEnabled(state);
5309 else if (contains(vvl, var)) {
5310 setViewVariable(var, val);
5312 m_storedVariables[var] = val;
5324 if (!replaceTabsSet && spaceIndent >= 0) {
5325 m_config->setReplaceTabsDyn(spaceIndent > 0);
5329void KTextEditor::DocumentPrivate::setViewVariable(
const QString &var,
const QString &val)
5334 for (
auto view :
std::as_const(m_views)) {
5335 auto v =
static_cast<ViewPrivate *
>(view);
5338 if (checkBoolValue(val, &state)) {
5341 if (v->config()->
setValue(var, help)) {
5342 }
else if (v->rendererConfig()->
setValue(var, help)) {
5344 }
else if (var ==
QLatin1String(
"dynamic-word-wrap") && checkBoolValue(val, &state)) {
5345 v->config()->setDynWordWrap(state);
5346 }
else if (var ==
QLatin1String(
"block-selection") && checkBoolValue(val, &state)) {
5347 v->setBlockSelection(state);
5350 }
else if (var ==
QLatin1String(
"icon-bar-color") && checkColorValue(val, c)) {
5351 v->rendererConfig()->setIconBarColor(c);
5354 else if (var ==
QLatin1String(
"background-color") && checkColorValue(val, c)) {
5355 v->rendererConfig()->setBackgroundColor(c);
5356 }
else if (var ==
QLatin1String(
"selection-color") && checkColorValue(val, c)) {
5357 v->rendererConfig()->setSelectionColor(c);
5358 }
else if (var ==
QLatin1String(
"current-line-color") && checkColorValue(val, c)) {
5359 v->rendererConfig()->setHighlightedLineColor(c);
5360 }
else if (var ==
QLatin1String(
"bracket-highlight-color") && checkColorValue(val, c)) {
5361 v->rendererConfig()->setHighlightedBracketColor(c);
5362 }
else if (var ==
QLatin1String(
"word-wrap-marker-color") && checkColorValue(val, c)) {
5363 v->rendererConfig()->setWordWrapMarkerColor(c);
5369 _f.setFixedPitch(
QFont(val).fixedPitch());
5374 v->rendererConfig()->setFont(_f);
5376 v->rendererConfig()->setSchema(val);
5381bool KTextEditor::DocumentPrivate::checkBoolValue(
QString val,
bool *result)
5385 if (contains(trueValues, val)) {
5391 if (contains(falseValues, val)) {
5398bool KTextEditor::DocumentPrivate::checkIntValue(
const QString &val,
int *result)
5401 *result = val.
toInt(&ret);
5405bool KTextEditor::DocumentPrivate::checkColorValue(
const QString &val,
QColor &c)
5412QString KTextEditor::DocumentPrivate::variable(
const QString &name)
const
5414 auto it = m_storedVariables.find(name);
5415 if (it == m_storedVariables.end()) {
5421void KTextEditor::DocumentPrivate::setVariable(
const QString &name,
const QString &value)
5423 QString s = QStringLiteral(
"kate: ");
5427 readVariableLine(s);
5432void KTextEditor::DocumentPrivate::slotModOnHdDirty(
const QString &path)
5434 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskModified)) {
5436 m_modOnHdReason = OnDiskModified;
5438 if (!m_modOnHdTimer.isActive()) {
5439 m_modOnHdTimer.start();
5444void KTextEditor::DocumentPrivate::slotModOnHdCreated(
const QString &path)
5446 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskCreated)) {
5448 m_modOnHdReason = OnDiskCreated;
5450 if (!m_modOnHdTimer.isActive()) {
5451 m_modOnHdTimer.start();
5456void KTextEditor::DocumentPrivate::slotModOnHdDeleted(
const QString &path)
5458 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskDeleted)) {
5460 m_modOnHdReason = OnDiskDeleted;
5462 if (!m_modOnHdTimer.isActive()) {
5463 m_modOnHdTimer.start();
5468void KTextEditor::DocumentPrivate::slotDelayedHandleModOnHd()
5472 if (!oldDigest.
isEmpty() && !url().isEmpty() && url().isLocalFile()) {
5474 if (m_modOnHdReason != OnDiskDeleted && m_modOnHdReason != OnDiskCreated && createDigest() && oldDigest == checksum()) {
5476 m_modOnHdReason = OnDiskUnmodified;
5477 m_prevModOnHdReason = OnDiskUnmodified;
5484 if (m_modOnHd && !isModified() &&
QFile::exists(url().toLocalFile())
5485 && config()->value(KateDocumentConfig::AutoReloadIfStateIsInVersionControl).toBool()) {
5492 git.
start(fullGitPath, args);
5499 m_modOnHdReason = OnDiskUnmodified;
5500 m_prevModOnHdReason = OnDiskUnmodified;
5510 Q_EMIT modifiedOnDisk(
this, m_modOnHd, m_modOnHdReason);
5513QByteArray KTextEditor::DocumentPrivate::checksum()
const
5515 return m_buffer->digest();
5518bool KTextEditor::DocumentPrivate::createDigest()
5522 if (url().isLocalFile()) {
5523 QFile f(url().toLocalFile());
5527 const QString header = QStringLiteral(
"blob %1").
arg(f.size());
5530 while (!f.atEnd()) {
5531 crypto.addData(f.read(256 * 1024));
5534 digest = crypto.result();
5539 m_buffer->setDigest(digest);
5543QString KTextEditor::DocumentPrivate::reasonedMOHString()
const
5548 switch (m_modOnHdReason) {
5549 case OnDiskModified:
5550 return i18n(
"The file '%1' was modified on disk.", str);
5553 return i18n(
"The file '%1' was created on disk.", str);
5556 return i18n(
"The file '%1' was deleted on disk.", str);
5565void KTextEditor::DocumentPrivate::removeTrailingSpacesAndAddNewLineAtEof()
5568 const int remove = config()->removeSpaces();
5569 const bool newLineAtEof = config()->newLineAtEof();
5570 if (remove == 0 && !newLineAtEof) {
5575 const bool wordWrapEnabled = config()->wordWrap();
5576 if (wordWrapEnabled) {
5583 const int lines = this->lines();
5585 for (
int line = 0; line < lines; ++line) {
5591 if (remove == 2 || textline.markedAsModified() || textline.markedAsSavedOnDisk()) {
5592 const int p = textline.
lastChar() + 1;
5593 const int l = textline.
length() - p;
5595 editRemoveText(line, p, l);
5604 Q_ASSERT(lines > 0);
5605 const auto length = lineLength(lines - 1);
5609 const auto oldEndOfDocumentCursor = documentEnd();
5610 std::vector<KTextEditor::ViewPrivate *> viewsToRestoreCursors;
5611 for (
auto view :
std::as_const(m_views)) {
5612 auto v =
static_cast<ViewPrivate *
>(view);
5613 if (v->cursorPosition() == oldEndOfDocumentCursor) {
5614 viewsToRestoreCursors.push_back(v);
5619 editWrapLine(lines - 1, length);
5622 for (
auto v : viewsToRestoreCursors) {
5623 v->setCursorPosition(oldEndOfDocumentCursor);
5631 if (wordWrapEnabled) {
5636void KTextEditor::DocumentPrivate::removeAllTrailingSpaces()
5639 const int lines = this->lines();
5640 for (
int line = 0; line < lines; ++line) {
5642 const int p = textline.
lastChar() + 1;
5643 const int l = textline.
length() - p;
5645 editRemoveText(line, p, l);
5651bool KTextEditor::DocumentPrivate::updateFileType(
const QString &newType,
bool user)
5653 if (user || !m_fileTypeSetByUser) {
5659 if (fileType.name.
isEmpty()) {
5664 m_fileTypeSetByUser = user;
5666 m_fileType = newType;
5668 m_config->configStart();
5673 if ((user || !m_hlSetByUser) && !fileType.hl.
isEmpty()) {
5674 int hl(KateHlManager::self()->nameFind(fileType.hl));
5677 m_buffer->setHighlight(hl);
5684 if (!m_indenterSetByUser && !fileType.indenter.
isEmpty()) {
5685 config()->setIndentationMode(fileType.indenter);
5689 for (
auto view :
std::as_const(m_views)) {
5690 auto v =
static_cast<ViewPrivate *
>(view);
5695 bool bom_settings =
false;
5696 if (m_bomSetByUser) {
5697 bom_settings = m_config->bom();
5699 readVariableLine(fileType.varLine);
5700 if (m_bomSetByUser) {
5701 m_config->setBom(bom_settings);
5703 m_config->configEnd();
5704 for (
auto view :
std::as_const(m_views)) {
5705 auto v =
static_cast<ViewPrivate *
>(view);
5712 Q_EMIT modeChanged(
this);
5716void KTextEditor::DocumentPrivate::slotQueryClose_save(
bool *handled,
bool *abortClosing)
5719 *abortClosing =
true;
5720 if (url().isEmpty()) {
5721 const QUrl res = getSaveFileUrl(
i18n(
"Save File"));
5723 *abortClosing =
true;
5727 *abortClosing =
false;
5730 *abortClosing =
false;
5737QStringList KTextEditor::DocumentPrivate::configKeys()
const
5740 return m_config->configKeys();
5746 return m_config->
value(key);
5749void KTextEditor::DocumentPrivate::setConfigValue(
const QString &key,
const QVariant &value)
5752 m_config->setValue(key, value);
5766 bool changed = removeText(range, block);
5767 changed |= insertText(range.
start(), s, block);
5772KateHighlighting *KTextEditor::DocumentPrivate::highlight()
const
5774 return m_buffer->highlight();
5779 m_buffer->ensureHighlighted(i);
5780 return m_buffer->plainLine(i);
5783Kate::TextLine KTextEditor::DocumentPrivate::plainKateTextLine(
int i)
5785 return m_buffer->plainLine(i);
5788bool KTextEditor::DocumentPrivate::isEditRunning()
const
5790 return editIsRunning;
5793void KTextEditor::DocumentPrivate::setUndoMergeAllEdits(
bool merge)
5795 if (merge && m_undoMergeAllEdits) {
5800 m_undoManager->undoSafePoint();
5801 m_undoManager->setAllowComplexMerge(merge);
5802 m_undoMergeAllEdits =
merge;
5815 return new Kate::TextRange(buffer(), range, insertBehaviors, emptyBehavior);
5818qint64 KTextEditor::DocumentPrivate::revision()
const
5820 return m_buffer->history().revision();
5823qint64 KTextEditor::DocumentPrivate::lastSavedRevision()
const
5825 return m_buffer->history().lastSavedRevision();
5828void KTextEditor::DocumentPrivate::lockRevision(qint64 revision)
5830 m_buffer->history().lockRevision(revision);
5833void KTextEditor::DocumentPrivate::unlockRevision(qint64 revision)
5835 m_buffer->history().unlockRevision(revision);
5838void KTextEditor::DocumentPrivate::transformCursor(
int &line,
5841 qint64 fromRevision,
5844 m_buffer->history().transformCursor(line, column, insertBehavior, fromRevision, toRevision);
5849 qint64 fromRevision,
5852 int line = cursor.
line();
5853 int column = cursor.
column();
5854 m_buffer->history().transformCursor(line, column, insertBehavior, fromRevision, toRevision);
5861 qint64 fromRevision,
5864 m_buffer->history().transformRange(range, insertBehaviors, emptyBehavior, fromRevision, toRevision);
5873 m_annotationModel = model;
5874 Q_EMIT annotationModelChanged(oldmodel, m_annotationModel);
5879 return m_annotationModel;
5884bool KTextEditor::DocumentPrivate::queryClose()
5886 if (!isModified() || (isEmpty() && url().isEmpty())) {
5890 QString docName = documentName();
5893 i18n(
"The document \"%1\" has been modified.\n"
5894 "Do you want to save your changes or discard them?",
5896 i18n(
"Close Document"),
5900 bool abortClose =
false;
5901 bool handled =
false;
5905 sigQueryClose(&handled, &abortClose);
5907 if (url().isEmpty()) {
5908 const QUrl url = getSaveFileUrl(
i18n(
"Save File"));
5917 }
else if (abortClose) {
5920 return waitSaveComplete();
5928void KTextEditor::DocumentPrivate::slotStarted(
KIO::Job *job)
5931 if (m_documentState == DocumentIdle) {
5932 m_documentState = DocumentLoading;
5940 if (m_documentState == DocumentLoading) {
5942 m_readWriteStateBeforeLoading = isReadWrite();
5947 setReadWrite(
false);
5957void KTextEditor::DocumentPrivate::slotCompleted()
5961 if (m_documentState == DocumentLoading) {
5962 setReadWrite(m_readWriteStateBeforeLoading);
5963 delete m_loadingMessage;
5967 if (m_documentState == DocumentSaving || m_documentState == DocumentSavingAs) {
5968 Q_EMIT documentSavedOrUploaded(
this, m_documentState == DocumentSavingAs);
5972 m_documentState = DocumentIdle;
5973 m_reloading =
false;
5976void KTextEditor::DocumentPrivate::slotCanceled()
5980 if (m_documentState == DocumentLoading) {
5981 setReadWrite(m_readWriteStateBeforeLoading);
5982 delete m_loadingMessage;
5984 if (!m_openingError) {
5985 showAndSetOpeningErrorAccess();
5992 m_documentState = DocumentIdle;
5993 m_reloading =
false;
5996void KTextEditor::DocumentPrivate::slotTriggerLoadingMessage()
6000 if (m_documentState != DocumentLoading) {
6005 delete m_loadingMessage;
6014 m_loadingMessage->addAction(cancel);
6018 postMessage(m_loadingMessage);
6021void KTextEditor::DocumentPrivate::slotAbortLoading()
6024 if (!m_loadingJob) {
6030 m_loadingJob->kill(KJob::EmitResult);
6031 m_loadingJob =
nullptr;
6034void KTextEditor::DocumentPrivate::slotUrlChanged(
const QUrl &url)
6044 Q_EMIT documentUrlChanged(
this);
6047bool KTextEditor::DocumentPrivate::save()
6051 if ((m_documentState != DocumentIdle) && (m_documentState != DocumentPreSavingAs)) {
6056 if (m_documentState == DocumentIdle) {
6057 m_documentState = DocumentSaving;
6059 m_documentState = DocumentSavingAs;
6063 Q_EMIT aboutToSave(
this);
6069bool KTextEditor::DocumentPrivate::saveAs(
const QUrl &url)
6080 if (m_documentState != DocumentIdle) {
6085 m_documentState = DocumentPreSavingAs;
6091QString KTextEditor::DocumentPrivate::defaultDictionary()
const
6093 return m_defaultDictionary;
6098 return m_dictionaryRanges;
6101void KTextEditor::DocumentPrivate::clearDictionaryRanges()
6103 for (
auto i = m_dictionaryRanges.cbegin(); i != m_dictionaryRanges.cend(); ++i) {
6106 m_dictionaryRanges.clear();
6107 if (m_onTheFlyChecker) {
6108 m_onTheFlyChecker->refreshSpellCheck();
6110 Q_EMIT dictionaryRangesPresent(
false);
6117 setDictionary(newDictionary, rangeOnLine(range, i));
6120 setDictionary(newDictionary, range);
6123 Q_EMIT dictionaryRangesPresent(!m_dictionaryRanges.isEmpty());
6129 if (!newDictionaryRange.
isValid() || newDictionaryRange.
isEmpty()) {
6134 for (
auto i = m_dictionaryRanges.begin(); i != m_dictionaryRanges.end();) {
6135 qCDebug(LOG_KTE) <<
"new iteration" << newDictionaryRange;
6136 if (newDictionaryRange.
isEmpty()) {
6139 QPair<KTextEditor::MovingRange *, QString> pair = *i;
6140 QString dictionarySet = pair.second;
6142 qCDebug(LOG_KTE) << *dictionaryRange << dictionarySet;
6143 if (dictionaryRange->
contains(newDictionaryRange) && newDictionary == dictionarySet) {
6144 qCDebug(LOG_KTE) <<
"dictionaryRange contains newDictionaryRange";
6147 if (newDictionaryRange.
contains(*dictionaryRange)) {
6148 delete dictionaryRange;
6149 i = m_dictionaryRanges.erase(i);
6150 qCDebug(LOG_KTE) <<
"newDictionaryRange contains dictionaryRange";
6156 if (dictionarySet == newDictionary) {
6159 Q_ASSERT(remainingRanges.
size() == 1);
6160 newDictionaryRange = remainingRanges.
first();
6162 qCDebug(LOG_KTE) <<
"dictionarySet == newDictionary";
6166 for (
auto j = remainingRanges.
begin(); j != remainingRanges.
end(); ++j) {
6169 newRanges.
push_back({remainingRange, dictionarySet});
6171 i = m_dictionaryRanges.erase(i);
6172 delete dictionaryRange;
6177 m_dictionaryRanges += newRanges;
6182 m_dictionaryRanges.push_back({newDictionaryMovingRange, newDictionary});
6184 if (m_onTheFlyChecker && !newDictionaryRange.
isEmpty()) {
6185 m_onTheFlyChecker->refreshSpellCheck(newDictionaryRange);
6189void KTextEditor::DocumentPrivate::setDefaultDictionary(
const QString &dict)
6191 if (m_defaultDictionary == dict) {
6195 m_defaultDictionary = dict;
6197 if (m_onTheFlyChecker) {
6198 m_onTheFlyChecker->updateConfig();
6199 refreshOnTheFlyCheck();
6201 Q_EMIT defaultDictionaryChanged(
this);
6204void KTextEditor::DocumentPrivate::onTheFlySpellCheckingEnabled(
bool enable)
6206 if (isOnTheFlySpellCheckingEnabled() == enable) {
6211 Q_ASSERT(m_onTheFlyChecker ==
nullptr);
6212 m_onTheFlyChecker =
new KateOnTheFlyChecker(
this);
6214 delete m_onTheFlyChecker;
6215 m_onTheFlyChecker =
nullptr;
6218 for (
auto view :
std::as_const(m_views)) {
6219 static_cast<ViewPrivate *
>(view)->reflectOnTheFlySpellCheckStatus(enable);
6223bool KTextEditor::DocumentPrivate::isOnTheFlySpellCheckingEnabled()
const
6225 return m_onTheFlyChecker !=
nullptr;
6230 if (!m_onTheFlyChecker) {
6233 return m_onTheFlyChecker->dictionaryForMisspelledRange(range);
6237void KTextEditor::DocumentPrivate::clearMisspellingForWord(
const QString &word)
6239 if (m_onTheFlyChecker) {
6240 m_onTheFlyChecker->clearMisspellingForWord(word);
6246 if (m_onTheFlyChecker) {
6247 m_onTheFlyChecker->refreshSpellCheck(range);
6253 deleteDictionaryRange(movingRange);
6258 deleteDictionaryRange(movingRange);
6263 qCDebug(LOG_KTE) <<
"deleting" << movingRange;
6265 auto finder = [=](
const QPair<KTextEditor::MovingRange *, QString> &item) ->
bool {
6266 return item.first == movingRange;
6269 auto it = std::find_if(m_dictionaryRanges.begin(), m_dictionaryRanges.end(), finder);
6271 if (it != m_dictionaryRanges.end()) {
6272 m_dictionaryRanges.erase(it);
6276 Q_ASSERT(std::find_if(m_dictionaryRanges.begin(), m_dictionaryRanges.end(), finder) == m_dictionaryRanges.end());
6279bool KTextEditor::DocumentPrivate::containsCharacterEncoding(
KTextEditor::Range range)
6281 KateHighlighting *highlighting = highlight();
6283 const int rangeStartLine = range.
start().
line();
6284 const int rangeStartColumn = range.
start().
column();
6285 const int rangeEndLine = range.
end().
line();
6286 const int rangeEndColumn = range.
end().
column();
6288 for (
int line = range.
start().
line(); line <= rangeEndLine; ++line) {
6290 const int startColumn = (line == rangeStartLine) ? rangeStartColumn : 0;
6291 const int endColumn = (line == rangeEndLine) ? rangeEndColumn : textLine.length();
6292 for (
int col = startColumn; col < endColumn; ++col) {
6294 const KatePrefixStore &prefixStore = highlighting->getCharacterEncodingsPrefixStore(attr);
6304int KTextEditor::DocumentPrivate::computePositionWrtOffsets(
const OffsetList &offsetList,
int pos)
6306 int previousOffset = 0;
6307 for (
auto i = offsetList.cbegin(); i != offsetList.cend(); ++i) {
6308 if (i->first > pos) {
6311 previousOffset = i->second;
6313 return pos + previousOffset;
6317 KTextEditor::DocumentPrivate::OffsetList &decToEncOffsetList,
6318 KTextEditor::DocumentPrivate::OffsetList &encToDecOffsetList)
6322 int decToEncCurrentOffset = 0;
6323 int encToDecCurrentOffset = 0;
6327 KateHighlighting *highlighting = highlight();
6330 const int rangeStartLine = range.
start().
line();
6331 const int rangeStartColumn = range.
start().
column();
6332 const int rangeEndLine = range.
end().
line();
6333 const int rangeEndColumn = range.
end().
column();
6335 for (
int line = range.
start().
line(); line <= rangeEndLine; ++line) {
6336 textLine = kateTextLine(line);
6337 int startColumn = (line == rangeStartLine) ? rangeStartColumn : 0;
6338 int endColumn = (line == rangeEndLine) ? rangeEndColumn : textLine.length();
6339 for (
int col = startColumn; col < endColumn;) {
6341 const KatePrefixStore &prefixStore = highlighting->getCharacterEncodingsPrefixStore(attr);
6344 if (!matchingPrefix.
isEmpty()) {
6346 const QChar &c = characterEncodingsHash.
value(matchingPrefix);
6347 const bool isNullChar = c.
isNull();
6351 i += matchingPrefix.
length();
6352 col += matchingPrefix.
length();
6354 decToEncCurrentOffset = decToEncCurrentOffset - (isNullChar ? 0 : 1) + matchingPrefix.
length();
6355 encToDecCurrentOffset = encToDecCurrentOffset - matchingPrefix.
length() + (isNullChar ? 0 : 1);
6356 newI += (isNullChar ? 0 : 1);
6357 decToEncOffsetList.push_back(QPair<int, int>(newI, decToEncCurrentOffset));
6358 encToDecOffsetList.push_back(QPair<int, int>(i, encToDecCurrentOffset));
6368 if (previous < range.
end()) {
6374void KTextEditor::DocumentPrivate::replaceCharactersByEncoding(
KTextEditor::Range range)
6376 KateHighlighting *highlighting = highlight();
6379 const int rangeStartLine = range.
start().
line();
6380 const int rangeStartColumn = range.
start().
column();
6381 const int rangeEndLine = range.
end().
line();
6382 const int rangeEndColumn = range.
end().
column();
6384 for (
int line = range.
start().
line(); line <= rangeEndLine; ++line) {
6385 textLine = kateTextLine(line);
6386 int startColumn = (line == rangeStartLine) ? rangeStartColumn : 0;
6387 int endColumn = (line == rangeEndLine) ? rangeEndColumn : textLine.length();
6388 for (
int col = startColumn; col < endColumn;) {
6390 const QHash<QChar, QString> &reverseCharacterEncodingsHash = highlighting->getReverseCharacterEncodings(attr);
6391 auto it = reverseCharacterEncodingsHash.
find(textLine.
at(col));
6392 if (it != reverseCharacterEncodingsHash.
end()) {
6394 col += (*it).length();
6406QStringList KTextEditor::DocumentPrivate::embeddedHighlightingModes()
const
6408 return highlight()->getEmbeddedHighlightingModes();
6413 return highlight()->higlightingModeForLocation(
this, position);
6428 if (line < 0 || line >= lines() || column < 0) {
6429 return KSyntaxHighlighting::Theme::TextStyle::Normal;
6437 if (column < tl.
length()) {
6439 }
else if (column == tl.
length()) {
6443 return KSyntaxHighlighting::Theme::TextStyle::Normal;
6446 return KSyntaxHighlighting::Theme::TextStyle::Normal;
6449 return highlight()->defaultStyleForAttribute(attribute);
6452bool KTextEditor::DocumentPrivate::isComment(
int line,
int column)
6454 return defStyleNum(line, column) == KSyntaxHighlighting::Theme::TextStyle::Comment;
6457int KTextEditor::DocumentPrivate::findTouchedLine(
int startLine,
bool down)
6459 const int offset = down ? 1 : -1;
6460 const int lineCount = lines();
6461 while (startLine >= 0 && startLine < lineCount) {
6463 if (tl.markedAsModified() || tl.markedAsSavedOnDisk()) {
6466 startLine += offset;
6475 delete m_activeTemplateHandler.data();
6476 m_activeTemplateHandler = handler;
6489 qCWarning(LOG_KTE) <<
"trying to post a message to a view of another document:" << message->
text();
6499 closeAction->
setToolTip(
i18nc(
"Close the message being displayed",
"Close message"));
6505 const auto messageActions = message->
actions();
6506 managedMessageActions.
reserve(messageActions.size());
6507 for (
QAction *action : messageActions) {
6508 action->setParent(
nullptr);
6509 managedMessageActions.
append(std::shared_ptr<QAction>(action));
6511 m_messageHash.insert(message, managedMessageActions);
6514 if (KTextEditor::ViewPrivate *view = qobject_cast<KTextEditor::ViewPrivate *>(message->
view())) {
6515 view->postMessage(message, managedMessageActions);
6517 for (
auto view :
std::as_const(m_views)) {
6518 static_cast<ViewPrivate *
>(view)->postMessage(message, managedMessageActions);
6523 connect(message, &Message::closed,
this, &DocumentPrivate::messageDestroyed);
6531 Q_ASSERT(m_messageHash.contains(message));
6532 m_messageHash.remove(message);
6536#include "moc_katedocument.cpp"
bool hasKey(const char *key) const
void writeEntry(const char *key, const char *value, WriteConfigFlags pFlags=Normal)
QString readEntry(const char *key, const char *aDefault=nullptr) const
void addFile(const QString &file)
void removeFile(const QString &file)
void deleted(const QString &path)
void dirty(const QString &path)
void created(const QString &path)
mode_t permissions() const
const UDSEntry & statResult() const
virtual Q_SCRIPTABLE void start()=0
Ptr findByDevice(const QString &device) const
static List currentMountPoints(DetailsNeededFlags infoNeeded=BasicInfoNeeded)
static KNetworkMounts * self()
MediumSideEffectsOptimizations
virtual QWidget * widget()
virtual bool openUrl(const QUrl &url)
void urlChanged(const QUrl &url)
virtual void setReadWrite(bool readwrite=true)
virtual bool saveAs(const QUrl &url)
An model for providing line annotation information.
bool closeDocument(KTextEditor::Document *document)
Close the given document.
The Cursor represents a position in a Document.
constexpr int column() const noexcept
Retrieve the column on which this cursor is situated.
void setColumn(int column) noexcept
Set the cursor column to column.
void setPosition(Cursor position) noexcept
Set the current cursor position to position.
constexpr bool isValid() const noexcept
Returns whether the current position of this cursor is a valid position (line + column must both be >...
static constexpr Cursor start() noexcept
Returns a cursor representing the start of any document - i.e., line 0, column 0.
void setLine(int line) noexcept
Set the cursor line to line.
constexpr int line() const noexcept
Retrieve the line on which this cursor is situated.
static constexpr Cursor invalid() noexcept
Returns an invalid cursor.
A Cursor which is bound to a specific Document.
A KParts derived class representing a text document.
virtual QString text() const =0
Get the document content.
static int reservedMarkersCount()
Get the number of predefined mark types we have so far.
KateModeManager * modeManager()
global mode manager used to manage the modes centrally
void deregisterDocument(KTextEditor::DocumentPrivate *doc)
unregister document at the factory
KTextEditor::Application * application() const override
Current hosting application, if any set.
KDirWatch * dirWatch()
global dirwatch
void registerDocument(KTextEditor::DocumentPrivate *doc)
register document at the factory this allows us to loop over all docs for example on config changes
static KTextEditor::EditorPrivate * self()
Kate Part Internal stuff ;)
QList< KTextEditor::Document * > documents() override
Returns a list of all documents of this editor.
KateVariableExpansionManager * variableExpansionManager()
Returns the variable expansion manager.
static Editor * instance()
Accessor to get the Editor instance.
An object representing lines from a start line to an end line.
This class allows the application that embeds the KTextEditor component to allow it to access parts o...
QList< KTextEditor::View * > views()
Get a list of all views for this main window.
uint type
The mark types in the line, combined with logical OR.
int line
The line that contains the mark.
This class holds a Message to display in Views.
@ TopInView
show message as view overlay in the top right corner.
KTextEditor::View * view() const
This function returns the view you set by setView().
int autoHide() const
Returns the auto hide time in milliseconds.
void addAction(QAction *action, bool closeOnTrigger=true)
Adds an action to the message.
QString text() const
Returns the text set in the constructor.
@ Error
error message type
@ Warning
warning message type
void setDocument(KTextEditor::Document *document)
Set the document pointer to document.
QList< QAction * > actions() const
Accessor to all actions, mainly used in the internal implementation to add the actions into the gui.
A Cursor which is bound to a specific Document, and maintains its position.
InsertBehavior
Insert behavior of this cursor, should it stay if text is insert at its position or should it move.
@ MoveOnInsert
move on insert
A range that is bound to a specific Document, and maintains its position.
EmptyBehavior
Behavior of range if it becomes empty.
const Range toRange() const
Convert this clever range into a dumb one.
virtual void setFeedback(MovingRangeFeedback *feedback)=0
Sets the currently active MovingRangeFeedback for this range.
bool contains(const Range &range) const
Check whether the this range wholly encompasses range.
@ DoNotExpand
Don't expand to encapsulate new characters in either direction. This is the default.
@ ExpandRight
Expand to encapsulate new characters to the right of the range.
@ ExpandLeft
Expand to encapsulate new characters to the left of the range.
An object representing a section of text, from one Cursor to another.
constexpr Cursor end() const noexcept
Get the end position of this range.
constexpr Cursor start() const noexcept
Get the start position of this range.
void setEnd(Cursor end) noexcept
Set the end cursor to end.
constexpr bool isEmpty() const noexcept
Returns true if this range contains no characters, ie.
void setRange(Range range) noexcept
Set the start and end cursors to range.start() and range.end() respectively.
constexpr bool overlaps(Range range) const noexcept
Check whether the this range overlaps with range.
constexpr int columnWidth() const noexcept
Returns the number of columns separating the start() and end() positions.
constexpr bool onSingleLine() const noexcept
Check whether this range is wholly contained within one line, ie.
static constexpr Range invalid() noexcept
Returns an invalid range.
constexpr bool isValid() const noexcept
Validity check.
constexpr bool contains(Range range) const noexcept
Check whether the this range wholly encompasses range.
constexpr Range intersect(Range range) const noexcept
Intersects this range with another, returning the shared area of the two ranges.
void setBothLines(int line) noexcept
Convenience function.
constexpr int numberOfLines() const noexcept
Returns the number of lines separating the start() and end() positions.
void setStart(Cursor start) noexcept
Set the start cursor to start.
A text widget with KXMLGUIClient that represents a Document.
virtual QMenu * defaultContextMenu(QMenu *menu=nullptr) const =0
Populate menu with default text editor actions.
virtual bool setCursorPosition(Cursor position)=0
Set the view's new cursor to position.
virtual Document * document() const =0
Get the view's document, that means the view is a view of the returned document.
void focusIn(KTextEditor::View *view)
This signal is emitted whenever the view gets the focus.
virtual void setContextMenu(QMenu *menu)=0
Set a context menu for this view to menu.
Provides Auto-Indent functionality for katepart.
The KateBuffer class maintains a collections of lines.
void tagLines(KTextEditor::LineRange lineRange)
Emitted when the highlighting of a certain range has changed.
void configEnd()
End a config change transaction, update the concerned KateDocumentConfig/KateDocumentConfig/KateDocum...
void configStart()
Start some config changes.
bool setValue(const int key, const QVariant &value)
Set a config value.
File indentation detecter.
This dialog will prompt the user for what do with a file that is modified on disk.
Object to help to search for plain text.
This class can be used to efficiently search for occurrences of strings in a given string.
QString findPrefix(const QString &s, int start=0) const
Returns the shortest prefix of the given string that is contained in this prefix store starting at po...
Object to help to search for regexp.
static QString escapePlaintext(const QString &text)
Returns a modified version of text where escape sequences are resolved, e.g.
const QFont & currentFont() const
Access currently used font.
Inserts a template and offers advanced snippet features, like navigation and mirroring.
KateUndoManager implements a document's history.
Class for tracking editing actions.
Class representing a 'clever' text cursor.
Class representing a single text line.
int attribute(int pos) const
Gets the attribute at the given position use KRenderer::attributes to get the KTextAttribute for this...
const QString & text() const
Accessor to the text contained in this line.
bool endsWith(const QString &match) const
Returns true, if the line ends with match, otherwise returns false.
void setAutoWrapped(bool wrapped)
set auto-wrapped property
const QList< Attribute > & attributesList() const
Accessor to attributes.
int virtualLength(int tabWidth) const
Returns the text length with each tab expanded into tabWidth characters.
QString string(int column, int length) const
Returns the substring with length beginning at the given column.
int previousNonSpaceChar(int pos) const
Find the position of the previous char that is not a space.
int length() const
Returns the line's length.
bool isAutoWrapped() const
Returns true, if the line was automagically wrapped, otherwise returns false.
bool startsWith(const QString &match) const
Returns true, if the line starts with match, otherwise returns false.
int lastChar() const
Returns the position of the last non-whitespace character.
QChar at(int column) const
Returns the character at the given column.
int firstChar() const
Returns the position of the first non-whitespace character.
int toVirtualColumn(int column, int tabWidth) const
Returns the column with each tab expanded into tabWidth characters.
bool matchesAt(int column, const QString &match) const
Returns true, if match equals to the text at position column, otherwise returns false.
int nextNonSpaceChar(int pos) const
Find the position of the next char that is not a space.
int fromVirtualColumn(int column, int tabWidth) const
Returns the "real" column where each tab only counts one character.
Q_SCRIPTABLE Q_NOREPLY void start()
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
Type type(const QSqlDatabase &db)
char * toString(const EngineQuery &query)
KCALUTILS_EXPORT QString mimeType()
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
QAction * saveAs(const QObject *recvr, const char *slot, QObject *parent)
KIOCORE_EXPORT DeleteJob * del(const QList< QUrl > &src, JobFlags flags=DefaultFlags)
KIOCORE_EXPORT StatJob * stat(const QUrl &url, JobFlags flags=DefaultFlags)
KIOCORE_EXPORT FileCopyJob * file_copy(const QUrl &src, const QUrl &dest, int permissions=-1, JobFlags flags=DefaultFlags)
QString path(const QString &relativePath)
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
ButtonCode warningTwoActionsCancel(QWidget *parent, const QString &text, const QString &title, const KGuiItem &primaryAction, const KGuiItem &secondaryAction, const KGuiItem &cancelAction=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Options(Notify|Dangerous))
QStringView merge(QStringView lhs, QStringView rhs)
bool isValid(QStringView ifopt)
KIOCORE_EXPORT QString dir(const QString &fileClass)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
const QList< QKeySequence > & end()
QString name(StandardShortcut id)
const QList< QKeySequence > & save()
const QList< QKeySequence > & help()
KCOREADDONS_EXPORT QString csqueeze(const QString &str, int maxlen=40)
The KTextEditor namespace contains all the public API that is required to use the KTextEditor compone...
void triggered(bool checked)
QByteArray & append(QByteArrayView data)
bool isEmpty() const const
qsizetype size() const const
QByteArray toHex(char separator) const const
bool isHighSurrogate(char32_t ucs4)
bool isLowSurrogate(char32_t ucs4)
bool isNull() const const
bool isSpace(char32_t ucs4)
char32_t mirroredChar(char32_t ucs4)
char toLatin1() const const
char32_t toUpper(char32_t ucs4)
QColor fromString(QAnyStringView name)
bool isValid() const const
bool copy(const QString &fileName, const QString &newName)
bool exists() const const
QUrl getSaveFileUrl(QWidget *parent, const QString &caption, const QUrl &dir, const QString &filter, QString *selectedFilter, Options options, const QStringList &supportedSchemes)
QString canonicalFilePath() const const
bool isSymLink() const const
iterator find(const Key &key)
T value(const Key &key) const const
QIcon fromTheme(const QString &name)
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
const_iterator cbegin() const const
const_iterator cend() const const
qsizetype count() const const
bool isEmpty() const const
void prepend(parameter_type value)
void push_back(parameter_type value)
void reserve(qsizetype size)
qsizetype size() const const
T value(qsizetype i) const const
QString toLower(const QString &str) const const
QString toUpper(const QString &str) const const
QMimeType mimeTypeForData(QIODevice *device) const const
QMimeType mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mode) const const
QMimeType mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device) const const
void setParent(QObject *parent)
int exitCode() const const
void setWorkingDirectory(const QString &dir)
void start(OpenMode mode)
bool waitForFinished(int msecs)
bool waitForStarted(int msecs)
QRegularExpressionMatch match(QStringView subjectView, qsizetype offset, MatchType matchType, MatchOptions matchOptions) const const
UnanchoredWildcardConversion
QString wildcardToRegularExpression(QStringView pattern, WildcardConversionOptions options)
bool contains(const QSet< T > &other) const const
iterator insert(const T &value)
QString findExecutable(const QString &executableName, const QStringList &paths)
qsizetype count() const const
QString & append(QChar ch)
QString arg(Args &&... args) const const
const QChar at(qsizetype position) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString fromUtf8(QByteArrayView str)
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
bool isNull() const const
QString left(qsizetype n) const const
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) const const
QString repeated(qsizetype times) const const
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
void reserve(qsizetype size)
qsizetype size() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
int toInt(bool *ok, int base) const const
QByteArray toLatin1() const const
QString toLower() const const
QString toUpper() const const
QString trimmed() const const
QString join(QChar separator) const const
QStringView mid(qsizetype start, qsizetype length) const const
QList< QStringView > split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QTextStream & left(QTextStream &stream)
QTextStream & right(QTextStream &stream)
virtual QString fileName() const const override
int nextCursorPosition(int oldPos, CursorMode mode) const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QUrl adjusted(FormattingOptions options) const const
QString fileName(ComponentFormattingOptions options) const const
QUrl fromLocalFile(const QString &localFile)
bool isEmpty() const const
bool isLocalFile() const const
bool isValid() const const
QString path(ComponentFormattingOptions options) const const
QString url(FormattingOptions options) const const
const_iterator cbegin() const const
const_iterator cend() const const
void reserve(qsizetype size)