Skip to content

Commit

Permalink
Merge branch 'master' into imenu-sort-function
Browse files Browse the repository at this point in the history
  • Loading branch information
rnikoopour authored Oct 14, 2024
2 parents 7cae810 + abfc10f commit 0ecaed6
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 9 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ Type `C-c C-d C-c` to kill the URL (i.e. copy it to the clipboard) for the docum
You can also type `C-c C-d C-r` to insert a comment containing a link to
this documentation right above the resource or data block.

This feature requires either:

- a `required_provider` declaration in any `.tf` file in current directory
(see [Terraform doc](https://developer.hashicorp.com/terraform/language/providers/requirements#requiring-providers))
- a working `terraform providers` command. This command may require a
valid token (at least for AWS).

## Customize Variables

#### `terraform-indent-level`(Default: `2`)
Expand Down
63 changes: 54 additions & 9 deletions terraform-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@
"Face for varriables."
:group 'terraform-mode)

(defconst terraform--constants-regexp
(concat "\\(?:^\\|[^.]\\)" (regexp-opt '("null") 'words)))

(defconst terraform--block-builtins-without-name-or-type-regexp
(rx line-start
(zero-or-more space)
Expand Down Expand Up @@ -154,6 +157,7 @@
(1 'terraform-builtin-face)
(2 'terraform-resource-type-face t)
(3 'terraform-resource-name-face t))
(,terraform--constants-regexp 1 'font-lock-constant-face)
,@hcl-font-lock-keywords))

(defun terraform-format-buffer ()
Expand Down Expand Up @@ -252,18 +256,59 @@
(when (re-search-forward (concat "/\\(.*?\\)/" provider "\\]") nil t)
(match-string 1)))))

(defun terraform--get-resource-provider-source (provider &optional dir)
"Return Terraform provider source for PROVIDER located in DIR.
Terraform provider source is searched in `required_provider' declaration
in current buffer or in other Terraform files located in the same directory
of the file of current buffer. If still not found, the provider source is
searched by running command `terraform providers'.
The DIR parameter is optional and used only for tests."
(goto-char (point-min))
;; find current directory if it's not specified in arguments
(if (and (not dir) buffer-file-name) (setq dir (file-name-directory buffer-file-name)))
(let (tf-files
;; try to find provider source in current buffer
(provider-source (terraform--get-resource-provider-source-in-buffer provider)))
;; check if terraform provider-source was found
(when (and (= (length provider-source) 0) dir)
;; find all terraform files of this project. One of them
;; should contain required_provider declaration
(setq tf-files (directory-files dir nil "^[[:alnum:][:blank:]_.-]+\\.tf$")))
;; iterate on terraform files until a provider source is found
(while (and (= (length provider-source) 0) tf-files)
(with-temp-buffer
(let* ((file (pop tf-files))
(file-path (if dir (concat dir "/" file) file)))
(insert-file-contents file-path)
;; look for provider source in a terraform file
(setq provider-source (terraform--get-resource-provider-source-in-buffer provider)))))
provider-source))

(defun terraform--get-resource-provider-source-in-buffer (provider)
"Search and return provider namespace for PROVIDER in current buffer.
Return nil if not found."
(goto-char (point-min))
(if (and (re-search-forward "^terraform[[:blank:]]*{" nil t)
(re-search-forward "^[[:blank:]]*required_providers[[:blank:]]*{" nil t)
(re-search-forward (concat "^[[:blank:]]*" provider "[[:blank:]]*=[[:blank:]]*{") nil t)
(re-search-forward "^[[:blank:]]*source[[:blank:]]*=[[:blank:]]*\"\\([a-z/]+\\)\"" nil t))
(match-string 1)))

(defun terraform--resource-url (resource doc-dir)
"Return the url containing the documentation for RESOURCE using DOC-DIR."
(let* ((provider (terraform--extract-provider resource))
(provider-ns (terraform--get-resource-provider-namespace provider))
;; search provider source in terraform files
(provider-source (terraform--get-resource-provider-source provider))
(resource-name (terraform--extract-resource resource)))
(if provider-ns
(format "https://registry.terraform.io/providers/%s/%s/latest/docs/%s/%s"
provider-ns
provider
doc-dir
resource-name)
(user-error "Can not determine the provider namespace for %s" provider))))
(when (= (length provider-source) 0)
;; fallback to old method with terraform providers command
(setq provider-source (concat
(terraform--get-resource-provider-namespace provider)
"/" provider)))
(if (> (length provider-source) 0)
(format "https://registry.terraform.io/providers/%s/latest/docs/%s/%s"
provider-source doc-dir resource-name)
(user-error "Can not determine the provider source for %s" provider))))

(defun terraform--resource-url-at-point ()
(save-excursion
Expand Down Expand Up @@ -359,7 +404,7 @@ If the point is not at the heading, call
(imenu-add-to-menubar "Terraform"))

;;;###autoload
(add-to-list 'auto-mode-alist '("\\.tf\\(vars\\)?\\'" . terraform-mode))
(add-to-list 'auto-mode-alist '("\\.t\\(f\\(vars\\)?\\|ofu\\)\\'" . terraform-mode))

(provide 'terraform-mode)

Expand Down
1 change: 1 addition & 0 deletions test/fixtures/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# blah blah
13 changes: 13 additions & 0 deletions test/fixtures/provider.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# blah blah
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5"
}
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.47"
}
}
}
45 changes: 45 additions & 0 deletions test/test-command.el
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,49 @@ resource \"elasticstack_elasticsearch_security_user\" \"filebeat_writer\" {
(cl-letf (((symbol-function 'terraform--get-resource-provider-namespace) (lambda (prov) "elastic")))
(should (equal (terraform--resource-url-at-point) "https://registry.terraform.io/providers/elastic/elasticstack/latest/docs/resources/elasticsearch_security_user")))))

(ert-deftest command--terraform--get-resource-provider-source-in-buffer ()
(with-terraform-temp-buffer
"
# blah blah
terraform {
required_providers {
aws = {
source = \"hashicorp/aws\"
version = \"~> 5\"
}
azurerm = {
source = \"hashicorp/azurerm\"
version = \"~> 3.47\"
}
}
}
"
(should (equal (terraform--get-resource-provider-source-in-buffer "azurerm") "hashicorp/azurerm"))
;;(should (equal (terraform--get-resource-provider-source-in-buffer "plop") nil))
(should (equal (terraform--get-resource-provider-source-in-buffer "aws") "hashicorp/aws"))))

;; required_providers is defined in current buffer
(ert-deftest command--terraform--get-resource-provider-source ()
(with-terraform-temp-buffer
"
# blah blah
terraform {
required_providers {
aws = {
source = \"hashicorp/aws\"
version = \"~> 5\"
}
azurerm = {
source = \"hashicorp/azurerm\"
version = \"~> 3.47\"
}
}
}
"
(should (equal (terraform--get-resource-provider-source "aws" "test/fixtures") "hashicorp/aws"))))

;; required_providers is defined in another file
(ert-deftest command--terraform--get-resource-provider-source-provider-in-file ()
(should (equal (terraform--get-resource-provider-source "aws" "test/fixtures") "hashicorp/aws")))

;;; test-command.el ends here
8 changes: 8 additions & 0 deletions test/test-highlighting.el
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@
keyword
(should (face-at-cursor-p 'font-lock-constant-face)))))

(ert-deftest font-lock--constants-keywords ()
"Syntax highlight of constant keywords"

(dolist (keyword '("null"))
(with-terraform-temp-buffer
keyword
(should (face-at-cursor-p 'font-lock-constant-face)))))

(ert-deftest font-lock--provider-block--with-one-space ()
"Syntax highlight of `provider' block."

Expand Down

0 comments on commit 0ecaed6

Please sign in to comment.