123 lines
3.8 KiB
C++
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 ¬e) const override;
|
|
void paintInlineNote(const KTextEditor::InlineNote ¬e, 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
|