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
10 changes: 7 additions & 3 deletions docs/api/linting.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ plugins:
styles_ordering: true
```

After running `dart pub get` you will now see additional Jaspr lints
when invoking code assist on a component function like `div()` or `p()`.

After running `dart pub get` you now get additional lints and code assists in your IDE or when running `dart analyze`.

## Lint Rules

Expand Down Expand Up @@ -140,6 +138,12 @@ when invoking code assist on a component function like `div()` or `p()`.
</Property>
</Card>

<Card>
<Property name="unsafe_imports" type="Warning">
Warns about unsafe platform-specific imports in your code depending on whether it is executed on the server or the client.
</Property>
</Card>

## Code Assists

<Card>
Expand Down
2 changes: 1 addition & 1 deletion modules/jaspr-code/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## 0.4.0
## 0.4.0-wip

- Jaspr is now installed using `dart install jaspr_cli` instead of `dart pub global activate jaspr_cli`.
This requires Dart 3.10 or later.
Expand Down
2 changes: 1 addition & 1 deletion modules/jaspr-code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "jaspr-code",
"displayName": "Jaspr",
"description": "Jaspr framework support for Visual Studio Code",
"version": "0.4.0",
"version": "0.4.0-wip",
"repository": {
"url": "https://github.com/schultek/jaspr",
"directory": "modules/jaspr-code"
Expand Down
166 changes: 91 additions & 75 deletions modules/jaspr-code/src/code_lenses/component_code_lens.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,46 @@
import { lspToRange } from "../helpers/object_helper";
import * as vs from "vscode";
import { ScopeResults, ScopesDomain, ScopeTarget } from "../jaspr/scopes_domain";
import { dartExtensionApi } from "../api";
import { PublicOutline } from "dart-code/src/extension/api/interfaces";

export type ScopeResults = {
locations: Record<string, ScopeLocation>;
scopes: Record<string, ScopeLibraryResult>;
}

export type ScopeLibraryResult = {
components: string[];
clientScopeRoots?: string[];
serverScopeRoots?: string[];
};

export type ScopeLocation = {
path: string;
name: string;
line: number;
char: number;
length: number;
};


export class ComponentCodeLensProvider implements vs.CodeLensProvider, vs.Disposable {
private scopesDomain: ScopesDomain;
private watcher: vs.FileSystemWatcher;

private _onDidChangeCodeLenses: vs.EventEmitter<void> = new vs.EventEmitter<void>();
public readonly onDidChangeCodeLenses: vs.Event<void> = this._onDidChangeCodeLenses.event;

private hintCommand: vs.Disposable;
private scopeResults: ScopeResults = {};
private scopeResults: Record<string, ScopeResults> = {};

constructor() {

this.watcher = vs.workspace.createFileSystemWatcher("**/.dart_tool/jaspr/scopes.json");

this.watcher.onDidChange(async (uri) => {
await this.loadScopes(uri);
this._onDidChangeCodeLenses.fire();
});

constructor(scopesDomain: ScopesDomain) {
this.scopesDomain = scopesDomain;
this.scopesDomain.onDidChangeScopes((results: ScopeResults) => {
this.scopeResults = results;
this.watcher.onDidCreate(async (uri) => {
await this.loadScopes(uri);
this._onDidChangeCodeLenses.fire();
});

Expand All @@ -43,70 +67,68 @@ export class ComponentCodeLensProvider implements vs.CodeLensProvider, vs.Dispos
);
}

public async provideCodeLenses(
private async loadScopes(uri: vs.Uri) {
const content = await vs.workspace.fs.readFile(uri);
this.scopeResults[uri.fsPath] = JSON.parse(content.toString());
}

public provideCodeLenses(
document: vs.TextDocument,
token: vs.CancellationToken
): Promise<vs.CodeLens[] | undefined> {
const outline = await dartExtensionApi.workspace.getOutline(
document,
token
);
if (!outline?.children?.length) {
return;
}

): vs.CodeLens[] | undefined {
const results: vs.CodeLens[] = [];

const serverRoots: ScopeTarget[] = [];
const clientRoots: ScopeTarget[] = [];
const components: PublicOutline[] = [];
let scopeResults: ScopeResults | null = null;
let item: ScopeLibraryResult | null = null;
for (const key in this.scopeResults) {
scopeResults = this.scopeResults[key];
item = scopeResults.scopes[document.uri.fsPath];

if (item) break;
}

const item = this.scopeResults[document.uri.fsPath];
if (!item) {
if (!scopeResults || !item) {
return;
}

for (let child of outline.children) {
const component = item.components?.find((c) => c === child.element.name);
if (!component) {
continue;
}
const mapIdsToLocations = (ids?: string[]): vs.Location[] => {
if (!ids) return [];
return ids.map((id) => scopeResults.locations[id]).map(this.scopeLocationToLocation);
};

serverRoots.push(...(item.serverScopeRoots ?? []));
clientRoots.push(...(item.clientScopeRoots ?? []));
components.push(child);
}
const serverRootLocations = mapIdsToLocations(item.serverScopeRoots);
const clientRootLocations = mapIdsToLocations(item.clientScopeRoots);

const showScopeHint = (serverRootLocations.length > 0 || clientRootLocations.length > 0) && vs.workspace
.getConfiguration("jaspr.scopes", document)
.get("showHint", true);

for (let componentId of item.components) {

const location = scopeResults.locations[componentId];
const range = this.scopeLocationToRange(location);

for (let component of components) {
if (serverRoots.length > 0) {
if (serverRootLocations.length > 0) {
results.push(
this.createCodeLens(
document,
component,
"Server Scope",
serverRoots.map(targetToLocation)
)
new vs.CodeLens(range, {
arguments: [document.uri, range.start, serverRootLocations, "peek"],
command: "editor.action.peekLocations",
title: "Server Scope",
})
);
}

if (clientRoots.length > 0) {
if (clientRootLocations.length > 0) {
results.push(
this.createCodeLens(
document,
component,
"Client Scope",
clientRoots.map(targetToLocation)
)
new vs.CodeLens(range, {
arguments: [document.uri, range.start, clientRootLocations, "peek"],
command: "editor.action.peekLocations",
title: "Client Scope",
})
);
}

if (
(serverRoots.length > 0 || clientRoots.length > 0) &&
vs.workspace
.getConfiguration("jaspr.scopes", document)
.get("showHint", true)
) {
var range = lspToRange(component.codeRange);
if (showScopeHint) {
results.push(
new vs.CodeLens(range, {
command: "jaspr.action.showScopeHint",
Expand All @@ -119,29 +141,23 @@ export class ComponentCodeLensProvider implements vs.CodeLensProvider, vs.Dispos
return results;
}

private createCodeLens(
document: vs.TextDocument,
element: any,
name: string,
targets: vs.Location[]
): vs.CodeLens {
var range = lspToRange(element.codeRange);
return new vs.CodeLens(range, {
arguments: [document.uri, range.start, targets, "peek"],
command: "editor.action.peekLocations",
title: name,
});
private scopeLocationToLocation(location: ScopeLocation): vs.Location {
return new vs.Location(
vs.Uri.file(location.path),
new vs.Position(location.line - 1, location.char)
);
}

private scopeLocationToRange(location: ScopeLocation): vs.Range {
return new vs.Range(
new vs.Position(location.line - 1, location.char),
new vs.Position(location.line - 1, location.char + location.length)
);
}

public dispose(): any {
this._onDidChangeCodeLenses.dispose();
this.watcher.dispose();
this.hintCommand.dispose();
}
}

function targetToLocation(target: ScopeTarget): vs.Location {
return new vs.Location(
vs.Uri.file(target.path),
new vs.Position(target.line - 1, target.character)
);
}
}
17 changes: 4 additions & 13 deletions modules/jaspr-code/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ import { jasprClean, jasprDoctor, jasprServe } from "./commands";
import {
findJasprProjectFolders,
} from "./helpers/project_helper";
import { JasprToolingDaemon } from "./jaspr/tooling_daemon";
import { ScopesDomain } from "./jaspr/scopes_domain";
import { HtmlDomain } from "./jaspr/html_domain";
import { HtmlPasteProvider } from "./jaspr/html_paste_provider";
import { dartExtensionApi } from "./api";
import { ComponentCodeLensProvider } from "./code_lenses/component_code_lens";
import { ServeCodeLensProvider } from "./code_lenses/serve_code_lens";
Expand Down Expand Up @@ -67,17 +65,10 @@ export async function activate(context: vscode.ExtensionContext) {
vscode.commands.registerCommand("jaspr.serve", jasprServe(context))
);

const toolingDaemon = new JasprToolingDaemon();
context.subscriptions.push(toolingDaemon);
await toolingDaemon.start(context);

const scopesDomain = new ScopesDomain(toolingDaemon);
context.subscriptions.push(scopesDomain);

context.subscriptions.push(
vscode.languages.registerCodeLensProvider(
{ language: "dart", scheme: "file" },
new ComponentCodeLensProvider(scopesDomain)
new ComponentCodeLensProvider()
)
);

Expand All @@ -90,6 +81,6 @@ export async function activate(context: vscode.ExtensionContext) {
);
}

const htmlDomain = new HtmlDomain(toolingDaemon);
context.subscriptions.push(htmlDomain);
const htmlPasteProvider = new HtmlPasteProvider();
context.subscriptions.push(htmlPasteProvider);
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import * as vscode from "vscode";
import { JasprToolingDaemon } from "./tooling_daemon";
import { runJaspr } from "../helpers/process_helper";
import { cwd } from "process";

export class HtmlDomain
implements vscode.DocumentPasteEditProvider, vscode.Disposable
{
private toolingDaemon: JasprToolingDaemon;
export class HtmlPasteProvider
implements vscode.DocumentPasteEditProvider, vscode.Disposable {
private pasteEditProviderDisposable: vscode.Disposable | undefined;

static readonly kind = vscode.DocumentDropOrPasteEditKind.Empty.append(
Expand All @@ -13,15 +12,13 @@ export class HtmlDomain
"jaspr"
);

constructor(toolingDaemon: JasprToolingDaemon) {
this.toolingDaemon = toolingDaemon;

constructor() {
this.pasteEditProviderDisposable =
vscode.languages.registerDocumentPasteEditProvider(
{ language: "dart" },
this,
{
providedPasteEditKinds: [HtmlDomain.kind],
providedPasteEditKinds: [HtmlPasteProvider.kind],
pasteMimeTypes: ["text/plain"],
}
);
Expand All @@ -34,30 +31,26 @@ export class HtmlDomain
context: vscode.DocumentPasteEditContext,
token: vscode.CancellationToken
) {
if (
this.toolingDaemon.jasprVersion === undefined ||
this.toolingDaemon.jasprVersion < "0.21.2"
) {
return;
}


const content = await data.get("text/plain")?.asString();
if (content?.startsWith("<")) {
if (content && /^<[a-z]+(\s+[^>]*)*>/i.test(content)) {
try {
const result = await this.toolingDaemon.sendMessage(
"html.convert",
{ html: content },
1000
);

const output = await runJaspr(['convert-html', '--html', JSON.stringify(content), '--json'], cwd());
if (token.isCancellationRequested) {
return;
}
const result = output.split('\n').find((line) => line.startsWith('{"result":'));
if (!result) {
vscode.window.showErrorMessage("Failed to convert HTML to Jaspr: " + output);
return;
}

return [
new vscode.DocumentPasteEdit(
result,
JSON.parse(result)['result'],
"Convert to Jaspr",
HtmlDomain.kind
HtmlPasteProvider.kind
),
];
} catch (e) {
Expand Down
Loading
Loading