From c73a43e5ea05a7655267f49f332d18dd9c7c0574 Mon Sep 17 00:00:00 2001
From: nickyinluo <nickyinluo@tencent.com>
Date: Fri, 3 Nov 2023 22:16:46 +0800
Subject: [PATCH 1/6] workflow debug

---
 .github/workflows/pre-release.yml | 5 +++--
 .github/workflows/release.yml     | 3 ---
 package.json                      | 2 +-
 3 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml
index 276bf48..c649453 100644
--- a/.github/workflows/pre-release.yml
+++ b/.github/workflows/pre-release.yml
@@ -3,6 +3,7 @@ on:
     push:
         branches:
             - master
+            - feat/** # debug
         tags:
             - v*
             - v*.*.*
@@ -26,7 +27,7 @@ jobs:
             - run: npm test
               if: runner.os != 'Linux'
             - name: Pre release
-              if: success() && startsWith(github.ref, 'refs/tags/')
-              run: vsce package --no-yarn
+              if: success()
+              run: npm run package.patch
               env:
                   VSCE_PAT: ${{ secrets.VS_MARKETPLACE_TOKEN }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 4a922e6..bc17405 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,8 +1,5 @@
 name: release
 on:
-    push:
-        branches:
-            - master
     release:
         types:
             - created
diff --git a/package.json b/package.json
index 7b43bb6..07f8b88 100644
--- a/package.json
+++ b/package.json
@@ -235,7 +235,7 @@
     "scripts": {
         "compile": "tsc -p ./",
         "watch": "tsc -watch -p ./",
-        "package": "vsce package --no-yarn",
+        "package.patch": "vsce package patch --no-yarn",
         "release": "vsce publish --no-yarn",
         "pretest": "npm run compile && npm run lint",
         "lint": "eslint src --ext ts",

From 27c26a143c03be173ac5b047c870684482c793c8 Mon Sep 17 00:00:00 2001
From: nickyinluo <nickyinluo@tencent.com>
Date: Mon, 6 Nov 2023 11:25:13 +0800
Subject: [PATCH 2/6] 0.0.8

---
 package-lock.json | 4 ++--
 package.json      | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 16b2ecd..f3bd624 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
     "name": "vscode-tencentcloud-terraform",
-    "version": "0.0.7",
+    "version": "0.0.8",
     "lockfileVersion": 3,
     "requires": true,
     "packages": {
         "": {
             "name": "vscode-tencentcloud-terraform",
-            "version": "0.0.7",
+            "version": "0.0.8",
             "license": "MIT",
             "dependencies": {
                 "@types/jsdom": "^21.1.4",
diff --git a/package.json b/package.json
index 07f8b88..2e6dcd3 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
     "name": "vscode-tencentcloud-terraform",
     "displayName": "Tencent Cloud Terraform",
     "description": "VS Code extension for developing with Terraform on Tencent Cloud",
-    "version": "0.0.7",
+    "version": "0.0.8",
     "license": "MIT",
     "publisher": "Tencent-Cloud",
     "icon": "images/tc-tf-logo.png",

From 34f93897de2cfdf4d0ae38f8e09a0141d99e1041 Mon Sep 17 00:00:00 2001
From: nickyinluo <nickyinluo@tencent.com>
Date: Mon, 6 Nov 2023 11:25:35 +0800
Subject: [PATCH 3/6] 0.0.9

---
 package-lock.json | 4 ++--
 package.json      | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index f3bd624..4ea018f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
     "name": "vscode-tencentcloud-terraform",
-    "version": "0.0.8",
+    "version": "0.0.9",
     "lockfileVersion": 3,
     "requires": true,
     "packages": {
         "": {
             "name": "vscode-tencentcloud-terraform",
-            "version": "0.0.8",
+            "version": "0.0.9",
             "license": "MIT",
             "dependencies": {
                 "@types/jsdom": "^21.1.4",
diff --git a/package.json b/package.json
index 2e6dcd3..a77bac9 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
     "name": "vscode-tencentcloud-terraform",
     "displayName": "Tencent Cloud Terraform",
     "description": "VS Code extension for developing with Terraform on Tencent Cloud",
-    "version": "0.0.8",
+    "version": "0.0.9",
     "license": "MIT",
     "publisher": "Tencent-Cloud",
     "icon": "images/tc-tf-logo.png",

From 9f88cbab767988950548d334a43016918a0c8002 Mon Sep 17 00:00:00 2001
From: nickyinluo <nickyinluo@tencent.com>
Date: Mon, 6 Nov 2023 17:35:33 +0800
Subject: [PATCH 4/6] 0.0.10

---
 package-lock.json | 4 ++--
 package.json      | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 4ea018f..63e2b6e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
     "name": "vscode-tencentcloud-terraform",
-    "version": "0.0.9",
+    "version": "0.0.10",
     "lockfileVersion": 3,
     "requires": true,
     "packages": {
         "": {
             "name": "vscode-tencentcloud-terraform",
-            "version": "0.0.9",
+            "version": "0.0.10",
             "license": "MIT",
             "dependencies": {
                 "@types/jsdom": "^21.1.4",
diff --git a/package.json b/package.json
index a77bac9..fe84fbd 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
     "name": "vscode-tencentcloud-terraform",
     "displayName": "Tencent Cloud Terraform",
     "description": "VS Code extension for developing with Terraform on Tencent Cloud",
-    "version": "0.0.9",
+    "version": "0.0.10",
     "license": "MIT",
     "publisher": "Tencent-Cloud",
     "icon": "images/tc-tf-logo.png",

From b1e1d896e49812012c65db0e5563da4c08360e90 Mon Sep 17 00:00:00 2001
From: nickyinluo <nickyinluo@tencent.com>
Date: Fri, 10 Nov 2023 23:18:18 +0800
Subject: [PATCH 5/6] tips options demo

---
 .github/.DS_Store                         | Bin 0 -> 8196 bytes
 .github/workflows/pr-check.yml            |  32 +++++++
 .vscode/launch.json                       |  76 +++++++++------
 .vscode/tasks.json                        |  14 +++
 config/tips/tiat-resources.json           |  60 ++++++++++--
 package.json                              |   2 +-
 src/autocomplete/TerraformTipsProvider.ts | 107 ++++++++++++++++++++--
 src/extension.ts                          |   8 +-
 8 files changed, 252 insertions(+), 47 deletions(-)
 create mode 100644 .github/.DS_Store
 create mode 100644 .github/workflows/pr-check.yml

diff --git a/.github/.DS_Store b/.github/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..6fd6773577aab747ec0bf0cb42124ad516e10eb2
GIT binary patch
diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml
new file mode 100644
index 0000000..e1a9887
--- /dev/null
+++ b/.github/workflows/pr-check.yml
@@ -0,0 +1,32 @@
+name: pr-check
+on:
+  pull_request:
+    paths:
+      - '**.yml'
+      - 'src/**'
+      - 'config/**'
+permissions: write-all
+
+jobs:
+  pr-check:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v3
+      - run: echo "ok"
+      - name: Install Node.js
+        uses: actions/setup-node@v3
+        with:
+          node-version: 16.x
+      - name: Install the vsce npm package
+        run: |
+          npm install
+          npm install -g vsce
+      - name: Run headless test
+        uses: coactions/setup-xvfb@v1
+        if: runner.os == 'Linux'
+        with:
+          run: xvfb-run -a npm test
+      - name: Run test
+        run: npm test
+        if: runner.os != 'Linux'
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 0a7f04a..f10838a 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -3,35 +3,51 @@
 // Hover to view descriptions of existing attributes.
 // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
 {
-	"version": "0.2.0",
-	"configurations": [
-		{
-			"name": "Run Extension",
-			"type": "extensionHost",
-			"request": "launch",
-			"args": [
-				"--extensionDevelopmentPath=${workspaceFolder}"
-			],
-			"outFiles": [
-				"${workspaceFolder}/out/**/*.js"
-			],
-			"preLaunchTask": "${defaultBuildTask}",
-			"env": {
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "Run Extension",
+            "type": "extensionHost",
+            "request": "launch",
+            "args": [
+                "--extensionDevelopmentPath=${workspaceFolder}"
+            ],
+            "outFiles": [
+                "${workspaceFolder}/out/**/*.js"
+            ],
+            "preLaunchTask": "${defaultBuildTask}",
+            "env": {
                 "DEBUG": "true" // <---- EXAMPLE ENVIRONMENT VARIABLE
             }
-		},
-		{
-			"name": "Extension Tests",
-			"type": "extensionHost",
-			"request": "launch",
-			"args": [
-				"--extensionDevelopmentPath=${workspaceFolder}",
-				"--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
-			],
-			"outFiles": [
-				"${workspaceFolder}/out/test/**/*.js"
-			],
-			"preLaunchTask": "${defaultBuildTask}"
-		}
-	]
-}
+        },
+        {
+            "name": "Debug Extension",
+            "type": "extensionHost",
+            "request": "launch",
+            "args": [
+                "--extensionDevelopmentPath=${workspaceFolder}"
+            ],
+            "outFiles": [
+                "${workspaceFolder}/out/**/*.js"
+            ],
+            "preLaunchTask": "compile_build",
+            "env": {
+                "DEBUG": "true"
+            },
+            "sourceMaps": true
+        },
+        {
+            "name": "Extension Tests",
+            "type": "extensionHost",
+            "request": "launch",
+            "args": [
+                "--extensionDevelopmentPath=${workspaceFolder}",
+                "--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
+            ],
+            "outFiles": [
+                "${workspaceFolder}/out/test/**/*.js"
+            ],
+            "preLaunchTask": "${defaultBuildTask}"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 3b17e53..5f1e27e 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -15,6 +15,20 @@
 				"kind": "build",
 				"isDefault": true
 			}
