Add table formatting on paste of detected table (#957)

This commit is contained in:
Erik Michelson 2021-03-10 22:52:20 +01:00 committed by GitHub
parent 107f0f6fa3
commit 0b4a0afa16
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 375 additions and 27 deletions

View file

@ -0,0 +1,33 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { ChangeEvent, useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
import { ApplicationState } from '../../../../../redux'
import { setEditorSmartPaste } from '../../../../../redux/editor/methods'
import { EditorPreferenceInput, EditorPreferenceInputType } from './editor-preference-input'
export const EditorPreferenceSmartPasteSelect: React.FC = () => {
const smartPasteEnabled = useSelector((state: ApplicationState) => Boolean(state.editorConfig.smartPaste)
.toString())
const saveSmartPaste = useCallback((event: ChangeEvent<HTMLSelectElement>) => {
const smartPasteActivated: boolean = event.target.value === 'true'
setEditorSmartPaste(smartPasteActivated)
}, [])
const { t } = useTranslation()
return (
<EditorPreferenceInput
onChange={ saveSmartPaste }
value={ smartPasteEnabled }
property={ 'smartPaste' }
type={ EditorPreferenceInputType.BOOLEAN }
>
<option value='true'>{ t(`common.yes`) }</option>
<option value='false'>{ t(`common.no`) }</option>
</EditorPreferenceInput>
)
}

View file

@ -19,6 +19,7 @@ import { EditorPreferenceLigaturesSelect } from './editor-preference-ligatures-s
import { EditorPreferenceNumberProperty } from './editor-preference-number-property'
import { EditorPreferenceProperty } from './editor-preference-property'
import { EditorPreferenceSelectProperty } from './editor-preference-select-property'
import { EditorPreferenceSmartPasteSelect } from './editor-preference-smart-paste-select'
export const EditorPreferences: React.FC = () => {
const { t } = useTranslation()
@ -57,6 +58,9 @@ export const EditorPreferences: React.FC = () => {
<ListGroup.Item>
<EditorPreferenceLigaturesSelect/>
</ListGroup.Item>
<ListGroup.Item>
<EditorPreferenceSmartPasteSelect/>
</ListGroup.Item>
<ListGroup.Item>
<EditorPreferenceInput onChange={ () => alert('This feature is not yet implemented.') }
property={ EditorPreferenceProperty.SPELL_CHECK }

View file

@ -0,0 +1,63 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Mock } from 'ts-mockery'
import { Editor } from 'codemirror'
import { isCursorInCodefence } from './codefenceDetection'
Mock.configure('jest')
const mockEditor = (content: string, line: number) => {
const contentLines = content.split('\n')
return Mock.of<Editor>({
getCursor() {
return {
line: line,
ch: 0
}
},
getDoc() {
return {
getLine(ln: number) {
return contentLines[ln] ?? ''
}
}
}
})
}
describe('Check whether cursor is in codefence', () => {
it('returns false for empty document', () => {
const editor = mockEditor('', 0)
expect(isCursorInCodefence(editor))
.toBe(false)
})
it('returns true with one open codefence directly above', () => {
const editor = mockEditor('```\n', 1)
expect(isCursorInCodefence(editor))
.toBe(true)
})
it('returns true with one open codefence and empty lines above', () => {
const editor = mockEditor('```\n\n\n', 3)
expect(isCursorInCodefence(editor))
.toBe(true)
})
it('returns false with one completed codefence above', () => {
const editor = mockEditor('```\n\n```\n', 3)
expect(isCursorInCodefence(editor))
.toBe(false)
})
it('returns true with one completed and one open codefence above', () => {
const editor = mockEditor('```\n\n```\n\n```\n\n', 6)
expect(isCursorInCodefence(editor))
.toBe(true)
})
})

View file

@ -0,0 +1,19 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Editor } from 'codemirror'
export const isCursorInCodefence = (editor: Editor): boolean => {
const currentLine = editor.getCursor().line
let codefenceCount = 0
for (let line = currentLine; line >= 0; --line) {
const markdownContentLine = editor.getDoc().getLine(line)
if (markdownContentLine.startsWith('```')) {
codefenceCount++
}
}
return codefenceCount % 2 === 1
}

View file

@ -0,0 +1,45 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Editor } from 'codemirror'
import { convertClipboardTableToMarkdown, isTable } from '../../table-extractor'
import { handleUpload } from '../../upload-handler'
import { insertAtCursor } from './toolbarButtonUtils'
import { isCursorInCodefence } from './codefenceDetection'
type ClipboardDataFormats = 'text' | 'url' | 'text/plain' | 'text/uri-list' | 'text/html'
export interface PasteEvent {
clipboardData: {
files: FileList,
getData: (format: ClipboardDataFormats) => string
},
preventDefault: () => void
}
export const handleTablePaste = (event: PasteEvent, editor: Editor): boolean => {
const pasteText = event.clipboardData.getData('text')
if (!pasteText || isCursorInCodefence(editor) || !isTable(pasteText)) {
return false
}
event.preventDefault()
const markdownTable = convertClipboardTableToMarkdown(pasteText)
insertAtCursor(editor, markdownTable)
return true
}
export const handleFilePaste = (event: PasteEvent, editor: Editor): boolean => {
if (!event.clipboardData.files || event.clipboardData.files.length < 1) {
return false
}
event.preventDefault()
const files: FileList = event.clipboardData.files
if (files && files.length >= 1) {
handleUpload(files[0], editor)
return true
}
return false
}