12 "github.com/go-errors/errors"
13 isatty "github.com/mattn/go-isatty"
14 lua "github.com/yuin/gopher-lua"
15 "github.com/zyedidia/micro/internal/action"
16 "github.com/zyedidia/micro/internal/buffer"
17 "github.com/zyedidia/micro/internal/config"
18 "github.com/zyedidia/micro/internal/screen"
19 "github.com/zyedidia/micro/internal/shell"
20 "github.com/zyedidia/micro/internal/util"
21 "github.com/zyedidia/tcell"
26 events chan tcell.Event
30 flagVersion = flag.Bool("version", false, "Show the version number and information")
31 flagConfigDir = flag.String("config-dir", "", "Specify a custom location for the configuration directory")
32 flagOptions = flag.Bool("options", false, "Show all option help")
33 flagDebug = flag.Bool("debug", false, "Enable debug mode (prints debug info to ./log.txt)")
34 flagPlugin = flag.String("plugin", "", "Plugin command")
35 flagClean = flag.Bool("clean", false, "Clean configuration directory")
36 optionFlags map[string]*string
41 fmt.Println("Usage: micro [OPTIONS] [FILE]...")
43 fmt.Println(" \tCleans the configuration directory")
44 fmt.Println("-config-dir dir")
45 fmt.Println(" \tSpecify a custom location for the configuration directory")
46 fmt.Println("[FILE]:LINE:COL")
47 fmt.Println(" \tSpecify a line and column to start the cursor at when opening a buffer")
48 fmt.Println("-options")
49 fmt.Println(" \tShow all option help")
51 fmt.Println(" \tEnable debug mode (enables logging to ./log.txt)")
52 fmt.Println("-version")
53 fmt.Println(" \tShow the version number and information")
55 fmt.Print("\nMicro's plugin's can be managed at the command line with the following commands.\n")
56 fmt.Println("-plugin install [PLUGIN]...")
57 fmt.Println(" \tInstall plugin(s)")
58 fmt.Println("-plugin remove [PLUGIN]...")
59 fmt.Println(" \tRemove plugin(s)")
60 fmt.Println("-plugin update [PLUGIN]...")
61 fmt.Println(" \tUpdate plugin(s) (if no argument is given, updates all plugins)")
62 fmt.Println("-plugin search [PLUGIN]...")
63 fmt.Println(" \tSearch for a plugin")
64 fmt.Println("-plugin list")
65 fmt.Println(" \tList installed plugins")
66 fmt.Println("-plugin available")
67 fmt.Println(" \tList available plugins")
69 fmt.Print("\nMicro's options can also be set via command line arguments for quick\nadjustments. For real configuration, please use the settings.json\nfile (see 'help options').\n\n")
70 fmt.Println("-option value")
71 fmt.Println(" \tSet `option` to `value` for this session")
72 fmt.Println(" \tFor example: `micro -syntax off file.c`")
73 fmt.Println("\nUse `micro -options` to see the full list of configuration options")
76 optionFlags = make(map[string]*string)
78 for k, v := range config.DefaultAllSettings() {
79 optionFlags[k] = flag.String(k, "", fmt.Sprintf("The %s option. Default value: '%v'.", k, v))
85 // If -version was passed
86 fmt.Println("Version:", util.Version)
87 fmt.Println("Commit hash:", util.CommitHash)
88 fmt.Println("Compiled on", util.CompileDate)
93 // If -options was passed
95 m := config.DefaultAllSettings()
97 keys = append(keys, k)
100 for _, k := range keys {
102 fmt.Printf("-%s value\n", k)
103 fmt.Printf(" \tDefault value: '%v'\n", v)
108 if util.Debug == "OFF" && *flagDebug {
113 // DoPluginFlags parses and executes any flags that require LoadAllPlugins (-plugin and -clean)
114 func DoPluginFlags() {
115 if *flagClean || *flagPlugin != "" {
116 config.LoadAllPlugins()
118 if *flagPlugin != "" {
121 config.PluginCommand(os.Stdout, *flagPlugin, args)
122 } else if *flagClean {
130 // LoadInput determines which files should be loaded into buffers
131 // based on the input stored in flag.Args()
132 func LoadInput() []*buffer.Buffer {
133 // There are a number of ways micro should start given its input
135 // 1. If it is given a files in flag.Args(), it should open those
137 // 2. If there is no input file and the input is not a terminal, that means
138 // something is being piped in and the stdin should be opened in an
141 // 3. If there is no input file and the input is a terminal, an empty buffer
148 buffers := make([]*buffer.Buffer, 0, len(args))
150 btype := buffer.BTDefault
151 if !isatty.IsTerminal(os.Stdout.Fd()) {
152 btype = buffer.BTStdout
157 // We go through each file and load it
158 for i := 0; i < len(args); i++ {
159 buf, err := buffer.NewBufferFromFile(args[i], btype)
161 screen.TermMessage(err)
164 // If the file didn't exist, input will be empty, and we'll open an empty buffer
165 buffers = append(buffers, buf)
167 } else if !isatty.IsTerminal(os.Stdin.Fd()) {
169 // The input is not a terminal, so something is being piped in
170 // and we should read from stdin
171 input, err = ioutil.ReadAll(os.Stdin)
173 screen.TermMessage("Error reading from stdin: ", err)
176 buffers = append(buffers, buffer.NewBufferFromString(string(input), filename, btype))
178 // Option 3, just open an empty buffer
179 buffers = append(buffers, buffer.NewBufferFromString(string(input), filename, btype))
187 if util.Stdout.Len() > 0 {
188 fmt.Fprint(os.Stdout, util.Stdout.String())
193 // runtime.SetCPUProfileRate(400)
194 // f, _ := os.Create("micro.prof")
195 // pprof.StartCPUProfile(f)
196 // defer pprof.StopCPUProfile()
204 err = config.InitConfigDir(*flagConfigDir)
206 screen.TermMessage(err)
209 config.InitRuntimeFiles()
210 err = config.ReadSettings()
212 screen.TermMessage(err)
214 config.InitGlobalSettings()
217 for k, v := range optionFlags {
219 nativeValue, err := config.GetNativeValue(k, config.DefaultAllSettings()[k], *v)
221 screen.TermMessage(err)
224 config.GlobalSettings[k] = nativeValue
233 if err := recover(); err != nil {
235 fmt.Println("Micro encountered an error:", err)
236 // backup all open buffers
237 for _, b := range buffer.OpenBuffers {
240 // Print the stack trace too
241 fmt.Print(errors.Wrap(err, 2).ErrorStack())
246 err = config.LoadAllPlugins()
248 screen.TermMessage(err)
251 action.InitBindings()
252 action.InitCommands()
254 err = config.InitColorscheme()
256 screen.TermMessage(err)
262 // No buffers to open
270 err = config.RunPluginFn("init")
272 screen.TermMessage(err)
275 events = make(chan tcell.Event)
277 // Here is the event loop which runs in a separate thread
281 e := screen.Screen.PollEvent()
289 // clear the drawchan so we don't redraw excessively
290 // if someone requested a redraw before we started displaying
291 for len(screen.DrawChan()) > 0 {
295 // wait for initial resize event
297 case event := <-events:
298 action.Tabs.HandleEvent(event)
299 case <-time.After(10 * time.Millisecond):
300 // time out after 10ms
303 // Since this loop is very slow (waits for user input every time) it's
304 // okay to be inefficient and run it via a function every time
305 // We do this so we can recover from panics without crashing the editor
311 // DoEvent runs the main action loop of the editor
313 var event tcell.Event
315 // recover from errors without crashing the editor
317 if err := recover(); err != nil {
318 if e, ok := err.(*lua.ApiError); ok {
319 screen.TermMessage("Lua API error:", e)
321 screen.TermMessage("Micro encountered an error:", errors.Wrap(err, 2).ErrorStack(), "\nIf you can reproduce this error, please report it at https://github.com/zyedidia/micro/issues")
325 // Display everything
326 screen.Screen.Fill(' ', config.DefStyle)
327 screen.Screen.HideCursor()
328 action.Tabs.Display()
329 for _, ep := range action.MainTab().Panes {
332 action.MainTab().Display()
333 action.InfoBar.Display()
336 // Check for new events
338 case f := <-shell.Jobs:
339 // If a new job has finished while running in the background we should execute the callback
340 f.Function(f.Output, f.Args)
341 case <-config.Autosave:
342 for _, b := range buffer.OpenBuffers {
345 case <-shell.CloseTerms:
346 case event = <-events:
347 case <-screen.DrawChan():
350 if action.InfoBar.HasPrompt {
351 action.InfoBar.HandleEvent(event)
353 action.Tabs.HandleEvent(event)