Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,14 +180,13 @@ export class UI {
if (padding[right]) {
str += ' '.repeat(padding[right])
}

// if prior row is span, try to render the
// current row on the prior line.
if (r === 0 && lines.length > 0) {
str = this.renderInline(str, lines[lines.length - 1])
}
})

// if prior row is span, try to render the complete row on the prior line.
if (r === 0 && lines.length > 0) {
str = this.renderInline(str, lines[lines.length - 1])
}

// remove trailing whitespace.
lines.push({
text: str.replace(/ +$/, ''),
Expand Down Expand Up @@ -217,13 +216,16 @@ export class UI {
return target + source
}

if (leadingWhitespace < targetTextWidth) {
const sourceText = source.trimLeft()
// Keep separate pieces of visible text from running into each other.
const minimumGap = targetTextWidth > 0 && mixin.stringWidth(sourceText) > 0 ? 1 : 0
if (leadingWhitespace < targetTextWidth + minimumGap) {
return source
}

previousLine.hidden = true

return target.trimRight() + ' '.repeat(leadingWhitespace - targetTextWidth) + source.trimLeft()
return target.trimRight() + ' '.repeat(leadingWhitespace - targetTextWidth) + sourceText
}

private rasterize (row: ColumnArray) {
Expand Down
56 changes: 56 additions & 0 deletions test/cliui.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,62 @@ describe('cliui', () => {
})

describe('span', () => {
describe('span boundary', () => {
for (const [name, text] of [
['plain text', '1234567890'],
['ANSI text', '\u001b[32m1234567890\u001b[39m'],
['wide text', '漢字漢字漢']
]) {
it(`starts a new line when ${name} leaves no separating space`, () => {
const ui = cliui({ width: 20 })
ui.span(text)
ui.div({ text: '[boolean]', padding: [0, 0, 0, 10] })

stripAnsi(ui.toString()).should.equal(`${stripAnsi(text)}\n [boolean]`)
})
}

it('appends text when one separating space fits', () => {
const ui = cliui({ width: 20 })
ui.span('123456789')
ui.div({ text: '[boolean]', padding: [0, 0, 0, 10] })

ui.toString().should.equal('123456789 [boolean]')
})

it('checks the boundary after assembling all columns', () => {
const ui = cliui({ width: 20 })
ui.span('1234567890')
ui.div({ text: '', width: 10 }, { text: '[boolean]', width: 10 })

ui.toString().should.equal('1234567890\n [boolean]')
})

it('appends a multi-column row only once when wrapping is disabled', () => {
const ui = cliui({ width: 20, wrap: false })
ui.span('prefix')
ui.div('first', 'second')

ui.toString().should.equal('prefixfirstsecond')
})

it('does not introduce a blank line for an empty span', () => {
const ui = cliui({ width: 20 })
ui.span('')
ui.div('[boolean]')

ui.toString().should.equal('[boolean]')
})

it('does not introduce a blank line for an empty following column', () => {
const ui = cliui({ width: 20 })
ui.span('1234567890')
ui.div({ text: '', padding: [0, 0, 0, 10] })

ui.toString().should.equal('1234567890')
})
})

it('appends the next row to the end of the prior row if it fits', () => {
const ui = cliui({
width: 40
Expand Down