├── dcd ├── dcd.exe ├── dacecd.gif ├── .dacecdrc ├── install └── install.go ├── config ├── config_test.go └── config.go ├── dcd.sh ├── cd ├── cd_test.go └── cd.go ├── .gitattributes ├── .gitignore ├── panel ├── panel_test.go └── panel.go ├── line └── line.go ├── filter ├── filter_test.go └── filter.go ├── README.md └── dcd.go /dcd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dacez/dcd/HEAD/dcd -------------------------------------------------------------------------------- /dcd.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dacez/dcd/HEAD/dcd.exe -------------------------------------------------------------------------------- /dacecd.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dacez/dcd/HEAD/dacecd.gif -------------------------------------------------------------------------------- /.dacecdrc: -------------------------------------------------------------------------------- 1 | { 2 | "ContainDirs": [ 3 | "C:/Users/dace" 4 | ], 5 | "HisCount":100 6 | } 7 | -------------------------------------------------------------------------------- /install/install.go: -------------------------------------------------------------------------------- 1 | package install 2 | 3 | import ( 4 | _ "io/ioutil" 5 | ) 6 | 7 | func Install() { 8 | } 9 | -------------------------------------------------------------------------------- /config/config_test.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestGetConfig(t *testing.T) { 9 | fmt.Println(*GetConfig()) 10 | } 11 | -------------------------------------------------------------------------------- /dcd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | unalias cd 3 | alias dcd='$HOME/.dacecd/dcd' 4 | if [ $# == 0 ];then 5 | cd 6 | else 7 | cd $1 8 | fi 9 | dcd `pwd` 10 | unalias dcd 11 | alias cd='source $HOME/.dacecd/dcd.sh' 12 | -------------------------------------------------------------------------------- /cd/cd_test.go: -------------------------------------------------------------------------------- 1 | package cd 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestGetDirs(t *testing.T) { 9 | dirs := GetDirs() 10 | for _, v := range dirs { 11 | fmt.Println(v) 12 | } 13 | } 14 | 15 | func TestGetAllDir(t *testing.T) { 16 | dirs := make([]string, 0) 17 | GetAllDir("~/", &dirs) 18 | for _, v := range dirs { 19 | fmt.Println(v) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | dcd 45 | *.exe 46 | *.swp 47 | dcd 48 | -------------------------------------------------------------------------------- /panel/panel_test.go: -------------------------------------------------------------------------------- 1 | package panel 2 | 3 | import ( 4 | "dcd/cd" 5 | "github.com/nsf/termbox-go" 6 | "testing" 7 | ) 8 | 9 | func TestInit(t *testing.T) { 10 | err := termbox.Init() 11 | if err != nil { 12 | t.Fatal("termboxInit", err) 13 | } 14 | defer termbox.Close() 15 | 16 | var outputPanel Panel 17 | outputPanel.Init(0, 0, 80, 20, termbox.ColorWhite, termbox.ColorBlack, OutputType, 0, 0) 18 | var dirs []string 19 | cd.GetAllDir("E:\\GoProject\\src", &dirs) 20 | for _, v := range dirs { 21 | outputPanel.PushLine([]byte(v)) 22 | } 23 | outputPanel.StartFilter() 24 | 25 | var inputPanel Panel 26 | inputPanel.Init(0, 20, 80, 2, termbox.ColorWhite, termbox.ColorBlack, InputType, 0, 0) 27 | termbox.Flush() 28 | 29 | mainloop: 30 | for { 31 | switch ev := termbox.PollEvent(); ev.Type { 32 | case termbox.EventKey: 33 | switch ev.Key { 34 | case termbox.KeyEsc: 35 | break mainloop 36 | case termbox.KeyArrowDown: 37 | outputPanel.Down() 38 | case termbox.KeyArrowUp: 39 | outputPanel.Up() 40 | case termbox.KeyBackspace: 41 | inputPanel.Pop() 42 | default: 43 | if ev.Ch != 0 { 44 | inputPanel.Push(ev.Ch) 45 | outputPanel.FilterPush(string(ev.Ch)) 46 | } 47 | } 48 | } 49 | termbox.Clear(termbox.ColorDefault, termbox.ColorDefault) 50 | outputPanel.Draw() 51 | inputPanel.Draw() 52 | termbox.Flush() 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /line/line.go: -------------------------------------------------------------------------------- 1 | package line 2 | 3 | import ( 4 | "github.com/mattn/go-runewidth" 5 | "github.com/nsf/termbox-go" 6 | "unicode/utf8" 7 | ) 8 | 9 | type Line struct { 10 | Cs []termbox.Cell 11 | Bg termbox.Attribute 12 | Fg termbox.Attribute 13 | } 14 | 15 | func (p *Line) GetString() string { 16 | s := "" 17 | for _, v := range p.Cs { 18 | s += string(v.Ch) 19 | } 20 | return s 21 | } 22 | 23 | func (p *Line) PushCell(c termbox.Cell) { 24 | p.Cs = append(p.Cs, c) 25 | } 26 | 27 | func (p *Line) PopCell() int { 28 | if len(p.Cs) > 0 { 29 | w := runewidth.RuneWidth(p.Cs[len(p.Cs)-1].Ch) 30 | p.Cs = p.Cs[0 : len(p.Cs)-1] 31 | return w 32 | } 33 | return 0 34 | } 35 | 36 | func (p *Line) PushBytes(b []byte) { 37 | var utf8str []rune 38 | for i := 0; i < len(b); { 39 | r, l := utf8.DecodeRune(b[i:]) 40 | if l == 0 || r == utf8.RuneError { 41 | break 42 | } 43 | i += l 44 | if r == '\t' { 45 | utf8str = append(utf8str, ' ') 46 | utf8str = append(utf8str, ' ') 47 | } else { 48 | utf8str = append(utf8str, r) 49 | } 50 | } 51 | for _, v := range utf8str { 52 | p.Cs = append(p.Cs, termbox.Cell{Ch: v, Bg: p.Bg, Fg: p.Fg}) 53 | } 54 | } 55 | 56 | func (p *Line) GetHeight(width int) int { 57 | l := 0 58 | h := 1 59 | for _, v := range p.Cs { 60 | if l+runewidth.RuneWidth(v.Ch) > width { 61 | h++ 62 | l = runewidth.RuneWidth(v.Ch) 63 | } else { 64 | l += runewidth.RuneWidth(v.Ch) 65 | } 66 | } 67 | return h 68 | } 69 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "os" 7 | "runtime" 8 | ) 9 | 10 | type DirMode int 11 | type FindMode int 12 | 13 | const ( 14 | HisMode DirMode = 0 15 | AllMode DirMode = 1 16 | ) 17 | 18 | const ( 19 | NameMode FindMode = 0 20 | PathMode FindMode = 1 21 | ) 22 | 23 | type Config struct { 24 | ContainDirs []string 25 | HisCount int 26 | Home string 27 | DirectoryMode DirMode 28 | FuzzyFindMode FindMode 29 | } 30 | 31 | var conf Config 32 | 33 | func init() { 34 | home := "" 35 | confPath := "" 36 | if runtime.GOOS == "windows" { 37 | home = "C:" + os.Getenv("HOMEPATH") 38 | confPath = home + "\\.dacecd\\.dacecdrc" 39 | } else { 40 | home = os.Getenv("HOME") 41 | confPath = home + "/.dacecd/.dacecdrc" 42 | } 43 | content, _ := ioutil.ReadFile(confPath) 44 | json.Unmarshal(content, &conf) 45 | conf.Home = home 46 | } 47 | 48 | func GetConfig() *Config { 49 | return &conf 50 | } 51 | 52 | func GetStateLine() string { 53 | retStr := "Directory[" 54 | if GetConfig().DirectoryMode == AllMode { 55 | retStr += " All " 56 | } else if GetConfig().DirectoryMode == HisMode { 57 | retStr += "History" 58 | } else { 59 | return "" 60 | } 61 | retStr += "] (Ctrl-X) FindMode[" 62 | 63 | if GetConfig().FuzzyFindMode == NameMode { 64 | retStr += "Name" 65 | } else if GetConfig().FuzzyFindMode == PathMode { 66 | retStr += "Path" 67 | } else { 68 | return "" 69 | } 70 | retStr += "] (Ctrl-D)" 71 | return retStr 72 | } 73 | -------------------------------------------------------------------------------- /filter/filter_test.go: -------------------------------------------------------------------------------- 1 | package filter 2 | 3 | import ( 4 | "fmt" 5 | "github.com/dacez/dcd/line" 6 | "testing" 7 | ) 8 | 9 | func TestSlice(t *testing.T) { 10 | ls := make([]line.Line, 6) 11 | ls[0].PushBytes([]byte("zzz\\abcdefghijklmn")) 12 | ls[1].PushBytes([]byte("zzz\\abcdefghijklm")) 13 | ls[2].PushBytes([]byte("zzz\\abcdefghijkl")) 14 | ls[3].PushBytes([]byte("zzz\\abcdefghijk")) 15 | ls[4].PushBytes([]byte("zzz\\abcdefghij")) 16 | ls[5].PushBytes([]byte("zzz\\abcdefghi")) 17 | var f Filter 18 | f.Init(ls) 19 | f.Type = PathType 20 | fmt.Println("------1") 21 | f.Push("z") 22 | f.Push("a") 23 | f.Push("b") 24 | f.Push("c") 25 | f.Push("d") 26 | f.Push("e") 27 | f.Push("f") 28 | f.Push("g") 29 | f.Push("h") 30 | f.Print() 31 | 32 | fmt.Println("------2") 33 | f.Push("i") 34 | f.Print() 35 | 36 | fmt.Println("------3") 37 | f.Push("j") 38 | f.Print() 39 | 40 | fmt.Println("------4") 41 | f.Push("k") 42 | f.Print() 43 | 44 | fmt.Println("------5") 45 | f.Push("l") 46 | f.Print() 47 | 48 | fmt.Println("------6") 49 | f.Push("m") 50 | f.Print() 51 | 52 | fmt.Println("------7") 53 | f.Push("n") 54 | f.Print() 55 | 56 | fmt.Println("------8") 57 | f.Push("o") 58 | f.Print() 59 | } 60 | 61 | func TestSlice1(t *testing.T) { 62 | ls := make([]line.Line, 1) 63 | ls[0].PushBytes([]byte("E:\\GoProject\\src\\github.com\\gopherjs\\gopherjs\\node-syscall")) 64 | var f Filter 65 | f.Init(ls) 66 | f.Type = NameType 67 | f.Push("d") 68 | f.Push("a") 69 | f.Push("c") 70 | f.Push("e") 71 | f.Print() 72 | } 73 | -------------------------------------------------------------------------------- /filter/filter.go: -------------------------------------------------------------------------------- 1 | package filter 2 | 3 | import ( 4 | "fmt" 5 | "github.com/dacez/dcd/config" 6 | "github.com/dacez/dcd/line" 7 | "runtime" 8 | "sort" 9 | "strings" 10 | ) 11 | 12 | type FilterType int 13 | 14 | type ResultItem struct { 15 | Index int 16 | Line line.Line 17 | Pos int 18 | } 19 | 20 | type ResultItemSlice []ResultItem 21 | 22 | func (p ResultItemSlice) Len() int { 23 | return len(p) 24 | } 25 | 26 | func (p ResultItemSlice) Less(i, j int) bool { 27 | return len(p[i].Line.Cs)-p[i].Index < len(p[j].Line.Cs)-p[j].Index 28 | } 29 | 30 | func (p ResultItemSlice) Swap(i, j int) { 31 | p[i], p[j] = p[j], p[i] 32 | } 33 | 34 | type Filter struct { 35 | Results ResultItemSlice 36 | Type FilterType 37 | } 38 | 39 | func (p *Filter) Init(ls []line.Line) { 40 | p.Results = make(ResultItemSlice, 0) 41 | for i, v := range ls { 42 | p.Results = append(p.Results, ResultItem{Pos: i, Index: 0, Line: v}) 43 | } 44 | } 45 | 46 | func (p *Filter) Push(k string) { 47 | partResult := make(ResultItemSlice, 0) 48 | s := "/" 49 | if runtime.GOOS == "windows" { 50 | s = "\\" 51 | } 52 | for _, v := range p.Results { 53 | if v.Index >= len(v.Line.Cs) { 54 | continue 55 | } 56 | l := v.Line.GetString() 57 | if config.GetConfig().FuzzyFindMode == config.NameMode && v.Index == 0 { 58 | li := strings.LastIndex(l, s) 59 | if li != -1 && li <= len(l)-1 { 60 | if li == len(l)-1 { 61 | v.Index = li 62 | } else { 63 | v.Index = li + 1 64 | } 65 | } 66 | } 67 | k = strings.ToLower(k) 68 | l = strings.ToLower(l[v.Index:]) 69 | if strings.Contains(l, k) { 70 | v.Index += strings.Index(l, k) + len(k) 71 | partResult = append(partResult, v) 72 | } 73 | } 74 | if config.GetConfig().DirectoryMode == config.AllMode { 75 | sort.Sort(partResult) 76 | } 77 | p.Results = partResult 78 | } 79 | 80 | func (p *Filter) Print() { 81 | for _, v := range p.Results { 82 | fmt.Println(v.Line.GetString()) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dacecd 2 | ## 中文介绍 3 | 代替cd的一个工具,平时经常遇到在多个文件夹之前切换的问题,每次cd都要打很长的命令,费时费力。 4 | 该工具采用fuzzyfind的方法解决该问题,让你闪电般打开想要的文件夹,深藏功与名。 5 | 两种模式:历史模式和搜索模式 6 | 历史模式记录你打开过的文件夹,运行cd命令就会被记录,然后fuzzyfind。 7 | 搜索模式打开所有文件夹,然后fuzzyfind,如果文件夹太多,加载速度较慢,fuzzyfind还是很快。 8 | 默认模式为历史模式。 9 | 10 | ## Introduction 11 | like cd command 12 | but it can save the cd history and fuzzy find dirs 13 | cdl to play 14 | Ctrl-x to hash all directories 15 | Ctrl-d to change fuzzy find mode 16 | 17 | ## How To Use 18 | Type cdl to launch 19 | Ctrl - x to switch println all files or history files 20 | Ctrl - d to switch fuzzy find mode path or name 21 | Esc to exit 22 | Enter to go to the select directory 23 | Ctrl - j down the select line 24 | Ctrl - n down the select line 25 | Down down the select line 26 | Ctrl - k up the select line 27 | Ctrl - p up the select line 28 | Up up the select line 29 | 30 | 31 | 32 | ## How To Install (Linux 64bit Only) 33 | Download the execute file dcd (I compile the 64bit for linux only) 34 | Download the dcd.sh 35 | mkdir .dacecd in your homepath then move dcd.sh and dcd in it 36 | vim $Home/.dacecd/.dcd.sh 37 | 38 | #!/bin/bash 39 | unalias cd 40 | alias dcd='$Home/.dacecd/dcd' 41 | if [ $# == 0 ];then 42 | cd 43 | else 44 | cd $1 45 | fi 46 | dcd `pwd` 47 | unalias dcd 48 | alias cd='source $Home/.dacecd/dcd.sh' 49 | 50 | ## Source Install (32bit or 64bit) 51 | go get -u github.com/dacez/dcd 52 | 53 | ## How To Config 54 | 55 | ###First: 56 | vim $Home/.dacecd/.dacecdrc 57 | 58 | { 59 | "ContainDirs": [ 60 | "~/QQMail/micromsg", 61 | "~/QQMail/mmcomm" 62 | ], 63 | "HisCount":100 64 | } 65 | 66 | Modify the ContainDirs that you need to fuzzy. The .dacecdrc must be a json. 67 | ###Second: 68 | vim .profile or .bashrc 69 | Add two lines below: 70 | 71 | alias cd='source $Home/.dacecd/dcd.sh' 72 | alias cdl='$Home/.dacecd/dcd;source $Home/.dacecd/command.sh' 73 | -------------------------------------------------------------------------------- /cd/cd.go: -------------------------------------------------------------------------------- 1 | package cd 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/dacez/dcd/config" 6 | "io/ioutil" 7 | "os" 8 | "runtime" 9 | "strings" 10 | ) 11 | 12 | func GetAllDir(root string, dirs *[]string) { 13 | if root[0:1] == "~" { 14 | root = strings.Replace(root, "~", config.GetConfig().Home, 1) 15 | } 16 | ds, err := ioutil.ReadDir(root) 17 | if err != nil { 18 | return 19 | } 20 | s := "/" 21 | if runtime.GOOS == "windows" { 22 | s = "\\" 23 | } 24 | for _, v := range ds { 25 | if v.Name()[0] == '.' || v.IsDir() == false { 26 | continue 27 | } 28 | *dirs = append(*dirs, root+s+v.Name()) 29 | GetAllDir(root+s+v.Name(), dirs) 30 | } 31 | } 32 | 33 | func GetHis(dirs *[]string) { 34 | tmpDirs := make([]string, 0) 35 | content, _ := ioutil.ReadFile(config.GetConfig().Home + "/.dacecd/.dacecdhis") 36 | json.Unmarshal(content, &tmpDirs) 37 | curDir, _ := os.Getwd() 38 | for _, v := range tmpDirs { 39 | if v != curDir { 40 | *dirs = append(*dirs, v) 41 | } 42 | } 43 | } 44 | 45 | func RmHis(dir string) { 46 | if dir == "" { 47 | return 48 | } 49 | var dirs []string 50 | content, _ := ioutil.ReadFile(config.GetConfig().Home + "/.dacecd/.dacecdhis") 51 | json.Unmarshal(content, &dirs) 52 | var tmpDirs []string 53 | for _, v := range dirs { 54 | if v != dir && v != "" { 55 | tmpDirs = append(tmpDirs, v) 56 | } 57 | } 58 | if len(tmpDirs) > config.GetConfig().HisCount { 59 | tmpDirs = tmpDirs[0:config.GetConfig().HisCount] 60 | } 61 | w, _ := json.Marshal(tmpDirs) 62 | ioutil.WriteFile(config.GetConfig().Home+"/.dacecd/.dacecdhis", w, os.ModePerm) 63 | } 64 | 65 | func PushHis(dir string) { 66 | if dir == "" { 67 | return 68 | } 69 | var dirs []string 70 | content, _ := ioutil.ReadFile(config.GetConfig().Home + "/.dacecd/.dacecdhis") 71 | json.Unmarshal(content, &dirs) 72 | var tmpDirs []string 73 | tmpDirs = append(tmpDirs, dir) 74 | for _, v := range dirs { 75 | if v != dir && v != "" { 76 | tmpDirs = append(tmpDirs, v) 77 | } 78 | } 79 | if len(tmpDirs) > config.GetConfig().HisCount { 80 | tmpDirs = tmpDirs[0:config.GetConfig().HisCount] 81 | } 82 | w, _ := json.Marshal(tmpDirs) 83 | ioutil.WriteFile(config.GetConfig().Home+"/.dacecd/.dacecdhis", w, os.ModePerm) 84 | } 85 | 86 | func GetDirs() []string { 87 | var retDirs []string 88 | var dirs []string 89 | if config.GetConfig().DirectoryMode == config.HisMode { 90 | GetHis(&dirs) 91 | retDirs = append(retDirs, dirs...) 92 | } else if config.GetConfig().DirectoryMode == config.AllMode { 93 | for _, v := range config.GetConfig().ContainDirs { 94 | dirs = make([]string, 0) 95 | GetAllDir(v, &dirs) 96 | retDirs = append(retDirs, dirs...) 97 | } 98 | } 99 | return retDirs 100 | } 101 | -------------------------------------------------------------------------------- /dcd.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/dacez/dcd/cd" 5 | "github.com/dacez/dcd/config" 6 | "github.com/dacez/dcd/panel" 7 | "github.com/nsf/termbox-go" 8 | "io/ioutil" 9 | "os" 10 | "os/exec" 11 | "runtime" 12 | ) 13 | 14 | func main() { 15 | if len(os.Args) > 1 { 16 | cd.PushHis(os.Args[1]) 17 | return 18 | } 19 | err := termbox.Init() 20 | if err != nil { 21 | return 22 | } 23 | defer termbox.Close() 24 | 25 | home := config.GetConfig().Home 26 | 27 | os.Mkdir(home+"/.dacecd", os.ModeDir|os.ModePerm) 28 | 29 | w, h := termbox.Size() 30 | 31 | var inputPanel panel.Panel 32 | inputPanel.Init(0, 0, w, 2, termbox.ColorWhite, termbox.ColorBlack, 33 | panel.InputType, 0, 0) 34 | 35 | var outputPanel panel.Panel 36 | outputPanel.Init(0, 2, w, h-4, termbox.ColorWhite, termbox.ColorBlack, 37 | panel.OutputType, 0, 0) 38 | dirs := cd.GetDirs() 39 | outputPanel.InitBuffers(dirs) 40 | 41 | var statePanel panel.Panel 42 | statePanel.Init(0, h-2, w, 2, termbox.ColorWhite, termbox.ColorBlack, 43 | panel.OutputType, 0, 0) 44 | statePanel.InitBuffer(config.GetStateLine()) 45 | 46 | inputPanel.Draw() 47 | outputPanel.Draw() 48 | statePanel.Draw() 49 | termbox.Flush() 50 | 51 | mainloop: 52 | for { 53 | switch ev := termbox.PollEvent(); ev.Type { 54 | case termbox.EventKey: 55 | switch ev.Key { 56 | case termbox.KeyEsc, termbox.KeyCtrlC: 57 | ioutil.WriteFile(home+"/.dacecd/command.sh", []byte("cd ."), os.ModePerm) 58 | break mainloop 59 | case termbox.KeyArrowDown, termbox.KeyCtrlJ, termbox.KeyCtrlN: 60 | outputPanel.Down() 61 | case termbox.KeyCtrlR: 62 | l := outputPanel.GetSelectLine() 63 | outputPanel.DelSelectLine() 64 | cd.RmHis(l.GetString()) 65 | case termbox.KeyCtrlX: 66 | if config.GetConfig().DirectoryMode == config.HisMode { 67 | config.GetConfig().DirectoryMode = config.AllMode 68 | } else if config.GetConfig().DirectoryMode == config.AllMode { 69 | config.GetConfig().DirectoryMode = config.HisMode 70 | } 71 | dirs = cd.GetDirs() 72 | outputPanel.InitBuffers(dirs) 73 | for _, v := range inputPanel.GetSelectLine().Cs { 74 | outputPanel.FilterPush(string(v.Ch)) 75 | } 76 | case termbox.KeyCtrlD: 77 | if config.GetConfig().FuzzyFindMode == config.NameMode { 78 | config.GetConfig().FuzzyFindMode = config.PathMode 79 | } else if config.GetConfig().FuzzyFindMode == config.PathMode { 80 | config.GetConfig().FuzzyFindMode = config.NameMode 81 | } 82 | dirs = cd.GetDirs() 83 | outputPanel.InitBuffers(dirs) 84 | for _, v := range inputPanel.GetSelectLine().Cs { 85 | outputPanel.FilterPush(string(v.Ch)) 86 | } 87 | case termbox.KeyArrowUp, termbox.KeyCtrlK, termbox.KeyCtrlP: 88 | outputPanel.Up() 89 | case termbox.KeyBackspace: 90 | inputPanel.Pop() 91 | outputPanel.InitFilter() 92 | for _, v := range inputPanel.GetSelectLine().Cs { 93 | outputPanel.FilterPush(string(v.Ch)) 94 | } 95 | case termbox.KeyEnter: 96 | sl := outputPanel.GetSelectLine() 97 | godir := sl.GetString() 98 | if runtime.GOOS == "windows" { 99 | cmd := exec.Command("explorer.exe", "/root,"+godir) 100 | cmd.Run() 101 | } else { 102 | ioutil.WriteFile(home+"/.dacecd/command.sh", []byte("cd "+godir), os.ModePerm) 103 | } 104 | cd.PushHis(godir) 105 | break mainloop 106 | default: 107 | if ev.Ch != 0 { 108 | inputPanel.Push(ev.Ch) 109 | outputPanel.FilterPush(string(ev.Ch)) 110 | } 111 | } 112 | } 113 | termbox.Clear(termbox.ColorDefault, termbox.ColorDefault) 114 | inputPanel.Draw() 115 | outputPanel.Draw() 116 | statePanel.InitBuffer(config.GetStateLine()) 117 | statePanel.Draw() 118 | termbox.Flush() 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /panel/panel.go: -------------------------------------------------------------------------------- 1 | package panel 2 | 3 | import ( 4 | "github.com/dacez/dcd/filter" 5 | "github.com/dacez/dcd/line" 6 | "github.com/mattn/go-runewidth" 7 | "github.com/nsf/termbox-go" 8 | ) 9 | 10 | type PanelType uint32 11 | 12 | const ( 13 | InputType PanelType = iota 14 | OutputType 15 | ) 16 | 17 | type Panel struct { 18 | X int 19 | Y int 20 | Width int 21 | Height int 22 | Bg termbox.Attribute 23 | Fg termbox.Attribute 24 | 25 | Type PanelType 26 | PosX int 27 | PosY int 28 | 29 | ft filter.Filter 30 | 31 | buffers []line.Line 32 | lines []line.Line 33 | startLine int 34 | selectLine int 35 | } 36 | 37 | func (p *Panel) drawEdge() { 38 | for i := p.X; i < p.X+p.Width; i++ { 39 | termbox.SetCell(i, p.Y+p.Height-1, '-', termbox.ColorGreen, p.Bg) 40 | } 41 | for i := p.Y; i < p.Y+p.Height; i++ { 42 | termbox.SetCell(p.X+p.Width-1, i, '|', termbox.ColorGreen, p.Bg) 43 | } 44 | } 45 | 46 | func (p *Panel) drawCursor() { 47 | if p.Type == InputType { 48 | termbox.SetCursor(p.X+p.PosX, p.Y+p.PosY) 49 | } else if p.Type == OutputType { 50 | } else { 51 | } 52 | } 53 | 54 | func (p *Panel) drawLines() { 55 | if p.Type == InputType { 56 | p.lines = p.buffers 57 | } 58 | if p.selectLine < p.startLine { 59 | p.startLine = p.selectLine 60 | } 61 | minStartLine := 0 62 | linesHeight := 0 63 | for i := p.selectLine; i >= 0 && i < len(p.lines); i-- { 64 | if p.lines[i].GetHeight(p.Width)+linesHeight >= p.Height { 65 | break 66 | } else { 67 | linesHeight += p.lines[i].GetHeight(p.Width) 68 | minStartLine = i 69 | } 70 | } 71 | if minStartLine > p.startLine { 72 | p.startLine = minStartLine 73 | } 74 | endLine := 0 75 | linesHeight = 0 76 | for i := p.startLine; i < len(p.lines); i++ { 77 | if p.lines[i].GetHeight(p.Width)+linesHeight >= p.Height { 78 | break 79 | } else { 80 | linesHeight += p.lines[i].GetHeight(p.Width) 81 | endLine = i 82 | } 83 | } 84 | yIndex := 0 85 | xIndex := 0 86 | for i := p.startLine; i <= endLine && i < len(p.lines); i++ { 87 | xIndex = 0 88 | for _, v := range p.lines[i].Cs { 89 | if xIndex+runewidth.RuneWidth(v.Ch) < p.Width { 90 | } else { 91 | yIndex++ 92 | xIndex = 0 93 | } 94 | if i == p.selectLine && p.Type == OutputType { 95 | termbox.SetCell(p.X+xIndex, p.Y+yIndex, v.Ch, v.Fg|p.lines[i].Fg, v.Bg|termbox.ColorBlue) 96 | } else { 97 | termbox.SetCell(p.X+xIndex, p.Y+yIndex, v.Ch, v.Fg|p.lines[i].Fg, v.Bg|p.lines[i].Bg) 98 | } 99 | xIndex += runewidth.RuneWidth(v.Ch) 100 | } 101 | yIndex++ 102 | } 103 | } 104 | 105 | func (p *Panel) Init(x, y, w, h int, fg, bg termbox.Attribute, t PanelType, px, py int) { 106 | p.X = x 107 | p.Y = y 108 | p.Width = w 109 | p.Height = h 110 | p.Bg = bg 111 | p.Fg = fg 112 | p.Type = t 113 | p.PosX = px 114 | p.PosY = py 115 | p.selectLine = 0 116 | } 117 | 118 | func (p *Panel) InitBuffer(str string) { 119 | tmpStrs := make([]string, 0) 120 | tmpStrs = append(tmpStrs, str) 121 | p.InitBuffers(tmpStrs) 122 | } 123 | 124 | func (p *Panel) InitBuffers(strs []string) { 125 | if p.Type == InputType { 126 | return 127 | } 128 | p.selectLine = 0 129 | p.buffers = make([]line.Line, 0) 130 | for _, v := range strs { 131 | p.PushLine([]byte(v)) 132 | } 133 | p.InitFilter() 134 | } 135 | 136 | func (p *Panel) PushLine(b []byte) { 137 | l := line.Line{Fg: p.Fg, Bg: p.Bg} 138 | l.PushBytes(b) 139 | p.buffers = append(p.buffers, l) 140 | } 141 | 142 | func (p *Panel) filterToLine() { 143 | p.lines = make([]line.Line, 0) 144 | for _, v := range p.ft.Results { 145 | p.lines = append(p.lines, p.buffers[v.Pos]) 146 | } 147 | } 148 | 149 | func (p *Panel) InitFilter() { 150 | p.ft.Init(p.buffers) 151 | p.filterToLine() 152 | } 153 | 154 | func (p *Panel) FilterPush(s string) { 155 | p.ft.Push(s) 156 | p.filterToLine() 157 | p.selectLine = 0 158 | } 159 | 160 | func (p *Panel) Draw() { 161 | p.drawEdge() 162 | p.drawCursor() 163 | p.drawLines() 164 | } 165 | 166 | func (p *Panel) Up() { 167 | if p.selectLine > 0 && p.Type == OutputType { 168 | p.selectLine-- 169 | } 170 | } 171 | 172 | func (p *Panel) Down() { 173 | if p.selectLine < len(p.lines)-1 && p.Type == OutputType { 174 | p.selectLine++ 175 | } 176 | } 177 | 178 | func (p *Panel) Push(Ch rune) { 179 | if p.PosX >= p.Width-2 || p.Type == OutputType { 180 | return 181 | } 182 | p.PosX += runewidth.RuneWidth(Ch) 183 | if len(p.buffers) == 0 { 184 | l := line.Line{Fg: p.Fg, Bg: p.Bg} 185 | p.buffers = append(p.buffers, l) 186 | } 187 | p.buffers[0].PushCell(termbox.Cell{Ch: Ch, Fg: p.Fg, Bg: p.Bg}) 188 | } 189 | 190 | func (p *Panel) Pop() { 191 | if p.PosX <= 0 || p.Type == OutputType { 192 | return 193 | } 194 | p.PosX -= p.buffers[0].PopCell() 195 | } 196 | 197 | func (p *Panel) GetSelectLine() line.Line { 198 | if p.Type == InputType { 199 | if len(p.lines) != 1 { 200 | return line.Line{} 201 | } 202 | return p.lines[0] 203 | } else if p.Type == OutputType { 204 | if len(p.lines) <= 0 { 205 | return line.Line{} 206 | } 207 | return p.lines[p.selectLine] 208 | } 209 | return line.Line{} 210 | } 211 | 212 | func (p *Panel) DelSelectLine() { 213 | if p.Type == InputType { 214 | return 215 | } else if p.Type == OutputType { 216 | var ls []line.Line 217 | for i, v := range p.lines { 218 | if i != p.selectLine { 219 | ls = append(ls, v) 220 | } 221 | } 222 | p.lines = ls 223 | return 224 | } 225 | return 226 | } 227 | --------------------------------------------------------------------------------