import { workspace , window , languages , commands , StatusBarAlignment , DocumentSelector , ExtensionContext , ProgressLocation , Location , Range , Uri } from 'vscode' ; import { LanguageClient , ServerOptions , TransportKind , LanguageClientOptions } from 'vscode-languageclient/node' ; import * as path from 'path' ; import { SystemVerilogDefinitionProvider } from './providers/DefinitionProvider' ; import { SystemVerilogDocumentSymbolProvider } from './providers/DocumentSymbolProvider' ; import { SystemVerilogFormatProvider } from './providers/FormatProvider' ; import { SystemVerilogHoverProvider } from './providers/HoverProvider' ; import { SystemVerilogWorkspaceSymbolProvider } from './providers/WorkspaceSymbolProvider' ; import { SystemVerilogModuleInstantiator } from './providers/ModuleInstantiator' ; import { SystemVerilogParser } from './parser' ; import { SystemVerilogIndexer } from './indexer' ; import { SystemVerilogSymbol } from './symbol' ; let client ; let closeWindowProgress = true ; const selector = [ { scheme : 'file' , language : 'systemverilog' } , { scheme : 'file' , language : 'verilog' } ] ; export function activate ( context ) { const outputChannel = window . createOutputChannel ( 'SystemVerilog' ) ; const statusBar = window . createStatusBarItem ( StatusBarAlignment . Left , 0 ) ; statusBar . text = 'SystemVerilog: Activating' ; statusBar . show ( ) ; statusBar . command = 'systemverilog.build_index' ; const parser = new SystemVerilogParser ( ) ; const indexer = new SystemVerilogIndexer ( statusBar , parser , outputChannel ) ; const docProvider = new SystemVerilogDocumentSymbolProvider ( parser ) ; const symProvider = new SystemVerilogWorkspaceSymbolProvider ( indexer ) ; const defProvider = new SystemVerilogDefinitionProvider ( ) ; const hoverProvider = new SystemVerilogHoverProvider ( ) ; const moduleInstantiator = new SystemVerilogModuleInstantiator ( ) ; const formatProvider = new SystemVerilogFormatProvider ( outputChannel ) ; context . subscriptions . push ( statusBar ) ; context . subscriptions . push ( languages . registerDocumentSymbolProvider ( selector , docProvider ) ) ; context . subscriptions . push ( languages . registerDefinitionProvider ( selector , defProvider ) ) ; context . subscriptions . push ( languages . registerHoverProvider ( selector , hoverProvider ) ) ; context . subscriptions . push ( languages . registerWorkspaceSymbolProvider ( symProvider ) ) ; context . subscriptions . push ( languages . registerDocumentRangeFormattingEditProvider ( selector , formatProvider ) ) ; context . subscriptions . push ( languages . registerDocumentFormattingEditProvider ( selector , formatProvider ) ) ; const build_handler = ( ) => { indexer . build_index ( ) . then ( ( _ ) => saveIndex ( ) ) ; } ; const instantiate_handler = ( ) => { moduleInstantiator . instantiateModule ( ) ; } ; context . subscriptions . push ( commands . registerCommand ( 'systemverilog.build_index' , build_handler ) ) ; context . subscriptions . push ( commands . registerCommand ( 'systemverilog.auto_instantiate' , instantiate_handler ) ) ; context . subscriptions . push ( commands . registerCommand ( 'systemverilog.compile' , compileOpenedDocument ) ) ; context . subscriptions . push ( workspace . onDidSaveTextDocument ( ( doc ) => { indexer . onChange ( doc ) ; saveIndex ( ) ; } ) ) ; context . subscriptions . push ( window . onDidChangeActiveTextEditor ( ( editor ) => { if ( editor !== undefined ) { indexer . onChange ( editor . document ) ; saveIndex ( ) ; } } ) ) ; const watcher = workspace . createFileSystemWatcher ( '**/*.{sv,v,svh,vh}' , false , false , false ) ; context . subscriptions . push ( watcher . onDidCreate ( ( uri ) => { indexer . onCreate ( uri ) ; saveIndex ( ) ; } ) ) ; context . subscriptions . push ( watcher . onDidDelete ( ( uri ) => { indexer . onDelete ( uri ) ; saveIndex ( ) ; } ) ) ; context . subscriptions . push ( watcher . onDidChange ( ( uri ) => { indexer . onDelete ( uri ) ; saveIndex ( ) ; } ) ) ; context . subscriptions . push ( watcher ) ; const settings = workspace . getConfiguration ( ) ; try { loadIndex ( ) ; } catch ( error ) { if ( settings . get ( 'systemverilog.disableIndexing' ) ) { statusBar . text = 'SystemVerilog: Indexing disabled on boot' ; } else { commands . executeCommand ( 'systemverilog.build_index' ) ; } } function saveIndex ( ) { const syms = [ ... indexer . symbols ] ; context . workspaceState . update ( 'symbols' , syms ) ; } function loadIndex ( ) { const symbols = context . workspaceState . get ( 'symbols' ) ; let num_symbols = 0 ; if ( symbols ) { symbols . forEach ( ( entry ) => { const syms = new Array < SystemVerilogSymbol > ( ) ; entry [ 1 ] . forEach ( ( s ) => { syms . push ( new SystemVerilogSymbol ( s . name , s . type , s . containerName , new Location ( Uri . file ( entry [ 0 ] ) , new Range ( s . location . range [ 0 ] . line , s . location . range [ 0 ] . character , s . location . range [ 1 ] . line , s . location . range [ 1 ] . character ) ) ) ) ; num_symbols += 1 ; } ) ; indexer . symbols . set ( entry [ 0 ] , syms ) ; indexer . symbolsCount = num_symbols ; } ) ; statusBar . text = `SystemVerilog: ${ num_symbols } indexed objects` ; } else { throw new Error ( 'Could not load index' ) ; } } /**
        Sends a notification to the LSP to compile the opened document.
        Keeps the `Progress` window opened until `extensionLanguageClient.closeWindowProgress` is set to true or
        the interval iterations count reaches the maximum value.
        `closeWindowProgress` is updated to true when a notification is sent to the client from LSP.
    */ function compileOpenedDocument ( ) { const { document } = window . activeTextEditor ; closeWindowProgress = false ; if ( ! document ) { window . showErrorMessage ( 'There is no open document!' ) ; return ; } Promise . resolve ( window . withProgress ( { location : ProgressLocation . Notification , title : 'SystemVerilog document compiling...' , cancellable : true } , async ( _progress , _token ) => { client . sendNotification ( 'compileOpenedDocument' , document . uri . toString ( ) ) ; let intervalCount = 0 ; const interval = setInterval ( ( ) => { if ( closeWindowProgress || intervalCount > 5 ) { clearInterval ( interval ) ; } intervalCount += 1 ; } , 500 ) ; } ) ) . catch ( ( error ) => { outputChannel . appendLine ( error ) ; window . showErrorMessage ( error ) ; } ) ; } /** Starts the `LanguageClient` */ const serverModule = context . asAbsolutePath ( path . join ( 'dist' , 'server' , 'server.js' ) ) ; const serverOptions = { run : { module : serverModule , transport : TransportKind . ipc } , debug : { module : serverModule , transport : TransportKind . ipc , options : { execArgv : [ '--nolazy' , '--inspect=6009' ] } } } ; const clientOptions = { documentSelector : selector as string [ ] } ; client = new LanguageClient ( 'systemverilog' , 'SystemVerilog Language Server' , serverOptions , clientOptions ) ; client . start ( ) ; client . onReady ( ) . then ( ( ) => { try { client . sendNotification ( 'workspaceRootPath' , workspace . workspaceFolders [ 0 ] . uri . fsPath ) ; } catch { } client . onNotification ( 'closeWindowProgress' , ( ) => { closeWindowProgress = true ; } ) ; workspace . onDidChangeConfiguration ( ( ) => { client . sendNotification ( 'onDidChangeConfiguration' ) ; } ) ; } ) ; } export function deactivate ( ) { if ( ! client ) { return undefined ; } return client . stop ( ) ; }