convert the following vim scripting function to Qt c++:
" render a suggestion at the current cursor location
function! s:fim_render(pos_x, pos_y, data)
" do not show if there is a completion in progress
if pumvisible()
return
endif
let l:raw = a:data
let l:can_accept = v:true
let l:has_info = v:false
let l:n_prompt = 0
let l:t_prompt_ms = 1.0
let l:s_prompt = 0
let l:n_predict = 0
let l:t_predict_ms = 1.0
let l:s_predict = 0
let l:content = []
" get the generated suggestion
if l:can_accept
let l:response = json_decode(l:raw)
for l:part in split(get(l:response, 'content', ''), "\n", 1)
call add(l:content, l:part)
endfor
" remove trailing new lines
while len(l:content) > 0 && l:content[-1] == ""
call remove(l:content, -1)
endwhile
let l:n_cached = get(l:response, 'tokens_cached', 0)
let l:truncated = get(l:response, 'timings/truncated', v:false)
" if response.timings is available
if has_key(l:response, 'timings/prompt_n') && has_key(l:response, 'timings/prompt_ms') && has_key(l:response, 'timings/prompt_per_second')
\ && has_key(l:response, 'timings/predicted_n') && has_key(l:response, 'timings/predicted_ms') && has_key(l:response, 'timings/predicted_per_second')
let l:n_prompt = get(l:response, 'timings/prompt_n', 0)
let l:t_prompt_ms = get(l:response, 'timings/prompt_ms', 1)
let l:s_prompt = get(l:response, 'timings/prompt_per_second', 0)
let l:n_predict = get(l:response, 'timings/predicted_n', 0)
let l:t_predict_ms = get(l:response, 'timings/predicted_ms', 1)
let l:s_predict = get(l:response, 'timings/predicted_per_second', 0)
endif
let l:has_info = v:true
endif
if len(l:content) == 0
call add(l:content, "")
let l:can_accept = v:false
endif
let l:pos_x = a:pos_x
let l:pos_y = a:pos_y
let l:line_cur = getline(l:pos_y)
" if the current line is full of whitespaces, trim as much whitespaces from the suggestion
if match(l:line_cur, '^\s*$') >= 0
let l:lead = min([strlen(matchstr(l:content[0], '^\s*')), strlen(l:line_cur)])
let l:line_cur = strpart(l:content[0], 0, l:lead)
let l:content[0] = strpart(l:content[0], l:lead)
endif
let l:line_cur_prefix = strpart(l:line_cur, 0, l:pos_x)
let l:line_cur_suffix = strpart(l:line_cur, l:pos_x)
" NOTE: the following is logic for discarding predictions that repeat existing text
" the code is quite ugly and there is very likely a simpler and more canonical way to implement this
"
" still, I wonder if there is some better way that avoids having to do these special hacks?
" on one hand, the LLM 'sees' the contents of the file before we start editing, so it is normal that it would
" start generating whatever we have given it via the extra context. but on the other hand, it's not very
" helpful to re-generate the same code that is already there
" truncate the suggestion if the first line is empty
if len(l:content) == 1 && l:content[0] == ""
let l:content = [""]
endif
" ... and the next lines are repeated
if len(l:content) > 1 && l:content[0] == "" && l:content[1:] == getline(l:pos_y + 1, l:pos_y + len(l:content) - 1)
let l:content = [""]
endif
" truncate the suggestion if it repeats the suffix
if len(l:content) == 1 && l:content[0] == l:line_cur_suffix
let l:content = [""]
endif
" find the first non-empty line (strip whitespace)
let l:cmp_y = l:pos_y + 1
while l:cmp_y < line('$') && getline(l:cmp_y) =~? '^\s*$'
let l:cmp_y += 1
endwhile
if (l:line_cur_prefix . l:content[0]) == getline(l:cmp_y)
" truncate the suggestion if it repeats the next line
if len(l:content) == 1
let l:content = [""]
endif
" ... or if the second line of the suggestion is the prefix of line l:cmp_y + 1
if len(l:content) == 2 && l:content[-1] == getline(l:cmp_y + 1)[:len(l:content[-1]) - 1]
let l:content = [""]
endif
" ... or if the middle chunk of lines of the suggestion is the same as [l:cmp_y + 1, l:cmp_y + len(l:content) - 1)
if len(l:content) > 2 && join(l:content[1:-1], "\n") == join(getline(l:cmp_y + 1, l:cmp_y + len(l:content) - 1), "\n")
let l:content = [""]
endif
endif
let l:content[-1] .= l:line_cur_suffix
" if only whitespaces - do not accept
if join(l:content, "\n") =~? '^\s*$'
let l:can_accept = v:false
endif
" display virtual text with the suggestion
let l:bufnr = bufnr('%')
let l:info = ''
let s:hint_shown = v:true
let s:fim_data['pos_x'] = l:pos_x
let s:fim_data['pos_y'] = l:pos_y
let s:fim_data['line_cur'] = l:line_cur
let s:fim_data['can_accept'] = l:can_accept
let s:fim_data['content'] = l:content
endfunction
Here’s the C++ equivalent of your Vim script function using Qt:
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QStringList>
#include <QRegularExpression>
#include <QTextBlock>
#include <QDebug>
class FimRenderer {
private:
bool m_hintShown = false;
struct FimData {
int pos_x = 0;
int pos_y = 0;
QString line_cur;
bool can_accept = false;
QStringList content;
} m_fim_data;
public:
void fim_render(int pos_x, int pos_y, const QString& data) {
// do not show if there is a completion in progress
// Assuming we have some way to check if completion is visible
if (is_completion_visible()) {
return;
}
QString raw = data;
bool can_accept = true;
bool has_info = false;
int n_prompt = 0;
double t_prompt_ms = 1.0;
double s_prompt = 0;
int n_predict = 0;
double t_predict_ms = 1.0;
double s_predict = 0;
QStringList content;
// get the generated suggestion
if (can_accept) {
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(raw.toUtf8(), &error);
if (error.error != QJsonParseError::NoError) {
= false;
can_accept << "";
content return;
}
QJsonObject response = doc.object();
// Extract content
QString content_str = response.value("content").toString();
= content_str.split('\n', Qt::SkipEmptyParts);
content
// Remove trailing new lines
while (!content.isEmpty() && content.back().isEmpty()) {
.removeLast();
content}
int n_cached = response.value("tokens_cached").toInt(0);
bool truncated = response.value("timings/truncated").toBool(false);
// Check if timings data is available
if (response.contains("timings/prompt_n") &&
.contains("timings/prompt_ms") &&
response.contains("timings/prompt_per_second") &&
response.contains("timings/predicted_n") &&
response.contains("timings/predicted_ms") &&
response.contains("timings/predicted_per_second")) {
response
= response.value("timings/prompt_n").toInt(0);
n_prompt = response.value("timings/prompt_ms").toDouble(1.0);
t_prompt_ms s_prompt = response.value("timings/prompt_per_second").toDouble(0);
= response.value("timings/predicted_n").toInt(0);
n_predict = response.value("timings/predicted_ms").toDouble(1.0);
t_predict_ms s_predict = response.value("timings/predicted_per_second").toDouble(0);
}
= true;
has_info }
if (content.isEmpty()) {
<< "";
content = false;
can_accept }
int line_cur_pos_y = pos_y; // This would come from actual editor context
QString line_cur = get_line_content(line_cur_pos_y); // Implement based on your editor
// if the current line is full of whitespaces, trim as much whitespaces from the suggestion
QRegularExpression whitespace_pattern("^\\s*$");
if (whitespace_pattern.match(line_cur).hasMatch()) {
int lead = qMin(line_cur.length(), content.isEmpty() ? 0 : content.first().length());
QString line_cur_trimmed = line_cur.left(lead);
if (!content.isEmpty()) {
[0] = content[0].mid(lead);
content}
}
QString line_cur_prefix = line_cur.left(pos_x);
QString line_cur_suffix = line_cur.mid(pos_x);
// Logic for discarding predictions that repeat existing text
// truncate the suggestion if the first line is empty
if (content.size() == 1 && content[0].isEmpty()) {
= QStringList{""};
content }
// ... and the next lines are repeated
if (content.size() > 1 && content[0].isEmpty() &&
.mid(1) == get_lines_content(line_cur_pos_y + 1, line_cur_pos_y + content.size() - 1)) {
content= QStringList{""};
content }
// truncate the suggestion if it repeats the suffix
if (content.size() == 1 && content[0] == line_cur_suffix) {
= QStringList{""};
content }
// Find the first non-empty line
int cmp_y = line_cur_pos_y + 1;
while (cmp_y < get_total_lines() &&
(cmp_y).trimmed().isEmpty()) {
get_line_content++;
cmp_y}
if (!content.isEmpty() &&
(line_cur_prefix + content[0]) == get_line_content(cmp_y)) {
// truncate the suggestion if it repeats the next line
if (content.size() == 1) {
= QStringList{""};
content }
// ... or if the second line of the suggestion is the prefix of line cmp_y + 1
if (content.size() == 2 &&
.back() == get_line_content(cmp_y + 1).left(content.back().length())) {
content= QStringList{""};
content }
// ... or if the middle chunk of lines of the suggestion is the same as [cmp_y + 1, cmp_y + content.size() - 1)
if (content.size() > 2 &&
.mid(1).join("\n") == get_lines_content(cmp_y + 1, cmp_y + content.size() - 1).join("\n")) {
content= QStringList{""};
content }
}
// Add suffix to last line
if (!content.isEmpty()) {
.back() += line_cur_suffix;
content}
// if only whitespaces - do not accept
QString combined_content = content.join("\n");
QRegularExpression whitespace_only_pattern("^\\s*$");
if (whitespace_only_pattern.match(combined_content).hasMatch()) {
= false;
can_accept }
// display virtual text with the suggestion
m_fim_data.pos_x = pos_x;
m_fim_data.pos_y = pos_y;
m_fim_data.line_cur = line_cur;
m_fim_data.can_accept = can_accept;
m_fim_data.content = content;
m_hintShown = true;
}
private:
bool is_completion_visible() {
// Implement based on your Qt editor implementation
return false; // Placeholder
}
QString get_line_content(int line_number) {
// Implement based on your Qt editor implementation
return ""; // Placeholder
}
QStringList get_lines_content(int start_line, int end_line) {
// Implement based on your Qt editor implementation
return QStringList(); // Placeholder
}
int get_total_lines() {
// Implement based on your Qt editor implementation
return 0; // Placeholder
}
};
Key differences from the original Vim script:
QStringList
instead of Vim lists, QJsonObject
for JSON handling instead
of json_decode
QRegularExpression
instead of Vim’s regex matchingget_line_content
, etc.) need to be implemented
based on your specific Qt-based editor setupImportant Notes:
get_line_content()
, get_lines_content()
, and
get_total_lines()
based on your Qt editor
implementationis_completion_visible()
method needs to be
implemented to detect if there’s an active completion popupQJsonDocument
which is more
robust than Vim’s JSON handling