Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions data/playground/html.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
<html>

<head>
<link rel="stylesheet" href="./index.css">
<style>
p {
color: red;
font-size: 20px;
}
</style>
<title>
Hello
</title>
</head>
<body>
<script>
<h1>Title</h1>
</script>
<h1>Title</h1>
<div id="this-is-my-2id" data-a=5>
Hello world
<!-- Comment -->
</div>
<br/>
</body>

</html>
1 change: 1 addition & 0 deletions src/languages/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export const supportedLanguageIds = [
"clojure",
"cpp",
"csharp",
"html",
"java",
"javascript",
"javascriptreact",
Expand Down
6 changes: 4 additions & 2 deletions src/languages/getNodeMatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -46,9 +47,10 @@ const languageMatchers: Record<
Record<ScopeType, NodeMatcher>
> = {
c: cpp,
clojure,
cpp,
csharp: csharp,
csharp,
clojure,
html,
java,
javascript: typescript,
javascriptreact: typescript,
Expand Down
6 changes: 5 additions & 1 deletion src/languages/getTextFragmentExtractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -126,6 +126,10 @@ const textFragmentExtractors: Record<
),
cpp: constructDefaultTextFragmentExtractor("cpp"),
csharp: constructDefaultTextFragmentExtractor("csharp"),
html: constructDefaultTextFragmentExtractor(
"html",
htmlStringTextFragmentExtractor
),
java: constructDefaultTextFragmentExtractor(
"java",
constructHackedStringTextFragmentExtractor("java")
Expand Down
52 changes: 52 additions & 0 deletions src/languages/html.ts
Original file line number Diff line number Diff line change
@@ -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<Record<ScopeType, NodeMatcherAlternative>> = {
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") {
Copy link
Member

Choose a reason for hiding this comment

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

Will an attribute value always be a string?

Copy link
Member Author

Choose a reason for hiding this comment

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

No. But it's the only type that can contain other matching pairs

Copy link
Member

Choose a reason for hiding this comment

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

Ok so this is a hack, then, right? Because there might be things that are children that are not strings? It's ok if it's a hack, we should just document that in a comment

Copy link
Member

Choose a reason for hiding this comment

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

You could also just check node parent type as well maybe?

Copy link
Member

Choose a reason for hiding this comment

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

Or check that it has first and last child of type "?

Copy link
Member Author

@AndreasArvidsson AndreasArvidsson Dec 16, 2021

Choose a reason for hiding this comment

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

I wouldn't say it's a hack. It is a type that can contain text based matching pairs. But I can't contain any children in the tree sitter as far as I know. I tried with few different values and it all behaved as expected.

return getNodeRange(node);
}

return null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const delimiterToText: Record<
SimpleSurroundingPairName,
[IndividualDelimiterText, IndividualDelimiterText]
> = {
angleBrackets: ["<", ">"],
angleBrackets: [["</", "<"], [">", "/>"]],
backtickQuotes: ["`", "`"],
curlyBrackets: [["{", "${"], "}"],
doubleQuotes: ['"', '"'],
Expand Down
Original file line number Diff line number Diff line change
@@ -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: |-
<html id="value">

</html>
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: |-
<html id>

</html>
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}}]
Original file line number Diff line number Diff line change
@@ -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: |-
<html id="value">

</html>
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: |-
<html >

</html>
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}}]
Original file line number Diff line number Diff line change
@@ -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: <!-- Comment -->
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}}]
Original file line number Diff line number Diff line change
@@ -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: |-
<html>

</html>
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}}]
28 changes: 28 additions & 0 deletions src/test/suite/fixtures/recorded/languages/html/clearEndTag.yml
Original file line number Diff line number Diff line change
@@ -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: |-
<html>

</html>
selections:
- anchor: {line: 1, character: 4}
active: {line: 1, character: 4}
marks: {}
finalState:
documentContents: |
<html>

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}}]
29 changes: 29 additions & 0 deletions src/test/suite/fixtures/recorded/languages/html/clearKey.yml
Original file line number Diff line number Diff line change
@@ -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: |-
<html id="value">

</html>
selections:
- anchor: {line: 0, character: 9}
active: {line: 0, character: 9}
marks: {}
finalState:
documentContents: |-
<html ="value">

</html>
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}}]
29 changes: 29 additions & 0 deletions src/test/suite/fixtures/recorded/languages/html/clearName.yml
Original file line number Diff line number Diff line change
@@ -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: |-
<html id="value">

</html>
selections:
- anchor: {line: 0, character: 9}
active: {line: 0, character: 9}
marks: {}
finalState:
documentContents: |-
< id="value">

</html>
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}}]
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
languageId: html
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if "clear" should leave the angles. Can't decide

Copy link
Member Author

Choose a reason for hiding this comment

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

Take and clear always have the same range so far. I don't want to change that

command:
version: 1
spokenForm: clear start tag
action: clearAndSetSelection
targets:
- type: primitive
modifier: {type: containingScope, scopeType: xmlStartTag, includeSiblings: false}
initialState:
documentContents: |-
<html>

</html>
selections:
- anchor: {line: 1, character: 4}
active: {line: 1, character: 4}
marks: {}
finalState:
documentContents: |2-


</html>
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: xmlStartTag, includeSiblings: false}}]
Loading