11import { 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';
33import * as _ from "lodash" ;
44import * as vscode from 'vscode' ;
5+ import { executeCommandByExec } from "@/utils/cpUtils" ;
6+ import * as fs from "fs" ;
7+ import * as path from "path" ;
58
69let topLevelTypes = [ "output" , "provider" , "resource" , "variable" , "data" ] ;
710let topLevelRegexes = topLevelTypes . map ( o => {
@@ -15,6 +18,29 @@ interface TerraformCompletionContext extends vscode.CompletionContext {
1518 resourceType ?: string ;
1619}
1720
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+
1844const TEXT_MIN_SORT = "a" ;
1945const TEXT_FILTER = " " ;
2046
@@ -76,15 +102,21 @@ export class TerraformTipsProvider implements CompletionItemProvider {
76102 // We're trying to type the exported field for the let
77103 const resourceType = parts [ 0 ] ;
78104 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 ;
86119 } ) ;
87- return result ;
88120 }
89121
90122 // Which part are we completing for?
@@ -106,12 +138,18 @@ export class TerraformTipsProvider implements CompletionItemProvider {
106138 if ( endwithEqual ) {
107139 const lineBeforeEqualSign = lineTillCurrentPosition . substring ( 0 , includeEqual ) . trim ( ) ;
108140 // 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+ } ) ;
115153 }
116154 this . resourceType = "" ;
117155 return [ ] ;
@@ -126,8 +164,14 @@ export class TerraformTipsProvider implements CompletionItemProvider {
126164 if ( parentType && parentType . type === "resource" ) {
127165 // typing a arg in resource
128166 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+ } ) ;
131175 }
132176 else if ( parentType && parentType . type !== "resource" ) {
133177 // We don't want to accidentally include some other containers stuff
@@ -242,13 +286,20 @@ export class TerraformTipsProvider implements CompletionItemProvider {
242286 if ( parts . length === 2 && parts [ 0 ] === "resource" ) {
243287 let r = parts [ 1 ] . replace ( / " / g, '' ) ;
244288 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 [ ] ;
250301 } ) ;
251- return possibleResources ;
302+
252303 }
253304 return [ ] ;
254305 }
@@ -305,4 +356,70 @@ export class TerraformTipsProvider implements CompletionItemProvider {
305356 }
306357 }
307358 }
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