Commit d5138d6a authored by Phil Hughes's avatar Phil Hughes

IDE open changed files in diff viewer

Closes #4568
parent 5ea0fbfc
......@@ -132,13 +132,33 @@
.multi-file-tabs {
display: flex;
overflow-x: auto;
background-color: $white-normal;
box-shadow: inset 0 -1px $white-dark;
> li {
> ul {
display: flex;
overflow-x: auto;
}
li {
position: relative;
}
.dropdown {
display: flex;
margin-left: auto;
padding: 0 $grid-size;
background-color: $white-light;
&.shadow {
box-shadow: 0 0 10px rgba(0,0,0,.4);
}
.btn {
margin-top: auto;
margin-bottom: auto;
}
}
}
.multi-file-tab {
......
......@@ -24,8 +24,11 @@
methods: {
...mapActions([
'discardFileChanges',
'updateViewer',
]),
openFileInEditor(file) {
this.updateViewer('diff');
router.push(`/project${file.url}`);
},
},
......
......@@ -15,6 +15,7 @@ export default {
'leftPanelCollapsed',
'rightPanelCollapsed',
'panelResizing',
'viewer',
]),
shouldHideEditor() {
return this.activeFile && this.activeFile.binary && !this.activeFile.raw;
......@@ -37,6 +38,9 @@ export default {
this.editor.updateDimensions();
}
},
viewer() {
this.createEditorInstance();
},
},
beforeDestroy() {
this.editor.dispose();
......@@ -66,15 +70,25 @@ export default {
this.editor.clearEditor();
this.getRawFileData(this.activeFile)
.then(() => {
this.editor.createInstance(this.$refs.editor);
})
.then(() => this.setupEditor())
.then(() => this.createEditorInstance())
.catch((err) => {
flash('Error setting up monaco. Please try again.', 'alert', document, null, false, true);
throw err;
});
},
createEditorInstance() {
this.editor.dispose();
this.$nextTick(() => {
if (this.viewer === 'editor') {
this.editor.createInstance(this.$refs.editor);
} else {
this.editor.createDiffInstance(this.$refs.editor);
}
this.setupEditor();
});
},
setupEditor() {
if (!this.activeFile) return;
......
<script>
import { mapState } from 'vuex';
import { mapActions, mapState } from 'vuex';
import RepoTab from './repo_tab.vue';
export default {
components: {
'repo-tab': RepoTab,
},
data() {
return {
showShadow: false,
};
},
computed: {
...mapState([
'openFiles',
'viewer',
]),
},
methods: {
...mapActions([
'updateViewer',
]),
},
updated() {
if (!this.$refs.tabsScroller) return;
this.showShadow = this.$refs.tabsScroller.scrollWidth > this.$refs.tabsScroller.offsetWidth;
},
};
</script>
<template>
<ul
class="multi-file-tabs list-unstyled append-bottom-0"
>
<repo-tab
v-for="tab in openFiles"
:key="tab.key"
:tab="tab"
/>
</ul>
<div class="multi-file-tabs">
<ul
class="list-unstyled append-bottom-0"
ref="tabsScroller"
>
<repo-tab
v-for="tab in openFiles"
:key="tab.key"
:tab="tab"
/>
</ul>
<div
class="dropdown"
:class="{
shadow: showShadow,
}"
>
<button class="btn btn-primary btn-sm" data-toggle="dropdown">
<template v-if="viewer === 'editor'">
Editing
</template>
<template v-else>
Reviewing
</template>
<i class="fa fa-chevron-down"></i>
</button>
<div class="dropdown-menu dropdown-menu-selectable dropdown-open-left">
<ul>
<li>
<a
href="#"
@click.prevent="updateViewer('editor')"
:class="{
'is-active': viewer === 'editor',
}"
>
<strong class="dropdown-menu-inner-title">Editing</strong>
<span class="dropdown-menu-inner-content">
View and edit lines
</span>
</a>
</li>
<li>
<a
href="#"
@click.prevent="updateViewer('diff')"
:class="{
'is-active': viewer === 'diff',
}"
>
<strong class="dropdown-menu-inner-title">Reviewing</strong>
<span class="dropdown-menu-inner-content">
Compare changes with the last commit
</span>
</a>
</li>
</ul>
</div>
</div>
</div>
</template>
......@@ -26,6 +26,9 @@ export default class Model {
this.events = new Map();
this.updateContent = this.updateContent.bind(this);
this.dispose = this.dispose.bind(this);
eventHub.$on(`editor.update.model.dispose.${this.file.path}`, this.dispose);
eventHub.$on(`editor.update.model.content.${this.file.path}`, this.updateContent);
}
......@@ -75,6 +78,7 @@ export default class Model {
this.disposable.dispose();
this.events.clear();
eventHub.$off(`editor.update.model.dispose.${this.file.path}`, this.dispose);
eventHub.$off(`editor.update.model.content.${this.file.path}`, this.updateContent);
}
}
import eventHub from 'ee/ide/eventhub';
import Disposable from './disposable';
import Model from './model';
......@@ -25,9 +26,17 @@ export default class ModelManager {
this.models.set(model.path, model);
this.disposable.add(model);
eventHub.$on(`editor.update.model.dispose.${file.path}`, this.removeCachedModel.bind(this, file));
return model;
}
removeCachedModel(file) {
this.models.delete(file.path);
eventHub.$off(`editor.update.model.dispose.${file.path}`, this.removeCachedModel);
}
dispose() {
// dispose of all the models
this.disposable.dispose();
......
......@@ -34,6 +34,10 @@ export default class Editor {
createInstance(domElement) {
if (!this.instance) {
Object.assign(domElement, {
innerHTML: '',
});
this.disposable.add(
this.instance = this.monaco.editor.create(domElement, {
model: null,
......@@ -53,11 +57,36 @@ export default class Editor {
}
}
createDiffInstance(domElement) {
if (!this.instance) {
Object.assign(domElement, {
innerHTML: '',
});
this.disposable.add(
this.instance = this.monaco.editor.createDiffEditor(domElement, {
readOnly: true,
}),
);
window.addEventListener('resize', this.debouncedUpdate, false);
}
}
createModel(file) {
return this.modelManager.addModel(file);
}
attachModel(model) {
if (this.instance.getEditorType() === 'vs.editor.IDiffEditor') {
this.instance.setModel({
original: model.getOriginalModel(),
modified: model.getModel(),
});
return;
}
this.instance.setModel(model.getModel());
if (this.dirtyDiffController) this.dirtyDiffController.attachModel(model);
......@@ -113,6 +142,8 @@ export default class Editor {
}
onPositionChange(cb) {
if (!this.instance.onDidChangeCursorPosition) return;
this.disposable.add(
this.instance.onDidChangeCursorPosition(e => cb(this.instance, e)),
);
......
......@@ -84,6 +84,10 @@ export const scrollToTab = () => {
});
};
export const updateViewer = ({ commit }, viewer) => {
commit(types.UPDATE_VIEWER, viewer);
};
export * from './actions/tree';
export * from './actions/file';
export * from './actions/project';
......
import { normalizeHeaders } from '~/lib/utils/common_utils';
import flash from '~/flash';
import eventHub from 'ee/ide/eventhub';
import service from '../../services';
import * as types from '../mutation_types';
import router from '../../ide_router';
......@@ -27,6 +28,8 @@ export const closeFile = ({ commit, state, dispatch }, file) => {
}
dispatch('getLastCommitData');
eventHub.$emit(`editor.update.model.dispose.${file.path}`);
};
export const setFileActive = ({ commit, state, getters, dispatch }, file) => {
......
......@@ -46,3 +46,5 @@ export const SET_EDIT_MODE = 'SET_EDIT_MODE';
export const TOGGLE_EDIT_MODE = 'TOGGLE_EDIT_MODE';
export const SET_CURRENT_BRANCH = 'SET_CURRENT_BRANCH';
export const UPDATE_VIEWER = 'UPDATE_VIEWER';
......@@ -57,6 +57,11 @@ export default {
lastCommitMsg,
});
},
[types.UPDATE_VIEWER](state, viewer) {
Object.assign(state, {
viewer,
});
},
...projectMutations,
...fileMutations,
...treeMutations,
......
......@@ -20,4 +20,5 @@ export default () => ({
leftPanelCollapsed: false,
rightPanelCollapsed: false,
panelResizing: false,
viewer: 'editor',
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment