Bläddra i källkod

Merge pull request #5181 from Feoramund/fix-5167

Make `odin help` more precise
Jeroen van Rijn 3 månader sedan
förälder
incheckning
b7783cac05
1 ändrade filer med 80 tillägg och 22 borttagningar
  1. 80 22
      src/main.cpp

+ 80 - 22
src/main.cpp

@@ -278,10 +278,10 @@ gb_internal void usage(String argv0, String argv1 = {}) {
 	print_usage_line(1, "                  One must contain the program's entry point, all must be in the same package.");
 	print_usage_line(1, "run               Same as 'build', but also then runs the newly compiled executable.");
 	print_usage_line(1, "bundle            Bundles a directory in a specific layout for that platform.");
-	print_usage_line(1, "check             Parses, and type checks a directory of .odin files.");
+	print_usage_line(1, "check             Parses and type checks a directory of .odin files.");
 	print_usage_line(1, "strip-semicolon   Parses, type checks, and removes unneeded semicolons from the entire program.");
 	print_usage_line(1, "test              Builds and runs procedures with the attribute @(test) in the initial package.");
-	print_usage_line(1, "doc               Generates documentation on a directory of .odin files.");
+	print_usage_line(1, "doc               Generates documentation from a directory of .odin files.");
 	print_usage_line(1, "version           Prints version.");
 	print_usage_line(1, "report            Prints information useful to reporting a bug.");
 	print_usage_line(1, "root              Prints the root path where Odin looks for the builtin collections.");
@@ -2221,20 +2221,30 @@ gb_internal void remove_temp_files(lbGenerator *gen) {
 }
 
 
-gb_internal void print_show_help(String const arg0, String command, String optional_flag = {}) {
+gb_internal int print_show_help(String const arg0, String command, String optional_flag = {}) {
+	bool help_resolved = false;
+	bool printed_usage_header = false;
+	bool printed_flags_header = false;
+
 	if (command == "help" && optional_flag.len != 0 && optional_flag[0] != '-') {
 		command = optional_flag;
 		optional_flag = {};
 	}
 
-	print_usage_line(0, "%.*s is a tool for managing Odin source code.", LIT(arg0));
-	print_usage_line(0, "Usage:");
-	print_usage_line(1, "%.*s %.*s [arguments]", LIT(arg0), LIT(command));
-	print_usage_line(0, "");
-	defer (print_usage_line(0, ""));
-
+	auto const print_usage_header_once = [&help_resolved, &printed_usage_header, arg0, command]() {
+		if (printed_usage_header) {
+			return;
+		}
+		print_usage_line(0, "%.*s is a tool for managing Odin source code.", LIT(arg0));
+		print_usage_line(0, "Usage:");
+		print_usage_line(1, "%.*s %.*s [arguments]", LIT(arg0), LIT(command));
+		print_usage_line(0, "");
+		help_resolved = true;
+		printed_usage_header = true;
+	};
 
 	if (command == "build") {
+		print_usage_header_once();
 		print_usage_line(1, "build   Compiles directory of .odin files as an executable.");
 		print_usage_line(2, "One must contain the program's entry point, all must be in the same package.");
 		print_usage_line(2, "Use `-file` to build a single file instead.");
@@ -2243,6 +2253,7 @@ gb_internal void print_show_help(String const arg0, String command, String optio
 		print_usage_line(3, "odin build <dir>                 Builds package in <dir>.");
 		print_usage_line(3, "odin build filename.odin -file   Builds single-file package, must contain entry point.");
 	} else if (command == "run") {
+		print_usage_header_once();
 		print_usage_line(1, "run     Same as 'build', but also then runs the newly compiled executable.");
 		print_usage_line(2, "Append an empty flag and then the args, '-- <args>', to specify args for the output.");
 		print_usage_line(2, "Examples:");
@@ -2250,28 +2261,40 @@ gb_internal void print_show_help(String const arg0, String command, String optio
 		print_usage_line(3, "odin run <dir>                 Builds and runs package in <dir>.");
 		print_usage_line(3, "odin run filename.odin -file   Builds and runs single-file package, must contain entry point.");
 	} else if (command == "check") {
+		print_usage_header_once();
 		print_usage_line(1, "check   Parses and type checks directory of .odin files.");
 		print_usage_line(2, "Examples:");
 		print_usage_line(3, "odin check .                     Type checks package in current directory.");
 		print_usage_line(3, "odin check <dir>                 Type checks package in <dir>.");
 		print_usage_line(3, "odin check filename.odin -file   Type checks single-file package, must contain entry point.");
 	} else if (command == "test") {
+		print_usage_header_once();
 		print_usage_line(1, "test    Builds and runs procedures with the attribute @(test) in the initial package.");
 	} else if (command == "doc") {
+		print_usage_header_once();
 		print_usage_line(1, "doc     Generates documentation from a directory of .odin files.");
 		print_usage_line(2, "Examples:");
 		print_usage_line(3, "odin doc .                     Generates documentation on package in current directory.");
 		print_usage_line(3, "odin doc <dir>                 Generates documentation on package in <dir>.");
 		print_usage_line(3, "odin doc filename.odin -file   Generates documentation on single-file package.");
 	} else if (command == "version") {
+		print_usage_header_once();
 		print_usage_line(1, "version   Prints version.");
 	} else if (command == "strip-semicolon") {
+		print_usage_header_once();
 		print_usage_line(1, "strip-semicolon");
 		print_usage_line(2, "Parses and type checks .odin file(s) and then removes unneeded semicolons from the entire project.");
 	} else if (command == "bundle")  {
+		print_usage_header_once();
 		print_usage_line(1, "bundle <platform>   Bundles a directory in a specific layout for that platform");
 		print_usage_line(2, "Supported platforms:");
 		print_usage_line(3, "android");
+	} else if (command == "report") {
+		print_usage_header_once();
+		print_usage_line(1, "report  Prints information useful to reporting a bug.");
+	} else if (command == "root") {
+		print_usage_header_once();
+		print_usage_line(1, "root    Prints the root path where Odin looks for the builtin collections.");
 	}
 
 	bool doc             = command == "doc";
@@ -2293,13 +2316,10 @@ gb_internal void print_show_help(String const arg0, String command, String optio
 		check           = true;
 	}
 
-	print_usage_line(0, "");
-	print_usage_line(1, "Flags");
-	print_usage_line(0, "");
 
 
 
-	auto const print_flag = [&optional_flag](char const *flag) -> bool {
+	auto const print_flag = [&optional_flag, &help_resolved, &printed_flags_header, print_usage_header_once](char const *flag) -> bool {
 		if (optional_flag.len != 0) {
 			String f = make_string_c(flag);
 			isize i = string_index_byte(f, ':');
@@ -2310,6 +2330,14 @@ gb_internal void print_show_help(String const arg0, String command, String optio
 				return false;
 			}
 		}
+		print_usage_header_once();
+		if (!printed_flags_header) {
+			print_usage_line(0, "");
+			print_usage_line(1, "Flags");
+			print_usage_line(0, "");
+			printed_flags_header = true;
+		}
+		help_resolved = true;
 		print_usage_line(0, "");
 		print_usage_line(1, flag);
 		return true;
@@ -2867,6 +2895,21 @@ gb_internal void print_show_help(String const arg0, String command, String optio
 			print_usage_line(2, "If this is omitted, the terminal will prompt you to provide it.");
 		}
 	}
+
+	if (!help_resolved) {
+		usage(arg0);
+		print_usage_line(0, "");
+		if (command == "help") {
+			print_usage_line(0, "'%.*s' is not a recognized flag.", LIT(optional_flag));
+		} else {
+			print_usage_line(0, "'%.*s' is not a recognized command.", LIT(command));
+		}
+		return 1;
+	}
+
+	print_usage_line(0, "");
+
+	return 0;
 }
 
 gb_internal void print_show_unused(Checker *c) {
@@ -3239,6 +3282,16 @@ int main(int arg_count, char const **arg_ptr) {
 	String run_args_string = {};
 	isize  last_non_run_arg = args.count;
 
+	for_array(i, args) {
+		if (args[i] == "--") {
+			break;
+		}
+		if (args[i] == "-help" || args[i] == "--help") {
+			build_context.show_help = true;
+			return print_show_help(args[0], command);
+		}
+	}
+
 	bool run_output = false;
 	if (command == "run" || command == "test") {
 		if (args.count < 3) {
@@ -3332,6 +3385,10 @@ int main(int arg_count, char const **arg_ptr) {
 		return 1;
 		#endif
 	} else if (command == "version") {
+		if (args.count != 2) {
+			usage(args[0]);
+			return 1;
+		}
 		build_context.command_kind = Command_version;
 		gb_printf("%.*s version %.*s", LIT(args[0]), LIT(ODIN_VERSION));
 
@@ -3346,6 +3403,10 @@ int main(int arg_count, char const **arg_ptr) {
 		gb_printf("\n");
 		return 0;
 	} else if (command == "report") {
+		if (args.count != 2) {
+			usage(args[0]);
+			return 1;
+		}
 		build_context.command_kind = Command_bug_report;
 		print_bug_report_help();
 		return 0;
@@ -3354,8 +3415,7 @@ int main(int arg_count, char const **arg_ptr) {
 			usage(args[0]);
 			return 1;
 		} else {
-			print_show_help(args[0], args[1], args[2]);
-			return 0;
+			return print_show_help(args[0], args[1], args[2]);
 		}
 	} else if (command == "bundle") {
 		if (args.count < 4) {
@@ -3371,6 +3431,10 @@ int main(int arg_count, char const **arg_ptr) {
 		}
 		init_filename = args[3];
 	} else if (command == "root") {
+		if (args.count != 2) {
+			usage(args[0]);
+			return 1;
+		}
 		gb_printf("%.*s", LIT(odin_root_dir()));
 		return 0;
 	} else if (command == "clear-cache") {
@@ -3386,11 +3450,6 @@ int main(int arg_count, char const **arg_ptr) {
 
 	init_filename = copy_string(permanent_allocator(), init_filename);
 
-	if (init_filename == "-help" ||
-	    init_filename == "--help") {
-		build_context.show_help = true;
-	}
-
 	if (init_filename.len > 0 && !build_context.show_help) {
 		// The command must be build, run, test, check, or another that takes a directory or filename.
 		if (!path_is_directory(init_filename)) {
@@ -3441,8 +3500,7 @@ int main(int arg_count, char const **arg_ptr) {
 	}
 
 	if (build_context.show_help) {
-		print_show_help(args[0], command);
-		return 0;
+		return print_show_help(args[0], command);
 	}
 
 	if (command == "bundle") {