Skip to content

Commit

Permalink
fix: support cc for absolute path in meta path
Browse files Browse the repository at this point in the history
  • Loading branch information
marufrasully committed Apr 25, 2024
1 parent 89419f3 commit d4a33dd
Show file tree
Hide file tree
Showing 11 changed files with 1,127 additions and 621 deletions.
236 changes: 75 additions & 161 deletions packages/fe/src/services/completion/providers/context-path.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,17 @@
import { getUI5PropertyByXMLAttributeKey } from "@ui5-language-assistant/logic-utils";
import {
getPathConstraintsForControl,
getNextPossibleContextPathTargets,
getRootElements,
resolvePathTarget,
} from "../../../utils";
import { getPathConstraintsForControl } from "../../../utils";
import {
AnnotationTargetInXMLAttributeValueCompletion,
SAP_FE_MACROS,
} from "../../../types";

import { UI5AttributeValueCompletionOptions } from "./index";
import { CompletionSuggestion } from "../../../types/completion";
import {
EntityContainer,
EntitySet,
EntityType,
NavigationProperty,
Singleton,
} from "@sap-ux/vocabularies-types";
import { getAffectedRange } from "../utils";
import { AnnotationTargetInXMLAttributeValueTypeName } from "../../../types/completion";

type ApplicableMetadataElement =
| EntityContainer
| EntitySet
| EntityType
| Singleton
| NavigationProperty;

interface CompletionSuggestion {
element: ApplicableMetadataElement;
isLastSegment: boolean;
}
getNavigationSuggestion,
getRootElementSuggestions,
suggestionToTargetCompletion,
} from "./utils";

