Add ability to change theme on editor

pull/318/head
wojciechkepka 3 years ago committed by Blaž Hrastnik
parent f424a61054
commit ce97a2f05f

7
Cargo.lock generated

@ -17,6 +17,12 @@ version = "1.0.41"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15af2628f6890fe2609a3b91bef4c83450512802e59489f9c1cb1fa5df064a61" checksum = "15af2628f6890fe2609a3b91bef4c83450512802e59489f9c1cb1fa5df064a61"
[[package]]
name = "arc-swap"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e906254e445520903e7fc9da4f709886c84ae4bc4ddaf0e093188d66df4dc820"
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.0.1" version = "1.0.1"
@ -254,6 +260,7 @@ dependencies = [
name = "helix-core" name = "helix-core"
version = "0.2.0" version = "0.2.0"
dependencies = [ dependencies = [
"arc-swap",
"etcetera", "etcetera",
"helix-syntax", "helix-syntax",
"once_cell", "once_cell",

@ -25,6 +25,7 @@ unicode-general-category = "0.4.0"
# slab = "0.4.2" # slab = "0.4.2"
tree-sitter = "0.19" tree-sitter = "0.19"
once_cell = "1.8" once_cell = "1.8"
arc-swap = "1"
regex = "1" regex = "1"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }

@ -254,8 +254,7 @@ where
Configuration, IndentationConfiguration, Lang, LanguageConfiguration, Loader, Configuration, IndentationConfiguration, Lang, LanguageConfiguration, Loader,
}; };
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
let loader = Loader::new( let loader = Loader::new(Configuration {
Configuration {
language: vec![LanguageConfiguration { language: vec![LanguageConfiguration {
scope: "source.rust".to_string(), scope: "source.rust".to_string(),
file_types: vec!["rs".to_string()], file_types: vec!["rs".to_string()],
@ -271,9 +270,7 @@ where
}), }),
indent_query: OnceCell::new(), indent_query: OnceCell::new(),
}], }],
}, });
Vec::new(),
);
// set runtime path so we can find the queries // set runtime path so we can find the queries
let mut runtime = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); let mut runtime = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));

