5 "github.com/gdamore/tcell"
6 "github.com/go-errors/errors"
7 "github.com/mattn/go-isatty"
8 "github.com/mitchellh/go-homedir"
14 synLinesUp = 75 // How many lines up to look to do syntax highlighting
15 synLinesDown = 75 // How many lines down to look to do syntax highlighting
16 doubleClickThreshold = 400 // How many milliseconds to wait before a second click is not a double click
17 undoThreshold = 500 // If two events are less than n milliseconds apart, undo both of them
24 // Object to send messages and prompts to the user
30 // Where the user's configuration is
31 // This should be $XDG_CONFIG_HOME/micro
32 // If $XDG_CONFIG_HOME is not set, it is ~/.config/micro
36 // LoadInput loads the file input for the editor
37 func LoadInput() (string, []byte, error) {
38 // There are a number of ways micro should start given its input
39 // 1. If it is given a file in os.Args, it should open that
41 // 2. If there is no input file and the input is not a terminal, that means
42 // something is being piped in and the stdin should be opened in an
45 // 3. If there is no input file and the input is a terminal, an empty buffer
48 // These are empty by default so if we get to option 3, we can just returns the
57 // Check that the file exists
58 if _, e := os.Stat(filename); e == nil {
59 input, err = ioutil.ReadFile(filename)
61 } else if !isatty.IsTerminal(os.Stdin.Fd()) {
63 // The input is not a terminal, so something is being piped in
64 // and we should read from stdin
65 input, err = ioutil.ReadAll(os.Stdin)
68 // Option 3, or just return whatever we got
69 return filename, input, err
72 // InitConfigDir finds the configuration directory for micro according to the
74 // If no directory is found, it creates one.
75 func InitConfigDir() {
76 xdgHome := os.Getenv("XDG_CONFIG_HOME")
78 home, err := homedir.Dir()
80 TermMessage("Error finding your home directory\nCan't load syntax files")
83 xdgHome = home + "/.config"
85 configDir = xdgHome + "/micro"
87 if _, err := os.Stat(xdgHome); os.IsNotExist(err) {
88 err = os.Mkdir(xdgHome, os.ModePerm)
90 TermMessage("Error creating XDG_CONFIG_HOME directory: " + err.Error())
94 if _, err := os.Stat(configDir); os.IsNotExist(err) {
95 err = os.Mkdir(configDir, os.ModePerm)
97 TermMessage("Error creating configuration directory: " + err.Error())
103 filename, input, err := LoadInput()
113 // Load the syntax files, including the colorscheme
116 // Should we enable true color?
117 truecolor := os.Getenv("MICRO_TRUECOLOR") == "1"
119 // In order to enable true color, we have to set the TERM to `xterm-truecolor` when
120 // initializing tcell, but after that, we can set the TERM back to whatever it was
121 oldTerm := os.Getenv("TERM")
123 os.Setenv("TERM", "xterm-truecolor")
127 screen, err = tcell.NewScreen()
132 if err = screen.Init(); err != nil {
137 // Now we can put the TERM back to what it was before
139 os.Setenv("TERM", oldTerm)
142 // This is just so if we have an error, we can exit cleanly and not completely
143 // mess up the terminal being worked in
145 if err := recover(); err != nil {
147 fmt.Println("Micro encountered an error:", err)
148 // Print the stack trace too
149 fmt.Print(errors.Wrap(err, 2).ErrorStack())
155 defStyle = tcell.StyleDefault.
156 Foreground(tcell.ColorDefault).
157 Background(tcell.ColorDefault)
159 // There may be another default style defined in the colorscheme
160 if style, ok := colorscheme["default"]; ok {
164 screen.SetStyle(defStyle)
167 messenger = new(Messenger)
168 view := NewView(NewBuffer(string(input), filename))
171 // Display everything
179 // Wait for the user's action
180 event := screen.PollEvent()
183 HandleSearchEvent(event, view)
185 // Check if we should quit
186 switch e := event.(type) {
187 case *tcell.EventKey:
190 // Make sure not to quit if there are unsaved changes
191 if view.CanClose("Quit anyway? ") {
196 input, canceled := messenger.Prompt("> ")
198 HandleCommand(input, view)
202 // Make sure to resize the view if the user resized the terminal while looking at the help text
203 view.Resize(screen.Size())
207 // Send it to the view
208 view.HandleEvent(event)