[Feat] finished mvp version

This commit is contained in:
Mist 2024-02-19 22:27:17 +08:00
parent 4b3581230d
commit a088d8b580
41 changed files with 911 additions and 734 deletions

View file

@ -1,11 +0,0 @@
[target.x86_64-apple-darwin]
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]
[target.aarch64-apple-darwin]
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]

15
.gitignore vendored
View file

@ -3,8 +3,17 @@
/lua_modules
/.luarocks
# OS
.DS_Store
# Cargo
target
# Nodejs
node_modules
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
output.css

660
Cargo.lock generated
View file

@ -1,660 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bindgen"
version = "0.68.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078"
dependencies = [
"bitflags",
"cexpr",
"clang-sys",
"lazy_static",
"lazycell",
"peeking_take_while",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"syn",
]
[[package]]
name = "bitflags"
version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
[[package]]
name = "bstr"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc"
dependencies = [
"memchr",
"serde",
]
[[package]]
name = "cc"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"libc",
]
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clang-sys"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]]
name = "either"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
[[package]]
name = "errno"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "glob"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "home"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "itoa"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "libloading"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161"
dependencies = [
"cfg-if",
"windows-sys 0.48.0",
]
[[package]]
name = "libuv-sys2"
version = "1.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6125e1a220a5698a154ce76762d2ef8884baf9f77da7ceb8a3bd8c5ce27df343"
dependencies = [
"bindgen",
"cc",
"pkg-config",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
[[package]]
name = "lua-src"
version = "546.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2da0daa7eee611a4c30c8f5ee31af55266e26e573971ba9336d2993e2da129b2"
dependencies = [
"cc",
]
[[package]]
name = "luajit-src"
version = "210.5.6+9cc2e42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b365d859c9ffc187f48bb3e25ec80c3b40cf3f68f53544f4adeaee70554157"
dependencies = [
"cc",
"which",
]
[[package]]
name = "memchr"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]]
name = "mini-internal"
version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51c55587ac25c2d63a75e4171221b2803a9b9e83aeb7bdfde5833c4cd578b50d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniserde"
version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8621df4a46e5de4c1541242407da38281ea0e320b94e238477688a36a1a059f7"
dependencies = [
"itoa",
"mini-internal",
"ryu",
]
[[package]]
name = "mlua"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d3561f79659ff3afad7b25e2bf2ec21507fe601ebecb7f81088669ec4bfd51e"
dependencies = [
"bstr",
"mlua-sys",
"num-traits",
"once_cell",
"rustc-hash",
]
[[package]]
name = "mlua-sys"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2847b42764435201d8cbee1f517edb79c4cca4181877b90047587c89e1b7bce4"
dependencies = [
"cc",
"cfg-if",
"lua-src",
"luajit-src",
"pkg-config",
]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "num-traits"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
dependencies = [
"autocfg",
]
[[package]]
name = "nvim-oxi"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c9aef30b9ac1033e2d176b76f5eabab48d710a25cf0aa01068b5224a179d88"
dependencies = [
"miniserde",
"mlua",
"oxi-api",
"oxi-libuv",
"oxi-luajit",
"oxi-macros",
"oxi-types",
"thiserror",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "oxi-api"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e969161dafa13429fe816dfcbdd6cb138c3ed872e263f2a7cab3c41acd8cf174"
dependencies = [
"oxi-luajit",
"oxi-macros",
"oxi-types",
"serde",
"serde_repr",
"thiserror",
]
[[package]]
name = "oxi-libuv"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da0c7850edec97513b9f4d77ce9b2f3c80eb60b259c8035e334f3f290785b76"
dependencies = [
"libuv-sys2",
"once_cell",
"oxi-luajit",
"thiserror",
]
[[package]]
name = "oxi-luajit"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a644c4c3c35816a6eb174570fc54a968c6047b8b845c1f253c0184fd8aeb25ab"
dependencies = [
"once_cell",
"thiserror",
]
[[package]]
name = "oxi-macros"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae7527f018ccf611bf798ecae060c199ba921998125c836c7f236c2425ba0d89"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "oxi-types"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05813abd7733b9ef9618811d66627ce80db6b8321140613872989c6aae5c25db"
dependencies = [
"libc",
"oxi-luajit",
"serde",
"thiserror",
]
[[package]]
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "pkg-config"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
[[package]]
name = "proc-macro2"
version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustix"
version = "0.38.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.52.0",
]
[[package]]
name = "ryu"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
[[package]]
name = "serde"
version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_repr"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "snap"
version = "0.1.0"
dependencies = [
"nvim-oxi",
]
[[package]]
name = "syn"
version = "2.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "which"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fa5e0c10bf77f44aac573e498d1a82d5fbd5e91f6fc0a99e7be4b38e85e101c"
dependencies = [
"either",
"home",
"once_cell",
"rustix",
"windows-sys 0.52.0",
]
[[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.5",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.0",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
name = "windows-targets"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
dependencies = [
"windows_aarch64_gnullvm 0.52.0",
"windows_aarch64_msvc 0.52.0",
"windows_i686_gnu 0.52.0",
"windows_i686_msvc 0.52.0",
"windows_x86_64_gnu 0.52.0",
"windows_x86_64_gnullvm 0.52.0",
"windows_x86_64_msvc 0.52.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"

View file

@ -1,10 +0,0 @@
[package]
name = "snap"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
nvim-oxi = {version = "0.4.2", features = ["neovim-nightly", "libuv", "mlua", "test", "__vendored_luajit"]}

59
lua/codesnap/client.lua Normal file
View file

@ -0,0 +1,59 @@
local logger = require("codesnap.utils.logger")
local path_utils = require("codesnap.utils.path")
local client = {
job_id = 0,
}
function client:connect()
return vim.fn.jobstart({
path_utils.back(path_utils.back(debug.getinfo(1, "S").source:sub(2):match("(.*[/\\])")))
.. "/snap-server/target/debug/snap-server",
}, {
stderr_buffered = true,
rpc = true,
on_stderr = function(_, err)
vim.fn.jobstop(self.job_id)
logger.error(err)
end,
on_exit = function()
vim.fn.chanclose(self.job_id)
self.job_id = 0
end,
})
end
function client:init()
return self.job_id == 0 and client:connect() or self.job_id
end
function client:start()
self.job_id = client:init()
if self.job_id == 0 then
logger.error("cannot start rpc process")
return
end
if self.job_id == -1 then
logger.error("rpc process is not executable")
vim.fn.jobstop(self.job_id)
return
end
return self
end
function client:send(event, message)
vim.fn.rpcnotify(self.job_id, event, message)
end
function client:stop()
if self.job_id == 0 or self.job_id == -1 then
return
end
vim.fn.jobstop(self.job_id)
end
return client

View file

@ -1,10 +1,26 @@
local table_utils = require("utils.table")
local table_utils = require("codesnap.utils.table")
local static = require("codesnap.static")
local client = require("codesnap.client")
local visual_utils = require("codesnap.utils.visual")
local main = {}
function main.setup(config)
static.config = table_utils.merge(static.config, config)
static.config = table_utils.merge(static.config, config == nil and {} or config)
print(vim.inspect(static.config))
print(table_utils.serialize_json(static.config))
print()
if static.config.auto_load then
client:start()
end
client:send("config_setup", static.config)
end
function main.preview_code()
client:send("preview_code", { content = visual_utils.get_selected_text(), language = vim.bo.filetype })
end
return main

View file

@ -1,13 +1,11 @@
return {
config = {
breadcrumbs = false,
breadcrumbs = true,
column_number = true,
mac_window_bar = true,
background = {
color = "#ff0000",
grandient = true,
},
opacity = true,
watermark = "CodeSnap.nvim",
auto_load = true,
},
preview_switch = false,
preview_switch = true,
}

View file

@ -20,4 +20,14 @@ function list_utils.includes(list, value)
end) ~= nil
end
function list_utils.map(list, fn)
local result = {}
for i, value in ipairs(list) do
table.insert(result, fn(value, i))
end
return result
end
return list_utils

View file

@ -0,0 +1,11 @@
local logger = {}
function logger.log(level, message)
vim.api.nvim_notify("[" .. level .. "] CodeSnap: " .. tostring(vim.inspect(message)), vim.log.levels[level], {})
end
function logger.error(message)
logger.log("ERROR", message)
end
return logger

View file

@ -0,0 +1,9 @@
local path_utils = {}
function path_utils.back(path)
local parsed_path, _ = path:gsub("/[^\\/]+/?$", "")
return parsed_path
end
return path_utils

View file

@ -1,3 +1,4 @@
local list_utils = require("codesnap.utils.list")
local table_utils = {}
function table_utils.assign(t, props)
@ -14,4 +15,56 @@ function table_utils.merge(t1, t2)
return t1
end
function table_utils.is_array(t)
return type(t[1]) == "number"
end
function table_utils.typeof(value)
if type(value) == "table" then
if table_utils.is_array(value) then
return "array"
else
return "table"
end
end
return type(value)
end
function table_utils.serialize_array(t)
local result = list_utils.map(t, function(ele)
table_utils.serialize_json(ele)
end)
return "[" .. result.concat(t, ",") .. "]"
end
function table_utils.serialize_table(t)
local result = {}
for key, value in pairs(t) do
table.insert(result, string.format('"%s":%s', key, table_utils.serialize_json(value)))
end
return "{" .. table.concat(result, ",") .. "}"
end
function table_utils.serialize_string(value)
return '"' .. value .. '"'
end
function table_utils.serialize_json(t)
local complex_type_parser = {
array = table_utils.serialize_array,
table = table_utils.serialize_table,
string = table_utils.serialize_string,
}
local parse = complex_type_parser[table_utils.typeof(t)] or function(v)
return v
end
return parse(t)
end
return table_utils

View file

@ -0,0 +1,30 @@
local visual_utils = {}
function visual_utils.get_selected_text()
local start_pos = vim.fn.getpos("v")
local end_pos = vim.fn.getpos(".")
if start_pos[2] == end_pos[2] then
return vim.api.nvim_buf_get_lines(0, start_pos[2] - 1, start_pos[2], false)[1]:sub(start_pos[3], end_pos[3] - 1)
else
-- 如果选中的是多行文本,则需要分别获取每一行的文本
local selected_text = {}
for i = start_pos[2], end_pos[2] do
-- 使用 vim.api.nvim_buf_get_lines() 函数获取选中的文本
local line_text = vim.api.nvim_buf_get_lines(0, i - 1, i, false)[1]
-- 如果是选中的第一行,需要从 mark 'v' 的列开始获取
if i == start_pos[2] then
line_text = line_text:sub(start_pos[3])
end
-- 如果是选中的最后一行,需要获取到当前光标的列
if i == end_pos[2] then
line_text = line_text:sub(1, end_pos[3] - 1)
end
table.insert(selected_text, line_text)
end
-- 输出当前选中的文本
return table.concat(selected_text, "\n")
end
end
return visual_utils

View file

@ -1,5 +1,29 @@
local codesnap = require("codesnap")
local static = require("codesnap.static")
local client = require("codesnap.client")
vim.api.nvim_create_user_command("CodeSnap", function()
codesnap.setup()
end, {})
-- snap code
vim.api.nvim_create_user_command("CodeSnap", function() end, {})
vim.api.nvim_create_user_command("CodeSnapPreviewOn", function() end, {})
vim.api.nvim_create_user_command("CodeSnapPreviewOff", function() end, {})
vim.api.nvim_create_autocmd({ "CursorMoved" }, {
callback = function()
local mode = vim.api.nvim_get_mode().mode
if mode ~= "v" or not static.preview_switch then
return
end
codesnap.preview_code()
end,
})
vim.api.nvim_create_autocmd({ "VimLeavePre" }, {
pattern = "*",
callback = function()
client:stop()
end,
})

View file

@ -8,6 +8,7 @@
"name": "my-app",
"version": "0.1.0",
"dependencies": {
"@tailwindcss/typography": "^0.5.10",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
@ -15,14 +16,17 @@
"@types/node": "^16.18.81",
"@types/react": "^18.2.55",
"@types/react-dom": "^18.2.19",
"highlight.js": "^11.9.0",
"html-to-image": "^1.11.11",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"react-use-websocket": "^3.0.0",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
},
"devDependencies": {
"react-use-websocket": "^3.0.0",
"daisyui": "^3.9.4",
"tailwindcss": "^3.4.1"
}
},
@ -3660,6 +3664,32 @@
"url": "https://github.com/sponsors/gregberge"
}
},
"node_modules/@tailwindcss/typography": {
"version": "0.5.10",
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.10.tgz",
"integrity": "sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw==",
"dependencies": {
"lodash.castarray": "^4.4.0",
"lodash.isplainobject": "^4.0.6",
"lodash.merge": "^4.6.2",
"postcss-selector-parser": "6.0.10"
},
"peerDependencies": {
"tailwindcss": ">=3.0.0 || insiders"
}
},
"node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": {
"version": "6.0.10",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
"integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
},
"engines": {
"node": ">=4"
}
},
"node_modules/@testing-library/dom": {
"version": "9.3.4",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz",
@ -6362,6 +6392,16 @@
"resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz",
"integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w=="
},
"node_modules/css-selector-tokenizer": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz",
"integrity": "sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==",
"dev": true,
"dependencies": {
"cssesc": "^3.0.0",
"fastparse": "^1.1.2"
}
},
"node_modules/css-tree": {
"version": "1.0.0-alpha.37",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz",
@ -6559,6 +6599,26 @@
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
"node_modules/daisyui": {
"version": "3.9.4",
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.9.4.tgz",
"integrity": "sha512-fvi2RGH4YV617/6DntOVGcOugOPym9jTGWW2XySb5ZpvdWO4L7bEG77VHirrnbRUEWvIEVXkBpxUz2KFj0rVnA==",
"dev": true,
"dependencies": {
"colord": "^2.9",
"css-selector-tokenizer": "^0.8",
"postcss": "^8",
"postcss-js": "^4",
"tailwindcss": "^3.1"
},
"engines": {
"node": ">=16.9.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/daisyui"
}
},
"node_modules/damerau-levenshtein": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
@ -8065,6 +8125,12 @@
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="
},
"node_modules/fastparse": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz",
"integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==",
"dev": true
},
"node_modules/fastq": {
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
@ -8878,6 +8944,14 @@
"he": "bin/he"
}
},
"node_modules/highlight.js": {
"version": "11.9.0",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.9.0.tgz",
"integrity": "sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==",
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/hoopy": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz",
@ -8980,6 +9054,11 @@
"node": ">=12"
}
},
"node_modules/html-to-image": {
"version": "1.11.11",
"resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.11.11.tgz",
"integrity": "sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA=="
},
"node_modules/html-webpack-plugin": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.0.tgz",
@ -12108,11 +12187,21 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash.castarray": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
"integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q=="
},
"node_modules/lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
},
"node_modules/lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
},
"node_modules/lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@ -14817,7 +14906,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/react-use-websocket/-/react-use-websocket-3.0.0.tgz",
"integrity": "sha512-BInlbhXYrODBPKIplDAmI0J1VPM+1KhCLN09o+dzgQ8qMyrYs4t5kEYmCrTqyRuMTmpahylHFZWQXpfYyDkqOw==",
"dev": true,
"peerDependencies": {
"react": ">= 16.8.0",
"react-dom": ">= 16.8.0"
@ -20429,6 +20517,28 @@
"loader-utils": "^2.0.0"
}
},
"@tailwindcss/typography": {
"version": "0.5.10",
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.10.tgz",
"integrity": "sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw==",
"requires": {
"lodash.castarray": "^4.4.0",
"lodash.isplainobject": "^4.0.6",
"lodash.merge": "^4.6.2",
"postcss-selector-parser": "6.0.10"
},
"dependencies": {
"postcss-selector-parser": {
"version": "6.0.10",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
"integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
"requires": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
}
}
}
},
"@testing-library/dom": {
"version": "9.3.4",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz",
@ -22460,6 +22570,16 @@
"resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz",
"integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w=="
},
"css-selector-tokenizer": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz",
"integrity": "sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==",
"dev": true,
"requires": {
"cssesc": "^3.0.0",
"fastparse": "^1.1.2"
}
},
"css-tree": {
"version": "1.0.0-alpha.37",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz",
@ -22602,6 +22722,19 @@
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
"daisyui": {
"version": "3.9.4",
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.9.4.tgz",
"integrity": "sha512-fvi2RGH4YV617/6DntOVGcOugOPym9jTGWW2XySb5ZpvdWO4L7bEG77VHirrnbRUEWvIEVXkBpxUz2KFj0rVnA==",
"dev": true,
"requires": {
"colord": "^2.9",
"css-selector-tokenizer": "^0.8",
"postcss": "^8",
"postcss-js": "^4",
"tailwindcss": "^3.1"
}
},
"damerau-levenshtein": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
@ -23723,6 +23856,12 @@
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="
},
"fastparse": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz",
"integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==",
"dev": true
},
"fastq": {
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
@ -24282,6 +24421,11 @@
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
},
"highlight.js": {
"version": "11.9.0",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.9.0.tgz",
"integrity": "sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw=="
},
"hoopy": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz",
@ -24364,6 +24508,11 @@
"terser": "^5.10.0"
}
},
"html-to-image": {
"version": "1.11.11",
"resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.11.11.tgz",
"integrity": "sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA=="
},
"html-webpack-plugin": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.0.tgz",
@ -26594,11 +26743,21 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"lodash.castarray": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
"integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q=="
},
"lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
},
"lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
},
"lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@ -28373,7 +28532,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/react-use-websocket/-/react-use-websocket-3.0.0.tgz",
"integrity": "sha512-BInlbhXYrODBPKIplDAmI0J1VPM+1KhCLN09o+dzgQ8qMyrYs4t5kEYmCrTqyRuMTmpahylHFZWQXpfYyDkqOw==",
"dev": true,
"requires": {}
},
"read-cache": {

View file

@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@tailwindcss/typography": "^0.5.10",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
@ -10,12 +11,14 @@
"@types/node": "^16.18.81",
"@types/react": "^18.2.55",
"@types/react-dom": "^18.2.19",
"highlight.js": "^11.9.0",
"html-to-image": "^1.11.11",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"react-use-websocket": "^3.0.0",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4",
"react-use-websocket": "^3.0.0"
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
@ -44,6 +47,7 @@
]
},
"devDependencies": {
"daisyui": "^3.9.4",
"tailwindcss": "^3.4.1"
}
}