/**
* Suggests values for macros contextPath
Expand All @@ -46,150 +26,84 @@ export function contextPathSuggestions({
attribute,
context.ui5Model
);
if (!ui5Property) {
return [];
}
const metaPathStartsWithAbsolutePath = element.attributes.find(
(i) => i.key === "metaPath" && i.value?.startsWith("/")
);
// no CC for contextPath if metaPath starts with absolute path
if (metaPathStartsWithAbsolutePath) {
return [];
}

if (
ui5Property?.library === SAP_FE_MACROS &&
ui5Property.parent?.name === "Chart" &&
ui5Property.name === "contextPath"
!["contextPath"].includes(ui5Property.name) ||
ui5Property?.library !== SAP_FE_MACROS ||
ui5Property.parent?.name !== "Chart"
) {
const mainServicePath = context.manifestDetails.mainServicePath;
const service = mainServicePath
? context.services[mainServicePath]
: undefined;
if (!service) {
return [];
}
const metadata = service.convertedMetadata;
const { expectedAnnotations, expectedTypes } = getPathConstraintsForControl(
element.name,
ui5Property
);
const isPropertyPath = expectedTypes.includes("Property");
const suggestions: CompletionSuggestion[] = [];
const segments = (attribute.value || "").split("/");
const precedingSegments = (prefix || "").split("/");
const completionSegmentIndex = precedingSegments.length - 1;
precedingSegments.pop();
const completionSegmentOffset =
precedingSegments.join("/").length + (precedingSegments.length ? 1 : 0);
const isAbsolutePath = segments.length > 1 && !segments[0];
if (!isAbsolutePath && completionSegmentIndex > 0) {
// relative paths are not supported
return [];
}
if (expectedAnnotations.length + expectedTypes.length === 0) {
return [];
}
return [];
}

const isNextSegmentPossible = (
currentTarget: EntitySet | EntityType | Singleton | EntityContainer,
milestones: string[] = []
): boolean => {
return (
getNextPossibleContextPathTargets(
service.convertedMetadata,
currentTarget,
{
allowedTerms: expectedAnnotations,
allowedTargets: expectedTypes,
isPropertyPath,
},
[...milestones, currentTarget.fullyQualifiedName]
).length > 0
);
};
const mainServicePath = context.manifestDetails.mainServicePath;
const service = mainServicePath
? context.services[mainServicePath]
: undefined;
if (!service) {
return [];
}
const metadata = service.convertedMetadata;
const { expectedAnnotations, expectedTypes } = getPathConstraintsForControl(
element.name,
ui5Property
);
if (expectedAnnotations.length + expectedTypes.length === 0) {
return [];
}
const isPropertyPath = expectedTypes.includes("Property");
const suggestions: CompletionSuggestion[] = [];
const segments = (attribute.value || "").split("/");
const precedingSegments = (prefix || "").split("/");
const completionSegmentIndex = precedingSegments.length - 1;
precedingSegments.pop();
const completionSegmentOffset =
precedingSegments.join("/").length + (precedingSegments.length ? 1 : 0);
const isAbsolutePath = segments.length > 1 && !segments[0];
if (!isAbsolutePath && completionSegmentIndex > 0) {
// relative paths are not supported
return [];
}

if (completionSegmentIndex < 2) {
// completion for root element
const roots = getRootElements(
if (completionSegmentIndex < 2) {
// completion for root element
suggestions.push(
...getRootElementSuggestions(
metadata,
expectedAnnotations,
expectedTypes,
isPropertyPath
);
suggestions.push(
...roots.map((root) => ({
element: root,
isLastSegment: !isNextSegmentPossible(root),
}))
);
} else {
// completion for navigation property segment
const precedingPath = segments.slice(0, completionSegmentIndex).join("/");
const { target, isCollection, milestones } = resolvePathTarget(
service.convertedMetadata,
precedingPath
);
if (!target) {
// target not resolved or path leads to collection - no further segments possible
return [];
} else if (target._type === "Property") {
// no further segments possible after entity property, container is not supported
return [];
} else {
const possibleTargets = getNextPossibleContextPathTargets(
service.convertedMetadata,
target,
{
allowedTerms: expectedAnnotations,
allowedTargets: expectedTypes,
isPropertyPath,
isCollection: isCollection ? false : undefined,
},
milestones
);
suggestions.push(
...possibleTargets.map((t) => {
const entityType =
t._type === "NavigationProperty" ? t.targetType : t.entityType;
return {
element: t,
isLastSegment: !isNextSegmentPossible(entityType, milestones),
};
})
);
}
}

const sortMap: Record<string, string> = {
EntityContainer: "Z",
EntityType: "A",
EntitySet: "B",
Singleton: "C",
NavigationProperty: "N",
};

const getSuggestionText = (suggestion: CompletionSuggestion): string => {
const isFullyQualifiedName = [
"EntityContainer",
"EntitySet",
"Singleton",
].includes(suggestion.element._type);
return `${completionSegmentIndex === 0 ? "/" : ""}${
isFullyQualifiedName && completionSegmentIndex < 2
? suggestion.element.fullyQualifiedName
: suggestion.element.name
}`;
)
);
} else {
// completion for navigation property segment
const precedingPath = segments.slice(0, completionSegmentIndex).join("/");
const options = {
allowedTerms: expectedAnnotations,
allowedTargets: expectedTypes,
isPropertyPath,
};

return suggestions.map((suggestion) => {
const text = getSuggestionText(suggestion);
return {
type: AnnotationTargetInXMLAttributeValueTypeName,
node: {
kind: suggestion.element._type,
name: text,
text,
affectedRange: getAffectedRange(
attribute.syntax.value,
completionSegmentOffset
),
commitCharacters: suggestion.isLastSegment ? [] : ["/"],
commitCharactersRequired: true,
sortText: sortMap[suggestion.element._type] + text,
},
};
});
suggestions.push(
...getNavigationSuggestion(
service.convertedMetadata,
precedingPath,
options
)
);
}
return [];
return suggestionToTargetCompletion(
attribute,
suggestions,
completionSegmentIndex,
completionSegmentOffset
);
}

0 comments on commit d4a33dd

Please sign in to comment.