import { StatusBarItem , GlobPattern , window , ProgressLocation , workspace , TextDocument , Uri , OutputChannel } from 'vscode' ; import { CancellationToken } from 'vscode-languageclient/node' ; import * as glob from 'glob' ; import * as minimatch from 'minimatch' ; import { SystemVerilogParser } from './parser' ; import { isSystemVerilogDocument , isVerilogDocument } from './utils/client' ; import { SystemVerilogSymbol } from './symbol' ; export class SystemVerilogIndexer { public symbols : Map < string , Array < SystemVerilogSymbol > > ; public mostRecentSymbols : Array < SystemVerilogSymbol > ; public building : Boolean = false ; public statusbar : StatusBarItem ; public parser : SystemVerilogParser ; public symbolsCount : number = 0 ; public NUM_FILES : number = 250 ; public parallelProcessing : number ; public filesGlob : string = undefined ; public exclude : GlobPattern = undefined ; public forceFastIndexing : Boolean = false ; public outputChannel : OutputChannel ; constructor ( statusbar , parser , channel ) { this . statusbar = statusbar ; this . parser = parser ; this . outputChannel = channel ; this . symbols = new Map < string , Array < SystemVerilogSymbol > > ( ) ; } return public async build_index ( ) { let cancelled = false ; this . building = true ; this . symbolsCount = 0 ; this . statusbar . text = 'SystemVerilog: Indexing..' ; const settings = workspace . getConfiguration ( ) ; this . parallelProcessing = settings . get ( 'systemverilog.parallelProcessing' ) ; this . forceFastIndexing = settings . get ( 'systemverilog.forceFastIndexing' ) ; return await window . withProgress ( { location : ProgressLocation . Notification , title : 'SystemVerilog Indexing...' , cancellable : true } , async ( _progress , token ) => { this . symbols = new Map < string , Array < SystemVerilogSymbol > > ( ) ; const uris = await this . find_files ( token ) ; console . time ( 'build_index' ) ; for ( let filenr = 0 ; filenr < uris . length ; filenr += this . parallelProcessing ) { const subset = uris . slice ( filenr , filenr + this . parallelProcessing ) ; if ( token . isCancellationRequested ) { cancelled = true ; break ; } await Promise . all ( subset . map ( ( uri ) => this . processFile ( uri , uris . length ) ) ) ; } } ) . then ( ( ) => { this . building = false ; console . timeEnd ( 'build_index' ) ; if ( cancelled ) { this . statusbar . text = 'SystemVerilog: Indexing cancelled' ; } else { this . statusbar . text = `SystemVerilog: ${ this . symbolsCount } indexed objects` ; } } ) ; } /**
     * find_files
     */ public async find_files ( token ) { return new Promise ( async ( resolve ) => { const settings = workspace . getConfiguration ( ) ; const globArray = settings . get ( 'systemverilog.includeIndexing' ) ; const exclude = settings . get ( 'systemverilog.excludeIndexing' ) ; let uris = [ ] ; const find = ( str ) => { if ( str . startsWith ( '*' ) ) { return workspace . findFiles ( str , exclude , undefined , token ) . then ( ( files ) => { uris = uris . concat ( files ) ; } ) ; } const files = glob . sync ( str , { ignore : exclude } ) ; uris = uris . concat ( files . map ( Uri . file ) ) ; } ; await Promise . all ( globArray . map ( find ) ) ; resolve ( uris ) ; } ) ; } param uri param total_files public async processFile ( uri , total_files = 0 ) { return new Promise ( async ( resolve ) => { resolve ( workspace . openTextDocument ( uri ) . then ( ( doc ) => { if ( total_files >= 1000 * this . parallelProcessing || this . forceFastIndexing ) { return this . parser . get_all_recursive ( doc , 'fast' , 0 ) ; } if ( total_files >= 100 * this . parallelProcessing ) { return this . parser . get_all_recursive ( doc , 'declaration' , 0 ) ; } return this . parser . get_all_recursive ( doc , 'declaration' , 1 ) ; } ) ) ; } ) . then ( ( output ) => { if ( output . length > 0 ) { if ( this . symbols . has ( uri . fsPath ) ) { this . symbolsCount += output . length - this . symbols . get ( uri . fsPath ) . length ; } else { this . symbolsCount += output . length ; } this . symbols . set ( uri . fsPath , output ) ; if ( total_files === 0 ) { this . statusbar . text = `SystemVerilog: ${ this . symbolsCount } indexed objects` ; } } } ) . catch ( ( error ) => { this . outputChannel . appendLine ( `SystemVerilog: Indexing: Unable to process file: ${ uri . toString ( ) }` ) ; this . outputChannel . appendLine ( error ) ; if ( this . symbols . has ( uri . fsPath ) ) { this . symbolsCount -= this . symbols . get ( uri . fsPath ) . length ; this . symbols . delete ( uri . fsPath ) ; } return undefined ; } ) ; } param document return public async onChange ( document ) { return await new Promise ( ( ) => { if ( ! isSystemVerilogDocument ( document ) && ! isVerilogDocument ( document ) ) { return ; } if ( ! workspace . getConfiguration ( ) . get ( 'systemverilog.enableIncrementalIndexing' ) ) { return ; } if ( ! minimatch ( document . uri . toString ( ) , workspace . getConfiguration ( ) . get ( 'systemverilog.excludeIndexing' ) . toString ( ) ) ) { return this . processFile ( document . uri ) ; } } ) ; } param uri return public async onCreate ( uri ) { return await new Promise ( ( ) => workspace . openTextDocument ( uri ) . then ( ( document ) => this . onChange ( document ) ) ) ; } param uri return public async onDelete ( uri ) { return await new Promise ( ( ) => workspace . openTextDocument ( uri ) . then ( ( document ) => this . onChange ( document ) ) ) ; } param fsPath param symbolsMap return addDocumentSymbols ( document , symbolsMap ) { return new Promise ( async ( resolve ) => { if ( ! document || ! symbolsMap ) { resolve ( new Array < SystemVerilogSymbol > ( ) ) ; return ; } if ( ! isSystemVerilogDocument ( document ) && ! isVerilogDocument ( document ) ) { resolve ( new Array < SystemVerilogSymbol > ( ) ) ; return ; } resolve ( workspace . openTextDocument ( document . uri ) . then ( ( doc ) => this . parser . get_all_recursive ( doc , 'declaration' , 1 ) ) ) ; } ) . then ( ( output ) => { if ( output . length > 0 ) { symbolsMap . set ( document . uri . fsPath , output ) ; } return output . length ; } ) ; } param fsPath param symbolsMap return removeDocumentSymbols ( fsPath , symbolsMap ) { if ( ! fsPath || ! symbolsMap ) { return 0 ; } let deletedCount = 0 ; if ( symbolsMap . has ( fsPath ) ) { deletedCount = symbolsMap . get ( fsPath ) . length ; symbolsMap . delete ( fsPath ) ; } return deletedCount * - 1 ; } param recentSymbols updateMostRecentSymbols ( recentSymbols ) { if ( this . mostRecentSymbols ) { if ( ! recentSymbols ) { return ; } while ( recentSymbols . length > 0 ) { const currentSymbol = recentSymbols . pop ( ) ; for ( let i = 0 ; i < this . mostRecentSymbols . length ; i ++ ) { const symbol = this . mostRecentSymbols [ i ] ; if ( symbol === currentSymbol ) { this . mostRecentSymbols . splice ( i , 1 ) ; break ; } } if ( this . mostRecentSymbols . length >= this . NUM_FILES ) { this . mostRecentSymbols . pop ( ) ; } this . mostRecentSymbols . unshift ( currentSymbol ) ; } } else { let maxSymbols = new Array < SystemVerilogSymbol > ( ) ; for ( const list of this . symbols . values ( ) ) { if ( maxSymbols . length + list . length >= this . NUM_FILES ) { const limit = this . NUM_FILES - maxSymbols . length ; maxSymbols = maxSymbols . concat ( list . slice ( - 1 * limit ) ) ; break ; } else { maxSymbols = maxSymbols . concat ( list ) ; } } this . mostRecentSymbols = maxSymbols ; } } }