diff --git a/data/playground/html.html b/data/playground/html.html
index 4b1b112f31..875272ae9c 100644
--- a/data/playground/html.html
+++ b/data/playground/html.html
@@ -1,9 +1,25 @@
-
+
+
+
+
+ Hello
+
+
+ Title
+
+ Hello world
+
+
+
\ No newline at end of file
diff --git a/src/languages/constants.ts b/src/languages/constants.ts
index 86bc029c1e..29bf68b482 100644
--- a/src/languages/constants.ts
+++ b/src/languages/constants.ts
@@ -3,6 +3,7 @@ export const supportedLanguageIds = [
"clojure",
"cpp",
"csharp",
+ "html",
"java",
"javascript",
"javascriptreact",
diff --git a/src/languages/getNodeMatcher.ts b/src/languages/getNodeMatcher.ts
index 830790c2b5..35249d9bf1 100644
--- a/src/languages/getNodeMatcher.ts
+++ b/src/languages/getNodeMatcher.ts
@@ -13,6 +13,7 @@ import csharp from "./csharp";
import { patternMatchers as json } from "./json";
import { patternMatchers as typescript } from "./typescript";
import java from "./java";
+import { patternMatchers as html } from "./html";
import python from "./python";
import { UnsupportedLanguageError } from "../errors";
import { SupportedLanguageId } from "./constants";
@@ -46,9 +47,10 @@ const languageMatchers: Record<
Record
> = {
c: cpp,
- clojure,
cpp,
- csharp: csharp,
+ csharp,
+ clojure,
+ html,
java,
javascript: typescript,
javascriptreact: typescript,
diff --git a/src/languages/getTextFragmentExtractor.ts b/src/languages/getTextFragmentExtractor.ts
index 1c955c04d7..07b806d196 100644
--- a/src/languages/getTextFragmentExtractor.ts
+++ b/src/languages/getTextFragmentExtractor.ts
@@ -2,13 +2,13 @@ import { SyntaxNode } from "web-tree-sitter";
import { SelectionWithEditor } from "../typings/Types";
import { stringTextFragmentExtractor as jsonStringTextFragmentExtractor } from "./json";
import { stringTextFragmentExtractor as typescriptStringTextFragmentExtractor } from "./typescript";
+import { stringTextFragmentExtractor as htmlStringTextFragmentExtractor } from "./html";
import { UnsupportedLanguageError } from "../errors";
import { Range } from "vscode";
import { SupportedLanguageId } from "./constants";
import {
getNodeInternalRange,
getNodeRange,
- makeRangeFromPositions,
} from "../util/nodeSelectors";
import { getNodeMatcher } from "./getNodeMatcher";
import { notSupported } from "../util/nodeMatchers";
@@ -126,6 +126,10 @@ const textFragmentExtractors: Record<
),
cpp: constructDefaultTextFragmentExtractor("cpp"),
csharp: constructDefaultTextFragmentExtractor("csharp"),
+ html: constructDefaultTextFragmentExtractor(
+ "html",
+ htmlStringTextFragmentExtractor
+ ),
java: constructDefaultTextFragmentExtractor(
"java",
constructHackedStringTextFragmentExtractor("java")
diff --git a/src/languages/html.ts b/src/languages/html.ts
new file mode 100644
index 0000000000..dae1b122f8
--- /dev/null
+++ b/src/languages/html.ts
@@ -0,0 +1,52 @@
+import {
+ createPatternMatchers,
+ leadingMatcher,
+ patternMatcher,
+} from "../util/nodeMatchers";
+import {
+ ScopeType,
+ NodeMatcherAlternative,
+ SelectionWithEditor,
+} from "../typings/Types";
+import { SyntaxNode } from "web-tree-sitter";
+import { getNodeRange } from "../util/nodeSelectors";
+
+const attribute = "*?.attribute!";
+
+const getStartTag = patternMatcher(`*?.start_tag!`);
+const getEndTag = patternMatcher(`*?.end_tag!`);
+
+const getTags = (selection: SelectionWithEditor, node: SyntaxNode) => {
+ const startTag = getStartTag(selection, node);
+ const endTag = getEndTag(selection, node);
+ return startTag != null && endTag != null ? startTag.concat(endTag) : null;
+};
+const nodeMatchers: Partial> = {
+ xmlElement: ["element", "script_element", "style_element"],
+ xmlBothTags: getTags,
+ xmlStartTag: getStartTag,
+ xmlEndTag: getEndTag,
+ attribute: attribute,
+ collectionItem: attribute,
+ name: "*?.tag_name!",
+ collectionKey: ["*?.attribute_name!"],
+ value: leadingMatcher(
+ ["*?.quoted_attribute_value!.attribute_value", "*?.attribute_value!"],
+ ["="]
+ ),
+ string: "quoted_attribute_value",
+ comment: "comment",
+};
+
+export const patternMatchers = createPatternMatchers(nodeMatchers);
+
+export function stringTextFragmentExtractor(
+ node: SyntaxNode,
+ selection: SelectionWithEditor
+) {
+ if (node.type === "attribute_value") {
+ return getNodeRange(node);
+ }
+
+ return null;
+}
\ No newline at end of file
diff --git a/src/processTargets/modifiers/surroundingPair/delimiterMaps.ts b/src/processTargets/modifiers/surroundingPair/delimiterMaps.ts
index d973ac8866..9a5f5e6a9b 100644
--- a/src/processTargets/modifiers/surroundingPair/delimiterMaps.ts
+++ b/src/processTargets/modifiers/surroundingPair/delimiterMaps.ts
@@ -9,7 +9,7 @@ export const delimiterToText: Record<
SimpleSurroundingPairName,
[IndividualDelimiterText, IndividualDelimiterText]
> = {
- angleBrackets: ["<", ">"],
+ angleBrackets: [["", "<"], [">", "/>"]],
backtickQuotes: ["`", "`"],
curlyBrackets: [["{", "${"], "}"],
doubleQuotes: ['"', '"'],
diff --git a/src/test/suite/fixtures/recorded/languages/html/chuckValueInk.yml b/src/test/suite/fixtures/recorded/languages/html/chuckValueInk.yml
new file mode 100644
index 0000000000..5ba7e592d3
--- /dev/null
+++ b/src/test/suite/fixtures/recorded/languages/html/chuckValueInk.yml
@@ -0,0 +1,33 @@
+languageId: html
+command:
+ version: 1
+ spokenForm: chuck value ink
+ action: remove
+ targets:
+ - type: primitive
+ modifier: {type: containingScope, scopeType: value, includeSiblings: false}
+ mark: {type: decoratedSymbol, symbolColor: default, character: i}
+initialState:
+ documentContents: |-
+
+
+
+ selections:
+ - anchor: {line: 1, character: 4}
+ active: {line: 1, character: 4}
+ marks:
+ default.i:
+ start: {line: 0, character: 6}
+ end: {line: 0, character: 8}
+finalState:
+ documentContents: |-
+
+
+
+ selections:
+ - anchor: {line: 1, character: 4}
+ active: {line: 1, character: 4}
+ thatMark:
+ - anchor: {line: 0, character: 8}
+ active: {line: 0, character: 8}
+fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: i}, selectionType: token, position: contents, insideOutsideType: outside, modifier: {type: containingScope, scopeType: value, includeSiblings: false}}]
diff --git a/src/test/suite/fixtures/recorded/languages/html/clearAttributeVest.yml b/src/test/suite/fixtures/recorded/languages/html/clearAttributeVest.yml
new file mode 100644
index 0000000000..af03c7e65d
--- /dev/null
+++ b/src/test/suite/fixtures/recorded/languages/html/clearAttributeVest.yml
@@ -0,0 +1,33 @@
+languageId: html
+command:
+ version: 1
+ spokenForm: clear attribute vest
+ action: clearAndSetSelection
+ targets:
+ - type: primitive
+ modifier: {type: containingScope, scopeType: attribute, includeSiblings: false}
+ mark: {type: decoratedSymbol, symbolColor: default, character: v}
+initialState:
+ documentContents: |-
+
+
+
+ selections:
+ - anchor: {line: 1, character: 4}
+ active: {line: 1, character: 4}
+ marks:
+ default.v:
+ start: {line: 0, character: 10}
+ end: {line: 0, character: 15}
+finalState:
+ documentContents: |-
+
+
+
+ selections:
+ - anchor: {line: 0, character: 6}
+ active: {line: 0, character: 6}
+ thatMark:
+ - anchor: {line: 0, character: 6}
+ active: {line: 0, character: 6}
+fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: v}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: attribute, includeSiblings: false}}]
diff --git a/src/test/suite/fixtures/recorded/languages/html/clearComment.yml b/src/test/suite/fixtures/recorded/languages/html/clearComment.yml
new file mode 100644
index 0000000000..c72e74deb2
--- /dev/null
+++ b/src/test/suite/fixtures/recorded/languages/html/clearComment.yml
@@ -0,0 +1,23 @@
+languageId: html
+command:
+ version: 1
+ spokenForm: clear comment
+ action: clearAndSetSelection
+ targets:
+ - type: primitive
+ modifier: {type: containingScope, scopeType: comment, includeSiblings: false}
+initialState:
+ documentContents:
+ selections:
+ - anchor: {line: 0, character: 8}
+ active: {line: 0, character: 8}
+ marks: {}
+finalState:
+ documentContents: ""
+ selections:
+ - anchor: {line: 0, character: 0}
+ active: {line: 0, character: 0}
+ thatMark:
+ - anchor: {line: 0, character: 0}
+ active: {line: 0, character: 0}
+fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: comment, includeSiblings: false}}]
diff --git a/src/test/suite/fixtures/recorded/languages/html/clearElement.yml b/src/test/suite/fixtures/recorded/languages/html/clearElement.yml
new file mode 100644
index 0000000000..ec2de69d43
--- /dev/null
+++ b/src/test/suite/fixtures/recorded/languages/html/clearElement.yml
@@ -0,0 +1,26 @@
+languageId: html
+command:
+ version: 1
+ spokenForm: clear element
+ action: clearAndSetSelection
+ targets:
+ - type: primitive
+ modifier: {type: containingScope, scopeType: xmlElement, includeSiblings: false}
+initialState:
+ documentContents: |-
+
+
+
+ selections:
+ - anchor: {line: 1, character: 4}
+ active: {line: 1, character: 4}
+ marks: {}
+finalState:
+ documentContents: ""
+ selections:
+ - anchor: {line: 0, character: 0}
+ active: {line: 0, character: 0}
+ thatMark:
+ - anchor: {line: 0, character: 0}
+ active: {line: 0, character: 0}
+fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: xmlElement, includeSiblings: false}}]
diff --git a/src/test/suite/fixtures/recorded/languages/html/clearEndTag.yml b/src/test/suite/fixtures/recorded/languages/html/clearEndTag.yml
new file mode 100644
index 0000000000..8494eddaa0
--- /dev/null
+++ b/src/test/suite/fixtures/recorded/languages/html/clearEndTag.yml
@@ -0,0 +1,28 @@
+languageId: html
+command:
+ version: 1
+ spokenForm: clear end tag
+ action: clearAndSetSelection
+ targets:
+ - type: primitive
+ modifier: {type: containingScope, scopeType: xmlEndTag, includeSiblings: false}
+initialState:
+ documentContents: |-
+
+
+
+ selections:
+ - anchor: {line: 1, character: 4}
+ active: {line: 1, character: 4}
+ marks: {}
+finalState:
+ documentContents: |
+
+
+ selections:
+ - anchor: {line: 2, character: 0}
+ active: {line: 2, character: 0}
+ thatMark:
+ - anchor: {line: 2, character: 0}
+ active: {line: 2, character: 0}
+fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: xmlEndTag, includeSiblings: false}}]
diff --git a/src/test/suite/fixtures/recorded/languages/html/clearKey.yml b/src/test/suite/fixtures/recorded/languages/html/clearKey.yml
new file mode 100644
index 0000000000..0e364d4fb5
--- /dev/null
+++ b/src/test/suite/fixtures/recorded/languages/html/clearKey.yml
@@ -0,0 +1,29 @@
+languageId: html
+command:
+ version: 1
+ spokenForm: clear key
+ action: clearAndSetSelection
+ targets:
+ - type: primitive
+ modifier: {type: containingScope, scopeType: collectionKey, includeSiblings: false}
+initialState:
+ documentContents: |-
+
+
+
+ selections:
+ - anchor: {line: 0, character: 9}
+ active: {line: 0, character: 9}
+ marks: {}
+finalState:
+ documentContents: |-
+
+
+
+ selections:
+ - anchor: {line: 0, character: 6}
+ active: {line: 0, character: 6}
+ thatMark:
+ - anchor: {line: 0, character: 6}
+ active: {line: 0, character: 6}
+fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: collectionKey, includeSiblings: false}}]
diff --git a/src/test/suite/fixtures/recorded/languages/html/clearName.yml b/src/test/suite/fixtures/recorded/languages/html/clearName.yml
new file mode 100644
index 0000000000..89dcadf8a6
--- /dev/null
+++ b/src/test/suite/fixtures/recorded/languages/html/clearName.yml
@@ -0,0 +1,29 @@
+languageId: html
+command:
+ version: 1
+ spokenForm: clear name
+ action: clearAndSetSelection
+ targets:
+ - type: primitive
+ modifier: {type: containingScope, scopeType: name, includeSiblings: false}
+initialState:
+ documentContents: |-
+
+
+
+ selections:
+ - anchor: {line: 0, character: 9}
+ active: {line: 0, character: 9}
+ marks: {}
+finalState:
+ documentContents: |-
+ < id="value">
+
+
+ selections:
+ - anchor: {line: 0, character: 1}
+ active: {line: 0, character: 1}
+ thatMark:
+ - anchor: {line: 0, character: 1}
+ active: {line: 0, character: 1}
+fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: name, includeSiblings: false}}]
diff --git a/src/test/suite/fixtures/recorded/languages/html/clearStartTag.yml b/src/test/suite/fixtures/recorded/languages/html/clearStartTag.yml
new file mode 100644
index 0000000000..498d53bfdf
--- /dev/null
+++ b/src/test/suite/fixtures/recorded/languages/html/clearStartTag.yml
@@ -0,0 +1,29 @@
+languageId: html
+command:
+ version: 1
+ spokenForm: clear start tag
+ action: clearAndSetSelection
+ targets:
+ - type: primitive
+ modifier: {type: containingScope, scopeType: xmlStartTag, includeSiblings: false}
+initialState:
+ documentContents: |-
+
+
+
+ selections:
+ - anchor: {line: 1, character: 4}
+ active: {line: 1, character: 4}
+ marks: {}
+finalState:
+ documentContents: |2-
+
+
+