├── .gitignore ├── media ├── demo.gif └── icon.png ├── .vscodeignore ├── .vscode ├── extensions.json └── launch.json ├── jsconfig.json ├── CHANGELOG.md ├── test ├── suite │ ├── extension.test.js │ └── index.js └── runTest.js ├── .eslintrc.json ├── LICENSE ├── package.json ├── README.md ├── vsc-extension-quickstart.md └── extension.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode-test/ 3 | *.vsix 4 | -------------------------------------------------------------------------------- /media/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msgul/sofa-extension/HEAD/media/demo.gif -------------------------------------------------------------------------------- /media/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msgul/sofa-extension/HEAD/media/icon.png -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | test/** 4 | .gitignore 5 | .yarnrc 6 | vsc-extension-quickstart.md 7 | **/jsconfig.json 8 | **/*.map 9 | **/.eslintrc.json 10 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "dbaeumer.vscode-eslint" 6 | ] 7 | } -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "ES2020", 5 | "checkJs": false, /* Typecheck .js files. */ 6 | "lib": [ 7 | "ES2020" 8 | ] 9 | }, 10 | "exclude": [ 11 | "node_modules" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to the "soffa" extension will be documented in this file. 4 | 5 | Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. 6 | 7 | ## [Unreleased] 8 | 9 | - Initial release -------------------------------------------------------------------------------- /test/suite/extension.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | // You can import and use all API from the 'vscode' module 4 | // as well as import your extension to test it 5 | const vscode = require('vscode'); 6 | // const myExtension = require('../extension'); 7 | 8 | suite('Extension Test Suite', () => { 9 | vscode.window.showInformationMessage('Start all tests.'); 10 | 11 | test('Sample test', () => { 12 | assert.strictEqual(-1, [1, 2, 3].indexOf(5)); 13 | assert.strictEqual(-1, [1, 2, 3].indexOf(0)); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": false, 4 | "commonjs": true, 5 | "es6": true, 6 | "node": true, 7 | "mocha": true 8 | }, 9 | "parserOptions": { 10 | "ecmaVersion": 2018, 11 | "ecmaFeatures": { 12 | "jsx": true 13 | }, 14 | "sourceType": "module" 15 | }, 16 | "rules": { 17 | "no-const-assign": "warn", 18 | "no-this-before-super": "warn", 19 | "no-undef": "warn", 20 | "no-unreachable": "warn", 21 | "no-unused-vars": "warn", 22 | "constructor-super": "warn", 23 | "valid-typeof": "warn" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/runTest.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const { runTests } = require('@vscode/test-electron'); 4 | 5 | async function main() { 6 | try { 7 | // The folder containing the Extension Manifest package.json 8 | // Passed to `--extensionDevelopmentPath` 9 | const extensionDevelopmentPath = path.resolve(__dirname, '../'); 10 | 11 | // The path to the extension test script 12 | // Passed to --extensionTestsPath 13 | const extensionTestsPath = path.resolve(__dirname, './suite/index'); 14 | 15 | // Download VS Code, unzip it and run the integration test 16 | await runTests({ extensionDevelopmentPath, extensionTestsPath }); 17 | } catch (err) { 18 | console.error('Failed to run tests'); 19 | process.exit(1); 20 | } 21 | } 22 | 23 | main(); 24 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that launches the extension inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "args": [ 13 | "--extensionDevelopmentPath=${workspaceFolder}" 14 | ] 15 | }, 16 | { 17 | "name": "Extension Tests", 18 | "type": "extensionHost", 19 | "request": "launch", 20 | "args": [ 21 | "--extensionDevelopmentPath=${workspaceFolder}", 22 | "--extensionTestsPath=${workspaceFolder}/test/suite/index" 23 | ] 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /test/suite/index.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const Mocha = require('mocha'); 3 | const glob = require('glob'); 4 | 5 | function run() { 6 | // Create the mocha test 7 | const mocha = new Mocha({ 8 | ui: 'tdd', 9 | color: true 10 | }); 11 | 12 | const testsRoot = path.resolve(__dirname, '..'); 13 | 14 | return new Promise((c, e) => { 15 | glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { 16 | if (err) { 17 | return e(err); 18 | } 19 | 20 | // Add files to the test suite 21 | files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); 22 | 23 | try { 24 | // Run the mocha test 25 | mocha.run(failures => { 26 | if (failures > 0) { 27 | e(new Error(`${failures} tests failed.`)); 28 | } else { 29 | c(); 30 | } 31 | }); 32 | } catch (err) { 33 | console.error(err); 34 | e(err); 35 | } 36 | }); 37 | }); 38 | } 39 | 40 | module.exports = { 41 | run 42 | }; 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Mert Şamil Gül 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sofa", 3 | "displayName": "Sofa", 4 | "description": "Sofa (StackOverFlow Answers)", 5 | "version": "0.0.2", 6 | "publisher": "msgul", 7 | "engines": { 8 | "vscode": "^1.60.0" 9 | }, 10 | "categories": [ 11 | "Other" 12 | ], 13 | "icon": "media/icon.png", 14 | "activationEvents": [ 15 | "onCommand:sofa.getcode" 16 | ], 17 | "main": "./extension.js", 18 | "contributes": { 19 | "commands": [ 20 | { 21 | "command": "sofa.getcode", 22 | "title": "Get the Code" 23 | } 24 | ], 25 | "keybindings": [ 26 | { 27 | "command": "sofa.getcode", 28 | "key": "ctrl+enter", 29 | "mac": "cmd+enter", 30 | "when": "editorTextFocus" 31 | } 32 | ] 33 | }, 34 | "scripts": { 35 | "lint": "eslint .", 36 | "pretest": "npm run lint", 37 | "test": "node ./test/runTest.js" 38 | }, 39 | "devDependencies": { 40 | "@types/vscode": "^1.60.0", 41 | "@types/glob": "^7.1.4", 42 | "@types/mocha": "^9.0.0", 43 | "@types/node": "14.x", 44 | "eslint": "^7.32.0", 45 | "glob": "^7.1.7", 46 | "mocha": "^9.1.1", 47 | "typescript": "^4.4.3", 48 | "@vscode/test-electron": "^1.6.2" 49 | }, 50 | "dependencies": { 51 | "cheerio": "^1.0.0-rc.10", 52 | "clipboardy": "^2.3.0", 53 | "request": "^2.88.2", 54 | "request-promise": "^4.2.6" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sofa (StackOverflow Answers) Extension 2 | 3 | Sofa is an **open-source** MIT-licensed **vscode extension** that is designed to speed up your coding time by bringing you the answers related to your problem on stackoverflow.com. 4 | 5 | ## Features 6 | 7 | - Retrieves all answers and scrapes the code out of them for you to use it. 8 | - Retrieved codes can be selected from vscode QuickPick. 9 | - Language of the current file is added to your question automatically. 10 | - Opens your browser and directs you to the StackOverflow page (optional) 11 | 12 | ## Usage 13 | 14 | - Type your problem and press ctrl + enter to retrieve the codes. You can pick one of the results from vscode.QuickPick. Press ctrl + enter again to get the StackOverflow question page. 15 | 16 | ![alt text](media/demo.gif) 17 | 18 | ### Examples 19 | 20 | - Python 21 | 22 | ```py 23 | # read line by line 24 | with open(filename) as file: 25 | lines = file.readlines() 26 | lines = [line.rstrip() for line in lines] 27 | ``` 28 | 29 | - Html 30 | 31 | ```html 32 | 33 |
34 |
35 |
Left
36 |
Right
37 |
38 |
39 | ``` 40 | - Javascript 41 | 42 | ```js 43 | // traverse all elements 44 | var all = document.getElementsByTagName("*"); 45 | for (var i = 0; i < all.length; i++) { 46 | console.log(all[i].nodeName); 47 | } 48 | ``` 49 | -------------------------------------------------------------------------------- /vsc-extension-quickstart.md: -------------------------------------------------------------------------------- 1 | # Welcome to your VS Code Extension 2 | 3 | ## What's in the folder 4 | 5 | * This folder contains all of the files necessary for your extension. 6 | * `package.json` - this is the manifest file in which you declare your extension and command. 7 | * The sample plugin registers a command and defines its title and command name. With this information VS Code can show the command in the command palette. It doesn’t yet need to load the plugin. 8 | * `extension.js` - this is the main file where you will provide the implementation of your command. 9 | * The file exports one function, `activate`, which is called the very first time your extension is activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`. 10 | * We pass the function containing the implementation of the command as the second parameter to `registerCommand`. 11 | 12 | ## Get up and running straight away 13 | 14 | * Press `F5` to open a new window with your extension loaded. 15 | * Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`. 16 | * Set breakpoints in your code inside `extension.js` to debug your extension. 17 | * Find output from your extension in the debug console. 18 | 19 | ## Make changes 20 | 21 | * You can relaunch the extension from the debug toolbar after changing code in `extension.js`. 22 | * You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes. 23 | 24 | ## Explore the API 25 | 26 | * You can open the full set of our API when you open the file `node_modules/@types/vscode/index.d.ts`. 27 | 28 | ## Run tests 29 | 30 | * Open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Extension Tests`. 31 | * Press `F5` to run the tests in a new window with your extension loaded. 32 | * See the output of the test result in the debug console. 33 | * Make changes to `src/test/suite/extension.test.js` or create new test files inside the `test/suite` folder. 34 | * The provided test runner will only consider files matching the name pattern `**.test.ts`. 35 | * You can create folders inside the `test` folder to structure your tests any way you want. 36 | ## Go further 37 | 38 | * [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VSCode extension marketplace. 39 | * Automate builds by setting up [Continuous Integration](https://code.visualstudio.com/api/working-with-extensions/continuous-integration). 40 | -------------------------------------------------------------------------------- /extension.js: -------------------------------------------------------------------------------- 1 | const vscode = require('vscode'); 2 | const request = require('request-promise'); 3 | const cheerio = require('cheerio'); 4 | const clipboardy = require('clipboardy'); 5 | 6 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; 7 | // to ignore ssl certificate error on stackoverflow 8 | 9 | let last_link; 10 | 11 | function activate(context) { 12 | let disposable = vscode.commands.registerCommand('sofa.getcode', function () { 13 | let language = vscode.window.activeTextEditor.document.languageId 14 | let activeLine = vscode.window.activeTextEditor.selection.active.line; 15 | vscode.window.activeTextEditor.selection.active.line = activeLine + 1; 16 | 17 | vscode.commands.executeCommand("editor.action.clipboardCopyAction").then(() => { 18 | vscode.commands.executeCommand("editor.action.addCommentLine"); 19 | vscode.commands.executeCommand("editor.action.insertLineAfter"); 20 | vscode.window.activeTextEditor.selection.active.line = activeLine + 1; 21 | vscode.env.clipboard.readText().then((text) => { 22 | console.log(text); 23 | let query = text.trim(); 24 | 25 | if(query == ""){ 26 | if(typeof last_link !== "undefined") 27 | vscode.env.openExternal(vscode.Uri.parse(last_link)); 28 | return; 29 | } 30 | 31 | query = language + "+" + query.replaceAll(" ", "+") + "+stackoverflow"; 32 | let url = "https://www.google.com/search?q=" + query; 33 | console.log(url); 34 | search(url); 35 | }); 36 | }); 37 | }); 38 | 39 | context.subscriptions.push(disposable); 40 | } 41 | 42 | function search(url) { 43 | request(url).then(html => { 44 | let $ = cheerio.load(html); 45 | let links = []; 46 | 47 | $("h3").each((index, element) => { 48 | let link = element.parent.attribs.href 49 | link = link.split("=")[1].split("&sa")[0] 50 | if(link.includes("stackoverflow.com")){ 51 | links.push(link); 52 | } 53 | }); 54 | 55 | if(links.length > 0) 56 | getcodes(links[0]); 57 | else 58 | vscode.window.showInformationMessage("Sofa could not find it!"); 59 | }) 60 | } 61 | 62 | function getcodes(link) { 63 | 64 | last_link = link; 65 | 66 | request(link).then(function (html) { 67 | let $ = cheerio.load(html); 68 | let list = []; 69 | 70 | $(".answercell").find("pre > code").each(function (index, element) { 71 | list.push($(element).text().replace(">>> ", "")) 72 | }) 73 | 74 | if (list.length > 1) { 75 | vscode.window.showQuickPick(list).then(result => { 76 | console.log(result); 77 | clipboardy.writeSync(result + "\n"); 78 | vscode.commands.executeCommand("editor.action.clipboardPasteAction"); 79 | }); 80 | 81 | } else if (list.length == 1) { 82 | clipboardy.writeSync(list[0] + "\n"); 83 | vscode.commands.executeCommand("editor.action.clipboardPasteAction"); 84 | } else { 85 | vscode.window.showInformationMessage("Sofa could not find it!"); 86 | } 87 | }).catch(function (error) { 88 | vscode.window.showInformationMessage("Request error!", error); 89 | }); 90 | } 91 | 92 | function deactivate() {} 93 | 94 | module.exports = { 95 | activate, 96 | deactivate 97 | } 98 | --------------------------------------------------------------------------------