diff --git a/frontend/locales/en.json b/frontend/locales/en.json index a80ac44ba..d5f4eb611 100644 --- a/frontend/locales/en.json +++ b/frontend/locales/en.json @@ -612,6 +612,10 @@ "syncScroll": { "label": "Sync Scrolling", "help": "Synchronizes the scroll state of editor and rendering view." + }, + "lineWrapping": { + "label": "Line Wrapping", + "help": "Breaks long lines so they're visible without scrolling." } }, "global": { diff --git a/frontend/src/components/editor-page/editor-pane/editor-pane.tsx b/frontend/src/components/editor-page/editor-pane/editor-pane.tsx index 5c69320a4..b5ff3fab3 100644 --- a/frontend/src/components/editor-page/editor-pane/editor-pane.tsx +++ b/frontend/src/components/editor-page/editor-pane/editor-pane.tsx @@ -13,6 +13,7 @@ import type { ScrollProps } from '../synced-scroll/scroll-props' import styles from './extended-codemirror/codemirror.module.scss' import { useCodeMirrorAutocompletionsExtension } from './hooks/codemirror-extensions/use-code-mirror-autocompletions-extension' import { useCodeMirrorFileInsertExtension } from './hooks/codemirror-extensions/use-code-mirror-file-insert-extension' +import { useCodeMirrorLineWrappingExtension } from './hooks/codemirror-extensions/use-code-mirror-line-wrapping-extension' import { useCodeMirrorRemoteCursorsExtension } from './hooks/codemirror-extensions/use-code-mirror-remote-cursor-extensions' import { useCodeMirrorScrollWatchExtension } from './hooks/codemirror-extensions/use-code-mirror-scroll-watch-extension' import { useCodeMirrorSpellCheckExtension } from './hooks/codemirror-extensions/use-code-mirror-spell-check-extension' @@ -39,7 +40,6 @@ import { markdown, markdownLanguage } from '@codemirror/lang-markdown' import { languages } from '@codemirror/language-data' import { lintGutter } from '@codemirror/lint' import { oneDark } from '@codemirror/theme-one-dark' -import { EditorView } from '@codemirror/view' import ReactCodeMirror from '@uiw/react-codemirror' import React, { useEffect, useMemo } from 'react' import { useTranslation } from 'react-i18next' @@ -67,6 +67,7 @@ export const EditorPane: React.FC = ({ scrollState, onScroll, o const tablePasteExtensions = useCodeMirrorTablePasteExtension() const fileInsertExtension = useCodeMirrorFileInsertExtension() const spellCheckExtension = useCodeMirrorSpellCheckExtension() + const lineWrappingExtension = useCodeMirrorLineWrappingExtension() const cursorActivityExtension = useCursorActivityCallback() const autoCompletionExtension = useCodeMirrorAutocompletionsExtension() @@ -95,7 +96,7 @@ export const EditorPane: React.FC = ({ scrollState, onScroll, o codeLanguages: (input) => findLanguageByCodeBlockName(languages, input) }), remoteCursorsExtension, - EditorView.lineWrapping, + lineWrappingExtension, editorScrollExtension, tablePasteExtensions, fileInsertExtension, @@ -115,7 +116,8 @@ export const EditorPane: React.FC = ({ scrollState, onScroll, o cursorActivityExtension, updateViewContextExtension, yjsExtension, - spellCheckExtension + spellCheckExtension, + lineWrappingExtension ] ) diff --git a/frontend/src/components/editor-page/editor-pane/hooks/codemirror-extensions/use-code-mirror-line-wrapping-extension.ts b/frontend/src/components/editor-page/editor-pane/hooks/codemirror-extensions/use-code-mirror-line-wrapping-extension.ts new file mode 100644 index 000000000..17e59627d --- /dev/null +++ b/frontend/src/components/editor-page/editor-pane/hooks/codemirror-extensions/use-code-mirror-line-wrapping-extension.ts @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { useApplicationState } from '../../../../../hooks/common/use-application-state' +import type { Extension } from '@codemirror/state' +import { EditorView } from '@codemirror/view' +import { useMemo } from 'react' + +/** + * Creates a {@link Extension codemirror extension} that activates or deactivates line wrapping. + */ +export const useCodeMirrorLineWrappingExtension = (): Extension => { + const lineWrapping = useApplicationState((state) => state.editorConfig.lineWrapping) + + return useMemo(() => (lineWrapping ? EditorView.lineWrapping : []), [lineWrapping]) +} diff --git a/frontend/src/components/layout/settings-dialog/editor/editor-settings-tab-content.tsx b/frontend/src/components/layout/settings-dialog/editor/editor-settings-tab-content.tsx index 6da42bb60..3a6f7f079 100644 --- a/frontend/src/components/layout/settings-dialog/editor/editor-settings-tab-content.tsx +++ b/frontend/src/components/layout/settings-dialog/editor/editor-settings-tab-content.tsx @@ -5,6 +5,7 @@ */ import { SettingLine } from '../utils/setting-line' import { LigatureSettingButtonGroup } from './ligature-setting-button-group' +import { LineWrappingSettingButtonGroup } from './line-wrapping-setting-button-group' import { SmartPasteSettingButtonGroup } from './smart-paste-setting-button-group' import { SyncScrollSettingButtonGroup } from './sync-scroll-setting-button-group' import React from 'react' @@ -28,6 +29,9 @@ export const EditorSettingsTabContent: React.FC = () => { + + + ) } diff --git a/frontend/src/components/layout/settings-dialog/editor/line-wrapping-setting-button-group.tsx b/frontend/src/components/layout/settings-dialog/editor/line-wrapping-setting-button-group.tsx new file mode 100644 index 000000000..f2cfbf621 --- /dev/null +++ b/frontend/src/components/layout/settings-dialog/editor/line-wrapping-setting-button-group.tsx @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { useApplicationState } from '../../../../hooks/common/use-application-state' +import { setEditorLineWrapping } from '../../../../redux/editor/methods' +import { OnOffButtonGroup } from '../utils/on-off-button-group' +import React from 'react' + +/** + * Allows to change if line wrapping should be used or not in the editor. + */ +export const LineWrappingSettingButtonGroup: React.FC = () => { + const enabled = useApplicationState((state) => state.editorConfig.lineWrapping) + return +} diff --git a/frontend/src/redux/editor/methods.ts b/frontend/src/redux/editor/methods.ts index c32beec3f..5baf9f541 100644 --- a/frontend/src/redux/editor/methods.ts +++ b/frontend/src/redux/editor/methods.ts @@ -8,6 +8,7 @@ import { Logger } from '../../utils/logger' import type { EditorConfig, SetEditorLigaturesAction, + SetEditorLineWrappingAction, SetEditorSmartPasteAction, SetEditorSyncScrollAction } from './types' @@ -44,6 +45,14 @@ export const setEditorSyncScroll = (syncScroll: boolean): void => { store.dispatch(action) } +export const setEditorLineWrapping = (lineWrapping: boolean): void => { + const action: SetEditorLineWrappingAction = { + type: EditorConfigActionType.SET_LINE_WRAPPING, + lineWrapping + } + store.dispatch(action) +} + export const setEditorLigatures = (ligatures: boolean): void => { const action: SetEditorLigaturesAction = { type: EditorConfigActionType.SET_LIGATURES, diff --git a/frontend/src/redux/editor/reducers.ts b/frontend/src/redux/editor/reducers.ts index 7b82eea66..f8c67d224 100644 --- a/frontend/src/redux/editor/reducers.ts +++ b/frontend/src/redux/editor/reducers.ts @@ -12,7 +12,8 @@ const initialState: EditorConfig = { ligatures: true, syncScroll: true, smartPaste: true, - spellCheck: false + spellCheck: false, + lineWrapping: true } const getInitialState = (): EditorConfig => { @@ -53,6 +54,13 @@ export const EditorConfigReducer: Reducer = ( } saveToLocalStorage(newState) return newState + case EditorConfigActionType.SET_LINE_WRAPPING: + newState = { + ...state, + lineWrapping: action.lineWrapping + } + saveToLocalStorage(newState) + return newState default: return state } diff --git a/frontend/src/redux/editor/types.ts b/frontend/src/redux/editor/types.ts index 5dd8cb1a4..5599fb8c5 100644 --- a/frontend/src/redux/editor/types.ts +++ b/frontend/src/redux/editor/types.ts @@ -9,6 +9,7 @@ export enum EditorConfigActionType { SET_EDITOR_VIEW_MODE = 'editor/view-mode/set', SET_SYNC_SCROLL = 'editor/syncScroll/set', SET_LIGATURES = 'editor/preferences/setLigatures', + SET_LINE_WRAPPING = 'editor/preferences/setLineWrapping', SET_SMART_PASTE = 'editor/preferences/setSmartPaste', SET_SPELL_CHECK = 'editor/preferences/setSpellCheck' } @@ -18,14 +19,21 @@ export interface EditorConfig { ligatures: boolean smartPaste: boolean spellCheck: boolean + lineWrapping: boolean } export type EditorConfigActions = | SetEditorSyncScrollAction | SetEditorLigaturesAction | SetEditorSmartPasteAction + | SetEditorLineWrappingAction | SetSpellCheckAction +export interface SetEditorLineWrappingAction extends Action { + type: EditorConfigActionType.SET_LINE_WRAPPING + lineWrapping: boolean +} + export interface SetEditorSyncScrollAction extends Action { type: EditorConfigActionType.SET_SYNC_SCROLL syncScroll: boolean