代码拉取完成,页面将自动刷新
const vscode = require('vscode');
const child = require('child_process');
const fetch = require('node-fetch').default;
const treeView = require('./src/treeView');
const moment = require('moment');
const { getRootPath, getSettingConfig, getIssuesFsPath, fixFilePathPre, setSettingsPassword } = require('./src/utils');
/**
* @param {vscode.ExtensionContext} context
*/
function activate(context) {
let globalScanFileMap = {};
let currentIssues = 'all';
let loginReady = false;
// 注册输出管道
const outputChannel = vscode.window.createOutputChannel('OperatorLink');
channelLog('extension "operatorlint" is now active!');
// 注册 TreeViewProvider
const issuesTree = new treeView.IssuesTreeViewProvider();
const hotspotsTree = new treeView.HotspotsTreeViewProvider();
const issuesView = vscode.window.createTreeView('issues', { treeDataProvider: issuesTree });
const hotspotsView = vscode.window.createTreeView('hotspots', { treeDataProvider: hotspotsTree });
// 监听treeview点击事件打开文件
issuesView.onDidChangeSelection(openFile);
hotspotsView.onDidChangeSelection(openFile);
// 注册扫描事件/接受扫描结果/打开window
const cppcheckStart = vscode.commands.registerCommand('operatorlint.startScanning', triggerScan);
const scanResults = vscode.commands.registerCommand('operatorlint.scanResults', () => reciveScanResult());
const openWin = vscode.commands.registerCommand('operatorlint.openWindow', operatorWin);
const openInput = vscode.commands.registerCommand('operatorlint.openInput', operatorInput);
const assignMy = vscode.commands.registerCommand('operatorlint.assignMy', () => updateIssuesType('my'));
const assignAll = vscode.commands.registerCommand('operatorlint.assignAll', () => updateIssuesType('all'));
// 注册代码提示集合
const collection = vscode.languages.createDiagnosticCollection('issuesmessage');
const changeAcitveEditor = vscode.window.onDidChangeActiveTextEditor(editor => editor && updateDiagnostics(editor.document));
const changeSettingsSub = vscode.workspace.onDidChangeConfiguration(settingsChange);
context.subscriptions.push(cppcheckStart);
context.subscriptions.push(scanResults);
context.subscriptions.push(changeAcitveEditor);
context.subscriptions.push(openWin);
context.subscriptions.push(openInput);
context.subscriptions.push(assignMy);
context.subscriptions.push(assignAll);
context.subscriptions.push(collection);
context.subscriptions.push(changeSettingsSub);
init();
//
// ---------------上面入口函数-----下面执行函数-----请遵守规则合理拆分代码文件-----------------------
//
/**
* 入口
*/
async function init() {
await updateIssuesType('all');
}
/**
* 监听settings.json文件变化
*/
async function settingsChange(e) {
if (e.affectsConfiguration('operatorlint.username') || e.affectsConfiguration('operatorlint.url')) {
loginReady = false;
}
}
/**
* 鉴权
* @returns {promise}
*/
async function passAuth(password) {
let result = null;
const { username, url } = getSettingConfig();
const formData = new URLSearchParams();
formData.append('login', username);
formData.append('password', password);
try {
const res = await fetch(url + '/api/authentication/login', {
method: 'post',
body: formData,
});
if (!res.ok) {
result = res.statusText || 'login failed';
channelLog('login failed:' + res.status + res.statusText);
}
} catch (e) {
result = 'Network failure or request blocked';
channelLog('login failed:' + e?.message);
}
return result;
}
/**
* 输出日志到output
* @param {*} message
*/
function channelLog(message) {
outputChannel.appendLine(message);
outputChannel.show();
}
/**
* 更新context
* @param {('my' | 'all')} type
*/
async function updateIssuesType(type) {
currentIssues = type;
await vscode.commands.executeCommand('setContext', 'operatorlint.issues.assign', type);
reciveScanResult();
}
/**
* 打开全局输入框
*/
function operatorInput() {
const { username, url } = getSettingConfig();
return new Promise((resolve, reject) => {
const inputBox = vscode.window.createInputBox();
inputBox.title = 'operator-lint';
inputBox.prompt = `Enter password for ${username}, ip ${url}`;
inputBox.ignoreFocusOut = true;
inputBox.password = true;
inputBox.placeholder = `Enter password for ${username}, ip ${url}`;
inputBox.onDidAccept(async () => {
const value = inputBox.value;
if (!value) {
inputBox.validationMessage = 'Password can not be blank';
return;
}
const result = await passAuth(value);
if (result) {
inputBox.validationMessage = result;
} else {
setSettingsPassword(value);
resolve();
inputBox.dispose();
}
});
inputBox.onDidHide(() => {
reject();
});
inputBox.show();
});
}
/**
* 打开webview
*/
function operatorWin() {
const settingsConfg = getSettingConfig();
const panel = vscode.window.createWebviewPanel(
'operatorlintWin',
'operatorlint window',
vscode.ViewColumn.One,
{ enableScripts: true }
);
panel.webview.html = getWebviewContent();
function getWebviewContent() {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>operatorlint</title>
</head>
<script>
// window.location.href = '${settingsConfg.url}';
</script>
<body>
<h1 style="text-align: center">建设中。</h1>
</body>
</html>`;
}
}
/**
* 代码错误/警告提示
* @param {*} document
*/
function updateDiagnostics(document) {
const diagIssues = globalScanFileMap[document.uri.query ? JSON.parse(document.uri.query).path : document.uri.fsPath];
if (diagIssues) {
collection.set(document.uri, diagIssues.map(i => ({
code: '',
message: i.message,
range: new vscode.Range(new vscode.Position(i.textRange.startLine - 1, i.textRange.startOffset), new vscode.Position(i.textRange.endLine - 1, i.textRange.endOffset)),
severity: vscode.DiagnosticSeverity.Warning,
source: '',
})));
} else {
collection.clear();
}
}
/**
* 触发远端扫描项目
* @param {{fsPath: string}} [param] 菜单右键会有传参传入
*/
async function triggerScan(param) {
await checkProjectInit();
const workspaceFolderPath = getRootPath();
const { username, password, url, projectkey } = getSettingConfig();
const fsPath = param?.fsPath ? param.fsPath.replace(workspaceFolderPath, '').replaceAll('\\', '/').substr(1) : '.';
const exeCode = `bash ${workspaceFolderPath}/codecheck.sh ${projectkey} ${url} ${username} ${password} ${fsPath}`;
vscode.window.showInformationMessage('Manual trigger scan successful.');
channelLog('sh exec.');
const childProcess = child.exec(exeCode, { cwd: workspaceFolderPath });
childProcess.stdout.on('data', data => {
const now = moment();
channelLog((`[${now.format('YYYY-MM-DD hh:mm:ss')} STDOUT]` + data).trim());
});
childProcess.stderr.on('data', data => {
const now = moment();
channelLog((`[${now.format('YYYY-MM-DD hh:mm:ss')} STDERR]` + data).trim());
});
childProcess.on('exit', data => {
const now = moment();
channelLog((`[${now.format('YYYY-MM-DD hh:mm:ss')} EXIT]` + data).trim());
reciveScanResult();
});
}
/**
* 确认项目初始化所必须的参数
* @returns {Promise}
*/
async function checkProjectInit() {
const rootPath = getRootPath();
const { username, url, projectkey } = getSettingConfig();
if (!username || !url || !projectkey) {
vscode.window.showErrorMessage('Please configure ".vscode/settings.json operatorlint.username operatorlint.url operatorlint.projectkey" parameters.');
return Promise.reject();
}
if (!rootPath) {
vscode.window.showErrorMessage('Please open the project file.');
return Promise.reject();
}
if (!loginReady) {
return await operatorInput();
}
return Promise.resolve();
}
/**
* 接收扫描结果
*/
async function reciveScanResult() {
await checkProjectInit();
const { username, password, url, projectkey } = getSettingConfig();
let issuesUrl = url + '/api/issues/search?resolved=false&s=FILE_LINE&componentKeys=' + projectkey + '&p=1&ps=500';
let hotspotsUrl = url + '/api/hotspots/search?status=TO_REVIEW&projectKey=' + projectkey + '&p=1&ps=500';
if (currentIssues === 'my') {
issuesUrl += '&assignees=__me__';
hotspotsUrl += '&onlyMine=true';
}
channelLog('start recive results.');
let resBody;
try {
resBody = await Promise.all([
fetch(issuesUrl, {
method: 'get',
headers: {
'Authorization': 'Basic ' + Buffer.from(`${username}:${password}`).toString('base64')
}
}),
fetch(hotspotsUrl, {
method: 'get',
headers: {
'Authorization': 'Basic ' + Buffer.from(`${username}:${password}`).toString('base64')
}
})
]);
} catch (e) {
channelLog('recive results error.');
channelLog(e);
loginReady = false;
}
if (resBody[0].ok) {
channelLog('Successfully obtained scan results.');
loginReady = true;
} else {
channelLog('recive results error.');
channelLog(resBody[0].statusText + resBody[1].statusText);
loginReady = false;
}
const res = [];
res[0] = await resBody?.[0]?.json();
res[1] = await resBody?.[1]?.json();
const issuesData = res?.[0]?.issues || [];
const hotspotsData = res?.[1]?.hotspots || [];
const issuesResult = [];
const hotspotsResult = [];
const issuesMap = {};
const hotspotsMap = {};
globalScanFileMap = {};
issuesData.forEach(i => {
if (!issuesMap[i.type]) {
issuesMap[i.type] = {};
}
if (!issuesMap[i.type][i.component]) {
issuesMap[i.type][i.component] = [];
}
issuesMap[i.type][i.component].push(i);
});
hotspotsData.forEach(i => {
if (hotspotsMap[i.component]) {
hotspotsMap[i.component].push(i);
} else {
hotspotsMap[i.component] = [i];
}
});
Object.keys(issuesMap).forEach(key => {
const parentItem = {
label: key,
children: []
};
issuesResult.push(parentItem);
Object.keys(issuesMap[key]).forEach(i => {
parentItem.children.push({
label: fixFilePathPre(i),
children: issuesMap[key][i].map(j => ({ ...j, label: j.message }))
});
if (!globalScanFileMap[getIssuesFsPath(i)]) globalScanFileMap[getIssuesFsPath(i)] = [];
globalScanFileMap[getIssuesFsPath(i)].push(...issuesMap[key][i].filter(j => j.textRange));
});
});
Object.keys(hotspotsMap).forEach(i => {
hotspotsResult.push({
label: fixFilePathPre(i),
children: hotspotsMap[i].map(j => ({ ...j, label: j.message }))
});
if (!globalScanFileMap[getIssuesFsPath(i)]) globalScanFileMap[getIssuesFsPath(i)] = [];
globalScanFileMap[getIssuesFsPath(i)].push(...hotspotsMap[i].filter(j => j.textRange));
});
issuesTree.refresh(issuesResult);
hotspotsTree.refresh(hotspotsResult);
if (vscode.window.activeTextEditor) updateDiagnostics(vscode.window.activeTextEditor.document);
}
/**
* 移动光标到issues代码
* @param {*} textRange
*/
function cursorToRange(textRange) {
if (vscode.window.activeTextEditor) {
vscode.window.activeTextEditor.revealRange(
new vscode.Range(new vscode.Position(textRange.endLine - 1, 0), new vscode.Position(textRange.endLine - 1, 0)),
vscode.TextEditorRevealType.InCenter
);
vscode.window.activeTextEditor.selection = new vscode.Selection(
new vscode.Position(textRange.endLine - 1, textRange.endOffset),
new vscode.Position(textRange.endLine - 1, textRange.endOffset)
);
}
}
/**
* 执行打开文件操作
* @param {*} e
*/
function openFile(e) {
if (!e?.selection?.[0]?.children) {
const rootPath = getRootPath();
const config = getSettingConfig();
const currentIssue = e.selection[0];
vscode.workspace
.openTextDocument(rootPath + '/' + currentIssue.component.replace(config.projectkey + ':', ''))
.then(async (doc) => {
await vscode.window.showTextDocument(doc);
currentIssue?.textRange && cursorToRange(currentIssue.textRange);
});
}
};
}
// This method is called when your extension is deactivated
function deactivate() { }
module.exports = {
activate,
deactivate
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。