aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/cmd/tools/vfmt.v
diff options
context:
space:
mode:
authorIndrajith K L2022-12-03 17:00:20 +0530
committerIndrajith K L2022-12-03 17:00:20 +0530
commitf5c4671bfbad96bf346bd7e9a21fc4317b4959df (patch)
tree2764fc62da58f2ba8da7ed341643fc359873142f /v_windows/v/cmd/tools/vfmt.v
downloadcli-tools-windows-f5c4671bfbad96bf346bd7e9a21fc4317b4959df.tar.gz
cli-tools-windows-f5c4671bfbad96bf346bd7e9a21fc4317b4959df.tar.bz2
cli-tools-windows-f5c4671bfbad96bf346bd7e9a21fc4317b4959df.zip
Adds most of the toolsHEADmaster
Diffstat (limited to 'v_windows/v/cmd/tools/vfmt.v')
-rw-r--r--v_windows/v/cmd/tools/vfmt.v334
1 files changed, 334 insertions, 0 deletions
diff --git a/v_windows/v/cmd/tools/vfmt.v b/v_windows/v/cmd/tools/vfmt.v
new file mode 100644
index 0000000..ae10f1a
--- /dev/null
+++ b/v_windows/v/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)
+}