Repository: lapce/lapce (language: rust)

Issue:
[CRASH] Carrige return (followed by string) crashes Lapce
## Lapce Version

v0.3.1

## System information

Arch linux kernel 6.7.6-arch1-1 (64-bit)
KDE Plasma version 5.27.10

## Describe the bug
When opening a text file with the a carriage return (`\r`) followed by any string in it, Lapce crashes instantly

## Steps to reproduce

1. Run `printf "\re" > ./test.txt`
or similar with a different string
3. Open `./test.txt` in Lapce

## Additional information

- Does not happen with newline (`\n`)



Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 09f2458ace..f9d5901a33 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@
 - [#2918](https://github.com/lapce/lapce/pull/2918): Allow searching the shortcuts overview by shortcut (e.g. "Ctrl+P") or when condition (e.g. "list_focus")
 - [#2955](https://github.com/lapce/lapce/pull/2955): Using glob patterns to hide files or directories in explorer
 - [#3026](https://github.com/lapce/lapce/pull/3026): Add missing file explorer context menu entries
+- [#3047](https://github.com/lapce/lapce/pull/3047): Add support for different CrLf/Lf line endings per-file
 
 ### Bug Fixes
 - [#2779](https://github.com/lapce/lapce/pull/2779): Fix files detection on fresh git/VCS repository
diff --git a/Cargo.lock b/Cargo.lock
index 9ff851a036..f75acf45f0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -119,7 +119,7 @@ dependencies = [
  "miow",
  "parking_lot 0.12.1",
  "piper",
- "polling 3.2.0",
+ "polling",
  "regex-automata 0.3.9",
  "rustix-openpty",
  "serde",
@@ -244,31 +244,31 @@ dependencies = [
 
 [[package]]
 name = "ashpd"
-version = "0.4.0"
+version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31688b40eb5d739049f721d8405c33d3796b3f51f2bea84421a542dafe397e41"
+checksum = "1b22517ee647547c01a687cf9b76074e1c91334032a4324f7243c6ee0f949390"
 dependencies = [
- "async-std",
  "enumflags2",
  "futures-channel",
  "futures-util",
- "once_cell",
  "rand",
  "serde",
  "serde_repr",
+ "tokio",
  "url",
  "zbus",
 ]
 
 [[package]]
 name = "async-broadcast"
-version = "0.4.1"
+version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d26004fe83b2d1cd3a97609b21e39f9a31535822210fe83205d2ce48866ea61"
+checksum = "258b52a1aa741b9f09783b2d86cf0aeeb617bbf847f6933340a39644227acbdb"
 dependencies = [
- "event-listener",
+ "event-listener 5.2.0",
+ "event-listener-strategy 0.5.0",
  "futures-core",
- "parking_lot 0.12.1",
+ "pin-project-lite",
 ]
 
 [[package]]
@@ -278,57 +278,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28"
 dependencies = [
  "concurrent-queue 1.2.4",
- "event-listener",
+ "event-listener 2.5.3",
  "futures-core",
 ]
 
 [[package]]
-name = "async-executor"
-version = "1.5.1"
+name = "async-channel"
+version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb"
+checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3"
 dependencies = [
- "async-lock",
- "async-task",
  "concurrent-queue 2.2.0",
- "fastrand 1.8.0",
- "futures-lite",
- "slab",
-]
-
-[[package]]
-name = "async-global-executor"
-version = "2.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776"
-dependencies = [
- "async-channel",
- "async-executor",
- "async-io",
- "async-lock",
- "blocking",
- "futures-lite",
- "once_cell",
+ "event-listener 5.2.0",
+ "event-listener-strategy 0.5.0",
+ "futures-core",
+ "pin-project-lite",
 ]
 
 [[package]]
 name = "async-io"
-version = "1.13.0"
+version = "2.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af"
+checksum = "8f97ab0c5b00a7cdbe5a371b9a782ee7be1316095885c8a4ea1daf490eb0ef65"
 dependencies = [
- "async-lock",
- "autocfg",
+ "async-lock 3.3.0",
  "cfg-if",
  "concurrent-queue 2.2.0",
- "futures-lite",
- "log",
+ "futures-io",
+ "futures-lite 2.2.0",
  "parking",
- "polling 2.8.0",
- "rustix 0.37.27",
+ "polling",
+ "rustix",
  "slab",
- "socket2 0.4.7",
- "waker-fn",
+ "tracing 0.1.37",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
@@ -337,44 +320,65 @@ version = "2.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b"
 dependencies = [
- "event-listener",
+ "event-listener 2.5.3",
+]
+
+[[package]]
+name = "async-lock"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b"
+dependencies = [
+ "event-listener 4.0.3",
+ "event-listener-strategy 0.4.0",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-process"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "451e3cf68011bd56771c79db04a9e333095ab6349f7e47592b788e9b98720cc8"
+dependencies = [
+ "async-channel 2.2.0",
+ "async-io",
+ "async-lock 3.3.0",
+ "async-signal",
+ "blocking",
+ "cfg-if",
+ "event-listener 5.2.0",
+ "futures-lite 2.2.0",
+ "rustix",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "async-recursion"
-version = "0.3.2"
+version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2"
+checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 1.0.101",
+ "syn 2.0.38",
 ]
 
 [[package]]
-name = "async-std"
-version = "1.12.0"
+name = "async-signal"
+version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d"
+checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5"
 dependencies = [
- "async-channel",
- "async-global-executor",
  "async-io",
- "async-lock",
- "crossbeam-utils",
- "futures-channel",
+ "async-lock 2.8.0",
+ "atomic-waker",
+ "cfg-if",
  "futures-core",
  "futures-io",
- "futures-lite",
- "gloo-timers",
- "kv-log-macro",
- "log",
- "memchr",
- "once_cell",
- "pin-project-lite",
- "pin-utils",
+ "rustix",
+ "signal-hook-registry",
  "slab",
- "wasm-bindgen-futures",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -527,11 +531,11 @@ version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc"
 dependencies = [
- "async-channel",
+ "async-channel 1.7.1",
  "async-task",
  "atomic-waker",
  "fastrand 1.8.0",
- "futures-lite",
+ "futures-lite 1.12.0",
  "once_cell",
 ]
 
@@ -604,8 +608,8 @@ checksum = "7b50b5a44d59a98c55a9eeb518f39bf7499ba19fd98ee7d22618687f3f10adbf"
 dependencies = [
  "bitflags 2.4.0",
  "log",
- "polling 3.2.0",
- "rustix 0.38.25",
+ "polling",
+ "rustix",
  "slab",
  "thiserror",
 ]
@@ -617,7 +621,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02"
 dependencies = [
  "calloop",
- "rustix 0.38.25",
+ "rustix",
  "wayland-backend",
  "wayland-client",
 ]
@@ -630,7 +634,7 @@ checksum = "b779b2d0a001c125b4584ad586268fb4b92d957bff8d26d7fe0dd78283faa814"
 dependencies = [
  "cap-primitives",
  "cap-std",
- "io-lifetimes 2.0.2",
+ "io-lifetimes",
  "windows-sys 0.48.0",
 ]
 
@@ -642,7 +646,7 @@ checksum = "6ffc30dee200c20b4dcb80572226f42658e1d9c4b668656d7cc59c33d50e396e"
 dependencies = [
  "cap-primitives",
  "cap-std",
- "rustix 0.38.25",
+ "rustix",
  "smallvec",
 ]
 
@@ -655,10 +659,10 @@ dependencies = [
  "ambient-authority",
  "fs-set-times",
  "io-extras",
- "io-lifetimes 2.0.2",
+ "io-lifetimes",
  "ipnet",
  "maybe-owned",
- "rustix 0.38.25",
+ "rustix",
  "windows-sys 0.48.0",
  "winx",
 ]
@@ -681,8 +685,8 @@ checksum = "84bade423fa6403efeebeafe568fdb230e8c590a275fba2ba978dd112efcf6e9"
 dependencies = [
  "cap-primitives",
  "io-extras",
- "io-lifetimes 2.0.2",
- "rustix 0.38.25",
+ "io-lifetimes",
+ "rustix",
 ]
 
 [[package]]
@@ -693,7 +697,7 @@ checksum = "f8f52b3c8f4abfe3252fd0a071f3004aaa3b18936ec97bdbd8763ce03aff6247"
 dependencies = [
  "cap-primitives",
  "once_cell",
- "rustix 0.38.25",
+ "rustix",
  "winx",
 ]
 
@@ -1255,7 +1259,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "strsim",
- "syn 1.0.101",
+ "syn 1.0.109",
 ]
 
 [[package]]
@@ -1266,7 +1270,7 @@ checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
 dependencies = [
  "darling_core",
  "quote",
- "syn 1.0.101",
+ "syn 1.0.109",
 ]
 
 [[package]]
@@ -1292,7 +1296,7 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 1.0.101",
+ "syn 1.0.109",
 ]
 
 [[package]]
@@ -1396,7 +1400,7 @@ dependencies = [
  "bytemuck",
  "drm-ffi",
  "drm-fourcc",
- "rustix 0.38.25",
+ "rustix",
 ]
 
 [[package]]
@@ -1406,7 +1410,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "41334f8405792483e32ad05fbb9c5680ff4e84491883d2947a4757dc54cb2ac6"
 dependencies = [
  "drm-sys",
- "rustix 0.38.25",
+ "rustix",
 ]
 
 [[package]]
@@ -1440,7 +1444,7 @@ dependencies = [
  "enum-ordinalize",
  "proc-macro2",
  "quote",
- "syn 1.0.101",
+ "syn 1.0.109",
 ]
 
 [[package]]
@@ -1467,6 +1471,12 @@ dependencies = [
  "encoding_rs",
 ]
 
+[[package]]
+name = "endi"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf"
+
 [[package]]
 name = "enum-ordinalize"
 version = "3.1.13"
@@ -1543,6 +1553,48 @@ version = "2.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
 
+[[package]]
+name = "event-listener"
+version = "4.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e"
+dependencies = [
+ "concurrent-queue 2.2.0",
+ "parking",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "event-listener"
+version = "5.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b5fb89194fa3cad959b833185b3063ba881dbfc7030680b314250779fb4cc91"
+dependencies = [
+ "concurrent-queue 2.2.0",
+ "parking",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "event-listener-strategy"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3"
+dependencies = [
+ "event-listener 4.0.3",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "event-listener-strategy"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "feedafcaa9b749175d5ac357452a9d41ea2911da598fde46ce1fe02c37751291"
+dependencies = [
+ "event-listener 5.2.0",
+ "pin-project-lite",
+]
+
 [[package]]
 name = "exr"
 version = "1.71.0"
@@ -1587,7 +1639,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0b0377f1edc77dbd1118507bc7a66e4ab64d2b90c66f90726dc801e73a8c68f9"
 dependencies = [
  "cfg-if",
- "rustix 0.38.25",
+ "rustix",
  "windows-sys 0.48.0",
 ]
 
@@ -1637,7 +1689,7 @@ checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
 [[package]]
 name = "floem"
 version = "0.1.1"
-source = "git+https://github.com/lapce/floem?rev=341afd255f98ca431e4f615864daa4dbcc057501#341afd255f98ca431e4f615864daa4dbcc057501"
+source = "git+https://github.com/lapce/floem?rev=764857c98dfdc2beb10966636da4cf79b677a089#764857c98dfdc2beb10966636da4cf79b677a089"
 dependencies = [
  "bitflags 2.4.0",
  "copypasta",
@@ -1699,11 +1751,13 @@ dependencies = [
 [[package]]
 name = "floem-editor-core"
 version = "0.1.1"
-source = "git+https://github.com/lapce/floem?rev=341afd255f98ca431e4f615864daa4dbcc057501#341afd255f98ca431e4f615864daa4dbcc057501"
+source = "git+https://github.com/lapce/floem?rev=764857c98dfdc2beb10966636da4cf79b677a089#764857c98dfdc2beb10966636da4cf79b677a089"
 dependencies = [
  "bitflags 1.3.2",
  "itertools",
  "lapce-xi-rope",
+ "memchr",
+ "once_cell",
  "serde",
  "strum",
  "strum_macros",
@@ -1762,7 +1816,7 @@ dependencies = [
  "raw-window-handle 0.5.2",
  "raw-window-handle 0.6.0",
  "redox_syscall 0.3.5",
- "rustix 0.38.25",
+ "rustix",
  "sctk-adwaita",
  "serde",
  "smithay-client-toolkit",
@@ -1785,7 +1839,7 @@ dependencies = [
 [[package]]
 name = "floem_reactive"
 version = "0.1.1"
-source = "git+https://github.com/lapce/floem?rev=341afd255f98ca431e4f615864daa4dbcc057501#341afd255f98ca431e4f615864daa4dbcc057501"
+source = "git+https://github.com/lapce/floem?rev=764857c98dfdc2beb10966636da4cf79b677a089#764857c98dfdc2beb10966636da4cf79b677a089"
 dependencies = [
  "smallvec",
 ]
@@ -1793,7 +1847,7 @@ dependencies = [
 [[package]]
 name = "floem_renderer"
 version = "0.1.1"
-source = "git+https://github.com/lapce/floem?rev=341afd255f98ca431e4f615864daa4dbcc057501#341afd255f98ca431e4f615864daa4dbcc057501"
+source = "git+https://github.com/lapce/floem?rev=764857c98dfdc2beb10966636da4cf79b677a089#764857c98dfdc2beb10966636da4cf79b677a089"
 dependencies = [
  "floem-cosmic-text",
  "floem-peniko",
@@ -1804,7 +1858,7 @@ dependencies = [
 [[package]]
 name = "floem_tiny_skia_renderer"
 version = "0.1.1"
-source = "git+https://github.com/lapce/floem?rev=341afd255f98ca431e4f615864daa4dbcc057501#341afd255f98ca431e4f615864daa4dbcc057501"
+source = "git+https://github.com/lapce/floem?rev=764857c98dfdc2beb10966636da4cf79b677a089#764857c98dfdc2beb10966636da4cf79b677a089"
 dependencies = [
  "anyhow",
  "bytemuck",
@@ -1821,7 +1875,7 @@ dependencies = [
 [[package]]
 name = "floem_vger_renderer"
 version = "0.1.1"
-source = "git+https://github.com/lapce/floem?rev=341afd255f98ca431e4f615864daa4dbcc057501#341afd255f98ca431e4f615864daa4dbcc057501"
+source = "git+https://github.com/lapce/floem?rev=764857c98dfdc2beb10966636da4cf79b677a089#764857c98dfdc2beb10966636da4cf79b677a089"
 dependencies = [
  "anyhow",
  "floem-peniko",
@@ -1957,8 +2011,8 @@ version = "0.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dd738b84894214045e8414eaded76359b4a5773f0a0a56b16575110739cdcf39"
 dependencies = [
- "io-lifetimes 2.0.2",
- "rustix 0.38.25",
+ "io-lifetimes",
+ "rustix",
  "windows-sys 0.48.0",
 ]
 
@@ -2050,6 +2104,19 @@ dependencies = [
  "waker-fn",
 ]
 
+[[package]]
+name = "futures-lite"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "445ba825b27408685aaecefd65178908c36c6e96aaf6d8599419d46e624192ba"
+dependencies = [
+ "fastrand 2.0.1",
+ "futures-core",
+ "futures-io",
+ "parking",
+ "pin-project-lite",
+]
+
 [[package]]
 name = "futures-macro"
 version = "0.3.28"
@@ -2231,18 +2298,6 @@ dependencies = [
  "regex-syntax 0.8.2",
 ]
 
-[[package]]
-name = "gloo-timers"
-version = "0.2.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c"
-dependencies = [
- "futures-channel",
- "futures-core",
- "js-sys",
- "wasm-bindgen",
-]
-
 [[package]]
 name = "glow"
 version = "0.13.0"
@@ -2704,7 +2759,7 @@ dependencies = [
  "proc-macro-hack",
  "proc-macro2",
  "quote",
- "syn 1.0.101",
+ "syn 1.0.109",
 ]
 
 [[package]]
@@ -2789,18 +2844,7 @@ version = "0.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9d3c230ee517ee76b1cc593b52939ff68deda3fae9e41eca426c6b4993df51c4"
 dependencies = [
- "io-lifetimes 2.0.2",
- "windows-sys 0.48.0",
-]
-
-[[package]]
-name = "io-lifetimes"
-version = "1.0.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
-dependencies = [
- "hermit-abi 0.3.2",
- "libc",
+ "io-lifetimes",
  "windows-sys 0.48.0",
 ]
 
@@ -2988,15 +3032,6 @@ dependencies = [
  "serde",
 ]
 
-[[package]]
-name = "kv-log-macro"
-version = "1.0.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
-dependencies = [
- "log",
-]
-
 [[package]]
 name = "lapce"
 version = "0.3.1"
@@ -3133,7 +3168,7 @@ dependencies = [
  "objc",
  "once_cell",
  "parking_lot 0.11.2",
- "polling 3.2.0",
+ "polling",
  "psp-types",
  "regex",
  "reqwest",
@@ -3290,12 +3325,6 @@ dependencies = [
  "safemem",
 ]
 
-[[package]]
-name = "linux-raw-sys"
-version = "0.3.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
-
 [[package]]
 name = "linux-raw-sys"
 version = "0.4.13"
@@ -3338,7 +3367,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
 dependencies = [
  "serde",
- "value-bag",
 ]
 
 [[package]]
@@ -3379,9 +3407,9 @@ checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4"
 
 [[package]]
 name = "memchr"
-version = "2.6.4"
+version = "2.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
+checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
 
 [[package]]
 name = "memfd"
@@ -3389,7 +3417,7 @@ version = "0.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64"
 dependencies = [
- "rustix 0.38.25",
+ "rustix",
 ]
 
 [[package]]
@@ -3584,26 +3612,26 @@ dependencies = [
 
 [[package]]
 name = "nix"
-version = "0.24.2"
+version = "0.26.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc"
+checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
 dependencies = [
  "bitflags 1.3.2",
  "cfg-if",
  "libc",
- "memoffset 0.6.5",
+ "memoffset 0.7.1",
 ]
 
 [[package]]
 name = "nix"
-version = "0.26.4"
+version = "0.27.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
+checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.4.0",
  "cfg-if",
  "libc",
- "memoffset 0.7.1",
+ "memoffset 0.9.0",
 ]
 
 [[package]]
@@ -3867,7 +3895,7 @@ checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 1.0.101",
+ "syn 1.0.109",
 ]
 
 [[package]]
@@ -3909,9 +3937,9 @@ dependencies = [
 
 [[package]]
 name = "ordered-stream"
-version = "0.0.1"
+version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44630c059eacfd6e08bdaa51b1db2ce33119caa4ddc1235e923109aa5f25ccb1"
+checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50"
 dependencies = [
  "futures-core",
  "pin-project-lite",
@@ -3937,7 +3965,7 @@ dependencies = [
  "proc-macro-error",
  "proc-macro2",
  "quote",
- "syn 1.0.101",
+ "syn 1.0.109",
 ]
 
 [[package]]
@@ -3957,9 +3985,9 @@ dependencies = [
 
 [[package]]
 name = "parking"
-version = "2.0.0"
+version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
+checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"
 
 [[package]]
 name = "parking_lot"
@@ -4129,22 +4157,6 @@ dependencies = [
  "miniz_oxide",
 ]
 
-[[package]]
-name = "polling"
-version = "2.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce"
-dependencies = [
- "autocfg",
- "bitflags 1.3.2",
- "cfg-if",
- "concurrent-queue 2.2.0",
- "libc",
- "log",
- "pin-project-lite",
- "windows-sys 0.48.0",
-]
-
 [[package]]
 name = "polling"
 version = "3.2.0"
@@ -4154,7 +4166,7 @@ dependencies = [
  "cfg-if",
  "concurrent-queue 2.2.0",
  "pin-project-lite",
- "rustix 0.38.25",
+ "rustix",
  "tracing 0.1.37",
  "windows-sys 0.48.0",
 ]
@@ -4196,7 +4208,7 @@ dependencies = [
  "proc-macro-error-attr",
  "proc-macro2",
  "quote",
- "syn 1.0.101",
+ "syn 1.0.109",
  "version_check",
 ]
 
@@ -4573,27 +4585,25 @@ dependencies = [
 
 [[package]]
 name = "rfd"
-version = "0.11.4"
+version = "0.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fe664af397d2b6a13a8ba1d172a2b5c87c6c5149039edbf8fa122b98c9ed96f"
+checksum = "373d2fc6310e2d14943d4e66ebed5b774a2b6b3b1610e7377edf124fb2760d6b"
 dependencies = [
  "ashpd",
- "async-io",
  "block",
  "dispatch",
- "futures-util",
  "js-sys",
  "log",
  "objc",
  "objc-foundation",
  "objc_id",
  "pollster",
- "raw-window-handle 0.5.2",
+ "raw-window-handle 0.6.0",
  "urlencoding",
  "wasm-bindgen",
  "wasm-bindgen-futures",
  "web-sys",
- "windows 0.44.0",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -4648,20 +4658,6 @@ dependencies = [
  "semver",
 ]
 
-[[package]]
-name = "rustix"
-version = "0.37.27"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2"
-dependencies = [
- "bitflags 1.3.2",
- "errno",
- "io-lifetimes 1.0.11",
- "libc",
- "linux-raw-sys 0.3.8",
- "windows-sys 0.48.0",
-]
-
 [[package]]
 name = "rustix"
 version = "0.38.25"
@@ -4685,7 +4681,7 @@ checksum = "a25c3aad9fc1424eb82c88087789a7d938e1829724f3e4043163baf0d13cfc12"
 dependencies = [
  "errno",
  "libc",
- "rustix 0.38.25",
+ "rustix",
 ]
 
 [[package]]
@@ -4860,7 +4856,7 @@ checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 1.0.101",
+ "syn 1.0.109",
 ]
 
 [[package]]
@@ -4899,19 +4895,15 @@ dependencies = [
 
 [[package]]
 name = "sha1"
-version = "0.6.1"
+version = "0.10.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770"
+checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
 dependencies = [
- "sha1_smol",
+ "cfg-if",
+ "cpufeatures",
+ "digest",
 ]
 
-[[package]]
-name = "sha1_smol"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
-
 [[package]]
 name = "sha2"
 version = "0.10.7"
@@ -5050,7 +5042,7 @@ dependencies = [
  "libc",
  "log",
  "memmap2 0.9.0",
- "rustix 0.38.25",
+ "rustix",
  "thiserror",
  "wayland-backend",
  "wayland-client",
@@ -5122,7 +5114,7 @@ dependencies = [
  "objc",
  "raw-window-handle 0.5.2",
  "redox_syscall 0.4.1",
- "rustix 0.38.25",
+ "rustix",
  "tiny-xlib",
  "wasm-bindgen",
  "wayland-backend",
@@ -5219,7 +5211,7 @@ dependencies = [
  "darling",
  "proc-macro2",
  "quote",
- "syn 1.0.101",
+ "syn 1.0.109",
 ]
 
 [[package]]
@@ -5237,7 +5229,7 @@ dependencies = [
  "heck 0.3.3",
  "proc-macro2",
  "quote",
- "syn 1.0.101",
+ "syn 1.0.109",
 ]
 
 [[package]]
@@ -5272,9 +5264,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "1.0.101"
+version = "1.0.109"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -5342,8 +5334,8 @@ dependencies = [
  "cap-fs-ext",
  "cap-std",
  "fd-lock",
- "io-lifetimes 2.0.2",
- "rustix 0.38.25",
+ "io-lifetimes",
+ "rustix",
  "windows-sys 0.48.0",
  "winx",
 ]
@@ -5573,6 +5565,7 @@ dependencies = [
  "signal-hook-registry",
  "socket2 0.5.5",
  "tokio-macros",
+ "tracing 0.1.37",
  "windows-sys 0.48.0",
 ]
 
@@ -5725,7 +5718,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 1.0.101",
+ "syn 1.0.109",
 ]
 
 [[package]]
@@ -5948,10 +5941,11 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
 
 [[package]]
 name = "uds_windows"
-version = "1.0.2"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce65604324d3cce9b966701489fbd0cf318cb1f7bd9dd07ac9a4ee6fb791930d"
+checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9"
 dependencies = [
+ "memoffset 0.9.0",
  "tempfile",
  "winapi",
 ]
@@ -6136,12 +6130,6 @@ version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc"
 
-[[package]]
-name = "value-bag"
-version = "1.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "126e423afe2dd9ac52142e7e9d5ce4135d7e13776c529d27fd6bc49f19e3280b"
-
 [[package]]
 name = "vcpkg"
 version = "0.2.15"
@@ -6224,9 +6212,9 @@ dependencies = [
  "cap-time-ext",
  "fs-set-times",
  "io-extras",
- "io-lifetimes 2.0.2",
+ "io-lifetimes",
  "once_cell",
- "rustix 0.38.25",
+ "rustix",
  "system-interface",
  "tracing 0.1.37",
  "wasi-common",
@@ -6245,7 +6233,7 @@ dependencies = [
  "cap-std",
  "io-extras",
  "log",
- "rustix 0.38.25",
+ "rustix",
  "thiserror",
  "tracing 0.1.37",
  "wasmtime",
@@ -6427,7 +6415,7 @@ dependencies = [
  "bincode",
  "directories-next",
  "log",
- "rustix 0.38.25",
+ "rustix",
  "serde",
  "serde_derive",
  "sha2",
@@ -6529,7 +6517,7 @@ checksum = "8702d1efdf73df040a586e239fffc6883d88edf60ac6f593b41392cc1f97c754"
 dependencies = [
  "cc",
  "cfg-if",
- "rustix 0.38.25",
+ "rustix",
  "wasmtime-asm-macros",
  "wasmtime-versioned-export-macros",
  "windows-sys 0.48.0",
@@ -6551,7 +6539,7 @@ dependencies = [
  "log",
  "object",
  "rustc-demangle",
- "rustix 0.38.25",
+ "rustix",
  "serde",
  "serde_derive",
  "target-lexicon",
@@ -6570,7 +6558,7 @@ checksum = "0fa128cdc680b5982087ea64eb73b63e96570b338fd6438b704b313eb854fd94"
 dependencies = [
  "object",
  "once_cell",
- "rustix 0.38.25",
+ "rustix",
  "wasmtime-versioned-export-macros",
 ]
 
@@ -6603,7 +6591,7 @@ dependencies = [
  "memoffset 0.9.0",
  "paste",
  "rand",
- "rustix 0.38.25",
+ "rustix",
  "sptr",
  "wasm-encoder",
  "wasmtime-asm-macros",
@@ -6657,11 +6645,11 @@ dependencies = [
  "fs-set-times",
  "futures",
  "io-extras",
- "io-lifetimes 2.0.2",
+ "io-lifetimes",
  "libc",
  "log",
  "once_cell",
- "rustix 0.38.25",
+ "rustix",
  "system-interface",
  "thiserror",
  "tokio",
@@ -7467,7 +7455,7 @@ dependencies = [
  "libc",
  "libloading 0.8.1",
  "once_cell",
- "rustix 0.38.25",
+ "rustix",
  "x11rb-protocol 0.13.0",
 ]
 
@@ -7504,6 +7492,16 @@ dependencies = [
  "nom",
 ]
 
+[[package]]
+name = "xdg-home"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21e5a325c3cb8398ad6cf859c1135b25dd29e186679cf2da7581d9679f63b38e"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
 [[package]]
 name = "xkbcommon-dl"
 version = "0.4.1"
@@ -7555,38 +7553,33 @@ checksum = "c94451ac9513335b5e23d7a8a2b61a7102398b8cca5160829d313e84c9d98be1"
 
 [[package]]
 name = "zbus"
-version = "3.3.0"
+version = "4.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41ce2de393c874ba871292e881bf3c13a0d5eb38170ebab2e50b4c410eaa222b"
+checksum = "7b8e3d6ae3342792a6cc2340e4394334c7402f3d793b390d2c5494a4032b3030"
 dependencies = [
  "async-broadcast",
- "async-channel",
- "async-executor",
- "async-io",
- "async-lock",
+ "async-process",
  "async-recursion",
- "async-task",
  "async-trait",
- "byteorder",
  "derivative",
- "dirs",
  "enumflags2",
- "event-listener",
+ "event-listener 5.2.0",
  "futures-core",
  "futures-sink",
  "futures-util",
  "hex",
- "nix 0.24.2",
- "once_cell",
+ "nix 0.27.1",
  "ordered-stream",
  "rand",
  "serde",
  "serde_repr",
  "sha1",
  "static_assertions",
+ "tokio",
  "tracing 0.1.37",
  "uds_windows",
- "winapi",
+ "windows-sys 0.52.0",
+ "xdg-home",
  "zbus_macros",
  "zbus_names",
  "zvariant",
@@ -7594,22 +7587,23 @@ dependencies = [
 
 [[package]]
 name = "zbus_macros"
-version = "3.3.0"
+version = "4.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a13d08f5dc6cf725b693cb6ceacd43cd430ec0664a879188f29e7d7dcd98f96d"
+checksum = "b7a3e850ff1e7217a3b7a07eba90d37fe9bb9e89a310f718afcde5885ca9b6d7"
 dependencies = [
  "proc-macro-crate",
  "proc-macro2",
  "quote",
  "regex",
- "syn 1.0.101",
+ "syn 1.0.109",
+ "zvariant_utils",
 ]
 
 [[package]]
 name = "zbus_names"
-version = "2.2.0"
+version = "3.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41a408fd8a352695690f53906dc7fd036be924ec51ea5e05666ff42685ed0af5"
+checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c"
 dependencies = [
  "serde",
  "static_assertions",
@@ -7674,13 +7668,12 @@ dependencies = [
 
 [[package]]
 name = "zvariant"
-version = "3.7.1"
+version = "4.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b794fb7f59af4105697b0449ba31731ee5dbb3e773a17dbdf3d36206ea1b1644"
+checksum = "4e09e8be97d44eeab994d752f341e67b3b0d80512a8b315a0671d47232ef1b65"
 dependencies = [
- "byteorder",
+ "endi",
  "enumflags2",
- "libc",
  "serde",
  "static_assertions",
  "url",
@@ -7689,12 +7682,24 @@ dependencies = [
 
 [[package]]
 name = "zvariant_derive"
-version = "3.7.1"
+version = "4.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd58d4b6c8e26d3dd2149c8c40c6613ef6451b9885ff1296d1ac86c388351a54"
+checksum = "72a5857e2856435331636a9fbb415b09243df4521a267c5bedcd5289b4d5799e"
 dependencies = [
  "proc-macro-crate",
  "proc-macro2",
  "quote",
- "syn 1.0.101",
+ "syn 1.0.109",
+ "zvariant_utils",
+]
+
+[[package]]
+name = "zvariant_utils"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00bedb16a193cc12451873fee2a1bc6550225acece0e36f333e68326c73c8172"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index c237522a4f..b8451ccbb0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -75,7 +75,7 @@ lapce-core = { path = "./lapce-core" }
 lapce-rpc = { path = "./lapce-rpc" }
 lapce-proxy = { path = "./lapce-proxy" }
 
-floem-editor-core = { git = "https://github.com/lapce/floem", rev = "341afd255f98ca431e4f615864daa4dbcc057501", features = ["serde"] }
+floem-editor-core = { git = "https://github.com/lapce/floem", rev = "764857c98dfdc2beb10966636da4cf79b677a089", features = ["serde"] }
 # floem-editor-core = { path = "../workspaces/floem/editor-core/", features = ["serde"] }
 
 [patch.crates-io]
diff --git a/defaults/settings.toml b/defaults/settings.toml
index b0b8dd7561..1615dd3a6c 100644
--- a/defaults/settings.toml
+++ b/defaults/settings.toml
@@ -34,6 +34,7 @@ highlight-selection-occurrences = true
 highlight-scope-lines = false
 autosave-interval = 0
 format-on-autosave = true
+normalize-line-endings = true
 enable-inlay-hints = true
 inlay-hint-font-family = ""
 inlay-hint-font-size = 0
diff --git a/lapce-app/Cargo.toml b/lapce-app/Cargo.toml
index 18157e723e..fb433f7280 100644
--- a/lapce-app/Cargo.toml
+++ b/lapce-app/Cargo.toml
@@ -53,7 +53,7 @@ sled = "0.34.7"
 bytemuck = "1.14.0"
 tokio = { version = "1.36", features = ["full"] }
 futures = "0.3.26"
-floem = { git = "https://github.com/lapce/floem", rev = "341afd255f98ca431e4f615864daa4dbcc057501", features = ["serde"] }
+floem = { git = "https://github.com/lapce/floem", rev = "764857c98dfdc2beb10966636da4cf79b677a089", features = ["serde"] }
 # floem = { path = "../../workspaces/floem", features = ["serde"] }
 config = { version = "0.13.2", default-features = false, features = ["toml"] }
 structdesc = { git = "https://github.com/lapce/structdesc" }
diff --git a/lapce-app/src/app.rs b/lapce-app/src/app.rs
index 90a5902af1..537992488e 100644
--- a/lapce-app/src/app.rs
+++ b/lapce-app/src/app.rs
@@ -2247,6 +2247,7 @@ fn palette_item(
         | PaletteItemContent::Workspace { .. }
         | PaletteItemContent::SshHost { .. }
         | PaletteItemContent::Language { .. }
+        | PaletteItemContent::LineEnding { .. }
         | PaletteItemContent::ColorTheme { .. }
         | PaletteItemContent::SCMReference { .. }
         | PaletteItemContent::TerminalProfile { .. }
diff --git a/lapce-app/src/command.rs b/lapce-app/src/command.rs
index ca519e3540..4964f563cb 100644
--- a/lapce-app/src/command.rs
+++ b/lapce-app/src/command.rs
@@ -470,6 +470,10 @@ pub enum LapceWorkbenchCommand {
     #[strum(message = "Change current file language")]
     ChangeFileLanguage,
 
+    #[strum(serialize = "change_file_line_ending")]
+    #[strum(message = "Change current file line ending")]
+    ChangeFileLineEnding,
+
     #[strum(serialize = "next_editor_tab")]
     #[strum(message = "Next Editor Tab")]
     NextEditorTab,
diff --git a/lapce-app/src/config/editor.rs b/lapce-app/src/config/editor.rs
index 2dde774548..b0ebfa077e 100644
--- a/lapce-app/src/config/editor.rs
+++ b/lapce-app/src/config/editor.rs
@@ -126,6 +126,11 @@ pub struct EditorConfig {
     )]
     pub format_on_save: bool,
 
+    #[field_names(
+        desc = "Whether newlines should be automatically converted to the current line ending"
+    )]
+    pub normalize_line_endings: bool,
+
     #[field_names(desc = "If matching brackets are highlighted")]
     pub highlight_matching_brackets: bool,
 
diff --git a/lapce-app/src/doc.rs b/lapce-app/src/doc.rs
index 84146a2cef..4a2352a827 100644
--- a/lapce-app/src/doc.rs
+++ b/lapce-app/src/doc.rs
@@ -44,6 +44,7 @@ use lapce_core::{
     editor::{Action, EditConf, EditType},
     indent::IndentStyle,
     language::LapceLanguage,
+    line_ending::LineEnding,
     mode::MotionMode,
     register::Register,
     rope_text_pos::RopeTextPosition,
@@ -650,6 +651,13 @@ impl Doc {
         self.buffer.with_untracked(|b| b.rev())
     }
 
+    /// Get the buffer's line-ending.  
+    /// Note: this may not be the same as what the actual line endings in the file are, rather this
+    /// is what the line-ending is set to (and what it will be saved as).
+    pub fn line_ending(&self) -> LineEnding {
+        self.buffer.with_untracked(|b| b.line_ending())
+    }
+
     fn on_update(&self, edits: Option<SmallVec<[SyntaxEdit; 3]>>) {
         batch(|| {
             self.clear_code_actions();
diff --git a/lapce-app/src/editor.rs b/lapce-app/src/editor.rs
index 7e1bef097f..80c88479eb 100644
--- a/lapce-app/src/editor.rs
+++ b/lapce-app/src/editor.rs
@@ -2021,34 +2021,43 @@ impl EditorData {
         }
 
         let config = self.common.config.get_untracked();
-        if let DocContent::File { path, .. } = content {
-            let format_on_save = allow_formatting && config.editor.format_on_save;
-            if format_on_save {
-                let editor = self.clone();
-                let send = create_ext_action(self.scope, move |result| {
-                    if let Ok(Ok(ProxyResponse::GetDocumentFormatting { edits })) =
-                        result
-                    {
-                        let current_rev = editor.doc().rev();
-                        if current_rev == rev {
-                            editor.do_text_edit(&edits);
-                        }
+        let DocContent::File { path, .. } = content else {
+            return;
+        };
+
+        // If we are disallowing formatting (such as due to a manual save without formatting),
+        // then we skip normalizing line endings as a common reason for that is large files.
+        // (but if the save is typical, even if config format_on_save is false, we normalize)
+        if allow_formatting && config.editor.normalize_line_endings {
+            self.run_edit_command(&EditCommand::NormalizeLineEndings);
+        }
+
+        let format_on_save = allow_formatting && config.editor.format_on_save;
+        if format_on_save {
+            let editor = self.clone();
+            let send = create_ext_action(self.scope, move |result| {
+                if let Ok(Ok(ProxyResponse::GetDocumentFormatting { edits })) =
+                    result
+                {
+                    let current_rev = editor.doc().rev();
+                    if current_rev == rev {
+                        editor.do_text_edit(&edits);
                     }
-                    editor.do_save(after_action);
-                });
+                }
+                editor.do_save(after_action);
+            });
 
-                let (tx, rx) = crossbeam_channel::bounded(1);
-                let proxy = self.common.proxy.clone();
-                std::thread::spawn(move || {
-                    proxy.get_document_formatting(path, move |result| {
-                        let _ = tx.send(result);
-                    });
-                    let result = rx.recv_timeout(std::time::Duration::from_secs(1));
-                    send(result);
+            let (tx, rx) = crossbeam_channel::bounded(1);
+            let proxy = self.common.proxy.clone();
+            std::thread::spawn(move || {
+                proxy.get_document_formatting(path, move |result| {
+                    let _ = tx.send(result);
                 });
-            } else {
-                self.do_save(after_action);
-            }
+                let result = rx.recv_timeout(std::time::Duration::from_secs(1));
+                send(result);
+            });
+        } else {
+            self.do_save(after_action);
         }
     }
 
diff --git a/lapce-app/src/focus_text.rs b/lapce-app/src/focus_text.rs
index 42915f6061..453cf25c87 100644
--- a/lapce-app/src/focus_text.rs
+++ b/lapce-app/src/focus_text.rs
@@ -5,7 +5,7 @@ use floem::{
         kurbo::{Point, Rect},
         Color,
     },
-    prop_extracter,
+    prop_extractor,
     reactive::create_effect,
     style::{FontFamily, FontSize, LineHeight, Style, TextColor},
     taffy::prelude::NodeId,
@@ -13,8 +13,8 @@ use floem::{
     Renderer,
 };
 
-prop_extracter! {
-    Extracter {
+prop_extractor! {
+    Extractor {
         color: TextColor,
         font_size: FontSize,
         font_family: FontFamily,
@@ -76,7 +76,7 @@ pub struct FocusText {
     available_text: Option<String>,
     available_width: Option<f32>,
     available_text_layout: Option<TextLayout>,
-    style: Extracter,
+    style: Extractor,
 }
 
 impl FocusText {
diff --git a/lapce-app/src/palette.rs b/lapce-app/src/palette.rs
index 13ffca7481..5cf9d08a63 100644
--- a/lapce-app/src/palette.rs
+++ b/lapce-app/src/palette.rs
@@ -20,7 +20,8 @@ use floem::{
 use itertools::Itertools;
 use lapce_core::{
     buffer::rope_text::RopeText, command::FocusCommand, language::LapceLanguage,
-    mode::Mode, movement::Movement, selection::Selection, syntax::Syntax,
+    line_ending::LineEnding, mode::Mode, movement::Movement, selection::Selection,
+    syntax::Syntax,
 };
 use lapce_rpc::proxy::ProxyResponse;
 use lapce_xi_rope::Rope;
@@ -387,6 +388,9 @@ impl PaletteData {
             PaletteKind::Language => {
                 self.get_languages();
             }
+            PaletteKind::LineEnding => {
+                self.get_line_endings();
+            }
             PaletteKind::SCMReferences => {
                 self.get_scm_references();
             }
@@ -962,6 +966,24 @@ impl PaletteData {
         self.items.set(items);
     }
 
+    fn get_line_endings(&self) {
+        let items = [LineEnding::Lf, LineEnding::CrLf]
+            .iter()
+            .map(|l| PaletteItem {
+                content: PaletteItemContent::LineEnding { kind: *l },
+                filter_text: l.as_str().to_string(),
+                score: 0,
+                indices: Vec::new(),
+            })
+            .collect();
+        if let Some(editor) = self.main_split.active_editor.get_untracked() {
+            let doc = editor.doc();
+            let line_ending = doc.line_ending();
+            self.preselect_matching(&items, line_ending.as_str());
+        }
+        self.items.set(items);
+    }
+
     fn get_scm_references(&self) {
         let branches = self.source_control.branches.get_untracked();
         let tags = self.source_control.tags.get_untracked();
@@ -1217,6 +1239,17 @@ impl PaletteData {
                     }
                     doc.trigger_syntax_change(None);
                 }
+                PaletteItemContent::LineEnding { kind } => {
+                    let Some(editor) = self.main_split.active_editor.get_untracked()
+                    else {
+                        return;
+                    };
+                    let doc = editor.doc();
+
+                    doc.buffer.update(|buffer| {
+                        buffer.set_line_ending(*kind);
+                    });
+                }
                 PaletteItemContent::SCMReference { name } => {
                     self.common
                         .lapce_command
@@ -1297,6 +1330,7 @@ impl PaletteData {
                 #[cfg(windows)]
                 PaletteItemContent::WslHost { .. } => {}
                 PaletteItemContent::Language { .. } => {}
+                PaletteItemContent::LineEnding { .. } => {}
                 PaletteItemContent::Reference { location, .. } => {
                     self.has_preview.set(true);
                     let (doc, new_doc) =
diff --git a/lapce-app/src/palette/item.rs b/lapce-app/src/palette/item.rs
index e80f00b797..8f0cf048bd 100644
--- a/lapce-app/src/palette/item.rs
+++ b/lapce-app/src/palette/item.rs
@@ -1,5 +1,6 @@
 use std::path::PathBuf;
 
+use lapce_core::line_ending::LineEnding;
 use lapce_rpc::dap_types::RunDebugConfig;
 use lsp_types::{Range, SymbolKind};
 
@@ -73,6 +74,9 @@ pub enum PaletteItemContent {
     Language {
         name: String,
     },
+    LineEnding {
+        kind: LineEnding,
+    },
     SCMReference {
         name: String,
     },
diff --git a/lapce-app/src/palette/kind.rs b/lapce-app/src/palette/kind.rs
index b72e8d700a..5cd123073d 100644
--- a/lapce-app/src/palette/kind.rs
+++ b/lapce-app/src/palette/kind.rs
@@ -19,6 +19,7 @@ pub enum PaletteKind {
     ColorTheme,
     IconTheme,
     Language,
+    LineEnding,
     SCMReferences,
     TerminalProfile,
     DiffFiles,
@@ -43,6 +44,7 @@ impl PaletteKind {
             | PaletteKind::ColorTheme
             | PaletteKind::IconTheme
             | PaletteKind::Language
+            | PaletteKind::LineEnding
             | PaletteKind::SCMReferences
             | PaletteKind::DiffFiles => "",
             #[cfg(windows)]
@@ -88,6 +90,9 @@ impl PaletteKind {
             PaletteKind::ColorTheme => Some(LapceWorkbenchCommand::ChangeColorTheme),
             PaletteKind::IconTheme => Some(LapceWorkbenchCommand::ChangeIconTheme),
             PaletteKind::Language => Some(LapceWorkbenchCommand::ChangeFileLanguage),
+            PaletteKind::LineEnding => {
+                Some(LapceWorkbenchCommand::ChangeFileLineEnding)
+            }
             PaletteKind::SCMReferences => {
                 Some(LapceWorkbenchCommand::PaletteSCMReferences)
             }
@@ -118,6 +123,7 @@ impl PaletteKind {
             | PaletteKind::ColorTheme
             | PaletteKind::IconTheme
             | PaletteKind::Language
+            | PaletteKind::LineEnding
             | PaletteKind::SCMReferences
             | PaletteKind::DiffFiles => input,
             PaletteKind::PaletteHelp
diff --git a/lapce-app/src/status.rs b/lapce-app/src/status.rs
index b137bcd7fc..3b7829e342 100644
--- a/lapce-app/src/status.rs
+++ b/lapce-app/src/status.rs
@@ -4,7 +4,7 @@ use std::{
 };
 
 use floem::{
-    reactive::{create_memo, ReadSignal, RwSignal},
+    reactive::{create_memo, Memo, ReadSignal, RwSignal},
     style::{AlignItems, CursorStyle, Display},
     view::View,
     views::{dyn_stack, label, stack, svg, Decorators},
@@ -17,6 +17,7 @@ use crate::{
     app::clickable_icon,
     command::LapceWorkbenchCommand,
     config::{color::LapceColor, icon::LapceIcons, LapceConfig},
+    editor::EditorData,
     listener::Listener,
     palette::kind::PaletteKind,
     panel::{kind::PanelKind, position::PanelContainerPosition},
@@ -290,7 +291,7 @@ pub fn status(
         }),
         stack({
             let palette_clone = palette.clone();
-            let cursor_info = label(move || {
+            let cursor_info = status_text(config, editor, move || {
                 if let Some(editor) = editor.get() {
                     let mut status = String::new();
                     let cursor = editor.cursor().get();
@@ -323,34 +324,21 @@ pub fn status(
             })
             .on_click_stop(move |_| {
                 palette_clone.run(PaletteKind::Line);
+            });
+            let palette_clone = palette.clone();
+            let line_ending_info = status_text(config, editor, move || {
+                if let Some(editor) = editor.get() {
+                    let doc = editor.doc_signal().get();
+                    doc.buffer.with(|b| b.line_ending()).as_str()
+                } else {
+                    ""
+                }
             })
-            .style(move |s| {
-                let config = config.get();
-                s.display(
-                    if editor
-                        .get()
-                        .map(|editor| {
-                            editor.doc_signal().get().content.with(|c| c.is_file())
-                        })
-                        .unwrap_or(false)
-                    {
-                        Display::Flex
-                    } else {
-                        Display::None
-                    },
-                )
-                .height_pct(100.0)
-                .padding_horiz(10.0)
-                .items_center()
-                .color(config.color(LapceColor::STATUS_FOREGROUND))
-                .hover(|s| {
-                    s.cursor(CursorStyle::Pointer).background(
-                        config.color(LapceColor::PANEL_HOVERED_BACKGROUND),
-                    )
-                })
+            .on_click_stop(move |_| {
+                palette_clone.run(PaletteKind::LineEnding);
             });
             let palette_clone = palette.clone();
-            let language_info = label(move || {
+            let language_info = status_text(config, editor, move || {
                 if let Some(editor) = editor.get() {
                     let doc = editor.doc_signal().get();
                     doc.syntax().with(|s| s.language.name())
@@ -360,33 +348,8 @@ pub fn status(
             })
             .on_click_stop(move |_| {
                 palette_clone.run(PaletteKind::Language);
-            })
-            .style(move |s| {
-                let config = config.get();
-                s.display(
-                    if editor
-                        .get()
-                        .map(|editor| {
-                            editor.doc_signal().get().content.with(|c| c.is_file())
-                        })
-                        .unwrap_or(false)
-                    {
-                        Display::Flex
-                    } else {
-                        Display::None
-                    },
-                )
-                .height_pct(100.0)
-                .padding_horiz(10.0)
-                .items_center()
-                .color(config.color(LapceColor::STATUS_FOREGROUND))
-                .hover(|s| {
-                    s.cursor(CursorStyle::Pointer).background(
-                        config.color(LapceColor::PANEL_HOVERED_BACKGROUND),
-                    )
-                })
             });
-            (cursor_info, language_info)
+            (cursor_info, line_ending_info, language_info)
         })
         .style(|s| {
             s.height_pct(100.0)
@@ -436,3 +399,32 @@ fn progress_view(
         },
     )
 }
+
+fn status_text<S: std::fmt::Display + 'static>(
+    config: ReadSignal<Arc<LapceConfig>>,
+    editor: Memo<Option<Rc<EditorData>>>,
+    text: impl Fn() -> S + 'static,
+) -> impl View {
+    label(text).style(move |s| {
+        let config = config.get();
+        let display = if editor
+            .get()
+            .map(|editor| editor.doc_signal().get().content.with(|c| c.is_file()))
+            .unwrap_or(false)
+        {
+            Display::Flex
+        } else {
+            Display::None
+        };
+
+        s.display(display)
+            .height_full()
+            .padding_horiz(10.0)
+            .items_center()
+            .color(config.color(LapceColor::STATUS_FOREGROUND))
+            .hover(|s| {
+                s.cursor(CursorStyle::Pointer)
+                    .background(config.color(LapceColor::PANEL_HOVERED_BACKGROUND))
+            })
+    })
+}
diff --git a/lapce-app/src/text_input.rs b/lapce-app/src/text_input.rs
index 0105510210..f1b2f997fb 100644
--- a/lapce-app/src/text_input.rs
+++ b/lapce-app/src/text_input.rs
@@ -10,7 +10,7 @@ use floem::{
         kurbo::{Line, Point, Rect, Size, Vec2},
         Color,
     },
-    prop_extracter,
+    prop_extractor,
     reactive::{create_effect, create_memo, create_rw_signal, ReadSignal, RwSignal},
     style::{
         CursorStyle, FontFamily, FontSize, FontStyle, FontWeight, LineHeight,
@@ -34,8 +34,8 @@ use crate::{
     keypress::KeyPressFocus,
 };
 
-prop_extracter! {
-    Extracter {
+prop_extractor! {
+    Extractor {
         color: TextColor,
         font_size: FontSize,
         font_family: FontFamily,
@@ -261,7 +261,7 @@ pub struct TextInput {
     on_cursor_pos: Option<Box<dyn Fn(Point)>>,
     hide_cursor: RwSignal<bool>,
     config: ReadSignal<Arc<LapceConfig>>,
-    style: Extracter,
+    style: Extractor,
 }
 
 impl TextInput {
diff --git a/lapce-app/src/window_tab.rs b/lapce-app/src/window_tab.rs
index 8bfb5db806..6eb1c86723 100644
--- a/lapce-app/src/window_tab.rs
+++ b/lapce-app/src/window_tab.rs
@@ -1013,6 +1013,9 @@ impl WindowTabData {
             ChangeFileLanguage => {
                 self.palette.run(PaletteKind::Language);
             }
+            ChangeFileLineEnding => {
+                self.palette.run(PaletteKind::LineEnding);
+            }
             DiffFiles => self.palette.run(PaletteKind::DiffFiles),
 
             // ==== Running / Debugging ====


Test patch: