Skip to content

Commit 4065868

Browse files
authored
Merge pull request #418 from codefori/worksofliam/issue414
Support multiline fixed-format procedure names
2 parents 0ac795c + 4d3daef commit 4065868

File tree

3 files changed

+115
-34
lines changed

3 files changed

+115
-34
lines changed

language/models/fixed.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,11 @@ export function parseDLine(lineNumber, lineIndex, content) {
131131
*/
132132
export function parsePLine(content, lineNumber, lineIndex) {
133133
content = content.padEnd(80);
134-
const name = content.substr(6, 16)
134+
let name = content.substr(6, 16).trimEnd();
135+
if (name.endsWith(`...`)) {
136+
name = name.substring(0, name.length - 3);
137+
}
138+
135139
const longForm = content.substring(6).trimEnd();
136140
const potentialName = longForm.endsWith(`...`) ? calculateToken(lineNumber, lineIndex+6, longForm.substring(0, longForm.length - 3)) : undefined;
137141
const start = content[23].toUpperCase() === `B`;

language/parser.ts

Lines changed: 67 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,35 @@ export default class Parser {
203203
return objectName;
204204
};
205205

206-
let potentialName: Token|undefined;
207-
let potentialNameUsed = false;
206+
// ========
207+
// Potential name logic is used for fixed-format only.
208+
// In fixed format, symbol names can be spread over multiple lines
209+
// and we use tokens to collect each part of the name.
210+
// ========
211+
let potentialName: Token[]|undefined;
212+
const pushPotentialNameToken = (token?: Token) => {
213+
if (!potentialName) {
214+
potentialName = [];
215+
}
216+
if (token) {
217+
potentialName.push(token);
218+
}
219+
}
220+
const getPotentialNameToken = (): Token|undefined => {
221+
if (potentialName && potentialName.length > 0) {
222+
return {
223+
type: `block`,
224+
range: {
225+
line: potentialName[0].range.line,
226+
start: potentialName[0].range.start,
227+
end: potentialName[potentialName.length - 1].range.end
228+
},
229+
value: potentialName.map(p => p.value).join(``),
230+
};
231+
} else {
232+
return undefined;
233+
}
234+
}
208235

209236
let currentGroup: "structs"|"procedures"|"constants";
210237

@@ -1445,8 +1472,6 @@ export default class Parser {
14451472
}
14461473
}
14471474

1448-
potentialName = cSpec.factor1;
1449-
14501475
switch (cSpec.opcode && cSpec.opcode.value) {
14511476
case `BEGSR`:
14521477

@@ -1523,29 +1548,34 @@ export default class Parser {
15231548
const pSpec = parsePLine(line, lineNumber, lineIndex);
15241549

15251550
if (pSpec.potentialName) {
1526-
potentialName = pSpec.potentialName;
1527-
potentialNameUsed = true;
1551+
pushPotentialNameToken(pSpec.potentialName);
1552+
15281553
tokens = [pSpec.potentialName];
15291554
} else {
15301555
if (pSpec.start) {
1531-
tokens = [...pSpec.keywordsRaw, pSpec.name]
1532-
potentialName = pSpec.name && pSpec.name.value.length > 0 ? pSpec.name : potentialName;
1556+
tokens = [...pSpec.keywordsRaw, pSpec.name];
15331557

1534-
if (potentialName) {
1558+
if (pSpec.name && pSpec.name.value.length > 0) {
1559+
pushPotentialNameToken(pSpec.name);
1560+
}
1561+
1562+
const currentNameToken = getPotentialNameToken();
1563+
1564+
if (currentNameToken) {
15351565
currentItem = new Declaration(`procedure`);
15361566

1537-
currentProcName = potentialName.value;
1567+
currentProcName = currentNameToken.value;
15381568
currentItem.name = currentProcName;
15391569
currentItem.keyword = pSpec.keywords;
15401570

15411571
currentItem.position = {
15421572
path: fileUri,
1543-
range: potentialName.range
1573+
range: currentNameToken.range
15441574
};
15451575

15461576
currentItem.range = {
1547-
start: potentialName.range.line,
1548-
end: potentialName.range.line
1577+
start: currentNameToken.range.line,
1578+
end: currentNameToken.range.line
15491579
};
15501580

15511581
currentItem.scope = new Cache(undefined, true);
@@ -1573,35 +1603,38 @@ export default class Parser {
15731603
case `D`:
15741604
const dSpec = parseDLine(lineNumber, lineIndex, line);
15751605

1576-
if (dSpec.potentialName && dSpec.potentialName) {
1577-
potentialName = dSpec.potentialName;
1578-
potentialNameUsed = true;
1606+
if (dSpec.potentialName) {
1607+
pushPotentialNameToken(dSpec.potentialName);
15791608
tokens = [dSpec.potentialName];
15801609
continue;
15811610
} else {
1582-
potentialName = dSpec.name && dSpec.name.value.length > 0 ? dSpec.name : potentialName;
1611+
1612+
if (dSpec.name && dSpec.name.value.length > 0) {
1613+
pushPotentialNameToken(dSpec.name);
1614+
}
1615+
15831616
tokens = [dSpec.field, ...dSpec.keywordsRaw, dSpec.name];
15841617

1585-
const useNameToken = potentialName ? potentialName : dSpec.field;
1618+
const currentNameToken = getPotentialNameToken();
15861619

15871620
switch (dSpec.field && dSpec.field.value) {
15881621
case `C`:
15891622
currentItem = new Declaration(`constant`);
1590-
currentItem.name = potentialName ? potentialName.value : NO_NAME;
1623+
currentItem.name = currentNameToken?.value || NO_NAME;
15911624
currentItem.keyword = dSpec.keywords || {};
15921625

15931626
// TODO: line number might be different with ...?
15941627
currentItem.position = {
15951628
path: fileUri,
1596-
range: useNameToken.range
1629+
range: dSpec.field.range
15971630
};
15981631

15991632
scope.addSymbol(currentItem);
16001633
resetDefinition = true;
16011634
break;
16021635
case `S`:
16031636
currentItem = new Declaration(`variable`);
1604-
currentItem.name = potentialName ? potentialName.value : NO_NAME;
1637+
currentItem.name = currentNameToken?.value || NO_NAME;
16051638
currentItem.keyword = {
16061639
...dSpec.keywords,
16071640
...prettyTypeFromToken(dSpec),
@@ -1610,7 +1643,7 @@ export default class Parser {
16101643
// TODO: line number might be different with ...?
16111644
currentItem.position = {
16121645
path: fileUri,
1613-
range: useNameToken.range
1646+
range: currentNameToken.range
16141647
};
16151648

16161649
scope.addSymbol(currentItem);
@@ -1619,20 +1652,20 @@ export default class Parser {
16191652

16201653
case `DS`:
16211654
currentItem = new Declaration(`struct`);
1622-
currentItem.name = potentialName ? potentialName.value : NO_NAME;
1655+
currentItem.name = currentNameToken?.value || NO_NAME;
16231656
currentItem.keyword = dSpec.keywords;
16241657

16251658
currentItem.position = {
16261659
path: fileUri,
1627-
range: useNameToken.range
1660+
range: currentNameToken?.range || dSpec.field.range
16281661
};
16291662

16301663
currentItem.range = {
16311664
start: currentItem.position.range.line,
16321665
end: currentItem.position.range.line
16331666
};
16341667

1635-
expandDs(fileUri, useNameToken, currentItem);
1668+
expandDs(fileUri, currentNameToken, currentItem);
16361669

16371670
currentGroup = `structs`;
16381671
scope.addSymbol(currentItem);
@@ -1641,15 +1674,15 @@ export default class Parser {
16411674

16421675
case `PR`:
16431676
currentItem = new Declaration(`procedure`);
1644-
currentItem.name = potentialName ? potentialName.value : NO_NAME;
1677+
currentItem.name = currentNameToken?.value || NO_NAME;
16451678
currentItem.keyword = {
16461679
...prettyTypeFromToken(dSpec),
16471680
...dSpec.keywords
16481681
}
16491682

16501683
currentItem.position = {
16511684
path: fileUri,
1652-
range: useNameToken.range
1685+
range: getPotentialNameToken().range
16531686
};
16541687

16551688
currentItem.range = {
@@ -1704,27 +1737,29 @@ export default class Parser {
17041737
// This happens when it's a blank parm.
17051738
const baseToken = dSpec.type || dSpec.len;
17061739
if (!potentialName && baseToken) {
1707-
potentialName = {
1740+
pushPotentialNameToken({
17081741
...baseToken,
17091742
value: NO_NAME
1710-
}
1743+
});
17111744
}
17121745

1746+
const currentNameToken = getPotentialNameToken();
1747+
17131748
if (potentialName) {
17141749
currentSub = new Declaration(`subitem`);
1715-
currentSub.name = potentialName.value;
1750+
currentSub.name = currentNameToken?.value || NO_NAME;
17161751
currentSub.keyword = {
17171752
...prettyTypeFromToken(dSpec),
17181753
...dSpec.keywords
17191754
}
17201755

17211756
currentSub.position = {
17221757
path: fileUri,
1723-
range: potentialName.range
1758+
range: currentNameToken?.range
17241759
};
17251760

17261761
// If the parameter has likeds, add the subitems to make it a struct.
1727-
await expandDs(fileUri, potentialName, currentSub);
1762+
await expandDs(fileUri, currentNameToken, currentSub);
17281763

17291764
currentItem.subItems.push(currentSub);
17301765
currentSub = undefined;
@@ -1766,7 +1801,6 @@ export default class Parser {
17661801

17671802
if (resetDefinition) {
17681803
potentialName = undefined;
1769-
potentialNameUsed = false;
17701804

17711805
currentItem = undefined;
17721806
currentTitle = undefined;

tests/suite/fixed.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,6 +1331,49 @@ test('f spec range', async () => {
13311331
});
13321332
});
13331333

1334+
test('multiline procedure names', async () => {
1335+
const lines = [
1336+
` * Procedure (abcTest)`,
1337+
` Pabc...`,
1338+
` PTest B Export`,
1339+
` Ddnasdhhfhbd PI`,
1340+
` P E`,
1341+
``,
1342+
` * Procedure (abcxyzTest)`,
1343+
` Pabc...`,
1344+
` Pxyz...`,
1345+
` PTest B Export`,
1346+
` Ddnasdhhfhbd PI`,
1347+
` P E`,
1348+
``,
1349+
``,
1350+
` * Procedure (Test)`,
1351+
` PTest... B Export`,
1352+
` Ddnasdhhfhbd PI`,
1353+
` P E`,
1354+
``,
1355+
``,
1356+
` * Procedure (Test)`,
1357+
` PTestA B Export`,
1358+
` Ddnasdhhfhbd PI`,
1359+
` P E`,
1360+
];
1361+
1362+
const cache = await parser.getDocs(uri, lines.join(`\n`), { ignoreCache: true, withIncludes: true });
1363+
expect(cache.procedures.length).to.equal(4);
1364+
expect(cache.procedures[0].name).to.equal(`abcTest`);
1365+
expect(cache.procedures[2].name).to.equal(`Test`);
1366+
expect(cache.procedures[3].name).to.equal(`TestA`);
1367+
1368+
const abcxyzTest = cache.find(`abcxyzTest`);
1369+
expect(abcxyzTest).toBeDefined();
1370+
expect(abcxyzTest.name).to.equal(`abcxyzTest`);
1371+
1372+
const procRange = abcxyzTest.range;
1373+
expect(lines[procRange.start]).to.equal(` Pabc...`);
1374+
expect(lines[procRange.end]).to.equal(` P E`);
1375+
});
1376+
13341377
test('incorrect range on prototypes and procedures (#412)', async () => {
13351378
const lines = [
13361379
``,

0 commit comments

Comments
 (0)