core-customization: improved SAFE variable handling (#14679)

* core-customization: improved SAFE variable handling

- SAFE can either be a function or t.
  - When it's t, use a default validation function
    `(lambda (val) (validate-value val TYPE t))`
  - When it's a function, use the supplied function.

* Fixed bugs in go and groovy layer

In almost any cases, it's better to supply `t` instead of a function, to
`SAFE` argument of `spacemacs|defc`.

For example,
```elisp
 (spacemacs|defc go-use-gocheck-for-testing nil
   "If using gocheck for testing when running the tests -check.f will be used instead of -run to specify the test that will be ran. Gocheck is mandatory for testing suites."
  'boolean nil #'booleanp)
```

If its value is nil, it evaluate to `nil`, which means that `nil` is not
a safe value for `go-use-gocheck-for-testing` local variable.

But clearly it is.

* core-customization: improved SAFE variable handling

- Added a function `spacemacs-customization//get-variable-validator`.
  This function is designed to be used with `safe-local-variable`
  property of spacemacs custom variables.
  See details in the docstring.

```elisp
(put 'FOO 'safe-local-variable
     (apply-partially 'spacemacs-customization//get-variable-validator
                      'FOO))
```

- This is better than a lambda since `apply-partially` returns a compiled
  function. (Though the performace gain may be tiny.)
- This is better than simply calling `validate-value`, because it returns
  nil when value is nil. But sometimes nil is a valid value.

Co-authored-by: Lucius Hu <lebensterben@users.noreply.github.com>
This commit is contained in:
Lucius Hu 2021-04-19 15:34:09 -04:00 committed by GitHub
parent 175840bc6e
commit 3bbc7a7d40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 41 additions and 18 deletions

View File

@ -63,8 +63,9 @@ TYPE should be a widget type for editing the symbol's value.
base types and useful composite types.
GROUP-OVERRIDE should be provided if you don't want Spacemacs to infer the
configuration group from the currently configured layer name.
SAFE should be filled with a function to be set to
safe-local-variable property.
SAFE should either be a function or t to be set to
safe-local-variable property. When it's t, use TYPE to determine
the safety.
NOTE: Use interactive function `spacemacs/customization-valid-p' to test if a
variable has a proper type. In interactive mode it will also `message'
@ -91,8 +92,11 @@ NOTE: Variables defined with a group listed in
,(format "%s\n\nTYPE: %s\n" doc type)
:type ,type
:group group)
(when (not (null ',safe))
(put ',symbol 'safe-local-variable ,safe))
(pcase ,safe
('t (put ',symbol 'safe-local-variable
(apply-partially 'spacemacs-customization//get-variable-validator
',symbol)))
((pred functionp) (put ',symbol 'safe-local-variable ,safe)))
(when (memq group spacemacs-customization-uncustomizable-groups)
;; HACK: This will make `custom-variable-p' return nil
;; so the `describe-variable' function won't add customization
@ -101,7 +105,7 @@ NOTE: Variables defined with a group listed in
(put ',symbol 'custom-autoload nil))))
(defun spacemacs/customization-valid-p (var-symbol)
"returns true if symbol refers spacemacs custom variable with valid value.
"Return t if VAR-SYMBOL is a spacemacs custom variable with valid value.
Emits message with the result when called interactively."
(interactive "v")
(let* ((defc? (get var-symbol 'spacemacs-customization--variable))
@ -117,6 +121,25 @@ Emits message with the result when called interactively."
(message "%s is not Spacemacs customization variable" var-symbol))))
valid?))
(defun spacemacs-customization//get-variable-validator (var-symbol val)
"Check the validity of VAL for the spacemacs custom variable VAR-SYMBOL.
Return t if VAL is valid, and return an error when VAL is invalid or when
VAR-SYMBOL is not a spacemacs custom variable.
This function should be used with `apply-partially', and be set to the
`safe-local-variable' perperty of VAR-SYMBOL. `apply-partially' returns a
validation function that takes one argument and validate it against the scheme
of VAR-SYMBOL.
When loading local variables, `safe-local-variable-p' would call the validation
function, and it would demote any error to a message and a nil return value.
Thus it returns t when VAL is valid and nil otherwise."
(if-let* ((_ (get var-symbol 'spacemacs-customization--variable))
(type (custom-variable-type var-symbol)))
(or (validate-value val type nil) t)
(user-error "%s is not a Spacemacs customization variable" var-symbol)))
(defun spacemacs-customization//group-variables (group-symbol)
"Given customization group symbol get its variables."
(let (ret-val)

View File

@ -29,19 +29,19 @@
"The backend to use for IDE features.
Possible values are `lsp' and `go-mode'.
If not set then `go-mode' is the default backend unless `lsp' layer is used."
'(choice (const lsp) (const go-mode)) nil #'symbolp)
'(choice (const lsp) (const go-mode)) nil t)
(spacemacs|defc go-use-gocheck-for-testing nil
"If using gocheck for testing when running the tests -check.f will be used instead of -run to specify the test that will be ran. Gocheck is mandatory for testing suites."
'boolean nil #'booleanp)
'boolean nil t)
(spacemacs|defc go-use-testify-for-testing nil
"If using testify for testing when running the tests -testify.m will be used instead of -run to specify the test that will be ran. Testify is mandatory for testing suites."
'boolean nil #'booleanp)
'boolean nil t)
(spacemacs|defc go-format-before-save nil
"Use gofmt before save. Set to non-nil to enable gofmt before saving. Default is nil."
'boolean nil #'booleanp)
'boolean nil t)
(spacemacs|defc go-tab-width 8
"Set the `tab-width' in Go mode. Default is 8."
@ -49,28 +49,28 @@ If not set then `go-mode' is the default backend unless `lsp' layer is used."
(spacemacs|defc go-use-golangci-lint nil
"Use `golangci-lint' if the variable has non-nil value."
'boolean nil #'booleanp)
'boolean nil t)
(spacemacs|defc go-test-buffer-name "*go test*"
"Name of the buffer for go test output. Default is *go test*."
'string nil #'stringp)
'string nil t)
(spacemacs|defc go-use-test-args ""
"Additional arguments to be supplied to `go test` during runtime."
'string nil #'stringp)
'string nil t)
(spacemacs|defc go-test-verbose nil
"Control verbosity of `go test` output"
'boolean nil #'booleanp)
'boolean nil t)
(spacemacs|defc go-run-args ""
"Additional arguments to by supplied to `go run` during runtime."
'string nil #'stringp)
'string nil t)
(spacemacs|defc go-run-command "go run"
"Go run command. Default is `go run`."
'string nil #'stringp)
'string nil t)
(spacemacs|defc go-test-command "go test"
"Go test command. Default is `go test`."
'string nil #'stringp)
'string nil t)

View File

@ -27,8 +27,8 @@
"The backend to use for IDE features.
Possible values are `lsp' and `company-groovy'.
If not set then 'company-groovy` is the default backend unless `lsp' layer is used"
'(choice (const lsp) (const company-groovy)) nil #'symbolp)
'(choice (const lsp) (const company-groovy)) nil t)
(spacemacs|defc groovy-lsp-jar-path "~/groovy-lsp-all.jar"
"The path to the lsp jar file"
'(file :must-match t) nil #'stringp)
'(file :must-match t) nil t)