Cleanup @/util folder (#8501)

- Partially addresses #8228
- Moves several files into `@/util/vue` and `@/util/data`
- Makes imports more consistent:
- Remove redundant trailing `.ts`
- Convert *some* imports to use `@/` rather than `./`
- Ideally it should be all imports other than the ones outside of `src/`, but I can't be sure I've found all of them
- Merge *some* duplicated imports (caused by one being an `import type`)

# Important Notes
None
This commit is contained in:
somebody1234 2023-12-14 08:27:31 +10:00 committed by GitHub
parent f5c3713f87
commit 9fb3d9a2fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
110 changed files with 340 additions and 259 deletions

View File

@ -4,8 +4,8 @@ import { createApp, ref } from 'vue'
import '../src/assets/base.css'
import { provideGuiConfig } from '../src/providers/guiConfig'
import { provideVisualizationConfig } from '../src/providers/visualizationConfig'
import { Vec2 } from '../src/util/data/vec2'
import { MockTransport, MockWebSocket } from '../src/util/net'
import { Vec2 } from '../src/util/vec2'
import MockApp from './MockApp.vue'
import { mockDataHandler, mockLSHandler } from './mockEngine'

View File

@ -13,8 +13,18 @@ await fs.writeFile(
`\
// Generated by \`scripts/generateIcons.js\`.
// Please run \`npm run generate\` to regenerate this file whenever \`icons.svg\` is changed.
import iconNames from '@/util/iconList.json'
export type Icon =
${iconNames?.map((name) => ` | '${name}'`).join('\n')}
export { iconNames }
const iconNamesSet = new Set(iconNames)
export function isIconName(value: string): value is Icon {
return iconNamesSet.has(value)
}
`,
)
console.info('Done.')

View File

@ -1,16 +1,16 @@
<script setup lang="ts">
import type { Diagnostic, Highlighter } from '@/components/CodeEditor/codemirror'
import { usePointer } from '@/composables/events'
import { useGraphStore } from '@/stores/graph'
import { useProjectStore } from '@/stores/project'
import { useSuggestionDbStore } from '@/stores/suggestionDatabase'
import { useAutoBlur } from '@/util/autoBlur'
import type { Diagnostic, Highlighter } from '@/util/codemirror'
import { usePointer } from '@/util/events'
import { chain } from '@/util/iterable'
import { chain } from '@/util/data/iterable'
import { unwrap } from '@/util/data/result'
import { qnJoin, tryQualifiedName } from '@/util/qualifiedName'
import { useLocalStorage } from '@vueuse/core'
import { rangeEncloses, type ExprId } from 'shared/yjsModel'
import { computed, onMounted, ref, watch, watchEffect } from 'vue'
import { qnJoin, tryQualifiedName } from '../util/qualifiedName'
import { unwrap } from '../util/result'
// Use dynamic imports to aid code splitting. The codemirror dependency is quite large.
const {
@ -30,7 +30,7 @@ const {
forceLinting,
lsDiagnosticsToCMDiagnostics,
hoverTooltip,
} = await import('@/util/codemirror')
} = await import('@/components/CodeEditor/codemirror')
const projectStore = useProjectStore()
const graphStore = useGraphStore()

View File

@ -2,26 +2,26 @@
import { componentBrowserBindings } from '@/bindings'
import { makeComponentList, type Component } from '@/components/ComponentBrowser/component'
import { Filtering } from '@/components/ComponentBrowser/filtering'
import { useComponentBrowserInput, type Usage } from '@/components/ComponentBrowser/input'
import { default as DocumentationPanel } from '@/components/DocumentationPanel.vue'
import GraphVisualization from '@/components/GraphEditor/GraphVisualization.vue'
import SvgIcon from '@/components/SvgIcon.vue'
import ToggleIcon from '@/components/ToggleIcon.vue'
import { useApproach } from '@/composables/animation'
import { useEvent, useResizeObserver } from '@/composables/events'
import type { useNavigator } from '@/composables/navigator'
import { useGraphStore } from '@/stores/graph'
import type { RequiredImport } from '@/stores/graph/imports'
import { useProjectStore } from '@/stores/project'
import { groupColorStyle, useSuggestionDbStore } from '@/stores/suggestionDatabase'
import { SuggestionKind, type SuggestionEntry } from '@/stores/suggestionDatabase/entry'
import type { VisualizationDataSource } from '@/stores/visualization'
import { useApproach } from '@/util/animation'
import { tryGetIndex } from '@/util/array'
import { useEvent, useResizeObserver } from '@/util/events'
import type { useNavigator } from '@/util/navigator'
import type { Opt } from '@/util/opt'
import { allRanges } from '@/util/range'
import { Vec2 } from '@/util/vec2'
import { tryGetIndex } from '@/util/data/array'
import type { Opt } from '@/util/data/opt'
import { allRanges } from '@/util/data/range'
import { Vec2 } from '@/util/data/vec2'
import type { SuggestionId } from 'shared/languageServerTypes/suggestions'
import { computed, nextTick, onMounted, ref, watch, type ComputedRef, type Ref } from 'vue'
import { useComponentBrowserInput, type Usage } from './ComponentBrowser/input'
import GraphVisualization from './GraphEditor/GraphVisualization.vue'
const ITEM_SIZE = 32
const TOP_BAR_HEIGHT = 32

View File

@ -16,7 +16,7 @@ import {
makeStaticMethod,
type SuggestionEntry,
} from '@/stores/suggestionDatabase/entry'
import { allRanges } from '@/util/range'
import { allRanges } from '@/util/data/range'
import shuffleSeed from 'shuffle-seed'
test.each([

View File

@ -11,8 +11,8 @@ import {
makeStaticMethod,
makeType,
} from '@/stores/suggestionDatabase/entry'
import { unwrap } from '@/util/data/result'
import { tryQualifiedName, type QualifiedName } from '@/util/qualifiedName'
import { unwrap } from '@/util/result'
test.each([
{ ...makeModuleMethod('Standard.Base.Data.read'), groupIndex: 0 },

View File

@ -1,3 +1,4 @@
import { useComponentBrowserInput } from '@/components/ComponentBrowser/input'
import { GraphDb } from '@/stores/graph/graphDatabase'
import type { RequiredImport } from '@/stores/graph/imports'
import { ComputedValueRegistry } from '@/stores/project/computedValueRegistry'
@ -13,11 +14,10 @@ import {
makeType,
type SuggestionEntry,
} from '@/stores/suggestionDatabase/entry'
import { unwrap } from '@/util/data/result'
import { tryIdentifier, tryQualifiedName } from '@/util/qualifiedName'
import { unwrap } from '@/util/result'
import type { ExprId } from 'shared/yjsModel'
import { expect, test } from 'vitest'
import { useComponentBrowserInput } from '../input'
test.each([
['', 0, { type: 'insert', position: 0 }, {}],

View File

@ -5,10 +5,10 @@ import {
type Environment,
type Placement,
} from '@/components/ComponentBrowser/placement'
import * as iterable from '@/util/iterable'
import { chain, map, range } from '@/util/iterable'
import { Rect } from '@/util/rect'
import { Vec2 } from '@/util/vec2'
import * as iterable from '@/util/data/iterable'
import { chain, map, range } from '@/util/data/iterable'
import { Rect } from '@/util/data/rect'
import { Vec2 } from '@/util/data/vec2'
import { fc, test as fcTest } from '@fast-check/vitest'
import { describe, expect, test, vi } from 'vitest'

View File

@ -6,11 +6,11 @@ import {
type SuggestionId,
} from '@/stores/suggestionDatabase/entry'
import { compareOpt } from '@/util/compare'
import { isSome } from '@/util/data/opt'
import { Range } from '@/util/data/range'
import { displayedIconOf } from '@/util/getIconName'
import type { Icon } from '@/util/iconName'
import { isSome } from '@/util/opt'
import { qnIsTopElement, qnLastSegmentIndex } from '@/util/qualifiedName'
import { Range } from '@/util/range'
interface ComponentLabel {
label: string

View File

@ -3,9 +3,9 @@ import {
type SuggestionEntry,
type Typename,
} from '@/stores/suggestionDatabase/entry'
import type { Opt } from '@/util/opt'
import type { Opt } from '@/util/data/opt'
import { Range } from '@/util/data/range'
import { qnIsTopElement, qnParent, type QualifiedName } from '@/util/qualifiedName'
import { Range } from '@/util/range'
export type SelfArg =
| {

View File

@ -1,7 +1,7 @@
import { bail } from '@/util/assert'
import { Rect } from '@/util/rect'
import { Rect } from '@/util/data/rect'
import { Vec2 } from '@/util/data/vec2'
import theme from '@/util/theme.json'
import { Vec2 } from '@/util/vec2'
export interface Environment {
screenBounds: Rect

View File

@ -11,9 +11,9 @@ import type { Docs, FunctionDocs, Sections, TypeDocs } from '@/components/Docume
import { lookupDocumentation, placeholder } from '@/components/DocumentationPanel/ir'
import { groupColorStyle, useSuggestionDbStore } from '@/stores/suggestionDatabase'
import type { SuggestionId } from '@/stores/suggestionDatabase/entry'
import { tryGetIndex } from '@/util/array'
import { tryGetIndex } from '@/util/data/array'
import { type Opt } from '@/util/data/opt'
import type { Icon as IconName } from '@/util/iconName'
import { type Opt } from '@/util/opt'
import type { QualifiedName } from '@/util/qualifiedName'
import { qnSegments, qnSlice } from '@/util/qualifiedName'
import { computed, watch } from 'vue'

View File

@ -1,6 +1,6 @@
<script setup lang="ts">
import SvgIcon from '@/components/SvgIcon.vue'
import { useEvent } from '@/util/events'
import { useEvent } from '@/composables/events'
import { ref } from 'vue'
const props = defineProps<{ modes: string[]; modelValue: string }>()

View File

@ -7,14 +7,15 @@ import {
nonDictatedPlacement,
previousNodeDictatedPlacement,
type Environment,
} from '@/components/ComponentBrowser/placement.ts'
} from '@/components/ComponentBrowser/placement'
import GraphEdges from '@/components/GraphEditor/GraphEdges.vue'
import GraphNodes from '@/components/GraphEditor/GraphNodes.vue'
import { Uploader, uploadedExpression } from '@/components/GraphEditor/upload'
import GraphMouse from '@/components/GraphMouse.vue'
import PlusButton from '@/components/PlusButton.vue'
import TopBar from '@/components/TopBar.vue'
import { useDoubleClick } from '@/composables/doubleClick.ts'
import { useDoubleClick } from '@/composables/doubleClick'
import { keyboardBusy, keyboardBusyExceptIn, useEvent } from '@/composables/events'
import { provideGraphNavigator } from '@/providers/graphNavigator'
import { provideGraphSelection } from '@/providers/graphSelection'
import { provideInteractionHandler, type Interaction } from '@/providers/interactionHandler'
@ -24,12 +25,11 @@ import type { RequiredImport } from '@/stores/graph/imports'
import { useProjectStore } from '@/stores/project'
import { groupColorVar, useSuggestionDbStore } from '@/stores/suggestionDatabase'
import { colorFromString } from '@/util/colors'
import { keyboardBusy, keyboardBusyExceptIn, useEvent } from '@/util/events'
import { qnLastSegment, tryQualifiedName } from '@/util/qualifiedName.ts'
import { Rect } from '@/util/rect.ts'
import { Vec2 } from '@/util/vec2'
import { Rect } from '@/util/data/rect'
import { Vec2 } from '@/util/data/vec2'
import { qnLastSegment, tryQualifiedName } from '@/util/qualifiedName'
import * as set from 'lib0/set'
import type { ExprId, NodeMetadata } from 'shared/yjsModel.ts'
import type { ExprId, NodeMetadata } from 'shared/yjsModel'
import { computed, onMounted, ref, watch } from 'vue'
import { type Usage } from './ComponentBrowser/input'
@ -216,7 +216,7 @@ const graphBindingsHandler = graphBindings.handler({
})
const handleClick = useDoubleClick(
(e) => {
(e: MouseEvent) => {
graphBindingsHandler(e)
},
() => {

View File

@ -1,11 +1,11 @@
<script setup lang="ts">
import { injectGraphNavigator } from '@/providers/graphNavigator.ts'
import { injectGraphSelection } from '@/providers/graphSelection.ts'
import { injectGraphNavigator } from '@/providers/graphNavigator'
import { injectGraphSelection } from '@/providers/graphSelection'
import { useGraphStore, type Edge } from '@/stores/graph'
import { assert } from '@/util/assert'
import { Rect } from '@/util/rect'
import { Rect } from '@/util/data/rect'
import { Vec2 } from '@/util/data/vec2'
import theme from '@/util/theme.json'
import { Vec2 } from '@/util/vec2'
import { clamp } from '@vueuse/core'
import { computed, ref } from 'vue'

View File

@ -1,11 +1,11 @@
<script setup lang="ts">
import GraphEdge from '@/components/GraphEditor/GraphEdge.vue'
import type { GraphNavigator } from '@/providers/graphNavigator.ts'
import { injectGraphSelection } from '@/providers/graphSelection.ts'
import type { GraphNavigator } from '@/providers/graphNavigator'
import { injectGraphSelection } from '@/providers/graphSelection'
import { injectInteractionHandler, type Interaction } from '@/providers/interactionHandler'
import { useGraphStore } from '@/stores/graph'
import { Vec2 } from '@/util/vec2.ts'
import type { ExprId } from 'shared/yjsModel.ts'
import { Vec2 } from '@/util/data/vec2'
import type { ExprId } from 'shared/yjsModel'
const graph = useGraphStore()
const selection = injectGraphSelection(true)

View File

@ -5,17 +5,17 @@ import GraphNodeError from '@/components/GraphEditor/GraphNodeError.vue'
import GraphVisualization from '@/components/GraphEditor/GraphVisualization.vue'
import NodeWidgetTree from '@/components/GraphEditor/NodeWidgetTree.vue'
import SvgIcon from '@/components/SvgIcon.vue'
import { useApproach } from '@/composables/animation'
import { useDoubleClick } from '@/composables/doubleClick'
import { usePointer, useResizeObserver } from '@/composables/events'
import { injectGraphNavigator } from '@/providers/graphNavigator'
import { injectGraphSelection } from '@/providers/graphSelection'
import { useGraphStore, type Node } from '@/stores/graph'
import { useProjectStore } from '@/stores/project'
import { useApproach } from '@/util/animation'
import { usePointer, useResizeObserver } from '@/util/events'
import type { Opt } from '@/util/data/opt'
import { Rect } from '@/util/data/rect'
import { Vec2 } from '@/util/data/vec2'
import { displayedIconOf } from '@/util/getIconName'
import type { Opt } from '@/util/opt'
import { Rect } from '@/util/rect'
import { Vec2 } from '@/util/vec2'
import { setIfUndefined } from 'lib0/map'
import type { ContentRange, ExprId, VisualizationIdentifier } from 'shared/yjsModel'
import { computed, ref, watch, watchEffect } from 'vue'
@ -214,20 +214,14 @@ function getRelatedSpanOffset(domNode: globalThis.Node, domOffset: number): numb
return domOffset
}
const handlePortClick = useDoubleClick<[portId: ExprId]>(
(_e, portId) => emit('outputPortClick', portId),
(portId) => {
emit('outputPortDoubleClick', portId)
},
const handlePortClick = useDoubleClick(
(portId: ExprId) => emit('outputPortClick', portId),
(portId: ExprId) => emit('outputPortDoubleClick', portId),
).handleClick
const handleNodeClick = useDoubleClick(
(e) => {
nodeEditHandler(e)
},
() => {
emit('doubleClick')
},
(e: MouseEvent) => nodeEditHandler(e),
() => emit('doubleClick'),
).handleClick
interface PortData {
clipRange: [number, number]
@ -339,7 +333,7 @@ function portGroupStyle(port: PortData) {
class="outputPortHoverArea"
@pointerenter="outputHovered = port.portId"
@pointerleave="outputHovered = undefined"
@pointerdown.stop.prevent="handlePortClick($event, port.portId)"
@pointerdown.stop.prevent="handlePortClick(port.portId)"
/>
<rect class="outputPort" />
</g>

View File

@ -7,7 +7,7 @@ import { injectGraphSelection } from '@/providers/graphSelection'
import type { UploadingFile as File, FileName } from '@/stores/awareness'
import { useGraphStore } from '@/stores/graph'
import { useProjectStore } from '@/stores/project'
import type { Vec2 } from '@/util/vec2'
import type { Vec2 } from '@/util/data/vec2'
import { stackItemsEqual } from 'shared/languageServerTypes'
import type { ContentRange, ExprId } from 'shared/yjsModel'
import { computed, toRaw } from 'vue'

View File

@ -11,13 +11,13 @@ import {
type VisualizationDataSource,
} from '@/stores/visualization'
import type { Visualization } from '@/stores/visualization/runtimeTypes'
import { toError } from '@/util/error'
import { toError } from '@/util/data/error'
import type { Opt } from '@/util/data/opt'
import { Rect } from '@/util/data/rect'
import type { Result } from '@/util/data/result'
import type { URLString } from '@/util/data/urlString'
import { Vec2 } from '@/util/data/vec2'
import type { Icon } from '@/util/iconName'
import type { Opt } from '@/util/opt'
import { Rect } from '@/util/rect'
import type { Result } from '@/util/result'
import type { URLString } from '@/util/urlString'
import { Vec2 } from '@/util/vec2'
import { computedAsync } from '@vueuse/core'
import type { VisualizationIdentifier } from 'shared/yjsModel'
import {

View File

@ -8,7 +8,7 @@ import {
usageKeyForInput,
} from '@/providers/widgetUsageInfo'
import { Ast } from '@/util/ast'
import type { Opt } from '@/util/opt'
import type { Opt } from '@/util/data/opt'
import { computed, proxyRefs } from 'vue'
const props = defineProps<{

View File

@ -1,11 +1,11 @@
<script setup lang="ts">
import NodeWidget from '@/components/GraphEditor/NodeWidget.vue'
import { useTransitioning } from '@/composables/animation'
import { ForcePort } from '@/providers/portInfo'
import { provideWidgetTree } from '@/providers/widgetTree'
import { useGraphStore } from '@/stores/graph'
import { useTransitioning } from '@/util/animation'
import { Ast } from '@/util/ast'
import { computed, toRef } from 'vue'
import NodeWidget from './NodeWidget.vue'
const props = defineProps<{ ast: Ast.Ast }>()
const graph = useGraphStore()

View File

@ -1,6 +1,6 @@
import { SnapGrid } from '@/components/GraphEditor/dragging'
import { Rect } from '@/util/rect'
import { Vec2 } from '@/util/vec2'
import { Rect } from '@/util/data/rect'
import { Vec2 } from '@/util/data/vec2'
import { expect, test } from 'vitest'
import { computed } from 'vue'

View File

@ -1,11 +1,11 @@
import { useApproach } from '@/composables/animation'
import { injectGraphSelection } from '@/providers/graphSelection'
import { useGraphStore } from '@/stores/graph'
import { useApproach } from '@/util/animation'
import { partitionPoint } from '@/util/array'
import type { Opt } from '@/util/opt'
import { Rect } from '@/util/rect'
import { partitionPoint } from '@/util/data/array'
import type { Opt } from '@/util/data/opt'
import { Rect } from '@/util/data/rect'
import { Vec2 } from '@/util/data/vec2'
import theme from '@/util/theme.json'
import { Vec2 } from '@/util/vec2'
import { iteratorFilter } from 'lib0/iterator'
import type { ExprId } from 'shared/yjsModel'
import { computed, markRaw, ref, watchEffect, type ComputedRef, type WatchStopHandle } from 'vue'

View File

@ -1,6 +1,6 @@
import { Awareness } from '@/stores/awareness'
import * as astText from '@/util/ast/text'
import { Vec2 } from '@/util/vec2'
import { Vec2 } from '@/util/data/vec2'
import { Keccak, sha3_224 as SHA3 } from '@noble/hashes/sha3'
import type { Hash } from '@noble/hashes/utils'
import { bytesToHex } from '@noble/hashes/utils'

View File

@ -7,7 +7,7 @@ import { useGraphStore } from '@/stores/graph'
import { useProjectStore, type NodeVisualizationConfiguration } from '@/stores/project'
import { Ast } from '@/util/ast'
import { ArgumentApplication } from '@/util/callTree'
import type { Opt } from '@/util/opt'
import type { Opt } from '@/util/data/opt'
import type { ExprId } from 'shared/yjsModel'
import { computed, proxyRefs } from 'vue'

View File

@ -1,5 +1,7 @@
<script setup lang="ts">
import NodeWidget from '@/components/GraphEditor/NodeWidget.vue'
import { useRaf } from '@/composables/animation'
import { useResizeObserver } from '@/composables/events'
import { injectGraphNavigator } from '@/providers/graphNavigator'
import { injectGraphSelection } from '@/providers/graphSelection'
import { ForcePort, injectPortInfo, providePortInfo } from '@/providers/portInfo'
@ -7,11 +9,9 @@ import type { WidgetInput } from '@/providers/widgetRegistry'
import { Score, defineWidget, widgetProps } from '@/providers/widgetRegistry'
import { injectWidgetTree } from '@/providers/widgetTree'
import { useGraphStore } from '@/stores/graph'
import { useRaf } from '@/util/animation'
import { Ast } from '@/util/ast'
import { ArgumentAst, ArgumentPlaceholder } from '@/util/callTree'
import { useResizeObserver } from '@/util/events'
import { Rect } from '@/util/rect'
import { Rect } from '@/util/data/rect'
import { uuidv4 } from 'lib0/random'
import type { ExprId } from 'shared/yjsModel'
import {

View File

@ -1,9 +1,10 @@
<script setup lang="ts">
import NodeWidget from '@/components/GraphEditor/NodeWidget.vue'
import DropdownWidget from '@/components/widgets/DropdownWidget.vue'
import { widgetProps } from '@/providers/widgetRegistry'
import { Score, defineWidget, widgetProps } from '@/providers/widgetRegistry'
import { useGraphStore } from '@/stores/graph'
import { qnJoin, qnSegments, tryQualifiedName } from '@/util/qualifiedName.ts'
import { ArgumentAst, ArgumentPlaceholder } from '@/util/callTree'
import { qnJoin, qnSegments, tryQualifiedName } from '@/util/qualifiedName'
import { computed, nextTick, onMounted, ref, watch } from 'vue'
const props = defineProps(widgetProps(widgetDefinition))
@ -89,9 +90,6 @@ watch(selectedIndex, (_index) => {
</script>
<script lang="ts">
import { defineWidget, Score } from '@/providers/widgetRegistry.ts'
import { ArgumentAst, ArgumentPlaceholder } from '@/util/callTree.ts'
export const widgetDefinition = defineWidget([ArgumentPlaceholder, ArgumentAst], {
priority: 999,
score: (props) => {

View File

@ -1,9 +1,9 @@
<script setup lang="ts">
import SelectionBrush from '@/components/SelectionBrush.vue'
import { useEvent } from '@/composables/events'
import { injectGraphNavigator } from '@/providers/graphNavigator'
import { injectGraphSelection } from '@/providers/graphSelection'
import { useEvent } from '@/util/events'
import { computed, ref } from 'vue'
import SelectionBrush from './SelectionBrush.vue'
const navigator = injectGraphNavigator(true)
const nodeSelection = injectGraphSelection(true)

View File

@ -1,6 +1,6 @@
<script setup lang="ts">
import { useApproach } from '@/util/animation'
import type { Vec2 } from '@/util/vec2'
import { useApproach } from '@/composables/animation'
import type { Vec2 } from '@/util/data/vec2'
import { computed, watch, type Ref } from 'vue'
const props = defineProps<{

View File

@ -5,8 +5,8 @@
* It displays one group defined in `@/assets/icons.svg` file, specified by `variant` property.
*/
import icons from '@/assets/icons.svg'
import type { URLString } from '@/util/data/urlString'
import type { Icon } from '@/util/iconName'
import type { URLString } from '@/util/urlString'
const props = defineProps<{ name: Icon | URLString; width?: number; height?: number }>()
</script>

View File

@ -53,4 +53,3 @@ const barStyle = computed(() => {
left: 9px;
}
</style>
@/providers/guiConfig

View File

@ -1,8 +1,8 @@
<script setup lang="ts">
import SvgIcon from '@/components/SvgIcon.vue'
import VisualizationSelector from '@/components/VisualizationSelector.vue'
import { PointerButtonMask, isTriggeredByKeyboard, usePointer } from '@/composables/events'
import { useVisualizationConfig } from '@/providers/visualizationConfig'
import { PointerButtonMask, isTriggeredByKeyboard, usePointer } from '@/util/events'
import { onMounted, ref, watchEffect } from 'vue'
const props = defineProps<{

View File

@ -1,8 +1,8 @@
<script setup lang="ts">
import SvgIcon from '@/components/SvgIcon.vue'
import { useAutoBlur } from '@/util/autoBlur'
import type { URLString } from '@/util/data/urlString'
import type { Icon } from '@/util/iconName'
import type { URLString } from '@/util/urlString'
import { computedAsync } from '@vueuse/core'
import { visIdentifierEquals, type VisualizationIdentifier } from 'shared/yjsModel'
import { onMounted, ref } from 'vue'

View File

@ -1,6 +1,6 @@
<script lang="ts">
import SvgIcon from '@/components/SvgIcon.vue'
import { useEvent } from '@/util/events'
import { useEvent } from '@/composables/events'
import { getTextWidth } from '@/util/measurement'
import { defineKeybinds } from '@/util/shortcuts'
import { VisualizationContainer, useVisualizationConfig } from '@/util/visualizationBuiltins'

View File

@ -1,7 +1,7 @@
<script lang="ts">
import SvgIcon from '@/components/SvgIcon.vue'
import { useEvent } from '@/composables/events'
import { useVisualizationConfig } from '@/providers/visualizationConfig'
import { useEvent } from '@/util/events'
import { getTextWidth } from '@/util/measurement'
import { VisualizationContainer, defineKeybinds } from '@/util/visualizationBuiltins'
import { computed, ref, watch, watchEffect, watchPostEffect } from 'vue'

View File

@ -1,10 +1,10 @@
<script lang="ts">
import SvgIcon from '@/components/SvgIcon.vue'
import { useRaf } from '@/composables/animation'
import { useEvent } from '@/composables/events'
import { useAppClass } from '@/providers/appClass'
import { useRaf } from '@/util/animation'
import { useEvent } from '@/util/events'
import { Range } from '@/util/range'
import { Vec2 } from '@/util/vec2'
import { Range } from '@/util/data/range'
import { Vec2 } from '@/util/data/vec2'
import { uuidv4 } from 'lib0/random'
import { nextTick } from 'process'
import { computed, ref, shallowReactive, watchEffect, watchPostEffect } from 'vue'

View File

@ -1,5 +1,5 @@
<script setup lang="ts">
import { PointerButtonMask, usePointer, useResizeObserver } from '@/util/events'
import { PointerButtonMask, usePointer, useResizeObserver } from '@/composables/events'
import { getTextWidth } from '@/util/measurement'
import { computed, ref, type StyleValue } from 'vue'

View File

@ -0,0 +1,3 @@
# Vue composables
This directory contains various Vue composables, mostly used to interact with the DOM.

View File

@ -1,6 +1,6 @@
import { useNavigator } from '@/util/navigator'
import { Rect } from '@/util/rect'
import { Vec2 } from '@/util/vec2'
import { useNavigator } from '@/composables/navigator'
import { Rect } from '@/util/data/rect'
import { Vec2 } from '@/util/data/vec2'
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
import { effectScope, ref } from 'vue'

View File

@ -1,3 +1,5 @@
/** @file Vue composables for running a callback on every frame, and smooth interpolation. */
import { watchSourceToRef } from '@/util/reactivity'
import { onScopeDispose, proxyRefs, ref, watch, type WatchSource } from 'vue'

View File

@ -1,15 +1,21 @@
/** @file A Vue composable that calls one of two given callbacks, depending on whether a click is
* a single click or a double click. */
/** Calls {@link onClick} if a click is a single click, or {@link onDoubleClick} if a click is
* a double click. For this function, a double click is defined as a second click that occurs within
* 200ms of the first click. The click count is reset to 0 upon double click, or after 200ms. */
export function useDoubleClick<Args extends any[]>(
onClick: (e: MouseEvent, ...args: Args) => void,
onClick: (...args: Args) => void,
onDoubleClick: (...args: Args) => void,
) {
const timeBetweenClicks = 200
let clickCount = 0
let singleClickTimer: ReturnType<typeof setTimeout>
const handleClick = (e: MouseEvent, ...args: Args) => {
const handleClick = (...args: Args) => {
clickCount++
if (clickCount === 1) {
onClick(e, ...args)
onClick(...args)
singleClickTimer = setTimeout(() => {
clickCount = 0
}, timeBetweenClicks)

View File

@ -1,5 +1,7 @@
import type { Opt } from '@/util/opt'
import { Vec2 } from '@/util/vec2'
/** @file Vue composables for listening to DOM events. */
import type { Opt } from '@/util/data/opt'
import { Vec2 } from '@/util/data/vec2'
import {
computed,
onScopeDispose,

View File

@ -1,7 +1,9 @@
import { useApproach } from '@/util/animation'
import { PointerButtonMask, useEvent, usePointer, useResizeObserver } from '@/util/events'
import { Rect } from '@/util/rect'
import { Vec2 } from '@/util/vec2'
/** @file A Vue composable for panning and zooming a DOM element. */
import { useApproach } from '@/composables/animation'
import { PointerButtonMask, useEvent, usePointer, useResizeObserver } from '@/composables/events'
import { Rect } from '@/util/data/rect'
import { Vec2 } from '@/util/data/vec2'
import { computed, proxyRefs, ref, type Ref } from 'vue'
function elemRect(target: Element | undefined): Rect {

View File

@ -1,10 +1,12 @@
/** @file A Vue composable for keeping track of selected DOM elements. */
import { selectionMouseBindings } from '@/bindings'
import { usePointer } from '@/util/events'
import type { NavigatorComposable } from '@/util/navigator'
import type { Rect } from '@/util/rect'
import type { Vec2 } from '@/util/vec2'
import { usePointer } from '@/composables/events'
import type { NavigatorComposable } from '@/composables/navigator'
import type { Rect } from '@/util/data/rect'
import type { Vec2 } from '@/util/data/vec2'
import { type ExprId } from 'shared/yjsModel'
import { computed, proxyRefs, reactive, ref, shallowRef } from 'vue'
import { type ExprId } from '../../shared/yjsModel.ts'
export type SelectionComposable<T> = ReturnType<typeof useSelection<T>>
export function useSelection<T>(

View File

@ -0,0 +1,11 @@
# Providers
This folder contains [Vue providers](provider).
All providers MUST be at the top level - so either `./providers/myProvider.ts`,
or `./providers/myProvider/index.ts`.
Each provider MAY have dependencies specific to itself, which MUST NOT be at the
top level - so `./providers/myProvider/myDependency.ts`.
[provider]: https://vuejs.org/guide/components/provide-inject

View File

@ -1,8 +1,3 @@
import { GraphDb } from '@/stores/graph/graphDatabase'
import { Ast } from '@/util/ast'
import { ApplicationKind, ArgumentPlaceholder } from '@/util/callTree'
import { describe, expect, test } from 'vitest'
import { defineComponent } from 'vue'
import {
Score,
WidgetRegistry,
@ -10,8 +5,13 @@ import {
type WidgetDefinition,
type WidgetInput,
type WidgetModule,
} from '../widgetRegistry'
import { DisplayMode, widgetConfigurationSchema } from '../widgetRegistry/configuration'
} from '@/providers/widgetRegistry'
import { DisplayMode, widgetConfigurationSchema } from '@/providers/widgetRegistry/configuration'
import { GraphDb } from '@/stores/graph/graphDatabase'
import { Ast } from '@/util/ast'
import { ApplicationKind, ArgumentPlaceholder } from '@/util/callTree'
import { describe, expect, test } from 'vitest'
import { defineComponent } from 'vue'
describe('WidgetRegistry', () => {
function makeMockWidget<T extends WidgetInput>(

View File

@ -1,6 +1,6 @@
import type { Opt } from '@/util/opt'
import { createContextStore } from '@/providers'
import type { Opt } from '@/util/data/opt'
import { reactive, watch, type WatchSource } from 'vue'
import { createContextStore } from '.'
export { provideFn as provideAppClassSet }
const { provideFn, injectFn: injectAppClassSet } = createContextStore('Port info', () => {

View File

@ -1,6 +1,6 @@
import { createContextStore } from '@/providers'
import { identity } from '@vueuse/core'
import type { ExprId } from 'shared/yjsModel'
import { createContextStore } from '.'
interface FunctionInfo {
callId: ExprId | undefined

View File

@ -1,5 +1,5 @@
import { useNavigator } from '@/util/navigator'
import { createContextStore } from '.'
import { useNavigator } from '@/composables/navigator'
import { createContextStore } from '@/providers'
export type GraphNavigator = ReturnType<typeof injectFn>
export { injectFn as injectGraphNavigator, provideFn as provideGraphNavigator }

View File

@ -1,8 +1,8 @@
import type { NavigatorComposable } from '@/util/navigator'
import type { Rect } from '@/util/rect'
import { useSelection } from '@/util/selection'
import type { NavigatorComposable } from '@/composables/navigator'
import { useSelection } from '@/composables/selection'
import { createContextStore } from '@/providers'
import type { Rect } from '@/util/data/rect'
import type { ExprId } from 'shared/yjsModel'
import { createContextStore } from '.'
const SELECTION_BRUSH_MARGIN_PX = 6

View File

@ -1,6 +1,6 @@
import { createContextStore } from '@/providers'
import { identity } from '@vueuse/core'
import { type Ref } from 'vue'
import { createContextStore } from '.'
export interface GuiConfig {
engine?: {

View File

@ -1,6 +1,6 @@
import type { GraphNavigator } from '@/providers/graphNavigator.ts'
import { createContextStore } from '@/providers'
import type { GraphNavigator } from '@/providers/graphNavigator'
import { watch, type WatchSource } from 'vue'
import { createContextStore } from '.'
export { injectFn as injectInteractionHandler, provideFn as provideInteractionHandler }
const { provideFn, injectFn } = createContextStore(

View File

@ -1,7 +1,7 @@
import { createContextStore } from '@/providers'
import { GetUsageKey } from '@/providers/widgetUsageInfo'
import { Ast } from '@/util/ast'
import { identity } from '@vueuse/core'
import { createContextStore } from '.'
import { GetUsageKey } from './widgetUsageInfo'
interface PortInfo {
portId: string

View File

@ -1,7 +1,7 @@
import { createContextStore } from '@/providers'
import type { URLString } from '@/util/data/urlString'
import { Vec2 } from '@/util/data/vec2'
import type { Icon } from '@/util/iconName'
import type { URLString } from '@/util/urlString'
import { Vec2 } from '@/util/vec2'
import type { VisualizationIdentifier } from 'shared/yjsModel'
import { reactive } from 'vue'

View File

@ -1,7 +1,7 @@
import { createContextStore } from '@/providers'
import { type WidgetConfiguration } from '@/providers/widgetRegistry/configuration'
import type { GraphDb } from '@/stores/graph/graphDatabase'
import { computed, shallowReactive, type Component, type PropType } from 'vue'
import { createContextStore } from '.'
export type WidgetComponent<T extends WidgetInput> = Component<WidgetProps<T>>

View File

@ -1,6 +1,6 @@
import { createContextStore } from '@/providers'
import { Ast } from '@/util/ast'
import { computed, proxyRefs, type Ref } from 'vue'
import { createContextStore } from '.'
export { injectFn as injectWidgetTree, provideFn as provideWidgetTree }
const { provideFn, injectFn } = createContextStore(

View File

@ -1,6 +1,6 @@
import { createContextStore } from '@/providers'
import type { WidgetComponent, WidgetInput } from '@/providers/widgetRegistry'
import { identity } from '@vueuse/core'
import { createContextStore } from '.'
import type { WidgetComponent, WidgetInput } from './widgetRegistry'
export { injectFn as injectWidgetUsageInfo, provideFn as provideWidgetUsageInfo }
const { provideFn, injectFn } = createContextStore('Widget usage info', identity<WidgetUsageInfo>)

View File

@ -0,0 +1,11 @@
# Stores
This folder contains [Pinia] stores.
All stores MUST be at the top level - so either `./stores/myStore.ts`, or
`./stores/myStore/index.ts`.
Each store MAY have dependencies specific to itself, which MUST NOT be at the
top level - so `./stores/myStore/myDependency.ts`.
[Pinia]: https://pinia.vuejs.org/

View File

@ -1,4 +1,4 @@
import { Vec2 } from '@/util/vec2'
import { Vec2 } from '@/util/data/vec2'
import type { StackItem } from 'shared/languageServerTypes'
import { reactive } from 'vue'
import { Awareness as YjsAwareness } from 'y-protocols/awareness'

View File

@ -1,8 +1,8 @@
import { GraphDb } from '@/stores/graph/graphDatabase'
import { Ast } from '@/util/ast'
import assert from 'assert'
import { IdMap, type ExprId } from 'shared/yjsModel'
import { expect, test } from 'vitest'
import { GraphDb } from '../graphDatabase'
test('Reading graph from definition', () => {
const code = 'function a =\n node1 = a + 4\n node2 = node1 + 4'

View File

@ -1,14 +1,14 @@
import { ComputedValueRegistry, type ExpressionInfo } from '@/stores/project/computedValueRegistry'
import { SuggestionDb, groupColorStyle, type Group } from '@/stores/suggestionDatabase'
import type { SuggestionEntry } from '@/stores/suggestionDatabase/entry'
import { arrayEquals, byteArraysEqual, tryGetIndex } from '@/util/array'
import { Ast, RawAst, RawAstExtended } from '@/util/ast'
import { AliasAnalyzer } from '@/util/ast/aliasAnalysis'
import { colorFromString } from '@/util/colors'
import { MappedKeyMap, MappedSet } from '@/util/containers'
import { arrayEquals, byteArraysEqual, tryGetIndex } from '@/util/data/array'
import type { Opt } from '@/util/data/opt'
import { Vec2 } from '@/util/data/vec2'
import { ReactiveDb, ReactiveIndex, ReactiveMapping } from '@/util/database/reactiveDb'
import type { Opt } from '@/util/opt'
import { Vec2 } from '@/util/vec2'
import * as set from 'lib0/set'
import { methodPointerEquals, type MethodCall } from 'shared/languageServerTypes'
import {

View File

@ -9,6 +9,7 @@ import {
type SuggestionEntry,
} from '@/stores/suggestionDatabase/entry'
import { Ast } from '@/util/ast'
import { unwrap } from '@/util/data/result'
import {
identifierUnchecked,
normalizeQualifiedName,
@ -19,7 +20,6 @@ import {
type Identifier,
type QualifiedName,
} from '@/util/qualifiedName'
import { unwrap } from '@/util/result'
// ========================
// === Imports analysis ===

View File

@ -11,9 +11,9 @@ import { useProjectStore } from '@/stores/project'
import { useSuggestionDbStore } from '@/stores/suggestionDatabase'
import { Ast } from '@/util/ast'
import { useObserveYjs } from '@/util/crdt'
import type { Opt } from '@/util/opt'
import { Rect } from '@/util/rect'
import { Vec2 } from '@/util/vec2'
import type { Opt } from '@/util/data/opt'
import { Rect } from '@/util/data/rect'
import { Vec2 } from '@/util/data/vec2'
import { defineStore } from 'pinia'
import type { StackItem } from 'shared/languageServerTypes'
import {

View File

@ -3,6 +3,9 @@ import { Awareness } from '@/stores/awareness'
import { ComputedValueRegistry } from '@/stores/project/computedValueRegistry'
import { VisualizationDataRegistry } from '@/stores/project/visualizationDataRegistry'
import { attachProvider, useObserveYjs } from '@/util/crdt'
import { nextEvent } from '@/util/data/observable'
import { isSome, type Opt } from '@/util/data/opt'
import { Err, Ok, type Result } from '@/util/data/result'
import { ReactiveMapping } from '@/util/database/reactiveDb'
import {
AsyncQueue,
@ -10,10 +13,7 @@ import {
createWebsocketClient,
rpcWithRetries as lsRpcWithRetries,
} from '@/util/net'
import { nextEvent } from '@/util/observable'
import { isSome, type Opt } from '@/util/opt'
import { tryQualifiedName } from '@/util/qualifiedName'
import { Err, Ok, type Result } from '@/util/result'
import { Client, RequestManager } from '@open-rpc/client-js'
import { computedAsync } from '@vueuse/core'
import * as array from 'lib0/array'

View File

@ -1,5 +1,5 @@
import type { ExecutionContext } from '@/stores/project'
import { Err, Ok, type Result } from '@/util/result'
import { Err, Ok, type Result } from '@/util/data/result'
import { OutboundPayload, VisualizationUpdate } from 'shared/binaryProtocol'
import type { DataServer } from 'shared/dataServer'
import type {

View File

@ -1,9 +1,9 @@
import { SuggestionDb, type Group } from '@/stores/suggestionDatabase'
import { SuggestionKind, entryQn, type SuggestionEntry } from '@/stores/suggestionDatabase/entry'
import { applyUpdates } from '@/stores/suggestionDatabase/lsUpdate'
import { unwrap } from '@/util/data/result'
import { parseDocs } from '@/util/docParser'
import { tryIdentifier, tryQualifiedName, type QualifiedName } from '@/util/qualifiedName'
import { unwrap } from '@/util/result'
import * as lsTypes from 'shared/languageServerTypes/suggestions'
import { expect, test } from 'vitest'

View File

@ -1,10 +1,10 @@
import type { Group } from '@/stores/suggestionDatabase'
import { findIndexOpt } from '@/util/array'
import { findIndexOpt } from '@/util/data/array'
import { isSome, type Opt } from '@/util/data/opt'
import { unwrap } from '@/util/data/result'
import { parseDocs, type Doc } from '@/util/docParser'
import type { Icon } from '@/util/iconName'
import { isSome, type Opt } from '@/util/opt'
import { tryQualifiedName, type QualifiedName } from '@/util/qualifiedName'
import { unwrap } from '@/util/result'
export interface DocumentationData {
documentation: Doc.Section[]

View File

@ -1,9 +1,9 @@
import { useProjectStore } from '@/stores/project'
import { entryQn, type SuggestionEntry, type SuggestionId } from '@/stores/suggestionDatabase/entry'
import { applyUpdates, entryFromLs } from '@/stores/suggestionDatabase/lsUpdate'
import { type Opt } from '@/util/data/opt'
import { ReactiveDb, ReactiveIndex } from '@/util/database/reactiveDb'
import { AsyncQueue, rpcWithRetries } from '@/util/net'
import { type Opt } from '@/util/opt'
import { qnJoin, qnParent, tryQualifiedName, type QualifiedName } from '@/util/qualifiedName'
import { defineStore } from 'pinia'
import { LanguageServer } from 'shared/languageServer'

View File

@ -11,9 +11,10 @@ import {
type Typename,
} from '@/stores/suggestionDatabase/entry'
import { assert, assertNever } from '@/util/assert'
import { type Opt } from '@/util/data/opt'
import { Err, Ok, withContext, type Result } from '@/util/data/result'
import type { Doc } from '@/util/docParser'
import type { Icon } from '@/util/iconName'
import { type Opt } from '@/util/opt'
import {
normalizeQualifiedName,
qnJoin,
@ -23,7 +24,6 @@ import {
type Identifier,
type QualifiedName,
} from '@/util/qualifiedName'
import { Err, Ok, withContext, type Result } from '@/util/result'
import * as lsTypes from 'shared/languageServerTypes/suggestions'
interface UnfinishedEntry {

View File

@ -17,8 +17,8 @@ import type {
import Compiler from '@/stores/visualization/compiler?worker'
import { VisualizationModule } from '@/stores/visualization/runtimeTypes'
import { assertNever } from '@/util/assert'
import { toError } from '@/util/error'
import type { Opt } from '@/util/opt'
import { toError } from '@/util/data/error'
import type { Opt } from '@/util/data/opt'
import { defineKeybinds } from '@/util/shortcuts'
import { VisualizationContainer, useVisualizationConfig } from '@/util/visualizationBuiltins'
import { Error as DataError } from 'shared/binaryProtocol'

View File

@ -21,8 +21,10 @@ import {
type VisualizationId,
} from '@/stores/visualization/metadata'
import type { VisualizationModule } from '@/stores/visualization/runtimeTypes'
import type { Opt } from '@/util/data/opt'
import { isUrlString } from '@/util/data/urlString'
import { isIconName } from '@/util/iconName'
import { rpcWithRetries } from '@/util/net'
import type { Opt } from '@/util/opt'
import { defineStore } from 'pinia'
import type { Event as LSEvent, VisualizationConfiguration } from 'shared/languageServerTypes'
import type { ExprId, VisualizationIdentifier } from 'shared/yjsModel'
@ -226,7 +228,8 @@ export const useVisualizationStore = defineStore('visualization', () => {
}
function icon(type: VisualizationIdentifier) {
return metadata.get(toVisualizationId(type))?.icon
const icon = metadata.get(toVisualizationId(type))?.icon
return icon && (isIconName(icon) || isUrlString(icon)) ? icon : undefined
}
function get(meta: VisualizationIdentifier, ignoreCache = false) {

View File

@ -1,6 +1,6 @@
import type { VisualizationModule } from '@/stores/visualization/runtimeTypes'
import type { Opt } from '@/util/data/opt'
import { ReactiveDb, ReactiveIndex } from '@/util/database/reactiveDb'
import type { Opt } from '@/util/opt'
import type { VisualizationIdentifier } from 'shared/yjsModel'
export interface VisualizationMetadata

View File

@ -1,6 +1,5 @@
import iconNames from '@/util/iconList.json'
import type { Icon } from '@/util/iconName'
import type { URLString } from '@/util/urlString'
import { isUrlString } from '@/util/data/urlString'
import { isIconName } from '@/util/iconName'
import type { DefineComponent, PropType } from 'vue'
import * as z from 'zod'
@ -42,8 +41,7 @@ export const VisualizationModule = z.object({
icon: z
.string()
.transform((s) => {
if (iconNames.includes(s)) return s as Icon
else if (s.includes(':')) return s as URLString
if (isIconName(s) || isUrlString(s)) return s
console.warn(`Invalid icon name '${s}'`)
return undefined
})

View File

@ -0,0 +1,4 @@
# Utilities
This folder contains code (mostly utility functions) that are not related to any
specific part of the UI.

View File

@ -1,4 +1,4 @@
import { partitionPoint } from '@/util/array'
import { partitionPoint } from '@/util/data/array'
import { fc, test as fcTest } from '@fast-check/vitest'
import { expect } from 'vitest'

View File

@ -1,4 +1,4 @@
import { MultiRange, Range } from '@/util/range'
import { MultiRange, Range } from '@/util/data/range'
import { expect, test } from 'vitest'
function r(...r: [start: number, end: number][]) {

View File

@ -1,6 +1,6 @@
import { LazySyncEffectSet } from '@/util/reactivity'
import { expect, test, vi } from 'vitest'
import { nextTick, reactive, ref } from 'vue'
import { LazySyncEffectSet } from '../reactivity'
test('LazySyncEffectSet', async () => {
const lazySet = new LazySyncEffectSet()

View File

@ -25,12 +25,11 @@
* All unannotated identifiers are assumed to preexist in the environment (captured from an external scope or imports).
*/
import type { ContentRange } from '@/../../../../shared/yjsModel'
import { assertDefined } from '@/util/assert'
import { AliasAnalyzer } from '@/util/ast/aliasAnalysis'
import { MappedKeyMap, MappedSet } from '@/util/containers'
import { IdMap, type ContentRange } from 'shared/yjsModel'
import { expect, test } from 'vitest'
import { IdMap } from '../../../../shared/yjsModel'
import { AliasAnalyzer } from '../aliasAnalysis'
/** The type of annotation. */
enum AnnotationType {
@ -57,7 +56,7 @@ function parseAnnotations(annotatedCode: string): {
unannotatedCode: string
annotations: MappedKeyMap<ContentRange, Annotation>
} {
const annotations = new MappedKeyMap(IdMap.keyForRange)
const annotations = new MappedKeyMap<ContentRange, Annotation>(IdMap.keyForRange)
// Iterate over all annotations (either bindings or usages).
// I.e. we want to cover both `«1,x»` and `»1,x«` cases, while keeping the track of the annotation type.
@ -69,7 +68,14 @@ function parseAnnotations(annotatedCode: string): {
const unannotatedCode = annotatedCode.replace(
annotationRegex,
(match, bindingPrefix, bindingName, usagePrefix, usageName, offset) => {
(
match,
bindingPrefix: string | undefined,
bindingName: string | undefined,
usagePrefix: string | undefined,
usageName: string | undefined,
offset: number,
) => {
console.log(`Processing annotated identifier ${match}.`)
// Sanity check: either both binding prefix and name are present, or both usage prefix and name are present.
@ -78,13 +84,13 @@ function parseAnnotations(annotatedCode: string): {
expect(usagePrefix != null).toBe(usageName != null)
expect(bindingPrefix != null).not.toBe(usagePrefix != null)
const id = parseInt(bindingPrefix ?? usagePrefix, 10)
const name = bindingName ?? usageName
const id = parseInt(bindingPrefix ?? usagePrefix ?? '0', 10)
const name = bindingName ?? usageName ?? ''
const kind = bindingPrefix != null ? AnnotationType.Binding : AnnotationType.Usage
const start = offset - accumulatedOffset
const end = start + name.length
const range = [start, end]
const range: ContentRange = [start, end]
const annotation = new Annotation(kind, id)
accumulatedOffset += match.length - name.length

View File

@ -1,6 +1,4 @@
import { Token, Tree } from '@/generated/ast'
import type { LazyObject } from '@/util/parserSupport'
import { assert, expect, test } from 'vitest'
import {
astContainingChar,
childrenAstNodes,
@ -11,7 +9,9 @@ import {
readAstSpan,
readTokenSpan,
walkRecursive,
} from '..'
} from '@/util/ast'
import type { LazyObject } from '@/util/parserSupport'
import { assert, expect, test } from 'vitest'
function validateSpans(obj: LazyObject, initialPos?: number): number {
const state = { pos: initialPos ?? 0 }

View File

@ -1,7 +1,7 @@
import { SuggestionKind, type SuggestionEntry } from '@/stores/suggestionDatabase/entry'
import { Ast } from '@/util/ast'
import { ArgumentApplication, ArgumentPlaceholder } from '@/util/callTree'
import { isSome } from '@/util/opt'
import { isSome } from '@/util/data/opt'
import { type Identifier, type QualifiedName } from '@/util/qualifiedName'
import type { MethodCall } from 'shared/languageServerTypes'
import { assert, expect, test } from 'vitest'

View File

@ -1,12 +1,11 @@
import * as RawAst from '@/generated/ast'
import { parseEnso } from '@/util/ast'
import { AstExtended as RawAstExtended } from '@/util/ast/extended'
import { Err, Ok, type Result } from '@/util/data/result'
import type { LazyObject } from '@/util/parserSupport'
import { Err, Ok, type Result } from '@/util/result'
import * as random from 'lib0/random'
import { IdMap, type ExprId } from 'shared/yjsModel'
import { reactive } from 'vue'
import type { ExprId } from '../../../shared/yjsModel'
import { IdMap } from '../../../shared/yjsModel'
interface Module {
get(id: AstId): Ast | null

View File

@ -7,11 +7,9 @@ import {
readAstOrTokenSpan,
readTokenSpan,
} from '@/util/ast'
import { MappedKeyMap, MappedSet, NonEmptyStack } from '@/util/containers'
import type { LazyObject } from '@/util/parserSupport.ts'
import type { ContentRange } from '../../../shared/yjsModel'
import { IdMap, rangeIsBefore } from '../../../shared/yjsModel'
import type { LazyObject } from '@/util/parserSupport'
import { IdMap, rangeIsBefore, type ContentRange } from 'shared/yjsModel'
const ACCESSOR_OPERATOR = '.'

View File

@ -1,11 +1,6 @@
import * as Ast from '@/generated/ast'
import { Token, Tree } from '@/generated/ast'
import { assert } from '@/util/assert'
import * as encoding from 'lib0/encoding'
import { digest } from 'lib0/hash/sha256'
import * as map from 'lib0/map'
import type { ContentRange, ExprId, IdMap } from 'shared/yjsModel'
import { markRaw } from 'vue'
import {
childrenAstNodesOrTokens,
debugAst,
@ -15,8 +10,13 @@ import {
visitGenerator,
visitRecursive,
walkRecursive,
} from '.'
import type { Opt } from '../opt'
} from '@/util/ast'
import type { Opt } from '@/util/data/opt'
import * as encoding from 'lib0/encoding'
import * as sha256 from 'lib0/hash/sha256'
import * as map from 'lib0/map'
import type { ContentRange, ExprId, IdMap } from 'shared/yjsModel'
import { markRaw } from 'vue'
type ExtractType<V, T> = T extends ReadonlyArray<infer Ts>
? Extract<V, { type: Ts }>
@ -218,7 +218,7 @@ class AstExtendedCtx<HasIdMap extends boolean> {
getHash(ast: AstExtended<Tree | Token, boolean>) {
const key = AstExtendedCtx.getHashKey(ast)
return map.setIfUndefined(this.contentHashes, key, () =>
digest(
sha256.digest(
encoding.encode((encoder) => {
const whitespace = ast.whitespaceLength()
encoding.writeUint32(encoder, whitespace)

View File

@ -1,12 +1,12 @@
import * as RawAst from '@/generated/ast'
import { assert } from '@/util/assert'
import * as Ast from '@/util/ast/abstract'
import { AstExtended as RawAstExtended } from '@/util/ast/extended'
import { isResult, mapOk } from '@/util/data/result'
import { parse } from '@/util/ffi'
import { LazyObject, LazySequence } from '@/util/parserSupport'
import { isResult, mapOk } from '@/util/result'
import * as map from 'lib0/map'
import type { ContentRange } from 'shared/yjsModel'
import { AstExtended as RawAstExtended } from './extended'
export { Ast, RawAst, RawAstExtended }

View File

@ -1,6 +1,6 @@
import { assert } from '@/util/assert'
import { RawAst, RawAstExtended } from '@/util/ast'
import { zip } from '@/util/iterable'
import { zip } from '@/util/data/iterable'
import { mapIterator } from 'lib0/iterator'
/** An operand of one of the applications inside `GeneralOprApp` */

View File

@ -1,4 +1,4 @@
import { useEvent } from '@/util/events'
import { useEvent } from '@/composables/events'
import type { Ref } from 'vue'
export function useAutoBlur(root: Ref<HTMLElement | SVGElement | MathMLElement | undefined>) {

View File

@ -1,8 +1,8 @@
import type { ForcePort } from '@/providers/portInfo'
import type { SuggestionEntry, SuggestionEntryArgument } from '@/stores/suggestionDatabase/entry'
import { Ast } from '@/util/ast'
import { tryGetIndex } from '@/util/data/array'
import type { MethodCall } from 'shared/languageServerTypes'
import { tryGetIndex } from './array'
import { Ast } from './ast'
export const enum ApplicationKind {
Prefix,

View File

@ -1,4 +1,4 @@
import { isNone, isSome, type Opt } from '@/util/opt'
import { isNone, isSome, type Opt } from '@/util/data/opt'
/**
* Compare two optional numbers. Returns a comparision result like specified for `sort`

View File

@ -1,5 +1,5 @@
import type { NonEmptyArray } from '@/util/array.ts'
import { assertDefined, assertEqual } from '@/util/assert'
import type { NonEmptyArray } from '@/util/data/array'
import { mapIterator } from 'lib0/iterator'
/**

View File

@ -1,4 +1,4 @@
import type { Opt } from '@/util/opt'
import type { Opt } from '@/util/data/opt'
import { watchEffect, type Ref } from 'vue'
import type { Awareness } from 'y-protocols/awareness'
import { WebsocketProvider } from 'y-websocket'

View File

@ -0,0 +1,7 @@
# Data structures
This folder contains:
- classes defining data structures
- branded types (newtypes) for specific data types
- utility functions for data structures

View File

@ -1,4 +1,6 @@
import type { Opt } from './opt'
/** @file Functions for querying and manipulating arrays. */
import type { Opt } from '@/util/data/opt'
/** An array that has at least one element present at all times. */
export type NonEmptyArray<T> = [T, ...T[]]

View File

@ -1,3 +1,5 @@
/** @file Functions for creating errors. */
/** Returns {@link Error}s as-is, wraps all other values in an {@link Error}. */
export function toError(error: unknown) {
return error instanceof Error ? error : new Error(String(error))

View File

@ -1,3 +1,5 @@
/** @file Functions for manipulating {@link Iterable}s. */
export function* empty(): Generator<never> {}
export function* range(start: number, stop: number, step = start <= stop ? 1 : -1) {

View File

@ -1,3 +1,5 @@
/** Functions for querying {@link ObservableV2}s. */
import type { ObservableV2 } from 'lib0/observable'
export type Events<O extends ObservableV2<any>> = O extends ObservableV2<infer E> ? E : never

View File

@ -0,0 +1,21 @@
/** @file A value that may be `null` or `undefined`. */
/** Optional value type. This is a replacement for `T | null | undefined` that is more
* convenient to use. We do not select a single value to represent "no value", because we are using
* libraries that disagree whether `null` (e.g. Yjs) or `undefined` (e.g. Vue) should be used for
* that purpose. We want to be compatible with both without needless conversions. In our own code,
* we should return `undefined` for "no value", since that is the default value for empty or no
* `return` expression. In order to test whether an `Opt<T>` is defined or not, use `x == null` or
* `isSome` function.
*
* Note: For JSON-serialized data, prefer explicit `null` over `undefined`, since `undefined` is
* not serializable. Alternatively, use optional field syntax (e.g. `{ x?: number }`). */
export type Opt<T> = T | null | undefined
export function isSome<T>(value: Opt<T>): value is T {
return value != null
}
export function isNone(value: Opt<any>): value is null | undefined {
return value == null
}

View File

@ -1,4 +1,6 @@
import { partitionPoint } from '@/util/array'
/** @file Ranges between two numbers, and sets of multiple ranges with gaps between. */
import { partitionPoint } from '@/util/data/array'
export interface RangeWithMatch {
readonly start: number

View File

@ -1,8 +1,8 @@
import { Vec2 } from '@/util/vec2'
/** @file Axis-aligned rectangle. Defined in terms of a top-left point and a size. */
/**
* Axis-aligned rectangle. Defined in terms of a top-left point and a size.
*/
import { Vec2 } from '@/util/data/vec2'
/** Axis-aligned rectangle. Defined in terms of a top-left point and a size. */
export class Rect {
constructor(
readonly pos: Vec2,

View File

@ -1,4 +1,7 @@
import { isSome, type Opt } from '@/util/opt'
/** @file A generic type that can either hold a value representing a successful result,
* or an error. */
import { isSome, type Opt } from '@/util/data/opt'
export type Result<T = undefined, E = string> =
| { ok: true; value: T }

Some files were not shown because too many files have changed in this diff Show More