+        },
+        {
+            "label": "compile_build",
+            "type": "npm",
+            "script": "esbuild-watch",
+            "problemMatcher": "$tsc-watch",
+            "isBackground": true,
+            "presentation": {
+                "reveal": "never"
+            },
+            "group": {
+                "kind": "build",
+                "isDefault": true
+            }
 		}
 	]
 }
diff --git a/config/tips/tiat-resources.json b/config/tips/tiat-resources.json
index a18fd55..2cba348 100644
--- a/config/tips/tiat-resources.json
+++ b/config/tips/tiat-resources.json
@@ -14981,7 +14981,12 @@
             },
             {
                 "name": "allocate_public_ip",
-                "description": "- (Optional, Bool, ForceNew) Associate a public IP address with an instance in a VPC or Classic. Boolean value, Default is false."
+                "description": "- (Optional, Bool, ForceNew) Associate a public IP address with an instance in a VPC or Classic. Boolean value, Default is false.",
+                "default": false,
+                "options": [
+                    true,
+                    false
+                ]
             },
             {
                 "name": "bandwidth_package_id",
@@ -15005,7 +15010,12 @@
             },
             {
                 "name": "disable_api_termination",
-                "description": "- (Optional, Bool) Whether the termination protection is enabled. Default is false. If set true, which means that this instance can not be deleted by an API action."
+                "description": "- (Optional, Bool) Whether the termination protection is enabled. Default is false. If set true, which means that this instance can not be deleted by an API action.",
+                "default": false,
+                "options": [
+                    true,
+                    false
+                ]
             },
             {
                 "name": "disable_monitor_service",
@@ -15029,11 +15039,24 @@
             },
             {
                 "name": "instance_charge_type_prepaid_renew_flag",
-                "description": "- (Optional, String) Auto renewal flag. Valid values: NOTIFY_AND_AUTO_RENEW: notify upon expiration and renew automatically, NOTIFY_AND_MANUAL_RENEW: notify upon expiration but do not renew automatically, DISABLE_NOTIFY_AND_MANUAL_RENEW: neither notify upon expiration nor renew automatically. Default value: NOTIFY_AND_MANUAL_RENEW. If this parameter is specified as NOTIFY_AND_AUTO_RENEW, the instance will be automatically renewed on a monthly basis if the account balance is sufficient. NOTE: it only works when instance_charge_type is set to PREPAID."
+                "description": "- (Optional, String) Auto renewal flag. Valid values: NOTIFY_AND_AUTO_RENEW: notify upon expiration and renew automatically, NOTIFY_AND_MANUAL_RENEW: notify upon expiration but do not renew automatically, DISABLE_NOTIFY_AND_MANUAL_RENEW: neither notify upon expiration nor renew automatically. Default value: NOTIFY_AND_MANUAL_RENEW. If this parameter is specified as NOTIFY_AND_AUTO_RENEW, the instance will be automatically renewed on a monthly basis if the account balance is sufficient. NOTE: it only works when instance_charge_type is set to PREPAID.",
+                "default": "NOTIFY_AND_MANUAL_RENEW",
+                "options": [
+                    "NOTIFY_AND_AUTO_RENEW",
+                    "NOTIFY_AND_MANUAL_RENEW",
+                    "DISABLE_NOTIFY_AND_MANUAL_RENEW"
+                ]
             },
             {
                 "name": "instance_charge_type",
-                "description": "- (Optional, String) The charge type of instance. Valid values are PREPAID, POSTPAID_BY_HOUR, SPOTPAID and CDHPAID. The default is POSTPAID_BY_HOUR. Note: TencentCloud International only supports POSTPAID_BY_HOUR and CDHPAID. PREPAID instance may not allow to delete before expired. SPOTPAID instance must set spot_instance_type and spot_max_price at the same time. CDHPAID instance must set cdh_instance_type and cdh_host_id."
+                "description": "- (Optional, String) The charge type of instance. Valid values are PREPAID, POSTPAID_BY_HOUR, SPOTPAID and CDHPAID. The default is POSTPAID_BY_HOUR. Note: TencentCloud International only supports POSTPAID_BY_HOUR and CDHPAID. PREPAID instance may not allow to delete before expired. SPOTPAID instance must set spot_instance_type and spot_max_price at the same time. CDHPAID instance must set cdh_instance_type and cdh_host_id.",
+                "default": "POSTPAID_BY_HOUR",
+                "options": [
+                    "PREPAID",
+                    "POSTPAID_BY_HOUR",
+                    "SPOTPAID",
+                    "CDHPAID"
+                ]
             },
             {
                 "name": "instance_count",
@@ -15049,7 +15072,14 @@
             },
             {
                 "name": "internet_charge_type",
-                "description": "- (Optional, String) Internet charge type of the instance, Valid values are BANDWIDTH_PREPAID, TRAFFIC_POSTPAID_BY_HOUR, BANDWIDTH_POSTPAID_BY_HOUR and BANDWIDTH_PACKAGE. If not set, internet charge type are consistent with the cvm charge type by default. This value takes NO Effect when changing and does not need to be set when allocate_public_ip is false."
+                "description": "- (Optional, String) Internet charge type of the instance, Valid values are BANDWIDTH_PREPAID, TRAFFIC_POSTPAID_BY_HOUR, BANDWIDTH_POSTPAID_BY_HOUR and BANDWIDTH_PACKAGE. If not set, internet charge type are consistent with the cvm charge type by default. This value takes NO Effect when changing and does not need to be set when allocate_public_ip is false.",
+                "default": "",
+                "options": [
+                    "BANDWIDTH_PREPAID",
+                    "TRAFFIC_POSTPAID_BY_HOUR",
+                    "BANDWIDTH_POSTPAID_BY_HOUR",
+                    "BANDWIDTH_PACKAGE"
+                ]
             },
             {
                 "name": "internet_max_bandwidth_out",
@@ -15105,7 +15135,12 @@
             },
             {
                 "name": "stopped_mode",
-                "description": "- (Optional, String) Billing method of a pay-as-you-go instance after shutdown. Available values: KEEP_CHARGING,STOP_CHARGING. Default KEEP_CHARGING."
+                "description": "- (Optional, String) Billing method of a pay-as-you-go instance after shutdown. Available values: KEEP_CHARGING,STOP_CHARGING. Default KEEP_CHARGING.",
+                "default": "KEEP_CHARGING",
+                "options": [
+                    "KEEP_CHARGING",
+                    "STOP_CHARGING"
+                ]
             },
             {
                 "name": "subnet_id",
@@ -15121,7 +15156,18 @@
             },
             {
                 "name": "system_disk_type",
-                "description": "- (Optional, String) System disk type. For more information on limits of system disk types, see Storage Overview. Valid values: LOCAL_BASIC: local disk, LOCAL_SSD: local SSD disk, CLOUD_BASIC: cloud disk, CLOUD_SSD: cloud SSD disk, CLOUD_PREMIUM: Premium Cloud Storage, CLOUD_BSSD: Basic SSD, CLOUD_HSSD: Enhanced SSD, CLOUD_TSSD: Tremendous SSD. NOTE: If modified, the instance may force stop."
+                "description": "- (Optional, String) System disk type. For more information on limits of system disk types, see Storage Overview. Valid values: LOCAL_BASIC: local disk, LOCAL_SSD: local SSD disk, CLOUD_BASIC: cloud disk, CLOUD_SSD: cloud SSD disk, CLOUD_PREMIUM: Premium Cloud Storage, CLOUD_BSSD: Basic SSD, CLOUD_HSSD: Enhanced SSD, CLOUD_TSSD: Tremendous SSD. NOTE: If modified, the instance may force stop.",
+                "default": "",
+                "options": [
+                    "LOCAL_BASIC",
+                    "LOCAL_SSD",
+                    "CLOUD_BASIC",
+                    "CLOUD_SSD",
+                    "CLOUD_PREMIUM",
+                    "CLOUD_BSSD",
+                    "CLOUD_HSSD",
+                    "CLOUD_TSSD"
+                ]
             },
             {
                 "name": "tags",
diff --git a/package.json b/package.json
index fe84fbd..99051d4 100644
--- a/package.json
+++ b/package.json
@@ -240,7 +240,7 @@
         "pretest": "npm run compile && npm run lint",
         "lint": "eslint src --ext ts",
         "test": "npm run compile",
-        "vscode:prepublish": "npm run esbuild-base -- --minify",
+        "vscode:prepublish": "npm run esbuild-base -- --sourcemap",
         "esbuild-base": "esbuild ./src/extension.ts --bundle --outfile=out/main.js --external:vscode --format=cjs --platform=node",
         "esbuild": "npm run esbuild-base -- --sourcemap",
         "esbuild-watch": "npm run esbuild-base -- --sourcemap --watch",
diff --git a/src/autocomplete/TerraformTipsProvider.ts b/src/autocomplete/TerraformTipsProvider.ts
index 78efb47..10edf68 100644
--- a/src/autocomplete/TerraformTipsProvider.ts
+++ b/src/autocomplete/TerraformTipsProvider.ts
@@ -1,6 +1,7 @@
 import { CompletionItemProvider, TextDocument, Position, CancellationToken, CompletionItem, CompletionItemKind } from "vscode";
 import resources from '../../config/tips/tiat-resources.json';
 import * as _ from "lodash";
+import * as vscode from 'vscode';
 
 var topLevelTypes = ["output", "provider", "resource", "variable", "data"];
 var topLevelRegexes = topLevelTypes.map(o => {
@@ -10,12 +11,18 @@ var topLevelRegexes = topLevelTypes.map(o => {
     };
 });
 
+interface TerraformCompletionContext extends vscode.CompletionContext {
+    resourceType?: string;
+}
+
 export class TerraformTipsProvider implements CompletionItemProvider {
     document: TextDocument;
     position: Position;
     token: CancellationToken;
 
-    public provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken): CompletionItem[] {
+
+
+    public provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken, context: TerraformCompletionContext): CompletionItem[] {
         this.document = document;
         this.position = position;
         this.token = token;
@@ -48,7 +55,7 @@ export class TerraformTipsProvider implements CompletionItemProvider {
             }
             else if (parts.length === 2) {
                 // We're trying to type the resource name
-                var resourceType = parts[0];
+                const resourceType = parts[0];
 
                 // Get a list of all the names for this resource type
                 var names = this.getNamesForResourceType(document, resourceType);
@@ -56,7 +63,7 @@ export class TerraformTipsProvider implements CompletionItemProvider {
             }
             else if (parts.length === 3) {
                 // We're trying to type the exported field for the var
-                var resourceType = parts[0];
+                const resourceType = parts[0];
                 var resourceName = parts[1];
                 var attrs = resources[resourceType].attrs;
                 var result = _.map(attrs, o => {
@@ -78,12 +85,30 @@ export class TerraformTipsProvider implements CompletionItemProvider {
             return this.getHintsForStrings(possibleResources);
         }
 
+        // type '='
+        if (lineTillCurrentPosition.endsWith('=') && context.resourceType) {
+
+            // const completionItems: CompletionItem[] = [
+            //     new CompletionItem('Option1', CompletionItemKind.Value),
+            //     new CompletionItem('Option2', CompletionItemKind.Value)
+            // ];
+
+            const match = lineTillCurrentPosition.match(/(\w+)\s*=/);
+            if (match) {
+                // load options
+                const name = match[1];
+                const strs = this.findArgByName(resources[context.resourceType].args, name);
+                return this.packOptionsFormStrings(strs);
+            }
+            return [];
+        }
+
         // Check if we're in a resource definition
         for (var i = position.line - 1; i >= 0; i--) {
             let line = document.lineAt(i).text;
             let parentType = this.getParentType(line);
             if (parentType && parentType.type === "resource") {
-                let resourceType = this.getResourceTypeFromLine(line);
+                const resourceType = this.getResourceTypeFromLine(line);
                 let ret = this.getItemsForArgs(resources[resourceType].args, resourceType);
                 return ret;
             }
@@ -96,6 +121,31 @@ export class TerraformTipsProvider implements CompletionItemProvider {
         return [];
     }
 
+    findResourceType(document: vscode.TextDocument, position: vscode.Position): string | undefined {
+        const resourceRegex = /resource\s+"([^"]+)"\s+"[^"]+"\s+\{/;
+
+        for (let i = position.line - 1; i >= 0; i--) {
+            const line = document.lineAt(i).text;
+            const match = line.match(resourceRegex);
+
+            if (match) {
+                return match[1];
+            }
+        }
+
+        return undefined;
+    }
+
+    packOptionsFormStrings(strings: string[]): CompletionItem[] {
+        return _.map(strings, s => {
+            return new CompletionItem(s, CompletionItemKind.Enum);
+        });
+    }
+
+    findArgByName(args: any, name: string): any {
+        return args.find((arg) => arg.name === name);
+    }
+
     getNamesForResourceType(document: TextDocument, resourceType: string): string[] {
         var r = new RegExp('resource "' + resourceType + '" "([a-zA-Z0-9\-_]+)"');
         var found = [];
@@ -127,7 +177,7 @@ export class TerraformTipsProvider implements CompletionItemProvider {
 
     isTopLevelType(line: string): boolean {
         for (var i = 0; i < topLevelTypes.length; i++) {
-            var resourceType = topLevelTypes[i];
+            let resourceType = topLevelTypes[i];
             if (resourceType.indexOf(line) === 0) {
                 return true;
             }
@@ -137,7 +187,7 @@ export class TerraformTipsProvider implements CompletionItemProvider {
 
     getTopLevelType(line: string): CompletionItem[] {
         for (var i = 0; i < topLevelTypes.length; i++) {
-            var resourceType = topLevelTypes[i];
+            let resourceType = topLevelTypes[i];
             if (resourceType.indexOf(line) === 0) {
                 return [new CompletionItem(resourceType, CompletionItemKind.Enum)];
             }
@@ -200,9 +250,50 @@ export class TerraformTipsProvider implements CompletionItemProvider {
     getItemsForArgs(args, type) {
         return _.map(args, o => {
             let c = new CompletionItem(`${o.name} (${type})`, CompletionItemKind.Property);
-            c.detail = o.description;
-            c.insertText = o.name;
+            let text = o.name;
+            if (o.default) {
+                text = text + ' = ' + o.default;
+            }
+            let desc = o.description;
+            if (o.options && o.options.length > 0) {
+                let options = "";
+                o.options.prototype.forEach(oo => {
+                    options = options + oo + ',';
+                });
+                desc = 'Optional Values: ' + options;
+            }
+            c.insertText = text;
+            c.detail = desc;
             return c;
         });
     }
+
+    public handleCharacterEvent(event: vscode.TextDocumentChangeEvent) {
+        const activeEditor = vscode.window.activeTextEditor;
+
+        if (!activeEditor || event.document !== activeEditor.document) {
+            return;
+        }
+
+        const changes = event.contentChanges[0];
+        if (changes.text === '=') {
+            const position = activeEditor.selection.active;
+            const resourceType = this.findResourceType(event.document, position);
+
+            if (resourceType) {
+                setTimeout(() => {
+                    const cancellationTokenSource = new vscode.CancellationTokenSource();
+                    const context: TerraformCompletionContext = {
+                        triggerKind: vscode.CompletionTriggerKind.Invoke,
+                        triggerCharacter: undefined,
+                        resourceType,
+                    };
+                    this.provideCompletionItems(event.document, position, cancellationTokenSource.token, context);
+                    vscode.commands.executeCommand('editor.action.triggerSuggest');
+                }, 10);
+            }
+        }
+    }
+
+
 }
\ No newline at end of file
diff --git a/src/extension.ts b/src/extension.ts
index b049bd1..6ef888d 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -95,7 +95,13 @@ export async function activate(context: vscode.ExtensionContext) {
 
     // auto-complete
     console.log('activate the auto complete(snippets and lint) feature');
-    context.subscriptions.push(vscode.languages.registerCompletionItemProvider(TF_MODE, new TerraformTipsProvider(), '.'));
+    const tipsProvider = new TerraformTipsProvider();
+    context.subscriptions.push(vscode.languages.registerCompletionItemProvider(TF_MODE, tipsProvider, '.'));
+    context.subscriptions.push(
+        vscode.workspace.onDidChangeTextDocument((event) => {
+            tipsProvider.handleCharacterEvent(event);
+        })
+    );
     context.subscriptions.push(vscode.languages.registerDefinitionProvider(TF_MODE, new TerraformResDocProvider()));
 
     // import-resource

From fbf8e986d91e5d6c22d021558be41f92a7ebcee9 Mon Sep 17 00:00:00 2001
From: nickyinluo <nickyinluo@tencent.com>
Date: Mon, 13 Nov 2023 21:16:26 +0800
Subject: [PATCH 6/6] feat:support tips of options

---
 src/autocomplete/TerraformTipsProvider.ts | 174 ++++++++++------------
 src/extension.ts                          |   2 +-
 2 files changed, 80 insertions(+), 96 deletions(-)

diff --git a/src/autocomplete/TerraformTipsProvider.ts b/src/autocomplete/TerraformTipsProvider.ts
index 10edf68..a97ae8d 100644
--- a/src/autocomplete/TerraformTipsProvider.ts
+++ b/src/autocomplete/TerraformTipsProvider.ts
@@ -3,8 +3,8 @@ import resources from '../../config/tips/tiat-resources.json';
 import * as _ from "lodash";
 import * as vscode from 'vscode';
 
-var topLevelTypes = ["output", "provider", "resource", "variable", "data"];
-var topLevelRegexes = topLevelTypes.map(o => {
+let topLevelTypes = ["output", "provider", "resource", "variable", "data"];
+let topLevelRegexes = topLevelTypes.map(o => {
     return {
         type: o,
         regex: new RegExp(o + ' "[A-Za-z0-9\-_]+" "[A-Za-z0-9\-_]*" \{')
@@ -19,8 +19,7 @@ export class TerraformTipsProvider implements CompletionItemProvider {
     document: TextDocument;
     position: Position;
     token: CancellationToken;
-
-
+    resourceType: string | null = null;
 
     public provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken, context: TerraformCompletionContext): CompletionItem[] {
         this.document = document;
@@ -28,8 +27,8 @@ export class TerraformTipsProvider implements CompletionItemProvider {
         this.token = token;
 
         // Check if we're on the top level
-        let lineText = document.lineAt(position.line).text;
-        let lineTillCurrentPosition = lineText.substr(0, position.character);
+        const lineText = document.lineAt(position.line).text;
+        const lineTillCurrentPosition = lineText.substring(0, position.character);
 
         // Are we trying to type a resource type?
         if (this.isTopLevelType(lineTillCurrentPosition)) {
@@ -39,16 +38,16 @@ export class TerraformTipsProvider implements CompletionItemProvider {
         // Are we trying to type a variable?
         if (this.isTypingTfCode(lineTillCurrentPosition)) {
             // These variables should always just have 3 parts, resource type, resource name, exported field
-            var varString = this.getVariableString(lineTillCurrentPosition);
-            var parts = varString.split(".");
+            let varString = this.getVariableString(lineTillCurrentPosition);
+            let parts = varString.split(".");
 
             if (parts.length === 1) {
                 // We're trying to type the resource type
-                var resourceTypePrefix = parts[1];
+                let resourceTypePrefix = parts[1];
 
                 // Get a list of all the resource types we've defined in this file
-                var definedResourceTypes = this.getDefinedResourceTypes(document);
-                var finalResourceTypes = _.filter(definedResourceTypes, o => (o.indexOf(resourceTypePrefix) === 0));
+                let definedResourceTypes = this.getDefinedResourceTypes(document);
+                let finalResourceTypes = _.filter(definedResourceTypes, o => (o.indexOf(resourceTypePrefix) === 0));
                 return _.map(finalResourceTypes, o => {
                     return new CompletionItem(o, CompletionItemKind.Field);
                 });
@@ -58,15 +57,15 @@ export class TerraformTipsProvider implements CompletionItemProvider {
                 const resourceType = parts[0];
 
                 // Get a list of all the names for this resource type
-                var names = this.getNamesForResourceType(document, resourceType);
+                let names = this.getNamesForResourceType(document, resourceType);
                 return _.map(names, o => new CompletionItem(o, CompletionItemKind.Field));
             }
             else if (parts.length === 3) {
-                // We're trying to type the exported field for the var
+                // We're trying to type the exported field for the let
                 const resourceType = parts[0];
-                var resourceName = parts[1];
-                var attrs = resources[resourceType].attrs;
-                var result = _.map(attrs, o => {
+                let resourceName = parts[1];
+                let attrs = resources[resourceType].attrs;
+                let result = _.map(attrs, o => {
                     let c = new CompletionItem(`${o.name} (${resourceType})`, CompletionItemKind.Property);
                     c.detail = o.description;
                     c.insertText = o.name;
@@ -85,36 +84,39 @@ export class TerraformTipsProvider implements CompletionItemProvider {
             return this.getHintsForStrings(possibleResources);
         }
 
-        // type '='
-        if (lineTillCurrentPosition.endsWith('=') && context.resourceType) {
-
-            // const completionItems: CompletionItem[] = [
-            //     new CompletionItem('Option1', CompletionItemKind.Value),
-            //     new CompletionItem('Option2', CompletionItemKind.Value)
-            // ];
-
-            const match = lineTillCurrentPosition.match(/(\w+)\s*=/);
-            if (match) {
+        const endwithEqual = lineTillCurrentPosition.endsWith('=');
+        const includeEqual = lineTillCurrentPosition.indexOf('=');
+        // handle options
+        if (this.resourceType) {
+            // when we typing a '=' character
+            if (endwithEqual) {
+                const lineBeforeEqualSign = lineTillCurrentPosition.substring(0, includeEqual).trim();
                 // load options
-                const name = match[1];
-                const strs = this.findArgByName(resources[context.resourceType].args, name);
-                return this.packOptionsFormStrings(strs);
+                const name = lineBeforeEqualSign;
+                const argStrs = this.findArgByName(resources[this.resourceType].args, name);
+                const options = this.getOptionsFormArg(argStrs);
+                // clear resource type
+                this.resourceType = "";
+                return (options).length ? options : [];
             }
             return [];
         }
 
         // Check if we're in a resource definition
-        for (var i = position.line - 1; i >= 0; i--) {
-            let line = document.lineAt(i).text;
-            let parentType = this.getParentType(line);
-            if (parentType && parentType.type === "resource") {
-                const resourceType = this.getResourceTypeFromLine(line);
-                let ret = this.getItemsForArgs(resources[resourceType].args, resourceType);
-                return ret;
-            }
-            else if (parentType && parentType.type !== "resource") {
-                // We don't want to accidentally include some other containers stuff
-                return [];
+        if (includeEqual < 0 && !endwithEqual) {
+            // we're not in options case
+            for (let i = position.line - 1; i >= 0; i--) {
+                let line = document.lineAt(i).text;
+                let parentType = this.getParentType(line);
+                if (parentType && parentType.type === "resource") {
+                    const resourceType = this.getResourceTypeFromLine(line);
+                    const ret = this.getItemsForArgs(resources[resourceType].args, resourceType);
+                    return ret;
+                }
+                else if (parentType && parentType.type !== "resource") {
+                    // We don't want to accidentally include some other containers stuff
+                    return [];
+                }
             }
         }
 
@@ -126,7 +128,7 @@ export class TerraformTipsProvider implements CompletionItemProvider {
 
         for (let i = position.line - 1; i >= 0; i--) {
             const line = document.lineAt(i).text;
-            const match = line.match(resourceRegex);
+            const match = RegExp(resourceRegex).exec(line);
 
             if (match) {
                 return match[1];
@@ -136,22 +138,25 @@ export class TerraformTipsProvider implements CompletionItemProvider {
         return undefined;
     }
 
-    packOptionsFormStrings(strings: string[]): CompletionItem[] {
-        return _.map(strings, s => {
-            return new CompletionItem(s, CompletionItemKind.Enum);
+    getOptionsFormArg(opts: string[]): CompletionItem[] {
+        return _.map(opts, opt => {
+            let c = new CompletionItem(opt, CompletionItemKind.Value);
+            c.insertText = "\"" + opt + "\"";
+            return c;
         });
     }
 
     findArgByName(args: any, name: string): any {
-        return args.find((arg) => arg.name === name);
+        const arg = args.find((arg) => arg.name === name);
+        return arg.options;
     }
 
     getNamesForResourceType(document: TextDocument, resourceType: string): string[] {
-        var r = new RegExp('resource "' + resourceType + '" "([a-zA-Z0-9\-_]+)"');
-        var found = [];
-        for (var i = 0; i < document.lineCount; i++) {
-            var line = document.lineAt(i).text;
-            var result = line.match(r);
+        let r = new RegExp('resource "' + resourceType + '" "([a-zA-Z0-9\-_]+)"');
+        let found = [];
+        for (let i = 0; i < document.lineCount; i++) {
+            let line = document.lineAt(i).text;
+            let result = RegExp(r).exec(line);
             if (result && result.length > 1) {
                 found.push(result[1]);
             }
@@ -163,11 +168,11 @@ export class TerraformTipsProvider implements CompletionItemProvider {
      * Returns a list of resource type strings
      */
     getDefinedResourceTypes(document: TextDocument) {
-        var r = /resource "([a-zA-Z0-9\-_]+)"/;
-        var found = [];
-        for (var i = 0; i < document.lineCount; i++) {
-            var line = document.lineAt(i).text;
-            var result = line.match(r);
+        let r = /resource "([a-zA-Z0-9\-_]+)"/;
+        let found = [];
+        for (let i = 0; i < document.lineCount; i++) {
+            let line = document.lineAt(i).text;
+            let result = line.match(r);
             if (result && result.length > 1) {
                 found.push(result[1]);
             }
@@ -176,9 +181,9 @@ export class TerraformTipsProvider implements CompletionItemProvider {
     }
 
     isTopLevelType(line: string): boolean {
-        for (var i = 0; i < topLevelTypes.length; i++) {
-            let resourceType = topLevelTypes[i];
-            if (resourceType.indexOf(line) === 0) {
+        for (const element of topLevelTypes) {
+            let resourceType = element;
+            if (resourceType.startsWith(line)) {
                 return true;
             }
         }
@@ -186,9 +191,9 @@ export class TerraformTipsProvider implements CompletionItemProvider {
     }
 
     getTopLevelType(line: string): CompletionItem[] {
-        for (var i = 0; i < topLevelTypes.length; i++) {
-            let resourceType = topLevelTypes[i];
-            if (resourceType.indexOf(line) === 0) {
+        for (const element of topLevelTypes) {
+            let resourceType = element;
+            if (resourceType.startsWith(line)) {
                 return [new CompletionItem(resourceType, CompletionItemKind.Enum)];
             }
         }
@@ -196,13 +201,14 @@ export class TerraformTipsProvider implements CompletionItemProvider {
     }
 
     isTypingTfCode(line: string): boolean {
-        var r = /\$\{[0-9a-zA-Z_\.\-]*$/;
+        let r = /\$\{[0-9a-zA-Z_\.\-]*$/;
         return r.test(line);
     }
 
     getVariableString(line: string): string {
-        var r = /\$\{([0-9a-zA-Z_\.\-]*)$/;
-        var result = line.match(r);
+        let r = /\$\{([0-9a-zA-Z_\.\-]*)$/;
+        let result = RegExp(r).exec(line);
+        // let result = line.match(r);
         if (result.length > 1) {
             return result[1];
         }
@@ -214,7 +220,7 @@ export class TerraformTipsProvider implements CompletionItemProvider {
         if (parts.length === 2 && parts[0] === "resource") {
             let r = parts[1].replace(/"/g, '');
             let regex = new RegExp("^" + r);
-            var possibleResources = _.filter(_.keys(resources), k => {
+            let possibleResources = _.filter(_.keys(resources), k => {
                 if (regex.test(k)) {
                     return true;
                 }
@@ -232,8 +238,8 @@ export class TerraformTipsProvider implements CompletionItemProvider {
     }
 
     getParentType(line: string): boolean | any {
-        for (var i = 0; i < topLevelRegexes.length; i++) {
-            let tl = topLevelRegexes[i];
+        for (const element of topLevelRegexes) {
+            let tl = element;
             if (tl.regex.test(line)) {
                 return tl;
             }
@@ -242,28 +248,16 @@ export class TerraformTipsProvider implements CompletionItemProvider {
     }
 
     getResourceTypeFromLine(line: string): string {
-        var lineParts = line.split(" ");
-        var type = lineParts[1];
+        let lineParts = line.split(" ");
+        let type = lineParts[1];
         return type.replace(/"/g, '');
     }
 
     getItemsForArgs(args, type) {
         return _.map(args, o => {
             let c = new CompletionItem(`${o.name} (${type})`, CompletionItemKind.Property);
-            let text = o.name;
-            if (o.default) {
-                text = text + ' = ' + o.default;
-            }
-            let desc = o.description;
-            if (o.options && o.options.length > 0) {
-                let options = "";
-                o.options.prototype.forEach(oo => {
-                    options = options + oo + ',';
-                });
-                desc = 'Optional Values: ' + options;
-            }
-            c.insertText = text;
-            c.detail = desc;
+            c.detail = o.description;
+            c.insertText = o.name;
             return c;
         });
     }
@@ -281,19 +275,9 @@ export class TerraformTipsProvider implements CompletionItemProvider {
             const resourceType = this.findResourceType(event.document, position);
 
             if (resourceType) {
-                setTimeout(() => {
-                    const cancellationTokenSource = new vscode.CancellationTokenSource();
-                    const context: TerraformCompletionContext = {
-                        triggerKind: vscode.CompletionTriggerKind.Invoke,
-                        triggerCharacter: undefined,
-                        resourceType,
-                    };
-                    this.provideCompletionItems(event.document, position, cancellationTokenSource.token, context);
-                    vscode.commands.executeCommand('editor.action.triggerSuggest');
-                }, 10);
+                this.resourceType = resourceType;
+                vscode.commands.executeCommand('editor.action.triggerSuggest');
             }
         }
     }
-
-
 }
\ No newline at end of file
diff --git a/src/extension.ts b/src/extension.ts
index 6ef888d..e6e0528 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -96,12 +96,12 @@ export async function activate(context: vscode.ExtensionContext) {
     // auto-complete
     console.log('activate the auto complete(snippets and lint) feature');
     const tipsProvider = new TerraformTipsProvider();
-    context.subscriptions.push(vscode.languages.registerCompletionItemProvider(TF_MODE, tipsProvider, '.'));
     context.subscriptions.push(
         vscode.workspace.onDidChangeTextDocument((event) => {
             tipsProvider.handleCharacterEvent(event);
         })
     );
+    context.subscriptions.push(vscode.languages.registerCompletionItemProvider(TF_MODE, tipsProvider));
     context.subscriptions.push(vscode.languages.registerDefinitionProvider(TF_MODE, new TerraformResDocProvider()));
 
     // import-resource
