diff --git a/packages/ai-code-completion/src/browser/ai-code-completion-preference.ts b/packages/ai-code-completion/src/browser/ai-code-completion-preference.ts index 3dd995bc96618..60cab4a55a59b 100644 --- a/packages/ai-code-completion/src/browser/ai-code-completion-preference.ts +++ b/packages/ai-code-completion/src/browser/ai-code-completion-preference.ts @@ -18,6 +18,7 @@ import { PreferenceSchema } from '@theia/core/lib/browser/preferences/preference import { AI_CORE_PREFERENCES_TITLE } from '@theia/ai-core/lib/browser/ai-core-preferences'; export const PREF_AI_INLINE_COMPLETION_ENABLE = 'ai-features.codeCompletion.enableCodeCompletion'; +export const PREF_AI_INLINE_COMPLETION_EXCLUDED_EXTENSIONS = 'ai-features.codeCompletion.excludedFileExtensions'; export const AICodeCompletionPreferencesSchema: PreferenceSchema = { type: 'object', @@ -27,6 +28,15 @@ export const AICodeCompletionPreferencesSchema: PreferenceSchema = { type: 'boolean', description: 'Enable AI completions inline within any (Monaco) editor.', default: false + }, + [PREF_AI_INLINE_COMPLETION_EXCLUDED_EXTENSIONS]: { + title: 'Excluded File Extensions', + type: 'array', + description: 'Specify file extensions (e.g., .md, .txt) where AI completions should be disabled.', + items: { + type: 'string' + }, + default: [] } } }; diff --git a/packages/ai-code-completion/src/browser/ai-code-frontend-application-contribution.ts b/packages/ai-code-completion/src/browser/ai-code-frontend-application-contribution.ts index 3f430939a81e5..dc3b361c70ed8 100644 --- a/packages/ai-code-completion/src/browser/ai-code-frontend-application-contribution.ts +++ b/packages/ai-code-completion/src/browser/ai-code-frontend-application-contribution.ts @@ -21,7 +21,7 @@ import { inject, injectable } from '@theia/core/shared/inversify'; import { AIActivationService } from '@theia/ai-core/lib/browser'; import { Disposable } from '@theia/core'; import { AICodeInlineCompletionsProvider } from './ai-code-inline-completion-provider'; -import { PREF_AI_INLINE_COMPLETION_ENABLE } from './ai-code-completion-preference'; +import { PREF_AI_INLINE_COMPLETION_ENABLE, PREF_AI_INLINE_COMPLETION_EXCLUDED_EXTENSIONS } from './ai-code-completion-preference'; @injectable() export class AIFrontendApplicationContribution implements FrontendApplicationContribution { @@ -38,27 +38,59 @@ export class AIFrontendApplicationContribution implements FrontendApplicationCon onDidInitializeLayout(): void { this.preferenceService.ready.then(() => { - this.handlePreference(PREF_AI_INLINE_COMPLETION_ENABLE, enable => this.handleInlineCompletions(enable)); + // Handle changes in both enable and excluded file extensions preferences + this.handlePreferences(); }); } - protected handlePreference(name: string, handler: (enable: boolean) => Disposable): void { - const enable = this.preferenceService.get(name, false) && this.activationService.isActive; - this.toDispose.set(name, handler(enable)); + protected handlePreferences(): void { + const handler = () => this.handleInlineCompletions( + this.preferenceService.get(PREF_AI_INLINE_COMPLETION_ENABLE, false) && this.activationService.isActive + ); + + this.toDispose.set('inlineCompletions', handler()); this.preferenceService.onPreferenceChanged(event => { - if (event.preferenceName === name) { - this.toDispose.get(name)?.dispose(); - this.toDispose.set(name, handler(event.newValue && this.activationService.isActive)); + if (event.preferenceName === PREF_AI_INLINE_COMPLETION_ENABLE || event.preferenceName === PREF_AI_INLINE_COMPLETION_EXCLUDED_EXTENSIONS) { + // Re-apply the completions provider when either the enable or excluded file extensions change + this.toDispose.get('inlineCompletions')?.dispose(); + this.toDispose.set('inlineCompletions', handler()); } }); + this.activationService.onDidChangeActiveStatus(change => { - this.toDispose.get(name)?.dispose(); - this.toDispose.set(name, handler(this.preferenceService.get(name, false) && change)); + // Re-apply the completions provider when the activation status changes + this.toDispose.get('inlineCompletions')?.dispose(); + this.toDispose.set('inlineCompletions', handler()); }); } protected handleInlineCompletions(enable: boolean): Disposable { - return enable ? monaco.languages.registerInlineCompletionsProvider({ scheme: 'file' }, this.inlineCodeCompletionProvider) : Disposable.NULL; + if (!enable) { + return Disposable.NULL; + } + + // Get excluded file extensions from preferences + const excludedExtensions = this.preferenceService.get(PREF_AI_INLINE_COMPLETION_EXCLUDED_EXTENSIONS, []); + + return monaco.languages.registerInlineCompletionsProvider( + { scheme: 'file' }, + { + provideInlineCompletions: (model, position, context, token) => { + const fileName = model.uri.toString(); + + // Exclude specific file types based on preferences + if (excludedExtensions.some(ext => fileName.endsWith(ext))) { + return { items: [] }; // Return empty result for excluded files + } + + // If file type is allowed, return the code completions + return this.inlineCodeCompletionProvider.provideInlineCompletions(model, position, context, token); + }, + freeInlineCompletions: completions => { + // No clean up resources necessary + } + } + ); } }