diff --git a/runtime/syntax/go.micro b/runtime/syntax/go.micro index 1feff130..21fa52d8 100644 --- a/runtime/syntax/go.micro +++ b/runtime/syntax/go.micro @@ -1,6 +1,5 @@ syntax "Go" "\.go$" -color identifier "[A-Za-z_][A-Za-z0-9_]*[[:space:]]*[()]" color statement "\b(append|cap|close|complex|copy|delete|imag|len)\b" color statement "\b(make|new|panic|print|println|protect|real|recover)\b" color type "\b(u?int(8|16|32|64)?|float(32|64)|complex(64|128))\b" diff --git a/src/colorscheme.go b/src/colorscheme.go index 6b41d669..721e243f 100644 --- a/src/colorscheme.go +++ b/src/colorscheme.go @@ -28,7 +28,7 @@ func LoadDefaultColorscheme() { TermMessage("Error finding your home directory\nCan't load runtime files") return } - LoadColorscheme(options["colorscheme"].(string), dir+"/.micro/colorschemes") + LoadColorscheme(settings.Colorscheme, dir+"/.micro/colorschemes") } // LoadColorscheme loads the given colorscheme from a directory diff --git a/src/cursor.go b/src/cursor.go index 70b7442e..8b0f56fb 100644 --- a/src/cursor.go +++ b/src/cursor.go @@ -257,7 +257,7 @@ func (c *Cursor) Start() { // GetCharPosInLine gets the char position of a visual x y coordinate (this is necessary because tabs are 1 char but 4 visual spaces) func (c *Cursor) GetCharPosInLine(lineNum, visualPos int) int { // Get the tab size - tabSize := options["tabsize"].(int) + tabSize := settings.TabSize // This is the visual line -- every \t replaced with the correct number of spaces visualLine := strings.Replace(c.v.buf.lines[lineNum], "\t", "\t"+Spaces(tabSize-1), -1) if visualPos > Count(visualLine) { @@ -273,7 +273,7 @@ func (c *Cursor) GetCharPosInLine(lineNum, visualPos int) int { // GetVisualX returns the x value of the cursor in visual spaces func (c *Cursor) GetVisualX() int { runes := []rune(c.v.buf.lines[c.y]) - tabSize := options["tabsize"].(int) + tabSize := settings.TabSize return c.x + NumOccurences(string(runes[:c.x]), '\t')*(tabSize-1) } diff --git a/src/highlighter.go b/src/highlighter.go index e383c1aa..f4483448 100644 --- a/src/highlighter.go +++ b/src/highlighter.go @@ -31,12 +31,12 @@ var syntaxFiles map[[2]*regexp.Regexp]FileTypeRules // LoadSyntaxFiles loads the syntax files from the default directory ~/.micro func LoadSyntaxFiles() { - dir, err := homedir.Dir() + home, err := homedir.Dir() if err != nil { - TermMessage("Error finding your home directory\nCan't load runtime files") + TermMessage("Error finding your home directory\nCan't load syntax files") return } - LoadSyntaxFilesFromDir(dir + "/.micro/syntax") + LoadSyntaxFilesFromDir(home + "/.micro/syntax") } // JoinRule takes a syntax rule (which can be multiple regular expressions) diff --git a/src/micro.go b/src/micro.go index 884272d1..ee038597 100644 --- a/src/micro.go +++ b/src/micro.go @@ -64,7 +64,7 @@ func main() { os.Exit(1) } - InitOptions() + InitSettings() // Should we enable true color? truecolor := os.Getenv("MICRO_TRUECOLOR") == "1" diff --git a/src/option.go b/src/option.go deleted file mode 100644 index e4ffa884..00000000 --- a/src/option.go +++ /dev/null @@ -1,47 +0,0 @@ -package main - -import ( - "strconv" - "strings" -) - -// The options that the user can set -var options map[string]interface{} - -// InitOptions initializes the options map and sets all options to their default values -func InitOptions() { - options = make(map[string]interface{}) - options["tabsize"] = 4 - options["colorscheme"] = "default" -} - -// SetOption prompts the user to set an option and checks that the response is valid -func SetOption(view *View) { - choice, canceled := messenger.Prompt("Option: ") - if !canceled { - split := strings.Split(choice, "=") - if len(split) == 2 { - option := strings.TrimSpace(split[0]) - value := strings.TrimSpace(split[1]) - if _, exists := options[option]; exists { - if option == "tabsize" { - tsize, err := strconv.Atoi(value) - if err != nil { - messenger.Error("Invalid value for " + option) - return - } - options[option] = tsize - } - if option == "colorscheme" { - options[option] = value - LoadSyntaxFiles() - view.buf.UpdateRules() - } - } else { - messenger.Error("Option " + option + " does not exist") - } - } else { - messenger.Error("Invalid option, please use option = value") - } - } -} diff --git a/src/settings.go b/src/settings.go new file mode 100644 index 00000000..e0103836 --- /dev/null +++ b/src/settings.go @@ -0,0 +1,110 @@ +package main + +import ( + "encoding/json" + "github.com/mitchellh/go-homedir" + "io/ioutil" + "os" + "strconv" + "strings" +) + +// The options that the user can set +var settings Settings + +// All the possible settings +var possibleSettings = []string{"colorscheme", "tabsize", "autoindent"} + +// The Settings struct contains the settings for micro +type Settings struct { + Colorscheme string `json:"colorscheme"` + TabSize int `json:"tabsize"` + AutoIndent bool `json:"autoindent"` +} + +// InitSettings initializes the options map and sets all options to their default values +func InitSettings() { + home, err := homedir.Dir() + if err != nil { + TermMessage("Error finding your home directory\nCan't load settings file") + return + } + + filename := home + "/.micro/settings.json" + if _, e := os.Stat(filename); e == nil { + input, err := ioutil.ReadFile(filename) + if err != nil { + TermMessage("Error reading settings.json file: " + err.Error()) + return + } + + json.Unmarshal(input, &settings) + } else { + settings = DefaultSettings() + err := WriteSettings(filename) + if err != nil { + TermMessage("Error writing settings.json file: " + err.Error()) + } + } +} + +// WriteSettings writes the settings to the specified filename as JSON +func WriteSettings(filename string) error { + txt, _ := json.MarshalIndent(settings, "", " ") + err := ioutil.WriteFile(filename, txt, 0644) + return err +} + +// DefaultSettings returns the default settings for micro +func DefaultSettings() Settings { + return Settings{ + Colorscheme: "default", + TabSize: 4, + AutoIndent: true, + } +} + +// SetOption prompts the user to set an option and checks that the response is valid +func SetOption(view *View) { + choice, canceled := messenger.Prompt("Option: ") + + home, err := homedir.Dir() + if err != nil { + messenger.Error("Error finding your home directory\nCan't load settings file") + return + } + + filename := home + "/.micro/settings.json" + + if !canceled { + split := strings.Split(choice, " ") + if len(split) == 2 { + option := strings.TrimSpace(split[0]) + value := strings.TrimSpace(split[1]) + + if Contains(possibleSettings, option) { + if option == "tabsize" { + tsize, err := strconv.Atoi(value) + if err != nil { + messenger.Error("Invalid value for " + option) + return + } + settings.TabSize = tsize + } else if option == "colorscheme" { + settings.Colorscheme = value + LoadSyntaxFiles() + view.buf.UpdateRules() + } + err := WriteSettings(filename) + if err != nil { + messenger.Error("Error writing to settings.json: " + err.Error()) + return + } + } else { + messenger.Error("Option " + option + " does not exist") + } + } else { + messenger.Error("Invalid option, please use option value") + } + } +} diff --git a/src/util.go b/src/util.go index ca2ffb05..2ad55387 100644 --- a/src/util.go +++ b/src/util.go @@ -60,3 +60,13 @@ func IsWordChar(str string) bool { c := str[0] return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '_') } + +// Contains returns whether or not a string array contains a given string +func Contains(list []string, a string) bool { + for _, b := range list { + if b == a { + return true + } + } + return false +} diff --git a/src/view.go b/src/view.go index 55c566da..6b6f76c7 100644 --- a/src/view.go +++ b/src/view.go @@ -626,7 +626,7 @@ func (v *View) DisplayView() { if ch == '\t' { screen.SetContent(x+tabchars, lineN, ' ', nil, lineStyle) - tabSize := options["tabsize"].(int) + tabSize := settings.TabSize for i := 0; i < tabSize-1; i++ { tabchars++ screen.SetContent(x+tabchars, lineN, ' ', nil, lineStyle)