Repository: talonhub/community

Issue:
Support chaining keywords in programming languages
## The goal

I have a [commit](https://github.com/pokey/pokey_talon/commit/aab118daa6f027e67a1075f025b7869ffde64168) on my repo that enables me to chain programming language keywords, for example `"state return null"` or `"state export default const"` , instead of `"state return state null"` or `"state export state default state const"`.  I’m trying to figure out how to reconcile this setup with @wenkokke’s fine-grained tags [commit](https://github.com/knausj85/knausj_talon/commit/73d94794bcb330374f18432b0f9f2af35082849c).

## How it works on my branch

Here are the important parts of my commit:

We first define a list and an action for keywords:
https://github.com/knausj85/knausj_talon/blob/aab118daa6f027e67a1075f025b7869ffde64168/code/keywords.py#L4-L12

Then define a spoken form that allows "state" followed by one or more keywords:
https://github.com/knausj85/knausj_talon/blob/aab118daa6f027e67a1075f025b7869ffde64168/lang/programming.talon#L33

Then each language defines its keywords, including spacing around them.  See eg Typescript:
https://github.com/knausj85/knausj_talon/blob/aab118daa6f027e67a1075f025b7869ffde64168/lang/typescript/typescript.py#L50-L77

## The problems

There’s kind of two problems to solve:

### Many tags

The first is how we handle the fact that after @wenkokke’s commit, these keywords are now spread across many tags. 
 We would somehow need to define the lists in all the separate tags, then merge all of them together in a capture or something?

### Actions vs lists
The other problem is that I just use a list of strings to insert, whereas in knausj they are actions.  The latter is a bit more flexible, but I’m not sure how to reconcile that with my approach


Discussion:
We could adopt my suggestion of giving each root tag, like op, a unique prefix, e.g., “op”?
@wenkokke sorry I'm not sure I understand.  Could you possibly elaborate?
I also think there's a related point around being able to support two use cases in knausj:

1. to write simple keywords, and
2. to build more complex expressions.

For example I might want to just output `if ` (1. above), or I might want to insert an `if` statement including parens and brackets (2. above).  The latter should be an action so that I can eg override it to use snippets.  Maybe the former could just be a list, and allow chaining?  Fwiw in my repo the former is covered by `state if`, and the latter is `snip if`

Resolving patch:
diff --git a/lang/javascript/javascript.py b/lang/javascript/javascript.py
--- a/lang/javascript/javascript.py
+++ b/lang/javascript/javascript.py
@@ -44,6 +44,32 @@
     "then": "then",
 }
 
+ctx.lists["user.code_keyword"] = {
+    "a sink": "async ",
+    "await": "await ",
+    "break": "break",
+    "class": "class ",
+    "const": "const ",
+    "continue": "continue",
+    "default": "default ",
+    "export": "export ",
+    "false": "false",
+    "function": "function ",
+    "import": "import ",
+    "let": "let ",
+    "new": "new ",
+    "null": "null",
+    "private": "private ",
+    "protected": "protected ",
+    "public": "public ",
+    "return": "return ",
+    "throw": "throw ",
+    "true": "true",
+    "try": "try ",
+    "undefined": "undefined",
+    "yield": "yield ",
+}
+
 
 @ctx.action_class("user")
 class UserActions:
diff --git a/lang/python/python.py b/lang/python/python.py
--- a/lang/python/python.py
+++ b/lang/python/python.py
@@ -59,6 +59,20 @@
     "no return": "NoReturn",
 }
 
+ctx.lists["user.code_keyword"] = {
+    "break": "break",
+    "continue": "continue",
+    "class": "class ",
+    "return": "return ",
+    "import": "import ",
+    "null": "None",
+    "none": "None",
+    "true": "True",
+    "false": "False",
+    "yield": "yield ",
+    "from": "from ",
+}
+
 exception_list = [
     "BaseException",
     "SystemExit",
diff --git a/lang/tags/keywords.py b/lang/tags/keywords.py
new file mode 100644
--- /dev/null
+++ b/lang/tags/keywords.py
@@ -0,0 +1,20 @@
+from talon import Module, actions
+
+mod = Module()
+
+mod.tag("code_keywords", desc="Tag for enabling commands for keywords")
+
+mod.list("code_keyword", desc="List of keywords for active language")
+
+
+@mod.capture(rule=("{user.code_keyword}"))
+def code_keyword(m) -> str:
+    return str(m)
+
+
+@mod.action_class
+class Actions:
+    def code_keyword(keywords: list[str]):
+        """Adds keywords"""
+        for keyword in keywords:
+            actions.insert(keyword)


Test patch: