forked from Mirrors/helix
@ -1,41 +0,0 @@
|
||||
name: Github Pages
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup mdBook
|
||||
uses: peaceiris/actions-mdbook@v1
|
||||
with:
|
||||
mdbook-version: 'latest'
|
||||
# mdbook-version: '0.4.8'
|
||||
|
||||
- run: mdbook build book
|
||||
|
||||
- name: Set output directory
|
||||
run: |
|
||||
OUTDIR=$(basename ${{ github.ref }})
|
||||
echo "OUTDIR=$OUTDIR" >> $GITHUB_ENV
|
||||
|
||||
- name: Deploy
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./book/book
|
||||
destination_dir: ./${{ env.OUTDIR }}
|
||||
|
||||
- name: Deploy stable
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
if: startswith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./book/book
|
@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="CPP_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/helix.iml" filepath="$PROJECT_DIR$/.idea/helix.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
@ -1 +0,0 @@
|
||||
../runtime/themes
|
@ -1 +0,0 @@
|
||||
../../../src/indent.rs
|
@ -0,0 +1,914 @@
|
||||
use super::{Prompt, Tree, TreeItem, TreeOp};
|
||||
use crate::{
|
||||
compositor::{Component, Compositor, Context, EventResult},
|
||||
ctrl, key, shift, ui,
|
||||
};
|
||||
use anyhow::{bail, ensure, Result};
|
||||
use helix_core::Position;
|
||||
use helix_view::{
|
||||
editor::Action,
|
||||
graphics::{CursorKind, Modifier, Rect},
|
||||
input::{Event, KeyEvent},
|
||||
Editor,
|
||||
};
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::Ordering;
|
||||
use std::path::{Path, PathBuf};
|
||||
use tui::{
|
||||
buffer::Buffer as Surface,
|
||||
text::{Span, Spans},
|
||||
widgets::{Block, Borders, Widget},
|
||||
};
|
||||
|
||||
macro_rules! get_theme {
|
||||
($theme: expr, $s1: expr, $s2: expr) => {
|
||||
$theme.try_get($s1).unwrap_or_else(|| $theme.get($s2))
|
||||
};
|
||||
}
|
||||
|
||||
const ICONS: &'static [&'static str] =
|
||||
&["", "", "", "", "", "ﰟ", "", "", "", "ﯤ", "", "ﬥ"];
|
||||
|
||||
const ICONS_EXT: &'static [&'static str] = &[
|
||||
".rs", ".md", ".js", ".c", ".png", ".svg", ".css", ".html", ".lua", ".ts", ".py", ".json",
|
||||
];
|
||||
|
||||
const ICONS_COLORS: &'static [helix_view::theme::Color] = &[
|
||||
helix_view::theme::Color::Rgb(227, 134, 84),
|
||||
helix_view::theme::Color::LightCyan,
|
||||
helix_view::theme::Color::Yellow,
|
||||
helix_view::theme::Color::Blue,
|
||||
helix_view::theme::Color::Yellow,
|
||||
helix_view::theme::Color::Yellow,
|
||||
helix_view::theme::Color::Green,
|
||||
helix_view::theme::Color::Blue,
|
||||
helix_view::theme::Color::Red,
|
||||
helix_view::theme::Color::Blue,
|
||||
helix_view::theme::Color::Red,
|
||||
];
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
enum FileType {
|
||||
File,
|
||||
Dir,
|
||||
Exe,
|
||||
Placeholder,
|
||||
Parent,
|
||||
Root,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct FileInfo {
|
||||
file_type: FileType,
|
||||
expanded: bool,
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
impl FileInfo {
|
||||
fn new(path: PathBuf, file_type: FileType) -> Self {
|
||||
Self {
|
||||
path,
|
||||
file_type,
|
||||
expanded: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn root(path: PathBuf) -> Self {
|
||||
Self {
|
||||
file_type: FileType::Root,
|
||||
path,
|
||||
expanded: true,
|
||||
}
|
||||
}
|
||||
|
||||
fn parent(path: &Path) -> Self {
|
||||
let p = path.parent().unwrap_or_else(|| Path::new(""));
|
||||
Self {
|
||||
file_type: FileType::Parent,
|
||||
path: p.to_path_buf(),
|
||||
expanded: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_text(&self) -> Cow<'static, str> {
|
||||
match self.file_type {
|
||||
FileType::Parent => "..".into(),
|
||||
FileType::Placeholder => "---".into(),
|
||||
FileType::Root => {
|
||||
if let Some(path) = self.path.iter().last() {
|
||||
format!("- {} -", path.to_string_lossy()).into()
|
||||
} else {
|
||||
Cow::from("/")
|
||||
}
|
||||
}
|
||||
FileType::File | FileType::Exe | FileType::Dir => self
|
||||
.path
|
||||
.file_name()
|
||||
.map_or("/".into(), |p| p.to_string_lossy().into_owned().into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TreeItem for FileInfo {
|
||||
type Params = State;
|
||||
fn text(&self, cx: &mut Context, selected: bool, state: &mut State) -> Spans {
|
||||
let text = self.get_text();
|
||||
let theme = &cx.editor.theme;
|
||||
|
||||
let style = match self.file_type {
|
||||
FileType::Parent | FileType::Dir | FileType::Root => "ui.explorer.dir",
|
||||
FileType::File | FileType::Exe | FileType::Placeholder => "ui.explorer.file",
|
||||
};
|
||||
let mut style = theme.try_get(style).unwrap_or_else(|| theme.get("ui.text"));
|
||||
if selected {
|
||||
let patch = match state.focus {
|
||||
true => "ui.explorer.focus",
|
||||
false => "ui.explorer.unfocus",
|
||||
};
|
||||
if let Some(patch) = theme.try_get(patch) {
|
||||
style = style.patch(patch);
|
||||
} else {
|
||||
style = style.add_modifier(Modifier::REVERSED);
|
||||
}
|
||||
}
|
||||
Spans::from(Span::styled(text, style))
|
||||
}
|
||||
|
||||
fn is_child(&self, other: &Self) -> bool {
|
||||
if let FileType::Parent = other.file_type {
|
||||
return false;
|
||||
}
|
||||
if let FileType::Placeholder = self.file_type {
|
||||
self.path == other.path
|
||||
} else {
|
||||
self.path.parent().map_or(false, |p| p == other.path)
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
use FileType::*;
|
||||
match (self.file_type, other.file_type) {
|
||||
(Parent, _) => return Ordering::Less,
|
||||
(_, Parent) => return Ordering::Greater,
|
||||
(Root, _) => return Ordering::Less,
|
||||
(_, Root) => return Ordering::Greater,
|
||||
_ => {}
|
||||
};
|
||||
|
||||
if self.path == other.path {
|
||||
match (self.file_type, other.file_type) {
|
||||
(_, Placeholder) => return Ordering::Less,
|
||||
(Placeholder, _) => return Ordering::Greater,
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
if let (Some(p1), Some(p2)) = (self.path.parent(), other.path.parent()) {
|
||||
if p1 == p2 {
|
||||
match (self.file_type, other.file_type) {
|
||||
(Dir, File | Exe) => return Ordering::Less,
|
||||
(File | Exe, Dir) => return Ordering::Greater,
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
self.path.cmp(&other.path)
|
||||
}
|
||||
|
||||
fn get_childs(&self) -> Result<Vec<Self>> {
|
||||
match self.file_type {
|
||||
FileType::Root | FileType::Dir => {}
|
||||
_ => return Ok(vec![]),
|
||||
};
|
||||
let mut ret: Vec<_> = std::fs::read_dir(&self.path)?
|
||||
.filter_map(|entry| entry.ok())
|
||||
.filter_map(|entry| {
|
||||
entry.metadata().ok().map(|meta| {
|
||||
let is_exe = false;
|
||||
let file_type = match (meta.is_dir(), is_exe) {
|
||||
(true, _) => FileType::Dir,
|
||||
(_, false) => FileType::File,
|
||||
(_, true) => FileType::Exe,
|
||||
};
|
||||
Self {
|
||||
file_type,
|
||||
path: self.path.join(entry.file_name()),
|
||||
expanded: false,
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
if ret.is_empty() {
|
||||
ret.push(Self {
|
||||
path: self.path.clone(),
|
||||
file_type: FileType::Placeholder,
|
||||
expanded: false,
|
||||
})
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn filter(&self, _cx: &mut Context, s: &str, _params: &mut Self::Params) -> bool {
|
||||
if s.is_empty() {
|
||||
false
|
||||
} else {
|
||||
self.get_text().contains(s)
|
||||
}
|
||||
}
|
||||
|
||||
fn icon(&self) -> Option<(&'static str, &'static helix_view::theme::Color)> {
|
||||
return match self.file_type {
|
||||
FileType::Dir => {
|
||||
if self.expanded {
|
||||
Some(("", &helix_view::theme::Color::Yellow))
|
||||
} else {
|
||||
Some(("", &helix_view::theme::Color::Yellow))
|
||||
}
|
||||
}
|
||||
FileType::File => {
|
||||
for (i, ext) in ICONS_EXT.iter().enumerate() {
|
||||
if self.get_text().ends_with(ext) {
|
||||
let color = ICONS_COLORS
|
||||
.iter()
|
||||
.nth(i)
|
||||
.unwrap_or(&helix_view::theme::Color::Blue);
|
||||
return ICONS.iter().nth(i).map(|c| (*c, color));
|
||||
}
|
||||
}
|
||||
return Some(("", &helix_view::theme::Color::LightBlue));
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum PromptAction {
|
||||
Search(bool), // search next/search pre
|
||||
Mkdir,
|
||||
CreateFile,
|
||||
RemoveDir,
|
||||
RemoveFile,
|
||||
Filter,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct State {
|
||||
focus: bool,
|
||||
current_root: PathBuf,
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn new(focus: bool, current_root: PathBuf) -> Self {
|
||||
Self {
|
||||
focus,
|
||||
current_root,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Explorer {
|
||||
tree: Tree<FileInfo>,
|
||||
state: State,
|
||||
prompt: Option<(PromptAction, Prompt)>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
on_next_key: Option<Box<dyn FnMut(&mut Context, &mut Self, &KeyEvent) -> EventResult>>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
repeat_motion: Option<Box<dyn FnMut(&mut Self, PromptAction, &mut Context) + 'static>>,
|
||||
}
|
||||
|
||||
impl Explorer {
|
||||
pub fn new(cx: &mut Context) -> Result<Self> {
|
||||
let current_root = std::env::current_dir().unwrap_or_else(|_| "./".into());
|
||||
let items = Self::get_items(current_root.clone(), cx)?;
|
||||
Ok(Self {
|
||||
tree: Tree::build_tree(items)
|
||||
.with_enter_fn(Self::toggle_current)
|
||||
.with_folded_fn(Self::fold_current),
|
||||
state: State::new(true, current_root),
|
||||
repeat_motion: None,
|
||||
prompt: None,
|
||||
on_next_key: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_selection(&mut self, path: &Path) {
|
||||
let info = if path.is_file() {
|
||||
FileInfo::new(path.into(), FileType::File)
|
||||
} else {
|
||||
FileInfo::new(path.into(), FileType::Dir)
|
||||
};
|
||||
self.tree.select(&info);
|
||||
self.tree.save_view();
|
||||
}
|
||||
|
||||
pub fn new_explorer_recursion() -> Result<Self> {
|
||||
let current_root = std::env::current_dir().unwrap_or_else(|_| "./".into());
|
||||
let parent = FileInfo::parent(¤t_root);
|
||||
let root = FileInfo::root(current_root.clone());
|
||||
let mut tree = Tree::build_from_root(root, usize::MAX / 2)?
|
||||
.with_enter_fn(Self::toggle_current)
|
||||
.with_folded_fn(Self::fold_current);
|
||||
tree.insert_current_level(parent);
|
||||
Ok(Self {
|
||||
tree,
|
||||
state: State::new(true, current_root),
|
||||
repeat_motion: None,
|
||||
prompt: None,
|
||||
on_next_key: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn focus(&mut self) {
|
||||
self.state.focus = true
|
||||
}
|
||||
|
||||
pub fn unfocus(&mut self) {
|
||||
self.state.focus = false;
|
||||
}
|
||||
|
||||
pub fn is_focus(&self) -> bool {
|
||||
self.state.focus
|
||||
}
|
||||
|
||||
fn get_items(p: PathBuf, cx: &mut Context) -> Result<Vec<FileInfo>> {
|
||||
let mut items = Vec::new();
|
||||
let root = FileInfo::root(p);
|
||||
let childs = root.get_childs()?;
|
||||
if cx.editor.config().explorer.is_tree() {
|
||||
items.push(root)
|
||||
}
|
||||
items.extend(childs);
|
||||
Ok(items)
|
||||
}
|
||||
|
||||
fn render_preview(&mut self, area: Rect, surface: &mut Surface, editor: &Editor) {
|
||||
if area.height <= 2 || area.width < 60 {
|
||||
return;
|
||||
}
|
||||
let item = self.tree.current().item();
|
||||
if item.file_type == FileType::Placeholder {
|
||||
return;
|
||||
}
|
||||
let head_area = render_block(
|
||||
area.clip_bottom(area.height - 2),
|
||||
surface,
|
||||
Borders::BOTTOM,
|
||||
None,
|
||||
);
|
||||
let path_str = format!("{}", item.path.display());
|
||||
surface.set_stringn(
|
||||
head_area.x,
|
||||
head_area.y,
|
||||
path_str,
|
||||
head_area.width as usize,
|
||||
get_theme!(editor.theme, "ui.explorer.dir", "ui.text"),
|
||||
);
|
||||
|
||||
let body_area = area.clip_top(2);
|
||||
let style = editor.theme.get("ui.text");
|
||||
if let Ok(preview_content) = get_preview(&item.path, body_area.height as usize) {
|
||||
preview_content
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.for_each(|(row, line)| {
|
||||
surface.set_stringn(
|
||||
body_area.x,
|
||||
body_area.y + row as u16,
|
||||
line,
|
||||
body_area.width as usize,
|
||||
style,
|
||||
);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn new_search_prompt(&mut self, search_next: bool) {
|
||||
self.tree.save_view();
|
||||
self.prompt = Some((
|
||||
PromptAction::Search(search_next),
|
||||
Prompt::new("search: ".into(), None, ui::completers::none, |_, _, _| {}),
|
||||
))
|
||||
}
|
||||
|
||||
fn new_filter_prompt(&mut self) {
|
||||
self.tree.save_view();
|
||||
self.prompt = Some((
|
||||
PromptAction::Filter,
|
||||
Prompt::new("filter: ".into(), None, ui::completers::none, |_, _, _| {}),
|
||||
))
|
||||
}
|
||||
|
||||
fn new_mkdir_prompt(&mut self) {
|
||||
self.prompt = Some((
|
||||
PromptAction::Mkdir,
|
||||
Prompt::new("mkdir: ".into(), None, ui::completers::none, |_, _, _| {}),
|
||||
));
|
||||
}
|
||||
|
||||
fn new_create_file_prompt(&mut self) {
|
||||
self.prompt = Some((
|
||||
PromptAction::CreateFile,
|
||||
Prompt::new(
|
||||
"create file: ".into(),
|
||||
None,
|
||||
ui::completers::none,
|
||||
|_, _, _| {},
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
fn new_remove_file_prompt(&mut self, cx: &mut Context) {
|
||||
let item = self.tree.current_item();
|
||||
let check = || {
|
||||
ensure!(item.file_type != FileType::Placeholder, "The path is empty");
|
||||
ensure!(
|
||||
item.file_type != FileType::Parent,
|
||||
"can not remove parent dir"
|
||||
);
|
||||
ensure!(item.path.is_file(), "The path is not a file");
|
||||
let doc = cx.editor.document_by_path(&item.path);
|
||||
ensure!(doc.is_none(), "The file is opened");
|
||||
Ok(())
|
||||
};
|
||||
if let Err(e) = check() {
|
||||
cx.editor.set_error(format!("{e}"));
|
||||
return;
|
||||
}
|
||||
let p = format!("remove file: {}, YES? ", item.path.display());
|
||||
self.prompt = Some((
|
||||
PromptAction::RemoveFile,
|
||||
Prompt::new(p.into(), None, ui::completers::none, |_, _, _| {}),
|
||||
));
|
||||
}
|
||||
|
||||
fn new_remove_dir_prompt(&mut self, cx: &mut Context) {
|
||||
let item = self.tree.current_item();
|
||||
let check = || {
|
||||
ensure!(item.file_type != FileType::Placeholder, "The path is empty");
|
||||
ensure!(
|
||||
item.file_type != FileType::Parent,
|
||||
"can not remove parent dir"
|
||||
);
|
||||
ensure!(item.path.is_dir(), "The path is not a dir");
|
||||
let doc = cx.editor.documents().find(|doc| {
|
||||
doc.path()
|
||||
.map(|p| p.starts_with(&item.path))
|
||||
.unwrap_or(false)
|
||||
});
|
||||
ensure!(doc.is_none(), "There are files opened under the dir");
|
||||
Ok(())
|
||||
};
|
||||
if let Err(e) = check() {
|
||||
cx.editor.set_error(format!("{e}"));
|
||||
return;
|
||||
}
|
||||
let p = format!("remove dir: {}, YES? ", item.path.display());
|
||||
self.prompt = Some((
|
||||
PromptAction::RemoveDir,
|
||||
Prompt::new(p.into(), None, ui::completers::none, |_, _, _| {}),
|
||||
));
|
||||
}
|
||||
|
||||
fn fold_current(item: &mut FileInfo, _cx: &mut Context, _state: &mut State) {
|
||||
if item.path.is_dir() && item.file_type != FileType::Root {
|
||||
item.expanded = false;
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle_current(
|
||||
item: &mut FileInfo,
|
||||
cx: &mut Context,
|
||||
state: &mut State,
|
||||
) -> TreeOp<FileInfo> {
|
||||
if item.file_type == FileType::Placeholder {
|
||||
return TreeOp::Noop;
|
||||
}
|
||||
if item.path == Path::new("") {
|
||||
return TreeOp::Noop;
|
||||
}
|
||||
let meta = match std::fs::metadata(&item.path) {
|
||||
Ok(meta) => meta,
|
||||
Err(e) => {
|
||||
cx.editor.set_error(format!("{e}"));
|
||||
return TreeOp::Noop;
|
||||
}
|
||||
};
|
||||
if meta.is_file() {
|
||||
if let Err(e) = cx.editor.open(&item.path.clone(), Action::Replace) {
|
||||
cx.editor.set_error(format!("{e}"));
|
||||
}
|
||||
state.focus = false;
|
||||
return TreeOp::Noop;
|
||||
}
|
||||
|
||||
if item.path.is_dir() {
|
||||
item.expanded = true;
|
||||
if cx.editor.config().explorer.is_list() || item.file_type == FileType::Parent {
|
||||
match Self::get_items(item.path.clone(), cx) {
|
||||
Ok(items) => {
|
||||
state.current_root = item.path.clone();
|
||||
return TreeOp::ReplaceTree(items);
|
||||
}
|
||||
Err(e) => cx.editor.set_error(format!("{e}")),
|
||||
}
|
||||
} else {
|
||||
return TreeOp::GetChildsAndInsert;
|
||||
}
|
||||
}
|
||||
cx.editor.set_error("unkonw file type");
|
||||
TreeOp::Noop
|
||||
}
|
||||
|
||||
fn render_float(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) {
|
||||
let background = cx.editor.theme.get("ui.background");
|
||||
let column_width = cx.editor.config().explorer.column_width as u16;
|
||||
surface.clear_with(area, background);
|
||||
let area = render_block(area, surface, Borders::ALL, None);
|
||||
|
||||
let mut preview_area = area.clip_left(column_width + 1);
|
||||
if let Some((_, prompt)) = self.prompt.as_mut() {
|
||||
let area = preview_area.clip_bottom(2);
|
||||
let promp_area = render_block(
|
||||
preview_area.clip_top(area.height),
|
||||
surface,
|
||||
Borders::TOP,
|
||||
None,
|
||||
);
|
||||
prompt.render(promp_area, surface, cx);
|
||||
preview_area = area;
|
||||
}
|
||||
self.render_preview(preview_area, surface, cx.editor);
|
||||
|
||||
let list_area = render_block(
|
||||
area.clip_right(preview_area.width),
|
||||
surface,
|
||||
Borders::RIGHT,
|
||||
None,
|
||||
);
|
||||
self.tree.render(list_area, surface, cx, &mut self.state);
|
||||
}
|
||||
|
||||
fn render_embed(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) {
|
||||
let config = &cx.editor.config().explorer;
|
||||
let side_area = area
|
||||
.with_width(area.width.min(config.column_width as u16 + 2))
|
||||
.clip_bottom(1);
|
||||
|
||||
let background = cx.editor.theme.get("ui.statusline");
|
||||
surface.clear_with(side_area, background);
|
||||
|
||||
let preview_area = area.clip_left(side_area.width).clip_bottom(2);
|
||||
let prompt_area = area.clip_top(side_area.height);
|
||||
|
||||
let border_style = cx.editor.theme.get("ui.explorer.border");
|
||||
let list_area = render_block(
|
||||
side_area.clip_left(1),
|
||||
surface,
|
||||
Borders::RIGHT,
|
||||
Some(border_style),
|
||||
)
|
||||
.clip_bottom(1);
|
||||
self.tree.render(list_area, surface, cx, &mut self.state);
|
||||
|
||||
{
|
||||
let statusline = if self.is_focus() {
|
||||
cx.editor.theme.get("ui.statusline")
|
||||
} else {
|
||||
cx.editor.theme.get("ui.statusline.inactive")
|
||||
};
|
||||
let area = side_area.clip_top(list_area.height).clip_right(1);
|
||||
surface.clear_with(area, statusline);
|
||||
}
|
||||
|
||||
if self.is_focus() {
|
||||
if preview_area.width < 30 || preview_area.height < 3 {
|
||||
return;
|
||||
}
|
||||
let width = preview_area.width.min(90);
|
||||
let mut y = self.tree.row().saturating_sub(1) as u16;
|
||||
let height = (preview_area.height).min(25);
|
||||
if (height + y) > preview_area.height {
|
||||
y = preview_area.height - height;
|
||||
}
|
||||
let area = Rect::new(preview_area.x, y, width, height);
|
||||
surface.clear_with(area, background);
|
||||
let area = render_block(area, surface, Borders::all(), None);
|
||||
self.render_preview(area, surface, cx.editor);
|
||||
}
|
||||
|
||||
if let Some((_, prompt)) = self.prompt.as_mut() {
|
||||
prompt.render_prompt(prompt_area, surface, cx)
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_filter_event(&mut self, event: &KeyEvent, cx: &mut Context) -> EventResult {
|
||||
let (action, mut prompt) = self.prompt.take().unwrap();
|
||||
match event {
|
||||
key!(Tab) | key!(Down) | ctrl!('j') => {
|
||||
self.tree.clean_recycle();
|
||||
return self
|
||||
.tree
|
||||
.handle_event(Event::Key(event.clone()), cx, &mut self.state);
|
||||
}
|
||||
key!(Enter) => {
|
||||
self.tree.clean_recycle();
|
||||
return self
|
||||
.tree
|
||||
.handle_event(Event::Key(event.clone()), cx, &mut self.state);
|
||||
}
|
||||
key!(Esc) | ctrl!('c') => self.tree.restore_recycle(),
|
||||
_ => {
|
||||
if let EventResult::Consumed(_) = prompt.handle_event(&Event::Key(*event), cx) {
|
||||
self.tree.filter(prompt.line(), cx, &mut self.state);
|
||||
}
|
||||
self.prompt = Some((action, prompt));
|
||||
}
|
||||
};
|
||||
EventResult::Consumed(None)
|
||||
}
|
||||
|
||||
fn handle_search_event(&mut self, event: &KeyEvent, cx: &mut Context) -> EventResult {
|
||||
let (action, mut prompt) = self.prompt.take().unwrap();
|
||||
let search_next = match action {
|
||||
PromptAction::Search(search_next) => search_next,
|
||||
_ => return EventResult::Ignored(None),
|
||||
};
|
||||
match event {
|
||||
key!(Tab) | key!(Down) | ctrl!('j') => {
|
||||
return self
|
||||
.tree
|
||||
.handle_event(Event::Key(event.clone()), cx, &mut self.state)
|
||||
}
|
||||
key!(Enter) => {
|
||||
let search_str = prompt.line().clone();
|
||||
if !search_str.is_empty() {
|
||||
self.repeat_motion = Some(Box::new(move |explorer, action, cx| {
|
||||
if let PromptAction::Search(is_next) = action {
|
||||
explorer.tree.save_view();
|
||||
if is_next == search_next {
|
||||
explorer
|
||||
.tree
|
||||
.search_next(cx, &search_str, &mut explorer.state);
|
||||
} else {
|
||||
explorer
|
||||
.tree
|
||||
.search_pre(cx, &search_str, &mut explorer.state);
|
||||
}
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
self.repeat_motion = None;
|
||||
}
|
||||
return self
|
||||
.tree
|
||||
.handle_event(Event::Key(event.clone()), cx, &mut self.state);
|
||||
}
|
||||
key!(Esc) | ctrl!('c') => self.tree.restore_view(),
|
||||
_ => {
|
||||
if let EventResult::Consumed(_) = prompt.handle_event(&Event::Key(*event), cx) {
|
||||
if search_next {
|
||||
self.tree.search_next(cx, prompt.line(), &mut self.state);
|
||||
} else {
|
||||
self.tree.search_pre(cx, prompt.line(), &mut self.state);
|
||||
}
|
||||
}
|
||||
self.prompt = Some((action, prompt));
|
||||
}
|
||||
};
|
||||
EventResult::Consumed(None)
|
||||
}
|
||||
|
||||
fn handle_prompt_event(&mut self, event: &KeyEvent, cx: &mut Context) -> EventResult {
|
||||
match &self.prompt {
|
||||
Some((PromptAction::Search(_), _)) => return self.handle_search_event(event, cx),
|
||||
Some((PromptAction::Filter, _)) => return self.handle_filter_event(event, cx),
|
||||
_ => {}
|
||||
};
|
||||
let (action, mut prompt) = match self.prompt.take() {
|
||||
Some((action, p)) => (action, p),
|
||||
_ => return EventResult::Ignored(None),
|
||||
};
|
||||
let line = prompt.line();
|
||||
match (action, event) {
|
||||
(PromptAction::Mkdir, key!(Enter)) => {
|
||||
if let Err(e) = self.new_path(line, true) {
|
||||
cx.editor.set_error(format!("{e}"))
|
||||
}
|
||||
}
|
||||
(PromptAction::CreateFile, key!(Enter)) => {
|
||||
if let Err(e) = self.new_path(line, false) {
|
||||
cx.editor.set_error(format!("{e}"))
|
||||
}
|
||||
}
|
||||
(PromptAction::RemoveDir, key!(Enter)) => {
|
||||
let item = self.tree.current_item();
|
||||
if let Err(e) = std::fs::remove_dir_all(&item.path) {
|
||||
cx.editor.set_error(format!("{e}"));
|
||||
} else {
|
||||
self.tree.fold_current_child();
|
||||
self.tree.remove_current();
|
||||
}
|
||||
}
|
||||
(PromptAction::RemoveFile, key!(Enter)) => {
|
||||
if line == "YES" {
|
||||
let item = self.tree.current_item();
|
||||
if let Err(e) = std::fs::remove_file(&item.path) {
|
||||
cx.editor.set_error(format!("{e}"));
|
||||
} else {
|
||||
self.tree.remove_current();
|
||||
}
|
||||
}
|
||||
}
|
||||
(_, key!(Esc) | ctrl!('c')) => {}
|
||||
_ => {
|
||||
prompt.handle_event(&Event::Key(*event), cx);
|
||||
self.prompt = Some((action, prompt));
|
||||
}
|
||||
}
|
||||
EventResult::Consumed(None)
|
||||
}
|
||||
|
||||
fn new_path(&mut self, file_name: &str, is_dir: bool) -> Result<()> {
|
||||
let current = self.tree.current_item();
|
||||
let current_parent = if current.file_type == FileType::Placeholder {
|
||||
¤t.path
|
||||
} else {
|
||||
current
|
||||
.path
|
||||
.parent()
|
||||
.ok_or_else(|| anyhow::anyhow!("can not get parent dir"))?
|
||||
};
|
||||
let p = helix_core::path::get_normalized_path(¤t_parent.join(file_name));
|
||||
match p.parent() {
|
||||
Some(p) if p == current_parent => {}
|
||||
_ => bail!("The file name is not illegal"),
|
||||
};
|
||||
|
||||
let f = if is_dir {
|
||||
std::fs::create_dir(&p)?;
|
||||
FileInfo::new(p, FileType::Dir)
|
||||
} else {
|
||||
let mut fd = std::fs::OpenOptions::new();
|
||||
fd.create_new(true).write(true).open(&p)?;
|
||||
FileInfo::new(p, FileType::File)
|
||||
};
|
||||
if current.file_type == FileType::Placeholder {
|
||||
self.tree.replace_current(f);
|
||||
} else {
|
||||
self.tree.insert_current_level(f);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Explorer {
|
||||
/// Process input events, return true if handled.
|
||||
fn handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult {
|
||||
let key_event = match event {
|
||||
Event::Key(event) => *event,
|
||||
Event::Resize(..) => return EventResult::Consumed(None),
|
||||
_ => return EventResult::Ignored(None),
|
||||
};
|
||||
if !self.is_focus() {
|
||||
return EventResult::Ignored(None);
|
||||
}
|
||||
if let Some(mut on_next_key) = self.on_next_key.take() {
|
||||
return on_next_key(cx, self, &key_event);
|
||||
}
|
||||
|
||||
if let EventResult::Consumed(c) = self.handle_prompt_event(&key_event, cx) {
|
||||
return EventResult::Consumed(c);
|
||||
}
|
||||
|
||||
let close_fn = EventResult::Consumed(Some(Box::new(|compositor: &mut Compositor, _| {
|
||||
if let Some(editor) = compositor.find::<ui::EditorView>() {
|
||||
editor.explorer = None;
|
||||
}
|
||||
})));
|
||||
|
||||
match key_event {
|
||||
key!(Esc) => self.unfocus(),
|
||||
ctrl!('c') => return close_fn,
|
||||
key!('n') => {
|
||||
if let Some(mut repeat_motion) = self.repeat_motion.take() {
|
||||
repeat_motion(self, PromptAction::Search(true), cx);
|
||||
self.repeat_motion = Some(repeat_motion);
|
||||
}
|
||||
}
|
||||
shift!('N') => {
|
||||
if let Some(mut repeat_motion) = self.repeat_motion.take() {
|
||||
repeat_motion(self, PromptAction::Search(false), cx);
|
||||
self.repeat_motion = Some(repeat_motion);
|
||||
}
|
||||
}
|
||||
key!('f') => self.new_filter_prompt(),
|
||||
key!('/') => self.new_search_prompt(true),
|
||||
key!('?') => self.new_search_prompt(false),
|
||||
key!('m') => {
|
||||
self.on_next_key = Some(Box::new(|_, explorer, event| {
|
||||
match event {
|
||||
key!('d') => explorer.new_mkdir_prompt(),
|
||||
key!('f') => explorer.new_create_file_prompt(),
|
||||
_ => return EventResult::Ignored(None),
|
||||
};
|
||||
EventResult::Consumed(None)
|
||||
}));
|
||||
}
|
||||
key!('r') => {
|
||||
self.on_next_key = Some(Box::new(|cx, explorer, event| {
|
||||
match event {
|
||||
key!('d') => explorer.new_remove_dir_prompt(cx),
|
||||
key!('f') => explorer.new_remove_file_prompt(cx),
|
||||
_ => return EventResult::Ignored(None),
|
||||
};
|
||||
EventResult::Consumed(None)
|
||||
}));
|
||||
}
|
||||
_ => {
|
||||
self.tree
|
||||
.handle_event(Event::Key(key_event.clone()), cx, &mut self.state);
|
||||
}
|
||||
}
|
||||
|
||||
EventResult::Consumed(None)
|
||||
}
|
||||
|
||||
fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) {
|
||||
if area.width < 10 || area.height < 5 {
|
||||
cx.editor.set_error("explorer render area is too small");
|
||||
return;
|
||||
}
|
||||
let config = &cx.editor.config().explorer;
|
||||
if config.is_embed() {
|
||||
self.render_embed(area, surface, cx);
|
||||
} else {
|
||||
self.render_float(area, surface, cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn cursor(&self, area: Rect, editor: &Editor) -> (Option<Position>, CursorKind) {
|
||||
let prompt = match self.prompt.as_ref() {
|
||||
Some((_, prompt)) => prompt,
|
||||
None => return (None, CursorKind::Hidden),
|
||||
};
|
||||
let config = &editor.config().explorer;
|
||||
let (x, y) = if config.is_overlay() {
|
||||
let colw = config.column_width as u16;
|
||||
if area.width > colw {
|
||||
(area.x + colw + 2, area.y + area.height - 2)
|
||||
} else {
|
||||
return (None, CursorKind::Hidden);
|
||||
}
|
||||
} else {
|
||||
(area.x, area.y + area.height - 1)
|
||||
};
|
||||
prompt.cursor(Rect::new(x, y, area.width, 1), editor)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_preview(p: impl AsRef<Path>, max_line: usize) -> Result<Vec<String>> {
|
||||
let p = p.as_ref();
|
||||
if p.is_dir() {
|
||||
return Ok(p
|
||||
.read_dir()?
|
||||
.filter_map(|entry| entry.ok())
|
||||
.take(max_line)
|
||||
.map(|entry| {
|
||||
if entry.file_type().map(|ft| ft.is_dir()).unwrap_or(false) {
|
||||
format!("{}/", entry.file_name().to_string_lossy())
|
||||
} else {
|
||||
format!("{}", entry.file_name().to_string_lossy())
|
||||
}
|
||||
})
|
||||
.collect());
|
||||
}
|
||||
|
||||
ensure!(p.is_file(), "path: {} is not file or dir", p.display());
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::BufRead;
|
||||
let mut fd = OpenOptions::new();
|
||||
fd.read(true);
|
||||
let fd = fd.open(p)?;
|
||||
Ok(std::io::BufReader::new(fd)
|
||||
.lines()
|
||||
.take(max_line)
|
||||
.filter_map(|line| line.ok())
|
||||
.map(|line| line.replace('\t', " "))
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn render_block(
|
||||
area: Rect,
|
||||
surface: &mut Surface,
|
||||
borders: Borders,
|
||||
border_style: Option<helix_view::theme::Style>,
|
||||
) -> Rect {
|
||||
let mut block = Block::default().borders(borders);
|
||||
if let Some(style) = border_style {
|
||||
block = block.border_style(style);
|
||||
}
|
||||
let inner = block.inner(area);
|
||||
block.render(area, surface);
|
||||
inner
|
||||
}
|
@ -0,0 +1,707 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::iter::Peekable;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::{
|
||||
compositor::{Context, EventResult},
|
||||
ctrl, key, shift,
|
||||
};
|
||||
use helix_core::unicode::width::UnicodeWidthStr;
|
||||
use helix_view::{
|
||||
graphics::Rect,
|
||||
input::{Event, KeyEvent},
|
||||
};
|
||||
use tui::{buffer::Buffer as Surface, text::Spans};
|
||||
|
||||
pub trait TreeItem: Sized {
|
||||
type Params;
|
||||
|
||||
fn text(&self, cx: &mut Context, selected: bool, params: &mut Self::Params) -> Spans;
|
||||
fn is_child(&self, other: &Self) -> bool;
|
||||
fn cmp(&self, other: &Self) -> Ordering;
|
||||
fn icon(&self) -> Option<(&'static str, &'static helix_view::theme::Color)>;
|
||||
|
||||
fn filter(&self, cx: &mut Context, s: &str, params: &mut Self::Params) -> bool {
|
||||
self.text(cx, false, params)
|
||||
.0
|
||||
.into_iter()
|
||||
.map(|s| s.content)
|
||||
.collect::<Vec<_>>()
|
||||
.concat()
|
||||
.contains(s)
|
||||
}
|
||||
|
||||
fn get_childs(&self) -> Result<Vec<Self>> {
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
fn tree_item_cmp<T: TreeItem>(item1: &T, item2: &T) -> Ordering {
|
||||
if item1.is_child(item2) {
|
||||
return Ordering::Greater;
|
||||
}
|
||||
if item2.is_child(item1) {
|
||||
return Ordering::Less;
|
||||
}
|
||||
|
||||
T::cmp(item1, item2)
|
||||
}
|
||||
|
||||
fn vec_to_tree<T: TreeItem>(mut items: Vec<T>, level: usize) -> Vec<Elem<T>> {
|
||||
fn get_childs<T, Iter>(iter: &mut Peekable<Iter>, elem: &mut Elem<T>)
|
||||
where
|
||||
T: TreeItem,
|
||||
Iter: Iterator<Item = T>,
|
||||
{
|
||||
let level = elem.level + 1;
|
||||
loop {
|
||||
if !iter.peek().map_or(false, |next| next.is_child(&elem.item)) {
|
||||
break;
|
||||
}
|
||||
let mut child = Elem::new(iter.next().unwrap(), level);
|
||||
if iter.peek().map_or(false, |nc| nc.is_child(&child.item)) {
|
||||
get_childs(iter, &mut child);
|
||||
}
|
||||
elem.folded.push(child);
|
||||
}
|
||||
}
|
||||
|
||||
items.sort_by(tree_item_cmp);
|
||||
let mut elems = Vec::with_capacity(items.len());
|
||||
let mut iter = items.into_iter().peekable();
|
||||
while let Some(item) = iter.next() {
|
||||
let mut elem = Elem::new(item, level);
|
||||
if iter.peek().map_or(false, |next| next.is_child(&elem.item)) {
|
||||
get_childs(&mut iter, &mut elem);
|
||||
}
|
||||
expand_elems(&mut elems, elem);
|
||||
}
|
||||
elems
|
||||
}
|
||||
|
||||
// return total elems's count contain self
|
||||
fn get_elems_recursion<T: TreeItem>(t: &mut Elem<T>, depth: usize) -> Result<usize> {
|
||||
let mut childs = t.item.get_childs()?;
|
||||
childs.sort_by(tree_item_cmp);
|
||||
let mut elems = Vec::with_capacity(childs.len());
|
||||
let level = t.level + 1;
|
||||
let mut total = 1;
|
||||
for child in childs {
|
||||
let mut elem = Elem::new(child, level);
|
||||
let count = if depth > 0 {
|
||||
get_elems_recursion(&mut elem, depth - 1)?
|
||||
} else {
|
||||
1
|
||||
};
|
||||
elems.push(elem);
|
||||
total += count;
|
||||
}
|
||||
t.folded = elems;
|
||||
Ok(total)
|
||||
}
|
||||
|
||||
fn expand_elems<T: TreeItem>(dist: &mut Vec<Elem<T>>, mut t: Elem<T>) {
|
||||
let childs = std::mem::take(&mut t.folded);
|
||||
dist.push(t);
|
||||
for child in childs {
|
||||
expand_elems(dist, child)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum TreeOp<T> {
|
||||
Noop,
|
||||
Restore,
|
||||
InsertChild(Vec<T>),
|
||||
GetChildsAndInsert,
|
||||
ReplaceTree(Vec<T>),
|
||||
}
|
||||
|
||||
pub struct Elem<T> {
|
||||
item: T,
|
||||
level: usize,
|
||||
folded: Vec<Self>,
|
||||
}
|
||||
|
||||
impl<T: Clone> Clone for Elem<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
item: self.item.clone(),
|
||||
level: self.level,
|
||||
folded: self.folded.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Elem<T> {
|
||||
pub fn new(item: T, level: usize) -> Self {
|
||||
Self {
|
||||
item,
|
||||
level,
|
||||
folded: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn item(&self) -> &T {
|
||||
&self.item
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Tree<T: TreeItem> {
|
||||
items: Vec<Elem<T>>,
|
||||
recycle: Option<(String, Vec<Elem<T>>)>,
|
||||
selected: usize,
|
||||
save_view: (usize, usize), // (selected, row)
|
||||
row: usize,
|
||||
col: usize,
|
||||
max_len: usize,
|
||||
count: usize,
|
||||
tree_symbol_style: String,
|
||||
#[allow(clippy::type_complexity)]
|
||||
pre_render: Option<Box<dyn Fn(&mut Self, Rect) + 'static>>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
on_opened_fn:
|
||||
Option<Box<dyn FnMut(&mut T, &mut Context, &mut T::Params) -> TreeOp<T> + 'static>>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
on_folded_fn: Option<Box<dyn FnMut(&mut T, &mut Context, &mut T::Params) + 'static>>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
on_next_key: Option<Box<dyn FnMut(&mut Context, &mut Self, KeyEvent)>>,
|
||||
}
|
||||
|
||||
impl<T: TreeItem> Tree<T> {
|
||||
pub fn new(items: Vec<Elem<T>>) -> Self {
|
||||
Self {
|
||||
items,
|
||||
recycle: None,
|
||||
selected: 0,
|
||||
save_view: (0, 0),
|
||||
row: 0,
|
||||
col: 0,
|
||||
max_len: 0,
|
||||
count: 0,
|
||||
tree_symbol_style: "ui.explorer.guide".into(),
|
||||
pre_render: None,
|
||||
on_opened_fn: None,
|
||||
on_folded_fn: None,
|
||||
on_next_key: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace_with_new_items(&mut self, items: Vec<T>) {
|
||||
let old = std::mem::replace(self, Self::new(vec_to_tree(items, 0)));
|
||||
self.on_opened_fn = old.on_opened_fn;
|
||||
self.on_folded_fn = old.on_folded_fn;
|
||||
self.tree_symbol_style = old.tree_symbol_style;
|
||||
}
|
||||
|
||||
pub fn build_tree(items: Vec<T>) -> Self {
|
||||
Self::new(vec_to_tree(items, 0))
|
||||
}
|
||||
|
||||
pub fn build_from_root(t: T, depth: usize) -> Result<Self> {
|
||||
let mut elem = Elem::new(t, 0);
|
||||
let count = get_elems_recursion(&mut elem, depth)?;
|
||||
let mut elems = Vec::with_capacity(count);
|
||||
expand_elems(&mut elems, elem);
|
||||
Ok(Self::new(elems))
|
||||
}
|
||||
|
||||
pub fn with_enter_fn<F>(mut self, f: F) -> Self
|
||||
where
|
||||
F: FnMut(&mut T, &mut Context, &mut T::Params) -> TreeOp<T> + 'static,
|
||||
{
|
||||
self.on_opened_fn = Some(Box::new(f));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_folded_fn<F>(mut self, f: F) -> Self
|
||||
where
|
||||
F: FnMut(&mut T, &mut Context, &mut T::Params) + 'static,
|
||||
{
|
||||
self.on_folded_fn = Some(Box::new(f));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn tree_symbol_style(mut self, style: String) -> Self {
|
||||
self.tree_symbol_style = style;
|
||||
self
|
||||
}
|
||||
|
||||
fn next_item(&self) -> Option<&Elem<T>> {
|
||||
self.items.get(self.selected + 1)
|
||||
}
|
||||
|
||||
fn next_not_descendant_pos(&self, index: usize) -> usize {
|
||||
let item = &self.items[index];
|
||||
self.find(index + 1, false, |n| n.level <= item.level)
|
||||
.unwrap_or(self.items.len())
|
||||
}
|
||||
|
||||
fn find_parent(&self, index: usize) -> Option<usize> {
|
||||
let item = &self.items[index];
|
||||
self.find(index, true, |p| p.level < item.level)
|
||||
}
|
||||
|
||||
// rev start: start - 1
|
||||
fn find<F>(&self, start: usize, rev: bool, f: F) -> Option<usize>
|
||||
where
|
||||
F: FnMut(&Elem<T>) -> bool,
|
||||
{
|
||||
let iter = self.items.iter();
|
||||
if rev {
|
||||
iter.take(start).rposition(f)
|
||||
} else {
|
||||
iter.skip(start).position(f).map(|p| p + start)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TreeItem> Tree<T> {
|
||||
pub fn on_enter(&mut self, cx: &mut Context, params: &mut T::Params) {
|
||||
if self.items.is_empty() {
|
||||
return;
|
||||
}
|
||||
if let Some(next_level) = self.next_item().map(|elem| elem.level) {
|
||||
let current = &mut self.items[self.selected];
|
||||
let current_level = current.level;
|
||||
if next_level > current_level {
|
||||
if let Some(mut on_folded_fn) = self.on_folded_fn.take() {
|
||||
on_folded_fn(&mut current.item, cx, params);
|
||||
self.on_folded_fn = Some(on_folded_fn);
|
||||
}
|
||||
self.fold_current_child();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(mut on_open_fn) = self.on_opened_fn.take() {
|
||||
let mut f = || {
|
||||
let current = &mut self.items[self.selected];
|
||||
let items = match on_open_fn(&mut current.item, cx, params) {
|
||||
TreeOp::Restore => {
|
||||
let inserts = std::mem::take(&mut current.folded);
|
||||
let _: Vec<_> = self
|
||||
.items
|
||||
.splice(self.selected + 1..self.selected + 1, inserts)
|
||||
.collect();
|
||||
return;
|
||||
}
|
||||
TreeOp::InsertChild(items) => items,
|
||||
TreeOp::GetChildsAndInsert => match current.item.get_childs() {
|
||||
Ok(items) => items,
|
||||
Err(e) => return cx.editor.set_error(format!("{e}")),
|
||||
},
|
||||
TreeOp::ReplaceTree(items) => return self.replace_with_new_items(items),
|
||||
TreeOp::Noop => return,
|
||||
};
|
||||
current.folded = vec![];
|
||||
let inserts = vec_to_tree(items, current.level + 1);
|
||||
let _: Vec<_> = self
|
||||
.items
|
||||
.splice(self.selected + 1..self.selected + 1, inserts)
|
||||
.collect();
|
||||
};
|
||||
f();
|
||||
self.on_opened_fn = Some(on_open_fn)
|
||||
} else {
|
||||
let current = &mut self.items[self.selected];
|
||||
let inserts = std::mem::take(&mut current.folded);
|
||||
let _: Vec<_> = self
|
||||
.items
|
||||
.splice(self.selected + 1..self.selected + 1, inserts)
|
||||
.collect();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fold_current_level(&mut self) {
|
||||
let start = match self.find_parent(self.selected) {
|
||||
Some(start) => start,
|
||||
None => return,
|
||||
};
|
||||
self.selected = start;
|
||||
self.fold_current_child();
|
||||
}
|
||||
|
||||
pub fn fold_current_child(&mut self) {
|
||||
if self.selected + 1 >= self.items.len() {
|
||||
return;
|
||||
}
|
||||
let pos = self.next_not_descendant_pos(self.selected);
|
||||
if self.selected < pos {
|
||||
self.items[self.selected].folded = self.items.drain(self.selected + 1..pos).collect();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn search_next(&mut self, cx: &mut Context, s: &str, params: &mut T::Params) {
|
||||
let skip = self.save_view.0 + 1;
|
||||
self.selected = self
|
||||
.find(skip, false, |e| e.item.filter(cx, s, params))
|
||||
.unwrap_or(self.save_view.0);
|
||||
|
||||
self.row = (self.save_view.1 + self.selected).saturating_sub(self.save_view.0);
|
||||
}
|
||||
|
||||
pub fn search_pre(&mut self, cx: &mut Context, s: &str, params: &mut T::Params) {
|
||||
let take = self.save_view.0;
|
||||
self.selected = self
|
||||
.find(take, true, |e| e.item.filter(cx, s, params))
|
||||
.unwrap_or(self.save_view.0);
|
||||
|
||||
self.row = (self.save_view.1 + self.selected).saturating_sub(self.save_view.0);
|
||||
}
|
||||
|
||||
pub fn move_down(&mut self, rows: usize) {
|
||||
let len = self.items.len();
|
||||
if len > 0 {
|
||||
self.selected = std::cmp::min(self.selected + rows, len.saturating_sub(1));
|
||||
self.row = std::cmp::min(self.selected, self.row + rows);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_up(&mut self, rows: usize) {
|
||||
let len = self.items.len();
|
||||
if len > 0 {
|
||||
self.selected = self.selected.saturating_sub(rows);
|
||||
self.row = std::cmp::min(self.selected, self.row.saturating_sub(rows));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_left(&mut self, cols: usize) {
|
||||
self.col = self.col.saturating_sub(cols);
|
||||
}
|
||||
|
||||
pub fn move_right(&mut self, cols: usize) {
|
||||
self.pre_render = Some(Box::new(move |tree: &mut Self, area: Rect| {
|
||||
let max_scroll = tree.max_len.saturating_sub(area.width as usize);
|
||||
tree.col = max_scroll.min(tree.col + cols);
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn move_down_half_page(&mut self) {
|
||||
self.pre_render = Some(Box::new(|tree: &mut Self, area: Rect| {
|
||||
tree.move_down((area.height / 2) as usize);
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn move_up_half_page(&mut self) {
|
||||
self.pre_render = Some(Box::new(|tree: &mut Self, area: Rect| {
|
||||
tree.move_up((area.height / 2) as usize);
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn move_down_page(&mut self) {
|
||||
self.pre_render = Some(Box::new(|tree: &mut Self, area: Rect| {
|
||||
tree.move_down((area.height) as usize);
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn move_up_page(&mut self) {
|
||||
self.pre_render = Some(Box::new(|tree: &mut Self, area: Rect| {
|
||||
tree.move_up((area.height) as usize);
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn save_view(&mut self) {
|
||||
self.save_view = (self.selected, self.row);
|
||||
}
|
||||
|
||||
pub fn restore_view(&mut self) {
|
||||
(self.selected, self.row) = self.save_view;
|
||||
}
|
||||
|
||||
pub fn current(&self) -> &Elem<T> {
|
||||
&self.items[self.selected]
|
||||
}
|
||||
|
||||
pub fn current_item(&self) -> &T {
|
||||
&self.items[self.selected].item
|
||||
}
|
||||
|
||||
pub fn row(&self) -> usize {
|
||||
self.row
|
||||
}
|
||||
|
||||
pub fn remove_current(&mut self) -> T {
|
||||
let elem = self.items.remove(self.selected);
|
||||
self.selected = self.selected.saturating_sub(1);
|
||||
elem.item
|
||||
}
|
||||
|
||||
pub fn replace_current(&mut self, item: T) {
|
||||
self.items[self.selected].item = item;
|
||||
}
|
||||
|
||||
pub fn select(&mut self, select_item: &T) {
|
||||
let selected = self
|
||||
.items
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, i)| i.item.cmp(select_item) == Ordering::Equal)
|
||||
.next();
|
||||
if let Some((idx, _)) = selected {
|
||||
self.selected = idx;
|
||||
self.row = idx;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_current_level(&mut self, item: T) {
|
||||
let current = self.current();
|
||||
let level = current.level;
|
||||
let pos = match current.item.cmp(&item) {
|
||||
Ordering::Less => self
|
||||
.find(self.selected + 1, false, |e| {
|
||||
e.level < level || (e.level == level && e.item.cmp(&item) != Ordering::Less)
|
||||
})
|
||||
.unwrap_or(self.items.len()),
|
||||
|
||||
Ordering::Greater => {
|
||||
match self.find(self.selected, true, |elem| {
|
||||
elem.level < level
|
||||
|| (elem.level == level && elem.item.cmp(&item) != Ordering::Greater)
|
||||
}) {
|
||||
Some(p) if self.items[p].level == level => self.next_not_descendant_pos(p),
|
||||
Some(p) => p + 1,
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
Ordering::Equal => self.selected + 1,
|
||||
};
|
||||
self.items.insert(pos, Elem::new(item, level));
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TreeItem> Tree<T> {
|
||||
pub fn render(
|
||||
&mut self,
|
||||
area: Rect,
|
||||
surface: &mut Surface,
|
||||
cx: &mut Context,
|
||||
params: &mut T::Params,
|
||||
) {
|
||||
if let Some(pre_render) = self.pre_render.take() {
|
||||
pre_render(self, area);
|
||||
}
|
||||
|
||||
self.max_len = 0;
|
||||
self.row = std::cmp::min(self.row, area.height.saturating_sub(1) as usize);
|
||||
let style = cx.editor.theme.get(&self.tree_symbol_style);
|
||||
let folder_style = cx.editor.theme.get("special");
|
||||
let last_item_index = self.items.len().saturating_sub(1);
|
||||
let skip = self.selected.saturating_sub(self.row);
|
||||
let iter = self
|
||||
.items
|
||||
.iter()
|
||||
.skip(skip)
|
||||
.take(area.height as usize)
|
||||
.enumerate();
|
||||
for (index, elem) in iter {
|
||||
let row = index as u16;
|
||||
let mut area = Rect::new(area.x, area.y + row, area.width, 1);
|
||||
let indent = if elem.level > 0 {
|
||||
if index + skip != last_item_index {
|
||||
format!("{}", "│ ".repeat(elem.level - 1))
|
||||
} else {
|
||||
format!("{}", "".repeat(elem.level - 1))
|
||||
}
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
|
||||
let indent_len = indent.chars().count();
|
||||
if indent_len > self.col {
|
||||
let indent: String = indent.chars().skip(self.col).collect();
|
||||
if !indent.is_empty() {
|
||||
surface.set_stringn(area.x, area.y, &indent, area.width as usize, style);
|
||||
area = area.clip_left(indent.width() as u16);
|
||||
}
|
||||
};
|
||||
let mut start_index = self.col.saturating_sub(indent_len);
|
||||
let mut text = elem.item.text(cx, skip + index == self.selected, params);
|
||||
self.max_len = self.max_len.max(text.width() + indent.len() - 2);
|
||||
for span in text.0.iter_mut() {
|
||||
if area.width == 0 {
|
||||
return;
|
||||
}
|
||||
if start_index == 0 {
|
||||
let mut icon_offset = 0;
|
||||
if let Some((icon, color)) = elem.item.icon() {
|
||||
let style = folder_style.fg(*color);
|
||||
surface.set_string(area.x, area.y, icon, style);
|
||||
icon_offset = 2;
|
||||
}
|
||||
surface.set_span(area.x + icon_offset, area.y, span, area.width - icon_offset);
|
||||
area = area.clip_left((span.width() - icon_offset as usize) as u16);
|
||||
} else {
|
||||
let span_width = span.width();
|
||||
if start_index > span_width {
|
||||
start_index -= span_width;
|
||||
} else {
|
||||
let content: String = span
|
||||
.content
|
||||
.chars()
|
||||
.filter(|c| {
|
||||
if start_index > 0 {
|
||||
start_index = start_index.saturating_sub(c.to_string().width());
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let mut cont = String::new();
|
||||
cont.push_str("");
|
||||
cont.push_str(&content);
|
||||
surface.set_string_truncated(
|
||||
area.x,
|
||||
area.y,
|
||||
&cont,
|
||||
area.width as usize,
|
||||
|_| span.style,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
start_index = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_event(
|
||||
&mut self,
|
||||
event: Event,
|
||||
cx: &mut Context,
|
||||
params: &mut T::Params,
|
||||
) -> EventResult {
|
||||
let key_event = match event {
|
||||
Event::Key(event) => event,
|
||||
Event::Resize(..) => return EventResult::Consumed(None),
|
||||
_ => return EventResult::Ignored(None),
|
||||
};
|
||||
if let Some(mut on_next_key) = self.on_next_key.take() {
|
||||
on_next_key(cx, self, key_event);
|
||||
return EventResult::Consumed(None);
|
||||
}
|
||||
let count = std::mem::replace(&mut self.count, 0);
|
||||
match key_event.into() {
|
||||
key!(i @ '0'..='9') => self.count = i.to_digit(10).unwrap() as usize + count * 10,
|
||||
key!('k') | shift!(Tab) | key!(Up) => self.move_up(1.max(count)),
|
||||
key!('j') | key!(Tab) | key!(Down) => self.move_down(1.max(count)),
|
||||
key!('z') => self.fold_current_level(),
|
||||
key!('h') => self.move_left(1.max(count)),
|
||||
key!('l') => self.move_right(1.max(count)),
|
||||
shift!('G') => self.move_down(usize::MAX / 2),
|
||||
key!(Enter) => self.on_enter(cx, params),
|
||||
key!(' ') => self.on_enter(cx, params),
|
||||
ctrl!('d') | ctrl!('j') => self.move_down_half_page(),
|
||||
ctrl!('u') | ctrl!('k') => self.move_up_half_page(),
|
||||
shift!('D') => self.move_down_page(),
|
||||
shift!('U') => self.move_up_page(),
|
||||
key!('g') => {
|
||||
self.on_next_key = Some(Box::new(|_, tree, event| match event.into() {
|
||||
key!('g') => tree.move_up(usize::MAX / 2),
|
||||
key!('e') => tree.move_down(usize::MAX / 2),
|
||||
_ => {}
|
||||
}));
|
||||
}
|
||||
_ => return EventResult::Ignored(None),
|
||||
}
|
||||
|
||||
EventResult::Consumed(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TreeItem + Clone> Tree<T> {
|
||||
pub fn filter(&mut self, s: &str, cx: &mut Context, params: &mut T::Params) {
|
||||
fn filter_recursion<T>(
|
||||
elems: &Vec<Elem<T>>,
|
||||
mut index: usize,
|
||||
s: &str,
|
||||
cx: &mut Context,
|
||||
params: &mut T::Params,
|
||||
) -> (Vec<Elem<T>>, usize)
|
||||
where
|
||||
T: TreeItem + Clone,
|
||||
{
|
||||
let mut retain = vec![];
|
||||
let elem = &elems[index];
|
||||
loop {
|
||||
let child = match elems.get(index + 1) {
|
||||
Some(child) if child.item.is_child(&elem.item) => child,
|
||||
_ => break,
|
||||
};
|
||||
index += 1;
|
||||
let next = elems.get(index + 1);
|
||||
if next.map_or(false, |n| n.item.is_child(&child.item)) {
|
||||
let (sub_retain, current_index) = filter_recursion(elems, index, s, cx, params);
|
||||
retain.extend(sub_retain);
|
||||
index = current_index;
|
||||
} else if child.item.filter(cx, s, params) {
|
||||
retain.push(child.clone());
|
||||
}
|
||||
}
|
||||
if !retain.is_empty() || elem.item.filter(cx, s, params) {
|
||||
retain.insert(0, elem.clone());
|
||||
}
|
||||
(retain, index)
|
||||
}
|
||||
|
||||
if s.is_empty() {
|
||||
if let Some((_, recycle)) = self.recycle.take() {
|
||||
self.items = recycle;
|
||||
self.restore_view();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let mut retain = vec![];
|
||||
let mut index = 0;
|
||||
let items = match &self.recycle {
|
||||
Some((pre, _)) if pre == s => return,
|
||||
Some((pre, recycle)) if pre.contains(s) => recycle,
|
||||
_ => &self.items,
|
||||
};
|
||||
while let Some(elem) = items.get(index) {
|
||||
let next = items.get(index + 1);
|
||||
if next.map_or(false, |n| n.item.is_child(&elem.item)) {
|
||||
let (sub_items, current_index) = filter_recursion(items, index, s, cx, params);
|
||||
index = current_index;
|
||||
retain.extend(sub_items);
|
||||
} else if elem.item.filter(cx, s, params) {
|
||||
retain.push(elem.clone())
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
|
||||
if retain.is_empty() {
|
||||
if let Some((_, recycle)) = self.recycle.take() {
|
||||
self.items = recycle;
|
||||
self.restore_view();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let recycle = std::mem::replace(&mut self.items, retain);
|
||||
if let Some(r) = self.recycle.as_mut() {
|
||||
r.0 = s.into()
|
||||
} else {
|
||||
self.recycle = Some((s.into(), recycle));
|
||||
self.save_view();
|
||||
}
|
||||
|
||||
self.selected = self
|
||||
.find(0, false, |elem| elem.item.filter(cx, s, params))
|
||||
.unwrap_or(0);
|
||||
self.row = self.selected;
|
||||
}
|
||||
|
||||
pub fn clean_recycle(&mut self) {
|
||||
self.recycle = None;
|
||||
}
|
||||
|
||||
pub fn restore_recycle(&mut self) {
|
||||
if let Some((_, recycle)) = self.recycle.take() {
|
||||
self.items = recycle;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
# Author : Sam Sartor <me@samsartor.com>, Trivernis <trivernis@pm.me>
|
||||
# A port of https://github.com/bceskavich/dracula-at-night
|
||||
"comment" = { fg = "comment" }
|
||||
"constant" = { fg = "purple" }
|
||||
"constant.character.escape" = { fg = "pink" }
|
||||
"function" = { fg = "green" }
|
||||
"keyword" = { fg = "pink" }
|
||||
"operator" = { fg = "pink" }
|
||||
"special" = { fg = "yellow" }
|
||||
"punctuation" = { fg = "foreground" }
|
||||
"string" = { fg = "yellow" }
|
||||
"string.regexp" = { fg = "red" }
|
||||
"tag" = { fg = "pink" }
|
||||
"attribute" = { fg = "cyan" }
|
||||
"type" = { fg = "cyan", modifiers = ["italic"] }
|
||||
"type.enum.variant" = { fg = "foreground", modifiers = ["italic"] }
|
||||
"variable" = { fg = "foreground" }
|
||||
"variable.builtin" = { fg = "cyan", modifiers = ["italic"] }
|
||||
"variable.parameter" = { fg ="orange", modifiers = ["italic"] }
|
||||
|
||||
"diff.plus" = { fg = "green" }
|
||||
"diff.delta" = { fg = "orange" }
|
||||
"diff.minus" = { fg = "red" }
|
||||
|
||||
"ui.background" = { fg = "foreground", bg = "background" }
|
||||
"ui.cursor" = { fg = "background", bg = "orange", modifiers = ["dim"] }
|
||||
"ui.cursor.match" = { fg = "green", modifiers = ["underlined"] }
|
||||
"ui.cursor.primary" = { fg = "background", bg = "cyan", modifier = ["dim"] }
|
||||
"ui.cursorline" = {bg = "background_dark"}
|
||||
"ui.help" = { fg = "foreground", bg = "background_dark" }
|
||||
"ui.linenr" = { fg = "comment" }
|
||||
"ui.linenr.selected" = { fg = "foreground" }
|
||||
"ui.menu" = { fg = "foreground", bg = "background_dark" }
|
||||
"ui.menu.selected" = { fg = "cyan", bg = "background_dark" }
|
||||
"ui.popup" = { fg = "foreground", bg = "background_dark" }
|
||||
"ui.selection" = { fg = "background", bg = "purple", modifiers = ["dim"] }
|
||||
"ui.selection.primary" = { fg = "background", bg = "pink" }
|
||||
"ui.text" = { fg = "foreground" }
|
||||
"ui.text.focus" = { fg = "cyan" }
|
||||
"ui.window" = { fg = "foreground" }
|
||||
"ui.virtual.ruler" = { bg = "ruler" }
|
||||
"ui.virtual.indent-guide" = { fg = "ruler" }
|
||||
|
||||
"ui.statusline" = { fg = "foreground", bg = "background_dark" }
|
||||
"ui.statusline.inactive" = { fg = "comment", bg = "background_dark" }
|
||||
"ui.statusline.normal" = { fg = "background_dark", bg = "purple"}
|
||||
"ui.statusline.insert" = { fg = "background_dark", bg = "pink"}
|
||||
"ui.statusline.select" = { fg = "background_dark", bg = "cyan"}
|
||||
|
||||
"error" = { fg = "red" }
|
||||
"warning" = { fg = "cyan" }
|
||||
|
||||
"markup.heading" = { fg = "purple", modifiers = ["bold"] }
|
||||
"markup.list" = "cyan"
|
||||
"markup.bold" = { fg = "orange", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "yellow", modifiers = ["italic"] }
|
||||
"markup.link.url" = "cyan"
|
||||
"markup.link.text" = "pink"
|
||||
"markup.quote" = { fg = "yellow", modifiers = ["italic"] }
|
||||
"markup.raw" = { fg = "foreground" }
|
||||
|
||||
"ui.explorer.file" = { fg = "foreground" }
|
||||
"ui.explorer.dir" = { fg = "cyan" }
|
||||
"ui.explorer.exe" = { fg = "foreground" }
|
||||
"ui.explorer.focus" = { modifiers = ["reversed"] }
|
||||
"ui.explorer.unfocus" = { bg = "comment" }
|
||||
|
||||
rainbow = ["#7c5ea3", "#9c5b95", "#9c5e80", "#6b4466"]
|
||||
|
||||
[palette]
|
||||
background = "#3A2A4D"
|
||||
background_dark = "#2B1C3D"
|
||||
foreground = "#f8f8f2"
|
||||
ruler = "#453254"
|
||||
comment = "#886C9C"
|
||||
red = "#ff5555"
|
||||
orange = "#ffb86c"
|
||||
yellow = "#f1fa8c"
|
||||
green = "#50fa7b"
|
||||
purple = "#bd93f9"
|
||||
cyan = "#8be9fd"
|
||||
pink = "#ff79c6"
|
@ -0,0 +1,84 @@
|
||||
# Author : Ingrid Rebecca Abraham <git@ingrids.email>
|
||||
|
||||
"attribute" = "#839A53"
|
||||
"keyword" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"keyword.directive" = "#6F873E"
|
||||
"namespace" = "#839A53"
|
||||
"punctuation" = "#C97270"
|
||||
"punctuation.delimiter" = "#C97270"
|
||||
"operator" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"special" = "#D68482"
|
||||
"variable.other.member" = "#89BEB7"
|
||||
"variable" = "#A6B6CE"
|
||||
"variable.parameter" = "#89BEB7"
|
||||
"type" = { fg = "#A6B6CE", modifiers = ["bold"] }
|
||||
"type.builtin" = "#839A53"
|
||||
"constructor" = { fg = "#839A53", modifiers = ["bold"] }
|
||||
"function" = { fg = "#89BEB7", modifiers = ["bold"] }
|
||||
"function.macro" = { fg = "#D4A520", modifiers = ["bold"] }
|
||||
"function.builtin" = "#89BEB7"
|
||||
"comment" = "#A6B6CE"
|
||||
"variable.builtin" = "#D4A520"
|
||||
"constant" = "#D4A520"
|
||||
"constant.builtin" = "#D4A520"
|
||||
"string" = "#D74E50"
|
||||
"constant.numeric" = "#D74E50"
|
||||
"constant.character.escape" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"label" = "#D68482"
|
||||
|
||||
"module" = "#839A53"
|
||||
|
||||
# TODO
|
||||
"markup.heading" = "blue"
|
||||
"markup.list" = "red"
|
||||
"markup.bold" = { fg = "yellow", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "magenta", modifiers = ["italic"] }
|
||||
"markup.link.url" = { fg = "yellow", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "red"
|
||||
"markup.quote" = "cyan"
|
||||
"markup.raw" = "green"
|
||||
|
||||
"diff.plus" = "#839A53"
|
||||
"diff.delta" = "#D4A520"
|
||||
"diff.minus" = "#D74E50"
|
||||
|
||||
"ui.background" = { bg = "#FFFCFD" }
|
||||
"ui.linenr" = { fg = "#bbbbbb" }
|
||||
"ui.linenr.selected" = { fg = "#ED5466", modifiers = ["bold"] }
|
||||
"ui.cursorline" = { bg = "#F3EAE9" }
|
||||
"ui.statusline" = { fg = "#250E07", bg = "#F3EAE9" }
|
||||
"ui.statusline.inactive" = { fg = "#7b91b3", bg = "#F3EAE9" }
|
||||
"ui.popup" = { fg = "#7B91b3", bg = "#F3E8E9" }
|
||||
"ui.window" = { bg = "#D8B8B3" }
|
||||
"ui.help" = { bg = "#D8B8B3", fg = "#250E07" }
|
||||
|
||||
"ui.text" = { fg = "#7B91B3" }
|
||||
"ui.text.focus" = { fg = "#250E07", modifiers= ["bold"] }
|
||||
"ui.virtual.whitespace" = "#A6B6CE"
|
||||
"ui.virtual.ruler" = { bg = "#F3EAE9" }
|
||||
|
||||
"ui.selection" = { bg = "#F3EAE9" }
|
||||
"ui.cursor.primary" = { bg = "#ED5466", fg = "#FFFCFD", modifiers = ["bold"] }
|
||||
"ui.cursor.match" = { bg = "#F3EAE9", fg = "#ED5466", modifiers = ["bold"] }
|
||||
"ui.menu" = { fg = "#7B91B3", bg = "#F3EAE9" }
|
||||
"ui.menu.selected" = { fg = "#D74E50", bg = "#F3EAE9" }
|
||||
|
||||
"warning" = "#D4A520"
|
||||
"error" = "#D74E50"
|
||||
"info" = "#839A53"
|
||||
"hint" = "#A6B6CE"
|
||||
<<<<<<< HEAD
|
||||
|
||||
"ui.explorer.file" = { fg = "#7B91B3" }
|
||||
"ui.explorer.dir" = { fg = "#89BEB7" }
|
||||
"ui.explorer.exe" = { fg = "#7B91B3" }
|
||||
"ui.explorer.focus" = { modifiers = ["reversed"] }
|
||||
"ui.explorer.unfocus" = { bg = "#F3EAE9" }
|
||||
||||||| 7ac72a39
|
||||
=======
|
||||
|
||||
"diagnostic.warning" = { underline = { color = "#D4A520", style = "curl" } }
|
||||
"diagnostic.error" = { underline = { color = "#D74E50", style = "curl" } }
|
||||
"diagnostic.info" = { underline = { color = "#839A53", style = "curl" } }
|
||||
"diagnostic.hint" = { underline = { color = "#A6B6CE", style = "curl" } }
|
||||
>>>>>>> master
|
@ -0,0 +1,84 @@
|
||||
# Author : Ingrid Rebecca Abraham <git@ingrids.email>
|
||||
|
||||
"attribute" = "#839A53"
|
||||
"keyword" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"keyword.directive" = "#6F873E"
|
||||
"namespace" = "#839A53"
|
||||
"punctuation" = "#C97270"
|
||||
"punctuation.delimiter" = "#C97270"
|
||||
"operator" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"special" = "#D68482"
|
||||
"variable.other.member" = "#89BEB7"
|
||||
"variable" = "#A6B6CE"
|
||||
"variable.parameter" = "#89BEB7"
|
||||
"type" = { fg = "#A6B6CE", modifiers = ["bold"] }
|
||||
"type.builtin" = "#839A53"
|
||||
"constructor" = { fg = "#839A53", modifiers = ["bold"] }
|
||||
"function" = { fg = "#89BEB7", modifiers = ["bold"] }
|
||||
"function.macro" = { fg = "#D4A520", modifiers = ["bold"] }
|
||||
"function.builtin" = "#89BEB7"
|
||||
"comment" = "#A6B6CE"
|
||||
"variable.builtin" = "#D4A520"
|
||||
"constant" = "#D4A520"
|
||||
"constant.builtin" = "#D4A520"
|
||||
"string" = "#D74E50"
|
||||
"constant.numeric" = "#D74E50"
|
||||
"constant.character.escape" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"label" = "#D68482"
|
||||
|
||||
"module" = "#839A53"
|
||||
|
||||
# TODO
|
||||
"markup.heading" = "blue"
|
||||
"markup.list" = "red"
|
||||
"markup.bold" = { fg = "yellow", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "magenta", modifiers = ["italic"] }
|
||||
"markup.link.url" = { fg = "yellow", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "red"
|
||||
"markup.quote" = "cyan"
|
||||
"markup.raw" = "green"
|
||||
|
||||
"diff.plus" = "#839A53"
|
||||
"diff.delta" = "#D4A520"
|
||||
"diff.minus" = "#D74E50"
|
||||
|
||||
"ui.background" = { bg = "#FFFCFD" }
|
||||
"ui.linenr" = { fg = "#bbbbbb" }
|
||||
"ui.linenr.selected" = { fg = "#ED5466", modifiers = ["bold"] }
|
||||
"ui.cursorline" = { bg = "#F3EAE9" }
|
||||
"ui.statusline" = { fg = "#250E07", bg = "#F3EAE9" }
|
||||
"ui.statusline.inactive" = { fg = "#7b91b3", bg = "#F3EAE9" }
|
||||
"ui.popup" = { fg = "#7B91b3", bg = "#F3E8E9" }
|
||||
"ui.window" = { bg = "#D8B8B3" }
|
||||
"ui.help" = { bg = "#D8B8B3", fg = "#250E07" }
|
||||
|
||||
"ui.text" = { fg = "#7B91B3" }
|
||||
"ui.text.focus" = { fg = "#250E07", modifiers= ["bold"] }
|
||||
"ui.virtual.whitespace" = "#A6B6CE"
|
||||
"ui.virtual.ruler" = { bg = "#F3EAE9" }
|
||||
|
||||
"ui.selection" = { bg = "#F3EAE9" }
|
||||
"ui.cursor.primary" = { bg = "#ED5466", fg = "#FFFCFD", modifiers = ["bold"] }
|
||||
"ui.cursor.match" = { bg = "#F3EAE9", fg = "#ED5466", modifiers = ["bold"] }
|
||||
"ui.menu" = { fg = "#7B91B3", bg = "#F3EAE9" }
|
||||
"ui.menu.selected" = { fg = "#D74E50", bg = "#F3EAE9" }
|
||||
|
||||
"warning" = "#D4A520"
|
||||
"error" = "#D74E50"
|
||||
"info" = "#839A53"
|
||||
"hint" = "#A6B6CE"
|
||||
<<<<<<< HEAD
|
||||
|
||||
"ui.explorer.file" = { fg = "#7B91B3" }
|
||||
"ui.explorer.dir" = { fg = "#89BEB7" }
|
||||
"ui.explorer.exe" = { fg = "#7B91B3" }
|
||||
"ui.explorer.focus" = { modifiers = ["reversed"] }
|
||||
"ui.explorer.unfocus" = { bg = "#F3EAE9" }
|
||||
||||||| 7ac72a39
|
||||
=======
|
||||
|
||||
"diagnostic.warning" = { underline = { color = "#D4A520", style = "curl" } }
|
||||
"diagnostic.error" = { underline = { color = "#D74E50", style = "curl" } }
|
||||
"diagnostic.info" = { underline = { color = "#839A53", style = "curl" } }
|
||||
"diagnostic.hint" = { underline = { color = "#A6B6CE", style = "curl" } }
|
||||
>>>>>>> master
|
@ -0,0 +1,69 @@
|
||||
# Author : Ingrid Rebecca Abraham <git@ingrids.email>
|
||||
|
||||
"attribute" = "#839A53"
|
||||
"keyword" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"keyword.directive" = "#6F873E"
|
||||
"namespace" = "#839A53"
|
||||
"punctuation" = "#C97270"
|
||||
"punctuation.delimiter" = "#C97270"
|
||||
"operator" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"special" = "#D68482"
|
||||
"variable.other.member" = "#89BEB7"
|
||||
"variable" = "#A6B6CE"
|
||||
"variable.parameter" = "#89BEB7"
|
||||
"type" = { fg = "#A6B6CE", modifiers = ["bold"] }
|
||||
"type.builtin" = "#839A53"
|
||||
"constructor" = { fg = "#839A53", modifiers = ["bold"] }
|
||||
"function" = { fg = "#89BEB7", modifiers = ["bold"] }
|
||||
"function.macro" = { fg = "#D4A520", modifiers = ["bold"] }
|
||||
"function.builtin" = "#89BEB7"
|
||||
"comment" = "#A6B6CE"
|
||||
"variable.builtin" = "#D4A520"
|
||||
"constant" = "#D4A520"
|
||||
"constant.builtin" = "#D4A520"
|
||||
"string" = "#D74E50"
|
||||
"constant.numeric" = "#D74E50"
|
||||
"constant.character.escape" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"label" = "#D68482"
|
||||
|
||||
"module" = "#839A53"
|
||||
|
||||
# TODO
|
||||
"markup.heading" = "blue"
|
||||
"markup.list" = "red"
|
||||
"markup.bold" = { fg = "yellow", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "magenta", modifiers = ["italic"] }
|
||||
"markup.link.url" = { fg = "yellow", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "red"
|
||||
"markup.quote" = "cyan"
|
||||
"markup.raw" = "green"
|
||||
|
||||
"diff.plus" = "#839A53"
|
||||
"diff.delta" = "#D4A520"
|
||||
"diff.minus" = "#D74E50"
|
||||
|
||||
"ui.background" = { bg = "#FFFCFD" }
|
||||
"ui.linenr" = { fg = "#bbbbbb" }
|
||||
"ui.linenr.selected" = { fg = "#ED5466", modifiers = ["bold"] }
|
||||
"ui.cursorline" = { bg = "#F3EAE9" }
|
||||
"ui.statusline" = { fg = "#250E07", bg = "#F3EAE9" }
|
||||
"ui.statusline.inactive" = { fg = "#7b91b3", bg = "#F3EAE9" }
|
||||
"ui.popup" = { fg = "#7B91b3", bg = "#F3E8E9" }
|
||||
"ui.window" = { bg = "#D8B8B3" }
|
||||
"ui.help" = { bg = "#D8B8B3", fg = "#250E07" }
|
||||
|
||||
"ui.text" = { fg = "#7B91B3" }
|
||||
"ui.text.focus" = { fg = "#250E07", modifiers= ["bold"] }
|
||||
"ui.virtual.whitespace" = "#A6B6CE"
|
||||
"ui.virtual.ruler" = { bg = "#F3EAE9" }
|
||||
|
||||
"ui.selection" = { bg = "#F3EAE9" }
|
||||
"ui.cursor.primary" = { bg = "#ED5466", fg = "#FFFCFD", modifiers = ["bold"] }
|
||||
"ui.cursor.match" = { bg = "#F3EAE9", fg = "#ED5466", modifiers = ["bold"] }
|
||||
"ui.menu" = { fg = "#7B91B3", bg = "#F3EAE9" }
|
||||
"ui.menu.selected" = { fg = "#D74E50", bg = "#F3EAE9" }
|
||||
|
||||
"warning" = "#D4A520"
|
||||
"error" = "#D74E50"
|
||||
"info" = "#839A53"
|
||||
"hint" = "#A6B6CE"
|
@ -0,0 +1,69 @@
|
||||
# Author : Ingrid Rebecca Abraham <git@ingrids.email>
|
||||
|
||||
"attribute" = "#839A53"
|
||||
"keyword" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"keyword.directive" = "#6F873E"
|
||||
"namespace" = "#839A53"
|
||||
"punctuation" = "#C97270"
|
||||
"punctuation.delimiter" = "#C97270"
|
||||
"operator" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"special" = "#D68482"
|
||||
"variable.other.member" = "#89BEB7"
|
||||
"variable" = "#A6B6CE"
|
||||
"variable.parameter" = "#89BEB7"
|
||||
"type" = { fg = "#A6B6CE", modifiers = ["bold"] }
|
||||
"type.builtin" = "#839A53"
|
||||
"constructor" = { fg = "#839A53", modifiers = ["bold"] }
|
||||
"function" = { fg = "#89BEB7", modifiers = ["bold"] }
|
||||
"function.macro" = { fg = "#D4A520", modifiers = ["bold"] }
|
||||
"function.builtin" = "#89BEB7"
|
||||
"comment" = "#A6B6CE"
|
||||
"variable.builtin" = "#D4A520"
|
||||
"constant" = "#D4A520"
|
||||
"constant.builtin" = "#D4A520"
|
||||
"string" = "#D74E50"
|
||||
"constant.numeric" = "#D74E50"
|
||||
"constant.character.escape" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"label" = "#D68482"
|
||||
|
||||
"module" = "#839A53"
|
||||
|
||||
# TODO
|
||||
"markup.heading" = "blue"
|
||||
"markup.list" = "red"
|
||||
"markup.bold" = { fg = "yellow", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "magenta", modifiers = ["italic"] }
|
||||
"markup.link.url" = { fg = "yellow", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "red"
|
||||
"markup.quote" = "cyan"
|
||||
"markup.raw" = "green"
|
||||
|
||||
"diff.plus" = "#839A53"
|
||||
"diff.delta" = "#D4A520"
|
||||
"diff.minus" = "#D74E50"
|
||||
|
||||
"ui.background" = { bg = "#FFFCFD" }
|
||||
"ui.linenr" = { fg = "#bbbbbb" }
|
||||
"ui.linenr.selected" = { fg = "#ED5466", modifiers = ["bold"] }
|
||||
"ui.cursorline" = { bg = "#F3EAE9" }
|
||||
"ui.statusline" = { fg = "#250E07", bg = "#F3EAE9" }
|
||||
"ui.statusline.inactive" = { fg = "#7b91b3", bg = "#F3EAE9" }
|
||||
"ui.popup" = { fg = "#7B91b3", bg = "#F3E8E9" }
|
||||
"ui.window" = { bg = "#D8B8B3" }
|
||||
"ui.help" = { bg = "#D8B8B3", fg = "#250E07" }
|
||||
|
||||
"ui.text" = { fg = "#7B91B3" }
|
||||
"ui.text.focus" = { fg = "#250E07", modifiers= ["bold"] }
|
||||
"ui.virtual.whitespace" = "#A6B6CE"
|
||||
"ui.virtual.ruler" = { bg = "#F3EAE9" }
|
||||
|
||||
"ui.selection" = { bg = "#F3EAE9" }
|
||||
"ui.cursor.primary" = { bg = "#ED5466", fg = "#FFFCFD", modifiers = ["bold"] }
|
||||
"ui.cursor.match" = { bg = "#F3EAE9", fg = "#ED5466", modifiers = ["bold"] }
|
||||
"ui.menu" = { fg = "#7B91B3", bg = "#F3EAE9" }
|
||||
"ui.menu.selected" = { fg = "#D74E50", bg = "#F3EAE9" }
|
||||
|
||||
"warning" = "#D4A520"
|
||||
"error" = "#D74E50"
|
||||
"info" = "#839A53"
|
||||
"hint" = "#A6B6CE"
|
@ -0,0 +1,75 @@
|
||||
# Author : Ingrid Rebecca Abraham <git@ingrids.email>
|
||||
|
||||
"attribute" = "#839A53"
|
||||
"keyword" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"keyword.directive" = "#6F873E"
|
||||
"namespace" = "#839A53"
|
||||
"punctuation" = "#C97270"
|
||||
"punctuation.delimiter" = "#C97270"
|
||||
"operator" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"special" = "#D68482"
|
||||
"variable.other.member" = "#89BEB7"
|
||||
"variable" = "#A6B6CE"
|
||||
"variable.parameter" = "#89BEB7"
|
||||
"type" = { fg = "#A6B6CE", modifiers = ["bold"] }
|
||||
"type.builtin" = "#839A53"
|
||||
"constructor" = { fg = "#839A53", modifiers = ["bold"] }
|
||||
"function" = { fg = "#89BEB7", modifiers = ["bold"] }
|
||||
"function.macro" = { fg = "#D4A520", modifiers = ["bold"] }
|
||||
"function.builtin" = "#89BEB7"
|
||||
"comment" = "#A6B6CE"
|
||||
"variable.builtin" = "#D4A520"
|
||||
"constant" = "#D4A520"
|
||||
"constant.builtin" = "#D4A520"
|
||||
"string" = "#D74E50"
|
||||
"constant.numeric" = "#D74E50"
|
||||
"constant.character.escape" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"label" = "#D68482"
|
||||
|
||||
"module" = "#839A53"
|
||||
|
||||
# TODO
|
||||
"markup.heading" = "blue"
|
||||
"markup.list" = "red"
|
||||
"markup.bold" = { fg = "yellow", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "magenta", modifiers = ["italic"] }
|
||||
"markup.link.url" = { fg = "yellow", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "red"
|
||||
"markup.quote" = "cyan"
|
||||
"markup.raw" = "green"
|
||||
|
||||
"diff.plus" = "#839A53"
|
||||
"diff.delta" = "#D4A520"
|
||||
"diff.minus" = "#D74E50"
|
||||
|
||||
"ui.background" = { bg = "#FFFCFD" }
|
||||
"ui.linenr" = { fg = "#bbbbbb" }
|
||||
"ui.linenr.selected" = { fg = "#ED5466", modifiers = ["bold"] }
|
||||
"ui.cursorline" = { bg = "#F3EAE9" }
|
||||
"ui.statusline" = { fg = "#250E07", bg = "#F3EAE9" }
|
||||
"ui.statusline.inactive" = { fg = "#7b91b3", bg = "#F3EAE9" }
|
||||
"ui.popup" = { fg = "#7B91b3", bg = "#F3E8E9" }
|
||||
"ui.window" = { bg = "#D8B8B3" }
|
||||
"ui.help" = { bg = "#D8B8B3", fg = "#250E07" }
|
||||
|
||||
"ui.text" = { fg = "#7B91B3" }
|
||||
"ui.text.focus" = { fg = "#250E07", modifiers= ["bold"] }
|
||||
"ui.virtual.whitespace" = "#A6B6CE"
|
||||
"ui.virtual.ruler" = { bg = "#F3EAE9" }
|
||||
|
||||
"ui.selection" = { bg = "#F3EAE9" }
|
||||
"ui.cursor.primary" = { bg = "#ED5466", fg = "#FFFCFD", modifiers = ["bold"] }
|
||||
"ui.cursor.match" = { bg = "#F3EAE9", fg = "#ED5466", modifiers = ["bold"] }
|
||||
"ui.menu" = { fg = "#7B91B3", bg = "#F3EAE9" }
|
||||
"ui.menu.selected" = { fg = "#D74E50", bg = "#F3EAE9" }
|
||||
|
||||
"warning" = "#D4A520"
|
||||
"error" = "#D74E50"
|
||||
"info" = "#839A53"
|
||||
"hint" = "#A6B6CE"
|
||||
|
||||
"ui.explorer.file" = { fg = "#7B91B3" }
|
||||
"ui.explorer.dir" = { fg = "#89BEB7" }
|
||||
"ui.explorer.exe" = { fg = "#7B91B3" }
|
||||
"ui.explorer.focus" = { modifiers = ["reversed"] }
|
||||
"ui.explorer.unfocus" = { bg = "#F3EAE9" }
|
@ -0,0 +1,75 @@
|
||||
# Author : Ingrid Rebecca Abraham <git@ingrids.email>
|
||||
|
||||
"attribute" = "#839A53"
|
||||
"keyword" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"keyword.directive" = "#6F873E"
|
||||
"namespace" = "#839A53"
|
||||
"punctuation" = "#C97270"
|
||||
"punctuation.delimiter" = "#C97270"
|
||||
"operator" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"special" = "#D68482"
|
||||
"variable.other.member" = "#89BEB7"
|
||||
"variable" = "#A6B6CE"
|
||||
"variable.parameter" = "#89BEB7"
|
||||
"type" = { fg = "#A6B6CE", modifiers = ["bold"] }
|
||||
"type.builtin" = "#839A53"
|
||||
"constructor" = { fg = "#839A53", modifiers = ["bold"] }
|
||||
"function" = { fg = "#89BEB7", modifiers = ["bold"] }
|
||||
"function.macro" = { fg = "#D4A520", modifiers = ["bold"] }
|
||||
"function.builtin" = "#89BEB7"
|
||||
"comment" = "#A6B6CE"
|
||||
"variable.builtin" = "#D4A520"
|
||||
"constant" = "#D4A520"
|
||||
"constant.builtin" = "#D4A520"
|
||||
"string" = "#D74E50"
|
||||
"constant.numeric" = "#D74E50"
|
||||
"constant.character.escape" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"label" = "#D68482"
|
||||
|
||||
"module" = "#839A53"
|
||||
|
||||
# TODO
|
||||
"markup.heading" = "blue"
|
||||
"markup.list" = "red"
|
||||
"markup.bold" = { fg = "yellow", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "magenta", modifiers = ["italic"] }
|
||||
"markup.link.url" = { fg = "yellow", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "red"
|
||||
"markup.quote" = "cyan"
|
||||
"markup.raw" = "green"
|
||||
|
||||
"diff.plus" = "#839A53"
|
||||
"diff.delta" = "#D4A520"
|
||||
"diff.minus" = "#D74E50"
|
||||
|
||||
"ui.background" = { bg = "#FFFCFD" }
|
||||
"ui.linenr" = { fg = "#bbbbbb" }
|
||||
"ui.linenr.selected" = { fg = "#ED5466", modifiers = ["bold"] }
|
||||
"ui.cursorline" = { bg = "#F3EAE9" }
|
||||
"ui.statusline" = { fg = "#250E07", bg = "#F3EAE9" }
|
||||
"ui.statusline.inactive" = { fg = "#7b91b3", bg = "#F3EAE9" }
|
||||
"ui.popup" = { fg = "#7B91b3", bg = "#F3E8E9" }
|
||||
"ui.window" = { bg = "#D8B8B3" }
|
||||
"ui.help" = { bg = "#D8B8B3", fg = "#250E07" }
|
||||
|
||||
"ui.text" = { fg = "#7B91B3" }
|
||||
"ui.text.focus" = { fg = "#250E07", modifiers= ["bold"] }
|
||||
"ui.virtual.whitespace" = "#A6B6CE"
|
||||
"ui.virtual.ruler" = { bg = "#F3EAE9" }
|
||||
|
||||
"ui.selection" = { bg = "#F3EAE9" }
|
||||
"ui.cursor.primary" = { bg = "#ED5466", fg = "#FFFCFD", modifiers = ["bold"] }
|
||||
"ui.cursor.match" = { bg = "#F3EAE9", fg = "#ED5466", modifiers = ["bold"] }
|
||||
"ui.menu" = { fg = "#7B91B3", bg = "#F3EAE9" }
|
||||
"ui.menu.selected" = { fg = "#D74E50", bg = "#F3EAE9" }
|
||||
|
||||
"warning" = "#D4A520"
|
||||
"error" = "#D74E50"
|
||||
"info" = "#839A53"
|
||||
"hint" = "#A6B6CE"
|
||||
|
||||
"ui.explorer.file" = { fg = "#7B91B3" }
|
||||
"ui.explorer.dir" = { fg = "#89BEB7" }
|
||||
"ui.explorer.exe" = { fg = "#7B91B3" }
|
||||
"ui.explorer.focus" = { modifiers = ["reversed"] }
|
||||
"ui.explorer.unfocus" = { bg = "#F3EAE9" }
|
@ -0,0 +1,74 @@
|
||||
# Author : Ingrid Rebecca Abraham <git@ingrids.email>
|
||||
|
||||
"attribute" = "#839A53"
|
||||
"keyword" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"keyword.directive" = "#6F873E"
|
||||
"namespace" = "#839A53"
|
||||
"punctuation" = "#C97270"
|
||||
"punctuation.delimiter" = "#C97270"
|
||||
"operator" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"special" = "#D68482"
|
||||
"variable.other.member" = "#89BEB7"
|
||||
"variable" = "#A6B6CE"
|
||||
"variable.parameter" = "#89BEB7"
|
||||
"type" = { fg = "#A6B6CE", modifiers = ["bold"] }
|
||||
"type.builtin" = "#839A53"
|
||||
"constructor" = { fg = "#839A53", modifiers = ["bold"] }
|
||||
"function" = { fg = "#89BEB7", modifiers = ["bold"] }
|
||||
"function.macro" = { fg = "#D4A520", modifiers = ["bold"] }
|
||||
"function.builtin" = "#89BEB7"
|
||||
"comment" = "#A6B6CE"
|
||||
"variable.builtin" = "#D4A520"
|
||||
"constant" = "#D4A520"
|
||||
"constant.builtin" = "#D4A520"
|
||||
"string" = "#D74E50"
|
||||
"constant.numeric" = "#D74E50"
|
||||
"constant.character.escape" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"label" = "#D68482"
|
||||
|
||||
"module" = "#839A53"
|
||||
|
||||
# TODO
|
||||
"markup.heading" = "blue"
|
||||
"markup.list" = "red"
|
||||
"markup.bold" = { fg = "yellow", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "magenta", modifiers = ["italic"] }
|
||||
"markup.link.url" = { fg = "yellow", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "red"
|
||||
"markup.quote" = "cyan"
|
||||
"markup.raw" = "green"
|
||||
|
||||
"diff.plus" = "#839A53"
|
||||
"diff.delta" = "#D4A520"
|
||||
"diff.minus" = "#D74E50"
|
||||
|
||||
"ui.background" = { bg = "#FFFCFD" }
|
||||
"ui.linenr" = { fg = "#bbbbbb" }
|
||||
"ui.linenr.selected" = { fg = "#ED5466", modifiers = ["bold"] }
|
||||
"ui.cursorline" = { bg = "#F3EAE9" }
|
||||
"ui.statusline" = { fg = "#250E07", bg = "#F3EAE9" }
|
||||
"ui.statusline.inactive" = { fg = "#7b91b3", bg = "#F3EAE9" }
|
||||
"ui.popup" = { fg = "#7B91b3", bg = "#F3E8E9" }
|
||||
"ui.window" = { bg = "#D8B8B3" }
|
||||
"ui.help" = { bg = "#D8B8B3", fg = "#250E07" }
|
||||
|
||||
"ui.text" = { fg = "#7B91B3" }
|
||||
"ui.text.focus" = { fg = "#250E07", modifiers= ["bold"] }
|
||||
"ui.virtual.whitespace" = "#A6B6CE"
|
||||
"ui.virtual.ruler" = { bg = "#F3EAE9" }
|
||||
|
||||
"ui.selection" = { bg = "#F3EAE9" }
|
||||
"ui.cursor.primary" = { bg = "#ED5466", fg = "#FFFCFD", modifiers = ["bold"] }
|
||||
"ui.cursor.match" = { bg = "#F3EAE9", fg = "#ED5466", modifiers = ["bold"] }
|
||||
"ui.menu" = { fg = "#7B91B3", bg = "#F3EAE9" }
|
||||
"ui.menu.selected" = { fg = "#D74E50", bg = "#F3EAE9" }
|
||||
|
||||
"warning" = "#D4A520"
|
||||
"error" = "#D74E50"
|
||||
"info" = "#839A53"
|
||||
"hint" = "#A6B6CE"
|
||||
|
||||
"diagnostic.warning" = { underline = { color = "#D4A520", style = "curl" } }
|
||||
"diagnostic.error" = { underline = { color = "#D74E50", style = "curl" } }
|
||||
"diagnostic.info" = { underline = { color = "#839A53", style = "curl" } }
|
||||
"diagnostic.hint" = { underline = { color = "#A6B6CE", style = "curl" } }
|
@ -0,0 +1,74 @@
|
||||
# Author : Ingrid Rebecca Abraham <git@ingrids.email>
|
||||
|
||||
"attribute" = "#839A53"
|
||||
"keyword" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"keyword.directive" = "#6F873E"
|
||||
"namespace" = "#839A53"
|
||||
"punctuation" = "#C97270"
|
||||
"punctuation.delimiter" = "#C97270"
|
||||
"operator" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"special" = "#D68482"
|
||||
"variable.other.member" = "#89BEB7"
|
||||
"variable" = "#A6B6CE"
|
||||
"variable.parameter" = "#89BEB7"
|
||||
"type" = { fg = "#A6B6CE", modifiers = ["bold"] }
|
||||
"type.builtin" = "#839A53"
|
||||
"constructor" = { fg = "#839A53", modifiers = ["bold"] }
|
||||
"function" = { fg = "#89BEB7", modifiers = ["bold"] }
|
||||
"function.macro" = { fg = "#D4A520", modifiers = ["bold"] }
|
||||
"function.builtin" = "#89BEB7"
|
||||
"comment" = "#A6B6CE"
|
||||
"variable.builtin" = "#D4A520"
|
||||
"constant" = "#D4A520"
|
||||
"constant.builtin" = "#D4A520"
|
||||
"string" = "#D74E50"
|
||||
"constant.numeric" = "#D74E50"
|
||||
"constant.character.escape" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"label" = "#D68482"
|
||||
|
||||
"module" = "#839A53"
|
||||
|
||||
# TODO
|
||||
"markup.heading" = "blue"
|
||||
"markup.list" = "red"
|
||||
"markup.bold" = { fg = "yellow", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "magenta", modifiers = ["italic"] }
|
||||
"markup.link.url" = { fg = "yellow", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "red"
|
||||
"markup.quote" = "cyan"
|
||||
"markup.raw" = "green"
|
||||
|
||||
"diff.plus" = "#839A53"
|
||||
"diff.delta" = "#D4A520"
|
||||
"diff.minus" = "#D74E50"
|
||||
|
||||
"ui.background" = { bg = "#FFFCFD" }
|
||||
"ui.linenr" = { fg = "#bbbbbb" }
|
||||
"ui.linenr.selected" = { fg = "#ED5466", modifiers = ["bold"] }
|
||||
"ui.cursorline" = { bg = "#F3EAE9" }
|
||||
"ui.statusline" = { fg = "#250E07", bg = "#F3EAE9" }
|
||||
"ui.statusline.inactive" = { fg = "#7b91b3", bg = "#F3EAE9" }
|
||||
"ui.popup" = { fg = "#7B91b3", bg = "#F3E8E9" }
|
||||
"ui.window" = { bg = "#D8B8B3" }
|
||||
"ui.help" = { bg = "#D8B8B3", fg = "#250E07" }
|
||||
|
||||
"ui.text" = { fg = "#7B91B3" }
|
||||
"ui.text.focus" = { fg = "#250E07", modifiers= ["bold"] }
|
||||
"ui.virtual.whitespace" = "#A6B6CE"
|
||||
"ui.virtual.ruler" = { bg = "#F3EAE9" }
|
||||
|
||||
"ui.selection" = { bg = "#F3EAE9" }
|
||||
"ui.cursor.primary" = { bg = "#ED5466", fg = "#FFFCFD", modifiers = ["bold"] }
|
||||
"ui.cursor.match" = { bg = "#F3EAE9", fg = "#ED5466", modifiers = ["bold"] }
|
||||
"ui.menu" = { fg = "#7B91B3", bg = "#F3EAE9" }
|
||||
"ui.menu.selected" = { fg = "#D74E50", bg = "#F3EAE9" }
|
||||
|
||||
"warning" = "#D4A520"
|
||||
"error" = "#D74E50"
|
||||
"info" = "#839A53"
|
||||
"hint" = "#A6B6CE"
|
||||
|
||||
"diagnostic.warning" = { underline = { color = "#D4A520", style = "curl" } }
|
||||
"diagnostic.error" = { underline = { color = "#D74E50", style = "curl" } }
|
||||
"diagnostic.info" = { underline = { color = "#839A53", style = "curl" } }
|
||||
"diagnostic.hint" = { underline = { color = "#A6B6CE", style = "curl" } }
|
@ -1,3 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "1.61.0"
|
||||
channel = "1.66.0"
|
||||
components = ["rustfmt", "rust-src"]
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 359 KiB After Width: | Height: | Size: 271 KiB |
Loading…
Reference in New Issue