-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpatch.go
104 lines (88 loc) · 2.78 KB
/
patch.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// SPDX-FileCopyrightText: 2020 Alvar Penning
//
// SPDX-License-Identifier: GPL-3.0-or-later
package pmwiki
import (
"bufio"
"fmt"
"io"
"strings"
)
// patchType describes the change of a patchAction.
type patchType int
const (
_ patchType = iota
// addition of lines from a Patch.
addition
// deletion of lines from a Patch.
deletion
// change of lines from a Patch, combined addition and deletion.
change
)
// patchAction is one change within a Patch.
type patchAction struct {
mode patchType
startLine int
additionLines []string
deletionLines []string
}
// apply this patchAction. Will be called from Patch.Apply.
func (patchAction patchAction) apply(scanner *bufio.Scanner, out io.Writer) (consumed int, err error) {
if patchAction.mode == deletion || patchAction.mode == change {
for consumed = 0; consumed < len(patchAction.deletionLines) && scanner.Scan(); consumed++ {
// Sometimes PmWiki truncates whitespaces from patches, because oh̨ m͞y gǫd ͜p̀mwi͝ki s̡̧͝t͏a̢̧̛h̀p̕ ͝w͏̸̵h͢a͢͞t̴̨̀ a̧̧re ̢͠y̨͠o̧͏ư̴̴ d͡o̴i͜ng̕?̸̕͞!̡͠!
input := scanner.Text()
expected := patchAction.deletionLines[consumed]
if input != expected && input != strings.TrimSpace(expected) {
err = fmt.Errorf("patch:%d expected \"%s\", got \"%s\"", consumed, patchAction.deletionLines[consumed], input)
return
}
}
if scannerErr := scanner.Err(); scannerErr != nil {
err = scannerErr
return
}
}
if patchAction.mode == addition || patchAction.mode == change {
for _, line := range patchAction.additionLines {
if _, err = fmt.Fprint(out, line, "\n"); err != nil {
return
}
}
}
return
}
// Patch is the difference between two revisions stored as a `diff`.
type Patch []patchAction
// Apply this Patch to an input stream and write the patched result back to an output stream.
func (patch Patch) Apply(in io.Reader, out io.Writer) error {
scanner := bufio.NewScanner(in)
for patchNo, line := 0, 0; ; {
// Deletion / Change first
if patchNo < len(patch) && patch[patchNo].startLine == line && (patch[patchNo].mode == deletion || patch[patchNo].mode == change) {
if consumed, err := patch[patchNo].apply(scanner, out); err != nil {
return err
} else {
patchNo++
line += consumed
}
}
// Consume a line, unless we have not started reading, e.g., for an addition patch starting at line zero
if line > 0 {
if !scanner.Scan() {
return scanner.Err()
} else if _, err := fmt.Fprint(out, scanner.Text(), "\n"); err != nil {
return err
}
}
// Addition second
if patchNo < len(patch) && patch[patchNo].startLine == line && patch[patchNo].mode == addition {
if _, err := patch[patchNo].apply(scanner, out); err != nil {
return err
} else {
patchNo++
}
}
line++
}
}