├── .gitignore ├── media ├── icon.png ├── doc-compile.png ├── doc-addplugin.png ├── doc-addplugin2.png ├── doc-selectversion.png └── remix.svg ├── .vscodeignore ├── tslint.json ├── tsconfig.json ├── src ├── utils.ts ├── plugins │ ├── ext_api_plugin.ts │ ├── web3provider.ts │ ├── compile_worker.ts │ ├── wallet.ts │ ├── native_solidity_plugin.ts │ └── udapp.ts ├── optionInputs.ts ├── rmxPlugins.ts ├── types.ts └── extension.ts ├── resources ├── light │ ├── document.svg │ ├── edit.svg │ ├── folder.svg │ ├── boolean.svg │ ├── dependency.svg │ ├── refresh.svg │ ├── number.svg │ ├── string.svg │ └── settings.svg └── dark │ ├── edit.svg │ ├── document.svg │ ├── folder.svg │ ├── boolean.svg │ ├── dependency.svg │ ├── refresh.svg │ ├── number.svg │ ├── string.svg │ └── settings.svg ├── .vscode ├── settings.json ├── tasks.json └── launch.json ├── .github └── workflows │ └── codeql-analysis.yml ├── package.json ├── README.md └── vscode.proposed.d.ts /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | error.log 4 | *.vsix -------------------------------------------------------------------------------- /media/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remix-project-org/remix-vscode/HEAD/media/icon.png -------------------------------------------------------------------------------- /media/doc-compile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remix-project-org/remix-vscode/HEAD/media/doc-compile.png -------------------------------------------------------------------------------- /media/doc-addplugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remix-project-org/remix-vscode/HEAD/media/doc-addplugin.png -------------------------------------------------------------------------------- /media/doc-addplugin2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remix-project-org/remix-vscode/HEAD/media/doc-addplugin2.png -------------------------------------------------------------------------------- /media/doc-selectversion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remix-project-org/remix-vscode/HEAD/media/doc-selectversion.png -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | test/** 5 | src/** 6 | **/*.map 7 | .gitignore 8 | tsconfig.json 9 | .github/ 10 | .git/ -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["tslint-config-prettier"], 3 | "rules": { 4 | "prettier": true, 5 | "indent": [ 6 | true, 7 | "tabs" 8 | ], 9 | "semicolon": [ 10 | true, 11 | "always" 12 | ], 13 | "max-line-length": { 14 | "options": [160] 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "sourceMap": true, 7 | "rootDir": "src", 8 | "esModuleInterop": true 9 | }, 10 | "exclude": [ 11 | "node_modules", 12 | ".vscode-test", 13 | "vscode.proposed.d.ts" 14 | ] 15 | } -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import { PluginInfo } from './types'; 2 | import { ViewColumn } from "vscode"; 3 | 4 | export function ToViewColumn(pluginData: PluginInfo) { 5 | switch (pluginData.location) { 6 | case "sidePanel": 7 | return ViewColumn.Two; 8 | default: 9 | return ViewColumn.Active; 10 | } 11 | } 12 | 13 | export function GetPluginData(pluginId: string, data:PluginInfo[]): PluginInfo { 14 | const p: PluginInfo[] = data.filter(i => { 15 | return i.name == pluginId; 16 | }); 17 | return p[0]; 18 | } -------------------------------------------------------------------------------- /resources/light/document.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/edit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/edit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | "typescript.tsdk": "./node_modules/typescript/lib", // we want to use the TS server from our node_modules folder to control its version 10 | "typescript.tsc.autoDetect": "off", 11 | "editor.insertSpaces": false, 12 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /resources/dark/document.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/folder.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/folder.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/boolean.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/dependency.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/boolean.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/dependency.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/refresh.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/refresh.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/plugins/ext_api_plugin.ts: -------------------------------------------------------------------------------- 1 | import { extensions, window } from "vscode"; 2 | import { CommandPlugin } from "@remixproject/engine-vscode"; 3 | 4 | const profile = { 5 | name: "vscodeExtAPI", 6 | displayName: "Extension API connector", 7 | description: "Connects VScode Extension API with remix plugins.", 8 | kind: "connector", 9 | permission: true, 10 | documentation: 11 | "", 12 | version: "0.0.1", 13 | methods: ["executeCommand"], 14 | }; 15 | export class ExtAPIPlugin extends CommandPlugin { 16 | constructor() { 17 | super(profile); 18 | } 19 | async executeCommand(extName: string, cmdName: string, payload?: any[]) { 20 | try { 21 | const ext = extensions.getExtension(extName); 22 | await ext.activate(); 23 | const extAPI = ext.exports; 24 | extAPI[cmdName](...payload || []); 25 | } catch (e) { 26 | // extension can not be activated or any other error 27 | console.error(e) 28 | window.showErrorMessage(`${extName} extension is not installed or disabled.`) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | { 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "name": "Launch Extension", 7 | "type": "extensionHost", 8 | "request": "launch", 9 | "runtimeExecutable": "${execPath}", 10 | "args": [ 11 | "--extensionDevelopmentPath=${workspaceRoot}" 12 | ], 13 | "stopOnEntry": false, 14 | "sourceMaps": true, 15 | "outFiles": [ 16 | "${workspaceRoot}/out/src/**/*.js" 17 | ], 18 | "preLaunchTask": "npm: watch" 19 | }, 20 | { 21 | "type": "node", 22 | "request": "attach", 23 | "name": "Attach to Extension Host", 24 | "protocol": "inspector", 25 | "port": 5870, 26 | "restart": true, 27 | "outFiles": [ 28 | "${workspaceRoot}/out/src" 29 | ] 30 | } 31 | ] 32 | } -------------------------------------------------------------------------------- /src/plugins/web3provider.ts: -------------------------------------------------------------------------------- 1 | import { Plugin } from "@remixproject/engine"; 2 | 3 | export const profile = { 4 | name: "web3Provider", 5 | displayName: "Global Web3 Provider", 6 | description: 7 | "Represent the current web3 provider used by the app at global scope", 8 | methods: ["sendAsync", "setProvider", "disconnect"], 9 | version: "0.0.1", 10 | kind: "provider", 11 | }; 12 | 13 | export class Web3ProviderModule extends Plugin { 14 | web3Provider: any; 15 | constructor() { 16 | super(profile); 17 | } 18 | 19 | setProvider(provider: any){ 20 | this.web3Provider = provider 21 | } 22 | 23 | disconnect(){ 24 | try{ 25 | delete this.web3Provider 26 | }catch(e){ 27 | console.log(e) 28 | } 29 | } 30 | 31 | sendAsync(payload: any) { 32 | return new Promise((resolve, reject) => { 33 | this.web3Provider[ 34 | this.web3Provider.sendAsync ? "sendAsync" : "send" 35 | ](payload, (error, message) => { 36 | if (error) return reject(error); 37 | resolve(message); 38 | }); 39 | }); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/optionInputs.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Shows a pick list using window.showQuickPick(). 3 | */ 4 | import { commands, ExtensionContext } from 'vscode'; 5 | 6 | /** 7 | * Activate a plugin 8 | * @param context vscode Execution context 9 | * @param pluginId plugin ID mentioned as plugin ViewItem 10 | */ 11 | export async function pluginActivate(context: ExtensionContext, pluginId: string) { 12 | commands.executeCommand('extension.activateRmxPlugin', pluginId) 13 | } 14 | 15 | /** 16 | * Deactivate a plugin with given id 17 | * @param context vscode Execution context 18 | * @param pluginId plugin ID mentioned as plugin ViewItem 19 | */ 20 | export async function pluginDeactivate(context: ExtensionContext, pluginId: string) { 21 | commands.executeCommand('extension.deActivateRmxPlugin', pluginId); 22 | } 23 | 24 | /** 25 | * Uninstall a plugin with given id 26 | * @param context vscode Execution context 27 | * @param pluginId plugin ID mentioned as plugin ViewItem 28 | */ 29 | export async function pluginUninstall(context: ExtensionContext, pluginId: string) { 30 | commands.executeCommand('rmxPlugins.uninstallRmxPlugin', pluginId); 31 | } 32 | 33 | /** 34 | * Open Documentation 35 | * @param context vscode Execution context 36 | * @param pluginId plugin ID mentioned as plugin ViewItem 37 | */ 38 | export async function pluginDocumentation(context: ExtensionContext, pluginId: string) { 39 | commands.executeCommand('rmxPlugins.openDocumentation', pluginId); 40 | } -------------------------------------------------------------------------------- /media/remix.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | remix_logo 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | schedule: 21 | - cron: '22 6 * * 0' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | language: [ 'javascript' ] 32 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 33 | # Learn more: 34 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 35 | 36 | steps: 37 | - name: Checkout repository 38 | uses: actions/checkout@v2 39 | 40 | # Initializes the CodeQL tools for scanning. 41 | - name: Initialize CodeQL 42 | uses: github/codeql-action/init@v1 43 | with: 44 | languages: ${{ matrix.language }} 45 | # If you wish to specify custom queries, you can do so here or in a config file. 46 | # By default, queries listed here will override any specified in a config file. 47 | # Prefix the list here with "+" to use these queries and those in the config file. 48 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 49 | 50 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 51 | # If this step fails, then you should remove it and run the build manually (see below) 52 | - name: Autobuild 53 | uses: github/codeql-action/autobuild@v1 54 | 55 | # ℹ️ Command-line programs to run using the OS shell. 56 | # 📚 https://git.io/JvXDl 57 | 58 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 59 | # and modify them (or add more) to build your code if your project 60 | # uses a compiled language 61 | 62 | #- run: | 63 | # make bootstrap 64 | # make release 65 | 66 | - name: Perform CodeQL Analysis 67 | uses: github/codeql-action/analyze@v1 68 | -------------------------------------------------------------------------------- /src/rmxPlugins.ts: -------------------------------------------------------------------------------- 1 | import {TreeItem, TreeDataProvider, EventEmitter, Event, TreeItemCollapsibleState, Command} from "vscode"; 2 | import { Uri } from "vscode"; 3 | import { PluginInfo } from "./types"; 4 | 5 | export class RmxPluginsProvider implements TreeDataProvider { 6 | private _onDidChangeTreeData: EventEmitter = new EventEmitter(); 7 | readonly onDidChangeTreeData: Event = this._onDidChangeTreeData.event; 8 | private data: PluginInfo[]; 9 | private defaultData: PluginInfo[]; 10 | constructor(private workspaceRoot: string) { 11 | this.data = []; 12 | } 13 | 14 | setDefaultData(data:any[]){ 15 | this.defaultData = data 16 | this.refresh() 17 | } 18 | 19 | getData(){ 20 | return this.data 21 | } 22 | 23 | add(data: PluginInfo): void { 24 | this.data.push(data); 25 | this._onDidChangeTreeData.fire(null); 26 | } 27 | 28 | remove(id: string):void { 29 | this.data = this.data.filter((plugin: PluginInfo)=>{ 30 | return plugin.name != id 31 | }) 32 | this._onDidChangeTreeData.fire(null); 33 | } 34 | 35 | refresh():void { 36 | this.data = [... this.defaultData.sort((a,b) => (a.displayName < b.displayName)?-1:1)] 37 | this._onDidChangeTreeData.fire(null); 38 | } 39 | 40 | getTreeItem(element: PluginInterface): TreeItem { 41 | return element; 42 | } 43 | 44 | getChildren(element?: PluginInterface): Thenable { 45 | return this.getRmxPlugins().then((children) => { 46 | return Promise.resolve(children); 47 | }); 48 | } 49 | private toPlugin = (pluginName: string, id: string, text: string, icon: string): PluginInterface => { 50 | return new PluginInterface( 51 | pluginName, 52 | id, 53 | text, 54 | TreeItemCollapsibleState.None, 55 | { 56 | command: "rmxPlugins.showPluginOptions", 57 | title: pluginName, 58 | arguments: [id], 59 | }, 60 | Uri.parse(icon) 61 | ); 62 | }; 63 | 64 | private async getRmxPlugins(): Promise { 65 | try { 66 | const plugins = this.data 67 | ? this.data.map((plugin) => this.toPlugin(plugin.displayName, plugin.name, plugin.description, plugin.icon)) 68 | : []; 69 | return Promise.resolve(plugins); 70 | } catch (error) { 71 | throw error; 72 | } 73 | } 74 | } 75 | 76 | export class PluginInterface extends TreeItem { 77 | constructor( 78 | public readonly label: string, 79 | public readonly id: string, 80 | private text: string, 81 | public readonly collapsibleState: TreeItemCollapsibleState, 82 | public readonly command?: Command, 83 | public readonly iconURI?: Uri 84 | ) { 85 | super(label, collapsibleState); 86 | } 87 | tooltip = `${this.label}-${this.text}`; 88 | description = this.text; 89 | iconPath = { 90 | light: this.iconURI, 91 | dark: this.iconURI, 92 | }; 93 | contextValue = "options"; 94 | } 95 | -------------------------------------------------------------------------------- /resources/dark/number.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/number.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/string.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/string.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/plugins/compile_worker.ts: -------------------------------------------------------------------------------- 1 | import * as solc from "solc"; 2 | import * as path from "path"; 3 | import * as fs from "fs"; 4 | import axios from "axios"; 5 | import { RemixURLResolver } from "@remix-project/remix-url-resolver"; 6 | 7 | function handleLocal(pathString: string, root: string) { 8 | try { 9 | const o = { encoding: "UTF-8" }; 10 | // hack for compiler imports to work (do not change) 11 | const p = path.resolve(root, pathString); 12 | 13 | const content = fs.readFileSync(p, o); 14 | return content; 15 | } catch (error) { 16 | // @ts-ignore 17 | process.send({ error }); 18 | throw error; 19 | } 20 | } 21 | 22 | function findImports(path: any, root: string) { 23 | // TODO: We need current solc file path here for relative local import 24 | // @ts-ignore 25 | process.send({ processMessage: "importing file: " + path }); 26 | const FSHandler = [ 27 | { 28 | type: "local", 29 | match: (url: string) => { 30 | return /(^(?!(?:http:\/\/)|(?:https:\/\/)?(?:www.)?(?:github.com)))(^\/*[\w+-_/]*\/)*?(\w+\.sol)/g.exec(url); 31 | }, 32 | handle: (match: Array) => { 33 | return handleLocal(match[0], root); 34 | } 35 | } 36 | ]; 37 | const urlResolver = new RemixURLResolver(); 38 | // this section usually executes after solc returns error file not found 39 | urlResolver.resolve(path, FSHandler) 40 | .then((data: any) => { 41 | // @ts-ignore 42 | process.send({ data, path }); 43 | }) 44 | .catch((e: Error) => { 45 | // @ts-ignore 46 | process.send({ error: e }); 47 | }); 48 | return { 'error': 'Deferred import' }; 49 | } 50 | 51 | process.on("message", async m => { 52 | if (m.command === "compile") { 53 | const vnReg = /(^[0-9].[0-9].[0-9]\+commit\..*?)+(\.)/g; 54 | const vnRegArr = vnReg.exec(solc.version()); 55 | // @ts-ignore 56 | const vn = 'v' + (vnRegArr ? vnRegArr[1] : ''); 57 | const input = m.payload; 58 | if (m.version === vn || m.version === 'latest') { 59 | try { 60 | console.log("compiling with local version: ", solc.version()); 61 | const output = await solc.compile(JSON.stringify(input), { import: (path) => findImports(path, m.root) }); 62 | // @ts-ignore 63 | process.send({ compiled: output, version:solc.version() }); 64 | // we should not exit process here as findImports still might be running 65 | } catch (e) { 66 | console.error(e); 67 | // @ts-ignore 68 | process.send({ error: e }); 69 | // @ts-ignore 70 | process.exit(1); 71 | } 72 | } else { 73 | const v = m.version.replace('soljson-', '').replace('.js', ''); 74 | console.log("Loading remote version " + v + "..."); 75 | solc.loadRemoteVersion(v, async (err: Error, newSolc: any) => { 76 | if (err) { 77 | console.error(err); 78 | // @ts-ignore 79 | process.send({ error: e }); 80 | } else { 81 | console.log("compiling with remote version ", newSolc.version()); 82 | try { 83 | const output = await newSolc.compile(JSON.stringify(input), { import: (path) => findImports(path, m.root) }); 84 | // @ts-ignore 85 | process.send({ compiled: output, version:newSolc.version() }); 86 | } catch (e) { 87 | console.error(e); 88 | // @ts-ignore 89 | process.send({ error: e }); 90 | // @ts-ignore 91 | process.exit(1); 92 | } 93 | } 94 | }); 95 | } 96 | } 97 | if (m.command === "fetch_compiler_verison") { 98 | axios 99 | .get("https://solc-bin.ethereum.org/bin/list.json") 100 | .then((res: any) => { 101 | // @ts-ignore 102 | process.send({ versions: res.data.releases }); 103 | }) 104 | .catch((e: Error) => { 105 | // @ts-ignore 106 | process.send({ error: e }); 107 | // @ts-ignore 108 | process.exit(1); 109 | }); 110 | } 111 | }); -------------------------------------------------------------------------------- /src/plugins/wallet.ts: -------------------------------------------------------------------------------- 1 | import { Plugin } from "@remixproject/engine"; 2 | import WalletConnectProvider from "@walletconnect/web3-provider"; 3 | import { OutputChannel, window } from "vscode"; 4 | 5 | const profile = { 6 | name: "walletconnect", 7 | displayName: "walletconnect", 8 | description: "", 9 | icon: "assets/img/fileManager.webp", 10 | version: "0.0.1", 11 | methods: ["connect", "disconnect", "sendAsync"], 12 | events:["connect", "disconnect", "displayUri", "accountsChanged", "chainChanged"], 13 | kind: "provider", 14 | }; 15 | export default class WalletConnect extends Plugin { 16 | private outputChannel: OutputChannel; 17 | provider: WalletConnectProvider; 18 | constructor() { 19 | super(profile); 20 | this.outputChannel = window.createOutputChannel("Remix IDE"); 21 | } 22 | 23 | async createProvider() { 24 | this.provider = new WalletConnectProvider({ 25 | infuraId: "83d4d660ce3546299cbe048ed95b6fad", 26 | bridge: "https://wallet-connect-bridge.dyn.plugin.remixproject.org:8080", 27 | qrcode: false, 28 | clientMeta: { 29 | description: "Remix Extension", 30 | url: "https://remix.ethereum.org", 31 | icons: ["https://walletconnect.org/walletconnect-logo.png"], 32 | name: "Remix Extension", 33 | }, 34 | }); 35 | await this.call("web3Provider", "setProvider", this.provider); 36 | await this.setListeners() 37 | } 38 | 39 | async setListeners() { 40 | this.emit("disconnect") 41 | this.provider.connector.on("display_uri", (err, payload) => { 42 | const uri = payload.params[0]; 43 | this.print(`Connect to wallet with URI: ${uri}`) 44 | this.call("vscodeudapp" as any, "qr", uri); 45 | }); 46 | 47 | // Subscribe to accounts change 48 | this.provider.on("accountsChanged", (accounts: string[]) => { 49 | for(const account of accounts){ 50 | this.print(`Wallet account : ${account}`) 51 | } 52 | this.emit('accountsChanged', accounts || []) 53 | this.call("udapp" as any, "getAccounts"); 54 | //this.call("walletconnect" as any, "dismiss"); 55 | //this.provider.disconnect(); 56 | }); 57 | 58 | // Subscribe to chainId change 59 | this.provider.on("chainChanged", (chainId: number) => { 60 | this.print(`Wallet chain changed : ${chainId}`) 61 | this.emit("chainChanged", chainId) 62 | }); 63 | 64 | // Subscribe to session disconnection 65 | this.provider.on("disconnect", (code: number, reason: string) => { 66 | this.emit("disconnect") 67 | this.print(`Disconnected from wallet`) 68 | console.log(code, reason); 69 | }); 70 | } 71 | 72 | async connect() { 73 | await this.createProvider() 74 | 75 | this.provider.enable(); 76 | this.print(`Connect to wallet`) 77 | //this.emit("connect") 78 | } 79 | 80 | async disconnect(){ 81 | await this.call("web3Provider", "disconnect"); 82 | this.print(`Disconnected from wallet`) 83 | this.emit("disconnect") 84 | } 85 | 86 | // terminal printing 87 | private getNow(): string { 88 | const date = new Date(Date.now()); 89 | return date.toLocaleTimeString(); 90 | } 91 | 92 | private print(m: string) { 93 | const now = this.getNow(); 94 | this.outputChannel.appendLine(`[${now}]: ${m}`); 95 | this.outputChannel.show(); 96 | } 97 | 98 | sendAsync = (data) => { 99 | return new Promise((resolve, reject) => { 100 | if (this.provider) { 101 | this.provider.sendAsync(data, (error, message) => { 102 | if (error) return reject(error); 103 | resolve(message); 104 | }); 105 | } else { 106 | return reject("Provider not loaded"); 107 | } 108 | }); 109 | }; 110 | } 111 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ethereum-remix", 3 | "displayName": "Ethereum Remix", 4 | "description": "Ethereum Remix plugins in VSCode", 5 | "version": "0.0.8", 6 | "publisher": "RemixProject", 7 | "enableProposedApi": true, 8 | "icon": "media/icon.png", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/ethereum/remix-vscode" 12 | }, 13 | "engines": { 14 | "vscode": "^1.47.0" 15 | }, 16 | "categories": [ 17 | "Extension Packs" 18 | ], 19 | "keywords": [ 20 | "ethereum", 21 | "Remix" 22 | ], 23 | "activationEvents": [ 24 | "onView:rmxPlugins", 25 | "onCommand:remix.compile" 26 | ], 27 | "main": "./out/extension.js", 28 | "contributes": { 29 | "viewsWelcome": [ 30 | { 31 | "view": "remix-plugins", 32 | "contents": "No remix plugins found [learn more](https://remix.ethereum.org/).\n[Add Plugin](command:rmxPlugins.addEntry)", 33 | "when": "view == remix-plugins" 34 | } 35 | ], 36 | "viewsContainers": { 37 | "activitybar": [ 38 | { 39 | "id": "remix-plugins", 40 | "title": "Ethereum Remix", 41 | "icon": "media/remix.svg" 42 | } 43 | ] 44 | }, 45 | "views": { 46 | "remix-plugins": [ 47 | { 48 | "id": "rmxPlugins", 49 | "name": "Plugins", 50 | "icon": "media/remix.svg", 51 | "contextualTitle": "Remix" 52 | } 53 | ] 54 | }, 55 | "commands": [ 56 | { 57 | "command": "rmxPlugins.refreshEntry", 58 | "title": "Refresh", 59 | "icon": { 60 | "light": "resources/light/refresh.svg", 61 | "dark": "resources/dark/refresh.svg" 62 | }, 63 | "category": "Remix" 64 | }, 65 | { 66 | "command": "rmxPlugins.addEntry", 67 | "title": "Add plugin", 68 | "category": "Remix" 69 | }, 70 | { 71 | "command": "rmxPlugins.deploy", 72 | "title": "Deploy compiled contract to Wallet Connect", 73 | "category": "Remix" 74 | }, 75 | { 76 | "command": "rmxPlugins.walletConnect", 77 | "title": "Connect to mobile Wallet", 78 | "category": "Remix" 79 | }, 80 | { 81 | "command": "rmxPlugins.walletDisconnect", 82 | "title": "Disconnect from mobile Wallet", 83 | "category": "Remix" 84 | }, 85 | { 86 | "command": "rmxPlugins.showPluginOptions", 87 | "title": "Options", 88 | "icon": { 89 | "light": "resources/light/settings.svg", 90 | "dark": "resources/dark/settings.svg" 91 | }, 92 | "category": "Remix" 93 | }, 94 | { 95 | "command": "rmxPlugins.compile.solidity", 96 | "title": "Compile with Solidity extension", 97 | "category": "Remix" 98 | }, 99 | { 100 | "command": "rmxPlugins.compile", 101 | "title": "Compile with Remix Solidity/YUL compiler", 102 | "category": "Remix" 103 | }, 104 | { 105 | "command": "rmxPlugins.versionSelector", 106 | "title": "Remix compiler version selector", 107 | "category": "Remix" 108 | }, 109 | { 110 | "command": "rmxPlugins.optimizerSelector", 111 | "title": "Yul optimizer", 112 | "category": "Remix" 113 | }, 114 | { 115 | "command": "rmxPlugins.languageSelector", 116 | "title": "Language selector", 117 | "category": "Remix" 118 | } 119 | ], 120 | "keybindings": [ 121 | { 122 | "key": "ctrl+alt+c", 123 | "command": "rmxPlugins.compile" 124 | }, 125 | { 126 | "key": "ctrl+alt+v", 127 | "command": "rmxPlugins.versionSelector" 128 | } 129 | ], 130 | "menus": { 131 | "view/title": [ 132 | { 133 | "command": "rmxPlugins.refreshEntry", 134 | "when": "view == rmxPlugins", 135 | "group": "navigation" 136 | }, 137 | { 138 | "command": "rmxPlugins.addEntry", 139 | "when": "view == rmxPlugins" 140 | } 141 | ], 142 | "view/item/context": [ 143 | { 144 | "command": "rmxPlugins.showPluginOptions", 145 | "when": "view == rmxPlugins && viewItem == options", 146 | "group": "inline" 147 | } 148 | ] 149 | } 150 | }, 151 | "scripts": { 152 | "vscode:prepublish": "npm run -S esbuild-base -- --minify", 153 | "esbuild-base": "esbuild ./src/extension.ts --bundle --outfile=out/main.js --external:vscode --external:electron --format=cjs --platform=node", 154 | "esbuild": "npm run -S esbuild-base -- --sourcemap", 155 | "esbuild-watch": "npm run -S esbuild-base -- --sourcemap --watch", 156 | "test-compile": "tsc -p ./", 157 | "watch": "tsc -watch -p ./" 158 | }, 159 | "devDependencies": { 160 | "@types/mkdirp": "^0.5.2", 161 | "@types/node": "^10.12.21", 162 | "@types/rimraf": "^2.0.2", 163 | "@types/vscode": "^1.47.0", 164 | "esbuild": "^0.12.3", 165 | "tslint": "^5.12.1", 166 | "typescript": "^3.9.7" 167 | }, 168 | "dependencies": { 169 | "@remix-project/remix-url-resolver": "^0.0.18", 170 | "@remixproject/engine": "^0.3.17", 171 | "@remixproject/engine-vscode": "^0.3.17", 172 | "@remixproject/plugin": "^0.3.17", 173 | "@remixproject/plugin-api": "^0.3.17", 174 | "@remixproject/plugin-utils": "^0.3.17", 175 | "@remixproject/plugin-vscode": "^0.3.17", 176 | "@types/semver": "^7.3.6", 177 | "@walletconnect/web3-provider": "^1.4.1", 178 | "axios": "^0.21.1", 179 | "ftp": "^0.3.10", 180 | "ipfs-http-client": "47.0.1", 181 | "isomorphic-git": "^1.8.1", 182 | "jsonc-parser": "^0.4.2", 183 | "mkdirp": "^0.5.1", 184 | "query-string": "^7.0.0", 185 | "remix-url-resolver": "^0.0.16", 186 | "rimraf": "^2.6.2", 187 | "semver": "^7.3.5", 188 | "solc": "^0.7.0", 189 | "tslint-config-prettier": "^1.18.0", 190 | "web3": "^1.3.5" 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATED 2 | 3 | This extension has been removed from the VSCODE marketplace and will be replaced by a dedicated stand-alone dektop application. 4 | 5 | # Ethereum Remix Project extension for Visual Studio Code 6 | This project brings Remix plugins to Visual Studio Code.
Remix plugins can perform a variety of tasks such as verifying contracts, linting, generating documentation, running tutorials, compiling, debugging and much more.
The Remix Plugin API allows plugins that run in Remix to run in Visual Studio Code too.
7 | It allows developers to access and interact with the file system, components, extensions and other Remix plugins without actually having to create a different code base. 8 | 9 | For more info on what Remix is and what plugins do please visit our [Remix IDE](https://remix.ethereum.org/) and [Remix Project website](https://remix-project.org/) 10 | 11 | - [Ethereum Remix Project extension for Visual Studio Code](#ethereum-remix-project-extension-for-visual-studio-code) 12 | - [A beta release.](#a-beta-release) 13 | - [Installation from the Visual Studio Code Marketplace](#installation-from-the-visual-studio-code-marketplace) 14 | - [Requirements](#requirements) 15 | - [The plugins](#the-plugins) 16 | - [Select, activate, deactivate a plugin](#select-activate-deactivate-a-plugin) 17 | - [Load a development plugin](#load-a-development-plugin) 18 | - [Compiling Solidity & Yul](#compiling-solidity--yul) 19 | - [Deploying contracts & Wallet Connect](#deploying-contracts--wallet-connect) 20 | - [Contributing and development](#contributing-and-development) 21 | 22 | ## A beta release. 23 | 24 | As we are continuing development of Remix and the Plugin API more functionalities will open up.
So at first it might feel limited. In this beta release we show you a glimpse of what the possibilities are, inviting everybody to contribute & comment. 25 | 26 | ## Installation from the Visual Studio Code Marketplace 27 | [Install from Visual Studio Code Marketplace](https://marketplace.visualstudio.com/items?itemName=RemixProject.ethereum-remix) 28 | 29 | 30 | ## Requirements 31 | 32 | Before being able to use the extension, you need to have at least a folder opened or a workspace. 33 | 34 | ## The plugins 35 | 36 | The extension loads with a default set of plugins. As time goes on we will add more plugins.
You can also suggest and create plugins using our plugin system and API. Feel free to contact us on our gitter channels. 37 | [Remix](https://gitter.im/ethereum/remix) & [Remix Dev](https://gitter.im/ethereum/remix-dev) and read up on the basics 38 | on [Read the docs](https://remix-plugins-directory.readthedocs.io/en/latest/) 39 | ## Select, activate, deactivate a plugin 40 | 41 | Before you can use a plugin, it needs to be activated. Activation means you open it and it will run. 42 | 43 | ![Activate, deactivate remix plugin](https://j.gifs.com/2xn4KP.gif) 44 | 45 | ## Load a development plugin 46 | 47 | As you develop your own plugin or you want to try out a plugin by using a custom URL you can easily add it to the list. 48 | 49 | * Click on 'add plugin' from the `Remix Plugins More actions` menu on top right 50 |

51 | 52 |

53 | 54 | * The plugin system requires a valid JSON string that contains the information needed. 55 | Add your `JSON` plugin info into the input box. An example is given below. 56 | 57 | The URL can contain any URL or a local file.
Make sure to give your plugin a unique name. You can't have two plugins with the same name. 58 | ```json 59 | { 60 | "name": "dev-plugin", 61 | "displayName": "My first plugin", 62 | "methods": [], 63 | "version": "0.0.1-dev", 64 | "url": "http://localhost:3000", 65 | "description": "A great plugin for the Remix project", 66 | "icon": "https://raw.githubusercontent.com/protofire/solhint/master/solhint-icon.png", 67 | "location": "sidePanel" 68 | } 69 | ``` 70 | 71 |

72 | 73 |

74 | 75 | ## Compiling Solidity & Yul 76 | 77 | Our extension provides some basic functionality to get started with Solidity and Yul development.
Quite a few remix plugins use the results of compilation to generate content for you. So this is extremely useful. 78 | 79 | At this time you can compile your files using two methods: 80 | * the internal Remix compiler 81 | * the compiler provided by the Solidity Extension (https://marketplace.visualstudio.com/items?itemName=JuanBlanco.solidity) 82 | 83 | Compiling with the Solidity extension provides some extra features that are included in the Solidity extension. 84 | 85 | *Warning: when you have the Solidity extension installed you usually hit F5 to compile.
This will not work (yet) in conjunction with Remix.
You 86 | should compile with the 'Compile with Solidity extension'. This way you get the benefits of both extensions.* 87 | 88 | *You should have a sol file actively selected when compiling. If you have the plugin in focus it won't be able to tell which file you want to compile.* 89 | 90 | To compile use the command palette ( Shift+cmd+p ) and type in REMIX. You will see both options there. 91 | 92 |

93 | 94 |

95 | 96 | You can also change the compiler version of both compilers from the command palette. 97 | 98 |

99 | 100 |

101 | 102 | You can change to Yul development the same way using the language selector in the command palette. 103 | 104 | ## Deploying contracts & Wallet Connect 105 | 106 | 107 | ## Contributing and development 108 | 109 | You can checkout our code on [github](https://github.com/ethereum/remix-vscode) 110 | * Make sure the extension from the marketplace is not installed, running both versions will not work. 111 | * Open up the repo in Visual Studio Code. 112 | * run Yarn 113 | * hit F5 114 | * A new Visual Studio Code window will open where the extension is running. 115 | -------------------------------------------------------------------------------- /resources/dark/settings.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /resources/light/settings.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/plugins/native_solidity_plugin.ts: -------------------------------------------------------------------------------- 1 | import { CommandPlugin } from "@remixproject/engine-vscode"; 2 | import { window, OutputChannel, workspace, commands } from "vscode"; 3 | import { fork, ChildProcess } from "child_process"; 4 | import * as path from "path"; 5 | import { ISources, CompilerInput, CompilerInputOptions } from "../types"; 6 | import { relativePath } from '@remixproject/engine-vscode/util/path' 7 | const profile = { 8 | name: 'solidity', 9 | displayName: 'Solidity compiler', 10 | description: 'Compile solidity contracts', 11 | kind: 'compiler', 12 | permission: true, 13 | location: 'sidePanel', 14 | documentation: 'https://remix-ide.readthedocs.io/en/latest/solidity_editor.html', 15 | version: '0.0.1', 16 | methods: ['getCompilationResult', 'compile', 'compileWithParameters', 'setCompilerConfig'] 17 | }; 18 | 19 | interface ICompilationResult { 20 | source: { 21 | target: string; 22 | sources: ISources; 23 | }; 24 | data: any; 25 | } 26 | 27 | export default class NativeSolcPlugin extends CommandPlugin { 28 | private version: string = 'latest'; 29 | private versions: Array; 30 | private outputChannel: OutputChannel; 31 | private compilationResult: ICompilationResult; 32 | constructor() { 33 | super(profile); 34 | this.outputChannel = window.createOutputChannel("Remix IDE"); 35 | this.loadSolidityVersions(); 36 | } 37 | getVersion() { 38 | return 0.1; 39 | } 40 | private createWorker(): ChildProcess { 41 | // enable --inspect for debug 42 | // return fork(path.join(__dirname, "compile_worker.js"), [], { 43 | // execArgv: ["--inspect=" + (process.debugPort + 1)] 44 | // }); 45 | return fork(path.join(__dirname, "compile_worker.js")); 46 | } 47 | private getNow(): string { 48 | const date = new Date(Date.now()); 49 | return date.toLocaleTimeString(); 50 | } 51 | private print(m: string) { 52 | const now = this.getNow(); 53 | this.outputChannel.appendLine(`[${now}]: ${m}`); 54 | this.outputChannel.show(); 55 | } 56 | async compile(_version: string, opts: CompilerInputOptions) { 57 | this.print("Compilation started!") 58 | this.version = _version in this.versions ? this.versions[_version] : _version; 59 | const fileName = await this.call('fileManager', 'getCurrentFile') 60 | this.print(`Compiling ${fileName} ...`); 61 | const editorContent = window.activeTextEditor ? window.activeTextEditor.document.getText() : undefined; 62 | const sources: ISources = {}; 63 | if (fileName) { 64 | sources[fileName] = { 65 | content: editorContent, 66 | }; 67 | } 68 | const solcWorker = this.createWorker(); 69 | console.log(`Solidity compiler invoked with WorkerID: ${solcWorker.pid}`); 70 | console.log(`Compiling with solidity version ${this.version}`); 71 | var input: CompilerInput = { 72 | language: opts.language, 73 | sources, 74 | settings: { 75 | outputSelection: { 76 | "*": { 77 | "": ["ast"], 78 | '*': ['abi', 'metadata', 'devdoc', 'userdoc', 'evm.legacyAssembly', 'evm.bytecode', 'evm.deployedBytecode', 'evm.methodIdentifiers', 'evm.gasEstimates', 'evm.assembly'] 79 | }, 80 | }, 81 | optimizer: { 82 | enabled: opts.optimize === true || opts.optimize === 1, 83 | runs: opts.runs || 200, 84 | details: { 85 | yul: Boolean(opts.language === 'Yul' && opts.optimize) 86 | } 87 | }, 88 | libraries: opts.libraries, 89 | }, 90 | }; 91 | if (opts.evmVersion) { 92 | input.settings.evmVersion = opts.evmVersion 93 | } 94 | if (opts.language) { 95 | input.language = opts.language 96 | } 97 | if (opts.language === 'Yul' && input.settings.optimizer.enabled) { 98 | if (!input.settings.optimizer.details) 99 | input.settings.optimizer.details = {} 100 | input.settings.optimizer.details.yul = true 101 | } 102 | solcWorker.send({ 103 | command: "compile", 104 | root: workspace.workspaceFolders[0].uri.fsPath, 105 | payload: input, 106 | version: this.version, 107 | }); 108 | solcWorker.on("message", (m: any) => { 109 | console.log(`............................Solidity worker message............................`); 110 | console.log(m); 111 | if (m.error) { 112 | this.print(m.error); 113 | console.error(m.error); 114 | } else if (m.data && m.path) { 115 | sources[m.path] = { 116 | content: m.data.content, 117 | }; 118 | solcWorker.send({ 119 | command: "compile", 120 | root: workspace.workspaceFolders[0].uri.fsPath, 121 | payload: input, 122 | version: this.version, 123 | }); 124 | } else if (m.compiled) { 125 | const languageVersion = this.version; 126 | const compiled = JSON.parse(m.compiled); 127 | if (compiled.errors) { 128 | this.print(`Compilation error while compiling ${fileName} with solidity version ${m?.version}.`); 129 | logError(compiled?.errors) 130 | } 131 | if (compiled.contracts) { 132 | const source = { sources }; 133 | const data = JSON.parse(m.compiled); 134 | this.compilationResult = { 135 | source: { 136 | sources, 137 | target: fileName 138 | }, 139 | data 140 | } 141 | this.print(`Compilation finished for ${fileName} with solidity version ${m?.version}.`); 142 | this.emit('compilationFinished', fileName, source, languageVersion, data); 143 | } 144 | } 145 | }) 146 | 147 | const errorKeysToLog = ['formattedMessage'] 148 | const logError = (errors: any[]) => { 149 | for (let i in errors) { 150 | if (['number', 'string'].includes(typeof errors[i])) { 151 | if (errorKeysToLog.includes(i)) 152 | this.print(errors[i]) 153 | } else { 154 | logError(errors[i]) 155 | } 156 | } 157 | } 158 | } 159 | 160 | async compileWithSolidityExtension() { 161 | commands.executeCommand("solidity.compile.active").then(async (listOFiles: string[]) => { 162 | if (listOFiles) 163 | for (let file of listOFiles) { 164 | await this.parseSolcOutputFile(file) 165 | } 166 | }) 167 | } 168 | 169 | async parseSolcOutputFile(file: string) { 170 | console.log(file) 171 | this.print(`Compiling with Solidity Extension`) 172 | const content = await this.call("fileManager", "readFile", file) 173 | const parsedContent = JSON.parse(content) 174 | const sourcePath = parsedContent.sourcePath 175 | const solcOutput = `${path.basename(parsedContent.sourcePath).split('.').slice(0, -1).join('.')}-solc-output.json` 176 | const outputDir = path.dirname(file) 177 | let raw = await this.call("fileManager", "readFile", `${outputDir}/${solcOutput}`) 178 | console.log(`${outputDir}/${solcOutput}`); 179 | const relativeFilePath = relativePath(sourcePath) 180 | var re = new RegExp(`${sourcePath}`, "gi"); 181 | raw = raw.replace(re, relativeFilePath) 182 | const compiled = JSON.parse(raw) 183 | let source = {} 184 | const fileKeys = Object.keys(compiled.sources) 185 | for (let s of fileKeys) { 186 | source[s] = { content: await this.call("fileManager", "readFile", s) } 187 | } 188 | this.compilationResult = { 189 | source: { 190 | sources: source, 191 | target: relativeFilePath 192 | }, 193 | data: compiled 194 | } 195 | this.print(`Compilation finished for ${relativeFilePath} with solidity version ${parsedContent?.compiler.version}.`); 196 | this.emit('compilationFinished', relativeFilePath, { sources: source }, parsedContent?.compiler.version, compiled); 197 | } 198 | 199 | getCompilationResult() { 200 | return this.compilationResult; 201 | } 202 | private loadSolidityVersions() { 203 | const solcWorker = this.createWorker(); 204 | solcWorker.send({ command: "fetch_compiler_verison" }); 205 | solcWorker.on("message", (m: any) => { 206 | this.versions = m.versions; 207 | }); 208 | } 209 | getSolidityVersions() { 210 | return this.versions; 211 | } 212 | } -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export interface PluginInfo { 2 | name: string; 3 | displayName: string; 4 | description: string; 5 | methods?: (string | null)[] | null; 6 | events?: null[] | null; 7 | version?: string | null; 8 | url: string; 9 | icon: string; 10 | location: string; 11 | kind?: string | null; 12 | notifications?: Notifications | null; 13 | documentation?: string | null; 14 | permission?: boolean; 15 | targets?: string[]; 16 | targetVersion?: any | null; 17 | } 18 | 19 | 20 | interface Notifications { 21 | solidity?: string[] | null; 22 | udapp?: string[] | null; 23 | network?: string[] | null; 24 | } 25 | export interface CompilerInput { 26 | // Required: Source code language. Currently supported are "Solidity" and "Yul". 27 | language: Language, 28 | // Required 29 | sources: Source, 30 | // Optional 31 | settings: 32 | { 33 | // Optional: Sorted list of remappings 34 | remappings?: string[], 35 | // Optional: Optimizer settings 36 | optimizer: { 37 | // disabled by default 38 | enabled: boolean, 39 | // Optimize for how many times you intend to run the code. 40 | // Lower values will optimize more for initial deployment cost, higher 41 | // values will optimize more for high-frequency usage. 42 | runs: number, 43 | // Switch optimizer components on or off in detail. 44 | // The "enabled" switch above provides two defaults which can be 45 | // tweaked here. If "details" is given, "enabled" can be omitted. 46 | details?: { 47 | // The peephole optimizer is always on if no details are given, 48 | // use details to switch it off. 49 | peephole?: boolean, 50 | // The unused jumpdest remover is always on if no details are given, 51 | // use details to switch it off. 52 | jumpdestRemover?: boolean, 53 | // Sometimes re-orders literals in commutative operations. 54 | orderLiterals?: boolean, 55 | // Removes duplicate code blocks 56 | deduplicate?: boolean, 57 | // Common subexpression elimination, this is the most complicated step but 58 | // can also provide the largest gain. 59 | cse?: boolean, 60 | // Optimize representation of literal numbers and strings in code. 61 | constantOptimizer?: boolean, 62 | // The new Yul optimizer. Mostly operates on the code of ABIEncoderV2. 63 | // It can only be activated through the details here. 64 | yul?: boolean, 65 | // Tuning options for the Yul optimizer. 66 | yulDetails?: { 67 | // Improve allocation of stack slots for variables, can free up stack slots early. 68 | // Activated by default if the Yul optimizer is activated. 69 | stackAllocation: boolean 70 | } 71 | } 72 | }, 73 | // Version of the EVM to compile for. 74 | // Affects type checking and code generation. 75 | evmVersion?: EVMVersion, 76 | // Optional: Debugging settings 77 | debug?: { 78 | // How to treat revert (and require) reason strings. Settings are 79 | // "default", "strip", "debug" and "verboseDebug". 80 | // "default" does not inject compiler-generated revert strings and keeps user-supplied ones. 81 | // "strip" removes all revert strings (if possible, i.e. if literals are used) keeping side-effects 82 | // "debug" injects strings for compiler-generated internal reverts (not yet implemented) 83 | // "verboseDebug" even appends further information to user-supplied revert strings (not yet implemented) 84 | revertStrings: 'default' | 'strip' | 'debug' | 'verboseDebug' 85 | } 86 | // Metadata settings (optional) 87 | metadata?: { 88 | // Use only literal content and not URLs (false by default) 89 | useLiteralContent: boolean, 90 | // Use the given hash method for the metadata hash that is appended to the bytecode. 91 | // The metadata hash can be removed from the bytecode via option "none". 92 | // The other options are "ipfs" and "bzzr1". 93 | // If the option is omitted, "ipfs" is used by default. 94 | bytecodeHash: 'ipfs' | 'bzzr1' | 'none' 95 | }, 96 | // Addresses of the libraries. If not all libraries are given here, 97 | // it can result in unlinked objects whose output data is different. 98 | libraries?: { 99 | // The top level key is the the name of the source file where the library is used. 100 | // If remappings are used, this source file should match the global path 101 | // after remappings were applied. 102 | // If this key is an empty string, that refers to a global level. 103 | [fileName: string]: Record 104 | } 105 | // The following can be used to select desired outputs based 106 | // on file and contract names. 107 | // If this field is omitted, then the compiler loads and does type checking, 108 | // but will not generate any outputs apart from errors. 109 | // The first level key is the file name and the second level key is the contract name. 110 | // An empty contract name is used for outputs that are not tied to a contract 111 | // but to the whole source file like the AST. 112 | // A star as contract name refers to all contracts in the file. 113 | // Similarly, a star as a file name matches all files. 114 | // To select all outputs the compiler can possibly generate, use 115 | // "outputSelection: { "*": { "*": [ "*" ], "": [ "*" ] } }" 116 | // but note that this might slow down the compilation process needlessly. 117 | // 118 | // The available output types are as follows: 119 | // 120 | // File level (needs empty string as contract name): 121 | // ast - AST of all source files 122 | // legacyAST - legacy AST of all source files 123 | // 124 | // Contract level (needs the contract name or "*"): 125 | // abi - ABI 126 | // devdoc - Developer documentation (natspec) 127 | // userdoc - User documentation (natspec) 128 | // metadata - Metadata 129 | // ir - Yul intermediate representation of the code before optimization 130 | // irOptimized - Intermediate representation after optimization 131 | // storageLayout - Slots, offsets and types of the contract's state variables. 132 | // evm.assembly - New assembly format 133 | // evm.legacyAssembly - Old-style assembly format in JSON 134 | // evm.bytecode.object - Bytecode object 135 | // evm.bytecode.opcodes - Opcodes list 136 | // evm.bytecode.sourceMap - Source mapping (useful for debugging) 137 | // evm.bytecode.linkReferences - Link references (if unlinked object) 138 | // evm.deployedBytecode* - Deployed bytecode (has the same options as evm.bytecode) 139 | // evm.methodIdentifiers - The list of function hashes 140 | // evm.gasEstimates - Function gas estimates 141 | // ewasm.wast - eWASM S-expressions format (not supported at the moment) 142 | // ewasm.wasm - eWASM binary format (not supported at the moment) 143 | // 144 | // Note that using a using `evm`, `evm.bytecode`, `ewasm`, etc. will select every 145 | // target part of that output. Additionally, `*` can be used as a wildcard to request everything. 146 | // 147 | outputSelection?: { 148 | '*': { 149 | '': [ 'ast' ], 150 | '*': [ '*' ] | [ 'abi', 'metadata', 'devdoc', 'userdoc', 'evm.legacyAssembly', 'evm.bytecode', 'evm.deployedBytecode', 'evm.methodIdentifiers', 'evm.gasEstimates', 'evm.assembly' ] 151 | } 152 | } 153 | } 154 | } 155 | export interface CompilerInputOptions { 156 | optimize: boolean | number, 157 | runs: number, 158 | libraries?: { 159 | [fileName: string]: Record 160 | }, 161 | evmVersion?: EVMVersion, 162 | language?: Language 163 | } 164 | 165 | type EVMVersion = 'homestead' | 'tangerineWhistle' | 'spuriousDragon' | 'byzantium' | 'constantinople' | 'petersburg' | 'istanbul' | 'muirGlacier' | null 166 | type Language = 'Solidity' | 'Yul' 167 | 168 | export interface Source { 169 | [fileName: string]: 170 | { 171 | // Optional: keccak256 hash of the source file 172 | keccak256?: string, 173 | // Required (unless "urls" is used): literal contents of the source file 174 | content: string, 175 | urls?: string[] 176 | } 177 | } 178 | 179 | export interface RemixPosition { 180 | start: { 181 | line: number; 182 | column: number; 183 | }; 184 | end: { 185 | line: number; 186 | column: number; 187 | }; 188 | } 189 | 190 | export interface Annotation extends Error { 191 | row: number; 192 | column: number; 193 | text: string; 194 | type: "error" | "warning" | "information"; 195 | } 196 | 197 | export interface ISource { 198 | content: string | undefined 199 | } 200 | export interface ISources { 201 | [key: string]: ISource 202 | } -------------------------------------------------------------------------------- /src/plugins/udapp.ts: -------------------------------------------------------------------------------- 1 | import { Plugin } from "@remixproject/engine"; 2 | import { 3 | absolutePath, 4 | relativePath, 5 | } from "@remixproject/engine-vscode/util/path"; 6 | import { 7 | window, 8 | workspace, 9 | Uri, 10 | commands, 11 | ViewColumn, 12 | InputBoxOptions, 13 | OutputChannel, 14 | QuickPickItem, 15 | } from "vscode"; 16 | import Web3 from "web3"; 17 | import { AbiInput, AbiItem } from "web3-utils"; 18 | 19 | const profile = { 20 | name: "udapp", 21 | displayName: "udapp", 22 | description: "", 23 | icon: "assets/img/fileManager.webp", 24 | version: "0.0.1", 25 | methods: ["deploy", "send", "addNetwork", "getAccounts", "setAccount", "disconnect"], 26 | events: ["receipt", "deploy"], 27 | kind: "file-system", 28 | }; 29 | export default class DeployModule extends Plugin { 30 | private outputChannel: OutputChannel; 31 | private web3Provider; 32 | public compiledContracts; 33 | private accounts: string[] = []; 34 | private web3: Web3; 35 | private networkName: string; 36 | constructor() { 37 | super(profile); 38 | this.outputChannel = window.createOutputChannel("Remix IDE"); 39 | this.compiledContracts = {}; 40 | } 41 | 42 | async setListeners() { 43 | // listen for plugins 44 | this.on( 45 | "manager", 46 | "pluginActivated", 47 | await this.addPluginProvider.bind(this) 48 | ); 49 | this.on( 50 | "manager", 51 | "pluginDeactivated", 52 | await this.removePluginProvider.bind(this) 53 | ); 54 | this.on( 55 | "solidity", 56 | "compilationFinished", 57 | (file, source, languageVersion, data) => { 58 | this.compiledContracts = data.contracts[file]; 59 | } 60 | ); 61 | } 62 | 63 | async addNetwork(network: string) { 64 | let networkprovider = new Web3.providers.HttpProvider(network); 65 | this.call("web3Provider", "setProvider", networkprovider); 66 | } 67 | 68 | async setAccount(account: string) { 69 | this.web3.eth.defaultAccount = account; 70 | this.print(`Account changed to ${this.web3.eth.defaultAccount}`); 71 | } 72 | 73 | async getAccounts(setAccount: boolean = true) { 74 | try { 75 | if (await this.web3.eth.net.isListening()) { 76 | let accounts = await this.web3.eth.getAccounts(); 77 | if (setAccount){ 78 | if (accounts.length > 0) this.web3.eth.defaultAccount = accounts[0]; 79 | this.print(`Account changed to ${this.web3.eth.defaultAccount}`); 80 | } 81 | return accounts; 82 | } 83 | } catch (e) { 84 | console.log(e); 85 | } 86 | return []; 87 | } 88 | 89 | // web3 90 | async addPluginProvider(profile) { 91 | if (profile.kind === "provider") { 92 | ((profile, app) => { 93 | let web3Provider = { 94 | async sendAsync(payload, callback) { 95 | try { 96 | const result = await app.call(profile.name, "sendAsync", payload); 97 | callback(null, result); 98 | } catch (e) { 99 | callback(e); 100 | } 101 | }, 102 | }; 103 | this.call("web3Provider", "setProvider", web3Provider); 104 | this.web3Provider = { 105 | async sendAsync(payload, callback) { 106 | try { 107 | const result = await app.call( 108 | "web3Provider", 109 | "sendAsync", 110 | payload 111 | ); 112 | callback(null, result); 113 | } catch (e) { 114 | callback(e); 115 | } 116 | }, 117 | }; 118 | this.web3 = new Web3(this.web3Provider); 119 | //app.blockchain.addProvider({ name: profile.displayName, provider: web3Provider }) 120 | })(profile, this); 121 | } 122 | } 123 | async removePluginProvider(profile) { 124 | if (profile.kind === "provider") this.web3Provider = null; 125 | } 126 | 127 | private getNow(): string { 128 | const date = new Date(Date.now()); 129 | return date.toLocaleTimeString(); 130 | } 131 | 132 | private print(m: string) { 133 | const now = this.getNow(); 134 | this.outputChannel.appendLine(`[${now}]: ${m}`); 135 | this.outputChannel.show(); 136 | } 137 | 138 | async showContractPicker() { 139 | const keys = Object.keys(this.compiledContracts); 140 | } 141 | 142 | async detectNetwork() { 143 | await this.web3.eth.net.getId((err, id) => { 144 | this.networkName = null; 145 | if (err) { 146 | this.print(`Could not detect network! Please connnect to your wallet.`); 147 | return; 148 | } 149 | // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md 150 | else if (id === 1) this.networkName = "Main"; 151 | else if (id === 2) this.networkName = "Morden (deprecated)"; 152 | else if (id === 3) this.networkName = "Ropsten"; 153 | else if (id === 4) this.networkName = "Rinkeby"; 154 | else if (id === 5) this.networkName = "Goerli"; 155 | else if (id === 42) this.networkName = "Kovan"; 156 | else this.networkName = "Custom"; 157 | this.print(`Network is ${this.networkName}!`); 158 | }); 159 | } 160 | 161 | async txDetailsLink(hash: string) { 162 | await this.detectNetwork(); 163 | const transactionDetailsLinks = { 164 | Main: "https://www.etherscan.io/address/", 165 | Rinkeby: "https://rinkeby.etherscan.io/address/", 166 | Ropsten: "https://ropsten.etherscan.io/address/", 167 | Kovan: "https://kovan.etherscan.io/address/", 168 | Goerli: "https://goerli.etherscan.io/address/", 169 | }; 170 | 171 | if (transactionDetailsLinks[this.networkName]) { 172 | return transactionDetailsLinks[this.networkName] + hash; 173 | } 174 | } 175 | 176 | async getContract(contractName: string) { 177 | const selectedContractKey = Object.keys(this.compiledContracts).find( 178 | (name) => name == contractName 179 | ); 180 | const c = this.compiledContracts[selectedContractKey]; 181 | console.log(c); 182 | return c; 183 | } 184 | 185 | async deploy(contractName: string, payload: any[]) { 186 | const c = await this.getContract(contractName); 187 | if (!c) { 188 | this.print("No contract specified."); 189 | return; 190 | } 191 | this.print(`Deploying contract ${contractName} started!`); 192 | //this.print(`Deploying ${Object.keys(c)} ...`); 193 | try { 194 | if (!this.web3Provider) { 195 | this.print( 196 | "No Web3 provider activated! Please activate a wallet or web3 provider plugin." 197 | ); 198 | return; 199 | } 200 | let accounts = await this.web3.eth.getAccounts(); 201 | await this.detectNetwork(); 202 | let contract = new this.web3.eth.Contract(c.abi); 203 | let deployObject = contract.deploy({ 204 | data: c.evm.bytecode.object, 205 | arguments: payload, 206 | }); 207 | let gasValue = await deployObject.estimateGas(); 208 | const gasBase = Math.ceil(gasValue * 1.2); 209 | const gas = gasBase; 210 | this.print(`Gas estimate ${gas}`); 211 | 212 | let me = this; 213 | await deployObject 214 | .send({ 215 | from: this.web3.eth.defaultAccount, 216 | gas: gas, 217 | }) 218 | .on("receipt", async function (receipt) { 219 | me.emit("deploy", { receipt: receipt, abi:c.abi, contractName: contractName }) 220 | me.print(`Contract deployed at ${receipt.contractAddress}`); 221 | const link: string = await me.txDetailsLink(receipt.contractAddress); 222 | me.print(link); 223 | }); 224 | this.print("Deploying ..."); 225 | } catch (e) { 226 | console.log("ERROR ", e); 227 | this.print(`There are errors deploying: ${e}`); 228 | throw new Error(`There are errors deploying: ${e}`); 229 | } 230 | } 231 | 232 | async send(abi: AbiItem, payload: any[], address: string) { 233 | try { 234 | if (!this.web3Provider) { 235 | this.print( 236 | "No Web3 provider activated! Please activate a wallet or web3 provider plugin." 237 | ); 238 | return; 239 | } 240 | await this.detectNetwork(); 241 | 242 | let contract = new this.web3.eth.Contract( 243 | JSON.parse(JSON.stringify([abi])), 244 | address 245 | ); 246 | let accounts = await this.web3.eth.getAccounts(); 247 | if (abi.stateMutability === "view" || abi.stateMutability === "pure") { 248 | try { 249 | this.print( 250 | `Calling method '${abi.name}' with ${JSON.stringify( 251 | payload 252 | )} from ${ 253 | this.web3.eth.defaultAccount 254 | } at contract address ${address}` 255 | ); 256 | const txReceipt = abi.name 257 | ? await contract.methods[abi.name](...payload).call({ 258 | from: this.web3.eth.defaultAccount, 259 | }) 260 | : null; 261 | this.print(JSON.stringify(txReceipt)); 262 | return txReceipt; 263 | // TODO: LOG 264 | } catch (e) { 265 | console.error(e); 266 | throw new Error(`There are errors calling: ${e}`); 267 | } 268 | } else { 269 | try { 270 | this.print( 271 | `Send data to method '${abi.name}' with ${JSON.stringify( 272 | payload 273 | )} from ${ 274 | this.web3.eth.defaultAccount 275 | } at contract address ${address}` 276 | ); 277 | const txReceipt = abi.name 278 | ? await contract.methods[abi.name](...payload).send({ 279 | from: this.web3.eth.defaultAccount, 280 | }) 281 | : null; 282 | this.print(JSON.stringify(txReceipt)); 283 | return txReceipt; 284 | // TODO: LOG 285 | } catch (e) { 286 | console.error(e); 287 | throw new Error(`There are errors sending data: ${e}`); 288 | } 289 | } 290 | } catch (e) { 291 | console.log("ERROR ", e); 292 | this.print(`There are errors sending data.`); 293 | throw new Error(`There are errors sending data: ${e}`); 294 | } 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | import { 3 | window, 4 | commands, 5 | workspace, 6 | InputBoxOptions, 7 | ExtensionContext, 8 | QuickPickItem, 9 | env, 10 | Uri, 11 | extensions, 12 | } from "vscode"; 13 | import { PluginManager, Engine } from "@remixproject/engine"; 14 | import { ThemeUrls } from "@remixproject/plugin-api"; 15 | import { 16 | VscodeAppManager, 17 | WebviewPlugin, 18 | ThemePlugin, 19 | FileManagerPlugin, 20 | EditorPlugin, 21 | EditorOptions, 22 | transformCmd, 23 | ThemeOptions, 24 | ContentImportPlugin, 25 | } from "@remixproject/engine-vscode"; 26 | 27 | import { RmxPluginsProvider } from "./rmxPlugins"; 28 | import NativeSolcPlugin from "./plugins/native_solidity_plugin"; 29 | import DeployModule from "./plugins/udapp"; 30 | import { 31 | pluginActivate, 32 | pluginDeactivate, 33 | pluginDocumentation, 34 | pluginUninstall, 35 | } from "./optionInputs"; 36 | import { ExtAPIPlugin } from "./plugins/ext_api_plugin"; 37 | import { ToViewColumn, GetPluginData } from "./utils"; 38 | import { PluginInfo, CompilerInputOptions } from "./types"; 39 | import { Profile } from "@remixproject/plugin-utils"; 40 | import WalletConnect from "./plugins/wallet"; 41 | import { Web3ProviderModule } from "./plugins/web3provider"; 42 | import semver from "semver"; 43 | const queryString = require("query-string"); 44 | 45 | class VscodeManager extends VscodeAppManager { 46 | onActivation() { 47 | } 48 | } 49 | 50 | export async function activate(context: ExtensionContext) { 51 | let selectedVersion: string = "latest"; 52 | let compilerOpts: CompilerInputOptions = { 53 | language: "Solidity", 54 | optimize: false, 55 | runs: 200, 56 | }; 57 | if (!workspace.workspaceFolders || !workspace.workspaceFolders[0]) { 58 | window.showErrorMessage( 59 | "Please open a workspace or folder before using this extension." 60 | ); 61 | return false; 62 | } 63 | const rmxPluginsProvider = new RmxPluginsProvider( 64 | workspace.workspaceFolders[0].uri.fsPath 65 | ); 66 | const editoropt: EditorOptions = { language: "solidity", transformCmd }; 67 | const engine = new Engine(); 68 | const manager = new VscodeManager(); 69 | const solpl = new NativeSolcPlugin(); 70 | const deployModule = new DeployModule(); 71 | const web3Povider = new Web3ProviderModule(); 72 | const vscodeExtAPI = new ExtAPIPlugin(); 73 | const wallet = new WalletConnect(); 74 | const filemanager = new FileManagerPlugin(); 75 | const editorPlugin = new EditorPlugin(editoropt); 76 | const importer = new ContentImportPlugin(); 77 | const themeURLs: Partial = { 78 | light: 79 | "https://remix-alpha.ethereum.org/assets/css/themes/remix-light_powaqg.css", 80 | dark: "https://remix-alpha.ethereum.org/assets/css/themes/remix-dark_tvx1s2.css", 81 | }; 82 | const themeOpts: ThemeOptions = { urls: themeURLs }; 83 | const theme = new ThemePlugin(themeOpts); 84 | 85 | engine.setPluginOption = ({ name, kind }) => { 86 | if (kind === "provider") return { queueTimeout: 60000 * 2 }; 87 | if (name === "udapp") return { queueTimeout: 60000 * 2 }; 88 | if (name === "LearnEth") return { queueTimeout: 60000 }; 89 | return { queueTimeout: 10000 }; 90 | }; 91 | 92 | engine.register([ 93 | manager, 94 | solpl, 95 | filemanager, 96 | editorPlugin, 97 | theme, 98 | importer, 99 | web3Povider, 100 | deployModule, 101 | wallet, 102 | vscodeExtAPI, 103 | ]); 104 | window.registerTreeDataProvider("rmxPlugins", rmxPluginsProvider); 105 | 106 | await manager.activatePlugin([ 107 | "web3Provider", 108 | "udapp", 109 | ]); 110 | await deployModule.setListeners(); 111 | await manager.activatePlugin(["walletconnect"]); 112 | 113 | // fetch default data from the plugins-directory filtered by engine 114 | const defaultPluginData = await manager.registeredPluginData(); 115 | /* const defaultPluginData = [ 116 | { 117 | name: "vscodeudapp", 118 | displayName: "Deploy & Run", 119 | events: [], 120 | methods: ["displayUri"], 121 | version: "0.1.0", 122 | url: "http://localhost:3000", 123 | documentation: 124 | "https://github.com/bunsenstraat/remix-vscode-walletconnect", 125 | description: "Connect to a network to run and deploy.", 126 | icon: "https://remix.ethereum.org/assets/img/deployAndRun.webp", 127 | location: "sidePanel", 128 | targets: ["vscode"], 129 | targetVersion: { 130 | vscode: ">=0.0.8", 131 | }, 132 | }, 133 | ]; */ 134 | rmxPluginsProvider.setDefaultData(defaultPluginData); 135 | // compile 136 | commands.registerCommand("rmxPlugins.compile", async () => { 137 | await manager.activatePlugin([ 138 | "solidity", 139 | "fileManager", 140 | "editor", 141 | "contentImport", 142 | ]); 143 | solpl.compile(selectedVersion, compilerOpts); 144 | }); 145 | commands.registerCommand("rmxPlugins.compile.solidity", async () => { 146 | await manager.activatePlugin([ 147 | "solidity", 148 | "fileManager", 149 | "editor", 150 | "contentImport", 151 | ]); 152 | try { 153 | let solextension = extensions.getExtension("juanblanco.solidity"); 154 | if (solextension.isActive) { 155 | solpl.compileWithSolidityExtension(); 156 | } else { 157 | window.showErrorMessage("The Solidity extension is not enabled."); 158 | } 159 | } catch (e) { 160 | window.showErrorMessage("The Solidity extension is not installed."); 161 | } 162 | }); 163 | 164 | const checkSemver = async (pluginData: PluginInfo) => { 165 | if (!(pluginData.targetVersion && pluginData.targetVersion.vscode)) 166 | return true; 167 | return semver.satisfies( 168 | context.extension.packageJSON.version, 169 | pluginData.targetVersion.vscode 170 | ); 171 | }; 172 | 173 | const activatePlugin = async (pluginId: string) => { 174 | // Get plugininfo from plugin array 175 | const pluginData: PluginInfo = GetPluginData( 176 | pluginId, 177 | rmxPluginsProvider.getData() 178 | ); 179 | const versionCheck = await checkSemver(pluginData); 180 | if (!versionCheck) { 181 | window.showErrorMessage( 182 | `This plugin requires an update of the extension. Please update now.` 183 | ); 184 | return false; 185 | } 186 | // choose window column for display 187 | const cl = ToViewColumn(pluginData); 188 | const plugin = new WebviewPlugin(pluginData, { context, column: cl }); 189 | if (!engine.isRegistered(pluginId)) { 190 | // @ts-ignore 191 | engine.register(plugin); 192 | } 193 | 194 | manager.activatePlugin([ 195 | pluginId, 196 | "solidity", 197 | "fileManager", 198 | "editor", 199 | "vscodeExtAPI", 200 | ]); 201 | const profile: Profile = await manager.getProfile(pluginId); 202 | window.showInformationMessage( 203 | `${profile.displayName} v${profile.version} activated.` 204 | ); 205 | }; 206 | 207 | commands.registerCommand("rmxPlugins.walletConnect", async () => { 208 | //await manager.activatePlugin(['walletconnect']); 209 | //await activatePlugin('qr') 210 | await wallet.connect(); 211 | //await web3Module.deploy() 212 | }); 213 | 214 | commands.registerCommand("rmxPlugins.walletDisconnect", async () => { 215 | //await manager.activatePlugin(['walletconnect']); 216 | await wallet.disconnect(); 217 | //await web3Module.deploy() 218 | }); 219 | 220 | commands.registerCommand("rmxPlugins.deploy", async () => { 221 | // await wallet.connect(); 222 | const contracts = Object.keys(deployModule.compiledContracts); 223 | const opts: Array = contracts.map((v): QuickPickItem => { 224 | const vopt: QuickPickItem = { 225 | label: v, 226 | description: `Deploy contract: ${v}`, 227 | }; 228 | return vopt; 229 | }); 230 | window.showQuickPick(opts).then(async (selected) => { 231 | if (selected) { 232 | await deployModule.deploy(selected.label, []); 233 | } 234 | }); 235 | }); 236 | 237 | // activate plugin 238 | commands.registerCommand( 239 | "extension.activateRmxPlugin", 240 | async (pluginId: string) => { 241 | await activatePlugin(pluginId); 242 | } 243 | ); 244 | commands.registerCommand("rmxPlugins.refreshEntry", () => { 245 | rmxPluginsProvider.refresh(); 246 | }); 247 | commands.registerCommand("rmxPlugins.addEntry", () => { 248 | const pluginjson: PluginInfo = { 249 | name: "remix-plugin-example", 250 | displayName: "Remix plugin example", 251 | methods: [], 252 | version: "0.0.1-dev", 253 | url: "", 254 | description: "Run remix plugin in your Remix project", 255 | icon: "", 256 | location: "sidePanel", 257 | }; 258 | const opts: InputBoxOptions = { 259 | value: JSON.stringify(pluginjson), 260 | placeHolder: "Add your plugin JSON", 261 | }; 262 | window.showInputBox(opts).then((input: string) => { 263 | if (input && input.length > 0) { 264 | const devPlugin: PluginInfo = JSON.parse(input); 265 | rmxPluginsProvider.add(devPlugin); 266 | } 267 | }); 268 | }); 269 | commands.registerCommand( 270 | "rmxPlugins.uninstallRmxPlugin", 271 | async (pluginId: string) => { 272 | commands.executeCommand("extension.deActivateRmxPlugin", pluginId); 273 | rmxPluginsProvider.remove(pluginId); 274 | } 275 | ); 276 | commands.registerCommand("rmxPlugins.showPluginOptions", async (plugin) => { 277 | let id = ""; 278 | if (plugin instanceof Object) id = plugin.id; 279 | else id = plugin; 280 | const pluginData = GetPluginData(id, rmxPluginsProvider.getData()); 281 | const options: { 282 | [key: string]: (context: ExtensionContext, id: string) => Promise; 283 | } = { 284 | Activate: pluginActivate, 285 | Deactivate: pluginDeactivate, 286 | }; 287 | if (pluginData.documentation) 288 | options["Documentation"] = pluginDocumentation; 289 | if ( 290 | (pluginData.targets && !pluginData.targets.includes("vscode")) || 291 | !pluginData.targets 292 | ) 293 | options["Uninstall"] = pluginUninstall; 294 | const quickPick = window.createQuickPick(); 295 | quickPick.items = Object.keys(options).map((label) => ({ label })); 296 | quickPick.onDidChangeSelection((selection) => { 297 | if (selection[0]) { 298 | options[selection[0].label](context, id).catch(console.error); 299 | } 300 | }); 301 | quickPick.onDidHide(() => quickPick.dispose()); 302 | quickPick.show(); 303 | }); 304 | commands.registerCommand( 305 | "extension.deActivateRmxPlugin", 306 | async (pluginId: string) => { 307 | manager.deactivatePlugin([pluginId]); 308 | editorPlugin.discardDecorations(); 309 | } 310 | ); 311 | // Version selector 312 | commands.registerCommand("rmxPlugins.versionSelector", async () => { 313 | try { 314 | await manager.activatePlugin(["solidity"]); 315 | const versions = solpl.getSolidityVersions(); 316 | const opts: Array = Object.keys(versions).map( 317 | (v): QuickPickItem => { 318 | const vopt: QuickPickItem = { 319 | label: v, 320 | description: `Solidity ${v}`, 321 | }; 322 | return vopt; 323 | } 324 | ); 325 | window.showQuickPick(opts).then((selected) => { 326 | if (selected) { 327 | selectedVersion = selected.label; 328 | } 329 | }); 330 | } catch (error) { 331 | console.log(error); 332 | } 333 | }); 334 | // Optimizer selector 335 | commands.registerCommand("rmxPlugins.optimizerSelector", async () => { 336 | try { 337 | const opts: Array = [ 338 | { 339 | label: "Enable", 340 | description: "Enable optimizer", 341 | }, 342 | { 343 | label: "Disable", 344 | description: "Disable optimizer", 345 | }, 346 | ]; 347 | window.showQuickPick(opts).then((selected) => { 348 | if (selected) { 349 | compilerOpts.optimize = Boolean(selected.label === "Enable"); 350 | } 351 | }); 352 | } catch (error) { 353 | console.log(error); 354 | } 355 | }); 356 | // Language selector 357 | commands.registerCommand("rmxPlugins.languageSelector", async () => { 358 | try { 359 | const opts: Array = [ 360 | { 361 | label: "Solidity", 362 | description: "Enable Solidity language", 363 | }, 364 | { 365 | label: "Yul", 366 | description: "Enable Yul language", 367 | }, 368 | ]; 369 | window.showQuickPick(opts).then((selected) => { 370 | if (selected) { 371 | switch (selected.label) { 372 | case "Solidity": 373 | compilerOpts.language = "Solidity"; 374 | compilerOpts.optimize = false; 375 | break; 376 | case "Yul": 377 | compilerOpts.language = "Yul"; 378 | compilerOpts.optimize = false; 379 | break; 380 | default: 381 | compilerOpts.language = "Solidity"; 382 | } 383 | } 384 | }); 385 | } catch (error) { 386 | console.log(error); 387 | } 388 | }); 389 | 390 | commands.registerCommand( 391 | "rmxPlugins.openDocumentation", 392 | async (pluginId: string) => { 393 | const pluginData: PluginInfo = GetPluginData( 394 | pluginId, 395 | rmxPluginsProvider.getData() 396 | ); 397 | if (pluginData.documentation) 398 | env.openExternal(Uri.parse(pluginData.documentation)); 399 | else 400 | window.showWarningMessage( 401 | `Documentation not provided for ${pluginData.displayName}.` 402 | ); 403 | } 404 | ); 405 | } 406 | -------------------------------------------------------------------------------- /vscode.proposed.d.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | /** 7 | * This is the place for API experiments and proposals. 8 | * These API are NOT stable and subject to change. They are only available in the Insiders 9 | * distribution and CANNOT be used in published extensions. 10 | * 11 | * To test these API in local environment: 12 | * - Use Insiders release of VS Code. 13 | * - Add `"enableProposedApi": true` to your package.json. 14 | * - Copy this file to your project. 15 | */ 16 | 17 | declare module 'vscode' { 18 | 19 | //#region Joh - ExecutionContext 20 | // THIS is a deprecated proposal 21 | export enum ExtensionExecutionContext { 22 | Local = 1, 23 | Remote = 2 24 | } 25 | export interface ExtensionContext { 26 | executionContext: ExtensionExecutionContext; 27 | } 28 | //#endregion 29 | 30 | //#region Joh - call hierarchy 31 | 32 | export enum CallHierarchyDirection { 33 | CallsFrom = 1, 34 | CallsTo = 2, 35 | } 36 | 37 | export class CallHierarchyItem { 38 | kind: SymbolKind; 39 | name: string; 40 | detail?: string; 41 | uri: Uri; 42 | range: Range; 43 | selectionRange: Range; 44 | 45 | constructor(kind: SymbolKind, name: string, detail: string, uri: Uri, range: Range, selectionRange: Range); 46 | } 47 | 48 | export interface CallHierarchyItemProvider { 49 | 50 | /** 51 | * Given a document and position compute a call hierarchy item. This is justed as 52 | * anchor for call hierarchy and then `resolveCallHierarchyItem` is being called. 53 | */ 54 | provideCallHierarchyItem( 55 | document: TextDocument, 56 | position: Position, 57 | token: CancellationToken 58 | ): ProviderResult; 59 | 60 | /** 61 | * Resolve a call hierarchy item, e.g. compute all calls from or to a function. 62 | * The result is an array of item/location-tuples. The location in the returned tuples 63 | * is always relative to the "caller" with the caller either being the provided item or 64 | * the returned item. 65 | * 66 | * @param item A call hierarchy item previously returned from `provideCallHierarchyItem` or `resolveCallHierarchyItem` 67 | * @param direction Resolve calls from a function or calls to a function 68 | * @param token A cancellation token 69 | */ 70 | resolveCallHierarchyItem( 71 | item: CallHierarchyItem, 72 | direction: CallHierarchyDirection, 73 | token: CancellationToken 74 | ): ProviderResult<[CallHierarchyItem, Location[]][]>; 75 | } 76 | 77 | export namespace languages { 78 | export function registerCallHierarchyProvider(selector: DocumentSelector, provider: CallHierarchyItemProvider): Disposable; 79 | } 80 | 81 | //#endregion 82 | 83 | 84 | //#region Alex - resolvers 85 | 86 | export interface RemoteAuthorityResolverContext { 87 | resolveAttempt: number; 88 | } 89 | 90 | export class ResolvedAuthority { 91 | readonly host: string; 92 | readonly port: number; 93 | 94 | constructor(host: string, port: number); 95 | } 96 | 97 | export class RemoteAuthorityResolverError extends Error { 98 | static NotAvailable(message?: string, handled?: boolean): RemoteAuthorityResolverError; 99 | static TemporarilyNotAvailable(message?: string): RemoteAuthorityResolverError; 100 | 101 | constructor(message?: string); 102 | } 103 | 104 | export interface RemoteAuthorityResolver { 105 | resolve(authority: string, context: RemoteAuthorityResolverContext): ResolvedAuthority | Thenable; 106 | } 107 | 108 | export interface ResourceLabelFormatter { 109 | scheme: string; 110 | authority?: string; 111 | formatting: ResourceLabelFormatting; 112 | } 113 | 114 | export interface ResourceLabelFormatting { 115 | label: string; // myLabel:/${path} 116 | separator: '/' | '\\' | ''; 117 | tildify?: boolean; 118 | normalizeDriveLetter?: boolean; 119 | workspaceSuffix?: string; 120 | authorityPrefix?: string; 121 | } 122 | 123 | export namespace workspace { 124 | export function registerRemoteAuthorityResolver(authorityPrefix: string, resolver: RemoteAuthorityResolver): Disposable; 125 | export function registerResourceLabelFormatter(formatter: ResourceLabelFormatter): Disposable; 126 | } 127 | 128 | //#endregion 129 | 130 | 131 | // #region Joh - code insets 132 | 133 | export interface WebviewEditorInset { 134 | readonly editor: TextEditor; 135 | readonly line: number; 136 | readonly height: number; 137 | readonly webview: Webview; 138 | readonly onDidDispose: Event; 139 | dispose(): void; 140 | } 141 | 142 | export namespace window { 143 | export function createWebviewTextEditorInset(editor: TextEditor, line: number, height: number, options?: WebviewOptions): WebviewEditorInset; 144 | } 145 | 146 | //#endregion 147 | 148 | //#region Joh - read/write in chunks 149 | 150 | export interface FileSystemProvider { 151 | open?(resource: Uri, options: { create: boolean }): number | Thenable; 152 | close?(fd: number): void | Thenable; 153 | read?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): number | Thenable; 154 | write?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): number | Thenable; 155 | } 156 | 157 | //#endregion 158 | 159 | //#region Rob: search provider 160 | 161 | /** 162 | * The parameters of a query for text search. 163 | */ 164 | export interface TextSearchQuery { 165 | /** 166 | * The text pattern to search for. 167 | */ 168 | pattern: string; 169 | 170 | /** 171 | * Whether or not `pattern` should match multiple lines of text. 172 | */ 173 | isMultiline?: boolean; 174 | 175 | /** 176 | * Whether or not `pattern` should be interpreted as a regular expression. 177 | */ 178 | isRegExp?: boolean; 179 | 180 | /** 181 | * Whether or not the search should be case-sensitive. 182 | */ 183 | isCaseSensitive?: boolean; 184 | 185 | /** 186 | * Whether or not to search for whole word matches only. 187 | */ 188 | isWordMatch?: boolean; 189 | } 190 | 191 | /** 192 | * A file glob pattern to match file paths against. 193 | * TODO@roblou - merge this with the GlobPattern docs/definition in vscode.d.ts. 194 | * @see [GlobPattern](#GlobPattern) 195 | */ 196 | export type GlobString = string; 197 | 198 | /** 199 | * Options common to file and text search 200 | */ 201 | export interface SearchOptions { 202 | /** 203 | * The root folder to search within. 204 | */ 205 | folder: Uri; 206 | 207 | /** 208 | * Files that match an `includes` glob pattern should be included in the search. 209 | */ 210 | includes: GlobString[]; 211 | 212 | /** 213 | * Files that match an `excludes` glob pattern should be excluded from the search. 214 | */ 215 | excludes: GlobString[]; 216 | 217 | /** 218 | * Whether external files that exclude files, like .gitignore, should be respected. 219 | * See the vscode setting `"search.useIgnoreFiles"`. 220 | */ 221 | useIgnoreFiles: boolean; 222 | 223 | /** 224 | * Whether symlinks should be followed while searching. 225 | * See the vscode setting `"search.followSymlinks"`. 226 | */ 227 | followSymlinks: boolean; 228 | 229 | /** 230 | * Whether global files that exclude files, like .gitignore, should be respected. 231 | * See the vscode setting `"search.useGlobalIgnoreFiles"`. 232 | */ 233 | useGlobalIgnoreFiles: boolean; 234 | } 235 | 236 | /** 237 | * Options to specify the size of the result text preview. 238 | * These options don't affect the size of the match itself, just the amount of preview text. 239 | */ 240 | export interface TextSearchPreviewOptions { 241 | /** 242 | * The maximum number of lines in the preview. 243 | * Only search providers that support multiline search will ever return more than one line in the match. 244 | */ 245 | matchLines: number; 246 | 247 | /** 248 | * The maximum number of characters included per line. 249 | */ 250 | charsPerLine: number; 251 | } 252 | 253 | /** 254 | * Options that apply to text search. 255 | */ 256 | export interface TextSearchOptions extends SearchOptions { 257 | /** 258 | * The maximum number of results to be returned. 259 | */ 260 | maxResults: number; 261 | 262 | /** 263 | * Options to specify the size of the result text preview. 264 | */ 265 | previewOptions?: TextSearchPreviewOptions; 266 | 267 | /** 268 | * Exclude files larger than `maxFileSize` in bytes. 269 | */ 270 | maxFileSize?: number; 271 | 272 | /** 273 | * Interpret files using this encoding. 274 | * See the vscode setting `"files.encoding"` 275 | */ 276 | encoding?: string; 277 | 278 | /** 279 | * Number of lines of context to include before each match. 280 | */ 281 | beforeContext?: number; 282 | 283 | /** 284 | * Number of lines of context to include after each match. 285 | */ 286 | afterContext?: number; 287 | } 288 | 289 | /** 290 | * Information collected when text search is complete. 291 | */ 292 | export interface TextSearchComplete { 293 | /** 294 | * Whether the search hit the limit on the maximum number of search results. 295 | * `maxResults` on [`TextSearchOptions`](#TextSearchOptions) specifies the max number of results. 296 | * - If exactly that number of matches exist, this should be false. 297 | * - If `maxResults` matches are returned and more exist, this should be true. 298 | * - If search hits an internal limit which is less than `maxResults`, this should be true. 299 | */ 300 | limitHit?: boolean; 301 | } 302 | 303 | /** 304 | * The parameters of a query for file search. 305 | */ 306 | export interface FileSearchQuery { 307 | /** 308 | * The search pattern to match against file paths. 309 | */ 310 | pattern: string; 311 | } 312 | 313 | /** 314 | * Options that apply to file search. 315 | */ 316 | export interface FileSearchOptions extends SearchOptions { 317 | /** 318 | * The maximum number of results to be returned. 319 | */ 320 | maxResults?: number; 321 | 322 | /** 323 | * A CancellationToken that represents the session for this search query. If the provider chooses to, this object can be used as the key for a cache, 324 | * and searches with the same session object can search the same cache. When the token is cancelled, the session is complete and the cache can be cleared. 325 | */ 326 | session?: CancellationToken; 327 | } 328 | 329 | /** 330 | * A preview of the text result. 331 | */ 332 | export interface TextSearchMatchPreview { 333 | /** 334 | * The matching lines of text, or a portion of the matching line that contains the match. 335 | */ 336 | text: string; 337 | 338 | /** 339 | * The Range within `text` corresponding to the text of the match. 340 | * The number of matches must match the TextSearchMatch's range property. 341 | */ 342 | matches: Range | Range[]; 343 | } 344 | 345 | /** 346 | * A match from a text search 347 | */ 348 | export interface TextSearchMatch { 349 | /** 350 | * The uri for the matching document. 351 | */ 352 | uri: Uri; 353 | 354 | /** 355 | * The range of the match within the document, or multiple ranges for multiple matches. 356 | */ 357 | ranges: Range | Range[]; 358 | 359 | /** 360 | * A preview of the text match. 361 | */ 362 | preview: TextSearchMatchPreview; 363 | } 364 | 365 | /** 366 | * A line of context surrounding a TextSearchMatch. 367 | */ 368 | export interface TextSearchContext { 369 | /** 370 | * The uri for the matching document. 371 | */ 372 | uri: Uri; 373 | 374 | /** 375 | * One line of text. 376 | * previewOptions.charsPerLine applies to this 377 | */ 378 | text: string; 379 | 380 | /** 381 | * The line number of this line of context. 382 | */ 383 | lineNumber: number; 384 | } 385 | 386 | export type TextSearchResult = TextSearchMatch | TextSearchContext; 387 | 388 | /** 389 | * A FileSearchProvider provides search results for files in the given folder that match a query string. It can be invoked by quickopen or other extensions. 390 | * 391 | * A FileSearchProvider is the more powerful of two ways to implement file search in VS Code. Use a FileSearchProvider if you wish to search within a folder for 392 | * all files that match the user's query. 393 | * 394 | * The FileSearchProvider will be invoked on every keypress in quickopen. When `workspace.findFiles` is called, it will be invoked with an empty query string, 395 | * and in that case, every file in the folder should be returned. 396 | */ 397 | export interface FileSearchProvider { 398 | /** 399 | * Provide the set of files that match a certain file path pattern. 400 | * @param query The parameters for this query. 401 | * @param options A set of options to consider while searching files. 402 | * @param token A cancellation token. 403 | */ 404 | provideFileSearchResults(query: FileSearchQuery, options: FileSearchOptions, token: CancellationToken): ProviderResult; 405 | } 406 | 407 | /** 408 | * A TextSearchProvider provides search results for text results inside files in the workspace. 409 | */ 410 | export interface TextSearchProvider { 411 | /** 412 | * Provide results that match the given text pattern. 413 | * @param query The parameters for this query. 414 | * @param options A set of options to consider while searching. 415 | * @param progress A progress callback that must be invoked for all results. 416 | * @param token A cancellation token. 417 | */ 418 | provideTextSearchResults(query: TextSearchQuery, options: TextSearchOptions, progress: Progress, token: CancellationToken): ProviderResult; 419 | } 420 | 421 | /** 422 | * Options that can be set on a findTextInFiles search. 423 | */ 424 | export interface FindTextInFilesOptions { 425 | /** 426 | * A [glob pattern](#GlobPattern) that defines the files to search for. The glob pattern 427 | * will be matched against the file paths of files relative to their workspace. Use a [relative pattern](#RelativePattern) 428 | * to restrict the search results to a [workspace folder](#WorkspaceFolder). 429 | */ 430 | include?: GlobPattern; 431 | 432 | /** 433 | * A [glob pattern](#GlobPattern) that defines files and folders to exclude. The glob pattern 434 | * will be matched against the file paths of resulting matches relative to their workspace. When `undefined` only default excludes will 435 | * apply, when `null` no excludes will apply. 436 | */ 437 | exclude?: GlobPattern | null; 438 | 439 | /** 440 | * The maximum number of results to search for 441 | */ 442 | maxResults?: number; 443 | 444 | /** 445 | * Whether external files that exclude files, like .gitignore, should be respected. 446 | * See the vscode setting `"search.useIgnoreFiles"`. 447 | */ 448 | useIgnoreFiles?: boolean; 449 | 450 | /** 451 | * Whether global files that exclude files, like .gitignore, should be respected. 452 | * See the vscode setting `"search.useGlobalIgnoreFiles"`. 453 | */ 454 | useGlobalIgnoreFiles?: boolean; 455 | 456 | /** 457 | * Whether symlinks should be followed while searching. 458 | * See the vscode setting `"search.followSymlinks"`. 459 | */ 460 | followSymlinks?: boolean; 461 | 462 | /** 463 | * Interpret files using this encoding. 464 | * See the vscode setting `"files.encoding"` 465 | */ 466 | encoding?: string; 467 | 468 | /** 469 | * Options to specify the size of the result text preview. 470 | */ 471 | previewOptions?: TextSearchPreviewOptions; 472 | 473 | /** 474 | * Number of lines of context to include before each match. 475 | */ 476 | beforeContext?: number; 477 | 478 | /** 479 | * Number of lines of context to include after each match. 480 | */ 481 | afterContext?: number; 482 | } 483 | 484 | export namespace workspace { 485 | /** 486 | * Register a search provider. 487 | * 488 | * Only one provider can be registered per scheme. 489 | * 490 | * @param scheme The provider will be invoked for workspace folders that have this file scheme. 491 | * @param provider The provider. 492 | * @return A [disposable](#Disposable) that unregisters this provider when being disposed. 493 | */ 494 | export function registerFileSearchProvider(scheme: string, provider: FileSearchProvider): Disposable; 495 | 496 | /** 497 | * Register a text search provider. 498 | * 499 | * Only one provider can be registered per scheme. 500 | * 501 | * @param scheme The provider will be invoked for workspace folders that have this file scheme. 502 | * @param provider The provider. 503 | * @return A [disposable](#Disposable) that unregisters this provider when being disposed. 504 | */ 505 | export function registerTextSearchProvider(scheme: string, provider: TextSearchProvider): Disposable; 506 | 507 | /** 508 | * Search text in files across all [workspace folders](#workspace.workspaceFolders) in the workspace. 509 | * @param query The query parameters for the search - the search string, whether it's case-sensitive, or a regex, or matches whole words. 510 | * @param callback A callback, called for each result 511 | * @param token A token that can be used to signal cancellation to the underlying search engine. 512 | * @return A thenable that resolves when the search is complete. 513 | */ 514 | export function findTextInFiles(query: TextSearchQuery, callback: (result: TextSearchResult) => void, token?: CancellationToken): Thenable; 515 | 516 | /** 517 | * Search text in files across all [workspace folders](#workspace.workspaceFolders) in the workspace. 518 | * @param query The query parameters for the search - the search string, whether it's case-sensitive, or a regex, or matches whole words. 519 | * @param options An optional set of query options. Include and exclude patterns, maxResults, etc. 520 | * @param callback A callback, called for each result 521 | * @param token A token that can be used to signal cancellation to the underlying search engine. 522 | * @return A thenable that resolves when the search is complete. 523 | */ 524 | export function findTextInFiles(query: TextSearchQuery, options: FindTextInFilesOptions, callback: (result: TextSearchResult) => void, token?: CancellationToken): Thenable; 525 | } 526 | 527 | //#endregion 528 | 529 | //#region Joao: diff command 530 | 531 | /** 532 | * The contiguous set of modified lines in a diff. 533 | */ 534 | export interface LineChange { 535 | readonly originalStartLineNumber: number; 536 | readonly originalEndLineNumber: number; 537 | readonly modifiedStartLineNumber: number; 538 | readonly modifiedEndLineNumber: number; 539 | } 540 | 541 | export namespace commands { 542 | 543 | /** 544 | * Registers a diff information command that can be invoked via a keyboard shortcut, 545 | * a menu item, an action, or directly. 546 | * 547 | * Diff information commands are different from ordinary [commands](#commands.registerCommand) as 548 | * they only execute when there is an active diff editor when the command is called, and the diff 549 | * information has been computed. Also, the command handler of an editor command has access to 550 | * the diff information. 551 | * 552 | * @param command A unique identifier for the command. 553 | * @param callback A command handler function with access to the [diff information](#LineChange). 554 | * @param thisArg The `this` context used when invoking the handler function. 555 | * @return Disposable which unregisters this command on disposal. 556 | */ 557 | export function registerDiffInformationCommand(command: string, callback: (diff: LineChange[], ...args: any[]) => any, thisArg?: any): Disposable; 558 | } 559 | 560 | //#endregion 561 | 562 | //#region Joh: decorations 563 | 564 | //todo@joh -> make class 565 | export interface DecorationData { 566 | letter?: string; 567 | title?: string; 568 | color?: ThemeColor; 569 | priority?: number; 570 | bubble?: boolean; 571 | source?: string; // hacky... we should remove it and use equality under the hood 572 | } 573 | 574 | export interface SourceControlResourceDecorations { 575 | source?: string; 576 | letter?: string; 577 | color?: ThemeColor; 578 | } 579 | 580 | export interface DecorationProvider { 581 | onDidChangeDecorations: Event; 582 | provideDecoration(uri: Uri, token: CancellationToken): ProviderResult; 583 | } 584 | 585 | export namespace window { 586 | export function registerDecorationProvider(provider: DecorationProvider): Disposable; 587 | } 588 | 589 | //#endregion 590 | 591 | //#region André: debug 592 | 593 | // deprecated 594 | 595 | export interface DebugConfigurationProvider { 596 | /** 597 | * Deprecated, use DebugAdapterDescriptorFactory.provideDebugAdapter instead. 598 | * @deprecated Use DebugAdapterDescriptorFactory.createDebugAdapterDescriptor instead 599 | */ 600 | debugAdapterExecutable?(folder: WorkspaceFolder | undefined, token?: CancellationToken): ProviderResult; 601 | } 602 | 603 | //#endregion 604 | 605 | //#region Rob, Matt: logging 606 | 607 | /** 608 | * The severity level of a log message 609 | */ 610 | export enum LogLevel { 611 | Trace = 1, 612 | Debug = 2, 613 | Info = 3, 614 | Warning = 4, 615 | Error = 5, 616 | Critical = 6, 617 | Off = 7 618 | } 619 | 620 | export namespace env { 621 | /** 622 | * Current logging level. 623 | */ 624 | export const logLevel: LogLevel; 625 | 626 | /** 627 | * An [event](#Event) that fires when the log level has changed. 628 | */ 629 | export const onDidChangeLogLevel: Event; 630 | } 631 | 632 | //#endregion 633 | 634 | //#region Joao: SCM validation 635 | 636 | /** 637 | * Represents the validation type of the Source Control input. 638 | */ 639 | export enum SourceControlInputBoxValidationType { 640 | 641 | /** 642 | * Something not allowed by the rules of a language or other means. 643 | */ 644 | Error = 0, 645 | 646 | /** 647 | * Something suspicious but allowed. 648 | */ 649 | Warning = 1, 650 | 651 | /** 652 | * Something to inform about but not a problem. 653 | */ 654 | Information = 2 655 | } 656 | 657 | export interface SourceControlInputBoxValidation { 658 | 659 | /** 660 | * The validation message to display. 661 | */ 662 | readonly message: string; 663 | 664 | /** 665 | * The validation type. 666 | */ 667 | readonly type: SourceControlInputBoxValidationType; 668 | } 669 | 670 | /** 671 | * Represents the input box in the Source Control viewlet. 672 | */ 673 | export interface SourceControlInputBox { 674 | 675 | /** 676 | * A validation function for the input box. It's possible to change 677 | * the validation provider simply by setting this property to a different function. 678 | */ 679 | validateInput?(value: string, cursorPosition: number): ProviderResult; 680 | } 681 | 682 | //#endregion 683 | 684 | //#region Joao: SCM selected provider 685 | 686 | export interface SourceControl { 687 | 688 | /** 689 | * Whether the source control is selected. 690 | */ 691 | readonly selected: boolean; 692 | 693 | /** 694 | * An event signaling when the selection state changes. 695 | */ 696 | readonly onDidChangeSelection: Event; 697 | } 698 | 699 | //#endregion 700 | 701 | //#region Joao: SCM Input Box 702 | 703 | /** 704 | * Represents the input box in the Source Control viewlet. 705 | */ 706 | export interface SourceControlInputBox { 707 | 708 | /** 709 | * Controls whether the input box is visible (default is `true`). 710 | */ 711 | visible: boolean; 712 | } 713 | 714 | //#endregion 715 | 716 | //#region Comments 717 | /** 718 | * Comments provider related APIs are still in early stages, they may be changed significantly during our API experiments. 719 | */ 720 | 721 | interface CommentInfo { 722 | /** 723 | * All of the comment threads associated with the document. 724 | */ 725 | threads: CommentThread[]; 726 | 727 | /** 728 | * The ranges of the document which support commenting. 729 | */ 730 | commentingRanges?: Range[]; 731 | 732 | /** 733 | * If it's in draft mode or not 734 | */ 735 | inDraftMode?: boolean; 736 | } 737 | 738 | /** 739 | * A comment is displayed within the editor or the Comments Panel, depending on how it is provided. 740 | */ 741 | export interface Comment { 742 | /** 743 | * The display name of the user who created the comment 744 | */ 745 | readonly userName: string; 746 | 747 | /** 748 | * The icon path for the user who created the comment 749 | */ 750 | readonly userIconPath?: Uri; 751 | 752 | /** 753 | * The id of the comment 754 | * 755 | * @deprecated Use Id instead 756 | */ 757 | readonly commentId: string; 758 | 759 | /** 760 | * @deprecated Use userIconPath instead. The avatar src of the user who created the comment 761 | */ 762 | gravatar?: string; 763 | 764 | /** 765 | * Whether the current user has permission to edit the comment. 766 | * 767 | * This will be treated as false if the comment is provided by a `WorkspaceCommentProvider`, or 768 | * if it is provided by a `DocumentCommentProvider` and no `editComment` method is given. 769 | * 770 | * DEPRECATED, use editCommand 771 | */ 772 | canEdit?: boolean; 773 | 774 | /** 775 | * Whether the current user has permission to delete the comment. 776 | * 777 | * This will be treated as false if the comment is provided by a `WorkspaceCommentProvider`, or 778 | * if it is provided by a `DocumentCommentProvider` and no `deleteComment` method is given. 779 | * 780 | * DEPRECATED, use deleteCommand 781 | */ 782 | canDelete?: boolean; 783 | 784 | /** 785 | * @deprecated 786 | * The command to be executed if the comment is selected in the Comments Panel 787 | */ 788 | command?: Command; 789 | 790 | /** 791 | * Deprecated 792 | */ 793 | isDraft?: boolean; 794 | 795 | /** 796 | * The command to be executed when users try to delete the comment 797 | */ 798 | deleteCommand?: Command; 799 | 800 | /** 801 | * Proposed Comment Reaction 802 | */ 803 | commentReactions?: CommentReaction[]; 804 | } 805 | 806 | /** 807 | * Deprecated 808 | */ 809 | export interface CommentThreadChangedEvent { 810 | /** 811 | * Added comment threads. 812 | */ 813 | readonly added: ReadonlyArray; 814 | 815 | /** 816 | * Removed comment threads. 817 | */ 818 | readonly removed: ReadonlyArray; 819 | 820 | /** 821 | * Changed comment threads. 822 | */ 823 | readonly changed: ReadonlyArray; 824 | 825 | /** 826 | * Changed draft mode 827 | */ 828 | readonly inDraftMode: boolean; 829 | } 830 | 831 | /** 832 | * Comment Reactions 833 | * Stay in proposed. 834 | */ 835 | interface CommentReaction { 836 | readonly hasReacted?: boolean; 837 | } 838 | 839 | /** 840 | * DEPRECATED 841 | */ 842 | interface DocumentCommentProvider { 843 | /** 844 | * Provide the commenting ranges and comment threads for the given document. The comments are displayed within the editor. 845 | */ 846 | provideDocumentComments(document: TextDocument, token: CancellationToken): Promise; 847 | 848 | /** 849 | * Called when a user adds a new comment thread in the document at the specified range, with body text. 850 | */ 851 | createNewCommentThread(document: TextDocument, range: Range, text: string, token: CancellationToken): Promise; 852 | 853 | /** 854 | * Called when a user replies to a new comment thread in the document at the specified range, with body text. 855 | */ 856 | replyToCommentThread(document: TextDocument, range: Range, commentThread: CommentThread, text: string, token: CancellationToken): Promise; 857 | 858 | /** 859 | * Called when a user edits the comment body to the be new text. 860 | */ 861 | editComment?(document: TextDocument, comment: Comment, text: string, token: CancellationToken): Promise; 862 | 863 | /** 864 | * Called when a user deletes the comment. 865 | */ 866 | deleteComment?(document: TextDocument, comment: Comment, token: CancellationToken): Promise; 867 | 868 | startDraft?(document: TextDocument, token: CancellationToken): Promise; 869 | deleteDraft?(document: TextDocument, token: CancellationToken): Promise; 870 | finishDraft?(document: TextDocument, token: CancellationToken): Promise; 871 | 872 | startDraftLabel?: string; 873 | deleteDraftLabel?: string; 874 | finishDraftLabel?: string; 875 | 876 | addReaction?(document: TextDocument, comment: Comment, reaction: CommentReaction): Promise; 877 | deleteReaction?(document: TextDocument, comment: Comment, reaction: CommentReaction): Promise; 878 | reactionGroup?: CommentReaction[]; 879 | 880 | /** 881 | * Notify of updates to comment threads. 882 | */ 883 | onDidChangeCommentThreads: Event; 884 | } 885 | 886 | /** 887 | * DEPRECATED 888 | */ 889 | interface WorkspaceCommentProvider { 890 | /** 891 | * Provide all comments for the workspace. Comments are shown within the comments panel. Selecting a comment 892 | * from the panel runs the comment's command. 893 | */ 894 | provideWorkspaceComments(token: CancellationToken): Promise; 895 | 896 | /** 897 | * Notify of updates to comment threads. 898 | */ 899 | onDidChangeCommentThreads: Event; 900 | } 901 | 902 | /** 903 | * Stay in proposed 904 | */ 905 | export interface CommentReactionProvider { 906 | availableReactions: CommentReaction[]; 907 | toggleReaction?(document: TextDocument, comment: Comment, reaction: CommentReaction): Promise; 908 | } 909 | 910 | export interface CommentThread { 911 | /** 912 | * The uri of the document the thread has been created on. 913 | */ 914 | readonly resource: Uri; 915 | 916 | /** 917 | * Optional additonal commands. 918 | * 919 | * `additionalCommands` are the secondary actions rendered on Comment Widget. 920 | */ 921 | additionalCommands?: Command[]; 922 | 923 | /** 924 | * The command to be executed when users try to delete the comment thread. Currently, this is only called 925 | * when the user collapses a comment thread that has no comments in it. 926 | */ 927 | deleteCommand?: Command; 928 | } 929 | 930 | 931 | export interface CommentController { 932 | /** 933 | * Optional reaction provider 934 | * Stay in proposed. 935 | */ 936 | reactionProvider?: CommentReactionProvider; 937 | } 938 | 939 | namespace workspace { 940 | /** 941 | * DEPRECATED 942 | * Use vscode.comment.createCommentController instead. 943 | */ 944 | export function registerDocumentCommentProvider(provider: DocumentCommentProvider): Disposable; 945 | /** 946 | * DEPRECATED 947 | * Use vscode.comment.createCommentController instead and we don't differentiate document comments and workspace comments anymore. 948 | */ 949 | export function registerWorkspaceCommentProvider(provider: WorkspaceCommentProvider): Disposable; 950 | } 951 | 952 | /** 953 | * A collection of [comments](#Comment) representing a conversation at a particular range in a document. 954 | */ 955 | export interface CommentThread { 956 | /** 957 | * A unique identifier of the comment thread. 958 | */ 959 | readonly id: string; 960 | 961 | /** 962 | * The uri of the document the thread has been created on. 963 | */ 964 | readonly uri: Uri; 965 | 966 | /** 967 | * Optional accept input command 968 | * 969 | * `acceptInputCommand` is the default action rendered on Comment Widget, which is always placed rightmost. 970 | * This command will be invoked when users the user accepts the value in the comment editor. 971 | * This command will disabled when the comment editor is empty. 972 | */ 973 | acceptInputCommand?: Command; 974 | } 975 | 976 | /** 977 | * A comment is displayed within the editor or the Comments Panel, depending on how it is provided. 978 | */ 979 | export interface Comment { 980 | /** 981 | * The id of the comment 982 | */ 983 | id: string; 984 | 985 | /** 986 | * The command to be executed if the comment is selected in the Comments Panel 987 | */ 988 | selectCommand?: Command; 989 | 990 | /** 991 | * The command to be executed when users try to save the edits to the comment 992 | */ 993 | editCommand?: Command; 994 | } 995 | 996 | /** 997 | * The comment input box in Comment Widget. 998 | */ 999 | export interface CommentInputBox { 1000 | /** 1001 | * Setter and getter for the contents of the comment input box 1002 | */ 1003 | value: string; 1004 | } 1005 | 1006 | /** 1007 | * Commenting range provider for a [comment controller](#CommentController). 1008 | */ 1009 | export interface CommentingRangeProvider { 1010 | /** 1011 | * Provide a list of ranges which allow new comment threads creation or null for a given document 1012 | */ 1013 | provideCommentingRanges(document: TextDocument, token: CancellationToken): ProviderResult; 1014 | } 1015 | 1016 | export interface EmptyCommentThreadFactory { 1017 | /** 1018 | * The method `createEmptyCommentThread` is called when users attempt to create new comment thread from the gutter or command palette. 1019 | * Extensions still need to call `createCommentThread` inside this call when appropriate. 1020 | */ 1021 | createEmptyCommentThread(document: TextDocument, range: Range): ProviderResult; 1022 | } 1023 | 1024 | /** 1025 | * A comment controller is able to provide [comments](#CommentThread) support to the editor and 1026 | * provide users various ways to interact with comments. 1027 | */ 1028 | export interface CommentController { 1029 | 1030 | /** 1031 | * The active [comment input box](#CommentInputBox) or `undefined`. The active `inputBox` is the input box of 1032 | * the comment thread widget that currently has focus. It's `undefined` when the focus is not in any CommentInputBox. 1033 | */ 1034 | readonly inputBox?: CommentInputBox; 1035 | 1036 | /** 1037 | * Create a [comment thread](#CommentThread). The comment thread will be displayed in visible text editors (if the resource matches) 1038 | * and Comments Panel once created. 1039 | * 1040 | * @param id An `id` for the comment thread. 1041 | * @param resource The uri of the document the thread has been created on. 1042 | * @param range The range the comment thread is located within the document. 1043 | * @param comments The ordered comments of the thread. 1044 | */ 1045 | createCommentThread(id: string, resource: Uri, range: Range, comments: Comment[]): CommentThread; 1046 | 1047 | /** 1048 | * Optional new comment thread factory. 1049 | */ 1050 | emptyCommentThreadFactory?: EmptyCommentThreadFactory; 1051 | 1052 | /** 1053 | * Optional reaction provider 1054 | */ 1055 | reactionProvider?: CommentReactionProvider; 1056 | 1057 | /** 1058 | * Dispose this comment controller. 1059 | * 1060 | * Once disposed, all [comment threads](#CommentThread) created by this comment controller will also be removed from the editor 1061 | * and Comments Panel. 1062 | */ 1063 | dispose(): void; 1064 | } 1065 | 1066 | namespace comment { 1067 | /** 1068 | * Creates a new [comment controller](#CommentController) instance. 1069 | * 1070 | * @param id An `id` for the comment controller. 1071 | * @param label A human-readable string for the comment controller. 1072 | * @return An instance of [comment controller](#CommentController). 1073 | */ 1074 | export function createCommentController(id: string, label: string): CommentController; 1075 | } 1076 | 1077 | //#endregion 1078 | 1079 | //#region Terminal 1080 | 1081 | /** 1082 | * An [event](#Event) which fires when a [Terminal](#Terminal)'s dimensions change. 1083 | */ 1084 | export interface TerminalDimensionsChangeEvent { 1085 | /** 1086 | * The [terminal](#Terminal) for which the dimensions have changed. 1087 | */ 1088 | readonly terminal: Terminal; 1089 | /** 1090 | * The new value for the [terminal's dimensions](#Terminal.dimensions). 1091 | */ 1092 | readonly dimensions: TerminalDimensions; 1093 | } 1094 | 1095 | namespace window { 1096 | /** 1097 | * An event which fires when the [dimensions](#Terminal.dimensions) of the terminal change. 1098 | */ 1099 | export const onDidChangeTerminalDimensions: Event; 1100 | } 1101 | 1102 | export interface Terminal { 1103 | /** 1104 | * The current dimensions of the terminal. This will be `undefined` immediately after the 1105 | * terminal is created as the dimensions are not known until shortly after the terminal is 1106 | * created. 1107 | */ 1108 | readonly dimensions: TerminalDimensions | undefined; 1109 | 1110 | /** 1111 | * Fires when the terminal's pty slave pseudo-device is written to. In other words, this 1112 | * provides access to the raw data stream from the process running within the terminal, 1113 | * including VT sequences. 1114 | */ 1115 | readonly onDidWriteData: Event; 1116 | } 1117 | 1118 | 1119 | export interface TerminalOptions { 1120 | /** 1121 | * When enabled the terminal will run the process as normal but not be surfaced to the user 1122 | * until `Terminal.show` is called. The typical usage for this is when you need to run 1123 | * something that may need interactivity but only want to tell the user about it when 1124 | * interaction is needed. Note that the terminals will still be exposed to all extensions 1125 | * as normal. 1126 | */ 1127 | runInBackground?: boolean; 1128 | } 1129 | 1130 | /** 1131 | * Represents the dimensions of a terminal. 1132 | */ 1133 | export interface TerminalDimensions { 1134 | /** 1135 | * The number of columns in the terminal. 1136 | */ 1137 | readonly columns: number; 1138 | 1139 | /** 1140 | * The number of rows in the terminal. 1141 | */ 1142 | readonly rows: number; 1143 | } 1144 | 1145 | /** 1146 | * Represents a terminal without a process where all interaction and output in the terminal is 1147 | * controlled by an extension. This is similar to an output window but has the same VT sequence 1148 | * compatibility as the regular terminal. 1149 | * 1150 | * Note that an instance of [Terminal](#Terminal) will be created when a TerminalRenderer is 1151 | * created with all its APIs available for use by extensions. When using the Terminal object 1152 | * of a TerminalRenderer it acts just like normal only the extension that created the 1153 | * TerminalRenderer essentially acts as a process. For example when an 1154 | * [Terminal.onDidWriteData](#Terminal.onDidWriteData) listener is registered, that will fire 1155 | * when [TerminalRenderer.write](#TerminalRenderer.write) is called. Similarly when 1156 | * [Terminal.sendText](#Terminal.sendText) is triggered that will fire the 1157 | * [TerminalRenderer.onDidAcceptInput](#TerminalRenderer.onDidAcceptInput) event. 1158 | * 1159 | * @deprecated Use [virtual processes](#TerminalVirtualProcess) instead. 1160 | * 1161 | * **Example:** Create a terminal renderer, show it and write hello world in red 1162 | * ```typescript 1163 | * const renderer = window.createTerminalRenderer('foo'); 1164 | * renderer.terminal.then(t => t.show()); 1165 | * renderer.write('\x1b[31mHello world\x1b[0m'); 1166 | * ``` 1167 | */ 1168 | export interface TerminalRenderer { 1169 | /** 1170 | * The name of the terminal, this will appear in the terminal selector. 1171 | * @deprecated Use [virtual processes](#TerminalVirtualProcess) instead. 1172 | */ 1173 | name: string; 1174 | 1175 | /** 1176 | * The dimensions of the terminal, the rows and columns of the terminal can only be set to 1177 | * a value smaller than the maximum value, if this is undefined the terminal will auto fit 1178 | * to the maximum value [maximumDimensions](TerminalRenderer.maximumDimensions). 1179 | * 1180 | * @deprecated Use [virtual processes](#TerminalVirtualProcess) instead. 1181 | * 1182 | * **Example:** Override the dimensions of a TerminalRenderer to 20 columns and 10 rows 1183 | * ```typescript 1184 | * terminalRenderer.dimensions = { 1185 | * cols: 20, 1186 | * rows: 10 1187 | * }; 1188 | * ``` 1189 | */ 1190 | dimensions: TerminalDimensions | undefined; 1191 | 1192 | /** 1193 | * The maximum dimensions of the terminal, this will be undefined immediately after a 1194 | * terminal renderer is created and also until the terminal becomes visible in the UI. 1195 | * Listen to [onDidChangeMaximumDimensions](TerminalRenderer.onDidChangeMaximumDimensions) 1196 | * to get notified when this value changes. 1197 | * 1198 | * @deprecated Use [virtual processes](#TerminalVirtualProcess) instead. 1199 | */ 1200 | readonly maximumDimensions: TerminalDimensions | undefined; 1201 | 1202 | /** 1203 | * The corresponding [Terminal](#Terminal) for this TerminalRenderer. 1204 | * 1205 | * @deprecated Use [virtual processes](#TerminalVirtualProcess) instead. 1206 | */ 1207 | readonly terminal: Terminal; 1208 | 1209 | /** 1210 | * Write text to the terminal. Unlike [Terminal.sendText](#Terminal.sendText) which sends 1211 | * text to the underlying _process_, this will write the text to the terminal itself. 1212 | * 1213 | * @param text The text to write. 1214 | * @deprecated Use [virtual processes](#TerminalVirtualProcess) instead. 1215 | * 1216 | * **Example:** Write red text to the terminal 1217 | * ```typescript 1218 | * terminalRenderer.write('\x1b[31mHello world\x1b[0m'); 1219 | * ``` 1220 | * 1221 | * **Example:** Move the cursor to the 10th row and 20th column and write an asterisk 1222 | * ```typescript 1223 | * terminalRenderer.write('\x1b[10;20H*'); 1224 | * ``` 1225 | */ 1226 | write(text: string): void; 1227 | 1228 | /** 1229 | * An event which fires on keystrokes in the terminal or when an extension calls 1230 | * [Terminal.sendText](#Terminal.sendText). Keystrokes are converted into their 1231 | * corresponding VT sequence representation. 1232 | * 1233 | * @deprecated Use [virtual processes](#TerminalVirtualProcess) instead. 1234 | * 1235 | * **Example:** Simulate interaction with the terminal from an outside extension or a 1236 | * workbench command such as `workbench.action.terminal.runSelectedText` 1237 | * ```typescript 1238 | * const terminalRenderer = window.createTerminalRenderer('test'); 1239 | * terminalRenderer.onDidAcceptInput(data => { 1240 | * console.log(data); // 'Hello world' 1241 | * }); 1242 | * terminalRenderer.terminal.sendText('Hello world'); 1243 | * ``` 1244 | */ 1245 | readonly onDidAcceptInput: Event; 1246 | 1247 | /** 1248 | * An event which fires when the [maximum dimensions](#TerminalRenderer.maximumDimensions) of 1249 | * the terminal renderer change. 1250 | * 1251 | * @deprecated Use [virtual processes](#TerminalVirtualProcess) instead. 1252 | */ 1253 | readonly onDidChangeMaximumDimensions: Event; 1254 | } 1255 | 1256 | export namespace window { 1257 | /** 1258 | * Create a [TerminalRenderer](#TerminalRenderer). 1259 | * 1260 | * @param name The name of the terminal renderer, this shows up in the terminal selector. 1261 | * @deprecated Use [virtual processes](#TerminalVirtualProcess) instead. 1262 | */ 1263 | export function createTerminalRenderer(name: string): TerminalRenderer; 1264 | } 1265 | 1266 | //#endregion 1267 | 1268 | //#region Terminal virtual process 1269 | 1270 | export namespace window { 1271 | /** 1272 | * Creates a [Terminal](#Terminal) where an extension acts as the process. 1273 | * 1274 | * @param options A [TerminalVirtualProcessOptions](#TerminalVirtualProcessOptions) object describing the 1275 | * characteristics of the new terminal. 1276 | * @return A new Terminal. 1277 | */ 1278 | export function createTerminal(options: TerminalVirtualProcessOptions): Terminal; 1279 | } 1280 | 1281 | /** 1282 | * Value-object describing what options a virtual process terminal should use. 1283 | */ 1284 | export interface TerminalVirtualProcessOptions { 1285 | /** 1286 | * A human-readable string which will be used to represent the terminal in the UI. 1287 | */ 1288 | name: string; 1289 | 1290 | /** 1291 | * An implementation of [TerminalVirtualProcess](#TerminalVirtualProcess) that allows an 1292 | * extension to act as a terminal's backing process. 1293 | */ 1294 | virtualProcess: TerminalVirtualProcess; 1295 | } 1296 | 1297 | /** 1298 | * Defines the interface of a terminal virtual process, enabling extensions to act as a process 1299 | * in the terminal. 1300 | */ 1301 | interface TerminalVirtualProcess { 1302 | /** 1303 | * An event that when fired will write data to the terminal. Unlike 1304 | * [Terminal.sendText](#Terminal.sendText) which sends text to the underlying _process_, 1305 | * this will write the text to the terminal itself. 1306 | * 1307 | * **Example:** Write red text to the terminal 1308 | * ```typescript 1309 | * const writeEmitter = new vscode.EventEmitter(); 1310 | * const virtualProcess: TerminalVirtualProcess = { 1311 | * onDidWrite: writeEmitter.event 1312 | * }; 1313 | * vscode.window.createTerminal({ name: 'My terminal', virtualProcess }); 1314 | * writeEmitter.fire('\x1b[31mHello world\x1b[0m'); 1315 | * ``` 1316 | * 1317 | * **Example:** Move the cursor to the 10th row and 20th column and write an asterisk 1318 | * ```typescript 1319 | * writeEmitter.fire('\x1b[10;20H*'); 1320 | * ``` 1321 | */ 1322 | onDidWrite: Event; 1323 | 1324 | /** 1325 | * An event that when fired allows overriding the [dimensions](#Terminal.dimensions) of the 1326 | * terminal. Note that when set the overridden dimensions will only take effect when they 1327 | * are lower than the actual dimensions of the terminal (ie. there will never be a scroll 1328 | * bar). Set to `undefined` for the terminal to go back to the regular dimensions. 1329 | * 1330 | * **Example:** Override the dimensions of a terminal to 20 columns and 10 rows 1331 | * ```typescript 1332 | * const dimensionsEmitter = new vscode.EventEmitter(); 1333 | * const virtualProcess: TerminalVirtualProcess = { 1334 | * onDidWrite: writeEmitter.event, 1335 | * onDidOverrideDimensions: dimensionsEmitter.event 1336 | * }; 1337 | * vscode.window.createTerminal({ name: 'My terminal', virtualProcess }); 1338 | * dimensionsEmitter.fire({ 1339 | * columns: 20, 1340 | * rows: 10 1341 | * }); 1342 | * ``` 1343 | */ 1344 | onDidOverrideDimensions?: Event; 1345 | 1346 | /** 1347 | * An event that when fired will exit the process with an exit code, this will behave the 1348 | * same for a virtual process as when a regular process exits with an exit code. 1349 | * 1350 | * **Example:** Exit with an exit code of `0` if the y key is pressed, otherwise `1`. 1351 | * ```typescript 1352 | * const writeEmitter = new vscode.EventEmitter(); 1353 | * const exitEmitter = new vscode.EventEmitter(); 1354 | * const virtualProcess: TerminalVirtualProcess = { 1355 | * onDidWrite: writeEmitter.event, 1356 | * input: data => exitEmitter.fire(data === 'y' ? 0 : 1) 1357 | * }; 1358 | * vscode.window.createTerminal({ name: 'Exit example', virtualProcess }); 1359 | * writeEmitter.fire('Press y to exit successfully'); 1360 | */ 1361 | onDidExit?: Event; 1362 | 1363 | /** 1364 | * Implement to handle keystrokes in the terminal or when an extension calls 1365 | * [Terminal.sendText](#Terminal.sendText). Keystrokes are converted into their 1366 | * corresponding VT sequence representation. 1367 | * 1368 | * @param data The sent data. 1369 | * 1370 | * **Example:** Echo input in the terminal. The sequence for enter (`\r`) is translated to 1371 | * CRLF to go to a new line and move the cursor to the start of the line. 1372 | * ```typescript 1373 | * const writeEmitter = new vscode.EventEmitter(); 1374 | * const virtualProcess: TerminalVirtualProcess = { 1375 | * onDidWrite: writeEmitter.event, 1376 | * input: data => writeEmitter.fire(data === '\r' ? '\r\n' : data) 1377 | * }; 1378 | * vscode.window.createTerminal({ name: 'Local echo', virtualProcess }); 1379 | * ``` 1380 | */ 1381 | input?(data: string): void; 1382 | 1383 | /** 1384 | * Implement to handle when the number of rows and columns that fit into the terminal panel 1385 | * changes, for example when font size changes or when the panel is resized. The initial 1386 | * state of a terminal's dimensions should be treated as `undefined` until this is triggered 1387 | * as the size of a terminal isn't know until it shows up in the user interface. 1388 | * 1389 | * @param dimensions The new dimensions. 1390 | */ 1391 | setDimensions?(dimensions: TerminalDimensions): void; 1392 | 1393 | /** 1394 | * Implement to handle when the terminal shuts down by an act of the user. 1395 | */ 1396 | shutdown?(): void; 1397 | } 1398 | 1399 | //#endregion 1400 | 1401 | //#region Joh -> exclusive document filters 1402 | 1403 | export interface DocumentFilter { 1404 | exclusive?: boolean; 1405 | } 1406 | 1407 | //#endregion 1408 | 1409 | //#region mjbvz,joh: https://github.com/Microsoft/vscode/issues/43768 1410 | export interface FileRenameEvent { 1411 | readonly oldUri: Uri; 1412 | readonly newUri: Uri; 1413 | } 1414 | 1415 | export interface FileWillRenameEvent { 1416 | readonly oldUri: Uri; 1417 | readonly newUri: Uri; 1418 | waitUntil(thenable: Thenable): void; 1419 | } 1420 | 1421 | export namespace workspace { 1422 | export const onWillRenameFile: Event; 1423 | export const onDidRenameFile: Event; 1424 | } 1425 | //#endregion 1426 | 1427 | //#region Alex - OnEnter enhancement 1428 | export interface OnEnterRule { 1429 | /** 1430 | * This rule will only execute if the text above the this line matches this regular expression. 1431 | */ 1432 | oneLineAboveText?: RegExp; 1433 | } 1434 | //#endregion 1435 | 1436 | //#region Tree View 1437 | 1438 | export interface TreeView { 1439 | 1440 | /** 1441 | * An optional human-readable message that will be rendered in the view. 1442 | */ 1443 | message?: string | MarkdownString; 1444 | 1445 | } 1446 | 1447 | /** 1448 | * Label describing the [Tree item](#TreeItem) 1449 | */ 1450 | export interface TreeItemLabel { 1451 | 1452 | /** 1453 | * A human-readable string describing the [Tree item](#TreeItem). 1454 | */ 1455 | label: string; 1456 | 1457 | /** 1458 | * Ranges in the label to highlight. A range is defined as a tuple of two number where the 1459 | * first is the inclusive start index and the second the exclusive end index 1460 | */ 1461 | highlights?: [number, number][]; 1462 | 1463 | } 1464 | 1465 | export class TreeItem2 extends TreeItem { 1466 | /** 1467 | * Label describing this item. When `falsy`, it is derived from [resourceUri](#TreeItem.resourceUri). 1468 | */ 1469 | label?: string | TreeItemLabel | /* for compilation */ any; 1470 | 1471 | /** 1472 | * @param label Label describing this item 1473 | * @param collapsibleState [TreeItemCollapsibleState](#TreeItemCollapsibleState) of the tree item. Default is [TreeItemCollapsibleState.None](#TreeItemCollapsibleState.None) 1474 | */ 1475 | constructor(label: TreeItemLabel, collapsibleState?: TreeItemCollapsibleState); 1476 | } 1477 | //#endregion 1478 | 1479 | /** 1480 | * Class used to execute an extension callback as a task. 1481 | */ 1482 | export class CustomExecution { 1483 | /** 1484 | * @param callback The callback that will be called when the extension callback task is executed. 1485 | */ 1486 | constructor(callback: (terminalRenderer: TerminalRenderer, cancellationToken: CancellationToken, thisArg?: any) => Thenable); 1487 | 1488 | /** 1489 | * The callback used to execute the task. 1490 | * @param terminalRenderer Used by the task to render output and receive input. 1491 | * @param cancellationToken Cancellation used to signal a cancel request to the executing task. 1492 | * @returns The callback should return '0' for success and a non-zero value for failure. 1493 | */ 1494 | callback: (terminalRenderer: TerminalRenderer, cancellationToken: CancellationToken, thisArg?: any) => Thenable; 1495 | } 1496 | 1497 | /** 1498 | * A task to execute 1499 | */ 1500 | export class Task2 extends Task { 1501 | /** 1502 | * Creates a new task. 1503 | * 1504 | * @param definition The task definition as defined in the taskDefinitions extension point. 1505 | * @param scope Specifies the task's scope. It is either a global or a workspace task or a task for a specific workspace folder. 1506 | * @param name The task's name. Is presented in the user interface. 1507 | * @param source The task's source (e.g. 'gulp', 'npm', ...). Is presented in the user interface. 1508 | * @param execution The process or shell execution. 1509 | * @param problemMatchers the names of problem matchers to use, like '$tsc' 1510 | * or '$eslint'. Problem matchers can be contributed by an extension using 1511 | * the `problemMatchers` extension point. 1512 | */ 1513 | constructor(taskDefinition: TaskDefinition, scope: WorkspaceFolder | TaskScope.Global | TaskScope.Workspace, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution, problemMatchers?: string | string[]); 1514 | 1515 | /** 1516 | * The task's execution engine 1517 | */ 1518 | execution2?: ProcessExecution | ShellExecution | CustomExecution; 1519 | } 1520 | 1521 | //#region Tasks 1522 | export interface TaskPresentationOptions { 1523 | /** 1524 | * Controls whether the task is executed in a specific terminal group using split panes. 1525 | */ 1526 | group?: string; 1527 | } 1528 | //#endregion 1529 | 1530 | // #region Ben - status bar item with ID and Name 1531 | 1532 | export namespace window { 1533 | 1534 | /** 1535 | * Options to configure the status bar item. 1536 | */ 1537 | export interface StatusBarItemOptions { 1538 | 1539 | /** 1540 | * A unique identifier of the status bar item. The identifier 1541 | * is for example used to allow a user to show or hide the 1542 | * status bar item in the UI. 1543 | */ 1544 | id: string; 1545 | 1546 | /** 1547 | * A human readable name of the status bar item. The name is 1548 | * for example used as a label in the UI to show or hide the 1549 | * status bar item. 1550 | */ 1551 | name: string; 1552 | 1553 | /** 1554 | * The alignment of the status bar item. 1555 | */ 1556 | alignment?: StatusBarAlignment; 1557 | 1558 | /** 1559 | * The priority of the status bar item. Higher value means the item should 1560 | * be shown more to the left. 1561 | */ 1562 | priority?: number; 1563 | } 1564 | 1565 | /** 1566 | * Creates a status bar [item](#StatusBarItem). 1567 | * 1568 | * @param options The options of the item. If not provided, some default values 1569 | * will be assumed. For example, the `StatusBarItemOptions.id` will be the id 1570 | * of the extension and the `StatusBarItemOptions.name` will be the extension name. 1571 | * @return A new status bar item. 1572 | */ 1573 | export function createStatusBarItem(options?: StatusBarItemOptions): StatusBarItem; 1574 | } 1575 | 1576 | //#endregion 1577 | 1578 | //#region Webview Resource Roots 1579 | 1580 | export interface Webview { 1581 | /** 1582 | * Root url from which local resources are loaded inside of webviews. 1583 | * 1584 | * This is `vscode-resource:` when vscode is run on the desktop. When vscode is run 1585 | * on the web, this points to a server endpoint. 1586 | */ 1587 | readonly resourceRoot: Thenable; 1588 | } 1589 | 1590 | //#endregion 1591 | 1592 | 1593 | //#region Joh - read/write files of any scheme 1594 | 1595 | export interface FileSystem { 1596 | stat(uri: Uri): Thenable; 1597 | readDirectory(uri: Uri): Thenable<[string, FileType][]>; 1598 | createDirectory(uri: Uri): Thenable; 1599 | readFile(uri: Uri): Thenable; 1600 | writeFile(uri: Uri, content: Uint8Array, options?: { create: boolean, overwrite: boolean }): Thenable; 1601 | delete(uri: Uri, options?: { recursive: boolean }): Thenable; 1602 | rename(source: Uri, target: Uri, options?: { overwrite: boolean }): Thenable; 1603 | copy(source: Uri, target: Uri, options?: { overwrite: boolean }): Thenable; 1604 | } 1605 | 1606 | export namespace workspace { 1607 | 1608 | export const fs: FileSystem; 1609 | } 1610 | 1611 | //#endregion 1612 | } 1613 | --------------------------------------------------------------------------------