Skip to content

Commit

Permalink
Merge pull request #42 from yoavbls/fix-formatting-bugs-and-add-tests
Browse files Browse the repository at this point in the history
Fix formatting bugs and add tests
  • Loading branch information
yoavbls authored Apr 22, 2023
2 parents 7e7f623 + 42308a6 commit ec8f8a2
Show file tree
Hide file tree
Showing 12 changed files with 259 additions and 89 deletions.
57 changes: 28 additions & 29 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,32 @@
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
{
"version": "0.2.0",
"configurations": [
{
"name": "Run Extension",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"outFiles": [
"${workspaceFolder}/dist/**/*.js"
],
"preLaunchTask": "${defaultBuildTask}"
},
{
"name": "Extension Tests",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
],
"outFiles": [
"${workspaceFolder}/out/**/*.js",
"${workspaceFolder}/dist/**/*.js"
],
"preLaunchTask": "tasks: watch-tests"
}
]
"version": "0.2.0",
"configurations": [
{
"name": "Run Extension",
"type": "extensionHost",
"request": "launch",
"args": ["--extensionDevelopmentPath=${workspaceFolder}"],
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
"preLaunchTask": "${defaultBuildTask}",
"env": {
"VSCODE_DEBUG_MODE": "true"
}
},
{
"name": "Extension Tests",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
],
"outFiles": [
"${workspaceFolder}/out/**/*.js",
"${workspaceFolder}/dist/**/*.js"
],
"preLaunchTask": "tasks: watch-tests"
}
]
}
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@
[![Visual Studio Code](https://img.shields.io/badge/--007ACC?logo=visual%20studio%20code&logoColor=ffffff)](https://marketplace.visualstudio.com/items?itemName=yoavbls.pretty-ts-errors) [![GitHub license](https://badgen.net/github/license/yoavbls/pretty-ts-errors)](https://github.com/yoavbls/pretty-ts-errors/blob/main/LICENSE) ![visitor badge](https://visitor-badge.glitch.me/badge?page_id=pretty-ts-errors)
[![GitHub stars](https://img.shields.io/github/stars/yoavbls/pretty-ts-errors.svg?style=social&label=Star)](https://GitHub.com/yoavbls/pretty-ts-errors/stargazers/)

<a href="https://github.com/yoavbls/pretty-ts-errors/issues/38"><img src="https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/assets/assets/mentions/vote.png" height="32" /></a>
<a href="https://github.com/yoavbls/pretty-ts-errors/issues/38"><img src="https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/assets/assets/mentions/vote.png" width="490px" /></a>

TypeScript errors become messier as the complexity of types increases. At some point, TypeScript will throw on you a shitty heap of parentheses and `"..."`.
This extension will help you understand what's going on. For example, in this relatively simple error:

<img src="./assets/this.png" style="max-height: 350px" height="350px" />&nbsp; &nbsp; <img src="./assets/instead-of-that.png" height="350px" width="350px" style="max-height: 350px" />
<img src="./assets/this.png" width="340.438px" />&nbsp; &nbsp; <img src="./assets/instead-of-that.png" width="350px" />


## Watch this
<a href="https://www.youtube.com/watch?v=9RM2aErJs-s" target="_blank">
<img src="https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/assets/assets/mentions/theo-video.png" alt="Watch theo's video" style="max-height: 150px" width="550" />
<img src="https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/assets/assets/mentions/theo-video.png" alt="Watch theo's video" width="550" />
</a>

## Features
Expand Down Expand Up @@ -56,20 +56,20 @@ Yes, these types include things like `... more ...`, `{ ... }`, etc in an incons
## Hype section
<a href="https://twitter.com/t3dotgg/status/1647759462709747713">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/assets/assets/mentions/theo-dark.png">
<img width="400" src="https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/assets/assets/mentions/theo-light.png">
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/assets/assets/mentions/theo-light.pn#gh-light-mode-only">
<img width="400" src="https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/assets/assets/mentions/theo-dark.png#gh-dark-mode-only">
</picture>
</a>
<a href="https://twitter.com/johnsoncodehk/status/1646214711204286465">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/assets/assets/mentions/johnson-dark.png">
<img width="400" src="https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/assets/assets/mentions/johnson-light.png">
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/assets/assets/mentions/johnson-light.png#gh-light-mode-only">
<img width="400" src="https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/assets/assets/mentions/johnson-dark.png#gh-dark-mode-only">
</picture>
</a>
<a href="https://twitter.com/tannerlinsley/status/1647982562026090496">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/assets/assets/mentions/tanner-dark.png">
<img width="400" src="https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/assets/assets/mentions/tanner-light.png">
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/assets/assets/mentions/tanner-light.png#gh-light-mode-only">
<img width="400" src="https://raw.githubusercontent.com/yoavbls/pretty-ts-errors/assets/assets/mentions/tanner-dark.png#gh-dark-mode-only">
</picture>
</a>

Expand Down
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
},
"homepage": "https://github.com/yoavbls/pretty-ts-errors",
"engines": {
"vscode": "^1.70.0"
"vscode": "^1.77.0"
},
"categories": [
"Programming Languages",
Expand Down Expand Up @@ -54,7 +54,7 @@
"scripts": {
"vscode:prepublish": "npm run package",
"compile": "node scripts/build",
"watch": "npm run compile --watch",
"watch": "npm run compile -- --watch",
"build": "vsce package",
"package": "node scripts/build -- --production",
"compile-tests": "tsc -p . --outDir out",
Expand All @@ -77,7 +77,6 @@
"eslint": "^8.20.0",
"glob": "^8.0.3",
"mocha": "^10.0.0",
"ts-loader": "^9.3.1",
"typescript": "^5.0.4"
},
"dependencies": {
Expand Down
1 change: 1 addition & 0 deletions src/components/title.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const title = (diagnostic: Diagnostic) => d/*html*/ `
`
: ""
}
<br>
<span>
`;
Expand Down
24 changes: 9 additions & 15 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,20 @@ import {
Range,
window,
} from "vscode";
import { createConverter } from "vscode-languageclient/lib/common/codeConverter";
import { formatDiagnostic } from "./format/formatDiagnostic";
import { prettify } from "./format/prettify";
import { hoverProvider } from "./provider/hoverProvider";
import { registerSelectedTextHoverProvider } from "./provider/selectedTextHoverProvider";
import { uriStore } from "./provider/uriStore";
import { has } from "./utils";
import { createConverter } from "vscode-languageclient/lib/common/codeConverter";
import { format } from "prettier";

export function activate(context: ExtensionContext) {
const registeredLanguages = new Set<string>();
const converter = createConverter();

registerSelectedTextHoverProvider(context);

context.subscriptions.push(
languages.onDidChangeDiagnostics(async (e) => {
e.uris.forEach((uri) => {
Expand All @@ -35,10 +38,11 @@ export function activate(context: ExtensionContext) {
: false
)
.forEach(async (diagnostic) => {

// formatDiagnostic converts message based on LSP Diagnostic type, not VSCode Diagnostic type, so it can be used in other IDEs.
// Here we convert VSCode Diagnostic to LSP Diagnostic to make formatDiagnostic recognize it.
const markdownString = new MarkdownString(formatDiagnostic(converter.asDiagnostic(diagnostic), prettify));
const markdownString = new MarkdownString(
formatDiagnostic(converter.asDiagnostic(diagnostic), prettify)
);

markdownString.isTrusted = true;
markdownString.supportHtml = true;
Expand All @@ -51,7 +55,7 @@ export function activate(context: ExtensionContext) {
});
uriStore[uri.path] = items;

if (hasTsDiagnostic && uri.scheme === "file") {
if (hasTsDiagnostic) {
const editor = window.visibleTextEditors.find(
(editor) => editor.document.uri.toString() === uri.toString()
);
Expand All @@ -60,7 +64,6 @@ export function activate(context: ExtensionContext) {
context.subscriptions.push(
languages.registerHoverProvider(
{
scheme: "file",
language: editor.document.languageId,
},
hoverProvider
Expand All @@ -72,12 +75,3 @@ export function activate(context: ExtensionContext) {
})
);
}

function prettify(text: string) {
return format(text, {
parser: "typescript",
printWidth: 60,
singleAttributePerLine: false,
arrowParens: "avoid",
});
}
39 changes: 28 additions & 11 deletions src/format/formatDiagnosticMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ const formatTypeScriptBlock = (_: string, code: string) =>
const formatSimpleTypeBlock = (_: string, code: string) =>
inlineCodeBlock(code, "type");

const formatTypeOrModuleBlock = (_: string, prefix: string, code: string, format: (type: string) => string) =>
const formatTypeOrModuleBlock = (
_: string,
prefix: string,
code: string,
format: (type: string) => string
) =>
formatTypeBlock(
prefix,
["module", "file", "file name"].includes(prefix.toLowerCase())
Expand All @@ -16,7 +21,10 @@ const formatTypeOrModuleBlock = (_: string, prefix: string, code: string, format
format
);

export const formatDiagnosticMessage = (message: string, format: (type: string) => string) =>
export const formatDiagnosticMessage = (
message: string,
format: (type: string) => string
) =>
message
// format declare module snippet
.replaceAll(
Expand All @@ -26,9 +34,9 @@ export const formatDiagnosticMessage = (message: string, format: (type: string)
)
// format missing props error
.replaceAll(
/(is missing the following properties from type .*: )(.+?)(?=and|$)/g,
(_, pre, post) =>
`${pre}<ul>${post
/(is missing the following properties from type )'(.*)': (.+?)(?=and|$)/g,
(_, pre, type, post) =>
`${pre}${formatTypeBlock("", type, format)}: <ul>${post
.split(", ")
.filter(Boolean)
.map((prop: string) => `<li>${prop}</li>`)
Expand All @@ -38,13 +46,21 @@ export const formatDiagnosticMessage = (message: string, format: (type: string)
.replaceAll(
/(types) '(.*?)' and '(.*?)'[\.]?/gi,
(_: string, p1: string, p2: string, p3: string) =>
`${formatTypeBlock(p1, p2, format)} and ${formatTypeBlock("", p3, format)}`
`${formatTypeBlock(p1, p2, format)} and ${formatTypeBlock(
"",
p3,
format
)}`
)
// Format type annotation options
.replaceAll(
/type annotation must be '(.*?)' or '(.*?)'[\.]?/gi,
(_: string, p1: string, p2: string, p3: string) =>
`${formatTypeBlock(p1, p2, format)} or ${formatTypeBlock("", p3, format)}`
`${formatTypeBlock(p1, p2, format)} or ${formatTypeBlock(
"",
p3,
format
)}`
)
.replaceAll(
/(Overload \d of \d), '(.*?)', /gi,
Expand All @@ -54,7 +70,7 @@ export const formatDiagnosticMessage = (message: string, format: (type: string)
.replaceAll(/^'"[^"]*"'$/g, formatTypeScriptBlock)
// Format types
.replaceAll(
/(type|type alias|interface|module|file|file name) '(.*?)'(?=[\s.])/gi,
/(type|type alias|interface|module|file|file name|method's) '(.*?)'(?=[\s.])/gi,
(_, p1: string, p2: string) => formatTypeOrModuleBlock(_, p1, p2, format)
)
// Format reversed types
Expand All @@ -80,6 +96,7 @@ export const formatDiagnosticMessage = (message: string, format: (type: string)
(_, p1: string, p2: string) => `${p1} ${formatTypeScriptBlock("", p2)}`
)
// Format regular code blocks
.replaceAll(/'((?:(?!:\s*}).)*?)'(?!\s*:)/g, (_: string, p1: string) => unstyledCodeBlock(p1));


.replaceAll(
/'((?:(?!:\s*}).)*?)' (?!\s*:)/g,
(_: string, p1: string) => `${unstyledCodeBlock(p1)} `
);
25 changes: 17 additions & 8 deletions src/format/formatTypeBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import {
} from "../components";
import { addMissingParentheses } from "./addMissingParentheses";

export function formatTypeBlock(prefix: string, type: string, format: (type: string) => string) {
export function formatTypeBlock(
prefix: string,
type: string,
format: (type: string) => string
) {
// Return a simple code block if it's just a parenthesis
if (type.match(/^(\[\]|\{\})$/)) {
return `${prefix} ${unstyledCodeBlock(type)}`;
Expand All @@ -20,7 +24,7 @@ export function formatTypeBlock(prefix: string, type: string, format: (type: str
return `${prefix} ${inlineCodeBlock(type, "type")}`;
}

const prettyType = convertToOriginalType(prettifyType(convertToValidType(type), format));
const prettyType = prettifyType(type, format);

if (prettyType.includes("\n")) {
return `${prefix}: ${multiLineCodeBlock(prettyType, "type")}`;
Expand All @@ -31,13 +35,18 @@ export function formatTypeBlock(prefix: string, type: string, format: (type: str
/**
* Try to make type prettier with prettier
*/
function prettifyType(type: string, format: (type: string) => string) {
export function prettifyType(
type: string,
format: (type: string) => string,
options?: { throwOnError?: boolean }
) {
try {
// Wrap type with valid statement, format it and extract the type back
return convertToOriginalType(
format(convertToValidType(type))
);
return convertToOriginalType(format(convertToValidType(type)));
} catch (e) {
if (options?.throwOnError) {
throw e;
}
return type;
}
}
Expand All @@ -47,7 +56,7 @@ const convertToValidType = (type: string) =>
// Add missing parentheses when the type ends with "...""
.replace(/(.*)\.\.\.$/, (_, p1) => addMissingParentheses(p1))
// Replace single parameter function destructuring because it's not a valid type
.replaceAll(/\((\{.*\})\:/g, (_, p1) => `(param: /* ${p1} */`)
// .replaceAll(/\((\{.*\})\:/g, (_, p1) => `(param: /* ${p1} */`)
// Change `(...): return` which is invalid to `(...) => return`
.replace(/^(\(.*\)): /, (_, p1) => `${p1} =>`)
.replaceAll(/... (\d{0,}) more .../g, (_, p1) => `___${p1}MORE___`)
Expand All @@ -62,6 +71,6 @@ const convertToOriginalType = (type: string) =>
.replaceAll(/___MORE___: (\d{0,});/g, (_, p1) => `... ${p1} more ...;`)
.replaceAll(/___(\d{0,})MORE___/g, (_, p1) => `... ${p1} more ...`)
.replaceAll(/... (\d{0,}) more .../g, (_, p1) => `/* ${p1} more */`) // ... x more ... not shown sell
.replaceAll(/\(param\: \/\* (\{ .* \}) \*\//g, (_, p1) => `(${p1}: `)
// .replaceAll(/\(param\: \/\* (\{ .* \}) \*\//g, (_, p1) => `(${p1}: `)
.replace(/type x =[ ]?((.|\n)*);.*/g, "$1")
.trim();
10 changes: 10 additions & 0 deletions src/format/prettify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { format } from "prettier";

export function prettify(text: string) {
return format(text, {
parser: "typescript",
printWidth: 60,
singleAttributePerLine: false,
arrowParens: "avoid",
});
}
Loading

0 comments on commit ec8f8a2

Please sign in to comment.