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

Code before:

	return l, charpos, padMode
}

func (b *Buffer) findNext(r *regexp.Regexp, start, end Loc, down bool) ([2]Loc, bool) {
	lastcn := util.CharacterCount(b.LineBytes(b.LinesNum() - 1))
	if start.Y > b.LinesNum()-1 {
		start.X = lastcn - 1
	}
	if end.Y > b.LinesNum()-1 {
		end.X = lastcn
	}
	start.Y = util.Clamp(start.Y, 0, b.LinesNum()-1)
	end.Y = util.Clamp(end.Y, 0, b.LinesNum()-1)

	if start.GreaterThan(end) {
		start, end = end, start
	}

	rPadded := padRegexp(r)

	var i0, i1, is int
	if down {
		i0, i1, is = start.Y, end.Y, 1
	} else {
		i0, i1, is = end.Y, start.Y, -1
	}

	for i := i0; is*i <= is*i1; i += is {
		l, charpos, padMode := findLineParams(b, start, end, i)

		var match []int
		if down {
			match = rPadded[padMode].FindIndex(l)
		} else {
			allMatches := rPadded[padMode].FindAllIndex(l, -1)
			if allMatches != nil {
				match = allMatches[len(allMatches)-1]
			}
		}

		if match != nil {
			start := Loc{charpos + util.RunePos(l, match[0]), i}
			if padMode&padStart != 0 {
				start = start.Move(1, b)
			}
			end := Loc{charpos + util.RunePos(l, match[1]), i}
			if padMode&padEnd != 0 {
				end = end.Move(-1, b)
			}
			return [2]Loc{start, end}, true

Review comment (refactor):
Argh, this looks ugly... I missed the fact that the direction of the loop also differs, so deduplication is not quite as trivial as I thought.

I'd suggest to drop this for now, leaving this refactoring effort to another PR (if at all).

Code after:
		r = regexp.MustCompile(".(?:" + r.String() + ").")
	}

	return l, charpos, padMode, r
}

func (b *Buffer) findDown(r *regexp.Regexp, start, end Loc) ([2]Loc, bool) {
	lastcn := util.CharacterCount(b.LineBytes(b.LinesNum() - 1))
	if start.Y > b.LinesNum()-1 {
		start.X = lastcn - 1
	}
	if end.Y > b.LinesNum()-1 {
		end.X = lastcn
	}
	start.Y = util.Clamp(start.Y, 0, b.LinesNum()-1)
	end.Y = util.Clamp(end.Y, 0, b.LinesNum()-1)

	if start.GreaterThan(end) {
		start, end = end, start
	}

	for i := start.Y; i <= end.Y; i++ {
		l, charpos, padMode, rPadded := findLineParams(b, start, end, i, r)

		match := rPadded.FindIndex(l)

		if match != nil {
			if padMode&padStart != 0 {
				_, size := utf8.DecodeRune(l[match[0]:])
				match[0] += size
			}
			if padMode&padEnd != 0 {
				_, size := utf8.DecodeLastRune(l[:match[1]])
				match[1] -= size
			}
			start := Loc{charpos + util.RunePos(l, match[0]), i}
			end := Loc{charpos + util.RunePos(l, match[1]), i}
			return [2]Loc{start, end}, true
		}
	}
	return [2]Loc{}, false
}

func (b *Buffer) findUp(r *regexp.Regexp, start, end Loc) ([2]Loc, bool) {
	lastcn := util.CharacterCount(b.LineBytes(b.LinesNum() - 1))
	if start.Y > b.LinesNum()-1 {
		start.X = lastcn - 1
	}
	if end.Y > b.LinesNum()-1 {
		end.X = lastcn
	}
Repository: zyedidia/micro
File: internal/util/util.go (Go)

Code before:
func IsWhitespace(c rune) bool {
	return unicode.IsSpace(c)
}

// IsBytesWhitespace returns true if the given bytes are all whitespace
func IsBytesWhitespace(b []byte) bool {
	for _, c := range b {
		if !IsWhitespace(rune(c)) {
			return false
		}
	}
	return true
}

// RunePos returns the rune index of a given byte index
// Make sure the byte index is not between code points
func RunePos(b []byte, i int) int {
	return CharacterCount(b[:i])
}

// IndexAnyUnquoted returns the first position is s of a character from chars.
// Escaped (with '\\') and quoted (with single or double quotes) characters
// are ignored. Returns -1 if not successful
func IndexAnyUnquoted(s, chars string) int {
	var q byte = 0
	for i := 0; i < len(s); i++ {
		if (q == 0 || q == '"') && s[i] == '\\' {
			i++
		} else if s[i] == q {
			q = 0
		} else if q == 0 && (s[i] == '\'' || s[i] == '"') {
			q = s[i]
		} else if q == 0 && strings.IndexByte(chars, s[i]) >= 0 {
			return i
		}
	}
	return -1
}

// MakeRelative will attempt to make a relative path between path and base
func MakeRelative(path, base string) (string, error) {
	if len(path) > 0 {
		rel, err := filepath.Rel(base, path)
		if err != nil {
			return path, err
		}
		return rel, nil
	}
	return path, nil
}


Review comment (suggestion):
This iterates over raw bytes, not Unicode runes. Although the end result is probably gonna be correct (as long as `chars` includes ASCII characters only) thanks to the fact that UTF-8 is backward compatible with ASCII, it's cleaner to iterate over runes:

```
for _, c := range s {
```

(See e.g. `util.IsSpaces()`, `util.CharacterCountInString()` etc.)

Code after:
func IsWhitespace(c rune) bool {
	return unicode.IsSpace(c)
}

// IsBytesWhitespace returns true if the given bytes are all whitespace
func IsBytesWhitespace(b []byte) bool {
	for _, c := range b {
		if !IsWhitespace(rune(c)) {
			return false
		}
	}
	return true
}

// RunePos returns the rune index of a given byte index
// Make sure the byte index is not between code points
func RunePos(b []byte, i int) int {
	return CharacterCount(b[:i])
}

// IndexAnyUnquoted returns the first position in s of a character from chars.
// Escaped (with backslash) and quoted (with single or double quotes) characters
// are ignored. Returns -1 if not successful
func IndexAnyUnquoted(s, chars string) int {
	var e bool
	var q rune
	for i, r := range s {
		if e {
			e = false
		} else if (q == 0 || q == '"') && r == '\\' {
			e = true
		} else if r == q {
			q = 0
		} else if q == 0 && (r == '\'' || r == '"') {
			q = r
		} else if q == 0 && strings.IndexRune(chars, r) >= 0 {
			return i
		}
	}
	return -1
}

// MakeRelative will attempt to make a relative path between path and base
func MakeRelative(path, base string) (string, error) {
	if len(path) > 0 {
		rel, err := filepath.Rel(base, path)
		if err != nil {
			return path, err
		}
		return rel, nil
	}
Repository: zyedidia/micro
File: internal/buffer/settings.go (Go)

Code before:
package buffer

import (
	"crypto/md5"
	"reflect"

	"github.com/zyedidia/micro/v2/internal/config"
	ulua "github.com/zyedidia/micro/v2/internal/lua"
	"github.com/zyedidia/micro/v2/internal/screen"
	luar "layeh.com/gopher-luar"
)

func (b *Buffer) ReloadSettings(reloadFiletype bool) {
	settings := config.ParsedSettings()

	_, local := b.LocalSettings["filetype"]
	_, volatile := config.VolatileSettings["filetype"]
	if reloadFiletype && !local && !volatile {
		// need to update filetype before updating other settings based on it
		oldValue := b.Settings["filetype"]
		b.Settings["filetype"] = "unknown"
		if v, ok := settings["filetype"]; ok {
			b.Settings["filetype"] = v
		}
		curValue := b.Settings["filetype"]
		if reflect.DeepEqual(oldValue, curValue) {
			b.doCallbacks("filetype", oldValue, curValue)
		}
	}

	// update syntax rules, which will also update filetype if needed
	b.UpdateRules()
	settings["filetype"] = b.Settings["filetype"]

	config.InitLocalSettings(settings, b.Path)
	for k, v := range config.DefaultCommonSettings() {
		if k == "filetype" {
			// prevent recursion
			continue
		}
		if _, ok := config.VolatileSettings[k]; ok {
			// reload should not override volatile settings
			continue
		}
		if _, ok := b.LocalSettings[k]; ok {
			// reload should not override local settings
			continue
		}
		if _, ok := settings[k]; ok {
			b.DoSetOptionNative(k, settings[k])
		} else {

Review comment (bug):
The callback seems to be called when the value is the same.
This pull request might be merged, but I will test later if there's no other bugs with the callback here.

Code after:
package buffer

import (
	"crypto/md5"
	"reflect"

	"github.com/zyedidia/micro/v2/internal/config"
	ulua "github.com/zyedidia/micro/v2/internal/lua"
	"github.com/zyedidia/micro/v2/internal/screen"
	luar "layeh.com/gopher-luar"
)

func (b *Buffer) ReloadSettings(reloadFiletype bool) {
	settings := config.ParsedSettings()
	config.UpdatePathGlobLocals(settings, b.AbsPath)

	oldFiletype := b.Settings["filetype"].(string)

	_, local := b.LocalSettings["filetype"]
	_, volatile := config.VolatileSettings["filetype"]
	if reloadFiletype && !local && !volatile {
		// need to update filetype before updating other settings based on it
		b.Settings["filetype"] = "unknown"
		if v, ok := settings["filetype"]; ok {
			b.Settings["filetype"] = v
		}
	}

	// update syntax rules, which will also update filetype if needed
	b.UpdateRules()

	curFiletype := b.Settings["filetype"].(string)
	if oldFiletype != curFiletype {
		b.doCallbacks("filetype", oldFiletype, curFiletype)
	}

	config.UpdateFileTypeLocals(settings, curFiletype)

	for k, v := range config.DefaultCommonSettings() {
		if k == "filetype" {
			// prevent recursion
			continue
		}
		if _, ok := config.VolatileSettings[k]; ok {
			// reload should not override volatile settings
			continue
		}
		if _, ok := b.LocalSettings[k]; ok {
			// reload should not override local settings
			continue
		}
Repository: zyedidia/micro
File: internal/buffer/settings.go (Go)

Code before:
package buffer

import (
	"crypto/md5"
	"reflect"

	"github.com/zyedidia/micro/v2/internal/config"
	ulua "github.com/zyedidia/micro/v2/internal/lua"
	"github.com/zyedidia/micro/v2/internal/screen"
	luar "layeh.com/gopher-luar"
)

func (b *Buffer) ReloadSettings(reloadFiletype bool) {
	settings := config.ParsedSettings()

	_, local := b.LocalSettings["filetype"]
	_, volatile := config.VolatileSettings["filetype"]
	if reloadFiletype && !local && !volatile {
		// need to update filetype before updating other settings based on it
		b.Settings["filetype"] = "unknown"
		settings["filetype"] = "unknown"
		config.InitLocalSettings(settings, b.Path)
		if v, ok := settings["filetype"]; ok {
			b.Settings["filetype"] = v
		}
	}

	// update syntax rules, which will also update filetype if needed
	b.UpdateRules()
	settings["filetype"] = b.Settings["filetype"]

	config.InitLocalSettings(settings, b.Path)
	for k, v := range config.DefaultCommonSettings() {
		if k == "filetype" {
			// prevent recursion
			continue
		}
		if _, ok := config.VolatileSettings[k]; ok {
			// reload should not override volatile settings
			continue
		}
		if _, ok := b.LocalSettings[k]; ok {
			// reload should not override local settings
			continue
		}
		if _, ok := settings[k]; ok {

Review comment (bug):
Is this line a leftover you forgot to remove? AFAICS it is buggy in several ways, and furthermore completely unneeded, since https://github.com/zyedidia/micro/pull/3662/commits/b40854a860e6abae69dc64e9eb61f4e59b7ed6c6? (Which btw means that https://github.com/zyedidia/micro/pull/3662/commits/b40854a860e6abae69dc64e9eb61f4e59b7ed6c6 should come before this commit, not after it.)

Also about the commit message: "buffer: `Fix InitLocalSettings()` on reload/changed `filetypes`" - AFAICS that is not what this commit actually does? Like, at all?

Code after:
package buffer

import (
	"crypto/md5"
	"reflect"

	"github.com/zyedidia/micro/v2/internal/config"
	ulua "github.com/zyedidia/micro/v2/internal/lua"
	"github.com/zyedidia/micro/v2/internal/screen"
	luar "layeh.com/gopher-luar"
)

func (b *Buffer) ReloadSettings(reloadFiletype bool) {
	settings := config.ParsedSettings()
	config.UpdatePathGlobLocals(settings, b.AbsPath)

	oldFiletype := b.Settings["filetype"].(string)

	_, local := b.LocalSettings["filetype"]
	_, volatile := config.VolatileSettings["filetype"]
	if reloadFiletype && !local && !volatile {
		// need to update filetype before updating other settings based on it
		b.Settings["filetype"] = "unknown"
		if v, ok := settings["filetype"]; ok {
			b.Settings["filetype"] = v
		}
	}

	// update syntax rules, which will also update filetype if needed
	b.UpdateRules()

	curFiletype := b.Settings["filetype"].(string)
	if oldFiletype != curFiletype {
		b.doCallbacks("filetype", oldFiletype, curFiletype)
	}

	config.UpdateFileTypeLocals(settings, curFiletype)

	for k, v := range config.DefaultCommonSettings() {
		if k == "filetype" {
			// prevent recursion
			continue
		}
		if _, ok := config.VolatileSettings[k]; ok {
			// reload should not override volatile settings
			continue
Repository: zyedidia/micro
File: internal/buffer/settings.go (Go)

Code before:
package buffer

import (
	"crypto/md5"
	"reflect"

	"github.com/zyedidia/micro/v2/internal/config"
	ulua "github.com/zyedidia/micro/v2/internal/lua"
	"github.com/zyedidia/micro/v2/internal/screen"
	luar "layeh.com/gopher-luar"
)

func (b *Buffer) ReloadSettings(reloadFiletype bool) {
	settings := config.ParsedSettings()

	_, local := b.LocalSettings["filetype"]
	_, volatile := config.VolatileSettings["filetype"]
	if reloadFiletype && !local && !volatile {
		// need to update filetype before updating other settings based on it
		b.Settings["filetype"] = "unknown"
		settings["filetype"] = "unknown"
		config.InitLocalSettings(settings, b.Path)
		if v, ok := settings["filetype"]; ok {
			b.Settings["filetype"] = v
		}
	}

	// update syntax rules, which will also update filetype if needed
	b.UpdateRules()
	settings["filetype"] = b.Settings["filetype"]

	config.InitLocalSettings(settings, b.Path)
	for k, v := range config.DefaultCommonSettings() {
		if k == "filetype" {
			// prevent recursion
			continue
		}
		if _, ok := config.VolatileSettings[k]; ok {
			// reload should not override volatile settings
			continue
		}
		if _, ok := b.LocalSettings[k]; ok {
			// reload should not override local settings
			continue
		}
		if _, ok := settings[k]; ok {
			b.DoSetOptionNative(k, settings[k])

Review comment (refactor):
BTW this duplicated calling of `InitLocalSettings()` looks ugly and confusing. I think we should at least add a comment explaining why it is here. But it seems cleaner to split `InitLocalSettings()` into 2 separate functions instead: for handling `ft:` settings and for handling glob settings?

Code after:
package buffer

import (
	"crypto/md5"
	"reflect"

	"github.com/zyedidia/micro/v2/internal/config"
	ulua "github.com/zyedidia/micro/v2/internal/lua"
	"github.com/zyedidia/micro/v2/internal/screen"
	luar "layeh.com/gopher-luar"
)

func (b *Buffer) ReloadSettings(reloadFiletype bool) {
	settings := config.ParsedSettings()
	config.UpdatePathGlobLocals(settings, b.AbsPath)

	oldFiletype := b.Settings["filetype"].(string)

	_, local := b.LocalSettings["filetype"]
	_, volatile := config.VolatileSettings["filetype"]
	if reloadFiletype && !local && !volatile {
		// need to update filetype before updating other settings based on it
		b.Settings["filetype"] = "unknown"
		if v, ok := settings["filetype"]; ok {
			b.Settings["filetype"] = v
		}
	}

	// update syntax rules, which will also update filetype if needed
	b.UpdateRules()

	curFiletype := b.Settings["filetype"].(string)
	if oldFiletype != curFiletype {
		b.doCallbacks("filetype", oldFiletype, curFiletype)
	}

	config.UpdateFileTypeLocals(settings, curFiletype)

	for k, v := range config.DefaultCommonSettings() {
		if k == "filetype" {
			// prevent recursion
			continue
		}
		if _, ok := config.VolatileSettings[k]; ok {
			// reload should not override volatile settings
			continue
		}
Repository: zyedidia/micro
File: internal/buffer/settings.go (Go)

Code before:
package buffer

import (
	"crypto/md5"
	"reflect"

	"github.com/zyedidia/micro/v2/internal/config"
	ulua "github.com/zyedidia/micro/v2/internal/lua"
	"github.com/zyedidia/micro/v2/internal/screen"
	luar "layeh.com/gopher-luar"
)

func (b *Buffer) ReloadSettings(reloadFiletype bool) {
	settings := config.ParsedSettings()
	oldFileType := b.Settings["filetype"]

	_, local := b.LocalSettings["filetype"]
	_, volatile := config.VolatileSettings["filetype"]
	if reloadFiletype && !local && !volatile {
		// need to update filetype before updating other settings based on it
		b.Settings["filetype"] = "unknown"
		config.ApplyPathGlobLocals(settings, b.Path)
		if v, ok := settings["filetype"]; ok {
			b.Settings["filetype"] = v
		}
	}

	// update syntax rules, which will also update filetype if needed
	b.UpdateRules()

	curFileType := b.Settings["filetype"]
	if oldFileType != curFileType {
		b.doCallbacks("filetype", oldFileType, curFileType)
	}

	settings["filetype"] = curFileType
	config.ApplyFileTypeLocals(settings)

	for k, v := range config.DefaultCommonSettings() {
		if k == "filetype" {

Review comment (suggestion):
`oldFiletype`? In line with `reloadFiletype`, etc.
(Yeah, it was me who suggested `oldFileType`, sorry.)

Code after:
package buffer

import (
	"crypto/md5"
	"reflect"

	"github.com/zyedidia/micro/v2/internal/config"
	ulua "github.com/zyedidia/micro/v2/internal/lua"
	"github.com/zyedidia/micro/v2/internal/screen"
	luar "layeh.com/gopher-luar"
)

func (b *Buffer) ReloadSettings(reloadFiletype bool) {
	settings := config.ParsedSettings()
	config.UpdatePathGlobLocals(settings, b.AbsPath)

	oldFiletype := b.Settings["filetype"].(string)

	_, local := b.LocalSettings["filetype"]
	_, volatile := config.VolatileSettings["filetype"]
	if reloadFiletype && !local && !volatile {
		// need to update filetype before updating other settings based on it
		b.Settings["filetype"] = "unknown"
		if v, ok := settings["filetype"]; ok {
			b.Settings["filetype"] = v
		}
	}

	// update syntax rules, which will also update filetype if needed
	b.UpdateRules()

	curFiletype := b.Settings["filetype"].(string)
	if oldFiletype != curFiletype {
		b.doCallbacks("filetype", oldFiletype, curFiletype)
	}

	config.UpdateFileTypeLocals(settings, curFiletype)

	for k, v := range config.DefaultCommonSettings() {
		if k == "filetype" {
Repository: zyedidia/micro
File: internal/buffer/buffer.go (Go)

Code before:

	hasBackup := false
	if !found {
		b.SharedBuffer = new(SharedBuffer)
		b.Type = btype

		b.AbsPath = absPath
		b.Path = path

		// this is a little messy since we need to know some settings to read
		// the file properly, but some settings depend on the filetype, which
		// we don't know until reading the file. We first read the settings
		// into a local variable and then use that to determine the encoding,
		// readonly, and fileformat necessary for reading the file and
		// assigning the filetype.
		settings := config.DefaultCommonSettings()
		b.Settings = config.DefaultCommonSettings()
		b.LocalSettings = make(map[string]bool)
		for k, v := range config.GlobalSettings {
			if _, ok := config.DefaultGlobalOnlySettings[k]; !ok {
				// make sure setting is not global-only
				settings[k] = v
				b.Settings[k] = v
			}
		}
		config.ApplyPathGlobLocals(settings, absPath)
		b.Settings["readonly"] = settings["readonly"]
		b.Settings["filetype"] = settings["filetype"]
		b.Settings["syntax"] = settings["syntax"]

		enc, err := htmlindex.Get(settings["encoding"].(string))
		if err != nil {
			enc = unicode.UTF8
			b.Settings["encoding"] = "utf-8"
		}

		var ok bool
		hasBackup, ok = b.ApplyBackup(size)

		if !ok {
			return NewBufferFromString("", "", btype)
		}
		if !hasBackup {
			reader := bufio.NewReader(transform.NewReader(r, enc.NewDecoder()))

			var ff FileFormat = FFAuto

			if size == 0 {
				// for empty files, use the fileformat setting instead of
				// autodetection
				switch settings["fileformat"] {

Review comment (suggestion):
This only updates `settings`. `b.Settings` are never updated (except 3 of them below).
This is a regression: glob locals don't work anymore (I assume you didn't test it?), they only work after a `reload`.

Code after:

	hasBackup := false
	if !found {
		b.SharedBuffer = new(SharedBuffer)
		b.Type = btype

		b.AbsPath = absPath
		b.Path = path

		b.Settings = config.DefaultCommonSettings()
		b.LocalSettings = make(map[string]bool)
		for k, v := range config.GlobalSettings {
			if _, ok := config.DefaultGlobalOnlySettings[k]; !ok {
				// make sure setting is not global-only
				b.Settings[k] = v
			}
		}
		config.UpdatePathGlobLocals(b.Settings, absPath)

		enc, err := htmlindex.Get(b.Settings["encoding"].(string))
		if err != nil {
			enc = unicode.UTF8
			b.Settings["encoding"] = "utf-8"
		}

		var ok bool
		hasBackup, ok = b.ApplyBackup(size)

		if !ok {
			return NewBufferFromString("", "", btype)
		}
		if !hasBackup {
			reader := bufio.NewReader(transform.NewReader(r, enc.NewDecoder()))

			var ff FileFormat = FFAuto

			if size == 0 {
				// for empty files, use the fileformat setting instead of
				// autodetection
				switch b.Settings["fileformat"] {
				case "unix":
					ff = FFUnix
				case "dos":
					ff = FFDos
				}
			} else {
				// in case of autodetection treat as locally set
				b.LocalSettings["fileformat"] = true
			}

			b.LineArray = NewLineArray(uint64(size), ff, reader)
Repository: zyedidia/micro
File: internal/buffer/settings.go (Go)

Code before:
)

func (b *Buffer) ReloadSettings(reloadFiletype bool) {
	settings := config.ParsedSettings()
	oldFileType := b.Settings["filetype"]

	_, local := b.LocalSettings["filetype"]
	_, volatile := config.VolatileSettings["filetype"]
	if reloadFiletype && !local && !volatile {
		// need to update filetype before updating other settings based on it
		b.Settings["filetype"] = "unknown"
		config.ApplyPathGlobLocals(settings, b.Path)
		if v, ok := settings["filetype"]; ok {
			b.Settings["filetype"] = v
		}
	}

	// update syntax rules, which will also update filetype if needed
	b.UpdateRules()

	curFileType := b.Settings["filetype"]
	if oldFileType != curFileType {
		b.doCallbacks("filetype", oldFileType, curFileType)
	}

	settings["filetype"] = curFileType
	config.ApplyFileTypeLocals(settings)

	for k, v := range config.DefaultCommonSettings() {
		if k == "filetype" {
			// prevent recursion
			continue
		}
		if _, ok := config.VolatileSettings[k]; ok {
			// reload should not override volatile settings
			continue
		}
		if _, ok := b.LocalSettings[k]; ok {
			// reload should not override local settings
			continue
		}
		if _, ok := settings[k]; ok {
			b.DoSetOptionNative(k, settings[k])
		} else {
			b.DoSetOptionNative(k, v)
		}
	}
}

func (b *Buffer) DoSetOptionNative(option string, nativeValue interface{}) {
	oldValue := b.Settings[option]

Review comment (suggestion):
Maybe it's cleaner to just pass the filetype to `ApplyFileTypeLocals()` as an argument...

Code after:
)

func (b *Buffer) ReloadSettings(reloadFiletype bool) {
	settings := config.ParsedSettings()
	config.UpdatePathGlobLocals(settings, b.AbsPath)

	oldFiletype := b.Settings["filetype"].(string)

	_, local := b.LocalSettings["filetype"]
	_, volatile := config.VolatileSettings["filetype"]
	if reloadFiletype && !local && !volatile {
		// need to update filetype before updating other settings based on it
		b.Settings["filetype"] = "unknown"
		if v, ok := settings["filetype"]; ok {
			b.Settings["filetype"] = v
		}
	}

	// update syntax rules, which will also update filetype if needed
	b.UpdateRules()

	curFiletype := b.Settings["filetype"].(string)
	if oldFiletype != curFiletype {
		b.doCallbacks("filetype", oldFiletype, curFiletype)
	}

	config.UpdateFileTypeLocals(settings, curFiletype)

	for k, v := range config.DefaultCommonSettings() {
		if k == "filetype" {
			// prevent recursion
			continue
		}
		if _, ok := config.VolatileSettings[k]; ok {
			// reload should not override volatile settings
			continue
		}
		if _, ok := b.LocalSettings[k]; ok {
			// reload should not override local settings
			continue
		}
		if _, ok := settings[k]; ok {
			b.DoSetOptionNative(k, settings[k])
		} else {
			b.DoSetOptionNative(k, v)
		}
	}
}

func (b *Buffer) DoSetOptionNative(option string, nativeValue interface{}) {
	oldValue := b.Settings[option]