ronantakizawa/codereview-bench huggingface dataset:
Repository: zyedidia/micro
File: internal/action/infocomplete.go (Go)

Code before:
func colorschemeComplete(input string) (string, []string) {
	var suggestions []string
	files := config.ListRuntimeFiles(config.RTColorscheme)

	for _, f := range files {
		if strings.HasPrefix(f.Name(), input) {
			suggestions = append(suggestions, f.Name())
		}
	}

	var chosen string
	if len(suggestions) == 1 {
		chosen = suggestions[0]
	}

	return chosen, suggestions
}

// filetypeComplete autocompletes filetype
func filetypeComplete(input string) (string, []string) {
	var suggestions []string

	// Since we need to complete the file type instead of the file name
	// we need to parse the RTSyntax* files and search for a matching FileType.
	// The following logic can be simplified to the previos state again,
	// in the moment the file names will represent the file types directly.
	for _, f := range config.ListRealRuntimeFiles(config.RTSyntax) {
		data, err := f.Data()
		if err != nil {
			continue
		}
		header, err := highlight.MakeHeaderYaml(data)
		if err != nil {
			continue
		}
		if strings.HasPrefix(header.FileType, input) {
			suggestions = append(suggestions, header.FileType)
		}
	}
headerLoop:
	for _, f := range config.ListRuntimeFiles(config.RTSyntaxHeader) {
		data, err := f.Data()
		if err != nil {
			continue
		}
		header, err := highlight.MakeHeader(data)
		if err != nil {
			continue
		}
		for _, v := range suggestions {
			if v == header.FileType {

Review comment (suggestion):
The comment is confusing to a casual reader not familiar with the context (what "previous state"? "the file type instead of the file name" - wtf? which file name? etc).

Code after:
func colorschemeComplete(input string) (string, []string) {
	var suggestions []string
	files := config.ListRuntimeFiles(config.RTColorscheme)

	for _, f := range files {
		if strings.HasPrefix(f.Name(), input) {
			suggestions = append(suggestions, f.Name())
		}
	}

	var chosen string
	if len(suggestions) == 1 {
		chosen = suggestions[0]
	}

	return chosen, suggestions
}

// filetypeComplete autocompletes filetype
func filetypeComplete(input string) (string, []string) {
	var suggestions []string

	// We cannot match filetypes just by names of syntax files,
	// since those names may be different from the actual filetype values
	// specified inside syntax files (e.g. "c++" filetype in cpp.yaml).
	// So we need to parse filetype values out of those files.
	for _, f := range config.ListRealRuntimeFiles(config.RTSyntax) {
		data, err := f.Data()
		if err != nil {
			continue
		}
		header, err := highlight.MakeHeaderYaml(data)
		if err != nil {
			continue
		}
		// Prevent duplicated defaults
		if header.FileType == "off" || header.FileType == "unknown" {
			continue
		}
		if strings.HasPrefix(header.FileType, input) {
			suggestions = append(suggestions, header.FileType)
		}
	}
headerLoop:
	for _, f := range config.ListRuntimeFiles(config.RTSyntaxHeader) {
		data, err := f.Data()
		if err != nil {
			continue
		}
		header, err := highlight.MakeHeader(data)
		if err != nil {
Repository: zyedidia/micro
File: pkg/highlight/highlighter.go (Go)

Code before:
		}
		input.Lock()
		line := input.LineBytesNoLock(i)
		highlights := make(LineMatch)

		var match LineMatch
		if i == 0 || input.StateNoLock(i-1) == nil {
			match = h.highlightEmptyRegion(highlights, 0, true, i, line, false)
		} else {
			match = h.highlightRegion(highlights, 0, true, i, line, input.StateNoLock(i-1), false)
		}

		input.SetMatchNoLock(i, match)
		input.Unlock()
	}
}

// ReHighlightStates will scan down from `startline` and set the appropriate end of line state
// for each line until it comes across a line whose state does not change
// returns the number of the final line
func (h *Highlighter) ReHighlightStates(input LineStates, startline int) int {
	// lines := input.LineData()

	h.lastRegion = nil
	if startline > 0 {
		h.lastRegion = input.StateNoLock(startline - 1)
	}
	for i := startline; i < input.LinesNum(); i++ {
		input.Lock()
		line := input.LineBytesNoLock(i)
		// highlights := make(LineMatch)

		// var match LineMatch
		if i == 0 || h.lastRegion == nil {
			h.highlightEmptyRegion(nil, 0, true, i, line, true)
		} else {
			h.highlightRegion(nil, 0, true, i, line, h.lastRegion, true)
		}
		curState := h.lastRegion
		lastState := input.StateNoLock(i)

		input.SetStateNoLock(i, curState)
		input.Unlock()

		if curState == lastState {
			return i
		}
	}

	return input.LinesNum() - 1
}

Review comment (suggestion):
No locking at all here?
Looks like one more argument for restoring per-line locks for `state` and `match`.

Code after:
// This assumes that all the states are set correctly
func (h *Highlighter) HighlightMatches(input LineStates, startline, endline int) {
	for i := startline; i <= endline; i++ {
		input.Lock()
		if i >= input.LinesNum() {
			input.Unlock()
			break
		}

		line := input.LineBytes(i)
		highlights := make(LineMatch)

		var match LineMatch
		if i == 0 || input.State(i-1) == nil {
			match = h.highlightEmptyRegion(highlights, 0, true, i, line, false)
		} else {
			match = h.highlightRegion(highlights, 0, true, i, line, input.State(i-1), false)
		}

		input.SetMatch(i, match)
		input.Unlock()
	}
}

// ReHighlightStates will scan down from `startline` and set the appropriate end of line state
// for each line until it comes across a line whose state does not change
// returns the number of the final line
func (h *Highlighter) ReHighlightStates(input LineStates, startline int) int {
	// lines := input.LineData()

	h.lastRegion = nil
	if startline > 0 {
		input.Lock()
		h.lastRegion = input.State(startline - 1)
		input.Unlock()
	}
	for i := startline; ; i++ {
		input.Lock()
		if i >= input.LinesNum() {
			input.Unlock()
			break
		}

		line := input.LineBytes(i)
		// highlights := make(LineMatch)

		// var match LineMatch
		if i == 0 || h.lastRegion == nil {
			h.highlightEmptyRegion(nil, 0, true, i, line, true)
		} else {
			h.highlightRegion(nil, 0, true, i, line, h.lastRegion, true)
Repository: zyedidia/micro
File: internal/buffer/line_array.go (Go)

Code before:
}

// Start returns the start of the buffer
func (la *LineArray) Start() Loc {
	return Loc{0, 0}
}

// End returns the location of the last character in the buffer
func (la *LineArray) End() Loc {
	numlines := len(la.lines)
	return Loc{util.CharacterCount(la.lines[numlines-1].data), numlines - 1}
}

// LineBytes returns line n as an array of bytes
func (la *LineArray) LineBytes(lineN int) []byte {
	if lineN >= len(la.lines) || lineN < 0 {
		return []byte{}
	}
	return la.lines[lineN].data
}

// State gets the highlight state for the given line number
func (la *LineArray) State(lineN int) highlight.State {
	la.lines[lineN].lock.Lock()
	defer la.lines[lineN].lock.Unlock()
	if lineN >= len(la.lines) || lineN < 0 {
		return nil
	}
	return la.lines[lineN].state
}

// SetState sets the highlight state at the given line number
func (la *LineArray) SetState(lineN int, s highlight.State) {
	la.lines[lineN].lock.Lock()
	defer la.lines[lineN].lock.Unlock()
	if lineN >= len(la.lines) || lineN < 0 {
		return
	}
	la.lines[lineN].state = s
}

// SetMatch sets the match at the given line number
func (la *LineArray) SetMatch(lineN int, m highlight.LineMatch) {
	la.lines[lineN].lock.Lock()
	defer la.lines[lineN].lock.Unlock()
	if lineN >= len(la.lines) || lineN < 0 {
		return
	}
	la.lines[lineN].match = m
}


Review comment (bug):
These should be before trying to lock `la.lines[lineN].lock`.

Because of this, the test actually still fails: I've run the test 100 times, 99 passed, but 1 failed, with `index out of range` in `SetState()`.

Code after:
func (la *LineArray) LinesNum() int {
	return len(la.lines)
}

// Start returns the start of the buffer
func (la *LineArray) Start() Loc {
	return Loc{0, 0}
}

// End returns the location of the last character in the buffer
func (la *LineArray) End() Loc {
	numlines := len(la.lines)
	return Loc{util.CharacterCount(la.lines[numlines-1].data), numlines - 1}
}

// LineBytes returns line n as an array of bytes
func (la *LineArray) LineBytes(lineN int) []byte {
	if lineN >= len(la.lines) || lineN < 0 {
		return []byte{}
	}
	return la.lines[lineN].data
}

// State gets the highlight state for the given line number
func (la *LineArray) State(lineN int) highlight.State {
	la.lines[lineN].lock.Lock()
	defer la.lines[lineN].lock.Unlock()
	return la.lines[lineN].state
}

// SetState sets the highlight state at the given line number
func (la *LineArray) SetState(lineN int, s highlight.State) {
	la.lines[lineN].lock.Lock()
	defer la.lines[lineN].lock.Unlock()
	la.lines[lineN].state = s
}

// SetMatch sets the match at the given line number
func (la *LineArray) SetMatch(lineN int, m highlight.LineMatch) {
	la.lines[lineN].lock.Lock()
	defer la.lines[lineN].lock.Unlock()
	la.lines[lineN].match = m
}

// Match retrieves the match for the given line number
func (la *LineArray) Match(lineN int) highlight.LineMatch {
	la.lines[lineN].lock.Lock()
	defer la.lines[lineN].lock.Unlock()
	return la.lines[lineN].match
}
Repository: zyedidia/micro
File: internal/buffer/buffer.go (Go)

Code before:
				if r == braceType[1] {
					i++
				} else if r == braceType[0] {
					i--
					if i == 0 {
						if startChar == braceType[1] {
							return Loc{x, y}, false, true
						}
						return Loc{x, y}, true, true
					}
				}
			}
		}
	}
	return start, true, false
}

// Retab changes all tabs to spaces or vice versa
func (b *Buffer) Retab() {
	toSpaces := b.Settings["tabstospaces"].(bool)
	tabsize := util.IntOpt(b.Settings["tabsize"])
	dirty := false

	for i := 0; ; i++ {
		b.Lock()
		if i >= b.LinesNum() {
			b.Unlock()
			break
		}

		l := b.LineBytes(i)
		ws := util.GetLeadingWhitespace(l)
		if len(ws) != 0 {
			if toSpaces {
				ws = bytes.ReplaceAll(ws, []byte{'\t'}, bytes.Repeat([]byte{' '}, tabsize))
			} else {
				ws = bytes.ReplaceAll(ws, bytes.Repeat([]byte{' '}, tabsize), []byte{'\t'})
			}
		}

		l = bytes.TrimLeft(l, " \t")
		b.lines[i].data = append(ws, l...)
		b.Unlock()

		b.MarkModified(i, i)
		dirty = true
	}

	b.isModified = dirty
}


Review comment (bug):
I don't think this check is needed here. The number of lines is only changed by the main goroutine, and this code is only running in the main goroutine.

I think we can just add `b.Lock()` and `b.Unlock()` around `b.lines[i].data = append(ws, l...)`.

BTW, this raw access to `data` from here looks ugly, and we can instead just replace it with `b.insert(Loc{0, i}, l)` right away. But that is less efficient than a simple `append()` (just as a consequence of the unfortunate inefficient implementation of lines as simple byte arrays), and moreover, even `insert()` is not quite the right thing to do here anyway. This code is actually buggy (completely regardless of our race issue): it bypasses the `EventHandler`, so in particular we cannot undo retab (try it).

So, unless we are gonna address this separate bug right away as well, perhaps let's just keep this "ugly" `append()` and just add locking around it for now.

Code after:
				if r == braceType[1] {
					i++
				} else if r == braceType[0] {
					i--
					if i == 0 {
						if startChar == braceType[1] {
							return Loc{x, y}, false, true
						}
						return Loc{x, y}, true, true
					}
				}
			}
		}
	}
	return start, true, false
}

// Retab changes all tabs to spaces or vice versa
func (b *Buffer) Retab() {
	toSpaces := b.Settings["tabstospaces"].(bool)
	tabsize := util.IntOpt(b.Settings["tabsize"])
	dirty := false

	for i := 0; i < b.LinesNum(); i++ {
		l := b.LineBytes(i)

		ws := util.GetLeadingWhitespace(l)
		if len(ws) != 0 {
			if toSpaces {
				ws = bytes.ReplaceAll(ws, []byte{'\t'}, bytes.Repeat([]byte{' '}, tabsize))
			} else {
				ws = bytes.ReplaceAll(ws, bytes.Repeat([]byte{' '}, tabsize), []byte{'\t'})
			}
		}

		l = bytes.TrimLeft(l, " \t")

		b.Lock()
		b.lines[i].data = append(ws, l...)
		b.Unlock()

		b.MarkModified(i, i)
		dirty = true
	}

	b.isModified = dirty
}

// ParseCursorLocation turns a cursor location like 10:5 (LINE:COL)
// into a loc
func ParseCursorLocation(cursorPositions []string) (Loc, error) {
Repository: zyedidia/micro
File: pkg/highlight/parser.go (Go)

Code before:
	return ru, nil
}

func parseRegion(group string, regionInfo map[interface{}]interface{}, prevRegion *region) (r *region, err error) {
	defer func() {
		if r := recover(); r != nil {
			var ok bool
			err, ok = r.(error)
			if !ok {
				err = fmt.Errorf("pkg: %v", r)
			}
		}
	}()

	r = new(region)
	if _, ok := Groups[group]; !ok {
		numGroups++
		Groups[group] = numGroups
	}
	groupNum := Groups[group]
	r.group = groupNum
	r.parent = prevRegion

	// start is mandatory
	if _, ok := regionInfo["start"]; ok {
		start := regionInfo["start"].(string)
		if start == "" {
			return nil, fmt.Errorf("Empty start in %s", group)
		}

		r.start, err = regexp.Compile(start)
		if err != nil {
			return nil, err
		}
	} else {
		return nil, fmt.Errorf("Missing start in %s", group)
	}

	// end is mandatory
	if _, ok := regionInfo["end"]; ok {
		end := regionInfo["end"].(string)
		if end == "" {
			return nil, fmt.Errorf("Empty end in %s", group)
		}

		r.end, err = regexp.Compile(end)
		if err != nil {
			return nil, err
		}
	} else {
		return nil, fmt.Errorf("Missing end in %s", group)

Review comment (suggestion):
I think we could avoid doing the map lookup twice
```suggestion
	if start, ok := regionInfo["start"]; ok {
		start := start.(string)
```

Code after:
	return ru, nil
}

func parseRegion(group string, regionInfo map[interface{}]interface{}, prevRegion *region) (r *region, err error) {
	defer func() {
		if r := recover(); r != nil {
			var ok bool
			err, ok = r.(error)
			if !ok {
				err = fmt.Errorf("pkg: %v", r)
			}
		}
	}()

	r = new(region)
	if _, ok := Groups[group]; !ok {
		numGroups++
		Groups[group] = numGroups
	}
	groupNum := Groups[group]
	r.group = groupNum
	r.parent = prevRegion

	// start is mandatory
	if start, ok := regionInfo["start"]; ok {
		start := start.(string)
		if start == "" {
			return nil, fmt.Errorf("Empty start in %s", group)
		}

		r.start, err = regexp.Compile(start)
		if err != nil {
			return nil, err
		}
	} else {
		return nil, fmt.Errorf("Missing start in %s", group)
	}

	// end is mandatory
	if end, ok := regionInfo["end"]; ok {
		end := end.(string)
		if end == "" {
			return nil, fmt.Errorf("Empty end in %s", group)
		}

		r.end, err = regexp.Compile(end)
		if err != nil {
			return nil, err
		}
	} else {
		return nil, fmt.Errorf("Missing end in %s", group)
Repository: zyedidia/micro
File: internal/buffer/buffer.go (Go)

Code before:
			size += n
			n, e = h.Write(l.data)
			if e != nil {
				return e
			}
			size += n
		}
	}

	if size > LargeFileThreshold {
		return ErrFileTooLarge
	}

	h.Sum((*out)[:0])
	return nil
}

