From f3e979bd599c50db7cf19070911732d35dcd8df3 Mon Sep 17 00:00:00 2001 From: River Tae Smith <22485304+r-tae@users.noreply.github.com> Date: Mon, 4 Sep 2023 02:33:01 +1000 Subject: [PATCH 01/20] Initial WIP elixir support --- data/playground/elixir.exs | 5 +++ .../recorded/languages/elixir/changeFunk.yml | 27 ++++++++++++ .../languages/elixir/changeFunk10.yml | 26 ++++++++++++ .../languages/elixir/changeFunk11.yml | 26 ++++++++++++ .../languages/elixir/changeFunk12.yml | 28 +++++++++++++ .../languages/elixir/changeFunk13.yml | 25 +++++++++++ .../recorded/languages/elixir/changeFunk2.yml | 27 ++++++++++++ .../recorded/languages/elixir/changeFunk3.yml | 25 +++++++++++ .../recorded/languages/elixir/changeFunk4.yml | 25 +++++++++++ .../recorded/languages/elixir/changeFunk5.yml | 26 ++++++++++++ .../recorded/languages/elixir/changeFunk6.yml | 26 ++++++++++++ .../recorded/languages/elixir/changeFunk7.yml | 27 ++++++++++++ .../recorded/languages/elixir/changeFunk8.yml | 26 ++++++++++++ .../recorded/languages/elixir/changeFunk9.yml | 26 ++++++++++++ .../languages/elixir/changeKeyFine.yml | 27 ++++++++++++ .../languages/elixir/chuckItemTwo.yml | 27 ++++++++++++ queries/elixir.scm | 41 +++++++++++++++++++ 17 files changed, 440 insertions(+) create mode 100644 data/playground/elixir.exs create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk10.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk11.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk12.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk13.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk3.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk4.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk5.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk6.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk7.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk8.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk9.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeKeyFine.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/chuckItemTwo.yml create mode 100644 queries/elixir.scm diff --git a/data/playground/elixir.exs b/data/playground/elixir.exs new file mode 100644 index 0000000000..105de80899 --- /dev/null +++ b/data/playground/elixir.exs @@ -0,0 +1,5 @@ +defmodule Elixir do + def elixir(x, something) when something = "inside" do + IO.inspect(Enum.count([Integer.digits(532), 2, 3])) + end +end diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk.yml new file mode 100644 index 0000000000..591582707e --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk.yml @@ -0,0 +1,27 @@ +languageId: elixir +command: + version: 6 + spokenForm: change funk + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: namedFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: | + def fun() do + # body + end + selections: + - anchor: {line: 1, character: 2} + active: {line: 1, character: 2} + marks: {} +finalState: + documentContents: |+ + + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk10.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk10.yml new file mode 100644 index 0000000000..3cbf333698 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk10.yml @@ -0,0 +1,26 @@ +languageId: elixir +command: + version: 6 + spokenForm: change funk + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: namedFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + def fun(x) when x == 1 do + x + end + selections: + - anchor: {line: 1, character: 2} + active: {line: 1, character: 2} + marks: {} +finalState: + documentContents: "" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk11.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk11.yml new file mode 100644 index 0000000000..c70aeebb75 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk11.yml @@ -0,0 +1,26 @@ +languageId: elixir +command: + version: 6 + spokenForm: change funk + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: namedFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + defp fun(x) do + x + end + selections: + - anchor: {line: 1, character: 2} + active: {line: 1, character: 2} + marks: {} +finalState: + documentContents: "" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk12.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk12.yml new file mode 100644 index 0000000000..afe4bd0370 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk12.yml @@ -0,0 +1,28 @@ +languageId: elixir +command: + version: 6 + spokenForm: change funk + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: namedFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + defmacro fun(x) do + quote do + [unquote(x)] + end + end + selections: + - anchor: {line: 2, character: 4} + active: {line: 2, character: 4} + marks: {} +finalState: + documentContents: "" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk13.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk13.yml new file mode 100644 index 0000000000..30767e532b --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk13.yml @@ -0,0 +1,25 @@ +languageId: elixir +command: + version: 6 + spokenForm: change funk + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: namedFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: | + defguard is_even(term) when is_integer(term) and rem(term, 2) == 0 + selections: + - anchor: {line: 0, character: 26} + active: {line: 0, character: 26} + marks: {} +finalState: + documentContents: |+ + + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk2.yml new file mode 100644 index 0000000000..96cc853d71 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk2.yml @@ -0,0 +1,27 @@ +languageId: elixir +command: + version: 6 + spokenForm: change funk + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: namedFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: | + def fun do + # body + end + selections: + - anchor: {line: 1, character: 2} + active: {line: 1, character: 2} + marks: {} +finalState: + documentContents: |+ + + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk3.yml new file mode 100644 index 0000000000..31bd410663 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk3.yml @@ -0,0 +1,25 @@ +languageId: elixir +command: + version: 6 + spokenForm: change funk + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: namedFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: | + def fun, do: 1 + selections: + - anchor: {line: 0, character: 14} + active: {line: 0, character: 14} + marks: {} +finalState: + documentContents: |+ + + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk4.yml new file mode 100644 index 0000000000..a2e32aba68 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk4.yml @@ -0,0 +1,25 @@ +languageId: elixir +command: + version: 6 + spokenForm: change funk + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: namedFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: | + def fun(), do: 1 + selections: + - anchor: {line: 0, character: 16} + active: {line: 0, character: 16} + marks: {} +finalState: + documentContents: |+ + + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk5.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk5.yml new file mode 100644 index 0000000000..c10c01c339 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk5.yml @@ -0,0 +1,26 @@ +languageId: elixir +command: + version: 6 + spokenForm: change funk + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: namedFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + def fun(x) do + x + end + selections: + - anchor: {line: 1, character: 2} + active: {line: 1, character: 2} + marks: {} +finalState: + documentContents: "" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk6.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk6.yml new file mode 100644 index 0000000000..d9ec85f355 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk6.yml @@ -0,0 +1,26 @@ +languageId: elixir +command: + version: 6 + spokenForm: change funk + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: namedFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + def fun x do + x + end + selections: + - anchor: {line: 1, character: 2} + active: {line: 1, character: 2} + marks: {} +finalState: + documentContents: "" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk7.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk7.yml new file mode 100644 index 0000000000..cb6bfc73bf --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk7.yml @@ -0,0 +1,27 @@ +languageId: elixir +command: + version: 6 + spokenForm: change funk + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: namedFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: | + def fun(x, y) do + x + y + end + selections: + - anchor: {line: 1, character: 2} + active: {line: 1, character: 2} + marks: {} +finalState: + documentContents: |+ + + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk8.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk8.yml new file mode 100644 index 0000000000..f842c7f924 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk8.yml @@ -0,0 +1,26 @@ +languageId: elixir +command: + version: 6 + spokenForm: change funk + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: namedFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + def fun x, y do + x + y + end + selections: + - anchor: {line: 1, character: 2} + active: {line: 1, character: 2} + marks: {} +finalState: + documentContents: "" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk9.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk9.yml new file mode 100644 index 0000000000..774bbc8ce6 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk9.yml @@ -0,0 +1,26 @@ +languageId: elixir +command: + version: 6 + spokenForm: change funk + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: namedFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + def fun(x, y \\ 1) do + x + y + end + selections: + - anchor: {line: 1, character: 2} + active: {line: 1, character: 2} + marks: {} +finalState: + documentContents: "" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeKeyFine.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeKeyFine.yml new file mode 100644 index 0000000000..f2bb375041 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeKeyFine.yml @@ -0,0 +1,27 @@ +languageId: elixir +command: + version: 6 + spokenForm: change key fine + action: + name: clearAndSetSelection + target: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: f} + modifiers: + - type: containingScope + scopeType: {type: collectionKey} + usePrePhraseSnapshot: true +initialState: + documentContents: "%{ \"foo\" => \"lorem\", \"bar\" => \"ipsum\", \"baz\" => \"dolor\"}" + selections: + - anchor: {line: 0, character: 56} + active: {line: 0, character: 56} + marks: + default.f: + start: {line: 0, character: 4} + end: {line: 0, character: 7} +finalState: + documentContents: "%{ => \"lorem\", \"bar\" => \"ipsum\", \"baz\" => \"dolor\"}" + selections: + - anchor: {line: 0, character: 3} + active: {line: 0, character: 3} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/chuckItemTwo.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/chuckItemTwo.yml new file mode 100644 index 0000000000..fd015cc97a --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/chuckItemTwo.yml @@ -0,0 +1,27 @@ +languageId: elixir +command: + version: 6 + spokenForm: chuck item two + action: + name: remove + target: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: '2'} + modifiers: + - type: containingScope + scopeType: {type: collectionItem} + usePrePhraseSnapshot: true +initialState: + documentContents: "[1, 2, 3]" + selections: + - anchor: {line: 0, character: 9} + active: {line: 0, character: 9} + marks: + default.2: + start: {line: 0, character: 4} + end: {line: 0, character: 5} +finalState: + documentContents: "[1, 3]" + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} diff --git a/queries/elixir.scm b/queries/elixir.scm new file mode 100644 index 0000000000..8658c54688 --- /dev/null +++ b/queries/elixir.scm @@ -0,0 +1,41 @@ +(comment) @comment +(anonymous_function) @anonymousFunction +(arguments) @arguments_list +(call + target: (_) @functionCallee) @functionCall @functionCallee.domain + +;;(keywords +;; (pair +;; key: (_) @collectionKey @collectionItem.trailing.end.startOf +;; value: (_) @value) @collectionItem @collectionItem.trailing.start.endOf +;; (",")? @collectionItem.trailing.start.startOf +;; (#allow-multiple! @collectionItem)) + +;; How does surrounding whitespace work for this +(map_content + (binary_operator + left: (_) @collectionKey @collectionKey.trailing.start.endOf + right: (_) @value @collectionKey.trailing.end.startOf) @collectionItem + ","? @collectionItem.trailing + (#shrink-to-match! @collectionItem "([^,]+)")) + +(call + target: (identifier) @ignore + (arguments + [ + ; zero-arity functions with no parentheses + (identifier) @functionName + ; regular function clause + (call target: (identifier) @functionName) + ; function clause with a guard clause + (binary_operator + left: (call target: (identifier) @functionName) + operator: "when") + ]) + (do_block + . + "do" @namedFunction.interior.start.endOf + . + "end" @namedFunction.interior.end.startOf + .)? + (#match? @ignore "^(def|defp|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp)$")) @namedFunction @functionName.domain From 74ac7ee2208895e6ab4e81876665d62d861d12b2 Mon Sep 17 00:00:00 2001 From: River Tae Smith <22485304+r-tae@users.noreply.github.com> Date: Mon, 4 Sep 2023 22:00:27 +1000 Subject: [PATCH 02/20] Address comments and format elixir.scm --- .../recorded/languages/elixir/changeKey.yml | 33 +++++++++ .../languages/elixir/changeLambda.yml | 23 +++++++ .../languages/elixir/changeNextComment.yml | 29 ++++++++ .../elixir/changePreviousComment.yml | 30 ++++++++ .../recorded/languages/elixir/changeValue.yml | 33 +++++++++ queries/elixir.scm | 69 ++++++++++++------- 6 files changed, 192 insertions(+), 25 deletions(-) create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeKey.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeLambda.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeNextComment.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changePreviousComment.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeValue.yml diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeKey.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeKey.yml new file mode 100644 index 0000000000..f94c03f260 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeKey.yml @@ -0,0 +1,33 @@ +languageId: elixir +command: + version: 6 + spokenForm: change key + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: collectionKey} + usePrePhraseSnapshot: true +initialState: + documentContents: | + %{ + :a => "lorem", + "b" => "ipsum", + 3 => "dolor" + } + selections: + - anchor: {line: 2, character: 13} + active: {line: 2, character: 13} + marks: {} +finalState: + documentContents: | + %{ + :a => "lorem", + => "ipsum", + 3 => "dolor" + } + selections: + - anchor: {line: 2, character: 2} + active: {line: 2, character: 2} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeLambda.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeLambda.yml new file mode 100644 index 0000000000..2b9110f3b1 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeLambda.yml @@ -0,0 +1,23 @@ +languageId: elixir +command: + version: 6 + spokenForm: change lambda + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: anonymousFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: add = fn x, y -> x + y end + selections: + - anchor: {line: 0, character: 18} + active: {line: 0, character: 18} + marks: {} +finalState: + documentContents: "add = " + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeNextComment.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeNextComment.yml new file mode 100644 index 0000000000..93d411054f --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeNextComment.yml @@ -0,0 +1,29 @@ +languageId: elixir +command: + version: 6 + spokenForm: change next comment + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: relativeScope + scopeType: {type: comment} + offset: 1 + length: 1 + direction: forward + usePrePhraseSnapshot: true +initialState: + documentContents: |- + + # blah + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: |+ + + selections: + - anchor: {line: 1, character: 0} + active: {line: 1, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changePreviousComment.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changePreviousComment.yml new file mode 100644 index 0000000000..bd0eebd1ca --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changePreviousComment.yml @@ -0,0 +1,30 @@ +languageId: elixir +command: + version: 6 + spokenForm: change previous comment + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: relativeScope + scopeType: {type: comment} + offset: 1 + length: 1 + direction: backward + usePrePhraseSnapshot: true +initialState: + documentContents: |- + # blah + x = 5 + selections: + - anchor: {line: 1, character: 5} + active: {line: 1, character: 5} + marks: {} +finalState: + documentContents: |- + + x = 5 + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeValue.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeValue.yml new file mode 100644 index 0000000000..21d197cbbb --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeValue.yml @@ -0,0 +1,33 @@ +languageId: elixir +command: + version: 6 + spokenForm: change value + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: value} + usePrePhraseSnapshot: true +initialState: + documentContents: | + %{ + :a => "lorem", + "b" => "ipsum", + 3 => "dolor" + } + selections: + - anchor: {line: 2, character: 4} + active: {line: 2, character: 4} + marks: {} +finalState: + documentContents: | + %{ + :a => "lorem", + "b" => , + 3 => "dolor" + } + selections: + - anchor: {line: 2, character: 9} + active: {line: 2, character: 9} diff --git a/queries/elixir.scm b/queries/elixir.scm index 8658c54688..559ed7fbfb 100644 --- a/queries/elixir.scm +++ b/queries/elixir.scm @@ -1,41 +1,60 @@ (comment) @comment (anonymous_function) @anonymousFunction -(arguments) @arguments_list + (call - target: (_) @functionCallee) @functionCall @functionCallee.domain + target: (_) @functionCallee +) @functionCall @functionCallee.domain -;;(keywords -;; (pair -;; key: (_) @collectionKey @collectionItem.trailing.end.startOf -;; value: (_) @value) @collectionItem @collectionItem.trailing.start.endOf -;; (",")? @collectionItem.trailing.start.startOf -;; (#allow-multiple! @collectionItem)) +(keywords + (pair + key: (_) @collectionKey @collectionItem.trailing.end.startOf + value: (_) @value + ) @collectionItem @collectionItem.trailing.start.endOf + ( + "," + ) +? + @collectionItem.trailing.start.startOf + (#allow-multiple! @collectionItem) +) -;; How does surrounding whitespace work for this +;; %{:a => "lorem", "b" => "ipsum", 3 => "dolor"}, (map_content + (_)? @_.leading.start.endOf + . (binary_operator - left: (_) @collectionKey @collectionKey.trailing.start.endOf - right: (_) @value @collectionKey.trailing.end.startOf) @collectionItem - ","? @collectionItem.trailing - (#shrink-to-match! @collectionItem "([^,]+)")) + left: (_) @collectionKey + right: (_) @value + ) @collectionItem @collectionKey.domain @value.domain @_.leading.end.startOf @_.trailing.start.endOf + . + (_)? @_.trailing.end.startOf + (#insertion-delimiter! @collectionItem ", ") +) (call - target: (identifier) @ignore + target: (identifier) @_target (arguments [ ; zero-arity functions with no parentheses (identifier) @functionName ; regular function clause - (call target: (identifier) @functionName) + (call + target: (identifier) @functionName + ) ; function clause with a guard clause (binary_operator - left: (call target: (identifier) @functionName) - operator: "when") - ]) - (do_block - . - "do" @namedFunction.interior.start.endOf - . - "end" @namedFunction.interior.end.startOf - .)? - (#match? @ignore "^(def|defp|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp)$")) @namedFunction @functionName.domain + left: (call + target: (identifier) @functionName + ) + operator: "when" + ) + ] + ) + (do_block + . + "do" @namedFunction.interior.start.endOf + "end" @namedFunction.interior.end.startOf + . + )? + (#match? @_target "^(def|defp|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp)$") +) @namedFunction @functionName.domain From f452edb87ecac07a1a518fce00850a1b6cc7dbfb Mon Sep 17 00:00:00 2001 From: River Tae Smith <22485304+r-tae@users.noreply.github.com> Date: Mon, 4 Sep 2023 22:03:19 +1000 Subject: [PATCH 03/20] Move func def `#match?` predicate for readability --- .gitignore | 5 +++++ data/playground/elixir.exs | 10 ++++++++++ queries/elixir.scm | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 90e5d79bb0..82944f28b0 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,8 @@ next-env.d.ts # test subset config packages/test-harness/testSubsetGrep.properties + +data/playground/.metals +data/playground/.vscode +data/playground/.scala-build +.talon diff --git a/data/playground/elixir.exs b/data/playground/elixir.exs index 105de80899..ca32475647 100644 --- a/data/playground/elixir.exs +++ b/data/playground/elixir.exs @@ -3,3 +3,13 @@ defmodule Elixir do IO.inspect(Enum.count([Integer.digits(532), 2, 3])) end end + + +[ + %{:a => "lorem", + # comment + "b" => "ipsum", + # other comment + 3 => "dolor"}, + [keyword: "list", foo: :bar] +] diff --git a/queries/elixir.scm b/queries/elixir.scm index 559ed7fbfb..10d591dd4d 100644 --- a/queries/elixir.scm +++ b/queries/elixir.scm @@ -33,6 +33,7 @@ (call target: (identifier) @_target + (#match? @_target "^(def|defp|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp)$") (arguments [ ; zero-arity functions with no parentheses @@ -56,5 +57,4 @@ "end" @namedFunction.interior.end.startOf . )? - (#match? @_target "^(def|defp|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp)$") ) @namedFunction @functionName.domain From 2e9ced4e21fe999f96e809c3b4596023969e79ad Mon Sep 17 00:00:00 2001 From: River Tae Smith <22485304+r-tae@users.noreply.github.com> Date: Mon, 4 Sep 2023 23:09:26 +1000 Subject: [PATCH 04/20] Support maps and map shorthand --- .../languages/elixir/changeFunkName.yml | 29 +++++++++++ queries/elixir.scm | 49 ++++++++++++------- 2 files changed, 59 insertions(+), 19 deletions(-) create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunkName.yml diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunkName.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunkName.yml new file mode 100644 index 0000000000..6b12bca374 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunkName.yml @@ -0,0 +1,29 @@ +languageId: elixir +command: + version: 6 + spokenForm: change funk name + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: functionName} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + def elixir(x, something) do + x = 5 + end + selections: + - anchor: {line: 1, character: 5} + active: {line: 1, character: 5} + marks: {} +finalState: + documentContents: |- + def (x, something) do + x = 5 + end + selections: + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} diff --git a/queries/elixir.scm b/queries/elixir.scm index 10d591dd4d..d401dca3d4 100644 --- a/queries/elixir.scm +++ b/queries/elixir.scm @@ -1,35 +1,46 @@ (comment) @comment (anonymous_function) @anonymousFunction - (call target: (_) @functionCallee ) @functionCall @functionCallee.domain -(keywords - (pair - key: (_) @collectionKey @collectionItem.trailing.end.startOf - value: (_) @value - ) @collectionItem @collectionItem.trailing.start.endOf - ( - "," - ) -? - @collectionItem.trailing.start.startOf - (#allow-multiple! @collectionItem) -) - -;; %{:a => "lorem", "b" => "ipsum", 3 => "dolor"}, +;; Map +;; %{:a => "lorem", "b" => "ipsum", 3 => "dolor"} (map_content (_)? @_.leading.start.endOf . + (binary_operator) @collectionItem @_.leading.end.startOf @_.trailing.start.endOf + . + (_)? @_.trailing.end.startOf + (#insertion-delimiter! @collectionItem ", ") +) +(map_content (binary_operator left: (_) @collectionKey right: (_) @value - ) @collectionItem @collectionKey.domain @value.domain @_.leading.end.startOf @_.trailing.start.endOf - . - (_)? @_.trailing.end.startOf + ) @collectionKey.domain @value.domain +) + +;; Shorthand map syntax +;; %{a: "lorem", b: "ipsum"} +(map_content + (keywords + (_)? @_.leading.start.endOf + . + (pair) @collectionItem @_.leading.end.startOf @_.trailing.start.endOf + . + (_)? @_.trailing.end.startOf + ) (#insertion-delimiter! @collectionItem ", ") ) +(map_content + (keywords + (pair + key: (_) @collectionKey + value: (_) @value + ) @collectionKey.domain @value.domain + ) +) (call target: (identifier) @_target @@ -56,5 +67,5 @@ "do" @namedFunction.interior.start.endOf "end" @namedFunction.interior.end.startOf . - )? + ) ) @namedFunction @functionName.domain From 0502350173f5e9b3e497b6b621ef0ef4382389f9 Mon Sep 17 00:00:00 2001 From: River Tae Smith <22485304+r-tae@users.noreply.github.com> Date: Mon, 4 Sep 2023 23:12:49 +1000 Subject: [PATCH 05/20] Add map tests --- .../languages/elixir/chuckItemTwo2.yml | 27 +++++++++++++++++++ .../languages/elixir/chuckItemTwo3.yml | 27 +++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/chuckItemTwo2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/chuckItemTwo3.yml diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/chuckItemTwo2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/chuckItemTwo2.yml new file mode 100644 index 0000000000..7b3a3592f1 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/chuckItemTwo2.yml @@ -0,0 +1,27 @@ +languageId: elixir +command: + version: 6 + spokenForm: chuck item two + action: + name: remove + target: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: '2'} + modifiers: + - type: containingScope + scopeType: {type: collectionItem} + usePrePhraseSnapshot: true +initialState: + documentContents: "%{a: 1, b: 2, c: 3}" + selections: + - anchor: {line: 0, character: 19} + active: {line: 0, character: 19} + marks: + default.2: + start: {line: 0, character: 11} + end: {line: 0, character: 12} +finalState: + documentContents: "%{a: 1, c: 3}" + selections: + - anchor: {line: 0, character: 13} + active: {line: 0, character: 13} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/chuckItemTwo3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/chuckItemTwo3.yml new file mode 100644 index 0000000000..8595dd9012 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/chuckItemTwo3.yml @@ -0,0 +1,27 @@ +languageId: elixir +command: + version: 6 + spokenForm: chuck item two + action: + name: remove + target: + type: primitive + mark: {type: decoratedSymbol, symbolColor: default, character: '2'} + modifiers: + - type: containingScope + scopeType: {type: collectionItem} + usePrePhraseSnapshot: true +initialState: + documentContents: "%{\"a\" => 1, :b => 2, 3 => 3}" + selections: + - anchor: {line: 0, character: 28} + active: {line: 0, character: 28} + marks: + default.2: + start: {line: 0, character: 18} + end: {line: 0, character: 19} +finalState: + documentContents: "%{\"a\" => 1, 3 => 3}" + selections: + - anchor: {line: 0, character: 19} + active: {line: 0, character: 19} From af5ffb4e859b918f953509a7befaf6f5b17924f1 Mon Sep 17 00:00:00 2001 From: River Tae Smith <22485304+r-tae@users.noreply.github.com> Date: Tue, 5 Sep 2023 03:12:24 +1000 Subject: [PATCH 06/20] Add module support (as "class" scope) --- data/playground/elixir/functions.ex | 62 +++++++++++++++++++++++++++++ queries/elixir.scm | 25 ++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 data/playground/elixir/functions.ex diff --git a/data/playground/elixir/functions.ex b/data/playground/elixir/functions.ex new file mode 100644 index 0000000000..1724f87973 --- /dev/null +++ b/data/playground/elixir/functions.ex @@ -0,0 +1,62 @@ +defmodule Funcs do + def no_args() do + end + + def no_args_no_parens do + end + + def one_arg(x) do + x + end + + def one_arg_no_parens(x) do + x + end + + def two_args(x, y) do + x + y + end + + def two_args_no_parens(x, y) do + x + y + end + + def default_args_no_parens(x, y \\ 1) do + x + y + end + + def default_args(x, y \\ 1) do + x + y + end + + def do_block(), do: 1 + def do_block(x), do: x + + def pattern_matching([{x, y} | tail]) do + x + y + end + + def one_guard(x) when x == 1 do + x + end + + def multiple_guard(x) when x > 10 when x < 5 do + x + end + + defp private(x) do + x + end + + defmacro macro(x) do + quote do + [unquote(x)] + end + end + + defguard guard(term) when is_integer(term) and rem(term, 2) == 0 + + def unquote(name)(unquote_splicing(args)) do + unquote(compiled) + end +end diff --git a/queries/elixir.scm b/queries/elixir.scm index d401dca3d4..55780babea 100644 --- a/queries/elixir.scm +++ b/queries/elixir.scm @@ -69,3 +69,28 @@ . ) ) @namedFunction @functionName.domain + +(call + target: (identifier) @_target + (#match? @_target "^(defmodule)$") + (arguments + (alias) @className + ) + (do_block + . + "do" @class.interior.start.endOf + "end" @class.interior.end.startOf + . + ) +) @class @className.domain + +(source) @className.iteration @class.iteration +(source) @statement.iteration + +(source) @namedFunction.iteration @functionName.iteration +;; Is it better to have the class as the function iteration scope? +;; elixir devs only +;; (call +;; target: (identifier) @_target +;; (#match? @_target "^(defmodule)$") +;; ) @namedFunction.iteration @functionName.iteration From 9547949f7c3d3d9665db0a5ac7385842ad7b9e42 Mon Sep 17 00:00:00 2001 From: River Tae Smith <22485304+r-tae@users.noreply.github.com> Date: Tue, 5 Sep 2023 04:17:20 +1000 Subject: [PATCH 07/20] Add more elixir syntax examples in playground --- data/playground/elixir.exs | 15 ----------- data/playground/elixir/calls.ex | 16 ++++++++++++ data/playground/elixir/data-structures.ex | 32 +++++++++++++++++++++++ 3 files changed, 48 insertions(+), 15 deletions(-) delete mode 100644 data/playground/elixir.exs create mode 100644 data/playground/elixir/calls.ex create mode 100644 data/playground/elixir/data-structures.ex diff --git a/data/playground/elixir.exs b/data/playground/elixir.exs deleted file mode 100644 index ca32475647..0000000000 --- a/data/playground/elixir.exs +++ /dev/null @@ -1,15 +0,0 @@ -defmodule Elixir do - def elixir(x, something) when something = "inside" do - IO.inspect(Enum.count([Integer.digits(532), 2, 3])) - end -end - - -[ - %{:a => "lorem", - # comment - "b" => "ipsum", - # other comment - 3 => "dolor"}, - [keyword: "list", foo: :bar] -] diff --git a/data/playground/elixir/calls.ex b/data/playground/elixir/calls.ex new file mode 100644 index 0000000000..b00bb9d610 --- /dev/null +++ b/data/playground/elixir/calls.ex @@ -0,0 +1,16 @@ +defmodule Calls do + a() + b.() + c do + 1 + end + d.() do + end + e.() do + 1 + end + f.(0) do + 1 + end + Alias.g() +end diff --git a/data/playground/elixir/data-structures.ex b/data/playground/elixir/data-structures.ex new file mode 100644 index 0000000000..f08289d599 --- /dev/null +++ b/data/playground/elixir/data-structures.ex @@ -0,0 +1,32 @@ +defmodule DataStructures do + [] + [a] + [A] + [1] + [1, 2] + [[1], 1] + %{} + %{a: 1, b: 2} + %{:a => 1, "b" => 2, c => 3} + # NOTE: this is troublesome for the queries, there are commas at two different levels + %{"a" => 1, b: 2, c: 3} + %{user | name: "Jane", email: "jane@example.com"} + %{user | "name" => "Jane"} + %AStruct{a: 1, b: 2} + %AnotherStruct{:a => 1, "b" => 2, c => 3} + %ThirdStruct{"a" => 1, b: 2, c: 3} + %User{user | name: "Jane", email: "jane@example.com"} + %User{user | "name" => "Jane"} + %_{} + %name{} + %^name{} + %__MODULE__{} + %__MODULE__.Child{} + %:"Elixir.Mod"{} + %fun(){} + %Mod.fun(){} + %fun.(){} + {} + {1} + {1, 2} +end From fc7d6ccdeead0de2db654a870302720467e67e1d Mon Sep 17 00:00:00 2001 From: River Tae Smith <22485304+r-tae@users.noreply.github.com> Date: Tue, 5 Sep 2023 15:49:16 +1000 Subject: [PATCH 08/20] Fix unfinished comment --- data/playground/elixir/data-structures.ex | 1 - queries/elixir.scm | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/data/playground/elixir/data-structures.ex b/data/playground/elixir/data-structures.ex index f08289d599..538f8cf762 100644 --- a/data/playground/elixir/data-structures.ex +++ b/data/playground/elixir/data-structures.ex @@ -8,7 +8,6 @@ defmodule DataStructures do %{} %{a: 1, b: 2} %{:a => 1, "b" => 2, c => 3} - # NOTE: this is troublesome for the queries, there are commas at two different levels %{"a" => 1, b: 2, c: 3} %{user | name: "Jane", email: "jane@example.com"} %{user | "name" => "Jane"} diff --git a/queries/elixir.scm b/queries/elixir.scm index 55780babea..b24db4969e 100644 --- a/queries/elixir.scm +++ b/queries/elixir.scm @@ -89,7 +89,7 @@ (source) @namedFunction.iteration @functionName.iteration ;; Is it better to have the class as the function iteration scope? -;; elixir devs only +;; I believe `def`s can only go inside modules ;; (call ;; target: (identifier) @_target ;; (#match? @_target "^(defmodule)$") From d5843c42de33ae9fc0aad9790ed83d165e2d7039 Mon Sep 17 00:00:00 2001 From: River Tae Smith <22485304+r-tae@users.noreply.github.com> Date: Tue, 5 Sep 2023 16:35:38 +1000 Subject: [PATCH 09/20] Keep `end` on its own line for "chuck inside funk" --- .../languages/elixir/chuckInsideFunk.yml | 31 ++++++++++++++++ .../languages/elixir/chuckInsideFunk2.yml | 35 +++++++++++++++++++ queries/elixir.scm | 8 ++--- 3 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/chuckInsideFunk.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/chuckInsideFunk2.yml diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/chuckInsideFunk.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/chuckInsideFunk.yml new file mode 100644 index 0000000000..31d5bb91ef --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/chuckInsideFunk.yml @@ -0,0 +1,31 @@ +languageId: elixir +command: + version: 6 + spokenForm: chuck inside funk + action: + name: remove + target: + type: primitive + modifiers: + - {type: interiorOnly} + - type: containingScope + scopeType: {type: namedFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + defmacro macro(x) do + quote do + [unquote(x)] + end + end + selections: + - anchor: {line: 2, character: 16} + active: {line: 2, character: 16} + marks: {} +finalState: + documentContents: |- + defmacro macro(x) do + end + selections: + - anchor: {line: 1, character: 0} + active: {line: 1, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/chuckInsideFunk2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/chuckInsideFunk2.yml new file mode 100644 index 0000000000..adb8199602 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/chuckInsideFunk2.yml @@ -0,0 +1,35 @@ +languageId: elixir +command: + version: 6 + spokenForm: chuck inside funk + action: + name: remove + target: + type: primitive + modifiers: + - {type: interiorOnly} + - type: containingScope + scopeType: {type: namedFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + defmodule Mod do + defmacro macro(x) do + quote do + [unquote(x)] + end + end + end + selections: + - anchor: {line: 3, character: 18} + active: {line: 3, character: 18} + marks: {} +finalState: + documentContents: |- + defmodule Mod do + defmacro macro(x) do + end + end + selections: + - anchor: {line: 2, character: 0} + active: {line: 2, character: 0} diff --git a/queries/elixir.scm b/queries/elixir.scm index b24db4969e..0e3640818d 100644 --- a/queries/elixir.scm +++ b/queries/elixir.scm @@ -62,12 +62,8 @@ ) ] ) - (do_block - . - "do" @namedFunction.interior.start.endOf - "end" @namedFunction.interior.end.startOf - . - ) + (do_block) @namedFunction.interior + (#shrink-to-match! @namedFunction.interior "^do\\n?\\w*(?.*?\\n)\\s*end$") ) @namedFunction @functionName.domain (call From 3b8456f27a7af0f7ad76a7a1c66bbaa772ea5bf5 Mon Sep 17 00:00:00 2001 From: River Tae Smith <22485304+r-tae@users.noreply.github.com> Date: Thu, 14 Sep 2023 15:16:08 +1000 Subject: [PATCH 10/20] Add negative funk test Calls of non-def functions with do end blocks shouldn't count as funks --- data/playground/elixir/calls.ex | 3 +++ .../languages/elixir/changeFunk14.yml | 22 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk14.yml diff --git a/data/playground/elixir/calls.ex b/data/playground/elixir/calls.ex index b00bb9d610..91099bbd29 100644 --- a/data/playground/elixir/calls.ex +++ b/data/playground/elixir/calls.ex @@ -9,6 +9,9 @@ defmodule Calls do e.() do 1 end + a x do + x + end f.(0) do 1 end diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk14.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk14.yml new file mode 100644 index 0000000000..7de4c4a748 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changeFunk14.yml @@ -0,0 +1,22 @@ +languageId: elixir +command: + version: 6 + spokenForm: change funk + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: namedFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + a x do + x + end + selections: + - anchor: {line: 1, character: 3} + active: {line: 1, character: 3} + marks: {} +thrownError: {name: NoContainingScopeError} From 36ba11440012f90fb0cc97b39457cd022cb3de11 Mon Sep 17 00:00:00 2001 From: River Tae Smith <22485304+r-tae@users.noreply.github.com> Date: Thu, 14 Sep 2023 15:50:59 +1000 Subject: [PATCH 11/20] Add elixirls extension dependency --- packages/common/src/extensionDependencies.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/common/src/extensionDependencies.ts b/packages/common/src/extensionDependencies.ts index 08a37aa65c..7010384065 100644 --- a/packages/common/src/extensionDependencies.ts +++ b/packages/common/src/extensionDependencies.ts @@ -5,4 +5,5 @@ export const extensionDependencies = [ "ms-python.python", "mrob95.vscode-talonscript", "jrieken.vscode-tree-sitter-query", + "jakebecker.elixir-ls", ]; From 8b74b41591b8dbe834ca271e30a0c3e15206eba9 Mon Sep 17 00:00:00 2001 From: River Tae Smith <22485304+r-tae@users.noreply.github.com> Date: Thu, 14 Sep 2023 18:25:05 +1000 Subject: [PATCH 12/20] Add defguard query --- queries/elixir.scm | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/queries/elixir.scm b/queries/elixir.scm index 0e3640818d..999796fe79 100644 --- a/queries/elixir.scm +++ b/queries/elixir.scm @@ -44,7 +44,7 @@ (call target: (identifier) @_target - (#match? @_target "^(def|defp|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp)$") + (#match? @_target "^(def|defp|defdelegate|defmacro|defmacrop|defn|defnp)$") (arguments [ ; zero-arity functions with no parentheses @@ -66,6 +66,35 @@ (#shrink-to-match! @namedFunction.interior "^do\\n?\\w*(?.*?\\n)\\s*end$") ) @namedFunction @functionName.domain +;; def fun(), do: 1 +(call + target: (identifier) @_target + (#match? @_target "^(def|defp|defdelegate|defmacro|defmacrop|defn|defnp)$") + (arguments + (keywords + (pair + key: (_) @do_keyword + value: (_) @namedFunction.interior + ) + ) + (#match? @do_keyword "^do: $") + ) +) @namedFunction @functionName.domain + +;; defguard guard(term) when is_integer(term) and rem(term, 2) == 0 +(call + target: (identifier) @_target + (#match? @_target "^(defguard|defguardp)$") + (arguments + (binary_operator + left: (call + target: (identifier) @functionName + ) + operator: "when" + ) + ) +) @namedFunction @functionName.domain + (call target: (identifier) @_target (#match? @_target "^(defmodule)$") From d965ad9dabcf495d1ba2f76ae63ffbf15439813f Mon Sep 17 00:00:00 2001 From: River Tae Smith <22485304+r-tae@users.noreply.github.com> Date: Sat, 16 Sep 2023 00:21:10 +1000 Subject: [PATCH 13/20] Remove unwanted .gitignore inclusions --- .gitignore | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.gitignore b/.gitignore index 82944f28b0..90e5d79bb0 100644 --- a/.gitignore +++ b/.gitignore @@ -42,8 +42,3 @@ next-env.d.ts # test subset config packages/test-harness/testSubsetGrep.properties - -data/playground/.metals -data/playground/.vscode -data/playground/.scala-build -.talon From 89955ddfeb0317d5a07e0f4b891573b6ff64027c Mon Sep 17 00:00:00 2001 From: River Tae Smith <22485304+r-tae@users.noreply.github.com> Date: Sat, 16 Sep 2023 00:21:57 +1000 Subject: [PATCH 14/20] Delete unecessary previous comment test --- .../elixir/changePreviousComment.yml | 30 ------------------- 1 file changed, 30 deletions(-) delete mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changePreviousComment.yml diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changePreviousComment.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changePreviousComment.yml deleted file mode 100644 index bd0eebd1ca..0000000000 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/elixir/changePreviousComment.yml +++ /dev/null @@ -1,30 +0,0 @@ -languageId: elixir -command: - version: 6 - spokenForm: change previous comment - action: - name: clearAndSetSelection - target: - type: primitive - modifiers: - - type: relativeScope - scopeType: {type: comment} - offset: 1 - length: 1 - direction: backward - usePrePhraseSnapshot: true -initialState: - documentContents: |- - # blah - x = 5 - selections: - - anchor: {line: 1, character: 5} - active: {line: 1, character: 5} - marks: {} -finalState: - documentContents: |- - - x = 5 - selections: - - anchor: {line: 0, character: 0} - active: {line: 0, character: 0} From e8f8c11ca9f1f468eb271fa587ddda034a6b553c Mon Sep 17 00:00:00 2001 From: River Tae Smith <22485304+r-tae@users.noreply.github.com> Date: Sat, 16 Sep 2023 00:25:59 +1000 Subject: [PATCH 15/20] Make defmodule namedFunction iteration scope --- queries/elixir.scm | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/queries/elixir.scm b/queries/elixir.scm index 999796fe79..75a9f59cef 100644 --- a/queries/elixir.scm +++ b/queries/elixir.scm @@ -111,11 +111,8 @@ (source) @className.iteration @class.iteration (source) @statement.iteration - (source) @namedFunction.iteration @functionName.iteration -;; Is it better to have the class as the function iteration scope? -;; I believe `def`s can only go inside modules -;; (call -;; target: (identifier) @_target -;; (#match? @_target "^(defmodule)$") -;; ) @namedFunction.iteration @functionName.iteration +(call + target: (identifier) @_target + (#match? @_target "^(defmodule)$") +) @namedFunction.iteration @functionName.iteration From 5a1d1ab10893209c15f3c361c03f43d8e1311576 Mon Sep 17 00:00:00 2001 From: River Tae Smith <22485304+r-tae@users.noreply.github.com> Date: Sat, 16 Sep 2023 00:27:16 +1000 Subject: [PATCH 16/20] Add @textFragment to string and comment --- queries/elixir.scm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/queries/elixir.scm b/queries/elixir.scm index 75a9f59cef..c0ec2cbbf9 100644 --- a/queries/elixir.scm +++ b/queries/elixir.scm @@ -1,4 +1,5 @@ -(comment) @comment +(comment) @comment @textFragment +(string) @string @textFragment (anonymous_function) @anonymousFunction (call target: (_) @functionCallee From 52fc129ffd8d753b5733080c9242f297b8f7f10e Mon Sep 17 00:00:00 2001 From: River Tae Smith <22485304+r-tae@users.noreply.github.com> Date: Sat, 16 Sep 2023 00:32:46 +1000 Subject: [PATCH 17/20] Add comment explaining elixir-ls dependency --- packages/common/src/extensionDependencies.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/common/src/extensionDependencies.ts b/packages/common/src/extensionDependencies.ts index a442a67c27..627214195c 100644 --- a/packages/common/src/extensionDependencies.ts +++ b/packages/common/src/extensionDependencies.ts @@ -1,11 +1,11 @@ export const extensionDependencies = [ "pokey.parse-tree", - // Register necessary file extensions for tests "scalameta.metals", "mrob95.vscode-talonscript", "jrieken.vscode-tree-sitter-query", + // necessary for Elixir tests + "jakebecker.elixir-ls", // Necessary for the `drink cell` and `pour cell` tests "ms-toolsai.jupyter", - "jakebecker.elixir-ls", ]; From 470a072e773f5fc821c68cb7cfef0ece92e1cb82 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Fri, 26 Jan 2024 14:18:32 +0000 Subject: [PATCH 18/20] Cleanup --- data/playground/elixir/calls.ex | 6 +++--- packages/common/src/extensionDependencies.ts | 8 +++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/data/playground/elixir/calls.ex b/data/playground/elixir/calls.ex index 91099bbd29..36506dbbb7 100644 --- a/data/playground/elixir/calls.ex +++ b/data/playground/elixir/calls.ex @@ -4,15 +4,15 @@ defmodule Calls do c do 1 end - d.() do + d() do end - e.() do + e() do 1 end a x do x end - f.(0) do + f(0) do 1 end Alias.g() diff --git a/packages/common/src/extensionDependencies.ts b/packages/common/src/extensionDependencies.ts index 374bf29e1e..3043dfa9e9 100644 --- a/packages/common/src/extensionDependencies.ts +++ b/packages/common/src/extensionDependencies.ts @@ -1,11 +1,13 @@ export const extensionDependencies = [ // Cursorless access to Tree sitter "pokey.parse-tree", + // Register necessary language-IDs for tests - "scala-lang.scala", // scala - "mrob95.vscode-talonscript", // talon - "jrieken.vscode-tree-sitter-query", // scm "jakebecker.elixir-ls", // elixir + "jrieken.vscode-tree-sitter-query", // scm + "mrob95.vscode-talonscript", // talon + "scala-lang.scala", // scala + // Necessary for the `drink cell` and `pour cell` tests "ms-toolsai.jupyter", ]; From 2471282ea193c63b9a958fc55dad5b908b7d8101 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Fri, 26 Jan 2024 14:40:21 +0000 Subject: [PATCH 19/20] Some fixes --- data/playground/elixir/data-structures.ex | 5 + .../common/src/scopeSupportFacets/elixir.ts | 13 +++ .../getLanguageScopeSupport.ts | 3 + .../scopeSupportFacetInfos.ts | 10 ++ .../scopeSupportFacets.types.ts | 3 + .../elixir/collectionItem.map.iteration.scope | 13 +++ .../scopes/elixir/collectionItem.map.scope | 59 +++++++++++ .../scopes/elixir/collectionItem.map2.scope | 98 +++++++++++++++++++ queries/elixir.scm | 39 +++++--- 9 files changed, 227 insertions(+), 16 deletions(-) create mode 100644 packages/common/src/scopeSupportFacets/elixir.ts create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/elixir/collectionItem.map.iteration.scope create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/elixir/collectionItem.map.scope create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/elixir/collectionItem.map2.scope diff --git a/data/playground/elixir/data-structures.ex b/data/playground/elixir/data-structures.ex index 538f8cf762..d13657effe 100644 --- a/data/playground/elixir/data-structures.ex +++ b/data/playground/elixir/data-structures.ex @@ -16,6 +16,11 @@ defmodule DataStructures do %ThirdStruct{"a" => 1, b: 2, c: 3} %User{user | name: "Jane", email: "jane@example.com"} %User{user | "name" => "Jane"} + %{ + :a => 1, + "b" => 2, + c => 3 + } %_{} %name{} %^name{} diff --git a/packages/common/src/scopeSupportFacets/elixir.ts b/packages/common/src/scopeSupportFacets/elixir.ts new file mode 100644 index 0000000000..82e72ebcb5 --- /dev/null +++ b/packages/common/src/scopeSupportFacets/elixir.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/naming-convention */ + +import { + LanguageScopeSupportFacetMap, + ScopeSupportFacetLevel, +} from "./scopeSupportFacets.types"; + +const { supported } = ScopeSupportFacetLevel; + +export const elixirScopeSupport: LanguageScopeSupportFacetMap = { + "collectionItem.map": supported, + "collectionItem.map.iteration": supported, +}; diff --git a/packages/common/src/scopeSupportFacets/getLanguageScopeSupport.ts b/packages/common/src/scopeSupportFacets/getLanguageScopeSupport.ts index 76f57d6216..6b525c0d0a 100644 --- a/packages/common/src/scopeSupportFacets/getLanguageScopeSupport.ts +++ b/packages/common/src/scopeSupportFacets/getLanguageScopeSupport.ts @@ -1,3 +1,4 @@ +import { elixirScopeSupport } from "./elixir"; import { htmlScopeSupport } from "./html"; import { javaScopeSupport } from "./java"; import { javascriptScopeSupport } from "./javascript"; @@ -25,6 +26,8 @@ export function getLanguageScopeSupport( return talonScopeSupport; case "typescript": return typescriptScopeSupport; + case "elixir": + return elixirScopeSupport; } throw Error(`Unsupported language: '${languageId}'`); } diff --git a/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts b/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts index ccb8dc057f..5005116840 100644 --- a/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts +++ b/packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts @@ -141,6 +141,16 @@ export const scopeSupportFacetInfos: Record< scopeType: "argumentOrParameter", isIteration: true, }, + "collectionItem.map": { + description: "An entry in a map/dictionary", + scopeType: "collectionItem", + }, + "collectionItem.map.iteration": { + description: + "Iteration scope for entries in a map/dictionary; should be between the braces", + scopeType: "collectionItem", + isIteration: true, + }, "comment.line": { description: "A line comment", diff --git a/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts b/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts index 2e052384b1..1e2473b7a7 100644 --- a/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts +++ b/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts @@ -37,6 +37,9 @@ const scopeSupportFacets = [ "argument.formal", "argument.formal.iteration", + "collectionItem.map", + "collectionItem.map.iteration", + "comment.line", "comment.block", diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/elixir/collectionItem.map.iteration.scope b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/elixir/collectionItem.map.iteration.scope new file mode 100644 index 0000000000..de5817f6ab --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/elixir/collectionItem.map.iteration.scope @@ -0,0 +1,13 @@ +defmodule DataStructures do + %{a: 1, b: 2} +end +--- + +[Range] = +[Domain] = 1:4-1:14 +0| defmodule DataStructures do + +1| %{a: 1, b: 2} + >----------< +2| end + diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/elixir/collectionItem.map.scope b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/elixir/collectionItem.map.scope new file mode 100644 index 0000000000..5dfec3f6eb --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/elixir/collectionItem.map.scope @@ -0,0 +1,59 @@ +defmodule DataStructures do + %{a: 1, b: 2} +end +--- + +[#1 Content] = +[#1 Domain] = 1:4-1:8 +0| defmodule DataStructures do + +1| %{a: 1, b: 2} + >----< +2| end + + +[#1 Removal] = 1:4-1:10 +0| defmodule DataStructures do + +1| %{a: 1, b: 2} + >------< +2| end + + +[#1 Trailing delimiter] = 1:8-1:10 +0| defmodule DataStructures do + +1| %{a: 1, b: 2} + >--< +2| end + + +[#1 Insertion delimiter] = ", " + + +[#2 Content] = +[#2 Domain] = 1:10-1:14 +0| defmodule DataStructures do + +1| %{a: 1, b: 2} + >----< +2| end + + +[#2 Removal] = 1:8-1:14 +0| defmodule DataStructures do + +1| %{a: 1, b: 2} + >------< +2| end + + +[#2 Leading delimiter] = 1:8-1:10 +0| defmodule DataStructures do + +1| %{a: 1, b: 2} + >--< +2| end + + +[#2 Insertion delimiter] = ", " diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/elixir/collectionItem.map2.scope b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/elixir/collectionItem.map2.scope new file mode 100644 index 0000000000..a9b099331b --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/elixir/collectionItem.map2.scope @@ -0,0 +1,98 @@ +defmodule DataStructures do + %{ + a: 1, + b: 2 + } +end +--- + +[#1 Content] = +[#1 Domain] = 2:4-2:8 +0| defmodule DataStructures do + +1| %{ + +2| a: 1, + >----< +3| b: 2 + +4| } + +5| end + + +[#1 Removal] = 2:4-3:4 +0| defmodule DataStructures do + +1| %{ + +2| a: 1, + >----- +3| b: 2 + ----< +4| } + +5| end + + +[#1 Trailing delimiter] = 2:8-3:4 +0| defmodule DataStructures do + +1| %{ + +2| a: 1, + >- +3| b: 2 + ----< +4| } + +5| end + + +[#1 Insertion delimiter] = ",\n" + + +[#2 Content] = +[#2 Domain] = 3:4-3:8 +0| defmodule DataStructures do + +1| %{ + +2| a: 1, + +3| b: 2 + >----< +4| } + +5| end + + +[#2 Removal] = 2:8-3:8 +0| defmodule DataStructures do + +1| %{ + +2| a: 1, + >- +3| b: 2 + --------< +4| } + +5| end + + +[#2 Leading delimiter] = 2:8-3:4 +0| defmodule DataStructures do + +1| %{ + +2| a: 1, + >- +3| b: 2 + ----< +4| } + +5| end + + +[#2 Insertion delimiter] = ",\n" diff --git a/queries/elixir.scm b/queries/elixir.scm index c0ec2cbbf9..14566a195e 100644 --- a/queries/elixir.scm +++ b/queries/elixir.scm @@ -7,13 +7,15 @@ ;; Map ;; %{:a => "lorem", "b" => "ipsum", 3 => "dolor"} -(map_content - (_)? @_.leading.start.endOf - . - (binary_operator) @collectionItem @_.leading.end.startOf @_.trailing.start.endOf - . - (_)? @_.trailing.end.startOf - (#insertion-delimiter! @collectionItem ", ") +( + (map_content + (_)? @_.leading.endOf + . + (binary_operator) @collectionItem + . + (_)? @_.trailing.startOf + ) @dummy + (#single-or-multi-line-delimiter! @collectionItem @dummy ", " ",\n") ) (map_content (binary_operator @@ -24,16 +26,21 @@ ;; Shorthand map syntax ;; %{a: "lorem", b: "ipsum"} -(map_content - (keywords - (_)? @_.leading.start.endOf - . - (pair) @collectionItem @_.leading.end.startOf @_.trailing.start.endOf - . - (_)? @_.trailing.end.startOf - ) - (#insertion-delimiter! @collectionItem ", ") +( + (map_content + (keywords + (_)? @_.leading.endOf + . + (pair) @collectionItem + . + (_)? @_.trailing.startOf + ) + (#single-or-multi-line-delimiter! @collectionItem @dummy ", " ",\n") + ) @dummy ) + +(map_content) @collectionItem.iteration @collectionKey.iteration @value.iteration + (map_content (keywords (pair From 8e082135420fc02fa90fc62846afa102aa482710 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Fri, 26 Jan 2024 14:41:43 +0000 Subject: [PATCH 20/20] [pre-commit.ci lite] apply automatic fixes --- .../fixtures/scopes/elixir/collectionItem.map.iteration.scope | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/elixir/collectionItem.map.iteration.scope b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/elixir/collectionItem.map.iteration.scope index de5817f6ab..0fdb9cf4af 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/elixir/collectionItem.map.iteration.scope +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/scopes/elixir/collectionItem.map.iteration.scope @@ -10,4 +10,3 @@ end 1| %{a: 1, b: 2} >----------< 2| end -