highlighter: Avoid crash when language not registered. (#954)
Close #952 Fallback to use plain text to display.
This commit is contained in:
parent
1032881103
commit
89c210c4ca
3 changed files with 64 additions and 51 deletions
|
|
@ -118,11 +118,12 @@ impl Example {
|
||||||
&language_state,
|
&language_state,
|
||||||
|this, state, _: &DropdownEvent<Vec<SharedString>>, cx| {
|
|this, state, _: &DropdownEvent<Vec<SharedString>>, cx| {
|
||||||
if let Some(val) = state.read(cx).selected_value() {
|
if let Some(val) = state.read(cx).selected_value() {
|
||||||
if let Some(language) = Language::from_str(&val) {
|
if val == "navi" {
|
||||||
this.language = Lang::BuiltIn(language);
|
|
||||||
} else {
|
|
||||||
this.language = Lang::External("navi");
|
this.language = Lang::External("navi");
|
||||||
|
} else {
|
||||||
|
this.language = Lang::BuiltIn(Language::from_str(&val));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.need_update = true;
|
this.need_update = true;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use super::HighlightTheme;
|
use super::HighlightTheme;
|
||||||
use crate::highlighter::LanguageRegistry;
|
use crate::highlighter::LanguageRegistry;
|
||||||
|
use anyhow::{anyhow, Context, Result};
|
||||||
use gpui::{App, HighlightStyle, SharedString};
|
use gpui::{App, HighlightStyle, SharedString};
|
||||||
use indexset::BTreeMap;
|
use indexset::BTreeMap;
|
||||||
use std::{
|
use std::{
|
||||||
|
|
@ -44,21 +45,34 @@ pub struct SyntaxHighlighter {
|
||||||
impl SyntaxHighlighter {
|
impl SyntaxHighlighter {
|
||||||
/// Create a new SyntaxHighlighter for HTML.
|
/// Create a new SyntaxHighlighter for HTML.
|
||||||
pub fn new(lang: &str, cx: &App) -> Self {
|
pub fn new(lang: &str, cx: &App) -> Self {
|
||||||
Self::build_combined_injections_query(&lang, cx).unwrap_or_else(|| panic!(
|
match Self::build_combined_injections_query(&lang, cx) {
|
||||||
"failed to build language {}, please make sure have registered the language in LanguageRegistry",
|
Ok(result) => result,
|
||||||
lang
|
Err(err) => {
|
||||||
))
|
tracing::warn!(
|
||||||
|
"SyntaxHighlighter init failed, fallback to use `text`, {}",
|
||||||
|
err
|
||||||
|
);
|
||||||
|
Self::build_combined_injections_query("text", cx).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build the combined injections query for the given language.
|
/// Build the combined injections query for the given language.
|
||||||
///
|
///
|
||||||
/// https://github.com/tree-sitter/tree-sitter/blob/v0.25.5/highlight/src/lib.rs#L336
|
/// https://github.com/tree-sitter/tree-sitter/blob/v0.25.5/highlight/src/lib.rs#L336
|
||||||
fn build_combined_injections_query(lang: &str, cx: &App) -> Option<Self> {
|
fn build_combined_injections_query(lang: &str, cx: &App) -> Result<Self> {
|
||||||
let registry = LanguageRegistry::global(cx);
|
let registry = LanguageRegistry::global(cx);
|
||||||
let config = registry.language(&lang)?;
|
let Some(config) = registry.language(&lang) else {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"language {:?} is not registered in `LanguageRegistry`",
|
||||||
|
lang
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
let mut parser = Parser::new();
|
let mut parser = Parser::new();
|
||||||
_ = parser.set_language(&config.language);
|
parser
|
||||||
|
.set_language(&config.language)
|
||||||
|
.context("parse set_language")?;
|
||||||
|
|
||||||
// Concatenate the query strings, keeping track of the start offset of each section.
|
// Concatenate the query strings, keeping track of the start offset of each section.
|
||||||
let mut query_source = String::new();
|
let mut query_source = String::new();
|
||||||
|
|
@ -70,12 +84,7 @@ impl SyntaxHighlighter {
|
||||||
|
|
||||||
// Construct a single query by concatenating the three query strings, but record the
|
// Construct a single query by concatenating the three query strings, but record the
|
||||||
// range of pattern indices that belong to each individual string.
|
// range of pattern indices that belong to each individual string.
|
||||||
let query = match Query::new(&config.language, &query_source) {
|
let query = Query::new(&config.language, &query_source).context("new query")?;
|
||||||
Ok(query) => Some(query),
|
|
||||||
Err(err) => {
|
|
||||||
panic!("failed create Query for language {}, err: {}", lang, err);
|
|
||||||
}
|
|
||||||
}?;
|
|
||||||
|
|
||||||
let mut locals_pattern_index = 0;
|
let mut locals_pattern_index = 0;
|
||||||
let mut highlights_pattern_index = 0;
|
let mut highlights_pattern_index = 0;
|
||||||
|
|
@ -164,7 +173,7 @@ impl SyntaxHighlighter {
|
||||||
|
|
||||||
// let highlight_indices = vec![None; query.capture_names().len()];
|
// let highlight_indices = vec![None; query.capture_names().len()];
|
||||||
|
|
||||||
Some(Self {
|
Ok(Self {
|
||||||
language: config.name.clone(),
|
language: config.name.clone(),
|
||||||
query: Some(query),
|
query: Some(query),
|
||||||
injection_queries,
|
injection_queries,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use gpui::SharedString;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, enum_iterator::Sequence)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, enum_iterator::Sequence)]
|
||||||
pub enum Language {
|
pub enum Language {
|
||||||
|
Plain,
|
||||||
Bash,
|
Bash,
|
||||||
C,
|
C,
|
||||||
CMake,
|
CMake,
|
||||||
|
|
@ -78,6 +79,7 @@ impl Language {
|
||||||
|
|
||||||
pub fn name(&self) -> &'static str {
|
pub fn name(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
|
Self::Plain => "text",
|
||||||
Self::Bash => "bash",
|
Self::Bash => "bash",
|
||||||
Self::C => "c",
|
Self::C => "c",
|
||||||
Self::CMake => "cmake",
|
Self::CMake => "cmake",
|
||||||
|
|
@ -113,41 +115,41 @@ impl Language {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_str(s: &str) -> Option<Self> {
|
pub fn from_str(s: &str) -> Self {
|
||||||
match s {
|
match s {
|
||||||
"bash" | "sh" => Some(Self::Bash),
|
"bash" | "sh" => Self::Bash,
|
||||||
"c" => Some(Self::C),
|
"c" => Self::C,
|
||||||
"cmake" => Some(Self::CMake),
|
"cmake" => Self::CMake,
|
||||||
"cpp" | "c++" => Some(Self::Cpp),
|
"cpp" | "c++" => Self::Cpp,
|
||||||
"csharp" | "cs" => Some(Self::CSharp),
|
"csharp" | "cs" => Self::CSharp,
|
||||||
"css" | "scss" => Some(Self::Css),
|
"css" | "scss" => Self::Css,
|
||||||
"diff" => Some(Self::Diff),
|
"diff" => Self::Diff,
|
||||||
"ejs" => Some(Self::Ejs),
|
"ejs" => Self::Ejs,
|
||||||
"elixir" | "ex" => Some(Self::Elixir),
|
"elixir" | "ex" => Self::Elixir,
|
||||||
"erb" => Some(Self::Erb),
|
"erb" => Self::Erb,
|
||||||
"go" => Some(Self::Go),
|
"go" => Self::Go,
|
||||||
"graphql" => Some(Self::GraphQL),
|
"graphql" => Self::GraphQL,
|
||||||
"html" => Some(Self::Html),
|
"html" => Self::Html,
|
||||||
"java" => Some(Self::Java),
|
"java" => Self::Java,
|
||||||
"javascript" | "js" => Some(Self::JavaScript),
|
"javascript" | "js" => Self::JavaScript,
|
||||||
"jsdoc" => Some(Self::JsDoc),
|
"jsdoc" => Self::JsDoc,
|
||||||
"json" | "jsonc" => Some(Self::Json),
|
"json" | "jsonc" => Self::Json,
|
||||||
"make" | "makefile" => Some(Self::Make),
|
"make" | "makefile" => Self::Make,
|
||||||
"markdown" | "md" | "mdx" => Some(Self::Markdown),
|
"markdown" | "md" | "mdx" => Self::Markdown,
|
||||||
"markdown_inline" | "markdown-inline" => Some(Self::MarkdownInline),
|
"markdown_inline" | "markdown-inline" => Self::MarkdownInline,
|
||||||
"proto" | "protobuf" => Some(Self::Proto),
|
"proto" | "protobuf" => Self::Proto,
|
||||||
"python" | "py" => Some(Self::Python),
|
"python" | "py" => Self::Python,
|
||||||
"ruby" | "rb" => Some(Self::Ruby),
|
"ruby" | "rb" => Self::Ruby,
|
||||||
"rust" | "rs" => Some(Self::Rust),
|
"rust" | "rs" => Self::Rust,
|
||||||
"scala" => Some(Self::Scala),
|
"scala" => Self::Scala,
|
||||||
"sql" => Some(Self::Sql),
|
"sql" => Self::Sql,
|
||||||
"swift" => Some(Self::Swift),
|
"swift" => Self::Swift,
|
||||||
"toml" => Some(Self::Toml),
|
"toml" => Self::Toml,
|
||||||
"tsx" => Some(Self::Tsx),
|
"tsx" => Self::Tsx,
|
||||||
"typescript" | "ts" => Some(Self::TypeScript),
|
"typescript" | "ts" => Self::TypeScript,
|
||||||
"yaml" | "yml" => Some(Self::Yaml),
|
"yaml" | "yml" => Self::Yaml,
|
||||||
"zig" => Some(Self::Zig),
|
"zig" => Self::Zig,
|
||||||
_ => None,
|
_ => Self::Plain,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -182,6 +184,7 @@ impl Language {
|
||||||
/// (language, query, injection, locals)
|
/// (language, query, injection, locals)
|
||||||
pub(super) fn config(&self) -> LanguageConfig {
|
pub(super) fn config(&self) -> LanguageConfig {
|
||||||
let (language, query, injection, locals) = match self {
|
let (language, query, injection, locals) = match self {
|
||||||
|
Self::Plain => (tree_sitter_json::LANGUAGE, "", "", ""),
|
||||||
Self::Json => (
|
Self::Json => (
|
||||||
tree_sitter_json::LANGUAGE,
|
tree_sitter_json::LANGUAGE,
|
||||||
include_str!("languages/json/highlights.scm"),
|
include_str!("languages/json/highlights.scm"),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue