Add ability to change theme on editor
This commit is contained in:
parent
f424a61054
commit
ce97a2f05f
10 changed files with 242 additions and 142 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -17,6 +17,12 @@ version = "1.0.41"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15af2628f6890fe2609a3b91bef4c83450512802e59489f9c1cb1fa5df064a61"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e906254e445520903e7fc9da4f709886c84ae4bc4ddaf0e093188d66df4dc820"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
|
@ -254,6 +260,7 @@ dependencies = [
|
|||
name = "helix-core"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"etcetera",
|
||||
"helix-syntax",
|
||||
"once_cell",
|
||||
|
|
|
@ -25,6 +25,7 @@ unicode-general-category = "0.4.0"
|
|||
# slab = "0.4.2"
|
||||
tree-sitter = "0.19"
|
||||
once_cell = "1.8"
|
||||
arc-swap = "1"
|
||||
regex = "1"
|
||||
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
|
|
@ -254,26 +254,23 @@ where
|
|||
Configuration, IndentationConfiguration, Lang, LanguageConfiguration, Loader,
|
||||
};
|
||||
use once_cell::sync::OnceCell;
|
||||
let loader = Loader::new(
|
||||
Configuration {
|
||||
language: vec![LanguageConfiguration {
|
||||
scope: "source.rust".to_string(),
|
||||
file_types: vec!["rs".to_string()],
|
||||
language_id: Lang::Rust,
|
||||
highlight_config: OnceCell::new(),
|
||||
//
|
||||
roots: vec![],
|
||||
auto_format: false,
|
||||
language_server: None,
|
||||
indent: Some(IndentationConfiguration {
|
||||
tab_width: 4,
|
||||
unit: String::from(" "),
|
||||
}),
|
||||
indent_query: OnceCell::new(),
|
||||
}],
|
||||
},
|
||||
Vec::new(),
|
||||
);
|
||||
let loader = Loader::new(Configuration {
|
||||
language: vec![LanguageConfiguration {
|
||||
scope: "source.rust".to_string(),
|
||||
file_types: vec!["rs".to_string()],
|
||||
language_id: Lang::Rust,
|
||||
highlight_config: OnceCell::new(),
|
||||
//
|
||||
roots: vec![],
|
||||
auto_format: false,
|
||||
language_server: None,
|
||||
indent: Some(IndentationConfiguration {
|
||||
tab_width: 4,
|
||||
unit: String::from(" "),
|
||||
}),
|
||||
indent_query: OnceCell::new(),
|
||||
}],
|
||||
});
|
||||
|
||||
// set runtime path so we can find the queries
|
||||
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))]
|
||||
fn runtime_dir() -> std::path::PathBuf {
|
||||
pub fn runtime_dir() -> std::path::PathBuf {
|
||||
if let Ok(dir) = std::env::var("HELIX_RUNTIME") {
|
||||
return dir.into();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use crate::{regex::Regex, Change, Rope, RopeSlice, Transaction};
|
||||
pub use helix_syntax::{get_language, get_language_name, Lang};
|
||||
|
||||
use arc_swap::ArcSwap;
|
||||
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
cell::RefCell,
|
||||
|
@ -143,35 +145,48 @@ fn read_query(language: &str, filename: &str) -> String {
|
|||
}
|
||||
|
||||
impl LanguageConfiguration {
|
||||
fn initialize_highlight(&self, scopes: &[String]) -> Option<Arc<HighlightConfiguration>> {
|
||||
let language = get_language_name(self.language_id).to_ascii_lowercase();
|
||||
|
||||
let highlights_query = read_query(&language, "highlights.scm");
|
||||
// always highlight syntax errors
|
||||
// highlights_query += "\n(ERROR) @error";
|
||||
|
||||
let injections_query = read_query(&language, "injections.scm");
|
||||
|
||||
let locals_query = "";
|
||||
|
||||
if highlights_query.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let language = get_language(self.language_id);
|
||||
let mut config = HighlightConfiguration::new(
|
||||
language,
|
||||
&highlights_query,
|
||||
&injections_query,
|
||||
locals_query,
|
||||
)
|
||||
.unwrap(); // TODO: no unwrap
|
||||
config.configure(scopes);
|
||||
Some(Arc::new(config))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn highlight_config(&self, scopes: &[String]) -> Option<Arc<HighlightConfiguration>> {
|
||||
self.highlight_config
|
||||
.get_or_init(|| {
|
||||
let language = get_language_name(self.language_id).to_ascii_lowercase();
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
let highlights_query = read_query(&language, "highlights.scm");
|
||||
// always highlight syntax errors
|
||||
// highlights_query += "\n(ERROR) @error";
|
||||
|
||||
let injections_query = read_query(&language, "injections.scm");
|
||||
|
||||
let locals_query = "";
|
||||
|
||||
if highlights_query.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let language = get_language(self.language_id);
|
||||
let mut config = HighlightConfiguration::new(
|
||||
language,
|
||||
&highlights_query,
|
||||
&injections_query,
|
||||
locals_query,
|
||||
)
|
||||
.unwrap(); // TODO: no unwrap
|
||||
config.configure(scopes);
|
||||
Some(Arc::new(config))
|
||||
}
|
||||
})
|
||||
.clone()
|
||||
pub fn is_highlight_initialized(&self) -> bool {
|
||||
self.highlight_config.get().is_some()
|
||||
}
|
||||
|
||||
pub fn indent_query(&self) -> Option<&IndentQuery> {
|
||||
|
@ -190,22 +205,18 @@ impl LanguageConfiguration {
|
|||
}
|
||||
}
|
||||
|
||||
pub static LOADER: OnceCell<Loader> = OnceCell::new();
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Loader {
|
||||
// highlight_names ?
|
||||
language_configs: Vec<Arc<LanguageConfiguration>>,
|
||||
language_config_ids_by_file_type: HashMap<String, usize>, // Vec<usize>
|
||||
scopes: Vec<String>,
|
||||
}
|
||||
|
||||
impl Loader {
|
||||
pub fn new(config: Configuration, scopes: Vec<String>) -> Self {
|
||||
pub fn new(config: Configuration) -> Self {
|
||||
let mut loader = Self {
|
||||
language_configs: Vec::new(),
|
||||
language_config_ids_by_file_type: HashMap::new(),
|
||||
scopes,
|
||||
};
|
||||
|
||||
for config in config.language {
|
||||
|
@ -225,10 +236,6 @@ impl Loader {
|
|||
loader
|
||||
}
|
||||
|
||||
pub fn scopes(&self) -> &[String] {
|
||||
&self.scopes
|
||||
}
|
||||
|
||||
pub fn language_config_for_file_name(&self, path: &Path) -> Option<Arc<LanguageConfiguration>> {
|
||||
// Find all the language configurations that match this file name
|
||||
// or a suffix of the file name.
|
||||
|
@ -253,6 +260,10 @@ impl Loader {
|
|||
.find(|config| config.scope == scope)
|
||||
.cloned()
|
||||
}
|
||||
|
||||
pub fn language_configs_iter(&self) -> impl Iterator<Item = &Arc<LanguageConfiguration>> {
|
||||
self.language_configs.iter()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TsParser {
|
||||
|
@ -771,7 +782,7 @@ pub struct HighlightConfiguration {
|
|||
combined_injections_query: Option<Query>,
|
||||
locals_pattern_index: usize,
|
||||
highlights_pattern_index: usize,
|
||||
highlight_indices: Vec<Option<Highlight>>,
|
||||
highlight_indices: ArcSwap<Vec<Option<Highlight>>>,
|
||||
non_local_variable_patterns: Vec<bool>,
|
||||
injection_content_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 {
|
||||
language,
|
||||
query,
|
||||
|
@ -956,17 +967,20 @@ impl HighlightConfiguration {
|
|||
///
|
||||
/// When highlighting, results are returned as `Highlight` values, which contain the index
|
||||
/// 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();
|
||||
self.highlight_indices.clear();
|
||||
self.highlight_indices
|
||||
.extend(self.query.capture_names().iter().map(move |capture_name| {
|
||||
let indices: Vec<_> = self
|
||||
.query
|
||||
.capture_names()
|
||||
.iter()
|
||||
.map(move |capture_name| {
|
||||
capture_parts.clear();
|
||||
capture_parts.extend(capture_name.split('.'));
|
||||
|
||||
let mut best_index = None;
|
||||
let mut best_match_len = 0;
|
||||
for (i, recognized_name) in recognized_names.iter().enumerate() {
|
||||
let recognized_name = recognized_name;
|
||||
let mut len = 0;
|
||||
let mut matches = true;
|
||||
for part in recognized_name.split('.') {
|
||||
|
@ -982,7 +996,10 @@ impl HighlightConfiguration {
|
|||
}
|
||||
}
|
||||
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
|
||||
// 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_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};
|
||||
|
||||
|
@ -14,7 +15,7 @@ use std::{
|
|||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::Error;
|
||||
use anyhow::{Context, Error};
|
||||
|
||||
use crossterm::{
|
||||
event::{Event, EventStream},
|
||||
|
@ -36,6 +37,8 @@ pub struct Application {
|
|||
compositor: Compositor,
|
||||
editor: Editor,
|
||||
|
||||
theme_loader: Arc<theme::Loader>,
|
||||
syn_loader: Arc<syntax::Loader>,
|
||||
callbacks: LspCallbacks,
|
||||
|
||||
lsp_progress: LspProgressMap,
|
||||
|
@ -47,7 +50,34 @@ impl Application {
|
|||
use helix_view::editor::Action;
|
||||
let mut compositor = Compositor::new()?;
|
||||
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));
|
||||
compositor.push(editor_view);
|
||||
|
@ -72,10 +102,14 @@ impl Application {
|
|||
editor.new_file(Action::VerticalSplit);
|
||||
}
|
||||
|
||||
editor.set_theme(theme);
|
||||
|
||||
let mut app = Self {
|
||||
compositor,
|
||||
editor,
|
||||
|
||||
theme_loader,
|
||||
syn_loader,
|
||||
callbacks: FuturesUnordered::new(),
|
||||
lsp_progress: LspProgressMap::new(),
|
||||
lsp_progress_enabled: config.global.lsp_progress,
|
||||
|
|
|
@ -246,34 +246,43 @@ impl Component for Completion {
|
|||
value: contents,
|
||||
})) => {
|
||||
// TODO: convert to wrapped text
|
||||
Markdown::new(format!(
|
||||
"```{}\n{}\n```\n{}",
|
||||
language,
|
||||
option.detail.as_deref().unwrap_or_default(),
|
||||
contents.clone()
|
||||
))
|
||||
Markdown::new(
|
||||
format!(
|
||||
"```{}\n{}\n```\n{}",
|
||||
language,
|
||||
option.detail.as_deref().unwrap_or_default(),
|
||||
contents.clone()
|
||||
),
|
||||
cx.editor.syn_loader.clone(),
|
||||
)
|
||||
}
|
||||
Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
|
||||
kind: lsp::MarkupKind::Markdown,
|
||||
value: contents,
|
||||
})) => {
|
||||
// TODO: set language based on doc scope
|
||||
Markdown::new(format!(
|
||||
"```{}\n{}\n```\n{}",
|
||||
language,
|
||||
option.detail.as_deref().unwrap_or_default(),
|
||||
contents.clone()
|
||||
))
|
||||
Markdown::new(
|
||||
format!(
|
||||
"```{}\n{}\n```\n{}",
|
||||
language,
|
||||
option.detail.as_deref().unwrap_or_default(),
|
||||
contents.clone()
|
||||
),
|
||||
cx.editor.syn_loader.clone(),
|
||||
)
|
||||
}
|
||||
None if option.detail.is_some() => {
|
||||
// TODO: copied from above
|
||||
|
||||
// TODO: set language based on doc scope
|
||||
Markdown::new(format!(
|
||||
"```{}\n{}\n```",
|
||||
language,
|
||||
option.detail.as_deref().unwrap_or_default(),
|
||||
))
|
||||
Markdown::new(
|
||||
format!(
|
||||
"```{}\n{}\n```",
|
||||
language,
|
||||
option.detail.as_deref().unwrap_or_default(),
|
||||
),
|
||||
cx.editor.syn_loader.clone(),
|
||||
)
|
||||
}
|
||||
None => return,
|
||||
};
|
||||
|
|
|
@ -7,25 +7,34 @@ use tui::{
|
|||
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};
|
||||
|
||||
pub struct Markdown {
|
||||
contents: String,
|
||||
|
||||
config_loader: Arc<syntax::Loader>,
|
||||
}
|
||||
|
||||
// TODO: pre-render and self reference via Pin
|
||||
// better yet, just use Tendril + subtendril for references
|
||||
|
||||
impl Markdown {
|
||||
pub fn new(contents: String) -> Self {
|
||||
Self { contents }
|
||||
pub fn new(contents: String, config_loader: Arc<syntax::Loader>) -> Self {
|
||||
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 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;
|
||||
|
||||
let rope = Rope::from(text.as_ref());
|
||||
let syntax = syntax::LOADER
|
||||
.get()
|
||||
.unwrap()
|
||||
let syntax = loader
|
||||
.language_config_for_scope(&format!("source.{}", language))
|
||||
.and_then(|config| config.highlight_config(theme.scopes()))
|
||||
.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 } => {
|
||||
let style = match highlights.first() {
|
||||
Some(span) => {
|
||||
theme.get(theme.scopes()[span.0].as_str())
|
||||
}
|
||||
Some(span) => theme.get(&theme.scopes()[span.0]),
|
||||
None => text_style,
|
||||
};
|
||||
|
||||
|
@ -196,7 +201,7 @@ impl Component for Markdown {
|
|||
fn render(&self, area: Rect, surface: &mut Surface, cx: &mut Context) {
|
||||
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)
|
||||
.wrap(Wrap { trim: false })
|
||||
|
@ -207,7 +212,7 @@ impl Component for Markdown {
|
|||
}
|
||||
|
||||
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 width = std::cmp::min(contents.width() as u16 + padding, viewport.0);
|
||||
let height = std::cmp::min(contents.height() as u16 + padding, viewport.1);
|
||||
|
|
|
@ -9,11 +9,11 @@ use std::sync::Arc;
|
|||
use helix_core::{
|
||||
chars::{char_is_linebreak, char_is_whitespace},
|
||||
history::History,
|
||||
syntax::{LanguageConfiguration, LOADER},
|
||||
syntax::{self, LanguageConfiguration},
|
||||
ChangeSet, Diagnostic, Rope, Selection, State, Syntax, Transaction,
|
||||
};
|
||||
|
||||
use crate::{DocumentId, ViewId};
|
||||
use crate::{DocumentId, Theme, ViewId};
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
@ -236,7 +236,11 @@ impl Document {
|
|||
}
|
||||
|
||||
// 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};
|
||||
|
||||
let doc = if !path.exists() {
|
||||
|
@ -256,6 +260,10 @@ impl Document {
|
|||
doc.set_path(&path)?;
|
||||
doc.detect_indent_style();
|
||||
|
||||
if let Some(loader) = config_loader {
|
||||
doc.detect_language(theme, loader);
|
||||
}
|
||||
|
||||
Ok(doc)
|
||||
}
|
||||
|
||||
|
@ -330,12 +338,10 @@ impl Document {
|
|||
}
|
||||
}
|
||||
|
||||
fn detect_language(&mut self) {
|
||||
if let Some(path) = self.path() {
|
||||
let loader = LOADER.get().unwrap();
|
||||
let language_config = loader.language_config_for_file_name(path);
|
||||
let scopes = loader.scopes();
|
||||
self.set_language(language_config, scopes);
|
||||
pub fn detect_language(&mut self, theme: Option<&Theme>, config_loader: &syntax::Loader) {
|
||||
if let Some(path) = &self.path {
|
||||
let language_config = config_loader.language_config_for_file_name(path);
|
||||
self.set_language(theme, language_config);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -472,18 +478,16 @@ impl Document {
|
|||
// and error out when document is saved
|
||||
self.path = Some(path);
|
||||
|
||||
// try detecting the language based on filepath
|
||||
self.detect_language();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_language(
|
||||
&mut self,
|
||||
theme: Option<&Theme>,
|
||||
language_config: Option<Arc<helix_core::syntax::LanguageConfiguration>>,
|
||||
scopes: &[String],
|
||||
) {
|
||||
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) {
|
||||
let syntax = Syntax::new(&self.text, highlight_config);
|
||||
self.syntax = Some(syntax);
|
||||
|
@ -497,12 +501,15 @@ impl Document {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn set_language2(&mut self, scope: &str) {
|
||||
let loader = LOADER.get().unwrap();
|
||||
let language_config = loader.language_config_for_scope(scope);
|
||||
let scopes = loader.scopes();
|
||||
pub fn set_language2(
|
||||
&mut self,
|
||||
scope: &str,
|
||||
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>>) {
|
||||
|
|
|
@ -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::terminal::CursorKind;
|
||||
|
||||
use futures_util::future;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
use std::{path::PathBuf, sync::Arc, time::Duration};
|
||||
|
||||
use slotmap::SlotMap;
|
||||
|
||||
|
@ -24,6 +28,9 @@ pub struct Editor {
|
|||
pub theme: Theme,
|
||||
pub language_servers: helix_lsp::Registry,
|
||||
|
||||
pub syn_loader: Arc<syntax::Loader>,
|
||||
pub theme_loader: Arc<theme::Loader>,
|
||||
|
||||
pub status_msg: Option<(String, Severity)>,
|
||||
}
|
||||
|
||||
|
@ -35,27 +42,11 @@ pub enum Action {
|
|||
}
|
||||
|
||||
impl Editor {
|
||||
pub fn new(mut area: tui::layout::Rect) -> Self {
|
||||
use helix_core::config_dir;
|
||||
let config = std::fs::read(config_dir().join("theme.toml"));
|
||||
// load $HOME/.config/helix/theme.toml, fallback to default config
|
||||
let toml = config
|
||||
.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()));
|
||||
|
||||
pub fn new(
|
||||
mut area: tui::layout::Rect,
|
||||
themes: Arc<theme::Loader>,
|
||||
config_loader: Arc<syntax::Loader>,
|
||||
) -> Self {
|
||||
let language_servers = helix_lsp::Registry::new();
|
||||
|
||||
// HAXX: offset the render area height by 1 to account for prompt/commandline
|
||||
|
@ -66,8 +57,10 @@ impl Editor {
|
|||
documents: SlotMap::with_key(),
|
||||
count: None,
|
||||
selected_register: RegisterSelection::default(),
|
||||
theme,
|
||||
theme: themes.default(),
|
||||
language_servers,
|
||||
syn_loader: config_loader,
|
||||
theme_loader: themes,
|
||||
registers: Registers::default(),
|
||||
status_msg: None,
|
||||
}
|
||||
|
@ -85,6 +78,32 @@ impl Editor {
|
|||
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) {
|
||||
for (view, _) in self.tree.views_mut() {
|
||||
let doc = &self.documents[view.doc];
|
||||
|
@ -168,7 +187,7 @@ impl Editor {
|
|||
let id = if let Some(id) = id {
|
||||
id
|
||||
} 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
|
||||
let language_server = doc
|
||||
|
@ -254,6 +273,10 @@ impl Editor {
|
|||
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 {
|
||||
// let id = self.view().doc;
|
||||
// let doc = &mut editor.documents[id];
|
||||
|
|
Loading…
Reference in a new issue