Add struct / array element parser (draft) - indetified issue no error on element out of bound
This commit is contained in:
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user