Repository: funkyremi/vscode-google-translate Branch: master Commit: a2b7b9574c22 Files: 21 Total size: 34.0 KB Directory structure: gitextract_x7f9err2/ ├── .eslintrc.json ├── .gitattributes ├── .github/ │ └── workflows/ │ └── release.yml ├── .gitignore ├── .vscode/ │ ├── extensions.json │ ├── launch.json │ └── settings.json ├── .vscodeignore ├── CHANGELOG.md ├── LICENSE.txt ├── README.md ├── extension.js ├── jsconfig.json ├── languages.js ├── package.json ├── src/ │ ├── commands.js │ ├── translate.js │ └── utils.js └── test/ ├── runTest.js └── suite/ ├── extension.test.js └── index.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .eslintrc.json ================================================ { "env": { "browser": false, "commonjs": true, "es6": true, "node": true }, "parserOptions": { "ecmaFeatures": { "jsx": true }, "sourceType": "module" }, "rules": { "no-const-assign": "warn", "no-this-before-super": "warn", "no-undef": "warn", "no-unreachable": "warn", "no-unused-vars": "warn", "constructor-super": "warn", "valid-typeof": "warn" } } ================================================ FILE: .gitattributes ================================================ # Set default behavior to automatically normalize line endings. * text=auto ================================================ FILE: .github/workflows/release.yml ================================================ on: release: types: [published] name: Deploy Extension jobs: Publish: name: Publish runs-on: ubuntu-latest steps: - uses: actions/checkout@master - name: Install uses: actions/npm@master with: args: install --unsafe-perm - name: Publish uses: lannonbr/vsce-action@master env: VSCE_TOKEN: ${{ secrets.VSCE_TOKEN }} with: args: publish -p $VSCE_TOKEN ================================================ FILE: .gitignore ================================================ node_modules .vscode-test/ server/out/* *.vsix ================================================ FILE: .vscode/extensions.json ================================================ { // See http://go.microsoft.com/fwlink/?LinkId=827846 // for the documentation about the extensions.json format "recommendations": [ "dbaeumer.vscode-eslint" ] } ================================================ FILE: .vscode/launch.json ================================================ // A launch configuration that launches the extension inside a new window // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 { "version": "0.2.0", "configurations": [ { "name": "Extension", "type": "extensionHost", "request": "launch", "runtimeExecutable": "${execPath}", "args": [ "--extensionDevelopmentPath=${workspaceFolder}" ] }, { "name": "Extension Tests", "type": "extensionHost", "request": "launch", "runtimeExecutable": "${execPath}", "args": [ "--extensionDevelopmentPath=${workspaceFolder}", "--extensionTestsPath=${workspaceFolder}/test" ] } ] } ================================================ FILE: .vscode/settings.json ================================================ // Place your settings in this file to overwrite default and user settings. { } ================================================ FILE: .vscodeignore ================================================ .vscode/** .vscode-test/** test/** .gitignore jsconfig.json vsc-extension-quickstart.md .eslintrc.json ================================================ FILE: CHANGELOG.md ================================================ # Change Log ## 1.5.0 - Refactor - Bug fixed - Dependencies bump - Performance improvements - Integration tests - Remove hover translation feature and typescript code ## 1.4.13 - Update translation dependencies ## 1.4.12 - Put back the mandatory modules ## 1.4.11 - Exclude extra files from release ## 1.4.10 - Hotfix for 403 error ## 1.4.9 - Fix Hebrew language ## 1.4.8 - Show's translations when hovering over code and text ## 1.4.7 - Translate camel case support ## 1.4.6 - Improve default language settings - Fix default shortcut ## 1.4.5 - Update translation library ## 1.4.4 - Add Uyghur language ## 1.4.3 - Add Chinese Traditional language ## 1.4.2 - Improve CI/CD ## 1.4.1 - Fix multi-line text translation ## 1.4.0 - Proxy support - Fix HTML entity not decoded - CI/CD with Github Actions ## 1.3.3 - Fix error messages not displayed ## 1.3.2 - Fix 'plugin stopped working' due to google unofficial api that stopped working ## 1.3.1 - Fix 'translation does nothing' random issue ## 1.3.0 - Add the translate of a line feature ## 1.2.0 - Prefered language setting ## 1.1.5 - Fix translation issue (#2) ## 1.1.4 - Change key biding to ALT+SHIFT+T to make it work with most of people - Update readme ## 1.1.3 - Change key biding to CTRL+SHIFT+T or CMD+SHIFT+T ## 1.1.2 - Code improvements - Better demo gif ## 1.1.1 - Add recently used languages ## 1.1.0 - Multiselect support - Update readme - MIT License - Gif example - Badges - Logo ## 1.0.2 - Minor improvements ## 1.0.1 - Minor improvements ## 1.0.0 - Initial release ================================================ FILE: LICENSE.txt ================================================ The MIT License (MIT) Copyright (c) 2016 HookyQR Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Vscode Google Translate Quickly translate text right in your code 🚀 ![Demo](demo.gif) ## Usage ### Translate selected text 1. Select some text to translate 1. Press `ALT+SHIFT+T` 1. Select the output languages you want and enjoy 👍 ### Translate a line under cursor This feature inserts a newline under the current one with translation 1. Set cursor/cursors on line(s) to translate 1. Select menu 'Translate line(s) under the cursor' 1. Select the output languages you want and enjoy ## Preferred language settings Want to quickly translate into a specific language? Run Command 'Set Preferred Language' or Set it in VSCode extension settings ## Proxy Support You can use a proxy to translate text with the following settings: ```js "vscodeGoogleTranslate.host": "120.0.0.1" // Proxy disabled if empty "vscodeGoogleTranslate.port": "8080" // Proxy port "vscodeGoogleTranslate.username": "admin" // Proxy auth disabled if empty "vscodeGoogleTranslate.password": "password" // Proxy password ``` ## Pull request Pull request are welcome. Fork the project, clone it, install dependencies `npm i` and start coding :-). Many thanks to the people who participate for making it awesome! ## Show your support **Give five stars 🤩** If you like it, [rate it](https://marketplace.visualstudio.com/items?itemName=funkyremi.vscode-google-translate&ssr=false#review-details) ================================================ FILE: extension.js ================================================ const { initializeCommands } = require('./src/commands'); function activate(context) { initializeCommands(context); } exports.activate = activate; ================================================ FILE: jsconfig.json ================================================ { "compilerOptions": { "module": "commonjs", "target": "es6", "checkJs": false, /* Typecheck .js files. */ "lib": [ "es6" ] }, "exclude": [ "node_modules" ] } ================================================ FILE: languages.js ================================================ const languages = [ { name: 'Afrikaans', value: 'af', }, { name: 'Albanian', value: 'sq', }, { name: 'Amharic', value: 'am', }, { name: 'Arabic', value: 'ar', }, { name: 'Armenian', value: 'hy', }, { name: 'Azeerbaijani', value: 'az', }, { name: 'Basque', value: 'eu', }, { name: 'Belarusian', value: 'be', }, { name: 'Bengali', value: 'bn', }, { name: 'Bosnian', value: 'bs', }, { name: 'Bulgarian', value: 'bg', }, { name: 'Catalan', value: 'ca', }, { name: 'Cebuano', value: 'ceb', }, { name: 'Chinese (Simplified)', value: 'zh-CN', }, { name: 'Chinese (Traditional)', value: 'zh-TW', }, { name: 'Corsican', value: 'co', }, { name: 'Croatian', value: 'hr', }, { name: 'Czech', value: 'cs', }, { name: 'Danish', value: 'da', }, { name: 'Dutch', value: 'nl', }, { name: 'English', value: 'en', }, { name: 'Esperanto', value: 'eo', }, { name: 'Estonian', value: 'et', }, { name: 'Finnish', value: 'fi', }, { name: 'French', value: 'fr', }, { name: 'Frisian', value: 'fy', }, { name: 'Galician', value: 'gl', }, { name: 'Georgian', value: 'ka', }, { name: 'German', value: 'de', }, { name: 'Greek', value: 'el', }, { name: 'Gujarati', value: 'gu', }, { name: 'Haitian Creole', value: 'ht', }, { name: 'Hausa', value: 'ha', }, { name: 'Hawaiian', value: 'haw', }, { name: 'Hebrew', value: 'iw', }, { name: 'Hindi', value: 'hi', }, { name: 'Hmong', value: 'hmn', }, { name: 'Hungarian', value: 'hu', }, { name: 'Icelandic', value: 'is', }, { name: 'Igbo', value: 'ig', }, { name: 'Indonesian', value: 'id', }, { name: 'Irish', value: 'ga', }, { name: 'Italian', value: 'it', }, { name: 'Japanese', value: 'ja', }, { name: 'Javanese', value: 'jw', }, { name: 'Kannada', value: 'kn', }, { name: 'Kazakh', value: 'kk', }, { name: 'Khmer', value: 'km', }, { name: 'Korean', value: 'ko', }, { name: 'Kurdish', value: 'ku', }, { name: 'Kyrgyz', value: 'ky', }, { name: 'Lao', value: 'lo', }, { name: 'Latin', value: 'la', }, { name: 'Latvian', value: 'lv', }, { name: 'Lithuanian', value: 'lt', }, { name: 'Luxembourgish', value: 'lb', }, { name: 'Macedonian', value: 'mk', }, { name: 'Malagasy', value: 'mg', }, { name: 'Malay', value: 'ms', }, { name: 'Malayalam', value: 'ml', }, { name: 'Maltese', value: 'mt', }, { name: 'Maori', value: 'mi', }, { name: 'Marathi', value: 'mr', }, { name: 'Mongolian', value: 'mn', }, { name: 'Myanmar', value: 'my', }, { name: 'Nepali', value: 'ne', }, { name: 'Norwegian', value: 'no', }, { name: 'Nyanja', value: 'ny', }, { name: 'Pashto', value: 'ps', }, { name: 'Persian', value: 'fa', }, { name: 'Polish', value: 'pl', }, { name: 'Portuguese', value: 'pt', }, { name: 'Punjabi', value: 'pa', }, { name: 'Romanian', value: 'ro', }, { name: 'Russian', value: 'ru', }, { name: 'Samoan', value: 'sm', }, { name: 'Scots Gaelic', value: 'gd', }, { name: 'Serbian', value: 'sr', }, { name: 'Sesotho', value: 'st', }, { name: 'Shona', value: 'sn', }, { name: 'Sindhi', value: 'sd', }, { name: 'Sinhala', value: 'si', }, { name: 'Slovak', value: 'sk', }, { name: 'Slovenian', value: 'sl', }, { name: 'Somali', value: 'so', }, { name: 'Spanish', value: 'es', }, { name: 'Sundanese', value: 'su', }, { name: 'Swahili', value: 'sw', }, { name: 'Swedish', value: 'sv', }, { name: 'Tagalog', value: 'tl', }, { name: 'Tajik', value: 'tg', }, { name: 'Tamil', value: 'ta', }, { name: 'Telugu', value: 'te', }, { name: 'Thai', value: 'th', }, { name: 'Turkish', value: 'tr', }, { name: 'Ukrainian', value: 'uk', }, { name: 'Urdu', value: 'ur', }, { name: 'Uyghur', value: 'ug', }, { name: 'Uzbek', value: 'uz', }, { name: 'Vietnamese', value: 'vi', }, { name: 'Welsh', value: 'cy', }, { name: 'Xhosa', value: 'xh', }, { name: 'Yiddish', value: 'yi', }, { name: 'Yoruba', value: 'yo', }, { name: 'Zulu', value: 'zu', } ]; module.exports = languages; ================================================ FILE: package.json ================================================ { "name": "vscode-google-translate", "displayName": "Vscode Google Translate", "description": "Translate text right in your code", "publisher": "funkyremi", "version": "1.5.0", "license": "MIT", "icon": "icon.png", "repository": { "type": "git", "url": "https://github.com/funkyremi/vscode-google-translate.git" }, "engines": { "vscode": "^1.29.0" }, "categories": [ "Formatters" ], "activationEvents": [ "onCommand:extension.translateText", "onCommand:extension.translateTextPreferred", "onCommand:extension.translateLinesUnderCursor", "onCommand:extension.setPreferredLanguage", "onCommand:extension.translateLinesUnderCursorPreferred" ], "main": "./extension", "contributes": { "commands": [ { "command": "extension.translateText", "title": "Translate selection(s)" }, { "command": "extension.translateTextPreferred", "title": "Translate selection(s) to preferred language" }, { "command": "extension.translateLinesUnderCursor", "title": "Translate line(s) under the cursor" }, { "command": "extension.setPreferredLanguage", "title": "Set preferred target language" }, { "command": "extension.translateLinesUnderCursorPreferred", "title": "Translate line(s) under the cursor to preferred language" } ], "configuration": { "title": "Vscode Google Translate", "type": "object", "properties": { "vscodeGoogleTranslate.preferredLanguage": { "type": "string", "enum": [ "Afrikaans", "Albanian", "Amharic", "Arabic", "Armenian", "Azeerbaijani", "Basque", "Belarusian", "Bengali", "Bosnian", "Bulgarian", "Catalan", "Cebuano", "Chinese (Simplified)", "Chinese (Traditional)", "Corsican", "Croatian", "Czech", "Danish", "Dutch", "English", "Esperanto", "Estonian", "Finnish", "French", "Frisian", "Galician", "Georgian", "German", "Greek", "Gujarati", "Haitian Creole", "Hausa", "Hawaiian", "Hebrew", "Hindi", "Hmong", "Hungarian", "Icelandic", "Igbo", "Indonesian", "Irish", "Italian", "Japanese", "Javanese", "Kannada", "Kazakh", "Khmer", "Korean", "Kurdish", "Kyrgyz", "Lao", "Latin", "Latvian", "Lithuanian", "Luxembourgish", "Macedonian", "Malagasy", "Malay", "Malayalam", "Maltese", "Maori", "Marathi", "Mongolian", "Myanmar", "Nepali", "Norwegian", "Nyanja", "Pashto", "Persian", "Polish", "Portuguese", "Punjabi", "Romanian", "Russian", "Samoan", "Scots Gaelic", "Serbian", "Sesotho", "Shona", "Sindhi", "Sinhala", "Slovak", "Slovenian", "Somali", "Spanish", "Sundanese", "Swahili", "Swedish", "Tagalog", "Tajik", "Tamil", "Telugu", "Thai", "Turkish", "Ukrainian", "Urdu", "Uyghur", "Uzbek", "Vietnamese", "Welsh", "Xhosa", "Yiddish", "Yoruba", "Zulu" ], "description": "The preferred target language" }, "vscodeGoogleTranslate.proxyHost": { "type": "string", "description": "The proxy host (set it to enable proxy) (Optional)" }, "vscodeGoogleTranslate.proxyPort": { "type": "string", "description": "The proxy port (Optional)" }, "vscodeGoogleTranslate.proxyUsername": { "type": "string", "description": "The proxy username (Optional)" }, "vscodeGoogleTranslate.proxyPassword": { "type": "string", "description": "The proxy password (Optional)" } } }, "keybindings": [ { "key": "alt+shift+t", "when": "editorTextFocus", "command": "extension.translateText" } ] }, "scripts": { "test": "node ./test/runTest.js" }, "devDependencies": { "@types/mocha": "^10.0.6", "@types/node": "^20.11.24", "@vscode/test-electron": "^2.5.2", "eslint": "^8.56.0", "glob": "^11.0.3", "mocha": "^11.7.2" }, "dependencies": { "@types/vscode": "^1.84.0", "@vitalets/google-translate-api": "^9.2.1", "camelcase": "^8.0.0", "he": "^1.2.0", "http-proxy-agent": "^7.0.2", "humanize-string": "^3.0.0", "typescript": "^5.3.3" } } ================================================ FILE: src/commands.js ================================================ const vscode = require('vscode'); const he = require("he"); const languages = require('../languages.js'); const { recentlyUsed, updateLanguageList, getSelectedText, getSelectedLineText, getPreferredLanguage, setPreferredLanguage, } = require('./utils'); const { getTranslationPromise } = require('./translate'); function getTranslationsPromiseArray(selections, document, selectedLanguage) { return selections.map((selection) => { const selectedText = getSelectedText(document, selection); return getTranslationPromise(selectedText, selectedLanguage, selection); }); } function getTranslationsPromiseArrayLine( selections, document, selectedLanguage ) { return selections.map((selection) => { const selectedLineText = getSelectedLineText(document, selection); return getTranslationPromise(selectedLineText, selectedLanguage, selection); }); } function initializeCommands(context) { const translateText = vscode.commands.registerCommand( "extension.translateText", function () { const editor = vscode.window.activeTextEditor; const { document, selections } = editor; const quickPickData = recentlyUsed .map((r) => ({ label: r, description: "(recently used)", })) .concat(languages.map((r) => ({ label: r.name }))); vscode.window .showQuickPick(quickPickData) .then((selectedLanguage) => { if (!selectedLanguage) return; updateLanguageList(selectedLanguage.label); const translationsPromiseArray = getTranslationsPromiseArray( selections, document, languages.find((r) => r.name === selectedLanguage.label).value ); Promise.all(translationsPromiseArray) .then(function (results) { editor.edit((builder) => { results.forEach((r) => { if (!!r.translation) { builder.replace(r.selection, he.decode(r.translation)); } }); }); }) .catch((e) => vscode.window.showErrorMessage(e.message)); }) .catch((err) => { vscode.window.showErrorMessage(err.message); }); } ); context.subscriptions.push(translateText); const setPreferredLanguageFnc = vscode.commands.registerCommand( "extension.setPreferredLanguage", setPreferredLanguage ); context.subscriptions.push(setPreferredLanguageFnc); const translateTextPreferred = vscode.commands.registerCommand( "extension.translateTextPreferred", async function () { const editor = vscode.window.activeTextEditor; const { document, selections } = editor; // vscodeTranslate.preferredLanguage const preferredLanguage = await getPreferredLanguage(); const locale = languages.find( (element) => element.name === preferredLanguage ).value; if (!locale) { return; } const translationsPromiseArray = getTranslationsPromiseArray( selections, document, locale ); Promise.all(translationsPromiseArray) .then(function (results) { editor.edit((builder) => { results.forEach((r) => { if (!!r.translation) { builder.replace(r.selection, he.decode(r.translation)); } }); }); }) .catch((e) => vscode.window.showErrorMessage(e.message)); } ); context.subscriptions.push(translateTextPreferred); const translateLinesUnderCursor = vscode.commands.registerCommand( "extension.translateLinesUnderCursor", function translateLinesUnderCursorcallback() { const editor = vscode.window.activeTextEditor; const { document, selections } = editor; const quickPickData = recentlyUsed .map((r) => ({ label: r.name, description: "(recently used)", })) .concat(languages.map((r) => ({ label: r.name }))); vscode.window .showQuickPick(quickPickData) .then((selectedLanguage) => { if (!selectedLanguage) return; updateLanguageList(selectedLanguage.label); const translationsPromiseArray = getTranslationsPromiseArrayLine( selections, document, languages.find((r) => r.name === selectedLanguage.label).value ); Promise.all(translationsPromiseArray) .then(function (results) { editor.edit((builder) => { results.forEach((r) => { if (!!r.translation) { const ffix = ["", "\n"]; if ( editor.document.lineCount - 1 === r.selection.start.line ) [ffix[0], ffix[1]] = [ffix[1], ffix[0]]; const p = new vscode.Position(r.selection.start.line + 1); builder.insert(p, `${ffix[0]}${r.translation}${ffix[1]}`); } }); }); }) .catch((e) => vscode.window.showErrorMessage(e.message)); }) .catch((err) => { vscode.window.showErrorMessage(err.message); }); } ); context.subscriptions.push(translateLinesUnderCursor); const translateLinesUnderCursorPreferred = vscode.commands.registerCommand( "extension.translateLinesUnderCursorPreferred", async function translateLinesUnderCursorPreferredcallback() { const editor = vscode.window.activeTextEditor; const { document, selections } = editor; const preferredLanguage = await getPreferredLanguage(); const locale = languages.find( (element) => element.name === preferredLanguage ).value; if (!locale) { vscode.window.showWarningMessage( "Prefered language is requeried for this feature! Please set this in the settings." ); return; } const translationsPromiseArray = getTranslationsPromiseArrayLine( selections, document, locale ); Promise.all(translationsPromiseArray) .then(function (results) { editor.edit((builder) => { results.forEach((r) => { if (!!r.translation) { const ffix = ["", "\n"]; if (editor.document.lineCount - 1 === r.selection.start.line) [ffix[0], ffix[1]] = [ffix[1], ffix[0]]; const p = new vscode.Position(r.selection.start.line + 1); builder.insert(p, `${ffix[0]}${r.translation}${ffix[1]}`); } }); }); }) .catch((e) => vscode.window.showErrorMessage(e.message)); } ); context.subscriptions.push(translateLinesUnderCursorPreferred); } module.exports = { initializeCommands }; ================================================ FILE: src/translate.js ================================================ const { translate: gti } = require("@vitalets/google-translate-api"); const { HttpProxyAgent } = require('http-proxy-agent'); const humanizeString = require("humanize-string"); const camelcase = require("camelcase"); const { getProxyConfig } = require("./utils"); async function translate(text, options) { const translateOptions = { to: options.to, fetchOptions: {} }; if (options && options.proxy) { const proxyUrl = `http://${options.proxy.auth ? `${options.proxy.auth.username}:${options.proxy.auth.password}@` : ''}${options.proxy.host}:${options.proxy.port}`; translateOptions.fetchOptions.agent = new HttpProxyAgent(proxyUrl); } const { text: translatedText } = await gti(text, translateOptions); // The old code expected an object with a 'data' array. We'll mimic that. return { data: [translatedText] }; } async function getTranslationPromise(selectedText, selectedLanguage, selection) { const { host, port, username, password } = getProxyConfig(); const translationConfiguration = { to: selectedLanguage, }; if (!!host) { translationConfiguration.proxy = { host, port: Number(port), }; if (!!username) { translationConfiguration.proxy.auth = { username, password, }; } } try { let res = await translate(selectedText, translationConfiguration); if (!!res && !!res.data) { // If google rejects the string it will return the same string as input // We can try to split the string into parts, then translate again. Then return it to a // camel style casing if (res.data[0] === selectedText) { const humanizedRes = await translate(humanizeString(selectedText), translationConfiguration); if (!!humanizedRes && !!humanizedRes.data) { return { selection, translation: camelcase(humanizedRes.data[0]), }; } else { throw new Error("Google Translation API issue on fallback"); } } else { return { selection, translation: res.data[0], }; } } else { throw new Error("Google Translation API issue"); } } catch (e) { // The original function returned a Promise.reject, so we'll throw an error // which will be caught by the caller's .catch block. throw new Error("Google Translation API issue: " + e.message); } } module.exports = { getTranslationPromise } ================================================ FILE: src/utils.js ================================================ const vscode = require('vscode'); const languages = require('../languages.js'); /** * The list of recently used languages * * @type {Array.} */ const recentlyUsed = []; /** * Updates languages lists for the convenience of users * * @param {string} selectedLanguage The language code to update * @returns {undefined} */ function updateLanguageList(selectedLanguage) { const index = recentlyUsed.findIndex((r) => r === selectedLanguage); if (index !== -1) { // Remove the recently used language from the list recentlyUsed.splice(index, 1); } // Add the language in recently used languages recentlyUsed.splice(0, 0, selectedLanguage); } /** * Extracts a text from the active document selection * * @param {vscode.TextDocument} document The current document * @param {vscode.Selection} selection The current selection * @returns {string} A text */ function getSelectedText(document, selection) { const charRange = new vscode.Range( selection.start.line, selection.start.character, selection.end.line, selection.end.character ); return document.getText(charRange); } /** * Gets a text of the first line from active selection * * @param {vscode.TextDocument} document The current document * @param {vscode.Selection} selection The current selection * @returns {string} */ function getSelectedLineText(document, selection) { return document.getText( document.lineAt(selection.start.line).rangeIncludingLineBreak ); } /** * Returns user settings Preferred language. * If user hasn't set preferred lang. Prompt to set. */ function getPreferredLanguage() { return ( vscode.workspace .getConfiguration("vscodeGoogleTranslate") .get("preferredLanguage") || setPreferredLanguage() ); } async function setPreferredLanguage() { const quickPickData = recentlyUsed .map((r) => ({ label: r, description: "(recently used)", })) .concat(languages.map((r) => ({ label: r.name }))); const selectedLanguage = await vscode.window.showQuickPick(quickPickData); if (!selectedLanguage) { return; } vscode.workspace .getConfiguration() .update( "vscodeGoogleTranslate.preferredLanguage", selectedLanguage.label, vscode.ConfigurationTarget.Global ); return selectedLanguage.label; } /** * Returns user settings proxy config * * @returns {string} */ function getProxyConfig() { const config = vscode.workspace.getConfiguration("vscodeGoogleTranslate"); return { host: config.get("proxyHost"), port: config.get("proxyPort"), username: config.get("proxyUsername"), password: config.get("proxyPassword"), }; } module.exports = { recentlyUsed, updateLanguageList, getSelectedText, getSelectedLineText, getPreferredLanguage, setPreferredLanguage, getProxyConfig } ================================================ FILE: test/runTest.js ================================================ const path = require('path'); const { runTests } = require('@vscode/test-electron'); async function main() { try { // The folder containing the Extension Manifest package.json // Passed to `--extensionDevelopmentPath` const extensionDevelopmentPath = path.resolve(__dirname, '../'); // The path to the extension test script // Passed to --extensionTestsPath const extensionTestsPath = path.resolve(__dirname, './suite/index'); // Download VS Code, unzip it and run the integration test await runTests({ extensionDevelopmentPath, extensionTestsPath }); } catch (err) { console.error('Failed to run tests'); process.exit(1); } } main(); ================================================ FILE: test/suite/extension.test.js ================================================ /* global suite, test, setup */ const assert = require('assert'); const vscode = require('vscode'); suite("Extension Command Tests", function() { setup(async () => { // Set a preferred language to avoid the quick pick menu await vscode.workspace.getConfiguration('vscodeGoogleTranslate').update('preferredLanguage', 'French', vscode.ConfigurationTarget.Global); }); test("Should translate selected text", async () => { const document = await vscode.workspace.openTextDocument({ content: 'Hello World' }); const editor = await vscode.window.showTextDocument(document); const originalText = 'Hello World'; editor.selection = new vscode.Selection(new vscode.Position(0, 0), new vscode.Position(0, originalText.length)); // Give a moment for the extension to activate if it hasn't already await new Promise(resolve => setTimeout(resolve, 1000)); await vscode.commands.executeCommand('extension.translateTextPreferred'); // Add a delay to allow for the translation and edit to occur await new Promise(resolve => setTimeout(resolve, 2000)); const newText = editor.document.getText(); // We don't know the exact translation, but it should not be the original text. assert.notStrictEqual(newText, originalText); // A very basic check to see if it could be the French translation assert.ok(newText.toLowerCase().includes('bonjour')); }).timeout(5000); // Increase timeout to allow for API calls }); ================================================ FILE: test/suite/index.js ================================================ const path = require('path'); const Mocha = require('mocha'); const { glob } = require('glob'); function run() { // Create the mocha test const mocha = new Mocha({ ui: 'tdd', color: true }); const testsRoot = path.resolve(__dirname, '.'); return new Promise((c, e) => { glob('**/**.test.js', { cwd: testsRoot }) .then(files => { // Add files to the test suite files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); try { // Run the mocha test mocha.run(failures => { if (failures > 0) { e(new Error(`${failures} tests failed.`)); } else { c(); } }); } catch (err) { console.error(err); e(err); } }) .catch(err => { return e(err); }); }); } module.exports = { run };