import postcssValueParser from 'postcss-value-parser' ; import type { ParameterInformation , SignatureHelp , SignatureInformation , } from 'vscode-languageserver' ; import { StateParsedValue , systemValidators } from '@stylable/core' ; import type { ProviderPosition } from '../completion-providers' ; export function resolveStateTypeOrValidator ( pos , line ) { const valueStartChar = line . indexOf ( ':' ) + 1 ; const value = line . slice ( valueStartChar ) ; const { nodes : stateParts } = postcssValueParser ( value ) ; let requiredHinting = false ; const validator = { length : 0 , requiredHinting : false } ; let stateTypeValidatorToHint = null ; let length = valueStartChar ; for ( const statePart of stateParts ) { length += statePart . value . length ; if ( statePart . type === 'function' ) { const stateNodes = statePart . nodes ; length ++ ; ( { length , requiredHinting } = resolvePosInState ( pos . character , length , statePart . before ) ) ; if ( requiredHinting || stateTypeValidatorToHint ) { continue ; } ( { length , requiredHinting , stateTypeValidatorToHint } = resolveStateType ( stateNodes , length , requiredHinting , pos , validator , stateTypeValidatorToHint ) ) ; if ( requiredHinting || stateTypeValidatorToHint ) { continue ; } else { ( { length , requiredHinting } = resolvePosInState ( pos . character , length , statePart . after ) ) ; } length ++ ; } else if ( statePart . type === 'div' ) { length = length + statePart . before . length + statePart . after . length ; } } if ( stateTypeValidatorToHint ) { return stateTypeValidatorToHint ; } else if ( requiredHinting ) { return requiredHinting ; } return null ; } function resolveStateType ( stateNodes , length , requiredHinting , pos , validator , stateTypeValidatorToHint ) { for ( const typeNode of stateNodes ) { ( { length , requiredHinting } = resolvePosInState ( pos . character , length , typeNode . value ) ) ; if ( ! requiredHinting && typeNode . type === 'function' ) { length ++ ; validator = resolvePosInState ( pos . character , length , typeNode . before ) ; stateTypeValidatorToHint = isValidatorsHintingRequired ( validator . requiredHinting , stateTypeValidatorToHint , typeNode . value ) ; length = validator . length ; typeNode . nodes . forEach ( ( valNode ) => { ( { validator , length , stateTypeValidatorToHint } = resolveStateValidator ( pos , length , valNode , stateTypeValidatorToHint , typeNode ) ) ; } ) ; validator = resolvePosInState ( pos . character , length , typeNode . after ) ; stateTypeValidatorToHint = isValidatorsHintingRequired ( validator . requiredHinting , stateTypeValidatorToHint , typeNode . value ) ; length = validator . length ; length ++ ; } } return { length , requiredHinting , stateTypeValidatorToHint } ; } function resolveStateValidator ( pos , length , valNode , stateTypeValidatorToHint , typeNode ) { const validator = resolvePosInState ( pos . character , length , valNode . value ) ; stateTypeValidatorToHint = isValidatorsHintingRequired ( validator . requiredHinting , stateTypeValidatorToHint , typeNode . value ) ; if ( valNode . type === 'function' ) { length ++ ; const argsLength = valNode . nodes . reduce ( ( sum , node ) => { const quotes = ( node . quote && 2 ) || 0 ; const before = ( node . before && node . before . length ) || 0 ; const after = ( node . after && node . after . length ) || 0 ; return sum + node . value . length + before + after + quotes ; } , 0 ) ; length = validator . length + argsLength ; length ++ ; } return { validator , length , stateTypeValidatorToHint } ; } function isValidatorsHintingRequired ( requiredHinting , stateTypeValidatorToHint , type ) { return requiredHinting ? type : stateTypeValidatorToHint ; } export function createStateValidatorSignature ( type ) { const valiadtors = systemValidators [ type ] . subValidators ; if ( valiadtors ) { const validatorsString = Object . keys ( valiadtors ) . join ( ', ' ) ; const sigInfo = { label : `Supported "${ type }" validator types:\n- "${ validatorsString }"` , parameters : [ { label : validatorsString } ] as ParameterInformation [ ] , } ; return { activeParameter : 0 , activeSignature : 0 , signatures : [ sigInfo ] , } as SignatureHelp ; } else { return null ; } } export function createStateTypeSignature ( ) { const stateTypes = Object . keys ( systemValidators ) . join ( ' | ' ) ; const sigInfo = { label : `Supported state types:\n- "${ stateTypes }"` , parameters : [ { label : stateTypes } ] as ParameterInformation [ ] , } ; return { activeParameter : 0 , activeSignature : 0 , signatures : [ sigInfo ] , } as SignatureHelp ; } function resolvePosInState ( character , length , arg ) { let requiredHinting = false ; if ( isBetweenLengths ( character , length , arg ) ) { requiredHinting = true ; } length = length + arg . length ; return { length , requiredHinting } ; } export function isBetweenLengths ( location , length , modifier ) { return location >= length && location <= length + modifier . length ; } export function resolveStateParams ( stateDef ) { const typeArguments = [ ] ; if ( stateDef . arguments . length > 0 ) { stateDef . arguments . forEach ( ( arg ) => { if ( typeof arg === 'object' ) { if ( arg . args . length > 0 ) { typeArguments . push ( `${ arg . name }(${ arg . args . join ( ', ' ) })` ) ; } } else if ( typeof arg === 'string' ) { typeArguments . push ( arg ) ; } } ) ; } const parameters = typeArguments . length > 0 ? `${ stateDef . type }(${ typeArguments . join ( ', ' ) })` : stateDef . type ; return parameters ; }