Skip to content

Commit

Permalink
Merge pull request #2493 from codecrafters-io/code-mirror/fix-no-newl…
Browse files Browse the repository at this point in the history
…ine-eof

Fix "No newline at end of file" in diffs & add `@highlightNewlines` option to CodeMirror
  • Loading branch information
VasylMarchuk authored Dec 28, 2024
2 parents db2d283 + d797d7f commit 4f538f2
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 0 deletions.
1 change: 1 addition & 0 deletions app/components/code-mirror.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
{{did-update this.optionDidChange "editable" @editable}}
{{did-update this.optionDidChange "foldGutter" @foldGutter}}
{{did-update this.optionDidChange "highlightActiveLine" @highlightActiveLine}}
{{did-update this.optionDidChange "highlightNewlines" @highlightNewlines}}
{{did-update this.optionDidChange "highlightSelectionMatches" @highlightSelectionMatches}}
{{did-update this.optionDidChange "highlightSpecialChars" @highlightSpecialChars}}
{{did-update this.optionDidChange "highlightTrailingWhitespace" @highlightTrailingWhitespace}}
Expand Down
6 changes: 6 additions & 0 deletions app/components/code-mirror.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
} from '@codemirror/language';
import { languages } from '@codemirror/language-data';
import { markdown } from '@codemirror/lang-markdown';
import { highlightNewlines } from 'codecrafters-frontend/utils/code-mirror-highlight-newlines';

