Skip to content

Commit 689d0e7

Browse files
committed
Merge branch 'main' into fix-typedef-export
2 parents 8021e94 + d0c9e32 commit 689d0e7

File tree

21 files changed

+6329
-523
lines changed

21 files changed

+6329
-523
lines changed

.github/ls-screenshot.png

66 KB
Loading

Herebyfile.mjs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ const { values: options } = parseArgs({
5555
fix: { type: "boolean" },
5656
debug: { type: "boolean" },
5757

58+
insiders: { type: "boolean" },
59+
5860
race: { type: "boolean", default: parseEnvBoolean("RACE") },
5961
noembed: { type: "boolean", default: parseEnvBoolean("NOEMBED") },
6062
concurrentTestPrograms: { type: "boolean", default: parseEnvBoolean("CONCURRENT_TEST_PROGRAMS") },
@@ -348,6 +350,17 @@ export const testAll = task({
348350
},
349351
});
350352

353+
export const installExtension = task({
354+
name: "install-extension",
355+
run: async () => {
356+
await $({ cwd: path.join(__dirname, "_extension") })`npm run package`;
357+
await $({ cwd: path.join(__dirname, "_extension") })`${options.insiders ? "code-insiders" : "code"} --install-extension typescript-lsp.vsix`;
358+
console.log(pc.yellowBright("\nExtension installed. ") + "Add the following to your workspace or user settings.json:\n");
359+
console.log(pc.whiteBright(` "typescript-go.executablePath": "${path.join(__dirname, "built", "local", process.platform === "win32" ? "tsgo.exe" : "tsgo")}"\n`));
360+
console.log("Select 'TypeScript: Use TypeScript Go (Experimental)' in the command palette to enable the extension and disable built-in TypeScript support.\n");
361+
},
362+
});
363+
351364
const customLinterPath = "./_tools/custom-gcl";
352365
const customLinterHashPath = customLinterPath + ".hash";
353366

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,18 @@ This is mainly a testing entry point; for higher fidelity with regular `tsc`, ru
4545

4646
### Running LSP Prototype
4747

48-
To try the prototype LSP experience:
48+
* Run `hereby build` to build the LSP server
49+
* Run `hereby install-extension` to build and install the VS Code extension. (Use `--insiders` to target `code-insiders` instead of `code`.)
50+
* Copy the `"typescript-go.executablePath"` setting printed by `hereby install-extension` to your VS Code settings.
51+
* Select "TypeScript: Use TypeScript Go (Experimental)" from the VS Code command palette (or set `"typescript.experimental.useTsgo"` in your VS Code settings).
52+
53+
Alternatively, to debug and run the VS Code extension without installing it globally:
4954

5055
* Run VS Code in the repo workspace (`code .`)
5156
* Copy `.vscode/launch.template.json` to `.vscode/launch.json`
5257
* <kbd>F5</kbd> (or `Debug: Start Debugging` from the command palette)
5358

54-
This will launch a new VS Code instance which uses the Corsa LS as the backend. If correctly set up, you should see "typescript-go" as an option in the Output pane:
59+
This will launch a new VS Code instance which uses the Corsa LS as the backend. If correctly set up, you should see "tsgo" in the status bar when a TypeScript or JavaScript file is open:
5560

5661
![LSP Prototype Screenshot](.github/ls-screenshot.png)
5762

_extension/package.json

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
"private": true,
88
"version": "0.0.0",
99
"type": "commonjs",
10+
"repository": {
11+
"type": "git",
12+
"url": "https://github.com/microsoft/typescript-go"
13+
},
1014
"engines": {
1115
"vscode": "^1.91.0"
1216
},
@@ -34,33 +38,48 @@
3438
"typescript-go.pprofDir": {
3539
"type": "string",
3640
"description": "Directory to write pprof profiles to."
41+
},
42+
"typescript-go.executablePath": {
43+
"type": "string",
44+
"description": "Path to the tsgo binary. If not specified, the extension will look for it in the default location."
3745
}
3846
}
3947
}
4048
],
4149
"commands": [
4250
{
4351
"command": "typescript-go.restart",
44-
"title": "TypeScript Go: Restart Language Server"
52+
"title": "TypeScript Go: Restart Language Server",
53+
"enablement": "typescript-go.serverRunning"
4554
}
4655
],
4756
"menus": {
4857
"commandPalette": [
4958
{
50-
"command": "typescript-go.restart"
59+
"command": "typescript-go.restart",
60+
"when": "typescript-go.serverRunning"
5161
}
5262
]
5363
}
5464
},
5565
"main": "./dist/extension.js",
66+
"files": [
67+
"dist"
68+
],
5669
"scripts": {
5770
"build": "tsc",
58-
"watch": "tsc --watch"
71+
"watch": "tsc --watch",
72+
"build:prod": "esbuild src/extension.ts --bundle --external:vscode --platform=node --format=cjs --outfile=dist/extension.js --minify",
73+
"package": "vsce package --skip-license --no-dependencies --out typescript-lsp.vsix",
74+
"install-extension": "code --install-extension typescript-lsp.vsix",
75+
"vscode:prepublish": "npm run build:prod"
5976
},
6077
"dependencies": {
6178
"vscode-languageclient": "^9.0.1"
6279
},
6380
"devDependencies": {
64-
"@types/vscode": "^1.96.0"
81+
"@types/vscode": "^1.91.0",
82+
"@vscode/vsce": "^3.3.2",
83+
"esbuild": "^0.25.2"
6584
}
6685
}

