Skip to content

Commit

Permalink
Merge pull request #185 from sebjulliand/fix/exampleLoadLoop
Browse files Browse the repository at this point in the history
Examples browser cleanup
  • Loading branch information
worksofliam authored Jan 9, 2024
2 parents d279656 + c775947 commit 8df50db
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 70 deletions.
30 changes: 26 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,11 @@
"type": "string",
"description": "SQL formatting options: Lowercase or uppercase SQL identifiers",
"default": "preserve",
"enum": ["lower", "upper", "preserve"],
"enum": [
"lower",
"upper",
"preserve"
],
"enumDescriptions": [
"Format SQL identifiers in lowercase",
"Format SQL identifiers in uppercase",
Expand All @@ -86,7 +90,10 @@
"type": "string",
"description": "SQL formatting options: Lowercase or uppercase SQL keywords",
"default": "lower",
"enum": ["lower", "upper"],
"enum": [
"lower",
"upper"
],
"enumDescriptions": [
"Format reserved SQL keywords in lowercase",
"Format reserved SQL keywords in uppercase"
Expand Down Expand Up @@ -291,6 +298,12 @@
"category": "Db2 for i (Examples)",
"icon": "$(clear-all)"
},
{
"command": "vscode-db2i.examples.reload",
"title": "Reload examples",
"category": "Db2 for i",
"icon": "$(sync)"
},
{
"command": "vscode-db2i.jobManager.newJob",
"title": "New SQL Job",
Expand Down Expand Up @@ -420,6 +433,10 @@
{
"command": "vscode-db2i.jobManager.deleteConfig",
"when": "never"
},
{
"command": "vscode-db2i.examples.reload",
"when": "never"
}
],
"editor/context": [
Expand Down Expand Up @@ -466,7 +483,12 @@
"command": "vscode-db2i.examples.clearFilter",
"group": "navigation",
"when": "view == exampleBrowser"
},
},
{
"command": "vscode-db2i.examples.reload",
"group": "navigation@99",
"when": "view == exampleBrowser"
},
{
"command": "vscode-db2i.openSqlDocument",
"group": "navigation",
Expand Down Expand Up @@ -645,4 +667,4 @@
"sql-formatter": "^14.0.0",
"lru-cache": "^6.0.0"
}
}
}
8 changes: 4 additions & 4 deletions src/database/serviceInfo.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { SQLExample } from "../views/examples";
import { JobManager } from "../config";
import { SQLExample } from "../views/examples";
import Statement from "./statement";

export async function getServiceInfo(): Promise<SQLExample[]|undefined> {
export async function getServiceInfo(): Promise<SQLExample[]> {
// The reason we check for a selection is because we don't want it to prompt the user to start one here
if (JobManager.getSelection()) {
const resultSet = await JobManager.runSQL<{SERVICE_NAME: string, EXAMPLE: string}>(`select SERVICE_NAME, EXAMPLE from qsys2.services_info`);
const resultSet = await JobManager.runSQL<{ SERVICE_NAME: string, EXAMPLE: string }>(`select SERVICE_NAME, EXAMPLE from qsys2.services_info`);

return resultSet.map(r => ({
name: Statement.prettyName(r.SERVICE_NAME),
content: [r.EXAMPLE],
}))
} else {
return undefined;
return [{ name: "Please start an SQL job to load the examples", content: [""] }];
}
}
106 changes: 47 additions & 59 deletions src/views/examples/exampleBrowser.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import { EventEmitter, MarkdownString, workspace } from "vscode";
import { window } from "vscode";
import { CancellationToken, Event, ExtensionContext, ProviderResult, ThemeIcon, TreeDataProvider, TreeItem, TreeItemCollapsibleState, commands } from "vscode";
import { SQLExample, Examples, ServiceInfoLabel } from ".";
import { OSData, fetchSystemInfo } from "../../config";
import { Event, EventEmitter, ExtensionContext, MarkdownString, ThemeIcon, TreeDataProvider, TreeItem, TreeItemCollapsibleState, Uri, commands, window, workspace } from "vscode";
import { Examples, SQLExample, ServiceInfoLabel } from ".";
import { getInstance } from "../../base";
import { OSData, fetchSystemInfo } from "../../config";
import { getServiceInfo } from "../../database/serviceInfo";

const openExampleCommand = `vscode-db2i.examples.open`;

export class ExampleBrowser implements TreeDataProvider<any> {
private _onDidChangeTreeData: EventEmitter<TreeItem | undefined | null | void> = new EventEmitter<TreeItem | undefined | null | void>();
readonly onDidChangeTreeData: Event<TreeItem | undefined | null | void> = this._onDidChangeTreeData.event;
private currentFilter: string|undefined;

private currentFilter: string | undefined;

constructor(context: ExtensionContext) {
context.subscriptions.push(
Expand Down Expand Up @@ -40,6 +38,11 @@ export class ExampleBrowser implements TreeDataProvider<any> {
commands.registerCommand(`vscode-db2i.examples.clearFilter`, async () => {
this.currentFilter = undefined;
this.refresh();
}),

commands.registerCommand("vscode-db2i.examples.reload", () => {
delete Examples[ServiceInfoLabel];
this.refresh();
})
);

Expand All @@ -49,9 +52,9 @@ export class ExampleBrowser implements TreeDataProvider<any> {
// Refresh the examples when we have it, so we only display certain examples
this.refresh();
})
})
})
}

