8 "github.com/gdamore/tcell"
9 "github.com/gdamore/tcell/encoding"
10 "github.com/go-errors/errors"
11 "github.com/mattn/go-isatty"
12 "github.com/mitchellh/go-homedir"
16 synLinesUp = 75 // How many lines up to look to do syntax highlighting
17 synLinesDown = 75 // How many lines down to look to do syntax highlighting
18 doubleClickThreshold = 400 // How many milliseconds to wait before a second click is not a double click
19 undoThreshold = 500 // If two events are less than n milliseconds apart, undo both of them
26 // Object to send messages and prompts to the user
32 // Where the user's configuration is
33 // This should be $XDG_CONFIG_HOME/micro
34 // If $XDG_CONFIG_HOME is not set, it is ~/.config/micro
38 // LoadInput loads the file input for the editor
39 func LoadInput() (string, []byte, error) {
40 // There are a number of ways micro should start given its input
41 // 1. If it is given a file in os.Args, it should open that
43 // 2. If there is no input file and the input is not a terminal, that means
44 // something is being piped in and the stdin should be opened in an
47 // 3. If there is no input file and the input is a terminal, an empty buffer
50 // These are empty by default so if we get to option 3, we can just returns the
59 // Check that the file exists
60 if _, e := os.Stat(filename); e == nil {
61 input, err = ioutil.ReadFile(filename)
63 } else if !isatty.IsTerminal(os.Stdin.Fd()) {
65 // The input is not a terminal, so something is being piped in
66 // and we should read from stdin
67 input, err = ioutil.ReadAll(os.Stdin)
70 // Option 3, or just return whatever we got
71 return filename, input, err
74 // InitConfigDir finds the configuration directory for micro according to the
76 // If no directory is found, it creates one.
77 func InitConfigDir() {
78 xdgHome := os.Getenv("XDG_CONFIG_HOME")
80 home, err := homedir.Dir()
82 TermMessage("Error finding your home directory\nCan't load syntax files")
85 xdgHome = home + "/.config"
87 configDir = xdgHome + "/micro"
89 if _, err := os.Stat(xdgHome); os.IsNotExist(err) {
90 err = os.Mkdir(xdgHome, os.ModePerm)
92 TermMessage("Error creating XDG_CONFIG_HOME directory: " + err.Error())
96 if _, err := os.Stat(configDir); os.IsNotExist(err) {
97 err = os.Mkdir(configDir, os.ModePerm)
99 TermMessage("Error creating configuration directory: " + err.Error())
105 filename, input, err := LoadInput()
113 // Find the user's configuration directory (probably $XDG_CONFIG_HOME/micro)
115 // Load the user's settings
117 // Load the syntax files, including the colorscheme
120 // Should we enable true color?
121 truecolor := os.Getenv("MICRO_TRUECOLOR") == "1"
123 // In order to enable true color, we have to set the TERM to `xterm-truecolor` when
124 // initializing tcell, but after that, we can set the TERM back to whatever it was
125 oldTerm := os.Getenv("TERM")
127 os.Setenv("TERM", "xterm-truecolor")
131 screen, err = tcell.NewScreen()
136 if err = screen.Init(); err != nil {
141 // Now we can put the TERM back to what it was before
143 os.Setenv("TERM", oldTerm)
146 // This is just so if we have an error, we can exit cleanly and not completely
147 // mess up the terminal being worked in
149 if err := recover(); err != nil {
151 fmt.Println("Micro encountered an error:", err)
152 // Print the stack trace too
153 fmt.Print(errors.Wrap(err, 2).ErrorStack())
159 defStyle = tcell.StyleDefault.
160 Foreground(tcell.ColorDefault).
161 Background(tcell.ColorDefault)
163 // There may be another default style defined in the colorscheme
164 if style, ok := colorscheme["default"]; ok {
168 screen.SetStyle(defStyle)
171 messenger = new(Messenger)
172 view := NewView(NewBuffer(string(input), filename))
175 // Display everything
183 // Wait for the user's action
184 event := screen.PollEvent()
187 HandleSearchEvent(event, view)
189 // Check if we should quit
190 switch e := event.(type) {
191 case *tcell.EventKey:
194 // Make sure not to quit if there are unsaved changes
195 if view.CanClose("Quit anyway? ") {
200 input, canceled := messenger.Prompt("> ")
202 HandleCommand(input, view)
205 input, canceled := messenger.Prompt("$ ")
207 HandleShellCommand(input, view)
211 // Make sure to resize the view if the user resized the terminal while looking at the help text
212 view.Resize(screen.Size())
216 // Send it to the view
217 view.HandleEvent(event)