Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make JS/TS go to configuration commands work on non-file: file systems #183688

Merged
merged 1 commit into from May 30, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -239,7 +239,7 @@ class UpdateImportsOnFileRenameHandler extends Disposable {

for (const rename of renames) {
// Group renames by type (js/ts) and by workspace.
const key = `${this.client.getWorkspaceRootForResource(rename.jsTsFileThatIsBeingMoved)}@@@${doesResourceLookLikeATypeScriptFile(rename.jsTsFileThatIsBeingMoved)}`;
const key = `${this.client.getWorkspaceRootForResource(rename.jsTsFileThatIsBeingMoved)?.fsPath}@@@${doesResourceLookLikeATypeScriptFile(rename.jsTsFileThatIsBeingMoved)}`;
if (!groups.has(key)) {
groups.set(key, new Set());
}
Expand Down
Expand Up @@ -4,10 +4,11 @@
*--------------------------------------------------------------------------------------------*/

import * as vscode from 'vscode';
import { vscodeNotebookCell } from '../configuration/fileSchemes';
import { officeScript, vscodeNotebookCell } from '../configuration/fileSchemes';
import * as languageModeIds from '../configuration/languageIds';
import * as typeConverters from '../typeConverters';
import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService';
import { inMemoryResourcePrefix } from '../typescriptServiceClient';
import { coalesce } from '../utils/arrays';
import { Delayer, setImmediate } from '../utils/async';
import { nulToken } from '../utils/cancellation';
Expand Down Expand Up @@ -200,7 +201,7 @@ class SyncedBuffer {
const args: Proto.OpenRequestArgs = {
file: this.filepath,
fileContent: this.document.getText(),
projectRootPath: this.client.getWorkspaceRootForResource(this.document.uri),
projectRootPath: this.getProjectRootPath(this.document.uri),
};

const scriptKind = mode2ScriptKind(this.document.languageId);
Expand All @@ -219,6 +220,16 @@ class SyncedBuffer {
this.state = BufferState.Open;
}

private getProjectRootPath(resource: vscode.Uri): string | undefined {
const workspaceRoot = this.client.getWorkspaceRootForResource(resource);
if (workspaceRoot) {
const tsRoot = this.client.toTsFilePath(workspaceRoot);
return tsRoot?.startsWith(inMemoryResourcePrefix) ? undefined : tsRoot;
}

return resource.scheme === officeScript ? '/' : undefined;
}

public get resource(): vscode.Uri {
return this.document.uri;
}
Expand Down
13 changes: 6 additions & 7 deletions extensions/typescript-language-features/src/tsconfig.ts
Expand Up @@ -3,7 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as path from 'path';
import * as vscode from 'vscode';
import type * as Proto from './tsServer/protocol/protocol';
import { ITypeScriptServiceClient, ServerResponse } from './typescriptService';
Expand Down Expand Up @@ -87,10 +86,10 @@ function inferredProjectConfigSnippet(

export async function openOrCreateConfig(
projectType: ProjectType,
rootPath: string,
rootPath: vscode.Uri,
configuration: TypeScriptServiceConfiguration,
): Promise<vscode.TextEditor | null> {
const configFile = vscode.Uri.file(path.join(rootPath, projectType === ProjectType.TypeScript ? 'tsconfig.json' : 'jsconfig.json'));
const configFile = vscode.Uri.joinPath(rootPath, projectType === ProjectType.TypeScript ? 'tsconfig.json' : 'jsconfig.json');
const col = vscode.window.activeTextEditor?.viewColumn;
try {
const doc = await vscode.workspace.openTextDocument(configFile);
Expand All @@ -108,11 +107,11 @@ export async function openOrCreateConfig(
export async function openProjectConfigOrPromptToCreate(
projectType: ProjectType,
client: ITypeScriptServiceClient,
rootPath: string,
configFileName: string,
rootPath: vscode.Uri,
configFilePath: string,
): Promise<void> {
if (!isImplicitProjectConfigFile(configFileName)) {
const doc = await vscode.workspace.openTextDocument(configFileName);
if (!isImplicitProjectConfigFile(configFilePath)) {
const doc = await vscode.workspace.openTextDocument(client.toResource(configFilePath));
vscode.window.showTextDocument(doc, vscode.window.activeTextEditor?.viewColumn);
return;
}
Expand Down
Expand Up @@ -156,7 +156,7 @@ export interface ITypeScriptServiceClient {
*/
hasCapabilityForResource(resource: vscode.Uri, capability: ClientCapability): boolean;

getWorkspaceRootForResource(resource: vscode.Uri): string | undefined;
getWorkspaceRootForResource(resource: vscode.Uri): vscode.Uri | undefined;

readonly onTsServerStarted: vscode.Event<{ version: TypeScriptVersion; usedApiVersion: API }>;
readonly onProjectLanguageServiceStateChanged: vscode.Event<Proto.ProjectLanguageServiceStateEventBody>;
Expand Down
Expand Up @@ -91,10 +91,12 @@ namespace ServerState {
export type State = typeof None | Running | Errored;
}

export const emptyAuthority = 'ts-nul-authority';

export const inMemoryResourcePrefix = '^';

export default class TypeScriptServiceClient extends Disposable implements ITypeScriptServiceClient {

private readonly emptyAuthority = 'ts-nul-authority';
private readonly inMemoryResourcePrefix = '^';

private readonly _onReady?: { promise: Promise<void>; resolve: () => void; reject: () => void };
private _configuration: TypeScriptServiceConfiguration;
Expand Down Expand Up @@ -695,9 +697,9 @@ export default class TypeScriptServiceClient extends Disposable implements IType
return resource.fsPath;
}

return (this.isProjectWideIntellisenseOnWebEnabled() ? '' : this.inMemoryResourcePrefix)
return (this.isProjectWideIntellisenseOnWebEnabled() ? '' : inMemoryResourcePrefix)
+ '/' + resource.scheme
+ '/' + (resource.authority || this.emptyAuthority)
+ '/' + (resource.authority || emptyAuthority)
+ (resource.path.startsWith('/') ? resource.path : '/' + resource.path)
+ (resource.fragment ? '#' + resource.fragment : '');
}
Expand Down Expand Up @@ -739,46 +741,36 @@ export default class TypeScriptServiceClient extends Disposable implements IType
}
const parts = filepath.match(/^\/([^\/]+)\/([^\/]*)\/(.+)$/);
if (parts) {
const resource = vscode.Uri.parse(parts[1] + '://' + (parts[2] === this.emptyAuthority ? '' : parts[2]) + '/' + parts[3]);
const resource = vscode.Uri.parse(parts[1] + '://' + (parts[2] === emptyAuthority ? '' : parts[2]) + '/' + parts[3]);
return this.bufferSyncSupport.toVsCodeResource(resource);
}
}

if (filepath.startsWith(this.inMemoryResourcePrefix)) {
if (filepath.startsWith(inMemoryResourcePrefix)) {
const parts = filepath.match(/^\^\/([^\/]+)\/([^\/]*)\/(.+)$/);
if (parts) {
const resource = vscode.Uri.parse(parts[1] + '://' + (parts[2] === this.emptyAuthority ? '' : parts[2]) + '/' + parts[3]);
const resource = vscode.Uri.parse(parts[1] + '://' + (parts[2] === emptyAuthority ? '' : parts[2]) + '/' + parts[3]);
return this.bufferSyncSupport.toVsCodeResource(resource);
}
}
return this.bufferSyncSupport.toResource(filepath);
}

public getWorkspaceRootForResource(resource: vscode.Uri): string | undefined {
public getWorkspaceRootForResource(resource: vscode.Uri): vscode.Uri | undefined {
const roots = vscode.workspace.workspaceFolders ? Array.from(vscode.workspace.workspaceFolders) : undefined;
if (!roots?.length) {
if (resource.scheme === fileSchemes.officeScript) {
return '/';
}
return undefined;
}

let tsRootPath: string | undefined;
for (const root of roots.sort((a, b) => a.uri.fsPath.length - b.uri.fsPath.length)) {
if (root.uri.scheme === resource.scheme && root.uri.authority === resource.authority) {
if (resource.fsPath.startsWith(root.uri.fsPath + path.sep)) {
tsRootPath = this.toTsFilePath(root.uri);
break;
return root.uri;
}
}
}

tsRootPath ??= this.toTsFilePath(roots[0].uri);
if (!tsRootPath || tsRootPath.startsWith(this.inMemoryResourcePrefix)) {
return undefined;
}

return tsRootPath;
return undefined;
}

public execute(command: keyof TypeScriptRequests, args: any, token: vscode.CancellationToken, config?: ExecConfig): Promise<ServerResponse.Response<Proto.Response>> {
Expand Down
Expand Up @@ -62,18 +62,18 @@ export class IntellisenseStatus extends Disposable {

commandManager.register({
id: this.openOpenConfigCommandId,
execute: async (rootPath: string, projectType: ProjectType) => {
execute: async (root: vscode.Uri, projectType: ProjectType) => {
if (this._state.type === IntellisenseState.Type.Resolved) {
await openProjectConfigOrPromptToCreate(projectType, this._client, rootPath, this._state.configFile);
await openProjectConfigOrPromptToCreate(projectType, this._client, root, this._state.configFile);
} else if (this._state.type === IntellisenseState.Type.Pending) {
await openProjectConfigForFile(projectType, this._client, this._state.resource);
}
},
});
commandManager.register({
id: this.createOrOpenConfigCommandId,
execute: async (rootPath: string, projectType: ProjectType) => {
await openOrCreateConfig(projectType, rootPath, this._client.configuration);
execute: async (root: vscode.Uri, projectType: ProjectType) => {
await openOrCreateConfig(projectType, root, this._client.configuration);
},
});

Expand Down