// Package gololang is everything about Turbo Golo that is about *Golo*: how // the editor names itself, which language server it talks to, what a project's // starter files say, and how Golo source is coloured. // // Everything else the editor does lives in turbo-core, which knows nothing // about Golo. This package is the whole of the difference between Turbo Golo // and Turbo MoonBit, which is what makes a sixth editor a matter of writing one // of these rather than forking anything. // // gololang.Register() // teach the library to colour Golo // editor := app.New(screen, name, gololang.Profile()) package gololang import ( "rickub.com/turbo-editors/turbo-core/profile" "rickub.com/turbo-editors/turbo-core/syntax" ) // Name and Slug are what the editor calls itself. The slug is also its binary, // its project directory (as .turbo-golo) and the stem of its environment // variables (as TURBO_GOLO_…), so it is not free to change. const ( Name = "Turbo Golo" Slug = "turbo-golo" ) // Language is the name Golo is known by: the value LanguageOf returns for a // .golo file, and what a snippets file writes in its languages key. // // It is lower case because every other name in the registry is — "golo" // beside "toml" and "dockerfile" — while the language's own spelling, the one // a person reads, is Profile().Language. const Language syntax.Language = "golo" // ServerCommand is the language server Turbo Golo talks to, and InstallHint // the one place a user gets it from. // // The server is the interpreter itself: `golo lsp` puts the same binary that // runs a script into language-server mode, reusing its lexer, parser and AST. // So a machine that can run Golo can complete Golo, and there is nothing // separate to install — which is why the hint names the release page that // ships the binary rather than a package manager's command. // // It answers four of the nine questions turbo-core asks — completion, hover, // definition and the file's symbols — and publishes diagnostics unasked. It // advertises neither references, typeDefinition and implementation, nor // workspace/symbol, so those items report nothing found. That is documented // rather than worked around, and a test asserts it so a future golo gaining // them is noticed. const ( ServerCommand = "golo" InstallHint = "see https://codeberg.org/TypeUnsafe/golo-script/releases" ) // DefaultInstallDir is where GoloScript's own install.sh puts golo, gogolo and // wagolo. It is on PATH on almost every machine, and it is searched all the // same for the one where it is not: "completion silently does nothing" is what // a user sees when the editor cannot find a server they believe they installed. const DefaultInstallDir = "/usr/local/bin" // ServerArgs is what golo is started with. // // The lsp is not optional: golo with no argument starts its REPL and reads // standard input as Golo source, which the editor would see as a server that // answers nothing and never dies. It is a function rather than a variable so // that no caller can append to the package's own slice. func ServerArgs() []string { return []string{"lsp"} } // Profile returns the editor Turbo Golo is. // // It is a function rather than a variable for the same reason as in every other // editor of this family, even though nothing here reads the environment: a // caller that mutates what it gets back — appending to Server.Dirs, say — must // not be mutating the package's one copy. func Profile() profile.Profile { return profile.Profile{ Name: Name, Slug: Slug, // The language's own spelling. Golo is the language; GoloScript is // the implementation whose binary this editor talks to, the way Go is // the language and gc the compiler. This is what the About box and the // status bar read out, so it names the language. Language: "Golo", // G is free: the fixed menus take F, E, S, R, C, O, W, N and H — // which rules out the O of Golo but not its first letter — so the hot // key lands where a reader expects it. The menu is named after the // language and not after golo, gogolo or wagolo, because it holds // whatever the project put in its tools file, and the first tools // file anybody writes outgrows the language's own toolchain. ToolsMenu: "~G~olo", // None, on purpose. Golo has no project manifest — no golo.mod, no // build file — a script is a file and a program is a directory of // them. With nothing to look for, app.ProjectRoot hands the server // the directory of the file being edited, which is also all the // server needs: golo lsp answers about the file it is given and // resolves imports from the modules embedded in the binary, never // from disk. A marker would make the walk look like part of the rule // when there is no rule. RootMarkers: nil, Server: profile.Server{ Command: ServerCommand, Args: ServerArgs(), InstallHint: InstallHint, Dirs: ServerDirs(), }, Templates: profile.Templates{ Settings: settingsTemplate, Snippets: snippetsTemplate, Tools: toolsTemplate, Agents: agentsTemplate, }, } } // Register teaches turbo-core to colour Golo. // // It is called explicitly at start-up rather than from an init function so that // "which languages does this editor know?" is answered by reading main, not by // working out which packages were imported. // // A shebang is claimed. A Golo script may open with #!/usr/bin/env golo and be // run as a command: # opens a comment in Golo, so the interpreter reads the // line as one, and a file with no extension whose first line names golo is // Golo and nothing else. func Register() { syntax.Register(syntax.Definition{ Language: Language, Extensions: []string{".golo"}, Shebangs: []string{"golo"}, Highlight: Highlight, }) } // ServerDirs returns the directories golo is looked for in after PATH. // // There is one: the directory GoloScript's installer writes into. Golo has no // per-user toolchain directory and no environment variable naming one, so // there is nothing else to search — and listing a guess would be worse than // listing nothing, because a directory the server is never in costs a stat // on every start for no answer. func ServerDirs() []string { return []string{DefaultInstallDir} }