func parseDefFromFile(f config.RuntimeFile, header *highlight.Header) *highlight.Def {
	data, err := f.Data()
	if err != nil {
		screen.TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error())
		return nil
	}

	if header == nil {
		// TODO: use faster highlight.MakeHeader() when possible
		header, err = highlight.MakeHeaderYaml(data)
		if err != nil {
			screen.TermMessage("Error parsing header for syntax file " + f.Name() + ": " + err.Error())
			return nil
		}
	}

	file, err := highlight.ParseFile(data)
	if err != nil {
		screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
		return nil
	}

	syndef, err := highlight.ParseDef(file, header)
	if err != nil {
		screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
		return nil
	}

	return syndef
}

// findRealRuntimeSyntaxDef finds a specific syntax definition
// in the user's custom syntax files
func findRealRuntimeSyntaxDef(name string, header *highlight.Header) *highlight.Def {

Review comment (bug):
But `highlight.MakeHeader()` can only be used, when we iterate over `config.RTSyntaxHeader`, otherwise we can't guarantee, that we access the correct lines or am I wrong?
So in case of `findRuntimeSyntaxDef()` we could iterate over the `config.RTSyntaxHeader` first, to create the `header` structure and then iterate over the normal `config.RTSyntax` and passing the already created `header` to `parseDefFromFile()`.

Code after:
			size += n
			n, e = h.Write(l.data)
			if e != nil {
				return e
			}
			size += n
		}
	}

	if size > LargeFileThreshold {
		return ErrFileTooLarge
	}

	h.Sum((*out)[:0])
	return nil
}

