Skip to content

Commit 421ea58

Browse files
authored
Overhaul WorkspaceSymbolProvider (#772)
1 parent 84b2a99 commit 421ea58

File tree

3 files changed

+85
-78
lines changed

3 files changed

+85
-78
lines changed

docs/SettingsReference.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ The extensions in the InterSystems ObjectScript Extension Pack provide many sett
5656
| `"objectscript.ignoreInstallServerManager"` | Do not offer to install the [intersystems-community.servermanager](https://marketplace.visualstudio.com/items?itemName=intersystems-community.servermanager) extension. | `boolean` | `false` | |
5757
| `"objectscript.multilineMethodArgs"` | List method arguments on multiple lines, if the server supports it. | `boolean` | `false` | Only supported on IRIS 2019.1.2, 2020.1.1+, 2021.1.0+ and subsequent versions! On all other versions, this setting will have no effect. |
5858
| `"objectscript.overwriteServerChanges"` | Overwrite a changed server version without confirmation when importing the local file. | `boolean` | `false` | |
59-
| `"objectscript.searchAllDocTypes"` | Whether [Quick Open](https://code.visualstudio.com/docs/getstarted/tips-and-tricks#_quick-open) should search across all Studio Document types. Default is to only search classes, routines and include files. | `boolean` | `false` | |
6059
| `"objectscript.serverSideEditing"` | Allow editing code directly on the server after opening it from ObjectScript Explorer. | `boolean` | `false` | |
6160
| `"objectscript.serverSourceControl.disableOtherActionTriggers"` | Prevent server-side source control 'other action' triggers from firing. | `boolean` | `false` | |
6261
| `"objectscript.showExplorer"` | Show the ObjectScript Explorer view. | `boolean` | `true` | |

package.json

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -913,11 +913,6 @@
913913
"default": "cuk",
914914
"markdownDescription": "Compilation flags. Common compilation flags are ***b*** (compile dependent classes), ***k*** (keep generated source code) and ***u*** (skip related up-to-date documents). For descriptions of all available flags and qualifiers, click [here](https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=RCOS_vsystem#RCOS_vsystem_flags_qualifiers)."
915915
},
916-
"objectscript.searchAllDocTypes": {
917-
"type": "boolean",
918-
"default": false,
919-
"markdownDescription": "Whether [Quick Open](https://code.visualstudio.com/docs/getstarted/tips-and-tricks#_quick-open) should search across all Studio Document types. Default is to only search classes, routines and include files."
920-
},
921916
"objectscript.overwriteServerChanges": {
922917
"type": "boolean",
923918
"default": false,
Lines changed: 85 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,98 @@
11
import * as vscode from "vscode";
22
import { AtelierAPI } from "../api";
3-
import { ClassDefinition } from "../utils/classDefinition";
3+
import { currentWorkspaceFolder } from "../utils";
44
import { DocumentContentProvider } from "./DocumentContentProvider";
5-
import { config } from "../extension";
65

76
export class WorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvider {
8-
public provideWorkspaceSymbols(
9-
query: string,
10-
token: vscode.CancellationToken
11-
): vscode.ProviderResult<vscode.SymbolInformation[]> {
12-
if (query.length < 3) {
7+
private sql: string =
8+
"SELECT * FROM (" +
9+
"SELECT Name, Parent->ID AS Parent, 'method' AS Type FROM %Dictionary.MethodDefinition" +
10+
" UNION ALL %PARALLEL " +
11+
"SELECT Name, Parent->ID AS Parent, 'property' AS Type FROM %Dictionary.PropertyDefinition" +
12+
" UNION ALL %PARALLEL " +
13+
"SELECT Name, Parent->ID AS Parent, 'parameter' AS Type FROM %Dictionary.ParameterDefinition" +
14+
" UNION ALL %PARALLEL " +
15+
"SELECT Name, Parent->ID AS Parent, 'index' AS Type FROM %Dictionary.IndexDefinition" +
16+
" UNION ALL %PARALLEL " +
17+
"SELECT Name, Parent->ID AS Parent, 'foreignkey' AS Type FROM %Dictionary.ForeignKeyDefinition" +
18+
" UNION ALL %PARALLEL " +
19+
"SELECT Name, Parent->ID AS Parent, 'xdata' AS Type FROM %Dictionary.XDataDefinition" +
20+
" UNION ALL %PARALLEL " +
21+
"SELECT Name, Parent->ID AS Parent, 'query' AS Type FROM %Dictionary.QueryDefinition" +
22+
" UNION ALL %PARALLEL " +
23+
"SELECT Name, Parent->ID AS Parent, 'trigger' AS Type FROM %Dictionary.TriggerDefinition" +
24+
" UNION ALL %PARALLEL " +
25+
"SELECT Name, Parent->ID AS Parent, 'storage' AS Type FROM %Dictionary.StorageDefinition" +
26+
" UNION ALL %PARALLEL " +
27+
"SELECT Name, Parent->ID AS Parent, 'projection' AS Type FROM %Dictionary.ProjectionDefinition" +
28+
") WHERE %SQLUPPER Name %MATCHES ?";
29+
30+
public provideWorkspaceSymbols(query: string): vscode.ProviderResult<vscode.SymbolInformation[]> {
31+
if (query.length === 0) {
1332
return null;
1433
}
15-
return Promise.all([this.byStudioDocuments(query), this.byMethods(query)]).then(([documents, methods]) => [
16-
...documents,
17-
...methods,
18-
]);
19-
}
20-
21-
private getApi(): AtelierAPI {
22-
const currentFileUri = vscode.window.activeTextEditor?.document.uri;
23-
const firstFolder = vscode.workspace.workspaceFolders?.length ? vscode.workspace.workspaceFolders[0] : undefined;
24-
return new AtelierAPI(currentFileUri || firstFolder?.uri || "");
25-
}
26-
27-
private async byStudioDocuments(query: string): Promise<vscode.SymbolInformation[]> {
28-
const searchAllDocTypes = config("searchAllDocTypes");
29-
if (searchAllDocTypes) {
30-
// Note: This query could be expensive if there are too many files available across the namespaces
31-
// configured in the current vs code workspace. However, delimiting by specific file types
32-
// means custom Studio documents cannot be found. So this is a trade off
33-
query = `*${query}*`;
34-
} else {
35-
// Default is to only search classes, routines and include files
36-
query = `*${query}*.cls,*${query}*.mac,*${query}*.int,*${query}*.inc`;
34+
let pattern = "";
35+
for (let i = 0; i < query.length; i++) {
36+
const char = query.charAt(i);
37+
pattern += char === "*" || char === "?" ? `*\\${char}` : `*${char}`;
3738
}
38-
const sql = `SELECT TOP 10 Name FROM %Library.RoutineMgr_StudioOpenDialog(?,?,?,?,?,?,?)`;
39-
const api = this.getApi();
40-
const direction = "1";
41-
const orderBy = "1";
42-
const systemFiles = "1";
43-
const flat = "1";
44-
const notStudio = "0";
45-
const generated = "0";
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+
}
4670

47-
const kindFromName = (name: string) => {
48-
const nameLowerCase = name.toLowerCase();
49-
return nameLowerCase.endsWith("cls")
50-
? vscode.SymbolKind.Class
51-
: nameLowerCase.endsWith("zpm")
52-
? vscode.SymbolKind.Module
53-
: vscode.SymbolKind.File;
54-
};
55-
const data = await api.actionQuery(sql, [query, direction, orderBy, systemFiles, flat, notStudio, generated]);
56-
return data.result.content.map(({ Name }) => ({
57-
kind: kindFromName(Name),
58-
location: {
59-
uri: DocumentContentProvider.getUri(Name, undefined, api.ns),
60-
},
61-
name: Name,
62-
}));
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+
},
79+
});
80+
}
81+
return result;
82+
});
6383
}
6484

