fix conflicts

pull/83/head
JhonnyV-V 2024-06-27 19:04:26 -04:00
commit 856e44ec69
38 changed files with 843 additions and 158 deletions

19
.github/workflows/contributor_list.yml vendored Normal file
View File

@ -0,0 +1,19 @@
name: Auto Generate Contributor List
on:
push:
branches:
main
permissions:
contents: write
jobs:
generate-contributor-list:
name: Auto generate contributor list
runs-on: ubuntu-latest
steps:
- uses: wow-actions/contributors-list@v1
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
svgPath: CONTRIBUTORS.svg

21
.github/workflows/generate_toc.yml vendored Normal file
View File

@ -0,0 +1,21 @@
name: Generate TOC
on:
push:
paths:
README.md
branches:
main
permissions:
contents: write
jobs:
generate-toc:
name: TOC Generator
runs-on: ubuntu-latest
steps:
- uses: technote-space/toc-generator@v4
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TOC_TITLE: ''

33
CONTRIBUTORS.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 3.0 MiB

130
README.md
View File

@ -4,19 +4,66 @@
<p align="center"> <p align="center">
<img src="https://img.shields.io/badge/Neovim-57A143?logo=neovim&logoColor=fff&style=for-the-badge" alt="Neovim" /> <img src="https://img.shields.io/badge/For Neovim 0.9+-57A143?logo=neovim&logoColor=fff&style=for-the-badge" alt="Neovim" />
<img src="https://img.shields.io/badge/Made%20With%20Lua-2C2D72?logo=lua&logoColor=fff&style=for-the-badge" alt="made with lua" >
<img src="https://img.shields.io/github/actions/workflow/status/mistricky/codesnap.nvim/release.yml?style=for-the-badge&label=release" alt="release action status" /> <img src="https://img.shields.io/github/actions/workflow/status/mistricky/codesnap.nvim/release.yml?style=for-the-badge&label=release" alt="release action status" />
<img src="https://img.shields.io/github/actions/workflow/status/mistricky/codesnap.nvim/lint.yml?style=for-the-badge&label=Lint" alt="release action status" /> <img src="https://img.shields.io/github/actions/workflow/status/mistricky/codesnap.nvim/lint.yml?style=for-the-badge&label=Lint" alt="release action status" />
<a href="https://github.com/mistricky/codesnap.nvim/issues">
<img alt="Issues" src="https://img.shields.io/github/issues/mistricky/codesnap.nvim?style=for-the-badge&logo=github&color=%23ffbd5e">
</a>
<a href="https://github.com/mistricky/codesnap.nvim/blob/main/LICENSE">
<img alt="License" src="https://img.shields.io/github/license/mistricky/codesnap.nvim?style=for-the-badge&logo=github&color=%235ef1ff">
</a>
<a href="https://github.com/mistricky/codesnap.nvim/stars">
<img alt="stars" src="https://img.shields.io/github/stars/mistricky/codesnap.nvim?style=for-the-badge&logo=github&color=%23bd5eff">
</a>
<img src="https://img.shields.io/badge/Made%20With%20Lua-2C2D72?logo=lua&logoColor=fff&style=for-the-badge" alt="made with lua" >
<img src="https://img.shields.io/badge/Written%20in%20Rust-B7410E?logo=rust&logoColor=fff&style=for-the-badge" alt="written in rust" >
<a href="https://dotfyle.com/plugins/mistricky/codesnap.nvim">
<img src="https://dotfyle.com/plugins/mistricky/codesnap.nvim/shield?style=for-the-badge" />
</a>
</p> </p>
<h1 align="center">CodeSnap.nvim</h1> <h1 align="center">CodeSnap.nvim</h1>
<p align="center">📸 Snapshot plugin with rich features that can make pretty code snapshots for Neovim</p> <p align="center">📸 Snapshot plugin with rich features that can make pretty code snapshots for Neovim</p>
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [🚣Migration](#migration)
- [✨Features](#features)
- [Prerequirements](#prerequirements)
- [Install](#install)
- [Compile from source](#compile-from-source)
- [Compile on ARM](#compile-on-arm)
- [Keymappings](#keymappings)
- [Usage](#usage)
- [Copy into the clipboard](#copy-into-the-clipboard)
- [Copy into clipboard on Linux Wayland](#copy-into-clipboard-on-linux-wayland)
- [Save the snapshot](#save-the-snapshot)
- [Highlight code block](#highlight-code-block)
- [How to use](#how-to-use)
- [Specify language extension](#specify-language-extension)
- [Breadcrumbs](#breadcrumbs)
- [Show workspace in breadcrumbs](#show-workspace-in-breadcrumbs)
- [Custom path separator](#custom-path-separator)
- [Line number](#line-number)
- [Custom background](#custom-background)
- [Solid color background](#solid-color-background)
- [Watermark](#watermark)
- [Commands](#commands)
- [Configuration](#configuration)
- [Contribution](#contribution)
- [Contributors](#contributors)
- [License](#license)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## 🚣Migration ## 🚣Migration
If you have installed v0.x before, this chapter will show you what break changes version v1.x introduced. If you have installed v0.x before, this chapter will show you what break changes version v1.x introduced.
@ -34,7 +81,7 @@ v1.x has a different architecture and better performance than v0.x, and v1.x can
- 🤖 Generate snapshots using only a single command - 🤖 Generate snapshots using only a single command
- 🍞 Breadcrumbs for display file path - 🍞 Breadcrumbs for display file path
- 🌊 More beautiful background theme - 🌊 More beautiful background theme
- 🔢 [WIP] Column number - 🔢 Support for display line number make sharing code snapshot easier
## Prerequirements ## Prerequirements
- Neovim 0.9.0+ - Neovim 0.9.0+
@ -85,6 +132,24 @@ sudo dnf install libuv libuv-devel # On RHEL based systems
sudo apt-get install libtool libuv1-dev # On Debian based systems sudo apt-get install libtool libuv1-dev # On Debian based systems
``` ```
### Keymappings
If you use `Lazy.nvim` as your package manager, here are some examples show you how to configure keymappings for CodeSnap:
```lua
{
"mistricky/codesnap.nvim",
build = "make build_generator",
keys = {
{ "<leader>cc", "<cmd>CodeSnap<cr>", mode = "x", desc = "Save selected code snapshot into clipboard" },
{ "<leader>cs", "<cmd>CodeSnapSave<cr>", mode = "x", desc = "Save selected code snapshot in ~/Pictures" },
},
opts = {
save_path = "~/Pictures",
has_breadcrumbs = true,
bg_theme = "bamboo",
},
}
```
## Usage ## Usage
`CodeSnap.nvim` provides the following two ways to take snapshots of currently selected code `CodeSnap.nvim` provides the following two ways to take snapshots of currently selected code
@ -122,6 +187,25 @@ require("codesnap").setup({
https://github.com/mistricky/codesnap.nvim/assets/22574136/69b27e77-3dce-4bc3-8516-89ce636fe02d https://github.com/mistricky/codesnap.nvim/assets/22574136/69b27e77-3dce-4bc3-8516-89ce636fe02d
### Highlight code block
CodeSnap allows you to take code snapshots with highlights code blocks, we provide two commands for this scenario:
```shell
CodeSnapHighlight # Take code snapshot with highlights code blocks and copy it into the clipboard
CodeSnapSaveHighlight # Take code snapshot with highlights code blocks and save it somewhere
```
#### How to use
For take a code snapshot with highlights code blocks and save it somewhere. First you need to select code which you want to snapshot, then enter the command `CodeSnapSaveHighlight` to open a window show you the selected code which from previous step, now you can select code which you want to highlight, finally press the Enter key, CodeSnap will generate a snapshot with highlight blocks and save it in save_path.
Here is an example video:
https://github.com/mistricky/codesnap.nvim/assets/22574136/bea0bf6c-8fc9-4d09-9cab-4e1e6f47899c
### Specify language extension ### Specify language extension
In some scenarios, CodeSnap.nvim cannot auto-detect what language syntax should used to highlight code, for example, shell script can have no extension, they specify interpreters using shebang. In some scenarios, CodeSnap.nvim cannot auto-detect what language syntax should used to highlight code, for example, shell script can have no extension, they specify interpreters using shebang.
@ -144,6 +228,16 @@ require("codesnap").setup({
The breadcrumbs look like: The breadcrumbs look like:
![image](https://github.com/mistricky/codesnap.nvim/assets/22574136/23274faa-36a9-4d41-88a5-e48c44b4d5bf) ![image](https://github.com/mistricky/codesnap.nvim/assets/22574136/23274faa-36a9-4d41-88a5-e48c44b4d5bf)
### Show workspace in breadcrumbs
Breadcrumbs hide the workspace name by default, if you want to display workspace in breadcrumbs, you can just set `show_workspace` as true.
```lua
require("codesnap").setup({
-- ...
has_breadcrumbs = true
show_workspace = true
})
```
### Custom path separator ### Custom path separator
The CodeSnap.nvim uses `/` as the separator of the file path by default, of course, you can specify any symbol you prefer as the custom separator: The CodeSnap.nvim uses `/` as the separator of the file path by default, of course, you can specify any symbol you prefer as the custom separator:
```lua ```lua
@ -157,6 +251,17 @@ require("codesnap").setup({
![image](https://github.com/mistricky/codesnap.nvim/assets/22574136/84b80d0f-1467-4bdf-9cbd-aede868f93aa) ![image](https://github.com/mistricky/codesnap.nvim/assets/22574136/84b80d0f-1467-4bdf-9cbd-aede868f93aa)
## Line number
We also support displaying line number, you can set `has_line_number` to true to display line number.
```lua
require("codesnap").setup({
// ...
has_line_number = true,
})
```
![image](https://github.com/mistricky/codesnap.nvim/assets/22574136/3a5999b1-bb2a-4646-8d69-609be1d28140)
## Custom background ## Custom background
The `CodeSnap.nvim` comes with many beautiful backgrounds preset, you can set any background you like by setting `bg_theme` to its name, just like: The `CodeSnap.nvim` comes with many beautiful backgrounds preset, you can set any background you like by setting `bg_theme` to its name, just like:
@ -236,6 +341,16 @@ CodeSnap # Take a snapshot of the currently selected code and copy the snapshot
CodeSnapSave # Save the snapshot of the currently selected code and save it on the disk CodeSnapSave # Save the snapshot of the currently selected code and save it on the disk
``` ```
**Lua**
```lua
local codesnap <const> = require("codesnap")
-- Take a snapshot of the currently selected code and copy the snapshot into the clipboard
codesnap.copy_into_clipboard()
-- Save the snapshot of the currently selected code and save it on the disk
codesnap.save_snapshot()
```
## Configuration ## Configuration
Define your custom config using `setup` function Define your custom config using `setup` function
@ -254,6 +369,8 @@ There is a default config:
bg_theme = "default", bg_theme = "default",
breadcrumbs_separator = "/", breadcrumbs_separator = "/",
has_breadcrumbs = false, has_breadcrumbs = false,
has_line_number = false,
min_width = 0
} }
``` ```
@ -262,5 +379,10 @@ CodeSnap.nvim is a project that will be maintained for the long term, and we alw
The commit message convention of this project is following [commitlint-wizardoc](https://github.com/wizardoc/commitlint-wizardoc). The commit message convention of this project is following [commitlint-wizardoc](https://github.com/wizardoc/commitlint-wizardoc).
### Contributors
Thanks to all contributors for their contributions and works they have done.
<img src="CONTRIBUTORS.svg" />
## License ## License
MIT. MIT.

View File

@ -1,4 +1,4 @@
**codesnap.nvim** 📸 Snapshot plugin that can make pretty code snapshots with real-time previews for Neovim **codesnap.nvim** 📸 Snapshot plugin that can make pretty code snapshots with real-time previews for Neovim
Author: Mist <mist.zzh@gmail.com> Author: Mist <mist.zzh@gmail.com>
version: 1.1.12 version: 1.3.1

157
generator/Cargo.lock generated
View File

@ -8,6 +8,18 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "ahash"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "1.1.2" version = "1.1.2"
@ -17,6 +29,12 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "allocator-api2"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
[[package]] [[package]]
name = "arboard" name = "arboard"
version = "3.3.2" version = "3.3.2"
@ -87,7 +105,7 @@ dependencies = [
"regex", "regex",
"rustc-hash", "rustc-hash",
"shlex", "shlex",
"syn", "syn 2.0.52",
] ]
[[package]] [[package]]
@ -120,6 +138,39 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cached"
version = "0.49.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e8e463fceca5674287f32d252fb1d94083758b8709c160efae66d263e5f4eba"
dependencies = [
"ahash",
"cached_proc_macro",
"cached_proc_macro_types",
"hashbrown",
"instant",
"once_cell",
"thiserror",
]
[[package]]
name = "cached_proc_macro"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad9f16c0d84de31a2ab7fdf5f7783c14631f7075cf464eb3bb43119f61c9cb2a"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "cached_proc_macro_types"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.90" version = "1.0.90"
@ -245,6 +296,41 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "darling"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn 1.0.109",
]
[[package]]
name = "darling_macro"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e"
dependencies = [
"darling_core",
"quote",
"syn 1.0.109",
]
[[package]] [[package]]
name = "deranged" name = "deranged"
version = "0.3.11" version = "0.3.11"
@ -262,7 +348,7 @@ checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.52",
] ]
[[package]] [[package]]
@ -386,7 +472,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.52",
] ]
[[package]] [[package]]
@ -400,6 +486,7 @@ name = "generator"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"arboard", "arboard",
"cached",
"cosmic-text", "cosmic-text",
"nvim-oxi", "nvim-oxi",
"regex", "regex",
@ -433,6 +520,10 @@ name = "hashbrown"
version = "0.14.3" version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
dependencies = [
"ahash",
"allocator-api2",
]
[[package]] [[package]]
name = "home" name = "home"
@ -443,6 +534,12 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]] [[package]]
name = "image" name = "image"
version = "0.24.9" version = "0.24.9"
@ -467,6 +564,15 @@ dependencies = [
"hashbrown", "hashbrown",
] ]
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.10" version = "1.0.10"
@ -762,7 +868,7 @@ checksum = "7fa77de5791207f41bab307dadb91e66a2bbbd19433e4243ef98c9d45e03a007"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.52",
] ]
[[package]] [[package]]
@ -1033,7 +1139,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.52",
] ]
[[package]] [[package]]
@ -1055,7 +1161,7 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.52",
] ]
[[package]] [[package]]
@ -1091,6 +1197,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]] [[package]]
name = "swash" name = "swash"
version = "0.1.12" version = "0.1.12"
@ -1102,6 +1214,17 @@ dependencies = [
"zeno", "zeno",
] ]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.52" version = "2.0.52"
@ -1183,7 +1306,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.52",
] ]
[[package]] [[package]]
@ -1669,3 +1792,23 @@ name = "zeno"
version = "0.2.3" version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697" checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697"
[[package]]
name = "zerocopy"
version = "0.7.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
]

View File

@ -13,6 +13,7 @@ arboard = {features = ["wayland-data-control"], version = "3.3.2"}
thiserror = "1.0.58" thiserror = "1.0.58"
regex = "1.10.3" regex = "1.10.3"
two-face = "0.3.0" two-face = "0.3.0"
cached = "0.49.3"
wsl = "0.1.0" wsl = "0.1.0"
sys-info = "0.9.1" sys-info = "0.9.1"

View File

@ -1,6 +1,8 @@
use cached::proc_macro::cached;
use regex::Regex; use regex::Regex;
const MIN_WIDTH: f32 = 100.; const MIN_WIDTH: f32 = 100.;
pub const CHAR_WIDTH: f32 = 9.05;
fn min_width(width: f32) -> f32 { fn min_width(width: f32) -> f32 {
if width < MIN_WIDTH { if width < MIN_WIDTH {
@ -10,6 +12,9 @@ fn min_width(width: f32) -> f32 {
} }
} }
// Because the code block is input by users, we need to calculate the width and height
// to make sure render the width and height of the "editor" shape correctly
#[cached(key = "String", convert = r#"{ format!("{}", text) }"#)]
pub fn calc_wh(text: &str, char_wdith: f32, line_height: f32) -> (f32, f32) { pub fn calc_wh(text: &str, char_wdith: f32, line_height: f32) -> (f32, f32) {
let trimmed_text = prepare_code(text); let trimmed_text = prepare_code(text);
let lines = trimmed_text.lines(); let lines = trimmed_text.lines();
@ -34,29 +39,33 @@ fn replace_tab_to_space(text: &str) -> String {
str::replace(text, "\t", &spaces) str::replace(text, "\t", &spaces)
} }
// If the first line have indention, remove the same indention from subsequent lines // Find min indention of code lines, and remove the same indention from subsequent lines
fn trim_space(text: &str) -> String { fn trim_space(text: &str) -> String {
let lines = text.split("\n").collect::<Vec<&str>>(); let lines = text.split("\n").collect::<Vec<&str>>();
let first_line = lines.first().unwrap(); let regex = Regex::new(r"(?:^|\n)(\s*)").unwrap();
let head_spaces = Regex::new(r"^(\s*)").unwrap().find(first_line); let captures_iter = regex.captures_iter(text);
let space_lengths = captures_iter
.map(|capture| capture.get(1).unwrap().as_str().len())
.collect::<Vec<usize>>();
match head_spaces { if space_lengths.len() < lines.len() {
Some(head_spaces) => { return text.to_string();
return lines
.into_iter()
.map(|line| {
Regex::new(format!("^{}", head_spaces.as_str()).as_ref())
.unwrap()
.replace(line, "")
.to_string()
})
.collect::<Vec<String>>()
.join("\n");
}
None => text.to_string(),
} }
let need_to_remove_spaces = " ".repeat(space_lengths.into_iter().min().unwrap());
lines
.into_iter()
.map(|line| {
Regex::new(format!("^{}", need_to_remove_spaces).as_ref())
.unwrap()
.replace(line, "")
.to_string()
})
.collect::<Vec<String>>()
.join("\n")
} }
pub fn prepare_code(code: &str) -> String { pub fn prepare_code(code: &str) -> String {
replace_tab_to_space(&trim_space(&code)) trim_space(&replace_tab_to_space(&code))
} }

View File

@ -1,7 +1,10 @@
pub mod background; pub mod background;
pub mod breadcrumbs; pub mod breadcrumbs;
pub mod code_block;
pub mod container; pub mod container;
pub mod editor; pub mod editor;
pub mod highlight_code_block;
pub mod interface; pub mod interface;
pub mod line_number;
pub mod rect; pub mod rect;
pub mod watermark; pub mod watermark;

View File

@ -35,6 +35,7 @@ impl Component for Background {
context: &ComponentContext, context: &ComponentContext,
_render_params: &RenderParams, _render_params: &RenderParams,
_style: &ComponentStyle, _style: &ComponentStyle,
_parent_style: &ComponentStyle,
) -> render_error::Result<()> { ) -> render_error::Result<()> {
let mut paint = Paint::default(); let mut paint = Paint::default();
let w = pixmap.width() as f32; let w = pixmap.width() as f32;

View File

@ -5,7 +5,7 @@ use crate::{code::calc_wh, edges::margin::Margin, text::FontRenderer};
use super::interface::{ use super::interface::{
component::Component, component::Component,
style::{RawComponentStyle, Size}, style::{ComponentStyle, RawComponentStyle, Size},
}; };
pub struct Breadcrumbs { pub struct Breadcrumbs {
@ -41,6 +41,7 @@ impl Component for Breadcrumbs {
context: &super::interface::component::ComponentContext, context: &super::interface::component::ComponentContext,
render_params: &super::interface::component::RenderParams, render_params: &super::interface::component::RenderParams,
style: &super::interface::style::ComponentStyle, style: &super::interface::style::ComponentStyle,
_parent_style: &ComponentStyle,
) -> super::interface::render_error::Result<()> { ) -> super::interface::render_error::Result<()> {
if self.has_breadcrumbs { if self.has_breadcrumbs {
let attrs = Attrs::new() let attrs = Attrs::new()

View File

@ -0,0 +1,29 @@
use crate::edges::margin::Margin;
use super::interface::{
component::Component,
style::{RawComponentStyle, Style},
};
pub struct CodeBlock {
children: Vec<Box<dyn Component>>,
}
impl Component for CodeBlock {
fn children(&self) -> &Vec<Box<dyn Component>> {
&self.children
}
fn style(&self) -> RawComponentStyle {
Style::default().margin(Margin {
top: 10.,
..Margin::default()
})
}
}
impl CodeBlock {
pub fn from_children(children: Vec<Box<dyn Component>>) -> CodeBlock {
CodeBlock { children }
}
}

View File

@ -1,11 +1,10 @@
use crate::{ use crate::{
code::{calc_wh, prepare_code}, code::{calc_wh, prepare_code, CHAR_WIDTH},
components::interface::{ components::interface::{
component::{Component, ComponentContext, RenderParams}, component::{Component, ComponentContext, RenderParams},
render_error, render_error,
style::{ComponentStyle, RawComponentStyle, Size, Style}, style::{ComponentStyle, RawComponentStyle, Size, Style},
}, },
edges::margin::Margin,
highlight::Highlight, highlight::Highlight,
text::FontRenderer, text::FontRenderer,
}; };
@ -23,14 +22,9 @@ impl Component for Code {
} }
fn style(&self) -> RawComponentStyle { fn style(&self) -> RawComponentStyle {
let (w, h) = calc_wh(&self.value, 9.05, self.line_height); let (w, h) = calc_wh(&self.value, CHAR_WIDTH, self.line_height);
Style::default() Style::default().size(Size::Num(w), Size::Num(h))
.size(Size::Num(w), Size::Num(h))
.margin(Margin {
top: 10.,
..Margin::default()
})
} }
fn draw_self( fn draw_self(
@ -39,6 +33,7 @@ impl Component for Code {
context: &ComponentContext, context: &ComponentContext,
render_params: &RenderParams, render_params: &RenderParams,
style: &ComponentStyle, style: &ComponentStyle,
_parent_style: &ComponentStyle,
) -> render_error::Result<()> { ) -> render_error::Result<()> {
let params = &context.take_snapshot_params; let params = &context.take_snapshot_params;
let highlight = Highlight::new( let highlight = Highlight::new(

View File

@ -33,6 +33,7 @@ impl Component for MacTitleBar {
context: &ComponentContext, context: &ComponentContext,
render_params: &RenderParams, render_params: &RenderParams,
_style: &ComponentStyle, _style: &ComponentStyle,
_parent_style: &ComponentStyle,
) -> render_error::Result<()> { ) -> render_error::Result<()> {
self.draw_control_buttons( self.draw_control_buttons(
// Control bar construct by draw circles, after drawn, the path will be at the center, // Control bar construct by draw circles, after drawn, the path will be at the center,

View File

@ -0,0 +1,76 @@
use super::{
interface::{component::Component, style::ComponentStyle},
rect::EDITOR_PADDING,
};
use tiny_skia::{Color, Paint, Rect, Transform};
#[derive(Default)]
pub struct HighlightCodeBlock {
children: Vec<Box<dyn Component>>,
line_height: f32,
start_line_number: usize,
end_line_number: usize,
render_condition: bool,
}
impl Component for HighlightCodeBlock {
fn children(&self) -> &Vec<Box<dyn Component>> {
&self.children
}
fn render_condition(&self) -> bool {
self.render_condition
}
fn draw_self(
&self,
pixmap: &mut tiny_skia::Pixmap,
context: &super::interface::component::ComponentContext,
render_params: &super::interface::component::RenderParams,
_style: &super::interface::style::ComponentStyle,
parent_style: &ComponentStyle,
) -> super::interface::render_error::Result<()> {
let mut paint = Paint::default();
let start_y_offset = (self.start_line_number - 1) as f32 * self.line_height;
paint.anti_alias = false;
paint.set_color(Color::from_rgba8(255, 255, 255, 10));
pixmap.fill_rect(
Rect::from_xywh(
render_params.x - EDITOR_PADDING,
render_params.y + start_y_offset,
parent_style.width + EDITOR_PADDING * 2.,
(self.end_line_number - self.start_line_number + 1) as f32 * self.line_height,
)
.unwrap(),
&paint,
Transform::from_scale(context.scale_factor, context.scale_factor),
None,
);
Ok(())
}
}
impl HighlightCodeBlock {
pub fn from_line_number(
start_line_number: Option<usize>,
end_line_number: Option<usize>,
line_height: f32,
) -> HighlightCodeBlock {
if end_line_number < start_line_number {
panic!("end_line_number should be greater than start_line_number")
}
match start_line_number {
Some(start_line_number) => HighlightCodeBlock {
render_condition: true,
children: vec![],
line_height,
start_line_number,
end_line_number: end_line_number.unwrap(),
},
None => HighlightCodeBlock::default(),
}
}
}

View File

@ -1,13 +1,10 @@
use std::sync::Arc;
use tiny_skia::Pixmap;
use crate::{config::TakeSnapshotParams, edges::edge::Edge};
use super::{ use super::{
render_error, render_error,
style::{ComponentAlign, ComponentStyle, RawComponentStyle, Size, Style}, style::{ComponentAlign, ComponentStyle, RawComponentStyle, Size, Style},
}; };
use crate::{config::TakeSnapshotParams, edges::edge::Edge};
use std::sync::Arc;
use tiny_skia::Pixmap;
pub struct ComponentContext { pub struct ComponentContext {
pub scale_factor: f32, pub scale_factor: f32,
@ -73,6 +70,7 @@ pub trait Component {
_context: &ComponentContext, _context: &ComponentContext,
_render_params: &RenderParams, _render_params: &RenderParams,
_style: &ComponentStyle, _style: &ComponentStyle,
_parent_style: &ComponentStyle,
) -> render_error::Result<()> { ) -> render_error::Result<()> {
Ok(()) Ok(())
} }
@ -91,11 +89,17 @@ pub trait Component {
fn parsed_style(&self) -> Style<f32> { fn parsed_style(&self) -> Style<f32> {
let style = self.style(); let style = self.style();
let (width, height) = self.get_dynamic_wh(); let (width, height) = self.get_dynamic_wh();
let width = self.parse_size(style.width, width)
+ style.padding.horizontal()
+ style.margin.horizontal();
Style { Style {
width: self.parse_size(style.width, width) min_width: style.min_width,
+ style.padding.horizontal() width: if width > style.min_width {
+ style.margin.horizontal(), width
} else {
style.min_width
},
height: self.parse_size(style.height, height) height: self.parse_size(style.height, height)
+ style.padding.vertical() + style.padding.vertical()
+ style.margin.vertical(), + style.margin.vertical(),
@ -116,13 +120,13 @@ pub trait Component {
let style = self.parsed_style(); let style = self.parsed_style();
let render_params = self.initialize( let render_params = self.initialize(
&component_render_params.parse_into_render_params_with_style( &component_render_params.parse_into_render_params_with_style(
parent_style, parent_style.clone(),
sibling_style, sibling_style,
style.clone(), style.clone(),
), ),
); );
self.draw_self(pixmap, context, &render_params, &style)?; self.draw_self(pixmap, context, &render_params, &style, &parent_style)?;
let children = self.children(); let children = self.children();
let mut sibling_render_params = RenderParams { let mut sibling_render_params = RenderParams {
@ -152,6 +156,8 @@ pub trait Component {
Ok(render_params.clone()) Ok(render_params.clone())
} }
// Dynamic calculate width and height of children, if the children is empty, get_dynamic_wh
// will return (0., 0.)
fn get_dynamic_wh(&self) -> (f32, f32) { fn get_dynamic_wh(&self) -> (f32, f32) {
let children = self.children(); let children = self.children();
let calc_children_wh = |cb: fn((f32, f32), &Box<dyn Component>) -> (f32, f32)| { let calc_children_wh = |cb: fn((f32, f32), &Box<dyn Component>) -> (f32, f32)| {
@ -160,11 +166,13 @@ pub trait Component {
let style = self.style(); let style = self.style();
match style.align { match style.align {
// If align is row, width is sum of children width, height is max of children height
ComponentAlign::Row => calc_children_wh(|(w, h), child| { ComponentAlign::Row => calc_children_wh(|(w, h), child| {
let style = child.parsed_style(); let style = child.parsed_style();
(w + style.width, h.max(style.height)) (w + style.width, h.max(style.height))
}), }),
// If align is column, width is max of children width, height is sum of children height
ComponentAlign::Column => calc_children_wh(|(w, h), child| { ComponentAlign::Column => calc_children_wh(|(w, h), child| {
let style = child.parsed_style(); let style = child.parsed_style();

View File

@ -15,6 +15,7 @@ pub enum Size {
pub struct Style<T> { pub struct Style<T> {
pub width: T, pub width: T,
pub height: T, pub height: T,
pub min_width: f32,
pub align: ComponentAlign, pub align: ComponentAlign,
pub padding: Padding, pub padding: Padding,
pub margin: Margin, pub margin: Margin,
@ -26,6 +27,7 @@ pub type ComponentStyle = Style<f32>;
impl Default for RawComponentStyle { impl Default for RawComponentStyle {
fn default() -> Self { fn default() -> Self {
Style { Style {
min_width: 0.,
width: Size::Dynamic, width: Size::Dynamic,
height: Size::Dynamic, height: Size::Dynamic,
align: ComponentAlign::Row, align: ComponentAlign::Row,
@ -38,6 +40,7 @@ impl Default for RawComponentStyle {
impl Default for ComponentStyle { impl Default for ComponentStyle {
fn default() -> Self { fn default() -> Self {
Style { Style {
min_width: 0.,
width: 0., width: 0.,
height: 0., height: 0.,
align: ComponentAlign::Row, align: ComponentAlign::Row,
@ -54,6 +57,12 @@ impl RawComponentStyle {
self self
} }
// Only works if the width is calculate dynamically
pub fn min_width(mut self, min_width: f32) -> Self {
self.min_width = min_width;
self
}
pub fn align(mut self, align: ComponentAlign) -> Self { pub fn align(mut self, align: ComponentAlign) -> Self {
self.align = align; self.align = align;
self self

View File

@ -0,0 +1,100 @@
use super::interface::{
component::{Component, ComponentContext, RenderParams},
render_error,
style::{ComponentStyle, RawComponentStyle, Size, Style},
};
use crate::{code::CHAR_WIDTH, edges::margin::Margin, text::FontRenderer};
use cosmic_text::{Attrs, Color, Family};
const FONT_SIZE: f32 = 14.;
#[derive(Default)]
pub struct LineNumber {
children: Vec<Box<dyn Component>>,
line_height: f32,
render_condition: bool,
line_number_content: Vec<String>,
number_of_digit: usize,
}
impl Component for LineNumber {
fn render_condition(&self) -> bool {
return self.render_condition;
}
fn children(&self) -> &Vec<Box<dyn Component>> {
&self.children
}
fn style(&self) -> RawComponentStyle {
Style::default()
.size(
Size::Num(CHAR_WIDTH * self.number_of_digit as f32),
Size::Num(self.line_number_content.len() as f32 * self.line_height),
)
.margin(Margin {
right: 10.,
..Margin::default()
})
}
fn draw_self(
&self,
pixmap: &mut tiny_skia::Pixmap,
context: &ComponentContext,
render_params: &RenderParams,
style: &ComponentStyle,
_parent_style: &ComponentStyle,
) -> render_error::Result<()> {
FontRenderer::new(
FONT_SIZE,
self.line_height,
context.scale_factor,
&context.take_snapshot_params.fonts_folder,
)
.draw_text(
render_params.x,
render_params.y,
style.width,
style.height,
vec![(
&self.line_number_content.join("\n"),
Attrs::new()
.color(Color::rgb(73, 81, 98))
.family(Family::Name(&context.take_snapshot_params.code_font_family)),
)],
pixmap,
);
Ok(())
}
}
impl LineNumber {
pub fn new(content: &str, start_line_number: Option<usize>, line_height: f32) -> LineNumber {
match start_line_number {
None => LineNumber::default(),
Some(start_line_number) => {
let lines = content.split("\n").collect::<Vec<&str>>();
let max_line_number = lines.len() + start_line_number;
let number_of_digit = (max_line_number - 1).to_string().len();
LineNumber {
line_number_content: (start_line_number..max_line_number)
.map(|line_number| {
format!(
"{:>width$}",
line_number.to_string(),
width = number_of_digit
)
})
.collect::<Vec<String>>(),
number_of_digit,
children: vec![],
render_condition: true,
line_height,
}
}
}
}
}

View File

@ -1,14 +1,16 @@
use crate::edges::padding::Padding;
use super::interface::{ use super::interface::{
component::{Component, ComponentContext, RenderParams}, component::{Component, ComponentContext, RenderParams},
render_error, render_error,
style::{ComponentAlign, ComponentStyle, RawComponentStyle, Style}, style::{ComponentAlign, ComponentStyle, RawComponentStyle, Style},
}; };
use crate::edges::padding::Padding;
use tiny_skia::{FillRule, Paint, PathBuilder, Pixmap, Transform}; use tiny_skia::{FillRule, Paint, PathBuilder, Pixmap, Transform};
pub const EDITOR_PADDING: f32 = 20.;
pub struct Rect { pub struct Rect {
radius: f32, radius: f32,
min_width: f32,
children: Vec<Box<dyn Component>>, children: Vec<Box<dyn Component>>,
} }
@ -19,8 +21,9 @@ impl Component for Rect {
fn style(&self) -> RawComponentStyle { fn style(&self) -> RawComponentStyle {
Style::default() Style::default()
.min_width(self.min_width)
.align(ComponentAlign::Column) .align(ComponentAlign::Column)
.padding(Padding::from_value(20.)) .padding(Padding::from_value(EDITOR_PADDING))
} }
fn draw_self( fn draw_self(
@ -29,6 +32,7 @@ impl Component for Rect {
context: &ComponentContext, context: &ComponentContext,
render_params: &RenderParams, render_params: &RenderParams,
style: &ComponentStyle, style: &ComponentStyle,
_parent_style: &ComponentStyle,
) -> render_error::Result<()> { ) -> render_error::Result<()> {
let mut path_builder = PathBuilder::new(); let mut path_builder = PathBuilder::new();
let x = render_params.x; let x = render_params.x;
@ -92,7 +96,11 @@ impl Component for Rect {
} }
impl Rect { impl Rect {
pub fn new(radius: f32, children: Vec<Box<dyn Component>>) -> Rect { pub fn new(radius: f32, min_width: Option<f32>, children: Vec<Box<dyn Component>>) -> Rect {
Rect { radius, children } Rect {
radius,
children,
min_width: min_width.unwrap_or(0.),
}
} }
} }

View File

@ -21,6 +21,7 @@ impl Component for Watermark {
context: &ComponentContext, context: &ComponentContext,
render_params: &RenderParams, render_params: &RenderParams,
_style: &ComponentStyle, _style: &ComponentStyle,
_parent_style: &ComponentStyle,
) -> render_error::Result<()> { ) -> render_error::Result<()> {
let params = &context.take_snapshot_params; let params = &context.take_snapshot_params;

View File

@ -26,6 +26,10 @@ pub struct TakeSnapshotParams {
pub file_path: String, pub file_path: String,
pub breadcrumbs_separator: String, pub breadcrumbs_separator: String,
pub has_breadcrumbs: bool, pub has_breadcrumbs: bool,
pub start_line_number: Option<usize>,
pub highlight_start_line_number: Option<usize>,
pub highlight_end_line_number: Option<usize>,
pub min_width: Option<f32>,
} }
impl FromObject for TakeSnapshotParams { impl FromObject for TakeSnapshotParams {

View File

@ -53,6 +53,9 @@ impl Highlight {
))?, ))?,
}; };
// The Syntect clearly distinguish between PHP and PHP Source
// Should use PHP as highlight language if the source content contains "<php" tag
// Should use PHP Source as highlight language if the source content not contains "<php" tag
if let Some(identifier) = self.highlighting_language_source_map.get(&syntax.name[..]) { if let Some(identifier) = self.highlighting_language_source_map.get(&syntax.name[..]) {
if !self.content.contains(identifier) { if !self.content.contains(identifier) {
return Ok(syntax_set return Ok(syntax_set
@ -77,21 +80,28 @@ impl Highlight {
let mut highlight = HighlightLines::new(syntax, &theme_set.themes[theme]); let mut highlight = HighlightLines::new(syntax, &theme_set.themes[theme]);
let attrs = Attrs::new().family(Family::Name(self.font_family.as_ref())); let attrs = Attrs::new().family(Family::Name(self.font_family.as_ref()));
// Highlight the content line by line using highlight_line function
Ok(LinesWithEndings::from(&self.content) Ok(LinesWithEndings::from(&self.content)
.map(|line| highlight.highlight_line(line, &syntax_set).unwrap()) .map(|line| {
highlight
.highlight_line(line, &syntax_set)
.unwrap()
.into_iter()
.map(|(style, str)| {
let syntect::highlighting::Color { r, g, b, a: _ } = style.foreground;
let attrs = match style.font_style {
FontStyle::BOLD => attrs.weight(Weight::BOLD),
FontStyle::ITALIC => attrs.style(Style::Italic),
FontStyle::UNDERLINE => attrs.style(Style::Normal),
_ => attrs,
};
(str, attrs.color(cosmic_text::Color::rgb(r, g, b)))
})
.collect::<HighlightResult>()
})
.fold(vec![], |acc, cur| [acc, cur].concat()) .fold(vec![], |acc, cur| [acc, cur].concat())
.into_iter() .into_iter()
.map(move |(style, str)| {
let syntect::highlighting::Color { r, g, b, a: _ } = style.foreground;
let attrs = match style.font_style {
FontStyle::BOLD => attrs.weight(Weight::BOLD),
FontStyle::ITALIC => attrs.style(Style::Italic),
FontStyle::UNDERLINE => attrs.style(Style::Normal),
_ => attrs,
};
(str, attrs.color(cosmic_text::Color::rgb(r, g, b)))
})
.collect::<HighlightResult>()) .collect::<HighlightResult>())
} }
} }

View File

@ -4,17 +4,21 @@ use tiny_skia::Pixmap;
use crate::components::background::Background; use crate::components::background::Background;
use crate::components::breadcrumbs::Breadcrumbs; use crate::components::breadcrumbs::Breadcrumbs;
use crate::components::code_block::CodeBlock;
use crate::components::container::Container; use crate::components::container::Container;
use crate::components::editor::code::Code; use crate::components::editor::code::Code;
use crate::components::editor::mac_title_bar::MacTitleBar; use crate::components::editor::mac_title_bar::MacTitleBar;
use crate::components::highlight_code_block::HighlightCodeBlock;
use crate::components::interface::component::ComponentContext; use crate::components::interface::component::ComponentContext;
use crate::components::interface::render_error; use crate::components::interface::render_error;
use crate::components::line_number::LineNumber;
use crate::components::rect::Rect; use crate::components::rect::Rect;
use crate::components::watermark::Watermark; use crate::components::watermark::Watermark;
use crate::config::TakeSnapshotParams; use crate::config::TakeSnapshotParams;
// Scale the screenshot to 3 times its size // Scale the screenshot to 3 times its size
const SCALE_FACTOR: f32 = 3.; const SCALE_FACTOR: f32 = 3.;
const LINE_HEIGHT: f32 = 20.;
// The params is come from neovim instance // The params is come from neovim instance
pub fn take_snapshot(params: TakeSnapshotParams) -> render_error::Result<Pixmap> { pub fn take_snapshot(params: TakeSnapshotParams) -> render_error::Result<Pixmap> {
@ -25,6 +29,7 @@ pub fn take_snapshot(params: TakeSnapshotParams) -> render_error::Result<Pixmap>
let pixmap = Container::from_children(vec![Box::new(Background::from_children(vec![ let pixmap = Container::from_children(vec![Box::new(Background::from_children(vec![
Box::new(Rect::new( Box::new(Rect::new(
16., 16.,
params.min_width,
vec![ vec![
Box::new(MacTitleBar::from_radius(8., params.mac_window_bar)), Box::new(MacTitleBar::from_radius(8., params.mac_window_bar)),
Box::new(Breadcrumbs::from_path( Box::new(Breadcrumbs::from_path(
@ -33,7 +38,19 @@ pub fn take_snapshot(params: TakeSnapshotParams) -> render_error::Result<Pixmap>
params.breadcrumbs_separator, params.breadcrumbs_separator,
params.has_breadcrumbs, params.has_breadcrumbs,
)), )),
Box::new(Code::new(params.code, 20., 15.)), Box::new(CodeBlock::from_children(vec![
Box::new(HighlightCodeBlock::from_line_number(
params.highlight_start_line_number,
params.highlight_end_line_number,
LINE_HEIGHT,
)),
Box::new(LineNumber::new(
&params.code,
params.start_line_number,
LINE_HEIGHT,
)),
Box::new(Code::new(params.code, LINE_HEIGHT, 15.)),
])),
], ],
)), )),
Box::new(Watermark::new(params.watermark)), Box::new(Watermark::new(params.watermark)),

View File

@ -1,61 +0,0 @@
local logger = require("codesnap.utils.logger")
local static = require("codesnap.static")
local client = {
job_id = 0,
}
local cwd = static.cwd .. "/snap-server"
function client:connect()
return vim.fn.jobstart({
cwd .. "/target/release/snap-server",
}, {
cwd = cwd,
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

@ -25,8 +25,15 @@ local function parse_save_path(save_path)
return parsed_save_path .. auto_generate_snap_filename() return parsed_save_path .. auto_generate_snap_filename()
end end
local function get_file_path(show_workspace)
local relative_path = path_utils.get_relative_path()
return show_workspace and path_utils.get_workspace() .. "/" .. relative_path or relative_path
end
function config_module.get_config(extension) function config_module.get_config(extension)
local code = visual_utils.get_selected_text() local code = visual_utils.get_selected_text()
local start_line_number = visual_utils.get_start_line_number()
if string_utils.is_str_empty(code) then if string_utils.is_str_empty(code) then
error("No code is selected", 0) error("No code is selected", 0)
@ -40,7 +47,8 @@ function config_module.get_config(extension)
fonts_folder = assets_folder .. "/fonts", fonts_folder = assets_folder .. "/fonts",
themes_folder = assets_folder .. "/themes", themes_folder = assets_folder .. "/themes",
theme = "base16-onedark", theme = "base16-onedark",
file_path = static.config.has_breadcrumbs and path_utils.get_relative_path() or "", file_path = static.config.has_breadcrumbs and get_file_path(static.config.show_workspace) or "",
start_line_number = static.config.has_line_number and start_line_number or nil,
}, static.config) }, static.config)
config.save_path = parse_save_path(config.save_path) config.save_path = parse_save_path(config.save_path)

View File

@ -0,0 +1,55 @@
local string_utils = require("codesnap.utils.string")
local table_utils = require("codesnap.utils.table")
local highlight_module = {}
function highlight_module.call_cb_with_parsed_config(cb_name, highlight_start_line_number, highlight_end_line_number)
vim.api.nvim_buf_delete(0, {})
vim.schedule(function()
local main = require("codesnap")
local config = table_utils.merge(main.highlight_mode_config, {
highlight_start_line_number = highlight_start_line_number,
highlight_end_line_number = highlight_end_line_number,
})
main[cb_name](config)
end)
end
function highlight_module.create_highlight_selector_window(cb_name, code)
local width = 100
local height = #code + 2
local row = vim.fn.winheight(0) / 2 - height / 2
local col = vim.fn.winwidth(0) / 2 - width / 2
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, code)
local window_id = vim.api.nvim_open_win(bufnr, false, {
relative = "editor",
width = width,
height = height,
col = col,
row = row,
style = "minimal",
border = "rounded",
title = "Select highlight lines",
title_pos = "center",
})
vim.api.nvim_buf_set_option(bufnr, "modifiable", false)
vim.api.nvim_buf_set_option(bufnr, "filetype", vim.bo.filetype)
vim.api.nvim_buf_set_keymap(bufnr, "n", "q", ":q<CR>", {})
vim.api.nvim_buf_set_keymap(bufnr, "", "<ESC>", ":q<CR>", {})
vim.api.nvim_buf_set_keymap(
bufnr,
"v",
"<CR>",
":lua require('codesnap.highlight').call_cb_with_parsed_config('"
.. cb_name
.. "', require('codesnap.utils.visual').get_start_line_number(), require('codesnap.utils.visual').get_end_line_number())<CR>",
{ silent = true }
)
vim.api.nvim_set_current_win(window_id)
end
return highlight_module

View File

@ -1,24 +1,27 @@
local static = require("codesnap.static") local static = require("codesnap.static")
local visual_utils = require("codesnap.utils.visual")
local table_utils = require("codesnap.utils.table") local table_utils = require("codesnap.utils.table")
local string_utils = require("codesnap.utils.string") local string_utils = require("codesnap.utils.string")
local config_module = require("codesnap.config") local config_module = require("codesnap.config")
local highlight_module = require("codesnap.highlight")
local main = { local main = {
cwd = static.cwd, cwd = static.cwd,
preview_switch = static.preview_switch, preview_switch = static.preview_switch,
highlight_mode_config = nil,
} }
function main.setup(config) function main.setup(config)
static.config = table_utils.merge(static.config, config == nil and {} or config) static.config = table_utils.merge(static.config, config == nil and {} or config)
end end
function main.copy_into_clipboard(extension) function main.copy_into_clipboard_with_config(config)
require("generator").copy_into_clipboard(config_module.get_config(extension)) require("generator").copy_into_clipboard(config)
vim.cmd("delmarks <>") vim.cmd("delmarks <>")
vim.notify("Save snapshot into clipboard successfully") vim.notify("Save snapshot into clipboard successfully")
end end
function main.save_snapshot(extension) function main.save_snapshot_with_config(config)
if string_utils.is_str_empty(static.config.save_path) then if string_utils.is_str_empty(static.config.save_path) then
error( error(
"If you want to save snapshot in somewhere, you should config the save_path before, refer: https://github.com/mistricky/codesnap.nvim?tab=readme-ov-file#save-the-snapshot", "If you want to save snapshot in somewhere, you should config the save_path before, refer: https://github.com/mistricky/codesnap.nvim?tab=readme-ov-file#save-the-snapshot",
@ -32,11 +35,35 @@ function main.save_snapshot(extension)
error("The extension of save_path should be .png", 0) error("The extension of save_path should be .png", 0)
end end
local config = config_module.get_config(extension)
require("generator").save_snapshot(config) require("generator").save_snapshot(config)
vim.cmd("delmarks <>") vim.cmd("delmarks <>")
---@diagnostic disable-next-line: need-check-nil
vim.notify("Save snapshot in " .. config.save_path .. " successfully") vim.notify("Save snapshot in " .. config.save_path .. " successfully")
end end
-- Take a snapshot and copy it into clipboard
function main.copy_into_clipboard(extension)
main.copy_into_clipboard_with_config(config_module.get_config(extension))
end
-- Take a snapshot and save it into the specified path
function main.save_snapshot(extension)
main.save_snapshot_with_config(config_module.get_config(extension))
end
function main.highlight_mode_copy_into_clipboard(extension)
main.highlight_mode_config = config_module.get_config(extension)
highlight_module.create_highlight_selector_window(
"copy_into_clipboard_with_config",
visual_utils.get_selected_lines()
)
end
function main.highlight_mode_save_snapshot(extension)
main.highlight_mode_config = config_module.get_config(extension)
highlight_module.create_highlight_selector_window("save_snapshot_with_config", visual_utils.get_selected_lines())
end
return main return main

View File

@ -10,6 +10,9 @@ return {
bg_theme = "default", bg_theme = "default",
breadcrumbs_separator = "/", breadcrumbs_separator = "/",
has_breadcrumbs = false, has_breadcrumbs = false,
has_line_number = false,
show_workspace = false,
min_width = 0,
}, },
cwd = path_utils.back(path_utils.back(debug.getinfo(1, "S").source:sub(2):match("(.*[/\\])"))), cwd = path_utils.back(path_utils.back(debug.getinfo(1, "S").source:sub(2):match("(.*[/\\])"))),
preview_switch = true, preview_switch = true,

View File

@ -1,11 +0,0 @@
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

@ -1,17 +1,29 @@
local string_utils = require("codesnap.utils.string") local string_utils = require("codesnap.utils.string")
local path_utils = {} local path_utils = {}
function path_utils.get_escaped_cwd()
local cwd = vim.fn.getcwd()
return string_utils.escape(cwd)
end
function path_utils.back(path) function path_utils.back(path)
local parsed_path, _ = path:gsub("/[^\\/]+/?$", "") local parsed_path, _ = path:gsub("/[^\\/]+/?$", "")
return parsed_path return parsed_path
end end
function path_utils.get_workspace()
local cwd = vim.fn.getcwd()
local _, _, workspace = string.find(cwd, "/([^/]+)$")
return workspace == nil and "" or workspace
end
function path_utils.get_relative_path() function path_utils.get_relative_path()
local full_file_path = vim.fn.expand("%:p") local full_file_path = vim.fn.expand("%:p")
local cwd = vim.fn.getcwd()
return full_file_path:gsub(string_utils.escape(cwd), ""):sub(2) return full_file_path:gsub(path_utils.get_escaped_cwd(), ""):sub(2)
end end
return path_utils return path_utils

View File

@ -24,4 +24,14 @@ function string_util.convert_empty_to_nil(target)
end end
end end
function string_util.split(str, delimiter)
local result = {}
for token in string.gmatch(str, "[^" .. delimiter .. "]+") do
table.insert(result, token)
end
return result
end
return string_util return string_util

View File

@ -33,7 +33,7 @@ end
function table_utils.serialize_array(t) function table_utils.serialize_array(t)
local result = list_utils.map(t, function(ele) local result = list_utils.map(t, function(ele)
table_utils.serialize_json(ele) table_utils.to_string(ele)
end) end)
return "[" .. result.concat(t, ",") .. "]" return "[" .. result.concat(t, ",") .. "]"
@ -43,17 +43,17 @@ function table_utils.serialize_table(t)
local result = {} local result = {}
for key, value in pairs(t) do for key, value in pairs(t) do
table.insert(result, string.format('"%s":%s', key, table_utils.serialize_json(value))) table.insert(result, string.format("%s = %s", key, table_utils.to_string(value)))
end end
return "{" .. table.concat(result, ",") .. "}" return "{" .. table.concat(result, ",") .. "}"
end end
function table_utils.serialize_string(value) function table_utils.serialize_string(value)
return '"' .. value .. '"' return "[[" .. value .. "]]"
end end
function table_utils.serialize_json(t) function table_utils.to_string(t)
local complex_type_parser = { local complex_type_parser = {
array = table_utils.serialize_array, array = table_utils.serialize_array,
table = table_utils.serialize_table, table = table_utils.serialize_table,
@ -67,4 +67,14 @@ function table_utils.serialize_json(t)
return parse(t) return parse(t)
end end
-- function table_utils.to_string(t)
-- local result = ""
--
-- for key, value in pairs(t) do
-- result = result .. key .. ":" .. tostring(value) .. ","
-- end
--
-- return "{" .. result .. "}"
-- end
return table_utils return table_utils

View File

@ -2,7 +2,6 @@ local visual_utils = {}
-- Get all the lines from "from" to "to" and return them as a single string -- Get all the lines from "from" to "to" and return them as a single string
-- If "from" and "to" are the same, return the line at "from" -- If "from" and "to" are the same, return the line at "from"
local function get_whole_lines(from, to) local function get_whole_lines(from, to)
local lines = {} local lines = {}
if from == to then if from == to then
@ -15,10 +14,20 @@ local function get_whole_lines(from, to)
return table.concat(lines, "\n") return table.concat(lines, "\n")
end end
function visual_utils.get_selected_text() function visual_utils.get_start_line_number()
local selected_text = vim.fn.getline("'<", "'>") return vim.fn.line("'<")
end
return table.concat(selected_text, "\n") function visual_utils.get_end_line_number()
return vim.fn.line("'>")
end
function visual_utils.get_selected_lines()
return vim.fn.getline("'<", "'>")
end
function visual_utils.get_selected_text()
return table.concat(visual_utils.get_selected_lines(), "\n")
end end
function visual_utils.get_selected_text_realtime() function visual_utils.get_selected_text_realtime()

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -22,3 +22,15 @@ end
vim.api.nvim_create_user_command("CodeSnap", take_snapshot(codesnap.copy_into_clipboard), { nargs = "*", range = "%" }) vim.api.nvim_create_user_command("CodeSnap", take_snapshot(codesnap.copy_into_clipboard), { nargs = "*", range = "%" })
vim.api.nvim_create_user_command("CodeSnapSave", take_snapshot(codesnap.save_snapshot), { nargs = "*", range = "%" }) vim.api.nvim_create_user_command("CodeSnapSave", take_snapshot(codesnap.save_snapshot), { nargs = "*", range = "%" })
vim.api.nvim_create_user_command(
"CodeSnapHighlight",
take_snapshot(codesnap.highlight_mode_copy_into_clipboard),
{ nargs = "*", range = "%" }
)
vim.api.nvim_create_user_command(
"CodeSnapSaveHighlight",
take_snapshot(codesnap.highlight_mode_save_snapshot),
{ nargs = "*", range = "%" }
)

View File

@ -1,6 +1,6 @@
[project] [project]
name = "codesnap.nvim" name = "codesnap.nvim"
version = "1.1.12" version = "1.3.1"
description = "📸 Snapshot plugin that can make pretty code snapshots with real-time previews for Neovim" description = "📸 Snapshot plugin that can make pretty code snapshots with real-time previews for Neovim"
author = "Mist" author = "Mist"
email = "mist.zzh@gmail.com" email = "mist.zzh@gmail.com"