Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
vv9k committed Jun 15, 2021
1 parent a178d4a commit b3b6601
Show file tree
Hide file tree
Showing 10 changed files with 283 additions and 103 deletions.
2 changes: 1 addition & 1 deletion helix-core/src/indent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,10 +260,10 @@ where
scope: "source.rust".to_string(),
file_types: vec!["rs".to_string()],
language_id: Lang::Rust,
highlight_config: OnceCell::new(),
//
roots: vec![],
auto_format: false,
highlight_config: None,
language_server: None,
indent: Some(IndentationConfiguration {
tab_width: 4,
Expand Down
53 changes: 24 additions & 29 deletions helix-core/src/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ use std::{
borrow::Cow,
cell::RefCell,
collections::{HashMap, HashSet},
convert::TryInto,
fmt,
path::{Path, PathBuf},
sync::Arc,
sync::{Arc, Mutex},
};

use once_cell::sync::{Lazy, OnceCell};
Expand Down Expand Up @@ -35,9 +36,9 @@ pub struct LanguageConfiguration {
// injection_regex
// first_line_regex
//
#[serde(skip)]
pub(crate) highlight_config: OnceCell<Option<Arc<HighlightConfiguration>>>,
// tags_config OnceCell<> https://github.com/tree-sitter/tree-sitter/pull/583
#[serde(skip)]
pub highlight_config: Option<Arc<Mutex<HighlightConfiguration>>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub language_server: Option<LanguageServerConfiguration>,
#[serde(skip_serializing_if = "Option::is_none")]
Expand Down Expand Up @@ -144,34 +145,30 @@ fn read_query(language: &str, filename: &str) -> String {

impl LanguageConfiguration {
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();
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 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 injections_query = read_query(&language, "injections.scm");

let locals_query = "";
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()
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 indent_query(&self) -> Option<&IndentQuery> {
Expand All @@ -190,8 +187,6 @@ impl LanguageConfiguration {
}
}

pub static LOADER: OnceCell<Loader> = OnceCell::new();

#[derive(Debug)]
pub struct Loader {
// highlight_names ?
Expand Down
59 changes: 56 additions & 3 deletions helix-term/src/application.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
use dirs_next::config_dir;
use helix_core::syntax;
use helix_lsp::lsp;
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, ui};

use log::{error, info};

use std::{
collections::HashMap,
future::Future,
io::{self, stdout, Stdout, Write},
path::PathBuf,
sync::Arc,
time::Duration,
};

use anyhow::Error;
use serde::{Deserialize, Serialize};

use anyhow::{Context, Error};

use crossterm::{
event::{Event, EventStream},
Expand All @@ -32,10 +37,17 @@ pub type LspCallback =
pub type LspCallbacks = FuturesUnordered<LspCallback>;
pub type LspCallbackWrapper = Box<dyn FnOnce(&mut Editor, &mut Compositor) + Send>;

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Configuration {
pub theme: Option<String>,
}

pub struct Application {
compositor: Compositor,
editor: Editor,

theme_loader: Arc<theme::Loader>,
syn_loader: Arc<syntax::Loader>,
callbacks: LspCallbacks,
}

Expand All @@ -44,7 +56,44 @@ 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));

// 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 app_conf = Configuration { theme: None };
let app_conf = std::fs::read(conf_dir.join("config.toml"))
.context("failed to read global configuration")
.and_then(|v| {
toml::from_slice(v.as_slice()).context("failed to deserialize config.toml")
})
.unwrap_or_else(|_| app_conf);

let theme = if let Some(theme) = &app_conf.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,
theme.scopes().to_vec(),
));

let mut editor = Editor::new(size, theme_loader.clone(), syn_loader.clone());

compositor.push(Box::new(ui::EditorView::new()));

Expand All @@ -68,10 +117,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(),
};

Expand Down
33 changes: 32 additions & 1 deletion helix-term/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1134,6 +1134,23 @@ mod cmd {
_quit_all(editor, args, event, true)
}

fn theme(editor: &mut Editor, args: &[&str], event: PromptEvent) {
let theme = if let Some(theme) = args.first() {
theme
} else {
editor.set_error("theme name not provided".into());
return;
};

editor.set_theme_from_name(theme);
}

fn list_themes(editor: &mut Editor, args: &[&str], event: PromptEvent) {
let themes = editor.theme_loader.as_ref().names();
let status = format!("Available themes: {}", themes.join(", "));
editor.set_status(status);
}

pub const COMMAND_LIST: &[Command] = &[
Command {
name: "quit",
Expand Down Expand Up @@ -1247,6 +1264,20 @@ mod cmd {
fun: force_quit_all,
completer: None,
},
Command {
name: "theme",
alias: None,
doc: "Change the theme of current view. Requires theme name as argument (:theme <name>)",
fun: theme,
completer: None,
},
Command {
name: "list-themes",
alias: None,
doc: "List available themes.",
fun: list_themes,
completer: None,
},

];

Expand Down Expand Up @@ -2648,7 +2679,7 @@ pub fn hover(cx: &mut Context) {

// skip if contents empty

let contents = ui::Markdown::new(contents);
let contents = ui::Markdown::new(contents, editor.syn_loader.clone());
let mut popup = Popup::new(contents);
compositor.push(Box::new(popup));
}
Expand Down
2 changes: 1 addition & 1 deletion helix-term/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use helix_term::application::Application;
use helix_term::application::{self, Application};
use helix_term::args::Args;

use std::path::PathBuf;
Expand Down
43 changes: 26 additions & 17 deletions helix-term/src/ui/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down
27 changes: 17 additions & 10 deletions helix-term/src/ui/markdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -196,7 +203,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 })
Expand All @@ -207,7 +214,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);
Expand Down
Loading

0 comments on commit b3b6601

Please sign in to comment.