import { createConnection , CompletionItem , DocumentSymbolParams , Hover , HoverParams , InitializeParams , InitializeResult , Location , MarkupKind , ProposedFeatures , Range , SignatureHelp , SymbolInformation , TextDocuments , TextDocumentPositionParams , TextDocumentSyncKind , WorkspaceSymbolParams } from 'vscode-languageserver' ; import { TextDocument } from 'vscode-languageserver-textdocument' ; import { SystemVerilogIndexer } from './svindexer' ; import { SystemVerilogDefinitionProvider } from './svdefprovider' ; import { VerilatorDiagnostics } from './verilator' ; import { SystemVerilogCompleter } from './svcompleter' ; import { SystemVerilogSignatureHelpProvider } from './svsignhelpprovider' ; import { SystemVerilogFormatter } from './svformatter' ; import { ConnectionLogger , fsReadFile , isStringListEqual , uriToPath } from './genutils' ; import { default_settings } from './svutils' ; const BuildIndexCommand = "systemverilog.build_index" ; let connection = createConnection ( ProposedFeatures . all ) ; ConnectionLogger . setConnection ( connection ) ; let clientName ; let hasConfigurationCapability = false ; let documents = new TextDocuments ( TextDocument ) ; let svindexer = new SystemVerilogIndexer ( ) ; let svdefprovider = new SystemVerilogDefinitionProvider ( svindexer ) ; let verilator = new VerilatorDiagnostics ( svindexer ) ; let svcompleter = new SystemVerilogCompleter ( svindexer ) ; let svsignhelper = new SystemVerilogSignatureHelpProvider ( svindexer ) ; let svformatter = new SystemVerilogFormatter ( ) ; let settings = default_settings ; connection . onInitialize ( ( params ) => { hasConfigurationCapability = ! ! ( params . capabilities . workspace && ! ! params . capabilities . workspace . configuration ) ; clientName = ! ! params . clientInfo ? params . clientInfo . name : undefined ; try { svindexer . setRoot ( uriToPath ( params . rootUri ) ) ; if ( clientName . startsWith ( "vscode" ) ) { svindexer . setClientDir ( ".vscode" ) ; } else if ( clientName . startsWith ( "coc.nvim" ) ) { svindexer . setClientDir ( ".vim" ) ; } else if ( clientName . startsWith ( "Sublime" ) ) { svindexer . setClientDir ( ".sublime" ) ; } else if ( clientName . startsWith ( "emacs" ) ) { svindexer . setClientDir ( ".emacs" ) ; } else { svindexer . setClientDir ( ".svlangserver" ) ; } verilator . setOptionsFile ( svindexer . getVerilatorOptionsFile ( ) ) ; } catch ( error ) { ConnectionLogger . error ( error ) ; } const result = { capabilities : { textDocumentSync : TextDocumentSyncKind . Incremental , completionProvider : { resolveProvider : true } , documentSymbolProvider : true , workspaceSymbolProvider : true , definitionProvider : true , hoverProvider : true , signatureHelpProvider : { triggerCharacters : [ '(' , '[' , ',' ] , retriggerCharacters : [ ',' ] } , executeCommandProvider : { commands : [ BuildIndexCommand , ] } , documentFormattingProvider : true , documentRangeFormattingProvider : true , } } ; return result ; } ) ; function getCurrentSettings ( ) { let currentSettings = new Object ( ) ; settings . forEach ( ( val , prop ) => { currentSettings [ prop ] = val ; } ) ; return { settings : currentSettings } ; } function getSettings ( ) { if ( ! hasConfigurationCapability ) { return Promise . resolve ( getCurrentSettings ( ) ) ; } else if ( clientName == "coc.nvim" ) { return connection . workspace . getConfiguration ( ) . then ( svSettings => { let initSettings = new Object ( ) ; settings . forEach ( ( val , prop ) => { initSettings [ prop ] = svSettings [ prop ] == undefined ? settings [ prop ] : svSettings [ prop ] ; } ) ; return { settings : initSettings } ; } ) . catch ( error => { ConnectionLogger . error ( error ) ; return getCurrentSettings ( ) ; } ) ; } else { return connection . workspace . getConfiguration ( { section : 'systemverilog' } ) . then ( svSettings => { let initSettings = new Object ( ) ; settings . forEach ( ( val , prop ) => { let nprop = prop . substring ( "systemverilog." . length ) ; initSettings [ prop ] = svSettings [ nprop ] == undefined ? settings [ prop ] : svSettings [ nprop ] ; } ) ; return { settings : initSettings } ; } ) . catch ( error => { ConnectionLogger . error ( error ) ; return getCurrentSettings ( ) ; } ) ; } } function updateSettings ( change , forceUpdate = false ) { let oldSettings = new Map < string , any > ( ) ; for ( let [ prop , val ] of settings . entries ( ) ) { oldSettings . set ( prop , val ) ; if ( change . settings [ prop ] == undefined ) { let hierParts = prop . split ( "." ) ; let newVal = change . settings ; for ( let i = 0 ; i < hierParts . length ; i ++ ) { newVal = newVal [ hierParts [ i ] ] ; if ( newVal == undefined ) { break ; } } settings . set ( prop , newVal == undefined ? val : newVal ) ; } else { settings . set ( prop , change . settings [ prop ] ) ; } } for ( let [ prop , val ] of settings . entries ( ) ) { ConnectionLogger . log ( `INFO: settings[${ prop }] = ${ settings . get ( prop ) }` ) ; } let definesChanged = forceUpdate || ! isStringListEqual ( oldSettings . get ( "systemverilog.defines" ) , settings . get ( "systemverilog.defines" ) ) ; if ( forceUpdate || definesChanged || ! isStringListEqual ( oldSettings . get ( "systemverilog.includeIndexing" ) , settings . get ( "systemverilog.includeIndexing" ) ) || ! isStringListEqual ( oldSettings . get ( "systemverilog.libraryIndexing" ) , settings . get ( "systemverilog.libraryIndexing" ) ) || ! isStringListEqual ( oldSettings . get ( "systemverilog.excludeIndexing" ) , settings . get ( "systemverilog.excludeIndexing" ) ) ) { if ( definesChanged ) { svindexer . setDefines ( settings . get ( "systemverilog.defines" ) ) ; } svindexer . setLibraries ( settings . get ( "systemverilog.libraryIndexing" ) , settings . get ( "systemverilog.excludeIndexing" ) ) ; svindexer . index ( settings . get ( "systemverilog.includeIndexing" ) , settings . get ( "systemverilog.excludeIndexing" ) ) ; } verilator . setCommand ( settings . get ( "systemverilog.launchConfiguration" ) ) ; verilator . setDefines ( settings . get ( "systemverilog.defines" ) ) ; svformatter . setCommand ( settings . get ( "systemverilog.formatCommand" ) ) ; } connection . onInitialized ( ( ) => { try { getSettings ( ) . then ( initSettings => updateSettings ( initSettings , true ) ) . catch ( error => { ConnectionLogger . error ( error ) ; } ) ; } catch ( error ) { ConnectionLogger . error ( error ) ; } } ) ; connection . onDidChangeConfiguration ( change => { try { updateSettings ( change ) ; } catch ( error ) { ConnectionLogger . error ( error ) ; } } ) ; documents . onDidChangeContent ( change => { try { svindexer . processDocumentChanges ( change . document ) ; if ( settings . get ( "systemverilog.lintOnUnsaved" ) ) { lintDocument ( change . document . uri , change . document . getText ( ) ) ; } } catch ( error ) { ConnectionLogger . error ( error ) ; } } ) ; connection . onCompletion ( ( _textDocumentPosition ) : CompletionItem [ ] => { try { if ( settings . get ( "systemverilog.disableCompletionProvider" ) ) { return [ ] ; } return svcompleter . completionItems ( documents . get ( _textDocumentPosition . textDocument . uri ) , _textDocumentPosition . position ) ; } catch ( error ) { ConnectionLogger . error ( error ) ; return [ ] ; } } ) ; connection . onCompletionResolve ( ( item ) : CompletionItem => { return item ; } ) ; connection . onDocumentSymbol ( ( documentSymbolParams ) : Promise < SymbolInformation [ ] > => { try { return svindexer . getDocumentSymbols ( documents . get ( documentSymbolParams . textDocument . uri ) ) ; } catch ( error ) { ConnectionLogger . error ( error ) ; return Promise . resolve ( [ ] ) ; } } ) ; connection . onWorkspaceSymbol ( ( workspaceSymbolParams ) : Promise < SymbolInformation [ ] > => { try { return svindexer . getWorkspaceSymbols ( workspaceSymbolParams . query ) ; } catch ( error ) { ConnectionLogger . error ( error ) ; return Promise . resolve ( [ ] ) ; } } ) ; connection . onDefinition ( ( textDocumentPosition ) : Promise < Location [ ] > => { try { return svdefprovider . getDefinitionSymbolLocation ( documents . get ( textDocumentPosition . textDocument . uri ) , textDocumentPosition . position ) ; } catch ( error ) { ConnectionLogger . error ( error ) ; return Promise . resolve ( [ ] ) ; } } ) ; connection . onHover ( ( hoverParams ) : Promise < Hover > => { try { if ( settings . get ( "systemverilog.disableHoverProvider" ) ) { return Promise . resolve ( undefined ) ; } let defText = svdefprovider . getDefinitionText ( documents . get ( hoverParams . textDocument . uri ) , hoverParams . position ) ; if ( defText == undefined ) { return Promise . resolve ( undefined ) ; } return Promise . resolve ( { contents : { kind : MarkupKind . Markdown , value : [ "```" ] . concat ( defText . split ( /\r?\n/ ) . map ( s => "    " + s . trim ( ) ) ) . concat ( [ "```" ] ) . join ( '\n' ) , } , } ) ; } catch ( error ) { ConnectionLogger . error ( error ) ; return Promise . resolve ( undefined ) ; } } ) ; function lintDocument ( uri , text ? ) { if ( settings . get ( "systemverilog.disableLinting" ) ) { return ; } verilator . lint ( uriToPath ( uri ) , text ) . then ( ( diagnostics ) => { connection . sendDiagnostics ( { uri : uri , diagnostics } ) ; } ) . catch ( ( error ) => { ConnectionLogger . error ( error ) ; } ) ; } documents . onDidOpen ( ( event ) => { try { svindexer . indexOpenDocument ( event . document ) ; lintDocument ( event . document . uri ) ; } catch ( error ) { ConnectionLogger . error ( error ) ; } } ) ; documents . onDidSave ( ( event ) => { try { svindexer . indexOpenDocument ( event . document ) ; svindexer . updateFileInfoOnSave ( event . document ) ; lintDocument ( event . document . uri ) ; } catch ( error ) { ConnectionLogger . error ( error ) ; } } ) ; connection . onSignatureHelp ( ( textDocumentPosition ) : SignatureHelp => { try { if ( settings . get ( "systemverilog.disableSignatureHelpProvider" ) ) { return undefined ; } return svsignhelper . getSignatures ( documents . get ( textDocumentPosition . textDocument . uri ) , textDocumentPosition . position . line , textDocumentPosition . position . character ) ; } catch ( error ) { ConnectionLogger . error ( error ) ; return undefined ; } } ) ; connection . onExecuteCommand ( ( commandParams ) => { try { if ( commandParams . command == BuildIndexCommand ) { svindexer . setLibraries ( settings . get ( "systemverilog.libraryIndexing" ) , settings . get ( "systemverilog.excludeIndexing" ) ) ; svindexer . index ( settings . get ( "systemverilog.includeIndexing" ) , settings . get ( "systemverilog.excludeIndexing" ) ) ; } else { throw new Error ( `Unhandled command ${ commandParams . command }` ) ; } } catch ( error ) { ConnectionLogger . error ( error ) ; } } ) ; connection . onDocumentFormatting ( ( formatParams ) => { try { return svformatter . format ( documents . get ( formatParams . textDocument . uri ) , null , formatParams . options ) ; } catch ( error ) { ConnectionLogger . error ( error ) ; return Promise . resolve ( [ ] ) ; } } ) ; connection . onDocumentRangeFormatting ( ( rangeFormatParams ) => { try { return svformatter . format ( documents . get ( rangeFormatParams . textDocument . uri ) , rangeFormatParams . range , rangeFormatParams . options ) ; } catch ( error ) { ConnectionLogger . error ( error ) ; return Promise . resolve ( [ ] ) ; } } ) ; connection . onShutdown ( ( token ) => { verilator . cleanupTmpFiles ( ) ; svindexer . saveIndexOnExit ( ) ; } ) ; connection . onExit ( ( ) => { verilator . cleanupTmpFiles ( ) ; svindexer . saveIndexOnExit ( ) ; } ) ; process . on ( 'exit' , ( ) => { verilator . cleanupTmpFiles ( ) ; svindexer . saveIndexOnExit ( ) ; } ) ; process . on ( 'SIGTERM' , ( ) => { verilator . cleanupTmpFiles ( ) ; svindexer . saveIndexOnExit ( ) ; } ) ; documents . listen ( connection ) ; connection . listen ( ) ;