diff --git a/CHANGELOG.md b/CHANGELOG.md index d4d71565e445f7..d3e9d90b74612a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +## V 0.4.5 TODO + ## V 0.4.4 *9 January 2024* diff --git a/cmd/tools/changelog_helper.v b/cmd/tools/changelog_helper.v index 88d61c97ffa2c2..fab453f675ab8b 100644 --- a/cmd/tools/changelog_helper.v +++ b/cmd/tools/changelog_helper.v @@ -15,30 +15,47 @@ enum Category { db native cgen + js_backend comptime tools compiler_internals examples vfmt + os_support } -/* -#### Improvements in the language +const category_titles = '#### Improvements in the language + #### Breaking changes + #### Checker improvements/fixes + #### Parser improvements + #### Compiler internals + #### Standard library + #### Web + #### ORM + #### Database drivers + #### Native backend + #### C backend + +#### JavaScript backend + #### vfmt + #### Tools + #### Operating System support + #### Examples -*/ +' struct Line { category Category @@ -48,22 +65,63 @@ struct Line { const log_txt = 'log.txt' struct App { + version string // e.g. "0.4.5" total_lines int mut: + result string // resulting CHANGELOG.md counter int } +const is_interactive = false + +// Instantly updates CHANGELOG.md without confirming each line +fn no_interactive(version string) { +} + fn main() { + mut version := '' + + if os.args.len == 2 && os.args[1].starts_with('0.') { + version = os.args[1] + // no_interactive(version) + // return + } else { + println('Usage: v run tools/changelog_helper.v 0.4.5') + return + } if !os.exists(log_txt) { os.execute(git_log_cmd + ' > ' + log_txt) - println('log.txt generated, remove unnecessary commits from it and run the tool again') - return + println('log.txt generated') + // println('log.txt generated, remove unnecessary commits from it and run the tool again') + // return + } + mut lines := os.read_lines(log_txt)! + // Trim everything before current version, commit "(tag: 0.4.4) V 0.4.4" + mut prev_version := (version.replace('.', '').int() - 1).str() + prev_version = '0.${prev_version[0].ascii_str()}.${prev_version[1].ascii_str()}' + println('prev version=${prev_version}') + for i, line in lines { + if line == ('V ${prev_version}') { + lines = lines[..i].clone() + break + } + } + os.write_file(log_txt, lines.join('\n'))! + if true { + // return } - lines := os.read_lines(log_txt)! - changelog_txt := os.read_file('CHANGELOG.md')!.to_lower() mut app := &App{ total_lines: lines.len } + // Write categories at the top first + app.result = os.read_file('CHANGELOG.md')!.replace_once('V ${version} TODO', 'V ${version}\n' + + category_titles) + os.write_file('CHANGELOG.md', app.result)! + changelog_txt := os.read_file('CHANGELOG.md')!.to_lower() + if true { + // println(changelog_txt) + // return + } // mut counter := 0 // to display how many commits are left for line in lines { s := line.trim_space() @@ -81,7 +139,11 @@ fn main() { continue } - app.process_line(line)! + app.process_line(line.trim_space())! + } + println('writing changelog.md') + if !is_interactive { + os.write_file('CHANGELOG.md', app.result)! } println('done.') } @@ -96,11 +158,19 @@ fn (mut app App) process_line(text string) ! { } prefix := text[..semicolon_pos] // Get category based on keywords in the commit message/prefix - mut category := Category.improvements + mut category := Category.examples if text.contains('checker:') { category = .checker - } else if text.contains('cgen:') { + } else if is_examples(text) { + // Always skip examples and typos fixes + category = .examples + return + } else if is_os_support(text) { + category = .os_support + } else if is_cgen(text) { category = .cgen + } else if is_js_backend(text) { + category = .js_backend } else if is_db(text) { category = .db } else if is_stdlib(text) { @@ -121,9 +191,6 @@ fn (mut app App) process_line(text string) ! { category = .native } else if is_vfmt(text) { category = .vfmt - } else if is_examples(text) { - // TODO maybe always skip these as well? - category = .examples } else if text.contains('docs:') || text.contains('doc:') { // Always skip docs delete_processed_line_from_log(text)! @@ -133,6 +200,7 @@ fn (mut app App) process_line(text string) ! { else { return } + println('process_line: cat=${category} "${text}"') // Trim everything to the left of `:` for some commits (e.g. `checker: `) mut s := text @@ -140,66 +208,71 @@ fn (mut app App) process_line(text string) ! { // if true { // exit(0) //} - if semicolon_pos < 15 && prefix in ['checker', 'cgen'] { + if (semicolon_pos < 15 + && prefix in ['checker', 'cgen', 'parser', 'v.parser', 'ast', 'jsgen', 'v.gen.js', 'fmt', 'vfmt']) + || (semicolon_pos < 30 && prefix.contains(', ')) { s = '- ' + text[semicolon_pos + 2..].capitalize() } - // Get input from the user - print('\033[H\033[J') - println('${app.counter} / ${app.total_lines}') - // println('\n') - println(text) - input := os.input('${category}? ') - println("INPUT='${input}'") - match input { - '' { - println('GOT ENTER') - line := Line{category, s} - save_line(line)! - } - 'n', '0', 'no' { - // Ignore commit - println('ignored.') - } - 's', 'skip' { - // Skip - println('skipped.') - return - } - 'c', 'change' { - // Change category - for { - print_category_hint() - custom_category := os.input('${category} ?').int() - if custom_category == 0 { - println('wrong category') - } else { - unsafe { - line := Line{Category(custom_category - 1), s} - save_line(line)! + if is_interactive { + // Get input from the user + print('\033[H\033[J') + println('${app.counter} / ${app.total_lines}') + // println('\n') + println(text) + input := os.input('${category}? ') + println("INPUT='${input}'") + match input { + '' { + println('GOT ENTER') + line := Line{category, s} + save_line_interactive(line)! + } + 'n', '0', 'no' { + // Ignore commit + println('ignored.') + } + 's', 'skip' { + // Skip + println('skipped.') + return + } + 'c', 'change' { + // Change category + for { + print_category_hint() + custom_category := os.input('${category} ?').int() + if custom_category == 0 { + println('wrong category') + } else { + unsafe { + line := Line{Category(custom_category - 1), s} + save_line_interactive(line)! + } + break } - break } } + else {} } - else {} + app.counter++ + } else { + line := Line{category, s} + app.save_line(line)! } - app.counter++ // Don't forget to remove the line we just processed from log.txt delete_processed_line_from_log(text)! } -fn save_line(line Line) ! { - println('save line ${line}') - mut txt := os.read_file('CHANGELOG.md')! +fn (mut app App) save_line(line Line) ! { + // println('save line ${line}') + app.result = line.write_at_category(app.result) or { return error('') } +} - // match line.category { - //.checker { +fn save_line_interactive(line Line) ! { + println('save line interactive ${line}') + mut txt := os.read_file('CHANGELOG.md')! txt = line.write_at_category(txt) or { return error('') } - // println(txt.limit(1000)) - //} - // else {} - //} os.write_file('CHANGELOG.md', txt)! } @@ -214,11 +287,13 @@ const category_map = { .db: '#### Database drivers' .native: '#### Native backend' .cgen: '#### C backend' + .js_backend: '#### JavaScript backend' .comptime: '#### Comptime' .tools: '#### Tools' .compiler_internals: '#### Compiler internals' .examples: '#### Examples' .vfmt: '#### vfmt' + .os_support: '#### Operating System' } fn (l Line) write_at_category(txt string) ?string { @@ -276,6 +351,7 @@ const db_strings = [ const improvements_strings = [ 'all:', 'v:', + 'coroutines:', ] const parser_strings = [ @@ -293,7 +369,7 @@ const stdlib_strings = [ 'math:', 'math.big', 'crypto', - 'sokol:', + 'sokol', 'os:', 'rand:', 'math:', @@ -309,6 +385,10 @@ const stdlib_strings = [ 'readline', 'cli:', 'eventbus:', + 'encoding.', + 'bitfield:', + 'io:', + 'log:', ] fn is_stdlib(text string) bool { @@ -327,6 +407,25 @@ fn is_orm(text string) bool { return is_xxx(text, orm_strings) } +const cgen_strings = [ + 'cgen:', + 'v.gen.c:', +] + +fn is_cgen(text string) bool { + return is_xxx(text, cgen_strings) +} + +const js_backend_strings = [ + 'js:', + 'v.gen.js:', + 'jsgen:', +] + +fn is_js_backend(text string) bool { + return is_xxx(text, js_backend_strings) +} + const internal_strings = [ 'scanner:', 'transformer:', @@ -348,6 +447,10 @@ const examples_strings = [ 'tests', 'readme:', '.md:', + 'typos', + ' typo', + 'cleanup', + 'clean up', ] fn is_examples(text string) bool { @@ -362,6 +465,7 @@ const tools_strings = [ 'gitignore', 'benchmark', 'v.help:', + 'vtest', ] fn is_tools(text string) bool { @@ -374,6 +478,7 @@ fn is_parser(text string) bool { const web_strings = [ 'vweb:', + 'x.vweb:', 'websocket:', 'picoev:', 'mbedtls', @@ -404,6 +509,23 @@ fn is_vfmt(text string) bool { return is_xxx(text, vfmt_strings) } +const os_support_strings = [ + 'FreeBSD', + 'freebsd', + 'OpenBSD', + 'openbsd', + 'macOS', + 'macos', + 'Windows', + 'windows', + 'Linux', + 'linux', +] + +fn is_os_support(text string) bool { + return is_xxx(text, os_support_strings) +} + fn is_xxx(text string, words []string) bool { for s in words { if text.contains(s) {