'use strict' ; import { workspace , ExtensionContext , extensions , window , commands , Uri } from 'vscode' ; import { CommonLanguageClient , LanguageClientOptions , NotificationType , RequestType , RevealOutputChannelOn , } from 'vscode-languageclient' ; import { CUSTOM_SCHEMA_REQUEST , CUSTOM_CONTENT_REQUEST , SchemaExtensionAPI } from './schema-extension-api' ; import { joinPath } from './paths' ; import { getJsonSchemaContent , IJSONSchemaCache , JSONSchemaDocumentContentProvider } from './json-schema-content-provider' ; import { getConflictingExtensions , showUninstallConflictsNotification } from './extensionConflicts' ; import { TelemetryErrorHandler , TelemetryOutputChannel } from './telemetry' ; import { TextDecoder } from 'util' ; import { createJSONSchemaStatusBarItem } from './schema-status-bar-item' ; export interface ISchemaAssociations { [ pattern ] : string [ ] ; } export interface ISchemaAssociation { fileMatch : string [ ] ; uri : string ; } namespace SettingIds { export const maxItemsComputed = 'yaml.maxItemsComputed' ; } namespace StorageIds { export const maxItemsExceededInformation = 'yaml.maxItemsExceededInformation' ; } namespace SchemaAssociationNotification { export const type = new NotificationType ( 'json/schemaAssociations' ) ; } namespace VSCodeContentRequestRegistration { export const type = new NotificationType ( 'yaml/registerContentRequest' ) ; } namespace VSCodeContentRequest { export const type = new RequestType ( 'vscode/content' ) ; } namespace FSReadFile { export const type = new RequestType ( 'fs/readFile' ) ; } namespace DynamicCustomSchemaRequestRegistration { export const type = new NotificationType ( 'yaml/registerCustomSchemaRequest' ) ; } namespace ResultLimitReachedNotification { export const type = new NotificationType ( 'yaml/resultLimitReached' ) ; } export namespace SchemaSelectionRequests { export const type = new NotificationType ( 'yaml/supportSchemaSelection' ) ; export const schemaStoreInitialized = new NotificationType ( 'yaml/schema/store/initialized' ) ; } let client ; const lsName = 'YAML Support' ; export type LanguageClientConstructor = ( name : string , description : string , clientOptions : LanguageClientOptions ) => CommonLanguageClient ; export interface RuntimeEnvironment { readonly telemetry : TelemetryService ; readonly schemaCache : IJSONSchemaCache ; } export interface TelemetryService { send ( arg ) ; } export function startClient ( context , newLanguageClient , runtime ) { const telemetryErrorHandler = new TelemetryErrorHandler ( runtime . telemetry , lsName , 4 ) ; const outputChannel = window . createOutputChannel ( lsName ) ; const clientOptions = { documentSelector : [ { language : 'yaml' } , { language : 'dockercompose' } , { pattern : '*.y(a)ml' } ] , synchronize : { fileEvents : [ workspace . createFileSystemWatcher ( '**/*.?(e)y?(a)ml' ) , workspace . createFileSystemWatcher ( '**/*.json' ) ] , } , revealOutputChannelOn : RevealOutputChannelOn . Never , errorHandler : telemetryErrorHandler , outputChannel : new TelemetryOutputChannel ( outputChannel , runtime . telemetry ) , } ; client = newLanguageClient ( 'yaml' , lsName , clientOptions ) ; const disposable = client . start ( ) ; const schemaExtensionAPI = new SchemaExtensionAPI ( client ) ; context . subscriptions . push ( disposable ) ; context . subscriptions . push ( workspace . registerTextDocumentContentProvider ( 'json-schema' , new JSONSchemaDocumentContentProvider ( runtime . schemaCache , schemaExtensionAPI ) ) ) ; context . subscriptions . push ( client . onTelemetry ( ( e ) => { runtime . telemetry . send ( e ) ; } ) ) ; findConflicts ( ) ; client . onReady ( ) . then ( ( ) => { client . sendNotification ( SchemaAssociationNotification . type , getSchemaAssociations ( ) ) ; extensions . onDidChange ( ( ) => { client . sendNotification ( SchemaAssociationNotification . type , getSchemaAssociations ( ) ) ; findConflicts ( ) ; } ) ; client . sendNotification ( DynamicCustomSchemaRequestRegistration . type ) ; client . sendNotification ( VSCodeContentRequestRegistration . type ) ; client . sendNotification ( SchemaSelectionRequests . type ) ; client . onRequest ( CUSTOM_SCHEMA_REQUEST , ( resource ) => { return schemaExtensionAPI . requestCustomSchema ( resource ) ; } ) ; client . onRequest ( CUSTOM_CONTENT_REQUEST , ( uri ) => { return schemaExtensionAPI . requestCustomSchemaContent ( uri ) ; } ) ; client . onRequest ( VSCodeContentRequest . type , ( uri ) => { return getJsonSchemaContent ( uri , runtime . schemaCache ) ; } ) ; client . onRequest ( FSReadFile . type , ( fsPath ) => { return workspace . fs . readFile ( Uri . file ( fsPath ) ) . then ( ( uint8array ) => new TextDecoder ( ) . decode ( uint8array ) ) ; } ) ; runtime . telemetry . send ( { name : 'yaml.server.initialized' } ) ; client . onNotification ( ResultLimitReachedNotification . type , async ( message ) => { const shouldPrompt = context . globalState . get < boolean > ( StorageIds . maxItemsExceededInformation ) !== false ; if ( shouldPrompt ) { const ok = 'Ok' ; const openSettings = 'Open Settings' ; const neverAgain = "Don't Show Again" ; const pick = await window . showInformationMessage ( `${ message }\nUse setting '${ SettingIds . maxItemsComputed }' to configure the limit.` , ok , openSettings , neverAgain ) ; if ( pick === neverAgain ) { await context . globalState . update ( StorageIds . maxItemsExceededInformation , false ) ; } else if ( pick === openSettings ) { await commands . executeCommand ( 'workbench.action.openSettings' , SettingIds . maxItemsComputed ) ; } } } ) ; client . onNotification ( SchemaSelectionRequests . schemaStoreInitialized , ( ) => { createJSONSchemaStatusBarItem ( context , client ) ; } ) ; } ) ; return schemaExtensionAPI ; } /**
 * Finds extensions that conflict with VSCode-YAML.
 * If one or more conflicts are found then show an uninstall notification
 * If no conflicts are found then do nothing
 */ function findConflicts ( ) { const conflictingExtensions = getConflictingExtensions ( ) ; if ( conflictingExtensions . length > 0 ) { showUninstallConflictsNotification ( conflictingExtensions ) ; } } function getSchemaAssociations ( ) { const associations = [ ] ; extensions . all . forEach ( ( extension ) => { const packageJSON = extension . packageJSON ; if ( packageJSON && packageJSON . contributes && packageJSON . contributes . yamlValidation ) { const yamlValidation = packageJSON . contributes . yamlValidation ; if ( Array . isArray ( yamlValidation ) ) { yamlValidation . forEach ( ( jv ) => { let { fileMatch , url } = jv ; if ( typeof fileMatch === 'string' ) { fileMatch = [ fileMatch ] ; } if ( Array . isArray ( fileMatch ) && typeof url === 'string' ) { let uri = url ; if ( uri [ 0 ] === '.' && uri [ 1 ] === '/' ) { uri = joinPath ( extension . extensionUri , uri ) . toString ( ) ; } fileMatch = fileMatch . map ( ( fm ) => { if ( fm [ 0 ] === '%' ) { fm = fm . replace ( /%APP_SETTINGS_HOME%/ , '/User' ) ; fm = fm . replace ( /%MACHINE_SETTINGS_HOME%/ , '/Machine' ) ; fm = fm . replace ( /%APP_WORKSPACES_HOME%/ , '/Workspaces' ) ; } else if ( ! fm . match ( /^(\w+:\/\/|\/|!)/ ) ) { fm = '/' + fm ; } return fm ; } ) ; associations . push ( { fileMatch , uri } ) ; } } ) ; } } } ) ; return associations ; } export function logToExtensionOutputChannel ( message ) { client . outputChannel . appendLine ( message ) ; }