Article Title
7 |Article content
9 | 12 |├── .editorconfig
├── .gitattributes
├── .github
└── workflows
│ └── test.yaml
├── .gitignore
├── .version
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── Taskfile.yml
├── bundle
├── Syntaxes
│ └── goht.tmLanguage.json
└── info.plist
├── cmd
└── goht
│ ├── LICENSE
│ ├── cmd
│ ├── generate.go
│ ├── lsp.go
│ └── root.go
│ └── main.go
├── compiler
├── errors.go
├── helpers_test.go
├── lexer.go
├── lexer_iface.go
├── lexer_test.go
├── lexers.go
├── lexers_ego.go
├── lexers_ego_test.go
├── lexers_haml.go
├── lexers_haml_test.go
├── lexers_slim.go
├── lexers_slim_test.go
├── nodes.go
├── nodes_test.go
├── parser.go
├── render_test.go
├── source_map.go
├── source_map_test.go
├── template.go
├── template_test.go
├── testdata
│ ├── attributes.goht
│ ├── attributes.goht.go
│ ├── attributes.html
│ ├── comments.goht
│ ├── comments.goht.go
│ ├── comments.html
│ ├── conditionals.false.html
│ ├── conditionals.goht
│ ├── conditionals.goht.go
│ ├── conditionals.true.html
│ ├── ego_template.goht
│ ├── ego_template.goht.go
│ ├── ego_template.html
│ ├── elements.goht
│ ├── elements.goht.go
│ ├── elements.html
│ ├── filters.goht
│ ├── filters.goht.go
│ ├── filters.html
│ ├── imports.goht
│ ├── imports.goht.go
│ ├── imports.html
│ ├── interpolation.goht
│ ├── interpolation.goht.go
│ ├── interpolation.html
│ ├── nesting.html
│ ├── newlines.goht
│ ├── newlines.goht.go
│ ├── newlines.html
│ ├── obj_references.goht
│ ├── obj_references.goht.go
│ ├── obj_references.html
│ ├── package.goht
│ ├── package.goht.go
│ ├── package.html
│ ├── rendering.goht
│ ├── rendering.goht.go
│ ├── rendering.html
│ ├── slim_template.goht
│ ├── slim_template.goht.go
│ ├── slim_template.html
│ ├── slots.html
│ ├── slots_with_defaults.html
│ ├── whitespace.goht
│ ├── whitespace.goht.go
│ ├── whitespace.html
│ └── without_children.html
├── tokens.go
├── tokens_test.go
└── utils.go
├── docs
├── goht_header.png
├── goht_header_html.png
└── vscode_ide_example.png
├── examples
├── attributes
│ ├── additional.goht
│ ├── additional.goht.go
│ ├── classes.goht
│ ├── classes.goht.go
│ ├── general.goht
│ ├── general.goht.go
│ ├── names.goht
│ ├── names.goht.go
│ ├── optional.goht
│ └── optional.goht.go
├── commands
│ ├── children.goht
│ ├── children.goht.go
│ ├── render.goht
│ ├── render.goht.go
│ ├── slots.goht
│ └── slots.goht.go
├── comments
│ ├── html.goht
│ ├── html.goht.go
│ ├── rubystyle.goht
│ └── rubystyle.goht.go
├── doctype
│ ├── doctype.goht
│ └── doctype.goht.go
├── examples_test.go
├── filters
│ ├── css.goht
│ ├── css.goht.go
│ ├── javascript.goht
│ ├── javascript.goht.go
│ ├── text.goht
│ └── text.goht.go
├── formatting
│ ├── formats.goht
│ └── formats.goht.go
├── go
│ ├── code.goht
│ ├── code.goht.go
│ ├── doc.goht
│ ├── doc.goht.go
│ ├── imports.goht
│ ├── imports.goht.go
│ ├── inlining.goht
│ ├── inlining.goht.go
│ ├── interpolation.goht
│ ├── interpolation.goht.go
│ ├── package.goht
│ ├── package.goht.go
│ ├── receivers.goht
│ └── receivers.goht.go
├── hello
│ ├── world.goht
│ └── world.goht.go
├── indents
│ ├── tabs.goht
│ └── tabs.goht.go
├── tags
│ ├── general.goht
│ ├── general.goht.go
│ ├── inline.goht
│ ├── inline.goht.go
│ ├── objectreference.goht
│ ├── objectreference.goht.go
│ ├── selfclosing.goht
│ ├── selfclosing.goht.go
│ ├── whitespace.goht
│ └── whitespace.goht.go
├── testdata
│ ├── ego
│ │ └── hello_world.html
│ ├── haml
│ │ ├── attributes_attributesCmd.html
│ │ ├── attributes_classes.html
│ │ ├── attributes_complexNames.html
│ │ ├── attributes_conditionalAttrs.html
│ │ ├── attributes_dynamicAttrs.html
│ │ ├── attributes_formattedValue.html
│ │ ├── attributes_multilineAttrs.html
│ │ ├── attributes_simpleNames.html
│ │ ├── attributes_staticAttrs.html
│ │ ├── attributes_whitespaceAttrs.html
│ │ ├── commands_childrenExample.html
│ │ ├── commands_renderExample.html
│ │ ├── commands_renderWithChildrenExample.html
│ │ ├── comments_htmlComments.html
│ │ ├── comments_htmlCommentsNested.html
│ │ ├── comments_rubyStyle.html
│ │ ├── comments_rubyStyleNested.html
│ │ ├── doctype_doctype.html
│ │ ├── example_conditional.html
│ │ ├── example_doc.html
│ │ ├── example_escapeInterpolation.html
│ │ ├── example_executeCode.html
│ │ ├── example_ignoreInterpolation.html
│ │ ├── example_importExample.html
│ │ ├── example_interpolateCode.html
│ │ ├── example_noInterpolation.html
│ │ ├── example_packageExample.html
│ │ ├── example_renderCode.html
│ │ ├── example_shorthandConditional.html
│ │ ├── example_shorthandSwitch.html
│ │ ├── example_userDetails.html
│ │ ├── filters_css.html
│ │ ├── filters_escaped.html
│ │ ├── filters_javascript.html
│ │ ├── filters_plain.html
│ │ ├── filters_preserve.html
│ │ ├── formatting_boolExample.html
│ │ ├── formatting_floatExample.html
│ │ ├── formatting_intExample.html
│ │ ├── formatting_stringExample.html
│ │ ├── hello_world.html
│ │ ├── indents_usingTabs.html
│ │ ├── tags_alsoSelfClosing.html
│ │ ├── tags_combined.html
│ │ ├── tags_defaultToDivs.html
│ │ ├── tags_multipleClasses.html
│ │ ├── tags_objectRefs.html
│ │ ├── tags_prefixedObjectRefs.html
│ │ ├── tags_removeWhitespace.html
│ │ ├── tags_selfClosing.html
│ │ ├── tags_specifyTag.html
│ │ ├── tags_whitespace.html
│ │ ├── unescape_unescapeCode.html
│ │ ├── unescape_unescapeInterpolation.html
│ │ └── unescape_unescapeText.html
│ └── slim
│ │ ├── attributes_attributesCmd.html
│ │ ├── attributes_classes.html
│ │ ├── attributes_complexNames.html
│ │ ├── attributes_conditionalAttrs.html
│ │ ├── attributes_dynamicAttrs.html
│ │ ├── attributes_formattedValue.html
│ │ ├── attributes_multilineAttrs.html
│ │ ├── attributes_simpleNames.html
│ │ ├── attributes_staticAttrs.html
│ │ ├── attributes_whitespaceAttrs.html
│ │ ├── commands_childrenExample.html
│ │ ├── commands_renderExample.html
│ │ ├── commands_renderWithChildrenExample.html
│ │ ├── comments_htmlComments.html
│ │ ├── comments_htmlCommentsNested.html
│ │ ├── comments_rubyStyle.html
│ │ ├── comments_rubyStyleNested.html
│ │ ├── doctype_doctype.html
│ │ ├── example_conditional.html
│ │ ├── example_doc.html
│ │ ├── example_escapeInterpolation.html
│ │ ├── example_executeCode.html
│ │ ├── example_ignoreInterpolation.html
│ │ ├── example_importExample.html
│ │ ├── example_interpolateCode.html
│ │ ├── example_noInterpolation.html
│ │ ├── example_packageExample.html
│ │ ├── example_renderCode.html
│ │ ├── example_shorthandConditional.html
│ │ ├── example_shorthandSwitch.html
│ │ ├── example_userDetails.html
│ │ ├── filters_css.html
│ │ ├── filters_javascript.html
│ │ ├── formatting_boolExample.html
│ │ ├── formatting_floatExample.html
│ │ ├── formatting_intExample.html
│ │ ├── formatting_stringExample.html
│ │ ├── hello_world.html
│ │ ├── indents_usingTabs.html
│ │ ├── tags_addWhitespace.html
│ │ ├── tags_alsoSelfClosing.html
│ │ ├── tags_combined.html
│ │ ├── tags_defaultToDivs.html
│ │ ├── tags_inlineTags.html
│ │ ├── tags_multipleClasses.html
│ │ ├── tags_selfClosing.html
│ │ ├── tags_specifyTag.html
│ │ ├── tags_whitespace.html
│ │ └── unescape_unescapeCode.html
└── unescaping
│ ├── unescape.goht
│ └── unescape.goht.go
├── go.mod
├── go.sum
├── helpers.go
├── internal
├── logging
│ ├── logged_stream.go
│ └── logger.go
├── protocol
│ ├── LICENSE
│ ├── protocol.go
│ ├── tsclient.go
│ ├── tsdocument_changes.go
│ ├── tsjson.go
│ ├── tsprotocol.go
│ ├── tsserver.go
│ ├── uri.go
│ └── util.go
└── proxy
│ ├── client.go
│ ├── diagnostics_cache.go
│ ├── document.go
│ ├── document_contents.go
│ ├── file_names.go
│ ├── server.go
│ └── source_map_cache.go
├── runtime.go
└── version.go
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | indent_size = 2
7 | indent_style = tab
8 | insert_final_newline = true
9 | max_line_length = 140
10 | trim_trailing_whitespace = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto eol=lf
2 |
--------------------------------------------------------------------------------
/.github/workflows/test.yaml:
--------------------------------------------------------------------------------
1 | name: Test & Coverage
2 | on:
3 | push:
4 | branches:
5 | - main
6 | pull_request:
7 | branches:
8 | - main
9 |
10 | # a single job to run tests and coverage for a Golang project; report coverage to Coveralls
11 | jobs:
12 | test:
13 | name: Test
14 | runs-on: ubuntu-latest
15 | steps:
16 | - name: Checkout
17 | uses: actions/checkout@v4
18 | - name: Setup Go
19 | uses: actions/setup-go@v5
20 | with:
21 | go-version: 1.21
22 | - name: Install dependencies
23 | run: go mod download
24 | - name: Run tests
25 | run: go test -v -vet=all -coverprofile=profile.cov ./...
26 | - name: Install goveralls
27 | run: go install github.com/mattn/goveralls@latest
28 | - name: Report coverage to Coveralls
29 | env:
30 | COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
31 | run: goveralls -coverprofile=profile.cov -service=github
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.toptal.com/developers/gitignore/api/goland+all,windows,macos
2 | # Edit at https://www.toptal.com/developers/gitignore?templates=goland+all,windows,macos
3 |
4 | ### GoLand+all ###
5 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
6 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
7 |
8 | # User-specific stuff
9 | .idea/**/workspace.xml
10 | .idea/**/tasks.xml
11 | .idea/**/usage.statistics.xml
12 | .idea/**/dictionaries
13 | .idea/**/shelf
14 |
15 | # AWS User-specific
16 | .idea/**/aws.xml
17 |
18 | # Generated files
19 | .idea/**/contentModel.xml
20 |
21 | # Sensitive or high-churn files
22 | .idea/**/dataSources/
23 | .idea/**/dataSources.ids
24 | .idea/**/dataSources.local.xml
25 | .idea/**/sqlDataSources.xml
26 | .idea/**/dynamic.xml
27 | .idea/**/uiDesigner.xml
28 | .idea/**/dbnavigator.xml
29 |
30 | # Gradle
31 | .idea/**/gradle.xml
32 | .idea/**/libraries
33 |
34 | # Gradle and Maven with auto-import
35 | # When using Gradle or Maven with auto-import, you should exclude module files,
36 | # since they will be recreated, and may cause churn. Uncomment if using
37 | # auto-import.
38 | # .idea/artifacts
39 | # .idea/compiler.xml
40 | # .idea/jarRepositories.xml
41 | # .idea/modules.xml
42 | # .idea/*.iml
43 | # .idea/modules
44 | # *.iml
45 | # *.ipr
46 |
47 | # CMake
48 | cmake-build-*/
49 |
50 | # Mongo Explorer plugin
51 | .idea/**/mongoSettings.xml
52 |
53 | # File-based project format
54 | *.iws
55 |
56 | # IntelliJ
57 | out/
58 |
59 | # mpeltonen/sbt-idea plugin
60 | .idea_modules/
61 |
62 | # JIRA plugin
63 | atlassian-ide-plugin.xml
64 |
65 | # Cursive Clojure plugin
66 | .idea/replstate.xml
67 |
68 | # SonarLint plugin
69 | .idea/sonarlint/
70 |
71 | # Crashlytics plugin (for Android Studio and IntelliJ)
72 | com_crashlytics_export_strings.xml
73 | crashlytics.properties
74 | crashlytics-build.properties
75 | fabric.properties
76 |
77 | # Editor-based Rest Client
78 | .idea/httpRequests
79 |
80 | # Android studio 3.1+ serialized cache file
81 | .idea/caches/build_file_checksums.ser
82 |
83 | ### GoLand+all Patch ###
84 | # Ignore everything but code style settings and run configurations
85 | # that are supposed to be shared within teams.
86 |
87 | .idea/*
88 |
89 | !.idea/codeStyles
90 | !.idea/runConfigurations
91 |
92 | ### macOS ###
93 | # General
94 | .DS_Store
95 | .AppleDouble
96 | .LSOverride
97 |
98 | # Icon must end with two \r
99 | Icon
100 |
101 |
102 | # Thumbnails
103 | ._*
104 |
105 | # Files that might appear in the root of a volume
106 | .DocumentRevisions-V100
107 | .fseventsd
108 | .Spotlight-V100
109 | .TemporaryItems
110 | .Trashes
111 | .VolumeIcon.icns
112 | .com.apple.timemachine.donotpresent
113 |
114 | # Directories potentially created on remote AFP share
115 | .AppleDB
116 | .AppleDesktop
117 | Network Trash Folder
118 | Temporary Items
119 | .apdisk
120 |
121 | ### macOS Patch ###
122 | # iCloud generated files
123 | *.icloud
124 |
125 | ### Windows ###
126 | # Windows thumbnail cache files
127 | Thumbs.db
128 | Thumbs.db:encryptable
129 | ehthumbs.db
130 | ehthumbs_vista.db
131 |
132 | # Dump file
133 | *.stackdump
134 |
135 | # Folder config file
136 | [Dd]esktop.ini
137 |
138 | # Recycle Bin used on file shares
139 | $RECYCLE.BIN/
140 |
141 | # Windows Installer files
142 | *.cab
143 | *.msi
144 | *.msix
145 | *.msm
146 | *.msp
147 |
148 | # Windows shortcuts
149 | *.lnk
150 |
151 | # End of https://www.toptal.com/developers/gitignore/api/goland+all,windows,macos
152 | /scratch/
153 |
--------------------------------------------------------------------------------
/.version:
--------------------------------------------------------------------------------
1 | VERSION=v0.8.1
2 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to GoHT
2 |
3 | We are thrilled that you are interested in contributing to GoHT!
4 | GoHT is a Haml engine for Go, focused on compiling Haml into type-safe Go code.
5 | This document provides guidelines for contributing to various parts of the project, including the CLI, compiler, and runtime.
6 |
7 | ## Table of Contents
8 |
9 | - [Getting Started](#getting-started)
10 | - [Contributing to Different Sections](#contributing-to-different-sections)
11 | - [CLI](#cli)
12 | - [Compiler](#compiler)
13 | - [Runtime](#runtime)
14 | - [Documentation](#documentation)
15 | - [Bug Submissions](#bug-submissions)
16 | - [Pull Requests](#pull-requests)
17 | - [Adhering to the Haml Spec](#adhering-to-the-haml-spec)
18 | - [Coding Standards](#coding-standards)
19 |
20 | ## Getting Started
21 |
22 | Before you begin, please ensure you have a GitHub account and are familiar with the basics of making a pull request. If you are new to Git or GitHub, we recommend reviewing [GitHub's documentation](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests).
23 |
24 | ## Contributing to Different Sections
25 |
26 | ### CLI
27 | - Share your ideas for improving the CLI experience.
28 | - Contribute to the development or debugging of CLI features.
29 |
30 | ### Compiler
31 | - Help enhance the compiler’s efficiency and reliability.
32 | - Work on feature additions or bug fixes related to the compiler.
33 | - Improve code coverage of the compiler, covering corner and edge cases.
34 |
35 | ### Runtime
36 | - Participate in optimizing runtime performance.
37 | - Contribute to making the runtime more robust and fault-tolerant.
38 |
39 | ### Documentation
40 | - Help write and improve the documentation.
41 | - Contribute to the [examples](examples) directory.
42 |
43 | ## IDE Extensions
44 | - Contribute to the development of extensions and plugins for GoHT in various editors and IDEs that will use the built-in Language Server Protocol (LSP) support.
45 |
46 | ## Bug Submissions
47 |
48 | We welcome bug reports! If you've found a bug in GoHT, please submit it as an issue in our GitHub repository. Include as much detail as possible, such as:
49 |
50 | - A clear and concise description of the bug.
51 | - Steps to reproduce the bug.
52 | - Expected and actual behavior.
53 | - Screenshots or code snippets, if applicable.
54 |
55 | ## Pull Requests
56 |
57 | Contributions to fix bugs or add features are made through pull requests (PRs). Here's how you can submit a PR:
58 |
59 | 1. Fork the repository and create your branch from `master`.
60 | 2. Make your changes, ensuring they adhere to the project's coding standards.
61 | 3. Write tests for your changes and ensure that all tests pass.
62 | 4. Submit a pull request with a clear description of your changes.
63 |
64 | ## Adhering to the Haml Spec
65 |
66 | It is crucial for GoHT to stick as closely as possible to the Haml specification. However, due to differences in syntax and structure between Go and Ruby, some deviations are inevitable. When contributing, consider the following:
67 |
68 | - Strive for consistency with the Haml spec.
69 | - Document any necessary deviations due to language differences.
70 |
71 | ## Coding Standards
72 |
73 | - Write clean, readable, and well-documented code.
74 | - Follow Go's standard coding conventions.
75 | - Include tests for new features or bug fixes.
76 |
77 | Thank you for contributing to GoHT! Your efforts help make this project better for everyone.
78 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Michael Stack
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Taskfile.yml:
--------------------------------------------------------------------------------
1 | # https://taskfile.dev
2 | version: '3'
3 |
4 | silent: true
5 |
6 | dotenv:
7 | - ./.version
8 |
9 | tasks:
10 | version:
11 | desc: Set the version
12 | summary: |
13 | Set the version
14 | cmds:
15 | - git tag -a {{ .VERSION }} -m "Release {{ .VERSION }}"
16 | - git push origin
17 |
--------------------------------------------------------------------------------
/bundle/info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
before
\n\nafter
\nlast
\nbefore
\n\nafter
\nlast
\n"); __err != nil { 21 | return 22 | } 23 | if !__isBuf { 24 | _, __err = __w.Write(__buf.Bytes()) 25 | } 26 | return 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /compiler/testdata/comments.html: -------------------------------------------------------------------------------- 1 |before
2 | 3 |after
4 |last
5 |before
7 | 10 |after
11 |last
12 | -------------------------------------------------------------------------------- /compiler/testdata/conditionals.false.html: -------------------------------------------------------------------------------- 1 |before
2 |after
3 | -------------------------------------------------------------------------------- /compiler/testdata/conditionals.goht: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | @goht ConditionalsTest(v bool) { 4 | %p before 5 | - if v 6 | %p the condition was true 7 | %p after 8 | } 9 | -------------------------------------------------------------------------------- /compiler/testdata/conditionals.goht.go: -------------------------------------------------------------------------------- 1 | // Code generated by GoHT v0.8.1 - DO NOT EDIT. 2 | // https://github.com/stackus/goht 3 | 4 | package testdata 5 | 6 | import "context" 7 | import "io" 8 | import "github.com/stackus/goht" 9 | 10 | func ConditionalsTest(v bool) goht.Template { 11 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 12 | __buf, __isBuf := __w.(goht.Buffer) 13 | if !__isBuf { 14 | __buf = goht.GetBuffer() 15 | defer goht.ReleaseBuffer(__buf) 16 | } 17 | var __children goht.Template 18 | ctx, __children = goht.PopChildren(ctx) 19 | _ = __children 20 | if _, __err = __buf.WriteString("before
\n"); __err != nil { 21 | return 22 | } 23 | if v { 24 | if _, __err = __buf.WriteString("the condition was true
\n"); __err != nil { 25 | return 26 | } 27 | } 28 | if _, __err = __buf.WriteString("after
\n"); __err != nil { 29 | return 30 | } 31 | if !__isBuf { 32 | _, __err = __w.Write(__buf.Bytes()) 33 | } 34 | return 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /compiler/testdata/conditionals.true.html: -------------------------------------------------------------------------------- 1 |before
2 |the condition was true
3 |after
4 | -------------------------------------------------------------------------------- /compiler/testdata/ego_template.goht: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | @ego EgoTemplate() { 4 | 5 | 6 | 7 |Hello World
11 | <% for i := 0; i < 10; i++ { -%> 12 |Iteration: <%=%d i %>
13 | <%- } -%> 14 | <% if true { -%> 15 |Condition is true
16 | <%- } else if false { -%> 17 |Condition is false
18 | <%- } -%> 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /compiler/testdata/ego_template.goht.go: -------------------------------------------------------------------------------- 1 | // Code generated by GoHT v0.8.1 - DO NOT EDIT. 2 | // https://github.com/stackus/goht 3 | 4 | package testdata 5 | 6 | import "context" 7 | import "io" 8 | import "github.com/stackus/goht" 9 | 10 | func EgoTemplate() goht.Template { 11 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 12 | __buf, __isBuf := __w.(goht.Buffer) 13 | if !__isBuf { 14 | __buf = goht.GetBuffer() 15 | defer goht.ReleaseBuffer(__buf) 16 | } 17 | var __children goht.Template 18 | ctx, __children = goht.PopChildren(ctx) 19 | _ = __children 20 | if _, __err = __buf.WriteString("\n\n\t\n\t\tHello World
\n\t\t"); __err != nil { 21 | return 22 | } 23 | for i := 0; i < 10; i++ { 24 | if _, __err = __buf.WriteString("Iteration: "); __err != nil { 25 | return 26 | } 27 | var __var1 string 28 | if __var1, __err = goht.CaptureErrors(goht.EscapeString(goht.FormatString("%d", i))); __err != nil { 29 | return 30 | } 31 | if _, __err = __buf.WriteString(__var1); __err != nil { 32 | return 33 | } 34 | if _, __err = __buf.WriteString("
"); __err != nil { 35 | return 36 | } 37 | } 38 | if true { 39 | if _, __err = __buf.WriteString("Condition is true
"); __err != nil { 40 | return 41 | } 42 | } else if false { 43 | if _, __err = __buf.WriteString("Condition is false
"); __err != nil { 44 | return 45 | } 46 | } 47 | if _, __err = __buf.WriteString("\n\n"); __err != nil { 48 | return 49 | } 50 | if !__isBuf { 51 | _, __err = __w.Write(__buf.Bytes()) 52 | } 53 | return 54 | }) 55 | } 56 | -------------------------------------------------------------------------------- /compiler/testdata/ego_template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |Hello World
8 |Iteration: 0
Iteration: 1
Iteration: 2
Iteration: 3
Iteration: 4
Iteration: 5
Iteration: 6
Iteration: 7
Iteration: 8
Iteration: 9
Condition is true
9 | 10 | -------------------------------------------------------------------------------- /compiler/testdata/elements.goht: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | @goht ElementsTest() { 4 | !!! 5 | %html 6 | %body 7 | #main.wrapper 8 | %article 9 | %h1 Article Title 10 | %p Article content 11 | %footer 12 | %p Article footer 13 | %footer.wrapper 14 | %p Footer content 15 | } 16 | -------------------------------------------------------------------------------- /compiler/testdata/elements.goht.go: -------------------------------------------------------------------------------- 1 | // Code generated by GoHT v0.8.1 - DO NOT EDIT. 2 | // https://github.com/stackus/goht 3 | 4 | package testdata 5 | 6 | import "context" 7 | import "io" 8 | import "github.com/stackus/goht" 9 | 10 | func ElementsTest() goht.Template { 11 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 12 | __buf, __isBuf := __w.(goht.Buffer) 13 | if !__isBuf { 14 | __buf = goht.GetBuffer() 15 | defer goht.ReleaseBuffer(__buf) 16 | } 17 | var __children goht.Template 18 | ctx, __children = goht.PopChildren(ctx) 19 | _ = __children 20 | if _, __err = __buf.WriteString("\n\n\nArticle content
\n\nArticle content
9 | 12 |\nPlain text\n"); __err != nil { 22 | return 23 | } 24 | var __var1 string 25 | if __var1, __err = goht.CaptureErrors(str); __err != nil { 26 | return 27 | } 28 | if _, __err = __buf.WriteString(__var1); __err != nil { 29 | return 30 | } 31 | if _, __err = __buf.WriteString("\n
\n\n\tIndented Plain text\n\t"); __err != nil { 32 | return 33 | } 34 | var __var2 string 35 | if __var2, __err = goht.CaptureErrors(str); __err != nil { 36 | return 37 | } 38 | if _, __err = __buf.WriteString(__var2); __err != nil { 39 | return 40 | } 41 | if _, __err = __buf.WriteString("\n
\n\nEscaped <em>text</em>\n"); __err != nil { 42 | return 43 | } 44 | var __var3 string 45 | if __var3, __err = goht.CaptureErrors(goht.EscapeString(str)); __err != nil { 46 | return 47 | } 48 | if _, __err = __buf.WriteString(__var3); __err != nil { 49 | return 50 | } 51 | if _, __err = __buf.WriteString("\n
\n\nPreserved text "); __err != nil { 52 | return 53 | } 54 | var __var4 string 55 | if __var4, __err = goht.CaptureErrors(str); __err != nil { 56 | return 57 | } 58 | if _, __err = __buf.WriteString(__var4); __err != nil { 59 | return 60 | } 61 | if _, __err = __buf.WriteString(" \n
\n2 | Plain text 3 | Interpolated text 4 |
5 |6 | Indented Plain text 7 | Interpolated text 8 |
9 |10 | Escaped <em>text</em> 11 | Interpolated <em>text</em> 12 |
13 |14 | Preserved text Interpolated text 15 |
16 |"); __err != nil { 25 | return 26 | } 27 | var __var1 string 28 | if __var1, __err = goht.CaptureErrors(goht.EscapeString(s)); __err != nil { 29 | return 30 | } 31 | if _, __err = __buf.WriteString(__var1); __err != nil { 32 | return 33 | } 34 | if _, __err = __buf.WriteString("
\n"); __err != nil { 35 | return 36 | } 37 | if !__isBuf { 38 | _, __err = __w.Write(__buf.Bytes()) 39 | } 40 | return 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /compiler/testdata/imports.html: -------------------------------------------------------------------------------- 1 |Hello, world!
2 | -------------------------------------------------------------------------------- /compiler/testdata/interpolation.goht: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | var foo = "bar" 4 | 5 | @goht InterpolationTest() { 6 | %p This is #{foo} 7 | } 8 | -------------------------------------------------------------------------------- /compiler/testdata/interpolation.goht.go: -------------------------------------------------------------------------------- 1 | // Code generated by GoHT v0.8.1 - DO NOT EDIT. 2 | // https://github.com/stackus/goht 3 | 4 | package testdata 5 | 6 | import "context" 7 | import "io" 8 | import "github.com/stackus/goht" 9 | 10 | var foo = "bar" 11 | 12 | func InterpolationTest() goht.Template { 13 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 14 | __buf, __isBuf := __w.(goht.Buffer) 15 | if !__isBuf { 16 | __buf = goht.GetBuffer() 17 | defer goht.ReleaseBuffer(__buf) 18 | } 19 | var __children goht.Template 20 | ctx, __children = goht.PopChildren(ctx) 21 | _ = __children 22 | if _, __err = __buf.WriteString("This is "); __err != nil { 23 | return 24 | } 25 | var __var1 string 26 | if __var1, __err = goht.CaptureErrors(goht.EscapeString(foo)); __err != nil { 27 | return 28 | } 29 | if _, __err = __buf.WriteString(__var1); __err != nil { 30 | return 31 | } 32 | if _, __err = __buf.WriteString("
\n"); __err != nil { 33 | return 34 | } 35 | if !__isBuf { 36 | _, __err = __w.Write(__buf.Bytes()) 37 | } 38 | return 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /compiler/testdata/interpolation.html: -------------------------------------------------------------------------------- 1 |This is bar
2 | -------------------------------------------------------------------------------- /compiler/testdata/nesting.html: -------------------------------------------------------------------------------- 1 |Unprefixed
\nPrefixed
\n"); __err != nil { 64 | return 65 | } 66 | if !__isBuf { 67 | _, __err = __w.Write(__buf.Bytes()) 68 | } 69 | return 70 | }) 71 | } 72 | -------------------------------------------------------------------------------- /compiler/testdata/obj_references.html: -------------------------------------------------------------------------------- 1 |Unprefixed
2 |Prefixed
3 | -------------------------------------------------------------------------------- /compiler/testdata/package.goht: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | @goht PackageTest() { 4 | This is a package test. 5 | } 6 | -------------------------------------------------------------------------------- /compiler/testdata/package.goht.go: -------------------------------------------------------------------------------- 1 | // Code generated by GoHT v0.8.1 - DO NOT EDIT. 2 | // https://github.com/stackus/goht 3 | 4 | package testdata 5 | 6 | import "context" 7 | import "io" 8 | import "github.com/stackus/goht" 9 | 10 | func PackageTest() goht.Template { 11 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 12 | __buf, __isBuf := __w.(goht.Buffer) 13 | if !__isBuf { 14 | __buf = goht.GetBuffer() 15 | defer goht.ReleaseBuffer(__buf) 16 | } 17 | var __children goht.Template 18 | ctx, __children = goht.PopChildren(ctx) 19 | _ = __children 20 | if _, __err = __buf.WriteString("This is a package test.\n"); __err != nil { 21 | return 22 | } 23 | if !__isBuf { 24 | _, __err = __w.Write(__buf.Bytes()) 25 | } 26 | return 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /compiler/testdata/package.html: -------------------------------------------------------------------------------- 1 | This is a package test. 2 | -------------------------------------------------------------------------------- /compiler/testdata/rendering.goht: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | @haml ChildrenTest(v string) { 4 | .passed-in= v 5 | .children 6 | =@children 7 | .after After children 8 | } 9 | 10 | @haml RenderTest() { 11 | .parent 12 | .local This is local content 13 | =@render ChildrenTest("This is passed-in content") 14 | .child This is child content 15 | .after After parent 16 | } 17 | 18 | @haml WrapperTest() { 19 | .wrapper 20 | .list 21 | =@children 22 | } 23 | 24 | @haml WrappedTest(v string) { 25 | .item= v 26 | } 27 | 28 | @haml NestedRenderTest() { 29 | =@render WrapperTest() 30 | =@render WrappedTest("first") 31 | =@render WrappedTest("second") 32 | } 33 | 34 | @haml SlotTest() { 35 | .wrapper 36 | =@slot first 37 | =@slot second 38 | =@slot third 39 | } 40 | 41 | @haml SlotWithDefaultTest() { 42 | .wrapper 43 | =@slot first 44 | %p Default first 45 | =@slot second 46 | %p Default second 47 | =@slot third 48 | %p Default third 49 | } 50 | -------------------------------------------------------------------------------- /compiler/testdata/rendering.html: -------------------------------------------------------------------------------- 1 |Hello World
"); __err != nil { 21 | return 22 | } 23 | for i := 0; i < 10; i++ { 24 | if _, __err = __buf.WriteString("Iteration: "); __err != nil { 25 | return 26 | } 27 | var __var1 string 28 | if __var1, __err = goht.CaptureErrors(goht.EscapeString(goht.FormatString("%d", i))); __err != nil { 29 | return 30 | } 31 | if _, __err = __buf.WriteString(__var1); __err != nil { 32 | return 33 | } 34 | if _, __err = __buf.WriteString("
"); __err != nil { 35 | return 36 | } 37 | } 38 | if true { 39 | if _, __err = __buf.WriteString("Condition is true
"); __err != nil { 40 | return 41 | } 42 | } else if false { 43 | if _, __err = __buf.WriteString("Condition is false
"); __err != nil { 44 | return 45 | } 46 | } 47 | if _, __err = __buf.WriteString("\n"); __err != nil { 48 | return 49 | } 50 | if !__isBuf { 51 | _, __err = __w.Write(__buf.Bytes()) 52 | } 53 | return 54 | }) 55 | } 56 | -------------------------------------------------------------------------------- /compiler/testdata/slim_template.html: -------------------------------------------------------------------------------- 1 |Hello World
Iteration: 0
Iteration: 1
Iteration: 2
Iteration: 3
Iteration: 4
Iteration: 5
Iteration: 6
Iteration: 7
Iteration: 8
Iteration: 9
Condition is true
2 | -------------------------------------------------------------------------------- /compiler/testdata/slots.html: -------------------------------------------------------------------------------- 1 |Default first
3 |Default second
4 |Default third
5 |\nContent\n>☢~\nOuter\n~☢<Content\n
\n\nContent\n~☢<\nInner\n>☢~\nContent\n
\n\nContent\n>☢~~☢<\nBoth\n>☢~~☢<Content\n
\n"); __err != nil { 21 | return 22 | } 23 | if !__isBuf { 24 | _, __err = __w.Write(__buf.Bytes()) 25 | } 26 | return 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /compiler/testdata/whitespace.html: -------------------------------------------------------------------------------- 1 |2 | Content 3 | Outer 4 | Content 5 |
6 |7 | Content 8 | Inner 9 | Content 10 |
11 |12 | ContentBothContent 13 |
14 | -------------------------------------------------------------------------------- /compiler/testdata/without_children.html: -------------------------------------------------------------------------------- 1 |\nThe following was passed in from the calling template:\n"); __err != nil { 31 | return 32 | } 33 | if __err = __children.Render(ctx, __buf); __err != nil { 34 | return 35 | } 36 | if _, __err = __buf.WriteString("
\n"); __err != nil { 37 | return 38 | } 39 | if !__isBuf { 40 | _, __err = __w.Write(__buf.Bytes()) 41 | } 42 | return 43 | }) 44 | } 45 | 46 | func HamlChildrenExample() goht.Template { 47 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 48 | __buf, __isBuf := __w.(goht.Buffer) 49 | if !__isBuf { 50 | __buf = goht.GetBuffer() 51 | defer goht.ReleaseBuffer(__buf) 52 | } 53 | var __children goht.Template 54 | ctx, __children = goht.PopChildren(ctx) 55 | _ = __children 56 | if _, __err = __buf.WriteString("\nThe following was passed in from the calling template:\n"); __err != nil { 57 | return 58 | } 59 | if __err = __children.Render(ctx, __buf); __err != nil { 60 | return 61 | } 62 | if _, __err = __buf.WriteString("
\n"); __err != nil { 63 | return 64 | } 65 | if !__isBuf { 66 | _, __err = __w.Write(__buf.Bytes()) 67 | } 68 | return 69 | }) 70 | } 71 | 72 | func SlimChildrenExample() goht.Template { 73 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 74 | __buf, __isBuf := __w.(goht.Buffer) 75 | if !__isBuf { 76 | __buf = goht.GetBuffer() 77 | defer goht.ReleaseBuffer(__buf) 78 | } 79 | var __children goht.Template 80 | ctx, __children = goht.PopChildren(ctx) 81 | _ = __children 82 | if _, __err = __buf.WriteString("This is a paragraph
\n\n"); __err != nil { 26 | return 27 | } 28 | if !__isBuf { 29 | _, __err = __w.Write(__buf.Bytes()) 30 | } 31 | return 32 | }) 33 | } 34 | 35 | func HamlHtmlComments() goht.Template { 36 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 37 | __buf, __isBuf := __w.(goht.Buffer) 38 | if !__isBuf { 39 | __buf = goht.GetBuffer() 40 | defer goht.ReleaseBuffer(__buf) 41 | } 42 | var __children goht.Template 43 | ctx, __children = goht.PopChildren(ctx) 44 | _ = __children 45 | if _, __err = __buf.WriteString("This is a paragraph
\n\n"); __err != nil { 46 | return 47 | } 48 | if !__isBuf { 49 | _, __err = __w.Write(__buf.Bytes()) 50 | } 51 | return 52 | }) 53 | } 54 | 55 | // HTML comments in the Slim syntax use "/!" to indicate the start of 56 | // the comment. 57 | 58 | func SlimHtmlComments() goht.Template { 59 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 60 | __buf, __isBuf := __w.(goht.Buffer) 61 | if !__isBuf { 62 | __buf = goht.GetBuffer() 63 | defer goht.ReleaseBuffer(__buf) 64 | } 65 | var __children goht.Template 66 | ctx, __children = goht.PopChildren(ctx) 67 | _ = __children 68 | if _, __err = __buf.WriteString("This is a paragraph
\n"); __err != nil { 69 | return 70 | } 71 | if !__isBuf { 72 | _, __err = __w.Write(__buf.Bytes()) 73 | } 74 | return 75 | }) 76 | } 77 | 78 | // You may also use them to comment out nested elements. This does 79 | // not stop the nested elements from being parsed, just from being 80 | // displayed. 81 | 82 | func HtmlCommentsNested() goht.Template { 83 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 84 | __buf, __isBuf := __w.(goht.Buffer) 85 | if !__isBuf { 86 | __buf = goht.GetBuffer() 87 | defer goht.ReleaseBuffer(__buf) 88 | } 89 | var __children goht.Template 90 | ctx, __children = goht.PopChildren(ctx) 91 | _ = __children 92 | if _, __err = __buf.WriteString("This is a paragraph
\n\n"); __err != nil { 93 | return 94 | } 95 | if !__isBuf { 96 | _, __err = __w.Write(__buf.Bytes()) 97 | } 98 | return 99 | }) 100 | } 101 | 102 | func HamlHtmlCommentsNested() goht.Template { 103 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 104 | __buf, __isBuf := __w.(goht.Buffer) 105 | if !__isBuf { 106 | __buf = goht.GetBuffer() 107 | defer goht.ReleaseBuffer(__buf) 108 | } 109 | var __children goht.Template 110 | ctx, __children = goht.PopChildren(ctx) 111 | _ = __children 112 | if _, __err = __buf.WriteString("This is a paragraph
\n\n"); __err != nil { 113 | return 114 | } 115 | if !__isBuf { 116 | _, __err = __w.Write(__buf.Bytes()) 117 | } 118 | return 119 | }) 120 | } 121 | 122 | func SlimHtmlCommentsNested() goht.Template { 123 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 124 | __buf, __isBuf := __w.(goht.Buffer) 125 | if !__isBuf { 126 | __buf = goht.GetBuffer() 127 | defer goht.ReleaseBuffer(__buf) 128 | } 129 | var __children goht.Template 130 | ctx, __children = goht.PopChildren(ctx) 131 | _ = __children 132 | if _, __err = __buf.WriteString("This is a paragraph
\n"); __err != nil { 133 | return 134 | } 135 | if !__isBuf { 136 | _, __err = __w.Write(__buf.Bytes()) 137 | } 138 | return 139 | }) 140 | } 141 | -------------------------------------------------------------------------------- /examples/comments/rubystyle.goht: -------------------------------------------------------------------------------- 1 | package comments 2 | 3 | // You may use ruby style comments to completely remove a line or 4 | // even a block of nested elements. 5 | // This is accomplished by using the `-#` syntax. 6 | // This is useful for removing elements that are only used for 7 | // documentation purposes. 8 | // The nested elements commented out with this syntax will be 9 | // not the parsed by the compiler and will not be included in the 10 | // output. 11 | 12 | @goht RubyStyle() { 13 | %p This is the only paragraph in the output. 14 | -# %p This comment is removed from the output. 15 | } 16 | 17 | @haml HamlRubyStyle() { 18 | %p This is the only paragraph in the output. 19 | -# %p This comment is removed from the output. 20 | } 21 | 22 | // In the Slim syntax "RubyStyle" comments use a "/" to indicate the 23 | // start of the comment 24 | 25 | @slim SlimRubyStyle() { 26 | p This is the only paragraph in the output. 27 | / p This comment is removed from the output. 28 | } 29 | 30 | // Ruby style comments can comment nested content. 31 | 32 | @goht RubyStyleNested() { 33 | %p This is the only paragraph in the output. 34 | -# 35 | %p This paragraph is removed. 36 | %%% broken syntax is no problem. 37 | } 38 | 39 | @haml HamlRubyStyleNested() { 40 | %p This is the only paragraph in the output. 41 | -# 42 | %p This paragraph is removed. 43 | %%% broken syntax is no problem. 44 | } 45 | 46 | @slim SlimRubyStyleNested() { 47 | p This is the only paragraph in the output. 48 | / 49 | p This paragraph is removed. 50 | %%% broken syntax is no problem. 51 | } 52 | -------------------------------------------------------------------------------- /examples/comments/rubystyle.goht.go: -------------------------------------------------------------------------------- 1 | // Code generated by GoHT v0.8.1 - DO NOT EDIT. 2 | // https://github.com/stackus/goht 3 | 4 | package comments 5 | 6 | import "context" 7 | import "io" 8 | import "github.com/stackus/goht" 9 | 10 | // You may use ruby style comments to completely remove a line or 11 | // even a block of nested elements. 12 | // This is accomplished by using the `-#` syntax. 13 | // This is useful for removing elements that are only used for 14 | // documentation purposes. 15 | // The nested elements commented out with this syntax will be 16 | // not the parsed by the compiler and will not be included in the 17 | // output. 18 | 19 | func RubyStyle() goht.Template { 20 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 21 | __buf, __isBuf := __w.(goht.Buffer) 22 | if !__isBuf { 23 | __buf = goht.GetBuffer() 24 | defer goht.ReleaseBuffer(__buf) 25 | } 26 | var __children goht.Template 27 | ctx, __children = goht.PopChildren(ctx) 28 | _ = __children 29 | if _, __err = __buf.WriteString("This is the only paragraph in the output.
\n"); __err != nil { 30 | return 31 | } 32 | if !__isBuf { 33 | _, __err = __w.Write(__buf.Bytes()) 34 | } 35 | return 36 | }) 37 | } 38 | 39 | func HamlRubyStyle() goht.Template { 40 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 41 | __buf, __isBuf := __w.(goht.Buffer) 42 | if !__isBuf { 43 | __buf = goht.GetBuffer() 44 | defer goht.ReleaseBuffer(__buf) 45 | } 46 | var __children goht.Template 47 | ctx, __children = goht.PopChildren(ctx) 48 | _ = __children 49 | if _, __err = __buf.WriteString("This is the only paragraph in the output.
\n"); __err != nil { 50 | return 51 | } 52 | if !__isBuf { 53 | _, __err = __w.Write(__buf.Bytes()) 54 | } 55 | return 56 | }) 57 | } 58 | 59 | // In the Slim syntax "RubyStyle" comments use a "/" to indicate the 60 | // start of the comment 61 | 62 | func SlimRubyStyle() goht.Template { 63 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 64 | __buf, __isBuf := __w.(goht.Buffer) 65 | if !__isBuf { 66 | __buf = goht.GetBuffer() 67 | defer goht.ReleaseBuffer(__buf) 68 | } 69 | var __children goht.Template 70 | ctx, __children = goht.PopChildren(ctx) 71 | _ = __children 72 | if _, __err = __buf.WriteString("This is the only paragraph in the output.
\n"); __err != nil { 73 | return 74 | } 75 | if !__isBuf { 76 | _, __err = __w.Write(__buf.Bytes()) 77 | } 78 | return 79 | }) 80 | } 81 | 82 | // Ruby style comments can comment nested content. 83 | 84 | func RubyStyleNested() goht.Template { 85 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 86 | __buf, __isBuf := __w.(goht.Buffer) 87 | if !__isBuf { 88 | __buf = goht.GetBuffer() 89 | defer goht.ReleaseBuffer(__buf) 90 | } 91 | var __children goht.Template 92 | ctx, __children = goht.PopChildren(ctx) 93 | _ = __children 94 | if _, __err = __buf.WriteString("This is the only paragraph in the output.
\n"); __err != nil { 95 | return 96 | } 97 | if !__isBuf { 98 | _, __err = __w.Write(__buf.Bytes()) 99 | } 100 | return 101 | }) 102 | } 103 | 104 | func HamlRubyStyleNested() goht.Template { 105 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 106 | __buf, __isBuf := __w.(goht.Buffer) 107 | if !__isBuf { 108 | __buf = goht.GetBuffer() 109 | defer goht.ReleaseBuffer(__buf) 110 | } 111 | var __children goht.Template 112 | ctx, __children = goht.PopChildren(ctx) 113 | _ = __children 114 | if _, __err = __buf.WriteString("This is the only paragraph in the output.
\n"); __err != nil { 115 | return 116 | } 117 | if !__isBuf { 118 | _, __err = __w.Write(__buf.Bytes()) 119 | } 120 | return 121 | }) 122 | } 123 | 124 | func SlimRubyStyleNested() goht.Template { 125 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 126 | __buf, __isBuf := __w.(goht.Buffer) 127 | if !__isBuf { 128 | __buf = goht.GetBuffer() 129 | defer goht.ReleaseBuffer(__buf) 130 | } 131 | var __children goht.Template 132 | ctx, __children = goht.PopChildren(ctx) 133 | _ = __children 134 | if _, __err = __buf.WriteString("This is the only paragraph in the output.
\n"); __err != nil { 135 | return 136 | } 137 | if !__isBuf { 138 | _, __err = __w.Write(__buf.Bytes()) 139 | } 140 | return 141 | }) 142 | } 143 | -------------------------------------------------------------------------------- /examples/doctype/doctype.goht: -------------------------------------------------------------------------------- 1 | package doctype 2 | 3 | // Adds a doctype to the top of the page 4 | // HTML5 doctype is the default 5 | @goht Doctype() { 6 | !!! 7 | } 8 | 9 | @haml HamlDoctype() { 10 | !!! 11 | } 12 | 13 | @slim SlimDoctype() { 14 | doctype 15 | } 16 | -------------------------------------------------------------------------------- /examples/doctype/doctype.goht.go: -------------------------------------------------------------------------------- 1 | // Code generated by GoHT v0.8.1 - DO NOT EDIT. 2 | // https://github.com/stackus/goht 3 | 4 | package doctype 5 | 6 | import "context" 7 | import "io" 8 | import "github.com/stackus/goht" 9 | 10 | // Adds a doctype to the top of the page 11 | // HTML5 doctype is the default 12 | func Doctype() goht.Template { 13 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 14 | __buf, __isBuf := __w.(goht.Buffer) 15 | if !__isBuf { 16 | __buf = goht.GetBuffer() 17 | defer goht.ReleaseBuffer(__buf) 18 | } 19 | var __children goht.Template 20 | ctx, __children = goht.PopChildren(ctx) 21 | _ = __children 22 | if _, __err = __buf.WriteString("\n"); __err != nil { 23 | return 24 | } 25 | if !__isBuf { 26 | _, __err = __w.Write(__buf.Bytes()) 27 | } 28 | return 29 | }) 30 | } 31 | 32 | func HamlDoctype() goht.Template { 33 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 34 | __buf, __isBuf := __w.(goht.Buffer) 35 | if !__isBuf { 36 | __buf = goht.GetBuffer() 37 | defer goht.ReleaseBuffer(__buf) 38 | } 39 | var __children goht.Template 40 | ctx, __children = goht.PopChildren(ctx) 41 | _ = __children 42 | if _, __err = __buf.WriteString("\n"); __err != nil { 43 | return 44 | } 45 | if !__isBuf { 46 | _, __err = __w.Write(__buf.Bytes()) 47 | } 48 | return 49 | }) 50 | } 51 | 52 | func SlimDoctype() goht.Template { 53 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 54 | __buf, __isBuf := __w.(goht.Buffer) 55 | if !__isBuf { 56 | __buf = goht.GetBuffer() 57 | defer goht.ReleaseBuffer(__buf) 58 | } 59 | var __children goht.Template 60 | ctx, __children = goht.PopChildren(ctx) 61 | _ = __children 62 | if _, __err = __buf.WriteString("\n"); __err != nil { 63 | return 64 | } 65 | if !__isBuf { 66 | _, __err = __w.Write(__buf.Bytes()) 67 | } 68 | return 69 | }) 70 | } 71 | -------------------------------------------------------------------------------- /examples/filters/css.goht: -------------------------------------------------------------------------------- 1 | package filters 2 | 3 | // You can include CSS into your templates using the `css` filter. Inside 4 | // this filter you may include interpolated values. 5 | 6 | var color = "red" 7 | 8 | @goht Css() { 9 | :css 10 | .color { 11 | color: #{color}; 12 | } 13 | } 14 | 15 | @haml HamlCss() { 16 | :css 17 | .color { 18 | color: #{color}; 19 | } 20 | } 21 | 22 | @slim SlimCss() { 23 | :css 24 | .color { 25 | color: #{color}; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/filters/css.goht.go: -------------------------------------------------------------------------------- 1 | // Code generated by GoHT v0.8.1 - DO NOT EDIT. 2 | // https://github.com/stackus/goht 3 | 4 | package filters 5 | 6 | import "context" 7 | import "io" 8 | import "github.com/stackus/goht" 9 | 10 | // You can include CSS into your templates using the `css` filter. Inside 11 | // this filter you may include interpolated values. 12 | 13 | var color = "red" 14 | 15 | func Css() goht.Template { 16 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 17 | __buf, __isBuf := __w.(goht.Buffer) 18 | if !__isBuf { 19 | __buf = goht.GetBuffer() 20 | defer goht.ReleaseBuffer(__buf) 21 | } 22 | var __children goht.Template 23 | ctx, __children = goht.PopChildren(ctx) 24 | _ = __children 25 | if _, __err = __buf.WriteString(""); __err != nil { 36 | return 37 | } 38 | if !__isBuf { 39 | _, __err = __w.Write(__buf.Bytes()) 40 | } 41 | return 42 | }) 43 | } 44 | 45 | func HamlCss() goht.Template { 46 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 47 | __buf, __isBuf := __w.(goht.Buffer) 48 | if !__isBuf { 49 | __buf = goht.GetBuffer() 50 | defer goht.ReleaseBuffer(__buf) 51 | } 52 | var __children goht.Template 53 | ctx, __children = goht.PopChildren(ctx) 54 | _ = __children 55 | if _, __err = __buf.WriteString(""); __err != nil { 66 | return 67 | } 68 | if !__isBuf { 69 | _, __err = __w.Write(__buf.Bytes()) 70 | } 71 | return 72 | }) 73 | } 74 | 75 | func SlimCss() goht.Template { 76 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 77 | __buf, __isBuf := __w.(goht.Buffer) 78 | if !__isBuf { 79 | __buf = goht.GetBuffer() 80 | defer goht.ReleaseBuffer(__buf) 81 | } 82 | var __children goht.Template 83 | ctx, __children = goht.PopChildren(ctx) 84 | _ = __children 85 | if _, __err = __buf.WriteString("\n"); __err != nil { 96 | return 97 | } 98 | if !__isBuf { 99 | _, __err = __w.Write(__buf.Bytes()) 100 | } 101 | return 102 | }) 103 | } 104 | -------------------------------------------------------------------------------- /examples/filters/javascript.goht: -------------------------------------------------------------------------------- 1 | package filters 2 | 3 | // You can include JavaScript into your templates using the JavaScript 4 | // filter `:javascript`. You may include interpolation values within 5 | // the JavaScript code to have them replaced with values at render time. 6 | 7 | var name = "Bob" 8 | 9 | @goht JavaScript() { 10 | :javascript 11 | console.log("Hello #{name}!"); 12 | } 13 | 14 | @haml HamlJavaScript() { 15 | :javascript 16 | console.log("Hello #{name}!"); 17 | } 18 | 19 | @slim SlimJavaScript() { 20 | :javascript 21 | console.log("Hello #{name}!"); 22 | } 23 | -------------------------------------------------------------------------------- /examples/filters/javascript.goht.go: -------------------------------------------------------------------------------- 1 | // Code generated by GoHT v0.8.1 - DO NOT EDIT. 2 | // https://github.com/stackus/goht 3 | 4 | package filters 5 | 6 | import "context" 7 | import "io" 8 | import "github.com/stackus/goht" 9 | 10 | // You can include JavaScript into your templates using the JavaScript 11 | // filter `:javascript`. You may include interpolation values within 12 | // the JavaScript code to have them replaced with values at render time. 13 | 14 | var name = "Bob" 15 | 16 | func JavaScript() goht.Template { 17 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 18 | __buf, __isBuf := __w.(goht.Buffer) 19 | if !__isBuf { 20 | __buf = goht.GetBuffer() 21 | defer goht.ReleaseBuffer(__buf) 22 | } 23 | var __children goht.Template 24 | ctx, __children = goht.PopChildren(ctx) 25 | _ = __children 26 | if _, __err = __buf.WriteString(""); __err != nil { 37 | return 38 | } 39 | if !__isBuf { 40 | _, __err = __w.Write(__buf.Bytes()) 41 | } 42 | return 43 | }) 44 | } 45 | 46 | func HamlJavaScript() goht.Template { 47 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 48 | __buf, __isBuf := __w.(goht.Buffer) 49 | if !__isBuf { 50 | __buf = goht.GetBuffer() 51 | defer goht.ReleaseBuffer(__buf) 52 | } 53 | var __children goht.Template 54 | ctx, __children = goht.PopChildren(ctx) 55 | _ = __children 56 | if _, __err = __buf.WriteString(""); __err != nil { 67 | return 68 | } 69 | if !__isBuf { 70 | _, __err = __w.Write(__buf.Bytes()) 71 | } 72 | return 73 | }) 74 | } 75 | 76 | func SlimJavaScript() goht.Template { 77 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 78 | __buf, __isBuf := __w.(goht.Buffer) 79 | if !__isBuf { 80 | __buf = goht.GetBuffer() 81 | defer goht.ReleaseBuffer(__buf) 82 | } 83 | var __children goht.Template 84 | ctx, __children = goht.PopChildren(ctx) 85 | _ = __children 86 | if _, __err = __buf.WriteString("\n"); __err != nil { 97 | return 98 | } 99 | if !__isBuf { 100 | _, __err = __w.Write(__buf.Bytes()) 101 | } 102 | return 103 | }) 104 | } 105 | -------------------------------------------------------------------------------- /examples/filters/text.goht: -------------------------------------------------------------------------------- 1 | package filters 2 | 3 | // The `:plain` filter can be used to display a large amount of text 4 | // without any parsing. Lines may begin with Haml syntax and it will 5 | // be ignored. 6 | // Variable interpolation is still performed. 7 | 8 | @goht Plain() { 9 | %p 10 | :plain 11 | This is plain text. Itwillbe displayed as HTML. 12 | #{"This
\"will\"be interpolated with HTML intact."} 13 | } 14 | 15 | @haml HamlPlain() { 16 | %p 17 | :plain 18 | This is plain text. It
willbe displayed as HTML. 19 | #{"This
\"will\"be interpolated with HTML intact."} 20 | } 21 | 22 | @goht Escaped() { 23 | %p 24 | :escaped 25 | This is escaped text. It
will notbe displayed as HTML. 26 | #{"This
\"will not\"be interpolated with HTML intact."} 27 | } 28 | 29 | @haml HamlEscaped() { 30 | %p 31 | :escaped 32 | This is escaped text. It
will notbe displayed as HTML. 33 | #{"This
\"will not\"be interpolated with HTML intact."} 34 | } 35 | 36 | @goht Preserve() { 37 | %p 38 | :preserve 39 | This is preserved text. It
willbe displayed as HTML. 40 | #{"This
\"will\"be interpolated with HTML intact."} 41 | } 42 | 43 | @haml HamlPreserve() { 44 | %p 45 | :preserve 46 | This is preserved text. It
willbe displayed as HTML. 47 | #{"This
\"will\"be interpolated with HTML intact."} 48 | } 49 | -------------------------------------------------------------------------------- /examples/go/code.goht: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | // You may include any Go code outside the Goht templates. It will 4 | // be included in the generated file as-is. 5 | // The included code, or any Go code can then be called from within the 6 | // Goht templates using the various code calling syntax's. 7 | 8 | func sayHello() string { 9 | return "Hello, world!" 10 | } 11 | 12 | @goht ExecuteCode() { 13 | - foo := sayHello() 14 | %p= foo 15 | } 16 | 17 | @haml HamlExecuteCode() { 18 | - foo := sayHello() 19 | %p= foo 20 | } 21 | 22 | @slim SlimExecuteCode() { 23 | - foo := sayHello() 24 | p= foo 25 | } 26 | 27 | @goht RenderCode() { 28 | %p= sayHello() 29 | } 30 | 31 | @haml HamlRenderCode() { 32 | %p= sayHello() 33 | } 34 | 35 | @slim SlimRenderCode() { 36 | p= sayHello() 37 | } 38 | -------------------------------------------------------------------------------- /examples/go/doc.goht: -------------------------------------------------------------------------------- 1 | // Package example is an examples package for the Goht language. 2 | package example 3 | 4 | @goht Doc() { 5 | .doc An example of package documentation. 6 | } 7 | 8 | @haml HamlDoc() { 9 | .doc An example of package documentation. 10 | } 11 | 12 | @slim SlimDoc() { 13 | .doc An example of package documentation. 14 | } 15 | -------------------------------------------------------------------------------- /examples/go/doc.goht.go: -------------------------------------------------------------------------------- 1 | // Code generated by GoHT v0.8.1 - DO NOT EDIT. 2 | // https://github.com/stackus/goht 3 | 4 | package example 5 | 6 | import "context" 7 | import "io" 8 | import "github.com/stackus/goht" 9 | 10 | // Package example is an examples package for the Goht language. 11 | 12 | func Doc() goht.Template { 13 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 14 | __buf, __isBuf := __w.(goht.Buffer) 15 | if !__isBuf { 16 | __buf = goht.GetBuffer() 17 | defer goht.ReleaseBuffer(__buf) 18 | } 19 | var __children goht.Template 20 | ctx, __children = goht.PopChildren(ctx) 21 | _ = __children 22 | if _, __err = __buf.WriteString("
"); __err != nil { 31 | return 32 | } 33 | var __var1 string 34 | if __var1, __err = goht.CaptureErrors(goht.EscapeString(fmt.Sprintf("Hello, %s!", strings.TrimSuffix("World!", "!")))); __err != nil { 35 | return 36 | } 37 | if _, __err = __buf.WriteString(__var1); __err != nil { 38 | return 39 | } 40 | if _, __err = __buf.WriteString("
\n"); __err != nil { 41 | return 42 | } 43 | if !__isBuf { 44 | _, __err = __w.Write(__buf.Bytes()) 45 | } 46 | return 47 | }) 48 | } 49 | 50 | func HamlImportExample() goht.Template { 51 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 52 | __buf, __isBuf := __w.(goht.Buffer) 53 | if !__isBuf { 54 | __buf = goht.GetBuffer() 55 | defer goht.ReleaseBuffer(__buf) 56 | } 57 | var __children goht.Template 58 | ctx, __children = goht.PopChildren(ctx) 59 | _ = __children 60 | if _, __err = __buf.WriteString(""); __err != nil { 61 | return 62 | } 63 | var __var1 string 64 | if __var1, __err = goht.CaptureErrors(goht.EscapeString(fmt.Sprintf("Hello, %s!", strings.TrimSuffix("World!", "!")))); __err != nil { 65 | return 66 | } 67 | if _, __err = __buf.WriteString(__var1); __err != nil { 68 | return 69 | } 70 | if _, __err = __buf.WriteString("
\n"); __err != nil { 71 | return 72 | } 73 | if !__isBuf { 74 | _, __err = __w.Write(__buf.Bytes()) 75 | } 76 | return 77 | }) 78 | } 79 | 80 | func SlimImportExample() goht.Template { 81 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 82 | __buf, __isBuf := __w.(goht.Buffer) 83 | if !__isBuf { 84 | __buf = goht.GetBuffer() 85 | defer goht.ReleaseBuffer(__buf) 86 | } 87 | var __children goht.Template 88 | ctx, __children = goht.PopChildren(ctx) 89 | _ = __children 90 | if _, __err = __buf.WriteString(""); __err != nil { 91 | return 92 | } 93 | var __var1 string 94 | if __var1, __err = goht.CaptureErrors(goht.EscapeString(fmt.Sprintf("Hello, %s!", strings.TrimSuffix("World!", "!")))); __err != nil { 95 | return 96 | } 97 | if _, __err = __buf.WriteString(__var1); __err != nil { 98 | return 99 | } 100 | if _, __err = __buf.WriteString("
\n"); __err != nil { 101 | return 102 | } 103 | if !__isBuf { 104 | _, __err = __w.Write(__buf.Bytes()) 105 | } 106 | return 107 | }) 108 | } 109 | -------------------------------------------------------------------------------- /examples/go/inlining.goht: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // You can include Go code to handle conditional and loop statements 8 | // in your Goht templates. 9 | 10 | var isAdmin = true 11 | 12 | @goht Conditional() { 13 | .actions 14 | - if isAdmin { 15 | %button< 16 | Edit content 17 | - } else { 18 | %button Login 19 | - } 20 | %button View content 21 | } 22 | 23 | @haml HamlConditional() { 24 | .actions 25 | - if isAdmin { 26 | %button< 27 | Edit content 28 | - } else { 29 | %button Login 30 | - } 31 | %button View content 32 | } 33 | 34 | @slim SlimConditional() { 35 | .actions 36 | - if isAdmin { 37 | button 38 | Edit content 39 | - } else { 40 | button Login 41 | - } 42 | button View content 43 | } 44 | 45 | // However, we are using Haml and so we're into shortcuts. We can 46 | // continue to write out the brackets or we can use the shorthand 47 | // syntax. 48 | // The short hand form simply drops the opening and closing brackets 49 | // for statements that wrap around children. 50 | // Shorthand statements include: 51 | // for, if, else, else if, switch 52 | 53 | @goht ShorthandConditional() { 54 | .actions 55 | - if isAdmin 56 | %button< 57 | Edit content 58 | - else 59 | %button Login 60 | %button View content 61 | } 62 | 63 | @haml HamlShorthandConditional() { 64 | .actions 65 | - if isAdmin 66 | %button< 67 | Edit content 68 | - else 69 | %button Login 70 | %button View content 71 | } 72 | 73 | @slim SlimShorthandConditional() { 74 | .actions 75 | - if isAdmin 76 | button 77 | |Edit content 78 | - else 79 | button Login 80 | button View content 81 | } 82 | 83 | // With a switch statement, we can use the case and default keywords 84 | // but we will need to nest these statements if we're using the 85 | // shorthand syntax. (win some, lose some) 86 | 87 | @goht ShorthandSwitch() { 88 | .actions 89 | - switch isAdmin 90 | - case true: 91 | %button< 92 | Edit content 93 | - case false: 94 | %button Login 95 | %button View content 96 | } 97 | 98 | @haml HamlShorthandSwitch() { 99 | .actions 100 | - switch isAdmin 101 | - case true: 102 | %button< 103 | Edit content 104 | - case false: 105 | %button Login 106 | %button View content 107 | } 108 | 109 | @slim SlimShorthandSwitch() { 110 | .actions 111 | - switch isAdmin 112 | - case true: 113 | button 114 | |Edit content 115 | - case false: 116 | button Login 117 | button View content 118 | } 119 | 120 | // Haml supported splitting long code lines across multiple lines 121 | // using a comma. Each line that continues the statement must be indented 122 | // one additional level. To keep continuing the statement, you can 123 | // end the line with a comma. 124 | 125 | @haml HamlLongStatement() { 126 | .actions 127 | - action := longType{\ 128 | title: "Edit content", 129 | actions: "Edit content", 130 | } 131 | = fmt.Sprintf("Title: %s", 132 | action.title, 133 | ) 134 | - if action.title == "Edit content"\ 135 | && action.actions == "Edit content" 136 | %p= action.actions 137 | } 138 | 139 | // Slim supports splitting the control code across multiple lines which 140 | // is useful for long statements. The additional lines must be indented 141 | // one additional level. 142 | // 143 | // Ending a statement with a backslash or a comma will let you break 144 | // long statements across multiple lines. 145 | 146 | type longType struct { 147 | title string 148 | actions string 149 | } 150 | 151 | @slim SlimLongStatement() { 152 | .actions 153 | - action := longType{\ 154 | title: "Edit content", 155 | actions: "Edit content", 156 | } 157 | p=action.actions 158 | = fmt.Sprintf("Title: %s", 159 | action.title, 160 | ) 161 | } 162 | -------------------------------------------------------------------------------- /examples/go/interpolation.goht: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | // Variables and calls to functions that return values can be interpolated 4 | // into the Goht templates. 5 | 6 | var someVar = "Hello" 7 | 8 | @goht InterpolateCode() { 9 | %p #{someVar}, World! 10 | } 11 | 12 | @haml HamlInterpolateCode() { 13 | %p #{someVar}, World! 14 | } 15 | 16 | @slim SlimInterpolateCode() { 17 | p #{someVar}, World! 18 | } 19 | 20 | // Interpolation is not done within Go code or within a string literal. 21 | @goht NoInterpolation() { 22 | %p Do the following; No interpolation is necessary. 23 | %p= someVar + ", World!" 24 | %p= "No interpolation is #{performed} here." 25 | } 26 | 27 | @haml HamlNoInterpolation() { 28 | %p Do the following; No interpolation is necessary. 29 | %p= someVar + ", World!" 30 | %p= "No interpolation is #{performed} here." 31 | } 32 | 33 | @slim SlimNoInterpolation() { 34 | p Do the following; No interpolation is necessary. 35 | p= someVar + ", World!" 36 | p= "No interpolation is #{performed} here." 37 | } 38 | 39 | // Because the interpolation and tag id share the same starting character, 40 | // a `#` you will need to escape the interpolation with a backslash when it 41 | // is the first character of a line. 42 | // This is only necessary when it is the first character of a line and not 43 | // when it is the first character of text following a tag. 44 | 45 | @goht EscapeInterpolation() { 46 | \#{someVar}, World! 47 | %p #{someVar}, World! 48 | } 49 | 50 | @haml HamlEscapeInterpolation() { 51 | \#{someVar}, World! 52 | %p #{someVar}, World! 53 | } 54 | 55 | // There are also times when you want to ignore the interpolation and just 56 | // print the text. This is also handled with the backslash. 57 | // This can be done at the start of a line, after a tag or even mid-text. 58 | // 59 | // You will need to use two backslashes to escape the interpolation when 60 | // it is at the start of a line. This is because the first backslash is 61 | // triggering the parser to not interpret the next character as any 62 | // kind of special character. 63 | // This is also how you would escape a tag, id, or class character at the 64 | // start of a line. 65 | // 66 | // All three of the following uses of "someVar" are not interpolated, and 67 | // will render as "#{someVar}" in the final HTML. 68 | 69 | @goht IgnoreInterpolation() { 70 | \\#{someVar}, World! 71 | %p 72 | \\#{someVar}, World! 73 | A greeting: \#{someVar}, World! 74 | \. this line begins with a period 75 | \# this line begins with a hash 76 | \% this line begins with a percent 77 | } 78 | 79 | @haml HamlIgnoreInterpolation() { 80 | \\#{someVar}, World! 81 | %p 82 | \\#{someVar}, World! 83 | A greeting: \#{someVar}, World! 84 | \. this line begins with a period 85 | \# this line begins with a hash 86 | \% this line begins with a percent 87 | } 88 | -------------------------------------------------------------------------------- /examples/go/package.goht: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | // Just like in a Go file, you may include a package statement at the 4 | // top of your file. This is optional, but if you do include it, it 5 | // must be the first line of the file. If you do not specify a package, 6 | // the generated code will use "main" as the package name. 7 | 8 | @goht PackageExample() { 9 | } 10 | -------------------------------------------------------------------------------- /examples/go/package.goht.go: -------------------------------------------------------------------------------- 1 | // Code generated by GoHT v0.8.1 - DO NOT EDIT. 2 | // https://github.com/stackus/goht 3 | 4 | package example 5 | 6 | import "context" 7 | import "io" 8 | import "github.com/stackus/goht" 9 | 10 | // Just like in a Go file, you may include a package statement at the 11 | // top of your file. This is optional, but if you do include it, it 12 | // must be the first line of the file. If you do not specify a package, 13 | // the generated code will use "main" as the package name. 14 | 15 | func PackageExample() goht.Template { 16 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 17 | __buf, __isBuf := __w.(goht.Buffer) 18 | if !__isBuf { 19 | __buf = goht.GetBuffer() 20 | defer goht.ReleaseBuffer(__buf) 21 | } 22 | var __children goht.Template 23 | ctx, __children = goht.PopChildren(ctx) 24 | _ = __children 25 | if !__isBuf { 26 | _, __err = __w.Write(__buf.Bytes()) 27 | } 28 | return 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /examples/go/receivers.goht: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | type User struct { 4 | Name string 5 | Age int 6 | } 7 | 8 | // GoHT templates can be created as methods on any type that allows for method definitions 9 | // 10 | // The following will create a Details method on the User type. It will be called like so: 11 | // 12 | // user := User{Name: "John", Age: 30} 13 | // user.Details().Render(ctx, w)) 14 | // 15 | // It can also be called on using the `@render` command in a GoHT template: 16 | // 17 | // @render u.Details() 18 | 19 | @goht (u User) Details() { 20 | .name User name: #{u.Name} 21 | .age 22 | User Age: 23 | !=%d u.Age 24 | } 25 | 26 | @haml (u User) HamlDetails() { 27 | .name User name: #{u.Name} 28 | .age 29 | User Age: 30 | !=%d u.Age 31 | } 32 | 33 | @slim (u User) SlimDetails() { 34 | .name User name: #{u.Name} 35 | .age 36 | |User Age: 37 | =%d u.Age 38 | } 39 | -------------------------------------------------------------------------------- /examples/go/receivers.goht.go: -------------------------------------------------------------------------------- 1 | // Code generated by GoHT v0.8.1 - DO NOT EDIT. 2 | // https://github.com/stackus/goht 3 | 4 | package example 5 | 6 | import "context" 7 | import "io" 8 | import "github.com/stackus/goht" 9 | 10 | type User struct { 11 | Name string 12 | Age int 13 | } 14 | 15 | // GoHT templates can be created as methods on any type that allows for method definitions 16 | // 17 | // The following will create a Details method on the User type. It will be called like so: 18 | // 19 | // user := User{Name: "John", Age: 30} 20 | // user.Details().Render(ctx, w)) 21 | // 22 | // It can also be called on using the `@render` command in a GoHT template: 23 | // 24 | // @render u.Details() 25 | 26 | func (u User) Details() goht.Template { 27 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 28 | __buf, __isBuf := __w.(goht.Buffer) 29 | if !__isBuf { 30 | __buf = goht.GetBuffer() 31 | defer goht.ReleaseBuffer(__buf) 32 | } 33 | var __children goht.Template 34 | ctx, __children = goht.PopChildren(ctx) 35 | _ = __children 36 | if _, __err = __buf.WriteString("the following will loop a slice of strings and will pass each string into a child template
114 | <% for _, term := range terms { -%> 115 | <%@render termsWrapper(term) { -%> 116 |<%= term %>
117 | <%- } -%> 118 | <%- } -%> 119 | 120 | 121 | } 122 | -------------------------------------------------------------------------------- /examples/indents/tabs.goht: -------------------------------------------------------------------------------- 1 | package indents 2 | 3 | // You may use tabs to indent the Haml templates. 4 | // Goht does not change how indenting works in Haml. If you want 5 | // to use tabs, you must use tabs consistently. 6 | // Do not mix tabs and spaces in a single template. You would be 7 | // asking for trouble IMO but you may use tabs in one template and 8 | // spaces in another. 9 | 10 | @goht UsingTabs() { 11 | !!! 12 | %html{lang: "en"} 13 | %head 14 | %meta{charset: "utf-8"} 15 | %title 16 | Hello World 17 | %body 18 | %h1 19 | Hello World 20 | %p 21 | Hello World 22 | } 23 | 24 | @haml HamlUsingTabs() { 25 | !!! 26 | %html{lang: "en"} 27 | %head 28 | %meta{charset: "utf-8"} 29 | %title 30 | Hello World 31 | %body 32 | %h1 33 | Hello World 34 | %p 35 | Hello World 36 | } 37 | 38 | @slim SlimUsingTabs() { 39 | doctype 40 | html{lang: "en"} 41 | head 42 | meta{charset: "utf-8"} 43 | title 44 | |Hello World 45 | body 46 | h1 47 | |Hello World 48 | p 49 | |Hello World 50 | } 51 | -------------------------------------------------------------------------------- /examples/indents/tabs.goht.go: -------------------------------------------------------------------------------- 1 | // Code generated by GoHT v0.8.1 - DO NOT EDIT. 2 | // https://github.com/stackus/goht 3 | 4 | package indents 5 | 6 | import "context" 7 | import "io" 8 | import "github.com/stackus/goht" 9 | 10 | // You may use tabs to indent the Haml templates. 11 | // Goht does not change how indenting works in Haml. If you want 12 | // to use tabs, you must use tabs consistently. 13 | // Do not mix tabs and spaces in a single template. You would be 14 | // asking for trouble IMO but you may use tabs in one template and 15 | // spaces in another. 16 | 17 | func UsingTabs() goht.Template { 18 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 19 | __buf, __isBuf := __w.(goht.Buffer) 20 | if !__isBuf { 21 | __buf = goht.GetBuffer() 22 | defer goht.ReleaseBuffer(__buf) 23 | } 24 | var __children goht.Template 25 | ctx, __children = goht.PopChildren(ctx) 26 | _ = __children 27 | if _, __err = __buf.WriteString("\n\n\n\nHello World\n
\n\n\n"); __err != nil { 28 | return 29 | } 30 | if !__isBuf { 31 | _, __err = __w.Write(__buf.Bytes()) 32 | } 33 | return 34 | }) 35 | } 36 | 37 | func HamlUsingTabs() goht.Template { 38 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 39 | __buf, __isBuf := __w.(goht.Buffer) 40 | if !__isBuf { 41 | __buf = goht.GetBuffer() 42 | defer goht.ReleaseBuffer(__buf) 43 | } 44 | var __children goht.Template 45 | ctx, __children = goht.PopChildren(ctx) 46 | _ = __children 47 | if _, __err = __buf.WriteString("\n\n\n\nHello World\n
\n\n\n"); __err != nil { 48 | return 49 | } 50 | if !__isBuf { 51 | _, __err = __w.Write(__buf.Bytes()) 52 | } 53 | return 54 | }) 55 | } 56 | 57 | func SlimUsingTabs() goht.Template { 58 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 59 | __buf, __isBuf := __w.(goht.Buffer) 60 | if !__isBuf { 61 | __buf = goht.GetBuffer() 62 | defer goht.ReleaseBuffer(__buf) 63 | } 64 | var __children goht.Template 65 | ctx, __children = goht.PopChildren(ctx) 66 | _ = __children 67 | if _, __err = __buf.WriteString("Hello World
\n"); __err != nil { 68 | return 69 | } 70 | if !__isBuf { 71 | _, __err = __w.Write(__buf.Bytes()) 72 | } 73 | return 74 | }) 75 | } 76 | -------------------------------------------------------------------------------- /examples/tags/general.goht: -------------------------------------------------------------------------------- 1 | package tags 2 | // You may specify the type of tag that you want created with `%`. 3 | 4 | @goht SpecifyTag() { 5 | %p This is a paragraph tag. 6 | %main This is a main tag. 7 | } 8 | 9 | @haml HamlSpecifyTag() { 10 | %p This is a paragraph tag. 11 | %main This is a main tag. 12 | } 13 | 14 | @slim SlimSpecifyTag() { 15 | p This is a paragraph tag. 16 | main This is a main tag. 17 | } 18 | 19 | // You may also let the tag default to a `div` when using the id or 20 | // class syntax's, `#` and `.` respectively. 21 | 22 | @goht DefaultToDivs() { 23 | #main This is a div tag with an id of `main`. 24 | .main This is a div tag with a class of `main`. 25 | } 26 | 27 | @haml HamlDefaultToDivs() { 28 | #main This is a div tag with an id of `main`. 29 | .main This is a div tag with a class of `main`. 30 | } 31 | 32 | @slim SlimDefaultToDivs() { 33 | #main This is a div tag with an id of `main`. 34 | .main This is a div tag with a class of `main`. 35 | } 36 | 37 | // The three may also be combined. The `%` must come first, followed 38 | // by either the `#` or `.`. The `#` and `.` may be in any order. 39 | 40 | @goht Combined() { 41 | %p#main This is a paragraph tag with an id of `main`. 42 | %main.main This is a main tag with a class of `main`. 43 | .main#main This is a div tag with an id and class of `main`. 44 | %p.main#main This is a paragraph tag with an id and class of `main`. 45 | } 46 | 47 | @haml HamlCombined() { 48 | %p#main This is a paragraph tag with an id of `main`. 49 | %main.main This is a main tag with a class of `main`. 50 | .main#main This is a div tag with an id and class of `main`. 51 | %p.main#main This is a paragraph tag with an id and class of `main`. 52 | } 53 | 54 | @slim SlimCombined() { 55 | p#main This is a paragraph tag with an id of `main`. 56 | main.main This is a main tag with a class of `main`. 57 | .main#main This is a div tag with an id and class of `main`. 58 | p.main#main This is a paragraph tag with an id and class of `main`. 59 | } 60 | 61 | // The class operator may be repeated to add multiple classes. 62 | // Repeating the id operator will result in the id being overwritten 63 | // but will not throw an error. 64 | 65 | @goht MultipleClasses() { 66 | .main.main2 This is a div tag with two classes, `main` and `main2`. 67 | #main#main2 This is a div tag with an id of `main2`. 68 | } 69 | 70 | @haml HamlMultipleClasses() { 71 | .main.main2 This is a div tag with two classes, `main` and `main2`. 72 | #main#main2 This is a div tag with an id of `main2`. 73 | } 74 | 75 | @slim SlimMultipleClasses() { 76 | .main.main2 This is a div tag with two classes, `main` and `main2`. 77 | #main#main2 This is a div tag with an id of `main2`. 78 | } 79 | -------------------------------------------------------------------------------- /examples/tags/inline.goht: -------------------------------------------------------------------------------- 1 | package tags 2 | 3 | // Slim allows you to inline tags for those times when you really 4 | // want to keep things concise. 5 | 6 | @slim SlimInlineTags() { 7 | ul 8 | li: a.first First Item 9 | li: a.second First Item 10 | li: a.third First Item 11 | } 12 | -------------------------------------------------------------------------------- /examples/tags/inline.goht.go: -------------------------------------------------------------------------------- 1 | // Code generated by GoHT v0.8.1 - DO NOT EDIT. 2 | // https://github.com/stackus/goht 3 | 4 | package tags 5 | 6 | import "context" 7 | import "io" 8 | import "github.com/stackus/goht" 9 | 10 | // Slim allows you to inline tags for those times when you really 11 | // want to keep things concise. 12 | 13 | func SlimInlineTags() goht.Template { 14 | return goht.TemplateFunc(func(ctx context.Context, __w io.Writer, __sts ...goht.SlottedTemplate) (__err error) { 15 | __buf, __isBuf := __w.(goht.Buffer) 16 | if !__isBuf { 17 | __buf = goht.GetBuffer() 18 | defer goht.ReleaseBuffer(__buf) 19 | } 20 | var __children goht.Template 21 | ctx, __children = goht.PopChildren(ctx) 22 | _ = __children 23 | if _, __err = __buf.WriteString("\n"); __err != nil { 24 | return 25 | } 26 | if !__isBuf { 27 | _, __err = __w.Write(__buf.Bytes()) 28 | } 29 | return 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /examples/tags/objectreference.goht: -------------------------------------------------------------------------------- 1 | package tags 2 | // In HAML it is possible to include an object in the tag 3 | // declaration to make the addition of classes and ids easier. 4 | // This is done by using a pair of square brackets after the 5 | // tag, id, or class declaration. 6 | // 7 | // The object used will need to implement one or both of the 8 | // following methods: 9 | // - ObjectID() string 10 | // - ObjectClass() string 11 | 12 | type Foo struct { 13 | ID string 14 | } 15 | 16 | func (f *Foo) ObjectID() string { 17 | return f.ID 18 | } 19 | 20 | func (f *Foo) ObjectClass() string { 21 | return "foo" 22 | } 23 | 24 | // if obj below has the id "bar" then the output will be: 25 | //Some text
8 | // 9 | // %p 10 | // Some text 11 | // The above line will render as: 12 | //13 | // Some text 14 | //
15 | // 16 | // Whitespace can have subtle effects on the final output of the 17 | // rendered HTML, so it is important to understand how it works. 18 | 19 | @goht Whitespace() { 20 | %p This text has no whitespace between it and the tag. 21 | %p 22 | This text has whitespace between it and the tag. 23 | %p This tag has whitespace between it and the tag above. 24 | } 25 | 26 | @haml HamlWhitespace() { 27 | %p This text has no whitespace between it and the tag. 28 | %p 29 | This text has whitespace between it and the tag. 30 | %p This tag has whitespace between it and the tag above. 31 | } 32 | 33 | // Slim does not keep whitespace between tags by default. 34 | 35 | @slim SlimWhitespace() { 36 | p This text has no whitespace between it and the tag. 37 | p 38 | This text has NO whitespace between it and the tag. 39 | p This tag has NO whitespace between it and the tag above. 40 | } 41 | 42 | // You can control the whitespace that will be rendered between tags 43 | // in the final output by using the `>` and <` operators. 44 | // The `>` operator will remove all whitespace outside the tag, and 45 | // the `<` operator will remove all whitespace inside the tag. 46 | // These operators must be placed at the end of the tag but before 47 | // either the `!` or `=` operators. 48 | // You can use either or both of these operators on a tag, when using 49 | // both, the order does not matter. 50 | 51 | @goht RemoveWhitespace() { 52 | %p< 53 | This text has no whitespace between it and the parent tag. 54 | %p 55 | There is whitespace between this text and the parent tag. 56 | %p>< 57 | This text has no whitespace between it and the parent tag. 58 | There is also no whitespace between this tag and the sibling text above it. 59 | Finally, the tag has no whitespace between it and the outer tag. 60 | } 61 | 62 | @haml HamlRemoveWhitespace() { 63 | %p< 64 | This text has no whitespace between it and the parent tag. 65 | %p 66 | There is whitespace between this text and the parent tag. 67 | %p>< 68 | This text has no whitespace between it and the parent tag. 69 | There is also no whitespace between this tag and the sibling text above it. 70 | Finally, the tag has no whitespace between it and the outer tag. 71 | } 72 | 73 | // You can add whitespace between tags by using the `>` and `<` operators. 74 | // The `>` operator will add whitespace after the tag, and the `<` operator 75 | // will add whitespace before the tag. 76 | // 77 | // The operators must be placed at the end of the tag: 78 | // p> Text 79 | // p< Text 80 | // p<> Text 81 | 82 | @slim SlimAddWhitespace() { 83 | div> This tag has whitespace after it. 84 | div 85 | |There is whitespace between this text and the parent tag. 86 | p<> There is whitespace before and after this tag. 87 | } 88 | -------------------------------------------------------------------------------- /examples/testdata/ego/hello_world.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |the following will loop a slice of strings and will pass each string into a child template
21 |foo
22 |And it was passed in as well foo
23 |bar
24 |And it was passed in as well bar
25 |baz
26 |And it was passed in as well baz
27 |fizz
28 |And it was passed in as well fizz
29 |buzz
30 |And it was passed in as well buzz
31 |quux
32 |And it was passed in as well quux
33 | 34 | 35 | -------------------------------------------------------------------------------- /examples/testdata/haml/attributes_attributesCmd.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/testdata/haml/attributes_classes.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /examples/testdata/haml/attributes_complexNames.html: -------------------------------------------------------------------------------- 1 | Goht 2 | -------------------------------------------------------------------------------- /examples/testdata/haml/attributes_conditionalAttrs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /examples/testdata/haml/attributes_dynamicAttrs.html: -------------------------------------------------------------------------------- 1 |This is a paragraph.
2 | -------------------------------------------------------------------------------- /examples/testdata/haml/attributes_formattedValue.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /examples/testdata/haml/attributes_multilineAttrs.html: -------------------------------------------------------------------------------- 1 |This is a paragraph.
2 | -------------------------------------------------------------------------------- /examples/testdata/haml/attributes_simpleNames.html: -------------------------------------------------------------------------------- 1 | Goht 2 | -------------------------------------------------------------------------------- /examples/testdata/haml/attributes_staticAttrs.html: -------------------------------------------------------------------------------- 1 |This is a paragraph.
2 | -------------------------------------------------------------------------------- /examples/testdata/haml/attributes_whitespaceAttrs.html: -------------------------------------------------------------------------------- 1 |This is a paragraph.
2 |This is a paragraph.
3 |This is a paragraph.
4 |This is a paragraph.
5 | -------------------------------------------------------------------------------- /examples/testdata/haml/commands_childrenExample.html: -------------------------------------------------------------------------------- 1 |2 | The following was passed in from the calling template: 3 |
4 | -------------------------------------------------------------------------------- /examples/testdata/haml/commands_renderExample.html: -------------------------------------------------------------------------------- 1 |2 | The following was passed in from the calling template: 3 |
4 | 5 |the other template was rendered above.
6 | -------------------------------------------------------------------------------- /examples/testdata/haml/commands_renderWithChildrenExample.html: -------------------------------------------------------------------------------- 1 |The other template will be rendered below.
2 |3 | The following was passed in from the calling template: 4 | this content will be rendered by the other template. 5 |
6 | -------------------------------------------------------------------------------- /examples/testdata/haml/comments_htmlComments.html: -------------------------------------------------------------------------------- 1 |This is a paragraph
2 | 3 | -------------------------------------------------------------------------------- /examples/testdata/haml/comments_htmlCommentsNested.html: -------------------------------------------------------------------------------- 1 |This is a paragraph
2 | 5 | -------------------------------------------------------------------------------- /examples/testdata/haml/comments_rubyStyle.html: -------------------------------------------------------------------------------- 1 |This is the only paragraph in the output.
2 | -------------------------------------------------------------------------------- /examples/testdata/haml/comments_rubyStyleNested.html: -------------------------------------------------------------------------------- 1 |This is the only paragraph in the output.
2 | -------------------------------------------------------------------------------- /examples/testdata/haml/doctype_doctype.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /examples/testdata/haml/example_conditional.html: -------------------------------------------------------------------------------- 1 |Hello, World!
3 | -------------------------------------------------------------------------------- /examples/testdata/haml/example_executeCode.html: -------------------------------------------------------------------------------- 1 |Hello, world!
2 | -------------------------------------------------------------------------------- /examples/testdata/haml/example_ignoreInterpolation.html: -------------------------------------------------------------------------------- 1 | #{someVar}, World! 2 |3 | #{someVar}, World! 4 |
5 | A greeting: #{someVar}, World! 6 | . this line begins with a period 7 | # this line begins with a hash 8 | % this line begins with a percent 9 | -------------------------------------------------------------------------------- /examples/testdata/haml/example_importExample.html: -------------------------------------------------------------------------------- 1 |Hello, World!
2 | -------------------------------------------------------------------------------- /examples/testdata/haml/example_interpolateCode.html: -------------------------------------------------------------------------------- 1 |Hello, World!
2 | -------------------------------------------------------------------------------- /examples/testdata/haml/example_noInterpolation.html: -------------------------------------------------------------------------------- 1 |Do the following; No interpolation is necessary.
2 |Hello, World!
3 |No interpolation is #{performed} here.
4 | -------------------------------------------------------------------------------- /examples/testdata/haml/example_packageExample.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackus/goht/83ec781290f01db90bb84f996ad3b8d4c1819f73/examples/testdata/haml/example_packageExample.html -------------------------------------------------------------------------------- /examples/testdata/haml/example_renderCode.html: -------------------------------------------------------------------------------- 1 |Hello, world!
2 | -------------------------------------------------------------------------------- /examples/testdata/haml/example_shorthandConditional.html: -------------------------------------------------------------------------------- 1 |2 | This is escaped text. It <pre>will not</pre> be displayed as HTML. 3 | This <pre>"will not"</pre> be interpolated with HTML intact. 4 |
5 | -------------------------------------------------------------------------------- /examples/testdata/haml/filters_javascript.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/testdata/haml/filters_plain.html: -------------------------------------------------------------------------------- 1 |2 | This is plain text. It
willbe displayed as HTML. 3 | This
"will"be interpolated with HTML intact. 4 | 5 | -------------------------------------------------------------------------------- /examples/testdata/haml/filters_preserve.html: -------------------------------------------------------------------------------- 1 |
2 | This is preserved text. It
willbe displayed as HTML. This
"will"be interpolated with HTML intact. 3 | 4 | -------------------------------------------------------------------------------- /examples/testdata/haml/formatting_boolExample.html: -------------------------------------------------------------------------------- 1 |
The bool is (true).
2 | -------------------------------------------------------------------------------- /examples/testdata/haml/formatting_floatExample.html: -------------------------------------------------------------------------------- 1 |The float is (123.456000).
2 |The float is (1.234560e+02) in scientific notation.
3 |The float is (123.46) with 2 decimal places.
4 |The float is ( 123.46) with 2 decimal places and padded to 9 characters.
5 |The float is (123.46 ) with 2 decimal places and padded to 9 characters and left aligned.
6 |The float is (000123.46) with 2 decimal places and padded to 9 characters with 0s.
7 | -------------------------------------------------------------------------------- /examples/testdata/haml/formatting_intExample.html: -------------------------------------------------------------------------------- 1 |The integer is (123).
2 |The integer is (1111011) in binary.
3 |The integer is (173) in octal.
4 |The integer is (7b) in hex.
5 |The integer is (7B) in hex with uppercase.
6 |The integer is ({) as a character.
7 | -------------------------------------------------------------------------------- /examples/testdata/haml/formatting_stringExample.html: -------------------------------------------------------------------------------- 1 |The string is (Hello). Strings do not require any additional formatting.
2 |The string is ("Hello") quoted.
3 |The string is (48656c6c6f) as hex.
4 |The string is (48656C6C6F) as hex with uppercase.
5 |The string is (Hello) as is.
6 |The string is (Hell), truncated to 4 characters.
7 |The string is ( Hello), padded to 6 characters.
8 |The string is ( Hell), truncated to 4 characters and padded to 6 characters.
9 |The string is ("Hell"), truncated to 4 characters and padded to 6 characters and quoted.
10 | -------------------------------------------------------------------------------- /examples/testdata/haml/hello_world.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |foo
20 | 21 |And it was passed in as well foo
22 |bar
23 | 24 |And it was passed in as well bar
25 |baz
26 | 27 |And it was passed in as well baz
28 |fizz
29 | 30 |And it was passed in as well fizz
31 |buzz
32 | 33 |And it was passed in as well buzz
34 |quux
35 | 36 |And it was passed in as well quux
37 | 38 | 39 | -------------------------------------------------------------------------------- /examples/testdata/haml/indents_usingTabs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |13 | Hello World 14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /examples/testdata/haml/tags_alsoSelfClosing.html: -------------------------------------------------------------------------------- 1 |This is a paragraph tag with an id of `main`.
2 |This is a paragraph tag with an id and class of `main`.
5 | -------------------------------------------------------------------------------- /examples/testdata/haml/tags_defaultToDivs.html: -------------------------------------------------------------------------------- 1 |This text has no whitespace between it and the parent tag.
2 |3 | There is whitespace between this text and the parent tag.
This text has no whitespace between it and the parent tag. 4 | There is also no whitespace between this tag and the sibling text above it. 5 | Finally, the tag has no whitespace between it and the outer tag.
6 | -------------------------------------------------------------------------------- /examples/testdata/haml/tags_selfClosing.html: -------------------------------------------------------------------------------- 1 |
2 | A paragraph is not self closing.
3 |
This is a paragraph tag.
2 |This text has no whitespace between it and the tag.
2 |3 | This text has whitespace between it and the tag. 4 |
This tag has whitespace between it and the tag above.
5 | 6 | -------------------------------------------------------------------------------- /examples/testdata/haml/unescape_unescapeCode.html: -------------------------------------------------------------------------------- 1 |This is <em>NOT</em> unescaped HTML. (Ampersands everywhere!)
2 |This is unescaped HTML.
3 | -------------------------------------------------------------------------------- /examples/testdata/haml/unescape_unescapeInterpolation.html: -------------------------------------------------------------------------------- 1 |This <em>is</em> is escaped. (Ampersands everywhere!)
2 |This is is NOT escaped.
3 | -------------------------------------------------------------------------------- /examples/testdata/haml/unescape_unescapeText.html: -------------------------------------------------------------------------------- 1 |This is HTML.
2 |This is HTML.
3 | -------------------------------------------------------------------------------- /examples/testdata/slim/attributes_attributesCmd.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /examples/testdata/slim/attributes_classes.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /examples/testdata/slim/attributes_complexNames.html: -------------------------------------------------------------------------------- 1 | Goht 2 | -------------------------------------------------------------------------------- /examples/testdata/slim/attributes_conditionalAttrs.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /examples/testdata/slim/attributes_dynamicAttrs.html: -------------------------------------------------------------------------------- 1 |This is a paragraph.
2 | -------------------------------------------------------------------------------- /examples/testdata/slim/attributes_formattedValue.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /examples/testdata/slim/attributes_multilineAttrs.html: -------------------------------------------------------------------------------- 1 |This is a paragraph.
2 | -------------------------------------------------------------------------------- /examples/testdata/slim/attributes_simpleNames.html: -------------------------------------------------------------------------------- 1 | Goht 2 | -------------------------------------------------------------------------------- /examples/testdata/slim/attributes_staticAttrs.html: -------------------------------------------------------------------------------- 1 |This is a paragraph.
2 | -------------------------------------------------------------------------------- /examples/testdata/slim/attributes_whitespaceAttrs.html: -------------------------------------------------------------------------------- 1 |This is a paragraph.
This is a paragraph.
This is a paragraph.
This is a paragraph.
2 | -------------------------------------------------------------------------------- /examples/testdata/slim/commands_childrenExample.html: -------------------------------------------------------------------------------- 1 |2 | The following was passed in from the calling template: 3 |
4 |the other template was rendered above.
5 | -------------------------------------------------------------------------------- /examples/testdata/slim/commands_renderWithChildrenExample.html: -------------------------------------------------------------------------------- 1 |The other template will be rendered below.
2 | The following was passed in from the calling template: 3 | this content will be rendered by the other template.
4 | 5 | -------------------------------------------------------------------------------- /examples/testdata/slim/comments_htmlComments.html: -------------------------------------------------------------------------------- 1 |This is a paragraph
2 | -------------------------------------------------------------------------------- /examples/testdata/slim/comments_htmlCommentsNested.html: -------------------------------------------------------------------------------- 1 |This is a paragraph
2 | -------------------------------------------------------------------------------- /examples/testdata/slim/comments_rubyStyle.html: -------------------------------------------------------------------------------- 1 |This is the only paragraph in the output.
2 | -------------------------------------------------------------------------------- /examples/testdata/slim/comments_rubyStyleNested.html: -------------------------------------------------------------------------------- 1 |This is the only paragraph in the output.
2 | -------------------------------------------------------------------------------- /examples/testdata/slim/doctype_doctype.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /examples/testdata/slim/example_conditional.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /examples/testdata/slim/example_doc.html: -------------------------------------------------------------------------------- 1 |Hello, World!
3 | -------------------------------------------------------------------------------- /examples/testdata/slim/example_executeCode.html: -------------------------------------------------------------------------------- 1 |Hello, world!
2 | -------------------------------------------------------------------------------- /examples/testdata/slim/example_ignoreInterpolation.html: -------------------------------------------------------------------------------- 1 | #{someVar}, World! 2 |3 | #{someVar}, World! 4 |
5 | A greeting: #{someVar}, World! 6 | . this line begins with a period 7 | # this line begins with a hash 8 | % this line begins with a percent 9 | -------------------------------------------------------------------------------- /examples/testdata/slim/example_importExample.html: -------------------------------------------------------------------------------- 1 |Hello, World!
2 | -------------------------------------------------------------------------------- /examples/testdata/slim/example_interpolateCode.html: -------------------------------------------------------------------------------- 1 |Hello, World!
2 | -------------------------------------------------------------------------------- /examples/testdata/slim/example_noInterpolation.html: -------------------------------------------------------------------------------- 1 |Do the following; No interpolation is necessary.
Hello, World!
No interpolation is #{performed} here.
2 | -------------------------------------------------------------------------------- /examples/testdata/slim/example_packageExample.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stackus/goht/83ec781290f01db90bb84f996ad3b8d4c1819f73/examples/testdata/slim/example_packageExample.html -------------------------------------------------------------------------------- /examples/testdata/slim/example_renderCode.html: -------------------------------------------------------------------------------- 1 |Hello, world!
2 | -------------------------------------------------------------------------------- /examples/testdata/slim/example_shorthandConditional.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /examples/testdata/slim/example_shorthandSwitch.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /examples/testdata/slim/example_userDetails.html: -------------------------------------------------------------------------------- 1 |The bool is (true).
2 | -------------------------------------------------------------------------------- /examples/testdata/slim/formatting_floatExample.html: -------------------------------------------------------------------------------- 1 |The float is (123.456000).
The float is (1.234560e+02) in scientific notation.
The float is (123.46) with 2 decimal places.
The float is ( 123.46) with 2 decimal places and padded to 9 characters.
The float is (123.46 ) with 2 decimal places and padded to 9 characters and left aligned.
The float is (000123.46) with 2 decimal places and padded to 9 characters with 0s.
2 | -------------------------------------------------------------------------------- /examples/testdata/slim/formatting_intExample.html: -------------------------------------------------------------------------------- 1 |The integer is (123).
The integer is (1111011) in binary.
The integer is (173) in octal.
The integer is (7b) in hex.
The integer is (7B) in hex with uppercase.
The integer is ({) as a character.
2 | -------------------------------------------------------------------------------- /examples/testdata/slim/formatting_stringExample.html: -------------------------------------------------------------------------------- 1 |The string is (Hello). Strings do not require any additional formatting.
The string is ("Hello") quoted.
The string is (48656c6c6f) as hex.
The string is (48656C6C6F) as hex with uppercase.
The string is (Hello) as is.
The string is (Hell), truncated to 4 characters.
The string is ( Hello), padded to 6 characters.
The string is ( Hell), truncated to 4 characters and padded to 6 characters.
The string is ("Hell"), truncated to 4 characters and padded to 6 characters and quoted.
2 | -------------------------------------------------------------------------------- /examples/testdata/slim/hello_world.html: -------------------------------------------------------------------------------- 1 |the following will loop a slice of strings and will pass each string into a child template
foo
And it was passed in as well foo
12 |bar
And it was passed in as well bar
13 |baz
And it was passed in as well baz
14 |fizz
And it was passed in as well fizz
15 |buzz
And it was passed in as well buzz
16 |quux
And it was passed in as well quux
17 | 18 | -------------------------------------------------------------------------------- /examples/testdata/slim/indents_usingTabs.html: -------------------------------------------------------------------------------- 1 |Hello World
2 | -------------------------------------------------------------------------------- /examples/testdata/slim/tags_addWhitespace.html: -------------------------------------------------------------------------------- 1 |There is whitespace before and after this tag.
This is a paragraph tag with an id of `main`.
This is a paragraph tag with an id and class of `main`.
2 | -------------------------------------------------------------------------------- /examples/testdata/slim/tags_defaultToDivs.html: -------------------------------------------------------------------------------- 1 |A paragraph is not self closing.
This is a paragraph tag.
This text has no whitespace between it and the tag.
This tag has NO whitespace between it and the tag above.
2 | -------------------------------------------------------------------------------- /examples/testdata/slim/unescape_unescapeCode.html: -------------------------------------------------------------------------------- 1 |This is <em>is</em> escaped HTML. (Ampersands everywhere!)
This is NOT escaped HTML.
2 | -------------------------------------------------------------------------------- /examples/unescaping/unescape.goht: -------------------------------------------------------------------------------- 1 | package unescape 2 | 3 | // The text from variables are assumed to need HTML escaping. If you 4 | // want to include raw HTML, or you have already processed the variable 5 | // then you will need to unescape the text. This is accomplished with 6 | // the `!` operator. 7 | // Use care when using the unescape operator. You should only use it 8 | // when you are sure that the text is safe. If you are unsure, then 9 | // you should use the default escaping. 10 | 11 | @goht UnescapeCode() { 12 | %p= "This is NOT unescaped HTML. (Ampersands everywhere!)" 13 | %p!= "This is unescaped HTML." 14 | } 15 | 16 | @haml HamlUnescapeCode() { 17 | %p= "This is NOT unescaped HTML. (Ampersands everywhere!)" 18 | %p!= "This is unescaped HTML." 19 | } 20 | 21 | @slim SlimUnescapeCode() { 22 | p= "This is is escaped HTML. (Ampersands everywhere!)" 23 | p== "This is NOT escaped HTML." 24 | } 25 | 26 | // It can also affect the interpolated values. 27 | 28 | @goht UnescapeInterpolation() { 29 | - var html = "is" 30 | %p This #{html} is escaped. (Ampersands everywhere!) 31 | %p! This #{html} is NOT escaped. 32 | } 33 | 34 | @haml HamlUnescapeInterpolation() { 35 | - var html = "is" 36 | %p This #{html} is escaped. (Ampersands everywhere!) 37 | %p! This #{html} is NOT escaped. 38 | } 39 | 40 | // The plain text that you write into your Goht templates will not be 41 | // altered by the addition of the `!` operator. It is expected that this 42 | // text has already been HTML escaped properly. 43 | 44 | @goht UnescapeText() { 45 | %p This is HTML. 46 | %p! This is HTML. 47 | } 48 | 49 | @haml HamlUnescapeText() { 50 | %p This is HTML. 51 | %p! This is HTML. 52 | } 53 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/stackus/goht 2 | 3 | go 1.21.4 4 | 5 | require ( 6 | github.com/cenkalti/backoff/v4 v4.2.1 7 | github.com/charmbracelet/log v0.3.1 8 | github.com/rs/zerolog v1.32.0 9 | github.com/sergi/go-diff v1.3.1 10 | github.com/spf13/cobra v1.8.0 11 | github.com/stackus/errors v0.1.5 12 | go.lsp.dev/jsonrpc2 v0.10.0 13 | go.lsp.dev/pkg v0.0.0-20210717090340-384b27a52fb2 14 | ) 15 | 16 | require ( 17 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect 18 | github.com/charmbracelet/lipgloss v0.9.1 // indirect 19 | github.com/go-logfmt/logfmt v0.6.0 // indirect 20 | github.com/golang/protobuf v1.4.2 // indirect 21 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 22 | github.com/lucasb-eyer/go-colorful v1.2.0 // indirect 23 | github.com/mattn/go-colorable v0.1.13 // indirect 24 | github.com/mattn/go-isatty v0.0.19 // indirect 25 | github.com/mattn/go-runewidth v0.0.15 // indirect 26 | github.com/muesli/reflow v0.3.0 // indirect 27 | github.com/muesli/termenv v0.15.2 // indirect 28 | github.com/rivo/uniseg v0.2.0 // indirect 29 | github.com/segmentio/asm v1.1.3 // indirect 30 | github.com/segmentio/encoding v0.3.4 // indirect 31 | github.com/spf13/pflag v1.0.5 // indirect 32 | golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect 33 | golang.org/x/sys v0.13.0 // indirect 34 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect 35 | google.golang.org/grpc v1.38.0 // indirect 36 | google.golang.org/protobuf v1.25.0 // indirect 37 | ) 38 | -------------------------------------------------------------------------------- /helpers.go: -------------------------------------------------------------------------------- 1 | package goht 2 | 3 | // If returns the trueValue if the condition is true, otherwise falseValue. 4 | func If(condition bool, trueValue, falseValue string) string { 5 | if condition { 6 | return trueValue 7 | } 8 | return falseValue 9 | } 10 | -------------------------------------------------------------------------------- /internal/logging/logged_stream.go: -------------------------------------------------------------------------------- 1 | package logging 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "sync" 7 | "time" 8 | 9 | "github.com/rs/zerolog" 10 | "go.lsp.dev/jsonrpc2" 11 | ) 12 | 13 | // Majority of this logging has been lifted from: github.com/golang/tools/internal/lsp/protocol/log.go 14 | 15 | type LoggedStream struct { 16 | Label string 17 | jsonrpc2.Stream 18 | Logger zerolog.Logger 19 | } 20 | 21 | var _ jsonrpc2.Stream = (*LoggedStream)(nil) 22 | 23 | type req struct { 24 | method string 25 | start time.Time 26 | } 27 | 28 | type mapped struct { 29 | mu sync.Mutex 30 | clientCalls map[string]req 31 | serverCalls map[string]req 32 | } 33 | 34 | var maps = &mapped{ 35 | sync.Mutex{}, 36 | make(map[string]req), 37 | make(map[string]req), 38 | } 39 | 40 | func (s LoggedStream) logMsg(msg jsonrpc2.Message, isRead bool) { 41 | direction, pastTense := "Received", "Received" 42 | get, set := maps.client, maps.setServer 43 | if isRead { 44 | direction, pastTense = "Sending", "Sent" 45 | get, set = maps.server, maps.setClient 46 | } 47 | if msg == nil { 48 | return 49 | } 50 | tm := time.Now() 51 | 52 | var logMsg string 53 | args := map[string]any{} 54 | 55 | switch msg := msg.(type) { 56 | case *jsonrpc2.Call: 57 | id := fmt.Sprint(msg.ID()) 58 | logMsg = fmt.Sprintf("[%s] %s request '%s - (%s)'.", s.Label, direction, msg.Method(), id) 59 | args["params"] = string(msg.Params()) 60 | set(id, req{method: msg.Method(), start: tm}) 61 | case *jsonrpc2.Notification: 62 | logMsg = fmt.Sprintf("[%s] %s notification '%s'.", s.Label, direction, msg.Method()) 63 | args["params"] = string(msg.Params()) 64 | case *jsonrpc2.Response: 65 | id := fmt.Sprint(msg.ID()) 66 | if err := msg.Err(); err != nil { 67 | s.Logger.Error().Err(err).Msgf("[%s] %s #%s", s.Label, pastTense, id) 68 | return 69 | } 70 | cc := get(id) 71 | elapsed := tm.Sub(cc.start) 72 | logMsg = fmt.Sprintf("[%s] %s response '%s - (%s)' in %dms.", s.Label, direction, cc.method, id, elapsed/time.Millisecond) 73 | args["result"] = string(msg.Result()) 74 | } 75 | s.Logger.Info().Fields(args).Msg(logMsg) 76 | } 77 | 78 | func (s LoggedStream) Read(ctx context.Context) (jsonrpc2.Message, int64, error) { 79 | msg, count, err := s.Stream.Read(ctx) 80 | s.logMsg(msg, true) 81 | return msg, count, err 82 | } 83 | 84 | func (s LoggedStream) Write(ctx context.Context, msg jsonrpc2.Message) (int64, error) { 85 | count, err := s.Stream.Write(ctx, msg) 86 | s.logMsg(msg, false) 87 | return count, err 88 | } 89 | 90 | // these 4 methods are each used exactly once, but it seemed 91 | // better to have the encapsulation rather than ad hoc mutex 92 | // code in 4 places 93 | func (m *mapped) client(id string) req { 94 | m.mu.Lock() 95 | defer m.mu.Unlock() 96 | v := m.clientCalls[id] 97 | delete(m.clientCalls, id) 98 | return v 99 | } 100 | 101 | func (m *mapped) server(id string) req { 102 | m.mu.Lock() 103 | defer m.mu.Unlock() 104 | v := m.serverCalls[id] 105 | delete(m.serverCalls, id) 106 | return v 107 | } 108 | 109 | func (m *mapped) setClient(id string, r req) { 110 | m.mu.Lock() 111 | defer m.mu.Unlock() 112 | m.clientCalls[id] = r 113 | } 114 | 115 | func (m *mapped) setServer(id string, r req) { 116 | m.mu.Lock() 117 | defer m.mu.Unlock() 118 | m.serverCalls[id] = r 119 | } 120 | -------------------------------------------------------------------------------- /internal/logging/logger.go: -------------------------------------------------------------------------------- 1 | package logging 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | 8 | "github.com/rs/zerolog" 9 | ) 10 | 11 | func NewLogger(w io.Writer, level zerolog.Level) zerolog.Logger { 12 | return zerolog.New(zerolog.ConsoleWriter{ 13 | Out: w, 14 | TimeFormat: "2006-01-02 15:04:05.000", 15 | FormatLevel: func(i any) string { 16 | return strings.ToUpper(fmt.Sprintf("[%-5s]", i)) 17 | }, 18 | FormatFieldName: func(i any) string { 19 | return fmt.Sprintf("\n\t%s:", i) 20 | }, 21 | NoColor: true, 22 | }).Level(level).With().Timestamp().Logger() 23 | } 24 | -------------------------------------------------------------------------------- /internal/protocol/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /internal/protocol/tsdocument_changes.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package protocol 6 | 7 | import ( 8 | "encoding/json" 9 | "fmt" 10 | ) 11 | 12 | // DocumentChanges is a union of a file edit and directory rename operations 13 | // for package renaming feature. At most one field of this struct is non-nil. 14 | type DocumentChanges struct { 15 | TextDocumentEdit *TextDocumentEdit 16 | RenameFile *RenameFile 17 | } 18 | 19 | func (d *DocumentChanges) UnmarshalJSON(data []byte) error { 20 | var m map[string]interface{} 21 | 22 | if err := json.Unmarshal(data, &m); err != nil { 23 | return err 24 | } 25 | 26 | if _, ok := m["textDocument"]; ok { 27 | d.TextDocumentEdit = new(TextDocumentEdit) 28 | return json.Unmarshal(data, d.TextDocumentEdit) 29 | } 30 | 31 | d.RenameFile = new(RenameFile) 32 | return json.Unmarshal(data, d.RenameFile) 33 | } 34 | 35 | func (d *DocumentChanges) MarshalJSON() ([]byte, error) { 36 | if d.TextDocumentEdit != nil { 37 | return json.Marshal(d.TextDocumentEdit) 38 | } else if d.RenameFile != nil { 39 | return json.Marshal(d.RenameFile) 40 | } 41 | return nil, fmt.Errorf("Empty DocumentChanges union value") 42 | } 43 | -------------------------------------------------------------------------------- /internal/protocol/util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package protocol 6 | 7 | import ( 8 | "path/filepath" 9 | "strings" 10 | ) 11 | 12 | // inDir checks whether path is in the file tree rooted at dir. 13 | // It checks only the lexical form of the file names. 14 | // It does not consider symbolic links. 15 | // 16 | // Copied from go/src/cmd/go/internal/search/search.go. 17 | func inDir(dir, path string) bool { 18 | pv := strings.ToUpper(filepath.VolumeName(path)) 19 | dv := strings.ToUpper(filepath.VolumeName(dir)) 20 | path = path[len(pv):] 21 | dir = dir[len(dv):] 22 | switch { 23 | default: 24 | return false 25 | case pv != dv: 26 | return false 27 | case len(path) == len(dir): 28 | if path == dir { 29 | return true 30 | } 31 | return false 32 | case dir == "": 33 | return path != "" 34 | case len(path) > len(dir): 35 | if dir[len(dir)-1] == filepath.Separator { 36 | if path[:len(dir)] == dir { 37 | return path[len(dir):] != "" 38 | } 39 | return false 40 | } 41 | if path[len(dir)] == filepath.Separator && path[:len(dir)] == dir { 42 | if len(path) == len(dir)+1 { 43 | return true 44 | } 45 | return path[len(dir)+1:] != "" 46 | } 47 | return false 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /internal/proxy/client.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "strings" 7 | 8 | "github.com/rs/zerolog" 9 | 10 | "github.com/stackus/goht/compiler" 11 | "github.com/stackus/goht/internal/protocol" 12 | ) 13 | 14 | type Client struct { 15 | protocol.Client 16 | smc *SourceMapCache 17 | dc *DiagnosticsCache 18 | logger zerolog.Logger 19 | } 20 | 21 | var _ protocol.Client = (*Client)(nil) 22 | 23 | func NewClient(c protocol.Client, smc *SourceMapCache, dc *DiagnosticsCache, logger zerolog.Logger) *Client { 24 | return &Client{ 25 | Client: c, 26 | smc: smc, 27 | dc: dc, 28 | logger: logger, 29 | } 30 | } 31 | 32 | func (c *Client) PublishDiagnostics(ctx context.Context, params *protocol.PublishDiagnosticsParams) error { 33 | logger := c.logger.With(). 34 | Str("uri", string(params.URI)). 35 | Int("count", len(params.Diagnostics)). 36 | Logger() 37 | 38 | logger.Debug().Msg("SERVER -> CLIENT: PublishDiagnostics") 39 | 40 | _, gohtURI := toGohtURI(params.URI) 41 | sm, ok := c.smc.Get(string(gohtURI)) 42 | if !ok { 43 | c.logger.Warn().Msgf("unable to complete because the sourcemap for %q doesn't exist in the cache, has the didOpen notification been sent yet", gohtURI) 44 | return fmt.Errorf("unable to complete because the sourcemap for %q doesn't exist in the cache, has the didOpen notification been sent yet", gohtURI) 45 | } 46 | 47 | // update the uri to the original 48 | params.URI = gohtURI 49 | for i, diagnostic := range params.Diagnostics { 50 | var start, end compiler.Position 51 | start, ok = sm.SourcePositionFromTarget(int(diagnostic.Range.Start.Line), int(diagnostic.Range.Start.Character)) 52 | if !ok { 53 | logger.Warn().Any("diagnostic", diagnostic).Msg("unable to map start position") 54 | continue 55 | } 56 | 57 | if diagnostic.Range.Start.Line == diagnostic.Range.End.Line { 58 | length := diagnostic.Range.End.Character - diagnostic.Range.Start.Character 59 | diagnostic.Range.Start.Line = uint32(start.Line) 60 | diagnostic.Range.Start.Character = uint32(start.Col) 61 | diagnostic.Range.End.Line = uint32(start.Line) 62 | diagnostic.Range.End.Character = uint32(start.Col) + length 63 | params.Diagnostics[i] = diagnostic 64 | continue 65 | } 66 | 67 | end, ok = sm.SourcePositionFromTarget(int(diagnostic.Range.End.Line), int(diagnostic.Range.End.Character)) 68 | if !ok { 69 | logger.Warn().Any("diagnostic", diagnostic).Msg("unable to map end position") 70 | continue 71 | } 72 | 73 | diagnostic.Range.Start.Line = uint32(start.Line) 74 | diagnostic.Range.Start.Character = uint32(start.Col) 75 | diagnostic.Range.End.Line = uint32(end.Line) 76 | diagnostic.Range.End.Character = uint32(end.Col) 77 | params.Diagnostics[i] = diagnostic 78 | } 79 | params.Diagnostics = c.dc.WithGohtDiagnostics(string(gohtURI), params.Diagnostics) 80 | return c.Client.PublishDiagnostics(ctx, params) 81 | } 82 | 83 | const doNotEditMessage = "Do not edit this file!" 84 | 85 | func (c *Client) ShowMessage(ctx context.Context, params *protocol.ShowMessageParams) error { 86 | c.logger.Info().Str("message", params.Message).Msg("SERVER -> CLIENT: ShowMessage") 87 | // Why is this message ignored? 88 | // Because gopls will return a message to the client to not edit the file when it contains 89 | // a header with "DO NOT EDIT". We include such a header in our generated Go files because 90 | // 1. they are generated, and 2. we include them in the files gopls sees because that causes 91 | // gopls to not do certain things like checking the position of the cursor in the file. This 92 | // particular check is a problem because it causes gopls to throw "column is beyond end of line" 93 | // errors for just about any interaction the user has with their IDE. 94 | if strings.HasPrefix(params.Message, doNotEditMessage) { 95 | return nil 96 | } 97 | return c.Client.ShowMessage(ctx, params) 98 | } 99 | -------------------------------------------------------------------------------- /internal/proxy/diagnostics_cache.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/stackus/goht/internal/protocol" 7 | ) 8 | 9 | type DiagnosticsCache struct { 10 | gohtDiagnostics map[string][]protocol.Diagnostic 11 | goDiagnostics map[string][]protocol.Diagnostic 12 | mu sync.Mutex 13 | } 14 | 15 | func NewDiagnosticsCache() *DiagnosticsCache { 16 | return &DiagnosticsCache{ 17 | gohtDiagnostics: make(map[string][]protocol.Diagnostic), 18 | goDiagnostics: make(map[string][]protocol.Diagnostic), 19 | } 20 | } 21 | 22 | func (dc *DiagnosticsCache) WithGohtDiagnostics(uri string, diagnostics []protocol.Diagnostic) []protocol.Diagnostic { 23 | if diagnostics == nil { 24 | diagnostics = make([]protocol.Diagnostic, 0) 25 | } 26 | dc.mu.Lock() 27 | defer dc.mu.Unlock() 28 | dc.goDiagnostics[uri] = diagnostics 29 | if dc.gohtDiagnostics[uri] == nil { 30 | dc.gohtDiagnostics[uri] = make([]protocol.Diagnostic, 0) 31 | } 32 | return append(dc.gohtDiagnostics[uri], diagnostics...) 33 | } 34 | 35 | func (dc *DiagnosticsCache) WithGoDiagnostics(uri string, diagnostics []protocol.Diagnostic) []protocol.Diagnostic { 36 | if diagnostics == nil { 37 | diagnostics = make([]protocol.Diagnostic, 0) 38 | } 39 | dc.mu.Lock() 40 | defer dc.mu.Unlock() 41 | dc.gohtDiagnostics[uri] = diagnostics 42 | if dc.goDiagnostics[uri] == nil { 43 | dc.goDiagnostics[uri] = make([]protocol.Diagnostic, 0) 44 | } 45 | return append(dc.goDiagnostics[uri], diagnostics...) 46 | } 47 | 48 | func (dc *DiagnosticsCache) ClearGohtDiagnostics(uri string) { 49 | dc.mu.Lock() 50 | defer dc.mu.Unlock() 51 | dc.gohtDiagnostics[uri] = make([]protocol.Diagnostic, 0) 52 | } 53 | -------------------------------------------------------------------------------- /internal/proxy/document.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/stackus/goht/internal/protocol" 8 | ) 9 | 10 | type Document struct { 11 | lines []string 12 | } 13 | 14 | var _ fmt.Stringer = (*Document)(nil) 15 | 16 | func NewDocument(text string) *Document { 17 | return &Document{ 18 | lines: strings.Split(text, "\n"), 19 | } 20 | } 21 | 22 | func (d *Document) String() string { 23 | return strings.Join(d.lines, "\n") 24 | } 25 | 26 | func (d *Document) Apply(r *protocol.Range, text string) { 27 | lines := strings.Split(text, "\n") 28 | switch { 29 | case d.isWholeDocument(r): 30 | d.lines = lines 31 | case d.isInsert(r) && text != "": 32 | d.insert(int(r.Start.Line), int(r.Start.Character), lines) 33 | case d.isReplace(r) && text == "": 34 | d.delete(int(r.Start.Line), int(r.Start.Character), int(r.End.Line), int(r.End.Character)) 35 | case d.isReplace(r) && text != "": 36 | d.overwrite(int(r.Start.Line), int(r.Start.Character), int(r.End.Line), int(r.End.Character), lines) 37 | } 38 | } 39 | 40 | func (d *Document) isWholeDocument(r *protocol.Range) bool { 41 | if r == nil { 42 | return true 43 | } 44 | if r.Start.Line != 0 || r.Start.Character != 0 { 45 | return false 46 | } 47 | l, c := len(d.lines), len(d.lines[len(d.lines)-1]) 48 | return r.End.Line >= uint32(l) || r.End.Character >= uint32(c) 49 | } 50 | 51 | func (d *Document) isInsert(r *protocol.Range) bool { 52 | return r.Start.Line == r.End.Line && r.Start.Character == r.End.Character 53 | } 54 | 55 | func (d *Document) insert(line, col int, lines []string) { 56 | before := d.lines[line][:col] 57 | after := d.lines[line][col:] 58 | d.lines[line] = before + lines[0] 59 | if len(lines) > 1 { 60 | d.lines = append(d.lines[:line+1], append(lines[1:], d.lines[:line+1]...)...) 61 | } 62 | d.lines[line+len(lines)-1] = lines[len(lines)-1] + after 63 | } 64 | 65 | func (d *Document) isReplace(r *protocol.Range) bool { 66 | return r.Start.Line != r.End.Line || r.Start.Character != r.End.Character 67 | } 68 | 69 | func (d *Document) delete(startLine, startCol, endLine, endCol int) { 70 | if startLine == endLine { 71 | d.lines[startLine] = d.lines[startLine][:startCol] + d.lines[startLine][endCol:] 72 | return 73 | } 74 | d.lines[startLine] = d.lines[startLine][:startCol] 75 | d.lines[endLine] = d.lines[endLine][endCol:] 76 | d.lines = append(d.lines[:startLine+1], d.lines[endLine:]...) 77 | } 78 | 79 | func (d *Document) overwrite(startLine, startCol, endLine, endCol int, lines []string) { 80 | if startLine == endLine { 81 | d.lines[startLine] = d.lines[startLine][:startCol] + lines[0] + d.lines[startLine][endCol:] 82 | return 83 | } 84 | d.lines[startLine] = d.lines[startLine][:startCol] + lines[0] 85 | d.lines[endLine] = lines[len(lines)-1] + d.lines[endLine][endCol:] 86 | d.lines = append(d.lines[:startLine+1], append(lines[1:], d.lines[endLine:]...)...) 87 | } 88 | -------------------------------------------------------------------------------- /internal/proxy/document_contents.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | 7 | "github.com/stackus/goht/internal/protocol" 8 | ) 9 | 10 | type DocumentContents struct { 11 | documents map[string]*Document 12 | mu sync.Mutex 13 | } 14 | 15 | func NewDocumentContents() *DocumentContents { 16 | return &DocumentContents{ 17 | documents: make(map[string]*Document), 18 | } 19 | } 20 | 21 | func (dc *DocumentContents) Set(uri string, d *Document) { 22 | dc.mu.Lock() 23 | defer dc.mu.Unlock() 24 | dc.documents[uri] = d 25 | } 26 | 27 | func (dc *DocumentContents) Get(uri string) (d *Document, ok bool) { 28 | dc.mu.Lock() 29 | defer dc.mu.Unlock() 30 | d, ok = dc.documents[uri] 31 | return 32 | } 33 | 34 | func (dc *DocumentContents) Delete(uri string) { 35 | dc.mu.Lock() 36 | defer dc.mu.Unlock() 37 | delete(dc.documents, uri) 38 | } 39 | 40 | func (dc *DocumentContents) URIs() (uris []string) { 41 | dc.mu.Lock() 42 | defer dc.mu.Unlock() 43 | uris = make([]string, len(dc.documents)) 44 | var i int 45 | for k := range dc.documents { 46 | uris[i] = k 47 | i++ 48 | } 49 | return uris 50 | } 51 | 52 | func (dc *DocumentContents) Apply(uri string, changes []protocol.TextDocumentContentChangeEvent) (*Document, error) { 53 | dc.mu.Lock() 54 | defer dc.mu.Unlock() 55 | d, ok := dc.documents[uri] 56 | if !ok { 57 | return nil, fmt.Errorf("document %q not found", uri) 58 | } 59 | 60 | for _, change := range changes { 61 | d.Apply(change.Range, change.Text) 62 | } 63 | 64 | return d, nil 65 | } 66 | -------------------------------------------------------------------------------- /internal/proxy/file_names.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/stackus/goht/internal/protocol" 7 | ) 8 | 9 | // toGohtURI converts a Goht Go URI to a Goht URI. 10 | // 11 | // (e.g. "file:///path/to/file.goht.go" -> "file:///path/to/file.goht") 12 | func toGohtURI(uri protocol.DocumentURI) (bool, protocol.DocumentURI) { 13 | if !isGohtGoURI(uri) { 14 | return false, "" 15 | } 16 | return true, uri[:len(uri)-3] 17 | } 18 | 19 | // toGohtGoURI converts a Goht URI to a Goht Go URI. 20 | // 21 | // (e.g. "file:///path/to/file.goht" -> "file:///path/to/file.goht.go") 22 | func toGohtGoURI(uri protocol.DocumentURI) (bool, protocol.DocumentURI) { 23 | if !isGohtURI(uri) { 24 | return false, "" 25 | } 26 | return true, uri + ".go" 27 | } 28 | 29 | func isGohtURI(uri protocol.DocumentURI) bool { 30 | return strings.HasSuffix(string(uri), ".goht") 31 | } 32 | 33 | func isGohtGoURI(uri protocol.DocumentURI) bool { 34 | return strings.HasSuffix(string(uri), ".goht.go") 35 | } 36 | -------------------------------------------------------------------------------- /internal/proxy/source_map_cache.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/stackus/goht/compiler" 7 | ) 8 | 9 | type SourceMapCache struct { 10 | sourceMaps map[string]*compiler.SourceMap 11 | mu sync.Mutex 12 | } 13 | 14 | func NewSourceMapCache() *SourceMapCache { 15 | return &SourceMapCache{ 16 | sourceMaps: make(map[string]*compiler.SourceMap), 17 | } 18 | } 19 | 20 | func (smc *SourceMapCache) Set(uri string, sourceMap *compiler.SourceMap) { 21 | smc.mu.Lock() 22 | defer smc.mu.Unlock() 23 | smc.sourceMaps[uri] = sourceMap 24 | } 25 | 26 | func (smc *SourceMapCache) Get(uri string) (sourceMap *compiler.SourceMap, ok bool) { 27 | smc.mu.Lock() 28 | defer smc.mu.Unlock() 29 | sourceMap, ok = smc.sourceMaps[uri] 30 | return 31 | } 32 | 33 | func (smc *SourceMapCache) Delete(uri string) { 34 | smc.mu.Lock() 35 | defer smc.mu.Unlock() 36 | delete(smc.sourceMaps, uri) 37 | } 38 | -------------------------------------------------------------------------------- /version.go: -------------------------------------------------------------------------------- 1 | package goht 2 | 3 | import ( 4 | _ "embed" 5 | "strings" 6 | ) 7 | 8 | //go:embed .version 9 | var version string 10 | 11 | func Version() string { 12 | return strings.TrimSpace(strings.TrimPrefix(version, "VERSION=")) 13 | } 14 | --------------------------------------------------------------------------------