Skip to content

Commit db3d572

Browse files
committed
Fix EOL handling in web editor
1 parent fc09c30 commit db3d572

File tree

5 files changed

+37
-6
lines changed

5 files changed

+37
-6
lines changed

routers/web/repo/editor.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
287287
Operation: operation,
288288
FromTreePath: ctx.Repo.TreePath,
289289
TreePath: form.TreePath,
290-
ContentReader: strings.NewReader(strings.ReplaceAll(form.Content, "\r", "")),
290+
ContentReader: strings.NewReader(form.Content),
291291
},
292292
},
293293
Signoff: form.Signoff,

templates/repo/editor/edit.tmpl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@
3838
data-url="{{.Repository.Link}}/markup"
3939
data-context="{{.RepoLink}}"
4040
data-previewable-extensions="{{.PreviewableExtensions}}"
41-
data-line-wrap-extensions="{{.LineWrapExtensions}}">
42-
{{.FileContent}}</textarea>
41+
data-line-wrap-extensions="{{.LineWrapExtensions}}">{{.FileContent}}</textarea>
4342
<div class="editor-loading is-loading"></div>
43+
<script>window.monacoContent = "{{.FileContent}}";</script>
4444
</div>
4545
<div class="ui bottom attached tab segment markup" data-tab="preview">
4646
{{.locale.Tr "loading"}}

web_src/js/features/codeeditor.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import tinycolor from 'tinycolor2';
2-
import {basename, extname, isObject, isDarkTheme} from '../utils.js';
2+
import {basename, extname, isObject, isDarkTheme, detectEol} from '../utils.js';
33
import {onInputDebounce} from '../utils/dom.js';
44

55
const languagesByFilename = {};
@@ -62,7 +62,7 @@ export async function createMonaco(textarea, filename, editorOpts) {
6262
const monaco = await import(/* webpackChunkName: "monaco" */'monaco-editor');
6363

6464
initLanguages(monaco);
65-
let {language, ...other} = editorOpts;
65+
let {language, eol, ...other} = editorOpts;
6666
if (!language) language = getLanguage(filename);
6767

6868
const container = document.createElement('div');
@@ -105,14 +105,23 @@ export async function createMonaco(textarea, filename, editorOpts) {
105105
monaco.languages.register({id: 'vs.editor.nullLanguage'});
106106
monaco.languages.setLanguageConfiguration('vs.editor.nullLanguage', {});
107107

108+
// TODO: there must be a better way to preserve CRLF in the template rendering
109+
const value = window.monacoContent || '';
110+
delete window.monacoContent;
111+
textarea.value = value;
112+
108113
const editor = monaco.editor.create(container, {
109-
value: textarea.value,
114+
value,
110115
theme: 'gitea',
111116
language,
112117
...other,
113118
});
114119

115120
const model = editor.getModel();
121+
122+
// set eol mode to value from editorconfig or content-detected value
123+
model.setEOL(monaco.editor.EndOfLineSequence[eol || detectEol(value) || 'LF']);
124+
116125
model.onDidChangeContent(() => {
117126
textarea.value = editor.getValue();
118127
textarea.dispatchEvent(new Event('change')); // seems to be needed for jquery-are-you-sure
@@ -187,5 +196,7 @@ function getEditorConfigOptions(ec) {
187196
opts.trimAutoWhitespace = ec.trim_trailing_whitespace === true;
188197
opts.insertSpaces = ec.indent_style === 'space';
189198
opts.useTabStops = ec.indent_style === 'tab';
199+
const eol = ec.end_of_line?.toUpperCase();
200+
if (['LF', 'CRLF'].includes(eol)) opts.eol = eol;
190201
return opts;
191202
}

web_src/js/utils.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,14 @@ export function decodeURLEncodedBase64(base64url) {
128128
.replace(/_/g, '/')
129129
.replace(/-/g, '+'));
130130
}
131+
132+
// Detect dominant line ending in a string
133+
// based on https://github.com/sindresorhus/detect-newline
134+
export function detectEol(str) {
135+
const newlines = (str || '').match(/\r?\n/g) || [];
136+
if (newlines.length === 0) return undefined;
137+
const crlf = newlines.filter((newline) => newline === '\r\n').length;
138+
const lf = newlines.length - crlf;
139+
if (crlf === lf) return undefined;
140+
return crlf > lf ? 'CRLF' : 'LF';
141+
}

web_src/js/utils.test.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
basename, extname, isObject, stripTags, parseIssueHref,
44
parseUrl, translateMonth, translateDay, blobToDataURI,
55
toAbsoluteUrl, encodeURLEncodedBase64, decodeURLEncodedBase64,
6+
detectEol,
67
} from './utils.js';
78

89
test('basename', () => {
@@ -113,3 +114,11 @@ test('encodeURLEncodedBase64, decodeURLEncodedBase64', () => {
113114
expect(Array.from(decodeURLEncodedBase64('YQ'))).toEqual(Array.from(uint8array('a')));
114115
expect(Array.from(decodeURLEncodedBase64('YQ=='))).toEqual(Array.from(uint8array('a')));
115116
});
117+
118+
test('detectEol', () => {
119+
expect(detectEol(undefined)).toEqual(undefined);
120+
expect(detectEol('')).toEqual(undefined);
121+
expect(detectEol('a\nb')).toEqual('LF');
122+
expect(detectEol('a\nb\r\n')).toEqual(undefined);
123+
expect(detectEol('a\nb\r\nc\r\n')).toEqual('CRLF');
124+
});

0 commit comments

Comments
 (0)