Skip to content

Commit e526e60

Browse files
Add PHP support (#594)
* Start adding PHP support * Add class support * Add some PHP func stuff * Add argumentOrParameter * Add collections * Fix strings * Add values * Chuck assignment operators when chucking values * Add if statement * Fix args and params * Style fix * Prettify tests * Add name support * Change takeIfState test to changeIfState * Add functions * Add param type selector * Detect more types * Fix type cast chucking * Fix lambdas * Add object creation to function call
1 parent cfbfcb1 commit e526e60

File tree

94 files changed

+2966
-3
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+2966
-3
lines changed

src/languages/constants.ts

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const supportedLanguageIds = [
1111
"json",
1212
"jsonc",
1313
"markdown",
14+
"php",
1415
"python",
1516
"ruby",
1617
"scala",

src/languages/getNodeMatcher.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@ import { patternMatchers as json } from "./json";
1414
import { patternMatchers as typescript } from "./typescript";
1515
import java from "./java";
1616
import { patternMatchers as html } from "./html";
17+
import php from "./php";
1718
import python from "./python";
1819
import markdown from "./markdown";
1920
import scala from "./scala";
2021
import go from "./go";
21-
import { patternMatchers as ruby } from "./ruby"
22+
import { patternMatchers as ruby } from "./ruby";
2223
import { UnsupportedLanguageError } from "../errors";
2324
import { SupportedLanguageId } from "./constants";
2425

@@ -62,6 +63,7 @@ const languageMatchers: Record<
6263
json,
6364
jsonc: json,
6465
markdown,
66+
php,
6567
python,
6668
ruby,
6769
scala,

src/languages/getTextFragmentExtractor.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { SyntaxNode } from "web-tree-sitter";
22
import { SelectionWithEditor } from "../typings/Types";
3-
import { stringTextFragmentExtractor as jsonStringTextFragmentExtractor } from "./json";
4-
import { stringTextFragmentExtractor as typescriptStringTextFragmentExtractor } from "./typescript";
53
import { stringTextFragmentExtractor as htmlStringTextFragmentExtractor } from "./html";
4+
import { stringTextFragmentExtractor as jsonStringTextFragmentExtractor } from "./json";
5+
import { stringTextFragmentExtractor as phpStringTextFragmentExtractor } from "./php";
66
import { stringTextFragmentExtractor as rubyStringTextFragmentExtractor } from "./ruby";
7+
import { stringTextFragmentExtractor as typescriptStringTextFragmentExtractor } from "./typescript";
78
import { UnsupportedLanguageError } from "../errors";
89
import { Range } from "vscode";
910
import { SupportedLanguageId } from "./constants";
@@ -156,6 +157,10 @@ const textFragmentExtractors: Record<
156157
jsonStringTextFragmentExtractor
157158
),
158159
markdown: fullDocumentTextFragmentExtractor,
160+
php: constructDefaultTextFragmentExtractor(
161+
"php",
162+
phpStringTextFragmentExtractor
163+
),
159164
python: constructDefaultTextFragmentExtractor("python"),
160165
ruby: constructDefaultTextFragmentExtractor(
161166
"ruby",

src/languages/php.ts

+169
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
import { Selection, TextEditor } from "vscode";
2+
import { SyntaxNode } from "web-tree-sitter";
3+
import {
4+
NodeMatcherAlternative,
5+
ScopeType,
6+
SelectionWithContext,
7+
SelectionWithEditor,
8+
} from "../typings/Types";
9+
import { patternFinder } from "../util/nodeFinders";
10+
import {
11+
argumentMatcher,
12+
cascadingMatcher,
13+
createPatternMatchers,
14+
leadingMatcher,
15+
matcher,
16+
trailingMatcher,
17+
} from "../util/nodeMatchers";
18+
import { getNodeRange } from "../util/nodeSelectors";
19+
20+
// Generated by the following command:
21+
// > curl https://raw.githubusercontent.com/tree-sitter/tree-sitter-php/0ce134234214427b6aeb2735e93a307881c6cd6f/src/node-types.json \
22+
// | jq '[.[] | select(.type == "_statement") | .subtypes[].type]'
23+
const STATEMENT_TYPES = [
24+
"break_statement",
25+
"class_declaration",
26+
"compound_statement",
27+
"const_declaration",
28+
"continue_statement",
29+
"declare_statement",
30+
"do_statement",
31+
"echo_statement",
32+
"empty_statement",
33+
"enum_declaration",
34+
"expression_statement",
35+
"for_statement",
36+
"foreach_statement",
37+
"function_definition",
38+
"function_static_declaration",
39+
"global_declaration",
40+
"goto_statement",
41+
"if_statement",
42+
"interface_declaration",
43+
"named_label_statement",
44+
"namespace_definition",
45+
"namespace_use_declaration",
46+
"return_statement",
47+
"switch_statement",
48+
"trait_declaration",
49+
"try_statement",
50+
"unset_statement",
51+
"while_statement"
52+
];
53+
54+
// Taken from https://www.php.net/manual/en/language.operators.assignment.php
55+
const assignmentOperators = [
56+
"=",
57+
// Arithmetic
58+
"+=",
59+
"-=",
60+
"*=",
61+
"/=",
62+
"%=",
63+
"**=",
64+
// Bitwise
65+
"&=",
66+
"|=",
67+
"^=",
68+
"<<=",
69+
">>=",
70+
// Other
71+
".=",
72+
"??=",
73+
];
74+
75+
/**
76+
* Given a node representing the text of a type cast, will return the
77+
* content range as the text inner type, and the outside range includes
78+
* the surrounding parentheses, so that "chuck type" deletes the parens
79+
* @param editor The editor containing the node
80+
* @param node The node to extract from; will be the content of the type cast without the surrounding parens
81+
* @returns The selection with context
82+
*/
83+
function castTypeExtractor(
84+
editor: TextEditor,
85+
node: SyntaxNode
86+
): SelectionWithContext {
87+
const range = getNodeRange(node);
88+
const contentRange = range;
89+
const leftParenRange = getNodeRange(node.previousSibling!);
90+
const rightParenRange = getNodeRange(node.nextSibling!.nextSibling!);
91+
const outerRange = range.with(leftParenRange.start, rightParenRange.start);
92+
93+
return {
94+
selection: new Selection(contentRange.start, contentRange.end),
95+
context: {
96+
outerSelection: new Selection(outerRange.start, outerRange.end),
97+
},
98+
};
99+
}
100+
101+
const nodeMatchers: Partial<Record<ScopeType, NodeMatcherAlternative>> = {
102+
statement: STATEMENT_TYPES,
103+
ifStatement: "if_statement",
104+
class: "class_declaration",
105+
className: "class_declaration[name]",
106+
name: [
107+
"assignment_expression[left]",
108+
"class_declaration[name]",
109+
"function_definition[name]",
110+
"method_declaration[name]",
111+
],
112+
comment: "comment",
113+
string: "string",
114+
type: cascadingMatcher(
115+
trailingMatcher(["~cast_expression[type]"]),
116+
matcher(
117+
patternFinder("cast_expression[type]"),
118+
castTypeExtractor,
119+
),
120+
),
121+
122+
namedFunction: trailingMatcher(
123+
[
124+
"function_definition",
125+
"assignment_expression.anonymous_function_creation_expression",
126+
"assignment_expression.arrow_function",
127+
],
128+
[";"],
129+
),
130+
anonymousFunction: [
131+
"anonymous_function_creation_expression",
132+
"arrow_function",
133+
],
134+
functionCall: [
135+
"function_call_expression",
136+
"object_creation_expression",
137+
],
138+
functionName: [
139+
"function_definition[name]",
140+
"method_declaration[name]",
141+
],
142+
143+
value: leadingMatcher(
144+
[
145+
"array_element_initializer[1]",
146+
"assignment_expression[right]",
147+
"augmented_assignment_expression[right]",
148+
"return_statement[0]",
149+
],
150+
assignmentOperators.concat(["=>"]),
151+
),
152+
153+
collectionKey: trailingMatcher(["array_element_initializer[0]"], ["=>"]),
154+
collectionItem: argumentMatcher("array_creation_expression"),
155+
156+
argumentOrParameter: argumentMatcher("arguments", "formal_parameters"),
157+
};
158+
export default createPatternMatchers(nodeMatchers);
159+
160+
export function stringTextFragmentExtractor(
161+
node: SyntaxNode,
162+
_selection: SelectionWithEditor
163+
) {
164+
if (node.type === "string") {
165+
return getNodeRange(node);
166+
}
167+
168+
return null;
169+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
languageId: php
2+
command:
3+
version: 1
4+
spokenForm: change arg
5+
action: clearAndSetSelection
6+
targets:
7+
- type: primitive
8+
modifier: {type: containingScope, scopeType: argumentOrParameter, includeSiblings: false}
9+
initialState:
10+
documentContents: |-
11+
<?php
12+
13+
function myFunc($value)
14+
{
15+
16+
}
17+
selections:
18+
- anchor: {line: 2, character: 20}
19+
active: {line: 2, character: 20}
20+
marks: {}
21+
finalState:
22+
documentContents: |-
23+
<?php
24+
25+
function myFunc()
26+
{
27+
28+
}
29+
selections:
30+
- anchor: {line: 2, character: 16}
31+
active: {line: 2, character: 16}
32+
thatMark:
33+
- anchor: {line: 2, character: 16}
34+
active: {line: 2, character: 16}
35+
fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: argumentOrParameter, includeSiblings: false}, isImplicit: false}]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
languageId: php
2+
command:
3+
version: 1
4+
spokenForm: change arg
5+
action: clearAndSetSelection
6+
targets:
7+
- type: primitive
8+
modifier: {type: containingScope, scopeType: argumentOrParameter, includeSiblings: false}
9+
initialState:
10+
documentContents: |-
11+
<?php
12+
13+
myFunc($value, $num, $param);
14+
selections:
15+
- anchor: {line: 2, character: 16}
16+
active: {line: 2, character: 16}
17+
marks: {}
18+
finalState:
19+
documentContents: |-
20+
<?php
21+
22+
myFunc($value, , $param);
23+
selections:
24+
- anchor: {line: 2, character: 15}
25+
active: {line: 2, character: 15}
26+
thatMark:
27+
- anchor: {line: 2, character: 15}
28+
active: {line: 2, character: 15}
29+
fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: argumentOrParameter, includeSiblings: false}, isImplicit: false}]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
languageId: php
2+
command:
3+
version: 1
4+
spokenForm: change arg
5+
action: clearAndSetSelection
6+
targets:
7+
- type: primitive
8+
modifier: {type: containingScope, scopeType: argumentOrParameter, includeSiblings: false}
9+
initialState:
10+
documentContents: |-
11+
<?php
12+
13+
function myFunc(...$nums)
14+
{
15+
16+
}
17+
selections:
18+
- anchor: {line: 2, character: 20}
19+
active: {line: 2, character: 20}
20+
marks: {}
21+
finalState:
22+
documentContents: |-
23+
<?php
24+
25+
function myFunc()
26+
{
27+
28+
}
29+
selections:
30+
- anchor: {line: 2, character: 16}
31+
active: {line: 2, character: 16}
32+
thatMark:
33+
- anchor: {line: 2, character: 16}
34+
active: {line: 2, character: 16}
35+
fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: argumentOrParameter, includeSiblings: false}, isImplicit: false}]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
languageId: php
2+
command:
3+
version: 1
4+
spokenForm: change arg
5+
action: clearAndSetSelection
6+
targets:
7+
- type: primitive
8+
modifier: {type: containingScope, scopeType: argumentOrParameter, includeSiblings: false}
9+
initialState:
10+
documentContents: |-
11+
<?php
12+
13+
function myFunc($value, ...$nums)
14+
{
15+
16+
}
17+
selections:
18+
- anchor: {line: 2, character: 20}
19+
active: {line: 2, character: 20}
20+
marks: {}
21+
finalState:
22+
documentContents: |-
23+
<?php
24+
25+
function myFunc(, ...$nums)
26+
{
27+
28+
}
29+
selections:
30+
- anchor: {line: 2, character: 16}
31+
active: {line: 2, character: 16}
32+
thatMark:
33+
- anchor: {line: 2, character: 16}
34+
active: {line: 2, character: 16}
35+
fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: argumentOrParameter, includeSiblings: false}, isImplicit: false}]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
languageId: php
2+
command:
3+
version: 1
4+
spokenForm: change arg
5+
action: clearAndSetSelection
6+
targets:
7+
- type: primitive
8+
modifier: {type: containingScope, scopeType: argumentOrParameter, includeSiblings: false}
9+
initialState:
10+
documentContents: |-
11+
<?php
12+
13+
function myFunc($value, ...$nums)
14+
{
15+
16+
}
17+
selections:
18+
- anchor: {line: 2, character: 28}
19+
active: {line: 2, character: 28}
20+
marks: {}
21+
finalState:
22+
documentContents: |-
23+
<?php
24+
25+
function myFunc($value, )
26+
{
27+
28+
}
29+
selections:
30+
- anchor: {line: 2, character: 24}
31+
active: {line: 2, character: 24}
32+
thatMark:
33+
- anchor: {line: 2, character: 24}
34+
active: {line: 2, character: 24}
35+
fullTargets: [{type: primitive, mark: {type: cursor}, selectionType: token, position: contents, insideOutsideType: inside, modifier: {type: containingScope, scopeType: argumentOrParameter, includeSiblings: false}, isImplicit: false}]

0 commit comments

Comments
 (0)