From 26109c600f45ba618ccd9f1ee0efb6f835d71e17 Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Mon, 14 Oct 2024 17:19:07 +0200 Subject: [PATCH 1/2] Add preference to exclude files from code completion fixed #14313 Signed-off-by: Jonas Helming --- .../browser/ai-code-completion-preference.ts | 10 ++++ ...-code-frontend-application-contribution.ts | 54 +++++++++++++++---- 2 files changed, 53 insertions(+), 11 deletions(-) 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 + } + } + ); } } From 51757d165b11d18fd4d14b699497eaa709609835 Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Wed, 16 Oct 2024 09:25:05 +0200 Subject: [PATCH 2/2] Adress review comments Signed-off-by: Jonas Helming --- ...-code-frontend-application-contribution.ts | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) 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 dc3b361c70ed8..9d7c610979606 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 @@ -38,39 +38,35 @@ export class AIFrontendApplicationContribution implements FrontendApplicationCon onDidInitializeLayout(): void { this.preferenceService.ready.then(() => { - // Handle changes in both enable and excluded file extensions preferences this.handlePreferences(); }); } protected handlePreferences(): void { - const handler = () => this.handleInlineCompletions( - this.preferenceService.get(PREF_AI_INLINE_COMPLETION_ENABLE, false) && this.activationService.isActive - ); + const handler = () => this.handleInlineCompletions(); this.toDispose.set('inlineCompletions', handler()); this.preferenceService.onPreferenceChanged(event => { 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 => { - // 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 { + protected handleInlineCompletions(): Disposable { + const enable = this.preferenceService.get(PREF_AI_INLINE_COMPLETION_ENABLE, false) && this.activationService.isActive; + 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( @@ -79,16 +75,13 @@ export class AIFrontendApplicationContribution implements FrontendApplicationCon 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 + return { items: [] }; } - - // If file type is allowed, return the code completions return this.inlineCodeCompletionProvider.provideInlineCompletions(model, position, context, token); }, freeInlineCompletions: completions => { - // No clean up resources necessary + this.inlineCodeCompletionProvider.freeInlineCompletions(completions); } } );