Skip to content

Commit 66760c5

Browse files
akoclaude
andcommitted
feat: add SHOW FEATURES command with version registry queries
Implements SHOW FEATURES grammar, AST, visitor, and executor: - SHOW FEATURES [IN area] — list features for connected project version - SHOW FEATURES FOR VERSION x.y — query any version without a project - SHOW FEATURES ADDED SINCE x.y — show what's new since a given version Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 965d186 commit 66760c5

File tree

13 files changed

+5599
-5202
lines changed

13 files changed

+5599
-5202
lines changed

cmd/mxcli/lsp_completions_gen.go

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mdl/ast/ast_query.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,23 @@ func (t ShowObjectType) String() string {
207207
}
208208
}
209209

210+
// ShowFeaturesStmt represents SHOW FEATURES commands against the version registry.
211+
type ShowFeaturesStmt struct {
212+
// InArea filters by feature area (e.g., "domain_model", "microflows").
213+
// Empty means show all areas.
214+
InArea string
215+
216+
// ForVersion queries features for a specific version (SHOW FEATURES FOR VERSION x.y).
217+
// Empty means use the connected project's version.
218+
ForVersion string
219+
220+
// AddedSince shows features added after this version (SHOW FEATURES ADDED SINCE x.y).
221+
// Empty means show all features (not just new ones).
222+
AddedSince string
223+
}
224+
225+
func (s *ShowFeaturesStmt) isStatement() {}
226+
210227
// SelectStmt represents a SELECT query against catalog tables.
211228
type SelectStmt struct {
212229
Query string // The raw SQL query

mdl/executor/cmd_features.go

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
package executor
4+
5+
import (
6+
"fmt"
7+
"strings"
8+
9+
"github.com/mendixlabs/mxcli/mdl/ast"
10+
"github.com/mendixlabs/mxcli/sdk/versions"
11+
)
12+
13+
// execShowFeatures handles SHOW FEATURES, SHOW FEATURES FOR VERSION, and
14+
// SHOW FEATURES ADDED SINCE commands.
15+
func (e *Executor) execShowFeatures(s *ast.ShowFeaturesStmt) error {
16+
reg, err := versions.Load()
17+
if err != nil {
18+
return fmt.Errorf("failed to load version registry: %w", err)
19+
}
20+
21+
// Determine the project version to use.
22+
var pv versions.SemVer
23+
24+
switch {
25+
case s.AddedSince != "":
26+
// SHOW FEATURES ADDED SINCE x.y
27+
sinceV, err := versions.ParseSemVer(s.AddedSince)
28+
if err != nil {
29+
return fmt.Errorf("invalid version %q: %w", s.AddedSince, err)
30+
}
31+
return e.showFeaturesAddedSince(reg, sinceV)
32+
33+
case s.ForVersion != "":
34+
// SHOW FEATURES FOR VERSION x.y — no project connection needed
35+
pv, err = versions.ParseSemVer(s.ForVersion)
36+
if err != nil {
37+
return fmt.Errorf("invalid version %q: %w", s.ForVersion, err)
38+
}
39+
40+
default:
41+
// SHOW FEATURES [IN area] — requires project connection
42+
if e.reader == nil {
43+
return fmt.Errorf("not connected to a project\n hint: use SHOW FEATURES FOR VERSION x.y without a project connection")
44+
}
45+
rpv := e.reader.ProjectVersion()
46+
pv = versions.SemVer{Major: rpv.MajorVersion, Minor: rpv.MinorVersion, Patch: rpv.PatchVersion}
47+
}
48+
49+
if s.InArea != "" {
50+
return e.showFeaturesInArea(reg, pv, s.InArea)
51+
}
52+
return e.showFeaturesAll(reg, pv)
53+
}
54+
55+
func (e *Executor) showFeaturesAll(reg *versions.Registry, pv versions.SemVer) error {
56+
features := reg.FeaturesForVersion(pv)
57+
if len(features) == 0 {
58+
fmt.Fprintf(e.output, "No features found for version %s\n", pv)
59+
return nil
60+
}
61+
62+
fmt.Fprintf(e.output, "Features for Mendix %s:\n\n", pv)
63+
fmt.Fprintf(e.output, "| %-30s | %-9s | %-8s | %s |\n", "Feature", "Available", "Since", "Notes")
64+
fmt.Fprintf(e.output, "|%s|%s|%s|%s|\n",
65+
strings.Repeat("-", 32), strings.Repeat("-", 11), strings.Repeat("-", 10), strings.Repeat("-", 40))
66+
67+
available, unavailable := 0, 0
68+
for _, f := range features {
69+
avail := "Yes"
70+
if !f.Available {
71+
avail = "No"
72+
unavailable++
73+
} else {
74+
available++
75+
}
76+
notes := f.Notes
77+
if !f.Available && f.Workaround != nil {
78+
notes = f.Workaround.Description
79+
}
80+
if len(notes) > 38 {
81+
notes = notes[:35] + "..."
82+
}
83+
fmt.Fprintf(e.output, "| %-30s | %-9s | %-8s | %-38s |\n",
84+
f.DisplayName(), avail, f.MinVersion, notes)
85+
}
86+
87+
fmt.Fprintf(e.output, "\n(%d available, %d not available in %s)\n", available, unavailable, pv)
88+
return nil
89+
}
90+
91+
func (e *Executor) showFeaturesInArea(reg *versions.Registry, pv versions.SemVer, area string) error {
92+
features := reg.FeaturesInArea(area, pv)
93+
if len(features) == 0 {
94+
// Check if the area exists at all.
95+
areas := reg.Areas()
96+
fmt.Fprintf(e.output, "No features found in area %q for version %s\n", area, pv)
97+
fmt.Fprintf(e.output, "Available areas: %s\n", strings.Join(areas, ", "))
98+
return nil
99+
}
100+
101+
fmt.Fprintf(e.output, "Features in %s for Mendix %s:\n\n", area, pv)
102+
fmt.Fprintf(e.output, "| %-30s | %-9s | %-8s | %s |\n", "Feature", "Available", "Since", "Notes")
103+
fmt.Fprintf(e.output, "|%s|%s|%s|%s|\n",
104+
strings.Repeat("-", 32), strings.Repeat("-", 11), strings.Repeat("-", 10), strings.Repeat("-", 40))
105+
106+
for _, f := range features {
107+
avail := "Yes"
108+
if !f.Available {
109+
avail = "No"
110+
}
111+
notes := f.Notes
112+
if !f.Available && f.Workaround != nil {
113+
notes = f.Workaround.Description
114+
}
115+
if len(notes) > 38 {
116+
notes = notes[:35] + "..."
117+
}
118+
fmt.Fprintf(e.output, "| %-30s | %-9s | %-8s | %-38s |\n",
119+
f.DisplayName(), avail, f.MinVersion, notes)
120+
}
121+
122+
return nil
123+
}
124+
125+
func (e *Executor) showFeaturesAddedSince(reg *versions.Registry, sinceV versions.SemVer) error {
126+
added := reg.FeaturesAddedSince(sinceV)
127+
if len(added) == 0 {
128+
fmt.Fprintf(e.output, "No new features found since %s\n", sinceV)
129+
return nil
130+
}
131+
132+
fmt.Fprintf(e.output, "Features added since Mendix %s:\n\n", sinceV)
133+
fmt.Fprintf(e.output, "| %-30s | %-12s | %-10s | %s |\n", "Feature", "Area", "Since", "Notes")
134+
fmt.Fprintf(e.output, "|%s|%s|%s|%s|\n",
135+
strings.Repeat("-", 32), strings.Repeat("-", 14), strings.Repeat("-", 12), strings.Repeat("-", 40))
136+
137+
for _, f := range added {
138+
notes := f.Notes
139+
if f.MDL != "" && notes == "" {
140+
notes = f.MDL
141+
}
142+
if len(notes) > 38 {
143+
notes = notes[:35] + "..."
144+
}
145+
fmt.Fprintf(e.output, "| %-30s | %-12s | %-10s | %-38s |\n",
146+
f.DisplayName(), f.Area, f.MinVersion, notes)
147+
}
148+
149+
fmt.Fprintf(e.output, "\n(%d features added since %s)\n", len(added), sinceV)
150+
return nil
151+
}

mdl/executor/executor.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,8 @@ func (e *Executor) executeInner(stmt ast.Statement) error {
329329
// Query statements
330330
case *ast.ShowStmt:
331331
return e.execShow(s)
332+
case *ast.ShowFeaturesStmt:
333+
return e.execShowFeatures(s)
332334
case *ast.ShowWidgetsStmt:
333335
return e.execShowWidgets(s)
334336
case *ast.UpdateWidgetsStmt:

mdl/grammar/MDLLexer.g4

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,14 @@ SUBSCRIBE: S U B S C R I B E;
576576
SETTINGS: S E T T I N G S;
577577
CONFIGURATION: C O N F I G U R A T I O N;
578578

579+
// =============================================================================
580+
// VERSION FEATURE REGISTRY KEYWORDS
581+
// =============================================================================
582+
583+
FEATURES: F E A T U R E S;
584+
ADDED: A D D E D;
585+
SINCE: S I N C E;
586+
579587
// =============================================================================
580588
// SECURITY KEYWORDS
581589
// =============================================================================

mdl/grammar/MDLParser.g4

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2540,6 +2540,9 @@ showStatement
25402540
| SHOW REST CLIENTS (IN (qualifiedName | IDENTIFIER))? // SHOW REST CLIENTS [IN module]
25412541
| SHOW PUBLISHED REST SERVICES (IN (qualifiedName | IDENTIFIER))? // SHOW PUBLISHED REST SERVICES [IN module]
25422542
| SHOW LANGUAGES // SHOW LANGUAGES
2543+
| SHOW FEATURES (IN IDENTIFIER)? // SHOW FEATURES [IN area]
2544+
| SHOW FEATURES FOR VERSION NUMBER_LITERAL // SHOW FEATURES FOR VERSION 10.24
2545+
| SHOW FEATURES ADDED SINCE NUMBER_LITERAL // SHOW FEATURES ADDED SINCE 10.24
25432546
;
25442547

25452548
/**

mdl/grammar/parser/MDLLexer.interp

Lines changed: 10 additions & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)