diff --git a/Cargo.lock b/Cargo.lock index c12346524..b4fb40089 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -208,6 +208,21 @@ dependencies = [ "backtrace", ] +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -519,6 +534,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + [[package]] name = "criterion" version = "0.5.1" @@ -675,6 +699,15 @@ dependencies = [ "uuid", ] +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + [[package]] name = "difflib" version = "0.4.0" @@ -888,6 +921,22 @@ dependencies = [ "winapi", ] +[[package]] +name = "flate2" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "foldhash" version = "0.1.4" @@ -1320,7 +1369,7 @@ dependencies = [ "log", "num-format", "once_cell", - "quick-xml", + "quick-xml 0.26.0", "rgb", "str_stack", ] @@ -1616,6 +1665,7 @@ dependencies = [ "limbo_core", "miette", "rustyline", + "syntect", "tracing", "tracing-subscriber", ] @@ -1839,6 +1889,12 @@ dependencies = [ "uuid", ] +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "linkme" version = "0.3.31" @@ -2081,6 +2137,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-format" version = "0.4.4" @@ -2115,6 +2177,28 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "onig" +version = "6.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c4b31c8722ad9171c6d77d3557db078cab2bd50afcc9d09c8b315c59df8ca4f" +dependencies = [ + "bitflags 1.3.2", + "libc", + "once_cell", + "onig_sys", +] + +[[package]] +name = "onig_sys" +version = "69.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b829e3d7e9cc74c7e315ee8edb185bf4190da5acde74afd7fc59c35b1f086e7" +dependencies = [ + "cc", + "pkg-config", +] + [[package]] name = "oorandom" version = "11.1.4" @@ -2279,6 +2363,19 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +[[package]] +name = "plist" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" +dependencies = [ + "base64", + "indexmap", + "quick-xml 0.32.0", + "serde", + "time", +] + [[package]] name = "plotters" version = "0.3.7" @@ -2328,6 +2425,12 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "pprof" version = "0.14.0" @@ -2480,6 +2583,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "quick-xml" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" +dependencies = [ + "memchr", +] + [[package]] name = "quickcheck" version = "1.0.3" @@ -3079,6 +3191,28 @@ dependencies = [ "syn 2.0.96", ] +[[package]] +name = "syntect" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874dcfa363995604333cf947ae9f751ca3af4522c60886774c4963943b4746b1" +dependencies = [ + "bincode", + "bitflags 1.3.2", + "flate2", + "fnv", + "once_cell", + "onig", + "plist", + "regex-syntax", + "serde", + "serde_derive", + "serde_json", + "thiserror 1.0.69", + "walkdir", + "yaml-rust", +] + [[package]] name = "target-lexicon" version = "0.12.16" @@ -3206,6 +3340,37 @@ dependencies = [ "once_cell", ] +[[package]] +name = "time" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tinystr" version = "0.7.6" @@ -3831,6 +3996,15 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "yoke" version = "0.7.5" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index c78dae6b8..d7fa51c06 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -1,14 +1,14 @@ # Copyright 2023 the Limbo authors. All rights reserved. MIT license. [package] -name = "limbo_cli" -version.workspace = true authors.workspace = true default-run = "limbo" +description = "The Limbo interactive SQL shell" edition.workspace = true license.workspace = true +name = "limbo_cli" repository.workspace = true -description = "The Limbo interactive SQL shell" +version.workspace = true [package.metadata.dist] dist = true @@ -20,22 +20,23 @@ path = "main.rs" [dependencies] anyhow = "1.0.75" +cfg-if = "1.0.0" clap = { version = "4.5", features = ["derive"] } comfy-table = "7.1.4" +csv = "1.3.1" +ctrlc = "3.4.4" dirs = "5.0.1" env_logger = "0.10.1" limbo_core = { path = "../core", default-features = true, features = [ "completion", ] } +miette = { version = "7.4.0", features = ["fancy"] } rustyline = { version = "15.0.0", default-features = true, features = [ "derive", ] } -ctrlc = "3.4.4" -csv = "1.3.1" -miette = { version = "7.4.0", features = ["fancy"] } -cfg-if = "1.0.0" -tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } +syntect = "5.2.0" tracing = "0.1.41" +tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } [features] default = ["io_uring"] diff --git a/cli/SQL.sublime-syntax b/cli/SQL.sublime-syntax new file mode 100644 index 000000000..d4c9b4d12 --- /dev/null +++ b/cli/SQL.sublime-syntax @@ -0,0 +1,1670 @@ +%YAML 1.2 +--- +name: SQL +scope: source.sql +version: 2 +hidden: true + +variables: + string_escape: (?:\\.) + simple_identifier: (?:\w+) + simple_identifier_break: (?!\w) + + toplevel_reserved: |- + (?xi: alter | create | cross | delete | drop | from | grant | group | inner | insert | join + | left | on | order | outer | revoke | right | select | set | truncate | union + | update | where ) + additional_toplevel_reserved: (?!) + + # TODO: not all are supported by all dialects! + ddl_target: |- + (?xi: {{ddl_target_other}} + | (?: {{ddl_target_function_modifier}} \s+ )? {{ddl_target_function}} + | (?: {{ddl_target_index_modifier}} \s+ )? index + | (?: {{ddl_target_table_modifier}} \s+ )? table ) + ddl_target_function: |- + (?xi: procedure | function ) + ddl_target_other: |- + (?xi: aggregate | column | constraint | conversion | database | domain | group + | language | member | operator\s+class | operator | role | rule | schema | sequence + | tablespace | trigger | type | user | view ) + + ddl_target_inside_alter_table: |- + (?xi: constraint ) + + ddl_target_function_modifier: |- + (?xi: aggregate ) + ddl_target_index_modifier: |- + (?xi: clustered | fulltext | spatial | unique ) + ddl_target_table_modifier: |- + (?xi: temp(?:orary)? ) + + function_parameter_modifier: |- + (?xi: inout | in | out ) + + simple_types: |- + (?xi: boolean | bool | year ) + + types_with_optional_number: |- + (?xi: bit | datetime | int | number | n?(?:var)?char | varbinary ) + + type_modifiers: |- + (?xi: signed | unsigned | zerofill ) + + builtin_scalar_functions: |- + (?xi: current_(?: date | time(?:stamp)? ) ) + + builtin_user_functions: |- + (?xi: (?: current | session | system )_user ) + +contexts: + prototype: + - include: comments + + main: + - include: sql + + sql: + - include: statements + - include: statement-terminators + - include: expressions-or-column-names + + statements: + - include: create-statements + - include: drop-statements + - include: alter-statements + - include: dml-statements + - include: grant-statements + - include: revoke-statements + - include: other-statements + +###[ COMMENTS ]################################################################ + + comments: + - include: double-dash-comments + - include: block-comments + + double-dash-comments: + - match: '--' + scope: punctuation.definition.comment.sql + push: inside-double-dash-comment + + block-comments: + - match: /\*(?:\*(?!/))+ + scope: punctuation.definition.comment.begin.sql + push: inside-comment-docblock + - match: /\* + scope: punctuation.definition.comment.begin.sql + push: inside-comment-block + + inside-double-dash-comment: + - meta_include_prototype: false + - meta_scope: comment.line.double-dash.sql + - match: \n + pop: 1 + + inside-comment-docblock: + - meta_include_prototype: false + - meta_scope: comment.block.documentation.sql + - match: \*+/ + scope: punctuation.definition.comment.end.sql + pop: 1 + - match: ^\s*(\*)(?!\**/) + captures: + 1: punctuation.definition.comment.sql + + inside-comment-block: + - meta_include_prototype: false + - meta_scope: comment.block.sql + - match: \*+/ + scope: punctuation.definition.comment.end.sql + pop: 1 + +###[ DDL CREATE STATEMENTS ]################################################### + + create-statements: + - match: \b(?i:create)\b + scope: keyword.other.ddl.sql + push: + - create-meta + - create-target + + create-meta: + - meta_include_prototype: false + - meta_scope: meta.statement.create.sql + - include: immediately-pop + + create-target: + - include: create-function + - include: create-index + - include: create-table + - include: create-other + - include: else-pop + + create-function: + - match: \b(?:({{ddl_target_function_modifier}})\s+)?({{ddl_target_function}})\b + captures: + 1: keyword.other.ddl.sql + 2: keyword.other.ddl.sql + set: + - expect-function-characteristics + - expect-function-parameters + - expect-function-creation-name + - create-function-condition + + create-function-condition: + - meta_include_prototype: false + - include: maybe-condition + + create-index: + - match: \b(?i:(?:({{ddl_target_index_modifier}})\s+)?(index))\b + captures: + 1: keyword.other.ddl.sql + 2: keyword.other.ddl.sql + set: + - create-index-args + - expect-index-creation-name + - create-index-condition + + create-index-condition: + - meta_include_prototype: false + - include: maybe-condition + + create-index-args: + - meta_scope: meta.index.sql + - include: on-table-names + - include: create-common-args + + create-table: + - match: \b(?i:(?:({{ddl_target_table_modifier}})\s+)?(table))\b + captures: + 1: keyword.other.ddl.sql + 2: keyword.other.ddl.sql + set: + - create-table-args + - maybe-column-declaration-list + - expect-table-creation-name + - create-table-condition + + create-table-condition: + - meta_include_prototype: false + - include: maybe-condition + + create-table-args: + - meta_scope: meta.table.sql + - include: create-common-args + + create-other: + - match: \b{{ddl_target_other}}\b + scope: keyword.other.ddl.sql + set: + - create-other-args + - expect-other-creation-name + - create-other-condition + + create-other-condition: + - meta_include_prototype: false + - include: maybe-condition + + create-other-args: + - match: \b(?i:(as)\s+(table))\b + captures: + 1: keyword.context.block.sql + 2: support.type.sql + set: maybe-column-declaration-list + - match: \b(?i:as)\b + scope: keyword.context.block.sql + pop: 1 + - include: grant + - include: on-table-names + - include: create-common-args + + create-common-args: + - include: expressions + - include: pop-on-top-level-reserved-word + +###[ DDL DROP STATEMENTS ]##################################################### + + drop-statements: + - match: \b(?i:drop)\b + scope: keyword.other.ddl.sql + push: + - drop-meta + - drop-target + + drop-meta: + - meta_include_prototype: false + - meta_scope: meta.statement.drop.sql + - include: immediately-pop + + drop-target: + - include: drop-function + - include: drop-index + - include: drop-table + - include: drop-other + - include: else-pop + + drop-function: + - match: \b{{ddl_target_function}}\b + scope: keyword.other.ddl.sql + set: + - drop-function-args + - expect-function-name + - drop-function-condition + + drop-function-condition: + - meta_include_prototype: false + - include: maybe-condition + + drop-function-args: + - meta_include_prototype: false + - meta_scope: meta.function.sql + - include: immediately-pop + + drop-index: + - match: \b(?i:index)\b + scope: keyword.other.ddl.sql + set: + - drop-index-args + - expect-index-name + - drop-index-condition + + drop-index-condition: + - meta_include_prototype: false + - include: maybe-condition + + drop-index-args: + - meta_scope: meta.index.sql + - include: maybe-on-table-name + + drop-table: + - match: \b(?i:(?:({{ddl_target_table_modifier}})\s+)?(table))\b + captures: + 1: keyword.other.ddl.sql + 2: keyword.other.ddl.sql + set: + - drop-table-args + - expect-table-name + - drop-table-condition + + drop-table-condition: + - meta_include_prototype: false + - include: maybe-condition + + drop-table-args: + - meta_include_prototype: false + - meta_scope: meta.table.sql + - include: immediately-pop + + drop-other: + - match: \b(?i:user)\b + scope: storage.type.sql + set: + - expect-user-name + - maybe-condition + - match: \b{{ddl_target_other}}\b + scope: keyword.other.ddl.sql + set: + - drop-other-args + - expect-other-name + - drop-other-condition + + drop-other-condition: + - meta_include_prototype: false + - include: maybe-condition + + drop-other-args: + - include: maybe-on-table-name + +###[ DDL ALTER STATEMENTS ]#################################################### + + alter-statements: + - match: \b(?i:alter)\b + scope: keyword.other.ddl.sql + push: + - alter-meta + - alter-target + + alter-meta: + - meta_include_prototype: false + - meta_scope: meta.statement.alter.sql + - include: immediately-pop + + alter-target: + - include: alter-function + - include: alter-index + - include: alter-table + - include: alter-other + - include: else-pop + +###[ DDL ALTER FUNCTION STATEMENTS ]########################################### + + alter-function: + - match: \b(?:({{ddl_target_function_modifier}})\s+)?({{ddl_target_function}})\b + captures: + 1: keyword.other.ddl.sql + 2: keyword.other.ddl.sql + set: + - expect-function-characteristics + - expect-function-parameters + - expect-function-name + - alter-function-condition + + alter-function-condition: + - meta_include_prototype: false + - include: maybe-condition + +###[ DDL ALTER INDEX STATEMENTS ]############################################## + + alter-index: + - match: \b(?i:(?:({{ddl_target_index_modifier}})\s+)?(index))\b + captures: + 1: keyword.other.ddl.sql + 2: keyword.other.ddl.sql + set: + - alter-index-args + - expect-index-name + - alter-index-condition + + alter-index-condition: + - meta_include_prototype: false + - include: maybe-condition + + alter-index-args: + - include: alter-common + - include: pop-on-top-level-reserved-word + - include: expressions-or-column-names + +###[ DDL ALTER TABLE STATEMENTS ]############################################## + + alter-table: + - match: \b(?i:(?:({{ddl_target_table_modifier}})\s+)?(table))\b + captures: + 1: keyword.other.ddl.sql + 2: keyword.other.ddl.sql + set: + - alter-table-args + - expect-table-name + - alter-table-condition + + alter-table-condition: + - meta_include_prototype: false + - include: maybe-condition + + alter-table-args: + - include: alter-columns + - include: alter-common + - include: pop-on-top-level-reserved-word + - include: expressions-or-column-names + + alter-other: + - match: \b{{ddl_target_other}}\b + scope: keyword.other.ddl.sql + set: + - alter-other-args + - expect-other-name + - alter-other-condition + + alter-other-condition: + - meta_include_prototype: false + - include: maybe-condition + + alter-other-args: + - include: alter-common + - include: else-pop + + alter-columns: + - match: \b(?i:(?:(add|alter)\b(?:\s+(column)\b|(?!\s+(?:table|{{ddl_target_inside_alter_table}})\b)))) + captures: + 1: keyword.other.ddl.sql + 2: keyword.other.ddl.sql + push: + - expect-type + - expect-column-name-declaration + - match: \b(?i:(drop)(?:\s+(column)\b|(?!\s+{{ddl_target}}\b))) + scope: keyword.other.ddl.sql + push: expect-column-name + + alter-common: + - match: \b(?i:(add)\s+(constraint))\b + captures: + 1: keyword.other.ddl.sql + 2: keyword.other.ddl.sql + push: + - maybe-column-modifier + - expect-constraint-name + - match: \b(?i:(add)\s+(?:({{ddl_target_index_modifier}})\s+)?(index))\b + captures: + 1: keyword.other.ddl.sql + 2: keyword.other.ddl.sql + 3: keyword.other.ddl.sql + - match: \b(?i:(add)\s+(primary|foreign)\s+(key))\b + captures: + 1: keyword.other.ddl.sql + 2: keyword.other.ddl.sql + 3: keyword.other.ddl.sql + +###[ DDL STATEMENT PROTOTYPES ]################################################ + + maybe-on-table-name: + - include: on-table-names + - include: else-pop + + on-table-names: + - match: \b(?i:on)\b + scope: keyword.other.sql + push: expect-table-name + +###[ DML STATEMENTS ]########################################################## + + dml-statements: + - match: \b(?i:select)\b + scope: keyword.other.dml.sql + - match: \b(?i:union(?:\s+all)?)\b + scope: keyword.other.dml.sql + - match: \b(?i:(?:delete(?:\s+from)?))\b + scope: keyword.other.dml.sql + push: dml-delete + - match: \b(?i:update)\b + scope: keyword.other.dml.sql + push: dml-update + - match: \b(?i:(?:insert\s+into|truncate))\b + scope: keyword.other.dml.sql + push: expect-table-name + - include: set-statements + # expressions + - match: \b(?i:(?:default\s+)?values)\b + scope: keyword.other.dml.II.sql + - include: distinct + - include: join-expressions + - match: \b(?i:group\s+by|order\s+by|having|where)\b + scope: keyword.other.dml.sql + - match: \b(?i:from)\b + scope: keyword.other.dml.sql + push: table-name-or-subquery + - match: \b(?i:asc|desc)\b + scope: keyword.other.order.sql + + dml-delete: + - include: expect-table-name + + dml-update: + - match: (?={{simple_identifier}}\s*=) + pop: 1 + - include: expect-table-name + + distinct: + - match: \b(?i:distinct)\b + scope: keyword.other.dml.sql + + join-expressions: + - match: \b(?i:(?:(?:cross|inner|(?:full|left|right)(?:\s+outer)?)\s+)?join)\b + scope: keyword.other.dml.sql + push: + - join-condition + - table-name-or-subquery + + join-condition: + - match: \b(?i:on)\b + scope: keyword.control.conditional.sql + set: conditional-expression + - include: else-pop + + conditional-expression: + - match: (?=[,;)}]|\b(?:{{toplevel_reserved}}|{{additional_toplevel_reserved}})\b) + pop: 1 + - include: expressions + - include: expect-column-names + +###[ DML SET STATEMENTS ]###################################################### + + set-statements: + - match: \b(?i:set)\b(?!\s*\() + scope: keyword.other.dml.sql + push: + - set-meta + - set-target + + set-meta: + - meta_include_prototype: false + - meta_scope: meta.statement.set.sql + - include: immediately-pop + + set-target: + - include: else-pop + +###[ GRANT STATEMENTS ]######################################################## + + grant-statements: + - match: \b(?i:grant(?:\s+with\s+grant\s+option)?)\b + scope: keyword.other.authorization.sql + push: + - grant-meta + - grant + + grant-meta: + - meta_include_prototype: false + - meta_scope: meta.statement.grant.sql + - include: immediately-pop + +###[ REVOKE STATEMENTS ]####################################################### + + revoke-statements: + - match: \b(?i:revoke)\b + scope: keyword.other.ddl.sql + push: + - revoke-meta + - grant + + revoke-meta: + - meta_include_prototype: false + - meta_scope: meta.statement.revoke.sql + - include: immediately-pop + +###[ OTHER STATEMENTS ]######################################################## + + other-statements: [] + +###[ EXPRESSIONS ]############################################################# + + expressions-or-column-names: + - include: wildcard-identifiers + - include: expressions + - include: expect-column-names + + expressions: + - include: groups + - include: comma-separators + - include: operators + - include: column-alias-expressions + - include: case-expressions + - include: collate-expressions + - include: constraint-expressions + - include: literals-and-variables + - include: function-calls + - include: illegal-stray-brackets + - include: illegal-stray-parens + - match: (?=;) + pop: 1 + + column-alias-expressions: + - match: \b(?i:as)\b + scope: keyword.operator.assignment.alias.sql + push: expect-column-alias-name + + table-alias-expression: + - match: \b(?i:as)\b + scope: keyword.operator.assignment.alias.sql + set: expect-table-alias-name + + case-expressions: + - match: \b(?i:case)\b + scope: keyword.control.conditional.case.sql + push: inside-case-expression + + inside-case-expression: + - meta_scope: meta.statement.conditional.case.sql + - match: \b(?i:end)\b + scope: keyword.control.conditional.end.sql + pop: 1 + - match: \b(?i:(case)\s+(when))\b + captures: + 1: keyword.control.conditional.case.sql + 2: keyword.control.conditional.when.sql + - match: \b(?i:when)\b + scope: keyword.control.conditional.when.sql + - match: \b(?i:then)\b + scope: keyword.control.conditional.then.sql + - match: \b(?i:else)\b + scope: keyword.control.conditional.else.sql + - include: expressions-or-column-names + + collate-expressions: + - match: \b(?i:collate)\b + scope: keyword.other.sql + push: inside-collate-expression + + inside-collate-expression: + - match: '{{simple_identifier}}' + scope: support.constant.sql + pop: 1 + - include: else-pop + + constraint-expressions: + - match: \b(?i:constraint)\b + scope: storage.modifier.sql + push: expect-constraint-name + + maybe-check: + - match: \b(?i:check)\b + scope: keyword.other.sql + set: maybe-group + - include: else-pop + + maybe-column: + - match: \b(?i:column)\b + scope: keyword.other.ddl.sql + pop: 1 + - include: else-pop + + maybe-to: + - match: \b(?i:to)\b + scope: keyword.other.ddl.sql + pop: 1 + - include: else-pop + +###[ FUNCTION EXPRESSIONS ]#################################################### + + function-calls: + - include: built-in-aggregate-function-calls + - include: built-in-scalar-function-calls + - include: built-in-user-function-calls + - include: user-defined-function-calls + + built-in-aggregate-function-calls: + # List of SQL99 built-in functions from http://www.oreilly.com/catalog/sqlnut/chapter/ch04.html + - match: \b(?i:AVG|COUNT|MIN|MAX|SUM)(?=\s*\() + scope: support.function.aggregate.sql + push: function-call-arguments + + built-in-scalar-function-calls: + # List of SQL99 built-in functions from http://www.oreilly.com/catalog/sqlnut/chapter/ch04.html + - match: \b{{builtin_scalar_functions}}\b + scope: support.function.scalar.sql + push: function-call-arguments + + built-in-user-function-calls: + - match: \b{{builtin_user_functions}}\b + scope: support.function.user.sql + push: function-call-arguments + + built-in-user-function-call: + - match: \b{{builtin_user_functions}}\b + scope: support.function.user.sql + set: function-call-arguments + + user-defined-function-calls: + - match: \b{{simple_identifier}}(?=\s*\() + scope: support.function.sql + push: function-call-arguments + + function-call-arguments: + - meta_include_prototype: false + - meta_scope: meta.function-call.sql + - match: \( + scope: meta.group.sql punctuation.section.arguments.begin.sql + set: inside-function-call-arguments + - include: else-pop + + inside-function-call-arguments: + - meta_content_scope: meta.function-call.sql meta.group.sql + - match: \) + scope: meta.function-call.sql meta.group.sql punctuation.section.arguments.end.sql + pop: 1 + - match: ',' + scope: punctuation.separator.arguments.sql + - include: distinct + - include: expressions-or-column-names + +###[ GROUPS EXPRESSIONS ]###################################################### + + maybe-group: + - include: group + - include: else-pop + + group: + - match: \( + scope: punctuation.section.group.begin.sql + set: inside-group + + groups: + - match: \( + scope: punctuation.section.group.begin.sql + push: inside-group + + inside-group: + - meta_scope: meta.group.sql + - match: \) + scope: punctuation.section.group.end.sql + pop: 1 + - include: sql + +###[ COLUMN EXPRESSIONS ]###################################################### + + expect-column-declaration: + - include: column-declaration-list + - match: (?=\S) + set: + - maybe-column-modifier + - after-type + - expect-type + - column-name-declaration + - single-identifier + + maybe-column-declaration-list: + - include: column-declaration-list + - include: else-pop + + column-declaration-list: + - match: \( + scope: punctuation.section.group.begin.sql + set: inside-column-declaration-list + + inside-column-declaration-list: + - meta_scope: meta.group.table-columns.sql + - match: \) + scope: punctuation.section.group.end.sql + pop: 1 + - include: column-modifiers + - include: expressions + - include: expect-column-declarations + + expect-column-declarations: + - match: (?=\S) + push: + - maybe-column-modifier + - after-type + - expect-type + - column-name-declaration + - single-identifier + + maybe-column-modifier: + - include: column-modifiers + - include: else-pop + + column-modifiers: + - match: \b(?i:check)\b + scope: keyword.other.sql + - match: |- + \b(?xi: + (?: (?: fulltext | primary | unique ) \s+ )? key + | on \s+ (?: delete | update ) (?: \s+ cascade )? + | default + )\b + scope: storage.modifier.sql + - match: |- + \b(?xi: + foreign\s+key + )\b + scope: storage.modifier.sql + push: column-name-list + - match: \b(?i:unique)\b + scope: storage.modifier.sql + push: maybe-column-name-list + - match: \b(?i:references)\b + scope: storage.modifier.sql + push: + - maybe-column-name-list + - expect-table-name + + maybe-column-name-list: + - include: column-name-list + - include: else-pop + + column-name-list: + - match: \( + scope: punctuation.section.group.begin.sql + set: inside-column-name-list + + column-name-lists: + - match: \( + scope: punctuation.section.group.begin.sql + push: inside-column-name-list + + inside-column-name-list: + - meta_scope: meta.group.table-columns.sql + - match: \) + scope: punctuation.section.group.end.sql + pop: 1 + - include: expressions-or-column-names + +###[ FUNCTION EXPRESSIONS ]#################################################### + + expect-function-parameters: + - match: \( + scope: punctuation.section.group.begin.sql + set: inside-function-parameters + - include: else-pop + + inside-function-parameters: + - clear_scopes: 1 + - meta_scope: meta.function.parameters.sql meta.group.sql + - match: \) + scope: punctuation.section.group.end.sql + pop: 1 + - include: comma-separators + - match: (?=\S) + push: + - expect-type + - expect-parameter-name + - maybe-parameter-modifier + + expect-parameter-name: + - match: '{{simple_identifier}}' + scope: variable.parameter.sql + pop: 1 + - include: else-pop + + maybe-parameter-modifier: + - match: \b{{function_parameter_modifier}}\b + scope: storage.modifier.sql + pop: 1 + - include: else-pop + + expect-function-characteristics: + - meta_scope: meta.function.sql + - match: \b(?i:as|return)\b + scope: keyword.context.block.sql + pop: 1 + - match: \b(?i:language)\b + scope: storage.modifier.sql + push: expect-function-language-name + - match: \b(?i:returns)\b + scope: keyword.other.ddl.sql + push: expect-type + - include: create-common-args + + expect-function-language-name: + - match: '{{simple_identifier}}' + scope: constant.other.language.sql + pop: 1 + - include: else-pop + +###[ USER MANAGEMENT EXPRESSIONS ]############################################# + + grant: + - match: \b(?i:to)\b + scope: keyword.context.sql + push: expect-user-name + - include: comma-separators + - include: user-privileges + - include: pop-on-top-level-reserved-word + + user-privileges: + - include: column-name-lists + - match: \b(?i:all(?:\s+privileges)?)\b + scope: constant.language.sql + - match: \b(?i:(?:alter|create|drop|grant|revoke)\s+{{ddl_target}})\b + scope: constant.language.sql + - match: \b(?i:select|insert|update|delete|truncate|execute)\b + scope: constant.language.sql + +###[ TABLE NAMES OR SUBQUERIES ]############################################### + + table-name-or-subquery: + - meta_include_prototype: false + - include: table-subquery + - include: table-name-or-function-call + + table-subquery: + - match: (?=\() + set: + - maybe-table-alias + - group + + table-name-or-function-call: + - match: (?=\S) + pop: 1 # pop `table-name-or-subquery` before evaluating branches + branch_point: table-name-or-function-call + branch: + - table-name-not-function-call + - table-valued-function-call + + table-name-not-function-call: + - meta_include_prototype: false + - match: '' + set: + - maybe-table-alias + - table-name-fail-if-function-call + - table-name + - single-identifier + + table-name-fail-if-function-call: + - meta_include_prototype: false + - match: (?=\() + fail: table-name-or-function-call + - match: (?=\S) + pop: 1 + + table-valued-function-call: + - meta_include_prototype: false + - match: '' + set: + - maybe-table-alias + - function-call-arguments + - table-valued-function-name + - single-identifier + + table-valued-function-name: + - meta_include_prototype: false + - meta_content_scope: meta.table-valued-function-name.sql + - include: immediately-pop + + maybe-table-alias: + - include: pop-on-top-level-reserved-word + - include: table-timespecs + - include: table-alias-expression + - include: expect-table-alias-name + + table-timespecs: + - match: \b(?i:for\s+system_time)\b + scope: keyword.other.dml.sql + push: table-timespec-args + + table-timespec-args: + - match: \b(?i:as\s+of|between|and|from|to)\b + scope: keyword.operator.logical.sql + push: + - table-timespec-expression + - table-timespec-type + - match: \b(?i:all)\b + scope: constant.other.sql + pop: 1 + - include: else-pop + + table-timespec-type: + - match: \b(?i:timestamp|transaction)\b + scope: storage.type.sql + set: else-pop + - include: else-pop + + table-timespec-expression: + - include: expressions + - include: immediately-pop + +###[ TYPES ]################################################################### + + expect-type: + - meta_include_prototype: false + - include: comments + - include: built-in-type + - include: expect-user-type + + built-in-types: + - match: \b(?i:enum)\b + scope: storage.type.sql + push: + - after-type + - maybe-group + - match: |- + (?x) + \b(?: {{simple_types}} | {{types_with_optional_number}} ) + (?: ((\()(\d+)(?:\s*(,)\s*(\d+))?(\)) | \b(?!\s*\() ) ) + scope: storage.type.sql + captures: + 1: meta.parens.sql + 2: punctuation.definition.parens.begin.sql + 3: meta.number.integer.decimal.sql constant.numeric.value.sql + 4: punctuation.separator.sequence.sql + 5: meta.number.integer.decimal.sql constant.numeric.value.sql + 6: punctuation.definition.parens.end.sql + push: after-type + - match: \b{{type_modifiers}}\b + scope: storage.modifier.sql + + built-in-type: + - match: \b(?i:enum)\b + scope: storage.type.sql + set: + - after-type + - maybe-group + - match: |- + (?x) + \b(?: {{simple_types}} | {{types_with_optional_number}} ) + (?: ((\()(\d+)(?:\s*(,)\s*(\d+))?(\)) | \b(?!\s*\() ) ) + scope: storage.type.sql + captures: + 1: meta.parens.sql + 2: punctuation.definition.parens.begin.sql + 3: meta.number.integer.decimal.sql constant.numeric.value.sql + 4: punctuation.separator.sequence.sql + 5: meta.number.integer.decimal.sql constant.numeric.value.sql + 6: punctuation.definition.parens.end.sql + set: after-type + + expect-user-type: + - match: (?=\S) + set: [maybe-group, after-type, inside-user-type] + + inside-user-type: + # note: may contain foreign variable interpolation + - meta_scope: support.type.sql + - match: '{{simple_identifier_break}}' + pop: 1 + + after-type: + - match: \b{{type_modifiers}}\b + scope: storage.modifier.sql + pop: 1 + - include: assignment-operators + - include: else-pop + +###[ IDENTIFIERS ]############################################################# + + expect-table-alias-name: + # prevent prototypes from inheriting syntaxes + - meta_include_prototype: false + - include: comments + - match: (?=\S) + set: [table-alias-name, single-identifier] + + expect-column-alias-name: + # prevent prototypes from inheriting syntaxes + - meta_include_prototype: false + - include: comments + - match: (?=\S) + set: [column-alias-name, single-identifier] + + table-alias-name: + - meta_include_prototype: false + - meta_content_scope: meta.alias.table.sql + - include: immediately-pop + + column-alias-name: + - meta_include_prototype: false + - meta_content_scope: meta.alias.column.sql + - include: immediately-pop + + expect-column-names: + - match: (?=\S) + push: [maybe-operator, column-name, single-identifier] + + expect-column-name: + # prevent prototypes from inheriting syntaxes + - meta_include_prototype: false + - include: comments + - match: (?=\S) + set: [column-name, single-identifier] + + column-name: + - meta_include_prototype: false + - meta_content_scope: meta.column-name.sql + - include: immediately-pop + + expect-column-name-declaration: + # prevent prototypes from inheriting syntaxes + - meta_include_prototype: false + - include: comments + - match: (?=\S) + set: [column-name-declaration, single-identifier] + + column-name-declaration: + - meta_include_prototype: false + - meta_content_scope: meta.column-name.sql variable.other.member.declaration.sql + - include: immediately-pop + + expect-constraint-name: + # prevent prototypes from inheriting syntaxes + - meta_include_prototype: false + - match: (?=(?i:check|foreign|primary|unique|index|key|using|with)\b) + pop: 1 + - include: comments + - match: (?=\S) + set: [constraint-name, single-identifier] + + constraint-name: + - meta_include_prototype: false + - meta_content_scope: meta.constraint-name.sql + - include: immediately-pop + + expect-database-creation-name: + # prevent prototypes from inheriting syntaxes + - meta_include_prototype: false + - include: comments + - match: (?=\S) + set: [database-creation-name, single-identifier] + + database-creation-name: + - meta_include_prototype: false + - meta_content_scope: entity.name.struct.database.sql + - include: immediately-pop + + expect-database-name: + # prevent prototypes from inheriting syntaxes + - meta_include_prototype: false + - include: comments + - match: (?=\S) + set: [database-name, single-identifier] + + database-name: + - meta_include_prototype: false + - meta_content_scope: meta.database-name.sql + - include: immediately-pop + + expect-event-creation-name: + # prevent prototypes from inheriting syntaxes + - meta_include_prototype: false + - include: comments + - match: (?=\S) + set: [event-creation-name, single-identifier] + + event-creation-name: + - meta_include_prototype: false + - meta_scope: entity.name.event.sql + - include: immediately-pop + + expect-event-name: + # prevent prototypes from inheriting syntaxes + - meta_include_prototype: false + - include: comments + - match: (?=\S) + set: [event-name, single-identifier] + + event-name: + - meta_include_prototype: false + - meta_scope: meta.event-name.sql + - include: immediately-pop + + expect-index-creation-name: + # prevent prototypes from inheriting syntaxes + - meta_include_prototype: false + - include: comments + - match: (?=\S) + set: [index-creation-name, single-identifier] + + index-creation-name: + - meta_include_prototype: false + - meta_scope: entity.name.struct.index.sql + - include: immediately-pop + + expect-index-names: + - match: (?=\S) + push: [index-name, single-identifier] + + expect-index-name: + # prevent prototypes from inheriting syntaxes + - meta_include_prototype: false + - include: comments + - match: (?=\S) + set: [index-name, single-identifier] + + index-name: + - meta_include_prototype: false + - meta_scope: meta.index-name.sql + - include: immediately-pop + + expect-function-creation-name: + # prevent prototypes from inheriting syntaxes + - meta_include_prototype: false + - include: comments + - match: (?=\S) + set: [procedure-creation-name, single-identifier] + + expect-partition-creation-name: + # prevent prototypes from inheriting syntaxes + - meta_include_prototype: false + - include: comments + - match: (?=\S) + set: [partition-creation-name, single-identifier] + + partition-creation-name: + - meta_include_prototype: false + - meta_scope: entity.name.struct.partition.sql + - include: immediately-pop + + expect-partition-names: + - match: (?=\S) + push: [partition-name, single-identifier] + + expect-partition-name: + # prevent prototypes from inheriting syntaxes + - meta_include_prototype: false + - include: comments + - match: (?=\S) + set: [partition-name, single-identifier] + + partition-name: + - meta_include_prototype: false + - meta_scope: meta.partition-name.sql + - include: immediately-pop + + procedure-creation-name: + - meta_include_prototype: false + - meta_content_scope: entity.name.function.sql + - include: immediately-pop + + expect-function-name: + # prevent prototypes from inheriting syntaxes + - meta_include_prototype: false + - include: comments + - match: (?=\S) + set: [procedure-name, single-identifier] + + procedure-name: + - meta_include_prototype: false + - meta_content_scope: meta.procedure-name.sql + - include: immediately-pop + + expect-table-creation-name: + # prevent prototypes from inheriting syntaxes + - meta_include_prototype: false + - include: comments + - match: (?=\S) + set: [table-creation-name, single-identifier] + + table-creation-name: + - meta_include_prototype: false + - meta_scope: entity.name.struct.table.sql + - include: immediately-pop + + expect-table-names: + - match: (?=\S) + push: [table-name, single-identifier] + + expect-table-name: + # prevent prototypes from inheriting syntaxes + - meta_include_prototype: false + - include: comments + - match: (?=\S) + set: [table-name, single-identifier] + + table-name: + - meta_include_prototype: false + - meta_content_scope: meta.table-name.sql + - include: immediately-pop + + expect-user-name: + # prevent prototypes from inheriting syntaxes + - meta_include_prototype: false + - include: comments + - include: built-in-user-function-call + - match: (?=\S) + set: [user-name, single-identifier] + + user-name: + - meta_include_prototype: false + - meta_content_scope: meta.username.sql + - include: immediately-pop + + expect-user-name-declaration: + # prevent prototypes from inheriting syntaxes + - meta_include_prototype: false + - include: comments + - match: (?=\S) + set: [user-name-declaration, single-identifier] + + user-name-declaration: + - meta_include_prototype: false + - meta_content_scope: entity.name.user.sql + - include: immediately-pop + + expect-type-creation-name: + # prevent prototypes from inheriting syntaxes + - meta_include_prototype: false + - include: comments + - match: (?=\S) + set: [type-creation-name, single-identifier] + + type-creation-name: + - meta_include_prototype: false + - meta_scope: entity.name.type.cql + - include: immediately-pop + + expect-other-creation-name: + # prevent prototypes from inheriting syntaxes + - meta_include_prototype: false + - include: comments + - match: (?=\S) + set: [other-creation-name, single-identifier] + + other-creation-name: + - meta_include_prototype: false + - meta_scope: entity.name.struct.other.sql + - include: immediately-pop + + expect-other-name: + # prevent prototypes from inheriting syntaxes + - meta_include_prototype: false + - include: comments + - match: (?=\S) + set: [other-name, single-identifier] + + other-name: + - meta_include_prototype: false + - meta_scope: meta.other-name.sql + - include: immediately-pop + + single-identifier: + - meta_include_prototype: false + - include: pop-on-top-level-reserved-word + - match: '' + set: + - maybe-identifier-accessor + - identifier-part + + maybe-identifier-accessor: + - meta_include_prototype: false + - match: \s*(\.)\s*(\*) + captures: + 1: punctuation.accessor.dot.sql + 2: constant.other.wildcard.asterisk.sql + pop: 1 + - match: \s*(\.) + captures: + 1: punctuation.accessor.dot.sql + set: single-identifier + - include: immediately-pop + + identifier-part: + - meta_include_prototype: false + - include: backtick-quoted-identifier-part + - include: double-quoted-identifier-part + - include: single-quoted-identifier-part + - include: simple-identifier-part + + backtick-quoted-identifier-part: + - match: \` + scope: punctuation.definition.identifier.begin.sql + set: inside-backtick-quoted-identifier-part + + inside-backtick-quoted-identifier-part: + # note: may contain foreign variable interpolation + - match: \` + scope: punctuation.definition.identifier.end.sql + pop: 1 + + double-quoted-identifier-part: + - match: \" + scope: punctuation.definition.identifier.begin.sql + set: inside-double-quoted-identifier-part + + inside-double-quoted-identifier-part: + # note: may contain foreign variable interpolation + - match: \" + scope: punctuation.definition.identifier.end.sql + pop: 1 + + single-quoted-identifier-part: + - match: \' + scope: punctuation.definition.identifier.begin.sql + set: inside-single-quoted-identifier-part + + inside-single-quoted-identifier-part: + # note: may contain foreign variable interpolation + - match: \' + scope: punctuation.definition.identifier.end.sql + pop: 1 + + simple-identifier-part: + - match: (?=\S) + set: inside-simple-identifier-part + + inside-simple-identifier-part: + # note: may contain foreign variable interpolation + - match: '{{simple_identifier_break}}' + pop: 1 + + wildcard-identifiers: + - match: \* + scope: constant.other.wildcard.asterisk.sql + +###[ LITERALS ]################################################################ + + literals-and-variables: + - include: built-in-types + - include: constants + - include: numbers + - include: strings + + constants: + - match: \b(?i:null)\b + scope: constant.language.null.sql + + booleans: + - match: \b(?i:true)\b + scope: constant.language.boolean.true.sql + - match: \b(?i:false)\b + scope: constant.language.boolean.false.sql + + numbers: + - match: \b\d+(\.)\d+\b + scope: meta.number.float.decimal.sql constant.numeric.value.sql + captures: + 1: punctuation.separator.decimal.sql + - match: \b\d+\b + scope: meta.number.integer.decimal.sql constant.numeric.value.sql + + strings: + - match: \' + scope: punctuation.definition.string.begin.sql + push: inside-single-quoted-string + + inside-single-quoted-string: + - meta_include_prototype: false + - meta_scope: meta.string.sql string.quoted.single.sql + - match: \'\' + scope: constant.character.escape.sql + - match: \' + scope: punctuation.definition.string.end.sql + pop: 1 + - include: string-escapes + + string-escapes: + - match: '{{string_escape}}' + scope: constant.character.escape.sql + +###[ LIKE EXPRESSIONS ]######################################################## + + like-expressions: + - match: \b(?i:like)\b + scope: keyword.operator.logical.sql + branch_point: like-expressions + branch: + - like-string-not-followed-by-escape + - like-string-followed-by-escape-slash + - like-string-followed-by-escape-caret + - like-string-followed-by-escape-hash + - like-string-followed-by-unknown-escape + + like-string-not-followed-by-escape: + - meta_include_prototype: false + - match: \' + scope: punctuation.definition.string.begin.sql + set: + - like-escape-fail + - inside-like-single-quoted-string + - include: else-pop + + like-string-followed-by-escape-slash: + - meta_include_prototype: false + - match: \' + scope: punctuation.definition.string.begin.sql + set: + - like-escape-character-slash + - like-escape + - inside-like-single-quoted-string-slash-escape + - include: else-pop + + like-string-followed-by-escape-caret: + - meta_include_prototype: false + - match: \' + scope: punctuation.definition.string.begin.sql + set: + - like-escape-character-caret + - like-escape + - inside-like-single-quoted-string-caret-escape + - include: else-pop + + like-string-followed-by-escape-hash: + - meta_include_prototype: false + - match: \' + scope: punctuation.definition.string.begin.sql + set: + - like-escape-character-hash + - like-escape + - inside-like-single-quoted-string-hash-escape + - include: else-pop + + like-string-followed-by-unknown-escape: + - meta_include_prototype: false + - match: \' + scope: punctuation.definition.string.begin.sql + set: + - like-escape-character-any + - like-escape + - inside-like-single-quoted-string + - include: else-pop + + inside-like-single-quoted-string-slash-escape: + - meta_include_prototype: false + - meta_scope: meta.string.like.sql string.quoted.single.sql + - match: \\. + scope: constant.character.escape.sql + - include: inside-like-single-quoted-string + + inside-like-single-quoted-string-caret-escape: + - meta_include_prototype: false + - meta_scope: meta.string.like.sql string.quoted.single.sql + - match: \^. + scope: constant.character.escape.sql + - include: inside-like-single-quoted-string + + inside-like-single-quoted-string-hash-escape: + - meta_include_prototype: false + - meta_scope: meta.string.like.sql string.quoted.single.sql + - match: '#.' + scope: constant.character.escape.sql + - include: inside-like-single-quoted-string + + inside-like-single-quoted-string: + - meta_include_prototype: false + - meta_scope: meta.string.like.sql string.quoted.single.sql + - match: \' + scope: punctuation.definition.string.end.sql + pop: 1 + - match: '%' + scope: constant.other.wildcard.percent.sql + - match: '_' + scope: constant.other.wildcard.underscore.sql + + like-else-fail: + - match: (?=\S) + fail: like-expressions + + like-escape-fail: + - match: \b(?i:escape)\b + fail: like-expressions + - include: else-pop + + like-escape: + - match: \b(?i:escape)\b + scope: keyword.operator.word.sql + pop: 1 + - include: else-pop + + like-escape-character-any: + - meta_include_prototype: false + - match: (\')([^'])(\') + scope: meta.string.escape.sql string.quoted.single.sql + captures: + 1: punctuation.definition.string.begin.sql + 2: constant.character.escape.sql + 3: punctuation.definition.string.end.sql + pop: 1 + - include: else-pop + + like-escape-character-caret: + - meta_include_prototype: false + - match: (\')(\^)(\') + scope: meta.string.escape.sql string.quoted.single.sql + captures: + 1: punctuation.definition.string.begin.sql + 2: constant.character.escape.sql + 3: punctuation.definition.string.end.sql + pop: 1 + - include: like-else-fail + + like-escape-character-slash: + - meta_include_prototype: false + - match: (\')(\\)(\') + scope: meta.string.escape.sql string.quoted.single.sql + captures: + 1: punctuation.definition.string.begin.sql + 2: constant.character.escape.sql + 3: punctuation.definition.string.end.sql + pop: 1 + - include: like-else-fail + + like-escape-character-hash: + - meta_include_prototype: false + - match: (\')(#)(\') + scope: meta.string.escape.sql string.quoted.single.sql + captures: + 1: punctuation.definition.string.begin.sql + 2: constant.character.escape.sql + 3: punctuation.definition.string.end.sql + pop: 1 + - include: like-else-fail + +###[ OPERATORS ]############################################################### + + maybe-condition: + - meta_include_prototype: false + - include: conditions + - include: else-pop + + conditions: + - match: \b(?i:if)\b + scope: keyword.control.conditional.if.sql + - include: logical-operators + + maybe-operator: + - meta_include_prototype: false + - match: '<=>|[!<>]?=|<>|<|>' + scope: keyword.operator.comparison.sql + pop: 1 + - match: '[-+/*]' + scope: keyword.operator.arithmetic.sql + pop: 1 + - match: \b(?i:and|or|having|exists|between|in|not|is)\b + scope: keyword.operator.logical.sql + pop: 1 + - include: assignment-operators + - include: else-pop + + operators: + - match: '<=>|[!<>]?=|<>|<|>' + scope: keyword.operator.comparison.sql + - match: '[-+/*]' + scope: keyword.operator.arithmetic.sql + - include: logical-operators + + assignment-operators: + - match: '=' + scope: keyword.operator.assignment.sql + + logical-operators: + - match: \b(?i:and|or|having|exists|between|in|not|is)\b + scope: keyword.operator.logical.sql + + comma-separators: + - match: ',' + scope: punctuation.separator.sequence.sql + + statement-terminators: + - match: ';' + scope: punctuation.terminator.statement.sql + +###[ ILLEGALS ]################################################################ + + illegal-stray-brackets: + - match: \] + scope: invalid.illegal.stray.sql + + illegal-stray-parens: + - match: \) + scope: invalid.illegal.stray.sql + +###[ PROTOTYPES ]############################################################## + + else-pop: + - match: (?=\S) + pop: 1 + + immediately-pop: + - match: '' + pop: 1 + + pop-on-top-level-reserved-word: + - match: (?=[;)}]|\b(?:{{toplevel_reserved}}|{{additional_toplevel_reserved}})\b) + pop: 1 diff --git a/cli/app.rs b/cli/app.rs index 999dd935d..221311402 100644 --- a/cli/app.rs +++ b/cli/app.rs @@ -235,6 +235,7 @@ impl<'a> Limbo<'a> { opts: Settings::from(&opts), rl, }; + if opts.sql.is_some() { app.handle_first_input(opts.sql.as_ref().unwrap()); } diff --git a/cli/helper.rs b/cli/helper.rs index 3d809741f..e8e886946 100644 --- a/cli/helper.rs +++ b/cli/helper.rs @@ -5,6 +5,10 @@ use limbo_core::{Connection, StepResult}; use rustyline::completion::{extract_word, Completer, Pair}; use rustyline::highlight::Highlighter; use rustyline::{Completer, Helper, Hinter, Validator}; +use syntect::easy::HighlightLines; +use syntect::highlighting::{Style, ThemeSet}; +use syntect::parsing::SyntaxSet; +use syntect::util::{as_24_bit_terminal_escaped, LinesWithEndings}; macro_rules! try_result { ($expr:expr, $err:expr) => { @@ -19,17 +23,67 @@ macro_rules! try_result { pub struct LimboHelper { #[rustyline(Completer)] completer: SqlCompleter, + syntax_set: SyntaxSet, + theme_set: ThemeSet, } impl LimboHelper { pub fn new(conn: Rc, io: Arc) -> Self { + // Load only predefined syntax + let ps = SyntaxSet::load_defaults_newlines(); + let ts = ThemeSet::load_defaults(); LimboHelper { completer: SqlCompleter::new(conn, io), + syntax_set: ps, + theme_set: ts, } } } -impl Highlighter for LimboHelper {} +impl Highlighter for LimboHelper { + fn highlight<'l>(&self, line: &'l str, pos: usize) -> std::borrow::Cow<'l, str> { + let _ = pos; + // TODO use lifetimes to store highlight lines + let syntax = self.syntax_set.find_syntax_by_extension("sql").unwrap(); + let mut h = HighlightLines::new(syntax, &self.theme_set.themes["base16-ocean.dark"]); + let mut ret_line = String::new(); + for new_line in LinesWithEndings::from(line) { + let ranges: Vec<(Style, &str)> = h.highlight_line(new_line, &self.syntax_set).unwrap(); + let escaped = as_24_bit_terminal_escaped(&ranges[..], false); + ret_line.push_str(&escaped); + } + // Push this escape sequence to reset + // ret_line.push_str("\x1b[0m"); + std::borrow::Cow::Owned(ret_line) + } + + fn highlight_prompt<'b, 's: 'b, 'p: 'b>( + &'s self, + prompt: &'p str, + default: bool, + ) -> std::borrow::Cow<'b, str> { + let _ = default; + std::borrow::Cow::Owned(format!("\x1b[1;32m{}\x1b[0m", prompt)) + } + + fn highlight_hint<'h>(&self, hint: &'h str) -> std::borrow::Cow<'h, str> { + std::borrow::Cow::Borrowed(hint) + } + + fn highlight_candidate<'c>( + &self, + candidate: &'c str, + completion: rustyline::CompletionType, + ) -> std::borrow::Cow<'c, str> { + let _ = completion; + std::borrow::Cow::Borrowed(candidate) + } + + fn highlight_char(&self, line: &str, pos: usize, kind: rustyline::highlight::CmdKind) -> bool { + let _ = (line, pos, kind); + true + } +} pub struct SqlCompleter { conn: Rc,