diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d19157..13a31aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to the "vscode-deepl" extension will be documented in this f The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +### 1.1.1 + +- Added command "DeepL: Duplicate and translate" which duplicates and translates the selected text. + ### 1.1.0 - Removed the command "Translate from ... to ... below original text" diff --git a/README.md b/README.md index dcf9d4d..4b65afa 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ The following commands are available to translate texts: |DeepL: Translate to ...|alt+shift+t|Asks for the target language and translates the selected text into the target language |DeepL: Translate from ... to ...|alt+ctrl+shift+t|Asks for source and target language and translates the selected text from the source language into the target language |DeepL: Translate and paste from clipboard|ctrl+shift+v|Translates the clipboard content and paste it +|DeepL: Translate and paste from clipboard|ctrl+shift+d|Duplicates and translates the selected text The commands are accessible via the command pallette. @@ -67,6 +68,10 @@ Dont use this extension if you dont agree with their privacy policy! ## Release Notes +### 1.1.1 + +- Added command "DeepL: Duplicate and translate" which duplicates and translates the selected text. + ### 1.1.0 - Removed the command "Translate from ... to ... below original text" diff --git a/package.json b/package.json index f2d755e..cdadca7 100644 --- a/package.json +++ b/package.json @@ -211,6 +211,11 @@ "command": "deepl.translateAndPasteFromClipboard", "title": "DeepL: Translate and paste from clipboard", "shortTitle": "Translate and paste from clipboard" + }, + { + "command": "deepl.duplicateAndTranslate", + "title": "DeepL: Duplicate and translate", + "shortTitle": "Duplicate and translate" } ], "menus": { @@ -254,6 +259,12 @@ "when": "!isInDiffEditor", "key": "ctrl+shift+v", "mac": "cmd+shift+v" + }, + { + "command": "deepl.duplicateAndTranslate", + "when": "!isInDiffEditor", + "key": "ctrl+shift+d", + "mac": "cmd+shift+d" } ] }, diff --git a/src/commands.ts b/src/commands.ts index 4751075..ff60358 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -13,7 +13,7 @@ type GetTargetAndSourceLanguageRequest = { forceSourceLanguagePrompt: boolean; }; -async function getTargetAndSourceLanguage (request: GetTargetAndSourceLanguageRequest): Promise<{ targetLanguage: TargetLanguageCode, sourceLanguage: SourceLanguageCode | undefined }> { +async function getTargetAndSourceLanguage (request: GetTargetAndSourceLanguageRequest = { forceSourceLanguagePrompt: false, forceTargetLanguagePrompt: false }): Promise<{ targetLanguage: TargetLanguageCode, sourceLanguage: SourceLanguageCode | undefined }> { let targetLanguage = state.targetLanguage ?? getDefaultTargetLanguage(); if (request.forceTargetLanguagePrompt || !targetLanguage) { targetLanguage = await showTargetLanguagePrompt() ?? state.targetLanguage ?? getDefaultTargetLanguage(); @@ -68,18 +68,8 @@ function translateSelections(selections: vscode.Selection[], request: { targetLa return null; } - debug.write( - sourceLanguage - ? `Start translating '${text}' to '${targetLanguage}'` - : `Start translating '${text}' from '${sourceLanguage}' to '${targetLanguage}'` - ); const result = await deepl.translate(text, sourceLanguage, targetLanguage); progress.report({ increment }); - debug.write( - result - ? `Successfully translated '${text}' to '${result.text}'! (Source: '${result.detectedSourceLang}', Target: '${targetLanguage}')` - : `'${text}' could not be translated to '${targetLanguage}! (Reason: DeepL-API returned no translation)'` - ); return result; }) ); @@ -122,7 +112,9 @@ function createTranslateSelectionsCommand(request: GetTargetAndSourceLanguageReq await configureSettings(); } - const selections = vscode.window.activeTextEditor?.selections?.filter(selection => !selection.isEmpty); + const selections = vscode.window.activeTextEditor + ?.selections + ?.filter(selection => !selection.isEmpty); if (!selections || selections.length === 0) { return; } @@ -134,9 +126,71 @@ function createTranslateSelectionsCommand(request: GetTargetAndSourceLanguageReq export const setTargetLangauge = async () => state.targetLanguage = await showTargetLanguagePrompt(); export const configureSettings = async () => state.apiKey = await showApiKeyPrompt(); -export const translate = createTranslateSelectionsCommand({ forceTargetLanguagePrompt: false, forceSourceLanguagePrompt: false }); -export const translateTo = createTranslateSelectionsCommand({ forceTargetLanguagePrompt: true, forceSourceLanguagePrompt: false }); -export const translateFromTo = createTranslateSelectionsCommand({ forceTargetLanguagePrompt: true, forceSourceLanguagePrompt: true }); +export const translate = createTranslateSelectionsCommand({ + forceTargetLanguagePrompt: false, + forceSourceLanguagePrompt: false +}); +export const translateTo = createTranslateSelectionsCommand({ + forceTargetLanguagePrompt: true, + forceSourceLanguagePrompt: false +}); +export const translateFromTo = createTranslateSelectionsCommand({ + forceTargetLanguagePrompt: true, + forceSourceLanguagePrompt: true +}); + +export const duplicateAndTranslate = async () => { + const editor = vscode.window.activeTextEditor; + const selections = editor?.selections; + if (!selections || selections.length === 0) { + return; + } + + const { targetLanguage, sourceLanguage } = await getTargetAndSourceLanguage(); + + return displayTranslationNotification(async progress => { + const increment = 100 / 2 / selections.length; + + const translationResults = await Promise.all( + selections.map(async selection => { + const duplicateWholeLine = selection.isEmpty && selection.isSingleLine; + const range = duplicateWholeLine + ? editor.document.lineAt(selection.start.line).range + : new vscode.Range( + selection.start.line, + selection.start.character, + selection.end.line, + selection.end.character + ); + const text = editor.document.getText(range); + const result = await deepl.translate( + text, + sourceLanguage, + targetLanguage + ); + progress.report({ increment }); + + return { + text, + range, + result, + duplicateWholeLine + }; + }) + ); + + await editor.edit((editor: vscode.TextEditorEdit) => { + for (const translationResult of translationResults) { + const replacement = translationResult.duplicateWholeLine + ? `${translationResult.text}\n${translationResult.result.text}` + : `${translationResult.text}${translationResult.result.text}`; + + editor.replace(translationResult.range, replacement); + progress.report({ increment }); + } + }); + }); +}; export const translateAndPasteFromClipboard = async () => { const selections = vscode.window.activeTextEditor?.selections; @@ -149,14 +203,15 @@ export const translateAndPasteFromClipboard = async () => { return; } - const { targetLanguage, sourceLanguage } = await getTargetAndSourceLanguage({ - forceSourceLanguagePrompt: false, - forceTargetLanguagePrompt: false - }); + const { targetLanguage, sourceLanguage } = await getTargetAndSourceLanguage(); return displayTranslationNotification(async (progress) => { const increment = 100 / 2 / selections.length; - const translatedClipboardText = await deepl.translate(clipboardText, sourceLanguage, targetLanguage); + const translatedClipboardText = await deepl.translate( + clipboardText, + sourceLanguage, + targetLanguage + ); progress.report({ increment: increment * selections.length }); vscode.window.activeTextEditor?.edit((editor: vscode.TextEditorEdit) => { diff --git a/src/constants.ts b/src/constants.ts index bc859aa..7b4ca90 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -22,4 +22,5 @@ export const COMMAND_TRANSLATE = 'deepl.translate'; export const COMMAND_TRANSLATE_TO = 'deepl.translateTo'; export const COMMAND_TRANSLATE_FROM_TO = 'deepl.translateFromTo'; export const COMMAND_TRANSLATE_AND_PASTE_FROM_CLIPBOARD = 'deepl.translateAndPasteFromClipboard'; +export const COMMAND_DUPLICATE_AND_TRANSLATE = 'deepl.duplicateAndTranslate'; export const COMMAND_SET_TARGET_LANGAUGE = 'deepl.setTargetLanguage'; \ No newline at end of file diff --git a/src/deepl.ts b/src/deepl.ts index 562b07f..cf9bd16 100644 --- a/src/deepl.ts +++ b/src/deepl.ts @@ -67,6 +67,11 @@ const handleTranslationFailure = async (texts: T, r export async function translate(texts: T, sourceLanguage: SourceLanguageCode | undefined, targetLanguage: TargetLanguageCode, retries: number = 1): Promise { const translator = createTranslator(state.apiKey!); + debug.write( + sourceLanguage + ? `Start translating '${texts}' to '${targetLanguage}'` + : `Start translating '${texts}' from '${sourceLanguage}' to '${targetLanguage}'` + ); const results = await translator.translateText( texts, sourceLanguage ?? null, @@ -82,7 +87,6 @@ export async function translate(texts: T, sourceLan tagHandling: state.tagHandling || undefined } ); - const wasTranslationSuccessful = typeof texts === "string" ? (results as TextResult).text !== texts : (results as TextResult[]).filter((result, index) => result.text === texts[index]).length === 0; diff --git a/src/extension.ts b/src/extension.ts index b5da28e..bca04b6 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,8 +1,8 @@ import * as vscode from 'vscode'; import { setup } from './state'; import { createStatusBarItem } from './status-bar'; -import { configureSettings, translate, translateFromTo, translateTo, translateAndPasteFromClipboard, setTargetLangauge } from './commands'; -import { COMMAND_CONFIGURE, COMMAND_TRANSLATE, COMMAND_TRANSLATE_FROM_TO, COMMAND_TRANSLATE_TO, COMMAND_TRANSLATE_AND_PASTE_FROM_CLIPBOARD, COMMAND_SET_TARGET_LANGAUGE } from './constants'; +import { configureSettings, translate, translateFromTo, translateTo, translateAndPasteFromClipboard, setTargetLangauge, duplicateAndTranslate } from './commands'; +import { COMMAND_CONFIGURE, COMMAND_TRANSLATE, COMMAND_TRANSLATE_FROM_TO, COMMAND_TRANSLATE_TO, COMMAND_TRANSLATE_AND_PASTE_FROM_CLIPBOARD, COMMAND_SET_TARGET_LANGAUGE, COMMAND_DUPLICATE_AND_TRANSLATE } from './constants'; export async function activate(context: vscode.ExtensionContext) { await setup(context); @@ -14,6 +14,7 @@ export async function activate(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.commands.registerCommand(COMMAND_TRANSLATE_FROM_TO, translateFromTo)); context.subscriptions.push(vscode.commands.registerCommand(COMMAND_TRANSLATE_AND_PASTE_FROM_CLIPBOARD, translateAndPasteFromClipboard)); context.subscriptions.push(vscode.commands.registerCommand(COMMAND_SET_TARGET_LANGAUGE , setTargetLangauge)); + context.subscriptions.push(vscode.commands.registerCommand(COMMAND_DUPLICATE_AND_TRANSLATE , duplicateAndTranslate)); } export function deactivate() {}