func parseDefFromFile(f config.RuntimeFile, header *highlight.Header) *highlight.Def {
	data, err := f.Data()
	if err != nil {
		screen.TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error())
		return nil
	}

	if header == nil {
		header, err = highlight.MakeHeaderYaml(data)
		if err != nil {
			screen.TermMessage("Error parsing header for syntax file " + f.Name() + ": " + err.Error())
			return nil
		}
	}

	file, err := highlight.ParseFile(data)
	if err != nil {
		screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
		return nil
	}

	syndef, err := highlight.ParseDef(file, header)
	if err != nil {
		screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
		return nil
	}

	return syndef
}

// findRealRuntimeSyntaxDef finds a specific syntax definition
// in the user's custom syntax files
func findRealRuntimeSyntaxDef(name string, header *highlight.Header) *highlight.Def {
	for _, f := range config.ListRealRuntimeFiles(config.RTSyntax) {
Repository: zyedidia/micro
File: internal/buffer/buffer.go (Go)

Code before:
		for _, f := range config.ListRuntimeFiles(config.RTSyntax) {
			data, err := f.Data()
			if err != nil {
				screen.TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error())
				continue
			}

			header, err := highlight.MakeHeaderYaml(data)
			if err != nil {
				screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
				continue
			}

			for _, i := range includes {
				if header.FileType == i {
					file, err := highlight.ParseFile(data)
					if err != nil {
						screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
						continue
					}
					files = append(files, file)
					break
				}
			}
			// TODO: fix this mess. For each include, we should include
			// exactly one file: either a built-in one or a user's custom one
			if len(files) >= len(includes) {
				break
			}
		}

		highlight.ResolveIncludes(b.SyntaxDef, files)
	}

	if b.SyntaxDef != nil {
		b.Highlighter = highlight.NewHighlighter(b.SyntaxDef)
		if b.Settings["syntax"].(bool) {
			go func() {
				b.Highlighter.HighlightStates(b)
				b.Highlighter.HighlightMatches(b, 0, b.End().Y)
				screen.Redraw()
			}()
		}
	}
}

