Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use monaco for the git hook editor #13552

Merged
merged 3 commits into from
Nov 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion routers/repo/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -787,7 +787,6 @@ func GitHooks(ctx *context.Context) {
func GitHooksEdit(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.settings.githooks")
ctx.Data["PageIsSettingsGitHooks"] = true
ctx.Data["RequireSimpleMDE"] = true

name := ctx.Params(":name")
hook, err := ctx.Repo.GitRepo.GetHook(name)
Expand Down
2 changes: 1 addition & 1 deletion templates/repo/editor/edit.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
{{end}}
</div>
<div class="ui bottom attached active tab segment" data-tab="write">
<textarea id="edit_area" name="content" data-id="repo-{{.Repository.Name}}-{{.TreePath}}"
<textarea id="edit_area" name="content" class="hide" data-id="repo-{{.Repository.Name}}-{{.TreePath}}"
data-url="{{.Repository.APIURL}}/markdown"
data-context="{{.RepoLink}}"
data-markdown-file-exts="{{.MarkdownFileExts}}"
Expand Down
6 changes: 3 additions & 3 deletions templates/repo/settings/githook_edit.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@
{{with .Hook}}
<div class="inline field">
<label>{{$.i18n.Tr "repo.settings.githook_name"}}</label>
<span>{{.Name}}</span>
<span class="hook-filename">{{.Name}}</span>
</div>
<div class="field">
<label for="content">{{$.i18n.Tr "repo.settings.githook_content"}}</label>
<textarea id="content" name="content" rows="20" wrap="off" autofocus>{{if .IsActive}}{{.Content}}{{else}}{{.Sample}}{{end}}</textarea>
<textarea id="content" name="content" class="hide">{{if .IsActive}}{{.Content}}{{else}}{{.Sample}}{{end}}</textarea>
<div class="editor-loading is-loading"></div>
</div>

<div class="inline field">
<button class="ui green button">{{$.i18n.Tr "repo.settings.update_githook"}}</button>
</div>
Expand Down
4 changes: 3 additions & 1 deletion templates/repo/settings/githooks.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
<div class="item">
<span class="text {{if .IsActive}}green{{else}}grey{{end}}">{{svg "octicon-dot-fill"}}</span>
<span>{{.Name}}</span>
<a class="text blue ui right" href="{{$.RepoLink}}/settings/hooks/git/{{.Name}}"><i class="fa fa-pencil"></i></a>
<a class="text blue ui right" href="{{$.RepoLink}}/settings/hooks/git/{{.Name}}">
{{svg "octicon-pencil"}}
</a>
</div>
{{end}}
</div>
Expand Down
97 changes: 56 additions & 41 deletions web_src/js/features/codeeditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,11 @@ function getLanguage(filename) {
return languagesByFilename[filename] || languagesByExt[extname(filename)] || 'plaintext';
}

function updateEditor(monaco, editor, filenameInput) {
const newFilename = filenameInput.value;
editor.updateOptions(getOptions(filenameInput));
function updateEditor(monaco, editor, filename, lineWrapExts) {
editor.updateOptions({...getFileBasedOptions(filename, lineWrapExts)});
const model = editor.getModel();
const language = model.getModeId();
const newLanguage = getLanguage(newFilename);
const newLanguage = getLanguage(filename);
if (language !== newLanguage) monaco.editor.setModelLanguage(model, newLanguage);
}

Expand All @@ -41,33 +40,22 @@ function exportEditor(editor) {
if (!window.codeEditors.includes(editor)) window.codeEditors.push(editor);
}

export async function createCodeEditor(textarea, filenameInput, previewFileModes) {
const filename = basename(filenameInput.value);
const previewLink = document.querySelector('a[data-tab=preview]');
const markdownExts = (textarea.dataset.markdownFileExts || '').split(',');
const lineWrapExts = (textarea.dataset.lineWrapExtensions || '').split(',');
const isMarkdown = markdownExts.includes(extname(filename));

if (previewLink) {
if (isMarkdown && (previewFileModes || []).includes('markdown')) {
previewLink.dataset.url = previewLink.dataset.url.replace(/(.*)\/.*/i, `$1/markdown`);
previewLink.style.display = '';
} else {
previewLink.style.display = 'none';
}
}

export async function createMonaco(textarea, filename, editorOpts) {
const monaco = await import(/* webpackChunkName: "monaco" */'monaco-editor');

initLanguages(monaco);
let {language, ...other} = editorOpts;
if (!language) language = getLanguage(filename);

const container = document.createElement('div');
container.className = 'monaco-editor-container';
textarea.parentNode.appendChild(container);

const editor = monaco.editor.create(container, {
value: textarea.value,
language: getLanguage(filename),
...getOptions(filenameInput, lineWrapExts),
theme: isDarkTheme() ? 'vs-dark' : 'vs',
language,
...other,
});

const model = editor.getModel();
Expand All @@ -80,33 +68,60 @@ export async function createCodeEditor(textarea, filenameInput, previewFileModes
editor.layout();
});

filenameInput.addEventListener('keyup', () => {
updateEditor(monaco, editor, filenameInput);
});
exportEditor(editor);

const loading = document.querySelector('.editor-loading');
if (loading) loading.remove();

exportEditor(editor);
return {monaco, editor};
}

return editor;
function getFileBasedOptions(filename, lineWrapExts) {
return {
wordWrap: (lineWrapExts || []).includes(extname(filename)) ? 'on' : 'off',
};
}

function getOptions(filenameInput, lineWrapExts) {
const ec = getEditorconfig(filenameInput);
const theme = isDarkTheme() ? 'vs-dark' : 'vs';
const wordWrap = (lineWrapExts || []).includes(extname(filenameInput.value)) ? 'on' : 'off';

const opts = {theme, wordWrap};
if (isObject(ec)) {
opts.detectIndentation = !('indent_style' in ec) || !('indent_size' in ec);
if ('indent_size' in ec) opts.indentSize = Number(ec.indent_size);
if ('tab_width' in ec) opts.tabSize = Number(ec.tab_width) || opts.indentSize;
if ('max_line_length' in ec) opts.rulers = [Number(ec.max_line_length)];
opts.trimAutoWhitespace = ec.trim_trailing_whitespace === true;
opts.insertSpaces = ec.indent_style === 'space';
opts.useTabStops = ec.indent_style === 'tab';
export async function createCodeEditor(textarea, filenameInput, previewFileModes) {
const filename = basename(filenameInput.value);
const previewLink = document.querySelector('a[data-tab=preview]');
const markdownExts = (textarea.dataset.markdownFileExts || '').split(',');
const lineWrapExts = (textarea.dataset.lineWrapExtensions || '').split(',');
const isMarkdown = markdownExts.includes(extname(filename));
const editorConfig = getEditorconfig(filenameInput);

if (previewLink) {
if (isMarkdown && (previewFileModes || []).includes('markdown')) {
previewLink.dataset.url = previewLink.dataset.url.replace(/(.*)\/.*/i, `$1/markdown`);
previewLink.style.display = '';
} else {
previewLink.style.display = 'none';
}
}

const {monaco, editor} = await createMonaco(textarea, filename, {
...getFileBasedOptions(filenameInput.value, lineWrapExts),
...getEditorConfigOptions(editorConfig),
});

filenameInput.addEventListener('keyup', () => {
const filename = filenameInput.value;
updateEditor(monaco, editor, filename, lineWrapExts);
});

return editor;
}

function getEditorConfigOptions(ec) {
if (!isObject(ec)) return {};

const opts = {};
opts.detectIndentation = !('indent_style' in ec) || !('indent_size' in ec);
if ('indent_size' in ec) opts.indentSize = Number(ec.indent_size);
if ('tab_width' in ec) opts.tabSize = Number(ec.tab_width) || opts.indentSize;
if ('max_line_length' in ec) opts.rulers = [Number(ec.max_line_length)];
opts.trimAutoWhitespace = ec.trim_trailing_whitespace === true;
opts.insertSpaces = ec.indent_style === 'space';
opts.useTabStops = ec.indent_style === 'tab';
return opts;
}
17 changes: 6 additions & 11 deletions web_src/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import createDropzone from './features/dropzone.js';
import initTableSort from './features/tablesort.js';
import ActivityTopAuthors from './components/ActivityTopAuthors.vue';
import {initNotificationsTable, initNotificationCount} from './features/notification.js';
import {createCodeEditor} from './features/codeeditor.js';
import {createCodeEditor, createMonaco} from './features/codeeditor.js';
import {svg, svgs} from './svg.js';
import {stripTags} from './utils.js';

Expand Down Expand Up @@ -1732,15 +1732,10 @@ function initUserSettings() {
}
}

function initGithook() {
if ($('.edit.githook').length === 0) {
return;
}

CodeMirror.autoLoadMode(CodeMirror.fromTextArea($('#content')[0], {
lineNumbers: true,
mode: 'shell'
}), 'shell');
async function initGithook() {
if ($('.edit.githook').length === 0) return;
const filename = document.querySelector('.hook-filename').textContent;
await createMonaco($('#content')[0], filename, {language: 'shell'});
}

function initWebhook() {
Expand Down Expand Up @@ -2517,7 +2512,6 @@ $(document).ready(async () => {
initEditForm();
initEditor();
initOrganization();
initGithook();
initWebhook();
initAdmin();
initCodeView();
Expand Down Expand Up @@ -2575,6 +2569,7 @@ $(document).ready(async () => {
initServiceWorker(),
initNotificationCount(),
renderMarkdownContent(),
initGithook(),
]);
});

Expand Down
9 changes: 5 additions & 4 deletions web_src/less/_editor.less
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,6 @@
border-right: 1px solid var(--color-secondary) !important;
}

#edit_area {
display: none;
}

.monaco-editor-container {
width: 100%;
min-height: 200px;
Expand All @@ -73,3 +69,8 @@
color: transparent !important;
background-color: transparent !important;
}

.edit.githook .monaco-editor-container {
border: 1px solid var(--color-secondary);
height: 70vh;
}