Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/core-browser/src/common/common.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export namespace FILE_COMMANDS {
export const COPY_RELATIVE_PATH: Command = {
id: 'filetree.copy.relativepath',
category: CATEGORY,
label: '%file.copy.relativepath%',
};

export const COPY_FILE: Command = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
IApplicationService,
IClipboardService,
IContextKeyService,
OS,
OperatingSystem,
PreferenceService,
QuickOpenService,
} from '@opensumi/ide-core-browser';
Expand All @@ -15,6 +15,7 @@ import { EXPLORER_CONTAINER_ID } from '@opensumi/ide-explorer/lib/browser/explor
import { IFileServiceClient } from '@opensumi/ide-file-service';
import { IFileTreeAPI, IFileTreeService } from '@opensumi/ide-file-tree-next';
import { FileTreeContribution } from '@opensumi/ide-file-tree-next/lib/browser/file-tree-contribution';
import { FileTreeModelService } from '@opensumi/ide-file-tree-next/lib/browser/services/file-tree-model.service';
import { IMainLayoutService, IViewsRegistry } from '@opensumi/ide-main-layout';
import { ViewsRegistry } from '@opensumi/ide-main-layout/lib/browser/views-registry';
import { IDialogService, IMessageService, IWindowDialogService } from '@opensumi/ide-overlay';
Expand All @@ -26,6 +27,8 @@ import { MockQuickOpenService } from '../../../quick-open/src/common/mocks/quick

