1use core::fmt;
2
3use crate::{
4 Block, BlockRef, EntityListCursor, EntityListCursorMut, EntityMut, EntityRef, Operation,
5 OperationRef, Spanned,
6 entity::{EntityProjection, EntityProjectionMut},
7};
8
9#[derive(Default, Copy, Clone)]
19pub enum ProgramPoint {
20 #[default]
22 Invalid,
23 Block {
25 block: BlockRef,
27 position: Position,
29 },
30 Op {
32 op: OperationRef,
34 position: Position,
36 },
37}
38
39#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
41pub enum Position {
42 Before,
44 After,
46}
47
48impl<T> From<EntityRef<'_, T>> for ProgramPoint
49where
50 for<'a> ProgramPoint: From<&'a T>,
51{
52 #[inline]
53 fn from(entity: EntityRef<'_, T>) -> Self {
54 Self::from(&*entity)
55 }
56}
57
58impl<T> From<EntityMut<'_, T>> for ProgramPoint
59where
60 for<'a> ProgramPoint: From<&'a T>,
61{
62 #[inline]
63 fn from(entity: EntityMut<'_, T>) -> Self {
64 Self::from(&*entity)
65 }
66}
67
68impl From<&Operation> for ProgramPoint {
70 #[inline]
71 fn from(op: &Operation) -> Self {
72 Self::from(op.as_operation_ref())
73 }
74}
75
76impl From<OperationRef> for ProgramPoint {
78 #[inline]
79 fn from(op: OperationRef) -> Self {
80 Self::Op {
81 op,
82 position: Position::Before,
83 }
84 }
85}
86
87impl From<&Block> for ProgramPoint {
89 #[inline]
90 fn from(block: &Block) -> Self {
91 Self::at_start_of(block.as_block_ref())
92 }
93}
94
95impl From<BlockRef> for ProgramPoint {
97 #[inline]
98 fn from(block: BlockRef) -> Self {
99 Self::Block {
100 block,
101 position: Position::Before,
102 }
103 }
104}
105
106#[doc(hidden)]
107#[derive(Copy, Clone)]
108pub struct BlockPoint {
109 block: BlockRef,
110 point: Position,
111}
112impl From<BlockPoint> for ProgramPoint {
113 fn from(point: BlockPoint) -> Self {
114 ProgramPoint::Block {
115 block: point.block,
116 position: point.point,
117 }
118 }
119}
120impl From<BlockRef> for BlockPoint {
121 fn from(block: BlockRef) -> Self {
122 Self {
123 block,
124 point: Position::Before,
125 }
126 }
127}
128impl From<&Block> for BlockPoint {
129 fn from(block: &Block) -> Self {
130 Self {
131 block: block.as_block_ref(),
132 point: Position::Before,
133 }
134 }
135}
136
137impl ProgramPoint {
138 #[inline]
140 pub fn before(entity: impl Into<ProgramPoint>) -> Self {
141 entity.into()
142 }
143
144 pub fn after(entity: impl Into<ProgramPoint>) -> Self {
146 let mut pp = entity.into();
147 match &mut pp {
148 Self::Invalid => (),
149 Self::Op {
150 position: point, ..
151 }
152 | Self::Block {
153 position: point, ..
154 } => {
155 *point = Position::After;
156 }
157 }
158 pp
159 }
160
161 pub fn at_start_of(block: impl Into<BlockPoint>) -> Self {
163 let BlockPoint { block, .. } = block.into();
164 Self::Block {
165 block,
166 position: Position::Before,
167 }
168 }
169
170 pub fn at_end_of(block: impl Into<BlockPoint>) -> Self {
172 let BlockPoint { block, .. } = block.into();
173 Self::Block {
174 block,
175 position: Position::After,
176 }
177 }
178
179 pub fn is_at_block_start(&self) -> bool {
181 self.operation().is_some_and(|op| {
182 op.parent().is_some() && op.prev().is_none() && self.placement() == Position::Before
183 }) || matches!(self, Self::Block { position: Position::Before, block, .. } if block.borrow().body().is_empty())
184 }
185
186 pub fn is_at_block_end(&self) -> bool {
188 self.operation().is_some_and(|op| {
189 op.parent().is_some() && op.next().is_none() && self.placement() == Position::After
190 }) || matches!(self, Self::Block { position: Position::After, block, .. } if block.borrow().body().is_empty())
191 }
192
193 pub fn block(&self) -> Option<BlockRef> {
197 match self {
198 Self::Invalid => None,
199 Self::Block { block, .. } => Some(*block),
200 Self::Op { op, .. } => op.parent(),
201 }
202 }
203
204 pub fn operation(&self) -> Option<OperationRef> {
208 match self {
209 Self::Invalid => None,
210 Self::Block {
211 position: Position::Before,
212 block,
213 ..
214 } => block.borrow().body().front().as_pointer(),
215 Self::Block {
216 position: Position::After,
217 block,
218 ..
219 } => block.borrow().body().back().as_pointer(),
220 Self::Op { op, .. } => Some(*op),
221 }
222 }
223
224 #[track_caller]
230 pub fn next_operation(&self) -> Option<OperationRef> {
231 assert!(!self.is_at_block_end());
232 match self {
233 Self::Op {
234 position: Position::After,
235 op,
236 ..
237 } if op.parent().is_some() => op.next(),
238 Self::Op { op, .. } => Some(*op),
239 Self::Block {
240 position: Position::Before,
241 block,
242 } => block.borrow().front(),
243 Self::Block { .. } | Self::Invalid => None,
244 }
245 }
246
247 #[track_caller]
253 pub fn prev_operation(&self) -> Option<OperationRef> {
254 assert!(!self.is_at_block_start());
255 match self {
256 Self::Op {
257 position: Position::Before,
258 op,
259 ..
260 } if op.parent().is_some() => op.prev(),
261 Self::Op { op, .. } => Some(*op),
262 Self::Block {
263 position: Position::After,
264 block,
265 } => block.borrow().back(),
266 Self::Block { .. } | Self::Invalid => None,
267 }
268 }
269
270 #[inline]
272 pub fn is_valid(&self) -> bool {
273 !self.is_unset()
274 }
275
276 #[inline]
278 pub fn is_unset(&self) -> bool {
279 matches!(self, Self::Invalid)
280 }
281
282 pub fn placement(&self) -> Position {
284 match self {
285 Self::Invalid => Position::After,
286 Self::Block {
287 position: point, ..
288 }
289 | Self::Op {
290 position: point, ..
291 } => *point,
292 }
293 }
294
295 pub fn cursor<'a, 'b: 'a, 'c: 'b>(
317 &'c self,
318 ) -> Option<EntityProjection<'b, EntityListCursor<'a, Operation>>> {
319 match self {
320 Self::Invalid => None,
321 Self::Block {
322 block,
323 position: point,
324 } => Some(EntityRef::project(block.borrow(), |block| match point {
325 Position::Before => block.body().front(),
326 Position::After => block.body().back(),
327 })),
328 Self::Op {
329 op,
330 position: point,
331 } => {
332 let block = op.parent()?;
333 Some(EntityRef::project(block.borrow(), |block| match point {
334 Position::Before => {
335 if let Some(placement) = op.prev() {
336 unsafe { block.body().cursor_from_ptr(placement) }
337 } else {
338 block.body().cursor()
339 }
340 }
341 Position::After => unsafe { block.body().cursor_from_ptr(*op) },
342 }))
343 }
344 }
345 }
346
347 pub fn cursor_mut<'a, 'b: 'a, 'c: 'b>(
352 &'c mut self,
353 ) -> Option<EntityProjectionMut<'b, EntityListCursorMut<'a, Operation>>> {
354 match self {
355 Self::Invalid => None,
356 Self::Block {
357 block,
358 position: point,
359 } => Some(EntityMut::project(block.borrow_mut(), |block| match point {
360 Position::Before => block.body_mut().cursor_mut(),
361 Position::After => block.body_mut().back_mut(),
362 })),
363 Self::Op {
364 op,
365 position: point,
366 } => {
367 let mut block = op.parent()?;
368 Some(EntityMut::project(block.borrow_mut(), |block| match point {
369 Position::Before => {
370 if let Some(placement) = op.prev() {
371 unsafe { block.body_mut().cursor_mut_from_ptr(placement) }
372 } else {
373 block.body_mut().cursor_mut()
374 }
375 }
376 Position::After => unsafe { block.body_mut().cursor_mut_from_ptr(*op) },
377 }))
378 }
379 }
380 }
381}
382
383impl Eq for ProgramPoint {}
384
385impl PartialEq for ProgramPoint {
386 fn eq(&self, other: &Self) -> bool {
387 match (self, other) {
388 (Self::Invalid, Self::Invalid) => true,
389 (Self::Invalid, _) | (_, Self::Invalid) => false,
390 (
391 Self::Block {
392 block: x,
393 position: xp,
394 },
395 Self::Block {
396 block: y,
397 position: yp,
398 },
399 ) => x == y && xp == yp,
400 (
401 Self::Op {
402 op: x,
403 position: xp,
404 ..
405 },
406 Self::Op {
407 op: y,
408 position: yp,
409 ..
410 },
411 ) => x == y && xp == yp,
412 (..) => false,
413 }
414 }
415}
416
417impl core::hash::Hash for ProgramPoint {
418 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
419 core::mem::discriminant(self).hash(state);
420 match self {
421 Self::Invalid => (),
422 Self::Block {
423 block,
424 position: point,
425 } => {
426 core::ptr::hash(BlockRef::as_ptr(block), state);
427 point.hash(state);
428 }
429 Self::Op {
430 op,
431 position: point,
432 ..
433 } => {
434 core::ptr::hash(OperationRef::as_ptr(op), state);
435 point.hash(state);
436 }
437 }
438 }
439}
440
441impl Spanned for ProgramPoint {
442 fn span(&self) -> crate::SourceSpan {
443 use crate::SourceSpan;
444
445 match self {
446 Self::Invalid => SourceSpan::UNKNOWN,
447 Self::Block {
448 block,
449 position: point,
450 } => match point {
451 Position::Before => {
452 block.borrow().body().front().get().map(|op| op.span()).unwrap_or_default()
453 }
454 Position::After => {
455 block.borrow().body().back().get().map(|op| op.span()).unwrap_or_default()
456 }
457 },
458 Self::Op { op, .. } => op.borrow().span(),
459 }
460 }
461}
462
463impl fmt::Display for ProgramPoint {
464 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
465 use crate::EntityWithId;
466 match self {
467 Self::Invalid => f.write_str("<invalid>"),
468 Self::Block {
469 block,
470 position: point,
471 } => match point {
472 Position::Before => write!(f, "start({})", &block.borrow().id()),
473 Position::After => write!(f, "end({})", &block.borrow().id()),
474 },
475 Self::Op {
476 op,
477 position: point,
478 } => {
479 use crate::formatter::{const_text, display};
480 let block = op
481 .parent()
482 .map(|blk| display(blk.borrow().id()))
483 .unwrap_or_else(|| const_text("null"));
484 match point {
485 Position::Before => {
486 write!(f, "before({} in {block})", &op.borrow().name())
487 }
488 Position::After => {
489 write!(f, "after({} in {block})", &op.borrow().name())
490 }
491 }
492 }
493 }
494 }
495}
496
497impl fmt::Debug for ProgramPoint {
498 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
499 use crate::EntityWithId;
500 match self {
501 Self::Invalid => f.write_str("Invalid"),
502 Self::Block {
503 block,
504 position: point,
505 } => f
506 .debug_struct("Block")
507 .field("block", &block.borrow().id())
508 .field("point", point)
509 .finish(),
510 Self::Op {
511 op,
512 position: point,
513 } => f
514 .debug_struct("Op")
515 .field("block", &op.parent().map(|blk| blk.borrow().id()))
516 .field("point", point)
517 .field("op", &op.borrow())
518 .finish(),
519 }
520 }
521}