feat: add API for editing points and ranges
This commit is contained in:
parent
1a0868c487
commit
a69367f739
9 changed files with 209 additions and 39 deletions
|
|
@ -1,4 +1,4 @@
|
|||
use tree_sitter::{Node, Parser, Point, Tree};
|
||||
use tree_sitter::{InputEdit, Node, Parser, Point, Tree};
|
||||
use tree_sitter_generate::load_grammar_file;
|
||||
|
||||
use super::{
|
||||
|
|
@ -843,6 +843,92 @@ fn test_node_is_error() {
|
|||
assert!(child.is_error());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_edit_point() {
|
||||
let edit = InputEdit {
|
||||
start_byte: 5,
|
||||
old_end_byte: 5,
|
||||
new_end_byte: 10,
|
||||
start_position: Point::new(0, 5),
|
||||
old_end_position: Point::new(0, 5),
|
||||
new_end_position: Point::new(0, 10),
|
||||
};
|
||||
|
||||
// Point after edit
|
||||
let mut point = Point::new(0, 8);
|
||||
let mut byte = 8;
|
||||
edit.edit_point(&mut point, &mut byte);
|
||||
assert_eq!(point, Point::new(0, 13));
|
||||
assert_eq!(byte, 13);
|
||||
|
||||
// Point before edit
|
||||
let mut point = Point::new(0, 2);
|
||||
let mut byte = 2;
|
||||
edit.edit_point(&mut point, &mut byte);
|
||||
assert_eq!(point, Point::new(0, 2));
|
||||
assert_eq!(byte, 2);
|
||||
|
||||
// Point at edit start
|
||||
let mut point = Point::new(0, 5);
|
||||
let mut byte = 5;
|
||||
edit.edit_point(&mut point, &mut byte);
|
||||
assert_eq!(point, Point::new(0, 10));
|
||||
assert_eq!(byte, 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_edit_range() {
|
||||
use tree_sitter::{InputEdit, Point, Range};
|
||||
|
||||
let edit = InputEdit {
|
||||
start_byte: 10,
|
||||
old_end_byte: 15,
|
||||
new_end_byte: 20,
|
||||
start_position: Point::new(1, 0),
|
||||
old_end_position: Point::new(1, 5),
|
||||
new_end_position: Point::new(2, 0),
|
||||
};
|
||||
|
||||
// Range after edit
|
||||
let mut range = Range {
|
||||
start_byte: 20,
|
||||
end_byte: 25,
|
||||
start_point: Point::new(2, 0),
|
||||
end_point: Point::new(2, 5),
|
||||
};
|
||||
edit.edit_range(&mut range);
|
||||
assert_eq!(range.start_byte, 25);
|
||||
assert_eq!(range.end_byte, 30);
|
||||
assert_eq!(range.start_point, Point::new(3, 0));
|
||||
assert_eq!(range.end_point, Point::new(3, 5));
|
||||
|
||||
// Range before edit
|
||||
let mut range = Range {
|
||||
start_byte: 5,
|
||||
end_byte: 8,
|
||||
start_point: Point::new(0, 5),
|
||||
end_point: Point::new(0, 8),
|
||||
};
|
||||
edit.edit_range(&mut range);
|
||||
assert_eq!(range.start_byte, 5);
|
||||
assert_eq!(range.end_byte, 8);
|
||||
assert_eq!(range.start_point, Point::new(0, 5));
|
||||
assert_eq!(range.end_point, Point::new(0, 8));
|
||||
|
||||
// Range overlapping edit
|
||||
let mut range = Range {
|
||||
start_byte: 8,
|
||||
end_byte: 12,
|
||||
start_point: Point::new(0, 8),
|
||||
end_point: Point::new(1, 2),
|
||||
};
|
||||
edit.edit_range(&mut range);
|
||||
assert_eq!(range.start_byte, 8);
|
||||
assert_eq!(range.end_byte, 10);
|
||||
assert_eq!(range.start_point, Point::new(0, 8));
|
||||
assert_eq!(range.end_point, Point::new(1, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_node_sexp() {
|
||||
let mut parser = Parser::new();
|
||||
|
|
|
|||
|
|
@ -495,6 +495,14 @@ extern "C" {
|
|||
#[doc = " Check if two nodes are identical."]
|
||||
pub fn ts_node_eq(self_: TSNode, other: TSNode) -> bool;
|
||||
}
|
||||
extern "C" {
|
||||
#[doc = " Edit a point to keep it in-sync with source code that has been edited.\n\n This function updates a single point's byte offset and row/column position\n based on an edit operation. This is useful for editing points without\n requiring a tree or node instance."]
|
||||
pub fn ts_point_edit(point: *mut TSPoint, point_byte: *mut u32, edit: *const TSInputEdit);
|
||||
}
|
||||
extern "C" {
|
||||
#[doc = " Edit a range to keep it in-sync with source code that has been edited.\n\n This function updates a range's start and end positions based on an edit\n operation. This is useful for editing ranges without requiring a tree\n or node instance."]
|
||||
pub fn ts_range_edit(range: *mut TSRange, edit: *const TSInputEdit);
|
||||
}
|
||||
extern "C" {
|
||||
#[doc = " Create a new tree cursor starting from the given node.\n\n A tree cursor allows you to walk a syntax tree more efficiently than is\n possible using the [`TSNode`] functions. It is a mutable object that is always\n on a certain syntax node, and can be moved imperatively to different nodes.\n\n Note that the given node is considered the root of the cursor,\n and the cursor cannot walk outside this node."]
|
||||
pub fn ts_tree_cursor_new(node: TSNode) -> TSTreeCursor;
|
||||
|
|
|
|||
|
|
@ -120,6 +120,48 @@ pub struct InputEdit {
|
|||
pub new_end_position: Point,
|
||||
}
|
||||
|
||||
impl InputEdit {
|
||||
/// Edit a point to keep it in-sync with source code that has been edited.
|
||||
///
|
||||
/// This function updates a single point's byte offset and row/column position
|
||||
/// based on this edit operation. This is useful for editing points without
|
||||
/// requiring a tree or node instance.
|
||||
#[doc(alias = "ts_point_edit")]
|
||||
pub fn edit_point(&self, point: &mut Point, byte: &mut usize) {
|
||||
let edit = self.into();
|
||||
let mut ts_point = (*point).into();
|
||||
let mut ts_byte = *byte as u32;
|
||||
|
||||
unsafe {
|
||||
ffi::ts_point_edit(
|
||||
core::ptr::addr_of_mut!(ts_point),
|
||||
core::ptr::addr_of_mut!(ts_byte),
|
||||
&edit,
|
||||
);
|
||||
}
|
||||
|
||||
*point = ts_point.into();
|
||||
*byte = ts_byte as usize;
|
||||
}
|
||||
|
||||
/// Edit a range to keep it in-sync with source code that has been edited.
|
||||
///
|
||||
/// This function updates a range's start and end positions based on this edit
|
||||
/// operation. This is useful for editing ranges without requiring a tree
|
||||
/// or node instance.
|
||||
#[doc(alias = "ts_range_edit")]
|
||||
pub fn edit_range(&self, range: &mut Range) {
|
||||
let edit = self.into();
|
||||
let mut ts_range = (*range).into();
|
||||
|
||||
unsafe {
|
||||
ffi::ts_range_edit(core::ptr::addr_of_mut!(ts_range), &edit);
|
||||
}
|
||||
|
||||
*range = ts_range.into();
|
||||
}
|
||||
}
|
||||
|
||||
/// A single node within a syntax [`Tree`].
|
||||
#[doc(alias = "TSNode")]
|
||||
#[derive(Clone, Copy)]
|
||||
|
|
|
|||
|
|
@ -708,6 +708,24 @@ void ts_node_edit(TSNode *self, const TSInputEdit *edit);
|
|||
*/
|
||||
bool ts_node_eq(TSNode self, TSNode other);
|
||||
|
||||
/**
|
||||
* Edit a point to keep it in-sync with source code that has been edited.
|
||||
*
|
||||
* This function updates a single point's byte offset and row/column position
|
||||
* based on an edit operation. This is useful for editing points without
|
||||
* requiring a tree or node instance.
|
||||
*/
|
||||
void ts_point_edit(TSPoint *point, uint32_t *point_byte, const TSInputEdit *edit);
|
||||
|
||||
/**
|
||||
* Edit a range to keep it in-sync with source code that has been edited.
|
||||
*
|
||||
* This function updates a range's start and end positions based on an edit
|
||||
* operation. This is useful for editing ranges without requiring a tree
|
||||
* or node instance.
|
||||
*/
|
||||
void ts_range_edit(TSRange *range, const TSInputEdit *edit);
|
||||
|
||||
/************************/
|
||||
/* Section - TreeCursor */
|
||||
/************************/
|
||||
|
|
|
|||
|
|
@ -103,6 +103,40 @@ void ts_range_array_get_changed_ranges(
|
|||
}
|
||||
}
|
||||
|
||||
void ts_range_edit(TSRange *range, const TSInputEdit *edit) {
|
||||
if (range->end_byte >= edit->old_end_byte) {
|
||||
if (range->end_byte != UINT32_MAX) {
|
||||
range->end_byte = edit->new_end_byte + (range->end_byte - edit->old_end_byte);
|
||||
range->end_point = point_add(
|
||||
edit->new_end_point,
|
||||
point_sub(range->end_point, edit->old_end_point)
|
||||
);
|
||||
if (range->end_byte < edit->new_end_byte) {
|
||||
range->end_byte = UINT32_MAX;
|
||||
range->end_point = POINT_MAX;
|
||||
}
|
||||
}
|
||||
} else if (range->end_byte > edit->start_byte) {
|
||||
range->end_byte = edit->start_byte;
|
||||
range->end_point = edit->start_point;
|
||||
}
|
||||
|
||||
if (range->start_byte >= edit->old_end_byte) {
|
||||
range->start_byte = edit->new_end_byte + (range->start_byte - edit->old_end_byte);
|
||||
range->start_point = point_add(
|
||||
edit->new_end_point,
|
||||
point_sub(range->start_point, edit->old_end_point)
|
||||
);
|
||||
if (range->start_byte < edit->new_end_byte) {
|
||||
range->start_byte = UINT32_MAX;
|
||||
range->start_point = POINT_MAX;
|
||||
}
|
||||
} else if (range->start_byte > edit->start_byte) {
|
||||
range->start_byte = edit->start_byte;
|
||||
range->start_point = edit->start_point;
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
TreeCursor cursor;
|
||||
const TSLanguage *language;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "./lexer.c"
|
||||
#include "./node.c"
|
||||
#include "./parser.c"
|
||||
#include "./point.c"
|
||||
#include "./query.c"
|
||||
#include "./stack.c"
|
||||
#include "./subtree.c"
|
||||
|
|
|
|||
|
|
@ -861,13 +861,7 @@ void ts_node_edit(TSNode *self, const TSInputEdit *edit) {
|
|||
uint32_t start_byte = ts_node_start_byte(*self);
|
||||
TSPoint start_point = ts_node_start_point(*self);
|
||||
|
||||
if (start_byte >= edit->old_end_byte) {
|
||||
start_byte = edit->new_end_byte + (start_byte - edit->old_end_byte);
|
||||
start_point = point_add(edit->new_end_point, point_sub(start_point, edit->old_end_point));
|
||||
} else if (start_byte > edit->start_byte) {
|
||||
start_byte = edit->new_end_byte;
|
||||
start_point = edit->new_end_point;
|
||||
}
|
||||
ts_point_edit(&start_point, &start_byte, edit);
|
||||
|
||||
self->context[0] = start_byte;
|
||||
self->context[1] = start_point.row;
|
||||
|
|
|
|||
17
lib/src/point.c
Normal file
17
lib/src/point.c
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#include "point.h"
|
||||
|
||||
void ts_point_edit(TSPoint *point, uint32_t *byte, const TSInputEdit *edit) {
|
||||
uint32_t start_byte = *byte;
|
||||
TSPoint start_point = *point;
|
||||
|
||||
if (start_byte >= edit->old_end_byte) {
|
||||
start_byte = edit->new_end_byte + (start_byte - edit->old_end_byte);
|
||||
start_point = point_add(edit->new_end_point, point_sub(start_point, edit->old_end_point));
|
||||
} else if (start_byte > edit->start_byte) {
|
||||
start_byte = edit->new_end_byte;
|
||||
start_point = edit->new_end_point;
|
||||
}
|
||||
|
||||
*point = start_point;
|
||||
*byte = start_byte;
|
||||
}
|
||||
|
|
@ -54,37 +54,7 @@ const TSLanguage *ts_tree_language(const TSTree *self) {
|
|||
|
||||
void ts_tree_edit(TSTree *self, const TSInputEdit *edit) {
|
||||
for (unsigned i = 0; i < self->included_range_count; i++) {
|
||||
TSRange *range = &self->included_ranges[i];
|
||||
if (range->end_byte >= edit->old_end_byte) {
|
||||
if (range->end_byte != UINT32_MAX) {
|
||||
range->end_byte = edit->new_end_byte + (range->end_byte - edit->old_end_byte);
|
||||
range->end_point = point_add(
|
||||
edit->new_end_point,
|
||||
point_sub(range->end_point, edit->old_end_point)
|
||||
);
|
||||
if (range->end_byte < edit->new_end_byte) {
|
||||
range->end_byte = UINT32_MAX;
|
||||
range->end_point = POINT_MAX;
|
||||
}
|
||||
}
|
||||
} else if (range->end_byte > edit->start_byte) {
|
||||
range->end_byte = edit->start_byte;
|
||||
range->end_point = edit->start_point;
|
||||
}
|
||||
if (range->start_byte >= edit->old_end_byte) {
|
||||
range->start_byte = edit->new_end_byte + (range->start_byte - edit->old_end_byte);
|
||||
range->start_point = point_add(
|
||||
edit->new_end_point,
|
||||
point_sub(range->start_point, edit->old_end_point)
|
||||
);
|
||||
if (range->start_byte < edit->new_end_byte) {
|
||||
range->start_byte = UINT32_MAX;
|
||||
range->start_point = POINT_MAX;
|
||||
}
|
||||
} else if (range->start_byte > edit->start_byte) {
|
||||
range->start_byte = edit->start_byte;
|
||||
range->start_point = edit->start_point;
|
||||
}
|
||||
ts_range_edit(&self->included_ranges[i], edit);
|
||||
}
|
||||
|
||||
SubtreePool pool = ts_subtree_pool_new(0);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue