Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
vscode
提交
e878c3e9
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,发现更多精彩内容 >>
提交
e878c3e9
编写于
9月 05, 2019
作者:
S
Sandeep Somavarapu
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
strict property init - markers
上级
709d1ed9
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
54 addition
and
1108 deletion
+54
-1108
src/vs/workbench/contrib/markers/browser/markersPanel.ts
src/vs/workbench/contrib/markers/browser/markersPanel.ts
+28
-35
src/vs/workbench/contrib/markers/browser/markersPanelActions.ts
.../workbench/contrib/markers/browser/markersPanelActions.ts
+24
-21
src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts
...vs/workbench/contrib/markers/browser/markersTreeViewer.ts
+2
-2
src/vs/workbench/contrib/markers/browser/markersWidget.ts
src/vs/workbench/contrib/markers/browser/markersWidget.ts
+0
-1050
未找到文件。
src/vs/workbench/contrib/markers/browser/markersPanel.ts
浏览文件 @
e878c3e9
...
...
@@ -69,23 +69,21 @@ export class MarkersPanel extends Panel implements IMarkerFilterController {
private
lastSelectedRelativeTop
:
number
=
0
;
private
currentActiveResource
:
URI
|
null
=
null
;
private
tree
:
WorkbenchObjectTree
<
TreeElement
,
FilterData
>
;
private
treeLabels
:
ResourceLabels
;
private
rangeHighlightDecorations
:
RangeHighlightDecorations
;
private
actions
:
IAction
[]
;
private
collapseAllAction
:
IAction
;
private
filterAction
:
MarkersFilterAction
;
private
filterInputActionViewItem
:
MarkersFilterActionViewItem
;
private
treeContainer
:
HTMLElement
;
private
messageBoxContainer
:
HTMLElement
;
private
ariaLabelElement
:
HTMLElement
;
private
readonly
rangeHighlightDecorations
:
RangeHighlightDecorations
;
private
readonly
filter
:
Filter
;
private
tree
!
:
WorkbenchObjectTree
<
TreeElement
,
FilterData
>
;
private
treeContainer
!
:
HTMLElement
;
private
messageBoxContainer
!
:
HTMLElement
;
private
ariaLabelElement
!
:
HTMLElement
;
private
readonly
collapseAllAction
:
IAction
;
private
readonly
filterAction
:
MarkersFilterAction
;
private
filterInputActionViewItem
:
MarkersFilterActionViewItem
|
null
=
null
;
private
readonly
panelState
:
MementoObject
;
private
panelFoucusContextKey
:
IContextKey
<
boolean
>
;
private
filter
:
Filter
;
private
_onDidFilter
=
this
.
_register
(
new
Emitter
<
void
>
());
readonly
onDidFilter
:
Event
<
void
>
=
this
.
_onDidFilter
.
event
;
private
cachedFilterStats
:
{
total
:
number
;
filtered
:
number
;
}
|
undefined
=
undefined
;
...
...
@@ -114,12 +112,18 @@ export class MarkersPanel extends Panel implements IMarkerFilterController {
this
.
markersViewModel
=
instantiationService
.
createInstance
(
MarkersViewModel
,
this
.
panelState
[
'
multiline
'
]);
this
.
markersViewModel
.
onDidChange
(
this
.
onDidChangeViewState
,
this
,
this
.
disposables
);
this
.
setCurrentActiveEditor
();
this
.
filter
=
new
Filter
(
new
FilterOptions
());
this
.
rangeHighlightDecorations
=
this
.
_register
(
this
.
instantiationService
.
createInstance
(
RangeHighlightDecorations
));
// actions
this
.
collapseAllAction
=
new
Action
(
'
vs.tree.collapse
'
,
localize
(
'
collapseAll
'
,
"
Collapse All
"
),
'
monaco-tree-action collapse-all
'
,
true
,
async
()
=>
this
.
collapseAll
());
this
.
filterAction
=
this
.
instantiationService
.
createInstance
(
MarkersFilterAction
,
{
filterText
:
this
.
panelState
[
'
filter
'
]
||
''
,
filterHistory
:
this
.
panelState
[
'
filterHistory
'
]
||
[],
useFilesExclude
:
!!
this
.
panelState
[
'
useFilesExclude
'
]
});
}
public
create
(
parent
:
HTMLElement
):
void
{
super
.
create
(
parent
);
this
.
rangeHighlightDecorations
=
this
.
_register
(
this
.
instantiationService
.
createInstance
(
RangeHighlightDecorations
));
dom
.
addClass
(
parent
,
'
markers-panel
'
);
...
...
@@ -128,7 +132,6 @@ export class MarkersPanel extends Panel implements IMarkerFilterController {
this
.
createArialLabelElement
(
container
);
this
.
createMessageBox
(
container
);
this
.
createTree
(
container
);
this
.
createActions
();
this
.
createListeners
();
this
.
updateFilter
();
...
...
@@ -178,10 +181,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController {
}
public
getActions
():
IAction
[]
{
if
(
!
this
.
actions
)
{
this
.
createActions
();
}
return
this
.
actions
;
return
[
this
.
filterAction
,
this
.
collapseAllAction
];
}
public
showQuickFixes
(
marker
:
Marker
):
void
{
...
...
@@ -306,15 +306,14 @@ export class MarkersPanel extends Panel implements IMarkerFilterController {
const
onDidChangeRenderNodeCount
=
new
Relay
<
ITreeNode
<
any
,
any
>>
();
this
.
treeLabels
=
this
.
_register
(
this
.
instantiationService
.
createInstance
(
ResourceLabels
,
this
));
const
treeLabels
=
this
.
_register
(
this
.
instantiationService
.
createInstance
(
ResourceLabels
,
this
));
const
virtualDelegate
=
new
VirtualDelegate
(
this
.
markersViewModel
);
const
renderers
=
[
this
.
instantiationService
.
createInstance
(
ResourceMarkersRenderer
,
t
his
.
t
reeLabels
,
onDidChangeRenderNodeCount
.
event
),
this
.
instantiationService
.
createInstance
(
ResourceMarkersRenderer
,
treeLabels
,
onDidChangeRenderNodeCount
.
event
),
this
.
instantiationService
.
createInstance
(
MarkerRenderer
,
this
.
markersViewModel
),
this
.
instantiationService
.
createInstance
(
RelatedInformationRenderer
)
];
this
.
filter
=
new
Filter
(
new
FilterOptions
());
const
accessibilityProvider
=
this
.
instantiationService
.
createInstance
(
MarkersTreeAccessibilityProvider
);
const
identityProvider
=
{
...
...
@@ -396,17 +395,12 @@ export class MarkersPanel extends Panel implements IMarkerFilterController {
}));
}
private
createActions
():
void
{
this
.
collapseAllAction
=
new
Action
(
'
vs.tree.collapse
'
,
localize
(
'
collapseAll
'
,
"
Collapse All
"
),
'
monaco-tree-action collapse-all
'
,
true
,
async
()
=>
{
this
.
tree
.
collapseAll
();
this
.
tree
.
setSelection
([]);
this
.
tree
.
setFocus
([]);
this
.
tree
.
getHTMLElement
().
focus
();
this
.
tree
.
focusFirst
();
});
this
.
filterAction
=
this
.
instantiationService
.
createInstance
(
MarkersFilterAction
,
{
filterText
:
this
.
panelState
[
'
filter
'
]
||
''
,
filterHistory
:
this
.
panelState
[
'
filterHistory
'
]
||
[],
useFilesExclude
:
!!
this
.
panelState
[
'
useFilesExclude
'
]
});
this
.
actions
=
[
this
.
filterAction
,
this
.
collapseAllAction
];
private
collapseAll
():
void
{
this
.
tree
.
collapseAll
();
this
.
tree
.
setSelection
([]);
this
.
tree
.
setFocus
([]);
this
.
tree
.
getHTMLElement
().
focus
();
this
.
tree
.
focusFirst
();
}
private
createListeners
():
void
{
...
...
@@ -423,7 +417,6 @@ export class MarkersPanel extends Panel implements IMarkerFilterController {
this
.
updateFilter
();
}
}));
this
.
actions
.
forEach
(
a
=>
this
.
_register
(
a
));
}
private
onDidChangeModel
(
change
:
MarkerChangesEvent
)
{
...
...
src/vs/workbench/contrib/markers/browser/markersPanelActions.ts
浏览文件 @
e878c3e9
...
...
@@ -118,10 +118,9 @@ export interface IMarkerFilterController {
export
class
MarkersFilterActionViewItem
extends
BaseActionViewItem
{
private
delayedFilterUpdate
:
Delayer
<
void
>
;
private
container
:
HTMLElement
;
private
filterInputBox
:
HistoryInputBox
;
private
controlsContainer
:
HTMLInputElement
;
private
filterBadge
:
HTMLInputElement
;
private
container
:
HTMLElement
|
null
=
null
;
private
filterInputBox
:
HistoryInputBox
|
null
=
null
;
private
filterBadge
:
HTMLElement
|
null
=
null
;
private
focusContextKey
:
IContextKey
<
boolean
>
;
constructor
(
...
...
@@ -172,13 +171,13 @@ export class MarkersFilterActionViewItem extends BaseActionViewItem {
this
.
filterInputBox
.
inputElement
.
setAttribute
(
'
aria-labelledby
'
,
'
markers-panel-arialabel
'
);
this
.
_register
(
attachInputBoxStyler
(
this
.
filterInputBox
,
this
.
themeService
));
this
.
filterInputBox
.
value
=
this
.
action
.
filterText
;
this
.
_register
(
this
.
filterInputBox
.
onDidChange
(
filter
=>
this
.
delayedFilterUpdate
.
trigger
(()
=>
this
.
onDidInputChange
(
this
.
filterInputBox
))));
this
.
_register
(
this
.
filterInputBox
.
onDidChange
(
filter
=>
this
.
delayedFilterUpdate
.
trigger
(()
=>
this
.
onDidInputChange
(
this
.
filterInputBox
!
))));
this
.
_register
(
this
.
action
.
onDidChange
((
event
:
IMarkersFilterActionChangeEvent
)
=>
{
if
(
event
.
filterText
)
{
this
.
filterInputBox
.
value
=
this
.
action
.
filterText
;
this
.
filterInputBox
!
.
value
=
this
.
action
.
filterText
;
}
}));
this
.
_register
(
DOM
.
addStandardDisposableListener
(
this
.
filterInputBox
.
inputElement
,
DOM
.
EventType
.
KEY_DOWN
,
(
e
:
any
)
=>
this
.
onInputKeyDown
(
e
,
this
.
filterInputBox
)));
this
.
_register
(
DOM
.
addStandardDisposableListener
(
this
.
filterInputBox
.
inputElement
,
DOM
.
EventType
.
KEY_DOWN
,
(
e
:
any
)
=>
this
.
onInputKeyDown
(
e
,
this
.
filterInputBox
!
)));
this
.
_register
(
DOM
.
addStandardDisposableListener
(
container
,
DOM
.
EventType
.
KEY_DOWN
,
this
.
handleKeyboardEvent
));
this
.
_register
(
DOM
.
addStandardDisposableListener
(
container
,
DOM
.
EventType
.
KEY_UP
,
this
.
handleKeyboardEvent
));
...
...
@@ -189,24 +188,24 @@ export class MarkersFilterActionViewItem extends BaseActionViewItem {
}
private
createControls
(
container
:
HTMLElement
):
void
{
this
.
controlsContainer
=
DOM
.
append
(
container
,
DOM
.
$
(
'
.markers-panel-filter-controls
'
));
this
.
createBadge
(
this
.
controlsContainer
);
this
.
createFilesExcludeCheckbox
(
this
.
controlsContainer
);
const
controlsContainer
=
DOM
.
append
(
container
,
DOM
.
$
(
'
.markers-panel-filter-controls
'
));
this
.
createBadge
(
controlsContainer
);
this
.
createFilesExcludeCheckbox
(
controlsContainer
);
}
private
createBadge
(
container
:
HTMLElement
):
void
{
this
.
filterBadge
=
DOM
.
append
(
container
,
DOM
.
$
(
'
.markers-panel-filter-badge
'
));
const
filterBadge
=
this
.
filterBadge
=
DOM
.
append
(
container
,
DOM
.
$
(
'
.markers-panel-filter-badge
'
));
this
.
_register
(
attachStylerCallback
(
this
.
themeService
,
{
badgeBackground
,
badgeForeground
,
contrastBorder
},
colors
=>
{
const
background
=
colors
.
badgeBackground
?
colors
.
badgeBackground
.
toString
()
:
null
;
const
foreground
=
colors
.
badgeForeground
?
colors
.
badgeForeground
.
toString
()
:
null
;
const
border
=
colors
.
contrastBorder
?
colors
.
contrastBorder
.
toString
()
:
null
;
this
.
filterBadge
.
style
.
backgroundColor
=
background
;
filterBadge
.
style
.
backgroundColor
=
background
;
this
.
filterBadge
.
style
.
borderWidth
=
border
?
'
1px
'
:
null
;
this
.
filterBadge
.
style
.
borderStyle
=
border
?
'
solid
'
:
null
;
this
.
filterBadge
.
style
.
borderColor
=
border
;
this
.
filterBadge
.
style
.
color
=
foreground
;
filterBadge
.
style
.
borderWidth
=
border
?
'
1px
'
:
null
;
filterBadge
.
style
.
borderStyle
=
border
?
'
solid
'
:
null
;
filterBadge
.
style
.
borderColor
=
border
;
filterBadge
.
style
.
color
=
foreground
;
}));
this
.
updateBadge
();
this
.
_register
(
this
.
filterController
.
onDidFilter
(()
=>
this
.
updateBadge
()));
...
...
@@ -241,14 +240,18 @@ export class MarkersFilterActionViewItem extends BaseActionViewItem {
}
private
updateBadge
():
void
{
const
{
total
,
filtered
}
=
this
.
filterController
.
getFilterStats
();
DOM
.
toggleClass
(
this
.
filterBadge
,
'
hidden
'
,
total
===
filtered
||
filtered
===
0
);
this
.
filterBadge
.
textContent
=
localize
(
'
showing filtered problems
'
,
"
Showing {0} of {1}
"
,
filtered
,
total
);
this
.
adjustInputBox
();
if
(
this
.
filterBadge
)
{
const
{
total
,
filtered
}
=
this
.
filterController
.
getFilterStats
();
DOM
.
toggleClass
(
this
.
filterBadge
,
'
hidden
'
,
total
===
filtered
||
filtered
===
0
);
this
.
filterBadge
.
textContent
=
localize
(
'
showing filtered problems
'
,
"
Showing {0} of {1}
"
,
filtered
,
total
);
this
.
adjustInputBox
();
}
}
private
adjustInputBox
():
void
{
this
.
filterInputBox
.
inputElement
.
style
.
paddingRight
=
DOM
.
hasClass
(
this
.
container
,
'
small
'
)
||
DOM
.
hasClass
(
this
.
filterBadge
,
'
hidden
'
)
?
'
25px
'
:
'
150px
'
;
if
(
this
.
container
&&
this
.
filterInputBox
&&
this
.
filterBadge
)
{
this
.
filterInputBox
.
inputElement
.
style
.
paddingRight
=
DOM
.
hasClass
(
this
.
container
,
'
small
'
)
||
DOM
.
hasClass
(
this
.
filterBadge
,
'
hidden
'
)
?
'
25px
'
:
'
150px
'
;
}
}
// Action toolbar is swallowing some keys for action items which should not be for an input box
...
...
src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts
浏览文件 @
e878c3e9
...
...
@@ -510,7 +510,7 @@ export class MarkerViewModel extends Disposable {
}
}
private
_quickFixAction
:
QuickFixAction
;
private
_quickFixAction
:
QuickFixAction
|
null
=
null
;
get
quickFixAction
():
QuickFixAction
{
if
(
!
this
.
_quickFixAction
)
{
this
.
_quickFixAction
=
this
.
_register
(
this
.
instantiationService
.
createInstance
(
QuickFixAction
,
this
.
marker
));
...
...
@@ -616,7 +616,7 @@ export class MarkersViewModel extends Disposable {
private
bulkUpdate
:
boolean
=
false
;
private
hoveredMarker
:
Marker
|
null
;
private
hoveredMarker
:
Marker
|
null
=
null
;
private
hoverDelayer
:
Delayer
<
void
>
=
new
Delayer
<
void
>
(
300
);
constructor
(
...
...
src/vs/workbench/contrib/markers/browser/markersWidget.ts
已删除
100644 → 0
浏览文件 @
709d1ed9
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import
'
vs/css!./media/markers
'
;
import
{
URI
}
from
'
vs/base/common/uri
'
;
import
*
as
dom
from
'
vs/base/browser/dom
'
;
import
{
IAction
,
IActionViewItem
,
Action
}
from
'
vs/base/common/actions
'
;
import
{
ITelemetryService
}
from
'
vs/platform/telemetry/common/telemetry
'
;
import
{
IEditorService
,
SIDE_GROUP
,
ACTIVE_GROUP
}
from
'
vs/workbench/services/editor/common/editorService
'
;
import
Constants
from
'
vs/workbench/contrib/markers/browser/constants
'
;
import
{
Marker
,
ResourceMarkers
,
RelatedInformation
,
MarkersModel
,
MarkerChangesEvent
}
from
'
vs/workbench/contrib/markers/browser/markersModel
'
;
import
{
IInstantiationService
}
from
'
vs/platform/instantiation/common/instantiation
'
;
import
{
MarkersFilterActionViewItem
,
MarkersFilterAction
,
IMarkersFilterActionChangeEvent
}
from
'
vs/workbench/contrib/markers/browser/markersPanelActions
'
;
import
{
IConfigurationService
}
from
'
vs/platform/configuration/common/configuration
'
;
import
Messages
from
'
vs/workbench/contrib/markers/browser/messages
'
;
import
{
RangeHighlightDecorations
}
from
'
vs/workbench/browser/parts/editor/rangeDecorations
'
;
import
{
IThemeService
}
from
'
vs/platform/theme/common/themeService
'
;
import
{
ICodeEditor
}
from
'
vs/editor/browser/editorBrowser
'
;
import
{
IMarkersWorkbenchService
}
from
'
vs/workbench/contrib/markers/browser/markers
'
;
import
{
IStorageService
,
StorageScope
}
from
'
vs/platform/storage/common/storage
'
;
import
{
localize
}
from
'
vs/nls
'
;
import
{
IContextKey
,
IContextKeyService
}
from
'
vs/platform/contextkey/common/contextkey
'
;
import
{
Iterator
}
from
'
vs/base/common/iterator
'
;
import
{
ITreeElement
,
ITreeNode
,
ITreeContextMenuEvent
}
from
'
vs/base/browser/ui/tree/tree
'
;
import
{
Relay
,
Event
,
Emitter
}
from
'
vs/base/common/event
'
;
import
{
WorkbenchObjectTree
,
TreeResourceNavigator2
}
from
'
vs/platform/list/browser/listService
'
;
import
{
FilterOptions
}
from
'
vs/workbench/contrib/markers/browser/markersFilterOptions
'
;
import
{
IExpression
}
from
'
vs/base/common/glob
'
;
import
{
deepClone
}
from
'
vs/base/common/objects
'
;
import
{
IWorkspaceContextService
}
from
'
vs/platform/workspace/common/workspace
'
;
import
{
FilterData
,
Filter
,
VirtualDelegate
,
ResourceMarkersRenderer
,
MarkerRenderer
,
RelatedInformationRenderer
,
TreeElement
,
MarkersTreeAccessibilityProvider
,
MarkersViewModel
,
ResourceDragAndDrop
}
from
'
vs/workbench/contrib/markers/browser/markersTreeViewer
'
;
import
{
IContextMenuService
}
from
'
vs/platform/contextview/browser/contextView
'
;
import
{
Separator
,
ActionViewItem
}
from
'
vs/base/browser/ui/actionbar/actionbar
'
;
import
{
IMenuService
,
MenuId
}
from
'
vs/platform/actions/common/actions
'
;
import
{
IKeybindingService
}
from
'
vs/platform/keybinding/common/keybinding
'
;
import
{
IKeyboardEvent
,
StandardKeyboardEvent
}
from
'
vs/base/browser/keyboardEvent
'
;
import
{
KeyCode
}
from
'
vs/base/common/keyCodes
'
;
import
{
domEvent
}
from
'
vs/base/browser/event
'
;
import
{
IDisposable
,
dispose
,
Disposable
}
from
'
vs/base/common/lifecycle
'
;
import
{
ResourceLabels
}
from
'
vs/workbench/browser/labels
'
;
import
{
IMarker
}
from
'
vs/platform/markers/common/markers
'
;
import
{
withUndefinedAsNull
}
from
'
vs/base/common/types
'
;
import
{
MementoObject
}
from
'
vs/workbench/common/memento
'
;
import
{
Widget
}
from
'
vs/base/browser/ui/widget
'
;
import
{
InstantiationService
}
from
'
vs/platform/instantiation/common/instantiationService
'
;
function
createModelIterator
(
model
:
MarkersModel
):
Iterator
<
ITreeElement
<
TreeElement
>>
{
const
resourcesIt
=
Iterator
.
fromArray
(
model
.
resourceMarkers
);
return
Iterator
.
map
(
resourcesIt
,
m
=>
({
element
:
m
,
children
:
createResourceMarkersIterator
(
m
)
}));
}
function
createResourceMarkersIterator
(
resourceMarkers
:
ResourceMarkers
):
Iterator
<
ITreeElement
<
TreeElement
>>
{
const
markersIt
=
Iterator
.
fromArray
(
resourceMarkers
.
markers
);
return
Iterator
.
map
(
markersIt
,
m
=>
{
const
relatedInformationIt
=
Iterator
.
from
(
m
.
relatedInformation
);
const
children
=
Iterator
.
map
(
relatedInformationIt
,
r
=>
({
element
:
r
}));
return
{
element
:
m
,
children
};
});
}
class
MarkersWidgetActions
extends
Disposable
{
readonly
actions
:
IAction
[];
readonly
collapseAllAction
:
IAction
;
readonly
filterAction
:
MarkersFilterAction
;
constructor
(
tree
:
WorkbenchObjectTree
<
TreeElement
,
FilterData
>
,
panelState
:
MementoObject
,
@
IInstantiationService
instantiationService
:
IInstantiationService
)
{
super
();
this
.
collapseAllAction
=
new
Action
(
'
vs.tree.collapse
'
,
localize
(
'
collapseAll
'
,
"
Collapse All
"
),
'
monaco-tree-action collapse-all
'
,
true
,
async
()
=>
{
tree
.
collapseAll
();
tree
.
setSelection
([]);
tree
.
setFocus
([]);
tree
.
getHTMLElement
().
focus
();
tree
.
focusFirst
();
});
this
.
filterAction
=
instantiationService
.
createInstance
(
MarkersFilterAction
,
{
filterText
:
panelState
[
'
filter
'
]
||
''
,
filterHistory
:
panelState
[
'
filterHistory
'
]
||
[],
useFilesExclude
:
!!
panelState
[
'
useFilesExclude
'
]
});
this
.
actions
=
[
this
.
filterAction
,
this
.
collapseAllAction
];
}
}
class
MarkersTreeWidget
extends
Disposable
{
private
readonly
treeContainer
:
HTMLElement
;
private
readonly
tree
:
WorkbenchObjectTree
<
TreeElement
,
FilterData
>
;
constructor
(
parent
:
HTMLElement
,
markersViewModel
:
MarkersViewModel
,
@
IInstantiationService
private
readonly
instantiationService
:
IInstantiationService
,
@
IEditorService
private
readonly
editorService
:
IEditorService
,
@
IConfigurationService
private
readonly
configurationService
:
IConfigurationService
,
@
ITelemetryService
private
readonly
telemetryService
:
ITelemetryService
,
@
IThemeService
themeService
:
IThemeService
,
@
IMarkersWorkbenchService
private
readonly
markersWorkbenchService
:
IMarkersWorkbenchService
,
@
IStorageService
storageService
:
IStorageService
,
@
IContextKeyService
contextKeyService
:
IContextKeyService
,
@
IWorkspaceContextService
private
readonly
workspaceContextService
:
IWorkspaceContextService
,
@
IContextMenuService
private
readonly
contextMenuService
:
IContextMenuService
,
@
IMenuService
private
readonly
menuService
:
IMenuService
,
@
IKeybindingService
private
readonly
keybindingService
:
IKeybindingService
,
)
{
super
();
this
.
treeContainer
=
dom
.
append
(
parent
,
dom
.
$
(
'
.tree-container.show-file-icons
'
));
const
onDidChangeRenderNodeCount
=
new
Relay
<
ITreeNode
<
any
,
any
>>
();
const
virtualDelegate
=
new
VirtualDelegate
(
markersViewModel
);
const
renderers
=
[
this
.
instantiationService
.
createInstance
(
ResourceMarkersRenderer
,
this
.
treeLabels
,
onDidChangeRenderNodeCount
.
event
),
this
.
instantiationService
.
createInstance
(
MarkerRenderer
,
markersViewModel
),
this
.
instantiationService
.
createInstance
(
RelatedInformationRenderer
)
];
this
.
filter
=
new
Filter
(
new
FilterOptions
());
const
accessibilityProvider
=
this
.
instantiationService
.
createInstance
(
MarkersTreeAccessibilityProvider
);
const
identityProvider
=
{
getId
(
element
:
TreeElement
)
{
return
element
.
id
;
}
};
this
.
tree
=
this
.
_register
(
this
.
instantiationService
.
createInstance
(
WorkbenchObjectTree
,
'
MarkersPanel
'
,
this
.
treeContainer
,
virtualDelegate
,
renderers
,
{
filter
:
this
.
filter
,
accessibilityProvider
,
identityProvider
,
dnd
:
new
ResourceDragAndDrop
(
this
.
instantiationService
),
expandOnlyOnTwistieClick
:
(
e
:
TreeElement
)
=>
e
instanceof
Marker
&&
e
.
relatedInformation
.
length
>
0
}
));
onDidChangeRenderNodeCount
.
input
=
this
.
tree
.
onDidChangeRenderNodeCount
;
const
markerFocusContextKey
=
Constants
.
MarkerFocusContextKey
.
bindTo
(
this
.
tree
.
contextKeyService
);
const
relatedInformationFocusContextKey
=
Constants
.
RelatedInformationFocusContextKey
.
bindTo
(
this
.
tree
.
contextKeyService
);
this
.
_register
(
this
.
tree
.
onDidChangeFocus
(
focus
=>
{
markerFocusContextKey
.
set
(
focus
.
elements
.
some
(
e
=>
e
instanceof
Marker
));
relatedInformationFocusContextKey
.
set
(
focus
.
elements
.
some
(
e
=>
e
instanceof
RelatedInformation
));
}));
const
focusTracker
=
this
.
_register
(
dom
.
trackFocus
(
this
.
tree
.
getHTMLElement
()));
this
.
_register
(
focusTracker
.
onDidBlur
(()
=>
{
markerFocusContextKey
.
set
(
false
);
relatedInformationFocusContextKey
.
set
(
false
);
}));
const
markersNavigator
=
this
.
_register
(
new
TreeResourceNavigator2
(
this
.
tree
,
{
openOnFocus
:
true
}));
this
.
_register
(
Event
.
debounce
(
markersNavigator
.
onDidOpenResource
,
(
last
,
event
)
=>
event
,
75
,
true
)(
options
=>
{
this
.
openFileAtElement
(
options
.
element
,
!!
options
.
editorOptions
.
preserveFocus
,
options
.
sideBySide
,
!!
options
.
editorOptions
.
pinned
);
}));
this
.
_register
(
this
.
tree
.
onDidChangeCollapseState
(({
node
})
=>
{
const
{
element
}
=
node
;
if
(
element
instanceof
RelatedInformation
&&
!
node
.
collapsed
)
{
/* __GDPR__
"problems.expandRelatedInformation" : {
"source": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
"code" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
}
*/
this
.
telemetryService
.
publicLog
(
'
problems.expandRelatedInformation
'
,
this
.
getTelemetryData
(
element
.
marker
));
}
}));
this
.
_register
(
this
.
tree
.
onContextMenu
(
this
.
onContextMenu
,
this
));
this
.
_register
(
this
.
configurationService
.
onDidChangeConfiguration
(
e
=>
{
if
(
this
.
filterAction
.
useFilesExclude
&&
e
.
affectsConfiguration
(
'
files.exclude
'
))
{
this
.
updateFilter
();
}
}));
// move focus to input, whenever a key is pressed in the panel container
this
.
_register
(
domEvent
(
parent
,
'
keydown
'
)(
e
=>
{
if
(
this
.
filterInputActionViewItem
&&
this
.
keybindingService
.
mightProducePrintableCharacter
(
new
StandardKeyboardEvent
(
e
)))
{
this
.
filterInputActionViewItem
.
focus
();
}
}));
this
.
_register
(
Event
.
any
<
any
>
(
this
.
tree
.
onDidChangeSelection
,
this
.
tree
.
onDidChangeFocus
)(()
=>
{
const
elements
=
[...
this
.
tree
.
getSelection
(),
...
this
.
tree
.
getFocus
()];
for
(
const
element
of
elements
)
{
if
(
element
instanceof
Marker
)
{
const
viewModel
=
markersViewModel
.
getViewModel
(
element
);
if
(
viewModel
)
{
viewModel
.
showLightBulb
();
}
}
}
}));
this
.
_register
(
this
.
tree
.
onDidChangeSelection
(()
=>
{
let
selection
=
this
.
tree
.
getSelection
();
if
(
selection
&&
selection
.
length
>
0
)
{
this
.
lastSelectedRelativeTop
=
this
.
tree
.
getRelativeTop
(
selection
[
0
])
||
0
;
}
}));
}
public
layout
(
dimension
:
dom
.
Dimension
):
void
{
this
.
treeContainer
.
style
.
height
=
`
${
dimension
.
height
}
px`
;
this
.
tree
.
layout
(
dimension
.
height
,
dimension
.
width
);
}
public
isFoucsed
():
boolean
{
return
this
.
tree
.
getHTMLElement
()
===
document
.
activeElement
;
}
public
focus
():
void
{
this
.
tree
.
getHTMLElement
().
focus
();
}
refreshTree
(
markerOrChange
?:
Marker
|
MarkerChangesEvent
):
void
{
if
(
markerOrChange
)
{
if
(
markerOrChange
instanceof
Marker
)
{
this
.
tree
.
rerender
(
markerOrChange
);
}
else
{
if
(
markerOrChange
.
added
.
length
||
markerOrChange
.
removed
.
length
)
{
// Reset complete tree
this
.
tree
.
setChildren
(
null
,
createModelIterator
(
this
.
markersWorkbenchService
.
markersModel
));
}
else
{
// Update resource
for
(
const
updated
of
markerOrChange
.
updated
)
{
this
.
tree
.
setChildren
(
updated
,
createResourceMarkersIterator
(
updated
));
}
}
}
}
else
{
// Reset complete tree
this
.
tree
.
setChildren
(
null
,
createModelIterator
(
this
.
markersWorkbenchService
.
markersModel
));
}
}
toggleVisibility
(
show
:
boolean
):
void
{
dom
.
toggleClass
(
this
.
treeContainer
,
'
hidden
'
,
!
show
);
}
refilter
():
void
{
this
.
tree
.
refilter
();
}
private
isEmpty
():
boolean
{
const
{
total
,
filtered
}
=
this
.
getFilterStats
();
return
total
===
0
||
filtered
===
0
;
}
private
render
():
void
{
this
.
cachedFilterStats
=
undefined
;
this
.
tree
.
setChildren
(
null
,
createModelIterator
(
this
.
markersWorkbenchService
.
markersModel
));
dom
.
toggleClass
(
this
.
treeContainer
,
'
hidden
'
,
this
.
isEmpty
());
this
.
renderMessage
();
}
private
revealMarkersForCurrentActiveEditor
(
focus
:
boolean
=
false
):
void
{
let
currentActiveResource
=
this
.
getResourceForCurrentActiveResource
();
if
(
currentActiveResource
)
{
if
(
!
this
.
tree
.
isCollapsed
(
currentActiveResource
)
&&
this
.
hasSelectedMarkerFor
(
currentActiveResource
))
{
this
.
tree
.
reveal
(
this
.
tree
.
getSelection
()[
0
],
this
.
lastSelectedRelativeTop
);
if
(
focus
)
{
this
.
tree
.
setFocus
(
this
.
tree
.
getSelection
());
}
}
else
{
this
.
tree
.
expand
(
currentActiveResource
);
this
.
tree
.
reveal
(
currentActiveResource
,
0
);
if
(
focus
)
{
this
.
tree
.
setFocus
([
currentActiveResource
]);
this
.
tree
.
setSelection
([
currentActiveResource
]);
}
}
}
else
if
(
focus
)
{
this
.
tree
.
setSelection
([]);
this
.
tree
.
focusFirst
();
}
}
private
hasSelectedMarkerFor
(
resource
:
ResourceMarkers
):
boolean
{
let
selectedElement
=
this
.
tree
.
getSelection
();
if
(
selectedElement
&&
selectedElement
.
length
>
0
)
{
if
(
selectedElement
[
0
]
instanceof
Marker
)
{
if
(
resource
.
resource
.
toString
()
===
(
<
Marker
>
selectedElement
[
0
]).
marker
.
resource
.
toString
())
{
return
true
;
}
}
}
return
false
;
}
private
highlightCurrentSelectedMarkerRange
()
{
const
selections
=
this
.
tree
.
getSelection
();
if
(
selections
.
length
!==
1
)
{
return
;
}
const
selection
=
selections
[
0
];
if
(
!
(
selection
instanceof
Marker
))
{
return
;
}
this
.
rangeHighlightDecorations
.
highlightRange
(
selection
);
}
private
onContextMenu
(
e
:
ITreeContextMenuEvent
<
TreeElement
>
):
void
{
const
element
=
e
.
element
;
if
(
!
element
)
{
return
;
}
e
.
browserEvent
.
preventDefault
();
e
.
browserEvent
.
stopPropagation
();
this
.
contextMenuService
.
showContextMenu
({
getAnchor
:
()
=>
e
.
anchor
!
,
getActions
:
()
=>
this
.
getMenuActions
(
element
),
getActionViewItem
:
(
action
)
=>
{
const
keybinding
=
this
.
keybindingService
.
lookupKeybinding
(
action
.
id
);
if
(
keybinding
)
{
return
new
ActionViewItem
(
action
,
action
,
{
label
:
true
,
keybinding
:
keybinding
.
getLabel
()
});
}
return
undefined
;
},
onHide
:
(
wasCancelled
?:
boolean
)
=>
{
if
(
wasCancelled
)
{
this
.
tree
.
domFocus
();
}
}
});
}
private
getMenuActions
(
element
:
TreeElement
):
IAction
[]
{
const
result
:
IAction
[]
=
[];
if
(
element
instanceof
Marker
)
{
const
viewModel
=
this
.
markersViewModel
.
getViewModel
(
element
);
if
(
viewModel
)
{
const
quickFixActions
=
viewModel
.
quickFixAction
.
quickFixes
;
if
(
quickFixActions
.
length
)
{
result
.
push
(...
quickFixActions
);
result
.
push
(
new
Separator
());
}
}
}
const
menu
=
this
.
menuService
.
createMenu
(
MenuId
.
ProblemsPanelContext
,
this
.
tree
.
contextKeyService
);
const
groups
=
menu
.
getActions
();
menu
.
dispose
();
for
(
let
group
of
groups
)
{
const
[,
actions
]
=
group
;
result
.
push
(...
actions
);
result
.
push
(
new
Separator
());
}
result
.
pop
();
// remove last separator
return
result
;
}
public
getFocusElement
()
{
return
this
.
tree
.
getFocus
()[
0
];
}
public
dispose
():
void
{
super
.
dispose
();
this
.
tree
.
dispose
();
this
.
markersViewModel
.
dispose
();
this
.
disposables
=
dispose
(
this
.
disposables
);
}
}
export
class
MarkersWidget
extends
Widget
{
private
lastSelectedRelativeTop
:
number
=
0
;
private
currentActiveResource
:
URI
|
null
=
null
;
private
readonly
tree
:
WorkbenchObjectTree
<
TreeElement
,
FilterData
>
;
private
readonly
treeLabels
:
ResourceLabels
;
private
readonly
rangeHighlightDecorations
:
RangeHighlightDecorations
;
private
treeContainer
:
HTMLElement
;
private
messageBoxContainer
:
HTMLElement
;
private
ariaLabelElement
:
HTMLElement
;
private
readonly
panelState
:
MementoObject
;
private
panelFoucusContextKey
:
IContextKey
<
boolean
>
;
private
filter
:
Filter
;
private
filterInputActionViewItem
:
MarkersFilterActionViewItem
|
null
=
null
;
private
_onDidFilter
=
this
.
_register
(
new
Emitter
<
void
>
());
readonly
onDidFilter
:
Event
<
void
>
=
this
.
_onDidFilter
.
event
;
private
cachedFilterStats
:
{
total
:
number
;
filtered
:
number
;
}
|
undefined
=
undefined
;
private
currentResourceGotAddedToMarkersData
:
boolean
=
false
;
readonly
markersViewModel
:
MarkersViewModel
;
private
disposables
:
IDisposable
[]
=
[];
constructor
(
parent
:
HTMLElement
,
@
IInstantiationService
private
readonly
instantiationService
:
IInstantiationService
,
@
IEditorService
private
readonly
editorService
:
IEditorService
,
@
IConfigurationService
private
readonly
configurationService
:
IConfigurationService
,
@
ITelemetryService
private
readonly
telemetryService
:
ITelemetryService
,
@
IThemeService
themeService
:
IThemeService
,
@
IMarkersWorkbenchService
private
readonly
markersWorkbenchService
:
IMarkersWorkbenchService
,
@
IStorageService
storageService
:
IStorageService
,
@
IContextKeyService
contextKeyService
:
IContextKeyService
,
@
IWorkspaceContextService
private
readonly
workspaceContextService
:
IWorkspaceContextService
,
@
IContextMenuService
private
readonly
contextMenuService
:
IContextMenuService
,
@
IMenuService
private
readonly
menuService
:
IMenuService
,
@
IKeybindingService
private
readonly
keybindingService
:
IKeybindingService
,
)
{
super
();
this
.
panelFoucusContextKey
=
Constants
.
MarkerPanelFocusContextKey
.
bindTo
(
contextKeyService
);
this
.
panelState
=
this
.
getMemento
(
StorageScope
.
WORKSPACE
);
this
.
markersViewModel
=
instantiationService
.
createInstance
(
MarkersViewModel
,
this
.
panelState
[
'
multiline
'
]);
this
.
markersViewModel
.
onDidChange
(
this
.
onDidChangeViewState
,
this
,
this
.
disposables
);
this
.
setCurrentActiveEditor
();
this
.
rangeHighlightDecorations
=
this
.
_register
(
this
.
instantiationService
.
createInstance
(
RangeHighlightDecorations
));
this
.
treeLabels
=
this
.
_register
(
this
.
instantiationService
.
createInstance
(
ResourceLabels
,
this
));
this
.
createArialLabelElement
(
parent
);
this
.
createMessageBox
(
parent
);
this
.
tree
=
this
.
createTree
(
parent
);
this
.
actions
=
this
.
createActions
();
this
.
createListeners
();
this
.
updateFilter
();
this
.
_register
(
this
.
onDidFocus
(()
=>
this
.
panelFoucusContextKey
.
set
(
true
)));
this
.
_register
(
this
.
onDidBlur
(()
=>
this
.
panelFoucusContextKey
.
set
(
false
)));
this
.
_register
(
this
.
onDidChangeVisibility
(
visible
=>
{
if
(
visible
)
{
this
.
refreshPanel
();
}
else
{
this
.
rangeHighlightDecorations
.
removeHighlightRange
();
}
}));
this
.
render
();
}
public
layout
(
dimension
:
dom
.
Dimension
):
void
{
this
.
treeContainer
.
style
.
height
=
`
${
dimension
.
height
}
px`
;
this
.
tree
.
layout
(
dimension
.
height
,
dimension
.
width
);
if
(
this
.
filterInputActionViewItem
)
{
this
.
filterInputActionViewItem
.
toggleLayout
(
dimension
.
width
<
1200
);
}
}
public
focus
():
void
{
if
(
this
.
tree
.
getHTMLElement
()
===
document
.
activeElement
)
{
return
;
}
if
(
this
.
isEmpty
())
{
this
.
messageBoxContainer
.
focus
();
}
else
{
this
.
tree
.
getHTMLElement
().
focus
();
}
}
public
focusFilter
():
void
{
if
(
this
.
filterInputActionViewItem
)
{
this
.
filterInputActionViewItem
.
focus
();
}
}
public
getActions
():
IAction
[]
{
return
this
.
actions
;
}
public
showQuickFixes
(
marker
:
Marker
):
void
{
const
viewModel
=
this
.
markersViewModel
.
getViewModel
(
marker
);
if
(
viewModel
)
{
viewModel
.
quickFixAction
.
run
();
}
}
public
openFileAtElement
(
element
:
any
,
preserveFocus
:
boolean
,
sideByside
:
boolean
,
pinned
:
boolean
):
boolean
{
const
{
resource
,
selection
,
event
,
data
}
=
element
instanceof
Marker
?
{
resource
:
element
.
resource
,
selection
:
element
.
range
,
event
:
'
problems.selectDiagnostic
'
,
data
:
this
.
getTelemetryData
(
element
.
marker
)
}
:
element
instanceof
RelatedInformation
?
{
resource
:
element
.
raw
.
resource
,
selection
:
element
.
raw
,
event
:
'
problems.selectRelatedInformation
'
,
data
:
this
.
getTelemetryData
(
element
.
marker
)
}
:
{
resource
:
null
,
selection
:
null
,
event
:
null
,
data
:
null
};
if
(
resource
&&
selection
&&
event
)
{
/* __GDPR__
"problems.selectDiagnostic" : {
"source": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
"code" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
}
*/
/* __GDPR__
"problems.selectRelatedInformation" : {
"source": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
"code" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
}
*/
this
.
telemetryService
.
publicLog
(
event
,
data
);
this
.
editorService
.
openEditor
({
resource
,
options
:
{
selection
,
preserveFocus
,
pinned
,
revealIfVisible
:
true
},
},
sideByside
?
SIDE_GROUP
:
ACTIVE_GROUP
).
then
(
editor
=>
{
if
(
editor
&&
preserveFocus
)
{
this
.
rangeHighlightDecorations
.
highlightRange
({
resource
,
range
:
selection
},
<
ICodeEditor
>
editor
.
getControl
());
}
else
{
this
.
rangeHighlightDecorations
.
removeHighlightRange
();
}
});
return
true
;
}
else
{
this
.
rangeHighlightDecorations
.
removeHighlightRange
();
}
return
false
;
}
private
refreshPanel
(
markerOrChange
?:
Marker
|
MarkerChangesEvent
):
void
{
if
(
this
.
isVisible
())
{
this
.
cachedFilterStats
=
undefined
;
if
(
markerOrChange
)
{
if
(
markerOrChange
instanceof
Marker
)
{
this
.
tree
.
rerender
(
markerOrChange
);
}
else
{
if
(
markerOrChange
.
added
.
length
||
markerOrChange
.
removed
.
length
)
{
// Reset complete tree
this
.
tree
.
setChildren
(
null
,
createModelIterator
(
this
.
markersWorkbenchService
.
markersModel
));
}
else
{
// Update resource
for
(
const
updated
of
markerOrChange
.
updated
)
{
this
.
tree
.
setChildren
(
updated
,
createResourceMarkersIterator
(
updated
));
}
}
}
}
else
{
// Reset complete tree
this
.
tree
.
setChildren
(
null
,
createModelIterator
(
this
.
markersWorkbenchService
.
markersModel
));
}
const
{
total
,
filtered
}
=
this
.
getFilterStats
();
dom
.
toggleClass
(
this
.
treeContainer
,
'
hidden
'
,
total
===
0
||
filtered
===
0
);
this
.
renderMessage
();
this
.
_onDidFilter
.
fire
();
}
}
private
onDidChangeViewState
(
marker
?:
Marker
):
void
{
this
.
refreshPanel
(
marker
);
}
private
updateFilter
()
{
this
.
cachedFilterStats
=
undefined
;
this
.
filter
.
options
=
new
FilterOptions
(
this
.
filterAction
.
filterText
,
this
.
getFilesExcludeExpressions
());
this
.
tree
.
refilter
();
this
.
_onDidFilter
.
fire
();
const
{
total
,
filtered
}
=
this
.
getFilterStats
();
dom
.
toggleClass
(
this
.
treeContainer
,
'
hidden
'
,
total
===
0
||
filtered
===
0
);
this
.
renderMessage
();
}
private
getFilesExcludeExpressions
():
{
root
:
URI
,
expression
:
IExpression
}[]
|
IExpression
{
if
(
!
this
.
filterAction
.
useFilesExclude
)
{
return
[];
}
const
workspaceFolders
=
this
.
workspaceContextService
.
getWorkspace
().
folders
;
return
workspaceFolders
.
length
?
workspaceFolders
.
map
(
workspaceFolder
=>
({
root
:
workspaceFolder
.
uri
,
expression
:
this
.
getFilesExclude
(
workspaceFolder
.
uri
)
}))
:
this
.
getFilesExclude
();
}
private
getFilesExclude
(
resource
?:
URI
):
IExpression
{
return
deepClone
(
this
.
configurationService
.
getValue
(
'
files.exclude
'
,
{
resource
}))
||
{};
}
private
createMessageBox
(
parent
:
HTMLElement
):
void
{
this
.
messageBoxContainer
=
dom
.
append
(
parent
,
dom
.
$
(
'
.message-box-container
'
));
this
.
messageBoxContainer
.
setAttribute
(
'
aria-labelledby
'
,
'
markers-panel-arialabel
'
);
}
private
createArialLabelElement
(
parent
:
HTMLElement
):
void
{
this
.
ariaLabelElement
=
dom
.
append
(
parent
,
dom
.
$
(
''
));
this
.
ariaLabelElement
.
setAttribute
(
'
id
'
,
'
markers-panel-arialabel
'
);
this
.
ariaLabelElement
.
setAttribute
(
'
aria-live
'
,
'
polite
'
);
}
private
createTree
(
parent
:
HTMLElement
):
WorkbenchObjectTree
<
TreeElement
,
FilterData
>
{
this
.
treeContainer
=
dom
.
append
(
parent
,
dom
.
$
(
'
.tree-container.show-file-icons
'
));
const
onDidChangeRenderNodeCount
=
new
Relay
<
ITreeNode
<
any
,
any
>>
();
const
virtualDelegate
=
new
VirtualDelegate
(
this
.
markersViewModel
);
const
renderers
=
[
this
.
instantiationService
.
createInstance
(
ResourceMarkersRenderer
,
this
.
treeLabels
,
onDidChangeRenderNodeCount
.
event
),
this
.
instantiationService
.
createInstance
(
MarkerRenderer
,
this
.
markersViewModel
),
this
.
instantiationService
.
createInstance
(
RelatedInformationRenderer
)
];
this
.
filter
=
new
Filter
(
new
FilterOptions
());
const
accessibilityProvider
=
this
.
instantiationService
.
createInstance
(
MarkersTreeAccessibilityProvider
);
const
identityProvider
=
{
getId
(
element
:
TreeElement
)
{
return
element
.
id
;
}
};
const
tree
=
this
.
instantiationService
.
createInstance
(
WorkbenchObjectTree
,
'
MarkersPanel
'
,
this
.
treeContainer
,
virtualDelegate
,
renderers
,
{
filter
:
this
.
filter
,
accessibilityProvider
,
identityProvider
,
dnd
:
new
ResourceDragAndDrop
(
this
.
instantiationService
),
expandOnlyOnTwistieClick
:
(
e
:
TreeElement
)
=>
e
instanceof
Marker
&&
e
.
relatedInformation
.
length
>
0
}
);
onDidChangeRenderNodeCount
.
input
=
tree
.
onDidChangeRenderNodeCount
;
const
markerFocusContextKey
=
Constants
.
MarkerFocusContextKey
.
bindTo
(
tree
.
contextKeyService
);
const
relatedInformationFocusContextKey
=
Constants
.
RelatedInformationFocusContextKey
.
bindTo
(
tree
.
contextKeyService
);
this
.
_register
(
tree
.
onDidChangeFocus
(
focus
=>
{
markerFocusContextKey
.
set
(
focus
.
elements
.
some
(
e
=>
e
instanceof
Marker
));
relatedInformationFocusContextKey
.
set
(
focus
.
elements
.
some
(
e
=>
e
instanceof
RelatedInformation
));
}));
const
focusTracker
=
this
.
_register
(
dom
.
trackFocus
(
tree
.
getHTMLElement
()));
this
.
_register
(
focusTracker
.
onDidBlur
(()
=>
{
markerFocusContextKey
.
set
(
false
);
relatedInformationFocusContextKey
.
set
(
false
);
}));
const
markersNavigator
=
this
.
_register
(
new
TreeResourceNavigator2
(
tree
,
{
openOnFocus
:
true
}));
this
.
_register
(
Event
.
debounce
(
markersNavigator
.
onDidOpenResource
,
(
last
,
event
)
=>
event
,
75
,
true
)(
options
=>
{
this
.
openFileAtElement
(
options
.
element
,
!!
options
.
editorOptions
.
preserveFocus
,
options
.
sideBySide
,
!!
options
.
editorOptions
.
pinned
);
}));
this
.
_register
(
tree
.
onDidChangeCollapseState
(({
node
})
=>
{
const
{
element
}
=
node
;
if
(
element
instanceof
RelatedInformation
&&
!
node
.
collapsed
)
{
/* __GDPR__
"problems.expandRelatedInformation" : {
"source": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
"code" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
}
*/
this
.
telemetryService
.
publicLog
(
'
problems.expandRelatedInformation
'
,
this
.
getTelemetryData
(
element
.
marker
));
}
}));
this
.
_register
(
tree
.
onContextMenu
(
this
.
onContextMenu
,
this
));
this
.
_register
(
this
.
configurationService
.
onDidChangeConfiguration
(
e
=>
{
if
(
this
.
filterAction
.
useFilesExclude
&&
e
.
affectsConfiguration
(
'
files.exclude
'
))
{
this
.
updateFilter
();
}
}));
// move focus to input, whenever a key is pressed in the panel container
this
.
_register
(
domEvent
(
parent
,
'
keydown
'
)(
e
=>
{
if
(
this
.
filterInputActionViewItem
&&
this
.
keybindingService
.
mightProducePrintableCharacter
(
new
StandardKeyboardEvent
(
e
)))
{
this
.
filterInputActionViewItem
.
focus
();
}
}));
this
.
_register
(
Event
.
any
<
any
>
(
tree
.
onDidChangeSelection
,
tree
.
onDidChangeFocus
)(()
=>
{
const
elements
=
[...
tree
.
getSelection
(),
...
tree
.
getFocus
()];
for
(
const
element
of
elements
)
{
if
(
element
instanceof
Marker
)
{
const
viewModel
=
this
.
markersViewModel
.
getViewModel
(
element
);
if
(
viewModel
)
{
viewModel
.
showLightBulb
();
}
}
}
}));
return
tree
;
}
private
createActions
():
IAction
[]
{
this
.
collapseAllAction
=
new
Action
(
'
vs.tree.collapse
'
,
localize
(
'
collapseAll
'
,
"
Collapse All
"
),
'
monaco-tree-action collapse-all
'
,
true
,
async
()
=>
{
this
.
tree
.
collapseAll
();
this
.
tree
.
setSelection
([]);
this
.
tree
.
setFocus
([]);
this
.
tree
.
getHTMLElement
().
focus
();
this
.
tree
.
focusFirst
();
});
this
.
filterAction
=
this
.
instantiationService
.
createInstance
(
MarkersFilterAction
,
{
filterText
:
this
.
panelState
[
'
filter
'
]
||
''
,
filterHistory
:
this
.
panelState
[
'
filterHistory
'
]
||
[],
useFilesExclude
:
!!
this
.
panelState
[
'
useFilesExclude
'
]
});
return
[
this
.
filterAction
,
this
.
collapseAllAction
];
}
private
createListeners
():
void
{
this
.
_register
(
Event
.
any
<
MarkerChangesEvent
|
void
>
(
this
.
markersWorkbenchService
.
markersModel
.
onDidChange
,
this
.
editorService
.
onDidActiveEditorChange
)(
changes
=>
{
if
(
changes
)
{
this
.
onDidChangeModel
(
changes
);
}
else
{
this
.
onActiveEditorChanged
();
}
}));
this
.
_register
(
this
.
tree
.
onDidChangeSelection
(()
=>
this
.
onSelected
()));
this
.
_register
(
this
.
filterAction
.
onDidChange
((
event
:
IMarkersFilterActionChangeEvent
)
=>
{
if
(
event
.
filterText
||
event
.
useFilesExclude
)
{
this
.
updateFilter
();
}
}));
this
.
actions
.
forEach
(
a
=>
this
.
_register
(
a
));
}
private
onDidChangeModel
(
change
:
MarkerChangesEvent
)
{
const
resourceMarkers
=
[...
change
.
added
,
...
change
.
removed
,
...
change
.
updated
];
const
resources
:
URI
[]
=
[];
for
(
const
{
resource
}
of
resourceMarkers
)
{
this
.
markersViewModel
.
remove
(
resource
);
const
resourceMarkers
=
this
.
markersWorkbenchService
.
markersModel
.
getResourceMarkers
(
resource
);
if
(
resourceMarkers
)
{
for
(
const
marker
of
resourceMarkers
.
markers
)
{
this
.
markersViewModel
.
add
(
marker
);
}
}
resources
.
push
(
resource
);
}
this
.
currentResourceGotAddedToMarkersData
=
this
.
currentResourceGotAddedToMarkersData
||
this
.
isCurrentResourceGotAddedToMarkersData
(
resources
);
this
.
refreshPanel
(
change
);
this
.
updateRangeHighlights
();
if
(
this
.
currentResourceGotAddedToMarkersData
)
{
this
.
autoReveal
();
this
.
currentResourceGotAddedToMarkersData
=
false
;
}
}
private
isCurrentResourceGotAddedToMarkersData
(
changedResources
:
URI
[])
{
const
currentlyActiveResource
=
this
.
currentActiveResource
;
if
(
!
currentlyActiveResource
)
{
return
false
;
}
const
resourceForCurrentActiveResource
=
this
.
getResourceForCurrentActiveResource
();
if
(
resourceForCurrentActiveResource
)
{
return
false
;
}
return
changedResources
.
some
(
r
=>
r
.
toString
()
===
currentlyActiveResource
.
toString
());
}
private
onActiveEditorChanged
():
void
{
this
.
setCurrentActiveEditor
();
this
.
autoReveal
();
}
private
setCurrentActiveEditor
():
void
{
const
activeEditor
=
this
.
editorService
.
activeEditor
;
this
.
currentActiveResource
=
activeEditor
?
withUndefinedAsNull
(
activeEditor
.
getResource
())
:
null
;
}
private
onSelected
():
void
{
let
selection
=
this
.
tree
.
getSelection
();
if
(
selection
&&
selection
.
length
>
0
)
{
this
.
lastSelectedRelativeTop
=
this
.
tree
.
getRelativeTop
(
selection
[
0
])
||
0
;
}
}
private
isEmpty
():
boolean
{
const
{
total
,
filtered
}
=
this
.
getFilterStats
();
return
total
===
0
||
filtered
===
0
;
}
private
render
():
void
{
this
.
cachedFilterStats
=
undefined
;
this
.
tree
.
setChildren
(
null
,
createModelIterator
(
this
.
markersWorkbenchService
.
markersModel
));
dom
.
toggleClass
(
this
.
treeContainer
,
'
hidden
'
,
this
.
isEmpty
());
this
.
renderMessage
();
}
private
renderMessage
():
void
{
dom
.
clearNode
(
this
.
messageBoxContainer
);
const
{
total
,
filtered
}
=
this
.
getFilterStats
();
if
(
filtered
===
0
)
{
this
.
messageBoxContainer
.
style
.
display
=
'
block
'
;
this
.
messageBoxContainer
.
setAttribute
(
'
tabIndex
'
,
'
0
'
);
if
(
total
>
0
)
{
if
(
this
.
filter
.
options
.
filter
)
{
this
.
renderFilteredByFilterMessage
(
this
.
messageBoxContainer
);
}
else
{
this
.
renderFilteredByFilesExcludeMessage
(
this
.
messageBoxContainer
);
}
}
else
{
this
.
renderNoProblemsMessage
(
this
.
messageBoxContainer
);
}
}
else
{
this
.
messageBoxContainer
.
style
.
display
=
'
none
'
;
if
(
filtered
===
total
)
{
this
.
ariaLabelElement
.
setAttribute
(
'
aria-label
'
,
localize
(
'
No problems filtered
'
,
"
Showing {0} problems
"
,
total
));
}
else
{
this
.
ariaLabelElement
.
setAttribute
(
'
aria-label
'
,
localize
(
'
problems filtered
'
,
"
Showing {0} of {1} problems
"
,
filtered
,
total
));
}
this
.
messageBoxContainer
.
removeAttribute
(
'
tabIndex
'
);
}
}
private
renderFilteredByFilesExcludeMessage
(
container
:
HTMLElement
)
{
const
span1
=
dom
.
append
(
container
,
dom
.
$
(
'
span
'
));
span1
.
textContent
=
Messages
.
MARKERS_PANEL_NO_PROBLEMS_FILE_EXCLUSIONS_FILTER
;
const
link
=
dom
.
append
(
container
,
dom
.
$
(
'
a.messageAction
'
));
link
.
textContent
=
localize
(
'
disableFilesExclude
'
,
"
Disable Files Exclude Filter.
"
);
link
.
setAttribute
(
'
tabIndex
'
,
'
0
'
);
dom
.
addStandardDisposableListener
(
link
,
dom
.
EventType
.
CLICK
,
()
=>
this
.
filterAction
.
useFilesExclude
=
false
);
dom
.
addStandardDisposableListener
(
link
,
dom
.
EventType
.
KEY_DOWN
,
(
e
:
IKeyboardEvent
)
=>
{
if
(
e
.
equals
(
KeyCode
.
Enter
)
||
e
.
equals
(
KeyCode
.
Space
))
{
this
.
filterAction
.
useFilesExclude
=
false
;
e
.
stopPropagation
();
}
});
this
.
ariaLabelElement
.
setAttribute
(
'
aria-label
'
,
Messages
.
MARKERS_PANEL_NO_PROBLEMS_FILE_EXCLUSIONS_FILTER
);
}
private
renderFilteredByFilterMessage
(
container
:
HTMLElement
)
{
const
span1
=
dom
.
append
(
container
,
dom
.
$
(
'
span
'
));
span1
.
textContent
=
Messages
.
MARKERS_PANEL_NO_PROBLEMS_FILTERS
;
const
link
=
dom
.
append
(
container
,
dom
.
$
(
'
a.messageAction
'
));
link
.
textContent
=
localize
(
'
clearFilter
'
,
"
Clear Filter
"
);
link
.
setAttribute
(
'
tabIndex
'
,
'
0
'
);
const
span2
=
dom
.
append
(
container
,
dom
.
$
(
'
span
'
));
span2
.
textContent
=
'
.
'
;
dom
.
addStandardDisposableListener
(
link
,
dom
.
EventType
.
CLICK
,
()
=>
this
.
filterAction
.
filterText
=
''
);
dom
.
addStandardDisposableListener
(
link
,
dom
.
EventType
.
KEY_DOWN
,
(
e
:
IKeyboardEvent
)
=>
{
if
(
e
.
equals
(
KeyCode
.
Enter
)
||
e
.
equals
(
KeyCode
.
Space
))
{
this
.
filterAction
.
filterText
=
''
;
e
.
stopPropagation
();
}
});
this
.
ariaLabelElement
.
setAttribute
(
'
aria-label
'
,
Messages
.
MARKERS_PANEL_NO_PROBLEMS_FILTERS
);
}
private
renderNoProblemsMessage
(
container
:
HTMLElement
)
{
const
span
=
dom
.
append
(
container
,
dom
.
$
(
'
span
'
));
span
.
textContent
=
Messages
.
MARKERS_PANEL_NO_PROBLEMS_BUILT
;
this
.
ariaLabelElement
.
setAttribute
(
'
aria-label
'
,
Messages
.
MARKERS_PANEL_NO_PROBLEMS_BUILT
);
}
private
autoReveal
(
focus
:
boolean
=
false
):
void
{
let
autoReveal
=
this
.
configurationService
.
getValue
<
boolean
>
(
'
problems.autoReveal
'
);
if
(
typeof
autoReveal
===
'
boolean
'
&&
autoReveal
)
{
this
.
revealMarkersForCurrentActiveEditor
(
focus
);
}
}
private
revealMarkersForCurrentActiveEditor
(
focus
:
boolean
=
false
):
void
{
let
currentActiveResource
=
this
.
getResourceForCurrentActiveResource
();
if
(
currentActiveResource
)
{
if
(
!
this
.
tree
.
isCollapsed
(
currentActiveResource
)
&&
this
.
hasSelectedMarkerFor
(
currentActiveResource
))
{
this
.
tree
.
reveal
(
this
.
tree
.
getSelection
()[
0
],
this
.
lastSelectedRelativeTop
);
if
(
focus
)
{
this
.
tree
.
setFocus
(
this
.
tree
.
getSelection
());
}
}
else
{
this
.
tree
.
expand
(
currentActiveResource
);
this
.
tree
.
reveal
(
currentActiveResource
,
0
);
if
(
focus
)
{
this
.
tree
.
setFocus
([
currentActiveResource
]);
this
.
tree
.
setSelection
([
currentActiveResource
]);
}
}
}
else
if
(
focus
)
{
this
.
tree
.
setSelection
([]);
this
.
tree
.
focusFirst
();
}
}
private
getResourceForCurrentActiveResource
():
ResourceMarkers
|
null
{
return
this
.
currentActiveResource
?
this
.
markersWorkbenchService
.
markersModel
.
getResourceMarkers
(
this
.
currentActiveResource
)
:
null
;
}
private
hasSelectedMarkerFor
(
resource
:
ResourceMarkers
):
boolean
{
let
selectedElement
=
this
.
tree
.
getSelection
();
if
(
selectedElement
&&
selectedElement
.
length
>
0
)
{
if
(
selectedElement
[
0
]
instanceof
Marker
)
{
if
(
resource
.
resource
.
toString
()
===
(
<
Marker
>
selectedElement
[
0
]).
marker
.
resource
.
toString
())
{
return
true
;
}
}
}
return
false
;
}
private
updateRangeHighlights
()
{
this
.
rangeHighlightDecorations
.
removeHighlightRange
();
if
(
this
.
tree
.
getHTMLElement
()
===
document
.
activeElement
)
{
this
.
highlightCurrentSelectedMarkerRange
();
}
}
private
highlightCurrentSelectedMarkerRange
()
{
const
selections
=
this
.
tree
.
getSelection
();
if
(
selections
.
length
!==
1
)
{
return
;
}
const
selection
=
selections
[
0
];
if
(
!
(
selection
instanceof
Marker
))
{
return
;
}
this
.
rangeHighlightDecorations
.
highlightRange
(
selection
);
}
private
onContextMenu
(
e
:
ITreeContextMenuEvent
<
TreeElement
>
):
void
{
const
element
=
e
.
element
;
if
(
!
element
)
{
return
;
}
e
.
browserEvent
.
preventDefault
();
e
.
browserEvent
.
stopPropagation
();
this
.
contextMenuService
.
showContextMenu
({
getAnchor
:
()
=>
e
.
anchor
!
,
getActions
:
()
=>
this
.
getMenuActions
(
element
),
getActionViewItem
:
(
action
)
=>
{
const
keybinding
=
this
.
keybindingService
.
lookupKeybinding
(
action
.
id
);
if
(
keybinding
)
{
return
new
ActionViewItem
(
action
,
action
,
{
label
:
true
,
keybinding
:
keybinding
.
getLabel
()
});
}
return
undefined
;
},
onHide
:
(
wasCancelled
?:
boolean
)
=>
{
if
(
wasCancelled
)
{
this
.
tree
.
domFocus
();
}
}
});
}
private
getMenuActions
(
element
:
TreeElement
):
IAction
[]
{
const
result
:
IAction
[]
=
[];
if
(
element
instanceof
Marker
)
{
const
viewModel
=
this
.
markersViewModel
.
getViewModel
(
element
);
if
(
viewModel
)
{
const
quickFixActions
=
viewModel
.
quickFixAction
.
quickFixes
;
if
(
quickFixActions
.
length
)
{
result
.
push
(...
quickFixActions
);
result
.
push
(
new
Separator
());
}
}
}
const
menu
=
this
.
menuService
.
createMenu
(
MenuId
.
ProblemsPanelContext
,
this
.
tree
.
contextKeyService
);
const
groups
=
menu
.
getActions
();
menu
.
dispose
();
for
(
let
group
of
groups
)
{
const
[,
actions
]
=
group
;
result
.
push
(...
actions
);
result
.
push
(
new
Separator
());
}
result
.
pop
();
// remove last separator
return
result
;
}
public
getFocusElement
()
{
return
this
.
tree
.
getFocus
()[
0
];
}
public
getActionViewItem
(
action
:
IAction
):
IActionViewItem
|
undefined
{
if
(
action
.
id
===
MarkersFilterAction
.
ID
)
{
this
.
filterInputActionViewItem
=
this
.
instantiationService
.
createInstance
(
MarkersFilterActionViewItem
,
this
.
filterAction
,
this
);
return
this
.
filterInputActionViewItem
;
}
return
super
.
getActionViewItem
(
action
);
}
getFilterOptions
():
FilterOptions
{
return
this
.
filter
.
options
;
}
getFilterStats
():
{
total
:
number
;
filtered
:
number
;
}
{
if
(
!
this
.
cachedFilterStats
)
{
this
.
cachedFilterStats
=
this
.
computeFilterStats
();
}
return
this
.
cachedFilterStats
;
}
private
computeFilterStats
():
{
total
:
number
;
filtered
:
number
;
}
{
const
root
=
this
.
tree
.
getNode
();
let
total
=
0
;
let
filtered
=
0
;
for
(
const
resourceMarkerNode
of
root
.
children
)
{
for
(
const
markerNode
of
resourceMarkerNode
.
children
)
{
total
++
;
if
(
resourceMarkerNode
.
visible
&&
markerNode
.
visible
)
{
filtered
++
;
}
}
}
return
{
total
,
filtered
};
}
private
getTelemetryData
({
source
,
code
}:
IMarker
):
any
{
return
{
source
,
code
};
}
protected
saveState
():
void
{
this
.
panelState
[
'
filter
'
]
=
this
.
filterAction
.
filterText
;
this
.
panelState
[
'
filterHistory
'
]
=
this
.
filterAction
.
filterHistory
;
this
.
panelState
[
'
useFilesExclude
'
]
=
this
.
filterAction
.
useFilesExclude
;
this
.
panelState
[
'
multiline
'
]
=
this
.
markersViewModel
.
multiline
;
super
.
saveState
();
}
public
dispose
():
void
{
super
.
dispose
();
this
.
tree
.
dispose
();
this
.
markersViewModel
.
dispose
();
this
.
disposables
=
dispose
(
this
.
disposables
);
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录