Skip to content

Commit

Permalink
feat: hover support for proeprty binding (#645)
Browse files Browse the repository at this point in the history
* feat: hover support for proeprty binding

* fix: add seprator in doc and remove visibility

* fix: tooltip title
  • Loading branch information
marufrasully authored Aug 9, 2023
1 parent 746c257 commit de2cccc
Show file tree
Hide file tree
Showing 12 changed files with 476 additions and 98 deletions.
8 changes: 8 additions & 0 deletions .changeset/stupid-zoos-mate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@ui5-language-assistant/vscode-ui5-language-assistant-bas-ext": patch
"vscode-ui5-language-assistant": patch
"@ui5-language-assistant/language-server": patch
"@ui5-language-assistant/binding": patch
---

add hover support for proeprty binding
1 change: 1 addition & 0 deletions packages/binding/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { BINDING_ISSUE_TYPE } from "./types";

export { getCompletionItems } from "./services/completion";
export { bindingValidators } from "./services/diagnostics";
export { getHover } from "./services/hover";
export type { BindingIssue } from "./types";

export function isBindingIssue<T extends { issueType: string }>(
Expand Down
2 changes: 1 addition & 1 deletion packages/binding/src/definition/definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
} from "../types";
import { ui5NodeToFQN } from "@ui5-language-assistant/logic-utils";
import { forOwn } from "lodash";
import { getDocumentation } from "../services/completion/providers/documentation";
import { getDocumentation } from "../utils";
import { fallBackElements } from "./fall-back-definition";

const isBindingInfoName = (name: string): name is BindingInfoName => {
Expand Down
124 changes: 124 additions & 0 deletions packages/binding/src/services/hover/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { XMLAttribute } from "@xml-tools/ast";
import { getUI5PropertyByXMLAttributeKey } from "@ui5-language-assistant/logic-utils";
import { getLogger } from "../../utils";
import { Hover } from "vscode-languageserver";
import {
parseBinding,
isBindingAllowed,
isBindingExpression,
extractBindingSyntax,
rangeContained,
BindingParserTypes,
} from "@ui5-language-assistant/binding-parser";
import { Position } from "vscode-languageserver-types";
import { BindContext } from "../../types";
import { PROPERTY_BINDING_INFO } from "../../constant";
import { getDocumentation } from "../../utils";
import { UI5Typedef } from "@ui5-language-assistant/semantic-model-types";

const getHoverFromBinding = (
context: BindContext,
propertyBinding: UI5Typedef,
binding: BindingParserTypes.StructureValue
): Hover | undefined => {
let hover: Hover | undefined;
const cursorPos = context.textDocumentPosition?.position;
for (const element of binding.elements) {
if (
cursorPos &&
element.range &&
rangeContained(element.range, {
start: cursorPos,
end: cursorPos,
})
) {
// check if cursor is on key range
if (
cursorPos &&
element.key &&
rangeContained(element.key.range, {
start: cursorPos,
end: cursorPos,
})
) {
// check valid key
const property = propertyBinding.properties.find(
(prop) => prop.name === element.key?.originalText
);
if (property) {
return { contents: getDocumentation(context, property, true) };
}
}

// check collection value as they may have property binding
if (element.value?.type === "collection-value") {
for (const collectionEl of element.value.elements) {
if (collectionEl.type !== "structure-value") {
continue;
}
hover = getHoverFromBinding(context, propertyBinding, collectionEl);
if (hover) {
return hover;
}
}
}
}
}
return hover;
};

export const getHover = (
context: BindContext,
attribute: XMLAttribute
): Hover | undefined => {
try {
const propBinding = context.ui5Model.typedefs[PROPERTY_BINDING_INFO];
if (!propBinding) {
return;
}
const ui5Property = getUI5PropertyByXMLAttributeKey(
attribute,
context.ui5Model
);
if (!ui5Property) {
return;
}
const value = attribute.syntax.value;
const text = attribute.value ?? "";
if (text.trim().length === 0) {
return;
}

const extractedText = extractBindingSyntax(text);
for (const bindingSyntax of extractedText) {
const { expression, startIndex } = bindingSyntax;
if (isBindingExpression(expression)) {
continue;
}
/* istanbul ignore next */
const position: Position = {
character: (value?.startColumn ?? 0) + startIndex,
line: value?.startLine ? value.startLine - 1 : 0, // zero based index
};
const { ast, errors } = parseBinding(expression, position);
const cursorPos = context.textDocumentPosition?.position;
const binding = ast.bindings.find(
(b) =>
cursorPos &&
b.range &&
rangeContained(b.range, { start: cursorPos, end: cursorPos })
);
if (!binding) {
continue;
}
if (!isBindingAllowed(text, binding, errors)) {
continue;
}
return getHoverFromBinding(context, propBinding, binding);
}
return;
} catch (error) {
getLogger().debug("validatePropertyBindingInfo failed:", error);
return;
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import {
UI5Type,
UI5TypedefProp,
} from "@ui5-language-assistant/semantic-model-types";
import { BindContext } from "../../../types";
import { BindContext } from "../types";
import { MarkupKind } from "vscode-languageserver-types";
import { ui5NodeToFQN, getLink } from "@ui5-language-assistant/logic-utils";
import { PROPERTY_BINDING_INFO } from "../../../constant";
import { PROPERTY_BINDING_INFO } from "../constant";

const getType = (type: UI5Type | undefined): string[] => {
const result: string[] = [];
Expand Down Expand Up @@ -37,17 +37,18 @@ const getType = (type: UI5Type | undefined): string[] => {

export const getDocumentation = (
context: BindContext,
prop: UI5TypedefProp
prop: UI5TypedefProp,
forHover = false
): {
kind: MarkupKind;
value: string;
} => {
const link = getLink(context.ui5Model, PROPERTY_BINDING_INFO);
const values: string[] = [
`\`typedef ${PROPERTY_BINDING_INFO}\``,
`\`(typedef) ${PROPERTY_BINDING_INFO}\``,
forHover ? `---` : "",
`**Type:** ${getType(prop.type)}`,
`**Description:** ${prop.description}`,
`**Visibility:** ${prop.visibility}`,
`**Optional:** ${prop.optional}`,
`[More information](${link})`,
];
Expand Down
2 changes: 2 additions & 0 deletions packages/binding/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ export {
export { getCursorContext } from "./cursor";

export { getLogger } from "./logger";

export { getDocumentation } from "./documentation";
Loading

0 comments on commit de2cccc

Please sign in to comment.