Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate snippets for community format #2744

Merged
merged 12 commits into from
Jan 23, 2025
3 changes: 2 additions & 1 deletion cursorless-talon/src/actions/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@

# Don't wait for these actions to finish, usually because they hang on some kind of user interaction
no_wait_actions = [
"generateSnippet",
"rename",
]

Expand Down Expand Up @@ -99,6 +98,8 @@ def cursorless_command(action_name: str, target: CursorlessExplicitTarget): # p
)
elif action_name == "callAsFunction":
actions.user.private_cursorless_call(target)
elif action_name == "generateSnippet":
actions.user.private_cursorless_generate_snippet_action(target)
elif action_name in no_wait_actions:
action = {"name": action_name, "target": target}
actions.user.private_cursorless_command_no_wait(action)
Expand Down
71 changes: 71 additions & 0 deletions cursorless-talon/src/actions/generate_snippet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import glob
from pathlib import Path

from talon import Context, Module, actions, settings

from ..targets.target_types import CursorlessExplicitTarget

mod = Module()

ctx = Context()
ctx.matches = r"""
tag: user.cursorless_use_community_snippets
"""


@mod.action_class
class Actions:
def private_cursorless_generate_snippet_action(target: CursorlessExplicitTarget): # pyright: ignore [reportGeneralTypeIssues]
"""Generate a snippet from the given target"""
actions.user.private_cursorless_command_no_wait(
{
"name": "generateSnippet",
"target": target,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By default don't send dirPath argument. This is for the legacy Cursorless snippet format. The vscode setting for snippet directory will be used.

}
)


@ctx.action_class("user")
class UserActions:
def private_cursorless_generate_snippet_action(target: CursorlessExplicitTarget): # pyright: ignore [reportGeneralTypeIssues]
actions.user.private_cursorless_command_no_wait(
{
"name": "generateSnippet",
"target": target,
"dirPath": get_dir_path(),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the community snippet tag has been set send dirPath argument. A new community snippet will be added to this folder.

}
)


def get_dir_path() -> str:
settings_dir = get_setting_dir()
if settings_dir is not None:
return settings_dir
return get_community_snippets_dir()


def get_setting_dir() -> str | None:
try:
setting_dir = settings.get("user.snippets_dir")
if not setting_dir:
return None

dir = Path(str(setting_dir))

if not dir.is_absolute():
user_dir = Path(actions.path.talon_user())
dir = user_dir / dir

return str(dir.resolve())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd make all of these return Path and only coalesce to a string when you need to stick it into the JSON dictionary

except Exception:
return None


def get_community_snippets_dir() -> str:
files = glob.iglob(
f"{actions.path.talon_user()}/**/snippets/snippets/*.snippet",
recursive=True,
)
for file in files:
return str(Path(file).parent)
raise ValueError("Could not find community snippets directory")
55 changes: 55 additions & 0 deletions data/fixtures/recorded/actions/snippets/snipMakeFunk2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
languageId: typescript
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double up on existing snippet make tests in the community format

command:
version: 7
spokenForm: snippet make funk
action:
name: generateSnippet
dirPath: ""
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Including the dirPath argument indicates that this is a community snippet.

snippetName: snippetTest1
target:
type: primitive
modifiers:
- type: containingScope
scopeType: {type: namedFunction}
usePrePhraseSnapshot: true
spokenFormError: generateSnippet.snippetName
initialState:
documentContents: |2-
function helloWorld() {
const whatever = "hello";

if (whatever == null) {
console.log("hello")
}
}
selections:
- anchor: {line: 0, character: 13}
active: {line: 0, character: 23}
- anchor: {line: 3, character: 8}
active: {line: 5, character: 9}
marks: {}
finalState:
documentContents: |
name: snippetTest1
language: typescript
phrase:

$1.wrapperPhrase:
$0.wrapperPhrase:
-
function $1() {
const whatever = "hello";

$0
}
---
selections:
- anchor: {line: 2, character: 8}
active: {line: 2, character: 8}
thatMark:
- type: UntypedTarget
contentRange:
start: {line: 0, character: 4}
end: {line: 6, character: 5}
isReversed: false
hasExplicitRange: true
46 changes: 46 additions & 0 deletions data/fixtures/recorded/actions/snippets/snipMakeState2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
languageId: typescript
command:
version: 7
spokenForm: snippet make state
action:
name: generateSnippet
dirPath: ""
snippetName: snippetTest1
target:
type: primitive
modifiers:
- type: containingScope
scopeType: {type: statement}
usePrePhraseSnapshot: true
spokenFormError: generateSnippet.snippetName
initialState:
documentContents: |-
if () {
console.log("hello")
}
selections:
- anchor: {line: 0, character: 4}
active: {line: 0, character: 4}
marks: {}
finalState:
documentContents: |
name: snippetTest1
language: typescript
phrase:

$0.wrapperPhrase:
-
if ($0) {
console.log("hello")
}
---
selections:
- anchor: {line: 2, character: 8}
active: {line: 2, character: 8}
thatMark:
- type: UntypedTarget
contentRange:
start: {line: 0, character: 0}
end: {line: 2, character: 1}
isReversed: false
hasExplicitRange: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
languageId: plaintext
command:
version: 7
spokenForm: test snippet make line
action:
name: generateSnippet
dirPath: ""
snippetName: testSnippet
target:
type: primitive
modifiers:
- type: containingScope
scopeType: {type: line}
usePrePhraseSnapshot: true
spokenFormError: generateSnippet.snippetName
initialState:
documentContents: \textbf{$foo}
selections:
- anchor: {line: 0, character: 9}
active: {line: 0, character: 12}
marks: {}
finalState:
documentContents: |
name: testSnippet
language: plaintext
phrase:

$0.wrapperPhrase:
-
\textbf{\$$0}
---
selections:
- anchor: {line: 2, character: 8}
active: {line: 2, character: 8}
thatMark:
- type: UntypedTarget
contentRange:
start: {line: 0, character: 0}
end: {line: 0, character: 13}
isReversed: false
hasExplicitRange: true
1 change: 1 addition & 0 deletions packages/common/src/types/command/ActionDescriptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ export interface PasteActionDescriptor {

export interface GenerateSnippetActionDescriptor {
name: "generateSnippet";
dirPath?: string;
snippetName?: string;
target: PartialTargetDescriptor;
}
Expand Down
1 change: 1 addition & 0 deletions packages/cursorless-engine/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"lodash-es": "^4.17.21",
"moo": "0.5.2",
"nearley": "2.20.1",
"talon-snippets": "1.1.0",
"uuid": "^10.0.0",
"zod": "3.23.8"
},
Expand Down
Loading
Loading