diff --git a/cypress/integration/code.spec.ts b/cypress/integration/code.spec.ts deleted file mode 100644 index 89e1c0b91..000000000 --- a/cypress/integration/code.spec.ts +++ /dev/null @@ -1,98 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) - * - * SPDX-License-Identifier: AGPL-3.0-only - */ - -describe('Code', () => { - beforeEach(() => { - cy.visit('/n/test', { - onBeforeLoad (win: Window): void { - cy.spy(win.navigator.clipboard, 'writeText').as('copy') - } - }) - cy.get('.btn.active.btn-outline-secondary > i.fa-columns') - .should('exist') - - cy.get('.CodeMirror') - .click() - .get('textarea') - .as('codeinput') - }) - - describe('without = doesn\'t show gutter', () => { - it('without wrapLines active', () => { - cy.get('@codeinput') - .fill('```javascript \nlet x = 0\n```') - cy.get('.markdown-body > pre > code') - .should('have.class', 'hljs') - }) - - it('with wrapLines active', () => { - cy.get('@codeinput') - .fill('```javascript!\nlet x = 0\n```') - cy.get('.markdown-body > pre > code') - .should('have.class', 'hljs') - .should('have.class', 'wrapLines') - }) - }) - - describe('with = shows gutter', () => { - it('without wrapLines active', () => { - cy.get('@codeinput') - .fill('```javascript=\nlet x = 0\n```') - cy.get('.markdown-body > pre > code') - .should('have.class', 'hljs') - .should('have.class', 'showGutter') - cy.get('.markdown-body > pre > code > span') - .should('have.class', 'linenumber') - .should('have.attr', 'data-line-number', '1') - }) - - it('with wrapLines active', () => { - cy.get('@codeinput') - .fill('```javascript=! \nlet x = 0\n```') - cy.get('.markdown-body > pre > code') - .should('have.class', 'hljs') - .should('have.class', 'showGutter') - .should('have.class', 'wrapLines') - cy.get('.markdown-body > pre > code > span') - .should('have.class', 'linenumber') - .should('have.attr', 'data-line-number', '1') - }) - }) - - describe('with = shows gutter and number is used as startline', () => { - it('without wrapLines active', () => { - cy.get('@codeinput') - .fill('```javascript=100\nlet x = 0\n```') - cy.get('.markdown-body > pre > code') - .should('have.class', 'hljs') - .should('have.class', 'showGutter') - cy.get('.markdown-body > pre > code > span') - .should('have.class', 'linenumber') - .should('have.attr', 'data-line-number', '100') - }) - - it('with wrapLines active', () => { - cy.get('@codeinput') - .fill('```javascript=100! \nlet x = 0\n```') - cy.get('.markdown-body > pre > code') - .should('have.class', 'hljs') - .should('have.class', 'showGutter') - .should('have.class', 'wrapLines') - cy.get('.markdown-body > pre > code > span') - .should('have.class', 'linenumber') - .should('have.attr', 'data-line-number', '100') - }) - }) - - it('has a button', () => { - cy.get('@codeinput') - .fill('```javascript \nlet x = 0\n```') - cy.get('.markdown-body > pre > div > button > i') - .should('have.class', 'fa-files-o') - .click() - cy.get('@copy').should('be.calledWithExactly', 'let x = 0\n') - }) -}) diff --git a/cypress/integration/highlightedCodeBlock.spec.ts b/cypress/integration/highlightedCodeBlock.spec.ts new file mode 100644 index 000000000..1da2f8522 --- /dev/null +++ b/cypress/integration/highlightedCodeBlock.spec.ts @@ -0,0 +1,171 @@ +/* + * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +describe('Code', () => { + beforeEach(() => { + cy.visit('/n/test', { + onBeforeLoad (win: Window): void { + cy.spy(win.navigator.clipboard, 'writeText').as('copy') + } + }) + cy.get('.btn.active.btn-outline-secondary > i.fa-columns') + .should('exist') + + cy.get('.CodeMirror ') + .click() + cy.get('.CodeMirror textarea') + .as('codeinput') + }) + + describe('with just the language', () => { + it('doesn\'t show a gutter', () => { + cy.get('@codeinput') + .fill('```javascript \nlet x = 0\n```') + cy.get('.markdown-body > pre > code.hljs') + .should('be.visible') + .should('not.have.class', 'showGutter') + cy.get('.markdown-body > pre > code.hljs > .linenumber') + .should('not.be.visible') + }) + + describe('and line wrapping', () => { + it('doesn\'t show a gutter', () => { + cy.get('@codeinput') + .fill('```javascript! \nlet x = 0\n```') + cy.get('.markdown-body > pre > code.hljs') + .should('be.visible') + .should('not.have.class', 'showGutter') + .should('have.class', 'wrapLines') + cy.get('.markdown-body > pre > code.hljs > .linenumber') + .should('not.be.visible') + }) + }) + }) + + describe('with the language and show gutter', () => { + it('shows the correct line number', () => { + cy.get('@codeinput') + .fill('```javascript= \nlet x = 0\n```') + cy.get('.markdown-body > pre > code.hljs') + .should('be.visible') + .should('have.class', 'showGutter') + cy.get('.markdown-body > pre > code.hljs > .linenumber') + .should('be.visible') + .text() + .should('eq', '1') + }) + + describe('and line wrapping', () => { + it('shows the correct line number', () => { + cy.get('@codeinput') + .fill('```javascript=! \nlet x = 0\n```') + cy.get('.markdown-body > pre > code.hljs') + .should('be.visible') + .should('have.class', 'showGutter') + .should('have.class', 'wrapLines') + cy.get('.markdown-body > pre > code.hljs > .linenumber') + .should('be.visible') + .text() + .should('eq', '1') + }) + }) + }) + + describe('with the language, show gutter with a start number', () => { + it('shows the correct line number', () => { + cy.get('@codeinput') + .fill('```javascript=100 \nlet x = 0\n```') + cy.get('.markdown-body > pre > code.hljs') + .should('be.visible') + .should('have.class', 'showGutter') + cy.get('.markdown-body > pre > code.hljs > .linenumber') + .should('be.visible') + .text() + .should('eq', '100') + }) + + it('shows the correct line number and continues in another codeblock', () => { + cy.get('@codeinput') + .fill('```javascript=100 \nlet x = 0\nlet y = 1\n```\n\n```javascript=+\nlet y = 2\n```\n') + cy.get('.markdown-body > pre > code.hljs') + .should('be.visible') + .should('have.class', 'showGutter') + .first() + .find('.linenumber') + .first() + .should('be.visible') + .text() + .should('eq', '100') + cy.get('.markdown-body > pre > code.hljs') + .first() + .find('.linenumber') + .last() + .should('be.visible') + .text() + .should('eq', '101') + cy.get('.markdown-body > pre > code.hljs') + .last() + .find('.linenumber') + .first() + .should('be.visible') + .text() + .should('eq', '102') + }) + + describe('and line wrapping', () => { + it('shows the correct line number', () => { + cy.get('@codeinput') + .fill('```javascript=100! \nlet x = 0\n```') + cy.get('.markdown-body > pre > code.hljs') + .should('be.visible') + .should('have.class', 'showGutter') + .should('have.class', 'wrapLines') + cy.get('.markdown-body > pre > code.hljs > .linenumber') + .should('be.visible') + .text() + .should('eq', '100') + }) + + it('shows the correct line number and continues in another codeblock', () => { + cy.get('@codeinput') + .fill('```javascript=100! \nlet x = 0\nlet y = 1\n```\n\n```javascript=+\nlet y = 2\n```\n') + cy.get('.markdown-body > pre > code.hljs') + .should('be.visible') + .should('have.class', 'showGutter') + .should('have.class', 'wrapLines') + .first() + .find('.linenumber') + .first() + .should('be.visible') + .text() + .should('eq', '100') + cy.get('.markdown-body > pre > code.hljs') + .first() + .find('.linenumber') + .last() + .should('be.visible') + .text() + .should('eq', '101') + cy.get('.markdown-body > pre > code.hljs') + .last() + .find('.linenumber') + .first() + .should('be.visible') + .text() + .should('eq', '102') + }) + }) + }) + + it('has a working copy button', () => { + cy.get('@codeinput') + .fill('```javascript \nlet x = 0\n```') + cy.get('.markdown-body > pre > div > button > i') + .should('have.class', 'fa-files-o') + .click() + cy.get('@copy').should('be.calledWithExactly', 'let x = 0\n') + }) +}) diff --git a/src/components/markdown-renderer/replace-components/highlighted-fence/highlighted-code/highlighted-code.scss b/src/components/markdown-renderer/replace-components/highlighted-fence/highlighted-code/highlighted-code.scss index d6c1f8d31..8c9077877 100644 --- a/src/components/markdown-renderer/replace-components/highlighted-fence/highlighted-code/highlighted-code.scss +++ b/src/components/markdown-renderer/replace-components/highlighted-fence/highlighted-code/highlighted-code.scss @@ -26,26 +26,31 @@ display: grid; grid-template-columns: auto minmax(0, 1fr); + .codeline { + grid-column: 2; + white-space: nowrap; + } + + .linenumber { + grid-column: 1; + position: relative; + cursor: default; + z-index: 4; + padding: 0 8px 0 0; + min-width: 20px; + box-sizing: content-box; + color: #afafaf; + border-right: 3px solid #6ce26c; + flex-direction: column; + overflow: hidden; + user-select: none; + align-items: flex-end; + display: none; + } + &.showGutter { .linenumber { - position: relative; - cursor: default; - z-index: 4; - padding: 0 8px 0 0; - min-width: 20px; - box-sizing: content-box; - color: #afafaf; - border-right: 3px solid #6ce26c; - flex-direction: column; - overflow: hidden; - user-select: none; - display: flex; - align-items: flex-end; - - &:before { - content: attr(data-line-number); - } } } diff --git a/src/components/markdown-renderer/replace-components/highlighted-fence/highlighted-code/highlighted-code.tsx b/src/components/markdown-renderer/replace-components/highlighted-fence/highlighted-code/highlighted-code.tsx index 093b1c84c..8d47f09b7 100644 --- a/src/components/markdown-renderer/replace-components/highlighted-fence/highlighted-code/highlighted-code.tsx +++ b/src/components/markdown-renderer/replace-components/highlighted-fence/highlighted-code/highlighted-code.tsx @@ -41,7 +41,9 @@ export const HighlightedCode: React.FC = ({ code, language const unreplacedCode = !!language && languageSupported(language) ? hljs.default.highlight(language, code).value : escapeHtml(code) const replacedDom = replaceCode(unreplacedCode).map((line, index) => ( - + + { (startLineNumber || 1) + index } +
{line}
diff --git a/src/components/markdown-renderer/replace-components/highlighted-fence/highlighted-fence-replacer.tsx b/src/components/markdown-renderer/replace-components/highlighted-fence/highlighted-fence-replacer.tsx index 6e8b50076..2e43ad444 100644 --- a/src/components/markdown-renderer/replace-components/highlighted-fence/highlighted-fence-replacer.tsx +++ b/src/components/markdown-renderer/replace-components/highlighted-fence/highlighted-fence-replacer.tsx @@ -19,7 +19,7 @@ export class HighlightedCodeReplacer extends ComponentReplacer { const language = codeNode.attribs['data-highlight-language'] const extraData = codeNode.attribs['data-extra'] - const extraInfos = /(=(\d*|\+))?(!?)/.exec(extraData) + const extraInfos = /(=(\d+|\+)?)?(!?)/.exec(extraData) let showLineNumbers = false let startLineNumberAttribute = ''