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

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"path/filepath"
     6  	"strings"
     7  
     8  	"github.com/xyproto/env/v2"
     9  	"github.com/xyproto/mode"
    10  	"github.com/xyproto/vt100"
    11  )
    12  
    13  // TemplateProgram represents a string and cursor movement up, and then to the right
    14  // which can be used to position the cursor after inserting a string.
    15  type TemplateProgram struct {
    16  	text  string
    17  	right int
    18  	up    int
    19  }
    20  
    21  // TemplatePrograms maps from editor mode to simple example programs.
    22  type TemplatePrograms map[mode.Mode]TemplateProgram
    23  
    24  var templatePrograms TemplatePrograms
    25  
    26  // GetTemplatePrograms will return a map from editor mode to per-programming-language template program.
    27  // It is done this way to only initialize the map once, but not at the time when the program starts.
    28  func GetTemplatePrograms() TemplatePrograms {
    29  	if templatePrograms == nil {
    30  		fullName := getFullName()
    31  		// NOTE: Cursor coordinates are (X, -Y)
    32  		templatePrograms = TemplatePrograms{
    33  			mode.Agda: {
    34  				"module FILENAME where\n\nopen import Agda.Builtin.IO using (IO)\nopen import Agda.Builtin.Unit using (⊤)\nopen import Agda.Builtin.String using (String)\n\npostulate putStrLn : String → IO ⊤\n{-# FOREIGN GHC import qualified Data.Text as T #-}\n{-# COMPILE GHC putStrLn = putStrLn . T.unpack #-}\n\nmain : IO ⊤\nmain = putStrLn \"Hello, World!\"\n",
    35  				17,
    36  				1,
    37  			},
    38  			mode.Algol68: {
    39  				"print((\"Hello, World!\", newline))\n",
    40  				8,
    41  				1,
    42  			},
    43  			mode.Arduino: {
    44  				"void setup() {\n  Serial.begin(9600);\n  Serial.println(\"Hello, World!\");\n}\n\nvoid loop() {\n}\n",
    45  				16,
    46  				5,
    47  			},
    48  			mode.C: {
    49  				"#include <stdio.h>\n#include <stdlib.h>\n\nint main(int argc, char* argv[])\n{\n\tprintf(\"%s\\n\", \"Hello, World!\");\n\treturn EXIT_SUCCESS;\n}\n",
    50  				8,
    51  				3,
    52  			},
    53  			mode.Cpp: {
    54  				"#include <cstdlib>\n#include <iostream>\n#include <string>\n\nusing namespace std::string_literals;\n\nint main(int argc, char** argv)\n{\n    std::cout << \"Hello, World!\"s << std::endl;\n    return EXIT_SUCCESS;\n}\n",
    55  				14,
    56  				3,
    57  			},
    58  			mode.Clojure: {
    59  				"(ns example.hello\n  (:gen-class))\n\n(defn hello-world []\n  (println \"Hello, World!\"))\n\n(hello-world)\n",
    60  				10,
    61  				3,
    62  			},
    63  			mode.Crystal: {
    64  				"class Greeter\n  def initialize(@name : String)\n  end\n\n  def greet\n    puts \"Hello, #{@name}!\"\n  end\nend\n\nGreeter.new(\"World\").greet\n",
    65  				6,
    66  				5,
    67  			},
    68  			mode.CMake: {
    69  				"cmake_minimum_required(VERSION 3.5)\n\nproject(cmake-project-template)\n\nset(CMAKE_CXX_STANDARD 17)\nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -std=c++17 -O2\")\n\nset(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR})\n\nset(DIVISIBLE_INSTALL_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include)\nset(DIVISIBLE_INSTALL_BIN_DIR ${PROJECT_SOURCE_DIR}/bin)\nset(DIVISIBLE_INSTALL_LIB_DIR ${PROJECT_SOURCE_DIR}/lib)\n\nset(DIVISION_HEADERS_DIR ${PROJECT_SOURCE_DIR}/src/division)\n\ninclude_directories(${DIVISIBLE_INSTALL_INCLUDE_DIR})\ninclude_directories(${DIVISION_HEADERS_DIR})\n\nadd_subdirectory(src)\nadd_subdirectory(test)\n",
    70  				8,
    71  				18,
    72  			},
    73  			mode.CS: {
    74  				"using System;\n\nclass Greeter {\n    public static void Main(string[] args) {\n        Console.WriteLine(\"Hello, World!\");\n    }\n}\n",
    75  				19,
    76  				3,
    77  			},
    78  			mode.CSS: {
    79  				"body, h1, h2, h3, h4, h5, h6, p, ul, ol, li, figure, figcaption, blockquote, dl, dd {\n  margin: 0;\n  padding: 0;\n}\n\nbody {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\n  font-size: 16px; /* Good for readability */\n  line-height: 1.6; /* Good for readability */\n  color: #333; /* Sufficient contrast for readability */\n  background-color: #fff; /* Light background to ensure contrast */\n}\n\na, button {\n  color: #0066cc; /* Color should meet WCAG contrast ratio */\n  text-decoration: none; /* Underlines can be confusing for some dyslexic users */\n}\n\na:hover, a:focus, button:hover, button:focus {\n  text-decoration: underline; /* Indicate interactivity */\n  outline: none; /* Custom focus styles are more visually appealing */\n}\n\n.container {\n  width: 100%;\n  margin-right: auto;\n  margin-left: auto;\n  padding-right: 15px;\n  padding-left: 15px;\n}\n\n@media (min-width: 576px) { .container { max-width: 540px; } }\n@media (min-width: 768px) { .container { max-width: 720px; } }\n@media (min-width: 992px) { .container { max-width: 960px; } }\n@media (min-width: 1200px) { .container { max-width: 1140px; } }\n\n.padding-1 { padding: 0.25rem; }\n.padding-2 { padding: 0.5rem; }\n.padding-3 { padding: 1rem; }\n.margin-1 { margin: 0.25rem; }\n.margin-2 { margin: 0.5rem; }\n.margin-3 { margin: 1rem; }\n\n.flex-row {\n  display: flex;\n  flex-direction: row;\n}\n\n.flex-column {\n  display: flex;\n  flex-direction: column;\n}\n\n.justify-center {\n  justify-content: center;\n}\n\n.align-center {\n  align-items: center;\n}\n\nh1 { font-size: 2.25rem; }\nh2 { font-size: 1.8rem; }\nh3 { font-size: 1.5rem; }\nh4 { font-size: 1.2rem; }\nh5 { font-size: 1rem; }\nh6 { font-size: 0.875rem; }\n\n@media (max-width: 768px) {\n  h1 { font-size: 2rem; }\n  h2 { font-size: 1.75rem; }\n  h3 { font-size: 1.5rem; }\n}\n\n:focus-visible {\n  outline: 3px solid #ffbf47;\n  outline-offset: 3px;\n}\n\nlabel {\n  display: block;\n  margin-bottom: .5rem;\n}\n\ninput, select, textarea, button {\n  font: inherit; /* Ensure inputs use the same font */\n  color: inherit; /* Ensure inputs use the same color */\n  padding: .5rem;\n  margin-bottom: 1rem; /* Space out elements */\n  border: 1px solid #ccc; /* Slight border for definition */\n  border-radius: 4px; /* Modern look with rounded corners */\n}\n\ninput:focus, select:focus, textarea:focus, button:focus {\n  border-color: #0066cc;\n  box-shadow: 0 0 0 3px rgba(0,102,204,0.25);\n}\n\nimg, video {\n  max-width: 100%;\n  height: auto;\n}\n",
    80  				1,
    81  				1,
    82  			},
    83  			mode.D: {
    84  				"module main;\n\nimport std.stdio;\n\nvoid main(string[] args) {\n    writeln(\"Hello, World!\");\n}\n",
    85  				9,
    86  				2,
    87  			},
    88  			mode.Dart: {
    89  				"void main() {\n  print('Hello, World!');\n}\n",
    90  				7,
    91  				2,
    92  			},
    93  			mode.Email: {
    94  				"Hello ,\n\nBest regards,\n" + fullName + "\n",
    95  				6,
    96  				5,
    97  			},
    98  			mode.Erlang: {
    99  				"-module(hello).\n-export([hello_world/0]).\n\nhello_world() -> io:fwrite(\"hello, world\\n\").\n",
   100  				29,
   101  				2,
   102  			},
   103  			mode.Fortran77: {
   104  				"* Fortran 77\n       PRINT *, 'Hello, World!'\n       END\n",
   105  				10,
   106  				2,
   107  			},
   108  			mode.Fortran90: {
   109  				"program hello\n  ! Output the message\n  print *, 'Hello, World!'\nend program hello\n",
   110  				10,
   111  				2,
   112  			},
   113  			mode.Garnet: {
   114  				"fn main(): {} =\n    __print_str(\"Hello, World!\")\nend\n",
   115  				10,
   116  				2,
   117  			},
   118  			mode.GDScript: { // WIP
   119  				"extends Node\n\nexport var x = 42\nvar y = 64\n\nfunc _ready():\n\ty = 96\n\nfunc _process(delta):\n\ty = 128\n\n",
   120  				6,
   121  				5,
   122  			},
   123  			mode.Go: {
   124  				"package main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tfmt.Println(\"Hello, World!\")\n}\n",
   125  				13,
   126  				2,
   127  			},
   128  			mode.Haxe: {
   129  				"class Main {\n    static public function main():Void {\n        trace(\"Hello, World!\");\n    }\n}",
   130  				7,
   131  				2,
   132  			},
   133  			mode.Hare: {
   134  				"use fmt;\n\nexport fn main() void = {\n	fmt::println(\"Hello, World!\")!;\n};\n",
   135  				14,
   136  				2,
   137  			},
   138  			mode.Haskell: {
   139  				"main :: IO ()\nmain = putStrLn \"Hello, World!\"\n",
   140  				17,
   141  				1,
   142  			},
   143  			mode.HTML: {
   144  				"<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    <title>Hello</title>\n    <meta name=\"description\" content=\"Hello\">\n    <link rel=\"shortcut icon\" href=\"https://www.iconsdb.com/icons/download/orange/teapot-16.png\">\n    <link rel=\"stylesheet\" href=\"https://unpkg.com/@picocss/pico@latest/css/pico.classless.min.css\">\n  </head>\n  <body>\n    <header>\n      <hgroup>\n        <h1>Greetings</h1>\n        <h2>About to greet <code>the world</code></h2>\n      </hgroup>\n    </header>\n    <main>\n      <section id=\"hello\">\n        <h2>Hello, World!</h2>\n        Task completed.\n      </section>\n    </main>\n    <footer>\n      All done.\n    </footer>\n  </body>\n</html>\n",
   145  				4,
   146  				9,
   147  			},
   148  			mode.Inko: {
   149  				"import std.stdio.STDOUT\n\nclass async Main {\n  fn async main {\n    STDOUT.new.print('Hello, World!')\n  }\n}\n",
   150  				18,
   151  				3,
   152  			},
   153  			mode.Jakt: {
   154  				"function main() {\n    println(\"Hello, World!\")\n}\n",
   155  				9,
   156  				2,
   157  			},
   158  			mode.Java: {
   159  				"class Greeter {\n    public static void main(String[] args) {\n        System.out.println(\"Hello, World!\");\n    }\n}\n",
   160  				20,
   161  				3,
   162  			},
   163  			mode.JavaScript: {
   164  				"console.log('Hello, World!');\n",
   165  				13,
   166  				1,
   167  			},
   168  			mode.Just: {
   169  				"#!/usr/bin/env just --justfile\n\nhello:\n\t@echo \"Hello, World!\"\n",
   170  				7,
   171  				1,
   172  			},
   173  			mode.Koka: {
   174  				"fun main() {\n  println(\"Hello, World!\")\n}\n",
   175  				9,
   176  				2,
   177  			},
   178  			mode.Kotlin: {
   179  				"fun main() {\n    println(\"Hello, World!\")\n}\n",
   180  				9,
   181  				2,
   182  			},
   183  			mode.Lilypond: {
   184  				"\\version \"2.24.2\"\n\n\\score {\n  \\relative c' {\n    c d e f g a b c\n  }\n}\n",
   185  				0,
   186  				3,
   187  			},
   188  			mode.Lua: {
   189  				"print(\"Hello, World!\")\n",
   190  				7,
   191  				1,
   192  			},
   193  			mode.Markdown: {
   194  				"# Title\n\n## Subtitle\n\ntext\n",
   195  				4,
   196  				1,
   197  			},
   198  			mode.Mojo: {
   199  				"print(\"Hello, World!\")\n",
   200  				7,
   201  				1,
   202  			},
   203  			mode.Nim: {
   204  				"echo \"Hello, World!\"\n",
   205  				6,
   206  				1,
   207  			},
   208  			mode.ObjectPascal: {
   209  				"program Hello;\nconst\n  greeting = 'Hello, World!';\nbegin\n  writeln(greeting);\nend.\n",
   210  				12,
   211  				4,
   212  			},
   213  			mode.OCaml: {
   214  				"print_string \"Hello world!\\n\"",
   215  				14,
   216  				2,
   217  			},
   218  			mode.Odin: {
   219  				"package main\n\nimport \"core:fmt\"\n\nmain :: proc() {\n    fmt.println(\"Hello, World!\");\n}\n",
   220  				13,
   221  				2,
   222  			},
   223  			mode.Perl: {
   224  				"#!/usr/bin/env perl\n\nuse strict;\nuse utf8;\nuse warnings;\n\nbinmode(\\*STDOUT, \":utf8\");\nbinmode(\\*STDIN,  \":utf8\");\nbinmode(\\*STDERR, \":utf8\");\n\nsub main {\n  print(\"Hello, World!\\n\");\n}\n\nmain();\n",
   225  				7,
   226  				4,
   227  			},
   228  			mode.Python: {
   229  				"#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\ndef main():\n    print(\"Hello, World!\")\n\n\nif __name__ == \"__main__\":\n    main()\n",
   230  				7,
   231  				5,
   232  			},
   233  			mode.R: {
   234  				"print(\"Hello, World!\", quote=FALSE)",
   235  				7,
   236  				1,
   237  			},
   238  			mode.Rust: {
   239  				"fn main() {\n    println!(\"Hello, World!\");\n}\n",
   240  				10,
   241  				2,
   242  			},
   243  			mode.Scala: {
   244  				"object Hello {\n\tdef main(args: Array[String]) = {\n\t\tprintln(\"Hello, World!\")\n\t}\n}\n",
   245  				9,
   246  				3,
   247  			},
   248  			mode.Shell: {
   249  				"# Maintainer: " + fullName + " <" + env.Str("EMAIL", "email") + ">\n\npkgname=\npkgver=1.0.0\npkgrel=1\npkgdesc='Example application'\narch=(x86_64)\nurl='https://github.com/example/application'\nlicense=(BSD3)\nmakedepends=(git go)\nsource=(\"git+$url#commit=asdf\") # tag: v1.0.0\nb2sums=(SKIP)\n\nbuild() {\n  cd $pkgname\n  go build -v -mod=vendor -buildmode=pie -trimpath -ldflags=\"-s -w -extldflags \\\"${LDFLAGS}\\\"\"\n}\n\npackage() {\n  install -Dm755 $pkgname/$pkgname \"$pkgdir/usr/bin/$pkgname\"\n  install -Dm644 $pkgname/LICENSE \"$pkgdir/usr/share/licenses/$pkgname/LICENSE\"\n}\n",
   250  				8,
   251  				20,
   252  			},
   253  			// This one is a bit more elaborate than strictly needed
   254  			mode.StandardML: {
   255  				"let\n  val name = \"World\"\nin\n  map (fn x => (print (\"Hello, \" ^ x ^ \"!\\n\"))) [name]\nend;",
   256  				22,
   257  				1,
   258  			},
   259  			mode.Swift: {
   260  				"print(\"Hello, World!\")\n",
   261  				7,
   262  				1,
   263  			},
   264  			mode.Teal: {
   265  				"print(\"Hello, World!\")\n",
   266  				7,
   267  				1,
   268  			},
   269  			mode.TypeScript: {
   270  				"console.log('Hello, World!');\n",
   271  				13,
   272  				1,
   273  			},
   274  			mode.V: {
   275  				"fn main() {\n    name := 'World'\n    println('Hello, $name!')\n}\n",
   276  				9,
   277  				2,
   278  			},
   279  			mode.Zig: {
   280  				"const std = @import(\"std\");\n\npub fn main() !void {\n    const stdout = std.io.getStdOut().writer();\n    try stdout.print(\"Hello, World!\\n\", .{});\n}\n",
   281  				18,
   282  				2,
   283  			},
   284  		}
   285  	}
   286  	return templatePrograms
   287  }
   288  
   289  // BaseFilenameWithoutExtension returns the base filename, without the extension
   290  // For instance, "/some/where/main.c" becomes just "main".
   291  func (e *Editor) BaseFilenameWithoutExtension() string {
   292  	baseFilename := filepath.Base(e.filename)
   293  	ext := filepath.Ext(baseFilename)
   294  	return strings.TrimSuffix(baseFilename, ext)
   295  }
   296  
   297  // InsertTemplateProgram will insert a template program at the current cursor position,
   298  // if available. It will then reposition the cursor at an appropriate place in the template.
   299  func (e *Editor) InsertTemplateProgram(c *vt100.Canvas) error {
   300  	prog, found := GetTemplatePrograms()[e.mode]
   301  	if !found {
   302  		return fmt.Errorf("could not find a template program for %s", e.mode)
   303  	}
   304  
   305  	baseFilenameWithoutExt := e.BaseFilenameWithoutExtension()
   306  
   307  	// If the mode is Go and this is a test file, insert a test template instead
   308  	if e.mode == mode.Go && strings.HasSuffix(baseFilenameWithoutExt, "_test") {
   309  		prog = TemplateProgram{
   310  			"package main\n\nimport (\n\t\"testing\"\n)\n\nfunc TestSomething(t *testing.T) {\n\tt.Fail()\n}\n",
   311  			7,
   312  			2,
   313  		}
   314  	}
   315  
   316  	// Replace FILENAME with the base filename without extension, introduced because of Agda.
   317  	prog.text = strings.ReplaceAll(prog.text, "FILENAME", e.BaseFilenameWithoutExtension())
   318  
   319  	// Insert the template program
   320  	e.InsertStringAndMove(c, prog.text)
   321  
   322  	// Move the cursor up and to the right
   323  	for x := 0; x < prog.up; x++ {
   324  		e.Up(c, nil)
   325  	}
   326  	for x := 0; x < prog.right; x++ {
   327  		e.Next(c)
   328  	}
   329  
   330  	return nil
   331  }