import { Hover , Position } from 'vscode-languageserver' ; import { SvelteDocument } from '../SvelteDocument' ; import { documentation , SvelteTag , getLatestOpeningTag } from './SvelteTags' ; import { flatten } from '../../../utils' ; import { Document , isInTag } from '../../../lib/documents' ; import { AttributeContext , getAttributeContextAtPosition } from '../../../lib/documents/parseHtml' ; import { attributeCanHaveEventModifier } from './utils' ; import { getModifierData } from './getModifierData' ; /**
 * Get hover information for special svelte tags within moustache tags.
 */ export function getHoverInfo ( document , svelteDoc , position ) { const offset = svelteDoc . offsetAt ( position ) ; const isInStyleOrScript = isInTag ( position , svelteDoc . style ) || isInTag ( position , svelteDoc . script ) || isInTag ( position , svelteDoc . moduleScript ) ; const offsetStart = Math . max ( offset - 10 , 0 ) ; const charactersAroundOffset = svelteDoc . getText ( ) . substr ( offsetStart , 20 ) ; const isSvelteTag = tagRegexp . test ( charactersAroundOffset ) ; if ( isInStyleOrScript ) { return null ; } if ( isSvelteTag ) { return getTagHoverInfoAtOffset ( svelteDoc , offsetStart , charactersAroundOffset , offset ) ; } const attributeContext = getAttributeContextAtPosition ( document , position ) ; if ( ! attributeContext || ! attributeCanHaveEventModifier ( attributeContext ) ) { return null ; } const attributeOffset = svelteDoc . getText ( ) . lastIndexOf ( attributeContext . name , offset ) ; return getEventModifierHoverInfo ( attributeContext , attributeOffset , offset ) ; } function getTagHoverInfoAtOffset ( svelteDoc , charactersOffset , charactersAroundOffset , offset ) { const tag = getTagAtOffset ( svelteDoc , charactersOffset , charactersAroundOffset , offset ) ; if ( ! tag ) { return null ; } return { contents : documentation [ tag ] } ; } /**
 * Get the tag that is at the offset.
 */ function getTagAtOffset ( svelteDoc , charactersOffset , charactersAroundOffset , offset ) { const foundTag = tagPossibilities . find ( ( tagAndValues ) => tagAndValues . values . find ( ( value ) => isAroundOffset ( charactersOffset , charactersAroundOffset , value , offset ) ) ) ; if ( ! foundTag ) { return null ; } if ( foundTag . tag !== ':else' ) { return foundTag . tag ; } return getLatestOpeningTag ( svelteDoc , offset ) ; } function isAroundOffset ( charactersOffset , charactersAroundOffset , toFind , offset ) { const match = charactersAroundOffset . match ( toFind ) ; if ( ! match || match . index === undefined ) { return false ; } const idx = match . index + charactersOffset ; return idx <= offset && idx + toFind . length >= offset ; } const tagPossibilities = [ { tag : 'if' as const , values : [ '#if' , '/if' , ':else if' ] } , { tag : 'each' as const , values : [ '#each' , '/each' ] } , { tag : 'await' as const , values : [ '#await' , '/await' , ':then' , ':catch' ] } , { tag : 'key' as const , values : [ '#key' , '/key' ] } , { tag : 'html' as const , values : [ '@html' ] } , { tag : 'debug' as const , values : [ '@debug' ] } , { tag : 'const' as const , values : [ '@const' ] } , { tag : ':else' as const , values : [ ':else' ] } ] ; const tagRegexp = new RegExp ( `[\\s\\S]*{\\s*(${ flatten ( tagPossibilities . map ( ( p ) => p . values ) ) . join ( '|' ) })(\\s|})` ) ; function getEventModifierHoverInfo ( attributeContext , attributeOffset , offset ) { const { name } = attributeContext ; const modifierData = getModifierData ( ) ; const found = modifierData . find ( ( modifier ) => isAroundOffset ( attributeOffset , name , modifier . modifier , offset ) ) ; if ( ! found ) { return null ; } return { contents : found . documentation } ; }