Skip to content

Commit

Permalink
New branch for issue xi-editor#922.
Browse files Browse the repository at this point in the history
  • Loading branch information
sjoshid committed Mar 31, 2019
1 parent 533a66a commit 94e2b1b
Show file tree
Hide file tree
Showing 7 changed files with 217 additions and 16 deletions.
10 changes: 10 additions & 0 deletions rust/core-lib/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,16 @@ impl Client {
pub fn schedule_timer(&self, timeout: Instant, token: usize) {
self.0.schedule_timer(timeout, token);
}

pub fn toggle_tail_config_changed(&self, view_id: ViewId, is_tail_enabled: bool) {
self.0.send_rpc_notification(
"toggle_tail_config_changed",
&json!({
"view_id": view_id,
"is_tail_enabled": is_tail_enabled,
}),
);
}
}

#[derive(Debug, Serialize)]
Expand Down
8 changes: 8 additions & 0 deletions rust/core-lib/src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,14 @@ impl Editor {

Some(GetDataResponse { chunk, offset, first_line, first_line_offset })
}

pub(crate) fn tail_append(&mut self, tail: Rope) {
let buf_end = self.text.len();
let mut builder = DeltaBuilder::new(buf_end);
builder.replace(buf_end..buf_end, tail);
self.add_delta(builder.build());
self.set_pristine();
}
}

#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
Expand Down
22 changes: 22 additions & 0 deletions rust/core-lib/src/event_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,18 @@ impl<'a> EventContext<'a> {
ed.is_pristine(),
)
}

fn render_tail(&mut self) {
let _t = trace_block("EventContext::render_tail", &["core"]);
let ed = self.editor.borrow();
self.view.borrow_mut().render_tail(
ed.get_buffer(),
self.client,
self.style_map,
ed.get_layers().get_merged(),
ed.is_pristine(),
)
}
}

/// Helpers related to specific commands.
Expand Down Expand Up @@ -449,6 +461,16 @@ impl<'a> EventContext<'a> {
self.render();
}

pub(crate) fn reload_tail(&mut self, text: Rope) {
self.with_editor(|ed, _, _, _| ed.tail_append(text));
self.after_edit("core");
self.render_tail();
}

pub(crate) fn toggle_tail_config_changed(&mut self, is_tail_enabled: bool) {
self.client.toggle_tail_config_changed(self.view_id, is_tail_enabled);
}

