Markdown parser fork with extended syntax for personal use.
at hack 829 lines 26 kB view raw
1//! A tokenizer glues states from the state machine together. 2//! 3//! It facilitates everything needed to turn bytes into events with a state 4//! machine. 5//! It also enables the logic needed for parsing markdown, such as an 6//! [`attempt`][] to try and parse something, which can succeed or, when 7//! unsuccessful, revert the attempt. 8//! 9//! [`attempt`]: Tokenizer::attempt 10 11use crate::event::{Content, Event, Kind, Link, Name, Point, VOID_EVENTS}; 12use crate::message; 13use crate::parser::ParseState; 14use crate::resolve::{call as call_resolve, Name as ResolveName}; 15use crate::state::{call, State}; 16use crate::subtokenize::Subresult; 17 18#[cfg(feature = "log")] 19use crate::util::char::format_byte_opt; 20 21use crate::util::{constant::TAB_SIZE, edit_map::EditMap}; 22use alloc::{boxed::Box, string::String, vec, vec::Vec}; 23 24/// Containers. 25/// 26/// Containers are found when tokenizing 27/// [document content][crate::construct::document]. 28/// They parse a portion at the start of one or more lines. 29/// The rest of those lines is a different content type (specifically, flow), 30/// which they “contain”. 31#[derive(Debug, Eq, PartialEq)] 32pub enum Container { 33 /// [Block quote][crate::construct::block_quote]. 34 BlockQuote, 35 /// [List item][crate::construct::list_item]. 36 ListItem, 37 /// [GFM: Footnote definition][crate::construct::gfm_footnote_definition]. 38 GfmFootnoteDefinition, 39} 40 41/// Info used to tokenize a container. 42/// 43/// Practically, these fields are only used for list items. 44#[derive(Debug)] 45pub struct ContainerState { 46 /// Kind. 47 pub kind: Container, 48 /// Whether the first line was blank. 49 pub blank_initial: bool, 50 /// Size. 51 pub size: usize, 52} 53 54/// How to handle a byte. 55#[derive(Debug, PartialEq)] 56enum ByteAction { 57 /// This is a normal byte. 58 /// 59 /// Includes replaced bytes. 60 Normal(u8), 61 /// This byte must be ignored. 62 Ignore, 63 /// This is a new byte. 64 Insert(u8), 65} 66 67/// Label start kind. 68#[derive(Debug, PartialEq, Eq)] 69pub enum LabelKind { 70 /// Label (image) start. 71 /// 72 /// ```markdown 73 /// > | a ![b] c 74 /// ^^ 75 /// ``` 76 /// 77 /// Construct: [Label start (image)][crate::construct::label_start_image]. 78 Image, 79 /// Label (image) link. 80 /// 81 /// ```markdown 82 /// > | a [b] c 83 /// ^ 84 /// ``` 85 /// 86 /// Construct: [Label start (link)][crate::construct::label_start_link]. 87 Link, 88 /// GFM: Label (footnote) link. 89 /// 90 /// ```markdown 91 /// > | a [^b] c 92 /// ^^ 93 /// ``` 94 /// 95 /// Construct: [GFM: Label start (footnote)][crate::construct::gfm_label_start_footnote]. 96 GfmFootnote, 97 /// GFM: Label (footnote) link, not matching a footnote definition, so 98 /// handled as a label (link) start. 99 /// 100 /// ```markdown 101 /// > | a [^b](c) d 102 /// ^^ 103 /// ``` 104 /// 105 /// Construct: [Label end][crate::construct::label_end]. 106 GfmUndefinedFootnote, 107} 108 109/// Label start, looking for an end. 110#[derive(Debug)] 111pub struct LabelStart { 112 /// Kind of start. 113 pub kind: LabelKind, 114 /// Indices of where the label starts and ends in `events`. 115 pub start: (usize, usize), 116 /// A boolean used internally to figure out if a (link) label start can’t 117 /// be used anymore (because it would contain another link). 118 /// That link start is still looking for a balanced closing bracket though, 119 /// so we can’t remove it just yet. 120 pub inactive: bool, 121} 122 123/// Valid label. 124#[derive(Debug)] 125pub struct Label { 126 pub kind: LabelKind, 127 /// Indices of label start. 128 pub start: (usize, usize), 129 /// Indices of label end. 130 pub end: (usize, usize), 131} 132 133/// Different kinds of attempts. 134#[derive(Debug, PartialEq)] 135enum AttemptKind { 136 /// Discard what was tokenized when unsuccessful. 137 Attempt, 138 /// Discard always. 139 Check, 140} 141 142/// How to handle [`State::Ok`][] or [`State::Nok`][]. 143#[derive(Debug)] 144struct Attempt { 145 /// Where to go to when successful. 146 ok: State, 147 /// Where to go to when unsuccessful. 148 nok: State, 149 /// Kind of attempt. 150 kind: AttemptKind, 151 /// If needed, the progress to revert to. 152 /// 153 /// It is not needed to discard an [`AttemptKind::Attempt`] that has a 154 /// `nok` of [`State::Nok`][], because that means it is used in *another* 155 /// attempt, which will receive that `Nok`, and has to handle it. 156 progress: Option<Progress>, 157} 158 159/// The internal state of a tokenizer. 160/// 161/// Not to be confused with states from the state machine, this instead is all 162/// the information on where we currently are and what’s going on. 163#[derive(Clone, Debug)] 164struct Progress { 165 /// Length of `events`. 166 /// 167 /// It’s not allowed to remove events, so reverting will just pop stuff off. 168 events_len: usize, 169 /// Length of the stack. 170 /// 171 /// It’s not allowed to decrease the stack in an attempt. 172 stack_len: usize, 173 /// Previous code. 174 previous: Option<u8>, 175 /// Current code. 176 current: Option<u8>, 177 /// Current place in the file. 178 point: Point, 179} 180 181/// A lot of shared fields used to tokenize things. 182#[allow(clippy::struct_excessive_bools)] 183#[derive(Debug)] 184pub struct TokenizeState<'a> { 185 // Couple complex fields used to tokenize the document. 186 /// Tokenizer, used to tokenize flow in document. 187 pub document_child: Option<Box<Tokenizer<'a>>>, 188 /// State, used to tokenize containers. 189 pub document_child_state: Option<State>, 190 /// Stack of currently active containers. 191 pub document_container_stack: Vec<ContainerState>, 192 /// How many active containers continued. 193 pub document_continued: usize, 194 /// Index of last `data`. 195 pub document_data_index: Option<usize>, 196 /// Container exits by line number. 197 pub document_exits: Vec<Option<Vec<Event>>>, 198 /// Whether the previous flow was a paragraph or a definition. 199 pub document_lazy_accepting_before: bool, 200 /// Whether this is the first paragraph (potentially after definitions) in 201 /// a list item. 202 /// Used for GFM task list items. 203 pub document_at_first_paragraph_of_list_item: bool, 204 205 // Couple of very frequent settings for parsing whitespace. 206 pub space_or_tab_eol_content: Option<Content>, 207 pub space_or_tab_eol_connect: bool, 208 pub space_or_tab_eol_ok: bool, 209 pub space_or_tab_connect: bool, 210 pub space_or_tab_content: Option<Content>, 211 pub space_or_tab_min: usize, 212 pub space_or_tab_max: usize, 213 pub space_or_tab_size: usize, 214 pub space_or_tab_token: Name, 215 216 pub wikilinks: Vec<Label>, 217 pub wikilink_starts: Vec<LabelStart>, 218 219 // Couple of media related fields. 220 /// List of usable label starts. 221 /// 222 /// Used when tokenizing [text content][crate::construct::text]. 223 pub label_starts: Vec<LabelStart>, 224 /// List of unusable label starts. 225 /// 226 /// Used when tokenizing [text content][crate::construct::text]. 227 pub label_starts_loose: Vec<LabelStart>, 228 /// Stack of images and links. 229 /// 230 /// Used when tokenizing [text content][crate::construct::text]. 231 pub labels: Vec<Label>, 232 233 /// List of defined definition identifiers. 234 pub definitions: Vec<String>, 235 /// List of defined GFM footnote definition identifiers. 236 pub gfm_footnote_definitions: Vec<String>, 237 238 // Last error message provided at an EOF of an expression. 239 pub mdx_last_parse_error: Option<(String, String, String)>, 240 241 /// Whether to connect events. 242 pub connect: bool, 243 /// Marker. 244 pub marker: u8, 245 /// Secondary marker. 246 pub marker_b: u8, 247 /// Several markers. 248 pub markers: &'static [u8], 249 /// Whether something was seen. 250 pub seen: bool, 251 /// Size. 252 pub size: usize, 253 /// Secondary size. 254 pub size_b: usize, 255 /// Tertiary size. 256 pub size_c: usize, 257 /// Index. 258 pub start: usize, 259 /// Index. 260 pub end: usize, 261 /// Slot for an event name. 262 pub token_1: Name, 263 /// Slot for an event name. 264 pub token_2: Name, 265 /// Slot for an event name. 266 pub token_3: Name, 267 /// Slot for an event name. 268 pub token_4: Name, 269 /// Slot for an event name. 270 pub token_5: Name, 271 /// Slot for an event name. 272 pub token_6: Name, 273} 274 275/// A tokenizer itself. 276#[allow(clippy::struct_excessive_bools)] 277#[derive(Debug)] 278pub struct Tokenizer<'a> { 279 /// Jump between line endings. 280 column_start: Vec<(usize, usize)>, 281 // First line where this tokenizer starts. 282 first_line: usize, 283 /// Current point after the last line ending (excluding jump). 284 line_start: Point, 285 /// Track whether the current byte is already consumed (`true`) or expected 286 /// to be consumed (`false`). 287 /// 288 /// Tracked to make sure everything’s valid. 289 consumed: bool, 290 /// Stack of how to handle attempts. 291 attempts: Vec<Attempt>, 292 /// Current byte. 293 pub current: Option<u8>, 294 /// Previous byte. 295 pub previous: Option<u8>, 296 /// Current relative and absolute place in the file. 297 pub point: Point, 298 /// Semantic labels. 299 pub events: Vec<Event>, 300 /// Hierarchy of semantic labels. 301 /// 302 /// Tracked to make sure everything’s valid. 303 pub stack: Vec<Name>, 304 /// Edit map, to batch changes. 305 pub map: EditMap, 306 /// List of resolvers. 307 pub resolvers: Vec<ResolveName>, 308 /// Shared parsing state across tokenizers. 309 pub parse_state: &'a ParseState<'a>, 310 /// A lot of shared fields used to tokenize things. 311 pub tokenize_state: TokenizeState<'a>, 312 /// Whether we would be interrupting something. 313 /// 314 /// Used when tokenizing [flow content][crate::construct::flow]. 315 pub interrupt: bool, 316 /// Whether containers cannot “pierce” into the current construct. 317 /// 318 /// Used when tokenizing [document content][crate::construct::document]. 319 pub concrete: bool, 320 /// Whether this row is piercing into the current construct with more 321 /// containers. 322 /// 323 /// Used when tokenizing [document content][crate::construct::document]. 324 pub pierce: bool, 325 /// Whether this line is lazy: there are less containers than before. 326 pub lazy: bool, 327} 328 329impl<'a> Tokenizer<'a> { 330 /// Create a new tokenizer. 331 pub fn new(point: Point, parse_state: &'a ParseState) -> Tokenizer<'a> { 332 Tokenizer { 333 previous: None, 334 current: None, 335 // To do: reserve size when feeding? 336 column_start: vec![], 337 first_line: point.line, 338 line_start: point.clone(), 339 consumed: true, 340 attempts: vec![], 341 point, 342 stack: vec![], 343 events: vec![], 344 parse_state, 345 tokenize_state: TokenizeState { 346 connect: false, 347 document_container_stack: vec![], 348 document_exits: vec![], 349 document_continued: 0, 350 document_lazy_accepting_before: false, 351 document_data_index: None, 352 document_child_state: None, 353 document_child: None, 354 document_at_first_paragraph_of_list_item: false, 355 definitions: vec![], 356 gfm_footnote_definitions: vec![], 357 mdx_last_parse_error: None, 358 end: 0, 359 label_starts: vec![], 360 label_starts_loose: vec![], 361 marker: 0, 362 marker_b: 0, 363 markers: &[], 364 labels: vec![], 365 seen: false, 366 size: 0, 367 size_b: 0, 368 size_c: 0, 369 space_or_tab_eol_content: None, 370 space_or_tab_eol_connect: false, 371 space_or_tab_eol_ok: false, 372 space_or_tab_connect: false, 373 space_or_tab_content: None, 374 space_or_tab_min: 0, 375 space_or_tab_max: 0, 376 space_or_tab_size: 0, 377 space_or_tab_token: Name::SpaceOrTab, 378 start: 0, 379 token_1: Name::Data, 380 token_2: Name::Data, 381 token_3: Name::Data, 382 token_4: Name::Data, 383 token_5: Name::Data, 384 token_6: Name::Data, 385 wikilinks: vec![], 386 wikilink_starts: vec![], 387 }, 388 map: EditMap::new(), 389 interrupt: false, 390 pierce: false, 391 concrete: false, 392 lazy: false, 393 resolvers: vec![], 394 } 395 } 396 397 /// Register a resolver. 398 pub fn register_resolver(&mut self, name: ResolveName) { 399 if !self.resolvers.contains(&name) { 400 self.resolvers.push(name); 401 } 402 } 403 404 /// Register a resolver, before others. 405 pub fn register_resolver_before(&mut self, name: ResolveName) { 406 if !self.resolvers.contains(&name) { 407 self.resolvers.insert(0, name); 408 } 409 } 410 411 /// Define a jump between two places. 412 /// 413 /// This defines to which future index we move after a line ending. 414 pub fn define_skip(&mut self, mut point: Point) { 415 move_point_back(self, &mut point); 416 417 let info = (point.index, point.vs); 418 419 #[cfg(feature = "log")] 420 log::trace!("position: define skip: {:?} -> ({:?})", point.line, info); 421 422 let at = point.line - self.first_line; 423 424 if at >= self.column_start.len() { 425 self.column_start.push(info); 426 } else { 427 self.column_start[at] = info; 428 } 429 430 self.account_for_potential_skip(); 431 } 432 433 /// Increment the current positional info if we’re right after a line 434 /// ending, which has a skip defined. 435 fn account_for_potential_skip(&mut self) { 436 let at = self.point.line - self.first_line; 437 438 if self.point.column == 1 && at != self.column_start.len() { 439 self.move_to(self.column_start[at]); 440 } 441 } 442 443 /// Prepare for a next byte to get consumed. 444 fn expect(&mut self, byte: Option<u8>) { 445 debug_assert!(self.consumed, "expected previous byte to be consumed"); 446 self.consumed = false; 447 self.current = byte; 448 } 449 450 /// Consume the current byte. 451 /// Each state function is expected to call this to signal that this code is 452 /// used, or call a next function. 453 pub fn consume(&mut self) { 454 debug_assert!(!self.consumed, "expected code to *not* have been consumed: this might be because `State::Retry(x)` instead of `State::Next(x)` was returned"); 455 self.move_one(); 456 457 self.previous = self.current; 458 // While we’re not at eof, it is at least better to not have the 459 // same current code as `previous` *and* `current`. 460 self.current = None; 461 // Mark as consumed. 462 self.consumed = true; 463 } 464 465 /// Move to the next (virtual) byte. 466 fn move_one(&mut self) { 467 match byte_action(self.parse_state.bytes, &self.point) { 468 ByteAction::Ignore => { 469 self.point.index += 1; 470 } 471 ByteAction::Insert(byte) => { 472 self.previous = Some(byte); 473 self.point.column += 1; 474 self.point.vs += 1; 475 } 476 ByteAction::Normal(byte) => { 477 self.previous = Some(byte); 478 self.point.vs = 0; 479 self.point.index += 1; 480 481 if byte == b'\n' { 482 self.point.line += 1; 483 self.point.column = 1; 484 485 if self.point.line - self.first_line + 1 > self.column_start.len() { 486 self.column_start.push((self.point.index, self.point.vs)); 487 } 488 489 self.line_start = self.point.clone(); 490 491 self.account_for_potential_skip(); 492 493 #[cfg(feature = "log")] 494 log::trace!("position: after eol: `{:?}`", self.point); 495 } else { 496 self.point.column += 1; 497 } 498 } 499 } 500 } 501 502 /// Move (virtual) bytes. 503 fn move_to(&mut self, to: (usize, usize)) { 504 let (to_index, to_vs) = to; 505 while self.point.index < to_index || self.point.index == to_index && self.point.vs < to_vs { 506 self.move_one(); 507 } 508 } 509 510 /// Mark the start of a semantic label. 511 pub fn enter(&mut self, name: Name) { 512 enter_impl(self, name, None); 513 } 514 515 /// Enter with a link. 516 pub fn enter_link(&mut self, name: Name, link: Link) { 517 enter_impl(self, name, Some(link)); 518 } 519 520 /// Mark the end of a semantic label. 521 pub fn exit(&mut self, name: Name) { 522 let current = self.stack.pop().expect("cannot close w/o open tokens"); 523 524 debug_assert_eq!(current, name, "expected exit event to match current event"); 525 526 let previous = self.events.last().expect("cannot close w/o open event"); 527 let mut point = self.point.clone(); 528 529 debug_assert!( 530 current != previous.name 531 || previous.point.index != point.index 532 || previous.point.vs != point.vs, 533 "expected non-empty event" 534 ); 535 536 if VOID_EVENTS.iter().any(|d| d == &name) { 537 debug_assert!( 538 current == previous.name, 539 "expected event to be void, instead of including something" 540 ); 541 } 542 543 // A bit weird, but if we exit right after a line ending, we *don’t* want to consider 544 // potential skips. 545 if matches!(self.previous, Some(b'\n')) { 546 point = self.line_start.clone(); 547 } else { 548 move_point_back(self, &mut point); 549 } 550 551 #[cfg(feature = "log")] 552 log::debug!("exit: `{:?}`", name); 553 554 let event = Event { 555 kind: Kind::Exit, 556 name, 557 point, 558 link: None, 559 }; 560 self.events.push(event); 561 } 562 563 /// Capture the tokenizer progress. 564 fn capture(&mut self) -> Progress { 565 Progress { 566 previous: self.previous, 567 current: self.current, 568 point: self.point.clone(), 569 events_len: self.events.len(), 570 stack_len: self.stack.len(), 571 } 572 } 573 574 /// Apply tokenizer progress. 575 fn free(&mut self, previous: Progress) { 576 self.previous = previous.previous; 577 self.current = previous.current; 578 self.point = previous.point; 579 debug_assert!( 580 self.events.len() >= previous.events_len, 581 "expected to restore less events than before" 582 ); 583 self.events.truncate(previous.events_len); 584 debug_assert!( 585 self.stack.len() >= previous.stack_len, 586 "expected to restore less stack items than before" 587 ); 588 self.stack.truncate(previous.stack_len); 589 } 590 591 /// Stack an attempt, moving to `ok` on [`State::Ok`][] and `nok` on 592 /// [`State::Nok`][], reverting in both cases. 593 pub fn check(&mut self, ok: State, nok: State) { 594 // Always capture (and restore) when checking. 595 // No need to capture (and restore) when `nok` is `State::Nok`, because the 596 // parent attempt will do it. 597 let progress = Some(self.capture()); 598 let attempt = Attempt { 599 kind: AttemptKind::Check, 600 progress, 601 ok, 602 nok, 603 }; 604 self.attempts.push(attempt); 605 } 606 607 /// Stack an attempt, moving to `ok` on [`State::Ok`][] and `nok` on 608 /// [`State::Nok`][], reverting in the latter case. 609 pub fn attempt(&mut self, ok: State, nok: State) { 610 // Always capture (and restore) when checking. 611 // No need to capture (and restore) when `nok` is `State::Nok`, because the 612 // parent attempt will do it. 613 let progress = if nok == State::Nok { 614 None 615 } else { 616 Some(self.capture()) 617 }; 618 619 let attempt = Attempt { 620 kind: AttemptKind::Attempt, 621 progress, 622 ok, 623 nok, 624 }; 625 self.attempts.push(attempt); 626 } 627 628 /// Tokenize. 629 pub fn push(&mut self, from: (usize, usize), to: (usize, usize), state: State) -> State { 630 push_impl(self, from, to, state, false) 631 } 632 633 /// Flush. 634 pub fn flush(&mut self, state: State, resolve: bool) -> Result<Subresult, message::Message> { 635 let to = (self.point.index, self.point.vs); 636 let state = push_impl(self, to, to, state, true); 637 638 state.to_result()?; 639 640 let mut value = Subresult { 641 done: false, 642 gfm_footnote_definitions: self.tokenize_state.gfm_footnote_definitions.split_off(0), 643 definitions: self.tokenize_state.definitions.split_off(0), 644 }; 645 646 if resolve { 647 let resolvers = self.resolvers.split_off(0); 648 let mut index = 0; 649 let defs = &mut value.definitions; 650 let fn_defs = &mut value.gfm_footnote_definitions; 651 while index < resolvers.len() { 652 if let Some(mut result) = call_resolve(self, resolvers[index])? { 653 fn_defs.append(&mut result.gfm_footnote_definitions); 654 defs.append(&mut result.definitions); 655 } 656 index += 1; 657 } 658 659 self.map.consume(&mut self.events); 660 } 661 662 Ok(value) 663 } 664} 665 666/// Move back past ignored bytes. 667fn move_point_back(tokenizer: &mut Tokenizer, point: &mut Point) { 668 while point.index > 0 { 669 point.index -= 1; 670 let action = byte_action(tokenizer.parse_state.bytes, point); 671 if !matches!(action, ByteAction::Ignore) { 672 point.index += 1; 673 break; 674 } 675 } 676} 677 678/// Enter. 679fn enter_impl(tokenizer: &mut Tokenizer, name: Name, link: Option<Link>) { 680 let mut point = tokenizer.point.clone(); 681 move_point_back(tokenizer, &mut point); 682 683 #[cfg(feature = "log")] 684 log::debug!("enter: `{:?}`", name); 685 686 tokenizer.stack.push(name.clone()); 687 tokenizer.events.push(Event { 688 kind: Kind::Enter, 689 name, 690 point, 691 link, 692 }); 693} 694 695/// Run the tokenizer. 696fn push_impl( 697 tokenizer: &mut Tokenizer, 698 from: (usize, usize), 699 to: (usize, usize), 700 mut state: State, 701 flush: bool, 702) -> State { 703 debug_assert!( 704 from.0 > tokenizer.point.index 705 || (from.0 == tokenizer.point.index && from.1 >= tokenizer.point.vs), 706 "cannot move backwards" 707 ); 708 709 tokenizer.move_to(from); 710 711 loop { 712 match state { 713 State::Error(_) => break, 714 State::Ok | State::Nok => { 715 if let Some(attempt) = tokenizer.attempts.pop() { 716 if attempt.kind == AttemptKind::Check || state == State::Nok { 717 if let Some(progress) = attempt.progress { 718 tokenizer.free(progress); 719 } 720 } 721 722 tokenizer.consumed = true; 723 724 let next = if state == State::Ok { 725 attempt.ok 726 } else { 727 attempt.nok 728 }; 729 730 #[cfg(feature = "log")] 731 log::trace!("attempt: `{:?}` -> `{:?}`", state, next); 732 733 state = next; 734 } else { 735 break; 736 } 737 } 738 State::Next(name) => { 739 let action = if tokenizer.point.index < to.0 740 || (tokenizer.point.index == to.0 && tokenizer.point.vs < to.1) 741 { 742 Some(byte_action(tokenizer.parse_state.bytes, &tokenizer.point)) 743 } else if flush { 744 None 745 } else { 746 break; 747 }; 748 749 if let Some(ByteAction::Ignore) = action { 750 tokenizer.move_one(); 751 } else { 752 let byte = 753 if let Some(ByteAction::Insert(byte) | ByteAction::Normal(byte)) = action { 754 Some(byte) 755 } else { 756 None 757 }; 758 759 #[cfg(feature = "log")] 760 log::trace!("feed: {} to {:?}", format_byte_opt(byte), name); 761 762 tokenizer.expect(byte); 763 state = call(tokenizer, name); 764 } 765 } 766 State::Retry(name) => { 767 #[cfg(feature = "log")] 768 log::trace!("retry: `{:?}`", name); 769 770 state = call(tokenizer, name); 771 } 772 } 773 } 774 775 tokenizer.consumed = true; 776 777 if flush { 778 debug_assert!(matches!(state, State::Ok | State::Error(_)), "must be ok"); 779 } else { 780 debug_assert!( 781 matches!(state, State::Next(_) | State::Error(_)), 782 "must have a next state" 783 ); 784 } 785 786 state 787} 788 789/// Figure out how to handle a byte. 790fn byte_action(bytes: &[u8], point: &Point) -> ByteAction { 791 if point.index < bytes.len() { 792 let byte = bytes[point.index]; 793 794 if byte == b'\r' { 795 // CRLF. 796 if point.index < bytes.len() - 1 && bytes[point.index + 1] == b'\n' { 797 ByteAction::Ignore 798 } 799 // CR. 800 else { 801 ByteAction::Normal(b'\n') 802 } 803 } else if byte == b'\t' { 804 let remainder = point.column % TAB_SIZE; 805 let vs = if remainder == 0 { 806 0 807 } else { 808 TAB_SIZE - remainder 809 }; 810 811 // On the tab itself, first send it. 812 if point.vs == 0 { 813 if vs == 0 { 814 ByteAction::Normal(byte) 815 } else { 816 ByteAction::Insert(byte) 817 } 818 } else if vs == 0 { 819 ByteAction::Normal(b' ') 820 } else { 821 ByteAction::Insert(b' ') 822 } 823 } else { 824 ByteAction::Normal(byte) 825 } 826 } else { 827 unreachable!("out of bounds") 828 } 829}