From 02c30b49146f6a69aa2c003ab709d19997a38304 Mon Sep 17 00:00:00 2001 From: rjmacarthy Date: Wed, 20 Mar 2024 14:35:05 +0000 Subject: [PATCH 1/6] add ability to generate commit message --- src/extension/chat-service.ts | 10 ++++--- src/extension/providers/sidebar.ts | 15 +++++++++-- src/extension/templates.ts | 14 ++++++++++ src/extension/utils.ts | 27 +++++++++++++++++++ src/webview/chat.tsx | 42 ++++++++++-------------------- src/webview/selection.tsx | 35 ------------------------- 6 files changed, 74 insertions(+), 69 deletions(-) delete mode 100644 src/webview/selection.tsx diff --git a/src/extension/chat-service.ts b/src/extension/chat-service.ts index 062ef467..aff214c4 100644 --- a/src/extension/chat-service.ts +++ b/src/extension/chat-service.ts @@ -215,11 +215,12 @@ export class ChatService { private buildTemplatePrompt = async ( template: string, - language: CodeLanguageDetails + language: CodeLanguageDetails, + context?: string ) => { const editor = window.activeTextEditor const selection = editor?.selection - const selectionContext = editor?.document.getText(selection) || '' + const selectionContext = editor?.document.getText(selection) || context || '' const prompt = await this._templateProvider?.renderTemplate( template, { @@ -277,7 +278,7 @@ export class ChatService { return this.streamResponse({ requestBody, requestOptions }) } - public async streamTemplateCompletion(promptTemplate: string) { + public async streamTemplateCompletion(promptTemplate: string, context?: string) { const { language } = getLanguage() this._completion = '' this._promptTemplate = promptTemplate @@ -288,7 +289,8 @@ export class ChatService { }) const { prompt, selection } = await this.buildTemplatePrompt( promptTemplate, - language + language, + context, ) this._view?.webview.postMessage({ type: MESSAGE_NAME.twinngAddMessage, diff --git a/src/extension/providers/sidebar.ts b/src/extension/providers/sidebar.ts index 36046af2..333c5be4 100644 --- a/src/extension/providers/sidebar.ts +++ b/src/extension/providers/sidebar.ts @@ -1,6 +1,6 @@ import * as vscode from 'vscode' -import { getLanguage, getTextSelection, getTheme } from '../utils' +import { getGitChanges as getChangedUnidiff, getLanguage, getTextSelection, getTheme } from '../utils' import { MESSAGE_KEY, MESSAGE_NAME } from '../../common/constants' import { ChatService } from '../chat-service' import { @@ -91,13 +91,24 @@ export class SidebarProvider implements vscode.WebviewViewProvider { [MESSAGE_NAME.twinnyTextSelection]: this.getSelectedText, [MESSAGE_NAME.twinnyWorkspaceContext]: this.getTwinnyWorkspaceContext, [MESSAGE_NAME.twinnySetConfigValue]: this.setConfigurationValue, - [MESSAGE_NAME.twinnyGetConfigValue]: this.getConfigurationValue + [MESSAGE_NAME.twinnyGetConfigValue]: this.getConfigurationValue, + [MESSAGE_NAME.twinnyGetGitChanges]: this.getGitCommitMessage } eventHandlers[message.type as string]?.(message) } ) } + public getGitCommitMessage = async () => { + const unidiff = await getChangedUnidiff() + if (!unidiff?.length) return + this.setTwinnyWorkspaceContext({ + key: MESSAGE_KEY.lastConversation, + data: [] + }) + this.chatService?.streamTemplateCompletion('commit-message', unidiff) + } + public getConfigurationValue = (data: ClientMessage) => { if (!data.key) return const config = vscode.workspace.getConfiguration('twinny') diff --git a/src/extension/templates.ts b/src/extension/templates.ts index da9edff4..c4c095f5 100644 --- a/src/extension/templates.ts +++ b/src/extension/templates.ts @@ -64,6 +64,20 @@ Always reply with using markdown. For code refactoring, use markdown with code formatting. ` }, + { + name: 'commit-message', + template: `Only Generate short and concise commit messages. + Do not explain your response. + + Answer in a markdown codeblock under 50 characters. + + Here is the unidiff: \`\`\`{{code}}\`\`\` + + \`\`\` + + \`\`\` + ` + }, { name: 'chat', template: `{{#if (eq messages.length 1)}} diff --git a/src/extension/utils.ts b/src/extension/utils.ts index c646ee8f..d8728246 100644 --- a/src/extension/utils.ts +++ b/src/extension/utils.ts @@ -5,6 +5,7 @@ import { Position, Range, TextDocument, + extensions, window, } from 'vscode' @@ -360,6 +361,32 @@ export function safeParseJsonResponse( } } +export const getGitChanges = async () => { + let diffString = '' + const gitExtension = extensions.getExtension('vscode.git') + if (!gitExtension) { + console.log('Git extension is not available') + return '' + } + + await gitExtension.activate() + if (!gitExtension.exports.getAPI) { + console.log('Git API is not available') + return '' + } + + const api = gitExtension.exports.getAPI(1) + if (api && api.repositories.length > 0) { + const repo = api.repositories[0] + const diff: string[] = await repo.getDiff() + diffString = diff.join('') + } else { + return '' + } + + return diffString +} + export const logStreamOptions = (opts: StreamRequest) => { logger.log( ` diff --git a/src/webview/chat.tsx b/src/webview/chat.tsx index 73214032..c151bb09 100644 --- a/src/webview/chat.tsx +++ b/src/webview/chat.tsx @@ -8,7 +8,6 @@ import { VSCodeBadge } from '@vscode/webview-ui-toolkit/react' -import { Selection } from './selection' import { ASSISTANT, MESSAGE_KEY, @@ -17,7 +16,6 @@ import { } from '../common/constants' import { - useLanguage, useSelection, useTheme, useWorkSpaceContext @@ -25,9 +23,7 @@ import { import { DisabledAutoScrollIcon, EnabledAutoScrollIcon, - DisabledSelectionIcon, - EnabledSelectionIcon, - ScrollDownIcon + ScrollDownIcon, } from './icons' import { Suggestions } from './suggestions' @@ -45,17 +41,13 @@ import styles from './index.module.css' const global = globalThis as any export const Chat = () => { const [inputText, setInputText] = useState('') - const [isSelectionVisible, setIsSelectionVisible] = useState(false) const generatingRef = useRef(false) const stopRef = useRef(false) const theme = useTheme() - const language = useLanguage() const [loading, setLoading] = useState(false) const [messages, setMessages] = useState() const [completion, setCompletion] = useState() const [showModelSelect, setShowModelSelect] = useState(false) - - const markdownRef = useRef(null) const autoScrollContext = useWorkSpaceContext(MESSAGE_KEY.autoScroll) const [isAutoScrolledEnabled, setIsAutoScrolledEnabled] = useState< @@ -220,16 +212,18 @@ export const Chat = () => { }) } + const handleGetGitChanges = () => { + global.vscode.postMessage({ + type: MESSAGE_NAME.twinnyGetGitChanges, + } as ClientMessage) + } + const handleScrollBottom = () => { if (markdownRef.current) { markdownRef.current.scrollTop = markdownRef.current.scrollHeight } } - const handleToggleSelection = () => { - setIsSelectionVisible((prev) => !prev) - } - const handleToggleModelSelection = () => { setShowModelSelect((prev) => !prev) } @@ -282,11 +276,6 @@ export const Chat = () => { {!!selection.length && ( )} - {showModelSelect && }
@@ -302,23 +291,20 @@ export const Chat = () => { )} - + - {isSelectionVisible ? ( - - ) : ( - - )} + + {selection?.length}
void - language: LanguageType | undefined - isVisible: boolean -} - -export const Selection = ({ onSelect, language, isVisible }: SelectionProps) => { - const selection = useSelection(onSelect) - - const lang = getLanguageMatch(language, '') - - if (!selection) { - return null - } - - return ( - <> - {!!isVisible && ( - - )} - - ) -} From 90527beb77a73e2a44791ca31c1fa10497ed7c2a Mon Sep 17 00:00:00 2001 From: rjmacarthy Date: Mon, 1 Apr 2024 20:21:44 +0100 Subject: [PATCH 2/6] generate commit message from staged changes --- src/common/constants.ts | 1 + src/extension/providers/sidebar.ts | 18 +++++-- src/extension/templates.ts | 4 +- src/extension/utils.ts | 45 ++++++++-------- src/webview/chat.tsx | 6 +-- src/webview/icons.tsx | 84 ------------------------------ 6 files changed, 42 insertions(+), 116 deletions(-) diff --git a/src/common/constants.ts b/src/common/constants.ts index c8d99dcc..4d2299b5 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -51,6 +51,7 @@ export const MESSAGE_NAME = { twinnySetOllamaModel: 'twinny-set-ollama-model', twinnySetConfigValue: 'twinny-set-config-value', twinnyGetConfigValue: 'twinny-get-config-value', + twinnyGetGitChanges: 'twinny-get-git-changes', } export const MESSAGE_KEY = { diff --git a/src/extension/providers/sidebar.ts b/src/extension/providers/sidebar.ts index 333c5be4..ea3e300c 100644 --- a/src/extension/providers/sidebar.ts +++ b/src/extension/providers/sidebar.ts @@ -1,6 +1,11 @@ import * as vscode from 'vscode' -import { getGitChanges as getChangedUnidiff, getLanguage, getTextSelection, getTheme } from '../utils' +import { + getGitChanges, + getLanguage, + getTextSelection, + getTheme +} from '../utils' import { MESSAGE_KEY, MESSAGE_NAME } from '../../common/constants' import { ChatService } from '../chat-service' import { @@ -100,13 +105,18 @@ export class SidebarProvider implements vscode.WebviewViewProvider { } public getGitCommitMessage = async () => { - const unidiff = await getChangedUnidiff() - if (!unidiff?.length) return + const diff = await getGitChanges() + if (!diff.length) { + vscode.window.showInformationMessage( + 'No changes found in the current workspace.' + ) + return + } this.setTwinnyWorkspaceContext({ key: MESSAGE_KEY.lastConversation, data: [] }) - this.chatService?.streamTemplateCompletion('commit-message', unidiff) + this.chatService?.streamTemplateCompletion('commit-message', diff) } public getConfigurationValue = (data: ClientMessage) => { diff --git a/src/extension/templates.ts b/src/extension/templates.ts index c4c095f5..65cfa613 100644 --- a/src/extension/templates.ts +++ b/src/extension/templates.ts @@ -66,10 +66,10 @@ For code refactoring, use markdown with code formatting. }, { name: 'commit-message', - template: `Only Generate short and concise commit messages. + template: `You are an agent who generates git commit messages. Do not explain your response. - Answer in a markdown codeblock under 50 characters. + Answer in a markdown codeblock under 100 characters. Here is the unidiff: \`\`\`{{code}}\`\`\` diff --git a/src/extension/utils.ts b/src/extension/utils.ts index d8728246..fe6ed8ab 100644 --- a/src/extension/utils.ts +++ b/src/extension/utils.ts @@ -5,9 +5,13 @@ import { Position, Range, TextDocument, - extensions, window, + workspace } from 'vscode' +import * as util from 'util' +import { exec } from 'child_process' + +const execAsync = util.promisify(exec) import { Theme, @@ -243,7 +247,7 @@ export const getNextLineIsClosingBracket = () => { if (!editor) return false const position = editor.selection.active const nextLineText = editor.document - .lineAt(Math.min(position.line + 1, editor.document.lineCount -1)) + .lineAt(Math.min(position.line + 1, editor.document.lineCount - 1)) .text.trim() return getIsOnlyClosingBrackets(nextLineText) } @@ -361,30 +365,27 @@ export function safeParseJsonResponse( } } -export const getGitChanges = async () => { - let diffString = '' - const gitExtension = extensions.getExtension('vscode.git') - if (!gitExtension) { - console.log('Git extension is not available') - return '' - } - - await gitExtension.activate() - if (!gitExtension.exports.getAPI) { - console.log('Git API is not available') - return '' +export const getCurrentWorkspacePath = (): string | undefined => { + if (workspace.workspaceFolders && workspace.workspaceFolders.length > 0) { + const workspaceFolder = workspace.workspaceFolders[0] + return workspaceFolder.uri.fsPath + } else { + window.showInformationMessage('No workspace is open.') + return undefined } +} - const api = gitExtension.exports.getAPI(1) - if (api && api.repositories.length > 0) { - const repo = api.repositories[0] - const diff: string[] = await repo.getDiff() - diffString = diff.join('') - } else { +export const getGitChanges = async (): Promise => { + try { + const path = getCurrentWorkspacePath() + const { stdout } = await execAsync('git diff --cached', { + cwd: path + }) + return stdout + } catch (error) { + console.error('Error executing git command:', error) return '' } - - return diffString } export const logStreamOptions = (opts: StreamRequest) => { diff --git a/src/webview/chat.tsx b/src/webview/chat.tsx index c151bb09..68318dae 100644 --- a/src/webview/chat.tsx +++ b/src/webview/chat.tsx @@ -23,7 +23,6 @@ import { import { DisabledAutoScrollIcon, EnabledAutoScrollIcon, - ScrollDownIcon, } from './icons' import { Suggestions } from './suggestions' @@ -292,7 +291,7 @@ export const Chat = () => { @@ -302,9 +301,8 @@ export const Chat = () => { appearance="icon" onClick={handleScrollBottom} > - + - {selection?.length}
( /> ) - -export const EnabledSelectionIcon = () => ( - - - - -) - -export const DisabledSelectionIcon = () => ( - - - - - -) - -export const ScrollDownIcon = () => ( - - - - - -) From f6a8b04470ecbd5936a51e2f85a4fca23d9dc637 Mon Sep 17 00:00:00 2001 From: rjmacarthy Date: Mon, 1 Apr 2024 21:17:48 +0100 Subject: [PATCH 3/6] add commit directly to the terminal no quick select --- README.md | 15 ++++++----- package.json | 8 ++++++ src/common/constants.ts | 1 + src/extension/chat-service.ts | 42 +++++++++++++++++++----------- src/extension/providers/sidebar.ts | 8 +++++- src/extension/templates.ts | 4 +-- src/extension/utils.ts | 35 +++++++++++++++++++++++++ src/index.ts | 18 ++++++++++++- 8 files changed, 104 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 0c391036..635479ea 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ Through the side bar, have a conversation with your model and get explanations a - Conforms to the OpenAI API standard - Single or multiline fill-in-middle completions - Customisable prompt templates to add context to completions +- Generate git commit messages from staged changes (CTRL+SHIFT+T CTRL+SHIFT+G) - Easy installation via vscode extensions marketplace or by downloading and running a binary directly - Customisable settings to change API provider, model name, port number and path - Ollama, llamacpp, oobabooga and LM Studio API compatible @@ -123,13 +124,13 @@ If using Llama the model must support the Llama special tokens. ## Keyboard shortcuts -| Shortcut | Description | -| ---------------------------- | ---------------------------------------- | -| `ALT+\` | Trigger inline code completion | -| `CTRL+SHIFT+/` | Stop the inline code generation | -| `Tab` | Accept the inline code generated | -| `CTRL+SHIFT+t` | Open twinny sidebar | - +| Shortcut | Description | +| ---------------------------- | ---------------------------------------------| +| `ALT+\` | Trigger inline code completion | +| `CTRL+SHIFT+/` | Stop the inline code generation | +| `Tab` | Accept the inline code generated | +| `CTRL+SHIFT+t` | Open twinny sidebar | +| `CTRL+SHIFT+t CTRL+SHIT+g` | Generate commit messages from staged changes | ## Workspace context diff --git a/package.json b/package.json index ef688bb1..fe531c47 100644 --- a/package.json +++ b/package.json @@ -138,6 +138,10 @@ "command": "twinny.stopGeneration", "title": "Stop generation" }, + { + "command": "twinny.getGitCommitMessage", + "title": "Generate git commit message" + }, { "command": "twinny.disable", "title": "Disable twinny", @@ -194,6 +198,10 @@ "key": "CTRL+SHIFT+/", "command": "twinny.stopGeneration", "when": "twinnyGeneratingText" + }, + { + "key": "ctrl+shift+t ctrl+shift+g", + "command": "twinny.getGitCommitMessage" } ], "viewsContainers": { diff --git a/src/common/constants.ts b/src/common/constants.ts index 4d2299b5..cd693e85 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -16,6 +16,7 @@ export const ALL_BRACKETS = [...OPENING_BRACKETS, ...CLOSING_BRACKETS] as const export const BRACKET_REGEX = /^[()[\]{}]+$/ export const NORMALIZE_REGEX = /\s*\r?\n|\r/g export const LINE_BREAK_REGEX = /\r?\n|\r|\n/g +export const QUOTES_REGEX = /["'`]/g export const MAX_CONTEXT_LINE_COUNT = 200 export const SKIP_DECLARATION_SYMBOLS = ['='] export const IMPORT_SEPARATOR = [',', '{'] diff --git a/src/extension/chat-service.ts b/src/extension/chat-service.ts index aff214c4..845678a1 100644 --- a/src/extension/chat-service.ts +++ b/src/extension/chat-service.ts @@ -80,10 +80,14 @@ export class ChatService { return { requestOptions, requestBody } } - private onStreamData = (streamResponse: StreamResponse | undefined) => { + private onStreamData = ( + streamResponse: StreamResponse | undefined, + onEnd?: (completion: string) => void + ) => { try { const data = getChatDataFromProvider(this._apiProvider, streamResponse) this._completion = this._completion + data + if (onEnd) return this._view?.webview.postMessage({ type: MESSAGE_NAME.twinnyOnCompletion, value: { @@ -98,13 +102,14 @@ export class ChatService { } } - private onStreamEnd = () => { + private onStreamEnd = (onEnd?: (completion: string) => void) => { this._statusBar.text = '🤖' commands.executeCommand( 'setContext', CONTEXT_NAME.twinnyGeneratingText, false ) + if (onEnd) return onEnd(this._completion) this._view?.webview.postMessage({ type: MESSAGE_NAME.twinnyOnEnd, value: { @@ -126,7 +131,6 @@ export class ChatService { } private onStreamStart = (controller: AbortController) => { - this._statusBar.text = '$(loading~spin)' this._controller = controller commands.executeCommand( 'setContext', @@ -220,7 +224,8 @@ export class ChatService { ) => { const editor = window.activeTextEditor const selection = editor?.selection - const selectionContext = editor?.document.getText(selection) || context || '' + const selectionContext = + editor?.document.getText(selection) || context || '' const prompt = await this._templateProvider?.renderTemplate( template, { @@ -233,16 +238,19 @@ export class ChatService { private streamResponse({ requestBody, - requestOptions + requestOptions, + onEnd }: { requestBody: StreamBodyBase requestOptions: StreamRequestOptions + onEnd?: (completion: string) => void }) { return streamResponse({ body: requestBody, options: requestOptions, - onData: this.onStreamData, - onEnd: this.onStreamEnd, + onData: (streamResponse: StreamResponse | undefined) => + this.onStreamData(streamResponse, onEnd), + onEnd: () => this.onStreamEnd(onEnd), onStart: this.onStreamStart, onError: this.onStreamError }) @@ -278,27 +286,31 @@ export class ChatService { return this.streamResponse({ requestBody, requestOptions }) } - public async streamTemplateCompletion(promptTemplate: string, context?: string) { + public async streamTemplateCompletion( + promptTemplate: string, + context?: string, + onEnd?: (completion: string) => void + ) { const { language } = getLanguage() this._completion = '' this._promptTemplate = promptTemplate this.sendEditorLanguage() this.focusChatTab() - this._view?.webview.postMessage({ - type: MESSAGE_NAME.twinnyOnLoading - }) const { prompt, selection } = await this.buildTemplatePrompt( promptTemplate, language, - context, + context ) + this._statusBar.text = '$(loading~spin)' + this._view?.webview.postMessage({ + type: MESSAGE_NAME.twinnyOnLoading + }) this._view?.webview.postMessage({ type: MESSAGE_NAME.twinngAddMessage, value: { completion: kebabToSentence(promptTemplate) + '\n\n' + '```\n' + selection, - data: getLanguage(), - type: this._promptTemplate + data: getLanguage() } } as ServerMessage) const messageRoleContent = await this.buildMesageRoleContent( @@ -314,7 +326,7 @@ export class ChatService { prompt, messageRoleContent ) - return this.streamResponse({ requestBody, requestOptions }) + return this.streamResponse({ requestBody, requestOptions, onEnd }) } private updateConfig() { diff --git a/src/extension/providers/sidebar.ts b/src/extension/providers/sidebar.ts index ea3e300c..8d9780cc 100644 --- a/src/extension/providers/sidebar.ts +++ b/src/extension/providers/sidebar.ts @@ -116,7 +116,13 @@ export class SidebarProvider implements vscode.WebviewViewProvider { key: MESSAGE_KEY.lastConversation, data: [] }) - this.chatService?.streamTemplateCompletion('commit-message', diff) + this.chatService?.streamTemplateCompletion( + 'commit-message', + diff, + (completion: string) => { + vscode.commands.executeCommand('twinny.sendTerminalText', completion) + } + ) } public getConfigurationValue = (data: ClientMessage) => { diff --git a/src/extension/templates.ts b/src/extension/templates.ts index 65cfa613..1ab30f3f 100644 --- a/src/extension/templates.ts +++ b/src/extension/templates.ts @@ -67,15 +67,13 @@ For code refactoring, use markdown with code formatting. { name: 'commit-message', template: `You are an agent who generates git commit messages. - Do not explain your response. + Do not explain your response, reply in text only for terminal usage. Answer in a markdown codeblock under 100 characters. Here is the unidiff: \`\`\`{{code}}\`\`\` - \`\`\` - \`\`\` ` }, { diff --git a/src/extension/utils.ts b/src/extension/utils.ts index fe6ed8ab..9dfc4113 100644 --- a/src/extension/utils.ts +++ b/src/extension/utils.ts @@ -4,6 +4,7 @@ import { InlineCompletionTriggerKind, Position, Range, + Terminal, TextDocument, window, workspace @@ -26,8 +27,10 @@ import { supportedLanguages } from '../common/languages' import { ALL_BRACKETS, CLOSING_BRACKETS, + LINE_BREAK_REGEX, OPENING_BRACKETS, QUOTES, + QUOTES_REGEX, SKIP_DECLARATION_SYMBOLS } from '../common/constants' import { Logger } from '../common/logger' @@ -388,6 +391,38 @@ export const getGitChanges = async (): Promise => { } } +export const getTerminal = async (): Promise => { + const activeTerminal = window.activeTerminal + if (activeTerminal) { + return activeTerminal + } + const terminals = window.terminals + const items = terminals.map((t) => ({ + label: `name: ${t.name}`, + terminal: t + })) + + const item = await window.showQuickPick(items) + return item ? item.terminal : undefined +} + +export const getTerminalExists = (): boolean => { + if (window.terminals.length === 0) { + window.showErrorMessage('No active terminals') + return false + } + return true +} + +export const getSanitizedCommitMessage = (commitMessage: string) => { + const sanitizedMessage = commitMessage + .replace(QUOTES_REGEX, '') + .replace(LINE_BREAK_REGEX, '') + .trim() + + return `git commit -m "${sanitizedMessage}"` +} + export const logStreamOptions = (opts: StreamRequest) => { logger.log( ` diff --git a/src/index.ts b/src/index.ts index db708201..5fc0c804 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,7 +12,11 @@ import * as vscode from 'vscode' import { CompletionProvider } from './extension/providers/completion' import { SidebarProvider } from './extension/providers/sidebar' -import { delayExecution } from './extension/utils' +import { + delayExecution, + getTerminal, + getSanitizedCommitMessage +} from './extension/utils' import { setContext } from './extension/context' import { CONTEXT_NAME, @@ -135,6 +139,17 @@ export async function activate(context: ExtensionContext) { EXTENSION_NAME ) }), + commands.registerCommand( + 'twinny.sendTerminalText', + async (commitMessage: string) => { + const terminal = await getTerminal() + terminal?.sendText(getSanitizedCommitMessage(commitMessage)) + } + ), + commands.registerCommand('twinny.getGitCommitMessage', () => { + commands.executeCommand('twinny.sidebar.focus') + delayExecution(() => sidebarProvider.getGitCommitMessage()) + }), commands.registerCommand('twinny.newChat', () => { sidebarProvider.setTwinnyWorkspaceContext({ key: MESSAGE_KEY.lastConversation, @@ -144,6 +159,7 @@ export async function activate(context: ExtensionContext) { key: MESSAGE_KEY.lastConversation }) }), + window.registerWebviewViewProvider('twinny.sidebar', sidebarProvider), statusBar ) From b560ae28c1687e1046ced8193ffbfd4e01f090d8 Mon Sep 17 00:00:00 2001 From: rjmacarthy Date: Mon, 1 Apr 2024 21:44:30 +0100 Subject: [PATCH 4/6] clean up --- src/extension/chat-service.ts | 8 +++++++- src/extension/utils.ts | 20 +++++++------------- src/index.ts | 4 ++-- src/webview/chat.tsx | 2 ++ 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/extension/chat-service.ts b/src/extension/chat-service.ts index 845678a1..8972b7fe 100644 --- a/src/extension/chat-service.ts +++ b/src/extension/chat-service.ts @@ -109,7 +109,13 @@ export class ChatService { CONTEXT_NAME.twinnyGeneratingText, false ) - if (onEnd) return onEnd(this._completion) + if (onEnd) { + onEnd(this._completion) + this._view?.webview.postMessage({ + type: MESSAGE_NAME.twinnyOnEnd, + } as ServerMessage) + return + } this._view?.webview.postMessage({ type: MESSAGE_NAME.twinnyOnEnd, value: { diff --git a/src/extension/utils.ts b/src/extension/utils.ts index 9dfc4113..14c9ac15 100644 --- a/src/extension/utils.ts +++ b/src/extension/utils.ts @@ -31,7 +31,8 @@ import { OPENING_BRACKETS, QUOTES, QUOTES_REGEX, - SKIP_DECLARATION_SYMBOLS + SKIP_DECLARATION_SYMBOLS, + TWINNY } from '../common/constants' import { Logger } from '../common/logger' @@ -392,18 +393,11 @@ export const getGitChanges = async (): Promise => { } export const getTerminal = async (): Promise => { - const activeTerminal = window.activeTerminal - if (activeTerminal) { - return activeTerminal - } - const terminals = window.terminals - const items = terminals.map((t) => ({ - label: `name: ${t.name}`, - terminal: t - })) - - const item = await window.showQuickPick(items) - return item ? item.terminal : undefined + const twinnyTerminal = window.terminals.find((t) => t.name === TWINNY) + if (twinnyTerminal) return twinnyTerminal + const terminal = window.createTerminal({ name: TWINNY }) + terminal.show() + return terminal } export const getTerminalExists = (): boolean => { diff --git a/src/index.ts b/src/index.ts index 5fc0c804..3cd83be6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -143,12 +143,12 @@ export async function activate(context: ExtensionContext) { 'twinny.sendTerminalText', async (commitMessage: string) => { const terminal = await getTerminal() - terminal?.sendText(getSanitizedCommitMessage(commitMessage)) + terminal?.sendText(getSanitizedCommitMessage(commitMessage), false) } ), commands.registerCommand('twinny.getGitCommitMessage', () => { commands.executeCommand('twinny.sidebar.focus') - delayExecution(() => sidebarProvider.getGitCommitMessage()) + delayExecution(() => sidebarProvider.getGitCommitMessage(), 400) }), commands.registerCommand('twinny.newChat', () => { sidebarProvider.setTwinnyWorkspaceContext({ diff --git a/src/webview/chat.tsx b/src/webview/chat.tsx index 68318dae..5e1f0a21 100644 --- a/src/webview/chat.tsx +++ b/src/webview/chat.tsx @@ -70,6 +70,8 @@ export const Chat = () => { const selection = useSelection(scrollBottom) const handleCompletionEnd = (message: ServerMessage) => { + if (!message.value) return + setMessages((prev) => { const update = [ ...(prev || []), From ed28b2b47bf405133734f229501d30a0ea90fe14 Mon Sep 17 00:00:00 2001 From: rjmacarthy Date: Mon, 1 Apr 2024 21:49:26 +0100 Subject: [PATCH 5/6] lint --- src/extension/templates.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/extension/templates.ts b/src/extension/templates.ts index 1ab30f3f..cf6075c6 100644 --- a/src/extension/templates.ts +++ b/src/extension/templates.ts @@ -66,14 +66,16 @@ For code refactoring, use markdown with code formatting. }, { name: 'commit-message', - template: `You are an agent who generates git commit messages. - Do not explain your response, reply in text only for terminal usage. + template: `You are an agent who generates concise git commit messages. +Only reply with one line of text. - Answer in a markdown codeblock under 100 characters. +- Answer under 100 characters. - Here is the unidiff: \`\`\`{{code}}\`\`\` +E.g "Added a new feature" - +Here is the unidiff: \`\`\`{{code}}\`\`\` + + ` }, { From 16ed5816dcef86743d3122a8b8b591d37fecab26 Mon Sep 17 00:00:00 2001 From: rjmacarthy Date: Mon, 1 Apr 2024 21:50:57 +0100 Subject: [PATCH 6/6] 3.10.8 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 755c1f5a..eabebc14 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "twinny", - "version": "3.10.7", + "version": "3.10.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "twinny", - "version": "3.10.7", + "version": "3.10.8", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index fe531c47..65c2fe02 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "twinny", "displayName": "twinny - AI Code Completion and Chat", "description": "Locally hosted AI code completion plugin for vscode", - "version": "3.10.7", + "version": "3.10.8", "icon": "assets/icon.png", "keywords": [ "code-inference",