Skip to content

Commit c15e18a

Browse files
committed
Brought in the prev parameter for fim_ctx_local
... as used by llama.vim. I had to drop the `indent` part since it was not working that good. Subjectively the user experience dropped.
1 parent 4431185 commit c15e18a

2 files changed

Lines changed: 97 additions & 51 deletions

File tree

llamaplugin.cpp

Lines changed: 80 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -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

773803
QStringList 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+
784820
void LlamaPlugin::pick_chunk(const QStringList &text, bool noModifiedState, bool doEviction)
785821
{
786822
if (settings().ringNChunks.value() <= 0)

llamaplugin.h

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ private slots:
4949
void settingsUpdated();
5050

5151
// Completion handling
52-
void fim(int pos_x, int pos_y, bool isAuto = false);
52+
void fim(int pos_x, int pos_y, bool isAuto = false, const QStringList& prev = {});
5353
void fim_on_response(int pos_x, int pos_y, const QByteArray &hash, const QByteArray &response);
5454
void fim_try_hint(int pos_x, int pos_y);
5555
void fim_render(TextEditor::TextEditorWidget *editor,
@@ -59,18 +59,27 @@ private slots:
5959
void hideCompletionHint();
6060

6161
// Context management
62-
using ThreeQStrings = std::tuple<QString, QString, QString>;
63-
64-
ThreeQStrings fim_ctx_local(TextEditor::TextEditorWidget *editor,
65-
int pos_x,
66-
int pos_y,
67-
const QByteArray &prev = QByteArray());
62+
struct FimContext
63+
{
64+
QString prefix;
65+
QString middle;
66+
QString suffix;
67+
QString line_cur;
68+
QString line_cur_prefix;
69+
QString line_cur_suffix;
70+
};
71+
FimContext fim_ctx_local(TextEditor::TextEditorWidget *editor,
72+
int pos_x,
73+
int pos_y,
74+
const QStringList &prev = {});
6875
void pick_chunk(const QStringList &text, bool noModifiedState, bool doEviction);
6976
void ring_update();
7077
void pick_chunk_at_cursor(TextEditor::TextEditorWidget *editor);
7178

79+
using ThreeQStrings = std::tuple<QString, QString, QString>;
7280
ThreeQStrings getShowInfoStats(const QJsonObject &response);
7381
QStringList getlines(TextEditor::TextEditorWidget *editor, int startLine, int endLine);
82+
QString getline(TextEditor::TextEditorWidget *editor, int line);
7483

7584
QHash<QByteArray, QByteArray> m_cacheData;
7685
QHash<Utils::FilePath, int> m_lastEditLineHash;
@@ -95,6 +104,7 @@ private slots:
95104

96105
// Editor tracking
97106
std::unique_ptr<TextEditor::TextMark> m_textMark;
107+
QStringList m_suggestionContent;
98108
};
99109

100110
} // namespace LlamaCpp::Internal

0 commit comments

Comments
 (0)