Skip to content

Commit 22b440e

Browse files
committed
fix: address code review issues for pluggable widget engine
- Use atomic.Uint32 for placeholderCounter to prevent data races - Only ignore os.IsNotExist in loadDefinitionsFromDir, warn on other errors - Propagate registry init failure reason in unsupported widget type errors - Document LSP completion cache sync.Once limitation
1 parent 8852bce commit 22b440e

File tree

5 files changed

+21
-9
lines changed

5 files changed

+21
-9
lines changed

cmd/mxcli/lsp_completion.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ func mdlCompletionItems(linePrefixUpper string) []protocol.CompletionItem {
7979
}
8080

8181
// widgetRegistryCompletions returns completion items for registered widget types.
82+
// NOTE: Cached via sync.Once — new .def.json files added while the LSP server is
83+
// running will not appear until the server is restarted.
8284
var (
8385
widgetCompletionsOnce sync.Once
8486
widgetCompletionItems []protocol.CompletionItem

mdl/executor/cmd_pages_builder.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ type pageBuilder struct {
3434
themeRegistry *ThemeRegistry // Theme design property definitions (may be nil)
3535

3636
// Pluggable widget engine (lazily initialized)
37-
widgetRegistry *WidgetRegistry
38-
pluggableEngine *PluggableWidgetEngine
37+
widgetRegistry *WidgetRegistry
38+
pluggableEngine *PluggableWidgetEngine
39+
pluggableEngineErr error // stores init failure reason for better error messages
3940

4041
// Per-operation caches (may change during execution)
4142
layoutsCache []*pages.Layout
@@ -54,7 +55,8 @@ func (pb *pageBuilder) initPluggableEngine() {
5455
}
5556
registry, err := NewWidgetRegistry()
5657
if err != nil {
57-
fmt.Fprintf(os.Stderr, "warning: pluggable widget registry init failed: %v\n", err)
58+
pb.pluggableEngineErr = fmt.Errorf("widget registry init failed: %w", err)
59+
fmt.Fprintf(os.Stderr, "warning: %v\n", pb.pluggableEngineErr)
5860
return
5961
}
6062
if pb.reader != nil {

mdl/executor/cmd_pages_builder_v3.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,9 @@ func (pb *pageBuilder) buildWidgetV3(w *ast.WidgetV3) (pages.Widget, error) {
331331
return pb.pluggableEngine.Build(def, w)
332332
}
333333
}
334+
if pb.pluggableEngineErr != nil {
335+
return nil, fmt.Errorf("unsupported V3 widget type: %s (%v)", w.Type, pb.pluggableEngineErr)
336+
}
334337
return nil, fmt.Errorf("unsupported V3 widget type: %s", w.Type)
335338
}
336339

mdl/executor/widget_registry.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,11 @@ func (r *WidgetRegistry) LoadUserDefinitions(projectPath string) error {
109109
func (r *WidgetRegistry) loadDefinitionsFromDir(dir string) error {
110110
entries, err := os.ReadDir(dir)
111111
if err != nil {
112-
return nil // directory doesn't exist or not readable — not an error
112+
if os.IsNotExist(err) {
113+
return nil
114+
}
115+
fmt.Fprintf(os.Stderr, "warning: cannot read widget definitions from %s: %v\n", dir, err)
116+
return nil
113117
}
114118

115119
for _, entry := range entries {

sdk/widgets/augment.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package widgets
55
import (
66
"encoding/json"
77
"fmt"
8+
"sync/atomic"
89

910
"github.com/mendixlabs/mxcli/sdk/widgets/mpk"
1011
)
@@ -560,20 +561,20 @@ func buildNestedObjectType(children []mpk.PropertyDef) map[string]any {
560561

561562
// --- Helpers ---
562563

563-
// placeholderCounter generates sequential placeholder IDs.
564-
var placeholderCounter uint32
564+
// placeholderCounter generates sequential placeholder IDs (atomic for concurrent safety).
565+
var placeholderCounter atomic.Uint32
565566

566567
// placeholderID generates a placeholder hex ID. These will be remapped by collectIDs
567568
// in GetTemplateFullBSON, so exact values don't matter — they just need to be unique
568569
// 32-char hex strings.
569570
func placeholderID() string {
570-
placeholderCounter++
571-
return fmt.Sprintf("aa000000000000000000000000%06x", placeholderCounter)
571+
n := placeholderCounter.Add(1)
572+
return fmt.Sprintf("aa000000000000000000000000%06x", n)
572573
}
573574

574575
// ResetPlaceholderCounter resets the counter (for testing).
575576
func ResetPlaceholderCounter() {
576-
placeholderCounter = 0
577+
placeholderCounter.Store(0)
577578
}
578579

579580
// getMapField gets a nested map field from a JSON map.

0 commit comments

Comments
 (0)