mirror of https://github.com/nomic-ai/gpt4all
Add new solution for context links that does not force regular markdown (#938)
in responses which is disruptive to code completions in responses.pull/805/head^2
parent
d3ba1295a7
commit
a9c2f47303
@ -0,0 +1,109 @@
|
||||
#include "responsetext.h"
|
||||
|
||||
#include <QTextCharFormat>
|
||||
#include <QTextDocument>
|
||||
#include <QTextDocumentFragment>
|
||||
#include <QFontMetricsF>
|
||||
#include <QTextTableCell>
|
||||
|
||||
SyntaxHighlighter::SyntaxHighlighter(QObject *parent)
|
||||
: QSyntaxHighlighter(parent)
|
||||
{
|
||||
}
|
||||
|
||||
SyntaxHighlighter::~SyntaxHighlighter()
|
||||
{
|
||||
}
|
||||
|
||||
void SyntaxHighlighter::highlightBlock(const QString &text)
|
||||
{
|
||||
for (const HighlightingRule &rule : qAsConst(m_highlightingRules)) {
|
||||
QRegularExpressionMatchIterator matchIterator = rule.pattern.globalMatch(text);
|
||||
while (matchIterator.hasNext()) {
|
||||
QRegularExpressionMatch match = matchIterator.next();
|
||||
setFormat(match.capturedStart(), match.capturedLength(), rule.format);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ResponseText::ResponseText(QObject *parent)
|
||||
: QObject{parent}
|
||||
, m_textDocument(nullptr)
|
||||
, m_syntaxHighlighter(new SyntaxHighlighter(this))
|
||||
, m_isProcessingText(false)
|
||||
{
|
||||
}
|
||||
|
||||
QQuickTextDocument* ResponseText::textDocument() const
|
||||
{
|
||||
return m_textDocument;
|
||||
}
|
||||
|
||||
void ResponseText::setTextDocument(QQuickTextDocument* textDocument)
|
||||
{
|
||||
if (m_textDocument)
|
||||
disconnect(m_textDocument->textDocument(), &QTextDocument::contentsChanged, this, &ResponseText::handleTextChanged);
|
||||
|
||||
m_textDocument = textDocument;
|
||||
m_syntaxHighlighter->setDocument(m_textDocument->textDocument());
|
||||
connect(m_textDocument->textDocument(), &QTextDocument::contentsChanged, this, &ResponseText::handleTextChanged);
|
||||
}
|
||||
|
||||
QString ResponseText::getLinkAtPosition(int position) const
|
||||
{
|
||||
int i = 0;
|
||||
for (const auto &link : m_links) {
|
||||
if (position >= link.startPos && position < link.endPos)
|
||||
return link.href;
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
void ResponseText::handleTextChanged()
|
||||
{
|
||||
if (!m_textDocument || m_isProcessingText)
|
||||
return;
|
||||
|
||||
m_isProcessingText = true;
|
||||
QTextDocument* doc = m_textDocument->textDocument();
|
||||
QTextCursor cursor(doc);
|
||||
|
||||
QTextCharFormat linkFormat;
|
||||
linkFormat.setForeground(m_linkColor);
|
||||
linkFormat.setFontUnderline(true);
|
||||
|
||||
// Loop through the document looking for context links
|
||||
QRegularExpression re("\\[Context\\]\\((context://\\d+)\\)");
|
||||
QRegularExpressionMatchIterator i = re.globalMatch(doc->toPlainText());
|
||||
|
||||
QList<QRegularExpressionMatch> matches;
|
||||
while (i.hasNext())
|
||||
matches.append(i.next());
|
||||
|
||||
QVector<ContextLink> newLinks;
|
||||
|
||||
// Calculate new positions and store them in newLinks
|
||||
int positionOffset = 0;
|
||||
for(const auto &match : matches) {
|
||||
ContextLink newLink;
|
||||
newLink.href = match.captured(1);
|
||||
newLink.text = "Context";
|
||||
newLink.startPos = match.capturedStart() - positionOffset;
|
||||
newLink.endPos = newLink.startPos + newLink.text.length();
|
||||
newLinks.append(newLink);
|
||||
positionOffset += match.capturedLength() - newLink.text.length();
|
||||
}
|
||||
|
||||
// Replace the context links with the word "Context" in reverse order
|
||||
for(int index = matches.count() - 1; index >= 0; --index) {
|
||||
cursor.setPosition(matches.at(index).capturedStart());
|
||||
cursor.setPosition(matches.at(index).capturedEnd(), QTextCursor::KeepAnchor);
|
||||
cursor.removeSelectedText();
|
||||
cursor.setCharFormat(linkFormat);
|
||||
cursor.insertText(newLinks.at(index).text);
|
||||
cursor.setCharFormat(QTextCharFormat());
|
||||
}
|
||||
|
||||
m_links = newLinks;
|
||||
m_isProcessingText = false;
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
#ifndef RESPONSETEXT_H
|
||||
#define RESPONSETEXT_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
#include <QQuickTextDocument>
|
||||
#include <QSyntaxHighlighter>
|
||||
#include <QRegularExpression>
|
||||
|
||||
struct HighlightingRule
|
||||
{
|
||||
QRegularExpression pattern;
|
||||
QTextCharFormat format;
|
||||
};
|
||||
|
||||
class SyntaxHighlighter : public QSyntaxHighlighter {
|
||||
Q_OBJECT
|
||||
public:
|
||||
SyntaxHighlighter(QObject *parent);
|
||||
~SyntaxHighlighter();
|
||||
|
||||
void highlightBlock(const QString &text) override;
|
||||
|
||||
private:
|
||||
QVector<HighlightingRule> m_highlightingRules;
|
||||
};
|
||||
|
||||
struct ContextLink {
|
||||
int startPos = -1;
|
||||
int endPos = -1;
|
||||
QString text;
|
||||
QString href;
|
||||
};
|
||||
|
||||
class ResponseText : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QQuickTextDocument* textDocument READ textDocument WRITE setTextDocument NOTIFY textDocumentChanged())
|
||||
QML_ELEMENT
|
||||
public:
|
||||
explicit ResponseText(QObject *parent = nullptr);
|
||||
|
||||
QQuickTextDocument* textDocument() const;
|
||||
void setTextDocument(QQuickTextDocument* textDocument);
|
||||
|
||||
Q_INVOKABLE void setLinkColor(const QColor &c) { m_linkColor = c; }
|
||||
Q_INVOKABLE QString getLinkAtPosition(int position) const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void textDocumentChanged();
|
||||
|
||||
private Q_SLOTS:
|
||||
void handleTextChanged();
|
||||
|
||||
private:
|
||||
QQuickTextDocument *m_textDocument;
|
||||
SyntaxHighlighter *m_syntaxHighlighter;
|
||||
QVector<ContextLink> m_links;
|
||||
QColor m_linkColor;
|
||||
bool m_isProcessingText = false;
|
||||
};
|
||||
|
||||
#endif // RESPONSETEXT_H
|
Loading…
Reference in New Issue