pub(crate) fn plugin_info(&mut self) -> PluginBufferInfo {
let ed = self.editor.borrow();
let nb_lines = ed.get_buffer().measure::<LinesMetric>() + 1;
Expand Down
75 changes: 71 additions & 4 deletions rust/core-lib/src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use std::collections::HashMap;
use std::ffi::OsString;
use std::fmt;
use std::fs::{self, File, Permissions};
use std::io::{self, Read, Write};
use std::io::{self, Read, Seek, SeekFrom, Write};
use std::path::{Path, PathBuf};
use std::str;
use std::time::SystemTime;
Expand Down Expand Up @@ -54,6 +54,7 @@ pub struct FileInfo {
pub has_changed: bool,
#[cfg(target_family = "unix")]
pub permissions: Option<u32>,
pub tail_details: TailDetails,
}

pub enum FileError {
Expand All @@ -62,6 +63,13 @@ pub enum FileError {
HasChanged(PathBuf),
}

#[derive(Debug, Default)]
pub struct TailDetails {
pub current_position_in_tail: u64,
pub is_tail_enabled: bool,
pub is_at_bottom_of_file: bool,
}

#[derive(Debug, Clone, Copy)]
pub enum CharacterEncoding {
Utf8,
Expand Down Expand Up @@ -110,7 +118,7 @@ impl FileManager {
let _ = File::create(path).map_err(|e| FileError::Io(e, path.to_owned()))?;
}

let (rope, info) = try_load_file(path)?;
let (rope, info) = try_load_file(self, id, path)?;

self.open_files.insert(path.to_owned(), id);
if self.file_info.insert(id, info).is_none() {
Expand Down Expand Up @@ -147,6 +155,7 @@ impl FileManager {
has_changed: false,
#[cfg(target_family = "unix")]
permissions: get_permissions(path),
tail_details: TailDetails::default(),
};
self.open_files.insert(path.to_owned(), id);
self.file_info.insert(id, info);
Expand All @@ -172,28 +181,86 @@ impl FileManager {
}
Ok(())
}

/// Toggles the **is_tail_enabled** flag in TailDetails.
/// Also when flag is enabled, it sets the cursor in TailDetails ie **current_position_in_tail** to the end of file
/// being tailed.
#[cfg(feature = "notify")]
pub fn toggle_tail(&mut self, id: BufferId, enabled: bool) -> Result<(), FileError> {
if let Some(v) = self.file_info.get_mut(&id) {
if enabled {
let path = v.path.as_path();
let mut f = File::open(path).map_err(|e| FileError::Io(e, path.to_owned()))?;
let end_position =
f.seek(SeekFrom::End(0)).map_err(|e| FileError::Io(e, path.to_owned()))?;

v.tail_details.current_position_in_tail = end_position;
}
v.tail_details.is_tail_enabled = enabled;
}
Ok(())
}
}

fn try_load_file<P>(path: P) -> Result<(Rope, FileInfo), FileError>
fn try_load_file<P>(
file_manager: &FileManager,
buffer_id: BufferId,
path: P,
) -> Result<(Rope, FileInfo), FileError>
where
P: AsRef<Path>,
{
// TODO: support for non-utf8
// it's arguable that the rope crate should have file loading functionality
let mut f =
File::open(path.as_ref()).map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?;
let mut new_tail_details = TailDetails::default();
let mut bytes = Vec::new();
f.read_to_end(&mut bytes).map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?;

let file_info = file_manager.get_info(buffer_id);
match file_info {
Some(v) => {
let is_tail_enabled = v.tail_details.is_tail_enabled;
if is_tail_enabled {
debug!("Tailing file");
let end_position = f
.seek(SeekFrom::End(0))
.map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?;
let current_position = v.tail_details.current_position_in_tail;

let diff = end_position - current_position;
bytes = vec![0; diff as usize];
f.seek(SeekFrom::Current(-(bytes.len() as i64))).unwrap();
f.read_exact(&mut bytes).unwrap();

new_tail_details = TailDetails {
current_position_in_tail: end_position,
is_tail_enabled: v.tail_details.is_tail_enabled,
is_at_bottom_of_file: v.tail_details.is_at_bottom_of_file,
};
} else {
debug!("Tail is false, So loading entire file.");
f.read_to_end(&mut bytes)
.map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?;
}
}
None => {
debug!("Loading entire file");
f.read_to_end(&mut bytes).map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?;
}
}

let encoding = CharacterEncoding::guess(&bytes);
let rope = try_decode(bytes, encoding, path.as_ref())?;

let info = FileInfo {
encoding,
mod_time: get_mod_time(&path),
#[cfg(target_family = "unix")]
permissions: get_permissions(&path),
path: path.as_ref().to_owned(),
has_changed: false,
tail_details: new_tail_details,
};
Ok((rope, info))
}
Expand Down
36 changes: 29 additions & 7 deletions rust/core-lib/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,12 +174,19 @@ pub enum CoreNotification {
/// ```
Plugin(PluginNotification),
/// Tells `xi-core` to close the specified view.
CloseView { view_id: ViewId },
CloseView {
view_id: ViewId,
},
/// Tells `xi-core` to save the contents of the specified view's
/// buffer to the specified path.
Save { view_id: ViewId, file_path: String },
Save {
view_id: ViewId,
file_path: String,
},
/// Tells `xi-core` to set the theme.
SetTheme { theme_name: String },
SetTheme {
theme_name: String,
},
/// Notifies `xi-core` that the client has started.
ClientStarted {
#[serde(default)]
Expand All @@ -197,16 +204,31 @@ pub enum CoreNotification {
/// domain argument is `ConfigDomain::UserOverride(_)`, which
/// represents non-persistent view-specific settings, such as when
/// a user manually changes whitespace settings for a given view.
ModifyUserConfig { domain: ConfigDomainExternal, changes: Table },
ModifyUserConfig {
domain: ConfigDomainExternal,
changes: Table,
},
/// Control whether the tracing infrastructure is enabled.
/// This propagates to all peers that should respond by toggling its own
/// infrastructure on/off.
TracingConfig { enabled: bool },
TracingConfig {
enabled: bool,
},
/// Save trace data to the given path. The core will first send
/// CoreRequest::CollectTrace to all peers to collect the samples.
SaveTrace { destination: PathBuf, frontend_samples: Value },
SaveTrace {
destination: PathBuf,
frontend_samples: Value,
},
/// Tells `xi-core` to set the language id for the view.
SetLanguage { view_id: ViewId, language_id: LanguageId },
SetLanguage {
view_id: ViewId,
language_id: LanguageId,
},
ToggleTail {
view_id: ViewId,
enabled: bool,
},
}

/// The requests which make up the base of the protocol.
Expand Down
38 changes: 37 additions & 1 deletion rust/core-lib/src/tabs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ impl CoreState {
// handled at the top level
ClientStarted { .. } => (),
SetLanguage { view_id, language_id } => self.do_set_language(view_id, language_id),
ToggleTail { view_id, enabled } => self.do_toggle_tail(view_id, enabled),
}
}

Expand Down Expand Up @@ -550,6 +551,28 @@ impl CoreState {
fn after_stop_plugin(&mut self, plugin: &Plugin) {
self.iter_groups().for_each(|mut cx| cx.plugin_stopped(plugin));
}

#[cfg(feature = "notify")]
fn do_toggle_tail(&mut self, view_id: ViewId, enabled: bool) {
if let Some(view) = self.views.get_mut(&view_id) {
let buffer_id = view.borrow().get_buffer_id();
view.borrow_mut().toggle_tail(enabled);
match self.file_manager.toggle_tail(buffer_id, enabled) {
Ok(()) => {
debug!("Tail is {:?} for {:?}", enabled, view_id);
let mut context = self.make_context(view_id).unwrap();
context.toggle_tail_config_changed(enabled);
return;
}
Err(err) => error!("Error reading file: {}", err),
}
}
}

#[cfg(not(feature = "notify"))]
fn do_toggle_tail(&mut self, _view_id: ViewId, _enabled: bool) {
warn!("do_toggle_tail called without notify feature enabled.");
}
}

/// Idle, tracing, and file event handling
Expand Down Expand Up @@ -728,7 +751,20 @@ impl CoreState {
.find(|v| v.borrow().get_buffer_id() == buffer_id)
.map(|v| v.borrow().get_view_id())
.unwrap();
self.make_context(view_id).unwrap().reload(text);

let file_info = self.file_manager.get_info(buffer_id);

match file_info {
Some(v) => {
let tail_mode_on = v.tail_details.is_tail_enabled;
if tail_mode_on {
self.make_context(view_id).unwrap().reload_tail(text);
} else {
self.make_context(view_id).unwrap().reload(text);
}
}
None => error!("File info not found for buffer id {}", buffer_id),
};
}
}
}
Expand Down
Loading

0 comments on commit 94e2b1b

Please sign in to comment.