refresh() {
this._onDidChangeTreeData.fire();
}
Expand All @@ -60,74 +63,53 @@ export class ExampleBrowser implements TreeDataProvider<any> {
return element;
}

async getChildren(element?: ExampleGroupItem): Promise<any[]> {
// Unlike the bulk of the examples which are defined in views/examples/index.ts, the services examples are retrieved dynamically
if (!Examples[ServiceInfoLabel]) {
getServiceInfo().then(serviceExamples => {
Examples[ServiceInfoLabel] = serviceExamples;
this.refresh();
})
async getChildren(element?: ExampleGroupItem): Promise<SQLExampleItem[]> {
if (element) {
return element.getChildren();
}

if (this.currentFilter) {
// If there is a filter, then show all examples that include this criteria
let items: SQLExampleItem[] = [];

const upperFilter = this.currentFilter.toUpperCase();

for (const exampleName in Examples) {
items.push(
...Examples[exampleName]
.filter(example => exampleWorksForOnOS(example))
.filter(example => example.name.toUpperCase().includes(upperFilter) || example.content.some(line => line.toUpperCase().includes(upperFilter)))
.map(example => new SQLExampleItem(example))
)
else {
// Unlike the bulk of the examples which are defined in views/examples/index.ts, the services examples are retrieved dynamically
if (!Examples[ServiceInfoLabel]) {
Examples[ServiceInfoLabel] = await getServiceInfo();
}

return items;

} else {
if (element) {
return element.getChildren();
} else {
let items: ExampleGroupItem[] = [];

for (const exampleName in Examples) {
items.push(
new ExampleGroupItem(exampleName, Examples[exampleName])
)
}

return items;
if (this.currentFilter) {
// If there is a filter, then show all examples that include this criteria
const upperFilter = this.currentFilter.toUpperCase();
return Object.values(Examples)
.flatMap(examples => examples.filter(exampleWorksForOnOS))
.filter(example => example.name.toUpperCase().includes(upperFilter) || example.content.some(line => line.toUpperCase().includes(upperFilter)))
.sort(sort)
.map(example => new SQLExampleItem(example));
}
else {
return Object.entries(Examples)
.sort(([name1], [name2]) => sort(name1, name2))
.map(([name, examples]) => new ExampleGroupItem(name, examples));
}
}
}

getParent?(element: any) {
throw new Error("Method not implemented.");
}
}

class ExampleGroupItem extends TreeItem {
constructor(name: string, private group: SQLExample[]) {
super(name, TreeItemCollapsibleState.Collapsed);

this.iconPath = new ThemeIcon(`folder`);
this.iconPath = ThemeIcon.Folder;
}

getChildren(): SQLExampleItem[] {
return this.group
.filter(example => exampleWorksForOnOS(example))
.sort(sort)
.map(example => new SQLExampleItem(example));
}
}

class SQLExampleItem extends TreeItem {
constructor(example: SQLExample) {
super(example.name, TreeItemCollapsibleState.None);

this.iconPath = new ThemeIcon(`file`);

this.iconPath = ThemeIcon.File;
this.resourceUri = Uri.parse('_.sql');
this.tooltip = new MarkdownString(['```sql', example.content.join(`\n`), '```'].join(`\n`));

this.command = {
Expand All @@ -142,13 +124,19 @@ function exampleWorksForOnOS(example: SQLExample): boolean {
if (OSData) {
const myOsVersion = OSData.version;

// If this example has specific system requirements defined..
if (example.requirements && example.requirements[myOsVersion]) {
if (OSData.db2Level < example.requirements[myOsVersion]) {
return false;
}
// If this example has specific system requirements defined
if (example.requirements &&
example.requirements[myOsVersion] &&
OSData.db2Level < example.requirements[myOsVersion]) {
return false;
}
}

return true;
}

function sort(string1: string | SQLExample, string2: string | SQLExample) {
string1 = typeof string1 === "string" ? string1 : string1.name;
string2 = typeof string2 === "string" ? string2 : string2.name;
return string1.localeCompare(string2);
}
6 changes: 3 additions & 3 deletions src/views/examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ export interface SQLExample {
requirements?: ExampleSystemRequirements;
};

// Unlike the bulk of the examples defined below, the services examples are retrieved dynamically
export const ServiceInfoLabel = `IBM i (SQL) Services`;
// Unlike the bulk of the examples defined below, the services examples are retrieved dynamically
export const ServiceInfoLabel = `IBM i (SQL) Services`;

export let Examples: SQLExamplesList = {
export const Examples: SQLExamplesList = {
"Data Definition Language (DDL)": [
{
"name": "Create Schema",
Expand Down

0 comments on commit 8df50db

Please sign in to comment.