From 9d0e1db1018a9e2686ef4d9447361b84bca3b66c Mon Sep 17 00:00:00 2001 From: Christian Rocha Date: Sat, 4 Sep 2021 14:36:49 -0400 Subject: [PATCH] Tidy up tutorials --- README.md | 57 +++++++++++----------- tutorials/basics/README.md | 91 +++++++++++++++++------------------- tutorials/basics/main.go | 18 ++++--- tutorials/commands/README.md | 52 ++++++++++----------- 4 files changed, 108 insertions(+), 110 deletions(-) diff --git a/README.md b/README.md index 51f2f27..86a0d7c 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ This tutorial assumes you have a working knowledge of Go. ## Enough! Let's get to it. -For this tutorial we're making a to-do list. +For this tutorial we're making a shopping list. To start we'll define our package and import some libraries. Our only external import will be the Bubble Tea library, which we'll call `tea` for short. @@ -88,16 +88,14 @@ type model struct { ## Initialization -Next we'll define our application’s initial state. We’ll store our initial -model in a simple variable, and then define the `Init` method. `Init` can -return a `Cmd` that could perform some initial I/O. For now, we don't need to -do any I/O, so for the command we'll just return `nil`, which translates to "no -command." +Next we’ll define our application’s initial state. In this case we’re defining +a function to return our initial model, however we could just as easily define +the initial model as a variable elsewhere, too. ```go -func main() { - initialModel := model{ - // Our to-do list is just a grocery list +func initialModel() model { + return model{ + // Our shopping list is a grocery list choices: []string{"Buy carrots", "Buy celery", "Buy kohlrabi"}, // A map which indicates which choices are selected. We're using @@ -106,7 +104,13 @@ func main() { selected: make(map[int]struct{}), } } +``` +Next we define the `Init` method. `Init` can return a `Cmd` that could perform +some initial I/O. For now, we don't need to do any I/O, so for the command +we'll just return `nil`, which translates to "no command." + +```go func (m model) Init() tea.Cmd { // Just return `nil`, which means "no I/O right now, please." return nil @@ -115,15 +119,15 @@ func (m model) Init() tea.Cmd { ## The Update Method -Next we'll define the update method. The update function is called when -"things happen." Its job is to look at what has happened and return an updated -model in response to whatever happened. It can also return a `Cmd` and make -more things happen, but for now don't worry about that part. +Next up is the update method. The update function is called when ”things +happen.” Its job is to look at what has happened and return an updated model in +response. It can also return a `Cmd` to make more things happen, but for now +don't worry about that part. -In our case, when a user presses the down arrow, `update`'s job is to notice +In our case, when a user presses the down arrow, `Update`’s job is to notice that the down arrow was pressed and move the cursor accordingly (or not). -The "something happened" comes in the form of a `Msg`, which can be any type. +The “something happened” comes in the form of a `Msg`, which can be any type. Messages are the result of some I/O that took place, such as a keypress, timer tick, or a response from a server. @@ -177,18 +181,18 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } ``` -You may have noticed that "ctrl+c" and "q" above return a `tea.Quit` command -with the model. That's a special command which instructs the Bubble Tea runtime -to quit, exiting the program. +You may have noticed that ctrl+c and q above return +a `tea.Quit` command with the model. That’s a special command which instructs +the Bubble Tea runtime to quit, exiting the program. ## The View Method -At last, it's time to render our UI. Of all the methods, the view is the +At last, it’s time to render our UI. Of all the methods, the view is the simplest. We look at the model in it's current state and use it to return a `string`. That string is our UI! -Because the view describes the entire UI of your application, you don't have -to worry about redraw logic and stuff like that. Bubble Tea takes care of it +Because the view describes the entire UI of your application, you don’t have to +worry about redrawing logic and stuff like that. Bubble Tea takes care of it for you. ```go @@ -230,12 +234,7 @@ The last step is to simply run our program. We pass our initial model to ```go func main() { - initialModel := model{ - choices: []string{"Buy carrots", "Buy celery", "Buy kohlrabi"}, - selected: make(map[int]struct{}), - } - - p := tea.NewProgram(initialModel) + p := tea.NewProgram(initialModel()) if err := p.Start(); err != nil { fmt.Printf("Alas, there's been an error: %v", err) os.Exit(1) @@ -243,7 +242,7 @@ func main() { } ``` -## What's Next? +## What’s Next? This tutorial covers the basics of building an interactive terminal UI, but in the real world you'll also need to perform I/O. To learn about that have a @@ -308,12 +307,10 @@ Czaplicki et alia and the excellent [go-tea][gotea] by TJ Holowaychuk. [elm]: https://guide.elm-lang.org/architecture/ [gotea]: https://github.com/tj/go-tea - ## License [MIT](https://github.com/charmbracelet/bubbletea/raw/master/LICENSE) - *** Part of [Charm](https://charm.sh). diff --git a/tutorials/basics/README.md b/tutorials/basics/README.md index a6393df..37f1563 100644 --- a/tutorials/basics/README.md +++ b/tutorials/basics/README.md @@ -14,7 +14,7 @@ This tutorial assumes you have a working knowledge of Go. ## Enough! Let's get to it. -For this tutorial we're making a to-do list. +For this tutorial we're making a shopping list. To start we'll define our package and import some libraries. Our only external import will be the Bubble Tea library, which we'll call `tea` for short. @@ -52,25 +52,29 @@ type model struct { ## Initialization -Next we'll define our application’s initial state. We’ll store our initial -model in a simple variable, and then define the `Init` method. `Init` can -return a `Cmd` that could perform some initial I/O. For now, we don't need to -do any I/O, so for the command we'll just return `nil`, which translates to "no -command." +Next we’ll define our application’s initial state. In this case we’re defining +a function to return our initial model, however we could just as easily define +the initial model as a variable elsewhere, too. ```go -func main() { - initialModel := model{ - // Our to-do list is just a grocery list - choices: []string{"Buy carrots", "Buy celery", "Buy kohlrabi"}, +func initialModel() model { + return model{ + // Our shopping list is a grocery list + choices: []string{"Buy carrots", "Buy celery", "Buy kohlrabi"}, - // A map which indicates which choices are selected. We're using - // the map like a mathematical set. The keys refer to the indexes - // of the `choices` slice, above. - selected: make(map[int]struct{}), + // A map which indicates which choices are selected. We're using + // the map like a mathematical set. The keys refer to the indexes + // of the `choices` slice, above. + selected: make(map[int]struct{}), } } +``` +Next we define the `Init` method. `Init` can return a `Cmd` that could perform +some initial I/O. For now, we don't need to do any I/O, so for the command +we'll just return `nil`, which translates to "no command." + +```go func (m model) Init() tea.Cmd { // Just return `nil`, which means "no I/O right now, please." return nil @@ -79,15 +83,15 @@ func (m model) Init() tea.Cmd { ## The Update Method -Next we'll define the update method. The update function is called when -"things happen." Its job is to look at what has happened and return an updated -model in response to whatever happened. It can also return a `Cmd` and make -more things happen, but for now don't worry about that part. +Next up is the update method. The update function is called when ”things +happen.” Its job is to look at what has happened and return an updated model in +response. It can also return a `Cmd` to make more things happen, but for now +don't worry about that part. -In our case, when a user presses the down arrow, `update`'s job is to notice +In our case, when a user presses the down arrow, `Update`’s job is to notice that the down arrow was pressed and move the cursor accordingly (or not). -The "something happened" comes in the form of a `Msg`, which can be any type. +The “something happened” comes in the form of a `Msg`, which can be any type. Messages are the result of some I/O that took place, such as a keypress, timer tick, or a response from a server. @@ -141,18 +145,18 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } ``` -You may have noticed that "ctrl+c" and "q" above return a `tea.Quit` command -with the model. That's a special command which instructs the Bubble Tea runtime -to quit, exiting the program. +You may have noticed that ctrl+c and q above return +a `tea.Quit` command with the model. That’s a special command which instructs +the Bubble Tea runtime to quit, exiting the program. ## The View Method -At last, it's time to render our UI. Of all the methods, the view is the +At last, it’s time to render our UI. Of all the methods, the view is the simplest. We look at the model in it's current state and use it to return a `string`. That string is our UI! -Because the view describes the entire UI of your application, you don't have -to worry about redraw logic and stuff like that. Bubble Tea takes care of it +Because the view describes the entire UI of your application, you don’t have to +worry about redrawing logic and stuff like that. Bubble Tea takes care of it for you. ```go @@ -194,12 +198,7 @@ The last step is to simply run our program. We pass our initial model to ```go func main() { - initialModel := model{ - choices: []string{"Carrots", "Celery", "Kohlrabi"}, - selected: make(map[int]struct{}), - } - - p := tea.NewProgram(initialModel) + p := tea.NewProgram(initialModel()) if err := p.Start(); err != nil { fmt.Printf("Alas, there's been an error: %v", err) os.Exit(1) @@ -207,7 +206,7 @@ func main() { } ``` -## What's Next? +## What’s Next? This tutorial covers the basics of building an interactive terminal UI, but in the real world you'll also need to perform I/O. To learn about that have a @@ -220,22 +219,10 @@ there are [Go Docs][docs]. [examples]: http://github.com/charmbracelet/bubbletea/tree/master/examples [docs]: https://pkg.go.dev/github.com/charmbracelet/bubbletea?tab=doc -### Bubble Tea in the Wild +## Additional Resources -For some Bubble Tea programs in production, see: - -* [Glow](https://github.com/charmbracelet/glow): a markdown reader, browser and online markdown stash -* [The Charm Tool](https://github.com/charmbracelet/charm): the Charm user account manager - -### Libraries we use with Bubble Tea - -* [Bubbles][bubbles]: various Bubble Tea components -* [Termenv][termenv]: Advanced ANSI styling for terminal applications -* [Reflow][reflow]: ANSI-aware methods for formatting and generally working with text. Of particular note is `PrintableRuneWidth` in the `ansi` sub-package which measures the physical widths of strings. Many runes, such as East Asian characters, emojis, and various unicode symbols are two cells wide, so measuring a layout with `len()` often won't cut it. Reflow is particularly nice for this as it measures character widths while ignoring any ANSI sequences present. - -[termenv]: https://github.com/muesli/termenv -[reflow]: https://github.com/muesli/reflow -[bubbles]: https://github.com/charmbracelet/bubbles +* [Libraries we use with Bubble Tea](https://github.com/charmbracelet/bubbletea/#libraries-we-use-with-bubble-tea) +* [Bubble Tea in the Wild](https://github.com/charmbracelet/bubbletea/#bubble-tea-in-the-wild) ### Feedback @@ -243,3 +230,11 @@ We'd love to hear your thoughts on this tutorial. Feel free to drop us a note! * [Twitter](https://twitter.com/charmcli) * [The Fediverse](https://mastodon.technology/@charm) + +*** + +Part of [Charm](https://charm.sh). + +The Charm logo + +Charm热爱开源 • Charm loves open source diff --git a/tutorials/basics/main.go b/tutorials/basics/main.go index 44692e2..05d40b4 100644 --- a/tutorials/basics/main.go +++ b/tutorials/basics/main.go @@ -13,6 +13,17 @@ type model struct { selected map[int]struct{} } +func initialModel() model { + return model{ + choices: []string{"Buy carrots", "Buy celery", "Buy kohlrabi"}, + + // A map which indicates which choices are selected. We're using + // the map like a mathematical set. The keys refer to the indexes + // of the `choices` slice, above. + selected: make(map[int]struct{}), + } +} + func (m model) Init() tea.Cmd { return nil } @@ -67,12 +78,7 @@ func (m model) View() string { } func main() { - initialModel := model{ - choices: []string{"Carrots", "Celery", "Kohlrabi"}, - selected: make(map[int]struct{}), - } - - p := tea.NewProgram(initialModel) + p := tea.NewProgram(initialModel()) if err := p.Start(); err != nil { fmt.Printf("Alas, there's been an error: %v", err) os.Exit(1) diff --git a/tutorials/commands/README.md b/tutorials/commands/README.md index 6c505a0..5954acf 100644 --- a/tutorials/commands/README.md +++ b/tutorials/commands/README.md @@ -183,9 +183,19 @@ And that's that. There's one more thing you that is helpful to know about `Cmd`s are defined in Bubble Tea as `type Cmd func() Msg`. So they're just functions that don't take any arguments and return a `Msg`, which can be -anything. If you need to pass arguments to a command, you just make a function +any type. If you need to pass arguments to a command, you just make a function that returns a command. For example: +```go +func cmdWithArg(id int) tea.Cmd { + return func() tea.Msg { + return someMsg{id: int} + } +} +``` + +A more real-world example looks like: + ```go func checkSomeUrl(url string) tea.Cmd { return func() tea.Msg { @@ -199,40 +209,22 @@ func checkSomeUrl(url string) tea.Cmd { } ``` -Just make sure you do as much stuff as you can in the innermost function, -because that's the one that runs asynchronously. +Anyway, just make sure you do as much stuff as you can in the innermost +function, because that's the one that runs asynchronously. -## Anyway, Now What? +## Now What? After doing this tutorial and [the previous one][basics] you should be ready to -build a Bubble Tea program of your own. We also recommend that you look at the +build a Bubble Tea program of your own. We also recommend that you look at the Bubble Tea [example programs][examples] as well as [Bubbles][bubbles], a component library for Bubble Tea. And, of course, check out the [Go Docs][docs]. -### Bubble Tea in the Wild +## Additional Resources -For some Bubble Tea programs in production, see: - -* [Glow](https://github.com/charmbracelet/glow): a markdown reader, browser and online markdown stash -* [The Charm Tool](https://github.com/charmbracelet/charm): the Charm user account manager - -[examples]: http://github.com/charmbracelet/bubbletea/tree/master/examples -[docs]: https://pkg.go.dev/github.com/charmbracelet/bubbletea?tab=doc -[bubbles]: https://github.com/charmbracelet/bubbles - -### Libraries we use with Bubble Tea - -* [Bubbles][bubbles] various Bubble Tea components we've built -* [Termenv][termenv]: Advanced ANSI styling for terminal applications -* [Reflow][reflow]: ANSI-aware methods for reflowing blocks of text -* [go-runewidth][runewidth]: Get the physical width of strings in terms of terminal cells. Many runes, such as East Asian charcters and emojis, are two cells wide, so measuring a layout with `len()` often won't cut it! - -[termenv]: https://github.com/muesli/termenv -[reflow]: https://github.com/muesli/reflow -[bubbles]: https://github.com/charmbracelet/bubbles -[runewidth]: https://github.com/mattn/go-runewidth +* [Libraries we use with Bubble Tea](https://github.com/charmbracelet/bubbletea/#libraries-we-use-with-bubble-tea) +* [Bubble Tea in the Wild](https://github.com/charmbracelet/bubbletea/#bubble-tea-in-the-wild) ### Feedback @@ -240,3 +232,11 @@ We'd love to hear your thoughts on this tutorial. Feel free to drop us a note! * [Twitter](https://twitter.com/charmcli) * [The Fediverse](https://mastodon.technology/@charm) + +*** + +Part of [Charm](https://charm.sh). + +The Charm logo + +Charm热爱开源 • Charm loves open source