Use VS Code mode
This commit is contained in:
		| @@ -16,10 +16,10 @@ | |||||||
|       { "command": "modelsim.toggleWaveMode", "title": "Toggle Wave Debug Mode" } |       { "command": "modelsim.toggleWaveMode", "title": "Toggle Wave Debug Mode" } | ||||||
|     ], |     ], | ||||||
|     "keybindings": [ |     "keybindings": [ | ||||||
|       { "command": "modelsim.addWaveUnderCursor", "key": "ctrl+w", "args": { "mode": "auto" } }, |       { "command": "modelsim.addWaveUnderCursor", "key": "ctrl+w",     "when": "modelsim.mode", "args": { "mode": "auto" } }, | ||||||
|       { "command": "modelsim.addWaveUnderCursor", "key": "ctrl+e", "args": { "mode": "set" } }, |       { "command": "modelsim.addWaveUnderCursor", "key": "ctrl+e",     "when": "modelsim.mode", "args": { "mode": "set" } }, | ||||||
|       { "command": "modelsim.toggleWaveMode", "key": "ctrl+alt+m" }, |       { "command": "modelsim.toggleWaveMode",     "key": "ctrl+alt+m" }, | ||||||
|       { "command": "modelsim.zoomInWave", "key": "ctrl+alt+u" } |       { "command": "modelsim.zoomInWave",         "key": "ctrl+alt+u", "when": "modelsim.mode" } | ||||||
|     ], |     ], | ||||||
|     "viewsContainers": { |     "viewsContainers": { | ||||||
|       "panel": [ |       "panel": [ | ||||||
|   | |||||||
| @@ -10,6 +10,10 @@ import { requestModuleTree, addWave } from './wave_editor'; | |||||||
| export const lastHoverByDoc = new Map<string, { pos: vscode.Position; at: number }>(); | export const lastHoverByDoc = new Map<string, { pos: vscode.Position; at: number }>(); | ||||||
| export const HOVER_FRESH_MS = 2000; | export const HOVER_FRESH_MS = 2000; | ||||||
|  |  | ||||||
|  | let statusItem: vscode.StatusBarItem; | ||||||
|  | const CTX = 'modelsim.mode'; | ||||||
|  | export let waveMode = false; | ||||||
|  |  | ||||||
| // Hold MANY instance paths per module name, e.g. queue_slot -> ["/test/...[0]", "/test/...[1]", ...] | // Hold MANY instance paths per module name, e.g. queue_slot -> ["/test/...[0]", "/test/...[1]", ...] | ||||||
|  |  | ||||||
| const svSelectors: vscode.DocumentSelector = [ | const svSelectors: vscode.DocumentSelector = [ | ||||||
| @@ -17,20 +21,22 @@ const svSelectors: vscode.DocumentSelector = [ | |||||||
|   { language: 'verilog', scheme: 'file' } |   { language: 'verilog', scheme: 'file' } | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
| export let waveMode = false; |  | ||||||
| let statusItem: vscode.StatusBarItem; |  | ||||||
|  |  | ||||||
| function toggleWaveMode() { |  | ||||||
|  | async function toggleWaveMode() { | ||||||
|   waveMode = !waveMode; |   waveMode = !waveMode; | ||||||
|   statusItem.text = waveMode ? '🌊 Wave Debug Mode' : '✏️ Edit Mode'; |   statusItem.text = waveMode ? '🌊 Wave Debug Mode' : '✏️ Edit Mode'; | ||||||
|  |   await vscode.commands.executeCommand('setContext', CTX, waveMode); | ||||||
|   statusItem.tooltip = waveMode |   statusItem.tooltip = waveMode | ||||||
|     ? 'Wave Debug Mode — click to switch to Edit Mode' |     ? 'Wave Debug Mode — click to switch to Edit Mode' | ||||||
|     : 'Edit Mode — click to switch to Wave Debug Mode'; |     : 'Edit Mode — click to switch to Wave Debug Mode'; | ||||||
| } | } | ||||||
|  |  | ||||||
| export function activate(context: vscode.ExtensionContext) { | export async function activate(context: vscode.ExtensionContext) { | ||||||
|   statusItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 1000); |   statusItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 1000); | ||||||
|   statusItem.command = 'modelsim.toggleWaveMode'; // click to toggle |   // statusItem.command = 'modelsim.toggleWaveMode'; // click to toggle | ||||||
|  |  | ||||||
|  |   await vscode.commands.executeCommand('setContext', CTX, false); | ||||||
|  |  | ||||||
|   const cmd_2 = vscode.commands.registerCommand('modelsim.toggleWaveMode', async () => { |   const cmd_2 = vscode.commands.registerCommand('modelsim.toggleWaveMode', async () => { | ||||||
|     toggleWaveMode(); |     toggleWaveMode(); | ||||||
|   | |||||||
| @@ -43,116 +43,115 @@ export async function requestModuleTree( | |||||||
| export async function addWave( | export async function addWave( | ||||||
|   mode: String |   mode: String | ||||||
| ) { | ) { | ||||||
|   if (waveMode) { |   if (!waveMode) { | ||||||
|  |  | ||||||
|     const editor = vscode.window.activeTextEditor; |  | ||||||
|     if (!editor) return; |  | ||||||
|  |  | ||||||
|     const cfg = vscode.workspace.getConfiguration('modelsimWave'); |  | ||||||
|     const sharedDir = cfg.get<string>('sharedDir') || '.'; |  | ||||||
|     const topScope = cfg.get<string>('topScope') || 'sim:/test/'; // keep in sync with package.json |  | ||||||
|     const timeoutMs = cfg.get<number>('timeoutMs') ?? 5000; |  | ||||||
|  |  | ||||||
|     // Prefer last hover over caret |  | ||||||
|     const docKey = editor.document.uri.toString(); |  | ||||||
|     const hover = lastHoverByDoc.get(docKey); |  | ||||||
|     const now = Date.now(); |  | ||||||
|     const position = |  | ||||||
|       hover && now - hover.at <= HOVER_FRESH_MS ? hover.pos : editor.selection.active; |  | ||||||
|  |  | ||||||
|     const wordRange = editor.document.getWordRangeAtPosition(position); |  | ||||||
|     if (!wordRange) { |  | ||||||
|       vscode.window.showInformationMessage('Hover a signal, then press your shortcut.'); |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     const tokenText = editor.document.getText(wordRange); |  | ||||||
|     const isVariable = await looksLikeVariable(editor, position); |  | ||||||
|     if (!isVariable) { |  | ||||||
|       const choice = await vscode.window.showWarningMessage( |  | ||||||
|         `"${tokenText}" doesn’t look like a variable here. Add anyway?`, |  | ||||||
|         'Add', |  | ||||||
|         'Cancel' |  | ||||||
|       ); |  | ||||||
|       if (choice !== 'Add') return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Load module tree once per session |  | ||||||
|     if (Object.keys(moduleTree).length === 0) { |  | ||||||
|       moduleTree = await requestModuleTree(sharedDir, topScope, timeoutMs); |  | ||||||
|       console.log("Lenght: ", Object.keys(moduleTree).length); |  | ||||||
|       if (Object.keys(moduleTree).length === 0) { |  | ||||||
|         vscode.window.showErrorMessage('Failed to load module hierarchy from ModelSim.'); |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Get module name from file name (language-independent) |  | ||||||
|     const modName = path.basename(editor.document.fileName, path.extname(editor.document.fileName)); |  | ||||||
|  |  | ||||||
|     let instancePaths = |  | ||||||
|       moduleTree[modName].module ?? moduleTree[modName.toLowerCase()].module; |  | ||||||
|     if (!instancePaths || instancePaths.length === 0) { |  | ||||||
|       console.log( |  | ||||||
|         `No instance paths found for module "${modName}". ` + |  | ||||||
|           `Ensure your poller uses "find instances" and the design is loaded.` |  | ||||||
|       ); |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     // Add the token for every instance of this module (dedupe paths just in case) |  | ||||||
|     instancePaths = Array.from(new Set(instancePaths)).map(p => p.replace(/\/+/g, '/')); |  | ||||||
|  |  | ||||||
|     let instance_num = moduleTree[modName].last ?? moduleTree[modName.toLowerCase()].last; |  | ||||||
|     if (instancePaths.length > 1 && mode === 'set') { |  | ||||||
|         const input_num = await vscode.window.showInputBox({ |  | ||||||
|         title: 'Enter a number', |  | ||||||
|         prompt: 'This number will be used by the action.', |  | ||||||
|         placeHolder: moduleTree[modName].last !== undefined ? `Press Enter to reuse ${moduleTree[modName].last}` : 'e.g. 42', |  | ||||||
|         value: moduleTree[modName].last !== undefined ? String(moduleTree[modName].last) : '', |  | ||||||
|         ignoreFocusOut: true, |  | ||||||
|         validateInput: (value) => { |  | ||||||
|           if (value.trim() === '') return null; // allow Enter to reuse last |  | ||||||
|           const n = Number(value); |  | ||||||
|           if (!Number.isInteger(n)) return 'Please enter an integer.'; |  | ||||||
|           if (n < 0) return 'Please enter a non-negative integer.'; |  | ||||||
|           // optionally cap it |  | ||||||
|           if (n > 10000) return 'That’s too large (max 10000).'; |  | ||||||
|           return null; |  | ||||||
|         }, |  | ||||||
|       }); |  | ||||||
|  |  | ||||||
|       if (input_num === undefined) { |  | ||||||
|         // User hit Esc — do nothing |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
|    |  | ||||||
|       // Empty input means reuse last (if any) |  | ||||||
|       instance_num = (input_num.trim() === '' || Number(input_num) >= moduleTree[modName].module.length) && moduleTree[modName].last !== undefined ? moduleTree[modName].last : Number(input_num); |  | ||||||
|  |  | ||||||
|       moduleTree[modName].last = instance_num; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     let ok = true; |  | ||||||
|     // for (const p of instancePaths) { |  | ||||||
|     const cmd = `quietly add wave -noupdate \{${instancePaths[instance_num]}/${tokenText}\}`.replace(/\/+/g, '/'); |  | ||||||
|     console.log(cmd); |  | ||||||
|     const res = await sendAndAwait(sharedDir, cmd, timeoutMs); |  | ||||||
|     ok = ok && !!res; |  | ||||||
|     ok = ok && !!(await sendAndAwait(sharedDir, 'update', timeoutMs)); |  | ||||||
|  |  | ||||||
|     if (ok) { |  | ||||||
|       vscode.window.showInformationMessage( |  | ||||||
|         `Added "${tokenText}" for ${instancePaths.length} instance(s) of ${modName}.` |  | ||||||
|       ); |  | ||||||
|     } else { |  | ||||||
|       vscode.window.showWarningMessage('No response from ModelSim (timeout or error).'); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|   } else { |  | ||||||
|     await vscode.commands.executeCommand("workbench.action.closeActiveEditor"); |     await vscode.commands.executeCommand("workbench.action.closeActiveEditor"); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   const editor = vscode.window.activeTextEditor; | ||||||
|  |   if (!editor) return; | ||||||
|  |  | ||||||
|  |   const cfg = vscode.workspace.getConfiguration('modelsimWave'); | ||||||
|  |   const sharedDir = cfg.get<string>('sharedDir') || '.'; | ||||||
|  |   const topScope = cfg.get<string>('topScope') || 'sim:/test/'; // keep in sync with package.json | ||||||
|  |   const timeoutMs = cfg.get<number>('timeoutMs') ?? 5000; | ||||||
|  |  | ||||||
|  |   // Prefer last hover over caret | ||||||
|  |   const docKey = editor.document.uri.toString(); | ||||||
|  |   const hover = lastHoverByDoc.get(docKey); | ||||||
|  |   const now = Date.now(); | ||||||
|  |   const position = | ||||||
|  |     hover && now - hover.at <= HOVER_FRESH_MS ? hover.pos : editor.selection.active; | ||||||
|  |  | ||||||
|  |   const wordRange = editor.document.getWordRangeAtPosition(position); | ||||||
|  |   if (!wordRange) { | ||||||
|  |     vscode.window.showInformationMessage('Hover a signal, then press your shortcut.'); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   const tokenText = editor.document.getText(wordRange); | ||||||
|  |   const isVariable = await looksLikeVariable(editor, position); | ||||||
|  |   if (!isVariable) { | ||||||
|  |     const choice = await vscode.window.showWarningMessage( | ||||||
|  |       `"${tokenText}" doesn’t look like a variable here. Add anyway?`, | ||||||
|  |       'Add', | ||||||
|  |       'Cancel' | ||||||
|  |     ); | ||||||
|  |     if (choice !== 'Add') return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Load module tree once per session | ||||||
|  |   if (Object.keys(moduleTree).length === 0) { | ||||||
|  |     moduleTree = await requestModuleTree(sharedDir, topScope, timeoutMs); | ||||||
|  |     console.log("Lenght: ", Object.keys(moduleTree).length); | ||||||
|  |     if (Object.keys(moduleTree).length === 0) { | ||||||
|  |       vscode.window.showErrorMessage('Failed to load module hierarchy from ModelSim.'); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Get module name from file name (language-independent) | ||||||
|  |   const modName = path.basename(editor.document.fileName, path.extname(editor.document.fileName)); | ||||||
|  |  | ||||||
|  |   let instancePaths = | ||||||
|  |     moduleTree[modName].module ?? moduleTree[modName.toLowerCase()].module; | ||||||
|  |   if (!instancePaths || instancePaths.length === 0) { | ||||||
|  |     console.log( | ||||||
|  |       `No instance paths found for module "${modName}". ` + | ||||||
|  |         `Ensure your poller uses "find instances" and the design is loaded.` | ||||||
|  |     ); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   // Add the token for every instance of this module (dedupe paths just in case) | ||||||
|  |   instancePaths = Array.from(new Set(instancePaths)).map(p => p.replace(/\/+/g, '/')); | ||||||
|  |  | ||||||
|  |   let instance_num = moduleTree[modName].last ?? moduleTree[modName.toLowerCase()].last; | ||||||
|  |   if (instancePaths.length > 1 && mode === 'set') { | ||||||
|  |       const input_num = await vscode.window.showInputBox({ | ||||||
|  |       title: 'Enter a number', | ||||||
|  |       prompt: 'This number will be used by the action.', | ||||||
|  |       placeHolder: moduleTree[modName].last !== undefined ? `Press Enter to reuse ${moduleTree[modName].last}` : 'e.g. 42', | ||||||
|  |       value: moduleTree[modName].last !== undefined ? String(moduleTree[modName].last) : '', | ||||||
|  |       ignoreFocusOut: true, | ||||||
|  |       validateInput: (value) => { | ||||||
|  |         if (value.trim() === '') return null; // allow Enter to reuse last | ||||||
|  |         const n = Number(value); | ||||||
|  |         if (!Number.isInteger(n)) return 'Please enter an integer.'; | ||||||
|  |         if (n < 0) return 'Please enter a non-negative integer.'; | ||||||
|  |         // optionally cap it | ||||||
|  |         if (n > 10000) return 'That’s too large (max 10000).'; | ||||||
|  |         return null; | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     if (input_num === undefined) { | ||||||
|  |       // User hit Esc — do nothing | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Empty input means reuse last (if any) | ||||||
|  |     instance_num = (input_num.trim() === '' || Number(input_num) >= moduleTree[modName].module.length) && moduleTree[modName].last !== undefined ? moduleTree[modName].last : Number(input_num); | ||||||
|  |  | ||||||
|  |     moduleTree[modName].last = instance_num; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   let ok = true; | ||||||
|  |   // for (const p of instancePaths) { | ||||||
|  |   const cmd = `quietly add wave -noupdate \{${instancePaths[instance_num]}/${tokenText}\}`.replace(/\/+/g, '/'); | ||||||
|  |   console.log(cmd); | ||||||
|  |   const res = await sendAndAwait(sharedDir, cmd, timeoutMs); | ||||||
|  |   ok = ok && !!res; | ||||||
|  |   ok = ok && !!(await sendAndAwait(sharedDir, 'update', timeoutMs)); | ||||||
|  |  | ||||||
|  |   if (ok) { | ||||||
|  |     vscode.window.showInformationMessage( | ||||||
|  |       `Added "${tokenText}" for ${instancePaths.length} instance(s) of ${modName}.` | ||||||
|  |     ); | ||||||
|  |   } else { | ||||||
|  |     vscode.window.showWarningMessage('No response from ModelSim (timeout or error).'); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user