Skip to content

Refactor fence specifier attachment#212

Merged
leynos merged 2 commits intomainfrom
codex/refactor-attach_orphan_specifiers-function
Sep 12, 2025
Merged

Refactor fence specifier attachment#212
leynos merged 2 commits intomainfrom
codex/refactor-attach_orphan_specifiers-function

Conversation

@leynos
Copy link
Copy Markdown
Owner

@leynos leynos commented Sep 12, 2025

Summary

  • extract helper utilities for orphan specifiers and fences
  • refactor fence specifier attachment with peekable state machine

Testing

  • make fmt
  • make lint
  • make test

closes #109


https://chatgpt.com/codex/tasks/task_e_68c2e577d2d48322a55a0bc6846ce3c0

Summary by Sourcery

Improve readability and robustness of fence specifier attachment by extracting helper functions and employing a state machine in attach_orphan_specifiers.

Enhancements:

  • Extract helper utilities for orphan specifier normalization and fence detection
  • Refactor attach_orphan_specifiers to use a peekable iterator state machine for clearer logic

Documentation:

  • Add documentation and examples for the new fence specifier helper functions

Extract helpers and implement clearer state machine using lookahead to pair orphan specifiers with fences.
@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Sep 12, 2025

Reviewer's Guide

This PR reorganizes the fence specifier attachment logic by pulling out specialized helper functions for detecting and combining specifiers and rewrites the main attach_orphan_specifiers loop as a two‐state peekable iterator state machine, improving clarity and maintainability.

Sequence diagram for orphan specifier attachment with peekable state machine

sequenceDiagram
    participant Iterator
    participant StateMachine
    participant HelperFunctions
    Iterator->>StateMachine: next(line)
    StateMachine->>HelperFunctions: is_orphan_specifier(line)
    HelperFunctions-->>StateMachine: bool
    StateMachine->>Iterator: peek()
    Iterator-->>StateMachine: next_line
    StateMachine->>HelperFunctions: is_opening_fence_without_specifier(next_line)
    HelperFunctions-->>StateMachine: bool
    StateMachine->>HelperFunctions: attach_specifier_to_fence(fence, spec, indent)
    HelperFunctions-->>StateMachine: combined_line
    StateMachine->>Iterator: advance past blanks and fence
    StateMachine->>Iterator: next(line)
    Note over StateMachine,Iterator: State transitions between LookingForSpecifier and InsideFence
Loading

Class diagram for new helper functions in fences.rs

classDiagram
    class fences {
        +compress_fences(lines: &[String]) Vec<String>
        +attach_orphan_specifiers(lines: &[String]) Vec<String>
        +is_orphan_specifier(line: &str) bool
        +is_opening_fence_without_specifier(line: &str) bool
        +attach_specifier_to_fence(fence_line: &str, specifier: &str, spec_indent: &str) String
    }
    fences <|-- State
    class State {
        LookingForSpecifier
        InsideFence
    }
Loading

File-Level Changes

Change Details Files
Extract helper utilities for specifier and fence detection and formatting
  • Add is_orphan_specifier to validate orphaned language lines
  • Add is_opening_fence_without_specifier to detect unlabeled opening fences
  • Add attach_specifier_to_fence to merge a specifier into a fence line with proper indentation
src/fences.rs
Refactor attach_orphan_specifiers to use a peekable iterator and state machine
  • Introduce State enum (LookingForSpecifier, InsideFence) and switch on it
  • Use a peekable lines iterator to scan blank lines and upcoming fences before attachment
  • Simplify removal and insertion of specifier and fence lines using helper functions
src/fences.rs
Update documentation and error handling
  • Document panic scenario when a peeked fence line is missing
  • Enhance doc examples for new helper functions
src/fences.rs

Assessment against linked issues

Issue Objective Addressed Explanation
#109 Extract helper functions: is_orphan_specifier, is_opening_fence_without_specifier, attach_specifier_to_fence
#109 Use a state machine approach in attach_orphan_specifiers to clarify state transitions
#109 Separate concerns: line classification, state management, and line transformation within attach_orphan_specifiers

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Sep 12, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Warning

Rate limit exceeded

@leynos has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 12 minutes and 59 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 2e7a3dd and a58bc9c.

📒 Files selected for processing (2)
  • src/fences.rs (2 hunks)
  • tests/fences.rs (1 hunks)
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/refactor-attach_orphan_specifiers-function

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes and found some issues that need to be addressed.

Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments

### Comment 1
<location> `src/fences.rs:212` </location>
<code_context>
+                            iter.next();
+                        }
+                        let fence = iter.next().expect("peeked fence");
+                        out.push(attach_specifier_to_fence(fence, &spec, &indent));
+                        state = State::InsideFence;
                         continue;
</code_context>

<issue_to_address>
attach_specifier_to_fence may not handle all indentation edge cases.

Please review how final_indent is determined when the fence and specifier have different indentation, as this may cause inconsistent formatting. Update the logic or documentation to address these cases.
</issue_to_address>

### Comment 2
<location> `src/fences.rs:178` </location>
<code_context>
 /// ```
 #[must_use]
 pub fn attach_orphan_specifiers(lines: &[String]) -> Vec<String> {
-    let mut out: Vec<String> = Vec::with_capacity(lines.len());
-    let mut in_fence = false;
</code_context>

<issue_to_address>
Consider replacing the state machine and peekable iterator with a simple index-driven loop to streamline the function.

Consider dropping the two‐state machine and peekable iterator in favor of a simple index‐driven `while` loop.  It lets you

• get rid of `State`, the `peekable().clone()`, and blank counters  
• inline your fence‐without‐lang check and reduce helper bloat  

For example:

```rust
pub fn attach_orphan_specifiers(lines: &[String]) -> Vec<String> {
    let mut out = Vec::with_capacity(lines.len());
    let mut i = 0;

    while i < lines.len() {
        let line = &lines[i];

        // detect orphan specifier (inlining `is_orphan_specifier`)
        let (spec, indent) = normalize_specifier(line);
        if ORPHAN_LANG_RE.is_match(&spec)
           && out.last().map_or(true, |l| l.trim().is_empty())
        {
            // skip the specifier line
            i += 1;
            // skip any blank lines
            while i < lines.len() && lines[i].trim().is_empty() {
                i += 1;
            }
            // if next is an opening fence without lang
            if i < lines.len() {
                if let Some(cap) = FENCE_RE.captures(&lines[i]) {
                    let lang = cap.get(3).map_or("", |m| m.as_str());
                    if is_null_lang(lang) {
                        out.push(attach_specifier_to_fence(&lines[i], &spec, &indent));
                        i += 1;
                        continue;
                    }
                }
            }
            // fallback: no fence to attach to
            out.push(line.clone());
            continue;
        }

        // default path: just push and advance
        out.push(line.clone());
        i += 1;
    }

    out
}
```

This preserves all behavior but removes the nested state machine and helper proliferation.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/fences.rs Outdated
Comment thread src/fences.rs
@leynos leynos merged commit 2973488 into main Sep 12, 2025
3 checks passed
@leynos leynos deleted the codex/refactor-attach_orphan_specifiers-function branch September 12, 2025 02:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Refactor attach_orphan_specifiers function to reduce complexity

1 participant