Changeset View
Changeset View
Standalone View
Standalone View
src/document/katedocument.cpp
Show First 20 Lines • Show All 91 Lines • ▼ Show 20 Line(s) | 1099 | { | |||
---|---|---|---|---|---|
1100 | if (startLine < 0 || endLine < 0) { | 1100 | if (startLine < 0 || endLine < 0) { | ||
1101 | return false; | 1101 | return false; | ||
1102 | } | 1102 | } | ||
1103 | 1103 | | |||
1104 | if (!isReadWrite()) { | 1104 | if (!isReadWrite()) { | ||
1105 | return false; | 1105 | return false; | ||
1106 | } | 1106 | } | ||
1107 | 1107 | | |||
1108 | int col = config()->wordWrapAt(); | 1108 | editStart(); | ||
1109 | 1109 | | |||
1110 | if (col == 0) { | 1110 | while (startLine <= endLine) { | ||
1111 | return false; | 1111 | int newLines = scrutinizeToWrap(startLine); | ||
1112 | startLine += newLines + 1; | ||||
1113 | endLine += newLines; | ||||
1112 | } | 1114 | } | ||
1113 | 1115 | | |||
1114 | editStart(); | 1116 | editEnd(); | ||
1115 | 1117 | | |||
1116 | for (int line = startLine; (line <= endLine) && (line < lines()); line++) { | 1118 | return true; | ||
1117 | Kate::TextLine l = kateTextLine(line); | 1119 | } | ||
1118 | 1120 | | |||
1119 | if (!l) { | 1121 | int KTextEditor::DocumentPrivate::scrutinizeToWrap(int lineNumber) | ||
1120 | break; | 1122 | { | ||
1123 | const int wrapColumn = wordWrapAt(); | ||||
1124 | const int tabWidth = m_buffer->tabWidth(); | ||||
1125 | | ||||
1126 | int startLine = lineNumber; | ||||
1127 | | ||||
1128 | // Look backwards if a join with the previous line is possible | ||||
1129 | Kate::TextLine prevl = kateTextLine(lineNumber - 1); | ||||
1130 | Kate::TextLine line = kateTextLine(lineNumber); | ||||
1131 | if (prevl && line && line->isAutoWrapped()) { | ||||
1132 | int firstOnThis = line->string().indexOf(QLatin1Char(' ')); | ||||
1133 | if (firstOnThis < (wrapColumn - prevl->virtualLength(tabWidth) + 1)) { | ||||
1134 | --startLine; | ||||
1135 | --lineNumber; | ||||
1136 | editUnWrapLine(lineNumber); | ||||
1121 | } | 1137 | } | ||
1138 | } | ||||
1122 | 1139 | | |||
1123 | //qCDebug(LOG_KTE) << "try wrap line: " << line; | 1140 | --lineNumber; | ||
1141 | do { | ||||
1142 | ++lineNumber; | ||||
1143 | line = kateTextLine(lineNumber); | ||||
1144 | if (!line) { | ||||
1145 | break; // Should never happens | ||||
1146 | } | ||||
1124 | 1147 | | |||
1125 | if (l->virtualLength(m_buffer->tabWidth()) > col) { | 1148 | // Look forward if a join with the next line is useful; In other words: | ||
1126 | Kate::TextLine nextl = kateTextLine(line + 1); | 1149 | // Don't join dumb, be smart and avoid a lot of unneeded work | ||
1150 | int firstOnNext = wrapColumn + 1; // Length of first word in next line | ||||
1151 | Kate::TextLine nextl = kateTextLine(lineNumber + 1); | ||||
1152 | if (nextl && nextl->isAutoWrapped()) { | ||||
1153 | firstOnNext = nextl->string().indexOf(QLatin1Char(' ')); | ||||
1154 | } | ||||
1155 | if (firstOnNext < (wrapColumn - line->virtualLength(tabWidth) + 1)) { | ||||
1156 | editUnWrapLine(lineNumber); | ||||
1157 | } | ||||
1127 | 1158 | | |||
1128 | //qCDebug(LOG_KTE) << "do wrap line: " << line; | 1159 | } while (wrapLine(lineNumber)); | ||
1129 | 1160 | | |||
1130 | int eolPosition = l->length() - 1; | 1161 | return lineNumber - startLine; | ||
1162 | } | ||||
1131 | 1163 | | |||
1132 | // take tabs into account here, too | 1164 | bool KTextEditor::DocumentPrivate::wrapLine(int lineNumber) | ||
1133 | int x = 0; | 1165 | { | ||
1134 | const QString &t = l->string(); | 1166 | Kate::TextLine line = kateTextLine(lineNumber); | ||
1135 | int z2 = 0; | | |||
1136 | for (; z2 < l->length(); z2++) { | | |||
1137 | static const QChar tabChar(QLatin1Char('\t')); | | |||
1138 | if (t.at(z2) == tabChar) { | | |||
1139 | x += m_buffer->tabWidth() - (x % m_buffer->tabWidth()); | | |||
1140 | } else { | | |||
1141 | x++; | | |||
1142 | } | | |||
1143 | 1167 | | |||
1144 | if (x > col) { | 1168 | if (!line) { | ||
1145 | break; | 1169 | return false; | ||
1146 | } | 1170 | } | ||
1147 | } | | |||
1148 | 1171 | | |||
1149 | const int colInChars = qMin(z2, l->length() - 1); | 1172 | const int wrapColumn = wordWrapAt(); | ||
1150 | int searchStart = colInChars; | 1173 | const int tabWidth = m_buffer->tabWidth(); | ||
1151 | 1174 | | |||
1152 | // If where we are wrapping is an end of line and is a space we don't | 1175 | if (wrapColumn == 0) { // setWordWrapAt/config()->setWordWrapAt should avoid bad settings | ||
1153 | // want to wrap there | 1176 | return false; | ||
1154 | if (searchStart == eolPosition && t.at(searchStart).isSpace()) { | 1177 | } | ||
1155 | searchStart--; | | |||
1156 | } | | |||
1157 | 1178 | | |||
1158 | // Scan backwards looking for a place to break the line | 1179 | // Hack due to strange general behavior that an over size space is allowed (is not removed) | ||
1159 | // We are not interested in breaking at the first char | 1180 | // when the line will wrapped but not when it's there on an 2nd invoke. | ||
1160 | // of the line (if it is a space), but we are at the second | 1181 | const int lastSpace = line->endsWith(QLatin1String(" ")) ? 1 : 0; | ||
1161 | // anders: if we can't find a space, try breaking on a word | | |||
1162 | // boundary, using KateHighlight::canBreakAt(). | | |||
1163 | // This could be a priority (setting) in the hl/filetype/document | | |||
1164 | int z = -1; | | |||
1165 | int nw = -1; // alternative position, a non word character | | |||
1166 | for (z = searchStart; z >= 0; z--) { | | |||
1167 | if (t.at(z).isSpace()) { | | |||
1168 | break; | | |||
1169 | } | | |||
1170 | if ((nw < 0) && highlight()->canBreakAt(t.at(z), l->attribute(z))) { | | |||
1171 | nw = z; | | |||
1172 | } | | |||
1173 | } | | |||
1174 | 1182 | | |||
1175 | if (z >= 0) { | 1183 | if (line->virtualLength(tabWidth) - lastSpace <= wrapColumn) { | ||
1176 | // So why don't we just remove the trailing space right away? | 1184 | return false; // Nothing to do; we are done | ||
1177 | // Well, the (view's) cursor may be directly in front of that space | 1185 | } | ||
1178 | // (user typing text before the last word on the line), and if that | | |||
1179 | // happens, the cursor would be moved to the next line, which is not | | |||
1180 | // what we want (bug #106261) | | |||
1181 | z++; | | |||
1182 | } else { | | |||
1183 | // There was no space to break at so break at a nonword character if | | |||
1184 | // found, or at the wrapcolumn ( that needs be configurable ) | | |||
1185 | // Don't try and add any white space for the break | | |||
1186 | if ((nw >= 0) && nw < colInChars) { | | |||
1187 | nw++; // break on the right side of the character | | |||
1188 | } | | |||
1189 | z = (nw >= 0) ? nw : colInChars; | | |||
1190 | } | | |||
1191 | 1186 | | |||
1192 | if (nextl && !nextl->isAutoWrapped()) { | 1187 | const int eolPosition = line->length() - 1; | ||
1193 | editWrapLine(line, z, true); | | |||
1194 | editMarkLineAutoWrapped(line + 1, true); | | |||
1195 | 1188 | | |||
1196 | endLine++; | 1189 | // take tabs into account here, too | ||
1197 | } else { | 1190 | const QString &text = line->string(); | ||
1198 | if (nextl && (nextl->length() > 0) && !nextl->at(0).isSpace() && ((l->length() < 1) || !l->at(l->length() - 1).isSpace())) { | 1191 | static const QChar tabChar(QLatin1Char('\t')); | ||
1199 | editInsertText(line + 1, 0, QLatin1String(" ")); | 1192 | int z2 = 0; | ||
1200 | } | 1193 | for (int x = 0; z2 < line->length(); z2++) { | ||
1194 | if (text.at(z2) == tabChar) { | ||||
1195 | x += tabWidth - (x % tabWidth); | ||||
1196 | } else { | ||||
1197 | x++; | ||||
1198 | } | ||||
1201 | 1199 | | |||
1202 | bool newLineAdded = false; | 1200 | if (x > wrapColumn) { | ||
1203 | editWrapLine(line, z, false, &newLineAdded); | 1201 | break; | ||
1202 | } | ||||
1203 | } | ||||
1204 | 1204 | | |||
1205 | editMarkLineAutoWrapped(line + 1, true); | 1205 | const int trueColumn = qMin(z2, eolPosition); | ||
1206 | int searchStart = trueColumn; | ||||
1206 | 1207 | | |||
1207 | endLine++; | 1208 | // If where we are wrapping is an end of line and is a space we don't | ||
1208 | } | 1209 | // want to wrap there | ||
1210 | if (searchStart == eolPosition && text.at(searchStart).isSpace()) { | ||||
1211 | searchStart--; | ||||
1212 | } | ||||
1213 | | ||||
1214 | // Scan backwards looking for a place to break the line | ||||
1215 | // We are not interested in breaking at the first char | ||||
1216 | // of the line (if it is a space), but we are at the second | ||||
1217 | // anders: if we can't find a space, try breaking on a word | ||||
1218 | // boundary, using KateHighlight::canBreakAt(). | ||||
1219 | // This could be a priority (setting) in the hl/filetype/document | ||||
1220 | int z = -1; | ||||
1221 | int nw = -1; // alternative position, a non word character | ||||
1222 | for (z = searchStart; z >= 0; z--) { | ||||
1223 | if (text.at(z).isSpace()) { | ||||
1224 | break; | ||||
1225 | } | ||||
1226 | if ((nw < 0) && highlight()->canBreakAt(text.at(z), line->attribute(z))) { | ||||
1227 | nw = z; | ||||
1209 | } | 1228 | } | ||
1210 | } | 1229 | } | ||
1211 | 1230 | | |||
1212 | editEnd(); | 1231 | if (z >= 0) { | ||
1232 | // So why don't we just remove the trailing space right away? | ||||
1233 | // Well, the (view's) cursor may be directly in front of that space | ||||
1234 | // (user typing text before the last word on the line), and if that | ||||
1235 | // happens, the cursor would be moved to the next line, which is not | ||||
1236 | // what we want (bug #106261) | ||||
1237 | z++; | ||||
1238 | } else { | ||||
1239 | // There was no space to break at so break at a nonword character if | ||||
1240 | // found, or at the wrapcolumn ( that needs be configurable ) | ||||
1241 | // Don't try and add any white space for the break | ||||
1242 | if ((nw >= 0) && nw < trueColumn) { | ||||
1243 | nw++; // break on the right side of the character | ||||
1244 | } | ||||
1245 | z = (nw >= 0) ? nw : trueColumn; | ||||
1246 | } | ||||
1213 | 1247 | | |||
1214 | return true; | 1248 | bool newLineAdded = false; | ||
1249 | editWrapLine(lineNumber, z, true, &newLineAdded); | ||||
1250 | editMarkLineAutoWrapped(lineNumber + 1, true); | ||||
1251 | | ||||
1252 | return newLineAdded; | ||||
1215 | } | 1253 | } | ||
1216 | 1254 | | |||
1217 | bool KTextEditor::DocumentPrivate::editInsertText(int line, int col, const QString &s) | 1255 | bool KTextEditor::DocumentPrivate::editInsertText(int line, int col, const QString &s) | ||
1218 | { | 1256 | { | ||
1219 | // verbose debug | 1257 | // verbose debug | ||
1220 | EDIT_DEBUG << "editInsertText" << line << col << s; | 1258 | EDIT_DEBUG << "editInsertText" << line << col << s; | ||
1221 | 1259 | | |||
1222 | if (line < 0 || col < 0) { | 1260 | if (line < 0 || col < 0) { | ||
▲ Show 20 Lines • Show All 173 Lines • ▼ Show 20 Line(s) | 1410 | if (!nextLine || newLine) { | |||
1396 | 1434 | | |||
1397 | // yes, we added a new line ! | 1435 | // yes, we added a new line ! | ||
1398 | if (newLineAdded) { | 1436 | if (newLineAdded) { | ||
1399 | (*newLineAdded) = true; | 1437 | (*newLineAdded) = true; | ||
1400 | } | 1438 | } | ||
1401 | } else { | 1439 | } else { | ||
1402 | m_buffer->wrapLine(KTextEditor::Cursor(line, col)); | 1440 | m_buffer->wrapLine(KTextEditor::Cursor(line, col)); | ||
1403 | m_buffer->unwrapLine(line + 2); | 1441 | m_buffer->unwrapLine(line + 2); | ||
1404 | | ||||
1405 | // no, no new line added ! | 1442 | // no, no new line added ! | ||
1406 | if (newLineAdded) { | 1443 | if (newLineAdded) { | ||
1407 | (*newLineAdded) = false; | 1444 | (*newLineAdded) = false; | ||
1408 | } | 1445 | } | ||
1409 | } | 1446 | } | ||
1410 | 1447 | | |||
1411 | // remember last change cursor | 1448 | // remember last change cursor | ||
1412 | m_editLastChangeStartCursor = KTextEditor::Cursor(line, col); | 1449 | m_editLastChangeStartCursor = KTextEditor::Cursor(line, col); | ||
▲ Show 20 Lines • Show All 182 Lines • ▼ Show 20 Line(s) | 3148 | { | |||
3132 | Kate::TextLine textLine = plainKateTextLine(ln); | 3169 | Kate::TextLine textLine = plainKateTextLine(ln); | ||
3133 | 3170 | | |||
3134 | if (c.column() > textLine->length()) { | 3171 | if (c.column() > textLine->length()) { | ||
3135 | c.setColumn(textLine->length()); | 3172 | c.setColumn(textLine->length()); | ||
3136 | } | 3173 | } | ||
3137 | 3174 | | |||
3138 | // first: wrap line | 3175 | // first: wrap line | ||
3139 | editWrapLine(c.line(), c.column()); | 3176 | editWrapLine(c.line(), c.column()); | ||
3140 | 3177 | // Ensure in static wrap mode not to re-join the new line | |||
3178 | editMarkLineAutoWrapped(c.line(), false); | ||||
3141 | // end edit session here, to have updated HL in userTypedChar! | 3179 | // end edit session here, to have updated HL in userTypedChar! | ||
3142 | editEnd(); | 3180 | editEnd(); | ||
3143 | 3181 | | |||
3144 | // second: indent the new line, if needed... | 3182 | // second: indent the new line, if needed... | ||
3145 | m_indenter->userTypedChar(v, v->cursorPosition(), QLatin1Char('\n')); | 3183 | m_indenter->userTypedChar(v, v->cursorPosition(), QLatin1Char('\n')); | ||
3146 | } | 3184 | } | ||
3147 | 3185 | | |||
3148 | void KTextEditor::DocumentPrivate::transpose(const KTextEditor::Cursor &cursor) | 3186 | void KTextEditor::DocumentPrivate::transpose(const KTextEditor::Cursor &cursor) | ||
▲ Show 20 Lines • Show All 83 Lines • ▼ Show 20 Line(s) | 3268 | if (!view->config()->backspaceRemoveComposed()) { // Normal backspace behavior | |||
3232 | // move to left of surrogate pair | 3270 | // move to left of surrogate pair | ||
3233 | if (!isValidTextPosition(beginCursor)) { | 3271 | if (!isValidTextPosition(beginCursor)) { | ||
3234 | Q_ASSERT(col >= 2); | 3272 | Q_ASSERT(col >= 2); | ||
3235 | beginCursor.setColumn(col - 2); | 3273 | beginCursor.setColumn(col - 2); | ||
3236 | } | 3274 | } | ||
3237 | } else { | 3275 | } else { | ||
3238 | beginCursor.setColumn(view->textLayout(c)->previousCursorPosition(c.column())); | 3276 | beginCursor.setColumn(view->textLayout(c)->previousCursorPosition(c.column())); | ||
3239 | } | 3277 | } | ||
3240 | removeText(KTextEditor::Range(beginCursor, endCursor)); | | |||
3241 | // in most cases cursor is moved by removeText, but we should do it manually | 3278 | // in most cases cursor is moved by removeText, but we should do it manually | ||
3242 | // for past-end-of-line cursors in block mode | 3279 | // for past-end-of-line cursors in block mode | ||
3243 | view->setCursorPosition(beginCursor); | 3280 | Kate::TextLine textLine = plainKateTextLine(line); | ||
3281 | if (textLine && textLine->length() < static_cast<int>(col)) { | ||||
3282 | view->setCursorPosition(KTextEditor::Cursor(line, col - 1)); | ||||
3283 | } | ||||
3284 | removeText(KTextEditor::Range(beginCursor, endCursor)); | ||||
3244 | } | 3285 | } | ||
3245 | } else { | 3286 | } else { | ||
3246 | // col == 0: wrap to previous line | 3287 | // col == 0: wrap to previous line | ||
3247 | if (line >= 1) { | 3288 | if (line >= 1) { | ||
3248 | Kate::TextLine textLine = m_buffer->plainLine(line - 1); | 3289 | Kate::TextLine textLine = m_buffer->plainLine(line - 1); | ||
3249 | 3290 | | |||
3250 | // don't forget this check!!!! really!!!! | 3291 | // don't forget this check!!!! really!!!! | ||
3251 | if (!textLine) { | 3292 | if (!textLine) { | ||
▲ Show 20 Lines • Show All 91 Lines • Show Last 20 Lines |