Skip to content

Commit 2d5c0c0

Browse files
authored
Add warnings when a codeblock tries to use an unsupported language (#377)
* Add warnings when a codeblock tries to use an unsupported language * dotnet format * don't warn of language is empty
1 parent 6162b10 commit 2d5c0c0

File tree

5 files changed

+228
-2
lines changed

5 files changed

+228
-2
lines changed

src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,11 @@ public override bool Close(BlockProcessor processor, Block block)
8484
"console" => "json",
8585
"console-response" => "json",
8686
"console-result" => "json",
87-
"sh" => "bash",
88-
"yml" => "yaml",
8987
"terminal" => "bash",
9088
_ => codeBlock.Language
9189
};
90+
if (!string.IsNullOrEmpty(codeBlock.Language) && !CodeBlock.Languages.Contains(codeBlock.Language))
91+
codeBlock.EmitWarning($"Unknown language: {codeBlock.Language}");
9292

9393
var lines = codeBlock.Lines;
9494
var callOutIndex = 0;
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
5+
namespace Elastic.Markdown.Myst.CodeBlocks;
6+
7+
public static class CodeBlock
8+
{
9+
private static readonly IReadOnlyDictionary<string, string> LanguageMapping = new Dictionary<string, string>
10+
{
11+
{ "1c", "" }, // 1C
12+
{ "abnf", "" }, // ABNF
13+
{ "accesslog", "" }, // Access logs
14+
{ "ada", "" }, // Ada
15+
{ "arduino", "ino" }, // Arduino (C++ w/Arduino libs)
16+
{ "armasm", "arm" }, // ARM assembler
17+
{ "avrasm", "" }, // AVR assembler
18+
{ "actionscript", "as" }, // ActionScript
19+
{ "angelscript", "asc" }, // AngelScript
20+
{ "apache", "apacheconf" }, // Apache
21+
{ "applescript", "osascript" }, // AppleScript
22+
{ "arcade", "" }, // Arcade
23+
{ "asciidoc", "adoc" }, // AsciiDoc
24+
{ "aspectj", "" }, // AspectJ
25+
{ "autohotkey", "" }, // AutoHotkey
26+
{ "autoit", "" }, // AutoIt
27+
{ "awk", "mawk, nawk, gawk" }, // Awk
28+
{ "bash", "sh, zsh" }, // Bash
29+
{ "basic", "" }, // Basic
30+
{ "bnf", "" }, // BNF
31+
{ "brainfuck", "bf" }, // Brainfuck
32+
{ "csharp", "cs" }, // C#
33+
{ "c", "h" }, // C
34+
{ "cpp", "hpp, cc, hh, c++, h++, cxx, hxx" }, // C++
35+
{ "cal", "" }, // C/AL
36+
{ "cos", "cls" }, // Cache Object Script
37+
{ "cmake", "cmake.in" }, // CMake
38+
{ "coq", "" }, // Coq
39+
{ "csp", "" }, // CSP
40+
{ "css", "" }, // CSS
41+
{ "capnproto", "capnp" }, // Cap’n Proto
42+
{ "clojure", "clj" }, // Clojure
43+
{ "coffeescript", "coffee, cson, iced" }, // CoffeeScript
44+
{ "crmsh", "crm, pcmk" }, // Crmsh
45+
{ "crystal", "cr" }, // Crystal
46+
{ "d", "" }, // D
47+
{ "dart", "" }, // Dart
48+
{ "dpr", "dfm, pas, pascal" }, // Delphi
49+
{ "diff", "patch" }, // Diff
50+
{ "django", "jinja" }, // Django
51+
{ "dns", "zone, bind" }, // DNS Zone file
52+
{ "dockerfile", "docker" }, // Dockerfile
53+
{ "dos", "bat, cmd" }, // DOS
54+
{ "dsconfig", "" }, // dsconfig
55+
{ "dts", "" }, // DTS (Device Tree)
56+
{ "dust", "dst" }, // Dust
57+
{ "ebnf", "" }, // EBNF
58+
{ "elixir", "" }, // Elixir
59+
{ "elm", "" }, // Elm
60+
{ "erlang", "erl" }, // Erlang
61+
{ "excel", "xls, xlsx" }, // Excel
62+
{ "fsharp", "fs, fsx, fsi, fsscript" }, // F#
63+
{ "fix", "" }, // FIX
64+
{ "fortran", "f90, f95" }, // Fortran
65+
{ "gcode", "nc" }, // G-Code
66+
{ "gams", "gms" }, // Gams
67+
{ "gauss", "gss" }, // GAUSS
68+
{ "gherkin", "" }, // Gherkin
69+
{ "go", "golang" }, // Go
70+
{ "golo", "gololang" }, // Golo
71+
{ "gradle", "" }, // Gradle
72+
{ "graphql", "gql" }, // GraphQL
73+
{ "groovy", "" }, // Groovy
74+
{ "xml", "html, xhtml, rss, atom, xjb, xsd, xsl, plist, svg" }, // HTML, XML
75+
{ "http", "https" }, // HTTP
76+
{ "haml", "" }, // Haml
77+
{ "handlebars", "hbs, html.hbs, html.handlebars" }, // Handlebars
78+
{ "haskell", "hs" }, // Haskell
79+
{ "haxe", "hx" }, // Haxe
80+
{ "hy", "hylang" }, // Hy
81+
{ "ini", "toml" }, // Ini, TOML
82+
{ "inform7", "i7" }, // Inform7
83+
{ "irpf90", "" }, // IRPF90
84+
{ "json", "jsonc" }, // JSON
85+
{ "java", "jsp" }, // Java
86+
{ "javascript", "js, jsx" }, // JavaScript
87+
{ "julia", "jl" }, // Julia
88+
{ "julia-repl", "" }, // Julia REPL
89+
{ "kotlin", "kt" }, // Kotlin
90+
{ "tex", "" }, // LaTeX
91+
{ "leaf", "" }, // Leaf
92+
{ "lasso", "ls, lassoscript" }, // Lasso
93+
{ "less", "" }, // Less
94+
{ "ldif", "" }, // LDIF
95+
{ "lisp", "" }, // Lisp
96+
{ "livecodeserver", "" }, // LiveCode Server
97+
{ "livescript", "ls" }, // LiveScript
98+
{ "lua", "pluto" }, // Lua
99+
{ "makefile", "mk, mak, make" }, // Makefile
100+
{ "markdown", "md, mkdown, mkd" }, // Markdown
101+
{ "mathematica", "mma, wl" }, // Mathematica
102+
{ "matlab", "" }, // Matlab
103+
{ "maxima", "" }, // Maxima
104+
{ "mel", "" }, // Maya Embedded Language
105+
{ "mercury", "" }, // Mercury
106+
{ "mips", "mipsasm" }, // MIPS Assembler
107+
{ "mizar", "" }, // Mizar
108+
{ "mojolicious", "" }, // Mojolicious
109+
{ "monkey", "" }, // Monkey
110+
{ "moonscript", "moon" }, // Moonscript
111+
{ "n1ql", "" }, // N1QL
112+
{ "nsis", "" }, // NSIS
113+
{ "nginx", "nginxconf" }, // Nginx
114+
{ "nim", "nimrod" }, // Nim
115+
{ "nix", "" }, // Nix
116+
{ "ocaml", "ml" }, // OCaml
117+
{ "objectivec", "mm, objc, obj-c, obj-c++, objective-c++" }, // Objective C
118+
{ "openscad", "scad" }, // OpenSCAD
119+
{ "ruleslanguage", "" }, // Oracle Rules Language
120+
{ "oxygene", "" }, // Oxygene
121+
{ "pf", "pf.conf" }, // PF
122+
{ "php", "" }, // PHP
123+
{ "parser3", "" }, // Parser3
124+
{ "perl", "pl, pm" }, // Perl
125+
{ "plaintext", "txt, text" }, // Plaintext
126+
{ "pony", "" }, // Pony
127+
{ "pgsql", "postgres, postgresql" }, // PostgreSQL & PL/pgSQL
128+
{ "powershell", "ps, ps1" }, // PowerShell
129+
{ "processing", "" }, // Processing
130+
{ "prolog", "" }, // Prolog
131+
{ "properties", "" }, // Properties
132+
{ "proto", "protobuf" }, // Protocol Buffers
133+
{ "puppet", "pp" }, // Puppet
134+
{ "python", "py, gyp" }, // Python
135+
{ "profile", "" }, // Python profiler results
136+
{ "python-repl", "pycon" }, // Python REPL
137+
{ "k", "kdb" }, // Q
138+
{ "qml", "" }, // QML
139+
{ "r", "" }, // R
140+
{ "reasonml", "re" }, // ReasonML
141+
{ "rib", "" }, // RenderMan RIB
142+
{ "rsl", "" }, // RenderMan RSL
143+
{ "graph", "instances" }, // Roboconf
144+
{ "ruby", "rb, gemspec, podspec, thor, irb" }, // Ruby
145+
{ "rust", "rs" }, // Rust
146+
{ "SAS", "sas" }, // SAS
147+
{ "scss", "" }, // SCSS
148+
{ "sql", "" }, // SQL
149+
{ "p21", "step, stp" }, // STEP Part 21
150+
{ "scala", "" }, // Scala
151+
{ "scheme", "" }, // Scheme
152+
{ "scilab", "sci" }, // Scilab
153+
{ "shell", "console" }, // Shell
154+
{ "smali", "" }, // Smali
155+
{ "smalltalk", "st" }, // Smalltalk
156+
{ "sml", "ml" }, // SML
157+
{ "stan", "stanfuncs" }, // Stan
158+
{ "stata", "" }, // Stata
159+
{ "stylus", "styl" }, // Stylus
160+
{ "subunit", "" }, // SubUnit
161+
{ "svelte", "" }, // Svelte highlight.svelte
162+
{ "swift", "" }, // Swift
163+
{ "tcl", "tk" }, // Tcl
164+
{ "tap", "" }, // Test Anything Protocol
165+
{ "thrift", "" }, // Thrift
166+
{ "toit", "" }, // Toit toit-highlight
167+
{ "tp", "" }, // TP
168+
{ "twig", "craftcms" }, // Twig
169+
{ "typescript", "ts, tsx, mts, cts" }, // TypeScript
170+
{ "vbnet", "vb" }, // VB.Net
171+
{ "vbscript", "vbs" }, // VBScript
172+
{ "vhdl", "" }, // VHDL
173+
{ "vala", "" }, // Vala
174+
{ "verilog", "v" }, // Verilog
175+
{ "vim", "" }, // Vim Script
176+
{ "axapta", "x++" }, // X++
177+
{ "x86asm", "" }, // x86 Assembly
178+
{ "xl", "tao" }, // XL
179+
{ "xquery", "xpath, xq, xqm" }, // XQuery
180+
{ "yml", "yaml" }, // YAML
181+
{ "zephir", "zep" }, // Zephir
182+
};
183+
184+
185+
public static HashSet<string> Languages => new(
186+
LanguageMapping.Keys
187+
.Concat(LanguageMapping.Values
188+
.SelectMany(v => v.Split(',').Select(a => a.Trim()))
189+
.Where(v => !string.IsNullOrWhiteSpace(v))
190+
)
191+
, StringComparer.OrdinalIgnoreCase
192+
);
193+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
5+
module ``block elements``.``code blocks``
6+
7+
open Xunit
8+
open authoring
9+
10+
type ``warns on invalid language`` () =
11+
static let markdown = Setup.Markdown """
12+
```not-a-valid-language
13+
```
14+
"""
15+
16+
[<Fact>]
17+
let ``validate HTML: generates link and alt attr`` () =
18+
markdown |> hasWarning "Unknown language: not-a-valid-language"

tests/authoring/Framework/ErrorCollectorAssertions.fs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,14 @@ module DiagnosticsCollectorAssertions =
2929
|> List.ofArray
3030
let message = errorDiagnostics.FirstOrDefault().Message
3131
test <@ message.Contains(expected) @>
32+
33+
[<DebuggerStepThrough>]
34+
let hasWarning (expected: string) (actual: Lazy<GeneratorResults>) =
35+
let actual = actual.Value
36+
actual.Context.Collector.Warnings |> shouldBeGreaterThan 0
37+
let errorDiagnostics = actual.Context.Collector.Diagnostics
38+
.Where(fun d -> d.Severity = Severity.Warning)
39+
.ToArray()
40+
|> List.ofArray
41+
let message = errorDiagnostics.FirstOrDefault().Message
42+
test <@ message.Contains(expected) @>

tests/authoring/authoring.fsproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,8 @@
4444
<Compile Include="Generator\LinkReferenceFile.fs" />
4545
</ItemGroup>
4646

47+
<ItemGroup>
48+
<Compile Include="Blocks\CodeBlocks\CodeBlocks.fs" />
49+
</ItemGroup>
50+
4751
</Project>

0 commit comments

Comments
 (0)