more integration, not pretty but still making progress

pull/8675/merge^2
mattwparas 1 year ago
parent db5bf128bb
commit d0d9f7b5b0

@ -1,29 +1,111 @@
use steel::gc::unsafe_erased_pointers::CustomReference; use std::{borrow::Cow, cell::Cell, rc::Rc};
use ropey::iter::Chars;
use steel::{
gc::unsafe_erased_pointers::CustomReference,
rvals::Custom,
steel_vm::{builtin::BuiltInModule, register_fn::RegisterFn, register_fn::RegisterFnBorrowed},
};
impl steel::rvals::Custom for crate::Position {} impl steel::rvals::Custom for crate::Position {}
impl steel::rvals::Custom for crate::Selection {} impl steel::rvals::Custom for crate::Selection {}
struct SRopeSlice<'a>(crate::RopeSlice<'a>); pub struct SRopeSlice<'a> {
slice: crate::RopeSlice<'a>,
}
steel::custom_reference!(SRopeSlice<'a>); steel::custom_reference!(SRopeSlice<'a>);
impl<'a> CustomReference for SRopeSlice<'a> {} impl<'a> CustomReference for SRopeSlice<'a> {}
// impl Custom for SRopeSlice<'static> {}
pub struct SRopeSliceCowStr<'a>(Cow<'a, str>);
steel::custom_reference!(SRopeSliceCowStr<'a>);
impl<'a> CustomReference for SRopeSliceCowStr<'a> {}
struct CharIter<'a>(Chars<'a>);
impl<'a> SRopeSlice<'a> { impl<'a> SRopeSlice<'a> {
pub fn new(slice: crate::RopeSlice<'a>) -> Self {
Self { slice }
}
pub fn char_to_byte(&self, pos: usize) -> usize { pub fn char_to_byte(&self, pos: usize) -> usize {
self.0.char_to_byte(pos) self.slice.char_to_byte(pos)
} }
pub fn byte_slice(&'a self, lower: usize, upper: usize) -> SRopeSlice<'a> { pub fn byte_slice(&'a self, lower: usize, upper: usize) -> SRopeSlice<'a> {
SRopeSlice(self.0.byte_slice(lower..upper)) SRopeSlice {
slice: self.slice.byte_slice(lower..upper),
}
} }
pub fn line(&'a self, cursor: usize) -> SRopeSlice<'a> { pub fn line(&'a self, cursor: usize) -> SRopeSlice<'a> {
SRopeSlice(self.0.line(cursor)) SRopeSlice {
slice: self.slice.line(cursor),
}
}
pub fn as_cow(&'a self) -> SRopeSliceCowStr<'a> {
SRopeSliceCowStr(std::borrow::Cow::from(self.slice))
}
pub fn to_string(&self) -> String {
self.slice.to_string()
}
pub fn len_chars(&'a self) -> usize {
self.slice.len_chars()
}
pub fn slice(&'a self, lower: usize, upper: usize) -> SRopeSlice<'a> {
SRopeSlice {
slice: self.slice.byte_slice(lower..upper),
}
} }
// Reference types are really sus. Not sure how this is going to work, but it might? Hopefully it cleans pub fn get_char(&'a self, index: usize) -> Option<char> {
// itself up as we go... self.slice.get_char(index)
pub fn as_str(&'a self) -> Option<&'a str> {
self.0.as_str()
} }
} }
// RegisterFn::<
// _,
// steel::steel_vm::register_fn::MarkerWrapper7<(
// Context<'_>,
// helix_view::Editor,
// helix_view::Editor,
// Context<'static>,
// )>,
// helix_view::Editor,
// >::register_fn(&mut engine, "cx-editor!", get_editor);
pub fn rope_slice_module() -> BuiltInModule {
let mut module = BuiltInModule::new("helix/core/text");
// (SELF, ARG, SELFSTAT, RET, RETSTAT)
RegisterFnBorrowed::<
_,
steel::steel_vm::register_fn::MarkerWrapper9<(
SRopeSlice<'_>,
usize,
SRopeSlice<'static>,
SRopeSlice<'_>,
SRopeSlice<'static>,
)>,
SRopeSlice,
>::register_fn_borrowed(&mut module, "slice->line", SRopeSlice::line);
// TODO: Note the difficulty of the lifetime params here
module.register_fn("slice->string", SRopeSlice::to_string);
// module
// .register_fn("slice-char->byte", SRopeSlice::char_to_byte)
// .register_fn_borrowed::<S("slice->line", SRopeSlice::line);
// .register_fn("slice->byte-slice", SRopeSlice::byte_slice);
// module.register_fn("slice-len-chars", SRopeSlice::len_chars);
module
}

@ -758,184 +758,14 @@ static LISP_WORDS: Lazy<std::collections::HashSet<&'static str>> = Lazy::new(||
words.iter().copied().collect() words.iter().copied().collect()
}); });
// TODO: Allow for injecting hooks on indent
#[allow(clippy::too_many_arguments)]
fn call_indent_hook(
language_config: Option<&LanguageConfiguration>,
syntax: Option<&Syntax>,
indent_style: &IndentStyle,
tab_width: usize,
text: RopeSlice,
line_before: usize,
line_before_end_pos: usize,
current_line: usize,
) -> Option<String> {
if let Some(config) = language_config {
// TODO: If possible, this would be very cool to be implemented in steel itself. If not,
// a rust native method that is embedded in a dylib that this uses would also be helpful
if config.language_id == "scheme" {
log::info!("Implement better scheme indent mode!");
// TODO: walk backwards to find the previous s-expression?
// log::info!("{}", text);
// log::info!("{}", text.line(line_before));
let byte_pos = text.char_to_byte(line_before_end_pos);
let text_up_to_cursor = text.byte_slice(0..byte_pos);
let mut cursor = line_before;
let mut depth = 0;
loop {
let line = text_up_to_cursor.line(cursor);
// We want to ignore comments
if let Some(l) = line.as_str() {
if l.starts_with(";") {
if cursor == 0 {
break;
}
cursor -= 1;
continue;
}
}
// log::info!("Line: {}", line);
for (index, char) in line.chars_at(line.len_chars()).reversed().enumerate() {
match char {
')' | ']' | '}' => {
depth += 1;
}
'(' | '[' | '{' => {
// stack.push('(')
if depth == 0 {
log::info!(
"Found unmatched paren on line, index: {}, {}",
line,
index
);
// TODO: Here, then walk FORWARD, parsing the identifiers until there is a thing to line up with, for example:
// (define (foo-bar) RET) <-
// ^probably indent to here
let offset = line.len_chars() - index;
let mut char_iter_from_paren =
line.chars_at(line.len_chars() - index).enumerate();
let end;
// Walk until we've found whitespace, and then crunch the whitespace until the start of the next symbol
// if there is _no_ symbol after that, we should just default to the default behavior
while let Some((index, char)) = char_iter_from_paren.next() {
if char.is_whitespace() {
let mut last = index;
// This is the end of our range
end = index;
// If we have multiple parens in a row, match to the start:
// for instance, (cond [(equal? x 10) RET])
// ^ We want to line up to this
//
// To do so, just create an indent that is the width of the offset.
match line.get_char(offset) {
Some('(' | '[' | '{') => {
return Some(" ".repeat(offset));
}
_ => {}
}
// TODO: Don't unwrap here, we don't want that
// if LISP_WORDS.contains(
// line.slice(offset..offset + end).as_str().unwrap(),
// ) {
// return Some(" ".repeat(offset + 1));
// }
if line
.slice(offset..offset + end)
.as_str()
.map(|x| LISP_WORDS.contains(x))
.unwrap_or_default()
{
return Some(" ".repeat(offset + 1));
}
for _ in char_iter_from_paren
.take_while(|(_, x)| x.is_whitespace())
{
last += 1;
}
// If we have something like (list RET)
// We want the result to look like:
// (list
// )
//
// So we special case the lack of an additional word after
// the first symbol
if line.len_chars() == last + offset + 1 {
if let Some(c) = line.get_char(last + offset) {
if c.is_whitespace() {
return Some(" ".repeat(offset + 1));
}
}
}
return Some(" ".repeat(last + offset + 1));
}
}
log::info!("Found no symbol after the initial opening symbol");
return Some(" ".repeat(offset + 1));
}
depth -= 1;
}
_ => {}
}
}
if cursor == 0 {
break;
}
cursor -= 1;
}
// TODO: Implement heuristic for large files so we don't necessarily traverse the entire file backwards to check the matched parens?
return Some("".to_string());
}
}
None
}
// TODO: Do this to allow for custom indent operations. Unfortunately, we'll have to wrap
// all of the lifetimes up into references.
// impl<'a> steel::gc::unsafe_erased_pointers::CustomReference for RopeSlice<'a> {}
/// TODO: Come up with some elegant enough FFI for this, so that Steel can expose an API for this. /// TODO: Come up with some elegant enough FFI for this, so that Steel can expose an API for this.
/// Problem is - the issues with the `Any` type and using things with type id. /// Problem is - the issues with the `Any` type and using things with type id.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn custom_indent_for_newline( pub fn custom_indent_for_newline(
language_config: Option<&LanguageConfiguration>, language_config: Option<&LanguageConfiguration>,
_syntax: Option<&Syntax>,
_indent_style: &IndentStyle,
_tab_width: usize,
text: RopeSlice, text: RopeSlice,
line_before: usize, line_before: usize,
line_before_end_pos: usize, line_before_end_pos: usize,
_current_line: usize,
) -> Option<String> { ) -> Option<String> {
if let Some(config) = language_config { if let Some(config) = language_config {
// TODO: If possible, this would be very cool to be implemented in steel itself. If not, // TODO: If possible, this would be very cool to be implemented in steel itself. If not,
@ -943,11 +773,6 @@ pub fn custom_indent_for_newline(
if config.language_id == "scheme" { if config.language_id == "scheme" {
log::info!("Implement better scheme indent mode!"); log::info!("Implement better scheme indent mode!");
// TODO: walk backwards to find the previous s-expression?
// log::info!("{}", text);
// log::info!("{}", text.line(line_before));
let byte_pos = text.char_to_byte(line_before_end_pos); let byte_pos = text.char_to_byte(line_before_end_pos);
let text_up_to_cursor = text.byte_slice(0..byte_pos); let text_up_to_cursor = text.byte_slice(0..byte_pos);
@ -955,7 +780,6 @@ pub fn custom_indent_for_newline(
let mut cursor = line_before; let mut cursor = line_before;
let mut depth = 0; let mut depth = 0;
// for line in text_up_to_cursor.lines().reversed() {
loop { loop {
let line = text_up_to_cursor.line(cursor); let line = text_up_to_cursor.line(cursor);
@ -977,8 +801,6 @@ pub fn custom_indent_for_newline(
depth += 1; depth += 1;
} }
'(' | '[' | '{' => { '(' | '[' | '{' => {
// stack.push('(')
if depth == 0 { if depth == 0 {
log::info!( log::info!(
"Found unmatched paren on line, index: {}, {}", "Found unmatched paren on line, index: {}, {}",
@ -986,7 +808,7 @@ pub fn custom_indent_for_newline(
index index
); );
// TODO: Here, then walk FORWARD, parsing the identifiers until there is a thing to line up with, for example: // Here, then walk FORWARD, parsing the identifiers until there is a thing to line up with, for example:
// (define (foo-bar) RET) <- // (define (foo-bar) RET) <-
// ^probably indent to here // ^probably indent to here
@ -1018,13 +840,6 @@ pub fn custom_indent_for_newline(
_ => {} _ => {}
} }
// TODO: Don't unwrap here, we don't want that
// if LISP_WORDS.contains(
// line.slice(offset..offset + end).as_str().unwrap(),
// ) {
// return Some(" ".repeat(offset + 1));
// }
if line if line
.slice(offset..offset + end) .slice(offset..offset + end)
.as_str() .as_str()
@ -1121,16 +936,9 @@ pub fn indent_for_newline(
// TODO: @Matt - see if we can shell out to the steel plugin to identify indentation length // TODO: @Matt - see if we can shell out to the steel plugin to identify indentation length
// Something naive for steel could work, use the parser and // Something naive for steel could work, use the parser and
if let Some(indent_level) = custom_indent_for_newline( if let Some(indent_level) =
language_config, custom_indent_for_newline(language_config, text, line_before, line_before_end_pos)
syntax, {
indent_style,
tab_width,
text,
line_before,
line_before_end_pos,
current_line,
) {
return indent_level; return indent_level;
} }

@ -1,5 +1,10 @@
use fuzzy_matcher::FuzzyMatcher; use fuzzy_matcher::FuzzyMatcher;
use helix_core::{graphemes, shellwords::Shellwords, Selection, Tendril}; use helix_core::{
extensions::{rope_slice_module, SRopeSlice},
graphemes,
shellwords::Shellwords,
Selection, Tendril,
};
use helix_view::{ use helix_view::{
document::Mode, document::Mode,
editor::{Action, ConfigEvent}, editor::{Action, ConfigEvent},
@ -14,7 +19,10 @@ use steel::{
rvals::{ rvals::{
as_underlying_type, AsRefMutSteelValFromRef, AsRefSteelVal, FromSteelVal, IntoSteelVal, as_underlying_type, AsRefMutSteelValFromRef, AsRefSteelVal, FromSteelVal, IntoSteelVal,
}, },
steel_vm::{engine::Engine, register_fn::RegisterFn}, steel_vm::{
engine::Engine,
register_fn::{RegisterFn, RegisterFnBorrowed},
},
SteelErr, SteelVal, SteelErr, SteelVal,
}; };
@ -235,32 +243,41 @@ impl ScriptingEngine {
}; };
if let Some(extension) = extension { if let Some(extension) = extension {
if let SteelVal::HashMapV(map) = BUFFER_OR_EXTENSION_KEYBINDING_MAP.with(|x| x.clone()) if let SteelVal::Boxed(boxed_map) =
BUFFER_OR_EXTENSION_KEYBINDING_MAP.with(|x| x.clone())
{ {
if let Some(value) = map.get(&SteelVal::StringV(extension.into())) { if let SteelVal::HashMapV(map) = boxed_map.borrow().clone() {
if let SteelVal::Custom(inner) = value { if let Some(value) = map.get(&SteelVal::StringV(extension.into())) {
if let Some(_) = steel::rvals::as_underlying_type::<EmbeddedKeyMap>( if let SteelVal::Custom(inner) = value {
inner.borrow().as_ref(), if let Some(_) = steel::rvals::as_underlying_type::<EmbeddedKeyMap>(
) { inner.borrow().as_ref(),
return Some(value.clone()); ) {
return Some(value.clone());
}
} }
} }
} }
} }
} }
// TODO: Remove these clones if let SteelVal::Boxed(boxed_map) = REVERSE_BUFFER_MAP.with(|x| x.clone()) {
if let SteelVal::HashMapV(map) = REVERSE_BUFFER_MAP.with(|x| x.clone()) { if let SteelVal::HashMapV(map) = boxed_map.borrow().clone() {
if let Some(label) = map.get(&SteelVal::IntV(document_id_to_usize(doc_id) as isize)) { if let Some(label) = map.get(&SteelVal::IntV(document_id_to_usize(doc_id) as isize))
if let SteelVal::HashMapV(map) =
BUFFER_OR_EXTENSION_KEYBINDING_MAP.with(|x| x.clone())
{ {
if let Some(value) = map.get(label) { if let SteelVal::Boxed(boxed_map) =
if let SteelVal::Custom(inner) = value { BUFFER_OR_EXTENSION_KEYBINDING_MAP.with(|x| x.clone())
if let Some(_) = steel::rvals::as_underlying_type::<EmbeddedKeyMap>( {
inner.borrow().as_ref(), if let SteelVal::HashMapV(map) = boxed_map.borrow().clone() {
) { if let Some(value) = map.get(label) {
return Some(value.clone()); if let SteelVal::Custom(inner) = value {
if let Some(_) =
steel::rvals::as_underlying_type::<EmbeddedKeyMap>(
inner.borrow().as_ref(),
)
{
return Some(value.clone());
}
}
} }
} }
} }
@ -425,32 +442,32 @@ impl ScriptingEngine {
// External modules that can load via rust dylib. These can then be consumed from // External modules that can load via rust dylib. These can then be consumed from
// steel as needed, via the standard FFI for plugin functions. // steel as needed, via the standard FFI for plugin functions.
pub(crate) static EXTERNAL_DYLIBS: Lazy<Arc<RwLock<ExternalContainersAndModules>>> = // pub(crate) static EXTERNAL_DYLIBS: Lazy<Arc<RwLock<ExternalContainersAndModules>>> =
Lazy::new(|| { // Lazy::new(|| {
let mut containers = DylibContainers::new(); // let mut containers = DylibContainers::new();
// Load the plugins with respect to the extensions directory. // // Load the plugins with respect to the extensions directory.
// containers.load_modules_from_directory(Some( // // containers.load_modules_from_directory(Some(
// helix_loader::config_dir() // // helix_loader::config_dir()
// .join("extensions") // // .join("extensions")
// .to_str() // // .to_str()
// .unwrap() // // .unwrap()
// .to_string(), // // .to_string(),
// )); // // ));
println!("Found dylibs: {}", containers.containers.len()); // println!("Found dylibs: {}", containers.containers.len());
let modules = containers.create_commands(); // let modules = containers.create_commands();
println!("Modules length: {}", modules.len()); // println!("Modules length: {}", modules.len());
Arc::new(RwLock::new(ExternalContainersAndModules { // Arc::new(RwLock::new(ExternalContainersAndModules {
containers, // containers,
modules, // modules,
})) // }))
// Arc::new(RwLock::new(containers)) // // Arc::new(RwLock::new(containers))
}); // });
pub fn initialize_engine() { pub fn initialize_engine() {
ENGINE.with(|x| x.borrow().globals().first().copied()); ENGINE.with(|x| x.borrow().globals().first().copied());
@ -838,6 +855,25 @@ fn configure_engine() -> std::rc::Rc<std::cell::RefCell<steel::steel_vm::engine:
load_keymap_api(&mut engine, KeyMapApi::new()); load_keymap_api(&mut engine, KeyMapApi::new());
let mut rope_slice_module = rope_slice_module();
// Load the ropes + slice module
// engine.register_module(rope_slice_module());
// rope_slice_module.register_fn("document->slice", document_to_text);
RegisterFnBorrowed::<
_,
steel::steel_vm::register_fn::MarkerWrapper9<(
Document,
Document,
SRopeSlice<'_>,
SRopeSlice<'static>,
)>,
SRopeSlice,
>::register_fn_borrowed(&mut rope_slice_module, "document->slice", document_to_text);
engine.register_module(rope_slice_module);
// engine.register_fn("helix-current-keymap", get_keymap); // engine.register_fn("helix-current-keymap", get_keymap);
// engine.register_fn("helix-empty-keymap", empty_keymap); // engine.register_fn("helix-empty-keymap", empty_keymap);
// engine.register_fn("helix-default-keymap", default_keymap); // engine.register_fn("helix-default-keymap", default_keymap);
@ -1454,6 +1490,10 @@ fn get_document(editor: &mut Editor, doc_id: DocumentId) -> &Document {
editor.documents.get(&doc_id).unwrap() editor.documents.get(&doc_id).unwrap()
} }
fn document_to_text(doc: &Document) -> SRopeSlice<'_> {
SRopeSlice::new(doc.text().slice(..))
}
fn is_document_in_view(editor: &mut Editor, doc_id: DocumentId) -> Option<helix_view::ViewId> { fn is_document_in_view(editor: &mut Editor, doc_id: DocumentId) -> Option<helix_view::ViewId> {
editor editor
.tree .tree

@ -1,3 +1,3 @@
[toolchain] [toolchain]
channel = "1.65.0" channel = "1.70.0"
components = ["rustfmt", "rust-src"] components = ["rustfmt", "rust-src"]

Loading…
Cancel
Save