-
Notifications
You must be signed in to change notification settings - Fork 31
New ed #480
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Phase 0 and Phase 1 are complete. Here's what was accomplished: Summary Phase 0: Test Structure Migration - Created editors/tests/editors-tests.rs harness with only mod statements (following CONTRIBUTING.md pattern) - Migrated ex_tests.rs → tests/ex/mod.rs - Migrated integration_tests.rs → tests/integration/mod.rs - Migrated headless_editor_tests.rs → tests/headless/mod.rs - Deleted old standalone test files Phase 1: ed Editor Implementation Created a working POSIX ed line editor with: Files created: - editors/ed_main.rs - Entry point with clap CLI - editors/ed/mod.rs - Module exports - editors/ed/error.rs - Error types - editors/ed/buffer.rs - Line buffer with undo support - editors/ed/parser.rs - Tokenizer and command parser - editors/ed/editor.rs - Main editor logic - editors/tests/ed/mod.rs - 27 integration tests Commands implemented: - Basic: q, Q, a, i, c, p, n, l, d - File I/O: e, E, f, r, w, W, wq, x - Line operations: j, m, t, k, =, z - Substitution: s, & - Global: g, G, v, V - Other: u (undo), h, H, P, !, # Status: - ✅ cargo build --release - Compiles without errors or warnings - ✅ cargo test --release -p posixutils-editors - All 263 tests pass - ✅ cargo fmt --all -- --check - No formatting issues - ✅ cargo clippy -p posixutils-editors - No warnings The ed editor is now functional for basic POSIX editing tasks. Future phases (2-6) from the plan would add more advanced features like regex search addresses and signal handling.
Fixes for POSIX Compliance:
1. Semicolon separator semantics (editors/ed/parser.rs, editors/ed/editor.rs):
- Added relative_to_first field to Address struct
- Modified parser to set this flag when an address is preceded by a semicolon separator
- Added resolve_range() helper that handles semicolon semantics - when the second address has relative_to_first = true, it's resolved using the first address's value as the base
- Added resolve_address_with_base() and search functions with _from variants to support different base lines
2. Multiple offset parsing fix (editors/ed/parser.rs):
- Fixed bug where addr_dirty check was running after every token in SepOffCommand state
- Moved the check inside the specific match arms so addresses are only pushed when we see a separator or command, not after every offset
New Tests Added (editors/tests/ed/mod.rs):
- test_ed_semicolon_separator - Tests 2;+1p (prints lines 2 and 3)
- test_ed_comma_separator - Tests 2,4p (prints lines 2 to 4)
- test_ed_semicolon_with_offset - Tests 3;+2p (prints lines 3 to 5)
- test_ed_semicolon_current_line - Tests ;+2p with implicit current line
- test_ed_edit_command - Tests the e command
- test_ed_filename_command - Tests the f command
- test_ed_positive_offset, test_ed_negative_offset, test_ed_multiple_offsets - Tests address offsets
POSIX Compliance Fixes:
1. List command (editors/ed/editor.rs):
- Added POSIX-required escape sequences: \a, \f, \r, \v (in addition to \t, \b, \\)
- Fixed $ escaping within text (\$)
- Changed non-printable character output from 2-digit hex to 3-digit octal per POSIX
2. Change command (editors/ed/buffer.rs):
- Fixed current line handling when no new lines are input per POSIX:
- If buffer empty: current line = 0
- If line exists after deleted range: current line = that line
- If deleted at end: current line = new last line
New Tests Added (25 tests):
- Delete command: test_ed_delete_range, test_ed_delete_at_end, test_ed_delete_all
- Change command: test_ed_change_with_text, test_ed_change_no_text, test_ed_change_range
- Join command: test_ed_join_single_address, test_ed_join_default_range, test_ed_join_multiple
- Move command: test_ed_move_to_start, test_ed_move_to_end, test_ed_move_range
- Copy command: test_ed_copy_to_start, test_ed_copy_range
- Mark command: test_ed_mark_and_address, test_ed_mark_in_range
- Number command: test_ed_number_range
- List command: test_ed_list_tab, test_ed_list_dollar, test_ed_list_backslash
- Line number command: test_ed_line_number_current_unchanged, test_ed_line_number_default
- Search address: test_ed_search_forward_in_range, test_ed_search_backward_in_range, test_ed_search_wrap_forward
Phase 5 Summary
Code Changes
editors/ed/buffer.rs:
- Added in_global flag to suppress individual undo saves during global commands
- Added begin_global() method to save undo once and enable suppression
- Added end_global() method to re-enable individual undo saves
editors/ed/editor.rs:
- Modified resolve_address() and resolve_address_with_base() to &mut self to save patterns
- POSIX fix: Search addresses (/pattern/, ?pattern?) now save the pattern to last_pattern for subsequent null RE references
- Updated execute_global() to:
- Save one undo record for entire operation using begin_global()/end_global()
- Properly handle empty matching lines (current line unchanged per POSIX)
- Track current line set by last successful command
- Added direct handling for 'n' and 'l' commands in global
New Tests (17 tests added)
- test_ed_global_print_all_matches - g/pattern/p prints all matches
- test_ed_global_delete_all_matches - g/pattern/d deletes all matches
- test_ed_global_number_matches - g/pattern/n with line numbers
- test_ed_global_substitute - g/pattern/s substitute on matches
- test_ed_global_invert_print - v/pattern/p prints non-matches
- test_ed_global_invert_delete - v/pattern/d deletes non-matches
- test_ed_global_undo_entire_operation - Undo restores entire global operation
- test_ed_global_empty_command_defaults_to_print - Empty command defaults to 'p'
- test_ed_global_with_range - Global with address range
- test_ed_global_no_match_unchanged - No match leaves current line unchanged
- test_ed_global_current_line_after - Current line set by last command
- test_ed_global_different_delimiter - Different delimiter character
- test_ed_global_with_previous_pattern - Empty pattern uses previous RE
- test_ed_invert_global_with_range - v command with address range
- test_ed_global_substitute_with_flags - g with substitute and flags
- test_ed_global_undo_substitute - Undo global substitute
- test_ed_global_list_command - g with list command
Phase 6 (Help, Shell, Signals) is complete. Here's a summary:
Phase 6 Summary
Code Changes
editors/ed/editor.rs:
- Added last_shell_command field to store previous shell command for !! repetition
- Fixed Command::HelpMode (H) to print last error when turning help mode ON (per POSIX)
- Fixed Command::Shell for full POSIX compliance:
- % replaced with remembered pathname
- ! at start replaced with previous shell command text (for !!)
- If replacements performed, modified line printed before execution
- ! only printed on completion when not in silent mode
- Store command for future !! repetition
editors/ed_main.rs:
- Added setup_signals() function with SIGQUIT ignored (per POSIX)
- Note: Full SIGINT/SIGHUP handling would require deeper integration
New Tests (9 tests added)
- test_ed_help_command - h command prints last error message
- test_ed_help_mode_toggle - H command toggles help mode
- test_ed_help_mode_shows_last_error - H shows last error when turning on
- test_ed_shell_escape - Basic shell command execution
- test_ed_shell_escape_silent - Shell command with -s flag (no "!")
- test_ed_shell_percent_expansion - % replaced with pathname
- test_ed_shell_repeat - !! repeats last shell command
- test_ed_prompt_toggle - P command with -p option
- test_ed_prompt_with_p_option - -p option sets prompt string
Completed POSIX Compliance Fixes
1. Phase 1.1: Bare newline as .+1p - Already implemented, added tests to verify
2. Phase 1.2: SIGINT/SIGHUP signal handling - Added proper signal handlers:
- SIGINT: Prints "?" and continues
- SIGHUP: Saves buffer to ed.hup before exiting
3. Phase 2.1-2.3: Shell command integration - Implemented !command syntax for:
- e !command - Read shell output into buffer
- r !command - Append shell output after address
- w !command - Pipe buffer to shell command stdin
4. Phase 3.2: Nested command check - Added validation to reject g/G/v/V/! in global command lists
5. Phase 5.1: Long line folding - The l command now folds lines at 72 characters
Completed Features Phase 4.1: Escaped newline in substitute replacement (line splitting) - Modified convert_replacement() to preserve \<newline> sequences - Modified execute_substitute() to split lines when replacement contains internal newlines - Added line number tracking to handle multiple line splits in a range - Added guard to prevent line splitting in global command context (POSIX requirement) - Files modified: editors/ed/editor.rs, editors/ed/buffer.rs Phase 3.1: Multi-line command lists for g/v - Added check_line_continuation() function to detect backslash-newline continuation - Modified process_line() to accumulate continued lines before parsing - This enables both substitute replacement continuation and g/v command list continuation - Files modified: editors/ed/editor.rs Phase 3.3 & 3.4: Interactive G/V commands - Implemented execute_global_interactive() function - For each matching line: prints the line, reads a command from user - Handles empty line (skip), & (repeat previous command), and regular commands - Tracks line number shifts when lines are deleted/added - Checks for SIGINT to abort - Added last_interactive_cmd field to track previous command for & - Files modified: editors/ed/editor.rs
Phase 3.5: a/i/c with Input in Global Command Lists
POSIX Requirement: The a, i, and c commands can include text lines embedded in a global (g/v) command list, terminated by . (or optional if last command).
Changes Made:
1. editors/ed/editor.rs:
- Added GlobalCommand struct to represent a parsed command with optional embedded input lines
- Added find_command_char() helper to identify command character skipping address prefix
- Added parse_global_command_list() to parse multi-line command lists and extract a/i/c input
- Added execute_input_command_with_lines() helper to execute a/i/c with pre-supplied input
- Modified execute_global() to use the new parsing and handle embedded input
- Added line offset tracking to correctly adjust line numbers when insert/append operations shift buffer lines
2. editors/tests/ed/mod.rs:
- Added 6 test cases for global append/insert/change with embedded input
- Tests cover: basic append/insert/change, optional . terminator, multiple input lines, mixed commands
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR implements a new ed line editor for the posixutils-rs project, aiming for POSIX.1-2024 compliance. The implementation includes the core ed functionality with comprehensive integration tests.
Key changes:
- New ed line editor implementation with buffer management, command parsing, and editor logic
- Comprehensive integration tests for ed, ex, and vi components
- Updates to build configuration and documentation to include the new ed utility
Reviewed changes
Copilot reviewed 12 out of 16 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
editors/ed_main.rs |
Entry point for ed binary with signal handling and CLI argument parsing |
editors/ed/mod.rs |
Module organization for ed implementation |
editors/ed/error.rs |
Error type definitions for ed operations |
editors/ed/parser.rs |
Command line parser with tokenizer and command parsing logic |
editors/ed/editor.rs |
Main editor logic including command execution and global commands |
editors/ed/buffer.rs |
Buffer management with undo support and file I/O |
editors/tests/ed/mod.rs |
Comprehensive integration tests for ed (1394 lines) |
editors/tests/ex/mod.rs |
Integration tests for ex editor |
editors/tests/headless/mod.rs |
Headless editor tests for vi |
editors/tests/integration/mod.rs |
Integration tests for vi components |
editors/tests/editors-tests.rs |
Test harness with mod statements |
editors/Cargo.toml |
Added clap dependency and ed binary configuration |
lib/utils.tsv |
Added ed entry to utilities list |
README.md |
Updated checklist to mark ed as completed |
Cargo.lock |
Updated with clap dependency for editors |
m4/tests/integration_test.rs |
Simplified build/test logic (removed BUILD_ONCE) |
No description provided.