import * as vscode from 'vscode';
import * as fs from 'fs';
import * as fspath from 'path';
import { AbsPath, opeParam, MainOutput, ReportType } from '../../global';
import { hdlParam, HdlModule } from '../../hdlParser/core';
import { HdlModulePort, HdlModuleParam } from '../../hdlParser/common';
import { MarkdownString, RenderString, RenderType,
mergeSortByLine, getWavedromsFromFile, Count, WavedromString } from './common';
import { hdlPath, hdlFile } from '../../hdlFs';
import { getSymbolComments } from '../lsp/util/feature';
import { HdlLangID, ThemeType } from '../../global/enum';
import { makeDiagram } from './diagram';
function makeSVGElementByLink(link: AbsPath, caption?: string) {
let mainHtml;
if (caption) {
mainHtml = `

${caption}
`;
} else {
mainHtml = `
`;
}
return '
' + mainHtml + '
\n';
}
function selectFieldValue(obj: any, subName: string, ws: string, name: string): string {
if (subName === 'empty') {
return '——';
}
let value = obj[subName];
if (subName === 'instModPath' && value) {
value = value.replace(ws, '');
}
if (value && value.trim().length === 0) {
return '——';
}
// TODO : 1 not known
if (name === 'ports' && value === '1') {
return '——';
}
return value;
}
function makeTableFromObjArray(md: MarkdownString, array: any[], name: string, fieldNames: string[], displayNames: string[]) {
const ws = hdlPath.toSlash(opeParam.workspacePath) + '/';
console.log('enter showhtml');
if (array.length === 0) {
md.addText(`no ${name} info`);
} else {
const rows = [];
for (const obj of array) {
const data = [];
for (const subName of fieldNames) {
const value = selectFieldValue(obj, subName, ws, name);
data.push(value);
}
rows.push(data);
}
if (displayNames) {
md.addTable(displayNames, rows);
} else {
md.addTable(fieldNames, rows);
}
}
}
/**
* @description add attribute description to each port/param
* @param {string} path
* @param {Array} ports
*/
async function patchComment(path: AbsPath, ports: (HdlModulePort | HdlModuleParam)[]) {
if (!ports || !ports.length) {
return;
}
const ranges = ports.map(port => port.range);
const comments = await getSymbolComments(path, ranges);
console.log(ranges);
for (let i = 0; i < ports.length; ++ i) {
let inlineComment = comments[i].replace(/\n/, ' ');
if (inlineComment.startsWith('//') || inlineComment.startsWith('--')) {
inlineComment = inlineComment.substring(2);
}
ports[i].desc = inlineComment;
}
}
/**
* @description get basedoc obj from a module
* @param module
*/
async function getDocsFromModule(module: HdlModule): Promise {
const moduleName = module.name;
const portNum = module.ports.length;
const paramNum = module.params.length;
// add desc can optimizer in the future version
const paramPP = patchComment(module.path, module.params);
const portPP = patchComment(module.path, module.ports);
let topModuleDesc = '';
if (hdlParam.isTopModule(module.path, module.name)) {
topModuleDesc = '√';
} else {
topModuleDesc = '×';
}
const md = new MarkdownString(module.range.start.line);
if (module.languageId === HdlLangID.Vhdl) {
md.addTitle('Entity: `' + moduleName + '`', 1);
} else if (module.languageId === HdlLangID.Verilog) {
md.addTitle('Module: `' + moduleName + '`', 1);
} else if (module.languageId === HdlLangID.SystemVerilog) {
md.addTitle('Module: `' + moduleName + '`', 1);
}
// add module name
md.addTitle('Basic Info', 2);
const infos = [
`**File:** ${fspath.basename(module.file.path)}`,
`${paramNum} params, ${portNum} ports`,
'top module ' + topModuleDesc
];
md.addUnorderedList(infos);
md.addEnter();
const diagram = makeDiagram(module.params, module.ports);
md.addText(diagram);
// wait param and port patch
await paramPP;
await portPP;
// param section
md.addTitle('Params', 2);
makeTableFromObjArray(md, module.params, 'params',
['name', 'init', 'empty', 'desc'],
['Param Name', 'Init', 'Range', 'Description']);
md.addEnter();
// port section
md.addTitle('Ports', 2);
makeTableFromObjArray(md, module.ports, 'ports',
['name', 'type', 'width', 'desc'],
['Port Name', 'Direction', 'Range', 'Description']);
md.addEnter();
// dependency section
md.addTitle('Dependency', 2);
const insts = [];
for (const inst of module.getAllInstances()) {
insts.push(inst);
}
makeTableFromObjArray(md, insts, 'Dependencies',
['name', 'type', 'instModPath'],
['name', 'module', 'path']);
md.addEnter();
return md;
}
/**
* @description get basedoc obj according to a file
* @param path absolute path of the file
*/
async function getDocsFromFile(path: AbsPath): Promise {
const moduleFile = hdlParam.getHdlFile(path);
if (!moduleFile) {
MainOutput.report('Fail to export documentation of ' + path,
ReportType.Error);
const errorMsg = `${path} is not a valid hdl file in our parse list, check your property.json to see if arch.hardware.src is set correctly!
\ncurrent parse list: \n${opeParam.prjInfo.hardwareSrcPath}\n${opeParam.prjInfo.hardwareSimPath}`;
vscode.window.showErrorMessage(errorMsg);
return undefined;
}
const markdownStringPromises = [];
for (const module of moduleFile.getAllHdlModules()) {
const markdownStringPromise = getDocsFromModule(module);
markdownStringPromises.push(markdownStringPromise);
}
const fileDocs = [];
for (const p of markdownStringPromises) {
const markdownString = await p;
fileDocs.push(markdownString);
}
return fileDocs;
}
/**
* @description get render list of path
* @param path
*/
async function getRenderList(path: AbsPath): Promise {
if (!hdlFile.isHDLFile(path)) {
vscode.window.showErrorMessage('Please use the command in a HDL file!');
return [];
}
const docs = await getDocsFromFile(path);
const svgs = await getWavedromsFromFile(path);
if (docs && svgs) {
const renderList = mergeSortByLine(docs, svgs);
return renderList;
}
return undefined;
}
/**
* @description return render list of current file
*/
async function getCurrentRenderList(): Promise {
const editor = vscode.window.activeTextEditor;
if (editor) {
const currentFilePath = hdlPath.toSlash(editor.document.fileName);
return await getRenderList(currentFilePath);
}
return;
}
async function exportCurrentFileDocAsMarkdown() {
const editor = vscode.window.activeTextEditor;
if (!editor) {
return;
}
const currentFilePath = hdlPath.toSlash(editor.document.fileName);
const hdlFileName = hdlPath.basename(currentFilePath);
const renderList = await getRenderList(currentFilePath);
if (!renderList || renderList.length === 0) {
return;
}
const wsPath = opeParam.workspacePath;
const markdownFolderPath = hdlPath.join(wsPath, 'markdown');
if (!fs.existsSync(markdownFolderPath)) {
fs.mkdirSync(markdownFolderPath);
}
const currentRoot = hdlPath.join(markdownFolderPath, hdlFileName);
if (fs.existsSync(currentRoot)) {
hdlFile.rmSync(currentRoot);
}
fs.mkdirSync(currentRoot);
const figureFolder = hdlPath.join(currentRoot, 'figure');
fs.mkdirSync(figureFolder);
let markdown = '';
for (const r of renderList) {
if (r instanceof MarkdownString) {
markdown += r.renderMarkdown() + '\n';
} else if (r instanceof WavedromString) {
const svgString = r.render();
const svgName = 'wavedrom-' + Count.svgMakeTimes + '.svg';
const svgPath = hdlPath.join(figureFolder, svgName);
fs.writeFileSync(svgPath, svgString);
const relatePath = hdlPath.join('./figure', svgName);
const svgHtml = makeSVGElementByLink(relatePath);
markdown += '\n\n' + svgHtml + '\n\n';
}
}
const markdownName = 'index.md';
const markdownPath = hdlPath.join(currentRoot, markdownName);
Count.svgMakeTimes = 0;
fs.writeFileSync(markdownPath, markdown);
}
async function exportProjectDocAsMarkdown() {
vscode.window.showInformationMessage('this is exportProjectDocAsMarkdown');
}
export {
getDocsFromFile,
getRenderList,
getCurrentRenderList,
exportCurrentFileDocAsMarkdown,
exportProjectDocAsMarkdown
};