5 "github.com/gdamore/tcell"
6 "github.com/gdamore/tcell/encoding"
7 "github.com/go-errors/errors"
8 "github.com/mattn/go-isatty"
9 "github.com/mitchellh/go-homedir"
15 synLinesUp = 75 // How many lines up to look to do syntax highlighting
16 synLinesDown = 75 // How many lines down to look to do syntax highlighting
17 doubleClickThreshold = 400 // How many milliseconds to wait before a second click is not a double click
18 undoThreshold = 500 // If two events are less than n milliseconds apart, undo both of them
25 // Object to send messages and prompts to the user
31 // Where the user's configuration is
32 // This should be $XDG_CONFIG_HOME/micro
33 // If $XDG_CONFIG_HOME is not set, it is ~/.config/micro
37 // LoadInput loads the file input for the editor
38 func LoadInput() (string, []byte, error) {
39 // There are a number of ways micro should start given its input
40 // 1. If it is given a file in os.Args, it should open that
42 // 2. If there is no input file and the input is not a terminal, that means
43 // something is being piped in and the stdin should be opened in an
46 // 3. If there is no input file and the input is a terminal, an empty buffer
49 // These are empty by default so if we get to option 3, we can just returns the
58 // Check that the file exists
59 if _, e := os.Stat(filename); e == nil {
60 input, err = ioutil.ReadFile(filename)
62 } else if !isatty.IsTerminal(os.Stdin.Fd()) {
64 // The input is not a terminal, so something is being piped in
65 // and we should read from stdin
66 input, err = ioutil.ReadAll(os.Stdin)
69 // Option 3, or just return whatever we got
70 return filename, input, err
73 // InitConfigDir finds the configuration directory for micro according to the
75 // If no directory is found, it creates one.
76 func InitConfigDir() {
77 xdgHome := os.Getenv("XDG_CONFIG_HOME")
79 home, err := homedir.Dir()
81 TermMessage("Error finding your home directory\nCan't load syntax files")
84 xdgHome = home + "/.config"
86 configDir = xdgHome + "/micro"
88 if _, err := os.Stat(xdgHome); os.IsNotExist(err) {
89 err = os.Mkdir(xdgHome, os.ModePerm)
91 TermMessage("Error creating XDG_CONFIG_HOME directory: " + err.Error())
95 if _, err := os.Stat(configDir); os.IsNotExist(err) {
96 err = os.Mkdir(configDir, os.ModePerm)
98 TermMessage("Error creating configuration directory: " + err.Error())
104 filename, input, err := LoadInput()
112 // Find the user's configuration directory (probably $XDG_CONFIG_HOME/micro)
114 // Load the user's settings
116 // Load the syntax files, including the colorscheme
119 // Should we enable true color?
120 truecolor := os.Getenv("MICRO_TRUECOLOR") == "1"
122 // In order to enable true color, we have to set the TERM to `xterm-truecolor` when
123 // initializing tcell, but after that, we can set the TERM back to whatever it was
124 oldTerm := os.Getenv("TERM")
126 os.Setenv("TERM", "xterm-truecolor")
130 screen, err = tcell.NewScreen()
135 if err = screen.Init(); err != nil {
140 // Now we can put the TERM back to what it was before
142 os.Setenv("TERM", oldTerm)
145 // This is just so if we have an error, we can exit cleanly and not completely
146 // mess up the terminal being worked in
148 if err := recover(); err != nil {
150 fmt.Println("Micro encountered an error:", err)
151 // Print the stack trace too
152 fmt.Print(errors.Wrap(err, 2).ErrorStack())
158 defStyle = tcell.StyleDefault.
159 Foreground(tcell.ColorDefault).
160 Background(tcell.ColorDefault)
162 // There may be another default style defined in the colorscheme
163 if style, ok := colorscheme["default"]; ok {
167 screen.SetStyle(defStyle)
170 messenger = new(Messenger)
171 view := NewView(NewBuffer(string(input), filename))
174 // Display everything
182 // Wait for the user's action
183 event := screen.PollEvent()
186 HandleSearchEvent(event, view)
188 // Check if we should quit
189 switch e := event.(type) {
190 case *tcell.EventKey:
193 // Make sure not to quit if there are unsaved changes
194 if view.CanClose("Quit anyway? ") {
199 input, canceled := messenger.Prompt("> ")
201 HandleCommand(input, view)
205 // Make sure to resize the view if the user resized the terminal while looking at the help text
206 view.Resize(screen.Size())
210 // Send it to the view
211 view.HandleEvent(event)