Skip to content

Commit

Permalink
Buffer updates
Browse files Browse the repository at this point in the history
  • Loading branch information
kazcw committed Jun 3, 2024
1 parent ecd1e01 commit d328f76
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 22 deletions.
15 changes: 6 additions & 9 deletions app/gui2/src/components/lexical/formatting.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useBufferedWritable } from '@/util/reactivity'
import { $createCodeNode } from '@lexical/code'
import {
$isListNode,
Expand Down Expand Up @@ -30,7 +31,7 @@ import {
FORMAT_TEXT_COMMAND,
SELECTION_CHANGE_COMMAND,
} from 'lexical'
import { computed, ref } from 'vue'
import { ref } from 'vue'

export function useFormatting(editor: LexicalEditor) {
const selectionReaders = new Array<(selection: RangeSelection) => void>()
Expand Down Expand Up @@ -77,11 +78,9 @@ function useFormatProperty(

onReadSelection((selection) => (state.value = selection.hasFormat(property)))

return computed({
return useBufferedWritable({
get: () => state.value,
set: (value) => {
if (value !== state.value) editor.dispatchCommand(FORMAT_TEXT_COMMAND, property)
},
set: (_value) => editor.dispatchCommand(FORMAT_TEXT_COMMAND, property),
})
}

Expand Down Expand Up @@ -227,10 +226,8 @@ function useBlockType(
h3: () => $setBlocksType($getSelection(), () => $createHeadingNode('h3')),
}

return computed({
return useBufferedWritable({
get: () => state.value,
set: (value) => {
if (value !== state.value) editor.update($setBlockType[value])
},
set: (value) => editor.update($setBlockType[value]),
})
}
16 changes: 8 additions & 8 deletions app/gui2/src/components/lexical/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { blockTypes, normalizeHeadingLevel } from '@/components/lexical/formatting'
import { normalizeHeadingLevel } from '@/components/lexical/formatting'
import { unrefElement, type MaybeElement } from '@vueuse/core'
import {
createEditor,
type EditorThemeClasses,
type KlassConstructor,
type LexicalEditor,
type LexicalNode,
type LexicalNodeReplacement,
import type {
EditorThemeClasses,
KlassConstructor,
LexicalEditor,
LexicalNode,
LexicalNodeReplacement,
} from 'lexical'
import { createEditor } from 'lexical'
import { assertDefined } from 'shared/util/assert'
import { markRaw, onMounted, type Ref } from 'vue'

Expand Down
4 changes: 2 additions & 2 deletions app/gui2/src/components/lexical/sync.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ToValue } from '@/util/reactivity'
import { useBufferedWritable, type ToValue } from '@/util/reactivity'
import type { LexicalEditor } from 'lexical'
import { $createParagraphNode, $createTextNode, $getRoot, $setSelection } from 'lexical'
import { computed, shallowRef, toValue } from 'vue'
Expand Down Expand Up @@ -37,7 +37,7 @@ export function useLexicalSync<T>(
})

return {
content: computed({
content: useBufferedWritable({
get: () => getContent.value,
set: (content) => {
editor.update(() => $write(content, getContent), {
Expand Down
6 changes: 3 additions & 3 deletions app/gui2/src/composables/astDocumentation.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { type GraphStore } from '@/stores/graph'
import type { ToValue } from '@/util/reactivity'
import { useBufferedWritable, type ToValue } from '@/util/reactivity'
import type { Ast } from 'shared/ast'
import { computed, toValue } from 'vue'
import { toValue } from 'vue'

export function useAstDocumentation(graphStore: GraphStore, ast: ToValue<Ast | undefined>) {
return {
documentation: computed({
documentation: useBufferedWritable({
get: () => toValue(ast)?.documentingAncestor()?.documentation() ?? '',
set: (value) => {
const astValue = toValue(ast)
Expand Down
26 changes: 26 additions & 0 deletions app/gui2/src/util/reactivity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,29 @@ export function computedFallback<T>(
set: (val: T) => (base.value = val),
})
}

/** Given a "raw" getter and setter, returns a writable-computed that buffers `set` operations.
*
* When the setter of the returned ref is invoked, the raw setter will be called during the next callback flush if and
* only if the most recently set value does not compare strictly-equal to the current value (read from the raw getter).
*
* The getter of the returned ref immediately reflects the value of any pending write.
*/
export function useBufferedWritable<T>(raw: {
get: () => T
set: (value: T) => void
}): WritableComputedRef<T> {
const pendingWrite = shallowRef<{ pending: T }>()
watch(pendingWrite, () => {
if (pendingWrite.value) {
if (pendingWrite.value.pending !== raw.get()) {
raw.set(pendingWrite.value.pending)
}
pendingWrite.value = undefined
}
})
return computed({
get: () => (pendingWrite.value ? pendingWrite.value.pending : raw.get()),
set: (value: T) => (pendingWrite.value = { pending: value }),
})
}

0 comments on commit d328f76

Please sign in to comment.