Merge pull request #2034 from zed-industries/tab-focus-search

Max Brunsfeld created

Use tab instead of command-f to move focus from the search editor to the main editor

Change summary

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(-)

Detailed changes

Cargo.lock 🔗

@@ -2563,7 +2563,6 @@ dependencies = [
  "sum_tree",
  "time 0.3.17",
  "tiny-skia",
- "tree-sitter",
  "usvg",
  "util",
  "waker-fn",

assets/keymaps/default.json 🔗

@@ -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"
         }

crates/gpui/Cargo.toml 🔗

@@ -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"
 

crates/gpui/build.rs 🔗

@@ -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() {

crates/gpui/grammars/context-predicate/Cargo.toml 🔗

@@ -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"

crates/gpui/grammars/context-predicate/binding.gyp 🔗

@@ -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",
-      ]
-    }
-  ]
-}

crates/gpui/grammars/context-predicate/bindings/node/binding.cc 🔗

@@ -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

crates/gpui/grammars/context-predicate/bindings/node/index.js 🔗

@@ -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 (_) {}

crates/gpui/grammars/context-predicate/bindings/rust/build.rs 🔗

@@ -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());
-    */
-}

crates/gpui/grammars/context-predicate/bindings/rust/lib.rs 🔗

@@ -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");
-    }
-}

crates/gpui/grammars/context-predicate/corpus/expressions.txt 🔗

@@ -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))))))

crates/gpui/grammars/context-predicate/grammar.js 🔗

@@ -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), ")"),
-  }
-});

crates/gpui/grammars/context-predicate/package-lock.json 🔗

@@ -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
-    }
-  }
-}

crates/gpui/grammars/context-predicate/src/grammar.json 🔗

@@ -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": []
-}
-

crates/gpui/grammars/context-predicate/src/node-types.json 🔗

@@ -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
-  }
-]

crates/gpui/grammars/context-predicate/src/parser.c 🔗

@@ -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

crates/gpui/grammars/context-predicate/src/tree_sitter/parser.h 🔗

@@ -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_

crates/gpui/src/app.rs 🔗

@@ -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)]

crates/gpui/src/keymap_matcher.rs 🔗

@@ -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]

crates/gpui/src/keymap_matcher/binding.rs 🔗

@@ -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

crates/gpui/src/keymap_matcher/keymap_context.rs 🔗

@@ -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())),
+            )
+        );
+    }
+}

crates/gpui/src/presenter.rs 🔗

@@ -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