123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627 |
- /**
- * @param {string} value
- * @returns {RegExp}
- * */
- function escape(value) {
- return new RegExp(value.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'), 'm');
- }
- /**
- * @param {RegExp | string } re
- * @returns {string}
- */
- function source(re) {
- if (!re) return null;
- if (typeof re === "string") return re;
- return re.source;
- }
- /**
- * @param {RegExp | string } re
- * @returns {string}
- */
- function lookahead(re) {
- return concat('(?=', re, ')');
- }
- /**
- * @param {...(RegExp | string) } args
- * @returns {string}
- */
- function concat(...args) {
- const joined = args.map((x) => source(x)).join("");
- return joined;
- }
- /**
- * @param { Array<string | RegExp | Object> } args
- * @returns {object}
- */
- function stripOptionsFromArgs(args) {
- const opts = args[args.length - 1];
- if (typeof opts === 'object' && opts.constructor === Object) {
- args.splice(args.length - 1, 1);
- return opts;
- } else {
- return {};
- }
- }
- /** @typedef { {capture?: boolean} } RegexEitherOptions */
- /**
- * Any of the passed expresssions may match
- *
- * Creates a huge this | this | that | that match
- * @param {(RegExp | string)[] | [...(RegExp | string)[], RegexEitherOptions]} args
- * @returns {string}
- */
- function either(...args) {
- /** @type { object & {capture?: boolean} } */
- const opts = stripOptionsFromArgs(args);
- const joined = '('
- + (opts.capture ? "" : "?:")
- + args.map((x) => source(x)).join("|") + ")";
- return joined;
- }
- /*
- Language: F#
- Author: Jonas Follesø <jonas@follesoe.no>
- Contributors: Troy Kershaw <hello@troykershaw.com>, Henrik Feldt <henrik@haf.se>, Melvyn Laïly <melvyn.laily@gmail.com>
- Website: https://docs.microsoft.com/en-us/dotnet/fsharp/
- Category: functional
- */
- /** @type LanguageFn */
- function fsharp(hljs) {
- const KEYWORDS = [
- "abstract",
- "and",
- "as",
- "assert",
- "base",
- "begin",
- "class",
- "default",
- "delegate",
- "do",
- "done",
- "downcast",
- "downto",
- "elif",
- "else",
- "end",
- "exception",
- "extern",
- // "false", // literal
- "finally",
- "fixed",
- "for",
- "fun",
- "function",
- "global",
- "if",
- "in",
- "inherit",
- "inline",
- "interface",
- "internal",
- "lazy",
- "let",
- "match",
- "member",
- "module",
- "mutable",
- "namespace",
- "new",
- // "not", // built_in
- // "null", // literal
- "of",
- "open",
- "or",
- "override",
- "private",
- "public",
- "rec",
- "return",
- "static",
- "struct",
- "then",
- "to",
- // "true", // literal
- "try",
- "type",
- "upcast",
- "use",
- "val",
- "void",
- "when",
- "while",
- "with",
- "yield"
- ];
- const BANG_KEYWORD_MODE = {
- // monad builder keywords (matches before non-bang keywords)
- scope: 'keyword',
- match: /\b(yield|return|let|do|match|use)!/
- };
- const PREPROCESSOR_KEYWORDS = [
- "if",
- "else",
- "endif",
- "line",
- "nowarn",
- "light",
- "r",
- "i",
- "I",
- "load",
- "time",
- "help",
- "quit"
- ];
- const LITERALS = [
- "true",
- "false",
- "null",
- "Some",
- "None",
- "Ok",
- "Error",
- "infinity",
- "infinityf",
- "nan",
- "nanf"
- ];
- const SPECIAL_IDENTIFIERS = [
- "__LINE__",
- "__SOURCE_DIRECTORY__",
- "__SOURCE_FILE__"
- ];
- // Since it's possible to re-bind/shadow names (e.g. let char = 'c'),
- // these builtin types should only be matched when a type name is expected.
- const KNOWN_TYPES = [
- // basic types
- "bool",
- "byte",
- "sbyte",
- "int8",
- "int16",
- "int32",
- "uint8",
- "uint16",
- "uint32",
- "int",
- "uint",
- "int64",
- "uint64",
- "nativeint",
- "unativeint",
- "decimal",
- "float",
- "double",
- "float32",
- "single",
- "char",
- "string",
- "unit",
- "bigint",
- // other native types or lowercase aliases
- "option",
- "voption",
- "list",
- "array",
- "seq",
- "byref",
- "exn",
- "inref",
- "nativeptr",
- "obj",
- "outref",
- "voidptr",
- // other important FSharp types
- "Result"
- ];
- const BUILTINS = [
- // Somewhat arbitrary list of builtin functions and values.
- // Most of them are declared in Microsoft.FSharp.Core
- // I tried to stay relevant by adding only the most idiomatic
- // and most used symbols that are not already declared as types.
- "not",
- "ref",
- "raise",
- "reraise",
- "dict",
- "readOnlyDict",
- "set",
- "get",
- "enum",
- "sizeof",
- "typeof",
- "typedefof",
- "nameof",
- "nullArg",
- "invalidArg",
- "invalidOp",
- "id",
- "fst",
- "snd",
- "ignore",
- "lock",
- "using",
- "box",
- "unbox",
- "tryUnbox",
- "printf",
- "printfn",
- "sprintf",
- "eprintf",
- "eprintfn",
- "fprintf",
- "fprintfn",
- "failwith",
- "failwithf"
- ];
- const ALL_KEYWORDS = {
- keyword: KEYWORDS,
- literal: LITERALS,
- built_in: BUILTINS,
- 'variable.constant': SPECIAL_IDENTIFIERS
- };
- // (* potentially multi-line Meta Language style comment *)
- const ML_COMMENT =
- hljs.COMMENT(/\(\*(?!\))/, /\*\)/, {
- contains: ["self"]
- });
- // Either a multi-line (* Meta Language style comment *) or a single line // C style comment.
- const COMMENT = {
- variants: [
- ML_COMMENT,
- hljs.C_LINE_COMMENT_MODE,
- ]
- };
- // Most identifiers can contain apostrophes
- const IDENTIFIER_RE = /[a-zA-Z_](\w|')*/;
- const QUOTED_IDENTIFIER = {
- scope: 'variable',
- begin: /``/,
- end: /``/
- };
- // 'a or ^a where a can be a ``quoted identifier``
- const BEGIN_GENERIC_TYPE_SYMBOL_RE = /\B('|\^)/;
- const GENERIC_TYPE_SYMBOL = {
- scope: 'symbol',
- variants: [
- // the type name is a quoted identifier:
- { match: concat(BEGIN_GENERIC_TYPE_SYMBOL_RE, /``.*?``/) },
- // the type name is a normal identifier (we don't use IDENTIFIER_RE because there cannot be another apostrophe here):
- { match: concat(BEGIN_GENERIC_TYPE_SYMBOL_RE, hljs.UNDERSCORE_IDENT_RE) }
- ],
- relevance: 0
- };
- const makeOperatorMode = function({ includeEqual }) {
- // List or symbolic operator characters from the FSharp Spec 4.1, minus the dot, and with `?` added, used for nullable operators.
- let allOperatorChars;
- if (includeEqual)
- allOperatorChars = "!%&*+-/<=>@^|~?";
- else
- allOperatorChars = "!%&*+-/<>@^|~?";
- const OPERATOR_CHARS = Array.from(allOperatorChars);
- const OPERATOR_CHAR_RE = concat('[', ...OPERATOR_CHARS.map(escape), ']');
- // The lone dot operator is special. It cannot be redefined, and we don't want to highlight it. It can be used as part of a multi-chars operator though.
- const OPERATOR_CHAR_OR_DOT_RE = either(OPERATOR_CHAR_RE, /\./);
- // When a dot is present, it must be followed by another operator char:
- const OPERATOR_FIRST_CHAR_OF_MULTIPLE_RE = concat(OPERATOR_CHAR_OR_DOT_RE, lookahead(OPERATOR_CHAR_OR_DOT_RE));
- const SYMBOLIC_OPERATOR_RE = either(
- concat(OPERATOR_FIRST_CHAR_OF_MULTIPLE_RE, OPERATOR_CHAR_OR_DOT_RE, '*'), // Matches at least 2 chars operators
- concat(OPERATOR_CHAR_RE, '+'), // Matches at least one char operators
- );
- return {
- scope: 'operator',
- match: either(
- // symbolic operators:
- SYMBOLIC_OPERATOR_RE,
- // other symbolic keywords:
- // Type casting and conversion operators:
- /:\?>/,
- /:\?/,
- /:>/,
- /:=/, // Reference cell assignment
- /::?/, // : or ::
- /\$/), // A single $ can be used as an operator
- relevance: 0
- };
- };
- const OPERATOR = makeOperatorMode({ includeEqual: true });
- // This variant is used when matching '=' should end a parent mode:
- const OPERATOR_WITHOUT_EQUAL = makeOperatorMode({ includeEqual: false });
- const makeTypeAnnotationMode = function(prefix, prefixScope) {
- return {
- begin: concat( // a type annotation is a
- prefix, // should be a colon or the 'of' keyword
- lookahead( // that has to be followed by
- concat(
- /\s*/, // optional space
- either( // then either of:
- /\w/, // word
- /'/, // generic type name
- /\^/, // generic type name
- /#/, // flexible type name
- /``/, // quoted type name
- /\(/, // parens type expression
- /{\|/, // anonymous type annotation
- )))),
- beginScope: prefixScope,
- // BUG: because ending with \n is necessary for some cases, multi-line type annotations are not properly supported.
- // Examples where \n is required at the end:
- // - abstract member definitions in classes: abstract Property : int * string
- // - return type annotations: let f f' = f' () : returnTypeAnnotation
- // - record fields definitions: { A : int \n B : string }
- end: lookahead(
- either(
- /\n/,
- /=/)),
- relevance: 0,
- // we need the known types, and we need the type constraint keywords and literals. e.g.: when 'a : null
- keywords: hljs.inherit(ALL_KEYWORDS, { type: KNOWN_TYPES }),
- contains: [
- COMMENT,
- GENERIC_TYPE_SYMBOL,
- hljs.inherit(QUOTED_IDENTIFIER, { scope: null }), // match to avoid strange patterns inside that may break the parsing
- OPERATOR_WITHOUT_EQUAL
- ]
- };
- };
- const TYPE_ANNOTATION = makeTypeAnnotationMode(/:/, 'operator');
- const DISCRIMINATED_UNION_TYPE_ANNOTATION = makeTypeAnnotationMode(/\bof\b/, 'keyword');
- // type MyType<'a> = ...
- const TYPE_DECLARATION = {
- begin: [
- /(^|\s+)/, // prevents matching the following: `match s.stype with`
- /type/,
- /\s+/,
- IDENTIFIER_RE
- ],
- beginScope: {
- 2: 'keyword',
- 4: 'title.class'
- },
- end: lookahead(/\(|=|$/),
- keywords: ALL_KEYWORDS, // match keywords in type constraints. e.g.: when 'a : null
- contains: [
- COMMENT,
- hljs.inherit(QUOTED_IDENTIFIER, { scope: null }), // match to avoid strange patterns inside that may break the parsing
- GENERIC_TYPE_SYMBOL,
- {
- // For visual consistency, highlight type brackets as operators.
- scope: 'operator',
- match: /<|>/
- },
- TYPE_ANNOTATION // generic types can have constraints, which are type annotations. e.g. type MyType<'T when 'T : delegate<obj * string>> =
- ]
- };
- const COMPUTATION_EXPRESSION = {
- // computation expressions:
- scope: 'computation-expression',
- // BUG: might conflict with record deconstruction. e.g. let f { Name = name } = name // will highlight f
- match: /\b[_a-z]\w*(?=\s*\{)/
- };
- const PREPROCESSOR = {
- // preprocessor directives and fsi commands:
- begin: [
- /^\s*/,
- concat(/#/, either(...PREPROCESSOR_KEYWORDS)),
- /\b/
- ],
- beginScope: { 2: 'meta' },
- end: lookahead(/\s|$/)
- };
- // TODO: this definition is missing support for type suffixes and octal notation.
- // BUG: range operator without any space is wrongly interpreted as a single number (e.g. 1..10 )
- const NUMBER = {
- variants: [
- hljs.BINARY_NUMBER_MODE,
- hljs.C_NUMBER_MODE
- ]
- };
- // All the following string definitions are potentially multi-line.
- // BUG: these definitions are missing support for byte strings (suffixed with B)
- // "..."
- const QUOTED_STRING = {
- scope: 'string',
- begin: /"/,
- end: /"/,
- contains: [
- hljs.BACKSLASH_ESCAPE
- ]
- };
- // @"..."
- const VERBATIM_STRING = {
- scope: 'string',
- begin: /@"/,
- end: /"/,
- contains: [
- {
- match: /""/ // escaped "
- },
- hljs.BACKSLASH_ESCAPE
- ]
- };
- // """..."""
- const TRIPLE_QUOTED_STRING = {
- scope: 'string',
- begin: /"""/,
- end: /"""/,
- relevance: 2
- };
- const SUBST = {
- scope: 'subst',
- begin: /\{/,
- end: /\}/,
- keywords: ALL_KEYWORDS
- };
- // $"...{1+1}..."
- const INTERPOLATED_STRING = {
- scope: 'string',
- begin: /\$"/,
- end: /"/,
- contains: [
- {
- match: /\{\{/ // escaped {
- },
- {
- match: /\}\}/ // escaped }
- },
- hljs.BACKSLASH_ESCAPE,
- SUBST
- ]
- };
- // $@"...{1+1}..."
- const INTERPOLATED_VERBATIM_STRING = {
- scope: 'string',
- begin: /(\$@|@\$)"/,
- end: /"/,
- contains: [
- {
- match: /\{\{/ // escaped {
- },
- {
- match: /\}\}/ // escaped }
- },
- {
- match: /""/
- },
- hljs.BACKSLASH_ESCAPE,
- SUBST
- ]
- };
- // $"""...{1+1}..."""
- const INTERPOLATED_TRIPLE_QUOTED_STRING = {
- scope: 'string',
- begin: /\$"""/,
- end: /"""/,
- contains: [
- {
- match: /\{\{/ // escaped {
- },
- {
- match: /\}\}/ // escaped }
- },
- SUBST
- ],
- relevance: 2
- };
- // '.'
- const CHAR_LITERAL = {
- scope: 'string',
- match: concat(
- /'/,
- either(
- /[^\\']/, // either a single non escaped char...
- /\\(?:.|\d{3}|x[a-fA-F\d]{2}|u[a-fA-F\d]{4}|U[a-fA-F\d]{8})/ // ...or an escape sequence
- ),
- /'/
- )
- };
- // F# allows a lot of things inside string placeholders.
- // Things that don't currently seem allowed by the compiler: types definition, attributes usage.
- // (Strictly speaking, some of the followings are only allowed inside triple quoted interpolated strings...)
- SUBST.contains = [
- INTERPOLATED_VERBATIM_STRING,
- INTERPOLATED_STRING,
- VERBATIM_STRING,
- QUOTED_STRING,
- CHAR_LITERAL,
- BANG_KEYWORD_MODE,
- COMMENT,
- QUOTED_IDENTIFIER,
- TYPE_ANNOTATION,
- COMPUTATION_EXPRESSION,
- PREPROCESSOR,
- NUMBER,
- GENERIC_TYPE_SYMBOL,
- OPERATOR
- ];
- const STRING = {
- variants: [
- INTERPOLATED_TRIPLE_QUOTED_STRING,
- INTERPOLATED_VERBATIM_STRING,
- INTERPOLATED_STRING,
- TRIPLE_QUOTED_STRING,
- VERBATIM_STRING,
- QUOTED_STRING,
- CHAR_LITERAL
- ]
- };
- return {
- name: 'F#',
- aliases: [
- 'fs',
- 'f#'
- ],
- keywords: ALL_KEYWORDS,
- illegal: /\/\*/,
- classNameAliases: {
- 'computation-expression': 'keyword'
- },
- contains: [
- BANG_KEYWORD_MODE,
- STRING,
- COMMENT,
- QUOTED_IDENTIFIER,
- TYPE_DECLARATION,
- {
- // e.g. [<Attributes("")>] or [<``module``: MyCustomAttributeThatWorksOnModules>]
- // or [<Sealed; NoEquality; NoComparison; CompiledName("FSharpAsync`1")>]
- scope: 'meta',
- begin: /\[</,
- end: />\]/,
- relevance: 2,
- contains: [
- QUOTED_IDENTIFIER,
- // can contain any constant value
- TRIPLE_QUOTED_STRING,
- VERBATIM_STRING,
- QUOTED_STRING,
- CHAR_LITERAL,
- NUMBER
- ]
- },
- DISCRIMINATED_UNION_TYPE_ANNOTATION,
- TYPE_ANNOTATION,
- COMPUTATION_EXPRESSION,
- PREPROCESSOR,
- NUMBER,
- GENERIC_TYPE_SYMBOL,
- OPERATOR
- ]
- };
- }
- export { fsharp as default };
|