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 } = require('./gen/tools/VsPluginTool'); 23const path = require('path'); 24var exeFilePath = null; 25var globalPanel = null; 26 27var importToolChain = false; 28var extensionIds = []; 29var nextPluginId = null; 30// this method is called when your extension is activated 31// your extension is activated the very first time the command is executed 32 33/** 34 * @param {vscode.ExtensionContext} context 35 */ 36function activate(context) { 37 // Use the console to output diagnostic information (console.log) and errors (console.error) 38 // This line of code will only be executed once when your extension is activated 39 console.log('Congratulations, your extension "napi-gen" is now active!'); 40 let disposable = register(context, 'generate_napi'); 41 let disposableMenu = register(context, 'generate_napi_menu'); 42 context.subscriptions.push(disposable); 43 context.subscriptions.push(disposableMenu); 44 var platform = detectPlatform(); 45 if (platform == 'win') { 46 exeFilePath = __dirname + "/napi_generator-win.exe"; 47 } else if (platform == 'mac') { 48 exeFilePath = __dirname + "/napi_generator-macos"; 49 } else if (platform == 'Linux') { 50 exeFilePath = __dirname + "/napi_generator-linux"; 51 } 52} 53 54function executor(name, genDir, mode,numberType, importIsCheck) { 55 var exec = require('child_process').exec; 56 exec(genCommand(name, genDir, mode,numberType, importIsCheck), function (error, stdout, stderr) { 57 VsPluginLog.logInfo('VsPlugin: stdout =' + stdout + ", stderr =" + stderr); 58 if (error || stdout.indexOf("success") < 0) { 59 vscode.window.showErrorMessage("genError:" + (error != null ? error : "") + stdout); 60 return VsPluginLog.logError("VsPlugin:" + error + stdout); 61 } 62 vscode.window.showInformationMessage("Generated successfully"); 63 }); 64} 65 66function genCommand(name, genDir, mode,numberType, importIsCheck) { 67 var genFileMode = mode == 0 ? " -f " : " -d "; 68 if (genDir == ""){ 69 return exeFilePath + genFileMode + name; 70 } 71 return exeFilePath + genFileMode + name + " -o " + genDir + " -n " + numberType + " -i " + importIsCheck; 72} 73 74function exeFileExit() { 75 if (fs.existsSync(exeFilePath)) { 76 return true; 77 } 78 return false; 79} 80 81function register(context, command) { 82 let disposable = vscode.commands.registerCommand(command, function (uri, boolValue, items) { 83 // The code you place here will be executed every time your command is executed 84 // Display a message box to the user 85 globalPanel = vscode.window.createWebviewPanel( 86 'generate', // Identifies the type of WebView 87 'Generate Napi Frame', // Title of the panel displayed to the user 88 vscode.ViewColumn.Two, // Display the WebView panel in the form of new columns in the editor 89 { 90 enableScripts: true, // Enable or disable JS, default is Enable 91 retainContextWhenHidden: true, // Keep the WebView state when it is hidden to avoid being reset 92 } 93 ); 94 95 if (typeof(boolValue) == 'boolean' && Array.isArray(items)) { 96 if (boolValue == true) { 97 //遍历数组item,查看当前插件id是数组的第几个元素,并拿出下一个元素,并判断当前id是否是最后一个元素并做相应处理 98 let myExtensionId = 'kaihong.napi-gen'; 99 for (let i = 0; i < items.length; i++) { 100 if (myExtensionId == items[i] && (i == items.length - 1)) { 101 importToolChain = false; 102 } else if (myExtensionId == items[i] && (i != items.length - 1)) { 103 importToolChain = boolValue; 104 nextPluginId = items[i + 1]; 105 } 106 extensionIds.push(items[i]); 107 } 108 } 109 } 110 globalPanel.webview.html = getWebviewContent(context, importToolChain); 111 let msg; 112 globalPanel.webview.onDidReceiveMessage(message => { 113 msg = message.msg; 114 if (msg == "cancel") { 115 globalPanel.dispose(); 116 } else if(msg == "param") { 117 checkReceiveMsg(message); 118 } else { 119 selectPath(globalPanel, message); 120 } 121 }, undefined, context.subscriptions); 122 let fn = re.getFileInPath(uri.fsPath); 123 let tt = re.match("((@ohos\.)*[a-zA-Z_0-9]+.d.ts)", fn); 124 var result = { 125 msg: "selectInterPath", 126 path: tt ? uri.fsPath : "" 127 } 128 globalPanel.webview.postMessage(result); 129 }); 130 return disposable; 131} 132 133function checkReceiveMsg(message) { 134 let mode = message.mode; 135 let name = message.fileNames; 136 let genDir = message.genDir; 137 let numberType = message.numberType; 138 let importIsCheck = message.importIsCheck; 139 let buttonName = message.buttonName; 140 checkMode(name, genDir, mode, numberType, importIsCheck); 141 if (buttonName == 'Next') { 142 startNextPlugin(); 143 } 144} 145 146/** 147* 获取插件执行命令 148*/ 149function nextPluginExeCommand(nextPluginId) { 150 if (nextPluginId == "kaihong.ApiScan") { 151 return 'api_scan'; 152 } else if (nextPluginId == "kaihong.gn-gen") { 153 return 'generate_gn'; 154 } else if (nextPluginId == "kaihong.service-gen") { 155 return 'generate_service'; 156 } else if (nextPluginId == "kaihong.ts-gen") { 157 return 'generate_ts'; 158 } else if (nextPluginId == "kaihong.napi-gen") { 159 return 'generate_napi'; 160 } 161 else { 162 return null; 163 } 164} 165 166/** 167* 执行完毕后开启工具链中下一个插件 168*/ 169function startNextPlugin() { 170 const extension = vscode.extensions.getExtension(nextPluginId); 171 if (extension) { 172 let startNextPlugin = nextPluginExeCommand(nextPluginId); 173 try { 174 vscode.commands.executeCommand(startNextPlugin, '', importToolChain, extensionIds); 175 } catch (error) { 176 console.error(error) 177 } 178 } 179} 180 181/** 182* 选择本地目录/文件夹 183*/ 184 function selectPath(panel, message) { 185 let msg = message.msg; 186 let mode = 1; 187 if (message.mode != undefined) { 188 mode = message.mode; 189 } 190 const options = { 191 canSelectMany: mode == 0 ? true : false,//是否可以选择多个 192 openLabel: mode == 0 ? '选择文件' : '选择文件夹',//打开选择的右下角按钮label 193 canSelectFiles: mode == 0 ? true : false,//是否选择文件 194 canSelectFolders: mode == 0 ? false : true,//是否选择文件夹 195 defaultUri:vscode.Uri.file(''),//默认打开本地路径 196 // 文件过滤选项,在文件夹选择模式下不可设置此配置,否则ubuntu系统下无法选择文件夹 197 filters: mode == 1 ? {} : { 'Text files': ['d.ts'] } 198 199 }; 200 201 return vscode.window.showOpenDialog(options).then(fileUri => { 202 if (fileUri && fileUri[0]) { 203 console.log('Selected file: ' + fileUri[0].fsPath); 204 let filePath = ""; 205 for (let index = 0; index < fileUri.length; index++) { 206 filePath += fileUri[index].fsPath.concat(","); 207 } 208 var result = { 209 msg: message.msg, 210 path: filePath.length > 0 ? filePath.substring(0, filePath.length - 1) : filePath 211 } 212 panel.webview.postMessage(result); 213 return fileUri[0].fsPath 214 } 215 }); 216} 217 218function checkMode(name, genDir, mode,numberType, importIsCheck) { 219 name = re.replaceAll(name, " ", ""); 220 if ("" == name) { 221 vscode.window.showErrorMessage("Please enter the path!"); 222 return; 223 } 224 if (mode == 0) { 225 if (name.indexOf(".") < 0) { 226 vscode.window.showErrorMessage("Please enter the correct file path!"); 227 return; 228 } 229 } else { 230 if (name.indexOf(".") > 0 || !fs.lstatSync(name).isDirectory()) { 231 vscode.window.showErrorMessage("Please enter the correct folder folder!"); 232 return; 233 } 234 } 235 if (exeFileExit()) { 236 executor(name, genDir, mode,numberType, importIsCheck); 237 } else { 238 vscode.window.showInformationMessage("Copy executable program to " + __dirname); 239 } 240} 241 242// this method is called when your extension is deactivated 243function deactivate() { } 244 245function getWebviewContent(context, importToolChain) { 246 let data = readFile(__dirname + '/vs_plugin_view.html'); 247 data = getWebViewContent(context, '/vs_plugin_view.html'); 248 let content = data.toString(); 249 if (importToolChain) { 250 content = content.replace('Ok', 'Next'); 251 } 252 return content; 253} 254 255function getWebViewContent(context, templatePath) { 256 const resourcePath = path.join(context.extensionPath, templatePath); 257 const dirPath = path.dirname(resourcePath); 258 let html = fs.readFileSync(resourcePath, 'utf-8'); 259 html = html.replace(/(<link.+?href="|<script.+?src="|<iframe.+?src="|<img.+?src=")(.+?)"/g, (m, $1, $2) => { 260 if ($2.indexOf("https://") < 0) { 261 return $1 + globalPanel.webview.asWebviewUri(vscode.Uri.file(path.resolve(dirPath, $2))) + '"'; 262 } else { 263 return $1 + $2+'"'; 264 } 265 }); 266 return html; 267} 268 269module.exports = { 270 activate, 271 deactivate 272}