Repository: biomejs/biome (language: rust)

Issue:
🐛 editorconfig rule `insert_final_newline` cannot be disabled with `insert_final_newline = unset`
### Environment information

```block
CLI:
  Version:                      1.9.4
  Color support:                true

Platform:
  CPU Architecture:             aarch64
  OS:                           macos

Environment:
  BIOME_LOG_PATH:               unset
  BIOME_LOG_PREFIX_NAME:        unset
  BIOME_CONFIG_PATH:            unset
  NO_COLOR:                     unset
  TERM:                         "xterm-256color"
  JS_RUNTIME_VERSION:           "v22.12.0"
  JS_RUNTIME_NAME:              "node"
  NODE_PACKAGE_MANAGER:         "npm/10.9.0"

Biome Configuration:
  Status:                       Loaded successfully
  Formatter disabled:           false
  Linter disabled:              false
  Organize imports disabled:    false
  VCS disabled:                 true

Workspace:
  Open Documents:               0
```

### What happened?

If I try to disable a rule such as `insert_final_newline = ignore`, which [should be supported](https://github.com/editorconfig/editorconfig/issues/282#issuecomment-264508592), I get an error:

```
configuration ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  ✖ Failed to parse the .editorconfig file.
    
    Caused by:
      Custom("expected 'true' or 'false'")
```

### Expected result

The configuration should validate properly and disable the rule for this pattern.

### Code of Conduct

- [x] I agree to follow Biome's Code of Conduct


Discussion / hints:
Hello @knyghty, please provide a minimal reproduction. You can use one of the following options:

- Provide a link to [our playground](https://biomejs.dev/playground), if it's applicable.
- Provide a link to GitHub repository. To easily create a reproduction, you can use our interactive CLI via `npm create @biomejs/biome-reproduction`

Issues marked with `S-Needs repro` will be **closed** if they have **no activity within 3 days**.
Unfortunately GitHub isn't a source of truth. The spec says otherwise https://spec.editorconfig.org/#supported-pairs

If you want, you can bring this to their attention.
It seems like those docs aren't either. It looks like this was merged in https://github.com/biomejs/biome/pull/3907 but only allows "off" and "unset" - and also I assume this isn't released yet since it's still not working on 1.9.4
Those should work in 1.9.4.
@dyc3 From what I can tell, it either does not, or I am fundamentally misunderstanding something:

```
❯ npx @biomejs/biome version
CLI:        1.9.4
Server:     not connected
❯ npx @biomejs/biome check
configuration ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  ✖ Failed to parse the .editorconfig file.
    
    Caused by:
      Custom("expected 'true' or 'false'")
  
```

Using this file:

```
# https://editorconfig.org/

root = true

[*]
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
end_of_line = lf
charset = utf-8

# Docstrings and comments use max_line_length = 79
[*.py]
max_line_length = 88

# Use 2 spaces for the HTML files
[*.html]
indent_size = 2

# The JSON files contain newlines inconsistently
[*.json]
indent_size = 2
insert_final_newline = unset

[**/admin/js/vendor/**]
indent_style = unset
indent_size = unset

# Minified JavaScript files shouldn't be changed
[**.min.js]
indent_style = unset
insert_final_newline = unset

# Makefiles always use tabs for indentation
[Makefile]
indent_style = tab

# Batch files use tabs for indentation
[*.bat]
indent_style = tab

[docs/**.txt]
max_line_length = 79

[*.yml]
indent_size = 2
```
(I should point out, I have to comment out _all_ lines using `unset` to get this to run)
I'm able to reproduce this. `unset` does work, it just doesn't work for `insert_final_newline`. Fix will be up soon.

Fix patch:
diff --git a/.changeset/wet-hornets-sparkle.md b/.changeset/wet-hornets-sparkle.md
new file mode 100644
index 000000000000..92c6da2a67be
--- /dev/null
+++ b/.changeset/wet-hornets-sparkle.md
@@ -0,0 +1,5 @@
+---
+"@biomejs/biome": patch
+---
+
+Fix Biome being unable to parse `insert_final_newline = unset` in EditorConfig files.
diff --git a/crates/biome_configuration/src/editorconfig.rs b/crates/biome_configuration/src/editorconfig.rs
index 833bb542fa14..885e440372cb 100644
--- a/crates/biome_configuration/src/editorconfig.rs
+++ b/crates/biome_configuration/src/editorconfig.rs
@@ -94,8 +94,8 @@ pub struct EditorConfigOptions {
     #[serde(deserialize_with = "deserialize_optional_value_from_string")]
     end_of_line: EditorconfigValue<LineEnding>,
     // Not a biome option, but we need it to emit a diagnostic when this is set to false.
-    #[serde(deserialize_with = "deserialize_optional_bool_from_string")]
-    insert_final_newline: Option<bool>,
+    #[serde(deserialize_with = "deserialize_optional_value_from_string")]
+    insert_final_newline: EditorconfigValue<bool>,
 }
 
 impl EditorConfigOptions {
@@ -120,7 +120,7 @@ impl EditorConfigOptions {
     fn validate(&self) -> Vec<EditorConfigDiagnostic> {
         let mut errors = vec![];
         // `insert_final_newline = false` results in formatting behavior that is incompatible with biome
-        if let Some(false) = self.insert_final_newline {
+        if let EditorconfigValue::Explicit(false) = self.insert_final_newline {
             errors.push(EditorConfigDiagnostic::incompatible(
                 "insert_final_newline",
                 "Biome always inserts a final newline. Set this option to true.",
@@ -167,13 +167,6 @@ where
     }
 }
 
-fn deserialize_optional_bool_from_string<'de, D>(deserializer: D) -> Result<Option<bool>, D::Error>
-where
-    D: Deserializer<'de>,
-{
-    deserialize_bool_from_string(deserializer).map(Some)
-}
-
 fn deserialize_optional_value_from_string<'de, D, T>(
     deserializer: D,
 ) -> Result<EditorconfigValue<T>, D::Error>
@@ -438,6 +431,7 @@ indent_style = unset
 indent_size = unset
 end_of_line = unset
 max_line_length = unset
+insert_final_newline = unset
 "#;
 
         let conf = parse_str(input).expect("Failed to parse editorconfig");


Test patch: