github.com/xyproto/orbiton/v2@v2.65.12-0.20240516144430-e10a419274ec/tutorial.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/xyproto/vt100"
     8  )
     9  
    10  // TutorialStep represents a step in the tutorial wizard
    11  type TutorialStep struct {
    12  	title       string
    13  	description string
    14  	expectKeys  []string
    15  }
    16  
    17  // Tutorial is a collection of steps
    18  type Tutorial []TutorialStep
    19  
    20  var tutorialSteps = Tutorial{
    21  	TutorialStep{
    22  		title:       "Start of text",
    23  		description: "Press ctrl-a to go to the start of the text on the current line.",
    24  		expectKeys:  []string{"c:1"}, // ctrl-a
    25  	},
    26  	TutorialStep{
    27  		title:       "Start of line",
    28  		description: "Press ctrl-a twice to go to the start of the line.",
    29  		expectKeys:  []string{"c:1", "c:1"}, // ctrl-a
    30  	},
    31  	TutorialStep{
    32  		title:       "End of the line above",
    33  		description: "Press ctrl-a three times to go to the end of the line above.",
    34  		expectKeys:  []string{"c:1", "c:1", "c:1"}, // ctrl-a
    35  	},
    36  	TutorialStep{
    37  		title:       "Create a bookmark",
    38  		description: "Press ctrl-b to bookmark the current line.",
    39  		expectKeys:  []string{"c:2"}, // ctrl-b
    40  	},
    41  	TutorialStep{
    42  		title:       "Jump to a bookmark",
    43  		description: "Press ctrl-b jump to the bookmark. It must be on a different line than the current line.",
    44  		expectKeys:  []string{"c:2"}, // ctrl-b
    45  	},
    46  	TutorialStep{
    47  		title:       "Remove a bookmark",
    48  		description: "Press ctrl-b to clear a bookmark. The bookmark must be set and the current line must be the bookmarked line.",
    49  		expectKeys:  []string{"c:2"}, // ctrl-b
    50  	},
    51  	TutorialStep{
    52  		title:       "Copy line",
    53  		description: "Press ctrl-c to copy the current line to the clipboard. If no clipboard is available, an internal buffer is used.",
    54  		expectKeys:  []string{"c:3"}, // ctrl-c
    55  	},
    56  	TutorialStep{
    57  		title:       "Copy block of text",
    58  		description: "Press ctrl-c twice to copy a block of text (until the next blank line) to the clipboard. If no clipboard is available, an internal buffer is used. For some terminal emulators, this must not be pressed too fast.",
    59  		expectKeys:  []string{"c:3", "c:3"}, // ctrl-c
    60  	},
    61  	TutorialStep{
    62  		title:       "Delete the current letter",
    63  		description: "Press ctrl-d to delete the current letter.",
    64  		expectKeys:  []string{"c:8"}, // ctrl-d
    65  	},
    66  	TutorialStep{
    67  		title:       "End of the line",
    68  		description: "Press ctrl-e to go to the end of the line.",
    69  		expectKeys:  []string{"c:5"}, // ctrl-e
    70  	},
    71  	TutorialStep{
    72  		title:       "Start of the next line",
    73  		description: "Press ctrl-e twice to go to the start of the next line.",
    74  		expectKeys:  []string{"c:5"}, // ctrl-e
    75  	},
    76  	TutorialStep{
    77  		title:       "Search",
    78  		description: "Press ctrl-f and type in the text to search for.",
    79  		expectKeys:  []string{"c:6"}, // ctrl-f
    80  	},
    81  	TutorialStep{
    82  		title:       "Search and replace",
    83  		description: "Press ctrl-f, type in text to search for, press Tab, type in text that all instances should be replaced with and then press return.",
    84  		expectKeys:  []string{"c:6"}, // ctrl-f
    85  	},
    86  	TutorialStep{
    87  		title:       "Go to definition",
    88  		description: "For some programming languages, ctrl-g can be pressed to jump to a definition, and ctrl-b can be used to jump back.",
    89  		expectKeys:  []string{"c:7"}, // ctrl-g
    90  	},
    91  	TutorialStep{
    92  		title:       "Toggle word count and cursor position status",
    93  		description: "ctrl-g will toggle a status bar at the bottom, containing the current line, total number of lines, the current rune, word count, file mode and either \"tabs\" or \"spaces\", unless the cursor is over a definition that can be jumped to.",
    94  		expectKeys:  []string{"c:7"}, // ctrl-g
    95  	},
    96  	TutorialStep{
    97  		title:       "Delete to letter to the left",
    98  		description: "Press ctrl-h or backspace to delete the letter to the left.",
    99  		expectKeys:  []string{"c:8"}, // ctrl-h or backspace
   100  	},
   101  	TutorialStep{
   102  		title:       "Indentation",
   103  		description: "Press ctrl-i or tab to indent a line.",
   104  		expectKeys:  []string{"c:9"}, // ctrl-i or tab
   105  	},
   106  	TutorialStep{
   107  		title:       "Join",
   108  		description: "Press ctrl-j to join this line with the next. The next line is placed after the current one.",
   109  		expectKeys:  []string{"c:10"}, // ctrl-j
   110  	},
   111  	TutorialStep{
   112  		title:       "Remove the rest of the line",
   113  		description: "Press ctrl-k to remove the rest of the current line.",
   114  		expectKeys:  []string{"c:11"}, // ctrl-k
   115  	},
   116  	TutorialStep{
   117  		title:       "Jump to a location",
   118  		description: "Press ctrl-l and then press one of the highlighted letters to jump there.",
   119  		expectKeys:  []string{"c:12"}, // ctrl-l
   120  	},
   121  	TutorialStep{
   122  		title:       "Jump to the top",
   123  		description: "Unless the cursor is already at the top, press ctrl-l and then return.",
   124  		expectKeys:  []string{"c:12"}, // ctrl-l
   125  	},
   126  	TutorialStep{
   127  		title:       "Jump to the top (method 2)",
   128  		description: "Press ctrl-l and then t to jump to the top of the file.",
   129  		expectKeys:  []string{"c:12"}, // ctrl-l
   130  	},
   131  	TutorialStep{
   132  		title:       "Jump to the top (method 3)",
   133  		description: "Press ctrl-l, type in 0 and press return.",
   134  		expectKeys:  []string{"c:12"}, // ctrl-l
   135  	},
   136  	TutorialStep{
   137  		title:       "Jump to the bottom",
   138  		description: "Press ctrl-l and then b to jump to the bottom of the file.",
   139  		expectKeys:  []string{"c:12"}, // ctrl-l
   140  	},
   141  	TutorialStep{
   142  		title:       "Jump to the bottom (method 2)",
   143  		description: "Press ctrl-l and return to jump to the top, then ctrl-l and return to jump to the bottom.",
   144  		expectKeys:  []string{"c:12"}, // ctrl-l
   145  	},
   146  	TutorialStep{
   147  		title:       "Jump to the bottom (method 3)",
   148  		description: "Press ctrl-l, type in 100% and press return.",
   149  		expectKeys:  []string{"c:12"}, // ctrl-l
   150  	},
   151  	TutorialStep{
   152  		title:       "Jump to the bottom (method 4)",
   153  		description: "Press ctrl-l, type in 1. and press return.",
   154  		expectKeys:  []string{"c:12"}, // ctrl-l
   155  	},
   156  	TutorialStep{
   157  		title:       "Jump to the center of the file",
   158  		description: "Press ctrl-l and then c to jump to the center of the file.",
   159  		expectKeys:  []string{"c:12"}, // ctrl-l
   160  	},
   161  	TutorialStep{
   162  		title:       "Jump to the center of the file (method 2)",
   163  		description: "Press ctrl-l, type in 50% and press return.",
   164  		expectKeys:  []string{"c:12"}, // ctrl-l
   165  	},
   166  	TutorialStep{
   167  		title:       "Jump to the center of the file (method 3)",
   168  		description: "Press ctrl-l, type in .5 and press return.",
   169  		expectKeys:  []string{"c:12"}, // ctrl-l
   170  	},
   171  	TutorialStep{
   172  		title:       "Move a line down or start a new line",
   173  		description: "Press ctrl-m or press return.",
   174  		expectKeys:  []string{"c:13"}, // ctrl-m
   175  	},
   176  	TutorialStep{
   177  		title:       "Move down 10 lines",
   178  		description: "Press ctrl-n to move and scroll down 10 lines.",
   179  		expectKeys:  []string{"c:14"}, // ctrl-n
   180  	},
   181  	TutorialStep{
   182  		title:       "Go to the next instance",
   183  		description: "When searching, press ctrl-n to go to the next instance.",
   184  		expectKeys:  []string{"c:14"}, // ctrl-n
   185  	},
   186  	TutorialStep{
   187  		title:       "Go to the next instruction",
   188  		description: "When debugging, press ctrl-n to go to the next instruction.",
   189  		expectKeys:  []string{"c:14"}, // ctrl-n
   190  	},
   191  	TutorialStep{
   192  		title:       "Main menu",
   193  		description: "Press ctrl-o to open the main menu.",
   194  		expectKeys:  []string{"c:15"}, // ctrl-o
   195  	},
   196  	TutorialStep{
   197  		title:       "Move up 10 lines",
   198  		description: "Press ctrl-p to move and scroll up 10 lines.",
   199  		expectKeys:  []string{"c:16"}, // ctrl-p
   200  	},
   201  	TutorialStep{
   202  		title:       "Go to the previous instance",
   203  		description: "When searching, press ctrl-p to go to the previous instance.",
   204  		expectKeys:  []string{"c:16"}, // ctrl-p
   205  	},
   206  	TutorialStep{
   207  		title:       "Cycle register pane layout",
   208  		description: "When debugging, press ctrl-p to cycle the size of the register pane from small, to large to hidden.",
   209  		expectKeys:  []string{"c:16"}, // ctrl-p
   210  	},
   211  	TutorialStep{
   212  		title:       "Quit",
   213  		description: "Press ctrl-q to quit, no questions asked.",
   214  		expectKeys:  []string{"c:17"}, // ctrl-q
   215  	},
   216  	TutorialStep{
   217  		title:       "Open a portal",
   218  		description: "Press ctrl-r to open a portal that can be used to paste lines into another file with ctrl-v.",
   219  		expectKeys:  []string{"c:18"}, // ctrl-r
   220  	},
   221  	TutorialStep{
   222  		title:       "Close a portal",
   223  		description: "If a portal is open, it will time out after 20 minutes, or it can be closed with ctrl-r.",
   224  		expectKeys:  []string{"c:18"}, // ctrl-r
   225  	},
   226  	TutorialStep{
   227  		title:       "Save",
   228  		description: "Press ctrl-s to save the current file.",
   229  		expectKeys:  []string{"c:19"}, // ctrl-s
   230  	},
   231  	TutorialStep{
   232  		title:       "Save without using the pinky finger",
   233  		description: "In rapid succession, press arrow right, arrow down and arrow left. When \"o:\" appears at the bottom, press arrow down to save.",
   234  		expectKeys:  []string{}, // tbd
   235  	},
   236  	TutorialStep{
   237  		title:       "Save and quit without using the pinky finger",
   238  		description: "In rapid succession, press arrow right, arrow down and arrow left. When \"o:\" appears at the bottom, press arrow up to save and quit.",
   239  		expectKeys:  []string{}, // tbd
   240  	},
   241  	TutorialStep{
   242  		title:       "Record macro",
   243  		description: "Press ctrl-t to record keypresses. Press ctrl-t again to stop recording.",
   244  		expectKeys:  []string{"c:20"}, // ctrl-t
   245  	},
   246  	TutorialStep{
   247  		title:       "Play back macro",
   248  		description: "Press ctrl-t to play back the current macro.",
   249  		expectKeys:  []string{"c:20"}, // ctrl-t
   250  	},
   251  	TutorialStep{
   252  		title:       "Clear macro",
   253  		description: "Press Esc to clear the current macro.",
   254  		expectKeys:  []string{"c:27"}, // esc
   255  	},
   256  	TutorialStep{
   257  		title:       "Toggle checkbox",
   258  		description: "When editing Markdown, move the cursor to a line with a checkbox (\"- [ ] TODO\") and press ctrl-t or ctrl-space to toggle it.",
   259  		expectKeys:  []string{"c:20"}, // ctrl-t
   260  	},
   261  	TutorialStep{
   262  		title:       "Edit table",
   263  		description: "When editing Markdown, move the cursor to a line with a table and press ctrl-t to enter the table editor. This feature currently only supports small tables.",
   264  		expectKeys:  []string{"c:20"}, // ctrl-t
   265  	},
   266  	TutorialStep{
   267  		title:       "Format table",
   268  		description: "When editing Markdown, move the cursor to a line with a table and press ctrl-w to format it.",
   269  		expectKeys:  []string{"c:23"}, // ctrl-w
   270  	},
   271  	TutorialStep{
   272  		title:       "Undo",
   273  		description: "Press ctrl-u to undo the last operation. There is no redo, yet.",
   274  		expectKeys:  []string{"c:21"}, // ctrl-u
   275  	},
   276  	TutorialStep{
   277  		title:       "Paste",
   278  		description: "Press ctrl-v to paste the first line from the clipboard. The string will be trimmed.",
   279  		expectKeys:  []string{"c:22"}, // ctrl-v
   280  	},
   281  	TutorialStep{
   282  		title:       "Paste",
   283  		description: "Press ctrl-v twice to paste the contents of the clipbard.",
   284  		expectKeys:  []string{"c:22", "c:22"}, // ctrl-v
   285  	},
   286  	TutorialStep{
   287  		title:       "Generate template",
   288  		description: "Open an empty source code file and press ctrl-w to generate a \"hello world\" program. This applies to several programming languages.",
   289  		expectKeys:  []string{"c:23"}, // ctrl-w
   290  	},
   291  	TutorialStep{
   292  		title:       "Format source code",
   293  		description: "Open a source code file and press ctrl-w to format it. This applies to several programming languages.",
   294  		expectKeys:  []string{"c:23"}, // ctrl-w
   295  	},
   296  	TutorialStep{
   297  		title:       "Cut",
   298  		description: "Press ctrl-x to cut the current line and place it in the clipboard.",
   299  		expectKeys:  []string{"c:24"}, // ctrl-x
   300  	},
   301  	TutorialStep{
   302  		title:       "Paste",
   303  		description: "Press ctrl-x twice to cut the current block of text (to a blank line) and place it in the clipboard.",
   304  		expectKeys:  []string{"c:24", "c:24"}, // ctrl-x
   305  	},
   306  	TutorialStep{
   307  		title:       "Go to start of line (method 2)",
   308  		description: "Press ctrl-y to go to the start of the text, then line, then the end of the line above. Same as ctrl-a.",
   309  		expectKeys:  []string{"c:25"}, // ctrl-y
   310  	},
   311  	TutorialStep{
   312  		title:       "Undo (method 2)",
   313  		description: "Press ctrl-z to undo the last operation. If ctrl-z backgrounds the application, run \"fg\" to bring it back.",
   314  		expectKeys:  []string{"c:26"}, // ctrl-z
   315  	},
   316  	TutorialStep{
   317  		title:       "Build source code",
   318  		description: "Open a source code file and press ctrl-space to build it. This works for some projects and programming languages.",
   319  		expectKeys:  []string{"c:0"}, // ctrl-space
   320  	},
   321  	TutorialStep{
   322  		title:       "Build and run",
   323  		description: "Open a source code file and press ctrl-space twice to build it, run it and also display stdout + stderr.",
   324  		expectKeys:  []string{"c:0", "c:0"}, // ctrl-space
   325  	},
   326  	TutorialStep{
   327  		title:       "Insert a file",
   328  		description: "Let the file be named include.txt, then select the 'Insert \"include.txt\" at the current line' option in the ctrl-o menu.",
   329  		expectKeys:  []string{}, // tbd
   330  	},
   331  	TutorialStep{
   332  		title:       "Insert a file (method 2)",
   333  		description: "In rapid succession, press arrow right, arrow down and arrow left. Then type \"insertfile somefile.txt\" to insert somefile.txt into the current file.",
   334  		expectKeys:  []string{}, // tbd
   335  	},
   336  	TutorialStep{
   337  		title:       "English spell check (experimental feature)",
   338  		description: "Press ctrl-f, type t and press return. Then press ctrl-n for next instance, ctrl-a to add the word temporarily or ctrl-i to ignore the word temporarily.",
   339  		expectKeys:  []string{}, // tbd
   340  	},
   341  	TutorialStep{
   342  		title:       "Jump to matching parenthesis",
   343  		description: "Be on a (, [, {, }, ] or ) character. Press ctrl-_ to jump to the matching one, for instance the next \")\" if the cursor is on \"(\".",
   344  		expectKeys:  []string{"c:31"}, // ctrl-_
   345  	},
   346  	TutorialStep{
   347  		title:       "Insert digraph",
   348  		description: "Press ctrl-_ to insert a digraph. For instance \"ae\" to insert \"æ\". These are the same as for ViM or NeoViM. Do not be on a (, [, {, }, ] or ) character.",
   349  		expectKeys:  []string{"c:31"}, // ctrl-_
   350  	},
   351  	TutorialStep{
   352  		title:       "Tutorial complete",
   353  		description: "Press q, esc or ctrl-q to end this tutorial.",
   354  		expectKeys:  []string{"c:32"}, // space
   355  	},
   356  }
   357  
   358  // LaunchTutorial launches a short and sweet tutorial that covers at least portals and cut/paste
   359  func LaunchTutorial(tty *vt100.TTY, c *vt100.Canvas, e *Editor, status *StatusBar) {
   360  	const repositionCursorAfterDrawing = false
   361  	const marginX = 4
   362  
   363  	minWidth := 32
   364  	for _, step := range tutorialSteps {
   365  		for _, line := range strings.Split(step.description, "\n") {
   366  			if len(line) > minWidth {
   367  				minWidth = len(line) + marginX
   368  			}
   369  		}
   370  	}
   371  
   372  	displayedStatusOnce := false
   373  
   374  	i := 0
   375  	for {
   376  		if i == 0 && !displayedStatusOnce {
   377  			status.SetMessage("q to end")
   378  			status.Show(c, e)
   379  			displayedStatusOnce = true
   380  		} else {
   381  			status.Clear(c)
   382  		}
   383  
   384  		step := tutorialSteps[i]
   385  		progress := fmt.Sprintf("%d / %d", i+1, len(tutorialSteps))
   386  		step.Draw(c, e, progress, minWidth, repositionCursorAfterDrawing)
   387  
   388  		// Wait for a keypress
   389  		key := tty.String()
   390  		switch key {
   391  		case " ", "c:13", "↓", "→", "j", "c:14", "n": // space, return, down, right, j, ctrl-n or n to go to the next step
   392  			if i < (len(tutorialSteps) - 1) {
   393  				i++
   394  			}
   395  			continue
   396  		case "↑", "←", "k", "c:16", "p": // up, left, k, ctrl-p or p to go to the previous step
   397  			if i > 0 {
   398  				i--
   399  			}
   400  			continue
   401  		case "c:17", "c:27", "q", "x": // ctrl-q, esc, q or x to exit
   402  			return
   403  		}
   404  		// Other keypress, do nothing
   405  	}
   406  }
   407  
   408  // Draw draws a step of the tutorial
   409  func (step TutorialStep) Draw(c *vt100.Canvas, e *Editor, progress string, minWidth int, repositionCursorAfterDrawing bool) {
   410  	canvasBox := NewCanvasBox(c)
   411  
   412  	// Window is the background box that will be drawn in the upper right
   413  	centerBox := NewBox()
   414  
   415  	centerBox.EvenLowerRightPlacement(canvasBox, minWidth)
   416  	e.redraw = true
   417  
   418  	// Then create a list box
   419  	listBox := NewBox()
   420  	listBox.FillWithMargins(centerBox, 2, 2)
   421  
   422  	// Get the current theme for the register box
   423  	bt := e.NewBoxTheme()
   424  	bt.Foreground = &e.BoxTextColor
   425  	bt.Background = &e.DebugInstructionsBackground
   426  
   427  	// First figure out how many lines of text this will be after word wrap
   428  	const dryRun = true
   429  	addedLines := e.DrawText(bt, c, listBox, step.description, dryRun)
   430  
   431  	if addedLines > listBox.H {
   432  		// Then adjust the box height and text position (addedLines could very well be 0)
   433  		centerBox.Y -= addedLines
   434  		centerBox.H += addedLines
   435  		listBox.Y -= addedLines
   436  	}
   437  
   438  	// Then draw the box with the text
   439  	e.DrawBox(bt, c, centerBox)
   440  	e.DrawTitle(bt, c, centerBox, step.title, true)
   441  	e.DrawFooter(bt, c, centerBox, "("+progress+")")
   442  	e.DrawText(bt, c, listBox, step.description, false)
   443  
   444  	// Blit
   445  	c.Draw()
   446  
   447  	// Reposition the cursor, if needed
   448  	if repositionCursorAfterDrawing {
   449  		x := e.pos.ScreenX()
   450  		y := e.pos.ScreenY()
   451  		vt100.SetXY(uint(x), uint(y))
   452  	}
   453  }