Просмотр исходного кода

[visual] Decorate horizontal spacing macros (#19200)

GitOrigin-RevId: 9ca1be0c00d1483b5d3a9baa214445ce4e3cf36c
Alf Eaton 2 лет назад
Родитель
Сommit
b156fb24d3

+ 16 - 0
services/web/frontend/js/features/source-editor/extensions/visual/atomic-decorations.ts

@@ -78,6 +78,10 @@ import {
 } from '../../components/table-generator/utils'
 import { debugConsole } from '@/utils/debugging'
 import { DescriptionItemWidget } from './visual-widgets/description-item'
+import {
+  createSpaceCommand,
+  hasSpaceSubstitution,
+} from '@/features/source-editor/extensions/visual/visual-widgets/space'
 
 type Options = {
   previewByPath: (path: string) => PreviewPath | null
@@ -1199,6 +1203,18 @@ export const atomicDecorations = (options: Options) => {
                     return false
                   }
                 }
+              } else if (hasSpaceSubstitution(commandName)) {
+                if (shouldDecorate(state, nodeRef)) {
+                  const replacement = createSpaceCommand(commandName)
+                  if (replacement) {
+                    decorations.push(
+                      Decoration.replace({
+                        widget: replacement,
+                      }).range(nodeRef.from, nodeRef.to)
+                    )
+                    return false
+                  }
+                }
               }
             }
           }

+ 3 - 0
services/web/frontend/js/features/source-editor/extensions/visual/visual-theme.ts

@@ -288,6 +288,9 @@ const mainVisualTheme = EditorView.theme({
     overflowWrap: 'break-word',
     hyphens: 'auto',
   },
+  '.ol-cm-space': {
+    display: 'inline-block',
+  },
   '.ol-cm-environment-centered': {
     '&.ol-cm-label-line, &.ol-cm-caption-line': {
       textAlign: 'center',

+ 61 - 0
services/web/frontend/js/features/source-editor/extensions/visual/visual-widgets/space.ts

@@ -0,0 +1,61 @@
+import { WidgetType } from '@codemirror/view'
+
+export class SpaceWidget extends WidgetType {
+  constructor(public width: string) {
+    super()
+  }
+
+  toDOM() {
+    const element = document.createElement('span')
+    element.classList.add('ol-cm-space')
+    element.style.width = this.width
+    return element
+  }
+
+  eq(widget: SpaceWidget) {
+    return widget.width === this.width
+  }
+
+  updateDOM(element: HTMLElement): boolean {
+    element.style.width = this.width
+    return true
+  }
+
+  ignoreEvent(event: Event) {
+    return event.type !== 'mousedown' && event.type !== 'mouseup'
+  }
+}
+
+// https://tex.stackexchange.com/a/74354
+export const COMMAND_WIDTHS = new Map([
+  // thin space
+  ['\\thinspace', 'calc(3em / 18)'],
+  ['\\,', 'calc(3em / 18)'],
+  // negative thin space
+  ['\\negthinspace', 'calc(-3em / 18)'],
+  ['\\!', 'calc(-3em / 18)'],
+  // medium space
+  ['\\medspace', 'calc(4em / 18)'],
+  ['\\:', 'calc(4em / 18)'],
+  ['\\>', 'calc(4em / 18)'],
+  // thick space
+  ['\\thickspace', 'calc(5em / 18)'],
+  ['\\;', 'calc(5em / 18)'],
+  // negative thick space
+  ['\\negthickspace', 'calc(-5em / 18)'],
+  // en, em and 2xem spaces
+  ['\\enspace', '0.5em'],
+  ['\\quad', '1em'],
+  ['\\qquad', '2em'],
+])
+
+export function createSpaceCommand(command: string): SpaceWidget | undefined {
+  const width = COMMAND_WIDTHS.get(command)
+  if (width !== undefined) {
+    return new SpaceWidget(width)
+  }
+}
+
+export function hasSpaceSubstitution(command: string): boolean {
+  return COMMAND_WIDTHS.has(command)
+}

+ 16 - 0
services/web/test/frontend/features/source-editor/components/codemirror-editor-visual.spec.tsx

@@ -642,6 +642,22 @@ describe('<CodeMirrorEditor/> in Visual mode', function () {
       cy.get('@second-line').click()
       cy.get('@first-line').should('have.text', 'Test ↩ test')
     })
+
+    it('decorates spacing commands', function () {
+      cy.get('@first-line').type('\\thinspace')
+      cy.get('@second-line').click()
+      cy.get('@first-line')
+        .find('.ol-cm-space')
+        .should('have.attr', 'style', 'width: calc(0.166667em);')
+    })
+
+    it('decorates spacing symbols', function () {
+      cy.get('@first-line').type('\\,')
+      cy.get('@second-line').click()
+      cy.get('@first-line')
+        .find('.ol-cm-space')
+        .should('have.attr', 'style', 'width: calc(0.166667em);')
+    })
   })
 
   describe('decorates theorems', function () {