// ClearMatches clears all of the syntax highlighting for the buffer
func (b *Buffer) ClearMatches() {
	for i := range b.lines {
		b.SetMatch(i, nil)
		b.SetState(i, nil)

Review comment (bug):
So, I was wrong about this one too: since #3066 we already ensure that exactly one file will be included.

This `len(files) >= len(includes)` check is now not strictly necessary, but it provides a simple optimization (to exit the loop earlier), so we can keep it.

Code after:
								break matchLoop
							}
						}
					}
				}
			}
			if length == 1 || !signatureMatch {
				syntaxFile = matches[0].fileName
				if matches[0].syntaxDef != nil {
					b.SyntaxDef = matches[0].syntaxDef
					foundDef = true
				}
				header = matches[0].header
			}
		}
	}

	if syntaxFile != "" && !foundDef {
		// we found a syntax file using a syntax header file
		b.SyntaxDef = findRuntimeSyntaxDef(syntaxFile, header)
	}

	if b.SyntaxDef != nil {
		b.Settings["filetype"] = b.SyntaxDef.FileType
	} else {
		// search for the default file in the user's custom syntax files
		b.SyntaxDef = findRealRuntimeSyntaxDef("default", nil)
		if b.SyntaxDef == nil {
			// search for the default file in the built-in syntax files
			b.SyntaxDef = findRuntimeSyntaxDef("default", nil)
		}
	}

	if b.SyntaxDef != nil {
		resolveIncludes(b.SyntaxDef)
	}

	if b.SyntaxDef != nil {
		b.Highlighter = highlight.NewHighlighter(b.SyntaxDef)
		if b.Settings["syntax"].(bool) {
			go func() {
				b.Highlighter.HighlightStates(b)
				b.Highlighter.HighlightMatches(b, 0, b.End().Y)
				screen.Redraw()
			}()
		}
	}
}

