Skip to content

Commit dfa064f

Browse files
committed
initial working MVP
1 parent b358fed commit dfa064f

File tree

8 files changed

+132
-8
lines changed

8 files changed

+132
-8
lines changed

package.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,27 @@
4747
"extensions": [
4848
".type"
4949
]
50+
},
51+
{
52+
"id": "type-markdown-injection"
5053
}
5154
],
5255
"grammars": [
5356
{
5457
"language": "type",
5558
"scopeName": "source.type",
5659
"path": "./syntaxes/type.tmGrammar.json"
60+
},
61+
{
62+
"language": "type-markdown-injection",
63+
"scopeName": "markdown.type.codeblock",
64+
"path": "./syntaxes/type-markdown-injection.tmGrammar.json",
65+
"injectTo": [
66+
"text.html.markdown"
67+
],
68+
"embeddedLanguages": {
69+
"meta.embedded.block.type": "type"
70+
}
5771
}
5872
]
5973
},

src/components/title.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@ import { Diagnostic } from "vscode-languageserver-types";
22
import { compressToEncodedURIComponent, d } from "../utils";
33
import { KNOWN_ERROR_NUMBERS } from "./consts/knownErrorNumbers";
44
import { miniLine } from "./miniLine";
5+
import { Uri } from 'vscode';
6+
import { PRETTY_TS_ERRORS_SCHEME } from '../provider/textDocumentProvider';
57

