Add struct / array element parser (draft) - indetified issue no error on element out of bound

This commit is contained in:
brice.boisson
2025-10-26 22:09:53 +01:00
parent b3b104844b
commit d805569ed6

View File

@@ -71,13 +71,14 @@ export async function addWave(
const position =
hover && now - hover.at <= HOVER_FRESH_MS ? hover.pos : editor.selection.active;
const wordRange = editor.document.getWordRangeAtPosition(position);
if (!wordRange) {
const expr = getExpressionAtPosition(editor.document, position);
if (!expr) {
vscode.window.showInformationMessage('Hover a signal, then press your shortcut.');
return;
}
let tokenText = expr.text;
let tokenText = editor.document.getText(wordRange);
const isVariable = await looksLikeVariable(editor, position);
if (!isVariable) {
const choice = await vscode.window.showWarningMessage(
@@ -212,3 +213,174 @@ function flattenSymbols(items: vscode.DocumentSymbol[]): vscode.DocumentSymbol[]
walk(items);
return out;
}
/**
* Expand the hovered token into its full left-only member-access expression.
*
* Examples on: foo.bar[1][i].baz[2]
* hover "foo" => "foo"
* hover "bar" => "foo.bar"
* hover "[1]" => "foo.bar[1]"
* hover "[i]" => "foo.bar[1][i]"
* hover "baz" => "foo.bar[1][i].baz"
* hover "[2]" => "foo.bar[1][i].baz[2]"
*/
export function getExpressionAtPosition(
document: vscode.TextDocument,
position: vscode.Position
): { text: string; range: vscode.Range } | undefined {
const text = document.getText();
const toff = document.offsetAt(position);
const isIdentChar = (c: string) => /[A-Za-z0-9_$]/.test(c);
const isSpace = (c: string) => /\s/.test(c);
// --- 1) Find the "segment" under the cursor: BRACKET GROUP [ ... ] first, then IDENT
let segStart = -1;
let segEnd = -1;
// Try bracket group first (fixes the [1] case)
const group = findEnclosingBracketGroup(text, toff);
if (group) {
segStart = group.start;
segEnd = group.end;
} else {
// No bracket group → try word
const identRegex = /[A-Za-z_$][A-Za-z0-9_$]*/;
const word =
document.getWordRangeAtPosition(position, identRegex) ??
(toff > 0 ? document.getWordRangeAtPosition(document.positionAt(toff - 1), identRegex) : undefined);
if (!word) return undefined;
segStart = document.offsetAt(word.start);
segEnd = document.offsetAt(word.end);
}
// --- 2) Expand LEFT to include ".ident" and bracket chains
let left = segStart;
const includePrevBracketGroup = (): boolean => {
if (left <= 0 || text[left - 1] !== ']') return false;
let depth = 1;
for (let k = left - 2; k >= 0; k--) {
const ch = text[k];
if (ch === ']') depth++;
else if (ch === '[') {
depth--;
if (depth === 0) {
left = k;
return true;
}
}
}
return false;
};
const includePrevDotIdent = (): boolean => {
let p = left;
// allow spaces before checking for '.'
while (p > 0 && isSpace(text[p - 1])) p--;
// --- dot case
if (p > 0 && text[p - 1] === '.') {
p--; // on '.'
// skip spaces before dot
while (p > 0 && isSpace(text[p - 1])) p--;
// consume any bracket groups before the dot
let q = p;
const consumePrevBracketGroupAt = (idx: number): number | null => {
if (idx <= 0 || text[idx - 1] !== ']') return null;
let d = 1;
for (let k = idx - 2; k >= 0; k--) {
const ch = text[k];
if (ch === ']') d++;
else if (ch === '[') {
d--;
if (d === 0) return k;
}
}
return null;
};
while (true) {
while (q > 0 && isSpace(text[q - 1])) q--;
const nextQ = consumePrevBracketGroupAt(q);
if (nextQ === null) break;
q = nextQ;
}
// now require identifier before them
while (q > 0 && isSpace(text[q - 1])) q--;
if (q <= 0 || !isIdentChar(text[q - 1])) return false;
let k = q - 1;
while (k >= 0 && isIdentChar(text[k])) k--;
left = k + 1;
// also include bracket groups attached even further left
while (includePrevBracketGroup()) {}
return true;
}
// --- bare ident case: only if adjacent (no whitespace)
if (left > 0 && isIdentChar(text[left - 1])) {
let k = left - 1;
while (k >= 0 && isIdentChar(text[k])) k--;
const start = k + 1;
// Stop before SV declaration keywords (logic, wire, etc.)
const token = text.slice(start, left);
const stopKeywords = new Set([
'logic','wire','reg','bit','byte','shortint','int','longint','integer','time',
'signed','unsigned','const','var','local','static','automatic'
]);
if (stopKeywords.has(token)) return false;
left = start;
while (includePrevBracketGroup()) {}
return true;
}
return false;
};
// include bracket groups and dot chains iteratively
while (includePrevBracketGroup()) {}
while (includePrevDotIdent()) {}
const range = new vscode.Range(document.positionAt(left), document.positionAt(segEnd));
const expr = text.slice(left, segEnd).trim();
if (!expr) return undefined;
return { text: expr, range };
}
/** Finds the [ ... ] group that contains `offset`, or just before it if cursor is on a ']'. */
function findEnclosingBracketGroup(text: string, offset: number): { start: number; end: number } | undefined {
let i = offset;
if (i > 0 && text[i - 1] === ']') i--; // hovering just after a ']' counts
for (let l = i; l >= 0; l--) {
const ch = text[l];
if (ch === '[') {
let depth = 1;
for (let r = l + 1; r < text.length; r++) {
const cr = text[r];
if (cr === '[') depth++;
else if (cr === ']') {
depth--;
if (depth === 0) {
if (i >= l && i <= r) {
return { start: l, end: r + 1 };
}
break;
}
}
}
} else if (ch === '\n' || ch === '\r') {
break;
}
}
return undefined;
}