Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
xxadev
vscode
提交
80ca1300
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,发现更多精彩内容 >>
提交
80ca1300
编写于
11月 28, 2019
作者:
S
Sandeep Somavarapu
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
implemente better merging strategy
上级
1fbe309e
变更
2
隐藏空白更改
内联
并排
Showing
2 changed file
with
353 addition
and
224 deletion
+353
-224
src/vs/workbench/services/keybinding/common/keybindingsMerge.ts
.../workbench/services/keybinding/common/keybindingsMerge.ts
+187
-97
src/vs/workbench/services/keybinding/test/electron-browser/keybindingsMerge.test.ts
...keybinding/test/electron-browser/keybindingsMerge.test.ts
+166
-127
未找到文件。
src/vs/workbench/services/keybinding/common/keybindingsMerge.ts
浏览文件 @
80ca1300
...
...
@@ -4,179 +4,269 @@
*--------------------------------------------------------------------------------------------*/
import
*
as
objects
from
'
vs/base/common/objects
'
;
import
{
parse
,
findNodeAtLocation
,
parseTree
}
from
'
vs/base/common/json
'
;
import
{
parse
}
from
'
vs/base/common/json
'
;
import
{
values
,
keys
}
from
'
vs/base/common/map
'
;
import
{
IUserFriendlyKeybinding
}
from
'
vs/platform/keybinding/common/keybinding
'
;
import
{
IUserFriendlyKeybinding
,
IKeybindingService
}
from
'
vs/platform/keybinding/common/keybinding
'
;
import
{
firstIndex
as
findFirstIndex
,
equals
}
from
'
vs/base/common/arrays
'
;
import
{
ContextKeyExpr
}
from
'
vs/platform/contextkey/common/contextkey
'
;
import
*
as
contentUtil
from
'
vs/platform/userDataSync/common/content
'
;
import
{
IKeybindingsMergeService
}
from
'
vs/platform/userDataSync/common/userDataSync
'
;
interface
ICompareResult
{
added
:
Set
<
string
>
;
removed
:
Set
<
string
>
;
updated
:
Set
<
string
>
;
}
interface
IMergeResult
{
added
:
Set
<
string
>
;
removed
:
Set
<
string
>
;
updated
:
Set
<
string
>
;
conflicts
:
Set
<
string
>
;
}
export
class
KeybindingsMergeService
implements
IKeybindingsMergeService
{
_serviceBrand
:
undefined
;
constructor
(
@
IKeybindingService
private
readonly
keybindingsService
:
IKeybindingService
)
{
}
public
async
merge
(
localContent
:
string
,
remoteContent
:
string
,
baseContent
:
string
|
null
):
Promise
<
{
mergeContent
:
string
,
hasChanges
:
boolean
,
hasConflicts
:
boolean
}
>
{
const
local
=
<
IUserFriendlyKeybinding
[]
>
parse
(
localContent
);
const
remote
=
<
IUserFriendlyKeybinding
[]
>
parse
(
remoteContent
);
const
base
=
baseContent
?
<
IUserFriendlyKeybinding
[]
>
parse
(
baseContent
)
:
null
;
const
byCommand
=
(
keybindings
:
IUserFriendlyKeybinding
[])
=>
{
const
map
:
Map
<
string
,
IUserFriendlyKeybinding
[]
>
=
new
Map
<
string
,
IUserFriendlyKeybinding
[]
>
();
for
(
const
keybinding
of
keybindings
)
{
const
command
=
keybinding
.
command
[
0
]
===
'
-
'
?
keybinding
.
command
.
substring
(
1
)
:
keybinding
.
command
;
let
value
=
map
.
get
(
command
);
const
normalize
=
(
keybinding
:
IUserFriendlyKeybinding
):
IUserFriendlyKeybinding
=>
({
...
keybinding
,
...{
key
:
this
.
keybindingsService
.
resolveUserBinding
(
keybinding
.
key
).
map
(
part
=>
part
.
getUserSettingsLabel
()).
join
(
'
'
)
}
});
const
normalizedLocal
=
local
.
map
(
keybinding
=>
normalize
(
keybinding
));
const
normalizedRemote
=
remote
.
map
(
keybinding
=>
normalize
(
keybinding
));
const
normalizedBase
=
base
?
base
.
map
(
keybinding
=>
normalize
(
keybinding
))
:
null
;
const
byKeybinding
=
(
keybindings
:
IUserFriendlyKeybinding
[],
normalized
:
IUserFriendlyKeybinding
[])
=>
{
const
map
:
Map
<
string
,
{
keybinding
:
IUserFriendlyKeybinding
,
normalized
:
IUserFriendlyKeybinding
}[]
>
=
new
Map
<
string
,
{
keybinding
:
IUserFriendlyKeybinding
,
normalized
:
IUserFriendlyKeybinding
}[]
>
();
for
(
let
index
=
0
;
index
<
normalized
.
length
;
index
++
)
{
let
value
=
map
.
get
(
normalized
[
index
].
key
);
if
(
!
value
)
{
value
=
[];
map
.
set
(
command
,
value
);
map
.
set
(
normalized
[
index
].
key
,
value
);
}
value
.
push
(
keybinding
);
value
.
push
(
{
keybinding
:
keybindings
[
index
],
normalized
:
normalized
[
index
]
}
);
}
return
map
;
};
const
localBy
Command
=
byCommand
(
l
ocal
);
const
remoteBy
Command
=
byCommand
(
r
emote
);
const
baseBy
Command
=
base
?
byCommand
(
base
)
:
null
;
const
localBy
Keybinding
=
byKeybinding
(
local
,
normalizedL
ocal
);
const
remoteBy
Keybinding
=
byKeybinding
(
remote
,
normalizedR
emote
);
const
baseBy
Keybinding
=
base
?
byKeybinding
(
base
,
normalizedBase
!
)
:
null
;
const
localToRemote
=
this
.
compare
(
localByCommand
,
remoteByCommand
);
if
(
localToRemote
.
added
.
size
===
0
&&
localToRemote
.
removed
.
size
===
0
&&
localToRemote
.
updated
.
size
===
0
)
{
const
localToRemote
ByKeybinding
=
this
.
compareByKeybinding
(
localByKeybinding
,
remoteByKeybinding
);
if
(
localToRemote
ByKeybinding
.
added
.
size
===
0
&&
localToRemoteByKeybinding
.
removed
.
size
===
0
&&
localToRemoteByKeybinding
.
updated
.
size
===
0
)
{
// No changes found between local and remote.
return
{
mergeContent
:
localContent
,
hasChanges
:
false
,
hasConflicts
:
false
};
}
const
conflictCommands
:
Set
<
string
>
=
new
Set
<
string
>
();
const
baseToLocal
=
baseByCommand
?
this
.
compare
(
baseByCommand
,
localByCommand
)
:
{
added
:
keys
(
localByCommand
).
reduce
((
r
,
k
)
=>
{
r
.
add
(
k
);
return
r
;
},
new
Set
<
string
>
()),
removed
:
new
Set
<
string
>
(),
updated
:
new
Set
<
string
>
()
};
const
baseToRemote
=
baseByCommand
?
this
.
compare
(
baseByCommand
,
remoteByCommand
)
:
{
added
:
keys
(
remoteByCommand
).
reduce
((
r
,
k
)
=>
{
r
.
add
(
k
);
return
r
;
},
new
Set
<
string
>
()),
removed
:
new
Set
<
string
>
(),
updated
:
new
Set
<
string
>
()
};
const
baseToLocalByKeybinding
=
baseByKeybinding
?
this
.
compareByKeybinding
(
baseByKeybinding
,
localByKeybinding
)
:
{
added
:
keys
(
localByKeybinding
).
reduce
((
r
,
k
)
=>
{
r
.
add
(
k
);
return
r
;
},
new
Set
<
string
>
()),
removed
:
new
Set
<
string
>
(),
updated
:
new
Set
<
string
>
()
};
if
(
baseToLocalByKeybinding
.
added
.
size
===
0
&&
baseToLocalByKeybinding
.
removed
.
size
===
0
&&
baseToLocalByKeybinding
.
updated
.
size
===
0
)
{
// Remote has moved forward and local has not. Return remote
return
{
mergeContent
:
remoteContent
,
hasChanges
:
true
,
hasConflicts
:
false
};
}
const
baseToRemoteByKeybinding
=
baseByKeybinding
?
this
.
compareByKeybinding
(
baseByKeybinding
,
remoteByKeybinding
)
:
{
added
:
keys
(
remoteByKeybinding
).
reduce
((
r
,
k
)
=>
{
r
.
add
(
k
);
return
r
;
},
new
Set
<
string
>
()),
removed
:
new
Set
<
string
>
(),
updated
:
new
Set
<
string
>
()
};
if
(
baseToRemoteByKeybinding
.
added
.
size
===
0
&&
baseToRemoteByKeybinding
.
removed
.
size
===
0
&&
baseToRemoteByKeybinding
.
updated
.
size
===
0
)
{
// Local has moved forward and remote has not. Return local.
return
{
mergeContent
:
localContent
,
hasChanges
:
true
,
hasConflicts
:
false
};
}
// Both local and remote has moved forward.
const
byCommand
=
(
keybindings
:
IUserFriendlyKeybinding
[],
normalized
:
IUserFriendlyKeybinding
[])
=>
{
const
map
:
Map
<
string
,
{
keybinding
:
IUserFriendlyKeybinding
,
normalized
:
IUserFriendlyKeybinding
}[]
>
=
new
Map
<
string
,
{
keybinding
:
IUserFriendlyKeybinding
,
normalized
:
IUserFriendlyKeybinding
}[]
>
();
for
(
let
index
=
0
;
index
<
normalized
.
length
;
index
++
)
{
const
command
=
normalized
[
index
].
command
[
0
]
===
'
-
'
?
normalized
[
index
].
command
.
substring
(
1
)
:
normalized
[
index
].
command
;
let
value
=
map
.
get
(
command
);
if
(
!
value
)
{
value
=
[];
map
.
set
(
command
,
value
);
}
value
.
push
({
keybinding
:
keybindings
[
index
],
normalized
:
normalized
[
index
]
});
}
return
map
;
};
const
localByCommand
=
byCommand
(
local
,
normalizedLocal
);
const
remoteByCommand
=
byCommand
(
remote
,
normalizedRemote
);
const
baseByCommand
=
base
?
byCommand
(
base
,
normalizedBase
!
)
:
null
;
const
localToRemoteByCommand
=
this
.
compareByCommand
(
localByCommand
,
remoteByCommand
);
const
baseToLocalByCommand
=
baseByCommand
?
this
.
compareByCommand
(
baseByCommand
,
localByCommand
)
:
{
added
:
keys
(
localByCommand
).
reduce
((
r
,
k
)
=>
{
r
.
add
(
k
);
return
r
;
},
new
Set
<
string
>
()),
removed
:
new
Set
<
string
>
(),
updated
:
new
Set
<
string
>
()
};
const
baseToRemoteByCommand
=
baseByCommand
?
this
.
compareByCommand
(
baseByCommand
,
remoteByCommand
)
:
{
added
:
keys
(
remoteByCommand
).
reduce
((
r
,
k
)
=>
{
r
.
add
(
k
);
return
r
;
},
new
Set
<
string
>
()),
removed
:
new
Set
<
string
>
(),
updated
:
new
Set
<
string
>
()
};
const
commandsMergeResult
=
this
.
computeMergeResult
(
localToRemoteByCommand
,
baseToLocalByCommand
,
baseToRemoteByCommand
);
const
keybindingsMergeResult
=
this
.
computeMergeResult
(
localToRemoteByKeybinding
,
baseToLocalByKeybinding
,
baseToRemoteByKeybinding
);
const
eol
=
contentUtil
.
getEol
(
localContent
);
let
mergeContent
=
localContent
;
// Removed commands in Local
for
(
const
command
of
values
(
baseToLocal
.
removed
))
{
// Removed commands in Remote
for
(
const
command
of
values
(
commandsMergeResult
.
removed
))
{
if
(
commandsMergeResult
.
conflicts
.
has
(
command
))
{
continue
;
}
mergeContent
=
this
.
removeKeybindings
(
mergeContent
,
eol
,
command
);
}
// Added commands in remote
for
(
const
command
of
values
(
commandsMergeResult
.
added
))
{
if
(
commandsMergeResult
.
conflicts
.
has
(
command
))
{
continue
;
}
const
keybindings
=
remoteByCommand
.
get
(
command
)
!
;
if
(
keybindings
.
some
(({
normalized
})
=>
keybindingsMergeResult
.
conflicts
.
has
(
normalized
.
key
)))
{
continue
;
}
mergeContent
=
this
.
addKeybindings
(
mergeContent
,
eol
,
keybindings
.
map
(({
keybinding
})
=>
keybinding
));
}
// Updated commands in Remote
for
(
const
command
of
values
(
commandsMergeResult
.
updated
))
{
if
(
commandsMergeResult
.
conflicts
.
has
(
command
))
{
continue
;
}
const
keybindings
=
remoteByCommand
.
get
(
command
)
!
;
if
(
keybindings
.
some
(({
normalized
})
=>
keybindingsMergeResult
.
conflicts
.
has
(
normalized
.
key
)))
{
continue
;
}
mergeContent
=
this
.
updateKeybindings
(
mergeContent
,
eol
,
command
,
keybindings
.
map
(({
keybinding
})
=>
keybinding
));
}
const
hasConflicts
=
commandsMergeResult
.
conflicts
.
size
>
0
||
keybindingsMergeResult
.
conflicts
.
size
>
0
;
if
(
hasConflicts
)
{
mergeContent
=
`<<<<<<< local
${
eol
}
`
+
mergeContent
+
`
${
eol
}
=======
${
eol
}
`
+
remoteContent
+
`
${
eol
}
>>>>>>> remote`
;
}
return
{
mergeContent
,
hasChanges
:
true
,
hasConflicts
};
}
private
computeMergeResult
(
localToRemote
:
ICompareResult
,
baseToLocal
:
ICompareResult
,
baseToRemote
:
ICompareResult
):
IMergeResult
{
const
added
:
Set
<
string
>
=
new
Set
<
string
>
();
const
removed
:
Set
<
string
>
=
new
Set
<
string
>
();
const
updated
:
Set
<
string
>
=
new
Set
<
string
>
();
const
conflicts
:
Set
<
string
>
=
new
Set
<
string
>
();
// Removed keys in Local
for
(
const
key
of
values
(
baseToLocal
.
removed
))
{
// Got updated in remote
if
(
baseToRemote
.
updated
.
has
(
command
))
{
conflict
Commands
.
add
(
command
);
if
(
baseToRemote
.
updated
.
has
(
key
))
{
conflict
s
.
add
(
key
);
}
}
// Removed
command
s in Remote
for
(
const
command
of
values
(
baseToRemote
.
removed
))
{
if
(
conflict
Commands
.
has
(
command
))
{
// Removed
key
s in Remote
for
(
const
key
of
values
(
baseToRemote
.
removed
))
{
if
(
conflict
s
.
has
(
key
))
{
continue
;
}
// Got updated in local
if
(
baseToLocal
.
updated
.
has
(
command
))
{
conflict
Commands
.
add
(
command
);
if
(
baseToLocal
.
updated
.
has
(
key
))
{
conflict
s
.
add
(
key
);
}
else
{
// remove the
command
mergeContent
=
this
.
removeKeybindings
(
mergeContent
,
eol
,
command
);
// remove the
key
removed
.
add
(
key
);
}
}
// Added
command
s in Local
for
(
const
command
of
values
(
baseToLocal
.
added
))
{
if
(
conflict
Commands
.
has
(
command
))
{
// Added
key
s in Local
for
(
const
key
of
values
(
baseToLocal
.
added
))
{
if
(
conflict
s
.
has
(
key
))
{
continue
;
}
// Got added in remote
if
(
baseToRemote
.
added
.
has
(
command
))
{
if
(
baseToRemote
.
added
.
has
(
key
))
{
// Has different value
if
(
localToRemote
.
updated
.
has
(
command
))
{
conflict
Commands
.
add
(
command
);
if
(
localToRemote
.
updated
.
has
(
key
))
{
conflict
s
.
add
(
key
);
}
}
}
// Added
command
s in remote
for
(
const
command
of
values
(
baseToRemote
.
added
))
{
if
(
conflict
Commands
.
has
(
command
))
{
// Added
key
s in remote
for
(
const
key
of
values
(
baseToRemote
.
added
))
{
if
(
conflict
s
.
has
(
key
))
{
continue
;
}
// Got added in local
if
(
baseToLocal
.
added
.
has
(
command
))
{
if
(
baseToLocal
.
added
.
has
(
key
))
{
// Has different value
if
(
localToRemote
.
updated
.
has
(
command
))
{
conflict
Commands
.
add
(
command
);
if
(
localToRemote
.
updated
.
has
(
key
))
{
conflict
s
.
add
(
key
);
}
}
else
{
mergeContent
=
this
.
addKeybinding
(
mergeContent
,
eol
,
command
,
remoteByCommand
.
get
(
command
)
!
);
added
.
add
(
key
);
}
}
// Updated
command
s in Local
for
(
const
command
of
values
(
baseToLocal
.
updated
))
{
if
(
conflict
Commands
.
has
(
command
))
{
// Updated
key
s in Local
for
(
const
key
of
values
(
baseToLocal
.
updated
))
{
if
(
conflict
s
.
has
(
key
))
{
continue
;
}
// Got updated in remote
if
(
baseToRemote
.
updated
.
has
(
command
))
{
if
(
baseToRemote
.
updated
.
has
(
key
))
{
// Has different value
if
(
localToRemote
.
updated
.
has
(
command
))
{
conflict
Commands
.
add
(
command
);
if
(
localToRemote
.
updated
.
has
(
key
))
{
conflict
s
.
add
(
key
);
}
}
}
// Updated
command
s in Remote
for
(
const
command
of
values
(
baseToRemote
.
updated
))
{
if
(
conflict
Commands
.
has
(
command
))
{
// Updated
key
s in Remote
for
(
const
key
of
values
(
baseToRemote
.
updated
))
{
if
(
conflict
s
.
has
(
key
))
{
continue
;
}
// Got updated in local
if
(
baseToLocal
.
updated
.
has
(
command
))
{
if
(
baseToLocal
.
updated
.
has
(
key
))
{
// Has different value
if
(
localToRemote
.
updated
.
has
(
command
))
{
conflict
Commands
.
add
(
command
);
if
(
localToRemote
.
updated
.
has
(
key
))
{
conflict
s
.
add
(
key
);
}
}
else
{
// update
the command
mergeContent
=
this
.
updateKeybinding
(
mergeContent
,
eol
,
command
,
remoteByCommand
.
get
(
command
)
!
);
// update
d key
updated
.
add
(
key
);
}
}
return
{
added
,
removed
,
updated
,
conflicts
};
}
const
conflicts
:
{
command
:
string
,
local
:
IUserFriendlyKeybinding
[]
|
undefined
,
remote
:
IUserFriendlyKeybinding
[]
|
undefined
,
firstIndex
:
number
}[]
=
[];
for
(
const
command
of
values
(
conflictCommands
))
{
const
local
=
localByCommand
.
get
(
command
);
const
remote
=
remoteByCommand
.
get
(
command
);
mergeContent
=
this
.
updateKeybinding
(
mergeContent
,
eol
,
command
,
[...
local
||
[],
...
remote
||
[]]);
conflicts
.
push
({
command
,
local
,
remote
,
firstIndex
:
-
1
});
}
private
compareByKeybinding
(
from
:
Map
<
string
,
{
keybinding
:
IUserFriendlyKeybinding
,
normalized
:
IUserFriendlyKeybinding
}[]
>
,
to
:
Map
<
string
,
{
keybinding
:
IUserFriendlyKeybinding
,
normalized
:
IUserFriendlyKeybinding
}[]
>
):
ICompareResult
{
const
fromKeys
=
keys
(
from
);
const
toKeys
=
keys
(
to
);
const
added
=
toKeys
.
filter
(
key
=>
fromKeys
.
indexOf
(
key
)
===
-
1
).
reduce
((
r
,
key
)
=>
{
r
.
add
(
key
);
return
r
;
},
new
Set
<
string
>
());
const
removed
=
fromKeys
.
filter
(
key
=>
toKeys
.
indexOf
(
key
)
===
-
1
).
reduce
((
r
,
key
)
=>
{
r
.
add
(
key
);
return
r
;
},
new
Set
<
string
>
());
const
updated
:
Set
<
string
>
=
new
Set
<
string
>
();
const
allKeybindings
=
<
IUserFriendlyKeybinding
[]
>
parse
(
mergeContent
);
for
(
const
conflict
of
conflicts
)
{
conflict
.
firstIndex
=
findFirstIndex
(
allKeybindings
,
keybinding
=>
keybinding
.
command
===
conflict
.
command
||
keybinding
.
command
===
`-
${
conflict
.
command
}
`
);
}
// Sort reverse so that conflicts content is added from last
conflicts
.
sort
((
a
,
b
)
=>
b
.
firstIndex
-
a
.
firstIndex
);
const
tree
=
parseTree
(
mergeContent
);
for
(
const
{
firstIndex
,
local
,
remote
}
of
conflicts
)
{
const
firstNode
=
findNodeAtLocation
(
tree
,
[
firstIndex
])
!
;
const
startLocalOffset
=
contentUtil
.
getLineStartOffset
(
mergeContent
,
eol
,
firstNode
.
offset
)
-
eol
.
length
;
let
endLocalOffset
=
startLocalOffset
;
if
(
local
)
{
const
lastLocalValueNode
=
findNodeAtLocation
(
tree
,
[
firstIndex
+
local
.
length
-
1
])
!
;
endLocalOffset
=
contentUtil
.
getLineEndOffset
(
mergeContent
,
eol
,
lastLocalValueNode
.
offset
+
lastLocalValueNode
.
length
);
for
(
const
key
of
fromKeys
)
{
if
(
removed
.
has
(
key
))
{
continue
;
}
let
remoteOffset
=
endLocalOffset
;
if
(
remote
)
{
const
lastRemoteValueNode
=
findNodeAtLocation
(
tree
,
[
firstIndex
+
(
local
?
local
.
length
:
0
)
+
remote
.
length
-
1
])
!
;
remoteOffset
=
contentUtil
.
getLineEndOffset
(
mergeContent
,
eol
,
lastRemoteValueNode
.
offset
+
lastRemoteValueNode
.
length
);
const
value1
:
IUserFriendlyKeybinding
[]
=
from
.
get
(
key
)
!
.
map
(({
normalized
})
=>
normalized
)
;
const
value2
:
IUserFriendlyKeybinding
[]
=
to
.
get
(
key
)
!
.
map
(({
normalized
})
=>
normalized
);
if
(
!
equals
(
value1
,
value2
,
(
a
,
b
)
=>
this
.
isSameKeybinding
(
a
,
b
)))
{
updated
.
add
(
key
);
}
mergeContent
=
mergeContent
.
substring
(
0
,
startLocalOffset
)
+
`
${
eol
}
<<<<<<< local`
+
mergeContent
.
substring
(
startLocalOffset
,
endLocalOffset
)
+
`
${
eol
}
=======`
+
mergeContent
.
substring
(
endLocalOffset
,
remoteOffset
)
+
`
${
eol
}
>>>>>>> remote`
+
mergeContent
.
substring
(
remoteOffset
);
}
return
{
mergeContent
,
hasChanges
:
true
,
hasConflicts
:
conflicts
.
length
>
0
};
return
{
added
,
removed
,
updated
};
}
private
compare
(
from
:
Map
<
string
,
IUserFriendlyKeybinding
[]
>
,
to
:
Map
<
string
,
IUserFriendlyKeybinding
[]
>
):
{
added
:
Set
<
string
>
,
removed
:
Set
<
string
>
,
updated
:
Set
<
string
>
}
{
private
compare
ByCommand
(
from
:
Map
<
string
,
{
keybinding
:
IUserFriendlyKeybinding
,
normalized
:
IUserFriendlyKeybinding
}[]
>
,
to
:
Map
<
string
,
{
keybinding
:
IUserFriendlyKeybinding
,
normalized
:
IUserFriendlyKeybinding
}[]
>
):
ICompareResult
{
const
fromKeys
=
keys
(
from
);
const
toKeys
=
keys
(
to
);
const
added
=
toKeys
.
filter
(
key
=>
fromKeys
.
indexOf
(
key
)
===
-
1
).
reduce
((
r
,
key
)
=>
{
r
.
add
(
key
);
return
r
;
},
new
Set
<
string
>
());
...
...
@@ -187,9 +277,9 @@ export class KeybindingsMergeService implements IKeybindingsMergeService {
if
(
removed
.
has
(
key
))
{
continue
;
}
const
value1
:
IUserFriendlyKeybinding
[]
=
from
.
get
(
key
)
!
;
const
value2
:
IUserFriendlyKeybinding
[]
=
to
.
get
(
key
)
!
;
if
(
!
this
.
areSameKeybindings
(
value1
,
value2
))
{
const
value1
:
IUserFriendlyKeybinding
[]
=
from
.
get
(
key
)
!
.
map
(({
normalized
})
=>
normalized
)
;
const
value2
:
IUserFriendlyKeybinding
[]
=
to
.
get
(
key
)
!
.
map
(({
normalized
})
=>
normalized
)
;
if
(
!
this
.
areSameKeybindings
WithSameCommand
(
value1
,
value2
))
{
updated
.
add
(
key
);
}
}
...
...
@@ -197,7 +287,7 @@ export class KeybindingsMergeService implements IKeybindingsMergeService {
return
{
added
,
removed
,
updated
};
}
private
areSameKeybindings
(
value1
:
IUserFriendlyKeybinding
[],
value2
:
IUserFriendlyKeybinding
[]):
boolean
{
private
areSameKeybindings
WithSameCommand
(
value1
:
IUserFriendlyKeybinding
[],
value2
:
IUserFriendlyKeybinding
[]):
boolean
{
// Compare entries adding keybindings
if
(
!
equals
(
value1
.
filter
(({
command
})
=>
command
[
0
]
!==
'
-
'
),
value2
.
filter
(({
command
})
=>
command
[
0
]
!==
'
-
'
),
(
a
,
b
)
=>
this
.
isSameKeybinding
(
a
,
b
)))
{
return
false
;
...
...
@@ -230,7 +320,7 @@ export class KeybindingsMergeService implements IKeybindingsMergeService {
return
true
;
}
private
addKeybinding
(
content
:
string
,
eol
:
string
,
command
:
string
,
keybindings
:
IUserFriendlyKeybinding
[]):
string
{
private
addKeybinding
s
(
content
:
string
,
eol
:
string
,
keybindings
:
IUserFriendlyKeybinding
[]):
string
{
for
(
const
keybinding
of
keybindings
)
{
content
=
contentUtil
.
edit
(
content
,
eol
,
[
-
1
],
keybinding
);
}
...
...
@@ -247,7 +337,7 @@ export class KeybindingsMergeService implements IKeybindingsMergeService {
return
content
;
}
private
updateKeybinding
(
content
:
string
,
eol
:
string
,
command
:
string
,
keybindings
:
IUserFriendlyKeybinding
[]):
string
{
private
updateKeybinding
s
(
content
:
string
,
eol
:
string
,
command
:
string
,
keybindings
:
IUserFriendlyKeybinding
[]):
string
{
const
allKeybindings
=
<
IUserFriendlyKeybinding
[]
>
parse
(
content
);
const
location
=
findFirstIndex
(
allKeybindings
,
keybinding
=>
keybinding
.
command
===
command
||
keybinding
.
command
===
`-
${
command
}
`
);
// Remove all entries with this command
...
...
src/vs/workbench/services/keybinding/test/electron-browser/keybindingsMerge.test.ts
浏览文件 @
80ca1300
...
...
@@ -4,12 +4,23 @@
*--------------------------------------------------------------------------------------------*/
import
*
as
assert
from
'
assert
'
;
import
{
workbenchInstantiationService
}
from
'
vs/workbench/test/workbenchTestServices
'
;
import
{
OS
}
from
'
vs/base/common/platform
'
;
import
{
KeybindingsMergeService
}
from
'
vs/workbench/services/keybinding/common/keybindingsMerge
'
;
import
{
MockKeybindingService
}
from
'
vs/platform/keybinding/test/common/mockKeybindingService
'
;
import
{
ResolvedKeybinding
,
ChordKeybinding
,
SimpleKeybinding
}
from
'
vs/base/common/keyCodes
'
;
import
{
KeybindingParser
}
from
'
vs/base/common/keybindingParser
'
;
import
{
USLayoutResolvedKeybinding
}
from
'
vs/platform/keybinding/common/usLayoutResolvedKeybinding
'
;
let
testObject
:
KeybindingsMergeService
;
suiteSetup
(()
=>
testObject
=
workbenchInstantiationService
().
createInstance
(
KeybindingsMergeService
));
suiteSetup
(()
=>
{
testObject
=
new
KeybindingsMergeService
(
new
class
extends
MockKeybindingService
{
resolveUserBinding
(
userBinding
:
string
):
ResolvedKeybinding
[]
{
const
parts
=
<
SimpleKeybinding
[]
>
KeybindingParser
.
parseUserBinding
(
userBinding
);
return
[
new
USLayoutResolvedKeybinding
(
new
ChordKeybinding
(
parts
),
OS
)];
}
});
});
suite
(
'
KeybindingsMerge - No Conflicts
'
,
()
=>
{
...
...
@@ -31,6 +42,21 @@ suite('KeybindingsMerge - No Conflicts', () => {
assert
.
equal
(
actual
.
mergeContent
,
localContent
);
});
test
(
'
merge when local and remote has entries in different order
'
,
async
()
=>
{
const
localContent
=
stringify
([
{
key
:
'
alt+d
'
,
command
:
'
a
'
,
when
:
'
editorTextFocus && !editorReadonly
'
},
{
key
:
'
alt+a
'
,
command
:
'
a
'
,
when
:
'
editorTextFocus
'
}
]);
const
remoteContent
=
stringify
([
{
key
:
'
alt+a
'
,
command
:
'
a
'
,
when
:
'
editorTextFocus
'
},
{
key
:
'
alt+d
'
,
command
:
'
a
'
,
when
:
'
editorTextFocus && !editorReadonly
'
}
]);
const
actual
=
await
testObject
.
merge
(
localContent
,
remoteContent
,
null
);
assert
.
ok
(
!
actual
.
hasChanges
);
assert
.
ok
(
!
actual
.
hasConflicts
);
assert
.
equal
(
actual
.
mergeContent
,
localContent
);
});
test
(
'
merge when local and remote are same with multiple entries
'
,
async
()
=>
{
const
localContent
=
stringify
([
{
key
:
'
alt+c
'
,
command
:
'
a
'
,
when
:
'
editorTextFocus && !editorReadonly
'
},
...
...
@@ -209,16 +235,10 @@ suite('KeybindingsMerge - No Conflicts', () => {
{
key
:
'
alt+d
'
,
command
:
'
b
'
},
{
key
:
'
cmd+d
'
,
command
:
'
a
'
},
]);
const
expected
=
stringify
([
{
key
:
'
shift+c
'
,
command
:
'
c
'
},
{
key
:
'
alt+c
'
,
command
:
'
a
'
,
when
:
'
editorTextFocus && !editorReadonly
'
},
{
key
:
'
cmd+d
'
,
command
:
'
a
'
},
{
key
:
'
alt+d
'
,
command
:
'
b
'
},
]);
const
actual
=
await
testObject
.
merge
(
localContent
,
remoteContent
,
localContent
);
assert
.
ok
(
actual
.
hasChanges
);
assert
.
ok
(
!
actual
.
hasConflicts
);
assert
.
equal
(
actual
.
mergeContent
,
expected
);
assert
.
equal
(
actual
.
mergeContent
,
remoteContent
);
});
test
(
'
merge when remote has moved forwareded with multiple changes and local stays with base
'
,
async
()
=>
{
...
...
@@ -238,19 +258,10 @@ suite('KeybindingsMerge - No Conflicts', () => {
{
key
:
'
cmd+d
'
,
command
:
'
c
'
,
when
:
'
context1
'
},
{
key
:
'
cmd+c
'
,
command
:
'
-c
'
},
]);
const
expected
=
stringify
([
{
key
:
'
alt+d
'
,
command
:
'
a
'
,
when
:
'
editorTextFocus && !editorReadonly
'
},
{
key
:
'
alt+d
'
,
command
:
'
-a
'
},
{
key
:
'
cmd+e
'
,
command
:
'
d
'
},
{
key
:
'
cmd+d
'
,
command
:
'
c
'
,
when
:
'
context1
'
},
{
key
:
'
cmd+c
'
,
command
:
'
-c
'
},
{
key
:
'
alt+f
'
,
command
:
'
f
'
},
{
key
:
'
alt+d
'
,
command
:
'
-f
'
},
]);
const
actual
=
await
testObject
.
merge
(
localContent
,
remoteContent
,
localContent
);
assert
.
ok
(
actual
.
hasChanges
);
assert
.
ok
(
!
actual
.
hasConflicts
);
assert
.
equal
(
actual
.
mergeContent
,
expected
);
assert
.
equal
(
actual
.
mergeContent
,
remoteContent
);
});
test
(
'
merge when a new entry is added to local
'
,
async
()
=>
{
...
...
@@ -397,47 +408,6 @@ suite('KeybindingsMerge - No Conflicts', () => {
assert
.
equal
(
actual
.
mergeContent
,
expected
);
});
test
(
'
merge when local and remote has moved forwareded with no conflicts
'
,
async
()
=>
{
const
baseContent
=
stringify
([
{
key
:
'
alt+d
'
,
command
:
'
a
'
,
when
:
'
editorTextFocus && !editorReadonly
'
},
{
key
:
'
alt+c
'
,
command
:
'
-a
'
},
{
key
:
'
cmd+e
'
,
command
:
'
d
'
},
{
key
:
'
alt+a
'
,
command
:
'
f
'
},
{
key
:
'
alt+d
'
,
command
:
'
-f
'
},
{
key
:
'
cmd+d
'
,
command
:
'
c
'
,
when
:
'
context1
'
},
{
key
:
'
cmd+c
'
,
command
:
'
-c
'
},
]);
const
localContent
=
stringify
([
{
key
:
'
alt+d
'
,
command
:
'
-f
'
},
{
key
:
'
cmd+e
'
,
command
:
'
d
'
},
{
key
:
'
cmd+c
'
,
command
:
'
-c
'
},
{
key
:
'
cmd+d
'
,
command
:
'
c
'
,
when
:
'
context1
'
},
{
key
:
'
alt+a
'
,
command
:
'
f
'
},
{
key
:
'
alt+e
'
,
command
:
'
e
'
},
]);
const
remoteContent
=
stringify
([
{
key
:
'
alt+a
'
,
command
:
'
f
'
},
{
key
:
'
cmd+c
'
,
command
:
'
-c
'
},
{
key
:
'
cmd+d
'
,
command
:
'
d
'
},
{
key
:
'
alt+d
'
,
command
:
'
-f
'
},
{
key
:
'
alt+c
'
,
command
:
'
c
'
,
when
:
'
context1
'
},
{
key
:
'
alt+g
'
,
command
:
'
g
'
,
when
:
'
context2
'
},
]);
const
expected
=
stringify
([
{
key
:
'
alt+d
'
,
command
:
'
-f
'
},
{
key
:
'
cmd+d
'
,
command
:
'
d
'
},
{
key
:
'
cmd+c
'
,
command
:
'
-c
'
},
{
key
:
'
alt+c
'
,
command
:
'
c
'
,
when
:
'
context1
'
},
{
key
:
'
alt+a
'
,
command
:
'
f
'
},
{
key
:
'
alt+e
'
,
command
:
'
e
'
},
{
key
:
'
alt+g
'
,
command
:
'
g
'
,
when
:
'
context2
'
},
]);
const
actual
=
await
testObject
.
merge
(
localContent
,
remoteContent
,
baseContent
);
assert
.
ok
(
actual
.
hasChanges
);
assert
.
ok
(
!
actual
.
hasConflicts
);
assert
.
equal
(
actual
.
mergeContent
,
expected
);
});
});
suite
(
'
KeybindingsMerge - Conflicts
'
,
()
=>
{
...
...
@@ -449,21 +419,23 @@ suite('KeybindingsMerge - Conflicts', () => {
assert
.
ok
(
actual
.
hasChanges
);
assert
.
ok
(
actual
.
hasConflicts
);
assert
.
equal
(
actual
.
mergeContent
,
`
[
<<<<<<< local
`
<<<<<<< local
[
{
"key": "alt+d",
"command": "a",
"when": "editorTextFocus && !editorReadonly"
},
}
]
=======
[
{
"key": "alt+c",
"command": "a",
"when": "editorTextFocus && !editorReadonly"
}
>>>>>>> remote
]
`
);
]
>>>>>>> remote
`
);
});
test
(
'
merge when local and remote with different keybinding
'
,
async
()
=>
{
...
...
@@ -479,8 +451,8 @@ suite('KeybindingsMerge - Conflicts', () => {
assert
.
ok
(
actual
.
hasChanges
);
assert
.
ok
(
actual
.
hasConflicts
);
assert
.
equal
(
actual
.
mergeContent
,
`
[
<<<<<<< local
`
<<<<<<< local
[
{
"key": "alt+d",
"command": "a",
...
...
@@ -490,8 +462,10 @@ suite('KeybindingsMerge - Conflicts', () => {
"key": "alt+a",
"command": "-a",
"when": "editorTextFocus && !editorReadonly"
},
}
]
=======
[
{
"key": "alt+c",
"command": "a",
...
...
@@ -502,48 +476,8 @@ suite('KeybindingsMerge - Conflicts', () => {
"command": "-a",
"when": "editorTextFocus && !editorReadonly"
}
>>>>>>> remote
]`
);
});
test
(
'
merge when local and remote has entries in different order
'
,
async
()
=>
{
const
localContent
=
stringify
([
{
key
:
'
alt+d
'
,
command
:
'
a
'
,
when
:
'
editorTextFocus && !editorReadonly
'
},
{
key
:
'
alt+a
'
,
command
:
'
a
'
,
when
:
'
editorTextFocus
'
}
]);
const
remoteContent
=
stringify
([
{
key
:
'
alt+a
'
,
command
:
'
a
'
,
when
:
'
editorTextFocus
'
},
{
key
:
'
alt+d
'
,
command
:
'
a
'
,
when
:
'
editorTextFocus && !editorReadonly
'
}
]);
const
actual
=
await
testObject
.
merge
(
localContent
,
remoteContent
,
null
);
assert
.
ok
(
actual
.
hasChanges
);
assert
.
ok
(
actual
.
hasConflicts
);
assert
.
equal
(
actual
.
mergeContent
,
`[
<<<<<<< local
{
"key": "alt+d",
"command": "a",
"when": "editorTextFocus && !editorReadonly"
},
{
"key": "alt+a",
"command": "a",
"when": "editorTextFocus"
},
=======
{
"key": "alt+a",
"command": "a",
"when": "editorTextFocus"
},
{
"key": "alt+d",
"command": "a",
"when": "editorTextFocus && !editorReadonly"
}
>>>>>>> remote
]`
);
]
>>>>>>> remote`
);
});
test
(
'
merge when the entry is removed in local but updated in remote
'
,
async
()
=>
{
...
...
@@ -554,16 +488,17 @@ suite('KeybindingsMerge - Conflicts', () => {
assert
.
ok
(
actual
.
hasChanges
);
assert
.
ok
(
actual
.
hasConflicts
);
assert
.
equal
(
actual
.
mergeContent
,
`
[
<<<<<<< local
`
<<<<<<< local
[]
=======
[
{
"key": "alt+c",
"command": "a",
"when": "editorTextFocus && !editorReadonly"
}
>>>>>>> remote
]
`
);
]
>>>>>>> remote
`
);
});
test
(
'
merge when the entry is removed in local but updated in remote and a new entry is added in local
'
,
async
()
=>
{
...
...
@@ -574,20 +509,22 @@ suite('KeybindingsMerge - Conflicts', () => {
assert
.
ok
(
actual
.
hasChanges
);
assert
.
ok
(
actual
.
hasConflicts
);
assert
.
equal
(
actual
.
mergeContent
,
`[
`<<<<<<< local
[
{
"key": "alt+b",
"command": "b"
}
,
<<<<<<< local
}
]
=======
[
{
"key": "alt+c",
"command": "a",
"when": "editorTextFocus && !editorReadonly"
}
>>>>>>> remote
]
`
);
]
>>>>>>> remote
`
);
});
test
(
'
merge when the entry is removed in remote but updated in local
'
,
async
()
=>
{
...
...
@@ -598,16 +535,17 @@ suite('KeybindingsMerge - Conflicts', () => {
assert
.
ok
(
actual
.
hasChanges
);
assert
.
ok
(
actual
.
hasConflicts
);
assert
.
equal
(
actual
.
mergeContent
,
`
[
<<<<<<< local
`
<<<<<<< local
[
{
"key": "alt+c",
"command": "a",
"when": "editorTextFocus && !editorReadonly"
}
]
=======
>>>>>>> remote
]
`
);
[]
>>>>>>> remote
`
);
});
test
(
'
merge when the entry is removed in remote but updated in local and a new entry is added in remote
'
,
async
()
=>
{
...
...
@@ -618,20 +556,121 @@ suite('KeybindingsMerge - Conflicts', () => {
assert
.
ok
(
actual
.
hasChanges
);
assert
.
ok
(
actual
.
hasConflicts
);
assert
.
equal
(
actual
.
mergeContent
,
`
[
<<<<<<< local
`
<<<<<<< local
[
{
"key": "alt+c",
"command": "a",
"when": "editorTextFocus && !editorReadonly"
},
{
"key": "alt+b",
"command": "b"
}
]
=======
>>>>>>> remote
[
{
"key": "alt+b",
"command": "b"
}
]`
);
]
>>>>>>> remote`
);
});
test
(
'
merge when local and remote has moved forwareded with conflicts
'
,
async
()
=>
{
const
baseContent
=
stringify
([
{
key
:
'
alt+d
'
,
command
:
'
a
'
,
when
:
'
editorTextFocus && !editorReadonly
'
},
{
key
:
'
alt+c
'
,
command
:
'
-a
'
},
{
key
:
'
cmd+e
'
,
command
:
'
d
'
},
{
key
:
'
alt+a
'
,
command
:
'
f
'
},
{
key
:
'
alt+d
'
,
command
:
'
-f
'
},
{
key
:
'
cmd+d
'
,
command
:
'
c
'
,
when
:
'
context1
'
},
{
key
:
'
cmd+c
'
,
command
:
'
-c
'
},
]);
const
localContent
=
stringify
([
{
key
:
'
alt+d
'
,
command
:
'
-f
'
},
{
key
:
'
cmd+e
'
,
command
:
'
d
'
},
{
key
:
'
cmd+c
'
,
command
:
'
-c
'
},
{
key
:
'
cmd+d
'
,
command
:
'
c
'
,
when
:
'
context1
'
},
{
key
:
'
alt+a
'
,
command
:
'
f
'
},
{
key
:
'
alt+e
'
,
command
:
'
e
'
},
]);
const
remoteContent
=
stringify
([
{
key
:
'
alt+a
'
,
command
:
'
f
'
},
{
key
:
'
cmd+c
'
,
command
:
'
-c
'
},
{
key
:
'
cmd+d
'
,
command
:
'
d
'
},
{
key
:
'
alt+d
'
,
command
:
'
-f
'
},
{
key
:
'
alt+c
'
,
command
:
'
c
'
,
when
:
'
context1
'
},
{
key
:
'
alt+g
'
,
command
:
'
g
'
,
when
:
'
context2
'
},
]);
const
actual
=
await
testObject
.
merge
(
localContent
,
remoteContent
,
baseContent
);
assert
.
ok
(
actual
.
hasChanges
);
assert
.
ok
(
actual
.
hasConflicts
);
assert
.
equal
(
actual
.
mergeContent
,
`<<<<<<< local
[
{
"key": "alt+d",
"command": "-f"
},
{
"key": "cmd+d",
"command": "d"
},
{
"key": "cmd+c",
"command": "-c"
},
{
"key": "cmd+d",
"command": "c",
"when": "context1"
},
{
"key": "alt+a",
"command": "f"
},
{
"key": "alt+e",
"command": "e"
},
{
"key": "alt+g",
"command": "g",
"when": "context2"
}
]
=======
[
{
"key": "alt+a",
"command": "f"
},
{
"key": "cmd+c",
"command": "-c"
},
{
"key": "cmd+d",
"command": "d"
},
{
"key": "alt+d",
"command": "-f"
},
{
"key": "alt+c",
"command": "c",
"when": "context1"
},
{
"key": "alt+g",
"command": "g",
"when": "context2"
}
]
>>>>>>> remote`
);
});
});
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录