Skip to content

Commit

Permalink
Merge branch 'main' into feature/global-tree-search
Browse files Browse the repository at this point in the history
  • Loading branch information
wesleymartincc committed Jan 3, 2025
2 parents 8be9b73 + fec43c9 commit e1b8d86
Show file tree
Hide file tree
Showing 40 changed files with 1,679 additions and 377 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
uses: actions/setup-go@v5

- name: Golangci-lint
uses: golangci/golangci-lint-action@v6.0.1
uses: golangci/golangci-lint-action@v6

- name: Test
run: go test -v ./...
Expand Down
5 changes: 5 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ linters-settings:
# report when replacing `errors.New(fmt.Sprintf())` with `fmt.Errorf()` is possible
- name: errorf

# enforces conventions on source file names
- name: filename-format
arguments: ["^[_a-z][_a-z0-9]*\\.go$"]

# incrementing an integer variable by 1 is recommended to be done using the `++` operator
- name: increment-decrement

Expand Down Expand Up @@ -140,6 +144,7 @@ linters-settings:

# warns when initialism, variable or package naming conventions are not followed.
- name: var-naming
arguments: [[], ["DB", "DML"]]

# if-then-else conditional with identical implementations in both branches is an error.
- name: identical-branches
Expand Down
140 changes: 136 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
</ul>
</li>
<li><a href="#usage">Usage</a></li>
<li><a href="#commands">Commands</a></li>
<li><a href="#keybindings">Keybindings</a></li>
<li><a href="#roadmap">Roadmap</a></li>
<li><a href="#contributing">Contributing</a></li>
Expand Down Expand Up @@ -132,22 +133,153 @@ makepkg -si

## Usage