65-
private async byMethods(query: string): Promise<vscode.SymbolInformation[]> {
66-
const api = this.getApi();
67-
query = query.toUpperCase();
68-
query = `*${query}*`;
69-
const getLocation = async (className, name) => {
70-
const classDef = new ClassDefinition(className, undefined, api.ns);
71-
return classDef.getMemberLocation(name);
72-
};
73-
const sql = `
74-
SELECT TOP 10 Parent ClassName, Name FROM %Dictionary.MethodDefinition WHERE %SQLUPPER Name %MATCHES ?`;
75-
return api
76-
.actionQuery(sql, [query])
77-
.then((data): Promise<vscode.SymbolInformation>[] =>
78-
data.result.content.map(
79-
async ({ ClassName, Name }): Promise<vscode.SymbolInformation> =>
80-
new vscode.SymbolInformation(Name, vscode.SymbolKind.Method, ClassName, await getLocation(ClassName, Name))
81-
)
82-
)
83-
.then((data) => Promise.all(data));
85+
resolveWorkspaceSymbol(symbol: vscode.SymbolInformation): vscode.ProviderResult<vscode.SymbolInformation> {
86+
return vscode.commands
87+
.executeCommand<vscode.DocumentSymbol[]>("vscode.executeDocumentSymbolProvider", symbol.location.uri)
88+
.then((docSymbols) => {
89+
for (const docSymbol of docSymbols[0].children) {
90+
if (docSymbol.name === symbol.name && docSymbol.kind === symbol.kind) {
91+
symbol.location.range = docSymbol.selectionRange;
92+
break;
93+
}
94+
}
95+
return symbol;
96+
});
8497
}
8598
}

0 commit comments

Comments
 (0)