@ -50,7 +50,7 @@ pub fn find_root(root: Option<&str>) -> Option<std::path::PathBuf> {
} }
#[cfg(not(embed_runtime))] #[cfg(not(embed_runtime))]
fn runtime_dir() -> std::path::PathBuf { pub fn runtime_dir() -> std::path::PathBuf {
if let Ok(dir) = std::env::var("HELIX_RUNTIME") { if let Ok(dir) = std::env::var("HELIX_RUNTIME") {
return dir.into(); return dir.into();
} }

@ -1,6 +1,8 @@
use crate::{regex::Regex, Change, Rope, RopeSlice, Transaction}; use crate::{regex::Regex, Change, Rope, RopeSlice, Transaction};
pub use helix_syntax::{get_language, get_language_name, Lang}; pub use helix_syntax::{get_language, get_language_name, Lang};
use arc_swap::ArcSwap;
use std::{ use std::{
borrow::Cow, borrow::Cow,
cell::RefCell, cell::RefCell,
@ -143,9 +145,7 @@ fn read_query(language: &str, filename: &str) -> String {
} }
impl LanguageConfiguration { impl LanguageConfiguration {
pub fn highlight_config(&self, scopes: &[String]) -> Option<Arc<HighlightConfiguration>> { fn initialize_highlight(&self, scopes: &[String]) -> Option<Arc<HighlightConfiguration>> {
self.highlight_config
.get_or_init(|| {
let language = get_language_name(self.language_id).to_ascii_lowercase(); let language = get_language_name(self.language_id).to_ascii_lowercase();
let highlights_query = read_query(&language, "highlights.scm"); let highlights_query = read_query(&language, "highlights.scm");
@ -170,9 +170,24 @@ impl LanguageConfiguration {
config.configure(scopes); config.configure(scopes);
Some(Arc::new(config)) Some(Arc::new(config))
} }
}) }
pub fn highlight_config(&self, scopes: &[String]) -> Option<Arc<HighlightConfiguration>> {
if let Some(config) = self.highlight_config.get() {
if let Some(config) = config {
config.configure(scopes);
}
config.clone()
} else {
self.highlight_config
.get_or_init(|| self.initialize_highlight(scopes))
.clone() .clone()
} }
}
pub fn is_highlight_initialized(&self) -> bool {
self.highlight_config.get().is_some()
}
pub fn indent_query(&self) -> Option<&IndentQuery> { pub fn indent_query(&self) -> Option<&IndentQuery> {
self.indent_query self.indent_query
@ -190,22 +205,18 @@ impl LanguageConfiguration {
} }
} }
pub static LOADER: OnceCell<Loader> = OnceCell::new();
#[derive(Debug)] #[derive(Debug)]
pub struct Loader { pub struct Loader {
// highlight_names ? // highlight_names ?
language_configs: Vec<Arc<LanguageConfiguration>>, language_configs: Vec<Arc<LanguageConfiguration>>,
language_config_ids_by_file_type: HashMap<String, usize>, // Vec<usize> language_config_ids_by_file_type: HashMap<String, usize>, // Vec<usize>
scopes: Vec<String>,
} }
impl Loader { impl Loader {
pub fn new(config: Configuration, scopes: Vec<String>) -> Self { pub fn new(config: Configuration) -> Self {
let mut loader = Self { let mut loader = Self {
language_configs: Vec::new(), language_configs: Vec::new(),
language_config_ids_by_file_type: HashMap::new(), language_config_ids_by_file_type: HashMap::new(),
scopes,
}; };
for config in config.language { for config in config.language {
@ -225,10 +236,6 @@ impl Loader {
loader loader
} }
pub fn scopes(&self) -> &[String] {
&self.scopes
}
pub fn language_config_for_file_name(&self, path: &Path) -> Option<Arc<LanguageConfiguration>> { pub fn language_config_for_file_name(&self, path: &Path) -> Option<Arc<LanguageConfiguration>> {
// Find all the language configurations that match this file name // Find all the language configurations that match this file name
// or a suffix of the file name. // or a suffix of the file name.
@ -253,6 +260,10 @@ impl Loader {
.find(|config| config.scope == scope) .find(|config| config.scope == scope)
.cloned() .cloned()
} }
pub fn language_configs_iter(&self) -> impl Iterator<Item = &Arc<LanguageConfiguration>> {
self.language_configs.iter()
}
} }
pub struct TsParser { pub struct TsParser {
@ -771,7 +782,7 @@ pub struct HighlightConfiguration {
combined_injections_query: Option<Query>, combined_injections_query: Option<Query>,
locals_pattern_index: usize, locals_pattern_index: usize,
highlights_pattern_index: usize, highlights_pattern_index: usize,
highlight_indices: Vec<Option<Highlight>>, highlight_indices: ArcSwap<Vec<Option<Highlight>>>,
non_local_variable_patterns: Vec<bool>, non_local_variable_patterns: Vec<bool>,
injection_content_capture_index: Option<u32>, injection_content_capture_index: Option<u32>,
injection_language_capture_index: Option<u32>, injection_language_capture_index: Option<u32>,
@ -923,7 +934,7 @@ impl HighlightConfiguration {
} }
} }
let highlight_indices = vec![None; query.capture_names().len()]; let highlight_indices = ArcSwap::from_pointee(vec![None; query.capture_names().len()]);
Ok(Self { Ok(Self {
language, language,
query, query,
@ -956,17 +967,20 @@ impl HighlightConfiguration {
/// ///
/// When highlighting, results are returned as `Highlight` values, which contain the index /// When highlighting, results are returned as `Highlight` values, which contain the index
/// of the matched highlight this list of highlight names. /// of the matched highlight this list of highlight names.
pub fn configure(&mut self, recognized_names: &[String]) { pub fn configure(&self, recognized_names: &[String]) {
let mut capture_parts = Vec::new(); let mut capture_parts = Vec::new();
self.highlight_indices.clear(); let indices: Vec<_> = self
self.highlight_indices .query
.extend(self.query.capture_names().iter().map(move |capture_name| { .capture_names()
.iter()
.map(move |capture_name| {
capture_parts.clear(); capture_parts.clear();
capture_parts.extend(capture_name.split('.')); capture_parts.extend(capture_name.split('.'));
let mut best_index = None; let mut best_index = None;
let mut best_match_len = 0; let mut best_match_len = 0;
for (i, recognized_name) in recognized_names.iter().enumerate() { for (i, recognized_name) in recognized_names.iter().enumerate() {
let recognized_name = recognized_name;
let mut len = 0; let mut len = 0;
let mut matches = true; let mut matches = true;
for part in recognized_name.split('.') { for part in recognized_name.split('.') {
@ -982,7 +996,10 @@ impl HighlightConfiguration {
} }
} }
best_index.map(Highlight) best_index.map(Highlight)
})); })
.collect();
self.highlight_indices.store(Arc::new(indices));
} }
} }
@ -1561,7 +1578,7 @@ where
} }
} }
let current_highlight = layer.config.highlight_indices[capture.index as usize]; let current_highlight = layer.config.highlight_indices.load()[capture.index as usize];
// If this node represents a local definition, then store the current // If this node represents a local definition, then store the current
// highlight value on the local scope entry representing this node. // highlight value on the local scope entry representing this node.

@ -1,5 +1,6 @@
use helix_core::syntax;
use helix_lsp::{lsp, LspProgressMap}; use helix_lsp::{lsp, LspProgressMap};
use helix_view::{document::Mode, Document, Editor, Theme, View}; use helix_view::{document::Mode, theme, Document, Editor, Theme, View};
use crate::{args::Args, compositor::Compositor, config::Config, keymap::Keymaps, ui}; use crate::{args::Args, compositor::Compositor, config::Config, keymap::Keymaps, ui};
@ -14,7 +15,7 @@ use std::{
time::Duration, time::Duration,
}; };
use anyhow::Error; use anyhow::{Context, Error};
use crossterm::{ use crossterm::{
event::{Event, EventStream}, event::{Event, EventStream},
@ -36,6 +37,8 @@ pub struct Application {
compositor: Compositor, compositor: Compositor,
editor: Editor, editor: Editor,
theme_loader: Arc<theme::Loader>,
syn_loader: Arc<syntax::Loader>,
callbacks: LspCallbacks, callbacks: LspCallbacks,
lsp_progress: LspProgressMap, lsp_progress: LspProgressMap,
@ -47,7 +50,34 @@ impl Application {
use helix_view::editor::Action; use helix_view::editor::Action;
let mut compositor = Compositor::new()?; let mut compositor = Compositor::new()?;
let size = compositor.size(); let size = compositor.size();
let mut editor = Editor::new(size);
let conf_dir = helix_core::config_dir();
let theme_loader =
std::sync::Arc::new(theme::Loader::new(&conf_dir, &helix_core::runtime_dir()));
// load $HOME/.config/helix/languages.toml, fallback to default config
let lang_conf = std::fs::read(conf_dir.join("languages.toml"));
let lang_conf = lang_conf
.as_deref()
.unwrap_or(include_bytes!("../../languages.toml"));
let theme = if let Some(theme) = &config.global.theme {
match theme_loader.load(theme) {
Ok(theme) => theme,
Err(e) => {
log::warn!("failed to load theme `{}` - {}", theme, e);
theme_loader.default()
}
}
} else {
theme_loader.default()
};
let syn_loader_conf = toml::from_slice(lang_conf).expect("Could not parse languages.toml");
let syn_loader = std::sync::Arc::new(syntax::Loader::new(syn_loader_conf));
let mut editor = Editor::new(size, theme_loader.clone(), syn_loader.clone());
let mut editor_view = Box::new(ui::EditorView::new(config.keymaps)); let mut editor_view = Box::new(ui::EditorView::new(config.keymaps));
compositor.push(editor_view); compositor.push(editor_view);
@ -72,10 +102,14 @@ impl Application {
editor.new_file(Action::VerticalSplit); editor.new_file(Action::VerticalSplit);
} }
editor.set_theme(theme);
let mut app = Self { let mut app = Self {
compositor, compositor,
editor, editor,
theme_loader,
syn_loader,
callbacks: FuturesUnordered::new(), callbacks: FuturesUnordered::new(),
lsp_progress: LspProgressMap::new(), lsp_progress: LspProgressMap::new(),
lsp_progress_enabled: config.global.lsp_progress, lsp_progress_enabled: config.global.lsp_progress,

@ -246,34 +246,43 @@ impl Component for Completion {
value: contents, value: contents,
})) => { })) => {
// TODO: convert to wrapped text // TODO: convert to wrapped text
Markdown::new(format!( Markdown::new(
format!(
"```{}\n{}\n```\n{}", "```{}\n{}\n```\n{}",
language, language,
option.detail.as_deref().unwrap_or_default(), option.detail.as_deref().unwrap_or_default(),
contents.clone() contents.clone()
)) ),
cx.editor.syn_loader.clone(),
)
} }
Some(lsp::Documentation::MarkupContent(lsp::MarkupContent { Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
kind: lsp::MarkupKind::Markdown, kind: lsp::MarkupKind::Markdown,
value: contents, value: contents,
})) => { })) => {
// TODO: set language based on doc scope // TODO: set language based on doc scope
Markdown::new(format!( Markdown::new(
format!(
"```{}\n{}\n```\n{}", "```{}\n{}\n```\n{}",
language, language,
option.detail.as_deref().unwrap_or_default(), option.detail.as_deref().unwrap_or_default(),
contents.clone() contents.clone()
)) ),
cx.editor.syn_loader.clone(),
)
} }
None if option.detail.is_some() => { None if option.detail.is_some() => {
// TODO: copied from above // TODO: copied from above
// TODO: set language based on doc scope // TODO: set language based on doc scope
Markdown::new(format!( Markdown::new(
format!(
"```{}\n{}\n```", "```{}\n{}\n```",
language, language,
option.detail.as_deref().unwrap_or_default(), option.detail.as_deref().unwrap_or_default(),
)) ),
cx.editor.syn_loader.clone(),
)
} }
None => return, None => return,
}; };

@ -7,25 +7,34 @@ use tui::{
text::Text, text::Text,
}; };
use std::borrow::Cow; use std::{borrow::Cow, sync::Arc};
use helix_core::Position; use helix_core::{syntax, Position};
use helix_view::{Editor, Theme}; use helix_view::{Editor, Theme};
pub struct Markdown { pub struct Markdown {
contents: String, contents: String,
config_loader: Arc<syntax::Loader>,
} }
// TODO: pre-render and self reference via Pin // TODO: pre-render and self reference via Pin
// better yet, just use Tendril + subtendril for references // better yet, just use Tendril + subtendril for references
impl Markdown { impl Markdown {
pub fn new(contents: String) -> Self { pub fn new(contents: String, config_loader: Arc<syntax::Loader>) -> Self {
Self { contents } Self {
contents,
config_loader,
}
} }
} }
fn parse<'a>(contents: &'a str, theme: Option<&Theme>) -> tui::text::Text<'a> { fn parse<'a>(
contents: &'a str,
theme: Option<&Theme>,
loader: &syntax::Loader,
) -> tui::text::Text<'a> {
use pulldown_cmark::{CodeBlockKind, CowStr, Event, Options, Parser, Tag}; use pulldown_cmark::{CodeBlockKind, CowStr, Event, Options, Parser, Tag};
use tui::text::{Span, Spans, Text}; use tui::text::{Span, Spans, Text};
@ -79,9 +88,7 @@ fn parse<'a>(contents: &'a str, theme: Option<&Theme>) -> tui::text::Text<'a> {
use helix_core::Rope; use helix_core::Rope;
let rope = Rope::from(text.as_ref()); let rope = Rope::from(text.as_ref());
let syntax = syntax::LOADER let syntax = loader
.get()
.unwrap()
.language_config_for_scope(&format!("source.{}", language)) .language_config_for_scope(&format!("source.{}", language))
.and_then(|config| config.highlight_config(theme.scopes())) .and_then(|config| config.highlight_config(theme.scopes()))
.map(|config| Syntax::new(&rope, config)); .map(|config| Syntax::new(&rope, config));
@ -101,9 +108,7 @@ fn parse<'a>(contents: &'a str, theme: Option<&Theme>) -> tui::text::Text<'a> {
} }
HighlightEvent::Source { start, end } => { HighlightEvent::Source { start, end } => {
let style = match highlights.first() { let style = match highlights.first() {
Some(span) => { Some(span) => theme.get(&theme.scopes()[span.0]),
theme.get(theme.scopes()[span.0].as_str())
}
None => text_style, None => text_style,
}; };
@ -196,7 +201,7 @@ impl Component for Markdown {
fn render(&self, area: Rect, surface: &mut Surface, cx: &mut Context) { fn render(&self, area: Rect, surface: &mut Surface, cx: &mut Context) {
use tui::widgets::{Paragraph, Widget, Wrap}; use tui::widgets::{Paragraph, Widget, Wrap};
let text = parse(&self.contents, Some(&cx.editor.theme)); let text = parse(&self.contents, Some(&cx.editor.theme), &self.config_loader);
let par = Paragraph::new(text) let par = Paragraph::new(text)
.wrap(Wrap { trim: false }) .wrap(Wrap { trim: false })
@ -207,7 +212,7 @@ impl Component for Markdown {
} }
fn required_size(&mut self, viewport: (u16, u16)) -> Option<(u16, u16)> { fn required_size(&mut self, viewport: (u16, u16)) -> Option<(u16, u16)> {
let contents = parse(&self.contents, None); let contents = parse(&self.contents, None, &self.config_loader);
let padding = 2; let padding = 2;
let width = std::cmp::min(contents.width() as u16 + padding, viewport.0); let width = std::cmp::min(contents.width() as u16 + padding, viewport.0);
let height = std::cmp::min(contents.height() as u16 + padding, viewport.1); let height = std::cmp::min(contents.height() as u16 + padding, viewport.1);

@ -9,11 +9,11 @@ use std::sync::Arc;
use helix_core::{ use helix_core::{
chars::{char_is_linebreak, char_is_whitespace}, chars::{char_is_linebreak, char_is_whitespace},
history::History, history::History,
syntax::{LanguageConfiguration, LOADER}, syntax::{self, LanguageConfiguration},
ChangeSet, Diagnostic, Rope, Selection, State, Syntax, Transaction, ChangeSet, Diagnostic, Rope, Selection, State, Syntax, Transaction,
}; };
use crate::{DocumentId, ViewId}; use crate::{DocumentId, Theme, ViewId};
use std::collections::HashMap; use std::collections::HashMap;
@ -236,7 +236,11 @@ impl Document {
} }
// TODO: async fn? // TODO: async fn?
pub fn load(path: PathBuf) -> Result<Self, Error> { pub fn load(
path: PathBuf,
theme: Option<&Theme>,
config_loader: Option<&syntax::Loader>,
) -> Result<Self, Error> {
use std::{fs::File, io::BufReader}; use std::{fs::File, io::BufReader};
let doc = if !path.exists() { let doc = if !path.exists() {
@ -256,6 +260,10 @@ impl Document {
doc.set_path(&path)?; doc.set_path(&path)?;
doc.detect_indent_style(); doc.detect_indent_style();
if let Some(loader) = config_loader {
doc.detect_language(theme, loader);
}
Ok(doc) Ok(doc)
} }
@ -330,12 +338,10 @@ impl Document {
} }
} }
fn detect_language(&mut self) { pub fn detect_language(&mut self, theme: Option<&Theme>, config_loader: &syntax::Loader) {
if let Some(path) = self.path() { if let Some(path) = &self.path {
let loader = LOADER.get().unwrap(); let language_config = config_loader.language_config_for_file_name(path);
let language_config = loader.language_config_for_file_name(path); self.set_language(theme, language_config);
let scopes = loader.scopes();
self.set_language(language_config, scopes);
} }
} }
@ -472,18 +478,16 @@ impl Document {
// and error out when document is saved // and error out when document is saved
self.path = Some(path); self.path = Some(path);
// try detecting the language based on filepath
self.detect_language();
Ok(()) Ok(())
} }
pub fn set_language( pub fn set_language(
&mut self, &mut self,
theme: Option<&Theme>,
language_config: Option<Arc<helix_core::syntax::LanguageConfiguration>>, language_config: Option<Arc<helix_core::syntax::LanguageConfiguration>>,
scopes: &[String],
) { ) {
if let Some(language_config) = language_config { if let Some(language_config) = language_config {
let scopes = theme.map(|theme| theme.scopes()).unwrap_or(&[]);
if let Some(highlight_config) = language_config.highlight_config(scopes) { if let Some(highlight_config) = language_config.highlight_config(scopes) {
let syntax = Syntax::new(&self.text, highlight_config); let syntax = Syntax::new(&self.text, highlight_config);
self.syntax = Some(syntax); self.syntax = Some(syntax);
@ -497,12 +501,15 @@ impl Document {
}; };
} }
pub fn set_language2(&mut self, scope: &str) { pub fn set_language2(
let loader = LOADER.get().unwrap(); &mut self,
let language_config = loader.language_config_for_scope(scope); scope: &str,
let scopes = loader.scopes(); theme: Option<&Theme>,
config_loader: Arc<syntax::Loader>,
) {
let language_config = config_loader.language_config_for_scope(scope);
self.set_language(language_config, scopes); self.set_language(theme, language_config);
} }
pub fn set_language_server(&mut self, language_server: Option<Arc<helix_lsp::Client>>) { pub fn set_language_server(&mut self, language_server: Option<Arc<helix_lsp::Client>>) {

@ -1,10 +1,14 @@
use crate::{theme::Theme, tree::Tree, Document, DocumentId, RegisterSelection, View, ViewId}; use crate::{
theme::{self, Theme},
tree::Tree,
Document, DocumentId, RegisterSelection, View, ViewId,
};
use helix_core::syntax;
use tui::layout::Rect; use tui::layout::Rect;
use tui::terminal::CursorKind; use tui::terminal::CursorKind;
use futures_util::future; use futures_util::future;
use std::path::PathBuf; use std::{path::PathBuf, sync::Arc, time::Duration};
use std::time::Duration;
use slotmap::SlotMap; use slotmap::SlotMap;
@ -24,6 +28,9 @@ pub struct Editor {
pub theme: Theme, pub theme: Theme,
pub language_servers: helix_lsp::Registry, pub language_servers: helix_lsp::Registry,
pub syn_loader: Arc<syntax::Loader>,
pub theme_loader: Arc<theme::Loader>,
pub status_msg: Option<(String, Severity)>, pub status_msg: Option<(String, Severity)>,
} }
@ -35,27 +42,11 @@ pub enum Action {
} }
impl Editor { impl Editor {
pub fn new(mut area: tui::layout::Rect) -> Self { pub fn new(
use helix_core::config_dir; mut area: tui::layout::Rect,
let config = std::fs::read(config_dir().join("theme.toml")); themes: Arc<theme::Loader>,
// load $HOME/.config/helix/theme.toml, fallback to default config config_loader: Arc<syntax::Loader>,
let toml = config ) -> Self {
.as_deref()
.unwrap_or(include_bytes!("../../theme.toml"));
let theme: Theme = toml::from_slice(toml).expect("failed to parse theme.toml");
// initialize language registry
use helix_core::syntax::{Loader, LOADER};
// load $HOME/.config/helix/languages.toml, fallback to default config
let config = std::fs::read(helix_core::config_dir().join("languages.toml"));
let toml = config
.as_deref()
.unwrap_or(include_bytes!("../../languages.toml"));
let config = toml::from_slice(toml).expect("Could not parse languages.toml");
LOADER.get_or_init(|| Loader::new(config, theme.scopes().to_vec()));
let language_servers = helix_lsp::Registry::new(); let language_servers = helix_lsp::Registry::new();
// HAXX: offset the render area height by 1 to account for prompt/commandline // HAXX: offset the render area height by 1 to account for prompt/commandline
@ -66,8 +57,10 @@ impl Editor {
documents: SlotMap::with_key(), documents: SlotMap::with_key(),
count: None, count: None,
selected_register: RegisterSelection::default(), selected_register: RegisterSelection::default(),
theme, theme: themes.default(),
language_servers, language_servers,
syn_loader: config_loader,
theme_loader: themes,
registers: Registers::default(), registers: Registers::default(),
status_msg: None, status_msg: None,
} }
@ -85,6 +78,32 @@ impl Editor {
self.status_msg = Some((error, Severity::Error)); self.status_msg = Some((error, Severity::Error));
} }
pub fn set_theme(&mut self, theme: Theme) {
let scopes = theme.scopes();
for config in self
.syn_loader
.language_configs_iter()
.filter(|cfg| cfg.is_highlight_initialized())
{
config.highlight_config(scopes);
}
self.theme = theme;
self._refresh();
}
pub fn set_theme_from_name(&mut self, theme: &str) {
let theme = match self.theme_loader.load(theme.as_ref()) {
Ok(theme) => theme,
Err(e) => {
log::warn!("failed setting theme `{}` - {}", theme, e);
return;
}
};
self.set_theme(theme);
}
fn _refresh(&mut self) { fn _refresh(&mut self) {
for (view, _) in self.tree.views_mut() { for (view, _) in self.tree.views_mut() {
let doc = &self.documents[view.doc]; let doc = &self.documents[view.doc];
@ -168,7 +187,7 @@ impl Editor {
let id = if let Some(id) = id { let id = if let Some(id) = id {
id id
} else { } else {
let mut doc = Document::load(path)?; let mut doc = Document::load(path, Some(&self.theme), Some(&self.syn_loader))?;
// try to find a language server based on the language name // try to find a language server based on the language name
let language_server = doc let language_server = doc
@ -254,6 +273,10 @@ impl Editor {
self.documents.iter().map(|(_id, doc)| doc) self.documents.iter().map(|(_id, doc)| doc)
} }
pub fn documents_mut(&mut self) -> impl Iterator<Item = &mut Document> {
self.documents.iter_mut().map(|(_id, doc)| doc)
}
// pub fn current_document(&self) -> Document { // pub fn current_document(&self) -> Document {
// let id = self.view().doc; // let id = self.view().doc;
// let doc = &mut editor.documents[id]; // let doc = &mut editor.documents[id];

Loading…
Cancel
Save