mirror of https://github.com/lapce/lapce.git
add support for scratch buffer
This commit is contained in:
parent
c404f7194f
commit
3b772bfe2d
|
@ -234,11 +234,17 @@ pub fn init_content(&mut self, content: Rope) {
|
|||
self.set_pristine();
|
||||
}
|
||||
|
||||
pub fn reload(&mut self, content: Rope) -> (RopeDelta, InvalLines) {
|
||||
pub fn reload(
|
||||
&mut self,
|
||||
content: Rope,
|
||||
set_prisitine: bool,
|
||||
) -> (RopeDelta, InvalLines) {
|
||||
let delta = LineHashDiff::compute_delta(&self.text, &content);
|
||||
self.this_edit_type = EditType::Other;
|
||||
let (delta, inval_lines) = self.add_delta(delta);
|
||||
self.set_pristine();
|
||||
if set_prisitine {
|
||||
self.set_pristine();
|
||||
}
|
||||
(delta, inval_lines)
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
use crate::alert::AlertContentData;
|
||||
use crate::data::LapceWorkspace;
|
||||
use crate::document::BufferContent;
|
||||
use crate::rich_text::RichText;
|
||||
use crate::{
|
||||
data::{EditorTabChild, SplitContent},
|
||||
|
@ -240,6 +241,10 @@ pub enum LapceWorkbenchCommand {
|
|||
#[strum(serialize = "new_window")]
|
||||
NewWindow,
|
||||
|
||||
#[strum(message = "New File")]
|
||||
#[strum(serialize = "new_file")]
|
||||
NewFile,
|
||||
|
||||
#[strum(serialize = "connect_ssh_host")]
|
||||
#[strum(message = "Connect to SSH Host")]
|
||||
ConnectSshHost,
|
||||
|
@ -469,6 +474,8 @@ pub enum LapceUICommand {
|
|||
Scroll((f64, f64)),
|
||||
ScrollTo((f64, f64)),
|
||||
ForceScrollTo(f64, f64),
|
||||
SaveAs(BufferContent, PathBuf, WidgetId, bool),
|
||||
SaveAsSuccess(BufferContent, u64, PathBuf, WidgetId, bool),
|
||||
HomeDir(PathBuf),
|
||||
FileChange(notify::Event),
|
||||
ProxyUpdateStatus(ProxyStatus),
|
||||
|
|
|
@ -10,8 +10,7 @@
|
|||
use anyhow::Result;
|
||||
use crossbeam_channel::{unbounded, Receiver, Sender};
|
||||
use druid::{
|
||||
piet::{PietText, PietTextLayout, Text, TextLayout, TextLayoutBuilder},
|
||||
theme, Command, Data, Env, EventCtx, ExtEventSink, FontFamily, Lens, Point,
|
||||
piet::PietText, theme, Command, Data, Env, EventCtx, ExtEventSink, Lens, Point,
|
||||
Rect, Size, Target, Vec2, WidgetId, WindowId,
|
||||
};
|
||||
|
||||
|
@ -25,11 +24,10 @@
|
|||
selection::Selection,
|
||||
};
|
||||
use lapce_rpc::{
|
||||
plugin::PluginDescription, source_control::FileDiff, terminal::TermId,
|
||||
};
|
||||
use lsp_types::{
|
||||
CodeActionOrCommand, Diagnostic, Position, ProgressToken, TextEdit,
|
||||
buffer::BufferId, plugin::PluginDescription, source_control::FileDiff,
|
||||
terminal::TermId,
|
||||
};
|
||||
use lsp_types::{Diagnostic, Position, ProgressToken, TextEdit};
|
||||
use notify::Watcher;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
@ -707,6 +705,9 @@ pub fn editor_view_content(
|
|||
BufferContent::File(path) => {
|
||||
self.main_split.open_docs.get(path).unwrap().clone()
|
||||
}
|
||||
BufferContent::Scratch(id) => {
|
||||
self.main_split.scratch_docs.get(id).unwrap().clone()
|
||||
}
|
||||
BufferContent::Local(kind) => {
|
||||
self.main_split.local_docs.get(kind).unwrap().clone()
|
||||
}
|
||||
|
@ -742,39 +743,12 @@ pub fn code_action_size(&self, text: &mut PietText, _env: &Env) -> Size {
|
|||
BufferContent::File(path) => {
|
||||
let doc = self.main_split.open_docs.get(path).unwrap();
|
||||
let offset = editor.new_cursor.offset();
|
||||
let prev_offset = doc.buffer().prev_code_boundary(offset);
|
||||
let empty_vec = Vec::new();
|
||||
let code_actions =
|
||||
doc.code_actions.get(&prev_offset).unwrap_or(&empty_vec);
|
||||
|
||||
let action_text_layouts: Vec<PietTextLayout> = code_actions
|
||||
.iter()
|
||||
.map(|code_action| {
|
||||
let title = match code_action {
|
||||
CodeActionOrCommand::Command(cmd) => {
|
||||
cmd.title.to_string()
|
||||
}
|
||||
CodeActionOrCommand::CodeAction(action) => {
|
||||
action.title.to_string()
|
||||
}
|
||||
};
|
||||
|
||||
text.new_text_layout(title)
|
||||
.font(FontFamily::SYSTEM_UI, 14.0)
|
||||
.build()
|
||||
.unwrap()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut width = 0.0;
|
||||
for text_layout in &action_text_layouts {
|
||||
let line_width = text_layout.size().width + 10.0;
|
||||
if line_width > width {
|
||||
width = line_width;
|
||||
}
|
||||
}
|
||||
let line_height = self.config.editor.line_height as f64;
|
||||
Size::new(width, code_actions.len() as f64 * line_height)
|
||||
doc.code_action_size(text, offset, &self.config)
|
||||
}
|
||||
BufferContent::Scratch(id) => {
|
||||
let doc = self.main_split.scratch_docs.get(id).unwrap();
|
||||
let offset = editor.new_cursor.offset();
|
||||
doc.code_action_size(text, offset, &self.config)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -810,6 +784,11 @@ pub fn update_from_editor_buffer_data(
|
|||
.open_docs
|
||||
.insert(path.clone(), editor_buffer_data.doc);
|
||||
}
|
||||
BufferContent::Scratch(id) => {
|
||||
self.main_split
|
||||
.scratch_docs
|
||||
.insert(*id, editor_buffer_data.doc);
|
||||
}
|
||||
BufferContent::Local(kind) => {
|
||||
self.main_split
|
||||
.local_docs
|
||||
|
@ -846,8 +825,8 @@ pub fn code_action_origin(
|
|||
*editor.window_origin.borrow()
|
||||
- self.window_origin.borrow().to_vec2()
|
||||
}
|
||||
BufferContent::File(path) => {
|
||||
let doc = self.main_split.open_docs.get(path).unwrap();
|
||||
BufferContent::File(_) | BufferContent::Scratch(_) => {
|
||||
let doc = self.main_split.editor_doc(editor.view_id);
|
||||
let offset = editor.new_cursor.offset();
|
||||
let (line, col) = doc.buffer().offset_to_line_col(offset);
|
||||
let width = config.editor_char_width(text);
|
||||
|
@ -884,8 +863,8 @@ pub fn completion_origin(
|
|||
*editor.window_origin.borrow()
|
||||
- self.window_origin.borrow().to_vec2()
|
||||
}
|
||||
BufferContent::File(path) => {
|
||||
let doc = self.main_split.open_docs.get(path).unwrap();
|
||||
BufferContent::File(_) | BufferContent::Scratch(_) => {
|
||||
let doc = self.main_split.editor_doc(editor.view_id);
|
||||
let offset = self.completion.offset;
|
||||
let (line, col) = doc.buffer().offset_to_line_col(offset);
|
||||
let width = config.editor_char_width(text);
|
||||
|
@ -940,8 +919,8 @@ pub fn hover_origin(
|
|||
*editor.window_origin.borrow()
|
||||
- self.window_origin.borrow().to_vec2()
|
||||
}
|
||||
BufferContent::File(path) => {
|
||||
let doc = self.main_split.open_docs.get(path).unwrap();
|
||||
BufferContent::File(_) | BufferContent::Scratch(_) => {
|
||||
let doc = self.main_split.editor_doc(editor.view_id);
|
||||
let offset = self.hover.offset;
|
||||
let (line, col) = doc.buffer().offset_to_line_col(offset);
|
||||
let point = doc.point_of_line_col(
|
||||
|
@ -1117,6 +1096,9 @@ pub fn run_workbench_command(
|
|||
Target::Widget(self.palette.widget_id),
|
||||
));
|
||||
}
|
||||
LapceWorkbenchCommand::NewFile => {
|
||||
self.main_split.new_file(ctx, &self.config);
|
||||
}
|
||||
LapceWorkbenchCommand::OpenLogFile => {
|
||||
if let Some(path) = Config::log_file() {
|
||||
let editor_view_id = self.main_split.active.clone();
|
||||
|
@ -1391,7 +1373,7 @@ pub fn run_workbench_command(
|
|||
return;
|
||||
}
|
||||
self.proxy.git_commit(message, diffs);
|
||||
Arc::make_mut(doc).reload(Rope::from(""));
|
||||
Arc::make_mut(doc).reload(Rope::from(""), true);
|
||||
let editor = self
|
||||
.main_split
|
||||
.editors
|
||||
|
@ -1635,7 +1617,7 @@ pub fn set_picker_pwd(&mut self, pwd: PathBuf) {
|
|||
.get_mut(&LocalBufferKind::FilePicker)
|
||||
.unwrap();
|
||||
let doc = Arc::make_mut(doc);
|
||||
doc.reload(Rope::from(s));
|
||||
doc.reload(Rope::from(s), true);
|
||||
let editor = self
|
||||
.main_split
|
||||
.editors
|
||||
|
@ -1860,6 +1842,7 @@ pub struct LapceMainSplitData {
|
|||
pub open_docs: im::HashMap<PathBuf, Arc<Document>>,
|
||||
pub local_docs: im::HashMap<LocalBufferKind, Arc<Document>>,
|
||||
pub value_docs: im::HashMap<String, Arc<Document>>,
|
||||
pub scratch_docs: im::HashMap<BufferId, Arc<Document>>,
|
||||
pub register: Arc<Register>,
|
||||
pub proxy: Arc<LapceProxy>,
|
||||
pub palette_preview_editor: Arc<WidgetId>,
|
||||
|
@ -1884,6 +1867,7 @@ pub fn editor_doc(&self, editor_view_id: WidgetId) -> Arc<Document> {
|
|||
BufferContent::File(path) => self.open_docs.get(path).unwrap().clone(),
|
||||
BufferContent::Local(kind) => self.local_docs.get(kind).unwrap().clone(),
|
||||
BufferContent::Value(name) => self.value_docs.get(name).unwrap().clone(),
|
||||
BufferContent::Scratch(id) => self.scratch_docs.get(id).unwrap().clone(),
|
||||
};
|
||||
doc
|
||||
}
|
||||
|
@ -2082,6 +2066,7 @@ fn get_editor_or_new(
|
|||
ctx: &mut EventCtx,
|
||||
editor_view_id: Option<WidgetId>,
|
||||
path: Option<PathBuf>,
|
||||
scratch: bool,
|
||||
config: &Config,
|
||||
) -> &mut LapceEditorData {
|
||||
match editor_view_id {
|
||||
|
@ -2093,7 +2078,7 @@ fn get_editor_or_new(
|
|||
match &editor_tab.children[editor_tab.active] {
|
||||
EditorTabChild::Editor(id, _) => {
|
||||
if config.editor.show_tab {
|
||||
if let Some(path) = path {
|
||||
if path.is_some() || scratch {
|
||||
let mut editor_size = Size::ZERO;
|
||||
for (i, child) in
|
||||
editor_tab.children.iter().enumerate()
|
||||
|
@ -2107,24 +2092,26 @@ fn get_editor_or_new(
|
|||
if current_size.height > 0.0 {
|
||||
editor_size = current_size;
|
||||
}
|
||||
if editor.content
|
||||
== BufferContent::File(
|
||||
path.clone(),
|
||||
)
|
||||
{
|
||||
editor_tab.active = i;
|
||||
ctx.submit_command(
|
||||
if let Some(path) = path.as_ref() {
|
||||
if editor.content
|
||||
== BufferContent::File(
|
||||
path.clone(),
|
||||
)
|
||||
{
|
||||
editor_tab.active = i;
|
||||
ctx.submit_command(
|
||||
Command::new(
|
||||
LAPCE_UI_COMMAND,
|
||||
LapceUICommand::Focus,
|
||||
Target::Widget(*id),
|
||||
),
|
||||
);
|
||||
return Arc::make_mut(
|
||||
self.editors
|
||||
.get_mut(id)
|
||||
.unwrap(),
|
||||
);
|
||||
return Arc::make_mut(
|
||||
self.editors
|
||||
.get_mut(id)
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2245,7 +2232,8 @@ pub fn jump_to_position(
|
|||
position: Position,
|
||||
config: &Config,
|
||||
) {
|
||||
let editor = self.get_editor_or_new(ctx, editor_view_id, None, config);
|
||||
let editor =
|
||||
self.get_editor_or_new(ctx, editor_view_id, None, false, config);
|
||||
if let BufferContent::File(path) = &editor.content {
|
||||
let location = EditorLocationNew {
|
||||
path: path.clone(),
|
||||
|
@ -2269,6 +2257,7 @@ pub fn jump_to_location(
|
|||
ctx,
|
||||
editor_view_id,
|
||||
Some(location.path.clone()),
|
||||
false,
|
||||
config,
|
||||
)
|
||||
.view_id;
|
||||
|
@ -2277,6 +2266,7 @@ pub fn jump_to_location(
|
|||
ctx,
|
||||
Some(editor_view_id),
|
||||
Some(location.path.clone()),
|
||||
false,
|
||||
config,
|
||||
);
|
||||
editor.save_jump_location(&doc);
|
||||
|
@ -2284,6 +2274,24 @@ pub fn jump_to_location(
|
|||
editor_view_id
|
||||
}
|
||||
|
||||
pub fn new_file(&mut self, ctx: &mut EventCtx, config: &Config) {
|
||||
let tab_id = *self.tab_id;
|
||||
let proxy = self.proxy.clone();
|
||||
let buffer_id = BufferId::next();
|
||||
let content = BufferContent::Scratch(buffer_id);
|
||||
let doc =
|
||||
Document::new(content.clone(), tab_id, ctx.get_external_handle(), proxy);
|
||||
self.scratch_docs.insert(buffer_id, Arc::new(doc));
|
||||
|
||||
let editor = self.get_editor_or_new(ctx, None, None, true, config);
|
||||
editor.content = content;
|
||||
editor.new_cursor = if config.lapce.modal {
|
||||
Cursor::new(CursorMode::Normal(0), None, None)
|
||||
} else {
|
||||
Cursor::new(CursorMode::Insert(Selection::caret(0)), None, None)
|
||||
};
|
||||
}
|
||||
|
||||
pub fn go_to_location(
|
||||
&mut self,
|
||||
ctx: &mut EventCtx,
|
||||
|
@ -2296,6 +2304,7 @@ pub fn go_to_location(
|
|||
ctx,
|
||||
editor_view_id,
|
||||
Some(location.path.clone()),
|
||||
false,
|
||||
config,
|
||||
)
|
||||
.view_id;
|
||||
|
@ -2304,6 +2313,7 @@ pub fn go_to_location(
|
|||
BufferContent::File(path) => path != &location.path,
|
||||
BufferContent::Local(_) => true,
|
||||
BufferContent::Value(_) => true,
|
||||
BufferContent::Scratch(_) => true,
|
||||
};
|
||||
if new_buffer {
|
||||
self.db.save_doc_position(&self.workspace, &doc);
|
||||
|
@ -2356,6 +2366,7 @@ pub fn go_to_location(
|
|||
ctx,
|
||||
Some(editor_view_id),
|
||||
Some(location.path.clone()),
|
||||
false,
|
||||
config,
|
||||
);
|
||||
if let Some(version) = location.history.as_ref() {
|
||||
|
@ -2401,7 +2412,7 @@ pub fn jump_to_line(
|
|||
config: &Config,
|
||||
) {
|
||||
let editor_view_id = self
|
||||
.get_editor_or_new(ctx, editor_view_id, None, config)
|
||||
.get_editor_or_new(ctx, editor_view_id, None, false, config)
|
||||
.view_id;
|
||||
let doc = self.editor_doc(editor_view_id);
|
||||
let offset = doc.buffer().first_non_blank_character_on_line(if line > 0 {
|
||||
|
@ -2444,6 +2455,7 @@ pub fn new(
|
|||
)),
|
||||
);
|
||||
let value_docs = im::HashMap::new();
|
||||
let scratch_docs = im::HashMap::new();
|
||||
|
||||
let editor = LapceEditorData::new(
|
||||
Some(palette_preview_editor),
|
||||
|
@ -2462,6 +2474,7 @@ pub fn new(
|
|||
open_docs,
|
||||
local_docs,
|
||||
value_docs,
|
||||
scratch_docs,
|
||||
active: Arc::new(None),
|
||||
active_tab: Arc::new(None),
|
||||
register: Arc::new(Register::default()),
|
||||
|
@ -2590,6 +2603,86 @@ pub fn update_split_layout_rect(&self, split_id: WidgetId, rect: Rect) {
|
|||
*split.layout_rect.borrow_mut() = rect;
|
||||
}
|
||||
|
||||
pub fn save_as_success(
|
||||
&mut self,
|
||||
ctx: &mut EventCtx,
|
||||
content: &BufferContent,
|
||||
rev: u64,
|
||||
path: &Path,
|
||||
view_id: WidgetId,
|
||||
exit: bool,
|
||||
) {
|
||||
match content {
|
||||
BufferContent::Scratch(id) => {
|
||||
let doc = self.scratch_docs.get(id).unwrap();
|
||||
if doc.rev() == rev {
|
||||
let new_content = BufferContent::File(path.to_path_buf());
|
||||
for (_, editor) in self.editors.iter_mut() {
|
||||
if editor.content == BufferContent::Scratch(*id) {
|
||||
Arc::make_mut(editor).content = new_content.clone();
|
||||
}
|
||||
}
|
||||
|
||||
let mut doc = self.scratch_docs.remove(id).unwrap();
|
||||
let mut_doc = Arc::make_mut(&mut doc);
|
||||
mut_doc.buffer_mut().set_pristine();
|
||||
mut_doc.set_content(new_content);
|
||||
self.open_docs.insert(path.to_path_buf(), doc);
|
||||
if exit {
|
||||
ctx.submit_command(Command::new(
|
||||
LAPCE_COMMAND,
|
||||
LapceCommand {
|
||||
kind: CommandKind::Focus(FocusCommand::SplitClose),
|
||||
data: None,
|
||||
},
|
||||
Target::Widget(view_id),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
BufferContent::File(_) => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save_as(
|
||||
&mut self,
|
||||
ctx: &mut EventCtx,
|
||||
content: &BufferContent,
|
||||
path: &Path,
|
||||
view_id: WidgetId,
|
||||
exit: bool,
|
||||
) {
|
||||
match content {
|
||||
BufferContent::Scratch(id) => {
|
||||
let event_sink = ctx.get_external_handle();
|
||||
let doc = self.scratch_docs.get(id).unwrap();
|
||||
let rev = doc.rev();
|
||||
let path = path.to_path_buf();
|
||||
let content = content.clone();
|
||||
self.proxy.save_buffer_as(
|
||||
doc.id(),
|
||||
path.to_path_buf(),
|
||||
doc.rev(),
|
||||
doc.buffer().text().to_string(),
|
||||
Box::new(move |result| {
|
||||
if let Ok(_r) = result {
|
||||
let _ = event_sink.submit_command(
|
||||
LAPCE_UI_COMMAND,
|
||||
LapceUICommand::SaveAsSuccess(
|
||||
content, rev, path, view_id, exit,
|
||||
),
|
||||
Target::Auto,
|
||||
);
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
BufferContent::File(_) => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn editor_close(
|
||||
&mut self,
|
||||
ctx: &mut EventCtx,
|
||||
|
@ -2597,12 +2690,11 @@ pub fn editor_close(
|
|||
force: bool,
|
||||
) {
|
||||
let editor = self.editors.get(&view_id).unwrap();
|
||||
if let BufferContent::File(path) = &editor.content {
|
||||
let doc = self.open_docs.get(path).unwrap();
|
||||
if let BufferContent::File(_) | BufferContent::Scratch(_) = &editor.content {
|
||||
let doc = self.editor_doc(view_id);
|
||||
if !force && !doc.buffer().is_pristine() {
|
||||
let exits = self.editors.iter().any(|(_, e)| {
|
||||
e.content == BufferContent::File(path.to_path_buf())
|
||||
&& e.view_id != view_id
|
||||
&e.content == doc.content() && e.view_id != view_id
|
||||
});
|
||||
if !exits {
|
||||
ctx.submit_command(Command::new(
|
||||
|
@ -2610,9 +2702,7 @@ pub fn editor_close(
|
|||
LapceUICommand::ShowAlert(AlertContentData {
|
||||
title: format!(
|
||||
"Do you want to save the changes you made to {}?",
|
||||
path.file_name()
|
||||
.and_then(|f| f.to_str())
|
||||
.unwrap_or("")
|
||||
doc.content().file_name()
|
||||
),
|
||||
msg: "Your changes will be lost if you don't save them."
|
||||
.to_string(),
|
||||
|
@ -2644,7 +2734,7 @@ pub fn editor_close(
|
|||
return;
|
||||
}
|
||||
}
|
||||
self.db.save_doc_position(&self.workspace, doc);
|
||||
self.db.save_doc_position(&self.workspace, &doc);
|
||||
}
|
||||
if let Some(tab_id) = editor.tab_id {
|
||||
let editor_tab = self.editor_tabs.get(&tab_id).unwrap();
|
||||
|
@ -3080,8 +3170,15 @@ pub fn save_jump_location(&mut self, doc: &Document) {
|
|||
}
|
||||
|
||||
pub fn editor_info(&self, data: &LapceTabData) -> EditorInfo {
|
||||
let unsaved = if let BufferContent::Scratch(id) = &self.content {
|
||||
let doc = data.main_split.scratch_docs.get(id).unwrap();
|
||||
Some(doc.buffer().text().to_string())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let info = EditorInfo {
|
||||
content: self.content.clone(),
|
||||
unsaved,
|
||||
scroll_offset: (self.scroll_offset.x, self.scroll_offset.y),
|
||||
position: if let BufferContent::File(path) = &self.content {
|
||||
let doc = data.main_split.open_docs.get(path).unwrap().clone();
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
use druid::{ExtEventSink, Point, Rect, Size, Vec2, WidgetId};
|
||||
use lsp_types::Position;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use xi_rope::Rope;
|
||||
|
||||
use crate::{
|
||||
config::Config,
|
||||
|
@ -233,6 +234,7 @@ pub struct BufferInfo {
|
|||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct EditorInfo {
|
||||
pub content: BufferContent,
|
||||
pub unsaved: Option<String>,
|
||||
pub scroll_offset: (f64, f64),
|
||||
pub position: Option<Position>,
|
||||
}
|
||||
|
@ -285,6 +287,19 @@ pub fn to_data(
|
|||
));
|
||||
data.open_docs.insert(path.clone(), doc);
|
||||
}
|
||||
} else if let BufferContent::Scratch(id) = &self.content {
|
||||
if !data.scratch_docs.contains_key(id) {
|
||||
let mut doc = Document::new(
|
||||
self.content.clone(),
|
||||
tab_id,
|
||||
event_sink,
|
||||
data.proxy.clone(),
|
||||
);
|
||||
if let Some(text) = &self.unsaved {
|
||||
doc.reload(Rope::from(text), false);
|
||||
}
|
||||
data.scratch_docs.insert(*id, Arc::new(doc));
|
||||
}
|
||||
}
|
||||
data.insert_editor(Arc::new(editor_data.clone()), config);
|
||||
editor_data
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
piet::{
|
||||
PietText, PietTextLayout, Text, TextAttribute, TextLayout, TextLayoutBuilder,
|
||||
},
|
||||
ExtEventSink, FontFamily, Point, Target, Vec2, WidgetId,
|
||||
ExtEventSink, FontFamily, Point, Size, Target, Vec2, WidgetId,
|
||||
};
|
||||
use lapce_core::{
|
||||
buffer::{Buffer, DiffLines, InvalLines},
|
||||
|
@ -32,7 +32,7 @@
|
|||
buffer::{BufferId, NewBufferResponse},
|
||||
style::{LineStyle, LineStyles, Style},
|
||||
};
|
||||
use lsp_types::CodeActionResponse;
|
||||
use lsp_types::{CodeActionOrCommand, CodeActionResponse};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use xi_rope::{spans::Spans, Rope, RopeDelta};
|
||||
|
||||
|
@ -99,6 +99,7 @@ pub enum BufferContent {
|
|||
File(PathBuf),
|
||||
Local(LocalBufferKind),
|
||||
Value(String),
|
||||
Scratch(BufferId),
|
||||
}
|
||||
|
||||
impl BufferContent {
|
||||
|
@ -119,6 +120,7 @@ pub fn is_special(&self) -> bool {
|
|||
LocalBufferKind::Empty => false,
|
||||
},
|
||||
BufferContent::Value(_) => true,
|
||||
BufferContent::Scratch(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,6 +136,7 @@ pub fn is_input(&self) -> bool {
|
|||
LocalBufferKind::Empty | LocalBufferKind::SourceControl => false,
|
||||
},
|
||||
BufferContent::Value(_) => true,
|
||||
BufferContent::Scratch(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,6 +144,7 @@ pub fn is_search(&self) -> bool {
|
|||
match &self {
|
||||
BufferContent::File(_) => false,
|
||||
BufferContent::Value(_) => false,
|
||||
BufferContent::Scratch(_) => false,
|
||||
BufferContent::Local(local) => matches!(local, LocalBufferKind::Search),
|
||||
}
|
||||
}
|
||||
|
@ -150,6 +154,17 @@ pub fn is_settings(&self) -> bool {
|
|||
BufferContent::File(_) => false,
|
||||
BufferContent::Value(_) => true,
|
||||
BufferContent::Local(_) => false,
|
||||
BufferContent::Scratch(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file_name(&self) -> &str {
|
||||
match self {
|
||||
BufferContent::File(p) => {
|
||||
p.file_name().and_then(|f| f.to_str()).unwrap_or("")
|
||||
}
|
||||
BufferContent::Scratch(_) => "[Untitled]",
|
||||
_ => "",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -187,10 +202,15 @@ pub fn new(
|
|||
BufferContent::File(path) => Syntax::init(path),
|
||||
BufferContent::Local(_) => None,
|
||||
BufferContent::Value(_) => None,
|
||||
BufferContent::Scratch(_) => None,
|
||||
};
|
||||
let id = match &content {
|
||||
BufferContent::Scratch(id) => *id,
|
||||
_ => BufferId::next(),
|
||||
};
|
||||
|
||||
Self {
|
||||
id: BufferId::next(),
|
||||
id,
|
||||
tab_id,
|
||||
buffer: Buffer::new(""),
|
||||
content,
|
||||
|
@ -219,6 +239,17 @@ pub fn loaded(&self) -> bool {
|
|||
self.loaded
|
||||
}
|
||||
|
||||
pub fn set_content(&mut self, content: BufferContent) {
|
||||
self.content = content;
|
||||
self.syntax = match &self.content {
|
||||
BufferContent::File(path) => Syntax::init(path),
|
||||
BufferContent::Local(_) => None,
|
||||
BufferContent::Value(_) => None,
|
||||
BufferContent::Scratch(_) => None,
|
||||
};
|
||||
self.on_update(None);
|
||||
}
|
||||
|
||||
pub fn content(&self) -> &BufferContent {
|
||||
&self.content
|
||||
}
|
||||
|
@ -234,15 +265,15 @@ pub fn init_content(&mut self, content: Rope) {
|
|||
self.on_update(None);
|
||||
}
|
||||
|
||||
pub fn reload(&mut self, content: Rope) {
|
||||
pub fn reload(&mut self, content: Rope, set_pristine: bool) {
|
||||
self.code_actions.clear();
|
||||
let delta = self.buffer.reload(content);
|
||||
let delta = self.buffer.reload(content, set_pristine);
|
||||
self.apply_deltas(&[delta]);
|
||||
}
|
||||
|
||||
pub fn handle_file_changed(&mut self, content: Rope) {
|
||||
if self.buffer.is_pristine() {
|
||||
self.reload(content);
|
||||
self.reload(content, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -442,6 +473,7 @@ fn on_update(&mut self, delta: Option<&RopeDelta>) {
|
|||
fn notify_special(&self) {
|
||||
match &self.content {
|
||||
BufferContent::File(_) => {}
|
||||
BufferContent::Scratch(_) => {}
|
||||
BufferContent::Local(local) => {
|
||||
let s = self.buffer.text().to_string();
|
||||
match local {
|
||||
|
@ -1384,6 +1416,44 @@ pub fn move_offset(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn code_action_size(
|
||||
&self,
|
||||
text: &mut PietText,
|
||||
offset: usize,
|
||||
config: &Config,
|
||||
) -> Size {
|
||||
let prev_offset = self.buffer.prev_code_boundary(offset);
|
||||
let empty_vec = Vec::new();
|
||||
let code_actions = self.code_actions.get(&prev_offset).unwrap_or(&empty_vec);
|
||||
|
||||
let action_text_layouts: Vec<PietTextLayout> = code_actions
|
||||
.iter()
|
||||
.map(|code_action| {
|
||||
let title = match code_action {
|
||||
CodeActionOrCommand::Command(cmd) => cmd.title.to_string(),
|
||||
CodeActionOrCommand::CodeAction(action) => {
|
||||
action.title.to_string()
|
||||
}
|
||||
};
|
||||
|
||||
text.new_text_layout(title)
|
||||
.font(FontFamily::SYSTEM_UI, 14.0)
|
||||
.build()
|
||||
.unwrap()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut width = 0.0;
|
||||
for text_layout in &action_text_layouts {
|
||||
let line_width = text_layout.size().width + 10.0;
|
||||
if line_width > width {
|
||||
width = line_width;
|
||||
}
|
||||
}
|
||||
let line_height = config.editor.line_height as f64;
|
||||
Size::new(width, code_actions.len() as f64 * line_height)
|
||||
}
|
||||
|
||||
pub fn reset_find(&self, current_find: &Find) {
|
||||
{
|
||||
let find = self.find.borrow();
|
||||
|
|
|
@ -1111,6 +1111,36 @@ fn save(&mut self, ctx: &mut EventCtx, exit: bool) {
|
|||
Target::Auto,
|
||||
);
|
||||
});
|
||||
} else if let BufferContent::Scratch(_) = self.doc.content() {
|
||||
let workspace = self.main_split.workspace.clone();
|
||||
let event_sink = ctx.get_external_handle();
|
||||
let tab_id = *self.main_split.tab_id;
|
||||
let content = self.doc.content().clone();
|
||||
let view_id = self.editor.view_id;
|
||||
thread::spawn(move || {
|
||||
let dirs = directories::UserDirs::new();
|
||||
|
||||
let dir = workspace
|
||||
.path
|
||||
.as_deref()
|
||||
.or_else(|| dirs.as_ref().map(|u| u.home_dir()))
|
||||
.map(|u| u.join("Untitled"));
|
||||
let dir = dir
|
||||
.as_ref()
|
||||
.and_then(|u| u.to_str())
|
||||
.unwrap_or("./Untitled");
|
||||
|
||||
if let Some(path) =
|
||||
tinyfiledialogs::save_file_dialog("Save File", dir)
|
||||
{
|
||||
let path = PathBuf::from(path);
|
||||
let _ = event_sink.submit_command(
|
||||
LAPCE_UI_COMMAND,
|
||||
LapceUICommand::SaveAs(content, path, view_id, exit),
|
||||
Target::Widget(tab_id),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1851,6 +1881,7 @@ fn check_condition(&self, condition: &str) -> bool {
|
|||
"input_focus" => self.editor.content.is_input(),
|
||||
"editor_focus" => match self.editor.content {
|
||||
BufferContent::File(_) => true,
|
||||
BufferContent::Scratch(_) => true,
|
||||
BufferContent::Local(_) => false,
|
||||
BufferContent::Value(_) => false,
|
||||
},
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
use lapce_rpc::buffer::BufferId;
|
||||
use lapce_rpc::core::{CoreNotification, CoreRequest};
|
||||
use lapce_rpc::plugin::PluginDescription;
|
||||
use lapce_rpc::proxy::ProxyRequest;
|
||||
use lapce_rpc::source_control::FileDiff;
|
||||
use lapce_rpc::terminal::TermId;
|
||||
use lapce_rpc::RpcHandler;
|
||||
|
@ -434,6 +435,23 @@ pub fn new_buffer(
|
|||
);
|
||||
}
|
||||
|
||||
pub fn save_buffer_as(
|
||||
&self,
|
||||
buffer_id: BufferId,
|
||||
path: PathBuf,
|
||||
rev: u64,
|
||||
content: String,
|
||||
f: Box<dyn Callback>,
|
||||
) {
|
||||
let request = ProxyRequest::SaveBufferAs {
|
||||
buffer_id,
|
||||
path,
|
||||
rev,
|
||||
content,
|
||||
};
|
||||
self.rpc.send_rpc_request_value_async(request, f);
|
||||
}
|
||||
|
||||
pub fn update(&self, buffer_id: BufferId, delta: &RopeDelta, rev: u64) {
|
||||
self.rpc.send_rpc_notification(
|
||||
"update",
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::{collections::HashSet, io::BufRead};
|
||||
use xi_rope::Rope;
|
||||
|
||||
const OPEN_FILE_EVENT_TOKEN: WatchToken = WatchToken(1);
|
||||
const WORKSPACE_EVENT_TOKEN: WatchToken = WatchToken(2);
|
||||
|
@ -566,6 +567,26 @@ fn handle_request(&self, id: RequestId, rpc: ProxyRequest) {
|
|||
self.lsp.lock().save_buffer(buffer);
|
||||
self.respond(id, resp);
|
||||
}
|
||||
SaveBufferAs {
|
||||
buffer_id,
|
||||
path,
|
||||
rev,
|
||||
content,
|
||||
} => {
|
||||
let mut buffer =
|
||||
Buffer::new(buffer_id, path.clone(), self.git_sender.clone());
|
||||
buffer.rope = Rope::from(content);
|
||||
buffer.rev = rev;
|
||||
let resp = buffer.save(rev).map(|_r| json!({}));
|
||||
if resp.is_ok() {
|
||||
self.buffers.lock().insert(buffer_id, buffer);
|
||||
self.open_files
|
||||
.lock()
|
||||
.insert(path.to_str().unwrap().to_string(), buffer_id);
|
||||
let _ = self.git_sender.send((buffer_id, 0));
|
||||
}
|
||||
self.respond(id, resp);
|
||||
}
|
||||
GlobalSearch { pattern } => {
|
||||
if let Some(workspace) = self.workspace.lock().clone() {
|
||||
let local_dispatcher = self.clone();
|
||||
|
|
|
@ -160,6 +160,47 @@ fn send_rpc_request_common(
|
|||
}
|
||||
}
|
||||
|
||||
fn send_rpc_request_value_common<T: serde::Serialize>(
|
||||
&self,
|
||||
request: T,
|
||||
rh: ResponseHandler,
|
||||
) {
|
||||
let id = self.id.fetch_add(1, Ordering::Relaxed);
|
||||
{
|
||||
let mut pending = self.pending.lock();
|
||||
pending.insert(id, rh);
|
||||
}
|
||||
let mut request = serde_json::to_value(request).unwrap();
|
||||
request
|
||||
.as_object_mut()
|
||||
.unwrap()
|
||||
.insert("id".to_string(), json!(id));
|
||||
|
||||
if let Err(_e) = self.sender.send(request) {
|
||||
let mut pending = self.pending.lock();
|
||||
if let Some(rh) = pending.remove(&id) {
|
||||
rh.invoke(Err(json!("io error")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_rpc_request_value<T: serde::Serialize>(
|
||||
&self,
|
||||
request: T,
|
||||
) -> Result<Value, Value> {
|
||||
let (tx, rx) = crossbeam_channel::bounded(1);
|
||||
self.send_rpc_request_value_common(request, ResponseHandler::Chan(tx));
|
||||
rx.recv().unwrap_or_else(|_| Err(json!("io error")))
|
||||
}
|
||||
|
||||
pub fn send_rpc_request_value_async<T: serde::Serialize>(
|
||||
&self,
|
||||
request: T,
|
||||
f: Box<dyn Callback>,
|
||||
) {
|
||||
self.send_rpc_request_value_common(request, ResponseHandler::Callback(f));
|
||||
}
|
||||
|
||||
pub fn send_rpc_request(
|
||||
&self,
|
||||
method: &str,
|
||||
|
|
|
@ -110,6 +110,12 @@ pub enum ProxyRequest {
|
|||
rev: u64,
|
||||
buffer_id: BufferId,
|
||||
},
|
||||
SaveBufferAs {
|
||||
buffer_id: BufferId,
|
||||
path: PathBuf,
|
||||
rev: u64,
|
||||
content: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
|
|
@ -285,7 +285,7 @@ pub fn get_size(
|
|||
let line_height = data.config.editor.line_height as f64;
|
||||
let width = data.config.editor_char_width(text);
|
||||
match &data.editor.content {
|
||||
BufferContent::File(_) => {
|
||||
BufferContent::File(_) | BufferContent::Scratch(_) => {
|
||||
if data.editor.code_lens {
|
||||
if let Some(syntax) = data.doc.syntax() {
|
||||
let height =
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::iter::Iterator;
|
||||
use std::{iter::Iterator, path::PathBuf};
|
||||
|
||||
use druid::{
|
||||
piet::{Text, TextLayout as TextLayoutTrait, TextLayoutBuilder},
|
||||
|
@ -135,10 +135,19 @@ pub fn paint_buffer(
|
|||
clip_rect.x1 = icon.rect.x0;
|
||||
}
|
||||
}
|
||||
if let BufferContent::File(path) = data.doc.content() {
|
||||
if let BufferContent::File(_) | BufferContent::Scratch(_) =
|
||||
data.doc.content()
|
||||
{
|
||||
let mut path = match data.doc.content() {
|
||||
BufferContent::File(path) => path.to_path_buf(),
|
||||
BufferContent::Scratch(_) => {
|
||||
PathBuf::from(data.doc.content().file_name())
|
||||
}
|
||||
_ => PathBuf::from(""),
|
||||
};
|
||||
|
||||
ctx.with_save(|ctx| {
|
||||
ctx.clip(clip_rect);
|
||||
let mut path = path.clone();
|
||||
let svg = file_svg_new(&path);
|
||||
|
||||
let width = 13.0;
|
||||
|
|
|
@ -359,6 +359,8 @@ fn layout(
|
|||
text = s.to_string();
|
||||
}
|
||||
}
|
||||
} else if let BufferContent::Scratch(_) = &editor.content {
|
||||
text = editor.content.file_name().to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -112,7 +112,7 @@ pub fn request_focus(
|
|||
));
|
||||
}
|
||||
match &editor.content {
|
||||
BufferContent::File(_) => {
|
||||
BufferContent::File(_) | BufferContent::Scratch(_) => {
|
||||
data.focus_area = FocusArea::Editor;
|
||||
data.main_split.active = Arc::new(Some(self.view_id));
|
||||
data.main_split.active_tab = Arc::new(editor.tab_id);
|
||||
|
|
|
@ -668,7 +668,7 @@ pub fn new(
|
|||
event_sink,
|
||||
data.proxy.clone(),
|
||||
);
|
||||
doc.reload(Rope::from(&input));
|
||||
doc.reload(Rope::from(&input), true);
|
||||
data.main_split.value_docs.insert(name, Arc::new(doc));
|
||||
let editor = LapceEditorData::new(None, None, content, &data.config);
|
||||
let view_id = editor.view_id;
|
||||
|
|
|
@ -330,7 +330,7 @@ fn handle_event(
|
|||
.local_docs
|
||||
.get_mut(&LocalBufferKind::Palette)
|
||||
.unwrap();
|
||||
Arc::make_mut(doc).reload(Rope::from(pattern));
|
||||
Arc::make_mut(doc).reload(Rope::from(pattern), true);
|
||||
let editor = data
|
||||
.main_split
|
||||
.editors
|
||||
|
@ -354,7 +354,7 @@ fn handle_event(
|
|||
.get_mut(&LocalBufferKind::Search)
|
||||
.unwrap();
|
||||
if &doc.buffer().text().to_string() != pattern {
|
||||
Arc::make_mut(doc).reload(Rope::from(pattern));
|
||||
Arc::make_mut(doc).reload(Rope::from(pattern), true);
|
||||
}
|
||||
if pattern.is_empty() {
|
||||
Arc::make_mut(&mut data.find).unset();
|
||||
|
@ -624,7 +624,7 @@ fn handle_event(
|
|||
location,
|
||||
} => {
|
||||
let doc = data.main_split.open_docs.get_mut(path).unwrap();
|
||||
Arc::make_mut(doc).reload(Rope::from(content));
|
||||
Arc::make_mut(doc).reload(Rope::from(content), true);
|
||||
data.main_split.go_to_location(
|
||||
ctx,
|
||||
Some(*editor_view_id),
|
||||
|
@ -819,6 +819,22 @@ fn handle_event(
|
|||
}
|
||||
ctx.set_handled();
|
||||
}
|
||||
LapceUICommand::SaveAs(content, path, view_id, exit) => {
|
||||
data.main_split.save_as(ctx, content, path, *view_id, *exit);
|
||||
ctx.set_handled();
|
||||
}
|
||||
LapceUICommand::SaveAsSuccess(
|
||||
content,
|
||||
rev,
|
||||
path,
|
||||
view_id,
|
||||
exit,
|
||||
) => {
|
||||
data.main_split.save_as_success(
|
||||
ctx, content, *rev, path, *view_id, *exit,
|
||||
);
|
||||
ctx.set_handled();
|
||||
}
|
||||
LapceUICommand::OpenFileChanged { path, content } => {
|
||||
let doc = data.main_split.open_docs.get_mut(path).unwrap();
|
||||
let doc = Arc::make_mut(doc);
|
||||
|
@ -828,7 +844,7 @@ fn handle_event(
|
|||
let doc = data.main_split.open_docs.get_mut(path).unwrap();
|
||||
if doc.rev() + 1 == *rev {
|
||||
let doc = Arc::make_mut(doc);
|
||||
doc.reload(content.to_owned());
|
||||
doc.reload(content.to_owned(), true);
|
||||
|
||||
for (_, editor) in data.main_split.editors.iter_mut() {
|
||||
if &editor.content == doc.content()
|
||||
|
|
Loading…
Reference in New Issue