Skip to content

Commit

Permalink
Feat: add goto controller's definition from xml view files (#701)
Browse files Browse the repository at this point in the history
* feat: go to controller's definition from xml view files

* fix: adapt failing test

* fix: change set

* fix: file resolution

* fix: sonar cloud issue

* fix: lint

* fix: add new package to sonar project

* fix: refactor for future reuse

* fix: typo

* fix: sonar properties

* fix: sonar issues

* fix: review comments

* fix: failing test

* fix: sonar issue
  • Loading branch information
marufrasully authored Apr 3, 2024
1 parent e8498f0 commit ceab281
Show file tree
Hide file tree
Showing 36 changed files with 987 additions and 5 deletions.
12 changes: 12 additions & 0 deletions .changeset/light-queens-listen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"@ui5-language-assistant/vscode-ui5-language-assistant-bas-ext": patch
"vscode-ui5-language-assistant": patch
"@ui5-language-assistant/xml-views-completion": patch
"@ui5-language-assistant/xml-views-definition": patch
"@ui5-language-assistant/xml-views-validation": patch
"@ui5-language-assistant/xml-views-tooltip": patch
"@ui5-language-assistant/language-server": patch
"@ui5-language-assistant/context": patch
---

Enable go to controller's definition from XML view file
11 changes: 9 additions & 2 deletions packages/context/src/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,8 @@ function collectViewsCustomTemplates(
* @param manifest manifest of an app
*/
async function extractManifestDetails(
manifest: Manifest
manifest: Manifest,
manifestPath: string
): Promise<ManifestDetails> {
const customViews = {};
const targets = manifest["sap.ui5"]?.routing?.targets || {};
Expand Down Expand Up @@ -258,6 +259,8 @@ async function extractManifestDetails(
customViews,
flexEnabled,
minUI5Version,
appId: manifest["sap.app"]?.id ?? "",
manifestPath,
};
}

Expand All @@ -271,6 +274,8 @@ export async function getManifestDetails(
const manifestPath = await findManifestPath(documentPath);
if (!manifestPath) {
return {
appId: "",
manifestPath: "",
flexEnabled: false,
customViews: {},
mainServicePath: undefined,
Expand All @@ -280,13 +285,15 @@ export async function getManifestDetails(
const manifest = await getUI5Manifest(manifestPath);
if (!manifest) {
return {
appId: "",
manifestPath: "",
flexEnabled: false,
customViews: {},
mainServicePath: undefined,
minUI5Version: undefined,
};
}
return extractManifestDetails(manifest);
return extractManifestDetails(manifest, manifestPath);
}

/**
Expand Down
4 changes: 4 additions & 0 deletions packages/context/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,16 @@ export interface ServiceDetails {
* @param minUI5Version minimum version of UI5
* @param mainServicePath path to a main OData service
* @param customViews record of views id and their entity set
* @param appId application id under `sap.app` namespace
* @param manifestPath path to manifest.json file
*/
export type ManifestDetails = {
flexEnabled: boolean;
minUI5Version: string | undefined;
mainServicePath: string | undefined;
customViews: { [name: string]: { entitySet?: string; contextPath?: string } };
appId: string;
manifestPath: string;
};
/**
* @param framework UI5 framework
Expand Down
4 changes: 4 additions & 0 deletions packages/context/test/unit/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ describe("context", () => {
const getManifestDetailsStub = jest
.spyOn(manifest, "getManifestDetails")
.mockResolvedValue({
appId: "",
manifestPath: "",
mainServicePath: "/",
customViews: {},
flexEnabled: false,
Expand Down Expand Up @@ -54,6 +56,8 @@ describe("context", () => {
const getManifestDetailsStub = jest
.spyOn(manifest, "getManifestDetails")
.mockResolvedValue({
appId: "",
manifestPath: "",
mainServicePath: "/",
customViews: {},
flexEnabled: false,
Expand Down
12 changes: 12 additions & 0 deletions packages/context/test/unit/manifest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ describe("manifest", () => {
mainServicePath: "/processor/",
flexEnabled: true,
minUI5Version: "1.108.26",
appId: "sap.fe.demo.managetravels",
manifestPath: join(appRoot, "manifest.json"),
});
});

Expand Down Expand Up @@ -173,8 +175,11 @@ describe("manifest", () => {
});
try {
const result = await getManifestDetails(docPath);
// adapt manifestPath
result.manifestPath = result.manifestPath.split(appRoot).join(".");
expect(result).toMatchInlineSnapshot(`
Object {
"appId": "",
"customViews": Object {
"template1": Object {
"contextPath": "/Incidents/to_Customer",
Expand Down Expand Up @@ -211,6 +216,7 @@ describe("manifest", () => {
},
"flexEnabled": false,
"mainServicePath": "//",
"manifestPath": "./manifest.json",
"minUI5Version": undefined,
}
`);
Expand Down Expand Up @@ -256,6 +262,8 @@ describe("manifest", () => {
mainServicePath: "/processor/",
flexEnabled: true,
minUI5Version: "1.108.26",
appId: "sap.fe.demo.managetravels",
manifestPath: join(appRoot, "manifest.json"),
});
} finally {
mock.restore();
Expand Down Expand Up @@ -284,6 +292,8 @@ describe("manifest", () => {
flexEnabled: false,
mainServicePath: undefined,
minUI5Version: undefined,
appId: "",
manifestPath: "",
});
} finally {
cacheSpy.mockRestore();
Expand All @@ -309,6 +319,8 @@ describe("manifest", () => {
flexEnabled: false,
mainServicePath: undefined,
minUI5Version: undefined,
appId: "",
manifestPath: "",
});
} finally {
cacheGetSpy.mockRestore();
Expand Down
2 changes: 2 additions & 0 deletions packages/context/test/unit/manifest_with_mock.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,11 @@ describe("manifest", () => {
const result = await getManifestDetails(docPath);
expect(result).toMatchInlineSnapshot(`
Object {
"appId": "",
"customViews": Object {},
"flexEnabled": false,
"mainServicePath": undefined,
"manifestPath": "",
"minUI5Version": undefined,
}
`);
Expand Down
1 change: 1 addition & 0 deletions packages/language-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"node": ">=10.0.0"
},
"dependencies": {
"@ui5-language-assistant/xml-views-definition": "0.0.1",
"@ui5-language-assistant/binding": "1.0.27",
"@sap/swa-for-sapbas-vsx": "1.1.9",
"@ui5-language-assistant/logger": "0.0.1",
Expand Down
4 changes: 4 additions & 0 deletions packages/language-server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import { initSwa } from "./swa";
import { getLogger, setLogLevel } from "./logger";
import { initI18n } from "./i18n";
import { isXMLView } from "@ui5-language-assistant/logic-utils";
import { getDefinition } from "@ui5-language-assistant/xml-views-definition";

const connection = createConnection(ProposedFeatures.all);
const documents = new TextDocuments(TextDocument);
Expand Down Expand Up @@ -91,6 +92,7 @@ connection.onInitialize(
triggerCharacters: ['"', "'", ":", "<", "/"],
},
hoverProvider: true,
definitionProvider: true,
codeActionProvider: true,
// Each command executes a different code action scenario
executeCommandProvider: {
Expand All @@ -104,6 +106,8 @@ connection.onInitialize(
}
);

connection.onDefinition(getDefinition);

connection.onInitialized(async (): Promise<void> => {
getLogger().info("`onInitialized` event");
if (hasConfigurationCapability) {
Expand Down
2 changes: 2 additions & 0 deletions packages/language-server/test/unit/completion-items-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ export const getDefaultContext = (ui5Model: UI5SemanticModel): Context => {
ui5Model,
customViewId: "",
manifestDetails: {
appId: "",
manifestPath: "",
flexEnabled: false,
customViews: {},
mainServicePath: undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ export async function computeNewDiagnosticLSPResponse(
appContext = {
...getDefaultContext(ui5Model),
manifestDetails: {
appId: "",
manifestPath: "",
flexEnabled: options ? options.flexEnabled : false,
customViews: {},
mainServicePath: undefined,
Expand Down
2 changes: 2 additions & 0 deletions packages/xml-views-completion/test/unit/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ export const getDefaultContext = (ui5Model: UI5SemanticModel): Context => {
ui5Model,
customViewId: "",
manifestDetails: {
appId: "",
manifestPath: "",
flexEnabled: false,
customViews: {},
mainServicePath: undefined,
Expand Down
Empty file.
4 changes: 4 additions & 0 deletions packages/xml-views-definition/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Contribution Guide

This package does not have any unique development flows.
Please see the top level [Contribution Guide](../../CONTRIBUTING.md).
39 changes: 39 additions & 0 deletions packages/xml-views-definition/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[![npm (scoped)](https://img.shields.io/npm/v/@ui5-language-assistant/xml-views-definition.svg)](https://www.npmjs.com/package/@ui5-language-assistant/xml-views-definition)

# @ui5-language-assistant/xml-views-definition

Logic for goto definition of Language Server Protocol (LSP).

## Supported scenarios:

### From XML to controllers' definition:

It supports dot or object notation for following XML attributes.

- "controllerName"
- "template:require"
- "core:require"

It resolves controllers' definition as follows:

1. It tries to load `<path>.controller.js`
2. It tries to load `<path>.js`
3. It tries to load `<path>.controller.ts`
4. It tries to load `<path>.ts`

## Usage

This package only exposes programmatic APIs, import the package and use the exported APIs
defined in [api.d.ts](./api.d.ts).

## Support

Please open [issues](https://github.com/SAP/ui5-language-assistant/issues) on github.

## Contributing

See [CONTRIBUTING.md](./CONTRIBUTING.md).

## Licensing

Copyright 2022 SAP SE. Please see our [LICENSE](../../LICENSE) for copyright and license information. Detailed information including third-party components and their licensing/copyright information is available [via the REUSE tool](https://api.reuse.software/info/github.com/SAP/ui5-language-assistant).
1 change: 1 addition & 0 deletions packages/xml-views-definition/api.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { getDefinition } from "./src/api";
15 changes: 15 additions & 0 deletions packages/xml-views-definition/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const { join } = require("path");
const defaultConfig = require("../../jest.config");

module.exports = {
...defaultConfig,
globals: {
"ts-jest": {
tsconfig: join(__dirname, "tsconfig-test.json"),
diagnostics: {
// warnOnly: true,
exclude: /\.(spec|test)\.ts$/,
},
},
},
};
40 changes: 40 additions & 0 deletions packages/xml-views-definition/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "@ui5-language-assistant/xml-views-definition",
"version": "0.0.1",
"private": true,
"description": "Definition logic for UI5 XML-Views",
"keywords": [],
"files": [
".reuse",
"LICENSES",
"lib/src",
"api.d.ts",
"src"
],
"main": "lib/src/api.js",
"repository": "https://github.com/sap/ui5-language-assistant/",
"license": "Apache-2.0",
"typings": "./api.d.ts",
"dependencies": {
"vscode-languageserver": "8.0.2",
"@xml-tools/ast": "5.0.0",
"vscode-languageserver-textdocument": "1.0.1",
"vscode-uri": "2.1.2",
"@ui5-language-assistant/context": "4.0.24",
"@xml-tools/parser": "1.0.7",
"@ui5-language-assistant/binding-parser": "1.0.7"
},
"devDependencies": {
"vscode-languageserver-types": "3.17.2",
"@ui5-language-assistant/test-framework": "4.0.12"
},
"scripts": {
"ci": "npm-run-all clean compile lint coverage",
"clean": "rimraf ./lib ./coverage ./nyc_output",
"compile": "yarn run clean && tsc -p .",
"compile:watch": "tsc -p . --watch",
"lint": "eslint . --ext .ts --max-warnings=0 --ignore-path=../../.gitignore",
"test": "jest --ci --forceExit --detectOpenHandles --maxWorkers=1 --coverage=false",
"coverage": "jest --ci --forceExit --detectOpenHandles --maxWorkers=1 --coverage=true"
}
}
16 changes: 16 additions & 0 deletions packages/xml-views-definition/src/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Location } from "vscode-languageserver-types";
import { DefinitionParams } from "vscode-languageserver";
import { getControllerLocation } from "./controller";

/**
* Get definition location(s). This method implements `onDefinition` request of LSP (Language Server Protocol)
*
* @param param definition param
* @returns definition location(s)
*/
export async function getDefinition(
param: DefinitionParams
): Promise<Location[]> {
const ctrLoc = await getControllerLocation(param);
return [...ctrLoc];
}
Loading

0 comments on commit ceab281

Please sign in to comment.