[studio] Add change-format subcommand
Build / build (push) Successful in 1m13s

This commit is contained in:
2026-05-16 16:11:21 -05:00
parent d32b1033c3
commit 899eaa9dce
8 changed files with 219 additions and 4 deletions
@@ -47,3 +47,5 @@ install(
LIBRARY DESTINATION lib LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib ARCHIVE DESTINATION lib
) )
add_subdirectory(subcommands)
+49 -3
View File
@@ -14,24 +14,68 @@
#include <studio/context.hpp> #include <studio/context.hpp>
#include <studioapp/studioapp.hpp> #include <studioapp/studioapp.hpp>
#include "subcommands/change-format/change-format.hpp"
#include "configfile.hpp"
#include "studioui.hpp" #include "studioui.hpp"
namespace studio { namespace studio {
static ox::Error convertStudioConfigV1ToStudioConfigV2(
keel::Context&,
StudioConfigV1 &src,
StudioConfigV2 &dst) noexcept {
dst.projects.emplace_back(StudioConfigV2::ProjectConfig{
.projectPath = std::move(src.projectPath),
.activeTabItemName = std::move(src.activeTabItemName),
.openFiles = std::move(src.openFiles),
});
dst.showProjectExplorer = src.showProjectExplorer;
return {};
}
static struct: Module {
ox::String id() const noexcept final {
return ox::String{"net.drinkingtea.studio"};
}
ox::Vector<Command> commands() const final {
return {
{
"change-format",
cmdChangeFormat,
}
};
}
} constexpr mod;
static struct: keel::Module {
ox::String id() const noexcept override {
return ox::String{"net.drinkingtea.studio"};
}
ox::Vector<keel::Converter> converters() const noexcept override {
return {
keel::Converter::make<convertStudioConfigV1ToStudioConfigV2>(),
};
}
} constexpr kmod;
class StudioUIDrawer: public turbine::gl::Drawer { class StudioUIDrawer: public turbine::gl::Drawer {
private: private:
StudioUI &m_ui; StudioUI &m_ui;
public: public:
explicit StudioUIDrawer(StudioUI &ui) noexcept: m_ui(ui) { explicit StudioUIDrawer(StudioUI &ui) noexcept: m_ui(ui) {
} }
protected:
void draw(turbine::Context&) noexcept final { void draw(turbine::Context&) noexcept final {
m_ui.draw(); m_ui.draw();
} }
}; };
static void keyEventHandler(turbine::Context &ctx, turbine::Key key, bool down) noexcept { static void keyEventHandler(turbine::Context &ctx, turbine::Key const key, bool const down) noexcept {
auto const sctx = turbine::applicationData<studio::Context>(ctx); auto const sctx = turbine::applicationData<Context>(ctx);
sctx->ui.handleKeyEvent(key, down); sctx->ui.handleKeyEvent(key, down);
} }
@@ -65,6 +109,8 @@ static ox::Error run(
ox::StringViewCR appName, ox::StringViewCR appName,
ox::StringViewCR projectDataDir, ox::StringViewCR projectDataDir,
ox::SpanView<ox::CString> const &args) { ox::SpanView<ox::CString> const &args) {
keel::registerModule(kmod);
registerModule(&mod);
// seed UUID generator // seed UUID generator
auto const time = std::time(nullptr); auto const time = std::time(nullptr);
ox::UUID::seedGenerator({ ox::UUID::seedGenerator({
@@ -0,0 +1,68 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#pragma once
#include <ox/std/string.hpp>
#include <ox/std/vector.hpp>
#include <ox/model/def.hpp>
namespace studio {
struct StudioConfigV1 {
static constexpr auto TypeName = "net.drinkingtea.studio.StudioConfig";
static constexpr auto TypeVersion = 1;
ox::String projectPath;
ox::String activeTabItemName;
ox::Vector<ox::String> openFiles;
bool showProjectExplorer = true;
};
OX_MODEL_BEGIN(StudioConfigV1)
OX_MODEL_FIELD_RENAME(activeTabItemName, active_tab_item_name)
OX_MODEL_FIELD_RENAME(projectPath, project_path)
OX_MODEL_FIELD_RENAME(openFiles, open_files)
OX_MODEL_FIELD_RENAME(showProjectExplorer, show_project_explorer)
OX_MODEL_END()
struct StudioConfigV2 {
static constexpr auto TypeName = "net.drinkingtea.studio.StudioConfig";
static constexpr auto TypeVersion = 2;
struct ProjectConfig {
static constexpr auto TypeName = "net.drinkingtea.studio.ProjectConfig";
static constexpr auto TypeVersion = 2;
ox::String projectPath;
ox::String activeTabItemName;
ox::Vector<ox::String> openFiles;
};
ox::Vector<ProjectConfig> projects;
bool showProjectExplorer = true;
[[nodiscard]]
constexpr ProjectConfig const *project() const {
return projects.empty() ? nullptr : &projects[0];
}
[[nodiscard]]
constexpr ProjectConfig *project() {
return projects.empty() ? nullptr : &projects[0];
}
};
OX_MODEL_BEGIN(StudioConfigV2::ProjectConfig)
OX_MODEL_FIELD_RENAME(activeTabItemName, active_tab_item_name)
OX_MODEL_FIELD_RENAME(projectPath, project_path)
OX_MODEL_FIELD_RENAME(openFiles, open_files)
OX_MODEL_END()
OX_MODEL_BEGIN(StudioConfigV2)
OX_MODEL_FIELD(projects)
OX_MODEL_FIELD_RENAME(showProjectExplorer, show_project_explorer)
OX_MODEL_END()
using StudioConfig = StudioConfigV2;
}
@@ -0,0 +1,10 @@
target_sources(
StudioAppLib PRIVATE
change-format/change-format.cpp
)
target_link_libraries(
StudioAppLib PUBLIC
OxClArgs
)
@@ -0,0 +1,66 @@
/*
* Copyright 2016 - 2026 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <ox/clargs/clargs.hpp>
#include <ox/std/trace.hpp>
#include <studio/project.hpp>
#include "change-format.hpp"
namespace studio {
static ox::Error convertFile(
ox::FileSystem &fs,
ox::TypeStore &ts,
ox::StringViewCR path,
ox::ClawFormat const fmt) noexcept {
ox::Buffer buff;
ox::ModelObject obj;
OX_RETURN_ERROR(fs.read(path).moveTo(buff).reoriginate(1, "unable to read file"));
OX_RETURN_ERROR(keel::readAsset(ts, buff).moveTo(obj).reoriginate(1, "unable to parse file"));
OX_RETURN_ERROR(ox::writeClaw(obj, fmt).moveTo(buff));
OX_RETURN_ERROR(fs.write(path, buff).reoriginate(1, "unable to write file"));
return {};
}
static void printUsage() noexcept {
oxErr(R"(usage: convert-file {mc,oc} [files...]\n)");
}
[[nodiscard]]
static constexpr ox::Result<ox::ClawFormat> getFmt(ox::StringViewCR fmtStr) noexcept {
if (fmtStr == "mc") {
return ox::ClawFormat::Metal;
} else if (fmtStr == "oc") {
return ox::ClawFormat::Metal;
}
return ox::Error(1, "invalid format");
}
ox::Error cmdChangeFormat(Project &project, ox::SpanView<ox::CString> const args) noexcept {
if (args.size() < 2) {
printUsage();
return ox::Error{1, "invalid input"};
}
auto const [fmt, fmtErr] = getFmt(args[0]);
if (fmtErr) {
printUsage();
}
auto &fs = project.romFs();
auto &ts = project.typeStore();
for (auto const &file : args + 1) {
auto const err = convertFile(fs, ts, file, fmt);
if (err) {
oxErrf("Failed to convert {}: {}\n", file, err.msg);
}
}
return {};
}
}
@@ -0,0 +1,11 @@
/*
* Copyright 2016 - 2026 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#pragma once
namespace studio {
ox::Error cmdChangeFormat(Project &project, ox::SpanView<ox::CString> args) noexcept;
}
@@ -122,6 +122,16 @@ class Project: public ox::SignalHandler {
ox::Error writeTypeStore() noexcept; ox::Error writeTypeStore() noexcept;
[[nodiscard]]
constexpr ox::TypeStore &typeStore() noexcept {
return m_typeStore;
}
[[nodiscard]]
constexpr ox::TypeStore const &typeStore() const noexcept {
return m_typeStore;
}
private: private:
void buildFileIndex() noexcept; void buildFileIndex() noexcept;
+3 -1
View File
@@ -14,13 +14,15 @@
namespace studio { namespace studio {
static_assert(fileExt("main.c").value == "c"); static_assert(fileExt("main.c").value == "c");
static_assert(fileExt("main.cc").value == "cc");
static_assert(fileExt("main.cpp").value == "cpp");
static_assert(fileExt("a.b.c").value == "c"); static_assert(fileExt("a.b.c").value == "c");
static_assert(parentDir("/a/b/c") == "/a/b"); static_assert(parentDir("/a/b/c") == "/a/b");
static_assert(parentDir("/a/b/c/") == "/a/b"); static_assert(parentDir("/a/b/c/") == "/a/b");
static void generateTypes(ox::TypeStore &ts) noexcept { static void generateTypes(ox::TypeStore &ts) noexcept {
for (auto const mod : keel::modules()) { for (auto const mod : keel::modules()) {
for (auto gen : mod->types()) { for (auto const gen : mod->types()) {
oxLogError(gen(ts)); oxLogError(gen(ts));
} }
} }