'use strict' ; import { createConnection , IConnection , TextDocuments , TextDocument , InitializeParams , InitializeResult , NotificationType , RequestType } from 'vscode-languageserver' ; import { xhr , XHRResponse , configure as configureHttpRequests , getErrorStatusDescription } from 'request-light' ; import path = require ( 'path' ) ; import fs = require ( 'fs' ) ; import URI from './utils/uri' ; import * as URL from 'url' ; import Strings = require ( './utils/strings' ) ; import { JSONDocument , JSONSchema , LanguageSettings , getLanguageService } from 'vscode-json-languageservice' ; import { ProjectJSONContribution } from './jsoncontributions/projectJSONContribution' ; import { GlobPatternContribution } from './jsoncontributions/globPatternContribution' ; import { FileAssociationContribution } from './jsoncontributions/fileAssociationContribution' ; import { getLanguageModelCache } from './languageModelCache' ; import * as nls from 'vscode-nls' ; nls . config ( process . env [ 'VSCODE_NLS_CONFIG' ] ) ; interface ISchemaAssociations { [ pattern ] : string [ ] ; } namespace SchemaAssociationNotification { export const type = { get method ( ) { return 'json/schemaAssociations' ; } } ; } namespace VSCodeContentRequest { export const type = { get method ( ) { return 'vscode/content' ; } } ; } let connection = createConnection ( ) ; console . log = connection . console . log . bind ( connection . console ) ; console . error = connection . console . error . bind ( connection . console ) ; let documents = new TextDocuments ( ) ; documents . listen ( connection ) ; const filesAssociationContribution = new FileAssociationContribution ( ) ; let workspaceRoot ; connection . onInitialize ( ( params ) : InitializeResult => { workspaceRoot = URI . parse ( params . rootPath ) ; filesAssociationContribution . setLanguageIds ( params . initializationOptions . languageIds ) ; return { capabilities : { textDocumentSync : documents . syncKind , completionProvider : { resolveProvider : true , triggerCharacters : [ '"' , ':' ] } , hoverProvider : true , documentSymbolProvider : true , documentRangeFormattingProvider : true , documentFormattingProvider : true } } ; } ) ; let workspaceContext = { resolveRelativePath : ( relativePath , resource ) => { return URL . resolve ( resource , relativePath ) ; } } ; let schemaRequestService = ( uri ) : Thenable < string > => { if ( Strings . startsWith ( uri , 'file://' ) ) { let fsPath = URI . parse ( uri ) . fsPath ; return new Promise < string > ( ( c , e ) => { fs . readFile ( fsPath , 'UTF-8' , ( err , result ) => { err ? e ( '' ) : c ( result . toString ( ) ) ; } ) ; } ) ; } else if ( Strings . startsWith ( uri , 'vscode://' ) ) { return connection . sendRequest ( VSCodeContentRequest . type , uri ) . then ( responseText => { return responseText ; } , error => { return error . message ; } ) ; } if ( uri . indexOf ( '//schema.management.azure.com/' ) !== - 1 ) { connection . telemetry . logEvent ( { key : 'json.schema' , value : { schemaURL : uri } } ) ; } return xhr ( { url : uri , followRedirects : 5 } ) . then ( response => { return response . responseText ; } , ( error ) => { return Promise . reject ( error . responseText || getErrorStatusDescription ( error . status ) || error . toString ( ) ) ; } ) ; } ; let languageService = getLanguageService ( { schemaRequestService , workspaceContext , contributions : [ new ProjectJSONContribution ( ) , new GlobPatternContribution ( ) , filesAssociationContribution ] } ) ; interface Settings { json : { schemas : JSONSchemaSettings [ ] ; } ; http : { proxy : string ; proxyStrictSSL : boolean ; } ; } interface JSONSchemaSettings { fileMatch ? : string [ ] ; url ? : string ; schema ? : JSONSchema ; } let jsonConfigurationSettings = void 0 ; let schemaAssociations = void 0 ; connection . onDidChangeConfiguration ( ( change ) => { var settings = < Settings > change . settings ; configureHttpRequests ( settings . http && settings . http . proxy , settings . http && settings . http . proxyStrictSSL ) ; jsonConfigurationSettings = settings . json && settings . json . schemas ; updateConfiguration ( ) ; } ) ; connection . onNotification ( SchemaAssociationNotification . type , associations => { schemaAssociations = associations ; updateConfiguration ( ) ; } ) ; function updateConfiguration ( ) { let languageSettings = { validate : true , allowComments : true , schemas : [ ] } ; if ( schemaAssociations ) { for ( var pattern in schemaAssociations ) { let association = schemaAssociations [ pattern ] ; if ( Array . isArray ( association ) ) { association . forEach ( uri => { languageSettings . schemas . push ( { uri , fileMatch : [ pattern ] } ) ; } ) ; } } } if ( jsonConfigurationSettings ) { jsonConfigurationSettings . forEach ( schema => { let uri = schema . url ; if ( ! uri && schema . schema ) { uri = schema . schema . id ; } if ( ! uri && schema . fileMatch ) { uri = 'vscode://schemas/custom/' + encodeURIComponent ( schema . fileMatch . join ( '&' ) ) ; } if ( uri ) { if ( uri [ 0 ] === '.' && workspaceRoot ) { uri = URI . file ( path . normalize ( path . join ( workspaceRoot . fsPath , uri ) ) ) . toString ( ) ; } languageSettings . schemas . push ( { uri , fileMatch : schema . fileMatch , schema : schema . schema } ) ; } } ) ; } languageService . configure ( languageSettings ) ; documents . all ( ) . forEach ( triggerValidation ) ; } documents . onDidChangeContent ( ( change ) => { triggerValidation ( change . document ) ; } ) ; documents . onDidClose ( event => { cleanPendingValidation ( event . document ) ; connection . sendDiagnostics ( { uri : event . document . uri , diagnostics : [ ] } ) ; } ) ; let pendingValidationRequests = { } ; const validationDelayMs = 200 ; function cleanPendingValidation ( textDocument ) { let request = pendingValidationRequests [ textDocument . uri ] ; if ( request ) { clearTimeout ( request ) ; delete pendingValidationRequests [ textDocument . uri ] ; } } function triggerValidation ( textDocument ) { cleanPendingValidation ( textDocument ) ; pendingValidationRequests [ textDocument . uri ] = setTimeout ( ( ) => { delete pendingValidationRequests [ textDocument . uri ] ; validateTextDocument ( textDocument ) ; } , validationDelayMs ) ; } function validateTextDocument ( textDocument ) { if ( textDocument . getText ( ) . length === 0 ) { connection . sendDiagnostics ( { uri : textDocument . uri , diagnostics : [ ] } ) ; return ; } let jsonDocument = getJSONDocument ( textDocument ) ; languageService . doValidation ( textDocument , jsonDocument ) . then ( diagnostics => { connection . sendDiagnostics ( { uri : textDocument . uri , diagnostics } ) ; } ) ; } connection . onDidChangeWatchedFiles ( ( change ) => { let hasChanges = false ; change . changes . forEach ( c => { if ( languageService . resetSchema ( c . uri ) ) { hasChanges = true ; } } ) ; if ( hasChanges ) { documents . all ( ) . forEach ( validateTextDocument ) ; } } ) ; let jsonDocuments = getLanguageModelCache < JSONDocument > ( 10 , 60 , document => languageService . parseJSONDocument ( document ) ) ; function getJSONDocument ( document ) { return jsonDocuments . get ( document ) ; } connection . onCompletion ( textDocumentPosition => { let document = documents . get ( textDocumentPosition . textDocument . uri ) ; let jsonDocument = getJSONDocument ( document ) ; return languageService . doComplete ( document , textDocumentPosition . position , jsonDocument ) ; } ) ; connection . onCompletionResolve ( completionItem => { return languageService . doResolve ( completionItem ) ; } ) ; connection . onHover ( textDocumentPositionParams => { let document = documents . get ( textDocumentPositionParams . textDocument . uri ) ; let jsonDocument = getJSONDocument ( document ) ; return languageService . doHover ( document , textDocumentPositionParams . position , jsonDocument ) ; } ) ; connection . onDocumentSymbol ( documentSymbolParams => { let document = documents . get ( documentSymbolParams . textDocument . uri ) ; let jsonDocument = getJSONDocument ( document ) ; return languageService . findDocumentSymbols ( document , jsonDocument ) ; } ) ; connection . onDocumentFormatting ( formatParams => { let document = documents . get ( formatParams . textDocument . uri ) ; return languageService . format ( document , null , formatParams . options ) ; } ) ; connection . onDocumentRangeFormatting ( formatParams => { let document = documents . get ( formatParams . textDocument . uri ) ; return languageService . format ( document , formatParams . range , formatParams . options ) ; } ) ; connection . listen ( ) ;