9 private $warningCallback;
12 public static function edit( $text, array $deletions, array $changes, $warningCallback = null ) {
13 $editor = new self( $text, $deletions, $changes, $warningCallback );
15 return $editor->result
;
18 private function __construct( $text, array $deletions, array $changes, $warningCallback ) {
19 $this->lines
= explode( "\n", $text );
20 $this->numLines
= count( $this->lines
);
21 $this->deletions
= array_flip( $deletions );
22 $this->changes
= $changes;
24 $this->warningCallback
= $warningCallback;
28 private function execute() {
29 while ( $this->pos
< $this->numLines
) {
30 $line = $this->lines
[$this->pos
];
31 switch ( $this->getHeading( $line ) ) {
37 case 'transparenthooks':
41 if ( $this->pos
< $this->numLines
- 1 ) {
44 $this->emitComment( $line );
48 foreach ( $this->deletions
as $deletion => $unused ) {
49 $this->warning( "Could not find test \"$deletion\" to delete it" );
51 foreach ( $this->changes
as $test => $sectionChanges ) {
52 foreach ( $sectionChanges as $section => $change ) {
53 $this->warning( "Could not find section \"$section\" in test \"$test\" " .
54 "to {$change['op']} it" );
59 private function warning( $text ) {
60 $cb = $this->warningCallback
;
66 private function getHeading( $line ) {
67 if ( preg_match( '/^!!\s*(\S+)/', $line, $m ) ) {
74 private function parseTest() {
76 $line = $this->lines
[$this->pos++
];
77 $heading = $this->getHeading( $line );
80 'headingLine' => $line,
84 while ( $this->pos
< $this->numLines
) {
85 $line = $this->lines
[$this->pos++
];
86 $nextHeading = $this->getHeading( $line );
87 if ( $nextHeading === 'end' ) {
90 // Add trailing line breaks to the "end" section, to allow for neat deletions
92 for ( $i = 0; $i < $this->numLines
- $this->pos
- 1; $i++
) {
93 if ( $this->lines
[$this->pos +
$i] === '' ) {
99 $this->pos +
= strlen( $trail );
103 'headingLine' => $line,
106 $this->emitTest( $test );
108 } elseif ( $nextHeading !== false ) {
110 $heading = $nextHeading;
113 'headingLine' => $line,
117 $section['contents'] .= "$line\n";
121 throw new Exception( 'Unexpected end of file' );
124 private function parseHooks() {
125 $line = $this->lines
[$this->pos++
];
126 $heading = $this->getHeading( $line );
127 $expectedEnd = 'end' . $heading;
131 $line = $this->lines
[$this->pos++
];
132 $nextHeading = $this->getHeading( $line );
133 $contents .= "$line\n";
134 } while ( $this->pos
< $this->numLines
&& $nextHeading !== $expectedEnd );
136 if ( $nextHeading !== $expectedEnd ) {
137 throw new Exception( 'Unexpected end of file' );
139 $this->emitHooks( $heading, $contents );
142 protected function emitComment( $contents ) {
143 $this->result
.= $contents;
146 protected function emitTest( $test ) {
148 foreach ( $test as $section ) {
149 if ( $section['name'] === 'test' ) {
150 $testName = rtrim( $section['contents'], "\n" );
153 if ( isset( $this->deletions
[$testName] ) ) {
154 // Acknowledge deletion
155 unset( $this->deletions
[$testName] );
158 if ( isset( $this->changes
[$testName] ) ) {
159 $changes =& $this->changes
[$testName];
160 foreach ( $test as $i => $section ) {
161 $sectionName = $section['name'];
162 if ( isset( $changes[$sectionName] ) ) {
163 $change = $changes[$sectionName];
164 switch ( $change['op'] ) {
166 $test[$i]['name'] = $change['value'];
167 $test[$i]['headingLine'] = "!! {$change['value']}";
170 $test[$i]['contents'] = $change['value'];
173 $test[$i]['deleted'] = true;
176 throw new Exception( "Unknown op: ${change['op']}" );
179 // Note that we use the old section name for the rename op
180 unset( $changes[$sectionName] );
184 foreach ( $test as $section ) {
185 if ( isset( $section['deleted'] ) ) {
188 $this->result
.= $section['headingLine'] . "\n";
189 $this->result
.= $section['contents'];
193 protected function emitHooks( $heading, $contents ) {
194 $this->result
.= $contents;