internal/commentpolicy/syntax.go
1
package commentpolicy
3
import (
4
"path/filepath"
5
"strings"
6
)
8
type blockDelimiter struct {
9
open string
10
close string
11
}
13
type commentSyntax struct {
14
line []string
15
block []blockDelimiter
16
directives []string
17
}
19
var cFamily = commentSyntax{
20
line: []string{"//"},
21
block: []blockDelimiter{{"/*", "*/"}},
22
}
24
var hashOnly = commentSyntax{line: []string{"#"}}
26
func withDirectives(base commentSyntax, directives ...string) commentSyntax {
27
base.directives = directives
28
return base
29
}
31
//go:generate go run ./gen -out ../../docs/reference/languages.md
33
var syntaxByExtension = map[string]commentSyntax{
34
".yaml": withDirectives(hashOnly, "yaml-language-server:", "yamllint", "renovate:", "noqa",
35
"zizmor:", "x-release-please", "checkov:", "kubeconform", "helm-docs"),
36
".yml": withDirectives(hashOnly, "yaml-language-server:", "yamllint", "renovate:", "noqa",
37
"zizmor:", "x-release-please", "checkov:", "kubeconform", "helm-docs"),
38
".toml": withDirectives(hashOnly, "schema:", "renovate:", "x-release-please"),
39
".ini": hashOnly,
40
".cfg": hashOnly,
41
".conf": hashOnly,
42
".env": hashOnly,
44
".py": withDirectives(hashOnly, "type:", "noqa", "pylint:", "mypy:", "ruff:", "fmt:", "pragma:", "-*-"),
45
".pyi": withDirectives(hashOnly, "type:", "noqa", "pylint:"),
47
".sh": withDirectives(hashOnly, "shellcheck", "!"),
48
".bash": withDirectives(hashOnly, "shellcheck", "!"),
49
".zsh": withDirectives(hashOnly, "shellcheck", "!"),
50
".fish": hashOnly,
52
".rb": withDirectives(hashOnly, "frozen_string_literal:", "rubocop:", "encoding:"),
53
".pl": hashOnly,
54
".r": hashOnly,
55
".tf": hashOnly,
56
".hcl": hashOnly,
58
".js": withDirectives(cFamily, "eslint", "@ts-", "prettier-", "istanbul ", "jshint", "global ", "jsx"),
59
".jsx": withDirectives(cFamily, "eslint", "@ts-", "prettier-"),
60
".mjs": withDirectives(cFamily, "eslint", "@ts-", "prettier-"),
61
".cjs": withDirectives(cFamily, "eslint", "@ts-", "prettier-"),
62
".ts": withDirectives(cFamily,
63
"eslint", "@ts-", "prettier-", "tslint:", "/ <reference", "istanbul "),
64
".tsx": withDirectives(cFamily, "eslint", "@ts-", "prettier-", "tslint:"),
65
".jsonc": cFamily,
67
".rs": withDirectives(cFamily, "!", "/", "rustfmt::", "clippy::", "allow(", "cfg("),
69
".c": withDirectives(cFamily, "NOLINT", "clang-format", "cppcheck-suppress"),
70
".h": withDirectives(cFamily, "NOLINT", "clang-format", "cppcheck-suppress"),
71
".cc": withDirectives(cFamily, "NOLINT", "clang-format", "cppcheck-suppress"),
72
".cpp": withDirectives(cFamily, "NOLINT", "clang-format", "cppcheck-suppress"),
73
".cxx": withDirectives(cFamily, "NOLINT", "clang-format"),
74
".hpp": withDirectives(cFamily, "NOLINT", "clang-format"),
75
".hh": withDirectives(cFamily, "NOLINT", "clang-format"),
77
".java": withDirectives(cFamily, "CHECKSTYLE", "NOPMD", "SUPPRESS"),
78
".kt": withDirectives(cFamily, "ktlint-", "noinspection"),
79
".kts": cFamily,
80
".scala": cFamily,
81
".swift": withDirectives(cFamily, "swiftlint:", "swift-format-"),
82
".cs": cFamily,
83
".php": withDirectives(cFamily, "phpcs:", "@phpstan-"),
84
".dart": withDirectives(cFamily, "ignore:", "ignore_for_file:"),
85
".go2": cFamily,
86
".zig": cFamily,
87
".proto": cFamily,
88
".css": {block: []blockDelimiter{{"/*", "*/"}}},
89
".scss": cFamily,
90
".less": cFamily,
92
".lua": withDirectives(commentSyntax{
93
line: []string{"--"},
94
block: []blockDelimiter{{"--[[", "]]"}},
95
}, "luacheck:", "@"),
96
".sql": withDirectives(commentSyntax{
97
line: []string{"--"},
98
block: []blockDelimiter{{"/*", "*/"}},
99
}, "noqa"),
100
".hs": {line: []string{"--"}, block: []blockDelimiter{{"{-", "-}"}}},
101
".ex": hashOnly,
102
".mod": withDirectives(cFamily, "indirect"),
103
".vim": {line: []string{`"`}},
104
}
106
var fallbackSyntax = commentSyntax{
107
line: []string{"#", "//"},
108
}
110
var uncommentableExtensions = map[string]bool{
111
".md": true, ".markdown": true, ".mdx": true, ".mdc": true,
112
".rst": true, ".txt": true, ".adoc": true,
113
".json": true, ".lock": true, ".sum": true,
114
".csv": true, ".tsv": true,
115
".svg": true, ".html": true, ".htm": true, ".xml": true,
116
}
118
func syntaxFor(file string) (commentSyntax, bool) {
119
extension := strings.ToLower(filepath.Ext(file))
120
if uncommentableExtensions[extension] {
121
return commentSyntax{}, false
122
}
123
if known, found := syntaxByExtension[extension]; found {
124
return known, true
125
}
126
if extension == "" && looksLikeScript(file) {
127
return withDirectives(hashOnly, "shellcheck", "!", "syntax=", "escape="), true
128
}
129
return fallbackSyntax, true
130
}
132
var scriptFilenames = []string{
133
"makefile", "dockerfile", "justfile", "rakefile", "gemfile", "brewfile", "vagrantfile",
134
}
136
func looksLikeScript(file string) bool {
137
if hasScriptFilename(file) {
138
return true
139
}
140
base := strings.ToLower(filepath.Base(file))
141
return strings.HasPrefix(base, ".") && !strings.Contains(base[1:], ".")
142
}