/** Conversion function from QtScript range to KTextEditor::Range */ static void rangeFromScriptValue(const QScriptValue &obj, KTextEditor::Range &range) { range.start().setPosition(obj.property("start").property("line").toInt32(), obj.property("start").property("column").toInt32()); range.end().setPosition(obj.property("end").property("line").toInt32(), obj.property("end").property("column").toInt32()); }
QRect KTextEditorHelpers::getItemBoundingRect(const KTextEditor::View* view, const KTextEditor::Range& itemRange) { QPoint startPoint = view->mapToGlobal(view->cursorToCoordinate(itemRange.start())); QPoint endPoint = view->mapToGlobal(view->cursorToCoordinate(itemRange.end())); endPoint.ry() += getLineHeight(view, itemRange.start().line()); return QRect(startPoint, endPoint); }
/** Conversion function from QtScript range to KTextEditor::Range */ static QScriptValue rangeToScriptValue(QScriptEngine *engine, const KTextEditor::Range &range) { QString code = QString("new Range(%1, %2, %3, %4);").arg(range.start().line()) .arg(range.start().column()) .arg(range.end().line()) .arg(range.end().column()); return engine->evaluate(code); }
void ClangCodeCompletionModel::executeCompletionItem2( KTextEditor::Document* const doc , const KTextEditor::Range& word , const QModelIndex& index ) const { assert("Active view expected to be equal to the stored one" && doc->activeView() == m_current_view); assert("Invalid index is not expected here!" && index.isValid()); assert("Parent index is not valid" && index.parent().isValid()); assert("Parent index must be GROUP" && index.parent().internalId() == Level::GROUP); assert( "Parent index points to invalid group" && 0 <= index.internalId() && unsigned(index.internalId()) < m_groups.size() ); assert( "Index points to invalid item" && 0 <= index.row() && unsigned(index.row()) < m_groups[index.internalId()].second.m_completions.size() ); auto* const template_iface = qobject_cast<KTextEditor::TemplateInterface2*>(m_current_view); if (template_iface) { kDebug(DEBUG_AREA) << "TemplateInterface available for a view" << m_current_view; const auto result = m_groups[index.internalId()] .second.m_completions[index.row()] .getCompletionTemplate(); kDebug(DEBUG_AREA) << "Template:" << result.m_tpl; kDebug(DEBUG_AREA) << "Values:" << result.m_values; // Check if current template is a function and there is a '()' right after cursor auto range = word; if (result.m_is_function) { const auto next_word_range = DocumentProxy(doc).firstWordAfterCursor(word.end()); kDebug(DEBUG_AREA) << "OK THIS IS FUNCTION TEMPLATE: next word range" << next_word_range; kDebug(DEBUG_AREA) << "replace range before:" << range; if (next_word_range.isValid() && doc->text(next_word_range).startsWith(QLatin1String("()"))) { range.end().setColumn(next_word_range.start().column() + 2); kDebug(DEBUG_AREA) << "replace range after:" << range; } } doc->removeText(range); template_iface->insertTemplateText(range.start(), result.m_tpl, result.m_values, nullptr); } else { kDebug(DEBUG_AREA) << "No TemplateInterface for a view" << m_current_view; const auto p = m_groups[index.internalId()].second.m_completions[index.row()].executeCompletion(); doc->replaceText(word, p.first); // Try to reposition a cursor inside a current (hope it still is) view auto pos = word.start(); pos.setColumn(pos.column() + p.second); m_current_view->setCursorPosition(pos); } }
void CodeCompletionWorker::updateContextRange(KTextEditor::Range& contextRange, KTextEditor::View* /*view*/, DUContextPointer context) const { if(context && context->owner() && context->owner()->type<FunctionType>()) { if(!context->owner()->type<FunctionType>()->returnType()) { //For constructor completion, we need some more context contextRange.start().setLine(contextRange.start().line() > 30 ? contextRange.start().line()-30 : 0); contextRange.start().setColumn(0); } } }
// Scan throughout the entire document for possible completions, // ignoring any dublets const QStringList KateWordCompletionModel::allMatches( KTextEditor::View *view, const KTextEditor::Range &range ) const { KTextEditor::Document *doc = view->document(); QString match_str = doc->text(range); QString s, m; QSet<QString> seen; QStringList l; int i( 0 ); int pos( 0 ); QRegExp re( "\\b(" + match_str + "\\w{1,})" ); while( i < doc->lines() ) { s = doc->line( i ); pos = 0; while ( pos >= 0 ) { pos = re.indexIn( s, pos ); if ( pos >= 0 ) { // typing in the middle of a word if ( ! ( i == range.start().line() && pos == range.start().column() ) ) { m = re.cap( 1 ); if ( ! seen.contains( m ) ) { seen.insert( m ); l << m; } } pos += re.matchedLength(); } } i++; } // Global completion // int db_area = KDebug::registerArea("ktuan-debug"); QMap<QString, QStringList>::const_iterator ci = doc_word_list.constBegin(); while (ci != doc_word_list.constEnd()) { if (ci.key() != doc->url().prettyUrl()) { QStringList list = ci.value(); foreach (QString word, list) { // kDebug(db_area) << "complete word " << word; if (word.startsWith(match_str) && !seen.contains(word)) { // kDebug(db_area) << "Global completion"; seen.insert(word); l << word; } } } ++ci; }
void TextHistory::removeText(const KTextEditor::Range &range, int oldLineLength) { // create and add new entry Entry entry; entry.type = Entry::RemoveText; entry.line = range.start().line(); entry.column = range.start().column(); entry.length = range.end().column() - range.start().column(); entry.oldLineLength = oldLineLength; addEntry(entry); }
void ImplementFunctionCompletionItem::execute(KTextEditor::Document* document, const KTextEditor::Range& word) { const QString finalText = m_name + "(" + m_arguments.join(", ") + "):"; document->replaceText(word, finalText); // 4 spaces is indentation for python. everyone does it like this. you must, too. // TODO use kate settings document->insertLine(word.start().line() + 1, m_previousIndent + " "); if ( View* view = document->activeView() ) { view->setCursorPosition(Cursor(word.end().line() + 1, m_previousIndent.length() + 4)); } }
void LaTeXInfo::startLaTeXCompletion(KTextEditor::View *view) { KTextEditor::CodeCompletionInterface* completionInterface = qobject_cast<KTextEditor::CodeCompletionInterface*>(view); if(!completionInterface) { return; } KTextEditor::Range range = m_latexCompletionModel->completionRange(view, view->cursorPosition()); if(!range.isValid()) { range = KTextEditor::Range(view->cursorPosition(), view->cursorPosition()); } completionInterface->startCompletion(range, m_latexCompletionModel); }
void KeywordItem::execute(KTextEditor::View* view, const KTextEditor::Range& word) { KTextEditor::Document *document = view->document(); if ( !m_replacement.isEmpty() ) { QString replacement = m_replacement; replacement = replacement.replace('\n', '\n' + getIndendation(document->line(word.start().line()))); replacement = replacement.replace(QLatin1String("%INDENT%"), indentString(document)); int cursorPos = replacement.indexOf(QStringLiteral("%CURSOR%")); int selectionEnd = -1; if ( cursorPos != -1 ) { replacement.remove(QStringLiteral("%CURSOR%")); } else { cursorPos = replacement.indexOf(QStringLiteral("%SELECT%")); if ( cursorPos != -1 ) { replacement.remove(QStringLiteral("%SELECT%")); selectionEnd = replacement.indexOf(QStringLiteral("%ENDSELECT%"), cursorPos + 1); if ( selectionEnd == -1 ) { selectionEnd = replacement.length(); } replacement.remove(QStringLiteral("%ENDSELECT%")); } } document->replaceText(word, replacement); if ( cursorPos != -1 ) { if (view) { replacement = replacement.left(cursorPos); KTextEditor::Cursor newPos( word.start().line() + replacement.count('\n'), word.start().column() + replacement.length() - replacement.lastIndexOf('\n') - 1 ); view->setCursorPosition(newPos); if ( selectionEnd != -1 ) { ///TODO: maybe we want to support multi-line selections in the future? view->setSelection( KTextEditor::Range( newPos, KTextEditor::Cursor( newPos.line(), newPos.column() + selectionEnd - cursorPos ) ) ); } } } } else { document->replaceText(word, m_keyword + ' '); } }
void SwapFile::removeText (const KTextEditor::Range &range) { // skip if not open if (!m_swapfile.isOpen ()) return; // format: qint8, int, int, int Q_ASSERT (range.start().line() == range.end().line()); m_stream << EA_RemoveText << range.start().line() << range.start().column() << range.end().column(); m_needSync = true; }
void KDocumentTextBuffer::localTextRemoved( KTextEditor::Document *document, const KTextEditor::Range &range, const QString& oldText ) { if ( m_aboutToClose ) return; kDebug() << "local text removed:" << kDocument() << range; emit localChangedText(range, user(), true); Q_UNUSED(document) textOpPerformed(); if( !m_user.isNull() ) { unsigned int offset = cursorToOffset_kte( range.start() ); unsigned int len = countUnicodeCharacters(oldText); blockRemoteRemove = true; kDebug() << "ERASING TEXT" << oldText << "with len" << len << "offset" << offset << "range" << range; kDebug() << offset << len << length(); if( len > 0 ) eraseText( offset, len, m_user ); else kDebug() << "0 legth delete operation. Skipping."; checkConsistency(); } else kDebug() << "Could not remove text: No local user set."; }
void PreprocessorCompletionModel::executeCompletionItem2( KTextEditor::Document* const doc , const KTextEditor::Range& word , const QModelIndex& index ) const { assert("Invalid index is not expected here!" && index.isValid()); assert("Parent index is not valid" && index.parent().isValid()); assert("Parent index must be GROUP" && index.parent().internalId() == Level::GROUP); assert("Index points to invalid item" && unsigned(index.row()) < COMPLETIONS.size()); auto text = COMPLETIONS[index.row()].text; const auto column = text.indexOf('|'); if (column != -1) text.remove(column, 1); doc->replaceText(word, text); // Try to reposition a cursor inside a current view if (column != -1) { auto pos = word.start(); pos.setColumn(pos.column() + column); doc->activeView()->setCursorPosition(pos); } }
void SnippetCompletionItem::execute( KTextEditor::View* view, const KTextEditor::Range& word ) { QMap< QString, QString > values = QMap<QString, QString>(); KTextEditor::TemplateInterface2* templateIface2 = qobject_cast<KTextEditor::TemplateInterface2*>(view); if (templateIface2) templateIface2->insertTemplateText(word.start(), m_snippet, values, m_repo->registeredScript()); }
TextRange::TextRange (TextBuffer &buffer, const KTextEditor::Range &range, InsertBehaviors insertBehavior, EmptyBehavior emptyBehavior) : m_buffer (buffer) , m_start (buffer, this, range.start(), (insertBehavior & ExpandLeft) ? Kate::TextCursor::StayOnInsert : Kate::TextCursor::MoveOnInsert) , m_end (buffer, this, range.end(), (insertBehavior & ExpandRight) ? Kate::TextCursor::MoveOnInsert : Kate::TextCursor::StayOnInsert) , m_view (0) , m_feedback (0) , m_zDepth (0.0) , m_attributeOnlyForViews (false) , m_invalidateIfEmpty (emptyBehavior == InvalidateIfEmpty) { // remember this range in buffer m_buffer.m_ranges.insert (this); // check if range now invalid, there can happen no feedback, as m_feedback == 0 checkValidity (); }
void KateViInsertMode::textInserted(KTextEditor::Document* document, KTextEditor::Range range) { if (m_isExecutingCompletion) { m_textInsertedByCompletion += document->text(range); m_textInsertedByCompletionEndPos = range.end(); } }
void TextRange::setRange (const KTextEditor::Range &range) { // avoid work if nothing changed! if (range == toRange()) return; // remember old line range int oldStartLine = m_start.line(); int oldEndLine = m_end.line(); // change start and end cursor m_start.setPosition (range.start ()); m_end.setPosition (range.end ()); // check if range now invalid, don't emit feedback here, will be handled below // otherwise you can't delete ranges in feedback! checkValidity (oldStartLine, oldEndLine, false); // no attribute or feedback set, be done if (!m_attribute && !m_feedback) return; // get full range int startLineMin = oldStartLine; if (oldStartLine == -1 || (m_start.line() != -1 && m_start.line() < oldStartLine)) startLineMin = m_start.line(); int endLineMax = oldEndLine; if (oldEndLine == -1 || m_end.line() > oldEndLine) endLineMax = m_end.line(); /** * notify buffer about attribute change, it will propagate the changes * notify right view */ m_buffer.notifyAboutRangeChange (m_view, startLineMin, endLineMax, m_attribute); // perhaps need to notify stuff! if (m_feedback) { // do this last: may delete this range if (!toRange().isValid()) m_feedback->rangeInvalid (this); else if (toRange().isEmpty()) m_feedback->rangeEmpty (this); } }
void PreprocessorCompletionModel::completionInvoked( KTextEditor::View* const view , const KTextEditor::Range& word , InvocationType /*itype*/ ) { // Reuse shouldStartCompletion to disable/enable completion m_should_complete = shouldStartCompletion(view, QString{}, false, word.end()); }
void execute(KTextEditor::View* view, const KTextEditor::Range& word) override { auto document = view->document(); auto range = word; const int lineNumber = word.end().line(); const QString line = document->line(lineNumber); const auto properties = includePathProperties(line, word.end().column()); if (!properties.valid) { return; } QString newText = includeItem.isDirectory ? (includeItem.name + QLatin1Char('/')) : includeItem.name; if (properties.inputFrom == -1) { newText.prepend(QLatin1Char('<')); } else { range.setStart({lineNumber, properties.inputFrom}); } if (properties.inputTo == -1) { // Add suffix if (properties.local) { newText += QLatin1Char('"'); } else { newText += QLatin1Char('>'); } // replace the whole line range.setEnd({lineNumber, line.size()}); } else { range.setEnd({lineNumber, properties.inputTo}); } document->replaceText(range, newText); if (includeItem.isDirectory) { // ensure we can continue to add files/paths when we just added a directory int offset = (properties.inputTo == -1) ? 1 : 0; view->setCursorPosition(range.start() + KTextEditor::Cursor(0, newText.length() - offset)); } else { // place cursor at end of line view->setCursorPosition({lineNumber, document->lineLength(lineNumber)}); } }
QSize GrepOutputDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { const GrepOutputModel *model = dynamic_cast<const GrepOutputModel *>(index.model()); const GrepOutputItem *item = model ? dynamic_cast<const GrepOutputItem *>(model->itemFromIndex(index)) : nullptr; QSize ret = QStyledItemDelegate::sizeHint(option, index); //take account of additional width required for highlighting (bold text) //and line numbers. These are not included in the default Qt size calculation. if(item && item->isText()) { QFont font = option.font; QFontMetrics metrics(font); font.setBold(true); QFontMetrics bMetrics(font); const KTextEditor::Range rng = item->change()->m_range; int width = metrics.width(item->text().left(rng.start().column())) + metrics.width(item->text().right(item->text().length() - rng.end().column())) + bMetrics.width(item->text().mid(rng.start().column(), rng.end().column() - rng.start().column())) + option.fontMetrics.width(i18n("Line %1: ",item->lineNumber())) + std::max(option.decorationSize.width(), 0); ret.setWidth(width); }else{ // This is only used for titles, so not very performance critical QString text; if(item) text = item->text(); else text = index.data().toString(); QTextDocument doc; doc.setDocumentMargin(0); doc.setHtml(text); QSize newSize = doc.size().toSize(); if(newSize.height() > ret.height()) ret.setHeight(newSize.height()); } return ret; }
/** * We have to stop \c #include completion when current line would parsed well * (i.e. contains complete \c #include expression) or have no \c #include at all. */ bool IncludeHelperCompletionModel::shouldAbortCompletion( KTextEditor::View* view , const KTextEditor::Range& range , const QString& current_completion ) { kDebug(DEBUG_AREA) << "range=" << range << ", current_completion=" << current_completion; kDebug(DEBUG_AREA) << "m_should_complete=" << m_should_complete << ", closer=" << m_closer; // Get current line const auto line = view->document()->line(range.end().line()); // Try to parse it... auto r = parseIncludeDirective(line, false); // nothing to complete for lines w/o #include const auto need_abort = !r.m_range.isValid() || range.end().column() < r.m_range.start().column() || range.end().column() > (r.m_range.end().column() + 1) ; kDebug(DEBUG_AREA) << "result=" << need_abort; return need_abort; }
bool KateAutoIndent::changeIndent (const KTextEditor::Range &range, int change) { QList<int> skippedLines; // loop over all lines given... for (int line = range.start().line () < 0 ? 0 : range.start().line (); line <= qMin (range.end().line (), doc->lines()-1); ++line) { // don't indent empty lines if (doc->line(line).isEmpty()) { skippedLines.append (line); continue; } // don't indent the last line when the cursor is on the first column if (line == range.end().line() && range.end().column() == 0) { skippedLines.append (line); continue; } doIndentRelative(line, change * indentWidth); } if (skippedLines.count() > range.numberOfLines()) { // all lines were empty, so indent them nevertheless foreach (int line, skippedLines) doIndentRelative(line, change * indentWidth); }
void AdaptSignatureAssistant::textChanged(KTextEditor::View* view, const KTextEditor::Range& invocationRange, const QString& removedText) { reset(); m_view = view; //FIXME: update signature assistant to play well with the rename assistant KTextEditor::Range sigAssistRange = invocationRange; if (!removedText.isEmpty()) { sigAssistRange.setRange(sigAssistRange.start(), sigAssistRange.start()); } m_document = view->document()->url(); DUChainReadLocker lock(DUChain::lock(), 300); if(!lock.locked()) { qCDebug(CPP) << "failed to lock duchain in time"; return; } KTextEditor::Range simpleInvocationRange = KTextEditor::Range(sigAssistRange); Declaration* funDecl = getDeclarationAtCursor(simpleInvocationRange.start(), m_document); if(!funDecl || !funDecl->type<FunctionType>()) return; if(QtFunctionDeclaration* classFun = dynamic_cast<QtFunctionDeclaration*>(funDecl)) { if (classFun->isSignal()) { // do not offer to change signature of a signal, as the implementation will be generated by moc return; } } Declaration* otherSide = 0; FunctionDefinition* definition = dynamic_cast<FunctionDefinition*>(funDecl); if (definition) { m_editingDefinition = true; otherSide = definition->declaration(); } else if ((definition = FunctionDefinition::definition(funDecl))) { m_editingDefinition = false; otherSide = definition; } if (!otherSide) return; m_otherSideContext = DUContextPointer(DUChainUtils::getFunctionContext(otherSide)); if (!m_otherSideContext) return; m_declarationName = funDecl->identifier(); m_otherSideId = otherSide->id(); m_otherSideTopContext = ReferencedTopDUContext(otherSide->topContext()); m_oldSignature = getDeclarationSignature(otherSide, m_otherSideContext.data(), true); //Schedule an update, to make sure the ranges match DUChain::self()->updateContextForUrl(m_otherSideTopContext->url(), TopDUContext::AllDeclarationsAndContexts); }
void LaTeXCompletionModel::completionInvoked(KTextEditor::View *view, const KTextEditor::Range &range, InvocationType invocationType) { if(!range.isValid() || (invocationType == AutomaticInvocation && !KileConfig::completeAuto())) { m_completionList.clear(); reset(); return; } Q_UNUSED(invocationType); m_currentView = view; KILE_DEBUG() << "building model..."; buildModel(view, range); }
void TextBuffer::removeText (const KTextEditor::Range &range) { // only allowed if editing transaction running Q_ASSERT (m_editingTransactions > 0); // only ranges on one line are supported Q_ASSERT (range.start().line() == range.end().line()); // start colum <= end column and >= 0 Q_ASSERT (range.start().column() <= range.end().column()); Q_ASSERT (range.start().column() >= 0); // skip work, if no text to remove if (range.isEmpty()) return; // get block, this will assert on invalid line int blockIndex = blockForLine (range.start().line()); // let the block handle the removeText, retrieve removed text QString text; m_blocks.at(blockIndex)->removeText (range, text); // remember changes ++m_revision; // update changed line interval if (range.start().line() < m_editingMinimalLineChanged || m_editingMinimalLineChanged == -1) m_editingMinimalLineChanged = range.start().line(); if (range.start().line() > m_editingMaximalLineChanged) m_editingMaximalLineChanged = range.start().line(); // emit signal about done change emit textRemoved (range, text); }
// Scan throughout the entire document for possible completions, // ignoring any dublets const QStringList KateWordCompletionModel::allMatches( KTextEditor::View *view, const KTextEditor::Range &range ) const { QStringList l; int i( 0 ); int pos( 0 ); KTextEditor::Document *doc = view->document(); QRegExp re( "\\b(" + doc->text( range ) + "\\w{1,})" ); QString s, m; QSet<QString> seen; while( i < doc->lines() ) { s = doc->line( i ); pos = 0; while ( pos >= 0 ) { pos = re.indexIn( s, pos ); if ( pos >= 0 ) { // typing in the middle of a word if ( ! ( i == range.start().line() && pos == range.start().column() ) ) { m = re.cap( 1 ); if ( ! seen.contains( m ) ) { seen.insert( m ); l << m; } } pos += re.matchedLength(); } } i++; } return l; }
void FunctionDeclarationCompletionItem::executed(KTextEditor::View* view, const KTextEditor::Range& word) { qCDebug(KDEV_PYTHON_CODECOMPLETION) << "FunctionDeclarationCompletionItem executed"; KTextEditor::Document* document = view->document(); auto resolvedDecl = Helper::resolveAliasDeclaration(declaration().data()); DUChainReadLocker lock; auto functionDecl = Helper::functionForCalled(resolvedDecl).declaration; lock.unlock(); if ( ! functionDecl && (! resolvedDecl || ! resolvedDecl->abstractType() || resolvedDecl->abstractType()->whichType() != AbstractType::TypeStructure) ) { qCritical(KDEV_PYTHON_CODECOMPLETION) << "ERROR: could not get declaration data, not executing completion item!"; return; } QString suffix = "()"; KTextEditor::Range checkPrefix(word.start().line(), 0, word.start().line(), word.start().column()); KTextEditor::Range checkSuffix(word.end().line(), word.end().column(), word.end().line(), document->lineLength(word.end().line())); if ( m_doNotCall || document->text(checkSuffix).trimmed().startsWith('(') || document->text(checkPrefix).trimmed().endsWith('@') || (functionDecl && Helper::findDecoratorByName(functionDecl, QLatin1String("property"))) ) { // don't insert brackets if they're already there, // the item is a decorator, or if it's an import item. suffix.clear(); } // place cursor behind bracktes by default int skip = 2; if ( functionDecl ) { bool needsArguments = false; int argumentCount = functionDecl->type<FunctionType>()->arguments().length(); if ( functionDecl->context()->type() == KDevelop::DUContext::Class ) { // it's a member function, so it has the implicit self // TODO static methods needsArguments = argumentCount > 1; } else { // it's a free function needsArguments = argumentCount > 0; } if ( needsArguments ) { // place cursor in brackets if there's parameters skip = 1; } } document->replaceText(word, declaration()->identifier().toString() + suffix); view->setCursorPosition( Cursor(word.end().line(), word.end().column() + skip) ); }
void IncludeHelperCompletionModel::completionInvoked( KTextEditor::View* view , const KTextEditor::Range& range , InvocationType ) { auto* doc = view->document(); kDebug(DEBUG_AREA) << range << ", " << doc->text(range); const auto& t = doc->line(range.start().line()).left(range.start().column()); kDebug(DEBUG_AREA) << "text to parse: " << t; auto r = parseIncludeDirective(t, false); if (r.m_range.isValid()) { m_should_complete = range.start().column() >= r.m_range.start().column() && range.start().column() <= r.m_range.end().column(); if (m_should_complete) { r.m_range.setBothLines(range.start().line()); kDebug(DEBUG_AREA) << "parsed range: " << r.m_range; m_closer = r.close_char(); updateCompletionList(doc->text(r.m_range), r.m_type == IncludeStyle::local); } } }
void NormalDeclarationCompletionItem::execute(KTextEditor::View* view, const KTextEditor::Range& word) { if( m_completionContext && m_completionContext->depth() != 0 ) return; //Do not replace any text when it is an argument-hint KTextEditor::Document* document = view->document(); QString newText; { KDevelop::DUChainReadLocker lock(KDevelop::DUChain::lock()); if(m_declaration) { newText = declarationName(); } else { qCDebug(LANGUAGE) << "Declaration disappeared"; return; } } document->replaceText(word, newText); KTextEditor::Range newRange = word; newRange.setEnd(KTextEditor::Cursor(newRange.end().line(), newRange.start().column() + newText.length())); executed(view, newRange); }
void KDocumentTextBuffer::localTextInserted( KTextEditor::Document *document, const KTextEditor::Range &range ) { if ( m_aboutToClose ) return; emit localChangedText(range, user(), false); Q_UNUSED(document) textOpPerformed(); if( m_user.isNull() ) { kDebug() << "Could not insert text: No local user set."; return; } unsigned int offset = cursorToOffset_kte(range.start()); kDebug() << "local text inserted" << kDocument() << "( range" << range << ")" << m_user << "offset:" << offset; QInfinity::TextChunk chunk(encoding()); QString text = kDocument()->text(range); #ifdef ENABLE_TAB_HACK if ( text.contains('\t') ) { text = text.replace('\t', " "); kDocument()->blockSignals(true); kDocument()->replaceText(range, text); kDocument()->blockSignals(false); } #endif Q_ASSERT(encoder()); if ( text.isEmpty() ) { kDebug() << "Skipping empty insert."; return; } QByteArray encodedText = codec()->fromUnicode( text ); if ( encodedText.size() == 0 ) { kDebug() << "Got empty encoded text from non empty string " "Skipping insertion"; } else { chunk.insertText( 0, encodedText, countUnicodeCharacters(text), m_user->id() ); blockRemoteInsert = true; kDebug() << "inserting chunk of size" << chunk.length() << "into local buffer" << kDocument()->url(); insertChunk( offset, chunk, m_user ); kDebug() << "done inserting chunk"; checkConsistency(); } }