1
1
import { CompletionItemProvider , TextDocument , Position , CancellationToken , CompletionItem , CompletionItemKind } from "vscode" ;
2
- import resources from '../../config/tips/tiat-resources.json' ;
2
+ // import resources from '../../config/tips/tiat-resources.json';
3
3
import * as _ from "lodash" ;
4
4
import * as vscode from 'vscode' ;
5
+ import { executeCommandByExec } from "@/utils/cpUtils" ;
6
+ import * as fs from "fs" ;
7
+ import * as path from "path" ;
5
8
6
9
let topLevelTypes = [ "output" , "provider" , "resource" , "variable" , "data" ] ;
7
10
let topLevelRegexes = topLevelTypes . map ( o => {
@@ -15,6 +18,29 @@ interface TerraformCompletionContext extends vscode.CompletionContext {
15
18
resourceType ?: string ;
16
19
}
17
20
21
+ interface Argument {
22
+ name : string ;
23
+ description : string ;
24
+ options ?: Array < string > ;
25
+ detail ?: Array < Argument > ;
26
+ }
27
+
28
+ interface Attribute {
29
+ name : string ;
30
+ description : string ;
31
+ detail ?: Array < Attribute > ;
32
+ }
33
+
34
+ interface Tips {
35
+ version : string ;
36
+ resource : {
37
+ [ key : string ] : {
38
+ args : Array < Argument > ;
39
+ attrs : Array < Attribute > ;
40
+ } ;
41
+ } ;
42
+ }
43
+
18
44
const TEXT_MIN_SORT = "a" ;
19
45
const TEXT_FILTER = " " ;
20
46
@@ -76,15 +102,21 @@ export class TerraformTipsProvider implements CompletionItemProvider {
76
102
// We're trying to type the exported field for the let
77
103
const resourceType = parts [ 0 ] ;
78
104
let resourceName = parts [ 1 ] ;
79
- let attrs = resources [ resourceType ] . attrs ;
80
- let result = _ . map ( attrs , o => {
81
- let c = new CompletionItem ( `${ o . name } (${ resourceType } )` , CompletionItemKind . Property ) ;
82
- c . detail = o . description ;
83
- c . insertText = o . name ;
84
- c . sortText = TEXT_MIN_SORT ;
85
- return c ;
105
+ loadResource ( ) . then ( tips => {
106
+ const resources = tips . resource ;
107
+ let attrs = resources [ resourceType ] . attrs ;
108
+ let result = _ . map ( attrs , o => {
109
+ let c = new CompletionItem ( `${ o . name } (${ resourceType } )` , CompletionItemKind . Property ) ;
110
+ c . detail = o . description ;
111
+ c . insertText = o . name ;
112
+ c . sortText = TEXT_MIN_SORT ;
113
+ return c ;
114
+ } ) ;
115
+ return result ;
116
+ } ) . catch ( error => {
117
+ console . error ( "Can not load resource from json." ) ;
118
+ return ;
86
119
} ) ;
87
- return result ;
88
120
}
89
121
90
122
// Which part are we completing for?
@@ -106,12 +138,18 @@ export class TerraformTipsProvider implements CompletionItemProvider {
106
138
if ( endwithEqual ) {
107
139
const lineBeforeEqualSign = lineTillCurrentPosition . substring ( 0 , includeEqual ) . trim ( ) ;
108
140
// load options
109
- const name = lineBeforeEqualSign ;
110
- const argStrs = this . findArgByName ( resources [ this . resourceType ] . args , name ) ;
111
- const options = this . getOptionsFormArg ( argStrs ) ;
112
- // clear resource type
113
- this . resourceType = "" ;
114
- return ( options ) . length ? options : [ ] ;
141
+ loadResource ( ) . then ( tips => {
142
+ const name = lineBeforeEqualSign ;
143
+ const resources = tips . resource ;
144
+ const argStrs = this . findArgByName ( resources [ this . resourceType ] . args , name ) ;
145
+ const options = this . getOptionsFormArg ( argStrs ) ;
146
+ // clear resource type
147
+ this . resourceType = "" ;
148
+ return ( options ) . length ? options : [ ] ;
149
+ } ) . catch ( error => {
150
+ console . error ( "Can not load resource from json." ) ;
151
+ return [ ] ;
152
+ } ) ;
115
153
}
116
154
this . resourceType = "" ;
117
155
return [ ] ;
@@ -126,8 +164,14 @@ export class TerraformTipsProvider implements CompletionItemProvider {
126
164
if ( parentType && parentType . type === "resource" ) {
127
165
// typing a arg in resource
128
166
const resourceType = this . getResourceTypeFromLine ( line ) ;
129
- const ret = this . getItemsForArgs ( resources [ resourceType ] . args , resourceType ) ;
130
- return ret ;
167
+ loadResource ( ) . then ( tips => {
168
+ const resources = tips . resource ;
169
+ const ret = this . getItemsForArgs ( resources [ resourceType ] . args , resourceType ) ;
170
+ return ret ;
171
+ } ) . catch ( error => {
172
+ console . error ( "Can not load resource from json." ) ;
173
+ return [ ] ;
174
+ } ) ;
131
175
}
132
176
else if ( parentType && parentType . type !== "resource" ) {
133
177
// We don't want to accidentally include some other containers stuff
@@ -242,13 +286,20 @@ export class TerraformTipsProvider implements CompletionItemProvider {
242
286
if ( parts . length === 2 && parts [ 0 ] === "resource" ) {
243
287
let r = parts [ 1 ] . replace ( / " / g, '' ) ;
244
288
let regex = new RegExp ( "^" + r ) ;
245
- let possibleResources = _ . filter ( _ . keys ( resources ) , k => {
246
- if ( regex . test ( k ) ) {
247
- return true ;
248
- }
249
- return false ;
289
+ loadResource ( ) . then ( tips => {
290
+ const resources = tips . resource ;
291
+ let possibleResources = _ . filter ( _ . keys ( resources ) , k => {
292
+ if ( regex . test ( k ) ) {
293
+ return true ;
294
+ }
295
+ return false ;
296
+ } ) ;
297
+ return possibleResources ;
298
+ } ) . catch ( error => {
299
+ console . error ( "Can not load resource from json." ) ;
300
+ return [ ] ;
250
301
} ) ;
251
- return possibleResources ;
302
+
252
303
}
253
304
return [ ] ;
254
305
}
@@ -305,4 +356,70 @@ export class TerraformTipsProvider implements CompletionItemProvider {
305
356
}
306
357
}
307
358
}
308
- }
359
+ }
360
+
361
+ async function sortJsonFiles ( dir : string ) {
362
+ const files = fs . readdirSync ( dir ) ;
363
+ const jsonFiles = files . filter ( file => path . extname ( file ) === '.json' ) ;
364
+
365
+ // import files
366
+ const versions = await Promise . all ( jsonFiles . map ( async file => {
367
+ const jsonPath = path . join ( dir , file ) ;
368
+ const json = await import ( jsonPath ) ;
369
+ const version = json . version as string ;
370
+ return {
371
+ json,
372
+ version
373
+ } ;
374
+ } ) ) ;
375
+
376
+ // sort with version desc
377
+ versions . sort ( ( a , b ) => compareVersions ( b . version , a . version ) ) ;
378
+
379
+ return versions ;
380
+ }
381
+
382
+ function compareVersions ( a , b ) {
383
+ if ( a === 'latest' ) { return 1 ; }
384
+ if ( b === 'latest' ) { return - 1 ; }
385
+ const aParts = a . split ( '.' ) . map ( Number ) ;
386
+ const bParts = b . split ( '.' ) . map ( Number ) ;
387
+
388
+ for ( let i = 0 ; i < aParts . length ; i ++ ) {
389
+ if ( aParts [ i ] > bParts [ i ] ) {
390
+ return 1 ;
391
+ } else if ( aParts [ i ] < bParts [ i ] ) {
392
+ return - 1 ;
393
+ }
394
+ }
395
+ //equal
396
+ return 0 ;
397
+ }
398
+
399
+ async function loadResource ( ) : Promise < Tips > {
400
+ let tfVersion : string ;
401
+ await executeCommandByExec ( "terraform version" ) . then ( output => {
402
+ let match = output . match ( / t e n c e n t c l o u d s t a c k \/ t e n c e n t c l o u d \ ( v \d + \. \d + \. \d + ) / ) ;
403
+
404
+ if ( match ) {
405
+ tfVersion = match [ 1 ] ;
406
+ } else {
407
+ tfVersion = "latest" ;
408
+ }
409
+ console . log ( `version:v${ tfVersion } ` ) ; //1.81.54
410
+ } ) . catch ( error => {
411
+ console . error ( `execute terraform version failed: ${ error } ` ) ;
412
+ } ) ;
413
+
414
+ const tipFiles = await sortJsonFiles ( "../../config/tips" ) ;
415
+ let result : Tips | null = null ;
416
+
417
+ tipFiles . some ( file => {
418
+ if ( compareVersions ( tfVersion , file . version ) >= 0 ) {
419
+ result = file . json as Tips ;
420
+ return true ;
421
+ }
422
+ return false ;
423
+ } ) ;
424
+ return result ;
425
+ }
0 commit comments