diff options
-rw-r--r-- | continuedev/pyproject.toml | 2 | ||||
-rw-r--r-- | continuedev/src/continuedev/server/ide.py | 6 | ||||
-rw-r--r-- | continuedev/src/continuedev/steps/chat.py | 5 | ||||
-rw-r--r-- | continuedev/src/continuedev/steps/core/core.py | 41 | ||||
-rw-r--r-- | extension/.gitignore | 3 | ||||
-rw-r--r-- | extension/package-lock.json | 4 | ||||
-rw-r--r-- | extension/package.json | 4 | ||||
-rw-r--r-- | extension/scripts/requirements.txt | 2 | ||||
-rw-r--r-- | extension/src/activation/activate.ts | 32 | ||||
-rw-r--r-- | extension/src/continueIdeClient.ts | 28 | ||||
-rw-r--r-- | extension/src/debugPanel.ts | 13 | ||||
-rw-r--r-- | extension/src/lang-server/codeLens.ts | 19 | ||||
-rw-r--r-- | extension/src/suggestions.ts | 50 | ||||
-rw-r--r-- | extension/src/util/messenger.ts | 56 |
14 files changed, 161 insertions, 104 deletions
diff --git a/continuedev/pyproject.toml b/continuedev/pyproject.toml index 64d88b8c..e33627e7 100644 --- a/continuedev/pyproject.toml +++ b/continuedev/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "continuedev" -version = "0.1.1" +version = "0.1.2" description = "" authors = ["Nate Sesti <sestinj@gmail.com>"] readme = "README.md" diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py index 3c3555f1..e1f19447 100644 --- a/continuedev/src/continuedev/server/ide.py +++ b/continuedev/src/continuedev/server/ide.py @@ -110,7 +110,8 @@ class IdeProtocolServer(AbstractIdeProtocolServer): session_manager: SessionManager sub_queue: AsyncSubscriptionQueue = AsyncSubscriptionQueue() - def __init__(self, session_manager: SessionManager): + def __init__(self, session_manager: SessionManager, websocket: WebSocket): + self.websocket = websocket self.session_manager = session_manager async def _send_json(self, message_type: str, data: Any): @@ -354,8 +355,7 @@ async def websocket_endpoint(websocket: WebSocket): print("Accepted websocket connection from, ", websocket.client) await websocket.send_json({"messageType": "connected", "data": {}}) - ideProtocolServer = IdeProtocolServer(session_manager) - ideProtocolServer.websocket = websocket + ideProtocolServer = IdeProtocolServer(session_manager, websocket) while AppStatus.should_exit is False: message = await websocket.receive_text() diff --git a/continuedev/src/continuedev/steps/chat.py b/continuedev/src/continuedev/steps/chat.py index 6a2c136e..8494563b 100644 --- a/continuedev/src/continuedev/steps/chat.py +++ b/continuedev/src/continuedev/steps/chat.py @@ -23,7 +23,10 @@ class SimpleChatStep(Step): name: str = "Chat" async def run(self, sdk: ContinueSDK): - self.description = f"```{self.user_input}```\n\n" + self.description = f"`{self.user_input}`\n\n" + if self.user_input.strip() == "": + self.user_input = "Explain this code's function is a concise list of markdown bullets." + self.description = "" await sdk.update_ui() async for chunk in sdk.models.default.stream_complete(self.user_input, with_history=await sdk.get_chat_context()): diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index dfc7d309..f81b3f6d 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -158,6 +158,14 @@ class DefaultModelEditCodeStep(Step): description = await models.gpt3516k.complete( f"{self._prompt_and_completion}\n\nPlease give brief a description of the changes made above using markdown bullet points. Be concise and only mention changes made to the commit before, not prefix or suffix:") self.name = await models.gpt3516k.complete(f"Write a very short title to describe this requested change: '{self.user_input}'. This is the title:") + + # Remove quotes from title and description if they are wrapped + if description.startswith('"') and description.endswith('"'): + description = description[1:-1] + + if self.name.startswith('"') and self.name.endswith('"'): + self.name = self.name[1:-1] + return f"`{self.user_input}`\n\n" + description async def get_prompt_parts(self, rif: RangeInFileWithContents, sdk: ContinueSDK, full_file_contents: str): @@ -170,6 +178,10 @@ class DefaultModelEditCodeStep(Step): total_tokens = model_to_use.count_tokens( full_file_contents + self._prompt + self.user_input) + BUFFER_FOR_FUNCTIONS + DEFAULT_MAX_TOKENS + TOKENS_TO_BE_CONSIDERED_LARGE_RANGE = 1000 + if model_to_use.count_tokens(rif.contents) > TOKENS_TO_BE_CONSIDERED_LARGE_RANGE: + self.description += "\n\n**It looks like you've selected a large range to edit, which may take a while to complete. If you'd like to cancel, click the 'X' button above. If you highlight a more specific range, Continue will only edit within it.**" + # If using 3.5 and overflows, upgrade to 3.5.16k if model_to_use.name == "gpt-3.5-turbo": if total_tokens > MAX_TOKENS_FOR_MODEL["gpt-3.5-turbo"]: @@ -267,8 +279,8 @@ class DefaultModelEditCodeStep(Step): file_prefix, contents, file_suffix, model_to_use = await self.get_prompt_parts( rif, sdk, full_file_contents) - # contents, common_whitespace = dedent_and_get_common_whitespace( - # contents) + contents, common_whitespace = dedent_and_get_common_whitespace( + contents) prompt = self.compile_prompt(file_prefix, contents, file_suffix, sdk) full_file_contents_lines = full_file_contents.split("\n") @@ -304,7 +316,7 @@ class DefaultModelEditCodeStep(Step): if len(current_block_lines) == 0: # Set this as the start of the next block current_block_start = rif.range.start.line + len(original_lines) - len( - original_lines_below_previous_blocks) + offset_from_blocks # current_line_in_file + original_lines_below_previous_blocks) + offset_from_blocks if len(original_lines_below_previous_blocks) > 0 and line == original_lines_below_previous_blocks[0]: # Line is equal to the next line in file, move past this line original_lines_below_previous_blocks = original_lines_below_previous_blocks[ @@ -335,12 +347,23 @@ class DefaultModelEditCodeStep(Step): lines_stripped.append(current_block_lines.pop()) index_of_last_line_in_block -= 1 + # It's also possible that some lines match at the beginning of the block + # lines_stripped_at_beginning = [] + # j = 0 + # while len(current_block_lines) > 0 and current_block_lines[0] == original_lines_below_previous_blocks[first_valid_match[0] - first_valid_match[1] + j]: + # lines_stripped_at_beginning.append( + # current_block_lines.pop(0)) + # j += 1 + # # current_block_start += 1 + # Insert the suggestion replacement = "\n".join(current_block_lines) + start_line = current_block_start + 1 + end_line = current_block_start + index_of_last_line_in_block await sdk.ide.showSuggestion(FileEdit( filepath=rif.filepath, range=Range.from_shorthand( - current_block_start + 1, 0, current_block_start + index_of_last_line_in_block, 0), + start_line, 0, end_line, 0), replacement=replacement )) if replacement == "": @@ -411,7 +434,7 @@ class DefaultModelEditCodeStep(Step): line = line.rstrip() # Add the common whitespace that was removed before prompting - # line = common_whitespace + line + line = common_whitespace + line # Lines that should signify the end of generation if self.is_end_line(line): @@ -437,7 +460,7 @@ class DefaultModelEditCodeStep(Step): # Add the unfinished line if unfinished_line != "" and not self.line_to_be_ignored(unfinished_line, completion_lines_covered == 0) and not self.is_end_line(unfinished_line): - # unfinished_line = common_whitespace + unfinished_line + unfinished_line = common_whitespace + unfinished_line lines.append(unfinished_line) await handle_generated_line(unfinished_line) completion_lines_covered += 1 @@ -459,6 +482,12 @@ class DefaultModelEditCodeStep(Step): current_block_lines = current_block_lines[:- num_to_remove] if num_to_remove > 0 else current_block_lines + # It's also possible that some lines match at the beginning of the block + # while len(current_block_lines) > 0 and len(original_lines_below_previous_blocks) > 0 and current_block_lines[0] == original_lines_below_previous_blocks[0]: + # current_block_lines.pop(0) + # original_lines_below_previous_blocks.pop(0) + # current_block_start += 1 + await sdk.ide.showSuggestion(FileEdit( filepath=rif.filepath, range=Range.from_shorthand( diff --git a/extension/.gitignore b/extension/.gitignore index 6d1b35bf..43b10889 100644 --- a/extension/.gitignore +++ b/extension/.gitignore @@ -3,4 +3,5 @@ node_modules/ out/ .vscode-test/ data/ -src/client
\ No newline at end of file +src/client +.continue diff --git a/extension/package-lock.json b/extension/package-lock.json index 26e1a631..7565f480 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.91", + "version": "0.0.97", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.91", + "version": "0.0.97", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index 314e540f..08f5f081 100644 --- a/extension/package.json +++ b/extension/package.json @@ -14,7 +14,7 @@ "displayName": "Continue", "pricing": "Free", "description": "The open-source coding autopilot", - "version": "0.0.91", + "version": "0.0.97", "publisher": "Continue", "engines": { "vscode": "^1.74.0" @@ -210,7 +210,7 @@ "lint": "eslint src --ext ts", "test": "node ./out/test/runTest.js", "package": "cp ./config/prod_config.json ./config/config.json && mkdir -p ./build && vsce package --out ./build && cp ./config/dev_config.json ./config/config.json", - "full-package": "cd ../continuedev && poetry build && cp ./dist/continuedev-0.1.1-py3-none-any.whl ../extension/scripts/continuedev-0.1.1-py3-none-any.whl && cd ../extension && npm install && npm run typegen && npm run clientgen && cd react-app && npm install && npm run build && cd .. && npm run package", + "full-package": "cd ../continuedev && poetry build && cp ./dist/continuedev-0.1.2-py3-none-any.whl ../extension/scripts/continuedev-0.1.2-py3-none-any.whl && cd ../extension && npm install && npm run typegen && npm run clientgen && cd react-app && npm install && npm run build && cd .. && npm run package", "install-extension": "code --install-extension ./build/continue-0.0.8.vsix", "uninstall": "code --uninstall-extension .continue", "reinstall": "rm -rf ./build && npm run package && npm run uninstall && npm run install-extension" diff --git a/extension/scripts/requirements.txt b/extension/scripts/requirements.txt index e6ba1e0a..c51c9d73 100644 --- a/extension/scripts/requirements.txt +++ b/extension/scripts/requirements.txt @@ -3,4 +3,4 @@ # typer==0.7.0 # pydantic # pytest -./continuedev-0.1.1-py3-none-any.whl
\ No newline at end of file +./continuedev-0.1.2-py3-none-any.whl
\ No newline at end of file diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts index cd8f0cf3..18650561 100644 --- a/extension/src/activation/activate.ts +++ b/extension/src/activation/activate.ts @@ -20,6 +20,21 @@ export async function activateExtension( ) { extensionContext = context; + await new Promise((resolve, reject) => { + vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: + "Starting Continue Server... (it may take a minute to download Python packages)", + cancellable: false, + }, + async (progress, token) => { + await startContinuePythonServer(); + resolve(null); + } + ); + }); + sendTelemetryEvent(TelemetryEvent.ExtensionActivated); registerAllCodeLensProviders(context); registerAllCommands(context); @@ -33,7 +48,7 @@ export async function activateExtension( // Setup the left panel (async () => { - const sessionIdPromise = ideProtocolClient.getSessionId(); + const sessionIdPromise = await ideProtocolClient.getSessionId(); const provider = new ContinueGUIWebviewViewProvider(sessionIdPromise); context.subscriptions.push( @@ -46,21 +61,6 @@ export async function activateExtension( ) ); })(); - - await new Promise((resolve, reject) => { - vscode.window.withProgress( - { - location: vscode.ProgressLocation.Notification, - title: - "Starting Continue Server... (it may take a minute to download Python packages)", - cancellable: false, - }, - async (progress, token) => { - await startContinuePythonServer(); - resolve(null); - } - ); - }); // All opened terminals should be replaced by our own terminal // vscode.window.onDidOpenTerminal((terminal) => {}); diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index b179cbf3..21104abe 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -32,8 +32,8 @@ class IdeProtocolClient { messenger.onClose(() => { this.messenger = null; }); - messenger.onMessage((messageType, data) => { - this.handleMessage(messageType, data); + messenger.onMessage((messageType, data, messenger) => { + this.handleMessage(messageType, data, messenger); }); // Setup listeners for any file changes in open editors @@ -67,41 +67,45 @@ class IdeProtocolClient { // }); } - async handleMessage(messageType: string, data: any) { + async handleMessage( + messageType: string, + data: any, + messenger: WebsocketMessenger + ) { switch (messageType) { case "highlightedCode": - this.messenger?.send("highlightedCode", { + messenger.send("highlightedCode", { highlightedCode: this.getHighlightedCode(), }); break; case "workspaceDirectory": - this.messenger?.send("workspaceDirectory", { + messenger.send("workspaceDirectory", { workspaceDirectory: this.getWorkspaceDirectory(), }); break; case "uniqueId": - this.messenger?.send("uniqueId", { + messenger.send("uniqueId", { uniqueId: this.getUniqueId(), }); break; case "getUserSecret": - this.messenger?.send("getUserSecret", { + messenger.send("getUserSecret", { value: await this.getUserSecret(data.key), }); break; case "openFiles": - this.messenger?.send("openFiles", { + messenger.send("openFiles", { openFiles: this.getOpenFiles(), }); break; case "readFile": - this.messenger?.send("readFile", { + messenger.send("readFile", { contents: this.readFile(data.filepath), }); break; case "editFile": const fileEdit = await this.editFile(data.edit); - this.messenger?.send("editFile", { + messenger.send("editFile", { fileEdit, }); break; @@ -109,7 +113,7 @@ class IdeProtocolClient { this.highlightCode(data.rangeInFile, data.color); break; case "runCommand": - this.messenger?.send("runCommand", { + messenger.send("runCommand", { output: await this.runCommand(data.command), }); break; @@ -249,6 +253,8 @@ class IdeProtocolClient { ) { clearInterval(interval); resolve(null); + } else { + console.log("Websocket not yet open, trying again..."); } }, 1000); }); diff --git a/extension/src/debugPanel.ts b/extension/src/debugPanel.ts index b176eee7..b88c86f3 100644 --- a/extension/src/debugPanel.ts +++ b/extension/src/debugPanel.ts @@ -132,7 +132,7 @@ let streamManager = new StreamManager(); export let debugPanelWebview: vscode.Webview | undefined; export function setupDebugPanel( panel: vscode.WebviewPanel | vscode.WebviewView, - sessionIdPromise: Promise<string> + sessionIdPromise: Promise<string> | string ): string { debugPanelWebview = panel.webview; panel.onDidDispose(() => { @@ -230,7 +230,12 @@ export function setupDebugPanel( panel.webview.onDidReceiveMessage(async (data) => { switch (data.type) { case "onLoad": { - const sessionId = await sessionIdPromise; + let sessionId: string; + if (typeof sessionIdPromise === "string") { + sessionId = sessionIdPromise; + } else { + sessionId = await sessionIdPromise; + } panel.webview.postMessage({ type: "onLoad", vscMachineId: vscode.env.machineId, @@ -350,9 +355,9 @@ export class ContinueGUIWebviewViewProvider implements vscode.WebviewViewProvider { public static readonly viewType = "continue.continueGUIView"; - private readonly sessionIdPromise: Promise<string>; + private readonly sessionIdPromise: Promise<string> | string; - constructor(sessionIdPromise: Promise<string>) { + constructor(sessionIdPromise: Promise<string> | string) { this.sessionIdPromise = sessionIdPromise; } diff --git a/extension/src/lang-server/codeLens.ts b/extension/src/lang-server/codeLens.ts index 21448e31..3bd4f153 100644 --- a/extension/src/lang-server/codeLens.ts +++ b/extension/src/lang-server/codeLens.ts @@ -60,18 +60,15 @@ class SuggestionsCodeLensProvider implements vscode.CodeLensProvider { } } -const allCodeLensProviders: { [langauge: string]: vscode.CodeLensProvider[] } = - { - // python: [new SuggestionsCodeLensProvider(), new PytestCodeLensProvider()], - "*": [new SuggestionsCodeLensProvider()], - }; +let suggestionsCodeLensDisposable: vscode.Disposable | undefined = undefined; export function registerAllCodeLensProviders(context: vscode.ExtensionContext) { - for (const language in allCodeLensProviders) { - for (const codeLensProvider of allCodeLensProviders[language]) { - context.subscriptions.push( - vscode.languages.registerCodeLensProvider(language, codeLensProvider) - ); - } + if (suggestionsCodeLensDisposable) { + suggestionsCodeLensDisposable.dispose(); } + suggestionsCodeLensDisposable = vscode.languages.registerCodeLensProvider( + "*", + new SuggestionsCodeLensProvider() + ); + context.subscriptions.push(suggestionsCodeLensDisposable); } diff --git a/extension/src/suggestions.ts b/extension/src/suggestions.ts index e269f38a..baa49711 100644 --- a/extension/src/suggestions.ts +++ b/extension/src/suggestions.ts @@ -4,6 +4,8 @@ import { openEditorAndRevealRange } from "./util/vscode"; import { translate, readFileAtRange } from "./util/vscode"; import * as fs from "fs"; import * as path from "path"; +import { registerAllCodeLensProviders } from "./lang-server/codeLens"; +import { extensionContext } from "./activation/activate"; export interface SuggestionRanges { oldRange: vscode.Range; @@ -125,6 +127,10 @@ export function rerenderDecorations(editorUri: string) { suggestions[idx].newRange, vscode.TextEditorRevealType.Default ); + + if (extensionContext) { + registerAllCodeLensProviders(extensionContext); + } } export function suggestionDownCommand() { @@ -337,42 +343,14 @@ export async function showSuggestion( range: vscode.Range, suggestion: string ): Promise<boolean> { - // const existingCode = await readFileAtRange( - // new vscode.Range(range.start, range.end), - // editorFilename - // ); - - // If any of the outside lines are the same, don't repeat them in the suggestion - // const slines = suggestion.split("\n"); - // const elines = existingCode.split("\n"); - // let linesRemovedBefore = 0; - // let linesRemovedAfter = 0; - // while (slines.length > 0 && elines.length > 0 && slines[0] === elines[0]) { - // slines.shift(); - // elines.shift(); - // linesRemovedBefore++; - // } - - // while ( - // slines.length > 0 && - // elines.length > 0 && - // slines[slines.length - 1] === elines[elines.length - 1] - // ) { - // slines.pop(); - // elines.pop(); - // linesRemovedAfter++; - // } - - // suggestion = slines.join("\n"); - // if (suggestion === "") return Promise.resolve(false); // Don't even make a suggestion if they are exactly the same - - // range = new vscode.Range( - // new vscode.Position(range.start.line + linesRemovedBefore, 0), - // new vscode.Position( - // range.end.line - linesRemovedAfter, - // elines.at(-1)?.length || 0 - // ) - // ); + // Check for empty suggestions: + if ( + suggestion === "" && + range.start.line === range.end.line && + range.start.character === range.end.character + ) { + return Promise.resolve(false); + } const editor = await openEditorAndRevealRange(editorFilename, range); if (!editor) return Promise.resolve(false); diff --git a/extension/src/util/messenger.ts b/extension/src/util/messenger.ts index e4133230..b1df161b 100644 --- a/extension/src/util/messenger.ts +++ b/extension/src/util/messenger.ts @@ -1,5 +1,6 @@ console.log("Websocket import"); const WebSocket = require("ws"); +import fetch from "node-fetch"; export abstract class Messenger { abstract send(messageType: string, data: object): void; @@ -50,18 +51,49 @@ export class WebsocketMessenger extends Messenger { return newWebsocket; } + async checkServerRunning(serverUrl: string): Promise<boolean> { + // Check if already running by calling /health + try { + const response = await fetch(serverUrl + "/health"); + if (response.status === 200) { + console.log("Continue python server already running"); + return true; + } else { + return false; + } + } catch (e) { + return false; + } + } + constructor(serverUrl: string) { super(); this.serverUrl = serverUrl; this.websocket = this._newWebsocket(); - const interval = setInterval(() => { - if (this.websocket.readyState === this.websocket.OPEN) { - clearInterval(interval); - } else if (this.websocket.readyState !== this.websocket.CONNECTING) { - this.websocket = this._newWebsocket(); - } - }, 1000); + // Wait until the server is running + // const interval = setInterval(async () => { + // if ( + // await this.checkServerRunning( + // serverUrl.replace("/ide/ws", "").replace("ws://", "http://") + // ) + // ) { + // this.websocket = this._newWebsocket(); + // clearInterval(interval); + // } else { + // console.log( + // "Waiting for python server to start-----------------------" + // ); + // } + // }, 1000); + + // const interval = setInterval(() => { + // if (this.websocket.readyState === this.websocket.OPEN) { + // clearInterval(interval); + // } else if (this.websocket.readyState !== this.websocket.CONNECTING) { + // this.websocket = this._newWebsocket(); + // } + // }, 1000); } send(messageType: string, data: object) { @@ -99,10 +131,16 @@ export class WebsocketMessenger extends Messenger { }); } - onMessage(callback: (messageType: string, data: any) => void): void { + onMessage( + callback: ( + messageType: string, + data: any, + messenger: WebsocketMessenger + ) => void + ): void { this.websocket.addEventListener("message", (event) => { const msg = JSON.parse(event.data); - callback(msg.messageType, msg.data); + callback(msg.messageType, msg.data, this); }); } |