diff view

This commit is contained in:
Dongdong Zhou 2022-05-04 19:17:08 +01:00
parent 8369ba29fc
commit 13929bafb6
10 changed files with 381 additions and 84 deletions

View File

@ -573,7 +573,7 @@ pub fn retrieve_file_head(
LapceUICommand::LoadBufferHead {
path,
content: Rope::from(resp.content),
id: resp.id,
version: resp.version,
},
Target::Widget(tab_id),
);
@ -2213,7 +2213,7 @@ fn buffer_diff(
Some(changes)
}
fn rope_diff(
pub fn rope_diff(
left_rope: Rope,
right_rope: Rope,
rev: u64,

View File

@ -639,7 +639,7 @@ pub enum LapceUICommand {
},
LoadBufferHead {
path: PathBuf,
id: String,
version: String,
content: Rope,
},
LoadBufferAndGoToPosition {

View File

@ -2431,7 +2431,7 @@ pub fn go_to_location(
Vec2::new(info.scroll_offset.0, info.scroll_offset.1);
doc.cursor_offset = info.cursor_offset;
}
doc.retrieve_file(self.proxy.clone(), vec![(editor_view_id, location)]);
doc.retrieve_file(vec![(editor_view_id, location)]);
self.open_docs.insert(path.clone(), Arc::new(doc));
self.open_files.insert(
path.clone(),
@ -2459,14 +2459,9 @@ pub fn go_to_location(
None => (doc.cursor_offset, Some(&doc.scroll_offset)),
};
if let Some(compare) = location.history.as_ref() {
// if !buffer.histories().contains_key(compare) {
// buffer.retrieve_file_head(
// *self.tab_id,
// self.proxy.clone(),
// ctx.get_external_handle(),
// );
// }
if let Some(version) = location.history.as_ref() {
let doc = self.open_docs.get_mut(&path).unwrap();
Arc::make_mut(doc).retrieve_history(version);
}
let editor = self.get_editor_or_new(
@ -2475,8 +2470,8 @@ pub fn go_to_location(
Some(location.path.clone()),
config,
);
if let Some(compare) = location.history.as_ref() {
editor.view = EditorView::Diff(compare.to_string());
if let Some(version) = location.history.as_ref() {
editor.view = EditorView::Diff(version.to_string());
}
editor.content = BufferContent::File(path.clone());
editor.compare = location.history.clone();
@ -2606,7 +2601,7 @@ pub fn new(
active: Arc::new(None),
active_tab: Arc::new(None),
register: Arc::new(Register::default()),
proxy: proxy.clone(),
proxy,
palette_preview_editor: Arc::new(palette_preview_editor),
show_code_actions: false,
current_code_actions: 0,
@ -2629,11 +2624,8 @@ pub fn new(
);
main_split_data.split_id = Arc::new(split_data.widget_id);
for (path, locations) in positions.into_iter() {
main_split_data
.open_docs
.get(&path)
.unwrap()
.retrieve_file(proxy.clone(), locations.clone());
Arc::make_mut(main_split_data.open_docs.get_mut(&path).unwrap())
.retrieve_file(locations.clone());
}
} else {
main_split_data.splits.insert(

View File

@ -3,7 +3,10 @@
collections::{HashMap, HashSet},
path::PathBuf,
rc::Rc,
sync::{atomic, Arc},
sync::{
atomic::{self},
Arc,
},
};
use druid::{
@ -26,18 +29,19 @@
word::WordCursor,
};
use lapce_rpc::{
buffer::{BufferId, NewBufferResponse},
buffer::{BufferHeadResponse, BufferId, NewBufferResponse},
style::{LineStyle, LineStyles, Style},
};
use lsp_types::CodeActionResponse;
use xi_rope::{spans::Spans, RopeDelta};
use xi_rope::{spans::Spans, Rope, RopeDelta};
use crate::{
buffer::{BufferContent, LocalBufferKind},
buffer::{rope_diff, BufferContent, DiffLines, DiffResult, LocalBufferKind},
command::{LapceUICommand, LAPCE_UI_COMMAND},
config::{Config, LapceTheme},
editor::EditorLocationNew,
find::{Find, FindProgress},
history::DocumentHisotry,
proxy::LapceProxy,
};
@ -53,15 +57,16 @@ fn put_string(&mut self, s: impl AsRef<str>) {
}
}
struct TextLayoutCache {
#[derive(Clone, Default)]
pub struct TextLayoutCache {
font_size: usize,
font_family: FontFamily,
tab_wdith: usize,
layouts: HashMap<usize, Arc<PietTextLayout>>,
pub layouts: HashMap<usize, Arc<PietTextLayout>>,
}
impl TextLayoutCache {
fn new() -> Self {
pub fn new() -> Self {
Self {
font_size: 0,
font_family: FontFamily::SYSTEM_UI,
@ -74,7 +79,7 @@ fn clear(&mut self) {
self.layouts.clear();
}
fn check_attributes(
pub fn check_attributes(
&mut self,
font_size: usize,
font_family: FontFamily,
@ -95,7 +100,7 @@ fn check_attributes(
#[derive(Clone)]
pub struct Document {
id: BufferId,
tab_id: WidgetId,
pub tab_id: WidgetId,
buffer: Buffer,
content: BufferContent,
syntax: Option<Syntax>,
@ -104,13 +109,14 @@ pub struct Document {
text_layouts: Rc<RefCell<TextLayoutCache>>,
load_started: Rc<RefCell<bool>>,
loaded: bool,
histories: im::HashMap<String, DocumentHisotry>,
pub cursor_offset: usize,
pub scroll_offset: Vec2,
pub code_actions: im::HashMap<usize, CodeActionResponse>,
pub find: Rc<RefCell<Find>>,
find_progress: Rc<RefCell<FindProgress>>,
event_sink: ExtEventSink,
proxy: Arc<LapceProxy>,
pub event_sink: ExtEventSink,
pub proxy: Arc<LapceProxy>,
}
impl Document {
@ -130,6 +136,7 @@ pub fn new(
text_layouts: Rc::new(RefCell::new(TextLayoutCache::new())),
semantic_styles: None,
load_started: Rc::new(RefCell::new(false)),
histories: im::HashMap::new(),
loaded: false,
cursor_offset: 0,
scroll_offset: Vec2::ZERO,
@ -165,11 +172,7 @@ pub fn load_content(&mut self, content: &str) {
self.on_update(None);
}
pub fn retrieve_file(
&self,
proxy: Arc<LapceProxy>,
locations: Vec<(WidgetId, EditorLocationNew)>,
) {
pub fn retrieve_file(&mut self, locations: Vec<(WidgetId, EditorLocationNew)>) {
if self.loaded || *self.load_started.borrow() {
return;
}
@ -180,6 +183,7 @@ pub fn retrieve_file(
let tab_id = self.tab_id;
let path = path.clone();
let event_sink = self.event_sink.clone();
let proxy = self.proxy.clone();
std::thread::spawn(move || {
proxy.new_buffer(
id,
@ -204,6 +208,58 @@ pub fn retrieve_file(
)
});
}
self.retrieve_history("head");
}
pub fn retrieve_history(&mut self, version: &str) {
if self.histories.contains_key(version) {
return;
}
let history = DocumentHisotry::new(version.to_string());
history.retrieve(self);
self.histories.insert(version.to_string(), history);
}
pub fn load_history(&mut self, version: &str, content: Rope) {
let mut history = DocumentHisotry::new(version.to_string());
history.load_content(content, self);
self.histories.insert(version.to_string(), history);
}
pub fn get_history(&self, version: &str) -> Option<&DocumentHisotry> {
self.histories.get(version)
}
fn trigger_head_change(&self) {
if let Some(head) = self.histories.get("head") {
head.trigger_update_change(self);
}
}
pub fn update_history_changes(
&mut self,
rev: u64,
version: &str,
changes: Arc<Vec<DiffLines>>,
) {
if rev != self.rev() {
return;
}
if let Some(history) = self.histories.get_mut(version) {
history.update_changes(changes);
}
}
pub fn update_history_styles(
&mut self,
version: &str,
styles: Arc<Spans<Style>>,
) {
if let Some(history) = self.histories.get_mut(version) {
history.update_styles(styles);
}
}
fn on_update(&mut self, delta: Option<&RopeDelta>) {
@ -211,6 +267,7 @@ fn on_update(&mut self, delta: Option<&RopeDelta>) {
*self.find_progress.borrow_mut() = FindProgress::Started;
self.clear_style_cache();
self.trigger_syntax_change(delta);
self.trigger_head_change();
self.notify_special();
}

View File

@ -1 +1,238 @@
pub struct DocumentHisotry {}
use std::{
cell::RefCell,
rc::Rc,
sync::{atomic, Arc},
};
use druid::{
piet::{PietText, PietTextLayout, Text, TextAttribute, TextLayoutBuilder},
Target,
};
use lapce_core::{buffer::Buffer, style::line_styles, syntax::Syntax};
use lapce_rpc::{
buffer::BufferHeadResponse,
style::{LineStyle, LineStyles, Style},
};
use xi_rope::{spans::Spans, Rope};
use crate::{
buffer::{rope_diff, BufferContent, DiffLines},
command::{LapceUICommand, LAPCE_UI_COMMAND},
config::{Config, LapceTheme},
document::{Document, TextLayoutCache},
};
#[derive(Clone)]
pub struct DocumentHisotry {
version: String,
buffer: Buffer,
styles: Arc<Spans<Style>>,
line_styles: Rc<RefCell<LineStyles>>,
changes: Arc<Vec<DiffLines>>,
text_layouts: Rc<RefCell<TextLayoutCache>>,
}
impl DocumentHisotry {
pub fn new(version: String) -> Self {
Self {
version,
buffer: Buffer::new(""),
styles: Arc::new(Spans::default()),
line_styles: Rc::new(RefCell::new(LineStyles::new())),
text_layouts: Rc::new(RefCell::new(TextLayoutCache::new())),
changes: Arc::new(Vec::new()),
}
}
pub fn load_content(&mut self, content: Rope, doc: &Document) {
self.buffer.load_content(&content.slice_to_cow(..));
self.trigger_update_change(doc);
self.retrieve_history_styles(doc);
}
pub fn get_text_layout(
&self,
text: &mut PietText,
line: usize,
config: &Config,
) -> Arc<PietTextLayout> {
self.text_layouts.borrow_mut().check_attributes(
config.editor.font_size,
config.editor.font_family(),
config.editor.tab_width,
);
if self.text_layouts.borrow().layouts.get(&line).is_none() {
self.text_layouts
.borrow_mut()
.layouts
.insert(line, Arc::new(self.new_text_layout(text, line, config)));
}
self.text_layouts
.borrow()
.layouts
.get(&line)
.cloned()
.unwrap()
}
fn new_text_layout(
&self,
text: &mut PietText,
line: usize,
config: &Config,
) -> PietTextLayout {
let line_content = self.buffer.line_content(line);
let font_family = config.editor.font_family();
let font_size = config.editor.font_size;
let tab_width =
config.tab_width(text, config.editor.font_family(), font_size);
let mut layout_builder = text
.new_text_layout(line_content.to_string())
.font(font_family, font_size as f64)
.text_color(
config
.get_color_unchecked(LapceTheme::EDITOR_FOREGROUND)
.clone(),
)
.set_tab_width(tab_width);
let styles = self.line_style(line);
for line_style in styles.iter() {
if let Some(fg_color) = line_style.style.fg_color.as_ref() {
if let Some(fg_color) = config.get_style_color(fg_color) {
layout_builder = layout_builder.range_attribute(
line_style.start..line_style.end,
TextAttribute::TextColor(fg_color.clone()),
);
}
}
}
layout_builder.build().unwrap()
}
fn line_style(&self, line: usize) -> Arc<Vec<LineStyle>> {
if self.line_styles.borrow().get(&line).is_none() {
let line_styles = line_styles(self.buffer.text(), line, &self.styles);
self.line_styles
.borrow_mut()
.insert(line, Arc::new(line_styles));
}
self.line_styles.borrow().get(&line).cloned().unwrap()
}
pub fn retrieve(&self, doc: &Document) {
if let BufferContent::File(path) = &doc.content() {
let id = doc.id();
let tab_id = doc.tab_id;
let path = path.clone();
let proxy = doc.proxy.clone();
let event_sink = doc.event_sink.clone();
std::thread::spawn(move || {
proxy.get_buffer_head(
id,
path.clone(),
Box::new(move |result| {
if let Ok(res) = result {
if let Ok(resp) =
serde_json::from_value::<BufferHeadResponse>(res)
{
let _ = event_sink.submit_command(
LAPCE_UI_COMMAND,
LapceUICommand::LoadBufferHead {
path,
content: Rope::from(resp.content),
version: resp.version,
},
Target::Widget(tab_id),
);
}
}
}),
)
});
}
}
pub fn trigger_update_change(&self, doc: &Document) {
if let BufferContent::File(path) = &doc.content() {
let id = doc.id();
let rev = doc.rev();
let atomic_rev = doc.buffer().atomic_rev();
let path = path.clone();
let left_rope = self.buffer.text().clone();
let right_rope = doc.buffer().text().clone();
let event_sink = doc.event_sink.clone();
let tab_id = doc.tab_id;
rayon::spawn(move || {
if atomic_rev.load(atomic::Ordering::Acquire) != rev {
return;
}
let changes =
rope_diff(left_rope, right_rope, rev, atomic_rev.clone());
if changes.is_none() {
return;
}
let changes = changes.unwrap();
if atomic_rev.load(atomic::Ordering::Acquire) != rev {
return;
}
let _ = event_sink.submit_command(
LAPCE_UI_COMMAND,
LapceUICommand::UpdateHistoryChanges {
id,
path,
rev,
history: "head".to_string(),
changes: Arc::new(changes),
},
Target::Widget(tab_id),
);
});
}
}
pub fn changes(&self) -> &[DiffLines] {
&self.changes
}
pub fn update_changes(&mut self, changes: Arc<Vec<DiffLines>>) {
self.changes = changes;
}
pub fn update_styles(&mut self, styles: Arc<Spans<Style>>) {
self.styles = styles;
self.line_styles.borrow_mut().clear();
}
fn retrieve_history_styles(&self, doc: &Document) {
if let BufferContent::File(path) = &doc.content() {
let id = doc.id();
let path = path.clone();
let tab_id = doc.tab_id;
let version = self.version.to_string();
let event_sink = doc.event_sink.clone();
let content = self.buffer.text().clone();
rayon::spawn(move || {
if let Some(syntax) =
Syntax::init(&path).map(|s| s.parse(0, content, None))
{
if let Some(styles) = syntax.styles {
let _ = event_sink.submit_command(
LAPCE_UI_COMMAND,
LapceUICommand::UpdateHistoryStyle {
id,
path,
history: version,
highlights: styles,
},
Target::Widget(tab_id),
);
}
}
});
}
}
}

View File

@ -404,7 +404,7 @@ fn handle_request(&self, id: RequestId, rpc: ProxyRequest) {
let result = file_get_head(&workspace, &path);
if let Ok((_blob_id, content)) = result {
let resp = BufferHeadResponse {
id: "head".to_string(),
version: "head".to_string(),
content,
};
let _ = self.sender.send(json!({

View File

@ -19,6 +19,6 @@ pub struct NewBufferResponse {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BufferHeadResponse {
pub id: String,
pub version: String,
pub content: String,
}

View File

@ -13,6 +13,7 @@
use lapce_core::command::FocusCommand;
use lapce_core::mode::{Mode, VisualMode};
use lapce_data::command::CommandKind;
use lapce_data::data::EditorView;
use lapce_data::keypress::KeyPressFocus;
use lapce_data::{
buffer::{matching_pair_direction, BufferContent, DiffLines, LocalBufferKind},
@ -479,12 +480,12 @@ fn paint_content(
if !data.editor.content.is_input() && data.editor.code_lens {
Self::paint_code_lens_content(data, ctx, is_focused);
} else if let Some(compare) = data.editor.compare.as_ref() {
if let Some(changes) = data.buffer.history_changes.get(compare) {
} else if let EditorView::Diff(version) = &data.editor.view {
if let Some(history) = data.doc.get_history(version) {
let cursor_line =
data.buffer.line_of_offset(data.editor.cursor.offset());
let mut line = 0;
for change in changes.iter() {
for change in history.changes().iter() {
match change {
DiffLines::Left(range) => {
let len = range.len();
@ -509,24 +510,19 @@ fn paint_content(
continue;
}
let actual_line = l - (line - len) + range.start;
if let Some(text_layout) =
data.buffer.history_text_layout(
ctx,
compare,
actual_line,
None,
[rect.x0, rect.x1],
&data.config,
)
{
ctx.draw_text(
&text_layout,
Point::new(
0.0,
line_height * l as f64 + y_shift,
),
);
}
let text_layout = history.get_text_layout(
ctx.text(),
actual_line,
&data.config,
);
ctx.draw_text(
&text_layout,
Point::new(
0.0,
line_height * l as f64 + y_shift,
),
);
if l > end_line {
break;
}
@ -600,13 +596,10 @@ fn paint_content(
char_width,
line_height,
);
let text_layout = data.buffer.new_text_layout(
ctx,
let text_layout = data.doc.get_text_layout(
ctx.text(),
rope_line,
&data.buffer.line_content(rope_line),
None,
font_size,
[rect.x0, rect.x1],
&data.config,
);
ctx.draw_text(
@ -662,13 +655,10 @@ fn paint_content(
char_width,
line_height,
);
let text_layout = data.buffer.new_text_layout(
ctx,
let text_layout = data.doc.get_text_layout(
ctx.text(),
rope_line,
&data.buffer.line_content(rope_line),
None,
font_size,
[rect.x0, rect.x1],
&data.config,
);
ctx.draw_text(

View File

@ -7,7 +7,7 @@
use lapce_data::{
buffer::DiffLines,
config::LapceTheme,
data::LapceTabData,
data::{EditorView, LapceTabData},
editor::{LapceEditorBufferData, Syntax},
};
@ -108,25 +108,28 @@ fn paint_gutter_inline_diff(
&self,
data: &LapceEditorBufferData,
ctx: &mut PaintCtx,
compare: &str,
version: &str,
) {
if data.buffer.history_changes.get(compare).is_none() {
if data.doc.get_history(version).is_none() {
return;
}
let history = data.doc.get_history(version).unwrap();
let self_size = ctx.size();
let rect = self_size.to_rect();
let changes = data.buffer.history_changes.get(compare).unwrap();
let line_height = data.config.editor.line_height as f64;
let scroll_offset = data.editor.scroll_offset;
let start_line = (scroll_offset.y / line_height).floor() as usize;
let end_line =
(scroll_offset.y + rect.height() / line_height).ceil() as usize;
let current_line = data.editor.cursor.current_line(data.buffer.data());
let last_line = data.buffer.last_line();
let current_line = data
.doc
.buffer()
.line_of_offset(data.editor.new_cursor.offset());
let last_line = data.doc.buffer().last_line();
let width = data.config.editor_char_width(ctx.text());
let mut line = 0;
for change in changes.iter() {
for change in history.changes().iter() {
match change {
DiffLines::Left(r) => {
let len = r.len();
@ -467,8 +470,8 @@ fn paint_gutter(&self, data: &LapceEditorBufferData, ctx: &mut PaintCtx) {
ctx.with_save(|ctx| {
let clip_rect = rect;
ctx.clip(clip_rect);
if let Some(compare) = data.editor.compare.as_ref() {
self.paint_gutter_inline_diff(data, ctx, compare);
if let EditorView::Diff(version) = &data.editor.view {
self.paint_gutter_inline_diff(data, ctx, version);
return;
}
if data.editor.code_lens {
@ -479,7 +482,7 @@ fn paint_gutter(&self, data: &LapceEditorBufferData, ctx: &mut PaintCtx) {
let scroll_offset = data.editor.scroll_offset;
let start_line = (scroll_offset.y / line_height).floor() as usize;
let num_lines = (ctx.size().height / line_height).floor() as usize;
let last_line = data.buffer.last_line();
let last_line = data.doc.buffer().last_line();
let current_line = data
.doc
.buffer()
@ -536,13 +539,13 @@ fn paint_gutter(&self, data: &LapceEditorBufferData, ctx: &mut PaintCtx) {
ctx.draw_text(&text_layout, Point::new(x, y));
}
if let Some(changes) = data.buffer.history_changes.get("head") {
if let Some(history) = data.doc.get_history("head") {
let end_line =
(scroll_offset.y + rect.height() / line_height).ceil() as usize;
let mut line = 0;
let mut last_change = None;
for change in changes.iter() {
for change in history.changes().iter() {
let len = match change {
DiffLines::Left(_range) => 0,
DiffLines::Skip(_left, right) => right.len(),

View File

@ -433,11 +433,19 @@ fn event(
matches.clone();
}
}
LapceUICommand::LoadBufferHead { path, id, content } => {
LapceUICommand::LoadBufferHead {
path,
version,
content,
} => {
let buffer =
data.main_split.open_files.get_mut(path).unwrap();
let buffer = Arc::make_mut(buffer);
buffer.load_history(id, content.clone());
buffer.load_history(version, content.clone());
let doc = data.main_split.open_docs.get_mut(path).unwrap();
let doc = Arc::make_mut(doc);
doc.load_history(version, content.clone());
ctx.set_handled();
}
LapceUICommand::UpdateTerminalTitle(term_id, title) => {
@ -974,6 +982,12 @@ fn event(
history,
changes.clone(),
);
let doc = data.main_split.open_docs.get_mut(path).unwrap();
Arc::make_mut(doc).update_history_changes(
*rev,
history,
changes.clone(),
);
}
LapceUICommand::UpdateHistoryStyle {
path,
@ -991,6 +1005,10 @@ fn event(
.history_line_styles
.borrow_mut()
.insert(history.to_string(), HashMap::new());
let doc = data.main_split.open_docs.get_mut(path).unwrap();
Arc::make_mut(doc)
.update_history_styles(history, highlights.to_owned());
}
LapceUICommand::UpdatePickerPwd(path) => {
Arc::make_mut(&mut data.picker).pwd = path.clone();