Skip to content
Snippets Groups Projects
Commit f62359c2 authored by Tim Zallmann's avatar Tim Zallmann
Browse files

Basic Setup for MR Showing

parent 06afa5a3
No related branches found
No related tags found
No related merge requests found
Showing
with 902 additions and 260 deletions
Loading
Loading
@@ -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',
Loading
Loading
@@ -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);
 
Loading
Loading
@@ -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));
},
 
Loading
Loading
@@ -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);
 
Loading
Loading
@@ -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);
},
Loading
Loading
@@ -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',
Loading
Loading
@@ -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) {
Loading
Loading
@@ -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);
},
Loading
Loading
@@ -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,
),
});
},
 
Loading
Loading
<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>
Loading
Loading
@@ -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>
Loading
Loading
@@ -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="#"
Loading
Loading
<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,
},
computed: {
...mapState(['changedFiles', 'openFiles', 'viewer']),
...mapGetters(['activeFile', 'hasChanges']),
committedStateSvgPath: {
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>
Loading
Loading
@@ -63,6 +63,7 @@
:files="openFiles"
:viewer="viewer"
:has-changes="hasChanges"
:has-merge-request="hasMergeRequest"
/>
<repo-editor
class="multi-file-edit-pane-content"
Loading
Loading
Loading
Loading
@@ -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;
})
Loading
Loading
@@ -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;
});
},
Loading
Loading
@@ -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) {
Loading
Loading
<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>
Loading
Loading
@@ -55,6 +60,7 @@
:viewer="viewer"
:show-shadow="showShadow"
:has-changes="hasChanges"
:has-merge-request="hasMergeRequest"
@click="updateViewer"
/>
</div>
Loading
Loading
Loading
Loading
@@ -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);
 
Loading
Loading
@@ -44,7 +45,7 @@ const router = new VueRouter({
component: EmptyRouterComponent,
},
{
path: 'mr/:mrid',
path: 'merge_requests/:mrid',
component: EmptyRouterComponent,
},
],
Loading
Loading
@@ -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 => {
Loading
Loading
Loading
Loading
@@ -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);
Loading
Loading
@@ -58,6 +68,10 @@ export default class Model {
return this.originalModel;
}
 
getTargetModel() {
return this.targetModel;
}
setValue(value) {
this.getModel().setValue(value);
}
Loading
Loading
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\x85]/),
delimiters = source.match(/\r\n|[\n\v\f\r\x85]/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
};
};
Loading
Loading
@@ -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,
Loading
Loading
Loading
Loading
@@ -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);
},
Loading
Loading
Loading
Loading
@@ -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';
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);
Loading
Loading
@@ -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];
Loading
Loading
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);
}
});
Loading
Loading
@@ -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) => {
Loading
Loading
@@ -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 });
Loading
Loading
@@ -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();
}
});
Loading
Loading
@@ -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;
Loading
Loading
@@ -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';
Loading
Loading
@@ -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';
Loading
Loading
@@ -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';
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';
Loading
Loading
@@ -100,6 +101,7 @@ export default {
});
},
...projectMutations,
...mergeRequestMutation,
...fileMutations,
...treeMutations,
...branchMutations,
Loading
Loading
Loading
Loading
@@ -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;
 
Loading
Loading
@@ -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,
Loading
Loading
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,
});
},
};
Loading
Loading
@@ -11,6 +11,7 @@ export default {
Object.assign(project, {
tree: [],
branches: {},
mergeRequests: {},
active: true,
});
 
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment