Skip to content

Commit ed63c57

Browse files
Merge pull request #261 from laravel/livewire
Additional Livewire, Volt, and Blade support
2 parents c7fd315 + d378743 commit ed63c57

File tree

10 files changed

+202
-30
lines changed

10 files changed

+202
-30
lines changed

generatable.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,16 @@
4848
"type": "bladeComponent",
4949
"label": "Blade components",
5050
"features": [
51-
"link"
51+
"link",
52+
"completion"
53+
]
54+
},
55+
{
56+
"type": "livewireComponent",
57+
"label": "Livewire components",
58+
"features": [
59+
"link",
60+
"completion"
5261
]
5362
}
5463
]

generate-config.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
'diagnostics' => "Enable diagnostics for {$label}.",
2424
'hover' => "Enable hover information for {$label}.",
2525
'link' => "Enable linking for {$label}.",
26+
'completion' => "Enable completion for {$label}.",
2627
default => null,
2728
},
2829
];

package.json

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@
132132
"type": "boolean",
133133
"default": true,
134134
"generated": true,
135-
"description": null
135+
"description": "Enable completion for app bindings."
136136
},
137137
"Laravel.asset.diagnostics": {
138138
"type": "boolean",
@@ -156,7 +156,7 @@
156156
"type": "boolean",
157157
"default": true,
158158
"generated": true,
159-
"description": null
159+
"description": "Enable completion for asset."
160160
},
161161
"Laravel.auth.diagnostics": {
162162
"type": "boolean",
@@ -180,14 +180,20 @@
180180
"type": "boolean",
181181
"default": true,
182182
"generated": true,
183-
"description": null
183+
"description": "Enable completion for auth."
184184
},
185185
"Laravel.bladeComponent.link": {
186186
"type": "boolean",
187187
"default": true,
188188
"generated": true,
189189
"description": "Enable linking for Blade components."
190190
},
191+
"Laravel.bladeComponent.completion": {
192+
"type": "boolean",
193+
"default": true,
194+
"generated": true,
195+
"description": "Enable completion for Blade components."
196+
},
191197
"Laravel.config.diagnostics": {
192198
"type": "boolean",
193199
"default": true,
@@ -210,7 +216,7 @@
210216
"type": "boolean",
211217
"default": true,
212218
"generated": true,
213-
"description": null
219+
"description": "Enable completion for config."
214220
},
215221
"Laravel.controllerAction.diagnostics": {
216222
"type": "boolean",
@@ -234,7 +240,7 @@
234240
"type": "boolean",
235241
"default": true,
236242
"generated": true,
237-
"description": null
243+
"description": "Enable completion for controller actions."
238244
},
239245
"Laravel.env.diagnostics": {
240246
"type": "boolean",
@@ -258,7 +264,7 @@
258264
"type": "boolean",
259265
"default": true,
260266
"generated": true,
261-
"description": null
267+
"description": "Enable completion for env."
262268
},
263269
"Laravel.inertia.diagnostics": {
264270
"type": "boolean",
@@ -282,7 +288,19 @@
282288
"type": "boolean",
283289
"default": true,
284290
"generated": true,
285-
"description": null
291+
"description": "Enable completion for Inertia."
292+
},
293+
"Laravel.livewireComponent.link": {
294+
"type": "boolean",
295+
"default": true,
296+
"generated": true,
297+
"description": "Enable linking for Livewire components."
298+
},
299+
"Laravel.livewireComponent.completion": {
300+
"type": "boolean",
301+
"default": true,
302+
"generated": true,
303+
"description": "Enable completion for Livewire components."
286304
},
287305
"Laravel.middleware.diagnostics": {
288306
"type": "boolean",
@@ -306,7 +324,7 @@
306324
"type": "boolean",
307325
"default": true,
308326
"generated": true,
309-
"description": null
327+
"description": "Enable completion for middleware."
310328
},
311329
"Laravel.mix.diagnostics": {
312330
"type": "boolean",
@@ -330,7 +348,7 @@
330348
"type": "boolean",
331349
"default": true,
332350
"generated": true,
333-
"description": null
351+
"description": "Enable completion for mix."
334352
},
335353
"Laravel.paths.link": {
336354
"type": "boolean",
@@ -360,7 +378,7 @@
360378
"type": "boolean",
361379
"default": true,
362380
"generated": true,
363-
"description": null
381+
"description": "Enable completion for route."
364382
},
365383
"Laravel.translation.diagnostics": {
366384
"type": "boolean",
@@ -384,7 +402,7 @@
384402
"type": "boolean",
385403
"default": true,
386404
"generated": true,
387-
"description": null
405+
"description": "Enable completion for translation."
388406
},
389407
"Laravel.view.diagnostics": {
390408
"type": "boolean",
@@ -408,7 +426,7 @@
408426
"type": "boolean",
409427
"default": true,
410428
"generated": true,
411-
"description": null
429+
"description": "Enable completion for view."
412430
}
413431
}
414432
}

