✨ feat(vscode): 只有从「未提交的更改」点开的预览才对比上一次提交
This commit is contained in:
1 parent
4ec2b9d5d6
commit
3c7513b7cd
18 files changed
+366
-143
No files matched your search
@@ -1,5 +1,5 @@
|
||||
import type { WorkspaceFolder } from 'vscode'
|
||||
import type { ChangedSection, FileNode, FileTreeDecorator, TreeNode } from './fileTree'
|
||||
import type { ChangedSection, FileNode, FileOpenCommand, FileTreeDecorator, TreeNode } from './fileTree'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { Uri } from 'vscode'
|
||||
import { buildChangedTree } from './changedTree'
|
||||
@@ -68,6 +68,7 @@ vi.mock('vscode', async () => {
|
||||
const FILE = 1
|
||||
const DIR = 2
|
||||
const decorator = { onDidChange: () => ({ dispose: () => {} }), decorate: () => {} } as unknown as FileTreeDecorator
|
||||
const OPEN: FileOpenCommand = { command: 'open', from: 'other' }
|
||||
let folder: WorkspaceFolder
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -108,7 +109,7 @@ function topOf(files: FileTreeProvider, node: TreeNode): TreeNode {
|
||||
|
||||
describe('工作区文件树:按 uri 找到那一行、一层层找上一层', () => {
|
||||
it('多层目录:getParent 交回 getChildren 给过的同一个对象,根的上一层没有', async () => {
|
||||
const files = new FileTreeProvider('open', decorator)
|
||||
const files = new FileTreeProvider(OPEN, decorator)
|
||||
const [root] = await files.getChildren()
|
||||
const backend = await childNamed(files, root, 'backend')
|
||||
const src = await childNamed(files, backend, 'src')
|
||||
@@ -123,7 +124,7 @@ describe('工作区文件树:按 uri 找到那一行、一层层找上一层',
|
||||
})
|
||||
|
||||
it('nodeOf 一层层读目录找到文件,就是树上摆的那一行', async () => {
|
||||
const files = new FileTreeProvider('open', decorator)
|
||||
const files = new FileTreeProvider(OPEN, decorator)
|
||||
const found = await files.nodeOf(Uri.file('/ws/backend/src/luffy/visibility.py'))
|
||||
|
||||
expect(found?.isDirectory).toBe(false)
|
||||
@@ -133,7 +134,7 @@ describe('工作区文件树:按 uri 找到那一行、一层层找上一层',
|
||||
})
|
||||
|
||||
it('目录也能找到', async () => {
|
||||
const files = new FileTreeProvider('open', decorator)
|
||||
const files = new FileTreeProvider(OPEN, decorator)
|
||||
const found = await files.nodeOf(Uri.file('/ws/backend/src'))
|
||||
|
||||
expect(found?.isDirectory).toBe(true)
|
||||
@@ -143,7 +144,7 @@ describe('工作区文件树:按 uri 找到那一行、一层层找上一层',
|
||||
|
||||
it('树上没有的找不到:工作区外、被 files.exclude 藏了的目录里、盘上没有的', async () => {
|
||||
disk.exclude = { '**/dist': true }
|
||||
const files = new FileTreeProvider('open', decorator)
|
||||
const files = new FileTreeProvider(OPEN, decorator)
|
||||
|
||||
expect(await files.nodeOf(Uri.file('/elsewhere/a.py'))).toBeUndefined()
|
||||
expect(await files.nodeOf(Uri.file('/ws/dist/bundle.js'))).toBeUndefined()
|
||||
@@ -163,7 +164,7 @@ describe('只摆改动的文件时:在树上就找到,不在就没有', () =
|
||||
}
|
||||
|
||||
it('找到那个分组里的那一行,上一层一直走到分组行', async () => {
|
||||
const files = new FileTreeProvider('open', decorator)
|
||||
const files = new FileTreeProvider(OPEN, decorator)
|
||||
const { staged, unstaged } = sections()
|
||||
files.show({ kind: 'changes', view: [staged, unstaged] })
|
||||
const found = await files.nodeOf(Uri.file('/ws/backend/src/luffy/visibility.py'))
|
||||
@@ -176,7 +177,7 @@ describe('只摆改动的文件时:在树上就找到,不在就没有', () =
|
||||
})
|
||||
|
||||
it('没摆出来的文件、目录找不到', async () => {
|
||||
const files = new FileTreeProvider('open', decorator)
|
||||
const files = new FileTreeProvider(OPEN, decorator)
|
||||
const { staged, unstaged } = sections()
|
||||
files.show({ kind: 'changes', view: [staged, unstaged] })
|
||||
|
||||
@@ -186,7 +187,7 @@ describe('只摆改动的文件时:在树上就找到,不在就没有', () =
|
||||
})
|
||||
|
||||
it('同一个文件摆在两个分组里时取最先摆的', async () => {
|
||||
const files = new FileTreeProvider('open', decorator)
|
||||
const files = new FileTreeProvider(OPEN, decorator)
|
||||
const { staged, unstaged } = sections()
|
||||
files.show({ kind: 'changes', view: [staged, unstaged] })
|
||||
|
||||
@@ -194,7 +195,7 @@ describe('只摆改动的文件时:在树上就找到,不在就没有', () =
|
||||
})
|
||||
|
||||
it('分组没有名字时根就在最上面一层,根的上一层没有', async () => {
|
||||
const files = new FileTreeProvider('open', decorator)
|
||||
const files = new FileTreeProvider(OPEN, decorator)
|
||||
files.show({ kind: 'changes', view: [{ roots: [{ folder, tree: buildChangedTree([{ path: 'backend/src/gate.py', change: 'A' }]) }] }] })
|
||||
const found = await files.nodeOf(Uri.file('/ws/backend/src/gate.py'))
|
||||
const [root] = await files.getChildren()
|
||||
@@ -208,7 +209,7 @@ describe('收藏栏:一个目录下的完整文件树', () => {
|
||||
it('直接摆目录下的各项(不套一层根),规则同工作区文件树:目录在前、files.exclude 藏掉的不摆', async () => {
|
||||
disk.dirs.set('/ws/backend', [['src', DIR], ['main.py', FILE], ['build', DIR]])
|
||||
disk.exclude = { '**/build': true }
|
||||
const files = new FileTreeProvider('open', decorator, { kind: 'directory', uri: Uri.file('/ws/backend') })
|
||||
const files = new FileTreeProvider(OPEN, decorator, { kind: 'directory', uri: Uri.file('/ws/backend') })
|
||||
const top = await files.getChildren()
|
||||
|
||||
expect(top.map(node => !isGroupNode(node) && [node.uri.path, node.isDirectory, node.isRoot])).toEqual([
|
||||
@@ -220,7 +221,7 @@ describe('收藏栏:一个目录下的完整文件树', () => {
|
||||
})
|
||||
|
||||
it('最上面一层没有上一层;往下找得到,上一层走到收藏的目录下面为止', async () => {
|
||||
const files = new FileTreeProvider('open', decorator, { kind: 'directory', uri: Uri.file('/ws/backend') })
|
||||
const files = new FileTreeProvider(OPEN, decorator, { kind: 'directory', uri: Uri.file('/ws/backend') })
|
||||
const found = await files.nodeOf(Uri.file('/ws/backend/src/luffy/visibility.py'))
|
||||
|
||||
expect(chainUp(files, found)).toEqual(['/ws/backend/src/luffy/visibility.py', '/ws/backend/src/luffy', '/ws/backend/src'])
|
||||
@@ -228,7 +229,7 @@ describe('收藏栏:一个目录下的完整文件树', () => {
|
||||
})
|
||||
|
||||
it('目录外面的找不到;目录被删了时树是空的', async () => {
|
||||
const files = new FileTreeProvider('open', decorator, { kind: 'directory', uri: Uri.file('/ws/backend') })
|
||||
const files = new FileTreeProvider(OPEN, decorator, { kind: 'directory', uri: Uri.file('/ws/backend') })
|
||||
|
||||
expect(await files.nodeOf(Uri.file('/ws/README.md'))).toBeUndefined()
|
||||
expect(await files.nodeOf(Uri.file('/ws/backend'))).toBeUndefined()
|
||||
@@ -239,10 +240,32 @@ describe('收藏栏:一个目录下的完整文件树', () => {
|
||||
|
||||
it('目录不在任何工作区文件夹里(文件夹被移出工作区):拿目录自己当根', async () => {
|
||||
disk.folders = []
|
||||
const files = new FileTreeProvider('open', decorator, { kind: 'directory', uri: Uri.file('/ws/backend') })
|
||||
const files = new FileTreeProvider(OPEN, decorator, { kind: 'directory', uri: Uri.file('/ws/backend') })
|
||||
const [src] = await files.getChildren()
|
||||
|
||||
expect(!isGroupNode(src) && src.folder.uri.path).toBe('/ws/backend')
|
||||
expect(files.getParent(src)).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('点文件打开', () => {
|
||||
it('命令带上文件和这一栏的 from:「未提交的更改」那一栏的是 uncommitted;删掉的文件不挂命令', async () => {
|
||||
const files = new FileTreeProvider({ command: 'open', from: 'uncommitted' }, decorator)
|
||||
files.show({ kind: 'changes', view: [{ label: '更改', roots: [{ folder, tree: buildChangedTree([
|
||||
{ path: 'backend/src/gate.py', change: 'M' },
|
||||
{ path: 'README.md', change: 'D' },
|
||||
]) }] }] })
|
||||
const gate = await files.nodeOf(Uri.file('/ws/backend/src/gate.py'))
|
||||
const readme = await files.nodeOf(Uri.file('/ws/README.md'))
|
||||
|
||||
expect(files.getTreeItem(gate!).command).toEqual({ command: 'open', title: '打开', arguments: [gate!.uri, 'uncommitted'] })
|
||||
expect(files.getTreeItem(readme!).command).toBeUndefined()
|
||||
})
|
||||
|
||||
it('别的栏的是 other', async () => {
|
||||
const files = new FileTreeProvider(OPEN, decorator)
|
||||
const readme = await files.nodeOf(Uri.file('/ws/README.md'))
|
||||
|
||||
expect(files.getTreeItem(readme!).command?.arguments).toEqual([readme!.uri, 'other'])
|
||||
})
|
||||
})
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Event, TreeDataProvider, WorkspaceFolder } from 'vscode'
|
||||
import type { OpenedFrom } from '../diff/diffBase'
|
||||
import type { ChangedTree, FileChange } from './changedTree'
|
||||
import type { ExcludeSetting } from './fileEntries'
|
||||
import * as path from 'node:path'
|
||||
@@ -70,6 +71,12 @@ export interface FileTreeDecorator {
|
||||
decorate: (item: TreeItem, node: FileNode) => void
|
||||
}
|
||||
|
||||
/** 点文件时执行的命令:参数是文件 Uri 和 `from`,预览按它知道这个标签页是从哪儿打开的(见 [OpenedFrom])。 */
|
||||
export interface FileOpenCommand {
|
||||
command: string
|
||||
from: OpenedFrom
|
||||
}
|
||||
|
||||
/** 批量改文件(git checkout、构建输出)时合并成一次刷新。 */
|
||||
const REFRESH_DELAY_MS = 300
|
||||
|
||||
@@ -92,11 +99,11 @@ export class FileTreeProvider implements TreeDataProvider<TreeNode> {
|
||||
private directoryFolder: WorkspaceFolder | undefined
|
||||
|
||||
/**
|
||||
* @param openCommand 点文件时执行的命令,参数是文件 Uri
|
||||
* @param open 点文件时执行的命令和这一栏的 `from`
|
||||
* @param decorator 给行添信息(文件注释)
|
||||
* @param content 一开始摆什么,默认是工作区文件树
|
||||
*/
|
||||
constructor(private readonly openCommand: string, private readonly decorator: FileTreeDecorator, private content: TreeContent = WORKSPACE_TREE) {
|
||||
constructor(private readonly open: FileOpenCommand, private readonly decorator: FileTreeDecorator, private content: TreeContent = WORKSPACE_TREE) {
|
||||
this.groups = groupsOf(content)
|
||||
// 只关心增删:文件内容变化不影响树的结构
|
||||
const watcher = workspace.createFileSystemWatcher('**/*', false, true, false)
|
||||
@@ -190,7 +197,7 @@ export class FileTreeProvider implements TreeDataProvider<TreeNode> {
|
||||
if (node.change === 'D')
|
||||
item.tooltip = DELETED_TOOLTIP // 删掉的文件没有内容可看,不挂打开的命令
|
||||
else
|
||||
item.command = { command: this.openCommand, title: '打开', arguments: [node.uri] }
|
||||
item.command = { command: this.open.command, title: '打开', arguments: [node.uri, this.open.from] }
|
||||
return item
|
||||
}
|
||||
const expanded = node.isRoot || this.content.kind === 'changes'
|
||||
|
||||
Reference in new issue
Block a user