diff --git a/.vscode/settings.json b/.vscode/settings.json index db2967d2e5..d17afc5ae5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -45,6 +45,7 @@ "extensions/.continue-debug": true // "sync/**": true }, + "editor.formatOnSave": true, "eslint.workingDirectories": ["./core"], "typescript.tsdk": "node_modules/typescript/lib", "conventionalCommits.showNewVersionNotes": false, diff --git a/core/config/sharedConfig.ts b/core/config/sharedConfig.ts index ad8b81341c..4399251881 100644 --- a/core/config/sharedConfig.ts +++ b/core/config/sharedConfig.ts @@ -18,6 +18,7 @@ export const sharedConfigSchema = z useChromiumForDocsCrawling: z.boolean(), readResponseTTS: z.boolean(), promptPath: z.string(), + useCurrentFileAsContext: z.boolean(), // `ui` in `ContinueConfig` showSessionTabs: z.boolean(), @@ -166,6 +167,10 @@ export function modifyAnyConfigWithSharedConfig< if (sharedConfig.readResponseTTS !== undefined) { configCopy.experimental.readResponseTTS = sharedConfig.readResponseTTS; } + if (sharedConfig.useCurrentFileAsContext !== undefined) { + configCopy.experimental.useCurrentFileAsContext = + sharedConfig.useCurrentFileAsContext; + } return configCopy; } diff --git a/core/index.d.ts b/core/index.d.ts index aa50ac4707..794c03ce01 100644 --- a/core/index.d.ts +++ b/core/index.d.ts @@ -1334,6 +1334,11 @@ export interface ExperimentalConfig { */ useChromiumForDocsCrawling?: boolean; modelContextProtocolServers?: ExperimentalMCPOptions[]; + + /** + * If enabled, will add the current file as context. + */ + useCurrentFileAsContext?: boolean; } export interface AnalyticsConfig { diff --git a/extensions/vscode/src/VsCodeIde.ts b/extensions/vscode/src/VsCodeIde.ts index 4a553ef602..48a35fa69f 100644 --- a/extensions/vscode/src/VsCodeIde.ts +++ b/extensions/vscode/src/VsCodeIde.ts @@ -335,7 +335,10 @@ class VsCodeIde implements IDE { const pathToLastModified: FileStatsMap = {}; await Promise.all( files.map(async (file) => { - const stat = await this.ideUtils.stat(vscode.Uri.parse(file), false /* No need to catch ENOPRO exceptions */); + const stat = await this.ideUtils.stat( + vscode.Uri.parse(file), + false /* No need to catch ENOPRO exceptions */, + ); pathToLastModified[file] = { lastModified: stat!.mtime, size: stat!.size, @@ -401,7 +404,8 @@ class VsCodeIde implements IDE { const configs: ContinueRcJson[] = []; for (const workspaceDir of workspaceDirs) { const files = await this.ideUtils.readDirectory(workspaceDir); - if (files === null) {//Unlikely, but just in case... + if (files === null) { + //Unlikely, but just in case... continue; } for (const [filename, type] of files) { @@ -753,7 +757,7 @@ class VsCodeIde implements IDE { async listDir(dir: string): Promise<[string, FileType][]> { const entries = await this.ideUtils.readDirectory(vscode.Uri.parse(dir)); - return entries === null? [] : entries as any; + return entries === null ? [] : (entries as any); } private getIdeSettingsSync(): IdeSettings { diff --git a/gui/src/components/mainInput/TipTapEditor/useMainEditorWebviewListeners.ts b/gui/src/components/mainInput/TipTapEditor/useMainEditorWebviewListeners.ts index 12eef0717d..0fe79297c6 100644 --- a/gui/src/components/mainInput/TipTapEditor/useMainEditorWebviewListeners.ts +++ b/gui/src/components/mainInput/TipTapEditor/useMainEditorWebviewListeners.ts @@ -3,11 +3,13 @@ import { InputModifiers } from "core"; import { rifWithContentsToContextItem } from "core/commands/util"; import { MutableRefObject } from "react"; import { useWebviewListener } from "../../../hooks/useWebviewListener"; +import { useAppSelector } from "../../../redux/hooks"; import { clearCodeToEdit } from "../../../redux/slices/editState"; import { setNewestToolbarPreviewForInput } from "../../../redux/slices/sessionSlice"; import { AppDispatch } from "../../../redux/store"; import { loadSession, saveCurrentSession } from "../../../redux/thunks/session"; import { CodeBlock, PromptBlock } from "./extensions"; +import { insertCurrentFileContextMention } from "./utils/insertCurrentFileContextMention"; /** * Hook for setting up main editor specific webview listeners @@ -27,6 +29,14 @@ export function useMainEditorWebviewListeners({ inputId: string; editorFocusedRef: MutableRefObject; }) { + const activeContextProviders = useAppSelector( + (state) => state.config.config.contextProviders, + ); + const useCurrentFileAsContext = useAppSelector( + (state) => state.config.config.experimental?.useCurrentFileAsContext, + ); + const isInEdit = useAppSelector((state) => state.session.isInEdit); + useWebviewListener( "isContinueInputFocused", async () => { @@ -178,4 +188,14 @@ export function useMainEditorWebviewListeners({ }, [], ); + + useWebviewListener( + "newSession", + async () => { + // do not insert current file context mention if we are in edit mode or if addFileContext is disabled + if (!editor || isInEdit || !useCurrentFileAsContext) return; + insertCurrentFileContextMention(editor, activeContextProviders); + }, + [editor, activeContextProviders, isInEdit, useCurrentFileAsContext], + ); } diff --git a/gui/src/components/mainInput/TipTapEditor/utils/insertCurrentFileContextMention.ts b/gui/src/components/mainInput/TipTapEditor/utils/insertCurrentFileContextMention.ts new file mode 100644 index 0000000000..764ea9f9b2 --- /dev/null +++ b/gui/src/components/mainInput/TipTapEditor/utils/insertCurrentFileContextMention.ts @@ -0,0 +1,34 @@ +import { Editor } from "@tiptap/core"; +import { ContextProviderDescription } from "core"; +import * as ContinueExtensions from "../extensions"; + +/** + * inserts the current file context as a removable mention in the editor area + * + * uses tiptap's `create` method to create a new node under the hood + */ +export function insertCurrentFileContextMention( + editor: Editor, + contextProviders: ContextProviderDescription[], +) { + const foundCurrentFileProvider = contextProviders.find( + (provider) => provider.title === "currentFile", + ); + + if (foundCurrentFileProvider) { + const node = editor.schema.nodes[ContinueExtensions.Mention.name].create({ + name: foundCurrentFileProvider.displayTitle, + description: foundCurrentFileProvider.description, + id: foundCurrentFileProvider.title, + label: foundCurrentFileProvider.displayTitle, + renderInlineAs: foundCurrentFileProvider.renderInlineAs, + type: foundCurrentFileProvider.type, + itemType: "contextProvider", + }); + + editor + .chain() + .insertContent([node.toJSON(), { type: "text", text: " " }]) // add a space after the mention + .run(); + } +} diff --git a/gui/src/pages/config/UserSettingsForm.tsx b/gui/src/pages/config/UserSettingsForm.tsx index d116e34992..7f521b95f1 100644 --- a/gui/src/pages/config/UserSettingsForm.tsx +++ b/gui/src/pages/config/UserSettingsForm.tsx @@ -76,6 +76,8 @@ export function UserSettingsForm() { const autoAcceptEditToolDiffs = config.ui?.autoAcceptEditToolDiffs ?? false; const displayRawMarkdown = config.ui?.displayRawMarkdown ?? false; const disableSessionTitles = config.disableSessionTitles ?? false; + const useCurrentFileAsContext = + config.experimental?.useCurrentFileAsContext ?? false; const allowAnonymousTelemetry = config.allowAnonymousTelemetry ?? true; const disableIndexing = config.disableIndexing ?? false; @@ -336,7 +338,7 @@ export function UserSettingsForm() { -
+
setShowExperimental(!showExperimental)} @@ -353,7 +355,7 @@ export function UserSettingsForm() { showExperimental ? "max-h-40" : "max-h-0" }`} > -
+
@@ -374,6 +376,16 @@ export function UserSettingsForm() { } /> + + + handleUpdate({ + useCurrentFileAsContext: !useCurrentFileAsContext, + }) + } + text="Add Current File by Default" + />