forked from mars/breed
Live theme reload
This commit is contained in:
parent
b7f9c8ba2e
commit
7d46cbcf9b
|
@ -76,8 +76,10 @@ name = "breed"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arg",
|
"arg",
|
||||||
|
"crossbeam-channel",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"libc",
|
"libc",
|
||||||
|
"notify",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"ropey",
|
"ropey",
|
||||||
|
@ -127,6 +129,25 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-channel"
|
||||||
|
version = "0.5.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossterm"
|
name = "crossterm"
|
||||||
version = "0.26.1"
|
version = "0.26.1"
|
||||||
|
@ -158,6 +179,18 @@ version = "1.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "filetime"
|
||||||
|
version = "0.2.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.0.25"
|
version = "1.0.25"
|
||||||
|
@ -174,6 +207,15 @@ version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fsevent-sys"
|
||||||
|
version = "4.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glob"
|
name = "glob"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
@ -196,12 +238,52 @@ dependencies = [
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inotify"
|
||||||
|
version = "0.9.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"inotify-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inotify-sys"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
|
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kqueue"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2c8fc60ba15bf51257aa9807a48a61013db043fcf3a78cb0d916e8e396dcad98"
|
||||||
|
dependencies = [
|
||||||
|
"kqueue-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kqueue-sys"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8367585489f01bc55dd27404dcf56b95e6da061a256a666ab23be9ba96a2e587"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
|
@ -279,7 +361,7 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"wasi",
|
"wasi",
|
||||||
"windows-sys",
|
"windows-sys 0.45.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -292,6 +374,24 @@ dependencies = [
|
||||||
"minimal-lexical",
|
"minimal-lexical",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "notify"
|
||||||
|
version = "5.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "58ea850aa68a06e48fdb069c0ec44d0d64c8dbffa49bf3b6f7f0a901fdea1ba9"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"crossbeam-channel",
|
||||||
|
"filetime",
|
||||||
|
"fsevent-sys",
|
||||||
|
"inotify",
|
||||||
|
"kqueue",
|
||||||
|
"libc",
|
||||||
|
"mio",
|
||||||
|
"walkdir",
|
||||||
|
"windows-sys 0.42.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.17.1"
|
version = "1.17.1"
|
||||||
|
@ -340,7 +440,7 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"windows-sys",
|
"windows-sys 0.45.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -695,13 +795,37 @@ version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm 0.42.2",
|
||||||
|
"windows_aarch64_msvc 0.42.2",
|
||||||
|
"windows_i686_gnu 0.42.2",
|
||||||
|
"windows_i686_msvc 0.42.2",
|
||||||
|
"windows_x86_64_gnu 0.42.2",
|
||||||
|
"windows_x86_64_gnullvm 0.42.2",
|
||||||
|
"windows_x86_64_msvc 0.42.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.45.0"
|
version = "0.45.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-targets",
|
"windows-targets 0.42.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -710,13 +834,28 @@ version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows_aarch64_gnullvm",
|
"windows_aarch64_gnullvm 0.42.2",
|
||||||
"windows_aarch64_msvc",
|
"windows_aarch64_msvc 0.42.2",
|
||||||
"windows_i686_gnu",
|
"windows_i686_gnu 0.42.2",
|
||||||
"windows_i686_msvc",
|
"windows_i686_msvc 0.42.2",
|
||||||
"windows_x86_64_gnu",
|
"windows_x86_64_gnu 0.42.2",
|
||||||
"windows_x86_64_gnullvm",
|
"windows_x86_64_gnullvm 0.42.2",
|
||||||
"windows_x86_64_msvc",
|
"windows_x86_64_msvc 0.42.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm 0.48.0",
|
||||||
|
"windows_aarch64_msvc 0.48.0",
|
||||||
|
"windows_i686_gnu 0.48.0",
|
||||||
|
"windows_i686_msvc 0.48.0",
|
||||||
|
"windows_x86_64_gnu 0.48.0",
|
||||||
|
"windows_x86_64_gnullvm 0.48.0",
|
||||||
|
"windows_x86_64_msvc 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -725,42 +864,84 @@ version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.42.2"
|
version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.42.2"
|
version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.42.2"
|
version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.42.2"
|
version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.42.2"
|
version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.42.2"
|
version = "0.42.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
|
|
|
@ -7,7 +7,9 @@ license = "AGPL-3.0-or-later"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
arg = "0.4.1"
|
arg = "0.4.1"
|
||||||
crossterm = "0.26"
|
crossterm = "0.26"
|
||||||
|
crossbeam-channel = "0.5"
|
||||||
libc = "0.2.141"
|
libc = "0.2.141"
|
||||||
|
notify = "5"
|
||||||
once_cell = "1.17"
|
once_cell = "1.17"
|
||||||
parking_lot = "0.12"
|
parking_lot = "0.12"
|
||||||
ropey = "1.6"
|
ropey = "1.6"
|
||||||
|
|
|
@ -36,6 +36,8 @@ pub struct Buffer {
|
||||||
text: Rope,
|
text: Rope,
|
||||||
syntax_set: Arc<SyntaxSet>,
|
syntax_set: Arc<SyntaxSet>,
|
||||||
parser: ParseState,
|
parser: ParseState,
|
||||||
|
style_dirty: bool,
|
||||||
|
style_generation: usize,
|
||||||
styled: Vec<Vec<(Style, Range<usize>)>>,
|
styled: Vec<Vec<(Style, Range<usize>)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +53,8 @@ impl Buffer {
|
||||||
text: Rope::from_str(text),
|
text: Rope::from_str(text),
|
||||||
syntax_set: Arc::new(SyntaxSet::load_defaults_newlines()),
|
syntax_set: Arc::new(SyntaxSet::load_defaults_newlines()),
|
||||||
parser,
|
parser,
|
||||||
|
style_dirty: true,
|
||||||
|
style_generation: 0,
|
||||||
styled: Vec::new(),
|
styled: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -60,13 +64,15 @@ impl Buffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(
|
pub fn draw(
|
||||||
&self,
|
&mut self,
|
||||||
styles: &mut StyleStore,
|
|
||||||
cols: u16,
|
cols: u16,
|
||||||
rows: u16,
|
rows: u16,
|
||||||
scroll: Cursor,
|
scroll: Cursor,
|
||||||
out: &mut (impl ExecutableCommand + Write),
|
out: &mut (impl ExecutableCommand + Write),
|
||||||
) -> crossterm::Result<u32> {
|
) -> crossterm::Result<u32> {
|
||||||
|
self.parse();
|
||||||
|
|
||||||
|
let mut styles = self.styles.lock();
|
||||||
let linenr_style = styles.get_scope("ui.linenr");
|
let linenr_style = styles.get_scope("ui.linenr");
|
||||||
let linenr_width = self.text.len_lines().ilog10() + 1;
|
let linenr_width = self.text.len_lines().ilog10() + 1;
|
||||||
let gutter_width = linenr_width + 1;
|
let gutter_width = linenr_width + 1;
|
||||||
|
@ -114,19 +120,23 @@ impl Buffer {
|
||||||
pub fn remove(&mut self, cursor: Cursor) {
|
pub fn remove(&mut self, cursor: Cursor) {
|
||||||
let index = self.cursor_to_char(cursor);
|
let index = self.cursor_to_char(cursor);
|
||||||
self.text.remove(index..=index);
|
self.text.remove(index..=index);
|
||||||
self.parse();
|
self.style_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_char(&mut self, cursor: Cursor, c: char) {
|
pub fn insert_char(&mut self, cursor: Cursor, c: char) {
|
||||||
let index = self.cursor_to_char(cursor);
|
let index = self.cursor_to_char(cursor);
|
||||||
self.text.insert_char(index, c);
|
self.text.insert_char(index, c);
|
||||||
self.parse();
|
self.style_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the whole file from scratch.
|
|
||||||
fn parse(&mut self) {
|
fn parse(&mut self) {
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
let mut styles = self.styles.lock();
|
let mut styles = self.styles.lock();
|
||||||
|
|
||||||
|
if styles.generation() == self.style_generation && !self.style_dirty {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
self.styled.clear();
|
self.styled.clear();
|
||||||
let mut parser = self.parser.clone();
|
let mut parser = self.parser.clone();
|
||||||
let mut line_buf = String::new();
|
let mut line_buf = String::new();
|
||||||
|
@ -163,6 +173,9 @@ impl Buffer {
|
||||||
|
|
||||||
self.styled.push(line);
|
self.styled.push(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.style_dirty = true;
|
||||||
|
self.style_generation = styles.generation();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clamped_cursor(&self, cursor: Cursor) -> Cursor {
|
pub fn clamped_cursor(&self, cursor: Cursor) -> Cursor {
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Marceline Cramer
|
||||||
|
* Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU Affero General Public License as published by the Free
|
||||||
|
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||||
|
* later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||||
|
* details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crossbeam_channel::{unbounded, Receiver, RecvError, Sender};
|
||||||
|
use notify::{Event, EventKind, RecommendedWatcher, Watcher};
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
|
use crate::theme::{StyleStore, Theme};
|
||||||
|
|
||||||
|
pub struct Config {
|
||||||
|
/// Whether or not to wrap lines when moving the cursor left and right.
|
||||||
|
pub move_linewrap: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
move_linewrap: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the configuration directory. Panics if unavailable.
|
||||||
|
pub fn get_config_dir() -> PathBuf {
|
||||||
|
std::env::var_os("HOME")
|
||||||
|
.map(PathBuf::try_from)
|
||||||
|
.expect("$HOME is unset")
|
||||||
|
.unwrap()
|
||||||
|
.join(".config/breed")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Watches a theme file and automatically reloads it into a [StyleStore].
|
||||||
|
pub struct ThemeWatcher {
|
||||||
|
styles: Arc<Mutex<StyleStore>>,
|
||||||
|
watcher: RecommendedWatcher,
|
||||||
|
current_path: PathBuf,
|
||||||
|
fs_rx: Receiver<notify::Result<Event>>,
|
||||||
|
command_rx: Receiver<PathBuf>,
|
||||||
|
quit: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ThemeWatcher {
|
||||||
|
pub fn spawn(styles: Arc<Mutex<StyleStore>>) -> Sender<PathBuf> {
|
||||||
|
let themes_dir = get_config_dir().join("themes");
|
||||||
|
let default_path = themes_dir.join("default.toml");
|
||||||
|
let (fs_tx, fs_rx) = unbounded();
|
||||||
|
let (command_tx, command_rx) = unbounded();
|
||||||
|
let watcher = notify::recommended_watcher(fs_tx).unwrap();
|
||||||
|
let mut watcher = Self {
|
||||||
|
styles,
|
||||||
|
current_path: PathBuf::new(),
|
||||||
|
watcher,
|
||||||
|
fs_rx,
|
||||||
|
command_rx,
|
||||||
|
quit: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
watcher.watch_theme(&default_path).unwrap();
|
||||||
|
|
||||||
|
std::thread::spawn(move || watcher.run());
|
||||||
|
|
||||||
|
command_tx
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_theme(&mut self, path: &Path) -> Result<(), String> {
|
||||||
|
let name = path.file_stem().unwrap_or_default().to_string_lossy();
|
||||||
|
let contents = std::fs::read_to_string(path)
|
||||||
|
.map_err(|err| format!("Failed to read theme file: {:?}", err))?;
|
||||||
|
let value =
|
||||||
|
toml::from_str(&contents).map_err(|err| format!("Failed to parse theme: {:?}", err))?;
|
||||||
|
let theme = Theme::from_value(&name, value)?;
|
||||||
|
self.styles.lock().apply(theme);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn watch_theme(&mut self, path: &Path) -> notify::Result<()> {
|
||||||
|
let old = self.current_path.clone();
|
||||||
|
let mode = notify::RecursiveMode::NonRecursive;
|
||||||
|
self.watcher.watch(&path, mode)?;
|
||||||
|
self.current_path = path.to_path_buf();
|
||||||
|
|
||||||
|
if old != PathBuf::new() {
|
||||||
|
self.watcher.unwatch(&old)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.load_theme(path).map_err(|err| notify::Error {
|
||||||
|
kind: notify::ErrorKind::Generic(err),
|
||||||
|
paths: vec![path.to_owned()],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_fs(&mut self, event: Result<notify::Result<Event>, crossbeam_channel::RecvError>) {
|
||||||
|
match event {
|
||||||
|
Err(RecvError) => self.quit = true,
|
||||||
|
Ok(Ok(event)) => match event.kind {
|
||||||
|
EventKind::Modify(_) => {
|
||||||
|
for path in event.paths {
|
||||||
|
self.load_theme(&path).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
_ => {} // TODO display errors?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_command(&mut self, path: Result<PathBuf, RecvError>) {
|
||||||
|
match path {
|
||||||
|
Err(RecvError) => self.quit = true,
|
||||||
|
Ok(path) => {
|
||||||
|
self.watch_theme(&path).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(mut self) {
|
||||||
|
while !self.quit {
|
||||||
|
crossbeam_channel::select! {
|
||||||
|
recv(self.fs_rx) -> event => self.on_fs(event),
|
||||||
|
recv(self.command_rx) -> path => self.on_command(path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
src/main.rs
22
src/main.rs
|
@ -22,12 +22,14 @@ use std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{stdout, Read, Stdout, Write},
|
io::{stdout, Read, Stdout, Write},
|
||||||
os::fd::FromRawFd,
|
os::fd::FromRawFd,
|
||||||
|
path::PathBuf,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crossbeam_channel::Sender;
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
cursor,
|
cursor,
|
||||||
event::{read, Event, KeyCode, KeyEvent},
|
event::{Event, KeyCode, KeyEvent},
|
||||||
terminal, ExecutableCommand, Result,
|
terminal, ExecutableCommand, Result,
|
||||||
};
|
};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
@ -35,6 +37,7 @@ use yacexits::{exit, EX_DATAERR, EX_UNAVAILABLE};
|
||||||
|
|
||||||
mod actions;
|
mod actions;
|
||||||
mod buffer;
|
mod buffer;
|
||||||
|
mod config;
|
||||||
mod theme;
|
mod theme;
|
||||||
|
|
||||||
use buffer::Buffer;
|
use buffer::Buffer;
|
||||||
|
@ -104,6 +107,7 @@ pub struct State {
|
||||||
pub size: (usize, usize),
|
pub size: (usize, usize),
|
||||||
pub mode: Mode,
|
pub mode: Mode,
|
||||||
pub quit: bool,
|
pub quit: bool,
|
||||||
|
pub theme_tx: Sender<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
|
@ -111,6 +115,7 @@ impl State {
|
||||||
let styles = Arc::new(Mutex::new(StyleStore::default()));
|
let styles = Arc::new(Mutex::new(StyleStore::default()));
|
||||||
let buffer = Buffer::from_str(styles.clone(), text);
|
let buffer = Buffer::from_str(styles.clone(), text);
|
||||||
let (cols, rows) = terminal::size()?;
|
let (cols, rows) = terminal::size()?;
|
||||||
|
let theme_tx = config::ThemeWatcher::spawn(styles.clone());
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
styles,
|
styles,
|
||||||
|
@ -120,6 +125,7 @@ impl State {
|
||||||
size: (cols as usize, rows as usize),
|
size: (cols as usize, rows as usize),
|
||||||
mode: Mode::default(),
|
mode: Mode::default(),
|
||||||
quit: false,
|
quit: false,
|
||||||
|
theme_tx,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,11 +158,12 @@ impl State {
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// done with styles
|
||||||
|
drop(styles);
|
||||||
|
|
||||||
// draw buffer
|
// draw buffer
|
||||||
let buffer_rows = if show_status_bar { rows - 1 } else { rows };
|
let buffer_rows = if show_status_bar { rows - 1 } else { rows };
|
||||||
let lr_width = self
|
let lr_width = self.buffer.draw(cols, buffer_rows, self.scroll, out)?;
|
||||||
.buffer
|
|
||||||
.draw(&mut styles, cols, buffer_rows, self.scroll, out)?;
|
|
||||||
|
|
||||||
// draw cursor
|
// draw cursor
|
||||||
let cursor_pos = set_cursor_pos.unwrap_or_else(|| {
|
let cursor_pos = set_cursor_pos.unwrap_or_else(|| {
|
||||||
|
@ -341,10 +348,13 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn screen_main(stdout: &mut Stdout, mut state: State) -> Result<()> {
|
fn screen_main(stdout: &mut Stdout, mut state: State) -> Result<()> {
|
||||||
|
let poll_timeout = std::time::Duration::from_millis(100);
|
||||||
while !state.quit {
|
while !state.quit {
|
||||||
state.draw(stdout)?;
|
state.draw(stdout)?;
|
||||||
let event = read()?;
|
if let true = crossterm::event::poll(poll_timeout)? {
|
||||||
state.on_event(event);
|
let event = crossterm::event::read()?;
|
||||||
|
state.on_event(event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
15
src/theme.rs
15
src/theme.rs
|
@ -24,13 +24,7 @@ use crossterm::{style::Color, ExecutableCommand};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use toml::{map::Map, Value};
|
use toml::{map::Map, Value};
|
||||||
|
|
||||||
pub static DEFAULT_THEME: Lazy<Theme> = Lazy::new(|| {
|
#[derive(Debug, Default)]
|
||||||
let text = include_str!("../default_theme.toml");
|
|
||||||
let value = toml::from_str(text).expect("Failed to parse default theme");
|
|
||||||
Theme::from_value("default", value).expect("Failed to load default theme")
|
|
||||||
});
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct StyleStore {
|
pub struct StyleStore {
|
||||||
/// The current theme.
|
/// The current theme.
|
||||||
theme: Theme,
|
theme: Theme,
|
||||||
|
@ -45,13 +39,6 @@ pub struct StyleStore {
|
||||||
styles: Vec<Style>,
|
styles: Vec<Style>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for StyleStore {
|
|
||||||
fn default() -> Self {
|
|
||||||
let theme = Lazy::force(&DEFAULT_THEME).to_owned();
|
|
||||||
Self::new(theme)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StyleStore {
|
impl StyleStore {
|
||||||
/// Creates a new style store with a given theme.
|
/// Creates a new style store with a given theme.
|
||||||
pub fn new(theme: Theme) -> Self {
|
pub fn new(theme: Theme) -> Self {
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
|
|
||||||
"constructor" = "foam"
|
"constructor" = "foam"
|
||||||
|
|
||||||
|
|
||||||
"comment" = { fg = "muted", modifiers = ["italic"]}
|
"comment" = { fg = "muted", modifiers = ["italic"]}
|
||||||
"constant" = "foam"
|
"constant" = "foam"
|
||||||
"constant.builtin" = "rose"
|
"constant.builtin" = "rose"
|
Reference in New Issue