Skip to content
Permalink
9bfb9ba527
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
309 lines (248 sloc) 6.99 KB
'use strict'
const ACTUAL = Symbol('lineBuilder.gutters.ACTUAL')
const EXPECTED = Symbol('lineBuilder.gutters.EXPECTED')
function translateGutter (theme, invert, gutter) {
if (invert) {
if (gutter === ACTUAL) return theme.diffGutters.expected
if (gutter === EXPECTED) return theme.diffGutters.actual
} else {
if (gutter === ACTUAL) return theme.diffGutters.actual
if (gutter === EXPECTED) return theme.diffGutters.expected
}
return theme.diffGutters.padding
}
class Line {
constructor (isFirst, isLast, gutter, stringValue) {
this.isFirst = isFirst
this.isLast = isLast
this.gutter = gutter
this.stringValue = stringValue
}
* [Symbol.iterator] () {
yield this
}
get isEmpty () {
return false
}
get hasGutter () {
return this.gutter !== null
}
get isSingle () {
return this.isFirst && this.isLast
}
append (other) {
return this.concat(other)
}
concat (other) {
return new Collection()
.append(this)
.append(other)
}
toString (options) {
if (options.diff === false) return this.stringValue
return translateGutter(options.theme, options.invert, this.gutter) + this.stringValue
}
mergeWithInfix (infix, other) {
if (other.isLine !== true) {
return new Collection()
.append(this)
.mergeWithInfix(infix, other)
}
return new Line(this.isFirst, other.isLast, other.gutter, this.stringValue + infix + other.stringValue)
}
withFirstPrefixed (prefix) {
if (!this.isFirst) return this
return new Line(true, this.isLast, this.gutter, prefix + this.stringValue)
}
withLastPostfixed (postfix) {
if (!this.isLast) return this
return new Line(this.isFirst, true, this.gutter, this.stringValue + postfix)
}
stripFlags () {
return new Line(false, false, this.gutter, this.stringValue)
}
decompose () {
return new Collection()
.append(this)
.decompose()
}
}
Object.defineProperty(Line.prototype, 'isLine', { value: true })
class Collection {
constructor () {
this.buffer = []
}
* [Symbol.iterator] () {
for (const appended of this.buffer) {
for (const line of appended) yield line
}
}
get isEmpty () {
return this.buffer.length === 0
}
get hasGutter () {
for (const line of this) {
if (line.hasGutter) return true
}
return false
}
get isSingle () {
const iterator = this[Symbol.iterator]()
iterator.next()
return iterator.next().done === true
}
append (lineOrLines) {
if (!lineOrLines.isEmpty) this.buffer.push(lineOrLines)
return this
}
concat (other) {
return new Collection()
.append(this)
.append(other)
}
toString (options) {
let lines = this
if (options.invert) {
lines = new Collection()
let buffer = new Collection()
let prev = null
for (const line of this) {
if (line.gutter === ACTUAL) {
if (prev !== null && prev.gutter !== ACTUAL && !buffer.isEmpty) {
lines.append(buffer)
buffer = new Collection()
}
buffer.append(line)
} else if (line.gutter === EXPECTED) {
lines.append(line)
} else {
if (!buffer.isEmpty) {
lines.append(buffer)
buffer = new Collection()
}
lines.append(line)
}
prev = line
}
lines.append(buffer)
}
return Array.from(lines, line => line.toString(options)).join('\n')
}
mergeWithInfix (infix, from) {
if (from.isEmpty) throw new Error('Cannot merge, `from` is empty.')
const otherLines = Array.from(from)
if (!otherLines[0].isFirst) throw new Error('Cannot merge, `from` has no first line.')
const merged = new Collection()
let seenLast = false
for (const line of this) {
if (seenLast) throw new Error('Cannot merge line, the last line has already been seen.')
if (!line.isLast) {
merged.append(line)
continue
}
seenLast = true
for (const other of otherLines) {
if (other.isFirst) {
merged.append(line.mergeWithInfix(infix, other))
} else {
merged.append(other)
}
}
}
return merged
}
withFirstPrefixed (prefix) {
return new Collection()
.append(Array.from(this, line => line.withFirstPrefixed(prefix)))
}
withLastPostfixed (postfix) {
return new Collection()
.append(Array.from(this, line => line.withLastPostfixed(postfix)))
}
stripFlags () {
return new Collection()
.append(Array.from(this, line => line.stripFlags()))
}
decompose () {
const first = { actual: new Collection(), expected: new Collection() }
const last = { actual: new Collection(), expected: new Collection() }
const remaining = new Collection()
for (const line of this) {
if (line.isFirst && line.gutter === ACTUAL) {
first.actual.append(line)
} else if (line.isFirst && line.gutter === EXPECTED) {
first.expected.append(line)
} else if (line.isLast && line.gutter === ACTUAL) {
last.actual.append(line)
} else if (line.isLast && line.gutter === EXPECTED) {
last.expected.append(line)
} else {
remaining.append(line)
}
}
return { first, last, remaining }
}
}
Object.defineProperty(Collection.prototype, 'isCollection', { value: true })
function setDefaultGutter (iterable, gutter) {
return new Collection()
.append(Array.from(iterable, line => {
return line.gutter === null
? new Line(line.isFirst, line.isLast, gutter, line.stringValue)
: line
}))
}
module.exports = {
buffer () {
return new Collection()
},
first (stringValue) {
return new Line(true, false, null, stringValue)
},
last (stringValue) {
return new Line(false, true, null, stringValue)
},
line (stringValue) {
return new Line(false, false, null, stringValue)
},
single (stringValue) {
return new Line(true, true, null, stringValue)
},
setDefaultGutter (lineOrCollection) {
return lineOrCollection
},
actual: {
first (stringValue) {
return new Line(true, false, ACTUAL, stringValue)
},
last (stringValue) {
return new Line(false, true, ACTUAL, stringValue)
},
line (stringValue) {
return new Line(false, false, ACTUAL, stringValue)
},
single (stringValue) {
return new Line(true, true, ACTUAL, stringValue)
},
setDefaultGutter (lineOrCollection) {
return setDefaultGutter(lineOrCollection, ACTUAL)
},
},
expected: {
first (stringValue) {
return new Line(true, false, EXPECTED, stringValue)
},
last (stringValue) {
return new Line(false, true, EXPECTED, stringValue)
},
line (stringValue) {
return new Line(false, false, EXPECTED, stringValue)
},
single (stringValue) {
return new Line(true, true, EXPECTED, stringValue)
},
setDefaultGutter (lineOrCollection) {
return setDefaultGutter(lineOrCollection, EXPECTED)
},
},
}