Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cmd/ktor/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -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(".")
Expand Down
4 changes: 2 additions & 2 deletions internal/app/cli/command/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
18 changes: 12 additions & 6 deletions internal/app/cli/processing.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand All @@ -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 {
Expand All @@ -77,6 +81,7 @@ type Input struct {
CommandArgs []string
CommandOptions map[Flag]string
Verbose bool
VersionCatalog bool
}

func ProcessArgs(args *Args) (*Input, error) {
Expand Down Expand Up @@ -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 {
Expand Down
10 changes: 8 additions & 2 deletions internal/app/generate/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions internal/app/i18n/bundle.en.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand Down Expand Up @@ -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.",
}
2 changes: 2 additions & 0 deletions internal/app/i18n/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const (
SelectedPluginsCount
ProjectNameCaption
LocationCaption
VersionCatalogCaption
SearchPluginsCaption
CreateProjectButton
NoPluginsFound
Expand Down Expand Up @@ -117,4 +118,5 @@ const (
KtorGradlePluginNotFound
StartingCommandMsg
ErrorExecutingCommandMsg
VersionCatalogOptionDescr
)
1 change: 1 addition & 0 deletions internal/app/interactive/draw/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type Element int
const (
ProjectNameInput Element = iota
LocationInput
VersionCatalogCheckbox
SearchInput
Tabs
CreateButton
Expand Down
25 changes: 25 additions & 0 deletions internal/app/interactive/draw/tui.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
33 changes: 25 additions & 8 deletions internal/app/interactive/entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)))
Expand Down Expand Up @@ -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)
Expand All @@ -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:
Comment on lines 273 to +278
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Enter on the VersionCatalog checkbox doesn’t advance.

The early return means users can’t continue with Enter and must use Tab, which breaks the otherwise “Enter-to-advance” flow. If advancing is intended, drop the return to let the subsequent switch set PluginsShown and move focus.

🛠️ Suggested tweak
case draw.VersionCatalogCheckbox:
    mdl.VersionCatalog = !mdl.VersionCatalog
-   return
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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:
case key == tcell.KeyEnter && mod == tcell.ModNone:
switch drawState.ActiveElement {
case draw.VersionCatalogCheckbox:
mdl.VersionCatalog = !mdl.VersionCatalog
case draw.Tabs:
🤖 Prompt for AI Agents
In `@internal/app/interactive/entry.go` around lines 273 - 278, The Enter handler
currently toggles mdl.VersionCatalog and returns early inside the case for
draw.VersionCatalogCheckbox, preventing the later switch that sets PluginsShown
and advances focus; remove the early return in the Enter branch so that after
flipping mdl.VersionCatalog execution falls through to the subsequent switch
(the same Enter handler handling draw.Tabs) which will set PluginsShown and
update drawState.ActiveElement to advance focus.

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
}
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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 {
Expand Down
9 changes: 5 additions & 4 deletions internal/app/interactive/model/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down