_extension/src/extension.ts

Lines changed: 89 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,30 @@ import {
1111
} from "vscode-languageclient/node";
1212

1313
let client: LanguageClient;
14+
let statusBarItem: vscode.StatusBarItem;
15+
16+
const BUILTIN_TS_EXTENSION_ID = "vscode.typescript-language-features";
1417

1518
export function activate(context: vscode.ExtensionContext) {
16-
context.subscriptions.push(vscode.commands.registerCommand("typescript-go.restart", () => {
17-
client.restart();
18-
}));
19+
const tsExtension = vscode.extensions.getExtension(BUILTIN_TS_EXTENSION_ID);
20+
if (tsExtension?.isActive && !vscode.workspace.getConfiguration("typescript").get<boolean>("experimental.useTsgo")) {
21+
return;
22+
}
1923

2024
const output = vscode.window.createOutputChannel("typescript-go", "log");
2125
const traceOutput = vscode.window.createOutputChannel("typescript-go (LSP)");
2226

23-
const exe = context.asAbsolutePath(
27+
setupStatusBar(context);
28+
registerCommands(context, output, traceOutput);
29+
30+
const config = vscode.workspace.getConfiguration("typescript-go");
31+
32+
const exe = config.get<string>("executablePath") || context.asAbsolutePath(
2433
path.join("../", "built", "local", `tsgo${process.platform === "win32" ? ".exe" : ""}`),
2534
);
2635

2736
output.appendLine(`Resolved to ${exe}`);
2837

29-
const config = vscode.workspace.getConfiguration("typescript-go");
30-
3138
// Get pprofDir
3239
const pprofDir = config.get<string>("pprofDir");
3340
const pprofArgs = pprofDir ? ["-pprofDir", pprofDir] : [];
@@ -115,13 +122,86 @@ export function activate(context: vscode.ExtensionContext) {
115122

116123
output.appendLine(`Starting language server...`);
117124
client.start();
125+
vscode.commands.executeCommand("setContext", "typescript-go.serverRunning", true);
126+
}
127+
128+
/**
129+
* Sets up the status bar item for TypeScript Go
130+
* @param context Extension context
131+
*/
132+
function setupStatusBar(context: vscode.ExtensionContext): void {
133+
statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);
134+
statusBarItem.text = "$(beaker) tsgo";
135+
statusBarItem.tooltip = "TypeScript Go Language Server";
136+
statusBarItem.command = "typescript-go.showMenu";
137+
statusBarItem.backgroundColor = new vscode.ThemeColor("statusBarItem.warningBackground");
138+
statusBarItem.show();
139+
context.subscriptions.push(statusBarItem);
140+
}
141+
142+
/**
143+
* Registers all commands for the extension
144+
* @param context Extension context
145+
*/
146+
function registerCommands(context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel, traceOutputChannel: vscode.OutputChannel): void {
147+
context.subscriptions.push(vscode.commands.registerCommand("typescript-go.restart", async () => {
148+
await client.restart();
149+
}));
150+
151+
context.subscriptions.push(vscode.commands.registerCommand("typescript-go.output.focus", () => {
152+
outputChannel.show();
153+
}));
154+
155+
context.subscriptions.push(vscode.commands.registerCommand("typescript-go.lsp-trace.focus", () => {
156+
traceOutputChannel.show();
157+
}));
158+
159+
context.subscriptions.push(vscode.commands.registerCommand("typescript-go.showMenu", showQuickPickMenu));
118160
}
119161

120-
export function deactivate(): Thenable<void> | undefined {
162+
/**
163+
* Shows the quick pick menu for TypeScript Go options
164+
*/
165+
async function showQuickPickMenu(): Promise<void> {
166+
const selected = await vscode.window.showQuickPick([
167+
{ label: "$(refresh) Restart Server", description: "Restart the TypeScript Go language server" },
168+
{ label: "$(output) Show TS Server Log", description: "Show the TypeScript Go server log" },
169+
{ label: "$(debug-console) Show LSP Messages", description: "Show the LSP communication trace" },
170+
{ label: "$(stop-circle) Disable TypeScript Go", description: "Switch back to the built-in TypeScript extension" },
171+
], {
172+
placeHolder: "TypeScript Go Options",
173+
});
174+
175+
if (selected) {
176+
if (selected.label.includes("Restart Server")) {
177+
await vscode.commands.executeCommand("typescript-go.restart");
178+
}
179+
else if (selected.label.includes("Show TS Server Log")) {
180+
await vscode.commands.executeCommand("typescript-go.output.focus");
181+
}
182+
else if (selected.label.includes("Show LSP Messages")) {
183+
await vscode.commands.executeCommand("typescript-go.lsp-trace.focus");
184+
}
185+
else if (selected.label.includes("Disable TypeScript Go")) {
186+
// Fire and forget, because this command will restart the whole extension host
187+
// and awaiting it shows a weird cancellation error.
188+
vscode.commands.executeCommand("typescript.experimental.disableTsgo");
189+
}
190+
}
191+
}
192+
193+
export async function deactivate(): Promise<void> {
194+
// Dispose of status bar item
195+
if (statusBarItem) {
196+
statusBarItem.dispose();
197+
}
198+
121199
if (!client) {
122-
return undefined;
200+
return;
123201
}
124-
return client.stop();
202+
203+
await client.stop();
204+
return vscode.commands.executeCommand("setContext", "typescript-go.serverRunning", false);
125205
}
126206

127207
function getLanguageForUri(uri: vscode.Uri): string | undefined {

cmd/tsgo/main.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"os"
99
"runtime"
10+
"runtime/debug"
1011
"slices"
1112
"strconv"
1213
"strings"
@@ -59,6 +60,7 @@ type cliOptions struct {
5960
listFiles tristateFlag
6061
listFilesOnly tristateFlag
6162
showConfig tristateFlag
63+
version bool
6264
}
6365

6466
devel struct {
@@ -103,6 +105,7 @@ func parseArgs() *cliOptions {
103105
flag.Var(&opts.tsc.listFiles, "listFiles", diagnostics.Print_all_of_the_files_read_during_the_compilation.Format())
104106
flag.Var(&opts.tsc.listFilesOnly, "listFilesOnly", diagnostics.Print_names_of_files_that_are_part_of_the_compilation_and_then_stop_processing.Format())
105107
flag.Var(&opts.tsc.showConfig, "showConfig", diagnostics.Print_the_final_configuration_instead_of_building.Format())
108+
flag.BoolVar(&opts.tsc.version, "version", false, diagnostics.Print_the_compiler_s_version.Format())
106109

107110
flag.BoolVar(&opts.devel.quiet, "q", false, "Do not print diagnostics.")
108111
flag.BoolVar(&opts.devel.quiet, "quiet", false, "Do not print diagnostics.")
@@ -144,6 +147,21 @@ func runMain() int {
144147
defer profileSession.Stop()
145148
}
146149

150+
if opts.tsc.version {
151+
// Get build info to extract the commit SHA
152+
buildInfo, _ := debug.ReadBuildInfo()
153+
version := core.Version
154+
for _, setting := range buildInfo.Settings {
155+
if setting.Key == "vcs.revision" {
156+
version += "-" + setting.Value
157+
break
158+
}
159+
}
160+
161+
fmt.Println(diagnostics.Version_0.Format(version))
162+
return 0
163+
}
164+
147165
startTime := time.Now()
148166

149167
currentDirectory, err := os.Getwd()

internal/ast/utilities.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2792,3 +2792,12 @@ func IsClassMemberModifier(token Kind) bool {
27922792
func IsParameterPropertyModifier(kind Kind) bool {
27932793
return ModifierToFlag(kind)&ModifierFlagsParameterPropertyModifier != 0
27942794
}
2795+
2796+
func ForEachChildAndJSDoc(node *Node, sourceFile *SourceFile, v Visitor) bool {
2797+
if node.Flags&NodeFlagsHasJSDoc != 0 {
2798+
if visitNodes(v, node.JSDoc(sourceFile)) {
2799+
return true
2800+
}
2801+
}
2802+
return node.ForEachChild(v)
2803+
}

internal/astnav/tokens.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -418,8 +418,8 @@ func findRightmostValidToken(endPos int, sourceFile *ast.SourceFile, containingN
418418
if position == -1 {
419419
position = containingNode.End()
420420
}
421-
var find func(n *ast.Node) *ast.Node
422-
find = func(n *ast.Node) *ast.Node {
421+
var find func(n *ast.Node, endPos int) *ast.Node
422+
find = func(n *ast.Node, endPos int) *ast.Node {
423423
if n == nil {
424424
return nil
425425
}
@@ -555,10 +555,13 @@ func findRightmostValidToken(endPos int, sourceFile *ast.SourceFile, containingN
555555
return n
556556
}
557557
// Case 1: recur on rightmostValidNode.
558-
return find(rightmostValidNode)
558+
if rightmostValidNode != nil {
559+
endPos = rightmostValidNode.End()
560+
}
561+
return find(rightmostValidNode, endPos)
559562
}
560563

561-
return find(containingNode)
564+
return find(containingNode, endPos)
562565
}
563566

564567
// !!!

internal/astnav/tokens_test.go

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,15 @@ func TestFindPrecedingToken(t *testing.T) {
354354

355355
func TestUnitFindPrecedingToken(t *testing.T) {
356356
t.Parallel()
357-
fileContent := `import {
357+
testCases := []struct {
358+
name string
359+
fileContent string
360+
position int
361+
expectedKind ast.Kind
362+
}{
363+
{
364+
name: "after dot in jsdoc",
365+
fileContent: `import {
358366
CharacterCodes,
359367
compareStringsCaseInsensitive,
360368
compareStringsCaseSensitive,
@@ -399,11 +407,25 @@ backslashRegExp.
399407
*/
400408
export function isAnyDirectorySeparator(charCode: number): boolean {
401409
return charCode === CharacterCodes.slash || charCode === CharacterCodes.backslash;
402-
}`
403-
file := parser.ParseSourceFile("/file.ts", "/file.ts", fileContent, core.ScriptTargetLatest, scanner.JSDocParsingModeParseAll)
404-
position := 839
405-
token := astnav.FindPrecedingToken(file, position)
406-
assert.Equal(t, token.Kind, ast.KindDotToken)
410+
}`,
411+
position: 839,
412+
expectedKind: ast.KindDotToken,
413+
},
414+
{
415+
name: "after comma in parameter list",
416+
fileContent: `takesCb((n, s, ))`,
417+
position: 15,
418+
expectedKind: ast.KindCommaToken,
419+
},
420+
}
421+
for _, testCase := range testCases {
422+
t.Run(testCase.name, func(t *testing.T) {
423+
t.Parallel()
424+
file := parser.ParseSourceFile("/file.ts", "/file.ts", testCase.fileContent, core.ScriptTargetLatest, scanner.JSDocParsingModeParseAll)
425+
token := astnav.FindPrecedingToken(file, testCase.position)
426+
assert.Equal(t, token.Kind, testCase.expectedKind)
427+
})
428+
}
407429
}
408430

409431
func tsFindPrecedingTokens(t *testing.T, fileText string, positions []int) []*tokenInfo {

internal/execute/export_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ func CommandLineTestWatch(sys System, cb cbType, commandLineArgs []string) (*tso
1717
return parsedCommandLine, w
1818
}
1919

20+
func StartForTest(w *watcher) {
21+
// this function should perform any initializations before w.doCycle() in `start(watcher)`
22+
w.initialize()
23+
}
24+
2025
func RunWatchCycle(w *watcher) {
2126
// this function should perform the same stuff as w.doCycle() without printing time-related output
2227
if w.hasErrorsInTsConfig() {

0 commit comments

Comments
 (0)