diff options
Diffstat (limited to 'app/assets/javascripts/ide/components/repo_editor.vue')
-rw-r--r-- | app/assets/javascripts/ide/components/repo_editor.vue | 89 |
1 files changed, 67 insertions, 22 deletions
diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue index c72a8b2b0d0..a7646083428 100644 --- a/app/assets/javascripts/ide/components/repo_editor.vue +++ b/app/assets/javascripts/ide/components/repo_editor.vue @@ -14,6 +14,9 @@ import Editor from '../lib/editor'; import FileTemplatesBar from './file_templates/bar.vue'; import { __ } from '~/locale'; import { extractMarkdownImagesFromEntries } from '../stores/utils'; +import { getPathParent, readFileAsDataURL } from '../utils'; +import { getRulesWithTraversal } from '../lib/editorconfig/parser'; +import mapRulesToMonaco from '../lib/editorconfig/rules_mapper'; export default { components: { @@ -31,6 +34,7 @@ export default { return { content: '', images: {}, + rules: {}, }; }, computed: { @@ -50,7 +54,6 @@ export default { 'getStagedFile', 'isEditModeActive', 'isCommitModeActive', - 'isReviewModeActive', 'currentBranch', ]), ...mapGetters('fileTemplates', ['showFileTemplatesBar']), @@ -82,10 +85,6 @@ export default { active: this.isPreviewViewMode, }; }, - fileType() { - const info = viewerInformationForPath(this.file.path); - return (info && info.id) || ''; - }, showEditor() { return !this.shouldHideEditor && this.isEditorViewMode; }, @@ -98,6 +97,12 @@ export default { currentBranchCommit() { return this.currentBranch?.commit.id; }, + previewMode() { + return viewerInformationForPath(this.file.path); + }, + fileType() { + return this.previewMode?.id || ''; + }, }, watch: { file(newVal, oldVal) { @@ -165,6 +170,12 @@ export default { this.editor = Editor.create(this.editorOptions); } this.initEditor(); + + // listen in capture phase to be able to override Monaco's behaviour. + window.addEventListener('paste', this.onPaste, true); + }, + destroyed() { + window.removeEventListener('paste', this.onPaste, true); }, methods: { ...mapActions([ @@ -174,10 +185,10 @@ export default { 'setFileLanguage', 'setEditorPosition', 'setFileViewMode', - 'setFileEOL', 'updateViewer', 'removePendingTab', 'triggerFilesChange', + 'addTempImage', ]), initEditor() { if (this.shouldHideEditor && (this.file.content || this.file.raw)) { @@ -186,7 +197,7 @@ export default { this.editor.clearEditor(); - this.fetchFileData() + Promise.all([this.fetchFileData(), this.fetchEditorconfigRules()]) .then(() => { this.createEditorInstance(); }) @@ -223,7 +234,7 @@ export default { if (this.viewer === viewerTypes.edit) { this.editor.createInstance(this.$refs.editor); } else { - this.editor.createDiffInstance(this.$refs.editor, !this.isReviewModeActive); + this.editor.createDiffInstance(this.$refs.editor); } this.setupEditor(); @@ -245,15 +256,15 @@ export default { this.editor.attachModel(this.model); } + this.model.updateOptions(this.rules); + this.model.onChange(model => { const { file } = model; + if (!file.active) return; - if (file.active) { - this.changeFileContent({ - path: file.path, - content: model.getModel().getValue(), - }); - } + const monacoModel = model.getModel(); + const content = monacoModel.getValue(); + this.changeFileContent({ path: file.path, content }); }); // Handle Cursor Position @@ -274,16 +285,51 @@ export default { fileLanguage: this.model.language, }); - // Get File eol - this.setFileEOL({ - eol: this.model.eol, - }); + this.$emit('editorSetup'); }, refreshEditorDimensions() { if (this.showEditor) { this.editor.updateDimensions(); } }, + fetchEditorconfigRules() { + return getRulesWithTraversal(this.file.path, path => { + const entry = this.entries[path]; + if (!entry) return Promise.resolve(null); + + const content = entry.content || entry.raw; + if (content) return Promise.resolve(content); + + return this.getFileData({ path: entry.path, makeFileActive: false }).then(() => + this.getRawFileData({ path: entry.path }), + ); + }).then(rules => { + this.rules = mapRulesToMonaco(rules); + }); + }, + onPaste(event) { + const editor = this.editor.instance; + const reImage = /^image\/(png|jpg|jpeg|gif)$/; + const file = event.clipboardData.files[0]; + + if (editor.hasTextFocus() && this.fileType === 'markdown' && reImage.test(file?.type)) { + // don't let the event be passed on to Monaco. + event.preventDefault(); + event.stopImmediatePropagation(); + + return readFileAsDataURL(file).then(content => { + const parentPath = getPathParent(this.file.path); + const path = `${parentPath ? `${parentPath}/` : ''}${file.name}`; + + return this.addTempImage({ name: path, rawPath: content }).then(({ name: fileName }) => { + this.editor.replaceSelectedText(`![${fileName}](./${fileName})`); + }); + }); + } + + // do nothing if no image is found in the clipboard + return Promise.resolve(); + }, }, viewerTypes, FILE_VIEW_MODE_EDITOR, @@ -301,16 +347,15 @@ export default { role="button" @click.prevent="setFileViewMode({ file, viewMode: $options.FILE_VIEW_MODE_EDITOR })" > - <template v-if="viewer === $options.viewerTypes.edit">{{ __('Edit') }}</template> - <template v-else>{{ __('Review') }}</template> + {{ __('Edit') }} </a> </li> - <li v-if="file.previewMode" :class="previewTabCSS"> + <li v-if="previewMode" :class="previewTabCSS"> <a href="javascript:void(0);" role="button" @click.prevent="setFileViewMode({ file, viewMode: $options.FILE_VIEW_MODE_PREVIEW })" - >{{ file.previewMode.previewTitle }}</a + >{{ previewMode.previewTitle }}</a > </li> </ul> |