diff --git a/pdm.lock b/pdm.lock index 5eb1dc9..77a3f6f 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:1552949492ceb7e83bd450325139abaf32f11a376ccd18b765ac7d29d0d4a5a9" +content_hash = "sha256:de75a47a4ffd38f6346bdeff955ef030ed082f3c7eaf09ecc5a252bf821a4893" [[metadata.targets]] requires_python = ">=3.7" @@ -24,6 +24,20 @@ files = [ {file = "prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90"}, ] +[[package]] +name = "questionary" +version = "1.10.0" +requires_python = ">=3.6,<4.0" +summary = "Python library to build pretty command line user prompts ⭐️" +groups = ["default"] +dependencies = [ + "prompt-toolkit<4.0,>=2.0", +] +files = [ + {file = "questionary-1.10.0-py3-none-any.whl", hash = "sha256:fecfcc8cca110fda9d561cb83f1e97ecbb93c613ff857f655818839dac74ce90"}, + {file = "questionary-1.10.0.tar.gz", hash = "sha256:600d3aefecce26d48d97eee936fdb66e4bc27f934c3ab6dd1e292c4f43946d90"}, +] + [[package]] name = "wcwidth" version = "0.2.13" diff --git a/pyproject.toml b/pyproject.toml index 3ea5bb3..292eb62 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "innercore-mod-toolchain" -version = "0.1.0-dev11" +version = "0.1.0-dev12" description = "Inner Core Mod Toolchain for Horizon is a toolchain that allows you to efficiently develop and build modifications for mobile Minecraft: Bedrock Edition directly from your PC." authors = [ { name = "Horizon Team", email = "innercore2018@gmail.com" }, @@ -18,7 +18,7 @@ authors = [ maintainers = [ { name = "MaXFeeD", email = "maksfeed.mf@gmail.com" }, ] -dependencies = ["prompt-toolkit>=3.0.48"] +dependencies = ["prompt-toolkit>=3.0.48", "questionary>=1.10.0"] requires-python = ">=3.7" readme = "README.md" classifiers = [ diff --git a/src/icmtoolchain/component.py b/src/icmtoolchain/component.py index 3afbe49..3158074 100644 --- a/src/icmtoolchain/component.py +++ b/src/icmtoolchain/component.py @@ -210,6 +210,54 @@ def startup() -> None: pretty_print(f"* Setup procedure is completed, Inner Core Mod Toolchain has been installed to {get_script_directory()!r} directory. Execute `icmtoolchain --help` to obtain a list of available commands. You may need to restart your console to be able to access any commands.") +def startup_questionary() -> None: + from questionary import confirm as qconfirm + from questionary import form as qform + from questionary import text as qtext + + tsc = request_typescript(only_check=True) is not None + form = qform( + username = qtext( + "Who are you?", + default=get_username() or "...", + instruction="This username, or alias, will be used when creating a project. Author name identifies you on Inner Core Mods." + ), + nodejs = qconfirm( + "Do you plan to use Node.js for compilation? This will allow your code to be transpiled by TypeScript Compiler to use ESNext's features, but may increase reassembly time.", + default=tsc + ), + import_projects = qtext( + "Where should we look for projects?", + instruction="If you have used Inner Core Mod Toolchain earlier, you may choose where to search for projects. Either import an obsolete project or modification for Inner Core." + ) + ) + try: + answers = form.unsafe_ask() + except KeyboardInterrupt or EOFError: + pretty_print("* Preconfiguration was canceled, you can do it later, execute `icmtoolchain --help` for a list of commands.") + return None + + username = ensure_not_whitespace(answers["username"]) + if username: + # pretty_print_answer("Who are you?", username) + GLOBALS.TOOLCHAIN_CONFIG.set_value("template.author", username) + + typescript = answers["nodejs"] + # pretty_print_answer("Do you plan to use Node.js for compilation?", "Yes" if typescript else "No") + if typescript: + if GLOBALS.TOOLCHAIN_CONFIG.get_value("denyTypeScript"): + GLOBALS.TOOLCHAIN_CONFIG.remove_value("denyTypeScript") + GLOBALS.TOOLCHAIN_CONFIG.save() + request_typescript() + elif tsc: + GLOBALS.TOOLCHAIN_CONFIG.set_value("denyTypeScript", True) + GLOBALS.TOOLCHAIN_CONFIG.save() + + GLOBALS.TOOLCHAIN_CONFIG.save() + + pretty_print(f"* Setup procedure is completed, Inner Core Mod Toolchain has been installed to {get_script_directory()!r} directory. Execute `icmtoolchain --help` to obtain a list of available commands. You may need to restart your console to be able to access any commands.") + + def upgrade() -> int: pretty_print("Nothing to perform.") return 0 diff --git a/src/icmtoolchain/package.py b/src/icmtoolchain/package.py index 997aabe..e3a3bb2 100644 --- a/src/icmtoolchain/package.py +++ b/src/icmtoolchain/package.py @@ -45,16 +45,17 @@ def select_template() -> Optional[str]: ) def new_project(template: Optional[str] = "../toolchain-mod") -> Optional[int]: - if not template or not exists(GLOBALS.TOOLCHAIN_CONFIG.get_absolute_path(template)): - return new_project(template=select_template()) - template_make_path = GLOBALS.TOOLCHAIN_CONFIG.get_absolute_path(template + "/template.json") - try: - with open(template_make_path, encoding="utf-8") as template_make: - template_config = BaseConfig(json.loads(template_make.read())) - except BaseException as err: - if len(GLOBALS.PROJECT_MANAGER.templates) > 1: - return new_project(None) - abort(f"Malformed '{template}/template.json', nothing to do.", cause=err) + # if not template or not exists(GLOBALS.TOOLCHAIN_CONFIG.get_absolute_path(template)): + # return new_project(template=select_template()) + # template_make_path = GLOBALS.TOOLCHAIN_CONFIG.get_absolute_path(template + "/template.json") + # try: + # with open(template_make_path, encoding="utf-8") as template_make: + # template_config = BaseConfig(json.loads(template_make.read())) + # except BaseException as err: + # if len(GLOBALS.PROJECT_MANAGER.templates) > 1: + # return new_project(None) + # abort(f"Malformed '{template}/template.json', nothing to do.", cause=err) + template_config = BaseConfig() have_template = GLOBALS.TOOLCHAIN_CONFIG.get_value("template") is not None always_skip_description = GLOBALS.TOOLCHAIN_CONFIG.get_value("template.skipDescription", False) @@ -136,7 +137,7 @@ def new_project(template: Optional[str] = "../toolchain-mod") -> Optional[int]: ) contents.append(create_interactable) - if not have_template: + if not have_template or True: # XXX: TEST contents += [ Window(height=1), Interactable("You can override template by setting `template` property in your 'toolchain.json', it will be automatically apply when you create a new project. Properties remain same as `info` property in 'make.json'.", style="class:editable.hint") @@ -182,7 +183,7 @@ def new_project(template: Optional[str] = "../toolchain-mod") -> Optional[int]: pretty_print(f"Copying template {template!r} to {output_directory!r}") return GLOBALS.PROJECT_MANAGER.create_project( - template, + template or "XXX", output_directory, name_editable.get_value(fallback_allowed=True), author_editable.get_value(fallback_allowed=True), @@ -191,6 +192,81 @@ def new_project(template: Optional[str] = "../toolchain-mod") -> Optional[int]: client_side_selectable.is_checked(), ) +def new_project_questionary(template: Optional[str] = "../toolchain-mod") -> Optional[int]: + # if not template or not exists(GLOBALS.TOOLCHAIN_CONFIG.get_absolute_path(template)): + # return new_project(template=select_template()) + # template_make_path = GLOBALS.TOOLCHAIN_CONFIG.get_absolute_path(template + "/template.json") + # try: + # with open(template_make_path, encoding="utf-8") as template_make: + # template_config = BaseConfig(json.loads(template_make.read())) + # except BaseException as err: + # if len(GLOBALS.PROJECT_MANAGER.templates) > 1: + # return new_project(None) + # abort(f"Malformed '{template}/template.json', nothing to do.", cause=err) + template_config = BaseConfig() + + have_template = GLOBALS.TOOLCHAIN_CONFIG.get_value("template") is not None + always_skip_description = GLOBALS.TOOLCHAIN_CONFIG.get_value("template.skipDescription", False) + + from questionary import confirm as qconfirm + from questionary import form as qform + from questionary import text as qtext + if not always_skip_description: + form = qform( + name = qtext( + "Enter project name:", + default=GLOBALS.TOOLCHAIN_CONFIG.get_value("template.name", template_config.get_value("info.name", "")), + validate=lambda text: get_project_folder_by_name(GLOBALS.TOOLCHAIN_CONFIG.directory, text) is not None + ), + author = qtext( + "Enter author username:", + default=GLOBALS.TOOLCHAIN_CONFIG.get_value("template.author", template_config.get_value("info.author", "")) + ), + version = qtext( + "Enter project version:", + default=GLOBALS.TOOLCHAIN_CONFIG.get_value("template.version", template_config.get_value("info.version", "1.0")) + ), + description = qtext( + "Enter project description:", + default=GLOBALS.TOOLCHAIN_CONFIG.get_value("template.description", template_config.get_value("info.description", "")) + ), + client_side = qconfirm( + "Is that project client side only?", + default=GLOBALS.TOOLCHAIN_CONFIG.get_value("template.clientOnly", template_config.get_value("info.clientOnly", False)) + ) + ) + else: + form = qform( + name = qtext( + "Enter project name:", + default=GLOBALS.TOOLCHAIN_CONFIG.get_value("template.name", template_config.get_value("info.name")), + validate=lambda text: get_project_folder_by_name(GLOBALS.TOOLCHAIN_CONFIG.directory, text) is not None + ) + ) + try: + answers = form.unsafe_ask() + except KeyboardInterrupt or EOFError: + pretty_print("Abort.") + return None + + if not have_template or True: # XXX: TEST + pretty_print("You can override template by setting `template` property in your 'toolchain.json', it will be automatically apply when you create a new project. Properties remain same as `info` property in 'make.json'.", style="class:editable.hint") + + output_directory = answers["name"] + if not output_directory: + abort("Not found 'directory' property in observer!") + pretty_print(f"Copying template {template!r} to {output_directory!r}") + + return GLOBALS.PROJECT_MANAGER.create_project( + template or "XXX", + output_directory, + answers["name"], + answers["author"], + answers["version"], + answers["description"], + answers["client_side"], + ) + def resolve_make_format_map(make_obj: Dict[Any, Any], path: str) -> Dict[Any, Any]: make_obj_info = make_obj["info"] if "info" in make_obj else dict() identifier = name_to_identifier(basename(path)) diff --git a/src/icmtoolchain/task.py b/src/icmtoolchain/task.py index ff96bb8..c797d45 100644 --- a/src/icmtoolchain/task.py +++ b/src/icmtoolchain/task.py @@ -564,3 +564,29 @@ def task_cleanup() -> int: return 0 cleanup_relative_directory("build") return 0 + +### TESTS + +@task("testSetup1") +def task_setup_1() -> int: + from .component import startup + startup() + return 0 + +@task("testSetup2") +def task_setup_2() -> int: + from .component import startup_questionary + startup_questionary() + return 0 + +@task("testCreate1") +def task_create_1() -> int: + from .package import new_project + new_project(GLOBALS.PREFERRED_CONFIG.get_value("defaultTemplate", "../toolchain-mod")) + return 0 + +@task("testCreate2") +def task_create_2() -> int: + from .package import new_project_questionary + new_project_questionary(GLOBALS.PREFERRED_CONFIG.get_value("defaultTemplate", "../toolchain-mod")) + return 0