Skip to content

Commit ca4c2b4

Browse files
authored
feat(vscode): provide Run CodeLens on route handlers (#960)
1 parent be257ee commit ca4c2b4

File tree

7 files changed

+425
-38
lines changed

7 files changed

+425
-38
lines changed

extensions/vscode/README.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ Developed with 💙 by [Very Good Ventures][very_good_ventures_link] 🦄
88

99
Dart Frog can be installed from the [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=VeryGoodVentures.dart-frog) or by [searching within VS Code](https://code.visualstudio.com/docs/editor/extension-gallery#_search-for-an-extension).
1010

11+
## Demonstration
12+
13+
![demonstration](https://raw.githubusercontent.com/VeryGoodOpenSource/dart_frog/main/extensions/vscode/assets/new-route-middleware-usage.gif)
14+
1115
## Commands
1216

1317
| Command | Description | Launch from |
@@ -18,17 +22,20 @@ Dart Frog can be installed from the [VS Code Marketplace](https://marketplace.vi
1822
| `Dart Frog: New Route` | Generates a new route | Context menu or command palette |
1923
| `Dart Frog: New Middleware` | Generates a new middleware | Context menu or command palette |
2024
| `Dart Frog: Start Daemon` | Starts the Dart Frog daemon | Command palette |
21-
| `Dart Frog: Start Development Server` | Starts a Dart Frog server | Command palette |
25+
| `Dart Frog: Start Development Server` | Starts a Dart Frog server | Command palette and CodeLens |
2226
| `Dart Frog: Debug Development Server` | Debugs a Dart Frog server | Command palette |
2327
| `Dart Frog: Start and Debug Development Server` | Starts and debugs a Dart Frog server | Command palette |
2428
| `Dart Frog: Stop Development Server` | Stops a Dart Frog server | Command palette |
2529

26-
## Demonstration
30+
## Configuration
2731

28-
![demonstration](https://raw.githubusercontent.com/VeryGoodOpenSource/dart_frog/main/extensions/vscode/assets/new-route-middleware-usage.gif)
32+
| Name | Description | Type | Default |
33+
| -------------------------- | --------------------------------------------------- | ------- | ------- |
34+
| `dart-frog.enableCodeLens` | Whether or not to enable [CodeLens][code_lens_link] | Boolean | True |
2935

3036
[ci_link]: https://github.com/VeryGoodOpenSource/dart_frog/actions/workflows/main.yaml
3137
[dart_frog_link_light]: https://github.com/verygoodopensource/dart_frog
3238
[license_link]: https://opensource.org/licenses/MIT
3339
[logo_black]: https://raw.githubusercontent.com/VeryGoodOpenSource/dart_frog/main/assets/dart_frog_logo_black.png
3440
[very_good_ventures_link]: https://verygood.ventures
41+
[code_lens_link]: https://code.visualstudio.com/blogs/2017/02/12/code-lens-roundup

extensions/vscode/package.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,14 @@
9393
"when": "!(resourceDirname =~ /routes/) && !(explorerResourceIsFolder && resourceFilename == routes)"
9494
}
9595
]
96+
},
97+
"configuration": {
98+
"properties": {
99+
"dart-frog.enableCodeLens": {
100+
"type": "boolean",
101+
"default": true
102+
}
103+
}
96104
}
97105
},
98106
"extensionDependencies": [
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./on-request-code-lens";
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import {
2+
CodeLens,
3+
CodeLensProvider,
4+
Event,
5+
EventEmitter,
6+
Position,
7+
ProviderResult,
8+
TextDocument,
9+
workspace,
10+
} from "vscode";
11+
import { nearestDartFrogProject } from "../utils";
12+
import path = require("path");
13+
14+
abstract class ConfigurableCodeLensProvider implements CodeLensProvider {
15+
protected codeLenses: CodeLens[] = [];
16+
17+
protected _onDidChangeCodeLenses: EventEmitter<void> =
18+
new EventEmitter<void>();
19+
public readonly onDidChangeCodeLenses: Event<void> =
20+
this._onDidChangeCodeLenses.event;
21+
22+
constructor() {
23+
workspace.onDidChangeConfiguration(() => {
24+
this._onDidChangeCodeLenses.fire();
25+
});
26+
}
27+
28+
// eslint-disable-next-line no-unused-vars
29+
public provideCodeLenses(document: TextDocument): ProviderResult<CodeLens[]> {
30+
if (!this.hasEnabledCodeLenses()) {
31+
return undefined;
32+
}
33+
return this.codeLenses;
34+
}
35+
36+
public resolveCodeLens?(codeLens: CodeLens): ProviderResult<CodeLens> {
37+
if (!this.hasEnabledCodeLenses()) {
38+
return undefined;
39+
}
40+
return codeLens;
41+
}
42+
43+
private hasEnabledCodeLenses(): boolean {
44+
return workspace.getConfiguration("dart-frog").get("enableCodeLens", true);
45+
}
46+
}
47+
48+
// eslint-disable-next-line max-len
49+
abstract class RegularExpressionCodeLensProvider extends ConfigurableCodeLensProvider {
50+
abstract readonly regex: RegExp;
51+
52+
public provideCodeLenses(document: TextDocument): ProviderResult<CodeLens[]> {
53+
if (!super.provideCodeLenses(document)) {
54+
return undefined;
55+
}
56+
57+
this.codeLenses = [];
58+
const text = document.getText();
59+
let matches;
60+
while ((matches = this.regex.exec(text)) !== null) {
61+
const line = document.lineAt(document.positionAt(matches.index).line);
62+
const indexOf = line.text.indexOf(matches[0]);
63+
const position = new Position(line.lineNumber, indexOf);
64+
const range = document.getWordRangeAtPosition(
65+
position,
66+
new RegExp(this.regex)
67+
);
68+
if (range) {
69+
this.codeLenses.push(new CodeLens(range));
70+
}
71+
}
72+
return this.codeLenses;
73+
}
74+
}
75+
76+
// eslint-disable-next-line max-len
77+
abstract class OnRequestCodeLensProvider extends RegularExpressionCodeLensProvider {
78+
readonly regex: RegExp = /Response\s*onRequest\(RequestContext .*?\)\s*{/g;
79+
80+
public provideCodeLenses(document: TextDocument): ProviderResult<CodeLens[]> {
81+
if (document.languageId !== "dart") {
82+
return undefined;
83+
}
84+
85+
const dartFrogProjectPath = nearestDartFrogProject(document.uri.fsPath);
86+
if (!dartFrogProjectPath) {
87+
return undefined;
88+
}
89+
90+
const routesPath = path.join(dartFrogProjectPath, "routes");
91+
if (!document.uri.fsPath.startsWith(routesPath)) {
92+
return undefined;
93+
}
94+
95+
return super.provideCodeLenses(document);
96+
}
97+
}
98+
99+
/**
100+
* Shows a "Run" CodeLens on route handlers, which allows starting a development
101+
* server.
102+
*/
103+
export class RunOnRequestCodeLensProvider extends OnRequestCodeLensProvider {
104+
public resolveCodeLens?(codeLens: CodeLens): ProviderResult<CodeLens> {
105+
if (!super.resolveCodeLens!(codeLens)) {
106+
return undefined;
107+
}
108+
109+
codeLens.command = {
110+
title: "Run",
111+
tooltip: "Starts a development server",
112+
command: "dart-frog.start-dev-server",
113+
};
114+
return codeLens;
115+
}
116+
}

extensions/vscode/src/extension.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
readLatestDartFrogCLIVersion,
2020
suggestInstallingDartFrogCLI,
2121
} from "./utils";
22+
import { RunOnRequestCodeLensProvider } from "./code-lens";
2223

2324
/**
2425
* This method is called when the extension is activated.
@@ -41,6 +42,11 @@ export function activate(
4142
ensureCompatibleCLI();
4243
}
4344

45+
vscode.languages.registerCodeLensProvider(
46+
"dart",
47+
new RunOnRequestCodeLensProvider()
48+
);
49+
4450
context.subscriptions.push(
4551
vscode.commands.registerCommand("dart-frog.create", create),
4652
vscode.commands.registerCommand("dart-frog.install-cli", installCLI),

0 commit comments

Comments
 (0)