summaryrefslogtreecommitdiff
path: root/extension
diff options
context:
space:
mode:
authorNate Sesti <sestinj@gmail.com>2023-06-26 16:39:07 -0700
committerNate Sesti <sestinj@gmail.com>2023-06-26 16:39:07 -0700
commit0b3e6fdf553a481bdd899a2608376dbb95c8e72e (patch)
tree615381124b54fb8a10523335c971b5a2d208b767 /extension
parentdeca77889cfc8eaa4065694f113525eda365e5d0 (diff)
parent0862b388dc0f07e45b6daeaec1f8c05925623692 (diff)
downloadsncontinue-0b3e6fdf553a481bdd899a2608376dbb95c8e72e.tar.gz
sncontinue-0b3e6fdf553a481bdd899a2608376dbb95c8e72e.tar.bz2
sncontinue-0b3e6fdf553a481bdd899a2608376dbb95c8e72e.zip
Merge branch 'main' into styled-code
Diffstat (limited to 'extension')
-rw-r--r--extension/README.md17
-rw-r--r--extension/package-lock.json4
-rw-r--r--extension/package.json6
-rw-r--r--extension/react-app/package-lock.json32
-rw-r--r--extension/react-app/package.json1
-rw-r--r--extension/react-app/public/continue.gifbin0 -> 5242 bytes
-rw-r--r--extension/react-app/public/continue_arrow.pngbin1350 -> 0 bytes
-rw-r--r--extension/react-app/public/play_button.pngbin0 -> 1017 bytes
-rw-r--r--extension/react-app/src/components/CodeBlock.tsx7
-rw-r--r--extension/react-app/src/components/ComboBox.tsx29
-rw-r--r--extension/react-app/src/components/ContinueButton.tsx13
-rw-r--r--extension/react-app/src/components/DebugPanel.tsx2
-rw-r--r--extension/react-app/src/components/HeaderButtonWithText.tsx10
-rw-r--r--extension/react-app/src/components/LoadingCover.tsx50
-rw-r--r--extension/react-app/src/components/StepContainer.tsx40
-rw-r--r--extension/react-app/src/components/TextDialog.tsx2
-rw-r--r--extension/react-app/src/hooks/ContinueGUIClientProtocol.ts2
-rw-r--r--extension/react-app/src/hooks/useContinueGUIProtocol.ts4
-rw-r--r--extension/react-app/src/redux/slices/configSlice.ts16
-rw-r--r--extension/react-app/src/redux/store.ts1
-rw-r--r--extension/react-app/src/tabs/gui.tsx149
-rw-r--r--extension/scripts/.gitignore4
-rw-r--r--extension/scripts/continuedev-0.1.1-py3-none-any.whlbin85071 -> 0 bytes
-rw-r--r--extension/src/activation/activate.ts52
-rw-r--r--extension/src/activation/environmentSetup.ts175
-rw-r--r--extension/src/commands.ts98
-rw-r--r--extension/src/continueIdeClient.ts23
-rw-r--r--extension/src/debugPanel.ts174
-rw-r--r--extension/src/decorations.ts97
-rw-r--r--extension/src/extension.ts3
-rw-r--r--extension/src/lang-server/codeLens.ts34
-rw-r--r--extension/src/languages/index.d.ts13
-rw-r--r--extension/src/languages/index.ts19
-rw-r--r--extension/src/languages/javascript/index.ts16
-rw-r--r--extension/src/languages/notImplemented.ts10
-rw-r--r--extension/src/languages/python/index.ts74
36 files changed, 443 insertions, 734 deletions
diff --git a/extension/README.md b/extension/README.md
index 63200a85..b43203bb 100644
--- a/extension/README.md
+++ b/extension/README.md
@@ -2,21 +2,18 @@
**[Continue](https://continue.dev/docs/) is the open-source library for accelerating software development with language models**
-You can use Continue to use and create recipes that automate the most repetitive sequences of tasks in your workflows.
-
## Getting Started
-After install is complete, the Continue extension will automatically open. You can also open it by pressing `cmd+shift+p` on Mac / `ctrl+shift+p` on Windows and then selecting `Continue: Open Continue GUI`
-
-To test a few common recipes, open a blank python file and try the following:
+After install is complete, the Continue extension will automatically open. You can also open it by pressing `cmd+k` on Mac / `ctrl+k` on Windows and then selecting `Continue: Open Continue GUI`.
-- Ask "write a calculator class in python"
-- /comment to write comments for the class
-- /pytest to write Pytest unit tests in a separate file
-- Ask in natural language for a new method
+To learn how to use it, try to use Continue for the following after `calculator.py` opens:
+- "Write me a calculator class"
+- Ask for a new method (e.g. "exp", "mod", "sqrt")
+- Type /comment to write comments for the entire class
+- Ask about how the class works, how to write it in another language, etc.
Give Continue a star on GitHub [here](https://github.com/continuedev/continue)
## Feedback
-Have thoughts about Continue? Please [leave an issue](https://github.com/continuedev/continue/issues/new) or email us at hi@continue.dev :)
+Have thoughts about Continue? Please [leave an issue](https://github.com/continuedev/continue/issues/new) or email us at hi@continue.dev :) \ No newline at end of file
diff --git a/extension/package-lock.json b/extension/package-lock.json
index c5d97cfb..fcd4c755 100644
--- a/extension/package-lock.json
+++ b/extension/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "continue",
- "version": "0.0.55",
+ "version": "0.0.70",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "continue",
- "version": "0.0.55",
+ "version": "0.0.70",
"license": "Apache-2.0",
"dependencies": {
"@electron/rebuild": "^3.2.10",
diff --git a/extension/package.json b/extension/package.json
index 036d13f0..b56c60f4 100644
--- a/extension/package.json
+++ b/extension/package.json
@@ -13,8 +13,8 @@
"license": "Apache-2.0",
"displayName": "Continue",
"pricing": "Free",
- "description": "Refine code 10x faster",
- "version": "0.0.55",
+ "description": "Accelerating software development with language models",
+ "version": "0.0.70",
"publisher": "Continue",
"engines": {
"vscode": "^1.74.0"
@@ -81,7 +81,7 @@
{
"id": "continue",
"title": "Continue ",
- "icon": "react-app/dist/continue_arrow.png"
+ "icon": "react-app/dist/play_button.png"
}
]
},
diff --git a/extension/react-app/package-lock.json b/extension/react-app/package-lock.json
index 18ef3ee6..fa624b3f 100644
--- a/extension/react-app/package-lock.json
+++ b/extension/react-app/package-lock.json
@@ -18,6 +18,7 @@
"react-markdown": "^8.0.5",
"react-redux": "^8.0.5",
"react-syntax-highlighter": "^15.5.0",
+ "react-switch": "^7.0.0",
"styled-components": "^5.3.6",
"vscode-webview": "^1.0.1-beta.1"
},
@@ -2921,19 +2922,16 @@
}
}
},
- "node_modules/react-syntax-highlighter": {
- "version": "15.5.0",
- "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.5.0.tgz",
- "integrity": "sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==",
+ "node_modules/react-switch": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/react-switch/-/react-switch-7.0.0.tgz",
+ "integrity": "sha512-KkDeW+cozZXI6knDPyUt3KBN1rmhoVYgAdCJqAh7st7tk8YE6N0iR89zjCWO8T8dUTeJGTR0KU+5CHCRMRffiA==",
"dependencies": {
- "@babel/runtime": "^7.3.1",
- "highlight.js": "^10.4.1",
- "lowlight": "^1.17.0",
- "prismjs": "^1.27.0",
- "refractor": "^3.6.0"
+ "prop-types": "^15.7.2"
},
"peerDependencies": {
- "react": ">= 0.14.0"
+ "react": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/read-cache": {
@@ -5351,16 +5349,12 @@
"use-sync-external-store": "^1.0.0"
}
},
- "react-syntax-highlighter": {
- "version": "15.5.0",
- "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.5.0.tgz",
- "integrity": "sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==",
+ "react-switch": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/react-switch/-/react-switch-7.0.0.tgz",
+ "integrity": "sha512-KkDeW+cozZXI6knDPyUt3KBN1rmhoVYgAdCJqAh7st7tk8YE6N0iR89zjCWO8T8dUTeJGTR0KU+5CHCRMRffiA==",
"requires": {
- "@babel/runtime": "^7.3.1",
- "highlight.js": "^10.4.1",
- "lowlight": "^1.17.0",
- "prismjs": "^1.27.0",
- "refractor": "^3.6.0"
+ "prop-types": "^15.7.2"
}
},
"read-cache": {
diff --git a/extension/react-app/package.json b/extension/react-app/package.json
index d43ec55e..5fe27916 100644
--- a/extension/react-app/package.json
+++ b/extension/react-app/package.json
@@ -19,6 +19,7 @@
"react-markdown": "^8.0.5",
"react-redux": "^8.0.5",
"react-syntax-highlighter": "^15.5.0",
+ "react-switch": "^7.0.0",
"styled-components": "^5.3.6",
"vscode-webview": "^1.0.1-beta.1"
},
diff --git a/extension/react-app/public/continue.gif b/extension/react-app/public/continue.gif
new file mode 100644
index 00000000..daed6663
--- /dev/null
+++ b/extension/react-app/public/continue.gif
Binary files differ
diff --git a/extension/react-app/public/continue_arrow.png b/extension/react-app/public/continue_arrow.png
deleted file mode 100644
index 3b16ddf9..00000000
--- a/extension/react-app/public/continue_arrow.png
+++ /dev/null
Binary files differ
diff --git a/extension/react-app/public/play_button.png b/extension/react-app/public/play_button.png
new file mode 100644
index 00000000..af379375
--- /dev/null
+++ b/extension/react-app/public/play_button.png
Binary files differ
diff --git a/extension/react-app/src/components/CodeBlock.tsx b/extension/react-app/src/components/CodeBlock.tsx
index a720a6ce..9438ab23 100644
--- a/extension/react-app/src/components/CodeBlock.tsx
+++ b/extension/react-app/src/components/CodeBlock.tsx
@@ -22,7 +22,7 @@ const StyledCopyButton = styled.button<{ visible: boolean }>`
/* position: relative; */
float: right;
border: none;
- background-color: transparent;
+ background-color: ${secondaryDark};
cursor: pointer;
padding: 0;
/* margin: 4px; */
@@ -77,7 +77,10 @@ function CodeBlock(props: { language?: string; children: string }) {
setHovered(false);
}}
>
- <CopyButton visible={hovered} textToCopy={props.children} />
+ <CopyButton
+ visible={hovered}
+ textToCopy={(props.children as any).props.children[0]}
+ />
<StyledCode>{props.children}</StyledCode>
</StyledPre>
);
diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx
index ddc9d5dc..3816cee8 100644
--- a/extension/react-app/src/components/ComboBox.tsx
+++ b/extension/react-app/src/components/ComboBox.tsx
@@ -116,6 +116,15 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
disabled={props.disabled}
placeholder="Type '/' to see available slash commands."
{...getInputProps({
+ onChange: (e) => {
+ const target = e.target as HTMLTextAreaElement;
+ // Update the height of the textarea to match the content, up to a max of 200px.
+ target.style.height = "auto";
+ target.style.height = `${Math.min(
+ target.scrollHeight,
+ 300
+ ).toString()}px`;
+ },
onKeyDown: (event) => {
if (event.key === "Enter" && event.shiftKey) {
// Prevent Downshift's default 'Enter' behavior.
@@ -136,14 +145,24 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
} else if (event.key === "Tab" && items.length > 0) {
setInputValue(items[0].name);
event.preventDefault();
- } else if (event.key === "ArrowUp") {
+ } else if (
+ event.key === "ArrowUp" ||
+ (event.key === "ArrowDown" &&
+ event.currentTarget.value.split("\n").length > 1)
+ ) {
+ (event.nativeEvent as any).preventDownshiftDefault = true;
+ } else if (
+ event.key === "ArrowUp" &&
+ event.currentTarget.value.split("\n").length > 1
+ ) {
if (positionInHistory == 0) return;
setInputValue(history[positionInHistory - 1]);
setPositionInHistory((prev) => prev - 1);
- } else if (event.key === "ArrowDown") {
- if (positionInHistory >= history.length - 1) {
- setInputValue("");
- } else {
+ } else if (
+ event.key === "ArrowDown" &&
+ event.currentTarget.value.split("\n").length > 1
+ ) {
+ if (positionInHistory < history.length - 1) {
setInputValue(history[positionInHistory + 1]);
}
setPositionInHistory((prev) =>
diff --git a/extension/react-app/src/components/ContinueButton.tsx b/extension/react-app/src/components/ContinueButton.tsx
index c6117bf9..ef6719b7 100644
--- a/extension/react-app/src/components/ContinueButton.tsx
+++ b/extension/react-app/src/components/ContinueButton.tsx
@@ -1,6 +1,8 @@
import styled, { keyframes } from "styled-components";
import { Button } from ".";
import { Play } from "@styled-icons/heroicons-outline";
+import { useSelector } from "react-redux";
+import { RootStore } from "../redux/store";
let StyledButton = styled(Button)`
margin: auto;
@@ -25,14 +27,21 @@ let StyledButton = styled(Button)`
`;
function ContinueButton(props: { onClick?: () => void; hidden?: boolean }) {
+ const vscMediaUrl = useSelector(
+ (state: RootStore) => state.config.vscMediaUrl
+ );
+
return (
<StyledButton
hidden={props.hidden}
className="m-auto"
onClick={props.onClick}
>
- <Play />
- {/* <img src={"/continue_arrow.png"} width="16px"></img> */}
+ {vscMediaUrl ? (
+ <img src={`${vscMediaUrl}/play_button.png`} width="22px" />
+ ) : (
+ <Play />
+ )}
Continue
</StyledButton>
);
diff --git a/extension/react-app/src/components/DebugPanel.tsx b/extension/react-app/src/components/DebugPanel.tsx
index 30f38779..94dbac9e 100644
--- a/extension/react-app/src/components/DebugPanel.tsx
+++ b/extension/react-app/src/components/DebugPanel.tsx
@@ -6,6 +6,7 @@ import {
setApiUrl,
setVscMachineId,
setSessionId,
+ setVscMediaUrl,
} from "../redux/slices/configSlice";
import { setHighlightedCode } from "../redux/slices/miscSlice";
import { updateFileSystem } from "../redux/slices/debugContexSlice";
@@ -37,6 +38,7 @@ function DebugPanel(props: DebugPanelProps) {
dispatch(setApiUrl(event.data.apiUrl));
dispatch(setVscMachineId(event.data.vscMachineId));
dispatch(setSessionId(event.data.sessionId));
+ dispatch(setVscMediaUrl(event.data.vscMediaUrl));
break;
case "highlightedCode":
dispatch(setHighlightedCode(event.data.rangeInFile));
diff --git a/extension/react-app/src/components/HeaderButtonWithText.tsx b/extension/react-app/src/components/HeaderButtonWithText.tsx
index f9483f0f..30931f86 100644
--- a/extension/react-app/src/components/HeaderButtonWithText.tsx
+++ b/extension/react-app/src/components/HeaderButtonWithText.tsx
@@ -6,14 +6,20 @@ interface HeaderButtonWithTextProps {
text: string;
onClick?: (e: any) => void;
children: React.ReactNode;
+ disabled?: boolean;
}
const HeaderButtonWithText = (props: HeaderButtonWithTextProps) => {
const [hover, setHover] = useState(false);
return (
<HeaderButton
- style={{ padding: "3px" }}
- onMouseEnter={() => setHover(true)}
+ disabled={props.disabled}
+ style={{ padding: "1px", paddingLeft: hover ? "4px" : "1px" }}
+ onMouseEnter={() => {
+ if (!props.disabled) {
+ setHover(true);
+ }
+ }}
onMouseLeave={() => {
setHover(false);
}}
diff --git a/extension/react-app/src/components/LoadingCover.tsx b/extension/react-app/src/components/LoadingCover.tsx
new file mode 100644
index 00000000..a0f8f7a2
--- /dev/null
+++ b/extension/react-app/src/components/LoadingCover.tsx
@@ -0,0 +1,50 @@
+import React from "react";
+import styled from "styled-components";
+
+const StyledDiv = styled.div`
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100vh;
+ background: linear-gradient(
+ 101.79deg,
+ #12887a 0%,
+ #87245c 32%,
+ #e12637 63%,
+ #ffb215 100%
+ );
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ z-index: 10;
+`;
+
+const StyledImg = styled.img`
+ /* add your styles here */
+`;
+
+const StyledDiv2 = styled.div`
+ width: 50%;
+ height: 5px;
+ background: white;
+ margin-top: 20px;
+`;
+
+interface LoadingCoverProps {
+ message: string;
+ hidden?: boolean;
+}
+
+const LoadingCover = (props: LoadingCoverProps) => {
+ return (
+ <StyledDiv style={{ display: props.hidden ? "none" : "inherit" }}>
+ <StyledImg src="continue.gif" alt="centered image" width="50%" />
+ <StyledDiv2></StyledDiv2>
+ <p>{props.message}</p>
+ </StyledDiv>
+ );
+};
+
+export default LoadingCover;
diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx
index 1eb1d1fd..827d2d5f 100644
--- a/extension/react-app/src/components/StepContainer.tsx
+++ b/extension/react-app/src/components/StepContainer.tsx
@@ -49,9 +49,13 @@ const StepContainerDiv = styled.div<{ open: boolean }>`
/* padding: 8px; */
`;
-const HeaderDiv = styled.div<{ error: boolean }>`
+const HeaderDiv = styled.div<{ error: boolean; loading: boolean }>`
background-color: ${(props) =>
- props.error ? "#522" : vscBackgroundTransparent};
+ props.error
+ ? "#522"
+ : props.loading
+ ? vscBackgroundTransparent
+ : vscBackground};
display: grid;
grid-template-columns: 1fr auto auto;
grid-gap: 8px;
@@ -76,12 +80,22 @@ const StyledCode = styled.code`
color: lightgray;
`;
+const gradient = keyframes`
+ 0% {
+ background-position: 0px 0;
+ }
+ 100% {
+ background-position: 100em 0;
+ }
+`;
+
const GradientBorder = styled.div<{
borderWidth?: number;
borderRadius?: string;
borderColor?: string;
isFirst: boolean;
isLast: boolean;
+ loading: boolean;
}>`
border-radius: ${(props) => props.borderRadius || "0"};
padding-top: ${(props) =>
@@ -91,13 +105,18 @@ const GradientBorder = styled.div<{
background: ${(props) =>
props.borderColor
? props.borderColor
- : `linear-gradient(
+ : `repeating-linear-gradient(
101.79deg,
#12887a 0%,
- #87245c 37.64%,
- #e12637 65.98%,
- #ffb215 110.45%
+ #87245c 16%,
+ #e12637 33%,
+ #ffb215 55%,
+ #e12637 67%,
+ #87245c 85%,
+ #12887a 99%
)`};
+ animation: ${(props) => (props.loading ? gradient : "")} 6s linear infinite;
+ background-size: 200% 200%;
`;
function StepContainer(props: StepContainerProps) {
@@ -138,10 +157,15 @@ function StepContainer(props: StepContainerProps) {
>
<StepContainerDiv open={props.open}>
<GradientBorder
+ loading={props.historyNode.active as boolean | false}
isFirst={props.isFirst}
isLast={props.isLast}
borderColor={
- props.historyNode.observation?.error ? "#f00" : undefined
+ props.historyNode.observation?.error
+ ? "#f00"
+ : props.historyNode.active
+ ? undefined
+ : "white"
}
className="overflow-hidden cursor-pointer"
onClick={(e) => {
@@ -153,6 +177,7 @@ function StepContainer(props: StepContainerProps) {
}}
>
<HeaderDiv
+ loading={props.historyNode.active as boolean | false}
error={props.historyNode.observation?.error ? true : false}
>
<h4 className="m-2">
@@ -175,6 +200,7 @@ function StepContainer(props: StepContainerProps) {
<>
<HeaderButtonWithText
+ disabled={props.historyNode.active as boolean}
onClick={(e) => {
e.stopPropagation();
props.onDelete();
diff --git a/extension/react-app/src/components/TextDialog.tsx b/extension/react-app/src/components/TextDialog.tsx
index e50a7686..2632e572 100644
--- a/extension/react-app/src/components/TextDialog.tsx
+++ b/extension/react-app/src/components/TextDialog.tsx
@@ -15,6 +15,7 @@ const DialogContainer = styled.div`
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
+ width: 75%;
`;
const Dialog = styled.div`
@@ -76,7 +77,6 @@ const TextDialog = (props: {
<Dialog>
<P>Thanks for your feedback. We'll get back to you soon!</P>
<TextArea
- cols={50}
rows={10}
ref={textAreaRef}
onKeyDown={(e) => {
diff --git a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts
index 824bb086..3d8e0a38 100644
--- a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts
+++ b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts
@@ -13,6 +13,8 @@ abstract class AbstractContinueGUIClientProtocol {
callback: (commands: { name: string; description: string }[]) => void
): void;
+ abstract changeDefaultModel(model: string): void;
+
abstract sendClear(): void;
abstract retryAtIndex(index: number): void;
diff --git a/extension/react-app/src/hooks/useContinueGUIProtocol.ts b/extension/react-app/src/hooks/useContinueGUIProtocol.ts
index 59397742..f43a66ff 100644
--- a/extension/react-app/src/hooks/useContinueGUIProtocol.ts
+++ b/extension/react-app/src/hooks/useContinueGUIProtocol.ts
@@ -55,6 +55,10 @@ class ContinueGUIClientProtocol extends AbstractContinueGUIClientProtocol {
});
}
+ changeDefaultModel(model: string) {
+ this.messenger.send("change_default_model", { model });
+ }
+
sendClear() {
this.messenger.send("clear_history", {});
}
diff --git a/extension/react-app/src/redux/slices/configSlice.ts b/extension/react-app/src/redux/slices/configSlice.ts
index a6a641e6..1b107bed 100644
--- a/extension/react-app/src/redux/slices/configSlice.ts
+++ b/extension/react-app/src/redux/slices/configSlice.ts
@@ -37,9 +37,21 @@ export const configSlice = createSlice({
...state,
sessionId: action.payload,
}),
+ setVscMediaUrl: (
+ state: RootStore["config"],
+ action: { type: string; payload: string }
+ ) => ({
+ ...state,
+ vscMediaUrl: action.payload,
+ }),
},
});
-export const { setVscMachineId, setApiUrl, setWorkspacePath, setSessionId } =
- configSlice.actions;
+export const {
+ setVscMachineId,
+ setApiUrl,
+ setWorkspacePath,
+ setSessionId,
+ setVscMediaUrl,
+} = configSlice.actions;
export default configSlice.reducer;
diff --git a/extension/react-app/src/redux/store.ts b/extension/react-app/src/redux/store.ts
index f9eb0517..a5eef4ba 100644
--- a/extension/react-app/src/redux/store.ts
+++ b/extension/react-app/src/redux/store.ts
@@ -21,6 +21,7 @@ export interface RootStore {
vscMachineId: string | undefined;
sessionId: string | undefined;
sessionStarted: number | undefined;
+ vscMediaUrl: string | undefined;
};
chat: {
messages: ChatMessage[];
diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx
index 624d22d5..13b74423 100644
--- a/extension/react-app/src/tabs/gui.tsx
+++ b/extension/react-app/src/tabs/gui.tsx
@@ -1,11 +1,5 @@
import styled from "styled-components";
-import {
- defaultBorderRadius,
- vscBackground,
- Loader,
- MainTextInput,
- HeaderButton,
-} from "../components";
+import { defaultBorderRadius, Loader } from "../components";
import ContinueButton from "../components/ContinueButton";
import { useCallback, useEffect, useRef, useState } from "react";
import { History } from "../../../schema/History";
@@ -20,6 +14,11 @@ import {
import ComboBox from "../components/ComboBox";
import TextDialog from "../components/TextDialog";
import HeaderButtonWithText from "../components/HeaderButtonWithText";
+import ReactSwitch from "react-switch";
+import { usePostHog } from "posthog-js/react";
+import { useSelector } from "react-redux";
+import { RootStore } from "../redux/store";
+import LoadingCover from "../components/LoadingCover";
const TopGUIDiv = styled.div`
overflow: hidden;
@@ -33,7 +32,7 @@ const UserInputQueueItem = styled.div`
text-align: center;
`;
-const Footer = styled.footer`
+const Footer = styled.footer<{ dataSwitchChecked: boolean }>`
display: flex;
flex-direction: row;
gap: 8px;
@@ -42,6 +41,8 @@ const Footer = styled.footer`
align-items: center;
margin-top: 8px;
border-top: 0.1px solid gray;
+ background-color: ${(props) =>
+ props.dataSwitchChecked ? "#12887a33" : "transparent"};
`;
interface GUIProps {
@@ -49,14 +50,22 @@ interface GUIProps {
}
function GUI(props: GUIProps) {
+ const posthog = usePostHog();
+ const vscMachineId = useSelector(
+ (state: RootStore) => state.config.vscMachineId
+ );
+
+ const [usingFastModel, setUsingFastModel] = useState(false);
const [waitingForSteps, setWaitingForSteps] = useState(false);
const [userInputQueue, setUserInputQueue] = useState<string[]>([]);
const [availableSlashCommands, setAvailableSlashCommands] = useState<
{ name: string; description: string }[]
>([]);
+ const [dataSwitchChecked, setDataSwitchChecked] = useState(false);
+ const [showDataSharingInfo, setShowDataSharingInfo] = useState(false);
const [stepsOpen, setStepsOpen] = useState<boolean[]>([]);
const [history, setHistory] = useState<History | undefined>();
- // {
+ // {
// timeline: [
// {
// step: {
@@ -144,6 +153,7 @@ function GUI(props: GUIProps) {
// ],
// },
// {
+ // active: false,
// step: {
// name: "SolveTracebackStep",
// traceback: {
@@ -204,24 +214,41 @@ function GUI(props: GUIProps) {
}, [topGuiDivRef.current, scrollTimeout]);
useEffect(() => {
+ const listener = (e: any) => {
+ // Cmd + J to toggle fast model
+ if (e.key === "i" && e.metaKey && e.shiftKey) {
+ setUsingFastModel((prev) => !prev);
+ }
+ };
+ window.addEventListener("keydown", listener);
+
+ return () => {
+ window.removeEventListener("keydown", listener);
+ };
+ }, []);
+
+ useEffect(() => {
console.log("CLIENT ON STATE UPDATE: ", client, client?.onStateUpdate);
client?.onStateUpdate((state) => {
// Scroll only if user is at very bottom of the window.
+ setUsingFastModel(state.default_model === "gpt-3.5-turbo");
const shouldScrollToBottom =
topGuiDivRef.current &&
topGuiDivRef.current?.offsetHeight - window.scrollY < 100;
setWaitingForSteps(state.active);
setHistory(state.history);
setUserInputQueue(state.user_input_queue);
- const nextStepsOpen = [...stepsOpen];
- for (
- let i = nextStepsOpen.length;
- i < state.history.timeline.length;
- i++
- ) {
- nextStepsOpen.push(true);
- }
- setStepsOpen(nextStepsOpen);
+ setStepsOpen((prev) => {
+ const nextStepsOpen = [...prev];
+ for (
+ let i = nextStepsOpen.length;
+ i < state.history.timeline.length;
+ i++
+ ) {
+ nextStepsOpen.push(true);
+ }
+ return nextStepsOpen;
+ });
if (shouldScrollToBottom) {
scrollToBottom();
@@ -263,8 +290,9 @@ function GUI(props: GUIProps) {
const onMainTextInput = () => {
if (mainTextInputRef.current) {
- if (!client) return;
let input = mainTextInputRef.current.value;
+ mainTextInputRef.current.value = "";
+ if (!client) return;
if (
history?.timeline.length &&
@@ -301,6 +329,7 @@ function GUI(props: GUIProps) {
// const iterations = useSelector(selectIterations);
return (
<>
+ <LoadingCover hidden={true} message="Downloading local model..." />
<TextDialog
showDialog={showFeedbackDialog}
onEnter={(text) => {
@@ -323,9 +352,7 @@ function GUI(props: GUIProps) {
{typeof client === "undefined" && (
<>
<Loader></Loader>
- <p style={{ textAlign: "center" }}>
- Trying to reconnect with server...
- </p>
+ <p style={{ textAlign: "center" }}>Loading Continue server...</p>
</>
)}
{history?.timeline.map((node: HistoryNode, index: number) => {
@@ -340,7 +367,7 @@ function GUI(props: GUIProps) {
setStepsOpen(nextStepsOpen);
}}
onToggleAll={() => {
- setStepsOpen((prev) => prev.map(() => !prev[index]));
+ setStepsOpen((prev) => prev.map((_, index) => !prev[index]));
}}
key={index}
onUserInput={(input: string) => {
@@ -390,17 +417,87 @@ function GUI(props: GUIProps) {
/>
<ContinueButton onClick={onMainTextInput} />
</TopGUIDiv>
- <Footer>
+ <div
+ style={{
+ position: "fixed",
+ bottom: "50px",
+ backgroundColor: "white",
+ color: "black",
+ borderRadius: defaultBorderRadius,
+ padding: "16px",
+ margin: "16px",
+ }}
+ hidden={!showDataSharingInfo}
+ >
+ By turning on this switch, you signal that you would contribute this
+ software development data to a publicly accessible, open-source dataset
+ in the future.
+ <br />
+ <br />
+ <b>
+ {dataSwitchChecked
+ ? "No data is being collected. In the future, you would be contributing data"
+ : "No data is being collected. In the future, your data would not be shared"}
+ </b>
+ </div>
+ <Footer dataSwitchChecked={dataSwitchChecked}>
+ <div
+ style={{
+ display: "flex",
+ gap: "4px",
+ marginRight: "auto",
+ alignItems: "center",
+ }}
+ onMouseEnter={() => {
+ setShowDataSharingInfo(true);
+ }}
+ onMouseLeave={() => {
+ setShowDataSharingInfo(false);
+ }}
+ >
+ <ReactSwitch
+ height={20}
+ handleDiameter={20}
+ width={40}
+ onChange={() => {
+ posthog?.capture("data_switch_toggled", {
+ vscMachineId: vscMachineId,
+ dataSwitchChecked: !dataSwitchChecked,
+ });
+ setDataSwitchChecked((prev) => !prev);
+ }}
+ onColor="#12887a"
+ checked={dataSwitchChecked}
+ />
+ <span style={{ cursor: "help", fontSize: "14px" }}>
+ Contribute Data
+ </span>
+ </div>
+ <HeaderButtonWithText
+ onClick={() => {
+ client?.changeDefaultModel(
+ usingFastModel ? "gpt-4" : "gpt-3.5-turbo"
+ );
+ setUsingFastModel((prev) => !prev);
+ }}
+ text={usingFastModel ? "gpt-3.5-turbo" : "gpt-4"}
+ >
+ <div
+ style={{ fontSize: "18px", marginLeft: "2px", marginRight: "2px" }}
+ >
+ {usingFastModel ? "⚡" : "🧠"}
+ </div>
+ </HeaderButtonWithText>
<HeaderButtonWithText
onClick={() => {
client?.sendClear();
}}
- text="Clear History"
+ text="Clear All"
>
<Trash size="1.6em" />
</HeaderButtonWithText>
<a href="https://continue.dev/docs" className="no-underline">
- <HeaderButtonWithText text="Continue Docs">
+ <HeaderButtonWithText text="Docs">
<BookOpen size="1.6em" />
</HeaderButtonWithText>
</a>
diff --git a/extension/scripts/.gitignore b/extension/scripts/.gitignore
index 7af27c08..fbb3bf9f 100644
--- a/extension/scripts/.gitignore
+++ b/extension/scripts/.gitignore
@@ -1,3 +1,5 @@
testdb
env
-stdout.txt \ No newline at end of file
+stdout.txt
+.continue_env_installed
+**.whl \ No newline at end of file
diff --git a/extension/scripts/continuedev-0.1.1-py3-none-any.whl b/extension/scripts/continuedev-0.1.1-py3-none-any.whl
deleted file mode 100644
index 9d371e17..00000000
--- a/extension/scripts/continuedev-0.1.1-py3-none-any.whl
+++ /dev/null
Binary files differ
diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts
index 32726c86..0c92f095 100644
--- a/extension/src/activation/activate.ts
+++ b/extension/src/activation/activate.ts
@@ -2,30 +2,31 @@ import * as vscode from "vscode";
import { registerAllCommands } from "../commands";
import { registerAllCodeLensProviders } from "../lang-server/codeLens";
import { sendTelemetryEvent, TelemetryEvent } from "../telemetry";
-import { getExtensionUri } from "../util/vscode";
-import * as path from "path";
// import { openCapturedTerminal } from "../terminal/terminalEmulator";
import IdeProtocolClient from "../continueIdeClient";
import { getContinueServerUrl } from "../bridge";
-import { setupDebugPanel, ContinueGUIWebviewViewProvider } from "../debugPanel";
import { CapturedTerminal } from "../terminal/terminalEmulator";
+import { setupDebugPanel, ContinueGUIWebviewViewProvider } from "../debugPanel";
+import { startContinuePythonServer } from "./environmentSetup";
+// import { CapturedTerminal } from "../terminal/terminalEmulator";
export let extensionContext: vscode.ExtensionContext | undefined = undefined;
export let ideProtocolClient: IdeProtocolClient;
-export function activateExtension(
+export async function activateExtension(
context: vscode.ExtensionContext,
showTutorial: boolean
) {
- sendTelemetryEvent(TelemetryEvent.ExtensionActivated);
+ extensionContext = context;
+ sendTelemetryEvent(TelemetryEvent.ExtensionActivated);
registerAllCodeLensProviders(context);
registerAllCommands(context);
// vscode.window.registerWebviewViewProvider("continue.continueGUIView", setupDebugPanel);
-
- let serverUrl = getContinueServerUrl();
+ await startContinuePythonServer();
+ const serverUrl = getContinueServerUrl();
ideProtocolClient = new IdeProtocolClient(
`${serverUrl.replace("http", "ws")}/ide/ws`,
@@ -49,41 +50,8 @@ export function activateExtension(
})();
// All opened terminals should be replaced by our own terminal
- vscode.window.onDidOpenTerminal((terminal) => {
- if (terminal.name === "Continue") {
- return;
- }
- const options = terminal.creationOptions;
- const capturedTerminal = new CapturedTerminal({
- ...options,
- name: "Continue",
- });
- terminal.dispose();
- if (!ideProtocolClient.continueTerminal) {
- ideProtocolClient.continueTerminal = capturedTerminal;
- }
- });
+ // vscode.window.onDidOpenTerminal((terminal) => {});
// If any terminals are open to start, replace them
- vscode.window.terminals.forEach((terminal) => {
- if (terminal.name === "Continue") {
- return;
- }
- const options = terminal.creationOptions;
- const capturedTerminal = new CapturedTerminal(
- {
- ...options,
- name: "Continue",
- },
- (commandOutput: string) => {
- ideProtocolClient.sendCommandOutput(commandOutput);
- }
- );
- terminal.dispose();
- if (!ideProtocolClient.continueTerminal) {
- ideProtocolClient.continueTerminal = capturedTerminal;
- }
- });
-
- extensionContext = context;
+ // vscode.window.terminals.forEach((terminal) => {}
}
diff --git a/extension/src/activation/environmentSetup.ts b/extension/src/activation/environmentSetup.ts
index 593b727e..25b6f643 100644
--- a/extension/src/activation/environmentSetup.ts
+++ b/extension/src/activation/environmentSetup.ts
@@ -7,6 +7,25 @@ import * as fs from "fs";
import rebuild from "@electron/rebuild";
import { getContinueServerUrl } from "../bridge";
import fetch from "node-fetch";
+import * as vscode from "vscode";
+
+const MAX_RETRIES = 5;
+async function retryThenFail(
+ fn: () => Promise<any>,
+ retries: number = MAX_RETRIES
+): Promise<any> {
+ try {
+ return await fn();
+ } catch (e) {
+ if (retries > 0) {
+ return await retryThenFail(fn, retries - 1);
+ }
+ vscode.window.showErrorMessage(
+ "Failed to set up Continue extension. Please email nate@continue.dev and we'll get this fixed ASAP!"
+ );
+ throw e;
+ }
+}
async function runCommand(cmd: string): Promise<[string, string | undefined]> {
console.log("Running command: ", cmd);
@@ -77,55 +96,71 @@ function checkEnvExists() {
);
}
+function checkRequirementsInstalled() {
+ return fs.existsSync(
+ path.join(getExtensionUri().fsPath, "scripts", ".continue_env_installed")
+ );
+}
+
async function setupPythonEnv() {
console.log("Setting up python env for Continue extension...");
- if (checkEnvExists()) return;
-
- // Assemble the command to create the env
const [pythonCmd, pipCmd] = await getPythonPipCommands();
const [activateCmd, pipUpgradeCmd] = getActivateUpgradeCommands(
pythonCmd,
pipCmd
);
- const createEnvCommand = [
- `cd ${path.join(getExtensionUri().fsPath, "scripts")}`,
- `${pythonCmd} -m venv env`,
- ].join("; ");
- // Repeat until it is successfully created (sometimes it fails to generate the bin, need to try again)
- while (true) {
- const [, stderr] = await runCommand(createEnvCommand);
- if (stderr) {
- throw new Error(stderr);
- }
- if (checkEnvExists()) {
- break;
- } else {
- // Remove the env and try again
- const removeCommand = `rm -rf ${path.join(
- getExtensionUri().fsPath,
- "scripts",
- "env"
- )}`;
- await runCommand(removeCommand);
+ if (checkEnvExists()) {
+ console.log("Python env already exists, skipping...");
+ } else {
+ // Assemble the command to create the env
+ const createEnvCommand = [
+ `cd ${path.join(getExtensionUri().fsPath, "scripts")}`,
+ `${pythonCmd} -m venv env`,
+ ].join("; ");
+
+ // Repeat until it is successfully created (sometimes it fails to generate the bin, need to try again)
+ while (true) {
+ const [, stderr] = await runCommand(createEnvCommand);
+ if (stderr) {
+ throw new Error(stderr);
+ }
+ if (checkEnvExists()) {
+ break;
+ } else {
+ // Remove the env and try again
+ const removeCommand = `rm -rf ${path.join(
+ getExtensionUri().fsPath,
+ "scripts",
+ "env"
+ )}`;
+ await runCommand(removeCommand);
+ }
}
+ console.log(
+ "Successfully set up python env at ",
+ getExtensionUri().fsPath + "/scripts/env"
+ );
}
- console.log(
- "Successfully set up python env at ",
- getExtensionUri().fsPath + "/scripts/env"
- );
- const installRequirementsCommand = [
- `cd ${path.join(getExtensionUri().fsPath, "scripts")}`,
- activateCmd,
- pipUpgradeCmd,
- `${pipCmd} install -r requirements.txt`,
- ].join(" && ");
- const [, stderr] = await runCommand(installRequirementsCommand);
- if (stderr) {
- throw new Error(stderr);
- }
+ await retryThenFail(async () => {
+ if (checkRequirementsInstalled()) {
+ console.log("Python requirements already installed, skipping...");
+ } else {
+ const installRequirementsCommand = [
+ `cd ${path.join(getExtensionUri().fsPath, "scripts")}`,
+ activateCmd,
+ pipUpgradeCmd,
+ `${pipCmd} install -r requirements.txt`,
+ "touch .continue_env_installed",
+ ].join(" ; ");
+ const [, stderr] = await runCommand(installRequirementsCommand);
+ if (stderr) {
+ throw new Error(stderr);
+ }
+ }
+ });
}
function readEnvFile(path: string) {
@@ -180,15 +215,11 @@ export async function startContinuePythonServer() {
await setupPythonEnv();
// Check vscode settings
- let serverUrl = getContinueServerUrl();
+ const serverUrl = getContinueServerUrl();
if (serverUrl !== "http://localhost:8000") {
return;
}
- console.log("Starting Continue python server...");
-
- if (await checkServerRunning(serverUrl)) return;
-
let activateCmd = ". env/bin/activate";
let pythonCmd = "python3";
if (process.platform == "win32") {
@@ -199,35 +230,41 @@ export async function startContinuePythonServer() {
let command = `cd ${path.join(
getExtensionUri().fsPath,
"scripts"
- )} && ${activateCmd} && cd .. && ${pythonCmd} -m scripts.run_continue_server`;
+ )} ; ${activateCmd} ; cd .. ; ${pythonCmd} -m scripts.run_continue_server`;
+
+ return await retryThenFail(async () => {
+ console.log("Starting Continue python server...");
+
+ if (await checkServerRunning(serverUrl)) return;
- return new Promise(async (resolve, reject) => {
- try {
- const child = spawn(command, {
- shell: true,
- });
- child.stdout.on("data", (data: any) => {
- console.log(`stdout: ${data}`);
- });
- child.stderr.on("data", (data: any) => {
- console.log(`stderr: ${data}`);
- if (data.includes("Uvicorn running on")) {
- console.log("Successfully started Continue python server");
+ return new Promise(async (resolve, reject) => {
+ try {
+ const child = spawn(command, {
+ shell: true,
+ });
+ child.stdout.on("data", (data: any) => {
+ console.log(`stdout: ${data}`);
+ });
+ child.stderr.on("data", (data: any) => {
+ console.log(`stderr: ${data}`);
+ if (data.includes("Uvicorn running on")) {
+ console.log("Successfully started Continue python server");
+ resolve(null);
+ }
+ });
+ child.on("error", (error: any) => {
+ console.log(`error: ${error.message}`);
+ });
+ } catch (e) {
+ console.log("Failed to start Continue python server", e);
+ // If failed, check if it's because the server is already running (might have happened just after we checked above)
+ if (await checkServerRunning(serverUrl)) {
resolve(null);
+ } else {
+ reject();
}
- });
- child.on("error", (error: any) => {
- console.log(`error: ${error.message}`);
- });
- } catch (e) {
- console.log("Failed to start Continue python server", e);
- // If failed, check if it's because the server is already running (might have happened just after we checked above)
- if (await checkServerRunning(serverUrl)) {
- resolve(null);
- } else {
- reject();
}
- }
+ });
});
}
@@ -255,10 +292,10 @@ export async function downloadPython3() {
throw new Error("python3 not found");
} else if (os === "linux") {
command =
- "sudo apt update && upgrade && sudo apt install python3 python3-pip";
+ "sudo apt update ; upgrade ; sudo apt install python3 python3-pip";
} else if (os === "win32") {
command =
- "wget -O python_installer.exe https://www.python.org/ftp/python/3.11.3/python-3.11.3-amd64.exe && python_installer.exe /quiet InstallAllUsers=1 PrependPath=1 Include_test=0";
+ "wget -O python_installer.exe https://www.python.org/ftp/python/3.11.3/python-3.11.3-amd64.exe ; python_installer.exe /quiet InstallAllUsers=1 PrependPath=1 Include_test=0";
pythonCmd = "python";
}
diff --git a/extension/src/commands.ts b/extension/src/commands.ts
index 5392a7a3..77273343 100644
--- a/extension/src/commands.ts
+++ b/extension/src/commands.ts
@@ -3,7 +3,6 @@ import {
decorationManager,
showAnswerInTextEditor,
showGutterSpinner,
- writeAndShowUnitTest,
} from "./decorations";
import {
acceptSuggestionCommand,
@@ -12,19 +11,8 @@ import {
suggestionUpCommand,
} from "./suggestions";
import * as bridge from "./bridge";
-import { debugPanelWebview, setupDebugPanel } from "./debugPanel";
-// import { openCapturedTerminal } from "./terminal/terminalEmulator";
-import { getRightViewColumn } from "./util/vscode";
-import {
- findSuspiciousCode,
- runPythonScript,
- writeUnitTestForFunction,
-} from "./bridge";
+import { debugPanelWebview } from "./debugPanel";
import { sendTelemetryEvent, TelemetryEvent } from "./telemetry";
-import { getLanguageLibrary } from "./languages";
-import { SerializedDebugContext } from "./client";
-import { addFileSystemToDebugContext } from "./util/util";
-import { ideProtocolClient } from "./activation/activate";
// Copy everything over from extension.ts
const commandsMap: { [command: string]: (...args: any) => any } = {
@@ -62,79 +50,14 @@ const commandsMap: { [command: string]: (...args: any) => any } = {
"continue.acceptSuggestion": acceptSuggestionCommand,
"continue.rejectSuggestion": rejectSuggestionCommand,
"continue.focusContinueInput": async () => {
- if (!debugPanelWebview) {
- vscode.commands.executeCommand("continue.continueGUIView.focus");
- }
+ vscode.commands.executeCommand("continue.continueGUIView.focus");
debugPanelWebview?.postMessage({
type: "focusContinueInput",
});
},
- "continue.openCapturedTerminal": () => {
- // Happens in webview resolution function
- // openCapturedTerminal();
- },
- "continue.findSuspiciousCode": async (
- debugContext: SerializedDebugContext
- ) => {
- vscode.window.withProgress(
- {
- location: vscode.ProgressLocation.Notification,
- title: "Finding suspicious code",
- cancellable: false,
- },
- async (progress, token) => {
- let suspiciousCode = await findSuspiciousCode(debugContext);
- debugContext.rangesInFiles = suspiciousCode;
- let { filesystem } = addFileSystemToDebugContext(debugContext);
- debugPanelWebview?.postMessage({
- type: "findSuspiciousCode",
- codeLocations: suspiciousCode,
- filesystem,
- });
- }
- );
- },
- "continue.debugTest": async (fileAndFunctionSpecifier: string) => {
- sendTelemetryEvent(TelemetryEvent.AutoDebugThisTest);
- let editor = vscode.window.activeTextEditor;
- if (editor) editor.document.save();
- let { stdout } = await runPythonScript("run_unit_test.py", [
- fileAndFunctionSpecifier,
- ]);
- let traceback = getLanguageLibrary(
- fileAndFunctionSpecifier.split("::")[0]
- ).parseFirstStacktrace(stdout);
- if (!traceback) {
- vscode.window.showInformationMessage("The test passes!");
- return;
- }
- vscode.commands.executeCommand("continue.openContinueGUI").then(() => {
- setTimeout(() => {
- debugPanelWebview?.postMessage({
- type: "traceback",
- value: traceback,
- });
- }, 500);
- });
- },
};
const textEditorCommandsMap: { [command: string]: (...args: any) => {} } = {
- "continue.writeUnitTest": async (editor: vscode.TextEditor) => {
- let position = editor.selection.active;
-
- let gutterSpinnerKey = showGutterSpinner(editor, position.line);
- try {
- let test = await writeUnitTestForFunction(
- editor.document.fileName,
- position
- );
- writeAndShowUnitTest(editor.document.fileName, test);
- } catch {
- } finally {
- decorationManager.deleteDecoration(gutterSpinnerKey);
- }
- },
"continue.writeDocstring": async (editor: vscode.TextEditor, _) => {
sendTelemetryEvent(TelemetryEvent.GenerateDocstring);
let gutterSpinnerKey = showGutterSpinner(
@@ -201,20 +124,3 @@ async function answerQuestion(
}
);
}
-
-// async function suggestFixForAllWorkspaceProblems() {
-// Something like this, just figure out the loops for diagnostics vs problems
-// let problems = vscode.languages.getDiagnostics();
-// let codeSuggestions = await Promise.all(problems.map((problem) => {
-// return bridge.suggestFixForProblem(problem[0].fsPath, problem[1]);
-// }));
-// for (const [uri, diagnostics] of problems) {
-// for (let i = 0; i < diagnostics.length; i++) {
-// let diagnostic = diagnostics[i];
-// let suggestedCode = codeSuggestions[i];
-// // If you're going to do this for a bunch of files at once, it will show the unsaved icon in the tab
-// // BUT it would be better to have a single window to review all edits
-// showSuggestion(uri.fsPath, diagnostic.range, suggestedCode)
-// }
-// }
-// }
diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts
index 3b5de93f..3a77e348 100644
--- a/extension/src/continueIdeClient.ts
+++ b/extension/src/continueIdeClient.ts
@@ -12,7 +12,6 @@ import { debugPanelWebview, setupDebugPanel } from "./debugPanel";
import { FileEditWithFullContents } from "../schema/FileEditWithFullContents";
import fs = require("fs");
import { WebsocketMessenger } from "./util/messenger";
-import { CapturedTerminal } from "./terminal/terminalEmulator";
import { decorationManager } from "./decorations";
class IdeProtocolClient {
@@ -159,7 +158,7 @@ class IdeProtocolClient {
editor.setDecorations(decorationType, [range]);
// Listen for changes to cursor position and then remove the decoration (but keep for at least 2 seconds)
- setTimeout(() => {
+ const allowRemoveHighlight = () => {
const cursorDisposable = vscode.window.onDidChangeTextEditorSelection(
(event) => {
if (event.textEditor.document.uri.fsPath === rangeInFile.filepath) {
@@ -168,7 +167,8 @@ class IdeProtocolClient {
}
}
);
- }, 2000);
+ };
+ setTimeout(allowRemoveHighlight, 2000);
}
}
@@ -285,7 +285,7 @@ class IdeProtocolClient {
edit.range.start.line,
edit.range.start.character,
edit.range.end.line,
- edit.range.end.character + 1
+ edit.range.end.character
);
editor.edit((editBuilder) => {
@@ -325,17 +325,14 @@ class IdeProtocolClient {
return rangeInFiles;
}
- public continueTerminal: CapturedTerminal | undefined;
-
async runCommand(command: string) {
- if (!this.continueTerminal || this.continueTerminal.isClosed()) {
- this.continueTerminal = new CapturedTerminal({
- name: "Continue",
- });
+ if (vscode.window.terminals.length) {
+ vscode.window.terminals[0].sendText(command);
+ } else {
+ const terminal = vscode.window.createTerminal();
+ terminal.show();
+ terminal.sendText(command);
}
-
- this.continueTerminal.show();
- return await this.continueTerminal.runCommand(command);
}
sendCommandOutput(output: string) {
diff --git a/extension/src/debugPanel.ts b/extension/src/debugPanel.ts
index bb98eb46..b0db590a 100644
--- a/extension/src/debugPanel.ts
+++ b/extension/src/debugPanel.ts
@@ -1,21 +1,11 @@
import * as vscode from "vscode";
-import {
- debugApi,
- getContinueServerUrl,
- runPythonScript,
- unittestApi,
-} from "./bridge";
-import { writeAndShowUnitTest } from "./decorations";
-import { showSuggestion } from "./suggestions";
-import { getLanguageLibrary } from "./languages";
+import { getContinueServerUrl } from "./bridge";
import {
getExtensionUri,
getNonce,
openEditorAndRevealRange,
} from "./util/vscode";
-import { sendTelemetryEvent, TelemetryEvent } from "./telemetry";
-import { RangeInFile, SerializedDebugContext } from "./client";
-import { addFileSystemToDebugContext } from "./util/util";
+import { RangeInFile } from "./client";
const WebSocket = require("ws");
class StreamManager {
@@ -146,6 +136,9 @@ export function setupDebugPanel(
let extensionUri = getExtensionUri();
let scriptUri: string;
let styleMainUri: string;
+ let vscMediaUrl: string = debugPanelWebview
+ .asWebviewUri(vscode.Uri.joinPath(extensionUri, "react-app/dist"))
+ .toString();
const isProduction = true; // context?.extensionMode === vscode.ExtensionMode.Development;
if (!isProduction) {
@@ -236,6 +229,7 @@ export function setupDebugPanel(
vscMachineId: vscode.env.machineId,
apiUrl: getContinueServerUrl(),
sessionId,
+ vscMediaUrl,
});
// // Listen for changes to server URL in settings
@@ -273,71 +267,6 @@ export function setupDebugPanel(
connection.send(data.message);
break;
}
- case "listTenThings": {
- sendTelemetryEvent(TelemetryEvent.GenerateIdeas);
- let resp = await debugApi.listtenDebugListPost({
- serializedDebugContext: data.debugContext,
- });
- panel.webview.postMessage({
- type: "listTenThings",
- value: resp.completion,
- });
- break;
- }
- case "suggestFix": {
- let completion: string;
- let codeSelection = data.debugContext.rangesInFiles?.at(0);
- if (codeSelection) {
- completion = (
- await debugApi.inlineDebugInlinePost({
- inlineBody: {
- filecontents: await vscode.workspace.fs
- .readFile(vscode.Uri.file(codeSelection.filepath))
- .toString(),
- startline: codeSelection.range.start.line,
- endline: codeSelection.range.end.line,
- traceback: data.debugContext.traceback,
- },
- })
- ).completion;
- } else if (data.debugContext.traceback) {
- completion = (
- await debugApi.suggestionDebugSuggestionGet({
- traceback: data.debugContext.traceback,
- })
- ).completion;
- } else {
- break;
- }
- panel.webview.postMessage({
- type: "suggestFix",
- value: completion,
- });
- break;
- }
- case "findSuspiciousCode": {
- let traceback = getLanguageLibrary(".py").parseFirstStacktrace(
- data.debugContext.traceback
- );
- if (traceback === undefined) return;
- vscode.commands.executeCommand(
- "continue.findSuspiciousCode",
- data.debugContext
- );
- break;
- }
- case "queryEmbeddings": {
- let { results } = await runPythonScript("index.py query", [
- data.query,
- 2,
- vscode.workspace.workspaceFolders?.[0].uri.fsPath,
- ]);
- panel.webview.postMessage({
- type: "queryEmbeddings",
- results,
- });
- break;
- }
case "openFile": {
openEditorAndRevealRange(data.path, undefined, vscode.ViewColumn.One);
break;
@@ -351,20 +280,6 @@ export function setupDebugPanel(
streamManager.closeStream();
break;
}
- case "explainCode": {
- sendTelemetryEvent(TelemetryEvent.ExplainCode);
- let debugContext: SerializedDebugContext = addFileSystemToDebugContext(
- data.debugContext
- );
- let resp = await debugApi.explainDebugExplainPost({
- serializedDebugContext: debugContext,
- });
- panel.webview.postMessage({
- type: "explainCode",
- value: resp.completion,
- });
- break;
- }
case "withProgress": {
// This message allows withProgress to be used in the webview
if (data.done) {
@@ -395,83 +310,6 @@ export function setupDebugPanel(
);
break;
}
- case "makeEdit": {
- sendTelemetryEvent(TelemetryEvent.SuggestFix);
- let suggestedEdits = data.edits;
-
- if (
- typeof suggestedEdits === "undefined" ||
- suggestedEdits.length === 0
- ) {
- vscode.window.showInformationMessage(
- "Continue couldn't find a fix for this error."
- );
- return;
- }
-
- for (let i = 0; i < suggestedEdits.length; i++) {
- let edit = suggestedEdits[i];
- await showSuggestion(
- edit.filepath,
- new vscode.Range(
- edit.range.start.line,
- edit.range.start.character,
- edit.range.end.line,
- edit.range.end.character
- ),
- edit.replacement
- );
- }
- break;
- }
- case "generateUnitTest": {
- sendTelemetryEvent(TelemetryEvent.CreateTest);
- vscode.window.withProgress(
- {
- location: vscode.ProgressLocation.Notification,
- title: "Generating Unit Test...",
- cancellable: false,
- },
- async () => {
- for (let i = 0; i < data.debugContext.rangesInFiles?.length; i++) {
- let codeSelection = data.debugContext.rangesInFiles?.at(i);
- if (
- codeSelection &&
- codeSelection.filepath &&
- codeSelection.range
- ) {
- try {
- let filecontents = (
- await vscode.workspace.fs.readFile(
- vscode.Uri.file(codeSelection.filepath)
- )
- ).toString();
- let resp =
- await unittestApi.failingtestUnittestFailingtestPost({
- failingTestBody: {
- fp: {
- filecontents,
- lineno: codeSelection.range.end.line,
- },
- description: data.debugContext.description || "",
- },
- });
-
- if (resp.completion) {
- let decorationKey = await writeAndShowUnitTest(
- codeSelection.filepath,
- resp.completion
- );
- break;
- }
- } catch {}
- }
- }
- }
- );
-
- break;
- }
}
});
diff --git a/extension/src/decorations.ts b/extension/src/decorations.ts
index d2c94135..0587110c 100644
--- a/extension/src/decorations.ts
+++ b/extension/src/decorations.ts
@@ -1,7 +1,5 @@
import * as vscode from "vscode";
-import { getRightViewColumn, getTestFile } from "./util/vscode";
import * as path from "path";
-import { getLanguageLibrary } from "./languages";
export function showAnswerInTextEditor(
filename: string,
@@ -223,98 +221,3 @@ export function highlightCode(
return key;
}
-
-// Show unit test
-const pythonImportDistinguisher = (line: string): boolean => {
- if (line.startsWith("from") || line.startsWith("import")) {
- return true;
- }
- return false;
-};
-const javascriptImportDistinguisher = (line: string): boolean => {
- if (line.startsWith("import")) {
- return true;
- }
- return false;
-};
-const importDistinguishersMap: {
- [fileExtension: string]: (line: string) => boolean;
-} = {
- js: javascriptImportDistinguisher,
- ts: javascriptImportDistinguisher,
- py: pythonImportDistinguisher,
-};
-function getImportsFromFileString(
- fileString: string,
- importDistinguisher: (line: string) => boolean
-): Set<string> {
- let importLines = new Set<string>();
- for (let line of fileString.split("\n")) {
- if (importDistinguisher(line)) {
- importLines.add(line);
- }
- }
- return importLines;
-}
-function removeRedundantLinesFrom(
- fileContents: string,
- linesToRemove: Set<string>
-): string {
- let fileLines = fileContents.split("\n");
- fileLines = fileLines.filter((line: string) => {
- return !linesToRemove.has(line);
- });
- return fileLines.join("\n");
-}
-
-export async function writeAndShowUnitTest(
- filename: string,
- test: string
-): Promise<DecorationKey> {
- return new Promise((resolve, reject) => {
- let testFilename = getTestFile(filename, true);
- vscode.workspace.openTextDocument(testFilename).then((doc) => {
- let fileContent = doc.getText();
- let fileEmpty = fileContent.trim() === "";
- let existingImportLines = getImportsFromFileString(
- fileContent,
- importDistinguishersMap[doc.fileName.split(".").at(-1) || ".py"]
- );
-
- // Remove redundant imports, make sure pytest is there
- test = removeRedundantLinesFrom(test, existingImportLines);
- test =
- (fileEmpty
- ? `${getLanguageLibrary(".py").writeImport(
- testFilename,
- filename
- )}\nimport pytest\n\n`
- : "\n\n") +
- test.trim() +
- "\n";
-
- vscode.window
- .showTextDocument(doc, getRightViewColumn())
- .then((editor) => {
- let lastLine = editor.document.lineAt(editor.document.lineCount - 1);
- let testRange = new vscode.Range(
- lastLine.range.end,
- new vscode.Position(
- test.split("\n").length + lastLine.range.end.line,
- 0
- )
- );
- editor
- .edit((edit) => {
- edit.insert(lastLine.range.end, test);
- return true;
- })
- .then((success) => {
- if (!success) reject("Failed to insert test");
- let key = highlightCode(editor, testRange);
- resolve(key);
- });
- });
- });
- });
-}
diff --git a/extension/src/extension.ts b/extension/src/extension.ts
index 88af0d19..de8f55e3 100644
--- a/extension/src/extension.ts
+++ b/extension/src/extension.ts
@@ -13,7 +13,7 @@ async function dynamicImportAndActivate(
showTutorial: boolean
) {
const { activateExtension } = await import("./activation/activate");
- activateExtension(context, showTutorial);
+ await activateExtension(context, showTutorial);
}
export function activate(context: vscode.ExtensionContext) {
@@ -25,7 +25,6 @@ export function activate(context: vscode.ExtensionContext) {
cancellable: false,
},
async () => {
- await startContinuePythonServer();
dynamicImportAndActivate(context, true);
}
);
diff --git a/extension/src/lang-server/codeLens.ts b/extension/src/lang-server/codeLens.ts
index 2a362b62..26528d96 100644
--- a/extension/src/lang-server/codeLens.ts
+++ b/extension/src/lang-server/codeLens.ts
@@ -1,5 +1,4 @@
import * as vscode from "vscode";
-import { getLanguageLibrary } from "../languages";
import { editorToSuggestions } from "../suggestions";
class SuggestionsCodeLensProvider implements vscode.CodeLensProvider {
@@ -52,40 +51,9 @@ class SuggestionsCodeLensProvider implements vscode.CodeLensProvider {
}
}
-class PytestCodeLensProvider implements vscode.CodeLensProvider {
- public provideCodeLenses(
- document: vscode.TextDocument,
- token: vscode.CancellationToken
- ): vscode.CodeLens[] | Thenable<vscode.CodeLens[]> {
- let codeLenses: vscode.CodeLens[] = [];
- let lineno = 1;
- let languageLibrary = getLanguageLibrary(document.fileName);
- for (let line of document.getText().split("\n")) {
- if (
- languageLibrary.lineIsFunctionDef(line) &&
- languageLibrary.parseFunctionDefForName(line).startsWith("test_")
- ) {
- let functionToTest = languageLibrary.parseFunctionDefForName(line);
- let fileAndFunctionNameSpecifier =
- document.fileName + "::" + functionToTest;
- codeLenses.push(
- new vscode.CodeLens(new vscode.Range(lineno, 0, lineno, 1), {
- title: "Debug This Test",
- command: "continue.debugTest",
- arguments: [fileAndFunctionNameSpecifier],
- })
- );
- }
- lineno++;
- }
-
- return codeLenses;
- }
-}
-
const allCodeLensProviders: { [langauge: string]: vscode.CodeLensProvider[] } =
{
- python: [new SuggestionsCodeLensProvider(), new PytestCodeLensProvider()],
+ python: [new SuggestionsCodeLensProvider()],
};
export function registerAllCodeLensProviders(context: vscode.ExtensionContext) {
diff --git a/extension/src/languages/index.d.ts b/extension/src/languages/index.d.ts
deleted file mode 100644
index be7ddfbc..00000000
--- a/extension/src/languages/index.d.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-export interface LanguageLibrary {
- language: string;
- fileExtensions: string[];
- parseFirstStacktrace: (stdout: string) => string | undefined;
- lineIsFunctionDef: (line: string) => boolean;
- parseFunctionDefForName: (line: string) => string;
- lineIsComment: (line: string) => boolean;
- writeImport: (
- sourcePath: string,
- pathToImport: string,
- namesToImport?: string[] | undefined
- ) => string;
-}
diff --git a/extension/src/languages/index.ts b/extension/src/languages/index.ts
deleted file mode 100644
index 31d73a0b..00000000
--- a/extension/src/languages/index.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import pythonLanguageLibrary from "./python";
-import javascriptLanguageLibrary from "./javascript";
-import { LanguageLibrary } from "./index.d";
-
-export const languageLibraries: LanguageLibrary[] = [
- pythonLanguageLibrary,
- javascriptLanguageLibrary,
-];
-
-export function getLanguageLibrary(filepath: string): LanguageLibrary {
- for (let languageLibrary of languageLibraries) {
- for (let fileExtension of languageLibrary.fileExtensions) {
- if (filepath.endsWith(fileExtension)) {
- return languageLibrary;
- }
- }
- }
- throw new Error(`No language library found for file ${filepath}`);
-}
diff --git a/extension/src/languages/javascript/index.ts b/extension/src/languages/javascript/index.ts
deleted file mode 100644
index 1c21a2fc..00000000
--- a/extension/src/languages/javascript/index.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { LanguageLibrary } from "../index.d";
-import { notImplemented } from "../notImplemented";
-
-const NI = (propertyName: string) => notImplemented(propertyName, "javascript");
-
-const javascriptLangaugeLibrary: LanguageLibrary = {
- language: "javascript",
- fileExtensions: [".js", ".jsx", ".ts", ".tsx"],
- parseFirstStacktrace: NI("parseFirstStacktrace"),
- lineIsFunctionDef: NI("lineIsFunctionDef"),
- parseFunctionDefForName: NI("parseFunctionDefForName"),
- lineIsComment: NI("lineIsComment"),
- writeImport: NI("writeImport"),
-};
-
-export default javascriptLangaugeLibrary;
diff --git a/extension/src/languages/notImplemented.ts b/extension/src/languages/notImplemented.ts
deleted file mode 100644
index bbba2382..00000000
--- a/extension/src/languages/notImplemented.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-export function notImplemented(
- propertyName: string,
- langauge: string
-): (...args: any[]) => never {
- return (...args: any[]) => {
- throw new Error(
- `Property ${propertyName} not implemented for language ${langauge}.`
- );
- };
-}
diff --git a/extension/src/languages/python/index.ts b/extension/src/languages/python/index.ts
deleted file mode 100644
index 50282b45..00000000
--- a/extension/src/languages/python/index.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-import path = require("path");
-import { LanguageLibrary } from "../index.d";
-
-const tracebackStart = "Traceback (most recent call last):";
-const tracebackEnd = (buf: string): string | undefined => {
- let lines = buf
- .split("\n")
- .filter((line: string) => line.trim() !== "~~^~~")
- .filter((line: string) => line.trim() !== "");
- for (let i = 0; i < lines.length; i++) {
- if (
- lines[i].startsWith(" File") &&
- i + 2 < lines.length &&
- lines[i + 2][0] !== " "
- ) {
- return lines.slice(0, i + 3).join("\n");
- }
- }
- return undefined;
-};
-
-function parseFirstStacktrace(stdout: string): string | undefined {
- let startIdx = stdout.indexOf(tracebackStart);
- if (startIdx < 0) return undefined;
- stdout = stdout.substring(startIdx);
- return tracebackEnd(stdout);
-}
-
-function lineIsFunctionDef(line: string): boolean {
- return line.startsWith("def ");
-}
-
-function parseFunctionDefForName(line: string): string {
- return line.split("def ")[1].split("(")[0];
-}
-
-function lineIsComment(line: string): boolean {
- return line.trim().startsWith("#");
-}
-
-function writeImport(
- sourcePath: string,
- pathToImport: string,
- namesToImport: string[] | undefined = undefined
-): string {
- let segs = path.relative(sourcePath, pathToImport).split(path.sep);
- let importFrom = "";
- for (let seg of segs) {
- if (seg === "..") {
- importFrom = "." + importFrom;
- } else {
- if (!importFrom.endsWith(".")) {
- importFrom += ".";
- }
- importFrom += seg.split(".").slice(0, -1).join(".");
- }
- }
-
- return `from ${importFrom} import ${
- namesToImport ? namesToImport.join(", ") : "*"
- }`;
-}
-
-const pythonLangaugeLibrary: LanguageLibrary = {
- language: "python",
- fileExtensions: [".py"],
- parseFirstStacktrace,
- lineIsFunctionDef,
- parseFunctionDefForName,
- lineIsComment,
- writeImport,
-};
-
-export default pythonLangaugeLibrary;