From 9836ad1163a64ceacd1fe2ededb6fcda7f9fcc9d Mon Sep 17 00:00:00 2001 From: Mist Date: Sat, 27 Apr 2024 19:07:41 +0800 Subject: [PATCH] [Feat] support highlight code block --- generator/src/components.rs | 1 + generator/src/components/background.rs | 1 + generator/src/components/breadcrumbs.rs | 3 +- generator/src/components/editor/code.rs | 1 + .../src/components/editor/mac_title_bar.rs | 1 + .../src/components/highlight_code_block.rs | 76 +++++++++++++++++++ .../src/components/interface/component.rs | 5 +- generator/src/components/line_number.rs | 1 + generator/src/components/rect.rs | 8 +- generator/src/components/watermark.rs | 1 + generator/src/config.rs | 2 + generator/src/snapshot.rs | 15 +++- lua/codesnap/highlight.lua | 54 +++++++++++++ lua/codesnap/init.lua | 32 ++++++-- lua/codesnap/utils/string.lua | 10 +++ lua/codesnap/utils/table.lua | 18 ++++- lua/codesnap/utils/visual.lua | 4 + plugin/codesnap.lua | 12 +++ 18 files changed, 228 insertions(+), 17 deletions(-) create mode 100644 generator/src/components/highlight_code_block.rs create mode 100644 lua/codesnap/highlight.lua diff --git a/generator/src/components.rs b/generator/src/components.rs index 530c222..6a31557 100644 --- a/generator/src/components.rs +++ b/generator/src/components.rs @@ -3,6 +3,7 @@ pub mod breadcrumbs; pub mod code_block; pub mod container; pub mod editor; +pub mod highlight_code_block; pub mod interface; pub mod line_number; pub mod rect; diff --git a/generator/src/components/background.rs b/generator/src/components/background.rs index 8b4ca83..6fa69e2 100644 --- a/generator/src/components/background.rs +++ b/generator/src/components/background.rs @@ -35,6 +35,7 @@ impl Component for Background { context: &ComponentContext, _render_params: &RenderParams, _style: &ComponentStyle, + _parent_style: &ComponentStyle, ) -> render_error::Result<()> { let mut paint = Paint::default(); let w = pixmap.width() as f32; diff --git a/generator/src/components/breadcrumbs.rs b/generator/src/components/breadcrumbs.rs index e096dda..a41d44b 100644 --- a/generator/src/components/breadcrumbs.rs +++ b/generator/src/components/breadcrumbs.rs @@ -5,7 +5,7 @@ use crate::{code::calc_wh, edges::margin::Margin, text::FontRenderer}; use super::interface::{ component::Component, - style::{RawComponentStyle, Size}, + style::{ComponentStyle, RawComponentStyle, Size}, }; pub struct Breadcrumbs { @@ -41,6 +41,7 @@ impl Component for Breadcrumbs { context: &super::interface::component::ComponentContext, render_params: &super::interface::component::RenderParams, style: &super::interface::style::ComponentStyle, + _parent_style: &ComponentStyle, ) -> super::interface::render_error::Result<()> { if self.has_breadcrumbs { let attrs = Attrs::new() diff --git a/generator/src/components/editor/code.rs b/generator/src/components/editor/code.rs index c875e4a..7219d44 100644 --- a/generator/src/components/editor/code.rs +++ b/generator/src/components/editor/code.rs @@ -33,6 +33,7 @@ impl Component for Code { context: &ComponentContext, render_params: &RenderParams, style: &ComponentStyle, + _parent_style: &ComponentStyle, ) -> render_error::Result<()> { let params = &context.take_snapshot_params; let highlight = Highlight::new( diff --git a/generator/src/components/editor/mac_title_bar.rs b/generator/src/components/editor/mac_title_bar.rs index acf67c2..e112ad3 100644 --- a/generator/src/components/editor/mac_title_bar.rs +++ b/generator/src/components/editor/mac_title_bar.rs @@ -33,6 +33,7 @@ impl Component for MacTitleBar { context: &ComponentContext, render_params: &RenderParams, _style: &ComponentStyle, + _parent_style: &ComponentStyle, ) -> render_error::Result<()> { self.draw_control_buttons( // Control bar construct by draw circles, after drawn, the path will be at the center, diff --git a/generator/src/components/highlight_code_block.rs b/generator/src/components/highlight_code_block.rs new file mode 100644 index 0000000..3a2ea22 --- /dev/null +++ b/generator/src/components/highlight_code_block.rs @@ -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>, + line_height: f32, + start_line_number: usize, + end_line_number: usize, + render_condition: bool, +} + +impl Component for HighlightCodeBlock { + fn children(&self) -> &Vec> { + &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, 18)); + 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, + end_line_number: Option, + 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(), + } + } +} diff --git a/generator/src/components/interface/component.rs b/generator/src/components/interface/component.rs index 7dc73ef..3e64cf5 100644 --- a/generator/src/components/interface/component.rs +++ b/generator/src/components/interface/component.rs @@ -70,6 +70,7 @@ pub trait Component { _context: &ComponentContext, _render_params: &RenderParams, _style: &ComponentStyle, + _parent_style: &ComponentStyle, ) -> render_error::Result<()> { Ok(()) } @@ -113,13 +114,13 @@ pub trait Component { let style = self.parsed_style(); let render_params = self.initialize( &component_render_params.parse_into_render_params_with_style( - parent_style, + parent_style.clone(), sibling_style, 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 mut sibling_render_params = RenderParams { diff --git a/generator/src/components/line_number.rs b/generator/src/components/line_number.rs index 5412dac..b2a9ca1 100644 --- a/generator/src/components/line_number.rs +++ b/generator/src/components/line_number.rs @@ -44,6 +44,7 @@ impl Component for LineNumber { context: &ComponentContext, render_params: &RenderParams, style: &ComponentStyle, + _parent_style: &ComponentStyle, ) -> render_error::Result<()> { FontRenderer::new( FONT_SIZE, diff --git a/generator/src/components/rect.rs b/generator/src/components/rect.rs index 2fdc489..6289b77 100644 --- a/generator/src/components/rect.rs +++ b/generator/src/components/rect.rs @@ -1,12 +1,13 @@ -use crate::edges::padding::Padding; - use super::interface::{ component::{Component, ComponentContext, RenderParams}, render_error, style::{ComponentAlign, ComponentStyle, RawComponentStyle, Style}, }; +use crate::edges::padding::Padding; use tiny_skia::{FillRule, Paint, PathBuilder, Pixmap, Transform}; +pub const EDITOR_PADDING: f32 = 20.; + pub struct Rect { radius: f32, children: Vec>, @@ -20,7 +21,7 @@ impl Component for Rect { fn style(&self) -> RawComponentStyle { Style::default() .align(ComponentAlign::Column) - .padding(Padding::from_value(20.)) + .padding(Padding::from_value(EDITOR_PADDING)) } fn draw_self( @@ -29,6 +30,7 @@ impl Component for Rect { context: &ComponentContext, render_params: &RenderParams, style: &ComponentStyle, + _parent_style: &ComponentStyle, ) -> render_error::Result<()> { let mut path_builder = PathBuilder::new(); let x = render_params.x; diff --git a/generator/src/components/watermark.rs b/generator/src/components/watermark.rs index 8007413..7cb6f8a 100644 --- a/generator/src/components/watermark.rs +++ b/generator/src/components/watermark.rs @@ -21,6 +21,7 @@ impl Component for Watermark { context: &ComponentContext, render_params: &RenderParams, _style: &ComponentStyle, + _parent_style: &ComponentStyle, ) -> render_error::Result<()> { let params = &context.take_snapshot_params; diff --git a/generator/src/config.rs b/generator/src/config.rs index 4bc84dd..14fd02d 100644 --- a/generator/src/config.rs +++ b/generator/src/config.rs @@ -27,6 +27,8 @@ pub struct TakeSnapshotParams { pub breadcrumbs_separator: String, pub has_breadcrumbs: bool, pub start_line_number: Option, + pub highlight_start_line_number: Option, + pub highlight_end_line_number: Option, } impl FromObject for TakeSnapshotParams { diff --git a/generator/src/snapshot.rs b/generator/src/snapshot.rs index 1b608a6..8020296 100644 --- a/generator/src/snapshot.rs +++ b/generator/src/snapshot.rs @@ -8,6 +8,7 @@ use crate::components::code_block::CodeBlock; use crate::components::container::Container; use crate::components::editor::code::Code; 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::render_error; use crate::components::line_number::LineNumber; @@ -17,6 +18,7 @@ use crate::config::TakeSnapshotParams; // Scale the screenshot to 3 times its size const SCALE_FACTOR: f32 = 3.; +const LINE_HEIGHT: f32 = 20.; // The params is come from neovim instance pub fn take_snapshot(params: TakeSnapshotParams) -> render_error::Result { @@ -36,8 +38,17 @@ pub fn take_snapshot(params: TakeSnapshotParams) -> render_error::Result params.has_breadcrumbs, )), Box::new(CodeBlock::from_children(vec![ - Box::new(LineNumber::new(¶ms.code, params.start_line_number, 20.)), - Box::new(Code::new(params.code, 20., 15.)), + Box::new(HighlightCodeBlock::from_line_number( + params.highlight_start_line_number, + params.highlight_end_line_number, + LINE_HEIGHT, + )), + Box::new(LineNumber::new( + ¶ms.code, + params.start_line_number, + LINE_HEIGHT, + )), + Box::new(Code::new(params.code, LINE_HEIGHT, 15.)), ])), ], )), diff --git a/lua/codesnap/highlight.lua b/lua/codesnap/highlight.lua new file mode 100644 index 0000000..36a59c8 --- /dev/null +++ b/lua/codesnap/highlight.lua @@ -0,0 +1,54 @@ +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 = 20 + 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, string_utils.split(code, "\n")) + 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 the code needs to be highlighted", + 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", {}) + vim.api.nvim_buf_set_keymap(bufnr, "", "", ":q", {}) + vim.api.nvim_buf_set_keymap( + bufnr, + "v", + "", + ":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())", + { silent = true } + ) + vim.api.nvim_set_current_win(window_id) +end + +return highlight_module diff --git a/lua/codesnap/init.lua b/lua/codesnap/init.lua index 2a47c8e..559b129 100644 --- a/lua/codesnap/init.lua +++ b/lua/codesnap/init.lua @@ -2,23 +2,25 @@ local static = require("codesnap.static") local table_utils = require("codesnap.utils.table") local string_utils = require("codesnap.utils.string") local config_module = require("codesnap.config") +local highlight_module = require("codesnap.highlight") local main = { cwd = static.cwd, preview_switch = static.preview_switch, + highlight_mode_config = nil, } function main.setup(config) static.config = table_utils.merge(static.config, config == nil and {} or config) end -function main.copy_into_clipboard(extension) - require("generator").copy_into_clipboard(config_module.get_config(extension)) +function main.copy_into_clipboard_with_config(config) + require("generator").copy_into_clipboard(config) vim.cmd("delmarks <>") vim.notify("Save snapshot into clipboard successfully") end -function main.save_snapshot(extension) +function main.save_snapshot_with_config(config) if string_utils.is_str_empty(static.config.save_path) then 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", @@ -32,12 +34,32 @@ function main.save_snapshot(extension) error("The extension of save_path should be .png", 0) end - local config = config_module.get_config(extension) - require("generator").save_snapshot(config) vim.cmd("delmarks <>") ---@diagnostic disable-next-line: need-check-nil vim.notify("Save snapshot in " .. config.save_path .. " successfully") 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", main.highlight_mode_config.code) +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", main.highlight_mode_config.code) +end + return main diff --git a/lua/codesnap/utils/string.lua b/lua/codesnap/utils/string.lua index 00c6648..3398eb2 100644 --- a/lua/codesnap/utils/string.lua +++ b/lua/codesnap/utils/string.lua @@ -24,4 +24,14 @@ function string_util.convert_empty_to_nil(target) 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 diff --git a/lua/codesnap/utils/table.lua b/lua/codesnap/utils/table.lua index 732912a..bfb993a 100644 --- a/lua/codesnap/utils/table.lua +++ b/lua/codesnap/utils/table.lua @@ -33,7 +33,7 @@ end function table_utils.serialize_array(t) local result = list_utils.map(t, function(ele) - table_utils.serialize_json(ele) + table_utils.to_string(ele) end) return "[" .. result.concat(t, ",") .. "]" @@ -43,17 +43,17 @@ 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))) + table.insert(result, string.format("%s = %s", key, table_utils.to_string(value))) end return "{" .. table.concat(result, ",") .. "}" end function table_utils.serialize_string(value) - return '"' .. value .. '"' + return "[[" .. value .. "]]" end -function table_utils.serialize_json(t) +function table_utils.to_string(t) local complex_type_parser = { array = table_utils.serialize_array, table = table_utils.serialize_table, @@ -67,4 +67,14 @@ function table_utils.serialize_json(t) return parse(t) 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 diff --git a/lua/codesnap/utils/visual.lua b/lua/codesnap/utils/visual.lua index a586cbe..db7250e 100644 --- a/lua/codesnap/utils/visual.lua +++ b/lua/codesnap/utils/visual.lua @@ -18,6 +18,10 @@ function visual_utils.get_start_line_number() return vim.fn.line("'<") end +function visual_utils.get_end_line_number() + return vim.fn.line("'>") +end + function visual_utils.get_selected_text() local selected_text = vim.fn.getline("'<", "'>") diff --git a/plugin/codesnap.lua b/plugin/codesnap.lua index ab6ed18..845a9a8 100644 --- a/plugin/codesnap.lua +++ b/plugin/codesnap.lua @@ -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("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 = "%" } +)