1
1
import * as vscode from "vscode" ;
2
2
import { AtelierAPI } from "../api" ;
3
- import { currentWorkspaceFolder } from "../utils" ;
4
3
import { DocumentContentProvider } from "./DocumentContentProvider" ;
5
4
6
5
export class WorkspaceSymbolProvider implements vscode . WorkspaceSymbolProvider {
@@ -27,59 +26,154 @@ export class WorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvider {
27
26
"SELECT Name, Parent->ID AS Parent, 'projection' AS Type FROM %Dictionary.ProjectionDefinition" +
28
27
") WHERE %SQLUPPER Name %MATCHES ?" ;
29
28
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 [ ] > {
31
98
if ( query . length === 0 ) {
32
99
return null ;
33
100
}
101
+ // Convert query to a %MATCHES compatible pattern
34
102
let pattern = "" ;
35
103
for ( let i = 0 ; i < query . length ; i ++ ) {
36
104
const char = query . charAt ( i ) ;
37
105
pattern += char === "*" || char === "?" ? `*\\${ char } ` : `*${ char } ` ;
38
106
}
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 ,
79
128
} ) ;
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 ;
80
133
}
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 ;
82
149
} ) ;
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 : [ ] ) ) ) ;
83
177
}
84
178
85
179
resolveWorkspaceSymbol ( symbol : vscode . SymbolInformation ) : vscode . ProviderResult < vscode . SymbolInformation > {
0 commit comments