ollie/kde/kate/ollie_ghost.h

123 lines
3.8 KiB
C++

#ifndef OLLIE_GHOST_H
#define OLLIE_GHOST_H
#include <KTextEditor/InlineNoteProvider>
#include <KTextEditor/View>
#include <KTextEditor/Document>
#include <KTextEditor/Cursor>
#include <QTimer>
#include <QPainter>
#include <QFont>
#include <QFontMetricsF>
#include <QString>
#include <QStringList>
#include <QWidget>
class GhostOverlay;
/**
* OllieGhostProvider renders AI code completions as translucent gray text
* at the cursor position using KTextEditor's InlineNoteProvider API.
*
* The first line of a multi-line suggestion is rendered as an inline note
* at end-of-line. Continuation lines are rendered via a floating overlay
* widget positioned below the cursor line.
*
* Flow:
* 1. Cursor moves or text changes → debounce timer starts (500ms)
* 2. Timer fires → gather prefix/suffix, call 9P Complete asynchronously
* 3. Result arrives → store suggestion lines, emit inlineNotesChanged
* 4. paintInlineNote renders first line; GhostOverlay renders the rest
* 5. Tab → accept (insert text), any other input → dismiss
*/
class OllieGhostProvider : public KTextEditor::InlineNoteProvider
{
Q_OBJECT
public:
explicit OllieGhostProvider(KTextEditor::View *view, class OllieKatePlugin *plugin, QObject *parent = nullptr);
~OllieGhostProvider() override;
// InlineNoteProvider interface
QList<int> inlineNotes(int line) const override;
QSize inlineNoteSize(const KTextEditor::InlineNote &note) const override;
void paintInlineNote(const KTextEditor::InlineNote &note, QPainter &painter, Qt::LayoutDirection direction) const override;
/// Accept the current suggestion (insert into document). Returns true if accepted.
bool accept();
/// Dismiss the current suggestion without inserting.
void dismiss();
/// Cycle to next (+1) or previous (-1) suggestion.
void cycle(int direction);
/// Returns true if a suggestion is currently visible.
bool hasSuggestion() const;
/// Returns the view this provider is attached to.
KTextEditor::View *view() const { return m_view; }
/// Enable or disable ghost completions.
void setEnabled(bool enabled);
bool isEnabled() const { return m_enabled; }
public slots:
void requestCompletion();
private slots:
void onCursorPositionChanged();
void onTextChanged();
void onCompletionReply(const QString &result);
private:
void clearSuggestion();
void showSuggestionFromHistory();
void showOverlay();
void hideOverlay();
QString gatherPrefix() const;
QString gatherSuffix() const;
KTextEditor::View *m_view;
OllieKatePlugin *m_plugin;
QTimer *m_debounce;
GhostOverlay *m_overlay = nullptr;
// Current suggestion state
QStringList m_suggestionLines; // suggestion split by newline
int m_suggestionStartLine = -1; // document line where suggestion starts
int m_suggestionStartCol = -1; // column (end of line where cursor was)
KTextEditor::Cursor m_triggerCursor; // cursor position when completion was triggered
// Suggestion history for cycling
QList<QStringList> m_history; // all suggestions received at this cursor position
int m_historyIndex = -1; // current index into m_history
bool m_enabled = true; // ghost completions active
bool m_requesting = false; // true while 9P call is in flight
};
/**
* GhostOverlay is a transparent widget that paints continuation lines
* of a multi-line ghost suggestion over the editor viewport.
*/
class GhostOverlay : public QWidget
{
Q_OBJECT
public:
explicit GhostOverlay(QWidget *parent = nullptr);
void setLines(const QStringList &lines, const QFont &font);
void clear();
protected:
void paintEvent(QPaintEvent *event) override;
private:
QStringList m_lines;
QFont m_font;
};
#endif // OLLIE_GHOST_H