import * as LS from "vscode-languageserver/node" ; import * as PQLS from "@microsoft/powerquery-language-services" ; import * as PQP from "@microsoft/powerquery-parser" ; import { TextDocument } from "vscode-languageserver-textdocument" ; import { formatError } from "./errorUtils" ; import { StandardLibraryUtils } from "./standardLibrary" ; const LanguageId = "powerquery" ; interface ServerSettings { checkForDuplicateIdentifiers : boolean ; checkInvokeExpressions : boolean ; locale : string ; maintainWorkspaceCache : boolean ; } const defaultServerSettings = { checkForDuplicateIdentifiers : true , checkInvokeExpressions : false , locale : PQP . DefaultLocale , maintainWorkspaceCache : true , } ; const connection = LS . createConnection ( LS . ProposedFeatures . all ) ; const documents = new LS . TextDocuments ( TextDocument ) ; let serverSettings = defaultServerSettings ; let hasConfigurationCapability = false ; connection . onInitialize ( ( params ) => { const capabilities = { textDocumentSync : LS . TextDocumentSyncKind . Incremental , documentFormattingProvider : true , completionProvider : { resolveProvider : false , } , documentSymbolProvider : { workDoneProgress : false , } , hoverProvider : true , signatureHelpProvider : { triggerCharacters : [ "(" , "," ] , } , } ; hasConfigurationCapability = Boolean ( params . capabilities . workspace ?. configuration ) ; return { capabilities , } ; } ) ; connection . onInitialized ( async ( ) => { if ( hasConfigurationCapability ) { connection . client . register ( LS . DidChangeConfigurationNotification . type , undefined ) ; } serverSettings = await fetchConfigurationSettings ( ) ; } ) ; connection . onDidChangeConfiguration ( async ( ) => { serverSettings = await fetchConfigurationSettings ( ) ; documents . all ( ) . forEach ( validateDocument ) ; } ) ; documents . onDidClose ( ( event ) => { connection . sendDiagnostics ( { uri : event . document . uri , version : event . document . version , diagnostics : [ ] , } ) ; PQLS . documentClosed ( event . document ) ; } ) ; documents . onDidChangeContent ( ( event ) => { PQLS . documentClosed ( event . document ) ; validateDocument ( event . document ) . catch ( ( err ) => connection . console . error ( `validateDocument err: ${ formatError ( assertAsError ( err ) ) }` ) ) ; } ) ; async function validateDocument ( document ) { const result = PQLS . validate ( document , createValidationSettings ( getLocalizedStandardLibrary ( ) ) ) ; connection . sendDiagnostics ( { uri : document . uri , version : document . version , diagnostics : result . diagnostics , } ) ; } connection . onDocumentFormatting ( ( documentfomattingParams ) : LS . TextEdit [ ] => { const maybeDocument = documents . get ( documentfomattingParams . textDocument . uri ) ; if ( maybeDocument === undefined ) { return [ ] ; } const document = maybeDocument ; try { return PQLS . tryFormat ( document , documentfomattingParams . options , serverSettings . locale ) ; } catch ( err ) { const error = err as Error ; const errorMessage = error . message ; let userMessage ; if ( errorMessage ) { userMessage = errorMessage ; } else { userMessage = "An unknown error occured during formatting." ; } connection . window . showErrorMessage ( userMessage ) ; return [ ] ; } } ) ; connection . onCompletion ( async ( textDocumentPosition , _token ) : Promise < LS . CompletionItem [ ] > => { const document = documents . get ( textDocumentPosition . textDocument . uri ) ; if ( document ) { const analysis = createAnalysis ( document , textDocumentPosition . position ) ; return analysis . getAutocompleteItems ( ) . catch ( ( err ) => { connection . console . error ( `onCompletion error ${ formatError ( assertAsError ( err ) ) }` ) ; return [ ] ; } ) ; } return [ ] ; } ) ; connection . onDocumentSymbol ( async ( documentSymbolParams , _token ) : Promise < LS . DocumentSymbol [ ] | undefined > => { const document = documents . get ( documentSymbolParams . textDocument . uri ) ; if ( document ) { return PQLS . getDocumentSymbols ( document , PQP . DefaultSettings , serverSettings . maintainWorkspaceCache ) ; } return undefined ; } ) ; connection . onHover ( async ( textDocumentPosition , _token ) : Promise < LS . Hover > => { const emptyHover = { range : undefined , contents : [ ] , } ; const document = documents . get ( textDocumentPosition . textDocument . uri ) ; if ( document === undefined ) { return emptyHover ; } const analysis = createAnalysis ( document , textDocumentPosition . position ) ; return analysis . getHover ( ) . catch ( err => { connection . console . error ( `onHover error ${ formatError ( err ) }` ) ; return emptyHover ; } ) ; } ) ; connection . onSignatureHelp ( async ( textDocumentPosition , _token ) : Promise < LS . SignatureHelp > => { const emptySignatureHelp = { signatures : [ ] , activeParameter : null , activeSignature : 0 , } ; const document = documents . get ( textDocumentPosition . textDocument . uri ) ; if ( document ) { const analysis = createAnalysis ( document , textDocumentPosition . position ) ; return analysis . getSignatureHelp ( ) . catch ( ( err ) => { connection . console . error ( `onSignatureHelp error ${ formatError ( assertAsError ( err ) ) }` ) ; return emptySignatureHelp ; } ) ; } return emptySignatureHelp ; } ) ; documents . listen ( connection ) ; connection . listen ( ) ; function createAnalysis ( document , position ) { const localizedStandardLibrary = getLocalizedStandardLibrary ( ) ; return PQLS . AnalysisUtils . createAnalysis ( document , createAnalysisSettings ( localizedStandardLibrary ) , position ) ; } function createAnalysisSettings ( library ) { return { createInspectionSettingsFn : ( ) => createInspectionSettings ( library ) , library , maintainWorkspaceCache : serverSettings . maintainWorkspaceCache , } ; } function getLocalizedStandardLibrary ( ) { return StandardLibraryUtils . getOrCreateStandardLibrary ( serverSettings . locale ) ; } function createInspectionSettings ( library ) { return PQLS . InspectionUtils . createInspectionSettings ( { ... PQP . DefaultSettings , locale : serverSettings . locale , } , library . externalTypeResolver ) ; } function createValidationSettings ( library ) { return PQLS . ValidationSettingsUtils . createValidationSettings ( createInspectionSettings ( library ) , LanguageId , serverSettings . checkForDuplicateIdentifiers , serverSettings . checkInvokeExpressions ) ; } async function fetchConfigurationSettings ( ) { if ( ! hasConfigurationCapability ) { return defaultServerSettings ; } const config = await connection . workspace . getConfiguration ( { section : "powerquery" } ) ; return { checkForDuplicateIdentifiers : true , checkInvokeExpressions : config ?. diagnostics ?. experimental ?? false , locale : config ?. general ?. locale ?? PQP . DefaultLocale , maintainWorkspaceCache : true , } ; } function assertAsError < T > ( value ) { if ( value instanceof Error ) { return value ; } throw new Error ( `received an error value that isn't an instanceof Error` ) ; }