Split Code
This commit is contained in:
		
							
								
								
									
										200
									
								
								src/wave_editor.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								src/wave_editor.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,200 @@ | ||||
| import * as vscode from 'vscode'; | ||||
| import * as path from 'path'; | ||||
|  | ||||
| import { waveMode, lastHoverByDoc, HOVER_FRESH_MS } from './extension'; | ||||
| import { sendAndAwait } from './eda_connect'; | ||||
|  | ||||
| export let moduleTree: Record<string, { module: string[], last: number }> = {}; | ||||
|  | ||||
| export async function requestModuleTree( | ||||
|   sharedDir: string, | ||||
|   topScope: string, | ||||
|   timeoutMs: number | ||||
| ): Promise<Record<string, { module: string[], last: number }>> { | ||||
|   const tree: Record<string, string[]> = {}; | ||||
|   const payload = `get_module_tree ${topScope}`; | ||||
|   console.log("Requesting ModuleTree"); | ||||
|   vscode.window.showInformationMessage('Requesting ModuleTree'); | ||||
|   const res = await sendAndAwait(sharedDir, payload, timeoutMs); | ||||
|  | ||||
|   const text = (await res) ?? ""; | ||||
|   console.log("Module Hierarchy: ", text) | ||||
|   const rx = /\{([^}]+?)\s+\(([^)]+)\)\}/g; // {<path> (<type>)} | ||||
|   const moduleTree: Record<string, { module: string[], last: number }> = {}; | ||||
|  | ||||
|   let m: RegExpExecArray | null; | ||||
|   while ((m = rx.exec(text))) { | ||||
|     const rawPath = m[1].trim();   // e.g. "/test/u_queue/u_queue_slot[0]" | ||||
|     const modType = m[2].trim();   // e.g. "queue_slot" | ||||
|  | ||||
|     if (rawPath.startsWith("/std") || rawPath.startsWith("/std::")) continue; | ||||
|     console.log("RawPath: ", rawPath); | ||||
|     if (!(rawPath === `/${topScope}` || rawPath.startsWith(`/${topScope}/`))) continue; | ||||
|     console.log("RawPath: ", rawPath); | ||||
|  | ||||
|     ((moduleTree[modType] ??= { module: [], last: 0 }).module).push(rawPath); | ||||
|   } | ||||
|  | ||||
|   console.log(moduleTree); | ||||
|  | ||||
|   return moduleTree; | ||||
| } | ||||
|  | ||||
| export async function addWave( | ||||
|   mode: String | ||||
| ) { | ||||
|   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"); | ||||
|   } | ||||
| } | ||||
|  | ||||
| async function looksLikeVariable( | ||||
|   editor: vscode.TextEditor, | ||||
|   position: vscode.Position | ||||
| ): Promise<boolean> { | ||||
|   try { | ||||
|     const symbols = await vscode.commands.executeCommand<vscode.DocumentSymbol[]>( | ||||
|       'vscode.executeDocumentSymbolProvider', | ||||
|       editor.document.uri | ||||
|     ); | ||||
|     if (!symbols || symbols.length === 0) return true; | ||||
|  | ||||
|     const flat = flattenSymbols(symbols); | ||||
|     const hit = flat | ||||
|       .filter(s => s.range.contains(position)) | ||||
|       .sort((a, b) => a.range.end.compareTo(b.range.end)) | ||||
|       .pop(); | ||||
|     if (!hit) return true; | ||||
|  | ||||
|     const variableKinds = new Set([ | ||||
|       vscode.SymbolKind.Variable, | ||||
|       vscode.SymbolKind.Field, | ||||
|       vscode.SymbolKind.Constant, | ||||
|       vscode.SymbolKind.Property, | ||||
|       vscode.SymbolKind.EnumMember | ||||
|     ]); | ||||
|     return variableKinds.has(hit.kind); | ||||
|   } catch { | ||||
|     return true; | ||||
|   } | ||||
| } | ||||
|  | ||||
| function flattenSymbols(items: vscode.DocumentSymbol[]): vscode.DocumentSymbol[] { | ||||
|   const out: vscode.DocumentSymbol[] = []; | ||||
|   const walk = (arr: vscode.DocumentSymbol[]) => { | ||||
|     for (const s of arr) { | ||||
|       out.push(s); | ||||
|       if (s.children?.length) walk(s.children); | ||||
|     } | ||||
|   }; | ||||
|   walk(items); | ||||
|   return out; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user