Skip to content

Commit d1c09ca

Browse files
authored
Provide WorkspaceSymbols for all servers in workspace (#815)
1 parent 13d3700 commit d1c09ca

File tree

1 file changed

+137
-43
lines changed

1 file changed

+137
-43
lines changed

src/providers/WorkspaceSymbolProvider.ts

Lines changed: 137 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import * as vscode from "vscode";
22
import { AtelierAPI } from "../api";
3-
import { currentWorkspaceFolder } from "../utils";
43
import { DocumentContentProvider } from "./DocumentContentProvider";
54

65
export class WorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvider {
@@ -27,59 +26,154 @@ export class WorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvider {
2726
"SELECT Name, Parent->ID AS Parent, 'projection' AS Type FROM %Dictionary.ProjectionDefinition" +
2827
") WHERE %SQLUPPER Name %MATCHES ?";
2928

30-
public provideWorkspaceSymbols(query: string): vscode.ProviderResult<vscode.SymbolInformation[]> {
29+
private sqlNoSystem: string =
30+
"SELECT dict.Name, dict.Parent, dict.Type FROM (" +
31+
"SELECT Name, Parent->ID AS Parent, 'method' AS Type FROM %Dictionary.MethodDefinition" +
32+
" UNION ALL %PARALLEL " +
33+
"SELECT Name, Parent->ID AS Parent, 'property' AS Type FROM %Dictionary.PropertyDefinition" +
34+
" UNION ALL %PARALLEL " +
35+
"SELECT Name, Parent->ID AS Parent, 'parameter' AS Type FROM %Dictionary.ParameterDefinition" +
36+
" UNION ALL %PARALLEL " +
37+
"SELECT Name, Parent->ID AS Parent, 'index' AS Type FROM %Dictionary.IndexDefinition" +
38+
" UNION ALL %PARALLEL " +
39+
"SELECT Name, Parent->ID AS Parent, 'foreignkey' AS Type FROM %Dictionary.ForeignKeyDefinition" +
40+
" UNION ALL %PARALLEL " +
41+
"SELECT Name, Parent->ID AS Parent, 'xdata' AS Type FROM %Dictionary.XDataDefinition" +
42+
" UNION ALL %PARALLEL " +
43+
"SELECT Name, Parent->ID AS Parent, 'query' AS Type FROM %Dictionary.QueryDefinition" +
44+
" UNION ALL %PARALLEL " +
45+
"SELECT Name, Parent->ID AS Parent, 'trigger' AS Type FROM %Dictionary.TriggerDefinition" +
46+
" UNION ALL %PARALLEL " +
47+
"SELECT Name, Parent->ID AS Parent, 'storage' AS Type FROM %Dictionary.StorageDefinition" +
48+
" UNION ALL %PARALLEL " +
49+
"SELECT Name, Parent->ID AS Parent, 'projection' AS Type FROM %Dictionary.ProjectionDefinition" +
50+
") AS dict, (" +
51+
"SELECT Name FROM %Library.RoutineMgr_StudioOpenDialog(?,?,?,?,?,?,?)" +
52+
") AS sod WHERE %SQLUPPER dict.Name %MATCHES ? AND {fn CONCAT(dict.Parent,'.cls')} = sod.Name";
53+
54+
private queryResultToSymbols(data: any, folderUri: vscode.Uri) {
55+
const result = [];
56+
const uris: Map<string, vscode.Uri> = new Map();
57+
for (const element of data.result.content) {
58+
const kind: vscode.SymbolKind = (() => {
59+
switch (element.Type) {
60+
case "query":
61+
case "method":
62+
return vscode.SymbolKind.Method;
63+
case "parameter":
64+
return vscode.SymbolKind.Constant;
65+
case "index":
66+
return vscode.SymbolKind.Key;
67+
case "xdata":
68+
case "storage":
69+
return vscode.SymbolKind.Struct;
70+
case "property":
71+
default:
72+
return vscode.SymbolKind.Property;
73+
}
74+
})();
75+
76+
let uri: vscode.Uri;
77+
if (uris.has(element.Parent)) {
78+
uri = uris.get(element.Parent);
79+
} else {
80+
uri = DocumentContentProvider.getUri(`${element.Parent}.cls`, undefined, undefined, undefined, folderUri);
81+
uris.set(element.Parent, uri);
82+
}
83+
84+
result.push({
85+
name: element.Name,
86+
containerName:
87+
element.Type === "foreignkey" ? "ForeignKey" : element.Type.charAt(0).toUpperCase() + element.Type.slice(1),
88+
kind,
89+
location: {
90+
uri,
91+
},
92+
});
93+
}
94+
return result;
95+
}
96+
97+
public async provideWorkspaceSymbols(query: string): Promise<vscode.SymbolInformation[]> {
3198
if (query.length === 0) {
3299
return null;
33100
}
101+
// Convert query to a %MATCHES compatible pattern
34102
let pattern = "";
35103
for (let i = 0; i < query.length; i++) {
36104
const char = query.charAt(i);
37105
pattern += char === "*" || char === "?" ? `*\\${char}` : `*${char}`;
38106
}
39-
const workspace = currentWorkspaceFolder();
40-
const api = new AtelierAPI(workspace);
41-
return api.actionQuery(this.sql, [pattern.toUpperCase() + "*"]).then((data) => {
42-
const result = [];
43-
const uris: Map<string, vscode.Uri> = new Map();
44-
for (const element of data.result.content) {
45-
const kind: vscode.SymbolKind = (() => {
46-
switch (element.Type) {
47-
case "query":
48-
case "method":
49-
return vscode.SymbolKind.Method;
50-
case "parameter":
51-
return vscode.SymbolKind.Constant;
52-
case "index":
53-
return vscode.SymbolKind.Key;
54-
case "xdata":
55-
case "storage":
56-
return vscode.SymbolKind.Struct;
57-
case "property":
58-
default:
59-
return vscode.SymbolKind.Property;
60-
}
61-
})();
62-
63-
let uri: vscode.Uri;
64-
if (uris.has(element.Parent)) {
65-
uri = uris.get(element.Parent);
66-
} else {
67-
uri = DocumentContentProvider.getUri(`${element.Parent}.cls`, workspace);
68-
uris.set(element.Parent, uri);
69-
}
70-
71-
result.push({
72-
name: element.Name,
73-
containerName:
74-
element.Type === "foreignkey" ? "ForeignKey" : element.Type.charAt(0).toUpperCase() + element.Type.slice(1),
75-
kind,
76-
location: {
77-
uri,
78-
},
107+
pattern = pattern.toUpperCase() + "*";
108+
// Filter the folders to search so we don't query the same ns on the same server twice
109+
const serversToQuery: {
110+
api: AtelierAPI;
111+
uri: vscode.Uri;
112+
system: boolean;
113+
}[] = [];
114+
for (const folder of vscode.workspace.workspaceFolders) {
115+
const folderApi = new AtelierAPI(folder.uri);
116+
const found = serversToQuery.findIndex(
117+
(server) =>
118+
server.api.config.host.toLowerCase() === folderApi.config.host.toLowerCase() &&
119+
server.api.config.port === folderApi.config.port &&
120+
server.api.config.pathPrefix.toLowerCase() === folderApi.config.pathPrefix.toLowerCase() &&
121+
server.api.config.ns.toLowerCase() === folderApi.config.ns.toLowerCase()
122+
);
123+
if (found === -1) {
124+
serversToQuery.push({
125+
api: folderApi,
126+
uri: folder.uri,
127+
system: true,
79128
});
129+
} else if (serversToQuery[found].uri.scheme.startsWith("isfs") && !folder.uri.scheme.startsWith("isfs")) {
130+
// If we have multiple folders connected to the same server and ns
131+
// and one is not isfs, keep the non-isfs one
132+
serversToQuery[found].uri = folder.uri;
80133
}
81-
return result;
134+
}
135+
serversToQuery.map((server) => {
136+
if (server.api.config.ns.toLowerCase() !== "%sys") {
137+
const found = serversToQuery.findIndex(
138+
(server2) =>
139+
server2.api.config.host.toLowerCase() === server.api.config.host.toLowerCase() &&
140+
server2.api.config.port === server.api.config.port &&
141+
server2.api.config.pathPrefix.toLowerCase() === server.api.config.pathPrefix.toLowerCase() &&
142+
server2.api.config.ns.toLowerCase() === "%sys"
143+
);
144+
if (found !== -1) {
145+
server.system = false;
146+
}
147+
}
148+
return server;
82149
});
150+
return Promise.allSettled(
151+
serversToQuery
152+
.map((server) => {
153+
// Set the system property so we don't show system items multiple times if this
154+
// workspace is connected to both the %SYS and a non-%SYS namespace on the same server
155+
if (server.api.config.ns.toLowerCase() !== "%sys") {
156+
const found = serversToQuery.findIndex(
157+
(server2) =>
158+
server2.api.config.host.toLowerCase() === server.api.config.host.toLowerCase() &&
159+
server2.api.config.port === server.api.config.port &&
160+
server2.api.config.pathPrefix.toLowerCase() === server.api.config.pathPrefix.toLowerCase() &&
161+
server2.api.config.ns.toLowerCase() === "%sys"
162+
);
163+
if (found !== -1) {
164+
server.system = false;
165+
}
166+
}
167+
return server;
168+
})
169+
.map((server) =>
170+
server.system
171+
? server.api.actionQuery(this.sql, [pattern]).then((data) => this.queryResultToSymbols(data, server.uri))
172+
: server.api
173+
.actionQuery(this.sqlNoSystem, ["*.cls", "1", "1", "0", "1", "0", "0", pattern])
174+
.then((data) => this.queryResultToSymbols(data, server.uri))
175+
)
176+
).then((results) => results.flatMap((result) => (result.status === "fulfilled" ? result.value : [])));
83177
}
84178

85179
resolveWorkspaceSymbol(symbol: vscode.SymbolInformation): vscode.ProviderResult<vscode.SymbolInformation> {

0 commit comments

Comments
 (0)