├── .gitignore ├── logo.png ├── resources ├── demo.nes ├── d1.svg ├── d2.svg ├── d0.svg ├── rom.svg └── anes.svg ├── view ├── img │ ├── alipay.png │ ├── keyboard.png │ └── wechat.png ├── vscode.call.js ├── controller.js ├── index.html └── main.js ├── .vscodeignore ├── .vscode ├── extensions.json └── launch.json ├── jsconfig.json ├── CHANGELOG.md ├── README.en.md ├── test ├── suite │ ├── extension.test.js │ └── index.js └── runTest.js ├── download.js ├── .eslintrc.json ├── README.md ├── test.js ├── vsc-extension-quickstart.md ├── romRemoteTree.js ├── fileUtil.js ├── package.json ├── romLocalTree.js └── extension.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode-test/ 3 | *.vsix 4 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gamedilong/anes/HEAD/logo.png -------------------------------------------------------------------------------- /resources/demo.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gamedilong/anes/HEAD/resources/demo.nes -------------------------------------------------------------------------------- /view/img/alipay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gamedilong/anes/HEAD/view/img/alipay.png -------------------------------------------------------------------------------- /view/img/keyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gamedilong/anes/HEAD/view/img/keyboard.png -------------------------------------------------------------------------------- /view/img/wechat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gamedilong/anes/HEAD/view/img/wechat.png -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | test/** 4 | .gitignore 5 | vsc-extension-quickstart.md 6 | **/jsconfig.json 7 | **/*.map 8 | **/.eslintrc.json 9 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "dbaeumer.vscode-eslint" 6 | ] 7 | } -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "checkJs": false, /* Typecheck .js files. */ 6 | "lib": [ 7 | "es6" 8 | ] 9 | }, 10 | "exclude": [ 11 | "node_modules" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to the "anes" extension will be documented in this file. 4 | 5 | Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. 6 | 7 | ## [Unreleased] 8 | 9 | - Initial release -------------------------------------------------------------------------------- /README.en.md: -------------------------------------------------------------------------------- 1 | # anes README 2 | 3 | ANES is a vscode nes game plugin 4 | 5 | # Issues 6 | 7 | Currently, the extension is in the very initial phase. If you find any bug or have any suggestion/feature request, please submit the issues to the GitHub Repo. Or you can send email to 1104238614@qq.com. -------------------------------------------------------------------------------- /test/suite/extension.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | // You can import and use all API from the 'vscode' module 4 | // as well as import your extension to test it 5 | const vscode = require('vscode'); 6 | // const myExtension = require('../extension'); 7 | 8 | suite('Extension Test Suite', () => { 9 | vscode.window.showInformationMessage('Start all tests.'); 10 | 11 | test('Sample test', () => { 12 | assert.equal(-1, [1, 2, 3].indexOf(5)); 13 | assert.equal(-1, [1, 2, 3].indexOf(0)); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /download.js: -------------------------------------------------------------------------------- 1 | var request = require("request"); 2 | var fs = require("fs"); 3 | 4 | 5 | function download(url, filePath, callback){ 6 | let stream = fs.createWriteStream(filePath); 7 | request({url:url, timeout:10000}).pipe(stream).on("close", function (err) { 8 | console.log("文件[" + filePath + "]下载完毕"); 9 | callback(err); 10 | }); 11 | } 12 | 13 | 14 | // download('https://github.com/gamedilong/anes-repository/archive/master.zip','master.zip'); 15 | 16 | module.exports = { 17 | download 18 | } 19 | 20 | -------------------------------------------------------------------------------- /resources/d1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": false, 4 | "commonjs": true, 5 | "es6": true, 6 | "node": true, 7 | "mocha": true 8 | }, 9 | "parserOptions": { 10 | "ecmaVersion": 2018, 11 | "ecmaFeatures": { 12 | "jsx": true 13 | }, 14 | "sourceType": "module" 15 | }, 16 | "rules": { 17 | "no-const-assign": "warn", 18 | "no-this-before-super": "warn", 19 | "no-undef": "warn", 20 | "no-unreachable": "warn", 21 | "no-unused-vars": "warn", 22 | "constructor-super": "warn", 23 | "valid-typeof": "warn" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 小霸王 2 | 3 | 小霸王是一款基于vscode的nes游戏插件,能让你在紧张的开发之余在vscode里发松身心。通过劳逸结合,提升开发效率。 4 | # 功能 5 | ## 1. 本地仓库 6 | * 可以通过local菜单上的添加按钮添加本地nes rom文件。添加完毕后,会在local列表显示,点击即可开始游戏。 7 | * 本地仓库列表还支持 右键菜单 改名,删除功能 8 | 9 | ## 2.远程仓库 10 | * 选择想要玩的游戏,右键菜单下载。下载完成后,会加载到local菜单。点击即可完。 11 | 12 | * 远程仓库通过github维护,会不定时的更新一些新的游戏。可以通过remote上的refresh刷新按钮同步。 13 | 14 | * 远程rom仓库配置地址 https://github.com/gamedilong/anes-repository 15 | 16 | # Issues 17 | 18 | 这个插件是一个很初始版如果发现了任何的bug或者有任何建议, 请提交 issues to 到GitHub Repo https://github.com/gamedilong/anes-repository. 或者可以直接邮件到 1104238614@qq.com. -------------------------------------------------------------------------------- /resources/d2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/d0.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/runTest.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const { runTests } = require('vscode-test'); 4 | 5 | async function main() { 6 | try { 7 | // The folder containing the Extension Manifest package.json 8 | // Passed to `--extensionDevelopmentPath` 9 | const extensionDevelopmentPath = path.resolve(__dirname, '../'); 10 | 11 | // The path to the extension test script 12 | // Passed to --extensionTestsPath 13 | const extensionTestsPath = path.resolve(__dirname, './suite/index'); 14 | 15 | // Download VS Code, unzip it and run the integration test 16 | await runTests({ extensionDevelopmentPath, extensionTestsPath }); 17 | } catch (err) { 18 | console.error('Failed to run tests'); 19 | process.exit(1); 20 | } 21 | } 22 | 23 | main(); 24 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that launches the extension inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": [ 14 | "--extensionDevelopmentPath=${workspaceFolder}" 15 | ] 16 | }, 17 | { 18 | "name": "Extension Tests", 19 | "type": "extensionHost", 20 | "request": "launch", 21 | "runtimeExecutable": "${execPath}", 22 | "args": [ 23 | "--extensionDevelopmentPath=${workspaceFolder}", 24 | "--extensionTestsPath=${workspaceFolder}/test/suite/index" 25 | ] 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /view/vscode.call.js: -------------------------------------------------------------------------------- 1 | 2 | var vscode = acquireVsCodeApi(); 3 | var callbacks = {}; 4 | 5 | /** 6 | * 调用vscode原生api 7 | * @param data 可以是类似 {cmd: 'xxx', param1: 'xxx'},也可以直接是 cmd 字符串 8 | * @param cb 可选的回调函数 9 | */ 10 | function callVscode(data, cb) { 11 | if (typeof data === 'string') { 12 | data = { cmd: data }; 13 | } 14 | if (cb) { 15 | // 时间戳加上5位随机数 16 | const cbid = Date.now() + '' + Math.round(Math.random() * 100000); 17 | callbacks[cbid] = cb; 18 | data.cbid = cbid; 19 | } 20 | vscode.postMessage(data); 21 | } 22 | 23 | window.addEventListener('message', event => { 24 | const message = event.data; 25 | switch (message.cmd) { 26 | case 'vscodeCallback': 27 | (callbacks[message.cbid] || function () { })(message.data); 28 | delete callbacks[message.cbid]; 29 | break; 30 | default: break; 31 | } 32 | }); -------------------------------------------------------------------------------- /resources/rom.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/suite/index.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const Mocha = require('mocha'); 3 | const glob = require('glob'); 4 | 5 | function run() { 6 | // Create the mocha test 7 | const mocha = new Mocha({ 8 | ui: 'tdd', 9 | color: true 10 | }); 11 | 12 | const testsRoot = path.resolve(__dirname, '..'); 13 | 14 | return new Promise((c, e) => { 15 | glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { 16 | if (err) { 17 | return e(err); 18 | } 19 | 20 | // Add files to the test suite 21 | files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); 22 | 23 | try { 24 | // Run the mocha test 25 | mocha.run(failures => { 26 | if (failures > 0) { 27 | e(new Error(`${failures} tests failed.`)); 28 | } else { 29 | c(); 30 | } 31 | }); 32 | } catch (err) { 33 | console.error(err); 34 | e(err); 35 | } 36 | }); 37 | }); 38 | } 39 | 40 | module.exports = { 41 | run 42 | }; 43 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | let color = [0x525252, 0xB40000, 0xA00000, 0xB1003D, 0x740069, 0x00005B, 0x00005F, 0x001840, 0x002F10, 0x084A08, 0x006700, 0x124200, 0x6D2800, 0x000000, 0x000000, 0x000000, 0xC4D5E7, 0xFF4000, 0xDC0E22, 0xFF476B, 0xD7009F, 0x680AD7, 0x0019BC, 0x0054B1, 0x006A5B, 0x008C03, 0x00AB00, 0x2C8800, 0xA47200, 0x000000, 0x000000, 0x000000, 0xF8F8F8, 0xFFAB3C, 0xFF7981, 0xFF5BC5, 0xFF48F2, 0xDF49FF, 0x476DFF, 0x00B4F7, 0x00E0FF, 0x00E375, 0x03F42B, 0x78B82E, 0xE5E218, 0x787878, 0x000000, 0x000000, 0xFFFFFF, 0xFFF2BE, 0xF8B8B8, 0xF8B8D8, 0xFFB6FF, 0xFFC3FF, 0xC7D1FF, 0x9ADAFF, 0x88EDF8, 0x83FFDD, 0xB8F8B8, 0xF5F8AC, 0xFFFFB0, 0xF8D8F8, 0x000000, 0x000000]; 2 | 3 | for(var i = 0 ; i < color.length ; i ++){ 4 | console.log(getRed(color[i]),getGreen(color[i]),getBlue(color[i])); 5 | } 6 | function getRed (rgb) { 7 | return (rgb >> 16) & 0xff; 8 | } 9 | 10 | function getGreen (rgb) { 11 | return (rgb >> 8) & 0xff; 12 | } 13 | 14 | function getBlue (rgb) { 15 | return rgb & 0xff; 16 | } -------------------------------------------------------------------------------- /resources/anes.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /view/controller.js: -------------------------------------------------------------------------------- 1 | function showControll(){ 2 | console.log('showControll') 3 | var modalbox = document.getElementById('modalbox'); 4 | console.log(modalbox) 5 | modalbox.style.display = 'block'; 6 | 7 | showKeyMap(); 8 | } 9 | 10 | 11 | function showKeyMap(){ 12 | document.getElementById('key1_left').innerText= NeskeyMap.BUTTON_UP; 13 | document.getElementById('key1_right'); 14 | document.getElementById('key1_up'); 15 | document.getElementById('key1_down'); 16 | document.getElementById('key1_a'); 17 | document.getElementById('key1_b'); 18 | document.getElementById('key1_down'); 19 | document.getElementById('key1_select'); 20 | } 21 | 22 | function closeControllSet(){ 23 | var modalbox = document.getElementById('modalbox'); 24 | modalbox.style.display = 'none'; 25 | } 26 | 27 | function changeGameColor(){ 28 | if(nes){ 29 | console.log('changeGameColor') 30 | nes.moyu = !nes.moyu 31 | nes.reloadROM(); 32 | } 33 | } 34 | 35 | function showImgMask(){ 36 | var imgMask = document.getElementById('imgMask'); 37 | imgMask.style.display = "block"; 38 | } 39 | 40 | 41 | function closeMask(){ 42 | var imgMask = document.getElementById('imgMask'); 43 | imgMask.style.display = "none"; 44 | } -------------------------------------------------------------------------------- /vsc-extension-quickstart.md: -------------------------------------------------------------------------------- 1 | # Welcome to your VS Code Extension 2 | 3 | ## What's in the folder 4 | 5 | * This folder contains all of the files necessary for your extension. 6 | * `package.json` - this is the manifest file in which you declare your extension and command. 7 | * The sample plugin registers a command and defines its title and command name. With this information VS Code can show the command in the command palette. It doesn’t yet need to load the plugin. 8 | * `extension.js` - this is the main file where you will provide the implementation of your command. 9 | * The file exports one function, `activate`, which is called the very first time your extension is activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`. 10 | * We pass the function containing the implementation of the command as the second parameter to `registerCommand`. 11 | 12 | ## Get up and running straight away 13 | 14 | * Press `F5` to open a new window with your extension loaded. 15 | * Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`. 16 | * Set breakpoints in your code inside `extension.js` to debug your extension. 17 | * Find output from your extension in the debug console. 18 | 19 | ## Make changes 20 | 21 | * You can relaunch the extension from the debug toolbar after changing code in `extension.js`. 22 | * You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes. 23 | 24 | ## Explore the API 25 | 26 | * You can open the full set of our API when you open the file `node_modules/@types/vscode/index.d.ts`. 27 | 28 | ## Run tests 29 | 30 | * Open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Extension Tests`. 31 | * Press `F5` to run the tests in a new window with your extension loaded. 32 | * See the output of the test result in the debug console. 33 | * Make changes to `src/test/suite/extension.test.js` or create new test files inside the `test/suite` folder. 34 | * The provided test runner will only consider files matching the name pattern `**.test.ts`. 35 | * You can create folders inside the `test` folder to structure your tests any way you want. 36 | ## Go further 37 | 38 | * [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VSCode extension marketplace. 39 | * Automate builds by setting up [Continuous Integration](https://code.visualstudio.com/api/working-with-extensions/continuous-integration). 40 | -------------------------------------------------------------------------------- /romRemoteTree.js: -------------------------------------------------------------------------------- 1 | const vscode = require('vscode'); 2 | const FileUtil = require('./fileUtil'); 3 | const os = require('os'); 4 | const path = require('path'); 5 | const fs = require('fs'); 6 | 7 | class RomRemoteTree { 8 | constructor(context){ 9 | this.context = context; 10 | this.userRoot = os.homedir(); 11 | this._onDidChangeTreeData = new vscode.EventEmitter(); 12 | this.onDidChangeTreeData = this._onDidChangeTreeData.event; 13 | if(!FileUtil.pathExists(path.join(this.userRoot, '.anes','remote'))) { 14 | //if not exists create default ahost floder 15 | try{ 16 | //FileUtil.createDefaultAHostFloder(this.userRoot,this.context); 17 | }catch(e){ 18 | //vscode.window.showInformationMessage('Ahost need Administrator permission!'); 19 | } 20 | } 21 | } 22 | refresh(){ 23 | this._onDidChangeTreeData.fire(); 24 | } 25 | getTreeItem(element){ 26 | return element; 27 | } 28 | getChildren(element) { 29 | let remoteRomConfig = FileUtil.getRemoteRomConfig(this.userRoot); 30 | let remoteMeta = fs.readFileSync(path.join(this.userRoot, '.anes', 'remote','meta.json')); 31 | remoteMeta = remoteMeta.toString(); 32 | remoteMeta = JSON.parse(remoteMeta); 33 | let remoteMetaMap = {}; 34 | remoteMeta.forEach((meta)=>{ 35 | remoteMetaMap[meta.name] = meta; 36 | }); 37 | if(remoteRomConfig && remoteRomConfig.length >0){ 38 | let remoteRomList = []; 39 | remoteRomConfig.forEach((config)=>{ 40 | let meta = remoteMetaMap[config.name] ? remoteMetaMap[config.name] : {} 41 | remoteRomList.push(new DataItem({ 42 | label:config.name, 43 | url:config.url, 44 | children: null, 45 | command: null, 46 | downloadStatus: meta.downloadStatus, 47 | fileName: config.fileName, 48 | nesName: config.nesName 49 | })); 50 | }); 51 | return remoteRomList; 52 | }else{ 53 | return []; 54 | } 55 | } 56 | } 57 | 58 | class DataItem extends vscode.TreeItem{ 59 | constructor({label, url, children, command, downloadStatus,fileName,nesName}) { 60 | super(label, vscode.TreeItemCollapsibleState.None); 61 | downloadStatus = downloadStatus ? downloadStatus : 0; // download 0 downloading 1 downloaded 2 62 | console.log(`downloadStatus:${downloadStatus}`); 63 | this.downloadStatus = downloadStatus; 64 | this.iconPath = path.join(__filename,'..','resources', `d0.svg`); 65 | this.children = children; 66 | this.command = command; 67 | this.url = url; 68 | this.fileName = fileName; 69 | this.nesName= nesName; 70 | } 71 | } 72 | 73 | module.exports = RomRemoteTree; -------------------------------------------------------------------------------- /fileUtil.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | const META_FILE_NAME = 'meta.json'; 5 | 6 | const FileUtil = { 7 | pathExists: function(p) { 8 | try { 9 | fs.accessSync(p); 10 | } catch (err) { 11 | return false; 12 | } 13 | return true; 14 | }, 15 | createDefaultANesFloder:function(userRoot, context){ 16 | fs.mkdirSync(path.join(userRoot, '.anes')); 17 | fs.mkdirSync(path.join(userRoot, '.anes', 'local')); 18 | let defaultRomPath = FileUtil.getExtensionFileAbsolutePath(context, 'resources/demo.nes'); 19 | let basename= path.basename(defaultRomPath,'.nes'); 20 | let defaultRomDistPath = path.join(userRoot, '.anes','local',`${basename}.nes`,); 21 | 22 | this.copySync(defaultRomPath, defaultRomDistPath); 23 | this.writeMetaInfo(userRoot, 24 | [{ 25 | label: basename, 26 | path: defaultRomDistPath 27 | }] 28 | ) 29 | }, 30 | createDefaultRemoteFloder:function(){ 31 | //fs.mkdirSync(path.join(userRoot, '.anes')); 32 | fs.mkdirSync(path.join(userRoot, '.anes','remote')); 33 | 34 | }, 35 | writeMetaInfo:function(userRoot, metaInfo){ 36 | fs.writeFileSync(path.join(userRoot, '.anes', META_FILE_NAME),JSON.stringify(metaInfo)); 37 | }, 38 | addRomToResposity: function(userRoot, srcPath, gameName){ 39 | let fileName = path.basename(srcPath,'.nes'); 40 | let distPath = path.join(userRoot, '.anes', 'local',`${fileName}.nes`,); 41 | this.copySync(srcPath,distPath); 42 | let romConfigList = this.getRomConfigFileList(userRoot); 43 | romConfigList = romConfigList ? romConfigList : []; 44 | let exist = false; 45 | romConfigList.forEach(romConfig=>{ 46 | if(romConfig.label == fileName){ 47 | exist = true; 48 | } 49 | }) 50 | !exist && romConfigList.push({ 51 | label: gameName ? gameName : path.basename(srcPath), 52 | path: distPath 53 | }); 54 | this.writeMetaInfo(userRoot, romConfigList); 55 | }, 56 | getRomConfigFileList: function(userRoot){ 57 | let metaData = fs.readFileSync(path.join(userRoot, '.anes', META_FILE_NAME)); 58 | metaData = metaData.toString(); 59 | return JSON.parse(metaData); 60 | }, 61 | getRemoteRomConfig:function(userRoot){ 62 | let remoteList = fs.readFileSync(path.join(userRoot, '.anes','remote','anes-repository-master','list.json')); 63 | remoteList = remoteList.toString(); 64 | return JSON.parse(remoteList); 65 | }, 66 | getExtensionFileAbsolutePath:function(context, relativePath) { 67 | return path.join(context.extensionPath, relativePath); 68 | }, 69 | 70 | copySync:function(srcPath, distPath){ 71 | let data = fs.readFileSync(srcPath); 72 | fs.writeFileSync(distPath,data); 73 | } 74 | } 75 | 76 | module.exports = FileUtil; -------------------------------------------------------------------------------- /view/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 58 | 59 | 60 |
61 |
62 |

操作说明: 上[W] 下[S] 左[A] 右[D] 开始 Enter 选择 Tap 攻击[J] 跳跃[k]
63 | 操作无反应时检查下是否游戏界面失去焦点,鼠标左键点击确认游戏界面获得输入焦点即可 64 |

65 |
66 |
67 | 68 | 69 |
70 |
71 | 72 | 73 |
74 |
75 |
76 |
77 |

微信

78 | 79 |
80 |
81 |

支付宝

82 | 83 |
84 |
85 |
86 |
87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 99 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "anes", 3 | "displayName": "小霸王", 4 | "description": "vscode 小霸王", 5 | "publisher": "gamedilong", 6 | "version": "0.0.2", 7 | "repository":"https://github.com/gamedilong/anes-repository", 8 | "engines": { 9 | "vscode": "^1.47.0" 10 | }, 11 | "icon":"logo.png", 12 | "categories": [ 13 | "Other" 14 | ], 15 | "activationEvents": [ 16 | "onCommand:anes.openGameBox", 17 | "onCommand:anes.addRom", 18 | "onCommand:anes.deleteRom", 19 | "onCommand:anes.renameRom", 20 | "onCommand:anes.downloadRom", 21 | "onView:romLocal", 22 | "onView:romRemote" 23 | ], 24 | "main": "./extension.js", 25 | "contributes": { 26 | "commands": [ 27 | { 28 | "command": "anes.openGameBox", 29 | "title": "openGameBox" 30 | }, 31 | { 32 | "command": "anes.addRom", 33 | "title": "addRom", 34 | "icon": "$(new-file)" 35 | }, 36 | { 37 | "command": "anes.refreshRemote", 38 | "title": "refresh", 39 | "icon": "$(search-refresh)" 40 | }, 41 | { 42 | "command": "anes.renameRom", 43 | "title": "rename" 44 | }, 45 | { 46 | "command": "anes.deleteRom", 47 | "title": "delete" 48 | }, 49 | { 50 | "command": "anes.downloadRom", 51 | "title": "download" 52 | } 53 | ], 54 | "viewsContainers": { 55 | "activitybar": [ 56 | { 57 | "id": "anes", 58 | "title": "小霸王", 59 | "icon": "resources/anes.svg" 60 | } 61 | ] 62 | }, 63 | "views": { 64 | "anes": [ 65 | { 66 | "id": "romLocal", 67 | "name": "local" 68 | }, 69 | { 70 | "id": "romRemote", 71 | "name": "remote" 72 | } 73 | ] 74 | }, 75 | "menus": { 76 | "view/title": [ 77 | { 78 | "command": "anes.addRom", 79 | "when": "view == romLocal", 80 | "group": "navigation@1" 81 | }, 82 | { 83 | "command": "anes.refreshRemote", 84 | "when": "view == romRemote", 85 | "group": "navigation@1" 86 | } 87 | ], 88 | "view/item/context": [ 89 | { 90 | "command": "anes.deleteRom", 91 | "when": "view == romLocal", 92 | "group": "anes@1" 93 | }, 94 | { 95 | "command": "anes.renameRom", 96 | "when": "view == romLocal", 97 | "group": "anes@1" 98 | }, 99 | { 100 | "command": "anes.downloadRom", 101 | "when": "view == romRemote", 102 | "group": "anes@2" 103 | } 104 | ] 105 | } 106 | }, 107 | "scripts": { 108 | "lint": "eslint .", 109 | "pretest": "npm run lint", 110 | "test": "node ./test/runTest.js" 111 | }, 112 | "dependencies": { 113 | "compressing": "^1.5.1", 114 | "jsnes": "git://github.com/bfirsh/jsnes.git", 115 | "request": "^2.88.2", 116 | "unzip": "^0.1.11" 117 | }, 118 | "devDependencies": { 119 | "@types/glob": "^7.1.3", 120 | "@types/mocha": "^8.0.0", 121 | "@types/node": "^14.0.27", 122 | "@types/vscode": "^1.47.0", 123 | "eslint": "^7.6.0", 124 | "glob": "^7.1.6", 125 | "mocha": "^8.0.1", 126 | "typescript": "^3.8.3", 127 | "vscode-test": "^1.4.0" 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /romLocalTree.js: -------------------------------------------------------------------------------- 1 | const vscode = require('vscode'); 2 | const FileUtil = require('./fileUtil'); 3 | const os = require('os'); 4 | const path = require('path'); 5 | 6 | class RomLocalTree { 7 | constructor(context){ 8 | this.context = context; 9 | this.userRoot = os.homedir(); 10 | this._onDidChangeTreeData = new vscode.EventEmitter(); 11 | this.onDidChangeTreeData = this._onDidChangeTreeData.event; 12 | if(!FileUtil.pathExists(path.join(this.userRoot, '.anes'))) { 13 | //if not exists create default ahost floder 14 | try{ 15 | FileUtil.createDefaultANesFloder(this.userRoot,this.context); 16 | }catch(e){ 17 | vscode.window.showInformationMessage('Ahost need Administrator permission!'); 18 | } 19 | } 20 | } 21 | refresh(){ 22 | this._onDidChangeTreeData.fire(); 23 | } 24 | getTreeItem(element){ 25 | return element; 26 | } 27 | getChildren(element) { 28 | let romConfigFileList = FileUtil.getRomConfigFileList(this.userRoot); 29 | console.log(`romConfigFileList ${JSON.stringify(romConfigFileList)}`); 30 | if( romConfigFileList && romConfigFileList.length > 0){ 31 | let hostConfigs = []; 32 | romConfigFileList.forEach((romConfig) => { 33 | hostConfigs.push(new DataItem(romConfig.label,null,{ 34 | command:"anes.openGameBox",title:"",arguments:[romConfig] 35 | })); 36 | }) 37 | return hostConfigs; 38 | }else{ 39 | return []; 40 | } 41 | } 42 | deleteRom(item){ 43 | let romConfigList = FileUtil.getRomConfigFileList(this.userRoot); 44 | if(romConfigList && romConfigList.length > 0){ 45 | let deleteIndex = -1; 46 | romConfigList.forEach((romConfig,index)=>{ 47 | if(romConfig.label == item.label){ 48 | deleteIndex = index; 49 | } 50 | }); 51 | deleteIndex > -1 && romConfigList.splice(deleteIndex,1); 52 | FileUtil.writeMetaInfo(this.userRoot,romConfigList); 53 | this._onDidChangeTreeData.fire(); 54 | } 55 | } 56 | rename(item){ 57 | vscode.window.showInputBox({ placeHolder: 'Enter the new game name', value: item.label }) 58 | .then((value) => { 59 | if(value){ 60 | let romConfigList = FileUtil.getRomConfigFileList(this.userRoot); 61 | let exist = false; 62 | romConfigList.forEach(romConfig=>{ 63 | if(romConfig.label == value){ 64 | exist = true; 65 | } 66 | }); 67 | if(exist){ 68 | vscode.window.showInformationMessage('This name is aready exist!'); 69 | }else{ 70 | romConfigList.forEach(romConfig=>{ 71 | if(romConfig.label == item.label){ 72 | romConfig.label = value; 73 | } 74 | }); 75 | FileUtil.writeMetaInfo(this.userRoot,romConfigList); 76 | this._onDidChangeTreeData.fire(); 77 | } 78 | }else{ 79 | vscode.window.showInformationMessage('Please enter your game name!'); 80 | } 81 | }); 82 | } 83 | } 84 | 85 | class DataItem extends vscode.TreeItem{ 86 | constructor(label, children, command) { 87 | super(label, vscode.TreeItemCollapsibleState.None); 88 | this.iconPath = path.join(__filename,'..','resources', 'rom.svg');; 89 | this.children = children; 90 | this.command = command; 91 | } 92 | } 93 | 94 | module.exports = RomLocalTree; -------------------------------------------------------------------------------- /view/main.js: -------------------------------------------------------------------------------- 1 | var SCREEN_WIDTH = 256; 2 | var SCREEN_HEIGHT = 240; 3 | var FRAMEBUFFER_SIZE = SCREEN_WIDTH*SCREEN_HEIGHT; 4 | 5 | var canvas_ctx, image; 6 | var framebuffer_u8, framebuffer_u32; 7 | 8 | var AUDIO_BUFFERING = 512; 9 | var SAMPLE_COUNT = 4*1024; 10 | var SAMPLE_MASK = SAMPLE_COUNT - 1; 11 | var audio_samples_L = new Float32Array(SAMPLE_COUNT); 12 | var audio_samples_R = new Float32Array(SAMPLE_COUNT); 13 | var audio_write_cursor = 0, audio_read_cursor = 0; 14 | 15 | /*var NeskeyMap = { 16 | BUTTON_UP: 38, // up 17 | BUTTON_DOWN:40, // Down 18 | BUTTON_LEFT:37, // Left 19 | BUTTON_RIGHT:39, // Right 20 | BUTTON_A:65, // 'a' - qwerty, dvorak 21 | BUTTON_Q:81, // 'q' - azerty 22 | BUTTON_S:83, // 's' - qwerty, azerty 23 | BUTTON_O:79, // 'o' - dvorak 24 | BUTTON_SELECT:9, // Tab 25 | BUTTON_START:13 // Return 26 | }*/ 27 | 28 | var NeskeyMap = { 29 | BUTTON_UP: 87, // up -> W 30 | BUTTON_DOWN:83, // Down -> S 31 | BUTTON_LEFT:65, // Left -> D 32 | BUTTON_RIGHT:68, // Right -> A 33 | BUTTON_A: 73, // 's' - qwerty, azerty -> I 34 | BUTTON_Q: 75, // 'q' - azerty -> K 35 | BUTTON_S: 74, // 'a' - qwerty, dvorak -> J 36 | BUTTON_O:85, // 'o' - dvorak -> U 37 | BUTTON_SELECT:9, // Tab -> 1 38 | BUTTON_START:13 // Return -> 2 39 | } 40 | 41 | var nes = new jsnes.NES({ 42 | onFrame: function(framebuffer_24){ 43 | for(var i = 0; i < FRAMEBUFFER_SIZE; i++) framebuffer_u32[i] = 0xFF000000 | framebuffer_24[i]; 44 | }, 45 | onAudioSample: function(l, r){ 46 | audio_samples_L[audio_write_cursor] = l; 47 | audio_samples_R[audio_write_cursor] = r; 48 | audio_write_cursor = (audio_write_cursor + 1) & SAMPLE_MASK; 49 | }, 50 | moyu:true 51 | }); 52 | 53 | function onAnimationFrame(){ 54 | window.requestAnimationFrame(onAnimationFrame); 55 | 56 | image.data.set(framebuffer_u8); 57 | canvas_ctx.putImageData(image, 0, 0); 58 | } 59 | 60 | function audio_remain(){ 61 | return (audio_write_cursor - audio_read_cursor) & SAMPLE_MASK; 62 | } 63 | 64 | function audio_callback(event){ 65 | var dst = event.outputBuffer; 66 | var len = dst.length; 67 | 68 | // Attempt to avoid buffer underruns. 69 | if(audio_remain() < AUDIO_BUFFERING) nes.frame(); 70 | 71 | var dst_l = dst.getChannelData(0); 72 | var dst_r = dst.getChannelData(1); 73 | for(var i = 0; i < len; i++){ 74 | var src_idx = (audio_read_cursor + i) & SAMPLE_MASK; 75 | dst_l[i] = audio_samples_L[src_idx]; 76 | dst_r[i] = audio_samples_R[src_idx]; 77 | } 78 | 79 | audio_read_cursor = (audio_read_cursor + len) & SAMPLE_MASK; 80 | } 81 | 82 | function keyboard(callback, event){ 83 | var player1 = 1; 84 | console.log(`event.keyCode${event.keyCode}`); 85 | switch(event.keyCode){ 86 | case NeskeyMap.BUTTON_UP: // UP 87 | callback(player1, jsnes.Controller.BUTTON_UP); break; 88 | case NeskeyMap.BUTTON_DOWN: // Down 89 | callback(player1, jsnes.Controller.BUTTON_DOWN); break; 90 | case NeskeyMap.BUTTON_LEFT: // Left 91 | callback(player1, jsnes.Controller.BUTTON_LEFT); break; 92 | case NeskeyMap.BUTTON_RIGHT: // Right 93 | callback(player1, jsnes.Controller.BUTTON_RIGHT); break; 94 | case NeskeyMap.BUTTON_A: // 'a' - qwerty, dvorak 95 | case NeskeyMap.BUTTON_Q: // 'q' - azerty 96 | callback(player1, jsnes.Controller.BUTTON_A); break; 97 | case NeskeyMap.BUTTON_S: // 's' - qwerty, azerty 98 | case NeskeyMap.BUTTON_O: // 'o' - dvorak 99 | callback(player1, jsnes.Controller.BUTTON_B); break; 100 | case NeskeyMap.BUTTON_SELECT: // Tab 101 | callback(player1, jsnes.Controller.BUTTON_SELECT); break; 102 | case NeskeyMap.BUTTON_START: // Return 103 | callback(player1, jsnes.Controller.BUTTON_START); break; 104 | default: break; 105 | } 106 | } 107 | 108 | function nes_init(canvas_id){ 109 | var canvas = document.getElementById(canvas_id); 110 | canvas_ctx = canvas.getContext("2d"); 111 | image = canvas_ctx.getImageData(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); 112 | 113 | canvas_ctx.fillStyle = "black"; 114 | canvas_ctx.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); 115 | 116 | // Allocate framebuffer array. 117 | var buffer = new ArrayBuffer(image.data.length); 118 | framebuffer_u8 = new Uint8ClampedArray(buffer); 119 | framebuffer_u32 = new Uint32Array(buffer); 120 | 121 | // Setup audio. 122 | var audio_ctx = new window.AudioContext(); 123 | var script_processor = audio_ctx.createScriptProcessor(AUDIO_BUFFERING, 0, 2); 124 | script_processor.onaudioprocess = audio_callback; 125 | script_processor.connect(audio_ctx.destination); 126 | } 127 | 128 | function nes_boot(rom_data){ 129 | nes.loadROM(rom_data); 130 | window.requestAnimationFrame(onAnimationFrame); 131 | } 132 | 133 | function nes_load_data(canvas_id, rom_data){ 134 | nes_init(canvas_id); 135 | nes_boot(rom_data); 136 | 137 | } 138 | 139 | function nes_load_url(canvas_id, path){ 140 | nes_init(canvas_id); 141 | 142 | /*var req = new XMLHttpRequest(); 143 | req.open("GET", path); 144 | req.overrideMimeType("text/plain; charset=x-user-defined"); 145 | req.onerror = () => console.log(`Error loading ${path}: ${req.statusText}`); 146 | 147 | req.onload = function() { 148 | if (this.status === 200) { 149 | nes_boot(this.responseText); 150 | } else if (this.status === 0) { 151 | // Aborted, so ignore error 152 | } else { 153 | req.onerror(); 154 | } 155 | }; 156 | 157 | req.send();*/ 158 | callVscode({cmd: 'getRom', key: 'NES.GETROM'}, (data) => { 159 | //console.log('nes data:' + data); 160 | nes_boot(data); 161 | }); 162 | } 163 | 164 | document.addEventListener('keydown', (event) => {keyboard(nes.buttonDown, event)}); 165 | document.addEventListener('keyup', (event) => {keyboard(nes.buttonUp, event)}); 166 | 167 | 168 | 169 | function start(){ 170 | nes_load_url("nes-canvas", "InterglacticTransmissing.nes"); 171 | } -------------------------------------------------------------------------------- /extension.js: -------------------------------------------------------------------------------- 1 | // The module 'vscode' contains the VS Code extensibility API 2 | // Import the module and reference it with the alias vscode in your code below 3 | const vscode = require('vscode'); 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | const RomLocalTree = require('./romLocalTree'); 7 | const RomRemoteTree = require('./romRemoteTree'); 8 | const FileUtil = require('./fileUtil'); 9 | const { download } = require('./download'); 10 | const compressing = require('compressing'); 11 | 12 | const os = require('os'); 13 | // this method is called when your extension is activated 14 | // your extension is activated the very first time the command is executed 15 | 16 | /** 17 | * @param {vscode.ExtensionContext} context 18 | */ 19 | function activate(context) { 20 | 21 | // Use the console to output diagnostic information (console.log) and errors (console.error) 22 | // This line of code will only be executed once when your extension is activated 23 | 24 | // The command has been defined in the package.json file 25 | // Now provide the implementation of the command with registerCommand 26 | // The commandId parameter must match the command field in package.json 27 | const romLocalTree = new RomLocalTree(context); 28 | vscode.window.registerTreeDataProvider("romLocal", romLocalTree); 29 | 30 | 31 | 32 | let openGameBox = vscode.commands.registerCommand('anes.openGameBox', function (params) { 33 | // The code you place here will be executed every time your command is executed 34 | console.log('anes.openGameBox:' + JSON.stringify(params)); 35 | // Display a message box to the user 36 | const panel = vscode.window.createWebviewPanel( 37 | 'webview', // viewType 38 | "小霸王其乐无穷", // 视图标题 39 | vscode.ViewColumn.One, 40 | { 41 | enableScripts: true, // 启用JS,默认禁用 42 | retainContextWhenHidden: true // webview被隐藏时保持状态,避免被重置 43 | } 44 | ); 45 | let absPath = FileUtil.getExtensionFileAbsolutePath(context, 'resources'); 46 | 47 | panel.iconPath = vscode.Uri.file(path.join(absPath,"anes.svg")); 48 | 49 | 50 | panel.webview.html = getWebViewContent(context, 'view/index.html'); 51 | panel.webview.onDidReceiveMessage(message => { 52 | if (messageHandler[message.cmd]) { 53 | messageHandler[message.cmd](global, message); 54 | } else { 55 | // util.showError(`未找到名为 ${message.cmd} 回调方法!`); 56 | } 57 | }, undefined, context.subscriptions); 58 | 59 | 60 | function invokeCallback(panel, message, resp) { 61 | panel.webview.postMessage({cmd: 'vscodeCallback', cbid: message.cbid, data: resp}); 62 | } 63 | 64 | /** 65 | * 存放所有消息回调函数,根据 message.cmd 来决定调用哪个方法 66 | */ 67 | const messageHandler = { 68 | getRom(global, message) { 69 | //let romPath = FileUtil.getExtensionFileAbsolutePath(context,'resources/su.nes'); 70 | let romPath = params.path; 71 | fs.readFile(romPath, function(err, data) { 72 | if (err) return; 73 | console.log(data); 74 | console.log(data.toString('binary')) 75 | invokeCallback(panel, message, data.toString('binary')); 76 | }); 77 | } 78 | }; 79 | 80 | }); 81 | context.subscriptions.push(openGameBox); 82 | let addRom = vscode.commands.registerCommand('anes.addRom', function () { 83 | // The code you place here will be executed every time your command is executed 84 | vscode.window.showOpenDialog( 85 | { 86 | canSelectFiles:true, 87 | canSelectFolders:false, 88 | canSelectMany:true, 89 | filter: {"nes":['nes']}, 90 | defaultUri:vscode.Uri.file("/D:/"), 91 | }).then(function(files){ 92 | console.log(JSON.stringify(files)) 93 | if(files && files.length > 0){ 94 | let userRoot = os.homedir(); 95 | files.forEach((file)=>{ 96 | FileUtil.addRomToResposity(userRoot,file.fsPath); 97 | }); 98 | romLocalTree.refresh(); 99 | } 100 | } 101 | ) 102 | }); 103 | 104 | context.subscriptions.push(addRom); 105 | 106 | let deleteRom = vscode.commands.registerCommand('anes.deleteRom', function (romConfig) { 107 | // The code you place here will be executed every time your command is executed 108 | romLocalTree.deleteRom(romConfig); 109 | }); 110 | 111 | context.subscriptions.push(deleteRom); 112 | 113 | let renameRom = vscode.commands.registerCommand('anes.renameRom', function (romConfig) { 114 | 115 | romLocalTree.rename(romConfig); 116 | romLocalTree.refresh(); 117 | }); 118 | 119 | context.subscriptions.push(renameRom); 120 | let userRoot = os.homedir(); 121 | let romRemoteTree = null; 122 | if(!FileUtil.pathExists(path.join(userRoot, '.anes','remote'))){ 123 | let defaultRemoteRepository = path.join(userRoot, '.anes','remote'); 124 | fs.mkdirSync(path.join(userRoot, '.anes','remote')); 125 | fs.writeFileSync(path.join(userRoot, '.anes', 'remote','meta.json'),JSON.stringify([])); 126 | let defaultRomPath = FileUtil.getExtensionFileAbsolutePath(context, 'resources'); 127 | let zipPath = path.join(defaultRomPath,'master.zip'); 128 | download('https://github.com/gamedilong/anes-repository/archive/master.zip', zipPath , function(err){ 129 | if(!err){ 130 | console.log(`zipPath:${zipPath}, defaultRemoteRepository :${defaultRemoteRepository}`); 131 | compressing.zip.uncompress(zipPath, defaultRemoteRepository).then(function(){ 132 | console.log('success unzip') 133 | fs.unlinkSync(zipPath); 134 | romRemoteTree = new RomRemoteTree(context); 135 | vscode.window.registerTreeDataProvider("romRemote", romRemoteTree); 136 | }).catch(function(unzipErr){ 137 | vscode.window.showInformationMessage(`Download remote repository error info: ${unzipErr}`); 138 | }); 139 | }else{ 140 | vscode.window.showInformationMessage(`Download remote repository error info: ${err}`); 141 | } 142 | }); 143 | }else{ 144 | romRemoteTree = new RomRemoteTree(context); 145 | vscode.window.registerTreeDataProvider("romRemote", romRemoteTree); 146 | } 147 | 148 | let refreshRemote = vscode.commands.registerCommand('anes.refreshRemote', function () { 149 | console.log('refresh'); 150 | let defaultRemoteRepository = path.join(userRoot, '.anes','remote'); 151 | let defaultRomPath = FileUtil.getExtensionFileAbsolutePath(context, 'resources'); 152 | let zipPath = path.join(defaultRomPath,'master.zip'); 153 | download('https://github.com/gamedilong/anes-repository/archive/master.zip', zipPath , function(err){ 154 | if(!err){ 155 | compressing.zip.uncompress(zipPath, defaultRemoteRepository).then(function(){ 156 | fs.unlinkSync(zipPath); 157 | romRemoteTree.refresh(); 158 | vscode.window.registerTreeDataProvider("romRemote", romRemoteTree); 159 | }).catch(function(unzipErr){ 160 | vscode.window.showInformationMessage(`Refresh remote repository error info: ${unzipErr}`); 161 | }); 162 | }else{ 163 | vscode.window.showInformationMessage(`Refresh remote repository error info: ${err}`); 164 | } 165 | }); 166 | }); 167 | 168 | context.subscriptions.push(refreshRemote); 169 | 170 | let downloadRemote = vscode.commands.registerCommand('anes.downloadRom', function (remoteConfig) { 171 | /*if(remoteConfig.downloadStatus == 1 || remoteConfig.downloadStatus == 2){ 172 | return false; 173 | }*/ 174 | let remoteMeta = fs.readFileSync(path.join(userRoot, '.anes', 'remote','meta.json')); 175 | remoteMeta = remoteMeta.toString(); 176 | remoteMeta = JSON.parse(remoteMeta); 177 | remoteMeta.push({ 178 | name: remoteConfig.label, 179 | downloadStatus: 1 180 | }) 181 | fs.writeFileSync(path.join(userRoot, '.anes', 'remote','meta.json'),JSON.stringify(remoteMeta)); 182 | 183 | vscode.window.withProgress({ title: `Download:${remoteConfig.label}`, location: vscode.ProgressLocation.Notification, cancellable: false }, async (progress, token) => { 184 | let downloadSuccess = false; 185 | 186 | // The code you place here will be executed every time your command is executed 187 | download(remoteConfig.url, path.join(userRoot, '.anes','local',`${remoteConfig.fileName}`), function(err){ 188 | downloadSuccess = true; 189 | remoteMeta.forEach((meta)=>{ 190 | if(meta.name == remoteConfig.label){ 191 | meta.downloadStatus = 2; 192 | } 193 | }) 194 | if(remoteConfig.fileName.indexOf(".zip")>-1){ 195 | // 解压到本地仓库 196 | let zipPath = path.join(userRoot, '.anes','local',remoteConfig.fileName) 197 | let localPath = path.join(userRoot, '.anes','local') 198 | compressing.zip.uncompress(zipPath, localPath).then(function(){ 199 | fs.unlinkSync(zipPath); 200 | FileUtil.addRomToResposity(userRoot,path.join(userRoot, '.anes','local',remoteConfig.nesName),remoteConfig.label); 201 | // 本地仓库更新 202 | romLocalTree.refresh(); 203 | }).catch(function(err){ 204 | vscode.window.showInformationMessage(`download error :${err}`); 205 | }); 206 | }else{ 207 | FileUtil.addRomToResposity(userRoot,path.join(userRoot, '.anes','local',remoteConfig.fileName),remoteConfig.label); 208 | romLocalTree.refresh(); 209 | } 210 | if(!err){ 211 | vscode.window.showInformationMessage(`Game: ${remoteConfig.label} download success!`); 212 | }else{ 213 | remoteMeta.forEach((meta)=>{ 214 | if(meta.name == remoteConfig.label){ 215 | meta.downloadStatus = 0; 216 | } 217 | }) 218 | vscode.window.showInformationMessage(`Game: ${remoteConfig.label} download err, and err info is ${err}!`); 219 | } 220 | fs.writeFileSync(path.join(userRoot, '.anes', 'remote','meta.json'),JSON.stringify(remoteMeta)); 221 | }) 222 | await new Promise(resolve => { 223 | let intervalId = setInterval(() => { 224 | if (!downloadSuccess) { 225 | progress.report({ 226 | increment: 1 227 | //message: `${Math.round(count)}下载` 228 | }); 229 | return; 230 | } 231 | clearInterval(intervalId); 232 | resolve(undefined); 233 | }, 100); 234 | }); 235 | return "下载完成"; 236 | }); 237 | }); 238 | 239 | context.subscriptions.push(downloadRemote); 240 | 241 | } 242 | 243 | function getWebViewContent(context, templatePath) { 244 | const resourcePath = FileUtil.getExtensionFileAbsolutePath(context, templatePath); 245 | const dirPath = path.dirname(resourcePath); 246 | let html = fs.readFileSync(resourcePath, 'utf-8'); 247 | // vscode不支持直接加载本地资源,需要替换成其专有路径格式,这里只是简单的将样式和JS的路径替换 248 | html = html.replace(/( { 249 | return $1 + vscode.Uri.file(path.resolve(dirPath, $2)).with({ scheme: 'vscode-resource' }).toString() + '"'; 250 | }); 251 | return html; 252 | } 253 | 254 | exports.activate = activate; 255 | 256 | // this method is called when your extension is deactivated 257 | function deactivate() {} 258 | 259 | module.exports = { 260 | activate, 261 | deactivate 262 | } 263 | --------------------------------------------------------------------------------