Example #1
0
func (nc *navColumn) FullWidth(h int) int {
	if nc == nil {
		return 0
	}
	if nc.err != nil {
		return util.Wcswidth(nc.err.Error())
	}
	maxw := 0
	for _, s := range nc.candidates {
		maxw = max(maxw, util.Wcswidth(s.text))
	}
	if maxw >= navigationListingMinWidthForPadding {
		maxw += 2
	}
	if len(nc.candidates) > h {
		maxw++
	}
	return maxw
}
Example #2
0
func moveDotUp(ed *Editor) {
	sol := util.FindLastSOL(ed.line[:ed.dot])
	if sol == 0 {
		ed.flash()
		return
	}
	prevEOL := sol - 1
	prevSOL := util.FindLastSOL(ed.line[:prevEOL])
	width := util.Wcswidth(ed.line[sol:ed.dot])
	ed.dot = prevSOL + len(util.TrimWcwidth(ed.line[prevSOL:prevEOL], width))
}
Example #3
0
func moveDotDown(ed *Editor) {
	eol := util.FindFirstEOL(ed.line[ed.dot:]) + ed.dot
	if eol == len(ed.line) {
		ed.flash()
		return
	}
	nextSOL := eol + 1
	nextEOL := util.FindFirstEOL(ed.line[nextSOL:]) + nextSOL
	sol := util.FindLastSOL(ed.line[:ed.dot])
	width := util.Wcswidth(ed.line[sol:ed.dot])
	ed.dot = nextSOL + len(util.TrimWcwidth(ed.line[nextSOL:nextEOL], width))
}
Example #4
0
// maxWidth finds the maximum wcwidth of display texts of candidates [lo, hi).
// hi may be larger than the number of candidates, in which case it is truncated
// to the number of candidates.
func (comp *completion) maxWidth(lo, hi int) int {
	if hi > len(comp.candidates) {
		hi = len(comp.candidates)
	}
	width := 0
	for i := lo; i < hi; i++ {
		w := util.Wcswidth(comp.candidates[i].display.text)
		if width < w {
			width = w
		}
	}
	return width
}
Example #5
0
func wcswidth(ec *EvalCtx, s String) {
	out := ec.ports[1].Chan
	out <- String(strconv.Itoa(util.Wcswidth(string(s))))
}
Example #6
0
// refresh redraws the line editor. The dot is passed as an index into text;
// the corresponding position will be calculated.
func (w *writer) refresh(es *editorState, fullRefresh bool) error {
	height, width := sys.GetWinsize(int(w.file.Fd()))
	mode := es.mode.Mode()

	var bufNoti, bufLine, bufMode, bufTips, bufListing, buf *buffer
	// butNoti
	if len(es.notifications) > 0 {
		bufNoti = newBuffer(width)
		bufNoti.writes(strings.Join(es.notifications, "\n"), "")
		es.notifications = nil
	}

	// bufLine
	b := newBuffer(width)
	bufLine = b

	b.newlineWhenFull = true

	b.writeStyleds(es.prompt)

	if b.line() == 0 && b.col*2 < b.width {
		b.indent = b.col
	}

	// i keeps track of number of bytes written.
	i := 0

	// nowAt is called at every rune boundary.
	nowAt := func(i int) {
		if mode == modeCompletion && i == es.completion.begin {
			c := es.completion.selectedCandidate()
			b.writes(c.text, styleForCompleted)
		}
		if i == es.dot {
			b.dot = b.cursor()
		}
	}
	nowAt(0)
tokens:
	for _, token := range es.tokens {
		for _, r := range token.Text {
			if mode == modeCompletion &&
				es.completion.begin <= i && i <= es.completion.end {
				// Do nothing. This part is replaced by the completion candidate.
			} else {
				b.write(r, joinStyle(styleForType[token.Type], token.MoreStyle))
			}
			i += utf8.RuneLen(r)

			nowAt(i)
			if mode == modeHistory && i == len(es.hist.prefix) {
				break tokens
			}
		}
	}

	if mode == modeHistory {
		// Put the rest of current history, position the cursor at the
		// end of the line, and finish writing
		h := es.hist
		b.writes(h.line[len(h.prefix):], styleForCompletedHistory)
		b.dot = b.cursor()
	}

	// Write rprompt
	padding := b.width - b.col
	for _, s := range es.rprompt {
		padding -= util.Wcswidth(s.text)
	}
	if padding >= 1 {
		b.newlineWhenFull = false
		b.writePadding(padding, "")
		b.writeStyleds(es.rprompt)
	}

	// bufMode
	bufMode = es.mode.ModeLine(width)

	// bufTips
	// TODO tips is assumed to contain no newlines.
	if len(es.tips) > 0 {
		bufTips = newBuffer(width)
		bufTips.writes(strings.Join(es.tips, "\n"), styleForTip)
	}

	hListing := 0
	// Trim lines and determine the maximum height for bufListing
	// TODO come up with a UI to tell the user that something is not shown.
	switch {
	case height >= lines(bufNoti, bufLine, bufMode, bufTips):
		hListing = height - lines(bufLine, bufMode, bufTips)
	case height >= lines(bufNoti, bufLine, bufTips):
		bufMode = nil
	case height >= lines(bufNoti, bufLine):
		bufMode = nil
		if bufTips != nil {
			bufTips.trimToLines(0, height-lines(bufNoti, bufLine))
		}
	case height >= lines(bufLine):
		bufTips, bufMode = nil, nil
		if bufNoti != nil {
			n := len(bufNoti.cells)
			bufNoti.trimToLines(n-(height-lines(bufLine)), n)
		}
	case height >= 1:
		bufNoti, bufTips, bufMode = nil, nil, nil
		dotLine := bufLine.dot.line
		bufLine.trimToLines(dotLine+1-height, dotLine+1)
	default:
		// Broken terminal. Still try to render one line of bufLine.
		bufNoti, bufTips, bufMode = nil, nil, nil
		dotLine := bufLine.dot.line
		bufLine.trimToLines(dotLine, dotLine+1)
	}

	// bufListing.
	if hListing > 0 {
		if lister, ok := es.mode.(Lister); ok {
			bufListing = lister.List(width, hListing)
		}
		// XXX When in completion mode, we re-render the mode line, since the
		// scrollbar in the mode line depends on completion.lastShown which is
		// only known after the listing has been rendered. Since rendering the
		// scrollbar never adds additional lines to bufMode, we may do this
		// without recalculating the layout.
		if mode == modeCompletion {
			bufMode = es.mode.ModeLine(width)
		}
	}

	if logWriterDetail {
		Logger.Printf("bufLine %d, bufMode %d, bufTips %d, bufListing %d",
			lines(bufLine), lines(bufMode), lines(bufTips), lines(bufListing))
	}

	// Combine buffers (reusing bufLine)
	buf = bufLine
	buf.extend(bufMode, mode == modeLocation || mode == modeHistoryListing ||
		(mode == modeCompletion && es.completion.filtering) ||
		(mode == modeNavigation && es.navigation.filtering))
	buf.extend(bufTips, false)
	buf.extend(bufListing, false)

	return w.commitBuffer(bufNoti, buf, fullRefresh)
}