repo_description: A modern and intuitive terminal-based text editor
repo_topics: command-line cross-platform editor go golang micro terminal text-editor
code: package buffer
  
  import (
  	"crypto/md5"
  	"reflect"
  
  	"github.com/micro-editor/micro/v2/internal/config"
  	ulua "github.com/micro-editor/micro/v2/internal/lua"
  	"github.com/micro-editor/micro/v2/internal/screen"
  	"golang.org/x/text/encoding/htmlindex"
  	"golang.org/x/text/encoding/unicode"
  	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
  		}
  		if _, ok := settings[k]; ok {
  			b.DoSetOptionNative(k, settings[k])
  		} else {
  			b.DoSetOptionNative(k, v)
  		}
  	}
  }
  
  func (b *Buffer) DoSetOptionNative(option string, nativeValue any) {
  	oldValue := b.Settings[option]
  	if reflect.DeepEqual(oldValue, nativeValue) {
  		return
  	}
  
  	b.Settings[option] = nativeValue
  
  	if option == "fastdirty" {
  		if !nativeValue.(bool) {
  			if b.Size() > LargeFileThreshold {
  				b.Settings["fastdirty"] = true
  			} else {
  				if !b.isModified {
  					b.calcHash(&b.origHash)
  				} else {
  					// prevent using an old stale origHash value
  					b.origHash = [md5.Size]byte{}
  				}
  			}
  		}
  	} else if option == "statusline" {
  		screen.Redraw()
  	} else if option == "filetype" {
  		b.ReloadSettings(false)
  	} else if option == "fileformat" {
  		switch b.Settings["fileformat"].(string) {
  		case "unix":
  			b.Endings = FFUnix
  		case "dos":
  			b.Endings = FFDos
  		}
  		b.setModified()
  	} else if option == "syntax" {
  		if !nativeValue.(bool) {
  			b.ClearMatches()
  		} else {
  			b.UpdateRules()
  		}
  	} else if option == "encoding" {
  		enc, err := htmlindex.Get(b.Settings["encoding"].(string))
  		if err != nil {
  			enc = unicode.UTF8
  			b.Settings["encoding"] = "utf-8"
  		}
  		b.encoding = enc
  		b.setModified()
  	} else if option == "readonly" && b.Type.Kind == BTDefault.Kind {
  		b.Type.Readonly = nativeValue.(bool)
  	} else if option == "hlsearch" {
  		for _, buf := range OpenBuffers {
  			if b.SharedBuffer == buf.SharedBuffer {
  				buf.HighlightSearch = nativeValue.(bool)
  			}
  		}
  	} else {
  		for _, pl := range config.Plugins {
  			if option == pl.Name {
  				if nativeValue.(bool) {
  					if !pl.Loaded {
  						pl.Load()
  					}
  					_, err := pl.Call("init")
  					if err != nil && err != config.ErrNoSuchFunction {
  						screen.TermMessage(err)
  					}
  				} else if !nativeValue.(bool) && pl.Loaded {
  					_, err := pl.Call("deinit")
  					if err != nil && err != config.ErrNoSuchFunction {
  						screen.TermMessage(err)
  					}
  				}
  			}
  		}
  	}
  
  	b.doCallbacks(option, oldValue, nativeValue)
  }
  
  func (b *Buffer) SetOptionNative(option string, nativeValue any) error {
  	if err := config.OptionIsValid(option, nativeValue); err != nil {
  		return err
  	}
  
  	b.DoSetOptionNative(option, nativeValue)
  	b.LocalSettings[option] = true
  
  	return nil
  }
  
  // SetOption sets a given option to a value just for this buffer
  func (b *Buffer) SetOption(option, value string) error {
  	if _, ok := b.Settings[option]; !ok {
  		return config.ErrInvalidOption
  	}
  
  	nativeValue, err := config.GetNativeValue(option, value)
  	if err != nil {
  		return err
  	}
  
  	return b.SetOptionNative(option, nativeValue)
  }
  
  func (b *Buffer) doCallbacks(option string, oldValue any, newValue any) {
  	if b.OptionCallback != nil {
  		b.OptionCallback(option, newValue)
  	}
  
  	if err := config.RunPluginFn("onBufferOptionChanged",
  		luar.New(ulua.L, b), luar.New(ulua.L, option),
  		luar.New(ulua.L, oldValue), luar.New(ulua.L, newValue)); err != nil {
  		screen.TermMessage(err)
  	}
  }
  