1/* 2* Copyright (c) 2022 Shenzhen Kaihong Digital Industry Development Co., Ltd. 3* Licensed under the Apache License, Version 2.0 (the "License"); 4* you may not use this file except in compliance with the License. 5* You may obtain a copy of the License at 6* 7* http://www.apache.org/licenses/LICENSE-2.0 8* 9* Unless required by applicable law or agreed to in writing, software 10* distributed under the License is distributed on an "AS IS" BASIS, 11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12* See the License for the specific language governing permissions and 13* limitations under the License. 14*/ 15 16// The module 'vscode' contains the VS Code extensibility API 17// Import the module and reference it with the alias vscode in your code below 18const vscode = require('vscode'); 19const fs = require('fs'); 20const re = require('./gen/tools/VsPluginRe'); 21const { VsPluginLog } = require('./gen/tools/VsPluginLog'); 22const { detectPlatform, readFile, writeFile } = require('./gen/tools/VsPluginTool'); 23const path = require('path'); 24const os = require('os'); 25let exeFilePath = null; 26let flag = ''; 27let isTrue = false; 28let globalPanel = null; 29 30let importToolChain = false; 31let extensionIds = []; 32let nextPluginId = null; 33// this method is called when your extension is activated 34// your extension is activated the very first time the command is executed 35 36/** 37 * @param {vscode.ExtensionContext} context 38 */ 39function activate(context) { 40 // Use the console to output diagnostic information (console.log) and errors (console.error) 41 // This line of code will only be executed once when your extension is activated 42 console.log('Congratulations, your extension "gn-gen" is now active!'); 43 let disposable = register(context, 'generate_gn'); 44 let disposableMenu = register(context, 'generate_gn_menu'); 45 context.subscriptions.push(disposable); 46 context.subscriptions.push(disposableMenu); 47 let platform = detectPlatform(); 48 if (platform === 'win') { 49 exeFilePath = __dirname + '/gn-gen-win.exe'; 50 } else if (platform === 'mac') { 51 exeFilePath = __dirname + '/gn-gen-macos'; 52 } else if (platform === 'Linux') { 53 exeFilePath = __dirname + '/gn-gen-linux'; 54 } 55 vscode.window.onDidChangeActiveColorTheme(colorTheme => { 56 let result = { 57 msg: 'colorThemeChanged' 58 }; 59 globalPanel.webview.postMessage(result); 60 }); 61} 62 63function gnexecutor(outputCodeDir, originCodeDir, inputScriptDir, scriptType, transplantDir, 64 subsystemName, componentName, compileOptions) { 65 let exec = require('child_process').exec; 66 exec(genGnCommand(outputCodeDir, originCodeDir, inputScriptDir, scriptType, transplantDir, 67 subsystemName, componentName, compileOptions), 68 { 69 maxBuffer: 1024 * 1024 * 20 70 }, 71 function (error, stdout, stderr) { 72 VsPluginLog.logInfo('VsPlugin: stdout =' + stdout + ', stderr =' + stderr); 73 if (error || stdout.indexOf('generate gn ok') < 0) { 74 console.log(error); 75 vscode.window.showErrorMessage('genError:' + ((error !== null && error !== undefined) ? 76 error : '') + stdout); 77 return VsPluginLog.logError('VsPlugin:' + error + stdout); 78 } 79 vscode.window.showInformationMessage('Generated successfully'); 80 return ''; 81 }); 82} 83 84function genGnCommand(outputCodeDir, originCodeDir, inputScriptDir, scriptType, 85 transplantDir, subsystemName, componentName, compileOptions) { 86 let command = exeFilePath + ' -o ' + outputCodeDir + ' -p ' + originCodeDir + ' -f ' + inputScriptDir + 87 ' -t ' + scriptType + ' -s ' + subsystemName + ' -m ' + componentName + ' -d ' + transplantDir; 88 if (compileOptions !== '') { 89 command += ' -a ' + '\'' + compileOptions + '\''; 90 } 91 92 command = re.replaceAll(command, '\\\\', '/'); 93 94 console.log('command = ' + command); 95 return command; 96} 97 98/** 99 * 将插件界面读取的扩展配置更新到cfg.json文件中 100 * @param extFile 用户自定义的支持文件类型 101 * @param extFlag 用户自定义的支持编译选项 102 */ 103function refreshJsonCfg(extFile, extFlag) { 104 let cfgFilePath = __dirname + '/res/cfg.json'; 105 let jsonCfg = readFile(cfgFilePath); 106 let cfgObj = JSON.parse(jsonCfg.toString()); 107 cfgObj.fileSuffix = extFile; 108 cfgObj.compileflag = extFlag; 109 let cfgStr = JSON.stringify(cfgObj); 110 writeFile(cfgFilePath, cfgStr); 111} 112 113function exeFileExit() { 114 if (fs.existsSync(exeFilePath)) { 115 return true; 116 } 117 return false; 118} 119 120function register(context, command) { 121 let disposable = vscode.commands.registerCommand(command, function (uri, boolValue, items) { 122 // The code you place here will be executed every time your command is executed 123 // Display a message box to the user 124 globalPanel = vscode.window.createWebviewPanel( 125 'generate', // Identifies the type of WebView 126 'Gn Generate Frame', // Title of the panel displayed to the user 127 vscode.ViewColumn.Two, // Display the WebView panel in the form of new columns in the editor 128 { 129 enableScripts: true, // Enable or disable JS, default is Enable 130 retainContextWhenHidden: true, // Keep the WebView state when it is hidden to avoid being reset 131 } 132 ); 133 checkBoolval(boolValue, items); 134 globalPanel.webview.html = getWebviewContent(context, importToolChain); 135 let msg; 136 globalPanel.webview.onDidReceiveMessage(message => { 137 msg = message.msg; 138 if (msg === 'cancel') { 139 globalPanel.dispose(); 140 } else if (msg === 'gn') { 141 checkReceiveMsg(message); 142 } else { 143 selectPath(globalPanel, message); 144 } 145 }, undefined, context.subscriptions); 146 // 路径有效性判断 147 if (uri.fsPath !== undefined) { 148 let fn = re.getFileInPath(uri.fsPath); 149 let tt = re.match('([a-zA-Z_0-9]+.[a-zA-Z_0-9])', fn); 150 let result = { 151 msg: 'selectinputScriptDir', 152 path: tt ? uri.fsPath : '' 153 }; 154 globalPanel.webview.postMessage(result); 155 } 156 }); 157 return disposable; 158} 159 160function checkBoolval(boolValue, items) { 161 if (typeof (boolValue) === 'boolean' && Array.isArray(items)) { 162 if (boolValue === true) { 163 //遍历数组item,查看当前插件id是数组的第几个元素,并拿出下一个元素,并判断当前id是否是最后一个元素并做相应处理 164 getNextPlugin(items, boolValue); 165 } 166 } 167} 168 169function getNextPlugin(items, boolValue) { 170 let myExtensionId = 'kaihong.gn-gen'; 171 for (let i = 0; i < items.length; i++) { 172 if (myExtensionId === items[i] && (i === items.length - 1)) { 173 importToolChain = false; 174 } else if (myExtensionId === items[i] && (i !== items.length - 1)) { 175 importToolChain = boolValue; 176 nextPluginId = items[i + 1]; 177 } 178 extensionIds.push(items[i]); 179 } 180} 181 182function checkReceiveMsg(message) { 183 let outputCodeDir = message.outputCodeDir; 184 let originCodeDir = message.originCodeDir; 185 let inputScriptDir = message.inputScriptDir; 186 let scriptType = message.scriptType; 187 let transplantDir = message.transplantDir; 188 let subsystemName = message.subsystemName; 189 let componentName = message.componentName; 190 let compileOptions = message.compileOptions; 191 let buttonName = message.buttonName; 192 193 refreshJsonCfg(message.extFile, message.extFlag); 194 checkMode(outputCodeDir, originCodeDir, inputScriptDir, scriptType, 195 transplantDir, subsystemName, componentName, compileOptions); 196 197 if (buttonName === 'Next') { 198 startNextPlugin(); 199 } 200} 201 202/** 203* 获取插件执行命令 204*/ 205function nextPluginExeCommand(nextPluginId) { 206 if (nextPluginId === 'kaihong.ApiScan') { 207 return 'api_scan'; 208 } else if (nextPluginId === 'kaihong.gn-gen') { 209 return 'generate_gn'; 210 } else if (nextPluginId === 'kaihong.service-gen') { 211 return 'generate_service'; 212 } else if (nextPluginId === 'kaihong.ts-gen') { 213 return 'generate_ts'; 214 } else if (nextPluginId === 'kaihong.napi-gen') { 215 return 'generate_napi'; 216 } else { 217 return null; 218 } 219} 220 221/** 222* 执行完毕后开启工具链中下一个插件 223*/ 224function startNextPlugin() { 225 const extension = vscode.extensions.getExtension(nextPluginId); 226 if (extension) { 227 let startNextPlugin = nextPluginExeCommand(nextPluginId); 228 try { 229 vscode.commands.executeCommand(startNextPlugin, '', importToolChain, extensionIds); 230 } catch (error) { 231 console.error(error); 232 } 233 } 234} 235 236/** 237* 选择本地目录/文件夹 238*/ 239 function selectPath(panel, message) { 240 let mode = 1; 241 if (message.mode !== undefined && message.mode !== null) { 242 mode = message.mode; 243 } 244 flag = flag === '' ? '' : flag; 245 const options = { 246 canSelectMany: false, //是否可以选择多个 247 canSelectFiles: mode === 0 ? true : false, //是否选择文件 248 canSelectFolders: mode === 0 ? false : true, //是否选择文件夹 249 defaultUri:vscode.Uri.file(flag), //默认打开本地路径 250 filters: { 251 'All files': ['*'] 252 } 253 }; 254 255 return vscode.window.showOpenDialog(options).then(fileUri => { 256 if (fileUri && fileUri[0]) { 257 console.log('Selected file: ' + fileUri[0].fsPath); 258 let filePath = ''; 259 filePath = fileUri[0].fsPath.concat(','); 260 filePath = re.replaceAll(filePath, '\\\\', '/'); 261 if (filePath.substring(1, 2) === ':') { 262 let filePathTemp = filePath.substring(0, 1).toUpperCase(); 263 filePath = filePathTemp + filePath.substring(1, filePath.length); 264 } 265 let result = { 266 msg: message.msg, 267 path: filePath.length > 0 ? filePath.substring(0, filePath.length - 1) : filePath 268 }; 269 console.log('message.msg: ' + message.msg); 270 if (!isTrue && message.msg === 'selectoutputCodeDir') { 271 flag = filePath.substring(0, filePath.length - 1); 272 let fileTempName = 'out'; 273 let pos = flag.indexOf(fileTempName); 274 flag = flag.substr(0, pos - 1); 275 isTrue = true; 276 } 277 panel.webview.postMessage(result); 278 return fileUri[0].fsPath; 279 } else { 280 return ''; 281 } 282 }); 283} 284 285function checkMode(outputCodeDir, originCodeDir, inputScriptDir, scriptType, 286 transplantDir, subsystemName, componentName, compileOptions) { 287 outputCodeDir = re.replaceAll(outputCodeDir, ' ', ''); 288 if ('' === outputCodeDir) { 289 vscode.window.showErrorMessage('Please enter the outputCodeDir path!'); 290 return; 291 } 292 originCodeDir = re.replaceAll(originCodeDir, ' ', ''); 293 if ('' === originCodeDir) { 294 vscode.window.showErrorMessage('Please enter the originCodeDir path!'); 295 return; 296 } 297 inputScriptDir = re.replaceAll(inputScriptDir, ' ', ''); 298 if ('' === inputScriptDir) { 299 vscode.window.showErrorMessage('Please enter the inputScriptDir path!'); 300 return; 301 } 302 if (inputScriptDir.indexOf('.') < 0) { 303 vscode.window.showErrorMessage('Please enter the correct file path!'); 304 return; 305 } 306 transplantDir = re.replaceAll(transplantDir, ' ', ''); 307 if ('' === transplantDir) { 308 vscode.window.showErrorMessage('Please enter the transplantDir path!'); 309 return; 310 } 311 subsystemName = re.replaceAll(subsystemName, ' ', ''); 312 if ('' === subsystemName) { 313 vscode.window.showErrorMessage('Please enter the subsystemName!'); 314 return; 315 } 316 componentName = re.replaceAll(componentName, ' ', ''); 317 if ('' === componentName) { 318 vscode.window.showErrorMessage('Please enter the componentName!'); 319 return; 320 } 321 if (exeFileExit()) { 322 gnexecutor(outputCodeDir, originCodeDir, inputScriptDir, scriptType, 323 transplantDir, subsystemName, componentName, compileOptions); 324 } else { 325 vscode.window.showInformationMessage('Copy executable program to ' + __dirname); 326 } 327} 328 329// this method is called when your extension is deactivated 330function deactivate() { } 331 332function getWebviewContent(context, importToolChain) { 333 let data = readFile(__dirname + '/vs_plugin_view.html'); 334 data = getWebViewContent(context, '/vs_plugin_view.html'); 335 let content = data.toString(); 336 if (importToolChain) { 337 content = content.replace('Ok', 'Next'); 338 } 339 return content; 340} 341 342function getWebViewContent(context, templatePath) { 343 const reoriginCodeDir = path.join(context.extensionPath, templatePath); 344 const dirPath = path.dirname(reoriginCodeDir); 345 let html = fs.readFileSync(reoriginCodeDir, 'utf-8'); 346 html = html.replace(/(<link.+?href="|<script.+?src="|<iframe.+?src="|<img.+?src=")(.+?)"/g, (m, $1, $2) => { 347 if ($2.indexOf('https://') < 0) { 348 return $1 + globalPanel.webview.asWebviewUri(vscode.Uri.file(path.resolve(dirPath, $2))) + '"'; 349 } else { 350 return $1 + $2 + '"'; 351 } 352 }); 353 return html; 354} 355 356module.exports = { 357 activate, 358 deactivate 359}; 360