import { Remote } from '../remotes/remotebase' ; import * as util from 'util' ; import { Utility } from '../misc/utility' ; import { Labels } from '../labels/labels' ; import { MetaBlock } from '../misc/memorydump' ; import { Settings } from '../settings' ; import { MemoryDumpView } from './memorydumpview' ; import { BaseView } from './baseview' ; const MEM_COLUMNS = 8 ; /**
 * A Webview that shows a memory dump.
 * The memory dump can also be edited.
 * There is a rather complex messaging between the webview's html javascript (the webview
 * panel) and the extension (the typescript code):
 * - Display:
 * 		- Register coloring: If the address of a value is the same as a register value it is colored
 * differently. 'setColorsForRegisterPointers' calls 'setAddressColor' in the webview. This is done
 * when the panel is created or updated (each step) or when the webview becomes visible (e.g. if
 * it was hidden).
 * 		- The dump contents is updated on every step ('update').
 * 		- The hovering text (labels etc.): When the mouse is over a value or address the webview asks for the hovering text ('getValueInfoText/getAddressInfoText'.
 * 		This way the calculation of all labels is delayed. A message with the info is sent to the webview ('valueInfoText/addressInfoText').
 * - Editing:
 * 		- On double click the webview turns the cell in editable mode.
 * 		- When the user presses enter the new value is sent from the webview('valueChanged').
 * 		- The value is then changed in the remote and the real value (from the remote) is sent to
 * 		to the webview. The webview will then update all cells with the same address. I.e. if there
 * 		are 2 same cells both are updated.
 * 		- If there are several memory views all are informed about the new value to update their display.
 *
 * See design.md for a sequence chart.
 */ export class MemoryDumpViewWord extends MemoryDumpView { protected littleEndian : boolean ; /**
     * Creates the basic panel.
     */ constructor ( littleEndian ) { super ( ) ; this . littleEndian = littleEndian ; if ( ! littleEndian ) this . titlePrefix += "(big endian) " ; } param startAddress param size public addBlock ( startAddress , size , title ) { this . memDump . addBlockWithoutBoundary ( startAddress , 2 * size , title ) ; } param address param value protected async changeMemory ( address , value ) { let lowByte = value & 0xFF ; let highByte = value >> 8 ; if ( ! this . littleEndian ) { const tmp = lowByte ; lowByte = highByte ; highByte = tmp ; } const data = new Uint8Array ( [ lowByte , highByte ] ) ; await Remote . writeMemoryDump ( address , data ) ; const realData = await Remote . readMemoryDump ( address , 2 ) ; const realValue = realData [ 0 ] + 256 * realData [ 1 ] ; for ( const mdvb of MemoryDumpView . MemoryViews ) { const mdv = mdvb as MemoryDumpViewWord ; if ( ! isNaN ( mdv . memDump . getWordValueFor ( address , this . littleEndian ) ) ) { mdv . memDump . setWordValueFor ( address , realValue , true ) ; } } BaseView . staticCallUpdateWithoutRemote ( ) ; BaseView . sendChangeEvent ( ) ; } param address protected async getValueInfoText ( address ) { const value = this . memDump . getWordValueFor ( address , this . littleEndian ) ; const valFormattedString = await Utility . numberFormatted ( '' , value , 1 , Settings . launch . memoryViewer . valueHoverFormat , undefined ) ; let text = valFormattedString + '\n' ; const addrFormattedString = await Utility . numberFormatted ( '' , address , 2 , Settings . launch . memoryViewer . addressHoverFormat , undefined ) ; text += '@\n' + addrFormattedString ; const prevValue = this . memDump . getPrevWordValueFor ( address , this . littleEndian ) ; if ( ! isNaN ( prevValue ) ) { if ( prevValue != value ) { text += '\nPrevious value: ' + Utility . getHexString ( prevValue , 2 ) + 'h' ; } } const msg = { command : 'valueInfoText' , address : address . toString ( ) , text : text } ; this . sendMessageToWebView ( msg ) ; } param index param metaBlock protected createHtmlTable ( metaBlock ) { if ( ! metaBlock . data ) return '' ; const format = `			<table style="">
				<colgroup>
					<col>
					<col width="10em">
					<col span="%d" width="20em">
				</colgroup>

			%s
			</table>
		` ; let table = '' ; let address = metaBlock . address ; let i = 0 ; const data = metaBlock . data ; const len = data . length ; const addressColor = Settings . launch . memoryViewer . addressColor ; const bytesColor = Settings . launch . memoryViewer . bytesColor ; table += '<tr>\n<th>Address:</th> <th></th>' ; for ( let k = 0 ; k < MEM_COLUMNS ; k ++ ) { table += '<th>+' + ( 2 * k ) . toString ( 16 ) . toUpperCase ( ) + '</th>' ; } table += '\n</tr>' ; const littleEndian = this . littleEndian ; let firstAddress ; let secondAddress ; for ( let k = 0 ; k < len - 1 ; k += 2 ) { const addr64k = address & 0xFFFF ; if ( i == 0 ) { let addrText = Utility . getHexString ( addr64k , 4 ) + ':' ; table += '<tr>\n<td addressLine="' + addr64k + '" style="color:' + addressColor + '; border-radius:3px; cursor: pointer" onmouseover="mouseOverAddress(this)">' + addrText + '</td>\n' ; table += '<td> </td>\n' ; } const value = Utility . getUintFromMemory ( data , k , 2 , this . littleEndian ) ; let valueText = Utility . getHexString ( value , 4 ) ; const addr64k2 = ( addr64k + 1 ) & 0xFFFF ; if ( littleEndian ) { firstAddress = addr64k ; secondAddress = addr64k2 ; } else { firstAddress = addr64k2 ; secondAddress = addr64k ; } valueText = '<span address="' + secondAddress + '">' + valueText . substring ( 0 , 2 ) + '</span><span address="' + firstAddress + '">' + valueText . substr ( 2 , 2 ) + '</span>' ; if ( metaBlock . isInRange ( address ) ) valueText = this . addEmphasizeInRange ( valueText ) ; else valueText = this . addDeemphasizeNotInRange ( valueText ) ; if ( Labels . getLabelsForNumber64k ( addr64k ) . length > 0 ) valueText = this . addEmphasizeLabelled ( valueText ) ; const prevData = metaBlock . prevData ; if ( prevData ) { if ( prevData . length > 0 ) { const prevValue = Utility . getUintFromMemory ( prevData , k , 2 , this . littleEndian ) ; if ( value != prevValue ) { valueText = this . addEmphasizeChanged ( valueText ) ; } } } table += '<td address="' + addr64k + '" ondblclick="makeEditable(this)" onmouseover="mouseOverValue(this)" style="color:' + bytesColor + '">' + valueText + '</td>\n' ; if ( i == MEM_COLUMNS - 1 ) { table += '</tr>\n' ; } address += 2 ; i ++ ; if ( i >= MEM_COLUMNS ) i = 0 ; } const html = util . format ( format , MEM_COLUMNS , table ) ; return html ; } /**
     * Creates the script (i.e. functions) for all blocks (html tables).
     */ protected createHtmlScript ( ) { const html = `
		<script>
		const vscode = acquireVsCodeApi();


		//---- Handle Mouse Over, Calculation of hover text -------
		function mouseOverValue(obj) {
			const address = obj.getAttribute("address");
			// Send request to vscode to calculate the hover text
			vscode.postMessage({
				command: 'getValueInfoText',
				address: address
			});
		}

		function mouseOverAddress(obj) {
			// Send request to vscode to calculate the hover text
			const address = obj.getAttribute("addressLine");
			vscode.postMessage({
				command: 'getAddressInfoText',
				address: address
			});
		}


		//---- Handle Editing Cells --------
		let prevValue = '';	// Used to restore the value if ESC is pressed.
		let curObj = null;	// The currently used object (the tabbed cell)

		function keyPress(e) {
			let key = e.keyCode;

			if(key == 13) {	// ENTER key
				const value = curObj.innerText;
				prevValue = value;	// To prevent that the old value is shown in 'focusLost'
				const address = curObj.getAttribute("address");
				e.preventDefault();
				curObj.blur();
				// Send new value for address to vscode
				vscode.postMessage({
					command: 'valueChanged',
					address: address,
					value: value
				});
			}
			else if(key == 27) {	// ESC key, does not work in vscode
				// Use previous value
				e.preventDefault();
				curObj.blur();
			}
		}

		function focusLost(e) {	// = "blur"
			// Undo: Use previous value
			if(prevValue.length > 0)
				curObj.innerText = prevValue;
			curObj.contentEditable = false;
			curObj.removeEventListener("blur", focusLost);
			curObj.removeEventListener("keypress", keyPress);
			curObj = null;
		}

		function makeEditable(obj) {
			// makes the object editable on double click.
			curObj = obj;	// store object for use in other functions
			prevValue = curObj.innerText;	// store for undo
			if(!curObj.innerText.endsWith('h'))
				curObj.innerText += 'h';
			curObj.contentEditable = true;
			curObj.focus();
			selection = window.getSelection();    // Save the selection.

			// Select the text
			range = document.createRange();
			range.selectNodeContents(curObj);
			selection.removeAllRanges();          // Remove all ranges from the selection.
			selection.addRange(range);

			// Add listeners
			curObj.addEventListener("blur", focusLost, true);
			curObj.addEventListener("keypress", keyPress, true);
		}


		//---- Handle Messages from vscode extension --------
		window.addEventListener('message', event => {
			const message = event.data;

            switch (message.command) {
				case 'valueInfoText':
				{
					const objs = document.querySelectorAll("td[address='"+message.address+"']");
					for(let obj of objs) {
						obj.title = message.text;
					}
                }   break;

				case 'addressInfoText':
				{
					const objs = document.querySelectorAll("td[addressLine='"+message.address+"']");
					for(let obj of objs) {
						obj.title = message.text;
					}
				}   break;

				case 'setAddressColor':
				{
					const objs = document.querySelectorAll("span[address='"+message.address+"']");
					for(let obj of objs) {
						obj.style.backgroundColor = message.color;
						obj.style.borderRadius = '3px';
					}
				 }   break;

				case 'setMemoryTable':
				{
					// Set table as html string
			        const tableDiv=document.getElementById("mem_table_"+message.index);
					tableDiv.innerHTML=message.html;
 				}   break;

           }
        });

		//# sourceURL=memorydumpviewword.js
		</script>
` ; return html ; } }