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))
152 // We go through each file and load it
153 for i := 0; i < len(args); i++ {
154 buf, err := buffer.NewBufferFromFile(args[i], buffer.BTDefault)
156 screen.TermMessage(err)
159 // If the file didn't exist, input will be empty, and we'll open an empty buffer
160 buffers = append(buffers, buf)
162 } else if !isatty.IsTerminal(os.Stdin.Fd()) {
164 // The input is not a terminal, so something is being piped in
165 // and we should read from stdin
166 input, err = ioutil.ReadAll(os.Stdin)
168 screen.TermMessage("Error reading from stdin: ", err)
171 buffers = append(buffers, buffer.NewBufferFromString(string(input), filename, buffer.BTDefault))
173 // Option 3, just open an empty buffer
174 buffers = append(buffers, buffer.NewBufferFromString(string(input), filename, buffer.BTDefault))
183 // runtime.SetCPUProfileRate(400)
184 // f, _ := os.Create("micro.prof")
185 // pprof.StartCPUProfile(f)
186 // defer pprof.StopCPUProfile()
194 err = config.InitConfigDir(*flagConfigDir)
196 screen.TermMessage(err)
199 config.InitRuntimeFiles()
200 err = config.ReadSettings()
202 screen.TermMessage(err)
204 config.InitGlobalSettings()
207 for k, v := range optionFlags {
209 nativeValue, err := config.GetNativeValue(k, config.DefaultAllSettings()[k], *v)
211 screen.TermMessage(err)
214 config.GlobalSettings[k] = nativeValue
223 if err := recover(); err != nil {
225 fmt.Println("Micro encountered an error:", err)
226 // backup all open buffers
227 for _, b := range buffer.OpenBuffers {
230 // Print the stack trace too
231 fmt.Print(errors.Wrap(err, 2).ErrorStack())
236 err = config.LoadAllPlugins()
238 screen.TermMessage(err)
241 action.InitBindings()
242 action.InitCommands()
244 err = config.InitColorscheme()
246 screen.TermMessage(err)
252 // No buffers to open
260 err = config.RunPluginFn("init")
262 screen.TermMessage(err)
265 events = make(chan tcell.Event)
267 // Here is the event loop which runs in a separate thread
271 e := screen.Screen.PollEvent()
279 // clear the drawchan so we don't redraw excessively
280 // if someone requested a redraw before we started displaying
281 for len(screen.DrawChan()) > 0 {
285 // wait for initial resize event
287 case event := <-events:
288 action.Tabs.HandleEvent(event)
289 case <-time.After(10 * time.Millisecond):
290 // time out after 10ms
293 // Since this loop is very slow (waits for user input every time) it's
294 // okay to be inefficient and run it via a function every time
295 // We do this so we can recover from panics without crashing the editor
301 // DoEvent runs the main action loop of the editor
303 var event tcell.Event
305 // recover from errors without crashing the editor
307 if err := recover(); err != nil {
308 if e, ok := err.(*lua.ApiError); ok {
309 screen.TermMessage("Lua API error:", e)
311 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")
315 // Display everything
316 screen.Screen.Fill(' ', config.DefStyle)
317 screen.Screen.HideCursor()
318 action.Tabs.Display()
319 for _, ep := range action.MainTab().Panes {
322 action.MainTab().Display()
323 action.InfoBar.Display()
326 // Check for new events
328 case f := <-shell.Jobs:
329 // If a new job has finished while running in the background we should execute the callback
330 f.Function(f.Output, f.Args)
331 case <-config.Autosave:
332 for _, b := range buffer.OpenBuffers {
335 case <-shell.CloseTerms:
336 case event = <-events:
337 case <-screen.DrawChan():
340 if action.InfoBar.HasPrompt {
341 action.InfoBar.HandleEvent(event)
343 action.Tabs.HandleEvent(event)