@@ -4,20 +4,18 @@ import {
44 getRelatedTsFiles ,
55 loadClassInfosFromFiles ,
66 CollectedClassInfo ,
7- createClassInfoLookup ,
87 resolveViewBinding ,
98 ClassPropertyInfo ,
10- filterScreenLikeClasses ,
119 collectActionProperties ,
1210 extractConfigPropertyNames ,
13- filterClassesBySource ,
1411} from '../utils' ;
1512import {
1613 parseDocumentDom ,
1714 findNodeAtOffset ,
1815 elevateToElementNode ,
1916 getAttributeContext ,
2017 findParentViewName ,
18+ isActionStateBindTag ,
2119} from './html-shared' ;
2220import {
2321 ClientControlMetadata ,
@@ -27,6 +25,16 @@ import { getScreenTemplates } from '../services/screen-template-service';
2725import { getIncludeMetadata , IncludeMetadata } from '../services/include-service' ;
2826import { BackendFieldMetadata , normalizeMetaName } from '../backend-metadata-utils' ;
2927import { loadBackendFieldsForView } from './html-backend-utils' ;
28+ import { getBaseScreenDocument } from '../services/screen-html-service' ;
29+ import {
30+ HtmlIncludeFieldContext ,
31+ createHtmlFieldMetadataContext ,
32+ getFieldPropertiesFromViews ,
33+ getIncludeFieldContext ,
34+ getParentOrSelectorViewName ,
35+ getViewNameFromCustomizationSelectors ,
36+ resolveTemplateValue ,
37+ } from '../services/html-field-context-service' ;
3038
3139// Registers completions so HTML view bindings stay in sync with PX metadata.
3240export function registerHtmlCompletionProvider ( context : vscode . ExtensionContext ) {
@@ -103,13 +111,8 @@ export class HtmlCompletionProvider implements vscode.CompletionItemProvider {
103111 return undefined ;
104112 }
105113
106- const relevantClassInfos = filterClassesBySource ( classInfos , tsFilePaths ) ;
107- if ( ! relevantClassInfos . length ) {
108- return undefined ;
109- }
110-
111- const classInfoLookup = createClassInfoLookup ( classInfos ) ;
112- const screenClasses = filterScreenLikeClasses ( relevantClassInfos ) ;
114+ const metadataContext = createHtmlFieldMetadataContext ( classInfos , tsFilePaths ) ;
115+ const { classInfoLookup, screenClasses } = metadataContext ;
113116 // Completions are sourced from the same metadata as validation/definitions to keep behavior consistent.
114117
115118 if ( attributeContext . attributeName === 'view.bind' ) {
@@ -121,7 +124,9 @@ export class HtmlCompletionProvider implements vscode.CompletionItemProvider {
121124 }
122125
123126 if ( attributeContext . attributeName === 'state.bind' ) {
124- return this . createActionCompletions ( screenClasses ) ;
127+ return isActionStateBindTag ( attributeContext . tagName )
128+ ? this . createActionCompletions ( screenClasses , classInfoLookup , elementNode , attributeContext . value )
129+ : this . createControlStateCompletions ( attributeContext . value , screenClasses , classInfoLookup ) ;
125130 }
126131
127132 if ( attributeContext . attributeName === 'control-state.bind' && attributeContext . tagName === 'qp-field' ) {
@@ -130,7 +135,35 @@ export class HtmlCompletionProvider implements vscode.CompletionItemProvider {
130135
131136 if ( attributeContext . attributeName === 'name' && attributeContext . tagName === 'field' ) {
132137 // Field completions are scoped to the PXView resolved from the surrounding markup.
133- const viewName = findParentViewName ( elementNode ) ;
138+ const includeContext = getIncludeFieldContext ( {
139+ documentPath : document . uri . fsPath ,
140+ elementNode,
141+ hostTsFilePaths : tsFilePaths ,
142+ workspaceRoots : vscode . workspace . workspaceFolders ?. map ( folder => folder . uri . fsPath ) ,
143+ } ) ;
144+ if ( includeContext ) {
145+ const hostSelectorViewName = resolveTemplateValue (
146+ getViewNameFromCustomizationSelectors ( elementNode , includeContext . templateDocument ) ,
147+ includeContext . parameterValues
148+ ) ;
149+ const hostSelectorResolution = hostSelectorViewName
150+ ? resolveViewBinding ( hostSelectorViewName , screenClasses , classInfoLookup )
151+ : undefined ;
152+ if ( hostSelectorResolution ?. viewClass ) {
153+ const backendFields = await loadBackendFieldsForView ( hostSelectorViewName ! , screenClasses ) ;
154+ return this . createFieldCompletions ( hostSelectorResolution . viewClass . properties , backendFields ) ;
155+ }
156+
157+ const items = this . createIncludeFieldNameCompletions ( elementNode , includeContext ) ;
158+ if ( items ?. length ) {
159+ return items ;
160+ }
161+ }
162+
163+ const viewName = getParentOrSelectorViewName (
164+ elementNode ,
165+ getBaseScreenDocument ( document . uri . fsPath )
166+ ) ;
134167 if ( ! viewName ) {
135168 return undefined ;
136169 }
@@ -366,6 +399,22 @@ export class HtmlCompletionProvider implements vscode.CompletionItemProvider {
366399 return items . length ? items : undefined ;
367400 }
368401
402+ private createIncludeFieldNameCompletions (
403+ elementNode : any ,
404+ context : HtmlIncludeFieldContext
405+ ) : vscode . CompletionItem [ ] | undefined {
406+ const selectorViewName = getViewNameFromCustomizationSelectors ( elementNode , context . templateDocument ) ;
407+ const viewName = resolveTemplateValue ( selectorViewName , context . parameterValues ) ;
408+ if ( viewName ) {
409+ const resolution = resolveViewBinding ( viewName , context . screenClasses , context . classInfoLookup ) ;
410+ const viewClass = resolution ?. viewClass ;
411+ return viewClass ? this . createFieldCompletions ( viewClass . properties ) : undefined ;
412+ }
413+
414+ const fields = getFieldPropertiesFromViews ( context ) ;
415+ return fields . size ? this . createFieldCompletions ( fields ) : undefined ;
416+ }
417+
369418 private isInsideDataFeed ( node : any ) : boolean {
370419 let current = node ?. parent ?? node ?. parentNode ;
371420 while ( current ) {
@@ -590,14 +639,58 @@ export class HtmlCompletionProvider implements vscode.CompletionItemProvider {
590639 return items ;
591640 }
592641
593- private createActionCompletions ( screenClasses : CollectedClassInfo [ ] ) : vscode . CompletionItem [ ] {
642+ private createActionCompletions (
643+ screenClasses : CollectedClassInfo [ ] ,
644+ classInfoLookup : Map < string , CollectedClassInfo > ,
645+ elementNode : any ,
646+ currentValue ?: string
647+ ) : vscode . CompletionItem [ ] {
648+ const normalizedPrefix = ( currentValue ?? '' ) . trim ( ) . toLowerCase ( ) ;
594649 const actionMap = collectActionProperties ( screenClasses ) ;
595650 const items : vscode . CompletionItem [ ] = [ ] ;
596- actionMap . forEach ( ( property , name ) => {
651+ const seen = new Set < string > ( ) ;
652+ const addAction = ( name : string , property : ClassPropertyInfo ) => {
653+ if ( seen . has ( name ) ) {
654+ return ;
655+ }
656+ if ( normalizedPrefix && ! name . toLowerCase ( ) . startsWith ( normalizedPrefix ) ) {
657+ return ;
658+ }
659+ seen . add ( name ) ;
597660 const item = new vscode . CompletionItem ( name , vscode . CompletionItemKind . Function ) ;
598661 item . detail = property . typeName ?? 'PXActionState' ;
599662 items . push ( item ) ;
663+ } ;
664+
665+ actionMap . forEach ( ( property , name ) => {
666+ addAction ( name , property ) ;
600667 } ) ;
668+
669+ const viewName = findParentViewName ( elementNode ) ;
670+ const viewClass = viewName
671+ ? resolveViewBinding ( viewName , screenClasses , classInfoLookup ) ?. viewClass
672+ : undefined ;
673+ viewClass ?. properties . forEach ( ( property , name ) => {
674+ if ( property . kind === 'action' ) {
675+ addAction ( name , property ) ;
676+ }
677+ } ) ;
678+
679+ for ( const screenClass of screenClasses ) {
680+ for ( const [ propertyName , property ] of screenClass . properties ) {
681+ if ( property . kind !== 'view' && property . kind !== 'viewCollection' ) {
682+ continue ;
683+ }
684+
685+ const viewClass = resolveViewBinding ( propertyName , screenClasses , classInfoLookup ) ?. viewClass ;
686+ viewClass ?. properties . forEach ( ( viewProperty , actionName ) => {
687+ if ( viewProperty . kind === 'action' ) {
688+ addAction ( `${ propertyName } .${ actionName } ` , viewProperty ) ;
689+ }
690+ } ) ;
691+ }
692+ }
693+
601694 return items ;
602695 }
603696
0 commit comments