add markdown parsing + latex + tree-sitter highlighting for codeblocks in llm responses
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QVariantList>
|
||||
#include <QtQml>
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
class QQmlEngine;
|
||||
class QJSEngine;
|
||||
|
||||
namespace ZShell::llm {
|
||||
|
||||
// Syntax highlighting for LLM code blocks via tree-sitter.
|
||||
//
|
||||
// The tree-sitter runtime is linked. Grammar libraries
|
||||
// (libtree-sitter-<lang>.so) are dlopen()'d lazily, so a missing
|
||||
// grammar package degrades that language to plain text instead of
|
||||
// breaking the build or the app. Highlight queries are embedded at
|
||||
// build time (highlight-queries/*.scm, vendored from the grammar
|
||||
// repos, MIT).
|
||||
//
|
||||
// The fence language the LLM wrote (```cpp, ```python, ...) is mapped
|
||||
// to a grammar through an alias table.
|
||||
//
|
||||
// highlight() returns a list of span maps:
|
||||
// { "start": int, "length": int, "kind": QString }
|
||||
// where kind is a semantic role (keyword, string, comment, number,
|
||||
// function, type, ...) that QML maps to theme colors. An empty list
|
||||
// means "no highlighting" (unknown language or grammar not installed).
|
||||
class CodeHighlighter : public QObject {
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
|
||||
public:
|
||||
Q_INVOKABLE QVariantList highlight(const QString& code, const QString& language) const;
|
||||
|
||||
static CodeHighlighter* create(QQmlEngine*, QJSEngine*);
|
||||
|
||||
struct Grammar {
|
||||
QString lib; // soname to dlopen
|
||||
QString symbol; // tree_sitter_<lang>() entry point
|
||||
// Candidate query sources, first wins. Lets typescript reuse
|
||||
// the javascript query when it compiles against the grammar.
|
||||
std::vector<int> queries; // indices into querySources()
|
||||
};
|
||||
|
||||
private:
|
||||
struct State {
|
||||
bool bad = false; // permanent failure, do not retry
|
||||
void* lib = nullptr;
|
||||
const void* lang = nullptr; // const TSLanguage*
|
||||
void* query = nullptr; // TSQuery*
|
||||
};
|
||||
|
||||
[[nodiscard]] static const QHash<QString, QString>& aliases();
|
||||
[[nodiscard]] const std::vector<std::string>& querySources() const;
|
||||
// Maps a tree-sitter capture name to a role index (0 = unstyled).
|
||||
[[nodiscard]] static uint8_t roleFor(const char* name, uint32_t length);
|
||||
[[nodiscard]] static const char* roleName(uint8_t role);
|
||||
|
||||
mutable QHash<QString, State> m_states;
|
||||
static CodeHighlighter* s_instance;
|
||||
};
|
||||
|
||||
} // namespace ZShell::llm
|
||||
Reference in New Issue
Block a user