Skip to content

Commit 77a3b55

Browse files
Add the scope visualizer (#1653)
- Depends on #1651 - Depends on #1652 - Depends on #1649 - Depends on #1644 ## Checklist - [x] Add image to doc page - [x] File issue to migrate all our vscode references to use new thin wrapper - [x] File issue about reworking removal highlight range to return generalised range, and not using line range for line target content range - [x] Investigate cheatsheet performance issue - [x] Migrate todos from #1523 - [x] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [x] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [x] I have not broken the cheatsheet - [x] Compute border colors from background color? --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
1 parent 5106faa commit 77a3b55

39 files changed

+1527
-6
lines changed

cursorless-talon/src/cheatsheet/cheat_sheet.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from .get_list import get_list, get_lists
88
from .sections.actions import get_actions
99
from .sections.compound_targets import get_compound_targets
10+
from .sections.get_scope_visualizer import get_scope_visualizer
1011
from .sections.modifiers import get_modifiers
1112
from .sections.scopes import get_scopes
1213
from .sections.special_marks import get_special_marks
@@ -102,6 +103,11 @@ def cursorless_cheat_sheet_get_json():
102103
"id": "scopes",
103104
"items": get_scopes(),
104105
},
106+
{
107+
"name": "Scope visualizer",
108+
"id": "scopeVisualizer",
109+
"items": get_scope_visualizer(),
110+
},
105111
{
106112
"name": "Modifiers",
107113
"id": "modifiers",
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from ..get_list import get_list, get_raw_list, make_readable
2+
3+
4+
def get_scope_visualizer():
5+
show_scope_visualizer = list(get_raw_list("show_scope_visualizer").keys())[0]
6+
visualization_types = get_raw_list("visualization_type")
7+
8+
return [
9+
*get_list("hide_scope_visualizer", "command"),
10+
{
11+
"id": "show_scope_visualizer",
12+
"type": "command",
13+
"variations": [
14+
{
15+
"spokenForm": f"{show_scope_visualizer} <scope>",
16+
"description": "Visualize <scope>",
17+
},
18+
*[
19+
{
20+
"spokenForm": f"{show_scope_visualizer} <scope> {spoken_form}",
21+
"description": f"Visualize <scope> {make_readable(id).lower()} range",
22+
}
23+
for spoken_form, id in visualization_types.items()
24+
],
25+
],
26+
},
27+
]

cursorless-talon/src/cursorless.talon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,8 @@ tag: user.cursorless
2020
user.cursorless_wrap(cursorless_wrap_action, cursorless_target, cursorless_wrapper)
2121

2222
{user.cursorless_homophone} settings: user.cursorless_show_settings_in_ide()
23+
24+
{user.cursorless_show_scope_visualizer} <user.cursorless_scope_type> [{user.cursorless_visualization_type}]:
25+
user.private_cursorless_show_scope_visualizer(cursorless_scope_type, cursorless_visualization_type or "content")
26+
{user.cursorless_hide_scope_visualizer}:
27+
user.private_cursorless_hide_scope_visualizer()
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from talon import Module, app
2+
3+
from .csv_overrides import init_csv_and_watch_changes
4+
from .cursorless_command_server import run_rpc_command_no_wait
5+
6+
mod = Module()
7+
mod.list("cursorless_show_scope_visualizer", desc="Show scope visualizer")
8+
mod.list("cursorless_hide_scope_visualizer", desc="Hide scope visualizer")
9+
mod.list(
10+
"cursorless_visualization_type",
11+
desc='Cursorless visualization type, e.g. "removal" or "iteration"',
12+
)
13+
14+
# NOTE: Please do not change these dicts. Use the CSVs for customization.
15+
# See https://www.cursorless.org/docs/user/customization/
16+
visualization_types = {
17+
"removal": "removal",
18+
"iteration": "iteration",
19+
}
20+
21+
22+
@mod.action_class
23+
class Actions:
24+
def private_cursorless_show_scope_visualizer(
25+
scope_type: dict, visualization_type: str
26+
):
27+
"""Shows scope visualizer"""
28+
run_rpc_command_no_wait(
29+
"cursorless.showScopeVisualizer", scope_type, visualization_type
30+
)
31+
32+
def private_cursorless_hide_scope_visualizer():
33+
"""Hides scope visualizer"""
34+
run_rpc_command_no_wait("cursorless.hideScopeVisualizer")
35+
36+
37+
def on_ready():
38+
init_csv_and_watch_changes(
39+
"scope_visualizer",
40+
{
41+
"show_scope_visualizer": {"visualize": "showScopeVisualizer"},
42+
"hide_scope_visualizer": {"visualize nothing": "hideScopeVisualizer"},
43+
"visualization_type": visualization_types,
44+
},
45+
)
46+
47+
48+
app.register("ready", on_ready)

docs/user/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ Note that if the mark is `"this"`, and you have multiple cursors, the modifiers
143143

144144
##### Syntactic scopes
145145

146-
For programming languages where Cursorless has rich parse tree support, we support modifiers that expand to the nearest containing function, class, etc. See [the source code](../../queries) for a list of supported languages. Some languages are still supported using our legacy implementation; those will be listed in [here](../../packages/cursorless-engine/src/languages/LegacyLanguageId.ts). Below is a list of supported scope types, keeping in mind that this table can sometimes lag behind the actual list. Your cheatsheet (say `"cursorless cheatsheet"` with VSCode focused) will have the most up-to-date list.
146+
For programming languages where Cursorless has rich parse tree support, we support modifiers that expand to the nearest containing function, class, etc. See [the source code](../../queries) for a list of supported languages. Some languages are still supported using our legacy implementation; those will be listed in [here](../../packages/cursorless-engine/src/languages/LegacyLanguageId.ts). Below is a list of supported scope types, keeping in mind that this table can sometimes lag behind the actual list. Your cheatsheet (say `"cursorless cheatsheet"` with VSCode focused) will have the most up-to-date list. It can also be helpful to use the [scope visualizer](./scope-visualizer.md) to visualize the scope types on your own code.
147147

148148
| Term | Syntactic element |
149149
| -------------- | --------------------------------------------------- |
148 KB
Loading

docs/user/images/visualize-funk.png

133 KB
Loading

docs/user/images/visualize-token.png

152 KB
Loading

docs/user/scope-visualizer.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Scope visualizer
2+
3+
The scope visualizer allows you to visualize Cursorless scopes on your code in real time. It is useful to understand how Cursorless scopes work, and is also useful for Cursorless contributors as they develop new scopes.
4+
5+
## Usage
6+
7+
To start the scope visualizer, say `"visualize <scope>"`, where `<scope>` is the name of the scope you want to visualize. For example, `"visualize funk"`. To stop the scope visualizer, say `"visualize nothing"`.
8+
9+
You can also visualize removal and iteration ranges for scopes by saying `"visualize <scope> removal"` and `"visualize <scope> iteration"`, respectively.
10+
11+
## Examples
12+
13+
### `"visualize funk"`
14+
15+
![visualize funk](images/visualize-funk.png)
16+
17+
### `"visualize token"`
18+
19+
![visualize token](images/visualize-token.png)
20+
21+
### `"visualize block removal"`
22+
23+
![visualize block removal](images/visualize-block-removal.png)

packages/cheatsheet/src/lib/sampleSpokenFormInfos/defaults.json

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,6 +1030,40 @@
10301030
}
10311031
]
10321032
},
1033+
{
1034+
"name": "Scope visualizer",
1035+
"id": "scopeVisualizer",
1036+
"items": [
1037+
{
1038+
"id": "hideScopeVisualizer",
1039+
"type": "command",
1040+
"variations": [
1041+
{
1042+
"spokenForm": "visualize nothing",
1043+
"description": "Hide scope visualizer"
1044+
}
1045+
]
1046+
},
1047+
{
1048+
"id": "show_scope_visualizer",
1049+
"type": "command",
1050+
"variations": [
1051+
{
1052+
"spokenForm": "visualize <scope>",
1053+
"description": "Visualize <scope>"
1054+
},
1055+
{
1056+
"spokenForm": "visualize <scope> removal",
1057+
"description": "Visualize <scope> removal range"
1058+
},
1059+
{
1060+
"spokenForm": "visualize <scope> iteration",
1061+
"description": "Visualize <scope> iteration range"
1062+
}
1063+
]
1064+
}
1065+
]
1066+
},
10331067
{
10341068
"name": "Scopes",
10351069
"id": "scopes",
@@ -1374,6 +1408,16 @@
13741408
}
13751409
]
13761410
},
1411+
{
1412+
"id": "sentence",
1413+
"type": "scopeType",
1414+
"variations": [
1415+
{
1416+
"spokenForm": "sentence",
1417+
"description": "Sentence"
1418+
}
1419+
]
1420+
},
13771421
{
13781422
"id": "statement",
13791423
"type": "scopeType",

0 commit comments

Comments
 (0)