diff options
Diffstat (limited to 'v_windows/v/old/cmd/tools/vfmt.v')
-rw-r--r-- | v_windows/v/old/cmd/tools/vfmt.v | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/v_windows/v/old/cmd/tools/vfmt.v b/v_windows/v/old/cmd/tools/vfmt.v new file mode 100644 index 0000000..ae10f1a --- /dev/null +++ b/v_windows/v/old/cmd/tools/vfmt.v @@ -0,0 +1,334 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module main + +import os +import os.cmdline +import rand +import term +import v.ast +import v.pref +import v.fmt +import v.util +import v.util.diff +import v.parser +import vhelp + +struct FormatOptions { + is_l bool + is_c bool // NB: This refers to the '-c' fmt flag, NOT the C backend + is_w bool + is_diff bool + is_verbose bool + is_all bool + is_debug bool + is_noerror bool + is_verify bool // exit(1) if the file is not vfmt'ed + is_worker bool // true *only* in the worker processes. NB: workers can crash. +} + +const ( + formatted_file_token = '\@\@\@' + 'FORMATTED_FILE: ' + vtmp_folder = util.get_vtmp_folder() + term_colors = term.can_show_color_on_stderr() +) + +fn main() { + // if os.getenv('VFMT_ENABLE') == '' { + // eprintln('v fmt is disabled for now') + // exit(1) + // } + toolexe := os.executable() + util.set_vroot_folder(os.dir(os.dir(os.dir(toolexe)))) + args := util.join_env_vflags_and_os_args() + mut foptions := FormatOptions{ + is_c: '-c' in args + is_l: '-l' in args + is_w: '-w' in args + is_diff: '-diff' in args + is_verbose: '-verbose' in args || '--verbose' in args + is_all: '-all' in args || '--all' in args + is_worker: '-worker' in args + is_debug: '-debug' in args + is_noerror: '-noerror' in args + is_verify: '-verify' in args + } + if term_colors { + os.setenv('VCOLORS', 'always', true) + } + if foptions.is_verbose { + eprintln('vfmt foptions: $foptions') + } + if foptions.is_worker { + // -worker should be added by a parent vfmt process. + // We launch a sub process for each file because + // the v compiler can do an early exit if it detects + // a syntax error, but we want to process ALL passed + // files if possible. + foptions.format_file(cmdline.option(args, '-worker', '')) + exit(0) + } + // we are NOT a worker at this stage, i.e. we are a parent vfmt process + possible_files := cmdline.only_non_options(cmdline.options_after(args, ['fmt'])) + if foptions.is_verbose { + eprintln('vfmt toolexe: $toolexe') + eprintln('vfmt args: ' + os.args.str()) + eprintln('vfmt env_vflags_and_os_args: ' + args.str()) + eprintln('vfmt possible_files: ' + possible_files.str()) + } + files := util.find_all_v_files(possible_files) or { + verror(err.msg) + return + } + if os.is_atty(0) == 0 && files.len == 0 { + foptions.format_pipe() + exit(0) + } + if files.len == 0 || '-help' in args || '--help' in args { + vhelp.show_topic('fmt') + exit(0) + } + mut cli_args_no_files := []string{} + for a in os.args { + if a !in files { + cli_args_no_files << a + } + } + mut errors := 0 + for file in files { + fpath := os.real_path(file) + mut worker_command_array := cli_args_no_files.clone() + worker_command_array << ['-worker', util.quote_path(fpath)] + worker_cmd := worker_command_array.join(' ') + if foptions.is_verbose { + eprintln('vfmt worker_cmd: $worker_cmd') + } + worker_result := os.execute(worker_cmd) + // Guard against a possibly crashing worker process. + if worker_result.exit_code != 0 { + eprintln(worker_result.output) + if worker_result.exit_code == 1 { + eprintln('Internal vfmt error while formatting file: ${file}.') + } + errors++ + continue + } + if worker_result.output.len > 0 { + if worker_result.output.contains(formatted_file_token) { + wresult := worker_result.output.split(formatted_file_token) + formatted_warn_errs := wresult[0] + formatted_file_path := wresult[1].trim_right('\n\r') + foptions.post_process_file(fpath, formatted_file_path) or { errors = errors + 1 } + if formatted_warn_errs.len > 0 { + eprintln(formatted_warn_errs) + } + continue + } + } + errors++ + } + if errors > 0 { + eprintln('Encountered a total of: $errors errors.') + if foptions.is_noerror { + exit(0) + } + if foptions.is_verify { + exit(1) + } + if foptions.is_c { + exit(2) + } + exit(1) + } +} + +fn (foptions &FormatOptions) format_file(file string) { + mut prefs := pref.new_preferences() + prefs.is_fmt = true + if foptions.is_verbose { + eprintln('vfmt2 running fmt.fmt over file: $file') + } + table := ast.new_table() + // checker := checker.new_checker(table, prefs) + file_ast := parser.parse_file(file, table, .parse_comments, prefs) + // checker.check(file_ast) + formatted_content := fmt.fmt(file_ast, table, prefs, foptions.is_debug) + file_name := os.file_name(file) + ulid := rand.ulid() + vfmt_output_path := os.join_path(vtmp_folder, 'vfmt_${ulid}_$file_name') + os.write_file(vfmt_output_path, formatted_content) or { panic(err) } + if foptions.is_verbose { + eprintln('fmt.fmt worked and $formatted_content.len bytes were written to $vfmt_output_path .') + } + eprintln('$formatted_file_token$vfmt_output_path') +} + +fn (foptions &FormatOptions) format_pipe() { + mut prefs := pref.new_preferences() + prefs.is_fmt = true + if foptions.is_verbose { + eprintln('vfmt2 running fmt.fmt over stdin') + } + input_text := os.get_raw_lines_joined() + table := ast.new_table() + // checker := checker.new_checker(table, prefs) + file_ast := parser.parse_text(input_text, '', table, .parse_comments, prefs) + // checker.check(file_ast) + formatted_content := fmt.fmt(file_ast, table, prefs, foptions.is_debug) + print(formatted_content) + if foptions.is_verbose { + eprintln('fmt.fmt worked and $formatted_content.len bytes were written to stdout.') + } +} + +fn print_compiler_options(compiler_params &pref.Preferences) { + eprintln(' os: ' + compiler_params.os.str()) + eprintln(' ccompiler: $compiler_params.ccompiler') + eprintln(' path: $compiler_params.path ') + eprintln(' out_name: $compiler_params.out_name ') + eprintln(' vroot: $compiler_params.vroot ') + eprintln('lookup_path: $compiler_params.lookup_path ') + eprintln(' out_name: $compiler_params.out_name ') + eprintln(' cflags: $compiler_params.cflags ') + eprintln(' is_test: $compiler_params.is_test ') + eprintln(' is_script: $compiler_params.is_script ') +} + +fn (foptions &FormatOptions) post_process_file(file string, formatted_file_path string) ? { + if formatted_file_path.len == 0 { + return + } + if foptions.is_diff { + diff_cmd := diff.find_working_diff_command() or { + eprintln(err) + return + } + if foptions.is_verbose { + eprintln('Using diff command: $diff_cmd') + } + diff := diff.color_compare_files(diff_cmd, file, formatted_file_path) + if diff.len > 0 { + println(diff) + } + return + } + if foptions.is_verify { + diff_cmd := diff.find_working_diff_command() or { + eprintln(err) + return + } + x := diff.color_compare_files(diff_cmd, file, formatted_file_path) + if x.len != 0 { + println("$file is not vfmt'ed") + return error('') + } + return + } + fc := os.read_file(file) or { + eprintln('File $file could not be read') + return + } + formatted_fc := os.read_file(formatted_file_path) or { + eprintln('File $formatted_file_path could not be read') + return + } + is_formatted_different := fc != formatted_fc + if foptions.is_c { + if is_formatted_different { + eprintln('File is not formatted: $file') + return error('') + } + return + } + if foptions.is_l { + if is_formatted_different { + eprintln('File needs formatting: $file') + } + return + } + if foptions.is_w { + if is_formatted_different { + os.mv_by_cp(formatted_file_path, file) or { panic(err) } + eprintln('Reformatted file: $file') + } else { + eprintln('Already formatted file: $file') + } + return + } + print(formatted_fc) +} + +fn (f FormatOptions) str() string { + return + 'FormatOptions{ is_l: $f.is_l, is_w: $f.is_w, is_diff: $f.is_diff, is_verbose: $f.is_verbose,' + + ' is_all: $f.is_all, is_worker: $f.is_worker, is_debug: $f.is_debug, is_noerror: $f.is_noerror,' + + ' is_verify: $f.is_verify" }' +} + +fn file_to_mod_name_and_is_module_file(file string) (string, bool) { + mut mod_name := 'main' + mut is_module_file := false + flines := read_source_lines(file) or { return mod_name, is_module_file } + for fline in flines { + line := fline.trim_space() + if line.starts_with('module ') { + if !line.starts_with('module main') { + is_module_file = true + mod_name = line.replace('module ', ' ').trim_space() + } + break + } + } + return mod_name, is_module_file +} + +fn read_source_lines(file string) ?[]string { + source_lines := os.read_lines(file) or { return error('can not read $file') } + return source_lines +} + +fn get_compile_name_of_potential_v_project(file string) string { + // This function get_compile_name_of_potential_v_project returns: + // a) the file's folder, if file is part of a v project + // b) the file itself, if the file is a standalone v program + pfolder := os.real_path(os.dir(file)) + // a .v project has many 'module main' files in one folder + // if there is only one .v file, then it must be a standalone + all_files_in_pfolder := os.ls(pfolder) or { panic(err) } + mut vfiles := []string{} + for f in all_files_in_pfolder { + vf := os.join_path(pfolder, f) + if f.starts_with('.') || !f.ends_with('.v') || os.is_dir(vf) { + continue + } + vfiles << vf + } + if vfiles.len == 1 { + return file + } + // ///////////////////////////////////////////////////////////// + // At this point, we know there are many .v files in the folder + // We will have to read them all, and if there are more than one + // containing `fn main` then the folder contains multiple standalone + // v programs. If only one contains `fn main` then the folder is + // a project folder, that should be compiled with `v pfolder`. + mut main_fns := 0 + for f in vfiles { + slines := read_source_lines(f) or { panic(err) } + for line in slines { + if line.contains('fn main()') { + main_fns++ + if main_fns > 1 { + return file + } + } + } + } + return pfolder +} + +[noreturn] +fn verror(s string) { + util.verror('vfmt error', s) +} |