Cargo.lock 🔗
@@ -2563,7 +2563,6 @@ dependencies = [
"sum_tree",
"time 0.3.17",
"tiny-skia",
- "tree-sitter",
"usvg",
"util",
"waker-fn",
Max Brunsfeld created
Use tab instead of command-f to move focus from the search editor to the main editor
Cargo.lock | 1
assets/keymaps/default.json | 4
crates/gpui/Cargo.toml | 1
crates/gpui/build.rs | 12
crates/gpui/grammars/context-predicate/.gitignore | 2
crates/gpui/grammars/context-predicate/Cargo.toml | 20
crates/gpui/grammars/context-predicate/binding.gyp | 18
crates/gpui/grammars/context-predicate/bindings/node/binding.cc | 30
crates/gpui/grammars/context-predicate/bindings/node/index.js | 19
crates/gpui/grammars/context-predicate/bindings/rust/build.rs | 40
crates/gpui/grammars/context-predicate/bindings/rust/lib.rs | 52
crates/gpui/grammars/context-predicate/corpus/expressions.txt | 49
crates/gpui/grammars/context-predicate/grammar.js | 31
crates/gpui/grammars/context-predicate/package-lock.json | 44
crates/gpui/grammars/context-predicate/package.json | 10
crates/gpui/grammars/context-predicate/src/grammar.json | 208 -
crates/gpui/grammars/context-predicate/src/node-types.json | 353 -
crates/gpui/grammars/context-predicate/src/parser.c | 584 ---
crates/gpui/grammars/context-predicate/src/tree_sitter/parser.h | 223 -
crates/gpui/src/app.rs | 54
crates/gpui/src/keymap_matcher.rs | 84
crates/gpui/src/keymap_matcher/binding.rs | 12
crates/gpui/src/keymap_matcher/keymap_context.rs | 325 +
crates/gpui/src/presenter.rs | 2
24 files changed, 365 insertions(+), 1,813 deletions(-)
@@ -2563,7 +2563,6 @@ dependencies = [
"sum_tree",
"time 0.3.17",
"tiny-skia",
- "tree-sitter",
"usvg",
"util",
"waker-fn",
@@ -186,10 +186,10 @@
}
},
{
- "context": "BufferSearchBar",
+ "context": "BufferSearchBar > Editor",
"bindings": {
"escape": "buffer_search::Dismiss",
- "cmd-f": "buffer_search::FocusEditor",
+ "tab": "buffer_search::FocusEditor",
"enter": "search::SelectNextMatch",
"shift-enter": "search::SelectPrevMatch"
}
@@ -45,7 +45,6 @@ smallvec = { version = "1.6", features = ["union"] }
smol = "1.2"
time = { version = "0.3", features = ["serde", "serde-well-known"] }
tiny-skia = "0.5"
-tree-sitter = "0.20"
usvg = "0.14"
waker-fn = "1.1.0"
@@ -6,7 +6,6 @@ use std::{
fn main() {
generate_dispatch_bindings();
- compile_context_predicate_parser();
compile_metal_shaders();
generate_shader_bindings();
}
@@ -30,17 +29,6 @@ fn generate_dispatch_bindings() {
.expect("couldn't write dispatch bindings");
}
-fn compile_context_predicate_parser() {
- let dir = PathBuf::from("./grammars/context-predicate/src");
- let parser_c = dir.join("parser.c");
-
- println!("cargo:rerun-if-changed={}", &parser_c.to_str().unwrap());
- cc::Build::new()
- .include(&dir)
- .file(parser_c)
- .compile("tree_sitter_context_predicate");
-}
-
const SHADER_HEADER_PATH: &str = "./src/platform/mac/shaders/shaders.h";
fn compile_metal_shaders() {
@@ -1,2 +0,0 @@
-/node_modules
-/build
@@ -1,20 +0,0 @@
-[package]
-name = "tree-sitter-context-predicate"
-description = "context-predicate grammar for the tree-sitter parsing library"
-version = "0.0.1"
-keywords = ["incremental", "parsing", "context-predicate"]
-categories = ["parsing", "text-editors"]
-repository = "https://github.com/tree-sitter/tree-sitter-javascript"
-edition = "2021"
-license = "MIT"
-build = "bindings/rust/build.rs"
-include = ["bindings/rust/*", "grammar.js", "queries/*", "src/*"]
-
-[lib]
-path = "bindings/rust/lib.rs"
-
-[dependencies]
-tree-sitter = "0.20"
-
-[build-dependencies]
-cc = "1.0"
@@ -1,18 +0,0 @@
-{
- "targets": [
- {
- "target_name": "tree_sitter_context_predicate_binding",
- "include_dirs": [
- "<!(node -e \"require('nan')\")",
- "src"
- ],
- "sources": [
- "src/parser.c",
- "bindings/node/binding.cc"
- ],
- "cflags_c": [
- "-std=c99",
- ]
- }
- ]
-}
@@ -1,30 +0,0 @@
-#include "nan.h"
-#include "tree_sitter/parser.h"
-#include <node.h>
-
-using namespace v8;
-
-extern "C" TSLanguage *tree_sitter_context_predicate();
-
-namespace {
-
-NAN_METHOD(New) {}
-
-void Init(Local<Object> exports, Local<Object> module) {
- Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
- tpl->SetClassName(Nan::New("Language").ToLocalChecked());
- tpl->InstanceTemplate()->SetInternalFieldCount(1);
-
- Local<Function> constructor = Nan::GetFunction(tpl).ToLocalChecked();
- Local<Object> instance =
- constructor->NewInstance(Nan::GetCurrentContext()).ToLocalChecked();
- Nan::SetInternalFieldPointer(instance, 0, tree_sitter_context_predicate());
-
- Nan::Set(instance, Nan::New("name").ToLocalChecked(),
- Nan::New("context_predicate").ToLocalChecked());
- Nan::Set(module, Nan::New("exports").ToLocalChecked(), instance);
-}
-
-NODE_MODULE(tree_sitter_context_predicate_binding, Init)
-
-} // namespace
@@ -1,19 +0,0 @@
-try {
- module.exports = require("../../build/Release/tree_sitter_context_predicate_binding");
-} catch (error1) {
- if (error1.code !== 'MODULE_NOT_FOUND') {
- throw error1;
- }
- try {
- module.exports = require("../../build/Debug/tree_sitter_context_predicate_binding");
- } catch (error2) {
- if (error2.code !== 'MODULE_NOT_FOUND') {
- throw error2;
- }
- throw error1
- }
-}
-
-try {
- module.exports.nodeTypeInfo = require("../../src/node-types.json");
-} catch (_) {}
@@ -1,40 +0,0 @@
-fn main() {
- let src_dir = std::path::Path::new("src");
-
- let mut c_config = cc::Build::new();
- c_config.include(&src_dir);
- c_config
- .flag_if_supported("-Wno-unused-parameter")
- .flag_if_supported("-Wno-unused-but-set-variable")
- .flag_if_supported("-Wno-trigraphs");
- let parser_path = src_dir.join("parser.c");
- c_config.file(&parser_path);
-
- // If your language uses an external scanner written in C,
- // then include this block of code:
-
- /*
- let scanner_path = src_dir.join("scanner.c");
- c_config.file(&scanner_path);
- println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap());
- */
-
- c_config.compile("parser");
- println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap());
-
- // If your language uses an external scanner written in C++,
- // then include this block of code:
-
- /*
- let mut cpp_config = cc::Build::new();
- cpp_config.cpp(true);
- cpp_config.include(&src_dir);
- cpp_config
- .flag_if_supported("-Wno-unused-parameter")
- .flag_if_supported("-Wno-unused-but-set-variable");
- let scanner_path = src_dir.join("scanner.cc");
- cpp_config.file(&scanner_path);
- cpp_config.compile("scanner");
- println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap());
- */
-}
@@ -1,52 +0,0 @@
-//! This crate provides context_predicate language support for the [tree-sitter][] parsing library.
-//!
-//! Typically, you will use the [language][language func] function to add this language to a
-//! tree-sitter [Parser][], and then use the parser to parse some code:
-//!
-//! ```
-//! let code = "";
-//! let mut parser = tree_sitter::Parser::new();
-//! parser.set_language(tree_sitter_context_predicate::language()).expect("Error loading context_predicate grammar");
-//! let tree = parser.parse(code, None).unwrap();
-//! ```
-//!
-//! [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html
-//! [language func]: fn.language.html
-//! [Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html
-//! [tree-sitter]: https://tree-sitter.github.io/
-
-use tree_sitter::Language;
-
-extern "C" {
- fn tree_sitter_context_predicate() -> Language;
-}
-
-/// Get the tree-sitter [Language][] for this grammar.
-///
-/// [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html
-pub fn language() -> Language {
- unsafe { tree_sitter_context_predicate() }
-}
-
-/// The content of the [`node-types.json`][] file for this grammar.
-///
-/// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types
-pub const NODE_TYPES: &'static str = include_str!("../../src/node-types.json");
-
-// Uncomment these to include any queries that this grammar contains
-
-// pub const HIGHLIGHTS_QUERY: &'static str = include_str!("../../queries/highlights.scm");
-// pub const INJECTIONS_QUERY: &'static str = include_str!("../../queries/injections.scm");
-// pub const LOCALS_QUERY: &'static str = include_str!("../../queries/locals.scm");
-// pub const TAGS_QUERY: &'static str = include_str!("../../queries/tags.scm");
-
-#[cfg(test)]
-mod tests {
- #[test]
- fn test_can_load_grammar() {
- let mut parser = tree_sitter::Parser::new();
- parser
- .set_language(super::language())
- .expect("Error loading context_predicate language");
- }
-}
@@ -1,49 +0,0 @@
-==================
-Identifiers
-==================
-
-abc12
-
----
-
-(source (identifier))
-
-==================
-Negation
-==================
-
-!abc
-
----
-
-(source (not (identifier)))
-
-==================
-And/Or
-==================
-
-a || b && c && d
-
----
-
-(source
- (or
- (identifier)
- (and
- (and (identifier) (identifier))
- (identifier))))
-
-==================
-Expressions
-==================
-
-a && (b == c || d != e)
-
----
-
-(source
- (and
- (identifier)
- (parenthesized (or
- (equal (identifier) (identifier))
- (not_equal (identifier) (identifier))))))
@@ -1,31 +0,0 @@
-module.exports = grammar({
- name: 'context_predicate',
-
- rules: {
- source: $ => $._expression,
-
- _expression: $ => choice(
- $.identifier,
- $.not,
- $.and,
- $.or,
- $.equal,
- $.not_equal,
- $.parenthesized,
- ),
-
- identifier: $ => /[A-Za-z0-9_-]+/,
-
- not: $ => prec(3, seq("!", field("expression", $._expression))),
-
- and: $ => prec.left(2, seq(field("left", $._expression), "&&", field("right", $._expression))),
-
- or: $ => prec.left(1, seq(field("left", $._expression), "||", field("right", $._expression))),
-
- equal: $ => seq(field("left", $.identifier), "==", field("right", $.identifier)),
-
- not_equal: $ => seq(field("left", $.identifier), "!=", field("right", $.identifier)),
-
- parenthesized: $ => seq("(", field("expression", $._expression), ")"),
- }
-});
@@ -1,44 +0,0 @@
-{
- "name": "tree-sitter-context-predicate",
- "lockfileVersion": 2,
- "requires": true,
- "packages": {
- "": {
- "name": "tree-sitter-context-predicate",
- "dependencies": {
- "nan": "^2.14.0"
- },
- "devDependencies": {
- "tree-sitter-cli": "^0.19.5"
- }
- },
- "node_modules/nan": {
- "version": "2.14.2",
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
- "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ=="
- },
- "node_modules/tree-sitter-cli": {
- "version": "0.19.5",
- "resolved": "https://registry.npmjs.org/tree-sitter-cli/-/tree-sitter-cli-0.19.5.tgz",
- "integrity": "sha512-kRzKrUAwpDN9AjA3b0tPBwT1hd8N2oQvvvHup2OEsX6mdsSMLmAvR+NSqK9fe05JrRbVvG8mbteNUQsxlMQohQ==",
- "dev": true,
- "hasInstallScript": true,
- "bin": {
- "tree-sitter": "cli.js"
- }
- }
- },
- "dependencies": {
- "nan": {
- "version": "2.14.2",
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
- "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ=="
- },
- "tree-sitter-cli": {
- "version": "0.19.5",
- "resolved": "https://registry.npmjs.org/tree-sitter-cli/-/tree-sitter-cli-0.19.5.tgz",
- "integrity": "sha512-kRzKrUAwpDN9AjA3b0tPBwT1hd8N2oQvvvHup2OEsX6mdsSMLmAvR+NSqK9fe05JrRbVvG8mbteNUQsxlMQohQ==",
- "dev": true
- }
- }
-}
@@ -1,10 +0,0 @@
-{
- "name": "tree-sitter-context-predicate",
- "main": "bindings/node",
- "devDependencies": {
- "tree-sitter-cli": "^0.19.5"
- },
- "dependencies": {
- "nan": "^2.14.0"
- }
-}
@@ -1,208 +0,0 @@
-{
- "name": "context_predicate",
- "rules": {
- "source": {
- "type": "SYMBOL",
- "name": "_expression"
- },
- "_expression": {
- "type": "CHOICE",
- "members": [
- {
- "type": "SYMBOL",
- "name": "identifier"
- },
- {
- "type": "SYMBOL",
- "name": "not"
- },
- {
- "type": "SYMBOL",
- "name": "and"
- },
- {
- "type": "SYMBOL",
- "name": "or"
- },
- {
- "type": "SYMBOL",
- "name": "equal"
- },
- {
- "type": "SYMBOL",
- "name": "not_equal"
- },
- {
- "type": "SYMBOL",
- "name": "parenthesized"
- }
- ]
- },
- "identifier": {
- "type": "PATTERN",
- "value": "[A-Za-z0-9_-]+"
- },
- "not": {
- "type": "PREC",
- "value": 3,
- "content": {
- "type": "SEQ",
- "members": [
- {
- "type": "STRING",
- "value": "!"
- },
- {
- "type": "FIELD",
- "name": "expression",
- "content": {
- "type": "SYMBOL",
- "name": "_expression"
- }
- }
- ]
- }
- },
- "and": {
- "type": "PREC_LEFT",
- "value": 2,
- "content": {
- "type": "SEQ",
- "members": [
- {
- "type": "FIELD",
- "name": "left",
- "content": {
- "type": "SYMBOL",
- "name": "_expression"
- }
- },
- {
- "type": "STRING",
- "value": "&&"
- },
- {
- "type": "FIELD",
- "name": "right",
- "content": {
- "type": "SYMBOL",
- "name": "_expression"
- }
- }
- ]
- }
- },
- "or": {
- "type": "PREC_LEFT",
- "value": 1,
- "content": {
- "type": "SEQ",
- "members": [
- {
- "type": "FIELD",
- "name": "left",
- "content": {
- "type": "SYMBOL",
- "name": "_expression"
- }
- },
- {
- "type": "STRING",
- "value": "||"
- },
- {
- "type": "FIELD",
- "name": "right",
- "content": {
- "type": "SYMBOL",
- "name": "_expression"
- }
- }
- ]
- }
- },
- "equal": {
- "type": "SEQ",
- "members": [
- {
- "type": "FIELD",
- "name": "left",
- "content": {
- "type": "SYMBOL",
- "name": "identifier"
- }
- },
- {
- "type": "STRING",
- "value": "=="
- },
- {
- "type": "FIELD",
- "name": "right",
- "content": {
- "type": "SYMBOL",
- "name": "identifier"
- }
- }
- ]
- },
- "not_equal": {
- "type": "SEQ",
- "members": [
- {
- "type": "FIELD",
- "name": "left",
- "content": {
- "type": "SYMBOL",
- "name": "identifier"
- }
- },
- {
- "type": "STRING",
- "value": "!="
- },
- {
- "type": "FIELD",
- "name": "right",
- "content": {
- "type": "SYMBOL",
- "name": "identifier"
- }
- }
- ]
- },
- "parenthesized": {
- "type": "SEQ",
- "members": [
- {
- "type": "STRING",
- "value": "("
- },
- {
- "type": "FIELD",
- "name": "expression",
- "content": {
- "type": "SYMBOL",
- "name": "_expression"
- }
- },
- {
- "type": "STRING",
- "value": ")"
- }
- ]
- }
- },
- "extras": [
- {
- "type": "PATTERN",
- "value": "\\s"
- }
- ],
- "conflicts": [],
- "precedences": [],
- "externals": [],
- "inline": [],
- "supertypes": []
-}
-
@@ -1,353 +0,0 @@
-[
- {
- "type": "and",
- "named": true,
- "fields": {
- "left": {
- "multiple": false,
- "required": true,
- "types": [
- {
- "type": "and",
- "named": true
- },
- {
- "type": "equal",
- "named": true
- },
- {
- "type": "identifier",
- "named": true
- },
- {
- "type": "not",
- "named": true
- },
- {
- "type": "not_equal",
- "named": true
- },
- {
- "type": "or",
- "named": true
- },
- {
- "type": "parenthesized",
- "named": true
- }
- ]
- },
- "right": {
- "multiple": false,
- "required": true,
- "types": [
- {
- "type": "and",
- "named": true
- },
- {
- "type": "equal",
- "named": true
- },
- {
- "type": "identifier",
- "named": true
- },
- {
- "type": "not",
- "named": true
- },
- {
- "type": "not_equal",
- "named": true
- },
- {
- "type": "or",
- "named": true
- },
- {
- "type": "parenthesized",
- "named": true
- }
- ]
- }
- }
- },
- {
- "type": "equal",
- "named": true,
- "fields": {
- "left": {
- "multiple": false,
- "required": true,
- "types": [
- {
- "type": "identifier",
- "named": true
- }
- ]
- },
- "right": {
- "multiple": false,
- "required": true,
- "types": [
- {
- "type": "identifier",
- "named": true
- }
- ]
- }
- }
- },
- {
- "type": "not",
- "named": true,
- "fields": {
- "expression": {
- "multiple": false,
- "required": true,
- "types": [
- {
- "type": "and",
- "named": true
- },
- {
- "type": "equal",
- "named": true
- },
- {
- "type": "identifier",
- "named": true
- },
- {
- "type": "not",
- "named": true
- },
- {
- "type": "not_equal",
- "named": true
- },
- {
- "type": "or",
- "named": true
- },
- {
- "type": "parenthesized",
- "named": true
- }
- ]
- }
- }
- },
- {
- "type": "not_equal",
- "named": true,
- "fields": {
- "left": {
- "multiple": false,
- "required": true,
- "types": [
- {
- "type": "identifier",
- "named": true
- }
- ]
- },
- "right": {
- "multiple": false,
- "required": true,
- "types": [
- {
- "type": "identifier",
- "named": true
- }
- ]
- }
- }
- },
- {
- "type": "or",
- "named": true,
- "fields": {
- "left": {
- "multiple": false,
- "required": true,
- "types": [
- {
- "type": "and",
- "named": true
- },
- {
- "type": "equal",
- "named": true
- },
- {
- "type": "identifier",
- "named": true
- },
- {
- "type": "not",
- "named": true
- },
- {
- "type": "not_equal",
- "named": true
- },
- {
- "type": "or",
- "named": true
- },
- {
- "type": "parenthesized",
- "named": true
- }
- ]
- },
- "right": {
- "multiple": false,
- "required": true,
- "types": [
- {
- "type": "and",
- "named": true
- },
- {
- "type": "equal",
- "named": true
- },
- {
- "type": "identifier",
- "named": true
- },
- {
- "type": "not",
- "named": true
- },
- {
- "type": "not_equal",
- "named": true
- },
- {
- "type": "or",
- "named": true
- },
- {
- "type": "parenthesized",
- "named": true
- }
- ]
- }
- }
- },
- {
- "type": "parenthesized",
- "named": true,
- "fields": {
- "expression": {
- "multiple": false,
- "required": true,
- "types": [
- {
- "type": "and",
- "named": true
- },
- {
- "type": "equal",
- "named": true
- },
- {
- "type": "identifier",
- "named": true
- },
- {
- "type": "not",
- "named": true
- },
- {
- "type": "not_equal",
- "named": true
- },
- {
- "type": "or",
- "named": true
- },
- {
- "type": "parenthesized",
- "named": true
- }
- ]
- }
- }
- },
- {
- "type": "source",
- "named": true,
- "fields": {},
- "children": {
- "multiple": false,
- "required": true,
- "types": [
- {
- "type": "and",
- "named": true
- },
- {
- "type": "equal",
- "named": true
- },
- {
- "type": "identifier",
- "named": true
- },
- {
- "type": "not",
- "named": true
- },
- {
- "type": "not_equal",
- "named": true
- },
- {
- "type": "or",
- "named": true
- },
- {
- "type": "parenthesized",
- "named": true
- }
- ]
- }
- },
- {
- "type": "!",
- "named": false
- },
- {
- "type": "!=",
- "named": false
- },
- {
- "type": "&&",
- "named": false
- },
- {
- "type": "(",
- "named": false
- },
- {
- "type": ")",
- "named": false
- },
- {
- "type": "==",
- "named": false
- },
- {
- "type": "identifier",
- "named": true
- },
- {
- "type": "||",
- "named": false
- }
-]
@@ -1,584 +0,0 @@
-#include <tree_sitter/parser.h>
-
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
-#endif
-
-#define LANGUAGE_VERSION 13
-#define STATE_COUNT 18
-#define LARGE_STATE_COUNT 6
-#define SYMBOL_COUNT 17
-#define ALIAS_COUNT 0
-#define TOKEN_COUNT 9
-#define EXTERNAL_TOKEN_COUNT 0
-#define FIELD_COUNT 3
-#define MAX_ALIAS_SEQUENCE_LENGTH 3
-#define PRODUCTION_ID_COUNT 3
-
-enum {
- sym_identifier = 1,
- anon_sym_BANG = 2,
- anon_sym_AMP_AMP = 3,
- anon_sym_PIPE_PIPE = 4,
- anon_sym_EQ_EQ = 5,
- anon_sym_BANG_EQ = 6,
- anon_sym_LPAREN = 7,
- anon_sym_RPAREN = 8,
- sym_source = 9,
- sym__expression = 10,
- sym_not = 11,
- sym_and = 12,
- sym_or = 13,
- sym_equal = 14,
- sym_not_equal = 15,
- sym_parenthesized = 16,
-};
-
-static const char *const ts_symbol_names[] = {
- [ts_builtin_sym_end] = "end",
- [sym_identifier] = "identifier",
- [anon_sym_BANG] = "!",
- [anon_sym_AMP_AMP] = "&&",
- [anon_sym_PIPE_PIPE] = "||",
- [anon_sym_EQ_EQ] = "==",
- [anon_sym_BANG_EQ] = "!=",
- [anon_sym_LPAREN] = "(",
- [anon_sym_RPAREN] = ")",
- [sym_source] = "source",
- [sym__expression] = "_expression",
- [sym_not] = "not",
- [sym_and] = "and",
- [sym_or] = "or",
- [sym_equal] = "equal",
- [sym_not_equal] = "not_equal",
- [sym_parenthesized] = "parenthesized",
-};
-
-static const TSSymbol ts_symbol_map[] = {
- [ts_builtin_sym_end] = ts_builtin_sym_end,
- [sym_identifier] = sym_identifier,
- [anon_sym_BANG] = anon_sym_BANG,
- [anon_sym_AMP_AMP] = anon_sym_AMP_AMP,
- [anon_sym_PIPE_PIPE] = anon_sym_PIPE_PIPE,
- [anon_sym_EQ_EQ] = anon_sym_EQ_EQ,
- [anon_sym_BANG_EQ] = anon_sym_BANG_EQ,
- [anon_sym_LPAREN] = anon_sym_LPAREN,
- [anon_sym_RPAREN] = anon_sym_RPAREN,
- [sym_source] = sym_source,
- [sym__expression] = sym__expression,
- [sym_not] = sym_not,
- [sym_and] = sym_and,
- [sym_or] = sym_or,
- [sym_equal] = sym_equal,
- [sym_not_equal] = sym_not_equal,
- [sym_parenthesized] = sym_parenthesized,
-};
-
-static const TSSymbolMetadata ts_symbol_metadata[] = {
- [ts_builtin_sym_end] =
- {
- .visible = false,
- .named = true,
- },
- [sym_identifier] =
- {
- .visible = true,
- .named = true,
- },
- [anon_sym_BANG] =
- {
- .visible = true,
- .named = false,
- },
- [anon_sym_AMP_AMP] =
- {
- .visible = true,
- .named = false,
- },
- [anon_sym_PIPE_PIPE] =
- {
- .visible = true,
- .named = false,
- },
- [anon_sym_EQ_EQ] =
- {
- .visible = true,
- .named = false,
- },
- [anon_sym_BANG_EQ] =
- {
- .visible = true,
- .named = false,
- },
- [anon_sym_LPAREN] =
- {
- .visible = true,
- .named = false,
- },
- [anon_sym_RPAREN] =
- {
- .visible = true,
- .named = false,
- },
- [sym_source] =
- {
- .visible = true,
- .named = true,
- },
- [sym__expression] =
- {
- .visible = false,
- .named = true,
- },
- [sym_not] =
- {
- .visible = true,
- .named = true,
- },
- [sym_and] =
- {
- .visible = true,
- .named = true,
- },
- [sym_or] =
- {
- .visible = true,
- .named = true,
- },
- [sym_equal] =
- {
- .visible = true,
- .named = true,
- },
- [sym_not_equal] =
- {
- .visible = true,
- .named = true,
- },
- [sym_parenthesized] =
- {
- .visible = true,
- .named = true,
- },
-};
-
-enum {
- field_expression = 1,
- field_left = 2,
- field_right = 3,
-};
-
-static const char *const ts_field_names[] = {
- [0] = NULL,
- [field_expression] = "expression",
- [field_left] = "left",
- [field_right] = "right",
-};
-
-static const TSFieldMapSlice ts_field_map_slices[PRODUCTION_ID_COUNT] = {
- [1] = {.index = 0, .length = 1},
- [2] = {.index = 1, .length = 2},
-};
-
-static const TSFieldMapEntry ts_field_map_entries[] = {
- [0] = {field_expression, 1},
- [1] = {field_left, 0},
- {field_right, 2},
-};
-
-static const TSSymbol ts_alias_sequences[PRODUCTION_ID_COUNT]
- [MAX_ALIAS_SEQUENCE_LENGTH] = {
- [0] = {0},
-};
-
-static const uint16_t ts_non_terminal_alias_map[] = {
- 0,
-};
-
-static bool ts_lex(TSLexer *lexer, TSStateId state) {
- START_LEXER();
- eof = lexer->eof(lexer);
- switch (state) {
- case 0:
- if (eof)
- ADVANCE(7);
- if (lookahead == '!')
- ADVANCE(10);
- if (lookahead == '&')
- ADVANCE(2);
- if (lookahead == '(')
- ADVANCE(15);
- if (lookahead == ')')
- ADVANCE(16);
- if (lookahead == '=')
- ADVANCE(4);
- if (lookahead == '|')
- ADVANCE(5);
- if (lookahead == '\t' || lookahead == '\n' || lookahead == '\r' ||
- lookahead == ' ')
- SKIP(0)
- if (lookahead == '-' || ('0' <= lookahead && lookahead <= '9') ||
- ('A' <= lookahead && lookahead <= 'Z') || lookahead == '_' ||
- ('a' <= lookahead && lookahead <= 'z'))
- ADVANCE(8);
- END_STATE();
- case 1:
- if (lookahead == '!')
- ADVANCE(9);
- if (lookahead == '(')
- ADVANCE(15);
- if (lookahead == '\t' || lookahead == '\n' || lookahead == '\r' ||
- lookahead == ' ')
- SKIP(1)
- if (lookahead == '-' || ('0' <= lookahead && lookahead <= '9') ||
- ('A' <= lookahead && lookahead <= 'Z') || lookahead == '_' ||
- ('a' <= lookahead && lookahead <= 'z'))
- ADVANCE(8);
- END_STATE();
- case 2:
- if (lookahead == '&')
- ADVANCE(11);
- END_STATE();
- case 3:
- if (lookahead == '=')
- ADVANCE(14);
- END_STATE();
- case 4:
- if (lookahead == '=')
- ADVANCE(13);
- END_STATE();
- case 5:
- if (lookahead == '|')
- ADVANCE(12);
- END_STATE();
- case 6:
- if (eof)
- ADVANCE(7);
- if (lookahead == '!')
- ADVANCE(3);
- if (lookahead == '&')
- ADVANCE(2);
- if (lookahead == ')')
- ADVANCE(16);
- if (lookahead == '=')
- ADVANCE(4);
- if (lookahead == '|')
- ADVANCE(5);
- if (lookahead == '\t' || lookahead == '\n' || lookahead == '\r' ||
- lookahead == ' ')
- SKIP(6)
- END_STATE();
- case 7:
- ACCEPT_TOKEN(ts_builtin_sym_end);
- END_STATE();
- case 8:
- ACCEPT_TOKEN(sym_identifier);
- if (lookahead == '-' || ('0' <= lookahead && lookahead <= '9') ||
- ('A' <= lookahead && lookahead <= 'Z') || lookahead == '_' ||
- ('a' <= lookahead && lookahead <= 'z'))
- ADVANCE(8);
- END_STATE();
- case 9:
- ACCEPT_TOKEN(anon_sym_BANG);
- END_STATE();
- case 10:
- ACCEPT_TOKEN(anon_sym_BANG);
- if (lookahead == '=')
- ADVANCE(14);
- END_STATE();
- case 11:
- ACCEPT_TOKEN(anon_sym_AMP_AMP);
- END_STATE();
- case 12:
- ACCEPT_TOKEN(anon_sym_PIPE_PIPE);
- END_STATE();
- case 13:
- ACCEPT_TOKEN(anon_sym_EQ_EQ);
- END_STATE();
- case 14:
- ACCEPT_TOKEN(anon_sym_BANG_EQ);
- END_STATE();
- case 15:
- ACCEPT_TOKEN(anon_sym_LPAREN);
- END_STATE();
- case 16:
- ACCEPT_TOKEN(anon_sym_RPAREN);
- END_STATE();
- default:
- return false;
- }
-}
-
-static const TSLexMode ts_lex_modes[STATE_COUNT] = {
- [0] = {.lex_state = 0}, [1] = {.lex_state = 1}, [2] = {.lex_state = 1},
- [3] = {.lex_state = 1}, [4] = {.lex_state = 1}, [5] = {.lex_state = 1},
- [6] = {.lex_state = 6}, [7] = {.lex_state = 0}, [8] = {.lex_state = 0},
- [9] = {.lex_state = 0}, [10] = {.lex_state = 0}, [11] = {.lex_state = 0},
- [12] = {.lex_state = 0}, [13] = {.lex_state = 0}, [14] = {.lex_state = 0},
- [15] = {.lex_state = 0}, [16] = {.lex_state = 0}, [17] = {.lex_state = 0},
-};
-
-static const uint16_t ts_parse_table[LARGE_STATE_COUNT][SYMBOL_COUNT] = {
- [0] =
- {
- [ts_builtin_sym_end] = ACTIONS(1),
- [sym_identifier] = ACTIONS(1),
- [anon_sym_BANG] = ACTIONS(1),
- [anon_sym_AMP_AMP] = ACTIONS(1),
- [anon_sym_PIPE_PIPE] = ACTIONS(1),
- [anon_sym_EQ_EQ] = ACTIONS(1),
- [anon_sym_BANG_EQ] = ACTIONS(1),
- [anon_sym_LPAREN] = ACTIONS(1),
- [anon_sym_RPAREN] = ACTIONS(1),
- },
- [1] =
- {
- [sym_source] = STATE(15),
- [sym__expression] = STATE(13),
- [sym_not] = STATE(13),
- [sym_and] = STATE(13),
- [sym_or] = STATE(13),
- [sym_equal] = STATE(13),
- [sym_not_equal] = STATE(13),
- [sym_parenthesized] = STATE(13),
- [sym_identifier] = ACTIONS(3),
- [anon_sym_BANG] = ACTIONS(5),
- [anon_sym_LPAREN] = ACTIONS(7),
- },
- [2] =
- {
- [sym__expression] = STATE(7),
- [sym_not] = STATE(7),
- [sym_and] = STATE(7),
- [sym_or] = STATE(7),
- [sym_equal] = STATE(7),
- [sym_not_equal] = STATE(7),
- [sym_parenthesized] = STATE(7),
- [sym_identifier] = ACTIONS(3),
- [anon_sym_BANG] = ACTIONS(5),
- [anon_sym_LPAREN] = ACTIONS(7),
- },
- [3] =
- {
- [sym__expression] = STATE(14),
- [sym_not] = STATE(14),
- [sym_and] = STATE(14),
- [sym_or] = STATE(14),
- [sym_equal] = STATE(14),
- [sym_not_equal] = STATE(14),
- [sym_parenthesized] = STATE(14),
- [sym_identifier] = ACTIONS(3),
- [anon_sym_BANG] = ACTIONS(5),
- [anon_sym_LPAREN] = ACTIONS(7),
- },
- [4] =
- {
- [sym__expression] = STATE(11),
- [sym_not] = STATE(11),
- [sym_and] = STATE(11),
- [sym_or] = STATE(11),
- [sym_equal] = STATE(11),
- [sym_not_equal] = STATE(11),
- [sym_parenthesized] = STATE(11),
- [sym_identifier] = ACTIONS(3),
- [anon_sym_BANG] = ACTIONS(5),
- [anon_sym_LPAREN] = ACTIONS(7),
- },
- [5] =
- {
- [sym__expression] = STATE(12),
- [sym_not] = STATE(12),
- [sym_and] = STATE(12),
- [sym_or] = STATE(12),
- [sym_equal] = STATE(12),
- [sym_not_equal] = STATE(12),
- [sym_parenthesized] = STATE(12),
- [sym_identifier] = ACTIONS(3),
- [anon_sym_BANG] = ACTIONS(5),
- [anon_sym_LPAREN] = ACTIONS(7),
- },
-};
-
-static const uint16_t ts_small_parse_table[] = {
- [0] = 3,
- ACTIONS(11),
- 1,
- anon_sym_EQ_EQ,
- ACTIONS(13),
- 1,
- anon_sym_BANG_EQ,
- ACTIONS(9),
- 4,
- ts_builtin_sym_end,
- anon_sym_AMP_AMP,
- anon_sym_PIPE_PIPE,
- anon_sym_RPAREN,
- [13] = 1,
- ACTIONS(15),
- 4,
- ts_builtin_sym_end,
- anon_sym_AMP_AMP,
- anon_sym_PIPE_PIPE,
- anon_sym_RPAREN,
- [20] = 1,
- ACTIONS(17),
- 4,
- ts_builtin_sym_end,
- anon_sym_AMP_AMP,
- anon_sym_PIPE_PIPE,
- anon_sym_RPAREN,
- [27] = 1,
- ACTIONS(19),
- 4,
- ts_builtin_sym_end,
- anon_sym_AMP_AMP,
- anon_sym_PIPE_PIPE,
- anon_sym_RPAREN,
- [34] = 1,
- ACTIONS(21),
- 4,
- ts_builtin_sym_end,
- anon_sym_AMP_AMP,
- anon_sym_PIPE_PIPE,
- anon_sym_RPAREN,
- [41] = 1,
- ACTIONS(23),
- 4,
- ts_builtin_sym_end,
- anon_sym_AMP_AMP,
- anon_sym_PIPE_PIPE,
- anon_sym_RPAREN,
- [48] = 2,
- ACTIONS(27),
- 1,
- anon_sym_AMP_AMP,
- ACTIONS(25),
- 3,
- ts_builtin_sym_end,
- anon_sym_PIPE_PIPE,
- anon_sym_RPAREN,
- [57] = 3,
- ACTIONS(27),
- 1,
- anon_sym_AMP_AMP,
- ACTIONS(29),
- 1,
- ts_builtin_sym_end,
- ACTIONS(31),
- 1,
- anon_sym_PIPE_PIPE,
- [67] = 3,
- ACTIONS(27),
- 1,
- anon_sym_AMP_AMP,
- ACTIONS(31),
- 1,
- anon_sym_PIPE_PIPE,
- ACTIONS(33),
- 1,
- anon_sym_RPAREN,
- [77] = 1,
- ACTIONS(35),
- 1,
- ts_builtin_sym_end,
- [81] = 1,
- ACTIONS(37),
- 1,
- sym_identifier,
- [85] = 1,
- ACTIONS(39),
- 1,
- sym_identifier,
-};
-
-static const uint32_t ts_small_parse_table_map[] = {
- [SMALL_STATE(6)] = 0, [SMALL_STATE(7)] = 13, [SMALL_STATE(8)] = 20,
- [SMALL_STATE(9)] = 27, [SMALL_STATE(10)] = 34, [SMALL_STATE(11)] = 41,
- [SMALL_STATE(12)] = 48, [SMALL_STATE(13)] = 57, [SMALL_STATE(14)] = 67,
- [SMALL_STATE(15)] = 77, [SMALL_STATE(16)] = 81, [SMALL_STATE(17)] = 85,
-};
-
-static const TSParseActionEntry ts_parse_actions[] = {
- [0] = {.entry = {.count = 0, .reusable = false}},
- [1] = {.entry = {.count = 1, .reusable = false}},
- RECOVER(),
- [3] = {.entry = {.count = 1, .reusable = true}},
- SHIFT(6),
- [5] = {.entry = {.count = 1, .reusable = true}},
- SHIFT(2),
- [7] = {.entry = {.count = 1, .reusable = true}},
- SHIFT(3),
- [9] = {.entry = {.count = 1, .reusable = true}},
- REDUCE(sym__expression, 1),
- [11] = {.entry = {.count = 1, .reusable = true}},
- SHIFT(16),
- [13] = {.entry = {.count = 1, .reusable = true}},
- SHIFT(17),
- [15] = {.entry = {.count = 1, .reusable = true}},
- REDUCE(sym_not, 2, .production_id = 1),
- [17] = {.entry = {.count = 1, .reusable = true}},
- REDUCE(sym_equal, 3, .production_id = 2),
- [19] = {.entry = {.count = 1, .reusable = true}},
- REDUCE(sym_not_equal, 3, .production_id = 2),
- [21] = {.entry = {.count = 1, .reusable = true}},
- REDUCE(sym_parenthesized, 3, .production_id = 1),
- [23] = {.entry = {.count = 1, .reusable = true}},
- REDUCE(sym_and, 3, .production_id = 2),
- [25] = {.entry = {.count = 1, .reusable = true}},
- REDUCE(sym_or, 3, .production_id = 2),
- [27] = {.entry = {.count = 1, .reusable = true}},
- SHIFT(4),
- [29] = {.entry = {.count = 1, .reusable = true}},
- REDUCE(sym_source, 1),
- [31] = {.entry = {.count = 1, .reusable = true}},
- SHIFT(5),
- [33] = {.entry = {.count = 1, .reusable = true}},
- SHIFT(10),
- [35] = {.entry = {.count = 1, .reusable = true}},
- ACCEPT_INPUT(),
- [37] = {.entry = {.count = 1, .reusable = true}},
- SHIFT(8),
- [39] = {.entry = {.count = 1, .reusable = true}},
- SHIFT(9),
-};
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-#ifdef _WIN32
-#define extern __declspec(dllexport)
-#endif
-
-extern const TSLanguage *tree_sitter_context_predicate(void) {
- static const TSLanguage language = {
- .version = LANGUAGE_VERSION,
- .symbol_count = SYMBOL_COUNT,
- .alias_count = ALIAS_COUNT,
- .token_count = TOKEN_COUNT,
- .external_token_count = EXTERNAL_TOKEN_COUNT,
- .state_count = STATE_COUNT,
- .large_state_count = LARGE_STATE_COUNT,
- .production_id_count = PRODUCTION_ID_COUNT,
- .field_count = FIELD_COUNT,
- .max_alias_sequence_length = MAX_ALIAS_SEQUENCE_LENGTH,
- .parse_table = &ts_parse_table[0][0],
- .small_parse_table = ts_small_parse_table,
- .small_parse_table_map = ts_small_parse_table_map,
- .parse_actions = ts_parse_actions,
- .symbol_names = ts_symbol_names,
- .field_names = ts_field_names,
- .field_map_slices = ts_field_map_slices,
- .field_map_entries = ts_field_map_entries,
- .symbol_metadata = ts_symbol_metadata,
- .public_symbol_map = ts_symbol_map,
- .alias_map = ts_non_terminal_alias_map,
- .alias_sequences = &ts_alias_sequences[0][0],
- .lex_modes = ts_lex_modes,
- .lex_fn = ts_lex,
- };
- return &language;
-}
-#ifdef __cplusplus
-}
-#endif
@@ -1,223 +0,0 @@
-#ifndef TREE_SITTER_PARSER_H_
-#define TREE_SITTER_PARSER_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-
-#define ts_builtin_sym_error ((TSSymbol)-1)
-#define ts_builtin_sym_end 0
-#define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024
-
-typedef uint16_t TSStateId;
-
-#ifndef TREE_SITTER_API_H_
-typedef uint16_t TSSymbol;
-typedef uint16_t TSFieldId;
-typedef struct TSLanguage TSLanguage;
-#endif
-
-typedef struct {
- TSFieldId field_id;
- uint8_t child_index;
- bool inherited;
-} TSFieldMapEntry;
-
-typedef struct {
- uint16_t index;
- uint16_t length;
-} TSFieldMapSlice;
-
-typedef struct {
- bool visible;
- bool named;
- bool supertype;
-} TSSymbolMetadata;
-
-typedef struct TSLexer TSLexer;
-
-struct TSLexer {
- int32_t lookahead;
- TSSymbol result_symbol;
- void (*advance)(TSLexer *, bool);
- void (*mark_end)(TSLexer *);
- uint32_t (*get_column)(TSLexer *);
- bool (*is_at_included_range_start)(const TSLexer *);
- bool (*eof)(const TSLexer *);
-};
-
-typedef enum {
- TSParseActionTypeShift,
- TSParseActionTypeReduce,
- TSParseActionTypeAccept,
- TSParseActionTypeRecover,
-} TSParseActionType;
-
-typedef union {
- struct {
- uint8_t type;
- TSStateId state;
- bool extra;
- bool repetition;
- } shift;
- struct {
- uint8_t type;
- uint8_t child_count;
- TSSymbol symbol;
- int16_t dynamic_precedence;
- uint16_t production_id;
- } reduce;
- uint8_t type;
-} TSParseAction;
-
-typedef struct {
- uint16_t lex_state;
- uint16_t external_lex_state;
-} TSLexMode;
-
-typedef union {
- TSParseAction action;
- struct {
- uint8_t count;
- bool reusable;
- } entry;
-} TSParseActionEntry;
-
-struct TSLanguage {
- uint32_t version;
- uint32_t symbol_count;
- uint32_t alias_count;
- uint32_t token_count;
- uint32_t external_token_count;
- uint32_t state_count;
- uint32_t large_state_count;
- uint32_t production_id_count;
- uint32_t field_count;
- uint16_t max_alias_sequence_length;
- const uint16_t *parse_table;
- const uint16_t *small_parse_table;
- const uint32_t *small_parse_table_map;
- const TSParseActionEntry *parse_actions;
- const char * const *symbol_names;
- const char * const *field_names;
- const TSFieldMapSlice *field_map_slices;
- const TSFieldMapEntry *field_map_entries;
- const TSSymbolMetadata *symbol_metadata;
- const TSSymbol *public_symbol_map;
- const uint16_t *alias_map;
- const TSSymbol *alias_sequences;
- const TSLexMode *lex_modes;
- bool (*lex_fn)(TSLexer *, TSStateId);
- bool (*keyword_lex_fn)(TSLexer *, TSStateId);
- TSSymbol keyword_capture_token;
- struct {
- const bool *states;
- const TSSymbol *symbol_map;
- void *(*create)(void);
- void (*destroy)(void *);
- bool (*scan)(void *, TSLexer *, const bool *symbol_whitelist);
- unsigned (*serialize)(void *, char *);
- void (*deserialize)(void *, const char *, unsigned);
- } external_scanner;
-};
-
-/*
- * Lexer Macros
- */
-
-#define START_LEXER() \
- bool result = false; \
- bool skip = false; \
- bool eof = false; \
- int32_t lookahead; \
- goto start; \
- next_state: \
- lexer->advance(lexer, skip); \
- start: \
- skip = false; \
- lookahead = lexer->lookahead;
-
-#define ADVANCE(state_value) \
- { \
- state = state_value; \
- goto next_state; \
- }
-
-#define SKIP(state_value) \
- { \
- skip = true; \
- state = state_value; \
- goto next_state; \
- }
-
-#define ACCEPT_TOKEN(symbol_value) \
- result = true; \
- lexer->result_symbol = symbol_value; \
- lexer->mark_end(lexer);
-
-#define END_STATE() return result;
-
-/*
- * Parse Table Macros
- */
-
-#define SMALL_STATE(id) id - LARGE_STATE_COUNT
-
-#define STATE(id) id
-
-#define ACTIONS(id) id
-
-#define SHIFT(state_value) \
- {{ \
- .shift = { \
- .type = TSParseActionTypeShift, \
- .state = state_value \
- } \
- }}
-
-#define SHIFT_REPEAT(state_value) \
- {{ \
- .shift = { \
- .type = TSParseActionTypeShift, \
- .state = state_value, \
- .repetition = true \
- } \
- }}
-
-#define SHIFT_EXTRA() \
- {{ \
- .shift = { \
- .type = TSParseActionTypeShift, \
- .extra = true \
- } \
- }}
-
-#define REDUCE(symbol_val, child_count_val, ...) \
- {{ \
- .reduce = { \
- .type = TSParseActionTypeReduce, \
- .symbol = symbol_val, \
- .child_count = child_count_val, \
- __VA_ARGS__ \
- }, \
- }}
-
-#define RECOVER() \
- {{ \
- .type = TSParseActionTypeRecover \
- }}
-
-#define ACCEPT_INPUT() \
- {{ \
- .type = TSParseActionTypeAccept \
- }}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // TREE_SITTER_PARSER_H_
@@ -1349,21 +1349,24 @@ impl MutableAppContext {
/// Return keystrokes that would dispatch the given action closest to the focused view, if there are any.
pub(crate) fn keystrokes_for_action(
- &self,
+ &mut self,
window_id: usize,
- dispatch_path: &[usize],
+ view_stack: &[usize],
action: &dyn Action,
) -> Option<SmallVec<[Keystroke; 2]>> {
- for view_id in dispatch_path.iter().rev() {
+ self.keystroke_matcher.contexts.clear();
+ for view_id in view_stack.iter().rev() {
let view = self
.cx
.views
.get(&(window_id, *view_id))
.expect("view in responder chain does not exist");
- let keymap_context = view.keymap_context(self.as_ref());
+ self.keystroke_matcher
+ .contexts
+ .push(view.keymap_context(self.as_ref()));
let keystrokes = self
.keystroke_matcher
- .keystrokes_for_action(action, &keymap_context);
+ .keystrokes_for_action(action, &self.keystroke_matcher.contexts);
if keystrokes.is_some() {
return keystrokes;
}
@@ -6681,7 +6684,7 @@ mod tests {
view_3
});
- // This keymap's only binding dispatches an action on view 2 because that view will have
+ // This binding only dispatches an action on view 2 because that view will have
// "a" and "b" in its context, but not "c".
cx.add_bindings(vec![Binding::new(
"a",
@@ -6691,16 +6694,31 @@ mod tests {
cx.add_bindings(vec![Binding::new("b", Action("b".to_string()), None)]);
+ // This binding only dispatches an action on views 2 and 3, because they have
+ // a parent view with a in its context
+ cx.add_bindings(vec![Binding::new(
+ "c",
+ Action("c".to_string()),
+ Some("b > c"),
+ )]);
+
+ // This binding only dispatches an action on view 2, because they have
+ // a parent view with a in its context
+ cx.add_bindings(vec![Binding::new(
+ "d",
+ Action("d".to_string()),
+ Some("a && !b > b"),
+ )]);
+
let actions = Rc::new(RefCell::new(Vec::new()));
cx.add_action({
let actions = actions.clone();
move |view: &mut View, action: &Action, cx| {
- if action.0 == "a" {
- actions.borrow_mut().push(format!("{} a", view.id));
- } else {
- actions
- .borrow_mut()
- .push(format!("{} {}", view.id, action.0));
+ actions
+ .borrow_mut()
+ .push(format!("{} {}", view.id, action.0));
+
+ if action.0 == "b" {
cx.propagate_action();
}
}
@@ -6714,14 +6732,20 @@ mod tests {
});
cx.dispatch_keystroke(window_id, &Keystroke::parse("a").unwrap());
-
assert_eq!(&*actions.borrow(), &["2 a"]);
-
actions.borrow_mut().clear();
cx.dispatch_keystroke(window_id, &Keystroke::parse("b").unwrap());
-
assert_eq!(&*actions.borrow(), &["3 b", "2 b", "1 b", "global b"]);
+ actions.borrow_mut().clear();
+
+ cx.dispatch_keystroke(window_id, &Keystroke::parse("c").unwrap());
+ assert_eq!(&*actions.borrow(), &["3 c"]);
+ actions.borrow_mut().clear();
+
+ cx.dispatch_keystroke(window_id, &Keystroke::parse("d").unwrap());
+ assert_eq!(&*actions.borrow(), &["2 d"]);
+ actions.borrow_mut().clear();
}
#[crate::test(self)]
@@ -25,6 +25,7 @@ pub struct KeyPressed {
impl_actions!(gpui, [KeyPressed]);
pub struct KeymapMatcher {
+ pub contexts: Vec<KeymapContext>,
pending_views: HashMap<usize, KeymapContext>,
pending_keystrokes: Vec<Keystroke>,
keymap: Keymap,
@@ -33,6 +34,7 @@ pub struct KeymapMatcher {
impl KeymapMatcher {
pub fn new(keymap: Keymap) -> Self {
Self {
+ contexts: Vec::new(),
pending_views: Default::default(),
pending_keystrokes: Vec::new(),
keymap,
@@ -70,7 +72,7 @@ impl KeymapMatcher {
pub fn push_keystroke(
&mut self,
keystroke: Keystroke,
- dispatch_path: Vec<(usize, KeymapContext)>,
+ mut dispatch_path: Vec<(usize, KeymapContext)>,
) -> MatchResult {
let mut any_pending = false;
let mut matched_bindings: Vec<(usize, Box<dyn Action>)> = Vec::new();
@@ -78,7 +80,11 @@ impl KeymapMatcher {
let first_keystroke = self.pending_keystrokes.is_empty();
self.pending_keystrokes.push(keystroke.clone());
- for (view_id, context) in dispatch_path {
+ self.contexts.clear();
+ self.contexts
+ .extend(dispatch_path.iter_mut().map(|e| std::mem::take(&mut e.1)));
+
+ for (i, (view_id, _)) in dispatch_path.into_iter().enumerate() {
// Don't require pending view entry if there are no pending keystrokes
if !first_keystroke && !self.pending_views.contains_key(&view_id) {
continue;
@@ -87,14 +93,15 @@ impl KeymapMatcher {
// If there is a previous view context, invalidate that view if it
// has changed
if let Some(previous_view_context) = self.pending_views.remove(&view_id) {
- if previous_view_context != context {
+ if previous_view_context != self.contexts[i] {
continue;
}
}
// Find the bindings which map the pending keystrokes and current context
for binding in self.keymap.bindings().iter().rev() {
- match binding.match_keys_and_context(&self.pending_keystrokes, &context) {
+ match binding.match_keys_and_context(&self.pending_keystrokes, &self.contexts[i..])
+ {
BindingMatchResult::Complete(mut action) => {
// Swap in keystroke for special KeyPressed action
if action.name() == "KeyPressed" && action.namespace() == "gpui" {
@@ -105,7 +112,7 @@ impl KeymapMatcher {
matched_bindings.push((view_id, action))
}
BindingMatchResult::Partial => {
- self.pending_views.insert(view_id, context.clone());
+ self.pending_views.insert(view_id, self.contexts[i].clone());
any_pending = true;
}
_ => {}
@@ -129,13 +136,13 @@ impl KeymapMatcher {
pub fn keystrokes_for_action(
&self,
action: &dyn Action,
- context: &KeymapContext,
+ contexts: &[KeymapContext],
) -> Option<SmallVec<[Keystroke; 2]>> {
self.keymap
.bindings()
.iter()
.rev()
- .find_map(|binding| binding.keystrokes_for_action(action, context))
+ .find_map(|binding| binding.keystrokes_for_action(action, contexts))
}
}
@@ -349,27 +356,70 @@ mod tests {
}
#[test]
- fn test_context_predicate_eval() -> Result<()> {
- let predicate = KeymapContextPredicate::parse("a && b || c == d")?;
+ fn test_context_predicate_eval() {
+ let predicate = KeymapContextPredicate::parse("a && b || c == d").unwrap();
let mut context = KeymapContext::default();
context.set.insert("a".into());
- assert!(!predicate.eval(&context));
+ assert!(!predicate.eval(&[context]));
+ let mut context = KeymapContext::default();
+ context.set.insert("a".into());
context.set.insert("b".into());
- assert!(predicate.eval(&context));
+ assert!(predicate.eval(&[context]));
- context.set.remove("b");
+ let mut context = KeymapContext::default();
+ context.set.insert("a".into());
context.map.insert("c".into(), "x".into());
- assert!(!predicate.eval(&context));
+ assert!(!predicate.eval(&[context]));
+ let mut context = KeymapContext::default();
+ context.set.insert("a".into());
context.map.insert("c".into(), "d".into());
- assert!(predicate.eval(&context));
+ assert!(predicate.eval(&[context]));
- let predicate = KeymapContextPredicate::parse("!a")?;
- assert!(predicate.eval(&KeymapContext::default()));
+ let predicate = KeymapContextPredicate::parse("!a").unwrap();
+ assert!(predicate.eval(&[KeymapContext::default()]));
+ }
- Ok(())
+ #[test]
+ fn test_context_child_predicate_eval() {
+ let predicate = KeymapContextPredicate::parse("a && b > c").unwrap();
+ let contexts = [
+ context_set(&["e", "f"]),
+ context_set(&["c", "d"]), // match this context
+ context_set(&["a", "b"]),
+ ];
+
+ assert!(!predicate.eval(&contexts[0..]));
+ assert!(predicate.eval(&contexts[1..]));
+ assert!(!predicate.eval(&contexts[2..]));
+
+ let predicate = KeymapContextPredicate::parse("a && b > c && !d > e").unwrap();
+ let contexts = [
+ context_set(&["f"]),
+ context_set(&["e"]), // only match this context
+ context_set(&["c"]),
+ context_set(&["a", "b"]),
+ context_set(&["e"]),
+ context_set(&["c", "d"]),
+ context_set(&["a", "b"]),
+ ];
+
+ assert!(!predicate.eval(&contexts[0..]));
+ assert!(predicate.eval(&contexts[1..]));
+ assert!(!predicate.eval(&contexts[2..]));
+ assert!(!predicate.eval(&contexts[3..]));
+ assert!(!predicate.eval(&contexts[4..]));
+ assert!(!predicate.eval(&contexts[5..]));
+ assert!(!predicate.eval(&contexts[6..]));
+
+ fn context_set(names: &[&str]) -> KeymapContext {
+ KeymapContext {
+ set: names.iter().copied().map(str::to_string).collect(),
+ ..Default::default()
+ }
+ }
}
#[test]
@@ -41,24 +41,24 @@ impl Binding {
})
}
- fn match_context(&self, context: &KeymapContext) -> bool {
+ fn match_context(&self, contexts: &[KeymapContext]) -> bool {
self.context_predicate
.as_ref()
- .map(|predicate| predicate.eval(context))
+ .map(|predicate| predicate.eval(contexts))
.unwrap_or(true)
}
pub fn match_keys_and_context(
&self,
pending_keystrokes: &Vec<Keystroke>,
- context: &KeymapContext,
+ contexts: &[KeymapContext],
) -> BindingMatchResult {
if self
.keystrokes
.as_ref()
.map(|keystrokes| keystrokes.starts_with(&pending_keystrokes))
.unwrap_or(true)
- && self.match_context(context)
+ && self.match_context(contexts)
{
// If the binding is completed, push it onto the matches list
if self
@@ -79,9 +79,9 @@ impl Binding {
pub fn keystrokes_for_action(
&self,
action: &dyn Action,
- context: &KeymapContext,
+ contexts: &[KeymapContext],
) -> Option<SmallVec<[Keystroke; 2]>> {
- if self.action.eq(action) && self.match_context(context) {
+ if self.action.eq(action) && self.match_context(contexts) {
self.keystrokes.clone()
} else {
None
@@ -1,11 +1,5 @@
-use anyhow::anyhow;
-
+use anyhow::{anyhow, Result};
use collections::{HashMap, HashSet};
-use tree_sitter::{Language, Node, Parser};
-
-extern "C" {
- fn tree_sitter_context_predicate() -> Language;
-}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct KeymapContext {
@@ -29,80 +23,25 @@ pub enum KeymapContextPredicate {
Identifier(String),
Equal(String, String),
NotEqual(String, String),
+ Child(Box<KeymapContextPredicate>, Box<KeymapContextPredicate>),
Not(Box<KeymapContextPredicate>),
And(Box<KeymapContextPredicate>, Box<KeymapContextPredicate>),
Or(Box<KeymapContextPredicate>, Box<KeymapContextPredicate>),
}
impl KeymapContextPredicate {
- pub fn parse(source: &str) -> anyhow::Result<Self> {
- let mut parser = Parser::new();
- let language = unsafe { tree_sitter_context_predicate() };
- parser.set_language(language).unwrap();
- let source = source.as_bytes();
- let tree = parser.parse(source, None).unwrap();
- Self::from_node(tree.root_node(), source)
- }
-
- fn from_node(node: Node, source: &[u8]) -> anyhow::Result<Self> {
- let parse_error = "error parsing context predicate";
- let kind = node.kind();
-
- match kind {
- "source" => Self::from_node(node.child(0).ok_or_else(|| anyhow!(parse_error))?, source),
- "identifier" => Ok(Self::Identifier(node.utf8_text(source)?.into())),
- "not" => {
- let child = Self::from_node(
- node.child_by_field_name("expression")
- .ok_or_else(|| anyhow!(parse_error))?,
- source,
- )?;
- Ok(Self::Not(Box::new(child)))
- }
- "and" | "or" => {
- let left = Box::new(Self::from_node(
- node.child_by_field_name("left")
- .ok_or_else(|| anyhow!(parse_error))?,
- source,
- )?);
- let right = Box::new(Self::from_node(
- node.child_by_field_name("right")
- .ok_or_else(|| anyhow!(parse_error))?,
- source,
- )?);
- if kind == "and" {
- Ok(Self::And(left, right))
- } else {
- Ok(Self::Or(left, right))
- }
- }
- "equal" | "not_equal" => {
- let left = node
- .child_by_field_name("left")
- .ok_or_else(|| anyhow!(parse_error))?
- .utf8_text(source)?
- .into();
- let right = node
- .child_by_field_name("right")
- .ok_or_else(|| anyhow!(parse_error))?
- .utf8_text(source)?
- .into();
- if kind == "equal" {
- Ok(Self::Equal(left, right))
- } else {
- Ok(Self::NotEqual(left, right))
- }
- }
- "parenthesized" => Self::from_node(
- node.child_by_field_name("expression")
- .ok_or_else(|| anyhow!(parse_error))?,
- source,
- ),
- _ => Err(anyhow!(parse_error)),
+ pub fn parse(source: &str) -> Result<Self> {
+ let source = Self::skip_whitespace(source);
+ let (predicate, rest) = Self::parse_expr(source, 0)?;
+ if let Some(next) = rest.chars().next() {
+ Err(anyhow!("unexpected character {next:?}"))
+ } else {
+ Ok(predicate)
}
}
- pub fn eval(&self, context: &KeymapContext) -> bool {
+ pub fn eval(&self, contexts: &[KeymapContext]) -> bool {
+ let Some(context) = contexts.first() else { return false };
match self {
Self::Identifier(name) => context.set.contains(name.as_str()),
Self::Equal(left, right) => context
@@ -115,9 +54,245 @@ impl KeymapContextPredicate {
.get(left)
.map(|value| value != right)
.unwrap_or(true),
- Self::Not(pred) => !pred.eval(context),
- Self::And(left, right) => left.eval(context) && right.eval(context),
- Self::Or(left, right) => left.eval(context) || right.eval(context),
+ Self::Not(pred) => !pred.eval(contexts),
+ Self::Child(parent, child) => parent.eval(&contexts[1..]) && child.eval(contexts),
+ Self::And(left, right) => left.eval(contexts) && right.eval(contexts),
+ Self::Or(left, right) => left.eval(contexts) || right.eval(contexts),
+ }
+ }
+
+ fn parse_expr(mut source: &str, min_precedence: u32) -> anyhow::Result<(Self, &str)> {
+ type Op =
+ fn(KeymapContextPredicate, KeymapContextPredicate) -> Result<KeymapContextPredicate>;
+
+ let (mut predicate, rest) = Self::parse_primary(source)?;
+ source = rest;
+
+ 'parse: loop {
+ for (operator, precedence, constructor) in [
+ (">", PRECEDENCE_CHILD, Self::new_child as Op),
+ ("&&", PRECEDENCE_AND, Self::new_and as Op),
+ ("||", PRECEDENCE_OR, Self::new_or as Op),
+ ("==", PRECEDENCE_EQ, Self::new_eq as Op),
+ ("!=", PRECEDENCE_EQ, Self::new_neq as Op),
+ ] {
+ if source.starts_with(operator) && precedence >= min_precedence {
+ source = Self::skip_whitespace(&source[operator.len()..]);
+ let (right, rest) = Self::parse_expr(source, precedence + 1)?;
+ predicate = constructor(predicate, right)?;
+ source = rest;
+ continue 'parse;
+ }
+ }
+ break;
+ }
+
+ Ok((predicate, source))
+ }
+
+ fn parse_primary(mut source: &str) -> anyhow::Result<(Self, &str)> {
+ let next = source
+ .chars()
+ .next()
+ .ok_or_else(|| anyhow!("unexpected eof"))?;
+ match next {
+ '(' => {
+ source = Self::skip_whitespace(&source[1..]);
+ let (predicate, rest) = Self::parse_expr(source, 0)?;
+ if rest.starts_with(')') {
+ source = Self::skip_whitespace(&rest[1..]);
+ Ok((predicate, source))
+ } else {
+ Err(anyhow!("expected a ')'"))
+ }
+ }
+ '!' => {
+ let source = Self::skip_whitespace(&source[1..]);
+ let (predicate, source) = Self::parse_expr(&source, PRECEDENCE_NOT)?;
+ Ok((KeymapContextPredicate::Not(Box::new(predicate)), source))
+ }
+ _ if next.is_alphanumeric() || next == '_' => {
+ let len = source
+ .find(|c: char| !(c.is_alphanumeric() || c == '_'))
+ .unwrap_or(source.len());
+ let (identifier, rest) = source.split_at(len);
+ source = Self::skip_whitespace(rest);
+ Ok((
+ KeymapContextPredicate::Identifier(identifier.into()),
+ source,
+ ))
+ }
+ _ => Err(anyhow!("unexpected character {next:?}")),
+ }
+ }
+
+ fn skip_whitespace(source: &str) -> &str {
+ let len = source
+ .find(|c: char| !c.is_whitespace())
+ .unwrap_or(source.len());
+ &source[len..]
+ }
+
+ fn new_or(self, other: Self) -> Result<Self> {
+ Ok(Self::Or(Box::new(self), Box::new(other)))
+ }
+
+ fn new_and(self, other: Self) -> Result<Self> {
+ Ok(Self::And(Box::new(self), Box::new(other)))
+ }
+
+ fn new_child(self, other: Self) -> Result<Self> {
+ Ok(Self::Child(Box::new(self), Box::new(other)))
+ }
+
+ fn new_eq(self, other: Self) -> Result<Self> {
+ if let (Self::Identifier(left), Self::Identifier(right)) = (self, other) {
+ Ok(Self::Equal(left, right))
+ } else {
+ Err(anyhow!("operands must be identifiers"))
+ }
+ }
+
+ fn new_neq(self, other: Self) -> Result<Self> {
+ if let (Self::Identifier(left), Self::Identifier(right)) = (self, other) {
+ Ok(Self::NotEqual(left, right))
+ } else {
+ Err(anyhow!("operands must be identifiers"))
}
}
}
+
+const PRECEDENCE_CHILD: u32 = 1;
+const PRECEDENCE_OR: u32 = 2;
+const PRECEDENCE_AND: u32 = 3;
+const PRECEDENCE_EQ: u32 = 4;
+const PRECEDENCE_NOT: u32 = 5;
+
+#[cfg(test)]
+mod tests {
+ use super::KeymapContextPredicate::{self, *};
+
+ #[test]
+ fn test_parse_identifiers() {
+ // Identifiers
+ assert_eq!(
+ KeymapContextPredicate::parse("abc12").unwrap(),
+ Identifier("abc12".into())
+ );
+ assert_eq!(
+ KeymapContextPredicate::parse("_1a").unwrap(),
+ Identifier("_1a".into())
+ );
+ }
+
+ #[test]
+ fn test_parse_negations() {
+ assert_eq!(
+ KeymapContextPredicate::parse("!abc").unwrap(),
+ Not(Box::new(Identifier("abc".into())))
+ );
+ assert_eq!(
+ KeymapContextPredicate::parse(" ! ! abc").unwrap(),
+ Not(Box::new(Not(Box::new(Identifier("abc".into())))))
+ );
+ }
+
+ #[test]
+ fn test_parse_equality_operators() {
+ assert_eq!(
+ KeymapContextPredicate::parse("a == b").unwrap(),
+ Equal("a".into(), "b".into())
+ );
+ assert_eq!(
+ KeymapContextPredicate::parse("c!=d").unwrap(),
+ NotEqual("c".into(), "d".into())
+ );
+ assert_eq!(
+ KeymapContextPredicate::parse("c == !d")
+ .unwrap_err()
+ .to_string(),
+ "operands must be identifiers"
+ );
+ }
+
+ #[test]
+ fn test_parse_boolean_operators() {
+ assert_eq!(
+ KeymapContextPredicate::parse("a || b").unwrap(),
+ Or(
+ Box::new(Identifier("a".into())),
+ Box::new(Identifier("b".into()))
+ )
+ );
+ assert_eq!(
+ KeymapContextPredicate::parse("a || !b && c").unwrap(),
+ Or(
+ Box::new(Identifier("a".into())),
+ Box::new(And(
+ Box::new(Not(Box::new(Identifier("b".into())))),
+ Box::new(Identifier("c".into()))
+ ))
+ )
+ );
+ assert_eq!(
+ KeymapContextPredicate::parse("a && b || c&&d").unwrap(),
+ Or(
+ Box::new(And(
+ Box::new(Identifier("a".into())),
+ Box::new(Identifier("b".into()))
+ )),
+ Box::new(And(
+ Box::new(Identifier("c".into())),
+ Box::new(Identifier("d".into()))
+ ))
+ )
+ );
+ assert_eq!(
+ KeymapContextPredicate::parse("a == b && c || d == e && f").unwrap(),
+ Or(
+ Box::new(And(
+ Box::new(Equal("a".into(), "b".into())),
+ Box::new(Identifier("c".into()))
+ )),
+ Box::new(And(
+ Box::new(Equal("d".into(), "e".into())),
+ Box::new(Identifier("f".into()))
+ ))
+ )
+ );
+ assert_eq!(
+ KeymapContextPredicate::parse("a && b && c && d").unwrap(),
+ And(
+ Box::new(And(
+ Box::new(And(
+ Box::new(Identifier("a".into())),
+ Box::new(Identifier("b".into()))
+ )),
+ Box::new(Identifier("c".into())),
+ )),
+ Box::new(Identifier("d".into()))
+ ),
+ );
+ }
+
+ #[test]
+ fn test_parse_parenthesized_expressions() {
+ assert_eq!(
+ KeymapContextPredicate::parse("a && (b == c || d != e)").unwrap(),
+ And(
+ Box::new(Identifier("a".into())),
+ Box::new(Or(
+ Box::new(Equal("b".into(), "c".into())),
+ Box::new(NotEqual("d".into(), "e".into())),
+ )),
+ ),
+ );
+ assert_eq!(
+ KeymapContextPredicate::parse(" ( a || b ) ").unwrap(),
+ Or(
+ Box::new(Identifier("a".into())),
+ Box::new(Identifier("b".into())),
+ )
+ );
+ }
+}
@@ -573,7 +573,7 @@ pub struct LayoutContext<'a> {
impl<'a> LayoutContext<'a> {
pub(crate) fn keystrokes_for_action(
- &self,
+ &mut self,
action: &dyn Action,
) -> Option<SmallVec<[Keystroke; 2]>> {
self.app