2024-06-03 00:14:52 -04:00
|
|
|
#include "repl_wrapper.h"
|
2021-03-07 23:41:21 -05:00
|
|
|
|
|
|
|
#include "common/util/FileUtil.h"
|
2023-01-07 11:24:02 -05:00
|
|
|
#include "common/util/json_util.h"
|
|
|
|
#include "common/util/string_util.h"
|
2023-04-22 14:13:57 -04:00
|
|
|
#include "common/versions/versions.h"
|
2022-06-22 23:37:46 -04:00
|
|
|
|
2024-03-05 22:11:52 -05:00
|
|
|
#include "fmt/color.h"
|
|
|
|
#include "fmt/core.h"
|
2022-06-22 23:37:46 -04:00
|
|
|
#include "third-party/replxx/include/replxx.hxx"
|
2021-03-07 23:41:21 -05:00
|
|
|
|
2023-01-07 11:24:02 -05:00
|
|
|
namespace REPL {
|
|
|
|
void Wrapper::clear_screen() {
|
2021-03-07 23:41:21 -05:00
|
|
|
repl.clear_screen();
|
|
|
|
}
|
|
|
|
|
2024-06-03 00:14:52 -04:00
|
|
|
void Wrapper::print_welcome_message(const std::vector<std::string>& loaded_projects) {
|
|
|
|
std::string message;
|
|
|
|
message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " ..:::::..\n");
|
|
|
|
message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " .:-----------:.\n");
|
|
|
|
message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " .-----.");
|
|
|
|
message += fmt::format(fmt::emphasis::bold, " Welcome to OpenGOAL {}.{} [{}]",
|
|
|
|
versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR,
|
|
|
|
fmt::format(fg(fmt::color::gray), "{}", build_revision()));
|
|
|
|
if (!username.empty() && username != "#f" && username != "unknown") {
|
|
|
|
message += fmt::format(fg(fmt::color::light_green), " {}", username);
|
|
|
|
}
|
|
|
|
message += "!\n";
|
|
|
|
message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " .---.");
|
|
|
|
if (repl_config.game_version == GameVersion::Jak1) {
|
|
|
|
message += fmt::format(" [{}]: ", fmt::format(fg(fmt::color::orange), "jak1"));
|
|
|
|
} else if (repl_config.game_version == GameVersion::Jak2) {
|
|
|
|
message += fmt::format(" [{}]: ", fmt::format(fg(fmt::color::purple), "jak2"));
|
|
|
|
} else if (repl_config.game_version == GameVersion::Jak3) {
|
|
|
|
message += fmt::format(" [{}]: ", fmt::format(fg(fmt::color::gold), "jak3"));
|
|
|
|
} else {
|
|
|
|
message += fmt::format(" [{}]: ", fmt::format(fg(fmt::color::magenta), "jakx"));
|
|
|
|
}
|
|
|
|
const auto loaded_projects_str = fmt::format("{}", fmt::join(loaded_projects, ","));
|
|
|
|
message += fmt::format(fg(fmt::color::gray), "{}\n", loaded_projects_str);
|
|
|
|
message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " . --- .");
|
|
|
|
message +=
|
|
|
|
fmt::format(" Project Path: {}\n",
|
|
|
|
fmt::format(fg(fmt::color::gray), file_util::get_jak_project_dir().string()));
|
|
|
|
message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " - :===: -");
|
Parameterize the iso_data folder for goalc (#3692)
I hope this is everything I needed, and nothing I didn't.
## What's Changed
This update adds a command-line parameter to goalc, `--iso-path`.
Providing a path to a directory like
`D:\Files\Repositories\ArchipelaGOAL\iso_data\jak1` will inform the
compiler to use that directory instead.
## Why is this useful?
When combined with `--proj-path`, the compiler can be pointed to a
completely different project folder, given the `(mi)` command, and
immediately begin building from that directory, with everything it
needs. This eliminates the need to copy `iso_data` to multiple `data`
directories.
If a subsequent change to the Launcher is made, each mod could be passed
an --iso-path pointing to a single shared folder, allowing mods to each
run their own REPL _without_ requiring a copy of `iso_data` in a
subfolder.
## Independent testing required!
My local repositories are a little suspect, with a mod, a fork of
mod-base, and a fork of jak-project, all on the same drive. My
decompiler_out and iso_data folders are in the mod repo, not mod-base
nor jak-project. So what I did was make the change in the mod-base fork,
point `--proj-path and --iso-path` to the mod folders, and then ran
`(mi)`. The output showed a build starting with no errors.
Then I had to create this PR, which my fork of mod-base is unable to do,
so I created a patch file, forked jak-project, then applied the patch
there.
All this is to say that it would be preferable if someone could apply
this code to their own installation and see if it works. Even I wouldn't
take my own word for this.
---------
Co-authored-by: Tyler Wilding <xtvaser@gmail.com>
2024-10-18 00:03:14 -04:00
|
|
|
std::string effective_iso_path;
|
|
|
|
if (repl_config.iso_path.empty()) {
|
|
|
|
effective_iso_path = file_util::get_file_path({"iso_data"});
|
|
|
|
} else {
|
|
|
|
effective_iso_path = repl_config.iso_path;
|
|
|
|
}
|
|
|
|
message +=
|
|
|
|
fmt::format(" ISO Data Path: {}\n", fmt::format(fg(fmt::color::gray), effective_iso_path));
|
|
|
|
message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " --. .--: :--. .--");
|
2024-06-03 00:14:52 -04:00
|
|
|
message += " nREPL:";
|
|
|
|
if (!nrepl_alive) {
|
|
|
|
message += fmt::format(fg(fmt::color::red), "DISABLED\n");
|
|
|
|
} else {
|
|
|
|
message += fmt::format(fg(fmt::color::light_green), " Listening on {}\n",
|
|
|
|
repl_config.get_nrepl_port());
|
|
|
|
}
|
Parameterize the iso_data folder for goalc (#3692)
I hope this is everything I needed, and nothing I didn't.
## What's Changed
This update adds a command-line parameter to goalc, `--iso-path`.
Providing a path to a directory like
`D:\Files\Repositories\ArchipelaGOAL\iso_data\jak1` will inform the
compiler to use that directory instead.
## Why is this useful?
When combined with `--proj-path`, the compiler can be pointed to a
completely different project folder, given the `(mi)` command, and
immediately begin building from that directory, with everything it
needs. This eliminates the need to copy `iso_data` to multiple `data`
directories.
If a subsequent change to the Launcher is made, each mod could be passed
an --iso-path pointing to a single shared folder, allowing mods to each
run their own REPL _without_ requiring a copy of `iso_data` in a
subfolder.
## Independent testing required!
My local repositories are a little suspect, with a mod, a fork of
mod-base, and a fork of jak-project, all on the same drive. My
decompiler_out and iso_data folders are in the mod repo, not mod-base
nor jak-project. So what I did was make the change in the mod-base fork,
point `--proj-path and --iso-path` to the mod folders, and then ran
`(mi)`. The output showed a build starting with no errors.
Then I had to create this PR, which my fork of mod-base is unable to do,
so I created a patch file, forked jak-project, then applied the patch
there.
All this is to say that it would be preferable if someone could apply
this code to their own installation and see if it works. Even I wouldn't
take my own word for this.
---------
Co-authored-by: Tyler Wilding <xtvaser@gmail.com>
2024-10-18 00:03:14 -04:00
|
|
|
message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " .=======. =======.");
|
2024-06-03 00:14:52 -04:00
|
|
|
message += " Source File Search Dirs: ";
|
|
|
|
const auto search_dir_string =
|
|
|
|
fmt::format("{}", fmt::join(repl_config.asm_file_search_dirs, ","));
|
|
|
|
message += fmt::format("[{}]\n", fmt::format(fg(fmt::color::gray), search_dir_string));
|
Parameterize the iso_data folder for goalc (#3692)
I hope this is everything I needed, and nothing I didn't.
## What's Changed
This update adds a command-line parameter to goalc, `--iso-path`.
Providing a path to a directory like
`D:\Files\Repositories\ArchipelaGOAL\iso_data\jak1` will inform the
compiler to use that directory instead.
## Why is this useful?
When combined with `--proj-path`, the compiler can be pointed to a
completely different project folder, given the `(mi)` command, and
immediately begin building from that directory, with everything it
needs. This eliminates the need to copy `iso_data` to multiple `data`
directories.
If a subsequent change to the Launcher is made, each mod could be passed
an --iso-path pointing to a single shared folder, allowing mods to each
run their own REPL _without_ requiring a copy of `iso_data` in a
subfolder.
## Independent testing required!
My local repositories are a little suspect, with a mod, a fork of
mod-base, and a fork of jak-project, all on the same drive. My
decompiler_out and iso_data folders are in the mod repo, not mod-base
nor jak-project. So what I did was make the change in the mod-base fork,
point `--proj-path and --iso-path` to the mod folders, and then ran
`(mi)`. The output showed a build starting with no errors.
Then I had to create this PR, which my fork of mod-base is unable to do,
so I created a patch file, forked jak-project, then applied the patch
there.
All this is to say that it would be preferable if someone could apply
this code to their own installation and see if it works. Even I wouldn't
take my own word for this.
---------
Co-authored-by: Tyler Wilding <xtvaser@gmail.com>
2024-10-18 00:03:14 -04:00
|
|
|
message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " .-=====-. .-=====-");
|
|
|
|
message += fmt::format(" {} or {} for basic help and usage\n",
|
2024-06-03 00:14:52 -04:00
|
|
|
fmt::format(fg(fmt::color::cyan), "(repl-help)"),
|
|
|
|
fmt::format(fg(fmt::color::cyan), "(repl-keybinds)"));
|
|
|
|
message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " .-===========-.");
|
Parameterize the iso_data folder for goalc (#3692)
I hope this is everything I needed, and nothing I didn't.
## What's Changed
This update adds a command-line parameter to goalc, `--iso-path`.
Providing a path to a directory like
`D:\Files\Repositories\ArchipelaGOAL\iso_data\jak1` will inform the
compiler to use that directory instead.
## Why is this useful?
When combined with `--proj-path`, the compiler can be pointed to a
completely different project folder, given the `(mi)` command, and
immediately begin building from that directory, with everything it
needs. This eliminates the need to copy `iso_data` to multiple `data`
directories.
If a subsequent change to the Launcher is made, each mod could be passed
an --iso-path pointing to a single shared folder, allowing mods to each
run their own REPL _without_ requiring a copy of `iso_data` in a
subfolder.
## Independent testing required!
My local repositories are a little suspect, with a mod, a fork of
mod-base, and a fork of jak-project, all on the same drive. My
decompiler_out and iso_data folders are in the mod repo, not mod-base
nor jak-project. So what I did was make the change in the mod-base fork,
point `--proj-path and --iso-path` to the mod folders, and then ran
`(mi)`. The output showed a build starting with no errors.
Then I had to create this PR, which my fork of mod-base is unable to do,
so I created a patch file, forked jak-project, then applied the patch
there.
All this is to say that it would be preferable if someone could apply
this code to their own installation and see if it works. Even I wouldn't
take my own word for this.
---------
Co-authored-by: Tyler Wilding <xtvaser@gmail.com>
2024-10-18 00:03:14 -04:00
|
|
|
message +=
|
|
|
|
fmt::format(" {} to connect to the game\n", fmt::format(fg(fmt::color::cyan), "(lt)"));
|
|
|
|
message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " .-===-.");
|
|
|
|
message += fmt::format(" {} to recompile the active project.\n",
|
2024-06-03 00:14:52 -04:00
|
|
|
fmt::format(fg(fmt::color::cyan), "(mi)"));
|
|
|
|
message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " .\n");
|
|
|
|
fmt::print("{}", message);
|
2021-03-07 23:41:21 -05:00
|
|
|
}
|
|
|
|
|
2023-01-07 11:24:02 -05:00
|
|
|
void Wrapper::print_to_repl(const std::string& str) {
|
2022-05-06 18:19:37 -04:00
|
|
|
repl.print(str.data());
|
|
|
|
}
|
|
|
|
|
2023-01-07 11:24:02 -05:00
|
|
|
void Wrapper::set_history_max_size(size_t len) {
|
2021-03-07 23:41:21 -05:00
|
|
|
repl.set_max_history_size(len);
|
|
|
|
}
|
|
|
|
|
2023-01-07 11:24:02 -05:00
|
|
|
const char* Wrapper::readline(const std::string& prompt) {
|
2021-03-07 23:41:21 -05:00
|
|
|
return repl.input(prompt);
|
|
|
|
}
|
|
|
|
|
2023-01-07 11:24:02 -05:00
|
|
|
void Wrapper::add_to_history(const std::string& line) {
|
2021-03-07 23:41:21 -05:00
|
|
|
repl.history_add(line);
|
|
|
|
}
|
|
|
|
|
2023-01-07 11:24:02 -05:00
|
|
|
void Wrapper::save_history() {
|
2023-07-05 14:15:46 -04:00
|
|
|
fs::path path;
|
|
|
|
if (repl_config.per_game_history) {
|
|
|
|
path = file_util::get_user_config_dir() / game_version_names[repl_config.game_version] /
|
|
|
|
".opengoal.repl.history";
|
|
|
|
} else {
|
|
|
|
path = file_util::get_user_config_dir() / ".opengoal.repl.history";
|
|
|
|
}
|
2022-07-02 15:32:52 -04:00
|
|
|
file_util::create_dir_if_needed_for_file(path.string());
|
|
|
|
repl.history_save(path.string());
|
2021-03-07 23:41:21 -05:00
|
|
|
}
|
|
|
|
|
2023-01-07 11:24:02 -05:00
|
|
|
void Wrapper::load_history() {
|
2023-07-05 14:15:46 -04:00
|
|
|
fs::path path;
|
|
|
|
if (repl_config.per_game_history) {
|
|
|
|
path = file_util::get_user_config_dir() / game_version_names[repl_config.game_version] /
|
|
|
|
".opengoal.repl.history";
|
|
|
|
} else {
|
|
|
|
path = file_util::get_user_config_dir() / ".opengoal.repl.history";
|
|
|
|
}
|
2022-07-05 20:38:13 -04:00
|
|
|
if (fs::exists(path)) {
|
2022-06-30 18:05:26 -04:00
|
|
|
repl.history_load(path.string());
|
|
|
|
} else {
|
2022-07-02 15:32:52 -04:00
|
|
|
fmt::print("Couldn't locate REPL history file at '{}'\n", path.string());
|
2021-03-07 23:41:21 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-07 11:24:02 -05:00
|
|
|
std::pair<std::string, bool> Wrapper::get_current_repl_token(std::string const& context) {
|
2021-03-07 23:41:21 -05:00
|
|
|
// Find the current token
|
|
|
|
std::string token = "";
|
|
|
|
for (auto c = context.crbegin(); c != context.crend(); c++) {
|
|
|
|
if (std::isspace(*c)) {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
token = *c + token;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there is a preceeding '(' remove it
|
|
|
|
if (!token.empty() && token.at(0) == '(') {
|
|
|
|
token.erase(0, 1);
|
|
|
|
return {token, true};
|
|
|
|
}
|
|
|
|
return {token, false};
|
|
|
|
}
|
|
|
|
|
2023-01-07 11:24:02 -05:00
|
|
|
void Wrapper::print_help_message() {
|
2021-03-07 23:41:21 -05:00
|
|
|
fmt::print(fmt::emphasis::bold, "\nREPL Controls:\n");
|
|
|
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::cyan), "(:clear)\n");
|
|
|
|
fmt::print(" - Clear the current screen\n");
|
|
|
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::cyan), "(e)\n");
|
2022-06-30 21:11:58 -04:00
|
|
|
fmt::print(" - Exit the compiler\n");
|
2021-03-07 23:41:21 -05:00
|
|
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::cyan), "(lt [ip-address] [port-number])\n");
|
|
|
|
fmt::print(
|
|
|
|
" - Connect the listener to a running target. The IP address defaults to `127.0.0.1` and the "
|
|
|
|
"port to `8112`\n");
|
|
|
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::cyan), "(r [ip-address] [port-number])\n");
|
|
|
|
fmt::print(
|
|
|
|
" - Attempt to reset the target and reconnect. After this, the target will have nothing "
|
|
|
|
"loaded.\n");
|
|
|
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::cyan), "(:status)\n");
|
|
|
|
fmt::print(" - Send a ping-like message to the target. Requires the target to be connected\n");
|
|
|
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::cyan), "(shutdown-target)\n");
|
|
|
|
fmt::print(" - If the target is connected, make it exit\n");
|
|
|
|
|
|
|
|
fmt::print(fmt::emphasis::bold, "\nCompiling & Building:\n");
|
2022-06-30 21:11:58 -04:00
|
|
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::lime_green), "(mi)\n");
|
|
|
|
fmt::print(" - Build entire game\n");
|
|
|
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::lime_green), "(mng)\n");
|
|
|
|
fmt::print(" - Build game engine\n");
|
2021-03-07 23:41:21 -05:00
|
|
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::lime_green), "(m \"filename\")\n");
|
|
|
|
fmt::print(" - Compile an OpenGOAL source file\n");
|
|
|
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::lime_green), "(ml \"filename\")\n");
|
2022-06-30 21:11:58 -04:00
|
|
|
fmt::print(" - Compile and Load (or reload) an OpenGOAL source file\n");
|
2021-03-07 23:41:21 -05:00
|
|
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::lime_green), "(build-kernel)\n");
|
2022-06-30 21:11:58 -04:00
|
|
|
fmt::print(" - Build the GOAL kernel\n");
|
|
|
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::lime_green), "(make \"file-name\")\n");
|
2021-03-07 23:41:21 -05:00
|
|
|
fmt::print(
|
2022-06-30 21:11:58 -04:00
|
|
|
" - Build a file and any out-of-date dependencies. This file must be a target in the make "
|
|
|
|
"system.\n");
|
2021-03-07 23:41:21 -05:00
|
|
|
|
|
|
|
fmt::print(fmt::emphasis::bold, "\nOther:\n");
|
|
|
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::magenta), "(gs)\n");
|
|
|
|
fmt::print(" - Enter a GOOS REPL\n");
|
|
|
|
}
|
|
|
|
|
2023-01-07 11:24:02 -05:00
|
|
|
void Wrapper::print_keybind_help() {
|
|
|
|
fmt::print(fmt::emphasis::bold, "\nREPL KeyBinds:\n");
|
|
|
|
for (const auto& bind : repl_config.keybinds) {
|
|
|
|
fmt::print("{}\n", fmt::styled(bind.string(), fmt::fg(fmt::color::cyan)));
|
|
|
|
fmt::print("{}\n", fmt::styled(bind.description, fmt::fg(fmt::color::gray)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
replxx::Replxx::key_press_handler_t Wrapper::commit_text_action(std::string text_to_commit) {
|
repl: Add a few new quality of life improvements (#2030)
- You can define a `startup.gc` in your user folder, each line will be
executed on startup (deprecates the usefulness of some cli flags)
- You can define a `repl-config.json` file to override REPL settings.
Long-term this is a better approach than a bunch of CLI flags as well
- Via this, you can override the amount of time the repl will attempt to
listen for the target
- At the same time, I think i may have found why on Windows it can
sometimes take forever to timeout when the game dies, will dig into this
later
- Added some keybinds for common operations, shown here
https://user-images.githubusercontent.com/13153231/202890278-1ff2bb06-dddf-4bde-9178-aa0883799167.mp4
> builds the game, connects to it, attaches a debugger and continues,
launches it, gets the backtrace, stops the target -- all with only
keybinds.
If you want these keybinds to work inside VSCode's integrated terminal,
you need to add the following to your settings file
```json
"terminal.integrated.commandsToSkipShell": [
"-workbench.action.quickOpen",
"-workbench.action.quickOpenView"
]
```
2022-11-20 14:28:41 -05:00
|
|
|
return [this, text_to_commit](char32_t code) {
|
|
|
|
repl.set_state(
|
|
|
|
replxx::Replxx::State(text_to_commit.c_str(), static_cast<int>(text_to_commit.size())));
|
2023-01-07 11:24:02 -05:00
|
|
|
return repl.invoke(replxx::Replxx::ACTION::COMMIT_LINE, code);
|
repl: Add a few new quality of life improvements (#2030)
- You can define a `startup.gc` in your user folder, each line will be
executed on startup (deprecates the usefulness of some cli flags)
- You can define a `repl-config.json` file to override REPL settings.
Long-term this is a better approach than a bunch of CLI flags as well
- Via this, you can override the amount of time the repl will attempt to
listen for the target
- At the same time, I think i may have found why on Windows it can
sometimes take forever to timeout when the game dies, will dig into this
later
- Added some keybinds for common operations, shown here
https://user-images.githubusercontent.com/13153231/202890278-1ff2bb06-dddf-4bde-9178-aa0883799167.mp4
> builds the game, connects to it, attaches a debugger and continues,
launches it, gets the backtrace, stops the target -- all with only
keybinds.
If you want these keybinds to work inside VSCode's integrated terminal,
you need to add the following to your settings file
```json
"terminal.integrated.commandsToSkipShell": [
"-workbench.action.quickOpen",
"-workbench.action.quickOpenView"
]
```
2022-11-20 14:28:41 -05:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-01-07 11:24:02 -05:00
|
|
|
void Wrapper::init_settings() {
|
repl: Add a few new quality of life improvements (#2030)
- You can define a `startup.gc` in your user folder, each line will be
executed on startup (deprecates the usefulness of some cli flags)
- You can define a `repl-config.json` file to override REPL settings.
Long-term this is a better approach than a bunch of CLI flags as well
- Via this, you can override the amount of time the repl will attempt to
listen for the target
- At the same time, I think i may have found why on Windows it can
sometimes take forever to timeout when the game dies, will dig into this
later
- Added some keybinds for common operations, shown here
https://user-images.githubusercontent.com/13153231/202890278-1ff2bb06-dddf-4bde-9178-aa0883799167.mp4
> builds the game, connects to it, attaches a debugger and continues,
launches it, gets the backtrace, stops the target -- all with only
keybinds.
If you want these keybinds to work inside VSCode's integrated terminal,
you need to add the following to your settings file
```json
"terminal.integrated.commandsToSkipShell": [
"-workbench.action.quickOpen",
"-workbench.action.quickOpenView"
]
```
2022-11-20 14:28:41 -05:00
|
|
|
// NOTE - a nice popular project that uses replxx
|
|
|
|
// - https://github.com/ClickHouse/ClickHouse/blob/master/base/base/ReplxxLineReader.cpp#L366
|
2021-03-07 23:41:21 -05:00
|
|
|
repl.set_word_break_characters(" \t");
|
2023-06-29 16:32:48 -04:00
|
|
|
repl.set_complete_on_empty(false);
|
|
|
|
repl.set_indent_multiline(false);
|
|
|
|
repl.enable_bracketed_paste();
|
repl: Add a few new quality of life improvements (#2030)
- You can define a `startup.gc` in your user folder, each line will be
executed on startup (deprecates the usefulness of some cli flags)
- You can define a `repl-config.json` file to override REPL settings.
Long-term this is a better approach than a bunch of CLI flags as well
- Via this, you can override the amount of time the repl will attempt to
listen for the target
- At the same time, I think i may have found why on Windows it can
sometimes take forever to timeout when the game dies, will dig into this
later
- Added some keybinds for common operations, shown here
https://user-images.githubusercontent.com/13153231/202890278-1ff2bb06-dddf-4bde-9178-aa0883799167.mp4
> builds the game, connects to it, attaches a debugger and continues,
launches it, gets the backtrace, stops the target -- all with only
keybinds.
If you want these keybinds to work inside VSCode's integrated terminal,
you need to add the following to your settings file
```json
"terminal.integrated.commandsToSkipShell": [
"-workbench.action.quickOpen",
"-workbench.action.quickOpenView"
]
```
2022-11-20 14:28:41 -05:00
|
|
|
// Setup default keybinds
|
2023-01-07 11:24:02 -05:00
|
|
|
for (const auto& bind : repl_config.keybinds) {
|
|
|
|
char32_t code;
|
|
|
|
switch (bind.modifier) {
|
|
|
|
case KeyBind::Modifier::CTRL:
|
|
|
|
code = replxx::Replxx::KEY::control(bind.key.at(0));
|
|
|
|
break;
|
|
|
|
case KeyBind::Modifier::SHIFT:
|
|
|
|
code = replxx::Replxx::KEY::shift(bind.key.at(0));
|
|
|
|
break;
|
|
|
|
case KeyBind::Modifier::META:
|
|
|
|
code = replxx::Replxx::KEY::meta(bind.key.at(0));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
repl.bind_key(code, commit_text_action(bind.command));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Wrapper::reload_startup_file() {
|
2023-01-30 20:45:03 -05:00
|
|
|
startup_file = load_user_startup_file(username, repl_config.game_version);
|
2023-01-07 11:24:02 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string find_repl_username() {
|
|
|
|
// Two options - either:
|
|
|
|
// 1. look for the `user.txt` file, which should only contain the username
|
|
|
|
// 2. if this is absent AND there is a single folder inside the "user" folder, use that as the
|
|
|
|
// username
|
|
|
|
auto user_dir = file_util::get_jak_project_dir() / "goal_src" / "user";
|
|
|
|
auto dirs = file_util::find_directories_in_dir(user_dir);
|
|
|
|
if (dirs.size() == 1) {
|
|
|
|
return dirs.at(0).filename().string();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::regex allowed_chars("(^[0-9a-zA-Z\\-\\.\\!\\?<>]*$)");
|
|
|
|
if (file_util::file_exists((user_dir / "user.txt").string())) {
|
|
|
|
auto text = file_util::read_text_file(user_dir / "user.txt");
|
|
|
|
text = str_util::trim(text);
|
|
|
|
if (!text.empty() && std::regex_match(text, allowed_chars)) {
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-30 23:28:30 -04:00
|
|
|
return "unknown";
|
2023-01-07 11:24:02 -05:00
|
|
|
}
|
|
|
|
|
2023-01-30 20:45:03 -05:00
|
|
|
fs::path get_startup_file_path(const std::string& username, const GameVersion game_version) {
|
|
|
|
// - first check to see if there is a game version specific startup file to prefer
|
|
|
|
auto game_specific_path = file_util::get_jak_project_dir() / "goal_src" / "user" / username /
|
|
|
|
fmt::format("startup-{}.gc", version_to_game_name(game_version));
|
|
|
|
if (file_util::file_exists(game_specific_path.string())) {
|
|
|
|
return game_specific_path;
|
|
|
|
}
|
|
|
|
return file_util::get_jak_project_dir() / "goal_src" / "user" / username / "startup.gc";
|
|
|
|
}
|
|
|
|
|
|
|
|
StartupFile load_user_startup_file(const std::string& username, const GameVersion game_version) {
|
2023-01-07 11:24:02 -05:00
|
|
|
// Check for a `startup.gc` file, each line will be executed on the REPL on startup
|
2023-01-30 20:45:03 -05:00
|
|
|
auto startup_file_path = get_startup_file_path(username, game_version);
|
2023-01-07 11:24:02 -05:00
|
|
|
StartupFile startup_file;
|
|
|
|
if (file_util::file_exists(startup_file_path.string())) {
|
|
|
|
auto data = file_util::read_text_file(startup_file_path);
|
|
|
|
auto startup_cmds = str_util::split(data);
|
|
|
|
bool found_run_on_listen_line = false;
|
|
|
|
for (const auto& cmd : startup_cmds) {
|
|
|
|
if (found_run_on_listen_line) {
|
|
|
|
startup_file.run_after_listen.push_back(cmd);
|
|
|
|
} else {
|
|
|
|
startup_file.run_before_listen.push_back(cmd);
|
|
|
|
}
|
|
|
|
if (str_util::contains(cmd, "og:run-below-on-listen")) {
|
|
|
|
found_run_on_listen_line = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return startup_file;
|
|
|
|
}
|
|
|
|
|
2024-06-03 00:14:52 -04:00
|
|
|
REPL::Config load_repl_config(const std::string& username,
|
|
|
|
const GameVersion game_version,
|
|
|
|
const int nrepl_port) {
|
2023-01-07 11:24:02 -05:00
|
|
|
auto repl_config_path =
|
|
|
|
file_util::get_jak_project_dir() / "goal_src" / "user" / username / "repl-config.json";
|
2024-06-03 00:14:52 -04:00
|
|
|
REPL::Config loaded_config(game_version);
|
2023-01-07 11:24:02 -05:00
|
|
|
if (file_util::file_exists(repl_config_path.string())) {
|
|
|
|
try {
|
|
|
|
auto repl_config_data =
|
|
|
|
parse_commented_json(file_util::read_text_file(repl_config_path), "repl-config.json");
|
2024-06-03 00:14:52 -04:00
|
|
|
from_json(repl_config_data, loaded_config);
|
|
|
|
loaded_config.temp_nrepl_port = nrepl_port;
|
|
|
|
return loaded_config;
|
2023-01-07 11:24:02 -05:00
|
|
|
} catch (std::exception& e) {
|
2024-06-03 00:14:52 -04:00
|
|
|
// do nothing
|
2023-01-07 11:24:02 -05:00
|
|
|
}
|
|
|
|
}
|
2024-06-03 00:14:52 -04:00
|
|
|
loaded_config.temp_nrepl_port = nrepl_port;
|
|
|
|
return loaded_config;
|
2021-03-07 23:41:21 -05:00
|
|
|
}
|
2023-01-07 11:24:02 -05:00
|
|
|
} // namespace REPL
|