| 📦 Turbo Golo d710c1b k33g 20h ago | 1 | // Package gololang is everything about Turbo Golo that is about *Golo*: how |
| 2 | // the editor names itself, which language server it talks to, what a project's |
| 3 | // starter files say, and how Golo source is coloured. |
| 4 | // |
| 5 | // Everything else the editor does lives in turbo-core, which knows nothing |
| 6 | // about Golo. This package is the whole of the difference between Turbo Golo |
| 7 | // and Turbo MoonBit, which is what makes a sixth editor a matter of writing one |
| 8 | // of these rather than forking anything. |
| 9 | // |
| 10 | // gololang.Register() // teach the library to colour Golo |
| 11 | // editor := app.New(screen, name, gololang.Profile()) |
| 12 | package gololang |
| 13 | |
| 14 | import ( |
| 15 | "rickub.com/turbo-editors/turbo-core/profile" |
| 16 | "rickub.com/turbo-editors/turbo-core/syntax" |
| 17 | ) |
| 18 | |
| 19 | // Name and Slug are what the editor calls itself. The slug is also its binary, |
| 20 | // its project directory (as .turbo-golo) and the stem of its environment |
| 21 | // variables (as TURBO_GOLO_…), so it is not free to change. |
| 22 | const ( |
| 23 | Name = "Turbo Golo" |
| 24 | Slug = "turbo-golo" |
| 25 | ) |
| 26 | |
| 27 | // Language is the name Golo is known by: the value LanguageOf returns for a |
| 28 | // .golo file, and what a snippets file writes in its languages key. |
| 29 | // |
| 30 | // It is lower case because every other name in the registry is — "golo" |
| 31 | // beside "toml" and "dockerfile" — while the language's own spelling, the one |
| 32 | // a person reads, is Profile().Language. |
| 33 | const Language syntax.Language = "golo" |
| 34 | |
| 35 | // ServerCommand is the language server Turbo Golo talks to, and InstallHint |
| 36 | // the one place a user gets it from. |
| 37 | // |
| 38 | // The server is the interpreter itself: `golo lsp` puts the same binary that |
| 39 | // runs a script into language-server mode, reusing its lexer, parser and AST. |
| 40 | // So a machine that can run Golo can complete Golo, and there is nothing |
| 41 | // separate to install — which is why the hint names the release page that |
| 42 | // ships the binary rather than a package manager's command. |
| 43 | // |
| 44 | // It answers four of the nine questions turbo-core asks — completion, hover, |
| 45 | // definition and the file's symbols — and publishes diagnostics unasked. It |
| 46 | // advertises neither references, typeDefinition and implementation, nor |
| 47 | // workspace/symbol, so those items report nothing found. That is documented |
| 48 | // rather than worked around, and a test asserts it so a future golo gaining |
| 49 | // them is noticed. |
| 50 | const ( |
| 51 | ServerCommand = "golo" |
| 52 | InstallHint = "see https://codeberg.org/TypeUnsafe/golo-script/releases" |
| 53 | ) |
| 54 | |
| 55 | // DefaultInstallDir is where GoloScript's own install.sh puts golo, gogolo and |
| 56 | // wagolo. It is on PATH on almost every machine, and it is searched all the |
| 57 | // same for the one where it is not: "completion silently does nothing" is what |
| 58 | // a user sees when the editor cannot find a server they believe they installed. |
| 59 | const DefaultInstallDir = "/usr/local/bin" |
| 60 | |
| 61 | // ServerArgs is what golo is started with. |
| 62 | // |
| 63 | // The lsp is not optional: golo with no argument starts its REPL and reads |
| 64 | // standard input as Golo source, which the editor would see as a server that |
| 65 | // answers nothing and never dies. It is a function rather than a variable so |
| 66 | // that no caller can append to the package's own slice. |
| 67 | func ServerArgs() []string { return []string{"lsp"} } |
| 68 | |
| 69 | // Profile returns the editor Turbo Golo is. |
| 70 | // |
| 71 | // It is a function rather than a variable for the same reason as in every other |
| 72 | // editor of this family, even though nothing here reads the environment: a |
| 73 | // caller that mutates what it gets back — appending to Server.Dirs, say — must |
| 74 | // not be mutating the package's one copy. |
| 75 | func Profile() profile.Profile { |
| 76 | return profile.Profile{ |
| 77 | Name: Name, |
| 78 | Slug: Slug, |
| 79 | // The language's own spelling. Golo is the language; GoloScript is |
| 80 | // the implementation whose binary this editor talks to, the way Go is |
| 81 | // the language and gc the compiler. This is what the About box and the |
| 82 | // status bar read out, so it names the language. |
| 83 | Language: "Golo", |
| 84 | // G is free: the fixed menus take F, E, S, R, C, O, W, N and H — |
| 85 | // which rules out the O of Golo but not its first letter — so the hot |
| 86 | // key lands where a reader expects it. The menu is named after the |
| 87 | // language and not after golo, gogolo or wagolo, because it holds |
| 88 | // whatever the project put in its tools file, and the first tools |
| 89 | // file anybody writes outgrows the language's own toolchain. |
| 90 | ToolsMenu: "~G~olo", |
| 91 | // None, on purpose. Golo has no project manifest — no golo.mod, no |
| 92 | // build file — a script is a file and a program is a directory of |
| 93 | // them. With nothing to look for, app.ProjectRoot hands the server |
| 94 | // the directory of the file being edited, which is also all the |
| 95 | // server needs: golo lsp answers about the file it is given and |
| 96 | // resolves imports from the modules embedded in the binary, never |
| 97 | // from disk. A marker would make the walk look like part of the rule |
| 98 | // when there is no rule. |
| 99 | RootMarkers: nil, |
| 100 | Server: profile.Server{ |
| 101 | Command: ServerCommand, |
| 102 | Args: ServerArgs(), |
| 103 | InstallHint: InstallHint, |
| 104 | Dirs: ServerDirs(), |
| 105 | }, |
| 106 | Templates: profile.Templates{ |
| 107 | Settings: settingsTemplate, |
| 108 | Snippets: snippetsTemplate, |
| 109 | Tools: toolsTemplate, |
| 110 | Agents: agentsTemplate, |
| 111 | }, |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | // Register teaches turbo-core to colour Golo. |
| 116 | // |
| 117 | // It is called explicitly at start-up rather than from an init function so that |
| 118 | // "which languages does this editor know?" is answered by reading main, not by |
| 119 | // working out which packages were imported. |
| 120 | // |
| 121 | // A shebang is claimed. A Golo script may open with #!/usr/bin/env golo and be |
| 122 | // run as a command: # opens a comment in Golo, so the interpreter reads the |
| 123 | // line as one, and a file with no extension whose first line names golo is |
| 124 | // Golo and nothing else. |
| 125 | func Register() { |
| 126 | syntax.Register(syntax.Definition{ |
| 127 | Language: Language, |
| 128 | Extensions: []string{".golo"}, |
| 129 | Shebangs: []string{"golo"}, |
| 130 | Highlight: Highlight, |
| 131 | }) |
| 132 | } |
| 133 | |
| 134 | // ServerDirs returns the directories golo is looked for in after PATH. |
| 135 | // |
| 136 | // There is one: the directory GoloScript's installer writes into. Golo has no |
| 137 | // per-user toolchain directory and no environment variable naming one, so |
| 138 | // there is nothing else to search — and listing a guess would be worse than |
| 139 | // listing nothing, because a directory the server is never in costs a stat |
| 140 | // on every start for no answer. |
| 141 | func ServerDirs() []string { |
| 142 | return []string{DefaultInstallDir} |
| 143 | } |