src/completion/Blade.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,6 @@ export default class Blade implements vscode.CompletionItemProvider {
1212
token: vscode.CancellationToken,
1313
context: vscode.CompletionContext,
1414
): vscode.CompletionItem[] {
15-
let isBlade =
16-
["blade", "laravel-blade"].includes(document.languageId) ||
17-
document.fileName.endsWith(".blade.php");
18-
19-
if (!isBlade) {
20-
return [];
21-
}
22-
2315
return this.getDefaultDirectives(document, position).concat(
2416
getCustomBladeDirectives().items.map((directive) => {
2517
let completeItem = new vscode.CompletionItem(

src/extension.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import EloquentCompletion from "./completion/Eloquent";
1313
import Registry from "./completion/Registry";
1414
import ValidationCompletion from "./completion/Validation";
1515
import { updateDiagnostics } from "./diagnostic/diagnostic";
16+
import { completionProvider as bladeComponentCompletion } from "./features/bladeComponent";
17+
import { completionProvider as livewireComponentCompletion } from "./features/livewireComponent";
1618
import { hoverProviders } from "./hover/HoverProvider";
1719
import { linkProviders } from "./link/LinkProvider";
1820
import { configAffected } from "./support/config";
@@ -54,12 +56,13 @@ export function activate(context: vscode.ExtensionContext) {
5456

5557
console.log("Laravel VS Code Started...");
5658

57-
const LANGUAGES = [
58-
{ scheme: "file", language: "php" },
59+
const BLADE_LANGUAGES = [
5960
{ scheme: "file", language: "blade" },
6061
{ scheme: "file", language: "laravel-blade" },
6162
];
6263

64+
const LANGUAGES = [{ scheme: "file", language: "php" }, ...BLADE_LANGUAGES];
65+
6366
initVendorWatchers();
6467
setParserBinaryPath(context);
6568

@@ -112,7 +115,18 @@ export function activate(context: vscode.ExtensionContext) {
112115
...TRIGGER_CHARACTERS.concat(["|"]),
113116
),
114117
vscode.languages.registerCompletionItemProvider(
115-
LANGUAGES,
118+
BLADE_LANGUAGES,
119+
bladeComponentCompletion,
120+
"x",
121+
"-",
122+
),
123+
vscode.languages.registerCompletionItemProvider(
124+
BLADE_LANGUAGES,
125+
livewireComponentCompletion,
126+
":",
127+
),
128+
vscode.languages.registerCompletionItemProvider(
129+
BLADE_LANGUAGES,
116130
new BladeCompletion(),
117131
"@",
118132
),

src/features/bladeComponent.ts

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { getViews } from "@src/repositories/views";
2+
import { config } from "@src/support/config";
23
import { projectPath } from "@src/support/project";
34
import * as vscode from "vscode";
45
import { LinkProvider } from "..";
@@ -10,10 +11,10 @@ export const linkProvider: LinkProvider = (doc: vscode.TextDocument) => {
1011
const views = getViews().items;
1112

1213
lines.forEach((line, index) => {
13-
const match = line.match(/<\/?(x-[^\s>]+)/);
14+
const match = line.match(/<\/?x-([^\s>]+)/);
1415

1516
if (match && match.index !== undefined) {
16-
const componentName = match[1].replace("x-", "");
17+
const componentName = match[1];
1718
// Standard component
1819
const viewName = "components." + componentName;
1920
// Index component
@@ -39,3 +40,40 @@ export const linkProvider: LinkProvider = (doc: vscode.TextDocument) => {
3940

4041
return Promise.resolve(links);
4142
};
43+
44+
export const completionProvider: vscode.CompletionItemProvider = {
45+
provideCompletionItems(
46+
doc: vscode.TextDocument,
47+
pos: vscode.Position,
48+
): vscode.ProviderResult<vscode.CompletionItem[]> {
49+
if (!config("bladeComponent.completion", true)) {
50+
return undefined;
51+
}
52+
53+
const componentPrefixes = ["x", "x-"];
54+
const pathPrefix = "components.";
55+
const line = doc.lineAt(pos.line).text;
56+
57+
const match = componentPrefixes.find((prefix) => {
58+
const linePrefix = line.substring(
59+
pos.character - prefix.length,
60+
pos.character,
61+
);
62+
63+
return linePrefix !== prefix;
64+
});
65+
66+
if (!match) {
67+
return undefined;
68+
}
69+
70+
return getViews()
71+
.items.filter((view) => view.key.startsWith(pathPrefix))
72+
.map(
73+
(view) =>
74+
new vscode.CompletionItem(
75+
"x-" + view.key.replace(pathPrefix, ""),
76+
),
77+
);
78+
},
79+
};

src/features/livewireComponent.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { getViews } from "@src/repositories/views";
2+
import { config } from "@src/support/config";
3+
import { projectPath } from "@src/support/project";
4+
import * as vscode from "vscode";
5+
import { LinkProvider } from "..";
6+
7+
export const linkProvider: LinkProvider = (doc: vscode.TextDocument) => {
8+
const links: vscode.DocumentLink[] = [];
9+
const text = doc.getText();
10+
const lines = text.split("\n");
11+
const views = getViews().items;
12+
13+
lines.forEach((line, index) => {
14+
const match = line.match(/<\/?livewire:([^\s>]+)/);
15+
16+
if (match && match.index !== undefined) {
17+
const componentName = match[1];
18+
// Standard component
19+
const viewName = `livewire.${componentName}`;
20+
// Index component
21+
const view = views.find((v) => v.key === viewName);
22+
23+
if (view) {
24+
links.push(
25+
new vscode.DocumentLink(
26+
new vscode.Range(
27+
new vscode.Position(index, match.index + 1),
28+
new vscode.Position(
29+
index,
30+
match.index + match[0].length,
31+
),
32+
),
33+
vscode.Uri.parse(projectPath(view.path)),
34+
),
35+
);
36+
}
37+
}
38+
});
39+
40+
return Promise.resolve(links);
41+
};
42+
43+
export const completionProvider: vscode.CompletionItemProvider = {
44+
provideCompletionItems(
45+
doc: vscode.TextDocument,
46+
pos: vscode.Position,
47+
): vscode.ProviderResult<vscode.CompletionItem[]> {
48+
if (!config("livewireComponent.completion", true)) {
49+
return undefined;
50+
}
51+
52+
const componentPrefix = "<livewire:";
53+
const pathPrefix = "livewire.";
54+
const line = doc.lineAt(pos.line).text;
55+
const linePrefix = line.substring(
56+
pos.character - componentPrefix.length,
57+
pos.character,
58+
);
59+
60+
if (linePrefix !== componentPrefix) {
61+
return undefined;
62+
}
63+
64+
return getViews()
65+
.items.filter((view) => view.key.startsWith(pathPrefix))
66+
.map(
67+
(view) =>
68+
new vscode.CompletionItem(view.key.replace(pathPrefix, "")),
69+
);
70+
},
71+
};

0 commit comments

Comments
 (0)