import { Connection , Diagnostic , TextDocuments , Range , DiagnosticSeverity } from 'vscode-languageserver/node' ; import { TextDocument } from 'vscode-languageserver-textdocument' ; import * as path from 'path' ; import * as child from 'child_process' ; import { getPathFromUri } from '../utils/common' ; import { isSystemVerilogDocument , isVerilogDocument , getLineRange } from '../utils/server' ; import { DiagnosticData } from './DiagnosticData' ; export abstract class DocumentCompiler { public connection : Connection ; public documents : TextDocuments < TextDocument > ; public workspaceRootPath : string ; public configurations : Map < string , any > ; public compilerConfigurationsKeys : string [ ] ; constructor ( connection , documents , workspaceRootPath , configurations , compilerConfigurationsKeys ) { this . connection = connection ; this . documents = documents ; this . workspaceRootPath = workspaceRootPath ; this . configurations = configurations ; this . compilerConfigurationsKeys = compilerConfigurationsKeys ; } param document returns public getTextDocumentDiagnostics ( document ) { return new Promise ( ( resolve , reject ) => { if ( ! document ) { reject ( new Error ( 'SystemVerilog: Invalid document.' ) ) ; return ; } if ( ! isSystemVerilogDocument ( document ) && ! isVerilogDocument ( document ) ) { reject ( new Error ( 'The document is not a SystemVerilog/Verilog file.' ) ) ; return ; } const diagnosticCollection = new Map ( ) ; const filePath = getPathFromUri ( document . uri , this . workspaceRootPath ) ; const args = [ ] ; if ( this . configurations . get ( this . compilerConfigurationsKeys [ 0 ] ) == "Verilator" && this . configurations . has ( this . compilerConfigurationsKeys [ 2 ] ) ) { args . push ( this . configurations . get ( this . compilerConfigurationsKeys [ 2 ] ) ) ; } else if ( this . configurations . get ( this . compilerConfigurationsKeys [ 0 ] ) == "Verilator" ) { reject ( new Error ( `'${ this . compilerConfigurationsKeys [ 2 ] }' configuration is undefined.` ) ) ; return ; } if ( this . configurations . get ( this . compilerConfigurationsKeys [ 0 ] ) == "VCS" && this . configurations . has ( this . compilerConfigurationsKeys [ 3 ] ) ) { args . push ( this . configurations . get ( this . compilerConfigurationsKeys [ 3 ] ) ) ; } else if ( this . configurations . get ( this . compilerConfigurationsKeys [ 0 ] ) == "VCS" ) { reject ( new Error ( `'${ this . compilerConfigurationsKeys [ 3 ] }' configuration is undefined.` ) ) ; return ; } if ( this . configurations . get ( this . compilerConfigurationsKeys [ 0 ] ) == "Verible" && this . configurations . has ( this . compilerConfigurationsKeys [ 4 ] ) ) { args . push ( this . configurations . get ( this . compilerConfigurationsKeys [ 4 ] ) ) ; } else if ( this . configurations . get ( this . compilerConfigurationsKeys [ 0 ] ) == "Verible" ) { reject ( new Error ( `'${ this . compilerConfigurationsKeys [ 4 ] }' configuration is undefined.` ) ) ; return ; } args . push ( filePath ) ; this . connection . console . log ( args . join ( ' ' ) ) ; child . exec ( args . join ( ' ' ) , ( error , stdout , stderr ) => { this . connection . console . log ( stdout ) ; this . connection . console . log ( stderr ) ; this . parseDiagnostics ( error , stdout , stderr , document , filePath , diagnosticCollection ) ; resolve ( diagnosticCollection ) ; } ) ; } ) ; } param error param stdout param stderr param compiledDocument param documentFilePath param collection returns public abstract parseDiagnostics ( error , stdout , stderr , compiledDocument , documentFilePath , collection ) ; param document param resetDiagnostics param diagnosticData param collection returns publishDiagnosticForDocument ( compiledDocument , resetDiagnostics , diagnosticData , collection ) { let diagnostic ; let diagnostics ; if ( diagnosticData . filePath . localeCompare ( getPathFromUri ( compiledDocument . uri , this . workspaceRootPath ) ) === 0 ) { const range = getLineRange ( diagnosticData . line , diagnosticData . offendingSymbol , diagnosticData . charPosition ) ; diagnostic = { severity : diagnosticData . diagnosticSeverity , range , message : diagnosticData . problem , source : 'systemverilog' } ; if ( ! resetDiagnostics && collection . has ( compiledDocument . uri ) ) { diagnostics = collection . get ( compiledDocument . uri ) . concat ( [ diagnostic ] ) ; } else { diagnostics = [ diagnostic ] ; } collection . set ( compiledDocument . uri , diagnostics ) ; } else { const filteredUris = this . filterDocumentsUris ( diagnosticData . filePath ) ; if ( filteredUris . length === 1 ) { const uri = filteredUris [ 0 ] ; const document = this . documents . get ( uri ) ; const range = getLineRange ( diagnosticData . line , diagnosticData . offendingSymbol , diagnosticData . charPosition ) ; diagnostic = { severity : diagnosticData . diagnosticSeverity , range , message : diagnosticData . problem , source : 'systemverilog' } ; if ( ! resetDiagnostics && collection . has ( uri ) ) { diagnostics = collection . get ( uri ) . concat ( [ diagnostic ] ) ; } else { diagnostics = [ diagnostic ] ; } collection . set ( uri , diagnostics ) ; } } } param filePath returns filterDocumentsUris ( filePath ) { return this . documents . keys ( ) . filter ( ( key ) => { if ( path . basename ( key . trim ( ) ) . localeCompare ( path . basename ( filePath . trim ( ) ) ) === 0 ) { return true ; } return false ; } ) ; } param severity returns getDiagnosticSeverity ( severity ) { const tSeverity = severity . trim ( ) ; if ( tSeverity === 'Error' ) { return DiagnosticSeverity . Error ; } if ( tSeverity === 'Warning' ) { return DiagnosticSeverity . Warning ; } return DiagnosticSeverity . Information ; } }