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);
229 m_autoReloadThrottle.setInterval(QStandardPaths::isTestModeEnabled() ? 50 : 3000);
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()) {
705 auto insertStart = position;
706 int currentLine = position.
line();
707 int currentLineStart = 0;
708 const int totalLength = text.
length();
709 int insertColumn = position.
column();
712 if (position.
line() > lines()) {
714 while (line <= position.
line()) {
715 editInsertLine(line,
QString(),
false);
718 if (insertStart == position) {
719 insertStart = m_editLastChangeStartCursor;
727 int positionColumnExpanded = insertColumn;
728 const int tabWidth = config()->tabWidth();
730 if (currentLine < lines()) {
731 positionColumnExpanded = plainKateTextLine(currentLine).toVirtualColumn(insertColumn, tabWidth);
737 for (; pos < totalLength; pos++) {
738 const QChar &ch = text.
at(pos);
742 if (currentLineStart < pos) {
743 editInsertText(currentLine, insertColumn, text.
mid(currentLineStart, pos - currentLineStart), notify);
744 endCol = insertColumn + (pos - currentLineStart);
749 const auto wrapColumn = insertColumn + pos - currentLineStart;
750 const auto currentLineLength = lineLength(currentLine);
751 if (wrapColumn > currentLineLength) {
752 editInsertText(currentLine, currentLineLength,
QString(wrapColumn - currentLineLength,
QLatin1Char(
' ')), notify);
756 editWrapLine(currentLine, wrapColumn,
true,
nullptr, notify);
764 auto l = currentLine < lines();
765 if (currentLine == lastLine() + 1) {
766 editInsertLine(currentLine,
QString(), notify);
769 insertColumn = positionColumnExpanded;
771 insertColumn = plainKateTextLine(currentLine).fromVirtualColumn(insertColumn, tabWidth);
775 currentLineStart = pos + 1;
780 if (currentLineStart < pos) {
781 editInsertText(currentLine, insertColumn, text.
mid(currentLineStart, pos - currentLineStart), notify);
782 endCol = insertColumn + (pos - currentLineStart);
787 Q_EMIT textInsertedRange(
this, insertedRange);
795 if (!isReadWrite()) {
807 if (!isReadWrite()) {
819 Q_EMIT aboutToRemoveText(range);
825 if (range.
end().
line() > lastLine()) {
836 if (to <= lastLine()) {
837 editRemoveText(to, 0, range.
end().
column());
846 editRemoveLines(from + 1, to - 1);
850 editRemoveText(from, range.
start().
column(), m_buffer->plainLine(from).length() - range.
start().
column());
851 editUnWrapLine(from);
856 int startLine = qMax(0, range.
start().
line());
857 int vc1 = toVirtualColumn(range.
start());
858 int vc2 = toVirtualColumn(range.
end());
859 for (
int line = qMin(range.
end().
line(), lastLine()); line >= startLine; --line) {
860 int col1 = fromVirtualColumn(line, vc1);
861 int col2 = fromVirtualColumn(line, vc2);
862 editRemoveText(line, qMin(col1, col2), qAbs(col2 - col1));
870bool KTextEditor::DocumentPrivate::insertLine(
int l,
const QString &str)
872 if (!isReadWrite()) {
876 if (l < 0 || l > lines()) {
880 return editInsertLine(l, str);
883bool KTextEditor::DocumentPrivate::insertLines(
int line,
const QStringList &text)
885 if (!isReadWrite()) {
889 if (line < 0 || line > lines()) {
894 for (
const QString &
string : text) {
895 success &= editInsertLine(line++,
string);
901bool KTextEditor::DocumentPrivate::removeLine(
int line)
903 if (!isReadWrite()) {
907 if (line < 0 || line > lastLine()) {
911 return editRemoveLine(line);
914qsizetype KTextEditor::DocumentPrivate::totalCharacters()
const
917 for (
int i = 0; i < m_buffer->lines(); ++i) {
918 l += m_buffer->lineLength(i);
923int KTextEditor::DocumentPrivate::lines()
const
925 return m_buffer->lines();
928int KTextEditor::DocumentPrivate::lineLength(
int line)
const
930 return m_buffer->lineLength(line);
935 return m_buffer->cursorToOffset(c);
940 return m_buffer->offsetToCursor(offset);
943bool KTextEditor::DocumentPrivate::isLineModified(
int line)
const
945 if (line < 0 || line >= lines()) {
950 return l.markedAsModified();
953bool KTextEditor::DocumentPrivate::isLineSaved(
int line)
const
955 if (line < 0 || line >= lines()) {
960 return l.markedAsSavedOnDisk();
963bool KTextEditor::DocumentPrivate::isLineTouched(
int line)
const
965 if (line < 0 || line >= lines()) {
970 return l.markedAsModified() || l.markedAsSavedOnDisk();
978bool KTextEditor::DocumentPrivate::editStart()
982 if (editSessionNumber > 1) {
986 editIsRunning =
true;
991 m_undoManager->editStart();
993 for (
auto view :
std::as_const(m_views)) {
994 static_cast<ViewPrivate *
>(view)->editStart();
997 m_buffer->editStart();
1004bool KTextEditor::DocumentPrivate::editEnd()
1006 if (editSessionNumber == 0) {
1012 if (m_buffer->editChanged() && (editSessionNumber == 1)) {
1013 if (m_undoManager->isActive() && config()->wordWrap()) {
1014 wrapText(m_buffer->editTagStart(), m_buffer->editTagEnd());
1018 editSessionNumber--;
1020 if (editSessionNumber > 0) {
1026 m_buffer->editEnd();
1028 m_undoManager->editEnd();
1031 for (
auto view :
std::as_const(m_views)) {
1032 static_cast<ViewPrivate *
>(view)->editEnd(m_buffer->editTagStart(), m_buffer->editTagEnd(), m_buffer->editTagFrom());
1035 if (m_buffer->editChanged()) {
1037 Q_EMIT textChanged(
this);
1043 if (m_editLastChangeStartCursor.isValid()) {
1044 saveEditingPositions(m_editLastChangeStartCursor);
1047 if (config()->autoSave() && config()->autoSaveInterval() > 0) {
1048 m_autoSaveTimer.start();
1051 editIsRunning =
false;
1055void KTextEditor::DocumentPrivate::pushEditState()
1057 editStateStack.push(editSessionNumber);
1060void KTextEditor::DocumentPrivate::popEditState()
1062 if (editStateStack.isEmpty()) {
1066 int count = editStateStack.pop() - editSessionNumber;
1077void KTextEditor::DocumentPrivate::inputMethodStart()
1079 m_undoManager->inputMethodStart();
1082void KTextEditor::DocumentPrivate::inputMethodEnd()
1084 m_undoManager->inputMethodEnd();
1087bool KTextEditor::DocumentPrivate::wrapText(
int startLine,
int endLine)
1089 if (startLine < 0 || endLine < 0) {
1093 if (!isReadWrite()) {
1097 int col = config()->wordWrapAt();
1105 for (
int line = startLine; (line <= endLine) && (line < lines()); line++) {
1111 bool nextlValid = line + 1 < lines();
1116 int eolPosition = l.
length() - 1;
1122 for (; z2 < l.
length(); z2++) {
1124 if (t.
at(z2) == tabChar) {
1125 x += m_buffer->tabWidth() - (x % m_buffer->tabWidth());
1135 const int colInChars = qMin(z2, l.
length() - 1);
1136 int searchStart = colInChars;
1140 if (searchStart == eolPosition && t.
at(searchStart).
isSpace()) {
1152 for (z = searchStart; z >= 0; z--) {
1156 if ((nw < 0) && highlight()->canBreakAt(t.
at(z), l.
attribute(z))) {
1172 if ((nw >= 0) && nw < colInChars) {
1175 z = (nw >= 0) ? nw : colInChars;
1179 editWrapLine(line, z,
true);
1180 editMarkLineAutoWrapped(line + 1,
true);
1185 editInsertText(line + 1, 0, QStringLiteral(
" "));
1188 bool newLineAdded =
false;
1189 editWrapLine(line, z,
false, &newLineAdded);
1191 editMarkLineAutoWrapped(line + 1,
true);
1203bool KTextEditor::DocumentPrivate::wrapParagraph(
int first,
int last)
1205 if (first == last) {
1206 return wrapText(first, last);
1209 if (first < 0 || last < first) {
1213 if (last >= lines() || first > last) {
1217 if (!isReadWrite()) {
1224 std::unique_ptr<KTextEditor::MovingRange> range(newMovingRange(
KTextEditor::Range(first, 0, last, 0)));
1228 for (
int line = first; line <= range->
end().line(); ++line) {
1230 if (plainKateTextLine(first).firstChar() < 0) {
1233 curr->setPosition(curr->line() + 1, 0);
1238 if (plainKateTextLine(line).firstChar() < 0) {
1239 curr->setPosition(line, 0);
1240 joinLines(first, line - 1);
1243 wrapText(first, first);
1245 first = curr->line() + 1;
1251 bool needWrap = (curr->line() != range->
end().
line());
1252 if (needWrap && plainKateTextLine(first).firstChar() != -1) {
1253 joinLines(first, range->
end().
line());
1256 wrapText(first, first);
1264bool KTextEditor::DocumentPrivate::editInsertText(
int line,
int col,
const QString &s,
bool notify)
1267 EDIT_DEBUG <<
"editInsertText" << line << col << s;
1269 if (line < 0 || col < 0) {
1278 if (!isReadWrite()) {
1282 auto l = plainKateTextLine(line);
1292 if (col2 > length) {
1297 m_undoManager->slotTextInserted(line, col2, s2, l);
1303 m_buffer->insertText(m_editLastChangeStartCursor, s2);
1313bool KTextEditor::DocumentPrivate::editRemoveText(
int line,
int col,
int len)
1316 EDIT_DEBUG <<
"editRemoveText" << line << col << len;
1318 if (line < 0 || line >= lines() || col < 0 || len < 0) {
1322 if (!isReadWrite()) {
1339 len = qMin(len, l.
text().
size() - col);
1345 m_undoManager->slotTextRemoved(line, col, oldText, l);
1360bool KTextEditor::DocumentPrivate::editMarkLineAutoWrapped(
int line,
bool autowrapped)
1363 EDIT_DEBUG <<
"editMarkLineAutoWrapped" << line << autowrapped;
1365 if (line < 0 || line >= lines()) {
1369 if (!isReadWrite()) {
1375 m_undoManager->slotMarkLineAutoWrapped(line, autowrapped);
1379 m_buffer->setLineMetaData(line, l);
1386bool KTextEditor::DocumentPrivate::editWrapLine(
int line,
int col,
bool newLine,
bool *newLineAdded,
bool notify)
1389 EDIT_DEBUG <<
"editWrapLine" << line << col << newLine;
1391 if (line < 0 || line >= lines() || col < 0) {
1395 if (!isReadWrite()) {
1399 const auto tl = plainKateTextLine(line);
1403 const bool nextLineValid = lineLength(line + 1) >= 0;
1405 m_undoManager->slotLineWrapped(line, col, tl.length() - col, (!nextLineValid || newLine), tl);
1407 if (!nextLineValid || newLine) {
1411 for (
const auto &mark :
std::as_const(m_marks)) {
1412 if (mark->line >= line) {
1413 if ((col == 0) || (mark->line > line)) {
1419 for (
const auto &mark :
list) {
1420 m_marks.take(mark->line);
1423 for (
const auto &mark :
list) {
1425 m_marks.insert(mark->line, mark);
1429 Q_EMIT marksChanged(
this);
1434 (*newLineAdded) =
true;
1438 m_buffer->unwrapLine(line + 2);
1442 (*newLineAdded) =
false;
1458bool KTextEditor::DocumentPrivate::editUnWrapLine(
int line,
bool removeLine,
int length)
1461 EDIT_DEBUG <<
"editUnWrapLine" << line << removeLine << length;
1463 if (line < 0 || line >= lines() || line + 1 >= lines() || length < 0) {
1467 if (!isReadWrite()) {
1477 m_undoManager->slotLineUnWrapped(line, col, length, removeLine, tl, nextLine);
1480 m_buffer->unwrapLine(line + 1);
1483 m_buffer->unwrapLine(line + 1);
1487 for (
const auto &mark :
std::as_const(m_marks)) {
1488 if (mark->line >= line + 1) {
1492 if (mark->line == line + 1) {
1493 auto m = m_marks.take(line);
1495 mark->type |= m->type;
1501 for (
const auto &mark :
list) {
1502 m_marks.take(mark->line);
1505 for (
const auto &mark :
list) {
1507 m_marks.insert(mark->line, mark);
1511 Q_EMIT marksChanged(
this);
1517 Q_EMIT textRemoved(
this,
KTextEditor::Range(line, col, line + 1, 0), QStringLiteral(
"\n"));
1524bool KTextEditor::DocumentPrivate::editInsertLine(
int line,
const QString &s,
bool notify)
1527 EDIT_DEBUG <<
"editInsertLine" << line << s;
1533 if (!isReadWrite()) {
1537 if (line > lines()) {
1543 m_undoManager->slotLineInserted(line, s);
1557 for (
const auto &mark :
std::as_const(m_marks)) {
1558 if (mark->line >= line) {
1563 for (
const auto &mark :
list) {
1564 m_marks.take(mark->line);
1567 for (
const auto &mark :
list) {
1569 m_marks.insert(mark->line, mark);
1573 Q_EMIT marksChanged(
this);
1579 int prevLineLength = lineLength(line - 1);
1586 m_editLastChangeStartCursor = rangeInserted.start();
1589 Q_EMIT textInsertedRange(
this, rangeInserted);
1597bool KTextEditor::DocumentPrivate::editRemoveLine(
int line)
1599 return editRemoveLines(line, line);
1602bool KTextEditor::DocumentPrivate::editRemoveLines(
int from,
int to)
1605 EDIT_DEBUG <<
"editRemoveLines" << from << to;
1607 if (to < from || from < 0 || to > lastLine()) {
1611 if (!isReadWrite()) {
1616 return editRemoveText(0, 0, lineLength(0));
1623 for (
int line = to; line >= from; --line) {
1626 m_undoManager->slotLineRemoved(line, l.
text(), l);
1632 for (
int line = to; line >= from; --line) {
1634 if (line + 1 < m_buffer->lines()) {
1635 m_buffer->unwrapLine(line + 1);
1637 m_buffer->unwrapLine(line);
1645 int line = mark->line;
1648 }
else if (line >= from) {
1653 for (
int line : rmark) {
1654 delete m_marks.take(line);
1657 for (
auto mark :
list) {
1658 m_marks.take(mark->line);
1661 for (
auto mark :
list) {
1662 mark->line -= to - from + 1;
1663 m_marks.insert(mark->line, mark);
1667 Q_EMIT marksChanged(
this);
1672 if (to == lastLine() + to - from + 1) {
1675 int prevLineLength = lineLength(from - 1);
1681 m_editLastChangeStartCursor = rangeRemoved.start();
1692uint KTextEditor::DocumentPrivate::undoCount()
const
1694 return m_undoManager->undoCount();
1697uint KTextEditor::DocumentPrivate::redoCount()
const
1699 return m_undoManager->redoCount();
1702void KTextEditor::DocumentPrivate::undo()
1704 m_undoManager->undo();
1707void KTextEditor::DocumentPrivate::redo()
1709 m_undoManager->redo();
1715KTextEditor::DocumentPrivate::searchText(
KTextEditor::Range range,
const QString &pattern,
const KTextEditor::SearchOptions options)
const
1717 const bool escapeSequences = options.testFlag(KTextEditor::EscapeSequences);
1718 const bool regexMode = options.testFlag(KTextEditor::Regex);
1719 const bool backwards = options.testFlag(KTextEditor::Backwards);
1720 const bool wholeWords = options.testFlag(KTextEditor::WholeWords);
1731 return searcher.search(pattern, range, backwards, patternOptions);
1734 if (escapeSequences) {
1754QWidget *KTextEditor::DocumentPrivate::dialogParent()
1771QUrl KTextEditor::DocumentPrivate::getSaveFileUrl(
const QString &dialogTitle)
1774 QUrl startUrl = url();
1784 const auto views = mainWindow->
views();
1785 for (
auto view : views) {
1799bool KTextEditor::DocumentPrivate::setMode(
const QString &name)
1801 return updateFileType(name);
1806 return const_cast<KTextEditor::DocumentPrivate *
>(
this)->defStyleNum(position.
line(), position.
column());
1809QString KTextEditor::DocumentPrivate::mode()
const
1814QStringList KTextEditor::DocumentPrivate::modes()
const
1820 for (KateFileType *type : modeList) {
1827bool KTextEditor::DocumentPrivate::setHighlightingMode(
const QString &name)
1829 int mode = KateHlManager::self()->nameFind(name);
1833 m_buffer->setHighlight(mode);
1837QString KTextEditor::DocumentPrivate::highlightingMode()
const
1839 return highlight()->name();
1842QStringList KTextEditor::DocumentPrivate::highlightingModes()
const
1844 const auto modeList = KateHlManager::self()->modeList();
1847 for (
const auto &hl : modeList) {
1853QString KTextEditor::DocumentPrivate::highlightingModeSection(
int index)
const
1855 return KateHlManager::self()->modeList().at(index).section();
1858QString KTextEditor::DocumentPrivate::modeSection(
int index)
const
1863void KTextEditor::DocumentPrivate::bufferHlChanged()
1869 m_indenter->checkRequiredStyle();
1871 Q_EMIT highlightingModeChanged(
this);
1874void KTextEditor::DocumentPrivate::setDontChangeHlOnSave()
1876 m_hlSetByUser =
true;
1879void KTextEditor::DocumentPrivate::bomSetByUser()
1881 m_bomSetByUser =
true;
1888 if (!flags.
contains(QStringLiteral(
"SkipEncoding"))) {
1891 if (!tmpenc.
isEmpty() && (tmpenc != encoding())) {
1892 setEncoding(tmpenc);
1896 if (!flags.
contains(QStringLiteral(
"SkipUrl"))) {
1901 if (!url.isEmpty() && url.isValid()) {
1910 if (!flags.
contains(QStringLiteral(
"SkipMode"))) {
1914 if (kconfig.
hasKey(
"Mode Set By User")) {
1916 m_fileTypeSetByUser =
true;
1917 updateFileType(kconfig.
readEntry(
"Mode"));
1921 if (!flags.
contains(QStringLiteral(
"SkipHighlighting"))) {
1923 if (kconfig.
hasKey(
"Highlighting Set By User")) {
1924 const int mode = KateHlManager::self()->nameFind(kconfig.
readEntry(
"Highlighting"));
1925 m_hlSetByUser =
true;
1928 m_buffer->setHighlight(mode);
1935 if (!userSetIndentMode.
isEmpty()) {
1936 config()->setIndentationMode(userSetIndentMode);
1941 for (
int i = 0; i < marks.
count(); i++) {
1942 addMark(marks.
at(i), KTextEditor::DocumentPrivate::markType01);
1948 if (this->url().isLocalFile()) {
1955 if (!flags.
contains(QStringLiteral(
"SkipUrl"))) {
1966 if (m_fileTypeSetByUser && !flags.
contains(QStringLiteral(
"SkipMode"))) {
1970 kconfig.
writeEntry(
"Mode Set By User", m_fileTypeSetByUser);
1973 if (m_hlSetByUser && !flags.
contains(QStringLiteral(
"SkipHighlighting"))) {
1978 kconfig.
writeEntry(
"Highlighting Set By User", m_hlSetByUser);
1982 if (m_indenterSetByUser) {
1983 kconfig.
writeEntry(
"Indentation Mode", config()->indentationMode());
1988 for (
const auto &mark :
std::as_const(m_marks)) {
2001uint KTextEditor::DocumentPrivate::mark(
int line)
2011void KTextEditor::DocumentPrivate::setMark(
int line, uint markType)
2014 addMark(line, markType);
2017void KTextEditor::DocumentPrivate::clearMark(
int line)
2019 if (line < 0 || line > lastLine()) {
2023 if (
auto mark = m_marks.take(line)) {
2024 Q_EMIT markChanged(
this, *mark, MarkRemoved);
2025 Q_EMIT marksChanged(
this);
2032void KTextEditor::DocumentPrivate::addMark(
int line, uint markType)
2036 if (line < 0 || line > lastLine()) {
2040 if (markType == 0) {
2044 if ((mark = m_marks.value(line))) {
2046 markType &= ~mark->type;
2048 if (markType == 0) {
2053 mark->
type |= markType;
2057 mark->
type = markType;
2058 m_marks.insert(line, mark);
2064 temp.
type = markType;
2065 Q_EMIT markChanged(
this, temp, MarkAdded);
2067 Q_EMIT marksChanged(
this);
2072void KTextEditor::DocumentPrivate::removeMark(
int line, uint markType)
2074 if (line < 0 || line > lastLine()) {
2078 auto it = m_marks.find(line);
2079 if (it == m_marks.end()) {
2085 markType &= mark->
type;
2087 if (markType == 0) {
2092 mark->
type &= ~markType;
2097 temp.
type = markType;
2098 Q_EMIT markChanged(
this, temp, MarkRemoved);
2100 if (mark->
type == 0) {
2105 Q_EMIT marksChanged(
this);
2115void KTextEditor::DocumentPrivate::requestMarkTooltip(
int line,
QPoint position)
2122 bool handled =
false;
2123 Q_EMIT markToolTipRequested(
this, *mark, position, handled);
2126bool KTextEditor::DocumentPrivate::handleMarkClick(
int line)
2128 bool handled =
false;
2133 Q_EMIT markClicked(
this, *mark, handled);
2139bool KTextEditor::DocumentPrivate::handleMarkContextMenu(
int line,
QPoint position)
2141 bool handled =
false;
2146 Q_EMIT markContextMenuRequested(
this, *mark, position, handled);
2152void KTextEditor::DocumentPrivate::clearMarks()
2161 for (
const auto &m : marksCopy) {
2162 Q_EMIT markChanged(
this, *m, MarkRemoved);
2167 Q_EMIT marksChanged(
this);
2171void KTextEditor::DocumentPrivate::setMarkDescription(Document::MarkTypes type,
const QString &description)
2173 m_markDescriptions.insert(type, description);
2176QColor KTextEditor::DocumentPrivate::markColor(Document::MarkTypes type)
const
2179 if ((uint)
type >= (uint)markType01 && (uint)
type <= reserved) {
2180 return KateRendererConfig::global()->lineMarkerColor(type);
2186QString KTextEditor::DocumentPrivate::markDescription(Document::MarkTypes type)
const
2188 return m_markDescriptions.value(type,
QString());
2191void KTextEditor::DocumentPrivate::setEditableMarks(uint markMask)
2193 m_editableMarks = markMask;
2196uint KTextEditor::DocumentPrivate::editableMarks()
const
2198 return m_editableMarks;
2202void KTextEditor::DocumentPrivate::setMarkIcon(Document::MarkTypes markType,
const QIcon &icon)
2204 m_markIcons.insert(markType, icon);
2207QIcon KTextEditor::DocumentPrivate::markIcon(Document::MarkTypes markType)
const
2209 return m_markIcons.value(markType,
QIcon());
2213bool KTextEditor::DocumentPrivate::print()
2215 return KatePrinter::print(
this);
2218void KTextEditor::DocumentPrivate::printPreview()
2220 KatePrinter::printPreview(
this);
2225QString KTextEditor::DocumentPrivate::mimeType()
2227 if (!m_modOnHd && url().isLocalFile()) {
2235 for (
int i = 0; (i < lines()) && (buf.
size() <= 4096); ++i) {
2236 buf.
append(line(i).toUtf8());
2241 if (!url().path().isEmpty()) {
2251void KTextEditor::DocumentPrivate::showAndSetOpeningErrorAccess()
2254 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.",
2257 message->setWordWrap(
true);
2259 i18nc(
"translators: you can also translate 'Try Again' with 'Reload'",
"Try Again"),
2264 closeAction->
setToolTip(
i18nc(
"Close the message being displayed",
"Close message"));
2267 message->addAction(tryAgainAction);
2268 message->addAction(closeAction);
2271 postMessage(message);
2274 m_openingError =
true;
2278void KTextEditor::DocumentPrivate::openWithLineLengthLimitOverride()
2281 const int longestLine = m_buffer->longestLineLoaded();
2282 int newLimit = pow(2, ceil(log2(longestLine)));
2283 if (newLimit <= longestLine) {
2288 config()->setLineLengthLimit(newLimit);
2293 if (!m_openingError) {
2295 m_readWriteStateBeforeLoading =
true;
2299int KTextEditor::DocumentPrivate::lineLengthLimit()
const
2301 return config()->lineLengthLimit();
2305bool KTextEditor::DocumentPrivate::openFile()
2308 Q_EMIT aboutToInvalidateMovingInterfaceContent(
this);
2311 m_openingError =
false;
2317 QString currentEncoding = encoding();
2324 if (pos != -1 && !(m_reloading && m_userSetEncodingForNextReload)) {
2336 if (m_reloading && m_userSetEncodingForNextReload && (currentEncoding != encoding())) {
2337 setEncoding(currentEncoding);
2340 bool success = m_buffer->openFile(localFilePath(), (m_reloading && m_userSetEncodingForNextReload));
2353 for (
auto view :
std::as_const(m_views)) {
2356 static_cast<ViewPrivate *
>(view)->updateView(
true);
2360 Q_EMIT textChanged(
this);
2361 Q_EMIT loaded(
this);
2368 m_modOnHdReason = OnDiskUnmodified;
2369 m_prevModOnHdReason = OnDiskUnmodified;
2370 Q_EMIT modifiedOnDisk(
this, m_modOnHd, m_modOnHdReason);
2375 if (!isEmpty() && config()->autoDetectIndent() && !config()->isSet(KateDocumentConfig::IndentationWidth)
2376 && !config()->isSet(KateDocumentConfig::ReplaceTabsWithSpaces)) {
2378 auto result = detecter.detect(config()->indentationWidth(), config()->replaceTabsDyn());
2379 config()->setIndentationWidth(result.indentWidth);
2380 config()->setReplaceTabsDyn(result.indentUsingSpaces);
2387 showAndSetOpeningErrorAccess();
2391 if (m_buffer->brokenEncoding()) {
2393 setReadWrite(
false);
2394 m_readWriteStateBeforeLoading =
false;
2396 i18n(
"The file %1 was opened with %2 encoding but contained invalid characters.<br />"
2397 "It is set to read-only mode, as saving might destroy its content.<br />"
2398 "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.",
2400 m_buffer->textCodec()),
2402 message->setWordWrap(
true);
2403 postMessage(message);
2406 m_openingError =
true;
2410 if (m_buffer->tooLongLinesWrapped()) {
2412 setReadWrite(
false);
2413 m_readWriteStateBeforeLoading =
false;
2415 new KTextEditor::Message(
i18n(
"The file %1 was opened and contained lines longer than the configured Line Length Limit (%2 characters).<br />"
2416 "The longest of those lines was %3 characters long<br/>"
2417 "Those lines were wrapped and the document is set to read-only mode, as saving will modify its content.",
2419 config()->lineLengthLimit(),
2420 m_buffer->longestLineLoaded()),
2422 QAction *increaseAndReload =
new QAction(
i18n(
"Temporarily raise limit and reload file"), message);
2424 message->addAction(increaseAndReload,
true);
2425 message->addAction(
new QAction(
i18n(
"Close"), message),
true);
2426 message->setWordWrap(
true);
2427 postMessage(message);
2430 m_openingError =
true;
2439bool KTextEditor::DocumentPrivate::saveFile()
2442 delete m_modOnHdHandler;
2445 if (!url().isEmpty()) {
2446 if (m_fileChangedDialogsActivated && m_modOnHd) {
2449 if (!isModified()) {
2452 str +
i18n(
"Do you really want to save this unmodified file? You could overwrite changed data in the file on disk."),
2453 i18n(
"Trying to Save Unmodified File"),
2463 "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."),
2464 i18n(
"Possible Data Loss"),
2476 if (!m_buffer->canEncode()
2478 i18n(
"The selected encoding cannot encode every Unicode character in this document. Do you really want to save "
2479 "it? There could be some data lost."),
2480 i18n(
"Possible Data Loss"),
2488 if (!createBackupFile()) {
2493 QString oldPath = m_dirWatchFile;
2496 if (oldPath != localFilePath()) {
2499 if (url().isLocalFile()) {
2506 const bool variablesWereRead = readVariables();
2512 if (!variablesWereRead) {
2513 for (
auto *view :
std::as_const(m_views)) {
2514 auto v =
static_cast<ViewPrivate *
>(view);
2515 if (v->isVisible()) {
2516 const auto range = v->visibleRange();
2518 bool repaint =
false;
2520 if (isLineModified(i)) {
2527 v->updateView(
true);
2534 deactivateDirWatch();
2539 removeTrailingSpacesAndAddNewLineAtEof();
2544 if (!m_buffer->saveFile(localFilePath())) {
2546 activateDirWatch(oldPath);
2548 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 "
2549 "that enough disk space is available.\nThe original file may be lost or damaged. "
2550 "Don't quit the application until the file is successfully written.",
2566 m_modOnHdReason = OnDiskUnmodified;
2567 m_prevModOnHdReason = OnDiskUnmodified;
2568 Q_EMIT modifiedOnDisk(
this, m_modOnHd, m_modOnHdReason);
2573 m_undoManager->undoSafePoint();
2574 m_undoManager->updateLineModifications();
2582bool KTextEditor::DocumentPrivate::createBackupFile()
2585 const bool backupLocalFiles = config()->backupOnSaveLocal();
2586 const bool backupRemoteFiles = config()->backupOnSaveRemote();
2590 if (!backupLocalFiles && !backupRemoteFiles) {
2597 bool needBackup = backupLocalFiles && backupRemoteFiles;
2599 bool slowOrRemoteFile = !u.isLocalFile();
2600 if (!slowOrRemoteFile) {
2604 slowOrRemoteFile = (mountPoint && mountPoint->probablySlow());
2606 needBackup = (!slowOrRemoteFile && backupLocalFiles) || (slowOrRemoteFile && backupRemoteFiles);
2617 if (backupPrefix.isEmpty() && backupSuffix.isEmpty()) {
2624 u.setPath(backupPrefix + u.fileName() + backupSuffix);
2627 const QString fileName = u.fileName();
2629 u.setPath(u.path() + backupPrefix + fileName + backupSuffix);
2632 qCDebug(LOG_KTE) <<
"backup src file name: " << url();
2633 qCDebug(LOG_KTE) <<
"backup dst file name: " << u;
2636 bool backupSuccess =
false;
2639 if (u.isLocalFile()) {
2642 QFile backupFile(u.toLocalFile());
2643 if (backupFile.exists()) {
2644 backupFile.remove();
2647 backupSuccess =
QFile::copy(url().toLocalFile(), u.toLocalFile());
2649 backupSuccess =
true;
2655 if (statJob->
exec()) {
2660 backupSuccess = job->
exec();
2662 backupSuccess =
true;
2669 i18n(
"For file %1 no backup copy could be created before saving."
2670 " If an error occurs while saving, you might lose the data of this file."
2671 " A reason could be that the media you write to is full or the directory of the file is read-only for you.",
2673 i18n(
"Failed to create backup copy."),
2676 QStringLiteral(
"Backup Failed Warning"))
2684void KTextEditor::DocumentPrivate::readDirConfig()
2694 while (!seenDirectories.
contains(
dir.absolutePath())) {
2696 seenDirectories.
insert(
dir.absolutePath());
2704 QString line = stream.readLine();
2705 while ((linesRead < 32) && !line.
isNull()) {
2706 readVariableLine(line);
2708 line = stream.readLine();
2722#if EDITORCONFIG_FOUND
2726 EditorConfig editorConfig(
this);
2727 editorConfig.parse();
2731void KTextEditor::DocumentPrivate::activateDirWatch(
const QString &useFileName)
2733 QString fileToUse = useFileName;
2735 fileToUse = localFilePath();
2749 if (fileToUse == m_dirWatchFile) {
2754 deactivateDirWatch();
2757 if (url().isLocalFile() && !fileToUse.
isEmpty()) {
2759 m_dirWatchFile = fileToUse;
2763void KTextEditor::DocumentPrivate::deactivateDirWatch()
2765 if (!m_dirWatchFile.isEmpty()) {
2769 m_dirWatchFile.clear();
2772bool KTextEditor::DocumentPrivate::openUrl(
const QUrl &url)
2776 m_fileTypeSetByUser =
false;
2783bool KTextEditor::DocumentPrivate::closeUrl()
2788 if (!m_reloading && !url().isEmpty()) {
2789 if (m_fileChangedDialogsActivated && m_modOnHd) {
2791 delete m_modOnHdHandler;
2793 QWidget *parentWidget(dialogParent());
2796 +
i18n(
"Do you really want to continue to close this file? Data loss may occur."),
2797 i18n(
"Possible Data Loss"),
2800 QStringLiteral(
"kate_close_modonhd_%1").arg(m_modOnHdReason))
2803 m_reloading =
false;
2814 m_reloading =
false;
2820 Q_EMIT aboutToClose(
this);
2824 if (!m_messageHash.isEmpty()) {
2825 const auto keys = m_messageHash.keys();
2832 Q_EMIT aboutToInvalidateMovingInterfaceContent(
this);
2835 deactivateDirWatch();
2846 m_modOnHdReason = OnDiskUnmodified;
2847 m_prevModOnHdReason = OnDiskUnmodified;
2848 Q_EMIT modifiedOnDisk(
this, m_modOnHd, m_modOnHdReason);
2858 m_undoManager->clearUndo();
2859 m_undoManager->clearRedo();
2865 m_buffer->setHighlight(0);
2868 for (
auto view :
std::as_const(m_views)) {
2869 static_cast<ViewPrivate *
>(view)->clearSelection();
2870 static_cast<ViewPrivate *
>(view)->
clear();
2875 m_swapfile->fileClosed();
2882bool KTextEditor::DocumentPrivate::isDataRecoveryAvailable()
const
2884 return m_swapfile && m_swapfile->shouldRecover();
2887void KTextEditor::DocumentPrivate::recoverData()
2889 if (isDataRecoveryAvailable()) {
2890 m_swapfile->recover();
2894void KTextEditor::DocumentPrivate::discardDataRecovery()
2896 if (isDataRecoveryAvailable()) {
2897 m_swapfile->discard();
2901void KTextEditor::DocumentPrivate::setReadWrite(
bool rw)
2903 if (isReadWrite() == rw) {
2909 for (
auto v :
std::as_const(m_views)) {
2910 auto view =
static_cast<ViewPrivate *
>(v);
2911 view->slotUpdateUndo();
2912 view->slotReadWriteChanged();
2915 Q_EMIT readWriteChanged(
this);
2918void KTextEditor::DocumentPrivate::setModified(
bool m)
2920 if (isModified() != m) {
2923 for (
auto view :
std::as_const(m_views)) {
2924 static_cast<ViewPrivate *
>(view)->slotUpdateUndo();
2927 Q_EMIT modifiedChanged(
this);
2930 m_undoManager->setModified(m);
2936void KTextEditor::DocumentPrivate::makeAttribs(
bool needInvalidate)
2938 for (
auto view :
std::as_const(m_views)) {
2939 static_cast<ViewPrivate *
>(view)->renderer()->updateAttributes();
2942 if (needInvalidate) {
2943 m_buffer->invalidateHighlighting();
2946 for (
auto v :
std::as_const(m_views)) {
2947 auto view =
static_cast<ViewPrivate *
>(v);
2949 view->updateView(
true);
2954void KTextEditor::DocumentPrivate::internalHlChanged()
2961 Q_ASSERT(!m_views.contains(view));
2962 m_views.append(view);
2965 if (!m_fileType.isEmpty()) {
2970 readVariables(
true);
2972 setActiveView(view);
2977 Q_ASSERT(m_views.contains(view));
2978 m_views.removeAll(view);
2980 if (activeView() == view) {
2981 setActiveView(
nullptr);
2987 if (m_activeView == view) {
2991 m_activeView =
static_cast<KTextEditor::ViewPrivate *
>(view);
2994bool KTextEditor::DocumentPrivate::ownedView(KTextEditor::ViewPrivate *view)
2997 return (m_views.contains(view));
3000int KTextEditor::DocumentPrivate::toVirtualColumn(
int line,
int column)
const
3008 return toVirtualColumn(cursor.
line(), cursor.
column());
3011int KTextEditor::DocumentPrivate::fromVirtualColumn(
int line,
int column)
const
3017int KTextEditor::DocumentPrivate::fromVirtualColumn(
const KTextEditor::Cursor cursor)
const
3019 return fromVirtualColumn(cursor.
line(), cursor.
column());
3028 bool skipAutobrace = closingBracket ==
QLatin1Char(
'\'');
3029 if (highlight() && skipAutobrace) {
3031 skipAutobrace = highlight()->spellCheckingRequiredForLocation(
this, pos - Cursor{0, 1});
3034 if (!skipAutobrace && (closingBracket ==
QLatin1Char(
'\''))) {
3040 skipAutobrace = (count % 2 == 0) ?
true : false;
3042 if (!skipAutobrace && (closingBracket ==
QLatin1Char(
'\"'))) {
3047 skipAutobrace = (count % 2 == 0) ?
true : false;
3049 return skipAutobrace;
3052void KTextEditor::DocumentPrivate::typeChars(KTextEditor::ViewPrivate *view,
QString chars)
3060 QChar closingBracket;
3061 if (view->config()->autoBrackets()) {
3063 const QChar typedChar = chars.
at(0);
3064 const QChar openBracket = matchingStartBracket(typedChar);
3065 if (!openBracket.
isNull()) {
3067 if ((characterAt(curPos) == typedChar) && findMatchingBracket(curPos, 123 ).
isValid()) {
3069 view->cursorRight();
3075 if (chars.
size() == 1) {
3077 closingBracket = matchingEndBracket(typedChar);
3080 if (m_currentAutobraceClosingChar == typedChar && m_currentAutobraceRange) {
3082 m_currentAutobraceRange.reset(
nullptr);
3083 view->cursorRight();
3090 if (view->selection() && closingBracket.
isNull() && view->config()->encloseSelectionInChars()) {
3091 const QChar typedChar = chars.
at(0);
3092 if (view->config()->charsToEncloseSelection().
contains(typedChar)) {
3101 if (view->selection() && !closingBracket.
isNull()) {
3102 std::unique_ptr<KTextEditor::MovingRange> selectionRange(newMovingRange(view->selectionRange()));
3103 const int startLine = qMax(0, selectionRange->start().line());
3104 const int endLine = qMin(selectionRange->end().line(), lastLine());
3105 const bool blockMode = view->blockSelection() && (startLine != endLine);
3107 if (selectionRange->start().column() > selectionRange->end().column()) {
3110 selectionRange->setInsertBehaviors(MovingRange::ExpandLeft | MovingRange::ExpandRight);
3113 const int startColumn = qMin(selectionRange->start().column(), selectionRange->end().column());
3114 const int endColumn = qMax(selectionRange->start().column(), selectionRange->end().column());
3116 for (
int line = startLine; line <= endLine; ++line) {
3118 insertText(r.end(),
QString(closingBracket));
3119 view->slotTextInserted(view, r.end(),
QString(closingBracket));
3120 insertText(r.start(), chars);
3121 view->slotTextInserted(view, r.start(), chars);
3125 for (
const auto &cursor : view->secondaryCursors()) {
3126 if (!cursor.range) {
3129 const auto &currSelectionRange = cursor.range;
3130 auto expandBehaviour = currSelectionRange->insertBehaviors();
3132 insertText(currSelectionRange->end(),
QString(closingBracket));
3133 insertText(currSelectionRange->start(), chars);
3134 currSelectionRange->setInsertBehaviors(expandBehaviour);
3135 cursor.pos->
setPosition(currSelectionRange->end());
3136 auto mutableCursor =
const_cast<KTextEditor::ViewPrivate::SecondaryCursor *
>(&cursor);
3137 mutableCursor->anchor = currSelectionRange->start().toCursor();
3141 insertText(selectionRange->end(),
QString(closingBracket));
3142 view->slotTextInserted(view, selectionRange->end(),
QString(closingBracket));
3143 insertText(selectionRange->start(), chars);
3144 view->slotTextInserted(view, selectionRange->start(), chars);
3148 view->setSelection(selectionRange->toRange());
3149 view->setCursorPosition(selectionRange->end());
3156 if (!view->config()->persistentSelection() && view->selection()) {
3157 view->removeSelectedText();
3162 const bool multiLineBlockMode = view->blockSelection() && view->selection();
3163 if (view->currentInputMode()->overwrite()) {
3166 const int startLine = multiLineBlockMode ? qMax(0, selectionRange.
start().
line()) : view->cursorPosition().line();
3167 const int endLine = multiLineBlockMode ? qMin(selectionRange.
end().
line(), lastLine()) : startLine;
3168 const int virtualColumn = toVirtualColumn(multiLineBlockMode ? selectionRange.
end() : view->cursorPosition());
3170 for (
int line = endLine; line >= startLine; --line) {
3172 const int column = fromVirtualColumn(line, virtualColumn);
3176 if (oldCur.column() < lineLength(line)) {
3178 view->currentInputMode()->overwrittenChar(removed);
3185 chars = eventuallyReplaceTabs(view->cursorPosition(), chars);
3187 if (multiLineBlockMode) {
3189 const int startLine = qMax(0, selectionRange.
start().
line());
3190 const int endLine = qMin(selectionRange.
end().
line(), lastLine());
3191 const int column = toVirtualColumn(selectionRange.
end());
3192 for (
int line = endLine; line >= startLine; --line) {
3193 editInsertText(line, fromVirtualColumn(line, column), chars);
3195 int newSelectionColumn = toVirtualColumn(view->cursorPosition());
3198 view->setSelection(selectionRange);
3203 view->completionWidget()->setIgnoreBufferSignals(
true);
3204 const auto &sc = view->secondaryCursors();
3205 const bool hasClosingBracket = !closingBracket.
isNull();
3206 const QString closingChar = closingBracket;
3207 for (
const auto &c : sc) {
3208 insertText(c.cursor(), chars);
3209 const auto pos = c.cursor();
3210 const auto nextChar = view->document()->
text({pos, pos + Cursor{0, 1}}).trimmed();
3211 if (hasClosingBracket && !skipAutoBrace(closingBracket, pos) && (nextChar.isEmpty() || !nextChar.at(0).isLetterOrNumber())) {
3212 insertText(c.cursor(), closingChar);
3216 view->completionWidget()->setIgnoreBufferSignals(
false);
3218 insertText(view->cursorPosition(), chars);
3225 if (!closingBracket.
isNull() && !skipAutoBrace(closingBracket, view->cursorPosition())) {
3227 const auto cursorPos = view->cursorPosition();
3228 const auto nextChar = view->document()->
text({cursorPos, cursorPos + Cursor{0, 1}}).trimmed();
3229 if (nextChar.isEmpty() || !nextChar.at(0).isLetterOrNumber()) {
3230 insertText(view->cursorPosition(),
QString(closingBracket));
3231 const auto insertedAt(view->cursorPosition());
3232 view->setCursorPosition(cursorPos);
3237 chars.
append(closingBracket);
3239 m_currentAutobraceClosingChar = closingBracket;
3246 const auto &secondaryCursors = view->secondaryCursors();
3247 for (
const auto &c : secondaryCursors) {
3248 m_indenter->userTypedChar(view, c.cursor(), chars.
isEmpty() ?
QChar() : chars.at(chars.length() - 1));
3253 m_indenter->userTypedChar(view, b, chars.
isEmpty() ?
QChar() : chars.at(chars.length() - 1));
3256 view->slotTextInserted(view, oldCur, chars);
3261 if (m_currentAutobraceRange && !m_currentAutobraceRange->toRange().contains(newPos)) {
3262 m_currentAutobraceRange.reset();
3266void KTextEditor::DocumentPrivate::newLine(KTextEditor::ViewPrivate *v, KTextEditor::DocumentPrivate::NewLineIndent indent, NewLinePos newLinePos)
3270 if (!v->config()->persistentSelection() && v->selection()) {
3271 v->removeSelectedText();
3272 v->clearSelection();
3276 if (c.
line() > lastLine()) {
3286 int len = lineLength(ln);
3296 m_buffer->updateHighlighting();
3303 bool moveCursorToTop =
false;
3304 if (newLinePos == Above) {
3305 if (pos.
line() <= 0) {
3308 moveCursorToTop =
true;
3313 }
else if (newLinePos == Below) {
3314 int lastCol = lineLength(pos.
line());
3317 return std::pair{pos, moveCursorToTop};
3321 const auto &secondaryCursors = v->secondaryCursors();
3322 if (!secondaryCursors.empty()) {
3325 for (
const auto &c : secondaryCursors) {
3326 const auto [newPos, moveCursorToTop] = adjustCusorPos(c.cursor());
3328 insertNewLine(c.cursor());
3329 if (moveCursorToTop) {
3333 if (indent == KTextEditor::DocumentPrivate::Indent) {
3337 v->setCursorPosition(c.cursor());
3338 m_indenter->userTypedChar(v, c.cursor(),
QLatin1Char(
'\n'));
3344 v->setCursorPosition(savedPrimary.toCursor());
3347 const auto [newPos, moveCursorToTop] = adjustCusorPos(v->cursorPosition());
3348 v->setCursorPosition(newPos);
3349 insertNewLine(v->cursorPosition());
3350 if (moveCursorToTop) {
3351 v->setCursorPosition({0, 0});
3354 if (indent == KTextEditor::DocumentPrivate::Indent) {
3355 m_indenter->userTypedChar(v, v->cursorPosition(),
QLatin1Char(
'\n'));
3364 if (textLine.
length() < 2) {
3368 uint col = cursor.
column();
3374 if ((textLine.
length() - col) < 2) {
3378 uint line = cursor.
line();
3389 editRemoveText(line, col, 2);
3390 editInsertText(line, col, s);
3397 Q_ASSERT(!firstWord.
overlaps(secondWord));
3405 const QString tempString = text(secondWord);
3408 replaceText(secondWord, text(firstWord));
3409 replaceText(firstWord, tempString);
3415 int col = qMax(c.
column(), 0);
3416 int line = qMax(c.
line(), 0);
3417 if ((col == 0) && (line == 0)) {
3420 if (line >= lines()) {
3427 bool useNextBlock =
false;
3428 if (config()->backspaceIndents()) {
3435 if (pos < 0 || pos >= (
int)colX) {
3437 if ((
int)col > textLine.
length()) {
3443 useNextBlock =
true;
3446 if (!config()->backspaceIndents() || useNextBlock) {
3449 if (!view->config()->backspaceRemoveComposed()) {
3450 beginCursor.setColumn(col - 1);
3452 if (!isValidTextPosition(beginCursor)) {
3454 beginCursor.setColumn(col - 2);
3457 if (
auto l = view->textLayout(c)) {
3458 beginCursor.setColumn(l->previousCursorPosition(c.
column()));
3473 if (config()->wordWrap() && textLine.
endsWith(QStringLiteral(
" "))) {
3486void KTextEditor::DocumentPrivate::backspace(KTextEditor::ViewPrivate *view)
3488 if (!view->config()->persistentSelection() && view->hasSelections()) {
3492 if (view->blockSelection() && view->selection() && range.
start().
column() > 0 && toVirtualColumn(range.
start()) == toVirtualColumn(range.
end())) {
3495 view->setSelection(range);
3497 view->removeSelectedText();
3498 view->ensureUniqueCursors();
3506 const auto &multiCursors = view->secondaryCursors();
3507 view->completionWidget()->setIgnoreBufferSignals(
true);
3508 for (
const auto &c : multiCursors) {
3509 const auto newPos = backspaceAtCursor(view, c.cursor());
3514 view->completionWidget()->setIgnoreBufferSignals(
false);
3517 auto newPos = backspaceAtCursor(view, view->cursorPosition());
3519 view->setCursorPosition(newPos);
3522 view->ensureUniqueCursors();
3527 if (m_currentAutobraceRange) {
3528 const auto r = m_currentAutobraceRange->toRange();
3531 del(view, view->cursorPosition());
3532 m_currentAutobraceRange.reset();
3537void KTextEditor::DocumentPrivate::del(KTextEditor::ViewPrivate *view,
const KTextEditor::Cursor c)
3539 if (!view->config()->persistentSelection() && view->selection()) {
3542 if (view->blockSelection() && toVirtualColumn(range.
start()) == toVirtualColumn(range.
end())) {
3545 view->setSelection(range);
3547 view->removeSelectedText();
3552 if (c.
column() < m_buffer->lineLength(c.
line())) {
3555 }
else if (c.
line() < lastLine()) {
3560bool KTextEditor::DocumentPrivate::multiPaste(KTextEditor::ViewPrivate *view,
const QStringList &texts)
3562 if (texts.
isEmpty() || view->isMulticursorNotAllowed() || view->secondaryCursors().size() + 1 != (
size_t)texts.
size()) {
3566 m_undoManager->undoSafePoint();
3569 if (view->selection()) {
3570 view->removeSelectedText();
3573 auto plainSecondaryCursors = view->plainSecondaryCursors();
3574 KTextEditor::ViewPrivate::PlainSecondaryCursor primary;
3575 primary.pos = view->cursorPosition();
3576 primary.range = view->selectionRange();
3577 plainSecondaryCursors.append(primary);
3578 std::sort(plainSecondaryCursors.begin(), plainSecondaryCursors.end());
3582 for (
int i = texts.
size() - 1; i >= 0; --i) {
3584 text.
replace(re, QStringLiteral(
"\n"));
3587 insertText(pos, text,
false);
3595void KTextEditor::DocumentPrivate::paste(KTextEditor::ViewPrivate *view,
const QString &text)
3608 const bool isSingleLine = lines == 0;
3610 m_undoManager->undoSafePoint();
3616 bool skipIndentOnPaste =
false;
3618 const int length = lineLength(pos.
line());
3620 skipIndentOnPaste = length > 0;
3623 if (!view->config()->persistentSelection() && view->selection()) {
3624 pos = view->selectionRange().
start();
3625 if (view->blockSelection()) {
3626 pos = rangeOnLine(view->selectionRange(), pos.
line()).
start();
3633 view->removeSelectedText();
3636 if (config()->ovr()) {
3639 if (!view->blockSelection()) {
3640 int endColumn = (pasteLines.count() == 1 ? pos.
column() : 0) + pasteLines.last().length();
3643 int maxi = qMin(pos.
line() + pasteLines.count(), this->lines());
3645 for (
int i = pos.
line(); i < maxi; ++i) {
3646 int pasteLength = pasteLines.at(i - pos.
line()).length();
3652 insertText(pos, s, view->blockSelection());
3659 if (view->blockSelection()) {
3660 view->setCursorPositionInternal(pos);
3663 if (config()->indentPastedText()) {
3665 if (!skipIndentOnPaste) {
3666 m_indenter->indent(view, range);
3670 if (!view->blockSelection()) {
3671 Q_EMIT charactersSemiInteractivelyInserted(pos, s);
3673 m_undoManager->undoSafePoint();
3678 if (!isReadWrite()) {
3683 m_indenter->changeIndent(range, change);
3687void KTextEditor::DocumentPrivate::align(KTextEditor::ViewPrivate *view,
KTextEditor::Range range)
3689 m_indenter->indent(view, range);
3696 if (lines.
size() < 2) {
3702 int selectionStartColumn = range.
start().
column();
3704 for (
const auto &line : lines) {
3706 if (!
match.hasMatch()) {
3707 patternStartColumns.
append(-1);
3708 }
else if (
match.lastCapturedIndex() == 0) {
3709 patternStartColumns.
append(
match.capturedStart(0) + (blockwise ? selectionStartColumn : 0));
3711 patternStartColumns.
append(
match.capturedStart(1) + (blockwise ? selectionStartColumn : 0));
3714 if (!blockwise && patternStartColumns[0] != -1) {
3715 patternStartColumns[0] += selectionStartColumn;
3718 int maxColumn = *std::max_element(patternStartColumns.
cbegin(), patternStartColumns.
cend());
3721 for (
int i = 0; i < lines.
size(); ++i) {
3722 if (patternStartColumns[i] != -1) {
3729void KTextEditor::DocumentPrivate::insertTab(KTextEditor::ViewPrivate *view,
const KTextEditor::Cursor)
3731 if (!isReadWrite()) {
3735 int lineLen = line(view->cursorPosition().
line()).length();
3740 if (!view->config()->persistentSelection() && view->selection()) {
3741 view->removeSelectedText();
3742 }
else if (view->currentInputMode()->overwrite() && c.
column() < lineLen) {
3747 view->currentInputMode()->overwrittenChar(removed);
3751 c = view->cursorPosition();
3752 editInsertText(c.
line(), c.
column(), QStringLiteral(
"\t"));
3761bool KTextEditor::DocumentPrivate::removeStringFromBeginning(
int line,
const QString &str)
3785bool KTextEditor::DocumentPrivate::removeStringFromEnd(
int line,
const QString &str)
3790 bool there = textline.
endsWith(str);
3812 const bool replacetabs = config()->replaceTabsDyn();
3816 const int indentWidth = config()->indentationWidth();
3819 int column = cursorPos.
column();
3825 for (
const QChar ch : str) {
3826 if (ch == tabChar) {
3829 int spacesToInsert = indentWidth - (column % indentWidth);
3831 column += spacesToInsert;
3844void KTextEditor::DocumentPrivate::addStartLineCommentToSingleLine(
int line,
int attrib)
3846 const QString commentLineMark = highlight()->getCommentSingleLineStart(attrib) +
QLatin1Char(
' ');
3849 if (highlight()->getCommentSingleLinePosition(attrib) == KSyntaxHighlighting::CommentPosition::AfterWhitespace) {
3860bool KTextEditor::DocumentPrivate::removeStartLineCommentFromSingleLine(
int line,
int attrib)
3862 const QString shortCommentMark = highlight()->getCommentSingleLineStart(attrib);
3868 bool removed = (removeStringFromBeginning(line, longCommentMark) || removeStringFromBeginning(line, shortCommentMark));
3879void KTextEditor::DocumentPrivate::addStartStopCommentToSingleLine(
int line,
int attrib)
3881 const QString startCommentMark = highlight()->getCommentStart(attrib) +
QLatin1Char(
' ');
3882 const QString stopCommentMark =
QLatin1Char(
' ') + highlight()->getCommentEnd(attrib);
3890 const int col = m_buffer->lineLength(line);
3902bool KTextEditor::DocumentPrivate::removeStartStopCommentFromSingleLine(
int line,
int attrib)
3904 const QString shortStartCommentMark = highlight()->getCommentStart(attrib);
3906 const QString shortStopCommentMark = highlight()->getCommentEnd(attrib);
3912 const bool removedStart = (removeStringFromBeginning(line, longStartCommentMark) || removeStringFromBeginning(line, shortStartCommentMark));
3915 const bool removedStop = removedStart && (removeStringFromEnd(line, longStopCommentMark) || removeStringFromEnd(line, shortStopCommentMark));
3919 return (removedStart || removedStop);
3926void KTextEditor::DocumentPrivate::addStartStopCommentToSelection(
KTextEditor::Range selection,
bool blockSelection,
int attrib)
3928 const QString startComment = highlight()->getCommentStart(attrib);
3929 const QString endComment = highlight()->getCommentEnd(attrib);
3939 if (!blockSelection) {
3940 insertText(range.
end(), endComment);
3941 insertText(range.
start(), startComment);
3943 for (
int line = range.
start().
line(); line <= range.
end().
line(); line++) {
3945 insertText(subRange.
end(), endComment);
3946 insertText(subRange.
start(), startComment);
3957void KTextEditor::DocumentPrivate::addStartLineCommentToSelection(
KTextEditor::Range selection,
int attrib)
3960 int el = selection.
end().
line();
3963 if ((selection.
end().
column() == 0) && (el > 0)) {
3967 if (sl < 0 || el < 0 || sl >= lines() || el >= lines()) {
3973 const QString commentLineMark = highlight()->getCommentSingleLineStart(attrib) +
QLatin1Char(
' ');
3976 if (highlight()->getCommentSingleLinePosition(attrib) == KSyntaxHighlighting::CommentPosition::AfterWhitespace) {
3984 col = std::numeric_limits<int>::max();
3986 for (
int l = el; l >= sl; l--) {
3987 const auto line = plainKateTextLine(l);
3988 if (line.length() == 0) {
3991 col = qMin(col, qMax(0, line.firstChar()));
3998 if (col == std::numeric_limits<int>::max()) {
4005 for (
int l = el; l >= sl; l--) {
4012bool KTextEditor::DocumentPrivate::nextNonSpaceCharPos(
int &line,
int &col)
4014 for (; line >= 0 && line < m_buffer->lines(); line++) {
4028bool KTextEditor::DocumentPrivate::previousNonSpaceCharPos(
int &line,
int &col)
4030 while (line >= 0 && line < m_buffer->lines()) {
4052bool KTextEditor::DocumentPrivate::removeStartStopCommentFromSelection(
KTextEditor::Range selection,
int attrib)
4054 const QString startComment = highlight()->getCommentStart(attrib);
4055 const QString endComment = highlight()->getCommentEnd(attrib);
4057 int sl = qMax<int>(0, selection.
start().
line());
4058 int el = qMin<int>(selection.
end().
line(), lastLine());
4065 }
else if (el > 0) {
4067 ec = m_buffer->lineLength(el) - 1;
4070 const int startCommentLen = startComment.
length();
4071 const int endCommentLen = endComment.
length();
4075 bool remove = nextNonSpaceCharPos(sl, sc) && m_buffer->plainLine(sl).matchesAt(sc, startComment) && previousNonSpaceCharPos(el, ec)
4076 && ((ec - endCommentLen + 1) >= 0) && m_buffer->plainLine(el).matchesAt(ec - endCommentLen + 1, endComment);
4093 const QString startComment = highlight()->getCommentStart(attrib);
4094 const QString endComment = highlight()->getCommentEnd(attrib);
4095 const int startCommentLen = startComment.
length();
4096 const int endCommentLen = endComment.
length();
4098 const bool remove = m_buffer->plainLine(
start.line()).matchesAt(
start.column(), startComment)
4099 && m_buffer->plainLine(
end.line()).matchesAt(
end.column() - endCommentLen, endComment);
4113bool KTextEditor::DocumentPrivate::removeStartLineCommentFromSelection(
KTextEditor::Range selection,
int attrib,
bool toggleComment)
4115 const QString shortCommentMark = highlight()->getCommentSingleLineStart(attrib);
4118 const int startLine = selection.
start().
line();
4119 int endLine = selection.
end().
line();
4121 if ((selection.
end().
column() == 0) && (endLine > 0)) {
4125 bool removed =
false;
4131 if (toggleComment) {
4132 bool allLinesAreCommented =
true;
4133 for (
int line = endLine; line >= startLine; line--) {
4134 const auto ln = m_buffer->plainLine(line);
4135 const QString &text = ln.text();
4142 textView = textView.trimmed();
4143 if (!textView.startsWith(shortCommentMark) && !textView.startsWith(longCommentMark)) {
4144 allLinesAreCommented =
false;
4148 if (!allLinesAreCommented) {
4156 for (
int z = endLine; z >= startLine; z--) {
4158 removed = (removeStringFromBeginning(z, longCommentMark) || removeStringFromBeginning(z, shortCommentMark) || removed);
4169 const bool hasSelection = !selection.
isEmpty();
4170 int selectionCol = 0;
4175 const int line = c.
line();
4177 int startAttrib = 0;
4180 if (selectionCol < ln.
length()) {
4181 startAttrib = ln.
attribute(selectionCol);
4186 bool hasStartLineCommentMark = !(highlight()->getCommentSingleLineStart(startAttrib).isEmpty());
4187 bool hasStartStopCommentMark = (!(highlight()->getCommentStart(startAttrib).isEmpty()) && !(highlight()->getCommentEnd(startAttrib).isEmpty()));
4189 if (changeType == Comment) {
4190 if (!hasSelection) {
4191 if (hasStartLineCommentMark) {
4192 addStartLineCommentToSingleLine(line, startAttrib);
4193 }
else if (hasStartStopCommentMark) {
4194 addStartStopCommentToSingleLine(line, startAttrib);
4205 if (hasStartStopCommentMark
4206 && (!hasStartLineCommentMark
4209 addStartStopCommentToSelection(selection, blockSelect, startAttrib);
4210 }
else if (hasStartLineCommentMark) {
4211 addStartLineCommentToSelection(selection, startAttrib);
4215 bool removed =
false;
4216 const bool toggleComment = changeType == ToggleComment;
4217 if (!hasSelection) {
4218 removed = (hasStartLineCommentMark && removeStartLineCommentFromSingleLine(line, startAttrib))
4219 || (hasStartStopCommentMark && removeStartStopCommentFromSingleLine(line, startAttrib));
4222 removed = (hasStartStopCommentMark && removeStartStopCommentFromSelection(selection, startAttrib))
4223 || (hasStartLineCommentMark && removeStartLineCommentFromSelection(selection, startAttrib, toggleComment));
4227 if (!removed && toggleComment) {
4228 commentSelection(selection, c, blockSelect, Comment);
4237void KTextEditor::DocumentPrivate::comment(KTextEditor::ViewPrivate *v, uint line, uint column, CommentType change)
4240 const bool skipWordWrap = wordWrap();
4247 if (v->selection()) {
4248 const auto &cursors = v->secondaryCursors();
4249 for (
const auto &c : cursors) {
4253 commentSelection(c.range->toRange(), c.cursor(),
false, change);
4256 commentSelection(v->selectionRange(), c, v->blockSelection(), change);
4258 const auto &cursors = v->secondaryCursors();
4259 for (
const auto &c : cursors) {
4260 commentSelection({}, c.cursor(),
false, change);
4272void KTextEditor::DocumentPrivate::transformCursorOrRange(KTextEditor::ViewPrivate *v,
4275 KTextEditor::DocumentPrivate::TextTransform t)
4277 if (v->selection()) {
4289 if (range.
start().
line() == selection.
end().
line() || v->blockSelection()) {
4294 int swapCol =
start;
4304 if (t == Uppercase) {
4307 }
else if (t == Lowercase) {
4320 && !highlight()->isInWord(l.
at(range.
start().
column() - 1)))
4321 || (p && !highlight()->isInWord(s.
at(p - 1)))) {
4330 insertText(range.
start(), s);
4365 insertText(cursor, s);
4371void KTextEditor::DocumentPrivate::transform(KTextEditor::ViewPrivate *v,
const KTextEditor::Cursor c, KTextEditor::DocumentPrivate::TextTransform t)
4375 if (v->selection()) {
4376 const auto &cursors = v->secondaryCursors();
4377 for (
const auto &c : cursors) {
4381 auto pos = c.pos->toCursor();
4382 transformCursorOrRange(v, c.anchor, c.range->toRange(), t);
4386 const auto selRange = v->selectionRange();
4387 transformCursorOrRange(v, c, v->selectionRange(), t);
4388 v->setSelection(selRange);
4389 v->setCursorPosition(c);
4391 const auto &secondaryCursors = v->secondaryCursors();
4392 for (
const auto &c : secondaryCursors) {
4393 transformCursorOrRange(v, c.cursor(), {}, t);
4395 transformCursorOrRange(v, c, {}, t);
4401void KTextEditor::DocumentPrivate::joinLines(uint first, uint last)
4406 while (first < last) {
4407 if (line >= lines() || line + 1 >= lines()) {
4423 editRemoveText(line + 1, 0, pos);
4426 editInsertText(line + 1, 0, QStringLiteral(
" "));
4430 editRemoveText(line + 1, 0, tl.
length());
4433 editUnWrapLine(line);
4441 for (
auto view :
std::as_const(m_views)) {
4442 static_cast<ViewPrivate *
>(view)->tagLines(lineRange,
true);
4446void KTextEditor::DocumentPrivate::tagLine(
int line)
4448 tagLines({line, line});
4451void KTextEditor::DocumentPrivate::repaintViews(
bool paintOnlyDirty)
4453 for (
auto view :
std::as_const(m_views)) {
4454 static_cast<ViewPrivate *
>(view)->repaintText(paintOnlyDirty);
4467 if (maxLines < 0 ||
start.line() < 0 ||
start.line() >= lines()) {
4477 if (config()->ovr()) {
4478 if (isBracket(right)) {
4483 }
else if (isBracket(right)) {
4485 }
else if (isBracket(left)) {
4492 const QChar opposite = matchingBracket(bracket);
4497 const int searchDir = isStartBracket(bracket) ? 1 : -1;
4500 const int minLine = qMax(range.
start().
line() - maxLines, 0);
4501 const int maxLine = qMin(range.
start().
line() + maxLines, documentEnd().line());
4506 int validAttr = kateTextLine(cursor.
line()).attribute(cursor.
column());
4508 while (cursor.
line() >= minLine && cursor.
line() <= maxLine) {
4509 if (!cursor.move(searchDir)) {
4517 if (c == opposite) {
4519 if (searchDir > 0) {
4520 range.
setEnd(cursor.toCursor());
4527 }
else if (c == bracket) {
4543void KTextEditor::DocumentPrivate::updateDocName()
4546 if (!url().isEmpty() && (m_docName == removeNewLines(url().fileName()) || m_docName.
startsWith(removeNewLines(url().fileName()) +
QLatin1String(
" (")))) {
4552 std::vector<KTextEditor::DocumentPrivate *> docsWithSameName;
4556 auto doc =
static_cast<KTextEditor::DocumentPrivate *
>(kteDoc);
4557 if ((doc !=
this) && (doc->url().fileName() == url().
fileName())) {
4558 if (doc->m_docNameNumber > count) {
4559 count = doc->m_docNameNumber;
4561 docsWithSameName.push_back(doc);
4565 m_docNameNumber = count + 1;
4568 m_docName = removeNewLines(url().fileName());
4570 m_isUntitled = m_docName.isEmpty();
4572 if (!m_isUntitled && !docsWithSameName.empty()) {
4573 docsWithSameName.push_back(
this);
4574 uniquifyDocNames(docsWithSameName);
4579 m_docName =
i18n(
"Untitled");
4582 if (m_docNameNumber > 0) {
4587 if (oldName != m_docName) {
4588 Q_EMIT documentNameChanged(
this);
4603static QString shortestPrefix(
const std::vector<QString> &urls, KTextEditor::DocumentPrivate *doc)
4606 int lastSlash = url.lastIndexOf(
QLatin1Char(
'/'));
4607 if (lastSlash == -1) {
4611 int fileNameStart = lastSlash;
4614 lastSlash = url.lastIndexOf(
QLatin1Char(
'/'), lastSlash);
4615 if (lastSlash == -1) {
4618 return url.mid(lastSlash, fileNameStart);
4623 urlv = urlv.
mid(lastSlash);
4625 for (
size_t i = 0; i < urls.size(); ++i) {
4626 if (urls[i] == url) {
4630 if (urls[i].endsWith(urlv)) {
4631 lastSlash = url.lastIndexOf(
QLatin1Char(
'/'), lastSlash - 1);
4632 if (lastSlash <= 0) {
4634 return url.mid(0, fileNameStart);
4637 urlv = urlView.
mid(lastSlash);
4642 return url.mid(lastSlash + 1, fileNameStart - (lastSlash + 1));
4645void KTextEditor::DocumentPrivate::uniquifyDocNames(
const std::vector<KTextEditor::DocumentPrivate *> &docs)
4647 std::vector<QString> paths;
4648 paths.reserve(docs.size());
4649 std::transform(docs.begin(), docs.end(), std::back_inserter(paths), [](
const KTextEditor::DocumentPrivate *d) {
4650 return d->url().toString(QUrl::NormalizePathSegments | QUrl::PreferLocalFile);
4653 for (
const auto doc : docs) {
4654 const QString prefix = shortestPrefix(paths, doc);
4655 const QString fileName = doc->url().fileName();
4656 const QString oldName = doc->m_docName;
4659 doc->m_docName = fileName + QStringLiteral(
" - ") + prefix;
4661 doc->m_docName = fileName;
4664 if (doc->m_docName != oldName) {
4665 Q_EMIT doc->documentNameChanged(doc);
4672 if (url().isEmpty() || !m_modOnHd) {
4676 if (!isModified() && isAutoReload()) {
4677 onModOnHdAutoReload();
4681 if (!m_fileChangedDialogsActivated || m_modOnHdHandler) {
4686 if (m_modOnHdReason == m_prevModOnHdReason) {
4689 m_prevModOnHdReason = m_modOnHdReason;
4691 m_modOnHdHandler =
new KateModOnHdPrompt(
this, m_modOnHdReason, reasonedMOHString());
4692 connect(m_modOnHdHandler.data(), &KateModOnHdPrompt::saveAsTriggered,
this, &DocumentPrivate::onModOnHdSaveAs);
4693 connect(m_modOnHdHandler.data(), &KateModOnHdPrompt::closeTriggered,
this, &DocumentPrivate::onModOnHdClose);
4694 connect(m_modOnHdHandler.data(), &KateModOnHdPrompt::reloadTriggered,
this, &DocumentPrivate::onModOnHdReload);
4695 connect(m_modOnHdHandler.data(), &KateModOnHdPrompt::autoReloadTriggered,
this, &DocumentPrivate::onModOnHdAutoReload);
4696 connect(m_modOnHdHandler.data(), &KateModOnHdPrompt::ignoreTriggered,
this, &DocumentPrivate::onModOnHdIgnore);
4699void KTextEditor::DocumentPrivate::onModOnHdSaveAs()
4702 const QUrl res = getSaveFileUrl(
i18n(
"Save File"));
4708 delete m_modOnHdHandler;
4709 m_prevModOnHdReason = OnDiskUnmodified;
4710 Q_EMIT modifiedOnDisk(
this,
false, OnDiskUnmodified);
4717void KTextEditor::DocumentPrivate::onModOnHdClose()
4720 m_fileChangedDialogsActivated =
false;
4733void KTextEditor::DocumentPrivate::onModOnHdReload()
4736 m_prevModOnHdReason = OnDiskUnmodified;
4737 Q_EMIT modifiedOnDisk(
this,
false, OnDiskUnmodified);
4743 m_undoManager->clearUndo();
4744 m_undoManager->clearRedo();
4747 delete m_modOnHdHandler;
4750void KTextEditor::DocumentPrivate::autoReloadToggled(
bool b)
4752 m_autoReloadMode->setChecked(b);
4756 disconnect(&m_modOnHdTimer, &
QTimer::timeout,
this, &DocumentPrivate::onModOnHdAutoReload);
4760bool KTextEditor::DocumentPrivate::isAutoReload()
4762 return m_autoReloadMode->isChecked();
4765void KTextEditor::DocumentPrivate::delayAutoReload()
4767 if (isAutoReload()) {
4768 m_autoReloadThrottle.start();
4772void KTextEditor::DocumentPrivate::onModOnHdAutoReload()
4774 if (m_modOnHdHandler) {
4775 delete m_modOnHdHandler;
4776 autoReloadToggled(
true);
4779 if (!isAutoReload()) {
4783 if (m_modOnHd && !m_reloading && !m_autoReloadThrottle.isActive()) {
4785 m_prevModOnHdReason = OnDiskUnmodified;
4786 Q_EMIT modifiedOnDisk(
this,
false, OnDiskUnmodified);
4790 m_undoManager->clearUndo();
4791 m_undoManager->clearRedo();
4794 m_autoReloadThrottle.start();
4798void KTextEditor::DocumentPrivate::onModOnHdIgnore()
4801 delete m_modOnHdHandler;
4804void KTextEditor::DocumentPrivate::setModifiedOnDisk(ModifiedOnDiskReason reason)
4806 m_modOnHdReason = reason;
4807 m_modOnHd = (reason != OnDiskUnmodified);
4808 Q_EMIT modifiedOnDisk(
this, (reason != OnDiskUnmodified), reason);
4811class KateDocumentTmpMark
4818void KTextEditor::DocumentPrivate::setModifiedOnDiskWarning(
bool on)
4820 m_fileChangedDialogsActivated = on;
4823bool KTextEditor::DocumentPrivate::documentReload()
4825 if (url().isEmpty()) {
4837 m_undoManager->clearUndo();
4838 m_undoManager->clearRedo();
4843 delete m_modOnHdHandler;
4845 Q_EMIT aboutToReload(
this);
4849 std::transform(m_marks.cbegin(), m_marks.cend(), std::back_inserter(tmp), [
this](
KTextEditor::Mark *mark) {
4850 return KateDocumentTmpMark{.line=line(mark->line), .mark=*mark};
4854 const QString oldMode = mode();
4855 const bool modeByUser = m_fileTypeSetByUser;
4856 const QString oldHlMode = highlightingMode();
4857 const bool hlByUser = m_hlSetByUser;
4859 m_storedVariables.clear();
4863 std::transform(m_views.cbegin(), m_views.cend(), std::back_inserter(cursorPositions), [](
KTextEditor::View *v) {
4864 return std::pair<KTextEditor::ViewPrivate *, KTextEditor::Cursor>(static_cast<ViewPrivate *>(v), v->cursorPosition());
4869 for (
auto *view : m_views) {
4870 static_cast<ViewPrivate *
>(view)->clearSecondaryCursors();
4873 static_cast<ViewPrivate *
>(view)->clearFoldingState();
4878 KTextEditor::DocumentPrivate::openUrl(url());
4881 m_userSetEncodingForNextReload =
false;
4884 for (
auto v :
std::as_const(m_views)) {
4886 auto it = std::find_if(cursorPositions.
cbegin(), cursorPositions.
cend(), [v](
const std::pair<KTextEditor::ViewPrivate *, KTextEditor::Cursor> &p) {
4887 return p.first == v;
4889 v->setCursorPosition(it->second);
4895 const int lines = this->lines();
4896 for (
const auto &tmpMark : tmp) {
4897 if (tmpMark.mark.line < lines) {
4898 if (tmpMark.line == line(tmpMark.mark.line)) {
4899 setMark(tmpMark.mark.line, tmpMark.mark.type);
4906 updateFileType(oldMode,
true);
4909 setHighlightingMode(oldHlMode);
4912 Q_EMIT reloaded(
this);
4917bool KTextEditor::DocumentPrivate::documentSave()
4919 if (!url().
isValid() || !isReadWrite()) {
4920 return documentSaveAs();
4926bool KTextEditor::DocumentPrivate::documentSaveAs()
4928 const QUrl saveUrl = getSaveFileUrl(
i18n(
"Save File"));
4936bool KTextEditor::DocumentPrivate::documentSaveAsWithEncoding(
const QString &encoding)
4938 const QUrl saveUrl = getSaveFileUrl(
i18n(
"Save File"));
4943 setEncoding(encoding);
4947void KTextEditor::DocumentPrivate::documentSaveCopyAs()
4949 const QUrl saveUrl = getSaveFileUrl(
i18n(
"Save Copy of File"));
4955 if (!file->
open()) {
4959 if (!m_buffer->saveFile(file->
fileName())) {
4961 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 "
4962 "that enough disk space is available.",
4970 const auto url = this->url();
4972 if (
auto sj = qobject_cast<KIO::StatJob *>(j)) {
4983void KTextEditor::DocumentPrivate::setWordWrap(
bool on)
4985 config()->setWordWrap(on);
4988bool KTextEditor::DocumentPrivate::wordWrap()
const
4990 return config()->wordWrap();
4993void KTextEditor::DocumentPrivate::setWordWrapAt(uint col)
4995 config()->setWordWrapAt(col);
4998unsigned int KTextEditor::DocumentPrivate::wordWrapAt()
const
5000 return config()->wordWrapAt();
5003void KTextEditor::DocumentPrivate::setPageUpDownMovesCursor(
bool on)
5005 config()->setPageUpDownMovesCursor(on);
5008bool KTextEditor::DocumentPrivate::pageUpDownMovesCursor()
const
5010 return config()->pageUpDownMovesCursor();
5014bool KTextEditor::DocumentPrivate::setEncoding(
const QString &e)
5016 return m_config->setEncoding(e);
5019QString KTextEditor::DocumentPrivate::encoding()
const
5021 return m_config->encoding();
5024void KTextEditor::DocumentPrivate::updateConfig()
5026 m_undoManager->updateConfig();
5029 m_indenter->setMode(m_config->indentationMode());
5030 m_indenter->updateConfig();
5033 m_buffer->setTabWidth(config()->tabWidth());
5036 for (
auto view :
std::as_const(m_views)) {
5037 static_cast<ViewPrivate *
>(view)->updateDocumentConfig();
5041 if (m_onTheFlyChecker) {
5042 m_onTheFlyChecker->updateConfig();
5045 if (config()->autoSave()) {
5046 int interval = config()->autoSaveInterval();
5047 if (interval == 0) {
5048 m_autoSaveTimer.stop();
5050 m_autoSaveTimer.setInterval(interval * 1000);
5052 m_autoSaveTimer.start();
5057 Q_EMIT configChanged(
this);
5067bool KTextEditor::DocumentPrivate::readVariables(
bool onlyViewAndRenderer)
5069 const bool hasVariableline = [
this] {
5072 for (
int i = qMax(10, lines() - 10); i < lines(); ++i) {
5073 if (line(i).contains(s)) {
5078 for (
int i = 0; i < qMin(9, lines()); ++i) {
5079 if (line(i).contains(s)) {
5085 if (!hasVariableline) {
5089 if (!onlyViewAndRenderer) {
5090 m_config->configStart();
5094 for (
auto view :
std::as_const(m_views)) {
5095 auto v =
static_cast<ViewPrivate *
>(view);
5100 for (
int i = 0; i < qMin(9, lines()); ++i) {
5101 readVariableLine(line(i), onlyViewAndRenderer);
5104 for (
int i = qMax(10, lines() - 10); i < lines(); i++) {
5105 readVariableLine(line(i), onlyViewAndRenderer);
5109 if (!onlyViewAndRenderer) {
5110 m_config->configEnd();
5113 for (
auto view :
std::as_const(m_views)) {
5114 auto v =
static_cast<ViewPrivate *
>(view);
5121void KTextEditor::DocumentPrivate::readVariableLine(
const QString &t,
bool onlyViewAndRenderer)
5124 static const QRegularExpression kvLineWildcard(QStringLiteral(
"kate-wildcard\\((.*)\\):(.*)"));
5125 static const QRegularExpression kvLineMime(QStringLiteral(
"kate-mimetype\\((.*)\\):(.*)"));
5138 auto match = kvLine.match(t);
5139 if (
match.hasMatch()) {
5140 s =
match.captured(1);
5143 }
else if ((match = kvLineWildcard.match(t)).hasMatch()) {
5149 for (
const QString &pattern : wildcards) {
5156 found = wildcard.
match(matchPath ? pathOfFile : nameOfFile).hasMatch();
5167 s =
match.captured(2);
5170 }
else if ((match = kvLineMime.match(t)).hasMatch()) {
5178 s =
match.captured(2);
5186 static const auto vvl = {
5210 int spaceIndent = -1;
5211 bool replaceTabsSet =
false;
5216 while ((match = kvVar.match(s, startPos)).hasMatch()) {
5217 startPos =
match.capturedEnd(0);
5218 var =
match.captured(1);
5219 val =
match.captured(2).trimmed();
5224 if (onlyViewAndRenderer) {
5225 if (contains(vvl, var)) {
5226 setViewVariable(var, val);
5230 if (var ==
QLatin1String(
"word-wrap") && checkBoolValue(val, &state)) {
5235 else if (var ==
QLatin1String(
"backspace-indents") && checkBoolValue(val, &state)) {
5236 m_config->setBackspaceIndents(state);
5237 }
else if (var ==
QLatin1String(
"indent-pasted-text") && checkBoolValue(val, &state)) {
5238 m_config->setIndentPastedText(state);
5239 }
else if (var ==
QLatin1String(
"replace-tabs") && checkBoolValue(val, &state)) {
5240 m_config->setReplaceTabsDyn(state);
5241 replaceTabsSet =
true;
5242 }
else if (var ==
QLatin1String(
"remove-trailing-space") && checkBoolValue(val, &state)) {
5243 qCWarning(LOG_KTE) <<
i18n(
5244 "Using deprecated modeline 'remove-trailing-space'. "
5245 "Please replace with 'remove-trailing-spaces modified;', see "
5246 "https://docs.kde.org/?application=katepart&branch=stable5&path=config-variables.html#variable-remove-trailing-spaces");
5247 m_config->setRemoveSpaces(state ? 1 : 0);
5248 }
else if (var ==
QLatin1String(
"replace-trailing-space-save") && checkBoolValue(val, &state)) {
5249 qCWarning(LOG_KTE) <<
i18n(
5250 "Using deprecated modeline 'replace-trailing-space-save'. "
5251 "Please replace with 'remove-trailing-spaces all;', see "
5252 "https://docs.kde.org/?application=katepart&branch=stable5&path=config-variables.html#variable-remove-trailing-spaces");
5253 m_config->setRemoveSpaces(state ? 2 : 0);
5254 }
else if (var ==
QLatin1String(
"overwrite-mode") && checkBoolValue(val, &state)) {
5255 m_config->setOvr(state);
5256 }
else if (var ==
QLatin1String(
"keep-extra-spaces") && checkBoolValue(val, &state)) {
5257 m_config->setKeepExtraSpaces(state);
5258 }
else if (var ==
QLatin1String(
"tab-indents") && checkBoolValue(val, &state)) {
5259 m_config->setTabIndents(state);
5260 }
else if (var ==
QLatin1String(
"show-tabs") && checkBoolValue(val, &state)) {
5261 m_config->setShowTabs(state);
5262 }
else if (var ==
QLatin1String(
"show-trailing-spaces") && checkBoolValue(val, &state)) {
5263 m_config->setShowSpaces(state ? KateDocumentConfig::Trailing : KateDocumentConfig::
None);
5264 }
else if (var ==
QLatin1String(
"space-indent") && checkBoolValue(val, &state)) {
5266 spaceIndent = state;
5267 }
else if (var ==
QLatin1String(
"smart-home") && checkBoolValue(val, &state)) {
5268 m_config->setSmartHome(state);
5269 }
else if (var ==
QLatin1String(
"newline-at-eof") && checkBoolValue(val, &state)) {
5270 m_config->setNewLineAtEof(state);
5274 else if (var ==
QLatin1String(
"tab-width") && checkIntValue(val, &n)) {
5275 m_config->setTabWidth(n);
5276 }
else if (var ==
QLatin1String(
"indent-width") && checkIntValue(val, &n)) {
5277 m_config->setIndentationWidth(n);
5279 m_config->setIndentationMode(val);
5280 }
else if (var ==
QLatin1String(
"word-wrap-column") && checkIntValue(val, &n) && n > 0) {
5281 m_config->setWordWrapAt(n);
5287 if ((n = indexOf(l, val.
toLower())) != -1) {
5290 m_config->setEol(n);
5291 m_config->setAllowEolDetection(
false);
5294 if (checkBoolValue(val, &state)) {
5295 m_config->setBom(state);
5297 }
else if (var ==
QLatin1String(
"remove-trailing-spaces")) {
5300 m_config->setRemoveSpaces(1);
5302 m_config->setRemoveSpaces(2);
5304 m_config->setRemoveSpaces(0);
5307 setHighlightingMode(val);
5313 setDefaultDictionary(val);
5314 }
else if (var ==
QLatin1String(
"automatic-spell-checking") && checkBoolValue(val, &state)) {
5315 onTheFlySpellCheckingEnabled(state);
5319 else if (contains(vvl, var)) {
5320 setViewVariable(var, val);
5322 m_storedVariables[var] = val;
5334 if (!replaceTabsSet && spaceIndent >= 0) {
5335 m_config->setReplaceTabsDyn(spaceIndent > 0);
5339void KTextEditor::DocumentPrivate::setViewVariable(
const QString &var,
const QString &val)
5344 for (
auto view :
std::as_const(m_views)) {
5345 auto v =
static_cast<ViewPrivate *
>(view);
5348 if (checkBoolValue(val, &state)) {
5351 if (v->config()->
setValue(var, help)) {
5352 }
else if (v->rendererConfig()->
setValue(var, help)) {
5354 }
else if (var ==
QLatin1String(
"dynamic-word-wrap") && checkBoolValue(val, &state)) {
5355 v->config()->setDynWordWrap(state);
5356 }
else if (var ==
QLatin1String(
"block-selection") && checkBoolValue(val, &state)) {
5357 v->setBlockSelection(state);
5360 }
else if (var ==
QLatin1String(
"icon-bar-color") && checkColorValue(val, c)) {
5361 v->rendererConfig()->setIconBarColor(c);
5364 else if (var ==
QLatin1String(
"background-color") && checkColorValue(val, c)) {
5365 v->rendererConfig()->setBackgroundColor(c);
5366 }
else if (var ==
QLatin1String(
"selection-color") && checkColorValue(val, c)) {
5367 v->rendererConfig()->setSelectionColor(c);
5368 }
else if (var ==
QLatin1String(
"current-line-color") && checkColorValue(val, c)) {
5369 v->rendererConfig()->setHighlightedLineColor(c);
5370 }
else if (var ==
QLatin1String(
"bracket-highlight-color") && checkColorValue(val, c)) {
5371 v->rendererConfig()->setHighlightedBracketColor(c);
5372 }
else if (var ==
QLatin1String(
"word-wrap-marker-color") && checkColorValue(val, c)) {
5373 v->rendererConfig()->setWordWrapMarkerColor(c);
5379 _f.setFixedPitch(
QFont(val).fixedPitch());
5384 v->rendererConfig()->setFont(_f);
5386 v->rendererConfig()->setSchema(val);
5391bool KTextEditor::DocumentPrivate::checkBoolValue(
QString val,
bool *result)
5395 if (contains(trueValues, val)) {
5401 if (contains(falseValues, val)) {
5408bool KTextEditor::DocumentPrivate::checkIntValue(
const QString &val,
int *result)
5411 *result = val.
toInt(&ret);
5415bool KTextEditor::DocumentPrivate::checkColorValue(
const QString &val,
QColor &c)
5422QString KTextEditor::DocumentPrivate::variable(
const QString &name)
const
5424 auto it = m_storedVariables.find(name);
5425 if (it == m_storedVariables.end()) {
5431void KTextEditor::DocumentPrivate::setVariable(
const QString &name,
const QString &value)
5433 QString s = QStringLiteral(
"kate: ");
5437 readVariableLine(s);
5442void KTextEditor::DocumentPrivate::slotModOnHdDirty(
const QString &path)
5444 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskModified)) {
5446 m_modOnHdReason = OnDiskModified;
5448 if (!m_modOnHdTimer.isActive()) {
5449 m_modOnHdTimer.start();
5454void KTextEditor::DocumentPrivate::slotModOnHdCreated(
const QString &path)
5456 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskCreated)) {
5458 m_modOnHdReason = OnDiskCreated;
5460 if (!m_modOnHdTimer.isActive()) {
5461 m_modOnHdTimer.start();
5466void KTextEditor::DocumentPrivate::slotModOnHdDeleted(
const QString &path)
5468 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskDeleted)) {
5470 m_modOnHdReason = OnDiskDeleted;
5472 if (!m_modOnHdTimer.isActive()) {
5473 m_modOnHdTimer.start();
5478void KTextEditor::DocumentPrivate::slotDelayedHandleModOnHd()
5482 if (!oldDigest.
isEmpty() && !url().isEmpty() && url().isLocalFile()) {
5484 if (m_modOnHdReason != OnDiskDeleted && m_modOnHdReason != OnDiskCreated && createDigest() && oldDigest == checksum()) {
5486 m_modOnHdReason = OnDiskUnmodified;
5487 m_prevModOnHdReason = OnDiskUnmodified;
5494 if (m_modOnHd && !isModified() &&
QFile::exists(url().toLocalFile())
5495 && config()->value(KateDocumentConfig::AutoReloadIfStateIsInVersionControl).toBool()) {
5502 git.
start(fullGitPath, args);
5509 m_modOnHdReason = OnDiskUnmodified;
5510 m_prevModOnHdReason = OnDiskUnmodified;
5520 Q_EMIT modifiedOnDisk(
this, m_modOnHd, m_modOnHdReason);
5523QByteArray KTextEditor::DocumentPrivate::checksum()
const
5525 return m_buffer->digest();
5528bool KTextEditor::DocumentPrivate::createDigest()
5532 if (url().isLocalFile()) {
5533 QFile f(url().toLocalFile());
5537 const QString header = QStringLiteral(
"blob %1").
arg(f.size());
5540 while (!f.atEnd()) {
5541 crypto.addData(f.read(256 * 1024));
5544 digest = crypto.result();
5549 m_buffer->setDigest(digest);
5553QString KTextEditor::DocumentPrivate::reasonedMOHString()
const
5558 switch (m_modOnHdReason) {
5559 case OnDiskModified:
5560 return i18n(
"The file '%1' was modified on disk.", str);
5563 return i18n(
"The file '%1' was created on disk.", str);
5566 return i18n(
"The file '%1' was deleted on disk.", str);
5575void KTextEditor::DocumentPrivate::removeTrailingSpacesAndAddNewLineAtEof()
5578 const int remove = config()->removeSpaces();
5579 const bool newLineAtEof = config()->newLineAtEof();
5580 if (remove == 0 && !newLineAtEof) {
5585 const bool wordWrapEnabled = config()->wordWrap();
5586 if (wordWrapEnabled) {
5593 const int lines = this->lines();
5595 for (
int line = 0; line < lines; ++line) {
5601 if (remove == 2 || textline.markedAsModified() || textline.markedAsSavedOnDisk()) {
5602 const int p = textline.
lastChar() + 1;
5603 const int l = textline.
length() - p;
5605 editRemoveText(line, p, l);
5614 Q_ASSERT(lines > 0);
5615 const auto length = lineLength(lines - 1);
5619 const auto oldEndOfDocumentCursor = documentEnd();
5620 std::vector<KTextEditor::ViewPrivate *> viewsToRestoreCursors;
5621 for (
auto view :
std::as_const(m_views)) {
5622 auto v =
static_cast<ViewPrivate *
>(view);
5623 if (v->cursorPosition() == oldEndOfDocumentCursor) {
5624 viewsToRestoreCursors.push_back(v);
5629 editWrapLine(lines - 1, length);
5632 for (
auto v : viewsToRestoreCursors) {
5633 v->setCursorPosition(oldEndOfDocumentCursor);
5641 if (wordWrapEnabled) {
5646void KTextEditor::DocumentPrivate::removeAllTrailingSpaces()
5649 const int lines = this->lines();
5650 for (
int line = 0; line < lines; ++line) {
5652 const int p = textline.
lastChar() + 1;
5653 const int l = textline.
length() - p;
5655 editRemoveText(line, p, l);
5661bool KTextEditor::DocumentPrivate::updateFileType(
const QString &newType,
bool user)
5663 if (user || !m_fileTypeSetByUser) {
5669 if (fileType.name.
isEmpty()) {
5674 m_fileTypeSetByUser = user;
5676 m_fileType = newType;
5678 m_config->configStart();
5683 if ((user || !m_hlSetByUser) && !fileType.hl.
isEmpty()) {
5684 int hl(KateHlManager::self()->nameFind(fileType.hl));
5687 m_buffer->setHighlight(hl);
5694 if (!m_indenterSetByUser && !fileType.indenter.
isEmpty()) {
5695 config()->setIndentationMode(fileType.indenter);
5699 for (
auto view :
std::as_const(m_views)) {
5700 auto v =
static_cast<ViewPrivate *
>(view);
5705 bool bom_settings =
false;
5706 if (m_bomSetByUser) {
5707 bom_settings = m_config->bom();
5709 readVariableLine(fileType.varLine);
5710 if (m_bomSetByUser) {
5711 m_config->setBom(bom_settings);
5713 m_config->configEnd();
5714 for (
auto view :
std::as_const(m_views)) {
5715 auto v =
static_cast<ViewPrivate *
>(view);
5722 Q_EMIT modeChanged(
this);
5726void KTextEditor::DocumentPrivate::slotQueryClose_save(
bool *handled,
bool *abortClosing)
5729 *abortClosing =
true;
5730 if (url().isEmpty()) {
5731 const QUrl res = getSaveFileUrl(
i18n(
"Save File"));
5733 *abortClosing =
true;
5737 *abortClosing =
false;
5740 *abortClosing =
false;
5747QStringList KTextEditor::DocumentPrivate::configKeys()
const
5750 return m_config->configKeys();
5756 return m_config->
value(key);
5759void KTextEditor::DocumentPrivate::setConfigValue(
const QString &key,
const QVariant &value)
5762 m_config->setValue(key, value);
5776 bool changed = removeText(range, block);
5777 changed |= insertText(range.
start(), s, block);
5782KateHighlighting *KTextEditor::DocumentPrivate::highlight()
const
5784 return m_buffer->highlight();
5789 m_buffer->ensureHighlighted(i);
5790 return m_buffer->plainLine(i);
5793Kate::TextLine KTextEditor::DocumentPrivate::plainKateTextLine(
int i)
5795 return m_buffer->plainLine(i);
5798bool KTextEditor::DocumentPrivate::isEditRunning()
const
5800 return editIsRunning;
5803void KTextEditor::DocumentPrivate::setUndoMergeAllEdits(
bool merge)
5805 if (merge && m_undoMergeAllEdits) {
5810 m_undoManager->undoSafePoint();
5811 m_undoManager->setAllowComplexMerge(merge);
5812 m_undoMergeAllEdits =
merge;
5825 return new Kate::TextRange(buffer(), range, insertBehaviors, emptyBehavior);
5828qint64 KTextEditor::DocumentPrivate::revision()
const
5830 return m_buffer->history().revision();
5833qint64 KTextEditor::DocumentPrivate::lastSavedRevision()
const
5835 return m_buffer->history().lastSavedRevision();
5838void KTextEditor::DocumentPrivate::lockRevision(qint64 revision)
5840 m_buffer->history().lockRevision(revision);
5843void KTextEditor::DocumentPrivate::unlockRevision(qint64 revision)
5845 m_buffer->history().unlockRevision(revision);
5848void KTextEditor::DocumentPrivate::transformCursor(
int &line,
5851 qint64 fromRevision,
5854 m_buffer->history().transformCursor(line, column, insertBehavior, fromRevision, toRevision);
5859 qint64 fromRevision,
5862 int line = cursor.
line();
5863 int column = cursor.
column();
5864 m_buffer->history().transformCursor(line, column, insertBehavior, fromRevision, toRevision);
5871 qint64 fromRevision,
5874 m_buffer->history().transformRange(range, insertBehaviors, emptyBehavior, fromRevision, toRevision);
5883 m_annotationModel = model;
5884 Q_EMIT annotationModelChanged(oldmodel, m_annotationModel);
5889 return m_annotationModel;
5894bool KTextEditor::DocumentPrivate::queryClose()
5896 if (!isModified() || (isEmpty() && url().isEmpty())) {
5900 QString docName = documentName();
5903 i18n(
"The document \"%1\" has been modified.\n"
5904 "Do you want to save your changes or discard them?",
5906 i18n(
"Close Document"),
5910 bool abortClose =
false;
5911 bool handled =
false;
5915 sigQueryClose(&handled, &abortClose);
5917 if (url().isEmpty()) {
5918 const QUrl url = getSaveFileUrl(
i18n(
"Save File"));
5927 }
else if (abortClose) {
5930 return waitSaveComplete();
5938void KTextEditor::DocumentPrivate::slotStarted(
KIO::Job *job)
5941 if (m_documentState == DocumentIdle) {
5942 m_documentState = DocumentLoading;
5950 if (m_documentState == DocumentLoading) {
5952 m_readWriteStateBeforeLoading = isReadWrite();
5957 setReadWrite(
false);
5967void KTextEditor::DocumentPrivate::slotCompleted()
5971 if (m_documentState == DocumentLoading) {
5972 setReadWrite(m_readWriteStateBeforeLoading);
5973 delete m_loadingMessage;
5977 if (m_documentState == DocumentSaving || m_documentState == DocumentSavingAs) {
5978 Q_EMIT documentSavedOrUploaded(
this, m_documentState == DocumentSavingAs);
5982 m_documentState = DocumentIdle;
5983 m_reloading =
false;
5986void KTextEditor::DocumentPrivate::slotCanceled()
5990 if (m_documentState == DocumentLoading) {
5991 setReadWrite(m_readWriteStateBeforeLoading);
5992 delete m_loadingMessage;
5994 if (!m_openingError) {
5995 showAndSetOpeningErrorAccess();
6002 m_documentState = DocumentIdle;
6003 m_reloading =
false;
6006void KTextEditor::DocumentPrivate::slotTriggerLoadingMessage()
6010 if (m_documentState != DocumentLoading) {
6015 delete m_loadingMessage;
6024 m_loadingMessage->addAction(cancel);
6028 postMessage(m_loadingMessage);
6031void KTextEditor::DocumentPrivate::slotAbortLoading()
6034 if (!m_loadingJob) {
6040 m_loadingJob->kill(KJob::EmitResult);
6041 m_loadingJob =
nullptr;
6044void KTextEditor::DocumentPrivate::slotUrlChanged(
const QUrl &url)
6054 Q_EMIT documentUrlChanged(
this);
6057bool KTextEditor::DocumentPrivate::save()
6061 if ((m_documentState != DocumentIdle) && (m_documentState != DocumentPreSavingAs)) {
6066 if (m_documentState == DocumentIdle) {
6067 m_documentState = DocumentSaving;
6069 m_documentState = DocumentSavingAs;
6073 Q_EMIT aboutToSave(
this);
6079bool KTextEditor::DocumentPrivate::saveAs(
const QUrl &url)
6090 if (m_documentState != DocumentIdle) {
6095 m_documentState = DocumentPreSavingAs;
6101QString KTextEditor::DocumentPrivate::defaultDictionary()
const
6103 return m_defaultDictionary;
6108 return m_dictionaryRanges;
6111void KTextEditor::DocumentPrivate::clearDictionaryRanges()
6113 for (
auto i = m_dictionaryRanges.cbegin(); i != m_dictionaryRanges.cend(); ++i) {
6116 m_dictionaryRanges.clear();
6117 if (m_onTheFlyChecker) {
6118 m_onTheFlyChecker->refreshSpellCheck();
6120 Q_EMIT dictionaryRangesPresent(
false);
6127 setDictionary(newDictionary, rangeOnLine(range, i));
6130 setDictionary(newDictionary, range);
6133 Q_EMIT dictionaryRangesPresent(!m_dictionaryRanges.isEmpty());
6139 if (!newDictionaryRange.
isValid() || newDictionaryRange.
isEmpty()) {
6144 for (
auto i = m_dictionaryRanges.begin(); i != m_dictionaryRanges.end();) {
6145 qCDebug(LOG_KTE) <<
"new iteration" << newDictionaryRange;
6146 if (newDictionaryRange.
isEmpty()) {
6149 QPair<KTextEditor::MovingRange *, QString> pair = *i;
6150 QString dictionarySet = pair.second;
6152 qCDebug(LOG_KTE) << *dictionaryRange << dictionarySet;
6153 if (dictionaryRange->
contains(newDictionaryRange) && newDictionary == dictionarySet) {
6154 qCDebug(LOG_KTE) <<
"dictionaryRange contains newDictionaryRange";
6157 if (newDictionaryRange.
contains(*dictionaryRange)) {
6158 delete dictionaryRange;
6159 i = m_dictionaryRanges.erase(i);
6160 qCDebug(LOG_KTE) <<
"newDictionaryRange contains dictionaryRange";
6166 if (dictionarySet == newDictionary) {
6169 Q_ASSERT(remainingRanges.
size() == 1);
6170 newDictionaryRange = remainingRanges.
first();
6172 qCDebug(LOG_KTE) <<
"dictionarySet == newDictionary";
6176 for (
auto j = remainingRanges.
begin(); j != remainingRanges.
end(); ++j) {
6179 newRanges.
push_back({remainingRange, dictionarySet});
6181 i = m_dictionaryRanges.erase(i);
6182 delete dictionaryRange;
6187 m_dictionaryRanges += newRanges;
6192 m_dictionaryRanges.push_back({newDictionaryMovingRange, newDictionary});
6194 if (m_onTheFlyChecker && !newDictionaryRange.
isEmpty()) {
6195 m_onTheFlyChecker->refreshSpellCheck(newDictionaryRange);
6199void KTextEditor::DocumentPrivate::setDefaultDictionary(
const QString &dict)
6201 if (m_defaultDictionary == dict) {
6205 m_defaultDictionary = dict;
6207 if (m_onTheFlyChecker) {
6208 m_onTheFlyChecker->updateConfig();
6209 refreshOnTheFlyCheck();
6211 Q_EMIT defaultDictionaryChanged(
this);
6214void KTextEditor::DocumentPrivate::onTheFlySpellCheckingEnabled(
bool enable)
6216 if (isOnTheFlySpellCheckingEnabled() == enable) {
6221 Q_ASSERT(m_onTheFlyChecker ==
nullptr);
6222 m_onTheFlyChecker =
new KateOnTheFlyChecker(
this);
6224 delete m_onTheFlyChecker;
6225 m_onTheFlyChecker =
nullptr;
6228 for (
auto view :
std::as_const(m_views)) {
6229 static_cast<ViewPrivate *
>(view)->reflectOnTheFlySpellCheckStatus(enable);
6233bool KTextEditor::DocumentPrivate::isOnTheFlySpellCheckingEnabled()
const
6235 return m_onTheFlyChecker !=
nullptr;
6240 if (!m_onTheFlyChecker) {
6243 return m_onTheFlyChecker->dictionaryForMisspelledRange(range);
6247void KTextEditor::DocumentPrivate::clearMisspellingForWord(
const QString &word)
6249 if (m_onTheFlyChecker) {
6250 m_onTheFlyChecker->clearMisspellingForWord(word);
6256 if (m_onTheFlyChecker) {
6257 m_onTheFlyChecker->refreshSpellCheck(range);
6263 deleteDictionaryRange(movingRange);
6268 deleteDictionaryRange(movingRange);
6273 qCDebug(LOG_KTE) <<
"deleting" << movingRange;
6275 auto finder = [=](
const QPair<KTextEditor::MovingRange *, QString> &item) ->
bool {
6276 return item.first == movingRange;
6279 auto it = std::find_if(m_dictionaryRanges.begin(), m_dictionaryRanges.end(), finder);
6281 if (it != m_dictionaryRanges.end()) {
6282 m_dictionaryRanges.erase(it);
6286 Q_ASSERT(std::find_if(m_dictionaryRanges.begin(), m_dictionaryRanges.end(), finder) == m_dictionaryRanges.end());
6289bool KTextEditor::DocumentPrivate::containsCharacterEncoding(
KTextEditor::Range range)
6291 KateHighlighting *highlighting = highlight();
6293 const int rangeStartLine = range.
start().
line();
6294 const int rangeStartColumn = range.
start().
column();
6295 const int rangeEndLine = range.
end().
line();
6296 const int rangeEndColumn = range.
end().
column();
6298 for (
int line = range.
start().
line(); line <= rangeEndLine; ++line) {
6300 const int startColumn = (line == rangeStartLine) ? rangeStartColumn : 0;
6301 const int endColumn = (line == rangeEndLine) ? rangeEndColumn : textLine.length();
6302 for (
int col = startColumn; col < endColumn; ++col) {
6304 const KatePrefixStore &prefixStore = highlighting->getCharacterEncodingsPrefixStore(attr);
6314int KTextEditor::DocumentPrivate::computePositionWrtOffsets(
const OffsetList &offsetList,
int pos)
6316 int previousOffset = 0;
6317 for (
auto i = offsetList.cbegin(); i != offsetList.cend(); ++i) {
6318 if (i->first > pos) {
6321 previousOffset = i->second;
6323 return pos + previousOffset;
6327 KTextEditor::DocumentPrivate::OffsetList &decToEncOffsetList,
6328 KTextEditor::DocumentPrivate::OffsetList &encToDecOffsetList)
6332 int decToEncCurrentOffset = 0;
6333 int encToDecCurrentOffset = 0;
6337 KateHighlighting *highlighting = highlight();
6340 const int rangeStartLine = range.
start().
line();
6341 const int rangeStartColumn = range.
start().
column();
6342 const int rangeEndLine = range.
end().
line();
6343 const int rangeEndColumn = range.
end().
column();
6345 for (
int line = range.
start().
line(); line <= rangeEndLine; ++line) {
6346 textLine = kateTextLine(line);
6347 int startColumn = (line == rangeStartLine) ? rangeStartColumn : 0;
6348 int endColumn = (line == rangeEndLine) ? rangeEndColumn : textLine.length();
6349 for (
int col = startColumn; col < endColumn;) {
6351 const KatePrefixStore &prefixStore = highlighting->getCharacterEncodingsPrefixStore(attr);
6354 if (!matchingPrefix.
isEmpty()) {
6356 const QChar &c = characterEncodingsHash.
value(matchingPrefix);
6357 const bool isNullChar = c.
isNull();
6361 i += matchingPrefix.
length();
6362 col += matchingPrefix.
length();
6364 decToEncCurrentOffset = decToEncCurrentOffset - (isNullChar ? 0 : 1) + matchingPrefix.
length();
6365 encToDecCurrentOffset = encToDecCurrentOffset - matchingPrefix.
length() + (isNullChar ? 0 : 1);
6366 newI += (isNullChar ? 0 : 1);
6367 decToEncOffsetList.push_back(QPair<int, int>(newI, decToEncCurrentOffset));
6368 encToDecOffsetList.push_back(QPair<int, int>(i, encToDecCurrentOffset));
6378 if (previous < range.
end()) {
6384void KTextEditor::DocumentPrivate::replaceCharactersByEncoding(
KTextEditor::Range range)
6386 KateHighlighting *highlighting = highlight();
6389 const int rangeStartLine = range.
start().
line();
6390 const int rangeStartColumn = range.
start().
column();
6391 const int rangeEndLine = range.
end().
line();
6392 const int rangeEndColumn = range.
end().
column();
6394 for (
int line = range.
start().
line(); line <= rangeEndLine; ++line) {
6395 textLine = kateTextLine(line);
6396 int startColumn = (line == rangeStartLine) ? rangeStartColumn : 0;
6397 int endColumn = (line == rangeEndLine) ? rangeEndColumn : textLine.length();
6398 for (
int col = startColumn; col < endColumn;) {
6400 const QHash<QChar, QString> &reverseCharacterEncodingsHash = highlighting->getReverseCharacterEncodings(attr);
6401 auto it = reverseCharacterEncodingsHash.
find(textLine.
at(col));
6402 if (it != reverseCharacterEncodingsHash.
end()) {
6404 col += (*it).length();
6416QStringList KTextEditor::DocumentPrivate::embeddedHighlightingModes()
const
6418 return highlight()->getEmbeddedHighlightingModes();
6423 return highlight()->higlightingModeForLocation(
this, position);
6438 if (line < 0 || line >= lines() || column < 0) {
6439 return KSyntaxHighlighting::Theme::TextStyle::Normal;
6447 if (column < tl.
length()) {
6449 }
else if (column == tl.
length()) {
6453 return KSyntaxHighlighting::Theme::TextStyle::Normal;
6456 return KSyntaxHighlighting::Theme::TextStyle::Normal;
6459 return highlight()->defaultStyleForAttribute(attribute);
6462bool KTextEditor::DocumentPrivate::isComment(
int line,
int column)
6464 return defStyleNum(line, column) == KSyntaxHighlighting::Theme::TextStyle::Comment;
6467int KTextEditor::DocumentPrivate::findTouchedLine(
int startLine,
bool down)
6469 const int offset = down ? 1 : -1;
6470 const int lineCount = lines();
6471 while (startLine >= 0 && startLine < lineCount) {
6473 if (tl.markedAsModified() || tl.markedAsSavedOnDisk()) {
6476 startLine += offset;
6485 delete m_activeTemplateHandler.data();
6486 m_activeTemplateHandler = handler;
6499 qCWarning(LOG_KTE) <<
"trying to post a message to a view of another document:" << message->
text();
6509 closeAction->
setToolTip(
i18nc(
"Close the message being displayed",
"Close message"));
6515 const auto messageActions = message->
actions();
6516 managedMessageActions.
reserve(messageActions.size());
6517 for (
QAction *action : messageActions) {
6518 action->setParent(
nullptr);
6519 managedMessageActions.
append(std::shared_ptr<QAction>(action));
6521 m_messageHash.insert(message, managedMessageActions);
6524 if (KTextEditor::ViewPrivate *view = qobject_cast<KTextEditor::ViewPrivate *>(message->
view())) {
6525 view->postMessage(message, managedMessageActions);
6527 for (
auto view :
std::as_const(m_views)) {
6528 static_cast<ViewPrivate *
>(view)->postMessage(message, managedMessageActions);
6533 connect(message, &Message::closed,
this, &DocumentPrivate::messageDestroyed);
6541 Q_ASSERT(m_messageHash.contains(message));
6542 m_messageHash.remove(message);
6546#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.
KTextEditor::MainWindow * activeMainWindow()
Accessor to the active main window.
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.
QWidget * window()
Get the toplevel widget.
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)
bool remove(const QString &column, const QVariant &value)
char * toString(const EngineQuery &query)
KCALUTILS_EXPORT QString mimeType()
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
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)
QString name(StandardAction id)
QAction * save(const QObject *recvr, const char *slot, QObject *parent)
QAction * clear(const QObject *recvr, const char *slot, QObject *parent)
QAction * saveAs(const QObject *recvr, const char *slot, QObject *parent)
const QList< QKeySequence > & end()
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)