@@ -260,16 +260,12 @@ static double chunk_sim(const QStringList &c0, const QStringList &c1)
260260 return 2.0 * common / (c0.size () + c1.size ());
261261}
262262
263- void LlamaPlugin::fim (int pos_x, int pos_y, bool isAuto)
263+ void LlamaPlugin::fim (int pos_x, int pos_y, bool isAuto, const QStringList &prev )
264264{
265265 TextEditorWidget *editor = TextEditorWidget::currentTextEditorWidget ();
266266 if (!editor)
267267 return ;
268268
269- MultiTextCursor cursor = editor->multiTextCursor ();
270- if (cursor.hasMultipleCursors () || cursor.hasSelection () || editor->suggestionVisible ())
271- return ;
272-
273269 QTextDocument *currentDocument = editor->document ();
274270 if (!currentDocument)
275271 return ;
@@ -280,22 +276,26 @@ void LlamaPlugin::fim(int pos_x, int pos_y, bool isAuto)
280276 pos_y = cursor.blockNumber () + 1 ;
281277 }
282278
283- qCInfo (llamaLog) << " fim:" << pos_x << pos_y;
279+ qCInfo (llamaLog) << " fim:" << pos_x << pos_y << (prev. isEmpty () ? " " : " previous content! " ) ;
284280
285281 // avoid sending repeated requests too fast
286282 if (m_fimReply && m_fimReply->isRunning ()) {
287283 qCInfo (llamaLog) << " fim:" << pos_x << pos_y
288284 << " There is a fim network request is in progress, re-trying in 100ms" ;
289- QTimer::singleShot (100 , [this , isAuto]() {
285+ QTimer::singleShot (100 , [this , isAuto, prev ]() {
290286 // If the cursor has been moved, use the the actual cursor postion
291287 // for a more up to date fim call.
292- fim (-1 , -1 , isAuto);
288+ fim (-1 , -1 , isAuto, prev );
293289 });
294290 return ;
295291 }
296292
297293 // Get local context
298- auto [prefix, middle, suffix] = fim_ctx_local (editor, pos_x, pos_y);
294+ auto [prefix, middle, suffix, line_cur, line_cur_prefix, line_cur_suffix]
295+ = fim_ctx_local (editor, pos_x, pos_y, prev);
296+
297+ if (isAuto && line_cur_suffix.size () > settings ().maxLineSuffix .value ())
298+ return ;
299299
300300 // Check cache first
301301 const QByteArray hash = QCryptographicHash::hash ((prefix + middle + " Î" + suffix).toUtf8 (),
@@ -342,8 +342,8 @@ void LlamaPlugin::fim(int pos_x, int pos_y, bool isAuto)
342342 request[" samplers" ] = QJsonArray::fromStringList ({" top_k" , " top_p" , " infill" });
343343 request[" cache_prompt" ] = true ;
344344 request[" t_max_prompt_ms" ] = settings ().tMaxPromptMs .value ();
345- request[ " t_max_predict_ms " ]
346- = isAuto ? 250 : settings ().tMaxPredictMs .value (); // Faster for auto completion
345+ // the first request is quick - we will launch a speculative request after this one is displayed
346+ request[ " t_max_predict_ms " ] = prev. isEmpty () ? 250 : settings ().tMaxPredictMs .value ();
347347 request[" response_fields" ] = QJsonArray::fromStringList ({" content" ,
348348 " timings/prompt_n" ,
349349 " timings/prompt_ms" ,
@@ -390,7 +390,7 @@ void LlamaPlugin::fim(int pos_x, int pos_y, bool isAuto)
390390
391391 // Add extra context
392392 QJsonArray extraContext;
393- for (const Chunk &chunk : m_ringChunks) {
393+ for (const Chunk &chunk : std::as_const ( m_ringChunks) ) {
394394 QJsonObject chunkObj;
395395 chunkObj[" text" ] = chunk.str ;
396396 chunkObj[" time" ] = chunk.time .toString (Qt::ISODate);
@@ -549,7 +549,8 @@ void LlamaPlugin::fim_try_hint(int pos_x, int pos_y)
549549 if (!editor || editor->isReadOnly () || editor->multiTextCursor ().hasMultipleCursors ())
550550 return ;
551551
552- auto [prefix, middle, suffix] = fim_ctx_local (editor, pos_x, pos_y, {});
552+ auto [prefix, middle, suffix, line_cur, line_cur_prefix, line_cur_suffix]
553+ = fim_ctx_local (editor, pos_x, pos_y);
553554
554555 QString context = prefix + middle + " Î" + suffix;
555556 QByteArray hash = QCryptographicHash::hash (context.toUtf8 (), QCryptographicHash::Sha256).toHex ();
@@ -609,7 +610,7 @@ void LlamaPlugin::fim_try_hint(int pos_x, int pos_y)
609610
610611 if (editor->suggestionVisible ()) {
611612 // Call speculative FIM
612- fim (pos_x, pos_y, true );
613+ fim (pos_x, pos_y, true , m_suggestionContent );
613614 }
614615 }
615616}
@@ -621,7 +622,7 @@ void LlamaPlugin::fim_render(TextEditorWidget *editor,
621622 const QByteArray &response)
622623{
623624 // do not show if there is a completion in progress
624- if (editor->suggestionVisible ())
625+ if (editor->suggestionVisible () || !editor-> selectedText (). isEmpty () )
625626 return ;
626627
627628 // Parse JSON response
@@ -671,6 +672,8 @@ void LlamaPlugin::fim_render(TextEditorWidget *editor,
671672 }
672673
673674 if (can_accept) {
675+ // Text:positionInText has 1 based line and column values
676+ int currentIntPos = Text::positionInText (editor->document (), pos_y, pos_x + 1 );
674677 TextSuggestion::Data data;
675678 Text::Position currentPos = Text::Position::fromPositionInDocument (editor->document (),
676679 currentIntPos);
@@ -698,6 +701,8 @@ void LlamaPlugin::fim_render(TextEditorWidget *editor,
698701 editor->insertSuggestion (
699702 std::make_unique<TextEditor::TextSuggestion>(data, editor->document ()));
700703
704+ m_suggestionContent = content;
705+
701706 qCInfo (llamaLog) << " fim_render:" << pos_x << pos_y
702707 << " Prepared suggestion:" << content_str;
703708 }
@@ -725,49 +730,74 @@ void LlamaPlugin::hideCompletionHint()
725730 editor->clearSuggestion ();
726731}
727732
728- LlamaPlugin::ThreeQStrings LlamaPlugin::fim_ctx_local (TextEditorWidget *editor,
729- int pos_x,
730- int pos_y,
731- const QByteArray &prev)
733+ LlamaPlugin::FimContext LlamaPlugin::fim_ctx_local (TextEditorWidget *editor,
734+ int pos_x,
735+ int pos_y,
736+ const QStringList &prev)
732737{
733738 QTextDocument *document = editor->document ();
739+ int max_y = document->lineCount ();
734740
735- QTextBlock block = document->findBlockByNumber (pos_y - 1 );
736- if (!block.isValid ())
737- return {};
741+ QString lineCur;
742+ QString lineCurPrefix;
743+ QString lineCurSuffix;
744+ QStringList linesPrefix;
745+ QStringList linesSuffix;
738746
739- QString lineCur = block. text ();
740- QString lineCurPrefix = lineCur. left (pos_x);
741- QString lineCurSuffix = lineCur. mid (pos_x );
747+ if (prev. isEmpty ()) {
748+ // No previous completion
749+ lineCur = getline (editor, pos_y - 1 );
742750
743- if (lineCurSuffix. size () > settings (). maxLineSuffix . value ())
744- return {} ;
751+ lineCurPrefix = lineCur. left (pos_x);
752+ lineCurSuffix = lineCur. mid (pos_x) ;
745753
746- // Get prefix lines
747- QStringList linesPrefix;
748- int startLine = qMax (1 , pos_y - settings ().nPrefix .value ());
749- for (int i = startLine; i < pos_y; ++i) {
750- QTextBlock b = document->findBlockByNumber (i - 1 );
751- if (b.isValid ()) {
752- linesPrefix.append (b.text ());
753- }
754- }
754+ int startLine = qMax (1 , pos_y - settings ().nPrefix .value ());
755+ for (int i = startLine; i < pos_y; ++i)
756+ linesPrefix << getline (editor, i - 1 );
755757
756- // Get suffix lines
757- QStringList linesSuffix;
758- int endLine = qMin (document->lineCount (), pos_y + settings ().nSuffix .value ());
759- for (int i = pos_y + 1 ; i <= endLine; ++i) {
760- QTextBlock b = document->findBlockByNumber (i - 1 );
761- if (b.isValid ()) {
762- linesSuffix.append (b.text ());
758+ int endLine = qMin (max_y, pos_y + settings ().nSuffix .value ());
759+ for (int i = pos_y + 1 ; i <= endLine; ++i)
760+ linesSuffix << getline (editor, i - 1 );
761+ } else {
762+ // With previous completion
763+ if (prev.size () == 1 )
764+ lineCur = getline (editor, pos_y - 1 ) + prev.first ();
765+ else
766+ lineCur = prev.last (); // Use the last item of prev as current line
767+
768+ lineCurPrefix = lineCur;
769+ lineCurSuffix.clear ();
770+
771+ int startLine = qMax (1 , pos_y - settings ().nPrefix .value () + prev.size () - 1 );
772+ for (int i = startLine; i < pos_y; ++i)
773+ linesPrefix << getline (editor, i - 1 );
774+
775+ // Add modified previous lines to prefix
776+ if (prev.size () > 1 ) {
777+ linesPrefix << getline (editor, pos_y - 1 ) + prev.first ();
778+ for (int i = 1 ; i < prev.size () - 1 ; ++i) {
779+ linesPrefix << prev[i];
780+ }
763781 }
782+
783+ int endLine = qMin (max_y, pos_y + settings ().nSuffix .value ());
784+ for (int i = pos_y + 1 ; i <= endLine; ++i)
785+ linesSuffix << getline (editor, i - 1 );
764786 }
765787
766788 const QString prefix = linesPrefix.join (" \n " ) + " \n " ;
767789 const QString middle = lineCurPrefix;
768790 const QString suffix = lineCurSuffix + " \n " + linesSuffix.join (" \n " ) + " \n " ;
769791
770- return {prefix, middle, suffix};
792+ FimContext res;
793+ res.prefix = prefix;
794+ res.middle = middle;
795+ res.suffix = suffix;
796+ res.line_cur = lineCur;
797+ res.line_cur_prefix = lineCurPrefix;
798+ res.line_cur_suffix = lineCurSuffix;
799+
800+ return res;
771801}
772802
773803QStringList LlamaPlugin::getlines (TextEditorWidget *editor, int startLine, int endLine)
@@ -781,6 +811,12 @@ QStringList LlamaPlugin::getlines(TextEditorWidget *editor, int startLine, int e
781811 return lines;
782812}
783813
814+ QString LlamaPlugin::getline (TextEditorWidget *editor, int line)
815+ {
816+ QTextBlock block = editor->document ()->findBlockByNumber (line);
817+ return block.isValid () ? block.text () : QString ();
818+ }
819+
784820void LlamaPlugin::pick_chunk (const QStringList &text, bool noModifiedState, bool doEviction)
785821{
786822 if (settings ().ringNChunks .value () <= 0 )
0 commit comments