// ClearMatches clears all of the syntax highlighting for the buffer
func (b *Buffer) ClearMatches() {
Repository: zyedidia/micro
File: internal/action/bufpane.go (Go)

Code before:
			afn = f
			names = append(names, a)
		} else {
			screen.TermMessage("Error in bindings: action", a, "does not exist")
			continue
		}
		actionfns = append(actionfns, afn)
	}
	bufAction := func(h *BufPane, te *tcell.EventMouse) bool {
		cursors := h.Buf.GetCursors()
		success := true
		for i, a := range actionfns {
			innerSuccess := true
			for j, c := range cursors {
				if c == nil {
					continue
				}
				h.Buf.SetCurCursor(c.Num)
				h.Cursor = c
				if i == 0 || (success && types[i-1] == '&') || (!success && types[i-1] == '|') || (types[i-1] == ',') {
					innerSuccess = innerSuccess && h.execAction(a, names[i], j, te)
				} else {
					break
				}
			}
			// if the action changed the current BufPane to a BufPane again, update the reference
			bp, ok := MainTab().CurPane().(*BufPane)
			if !ok {
				return innerSuccess
			}
			h = bp
			success = innerSuccess
		}
		return true
	}

	switch e := k.(type) {
	case KeyEvent, KeySequenceEvent, RawEvent:
		BufBindings.RegisterKeyBinding(e, BufKeyActionGeneral(func(h *BufPane) bool {
			return bufAction(h, nil)
		}))
	case MouseEvent:
		BufBindings.RegisterMouseBinding(e, BufMouseActionGeneral(bufAction))
	}
}

// BufUnmap unmaps a key or mouse event from any action
func BufUnmap(k Event) {
	// TODO
	// delete(BufKeyBindings, k)
	//

Review comment (suggestion):
Doesn't sound quite clear to me. I'd leave the existing comment as is, and add a comment inside the `if !ok`, e.g.: "if the new pane is not a BufPane, stop".

Code after:
			afn = f
			names = append(names, a)
		} else {
			screen.TermMessage("Error in bindings: action", a, "does not exist")
			continue
		}
		actionfns = append(actionfns, afn)
	}
	bufAction := func(h *BufPane, te *tcell.EventMouse) bool {
		cursors := h.Buf.GetCursors()
		success := true
		for i, a := range actionfns {
			innerSuccess := true
			for j, c := range cursors {
				if c == nil {
					continue
				}
				h.Buf.SetCurCursor(c.Num)
				h.Cursor = c
				if i == 0 || (success && types[i-1] == '&') || (!success && types[i-1] == '|') || (types[i-1] == ',') {
					innerSuccess = innerSuccess && h.execAction(a, names[i], j, te)
				} else {
					break
				}
			}
			// if the action changed the current pane, update the reference
			h = MainTab().CurPane()
			success = innerSuccess
			if h == nil {
				// stop, in case the current pane is not a BufPane
				break
			}
		}
		return true
	}

	switch e := k.(type) {
	case KeyEvent, KeySequenceEvent, RawEvent:
		BufBindings.RegisterKeyBinding(e, BufKeyActionGeneral(func(h *BufPane) bool {
			return bufAction(h, nil)
		}))
	case MouseEvent:
		BufBindings.RegisterMouseBinding(e, BufMouseActionGeneral(bufAction))
	}
}

// BufUnmap unmaps a key or mouse event from any action
func BufUnmap(k Event) {
	// TODO
	// delete(BufKeyBindings, k)
	//