import { forEach } from "lodash" ; import { createConnection , TextDocuments , TextDocumentSyncKind , ProposedFeatures , TextDocumentPositionParams , CompletionItem , InitializeParams , Hover , DidChangeConfigurationNotification , } from "vscode-languageserver" ; import { URI } from "vscode-uri" ; import { TextDocument } from "vscode-languageserver-textdocument" ; import { UI5SemanticModel } from "@ui5-language-assistant/semantic-model-types" ; import { clearSettings , setGlobalSettings , clearDocumentSettings , setSettingsForDocument , hasSettingsForDocument , getSettingsForDocument , } from "@ui5-language-assistant/settings" ; import { commands } from "@ui5-language-assistant/user-facing-text" ; import { ServerInitializationOptions } from "../api" ; import { getSemanticModel } from "./ui5-model" ; import { getCompletionItems } from "./completion-items" ; import { getXMLViewDiagnostics } from "./xml-view-diagnostics" ; import { getHoverResponse } from "./hover" ; import { getFlexEnabledFlagForXMLFile , isManifestDoc , initializeManifestData , updateManifestData , } from "./manifest-handling" ; import { diagnosticToCodeActionFix } from "./quick-fix" ; import { executeCommand } from "./commads" ; import { initSwa } from "./swa" ; import { getLogger , setLogLevel } from "./logger" ; const connection = createConnection ( ProposedFeatures . all ) ; const documents = new TextDocuments ( TextDocument ) ; let semanticModelLoaded = undefined ; let manifestStateInitialized = undefined ; let initializationOptions ; let hasConfigurationCapability = false ; connection . onInitialize ( ( params ) => { getLogger ( ) . info ( "`onInitialize` event" , params ) ; if ( params ?. initializationOptions ?. logLevel ) { setLogLevel ( params ?. initializationOptions ?. logLevel ) ; } initSwa ( params ) ; const capabilities = params . capabilities ; const workspaceFolderUri = params . rootUri ; if ( workspaceFolderUri !== null ) { const workspaceFolderAbsPath = URI . parse ( workspaceFolderUri ) . fsPath ; manifestStateInitialized = initializeManifestData ( workspaceFolderAbsPath ) ; } hasConfigurationCapability = capabilities . workspace !== undefined && ( capabilities . workspace . configuration ?? false ) ; initializationOptions = params . initializationOptions ; return { capabilities : { textDocumentSync : TextDocumentSyncKind . Full , completionProvider : { resolveProvider : true , triggerCharacters : [ '"' , "'" , ":" , "<" ] , } , hoverProvider : true , codeActionProvider : true , executeCommandProvider : { commands : [ commands . QUICK_FIX_STABLE_ID_ERROR . name , commands . QUICK_FIX_STABLE_ID_FILE_ERRORS . name , ] , } , } , } ; } ) ; connection . onInitialized ( async ( ) => { getLogger ( ) . info ( "`onInitialized` event" ) ; semanticModelLoaded = getSemanticModel ( initializationOptions ?. modelCachePath ) ; if ( hasConfigurationCapability ) { connection . client . register ( DidChangeConfigurationNotification . type , { section : "UI5LanguageAssistant" , } ) ; } } ) ; connection . onCompletion ( async ( textDocumentPosition ) : Promise < CompletionItem [ ] > => { if ( semanticModelLoaded !== undefined ) { getLogger ( ) . debug ( "`onCompletion` event" , { textDocumentPosition , } ) ; const model = await semanticModelLoaded ; const documentUri = textDocumentPosition . textDocument . uri ; const document = documents . get ( documentUri ) ; if ( document ) { ensureDocumentSettingsUpdated ( document . uri ) ; const documentSettings = await getSettingsForDocument ( document . uri ) ; const completionItems = getCompletionItems ( { model , textDocumentPosition , document , documentSettings , } ) ; getLogger ( ) . trace ( "computed completion items" , { completionItems } ) ; return completionItems ; } } return [ ] ; } ) ; connection . onCompletionResolve ( ( item ) : CompletionItem => { return item ; } ) ; connection . onHover ( async ( textDocumentPosition ) : Promise < Hover | undefined > => { if ( semanticModelLoaded !== undefined ) { getLogger ( ) . debug ( "`onHover` event" , { textDocumentPosition , } ) ; const model = await semanticModelLoaded ; const documentUri = textDocumentPosition . textDocument . uri ; const document = documents . get ( documentUri ) ; if ( document ) { const hoverResponse = getHoverResponse ( model , textDocumentPosition , document ) ; getLogger ( ) . trace ( "computed hoverResponse" , { hoverResponse , } ) ; return hoverResponse ; } } return undefined ; } ) ; connection . onDidChangeWatchedFiles ( async ( changeEvent ) => { getLogger ( ) . debug ( "`onDidChangeWatchedFiles` event" , { changeEvent } ) ; forEach ( changeEvent . changes , async ( change ) => { const uri = change . uri ; if ( ! isManifestDoc ( uri ) ) { return ; } await updateManifestData ( uri , change . type ) ; } ) ; } ) ; documents . onDidChangeContent ( async ( changeEvent ) => { getLogger ( ) . trace ( "`onDidChangeContent` event" , { changeEvent } ) ; if ( semanticModelLoaded === undefined || manifestStateInitialized === undefined || ! isXMLView ( changeEvent . document . uri ) ) { return ; } const ui5Model = await semanticModelLoaded ; await manifestStateInitialized ; const documentUri = changeEvent . document . uri ; const document = documents . get ( documentUri ) ; if ( document !== undefined ) { const documentPath = URI . parse ( documentUri ) . fsPath ; const flexEnabled = getFlexEnabledFlagForXMLFile ( documentPath ) ; const diagnostics = getXMLViewDiagnostics ( { document , ui5Model , flexEnabled , } ) ; getLogger ( ) . trace ( "computed diagnostics" , { diagnostics } ) ; connection . sendDiagnostics ( { uri : changeEvent . document . uri , diagnostics } ) ; } } ) ; connection . onCodeAction ( async ( params ) => { getLogger ( ) . debug ( "`onCodeAction` event" , { params } ) ; if ( semanticModelLoaded === undefined ) { return ; } const ui5Model = await semanticModelLoaded ; const docUri = params . textDocument . uri ; const textDocument = documents . get ( docUri ) ; if ( textDocument === undefined ) { return undefined ; } const diagnostics = params . context . diagnostics ; const codeActions = diagnosticToCodeActionFix ( textDocument , diagnostics , ui5Model ) ; getLogger ( ) . trace ( "`computed codeActions" , { codeActions } ) ; return codeActions ; } ) ; connection . onExecuteCommand ( async ( params ) => { getLogger ( ) . debug ( "`onExecuteCommand` event" , { params } ) ; executeCommand ( connection , params ) ; } ) ; function ensureDocumentSettingsUpdated ( resource ) { if ( ! hasConfigurationCapability ) { return ; } if ( ! hasSettingsForDocument ( resource ) ) { const result = connection . workspace . getConfiguration ( { scopeUri : resource , section : "UI5LanguageAssistant" , } ) ; getLogger ( ) . debug ( "updating settings for document" , { result } ) ; setSettingsForDocument ( resource , result ) ; } } connection . onDidChangeConfiguration ( ( change ) => { getLogger ( ) . debug ( "`onDidChangeConfiguration` event" ) ; if ( hasConfigurationCapability ) { getLogger ( ) . trace ( "Reset all cached document settings" ) ; clearSettings ( ) ; } else { if ( change . settings . UI5LanguageAssistant !== undefined ) { const ui5LangAssistSettings = change . settings . UI5LanguageAssistant ; getLogger ( ) . trace ( "Set global settings" , { ui5LangAssistSettings , } ) ; setGlobalSettings ( ui5LangAssistSettings ) ; } } setLogLevel ( change ?. settings ?. UI5LanguageAssistant ?. logging ?. level ) ; } ) ; documents . onDidClose ( ( textDocumentChangeEvent ) => { getLogger ( ) . debug ( "`onDidClose` event" , { textDocumentChangeEvent } ) ; clearDocumentSettings ( textDocumentChangeEvent . document . uri ) ; } ) ; documents . listen ( connection ) ; connection . listen ( ) ; function isXMLView ( uri ) { return /(view|fragment)\.xml$/ . test ( uri ) ; }