Skip to content

Commit

Permalink
fixed references auto-comp to work with new reference syntax (#797)
Browse files Browse the repository at this point in the history
* fixed references auto-comp to work with new reference syntax
  • Loading branch information
roironn authored Mar 15, 2020
1 parent 013151f commit 2dcdebd
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 94 deletions.
133 changes: 76 additions & 57 deletions packages/vscode/src/salto/completions/suggestions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ import _ from 'lodash'
import { TypeElement, Field, isObjectType, isInstanceElement, isPrimitiveType,
isField, PrimitiveTypes, BuiltinTypes, isType, Value, getField,
getFieldNames, getFieldType, getAnnotationKey, ElemID, Element,
Values, CORE_ANNOTATIONS } from '@salto-io/adapter-api'
CORE_ANNOTATIONS } from '@salto-io/adapter-api'
import { dumpElemID, parseElemID } from '@salto-io/core'
import { resolvePath } from '@salto-io/adapter-utils'
import { ContextReference } from '../context'

interface InsertText {
Expand Down Expand Up @@ -68,41 +69,55 @@ const getAdapterNames = (
elements: ReadonlyArray<Element>
): string[] => _(elements).map(e => e.elemID.adapter).uniq().value()

const getValuesSuggestions = (values: Values, path: string[]): string[] => {
const relevantPath = path.slice(0, path.length - 1)
const target = _.isEmpty(relevantPath) ? values : _.get(values, relevantPath)
return _.isPlainObject(target) ? _.keys(target) : []
const refNameSuggestions = (
elements: readonly Element[],
refElemID: ElemID,
): Suggestions => {
const baseID = new ElemID(refElemID.adapter, refElemID.typeName)
const baseElement = elements.find(e => e.elemID.getFullName() === baseID.getFullName())
if (!baseElement) return []

switch (refElemID.idType) {
case 'annotation':
return _.keys(baseElement.annotationTypes)
case 'attr':
return _.keys(baseElement.annotations)
case 'field':
return isObjectType(baseElement) ? _.keys(baseElement.fields) : []
case 'instance':
return getAllInstances(elements, baseID.adapter, baseID.getFullName())
default:
return []
}
}

const getElementReferenceSuggestions = (
const refValueSuggestions = (
elements: readonly Element[],
matchElement: Element,
path: string[]
): string[] => {
if (isInstanceElement(matchElement)) {
return getValuesSuggestions(matchElement.value, path)
refElemID: ElemID,
): Suggestions => {
const { parent } = refElemID.createTopLevelParentID()
const parentElement = elements.find(
e => e.elemID.getFullName() === parent.getFullName()
)
if (_.isUndefined(parentElement)) return []
const refValue = resolvePath(parentElement, refElemID)
if (isField(refValue)) {
return _.keys(refValue.annotations)
}
if (isObjectType(matchElement) && path.length > 0) {
const [pathType, ...restOfPath] = path
if (pathType === 'instance' && _.isEmpty(restOfPath)) {
return getAllInstances(
elements,
matchElement.elemID.adapter,
matchElement.elemID.getFullName()
)
}
if (pathType === 'field' && restOfPath.length === 1) {
return _.keys(matchElement.fields)
}
if (pathType === 'field') {
const field = matchElement.fields[restOfPath[0]]
return field ? getValuesSuggestions(field.annotations, restOfPath.slice(1)) : []
}
if (pathType === 'attr') {
return getValuesSuggestions(matchElement.annotations, restOfPath)
}
if (isInstanceElement(refValue)) {
return [
..._.keys(refValue.value),
..._.keys(refValue.annotations),
]
}
if (_.isPlainObject(refValue)) {
return _.keys(refValue)
}
if (_.isArray(refValue)) {
return _.range(0, refValue.length).map(_.toString)
}
return ['instance', 'attr', 'field']

return []
}

const referenceSuggestions = (
Expand All @@ -113,39 +128,42 @@ const referenceSuggestions = (
const unquotedMatch = valueToken.match(/".*\$\{\s*([^}]*$)/)
if (!unquotedMatch && (valueToken.includes('"') || valueToken.includes("'"))) return []
const match = unquotedMatch ? unquotedMatch[1] : valueToken
const [adapter, ...elemIDParts] = match.split(ElemID.NAMESPACE_SEPARATOR)
// We still didn't define the type -> we are still defining the adapter
if (_.isEmpty(elemIDParts)) {
return getAdapterNames(elements || [])
}

if (elemIDParts.length === 1) {
return getAllTypes(elements || [], adapter)
.map(n => n.substring(adapter.length + 1))
}
const refParts = match.split(ElemID.NAMESPACE_SEPARATOR)
.slice(0, -1)
const refPartIndex = refParts.length

// try & catch here as we must consider the possibility of an illegal elemID here.
try {
const refElemID = ElemID.fromFullName(refParts.join(ElemID.NAMESPACE_SEPARATOR))

const refPartsResolvers = [
() => getAdapterNames(elements),
() => getAllTypes(elements || [], refElemID.adapter)
.map(n => n.substring(refElemID.adapter.length + 1)),
() => ['instance', 'attr', 'field'],
() => refNameSuggestions(elements, refElemID),
() => refValueSuggestions(elements, refElemID),
]

const elemID = new ElemID(adapter, ...elemIDParts)
const matchElement = _(elements)
.filter(e => elemID.getFullName().indexOf(e.elemID.getFullName()) === 0)
.sortBy(e => e.elemID.getFullName().length)
.last()
if (matchElement) {
const path = elemID.getFullName()
.slice(matchElement.elemID.getFullName().length + 1)
.split(ElemID.NAMESPACE_SEPARATOR)
return getElementReferenceSuggestions(elements, matchElement, path)
return refPartIndex >= refPartsResolvers.length
? refPartsResolvers[refPartsResolvers.length - 1]()
: refPartsResolvers[refPartIndex]()
} catch (e) {
return []
}

return []
}

export const valueSuggestions = (
attrName: string,
annotatingElem: TypeElement|Field,
valueType: TypeElement
valueType: TypeElement,
valueToken: string
): Suggestions => {
// If the annotating element is a list and we are not in a list content
// we need to created one

if (!_.isEmpty(valueToken)) return []

if (isField(annotatingElem) && annotatingElem.isList && attrName) {
return [{ label: '[]', insertText: '[$0]' }]
}
Expand All @@ -155,6 +173,7 @@ export const valueSuggestions = (
if (restrictionValues) {
return restrictionValues.map(v => JSON.stringify(v))
}

if (isObjectType(valueType)) {
return [{ label: '{}', insertText: '{$0}' }]
}
Expand Down Expand Up @@ -189,7 +208,7 @@ export const fieldValueSuggestions = (params: SuggestionsParams): Suggestions =>
const valueToken = _.last(params.tokens) || ''
return (valueField)
? [
...valueSuggestions(attrName, valueField, valueField.type),
...valueSuggestions(attrName, valueField, valueField.type, valueToken),
...referenceSuggestions(params.elements, valueToken),
]
: referenceSuggestions(params.elements, valueToken)
Expand Down Expand Up @@ -226,14 +245,14 @@ export const annoValueSuggestions = (params: SuggestionsParams): Suggestions =>
const attrField = getField(annoType, refPath.split(ElemID.NAMESPACE_SEPARATOR))
return (attrField)
? [
...valueSuggestions(annoName, attrField, attrField.type),
...valueSuggestions(annoName, attrField, attrField.type, valueToken),
...referenceSuggestions(params.elements, valueToken),
]
: referenceSuggestions(params.elements, valueToken)
}
return (annoType)
? [
...valueSuggestions(annoName, annoType, annoType),
...valueSuggestions(annoName, annoType, annoType, valueToken),
...referenceSuggestions(params.elements, valueToken),
]
: referenceSuggestions(params.elements, valueToken)
Expand Down
Loading

0 comments on commit 2dcdebd

Please sign in to comment.