```bash
> For a list of keyboard shortcuts press `?`
Open the TUI with:
```console
$ lazysql
```

To launch lazysql with the ability to pick from the saved connections.
```console
$ lazysql [connection_url]
```

To launch lazysql and connect to database at [connection_url].

### Connect to a DB

1. Start `lazysql`
2. Create a new connection (press `n`)
3. Provide a name for the connection as well as the URL to connect to (see <a href="#example-connection-urls">example connection URL</a>)
4. Connect to the DB (press `<Enter>`)

If you already have a connection set up:
1. Start `lazysql`
2. Select the right connection (press `j` and `h` for navigation)
3. Connect to the DB (press `c` or `<Enter>`)

### Create a table

There is currently no way to create a table from the TUI.
However you can run the query to create the table as a SQL-Query,
inside the <a href="#execute-sql-querys">SQL Editor</a>.

You can update the tree by pressing `R`, so you can see your newly created table.

### Execute SQL querys

1. Press `<Ctrl+E>` to open the built-in SQL Editor
2. Write the SQL query
3. Press `<Ctrl+R>` to execute the SQL query

> To switch back to the table-tree press `H`
>
> After executing a `SELECT`-query a table will be displayed under the SQL-Editor
> with the query-result. \
> To switch focus back to SQL-Editor press `/`
### Open/view a table

1. Expand the table-tree by pressing `e` or `<Enter>`
2. Select the table you want to view
- next node `j`
- previous node `k`
- last node `G`
- first node `g`
3. Press `<Enter>` to open the table

> To switch back to the table-tree press `H` \
> To switch back to the table press `L`
### Filter rows

1. [Open a table](#openview-a-table)
2. Press `/` to focus the filter input
3. Write a `WHERE`-clause to filter the table
4. Press `<Enter>` to submit your filter

> To remove the filter, focuse the filter input (press `/`) and press `<Esc>`.
### Insert a row

1. [Open a table](#openview-a-table)
2. Press `1` to switch to the record tab
3. Press `o` to insert a new row
4. Fill out all columns
5. Press `<Ctrl+S>` to save the changes

### Edit a column

1. [Open a table](#openview-a-table)
2. Press `1` to switch to the record tab
3. Move to the column you want to edit
4. Press `c` to edit, Press `<Enter>` to submit
5. Press `<Ctrl+S>` to save the changes

<p align="right">(<a href="#readme-top">back to top</a>)</p>

## Support

- [x] MySQL
- [x] PostgreSQL
- [x] SQLite
- [ ] MSSQL
- [x] MSSQL
- [ ] MongoDB

Support for multiple RDBMS is a work in progress.

<!-- COMMANDS -->

## Commands

In some cases, mostly when connecting to remote databases, it might be necessary to run a custom command
before being able to connect to the database. For example when you can only access the database through
a remote bastion, you would probably first need to open an SSH tunnel by running the following command
in a separate terminal:

```bash
ssh remote-bastion -L 5432:localhost:5432
```

In order to make it easier to run these commands, lazysql supports running custom commands before connecting
to the database. You can define these commands in the configuration file like this:

```toml
[[database]]
Name = 'server'
Provider = 'postgres'
DBName = 'foo'
URL = 'postgres://postgres:password@localhost:${port}/foo'
Commands = [
{ Command = 'ssh -tt remote-bastion -L ${port}:localhost:5432', WaitForPort = '${port}' }
]
```

The `Command` field is required and can contain any command that you would normally run in your terminal.
The `WaitForPort` field is optional and can be used to wait for a specific port to be open before continuing.

When you define the `${port}` variable in the URL field, lazysql will automatically replace it with a random
free port number. This port number will then be used in the connection URL and is available in the `Commands`
field so that you can use it to configure the command.

You can even chain commands to, for example, connect to a remote server and then to a postgres container
running in a remote k8s cluster:

```toml
[[database]]
Name = 'container'
Provider = 'postgres'
DBName = 'foo'
URL = 'postgres://postgres:password@localhost:${port}/foo'
Commands = [
{ Command = 'ssh -tt remote-bastion -L 6443:localhost:6443', WaitForPort = '6443' },
{ Command = 'kubectl port-forward service/postgres ${port}:5432 --kubeconfig /path/to/kube.conf', WaitForPort = '${port}' }
]
```

<!-- KEYBINDINGS -->

## Keybindings

### Global
Expand All @@ -157,7 +289,7 @@ Support for multiple RDBMS is a work in progress.
| q | Quit |
| CTRL + e | Open SQL editor |
| Backspace | Return to connection selection |
| ? | Show keybindings popup |
| ? | Show keybindings popup |

### Table

Expand Down Expand Up @@ -191,7 +323,7 @@ Support for multiple RDBMS is a work in progress.
| Key | Action |
| ------------ | --------------------------------- |
| CTRL + R | Run the SQL statement |
| CTRL + Space | Open external editor (Linux only) |
| CTRL + Space | Open external editor (Linux only) |

Specific editor for lazysql can be set by `$SQL_EDITOR`.

Expand Down
36 changes: 0 additions & 36 deletions app/App.go

This file was deleted.

119 changes: 119 additions & 0 deletions app/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package app

import (
"context"
"os"
"os/signal"
"sync"
"syscall"

"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)

var (
App *Application
Styles *Theme
)

type Application struct {
*tview.Application

context context.Context
cancelFn context.CancelFunc
waitGroup sync.WaitGroup
}

type Theme struct {
tview.Theme

SidebarTitleBorderColor string
}

func init() {
ctx, cancel := context.WithCancel(context.Background())

App = &Application{
Application: tview.NewApplication(),
context: ctx,
cancelFn: cancel,
}

App.register()
App.EnableMouse(true)
App.EnablePaste(true)

Styles = &Theme{
Theme: tview.Theme{
PrimitiveBackgroundColor: tcell.ColorDefault,
ContrastBackgroundColor: tcell.ColorBlue,
MoreContrastBackgroundColor: tcell.ColorGreen,
BorderColor: tcell.ColorWhite,
TitleColor: tcell.ColorWhite,
GraphicsColor: tcell.ColorGray,
PrimaryTextColor: tcell.ColorDefault.TrueColor(),
SecondaryTextColor: tcell.ColorYellow,
TertiaryTextColor: tcell.ColorGreen,
InverseTextColor: tcell.ColorWhite,
ContrastSecondaryTextColor: tcell.ColorBlack,
},
SidebarTitleBorderColor: "#666A7E",
}

tview.Styles = Styles.Theme
}

// Context returns the application context.
func (a *Application) Context() context.Context {
return a.context
}

// Register adds a task to the wait group and returns a
// function that decrements the task count when called.
//
// The application will not stop until all registered tasks
// have finished by calling the returned function!
func (a *Application) Register() func() {
a.waitGroup.Add(1)
return a.waitGroup.Done
}

// Run starts and blocks until the application is stopped.
func (a *Application) Run(root *tview.Pages) error {
a.SetRoot(root, true)
return a.Application.Run()
}

// Stop cancels the application context, waits for all
// tasks to finish, and then stops the application.
func (a *Application) Stop() {
a.cancelFn()
a.waitGroup.Wait()
a.Application.Stop()
}

// register listens for interrupt and termination signals to
// gracefully handle shutdowns by calling the Stop method.
func (a *Application) register() {
c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)

go func() {
<-c
a.Stop()
<-c
os.Exit(1)
}()

// Override the default input capture to listen for Ctrl+C
// and make it send an interrupt signal to the channel to
// trigger a graceful shutdown instead of closing the app
// immediately without waiting for tasks to finish.
a.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if event.Key() == tcell.KeyCtrlC {
c <- os.Interrupt
return nil
}
return event
})
}
6 changes: 5 additions & 1 deletion app/Keymap.go → app/keymap.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,12 @@ var Keymaps = KeymapSystem{
Bind{Key: Key{Code: tcell.KeyUp}, Cmd: cmd.MoveUp, Description: "Go up"},
Bind{Key: Key{Char: '/'}, Cmd: cmd.Search, Description: "Search"},
Bind{Key: Key{Char: 'n'}, Cmd: cmd.NextFoundNode, Description: "Go to next found node"},
Bind{Key: Key{Char: 'N'}, Cmd: cmd.PreviousFoundNode, Description: "Go to previous found node"},
Bind{Key: Key{Char: 'p'}, Cmd: cmd.PreviousFoundNode, Description: "Go to previous found node"},
Bind{Key: Key{Char: 'P'}, Cmd: cmd.NextFoundNode, Description: "Go to next found node"},
Bind{Key: Key{Char: 'c'}, Cmd: cmd.TreeCollapseAll, Description: "Collapse all"},
Bind{Key: Key{Char: 'e'}, Cmd: cmd.ExpandAll, Description: "Expand all"},
Bind{Key: Key{Char: 'R'}, Cmd: cmd.Refresh, Description: "Refresh tree"},
},
TreeFilterGroup: {
Bind{Key: Key{Code: tcell.KeyEscape}, Cmd: cmd.UnfocusTreeFilter, Description: "Unfocus tree filter"},
Expand All @@ -94,7 +97,7 @@ var Keymaps = KeymapSystem{
Bind{Key: Key{Char: 'b'}, Cmd: cmd.GotoPrev, Description: "Go to previous cell"},
Bind{Key: Key{Char: '$'}, Cmd: cmd.GotoEnd, Description: "Go to last cell"},
Bind{Key: Key{Char: '0'}, Cmd: cmd.GotoStart, Description: "Go to first cell"},
Bind{Key: Key{Char: 'y'}, Cmd: cmd.Copy, Description: "Copy cell to clipboard"},
Bind{Key: Key{Char: 'y'}, Cmd: cmd.Copy, Description: "Copy cell value to clipboard"},
Bind{Key: Key{Char: 'o'}, Cmd: cmd.AppendNewRow, Description: "Append new row"},
Bind{Key: Key{Char: 'J'}, Cmd: cmd.SortDesc, Description: "Sort descending"},
Bind{Key: Key{Char: 'R'}, Cmd: cmd.Refresh, Description: "Refresh the current table"},
Expand Down Expand Up @@ -134,6 +137,7 @@ var Keymaps = KeymapSystem{
Bind{Key: Key{Code: tcell.KeyEnter}, Cmd: cmd.CommitEdit, Description: "Add edit to pending changes"},
Bind{Key: Key{Code: tcell.KeyEscape}, Cmd: cmd.DiscardEdit, Description: "Discard edit"},
Bind{Key: Key{Char: 'C'}, Cmd: cmd.SetValue, Description: "Toggle value menu to put values like NULL, EMPTY or DEFAULT"},
Bind{Key: Key{Char: 'y'}, Cmd: cmd.Copy, Description: "Copy value to clipboard"},
},
},
}
Loading

0 comments on commit e1b8d86

Please sign in to comment.