summaryrefslogtreecommitdiff
path: root/pkgtools/pkglint/files/line.go
blob: c1bc1d9eaaf8bedcb69729accf2e9d15e0f57cf6 (plain)
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package main

// When files are read in by pkglint, they are interpreted in terms of
// lines. For Makefiles, line continuations are handled properly, allowing
// multiple raw lines to end in a single logical line. For other files
// there is a 1:1 translation.
//
// A difference between the raw and the logical lines is that the
// raw lines include the line end sequence, whereas the logical lines
// do not.
//
// Some methods allow modification of the raw lines contained in the
// logical line, but leave the “text” field untouched. These methods are
// used in the --autofix mode.

import (
	"fmt"
	"io"
	"strings"
)

type RawLine struct {
	lineno int
	textnl string
}

func (rline *RawLine) String() string {
	return sprintf("%d:%s", rline.lineno, rline.textnl)
}

type Line struct {
	fname   string
	lines   string
	text    string
	raw     []*RawLine
	changed bool
	before  []*RawLine
	after   []*RawLine
	extra   map[string]interface{}
}

func NewLine(fname, linenos, text string, rawLines []*RawLine) *Line {
	return &Line{fname, linenos, text, rawLines, false, nil, nil, make(map[string]interface{})}
}

func (self *Line) rawLines() []*RawLine {
	return append(self.before, append(self.raw, self.after...)...)
}
func (self *Line) printSource(out io.Writer) {
	if G.opts.PrintSource {
		io.WriteString(out, "\n")
		for _, rawLine := range self.rawLines() {
			fmt.Fprintf(out, "> %s", rawLine.textnl)
		}
	}
}
func (self *Line) fatalf(format string, args ...interface{}) bool {
	self.printSource(G.logErr)
	return fatalf(self.fname, self.lines, format, args...)
}
func (self *Line) errorf(format string, args ...interface{}) bool {
	self.printSource(G.logOut)
	return errorf(self.fname, self.lines, format, args...)
}
func (self *Line) warnf(format string, args ...interface{}) bool {
	self.printSource(G.logOut)
	return warnf(self.fname, self.lines, format, args...)
}
func (self *Line) notef(format string, args ...interface{}) bool {
	self.printSource(G.logOut)
	return notef(self.fname, self.lines, format, args...)
}
func (self *Line) debugf(format string, args ...interface{}) bool {
	self.printSource(G.logOut)
	return debugf(self.fname, self.lines, format, args...)
}
func (self *Line) explain(explanation ...string) {
	if G.opts.Explain {
		complete := strings.Join(explanation, "\n")
		if G.explanationsGiven[complete] {
			return
		}
		if G.explanationsGiven == nil {
			G.explanationsGiven = make(map[string]bool)
			G.explanationsGiven[complete] = true
		}

		io.WriteString(G.logOut, "\n")
		for _, explanationLine := range explanation {
			io.WriteString(G.logOut, "\t"+explanationLine+"\n")
		}
		io.WriteString(G.logOut, "\n")
	}
	G.explanationsAvailable = true
}
func (self *Line) String() string {
	return self.fname + ":" + self.lines + ": " + self.text
}

func (self *Line) insertBefore(line string) {
	self.before = append(self.before, &RawLine{0, line + "\n"})
	self.changed = true
	if G.opts.PrintAutofix {
		self.notef("Autofix: inserting a line %q before this line.", line)
	}
}
func (self *Line) insertAfter(line string) {
	self.after = append(self.after, &RawLine{0, line + "\n"})
	self.changed = true
	if G.opts.PrintAutofix {
		self.notef("Autofix: inserting a line %q after this line.", line)
	}
}
func (self *Line) delete() {
	self.raw = nil
	self.changed = true
}
func (self *Line) replace(from, to string) {
	for _, rawLine := range self.raw {
		if rawLine.lineno != 0 {
			if replaced := strings.Replace(rawLine.textnl, from, to, 1); replaced != rawLine.textnl {
				rawLine.textnl = replaced
				self.changed = true
				if G.opts.PrintAutofix {
					self.notef("Autofix: replacing %q with %q.", from, to)
				}
				G.autofixAvailable = true
			}
		}
	}
}
func (self *Line) replaceRegex(from, to string) {
	for _, rawLine := range self.raw {
		if rawLine.lineno != 0 {
			if replaced := regcomp(from).ReplaceAllString(rawLine.textnl, to); replaced != rawLine.textnl {
				rawLine.textnl = replaced
				self.changed = true
				if G.opts.PrintAutofix {
					self.notef("Autofix: replacing regular expression %q with %q.", from, to)
				}
				G.autofixAvailable = true
			}
		}
	}
}