Skip to content

Commit

Permalink
feat: icons in editor POC - failing tests
Browse files Browse the repository at this point in the history
  • Loading branch information
uxkjaer committed Oct 21, 2022
1 parent 352164f commit f4effa8
Show file tree
Hide file tree
Showing 4 changed files with 353 additions and 1 deletion.
4 changes: 4 additions & 0 deletions packages/semantic-model-types/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ export interface UI5EnumValue extends BaseUI5Node {
kind: "UI5EnumValue";
}

export interface UI5IconValue extends BaseUI5Node {
kind: "UI5IconValue";
}

export interface UI5Namespace extends BaseUI5Node {
kind: "UI5Namespace";
// Likely Not Relevant for XML.Views
Expand Down
94 changes: 93 additions & 1 deletion packages/vscode-ui5-language-assistant/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import {
commands,
env,
Uri,
OverviewRulerLane,
DecorationOptions,
Range,
DecorationRangeBehavior,
} from "vscode";
import {
LanguageClient,
Expand Down Expand Up @@ -46,7 +50,7 @@ export async function activate(context: ExtensionContext): Promise<void> {
window.onDidChangeActiveTextEditor(() => {
updateCurrentModel(undefined);
});

textDecorator(context);
client.start();
}

Expand Down Expand Up @@ -125,6 +129,94 @@ function updateCurrentModel(model: UI5Model | undefined) {
}
}

function textDecorator(context: ExtensionContext): void {
let timeout: NodeJS.Timer | undefined = undefined;

// create a decorator type that we use to decorate small numbers
const InlineIconDecoration = window.createTextEditorDecorationType({
textDecoration: "none; opacity: 0.6 !important;",
rangeBehavior: DecorationRangeBehavior.ClosedClosed,
});

const HideTextDecoration = window.createTextEditorDecorationType({
textDecoration: "none; display: none;", // a hack to inject custom style
});

let activeEditor = window.activeTextEditor;

function updateDecorations() {
if (!activeEditor) {
return;
}
const regEx = /sap-icon:\/\/(\w+)/g;
const text = activeEditor.document.getText();
const decoratirOptions: DecorationOptions[] = [];
let match;
while ((match = regEx.exec(text))) {
const startPos = activeEditor.document.positionAt(match.index);
const endPos = activeEditor.document.positionAt(
match.index + match[0].length
);
const item: DecorationOptions = {
range: new Range(startPos, endPos),
renderOptions: {
before: {
fontStyle: "SAP-icons",
contentText: "",
},
},
hoverMessage: "",
};

decoratirOptions.push(item);
}
activeEditor.setDecorations(InlineIconDecoration, decoratirOptions);
activeEditor.setDecorations(
HideTextDecoration,
decoratirOptions
.map(({ range }) => range)
.filter((i) => i.start.line !== activeEditor!.selection.start.line)
);
}

function triggerUpdateDecorations(throttle = false) {
if (timeout) {
clearTimeout(timeout);
timeout = undefined;
}
if (throttle) {
timeout = setTimeout(updateDecorations, 500);
} else {
updateDecorations();
}
}

if (activeEditor) {
triggerUpdateDecorations();
}

window.onDidChangeActiveTextEditor(
(editor) => {
activeEditor = editor;
if (editor) {
triggerUpdateDecorations();
}
},
null,
context.subscriptions
);

workspace.onDidChangeTextDocument(
(event) => {
if (activeEditor && event.document === activeEditor.document) {
triggerUpdateDecorations(true);
}
},
null,
context.subscriptions
);
}

export function deactivate(): Thenable<void> | undefined {
if (!client) {
return undefined;
Expand Down
41 changes: 41 additions & 0 deletions packages/xml-views-completion/src/providers/attributeValue/icon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { map } from "lodash";
import { XMLAttribute } from "@xml-tools/ast";
import { getUI5PropertyByXMLAttributeKey } from "@ui5-language-assistant/logic-utils";
import { UI5EnumsInXMLAttributeValueCompletion } from "../../../api";
import { filterMembersForSuggestion } from "../utils/filter-members";
import { UI5AttributeValueCompletionOptions } from "./index";
import {
UI5Field,
UI5IconValue,
} from "@ui5-language-assistant/semantic-model-types";

/**
* Suggests Enum value inside Attribute
* For example: 'ListSeparators' in 'showSeparators' attribute in `sap.m.ListBase` element
*/
export function iconSuggestions(
opts: UI5AttributeValueCompletionOptions
): void | UI5EnumsInXMLAttributeValueCompletion[] {
const ui5Property = getUI5PropertyByXMLAttributeKey(
opts.attribute,
opts.context
);
const propType = ui5Property?.type;
if (propType?.kind !== "UI5Namespace") {
return [];
}

const fields = propType.fields;
const prefix = opts.prefix ?? "";
const prefixMatchingIconValues: UI5Field[] = filterMembersForSuggestion(
fields,
prefix,
[]
);

// return map(prefixMatchingIconValues, (_) => ({
// type: "UI5EnumsInXMLAttributeValue",
// ui5Node: _,
// astNode: opts.attribute as XMLAttribute,
// }));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
// import { expect } from "chai";
// import { forEach, map } from "lodash";
// import { UI5SemanticModel } from "@ui5-language-assistant/semantic-model-types";
// import { generateModel } from "@ui5-language-assistant/test-utils";
// import { generate } from "@ui5-language-assistant/semantic-model";
// import { XMLAttribute, XMLElement } from "@xml-tools/ast";
// import { iconSuggestions } from "../../../src/providers/attributeValue/icon";
// import { UI5XMLViewCompletion } from "../../../api";
// import { testSuggestionsScenario } from "../../utils";

// describe("The ui5-language-assistant xml-views-completion", () => {
// let ui5SemanticModel: UI5SemanticModel;
// before(async function () {
// ui5SemanticModel = await generateModel({
// framework: "SAPUI5",
// version: "1.71.49",
// modelGenerator: generate,
// });
// });

// context("icon values", () => {
// context("applicable scenarios", () => {
// it("will suggest icon values with no prefix provided", () => {
// const xmlSnippet = `
// <mvc:View
// xmlns:mvc="sap.ui.core.mvc"
// xmlns="sap.m">
// <Button icon = "⇶">
// </Button>
// </mvc:View>`;

// testSuggestionsScenario({
// model: ui5SemanticModel,
// xmlText: xmlSnippet,
// providers: {
// attributeValue: [iconSuggestions],
// },
// assertion: (suggestions) => {
// const suggestedValues = map(suggestions, (_) => _.ui5Node.name);
// expect(suggestedValues).to.deep.equalInAnyOrder([
// "All",
// "Inner",
// "None",
// ]);
// expectIconValuesSuggestions(suggestions, "List");
// },
// });
// });

// it("will suggest icon values filtered by prefix", () => {
// const xmlSnippet = `
// <mvc:View
// xmlns:mvc="sap.ui.core.mvc"
// xmlns="sap.m">
// <Button icon = "⇶">
// </Button>
// </mvc:View>`;

// testSuggestionsScenario({
// model: ui5SemanticModel,
// xmlText: xmlSnippet,
// providers: {
// attributeValue: [iconSuggestions],
// },
// assertion: (suggestions) => {
// const suggestedValues = map(suggestions, (_) => _.ui5Node.name);
// expect(suggestedValues).to.deep.equalInAnyOrder(["Inner", "None"]);
// expectIconValuesSuggestions(suggestions, "List");
// },
// });
// });

// it("Will not suggest any icon values if none match the prefix", () => {
// const xmlSnippet = `
// <mvc:View
// xmlns:mvc="sap.ui.core.mvc"
// xmlns="sap.m">
// <Button icon = "⇶">
// </Button>
// </mvc:View>`;

// testSuggestionsScenario({
// model: ui5SemanticModel,
// xmlText: xmlSnippet,
// providers: {
// attributeValue: [iconSuggestions],
// },
// assertion: (suggestions) => {
// expect(suggestions).to.be.empty;
// },
// });
// });
// });

// context("none applicable scenarios", () => {
// it("will not provide any suggestions when the property is not of icon type", () => {
// const xmlSnippet = `
// <mvc:View
// xmlns:mvc="sap.ui.core.mvc"
// xmlns="sap.m">
// <List icon = "⇶">
// </List>
// </mvc:View>`;

// testSuggestionsScenario({
// model: ui5SemanticModel,
// xmlText: xmlSnippet,
// providers: {
// attributeValue: [iconSuggestions],
// },
// assertion: (suggestions) => {
// expect(suggestions).to.be.empty;
// },
// });
// });

// it("will not provide any suggestions when it is not an attribute value completion", () => {
// const xmlSnippet = `
// <mvc:View
// xmlns:mvc="sap.ui.core.mvc"
// xmlns="sap.m">
// <Button ⇶>
// </Button>
// </mvc:View>`;

// testSuggestionsScenario({
// model: ui5SemanticModel,
// xmlText: xmlSnippet,
// providers: {
// attributeValue: [iconSuggestions],
// },
// assertion: (suggestions) => {
// expect(suggestions).to.be.empty;
// },
// });
// });

// it("will not provide any suggestions when the property type is undefined", () => {
// const xmlSnippet = `
// <mvc:View
// xmlns:mvc="sap.ui.core.mvc"
// xmlns="sap.m">
// <App homeIcon = "⇶">
// </App>
// </mvc:View>`;

// testSuggestionsScenario({
// model: ui5SemanticModel,
// xmlText: xmlSnippet,
// providers: {
// attributeValue: [iconSuggestions],
// },
// assertion: (suggestions) => {
// expect(suggestions).to.be.empty;
// },
// });
// });

// it("will not provide any suggestions when not inside a UI5 Class", () => {
// const xmlSnippet = `
// <mvc:View
// xmlns:mvc="sap.ui.core.mvc"
// xmlns="sap.m">
// <Bamba icon = "⇶">
// </Bamba>
// </mvc:View>`;

// testSuggestionsScenario({
// model: ui5SemanticModel,
// xmlText: xmlSnippet,
// providers: {
// attributeValue: [iconSuggestions],
// },
// assertion: (suggestions) => {
// expect(ui5SemanticModel.classes["sap.ui.core.mvc.Bamba"]).to.be
// .undefined;
// expect(suggestions).to.be.empty;
// },
// });
// });

// it("Will not suggest any enum values if there is no matching UI5 property", () => {
// const xmlSnippet = `
// <mvc:View
// xmlns:mvc="sap.ui.core.mvc"
// xmlns="sap.m">
// <Button UNKNOWN = "⇶">
// </Button>
// </mvc:View>`;

// testSuggestionsScenario({
// model: ui5SemanticModel,
// xmlText: xmlSnippet,
// providers: {
// attributeValue: [iconSuggestions],
// },
// assertion: (suggestions) => {
// expect(suggestions).to.be.empty;
// },
// });
// });
// });
// });
// });

// function expectIconValuesSuggestions(
// suggestions: UI5XMLViewCompletion[],
// expectedParentTag: string
// ): void {
// forEach(suggestions, (_) => {
// expect(_.type).to.equal(`UI5IconInXMLAttributeValue`);
// expect((_.astNode as XMLAttribute).key).to.equal("showSeparators");
// expect((_.astNode.parent as XMLElement).name).to.equal(expectedParentTag);
// });
// }

0 comments on commit f4effa8

Please sign in to comment.