import 'package:codebrowser_dart/project.dart'; import 'package:codebrowser_dart/tag.dart'; import 'package:analyzer/dart/ast/token.dart'; import 'dart:io'; import 'package:path/path.dart' as path; import 'package:codebrowser_dart/html_header.dart'; class Generator { List tags = []; static bool _isLiteral(Token tok) { return tok.lexeme == 'true' || tok.lexeme == 'false' || tok.lexeme == 'null'; } void addTag(int start, int end, String name, {String attributes = ""}) { tags.add(Tag(start, end, name, attributes)); } void syntaxHighlight(Token begin, Token end) { Token? tok = begin; while (tok != end) { switch (tok!.type) { case TokenType.INT: case TokenType.DOUBLE: case TokenType.HEXADECIMAL: addTag(tok.charOffset, tok.charEnd, 'var'); break; default: if (tok.isKeyword || tok.isModifier) { if (_isLiteral(tok)) { addTag(tok.charOffset, tok.charEnd, 'var'); break; } addTag(tok.charOffset, tok.charEnd, 'b'); break; } break; } tok = tok.next; if (tok == null) { break; } } } void generateHtml( Project project, String sourceCode, String fileName, String dataPath, String outputDir, ) { String relativeDataPath = path.relative(dataPath, from: path.dirname(fileName)); String relativeFilePath = path.relative(fileName, from: outputDir); String relativeOutputPath = path.relative(outputDir, from: path.dirname(fileName)); StringBuffer out = StringBuffer( createHeader(relativeFilePath, relativeOutputPath, relativeDataPath, project)); out.write("
\n"); out.write("\n"); out.write("\n"); tags.sort((a, b) => a.start - b.start); out.write('\n'); int linenum = 1; out.write(''); out.write('\n'); linenum++; out.write('\n
$linenum'); bool inComment = false; List stack = []; int? currentTagEnd; int lastFoundTagIdx = 0; for (int i = 0; i < sourceCode.length; ++i) { // skip until we find a newline or `/`. `/` because it can be comment end if (inComment && sourceCode[i] != '\n' && sourceCode[i] != '/') { out.write(sourceCode[i]); continue; } if (currentTagEnd != null && i == currentTagEnd) { // remove from stack till we currentTagEnd == stack.top.end while (stack.isNotEmpty) { out.write(stack.last.close()); stack.removeLast(); if (stack.lastOrNull?.end != currentTagEnd) { break; } } currentTagEnd = stack.lastOrNull?.end; } for (int idx = lastFoundTagIdx; idx < tags.length; ++idx) { if (tags[idx].start == i) { final tag = tags[idx]; out.write(tag.open()); stack.add(tag); lastFoundTagIdx = idx + 1; currentTagEnd = stack.lastOrNull?.end; } else { break; } } switch (sourceCode[i]) { case '\n': if (inComment) { out.write(''); } var stackCopy = [...stack]; while (stack.isNotEmpty) { out.write(stack.last.close()); stack.removeLast(); } out.write('
$linenum'); if (inComment) { out.write(""); } for (final tag in stackCopy) { out.write(tag.open()); } stack = stackCopy; break; case ' ': out.write(' '); break; case '<': out.write('<'); break; case '>': out.write('>'); break; case '&': out.write('&'); break; case '/': if (i + 1 < sourceCode.length && sourceCode[i + 1] == '/') { // skip if doc comment if (i + 2 < sourceCode.length && sourceCode[i + 2] == '/') { out.write(sourceCode[i]); out.write(sourceCode[i + 1]); i += 1; break; } else { out.write(""); int end = sourceCode.indexOf('\n', i); out.write(sourceCode.substring(i, end)); out.write(""); i = end - 1; break; } } else if (i + 1 < sourceCode.length && sourceCode[i + 1] == '*') { out.write(""); inComment = true; } else if (inComment && i > 0 && sourceCode[i - 1] == '*') { inComment = false; out.write(sourceCode[i]); out.write(""); break; } out.write(sourceCode[i]); break; default: out.write(sourceCode[i]); break; } } out.write("
\n"); out.write(createFooter(project)); out.write("
\n\n\n"); File file = File("$fileName.html"); file.writeAsString(out.toString()); } }