describe('FileTreeContribution', () => {
let mockInjector: MockInjector;
let mockClipboardService;
let mockFileTreeModelService;
const tabbarHandlerMap = new Map();

const mockMainLayoutService = {
Expand All @@ -38,6 +41,7 @@ describe('FileTreeContribution', () => {
updateViewTitle: jest.fn(),
onActivate: jest.fn(),
onInActivate: jest.fn(),
isActivated: jest.fn(() => true),
};
tabbarHandlerMap.set(name, handler);
return handler;
Expand Down Expand Up @@ -72,6 +76,22 @@ describe('FileTreeContribution', () => {

beforeEach(() => {
mockInjector = createBrowserInjector([]);
mockClipboardService = {
writeText: jest.fn(),
};
mockFileTreeModelService = {
selectedFiles: [],
focusedFile: undefined,
contextMenuFile: undefined,
whenReady: Promise.resolve(),
contextKey: {
explorerViewletVisibleContext: {
set: jest.fn(),
},
},
performLocationOnHandleShow: jest.fn(),
handleTreeBlur: jest.fn(),
};

mockInjector.overrideProviders(
{
Expand Down Expand Up @@ -112,7 +132,11 @@ describe('FileTreeContribution', () => {
},
{
token: IClipboardService,
useValue: {},
useValue: mockClipboardService,
},
{
token: FileTreeModelService,
useValue: mockFileTreeModelService,
},
{
token: IDialogService,
Expand All @@ -137,7 +161,8 @@ describe('FileTreeContribution', () => {
{
token: IApplicationService,
useValue: {
getBackendOS: () => Promise.resolve(OS.type()),
backendOS: Promise.resolve(OperatingSystem.Linux),
getBackendOS: () => Promise.resolve(OperatingSystem.Linux),
},
},
Comment thread
coderabbitai[bot] marked this conversation as resolved.
);
Expand Down Expand Up @@ -205,4 +230,42 @@ describe('FileTreeContribution', () => {
expect(register).toHaveBeenCalled();
});
});

describe('copy path commands', () => {
const registerCommands = () => {
const contribution = mockInjector.get(FileTreeContribution);
const commands = new Map<string, any>();
contribution.registerCommands({
registerCommand: jest.fn((command, handler) => {
commands.set(command.id, { command, ...handler });
}),
} as any);
return commands;
};

it('uses the selected explorer file when copy path is triggered from a shortcut', async () => {
mockFileTreeModelService.selectedFiles = [{ uri: URI.file('/userhome/test.ts') }];
const commands = registerCommands();

await commands.get('filetree.copy.path').execute();

expect(mockClipboardService.writeText).toHaveBeenCalledWith('/userhome/test.ts');
});

it('uses the selected explorer file when copy relative path is triggered from a shortcut', async () => {
mockFileTreeModelService.selectedFiles = [{ uri: URI.file('/userhome/test.ts') }];
const commands = registerCommands();

await commands.get('filetree.copy.relativepath').execute();

expect(mockClipboardService.writeText).toHaveBeenCalledWith('test.ts');
});

it('exposes labels for copy path commands so users can configure shortcuts', () => {
const commands = registerCommands();

expect(commands.get('filetree.copy.path').command.label).toBe('%file.copy.path%');
expect(commands.get('filetree.copy.relativepath').command.label).toBe('%file.copy.relativepath%');
});
});
});
40 changes: 26 additions & 14 deletions packages/file-tree-next/src/browser/file-tree-contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,16 @@ export class FileTreeContribution
return resourceTitle;
}

private getExplorerTargetUri(uri?: URI): URI | undefined {
return (
uri ||
this.fileTreeModelService.activeUri ||
this.fileTreeModelService.focusedFile?.uri ||
this.fileTreeModelService.selectedFiles?.[0]?.uri ||
this.fileTreeModelService.contextMenuFile?.uri
);
}

private revealFile(locationUri: URI) {
if (locationUri) {
if (this.isRendered) {
Expand Down Expand Up @@ -749,13 +759,14 @@ export class FileTreeContribution

commands.registerCommand<ExplorerContextCallback>(FILE_COMMANDS.COPY_PATH, {
execute: async (uri) => {
if (!uri) {
const targetUri = this.getExplorerTargetUri(uri);
if (!targetUri) {
return;
}
const copyUri: URI = uri;
const copyUri: URI = targetUri;
let uriPath = copyUri.path.toString();
if (uri.scheme === DIFF_SCHEME) {
const query = uri.getParsedQuery();
if (targetUri.scheme === DIFF_SCHEME) {
const query = targetUri.getParsedQuery();
uriPath = new URI(query.modified).path.toString();
}
let pathStr: string = decodeURIComponent(uriPath);
Expand All @@ -770,14 +781,15 @@ export class FileTreeContribution

commands.registerCommand<ExplorerContextCallback>(FILE_COMMANDS.COPY_RELATIVE_PATH, {
execute: async (uri) => {
if (!uri) {
let targetUri = this.getExplorerTargetUri(uri);
if (!targetUri) {
return;
}
if (uri.scheme === DIFF_SCHEME) {
const query = uri.getParsedQuery();
uri = new URI(query.modified).withScheme('file');
if (targetUri.scheme === DIFF_SCHEME) {
const query = targetUri.getParsedQuery();
targetUri = new URI(query.modified).withScheme('file');
}
const node = this.fileTreeService.getNodeByPathOrUri(uri);
const node = this.fileTreeService.getNodeByPathOrUri(targetUri);
if (node) {
if (node.filestat.isInSymbolicDirectory) {
// 软链接文件需要通过直接通过文件树 Path 获取
Expand All @@ -789,20 +801,20 @@ export class FileTreeContribution
// 多工作区额外处理
for (const root of await this.workspaceService.roots) {
rootUri = new URI(root.uri);
if (rootUri.isEqual(uri)) {
if (rootUri.isEqual(targetUri)) {
return await this.clipboardService.writeText('./');
}
if (rootUri.isEqualOrParent(uri)) {
return await this.clipboardService.writeText(decodeURIComponent(rootUri.relative(uri)!.toString()));
if (rootUri.isEqualOrParent(targetUri)) {
return await this.clipboardService.writeText(decodeURIComponent(rootUri.relative(targetUri)!.toString()));
}
}
} else {
if (this.workspaceService.workspace) {
rootUri = new URI(this.workspaceService.workspace.uri);
if (rootUri.isEqual(uri)) {
if (rootUri.isEqual(targetUri)) {
return await this.clipboardService.writeText('./');
}
return await this.clipboardService.writeText(decodeURIComponent(rootUri.relative(uri)!.toString()));
return await this.clipboardService.writeText(decodeURIComponent(rootUri.relative(targetUri)!.toString()));
}
}
},
Expand Down
Loading