mirror of
https://github.com/gitui-org/gitui
synced 2026-05-24 09:28:21 +00:00
move syntax highlighting into compononent (see #725)
This commit is contained in:
parent
afa10fe756
commit
eab8fc202b
3 changed files with 172 additions and 79 deletions
|
|
@ -20,6 +20,7 @@ mod rename_branch;
|
||||||
mod reset;
|
mod reset;
|
||||||
mod revision_files;
|
mod revision_files;
|
||||||
mod stashmsg;
|
mod stashmsg;
|
||||||
|
mod syntax_text;
|
||||||
mod tag_commit;
|
mod tag_commit;
|
||||||
mod textinput;
|
mod textinput;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
@ -45,6 +46,7 @@ pub use rename_branch::RenameBranchComponent;
|
||||||
pub use reset::ResetComponent;
|
pub use reset::ResetComponent;
|
||||||
pub use revision_files::RevisionFilesComponent;
|
pub use revision_files::RevisionFilesComponent;
|
||||||
pub use stashmsg::StashMsgComponent;
|
pub use stashmsg::StashMsgComponent;
|
||||||
|
pub use syntax_text::SyntaxTextComponent;
|
||||||
pub use tag_commit::TagCommitComponent;
|
pub use tag_commit::TagCommitComponent;
|
||||||
pub use textinput::{InputType, TextInputComponent};
|
pub use textinput::{InputType, TextInputComponent};
|
||||||
pub use utils::filetree::FileTreeItemKind;
|
pub use utils::filetree::FileTreeItemKind;
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
use super::{
|
use super::{
|
||||||
visibility_blocking, CommandBlocking, CommandInfo, Component,
|
visibility_blocking, CommandBlocking, CommandInfo, Component,
|
||||||
DrawableComponent, EventState,
|
DrawableComponent, EventState, SyntaxTextComponent,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
keys::SharedKeyConfig,
|
keys::SharedKeyConfig,
|
||||||
queue::{InternalEvent, Queue},
|
queue::{InternalEvent, Queue},
|
||||||
strings::{self, order},
|
strings::{self, order},
|
||||||
ui::{self, style::SharedTheme, AsyncSyntaxJob},
|
ui::{self, style::SharedTheme},
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use async_utils::AsyncSingleJob;
|
|
||||||
use asyncgit::{
|
use asyncgit::{
|
||||||
sync::{self, CommitId, TreeFile},
|
sync::{self, CommitId, TreeFile},
|
||||||
AsyncNotification, CWD,
|
AsyncNotification, CWD,
|
||||||
|
|
@ -17,15 +16,14 @@ use asyncgit::{
|
||||||
use crossbeam_channel::Sender;
|
use crossbeam_channel::Sender;
|
||||||
use crossterm::event::Event;
|
use crossterm::event::Event;
|
||||||
use filetree::{FileTree, MoveSelection};
|
use filetree::{FileTree, MoveSelection};
|
||||||
use itertools::Either;
|
|
||||||
use std::{
|
use std::{
|
||||||
cell::Cell, collections::BTreeSet, convert::From, path::Path,
|
cell::Cell, collections::BTreeSet, convert::From, path::Path,
|
||||||
};
|
};
|
||||||
use tui::{
|
use tui::{
|
||||||
backend::Backend,
|
backend::Backend,
|
||||||
layout::{Constraint, Direction, Layout, Rect},
|
layout::{Constraint, Direction, Layout, Rect},
|
||||||
text::{Span, Text},
|
text::Span,
|
||||||
widgets::{Block, Borders, Clear, Paragraph, Wrap},
|
widgets::{Block, Borders, Clear},
|
||||||
Frame,
|
Frame,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -39,9 +37,8 @@ pub struct RevisionFilesComponent {
|
||||||
theme: SharedTheme,
|
theme: SharedTheme,
|
||||||
//TODO: store TreeFiles in `tree`
|
//TODO: store TreeFiles in `tree`
|
||||||
files: Vec<TreeFile>,
|
files: Vec<TreeFile>,
|
||||||
current_file: Option<(String, Either<ui::SyntaxText, String>)>,
|
// current_file: Option<(String, Either<ui::SyntaxText, String>)>,
|
||||||
async_highlighting:
|
current_file: SyntaxTextComponent,
|
||||||
AsyncSingleJob<AsyncSyntaxJob, AsyncNotification>,
|
|
||||||
tree: FileTree,
|
tree: FileTree,
|
||||||
scroll_top: Cell<usize>,
|
scroll_top: Cell<usize>,
|
||||||
revision: Option<CommitId>,
|
revision: Option<CommitId>,
|
||||||
|
|
@ -61,13 +58,12 @@ impl RevisionFilesComponent {
|
||||||
queue: queue.clone(),
|
queue: queue.clone(),
|
||||||
title: String::new(),
|
title: String::new(),
|
||||||
tree: FileTree::default(),
|
tree: FileTree::default(),
|
||||||
async_highlighting: AsyncSingleJob::new(
|
scroll_top: Cell::new(0),
|
||||||
sender.clone(),
|
current_file: SyntaxTextComponent::new(
|
||||||
AsyncNotification::SyntaxHighlighting,
|
sender,
|
||||||
|
key_config.clone(),
|
||||||
),
|
),
|
||||||
theme,
|
theme,
|
||||||
scroll_top: Cell::new(0),
|
|
||||||
current_file: None,
|
|
||||||
files: Vec::new(),
|
files: Vec::new(),
|
||||||
revision: None,
|
revision: None,
|
||||||
visible: false,
|
visible: false,
|
||||||
|
|
@ -99,24 +95,12 @@ impl RevisionFilesComponent {
|
||||||
|
|
||||||
///
|
///
|
||||||
pub fn update(&mut self, ev: AsyncNotification) {
|
pub fn update(&mut self, ev: AsyncNotification) {
|
||||||
if ev == AsyncNotification::SyntaxHighlighting {
|
self.current_file.update(ev);
|
||||||
if let Some(job) = self.async_highlighting.get_last() {
|
|
||||||
if let Some((path, content)) =
|
|
||||||
self.current_file.as_mut()
|
|
||||||
{
|
|
||||||
if let Some(syntax) = (*job.text).clone() {
|
|
||||||
if syntax.path() == Path::new(path) {
|
|
||||||
*content = Either::Left(syntax);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
pub fn any_work_pending(&self) -> bool {
|
pub fn any_work_pending(&self) -> bool {
|
||||||
self.async_highlighting.is_pending()
|
self.current_file.any_work_pending()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tree_item_to_span<'a>(
|
fn tree_item_to_span<'a>(
|
||||||
|
|
@ -170,48 +154,15 @@ impl RevisionFilesComponent {
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.to_string()
|
.to_string()
|
||||||
}) {
|
}) {
|
||||||
let already_loaded = self
|
if let Some(item) = self
|
||||||
.current_file
|
.files
|
||||||
.as_ref()
|
.iter()
|
||||||
.map(|(current_file, _)| current_file == &file)
|
.find(|f| f.path.ends_with(Path::new(&file)))
|
||||||
.unwrap_or_default();
|
{
|
||||||
|
self.current_file.load_file(file, item);
|
||||||
if !already_loaded {
|
|
||||||
self.load_file(file);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.current_file = None;
|
self.current_file.clear();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_file(&mut self, path: String) {
|
|
||||||
let path_path = Path::new(&path);
|
|
||||||
if let Some(item) =
|
|
||||||
self.files.iter().find(|f| f.path.ends_with(path_path))
|
|
||||||
{
|
|
||||||
//TODO: fetch file content async aswell
|
|
||||||
match sync::tree_file_content(CWD, item) {
|
|
||||||
Ok(content) => {
|
|
||||||
self.async_highlighting.spawn(
|
|
||||||
AsyncSyntaxJob::new(
|
|
||||||
content.clone(),
|
|
||||||
path.clone(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
self.current_file =
|
|
||||||
Some((path, Either::Right(content)))
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
self.current_file = Some((
|
|
||||||
path,
|
|
||||||
Either::Right(format!(
|
|
||||||
"error loading file: {}",
|
|
||||||
e
|
|
||||||
)),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -281,17 +232,7 @@ impl DrawableComponent for RevisionFilesComponent {
|
||||||
items,
|
items,
|
||||||
);
|
);
|
||||||
|
|
||||||
let content = Paragraph::new(
|
self.current_file.draw(f, chunks[1])?;
|
||||||
self.current_file.as_ref().map_or_else(
|
|
||||||
|| Text::from(""),
|
|
||||||
|(_, content)| match content {
|
|
||||||
Either::Left(syn) => syn.into(),
|
|
||||||
Either::Right(s) => Text::from(s.as_str()),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.wrap(Wrap { trim: false });
|
|
||||||
f.render_widget(content, chunks[1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
150
src/components/syntax_text.rs
Normal file
150
src/components/syntax_text.rs
Normal file
|
|
@ -0,0 +1,150 @@
|
||||||
|
use super::{
|
||||||
|
CommandBlocking, CommandInfo, Component, DrawableComponent,
|
||||||
|
EventState,
|
||||||
|
};
|
||||||
|
use crate::{
|
||||||
|
keys::SharedKeyConfig,
|
||||||
|
ui::{self, AsyncSyntaxJob},
|
||||||
|
};
|
||||||
|
use anyhow::Result;
|
||||||
|
use async_utils::AsyncSingleJob;
|
||||||
|
use asyncgit::{
|
||||||
|
sync::{self, TreeFile},
|
||||||
|
AsyncNotification, CWD,
|
||||||
|
};
|
||||||
|
use crossbeam_channel::Sender;
|
||||||
|
use itertools::Either;
|
||||||
|
use std::{convert::From, path::Path};
|
||||||
|
use tui::{
|
||||||
|
backend::Backend,
|
||||||
|
layout::Rect,
|
||||||
|
text::Text,
|
||||||
|
widgets::{Paragraph, Wrap},
|
||||||
|
Frame,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct SyntaxTextComponent {
|
||||||
|
current_file: Option<(String, Either<ui::SyntaxText, String>)>,
|
||||||
|
async_highlighting:
|
||||||
|
AsyncSingleJob<AsyncSyntaxJob, AsyncNotification>,
|
||||||
|
_key_config: SharedKeyConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SyntaxTextComponent {
|
||||||
|
///
|
||||||
|
pub fn new(
|
||||||
|
sender: &Sender<AsyncNotification>,
|
||||||
|
key_config: SharedKeyConfig,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
async_highlighting: AsyncSingleJob::new(
|
||||||
|
sender.clone(),
|
||||||
|
AsyncNotification::SyntaxHighlighting,
|
||||||
|
),
|
||||||
|
current_file: None,
|
||||||
|
_key_config: key_config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
pub fn update(&mut self, ev: AsyncNotification) {
|
||||||
|
if ev == AsyncNotification::SyntaxHighlighting {
|
||||||
|
if let Some(job) = self.async_highlighting.get_last() {
|
||||||
|
if let Some((path, content)) =
|
||||||
|
self.current_file.as_mut()
|
||||||
|
{
|
||||||
|
if let Some(syntax) = (*job.text).clone() {
|
||||||
|
if syntax.path() == Path::new(path) {
|
||||||
|
*content = Either::Left(syntax);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
pub fn any_work_pending(&self) -> bool {
|
||||||
|
self.async_highlighting.is_pending()
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.current_file = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
pub fn load_file(&mut self, path: String, item: &TreeFile) {
|
||||||
|
let already_loaded = self
|
||||||
|
.current_file
|
||||||
|
.as_ref()
|
||||||
|
.map(|(current_file, _)| current_file == &path)
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
if !already_loaded {
|
||||||
|
//TODO: fetch file content async aswell
|
||||||
|
match sync::tree_file_content(CWD, item) {
|
||||||
|
Ok(content) => {
|
||||||
|
self.async_highlighting.spawn(
|
||||||
|
AsyncSyntaxJob::new(
|
||||||
|
content.clone(),
|
||||||
|
path.clone(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
self.current_file =
|
||||||
|
Some((path, Either::Right(content)))
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
self.current_file = Some((
|
||||||
|
path,
|
||||||
|
Either::Right(format!(
|
||||||
|
"error loading file: {}",
|
||||||
|
e
|
||||||
|
)),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DrawableComponent for SyntaxTextComponent {
|
||||||
|
fn draw<B: Backend>(
|
||||||
|
&self,
|
||||||
|
f: &mut Frame<B>,
|
||||||
|
area: Rect,
|
||||||
|
) -> Result<()> {
|
||||||
|
let content =
|
||||||
|
Paragraph::new(self.current_file.as_ref().map_or_else(
|
||||||
|
|| Text::from(""),
|
||||||
|
|(_, content)| match content {
|
||||||
|
Either::Left(syn) => syn.into(),
|
||||||
|
Either::Right(s) => Text::from(s.as_str()),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.wrap(Wrap { trim: false });
|
||||||
|
f.render_widget(content, area);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for SyntaxTextComponent {
|
||||||
|
fn commands(
|
||||||
|
&self,
|
||||||
|
_out: &mut Vec<CommandInfo>,
|
||||||
|
_force_all: bool,
|
||||||
|
) -> CommandBlocking {
|
||||||
|
//TODO: scrolling
|
||||||
|
CommandBlocking::PassingOn
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event(
|
||||||
|
&mut self,
|
||||||
|
_event: crossterm::event::Event,
|
||||||
|
) -> Result<EventState> {
|
||||||
|
//TODO: scrolling
|
||||||
|
Ok(EventState::NotConsumed)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue