Skip to content

Commit ebcaec6

Browse files
committed
Better language tools
1 parent 16fb515 commit ebcaec6

File tree

8 files changed

+232
-76
lines changed

8 files changed

+232
-76
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# vscode-db2i
22

3-
Db2 for IBM i tools is a reimplementation of the Schemas tool inside of Access Client Solutions. There is a lot to do so it will not likely be on the Marketplace any time soon.
3+
Db2 for IBM i tools provides SQL functionality to VS Code
4+
5+
* Language tools. List tables and views and their columns when writing SQL, as well as procedures and functions
6+
* Language validator (must be enabled)
7+
* Schemas tool (in preview)
48

59
![](./media/main.png)
610

@@ -10,6 +14,7 @@ See the [Projects](https://github.com/halcyon-tech/vscode-db2i/projects) tab to
1014

1115
### Contribution notes
1216

17+
* We need help with the Schemas tool views.
1318
* Each object type in the tree list has a Defintion View (by clicking on the object). Each Definition View exists in it's own folder. For example the table definition exists at `./src/panels/table`. This means other types will get their own folder for their Definition View. Views might be `./src/panels/view` and procedures might be `./src/panels/procedure`.
1419
* Each object type has a class in the `./src/database` folder. For example, the table Definition View has the class `./src/view/table.js` which has all the methods needed to fetch information for that view. You may also add other static methods which could be used for commands specific to the table. Other objects would also get their own class.
1520
* **The best example to work from is `src/panels/view/index.js`.**

package.json

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,33 @@
99
"categories": [
1010
"Other"
1111
],
12+
"publisher": "halcyontechltd",
13+
"author": {
14+
"name": "Halcyon-Tech, Liam Allan"
15+
},
1216
"activationEvents": [
13-
"onView:schemaBrowser"
17+
"onStartupFinished"
1418
],
19+
"extensionDependencies": [
20+
"halcyontechltd.code-for-ibmi"
21+
],
1522
"main": "./src/extension.js",
1623
"contributes": {
24+
"configuration": {
25+
"title": "Db2 for IBM i",
26+
"properties": {
27+
"vscode-db2i.validator": {
28+
"type": "boolean",
29+
"description": "Enable/disable the SQL validator",
30+
"default": false
31+
},
32+
"vscode-db2i.schemas": {
33+
"type": "boolean",
34+
"description": "Enable/disable the Schemas tool (preview)",
35+
"default": false
36+
}
37+
}
38+
},
1739
"viewsContainers": {
1840
"activitybar": [
1941
{

src/configuration.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
2+
const vscode = require(`vscode`);
3+
4+
module.exports = class Configuration {
5+
/**
6+
* Returns variable not specific to a host (e.g. a global config)
7+
* @param {string} prop
8+
*/
9+
static get(prop) {
10+
const globalData = vscode.workspace.getConfiguration(`vscode-db2i`);
11+
return globalData.get(prop);
12+
}
13+
}

src/database/table.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ module.exports = class Table {
2323
const content = instance.getContent();
2424

2525
return content.runSQL([
26-
`SELECT * FROM QSYS2.SYSCOLUMNS`,
26+
`SELECT * FROM QSYS2.SYSCOLUMNS2`,
2727
`WHERE TABLE_SCHEMA = '${this.schema}' AND TABLE_NAME = '${this.tableName}'`,
2828
`ORDER BY ORDINAL_POSITION`
2929
].join(` `));

src/extension.js

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
const vscode = require(`vscode`);
44
const schemaBrowser = require(`./views/schemaBrowser`);
55

6-
const languageProvider = require(`./language/provider`);
6+
const Configuration = require(`./configuration`);
77

8-
const LanguageStore = require(`./language/store`);
8+
const languageProvider = require(`./language/provider`);
99

1010
// this method is called when your extension is activated
1111
// your extension is activated the very first time the command is executed
@@ -19,16 +19,16 @@ function activate(context) {
1919
// This line of code will only be executed once when your extension is activated
2020
console.log(`Congratulations, your extension "vscode-db2i" is now active!`);
2121

22-
context.subscriptions.push(
23-
vscode.window.registerTreeDataProvider(
24-
`schemaBrowser`,
25-
new schemaBrowser(context)
26-
),
27-
);
22+
if (Configuration.get(`schemas`)) {
23+
context.subscriptions.push(
24+
vscode.window.registerTreeDataProvider(
25+
`schemaBrowser`,
26+
new schemaBrowser(context)
27+
),
28+
);
29+
}
2830

29-
LanguageStore.refresh().then(() => {
30-
languageProvider.initialise(context)
31-
});
31+
languageProvider.initialise(context);
3232
}
3333

3434
// this method is called when your extension is deactivated

src/language/provider.js

Lines changed: 109 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const vscode = require(`vscode`);
33
const { Parser } = require(`node-sql-parser`);
44

55
const Store = require(`./store`);
6+
const Configuration = require(`../configuration`);
67

78
/** @type {{[path: string]: object}} */
89
const workingAst = {}
@@ -12,42 +13,54 @@ const workingAst = {}
1213
* @param {vscode.ExtensionContext} context
1314
*/
1415
exports.initialise = async (context) => {
16+
17+
let editTimeout;
18+
const linterDiagnostics = vscode.languages.createDiagnosticCollection(`SQL Diagnostics`);
1519

1620
context.subscriptions.push(
21+
linterDiagnostics,
22+
1723
vscode.workspace.onDidChangeTextDocument(async (editor) => {
1824
const document = editor.document;
19-
const text = document.getText();
20-
21-
if (text.endsWith(`.`)) return;
2225

2326
if (document.languageId === `sql`) {
27+
clearTimeout(editTimeout);
28+
29+
const text = document.getText();
30+
if (text.endsWith(`.`)) return;
31+
2432
const parser = new Parser();
2533
try {
2634
const sqlAst = parser.astify(document.getText(), {
2735
database: `DB2`,
2836
});
2937

30-
if (sqlAst) workingAst[document.uri.path] = sqlAst;
38+
if (sqlAst) {
39+
workingAst[document.uri.path] = sqlAst;
40+
}
41+
42+
linterDiagnostics.set(document.uri, []);
3143
} catch (e) {
32-
console.log(e);
44+
const location = e.location;
45+
46+
if (Configuration.get(`validator`)) {
47+
editTimeout = setTimeout(async () => {
48+
linterDiagnostics.set(document.uri, [{
49+
message: e.message,
50+
range: new vscode.Range(location.start.line-1, location.end.column-1, location.end.line-1, location.end.column-1),
51+
severity: vscode.DiagnosticSeverity.Error,
52+
}]);
53+
}, 600);
54+
}
3355
}
3456
}
3557
}),
3658

3759
vscode.languages.registerCompletionItemProvider({language: `sql` }, {
38-
// @ts-ignore
3960
provideCompletionItems: async (document, position) => {
4061
///** @type vscode.CompletionItem[] */
4162
const items = [];
4263

43-
Store.data.routines.forEach(proc => {
44-
const item = new vscode.CompletionItem(proc.name, proc.type === `PROCEDURE` ? vscode.CompletionItemKind.Method : vscode.CompletionItemKind.Function);
45-
item.insertText = new vscode.SnippetString(`${proc.name}(${proc.parameters.map((parm, index) => `\${${index+1}:${parm.name}}`).join(`:`)})\$0`)
46-
item.detail = `${proc.schema}.${proc.name} ${proc.type}`;
47-
item.documentation = new vscode.MarkdownString(`${proc.comment} (\`${proc.externalName}\`)`);
48-
items.push(item);
49-
});
50-
5164
const ast = workingAst[document.uri.path];
5265
if (ast) {
5366
if (ast.from && ast.from.length > 0) {
@@ -61,47 +74,104 @@ exports.initialise = async (context) => {
6174

6275
return items;
6376
}
64-
})
77+
}, ` `)
6578
),
6679

6780
vscode.languages.registerCompletionItemProvider({language: `sql` }, {
6881
provideCompletionItems: async (document, position) => {
6982
///** @type vscode.CompletionItem[] */
7083
const items = [];
7184

72-
const ast = workingAst[document.uri.path];
73-
if (ast) {
74-
if (ast.from && ast.from.length > 0) {
75-
const currentPosition = new vscode.Position(position.line, position.character - 1);
76-
const range = document.getWordRangeAtPosition(currentPosition);
85+
if (!Store.hasConnection()) return [];
86+
87+
const currentPosition = new vscode.Position(position.line, position.character - 1);
88+
const range = document.getWordRangeAtPosition(currentPosition);
89+
90+
const prefix = range ? document.getText(range) : null;
91+
92+
let fallbackLookup = false;
7793

78-
if (range) {
79-
const prefix = document.getText(range);
94+
const baseAst = workingAst[document.uri.path];
8095

81-
console.log(prefix);
96+
let astList = [];
97+
if (Array.isArray(baseAst)) astList = baseAst;
98+
else if (baseAst) astList = [baseAst];
8299

83-
const definedAs =
100+
if (prefix) {
101+
for (const ast of astList) {
102+
fallbackLookup = false;
103+
104+
if (ast) {
105+
if (ast.from && ast.from.length > 0) {
106+
const definedAs =
84107
ast.from.find(f => f.as === prefix) ||
85108
ast.from.find(f => f.table === prefix);
86109

87-
if (definedAs) {
88-
if (definedAs.db) {
89-
const columns = await Store.getTable(definedAs.db, definedAs.table);
90-
91-
columns.forEach(column => {
92-
const item = new vscode.CompletionItem(column.COLUMN_NAME.toLowerCase(), vscode.CompletionItemKind.Field);
93-
item.insertText = new vscode.SnippetString(column.COLUMN_NAME.toLowerCase());
94-
item.detail = column.DATA_TYPE;
95-
item.documentation = new vscode.MarkdownString(`${column.COLUMN_TEXT} (\`${definedAs.db}.${definedAs.table}\`)`);
96-
items.push(item);
97-
});
98-
} else {
99-
// Usually indicates we're trying to find tables in a schema
100-
// TODO
110+
if (definedAs) {
111+
112+
if (definedAs.db) {
113+
const columns = await Store.getColumns(definedAs.db, definedAs.table);
114+
115+
columns.forEach(column => {
116+
const item = new vscode.CompletionItem(column.COLUMN_NAME.toLowerCase(), vscode.CompletionItemKind.Field);
117+
item.insertText = new vscode.SnippetString(column.COLUMN_NAME.toLowerCase());
118+
item.detail = column.DATA_TYPE;
119+
item.documentation = new vscode.MarkdownString(`${column.COLUMN_TEXT} (\`${definedAs.db}.${definedAs.table}\`)`);
120+
items.push(item);
121+
});
122+
} else {
123+
const objects = await Store.getObjects(prefix);
124+
125+
objects.forEach(object => {
126+
let type;
127+
128+
switch (object.TABLE_TYPE) {
129+
case `T`: type = `Table`; break;
130+
case `V`: type = `View`; break;
131+
case `P`: type = `Table`; break;
132+
}
133+
134+
const item = new vscode.CompletionItem(object.TABLE_NAME.toLowerCase(), vscode.CompletionItemKind.Struct);
135+
item.insertText = new vscode.SnippetString(object.TABLE_NAME.toLowerCase());
136+
item.detail = type;
137+
item.documentation = object.TABLE_TEXT;
138+
items.push(item);
139+
});
140+
141+
const routines = await Store.routinesAvailable(prefix);
142+
143+
if (routines) {
144+
routines
145+
.filter(proc => proc.type === `FUNCTION`)
146+
.forEach(proc => {
147+
const item = new vscode.CompletionItem(proc.name.toLowerCase(), proc.type === `PROCEDURE` ? vscode.CompletionItemKind.Method : vscode.CompletionItemKind.Function);
148+
item.insertText = new vscode.SnippetString(`${proc.name.toLowerCase()}(${proc.parameters.map((parm, index) => `\${${index+1}:${parm.name}}`).join(`, `)})\$0`)
149+
item.detail = `${proc.schema}.${proc.name} ${proc.type}`;
150+
item.documentation = new vscode.MarkdownString(`${proc.comment} (\`${proc.externalName}\`)`);
151+
items.push(item);
152+
});
153+
}
154+
}
101155
}
102156
}
157+
158+
if (ast.type && ast.type === `call`) {
159+
fallbackLookup = true;
160+
}
103161
}
104-
}
162+
163+
if (fallbackLookup) {
164+
const routines = await Store.getRoutines(prefix);
165+
166+
routines.forEach(proc => {
167+
const item = new vscode.CompletionItem(proc.name.toLowerCase(), proc.type === `PROCEDURE` ? vscode.CompletionItemKind.Method : vscode.CompletionItemKind.Function);
168+
item.insertText = new vscode.SnippetString(`${proc.name.toLowerCase()}(${proc.parameters.map((parm, index) => `\${${index+1}:${parm.name}}`).join(`:`)})\$0`)
169+
item.detail = `${proc.schema}.${proc.name} ${proc.type}`;
170+
item.documentation = new vscode.MarkdownString(`${proc.comment} (\`${proc.externalName}\`)`);
171+
items.push(item);
172+
});
173+
}
174+
};
105175
}
106176

107177
return items;

0 commit comments

Comments
 (0)