├── .envrc ├── .gitignore ├── .vscode └── extensions.json ├── src ├── SyntaxHighlight │ ├── Language │ │ ├── Type.elm │ │ ├── NoLang.elm │ │ ├── Xml.elm │ │ ├── Helpers.elm │ │ ├── Json.elm │ │ ├── Nix.elm │ │ ├── Python.elm │ │ ├── Kotlin.elm │ │ ├── Sql.elm │ │ ├── Go.elm │ │ ├── Javascript.elm │ │ └── Elm.elm │ ├── Theme.elm │ ├── Theme │ │ ├── GitHub.elm │ │ ├── OneDark.elm │ │ ├── Monokai.elm │ │ └── Type.elm │ ├── Line.elm │ ├── Line │ │ └── Helpers.elm │ ├── Style.elm │ └── View.elm └── SyntaxHighlight.elm ├── demo ├── index.html ├── README.md ├── elm.json └── themes-page │ ├── elm.json │ ├── make-themes.js │ ├── Main.elm │ └── themes-template.html ├── elm.json ├── flake.lock ├── tests └── Language │ ├── Go.elm │ ├── Nix.elm │ ├── Sql.elm │ ├── NoLang.elm │ ├── Kotlin.elm │ ├── Python.elm │ ├── Json.elm │ ├── Xml.elm │ ├── Javascript.elm │ ├── Elm.elm │ └── Css.elm ├── CONTRIBUTING.md ├── README.md ├── CHANGELOG.md ├── .github └── workflows │ └── test-build-deploy.yml ├── flake.nix ├── themes.md └── LICENSE /.envrc: -------------------------------------------------------------------------------- 1 | use flake -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | elm-stuff 2 | .DS_Store 3 | .idea 4 | demo/elm-themes.js 5 | demo/themes.html 6 | demo/build 7 | elm.js 8 | node_modules 9 | .direnv 10 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "mkhl.direnv", 4 | "jnoortheen.nix-ide", 5 | "elmtooling.elm-ls-vscode" 6 | ] 7 | } -------------------------------------------------------------------------------- /src/SyntaxHighlight/Language/Type.elm: -------------------------------------------------------------------------------- 1 | module SyntaxHighlight.Language.Type exposing (Syntax(..), Token) 2 | 3 | 4 | type alias Token a = 5 | ( Syntax a, String ) 6 | 7 | 8 | type Syntax a 9 | = Normal 10 | | Comment 11 | | LineBreak 12 | | C a 13 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 |Loading...
12 | 13 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /demo/README.md: -------------------------------------------------------------------------------- 1 | # Elm Syntax Highlight demo 2 | 3 | All commands must be run in this folder (`demo`). 4 | 5 | ## Development 6 | 7 | ```sh 8 | elm reactor 9 | ``` 10 | 11 | Open your browser at http://localhost:8000/ and navigate to `src/Main.elm`. 12 | 13 | 14 | ## Run standalone 15 | 16 | Remove `"../src"` at the `source-directories` entry in `elm.json` file and install the package running: 17 | 18 | ```sh 19 | elm install pablohirafuji/elm-syntax-highlight 20 | ``` 21 | -------------------------------------------------------------------------------- /elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "package", 3 | "name": "pablohirafuji/elm-syntax-highlight", 4 | "summary": "Syntax highlighting in Elm", 5 | "license": "Apache-2.0", 6 | "version": "3.7.1", 7 | "exposed-modules": [ 8 | "SyntaxHighlight" 9 | ], 10 | "elm-version": "0.19.0 <= v < 0.20.0", 11 | "dependencies": { 12 | "elm/core": "1.0.0 <= v < 2.0.0", 13 | "elm/html": "1.0.0 <= v < 2.0.0", 14 | "elm/parser": "1.1.0 <= v < 2.0.0", 15 | "elm/regex": "1.0.0 <= v < 2.0.0" 16 | }, 17 | "test-dependencies": { 18 | "elm-explorations/test": "2.2.0 <= v < 3.0.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1741181794, 6 | "narHash": "sha256-KaY79mvudbMfufn3FW2Yzx/hiF848gj5y1rMjS/U8fA=", 7 | "owner": "NixOS", 8 | "repo": "nixpkgs", 9 | "rev": "bb1b98cea759f3739d88b1a6a0d379d61aaf5573", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "NixOS", 14 | "ref": "release-24.11", 15 | "repo": "nixpkgs", 16 | "type": "github" 17 | } 18 | }, 19 | "root": { 20 | "inputs": { 21 | "nixpkgs": "nixpkgs" 22 | } 23 | } 24 | }, 25 | "root": "root", 26 | "version": 7 27 | } 28 | -------------------------------------------------------------------------------- /demo/elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "application", 3 | "source-directories": [ 4 | "src", 5 | "../src" 6 | ], 7 | "elm-version": "0.19.1", 8 | "dependencies": { 9 | "direct": { 10 | "elm/browser": "1.0.2", 11 | "elm/core": "1.0.5", 12 | "elm/html": "1.0.0", 13 | "elm/json": "1.1.3", 14 | "elm/parser": "1.1.0", 15 | "elm/regex": "1.0.0", 16 | "elm/time": "1.0.0" 17 | }, 18 | "indirect": { 19 | "elm/url": "1.0.0", 20 | "elm/virtual-dom": "1.0.3" 21 | } 22 | }, 23 | "test-dependencies": { 24 | "direct": {}, 25 | "indirect": {} 26 | } 27 | } -------------------------------------------------------------------------------- /demo/themes-page/elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "application", 3 | "source-directories": [ 4 | ".", 5 | "../../src" 6 | ], 7 | "elm-version": "0.19.1", 8 | "dependencies": { 9 | "direct": { 10 | "elm/browser": "1.0.0", 11 | "elm/core": "1.0.0", 12 | "elm/html": "1.0.0", 13 | "elm/json": "1.0.0", 14 | "elm/parser": "1.1.0", 15 | "elm/regex": "1.0.0", 16 | "elm/time": "1.0.0" 17 | }, 18 | "indirect": { 19 | "elm/url": "1.0.0", 20 | "elm/virtual-dom": "1.0.2" 21 | } 22 | }, 23 | "test-dependencies": { 24 | "direct": {}, 25 | "indirect": {} 26 | } 27 | } -------------------------------------------------------------------------------- /src/SyntaxHighlight/Theme.elm: -------------------------------------------------------------------------------- 1 | module SyntaxHighlight.Theme exposing 2 | ( all 3 | , gitHub 4 | , monokai 5 | , oneDark 6 | ) 7 | 8 | import SyntaxHighlight.Theme.GitHub as GitHub 9 | import SyntaxHighlight.Theme.Monokai as Monokai 10 | import SyntaxHighlight.Theme.OneDark as OneDark 11 | 12 | 13 | 14 | -- Add all themes name and code here to show in the Demo and Themes page 15 | 16 | 17 | all : List ( String, String ) 18 | all = 19 | [ ( "Monokai", monokai ) 20 | , ( "GitHub", gitHub ) 21 | , ( "One Dark", oneDark ) 22 | ] 23 | 24 | 25 | monokai : String 26 | monokai = 27 | Monokai.css 28 | 29 | 30 | gitHub : String 31 | gitHub = 32 | GitHub.css 33 | 34 | 35 | oneDark : String 36 | oneDark = 37 | OneDark.css 38 | -------------------------------------------------------------------------------- /demo/themes-page/make-themes.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"); 2 | var path = require("path"); 3 | var templateFile = fs.readFileSync(path.resolve(__dirname + "/themes-template.html")); 4 | var templateStr = templateFile.toString(); 5 | 6 | 7 | var elm = require("./elm-themes").Elm.Main.init(); 8 | elm.ports.themesList.subscribe(function(themeList) { 9 | var themeListHtml = "" 10 | themeList.forEach(function(element, index, array) { 11 | themeListHtml += "" + element.content + "Hero
" <| 16 | Ok 17 | [ ( Normal, "<" ) 18 | , ( C Tag, "p" ) 19 | , ( Normal, " " ) 20 | , ( C Attribute, "class" ) 21 | , ( Normal, "=" ) 22 | , ( C AttributeValue, "\"" ) 23 | , ( C AttributeValue, "hero" ) 24 | , ( C AttributeValue, "\"" ) 25 | , ( Normal, ">Hero" ) 26 | , ( Normal, "" ) 27 | , ( C Tag, "p" ) 28 | , ( Normal, ">" ) 29 | ] 30 | , equalTest "Comment" "" ) 35 | , ( LineBreak, "\n" ) 36 | , ( Comment, "" ) 40 | , ( LineBreak, "\n" ) 41 | , ( Normal, "" ) 42 | , ( C Tag, "script" ) 43 | ] 44 | , equalTest "Incomplete tag" " 72 | Parser.run Xml.toRevTokens fuzzStr 73 | |> Result.map 74 | (List.reverse 75 | >> List.map Tuple.second 76 | >> String.concat 77 | ) 78 | |> equal (Ok fuzzStr) 79 | |> onFail ("Resulting error string: \"" ++ fuzzStr ++ "\"") 80 | ] 81 | 82 | 83 | equalTest : String -> String -> Result (List Parser.DeadEnd) (List ( T.Syntax Xml.Syntax, String )) -> Test 84 | equalTest testName testStr testResult = 85 | describe testName 86 | [ test "Syntax equality" <| 87 | \() -> 88 | Parser.run Xml.toRevTokens testStr 89 | |> Result.map List.reverse 90 | |> equal testResult 91 | , test "String equality" <| 92 | \() -> 93 | Parser.run Xml.toRevTokens testStr 94 | |> Result.map 95 | (List.reverse 96 | >> List.map Tuple.second 97 | >> String.concat 98 | ) 99 | |> equal (Ok testStr) 100 | ] 101 | -------------------------------------------------------------------------------- /src/SyntaxHighlight/Style.elm: -------------------------------------------------------------------------------- 1 | module SyntaxHighlight.Style exposing (Color(..), Required(..), RequiredStyles, Style, backgroundColor, bold, colorToCss, emptyIfFalse, italic, noEmphasis, styleToCss, textColor, toCss, toCssClass) 2 | 3 | {- 4 | The common uses of the styles are the following: 5 | 6 | - **Default**: Default style 7 | - **Comment**: Comment 8 | - **Style1**: Number 9 | - **Style2**: Literal string, attribute value 10 | - **Style3**: Keyword, tag, operator symbol (=+-*/...) 11 | - **Style4**: Keyword, group symbol ({}(),) 12 | - **Style5**: Function, attribute name 13 | - **Style6**: Literal keyword 14 | - **Style7**: Argument, parameter 15 | -} 16 | 17 | 18 | type Required 19 | = Default 20 | | Comment 21 | | Style1 22 | | Style2 23 | | Style3 24 | | Style4 25 | | Style5 26 | | Style6 27 | | Style7 28 | 29 | 30 | type alias RequiredStyles = 31 | { default : Style 32 | , highlight : Style 33 | , addition : Style 34 | , deletion : Style 35 | , comment : Style 36 | , style1 : Style 37 | , style2 : Style 38 | , style3 : Style 39 | , style4 : Style 40 | , style5 : Style 41 | , style6 : Style 42 | , style7 : Style 43 | } 44 | 45 | 46 | type alias Style = 47 | { isBold : Bool 48 | , isItalic : Bool 49 | , isUnderline : Bool 50 | , text : Color 51 | , background : Color 52 | } 53 | 54 | 55 | type Color 56 | = DefaultColor 57 | | Hex String 58 | | Rgb Int Int Int 59 | | Rgba Int Int Int Float 60 | 61 | 62 | noEmphasis : Color -> Color -> Style 63 | noEmphasis text background = 64 | { isBold = False 65 | , isItalic = False 66 | , isUnderline = False 67 | , text = text 68 | , background = background 69 | } 70 | 71 | 72 | textColor : Color -> Style 73 | textColor text = 74 | { isBold = False 75 | , isItalic = False 76 | , isUnderline = False 77 | , text = text 78 | , background = DefaultColor 79 | } 80 | 81 | 82 | backgroundColor : Color -> Style 83 | backgroundColor background = 84 | { isBold = False 85 | , isItalic = False 86 | , isUnderline = False 87 | , text = DefaultColor 88 | , background = background 89 | } 90 | 91 | 92 | italic : Style -> Style 93 | italic style = 94 | { style | isItalic = True } 95 | 96 | 97 | bold : Style -> Style 98 | bold style = 99 | { style | isBold = True } 100 | 101 | 102 | 103 | -- To Css string helpers 104 | 105 | 106 | toCss : List ( String, Style ) -> String 107 | toCss classes = 108 | List.map toCssClass classes 109 | |> String.concat 110 | 111 | 112 | toCssClass : ( String, Style ) -> String 113 | toCssClass ( selectors, style ) = 114 | if String.isEmpty selectors then 115 | "" 116 | 117 | else 118 | selectors ++ " {" ++ styleToCss style ++ "}" 119 | 120 | 121 | styleToCss : Style -> String 122 | styleToCss { isBold, isItalic, isUnderline, text, background } = 123 | String.concat 124 | [ emptyIfFalse isBold "font-weight: bold;" 125 | , emptyIfFalse isItalic "font-style: italic;" 126 | , emptyIfFalse isUnderline "text-decoration: underline;" 127 | , colorToCss "color: " text 128 | , colorToCss "background: " background 129 | ] 130 | 131 | 132 | emptyIfFalse : Bool -> String -> String 133 | emptyIfFalse bool str = 134 | if bool then 135 | str 136 | 137 | else 138 | "" 139 | 140 | 141 | colorToCss : String -> Color -> String 142 | colorToCss property color = 143 | case color of 144 | DefaultColor -> 145 | "" 146 | 147 | Hex hex -> 148 | property ++ hex ++ ";" 149 | 150 | Rgb r g b -> 151 | String.concat 152 | [ property 153 | , "rgb(" 154 | , String.fromInt r 155 | , ", " 156 | , String.fromInt g 157 | , "," 158 | , String.fromInt b 159 | , ");" 160 | ] 161 | 162 | Rgba r g b a -> 163 | String.concat 164 | [ property 165 | , "rgba(" 166 | , String.fromInt r 167 | , ", " 168 | , String.fromInt g 169 | , "," 170 | , String.fromInt b 171 | , ", " 172 | , String.fromFloat a 173 | , ");" 174 | ] 175 | -------------------------------------------------------------------------------- /tests/Language/Javascript.elm: -------------------------------------------------------------------------------- 1 | module Language.Javascript exposing (suite) 2 | 3 | import Expect exposing (Expectation, equal, equalLists, onFail) 4 | import Fuzz exposing (string) 5 | import Parser 6 | import Result exposing (Result(..)) 7 | import SyntaxHighlight.Language.Javascript as JS exposing (Syntax(..), toRevTokens) 8 | import SyntaxHighlight.Language.Type as T exposing (Syntax(..)) 9 | import Test exposing (..) 10 | 11 | 12 | suite : Test 13 | suite = 14 | describe "Javascript Language Test Suite" 15 | [ equalTest "Function* declaration" 16 | "function* anotherGenerator(i) {\n yield i + 1;\n yield i + 2;\n yield i + 3;\n}" 17 | <| 18 | Ok [ ( C DeclarationKeyword, "function" ), ( C Keyword, "*" ), ( Normal, " " ), ( C Function, "anotherGenerator" ), ( Normal, "(" ), ( C Param, "i" ), ( Normal, ")" ), ( Normal, " " ), ( Normal, "{" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C Keyword, "yield" ), ( Normal, " " ), ( Normal, "i" ), ( Normal, " " ), ( C Keyword, "+" ), ( Normal, " " ), ( C Number, "1" ), ( Normal, ";" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C Keyword, "yield" ), ( Normal, " " ), ( Normal, "i" ), ( Normal, " " ), ( C Keyword, "+" ), ( Normal, " " ), ( C Number, "2" ), ( Normal, ";" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C Keyword, "yield" ), ( Normal, " " ), ( Normal, "i" ), ( Normal, " " ), ( C Keyword, "+" ), ( Normal, " " ), ( C Number, "3" ), ( Normal, ";" ), ( LineBreak, "\n" ), ( Normal, "}" ) ] 19 | , equalTest "Class declaration" 20 | "class Pessoa extends Humano {\n constructor(nome, peso, altura) {\n super(peso, altura);\n this.nome = nome;\n }\n}" 21 | <| 22 | Ok [ ( C DeclarationKeyword, "class" ), ( Normal, " " ), ( C Function, "Pessoa" ), ( Normal, " " ), ( C Keyword, "extends" ), ( Normal, " " ), ( C ClassExtends, "Humano" ), ( Normal, " " ), ( Normal, "{" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C Function, "constructor" ), ( Normal, "(" ), ( C Param, "nome" ), ( Normal, "," ), ( Normal, " " ), ( C Param, "peso" ), ( Normal, "," ), ( Normal, " " ), ( C Param, "altura" ), ( Normal, ")" ), ( Normal, " " ), ( Normal, "{" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C Param, "super" ), ( Normal, "(" ), ( Normal, "peso" ), ( Normal, "," ), ( Normal, " " ), ( Normal, "altura" ), ( Normal, ");" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C Param, "this" ), ( C Keyword, "." ), ( Normal, "nome" ), ( Normal, " " ), ( C Keyword, "=" ), ( Normal, " " ), ( Normal, "nome" ), ( Normal, ";" ), ( LineBreak, "\n" ), ( Normal, " " ), ( Normal, "}" ), ( LineBreak, "\n" ), ( Normal, "}" ) ] 23 | , equalTest "Function" 24 | "function resolveAfter2Seconds(x) {\n return new Promise(resolve => {\n setTimeout(() => {\n resolve(x);\n }, 2000);\n });\n};" 25 | <| 26 | Ok [ ( C DeclarationKeyword, "function" ), ( Normal, " " ), ( C Function, "resolveAfter2Seconds" ), ( Normal, "(" ), ( C Param, "x" ), ( Normal, ")" ), ( Normal, " " ), ( Normal, "{" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C Keyword, "return" ), ( Normal, " " ), ( C Keyword, "new" ), ( Normal, " " ), ( C FunctionEval, "Promise" ), ( Normal, "(" ), ( Normal, "resolve" ), ( Normal, " " ), ( C Keyword, "=>" ), ( Normal, " " ), ( Normal, "{" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C FunctionEval, "setTimeout" ), ( Normal, "(" ), ( Normal, "()" ), ( Normal, " " ), ( C Keyword, "=>" ), ( Normal, " " ), ( Normal, "{" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C FunctionEval, "resolve" ), ( Normal, "(" ), ( Normal, "x" ), ( Normal, ");" ), ( LineBreak, "\n" ), ( Normal, " " ), ( Normal, "}," ), ( Normal, " " ), ( C Number, "2000" ), ( Normal, ");" ), ( LineBreak, "\n" ), ( Normal, " " ), ( Normal, "});" ), ( LineBreak, "\n" ), ( Normal, "};" ) ] 27 | , equalTest "Inline Comment" "// Comment\nvar = id; //Comment\n// Comment" <| 28 | Ok [ ( Comment, "// Comment" ), ( LineBreak, "\n" ), ( C DeclarationKeyword, "var" ), ( Normal, " " ), ( C Keyword, "=" ), ( Normal, " " ), ( Normal, "id" ), ( Normal, ";" ), ( Normal, " " ), ( Comment, "//Comment" ), ( LineBreak, "\n" ), ( Comment, "// Comment" ) ] 29 | , equalTest "Multiline Comment" "/* Comment */\nvar = id; /* Comment */\n/* Comment" <| 30 | Ok [ ( Comment, "/*" ), ( Comment, " Comment " ), ( Comment, "*/" ), ( LineBreak, "\n" ), ( C DeclarationKeyword, "var" ), ( Normal, " " ), ( C Keyword, "=" ), ( Normal, " " ), ( Normal, "id" ), ( Normal, ";" ), ( Normal, " " ), ( Comment, "/*" ), ( Comment, " Comment " ), ( Comment, "*/" ), ( LineBreak, "\n" ), ( Comment, "/*" ), ( Comment, " Comment" ) ] 31 | , fuzz string "Fuzz string" <| 32 | \fuzzStr -> 33 | Parser.run JS.toRevTokens fuzzStr 34 | |> Result.map 35 | (List.reverse 36 | >> List.map Tuple.second 37 | >> String.concat 38 | ) 39 | |> equal (Ok fuzzStr) 40 | |> onFail ("Resulting error string: \"" ++ fuzzStr ++ "\"") 41 | ] 42 | 43 | 44 | equalTest : String -> String -> Result (List Parser.DeadEnd) (List ( T.Syntax JS.Syntax, String )) -> Test 45 | equalTest testName testStr testResult = 46 | describe testName 47 | [ test "Syntax equality" <| 48 | \() -> 49 | Parser.run JS.toRevTokens testStr 50 | |> Result.map List.reverse 51 | |> equal testResult 52 | , test "String equality" <| 53 | \() -> 54 | Parser.run JS.toRevTokens testStr 55 | |> Result.map 56 | (List.reverse 57 | >> List.map Tuple.second 58 | >> String.concat 59 | ) 60 | |> equal (Ok testStr) 61 | ] 62 | -------------------------------------------------------------------------------- /tests/Language/Elm.elm: -------------------------------------------------------------------------------- 1 | module Language.Elm exposing (suite) 2 | 3 | import Expect exposing (Expectation, equal, equalLists, onFail) 4 | import Fuzz exposing (string) 5 | import Parser 6 | import Result exposing (Result(..)) 7 | import SyntaxHighlight.Language.Elm as Elm exposing (Syntax(..), toRevTokens) 8 | import SyntaxHighlight.Language.Type as T exposing (Syntax(..)) 9 | import Test exposing (..) 10 | 11 | 12 | suite : Test 13 | suite = 14 | describe "Elm Language Test Suite" 15 | [ equalTest "Module declaration" moduleDeclarationText moduleDeclarationResult 16 | , equalTest "Port module declaration" ("port " ++ moduleDeclarationText) ([ ( T.C Keyword, "port" ), ( Normal, " " ) ] ++ moduleDeclarationResult) 17 | , equalTest "Import declaration" "import Html.Attributes as Att exposing (Html, classList, (|>))" [ ( C Keyword, "import" ), ( Normal, " " ), ( Normal, "Html.Attributes" ), ( Normal, " " ), ( C Keyword, "as" ), ( Normal, " " ), ( Normal, "Att" ), ( Normal, " " ), ( T.C Keyword, "exposing" ), ( Normal, " " ), ( Normal, "(" ), ( C TypeSignature, "Html" ), ( Normal, "," ), ( Normal, " " ), ( C Function, "classList" ), ( Normal, "," ), ( Normal, " " ), ( C Function, "(|>)" ), ( Normal, ")" ) ] 18 | , equalTest "Function signature" functionSignatureText functionSignatureResult 19 | , equalTest "Port function signature" ("port " ++ functionSignatureText) ([ ( T.C Keyword, "port" ), ( Normal, " " ) ] ++ functionSignatureResult) 20 | , equalTest "Function body" "text str =" [ ( C Function, "text" ), ( Normal, " " ), ( Normal, "str" ), ( Normal, " " ), ( C BasicSymbol, "=" ) ] 21 | , equalTest "Case statement" " case maybe of\n Just str -> str\n Nothing -> str" [ ( Normal, " " ), ( T.C Keyword, "case" ), ( Normal, " " ), ( Normal, "maybe" ), ( Normal, " " ), ( T.C Keyword, "of" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C Capitalized, "Just" ), ( Normal, " " ), ( Normal, "str" ), ( Normal, " " ), ( C BasicSymbol, "->" ), ( Normal, " " ), ( Normal, "str" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C Capitalized, "Nothing" ), ( Normal, " " ), ( C BasicSymbol, "->" ), ( Normal, " " ), ( Normal, "str" ) ] 22 | , equalTest "Numbers" "math = (3+4.453) / 5 * 4.4" [ ( C Function, "math" ), ( Normal, " " ), ( C BasicSymbol, "=" ), ( Normal, " " ), ( C BasicSymbol, "(" ), ( C Number, "3" ), ( C BasicSymbol, "+" ), ( C Number, "4.453" ), ( C BasicSymbol, ")" ), ( Normal, " " ), ( C BasicSymbol, "/" ), ( Normal, " " ), ( C Number, "5" ), ( Normal, " " ), ( C BasicSymbol, "*" ), ( Normal, " " ), ( C Number, "4.4" ) ] 23 | , equalTest "String literal: Single quote" "char = 'c'" [ ( C Function, "char" ), ( Normal, " " ), ( C BasicSymbol, "=" ), ( Normal, " " ), ( C String, "'" ), ( C String, "c" ), ( C String, "'" ) ] 24 | , equalTest "String literal: Double quote" "string = \"hello\"" [ ( C Function, "string" ), ( Normal, " " ), ( C BasicSymbol, "=" ), ( Normal, " " ), ( C String, "\"" ), ( C String, "hello" ), ( C String, "\"" ) ] 25 | , equalTest "String literal: Triple double quote" "string = \"\"\"Great\nString\" with \"\" double quotes\"\"\" finished" [ ( C Function, "string" ), ( Normal, " " ), ( C BasicSymbol, "=" ), ( Normal, " " ), ( C String, "\"\"\"" ), ( C String, "Great" ), ( LineBreak, "\n" ), ( C String, "String" ), ( C String, "\" with " ), ( C String, "\"" ), ( C String, "\" double quotes" ), ( C String, "\"\"\"" ), ( Normal, " " ), ( Normal, "finished" ) ] 26 | , equalTest "Comment: Inline" "function = -- Comment\n functionBody" [ ( C Function, "function" ), ( Normal, " " ), ( C BasicSymbol, "=" ), ( Normal, " " ), ( Comment, "-- Comment" ), ( LineBreak, "\n" ), ( Normal, " " ), ( Normal, "functionBody" ) ] 27 | , equalTest "Comment: Multiline" "function = {- Multi\nline\ncomment-} functionBody" [ ( C Function, "function" ), ( Normal, " " ), ( C BasicSymbol, "=" ), ( Normal, " " ), ( Comment, "{-" ), ( Comment, " Multi" ), ( LineBreak, "\n" ), ( Comment, "line" ), ( LineBreak, "\n" ), ( Comment, "comment" ), ( Comment, "-}" ), ( Normal, " " ), ( Normal, "functionBody" ) ] 28 | , equalTest "Infix" "(,)" [ ( C Function, "(,)" ) ] 29 | , fuzz string "Fuzz string" <| 30 | \fuzzStr -> 31 | Parser.run Elm.toRevTokens fuzzStr 32 | |> Result.map 33 | (List.reverse 34 | >> List.map Tuple.second 35 | >> String.concat 36 | ) 37 | |> equal (Ok fuzzStr) 38 | |> onFail ("Resulting error string: \"" ++ fuzzStr ++ "\"") 39 | ] 40 | 41 | 42 | equalTest : String -> String -> List ( T.Syntax Elm.Syntax, String ) -> Test 43 | equalTest testName testStr testExpec = 44 | describe testName 45 | [ test "Syntax equality" <| 46 | \() -> 47 | Parser.run Elm.toRevTokens testStr 48 | |> Result.map List.reverse 49 | |> Result.withDefault [] 50 | |> equalLists testExpec 51 | , test "String equality" <| 52 | \() -> 53 | Parser.run Elm.toRevTokens testStr 54 | |> Result.map 55 | (List.reverse 56 | >> List.map Tuple.second 57 | >> String.concat 58 | ) 59 | |> equal (Ok testStr) 60 | ] 61 | 62 | 63 | moduleDeclarationText : String 64 | moduleDeclarationText = 65 | "module Main exposing (parser, Type)" 66 | 67 | 68 | moduleDeclarationResult : List ( T.Syntax Elm.Syntax, String ) 69 | moduleDeclarationResult = 70 | [ ( T.C Keyword, "module" ), ( Normal, " " ), ( Normal, "Main" ), ( Normal, " " ), ( T.C Keyword, "exposing" ), ( Normal, " " ), ( Normal, "(" ), ( C Function, "parser" ), ( Normal, "," ), ( Normal, " " ), ( T.C TypeSignature, "Type" ), ( Normal, ")" ) ] 71 | 72 | 73 | functionSignatureText : String 74 | functionSignatureText = 75 | "text : (SyntaxType, String) -> Html msg" 76 | 77 | 78 | functionSignatureResult : List ( T.Syntax Elm.Syntax, String ) 79 | functionSignatureResult = 80 | [ ( C Function, "text" ), ( Normal, " " ), ( C BasicSymbol, ":" ), ( Normal, " " ), ( Normal, "(" ), ( T.C TypeSignature, "SyntaxType" ), ( Normal, "," ), ( Normal, " " ), ( T.C TypeSignature, "String" ), ( Normal, ")" ), ( Normal, " " ), ( C BasicSymbol, "->" ), ( Normal, " " ), ( T.C TypeSignature, "Html" ), ( Normal, " " ), ( Normal, "msg" ) ] 81 | -------------------------------------------------------------------------------- /src/SyntaxHighlight/Language/Xml.elm: -------------------------------------------------------------------------------- 1 | module SyntaxHighlight.Language.Xml exposing 2 | ( Syntax(..) 3 | , syntaxToStyle 4 | -- Exposing for tests purpose 5 | 6 | , toLines 7 | , toRevTokens 8 | ) 9 | 10 | import Char 11 | import Parser exposing ((|.), DeadEnd, Parser, Step(..), andThen, chompIf, getChompedString, keyword, loop, map, oneOf, succeed, symbol) 12 | import SyntaxHighlight.Language.Helpers exposing (Delimiter, addThen, chompIfThenWhile, consThen, delimited, isLineBreak, isSpace, isWhitespace, thenChompWhile) 13 | import SyntaxHighlight.Language.Type as T 14 | import SyntaxHighlight.Line exposing (Line) 15 | import SyntaxHighlight.Line.Helpers as Line 16 | import SyntaxHighlight.Style as Style exposing (Required(..)) 17 | 18 | 19 | type alias Token = 20 | T.Token Syntax 21 | 22 | 23 | type Syntax 24 | = Tag 25 | | Attribute 26 | | AttributeValue 27 | 28 | 29 | toLines : String -> Result (List DeadEnd) (List Line) 30 | toLines = 31 | Parser.run toRevTokens 32 | >> Result.map (Line.toLines syntaxToStyle) 33 | 34 | 35 | toRevTokens : Parser (List Token) 36 | toRevTokens = 37 | loop [] mainLoop 38 | 39 | 40 | mainLoop : List Token -> Parser (Step (List Token) (List Token)) 41 | mainLoop revTokens = 42 | oneOf 43 | [ whitespace 44 | |> map (\n -> Loop (n :: revTokens)) 45 | , comment 46 | |> map (\n -> Loop (n ++ revTokens)) 47 | , chompIfThenWhile (\c -> c /= '<' && not (isLineBreak c)) 48 | |> getChompedString 49 | |> map (\n -> Loop (( T.Normal, n ) :: revTokens)) 50 | , openTag revTokens 51 | |> map Loop 52 | , succeed (Done revTokens) 53 | ] 54 | 55 | 56 | openTag : List Token -> Parser (List Token) 57 | openTag revTokens = 58 | openTagParser 59 | |> getChompedString 60 | |> map (\b -> ( T.Normal, b ) :: revTokens) 61 | |> andThen tag 62 | 63 | 64 | openTagParser : Parser () 65 | openTagParser = 66 | succeed () 67 | |. chompIf (\c -> c == '<') 68 | |. oneOf 69 | [ chompIf (\c -> c == '/' || c == '!' || c == '?') 70 | , succeed () 71 | ] 72 | 73 | 74 | tag : List Token -> Parser (List Token) 75 | tag revTokens = 76 | oneOf 77 | [ chompIf isStartTagChar 78 | |> thenChompWhile isTagChar 79 | |> getChompedString 80 | |> map (\b -> ( T.C Tag, b )) 81 | |> andThen 82 | (\n -> loop (n :: revTokens) attributeLoop) 83 | , succeed revTokens 84 | ] 85 | 86 | 87 | isStartTagChar : Char -> Bool 88 | isStartTagChar c = 89 | Char.isUpper c || Char.isLower c || Char.isDigit c 90 | 91 | 92 | isTagChar : Char -> Bool 93 | isTagChar c = 94 | isStartTagChar c || c == '-' 95 | 96 | 97 | attributeLoop : List Token -> Parser (Step (List Token) (List Token)) 98 | attributeLoop revTokens = 99 | oneOf 100 | [ chompIfThenWhile isAttributeChar 101 | |> getChompedString 102 | |> map (\b -> ( T.C Attribute, b )) 103 | |> consThen attributeConfirm revTokens 104 | |> map Loop 105 | , whitespace 106 | |> map (\n -> Loop (n :: revTokens)) 107 | , chompIfThenWhile (\c -> not (isWhitespace c) && c /= '>') 108 | |> getChompedString 109 | |> map (\b -> Loop (( T.Normal, b ) :: revTokens)) 110 | , succeed (Done revTokens) 111 | ] 112 | 113 | 114 | isAttributeChar : Char -> Bool 115 | isAttributeChar c = 116 | isTagChar c || c == '_' 117 | 118 | 119 | attributeConfirm : List Token -> Parser (List Token) 120 | attributeConfirm revTokens = 121 | oneOf 122 | [ whitespace 123 | |> consThen attributeConfirm revTokens 124 | , symbol "=" 125 | |> map (\_ -> ( T.Normal, "=" )) 126 | |> consThen attributeValueLoop revTokens 127 | , succeed revTokens 128 | ] 129 | 130 | 131 | attributeValueLoop : List Token -> Parser (List Token) 132 | attributeValueLoop revTokens = 133 | oneOf 134 | [ whitespace 135 | |> consThen attributeValueLoop revTokens 136 | , attributeValue 137 | |> addThen succeed revTokens 138 | , succeed revTokens 139 | ] 140 | 141 | 142 | 143 | -- Attribute Value 144 | 145 | 146 | attributeValue : Parser (List Token) 147 | attributeValue = 148 | oneOf 149 | [ doubleQuote 150 | , quote 151 | , chompIfThenWhile (\c -> not (isWhitespace c) && c /= '>') 152 | |> getChompedString 153 | |> map (\b -> [ ( T.C AttributeValue, b ) ]) 154 | ] 155 | 156 | 157 | doubleQuote : Parser (List Token) 158 | doubleQuote = 159 | delimited doubleQuoteDelimiter 160 | 161 | 162 | doubleQuoteDelimiter : Delimiter Token 163 | doubleQuoteDelimiter = 164 | { start = "\"" 165 | , end = "\"" 166 | , isNestable = False 167 | , defaultMap = \b -> ( T.C AttributeValue, b ) 168 | , innerParsers = [ lineBreakList ] 169 | , isNotRelevant = not << isLineBreak 170 | } 171 | 172 | 173 | quote : Parser (List Token) 174 | quote = 175 | delimited 176 | { doubleQuoteDelimiter 177 | | start = "'" 178 | , end = "'" 179 | } 180 | 181 | 182 | 183 | -- Comment 184 | 185 | 186 | comment : Parser (List Token) 187 | comment = 188 | delimited 189 | { doubleQuoteDelimiter 190 | | start = "" 192 | , defaultMap = \b -> ( T.Comment, b ) 193 | } 194 | 195 | 196 | 197 | -- Helpers 198 | 199 | 200 | whitespace : Parser Token 201 | whitespace = 202 | oneOf 203 | [ chompIfThenWhile isSpace 204 | |> getChompedString 205 | |> map (\s -> ( T.Normal, s )) 206 | , lineBreak 207 | ] 208 | 209 | 210 | lineBreak : Parser Token 211 | lineBreak = 212 | symbol "\n" 213 | |> map (\_ -> ( T.LineBreak, "\n" )) 214 | 215 | 216 | lineBreakList : Parser (List Token) 217 | lineBreakList = 218 | lineBreak 219 | |> map List.singleton 220 | 221 | 222 | syntaxToStyle : Syntax -> ( Style.Required, String ) 223 | syntaxToStyle syntax = 224 | case syntax of 225 | Tag -> 226 | ( Style3, "xml-t" ) 227 | 228 | Attribute -> 229 | ( Style5, "xml-a" ) 230 | 231 | AttributeValue -> 232 | ( Style2, "xlm-av" ) 233 | -------------------------------------------------------------------------------- /src/SyntaxHighlight/View.elm: -------------------------------------------------------------------------------- 1 | module SyntaxHighlight.View exposing (toBlockHtml, toInlineHtml, toStaticBlockHtml, toStaticInlineHtml) 2 | 3 | import Html exposing (Html, br, code, div, pre, span, text) 4 | import Html.Attributes exposing (attribute, class, classList) 5 | import SyntaxHighlight.Line exposing (..) 6 | import SyntaxHighlight.Style exposing (Required(..)) 7 | 8 | 9 | 10 | -- Html 11 | 12 | 13 | toBlockHtml : Maybe Int -> List Line -> Html msg 14 | toBlockHtml maybeStart lines = 15 | case maybeStart of 16 | Nothing -> 17 | pre [ class "elmsh" ] 18 | [ toInlineHtml lines ] 19 | 20 | Just start -> 21 | lines 22 | |> List.indexedMap (lineView start) 23 | |> code [] 24 | |> List.singleton 25 | |> pre [ class "elmsh" ] 26 | 27 | 28 | lineView : Int -> Int -> Line -> Html msg 29 | lineView start index { fragments, highlight } = 30 | div 31 | [ classList 32 | [ ( "elmsh-line", True ) 33 | , ( "elmsh-hl", highlight == Just Normal ) 34 | , ( "elmsh-add", highlight == Just Add ) 35 | , ( "elmsh-del", highlight == Just Del ) 36 | ] 37 | , attribute "data-elmsh-lc" (String.fromInt (start + index)) 38 | ] 39 | (List.map fragmentView fragments) 40 | 41 | 42 | toInlineHtml : List Line -> Html msg 43 | toInlineHtml lines = 44 | lines 45 | |> List.map 46 | (\{ highlight, fragments } -> 47 | if highlight == Nothing then 48 | List.map fragmentView fragments 49 | 50 | else 51 | [ span 52 | [ classList 53 | [ ( "elmsh-hl", highlight == Just Normal ) 54 | , ( "elmsh-add", highlight == Just Add ) 55 | , ( "elmsh-del", highlight == Just Del ) 56 | ] 57 | ] 58 | (List.map fragmentView fragments) 59 | ] 60 | ) 61 | |> List.concat 62 | |> code [ class "elmsh" ] 63 | 64 | 65 | fragmentView : Fragment -> Html msg 66 | fragmentView { text, requiredStyle, additionalClass } = 67 | if requiredStyle == Default && String.isEmpty additionalClass then 68 | Html.text text 69 | 70 | else 71 | span 72 | [ classList 73 | [ ( requiredStyleToString requiredStyle 74 | , requiredStyle /= Default 75 | ) 76 | , ( "elmsh-" ++ additionalClass 77 | , additionalClass /= "" 78 | ) 79 | ] 80 | ] 81 | [ Html.text text ] 82 | 83 | 84 | requiredStyleToString : Required -> String 85 | requiredStyleToString required = 86 | (++) "elmsh" <| 87 | case required of 88 | Default -> 89 | "0" 90 | 91 | Comment -> 92 | "-comm" 93 | 94 | Style1 -> 95 | "1" 96 | 97 | Style2 -> 98 | "2" 99 | 100 | Style3 -> 101 | "3" 102 | 103 | Style4 -> 104 | "4" 105 | 106 | Style5 -> 107 | "5" 108 | 109 | Style6 -> 110 | "6" 111 | 112 | Style7 -> 113 | "7" 114 | 115 | 116 | 117 | -- Static Html 118 | 119 | 120 | toStaticBlockHtml : Maybe Int -> List Line -> String 121 | toStaticBlockHtml maybeStart lines = 122 | case maybeStart of 123 | Nothing -> 124 | "" 125 | ++ toStaticInlineHtml lines 126 | ++ "" 127 | 128 | Just start -> 129 | String.concat 130 | [ "
"
131 | , List.indexedMap (staticLineView start) lines
132 | |> String.concat
133 | , ""
134 | ]
135 |
136 |
137 | staticLineView : Int -> Int -> Line -> String
138 | staticLineView start index { fragments, highlight } =
139 | String.concat
140 | [ ""
157 | , List.map
158 | (\{ highlight, fragments } ->
159 | if highlight == Nothing then
160 | List.map staticFragmentView fragments
161 |
162 | else
163 | [ " String.concat
172 | , ""
173 | ]
174 | )
175 | lines
176 | |> List.concat
177 | |> String.concat
178 | , ""
179 | ]
180 |
181 |
182 | staticFragmentView : Fragment -> String
183 | staticFragmentView { text, requiredStyle, additionalClass } =
184 | if requiredStyle == Default && String.isEmpty additionalClass then
185 | text
186 |
187 | else
188 | String.concat
189 | [ ""
198 | , text
199 | , ""
200 | ]
201 |
202 |
203 | emptyIfFalse : Bool -> String -> String
204 | emptyIfFalse bool str =
205 | if bool then
206 | str
207 |
208 | else
209 | ""
210 |
--------------------------------------------------------------------------------
/src/SyntaxHighlight/Language/Helpers.elm:
--------------------------------------------------------------------------------
1 | module SyntaxHighlight.Language.Helpers exposing
2 | ( Delimiter
3 | , addThen
4 | , chompIfThenWhile
5 | , consThen
6 | , delimited
7 | , escapable
8 | , isEscapable
9 | , isLineBreak
10 | , isNumber
11 | , isSpace
12 | , isWhitespace
13 | , number
14 | , numberExponentialNotation
15 | , thenChompWhile
16 | , whitespaceCharSet
17 | )
18 |
19 | import Char
20 | import Parser exposing (..)
21 | import Set exposing (Set)
22 |
23 |
24 | isWhitespace : Char -> Bool
25 | isWhitespace c =
26 | isSpace c || isLineBreak c
27 |
28 |
29 | whitespaceCharSet : Set Char
30 | whitespaceCharSet =
31 | Set.fromList
32 | [ ' '
33 | , '\t'
34 | , '\n'
35 | ]
36 |
37 |
38 | isSpace : Char -> Bool
39 | isSpace c =
40 | c == ' ' || c == '\t'
41 |
42 |
43 | isLineBreak : Char -> Bool
44 | isLineBreak c =
45 | c == '\n'
46 |
47 |
48 | numberExponentialNotation : Parser ()
49 | numberExponentialNotation =
50 | oneOf
51 | [ positiveNumberExponentialNotation
52 | , negativeNumberExponentialNotation
53 | , number
54 | ]
55 |
56 |
57 | negativeNumberExponentialNotation : Parser ()
58 | negativeNumberExponentialNotation =
59 | succeed ()
60 | |. backtrackable (symbol "-")
61 | |. positiveNumberExponentialNotation
62 |
63 |
64 | positiveNumberExponentialNotation : Parser ()
65 | positiveNumberExponentialNotation =
66 | succeed ()
67 | |. backtrackable float
68 |
69 |
70 | number : Parser ()
71 | number =
72 | oneOf
73 | [ positiveNumber
74 | , negativeNumber
75 | ]
76 |
77 |
78 | negativeNumber : Parser ()
79 | negativeNumber =
80 | succeed ()
81 | |. backtrackable (symbol "-")
82 | |. positiveNumber
83 |
84 |
85 | positiveNumber : Parser ()
86 | positiveNumber =
87 | succeed ()
88 | |. chompIf isNumber
89 | |. chompWhile isNumber
90 |
91 |
92 | isNumber : Char -> Bool
93 | isNumber c =
94 | Char.isDigit c || c == '.'
95 |
96 |
97 |
98 | {- Delimiter
99 |
100 | When defining isNotRelevant, make sure to add all chars that
101 | innerParsers starts with.
102 | -}
103 |
104 |
105 | type alias Delimiter a =
106 | { start : String
107 | , end : String
108 | , isNestable : Bool
109 | , defaultMap : String -> a
110 | , innerParsers : List (Parser (List a))
111 | , isNotRelevant : Char -> Bool
112 | }
113 |
114 |
115 | delimited : Delimiter a -> Parser (List a)
116 | delimited ({ start, isNotRelevant, defaultMap } as options) =
117 | symbol start
118 | |> map (always (defaultMap start))
119 | |> andThen (\n -> delimitedHelp options [ n ])
120 |
121 |
122 | delimitedHelp : Delimiter a -> List a -> Parser (List a)
123 | delimitedHelp ({ start, end, isNotRelevant } as options) revAList =
124 | case ( String.uncons options.start, String.uncons options.end ) of
125 | ( Nothing, _ ) ->
126 | problem "Trying to parse a delimited helper, but the start token cannot be an empty string!"
127 |
128 | ( _, Nothing ) ->
129 | problem "Trying to parse a delimited helper, but the end token cannot be an empty string!"
130 |
131 | ( Just ( startChar, _ ), Just ( endChar, _ ) ) ->
132 | if options.isNestable then
133 | delimitedNestable 1
134 | { options
135 | | isNotRelevant =
136 | \c -> isNotRelevant c && c /= startChar && c /= endChar
137 | }
138 | revAList
139 |
140 | else
141 | delimitedUnnestable
142 | { options
143 | | isNotRelevant =
144 | \c -> isNotRelevant c && c /= endChar
145 | }
146 | revAList
147 |
148 |
149 | delimitedUnnestable : Delimiter a -> List a -> Parser (List a)
150 | delimitedUnnestable ({ defaultMap, isNotRelevant, end, innerParsers } as options) revAList =
151 | oneOf
152 | [ symbol end |> map (always (defaultMap end :: revAList))
153 | , Parser.end |> map (always revAList)
154 | , oneOf innerParsers
155 | |> addThen (delimitedUnnestable options) revAList
156 | , chompIf (always True)
157 | |> thenChompWhile isNotRelevant
158 | |> getChompedString
159 | |> map defaultMap
160 | |> consThen (delimitedUnnestable options) revAList
161 | ]
162 |
163 |
164 | delimitedNestable : Int -> Delimiter a -> List a -> Parser (List a)
165 | delimitedNestable nestLevel ({ defaultMap, isNotRelevant, start, end, innerParsers } as options) revAList =
166 | oneOf
167 | [ symbol end
168 | |> map (always (defaultMap end :: revAList))
169 | |> andThen
170 | (\n ->
171 | if nestLevel == 1 then
172 | Parser.succeed n
173 |
174 | else
175 | delimitedNestable (nestLevel - 1) options n
176 | )
177 | , symbol start
178 | |> thenChompWhile isNotRelevant
179 | |> getChompedString
180 | |> map defaultMap
181 | |> consThen (delimitedNestable (nestLevel + 1) options) revAList
182 | , oneOf innerParsers
183 | |> addThen (delimitedUnnestable options) revAList
184 | , Parser.end |> map (always revAList)
185 | , chompIf (always True)
186 | |> thenChompWhile isNotRelevant
187 | |> getChompedString
188 | |> map defaultMap
189 | |> consThen (delimitedNestable nestLevel options) revAList
190 | ]
191 |
192 |
193 | thenChompWhile : (Char -> Bool) -> Parser a -> Parser a
194 | thenChompWhile isNotRelevant previousParser =
195 | previousParser
196 | |. chompWhile isNotRelevant
197 |
198 |
199 | chompIfThenWhile : (Char -> Bool) -> Parser ()
200 | chompIfThenWhile isNotRelevant =
201 | succeed ()
202 | |. chompIf isNotRelevant
203 | |. chompWhile isNotRelevant
204 |
205 |
206 | consThen : (List a -> Parser (List a)) -> List a -> Parser a -> Parser (List a)
207 | consThen f list pn =
208 | andThen (\n -> f (n :: list)) pn
209 |
210 |
211 | addThen : (List a -> Parser (List a)) -> List a -> Parser (List a) -> Parser (List a)
212 | addThen f list plist =
213 | andThen (\n -> f (n ++ list)) plist
214 |
215 |
216 |
217 | -- Inner parser Helpers
218 |
219 |
220 | escapable : Parser ()
221 | escapable =
222 | succeed ()
223 | |. backtrackable (symbol "\\")
224 | |. chompIf isEscapableChar
225 |
226 |
227 | isEscapable : Char -> Bool
228 | isEscapable c =
229 | c == '\\'
230 |
231 |
232 | isEscapableChar : Char -> Bool
233 | isEscapableChar c =
234 | Set.member c escapableSet
235 |
236 |
237 | escapableSet : Set Char
238 | escapableSet =
239 | Set.fromList
240 | [ '\''
241 | , '"'
242 | , '\\'
243 | , 'n'
244 | , 'r'
245 | , 't'
246 | , 'b'
247 | , 'f'
248 | , 'v'
249 | ]
250 |
--------------------------------------------------------------------------------
/src/SyntaxHighlight/Language/Json.elm:
--------------------------------------------------------------------------------
1 | module SyntaxHighlight.Language.Json exposing
2 | ( Syntax(..)
3 | , syntaxToStyle
4 | -- Exposing for tests purpose
5 |
6 | , toLines
7 | , toRevTokens
8 | )
9 |
10 | import Parser exposing ((|.), DeadEnd, Parser, Step(..), andThen, chompIf, getChompedString, keyword, loop, map, oneOf, succeed, symbol)
11 | import Set exposing (Set)
12 | import SyntaxHighlight.Language.Helpers exposing (Delimiter, chompIfThenWhile, delimited, isEscapable, isLineBreak, isSpace, isWhitespace, thenChompWhile, whitespaceCharSet)
13 | import SyntaxHighlight.Language.Type as T
14 | import SyntaxHighlight.Line exposing (Line)
15 | import SyntaxHighlight.Line.Helpers as Line
16 | import SyntaxHighlight.Style as Style exposing (Required(..))
17 |
18 |
19 | type alias Token =
20 | T.Token Syntax
21 |
22 |
23 | type Syntax
24 | = String
25 | | Escapable
26 | | Number
27 | | Boolean
28 | | Null
29 | | ObjectKey
30 | | Object
31 | | Array
32 |
33 |
34 | toLines : String -> Result (List DeadEnd) (List Line)
35 | toLines =
36 | Parser.run toRevTokens
37 | >> Result.map (Line.toLines syntaxToStyle)
38 |
39 |
40 | toRevTokens : Parser (List Token)
41 | toRevTokens =
42 | loop [] mainLoop
43 |
44 |
45 | mainLoop : List Token -> Parser (Step (List Token) (List Token))
46 | mainLoop revTokens =
47 | oneOf
48 | [ whitespace
49 | |> map (\n -> Loop (n :: revTokens))
50 | , object
51 | |> map (\n -> Loop (n ++ revTokens))
52 | , chompIf (always True)
53 | |> getChompedString
54 | |> map (\b -> Loop (( T.Normal, b ) :: revTokens))
55 | , succeed (Done revTokens)
56 | ]
57 |
58 |
59 | object : Parser (List Token)
60 | object =
61 | symbol "{"
62 | |> andThen
63 | (\_ -> loop [ ( T.C Object, "{" ) ] objectLoop)
64 |
65 |
66 | objectLoop : List Token -> Parser (Step (List Token) (List Token))
67 | objectLoop revTokens =
68 | oneOf
69 | [ whitespace
70 | |> map (\n -> Loop (n :: revTokens))
71 | , stringLiteral ObjectKey revTokens
72 | |> map Loop
73 | , symbol ":"
74 | |> andThen
75 | (\_ ->
76 | let
77 | revTokens_ =
78 | ( T.C Object, ":" )
79 | :: revTokens
80 | in
81 | oneOf
82 | [ whitespace
83 | |> andThen
84 | (\ws ->
85 | oneOf
86 | [ value
87 | |> map (\v -> v ++ [ ws ])
88 | , succeed [ ws ]
89 | ]
90 | )
91 | , value
92 | , succeed []
93 | ]
94 | |> map (\ns -> ns ++ revTokens_)
95 | )
96 | |> map Loop
97 | , symbol ","
98 | |> map (\_ -> ( T.C Object, "," ))
99 | |> map (\n -> Loop (n :: revTokens))
100 | , symbol "}"
101 | |> map (\_ -> ( T.C Object, "}" ))
102 | |> map (\n -> Done (n :: revTokens))
103 | , succeed (Done revTokens)
104 | ]
105 |
106 |
107 | value : Parser (List Token)
108 | value =
109 | oneOf
110 | [ stringLiteral String []
111 | , number
112 | |> map (\n -> [ n ])
113 | , object
114 | , array
115 | , keyword "null"
116 | |> getChompedString
117 | |> map (\s -> [ ( T.C Null, s ) ])
118 | , oneOf
119 | [ keyword "true"
120 | , keyword "false"
121 | ]
122 | |> getChompedString
123 | |> map (\s -> [ ( T.C Boolean, s ) ])
124 | ]
125 |
126 |
127 | array : Parser (List Token)
128 | array =
129 | symbol "["
130 | |> andThen
131 | (\_ -> loop [ ( T.C Array, "[" ) ] arrayLoop)
132 |
133 |
134 | arrayLoop : List Token -> Parser (Step (List Token) (List Token))
135 | arrayLoop revTokens =
136 | oneOf
137 | [ whitespace
138 | |> map (\n -> Loop (n :: revTokens))
139 | , symbol ","
140 | |> map (\_ -> ( T.C Array, "," ))
141 | |> map (\n -> Loop (n :: revTokens))
142 | , symbol "]"
143 | |> map (\_ -> ( T.C Array, "]" ))
144 | |> map (\n -> Done (n :: revTokens))
145 | , value
146 | |> map (\ns -> Loop (ns ++ revTokens))
147 | , succeed (Done revTokens)
148 | ]
149 |
150 |
151 |
152 | -- String literal
153 |
154 |
155 | stringLiteral : Syntax -> List Token -> Parser (List Token)
156 | stringLiteral syntax_ revTokens =
157 | delimited (doubleQuoteDelimiter syntax_)
158 | |> map (\n -> n ++ revTokens)
159 |
160 |
161 | doubleQuoteDelimiter : Syntax -> Delimiter Token
162 | doubleQuoteDelimiter syntax_ =
163 | { start = "\""
164 | , end = "\""
165 | , isNestable = False
166 | , defaultMap = \b -> ( T.C syntax_, b )
167 | , innerParsers =
168 | [ map List.singleton lineBreak
169 | , stringEscapable
170 | ]
171 | , isNotRelevant = \c -> not (isLineBreak c || isEscapable c)
172 | }
173 |
174 |
175 |
176 | -- Helpers
177 |
178 |
179 | whitespace : Parser Token
180 | whitespace =
181 | oneOf
182 | [ space
183 | , lineBreak
184 | ]
185 |
186 |
187 | space : Parser Token
188 | space =
189 | chompIfThenWhile isSpace
190 | |> getChompedString
191 | |> map (\b -> ( T.Normal, b ))
192 |
193 |
194 | lineBreak : Parser Token
195 | lineBreak =
196 | symbol "\n"
197 | |> map (\_ -> ( T.LineBreak, "\n" ))
198 |
199 |
200 | number : Parser Token
201 | number =
202 | SyntaxHighlight.Language.Helpers.numberExponentialNotation
203 | |> getChompedString
204 | |> map (\b -> ( T.C Number, b ))
205 |
206 |
207 | stringEscapable : Parser (List Token)
208 | stringEscapable =
209 | escapable
210 | |> getChompedString
211 | |> map (\b -> [ ( T.C Escapable, b ) ])
212 |
213 |
214 | escapable : Parser ()
215 | escapable =
216 | succeed ()
217 | |. Parser.backtrackable (symbol "\\")
218 | |. chompIf isEscapableChar
219 |
220 |
221 | isEscapableChar : Char -> Bool
222 | isEscapableChar c =
223 | Set.member c escapableSet
224 |
225 |
226 | escapableSet : Set Char
227 | escapableSet =
228 | Set.fromList
229 | [ '"'
230 | , '\\'
231 | , '/'
232 | , 'b'
233 | , 'f'
234 | , 'n'
235 | , 'r'
236 | , 't'
237 | , 'u'
238 | ]
239 |
240 |
241 | syntaxToStyle : Syntax -> ( Style.Required, String )
242 | syntaxToStyle syntax =
243 | case syntax of
244 | String ->
245 | ( Style2, "json-s" )
246 |
247 | Escapable ->
248 | ( Style1, "json-e" )
249 |
250 | Number ->
251 | ( Style1, "json-n" )
252 |
253 | Boolean ->
254 | ( Style3, "json-b" )
255 |
256 | Null ->
257 | ( Style3, "json-null" )
258 |
259 | ObjectKey ->
260 | ( Style4, "json-k" )
261 |
262 | Object ->
263 | ( Default, "json-o" )
264 |
265 | Array ->
266 | ( Default, "json-a" )
267 |
--------------------------------------------------------------------------------
/src/SyntaxHighlight/Language/Nix.elm:
--------------------------------------------------------------------------------
1 | module SyntaxHighlight.Language.Nix exposing
2 | ( Syntax(..)
3 | , syntaxToStyle
4 | -- Exposing for test purposes
5 |
6 | , toLines
7 | , toRevTokens
8 | )
9 |
10 | import Parser exposing((|.), DeadEnd, Parser, Step(..), andThen, backtrackable, getChompedString, loop, map, oneOf, succeed, symbol)
11 | import Regex exposing (Regex)
12 | import Set exposing (Set)
13 | import SyntaxHighlight.Language.Helpers exposing (Delimiter, chompIfThenWhile, delimited, escapable, isEscapable, isLineBreak, isSpace, isWhitespace, thenChompWhile)
14 | import SyntaxHighlight.Language.Type as T
15 | import SyntaxHighlight.Line exposing (Line)
16 | import SyntaxHighlight.Line.Helpers as Line
17 | import SyntaxHighlight.Style as Style exposing (Required(..))
18 |
19 |
20 | type alias Token =
21 | T.Token Syntax
22 |
23 |
24 | type Syntax
25 | = Number
26 | | String
27 | | Keyword
28 | | Operator
29 | | Function
30 | | Punctuation
31 | | Literal
32 |
33 |
34 | toLines : String -> Result (List DeadEnd) (List Line)
35 | toLines =
36 | Parser.run toRevTokens
37 | >> Result.map (Line.toLines syntaxToStyle)
38 |
39 |
40 | toRevTokens : Parser (List Token)
41 | toRevTokens =
42 | loop [] mainLoop
43 |
44 |
45 | mainLoop : List Token -> Parser (Step (List Token) (List Token))
46 | mainLoop revTokens =
47 | oneOf
48 | [ space
49 | |> map (\n -> Loop (n :: revTokens))
50 | , lineBreak
51 | |> map (\n -> Loop (n :: revTokens))
52 | , punctuationChar
53 | |> map (\n -> Loop (n :: revTokens))
54 | , number
55 | |> map (\n -> Loop (n :: revTokens))
56 | , comment
57 | |> map (\n -> Loop (n ++ revTokens))
58 | , stringLiteral
59 | |> andThen (\n -> loop (n ++ revTokens) stringBody)
60 | |> map Loop
61 | , chompIfThenWhile isIdentifierChar
62 | |> getChompedString
63 | |> andThen (keywordParser revTokens)
64 | |> map Loop
65 | , succeed (Done revTokens)
66 | ]
67 |
68 |
69 | isIdentifierChar : Char -> Bool
70 | isIdentifierChar c =
71 | not
72 | (isWhitespace c
73 | || isPunctuationChar c
74 | )
75 |
76 |
77 | stringBody : List Token -> Parser (Step (List Token) (List Token))
78 | stringBody revTokens =
79 | oneOf
80 | [ whitespaceOrCommentStep revTokens
81 | , stringLiteral |> map (\s -> Loop (s ++ revTokens))
82 | , succeed (Done revTokens)
83 | ]
84 |
85 |
86 | punctuationChar : Parser Token
87 | punctuationChar =
88 | chompIfThenWhile isPunctuationChar
89 | |> getChompedString
90 | |> map (\b -> ( T.C Punctuation, b ))
91 |
92 |
93 | isPunctuationChar : Char -> Bool
94 | isPunctuationChar c =
95 | Set.member c punctuatorSet
96 |
97 |
98 | punctuatorSet : Set Char
99 | punctuatorSet =
100 | Set.fromList [ '{', '}', '(', ')', '[', ']', '.', ',', ':', ';' ]
101 |
102 |
103 |
104 | -- Keywords
105 |
106 |
107 | keywordParser : List Token -> String -> Parser (List Token)
108 | keywordParser revTokens s =
109 | if isOperator s then
110 | succeed (( T.C Operator, s ) :: revTokens)
111 |
112 | else if isFunction s then
113 | succeed (( T.C Function, s ) :: revTokens)
114 |
115 | else if isKeyword s then
116 | succeed (( T.C Keyword, s ) :: revTokens)
117 |
118 | else if isLiteral s then
119 | succeed (( T.C Literal, s ) :: revTokens)
120 |
121 | else
122 | succeed (( T.Normal, s ) :: revTokens)
123 |
124 |
125 |
126 | isKeyword : String -> Bool
127 | isKeyword =
128 | Regex.contains keywordPattern
129 |
130 |
131 | keywordPattern : Regex
132 | keywordPattern =
133 | "^(assert|builtins|else|if|in|inherit|let|null|or|then|with)$"
134 | |> Regex.fromStringWith { caseInsensitive = False, multiline = False }
135 | |> Maybe.withDefault Regex.never
136 |
137 |
138 | isLiteral : String -> Bool
139 | isLiteral str =
140 | Set.member (String.toUpper str) literalSet
141 |
142 |
143 | literalSet : Set String
144 | literalSet =
145 | Set.fromList [ "false", "true" ]
146 |
147 |
148 | isFunction : String -> Bool
149 | isFunction =
150 | Regex.contains functionPattern
151 |
152 |
153 | functionPattern : Regex
154 | functionPattern =
155 | "^(?:abort|add|all|any|attrNames|attrValues|baseNameOf|compareVersions|concatLists|currentSystem|deepSeq|derivation|dirOf|div|elem(?:At)?|fetch(?:Tarball|url)|filter(?:Source)?|fromJSON|genList|getAttr|getEnv|hasAttr|hashString|head|import|intersectAttrs|is(?:Attrs|Bool|Function|Int|List|Null|String)|length|lessThan|listToAttrs|map|mul|parseDrvName|pathExists|read(?:Dir|File)|removeAttrs|replaceStrings|seq|sort|stringLength|sub(?:string)?|tail|throw|to(?:File|JSON|Path|String|XML)|trace|typeOf)|foldl'$"
156 | |> Regex.fromStringWith { caseInsensitive = False, multiline = False }
157 | |> Maybe.withDefault Regex.never
158 |
159 |
160 | isOperator : String -> Bool
161 | isOperator =
162 | Regex.contains operatorPattern
163 |
164 |
165 | operatorPattern : Regex
166 | operatorPattern =
167 | "^([=!<>]=?|\\+\\+?|\\|\\||&&|\\/\\/|->?|[?@])$"
168 | |> Regex.fromStringWith { caseInsensitive = False, multiline = False }
169 | |> Maybe.withDefault Regex.never
170 |
171 |
172 |
173 | -- Strings
174 |
175 |
176 | stringLiteral : Parser (List Token)
177 | stringLiteral =
178 | oneOf
179 | [ multilineString
180 | , quote
181 | , doubleQuote
182 | ]
183 |
184 |
185 | quote : Parser (List Token)
186 | quote =
187 | delimited quoteDelimiter
188 |
189 |
190 | quoteDelimiter : Delimiter Token
191 | quoteDelimiter =
192 | { start = "'"
193 | , end = "'"
194 | , isNestable = False
195 | , defaultMap = \b -> ( T.C String, b )
196 | , innerParsers = [ lineBreakList, nixEscapable ]
197 | , isNotRelevant = \c -> not (isLineBreak c || isEscapable c)
198 | }
199 |
200 |
201 | multilineString : Parser (List Token)
202 | multilineString =
203 | delimited
204 | { quoteDelimiter
205 | | start = "''"
206 | , end = "''"
207 | }
208 |
209 |
210 | doubleQuote : Parser (List Token)
211 | doubleQuote =
212 | delimited
213 | { quoteDelimiter
214 | | start = "\""
215 | , end = "\""
216 | }
217 |
218 |
219 | isStringLiteralChar : Char -> Bool
220 | isStringLiteralChar c =
221 | c == '\'' || c == '"'
222 |
223 |
224 |
225 | -- Comments
226 |
227 |
228 | comment : Parser (List Token)
229 | comment =
230 | oneOf
231 | [ inlineComment
232 | , multilineComment
233 | ]
234 |
235 |
236 | inlineComment : Parser (List Token)
237 | inlineComment =
238 | [ "#" ]
239 | |> List.map (symbol >> thenChompWhile (not << isLineBreak) >> getChompedString >> map (\b -> [ ( T.Comment, b ) ]))
240 | |> oneOf
241 |
242 |
243 | multilineComment : Parser (List Token)
244 | multilineComment =
245 | delimited
246 | { start = "/*"
247 | , end = "*/"
248 | , isNestable = False
249 | , defaultMap = \b -> ( T.Comment, b )
250 | , innerParsers = [ lineBreakList ]
251 | , isNotRelevant = not << isLineBreak
252 | }
253 |
254 |
255 |
256 | -- Helpers
257 |
258 |
259 | whitespaceOrCommentStep : List Token -> Parser (Step (List Token) (List Token))
260 | whitespaceOrCommentStep revTokens =
261 | oneOf
262 | [ space |> map (\s -> Loop (s :: revTokens))
263 | , lineBreak
264 | |> map (\s -> s :: revTokens)
265 | |> andThen checkContext
266 | , comment |> map (\s -> Loop (s ++ revTokens))
267 | ]
268 |
269 |
270 | checkContext : List Token -> Parser (Step (List Token) (List Token))
271 | checkContext revTokens =
272 | oneOf
273 | [ whitespaceOrCommentStep revTokens
274 | , succeed (Done revTokens)
275 | ]
276 |
277 |
278 | space : Parser Token
279 | space =
280 | chompIfThenWhile isSpace
281 | |> getChompedString
282 | |> map (\b -> ( T.Normal, b ))
283 |
284 |
285 | lineBreak : Parser Token
286 | lineBreak =
287 | symbol "\n"
288 | |> map (\_ -> ( T.LineBreak, "\n" ))
289 |
290 |
291 | lineBreakList : Parser (List Token)
292 | lineBreakList =
293 | symbol "\n"
294 | |> map (\_ -> [ ( T.LineBreak, "\n" ) ])
295 |
296 |
297 | number : Parser Token
298 | number =
299 | oneOf
300 | [ hexNumber
301 | , SyntaxHighlight.Language.Helpers.number
302 | ]
303 | |> getChompedString
304 | |> map (\b -> ( T.C Number, b ))
305 |
306 |
307 | hexNumber : Parser ()
308 | hexNumber =
309 | succeed ()
310 | |. backtrackable (symbol "0x")
311 | |. chompIfThenWhile Char.isHexDigit
312 |
313 |
314 | nixEscapable : Parser (List Token)
315 | nixEscapable =
316 | escapable
317 | |> getChompedString
318 | |> map (\b -> [ ( T.C Function, b ) ])
319 |
320 |
321 | syntaxToStyle : Syntax -> ( Style.Required, String )
322 | syntaxToStyle syntax =
323 | case syntax of
324 | Number ->
325 | ( Style1, "nix-n" )
326 |
327 | String ->
328 | ( Style2, "nix-s" )
329 |
330 | Keyword ->
331 | ( Style3, "nix-k" )
332 |
333 | Operator ->
334 | ( Style4, "nix-o" )
335 |
336 | Function ->
337 | ( Style5, "nix-f" )
338 |
339 | Punctuation ->
340 | ( Style6, "nix-p" )
341 |
342 | Literal ->
343 | ( Style7, "nix-l" )
344 |
345 |
--------------------------------------------------------------------------------
/src/SyntaxHighlight/Language/Python.elm:
--------------------------------------------------------------------------------
1 | module SyntaxHighlight.Language.Python exposing
2 | ( Syntax(..)
3 | , syntaxToStyle
4 | -- Exposing for tests purpose
5 |
6 | , toLines
7 | , toRevTokens
8 | )
9 |
10 | import Parser exposing ((|.), DeadEnd, Parser, Step(..), andThen, chompIf, getChompedString, keyword, loop, map, oneOf, succeed, symbol)
11 | import Set exposing (Set)
12 | import SyntaxHighlight.Language.Helpers exposing (Delimiter, addThen, chompIfThenWhile, consThen, delimited, isEscapable, isLineBreak, isSpace, isWhitespace, thenChompWhile)
13 | import SyntaxHighlight.Language.Type as T
14 | import SyntaxHighlight.Line exposing (Line)
15 | import SyntaxHighlight.Line.Helpers as Line
16 | import SyntaxHighlight.Style as Style exposing (Required(..))
17 |
18 |
19 |
20 | -- Author: brandly (https://github.com/brandly)
21 |
22 |
23 | type alias Token =
24 | T.Token Syntax
25 |
26 |
27 | type Syntax
28 | = Number
29 | | String
30 | | Keyword
31 | | DeclarationKeyword
32 | | FunctionEval
33 | | Function
34 | | LiteralKeyword
35 | | Param
36 |
37 |
38 | toLines : String -> Result (List DeadEnd) (List Line)
39 | toLines =
40 | Parser.run toRevTokens
41 | >> Result.map (Line.toLines syntaxToStyle)
42 |
43 |
44 | toRevTokens : Parser (List Token)
45 | toRevTokens =
46 | loop [] mainLoop
47 |
48 |
49 | mainLoop : List Token -> Parser (Step (List Token) (List Token))
50 | mainLoop revTokens =
51 | oneOf
52 | [ whitespaceOrCommentStep revTokens
53 | , stringLiteral
54 | |> map (\s -> Loop (s ++ revTokens))
55 | , oneOf
56 | [ operatorChar
57 | , groupChar
58 | , number
59 | ]
60 | |> map (\s -> Loop (s :: revTokens))
61 | , chompIfThenWhile isIdentifierNameChar
62 | |> getChompedString
63 | |> andThen (keywordParser revTokens)
64 | |> map Loop
65 | , succeed (Done revTokens)
66 | ]
67 |
68 |
69 | keywordParser : List Token -> String -> Parser (List Token)
70 | keywordParser revTokens n =
71 | if n == "def" then
72 | loop (( T.C DeclarationKeyword, n ) :: revTokens) functionDeclarationLoop
73 |
74 | else if n == "class" then
75 | loop (( T.C DeclarationKeyword, n ) :: revTokens) classDeclarationLoop
76 |
77 | else if isKeyword n then
78 | succeed (( T.C Keyword, n ) :: revTokens)
79 |
80 | else if isLiteralKeyword n then
81 | succeed (( T.C LiteralKeyword, n ) :: revTokens)
82 |
83 | else
84 | loop (( T.C FunctionEval, n ) :: revTokens) functionEvalLoop
85 |
86 |
87 | functionDeclarationLoop : List Token -> Parser (Step (List Token) (List Token))
88 | functionDeclarationLoop revTokens =
89 | oneOf
90 | [ whitespaceOrCommentStep revTokens
91 | , chompIfThenWhile isIdentifierNameChar
92 | |> getChompedString
93 | |> map (\b -> Loop (( T.C Function, b ) :: revTokens))
94 | , symbol "("
95 | |> andThen
96 | (\_ -> loop (( T.Normal, "(" ) :: revTokens) argLoop)
97 | |> map Loop
98 | , succeed (Done revTokens)
99 | ]
100 |
101 |
102 | argLoop : List Token -> Parser (Step (List Token) (List Token))
103 | argLoop revTokens =
104 | oneOf
105 | [ whitespaceOrCommentStep revTokens
106 | , chompIfThenWhile (\c -> not (isCommentChar c || isWhitespace c || c == ',' || c == ')'))
107 | |> getChompedString
108 | |> map (\b -> Loop (( T.C Param, b ) :: revTokens))
109 | , chompIfThenWhile (\c -> c == '/' || c == ',')
110 | |> getChompedString
111 | |> map (\b -> Loop (( T.Normal, b ) :: revTokens))
112 | , succeed (Done revTokens)
113 | ]
114 |
115 |
116 | functionEvalLoop : List Token -> Parser (Step (List Token) (List Token))
117 | functionEvalLoop revTokens =
118 | oneOf
119 | [ whitespaceOrCommentStep revTokens
120 | , symbol "("
121 | |> map (\_ -> Done (( T.Normal, "(" ) :: revTokens))
122 | , succeed (Done revTokens)
123 | ]
124 |
125 |
126 | classDeclarationLoop : List Token -> Parser (Step (List Token) (List Token))
127 | classDeclarationLoop revTokens =
128 | -- TODO: handle base classes
129 | oneOf
130 | [ whitespaceOrCommentStep revTokens
131 | , chompIfThenWhile isIdentifierNameChar
132 | |> getChompedString
133 | |> map (\b -> Loop (( T.C Function, b ) :: revTokens))
134 | , succeed (Done revTokens)
135 | ]
136 |
137 |
138 | isIdentifierNameChar : Char -> Bool
139 | isIdentifierNameChar c =
140 | not
141 | (isPunctuation c
142 | || isStringLiteralChar c
143 | || isCommentChar c
144 | || isWhitespace c
145 | )
146 |
147 |
148 |
149 | -- Reserved words
150 |
151 |
152 | isKeyword : String -> Bool
153 | isKeyword str =
154 | Set.member str keywordSet
155 |
156 |
157 | keywordSet : Set String
158 | keywordSet =
159 | Set.fromList
160 | [ "finally"
161 | , "is"
162 | , "return"
163 | , "continue"
164 | , "for"
165 | , "lambda"
166 | , "try"
167 | , "from"
168 | , "nonlocal"
169 | , "while"
170 | , "and"
171 | , "del"
172 | , "global"
173 | , "not"
174 | , "with"
175 | , "as"
176 | , "elif"
177 | , "if"
178 | , "or"
179 | , "yield"
180 | , "assert"
181 | , "else"
182 | , "import"
183 | , "pass"
184 | , "break"
185 | , "except"
186 | , "in"
187 | , "raise"
188 | ]
189 |
190 |
191 | isPunctuation : Char -> Bool
192 | isPunctuation c =
193 | Set.member c punctuationSet
194 |
195 |
196 | punctuationSet : Set Char
197 | punctuationSet =
198 | Set.union operatorSet groupSet
199 |
200 |
201 | operatorChar : Parser Token
202 | operatorChar =
203 | chompIfThenWhile isOperatorChar
204 | |> getChompedString
205 | |> map (\b -> ( T.C Keyword, b ))
206 |
207 |
208 | isOperatorChar : Char -> Bool
209 | isOperatorChar c =
210 | Set.member c operatorSet
211 |
212 |
213 | operatorSet : Set Char
214 | operatorSet =
215 | Set.fromList
216 | [ '+'
217 | , '-'
218 | , '*'
219 | , '/'
220 | , '='
221 | , '!'
222 | , '<'
223 | , '>'
224 | , '&'
225 | , '|'
226 | , '?'
227 | , '^'
228 | , ':'
229 | , '~'
230 | , '%'
231 | , '.'
232 | ]
233 |
234 |
235 | groupChar : Parser Token
236 | groupChar =
237 | chompIfThenWhile isGroupChar
238 | |> getChompedString
239 | |> map (\b -> ( T.Normal, b ))
240 |
241 |
242 | isGroupChar : Char -> Bool
243 | isGroupChar c =
244 | Set.member c groupSet
245 |
246 |
247 | groupSet : Set Char
248 | groupSet =
249 | Set.fromList
250 | [ '{'
251 | , '}'
252 | , '('
253 | , ')'
254 | , '['
255 | , ']'
256 | , ','
257 | , ';'
258 | ]
259 |
260 |
261 | isLiteralKeyword : String -> Bool
262 | isLiteralKeyword str =
263 | Set.member str literalKeywordSet
264 |
265 |
266 | literalKeywordSet : Set String
267 | literalKeywordSet =
268 | Set.fromList
269 | [ "True"
270 | , "False"
271 | , "None"
272 | ]
273 |
274 |
275 |
276 | -- String
277 |
278 |
279 | stringLiteral : Parser (List Token)
280 | stringLiteral =
281 | -- TODO: shortstring | longstring
282 | oneOf
283 | [ quote
284 | , doubleQuote
285 | ]
286 |
287 |
288 | quote : Parser (List Token)
289 | quote =
290 | delimited quoteDelimiter
291 |
292 |
293 | quoteDelimiter : Delimiter Token
294 | quoteDelimiter =
295 | { start = "'"
296 | , end = "'"
297 | , isNestable = False
298 | , defaultMap = \b -> ( T.C String, b )
299 |
300 | -- TODO: escapable chars
301 | , innerParsers = [ lineBreak ]
302 | , isNotRelevant = \c -> not (isLineBreak c || isEscapable c)
303 | }
304 |
305 |
306 | doubleQuote : Parser (List Token)
307 | doubleQuote =
308 | delimited
309 | { quoteDelimiter
310 | | start = "\""
311 | , end = "\""
312 | }
313 |
314 |
315 | isStringLiteralChar : Char -> Bool
316 | isStringLiteralChar c =
317 | c == '"' || c == '\''
318 |
319 |
320 |
321 | -- Comments
322 |
323 |
324 | comment : Parser (List Token)
325 | comment =
326 | oneOf
327 | [ inlineComment
328 | , multilineComment
329 | ]
330 |
331 |
332 | inlineComment : Parser (List Token)
333 | inlineComment =
334 | symbol "#"
335 | |> thenChompWhile (not << isLineBreak)
336 | |> getChompedString
337 | |> map (\b -> [ ( T.Comment, b ) ])
338 |
339 |
340 | multilineComment : Parser (List Token)
341 | multilineComment =
342 | -- TODO: might not need this at all. just parse as multiline string?
343 | delimited
344 | { start = "'''"
345 | , end = "'''"
346 | , isNestable = False
347 | , defaultMap = \b -> ( T.Comment, b )
348 | , innerParsers = [ lineBreak ]
349 | , isNotRelevant = \c -> not (isLineBreak c)
350 | }
351 |
352 |
353 | isCommentChar : Char -> Bool
354 | isCommentChar c =
355 | c == '#'
356 |
357 |
358 |
359 | -- Helpers
360 |
361 |
362 | whitespaceOrCommentStep : List Token -> Parser (Step (List Token) (List Token))
363 | whitespaceOrCommentStep revTokens =
364 | oneOf
365 | [ chompIfThenWhile isSpace
366 | |> getChompedString
367 | |> map (\s -> Loop (( T.Normal, s ) :: revTokens))
368 | , lineBreak
369 | |> map (\ns -> Loop (ns ++ revTokens))
370 | , comment
371 | |> map (\ns -> Loop (ns ++ revTokens))
372 | ]
373 |
374 |
375 | lineBreak : Parser (List Token)
376 | lineBreak =
377 | symbol "\n"
378 | |> map (\_ -> [ ( T.LineBreak, "\n" ) ])
379 |
380 |
381 | number : Parser Token
382 | number =
383 | SyntaxHighlight.Language.Helpers.number
384 | |> getChompedString
385 | |> map (\b -> ( T.C Number, b ))
386 |
387 |
388 | syntaxToStyle : Syntax -> ( Style.Required, String )
389 | syntaxToStyle syntax =
390 | case syntax of
391 | Number ->
392 | ( Style1, "py-n" )
393 |
394 | String ->
395 | ( Style2, "py-s" )
396 |
397 | Keyword ->
398 | ( Style3, "py-k" )
399 |
400 | DeclarationKeyword ->
401 | ( Style4, "py-dk" )
402 |
403 | Function ->
404 | ( Style5, "py-f" )
405 |
406 | LiteralKeyword ->
407 | ( Style6, "py-lk" )
408 |
409 | Param ->
410 | ( Style7, "py-p" )
411 |
412 | FunctionEval ->
413 | ( Default, "py-fe" )
414 |
--------------------------------------------------------------------------------
/src/SyntaxHighlight/Language/Kotlin.elm:
--------------------------------------------------------------------------------
1 | module SyntaxHighlight.Language.Kotlin exposing
2 | ( Syntax(..)
3 | , syntaxToStyle
4 | -- Exposing for test purposes
5 |
6 | , toLines
7 | , toRevTokens
8 | )
9 |
10 | import Parser exposing((|.), DeadEnd, Parser, Step(..), andThen, backtrackable, getChompedString, loop, map, oneOf, succeed, symbol)
11 | import Regex exposing (Regex)
12 | import Set exposing (Set)
13 | import SyntaxHighlight.Language.Helpers exposing (Delimiter, chompIfThenWhile, delimited, escapable, isEscapable, isLineBreak, isSpace, isWhitespace, thenChompWhile)
14 | import SyntaxHighlight.Language.Type as T
15 | import SyntaxHighlight.Line exposing (Line)
16 | import SyntaxHighlight.Line.Helpers as Line
17 | import SyntaxHighlight.Style as Style exposing (Required(..))
18 |
19 |
20 | type alias Token =
21 | T.Token Syntax
22 |
23 |
24 | type Syntax
25 | = Number
26 | | String
27 | | Keyword
28 | | Operator
29 | | Function
30 | | DeclarationKeyword
31 | | Param
32 | | Punctuation
33 | | Literal
34 |
35 |
36 | toLines : String -> Result (List DeadEnd) (List Line)
37 | toLines =
38 | Parser.run toRevTokens
39 | >> Result.map (Line.toLines syntaxToStyle)
40 |
41 |
42 | toRevTokens : Parser (List Token)
43 | toRevTokens =
44 | loop [] mainLoop
45 |
46 |
47 | mainLoop : List Token -> Parser (Step (List Token) (List Token))
48 | mainLoop revTokens =
49 | oneOf
50 | [ space
51 | |> map (\n -> Loop (n :: revTokens))
52 | , lineBreak
53 | |> map (\n -> Loop (n :: revTokens))
54 | , punctuationChar
55 | |> map (\n -> Loop (n :: revTokens))
56 | , number
57 | |> map (\n -> Loop (n :: revTokens))
58 | , comment
59 | |> map (\n -> Loop (n ++ revTokens))
60 | , stringLiteral
61 | |> andThen (\n -> loop (n ++ revTokens) stringBody)
62 | |> map Loop
63 | , chompIfThenWhile isIdentifierChar
64 | |> getChompedString
65 | |> andThen (keywordParser revTokens)
66 | |> map Loop
67 | , succeed (Done revTokens)
68 | ]
69 |
70 |
71 | isIdentifierChar : Char -> Bool
72 | isIdentifierChar c =
73 | not
74 | (isWhitespace c
75 | || isPunctuationChar c
76 | )
77 |
78 |
79 | stringBody : List Token -> Parser (Step (List Token) (List Token))
80 | stringBody revTokens =
81 | oneOf
82 | [ whitespaceOrCommentStep revTokens
83 | , stringLiteral |> map (\s -> Loop (s ++ revTokens))
84 | , succeed (Done revTokens)
85 | ]
86 |
87 |
88 | punctuationChar : Parser Token
89 | punctuationChar =
90 | chompIfThenWhile isPunctuationChar
91 | |> getChompedString
92 | |> map (\b -> ( T.C Punctuation, b ))
93 |
94 |
95 | isPunctuationChar : Char -> Bool
96 | isPunctuationChar c =
97 | Set.member c punctuatorSet
98 |
99 |
100 | punctuatorSet : Set Char
101 | punctuatorSet =
102 | Set.fromList [ '{', '}', '(', ')', '[', ']', '.', ',', ':', ';' ]
103 |
104 |
105 | isDeclarationKeyword : String -> Bool
106 | isDeclarationKeyword str =
107 | Set.member str declarationKeywordSet
108 |
109 |
110 | declarationKeywordSet : Set String
111 | declarationKeywordSet =
112 | Set.fromList
113 | [ "var"
114 | , "val"
115 | , "vararg"
116 | ]
117 |
118 | -- Keywords
119 |
120 |
121 | keywordParser : List Token -> String -> Parser (List Token)
122 | keywordParser revTokens s =
123 | if isOperator s then
124 | succeed (( T.C Operator, s ) :: revTokens)
125 |
126 | else if isFunction s then
127 | loop (( T.C DeclarationKeyword, s ) :: revTokens) functionDeclarationLoop
128 |
129 | else if isKeyword s then
130 | succeed (( T.C Keyword, s ) :: revTokens)
131 |
132 | else if isLiteral s then
133 | succeed (( T.C Literal, s ) :: revTokens)
134 |
135 | else if isDeclarationKeyword s then
136 | succeed (( T.C DeclarationKeyword, s ) :: revTokens)
137 |
138 | else
139 | succeed (( T.Normal, s ) :: revTokens)
140 |
141 |
142 | functionDeclarationLoop : List Token -> Parser (Step (List Token) (List Token))
143 | functionDeclarationLoop revTokens =
144 | oneOf
145 | [ whitespaceOrCommentStep revTokens
146 | , chompIfThenWhile isIdentifierNameChar
147 | |> getChompedString
148 | |> map (\b -> Loop (( T.C Function, b ) :: revTokens))
149 | , symbol "*"
150 | |> map (\_ -> Loop (( T.C Keyword, "*" ) :: revTokens))
151 | , succeed (Done revTokens)
152 | ]
153 |
154 |
155 | argLoop : List Token -> Parser (Step (List Token) (List Token))
156 | argLoop revTokens =
157 | oneOf
158 | [ whitespaceOrCommentStep revTokens
159 | , chompIfThenWhile (\c -> not (isCommentChar c || isWhitespace c || c == ',' || c == ')'))
160 | |> getChompedString
161 | |> map (\b -> Loop (( T.C Param, b ) :: revTokens))
162 | , chompIfThenWhile (\c -> c == '/' || c == ',')
163 | |> getChompedString
164 | |> map (\b -> Loop (( T.Normal, b ) :: revTokens))
165 | , succeed (Done revTokens)
166 | ]
167 |
168 |
169 | isKeyword : String -> Bool
170 | isKeyword =
171 | Regex.contains keywordPattern
172 |
173 |
174 | keywordPattern : Regex
175 | keywordPattern =
176 | "^(actual|abstract|annotation|break|by|catch|class|companion|const|constructor|continue|coroutine|crossinline|data|delegate|dynamic|do|else|enum|expect|external|final|finally|for|fun|get|if|import|infix|inline|interface|internal|lazy|lateinit|native|object|open|operator|out|override|package|private|protected|public|reified|return|sealed|set|super|suspend|tailrec|this|throw|try|typealias|typeof|when|while|yield)$"
177 | |> Regex.fromStringWith { caseInsensitive = False, multiline = False }
178 | |> Maybe.withDefault Regex.never
179 |
180 |
181 | isLiteral : String -> Bool
182 | isLiteral str =
183 | Set.member str literalSet
184 |
185 |
186 | literalSet : Set String
187 | literalSet =
188 | Set.fromList [ "false", "true", "null" ]
189 |
190 |
191 | isFunction : String -> Bool
192 | isFunction =
193 | (==) "fun"
194 |
195 |
196 | isOperator : String -> Bool
197 | isOperator =
198 | Regex.contains operatorPattern
199 |
200 |
201 | operatorPattern : Regex
202 | operatorPattern =
203 | "^([=!<>\\+]=?|\\+|\\|\\||&&|as\\??|!?in|!?is)$"
204 | |> Regex.fromStringWith { caseInsensitive = False, multiline = False }
205 | |> Maybe.withDefault Regex.never
206 |
207 |
208 |
209 | -- Strings
210 |
211 |
212 | stringLiteral : Parser (List Token)
213 | stringLiteral =
214 | oneOf
215 | [ multilineString
216 | , quote
217 | , doubleQuote
218 | ]
219 |
220 |
221 | quote : Parser (List Token)
222 | quote =
223 | delimited quoteDelimiter
224 |
225 |
226 | quoteDelimiter : Delimiter Token
227 | quoteDelimiter =
228 | { start = "'"
229 | , end = "'"
230 | , isNestable = False
231 | , defaultMap = \b -> ( T.C String, b )
232 | , innerParsers = [ lineBreakList, ktEscapable ]
233 | , isNotRelevant = \c -> not (isLineBreak c || isEscapable c)
234 | }
235 |
236 |
237 | multilineString : Parser (List Token)
238 | multilineString =
239 | delimited
240 | { quoteDelimiter
241 | | start = "\"\"\""
242 | , end = "\"\"\""
243 | }
244 |
245 |
246 | doubleQuote : Parser (List Token)
247 | doubleQuote =
248 | delimited
249 | { quoteDelimiter
250 | | start = "\""
251 | , end = "\""
252 | }
253 |
254 |
255 | isStringLiteralChar : Char -> Bool
256 | isStringLiteralChar c =
257 | c == '\'' || c == '"'
258 |
259 |
260 |
261 | -- Comments
262 |
263 |
264 | comment : Parser (List Token)
265 | comment =
266 | oneOf
267 | [ inlineComment
268 | , multilineComment
269 | ]
270 |
271 |
272 | inlineComment : Parser (List Token)
273 | inlineComment =
274 | [ "//" ]
275 | |> List.map (symbol >> thenChompWhile (not << isLineBreak) >> getChompedString >> map (\b -> [ ( T.Comment, b ) ]))
276 | |> oneOf
277 |
278 |
279 | multilineComment : Parser (List Token)
280 | multilineComment =
281 | delimited
282 | { start = "/*"
283 | , end = "*/"
284 | , isNestable = False
285 | , defaultMap = \b -> ( T.Comment, b )
286 | , innerParsers = [ lineBreakList ]
287 | , isNotRelevant = not << isLineBreak
288 | }
289 |
290 |
291 |
292 | -- Helpers
293 |
294 |
295 | whitespaceOrCommentStep : List Token -> Parser (Step (List Token) (List Token))
296 | whitespaceOrCommentStep revTokens =
297 | oneOf
298 | [ space |> map (\s -> Loop (s :: revTokens))
299 | , lineBreak
300 | |> map (\s -> s :: revTokens)
301 | |> andThen checkContext
302 | , comment |> map (\s -> Loop (s ++ revTokens))
303 | ]
304 |
305 |
306 | checkContext : List Token -> Parser (Step (List Token) (List Token))
307 | checkContext revTokens =
308 | oneOf
309 | [ whitespaceOrCommentStep revTokens
310 | , succeed (Done revTokens)
311 | ]
312 |
313 |
314 | space : Parser Token
315 | space =
316 | chompIfThenWhile isSpace
317 | |> getChompedString
318 | |> map (\b -> ( T.Normal, b ))
319 |
320 |
321 | lineBreak : Parser Token
322 | lineBreak =
323 | symbol "\n"
324 | |> map (\_ -> ( T.LineBreak, "\n" ))
325 |
326 |
327 | lineBreakList : Parser (List Token)
328 | lineBreakList =
329 | symbol "\n"
330 | |> map (\_ -> [ ( T.LineBreak, "\n" ) ])
331 |
332 |
333 | number : Parser Token
334 | number =
335 | oneOf
336 | [ hexNumber
337 | , SyntaxHighlight.Language.Helpers.number
338 | ]
339 | |> getChompedString
340 | |> map (\b -> ( T.C Number, b ))
341 |
342 |
343 | hexNumber : Parser ()
344 | hexNumber =
345 | succeed ()
346 | |. backtrackable (symbol "0x")
347 | |. chompIfThenWhile Char.isHexDigit
348 |
349 |
350 | ktEscapable : Parser (List Token)
351 | ktEscapable =
352 | escapable
353 | |> getChompedString
354 | |> map (\b -> [ ( T.C Function, b ) ])
355 |
356 |
357 | isCommentChar : Char -> Bool
358 | isCommentChar c =
359 | c == '/'
360 |
361 |
362 | isIdentifierNameChar : Char -> Bool
363 | isIdentifierNameChar c =
364 | not
365 | (isPunctuationChar c
366 | || isStringLiteralChar c
367 | || isCommentChar c
368 | || isWhitespace c
369 | )
370 |
371 |
372 | syntaxToStyle : Syntax -> ( Style.Required, String )
373 | syntaxToStyle syntax =
374 | case syntax of
375 | Number ->
376 | ( Style1, "kt-n" )
377 |
378 | String ->
379 | ( Style2, "kt-s" )
380 |
381 | DeclarationKeyword ->
382 | ( Style3, "kt-dk" )
383 |
384 | Keyword ->
385 | ( Style3, "kt-k" )
386 |
387 | Operator ->
388 | ( Style4, "kt-o" )
389 |
390 | Function ->
391 | ( Style5, "kt-f" )
392 |
393 | Punctuation ->
394 | ( Style6, "kt-pu" )
395 |
396 | Literal ->
397 | ( Style7, "kt-l" )
398 |
399 | Param ->
400 | ( Style7, "kt-pa" )
401 |
--------------------------------------------------------------------------------
/src/SyntaxHighlight/Language/Sql.elm:
--------------------------------------------------------------------------------
1 | module SyntaxHighlight.Language.Sql exposing
2 | ( Syntax(..)
3 | , syntaxToStyle
4 | -- Exposing for test purposes
5 |
6 | , toLines
7 | , toRevTokens
8 | )
9 |
10 | import Parser exposing ((|.), DeadEnd, Parser, Step(..), andThen, backtrackable, getChompedString, loop, map, oneOf, succeed, symbol)
11 | import Regex exposing (Regex)
12 | import Set exposing (Set)
13 | import SyntaxHighlight.Language.Helpers exposing (Delimiter, chompIfThenWhile, delimited, escapable, isEscapable, isLineBreak, isSpace, isWhitespace, thenChompWhile)
14 | import SyntaxHighlight.Language.Type as T
15 | import SyntaxHighlight.Line exposing (Line)
16 | import SyntaxHighlight.Line.Helpers as Line
17 | import SyntaxHighlight.Style as Style exposing (Required(..))
18 |
19 |
20 | type alias Token =
21 | T.Token Syntax
22 |
23 |
24 | type Syntax
25 | = Number
26 | | String
27 | | Keyword
28 | | Operator
29 | | Function
30 | | Punctuation
31 | | Literal
32 |
33 |
34 | toLines : String -> Result (List DeadEnd) (List Line)
35 | toLines =
36 | Parser.run toRevTokens
37 | >> Result.map (Line.toLines syntaxToStyle)
38 |
39 |
40 | toRevTokens : Parser (List Token)
41 | toRevTokens =
42 | loop [] mainLoop
43 |
44 |
45 | mainLoop : List Token -> Parser (Step (List Token) (List Token))
46 | mainLoop revTokens =
47 | oneOf
48 | [ space
49 | |> map (\n -> Loop (n :: revTokens))
50 | , lineBreak
51 | |> map (\n -> Loop (n :: revTokens))
52 | , punctuationChar
53 | |> map (\n -> Loop (n :: revTokens))
54 | , number
55 | |> map (\n -> Loop (n :: revTokens))
56 | , comment
57 | |> map (\n -> Loop (n ++ revTokens))
58 | , stringLiteral
59 | |> andThen (\n -> loop (n ++ revTokens) stringBody)
60 | |> map Loop
61 | , chompIfThenWhile isIdentifierChar
62 | |> getChompedString
63 | |> andThen (keywordParser revTokens)
64 | |> map Loop
65 | , succeed (Done revTokens)
66 | ]
67 |
68 |
69 | isIdentifierChar : Char -> Bool
70 | isIdentifierChar c =
71 | not
72 | (isWhitespace c
73 | || isPunctuationChar c
74 | )
75 |
76 |
77 | stringBody : List Token -> Parser (Step (List Token) (List Token))
78 | stringBody revTokens =
79 | oneOf
80 | [ whitespaceOrCommentStep revTokens
81 | , stringLiteral |> map (\s -> Loop (s ++ revTokens))
82 | , succeed (Done revTokens)
83 | ]
84 |
85 |
86 | punctuationChar : Parser Token
87 | punctuationChar =
88 | chompIfThenWhile isPunctuationChar
89 | |> getChompedString
90 | |> map (\b -> ( T.C Punctuation, b ))
91 |
92 |
93 | isPunctuationChar : Char -> Bool
94 | isPunctuationChar c =
95 | Set.member c punctuatorSet
96 |
97 |
98 | punctuatorSet : Set Char
99 | punctuatorSet =
100 | Set.fromList [ ';', '[', ']', '(', ')', '`', ',', '.' ]
101 |
102 |
103 |
104 | -- Keywords
105 |
106 |
107 | keywordParser : List Token -> String -> Parser (List Token)
108 | keywordParser revTokens s =
109 | if isOperator s then
110 | succeed (( T.C Operator, s ) :: revTokens)
111 |
112 | else if isFunction s then
113 | succeed (( T.C Function, s ) :: revTokens)
114 |
115 | else if isKeyword s then
116 | succeed (( T.C Keyword, s ) :: revTokens)
117 |
118 | else if isLiteral s then
119 | succeed (( T.C Literal, s ) :: revTokens)
120 |
121 | else
122 | succeed (( T.Normal, s ) :: revTokens)
123 |
124 |
125 | isKeyword : String -> Bool
126 | isKeyword =
127 | Regex.contains keywordPattern
128 |
129 |
130 | keywordPattern : Regex
131 | keywordPattern =
132 | "^(ACTION|ADD|AFTER|ALGORITHM|ALL|ALTER|ANALYZE|ANY|APPLY|AS|ASC|AUTHORIZATION|AUTO_INCREMENT|BACKUP|BDB|BEGIN|BERKELEYDB|BIGINT|BINARY|BIT|BLOB|BOOL|BOOLEAN|BREAK|BROWSE|BTREE|BULK|BY|CALL|CASCADED?|CASE|CHAIN|CHAR(?:ACTER|SET)?|CHECK(?:POINT)?|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMNS?|COMMENT|COMMIT(?:TED)?|COMPUTE|CONNECT|CONSISTENT|CONSTRAINT|CONTAINS(?:TABLE)?|CONTINUE|CONVERT|CREATE|CROSS|CURRENT(?:_DATE|_TIME|_TIMESTAMP|_USER)?|CURSOR|CYCLE|DATA(?:BASES?)?|DATE(?:TIME)?|DAY|DBCC|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFAULT|DEFINER|DELAYED|DELETE|DELIMITERS?|DENY|DESC|DESCRIBE|DETERMINISTIC|DISABLE|DISCARD|DISK|DISTINCT|DISTINCTROW|DISTRIBUTED|DO|DOUBLE|DROP|DUMMY|DUMP(?:FILE)?|DUPLICATE|ELSE(?:IF)?|ENABLE|ENCLOSED|END|ENGINE|ENUM|ERRLVL|ERRORS|ESCAPED?|EXCEPT|EXEC(?:UTE)?|EXISTS|EXIT|EXPLAIN|EXTENDED|FETCH|FIELDS|FILE|FILLFACTOR|FIRST|FIXED|FLOAT|FOLLOWING|FOR(?: EACH ROW)?|FORCE|FOREIGN|FREETEXT(?:TABLE)?|FROM|FULL|FUNCTION|GEOMETRY(?:COLLECTION)?|GLOBAL|GOTO|GRANT|GROUP|HANDLER|HASH|HAVING|HOLDLOCK|HOUR|IDENTITY(?:_INSERT|COL)?|IF|IGNORE|IMPORT|INDEX|INFILE|INNER|INNODB|INOUT|INSERT|INT|INTEGER|INTERSECT|INTERVAL|INTO|INVOKER|ISOLATION|ITERATE|JOIN|KEYS?|KILL|LANGUAGE|LAST|LEAVE|LEFT|LEVEL|LIMIT|LINENO|LINES|LINESTRING|LOAD|LOCAL|LOCK|LONG(?:BLOB|TEXT)|LOOP|MATCH(?:ED)?|MEDIUM(?:BLOB|INT|TEXT)|MERGE|MIDDLEINT|MINUTE|MODE|MODIFIES|MODIFY|MONTH|MULTI(?:LINESTRING|POINT|POLYGON)|NATIONAL|NATURAL|NCHAR|NEXT|NO|NONCLUSTERED|NULLIF|NUMERIC|OFF?|OFFSETS?|ON|OPEN(?:DATASOURCE|QUERY|ROWSET)?|OPTIMIZE|OPTION(?:ALLY)?|ORDER|OUT(?:ER|FILE)?|OVER|PARTIAL|PARTITION|PERCENT|PIVOT|PLAN|POINT|POLYGON|PRECEDING|PRECISION|PREPARE|PREV|PRIMARY|PRINT|PRIVILEGES|PROC(?:EDURE)?|PUBLIC|PURGE|QUICK|RAISERROR|READS?|REAL|RECONFIGURE|REFERENCES|RELEASE|RENAME|REPEAT(?:ABLE)?|REPLACE|REPLICATION|REQUIRE|RESIGNAL|RESTORE|RESTRICT|RETURNS?|REVOKE|RIGHT|ROLLBACK|ROUTINE|ROW(?:COUNT|GUIDCOL|S)?|RTREE|RULE|SAVE(?:POINT)?|SCHEMA|SECOND|SELECT|SERIAL(?:IZABLE)?|SESSION(?:_USER)?|SET(?:USER)?|SHARE|SHOW|SHUTDOWN|SIMPLE|SMALLINT|SNAPSHOT|SOME|SONAME|SQL|START(?:ING)?|STATISTICS|STATUS|STRIPED|SYSTEM_USER|TABLES?|TABLESPACE|TEMP(?:ORARY|TABLE)?|TERMINATED|TEXT(?:SIZE)?|THEN|TIME(?:STAMP)?|TINY(?:BLOB|INT|TEXT)|TOP?|TRAN(?:SACTIONS?)?|TRIGGER|TRUNCATE|TSEQUAL|TYPES?|UNBOUNDED|UNCOMMITTED|UNDEFINED|UNION|UNIQUE|UNLOCK|UNPIVOT|UNSIGNED|UPDATE(?:TEXT)?|USAGE|USE|USER|USING|VALUES?|VAR(?:BINARY|CHAR|CHARACTER|YING)|VIEW|WAITFOR|WARNINGS|WHEN|WHERE|WHILE|WITH(?: ROLLUP|IN)?|WORK|WRITE(?:TEXT)?|YEAR)$"
133 | |> Regex.fromStringWith { caseInsensitive = True, multiline = False }
134 | |> Maybe.withDefault Regex.never
135 |
136 |
137 | isLiteral : String -> Bool
138 | isLiteral str =
139 | Set.member (String.toUpper str) literalSet
140 |
141 |
142 | literalSet : Set String
143 | literalSet =
144 | Set.fromList [ "TRUE", "FALSE", "NULL" ]
145 |
146 |
147 | isFunction : String -> Bool
148 | isFunction str =
149 | Set.member (String.toUpper str) functionSet
150 |
151 |
152 | functionSet : Set String
153 | functionSet =
154 | Set.fromList [ "AVG", "COUNT", "FIRST", "FORMAT", "LAST", "LCASE", "LEN", "MAX", "MID", "MIN", "MOD", "NOW", "ROUND", "SUM", "UCASE" ]
155 |
156 |
157 | isOperator : String -> Bool
158 | isOperator =
159 | Regex.contains operatorPattern
160 |
161 |
162 | operatorPattern : Regex
163 | operatorPattern =
164 | "^([-+*\\/=%^~]|&&?|\\|\\|?|!=?|<(?:=>?|<|>)?|>[>=]?|AND|BETWEEN|IN|LIKE|NOT|OR|IS|DIV|REGEXP|RLIKE|SOUNDS LIKE|XOR)$"
165 | |> Regex.fromStringWith { caseInsensitive = True, multiline = False }
166 | |> Maybe.withDefault Regex.never
167 |
168 |
169 |
170 | -- Strings
171 |
172 |
173 | stringLiteral : Parser (List Token)
174 | stringLiteral =
175 | oneOf
176 | [ quote
177 | , doubleQuote
178 | ]
179 |
180 |
181 | quote : Parser (List Token)
182 | quote =
183 | delimited quoteDelimiter
184 |
185 |
186 | quoteDelimiter : Delimiter Token
187 | quoteDelimiter =
188 | { start = "'"
189 | , end = "'"
190 | , isNestable = False
191 | , defaultMap = \b -> ( T.C String, b )
192 | , innerParsers = [ lineBreakList, sqlEscapable ]
193 | , isNotRelevant = \c -> not (isLineBreak c || isEscapable c)
194 | }
195 |
196 |
197 | doubleQuote : Parser (List Token)
198 | doubleQuote =
199 | delimited
200 | { quoteDelimiter
201 | | start = "\""
202 | , end = "\""
203 | }
204 |
205 |
206 | isStringLiteralChar : Char -> Bool
207 | isStringLiteralChar c =
208 | c == '\'' || c == '"'
209 |
210 |
211 |
212 | -- Comments
213 |
214 |
215 | comment : Parser (List Token)
216 | comment =
217 | oneOf
218 | [ inlineComment
219 | , multilineComment
220 | ]
221 |
222 |
223 | inlineComment : Parser (List Token)
224 | inlineComment =
225 | [ "--", "$", "#" ]
226 | |> List.map (symbol >> thenChompWhile (not << isLineBreak) >> getChompedString >> map (\b -> [ ( T.Comment, b ) ]))
227 | |> oneOf
228 |
229 |
230 | multilineComment : Parser (List Token)
231 | multilineComment =
232 | delimited
233 | { start = "/*"
234 | , end = "*/"
235 | , isNestable = False
236 | , defaultMap = \b -> ( T.Comment, b )
237 | , innerParsers = [ lineBreakList ]
238 | , isNotRelevant = not << isLineBreak
239 | }
240 |
241 |
242 |
243 | -- Helpers
244 |
245 |
246 | whitespaceOrCommentStep : List Token -> Parser (Step (List Token) (List Token))
247 | whitespaceOrCommentStep revTokens =
248 | oneOf
249 | [ space |> map (\s -> Loop (s :: revTokens))
250 | , lineBreak
251 | |> map (\s -> s :: revTokens)
252 | |> andThen checkContext
253 | , comment |> map (\s -> Loop (s ++ revTokens))
254 | ]
255 |
256 |
257 | checkContext : List Token -> Parser (Step (List Token) (List Token))
258 | checkContext revTokens =
259 | oneOf
260 | [ whitespaceOrCommentStep revTokens
261 | , succeed (Done revTokens)
262 | ]
263 |
264 |
265 | space : Parser Token
266 | space =
267 | chompIfThenWhile isSpace
268 | |> getChompedString
269 | |> map (\b -> ( T.Normal, b ))
270 |
271 |
272 | lineBreak : Parser Token
273 | lineBreak =
274 | symbol "\n"
275 | |> map (\_ -> ( T.LineBreak, "\n" ))
276 |
277 |
278 | lineBreakList : Parser (List Token)
279 | lineBreakList =
280 | symbol "\n"
281 | |> map (\_ -> [ ( T.LineBreak, "\n" ) ])
282 |
283 |
284 | number : Parser Token
285 | number =
286 | oneOf
287 | [ hexNumber
288 | , SyntaxHighlight.Language.Helpers.number
289 | ]
290 | |> getChompedString
291 | |> map (\b -> ( T.C Number, b ))
292 |
293 |
294 | hexNumber : Parser ()
295 | hexNumber =
296 | succeed ()
297 | |. backtrackable (symbol "0x")
298 | |. chompIfThenWhile Char.isHexDigit
299 |
300 |
301 | sqlEscapable : Parser (List Token)
302 | sqlEscapable =
303 | escapable
304 | |> getChompedString
305 | |> map (\b -> [ ( T.C Function, b ) ])
306 |
307 |
308 | syntaxToStyle : Syntax -> ( Style.Required, String )
309 | syntaxToStyle syntax =
310 | case syntax of
311 | Number ->
312 | ( Style1, "sql-n" )
313 |
314 | String ->
315 | ( Style2, "sql-s" )
316 |
317 | Keyword ->
318 | ( Style3, "sql-k" )
319 |
320 | Operator ->
321 | ( Style4, "sql-o" )
322 |
323 | Function ->
324 | ( Style5, "sql-f" )
325 |
326 | Punctuation ->
327 | ( Style6, "sql-p" )
328 |
329 | Literal ->
330 | ( Style7, "sql-l" )
331 |
--------------------------------------------------------------------------------
/src/SyntaxHighlight/Language/Go.elm:
--------------------------------------------------------------------------------
1 | module SyntaxHighlight.Language.Go exposing
2 | ( Syntax(..)
3 | , syntaxToStyle
4 | -- Exposing for tests purpose
5 | , toLines
6 | , toRevTokens
7 | )
8 |
9 | import Parser exposing ((|.), (|=), DeadEnd, Parser, Step(..), andThen, backtrackable, chompIf, getChompedString, loop, map, oneOf, succeed, symbol)
10 | import Set exposing (Set)
11 | import SyntaxHighlight.Language.Helpers exposing (Delimiter, chompIfThenWhile, delimited, isLineBreak, isSpace, isWhitespace, thenChompWhile)
12 | import SyntaxHighlight.Language.Type as T
13 | import SyntaxHighlight.Line exposing (Line)
14 | import SyntaxHighlight.Line.Helpers as Line
15 | import SyntaxHighlight.Style as Style exposing (Required(..))
16 |
17 |
18 | type alias Token =
19 | T.Token Syntax
20 |
21 |
22 | type Syntax
23 | = Number
24 | | String
25 | | Keyword
26 | | DeclarationKeyword
27 | | Type
28 | | Function
29 | | LiteralKeyword
30 | | Package
31 | | Operator
32 | | Param
33 |
34 |
35 | toLines : String -> Result (List DeadEnd) (List Line)
36 | toLines =
37 | Parser.run toRevTokens
38 | >> Result.map (Line.toLines syntaxToStyle)
39 |
40 |
41 | toRevTokens : Parser (List Token)
42 | toRevTokens =
43 | loop [] mainLoop
44 |
45 |
46 | mainLoop : List Token -> Parser (Step (List Token) (List Token))
47 | mainLoop revTokens =
48 | oneOf
49 | [ whitespaceOrCommentStep revTokens
50 | , stringLiteral
51 | |> map (\s -> Loop (s ++ revTokens))
52 | , oneOf
53 | [ operatorChar
54 | , groupChar
55 | , number
56 | ]
57 | |> map (\s -> Loop (s :: revTokens))
58 | , chompIfThenWhile isIdentifierNameChar
59 | |> getChompedString
60 | |> andThen (keywordParser revTokens)
61 | |> map Loop
62 |
63 | -- TODO: Unknown token found, ignore until the end of the line
64 | , chompIfThenWhile (isLineBreak >> not)
65 | |> getChompedString
66 | |> andThen (\str -> succeed (( T.Normal, str ) :: revTokens))
67 | |> map Loop
68 | , succeed (Done revTokens)
69 | ]
70 |
71 |
72 | keywordParser : List Token -> String -> Parser (List Token)
73 | keywordParser revTokens n =
74 | if n == "func" then
75 | loop (( T.C DeclarationKeyword, n ) :: revTokens) functionDeclarationLoop
76 |
77 | else if isType n then
78 | succeed (( T.C Type, n ) :: revTokens)
79 |
80 | else if isPackage n then
81 | succeed (( T.C Package, n ) :: revTokens)
82 |
83 | else if isKeyword n then
84 | succeed (( T.C Keyword, n ) :: revTokens)
85 |
86 | else if isDeclarationKeyword n then
87 | succeed (( T.C DeclarationKeyword, n ) :: revTokens)
88 |
89 | else if isLiteralKeyword n then
90 | succeed (( T.C LiteralKeyword, n ) :: revTokens)
91 |
92 | else
93 | succeed (( T.Normal, n ) :: revTokens)
94 |
95 |
96 | functionDeclarationLoop : List Token -> Parser (Step (List Token) (List Token))
97 | functionDeclarationLoop revTokens =
98 | oneOf
99 | [ whitespaceOrCommentStep revTokens
100 | , chompIfThenWhile isIdentifierNameChar
101 | |> getChompedString
102 | |> map (\b -> Loop (( T.C Function, b ) :: revTokens))
103 | , symbol "("
104 | |> andThen
105 | (\_ -> loop (( T.Normal, "(" ) :: revTokens) argLoop)
106 | |> map Loop
107 | , succeed (Done revTokens)
108 | ]
109 |
110 |
111 | argLoop : List Token -> Parser (Step (List Token) (List Token))
112 | argLoop revTokens =
113 | oneOf
114 | [ whitespaceOrCommentStep revTokens
115 | , chompIfThenWhile (\c -> not (isCommentChar c || isWhitespace c || c == ',' || c == ')'))
116 | |> getChompedString
117 | |> andThen (argParser revTokens)
118 | |> map Loop
119 | , chompIfThenWhile (\c -> c == '/' || c == ',')
120 | |> getChompedString
121 | |> map (\b -> Loop (( T.Normal, b ) :: revTokens))
122 | , succeed (Done revTokens)
123 | ]
124 |
125 |
126 | argParser : List Token -> String -> Parser (List Token)
127 | argParser revTokens n =
128 | if isType n || String.startsWith "[]" n then
129 | succeed (( T.C Type, n ) :: revTokens)
130 |
131 | else
132 | succeed (( T.C Param, n ) :: revTokens)
133 |
134 |
135 | isIdentifierNameChar : Char -> Bool
136 | isIdentifierNameChar c =
137 | Char.isAlphaNum c || c == '_' || Char.toCode c > 127
138 |
139 |
140 |
141 | -- numbers
142 |
143 |
144 | number : Parser Token
145 | number =
146 | oneOf
147 | [ hexNumber
148 | , SyntaxHighlight.Language.Helpers.number
149 | , SyntaxHighlight.Language.Helpers.numberExponentialNotation
150 | ]
151 | |> getChompedString
152 | |> map (\b -> ( T.C Number, b ))
153 |
154 |
155 | hexNumber : Parser ()
156 | hexNumber =
157 | succeed ()
158 | |. backtrackable (symbol "0x")
159 | |. chompIfThenWhile Char.isHexDigit
160 |
161 |
162 |
163 | -- Reserved Words
164 |
165 |
166 | isKeyword : String -> Bool
167 | isKeyword str =
168 | Set.member str keywords
169 |
170 |
171 | keywords : Set String
172 | keywords =
173 | Set.fromList
174 | [ "break"
175 | , "case"
176 | , "chan"
177 | , "continue"
178 | , "default"
179 | , "defer"
180 | , "else"
181 | , "fallthrough"
182 | , "for"
183 | , "go"
184 | , "goto"
185 | , "if"
186 | , "interface"
187 | , "map"
188 | , "range"
189 | , "return"
190 | , "select"
191 | , "struct"
192 | , "switch"
193 | ]
194 |
195 |
196 | isType : String -> Bool
197 | isType str =
198 | Set.member str types
199 |
200 |
201 | types : Set String
202 | types =
203 | Set.fromList
204 | [ "bool"
205 | , "byte"
206 | , "complex64"
207 | , "complex128"
208 | , "error"
209 | , "float32"
210 | , "float64"
211 | , "int"
212 | , "int8"
213 | , "int16"
214 | , "int32"
215 | , "int64"
216 | , "rune"
217 | , "string"
218 | , "uint"
219 | , "uint8"
220 | , "uint16"
221 | , "uint32"
222 | , "uint64"
223 | , "uintptr"
224 | ]
225 |
226 |
227 | isDeclarationKeyword : String -> Bool
228 | isDeclarationKeyword str =
229 | Set.member str declarationKeywords
230 |
231 |
232 | declarationKeywords : Set String
233 | declarationKeywords =
234 | Set.fromList
235 | [ "func"
236 | , "type"
237 | , "var"
238 | , "const"
239 | , "import"
240 | ]
241 |
242 |
243 | isPackage : String -> Bool
244 | isPackage str =
245 | str == "package"
246 |
247 |
248 | operatorChar : Parser Token
249 | operatorChar =
250 | chompIf isOperator
251 | |> getChompedString
252 | |> map (\s -> ( T.C Operator, s ))
253 |
254 |
255 | isOperator : Char -> Bool
256 | isOperator c =
257 | Set.member c operatorSet
258 |
259 |
260 | operatorSet : Set Char
261 | operatorSet =
262 | Set.fromList [ '+', '-', '*', '/', '%', '&', '|', '^', '<', '>', '=', '!', ':', '.' ]
263 |
264 |
265 | groupChar : Parser Token
266 | groupChar =
267 | chompIf isGroupChar
268 | |> getChompedString
269 | |> map (\s -> ( T.Normal, s ))
270 |
271 |
272 | isGroupChar : Char -> Bool
273 | isGroupChar c =
274 | Set.member c groupCharSet
275 |
276 |
277 | groupCharSet : Set Char
278 | groupCharSet =
279 | Set.fromList [ '(', ')', '[', ']', '{', '}', ',', ';' ]
280 |
281 |
282 | isLiteralKeyword : String -> Bool
283 | isLiteralKeyword str =
284 | Set.member str literalKeywordSet
285 |
286 |
287 | literalKeywordSet : Set String
288 | literalKeywordSet =
289 | Set.fromList
290 | [ "true"
291 | , "false"
292 | , "nil"
293 | ]
294 |
295 |
296 |
297 | -- String literal
298 |
299 |
300 | stringLiteral : Parser (List Token)
301 | stringLiteral =
302 | oneOf
303 | [ rawString
304 | , doubleQuoteString
305 | , singleQuoteString
306 | ]
307 |
308 |
309 | rawString : Parser (List Token)
310 | rawString =
311 | let
312 | delimiter : Delimiter (T.Token Syntax)
313 | delimiter =
314 | { start = "`"
315 | , end = "`"
316 | , isNestable = False
317 | , defaultMap = \b -> ( T.C String, b )
318 | , innerParsers = []
319 | , isNotRelevant = \_ -> True
320 | }
321 | in
322 | delimited delimiter
323 |
324 |
325 | singleQuoteString : Parser (List Token)
326 | singleQuoteString =
327 | let
328 | delimiter : Delimiter (T.Token Syntax)
329 | delimiter =
330 | { start = "'"
331 | , end = "'"
332 | , isNestable = False
333 | , defaultMap = \b -> ( T.C String, b )
334 | , innerParsers = []
335 | , isNotRelevant = \c -> not (c == '"')
336 | }
337 | in
338 | delimited delimiter
339 |
340 |
341 | doubleQuoteString : Parser (List Token)
342 | doubleQuoteString =
343 | let
344 | delimiter : Delimiter (T.Token Syntax)
345 | delimiter =
346 | { start = "\""
347 | , end = "\""
348 | , isNestable = False
349 | , defaultMap = \b -> ( T.C String, b )
350 | , innerParsers = []
351 | , isNotRelevant = \c -> not (c == '"')
352 | }
353 | in
354 | delimited delimiter
355 |
356 |
357 |
358 | -- Comments
359 |
360 |
361 | comment : Parser (List Token)
362 | comment =
363 | oneOf
364 | [ inlineComment
365 | , multilineComment
366 | ]
367 |
368 |
369 | inlineComment : Parser (List Token)
370 | inlineComment =
371 | symbol "//"
372 | |> thenChompWhile (not << isLineBreak)
373 | |> getChompedString
374 | |> map (\b -> [ ( T.Comment, b ) ])
375 |
376 |
377 | multilineComment : Parser (List Token)
378 | multilineComment =
379 | delimited
380 | { start = "/*"
381 | , end = "*/"
382 | , isNestable = False
383 | , defaultMap = \b -> ( T.Comment, b )
384 | , innerParsers = [ lineBreakList ]
385 | , isNotRelevant = \c -> not (isLineBreak c)
386 | }
387 |
388 |
389 | isCommentChar : Char -> Bool
390 | isCommentChar c =
391 | c == '/'
392 |
393 |
394 |
395 | -- Helpers
396 |
397 |
398 | whitespaceOrCommentStep : List Token -> Parser (Step (List Token) (List Token))
399 | whitespaceOrCommentStep revTokens =
400 | oneOf
401 | [ chompIfThenWhile isSpace
402 | |> getChompedString
403 | |> map (\b -> Loop (( T.Normal, b ) :: revTokens))
404 | , lineBreakList
405 | |> map (\ns -> Loop (ns ++ revTokens))
406 | , comment
407 | |> map (\ns -> Loop (ns ++ revTokens))
408 | ]
409 |
410 |
411 | lineBreakList : Parser (List Token)
412 | lineBreakList =
413 | symbol "\n"
414 | |> map (\_ -> [ ( T.LineBreak, "\n" ) ])
415 |
416 |
417 | syntaxToStyle : Syntax -> ( Style.Required, String )
418 | syntaxToStyle syntax =
419 | case syntax of
420 | Number ->
421 | ( Style1, "go-n" )
422 |
423 | String ->
424 | ( Style2, "go-s" )
425 |
426 | Keyword ->
427 | ( Style3, "go-k" )
428 |
429 | DeclarationKeyword ->
430 | ( Style3, "go-dk" )
431 |
432 | Type ->
433 | ( Style4, "go-t" )
434 |
435 | Function ->
436 | ( Style5, "go-f" )
437 |
438 | LiteralKeyword ->
439 | ( Style6, "go-lk" )
440 |
441 | Package ->
442 | ( Style3, "go-p" )
443 |
444 | Operator ->
445 | ( Style3, "go-o" )
446 |
447 | Param ->
448 | ( Style7, "go-param" )
449 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
--------------------------------------------------------------------------------
/src/SyntaxHighlight/Language/Javascript.elm:
--------------------------------------------------------------------------------
1 | module SyntaxHighlight.Language.Javascript exposing
2 | ( Syntax(..)
3 | , syntaxToStyle
4 | -- Exposing for tests purpose
5 |
6 | , toLines
7 | , toRevTokens
8 | )
9 |
10 | import Parser exposing ((|.), DeadEnd, Parser, Step(..), andThen, chompIf, getChompedString, keyword, loop, map, oneOf, succeed, symbol)
11 | import Set exposing (Set)
12 | import SyntaxHighlight.Language.Helpers exposing (Delimiter, chompIfThenWhile, delimited, escapable, isEscapable, isLineBreak, isSpace, isWhitespace, thenChompWhile)
13 | import SyntaxHighlight.Language.Type as T
14 | import SyntaxHighlight.Line exposing (Line)
15 | import SyntaxHighlight.Line.Helpers as Line
16 | import SyntaxHighlight.Style as Style exposing (Required(..))
17 |
18 |
19 | type alias Token =
20 | T.Token Syntax
21 |
22 |
23 | type Syntax
24 | = Number
25 | | String
26 | | Keyword
27 | | DeclarationKeyword
28 | | FunctionEval
29 | | Function
30 | | LiteralKeyword
31 | | Param
32 | | ClassExtends
33 |
34 |
35 | toLines : String -> Result (List DeadEnd) (List Line)
36 | toLines =
37 | Parser.run toRevTokens
38 | >> Result.map (Line.toLines syntaxToStyle)
39 |
40 |
41 | toRevTokens : Parser (List Token)
42 | toRevTokens =
43 | loop [] mainLoop
44 |
45 |
46 | mainLoop : List Token -> Parser (Step (List Token) (List Token))
47 | mainLoop revTokens =
48 | oneOf
49 | [ whitespaceOrCommentStep revTokens
50 | , stringLiteral
51 | |> map (\s -> Loop (s ++ revTokens))
52 | , oneOf
53 | [ operatorChar
54 | , groupChar
55 | , number
56 | ]
57 | |> map (\s -> Loop (s :: revTokens))
58 | , chompIfThenWhile isIdentifierNameChar
59 | |> getChompedString
60 | |> andThen (keywordParser revTokens)
61 | |> map Loop
62 | , succeed (Done revTokens)
63 | ]
64 |
65 |
66 | keywordParser : List Token -> String -> Parser (List Token)
67 | keywordParser revTokens n =
68 | if n == "function" || n == "static" then
69 | loop (( T.C DeclarationKeyword, n ) :: revTokens) functionDeclarationLoop
70 |
71 | else if n == "class" then
72 | loop (( T.C DeclarationKeyword, n ) :: revTokens) classDeclarationLoop
73 |
74 | else if n == "this" || n == "super" then
75 | succeed (( T.C Param, n ) :: revTokens)
76 |
77 | else if n == "constructor" then
78 | loop (( T.C Function, n ) :: revTokens) functionDeclarationLoop
79 |
80 | else if isKeyword n then
81 | succeed (( T.C Keyword, n ) :: revTokens)
82 |
83 | else if isDeclarationKeyword n then
84 | succeed (( T.C DeclarationKeyword, n ) :: revTokens)
85 |
86 | else if isLiteralKeyword n then
87 | succeed (( T.C LiteralKeyword, n ) :: revTokens)
88 |
89 | else
90 | loop [] (functionEvalLoop n revTokens)
91 |
92 |
93 | functionDeclarationLoop : List Token -> Parser (Step (List Token) (List Token))
94 | functionDeclarationLoop revTokens =
95 | oneOf
96 | [ whitespaceOrCommentStep revTokens
97 | , chompIfThenWhile isIdentifierNameChar
98 | |> getChompedString
99 | |> map (\b -> Loop (( T.C Function, b ) :: revTokens))
100 | , symbol "*"
101 | |> map (\_ -> Loop (( T.C Keyword, "*" ) :: revTokens))
102 | , symbol "("
103 | |> andThen
104 | (\_ -> loop (( T.Normal, "(" ) :: revTokens) argLoop)
105 | |> map Loop
106 | , succeed (Done revTokens)
107 | ]
108 |
109 |
110 | argLoop : List Token -> Parser (Step (List Token) (List Token))
111 | argLoop revTokens =
112 | oneOf
113 | [ whitespaceOrCommentStep revTokens
114 | , chompIfThenWhile (\c -> not (isCommentChar c || isWhitespace c || c == ',' || c == ')'))
115 | |> getChompedString
116 | |> map (\b -> Loop (( T.C Param, b ) :: revTokens))
117 | , chompIfThenWhile (\c -> c == '/' || c == ',')
118 | |> getChompedString
119 | |> map (\b -> Loop (( T.Normal, b ) :: revTokens))
120 | , succeed (Done revTokens)
121 | ]
122 |
123 |
124 | functionEvalLoop : String -> List Token -> List Token -> Parser (Step (List Token) (List Token))
125 | functionEvalLoop identifier revTokens thisRevToken =
126 | oneOf
127 | [ whitespaceOrCommentStep thisRevToken
128 | , symbol "("
129 | |> map
130 | (\_ -> Done ((( T.Normal, "(" ) :: thisRevToken) ++ (( T.C FunctionEval, identifier ) :: revTokens)))
131 | , thisRevToken
132 | ++ (( T.Normal, identifier ) :: revTokens)
133 | |> Done
134 | |> succeed
135 | ]
136 |
137 |
138 | classDeclarationLoop : List Token -> Parser (Step (List Token) (List Token))
139 | classDeclarationLoop revTokens =
140 | oneOf
141 | [ whitespaceOrCommentStep revTokens
142 | , chompIfThenWhile isIdentifierNameChar
143 | |> getChompedString
144 | |> andThen
145 | (\n ->
146 | if n == "extends" then
147 | loop (( T.C Keyword, n ) :: revTokens) classExtendsLoop
148 | |> map Loop
149 |
150 | else
151 | succeed (Loop (( T.C Function, n ) :: revTokens))
152 | )
153 | , succeed (Done revTokens)
154 | ]
155 |
156 |
157 | classExtendsLoop : List Token -> Parser (Step (List Token) (List Token))
158 | classExtendsLoop revTokens =
159 | oneOf
160 | [ whitespaceOrCommentStep revTokens
161 | , chompIfThenWhile isIdentifierNameChar
162 | |> getChompedString
163 | |> map (\b -> Loop (( T.C ClassExtends, b ) :: revTokens))
164 | , succeed (Done revTokens)
165 | ]
166 |
167 |
168 | isIdentifierNameChar : Char -> Bool
169 | isIdentifierNameChar c =
170 | not
171 | (isPunctuaction c
172 | || isStringLiteralChar c
173 | || isCommentChar c
174 | || isWhitespace c
175 | )
176 |
177 |
178 |
179 | -- Reserved Words
180 |
181 |
182 | isKeyword : String -> Bool
183 | isKeyword str =
184 | Set.member str keywordSet
185 |
186 |
187 | keywordSet : Set String
188 | keywordSet =
189 | Set.fromList
190 | [ "break"
191 | , "do"
192 | , "instanceof"
193 | , "typeof"
194 | , "case"
195 | , "else"
196 | , "new"
197 | , "catch"
198 | , "finally"
199 | , "return"
200 | , "void"
201 | , "continue"
202 | , "for"
203 | , "switch"
204 | , "while"
205 | , "debugger"
206 | , "this"
207 | , "with"
208 | , "default"
209 | , "if"
210 | , "throw"
211 | , "delete"
212 | , "in"
213 | , "try"
214 | , "enum"
215 | , "extends"
216 | , "export"
217 | , "import"
218 | , "implements"
219 | , "private"
220 | , "public"
221 | , "yield"
222 | , "interface"
223 | , "package"
224 | , "protected"
225 | ]
226 |
227 |
228 | isDeclarationKeyword : String -> Bool
229 | isDeclarationKeyword str =
230 | Set.member str declarationKeywordSet
231 |
232 |
233 | declarationKeywordSet : Set String
234 | declarationKeywordSet =
235 | Set.fromList
236 | [ "var"
237 | , "const"
238 | , "let"
239 | ]
240 |
241 |
242 | isPunctuaction : Char -> Bool
243 | isPunctuaction c =
244 | Set.member c punctuactorSet
245 |
246 |
247 | punctuactorSet : Set Char
248 | punctuactorSet =
249 | Set.union operatorSet groupSet
250 |
251 |
252 | operatorChar : Parser Token
253 | operatorChar =
254 | chompIfThenWhile isOperatorChar
255 | |> getChompedString
256 | |> map (\b -> ( T.C Keyword, b ))
257 |
258 |
259 | isOperatorChar : Char -> Bool
260 | isOperatorChar c =
261 | Set.member c operatorSet
262 |
263 |
264 | operatorSet : Set Char
265 | operatorSet =
266 | Set.fromList
267 | [ '+'
268 | , '-'
269 | , '*'
270 | , '/'
271 | , '='
272 | , '!'
273 | , '<'
274 | , '>'
275 | , '&'
276 | , '|'
277 | , '?'
278 | , '^'
279 | , ':'
280 | , '~'
281 | , '%'
282 | , '.'
283 | ]
284 |
285 |
286 | groupChar : Parser Token
287 | groupChar =
288 | chompIfThenWhile isGroupChar
289 | |> getChompedString
290 | |> map (\b -> ( T.Normal, b ))
291 |
292 |
293 | isGroupChar : Char -> Bool
294 | isGroupChar c =
295 | Set.member c groupSet
296 |
297 |
298 | groupSet : Set Char
299 | groupSet =
300 | Set.fromList
301 | [ '{'
302 | , '}'
303 | , '('
304 | , ')'
305 | , '['
306 | , ']'
307 | , ','
308 | , ';'
309 | ]
310 |
311 |
312 | isLiteralKeyword : String -> Bool
313 | isLiteralKeyword str =
314 | Set.member str literalKeywordSet
315 |
316 |
317 | literalKeywordSet : Set String
318 | literalKeywordSet =
319 | Set.fromList
320 | [ "true"
321 | , "false"
322 | , "null"
323 | , "undefined"
324 | , "NaN"
325 | , "Infinity"
326 | ]
327 |
328 |
329 |
330 | -- String literal
331 |
332 |
333 | stringLiteral : Parser (List Token)
334 | stringLiteral =
335 | oneOf
336 | [ quote
337 | , doubleQuote
338 | , templateString
339 | ]
340 |
341 |
342 | quote : Parser (List Token)
343 | quote =
344 | delimited quoteDelimiter
345 |
346 |
347 | quoteDelimiter : Delimiter Token
348 | quoteDelimiter =
349 | { start = "'"
350 | , end = "'"
351 | , isNestable = False
352 | , defaultMap = \b -> ( T.C String, b )
353 | , innerParsers = [ lineBreakList, jsEscapable ]
354 | , isNotRelevant = \c -> not (isLineBreak c || isEscapable c)
355 | }
356 |
357 |
358 | doubleQuote : Parser (List Token)
359 | doubleQuote =
360 | delimited
361 | { quoteDelimiter
362 | | start = "\""
363 | , end = "\""
364 | }
365 |
366 |
367 | templateString : Parser (List Token)
368 | templateString =
369 | delimited
370 | { quoteDelimiter
371 | | start = "`"
372 | , end = "`"
373 | , innerParsers = [ lineBreakList, jsEscapable ]
374 | , isNotRelevant = \c -> not (isLineBreak c || isEscapable c)
375 | }
376 |
377 |
378 | isStringLiteralChar : Char -> Bool
379 | isStringLiteralChar c =
380 | c == '"' || c == '\'' || c == '`'
381 |
382 |
383 |
384 | -- Comments
385 |
386 |
387 | comment : Parser (List Token)
388 | comment =
389 | oneOf
390 | [ inlineComment
391 | , multilineComment
392 | ]
393 |
394 |
395 | inlineComment : Parser (List Token)
396 | inlineComment =
397 | symbol "//"
398 | |> thenChompWhile (not << isLineBreak)
399 | |> getChompedString
400 | |> map (\b -> [ ( T.Comment, b ) ])
401 |
402 |
403 | multilineComment : Parser (List Token)
404 | multilineComment =
405 | delimited
406 | { start = "/*"
407 | , end = "*/"
408 | , isNestable = False
409 | , defaultMap = \b -> ( T.Comment, b )
410 | , innerParsers = [ lineBreakList ]
411 | , isNotRelevant = \c -> not (isLineBreak c)
412 | }
413 |
414 |
415 | isCommentChar : Char -> Bool
416 | isCommentChar c =
417 | c == '/'
418 |
419 |
420 |
421 | -- Helpers
422 |
423 |
424 | whitespaceOrCommentStep : List Token -> Parser (Step (List Token) (List Token))
425 | whitespaceOrCommentStep revTokens =
426 | oneOf
427 | [ chompIfThenWhile isSpace
428 | |> getChompedString
429 | |> map (\b -> Loop (( T.Normal, b ) :: revTokens))
430 | , lineBreakList
431 | |> map (\ns -> Loop (ns ++ revTokens))
432 | , comment
433 | |> map (\ns -> Loop (ns ++ revTokens))
434 | ]
435 |
436 |
437 | lineBreakList : Parser (List Token)
438 | lineBreakList =
439 | symbol "\n"
440 | |> map (\_ -> [ ( T.LineBreak, "\n" ) ])
441 |
442 |
443 | number : Parser Token
444 | number =
445 | SyntaxHighlight.Language.Helpers.number
446 | |> getChompedString
447 | |> map (\b -> ( T.C Number, b ))
448 |
449 |
450 | jsEscapable : Parser (List Token)
451 | jsEscapable =
452 | escapable
453 | |> getChompedString
454 | |> map (\b -> [ ( T.C LiteralKeyword, b ) ])
455 |
456 |
457 | syntaxToStyle : Syntax -> ( Style.Required, String )
458 | syntaxToStyle syntax =
459 | case syntax of
460 | Number ->
461 | ( Style1, "js-n" )
462 |
463 | String ->
464 | ( Style2, "js-s" )
465 |
466 | Keyword ->
467 | ( Style3, "js-k" )
468 |
469 | DeclarationKeyword ->
470 | ( Style4, "js-dk" )
471 |
472 | FunctionEval ->
473 | ( Style4, "js-fe" )
474 |
475 | Function ->
476 | ( Style5, "js-f" )
477 |
478 | LiteralKeyword ->
479 | ( Style6, "js-lk" )
480 |
481 | Param ->
482 | ( Style7, "js-p" )
483 |
484 | ClassExtends ->
485 | ( Style5, "js-ce" )
486 |
--------------------------------------------------------------------------------
/src/SyntaxHighlight.elm:
--------------------------------------------------------------------------------
1 | module SyntaxHighlight exposing
2 | ( HCode
3 | , toBlockHtml, toInlineHtml, toStaticBlockHtml, toStaticInlineHtml
4 | , Highlight(..), highlightLines
5 | , css, elm, javascript, python, sql, xml, json, nix, kotlin, go, noLang
6 | , Theme, useTheme, monokai, gitHub, oneDark
7 | , ConsoleOptions, toConsole
8 | , CustomTransform, toCustom
9 | )
10 |
11 | {-| Syntax highlighting in Elm.
12 |
13 | @docs HCode
14 |
15 |
16 | ## Html view
17 |
18 | @docs toBlockHtml, toInlineHtml, toStaticBlockHtml, toStaticInlineHtml
19 |
20 |
21 | ## Helpers
22 |
23 | @docs Highlight, highlightLines
24 |
25 |
26 | ## Languages
27 |
28 | Error while parsing should not happen. If it happens, please [open an issue](https://github.com/pablohirafuji/elm-syntax-highlight/issues) with the code that gives the error and the language.
29 |
30 | @docs css, elm, javascript, python, sql, xml, json, nix, kotlin, go, noLang
31 |
32 |
33 | ## Themes
34 |
35 | @docs Theme, useTheme, monokai, gitHub, oneDark
36 |
37 |
38 | ## Console view
39 |
40 | @docs ConsoleOptions, toConsole
41 |
42 |
43 | ## Custom transformation
44 |
45 | @docs CustomTransform, toCustom
46 |
47 | -}
48 |
49 | import Html exposing (Html, text)
50 | import Parser
51 | import SyntaxHighlight.Language.Css as Css
52 | import SyntaxHighlight.Language.Elm as Elm
53 | import SyntaxHighlight.Language.Go as Go
54 | import SyntaxHighlight.Language.Javascript as Javascript
55 | import SyntaxHighlight.Language.Json as Json
56 | import SyntaxHighlight.Language.Kotlin as Kotlin
57 | import SyntaxHighlight.Language.Nix as Nix
58 | import SyntaxHighlight.Language.NoLang as NoLang
59 | import SyntaxHighlight.Language.Python as Python
60 | import SyntaxHighlight.Language.Sql as Sql
61 | import SyntaxHighlight.Language.Xml as Xml
62 | import SyntaxHighlight.Line as Line exposing (Highlight, Line)
63 | import SyntaxHighlight.Style as Style
64 | import SyntaxHighlight.Theme as Theme
65 | import SyntaxHighlight.View as View
66 |
67 |
68 | {-| A highlighted code.
69 | -}
70 | type HCode
71 | = HCode (List Line)
72 |
73 |
74 | {-| Transform a highlighted code into a Html block.
75 | The `Maybe Int` argument is for showing or not line count and, if so, starting from what number.
76 | -}
77 | toBlockHtml : Maybe Int -> HCode -> Html msg
78 | toBlockHtml maybeStart (HCode lines) =
79 | View.toBlockHtml maybeStart lines
80 |
81 |
82 | {-| Transform a highlighted code into inline Html.
83 |
84 | import SyntaxHighlight exposing (elm, toInlineHtml)
85 |
86 | info : Html msg
87 | info =
88 | p []
89 | [ text "This function signature "
90 | , elm "isEmpty : String -> Bool"
91 | |> Result.map toInlineHtml
92 | |> Result.withDefault
93 | (code [] [ text "isEmpty : String -> Bool" ])
94 | , text " means that a String argument is taken, then a Bool is returned."
95 | ]
96 |
97 | -}
98 | toInlineHtml : HCode -> Html msg
99 | toInlineHtml (HCode lines) =
100 | View.toInlineHtml lines
101 |
102 |
103 | {-| Transform a highlighted code into a static (pure text) Html block. The `Maybe Int` argument is for showing or not line count and, if so, starting from what number.
104 | -}
105 | toStaticBlockHtml : Maybe Int -> HCode -> String
106 | toStaticBlockHtml maybeStart (HCode lines) =
107 | View.toStaticBlockHtml maybeStart lines
108 |
109 |
110 | {-| Transform a highlighted code into static (pure text) inline Html.
111 | -}
112 | toStaticInlineHtml : HCode -> String
113 | toStaticInlineHtml (HCode lines) =
114 | View.toStaticInlineHtml lines
115 |
116 |
117 | {-| Parse Elm syntax.
118 | -}
119 | elm : String -> Result (List Parser.DeadEnd) HCode
120 | elm =
121 | Elm.toLines
122 | >> Result.map HCode
123 |
124 |
125 | {-| Parse XML syntax.
126 | -}
127 | xml : String -> Result (List Parser.DeadEnd) HCode
128 | xml =
129 | Xml.toLines
130 | >> Result.map HCode
131 |
132 |
133 | {-| Parse Javascript syntax.
134 | -}
135 | javascript : String -> Result (List Parser.DeadEnd) HCode
136 | javascript =
137 | Javascript.toLines
138 | >> Result.map HCode
139 |
140 |
141 | {-| Parse CSS syntax.
142 | -}
143 | css : String -> Result (List Parser.DeadEnd) HCode
144 | css =
145 | Css.toLines
146 | >> Result.map HCode
147 |
148 |
149 | {-| Parse Python syntax.
150 | -}
151 | python : String -> Result (List Parser.DeadEnd) HCode
152 | python =
153 | Python.toLines
154 | >> Result.map HCode
155 |
156 |
157 | {-| Parse Go syntax.
158 | -}
159 | go : String -> Result (List Parser.DeadEnd) HCode
160 | go =
161 | Go.toLines
162 | >> Result.map HCode
163 |
164 |
165 | {-| Parse SQL syntax.
166 | -}
167 | sql : String -> Result (List Parser.DeadEnd) HCode
168 | sql =
169 | Sql.toLines
170 | >> Result.map HCode
171 |
172 |
173 | {-| Parse JSON syntax.
174 | -}
175 | json : String -> Result (List Parser.DeadEnd) HCode
176 | json =
177 | Json.toLines
178 | >> Result.map HCode
179 |
180 |
181 | {-| Parse Nix syntax.
182 | -}
183 | nix : String -> Result (List Parser.DeadEnd) HCode
184 | nix =
185 | Nix.toLines
186 | >> Result.map HCode
187 |
188 |
189 | {-| Parse Kotlin syntax.
190 | -}
191 | kotlin : String -> Result (List Parser.DeadEnd) HCode
192 | kotlin =
193 | Kotlin.toLines
194 | >> Result.map HCode
195 |
196 |
197 | {-| Parse code from an unknown language with generic styling.
198 | -}
199 | noLang : String -> Result (List Parser.DeadEnd) HCode
200 | noLang =
201 | NoLang.toLines
202 | >> Result.map HCode
203 |
204 |
205 | {-| A theme defines the background and syntax colors.
206 | -}
207 | type Theme
208 | = Theme String
209 |
210 |
211 | {-| Transform a theme into Html. Any highlighted code transformed into Html in the same page will be themed according to the chosen `Theme`.
212 |
213 | To preview the themes, check out the [demo](https://pablohirafuji.github.io/elm-syntax-highlight/).
214 |
215 | import SyntaxHighlight exposing (elm, monokai, toBlockHtml, useTheme)
216 |
217 | view : Model -> Html msg
218 | view model =
219 | div []
220 | [ useTheme monokai
221 | , elm model.elmCode
222 | |> Result.map (toBlockHtml (Just 1))
223 | |> Result.withDefault
224 | (pre [] [ code [] [ text model.elmCode ] ])
225 | ]
226 |
227 | If you prefer to use CSS external stylesheet, you do **not** need this,
228 | just copy the theme CSS into your stylesheet.
229 | All themes can be found [here](https://pablohirafuji.github.io/elm-syntax-highlight/themes.html).
230 |
231 | -}
232 | useTheme : Theme -> Html msg
233 | useTheme (Theme theme) =
234 | Html.node "style" [] [ text theme ]
235 |
236 |
237 | {-| Monokai inspired theme.
238 | -}
239 | monokai : Theme
240 | monokai =
241 | Theme Theme.monokai
242 |
243 |
244 | {-| GitHub inspired theme.
245 | -}
246 | gitHub : Theme
247 | gitHub =
248 | Theme Theme.gitHub
249 |
250 |
251 | {-| Atom One Dark inspired theme.
252 | -}
253 | oneDark : Theme
254 | oneDark =
255 | Theme Theme.oneDark
256 |
257 |
258 | {-| Highlight type.
259 |
260 | - `Highlight` will highlight the line in a way to differentiate it from the rest, like github's yellow background.
261 | - `Add` will highlight in a manner that gives the ideia of new content added.
262 | - `Del` will highlight in a manner that gives the ideia of removed content.
263 |
264 | The specific styles will depend on the chosen `Theme`.
265 |
266 | -}
267 | type Highlight
268 | = Highlight
269 | | Add
270 | | Del
271 |
272 |
273 | {-| Highlight lines given a highlight type, start and end index.
274 | If no highlight type is given (`Nothing`), it will remove any
275 | highlight from the line range.
276 | Negative indexes are taken starting from the _end_ of the list.
277 | -}
278 | highlightLines : Maybe Highlight -> Int -> Int -> HCode -> HCode
279 | highlightLines maybeHighlight start end (HCode lines) =
280 | let
281 | maybeHighlight_ =
282 | case maybeHighlight of
283 | Nothing ->
284 | Nothing
285 |
286 | Just Highlight ->
287 | Just Line.Normal
288 |
289 | Just Add ->
290 | Just Line.Add
291 |
292 | Just Del ->
293 | Just Line.Del
294 | in
295 | Line.highlightLines maybeHighlight_ start end lines
296 | |> HCode
297 |
298 |
299 | {-| Console styling options.
300 | You can use the [rtfeldman/console-print](http://package.elm-lang.org/packages/rtfeldman/console-print/latest) package to fill in the styles.
301 |
302 | The common uses of the styles are the following:
303 |
304 | - **default**: Default style
305 | - **highlight**: Highlight style
306 | - **addition**: Addition style
307 | - **deletion**: Deletion style
308 | - **comment**: Comment
309 | - **style1**: Number
310 | - **style2**: Literal string, attribute value
311 | - **style3**: Keyword, tag, operator symbols (=+-\*/...)
312 | - **style4**: Keyword 2, group symbols ({}(),), type signature
313 | - **style5**: Function, attribute name
314 | - **style6**: Literal keyword, capitalized types
315 | - **style7**: Argument, parameter
316 |
317 | -}
318 | type alias ConsoleOptions =
319 | { default : String -> String
320 | , highlight : String -> String
321 | , addition : String -> String
322 | , deletion : String -> String
323 | , comment : String -> String
324 | , style1 : String -> String
325 | , style2 : String -> String
326 | , style3 : String -> String
327 | , style4 : String -> String
328 | , style5 : String -> String
329 | , style6 : String -> String
330 | , style7 : String -> String
331 | }
332 |
333 |
334 | {-| Transform a highlighted code into a list of console highlighted strings given the styling options defined by `ConsoleOptions`.
335 | Each string in the list is a line.
336 | -}
337 | toConsole : ConsoleOptions -> HCode -> List String
338 | toConsole options =
339 | toCustom
340 | { noOperation = String.concat
341 | , highlight = String.concat >> options.highlight
342 | , addition = String.concat >> options.addition
343 | , deletion = String.concat >> options.deletion
344 | , default = options.default
345 | , comment = options.comment
346 | , style1 = options.style1
347 | , style2 = options.style2
348 | , style3 = options.style3
349 | , style4 = options.style4
350 | , style5 = options.style5
351 | , style6 = options.style6
352 | , style7 = options.style7
353 | }
354 |
355 |
356 | {-| Custom transform options.
357 | The common uses of the styles are the following:
358 |
359 | - **noOperation**: No operation (aplly to the whole line)
360 | - **highlight**: Highlight style (aplly to the whole line)
361 | - **addition**: Addition style (aplly to the whole line)
362 | - **deletion**: Deletion style (aplly to the whole line)
363 | - **default**: Default style
364 | - **comment**: Comment
365 | - **style1**: Number
366 | - **style2**: Literal string, attribute value
367 | - **style3**: Keyword, tag, operator symbols (=+-\*/...)
368 | - **style4**: Keyword 2, group symbols ({}(),), type signature
369 | - **style5**: Function, attribute name
370 | - **style6**: Literal keyword, capitalized types
371 | - **style7**: Argument, parameter
372 |
373 | -}
374 | type alias CustomTransform fragment line =
375 | { noOperation : List fragment -> line
376 | , highlight : List fragment -> line
377 | , addition : List fragment -> line
378 | , deletion : List fragment -> line
379 | , default : String -> fragment
380 | , comment : String -> fragment
381 | , style1 : String -> fragment
382 | , style2 : String -> fragment
383 | , style3 : String -> fragment
384 | , style4 : String -> fragment
385 | , style5 : String -> fragment
386 | , style6 : String -> fragment
387 | , style7 : String -> fragment
388 | }
389 |
390 |
391 | {-| Transform a highlighted code into a list of anything you want. Each `line` in the list corresponds to a line in the original code.
392 | -}
393 | toCustom : CustomTransform fragment line -> HCode -> List line
394 | toCustom options (HCode lines) =
395 | List.map
396 | (\{ highlight, fragments } ->
397 | List.map (toCustomFragment options) fragments
398 | |> (\n ->
399 | case highlight of
400 | Nothing ->
401 | options.noOperation n
402 |
403 | Just Line.Normal ->
404 | options.highlight n
405 |
406 | Just Line.Add ->
407 | options.addition n
408 |
409 | Just Line.Del ->
410 | options.deletion n
411 | )
412 | )
413 | lines
414 |
415 |
416 | toCustomFragment : CustomTransform fragment line -> Line.Fragment -> fragment
417 | toCustomFragment options { text, requiredStyle, additionalClass } =
418 | case requiredStyle of
419 | Style.Default ->
420 | options.default text
421 |
422 | Style.Comment ->
423 | options.comment text
424 |
425 | Style.Style1 ->
426 | options.style1 text
427 |
428 | Style.Style2 ->
429 | options.style2 text
430 |
431 | Style.Style3 ->
432 | options.style3 text
433 |
434 | Style.Style4 ->
435 | options.style4 text
436 |
437 | Style.Style5 ->
438 | options.style5 text
439 |
440 | Style.Style6 ->
441 | options.style6 text
442 |
443 | Style.Style7 ->
444 | options.style7 text
445 |
--------------------------------------------------------------------------------
/tests/Language/Css.elm:
--------------------------------------------------------------------------------
1 | module Language.Css exposing (suite)
2 |
3 | import Expect exposing (Expectation, equal, onFail)
4 | import Fuzz exposing (string)
5 | import Parser
6 | import Result exposing (Result(..))
7 | import SyntaxHighlight.Language.Css as Css exposing (..)
8 | import SyntaxHighlight.Language.Type as T exposing (Syntax(..))
9 | import Test exposing (..)
10 |
11 |
12 | suite : Test
13 | suite =
14 | describe "Css Language Test Suite"
15 | [ equalTest "Namespace at-rule" namespaceStr namespaceResult
16 | , equalTest "Import at-rule" importStr importResult
17 | , equalTest "Media Query at-rule" mediaQueryStr mediaQueryResult
18 | , equalTest "Font Face at-rule" fontFaceStr fontFaceResult
19 | , equalTest "Keyframes at-rule" keyframesStr keyframesResult
20 | , equalTest "Counter Style at-rule" counterStyleStr counterStyleResult
21 | , equalTest "Page at-rule" pageStr pageResult
22 | , equalTest "Font Feature Values at-rule" fontFeatureValuesStr fontFeatureValuesResult
23 | , equalTest "Charset at-rule" charSetStr charSetResult
24 | , fuzz string "Fuzz string" <|
25 | \fuzzStr ->
26 | Parser.run Css.toRevTokens fuzzStr
27 | |> Result.map
28 | (List.reverse
29 | >> List.map Tuple.second
30 | >> String.concat
31 | )
32 | |> equal (Ok fuzzStr)
33 | |> onFail ("Resulting error string: \"" ++ fuzzStr ++ "\"")
34 | ]
35 |
36 |
37 | type alias ParserResult =
38 | Result (List Parser.DeadEnd) (List ( T.Syntax Css.Syntax, String ))
39 |
40 |
41 | equalTest : String -> String -> ParserResult -> Test
42 | equalTest title strToTest expected =
43 | describe title
44 | [ test "Syntax equality" <|
45 | \() ->
46 | Parser.run Css.toRevTokens strToTest
47 | |> Result.map List.reverse
48 | |> equal expected
49 | , test "String equality" <|
50 | \() ->
51 | Parser.run Css.toRevTokens strToTest
52 | |> Result.map
53 | (List.reverse
54 | >> List.map Tuple.second
55 | >> String.concat
56 | )
57 | |> equal (Ok strToTest)
58 | ]
59 |
60 |
61 |
62 | -- Codes from https://developer.mozilla.org/en-US/docs/Web/CSS
63 |
64 |
65 | namespaceStr : String
66 | namespaceStr =
67 | """@namespace prefix url(XML-namespace-URL);
68 | @namespace prefix "XML-namespace-URL";
69 | @namespace url(http://www.w3.org/1999/xhtml);
70 | @namespace svg "http://www.w3.org/2000/svg";"""
71 |
72 |
73 | namespaceResult : ParserResult
74 | namespaceResult =
75 | Ok [ ( C (AtRule Identifier), "@namespace" ), ( Normal, " " ), ( C (AtRule Prefix), "prefix" ), ( Normal, " " ), ( C PropertyValue, "url" ), ( Normal, "(" ), ( C String, "XML-namespace-URL" ), ( Normal, ")" ), ( Normal, ";" ), ( LineBreak, "\n" ), ( C (AtRule Identifier), "@namespace" ), ( Normal, " " ), ( C (AtRule Prefix), "prefix" ), ( Normal, " " ), ( C String, "\"" ), ( C String, "XML-namespace-URL" ), ( C String, "\"" ), ( Normal, ";" ), ( LineBreak, "\n" ), ( C (AtRule Identifier), "@namespace" ), ( Normal, " " ), ( C PropertyValue, "url" ), ( Normal, "(" ), ( C String, "http://www.w3.org/1999/xhtml" ), ( Normal, ")" ), ( Normal, ";" ), ( LineBreak, "\n" ), ( C (AtRule Identifier), "@namespace" ), ( Normal, " " ), ( C (AtRule Prefix), "svg" ), ( Normal, " " ), ( C String, "\"" ), ( C String, "http://www.w3.org/2000/svg" ), ( C String, "\"" ), ( Normal, ";" ) ]
76 |
77 |
78 | importStr : String
79 | importStr =
80 | """@import url("fineprint.css") print;
81 | @import url("bluish.css") projection, tv;
82 | @import 'custom.css';
83 | @import url("chrome://communicator/skin/");
84 | @import "common.css" screen, projection;
85 | @import url('landscape.css') screen and (orientation:landscape);"""
86 |
87 |
88 | importResult : ParserResult
89 | importResult =
90 | Ok [ ( C (AtRule Identifier), "@import" ), ( Normal, " " ), ( C PropertyValue, "url" ), ( Normal, "(" ), ( C String, "\"" ), ( C String, "fineprint.css" ), ( C String, "\"" ), ( Normal, ")" ), ( Normal, " " ), ( C (AtRule AtRuleValue), "print" ), ( Normal, ";" ), ( LineBreak, "\n" ), ( C (AtRule Identifier), "@import" ), ( Normal, " " ), ( C PropertyValue, "url" ), ( Normal, "(" ), ( C String, "\"" ), ( C String, "bluish.css" ), ( C String, "\"" ), ( Normal, ")" ), ( Normal, " " ), ( C (AtRule AtRuleValue), "projection" ), ( Normal, "," ), ( Normal, " " ), ( C (AtRule AtRuleValue), "tv" ), ( Normal, ";" ), ( LineBreak, "\n" ), ( C (AtRule Identifier), "@import" ), ( Normal, " " ), ( C String, "'" ), ( C String, "custom.css" ), ( C String, "'" ), ( Normal, ";" ), ( LineBreak, "\n" ), ( C (AtRule Identifier), "@import" ), ( Normal, " " ), ( C PropertyValue, "url" ), ( Normal, "(" ), ( C String, "\"" ), ( C String, "chrome://communicator/skin/" ), ( C String, "\"" ), ( Normal, ")" ), ( Normal, ";" ), ( LineBreak, "\n" ), ( C (AtRule Identifier), "@import" ), ( Normal, " " ), ( C String, "\"" ), ( C String, "common.css" ), ( C String, "\"" ), ( Normal, " " ), ( C (AtRule AtRuleValue), "screen" ), ( Normal, "," ), ( Normal, " " ), ( C (AtRule AtRuleValue), "projection" ), ( Normal, ";" ), ( LineBreak, "\n" ), ( C (AtRule Identifier), "@import" ), ( Normal, " " ), ( C PropertyValue, "url" ), ( Normal, "(" ), ( C String, "'" ), ( C String, "landscape.css" ), ( C String, "'" ), ( Normal, ")" ), ( Normal, " " ), ( C (AtRule AtRuleValue), "screen" ), ( Normal, " " ), ( C (AtRule Keyword), "and" ), ( Normal, " " ), ( Normal, "(" ), ( C (AtRule AtRuleValue), "orientation" ), ( Normal, ":" ), ( C (AtRule AtRuleValue), "landscape" ), ( Normal, ")" ), ( Normal, ";" ) ]
91 |
92 |
93 | mediaQueryStr : String
94 | mediaQueryStr =
95 | """@media screen and (min-width: 900px) {
96 | article {
97 | padding: 1rem 3rem;
98 | }
99 | }
100 |
101 | @supports (display: flex) {
102 | @media screen and (min-width: 900px) {
103 | article {
104 | display: flex;
105 | }
106 | }
107 | }"""
108 |
109 |
110 | mediaQueryResult : ParserResult
111 | mediaQueryResult =
112 | Ok [ ( C (AtRule Identifier), "@media" ), ( Normal, " " ), ( C (AtRule AtRuleValue), "screen" ), ( Normal, " " ), ( C (AtRule Keyword), "and" ), ( Normal, " " ), ( Normal, "(" ), ( C (AtRule AtRuleValue), "min-width" ), ( Normal, ":" ), ( Normal, " " ), ( C (AtRule AtRuleValue), "900px" ), ( Normal, ")" ), ( Normal, " " ), ( Normal, "{" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C (Selector Element), "article" ), ( Normal, " " ), ( Normal, "{" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C Property, "padding" ), ( Normal, ":" ), ( Normal, " " ), ( C Number, "1" ), ( C Unit, "rem" ), ( Normal, " " ), ( C Number, "3" ), ( C Unit, "rem" ), ( Normal, ";" ), ( LineBreak, "\n" ), ( Normal, " " ), ( Normal, "}" ), ( LineBreak, "\n" ), ( Normal, "}" ), ( LineBreak, "\n" ), ( LineBreak, "\n" ), ( C (AtRule Identifier), "@supports" ), ( Normal, " " ), ( Normal, "(" ), ( C (AtRule AtRuleValue), "display" ), ( Normal, ":" ), ( Normal, " " ), ( C (AtRule AtRuleValue), "flex" ), ( Normal, ")" ), ( Normal, " " ), ( Normal, "{" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C (AtRule Identifier), "@media" ), ( Normal, " " ), ( C (AtRule AtRuleValue), "screen" ), ( Normal, " " ), ( C (AtRule Keyword), "and" ), ( Normal, " " ), ( Normal, "(" ), ( C (AtRule AtRuleValue), "min-width" ), ( Normal, ":" ), ( Normal, " " ), ( C (AtRule AtRuleValue), "900px" ), ( Normal, ")" ), ( Normal, " " ), ( Normal, "{" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C (Selector Element), "article" ), ( Normal, " " ), ( Normal, "{" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C Property, "display" ), ( Normal, ":" ), ( Normal, " " ), ( C PropertyValue, "flex" ), ( Normal, ";" ), ( LineBreak, "\n" ), ( Normal, " " ), ( Normal, "}" ), ( LineBreak, "\n" ), ( Normal, " " ), ( Normal, "}" ), ( LineBreak, "\n" ), ( Normal, "}" ) ]
113 |
114 |
115 | fontFaceStr : String
116 | fontFaceStr =
117 | """@font-face {
118 | font-family: MyHelvetica;
119 | src: local("Helvetica Neue Bold"),
120 | local("HelveticaNeue-Bold"),
121 | url(MgOpenModernaBold.ttf);
122 | font-weight: bold;
123 | }
124 | """
125 |
126 |
127 | fontFaceResult : ParserResult
128 | fontFaceResult =
129 | Ok [ ( C (AtRule Identifier), "@font-face" ), ( Normal, " " ), ( Normal, "{" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C Property, "font-family" ), ( Normal, ":" ), ( Normal, " " ), ( C PropertyValue, "MyHelvetica" ), ( Normal, ";" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C Property, "src" ), ( Normal, ":" ), ( Normal, " " ), ( C PropertyValue, "local" ), ( Normal, "(" ), ( C String, "\"" ), ( C String, "Helvetica Neue Bold" ), ( C String, "\"" ), ( Normal, ")," ), ( LineBreak, "\n" ), ( Normal, " " ), ( C PropertyValue, "local" ), ( Normal, "(" ), ( C String, "\"" ), ( C String, "HelveticaNeue-Bold" ), ( C String, "\"" ), ( Normal, ")," ), ( LineBreak, "\n" ), ( Normal, " " ), ( C PropertyValue, "url" ), ( Normal, "(" ), ( C String, "MgOpenModernaBold.ttf" ), ( Normal, ")" ), ( Normal, ";" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C Property, "font-weight" ), ( Normal, ":" ), ( Normal, " " ), ( C PropertyValue, "bold" ), ( Normal, ";" ), ( LineBreak, "\n" ), ( Normal, "}" ), ( LineBreak, "\n" ) ]
130 |
131 |
132 | keyframesStr : String
133 | keyframesStr =
134 | """@keyframes identifier {
135 | 0% { top: 0; }
136 | 50% { top: 30px; left: 20px; }
137 | 50% { top: 10px; }
138 | 100% { top: 0; }
139 | }"""
140 |
141 |
142 | keyframesResult : ParserResult
143 | keyframesResult =
144 | Ok [ ( C (AtRule Identifier), "@keyframes" ), ( Normal, " " ), ( C (AtRule Prefix), "identifier" ), ( Normal, " " ), ( Normal, "{" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C (Selector Element), "0%" ), ( Normal, " " ), ( Normal, "{" ), ( Normal, " " ), ( C Property, "top" ), ( Normal, ":" ), ( Normal, " " ), ( C Number, "0" ), ( Normal, ";" ), ( Normal, " " ), ( Normal, "}" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C (Selector Element), "50%" ), ( Normal, " " ), ( Normal, "{" ), ( Normal, " " ), ( C Property, "top" ), ( Normal, ":" ), ( Normal, " " ), ( C Number, "30" ), ( C Unit, "px" ), ( Normal, ";" ), ( Normal, " " ), ( C Property, "left" ), ( Normal, ":" ), ( Normal, " " ), ( C Number, "20" ), ( C Unit, "px" ), ( Normal, ";" ), ( Normal, " " ), ( Normal, "}" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C (Selector Element), "50%" ), ( Normal, " " ), ( Normal, "{" ), ( Normal, " " ), ( C Property, "top" ), ( Normal, ":" ), ( Normal, " " ), ( C Number, "10" ), ( C Unit, "px" ), ( Normal, ";" ), ( Normal, " " ), ( Normal, "}" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C (Selector Element), "100%" ), ( Normal, " " ), ( Normal, "{" ), ( Normal, " " ), ( C Property, "top" ), ( Normal, ":" ), ( Normal, " " ), ( C Number, "0" ), ( Normal, ";" ), ( Normal, " " ), ( Normal, "}" ), ( LineBreak, "\n" ), ( Normal, "}" ) ]
145 |
146 |
147 | counterStyleStr : String
148 | counterStyleStr =
149 | """@counter-style winners-list {
150 | system: fixed;
151 | symbols: url(gold-medal.svg) url(silver-medal.svg) url(bronze-medal.svg);
152 | suffix: " ";
153 | }
154 | """
155 |
156 |
157 | counterStyleResult : ParserResult
158 | counterStyleResult =
159 | Ok [ ( C (AtRule Identifier), "@counter-style" ), ( Normal, " " ), ( C (AtRule Prefix), "winners-list" ), ( Normal, " " ), ( Normal, "{" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C Property, "system" ), ( Normal, ":" ), ( Normal, " " ), ( C PropertyValue, "fixed" ), ( Normal, ";" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C Property, "symbols" ), ( Normal, ":" ), ( Normal, " " ), ( C PropertyValue, "url" ), ( Normal, "(" ), ( C String, "gold-medal.svg" ), ( Normal, ")" ), ( Normal, " " ), ( C PropertyValue, "url" ), ( Normal, "(" ), ( C String, "silver-medal.svg" ), ( Normal, ")" ), ( Normal, " " ), ( C PropertyValue, "url" ), ( Normal, "(" ), ( C String, "bronze-medal.svg" ), ( Normal, ")" ), ( Normal, ";" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C Property, "suffix" ), ( Normal, ":" ), ( Normal, " " ), ( C String, "\"" ), ( C String, " " ), ( C String, "\"" ), ( Normal, ";" ), ( LineBreak, "\n" ), ( Normal, "}" ), ( LineBreak, "\n" ) ]
160 |
161 |
162 | pageStr : String
163 | pageStr =
164 | """@page {
165 | margin: 1cm;
166 | }
167 |
168 | @page :first {
169 | margin: 2cm;
170 | }"""
171 |
172 |
173 | pageResult : ParserResult
174 | pageResult =
175 | Ok [ ( C (AtRule Identifier), "@page" ), ( Normal, " " ), ( Normal, "{" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C Property, "margin" ), ( Normal, ":" ), ( Normal, " " ), ( C Number, "1" ), ( C Unit, "cm" ), ( Normal, ";" ), ( LineBreak, "\n" ), ( Normal, "}" ), ( LineBreak, "\n" ), ( LineBreak, "\n" ), ( C (AtRule Identifier), "@page" ), ( Normal, " " ), ( C (Selector PseudoClass), ":first" ), ( Normal, " " ), ( Normal, "{" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C Property, "margin" ), ( Normal, ":" ), ( Normal, " " ), ( C Number, "2" ), ( C Unit, "cm" ), ( Normal, ";" ), ( LineBreak, "\n" ), ( Normal, "}" ) ]
176 |
177 |
178 | fontFeatureValuesStr : String
179 | fontFeatureValuesStr =
180 | """@font-feature-values Font One {
181 | @styleset {
182 | nice-style: 12;
183 | }
184 | }
185 |
186 | @font-feature-values Font Two {
187 | @styleset {
188 | nice-style: 4;
189 | }
190 | }
191 | """
192 |
193 |
194 | fontFeatureValuesResult : ParserResult
195 | fontFeatureValuesResult =
196 | Ok [ ( C (AtRule Identifier), "@font-feature-values" ), ( Normal, " " ), ( C (AtRule Prefix), "Font" ), ( Normal, " " ), ( C (AtRule Prefix), "One" ), ( Normal, " " ), ( Normal, "{" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C (AtRule Identifier), "@styleset" ), ( Normal, " " ), ( Normal, "{" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C Property, "nice-style" ), ( Normal, ":" ), ( Normal, " " ), ( C Number, "12" ), ( Normal, ";" ), ( LineBreak, "\n" ), ( Normal, " " ), ( Normal, "}" ), ( LineBreak, "\n" ), ( Normal, "}" ), ( LineBreak, "\n" ), ( LineBreak, "\n" ), ( C (AtRule Identifier), "@font-feature-values" ), ( Normal, " " ), ( C (AtRule Prefix), "Font" ), ( Normal, " " ), ( C (AtRule Prefix), "Two" ), ( Normal, " " ), ( Normal, "{" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C (AtRule Identifier), "@styleset" ), ( Normal, " " ), ( Normal, "{" ), ( LineBreak, "\n" ), ( Normal, " " ), ( C Property, "nice-style" ), ( Normal, ":" ), ( Normal, " " ), ( C Number, "4" ), ( Normal, ";" ), ( LineBreak, "\n" ), ( Normal, " " ), ( Normal, "}" ), ( LineBreak, "\n" ), ( Normal, "}" ), ( LineBreak, "\n" ) ]
197 |
198 |
199 | charSetStr : String
200 | charSetStr =
201 | """@charset "UTF-8"; /* Set the encoding of the style sheet to Unicode UTF-8 */
202 | @charset 'iso-8859-15'; /* Invalid, wrong quoting style used */
203 | @charset "UTF-8"; /* Invalid, more than one space */
204 | @charset "UTF-8"; /* Invalid, there is a character (a space) before the at-rule */
205 | @charset UTF-8; /* Invalid, without ' or ", the charset is not a CSS