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

View file

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

View file

@ -12,9 +12,6 @@ use std::borrow::Cow;
use helix_core::Position; use helix_core::Position;
use helix_view::Editor; 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> { pub struct Menu<T> {
options: Vec<T>, options: Vec<T>,