Skip to content

Commit

Permalink
Add insert-final-newline config option
Browse files Browse the repository at this point in the history
This resolves helix-editor#4274 with the implementation largely based off of helix-editor#5435
and also addresses the review from @the-mikedavis on that PR.

The option name is from EditorConfig's `insert_final_newline`, which is
also used by VS Code as `files.insertFinalNewline`.

We match Vim's behavior in that :w will add the newline to unmodified
files but :wa will not; see helix-editor#1760.

Co-authored-by: Xalfer <[email protected]>
  • Loading branch information
zqianem and Xalfer committed Sep 3, 2023
1 parent a38ec6d commit 9773696
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 8 deletions.
1 change: 1 addition & 0 deletions book/src/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ Its settings will be merged with the configuration directory `config.toml` and t
| `text-width` | Maximum line length. Used for the `:reflow` command and soft-wrapping if `soft-wrap.wrap-at-text-width` is set | `80` |
| `workspace-lsp-roots` | Directories relative to the workspace root that are treated as LSP roots. Should only be set in `.helix/config.toml` | `[]` |
| `default-line-ending` | The line ending to use for new documents. Can be `native`, `lf`, `crlf`, `ff`, `cr` or `nel`. `native` uses the platform's native line ending (`crlf` on Windows, otherwise `lf`). | `native` |
| `insert-final-newline` | Whether to automatically insert a final newline on write if missing | `false` |

### `[editor.statusline]` Section

Expand Down
32 changes: 24 additions & 8 deletions helix-term/src/commands/typed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::job::Job;
use super::*;

use helix_core::fuzzy::fuzzy_match;
use helix_core::{encoding, shellwords::Shellwords};
use helix_core::{encoding, line_ending, shellwords::Shellwords};
use helix_view::document::DEFAULT_LANGUAGE_NAME;
use helix_view::editor::{Action, CloseError, ConfigEvent};
use serde_json::Value;
Expand Down Expand Up @@ -330,12 +330,12 @@ fn write_impl(
path: Option<&Cow<str>>,
force: bool,
) -> anyhow::Result<()> {
let editor_auto_fmt = cx.editor.config().auto_format;
let config = cx.editor.config();
let jobs = &mut cx.jobs;
let (view, doc) = current!(cx.editor);
let path = path.map(AsRef::as_ref);

let fmt = if editor_auto_fmt {
let fmt = if config.auto_format {
doc.auto_format().map(|fmt| {
let callback = make_format_callback(
doc.id(),
Expand All @@ -352,13 +352,26 @@ fn write_impl(
};

if fmt.is_none() {
if config.insert_final_newline {
insert_final_newline(doc, view);
}
let id = doc.id();
cx.editor.save(id, path, force)?;
}

Ok(())
}

fn insert_final_newline(doc: &mut Document, view: &mut View) {
let text = doc.text();
if line_ending::get_line_ending(&text.slice(..)).is_none() {
let eof = Selection::point(text.len_chars());
let insert = Transaction::insert(text, &eof, doc.line_ending.as_str().into());
doc.apply(&insert, view.id);
doc.append_changes_to_history(view);
}
}

fn write(
cx: &mut compositor::Context,
args: &[Cow<str>],
Expand Down Expand Up @@ -658,7 +671,7 @@ pub fn write_all_impl(
write_scratch: bool,
) -> anyhow::Result<()> {
let mut errors: Vec<&'static str> = Vec::new();
let auto_format = cx.editor.config().auto_format;
let config = cx.editor.config();
let jobs = &mut cx.jobs;
let current_view = view!(cx.editor);

Expand Down Expand Up @@ -693,7 +706,7 @@ pub fn write_all_impl(
current_view.id
};

let fmt = if auto_format {
let fmt = if config.auto_format {
doc.auto_format().map(|fmt| {
let callback = make_format_callback(
doc.id(),
Expand All @@ -709,16 +722,19 @@ pub fn write_all_impl(
};

if fmt.is_none() {
return Some(doc.id());
return Some((doc.id(), target_view));
}

None
})
.collect();

// manually call save for the rest of docs that don't have a formatter
for id in saves {
cx.editor.save::<PathBuf>(id, None, force)?;
for (doc_id, view_id) in saves {
if config.insert_final_newline {
insert_final_newline(doc_mut!(cx.editor, &doc_id), view_mut!(cx.editor, view_id));
}
cx.editor.save::<PathBuf>(doc_id, None, force)?;
}

if !errors.is_empty() && !force {
Expand Down
3 changes: 3 additions & 0 deletions helix-view/src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,8 @@ pub struct Config {
pub workspace_lsp_roots: Vec<PathBuf>,
/// Which line ending to choose for new documents. Defaults to `native`. i.e. `crlf` on Windows, otherwise `lf`.
pub default_line_ending: LineEndingConfig,
/// Whether to automatically insert a final newline on write if missing. Defaults to `false`.
pub insert_final_newline: bool,
/// Enables smart tab
pub smart_tab: Option<SmartTabConfig>,
}
Expand Down Expand Up @@ -842,6 +844,7 @@ impl Default for Config {
completion_replace: false,
workspace_lsp_roots: Vec::new(),
default_line_ending: LineEndingConfig::default(),
insert_final_newline: false,
smart_tab: Some(SmartTabConfig::default()),
}
}
Expand Down

0 comments on commit 9773696

Please sign in to comment.