14 "github.com/go-errors/errors"
15 isatty "github.com/mattn/go-isatty"
16 lua "github.com/yuin/gopher-lua"
17 "github.com/zyedidia/micro/v2/internal/action"
18 "github.com/zyedidia/micro/v2/internal/buffer"
19 "github.com/zyedidia/micro/v2/internal/clipboard"
20 "github.com/zyedidia/micro/v2/internal/config"
21 ulua "github.com/zyedidia/micro/v2/internal/lua"
22 "github.com/zyedidia/micro/v2/internal/screen"
23 "github.com/zyedidia/micro/v2/internal/shell"
24 "github.com/zyedidia/micro/v2/internal/util"
25 "github.com/zyedidia/tcell"
33 flagVersion = flag.Bool("version", false, "Show the version number and information")
34 flagConfigDir = flag.String("config-dir", "", "Specify a custom location for the configuration directory")
35 flagOptions = flag.Bool("options", false, "Show all option help")
36 flagDebug = flag.Bool("debug", false, "Enable debug mode (prints debug info to ./log.txt)")
37 flagPlugin = flag.String("plugin", "", "Plugin command")
38 flagClean = flag.Bool("clean", false, "Clean configuration directory")
39 optionFlags map[string]*string
44 fmt.Println("Usage: micro [OPTIONS] [FILE]...")
46 fmt.Println(" \tCleans the configuration directory")
47 fmt.Println("-config-dir dir")
48 fmt.Println(" \tSpecify a custom location for the configuration directory")
49 fmt.Println("[FILE]:LINE:COL (if the `parsecursor` option is enabled)")
50 fmt.Println("+LINE:COL")
51 fmt.Println(" \tSpecify a line and column to start the cursor at when opening a buffer")
52 fmt.Println("-options")
53 fmt.Println(" \tShow all option help")
55 fmt.Println(" \tEnable debug mode (enables logging to ./log.txt)")
56 fmt.Println("-version")
57 fmt.Println(" \tShow the version number and information")
59 fmt.Print("\nMicro's plugin's can be managed at the command line with the following commands.\n")
60 fmt.Println("-plugin install [PLUGIN]...")
61 fmt.Println(" \tInstall plugin(s)")
62 fmt.Println("-plugin remove [PLUGIN]...")
63 fmt.Println(" \tRemove plugin(s)")
64 fmt.Println("-plugin update [PLUGIN]...")
65 fmt.Println(" \tUpdate plugin(s) (if no argument is given, updates all plugins)")
66 fmt.Println("-plugin search [PLUGIN]...")
67 fmt.Println(" \tSearch for a plugin")
68 fmt.Println("-plugin list")
69 fmt.Println(" \tList installed plugins")
70 fmt.Println("-plugin available")
71 fmt.Println(" \tList available plugins")
73 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")
74 fmt.Println("-option value")
75 fmt.Println(" \tSet `option` to `value` for this session")
76 fmt.Println(" \tFor example: `micro -syntax off file.c`")
77 fmt.Println("\nUse `micro -options` to see the full list of configuration options")
80 optionFlags = make(map[string]*string)
82 for k, v := range config.DefaultAllSettings() {
83 optionFlags[k] = flag.String(k, "", fmt.Sprintf("The %s option. Default value: '%v'.", k, v))
89 // If -version was passed
90 fmt.Println("Version:", util.Version)
91 fmt.Println("Commit hash:", util.CommitHash)
92 fmt.Println("Compiled on", util.CompileDate)
97 // If -options was passed
99 m := config.DefaultAllSettings()
101 keys = append(keys, k)
104 for _, k := range keys {
106 fmt.Printf("-%s value\n", k)
107 fmt.Printf(" \tDefault value: '%v'\n", v)
112 if util.Debug == "OFF" && *flagDebug {
117 // DoPluginFlags parses and executes any flags that require LoadAllPlugins (-plugin and -clean)
118 func DoPluginFlags() {
119 if *flagClean || *flagPlugin != "" {
120 config.LoadAllPlugins()
122 if *flagPlugin != "" {
125 config.PluginCommand(os.Stdout, *flagPlugin, args)
126 } else if *flagClean {
134 // LoadInput determines which files should be loaded into buffers
135 // based on the input stored in flag.Args()
136 func LoadInput(args []string) []*buffer.Buffer {
137 // There are a number of ways micro should start given its input
139 // 1. If it is given a files in flag.Args(), it should open those
141 // 2. If there is no input file and the input is not a terminal, that means
142 // something is being piped in and the stdin should be opened in an
145 // 3. If there is no input file and the input is a terminal, an empty buffer
151 buffers := make([]*buffer.Buffer, 0, len(args))
153 btype := buffer.BTDefault
154 if !isatty.IsTerminal(os.Stdout.Fd()) {
155 btype = buffer.BTStdout
158 files := make([]string, 0, len(args))
159 flagStartPos := buffer.Loc{-1, -1}
160 flagr := regexp.MustCompile(`^\+(\d+)(?::(\d+))?$`)
161 for _, a := range args {
162 match := flagr.FindStringSubmatch(a)
163 if len(match) == 3 && match[2] != "" {
164 line, err := strconv.Atoi(match[1])
166 screen.TermMessage(err)
169 col, err := strconv.Atoi(match[2])
171 screen.TermMessage(err)
174 flagStartPos = buffer.Loc{col - 1, line - 1}
175 } else if len(match) == 3 && match[2] == "" {
176 line, err := strconv.Atoi(match[1])
178 screen.TermMessage(err)
181 flagStartPos = buffer.Loc{0, line - 1}
183 files = append(files, a)
189 // We go through each file and load it
190 for i := 0; i < len(files); i++ {
191 buf, err := buffer.NewBufferFromFileAtLoc(files[i], btype, flagStartPos)
193 screen.TermMessage(err)
196 // If the file didn't exist, input will be empty, and we'll open an empty buffer
197 buffers = append(buffers, buf)
199 } else if !isatty.IsTerminal(os.Stdin.Fd()) {
201 // The input is not a terminal, so something is being piped in
202 // and we should read from stdin
203 input, err = ioutil.ReadAll(os.Stdin)
205 screen.TermMessage("Error reading from stdin: ", err)
208 buffers = append(buffers, buffer.NewBufferFromStringAtLoc(string(input), filename, btype, flagStartPos))
210 // Option 3, just open an empty buffer
211 buffers = append(buffers, buffer.NewBufferFromStringAtLoc(string(input), filename, btype, flagStartPos))
219 if util.Stdout.Len() > 0 {
220 fmt.Fprint(os.Stdout, util.Stdout.String())
225 // runtime.SetCPUProfileRate(400)
226 // f, _ := os.Create("micro.prof")
227 // pprof.StartCPUProfile(f)
228 // defer pprof.StopCPUProfile()
236 err = config.InitConfigDir(*flagConfigDir)
238 screen.TermMessage(err)
241 config.InitRuntimeFiles()
242 err = config.ReadSettings()
244 screen.TermMessage(err)
246 err = config.InitGlobalSettings()
248 screen.TermMessage(err)
252 for k, v := range optionFlags {
254 nativeValue, err := config.GetNativeValue(k, config.DefaultAllSettings()[k], *v)
256 screen.TermMessage(err)
259 config.GlobalSettings[k] = nativeValue
268 fmt.Println("Fatal: Micro could not initialize a Screen.")
272 m := clipboard.SetMethod(config.GetGlobalOption("clipboard").(string))
273 clipErr := clipboard.Initialize(m)
276 if err := recover(); err != nil {
278 fmt.Println("Micro encountered an error:", err)
279 // backup all open buffers
280 for _, b := range buffer.OpenBuffers {
283 // Print the stack trace too
284 fmt.Print(errors.Wrap(err, 2).ErrorStack())
289 err = config.LoadAllPlugins()
291 screen.TermMessage(err)
294 action.InitBindings()
295 action.InitCommands()
297 err = config.InitColorscheme()
299 screen.TermMessage(err)
306 // No buffers to open
314 err = config.RunPluginFn("init")
316 screen.TermMessage(err)
320 action.InfoBar.Error(clipErr, " or change 'clipboard' option")
323 screen.Events = make(chan tcell.Event)
325 // Here is the event loop which runs in a separate thread
329 e := screen.Screen.PollEvent()
337 // clear the drawchan so we don't redraw excessively
338 // if someone requested a redraw before we started displaying
339 for len(screen.DrawChan()) > 0 {
343 // wait for initial resize event
345 case event := <-screen.Events:
346 action.Tabs.HandleEvent(event)
347 case <-time.After(10 * time.Millisecond):
348 // time out after 10ms
351 // Since this loop is very slow (waits for user input every time) it's
352 // okay to be inefficient and run it via a function every time
353 // We do this so we can recover from panics without crashing the editor
359 // DoEvent runs the main action loop of the editor
361 var event tcell.Event
363 // recover from errors without crashing the editor
365 if err := recover(); err != nil {
366 if e, ok := err.(*lua.ApiError); ok {
367 screen.TermMessage("Lua API error:", e)
369 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")
373 // Display everything
374 screen.Screen.Fill(' ', config.DefStyle)
375 screen.Screen.HideCursor()
376 action.Tabs.Display()
377 for _, ep := range action.MainTab().Panes {
380 action.MainTab().Display()
381 action.InfoBar.Display()
384 // Check for new events
386 case f := <-shell.Jobs:
387 // If a new job has finished while running in the background we should execute the callback
389 f.Function(f.Output, f.Args)
391 case <-config.Autosave:
393 for _, b := range buffer.OpenBuffers {
397 case <-shell.CloseTerms:
398 case event = <-screen.Events:
399 case <-screen.DrawChan():
400 for len(screen.DrawChan()) > 0 {
406 if action.InfoBar.HasPrompt {
407 action.InfoBar.HandleEvent(event)
409 action.Tabs.HandleEvent(event)