View file

@ -3,6 +3,10 @@
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin="anonymous">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous">
<link href="https://fonts.googleapis.com/css2?family=Pacifico&display=swap" rel="stylesheet" crossorigin="anonymous">
<link rel="stylesheet" href="https://mshaugh.github.io/nerdfont-webfonts/build/caskaydiacove-nerd-font.css" crossorigin="anonymous">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta

View file

@ -1,23 +1,60 @@
import React, { useCallback, useEffect, useState } from "react";
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import useWebSocket, { ReadyState } from "react-use-websocket";
import { ControlBar, Editor, Frame, Panel } from "./components";
import { useConfig, useEvent } from "./hooks";
import { toPng, toJpeg, toBlob, toPixelData, toSvg } from "html-to-image";
const CODE_EMPTY_PLACEHOLDER = `
`;
function App() {
const [socketUrl, setSocketUrl] = useState("ws://127.0.0.1:8080/ws");
const [messageHistory, setMessageHistory] = useState([]);
const { sendMessage, lastMessage, readyState } = useWebSocket(socketUrl);
const [socketUrl] = useState("ws://127.0.0.1:8080/ws");
const { sendMessage, lastMessage } = useWebSocket(socketUrl);
const event = useEvent(lastMessage);
const config = useConfig(event?.config_setup);
const frameRef = useRef<HTMLDivElement | null>(null);
const handleClickSendMessage = useCallback(() => {
sendMessage("Hello");
const handleCopyButtonClick = useCallback(async () => {
if (!frameRef.current) {
return;
}
const blob = await toBlob(frameRef.current);
const clipboardItem = new ClipboardItem({ "image/png": blob! });
navigator.clipboard.write([clipboardItem]);
}, []);
console.info(lastMessage);
return (
<div className="App">
<span className="text-3xl font-bold underline">
{lastMessage?.data ?? ""}
</span>
<button onClick={handleClickSendMessage}>Send</button>
<div className="w-full h-full flex flex-col items-center bg-deep-gray">
<p className="rainbow-text text-4xl font-extrabold mt-20">
CodeSnap.nvim
</p>
<Panel>
<ControlBar onCopyClick={handleCopyButtonClick}></ControlBar>
<div className="rounded-xl overflow-hidden">
<Frame ref={frameRef} watermark={config?.watermark}>
<Editor
language={event?.code?.language}
macStyleTitleBar={config?.mac_window_bar}
>
{event?.code?.content ?? CODE_EMPTY_PLACEHOLDER}
</Editor>
</Frame>
</div>
</Panel>
</div>
);
}

View file

@ -1 +0,0 @@
export const ControlBar = {};

View file

@ -0,0 +1,3 @@
export const ColorPicker = () => {
return <div className="btn flex-grow mr-1"></div>;
};

View file

@ -0,0 +1,53 @@
import { ColorPicker } from "./color-picker";
interface ControlBarProps {
onCopyClick(): void;
}
export const ControlBar = ({ onCopyClick }: ControlBarProps) => {
return (
<div
className="bg-neutral rounded-xl mb-2 p-1 flex flex-row items-center"
onClick={onCopyClick}
>
<ColorPicker></ColorPicker>
<div className="flex flex-row items-center">
<button className="btn mr-1">
<svg
role="img"
className="h-4 w-4 fill-neutral-500"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<title>X</title>
<path d="M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z" />
</svg>
</button>
<button className="btn mr-1">
Export
<svg
className="fill-neutral-content"
xmlns="http://www.w3.org/2000/svg"
height="24"
viewBox="0 -960 960 960"
width="24"
>
<path d="M480-320 280-520l56-58 104 104v-326h80v326l104-104 56 58-200 200ZM240-160q-33 0-56.5-23.5T160-240v-120h80v120h480v-120h80v120q0 33-23.5 56.5T720-160H240Z" />
</svg>
</button>
<button className="btn">
Copy
<svg
className="fill-neutral-content"
xmlns="http://www.w3.org/2000/svg"
height="20"
viewBox="0 -960 960 960"
width="20"
>
<path d="M360-240q-33 0-56.5-23.5T280-320v-480q0-33 23.5-56.5T360-880h360q33 0 56.5 23.5T800-800v480q0 33-23.5 56.5T720-240H360Zm0-80h360v-480H360v480ZM200-80q-33 0-56.5-23.5T120-160v-560h80v560h440v80H200Zm160-240v-480 480Z" />
</svg>{" "}
</button>
</div>
</div>
);
};

View file

@ -0,0 +1 @@
export * from "./control-bar";

View file

@ -0,0 +1,42 @@
import { MacStyleTitleBar } from "./mac-style-titlebar";
import hljs from "highlight.js";
export interface EditorProps {
macStyleTitleBar?: boolean;
language?: string;
opacity?: boolean;
children: string;
}
const highlightLanguage = (code: string, language?: string) => {
if (!language) {
return hljs.highlightAuto(code).value;
}
try {
return hljs.highlight(code, { language }).value;
} catch {
return hljs.highlightAuto(code).value;
}
};
export const Editor = ({
children,
language,
opacity = true,
macStyleTitleBar = true,
}: EditorProps) => (
<div
className={`editor-shadow ${opacity ? "bg-one-dark-base/[.93]" : "bg-one-dark-base"} rounded-2xl p-5 pb-7 border-border-color border-[1px]`}
>
{macStyleTitleBar && <MacStyleTitleBar />}
<pre className="mt-4">
<code
className="code"
dangerouslySetInnerHTML={{
__html: highlightLanguage(children, language),
}}
/>
</pre>
</div>
);

View file

@ -0,0 +1,18 @@
import { forwardRef, PropsWithChildren } from "react";
export interface FrameProps {
watermark?: string;
}
export const Frame = forwardRef<HTMLDivElement, PropsWithChildren<FrameProps>>(
({ children, watermark }, ref) => (
<div ref={ref} className="bg-stripe min-w-[800px] p-20">
{children}
{watermark && (
<p className="pacifico-regular text-xl opacity-50 font-bold text-white text-center w-full mt-14">
{watermark}
</p>
)}
</div>
),
);

View file

@ -0,0 +1,2 @@
export * from "./frame";
export * from "./editor";

View file

@ -0,0 +1,18 @@
interface ButtonProps {
color: string;
}
const Button = ({ color }: ButtonProps) => (
<div
className="w-4 h-4 mr-2 rounded-full"
style={{ backgroundColor: color }}
></div>
);
export const MacStyleTitleBar = () => (
<div className="flex flex-row">
<Button color="#FF5E57" />
<Button color="#FFBC2E" />
<Button color="#2BC841" />
</div>
);

View file

@ -1 +1,3 @@
export * from "./control-bar";
export * from "./panel";
export * from "./editor";

View file

@ -0,0 +1,7 @@
import { PropsWithChildren } from "react";
export interface PanelProps {}
export const Panel = ({ children }: PropsWithChildren<PanelProps>) => (
<div className="p-10 br-10">{children}</div>
);

View file

@ -0,0 +1,3 @@
export * from "./use-event";
export * from "./use-storage";
export * from "./use-config";

View file

@ -0,0 +1,18 @@
import { useLocalStorage } from "./use-storage";
export interface Config {
breadcrumbs: boolean;
column_number: boolean;
mac_window_bar: boolean;
opacity: boolean;
watermark: string;
auto_load: boolean;
}
const CONFIG_STORAGE_KEY = "CONFIG_STORAGE_KEY";
export const useConfig = (defaultConfig?: Config) => {
const [config] = useLocalStorage(CONFIG_STORAGE_KEY, defaultConfig);
return config;
};

View file

@ -0,0 +1,32 @@
import { useMemo } from "react";
import { Config } from "./use-config";
export enum EventType {
CONFIG_SETUP = "config_setup",
CODE = "code",
}
type CodeMessage = {
content: string;
language: string;
};
type ParsedConfig = {
[EventType.CODE]: CodeMessage;
[EventType.CONFIG_SETUP]: Config;
};
export const useEvent = (
event: MessageEvent<string> | null,
): Partial<ParsedConfig> | undefined =>
useMemo(() => {
if (!event) {
return undefined;
}
const parsedEvent = JSON.parse(event.data);
return {
[parsedEvent.name]: parsedEvent.data,
};
}, [event]);

View file

@ -0,0 +1,33 @@
import { useCallback, useMemo } from "react";
const createStorageHook =
(storage: Storage) =>
<T>(
key: string,
defaultData?: T,
): [NonNullable<T> | null, (value: T) => void] => {
const setValue = useCallback(
(value: T) => {
storage.setItem(key, JSON.stringify(value));
},
[key],
);
const value = useMemo(() => {
const value = storage.getItem(key);
if (defaultData) {
setValue(defaultData);
return defaultData;
}
return value ? JSON.parse(value) : value;
}, [key, defaultData, setValue]);
return [value, setValue];
};
export const useLocalStorage = createStorageHook(localStorage);
export const useSessionStorage = createStorageHook(sessionStorage);

View file

@ -1,6 +1,7 @@
import React from "react";
import ReactDOM from "react-dom/client";
import "./output.css";
import "highlight.js/styles/atom-one-dark.css";
import App from "./app";
import reportWebVitals from "./reportWebVitals";

View file

@ -2,3 +2,109 @@
@tailwind components;
@tailwind utilities;
html, body, #root {
height: 100%;
background-image:
linear-gradient(to right, rgba(36, 46, 54, 0.2) 2px, transparent 1px),
linear-gradient(to bottom, rgba(36, 46, 54, 0.2) 2px, transparent 1px);
background-size: 2.5rem 2.5rem;
background-position: center center;
}
@layer components {
.rainbow-text {
background-image: linear-gradient(90deg,hsl(var(--s)) 4%,color-mix(in oklch, hsl(var(--sf)), hsl(163.22deg 80% 43%)));
-webkit-text-fill-color: transparent;
background-clip: text;
}
.editor-shadow {
box-shadow: 0 20px 68px rgba(0, 0, 0, 0.55);
}
.code * {
font-family: 'CaskaydiaCove Nerd Font';
}
.pacifico-regular {
font-family: "Pacifico", cursive;
font-weight: 400;
font-style: normal;
}
.bg-stripe {
background: linear-gradient(to right, #1fa2ff, #12d8fa, #a6ffcb);
}
.bg-flare {
background: linear-gradient(to right, #f12711, #f5af19);
}
.bg-vanusa {
background: linear-gradient(to right, #da4453, #89216b);
}
.bg-sublime-light {
background: linear-gradient(to right, #fc5c7d, #6a82fb);
}
.bg-bighead {
background: linear-gradient(to right, #c94b4b, #4b134f);
}
.bg-velvet-sun {
background: linear-gradient(to right, #e1eec3, #f05053);
}
.bg-argon {
background: linear-gradient(to right, #03001e, #7303c0, #ec38bc, #fdeff9);
}
.bg-celestial {
background: linear-gradient(to right, #c33764, #1d2671);
}
.bg-relay {
background: linear-gradient(to right, #3a1c71, #d76d77, #ffaf7b);
}
.bg-crystal-clear {
background: linear-gradient(to right, #159957, #155799);
}
.bg-ibiza-sunset {
background: linear-gradient(to right, #ee0979, #ff6a00);
}
.bg-fresh-turboscent {
background: linear-gradient(to right, #f1f2b5, #135058);
}
.bg-cheer-up-emo-kid {
background: linear-gradient(to right, #556270, #ff6b6b);
}
.bg-starfall {
background: linear-gradient(to right, #f0c27b, #4b1248);
}
.bg-nelson {
background: linear-gradient(to right, #f2709c, #ff9472);
}
.bg-forever-lost {
background: linear-gradient(to right, #5d4157, #a8caba);
}
.bg-blurry-beach {
background: linear-gradient(to right, #d53369, #cbad6d);
}
.bg-influenza {
background: linear-gradient(to right, #c04848, #480048);
}
.bg-jshine {
background: linear-gradient(to right, #12c2e9, #c471ed, #f64f59);
}
.bg-calm-darya {
background: linear-gradient(to right, #5f2c82, #49a09d);
}
.bg-titanium {
background: linear-gradient(to right, #283048, #859398);
}
.bg-pinky {
background: linear-gradient(to right, #dd5e89, #f7bb97);
}
.bg-purple-paradise {
background: linear-gradient(to right, #1d2b64, #f8cdda);
}
.bg-horizon {
background: linear-gradient(to right, #003973, #e5e5be);
}
.bg-noon-to-dusk {
background: linear-gradient(to right, #ff6e7f, #bfe9ff);
}
}

View file

@ -2,8 +2,19 @@ import type { Config } from "tailwindcss";
export default {
content: ["./src/**/*.{js,jsx,ts,tsx}"],
theme: {
extend: {},
plugins: [require("daisyui"), require("@tailwindcss/typography")],
daisyui: {
themes: ["dark"],
},
theme: {
fontFamily: {
caskaydiacove: "CaskaydiaCove Nerd Font",
},
extend: {
colors: {
"one-dark-base": "#282C34",
"border-color": "#545F64",
},
},
},
plugins: [],
} satisfies Config;

View file

@ -13,8 +13,11 @@ impl<T> Event<T>
where
T: Serialize,
{
pub fn new(name: String, data: T) -> Event<T> {
Event { name, data }
pub fn new(name: &str, data: T) -> Event<T> {
Event {
name: name.to_string(),
data,
}
}
}

View file

@ -1,18 +1,23 @@
mod messages;
pub mod arguments;
pub mod config;
pub mod messages;
pub mod neovim;
use actix::{Actor, Addr, AsyncContext, Context};
use arguments::parse_string_first;
pub use config::Config;
pub use messages::Message;
use neovim::Neovim;
use serde_json::json;
use serde_json::{json, Value};
use std::{
any::Any,
collections::HashMap,
sync::{Arc, Mutex},
};
use crate::{
event::Event,
server::{ClientMessage, Server},
server::{ClientMessage, ConfigSetupMessage, Server},
};
pub struct EventHandler {
@ -37,22 +42,20 @@ impl EventHandler {
let receiver = self.neovim.lock().unwrap().create_receiver();
for (event_name, values) in receiver {
self.neovim.lock().unwrap().print(&event_name);
match Message::from(event_name.clone()) {
Message::PreviewCode => self.server.do_send(ClientMessage {
msg: Event::new(
"hello".to_string(),
serde_json::from_str::<String>(
values
.iter()
.map(|value| value.to_string())
.collect::<Vec<String>>()
.first()
.unwrap(),
)
"code",
serde_json::from_str::<Value>(parse_string_first(&values).as_str())
.unwrap(),
)
.into(),
}),
Message::ConfigSetup => self.server.do_send(ConfigSetupMessage {
msg: parse_string_first(&values),
}),
Message::Unknown => self
.neovim
.lock()

View file

@ -0,0 +1,9 @@
use neovim_lib::Value;
pub fn parse_string(values: &Vec<Value>) -> Vec<String> {
values.iter().map(|value| value.to_string()).collect()
}
pub fn parse_string_first(values: &Vec<Value>) -> String {
parse_string(values).first().unwrap().to_string()
}

View file

@ -0,0 +1,17 @@
use serde::{Deserialize, Serialize};
#[derive(Serialize, Debug, Deserialize, Clone)]
pub struct Config {
breadcrumbs: bool,
column_number: bool,
mac_window_bar: bool,
opacity: bool,
watermark: Option<String>,
auto_load: bool,
}
impl From<&str> for Config {
fn from(value: &str) -> Self {
return serde_json::from_str(value).unwrap();
}
}

View file

@ -1,6 +1,7 @@
#[derive(PartialEq, Hash)]
pub enum Message {
PreviewCode,
ConfigSetup,
Unknown,
}
@ -10,6 +11,7 @@ impl From<String> for Message {
fn from(value: String) -> Self {
match value.as_str() {
"preview_code" => Message::PreviewCode,
"config_setup" => Message::ConfigSetup,
_ => Message::Unknown,
}
}

View file

@ -11,7 +11,7 @@ use actix_web::{
App, Error, HttpRequest, HttpResponse, HttpServer,
};
use actix_web_actors::ws;
use event_handler::neovim::Neovim;
use event_handler::{neovim::Neovim, Config};
use event_handler::{EventHandler, Message};
use server::Server;
use session::Session;
@ -43,3 +43,21 @@ async fn main() -> std::io::Result<()> {
.run()
.await
}
// fn main() {
// let data = r#"
// {
// "breadcrumbs":true,
// "watermark":"CodeSnap.nvim",
// "mac_window_bar":true,
// "column_number":true,
// "auto_load":true,
// "background":{
// "grandient":true
// }
// }"#;
//
// let config: Config = serde_json::from_str(data).unwrap();
//
// println!("{:?}", config)
// }

View file

@ -1,11 +1,15 @@
use actix::{Actor, Context, Handler, Message, Recipient};
use rand::{rngs::ThreadRng, Rng};
use serde::Serialize;
use std::{
collections::HashMap,
sync::{Arc, Mutex},
};
use crate::event_handler::neovim::Neovim;
use crate::{
event::Event,
event_handler::{neovim::Neovim, Config},
};
type SessionID = usize;
@ -31,10 +35,17 @@ pub struct ClientMessage {
pub msg: String,
}
#[derive(Message)]
#[rtype(result = "()")]
pub struct ConfigSetupMessage {
pub msg: String,
}
pub struct Server {
rng: ThreadRng,
sessions: HashMap<SessionID, Recipient<ServerMessage>>,
neovim: Arc<Mutex<Neovim>>,
config: Option<Config>,
}
impl Server {
@ -43,6 +54,7 @@ impl Server {
rng: rand::thread_rng(),
sessions: HashMap::new(),
neovim,
config: None,
}
}
@ -51,6 +63,14 @@ impl Server {
session.do_send(ServerMessage(message.to_string()))
}
}
fn send_event_to_clients<T>(&self, event: Event<T>)
where
T: Serialize,
{
let stringify_event: String = event.into();
self.send_message_to_clients(stringify_event.as_str())
}
}
impl Actor for Server {
@ -65,6 +85,10 @@ impl Handler<Connect> for Server {
self.sessions.insert(id, msg.addr);
if let Some(config) = &self.config {
self.send_event_to_clients(Event::new("config_setup", config));
}
id
}
}
@ -84,3 +108,14 @@ impl Handler<ClientMessage> for Server {
self.send_message_to_clients(&msg.msg)
}
}
impl Handler<ConfigSetupMessage> for Server {
type Result = ();
fn handle(&mut self, msg: ConfigSetupMessage, _: &mut Self::Context) -> Self::Result {
let config = Config::from(msg.msg.as_str());
self.config = Some(config.clone());
self.send_event_to_clients(Event::new("config_setup", config));
}
}