import { ExternalImportsCallbacks , isSupportedDocument , parse , YAMLDocument } from "@serverless-ide/config" ; import { IConnection , TextDocument , TextDocuments } from "vscode-languageserver" ; interface CacheEntry { uri : string ; version : number ; yamlDocument : Promise < YAMLDocument > ; isSupported : boolean ; } export class DocumentNotSupportedError extends Error { constructor ( ) { super ( "Document is not supported" ) ; } } export class DocumentNotFoundError extends Error { constructor ( uri ) { super ( `Document with uri "${ uri }" was not found` ) ; } } export interface WorkplaceFiles { [ uri ] : string ; } export interface LifecycleCallbacks { onWorkplaceFilesInitialized : ( files : WorkplaceFiles ) => void ; onWorkplaceFilesChanged : ( files : WorkplaceFiles ) => void ; onFileCreated : ( uri : string ) => void ; onFileDeleted : ( uri : string ) => void ; onFileChanged : ( uri : string ) => void ; onFileOpened : ( uri : string ) => void ; onFileClosed : ( uri : string ) => void ; } /**
 * Parsed document provider service
 */ export class DocumentService { private cache : { [ key ] : CacheEntry ; } = { } ; private documents : TextDocuments | null = null ; private connection : IConnection ; private externalImportsCallbacks : ExternalImportsCallbacks ; private lifecycleCallbacks : LifecycleCallbacks ; private childrenHash : { [ childUri ] : string ; } = { } ; private parentHash : { [ parentUri ] : string [ ] ; } = { } ; private workspaceFiles : WorkplaceFiles = { } ; constructor ( connection , documents , externalImportsCallbacks , lifecycleCallbacks ) { this . connection = connection ; this . documents = documents ; this . externalImportsCallbacks = externalImportsCallbacks ; this . lifecycleCallbacks = lifecycleCallbacks ; this . requestWorkspaceFiles ( ) . then ( workspaceFiles => { this . lifecycleCallbacks . onWorkplaceFilesInitialized ( workspaceFiles ) ; } ) ; this . connection . workspace . onDidChangeWorkspaceFolders ( ( ) => { this . requestWorkspaceFiles ( ) . then ( workspaceFiles => { this . lifecycleCallbacks . onWorkplaceFilesChanged ( workspaceFiles ) ; } ) ; } ) ; this . connection . onNotification ( "workplace/oncreate" , uri => { this . workspaceFiles [ uri ] = uri ; this . lifecycleCallbacks . onFileCreated ( uri ) ; } ) ; this . connection . onNotification ( "workplace/ondelete" , uri => { delete this . workspaceFiles [ uri ] ; this . lifecycleCallbacks . onFileDeleted ( uri ) ; } ) ; this . connection . onNotification ( "workplace/onchanged" , uri => { this . lifecycleCallbacks . onFileChanged ( uri ) ; } ) ; documents . onDidClose ( event => { this . lifecycleCallbacks . onFileClosed ( event . document . uri ) ; } ) ; documents . onDidSave ( async ( event ) => { this . lifecycleCallbacks . onFileChanged ( event . document . uri ) ; } ) ; documents . onDidOpen ( async ( event ) => { this . lifecycleCallbacks . onFileOpened ( event . document . uri ) ; } ) ; } async getTextDocument ( uri ) { let document = this . documents . get ( uri ) ; if ( ! document ) { const requestedDocument = await this . requestWorkspaceFile ( uri ) ; if ( requestedDocument ) { this . workspaceFiles [ uri ] = uri ; return requestedDocument ; } throw new DocumentNotFoundError ( uri ) ; } return document ; } async getYamlDocument ( uri ) { const parentUri = this . childrenHash [ uri ] ; const document = await this . getTextDocument ( uri ) ; if ( document ) { return this . getDocument ( document , parentUri ) ; } else { throw new DocumentNotFoundError ( uri ) ; } } registerChildParentRelation ( uri , parentUri ) { this . childrenHash [ uri ] = parentUri ; if ( ! this . parentHash [ parentUri ] ) { this . parentHash [ parentUri ] = [ ] ; } this . parentHash [ parentUri ] . push ( uri ) ; } getChildrenUris ( parentUri ) { return this . parentHash [ parentUri ] ; } clear ( uri ) { delete this . cache [ uri ] ; this . clearRelations ( uri ) ; } clearRelations ( uri ) { const children = this . parentHash [ uri ] ; if ( children && children . length ) { children . forEach ( childUri => { delete this . childrenHash [ childUri ] ; delete this . cache [ childUri ] ; } ) ; } if ( this . childrenHash [ uri ] ) { this . parentHash [ this . childrenHash [ uri ] ] = this . parentHash [ this . childrenHash [ uri ] ] . filter ( childUri => childUri !== uri ) ; delete this . childrenHash [ uri ] ; } delete this . parentHash [ uri ] ; } private getDocument ( textDocument , parentUri ? ) { return this . parseDocument ( textDocument , parentUri ) ; } private async parseDocument ( textDocument , parentUri ? ) { const text = textDocument . getText ( ) ; let parent ; if ( parentUri ) { parent = await this . getYamlDocument ( parentUri ) ; } if ( parent || isSupportedDocument ( text ) ) { return Promise . resolve ( parse ( textDocument , this . externalImportsCallbacks , parent && { uri : parentUri , documentType : parent . documentType } ) ) ; } return Promise . reject ( new DocumentNotSupportedError ( ) ) ; } private async requestWorkspaceFiles ( ) { return await this . connection . sendRequest ( "workplace/list" , "**/*.{yaml,yml}" ) . then ( ( filesList ) => { filesList . forEach ( fileUri => { this . workspaceFiles [ fileUri ] = fileUri ; } ) ; return this . workspaceFiles ; } ) ; } private async requestWorkspaceFile ( uri ) { const content = await this . connection . sendRequest ( "workplace/file" , uri ) ; if ( content ) { return TextDocument . create ( uri , "yaml" , 0 , content ) ; } } }