syntax: Reuse parser instances. highlight_iter() no longer needs &mut.

This commit is contained in:
Blaž Hrastnik 2021-03-12 14:46:23 +09:00
parent 9dcfe25e4a
commit b7dd7310c4
3 changed files with 71 additions and 57 deletions

View file

@ -2,11 +2,12 @@ use crate::{Change, Rope, RopeSlice, Transaction};
pub use helix_syntax::Lang;
pub use helix_syntax::{get_language, get_language_name};
use std::cell::RefCell;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use once_cell::sync::OnceCell;
use once_cell::sync::{Lazy, OnceCell};
// largely based on tree-sitter/cli/src/loader.rs
pub struct LanguageConfiguration {
@ -65,8 +66,6 @@ impl LanguageConfiguration {
}
}
use once_cell::sync::Lazy;
pub static LOADER: Lazy<Loader> = Lazy::new(Loader::init);
pub struct Loader {
@ -145,13 +144,20 @@ impl Loader {
}
}
//
pub struct TSParser {
parser: tree_sitter::Parser,
cursors: Vec<QueryCursor>,
}
// could also just use a pool, or a single instance?
thread_local! {
pub static PARSER: RefCell<TSParser> = RefCell::new(TSParser {
parser: Parser::new(),
cursors: Vec::new(),
})
}
pub struct Syntax {
// grammar: Grammar,
parser: Parser,
cursors: Vec<QueryCursor>,
config: Arc<HighlightConfiguration>,
pub(crate) root_layer: LanguageLayer,
@ -163,10 +169,6 @@ impl Syntax {
/*language: Lang,*/ source: &Rope,
config: Arc<HighlightConfiguration>,
) -> Self {
// fetch grammar for parser based on language string
// let grammar = get_language(&language);
let parser = Parser::new();
let root_layer = LanguageLayer { tree: None };
// track markers of injections
@ -174,25 +176,25 @@ impl Syntax {
let mut syntax = Self {
// grammar,
parser,
cursors: Vec::new(),
config,
root_layer,
};
// update root layer
syntax.root_layer.parse(
&mut syntax.parser,
&syntax.config,
source,
0,
vec![Range {
start_byte: 0,
end_byte: usize::MAX,
start_point: Point::new(0, 0),
end_point: Point::new(usize::MAX, usize::MAX),
}],
);
PARSER.with(|ts_parser| {
syntax.root_layer.parse(
&mut ts_parser.borrow_mut(),
&syntax.config,
source,
0,
vec![Range {
start_byte: 0,
end_byte: usize::MAX,
start_point: Point::new(0, 0),
end_point: Point::new(usize::MAX, usize::MAX),
}],
);
});
syntax
}
@ -202,13 +204,15 @@ impl Syntax {
source: &Rope,
changeset: &ChangeSet,
) -> Result<(), Error> {
self.root_layer.update(
&mut self.parser,
&self.config,
old_source,
source,
changeset,
)
PARSER.with(|ts_parser| {
self.root_layer.update(
&mut ts_parser.borrow_mut(),
&self.config,
old_source,
source,
changeset,
)
})
// TODO: deal with injections and update them too
}
@ -229,7 +233,8 @@ impl Syntax {
/// Iterate over the highlighted regions for a given slice of source code.
pub fn highlight_iter<'a>(
&'a mut self,
&self,
ts_parser: &'a mut TSParser,
source: &'a [u8],
range: Option<std::ops::Range<usize>>,
cancellation_flag: Option<&'a AtomicUsize>,
@ -283,7 +288,7 @@ impl Syntax {
byte_offset: range.map(|r| r.start).unwrap_or(0), // TODO: simplify
injection_callback,
cancellation_flag,
highlighter: self,
highlighter: ts_parser,
iter_count: 0,
layers: vec![layer],
next_event: None,
@ -336,19 +341,21 @@ impl LanguageLayer {
fn parse(
&mut self,
parser: &mut Parser,
ts_parser: &mut TSParser,
config: &HighlightConfiguration,
source: &Rope,
mut depth: usize,
mut ranges: Vec<Range>,
) -> Result<(), Error> {
if parser.set_included_ranges(&ranges).is_ok() {
parser
if ts_parser.parser.set_included_ranges(&ranges).is_ok() {
ts_parser
.parser
.set_language(config.language)
.map_err(|_| Error::InvalidLanguage)?;
// unsafe { syntax.parser.set_cancellation_flag(cancellation_flag) };
let tree = parser
let tree = ts_parser
.parser
.parse_with(
&mut |byte, _| {
if byte <= source.len_bytes() {
@ -517,7 +524,7 @@ impl LanguageLayer {
fn update(
&mut self,
parser: &mut Parser,
ts_parser: &mut TSParser,
config: &HighlightConfiguration,
old_source: &Rope,
source: &Rope,
@ -535,7 +542,7 @@ impl LanguageLayer {
}
self.parse(
parser,
ts_parser,
config,
source,
0,
@ -652,7 +659,7 @@ where
{
source: &'a [u8],
byte_offset: usize,
highlighter: &'a mut Syntax,
highlighter: &'a mut TSParser,
injection_callback: F,
cancellation_flag: Option<&'a AtomicUsize>,
layers: Vec<HighlightIterLayer<'a>>,
@ -839,7 +846,7 @@ impl<'a> HighlightIterLayer<'a> {
/// added to the returned vector.
fn new<F: FnMut(&str) -> Option<&'a HighlightConfiguration> + 'a>(
source: &'a [u8],
highlighter: &mut Syntax,
highlighter: &mut TSParser,
cancellation_flag: Option<&'a AtomicUsize>,
injection_callback: &mut F,
mut config: &'a HighlightConfiguration,

View file

@ -3,7 +3,11 @@ use crate::compositor::{Component, Compositor, Context, EventResult};
use crate::keymap::{self, Keymaps};
use crate::ui::text_color;
use helix_core::{indent::TAB_WIDTH, syntax::HighlightEvent, Position, Range, State};
use helix_core::{
indent::TAB_WIDTH,
syntax::{self, HighlightEvent},
Position, Range, State,
};
use helix_view::{document::Mode, Document, Editor, Theme, View};
use std::borrow::Cow;
@ -34,7 +38,7 @@ impl EditorView {
}
pub fn render_view(
&self,
view: &mut View,
view: &View,
viewport: Rect,
surface: &mut Surface,
theme: &Theme,
@ -61,10 +65,9 @@ impl EditorView {
self.render_statusline(&view.doc, area, surface, theme, is_focused);
}
// TODO: ideally not &mut View but highlights require it because of cursor cache
pub fn render_buffer(
&self,
view: &mut View,
view: &View,
viewport: Rect,
surface: &mut Surface,
theme: &Theme,
@ -79,7 +82,7 @@ impl EditorView {
let range = {
// calculate viewport byte ranges
let start = text.line_to_byte(view.first_line);
let end = text.line_to_byte(last_line + 1); // TODO: double check
let end = text.line_to_byte(last_line + 1);
start..end
};
@ -87,12 +90,20 @@ impl EditorView {
// TODO: range doesn't actually restrict source, just highlight range
// TODO: cache highlight results
// TODO: only recalculate when state.doc is actually modified
let highlights: Vec<_> = match view.doc.syntax.as_mut() {
let highlights: Vec<_> = match &view.doc.syntax {
Some(syntax) => {
syntax
.highlight_iter(source_code.as_bytes(), Some(range), None, |_| None)
.unwrap()
.collect() // TODO: we collect here to avoid double borrow, fix later
syntax::PARSER.with(|ts_parser| {
syntax
.highlight_iter(
&mut ts_parser.borrow_mut(),
source_code.as_bytes(),
Some(range),
None,
|_| None,
)
.unwrap()
.collect() // TODO: we collect here to avoid holding the lock, fix later
})
}
None => vec![Ok(HighlightEvent::Source {
start: range.start,
@ -102,7 +113,6 @@ impl EditorView {
let mut spans = Vec::new();
let mut visual_x = 0;
let mut line = 0u16;
let text = view.doc.text();
'outer: for event in highlights {
match event.unwrap() {

View file

@ -12,9 +12,6 @@ use std::borrow::Cow;
use helix_core::Position;
use helix_view::Editor;
// TODO: factor out a popup component that we can reuse for displaying docs on autocomplete,
// diagnostics popups, etc.
pub struct Menu<T> {
options: Vec<T>,