feat: scope Liquid objects to step URIs for Flow config field LSP support#1238
feat: scope Liquid objects to step URIs for Flow config field LSP support#1238Gasser-Aly wants to merge 4 commits into
Conversation
setObjects notification
setObjects notification…s init flag Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| connection.onNotification(SetObjectsNotification.type, ({ uri, objects }) => { | ||
| themeDocset.setObjectsForURI(uri, objects); | ||
| }); |
There was a problem hiding this comment.
Why a notification and not a request? Does the client not care about if/when this resolves? Don't care to await to make sure the objects are set before proceeding?
There was a problem hiding this comment.
100% valid concern
e.g.
- flow opens a config field doc
- the doc contains some object
- the language server receives textDocument/didOpen
- this triggers runChecks
- at this point Flow hasn't sent
shopify/setObjects - unknown object occurs
| connection.onNotification(SetObjectsNotification.type, ({ uri, objects }) => { | ||
| themeDocset.setObjectsForURI(uri, objects); | ||
| }); |
There was a problem hiding this comment.
100% valid concern
e.g.
- flow opens a config field doc
- the doc contains some object
- the language server receives textDocument/didOpen
- this triggers runChecks
- at this point Flow hasn't sent
shopify/setObjects - unknown object occurs
| if (exact) return exact; | ||
|
|
||
| // Prefix match: allows registering by step path and matching file URIs within it | ||
| for (const [prefix, objects] of this.objectsByPrefix) { |
There was a problem hiding this comment.
A URI under step-10 can match a previously registered step-1 prefix, and overlapping prefixes will not pick the most specific match. This should use a normalized prefix boundary and/or longest-prefix match.
| { | ||
| "name": "@shopify/theme-check-common", | ||
| "version": "3.26.1", | ||
| "version": "3.26.1-flow.0", |
There was a problem hiding this comment.
You need to also update theme-language-server-common's package.json so it uses it
| if (this.isUriScopedMode() && uri && this.themeDocset.getObjectsForURI) { | ||
| const perURI = this.themeDocset.getObjectsForURI(uri); | ||
| if (perURI) return perURI; | ||
| } |
There was a problem hiding this comment.
Are we okay if Flow sends only step-specific objects, built-in Shopify types and augmented objects disappear for completion/hover/diagnostics? More question than concern here.
Why
Flow's Workflow Editor is a highly dynamic environment. As users build workflows, each step has its own runtime context — a step triggered by "customer created" has completely different Liquid objects available than one triggered by "order created". When a user changes a trigger or reconfigures a step, the available objects change too.
Each step can have multiple config fields, and each config field is an independently-editable Liquid snippet (a separate file). All config fields within a step share the same set of Liquid objects — the objects provided by that step's environment, fetched from the backend.
Before this PR, the Liquid LSP had no concept of per-file or per-step object scoping. It could only use the global theme object list, which meant:
This PR introduces the mechanism for clients to register custom Liquid objects scoped to a URI prefix, so the Flow UI can tell the LSP exactly which objects are valid for a given step's config fields.
What this PR adds
setObjectsForURI/getObjectsForURIonAugmentedThemeDocset— stores and looks upObjectEntry[]by exact URI or prefix match, so all config field files under a step URI share the same registered objects.shopify/setObjectsLSP notification — a newSetObjectsNotificationthat clients send to register custom objects for a URI. The Flow UI sends this after fetching the step's environment from the backend.shopify.supportsSetObjectsinitialization flag — clients opt in by setting this ininitializationOptions. Whentrue,TypeSystembypasses memoization for object lookups (since results are now URI-dependent) and routes through per-URI objects. Whenfalse(the default), all existing memoization is preserved — no performance impact for theme developers.TypeSystem—objectEntries,_objectMap, andglobalVariablesnow receive the current file URI and use per-URI objects when the flag is on, falling back to memo'd global results otherwise.ObjectAttributeCompletionProviderwhen custom objects are registered for the current URI — avoids surfacing misleading array/string built-in suggestions in Flow's custom object context.What's next
useLiquidLsp) already callsupdateObjects(stepUri, lspObjects)— it needs to be wired to send theshopify/setObjectsnotification using this new mechanism, and passshopify.supportsSetObjects: trueininitializationOptions.