diff --git a/cmd/ktor/main.go b/cmd/ktor/main.go index 11f623c..048c093 100644 --- a/cmd/ktor/main.go +++ b/cmd/ktor/main.go @@ -310,7 +310,7 @@ func main() { os.Exit(1) } - command.Generate(client, projectDir, projectName, []string{}, verboseLogger, hasGlobalLog, ctx) + command.Generate(client, projectDir, projectName, []string{}, args.VersionCatalog, verboseLogger, hasGlobalLog, ctx) return } @@ -324,7 +324,7 @@ func main() { return } - command.Generate(client, result.ProjectDir, result.ProjectName, result.Plugins, verboseLogger, hasGlobalLog, ctx) + command.Generate(client, result.ProjectDir, result.ProjectName, result.Plugins, result.VersionCatalog, verboseLogger, hasGlobalLog, ctx) case cli.OpenAPI: specPath := args.CommandArgs[0] projectDir, err := filepath.Abs(".") diff --git a/internal/app/cli/command/generate.go b/internal/app/cli/command/generate.go index 0d92bc8..f91aaf6 100644 --- a/internal/app/cli/command/generate.go +++ b/internal/app/cli/command/generate.go @@ -12,14 +12,14 @@ import ( "os" ) -func Generate(client *http.Client, projectDir, projectName string, plugins []string, verboseLogger *log.Logger, hasGlobalLog bool, ctx context.Context) { +func Generate(client *http.Client, projectDir, projectName string, plugins []string, versionCatalog bool, verboseLogger *log.Logger, hasGlobalLog bool, ctx context.Context) { homeDir, err := os.UserHomeDir() if err != nil { fmt.Fprintln(os.Stderr, i18n.Get(i18n.CannotDetermineHomeDir)) os.Exit(1) } - err = generate.Project(client, verboseLogger, projectDir, projectName, plugins, ctx) + err = generate.Project(client, verboseLogger, projectDir, projectName, plugins, versionCatalog, ctx) if err != nil { cli.ExitWithProjectError(err, projectDir, hasGlobalLog, homeDir) diff --git a/internal/app/cli/processing.go b/internal/app/cli/processing.go index eb511eb..ae4e7ad 100644 --- a/internal/app/cli/processing.go +++ b/internal/app/cli/processing.go @@ -42,11 +42,12 @@ type commandSpec struct { type Flag string const ( - Version Flag = "version" - Help = "help" - Verbose = "verbose" - OutDir = "outDir" - ProjectDir = "projectDir" + Version Flag = "version" + Help = "help" + Verbose = "verbose" + OutDir = "outDir" + ProjectDir = "projectDir" + VersionCatalog = "versionCatalog" ) var AllFlagsSpec = map[Flag]flagSpec{ @@ -64,6 +65,9 @@ var commandFlagSpec = map[Command]map[Flag]flagSpec{ DevCommand: { ProjectDir: {Aliases: []string{"-p", "--project"}, Description: i18n.Get(i18n.ProjectDirOptionDescr), hasArg: true}, }, + NewCommand: { + VersionCatalog: {Aliases: []string{"--version-catalog"}, Description: i18n.Get(i18n.VersionCatalogOptionDescr), hasArg: false}, + }, } type flagSpec struct { @@ -77,6 +81,7 @@ type Input struct { CommandArgs []string CommandOptions map[Flag]string Verbose bool + VersionCatalog bool } func ProcessArgs(args *Args) (*Input, error) { @@ -183,7 +188,8 @@ func ProcessArgs(args *Args) (*Input, error) { } } - return &Input{Command: Command(args.Command), CommandArgs: args.CommandArgs[argsIndex:], CommandOptions: commandOpts, Verbose: flags[Verbose]}, nil + _, versionCatalog := commandOpts[VersionCatalog] + return &Input{Command: Command(args.Command), CommandArgs: args.CommandArgs[argsIndex:], CommandOptions: commandOpts, Verbose: flags[Verbose], VersionCatalog: versionCatalog}, nil } func hasVararg(spec commandSpec) bool { diff --git a/internal/app/generate/project.go b/internal/app/generate/project.go index e463986..9beb2d0 100644 --- a/internal/app/generate/project.go +++ b/internal/app/generate/project.go @@ -18,7 +18,7 @@ import ( ) // Project Returns *app.Error on error -func Project(client *http.Client, logger *log.Logger, projectDir, project string, plugins []string, ctx context.Context) error { +func Project(client *http.Client, logger *log.Logger, projectDir, project string, plugins []string, versionCatalog bool, ctx context.Context) error { err := os.MkdirAll(projectDir, 0755) logger.Printf(i18n.Get(i18n.CreatingDir, projectDir)) @@ -46,6 +46,12 @@ func Project(client *http.Client, logger *log.Logger, projectDir, project string } logger.Println(i18n.Get(i18n.RequestGenServer)) + + versionCatalogValue := "false" + if versionCatalog { + versionCatalogValue = "true" + } + projectPayload := network.ProjectPayload{ Settings: network.ProjectSettings{ Name: project, @@ -55,7 +61,7 @@ func Project(client *http.Client, logger *log.Logger, projectDir, project string KtorVersion: settings.KtorVersion.DefaultId, KotlinVersion: settings.KotlinVersion.DefaultId, BuildSystemArgs: map[network.BuildSystemArgs]string{ - network.VersionCatalogBuildArg: "", + network.VersionCatalogBuildArg: versionCatalogValue, }, }, Plugins: plugins, diff --git a/internal/app/i18n/bundle.en.go b/internal/app/i18n/bundle.en.go index 1bb5aba..58896b7 100644 --- a/internal/app/i18n/bundle.en.go +++ b/internal/app/i18n/bundle.en.go @@ -52,6 +52,7 @@ var en = map[Message]string{ SelectedPluginsCount: "%d plugins added.", ProjectNameCaption: "Project name:", LocationCaption: "Location:", + VersionCatalogCaption: "Use Gradle version catalog:", SearchPluginsCaption: "Search for plugins:", CreateProjectButton: "CREATE PROJECT (CTRL+G)", NoPluginsFound: "No plugins found for the search query.", @@ -117,4 +118,5 @@ var en = map[Message]string{ KtorGradlePluginNotFound: "Ktor Gradle plugin of version %s or above cannot be found in the project %s.\n", StartingCommandMsg: "Starting %s command: JAVA_HOME=%s %s\n", ErrorExecutingCommandMsg: "Error while executing %s command: %s\n", + VersionCatalogOptionDescr: "Use Gradle version catalog for dependency management.", } diff --git a/internal/app/i18n/messages.go b/internal/app/i18n/messages.go index d234e00..d0f170d 100644 --- a/internal/app/i18n/messages.go +++ b/internal/app/i18n/messages.go @@ -54,6 +54,7 @@ const ( SelectedPluginsCount ProjectNameCaption LocationCaption + VersionCatalogCaption SearchPluginsCaption CreateProjectButton NoPluginsFound @@ -117,4 +118,5 @@ const ( KtorGradlePluginNotFound StartingCommandMsg ErrorExecutingCommandMsg + VersionCatalogOptionDescr ) diff --git a/internal/app/interactive/draw/state.go b/internal/app/interactive/draw/state.go index 8feb112..a3d9b97 100644 --- a/internal/app/interactive/draw/state.go +++ b/internal/app/interactive/draw/state.go @@ -28,6 +28,7 @@ type Element int const ( ProjectNameInput Element = iota LocationInput + VersionCatalogCheckbox SearchInput Tabs CreateButton diff --git a/internal/app/interactive/draw/tui.go b/internal/app/interactive/draw/tui.go index 54f05ba..b07e7e0 100644 --- a/internal/app/interactive/draw/tui.go +++ b/internal/app/interactive/draw/tui.go @@ -60,6 +60,31 @@ func Tui(scr tcell.Screen, st *State, mdl *model.State) { posY++ } + posY += 2 + posX = padding + + // Draw version catalog checkbox with text first + checkboxStyle := buttonStyle + textStyleForCheckbox := strongStyle + if st.ActiveElement == VersionCatalogCheckbox { + checkboxStyle = activeTabStyle + textStyleForCheckbox = activeTabStyle + } + + checkboxVal := ' ' + if mdl.VersionCatalog { + checkboxVal = 'x' + } + + // Draw label first, then [x] or [ ] checkbox + posX, posY = drawInlineText(scr, posX, posY, strongStyle, i18n.Get(i18n.VersionCatalogCaption)) + posX++ + scr.SetContent(posX, posY, '[', nil, textStyleForCheckbox) + posX++ + scr.SetContent(posX, posY, checkboxVal, nil, checkboxStyle) + posX++ + scr.SetContent(posX, posY, ']', nil, textStyleForCheckbox) + if !st.PluginsShown { return } diff --git a/internal/app/interactive/entry.go b/internal/app/interactive/entry.go index d6e06ac..6223069 100644 --- a/internal/app/interactive/entry.go +++ b/internal/app/interactive/entry.go @@ -3,14 +3,15 @@ package interactive import ( "context" "fmt" + "net/http" + "slices" + "strings" + "github.com/gdamore/tcell/v2" "github.com/ktorio/ktor-cli/internal/app/i18n" "github.com/ktorio/ktor-cli/internal/app/interactive/draw" "github.com/ktorio/ktor-cli/internal/app/interactive/model" "github.com/ktorio/ktor-cli/internal/app/network" - "net/http" - "slices" - "strings" ) func Run(client *http.Client, ctx context.Context) (result model.Result, err error) { @@ -71,14 +72,14 @@ func Run(client *http.Client, ctx context.Context) (result model.Result, err err processEvent(event, drawState, mdl, &result, &mdl.ProjectName) case draw.LocationInput: processEvent(event, drawState, mdl, &result, &mdl.ProjectDir) + case draw.VersionCatalogCheckbox: + processEvent(event, drawState, mdl, &result, nil) case draw.SearchInput: processEvent(event, drawState, mdl, &result, &mdl.Search) case draw.Tabs: processEvent(event, drawState, mdl, &result, nil) case draw.CreateButton: processEvent(event, drawState, mdl, &result, nil) - default: - panic("unhandled default case") } draw.HideCursorIfNeeded(drawState, scr) @@ -162,6 +163,11 @@ func processEvent(ev tcell.Event, drawState *draw.State, mdl *model.State, resul return } + if draw.IsElementActive(drawState, draw.VersionCatalogCheckbox) && ev.Rune() == ' ' { + mdl.VersionCatalog = !mdl.VersionCatalog + return + } + if draw.IsElementActive(drawState, draw.Tabs) && ev.Rune() == ' ' { toggleSelectedPlugin(drawState, mdl) mdl.StatusLine = fmt.Sprintf(i18n.Get(i18n.SelectedPluginsCount, len(mdl.AddedPlugins))) @@ -237,7 +243,9 @@ func processEvent(ev tcell.Event, drawState *draw.State, mdl *model.State, resul switch { case drawState.ActiveElement == draw.ProjectNameInput: draw.SwitchElement(drawState, 1) - case drawState.ActiveElement == draw.LocationInput && drawState.PluginsShown: + case drawState.ActiveElement == draw.LocationInput: + draw.SwitchElement(drawState, 1) + case drawState.ActiveElement == draw.VersionCatalogCheckbox && drawState.PluginsShown: draw.SwitchElement(drawState, 1) case drawState.PluginsShown: draw.SwitchElement(drawState, 1) @@ -263,11 +271,15 @@ func processEvent(ev tcell.Event, drawState *draw.State, mdl *model.State, resul onInputChanged(drawState, mdl, *input) } case key == tcell.KeyEnter && mod == tcell.ModNone: - if drawState.ActiveElement == draw.Tabs { + switch drawState.ActiveElement { + case draw.VersionCatalogCheckbox: + mdl.VersionCatalog = !mdl.VersionCatalog + return + case draw.Tabs: toggleSelectedPlugin(drawState, mdl) mdl.StatusLine = fmt.Sprintf(i18n.Get(i18n.SelectedPluginsCount, len(mdl.AddedPlugins))) return - } else if drawState.ActiveElement == draw.CreateButton { + case draw.CreateButton: if generateProject(result, mdl) { return } @@ -278,6 +290,8 @@ func processEvent(ev tcell.Event, drawState *draw.State, mdl *model.State, resul model.InitProjectDir(mdl) model.CheckProjectSettings(mdl) case draw.LocationInput: + // Just move to version catalog checkbox, don't show plugins yet + case draw.VersionCatalogCheckbox: drawState.PluginsShown = true default: // do nothing yet @@ -290,6 +304,8 @@ func processEvent(ev tcell.Event, drawState *draw.State, mdl *model.State, resul model.InitProjectDir(mdl) model.CheckProjectSettings(mdl) case draw.LocationInput: + // Just move to version catalog checkbox, don't show plugins yet + case draw.VersionCatalogCheckbox: drawState.PluginsShown = true default: // do nothing yet @@ -305,6 +321,7 @@ func processEvent(ev tcell.Event, drawState *draw.State, mdl *model.State, resul func generateProject(result *model.Result, mdl *model.State) bool { result.ProjectName = mdl.ProjectName result.ProjectDir = mdl.GetProjectPath() + result.VersionCatalog = mdl.VersionCatalog result.Plugins = []string{} for id := range mdl.AddedPlugins { diff --git a/internal/app/interactive/model/state.go b/internal/app/interactive/model/state.go index a778f98..69fe6da 100644 --- a/internal/app/interactive/model/state.go +++ b/internal/app/interactive/model/state.go @@ -33,10 +33,11 @@ type State struct { } type Result struct { - ProjectName string - ProjectDir string - Plugins []string - Quit bool + ProjectName string + ProjectDir string + Plugins []string + VersionCatalog bool + Quit bool } func NewState() *State {