Skip to content

Commit

Permalink
Imports in CB (#8315)
Browse files Browse the repository at this point in the history
  • Loading branch information
vitvakatu authored Nov 24, 2023
1 parent b33285c commit 8516ed5
Show file tree
Hide file tree
Showing 15 changed files with 1,071 additions and 158 deletions.
11 changes: 10 additions & 1 deletion app/gui2/shared/yjsModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,22 @@ export class DistributedModule {
this.undoManager = new Y.UndoManager([this.doc.contents, this.doc.idMap, this.doc.metadata])
}

insertNewNode(offset: number, pattern: string, expression: string, meta: NodeMetadata): ExprId {
insertNewNode(
offset: number,
pattern: string,
expression: string,
meta: NodeMetadata,
withImport?: { str: string; offset: number },
): ExprId {
// Spaces at the beginning are needed to place the new node in scope of the `main` function with proper indentation.
const lhs = ` ${pattern} = `
const content = lhs + expression
const range = [offset + lhs.length, offset + content.length] as const
const newId = random.uuidv4() as ExprId
this.transact(() => {
if (withImport) {
this.doc.contents.insert(withImport.offset, withImport.str + '\n')
}
this.doc.contents.insert(offset, content + '\n')
const start = Y.createRelativePositionFromTypeIndex(this.doc.contents, range[0], -1)
const end = Y.createRelativePositionFromTypeIndex(this.doc.contents, range[1])
Expand Down
5 changes: 3 additions & 2 deletions app/gui2/src/components/ComponentBrowser.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { default as DocumentationPanel } from '@/components/DocumentationPanel.v
import SvgIcon from '@/components/SvgIcon.vue'
import ToggleIcon from '@/components/ToggleIcon.vue'
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'
Expand Down Expand Up @@ -37,7 +38,7 @@ const props = defineProps<{
}>()
const emit = defineEmits<{
accepted: [searcherExpression: string]
accepted: [searcherExpression: string, requiredImports: RequiredImport[]]
closed: [searcherExpression: string]
canceled: []
}>()
Expand Down Expand Up @@ -303,7 +304,7 @@ function acceptSuggestion(index: Opt<Component> = null) {
}
function acceptInput() {
emit('accepted', input.code.value)
emit('accepted', input.code.value, input.importsToAdd())
}
// === Key Events Handler ===
Expand Down
106 changes: 98 additions & 8 deletions app/gui2/src/components/ComponentBrowser/__tests__/input.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import { GraphDb } from '@/stores/graph/graphDatabase'
import type { RequiredImport } from '@/stores/graph/imports'
import { SuggestionDb } from '@/stores/suggestionDatabase'
import {
makeCon,
makeFunction,
makeLocal,
makeMethod,
makeModule,
makeModuleMethod,
makeStaticMethod,
makeType,
type SuggestionEntry,
} from '@/stores/suggestionDatabase/entry'
import { readAstSpan } from '@/util/ast'
import { ComputedValueRegistry } from '@/util/computedValueRegistry'
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'
Expand Down Expand Up @@ -105,7 +110,7 @@ test.each([
mockGraphDb.mockNode('operator1', operator1Id)
mockGraphDb.mockNode('operator2', operator2Id)

const input = useComponentBrowserInput(mockGraphDb)
const input = useComponentBrowserInput(mockGraphDb, new SuggestionDb())
input.code.value = code
input.selection.value = { start: cursorPos, end: cursorPos }
const context = input.context.value
Expand All @@ -115,17 +120,17 @@ test.each([
case 'insert':
expect(context.position).toStrictEqual(expContext.position)
expect(
context.oprApp != null ? Array.from(context.oprApp.componentsReprs(code)) : undefined,
context.oprApp != null ? Array.from(context.oprApp.componentsReprs()) : undefined,
).toStrictEqual(expContext.oprApp)
break
case 'changeIdentifier':
expect(readAstSpan(context.identifier, code)).toStrictEqual(expContext.identifier)
expect(context.identifier.repr()).toStrictEqual(expContext.identifier)
expect(
context.oprApp != null ? Array.from(context.oprApp.componentsReprs(code)) : undefined,
context.oprApp != null ? Array.from(context.oprApp.componentsReprs()) : undefined,
).toStrictEqual(expContext.oprApp)
break
case 'changeLiteral':
expect(readAstSpan(context.literal, code)).toStrictEqual(expContext.literal)
expect(context.literal.repr()).toStrictEqual(expContext.literal)
}
expect(filter.pattern).toStrictEqual(expFiltering.pattern)
expect(filter.qualifiedNamePattern).toStrictEqual(expFiltering.qualifiedNamePattern)
Expand All @@ -147,6 +152,11 @@ const baseCases: ApplySuggestionCase[] = [
suggestion: makeLocal('local.Project.Main', 'operator1'),
expected: 'operator1 ',
},
{
code: 'Main.',
suggestion: makeFunction('local.Project.Main', 'func1'),
expected: 'Main.func1 ',
},
{
code: '',
suggestion: makeMethod('Standard.Base.Data.Vector.get'),
Expand All @@ -172,6 +182,21 @@ const baseCases: ApplySuggestionCase[] = [
suggestion: makeMethod('Standard.Base.Data.Vector.get'),
expected: 'operator1.get ',
},
{
code: 'Standard.Base.Data.',
suggestion: makeStaticMethod('Standard.Base.Data.Vector.new'),
expected: 'Standard.Base.Data.Vector.new ',
},
{
code: 'Base.',
suggestion: makeStaticMethod('Standard.Base.Data.Vector.new'),
expected: 'Base.Data.Vector.new ',
},
{
code: 'Base.Data.',
suggestion: makeStaticMethod('Standard.Base.Data.Vector.new'),
expected: 'Base.Data.Vector.new ',
},
{
code: 'Data.Vector.',
suggestion: makeStaticMethod('Standard.Base.Data.Vector.new'),
Expand Down Expand Up @@ -252,8 +277,8 @@ test.each([
({ code, cursorPos, suggestion, expected, expectedCursorPos }) => {
cursorPos = cursorPos ?? code.length
expectedCursorPos = expectedCursorPos ?? expected.length

const input = useComponentBrowserInput(GraphDb.Mock())
const graphMock = GraphDb.Mock()
const input = useComponentBrowserInput(graphMock, new SuggestionDb())
input.code.value = code
input.selection.value = { start: cursorPos, end: cursorPos }
input.applySuggestion(suggestion)
Expand All @@ -264,3 +289,68 @@ test.each([
})
},
)

interface ImportsCase {
description: string
suggestionId: number
initialCode?: string
manuallyEditedCode?: string
expectedCode: string
expectedImports: RequiredImport[]
}

test.each([
{
description: 'Basic case of adding required import',
suggestionId: 3,
expectedCode: 'Table.new ',
expectedImports: [
{
kind: 'Unqualified',
from: unwrap(tryQualifiedName('Standard.Base')),
import: unwrap(tryIdentifier('Table')),
},
],
},
{
description: 'Importing the head of partially edited qualified name',
suggestionId: 3,
initialCode: 'Base.',
expectedCode: 'Base.Table.new ',
expectedImports: [{ kind: 'Qualified', module: unwrap(tryQualifiedName('Standard.Base')) }],
},
{
description: 'Importing the head of partially edited qualified name (2)',
suggestionId: 3,
initialCode: 'Base.Table.',
expectedCode: 'Base.Table.new ',
expectedImports: [{ kind: 'Qualified', module: unwrap(tryQualifiedName('Standard.Base')) }],
},
{
description: 'Do not import if user changes input manually after applying suggestion',
suggestionId: 3,
manuallyEditedCode: '',
expectedCode: '',
expectedImports: [],
},
] as ImportsCase[])(
'$description',
({ suggestionId, initialCode, manuallyEditedCode, expectedCode, expectedImports }) => {
initialCode = initialCode ?? ''
const db = new SuggestionDb()
db.set(1, makeModule('Standard.Base'))
db.set(2, makeType('Standard.Base.Table'))
db.set(3, makeCon('Standard.Base.Table.new'))
const graphMock = GraphDb.Mock(undefined, db)
const input = useComponentBrowserInput(graphMock, db)
input.code.value = initialCode
input.selection.value = { start: initialCode.length, end: initialCode.length }
const suggestion = db.get(suggestionId)!
input.applySuggestion(suggestion)
if (manuallyEditedCode != null) {
input.code.value = manuallyEditedCode
}
expect(input.code.value).toEqual(expectedCode)
expect(input.importsToAdd()).toEqual(expectedImports)
},
)
Loading

0 comments on commit 8516ed5

Please sign in to comment.