on master help sign in
Add config for default line ending (#5621)
3fb9fafAlex
book/src/configuration.md+1
@@ -63,6 +63,7 @@ Its settings will be merged with the configuration directory `config.toml` and t
63| `color-modes` | Whether to color the mode indicator with different colors depending on the mode itself | `false` |
63| `color-modes` | Whether to color the mode indicator with different colors depending on the mode itself | `false` |
64| `text-width` | Maximum line length. Used for the `:reflow` command and soft-wrapping if `soft-wrap.wrap-at-text-width` is set | `80` |
64| `text-width` | Maximum line length. Used for the `:reflow` command and soft-wrapping if `soft-wrap.wrap-at-text-width` is set | `80` |
65| `workspace-lsp-roots` | Directories relative to the workspace root that are treated as LSP roots. Should only be set in `.helix/config.toml` | `[]` |
65| `workspace-lsp-roots` | Directories relative to the workspace root that are treated as LSP roots. Should only be set in `.helix/config.toml` | `[]` |
66+| `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` |
66
67
67### `[editor.statusline]` Section
68### `[editor.statusline]` Section
68
69
helix-core/src/lib.rs+1−1
@@ -66,5 +66,5 @@ pub use syntax::Syntax;
66
66
67pub use diagnostic::Diagnostic;
67pub use diagnostic::Diagnostic;
68
68
69−pub use line_ending::{LineEnding, DEFAULT_LINE_ENDING};
69+pub use line_ending::{LineEnding, NATIVE_LINE_ENDING};
70pub use transaction::{Assoc, Change, ChangeSet, Deletion, Operation, Transaction};
70pub use transaction::{Assoc, Change, ChangeSet, Deletion, Operation, Transaction};
helix-core/src/line_ending.rs+2−2
@@ -1,9 +1,9 @@
1use crate::{Rope, RopeSlice};
1use crate::{Rope, RopeSlice};
2
2
3#[cfg(target_os = "windows")]
3#[cfg(target_os = "windows")]
4−pub const DEFAULT_LINE_ENDING: LineEnding = LineEnding::Crlf;
4+pub const NATIVE_LINE_ENDING: LineEnding = LineEnding::Crlf;
5#[cfg(not(target_os = "windows"))]
5#[cfg(not(target_os = "windows"))]
6−pub const DEFAULT_LINE_ENDING: LineEnding = LineEnding::LF;
6+pub const NATIVE_LINE_ENDING: LineEnding = LineEnding::LF;
7
7
8/// Represents one of the valid Unicode line endings.
8/// Represents one of the valid Unicode line endings.
9#[derive(PartialEq, Eq, Copy, Clone, Debug)]
9#[derive(PartialEq, Eq, Copy, Clone, Debug)]
helix-term/tests/test/auto_pairs.rs+1−1
@@ -2,7 +2,7 @@ use helix_core::{auto_pairs::DEFAULT_PAIRS, hashmap};
2
2
3use super::*;
3use super::*;
4
4
5−const LINE_END: &str = helix_core::DEFAULT_LINE_ENDING.as_str();
5+const LINE_END: &str = helix_core::NATIVE_LINE_ENDING.as_str();
6
6
7fn differing_pairs() -> impl Iterator<Item = &'static (char, char)> {
7fn differing_pairs() -> impl Iterator<Item = &'static (char, char)> {
8 DEFAULT_PAIRS.iter().filter(|(open, close)| open != close)
8 DEFAULT_PAIRS.iter().filter(|(open, close)| open != close)
helix-term/tests/test/helpers.rs+1−1
@@ -244,7 +244,7 @@ pub fn test_editor_config() -> helix_view::editor::Config {
244/// character, and if one doesn't exist already, appends the system's
244/// character, and if one doesn't exist already, appends the system's
245/// appropriate line ending to the end of a string.
245/// appropriate line ending to the end of a string.
246pub fn platform_line(input: &str) -> String {
246pub fn platform_line(input: &str) -> String {
247− let line_end = helix_core::DEFAULT_LINE_ENDING.as_str();
247+ let line_end = helix_core::NATIVE_LINE_ENDING.as_str();
248
248
249 // we can assume that the source files in this code base will always
249 // we can assume that the source files in this code base will always
250 // be LF, so indoc strings will always insert LF
250 // be LF, so indoc strings will always insert LF
helix-view/src/document.rs+13−9
@@ -8,7 +8,6 @@ use helix_core::doc_formatter::TextFormat;
8use helix_core::encoding::Encoding;
8use helix_core::encoding::Encoding;
9use helix_core::syntax::{Highlight, LanguageServerFeature};
9use helix_core::syntax::{Highlight, LanguageServerFeature};
10use helix_core::text_annotations::{InlineAnnotation, TextAnnotations};
10use helix_core::text_annotations::{InlineAnnotation, TextAnnotations};
11−use helix_core::Range;
12use helix_vcs::{DiffHandle, DiffProviderRegistry};
11use helix_vcs::{DiffHandle, DiffProviderRegistry};
13
12
14use ::parking_lot::Mutex;
13use ::parking_lot::Mutex;
@@ -31,8 +30,8 @@ use helix_core::{
31 indent::{auto_detect_indent_style, IndentStyle},
30 indent::{auto_detect_indent_style, IndentStyle},
32 line_ending::auto_detect_line_ending,
31 line_ending::auto_detect_line_ending,
33 syntax::{self, LanguageConfiguration},
32 syntax::{self, LanguageConfiguration},
34− ChangeSet, Diagnostic, LineEnding, Rope, RopeBuilder, Selection, Syntax, Transaction,
33+ ChangeSet, Diagnostic, LineEnding, Range, Rope, RopeBuilder, Selection, Syntax, Transaction,
35− DEFAULT_LINE_ENDING,
34+ NATIVE_LINE_ENDING,
36};
35};
37
36
38use crate::editor::{Config, RedrawHandle};
37use crate::editor::{Config, RedrawHandle};
@@ -590,6 +589,7 @@ impl Document {
590 config: Arc<dyn DynAccess<Config>>,
589 config: Arc<dyn DynAccess<Config>>,
591 ) -> Self {
590 ) -> Self {
592 let (encoding, has_bom) = encoding_with_bom_info.unwrap_or((encoding::UTF_8, false));
591 let (encoding, has_bom) = encoding_with_bom_info.unwrap_or((encoding::UTF_8, false));
592+ let line_ending = config.load().default_line_ending.into();
593 let changes = ChangeSet::new(&text);
593 let changes = ChangeSet::new(&text);
594 let old_state = None;
594 let old_state = None;
595
595
@@ -603,7 +603,7 @@ impl Document {
603 inlay_hints: HashMap::default(),
603 inlay_hints: HashMap::default(),
604 inlay_hints_oudated: false,
604 inlay_hints_oudated: false,
605 indent_style: DEFAULT_INDENT,
605 indent_style: DEFAULT_INDENT,
606− line_ending: DEFAULT_LINE_ENDING,
606+ line_ending,
607 restore_cursor: false,
607 restore_cursor: false,
608 syntax: None,
608 syntax: None,
609 language: None,
609 language: None,
@@ -623,10 +623,12 @@ impl Document {
623 focused_at: std::time::Instant::now(),
623 focused_at: std::time::Instant::now(),
624 }
624 }
625 }
625 }
626+
626 pub fn default(config: Arc<dyn DynAccess<Config>>) -> Self {
627 pub fn default(config: Arc<dyn DynAccess<Config>>) -> Self {
627− let text = Rope::from(DEFAULT_LINE_ENDING.as_str());
628+ let text = Rope::from(NATIVE_LINE_ENDING.as_str());
628 Self::from(text, None, config)
629 Self::from(text, None, config)
629 }
630 }
631+
630 // TODO: async fn?
632 // TODO: async fn?
631 /// Create a new document from `path`. Encoding is auto-detected, but it can be manually
633 /// Create a new document from `path`. Encoding is auto-detected, but it can be manually
632 /// overwritten with the `encoding` parameter.
634 /// overwritten with the `encoding` parameter.
@@ -643,7 +645,7 @@ impl Document {
643 from_reader(&mut file, encoding)?
645 from_reader(&mut file, encoding)?
644 } else {
646 } else {
645 let encoding = encoding.unwrap_or(encoding::UTF_8);
647 let encoding = encoding.unwrap_or(encoding::UTF_8);
646− (Rope::from(DEFAULT_LINE_ENDING.as_str()), encoding, false)
648+ (Rope::from(NATIVE_LINE_ENDING.as_str()), encoding, false)
647 };
649 };
648
650
649 let mut doc = Self::from(rope, Some((encoding, has_bom)), config);
651 let mut doc = Self::from(rope, Some((encoding, has_bom)), config);
@@ -887,14 +889,16 @@ impl Document {
887
889
888 /// Detect the indentation used in the file, or otherwise defaults to the language indentation
890 /// Detect the indentation used in the file, or otherwise defaults to the language indentation
889 /// configured in `languages.toml`, with a fallback to tabs if it isn't specified. Line ending
891 /// configured in `languages.toml`, with a fallback to tabs if it isn't specified. Line ending
890− /// is likewise auto-detected, and will fallback to the default OS line ending.
892+ /// is likewise auto-detected, and will remain unchanged if no line endings were detected.
891 pub fn detect_indent_and_line_ending(&mut self) {
893 pub fn detect_indent_and_line_ending(&mut self) {
892 self.indent_style = auto_detect_indent_style(&self.text).unwrap_or_else(|| {
894 self.indent_style = auto_detect_indent_style(&self.text).unwrap_or_else(|| {
893 self.language_config()
895 self.language_config()
894 .and_then(|config| config.indent.as_ref())
896 .and_then(|config| config.indent.as_ref())
895 .map_or(DEFAULT_INDENT, |config| IndentStyle::from_str(&config.unit))
897 .map_or(DEFAULT_INDENT, |config| IndentStyle::from_str(&config.unit))
896 });
898 });
897− self.line_ending = auto_detect_line_ending(&self.text).unwrap_or(DEFAULT_LINE_ENDING);
899+ if let Some(line_ending) = auto_detect_line_ending(&self.text) {
900+ self.line_ending = line_ending;
901+ }
898 }
902 }
899
903
900 /// Reload the document from its path.
904 /// Reload the document from its path.
@@ -1921,7 +1925,7 @@ mod test {
1921 Document::default(Arc::new(ArcSwap::new(Arc::new(Config::default()))))
1925 Document::default(Arc::new(ArcSwap::new(Arc::new(Config::default()))))
1922 .text()
1926 .text()
1923 .to_string(),
1927 .to_string(),
1924− DEFAULT_LINE_ENDING.as_str()
1928+ NATIVE_LINE_ENDING.as_str()
1925 );
1929 );
1926 }
1930 }
1927
1931
helix-view/src/editor.rs+50−2
@@ -44,7 +44,7 @@ pub use helix_core::register::Registers;
44use helix_core::{
44use helix_core::{
45 auto_pairs::AutoPairs,
45 auto_pairs::AutoPairs,
46 syntax::{self, AutoPairConfig, SoftWrap},
46 syntax::{self, AutoPairConfig, SoftWrap},
47− Change,
47+ Change, LineEnding, NATIVE_LINE_ENDING,
48};
48};
49use helix_core::{Position, Selection};
49use helix_core::{Position, Selection};
50use helix_dap as dap;
50use helix_dap as dap;
@@ -273,7 +273,7 @@ pub struct Config {
273 pub search: SearchConfig,
273 pub search: SearchConfig,
274 pub lsp: LspConfig,
274 pub lsp: LspConfig,
275 pub terminal: Option<TerminalConfig>,
275 pub terminal: Option<TerminalConfig>,
276− /// Column numbers at which to draw the rulers. Default to `[]`, meaning no rulers.
276+ /// Column numbers at which to draw the rulers. Defaults to `[]`, meaning no rulers.
277 pub rulers: Vec<u16>,
277 pub rulers: Vec<u16>,
278 #[serde(default)]
278 #[serde(default)]
279 pub whitespace: WhitespaceConfig,
279 pub whitespace: WhitespaceConfig,
@@ -286,6 +286,8 @@ pub struct Config {
286 pub soft_wrap: SoftWrap,
286 pub soft_wrap: SoftWrap,
287 /// Workspace specific lsp ceiling dirs
287 /// Workspace specific lsp ceiling dirs
288 pub workspace_lsp_roots: Vec<PathBuf>,
288 pub workspace_lsp_roots: Vec<PathBuf>,
289+ /// Which line ending to choose for new documents. Defaults to `native`. i.e. `crlf` on Windows, otherwise `lf`.
290+ pub default_line_ending: LineEndingConfig,
289}
291}
290
292
291#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
293#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
@@ -727,6 +729,51 @@ impl Default for IndentGuidesConfig {
727 }
729 }
728}
730}
729
731
732+/// Line ending configuration.
733+#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
734+#[serde(rename_all = "lowercase")]
735+pub enum LineEndingConfig {
736+ /// The platform's native line ending.
737+ ///
738+ /// `crlf` on Windows, otherwise `lf`.
739+ Native,
740+ /// Line feed.
741+ LF,
742+ /// Carriage return followed by line feed.
743+ Crlf,
744+ /// Form feed.
745+ #[cfg(feature = "unicode-lines")]
746+ FF,
747+ /// Carriage return.
748+ #[cfg(feature = "unicode-lines")]
749+ CR,
750+ /// Next line.
751+ #[cfg(feature = "unicode-lines")]
752+ Nel,
753+}
754+
755+impl Default for LineEndingConfig {
756+ fn default() -> Self {
757+ LineEndingConfig::Native
758+ }
759+}
760+
761+impl From<LineEndingConfig> for LineEnding {
762+ fn from(line_ending: LineEndingConfig) -> Self {
763+ match line_ending {
764+ LineEndingConfig::Native => NATIVE_LINE_ENDING,
765+ LineEndingConfig::LF => LineEnding::LF,
766+ LineEndingConfig::Crlf => LineEnding::Crlf,
767+ #[cfg(feature = "unicode-lines")]
768+ LineEndingConfig::FF => LineEnding::FF,
769+ #[cfg(feature = "unicode-lines")]
770+ LineEndingConfig::CR => LineEnding::CR,
771+ #[cfg(feature = "unicode-lines")]
772+ LineEndingConfig::Nel => LineEnding::Nel,
773+ }
774+ }
775+}
776+
730impl Default for Config {
777impl Default for Config {
731 fn default() -> Self {
778 fn default() -> Self {
732 Self {
779 Self {
@@ -771,6 +818,7 @@ impl Default for Config {
771 text_width: 80,
818 text_width: 80,
772 completion_replace: false,
819 completion_replace: false,
773 workspace_lsp_roots: Vec::new(),
820 workspace_lsp_roots: Vec::new(),
821+ default_line_ending: LineEndingConfig::default(),
774 }
822 }
775 }
823 }
776}
824}