2016-03-17 21:27:57 +00:00
package main
import (
2016-04-19 17:40:30 +00:00
"flag"
2016-03-17 21:27:57 +00:00
"fmt"
2016-04-19 04:33:54 +00:00
"io/ioutil"
"os"
2016-09-19 11:23:47 +00:00
"path/filepath"
2016-04-30 22:25:45 +00:00
"runtime"
2018-02-02 21:53:08 +00:00
"strings"
2016-09-28 17:07:05 +00:00
"time"
2016-04-19 04:33:54 +00:00
2016-03-20 19:24:40 +00:00
"github.com/go-errors/errors"
2016-03-17 21:27:57 +00:00
"github.com/mattn/go-isatty"
2016-04-18 14:31:19 +00:00
"github.com/mitchellh/go-homedir"
2016-04-24 23:52:02 +00:00
"github.com/yuin/gopher-lua"
2016-07-24 14:51:08 +00:00
"github.com/zyedidia/clipboard"
2018-01-30 04:36:39 +00:00
"github.com/zyedidia/micro/cmd/micro/terminfo"
2016-05-01 23:07:54 +00:00
"github.com/zyedidia/tcell"
"github.com/zyedidia/tcell/encoding"
2017-02-20 18:08:14 +00:00
"layeh.com/gopher-luar"
2016-03-17 21:27:57 +00:00
)
2016-07-10 17:27:28 +00:00
const (
doubleClickThreshold = 400 // How many milliseconds to wait before a second click is not a double click
undoThreshold = 500 // If two events are less than n milliseconds apart, undo both of them
2016-09-28 17:07:05 +00:00
autosaveTime = 8 // Number of seconds to wait before autosaving
2016-07-10 17:27:28 +00:00
)
2016-04-16 13:55:40 +00:00
var (
// The main screen
screen tcell . Screen
2016-03-25 16:14:22 +00:00
2016-04-16 13:55:40 +00:00
// Object to send messages and prompts to the user
messenger * Messenger
2016-03-26 14:54:18 +00:00
2016-05-28 15:32:09 +00:00
// The default highlighting style
// This simply defines the default foreground and background colors
2016-04-16 21:33:13 +00:00
defStyle tcell . Style
2016-04-18 14:31:19 +00:00
// Where the user's configuration is
// This should be $XDG_CONFIG_HOME/micro
// If $XDG_CONFIG_HOME is not set, it is ~/.config/micro
configDir string
2016-04-19 17:40:30 +00:00
2016-05-28 15:32:09 +00:00
// Version is the version number or commit hash
2016-08-28 20:28:28 +00:00
// These variables should be set by the linker when compiling
2018-01-04 22:14:51 +00:00
Version = "0.0.0-unknown"
// CommitHash is the commit this version was built on
CommitHash = "Unknown"
// CompileDate is the date this binary was compiled on
2016-08-28 00:03:43 +00:00
CompileDate = "Unknown"
2016-04-19 17:58:02 +00:00
2016-05-28 15:32:09 +00:00
// The list of views
2016-06-08 17:26:50 +00:00
tabs [ ] * Tab
// This is the currently open tab
// It's just an index to the tab in the tabs array
curTab int
2016-06-15 15:19:00 +00:00
2016-07-10 17:26:05 +00:00
// Channel of jobs running in the background
jobs chan JobFunction
2018-01-29 21:47:55 +00:00
2016-07-10 17:26:05 +00:00
// Event channel
2018-01-29 21:47:55 +00:00
events chan tcell . Event
autosave chan bool
// Channels for the terminal emulator
2018-01-04 22:03:08 +00:00
updateterm chan bool
closeterm chan int
2018-01-29 21:47:55 +00:00
// How many redraws have happened
numRedraw uint
2016-04-16 13:55:40 +00:00
)
2016-04-06 19:59:26 +00:00
2016-07-10 17:26:05 +00:00
// LoadInput determines which files should be loaded into buffers
2016-09-03 15:26:01 +00:00
// based on the input stored in flag.Args()
2016-06-03 20:41:09 +00:00
func LoadInput ( ) [ ] * Buffer {
2016-03-25 16:14:22 +00:00
// There are a number of ways micro should start given its input
2016-05-28 15:32:09 +00:00
2016-09-03 15:26:01 +00:00
// 1. If it is given a files in flag.Args(), it should open those
2016-03-25 16:14:22 +00:00
// 2. If there is no input file and the input is not a terminal, that means
// something is being piped in and the stdin should be opened in an
// empty buffer
// 3. If there is no input file and the input is a terminal, an empty buffer
// should be opened
2016-03-17 21:27:57 +00:00
var filename string
2016-03-25 16:14:22 +00:00
var input [ ] byte
var err error
2017-05-03 15:04:56 +00:00
args := flag . Args ( )
buffers := make ( [ ] * Buffer , 0 , len ( args ) )
2016-03-17 21:27:57 +00:00
2017-05-03 15:04:56 +00:00
if len ( args ) > 0 {
2016-03-25 16:14:22 +00:00
// Option 1
2016-07-10 17:26:05 +00:00
// We go through each file and load it
2017-05-03 15:04:56 +00:00
for i := 0 ; i < len ( args ) ; i ++ {
2018-02-02 21:53:08 +00:00
if strings . HasPrefix ( args [ i ] , "+" ) {
if strings . Contains ( args [ i ] , ":" ) {
split := strings . Split ( args [ i ] , ":" )
* flagStartPos = split [ 0 ] [ 1 : ] + "," + split [ 1 ]
} else {
* flagStartPos = args [ i ] [ 1 : ] + ",0"
}
continue
}
2018-02-02 18:57:02 +00:00
buf , err := NewBufferFromFile ( args [ i ] )
if err != nil {
TermMessage ( err )
continue
2016-06-03 20:41:09 +00:00
}
2016-07-10 17:26:05 +00:00
// If the file didn't exist, input will be empty, and we'll open an empty buffer
2018-02-02 18:57:02 +00:00
buffers = append ( buffers , buf )
2016-03-17 21:27:57 +00:00
}
} else if ! isatty . IsTerminal ( os . Stdin . Fd ( ) ) {
2016-03-25 16:14:22 +00:00
// Option 2
// The input is not a terminal, so something is being piped in
// and we should read from stdin
input , err = ioutil . ReadAll ( os . Stdin )
2016-08-28 20:28:28 +00:00
if err != nil {
TermMessage ( "Error reading from stdin: " , err )
input = [ ] byte { }
}
2017-04-29 18:12:00 +00:00
buffers = append ( buffers , NewBufferFromString ( string ( input ) , filename ) )
2016-06-03 20:41:09 +00:00
} else {
// Option 3, just open an empty buffer
2017-04-29 18:12:00 +00:00
buffers = append ( buffers , NewBufferFromString ( string ( input ) , filename ) )
2016-03-17 21:27:57 +00:00
}
2016-06-03 20:41:09 +00:00
return buffers
2016-03-25 16:14:22 +00:00
}
2016-05-28 15:32:09 +00:00
// InitConfigDir finds the configuration directory for micro according to the XDG spec.
2016-04-18 14:31:19 +00:00
// If no directory is found, it creates one.
func InitConfigDir ( ) {
xdgHome := os . Getenv ( "XDG_CONFIG_HOME" )
if xdgHome == "" {
2016-05-28 15:32:09 +00:00
// The user has not set $XDG_CONFIG_HOME so we should act like it was set to ~/.config
2016-04-18 14:31:19 +00:00
home , err := homedir . Dir ( )
if err != nil {
2016-05-28 15:32:09 +00:00
TermMessage ( "Error finding your home directory\nCan't load config files" )
2016-04-18 14:31:19 +00:00
return
}
2016-04-18 14:36:41 +00:00
xdgHome = home + "/.config"
}
configDir = xdgHome + "/micro"
2017-09-11 03:20:21 +00:00
if len ( * flagConfigDir ) > 0 {
if _ , err := os . Stat ( * flagConfigDir ) ; os . IsNotExist ( err ) {
TermMessage ( "Error: " + * flagConfigDir + " does not exist. Defaulting to " + configDir + "." )
} else {
configDir = * flagConfigDir
return
}
}
2016-04-18 14:36:41 +00:00
if _ , err := os . Stat ( xdgHome ) ; os . IsNotExist ( err ) {
2016-05-28 15:32:09 +00:00
// If the xdgHome doesn't exist we should create it
2016-04-18 14:36:41 +00:00
err = os . Mkdir ( xdgHome , os . ModePerm )
if err != nil {
TermMessage ( "Error creating XDG_CONFIG_HOME directory: " + err . Error ( ) )
}
2016-04-18 14:31:19 +00:00
}
if _ , err := os . Stat ( configDir ) ; os . IsNotExist ( err ) {
2016-05-28 15:32:09 +00:00
// If the micro specific config directory doesn't exist we should create that too
2016-04-18 14:31:19 +00:00
err = os . Mkdir ( configDir , os . ModePerm )
if err != nil {
TermMessage ( "Error creating configuration directory: " + err . Error ( ) )
}
}
}
2016-04-20 01:25:13 +00:00
// InitScreen creates and initializes the tcell screen
func InitScreen ( ) {
2016-03-25 16:14:22 +00:00
// Should we enable true color?
2016-03-22 14:31:08 +00:00
truecolor := os . Getenv ( "MICRO_TRUECOLOR" ) == "1"
2018-01-30 04:36:39 +00:00
tcelldb := os . Getenv ( "TCELLDB" )
os . Setenv ( "TCELLDB" , configDir + "/.tcelldb" )
2016-03-25 16:14:22 +00:00
// In order to enable true color, we have to set the TERM to `xterm-truecolor` when
// initializing tcell, but after that, we can set the TERM back to whatever it was
2016-03-22 14:31:08 +00:00
oldTerm := os . Getenv ( "TERM" )
if truecolor {
os . Setenv ( "TERM" , "xterm-truecolor" )
}
2016-03-25 16:14:22 +00:00
// Initilize tcell
2016-04-20 01:25:13 +00:00
var err error
2016-03-25 16:14:22 +00:00
screen , err = tcell . NewScreen ( )
if err != nil {
2017-06-04 23:17:02 +00:00
if err == tcell . ErrTermNotFound {
2018-08-28 18:26:21 +00:00
err = terminfo . WriteDB ( configDir + "/.tcelldb" )
if err != nil {
fmt . Println ( err )
fmt . Println ( "Fatal: Micro could not create tcelldb" )
os . Exit ( 1 )
}
2018-01-30 04:36:39 +00:00
screen , err = tcell . NewScreen ( )
if err != nil {
fmt . Println ( err )
fmt . Println ( "Fatal: Micro could not initialize a screen." )
os . Exit ( 1 )
}
2018-01-30 18:04:26 +00:00
} else {
fmt . Println ( err )
fmt . Println ( "Fatal: Micro could not initialize a screen." )
os . Exit ( 1 )
2017-06-04 23:17:02 +00:00
}
2016-03-17 21:27:57 +00:00
}
2016-03-25 16:14:22 +00:00
if err = screen . Init ( ) ; err != nil {
fmt . Println ( err )
2016-03-17 21:27:57 +00:00
os . Exit ( 1 )
}
2016-03-17 22:20:07 +00:00
2016-03-25 16:14:22 +00:00
// Now we can put the TERM back to what it was before
2016-03-22 14:31:08 +00:00
if truecolor {
os . Setenv ( "TERM" , oldTerm )
}
2017-09-21 21:10:53 +00:00
if GetGlobalOption ( "mouse" ) . ( bool ) {
screen . EnableMouse ( )
}
2018-01-30 04:36:39 +00:00
os . Setenv ( "TCELLDB" , tcelldb )
2018-01-04 22:03:08 +00:00
// screen.SetStyle(defStyle)
2016-04-20 01:25:13 +00:00
}
2016-05-28 15:32:09 +00:00
// RedrawAll redraws everything -- all the views and the messenger
func RedrawAll ( ) {
2016-05-31 21:23:08 +00:00
messenger . Clear ( )
2017-03-27 17:15:00 +00:00
w , h := screen . Size ( )
for x := 0 ; x < w ; x ++ {
for y := 0 ; y < h ; y ++ {
screen . SetContent ( x , y , ' ' , nil , defStyle )
}
}
2018-03-03 00:50:33 +00:00
for _ , v := range tabs [ curTab ] . Views {
2016-06-08 17:26:50 +00:00
v . Display ( )
}
2016-07-11 23:10:29 +00:00
DisplayTabs ( )
messenger . Display ( )
2017-10-03 03:44:11 +00:00
if globalSettings [ "keymenu" ] . ( bool ) {
DisplayKeyMenu ( )
}
2016-04-20 01:25:13 +00:00
screen . Show ( )
2018-01-29 21:47:55 +00:00
if numRedraw % 50 == 0 {
runtime . GC ( )
}
numRedraw ++
2016-04-20 01:25:13 +00:00
}
2016-10-21 15:51:36 +00:00
func LoadAll ( ) {
// Find the user's configuration directory (probably $XDG_CONFIG_HOME/micro)
InitConfigDir ( )
// Build a list of available Extensions (Syntax, Colorscheme etc.)
InitRuntimeFiles ( )
// Load the user's settings
InitGlobalSettings ( )
InitCommands ( )
InitBindings ( )
2017-05-02 14:30:27 +00:00
InitColorscheme ( )
2018-09-22 03:18:04 +00:00
LoadPlugins ( )
2016-10-21 15:51:36 +00:00
for _ , tab := range tabs {
2018-03-03 00:50:33 +00:00
for _ , v := range tab . Views {
2016-10-21 15:51:36 +00:00
v . Buf . UpdateRules ( )
}
}
}
2017-11-29 06:06:16 +00:00
// Command line flags
2016-09-03 15:26:01 +00:00
var flagVersion = flag . Bool ( "version" , false , "Show the version number and information" )
var flagStartPos = flag . String ( "startpos" , "" , "LINE,COL to start the cursor at when opening a buffer." )
2017-09-11 03:20:21 +00:00
var flagConfigDir = flag . String ( "config-dir" , "" , "Specify a custom location for the configuration directory" )
2017-09-18 02:11:26 +00:00
var flagOptions = flag . Bool ( "options" , false , "Show all option help" )
2016-08-30 15:28:28 +00:00
2016-04-20 01:25:13 +00:00
func main ( ) {
2016-09-03 15:26:01 +00:00
flag . Usage = func ( ) {
fmt . Println ( "Usage: micro [OPTIONS] [FILE]..." )
2017-09-18 02:11:26 +00:00
fmt . Println ( "-config-dir dir" )
fmt . Println ( " \tSpecify a custom location for the configuration directory" )
fmt . Println ( "-startpos LINE,COL" )
2018-02-02 21:53:08 +00:00
fmt . Println ( "+LINE:COL" )
2017-09-18 02:11:26 +00:00
fmt . Println ( " \tSpecify a line and column to start the cursor at when opening a buffer" )
2018-02-02 21:53:08 +00:00
fmt . Println ( " \tThis can also be done by opening file:LINE:COL" )
2017-09-18 02:11:26 +00:00
fmt . Println ( "-options" )
fmt . Println ( " \tShow all option help" )
fmt . Println ( "-version" )
fmt . Println ( " \tShow the version number and information" )
2017-12-28 21:05:35 +00:00
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" )
2017-09-18 02:11:26 +00:00
fmt . Println ( "-option value" )
fmt . Println ( " \tSet `option` to `value` for this session" )
fmt . Println ( " \tFor example: `micro -syntax off file.c`" )
fmt . Println ( "\nUse `micro -options` to see the full list of configuration options" )
2016-09-03 15:26:01 +00:00
}
optionFlags := make ( map [ string ] * string )
for k , v := range DefaultGlobalSettings ( ) {
2017-09-18 02:11:26 +00:00
optionFlags [ k ] = flag . String ( k , "" , fmt . Sprintf ( "The %s option. Default value: '%v'" , k , v ) )
2016-09-03 15:26:01 +00:00
}
2016-04-20 01:25:13 +00:00
flag . Parse ( )
2016-09-03 15:26:01 +00:00
2016-04-20 01:25:13 +00:00
if * flagVersion {
2016-07-10 17:26:05 +00:00
// If -version was passed
2016-08-28 00:03:43 +00:00
fmt . Println ( "Version:" , Version )
fmt . Println ( "Commit hash:" , CommitHash )
fmt . Println ( "Compiled on" , CompileDate )
2016-04-20 01:25:13 +00:00
os . Exit ( 0 )
}
2017-09-18 02:11:26 +00:00
if * flagOptions {
// If -options was passed
for k , v := range DefaultGlobalSettings ( ) {
fmt . Printf ( "-%s value\n" , k )
fmt . Printf ( " \tThe %s option. Default value: '%v'\n" , k , v )
}
os . Exit ( 0 )
}
2016-07-10 17:26:05 +00:00
// Start the Lua VM for running plugins
2016-04-24 23:52:02 +00:00
L = lua . NewState ( )
defer L . Close ( )
2016-05-28 15:32:09 +00:00
// Some encoding stuff in case the user isn't using UTF-8
2016-04-20 01:25:13 +00:00
encoding . Register ( )
2016-04-20 11:23:16 +00:00
tcell . SetEncodingFallback ( tcell . EncodingFallbackASCII )
2016-04-20 01:25:13 +00:00
2016-11-23 16:56:12 +00:00
// Find the user's configuration directory (probably $XDG_CONFIG_HOME/micro)
InitConfigDir ( )
// Build a list of available Extensions (Syntax, Colorscheme etc.)
InitRuntimeFiles ( )
// Load the user's settings
InitGlobalSettings ( )
InitCommands ( )
InitBindings ( )
2016-07-10 17:26:05 +00:00
// Start the screen
2016-04-20 01:25:13 +00:00
InitScreen ( )
// This is just so if we have an error, we can exit cleanly and not completely
// mess up the terminal being worked in
2016-05-28 15:32:09 +00:00
// In other words we need to shut down tcell before the program crashes
2016-04-20 01:25:13 +00:00
defer func ( ) {
if err := recover ( ) ; err != nil {
screen . Fini ( )
fmt . Println ( "Micro encountered an error:" , err )
// Print the stack trace too
fmt . Print ( errors . Wrap ( err , 2 ) . ErrorStack ( ) )
os . Exit ( 1 )
}
} ( )
2016-03-17 21:27:57 +00:00
2016-07-10 17:26:05 +00:00
// Create a new messenger
// This is used for sending the user messages in the bottom of the editor
2016-03-26 14:54:18 +00:00
messenger = new ( Messenger )
2017-10-21 19:31:04 +00:00
messenger . LoadHistory ( )
2016-06-08 17:26:50 +00:00
2016-07-10 17:26:05 +00:00
// Now we load the input
2016-06-03 20:41:09 +00:00
buffers := LoadInput ( )
2016-11-28 15:51:09 +00:00
if len ( buffers ) == 0 {
screen . Fini ( )
os . Exit ( 1 )
}
2017-05-02 14:30:27 +00:00
2016-06-08 17:26:50 +00:00
for _ , buf := range buffers {
2016-07-10 17:26:05 +00:00
// For each buffer we create a new tab and place the view in that tab
2016-06-08 23:43:05 +00:00
tab := NewTabFromView ( NewView ( buf ) )
tab . SetNum ( len ( tabs ) )
tabs = append ( tabs , tab )
2016-08-24 23:55:44 +00:00
for _ , t := range tabs {
2018-03-03 00:50:33 +00:00
for _ , v := range t . Views {
2016-08-24 23:55:44 +00:00
v . Center ( false )
}
2016-09-08 21:30:41 +00:00
t . Resize ( )
2016-08-24 23:55:44 +00:00
}
2016-06-03 20:41:09 +00:00
}
2016-03-23 14:28:12 +00:00
2016-09-03 15:26:01 +00:00
for k , v := range optionFlags {
if * v != "" {
SetOption ( k , * v )
}
}
2016-07-10 17:26:05 +00:00
// Load all the plugin stuff
// We give plugins access to a bunch of variables here which could be useful to them
2016-04-30 22:25:45 +00:00
L . SetGlobal ( "OS" , luar . New ( L , runtime . GOOS ) )
2016-06-08 17:26:50 +00:00
L . SetGlobal ( "tabs" , luar . New ( L , tabs ) )
2018-08-15 15:18:27 +00:00
L . SetGlobal ( "GetTabs" , luar . New ( L , func ( ) [ ] * Tab {
return tabs
} ) )
2016-06-08 17:26:50 +00:00
L . SetGlobal ( "curTab" , luar . New ( L , curTab ) )
2016-04-24 23:52:02 +00:00
L . SetGlobal ( "messenger" , luar . New ( L , messenger ) )
2016-04-30 16:13:21 +00:00
L . SetGlobal ( "GetOption" , luar . New ( L , GetOption ) )
L . SetGlobal ( "AddOption" , luar . New ( L , AddOption ) )
2016-08-25 19:03:37 +00:00
L . SetGlobal ( "SetOption" , luar . New ( L , SetOption ) )
2016-08-25 20:01:42 +00:00
L . SetGlobal ( "SetLocalOption" , luar . New ( L , SetLocalOption ) )
2016-05-30 17:38:50 +00:00
L . SetGlobal ( "BindKey" , luar . New ( L , BindKey ) )
L . SetGlobal ( "MakeCommand" , luar . New ( L , MakeCommand ) )
2016-06-08 17:26:50 +00:00
L . SetGlobal ( "CurView" , luar . New ( L , CurView ) )
2016-06-22 17:54:42 +00:00
L . SetGlobal ( "IsWordChar" , luar . New ( L , IsWordChar ) )
2016-08-13 15:44:30 +00:00
L . SetGlobal ( "HandleCommand" , luar . New ( L , HandleCommand ) )
L . SetGlobal ( "HandleShellCommand" , luar . New ( L , HandleShellCommand ) )
2018-01-21 04:34:16 +00:00
L . SetGlobal ( "ExecCommand" , luar . New ( L , ExecCommand ) )
L . SetGlobal ( "RunShellCommand" , luar . New ( L , RunShellCommand ) )
L . SetGlobal ( "RunBackgroundShell" , luar . New ( L , RunBackgroundShell ) )
L . SetGlobal ( "RunInteractiveShell" , luar . New ( L , RunInteractiveShell ) )
L . SetGlobal ( "TermEmuSupported" , luar . New ( L , TermEmuSupported ) )
L . SetGlobal ( "RunTermEmulator" , luar . New ( L , RunTermEmulator ) )
2016-08-19 22:14:34 +00:00
L . SetGlobal ( "GetLeadingWhitespace" , luar . New ( L , GetLeadingWhitespace ) )
2016-09-02 17:41:13 +00:00
L . SetGlobal ( "MakeCompletion" , luar . New ( L , MakeCompletion ) )
2016-11-29 18:44:30 +00:00
L . SetGlobal ( "NewBuffer" , luar . New ( L , NewBufferFromString ) )
2018-02-02 18:57:02 +00:00
L . SetGlobal ( "NewBufferFromFile" , luar . New ( L , NewBufferFromFile ) )
2016-09-19 11:23:47 +00:00
L . SetGlobal ( "RuneStr" , luar . New ( L , func ( r rune ) string {
return string ( r )
} ) )
L . SetGlobal ( "Loc" , luar . New ( L , func ( x , y int ) Loc {
return Loc { x , y }
} ) )
2017-08-27 16:18:56 +00:00
L . SetGlobal ( "WorkingDirectory" , luar . New ( L , os . Getwd ) )
2016-09-19 11:23:47 +00:00
L . SetGlobal ( "JoinPaths" , luar . New ( L , filepath . Join ) )
2016-11-18 16:53:48 +00:00
L . SetGlobal ( "DirectoryName" , luar . New ( L , filepath . Dir ) )
2016-09-19 11:23:47 +00:00
L . SetGlobal ( "configDir" , luar . New ( L , configDir ) )
2016-10-21 15:51:36 +00:00
L . SetGlobal ( "Reload" , luar . New ( L , LoadAll ) )
2016-10-29 00:34:28 +00:00
L . SetGlobal ( "ByteOffset" , luar . New ( L , ByteOffset ) )
L . SetGlobal ( "ToCharPos" , luar . New ( L , ToCharPos ) )
2016-04-30 16:13:21 +00:00
2016-07-10 17:26:05 +00:00
// Used for asynchronous jobs
2016-06-15 15:19:00 +00:00
L . SetGlobal ( "JobStart" , luar . New ( L , JobStart ) )
2016-11-16 05:06:12 +00:00
L . SetGlobal ( "JobSpawn" , luar . New ( L , JobSpawn ) )
2016-06-15 15:19:00 +00:00
L . SetGlobal ( "JobSend" , luar . New ( L , JobSend ) )
L . SetGlobal ( "JobStop" , luar . New ( L , JobStop ) )
2016-09-15 14:42:45 +00:00
// Extension Files
L . SetGlobal ( "ReadRuntimeFile" , luar . New ( L , PluginReadRuntimeFile ) )
L . SetGlobal ( "ListRuntimeFiles" , luar . New ( L , PluginListRuntimeFiles ) )
2016-09-16 15:02:10 +00:00
L . SetGlobal ( "AddRuntimeFile" , luar . New ( L , PluginAddRuntimeFile ) )
2016-09-27 18:24:52 +00:00
L . SetGlobal ( "AddRuntimeFilesFromDirectory" , luar . New ( L , PluginAddRuntimeFilesFromDirectory ) )
2017-04-15 18:45:44 +00:00
L . SetGlobal ( "AddRuntimeFileFromMemory" , luar . New ( L , PluginAddRuntimeFileFromMemory ) )
2016-09-15 14:42:45 +00:00
2017-09-09 21:37:08 +00:00
// Access to Go stdlib
L . SetGlobal ( "import" , luar . New ( L , Import ) )
2016-06-15 15:19:00 +00:00
jobs = make ( chan JobFunction , 100 )
2016-09-08 11:47:13 +00:00
events = make ( chan tcell . Event , 100 )
2016-09-28 17:07:05 +00:00
autosave = make ( chan bool )
2018-01-04 22:03:08 +00:00
updateterm = make ( chan bool )
closeterm = make ( chan int )
2016-06-15 15:19:00 +00:00
2016-09-19 14:04:59 +00:00
LoadPlugins ( )
2016-08-25 00:03:02 +00:00
for _ , t := range tabs {
2018-03-03 00:50:33 +00:00
for _ , v := range t . Views {
2018-01-08 22:08:11 +00:00
GlobalPluginCall ( "onViewOpen" , v )
GlobalPluginCall ( "onBufferOpen" , v . Buf )
2016-08-25 00:03:02 +00:00
}
}
2017-05-17 16:12:58 +00:00
InitColorscheme ( )
2018-01-08 22:08:11 +00:00
messenger . style = defStyle
2017-05-17 16:12:58 +00:00
2016-07-10 17:26:05 +00:00
// Here is the event loop which runs in a separate thread
2016-06-15 15:19:00 +00:00
go func ( ) {
for {
2017-07-10 22:04:11 +00:00
if screen != nil {
events <- screen . PollEvent ( )
}
2016-06-15 15:19:00 +00:00
}
} ( )
2016-09-28 17:07:05 +00:00
go func ( ) {
for {
time . Sleep ( autosaveTime * time . Second )
if globalSettings [ "autosave" ] . ( bool ) {
autosave <- true
}
}
} ( )
2016-03-17 21:27:57 +00:00
for {
2016-08-21 21:40:39 +00:00
// Display everything
RedrawAll ( )
2016-03-25 16:14:22 +00:00
2016-06-15 15:19:00 +00:00
var event tcell . Event
2016-07-10 17:26:05 +00:00
// Check for new events
2016-06-15 15:19:00 +00:00
select {
case f := <- jobs :
2016-07-10 17:26:05 +00:00
// If a new job has finished while running in the background we should execute the callback
2016-06-15 15:19:00 +00:00
f . function ( f . output , f . args ... )
continue
2016-09-28 17:07:05 +00:00
case <- autosave :
2017-12-13 17:43:00 +00:00
if CurView ( ) . Buf . Path != "" {
CurView ( ) . Save ( true )
}
2018-01-04 22:03:08 +00:00
case <- updateterm :
continue
case vnum := <- closeterm :
2018-03-03 00:50:33 +00:00
tabs [ curTab ] . Views [ vnum ] . CloseTerminal ( )
2016-06-15 15:19:00 +00:00
case event = <- events :
}
2016-09-08 11:47:13 +00:00
for event != nil {
2017-11-06 01:07:14 +00:00
didAction := false
2016-09-08 11:47:13 +00:00
switch e := event . ( type ) {
2017-02-20 18:08:14 +00:00
case * tcell . EventResize :
for _ , t := range tabs {
t . Resize ( )
}
2016-09-08 11:47:13 +00:00
case * tcell . EventMouse :
2017-05-05 14:52:09 +00:00
if ! searching {
if e . Buttons ( ) == tcell . Button1 {
// If the user left clicked we check a couple things
_ , h := screen . Size ( )
x , y := e . Position ( )
if y == h - 1 && messenger . message != "" && globalSettings [ "infobar" ] . ( bool ) {
// If the user clicked in the bottom bar, and there is a message down there
// we copy it to the clipboard.
// Often error messages are displayed down there so it can be useful to easily
// copy the message
clipboard . WriteAll ( messenger . message , "primary" )
break
}
2016-07-01 22:12:37 +00:00
2017-05-05 14:52:09 +00:00
if CurView ( ) . mouseReleased {
// We loop through each view in the current tab and make sure the current view
// is the one being clicked in
2018-03-03 00:50:33 +00:00
for _ , v := range tabs [ curTab ] . Views {
2017-05-05 14:52:09 +00:00
if x >= v . x && x < v . x + v . Width && y >= v . y && y < v . y + v . Height {
tabs [ curTab ] . CurView = v . Num
}
2016-09-08 11:47:13 +00:00
}
2016-08-30 21:29:49 +00:00
}
2017-11-06 01:07:14 +00:00
} else if e . Buttons ( ) == tcell . WheelUp || e . Buttons ( ) == tcell . WheelDown {
var view * View
x , y := e . Position ( )
2018-03-03 00:50:33 +00:00
for _ , v := range tabs [ curTab ] . Views {
2017-11-06 01:07:14 +00:00
if x >= v . x && x < v . x + v . Width && y >= v . y && y < v . y + v . Height {
2018-03-03 00:50:33 +00:00
view = tabs [ curTab ] . Views [ v . Num ]
2017-11-06 01:07:14 +00:00
}
}
2017-11-28 02:44:29 +00:00
if view != nil {
view . HandleEvent ( e )
didAction = true
}
2016-07-01 22:12:37 +00:00
}
}
2016-06-17 15:24:54 +00:00
}
2017-11-06 01:07:14 +00:00
if ! didAction {
// This function checks the mouse event for the possibility of changing the current tab
// If the tab was changed it returns true
if TabbarHandleMouseEvent ( event ) {
break
}
2016-09-08 11:47:13 +00:00
2017-11-06 01:07:14 +00:00
if searching {
// Since searching is done in real time, we need to redraw every time
// there is a new event in the search bar so we need a special function
// to run instead of the standard HandleEvent.
HandleSearchEvent ( event , CurView ( ) )
} else {
// Send it to the view
CurView ( ) . HandleEvent ( event )
}
2016-09-08 11:47:13 +00:00
}
select {
case event = <- events :
default :
event = nil
}
2016-04-16 13:55:40 +00:00
}
2016-03-17 21:27:57 +00:00
}
}