diff options
Diffstat (limited to 'extension')
-rw-r--r-- | extension/README.md | 2 | ||||
-rw-r--r-- | extension/package.json | 55 | ||||
-rw-r--r-- | extension/react-app/src/components/ComboBox.tsx | 74 | ||||
-rw-r--r-- | extension/react-app/src/components/HeaderButtonWithText.tsx | 4 | ||||
-rw-r--r-- | extension/react-app/src/components/PillButton.tsx | 49 | ||||
-rw-r--r-- | extension/src/activation/activate.ts | 11 | ||||
-rw-r--r-- | extension/src/bridge.ts | 2 | ||||
-rw-r--r-- | extension/src/commands.ts | 6 | ||||
-rw-r--r-- | extension/src/debugPanel.ts | 4 |
9 files changed, 116 insertions, 91 deletions
diff --git a/extension/README.md b/extension/README.md index 59f5d155..69bcb1f9 100644 --- a/extension/README.md +++ b/extension/README.md @@ -36,6 +36,8 @@ By default, Continue uses GPT-4 and GPT-3.5-turbo via the OpenAI API. You can adjust the config to use different LLMs, including local, private models. Read more [here](https://continue.dev/docs/customization#change-the-default-llm). +To see the keyboard shortcuts offered by Continue, see the "Feature Contributions" tab above. + # Troubleshooting The Continue VS Code extension is currently in beta. It will attempt to start the Continue Python server locally for you, but sometimes this will fail, causing the "Starting Continue server..." not to disappear, or other hangups. While we are working on fixes to all of these problems, read here for common solutions: diff --git a/extension/package.json b/extension/package.json index 5e0a45b5..4924dd35 100644 --- a/extension/package.json +++ b/extension/package.json @@ -47,37 +47,17 @@ "continue.serverUrl": { "type": "string", "default": "http://localhost:65432", - "description": "The URL of the Continue server to use." + "description": "The URL of the Continue server. Only change this if you are running the server manually. If you want to use an LLM hosted at a custom URL, please see https://continue.dev/docs/customization#change-the-default-llm." }, "continue.OPENAI_API_KEY": { "type": "string", "default": null, - "description": "The OpenAI API key to use for code generation." + "description": "The OpenAI API key to use for code generation. Leave empty to get limited free usage of Continue." } } }, "commands": [ { - "command": "continue.suggestionDown", - "category": "Continue", - "title": "Suggestion Down" - }, - { - "command": "continue.suggestionUp", - "category": "Continue", - "title": "Suggestion Up" - }, - { - "command": "continue.acceptSuggestion", - "category": "Continue", - "title": "Accept Suggestion" - }, - { - "command": "continue.rejectSuggestion", - "category": "Continue", - "title": "Reject Suggestion" - }, - { "command": "continue.acceptDiff", "category": "Continue", "title": "Accept Diff" @@ -88,16 +68,6 @@ "title": "Reject Diff" }, { - "command": "continue.acceptAllSuggestions", - "category": "Continue", - "title": "Accept All Suggestions" - }, - { - "command": "continue.rejectAllSuggestions", - "category": "Continue", - "title": "Reject All Suggestions" - }, - { "command": "continue.quickTextEntry", "category": "Continue", "title": "Quick Text Entry" @@ -105,17 +75,17 @@ { "command": "continue.quickFix", "category": "Continue", - "title": "Quick Fix" + "title": "Quick Fix with Continue" }, { "command": "continue.viewLogs", "category": "Continue", - "title": "View Logs" + "title": "View Continue Server Logs" }, { "command": "continue.toggleAuxiliaryBar", "category": "Continue", - "title": "Toggle Auxiliary Bar" + "title": "Toggle Right Sidebar" }, { "command": "continue.focusContinueInputWithEdit", @@ -135,21 +105,6 @@ "key": "ctrl+shift+m" }, { - "command": "continue.suggestionDown", - "mac": "shift+ctrl+down", - "key": "shift+ctrl+down" - }, - { - "command": "continue.suggestionUp", - "mac": "shift+ctrl+up", - "key": "shift+ctrl+up" - }, - { - "command": "continue.acceptSuggestion", - "mac": "shift+ctrl+enter", - "key": "shift+ctrl+enter" - }, - { "command": "continue.acceptDiff", "mac": "shift+cmd+enter", "key": "shift+ctrl+enter" diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx index df3f970c..14e04a84 100644 --- a/extension/react-app/src/components/ComboBox.tsx +++ b/extension/react-app/src/components/ComboBox.tsx @@ -223,11 +223,56 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { const divRef = React.useRef<HTMLDivElement>(null); const ulRef = React.useRef<HTMLUListElement>(null); const showAbove = () => { - return (divRef.current?.getBoundingClientRect().top || 0) > UlMaxHeight; + return ( + (divRef.current?.getBoundingClientRect().top || Number.MAX_SAFE_INTEGER) > + UlMaxHeight + ); }; useImperativeHandle(ref, () => downshiftProps, [downshiftProps]); + const contextItemsDivRef = React.useRef<HTMLDivElement>(null); + const handleTabPressed = () => { + // Set the focus to the next item in the context items div + if (!contextItemsDivRef.current) { + return; + } + const focusableItems = + contextItemsDivRef.current.querySelectorAll(".pill-button"); + const focusableItemsArray = Array.from(focusableItems); + const focusedItemIndex = focusableItemsArray.findIndex( + (item) => item === document.activeElement + ); + console.log(focusedItemIndex, focusableItems); + if (focusedItemIndex === focusableItemsArray.length - 1) { + inputRef.current?.focus(); + } else if (focusedItemIndex !== -1) { + const nextItem = + focusableItemsArray[ + (focusedItemIndex + 1) % focusableItemsArray.length + ]; + (nextItem as any)?.focus(); + } else { + const firstItem = focusableItemsArray[0]; + (firstItem as any)?.focus(); + } + }; + + useEffect(() => { + if (typeof window !== "undefined") { + const listener = (e: any) => { + if (e.key === "Tab") { + e.preventDefault(); + handleTabPressed(); + } + }; + window.addEventListener("keydown", listener); + return () => { + window.removeEventListener("keydown", listener); + }; + } + }, []); + const [metaKeyPressed, setMetaKeyPressed] = useState(false); const [focused, setFocused] = useState(false); useEffect(() => { @@ -271,7 +316,10 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { return ( <> - <div className="px-2 flex gap-2 items-center flex-wrap mt-2"> + <div + className="px-2 flex gap-2 items-center flex-wrap mt-2" + ref={contextItemsDivRef} + > {props.selectedContextItems.map((item, idx) => { return ( <PillButton @@ -285,6 +333,10 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { } addingHighlightedCode={props.addingHighlightedCode} index={idx} + onDelete={() => { + client?.deleteContextWithIds([item.description.id]); + inputRef.current?.focus(); + }} /> ); })} @@ -303,6 +355,13 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { onClick={() => { props.onToggleAddContext(); }} + className="pill-button focus:outline-none focus:border-red-600 focus:border focus:border-solid" + onKeyDown={(e: KeyboardEvent) => { + e.preventDefault(); + if (e.key === "Enter") { + props.onToggleAddContext(); + } + }} > <DocumentPlusIcon width="1.4em" height="1.4em" /> </HeaderButtonWithText> @@ -329,10 +388,6 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { setFocused(true); dispatch(setBottomMessage(undefined)); }, - onBlur: (e) => { - setFocused(false); - postVscMessage("blurContinueInput", {}); - }, onKeyDown: (event) => { dispatch(setBottomMessage(undefined)); if (event.key === "Enter" && event.shiftKey) { @@ -356,6 +411,8 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { } else if (event.key === "Tab" && items.length > 0) { downshiftProps.setInputValue(items[0].name); event.preventDefault(); + } else if (event.key === "Tab") { + (event.nativeEvent as any).preventDownshiftDefault = true; } else if ( (event.key === "ArrowUp" || event.key === "ArrowDown") && event.currentTarget.value.split("\n").length > 1 @@ -383,9 +440,12 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { setCurrentlyInContextQuery(false); } else if (event.key === "Escape") { setCurrentlyInContextQuery(false); - if (downshiftProps.isOpen) { + if (downshiftProps.isOpen && items.length > 0) { downshiftProps.closeMenu(); } else { + (event.nativeEvent as any).preventDownshiftDefault = true; + // Remove focus from the input + inputRef.current?.blur(); // Move cursor back over to the editor postVscMessage("focusEditor", {}); } diff --git a/extension/react-app/src/components/HeaderButtonWithText.tsx b/extension/react-app/src/components/HeaderButtonWithText.tsx index de8e3c98..bcd36972 100644 --- a/extension/react-app/src/components/HeaderButtonWithText.tsx +++ b/extension/react-app/src/components/HeaderButtonWithText.tsx @@ -10,6 +10,8 @@ interface HeaderButtonWithTextProps { disabled?: boolean; inverted?: boolean; active?: boolean; + className?: string; + onKeyDown?: (e: any) => void; } const HeaderButtonWithText = (props: HeaderButtonWithTextProps) => { @@ -29,6 +31,8 @@ const HeaderButtonWithText = (props: HeaderButtonWithTextProps) => { setHover(false); }} onClick={props.onClick} + onKeyDown={props.onKeyDown} + className={props.className} > {props.children} </HeaderButton> diff --git a/extension/react-app/src/components/PillButton.tsx b/extension/react-app/src/components/PillButton.tsx index edef808e..0dcd43eb 100644 --- a/extension/react-app/src/components/PillButton.tsx +++ b/extension/react-app/src/components/PillButton.tsx @@ -84,8 +84,34 @@ interface PillButtonProps { index: number; addingHighlightedCode?: boolean; areMultipleItems?: boolean; + onDelete?: () => void; } +interface StyledButtonProps { + warning: string; + editing?: boolean; + areMultipleItems?: boolean; +} + +const StyledButton = styled(Button)<StyledButtonProps>` + position: relative; + border-color: ${(props) => + props.warning + ? "red" + : props.editing && props.areMultipleItems + ? vscForeground + : "transparent"}; + border-width: 1px; + border-style: solid; + + &:focus { + outline: none; + border-color: red; + border-width: 1px; + border-style: solid; + } +`; + const PillButton = (props: PillButtonProps) => { const [isHovered, setIsHovered] = useState(false); const client = useContext(GUIClientContext); @@ -132,17 +158,10 @@ const PillButton = (props: PillButtonProps) => { return ( <> <div style={{ position: "relative" }}> - <Button - style={{ - position: "relative", - borderColor: props.warning - ? "red" - : props.item.editing && props.areMultipleItems - ? vscForeground - : "transparent", - borderWidth: "1px", - borderStyle: "solid", - }} + <StyledButton + areMultipleItems={props.areMultipleItems} + warning={props.warning || ""} + editing={props.item.editing} onMouseEnter={() => { setIsHovered(true); if (props.onHover) { @@ -155,6 +174,12 @@ const PillButton = (props: PillButtonProps) => { props.onHover(false); } }} + className="pill-button" + onKeyDown={(e) => { + if (e.key === "Backspace") { + props.onDelete?.(); + } + }} > {isHovered && ( <GridDiv @@ -196,7 +221,7 @@ const PillButton = (props: PillButtonProps) => { </GridDiv> )} {props.item.description.name} - </Button> + </StyledButton> <StyledTooltip id={`edit-${props.index}`}> {props.item.editing ? "Editing this section (with entire file as context)" diff --git a/extension/src/activation/activate.ts b/extension/src/activation/activate.ts index 7a2fdfc3..d07719a8 100644 --- a/extension/src/activation/activate.ts +++ b/extension/src/activation/activate.ts @@ -61,17 +61,6 @@ export async function activateExtension(context: vscode.ExtensionContext) { startContinuePythonServer().then(() => { resolve(null); }); - - vscode.window - .showInformationMessage( - "Click here to view the server logs, or use the 'continue.viewLogs' VS Code command.", - "View Logs" - ) - .then((selection) => { - if (selection === "View Logs") { - vscode.commands.executeCommand("continue.viewLogs"); - } - }); }); console.log("Continue server started"); diff --git a/extension/src/bridge.ts b/extension/src/bridge.ts index 9c43ebc2..0d665826 100644 --- a/extension/src/bridge.ts +++ b/extension/src/bridge.ts @@ -7,7 +7,7 @@ export function getContinueServerUrl() { extensionContext && extensionContext.extensionMode === vscode.ExtensionMode.Development ) { - // return "http://localhost:8001"; + return "http://localhost:8001"; } return ( vscode.workspace.getConfiguration("continue").get<string>("serverUrl") || diff --git a/extension/src/commands.ts b/extension/src/commands.ts index 351c055d..cf6892f2 100644 --- a/extension/src/commands.ts +++ b/extension/src/commands.ts @@ -23,14 +23,8 @@ export const setFocusedOnContinueInput = (value: boolean) => { // Copy everything over from extension.ts const commandsMap: { [command: string]: (...args: any) => any } = { - "continue.suggestionDown": suggestionDownCommand, - "continue.suggestionUp": suggestionUpCommand, - "continue.acceptSuggestion": acceptSuggestionCommand, - "continue.rejectSuggestion": rejectSuggestionCommand, "continue.acceptDiff": acceptDiffCommand, "continue.rejectDiff": rejectDiffCommand, - "continue.acceptAllSuggestions": acceptAllSuggestionsCommand, - "continue.rejectAllSuggestions": rejectAllSuggestionsCommand, "continue.quickFix": async (message: string, code: string, edit: boolean) => { ideProtocolClient.sendMainUserInput( `${ diff --git a/extension/src/debugPanel.ts b/extension/src/debugPanel.ts index d1fe565f..b687c3e4 100644 --- a/extension/src/debugPanel.ts +++ b/extension/src/debugPanel.ts @@ -243,10 +243,6 @@ export function setupDebugPanel( vscode.commands.executeCommand("continue.viewLogs"); break; } - case "blurContinueInput": { - setFocusedOnContinueInput(false); - break; - } case "focusEditor": { setFocusedOnContinueInput(false); vscode.commands.executeCommand( |