Skip to content

Commit

Permalink
feat: implemented fuzzy cd (zoxide)
Browse files Browse the repository at this point in the history
  • Loading branch information
tsukinoko-kun committed Oct 2, 2024
1 parent 20fc755 commit d5a1be0
Show file tree
Hide file tree
Showing 15 changed files with 577 additions and 35 deletions.
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ tab_width = 4
indent_size = 4

[*.md]
indent_size = 4
indent_size = 2
trim_trailing_whitespace = false

eclint_indent_style = unset
Expand Down
42 changes: 26 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,40 @@ There are some bugs on Windows because I don't use Windows. Feel free to open PR

## Features

- Substitute environment variables and ~
- Tab completion
- Configurable and interactive prompt
- Command history
- Command aliases
- Context prompt (e.g. git branch)
- Context aware completions
- Substitute environment variables and ~
- Tab completion
- Configurable and interactive prompt
- Command history
- Command aliases
- Context prompt (e.g. git branch)
- Context aware completions

### Builtins

- `calc` (simple calculator)
- `cd` (change directory)
- `echo` (print arguments to stdout)
- `exit` (exit shell session)
- `printf` (print formatted string to stdout)
- `time` (time command execution)
- `zu` (similar to [ajeetdsouza/zoxide](https://github.com/ajeetdsouza/zoxide), change directory based on history and frequency)

## Upcoming

- More configuration options
- More builtins
- More configuration options
- More builtins

## Configuration

`~/.config/smash/config.toml`

- `ps1`: Prompt shown on user input
- `ps2`: Prompt shown on the executed commands
- `alias`: Map of command aliases where keys can be a string or list of strings
- `color`: Map of colors supporting all [charmbracelet/lipgloss](https://github.com/charmbracelet/lipgloss) colors
- `completion_text`: text color of completion list
- `completion_selected_bg`: background color of selected completion entry
- `on_start`: List of commands to run on shell start
- `ps1`: Prompt shown on user input
- `ps2`: Prompt shown on the executed commands
- `alias`: Map of command aliases where keys can be a string or list of strings
- `color`: Map of colors supporting all [charmbracelet/lipgloss](https://github.com/charmbracelet/lipgloss) colors
- `completion_text`: text color of completion list
- `completion_selected_bg`: background color of selected completion entry
- `on_start`: List of commands to run on shell start

## Install

Expand Down
33 changes: 33 additions & 0 deletions cmd/proto/proto.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package main

import (
"fmt"
"os"
"os/exec"
"path/filepath"
)

func main() {
// find all protobuf files
filepath.WalkDir(".", func(path string, d os.DirEntry, err error) error {
if err != nil {
return err
}
if filepath.Ext(path) == ".proto" {
fmt.Fprintf(os.Stderr, "compiling %s\n", path)
// compile the protobuf file
if err := run("protoc", "--go_out=.", path); err != nil {
return err
}
}
return nil
})
}

func run(name string, args ...string) error {
cmd := exec.Command(name, args...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/charmbracelet/bubbletea v1.1.1
github.com/charmbracelet/lipgloss v0.13.0
github.com/tsukinoko-kun/calc v1.1.2
google.golang.org/protobuf v1.34.2
)

require (
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4h
github.com/charmbracelet/x/term v0.2.0/go.mod h1:GVxgxAbjUrmpvIINHIQnJJKpMlHiZ4cktEQCN6GWyF0=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
Expand Down Expand Up @@ -45,3 +47,7 @@ golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
6 changes: 6 additions & 0 deletions internal/assert/assert.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,9 @@ func SmallerThan[T cmp.Ordered](x, smallerThan T, errorMessage string) {
panic(fmt.Sprintf("assertion failed: SmallerThan(%v, %v): %s", x, smallerThan, errorMessage))
}
}

func NotNil[T any](x *T, errorMessage string) {
if x == nil {
panic(fmt.Sprintf("assertion failed: NotNil(%v): %s", x, errorMessage))
}
}
2 changes: 2 additions & 0 deletions internal/env/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ var (
Config *smashConfig
Alias map[string][]string
HistoryFile string
ZuFile string
)

func init() {
loadVars()
HistoryFile = filepath.Join(getConfigDir(), "history.txt")
ZuFile = filepath.Join(getConfigDir(), "zu.bin")
Config = getConfigFile()
if Config.Alias != nil {
Alias = make(map[string][]string, len(Config.Alias))
Expand Down
54 changes: 38 additions & 16 deletions internal/shell/history/history.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import (
"smash/internal/env"
)

const maxHistory = 512
const (
historyHwm = 128
historyLwm = 64
)

var (
history []string
Expand All @@ -24,9 +27,10 @@ func loadHistory() error {
scanner := bufio.NewScanner(file)
for scanner.Scan() {
history = append(history, scanner.Text())
if len(history) > maxHistory {
history = history[1:]
}
}

if len(history) > historyHwm {
history = history[historyLwm:]
}

currentIndex = len(history)
Expand Down Expand Up @@ -66,35 +70,53 @@ func HistoryForward() (string, bool) {

// AddToHistory adds a new entry to the history
func AddToHistory(entry string) error {
// ensure the latest history is loaded
if len(history) == 0 {
if err := loadHistory(); err != nil {
return err
}
}

// check if the latest history entry is the same as the new entry
if len(history) != 0 {
latestHistoryEntry := history[len(history)-1]
if latestHistoryEntry == entry {
return nil
}
}

// append new entry to history in memory
history = append(history, entry)
if len(history) > maxHistory {
history = history[1:]
}
currentIndex = len(history)

// Append to file
file, err := os.OpenFile(env.HistoryFile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
return fmt.Errorf("failed to open history file: %w", err)
}
defer file.Close()
// ensure history length does not exceed the high water mark
if len(history) > historyHwm {
history = history[historyLwm:]

if _, err := fmt.Fprintln(file, entry); err != nil {
return fmt.Errorf("failed to write to history file: %w", err)
// write whole history to history file
file, err := os.Create(env.HistoryFile)
if err != nil {
return fmt.Errorf("failed to open history file: %w", err)
}
defer file.Close()
writer := bufio.NewWriter(file)
defer writer.Flush()
for _, entry := range history {
if _, err := writer.WriteString(entry + "\n"); err != nil {
return fmt.Errorf("failed to write to history file: %w", err)
}
}
} else {
// append new entry to history file
file, err := os.OpenFile(env.HistoryFile, os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
return fmt.Errorf("failed to open history file: %w", err)
}
defer file.Close()
if _, err := file.WriteString(entry + "\n"); err != nil {
return fmt.Errorf("failed to write to history file: %w", err)
}
}
currentIndex = len(history)

return nil
}
Expand Down
25 changes: 25 additions & 0 deletions internal/shell/parser/exe.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"os/exec"
"smash/internal/env"
"smash/internal/shell/parser/zu"
"smash/internal/system"
"strconv"
"strings"
Expand Down Expand Up @@ -250,6 +251,30 @@ func (e *exe) cd() error {
}
}

func (e *exe) z() error {
switch len(e.Args) {
case 0:
return pushDir(env.GetUser().HomeDir)
case 1:
if e.Args[0] == "-c" {
return zu.Clear()
} else if e.Args[0] == "-l" {
if l, err := zu.List(); err != nil {
return err
} else {
for _, e := range l {
_, _ = fmt.Println(e)
}
return nil
}
} else {
return zu.To(e.Args[0])
}
default:
return errors.New("z: too many operands")
}
}

func (e *exe) smashfetch(stdout io.Writer) error {
_, _ = fmt.Fprintln(stdout, system.SmashFetch())
return nil
Expand Down
3 changes: 3 additions & 0 deletions internal/shell/parser/shell_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ var InternalToolNames = [...]string{
"time",
"calc",
"cd",
"zu",
}

func (e *exe) internal(stdin io.Reader, stdout io.Writer, stderr io.Writer) (bool, error) {
Expand All @@ -27,6 +28,8 @@ func (e *exe) internal(stdin io.Reader, stdout io.Writer, stderr io.Writer) (boo
return true, e.calc(stdin, stdout)
case "cd":
return true, e.cd()
case "zu":
return true, e.z()
case "smashfetch":
return true, e.smashfetch(stdout)
case "sleep":
Expand Down
3 changes: 3 additions & 0 deletions internal/shell/parser/shell_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ var InternalToolNames = [...]string{
"time",
"calc",
"cd",
"zu",
"env",
}

Expand All @@ -33,6 +34,8 @@ func (e *exe) internal(stdin io.Reader, stdout io.Writer, stderr io.Writer) (boo
return true, e.calc(stdin, stdout)
case "cd":
return true, e.cd()
case "zu":
return true, e.z()
case "smashfetch":
return true, e.smashfetch(stdout)
case "sleep":
Expand Down
Loading

0 comments on commit d5a1be0

Please sign in to comment.