function generateHTMLElement(src: string): HTMLElement {
const div = document.createElement('div');
Expand Down Expand Up @@ -65,6 +66,7 @@ const OPTION_HANDLERS: { [key: string]: OptionHandler } = {
dropCursor: ({ dropCursor: enabled }) => (enabled ? [dropCursor()] : []),
editable: ({ editable }) => [EditorView.editable.of(!!editable)],
highlightActiveLine: ({ highlightActiveLine: enabled }) => (enabled ? [highlightActiveLine(), highlightActiveLineGutter()] : []),
highlightNewlines: ({ highlightNewlines: enabled }) => (enabled ? [highlightNewlines()] : []),
highlightSelectionMatches: ({ highlightSelectionMatches: enabled }) => (enabled ? [highlightSelectionMatches()] : []),
highlightSpecialChars: ({ highlightSpecialChars: enabled }) => (enabled ? [highlightSpecialChars()] : []),
highlightTrailingWhitespace: ({ highlightTrailingWhitespace: enabled }) => (enabled ? [highlightTrailingWhitespace()] : []),
Expand Down Expand Up @@ -228,6 +230,10 @@ export interface Signature {
* Enable inline highlighting of changes in the diff
*/
highlightChanges?: boolean;
/**
* Enable highlighting of new line symbols
*/
highlightNewlines?: boolean;
/**
* Enable highlighting of current selection matches in the document
*/
Expand Down
3 changes: 3 additions & 0 deletions app/controllers/demo/code-mirror.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const OPTION_DEFAULTS = {
foldGutter: true,
highlightActiveLine: true,
highlightChanges: false,
highlightNewlines: false,
highlightSelectionMatches: true,
highlightSpecialChars: true,
highlightTrailingWhitespace: true,
Expand Down Expand Up @@ -97,6 +98,7 @@ export default class DemoCodeMirrorController extends Controller {
'foldGutter',
'highlightActiveLine',
'highlightChanges',
'highlightNewlines',
'highlightSelectionMatches',
'highlightSpecialChars',
'highlightTrailingWhitespace',
Expand Down Expand Up @@ -147,6 +149,7 @@ export default class DemoCodeMirrorController extends Controller {
@tracked foldGutter = OPTION_DEFAULTS.foldGutter;
@tracked highlightActiveLine = OPTION_DEFAULTS.highlightActiveLine;
@tracked highlightChanges = OPTION_DEFAULTS.highlightChanges;
@tracked highlightNewlines = OPTION_DEFAULTS.highlightNewlines;
@tracked highlightSelectionMatches = OPTION_DEFAULTS.highlightSelectionMatches;
@tracked highlightSpecialChars = OPTION_DEFAULTS.highlightSpecialChars;
@tracked highlightTrailingWhitespace = OPTION_DEFAULTS.highlightTrailingWhitespace;
Expand Down
1 change: 1 addition & 0 deletions app/routes/demo/code-mirror.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const QUERY_PARAMS = [
'foldGutter',
'highlightActiveLine',
'highlightChanges',
'highlightNewlines',
'highlightSelectionMatches',
'highlightSpecialChars',
'highlightTrailingWhitespace',
Expand Down
5 changes: 5 additions & 0 deletions app/templates/demo/code-mirror.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,10 @@
<Input @type="checkbox" @checked={{this.highlightTrailingWhitespace}} />
<span class="ml-2">highlightTrailingWhitespace</span>
</label>
<label class="{{labelClasses}}" title="Enable highlighting of new line symbols">
<Input @type="checkbox" @checked={{this.highlightNewlines}} />
<span class="ml-2">highlightNewlines</span>
</label>
<label class="{{labelClasses}}" title="Enable inline highlighting of changes in the diff">
<Input @type="checkbox" @checked={{this.highlightChanges}} disabled={{not this.originalDocument}} />
<span class="ml-2 {{unless this.originalDocument 'text-gray-300'}}">highlightChanges</span>
Expand Down Expand Up @@ -328,6 +332,7 @@
@foldGutter={{this.foldGutter}}
@highlightActiveLine={{this.highlightActiveLine}}
@highlightChanges={{this.highlightChanges}}
@highlightNewlines={{this.highlightNewlines}}
@highlightSelectionMatches={{this.highlightSelectionMatches}}
@highlightSpecialChars={{this.highlightSpecialChars}}
@highlightTrailingWhitespace={{this.highlightTrailingWhitespace}}
Expand Down
84 changes: 84 additions & 0 deletions app/utils/code-mirror-highlight-newlines.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import type { Line } from '@codemirror/state';
import type { DecorationSet, ViewUpdate } from '@codemirror/view';
import { Decoration, EditorView, ViewPlugin, WidgetType } from '@codemirror/view';

class NewlineWidget extends WidgetType {
line: Line;
markerSymbol: string;

constructor({ line, markerSymbol = '↵' }: { line: Line; markerSymbol?: string }) {
super();
this.line = line;
this.markerSymbol = markerSymbol;
}

toDOM() {
const span = document.createElement('span');

span.textContent = this.markerSymbol;

span.className = 'cm-newline';

if (this.line.length === 0) {
span.className += ' cm-newline-empty';
}

return span;
}
}

const baseTheme = EditorView.baseTheme({
'.cm-newline': {
color: 'currentColor',
pointerEvents: 'none',
opacity: '0.5',
'&:not(.cm-newline-empty)': {
paddingLeft: '3px',
},
},
});

function highlightNewlines() {
return [
ViewPlugin.fromClass(
class {
decorations: DecorationSet;
constructor(view: EditorView) {
this.decorations = this.getDecorations(view);
}

getDecorations(view: EditorView) {
const widgets = [];

for (const { from, to } of view.visibleRanges) {
for (let pos = from; pos <= to; ) {
const line = view.state.doc.lineAt(pos);

if (line.length === 0) {
widgets.push(Decoration.widget({ widget: new NewlineWidget({ line }), side: 1 }).range(pos));
} else {
widgets.push(Decoration.widget({ widget: new NewlineWidget({ line }), side: 1 }).range(line.to));
}

pos = line.to + 1;
}
}

return Decoration.set(widgets, true);
}

update(update: ViewUpdate) {
if (update.docChanged || update.viewportChanged) {
this.decorations = this.getDecorations(update.view);
}
}
},
{
decorations: (v) => v.decorations,
},
),
baseTheme,
];
}

export { highlightNewlines };
4 changes: 4 additions & 0 deletions app/utils/parse-diff-as-document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ export default function parseDiffAsDocument(diff: string = '') {
const original = [];

for (const line of diffLines) {
if (line === '\\ No newline at end of file') {
continue;
}

if (line.startsWith('-')) {
original.push(line.substring(1));
} else if (line.startsWith('+')) {
Expand Down
12 changes: 12 additions & 0 deletions tests/integration/components/code-mirror-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,18 @@ module('Integration | Component | code-mirror', function (hooks) {
skip('it does something useful with the editor');
});

module('highlightNewlines', function () {
test("it doesn't break the editor when passed", async function (assert) {
this.set('highlightNewlines', true);
await render(hbs`<CodeMirror @highlightNewlines={{this.highlightNewlines}} />`);
assert.ok(codeMirror.hasRendered);
this.set('highlightNewlines', false);
assert.ok(codeMirror.hasRendered);
});

skip('it does something useful with the editor');
});

module('highlightSelectionMatches', function () {
test("it doesn't break the editor when passed", async function (assert) {
this.set('highlightSelectionMatches', true);
Expand Down
6 changes: 6 additions & 0 deletions tests/unit/utils/parse-diff-as-document-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,10 @@ module('Unit | Utility | parse-diff-as-document', function () {
assert.strictEqual(result.current, '', 'current document is an empty string');
assert.strictEqual(result.original, '', 'original document is an empty string');
});

test('it strips "\\ No newline at end of file" messages from the diff', async function (assert) {
const result = parseDiffAsDocument(' 1234\n\\ No newline at end of file\n+2345\n 6789\n-0123\n');
assert.strictEqual(result.current, '1234\n2345\n6789\n', 'current document is parsed correctly');
assert.strictEqual(result.original, '1234\n6789\n0123\n', 'original document is parsed correctly');
});
});

0 comments on commit 4f538f2

Please sign in to comment.