1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
# Reference: the app API
> Neutral description of what a command may call on an editor built on turbo-core. This is not the whole of `app`; it is the part a command and its tests use.
## Construction
| Function | Description |
| --- | --- |
| `New(screen tcell.Screen, themeName string, p profile.Profile) *App` | An editor drawing on `screen`, being the editor `p` describes. An unknown theme name falls back to the default rather than failing. |
| `ProjectRoot(p profile.Profile, files []string) string` | The directory a language server should work in: the nearest directory at or above the first file holding one of `p.RootMarkers`, or the working directory. |
## Running it
| Method | Description |
| --- | --- |
| `Run() error` | Draws and handles events until the user leaves. Returns nil when the screen was finalised. |
| `Quitting() bool` | Whether the editor is on its way out. |
| `Tick()` | One turn of the loop's state-driven work, without waiting for an event. |
| `Handle(event tcell.Event)` | Routes one event through the whole chain, as the loop does. |
| `Render()` | Lays out and paints one frame. |
`Tick`, `Handle` and `Render` are exported so an editor can drive itself end to end from a test, with no terminal and no event loop.
## Files
| Method | Description |
| --- | --- |
| `NewFile()` | Opens an empty window. |
| `Open(path string)` | Opens a file, raising the window when it is already open. |
| `SaveFile()`, `SaveFileAs()`, `CloseFile()` | As the File menu. |
| `Undo()`, `Redo()`, `Cut()`, `Copy()`, `Paste()`, `SelectAll()` | As the Edit menu. Redo is `Ctrl-R`; `Ctrl-Y` deletes a line. |
| `InsertLine()`, `DeleteLine()` | Turbo C's `Ctrl-N` and `Ctrl-Y`: a blank line opened above the cursor, and the cursor's own line removed. |
| `ActiveView() *editor.View` | The editing view of the front window, or nil when it is a terminal, the tree, or nothing. |
## Settings
| Method | Description |
| --- | --- |
| `UseSettings(s settings.Settings, path string)` | Applies a project's settings and remembers where they came from. The theme is not applied here — the caller resolves it, because a `-theme` flag overrides the project's choice. |
| `SettingsPath() string` | The settings file being followed, or `""`. |
| `SetAutosave(on bool, delay time.Duration)` | Turns autosave on or off. |
The settings file is also re-read **after every save that writes it**, from either save path, so a change made in the editor takes effect without a restart. Autosave and its delay are re-applied; the theme is not. A file that no longer parses reports `Saved, but not applied: …` and leaves the previous values in force.
## The project's three files
`settings.toml`, `snippets.toml` and `tools.toml` behave alike, and exactly one of each create/open pair is ever available: you can create the file the project has not got, and open the one it has. The menus grey out the other.
| Method | Description |
| --- | --- |
| `CreateProjectSettings()`, `CreateSnippets()`, `CreateTools()` | Write the starter file from the profile's template and open it. Creating over an existing file reports `Already there` and opens it unchanged — unreachable from the menu, which greys the item out. |
| `OpenProjectSettings()`, `OpenSnippets()`, `OpenTools()` | Open the project's file. Never writes: a project without one is told which item makes it. `OpenSnippets` opens the **project's** file, never the user's. |
| `HasProjectSettings() bool`, `HasProjectSnippets() bool`, `HasProjectTools() bool` | Whether the project has each file. What decides, for all six menu items, which of the pair is greyed out. |
## The language server
| Method | Description |
| --- | --- |
| `StartLanguageServer(ctx context.Context, root string)` | Starts it in the background, so a slow start-up does not hold the first keystroke up. |
| `Language() *Language` | The language-server side. Every method on it is a no-op when nothing is connected. |
| `RequestCompletion()` | Asks for a completion at the cursor. |
| `DescribeSymbol()`, `GoToDefinition()`, `GoToTypeDefinition()`, `FindImplementations()`, `FindReferences()` | The Code menu's questions about the symbol under the cursor. Each jumps when there is one answer, offers a list when there are several, and distinguishes "nothing found" from "the server is not ready". |
| `SymbolInFile()`, `SymbolInProject()` | The file's own outline, and a search across the project. Both always offer the list, even for a single match: finding *which* thing has a name is the answer. |
| `ShowProblems()` | Every diagnostic the server has reported, for every file it has spoken about. |
### Language
| Method | Description |
| --- | --- |
| `Ready() bool` | Whether a server is connected and initialised. |
| `Knows(path string) bool` | Whether the server has been told the document is open. Spelling-independent: a file opened by a relative path is known by its absolute one too. |
| `Status() string` | The one-line state shown on the status bar. |
| `Report() Report` | Status, server path, root and readiness together. |
| `Stop(ctx context.Context)` | Shuts the server down. |
| `TypeDefinition`, `Implementation`, `References` | The three location requests beside `Definition`. `References` counts the declaration as one of the answers. |
| `DocumentSymbols(ctx, path) ([]lsp.Symbol, error)` | What one file declares, flattened, in file order. |
| `WorkspaceSymbols(ctx, query) ([]lsp.Symbol, error)` | Symbols matching a query anywhere in the project. What "matching" means belongs to the server. |
| `Diagnostics(path string) []lsp.Diagnostic` | What the server reported for a file. The path is resolved to an absolute one first: a server publishes absolute URIs and a buffer may hold the relative path the command line gave it. |
| `FirstError(path string) (lsp.Diagnostic, bool)` | The first error-level diagnostic. |
| `AllDiagnostics() []FileDiagnostic` | Every problem for every file, sorted by file then line. |
Saving keeps the server's set of open documents true. Writing a file the server knows sends `didSave`; writing one it does not know — a window that began Untitled, saved for the first time — sends `didOpen`, so the document works from that save on. Save As under a genuinely different name also closes the old document on the server first; a mere change of spelling of the same file does not count as a rename.
## What is on screen
| Method | Description |
| --- | --- |
| `Profile() profile.Profile` | Which editor this is. |
| `Theme() *theme.Theme`, `ThemeName() string` | The theme in use. |
| `Desktop() *ui.Desktop` | The windows. |
| `MenuBar() *ui.MenuBar` | The bar. |
| `StatusBar() *ui.StatusBar` | The bar along the bottom. |
| `Completion() *CompletionBox` | The completion popup. |
| `Modals() int`, `TopModal() *ui.Dialog` | The dialog stack. |
| `Message(text string)` | A one-line note on the status bar. |
| `ShowMessage(title, message string)` | A modal box. |
## Tool parameters
A command may ask for values before it runs, by writing `{{label}}` where the value goes. `app.RunTool` opens a box for them; nothing here draws anything.
| Function or method | Description |
| --- | --- |
| `(Tool) Placeholders() []Placeholder` | The values the command asks for, in first-appearance order, a repeated label reported once. Nil when it asks for none. |
| `(Tool) Fill(values map[string]string) string` | The command with every placeholder replaced. A value is shell-quoted unless its placeholder is `Raw`; a label with no entry becomes empty. |
| `ShellQuote(value string) string` | Wraps a string so `sh -c` sees exactly one argument, whatever is in it. |
| `Placeholder` field | Type | Description |
| --- | --- | --- |
| `Label` | `string` | What to ask for: the text between the braces, trimmed, without a trailing `...`. |
| `Raw` | `bool` | The value goes in verbatim rather than shell-quoted. |
`Load` refuses a command with an unclosed `{{` or a placeholder with no label, so a `Tool` that came from a file always parses.
| Function | Description |
| --- | --- |
| `app.MaxParameterFields(screenHeight int) int` | How many values a box can ask for on a screen that tall. A caller with more to ask must say so rather than opening one. |
| `app.NewParametersDialog(title string, labels []string, initial map[string]string, screen ui.Rect) *ParametersDialog` | The box. `initial` pre-fills a field by label. |
| `(*ParametersDialog) Dialog() *ui.Dialog` | The modal to push. |
| `(*ParametersDialog) Values() map[string]string` | What was typed, by label. |
## Errors
`app` returns no errors from its actions: everything that can fail is reported to the user on the status bar or in a dialog, because the editor is what they would use to fix it.
## See also
- [Build an editor](../tutorials/build-an-editor.md)
- [Profile reference](profile.md)
|