import * as vscode from 'vscode' ; import { Labels , SourceFileEntry } from './labels/labels' ; import { Disassembly , DisassemblyClass } from './misc/disassembly' ; import { UnifiedPath } from './misc/unifiedpath' ; import { Utility } from './misc/utility' ; export let Decoration ; /**
 * Each decoration type (coverage, reverse debug, break) gets its own
 * instance of DecorationFileMap.
 */ class DecorationFileMap { public decoType : vscode . TextEditorDecorationType ; public fileMap : Map < string , Array < vscode . Range > | Array < vscode . DecorationOptions > > ; } /**
 * A singleton that holds the editor decorations for code coverage,
 * reverse debugging andother decorations, e.g. 'break'.
 *
 * Decorations are tedious to handle.
 * If an editor becomes inactive (hidden) it looses its decorations
 * therefore it is necessary
 * - to watch for changes of the active editor
 * - and to store the decorations for each document/editor
 * This means: decoration are added to the editor as soon as they occur and also
 * whenever the active editor changes.
 * What makes it even more complicated is the fact that the disasm.asm file may not even exist
 * when a decoration comes in. And, furthermore, the file may exist already but change
 * its contents.
 * The disasm.asm is created during the stackTraceRequest.
 * It requires decoration (like the other files) of
 * - coverage (green background)
 * - historySpot (the index numbers)
 * - revDbg history (gray background)
 * - break reason (text)
 * 'coverage' is a special case as here only the delta addresses are reported.
 * Therefore it is necessary to store all addresses that do not belong to any other
 * (ordinary) file (in unassignedCodeCoverageAddresses).
 * Also 'coverage' is not emitted in case of reverse debugging.
 * 'historySpot', 'revDbg' and 'break' always contain the complete decoration information.
 * All those calls are delayed in the debugAdapter until the disasm.asm file is created in the
 * stackTraceRequest. This is already done in the debugAdapter.
 */ export class DecorationClass { protected COVERAGE = "Coverage" ; protected REVERSE_DEBUG = "RevDbg" ; protected BREAK = "Break" ; protected HISTORY_SPOT = "HistorySpot" ; protected decorationFileMaps : Map < string , DecorationFileMap > ; protected unassignedCodeCoverageAddresses : Set < number > ; public static Initialize ( ) { Decoration = new DecorationClass ( ) ; } /**
     * Register for a change of the text editor to decorate it with the
     * covered lines.
     */ constructor ( ) { const coverageDecoType = vscode . window . createTextEditorDecorationType ( { isWholeLine : true , gutterIconSize : 'auto' , light : { backgroundColor : '#d5efc3a0' , } , dark : { backgroundColor : '#09300390' , } } ) ; const historySpotDecoType = vscode . window . createTextEditorDecorationType ( { isWholeLine : true , gutterIconSize : 'auto' , light : { after : { color : "#808080" , } } , dark : { after : { color : "#808080" , } } , } ) ; const revDbgDecoType = vscode . window . createTextEditorDecorationType ( { isWholeLine : true , gutterIconSize : 'auto' , light : { backgroundColor : '#C7C7C7' , } , dark : { backgroundColor : '#353535' , } } ) ; const breakDecoType = vscode . window . createTextEditorDecorationType ( { isWholeLine : true , gutterIconSize : 'auto' , light : { after : { color : "black" , } } , dark : { after : { color : "#808080" , } } } ) ; this . decorationFileMaps = new Map < string , DecorationFileMap > ( ) ; let decoFileMap = new DecorationFileMap ( ) ; decoFileMap . decoType = coverageDecoType ; decoFileMap . fileMap = new Map < string , Array < vscode . Range > > ( ) ; this . decorationFileMaps . set ( this . COVERAGE , decoFileMap ) ; decoFileMap = new DecorationFileMap ( ) ; decoFileMap . decoType = revDbgDecoType ; decoFileMap . fileMap = new Map < string , Array < vscode . Range > > ( ) ; this . decorationFileMaps . set ( this . REVERSE_DEBUG , decoFileMap ) ; decoFileMap = new DecorationFileMap ( ) ; decoFileMap . decoType = breakDecoType ; decoFileMap . fileMap = new Map < string , Array < vscode . DecorationOptions > > ( ) ; this . decorationFileMaps . set ( this . BREAK , decoFileMap ) ; decoFileMap = new DecorationFileMap ( ) ; decoFileMap . decoType = historySpotDecoType ; decoFileMap . fileMap = new Map < string , Array < vscode . DecorationOptions > > ( ) ; this . decorationFileMaps . set ( this . HISTORY_SPOT , decoFileMap ) ; this . unassignedCodeCoverageAddresses = new Set < number > ( ) ; vscode . window . onDidChangeActiveTextEditor ( editor => { this . setAllDecorations ( editor ) ; } ) ; } /**
     * Loops through all active editors and clear the coverage decorations.
     */ public clearCodeCoverage ( ) { this . clearDecorations ( this . COVERAGE ) ; } /**
     * Loops through all active editors and clears the reverse debug decorations.
     */ public clearRevDbgHistory ( ) { this . clearDecorations ( this . REVERSE_DEBUG ) ; } /**
     * Loops through all active editors and clears the 'Break' decorations.
     */ public clearBreak ( ) { this . clearDecorations ( this . BREAK ) ; } /**
     * Loops through all active editors and clears the 'historySpot' decorations.
     */ public clearHistorySpot ( ) { this . clearDecorations ( this . HISTORY_SPOT ) ; } param mapName protected clearDecorations ( mapName ) { const map = this . decorationFileMaps . get ( mapName ) as DecorationFileMap ; map . fileMap . clear ( ) ; const editors = vscode . window . visibleTextEditors ; for ( const editor of editors ) { editor . setDecorations ( map . decoType , [ ] ) ; } if ( mapName == this . COVERAGE ) this . unassignedCodeCoverageAddresses . clear ( ) ; } /**
     * Clears all decorations for all editors.
     */ public clearAllDecorations ( ) { for ( const [ , map ] of this . decorationFileMaps ) { map . fileMap . clear ( ) ; const editors = vscode . window . visibleTextEditors ; for ( const editor of editors ) { editor . setDecorations ( map . decoType , [ ] ) ; } } this . unassignedCodeCoverageAddresses . clear ( ) ; } /**
     * Clears all decorations but the code coverage decorations for all editors.
     */ public clearAllButCodeCoverageDecorations ( ) { for ( const [ name , map ] of this . decorationFileMaps ) { if ( name != this . COVERAGE ) { map . fileMap . clear ( ) ; const editors = vscode . window . visibleTextEditors ; for ( const editor of editors ) { editor . setDecorations ( map . decoType , [ ] ) ; } } } } /**
     * Sets decorations for all types.
     * Coverage, revers debug, breaks, short history.
     */ protected setAllDecorations ( editor ) { if ( ! editor ) return ; for ( const [ fileMapName , ] of this . decorationFileMaps ) { this . setDecorations ( editor , fileMapName ) ; } } param fileMapName protected setDecorations ( editor , fileMapName ) { const edFilename = UnifiedPath . getUnifiedPath ( editor . document . fileName ) ; if ( fileMapName == this . COVERAGE ) { if ( edFilename == DisassemblyClass . getAbsFilePath ( ) ) { this . setDisasmCoverageDecoration ( editor ) ; return ; } } const decoMap = this . decorationFileMaps . get ( fileMapName ) as DecorationFileMap ; Utility . assert ( decoMap ) ; const fileMap = decoMap . fileMap ; const decorations = fileMap . get ( edFilename ) ; if ( decorations ) { editor . setDecorations ( decoMap . decoType , decorations ) ; } } /**
     * Sets the decorations for the disassembler temp file.
     */ public setDisasmCoverageDecoration ( editor ) { const lines = Disassembly . getLinesForAddresses ( this . unassignedCodeCoverageAddresses ) ; const decorations = lines . map ( lineNr => new vscode . Range ( lineNr , 0 , lineNr , 1000 ) ) ; if ( decorations ) { const decoMap = this . decorationFileMaps . get ( this . COVERAGE ) as DecorationFileMap ; editor . setDecorations ( decoMap . decoType , decorations ) ; } } param coveredAddresses public showCodeCoverage ( coveredAddresses ) { const mapName = this . COVERAGE ; const decoMap = this . decorationFileMaps . get ( mapName ) as DecorationFileMap ; const fileMap = decoMap . fileMap ; coveredAddresses . forEach ( addr => { let location = Labels . getFileAndLineForAddress ( addr ) ; let filename = location . fileName ; if ( filename . length == 0 ) { this . unassignedCodeCoverageAddresses . add ( addr ) ; return ; } let lines = fileMap . get ( filename ) as Array < vscode . Range > ; if ( ! lines ) { lines = new Array < vscode . Range > ( ) ; fileMap . set ( filename , lines ) ; } const lineNr = location . lineNr ; const range = new vscode . Range ( lineNr , 0 , lineNr , 1000 ) ; lines . push ( range ) ; } ) ; const editors = vscode . window . visibleTextEditors ; for ( const editor of editors ) { this . setDecorations ( editor , this . COVERAGE ) ; } } param addresses public showRevDbgHistory ( addresses ) { this . clearRevDbgHistory ( ) ; const decoMap = this . decorationFileMaps . get ( this . REVERSE_DEBUG ) as DecorationFileMap ; const fileMap = decoMap . fileMap ; addresses . forEach ( addr => { const location = this . getFileAndLineForAddress ( addr ) ; const filename = location . fileName ; if ( filename . length == 0 ) return ; let lines = fileMap . get ( filename ) as Array < vscode . Range > ; if ( ! lines ) { lines = new Array < vscode . Range > ( ) ; fileMap . set ( filename , lines ) ; } const lineNr = location . lineNr ; const range = new vscode . Range ( lineNr , 0 , lineNr , 1000 ) ; lines . push ( range ) ; } ) ; const editors = vscode . window . visibleTextEditors ; for ( const editor of editors ) { this . setDecorations ( editor , this . REVERSE_DEBUG ) ; } } param pc param text public showBreak ( pc , text ) { const decoMap = this . decorationFileMaps . get ( this . BREAK ) as DecorationFileMap ; const fileMap = decoMap . fileMap ; fileMap . clear ( ) ; const location = this . getFileAndLineForAddress ( pc ) ; const filename = location . fileName ; if ( filename . length > 0 ) { let lines = fileMap . get ( filename ) as Array < vscode . DecorationOptions > ; if ( ! lines ) { lines = new Array < vscode . DecorationOptions > ( ) ; fileMap . set ( filename , lines ) ; } const lineNr = location . lineNr ; const deco = { range : new vscode . Range ( lineNr , 0 , lineNr , 1000 ) , hoverMessage : undefined , renderOptions : { after : { contentText : text , margin : "1.5em" } , } , } ; lines . push ( deco ) ; } const editors = vscode . window . visibleTextEditors ; for ( const editor of editors ) { this . setDecorations ( editor , this . BREAK ) ; } } param startIndex param addresses param registers public showHistorySpot ( startIndex , addresses , registers ) { this . clearHistorySpot ( ) ; const decoMap = this . decorationFileMaps . get ( this . HISTORY_SPOT ) as DecorationFileMap ; const fileMap = decoMap . fileMap ; const addressMap = new Map < string , { regText : string ; indexText : string ; } > ( ) ; let index = - startIndex - 1 ; addresses . forEach ( ( addr , k ) => { const location = this . getFileAndLineForAddress ( addr ) ; const locString = location . lineNr + ';' + location . fileName ; let entry = addressMap . get ( locString ) ; if ( ! entry ) { entry = { regText : registers [ k ] || '' , indexText : index . toString ( ) } ; addressMap . set ( locString , entry ) ; } else { entry . indexText += ", " + index . toString ( ) ; } index -- ; } ) ; for ( const [ locString , entry ] of addressMap ) { const k = locString . indexOf ( ';' ) ; const filename = locString . substr ( k + 1 ) ; if ( filename . length == 0 ) break ; let lines = fileMap . get ( filename ) as Array < vscode . DecorationOptions > ; if ( ! lines ) { lines = new Array < vscode . DecorationOptions > ( ) ; fileMap . set ( filename , lines ) ; } const lineNr = parseInt ( locString ) ; const deco = { range : new vscode . Range ( lineNr , 0 , lineNr , 1000 ) , hoverMessage : undefined , renderOptions : { after : { contentText : "[" + entry . indexText + "] " + entry . regText , margin : "2.5em" , } , } , } ; lines . push ( deco ) ; } const editors = vscode . window . visibleTextEditors ; for ( const editor of editors ) { this . setDecorations ( editor , this . HISTORY_SPOT ) ; } } param addr protected getFileAndLineForAddress ( addr ) { const location = Labels . getFileAndLineForAddress ( addr ) ; if ( location . fileName . length == 0 ) { const lineNr = Disassembly . getLineForAddress ( addr ) ; if ( lineNr != undefined ) { location . fileName = DisassemblyClass . getAbsFilePath ( ) ; location . lineNr = lineNr ; } } return location ; } }