17 "github.com/go-errors/errors"
18 isatty "github.com/mattn/go-isatty"
19 lua "github.com/yuin/gopher-lua"
20 "github.com/zyedidia/micro/v2/internal/action"
21 "github.com/zyedidia/micro/v2/internal/buffer"
22 "github.com/zyedidia/micro/v2/internal/clipboard"
23 "github.com/zyedidia/micro/v2/internal/config"
24 ulua "github.com/zyedidia/micro/v2/internal/lua"
25 "github.com/zyedidia/micro/v2/internal/screen"
26 "github.com/zyedidia/micro/v2/internal/shell"
27 "github.com/zyedidia/micro/v2/internal/util"
28 "github.com/zyedidia/tcell/v2"
36 flagVersion = flag.Bool("version", false, "Show the version number and information")
37 flagConfigDir = flag.String("config-dir", "", "Specify a custom location for the configuration directory")
38 flagOptions = flag.Bool("options", false, "Show all option help")
39 flagDebug = flag.Bool("debug", false, "Enable debug mode (prints debug info to ./log.txt)")
40 flagPlugin = flag.String("plugin", "", "Plugin command")
41 flagClean = flag.Bool("clean", false, "Clean configuration directory")
42 optionFlags map[string]*string
44 sigterm chan os.Signal
50 fmt.Println("Usage: micro [OPTIONS] [FILE]...")
52 fmt.Println(" \tCleans the configuration directory")
53 fmt.Println("-config-dir dir")
54 fmt.Println(" \tSpecify a custom location for the configuration directory")
55 fmt.Println("[FILE]:LINE:COL (if the `parsecursor` option is enabled)")
56 fmt.Println("+LINE:COL")
57 fmt.Println(" \tSpecify a line and column to start the cursor at when opening a buffer")
58 fmt.Println("-options")
59 fmt.Println(" \tShow all option help")
61 fmt.Println(" \tEnable debug mode (enables logging to ./log.txt)")
62 fmt.Println("-version")
63 fmt.Println(" \tShow the version number and information")
65 fmt.Print("\nMicro's plugin's can be managed at the command line with the following commands.\n")
66 fmt.Println("-plugin install [PLUGIN]...")
67 fmt.Println(" \tInstall plugin(s)")
68 fmt.Println("-plugin remove [PLUGIN]...")
69 fmt.Println(" \tRemove plugin(s)")
70 fmt.Println("-plugin update [PLUGIN]...")
71 fmt.Println(" \tUpdate plugin(s) (if no argument is given, updates all plugins)")
72 fmt.Println("-plugin search [PLUGIN]...")
73 fmt.Println(" \tSearch for a plugin")
74 fmt.Println("-plugin list")
75 fmt.Println(" \tList installed plugins")
76 fmt.Println("-plugin available")
77 fmt.Println(" \tList available plugins")
79 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")
80 fmt.Println("-option value")
81 fmt.Println(" \tSet `option` to `value` for this session")
82 fmt.Println(" \tFor example: `micro -syntax off file.c`")
83 fmt.Println("\nUse `micro -options` to see the full list of configuration options")
86 optionFlags = make(map[string]*string)
88 for k, v := range config.DefaultAllSettings() {
89 optionFlags[k] = flag.String(k, "", fmt.Sprintf("The %s option. Default value: '%v'.", k, v))
95 // If -version was passed
96 fmt.Println("Version:", util.Version)
97 fmt.Println("Commit hash:", util.CommitHash)
98 fmt.Println("Compiled on", util.CompileDate)
103 // If -options was passed
105 m := config.DefaultAllSettings()
107 keys = append(keys, k)
110 for _, k := range keys {
112 fmt.Printf("-%s value\n", k)
113 fmt.Printf(" \tDefault value: '%v'\n", v)
118 if util.Debug == "OFF" && *flagDebug {
123 // DoPluginFlags parses and executes any flags that require LoadAllPlugins (-plugin and -clean)
124 func DoPluginFlags() {
125 if *flagClean || *flagPlugin != "" {
126 config.LoadAllPlugins()
128 if *flagPlugin != "" {
131 config.PluginCommand(os.Stdout, *flagPlugin, args)
132 } else if *flagClean {
140 // LoadInput determines which files should be loaded into buffers
141 // based on the input stored in flag.Args()
142 func LoadInput(args []string) []*buffer.Buffer {
143 // There are a number of ways micro should start given its input
145 // 1. If it is given a files in flag.Args(), it should open those
147 // 2. If there is no input file and the input is not a terminal, that means
148 // something is being piped in and the stdin should be opened in an
151 // 3. If there is no input file and the input is a terminal, an empty buffer
157 buffers := make([]*buffer.Buffer, 0, len(args))
159 btype := buffer.BTDefault
160 if !isatty.IsTerminal(os.Stdout.Fd()) {
161 btype = buffer.BTStdout
164 files := make([]string, 0, len(args))
165 flagStartPos := buffer.Loc{-1, -1}
166 flagr := regexp.MustCompile(`^\+(\d+)(?::(\d+))?$`)
167 for _, a := range args {
168 match := flagr.FindStringSubmatch(a)
169 if len(match) == 3 && match[2] != "" {
170 line, err := strconv.Atoi(match[1])
172 screen.TermMessage(err)
175 col, err := strconv.Atoi(match[2])
177 screen.TermMessage(err)
180 flagStartPos = buffer.Loc{col - 1, line - 1}
181 } else if len(match) == 3 && match[2] == "" {
182 line, err := strconv.Atoi(match[1])
184 screen.TermMessage(err)
187 flagStartPos = buffer.Loc{0, line - 1}
189 files = append(files, a)
195 // We go through each file and load it
196 for i := 0; i < len(files); i++ {
197 buf, err := buffer.NewBufferFromFileAtLoc(files[i], btype, flagStartPos)
199 screen.TermMessage(err)
202 // If the file didn't exist, input will be empty, and we'll open an empty buffer
203 buffers = append(buffers, buf)
205 } else if !isatty.IsTerminal(os.Stdin.Fd()) {
207 // The input is not a terminal, so something is being piped in
208 // and we should read from stdin
209 input, err = ioutil.ReadAll(os.Stdin)
211 screen.TermMessage("Error reading from stdin: ", err)
214 buffers = append(buffers, buffer.NewBufferFromStringAtLoc(string(input), filename, btype, flagStartPos))
216 // Option 3, just open an empty buffer
217 buffers = append(buffers, buffer.NewBufferFromStringAtLoc(string(input), filename, btype, flagStartPos))
225 if util.Stdout.Len() > 0 {
226 fmt.Fprint(os.Stdout, util.Stdout.String())
231 // runtime.SetCPUProfileRate(400)
232 // f, _ := os.Create("micro.prof")
233 // pprof.StartCPUProfile(f)
234 // defer pprof.StopCPUProfile()
242 err = config.InitConfigDir(*flagConfigDir)
244 screen.TermMessage(err)
247 config.InitRuntimeFiles()
248 err = config.ReadSettings()
250 screen.TermMessage(err)
252 err = config.InitGlobalSettings()
254 screen.TermMessage(err)
258 for k, v := range optionFlags {
260 nativeValue, err := config.GetNativeValue(k, config.DefaultAllSettings()[k], *v)
262 screen.TermMessage(err)
265 config.GlobalSettings[k] = nativeValue
274 fmt.Println("Fatal: Micro could not initialize a Screen.")
277 m := clipboard.SetMethod(config.GetGlobalOption("clipboard").(string))
278 clipErr := clipboard.Initialize(m)
281 if err := recover(); err != nil {
282 if screen.Screen != nil {
285 if e, ok := err.(*lua.ApiError); ok {
286 fmt.Println("Lua API error:", e)
288 fmt.Println("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")
290 // backup all open buffers
291 for _, b := range buffer.OpenBuffers {
298 err = config.LoadAllPlugins()
300 screen.TermMessage(err)
303 action.InitBindings()
304 action.InitCommands()
306 err = config.InitColorscheme()
308 screen.TermMessage(err)
311 err = config.RunPluginFn("preinit")
313 screen.TermMessage(err)
317 buffer.SetMessager(action.InfoBar)
322 // No buffers to open
329 err = config.RunPluginFn("init")
331 screen.TermMessage(err)
334 err = config.RunPluginFn("postinit")
336 screen.TermMessage(err)
340 log.Println(clipErr, " or change 'clipboard' option")
343 if a := config.GetGlobalOption("autosave").(float64); a > 0 {
344 config.SetAutoTime(int(a))
345 config.StartAutoSave()
348 screen.Events = make(chan tcell.Event)
350 sigterm = make(chan os.Signal, 1)
351 sighup = make(chan os.Signal, 1)
352 signal.Notify(sigterm, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)
353 signal.Notify(sighup, syscall.SIGHUP)
355 // Here is the event loop which runs in a separate thread
359 e := screen.Screen.PollEvent()
367 // clear the drawchan so we don't redraw excessively
368 // if someone requested a redraw before we started displaying
369 for len(screen.DrawChan()) > 0 {
373 // wait for initial resize event
375 case event := <-screen.Events:
376 action.Tabs.HandleEvent(event)
377 case <-time.After(10 * time.Millisecond):
378 // time out after 10ms
386 // DoEvent runs the main action loop of the editor
388 var event tcell.Event
390 // Display everything
391 screen.DrawLock.Lock()
392 screen.Screen.Fill(' ', config.DefStyle)
393 screen.Screen.HideCursor()
394 action.Tabs.Display()
395 for _, ep := range action.MainTab().Panes {
398 action.MainTab().Display()
399 action.InfoBar.Display()
401 screen.DrawLock.Unlock()
403 // Check for new events
405 case f := <-shell.Jobs:
406 // If a new job has finished while running in the background we should execute the callback
408 f.Function(f.Output, f.Args)
410 case <-config.Autosave:
412 for _, b := range buffer.OpenBuffers {
416 case <-shell.CloseTerms:
417 case event = <-screen.Events:
418 case <-screen.DrawChan():
419 for len(screen.DrawChan()) > 0 {
423 for _, b := range buffer.OpenBuffers {
430 for _, b := range buffer.OpenBuffers {
436 if screen.Screen != nil {
444 if action.InfoBar.HasPrompt {
445 action.InfoBar.HandleEvent(event)
447 action.Tabs.HandleEvent(event)