;; -*- lexical-binding: t; -*- ;; Configuration as code! (use-package terraform-mode :mode "\\.tf\\'" :config (defun devdocs-terraform-resource-string (resource type) "Returns the devdocs for Terraform resource RESOURCE of type TYPE (\"data\" or \"resource\")as a string." (devdocs-as-string resource "terraform" type)) (with-eval-after-load 'devdocs (add-to-list 'devdocs-entry-aliases '("aws_alb" . "aws_lb"))) (defun terraform-block-metadata-at-pos (pos) (let* ((parser (treesit-parser-create 'hcl)) (current-node (treesit-node-at pos parser)) (block (treesit-node-top-level current-node "block")) (capture (when block (treesit-query-capture block '((block (identifier) @type (:match "^\\(data\\|resource\\)$" @type) (string_lit (_) (template_literal) @name (_))))))) (capture (let ((-compare-fn (lambda (a b) (eq (car a) (car b))))) (-distinct capture)))) (-map (lambda (r) (cons (car r) (treesit-node-text (cdr r) t))) capture))) (defun terraform-block-metadata-at-point () (terraform-block-metadata-at-pos (point))) (defun terraform-devdocs-eldoc-function (cb) (when-let* ((metadata (terraform-block-metadata-at-point)) (resource-name (alist-get 'name metadata)) (resource-type (alist-get 'type metadata)) (doc (devdocs-terraform-resource-string resource-name resource-type)) (summary (with-temp-buffer (insert doc) (goto-char (point-min)) (forward-line) (while (and (looking-at (rx space) t) (not (eobp))) (forward-line)) (buffer-substring-no-properties (line-beginning-position) (line-end-position))))) (funcall cb doc :echo summary))) (defun terraform-setup-eldoc () (add-hook 'eldoc-documentation-functions #'terraform-devdocs-eldoc-function nil t)) (add-hook 'terraform-mode-hook #'terraform-setup-eldoc) (defun find-terraform-root () "Returns the highest-level parent directory containing a .tf file." (let ((current-dir default-directory) (root nil)) (while (and (not (string-match locate-dominating-stop-dir-regexp current-dir)) (f-glob "*.tf" current-dir)) (setq root current-dir) (setq current-dir (f-parent current-dir))) (when root (file-name-as-directory root)))) ;; Use root terraform module to resolve providers (defun terraform-provider-ns-advice (oldfn &rest args) (let ((dir (or (find-terraform-root) default-directory))) (let ((default-directory dir)) (apply oldfn args)))) (advice-add 'terraform--get-resource-provider-namespace :around #'terraform-provider-ns-advice)) (defun terraform-jump-to-plan () "Jump to the top of the plan output in a buffer showing a terraform plan." (interactive) (when (eq major-mode 'vterm-mode) (vterm-copy-mode)) (search-backward "Terraform will perform the following actions:")) (provide 'init-terraform)