Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Léo-Paul Géneau
gitlab-ce
Commits
f62359c2
Commit
f62359c2
authored
Mar 23, 2018
by
Tim Zallmann
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Basic Setup for MR Showing
parent
06afa5a3
Changes
23
Hide whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
1006 additions
and
314 deletions
+1006
-314
app/assets/javascripts/api.js
app/assets/javascripts/api.js
+91
-59
app/assets/javascripts/ide/components/editor_mode_dropdown.vue
...ssets/javascripts/ide/components/editor_mode_dropdown.vue
+47
-24
app/assets/javascripts/ide/components/ide.vue
app/assets/javascripts/ide/components/ide.vue
+43
-42
app/assets/javascripts/ide/components/repo_editor.vue
app/assets/javascripts/ide/components/repo_editor.vue
+18
-5
app/assets/javascripts/ide/components/repo_tabs.vue
app/assets/javascripts/ide/components/repo_tabs.vue
+39
-33
app/assets/javascripts/ide/ide_router.js
app/assets/javascripts/ide/ide_router.js
+80
-1
app/assets/javascripts/ide/lib/common/model.js
app/assets/javascripts/ide/lib/common/model.js
+14
-0
app/assets/javascripts/ide/lib/diff/revert_patch.js
app/assets/javascripts/ide/lib/diff/revert_patch.js
+183
-0
app/assets/javascripts/ide/lib/editor.js
app/assets/javascripts/ide/lib/editor.js
+7
-0
app/assets/javascripts/ide/services/index.js
app/assets/javascripts/ide/services/index.js
+8
-1
app/assets/javascripts/ide/stores/actions.js
app/assets/javascripts/ide/stores/actions.js
+1
-0
app/assets/javascripts/ide/stores/actions/file.js
app/assets/javascripts/ide/stores/actions/file.js
+135
-46
app/assets/javascripts/ide/stores/actions/merge_request.js
app/assets/javascripts/ide/stores/actions/merge_request.js
+96
-0
app/assets/javascripts/ide/stores/actions/tree.js
app/assets/javascripts/ide/stores/actions/tree.js
+71
-49
app/assets/javascripts/ide/stores/getters.js
app/assets/javascripts/ide/stores/getters.js
+2
-0
app/assets/javascripts/ide/stores/mutation_types.js
app/assets/javascripts/ide/stores/mutation_types.js
+9
-0
app/assets/javascripts/ide/stores/mutations.js
app/assets/javascripts/ide/stores/mutations.js
+2
-0
app/assets/javascripts/ide/stores/mutations/file.js
app/assets/javascripts/ide/stores/mutations/file.js
+15
-0
app/assets/javascripts/ide/stores/mutations/merge_request.js
app/assets/javascripts/ide/stores/mutations/merge_request.js
+40
-0
app/assets/javascripts/ide/stores/mutations/project.js
app/assets/javascripts/ide/stores/mutations/project.js
+1
-0
app/assets/javascripts/ide/stores/state.js
app/assets/javascripts/ide/stores/state.js
+1
-0
app/assets/javascripts/ide/stores/utils.js
app/assets/javascripts/ide/stores/utils.js
+43
-11
app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue
.../vue_merge_request_widget/components/mr_widget_header.vue
+60
-43
No files found.
app/assets/javascripts/api.js
View file @
f62359c2
...
...
@@ -10,6 +10,9 @@ const Api = {
projectsPath
:
'
/api/:version/projects.json
'
,
projectPath
:
'
/api/:version/projects/:id
'
,
projectLabelsPath
:
'
/:namespace_path/:project_path/labels
'
,
mergeRequestPath
:
'
/api/:version/projects/:id/merge_requests/:mrid
'
,
mergeRequestChangesPath
:
'
/api/:version/projects/:id/merge_requests/:mrid/changes
'
,
groupLabelsPath
:
'
/groups/:namespace_path/-/labels
'
,
licensePath
:
'
/api/:version/templates/licenses/:key
'
,
gitignorePath
:
'
/api/:version/templates/gitignores/:key
'
,
...
...
@@ -22,25 +25,27 @@ const Api = {
createBranchPath
:
'
/api/:version/projects/:id/repository/branches
'
,
group
(
groupId
,
callback
)
{
const
url
=
Api
.
buildUrl
(
Api
.
groupPath
)
.
replace
(
'
:id
'
,
groupId
);
return
axios
.
get
(
url
)
.
then
(({
data
})
=>
{
callback
(
data
);
const
url
=
Api
.
buildUrl
(
Api
.
groupPath
).
replace
(
'
:id
'
,
groupId
);
return
axios
.
get
(
url
).
then
(({
data
})
=>
{
callback
(
data
);
return
data
;
});
return
data
;
});
},
// Return groups list. Filtered by query
groups
(
query
,
options
,
callback
=
$
.
noop
)
{
const
url
=
Api
.
buildUrl
(
Api
.
groupsPath
);
return
axios
.
get
(
url
,
{
params
:
Object
.
assign
({
search
:
query
,
per_page
:
20
,
},
options
),
})
return
axios
.
get
(
url
,
{
params
:
Object
.
assign
(
{
search
:
query
,
per_page
:
20
,
},
options
,
),
})
.
then
(({
data
})
=>
{
callback
(
data
);
...
...
@@ -51,12 +56,13 @@ const Api = {
// Return namespaces list. Filtered by query
namespaces
(
query
,
callback
)
{
const
url
=
Api
.
buildUrl
(
Api
.
namespacesPath
);
return
axios
.
get
(
url
,
{
params
:
{
search
:
query
,
per_page
:
20
,
},
})
return
axios
.
get
(
url
,
{
params
:
{
search
:
query
,
per_page
:
20
,
},
})
.
then
(({
data
})
=>
callback
(
data
));
},
...
...
@@ -73,9 +79,10 @@ const Api = {
defaults
.
membership
=
true
;
}
return
axios
.
get
(
url
,
{
params
:
Object
.
assign
(
defaults
,
options
),
})
return
axios
.
get
(
url
,
{
params
:
Object
.
assign
(
defaults
,
options
),
})
.
then
(({
data
})
=>
{
callback
(
data
);
...
...
@@ -85,8 +92,28 @@ const Api = {
// Return single project
project
(
projectPath
)
{
const
url
=
Api
.
buildUrl
(
Api
.
projectPath
)
.
replace
(
'
:id
'
,
encodeURIComponent
(
projectPath
));
const
url
=
Api
.
buildUrl
(
Api
.
projectPath
).
replace
(
'
:id
'
,
encodeURIComponent
(
projectPath
),
);
return
axios
.
get
(
url
);
},
// Return Merge Request for project
mergeRequest
(
projectPath
,
mergeRequestId
)
{
const
url
=
Api
.
buildUrl
(
Api
.
mergeRequestPath
)
.
replace
(
'
:id
'
,
encodeURIComponent
(
projectPath
))
.
replace
(
'
:mrid
'
,
mergeRequestId
);
return
axios
.
get
(
url
);
},
// Return Merge Request Changes
mergeRequestChanges
(
projectPath
,
mergeRequestId
)
{
const
url
=
Api
.
buildUrl
(
Api
.
mergeRequestChangesPath
)
.
replace
(
'
:id
'
,
encodeURIComponent
(
projectPath
))
.
replace
(
'
:mrid
'
,
mergeRequestId
);
return
axios
.
get
(
url
);
},
...
...
@@ -99,33 +126,39 @@ const Api = {
.
replace
(
'
:namespace_path
'
,
namespacePath
)
.
replace
(
'
:project_path
'
,
projectPath
);
}
else
{
url
=
Api
.
buildUrl
(
Api
.
groupLabelsPath
).
replace
(
'
:namespace_path
'
,
namespacePath
);
url
=
Api
.
buildUrl
(
Api
.
groupLabelsPath
).
replace
(
'
:namespace_path
'
,
namespacePath
,
);
}
return
axios
.
post
(
url
,
{
label
:
data
,
})
return
axios
.
post
(
url
,
{
label
:
data
,
})
.
then
(
res
=>
callback
(
res
.
data
))
.
catch
(
e
=>
callback
(
e
.
response
.
data
));
},
// Return group projects list. Filtered by query
groupProjects
(
groupId
,
query
,
callback
)
{
const
url
=
Api
.
buildUrl
(
Api
.
groupProjectsPath
)
.
replace
(
'
:id
'
,
groupId
);
return
axios
.
get
(
url
,
{
params
:
{
search
:
query
,
per_page
:
20
,
},
})
const
url
=
Api
.
buildUrl
(
Api
.
groupProjectsPath
)
.
replace
(
'
:id
'
,
groupId
);
return
axios
.
get
(
url
,
{
params
:
{
search
:
query
,
per_page
:
20
,
},
})
.
then
(({
data
})
=>
callback
(
data
));
},
commitMultiple
(
id
,
data
)
{
// see https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions
const
url
=
Api
.
buildUrl
(
Api
.
commitPath
)
.
replace
(
'
:id
'
,
encodeURIComponent
(
id
));
const
url
=
Api
.
buildUrl
(
Api
.
commitPath
).
replace
(
'
:id
'
,
encodeURIComponent
(
id
),
);
return
axios
.
post
(
url
,
JSON
.
stringify
(
data
),
{
headers
:
{
'
Content-Type
'
:
'
application/json; charset=utf-8
'
,
...
...
@@ -136,39 +169,34 @@ const Api = {
branchSingle
(
id
,
branch
)
{
const
url
=
Api
.
buildUrl
(
Api
.
branchSinglePath
)
.
replace
(
'
:id
'
,
encodeURIComponent
(
id
))
.
replace
(
'
:branch
'
,
branch
);
.
replace
(
'
:branch
'
,
encodeURIComponent
(
branch
)
);
return
axios
.
get
(
url
);
},
// Return text for a specific license
licenseText
(
key
,
data
,
callback
)
{
const
url
=
Api
.
buildUrl
(
Api
.
licensePath
)
.
replace
(
'
:key
'
,
key
);
return
axios
.
get
(
url
,
{
params
:
data
,
})
const
url
=
Api
.
buildUrl
(
Api
.
licensePath
)
.
replace
(
'
:key
'
,
key
);
return
axios
.
get
(
url
,
{
params
:
data
,
})
.
then
(
res
=>
callback
(
res
.
data
));
},
gitignoreText
(
key
,
callback
)
{
const
url
=
Api
.
buildUrl
(
Api
.
gitignorePath
)
.
replace
(
'
:key
'
,
key
);
return
axios
.
get
(
url
)
.
then
(({
data
})
=>
callback
(
data
));
const
url
=
Api
.
buildUrl
(
Api
.
gitignorePath
).
replace
(
'
:key
'
,
key
);
return
axios
.
get
(
url
).
then
(({
data
})
=>
callback
(
data
));
},
gitlabCiYml
(
key
,
callback
)
{
const
url
=
Api
.
buildUrl
(
Api
.
gitlabCiYmlPath
)
.
replace
(
'
:key
'
,
key
);
return
axios
.
get
(
url
)
.
then
(({
data
})
=>
callback
(
data
));
const
url
=
Api
.
buildUrl
(
Api
.
gitlabCiYmlPath
).
replace
(
'
:key
'
,
key
);
return
axios
.
get
(
url
).
then
(({
data
})
=>
callback
(
data
));
},
dockerfileYml
(
key
,
callback
)
{
const
url
=
Api
.
buildUrl
(
Api
.
dockerfilePath
).
replace
(
'
:key
'
,
key
);
return
axios
.
get
(
url
)
.
then
(({
data
})
=>
callback
(
data
));
return
axios
.
get
(
url
).
then
(({
data
})
=>
callback
(
data
));
},
issueTemplate
(
namespacePath
,
projectPath
,
key
,
type
,
callback
)
{
...
...
@@ -177,7 +205,8 @@ const Api = {
.
replace
(
'
:type
'
,
type
)
.
replace
(
'
:project_path
'
,
projectPath
)
.
replace
(
'
:namespace_path
'
,
namespacePath
);
return
axios
.
get
(
url
)
return
axios
.
get
(
url
)
.
then
(({
data
})
=>
callback
(
null
,
data
))
.
catch
(
callback
);
},
...
...
@@ -185,10 +214,13 @@ const Api = {
users
(
query
,
options
)
{
const
url
=
Api
.
buildUrl
(
this
.
usersPath
);
return
axios
.
get
(
url
,
{
params
:
Object
.
assign
({
search
:
query
,
per_page
:
20
,
},
options
),
params
:
Object
.
assign
(
{
search
:
query
,
per_page
:
20
,
},
options
,
),
});
},
...
...
app/assets/javascripts/ide/components/editor_mode_dropdown.vue
View file @
f62359c2
<
script
>
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
export
default
{
components
:
{
Icon
,
export
default
{
components
:
{
Icon
,
},
props
:
{
hasChanges
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
props
:
{
hasChanges
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
viewer
:
{
type
:
String
,
required
:
true
,
},
showShadow
:
{
type
:
Boolean
,
required
:
true
,
},
hasMergeRequest
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
methods
:
{
changeMode
(
mode
)
{
this
.
$emit
(
'
click
'
,
mode
);
},
viewer
:
{
type
:
String
,
required
:
true
,
},
};
showShadow
:
{
type
:
Boolean
,
required
:
true
,
},
},
methods
:
{
changeMode
(
mode
)
{
this
.
$emit
(
'
click
'
,
mode
);
},
},
};
</
script
>
<
template
>
...
...
@@ -43,7 +48,10 @@
}"
data-toggle="dropdown"
>
<template
v-if=
"viewer === 'editor'"
>
<template
v-if=
"viewer === 'mrdiff'"
>
{{
__
(
'
Reviewing (merge request)
'
)
}}
</
template
>
<
template
v-else-if=
"viewer === 'editor'"
>
{{
__
(
'
Editing
'
)
}}
</
template
>
<
template
v-else
>
...
...
@@ -57,6 +65,21 @@
</button>
<div
class=
"dropdown-menu dropdown-menu-selectable dropdown-open-left"
>
<ul>
<li
v-if=
"hasMergeRequest"
>
<a
href=
"#"
@
click.prevent=
"changeMode('mrdiff')"
:class=
"{
'is-active': viewer === 'mrdiff',
}"
>
<strong
class=
"dropdown-menu-inner-title"
>
{{ __('Reviewing (merge request)') }}
</strong>
<span
class=
"dropdown-menu-inner-content"
>
{{ __('Compare changes of the merge request') }}
</span>
</a>
</li>
<li
v-if=
"hasMergeRequest"
role=
"separator"
class=
"divider"
></li>
<li>
<a
href=
"#"
...
...
app/assets/javascripts/ide/components/ide.vue
View file @
f62359c2
<
script
>
import
{
mapState
,
mapGetters
}
from
'
vuex
'
;
import
ideSidebar
from
'
./ide_side_bar.vue
'
;
import
ideContextbar
from
'
./ide_context_bar.vue
'
;
import
repoTabs
from
'
./repo_tabs.vue
'
;
import
repoFileButtons
from
'
./repo_file_buttons.vue
'
;
import
ideStatusBar
from
'
./ide_status_bar.vue
'
;
import
repoEditor
from
'
./repo_editor.vue
'
;
import
{
mapState
,
mapGetters
}
from
'
vuex
'
;
import
ideSidebar
from
'
./ide_side_bar.vue
'
;
import
ideContextbar
from
'
./ide_context_bar.vue
'
;
import
repoTabs
from
'
./repo_tabs.vue
'
;
import
repoFileButtons
from
'
./repo_file_buttons.vue
'
;
import
ideStatusBar
from
'
./ide_status_bar.vue
'
;
import
repoEditor
from
'
./repo_editor.vue
'
;
export
default
{
components
:
{
ideSidebar
,
ideContextbar
,
repoTabs
,
repoFileButtons
,
ideStatusBar
,
repoEditor
,
export
default
{
components
:
{
ideSidebar
,
ideContextbar
,
repoTabs
,
repoFileButtons
,
ideStatusBar
,
repoEditor
,
},
props
:
{
emptyStateSvgPath
:
{
type
:
String
,
required
:
true
,
},
props
:
{
emptyStateSvgPath
:
{
type
:
String
,
required
:
true
,
},
noChangesStateSvgPath
:
{
type
:
String
,
required
:
true
,
},
committedStateSvgPath
:
{
type
:
String
,
required
:
true
,
},
noChangesStateSvgPath
:
{
type
:
String
,
required
:
true
,
},
com
puted
:
{
...
mapState
([
'
changedFiles
'
,
'
openFiles
'
,
'
viewer
'
])
,
...
mapGetters
([
'
activeFile
'
,
'
hasChanges
'
])
,
com
mittedStateSvgPath
:
{
type
:
String
,
required
:
true
,
},
mounted
()
{
const
returnValue
=
'
Are you sure you want to lose unsaved changes?
'
;
window
.
onbeforeunload
=
e
=>
{
if
(
!
this
.
changedFiles
.
length
)
return
undefined
;
},
computed
:
{
...
mapState
([
'
changedFiles
'
,
'
openFiles
'
,
'
viewer
'
]),
...
mapGetters
([
'
activeFile
'
,
'
hasChanges
'
,
'
hasMergeRequest
'
]),
},
mounted
()
{
const
returnValue
=
'
Are you sure you want to lose unsaved changes?
'
;
window
.
onbeforeunload
=
e
=>
{
if
(
!
this
.
changedFiles
.
length
)
return
undefined
;
Object
.
assign
(
e
,
{
returnValue
,
});
return
returnValue
;
};
},
};
Object
.
assign
(
e
,
{
returnValue
,
});
return
returnValue
;
};
},
};
</
script
>
<
template
>
...
...
@@ -63,6 +63,7 @@
:files=
"openFiles"
:viewer=
"viewer"
:has-changes=
"hasChanges"
:has-merge-request=
"hasMergeRequest"
/>
<repo-editor
class=
"multi-file-edit-pane-content"
...
...
app/assets/javascripts/ide/components/repo_editor.vue
View file @
f62359c2
...
...
@@ -70,7 +70,9 @@ export default {
this
.
getRawFileData
(
this
.
file
)
.
then
(()
=>
{
const
viewerPromise
=
this
.
delayViewerUpdated
?
this
.
updateViewer
(
'
editor
'
)
:
Promise
.
resolve
();
const
viewerPromise
=
this
.
delayViewerUpdated
?
this
.
updateViewer
(
'
editor
'
)
:
Promise
.
resolve
();
return
viewerPromise
;
})
...
...
@@ -78,8 +80,15 @@ export default {
this
.
updateDelayViewerUpdated
(
false
);
this
.
createEditorInstance
();
})
.
catch
((
err
)
=>
{
flash
(
'
Error setting up monaco. Please try again.
'
,
'
alert
'
,
document
,
null
,
false
,
true
);
.
catch
(
err
=>
{
flash
(
'
Error setting up monaco. Please try again.
'
,
'
alert
'
,
document
,
null
,
false
,
true
,
);
throw
err
;
});
},
...
...
@@ -101,9 +110,13 @@ export default {
this
.
model
=
this
.
editor
.
createModel
(
this
.
file
);
this
.
editor
.
attachModel
(
this
.
model
);
if
(
this
.
viewer
===
'
mrdiff
'
)
{
this
.
editor
.
attachMergeRequestModel
(
this
.
model
);
}
else
{
this
.
editor
.
attachModel
(
this
.
model
);
}
this
.
model
.
onChange
(
(
model
)
=>
{
this
.
model
.
onChange
(
model
=>
{
const
{
file
}
=
model
;
if
(
file
.
active
)
{
...
...
app/assets/javascripts/ide/components/repo_tabs.vue
View file @
f62359c2
<
script
>
import
{
mapActions
}
from
'
vuex
'
;
import
RepoTab
from
'
./repo_tab.vue
'
;
import
EditorMode
from
'
./editor_mode_dropdown.vue
'
;
import
{
mapActions
}
from
'
vuex
'
;
import
RepoTab
from
'
./repo_tab.vue
'
;
import
EditorMode
from
'
./editor_mode_dropdown.vue
'
;
export
default
{
components
:
{
RepoTab
,
EditorMode
,
export
default
{
components
:
{
RepoTab
,
EditorMode
,
},
props
:
{
files
:
{
type
:
Array
,
required
:
true
,
},
props
:
{
files
:
{
type
:
Array
,
required
:
true
,
},
viewer
:
{
type
:
String
,
required
:
true
,
},
hasChanges
:
{
type
:
Boolean
,
required
:
true
,
},
viewer
:
{
type
:
String
,
required
:
true
,
},
data
()
{
return
{
showShadow
:
false
,
};
hasChanges
:
{
type
:
Boolean
,
required
:
true
,
},
updated
()
{
if
(
!
this
.
$refs
.
tabsScroller
)
return
;
this
.
showShadow
=
this
.
$refs
.
tabsScroller
.
scrollWidth
>
this
.
$refs
.
tabsScroller
.
offsetWidth
;
},
methods
:
{
...
mapActions
([
'
updateViewer
'
]),
hasMergeRequest
:
{
type
:
Boolean
,
required
:
true
,
default
:
false
,
},
};
},
data
()
{
return
{
showShadow
:
false
,
};
},
updated
()
{
if
(
!
this
.
$refs
.
tabsScroller
)
return
;
this
.
showShadow
=
this
.
$refs
.
tabsScroller
.
scrollWidth
>
this
.
$refs
.
tabsScroller
.
offsetWidth
;
},
methods
:
{
...
mapActions
([
'
updateViewer
'
]),
},
};
</
script
>
<
template
>
...
...
@@ -55,6 +60,7 @@
:viewer=
"viewer"
:show-shadow=
"showShadow"
:has-changes=
"hasChanges"
:has-merge-request=
"hasMergeRequest"
@
click=
"updateViewer"
/>
</div>
...
...
app/assets/javascripts/ide/ide_router.js
View file @
f62359c2
...
...
@@ -2,6 +2,7 @@ import Vue from 'vue';
import
VueRouter
from
'
vue-router
'
;
import
flash
from
'
~/flash
'
;
import
store
from
'
./stores
'
;
import
{
getTreeEntry
}
from
'
./stores/utils
'
;
Vue
.
use
(
VueRouter
);
...
...
@@ -44,7 +45,7 @@ const router = new VueRouter({
component
:
EmptyRouterComponent
,
},
{
path
:
'
m
r
/:mrid
'
,
path
:
'
m
erge_requests
/:mrid
'
,
component
:
EmptyRouterComponent
,
},
],
...
...
@@ -96,6 +97,84 @@ router.beforeEach((to, from, next) => {
);
throw
e
;
});
}
else
if
(
to
.
params
.
mrid
)
{
store
.
dispatch
(
'
updateViewer
'
,
'
mrdiff
'
);
store
.
dispatch
(
'
getMergeRequestData
'
,
{
projectId
:
fullProjectId
,
mergeRequestId
:
to
.
params
.
mrid
,
})
.
then
(
mr
=>
{
store
.
dispatch
(
'
getBranchData
'
,
{
projectId
:
fullProjectId
,
branchId
:
mr
.
source_branch
,
});
store
.
dispatch
(
'
getFiles
'
,
{
projectId
:
fullProjectId
,
branchId
:
mr
.
source_branch
,
})
.
then
(()
=>
{
store
.
dispatch
(
'
getMergeRequestChanges
'
,
{
projectId
:
fullProjectId
,
mergeRequestId
:
to
.
params
.
mrid
,
})
.
then
(
mrChanges
=>
{
if
(
mrChanges
.
changes
.
length
>
0
)
{
}
mrChanges
.
changes
.
forEach
((
change
,
ind
)
=>
{
console
.
log
(
`CHANGE :
${
ind
}
: `
,
change
);
const
changeTreeEntry
=
store
.
state
.
entries
[
change
.
new_path
];
console
.
log
(
'
Tree Entry for the change
'
,
changeTreeEntry
,
change
.
diff
,
);
if
(
changeTreeEntry
)
{
store
.
dispatch
(
'
setFileMrDiff
'
,
{
file
:
changeTreeEntry
,
mrDiff
:
change
.
diff
,
});
store
.
dispatch
(
'
setFileTargetBranch
'
,
{
file
:
changeTreeEntry
,
targetBranch
:
mrChanges
.
target_branch
,
});
if
(
ind
===
0
)
{
store
.
dispatch
(
'
getFileData
'
,
change
.
new_path
);
}
else
{
// TODO : Implement Tab reloading
store
.
dispatch
(
'
preloadFileTab
'
,
changeTreeEntry
);
}
}
else
{
console
.
warn
(
`No Tree Entry for
${
change
.
new_path
}
`
);
}
});
})
.
catch
(
e
=>
{
flash
(
'
Error while loading the merge request changes. Please try again.
'
,
);
throw
e
;
});
})
.
catch
(
e
=>
{
flash
(
'
Error while loading the branch files. Please try again.
'
,
);
throw
e
;
});
})
.
catch
(
e
=>
{
throw
e
;
});
}
})
.
catch
(
e
=>
{
...
...
app/assets/javascripts/ide/lib/common/model.js
View file @
f62359c2
...
...
@@ -22,6 +22,16 @@ export default class Model {
)),
);
if
(
this
.
file
.
targetBranch
)
{
this
.
disposable
.
add
(
(
this
.
targetModel
=
this
.
monaco
.
editor
.
createModel
(
this
.
file
.
targetRaw
,
undefined
,
new
this
.
monaco
.
Uri
(
null
,
null
,
`target/
${
this
.
file
.
path
}
`
),
)),
);
}
this
.
events
=
new
Map
();
this
.
updateContent
=
this
.
updateContent
.
bind
(
this
);
...
...
@@ -58,6 +68,10 @@ export default class Model {
return
this
.
originalModel
;
}
getTargetModel
()
{
return
this
.
targetModel
;
}
setValue
(
value
)
{
this
.
getModel
().
setValue
(
value
);
}
...
...
app/assets/javascripts/ide/lib/diff/revert_patch.js
0 → 100644
View file @
f62359c2
export
function
revertPatch
(
source
,
uniDiff
,
options
=
{})
{
if
(
typeof
uniDiff
===
'
string
'
)
{
uniDiff
=
parsePatch
(
uniDiff
);
}
if
(
Array
.
isArray
(
uniDiff
))
{
if
(
uniDiff
.
length
>
1
)
{
throw
new
Error
(
'
applyPatch only works with a single input.
'
);
}
uniDiff
=
uniDiff
[
0
];
}
// Apply the diff to the input
let
lines
=
source
.
split
(
/
\r\n
|
[\n\v\f\r\x
85
]
/
),
delimiters
=
source
.
match
(
/
\r\n
|
[\n\v\f\r\x
85
]
/g
)
||
[],
hunks
=
uniDiff
.
hunks
,
compareLine
=
options
.
compareLine
||
((
lineNumber
,
line
,
operation
,
patchContent
)
=>
line
===
patchContent
),
errorCount
=
0
,
fuzzFactor
=
options
.
fuzzFactor
||
0
,
minLine
=
0
,
offset
=
0
,
removeEOFNL
,
addEOFNL
;
/**
* Checks if the hunk exactly fits on the provided location
*/
function
hunkFits
(
hunk
,
toPos
)
{
for
(
let
j
=
0
;
j
<
hunk
.
lines
.
length
;
j
++
)
{
let
line
=
hunk
.
lines
[
j
],
operation
=
line
[
0
],
content
=
line
.
substr
(
1
);
if
(
operation
===
'
'
||
operation
===
'
-
'
)
{
// Context sanity check
if
(
!
compareLine
(
toPos
+
1
,
lines
[
toPos
],
operation
,
content
))
{
errorCount
++
;
if
(
errorCount
>
fuzzFactor
)
{
return
false
;
}
}
toPos
++
;
}
}
return
true
;
}
// Search best fit offsets for each hunk based on the previous ones
for
(
let
i
=
0
;
i
<
hunks
.
length
;
i
++
)
{
let
hunk
=
hunks
[
i
],
maxLine
=
lines
.
length
-
hunk
.
oldLines
,
localOffset
=
0
,
toPos
=
offset
+
hunk
.
oldStart
-
1
;
const
iterator
=
distanceIterator
(
toPos
,
minLine
,
maxLine
);
for
(;
localOffset
!==
undefined
;
localOffset
=
iterator
())
{
if
(
hunkFits
(
hunk
,
toPos
+
localOffset
))
{
hunk
.
offset
=
offset
+=
localOffset
;
break
;
}
}
if
(
localOffset
===
undefined
)
{
return
false
;
}
// Set lower text limit to end of the current hunk, so next ones don't try
// to fit over already patched text
minLine
=
hunk
.
offset
+
hunk
.
oldStart
+
hunk
.
oldLines
;
}
// Apply patch hunks
let
diffOffset
=
0
;
for
(
let
i
=
0
;
i
<
hunks
.
length
;
i
++
)
{
let
hunk
=
hunks
[
i
],
toPos
=
hunk
.
oldStart
+
hunk
.
offset
+
diffOffset
-
1
;
diffOffset
+=
hunk
.
newLines
-
hunk
.
oldLines
;
if
(
toPos
<
0
)
{
// Creating a new file
toPos
=
0
;
}
for
(
let
j
=
0
;
j
<
hunk
.
lines
.
length
;
j
++
)
{
let
line
=
hunk
.
lines
[
j
],
operation
=
line
[
0
],
content
=
line
.
substr
(
1
),
delimiter
=
hunk
.
linedelimiters
[
j
];
// Turned around the commands to revert the applying
if
(
operation
===
'
'
)
{
toPos
++
;
}
else
if
(
operation
===
'
+
'
)
{
lines
.
splice
(
toPos
,
1
);
delimiters
.
splice
(
toPos
,
1
);
/* istanbul ignore else */
}
else
if
(
operation
===
'
-
'
)
{
lines
.
splice
(
toPos
,
0
,
content
);
delimiters
.
splice
(
toPos
,
0
,
delimiter
);
toPos
++
;
}
else
if
(
operation
===
'
\\
'
)
{
const
previousOperation
=
hunk
.
lines
[
j
-
1
]
?
hunk
.
lines
[
j
-
1
][
0
]
:
null
;
if
(
previousOperation
===
'
+
'
)
{
removeEOFNL
=
true
;
}
else
if
(
previousOperation
===
'
-
'
)
{
addEOFNL
=
true
;
}
}
}
}
// Handle EOFNL insertion/removal
if
(
removeEOFNL
)
{
while
(
!
lines
[
lines
.
length
-
1
])
{
lines
.
pop
();
delimiters
.
pop
();
}
}
else
if
(
addEOFNL
)
{
lines
.
push
(
''
);
delimiters
.
push
(
'
\n
'
);
}
for
(
let
_k
=
0
;
_k
<
lines
.
length
-
1
;
_k
++
)
{
lines
[
_k
]
=
lines
[
_k
]
+
delimiters
[
_k
];
}
return
lines
.
join
(
''
);
}
/**
* Utility Function
* @param {*} start
* @param {*} minLine
* @param {*} maxLine
*/
const
distanceIterator
=
function
(
start
,
minLine
,
maxLine
)
{
let
wantForward
=
true
,
backwardExhausted
=
false
,
forwardExhausted
=
false
,
localOffset
=
1
;
return
function
iterator
()
{
if
(
wantForward
&&
!
forwardExhausted
)
{
if
(
backwardExhausted
)
{
localOffset
++
;
}
else
{
wantForward
=
false
;
}
// Check if trying to fit beyond text length, and if not, check it fits
// after offset location (or desired location on first iteration)
if
(
start
+
localOffset
<=
maxLine
)
{
return
localOffset
;
}
forwardExhausted
=
true
;
}
if
(
!
backwardExhausted
)
{
if
(
!
forwardExhausted
)
{
wantForward
=
true
;
}
// Check if trying to fit before text beginning, and if not, check it fits
// before offset location
if
(
minLine
<=
start
-
localOffset
)
{
return
-
localOffset
++
;
}
backwardExhausted
=
true
;
return
iterator
();
}
// We tried to fit hunk before text beginning and beyond text length, then
// hunk can't fit on the text. Return undefined
};
};
app/assets/javascripts/ide/lib/editor.js
View file @
f62359c2
...
...
@@ -109,6 +109,13 @@ export default class Editor {
if
(
this
.
dirtyDiffController
)
this
.
dirtyDiffController
.
reDecorate
(
model
);
}
attachMergeRequestModel
(
model
)
{
this
.
instance
.
setModel
({
original
:
model
.
getTargetModel
(),
modified
:
model
.
getModel
(),
});
}
setupMonacoTheme
()
{
this
.
monaco
.
editor
.
defineTheme
(
gitlabTheme
.
themeName
,
...
...
app/assets/javascripts/ide/services/index.js
View file @
f62359c2
...
...
@@ -20,12 +20,19 @@ export default {
return
Promise
.
resolve
(
file
.
raw
);
}
return
Vue
.
http
.
get
(
file
.
rawPath
,
{
params
:
{
format
:
'
json
'
}
})
return
Vue
.
http
.
get
(
file
.
rawPath
,
{
params
:
{
format
:
'
json
'
}
})
.
then
(
res
=>
res
.
text
());
},
getProjectData
(
namespace
,
project
)
{
return
Api
.
project
(
`
${
namespace
}
/
${
project
}
`
);
},
getProjectMergeRequestData
(
projectId
,
mergeRequestId
)
{
return
Api
.
mergeRequest
(
projectId
,
mergeRequestId
);
},
getProjectMergeRequestChanges
(
projectId
,
mergeRequestId
)
{
return
Api
.
mergeRequestChanges
(
projectId
,
mergeRequestId
);
},
getBranchData
(
projectId
,
currentBranchId
)
{
return
Api
.
branchSingle
(
projectId
,
currentBranchId
);
},
...
...
app/assets/javascripts/ide/stores/actions.js
View file @
f62359c2
...
...
@@ -119,3 +119,4 @@ export const updateDelayViewerUpdated = ({ commit }, delay) => {
export
*
from
'
./actions/tree
'
;
export
*
from
'
./actions/file
'
;
export
*
from
'
./actions/project
'
;
export
*
from
'
./actions/merge_request
'
;
app/assets/javascripts/ide/stores/actions/file.js
View file @
f62359c2
import
{
normalizeHeaders
}
from
'
~/lib/utils/common_utils
'
;
import
{
parsePatch
,
applyPatches
}
from
'
diff
'
;
import
{
revertPatch
}
from
'
../../lib/diff/revert_patch
'
;
import
flash
from
'
~/flash
'
;
import
eventHub
from
'
../../eventhub
'
;
import
service
from
'
../../services
'
;
import
*
as
types
from
'
../mutation_types
'
;
import
router
from
'
../../ide_router
'
;
import
{
setPageTitle
}
from
'
../utils
'
;
import
{
setPageTitle
,
createTemp
,
findIndexOfFile
}
from
'
../utils
'
;
export
const
closeFile
=
({
commit
,
state
,
getters
,
dispatch
},
path
)
=>
{
const
indexOfClosedFile
=
state
.
openFiles
.
findIndex
(
f
=>
f
.
path
===
path
);
...
...
@@ -46,53 +48,140 @@ export const setFileActive = ({ commit, state, getters, dispatch }, path) => {
commit
(
types
.
SET_CURRENT_BRANCH
,
file
.
branchId
);
};
export
const
getFileData
=
({
state
,
commit
,
dispatch
},
file
)
=>
{
commit
(
types
.
TOGGLE_LOADING
,
{
entry
:
file
});
return
service
.
getFileData
(
file
.
url
)
.
then
(
res
=>
{
const
pageTitle
=
decodeURI
(
normalizeHeaders
(
res
.
headers
)[
'
PAGE-TITLE
'
]);
setPageTitle
(
pageTitle
);
return
res
.
json
();
})
.
then
(
data
=>
{
commit
(
types
.
SET_FILE_DATA
,
{
data
,
file
});
commit
(
types
.
TOGGLE_FILE_OPEN
,
file
.
path
);
dispatch
(
'
setFileActive
'
,
file
.
path
);
commit
(
types
.
TOGGLE_LOADING
,
{
entry
:
file
});
})
.
catch
(()
=>
{
commit
(
types
.
TOGGLE_LOADING
,
{
entry
:
file
});
flash
(
'
Error loading file data. Please try again.
'
,
'
alert
'
,
document
,
null
,
false
,
true
,
);
});
export
const
getFileData
=
({
state
,
commit
,
dispatch
},
path
)
=>
{
const
file
=
state
.
entries
[
path
];
return
new
Promise
((
resolve
,
reject
)
=>
{
commit
(
types
.
TOGGLE_LOADING
,
{
entry
:
file
});
service
.
getFileData
(
file
.
url
)
.
then
(
res
=>
{
const
pageTitle
=
decodeURI
(
normalizeHeaders
(
res
.
headers
)[
'
PAGE-TITLE
'
],
);
setPageTitle
(
pageTitle
);
return
res
.
json
();
})
.
then
(
data
=>
{
commit
(
types
.
SET_FILE_DATA
,
{
data
,
file
});
commit
(
types
.
TOGGLE_FILE_OPEN
,
path
);
dispatch
(
'
setFileActive
'
,
file
.
path
);
commit
(
types
.
TOGGLE_LOADING
,
{
entry
:
file
});
})
.
catch
(
err
=>
{
console
.
log
(
'
Error :
'
,
err
);
commit
(
types
.
TOGGLE_LOADING
,
{
entry
:
file
});
flash
(
'
Error loading file data. Please try again.
'
,
'
alert
'
,
document
,
null
,
false
,
true
,
);
});
});
};
export
const
preloadFileTab
=
({
state
,
commit
,
dispatch
},
file
)
=>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
commit
(
types
.
TOGGLE_LOADING
,
{
entry
:
file
});
service
.
getFileData
(
file
.
url
)
.
then
(
data
=>
{
commit
(
types
.
SET_FILE_DATA
,
{
data
,
file
});
commit
(
types
.
TOGGLE_FILE_OPEN
,
file
);
commit
(
types
.
TOGGLE_LOADING
,
{
entry
:
file
});
})
.
catch
(()
=>
{
commit
(
types
.
TOGGLE_LOADING
,
{
entry
:
file
});
flash
(
'
Error loading file data. Please try again.
'
,
'
alert
'
,
document
,
null
,
false
,
true
,
);
});
});
};
export
const
setFileTargetBranch
=
(
{
state
,
commit
},
{
file
,
targetBranch
},
)
=>
{
commit
(
types
.
SET_FILE_TARGET_BRANCH
,
{
file
,
targetBranch
,
targetRawPath
:
file
.
rawPath
.
replace
(
file
.
branchId
,
targetBranch
),
});
};
export
const
processFileMrDiff
=
({
state
,
commit
},
file
)
=>
{
const
patchObj
=
parsePatch
(
file
.
mrDiff
);
const
transformedContent
=
applyPatch
(
file
.
raw
,
file
.
mrDiff
);
debugger
;
};
export
const
getRawFileData
=
({
commit
,
dispatch
},
file
)
=>
service
.
getRawFileData
(
file
)
.
then
(
raw
=>
{
commit
(
types
.
SET_FILE_RAW_DATA
,
{
file
,
raw
});
})
.
catch
(()
=>
flash
(
'
Error loading file content. Please try again.
'
,
'
alert
'
,
document
,
null
,
false
,
true
,
),
);
export
const
setFileMrDiff
=
({
state
,
commit
},
{
file
,
mrDiff
})
=>
{
commit
(
types
.
SET_FILE_MR_DIFF
,
{
file
,
mrDiff
});
};
export
const
getRawFileData
=
({
commit
,
dispatch
},
file
)
=>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
service
.
getRawFileData
(
file
)
.
then
(
raw
=>
{
commit
(
types
.
SET_FILE_RAW_DATA
,
{
file
,
raw
});
if
(
file
.
mrDiff
)
{
const
patchObj
=
parsePatch
(
file
.
mrDiff
);
patchObj
[
0
].
hunks
.
forEach
(
hunk
=>
{
console
.
log
(
'
H
'
,
hunk
);
/*hunk.lines.forEach((line) => {
if (line.substr(0, 1) === '+') {
line = '-' + line.substr(1);
} else if (line.substr(0, 1) === '-') {
line = '+' + line.substr(1);
}
})*/
});
console
.
log
(
'
PATCH OBJ :
'
+
JSON
.
stringify
(
patchObj
));
const
transformedContent
=
revertPatch
(
raw
,
patchObj
,
{
compareLine
:
(
lineNumber
,
line
,
operation
,
patchContent
)
=>
{
const
tempLine
=
line
;
//line = patchContent;
//patchContent = tempLine;
if
(
operation
===
'
-
'
)
{
operation
=
'
+
'
;
}
else
if
(
operation
===
'
+
'
)
{
operation
=
'
-
'
;
}
console
.
log
(
'
COMPARE :
'
+
line
+
'
-
'
+
operation
+
'
-
'
+
patchContent
,
);
return
true
;
},
});
console
.
log
(
'
TRANSFORMED :
'
,
transformedContent
);
commit
(
types
.
SET_FILE_TARGET_RAW_DATA
,
{
file
,
raw
:
transformedContent
,
});
resolve
(
raw
);
}
else
{
resolve
(
raw
);
}
})
.
catch
(()
=>
{
flash
(
'
Error loading file content. Please try again.
'
);
reject
();
});
});
};
export
const
changeFileContent
=
({
state
,
commit
},
{
path
,
content
})
=>
{
const
file
=
state
.
entries
[
path
];
...
...
app/assets/javascripts/ide/stores/actions/merge_request.js
0 → 100644
View file @
f62359c2
import
flash
from
'
~/flash
'
;
import
service
from
'
../../services
'
;
import
*
as
types
from
'
../mutation_types
'
;
// eslint-disable-next-line import/prefer-default-export
export
const
getMergeRequestData
=
(
{
commit
,
state
,
dispatch
},
{
projectId
,
mergeRequestId
,
force
=
false
}
=
{},
)
=>
new
Promise
((
resolve
,
reject
)
=>
{
if
(
!
state
.
projects
[
projectId
].
mergeRequests
[
mergeRequestId
]
||
force
)
{
service
.
getProjectMergeRequestData
(
projectId
,
mergeRequestId
)
.
then
(
res
=>
res
.
data
)
.
then
(
data
=>
{
commit
(
types
.
SET_MERGE_REQUEST
,
{
projectPath
:
projectId
,
mergeRequestId
,
mergeRequest
:
data
,
});
if
(
!
state
.
currentMergeRequestId
)
{
commit
(
types
.
SET_CURRENT_MERGE_REQUEST
,
`
${
projectId
}
/
${
mergeRequestId
}
`
,
);
}
resolve
(
data
);
})
.
catch
(()
=>
{
flash
(
'
Error loading merge request data. Please try again.
'
);
reject
(
new
Error
(
`Merge Request not loaded
${
projectId
}
`
));
});
}
else
{
resolve
(
state
.
projects
[
projectId
].
mergeRequests
[
mergeRequestId
]);
}
});
// eslint-disable-next-line import/prefer-default-export
export
const
getMergeRequestChanges
=
(
{
commit
,
state
,
dispatch
},
{
projectId
,
mergeRequestId
,
force
=
false
}
=
{},
)
=>
new
Promise
((
resolve
,
reject
)
=>
{
if
(
!
state
.
projects
[
projectId
].
mergeRequests
[
mergeRequestId
].
changes
||
force
)
{
service
.
getProjectMergeRequestChanges
(
projectId
,
mergeRequestId
)
.
then
(
res
=>
res
.
data
)
.
then
(
data
=>
{
commit
(
types
.
SET_MERGE_REQUEST_CHANGES
,
{
projectPath
:
projectId
,
mergeRequestId
,
changes
:
data
,
});
resolve
(
data
);
})
.
catch
(()
=>
{
flash
(
'
Error loading merge request changes. Please try again.
'
);
reject
(
new
Error
(
`Merge Request Changes not loaded
${
projectId
}
`
));
});
}
else
{
resolve
(
state
.
projects
[
projectId
].
mergeRequests
[
mergeRequestId
].
changes
);
}
});
// eslint-disable-next-line import/prefer-default-export
export
const
getMergeRequestNotes
=
(
{
commit
,
state
,
dispatch
},
{
projectId
,
mergeRequestId
,
force
=
false
}
=
{},
)
=>
new
Promise
((
resolve
,
reject
)
=>
{
if
(
!
state
.
projects
[
projectId
].
mergeRequests
[
mergeRequestId
].
notes
||
force
)
{
service
.
getProjectMergeRequestNotes
(
projectId
,
mergeRequestId
)
.
then
(
res
=>
res
.
data
)
.
then
(
data
=>
{
commit
(
types
.
SET_MERGE_REQUEST_NOTES
,
{
projectPath
:
projectId
,
mergeRequestId
,
notes
:
data
,
});
resolve
(
data
);
})
.
catch
(()
=>
{
flash
(
'
Error loading merge request notes. Please try again.
'
);
reject
(
new
Error
(
`Merge Request Notes not loaded
${
projectId
}
`
));
});
}
else
{
resolve
(
state
.
projects
[
projectId
].
mergeRequests
[
mergeRequestId
].
notes
);
}
});
app/assets/javascripts/ide/stores/actions/tree.js
View file @
f62359c2
...
...
@@ -2,9 +2,7 @@ import { normalizeHeaders } from '~/lib/utils/common_utils';
import
flash
from
'
~/flash
'
;
import
service
from
'
../../services
'
;
import
*
as
types
from
'
../mutation_types
'
;
import
{
findEntry
,
}
from
'
../utils
'
;
import
{
findEntry
}
from
'
../utils
'
;
import
FilesDecoratorWorker
from
'
../workers/files_decorator_worker
'
;
export
const
toggleTreeOpen
=
({
commit
,
dispatch
},
path
)
=>
{
...
...
@@ -21,24 +19,33 @@ export const handleTreeEntryAction = ({ commit, dispatch }, row) => {
dispatch
(
'
setFileActive
'
,
row
.
path
);
}
else
{
dispatch
(
'
getFileData
'
,
row
);
dispatch
(
'
getFileData
'
,
row
.
path
);
}
};
export
const
getLastCommitData
=
({
state
,
commit
,
dispatch
,
getters
},
tree
=
state
)
=>
{
export
const
getLastCommitData
=
(
{
state
,
commit
,
dispatch
,
getters
},
tree
=
state
,
)
=>
{
if
(
!
tree
||
tree
.
lastCommitPath
===
null
||
!
tree
.
lastCommitPath
)
return
;
service
.
getTreeLastCommit
(
tree
.
lastCommitPath
)
.
then
((
res
)
=>
{
const
lastCommitPath
=
normalizeHeaders
(
res
.
headers
)[
'
MORE-LOGS-URL
'
]
||
null
;
service
.
getTreeLastCommit
(
tree
.
lastCommitPath
)
.
then
(
res
=>
{
const
lastCommitPath
=
normalizeHeaders
(
res
.
headers
)[
'
MORE-LOGS-URL
'
]
||
null
;
commit
(
types
.
SET_LAST_COMMIT_URL
,
{
tree
,
url
:
lastCommitPath
});
return
res
.
json
();
})
.
then
((
data
)
=>
{
data
.
forEach
((
lastCommit
)
=>
{
const
entry
=
findEntry
(
tree
.
tree
,
lastCommit
.
type
,
lastCommit
.
file_name
);
.
then
(
data
=>
{
data
.
forEach
(
lastCommit
=>
{
const
entry
=
findEntry
(
tree
.
tree
,
lastCommit
.
type
,
lastCommit
.
file_name
,
);
if
(
entry
)
{
commit
(
types
.
SET_LAST_COMMIT_DATA
,
{
entry
,
lastCommit
});
...
...
@@ -47,47 +54,62 @@ export const getLastCommitData = ({ state, commit, dispatch, getters }, tree = s
dispatch
(
'
getLastCommitData
'
,
tree
);
})
.
catch
(()
=>
flash
(
'
Error fetching log data.
'
,
'
alert
'
,
document
,
null
,
false
,
true
));
.
catch
(()
=>
flash
(
'
Error fetching log data.
'
,
'
alert
'
,
document
,
null
,
false
,
true
),
);
};
export
const
getFiles
=
(
{
state
,
commit
,
dispatch
},
{
projectId
,
branchId
}
=
{},
)
=>
new
Promise
((
resolve
,
reject
)
=>
{
if
(
!
state
.
trees
[
`
${
projectId
}
/
${
branchId
}
`
])
{
const
selectedProject
=
state
.
projects
[
projectId
];
commit
(
types
.
CREATE_TREE
,
{
treePath
:
`
${
projectId
}
/
${
branchId
}
`
});
service
.
getFiles
(
selectedProject
.
web_url
,
branchId
)
.
then
(
res
=>
res
.
json
())
.
then
((
data
)
=>
{
const
worker
=
new
FilesDecoratorWorker
();
worker
.
addEventListener
(
'
message
'
,
(
e
)
=>
{
const
{
entries
,
treeList
}
=
e
.
data
;
const
selectedTree
=
state
.
trees
[
`
${
projectId
}
/
${
branchId
}
`
];
commit
(
types
.
SET_ENTRIES
,
entries
);
commit
(
types
.
SET_DIRECTORY_DATA
,
{
treePath
:
`
${
projectId
}
/
${
branchId
}
`
,
data
:
treeList
});
commit
(
types
.
TOGGLE_LOADING
,
{
entry
:
selectedTree
,
forceValue
:
false
});
worker
.
terminate
();
resolve
();
});
worker
.
postMessage
({
data
,
projectId
,
branchId
,
)
=>
new
Promise
((
resolve
,
reject
)
=>
{
if
(
!
state
.
trees
[
`
${
projectId
}
/
${
branchId
}
`
])
{
const
selectedProject
=
state
.
projects
[
projectId
];
commit
(
types
.
CREATE_TREE
,
{
treePath
:
`
${
projectId
}
/
${
branchId
}
`
});
service
.
getFiles
(
selectedProject
.
web_url
,
branchId
)
.
then
(
res
=>
res
.
json
())
.
then
(
data
=>
{
const
worker
=
new
FilesDecoratorWorker
();
worker
.
addEventListener
(
'
message
'
,
e
=>
{
const
{
entries
,
treeList
}
=
e
.
data
;
const
selectedTree
=
state
.
trees
[
`
${
projectId
}
/
${
branchId
}
`
];
commit
(
types
.
SET_ENTRIES
,
entries
);
commit
(
types
.
SET_DIRECTORY_DATA
,
{
treePath
:
`
${
projectId
}
/
${
branchId
}
`
,
data
:
treeList
,
});
commit
(
types
.
TOGGLE_LOADING
,
{
entry
:
selectedTree
,
forceValue
:
false
,
});
worker
.
terminate
();
resolve
();
});
worker
.
postMessage
({
data
,
projectId
,
branchId
,
});
})
.
catch
(
e
=>
{
flash
(
'
Error loading tree data. Please try again.
'
,
'
alert
'
,
document
,
null
,
false
,
true
,
);
reject
(
e
);
});
})
.
catch
((
e
)
=>
{
flash
(
'
Error loading tree data. Please try again.
'
,
'
alert
'
,
document
,
null
,
false
,
true
);
reject
(
e
);
});
}
else
{
resolve
();
}
});
}
else
{
resolve
();
}
});
app/assets/javascripts/ide/stores/getters.js
View file @
f62359c2
...
...
@@ -28,3 +28,5 @@ export const currentIcon = state =>
state
.
rightPanelCollapsed
?
'
angle-double-left
'
:
'
angle-double-right
'
;
export
const
hasChanges
=
state
=>
!!
state
.
changedFiles
.
length
;
export
const
hasMergeRequest
=
state
=>
!!
state
.
currentMergeRequestId
;
app/assets/javascripts/ide/stores/mutation_types.js
View file @
f62359c2
...
...
@@ -11,6 +11,12 @@ export const SET_PROJECT = 'SET_PROJECT';
export
const
SET_CURRENT_PROJECT
=
'
SET_CURRENT_PROJECT
'
;
export
const
TOGGLE_PROJECT_OPEN
=
'
TOGGLE_PROJECT_OPEN
'
;
// Merge Request Mutation Types
export
const
SET_MERGE_REQUEST
=
'
SET_MERGE_REQUEST
'
;
export
const
SET_CURRENT_MERGE_REQUEST
=
'
SET_CURRENT_MERGE_REQUEST
'
;
export
const
SET_MERGE_REQUEST_CHANGES
=
'
SET_MERGE_REQUEST_CHANGES
'
;
export
const
SET_MERGE_REQUEST_NOTES
=
'
SET_MERGE_REQUEST_NOTES
'
;
// Branch Mutation Types
export
const
SET_BRANCH
=
'
SET_BRANCH
'
;
export
const
SET_BRANCH_WORKING_REFERENCE
=
'
SET_BRANCH_WORKING_REFERENCE
'
;
...
...
@@ -28,6 +34,7 @@ export const SET_FILE_DATA = 'SET_FILE_DATA';
export
const
TOGGLE_FILE_OPEN
=
'
TOGGLE_FILE_OPEN
'
;
export
const
SET_FILE_ACTIVE
=
'
SET_FILE_ACTIVE
'
;
export
const
SET_FILE_RAW_DATA
=
'
SET_FILE_RAW_DATA
'
;
export
const
SET_FILE_TARGET_RAW_DATA
=
'
SET_FILE_TARGET_RAW_DATA
'
;
export
const
UPDATE_FILE_CONTENT
=
'
UPDATE_FILE_CONTENT
'
;
export
const
SET_FILE_LANGUAGE
=
'
SET_FILE_LANGUAGE
'
;
export
const
SET_FILE_POSITION
=
'
SET_FILE_POSITION
'
;
...
...
@@ -39,5 +46,7 @@ export const TOGGLE_FILE_CHANGED = 'TOGGLE_FILE_CHANGED';
export
const
SET_CURRENT_BRANCH
=
'
SET_CURRENT_BRANCH
'
;
export
const
SET_ENTRIES
=
'
SET_ENTRIES
'
;
export
const
CREATE_TMP_ENTRY
=
'
CREATE_TMP_ENTRY
'
;
export
const
SET_FILE_MR_DIFF
=
'
SET_FILE_MR_DIFF
'
;
export
const
SET_FILE_TARGET_BRANCH
=
'
SET_FILE_TARGET_BRANCH
'
;
export
const
UPDATE_VIEWER
=
'
UPDATE_VIEWER
'
;
export
const
UPDATE_DELAY_VIEWER_CHANGE
=
'
UPDATE_DELAY_VIEWER_CHANGE
'
;
app/assets/javascripts/ide/stores/mutations.js
View file @
f62359c2
import
*
as
types
from
'
./mutation_types
'
;
import
projectMutations
from
'
./mutations/project
'
;
import
mergeRequestMutation
from
'
./mutations/merge_request
'
;
import
fileMutations
from
'
./mutations/file
'
;
import
treeMutations
from
'
./mutations/tree
'
;
import
branchMutations
from
'
./mutations/branch
'
;
...
...
@@ -100,6 +101,7 @@ export default {
});
},
...
projectMutations
,
...
mergeRequestMutation
,
...
fileMutations
,
...
treeMutations
,
...
branchMutations
,
...
...
app/assets/javascripts/ide/stores/mutations/file.js
View file @
f62359c2
...
...
@@ -35,6 +35,11 @@ export default {
raw
,
});
},
[
types
.
SET_FILE_TARGET_RAW_DATA
](
state
,
{
file
,
raw
})
{
Object
.
assign
(
file
,
{
targetRaw
:
raw
,
});
},
[
types
.
UPDATE_FILE_CONTENT
](
state
,
{
path
,
content
})
{
const
changed
=
content
!==
state
.
entries
[
path
].
raw
;
...
...
@@ -59,6 +64,16 @@ export default {
editorColumn
,
});
},
[
types
.
SET_FILE_MR_DIFF
](
state
,
{
file
,
mrDiff
})
{
Object
.
assign
(
file
,
{
mrDiff
,
});
},
[
types
.
SET_FILE_TARGET_BRANCH
](
state
,
{
file
,
targetBranch
})
{
Object
.
assign
(
file
,
{
targetBranch
,
});
},
[
types
.
DISCARD_FILE_CHANGES
](
state
,
path
)
{
Object
.
assign
(
state
.
entries
[
path
],
{
content
:
state
.
entries
[
path
].
raw
,
...
...
app/assets/javascripts/ide/stores/mutations/merge_request.js
0 → 100644
View file @
f62359c2
import
*
as
types
from
'
../mutation_types
'
;
export
default
{
[
types
.
SET_CURRENT_MERGE_REQUEST
](
state
,
currentMergeRequestId
)
{
Object
.
assign
(
state
,
{
currentMergeRequestId
,
});
},
[
types
.
SET_MERGE_REQUEST
](
state
,
{
projectPath
,
mergeRequestId
,
mergeRequest
},
)
{
// Add client side properties
Object
.
assign
(
mergeRequest
,
{
active
:
true
,
});
Object
.
assign
(
state
.
projects
[
projectPath
],
{
mergeRequests
:
{
[
mergeRequestId
]:
mergeRequest
,
},
});
},
[
types
.
SET_MERGE_REQUEST_CHANGES
](
state
,
{
projectPath
,
mergeRequestId
,
changes
},
)
{
Object
.
assign
(
state
.
projects
[
projectPath
].
mergeRequests
[
mergeRequestId
],
{
changes
,
});
},
[
types
.
SET_MERGE_REQUEST_NOTES
](
state
,
{
projectPath
,
mergeRequestId
,
notes
},
)
{
Object
.
assign
(
state
.
projects
[
projectPath
].
mergeRequests
[
mergeRequestId
],
{
notes
,
});
},
};
app/assets/javascripts/ide/stores/mutations/project.js
View file @
f62359c2
...
...
@@ -11,6 +11,7 @@ export default {
Object
.
assign
(
project
,
{
tree
:
[],
branches
:
{},
mergeRequests
:
{},
active
:
true
,
});
...
...
app/assets/javascripts/ide/stores/state.js
View file @
f62359c2
export
default
()
=>
({
currentProjectId
:
''
,
currentBranchId
:
''
,
currentMergeRequestId
:
''
,
changedFiles
:
[],
endpoints
:
{},
lastCommitMsg
:
''
,
...
...
app/assets/javascripts/ide/stores/utils.js
View file @
f62359c2
...
...
@@ -38,7 +38,7 @@ export const dataStructure = () => ({
eol
:
''
,
});
export
const
decorateData
=
(
entity
)
=>
{
export
const
decorateData
=
entity
=>
{
const
{
id
,
projectId
,
...
...
@@ -57,7 +57,6 @@ export const decorateData = (entity) => {
base64
=
false
,
file_lock
,
}
=
entity
;
return
{
...
...
@@ -80,17 +79,45 @@ export const decorateData = (entity) => {
base64
,
file_lock
,
};
};
export
const
findEntry
=
(
tree
,
type
,
name
,
prop
=
'
name
'
)
=>
tree
.
find
(
f
=>
f
.
type
===
type
&&
f
[
prop
]
===
name
,
);
/*
Takes the multi-dimensional tree and returns a flattened array.
This allows for the table to recursively render the table rows but keeps the data
structure nested to make it easier to add new files/directories.
*/
export
const
treeList
=
(
state
,
treeId
)
=>
{
const
baseTree
=
state
.
trees
[
treeId
];
if
(
baseTree
)
{
const
mapTree
=
arr
=>
!
arr
.
tree
||
!
arr
.
tree
.
length
?
[]
:
_
.
map
(
arr
.
tree
,
a
=>
[
a
,
mapTree
(
a
)]);
return
_
.
chain
(
baseTree
.
tree
)
.
map
(
arr
=>
[
arr
,
mapTree
(
arr
)])
.
flatten
()
.
value
();
}
return
[];
};
export
const
getTree
=
state
=>
(
namespace
,
projectId
,
branch
)
=>
state
.
trees
[
`
${
namespace
}
/
${
projectId
}
/
${
branch
}
`
];
export
const
getTreeEntry
=
(
store
,
treeId
,
path
)
=>
{
const
fileList
=
treeList
(
store
.
state
,
treeId
);
return
fileList
?
fileList
.
find
(
file
=>
file
.
path
===
path
)
:
null
;
};
export
const
findEntry
=
(
tree
,
type
,
name
,
prop
=
'
name
'
)
=>
tree
.
find
(
f
=>
f
.
type
===
type
&&
f
[
prop
]
===
name
);
export
const
findIndexOfFile
=
(
state
,
file
)
=>
state
.
findIndex
(
f
=>
f
.
path
===
file
.
path
);
export
const
findIndexOfFile
=
(
state
,
file
)
=>
state
.
findIndex
(
f
=>
f
.
path
===
file
.
path
);
export
const
setPageTitle
=
(
title
)
=>
{
export
const
setPageTitle
=
title
=>
{
document
.
title
=
title
;
};
...
...
@@ -120,6 +147,11 @@ const sortTreesByTypeAndName = (a, b) => {
return
0
;
};
export
const
sortTree
=
sortedTree
=>
sortedTree
.
map
(
entity
=>
Object
.
assign
(
entity
,
{
tree
:
entity
.
tree
.
length
?
sortTree
(
entity
.
tree
)
:
[],
})).
sort
(
sortTreesByTypeAndName
);
export
const
sortTree
=
sortedTree
=>
sortedTree
.
map
(
entity
=>
Object
.
assign
(
entity
,
{
tree
:
entity
.
tree
.
length
?
sortTree
(
entity
.
tree
)
:
[],
}),
)
.
sort
(
sortTreesByTypeAndName
);
app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue
View file @
f62359c2
<
script
>
import
tooltip
from
'
~/vue_shared/directives/tooltip
'
;
import
{
n__
}
from
'
~/locale
'
;
import
icon
from
'
~/vue_shared/components/icon.vue
'
;
import
clipboardButton
from
'
~/vue_shared/components/clipboard_button.vue
'
;
import
tooltip
from
'
~/vue_shared/directives/tooltip
'
;
import
{
n__
}
from
'
~/locale
'
;
import
icon
from
'
~/vue_shared/components/icon.vue
'
;
import
clipboardButton
from
'
~/vue_shared/components/clipboard_button.vue
'
;
export
default
{
name
:
'
MRWidgetHeader
'
,
directives
:
{
tooltip
,
export
default
{
name
:
'
MRWidgetHeader
'
,
directives
:
{
tooltip
,
},
components
:
{
icon
,
clipboardButton
,
},
props
:
{
mr
:
{
type
:
Object
,
required
:
true
,
},
components
:
{
icon
,
clipboardButton
,
},
computed
:
{
shouldShowCommitsBehindText
()
{
return
this
.
mr
.
divergedCommitsCount
>
0
;
},
props
:
{
mr
:
{
type
:
Object
,
required
:
true
,
},
commitsText
()
{
return
n__
(
'
%d commit behind
'
,
'
%d commits behind
'
,
this
.
mr
.
divergedCommitsCount
,
);
},
computed
:
{
shouldShowCommitsBehindText
()
{
return
this
.
mr
.
divergedCommitsCount
>
0
;
},
commitsText
()
{
return
n__
(
'
%d commit behind
'
,
'
%d commits behind
'
,
this
.
mr
.
divergedCommitsCount
);
},
branchNameClipboardData
()
{
// This supports code in app/assets/javascripts/copy_to_clipboard.js that
// works around ClipboardJS limitations to allow the context-specific
// copy/pasting of plain text or GFM.
return
JSON
.
stringify
({
text
:
this
.
mr
.
sourceBranch
,
gfm
:
`\`
${
this
.
mr
.
sourceBranch
}
\``
,
});
},
isSourceBranchLong
()
{
return
this
.
isBranchTitleLong
(
this
.
mr
.
sourceBranch
);
},
isTargetBranchLong
()
{
return
this
.
isBranchTitleLong
(
this
.
mr
.
targetBranch
);
},
branchNameClipboardData
()
{
// This supports code in app/assets/javascripts/copy_to_clipboard.js that
// works around ClipboardJS limitations to allow the context-specific
// copy/pasting of plain text or GFM.
return
JSON
.
stringify
({
text
:
this
.
mr
.
sourceBranch
,
gfm
:
`\`
${
this
.
mr
.
sourceBranch
}
\``
,
});
},
methods
:
{
isBranchTitleLong
(
branchTitle
)
{
return
branchTitle
.
length
>
32
;
},
isSourceBranchLong
()
{
return
this
.
isBranchTitleLong
(
this
.
mr
.
sourceBranch
);
},
};
isTargetBranchLong
()
{
return
this
.
isBranchTitleLong
(
this
.
mr
.
targetBranch
);
},
webIdePath
()
{
return
`
${
gon
.
relative_url_root
}
/-/ide/project
${
this
.
mr
.
statusPath
.
replace
(
'
.json
'
,
''
)}
`
;
},
},
methods
:
{
isBranchTitleLong
(
branchTitle
)
{
return
branchTitle
.
length
>
32
;
},
},
};
</
script
>
<
template
>
<div
class=
"mr-source-target"
>
...
...
@@ -96,6 +105,14 @@
</div>
<div
v-if=
"mr.isOpen"
>
<a
:disabled=
"mr.sourceBranchRemoved"
:href=
"webIdePath"
class=
"btn btn-sm btn-default inline js-web-ide"
type=
"button"
>
{{
s__
(
"
mrWidget|Open in Web IDE
"
)
}}
</a>
<button
data-target=
"#modal_merge_info"
data-toggle=
"modal"
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment