Skip to content

Commit

Permalink
Fix typescript plugin with typescript 5 (#78)
Browse files Browse the repository at this point in the history
* Fix typescript plugin in vscode

This is done by tweaking the logic slightly (typescript 5 returns a
slightly different AST tree) and also use the typescript util
functions that are passed into the plugin. This is important because
the vscode extension currently bundles a typescript version which
might be different from the one that the project is using. And given
that typescript relies on enums with numbers, we can run into issues
where the value 357 for SyntaxKind might mean one thing in one
version and something completely different in another typescript
version.

* Update changelog

* Bump version to 0.5.3
  • Loading branch information
flenter authored Jun 27, 2023
1 parent b86c099 commit 40a4001
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 26 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## [v0.5.3] - 2023-06-26

### Changed

- Fixed issue with running the typescript plugin in combination with typescript 5 (and also fix issues when the vscode extension is bundled with a different version of typescript compared to what vscode is using)

## [v0.5.2] - 2023-06-16

### Added
Expand Down
2 changes: 1 addition & 1 deletion packages/typescript-plugin/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@autometrics/typescript-plugin",
"version": "0.5.2",
"version": "0.5.3",
"description": "Language service plugin for Autometrics",
"author": "Fiberplane<[email protected]>",
"contributors": [
Expand Down
61 changes: 42 additions & 19 deletions packages/typescript-plugin/src/astHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import ts from "typescript/lib/tsserverlibrary";

import tsserver from "typescript/lib/tsserverlibrary";
import type {
Node,
SourceFile,
TypeChecker,
} from "typescript/lib/tsserverlibrary";
import { Tsserver } from "./types";
import type { NodeType } from "./types";

/**
Expand All @@ -9,8 +14,9 @@ import type { NodeType } from "./types";
* @param typechecker The helper utility typechecker
*/
export function isAutometricsWrappedOrDecorated(
node: ts.Node,
typechecker: ts.TypeChecker,
node: Node,
typechecker: TypeChecker,
ts: Tsserver,
) {
// Checks if the user is hovering over the autometrics wrapper itself in which
// case we should not show the queries
Expand All @@ -25,7 +31,7 @@ export function isAutometricsWrappedOrDecorated(
// Checks if the function the user is hovering over has a type
// AutometricsWrapper or is wrapped by a function that has a type
// AutometricsWrapper
const checkWrapperType = (node: ts.Node) => {
const checkWrapperType = (node: Node) => {
if (ts.isSourceFile(node)) {
return;
}
Expand All @@ -50,11 +56,12 @@ export function isAutometricsWrappedOrDecorated(
}

const isDecorated =
hasAutometricsDecorator(method) || hasAutometricsDecorator(method.parent);
hasAutometricsDecorator(method, ts) ||
hasAutometricsDecorator(method.parent, ts);
return isDecorated;
}

export function hasAutometricsDecorator(node: ts.Node) {
export function hasAutometricsDecorator(node: Node, ts: Tsserver) {
const decorators = ts.canHaveDecorators(node) && ts.getDecorators(node);
if (!decorators) {
return false;
Expand All @@ -69,14 +76,15 @@ export function hasAutometricsDecorator(node: ts.Node) {

/**
* Gets the node identifier
* @param node {ts.Node} - the node itself
* @param node {Node} - the node itself
* @param nodeType {NodeType} - so we know what kind of check to run
* @param typechecker {ts.TypeChecker} - helper util
*/
export function getNodeIdentifier(
node: ts.Node,
node: Node,
nodeType: NodeType,
typechecker: ts.TypeChecker,
typechecker: TypeChecker,
ts: Tsserver,
): string {
if (nodeType === "method" && ts.isIdentifier(node)) {
return node.escapedText.toString();
Expand Down Expand Up @@ -118,13 +126,17 @@ export function getNodeIdentifier(
* Gets the type of the node (we care only about functions or methods)
* @param node The node itself
* @param typechecker The helper utility
* @returns {NodeType}
* @returns {NodeType | undefined}
*/
export function getNodeType(node: ts.Node, typechecker: ts.TypeChecker) {
export function getNodeType(
node: Node,
typechecker: TypeChecker,
ts: Tsserver,
): "method" | "function" | undefined {
const declaration = typechecker.getSymbolAtLocation(node);

if (!declaration?.valueDeclaration || !node?.parent) {
return;
if (!declaration?.valueDeclaration) {
return undefined;
}

const { valueDeclaration } = declaration;
Expand Down Expand Up @@ -158,6 +170,8 @@ export function getNodeType(node: ts.Node, typechecker: ts.TypeChecker) {
) {
return "function";
}

return undefined;
}

/**
Expand All @@ -166,14 +180,23 @@ export function getNodeType(node: ts.Node, typechecker: ts.TypeChecker) {
* @param position Current cursor/mouse position
*/
export function getNodeAtCursor(
sourceFile: ts.SourceFile,
sourceFile: SourceFile,
position: number,
): ts.Node | undefined {
function find(node: ts.Node): ts.Node | undefined {
ts: Tsserver,
): Node | undefined {
const find = (node: Node): Node | undefined => {
if (position >= node.getStart() && position < node.getEnd()) {
return ts.forEachChild(node, find) || node;
const children = node.getChildren();
for (const child of children) {
const result = find(child);
if (result) {
return result;
}
}

return node;
}
}
};

return find(sourceFile);
}
15 changes: 9 additions & 6 deletions packages/typescript-plugin/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import tsserver from "typescript/lib/tsserverlibrary";
import type { QuickInfo, server } from "typescript/lib/tsserverlibrary";

import {
getNodeAtCursor,
Expand All @@ -13,21 +13,22 @@ import {
makePrometheusUrl,
} from "./queryHelpers";
import { createLogger, getProxy } from "./utils";
import { Tsserver } from "./types";

type Config = {
prometheusUrl?: string;
docsOutputFormat?: "prometheus" | "vscode";
};

function init(modules: { typescript: typeof tsserver }) {
function init(modules: { typescript: Tsserver }) {
let pluginConfig: Config;
const ts = modules.typescript;

function create({
config,
languageService,
project,
}: ts.server.PluginCreateInfo) {
}: server.PluginCreateInfo) {
const log = createLogger(project);
log("started");

Expand All @@ -49,11 +50,12 @@ function init(modules: { typescript: typeof tsserver }) {
const docsOutputFormat = pluginConfig.docsOutputFormat || "prometheus";

const sourceFile = languageService.getProgram().getSourceFile(filename);
const nodeAtCursor = getNodeAtCursor(sourceFile, position);
const nodeType = getNodeType(nodeAtCursor, typechecker);
const nodeAtCursor = getNodeAtCursor(sourceFile, position, ts);
const nodeType = getNodeType(nodeAtCursor, typechecker, ts);
const autometrics = isAutometricsWrappedOrDecorated(
nodeAtCursor,
typechecker,
ts,
);

// If either autometrics checker or node type is undefined return early
Expand All @@ -65,6 +67,7 @@ function init(modules: { typescript: typeof tsserver }) {
nodeAtCursor,
nodeType,
typechecker,
ts,
);

// The output of this plugin will
Expand Down Expand Up @@ -184,7 +187,7 @@ function init(modules: { typescript: typeof tsserver }) {
queries,
);

return <ts.QuickInfo>{
return <QuickInfo>{
...prior,
documentation: enrichedDocumentation,
};
Expand Down
3 changes: 3 additions & 0 deletions packages/typescript-plugin/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
import type tsserver from "typescript/lib/tsserverlibrary";

export type Tsserver = typeof tsserver;
export * from "./nodeType";

0 comments on commit 40a4001

Please sign in to comment.