6-
export const title = (diagnostic: Diagnostic) => d/*html*/ `
8+
export const title = (diagnostic: Diagnostic, uri: Uri) => d/*html*/ `
79
<span style="color:#f96363;">⚠ Error </span>${
810
typeof diagnostic.code === "number"
911
? d/*html*/ `
1012
<span style="color:#5f5f5f;">
11-
(TS${diagnostic.code})
12-
${errorCodeExplanationLink(diagnostic.code)} |
13+
(TS${diagnostic.code})
14+
${errorCodeExplanationLink(diagnostic.code)} |
1315
${errorMessageTranslationLink(diagnostic.message)}
16+
${errorMessageInANewFile(diagnostic, uri)}
1417
</span>
1518
`
1619
: ""
@@ -37,3 +40,15 @@ export const errorMessageTranslationLink = (message: Diagnostic["message"]) => {
3740
</span>
3841
</a>`;
3942
};
43+
44+
export const errorMessageInANewFile = (diagnostic: Diagnostic, uri: Uri) => {
45+
const range = `${diagnostic.range.start.line}:${diagnostic.range.start.character}-${diagnostic.range.end.line}:${diagnostic.range.end.character}`;
46+
const virtualFileUri = Uri.parse(`${PRETTY_TS_ERRORS_SCHEME}:${encodeURIComponent(uri.fsPath + '.md')}?range=${encodeURIComponent(range)}`);
47+
const args = [virtualFileUri];
48+
const href = Uri.parse(`command:markdown.showPreview?${encodeURIComponent(JSON.stringify(args))}`);
49+
return d/*html*/ `
50+
<a title="Open in new tab" href="${href}">
51+
<span class="codicon codicon-new-file">
52+
</span>
53+
</a>`;
54+
};

src/extension.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
MarkdownString,
55
Range,
66
window,
7+
workspace,
78
} from "vscode";
89
import { createConverter } from "vscode-languageclient/lib/common/codeConverter";
910
import { formatDiagnostic } from "./format/formatDiagnostic";
@@ -12,6 +13,7 @@ import { hoverProvider } from "./provider/hoverProvider";
1213
import { registerSelectedTextHoverProvider } from "./provider/selectedTextHoverProvider";
1314
import { uriStore } from "./provider/uriStore";
1415
import { has } from "./utils";
16+
import { PRETTY_TS_ERRORS_SCHEME, textDocumentContentProvider } from './provider/textDocumentProvider';
1517

1618
const cache = new Map();
1719

@@ -21,6 +23,10 @@ export function activate(context: ExtensionContext) {
2123

2224
registerSelectedTextHoverProvider(context);
2325

26+
context.subscriptions.push(
27+
workspace.registerTextDocumentContentProvider(PRETTY_TS_ERRORS_SCHEME, textDocumentContentProvider),
28+
);
29+
2430
context.subscriptions.push(
2531
languages.onDidChangeDiagnostics(async (e) => {
2632
e.uris.forEach((uri) => {
@@ -49,7 +55,7 @@ export function activate(context: ExtensionContext) {
4955

5056
if (!formattedMessage) {
5157
const markdownString = new MarkdownString(
52-
formatDiagnostic(converter.asDiagnostic(diagnostic), prettify)
58+
formatDiagnostic(converter.asDiagnostic(diagnostic), uri, prettify)
5359
);
5460

5561
markdownString.isTrusted = true;

src/format/embedSymbolLinks.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export function embedSymbolLinks(diagnostic: Diagnostic): Diagnostic {
1414
if (!symbol) {
1515
return diagnostic;
1616
}
17+
1718
return {
1819
...diagnostic,
1920
message: diagnostic.message.replaceAll(

src/format/formatDiagnostic.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@ import { d } from "../utils";
44
import { embedSymbolLinks } from "./embedSymbolLinks";
55
import { formatDiagnosticMessage } from "./formatDiagnosticMessage";
66
import { identSentences } from "./identSentences";
7+
import { Uri } from 'vscode';
78

89
export function formatDiagnostic(
910
diagnostic: Diagnostic,
11+
uri: Uri,
1012
format: (type: string) => string
1113
) {
1214
const newDiagnostic = embedSymbolLinks(diagnostic);
1315

1416
return d/*html*/ `
15-
${title(diagnostic)}
17+
${title(diagnostic, uri)}
1618
<span>
1719
${formatDiagnosticMessage(identSentences(newDiagnostic.message), format)}
1820
</span>

src/provider/selectedTextHoverProvider.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { miniLine } from "../components";
1010
import { formatDiagnostic } from "../format/formatDiagnostic";
1111
import { prettify } from "../format/prettify";
1212
import { d } from "../utils";
13+
import { uriStore } from './uriStore';
1314

1415
/**
1516
* Register an hover provider in debug only.
@@ -47,14 +48,21 @@ export function registerSelectedTextHoverProvider(context: ExtensionContext) {
4748
source: "ts",
4849
code: 1337,
4950
}),
51+
document.uri,
5052
prettify
5153
)
5254
),
5355
]
5456
: [];
5557

56-
contents[0].isTrusted = true;
57-
contents[0].supportHtml = true;
58+
if (contents.length) {
59+
contents[0].isTrusted = true;
60+
contents[0].supportHtml = true;
61+
}
62+
63+
if (range) {
64+
uriStore[document.uri.fsPath] = [{ range, contents }];
65+
}
5866

5967
return {
6068
contents,
@@ -65,7 +73,7 @@ export function registerSelectedTextHoverProvider(context: ExtensionContext) {
6573
);
6674
}
6775

68-
const debugHoverHeader = d/*html*/ `
76+
const debugHoverHeader = d/*html*/ `
6977
<span style="color:#f96363;">
7078
<span class="codicon codicon-debug"></span>
7179
Formatted selected text (debug only)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Range, TextDocumentContentProvider } from 'vscode';
2+
import { uriStore } from './uriStore';
3+
4+
export const PRETTY_TS_ERRORS_SCHEME = 'pretty-ts-errors';
5+
6+
export const textDocumentContentProvider: TextDocumentContentProvider = {
7+
provideTextDocumentContent(uri) {
8+
const searchParams = new URLSearchParams(uri.query);
9+
const fsPath = uri.fsPath.replace(/\.md$/, '');
10+
if (!searchParams.has('range')) {
11+
return `range query parameter is missing for uri: ${uri}`;
12+
}
13+
const items = uriStore[fsPath];
14+
if (!items) {
15+
return `no diagnostics found for ${fsPath}`;
16+
}
17+
const range = createRangeFromString(searchParams.get('range')!);
18+
const item = items.find((item) => {
19+
return item.range.isEqual(range);
20+
});
21+
if (!item) {
22+
return `no diagnostic found for ${fsPath} with range ${JSON.stringify(range)}`;
23+
}
24+
return item.contents.map((content) => content.value).join('\n');
25+
},
26+
};
27+
28+
function createRangeFromString(range: string) {
29+
const [start, end] = range.split('-');
30+
const [startLine, startCharacter] = start.split(':').map(Number);
31+
const [endLine, endCharacter] = end.split(':').map(Number);
32+
return new Range(startLine, startCharacter, endLine, endCharacter);
33+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"fileTypes": [],
3+
"injectionSelector": "L:text.html.markdown",
4+
"patterns": [
5+
{
6+
"include": "#type-code-block"
7+
}
8+
],
9+
"repository": {
10+
"type-code-block": {
11+
"begin": "(^|\\G)(\\s*)(\\`{3,}|~{3,})\\s*(?i:(type)(\\s+[^`~]*)?$)",
12+
"name": "markup.fenced_code.block.markdown",
13+
"end": "(^|\\G)(\\2|\\s{0,3})(\\3)\\s*$",
14+
"beginCaptures": {
15+
"3": {
16+
"name": "punctuation.definition.markdown"
17+
},
18+
"4": {
19+
"name": "fenced_code.block.language.markdown"
20+
},
21+
"5": {
22+
"name": "fenced_code.block.language.attributes.markdown"
23+
}
24+
},
25+
"endCaptures": {
26+
"3": {
27+
"name": "punctuation.definition.markdown"
28+
}
29+
},
30+
"patterns": [
31+
{
32+
"begin": "(^|\\G)(\\s*)(.*)",
33+
"while": "(^|\\G)(?!\\s*([`~]{3,})\\s*$)",
34+
"contentName": "meta.embedded.block.type",
35+
"patterns": [
36+
{
37+
"include": "source.type"
38+
}
39+
]
40+
}
41+
]
42+
}
43+
},
44+
"scopeName": "markdown.type.codeblock"
45+
}

0 commit comments

Comments
 (0)