Skip to content

Commit 78ee5b7

Browse files
committed
Merge branch 'main' into feature/generate_test
2 parents b0bf206 + 67c9183 commit 78ee5b7

20 files changed

+591
-79
lines changed

extension/client/src/extension.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { clearTableCache, buildRequestHandlers } from './requests';
2222
import { getServerImplementationProvider, getServerSymbolProvider } from './language/serverReferences';
2323
import { checkAndWait, loadBase, onCodeForIBMiConfigurationChange } from './base';
2424
import { registerCommands } from './commands';
25+
import { setLanguageSettings } from './language/config';
2526

2627
let client: LanguageClient;
2728

@@ -101,7 +102,7 @@ export function activate(context: ExtensionContext) {
101102

102103
context.subscriptions.push(getServerSymbolProvider());
103104
context.subscriptions.push(getServerImplementationProvider());
104-
105+
context.subscriptions.push(setLanguageSettings());
105106
// context.subscriptions.push(...initBuilder(client));
106107

107108

extension/client/src/language/columnAssist.ts

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
import { commands, DecorationOptions, ExtensionContext, Range, ThemeColor, window } from 'vscode';
2+
import { commands, DecorationOptions, ExtensionContext, Range, Selection, TextDocument, ThemeColor, window } from 'vscode';
33
import * as Configuration from "../configuration";
44
import { loadBase } from '../base';
55

@@ -42,6 +42,15 @@ const getAreasForLine = (line: string, index: number) => {
4242
}
4343
}
4444

