Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
vscode
提交
26ebe97d
V
vscode
项目概览
xxadev
/
vscode
与 Fork 源项目一致
从无法访问的项目Fork
通知
2
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
V
vscode
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
26ebe97d
编写于
10月 03, 2018
作者:
J
Joao Moreno
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
data tree: first steps
上级
90a6b23b
变更
5
隐藏空白更改
内联
并排
Showing
5 changed file
with
283 addition
and
51 deletion
+283
-51
src/vs/base/browser/ui/tree/abstractTree.ts
src/vs/base/browser/ui/tree/abstractTree.ts
+9
-9
src/vs/base/browser/ui/tree/dataTree.ts
src/vs/base/browser/ui/tree/dataTree.ts
+132
-0
src/vs/base/browser/ui/tree/objectTree.ts
src/vs/base/browser/ui/tree/objectTree.ts
+1
-1
test/tree/public/index.html
test/tree/public/index.html
+111
-40
test/tree/server.js
test/tree/server.js
+30
-1
未找到文件。
src/vs/base/browser/ui/tree/abstractTree.ts
浏览文件 @
26ebe97d
...
...
@@ -15,13 +15,13 @@ import { ITreeModel, ITreeNode } from 'vs/base/browser/ui/tree/tree';
import
{
ISpliceable
}
from
'
vs/base/common/sequence
'
;
import
{
IIndexTreeModelOptions
}
from
'
vs/base/browser/ui/tree/indexTreeModel
'
;
function
toTreeListOptions
<
T
>
(
options
?:
IListOptions
<
T
>
):
IListOptions
<
ITreeNode
<
T
,
any
>
>
{
export
function
createComposedTreeListOptions
<
T
,
N
extends
{
element
:
T
}
>
(
options
?:
IListOptions
<
T
>
):
IListOptions
<
N
>
{
if
(
!
options
)
{
return
undefined
;
}
let
identityProvider
:
IIdentityProvider
<
ITreeNode
<
T
,
any
>
>
|
undefined
=
undefined
;
let
multipleSelectionController
:
IMultipleSelectionController
<
ITreeNode
<
T
,
any
>
>
|
undefined
=
undefined
;
let
identityProvider
:
IIdentityProvider
<
N
>
|
undefined
=
undefined
;
let
multipleSelectionController
:
IMultipleSelectionController
<
N
>
|
undefined
=
undefined
;
if
(
options
.
identityProvider
)
{
identityProvider
=
el
=>
options
.
identityProvider
(
el
.
element
);
...
...
@@ -45,15 +45,15 @@ function toTreeListOptions<T>(options?: IListOptions<T>): IListOptions<ITreeNode
};
}
class
TreeDelegate
<
T
>
implements
IVirtualDelegate
<
ITreeNode
<
T
,
any
>
>
{
export
class
ComposedTreeDelegate
<
T
,
N
extends
{
element
:
T
}
>
implements
IVirtualDelegate
<
N
>
{
constructor
(
private
delegate
:
IVirtualDelegate
<
T
>
)
{
}
getHeight
(
element
:
ITreeNode
<
T
,
any
>
):
number
{
getHeight
(
element
:
N
):
number
{
return
this
.
delegate
.
getHeight
(
element
.
element
);
}
getTemplateId
(
element
:
ITreeNode
<
T
,
any
>
):
string
{
getTemplateId
(
element
:
N
):
string
{
return
this
.
delegate
.
getTemplateId
(
element
.
element
);
}
}
...
...
@@ -149,13 +149,13 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
renderers
:
IRenderer
<
T
,
any
>
[],
options
?:
ITreeOptions
<
T
,
TFilterData
>
)
{
const
treeDelegate
=
new
TreeDelegate
(
delegate
);
const
treeDelegate
=
new
ComposedTreeDelegate
<
T
,
ITreeNode
<
T
,
TFilterData
>>
(
delegate
);
const
onDidChangeCollapseStateRelay
=
new
Relay
<
ITreeNode
<
T
,
TFilterData
>>
();
const
treeRenderers
=
renderers
.
map
(
r
=>
new
TreeRenderer
(
r
,
onDidChangeCollapseStateRelay
.
event
));
const
treeRenderers
=
renderers
.
map
(
r
=>
new
TreeRenderer
<
T
,
TFilterData
,
any
>
(
r
,
onDidChangeCollapseStateRelay
.
event
));
this
.
disposables
.
push
(...
treeRenderers
);
this
.
view
=
new
List
(
container
,
treeDelegate
,
treeRenderers
,
toTreeListOptions
(
options
));
this
.
view
=
new
List
(
container
,
treeDelegate
,
treeRenderers
,
createComposedTreeListOptions
<
T
,
ITreeNode
<
T
,
TFilterData
>>
(
options
));
this
.
model
=
this
.
createModel
(
this
.
view
,
options
);
onDidChangeCollapseStateRelay
.
input
=
this
.
model
.
onDidChangeCollapseState
;
...
...
src/vs/base/browser/ui/tree/dataTree.ts
0 → 100644
浏览文件 @
26ebe97d
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import
{
ITreeOptions
,
ComposedTreeDelegate
,
createComposedTreeListOptions
}
from
'
vs/base/browser/ui/tree/abstractTree
'
;
import
{
ObjectTree
}
from
'
vs/base/browser/ui/tree/objectTree
'
;
import
{
IVirtualDelegate
,
IRenderer
}
from
'
vs/base/browser/ui/list/list
'
;
import
{
ITreeElement
}
from
'
vs/base/browser/ui/tree/tree
'
;
export
interface
IDataTreeElement
<
T
>
{
readonly
element
:
T
;
readonly
collapsible
?:
boolean
;
readonly
collapsed
?:
boolean
;
}
export
interface
IDataSource
<
T
extends
NonNullable
<
any
>>
{
hasChildren
(
element
:
T
|
null
):
boolean
;
getChildren
(
element
:
T
|
null
):
Thenable
<
IDataTreeElement
<
T
>
[]
>
;
}
enum
DataTreeNodeState
{
Idle
,
Loading
}
interface
IDataTreeNode
<
T
extends
NonNullable
<
any
>>
{
readonly
element
:
T
;
readonly
parent
:
IDataTreeNode
<
T
>
|
null
;
state
:
DataTreeNodeState
;
// promise: Thenable<any>;
}
interface
IDataTreeListTemplateData
<
T
>
{
templateData
:
T
;
}
class
DataTreeRenderer
<
T
,
TTemplateData
>
implements
IRenderer
<
IDataTreeNode
<
T
>
,
IDataTreeListTemplateData
<
TTemplateData
>>
{
readonly
templateId
:
string
;
constructor
(
private
renderer
:
IRenderer
<
T
,
TTemplateData
>
)
{
this
.
templateId
=
renderer
.
templateId
;
}
renderTemplate
(
container
:
HTMLElement
):
IDataTreeListTemplateData
<
TTemplateData
>
{
const
templateData
=
this
.
renderer
.
renderTemplate
(
container
);
return
{
templateData
};
}
renderElement
(
node
:
IDataTreeNode
<
T
>
,
index
:
number
,
templateData
:
IDataTreeListTemplateData
<
TTemplateData
>
):
void
{
this
.
renderer
.
renderElement
(
node
.
element
,
index
,
templateData
.
templateData
);
}
disposeElement
(
node
:
IDataTreeNode
<
T
>
):
void
{
// noop
}
disposeTemplate
(
templateData
:
IDataTreeListTemplateData
<
TTemplateData
>
):
void
{
this
.
renderer
.
disposeTemplate
(
templateData
.
templateData
);
}
}
export
class
DataTree
<
T
extends
NonNullable
<
any
>
,
TFilterData
=
void
>
{
private
tree
:
ObjectTree
<
IDataTreeNode
<
T
>
,
TFilterData
>
;
private
root
:
IDataTreeNode
<
T
>
;
private
nodes
=
new
Map
<
T
,
IDataTreeNode
<
T
>>
();
constructor
(
container
:
HTMLElement
,
delegate
:
IVirtualDelegate
<
T
>
,
renderers
:
IRenderer
<
T
,
any
>
[],
private
dataSource
:
IDataSource
<
T
>
,
options
?:
ITreeOptions
<
T
,
TFilterData
>
)
{
const
treeDelegate
=
new
ComposedTreeDelegate
<
T
,
IDataTreeNode
<
T
>>
(
delegate
);
const
treeRenderers
=
renderers
.
map
(
r
=>
new
DataTreeRenderer
(
r
));
const
treeOptions
=
createComposedTreeListOptions
<
T
,
IDataTreeNode
<
T
>>
(
options
);
this
.
tree
=
new
ObjectTree
(
container
,
treeDelegate
,
treeRenderers
,
treeOptions
);
this
.
root
=
{
element
:
undefined
,
// TODO@joao
parent
:
null
,
state
:
DataTreeNodeState
.
Idle
,
};
this
.
nodes
.
set
(
null
,
this
.
root
);
}
refresh
(
element
:
T
|
null
,
recursive
=
true
):
Thenable
<
void
>
{
const
node
:
IDataTreeNode
<
T
>
=
this
.
nodes
.
get
(
element
);
if
(
typeof
node
===
'
undefined
'
)
{
throw
new
Error
(
`Data tree node not found:
${
element
}
`
);
}
const
hasChildren
=
this
.
dataSource
.
hasChildren
(
element
);
if
(
!
hasChildren
)
{
this
.
tree
.
setChildren
(
node
===
this
.
root
?
null
:
node
);
return
Promise
.
resolve
(
null
);
}
else
{
node
.
state
=
DataTreeNodeState
.
Loading
;
return
this
.
dataSource
.
getChildren
(
element
)
.
then
(
children
=>
{
node
.
state
=
DataTreeNodeState
.
Idle
;
const
createTreeElement
=
(
el
:
IDataTreeElement
<
T
>
):
ITreeElement
<
IDataTreeNode
<
T
>>
=>
{
return
{
element
:
{
element
:
el
.
element
,
state
:
DataTreeNodeState
.
Idle
,
parent
:
node
},
collapsible
:
el
.
collapsible
,
collapsed
:
typeof
el
.
collapsed
===
'
boolean
'
?
el
.
collapsed
:
true
};
};
const
nodeChildren
=
children
.
map
<
ITreeElement
<
IDataTreeNode
<
T
>>>
(
createTreeElement
);
this
.
tree
.
setChildren
(
node
===
this
.
root
?
null
:
node
,
nodeChildren
);
},
err
=>
{
node
.
state
=
DataTreeNodeState
.
Idle
;
return
Promise
.
reject
(
err
);
});
}
}
}
\ No newline at end of file
src/vs/base/browser/ui/tree/objectTree.ts
浏览文件 @
26ebe97d
...
...
@@ -10,7 +10,7 @@ import { ISpliceable } from 'vs/base/common/sequence';
import
{
ITreeNode
,
ITreeModel
,
ITreeElement
}
from
'
vs/base/browser/ui/tree/tree
'
;
import
{
ObjectTreeModel
}
from
'
vs/base/browser/ui/tree/objectTreeModel
'
;
export
class
ObjectTree
<
T
,
TFilterData
=
void
>
extends
AbstractTree
<
T
,
TFilterData
,
ITreeNode
<
T
,
TFilterData
>>
{
export
class
ObjectTree
<
T
extends
NonNullable
<
any
>
,
TFilterData
=
void
>
extends
AbstractTree
<
T
,
TFilterData
,
ITreeNode
<
T
,
TFilterData
>>
{
protected
model
:
ObjectTreeModel
<
T
,
TFilterData
>
;
...
...
test/tree/public/index.html
浏览文件 @
26ebe97d
...
...
@@ -37,56 +37,118 @@
require
.
config
({
baseUrl
:
'
/static
'
});
require
([
'
vs/base/browser/ui/tree/indexTree
'
,
'
vs/base/browser/ui/tree/tree
'
,
'
vs/base/common/iterator
'
],
({
IndexTree
},
{
TreeVisibility
},
{
iter
})
=>
{
const
delegate
=
{
getHeight
()
{
return
22
;
},
getTemplateId
()
{
return
'
template
'
;
}
};
const
renderer
=
{
templateId
:
'
template
'
,
renderTemplate
(
container
)
{
return
container
;
},
renderElement
(
element
,
index
,
container
)
{
container
.
textContent
=
element
;
},
disposeTemplate
()
{
}
};
const
treeFilter
=
new
class
{
constructor
()
{
this
.
pattern
=
null
;
let
timeout
;
filter
.
oninput
=
()
=>
{
clearTimeout
(
timeout
);
timeout
=
setTimeout
(()
=>
this
.
updatePattern
(),
300
);
};
}
updatePattern
()
{
if
(
!
filter
.
value
)
{
require
([
'
vs/base/browser/ui/tree/indexTree
'
,
'
vs/base/browser/ui/tree/dataTree
'
,
'
vs/base/browser/ui/tree/tree
'
,
'
vs/base/common/iterator
'
],
({
IndexTree
},
{
DataTree
},
{
TreeVisibility
},
{
iter
})
=>
{
function
createIndexTree
()
{
const
delegate
=
{
getHeight
()
{
return
22
;
},
getTemplateId
()
{
return
'
template
'
;
}
};
const
renderer
=
{
templateId
:
'
template
'
,
renderTemplate
(
container
)
{
return
container
;
},
renderElement
(
element
,
index
,
container
)
{
container
.
textContent
=
element
;
},
disposeTemplate
()
{
}
};
const
treeFilter
=
new
class
{
constructor
()
{
this
.
pattern
=
null
;
}
else
{
this
.
pattern
=
new
RegExp
(
filter
.
value
,
'
i
'
);
let
timeout
;
filter
.
oninput
=
()
=>
{
clearTimeout
(
timeout
);
timeout
=
setTimeout
(()
=>
this
.
updatePattern
(),
300
);
};
}
updatePattern
()
{
if
(
!
filter
.
value
)
{
this
.
pattern
=
null
;
}
else
{
this
.
pattern
=
new
RegExp
(
filter
.
value
,
'
i
'
);
}
perf
(
'
refilter
'
,
()
=>
tree
.
refilter
());
}
filter
(
el
)
{
return
(
this
.
pattern
?
this
.
pattern
.
test
(
el
)
:
true
)
?
TreeVisibility
.
Visible
:
TreeVisibility
.
Recurse
;
perf
(
'
refilter
'
,
()
=>
tree
.
refilter
());
}
filter
(
el
)
{
return
(
this
.
pattern
?
this
.
pattern
.
test
(
el
)
:
true
)
?
TreeVisibility
.
Visible
:
TreeVisibility
.
Recurse
;
}
};
const
tree
=
new
IndexTree
(
container
,
delegate
,
[
renderer
],
{
filter
:
treeFilter
});
return
{
tree
,
treeFilter
};
}
function
createDataTree
()
{
const
delegate
=
{
getHeight
()
{
return
22
;
},
getTemplateId
()
{
return
'
template
'
;
}
};
const
renderer
=
{
templateId
:
'
template
'
,
renderTemplate
(
container
)
{
return
container
;
},
renderElement
(
element
,
index
,
container
)
{
container
.
textContent
=
element
.
name
;
},
disposeTemplate
()
{
}
};
const
treeFilter
=
new
class
{
constructor
()
{
this
.
pattern
=
null
;
let
timeout
;
filter
.
oninput
=
()
=>
{
clearTimeout
(
timeout
);
timeout
=
setTimeout
(()
=>
this
.
updatePattern
(),
300
);
};
}
updatePattern
()
{
if
(
!
filter
.
value
)
{
this
.
pattern
=
null
;
}
else
{
this
.
pattern
=
new
RegExp
(
filter
.
value
,
'
i
'
);
}
perf
(
'
refilter
'
,
()
=>
tree
.
refilter
());
}
filter
(
el
)
{
return
(
this
.
pattern
?
this
.
pattern
.
test
(
el
.
name
)
:
true
)
?
TreeVisibility
.
Visible
:
TreeVisibility
.
Recurse
;
}
};
const
dataSource
=
new
class
{
hasChildren
(
element
)
{
return
element
===
null
||
element
.
type
===
'
dir
'
;
}
getChildren
(
element
)
{
return
new
Promise
((
c
,
e
)
=>
{
const
xhr
=
new
XMLHttpRequest
();
xhr
.
open
(
'
GET
'
,
element
?
`/api/readdir?path=
${
element
.
path
}
`
:
'
/api/readdir
'
);
xhr
.
send
();
xhr
.
onreadystatechange
=
function
()
{
if
(
this
.
readyState
==
4
&&
this
.
status
==
200
)
{
const
els
=
JSON
.
parse
(
this
.
responseText
).
map
(
element
=>
({
element
,
collapsible
:
element
.
type
===
'
dir
'
}));
c
(
els
);
}
};
});
}
}
};
const
tree
=
new
IndexTree
(
container
,
delegate
,
[
renderer
]
,
{
filter
:
treeFilter
});
const
tree
=
new
DataTree
(
container
,
delegate
,
[
renderer
],
dataSource
,
{
filter
:
treeFilter
});
function
setModel
(
model
)
{
performance
.
mark
(
'
before splice
'
);
const
start
=
performance
.
now
();
;
console
.
log
(
'
splice took
'
,
performance
.
now
()
-
start
);
performance
.
mark
(
'
after splice
'
);
return
{
tree
,
treeFilter
};
}
switch
(
location
.
search
)
{
case
'
?problems
'
:
{
const
{
tree
,
treeFilter
}
=
createIndexTree
();
const
files
=
[];
for
(
let
i
=
0
;
i
<
10000
;
i
++
)
{
const
errors
=
[];
...
...
@@ -101,7 +163,16 @@
perf
(
'
splice
'
,
()
=>
tree
.
splice
([
0
],
0
,
files
));
break
;
}
case
'
?data
'
:
{
const
{
tree
,
treeFilter
}
=
createDataTree
();
tree
.
refresh
(
null
);
break
;
}
default
:
const
{
tree
,
treeFilter
}
=
createIndexTree
();
const
xhr
=
new
XMLHttpRequest
();
xhr
.
open
(
'
GET
'
,
'
/api/ls?path=
'
);
xhr
.
send
();
...
...
test/tree/server.js
浏览文件 @
26ebe97d
...
...
@@ -26,6 +26,29 @@ async function getTree(fsPath, level) {
return
{
element
,
collapsible
:
true
,
collapsed
:
false
,
children
};
}
async
function
readdir
(
relativePath
)
{
const
absolutePath
=
relativePath
?
path
.
join
(
root
,
relativePath
)
:
root
;
const
childNames
=
await
fs
.
readdir
(
absolutePath
);
const
childStats
=
await
Promise
.
all
(
childNames
.
map
(
async
name
=>
await
fs
.
stat
(
path
.
join
(
absolutePath
,
name
))));
const
result
=
[];
for
(
let
i
=
0
;
i
<
childNames
.
length
;
i
++
)
{
const
name
=
childNames
[
i
];
const
path
=
relativePath
?
`
${
relativePath
}
/
${
name
}
`
:
name
;
const
stat
=
childStats
[
i
];
if
(
stat
.
isFile
())
{
result
.
push
({
type
:
'
file
'
,
name
,
path
});
}
else
if
(
!
stat
.
isDirectory
()
||
name
===
'
.git
'
||
name
===
'
.build
'
)
{
continue
;
}
else
{
result
.
push
({
type
:
'
dir
'
,
name
,
path
});
}
}
return
result
;
}
app
.
use
(
serve
(
'
public
'
));
app
.
use
(
mount
(
'
/static
'
,
serve
(
'
../../out
'
)));
app
.
use
(
_
.
get
(
'
/api/ls
'
,
async
ctx
=>
{
...
...
@@ -33,7 +56,13 @@ app.use(_.get('/api/ls', async ctx => {
const
absolutePath
=
path
.
join
(
root
,
relativePath
);
ctx
.
body
=
await
getTree
(
absolutePath
,
0
);
}))
}));
app
.
use
(
_
.
get
(
'
/api/readdir
'
,
async
ctx
=>
{
const
relativePath
=
ctx
.
query
.
path
;
ctx
.
body
=
await
readdir
(
relativePath
);
}));
app
.
listen
(
3000
);
console
.
log
(
'
http://localhost:3000
'
);
\ No newline at end of file
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录