Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ node_modules/
out/
build/
vscode-out/
*.vsix
*.vsix
package-lock.json
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,27 @@ Rest assured, you can take a break safely.

- AI chat interface in sidebar
- Inline code editing with `Ctrl+I` / `Cmd+I`
- Inline code suggestions while typing (configurable)
- Agent mode with shell command execution
- Safety agent mode (Auto Approve)
- Diff preview and apply
- Auto context (file list, git status, package.json, tsconfig.json)
- Multiple AI model support (OpenAI, Anthropic, Google, etc.)

## Usage

### Inline Suggestions

Inline suggestions provide contextual code completions as you type, similar to GitHub Copilot. To enable:

1. Open VS Code Settings (`Ctrl+,` / `Cmd+,`)
2. Search for "Flixa: Inline Completion Enabled"
3. Enable the setting

Once enabled, suggestions will appear as grayed-out ghost text while typing. Press `Tab` or `Enter` to accept a suggestion.

**Note:** Inline suggestions are disabled by default to avoid unexpected API usage. Enable only when needed.

## License

MIT
12 changes: 12 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,18 @@
"bun.lockb"
],
"description": "Patterns to exclude from file list"
},
"flixa.inlineCompletion.enabled": {
"type": "boolean",
"default": false,
"description": "Enable inline code suggestions while typing"
},
"flixa.inlineCompletion.maxLength": {
"type": "number",
"default": 120,
"minimum": 40,
"maximum": 200,
"description": "Maximum character length for inline suggestions"
}
}
},
Expand Down
12 changes: 12 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { showDiffPreview } from './diff/preview';
import { applyPendingDiff } from './diff/apply';
import { PendingDiff, ImplementRequest, ApprovalMode } from './types';
import { setOutputChannel } from './logger';
import { FlixaInlineCompletionProvider } from './inline';

let pendingDiff: PendingDiff | null = null;
let chatViewProvider: ChatViewProvider | null = null;
Expand Down Expand Up @@ -36,6 +37,17 @@ export function activate(context: vscode.ExtensionContext): void {
console.log('[Flixa] CodeLens provider registered');
context.subscriptions.push(codeLensDisposable);

const inlineCompletionProvider = new FlixaInlineCompletionProvider();
const inlineCompletionDisposable = vscode.languages.registerInlineCompletionItemProvider(
[
{ scheme: 'file', pattern: '**/*' },
{ scheme: 'untitled' }
],
inlineCompletionProvider
);
console.log('[Flixa] Inline completion provider registered');
context.subscriptions.push(inlineCompletionDisposable);

chatViewProvider = new ChatViewProvider(
context.extensionUri,
context,
Expand Down
1 change: 1 addition & 0 deletions src/inline/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { FlixaInlineCompletionProvider } from './provider';
100 changes: 100 additions & 0 deletions src/inline/provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import * as vscode from 'vscode';
import { generateText } from 'ai';
import { getAnthropicProvider, getModel } from '../llm/provider';

export class FlixaInlineCompletionProvider implements vscode.InlineCompletionItemProvider {
private lastRequestTime = 0;
private debounceDelay = 300;
private cache: Map<string, { completion: string; timestamp: number }> = new Map();
private cacheTimeout = 5000;

async provideInlineCompletionItems(
document: vscode.TextDocument,
position: vscode.Position,
context: vscode.InlineCompletionContext,
token: vscode.CancellationToken
): Promise<vscode.InlineCompletionItem[] | undefined> {
const config = vscode.workspace.getConfiguration('flixa');
const enabled = config.get<boolean>('inlineCompletion.enabled', false);
const maxLength = config.get<number>('inlineCompletion.maxLength', 120);

if (!enabled) {
return undefined;
}

if (token.isCancellationRequested) {
return undefined;
}

const now = Date.now();
if (now - this.lastRequestTime < this.debounceDelay) {
return undefined;
}
this.lastRequestTime = now;

const currentLine = document.lineAt(position.line).text;
const precedingText = currentLine.substring(0, position.character);

if (!precedingText.trim()) {
return undefined;
}

const cacheKey = `${document.fileName}:${position.line}:${precedingText}`;
const cached = this.cache.get(cacheKey);
if (cached && now - cached.timestamp < this.cacheTimeout) {
return [{
insertText: cached.completion,
range: new vscode.Range(position, position),
}];
}

try {
const provider = getAnthropicProvider();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Guard against missing model/provider configuration before invoking the SDK; otherwise typing can throw repeatedly when the extension is enabled but not fully configured.

Suggested change
const provider = getAnthropicProvider();
const provider = getAnthropicProvider();
const model = getModel();
if (!provider || !model) {
return undefined;
}

const model = getModel();

const contextLines = 5;
const startLine = Math.max(0, position.line - contextLines);
const endLine = Math.min(document.lineCount - 1, position.line + contextLines);
const contextText = [];
for (let i = startLine; i <= endLine; i++) {
contextText.push(document.lineAt(i).text);
}

const abortController = new AbortController();
const listener = token.onCancellationRequested(() => {
abortController.abort();
});

const { text } = await generateText({
model: provider(model),
system: `You are a code completion AI. Suggest a natural continuation of the code. Keep it concise (under ${maxLength} chars). Return ONLY the completion text that follows the cursor, no explanation, no markdown.`,
prompt: `File: ${document.fileName}\nLanguage: ${document.languageId}\n\nContext:\n${contextText.join('\n')}\n\nCursor is at the end of line ${position.line + 1}. Complete the code naturally:`,
abortSignal: abortController.signal,
});

listener.dispose();

if (token.isCancellationRequested) {
return undefined;
}

const completion = text?.trim() || '';
if (!completion) {
return undefined;
}

this.cache.set(cacheKey, { completion, timestamp: now });

return [{
insertText: completion,
range: new vscode.Range(position, position),
}];
} catch (error) {
if (token.isCancellationRequested) {
return undefined;
}
console.error('[Flixa] Inline completion error:', error);
return undefined;
}
}
}
Loading