45+
function documentIsFree(document: TextDocument) {
46+
if (document.languageId === `rpgle`) {
47+
const line = document.getText(new Range(0, 0, 0, 6)).toUpperCase();
48+
return line === `**FREE`;
49+
}
50+
51+
return false;
52+
}
53+
4554
export function registerColumnAssist(context: ExtensionContext) {
4655
context.subscriptions.push(
4756
commands.registerCommand(`vscode-rpgle.assist.launchUI`, async () => {
@@ -50,7 +59,7 @@ export function registerColumnAssist(context: ExtensionContext) {
5059
const document = editor.document;
5160

5261
if (document.languageId === `rpgle`) {
53-
if (document.getText(new Range(0, 0, 0, 6)).toUpperCase() !== `**FREE`) {
62+
if (!documentIsFree(document)) {
5463
const lineNumber = editor.selection.start.line;
5564
const positionIndex = editor.selection.start.character;
5665

@@ -81,6 +90,13 @@ export function registerColumnAssist(context: ExtensionContext) {
8190
}
8291
}),
8392

93+
commands.registerCommand(`vscode-rpgle.assist.moveLeft`, () => {
94+
moveFromPosition(`left`);
95+
}),
96+
commands.registerCommand(`vscode-rpgle.assist.moveRight`, () => {
97+
moveFromPosition(`right`);
98+
}),
99+
84100
window.onDidChangeTextEditorSelection(e => {
85101
const editor = e.textEditor;
86102
if (rulerEnabled) {
@@ -92,13 +108,43 @@ export function registerColumnAssist(context: ExtensionContext) {
92108
)
93109
}
94110

111+
function moveFromPosition(direction: "left"|"right", editor = window.activeTextEditor) {
112+
if (editor && editor.document.languageId === `rpgle` && !documentIsFree(editor.document)) {
113+
const document = editor.document;
114+
const lineNumber = editor.selection.start.line;
115+
const positionIndex = editor.selection.start.character;
116+
117+
const positionsData = getAreasForLine(
118+
document.getText(new Range(lineNumber, 0, lineNumber, 100)),
119+
positionIndex
120+
);
121+
122+
if (positionsData) {
123+
let newIndex: number|undefined;
124+
if (direction === `left`) {
125+
newIndex = positionsData.active - 1;
126+
} else
127+
if (direction === `right`) {
128+
newIndex = positionsData.active + 1;
129+
}
130+
131+
if (newIndex !== undefined && newIndex >= 0 && newIndex < positionsData.specification.length) {
132+
const box = positionsData.specification[newIndex];
133+
if (box) {
134+
editor.selection = new Selection(lineNumber, box.start, lineNumber, box.start);
135+
}
136+
}
137+
}
138+
}
139+
}
140+
95141
function updateRuler(editor = window.activeTextEditor) {
96142
let clear = true;
97143

98144
if (editor) {
99145
const document = editor.document;
100146
if (document.languageId === `rpgle`) {
101-
if (document.getText(new Range(0, 0, 0, 6)).toUpperCase() !== `**FREE`) {
147+
if (!documentIsFree(document)) {
102148
const lineNumber = editor.selection.start.line;
103149
const positionIndex = editor.selection.start.character;
104150

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { languages } from "vscode";
2+
3+
export function setLanguageSettings() {
4+
return languages.setLanguageConfiguration(`rpgle`, {
5+
wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g
6+
});
7+
}

extension/server/src/providers/documentSymbols.ts

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,39 @@
11
import { DocumentSymbol, DocumentSymbolParams, Range, SymbolKind } from 'vscode-languageserver';
22
import { documents, parser, prettyKeywords } from '.';
33
import Cache from '../../../../language/models/cache';
4-
import { Position } from '../../../../language/models/DataPoints';
4+
import Declaration from '../../../../language/models/declaration';
55

66
export default async function documentSymbolProvider(handler: DocumentSymbolParams): Promise<DocumentSymbol[]> {
77
const currentPath = handler.textDocument.uri;
88
const symbols: DocumentSymbol[] = [];
99
const document = documents.get(currentPath);
1010

11+
const validRange = (def: Declaration) => {
12+
return def.range.start !== null && def.range.start >= 0 && def.range.end !== null;
13+
}
14+
15+
const expandStruct = (def: Declaration): DocumentSymbol => {
16+
let start = def.range.start || def.position.range.line;
17+
let end = def.range.end || def.position.range.line;
18+
let hasChildren = def.subItems && def.subItems.length > 0;
19+
20+
const parent = DocumentSymbol.create(
21+
def.name,
22+
prettyKeywords(def.keyword),
23+
hasChildren ? SymbolKind.Struct : SymbolKind.Property,
24+
Range.create(start, 0, end, 0),
25+
Range.create(start, 0, start, 0),
26+
);
27+
28+
if (hasChildren) {
29+
parent.children = def.subItems
30+
.filter(subitem => subitem.position && subitem.position.path === currentPath)
31+
.map(subitem => expandStruct(subitem));
32+
}
33+
34+
return parent;
35+
}
36+
1137
if (document) {
1238
const doc = await parser.getDocs(currentPath, document.getText());
1339

@@ -19,7 +45,7 @@ export default async function documentSymbolProvider(handler: DocumentSymbolPara
1945
const currentScopeDefs: DocumentSymbol[] = [];
2046

2147
scope.procedures
22-
.filter(proc => proc.position && proc.position.path === currentPath && proc.range.start && proc.range.end)
48+
.filter(proc => proc.position && proc.position.path === currentPath && validRange(proc))
2349
.forEach(proc => {
2450
const procDef = DocumentSymbol.create(
2551
proc.name,
@@ -47,8 +73,8 @@ export default async function documentSymbolProvider(handler: DocumentSymbolPara
4773
});
4874

4975
currentScopeDefs.push(
50-
...scope.subroutines.filter(sub => sub.position && sub.position.path === currentPath && sub.range.start && sub.range.end)
51-
.filter(def => def.range.start)
76+
...scope.subroutines
77+
.filter(sub => sub.position && sub.position.path === currentPath && validRange(sub))
5278
.map(def => DocumentSymbol.create(
5379
def.name,
5480
prettyKeywords(def.keyword),
@@ -137,27 +163,9 @@ export default async function documentSymbolProvider(handler: DocumentSymbolPara
137163
});
138164

139165
scope.structs
140-
.filter(struct => struct.position && struct.position.path === currentPath && struct.range.start && struct.range.end)
166+
.filter(struct => struct.position && struct.position.path === currentPath && validRange(struct))
141167
.forEach(struct => {
142-
const structDef = DocumentSymbol.create(
143-
struct.name,
144-
prettyKeywords(struct.keyword),
145-
SymbolKind.Struct,
146-
Range.create(struct.range.start!, 0, struct.range.end!, 0),
147-
Range.create(struct.range.start!, 0, struct.range.start!, 0),
148-
);
149-
150-
structDef.children = struct.subItems
151-
.filter(subitem => subitem.position && subitem.position.path === currentPath)
152-
.map(subitem => DocumentSymbol.create(
153-
subitem.name,
154-
prettyKeywords(subitem.keyword),
155-
SymbolKind.Property,
156-
Range.create(subitem.position.range.line, 0, subitem.position.range.line, 0),
157-
Range.create(subitem.position.range.line, 0, subitem.position.range.line, 0)
158-
));
159-
160-
currentScopeDefs.push(structDef);
168+
currentScopeDefs.push(expandStruct(struct));
161169
});
162170

163171
return currentScopeDefs;

extension/server/src/providers/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,21 @@ export function findFile(fileString: string, scheme = ``) {
2222

2323
export const parser = new Parser();
2424

25+
const wordMatch = /[\w\#\$@]/;
26+
2527
export function getWordRangeAtPosition(document: TextDocument, position: Position): string|undefined {
2628
const lines = document.getText().split(`\n`); // Safe to assume \n because \r is then at end of lines
2729
const line = Math.min(lines.length - 1, Math.max(0, position.line));
2830
const lineText = lines[line];
2931
const character = Math.min(lineText.length - 1, Math.max(0, position.character));
3032

3133
let startChar = character;
32-
while (startChar > 0 && !/[\s\W]/.test(lineText.charAt(startChar - 1))) {
34+
while (startChar > 0 && wordMatch.test(lineText.charAt(startChar - 1))) {
3335
startChar -= 1;
3436
}
3537

3638
let endChar = character;
37-
while (endChar < lineText.length && (!/[\s\W]/.test(lineText.charAt(endChar + 1)))) {
39+
while (endChar < lineText.length && wordMatch.test(lineText.charAt(endChar + 1))) {
3840
endChar += 1;
3941
}
4042

extension/server/src/server.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,10 @@ parser.setIncludeFileFetch(async (stringUri: string, includeString: string) => {
233233
let baseFile = parts.file || `QRPGLESRC`;
234234
let baseMember = parts.name;
235235

236+
if (parts.library && parts.library.startsWith(`*`)) {
237+
parts.library = undefined;
238+
}
239+
236240
if (parts.library) {
237241
cleanString = [
238242
``,

language/linter.ts

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import Document from "./document";
99
import { IssueRange, Rules, SelectBlock } from "./parserTypes";
1010
import Declaration from "./models/declaration";
1111
import { IRange, Token } from "./types";
12+
import { NO_NAME } from "./statement";
1213

1314
const BANNED_FROM_INCLUDES = [`NoUnreferenced`];
1415
const INCLUDE_EXTENSIONS = [`rpgleinc`, `rpgleh`];
@@ -83,8 +84,10 @@ export default class Linter {
8384

8485
const globalProcs = globalScope.procedures;
8586

86-
let inProcedure = false;
87-
let inSubroutine = false;
87+
interface ReferenceInfo {name: string, skipRules?: boolean};
88+
89+
let inProcedure: ReferenceInfo|undefined;
90+
let inSubroutine: ReferenceInfo|undefined;
8891
let inStruct: string[] = [];
8992
let inPrototype = false;
9093
let inOnExit = false;
@@ -121,7 +124,7 @@ export default class Linter {
121124

122125
for (let si = 0; si < doc.statements.length; si++) {
123126
const docStatement = doc.statements[si];
124-
const statement = docStatement.tokens;
127+
const statement = docStatement.tokens.some(t => t.type === `newline`) ? docStatement.tokens.filter(t => t.type !== `newline`) : docStatement.tokens;
125128
lineNumber = docStatement.range.line;
126129
currentIndent = docStatement.indent;
127130

@@ -368,7 +371,7 @@ export default class Linter {
368371
});
369372
}
370373

371-
inSubroutine = true;
374+
inSubroutine = {name: statement[1].value, skipRules: statement[1].type === `special`};
372375

373376
if (inProcedure) {
374377
if (rules.NoLocalSubroutines) {
@@ -378,7 +381,7 @@ export default class Linter {
378381
});
379382
}
380383
} else {
381-
if (rules.NoGlobalSubroutines) {
384+
if (rules.NoGlobalSubroutines && inSubroutine.skipRules !== true) {
382385
errors.push({
383386
offset: statement[0].range,
384387
type: `NoGlobalSubroutines`
@@ -394,10 +397,11 @@ export default class Linter {
394397
});
395398
}
396399

397-
inProcedure = true;
400+
value = statement[1].value;
401+
inProcedure = {name: value};
402+
398403
if (statement.length < 2) break;
399404
if (rules.RequiresProcedureDescription) {
400-
value = statement[1].value;
401405
const procDef = globalProcs.find(def => def.name.toUpperCase() === value.toUpperCase());
402406
if (procDef) {
403407
if (!procDef.description) {
@@ -547,22 +551,22 @@ export default class Linter {
547551

548552
switch (value) {
549553
case `ENDSR`:
554+
if (inProcedure === undefined && inSubroutine) {
555+
if (rules.NoGlobalSubroutines && inSubroutine.skipRules !== true) {
556+
errors.push({
557+
offset: statement[0].range,
558+
type: `NoGlobalSubroutines`
559+
});
560+
}
561+
}
562+
550563
if (!inSubroutine) {
551564
errors.push({
552565
offset: statement[0].range,
553566
type: `UnexpectedEnd`,
554567
});
555568
} else {
556-
inSubroutine = false;
557-
}
558-
559-
if (inProcedure === false) {
560-
if (rules.NoGlobalSubroutines) {
561-
errors.push({
562-
offset: statement[0].range,
563-
type: `NoGlobalSubroutines`
564-
});
565-
}
569+
inSubroutine = undefined;
566570
}
567571
break;
568572

@@ -572,14 +576,14 @@ export default class Linter {
572576
break;
573577

574578
case `END-PROC`:
575-
if (inProcedure === false || inSubroutine) {
579+
if (inProcedure === undefined || inSubroutine) {
576580
errors.push({
577581
offset: statement[0].range,
578582
type: `UnexpectedEnd`,
579583
});
580584
}
581585

582-
inProcedure = false;
586+
inProcedure = undefined;
583587
break;
584588
case `END-PR`:
585589
case `END-PI`:
@@ -1097,7 +1101,7 @@ export default class Linter {
10971101
// We only check the subfields if the parent is never references.
10981102

10991103
struct.subItems.forEach(subf => {
1100-
if (subf.references.length <= 1) {
1104+
if (subf.name !== NO_NAME && subf.references.length <= 1) {
11011105
// Add an error to subf
11021106
const possibleStatement = doc.getStatementByLine(subf.position.range.line);
11031107
if (possibleStatement) {

0 commit comments

Comments
 (0)