feat: Support authoring features (#977)
* feat: Initialize Language Server (#959) * feat: Support compiling and pushing diagnostics (#962) * feat: Support basic groovy syntax highlighting (#960) * feat: Support semantic highlighting (#967) * feat: Support document outline (#969) * feat: Support auto completion in dependencies closure (#970) * feat: Support Basic Auto Completion (#971) * fix: Add null check for visitors (#974) * feat: Show symbol detail for method calls (#973) * chore: Prepare for 3.8.0 (#978) * fix: Use padding to correct version order (#986) * fix: Dependency completion doesn't work when multiple dependencies closures exist (#984) * fix: Correct version completion kind (#985) * fix: Handle multiple content changes (#992) * feat: Support completion for settings.gradle (#988) * fix: Offer completion results from supertypes (#987) * feat: Provide dependencies content in outline (#998) * feat: Support basic java plugin aware (#989) * feat: Support basic tasks and dependencies (#1002) * chore: Add thirdpartynotice * chore: Fix ci
This commit is contained in:
Родитель
8750a4d929
Коммит
12c59040b4
|
@ -27,3 +27,6 @@ bin/
|
|||
|
||||
# Ignore Gradle build output directory
|
||||
build
|
||||
|
||||
# Jar files
|
||||
*.jar
|
||||
|
|
|
@ -41,6 +41,37 @@
|
|||
"hidden": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Debug Language Server: Launch Extension",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": ["--extensionDevelopmentPath=${workspaceFolder}/extension"],
|
||||
"outFiles": ["${workspaceFolder}/extension/dist/**/*.js"],
|
||||
"preLaunchTask": "Gradle: Build",
|
||||
"env": {
|
||||
"VSCODE_DEBUG_LANGUAGE_SERVER": "true",
|
||||
"VSCODE_GRADLE_PORT": "6006"
|
||||
},
|
||||
"presentation": {
|
||||
"group": "debug",
|
||||
"order": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "java",
|
||||
"name": "Debug Language Server: Launch Language Server",
|
||||
"request": "launch",
|
||||
"mainClass": "com.microsoft.gradle.GradleLanguageServer",
|
||||
"projectName": "gradle-language-server",
|
||||
"env": {
|
||||
"VSCODE_GRADLE_PORT": "6006"
|
||||
},
|
||||
"presentation": {
|
||||
"group": "debug",
|
||||
"order": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Test: Groovy Default",
|
||||
"type": "extensionHost",
|
||||
|
|
19
CHANGELOG.md
19
CHANGELOG.md
|
@ -4,18 +4,37 @@ All notable changes to the "vscode-gradle" extension will be documented in this
|
|||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## 3.8.0
|
||||
### Added
|
||||
- Support highlighting of Gradle file. [PR#960](https://github.com/microsoft/vscode-gradle/pull/960), [PR#967](https://github.com/microsoft/vscode-gradle/pull/967)
|
||||
- Provide document outline of Gradle file. [PR#969](https://github.com/microsoft/vscode-gradle/pull/969)
|
||||
- Show syntax diagnostics of Gradle file. [PR#962](https://github.com/microsoft/vscode-gradle/pull/962)
|
||||
- Support auto completion for dependencies. [PR#970](https://github.com/microsoft/vscode-gradle/pull/970)
|
||||
- Support auto completion for basic Gradle closures. [PR#971](https://github.com/microsoft/vscode-gradle/pull/971)
|
||||
- Support basic projects view. [PR#1002](https://github.com/microsoft/vscode-gradle/pull/1002)
|
||||
|
||||
### Changed
|
||||
- Upgrade vscode requirement to `1.60.0`. [PR#997](https://github.com/microsoft/vscode-gradle/pull/997)
|
||||
- Adopt the new `folder-library` icon. [PR#997](https://github.com/microsoft/vscode-gradle/pull/997)
|
||||
|
||||
### Fixed
|
||||
- [Bugs fixed](https://github.com/microsoft/vscode-gradle/issues?q=is%3Aissue+label%3Abug+milestone%3A3.8.0+is%3Aclosed)
|
||||
|
||||
## 3.7.1
|
||||
### Fixed
|
||||
- Fix the `Details` and `Changelog` tabs in the marketplace page. [PR#1012](https://github.com/microsoft/vscode-gradle/pull/1012)
|
||||
|
||||
|
||||
## 3.7.0
|
||||
### Added
|
||||
- Support dependency view. [PR#887](https://github.com/microsoft/vscode-gradle/pull/887)
|
||||
- Support local Gradle installation. [PR#926](https://github.com/microsoft/vscode-gradle/pull/926)
|
||||
|
||||
### Changed
|
||||
- Rename `Gradle Tasks` view to `Gradle Projects` view.
|
||||
- Hide `STOPPED` daemons in Gradle Daemons view by default. [PR#940](https://github.com/microsoft/vscode-gradle/pull/940)
|
||||
- Refine UX when there is no item in pinned tasks and recent tasks view. [PR#937](https://github.com/microsoft/vscode-gradle/pull/937)
|
||||
|
||||
### Fixed
|
||||
- [Bugs fixed](https://github.com/microsoft/vscode-gradle/issues?q=is%3Aissue+label%3Abug+milestone%3A3.7.0+is%3Aclosed)
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"comments": {
|
||||
"lineComment": "//",
|
||||
"blockComment": ["/*", "*/"]
|
||||
},
|
||||
"brackets": [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"]
|
||||
],
|
||||
"autoClosingPairs": [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["'", "'"],
|
||||
{
|
||||
"open": "/**",
|
||||
"close": " */",
|
||||
"notIn": ["string"]
|
||||
}
|
||||
],
|
||||
"surroundingPairs": [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["'", "'"],
|
||||
["<", ">"]
|
||||
]
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -2,7 +2,7 @@
|
|||
"name": "vscode-gradle",
|
||||
"displayName": "Gradle Tasks",
|
||||
"description": "Run Gradle tasks in VS Code",
|
||||
"version": "3.7.1",
|
||||
"version": "3.8.0",
|
||||
"private": true,
|
||||
"publisher": "vscjava",
|
||||
"aiKey": "b4aae7d0-c65b-4819-92bf-1d2f537ae7ce",
|
||||
|
@ -37,6 +37,25 @@
|
|||
],
|
||||
"main": "./dist/index.js",
|
||||
"contributes": {
|
||||
"languages": [
|
||||
{
|
||||
"id": "gradle",
|
||||
"extensions": [
|
||||
".gradle"
|
||||
],
|
||||
"aliases": [
|
||||
"Gradle"
|
||||
],
|
||||
"configuration": "./gradle-language-configuration.json"
|
||||
}
|
||||
],
|
||||
"grammars": [
|
||||
{
|
||||
"language": "gradle",
|
||||
"scopeName": "source.groovy",
|
||||
"path": "./syntaxes/groovy.tmLanguage.json"
|
||||
}
|
||||
],
|
||||
"problemMatchers": [
|
||||
{
|
||||
"owner": "gradle",
|
||||
|
@ -57,7 +76,12 @@
|
|||
{
|
||||
"id": "gradleTasksView",
|
||||
"name": "Gradle Projects",
|
||||
"when": "gradle:activated"
|
||||
"when": "gradle:activated && !gradle:defaultView"
|
||||
},
|
||||
{
|
||||
"id": "gradleDefaultProjectsView",
|
||||
"name": "Gradle Projects",
|
||||
"when": "gradle:activated && gradle:defaultView"
|
||||
},
|
||||
{
|
||||
"id": "pinnedTasksView",
|
||||
|
@ -437,7 +461,7 @@
|
|||
},
|
||||
{
|
||||
"command": "gradle.openSettings",
|
||||
"when": "view == gradleTasksView"
|
||||
"when": "view == gradleTasksView || view == gradleDefaultProjectsView"
|
||||
},
|
||||
{
|
||||
"command": "gradle.findTask",
|
||||
|
@ -445,7 +469,7 @@
|
|||
},
|
||||
{
|
||||
"command": "gradle.runBuild",
|
||||
"when": "view == gradleTasksView",
|
||||
"when": "view == gradleTasksView || view == gradleDefaultProjectsView",
|
||||
"group": "navigation@0"
|
||||
},
|
||||
{
|
||||
|
@ -497,12 +521,12 @@
|
|||
"view/item/context": [
|
||||
{
|
||||
"command": "gradle.runTask",
|
||||
"when": "view =~ /^gradleTasksView$|^pinnedTasksView$|^recentTasksView$/ && viewItem =~ /^debugTask.*$|^task.*$/",
|
||||
"when": "view =~ /^gradleTasksView$|^pinnedTasksView$|^recentTasksView$|^gradleDefaultProjectsView$/ && viewItem =~ /^debugTask.*$|^task.*$/",
|
||||
"group": "contextGroup1@0"
|
||||
},
|
||||
{
|
||||
"command": "gradle.runTaskWithArgs",
|
||||
"when": "view =~ /^gradleTasksView$|^pinnedTasksView$|^recentTasksView$/ && viewItem =~ /^debugTask(WithTerminals)?$|^task(WithTerminals)?$/",
|
||||
"when": "view =~ /^gradleTasksView$|^pinnedTasksView$|^recentTasksView$|^gradleDefaultProjectsView$/ && viewItem =~ /^debugTask(WithTerminals)?$|^task(WithTerminals)?$/",
|
||||
"group": "contextGroup1@1"
|
||||
},
|
||||
{
|
||||
|
@ -532,7 +556,7 @@
|
|||
},
|
||||
{
|
||||
"command": "gradle.runTask",
|
||||
"when": "view =~ /^gradleTasksView$|^pinnedTasksView$|^recentTasksView$/ && viewItem =~ /^debugTask.*$|^task.*$/",
|
||||
"when": "view =~ /^gradleTasksView$|^pinnedTasksView$|^recentTasksView$|^gradleDefaultProjectsView$/ && viewItem =~ /^debugTask.*$|^task.*$/",
|
||||
"group": "inline@3"
|
||||
},
|
||||
{
|
||||
|
@ -823,18 +847,19 @@
|
|||
"mocha": "^8.3.1",
|
||||
"prettier": "^2.2.1",
|
||||
"sinon": "^9.2.4",
|
||||
"snyk": "^1.695.0",
|
||||
"snyk": "^1.697.0",
|
||||
"string-argv": "^0.3.1",
|
||||
"tree-kill": "^1.2.2",
|
||||
"ts-loader": "^4.4.2",
|
||||
"ts-protoc-gen": "^0.14.0",
|
||||
"typescript": "^4.2.3",
|
||||
"typescript": "4.2.3",
|
||||
"vsce": "^1.88.0",
|
||||
"webpack": "^4.46.0",
|
||||
"webpack-cli": "^3.3.12"
|
||||
},
|
||||
"snyk": true,
|
||||
"dependencies": {
|
||||
"vscode-extension-telemetry-wrapper": "^0.9.0"
|
||||
"vscode-extension-telemetry-wrapper": "^0.9.0",
|
||||
"vscode-languageclient": "7.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import {
|
|||
PINNED_TASKS_VIEW,
|
||||
GRADLE_DAEMONS_VIEW,
|
||||
RECENT_TASKS_VIEW,
|
||||
GRADLE_DEFAULT_PROJECTS_VIEW,
|
||||
} from './views/constants';
|
||||
import { focusTaskInGradleTasksTree } from './views/viewUtil';
|
||||
import { COMMAND_RENDER_TASK, COMMAND_REFRESH } from './commands';
|
||||
|
@ -39,6 +40,8 @@ import { DependencyTreeItem } from './views/gradleTasks/DependencyTreeItem';
|
|||
import { GRADLE_DEPENDENCY_REVEAL } from './views/gradleTasks/DependencyUtils';
|
||||
import { GradleDependencyProvider } from './dependencies/GradleDependencyProvider';
|
||||
import { getSpecificVersionStatus } from './views/gradleDaemons/util';
|
||||
import { startLanguageServer } from './languageServer/languageServer';
|
||||
import { DefaultProjectsTreeDataProvider } from './views/defaultProject/DefaultProjectsTreeDataProvider';
|
||||
|
||||
export class Extension {
|
||||
private readonly client: GradleClient;
|
||||
|
@ -62,11 +65,14 @@ export class Extension {
|
|||
private readonly recentTasksTreeDataProvider: RecentTasksTreeDataProvider;
|
||||
private readonly recentTasksTreeView: vscode.TreeView<vscode.TreeItem>;
|
||||
private readonly gradleTasksTreeDataProvider: GradleTasksTreeDataProvider;
|
||||
private readonly defaultProjectsTreeView: vscode.TreeView<vscode.TreeItem>;
|
||||
private readonly defaultProjectsTreeDataProvider: DefaultProjectsTreeDataProvider;
|
||||
private readonly api: Api;
|
||||
private readonly commands: Commands;
|
||||
private readonly _onDidTerminalOpen: vscode.EventEmitter<vscode.Terminal> = new vscode.EventEmitter<vscode.Terminal>();
|
||||
private readonly onDidTerminalOpen: vscode.Event<vscode.Terminal> = this
|
||||
._onDidTerminalOpen.event;
|
||||
private readonly _onDidTerminalOpen: vscode.EventEmitter<vscode.Terminal> =
|
||||
new vscode.EventEmitter<vscode.Terminal>();
|
||||
private readonly onDidTerminalOpen: vscode.Event<vscode.Terminal> =
|
||||
this._onDidTerminalOpen.event;
|
||||
private recentTerminal: vscode.Terminal | undefined;
|
||||
|
||||
public constructor(private readonly context: vscode.ExtensionContext) {
|
||||
|
@ -152,6 +158,19 @@ export class Extension {
|
|||
treeDataProvider: this.recentTasksTreeDataProvider,
|
||||
showCollapseAll: false,
|
||||
});
|
||||
this.defaultProjectsTreeDataProvider = new DefaultProjectsTreeDataProvider(
|
||||
this.gradleTaskProvider,
|
||||
this.rootProjectsStore,
|
||||
this.client,
|
||||
this.icons
|
||||
);
|
||||
this.defaultProjectsTreeView = vscode.window.createTreeView(
|
||||
GRADLE_DEFAULT_PROJECTS_VIEW,
|
||||
{
|
||||
treeDataProvider: this.defaultProjectsTreeDataProvider,
|
||||
showCollapseAll: false,
|
||||
}
|
||||
);
|
||||
|
||||
this.gradleTaskManager = new GradleTaskManager(context);
|
||||
this.buildFileWatcher = new FileWatcher('**/*.{gradle,gradle.kts}');
|
||||
|
@ -198,6 +217,7 @@ export class Extension {
|
|||
);
|
||||
|
||||
void this.activate();
|
||||
void startLanguageServer(this.context);
|
||||
}
|
||||
|
||||
private storeSubscriptions(): void {
|
||||
|
@ -215,7 +235,8 @@ export class Extension {
|
|||
this.gradleDaemonsTreeView,
|
||||
this.gradleTasksTreeView,
|
||||
this.pinnedTasksTreeView,
|
||||
this.recentTasksTreeView
|
||||
this.recentTasksTreeView,
|
||||
this.defaultProjectsTreeView
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -229,6 +250,11 @@ export class Extension {
|
|||
'gradle:activated',
|
||||
activated
|
||||
);
|
||||
await vscode.commands.executeCommand(
|
||||
'setContext',
|
||||
'gradle:defaultView',
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
private registerCommands(): void {
|
||||
|
|
|
@ -46,6 +46,10 @@ import {
|
|||
} from './CancellationKeys';
|
||||
import { EventWaiter } from '../util/EventWaiter';
|
||||
import { getGradleConfig, getConfigJavaDebug } from '../util/config';
|
||||
import {
|
||||
setDefault,
|
||||
unsetDefault,
|
||||
} from '../views/defaultProject/DefaultProjectUtils';
|
||||
|
||||
function logBuildEnvironment(environment: Environment): void {
|
||||
const javaEnv = environment.getJavaEnvironment()!;
|
||||
|
@ -59,11 +63,13 @@ function logBuildEnvironment(environment: Environment): void {
|
|||
export class GradleClient implements vscode.Disposable {
|
||||
private readonly connectDeadline = 30; // seconds
|
||||
private grpcClient: GrpcClient | null = null;
|
||||
private readonly _onDidConnect: vscode.EventEmitter<null> = new vscode.EventEmitter<null>();
|
||||
private readonly _onDidConnectFail: vscode.EventEmitter<null> = new vscode.EventEmitter<null>();
|
||||
private readonly _onDidConnect: vscode.EventEmitter<null> =
|
||||
new vscode.EventEmitter<null>();
|
||||
private readonly _onDidConnectFail: vscode.EventEmitter<null> =
|
||||
new vscode.EventEmitter<null>();
|
||||
public readonly onDidConnect: vscode.Event<null> = this._onDidConnect.event;
|
||||
public readonly onDidConnectFail: vscode.Event<null> = this._onDidConnectFail
|
||||
.event;
|
||||
public readonly onDidConnectFail: vscode.Event<null> =
|
||||
this._onDidConnectFail.event;
|
||||
|
||||
private readonly waitForConnect = new EventWaiter(this.onDidConnect).wait;
|
||||
|
||||
|
@ -189,6 +195,7 @@ export class GradleClient implements vscode.Disposable {
|
|||
);
|
||||
break;
|
||||
case Output.OutputType.STDERR:
|
||||
void setDefault();
|
||||
stdErrLoggerStream.write(
|
||||
getBuildReply.getOutput()!.getOutputBytes_asU8()
|
||||
);
|
||||
|
@ -199,6 +206,7 @@ export class GradleClient implements vscode.Disposable {
|
|||
this.handleGetBuildCancelled(getBuildReply.getCancelled()!);
|
||||
break;
|
||||
case GetBuildReply.KindCase.GET_BUILD_RESULT:
|
||||
void unsetDefault();
|
||||
build = getBuildReply.getGetBuildResult()!.getBuild();
|
||||
break;
|
||||
case GetBuildReply.KindCase.ENVIRONMENT:
|
||||
|
@ -625,9 +633,8 @@ export class GradleClient implements vscode.Disposable {
|
|||
this.close();
|
||||
this._onDidConnectFail.fire(null);
|
||||
if (this.server.isReady()) {
|
||||
const connectivityState = this.grpcClient!.getChannel().getConnectivityState(
|
||||
true
|
||||
);
|
||||
const connectivityState =
|
||||
this.grpcClient!.getChannel().getConnectivityState(true);
|
||||
const enumKey = ConnectivityState[connectivityState];
|
||||
logger.error('The client has state:', enumKey);
|
||||
await this.showRestartMessage();
|
||||
|
|
|
@ -22,7 +22,8 @@ export class StopDaemonsCommand extends Command {
|
|||
) {
|
||||
return;
|
||||
}
|
||||
const gradleRootFolders = await this.rootProjectsStore.getProjectRootsWithUniqueVersions();
|
||||
const gradleRootFolders =
|
||||
await this.rootProjectsStore.getProjectRootsWithUniqueVersions();
|
||||
const promises: Promise<StopDaemonsReply | void>[] = gradleRootFolders.map(
|
||||
(rootProject) =>
|
||||
this.client.stopDaemons(rootProject.getProjectUri().fsPath)
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as net from 'net';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import {
|
||||
DidChangeConfigurationNotification,
|
||||
LanguageClientOptions,
|
||||
} from 'vscode-languageclient';
|
||||
import { LanguageClient, StreamInfo } from 'vscode-languageclient/node';
|
||||
import {
|
||||
getConfigGradleJavaHome,
|
||||
getConfigJavaImportGradleHome,
|
||||
getConfigJavaImportGradleUserHome,
|
||||
getConfigJavaImportGradleVersion,
|
||||
getConfigJavaImportGradleWrapperEnabled,
|
||||
} from '../util/config';
|
||||
const CHANNEL_NAME = 'Gradle Language Server';
|
||||
|
||||
export let isLanguageServerStarted = false;
|
||||
|
||||
export async function startLanguageServer(
|
||||
context: vscode.ExtensionContext
|
||||
): Promise<void> {
|
||||
void vscode.window.withProgress(
|
||||
{ location: vscode.ProgressLocation.Window },
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
(progress) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
return new Promise<void>(async (resolve, _reject) => {
|
||||
progress.report({
|
||||
message: 'Initializing Gradle Language Server',
|
||||
});
|
||||
const clientOptions: LanguageClientOptions = {
|
||||
documentSelector: [{ scheme: 'file', language: 'gradle' }],
|
||||
outputChannel: vscode.window.createOutputChannel(CHANNEL_NAME),
|
||||
outputChannelName: CHANNEL_NAME,
|
||||
initializationOptions: {
|
||||
settings: getGradleSettings(),
|
||||
},
|
||||
};
|
||||
let serverOptions;
|
||||
if (process.env.VSCODE_DEBUG_LANGUAGE_SERVER === 'true') {
|
||||
// debug mode
|
||||
const port = process.env.VSCODE_GRADLE_PORT;
|
||||
if (!port) {
|
||||
void vscode.window.showErrorMessage(
|
||||
'VSCODE_GRADLE_PORT is invalid, please check it in launch.json.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
serverOptions = awaitServerConnection.bind(null, port);
|
||||
} else {
|
||||
// keep consistent with gRPC server
|
||||
const javaHome = getConfigGradleJavaHome() || process.env.JAVA_HOME;
|
||||
if (!javaHome) {
|
||||
void vscode.window
|
||||
.showErrorMessage(
|
||||
'There is no valid JAVA_HOME setting to launch Gradle Language Server. Please check your "java.home" setting.',
|
||||
'Open Settings'
|
||||
)
|
||||
.then((answer) => {
|
||||
if (answer === 'Open Settings') {
|
||||
void vscode.commands.executeCommand(
|
||||
'workbench.action.openSettings',
|
||||
'java.home'
|
||||
);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
const args = [
|
||||
'-jar',
|
||||
path.resolve(
|
||||
context.extensionPath,
|
||||
'lib',
|
||||
'gradle-language-server.jar'
|
||||
),
|
||||
];
|
||||
serverOptions = {
|
||||
command: path.join(javaHome, 'bin', 'java'),
|
||||
args: args,
|
||||
};
|
||||
}
|
||||
const languageClient = new LanguageClient(
|
||||
'gradle',
|
||||
'Gradle Language Server',
|
||||
serverOptions,
|
||||
clientOptions
|
||||
);
|
||||
languageClient.onReady().then(resolve, (e) => {
|
||||
resolve();
|
||||
isLanguageServerStarted = true;
|
||||
void vscode.window.showErrorMessage(e);
|
||||
});
|
||||
const disposable = languageClient.start();
|
||||
context.subscriptions.push(disposable);
|
||||
context.subscriptions.push(
|
||||
vscode.workspace.onDidChangeConfiguration((e) => {
|
||||
if (e.affectsConfiguration('java.import.gradle')) {
|
||||
languageClient.sendNotification(
|
||||
DidChangeConfigurationNotification.type,
|
||||
{ settings: getGradleSettings() }
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function awaitServerConnection(port: string): Promise<StreamInfo> {
|
||||
const addr = parseInt(port);
|
||||
return new Promise((resolve, reject) => {
|
||||
const server = net.createServer((stream) => {
|
||||
server.close();
|
||||
resolve({ reader: stream, writer: stream });
|
||||
});
|
||||
server.on('error', reject);
|
||||
server.listen(addr, () => {
|
||||
server.removeListener('error', reject);
|
||||
});
|
||||
return server;
|
||||
});
|
||||
}
|
||||
|
||||
function getGradleSettings(): unknown {
|
||||
return {
|
||||
gradleHome: getConfigJavaImportGradleHome(),
|
||||
gradleVersion: getConfigJavaImportGradleVersion(),
|
||||
gradleWrapperEnabled: getConfigJavaImportGradleWrapperEnabled(),
|
||||
gradleUserHome: getConfigJavaImportGradleUserHome(),
|
||||
};
|
||||
}
|
|
@ -1,9 +1,10 @@
|
|||
import * as vscode from 'vscode';
|
||||
|
||||
export class ProgressHandler {
|
||||
private readonly _onDidProgressStart: vscode.EventEmitter<null> = new vscode.EventEmitter<null>();
|
||||
public readonly onDidProgressStart: vscode.Event<null> = this
|
||||
._onDidProgressStart.event;
|
||||
private readonly _onDidProgressStart: vscode.EventEmitter<null> =
|
||||
new vscode.EventEmitter<null>();
|
||||
public readonly onDidProgressStart: vscode.Event<null> =
|
||||
this._onDidProgressStart.event;
|
||||
|
||||
public constructor(
|
||||
private readonly progress: vscode.Progress<{ message?: string }>,
|
||||
|
|
|
@ -15,8 +15,10 @@ export interface ServerOptions {
|
|||
}
|
||||
|
||||
export class GradleServer implements vscode.Disposable {
|
||||
private readonly _onDidStart: vscode.EventEmitter<null> = new vscode.EventEmitter<null>();
|
||||
private readonly _onDidStop: vscode.EventEmitter<null> = new vscode.EventEmitter<null>();
|
||||
private readonly _onDidStart: vscode.EventEmitter<null> =
|
||||
new vscode.EventEmitter<null>();
|
||||
private readonly _onDidStop: vscode.EventEmitter<null> =
|
||||
new vscode.EventEmitter<null>();
|
||||
private ready = false;
|
||||
private port: number | undefined;
|
||||
private restarting = false;
|
||||
|
|
|
@ -2,7 +2,8 @@ import * as vscode from 'vscode';
|
|||
import { debounce } from '../util/decorators';
|
||||
|
||||
export abstract class EventedStore<V> implements vscode.Disposable {
|
||||
private readonly _onDidChange: vscode.EventEmitter<V | null> = new vscode.EventEmitter<V>();
|
||||
private readonly _onDidChange: vscode.EventEmitter<V | null> =
|
||||
new vscode.EventEmitter<V>();
|
||||
public readonly onDidChange: vscode.Event<V | null> = this._onDidChange.event;
|
||||
|
||||
@debounce(0)
|
||||
|
|
|
@ -50,11 +50,12 @@ export class RootProjectsStore extends StoreMap<string, RootProject> {
|
|||
|
||||
for (const workspaceFolder of workspaceFolders) {
|
||||
const configNestedFolders = getNestedProjectsConfig(workspaceFolder);
|
||||
const gradleProjectFoldersOutsideRoot = getGradleProjectFoldersOutsideRoot(
|
||||
configNestedFolders,
|
||||
gradleProjectFolders,
|
||||
workspaceFolder
|
||||
);
|
||||
const gradleProjectFoldersOutsideRoot =
|
||||
getGradleProjectFoldersOutsideRoot(
|
||||
configNestedFolders,
|
||||
gradleProjectFolders,
|
||||
workspaceFolder
|
||||
);
|
||||
if (gradleProjectFolders.includes(workspaceFolder.uri.fsPath)) {
|
||||
this.setRootProjectFolder(buildRootFolder(workspaceFolder.uri));
|
||||
}
|
||||
|
|
|
@ -2,16 +2,19 @@ import * as vscode from 'vscode';
|
|||
import { isGradleTask, getRunningGradleTasks } from './taskUtil';
|
||||
|
||||
export class GradleTaskManager implements vscode.Disposable {
|
||||
private readonly _onDidStartTask: vscode.EventEmitter<vscode.Task> = new vscode.EventEmitter<vscode.Task>();
|
||||
private readonly _onDidEndTask: vscode.EventEmitter<vscode.Task> = new vscode.EventEmitter<vscode.Task>();
|
||||
private readonly _onDidEndAllTasks: vscode.EventEmitter<null> = new vscode.EventEmitter<null>();
|
||||
private readonly _onDidStartTask: vscode.EventEmitter<vscode.Task> =
|
||||
new vscode.EventEmitter<vscode.Task>();
|
||||
private readonly _onDidEndTask: vscode.EventEmitter<vscode.Task> =
|
||||
new vscode.EventEmitter<vscode.Task>();
|
||||
private readonly _onDidEndAllTasks: vscode.EventEmitter<null> =
|
||||
new vscode.EventEmitter<null>();
|
||||
|
||||
public readonly onDidStartTask: vscode.Event<vscode.Task> = this
|
||||
._onDidStartTask.event;
|
||||
public readonly onDidEndTask: vscode.Event<vscode.Task> = this._onDidEndTask
|
||||
.event;
|
||||
public readonly onDidEndAllTasks: vscode.Event<null> = this._onDidEndAllTasks
|
||||
.event;
|
||||
public readonly onDidStartTask: vscode.Event<vscode.Task> =
|
||||
this._onDidStartTask.event;
|
||||
public readonly onDidEndTask: vscode.Event<vscode.Task> =
|
||||
this._onDidEndTask.event;
|
||||
public readonly onDidEndAllTasks: vscode.Event<null> =
|
||||
this._onDidEndAllTasks.event;
|
||||
|
||||
constructor(private readonly context: vscode.ExtensionContext) {
|
||||
this.context.subscriptions.push(
|
||||
|
|
|
@ -10,25 +10,27 @@ import { getConfigJavaDebug } from '../util/config';
|
|||
import { EventWaiter } from '../util/EventWaiter';
|
||||
|
||||
export class GradleTaskProvider
|
||||
implements vscode.TaskProvider, vscode.Disposable {
|
||||
implements vscode.TaskProvider, vscode.Disposable
|
||||
{
|
||||
private cachedTasks: vscode.Task[] = [];
|
||||
private readonly _onDidLoadTasks: vscode.EventEmitter<
|
||||
vscode.Task[]
|
||||
> = new vscode.EventEmitter<vscode.Task[]>();
|
||||
private readonly _onDidStartRefresh: vscode.EventEmitter<null> = new vscode.EventEmitter<null>();
|
||||
private readonly _onDidStopRefresh: vscode.EventEmitter<null> = new vscode.EventEmitter<null>();
|
||||
private readonly _onDidLoadTasks: vscode.EventEmitter<vscode.Task[]> =
|
||||
new vscode.EventEmitter<vscode.Task[]>();
|
||||
private readonly _onDidStartRefresh: vscode.EventEmitter<null> =
|
||||
new vscode.EventEmitter<null>();
|
||||
private readonly _onDidStopRefresh: vscode.EventEmitter<null> =
|
||||
new vscode.EventEmitter<null>();
|
||||
|
||||
constructor(
|
||||
private readonly rootProjectsStore: RootProjectsStore,
|
||||
private readonly client: GradleClient
|
||||
) {}
|
||||
|
||||
public readonly onDidLoadTasks: vscode.Event<vscode.Task[]> = this
|
||||
._onDidLoadTasks.event;
|
||||
public readonly onDidStartRefresh: vscode.Event<null> = this
|
||||
._onDidStartRefresh.event;
|
||||
public readonly onDidStopRefresh: vscode.Event<null> = this._onDidStopRefresh
|
||||
.event;
|
||||
public readonly onDidLoadTasks: vscode.Event<vscode.Task[]> =
|
||||
this._onDidLoadTasks.event;
|
||||
public readonly onDidStartRefresh: vscode.Event<null> =
|
||||
this._onDidStartRefresh.event;
|
||||
public readonly onDidStopRefresh: vscode.Event<null> =
|
||||
this._onDidStopRefresh.event;
|
||||
private loadTasksPromise?: Promise<vscode.Task[]>;
|
||||
|
||||
private readonly _waitForTasksLoad = new EventWaiter<vscode.Task[]>(
|
||||
|
|
|
@ -100,7 +100,8 @@ describe(getSuiteName('Gradle daemons'), () => {
|
|||
});
|
||||
|
||||
it('should filter out projects with duplicate gradle versions', async () => {
|
||||
const projects = await rootProjectsStore.getProjectRootsWithUniqueVersions();
|
||||
const projects =
|
||||
await rootProjectsStore.getProjectRootsWithUniqueVersions();
|
||||
assert.strictEqual(
|
||||
projects.length,
|
||||
2,
|
||||
|
@ -230,10 +231,9 @@ describe(getSuiteName('Gradle daemons'), () => {
|
|||
mockReply.setMessage('Stopped');
|
||||
mockClient.stopDaemon.resolves(mockReply);
|
||||
|
||||
const showWarningMessageStub = (sinon.stub(
|
||||
vscode.window,
|
||||
'showWarningMessage'
|
||||
) as SinonStub).resolves('Yes');
|
||||
const showWarningMessageStub = (
|
||||
sinon.stub(vscode.window, 'showWarningMessage') as SinonStub
|
||||
).resolves('Yes');
|
||||
|
||||
const mockDaemonInfoBusy = new DaemonInfo();
|
||||
mockDaemonInfoBusy.setStatus(DaemonInfo.DaemonStatus.BUSY);
|
||||
|
@ -280,10 +280,9 @@ describe(getSuiteName('Gradle daemons'), () => {
|
|||
.withArgs(mockWorkspaceFolder2.uri.fsPath)
|
||||
.resolves(mockReply2);
|
||||
|
||||
const showWarningMessageStub = (sinon.stub(
|
||||
vscode.window,
|
||||
'showWarningMessage'
|
||||
) as SinonStub).resolves('Yes');
|
||||
const showWarningMessageStub = (
|
||||
sinon.stub(vscode.window, 'showWarningMessage') as SinonStub
|
||||
).resolves('Yes');
|
||||
|
||||
await new StopDaemonsCommand(mockClient, rootProjectsStore).run();
|
||||
|
||||
|
|
|
@ -336,7 +336,8 @@ describe(getSuiteName('Gradle tasks'), () => {
|
|||
task,
|
||||
},
|
||||
]);
|
||||
const gradleProjects = (await gradleTasksTreeDataProvider.getChildren()) as ProjectTreeItem[];
|
||||
const gradleProjects =
|
||||
(await gradleTasksTreeDataProvider.getChildren()) as ProjectTreeItem[];
|
||||
removeCancellingTask(task);
|
||||
const group = gradleProjects[0].groups[0];
|
||||
const taskItem = group.tasks[0];
|
||||
|
@ -383,7 +384,8 @@ describe(getSuiteName('Gradle tasks'), () => {
|
|||
executeCommandStub.calledWith(COMMAND_RENDER_TASK, task),
|
||||
'Task was not rendered'
|
||||
);
|
||||
const gradleProjects = (await gradleTasksTreeDataProvider.getChildren()) as ProjectTreeItem[];
|
||||
const gradleProjects =
|
||||
(await gradleTasksTreeDataProvider.getChildren()) as ProjectTreeItem[];
|
||||
removeCancellingTask(task);
|
||||
const group = gradleProjects[0].groups[0];
|
||||
const taskItem = group.tasks[0];
|
||||
|
@ -430,7 +432,8 @@ describe(getSuiteName('Gradle tasks'), () => {
|
|||
});
|
||||
|
||||
it('should build root RootProject items at top level', async () => {
|
||||
const rootProjectItems = await gradleTasksTreeDataProvider.getChildren();
|
||||
const rootProjectItems =
|
||||
await gradleTasksTreeDataProvider.getChildren();
|
||||
assert.ok(rootProjectItems.length > 0, 'No root gradle projects found');
|
||||
assert.strictEqual(
|
||||
rootProjectItems.length,
|
||||
|
|
|
@ -348,10 +348,9 @@ describe(getSuiteName('Pinned tasks'), () => {
|
|||
childrenBefore[1] instanceof PinnedTaskTreeItem,
|
||||
'Pinned task is not PinnedTaskTreeItem'
|
||||
);
|
||||
const showWarningMessageStub = (sinon.stub(
|
||||
vscode.window,
|
||||
'showWarningMessage'
|
||||
) as SinonStub).resolves('Yes');
|
||||
const showWarningMessageStub = (
|
||||
sinon.stub(vscode.window, 'showWarningMessage') as SinonStub
|
||||
).resolves('Yes');
|
||||
await new ClearAllPinnedTasksCommand(pinnedTasksStore).run();
|
||||
assert.ok(
|
||||
showWarningMessageStub.calledWith(
|
||||
|
@ -379,7 +378,8 @@ describe(getSuiteName('Pinned tasks'), () => {
|
|||
|
||||
describe('With pinned tasks', () => {
|
||||
it('should build a pinned task treeitem', async () => {
|
||||
const workspaceTreeItems = await gradleTasksTreeDataProvider.getChildren();
|
||||
const workspaceTreeItems =
|
||||
await gradleTasksTreeDataProvider.getChildren();
|
||||
assert.ok(workspaceTreeItems.length > 0, 'No gradle projects found');
|
||||
const workspaceTreeItem = workspaceTreeItems[0] as RootProjectTreeItem;
|
||||
assert.ok(
|
||||
|
@ -404,7 +404,8 @@ describe(getSuiteName('Pinned tasks'), () => {
|
|||
await new PinTaskCommand(pinnedTasksTreeDataProvider).run(taskItem);
|
||||
const children = await pinnedTasksTreeDataProvider.getChildren();
|
||||
assert.strictEqual(children.length, 1);
|
||||
const pinnedTasksRootProjectTreeItem = children[0] as PinnedTasksRootProjectTreeItem;
|
||||
const pinnedTasksRootProjectTreeItem =
|
||||
children[0] as PinnedTasksRootProjectTreeItem;
|
||||
assert.strictEqual(
|
||||
pinnedTasksRootProjectTreeItem.collapsibleState,
|
||||
vscode.TreeItemCollapsibleState.Expanded
|
||||
|
|
|
@ -171,7 +171,8 @@ describe(getSuiteName('Recent tasks'), () => {
|
|||
path.join('resources', 'light', ICON_GRADLE_TASK)
|
||||
);
|
||||
|
||||
const workspaceTreeItem = recentTaskTreeItem.parentTreeItem as RecentTasksRootProjectTreeItem;
|
||||
const workspaceTreeItem =
|
||||
recentTaskTreeItem.parentTreeItem as RecentTasksRootProjectTreeItem;
|
||||
assert.ok(
|
||||
workspaceTreeItem,
|
||||
'parentTreeItem must reference a WorkSpace tree item'
|
||||
|
@ -229,7 +230,8 @@ describe(getSuiteName('Recent tasks'), () => {
|
|||
path.join('resources', 'light', ICON_GRADLE_TASK)
|
||||
);
|
||||
|
||||
const workspaceTreeItem = recentTaskTreeItem.parentTreeItem as RecentTasksRootProjectTreeItem;
|
||||
const workspaceTreeItem =
|
||||
recentTaskTreeItem.parentTreeItem as RecentTasksRootProjectTreeItem;
|
||||
assert.ok(
|
||||
workspaceTreeItem instanceof RecentTasksRootProjectTreeItem,
|
||||
'Tree item is not RecentTasksRootProjectTreeItem'
|
||||
|
@ -247,10 +249,9 @@ describe(getSuiteName('Recent tasks'), () => {
|
|||
'Task is not a RecentTaskTreeItem'
|
||||
);
|
||||
|
||||
const showWarningMessageStub = (sinon.stub(
|
||||
vscode.window,
|
||||
'showWarningMessage'
|
||||
) as SinonStub).resolves('Yes');
|
||||
const showWarningMessageStub = (
|
||||
sinon.stub(vscode.window, 'showWarningMessage') as SinonStub
|
||||
).resolves('Yes');
|
||||
|
||||
await new ClearAllRecentTasksCommand(recentTasksStore).run();
|
||||
|
||||
|
@ -367,10 +368,9 @@ describe(getSuiteName('Recent tasks'), () => {
|
|||
);
|
||||
assert.strictEqual(recentTaskTreeItemBefore.description, '(2)');
|
||||
|
||||
const showWarningMessageStub = (sinon.stub(
|
||||
vscode.window,
|
||||
'showWarningMessage'
|
||||
) as SinonStub).resolves('Yes');
|
||||
const showWarningMessageStub = (
|
||||
sinon.stub(vscode.window, 'showWarningMessage') as SinonStub
|
||||
).resolves('Yes');
|
||||
|
||||
await new CloseAllTaskTerminalsCommand(taskTerminalsStore).run();
|
||||
|
||||
|
|
|
@ -2,9 +2,10 @@ import * as vscode from 'vscode';
|
|||
import * as minimatch from 'minimatch';
|
||||
|
||||
export class FileWatcher implements vscode.Disposable {
|
||||
private readonly _onDidChange: vscode.EventEmitter<vscode.Uri> = new vscode.EventEmitter<vscode.Uri>();
|
||||
public readonly onDidChange: vscode.Event<vscode.Uri> = this._onDidChange
|
||||
.event;
|
||||
private readonly _onDidChange: vscode.EventEmitter<vscode.Uri> =
|
||||
new vscode.EventEmitter<vscode.Uri>();
|
||||
public readonly onDidChange: vscode.Event<vscode.Uri> =
|
||||
this._onDidChange.event;
|
||||
private onDidSaveDisposable: vscode.Disposable;
|
||||
private onDidDeleteDisposable: vscode.Disposable;
|
||||
private onDidCreateDisposable: vscode.Disposable;
|
||||
|
|
|
@ -4,16 +4,14 @@ export const JAVA_LANGUAGE_EXTENSION_ID = 'redhat.java';
|
|||
export const JAVA_DEBUGGER_EXTENSION_ID = 'vscjava.vscode-java-debug';
|
||||
|
||||
export function isJavaLanguageSupportExtensionActivated(): boolean {
|
||||
const javaExt:
|
||||
| vscode.Extension<unknown>
|
||||
| undefined = getJavaLanguageSupportExtension();
|
||||
const javaExt: vscode.Extension<unknown> | undefined =
|
||||
getJavaLanguageSupportExtension();
|
||||
return javaExt?.isActive || false;
|
||||
}
|
||||
|
||||
export function isJavaDebuggerExtensionActivated(): boolean {
|
||||
const javaExt:
|
||||
| vscode.Extension<unknown>
|
||||
| undefined = getJavaDebuggerExtension();
|
||||
const javaExt: vscode.Extension<unknown> | undefined =
|
||||
getJavaDebuggerExtension();
|
||||
return javaExt?.isActive || false;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ export const ICON_DAEMON_STOPPED = 'close.svg';
|
|||
|
||||
export const GRADLE_CONTAINER_VIEW = 'gradleContainerView';
|
||||
export const GRADLE_TASKS_VIEW = 'gradleTasksView';
|
||||
export const GRADLE_DEFAULT_PROJECTS_VIEW = 'gradleDefaultProjectsView';
|
||||
export const GRADLE_DAEMONS_VIEW = 'gradleDaemonsView';
|
||||
export const PINNED_TASKS_VIEW = 'pinnedTasksView';
|
||||
export const RECENT_TASKS_VIEW = 'recentTasksView';
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import { GradleClient } from '../../client';
|
||||
import { isLanguageServerStarted } from '../../languageServer/languageServer';
|
||||
import { RootProject } from '../../rootProject';
|
||||
import { GradleTaskDefinition } from '../../tasks';
|
||||
import { buildTaskId, createTaskFromDefinition } from '../../tasks/taskUtil';
|
||||
import { DependencyConfigurationTreeItem } from '../gradleTasks/DependencyConfigurationTreeItem';
|
||||
import { DependencyTreeItem } from '../gradleTasks/DependencyTreeItem';
|
||||
import { HintItem } from '../gradleTasks/HintItem';
|
||||
import { ProjectDependencyTreeItem } from '../gradleTasks/ProjectDependencyTreeItem';
|
||||
import { generateDefaultTaskDefinitions } from './DefaultProjectUtils';
|
||||
import { DefaultDependencyItem, DefaultTaskDefinition } from './types';
|
||||
|
||||
export class DefaultProjectProvider {
|
||||
private static defaultTaskDefinitions: DefaultTaskDefinition[] =
|
||||
generateDefaultTaskDefinitions();
|
||||
|
||||
constructor() {
|
||||
this.defaultTasks = [];
|
||||
this.defaultDependencies = new Map();
|
||||
}
|
||||
|
||||
private defaultTasks: vscode.Task[];
|
||||
private defaultDependencies: Map<string, vscode.TreeItem[]>;
|
||||
|
||||
public refresh(): void {
|
||||
this.defaultTasks = [];
|
||||
}
|
||||
|
||||
public async getDefaultTasks(
|
||||
rootProjects: RootProject[],
|
||||
client: GradleClient
|
||||
): Promise<vscode.Task[]> {
|
||||
if (this.defaultTasks.length) {
|
||||
return this.defaultTasks;
|
||||
}
|
||||
const tasks = [];
|
||||
for (const rootProject of rootProjects) {
|
||||
for (const defaultTaskDefinition of DefaultProjectProvider.defaultTaskDefinitions) {
|
||||
tasks.push(
|
||||
createTaskFromDefinition(
|
||||
this.generateDefaultTaskDefinition(
|
||||
rootProject,
|
||||
defaultTaskDefinition.name,
|
||||
rootProject.getWorkspaceFolder().name,
|
||||
path.join(rootProject.getProjectUri().fsPath, 'build.gradle'),
|
||||
defaultTaskDefinition.description,
|
||||
defaultTaskDefinition.group
|
||||
),
|
||||
rootProject,
|
||||
client
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
this.defaultTasks = tasks;
|
||||
return tasks;
|
||||
}
|
||||
|
||||
private generateDefaultTaskDefinition(
|
||||
rootProject: RootProject,
|
||||
script: string,
|
||||
projectName: string,
|
||||
projectFile: string,
|
||||
description: string,
|
||||
group?: string
|
||||
): Required<GradleTaskDefinition> {
|
||||
return {
|
||||
type: 'gradle',
|
||||
id: buildTaskId(rootProject.getProjectUri().fsPath, script, projectName),
|
||||
script,
|
||||
description: description,
|
||||
group: (group || 'other').toLowerCase(),
|
||||
project: projectName,
|
||||
buildFile: projectFile,
|
||||
rootProject: projectName,
|
||||
projectFolder: rootProject.getProjectUri().fsPath,
|
||||
workspaceFolder: rootProject.getWorkspaceFolder().uri.fsPath,
|
||||
args: '',
|
||||
javaDebug: false,
|
||||
};
|
||||
}
|
||||
|
||||
public async getDefaultDependencyItems(
|
||||
element: ProjectDependencyTreeItem
|
||||
): Promise<vscode.TreeItem[]> {
|
||||
const configurationMap: Map<string, DependencyConfigurationTreeItem> =
|
||||
new Map();
|
||||
const buildFileUri = vscode.Uri.file(
|
||||
// Due to no project knowledge, we only get informations from project root's build.gradle
|
||||
path.join(element.projectPath, 'build.gradle')
|
||||
);
|
||||
if (!isLanguageServerStarted) {
|
||||
return [new HintItem('No dependencies')];
|
||||
}
|
||||
const dependencyItems = await vscode.commands.executeCommand<
|
||||
DefaultDependencyItem[]
|
||||
>('gradle.getDependencies', buildFileUri.toString());
|
||||
if (!dependencyItems) {
|
||||
return [new HintItem('No dependencies')];
|
||||
}
|
||||
for (const dependencyItem of dependencyItems) {
|
||||
const configuration = dependencyItem.configuration;
|
||||
if (!configurationMap.has(configuration)) {
|
||||
const configItem: DependencyConfigurationTreeItem =
|
||||
new DependencyConfigurationTreeItem(
|
||||
dependencyItem.configuration,
|
||||
vscode.TreeItemCollapsibleState.Collapsed,
|
||||
element
|
||||
);
|
||||
configurationMap.set(configuration, configItem);
|
||||
}
|
||||
const configurationItem = configurationMap.get(configuration);
|
||||
if (!configurationItem) {
|
||||
continue;
|
||||
}
|
||||
const dependencyTreeItem: DependencyTreeItem = new DependencyTreeItem(
|
||||
dependencyItem.name,
|
||||
vscode.TreeItemCollapsibleState.None,
|
||||
configurationItem
|
||||
);
|
||||
dependencyTreeItem.command = {
|
||||
title: 'Show Dependency',
|
||||
command: 'vscode.open',
|
||||
arguments: [
|
||||
buildFileUri,
|
||||
<vscode.TextDocumentShowOptions>{ selection: dependencyItem.range },
|
||||
],
|
||||
};
|
||||
configurationItem.getChildren().push(dependencyTreeItem);
|
||||
}
|
||||
this.defaultDependencies.set(
|
||||
buildFileUri.toString(),
|
||||
Array.from(configurationMap.values())
|
||||
);
|
||||
return this.defaultDependencies.get(buildFileUri.toString()) || [];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { DefaultTaskDefinition } from './types';
|
||||
|
||||
export async function setDefault(): Promise<void> {
|
||||
await vscode.commands.executeCommand(
|
||||
'setContext',
|
||||
'gradle:defaultView',
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
export async function unsetDefault(): Promise<void> {
|
||||
await vscode.commands.executeCommand(
|
||||
'setContext',
|
||||
'gradle:defaultView',
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
export function generateDefaultTaskDefinitions(): DefaultTaskDefinition[] {
|
||||
const result = [];
|
||||
result.push(
|
||||
getDefaultTaskDefinition(
|
||||
'init',
|
||||
'build setup',
|
||||
'Initializes a new Gradle build.'
|
||||
)
|
||||
);
|
||||
result.push(
|
||||
getDefaultTaskDefinition(
|
||||
'wrapper',
|
||||
'build setup',
|
||||
'Generates Gradle wrapper files.'
|
||||
)
|
||||
);
|
||||
result.push(
|
||||
getDefaultTaskDefinition(
|
||||
'buildEnvironment',
|
||||
'help',
|
||||
'Displays all buildscript dependencies declared in root project.'
|
||||
)
|
||||
);
|
||||
result.push(
|
||||
getDefaultTaskDefinition(
|
||||
'dependencies',
|
||||
'help',
|
||||
'Displays all dependencies declared in root project.'
|
||||
)
|
||||
);
|
||||
result.push(
|
||||
getDefaultTaskDefinition(
|
||||
'dependencyInsight',
|
||||
'help',
|
||||
'Displays the insight into a specific dependency in root project.'
|
||||
)
|
||||
);
|
||||
result.push(
|
||||
getDefaultTaskDefinition('help', 'help', 'Displays a help message.')
|
||||
);
|
||||
result.push(
|
||||
getDefaultTaskDefinition(
|
||||
'javaToolchains',
|
||||
'help',
|
||||
'Displays the detected java toolchains.'
|
||||
)
|
||||
);
|
||||
result.push(
|
||||
getDefaultTaskDefinition(
|
||||
'outgoingVariants',
|
||||
'help',
|
||||
'Displays the outgoing variants of root project.'
|
||||
)
|
||||
);
|
||||
result.push(
|
||||
getDefaultTaskDefinition(
|
||||
'projects',
|
||||
'help',
|
||||
'Displays the sub-projects of root project.'
|
||||
)
|
||||
);
|
||||
result.push(
|
||||
getDefaultTaskDefinition(
|
||||
'properties',
|
||||
'help',
|
||||
'Displays the properties of root project.'
|
||||
)
|
||||
);
|
||||
result.push(
|
||||
getDefaultTaskDefinition(
|
||||
'tasks',
|
||||
'help',
|
||||
'Displays the tasks runnable from root project.'
|
||||
)
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
export function getDefaultTaskDefinition(
|
||||
name: string,
|
||||
group: string,
|
||||
description: string
|
||||
): DefaultTaskDefinition {
|
||||
return {
|
||||
name: name,
|
||||
group: group,
|
||||
description: description,
|
||||
};
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import {
|
||||
GradleTasksTreeDataProvider,
|
||||
GroupTreeItem,
|
||||
ProjectTreeItem,
|
||||
} from '..';
|
||||
import { GradleClient } from '../../client';
|
||||
import { RootProjectsStore } from '../../stores';
|
||||
import { GradleTaskProvider } from '../../tasks';
|
||||
import { DefaultProjectProvider } from './DefaultProjectProvider';
|
||||
import { Icons } from '../../icons';
|
||||
import { ProjectDependencyTreeItem } from '../gradleTasks/ProjectDependencyTreeItem';
|
||||
import { ProjectTaskTreeItem } from '../gradleTasks/ProjectTaskTreeItem';
|
||||
|
||||
export class DefaultProjectsTreeDataProvider
|
||||
implements vscode.TreeDataProvider<vscode.TreeItem>
|
||||
{
|
||||
private defaultProjectProvider: DefaultProjectProvider;
|
||||
|
||||
constructor(
|
||||
private readonly gradleTaskProvider: GradleTaskProvider,
|
||||
private readonly rootProjectStore: RootProjectsStore,
|
||||
private readonly client: GradleClient,
|
||||
private readonly icons: Icons
|
||||
) {
|
||||
this.defaultProjectProvider = new DefaultProjectProvider();
|
||||
}
|
||||
|
||||
public getTreeItem(element: vscode.TreeItem): vscode.TreeItem {
|
||||
return element;
|
||||
}
|
||||
|
||||
public async getChildren(
|
||||
element?: vscode.TreeItem
|
||||
): Promise<vscode.TreeItem[]> {
|
||||
if (!element) {
|
||||
// async configuring
|
||||
void this.gradleTaskProvider.loadTasks();
|
||||
return GradleTasksTreeDataProvider.buildItemsTreeFromTasks(
|
||||
await this.defaultProjectProvider.getDefaultTasks(
|
||||
await this.rootProjectStore.getProjectRoots(),
|
||||
this.client
|
||||
),
|
||||
this.rootProjectStore,
|
||||
false,
|
||||
this.icons
|
||||
);
|
||||
} else if (element instanceof ProjectTreeItem) {
|
||||
return GradleTasksTreeDataProvider.getChildrenForProjectTreeItem(element);
|
||||
} else if (element instanceof ProjectDependencyTreeItem) {
|
||||
return this.defaultProjectProvider.getDefaultDependencyItems(element);
|
||||
} else if (element instanceof ProjectTaskTreeItem) {
|
||||
return element.getChildren() || [];
|
||||
} else if (element instanceof GroupTreeItem) {
|
||||
return element.tasks;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { Range } from 'vscode-languageclient/node';
|
||||
|
||||
export interface DefaultDependencyItem {
|
||||
name: string;
|
||||
configuration: string;
|
||||
range: Range;
|
||||
}
|
||||
|
||||
export interface DefaultTaskDefinition {
|
||||
name: string;
|
||||
group: string;
|
||||
description: string;
|
||||
}
|
|
@ -12,13 +12,15 @@ import { HintItem } from '../gradleTasks/HintItem';
|
|||
import { getSpecificVersionStatus } from './util';
|
||||
|
||||
export class GradleDaemonsTreeDataProvider
|
||||
implements vscode.TreeDataProvider<vscode.TreeItem> {
|
||||
implements vscode.TreeDataProvider<vscode.TreeItem>
|
||||
{
|
||||
private cancelDeferred?: Deferred<vscode.TreeItem[]>;
|
||||
private treeItems: vscode.TreeItem[] = [];
|
||||
private specificVersion = false;
|
||||
private readonly _onDidChangeTreeData: vscode.EventEmitter<vscode.TreeItem | null> = new vscode.EventEmitter<vscode.TreeItem | null>();
|
||||
public readonly onDidChangeTreeData: vscode.Event<vscode.TreeItem | null> = this
|
||||
._onDidChangeTreeData.event;
|
||||
private readonly _onDidChangeTreeData: vscode.EventEmitter<vscode.TreeItem | null> =
|
||||
new vscode.EventEmitter<vscode.TreeItem | null>();
|
||||
public readonly onDidChangeTreeData: vscode.Event<vscode.TreeItem | null> =
|
||||
this._onDidChangeTreeData.event;
|
||||
|
||||
constructor(
|
||||
private readonly context: vscode.ExtensionContext,
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
import * as vscode from 'vscode';
|
||||
|
||||
export class DependencyConfigurationTreeItem extends vscode.TreeItem {
|
||||
private children: vscode.TreeItem[] | undefined;
|
||||
private children: vscode.TreeItem[];
|
||||
constructor(
|
||||
name: string,
|
||||
collapsibleState: vscode.TreeItemCollapsibleState,
|
||||
|
@ -13,13 +13,14 @@ export class DependencyConfigurationTreeItem extends vscode.TreeItem {
|
|||
) {
|
||||
super(name, collapsibleState);
|
||||
this.iconPath = iconPath;
|
||||
this.children = [];
|
||||
}
|
||||
|
||||
public setChildren(children: vscode.TreeItem[]): void {
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
public getChildren(): vscode.TreeItem[] | undefined {
|
||||
public getChildren(): vscode.TreeItem[] {
|
||||
return this.children;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,11 +38,12 @@ function protocolItem2DependencyConfigurationTreeItem(
|
|||
): DependencyConfigurationTreeItem | undefined {
|
||||
const name = protocolItem.getName();
|
||||
const storageMap = new Map();
|
||||
const configurationItem: DependencyConfigurationTreeItem = new DependencyConfigurationTreeItem(
|
||||
name,
|
||||
vscode.TreeItemCollapsibleState.Collapsed,
|
||||
parent
|
||||
);
|
||||
const configurationItem: DependencyConfigurationTreeItem =
|
||||
new DependencyConfigurationTreeItem(
|
||||
name,
|
||||
vscode.TreeItemCollapsibleState.Collapsed,
|
||||
parent
|
||||
);
|
||||
const children = protocolItem.getChildrenList();
|
||||
const treeChildren = [];
|
||||
for (const child of children) {
|
||||
|
|
|
@ -41,12 +41,13 @@ function resetCachedTreeItems(): void {
|
|||
}
|
||||
|
||||
export class GradleTasksTreeDataProvider
|
||||
implements vscode.TreeDataProvider<vscode.TreeItem> {
|
||||
implements vscode.TreeDataProvider<vscode.TreeItem>
|
||||
{
|
||||
private collapsed = true;
|
||||
|
||||
private readonly _onDidChangeTreeData: vscode.EventEmitter<vscode.TreeItem | null> = new vscode.EventEmitter<vscode.TreeItem | null>();
|
||||
public readonly onDidChangeTreeData: vscode.Event<vscode.TreeItem | null> = this
|
||||
._onDidChangeTreeData.event;
|
||||
private readonly _onDidChangeTreeData: vscode.EventEmitter<vscode.TreeItem | null> =
|
||||
new vscode.EventEmitter<vscode.TreeItem | null>();
|
||||
public readonly onDidChangeTreeData: vscode.Event<vscode.TreeItem | null> =
|
||||
this._onDidChangeTreeData.event;
|
||||
|
||||
constructor(
|
||||
private readonly context: vscode.ExtensionContext,
|
||||
|
@ -81,7 +82,12 @@ export class GradleTasksTreeDataProvider
|
|||
const tasks = await this.gradleTaskProvider.loadTasks();
|
||||
return tasks.length === 0
|
||||
? [new NoGradleTasksTreeItem()]
|
||||
: this.buildItemsTreeFromTasks(tasks);
|
||||
: GradleTasksTreeDataProvider.buildItemsTreeFromTasks(
|
||||
tasks,
|
||||
this.rootProjectStore,
|
||||
this.collapsed,
|
||||
this.icons
|
||||
);
|
||||
}
|
||||
|
||||
public refresh(treeItem: vscode.TreeItem | null = null): void {
|
||||
|
@ -114,7 +120,7 @@ export class GradleTasksTreeDataProvider
|
|||
return element.projects;
|
||||
}
|
||||
if (element instanceof ProjectTreeItem) {
|
||||
return this.getChildrenForProjectTreeItem(element);
|
||||
return GradleTasksTreeDataProvider.getChildrenForProjectTreeItem(element);
|
||||
}
|
||||
if (element instanceof GroupTreeItem) {
|
||||
return element.tasks;
|
||||
|
@ -136,12 +142,12 @@ export class GradleTasksTreeDataProvider
|
|||
return element.getChildren() || [];
|
||||
}
|
||||
if (!element) {
|
||||
return await this.buildTreeItems();
|
||||
return this.buildTreeItems();
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
public async getChildrenForProjectTreeItem(
|
||||
public static async getChildrenForProjectTreeItem(
|
||||
element: ProjectTreeItem
|
||||
): Promise<vscode.TreeItem[]> {
|
||||
const projectTaskItem = new ProjectTaskTreeItem(
|
||||
|
@ -155,26 +161,30 @@ export class GradleTasksTreeDataProvider
|
|||
if (!resourceUri) {
|
||||
return results;
|
||||
}
|
||||
const projectDependencyTreeItem: ProjectDependencyTreeItem = new ProjectDependencyTreeItem(
|
||||
'Dependencies',
|
||||
vscode.TreeItemCollapsibleState.Collapsed,
|
||||
element,
|
||||
path.dirname(resourceUri.fsPath),
|
||||
typeof element.label === 'string' ? element.label : resourceUri.fsPath
|
||||
);
|
||||
const projectDependencyTreeItem: ProjectDependencyTreeItem =
|
||||
new ProjectDependencyTreeItem(
|
||||
'Dependencies',
|
||||
vscode.TreeItemCollapsibleState.Collapsed,
|
||||
element,
|
||||
path.dirname(resourceUri.fsPath),
|
||||
typeof element.label === 'string' ? element.label : resourceUri.fsPath
|
||||
);
|
||||
return [...results, projectDependencyTreeItem];
|
||||
}
|
||||
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
public buildItemsTreeFromTasks(
|
||||
tasks: vscode.Task[]
|
||||
public static buildItemsTreeFromTasks(
|
||||
tasks: vscode.Task[],
|
||||
rootProjectStore: RootProjectsStore,
|
||||
collapsed: boolean,
|
||||
icons: Icons
|
||||
): RootProjectTreeItem[] | NoGradleTasksTreeItem[] {
|
||||
let gradleProjectTreeItem = null;
|
||||
|
||||
tasks.forEach((task) => {
|
||||
const definition = task.definition as GradleTaskDefinition;
|
||||
if (isWorkspaceFolder(task.scope) && isGradleTask(task)) {
|
||||
const rootProject = this.rootProjectStore.get(definition.projectFolder);
|
||||
const rootProject = rootProjectStore.get(definition.projectFolder);
|
||||
if (!rootProject) {
|
||||
return;
|
||||
}
|
||||
|
@ -208,7 +218,7 @@ export class GradleTasksTreeDataProvider
|
|||
);
|
||||
let parentTreeItem: ProjectTreeItem | GroupTreeItem = projectTreeItem;
|
||||
|
||||
if (!this.collapsed) {
|
||||
if (!collapsed) {
|
||||
const groupId = definition.group + definition.project;
|
||||
let groupTreeItem = groupTreeItemMap.get(groupId);
|
||||
if (!groupTreeItem) {
|
||||
|
@ -229,7 +239,7 @@ export class GradleTasksTreeDataProvider
|
|||
taskName,
|
||||
definition.description || taskName,
|
||||
'',
|
||||
this.icons,
|
||||
icons,
|
||||
rootProject.getJavaDebug()
|
||||
);
|
||||
taskTreeItem.setContext();
|
||||
|
|
|
@ -83,10 +83,12 @@ function buildGradleProjectTreeItem(
|
|||
}
|
||||
|
||||
export class PinnedTasksTreeDataProvider
|
||||
implements vscode.TreeDataProvider<vscode.TreeItem> {
|
||||
private readonly _onDidChangeTreeData: vscode.EventEmitter<vscode.TreeItem | null> = new vscode.EventEmitter<vscode.TreeItem | null>();
|
||||
public readonly onDidChangeTreeData: vscode.Event<vscode.TreeItem | null> = this
|
||||
._onDidChangeTreeData.event;
|
||||
implements vscode.TreeDataProvider<vscode.TreeItem>
|
||||
{
|
||||
private readonly _onDidChangeTreeData: vscode.EventEmitter<vscode.TreeItem | null> =
|
||||
new vscode.EventEmitter<vscode.TreeItem | null>();
|
||||
public readonly onDidChangeTreeData: vscode.Event<vscode.TreeItem | null> =
|
||||
this._onDidChangeTreeData.event;
|
||||
|
||||
constructor(
|
||||
private readonly pinnedTasksStore: PinnedTasksStore,
|
||||
|
|
|
@ -90,10 +90,12 @@ function buildGradleProjectTreeItem(
|
|||
}
|
||||
|
||||
export class RecentTasksTreeDataProvider
|
||||
implements vscode.TreeDataProvider<vscode.TreeItem> {
|
||||
private readonly _onDidChangeTreeData: vscode.EventEmitter<vscode.TreeItem | null> = new vscode.EventEmitter<vscode.TreeItem | null>();
|
||||
public readonly onDidChangeTreeData: vscode.Event<vscode.TreeItem | null> = this
|
||||
._onDidChangeTreeData.event;
|
||||
implements vscode.TreeDataProvider<vscode.TreeItem>
|
||||
{
|
||||
private readonly _onDidChangeTreeData: vscode.EventEmitter<vscode.TreeItem | null> =
|
||||
new vscode.EventEmitter<vscode.TreeItem | null>();
|
||||
public readonly onDidChangeTreeData: vscode.Event<vscode.TreeItem | null> =
|
||||
this._onDidChangeTreeData.event;
|
||||
|
||||
constructor(
|
||||
private readonly recentTasksStore: RecentTasksStore,
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,149 @@
|
|||
THIRD-PARTY SOFTWARE NOTICES AND INFORMATION
|
||||
For vscode-gradle package
|
||||
|
||||
This extension uses Open Source components. You can find the source code of their
|
||||
open source projects along with the license information below. We acknowledge and
|
||||
are grateful to these developers for their contribution to open source.
|
||||
|
||||
1. fs-extra (https://github.com/jprichardson/node-fs-extra)
|
||||
2. get-port (https://github.com/sindresorhus/get-port)
|
||||
3. minimatch (https://github.com/isaacs/minimatch)
|
||||
4. string-argv (https://github.com/mccormicka/string-argv)
|
||||
5. tree-kill (https://github.com/pkrumins/node-tree-kill)
|
||||
6. vscode-extension-telemetry-wrapper (https://github.com/Eskibear/vscode-extension-telemetry-wrapper)
|
||||
|
||||
fs-extra NOTICES BEGIN HERE
|
||||
=============================
|
||||
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2011-2017 JP Richardson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
|
||||
(the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
|
||||
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
END OF fs-extra NOTICES AND INFORMATION
|
||||
==================================
|
||||
|
||||
get-port NOTICES BEGIN HERE
|
||||
=============================
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
END OF get-port NOTICES AND INFORMATION
|
||||
==================================
|
||||
|
||||
minimatch NOTICES BEGIN HERE
|
||||
=============================
|
||||
|
||||
The ISC License
|
||||
|
||||
Copyright (c) Isaac Z. Schlueter and Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
END OF minimatch NOTICES AND INFORMATION
|
||||
=========================================
|
||||
|
||||
string-argv NOTICES BEGIN HERE
|
||||
=============================
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright 2014 Anthony McCormick
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
END OF string-argv NOTICES AND INFORMATION
|
||||
=========================================
|
||||
|
||||
node-tree-kill NOTICES BEGIN HERE
|
||||
=============================
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Peter Krumins
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
END OF node-tree-kill NOTICES AND INFORMATION
|
||||
=========================================
|
||||
|
||||
vscode-extension-telemetry-wrapper NOTICES BEGIN HERE
|
||||
==========================================================
|
||||
|
||||
Copyright 2018 Yan Zhang
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
|
||||
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
END OF vscode-extension-telemetry-wrapper NOTICES AND INFORMATION
|
||||
==========================================================
|
|
@ -0,0 +1,40 @@
|
|||
plugins {
|
||||
id "java"
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.eclipse.lsp4j:org.eclipse.lsp4j:0.12.0"
|
||||
implementation "org.eclipse.lsp4j:org.eclipse.lsp4j.jsonrpc:0.12.0"
|
||||
implementation "org.codehaus.groovy:groovy-eclipse-batch:3.0.8-01"
|
||||
implementation "com.google.code.gson:gson:2.8.7"
|
||||
implementation "org.apache.bcel:bcel:6.5.0"
|
||||
}
|
||||
|
||||
ext.mainClass = "com.microsoft.gradle.GradleLanguageServer"
|
||||
|
||||
task copyJar(type: Copy) {
|
||||
from "build/libs/gradle-language-server.jar"
|
||||
from configurations.runtimeClasspath
|
||||
into "../extension/lib"
|
||||
}
|
||||
|
||||
tasks.build.dependsOn tasks.copyJar
|
||||
tasks.copyJar.dependsOn jar
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
attributes(
|
||||
"Main-Class": "com.microsoft.gradle.GradleLanguageServer",
|
||||
"Class-Path": configurations.runtimeClasspath.collect { it.getName() }.join(' ')
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2021 Microsoft Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Microsoft Corporation - initial API and implementation
|
||||
*******************************************************************************/
|
||||
|
||||
package com.microsoft.gradle;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.microsoft.gradle.semantictokens.TokenModifier;
|
||||
import com.microsoft.gradle.semantictokens.TokenType;
|
||||
|
||||
import org.eclipse.lsp4j.CompletionOptions;
|
||||
import org.eclipse.lsp4j.DocumentFilter;
|
||||
import org.eclipse.lsp4j.ExecuteCommandOptions;
|
||||
import org.eclipse.lsp4j.InitializeParams;
|
||||
import org.eclipse.lsp4j.InitializeResult;
|
||||
import org.eclipse.lsp4j.SaveOptions;
|
||||
import org.eclipse.lsp4j.SemanticTokensLegend;
|
||||
import org.eclipse.lsp4j.SemanticTokensServerFull;
|
||||
import org.eclipse.lsp4j.SemanticTokensWithRegistrationOptions;
|
||||
import org.eclipse.lsp4j.ServerCapabilities;
|
||||
import org.eclipse.lsp4j.TextDocumentSyncKind;
|
||||
import org.eclipse.lsp4j.TextDocumentSyncOptions;
|
||||
import org.eclipse.lsp4j.WorkspaceFolder;
|
||||
import org.eclipse.lsp4j.jsonrpc.Launcher;
|
||||
import org.eclipse.lsp4j.services.LanguageClient;
|
||||
import org.eclipse.lsp4j.services.LanguageClientAware;
|
||||
import org.eclipse.lsp4j.services.LanguageServer;
|
||||
import org.eclipse.lsp4j.services.TextDocumentService;
|
||||
import org.eclipse.lsp4j.services.WorkspaceService;
|
||||
|
||||
public class GradleLanguageServer implements LanguageServer, LanguageClientAware {
|
||||
|
||||
private GradleServices gradleServices;
|
||||
|
||||
public static void main(String[] args) {
|
||||
GradleLanguageServer server = new GradleLanguageServer();
|
||||
try {
|
||||
Launcher<LanguageClient> launcher;
|
||||
String port = System.getenv("VSCODE_GRADLE_PORT");
|
||||
if (port == null) {
|
||||
// Launch Mode
|
||||
launcher = Launcher.createLauncher(server, LanguageClient.class, System.in, System.out);
|
||||
} else {
|
||||
// Debug Mode
|
||||
Socket socket = new Socket("localhost", Integer.parseInt(port));
|
||||
launcher = Launcher.createLauncher(server, LanguageClient.class, socket.getInputStream(),
|
||||
socket.getOutputStream());
|
||||
}
|
||||
server.connect(launcher.getRemoteProxy());
|
||||
launcher.startListening();
|
||||
} catch (IOException e) {
|
||||
server.exit();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public GradleLanguageServer() {
|
||||
this.gradleServices = new GradleServices();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<InitializeResult> initialize(InitializeParams params) {
|
||||
Map<?, ?> initOptions = new Gson().fromJson((JsonElement) params.getInitializationOptions(), Map.class);
|
||||
// TODO: support multiple workspace folders
|
||||
List<WorkspaceFolder> workspaceFolders = params.getWorkspaceFolders();
|
||||
for (WorkspaceFolder folder : workspaceFolders) {
|
||||
URI uri = URI.create(folder.getUri());
|
||||
this.gradleServices.getLibraryResolver().setWorkspacePath(Paths.get(uri));
|
||||
break;
|
||||
}
|
||||
Object settings = initOptions.get("settings");
|
||||
this.gradleServices.applySetting(settings);
|
||||
ServerCapabilities serverCapabilities = new ServerCapabilities();
|
||||
SemanticTokensWithRegistrationOptions semanticOptions = new SemanticTokensWithRegistrationOptions();
|
||||
semanticOptions.setFull(new SemanticTokensServerFull(false));
|
||||
semanticOptions.setRange(false);
|
||||
semanticOptions.setDocumentSelector(List.of(new DocumentFilter("gradle", "file", null)));
|
||||
semanticOptions.setLegend(new SemanticTokensLegend(
|
||||
Arrays.stream(TokenType.values()).map(TokenType::toString).collect(Collectors.toList()),
|
||||
Arrays.stream(TokenModifier.values()).map(TokenModifier::toString).collect(Collectors.toList())));
|
||||
serverCapabilities.setSemanticTokensProvider(semanticOptions);
|
||||
serverCapabilities.setDocumentSymbolProvider(true);
|
||||
TextDocumentSyncOptions textDocumentSyncOptions = new TextDocumentSyncOptions();
|
||||
textDocumentSyncOptions.setOpenClose(Boolean.TRUE);
|
||||
textDocumentSyncOptions.setSave(new SaveOptions(Boolean.TRUE));
|
||||
textDocumentSyncOptions.setChange(TextDocumentSyncKind.Incremental);
|
||||
serverCapabilities.setTextDocumentSync(textDocumentSyncOptions);
|
||||
CompletionOptions completionOptions = new CompletionOptions(false, Arrays.asList(".", ":"));
|
||||
serverCapabilities.setCompletionProvider(completionOptions);
|
||||
serverCapabilities.setExecuteCommandProvider(new ExecuteCommandOptions(GradleServices.supportedCommands));
|
||||
InitializeResult initializeResult = new InitializeResult(serverCapabilities);
|
||||
return CompletableFuture.completedFuture(initializeResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Object> shutdown() {
|
||||
return CompletableFuture.completedFuture(new Object());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exit() {
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextDocumentService getTextDocumentService() {
|
||||
return this.gradleServices;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorkspaceService getWorkspaceService() {
|
||||
return this.gradleServices;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(LanguageClient client) {
|
||||
this.gradleServices.connect(client);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,335 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2021 Microsoft Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Microsoft Corporation - initial API and implementation
|
||||
*******************************************************************************/
|
||||
|
||||
package com.microsoft.gradle;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.io.Files;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.microsoft.gradle.compile.CompletionVisitor;
|
||||
import com.microsoft.gradle.compile.CompletionVisitor.DependencyItem;
|
||||
import com.microsoft.gradle.compile.DocumentSymbolVisitor;
|
||||
import com.microsoft.gradle.compile.GradleCompilationUnit;
|
||||
import com.microsoft.gradle.compile.SemanticTokenVisitor;
|
||||
import com.microsoft.gradle.handlers.CompletionHandler;
|
||||
import com.microsoft.gradle.handlers.DefaultDependenciesHandler;
|
||||
import com.microsoft.gradle.handlers.DefaultDependenciesHandler.DefaultDependencyItem;
|
||||
import com.microsoft.gradle.handlers.DependencyCompletionHandler;
|
||||
import com.microsoft.gradle.manager.GradleFilesManager;
|
||||
import com.microsoft.gradle.resolver.GradleLibraryResolver;
|
||||
import com.microsoft.gradle.semantictokens.SemanticToken;
|
||||
import com.microsoft.gradle.utils.LSPUtils;
|
||||
|
||||
import org.codehaus.groovy.ast.expr.Expression;
|
||||
import org.codehaus.groovy.ast.expr.MethodCallExpression;
|
||||
import org.codehaus.groovy.ast.stmt.Statement;
|
||||
import org.codehaus.groovy.control.CompilationFailedException;
|
||||
import org.codehaus.groovy.control.ErrorCollector;
|
||||
import org.codehaus.groovy.control.Phases;
|
||||
import org.codehaus.groovy.control.messages.Message;
|
||||
import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
|
||||
import org.codehaus.groovy.syntax.SyntaxException;
|
||||
import org.eclipse.lsp4j.CompletionItem;
|
||||
import org.eclipse.lsp4j.CompletionList;
|
||||
import org.eclipse.lsp4j.CompletionParams;
|
||||
import org.eclipse.lsp4j.Diagnostic;
|
||||
import org.eclipse.lsp4j.DiagnosticSeverity;
|
||||
import org.eclipse.lsp4j.DidChangeConfigurationParams;
|
||||
import org.eclipse.lsp4j.DidChangeTextDocumentParams;
|
||||
import org.eclipse.lsp4j.DidChangeWatchedFilesParams;
|
||||
import org.eclipse.lsp4j.DidCloseTextDocumentParams;
|
||||
import org.eclipse.lsp4j.DidOpenTextDocumentParams;
|
||||
import org.eclipse.lsp4j.DidSaveTextDocumentParams;
|
||||
import org.eclipse.lsp4j.DocumentSymbol;
|
||||
import org.eclipse.lsp4j.DocumentSymbolParams;
|
||||
import org.eclipse.lsp4j.ExecuteCommandParams;
|
||||
import org.eclipse.lsp4j.Position;
|
||||
import org.eclipse.lsp4j.PublishDiagnosticsParams;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.SemanticTokens;
|
||||
import org.eclipse.lsp4j.SemanticTokensParams;
|
||||
import org.eclipse.lsp4j.SymbolInformation;
|
||||
import org.eclipse.lsp4j.TextDocumentContentChangeEvent;
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.eclipse.lsp4j.TextDocumentItem;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.eclipse.lsp4j.services.LanguageClient;
|
||||
import org.eclipse.lsp4j.services.LanguageClientAware;
|
||||
import org.eclipse.lsp4j.services.TextDocumentService;
|
||||
import org.eclipse.lsp4j.services.WorkspaceService;
|
||||
import org.eclipse.lsp4j.util.Ranges;
|
||||
|
||||
public class GradleServices implements TextDocumentService, WorkspaceService, LanguageClientAware {
|
||||
|
||||
public static final List<String> supportedCommands = List.of("gradle.getDependencies");
|
||||
|
||||
private LanguageClient client;
|
||||
private GradleFilesManager gradleFilesManager;
|
||||
private SemanticTokenVisitor semanticTokenVisitor;
|
||||
private DocumentSymbolVisitor documentSymbolVisitor;
|
||||
private CompletionVisitor completionVisitor;
|
||||
private GradleLibraryResolver libraryResolver;
|
||||
private DefaultDependenciesHandler defaultDependenciesHandler;
|
||||
|
||||
public GradleServices() {
|
||||
this.gradleFilesManager = new GradleFilesManager();
|
||||
this.semanticTokenVisitor = new SemanticTokenVisitor();
|
||||
this.documentSymbolVisitor = new DocumentSymbolVisitor();
|
||||
this.completionVisitor = new CompletionVisitor();
|
||||
this.libraryResolver = new GradleLibraryResolver();
|
||||
this.defaultDependenciesHandler = new DefaultDependenciesHandler();
|
||||
}
|
||||
|
||||
public GradleLibraryResolver getLibraryResolver() {
|
||||
return this.libraryResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(LanguageClient client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didOpen(DidOpenTextDocumentParams params) {
|
||||
URI uri = URI.create(params.getTextDocument().getUri());
|
||||
gradleFilesManager.didOpen(uri, params.getTextDocument().getText());
|
||||
GradleCompilationUnit unit = this.gradleFilesManager.getCompilationUnit(uri, params.getTextDocument().getVersion());
|
||||
compile(uri, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didChange(DidChangeTextDocumentParams params) {
|
||||
URI uri = URI.create(params.getTextDocument().getUri());
|
||||
for (TextDocumentContentChangeEvent change : params.getContentChanges()) {
|
||||
gradleFilesManager.didChange(uri, change);
|
||||
}
|
||||
GradleCompilationUnit unit = this.gradleFilesManager.getCompilationUnit(uri, params.getTextDocument().getVersion());
|
||||
compile(uri, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didClose(DidCloseTextDocumentParams params) {
|
||||
URI uri = URI.create(params.getTextDocument().getUri());
|
||||
gradleFilesManager.didClose(uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didSave(DidSaveTextDocumentParams params) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didChangeConfiguration(DidChangeConfigurationParams params) {
|
||||
Map<?, ?> settings = new Gson().fromJson((JsonElement) params.getSettings(), Map.class);
|
||||
this.applySetting(settings);
|
||||
}
|
||||
|
||||
public void applySetting(Object settings) {
|
||||
if (settings instanceof Map) {
|
||||
this.getLibraryResolver().setGradleHome((String) ((Map<?, ?>) settings).get("gradleHome"));
|
||||
this.getLibraryResolver().setGradleVersion((String) ((Map<?, ?>) settings).get("gradleVersion"));
|
||||
this.getLibraryResolver().setGradleWrapperEnabled((Boolean) ((Map<?, ?>) settings).get("gradleWrapperEnabled"));
|
||||
this.getLibraryResolver().setGradleUserHome((String) ((Map<?, ?>) settings).get("gradleUserHome"));
|
||||
this.getLibraryResolver().resolve();
|
||||
}
|
||||
}
|
||||
|
||||
private void compile(URI uri, GradleCompilationUnit unit) {
|
||||
if (unit == null) {
|
||||
return;
|
||||
}
|
||||
Set<PublishDiagnosticsParams> diagnostics = new HashSet<>();
|
||||
try {
|
||||
unit.compile(Phases.CANONICALIZATION);
|
||||
// Send empty diagnostic if there is no error
|
||||
diagnostics.add(new PublishDiagnosticsParams(uri.toString(), Collections.emptyList()));
|
||||
} catch (CompilationFailedException e) {
|
||||
diagnostics = generateDiagnostics(unit.getErrorCollector());
|
||||
}
|
||||
for (PublishDiagnosticsParams diagnostic : diagnostics) {
|
||||
client.publishDiagnostics(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
private Set<PublishDiagnosticsParams> generateDiagnostics(ErrorCollector collector) {
|
||||
// URI, List<Diagnostic>
|
||||
Map<String, List<Diagnostic>> diagnosticsStorage = new HashMap<>();
|
||||
for (Message error : collector.getErrors()) {
|
||||
if (error instanceof SyntaxErrorMessage) {
|
||||
SyntaxException exp = ((SyntaxErrorMessage) error).getCause();
|
||||
Range range = LSPUtils.toRange(exp);
|
||||
Diagnostic diagnostic = new Diagnostic();
|
||||
diagnostic.setRange(range);
|
||||
diagnostic.setSeverity(DiagnosticSeverity.Error);
|
||||
diagnostic.setMessage(exp.getMessage());
|
||||
diagnostic.setSource("Gradle");
|
||||
if (diagnosticsStorage.containsKey(exp.getSourceLocator())) {
|
||||
diagnosticsStorage.get(exp.getSourceLocator()).add(diagnostic);
|
||||
} else {
|
||||
List<Diagnostic> diagnostics = new ArrayList<>();
|
||||
diagnostics.add(diagnostic);
|
||||
diagnosticsStorage.put(exp.getSourceLocator(), diagnostics);
|
||||
}
|
||||
}
|
||||
}
|
||||
Set<PublishDiagnosticsParams> diagnosticsParams = new HashSet<>();
|
||||
for (Map.Entry<String, List<Diagnostic>> entry : diagnosticsStorage.entrySet()) {
|
||||
diagnosticsParams.add(new PublishDiagnosticsParams(entry.getKey(), entry.getValue()));
|
||||
}
|
||||
return diagnosticsParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<SemanticTokens> semanticTokensFull(SemanticTokensParams params) {
|
||||
URI uri = URI.create(params.getTextDocument().getUri());
|
||||
GradleCompilationUnit unit = this.gradleFilesManager.getCompilationUnit(uri);
|
||||
if (unit == null) {
|
||||
return CompletableFuture.completedFuture(new SemanticTokens(Collections.emptyList()));
|
||||
}
|
||||
this.semanticTokenVisitor.visitCompilationUnit(uri, unit);
|
||||
List<SemanticToken> semanticTokens = this.semanticTokenVisitor.getSemanticTokens(uri);
|
||||
if (semanticTokens == null) {
|
||||
return CompletableFuture.completedFuture(new SemanticTokens(Collections.emptyList()));
|
||||
}
|
||||
return CompletableFuture.completedFuture(new SemanticTokens(SemanticToken.encodedTokens(semanticTokens)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<Either<SymbolInformation, DocumentSymbol>>> documentSymbol(
|
||||
DocumentSymbolParams params) {
|
||||
URI uri = URI.create(params.getTextDocument().getUri());
|
||||
GradleCompilationUnit unit = this.gradleFilesManager.getCompilationUnit(uri);
|
||||
if (unit == null) {
|
||||
return CompletableFuture.completedFuture(Collections.emptyList());
|
||||
}
|
||||
this.documentSymbolVisitor.visitCompilationUnit(uri, unit);
|
||||
List<DocumentSymbol> documentSymbols = this.documentSymbolVisitor.getDocumentSymbols(uri);
|
||||
if (documentSymbols == null) {
|
||||
return CompletableFuture.completedFuture(Collections.emptyList());
|
||||
}
|
||||
List<Either<SymbolInformation, DocumentSymbol>> result = new ArrayList<>();
|
||||
for (DocumentSymbol symbol : documentSymbols) {
|
||||
result.add(Either.forRight(symbol));
|
||||
}
|
||||
return CompletableFuture.completedFuture(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Either<List<CompletionItem>, CompletionList>> completion(CompletionParams params) {
|
||||
URI uri = URI.create(params.getTextDocument().getUri());
|
||||
GradleCompilationUnit unit = this.gradleFilesManager.getCompilationUnit(uri);
|
||||
if (unit == null) {
|
||||
return CompletableFuture.completedFuture(Either.forLeft(Collections.emptyList()));
|
||||
}
|
||||
this.completionVisitor.visitCompilationUnit(uri, unit);
|
||||
List<DependencyItem> dependencies = this.completionVisitor.getDependencies(uri);
|
||||
if (dependencies == null) {
|
||||
return CompletableFuture.completedFuture(Either.forLeft(Collections.emptyList()));
|
||||
}
|
||||
for (DependencyItem dependency : dependencies) {
|
||||
if (Ranges.containsPosition(dependency.getRange(), params.getPosition())) {
|
||||
DependencyCompletionHandler handler = new DependencyCompletionHandler();
|
||||
return CompletableFuture
|
||||
.completedFuture(Either.forLeft(handler.getDependencyCompletionItems(dependency, params.getPosition())));
|
||||
}
|
||||
}
|
||||
// should return empty if in constants
|
||||
List<Expression> constants = this.completionVisitor.getConstants(uri);
|
||||
for (Expression constant : constants) {
|
||||
Range range = LSPUtils.toRange(constant);
|
||||
if (Ranges.containsPosition(range, params.getPosition())) {
|
||||
return CompletableFuture.completedFuture(Either.forLeft(Collections.emptyList()));
|
||||
}
|
||||
}
|
||||
Set<MethodCallExpression> methodCalls = this.completionVisitor.getMethodCalls(uri);
|
||||
MethodCallExpression containingCall = null;
|
||||
for (MethodCallExpression call : methodCalls) {
|
||||
Expression expression = call.getArguments();
|
||||
Range range = LSPUtils.toRange(expression);
|
||||
if (Ranges.containsPosition(range, params.getPosition()) && (containingCall == null || Ranges
|
||||
.containsRange(LSPUtils.toRange(containingCall.getArguments()), LSPUtils.toRange(call.getArguments())))) {
|
||||
// find inner containing call
|
||||
containingCall = call;
|
||||
}
|
||||
}
|
||||
boolean javaPluginsIncluded = this.libraryResolver.isJavaPluginsIncluded(this.completionVisitor.getPlugins(uri));
|
||||
CompletionHandler handler = new CompletionHandler();
|
||||
// check again
|
||||
if (containingCall == null && isGradleRoot(uri, params.getPosition())) {
|
||||
return CompletableFuture.completedFuture(Either
|
||||
.forLeft(handler.getCompletionItems(null, Paths.get(uri).getFileName().toString(), this.libraryResolver, javaPluginsIncluded)));
|
||||
}
|
||||
return CompletableFuture.completedFuture(Either.forLeft(
|
||||
handler.getCompletionItems(containingCall, Paths.get(uri).getFileName().toString(), this.libraryResolver, javaPluginsIncluded)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Object> executeCommand(ExecuteCommandParams params) {
|
||||
String command = params.getCommand();
|
||||
if (command.equals("gradle.getDependencies")) {
|
||||
List<Object> arguments = params.getArguments();
|
||||
if (arguments.isEmpty()) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
String uriString = new Gson().fromJson((JsonElement) arguments.get(0), String.class);
|
||||
URI uri = URI.create(uriString);
|
||||
if (this.gradleFilesManager.getCompilationUnit(uri) == null) {
|
||||
try {
|
||||
Path uriPath = Paths.get(uri);
|
||||
String content = Files.asCharSource(uriPath.toFile(), Charsets.UTF_8).read();
|
||||
DidOpenTextDocumentParams openDocumentParams = new DidOpenTextDocumentParams(new TextDocumentItem(uriString, "gradle", 1, content));
|
||||
this.didOpen(openDocumentParams);
|
||||
DocumentSymbolParams documentSymbolParams = new DocumentSymbolParams(new TextDocumentIdentifier(uriString));
|
||||
this.documentSymbol(documentSymbolParams);
|
||||
} catch (IOException e) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
}
|
||||
List<DocumentSymbol> dependencies = this.documentSymbolVisitor.getDependencies(uri);
|
||||
if (dependencies == null) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
List<DefaultDependencyItem> result = defaultDependenciesHandler.getDefaultDependencies(dependencies);
|
||||
return CompletableFuture.completedFuture(result);
|
||||
}
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
private boolean isGradleRoot(URI uri, Position position) {
|
||||
List<Statement> statements = this.completionVisitor.getStatements(uri);
|
||||
for (Statement statement : statements) {
|
||||
Range range = LSPUtils.toRange(statement);
|
||||
if (Ranges.containsPosition(range, position)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,251 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2021 Microsoft Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Microsoft Corporation - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package com.microsoft.gradle.compile;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.microsoft.gradle.utils.LSPUtils;
|
||||
|
||||
import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
|
||||
import org.codehaus.groovy.ast.ModuleNode;
|
||||
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
|
||||
import org.codehaus.groovy.ast.expr.ClosureExpression;
|
||||
import org.codehaus.groovy.ast.expr.ConstantExpression;
|
||||
import org.codehaus.groovy.ast.expr.Expression;
|
||||
import org.codehaus.groovy.ast.expr.GStringExpression;
|
||||
import org.codehaus.groovy.ast.expr.MapEntryExpression;
|
||||
import org.codehaus.groovy.ast.expr.MethodCallExpression;
|
||||
import org.codehaus.groovy.ast.expr.NamedArgumentListExpression;
|
||||
import org.codehaus.groovy.ast.expr.TupleExpression;
|
||||
import org.codehaus.groovy.ast.stmt.BlockStatement;
|
||||
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
|
||||
import org.codehaus.groovy.ast.stmt.Statement;
|
||||
import org.codehaus.groovy.control.SourceUnit;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
|
||||
public class CompletionVisitor extends ClassCodeVisitorSupport {
|
||||
|
||||
public class DependencyItem {
|
||||
private String text;
|
||||
private Range range;
|
||||
|
||||
public DependencyItem(String text, Range range) {
|
||||
this.text = text;
|
||||
this.range = range;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return this.text;
|
||||
}
|
||||
|
||||
public Range getRange() {
|
||||
return this.range;
|
||||
}
|
||||
}
|
||||
|
||||
private URI currentUri;
|
||||
private Map<URI, List<DependencyItem>> dependencies = new HashMap<>();
|
||||
private Map<URI, Set<MethodCallExpression>> methodCalls = new HashMap<>();
|
||||
private Map<URI, List<Statement>> statements = new HashMap<>();
|
||||
private Map<URI, List<Expression>> constants = new HashMap<>();
|
||||
private Map<URI, Set<String>> plugins = new HashMap<>();
|
||||
|
||||
public List<DependencyItem> getDependencies(URI uri) {
|
||||
return this.dependencies.get(uri);
|
||||
}
|
||||
|
||||
public Set<MethodCallExpression> getMethodCalls(URI uri) {
|
||||
return this.methodCalls.get(uri);
|
||||
}
|
||||
|
||||
public List<Statement> getStatements(URI uri) {
|
||||
return this.statements.get(uri);
|
||||
}
|
||||
|
||||
public List<Expression> getConstants(URI uri) {
|
||||
return this.constants.get(uri);
|
||||
}
|
||||
|
||||
public Set<String> getPlugins(URI uri) {
|
||||
return this.plugins.get(uri);
|
||||
}
|
||||
|
||||
public void visitCompilationUnit(URI uri, GradleCompilationUnit compilationUnit) {
|
||||
this.currentUri = uri;
|
||||
compilationUnit.iterator().forEachRemaining(unit -> visitSourceUnit(unit));
|
||||
}
|
||||
|
||||
public void visitSourceUnit(SourceUnit unit) {
|
||||
ModuleNode moduleNode = unit.getAST();
|
||||
if (moduleNode != null) {
|
||||
this.dependencies.put(this.currentUri, new ArrayList<>());
|
||||
this.methodCalls.put(this.currentUri, new HashSet<>());
|
||||
this.statements.put(this.currentUri, new ArrayList<>());
|
||||
this.constants.put(this.currentUri, new ArrayList<>());
|
||||
this.plugins.put(this.currentUri, new HashSet<>());
|
||||
visitModule(moduleNode);
|
||||
}
|
||||
}
|
||||
|
||||
public void visitModule(ModuleNode node) {
|
||||
BlockStatement blockStatement = node.getStatementBlock();
|
||||
this.statements.put(currentUri, blockStatement.getStatements());
|
||||
node.getClasses().forEach(classNode -> {
|
||||
super.visitClass(classNode);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodCallExpression(MethodCallExpression node) {
|
||||
this.methodCalls.get(this.currentUri).add(node);
|
||||
if (node.getMethodAsString().equals("dependencies")) {
|
||||
this.dependencies.get(this.currentUri).addAll(getDependencies(node));
|
||||
} else if (node.getMethodAsString().equals("plugins")) {
|
||||
// match plugins { id: ${id} }
|
||||
List<String> plugins = getPluginFromPlugins(node);
|
||||
this.plugins.get(this.currentUri).addAll(plugins);
|
||||
} else if (node.getMethodAsString().equals("apply")) {
|
||||
// match apply plugins: '${id}'
|
||||
String plugin = getPluginFromApply(node);
|
||||
if (plugin != null) {
|
||||
this.plugins.get(this.currentUri).add(plugin);
|
||||
}
|
||||
}
|
||||
super.visitMethodCallExpression(node);
|
||||
}
|
||||
|
||||
private List<DependencyItem> getDependencies(MethodCallExpression expression) {
|
||||
Expression argument = expression.getArguments();
|
||||
if (argument instanceof ArgumentListExpression) {
|
||||
return getDependencies((ArgumentListExpression) argument);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private List<DependencyItem> getDependencies(ArgumentListExpression argumentListExpression) {
|
||||
List<Expression> expressions = argumentListExpression.getExpressions();
|
||||
List<DependencyItem> symbols = new ArrayList<>();
|
||||
for (Expression expression : expressions) {
|
||||
if (expression instanceof ClosureExpression) {
|
||||
symbols.addAll(getDependencies((ClosureExpression) expression));
|
||||
} else if (expression instanceof GStringExpression || expression instanceof ConstantExpression) {
|
||||
// GStringExp: implementation "org.gradle:gradle-tooling-api:${gradleToolingApi}"
|
||||
// ConstantExp: implementation "org.gradle:gradle-tooling-api:6.8.0"
|
||||
symbols.add(new DependencyItem(expression.getText(), LSPUtils.toDependencyRange(expression)));
|
||||
} else if (expression instanceof MethodCallExpression) {
|
||||
symbols.addAll(getDependencies((MethodCallExpression) expression));
|
||||
}
|
||||
}
|
||||
return symbols;
|
||||
}
|
||||
|
||||
private List<DependencyItem> getDependencies(ClosureExpression expression) {
|
||||
Statement code = expression.getCode();
|
||||
if (code instanceof BlockStatement) {
|
||||
return getDependencies((BlockStatement) code);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private List<DependencyItem> getDependencies(BlockStatement blockStatement) {
|
||||
List<Statement> statements = blockStatement.getStatements();
|
||||
List<DependencyItem> results = new ArrayList<>();
|
||||
for (Statement statement : statements) {
|
||||
if (statement instanceof ExpressionStatement) {
|
||||
results.addAll(getDependencies((ExpressionStatement) statement));
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private List<DependencyItem> getDependencies(ExpressionStatement expressionStatement) {
|
||||
Expression expression = expressionStatement.getExpression();
|
||||
if (expression instanceof MethodCallExpression) {
|
||||
return getDependencies((MethodCallExpression) expression);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private String getPluginFromApply(MethodCallExpression node) {
|
||||
Expression argument = node.getArguments();
|
||||
if (argument instanceof TupleExpression) {
|
||||
List<Expression> expressions = ((TupleExpression)argument).getExpressions();
|
||||
for (Expression expression : expressions) {
|
||||
if (expression instanceof NamedArgumentListExpression) {
|
||||
List<MapEntryExpression> mapEntryExpressions = ((NamedArgumentListExpression)expression).getMapEntryExpressions();
|
||||
for (MapEntryExpression mapEntryExp: mapEntryExpressions) {
|
||||
Expression keyExpression = mapEntryExp.getKeyExpression();
|
||||
if (keyExpression instanceof ConstantExpression && keyExpression.getText().equals("plugin")) {
|
||||
return mapEntryExp.getValueExpression().getText();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<String> getPluginFromPlugins(MethodCallExpression node) {
|
||||
Expression objectExpression = node.getObjectExpression();
|
||||
if (objectExpression instanceof MethodCallExpression) {
|
||||
return getPluginFromPlugins((MethodCallExpression)objectExpression);
|
||||
}
|
||||
List<String> results = new ArrayList<>();
|
||||
Expression argument = node.getArguments();
|
||||
if (argument instanceof ArgumentListExpression) {
|
||||
List<Expression> expressions = ((ArgumentListExpression)argument).getExpressions();
|
||||
for (Expression expression : expressions) {
|
||||
if (expression instanceof ConstantExpression && node.getMethodAsString().equals("id")) {
|
||||
results.add(expression.getText());
|
||||
} else if (expression instanceof ClosureExpression) {
|
||||
Statement code = ((ClosureExpression)expression).getCode();
|
||||
if (code instanceof BlockStatement) {
|
||||
results.addAll(getPluginFromPlugins((BlockStatement) code));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private List<String> getPluginFromPlugins(BlockStatement code) {
|
||||
List<String> results = new ArrayList<>();
|
||||
List<Statement> statements = code.getStatements();
|
||||
for (Statement statement : statements) {
|
||||
if (statement instanceof ExpressionStatement) {
|
||||
Expression expression = ((ExpressionStatement)statement).getExpression();
|
||||
if (expression instanceof MethodCallExpression) {
|
||||
results.addAll(getPluginFromPlugins((MethodCallExpression) expression));
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitConstantExpression(ConstantExpression expression) {
|
||||
this.constants.get(currentUri).add(expression);
|
||||
super.visitConstantExpression(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitGStringExpression(GStringExpression expression) {
|
||||
this.constants.get(currentUri).add(expression);
|
||||
super.visitGStringExpression(expression);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,289 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2021 Microsoft Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Microsoft Corporation - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package com.microsoft.gradle.compile;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.microsoft.gradle.utils.LSPUtils;
|
||||
|
||||
import org.codehaus.groovy.ast.ModuleNode;
|
||||
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
|
||||
import org.codehaus.groovy.ast.expr.BinaryExpression;
|
||||
import org.codehaus.groovy.ast.expr.ClosureExpression;
|
||||
import org.codehaus.groovy.ast.expr.ConstantExpression;
|
||||
import org.codehaus.groovy.ast.expr.Expression;
|
||||
import org.codehaus.groovy.ast.expr.GStringExpression;
|
||||
import org.codehaus.groovy.ast.expr.MapEntryExpression;
|
||||
import org.codehaus.groovy.ast.expr.MethodCallExpression;
|
||||
import org.codehaus.groovy.ast.expr.NamedArgumentListExpression;
|
||||
import org.codehaus.groovy.ast.expr.PropertyExpression;
|
||||
import org.codehaus.groovy.ast.expr.TupleExpression;
|
||||
import org.codehaus.groovy.ast.expr.VariableExpression;
|
||||
import org.codehaus.groovy.ast.stmt.BlockStatement;
|
||||
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
|
||||
import org.codehaus.groovy.ast.stmt.Statement;
|
||||
import org.codehaus.groovy.control.SourceUnit;
|
||||
import org.eclipse.lsp4j.DocumentSymbol;
|
||||
import org.eclipse.lsp4j.SymbolKind;
|
||||
|
||||
public class DocumentSymbolVisitor {
|
||||
|
||||
private URI currentUri;
|
||||
private Map<URI, List<DocumentSymbol>> documentSymbols = new HashMap<>();
|
||||
private Map<URI, List<DocumentSymbol>> dependencies = new HashMap<>();
|
||||
|
||||
public List<DocumentSymbol> getDocumentSymbols(URI uri) {
|
||||
return this.documentSymbols.get(uri);
|
||||
}
|
||||
|
||||
public List<DocumentSymbol> getDependencies(URI uri) {
|
||||
return this.dependencies.get(uri);
|
||||
}
|
||||
|
||||
public void visitCompilationUnit(URI uri, GradleCompilationUnit compilationUnit) {
|
||||
this.currentUri = uri;
|
||||
compilationUnit.iterator().forEachRemaining(unit -> visitSourceUnit(uri, unit));
|
||||
}
|
||||
|
||||
public void visitSourceUnit(URI uri, SourceUnit unit) {
|
||||
ModuleNode moduleNode = unit.getAST();
|
||||
if (moduleNode != null) {
|
||||
this.documentSymbols.put(uri, new ArrayList<>());
|
||||
this.dependencies.put(uri, new ArrayList<>());
|
||||
visitModule(moduleNode);
|
||||
}
|
||||
}
|
||||
|
||||
public void visitModule(ModuleNode node) {
|
||||
BlockStatement blockStatement = node.getStatementBlock();
|
||||
List<Statement> statements = blockStatement.getStatements();
|
||||
for (Statement statement : statements) {
|
||||
if (statement instanceof ExpressionStatement) {
|
||||
DocumentSymbol symbol = getDocumentSymbol((ExpressionStatement) statement);
|
||||
if (symbol != null) {
|
||||
this.documentSymbols.get(this.currentUri).add(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public DocumentSymbol getDocumentSymbol(ExpressionStatement statement) {
|
||||
Expression expression = statement.getExpression();
|
||||
DocumentSymbol symbol = null;
|
||||
if (expression instanceof MethodCallExpression) {
|
||||
symbol = getDocumentSymbol((MethodCallExpression) expression);
|
||||
} else if (expression instanceof BinaryExpression) {
|
||||
symbol = getDocumentSymbol((BinaryExpression) expression);
|
||||
}
|
||||
if (symbol == null || symbol.getName() == null) {
|
||||
return null;
|
||||
}
|
||||
return symbol;
|
||||
}
|
||||
|
||||
private DocumentSymbol getDocumentSymbol(BinaryExpression expression) {
|
||||
Expression left = expression.getLeftExpression();
|
||||
Expression right = expression.getRightExpression();
|
||||
DocumentSymbol symbol = new DocumentSymbol();
|
||||
symbol.setName(left.getText());
|
||||
if (right instanceof ConstantExpression) {
|
||||
symbol.setDetail(right.getText());
|
||||
}
|
||||
symbol.setKind(SymbolKind.Property);
|
||||
symbol.setRange(LSPUtils.toRange(expression));
|
||||
symbol.setSelectionRange(LSPUtils.toRange(expression));
|
||||
return symbol;
|
||||
}
|
||||
|
||||
private DocumentSymbol getDocumentSymbol(MethodCallExpression expression) {
|
||||
DocumentSymbol symbol = new DocumentSymbol();
|
||||
symbol.setKind(SymbolKind.Function);
|
||||
String name = getSymbolName(expression);
|
||||
if (name == null) {
|
||||
return null;
|
||||
}
|
||||
symbol.setName(name);
|
||||
String detail = getSymbolDetail(expression);
|
||||
if (detail != null) {
|
||||
symbol.setDetail(detail);
|
||||
}
|
||||
symbol.setSelectionRange(LSPUtils.toRange(expression));
|
||||
symbol.setRange(LSPUtils.toRange(expression));
|
||||
if (expression.getMethodAsString().equals("dependencies")) {
|
||||
List<DocumentSymbol> dependencySymbols = getDependencies(expression);
|
||||
symbol.setChildren(dependencySymbols);
|
||||
this.dependencies.get(currentUri).addAll(dependencySymbols);
|
||||
}
|
||||
return symbol;
|
||||
}
|
||||
|
||||
private String getSymbolName(MethodCallExpression expression) {
|
||||
Expression objectExpression = expression.getObjectExpression();
|
||||
if (objectExpression instanceof VariableExpression) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
String objectText = objectExpression.getText();
|
||||
if (!objectText.equals("this")) {
|
||||
// variable "this" should be ignored
|
||||
builder.append(objectText);
|
||||
builder.append(".");
|
||||
}
|
||||
builder.append(expression.getMethodAsString());
|
||||
Expression arguments = expression.getArguments();
|
||||
if (arguments instanceof ArgumentListExpression) {
|
||||
List<Expression> expressions = ((ArgumentListExpression) arguments).getExpressions();
|
||||
for (Expression exp : expressions) {
|
||||
if (exp instanceof MethodCallExpression) {
|
||||
// for case: task taskName(Closure), we show "task taskName" in outline
|
||||
builder.append(" ");
|
||||
builder.append(getSymbolName((MethodCallExpression) exp));
|
||||
}
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
} else if (objectExpression instanceof PropertyExpression) {
|
||||
// for case: a.b.c.d("string"), we show "a.b.c.d" in outline
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(getSymbolName((PropertyExpression) objectExpression));
|
||||
builder.append(".");
|
||||
builder.append(expression.getMethodAsString());
|
||||
return builder.toString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getSymbolName(PropertyExpression expression) {
|
||||
Expression objectExpression = expression.getObjectExpression();
|
||||
Expression property = expression.getProperty();
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (objectExpression instanceof PropertyExpression) {
|
||||
builder.append(getSymbolName((PropertyExpression) objectExpression));
|
||||
} else if (objectExpression instanceof VariableExpression) {
|
||||
builder.append(objectExpression.getText());
|
||||
}
|
||||
if (property instanceof ConstantExpression) {
|
||||
builder.append(".");
|
||||
builder.append(property.getText());
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private String getSymbolDetail(MethodCallExpression expression) {
|
||||
Expression argument = expression.getArguments();
|
||||
if (argument instanceof ArgumentListExpression) {
|
||||
List<Expression> arguments = ((ArgumentListExpression) argument).getExpressions();
|
||||
if (!arguments.isEmpty() && arguments.get(0) instanceof ConstantExpression) {
|
||||
// if first arg is constantExpression, show it as detail
|
||||
return arguments.get(0).getText();
|
||||
}
|
||||
return null;
|
||||
} else if (argument instanceof TupleExpression) {
|
||||
// if argument is tupleExpression, show first argument as detail
|
||||
List<Expression> arguments = ((TupleExpression) argument).getExpressions();
|
||||
if (!arguments.isEmpty() && arguments.get(0) instanceof NamedArgumentListExpression) {
|
||||
NamedArgumentListExpression namedArgumentListExpression = (NamedArgumentListExpression) arguments.get(0);
|
||||
List<MapEntryExpression> mapEntryExpressions = namedArgumentListExpression.getMapEntryExpressions();
|
||||
if (!mapEntryExpressions.isEmpty()) {
|
||||
MapEntryExpression firstExpression = mapEntryExpressions.get(0);
|
||||
if (firstExpression.getValueExpression() instanceof ConstantExpression) {
|
||||
StringBuilder detail = new StringBuilder();
|
||||
detail.append(firstExpression.getKeyExpression().getText());
|
||||
detail.append(": ");
|
||||
detail.append(firstExpression.getValueExpression().getText());
|
||||
return detail.toString();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<DocumentSymbol> getDependencies(MethodCallExpression expression) {
|
||||
Expression argument = expression.getArguments();
|
||||
if (expression.getMethodAsString().equals("dependencies")) {
|
||||
return getDependencies((ArgumentListExpression) argument);
|
||||
}
|
||||
List<DocumentSymbol> results = new ArrayList<>();
|
||||
DocumentSymbol symbol = new DocumentSymbol();
|
||||
String name = expression.getMethodAsString();
|
||||
symbol.setName(name);
|
||||
String detail = getDetail(expression);
|
||||
if (detail != null) {
|
||||
symbol.setDetail(detail);
|
||||
}
|
||||
symbol.setKind(SymbolKind.Constant);
|
||||
symbol.setRange(LSPUtils.toRange(expression));
|
||||
symbol.setSelectionRange(LSPUtils.toRange(expression));
|
||||
results.add(symbol);
|
||||
return results;
|
||||
}
|
||||
|
||||
private List<DocumentSymbol> getDependencies(ArgumentListExpression argumentListExpression) {
|
||||
List<Expression> expressions = argumentListExpression.getExpressions();
|
||||
List<DocumentSymbol> symbols = new ArrayList<>();
|
||||
for (Expression expression : expressions) {
|
||||
if (expression instanceof ClosureExpression) {
|
||||
symbols.addAll(getDependencies((ClosureExpression) expression));
|
||||
} else if (expression instanceof MethodCallExpression) {
|
||||
symbols.addAll(getDependencies((MethodCallExpression) expression));
|
||||
}
|
||||
}
|
||||
return symbols;
|
||||
}
|
||||
|
||||
private List<DocumentSymbol> getDependencies(ClosureExpression expression) {
|
||||
Statement code = expression.getCode();
|
||||
if (code instanceof BlockStatement) {
|
||||
return getDependencies((BlockStatement) code);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private List<DocumentSymbol> getDependencies(BlockStatement blockStatement) {
|
||||
List<Statement> statements = blockStatement.getStatements();
|
||||
List<DocumentSymbol> symbols = new ArrayList<>();
|
||||
for (Statement statement : statements) {
|
||||
if (statement instanceof ExpressionStatement) {
|
||||
symbols.addAll(getDependencies((ExpressionStatement) statement));
|
||||
}
|
||||
}
|
||||
return symbols;
|
||||
}
|
||||
|
||||
private List<DocumentSymbol> getDependencies(ExpressionStatement expressionStatement) {
|
||||
Expression expression = expressionStatement.getExpression();
|
||||
List<DocumentSymbol> symbols = new ArrayList<>();
|
||||
if (expression instanceof MethodCallExpression) {
|
||||
symbols.addAll(getDependencies((MethodCallExpression) expression));
|
||||
}
|
||||
return symbols;
|
||||
}
|
||||
|
||||
private String getDetail(MethodCallExpression node) {
|
||||
Expression arguments = node.getArguments();
|
||||
if (arguments instanceof ArgumentListExpression) {
|
||||
List<Expression> expressions = ((ArgumentListExpression) arguments).getExpressions();
|
||||
for (Expression expression : expressions) {
|
||||
if (expression instanceof MethodCallExpression) {
|
||||
return getDetail((MethodCallExpression) expression);
|
||||
} else if (expression instanceof GStringExpression || expression instanceof ConstantExpression) {
|
||||
return expression.getText();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2021 Microsoft Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Microsoft Corporation - initial API and implementation
|
||||
*******************************************************************************/
|
||||
|
||||
package com.microsoft.gradle.compile;
|
||||
|
||||
import java.security.CodeSource;
|
||||
|
||||
import org.codehaus.groovy.control.CompilationUnit;
|
||||
import org.codehaus.groovy.control.CompilerConfiguration;
|
||||
|
||||
import groovy.lang.GroovyClassLoader;
|
||||
|
||||
public class GradleCompilationUnit extends CompilationUnit {
|
||||
private Integer version;
|
||||
|
||||
public GradleCompilationUnit(CompilerConfiguration configuration, CodeSource codeSource, GroovyClassLoader loader, Integer version) {
|
||||
super(configuration, codeSource, loader);
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public Integer getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2021 Microsoft Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Microsoft Corporation - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package com.microsoft.gradle.compile;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.microsoft.gradle.semantictokens.SemanticToken;
|
||||
import com.microsoft.gradle.semantictokens.TokenModifier;
|
||||
import com.microsoft.gradle.semantictokens.TokenType;
|
||||
|
||||
import org.codehaus.groovy.ast.ASTNode;
|
||||
import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
|
||||
import org.codehaus.groovy.ast.ModuleNode;
|
||||
import org.codehaus.groovy.ast.expr.MapEntryExpression;
|
||||
import org.codehaus.groovy.ast.expr.MethodCallExpression;
|
||||
import org.codehaus.groovy.ast.expr.PropertyExpression;
|
||||
import org.codehaus.groovy.ast.expr.VariableExpression;
|
||||
import org.codehaus.groovy.control.SourceUnit;
|
||||
|
||||
public class SemanticTokenVisitor extends ClassCodeVisitorSupport {
|
||||
|
||||
private URI currentUri;
|
||||
private Map<URI, List<SemanticToken>> tokens = new HashMap<>();
|
||||
|
||||
public List<SemanticToken> getSemanticTokens(URI uri) {
|
||||
return this.tokens.get(uri);
|
||||
}
|
||||
|
||||
private void addToken(int line, int column, int length, TokenType tokenType, int modifiers) {
|
||||
if (length > 0) {
|
||||
tokens.get(currentUri).add(new SemanticToken(line, column, length, tokenType, modifiers));
|
||||
}
|
||||
}
|
||||
|
||||
private void addToken(ASTNode node, TokenType tokenType, int modifiers) {
|
||||
addToken(node.getLineNumber(), node.getColumnNumber(), node.getLength(), tokenType, modifiers);
|
||||
}
|
||||
|
||||
private void addToken(ASTNode node, TokenType tokenType) {
|
||||
addToken(node.getLineNumber(), node.getColumnNumber(), node.getLength(), tokenType, 0);
|
||||
}
|
||||
|
||||
public void visitCompilationUnit(URI uri, GradleCompilationUnit compilationUnit) {
|
||||
this.currentUri = uri;
|
||||
compilationUnit.iterator().forEachRemaining(unit -> visitSourceUnit(uri, unit));
|
||||
}
|
||||
|
||||
public void visitSourceUnit(URI uri, SourceUnit unit) {
|
||||
ModuleNode moduleNode = unit.getAST();
|
||||
if (moduleNode != null) {
|
||||
this.tokens.put(uri, new ArrayList<>());
|
||||
visitModule(moduleNode);
|
||||
}
|
||||
}
|
||||
|
||||
public void visitModule(ModuleNode node) {
|
||||
node.getClasses().forEach(classNode -> {
|
||||
super.visitClass(classNode);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodCallExpression(MethodCallExpression node) {
|
||||
if (TokenModifier.isDefaultLibrary(node.getMethod().getText())) {
|
||||
addToken(node.getMethod(), TokenType.FUNCTION, TokenModifier.DEFAULT_LIBRARY.bitmask);
|
||||
} else {
|
||||
addToken(node.getMethod(), TokenType.FUNCTION);
|
||||
}
|
||||
super.visitMethodCallExpression(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMapEntryExpression(MapEntryExpression node) {
|
||||
addToken(node.getKeyExpression(), TokenType.PARAMETER);
|
||||
super.visitMapEntryExpression(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitVariableExpression(VariableExpression node) {
|
||||
addToken(node, TokenType.VARIABLE);
|
||||
super.visitVariableExpression(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPropertyExpression(PropertyExpression node) {
|
||||
addToken(node.getProperty(), TokenType.PROPERTY);
|
||||
super.visitPropertyExpression(node);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2021 Microsoft Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Microsoft Corporation - initial API and implementation
|
||||
*******************************************************************************/
|
||||
|
||||
package com.microsoft.gradle.delegate;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class GradleDelegate {
|
||||
private static String PROJECT = "org.gradle.api.Project";
|
||||
private static String SETTINGS = "org.gradle.api.initialization.Settings";
|
||||
private static Map<String, String> delegateMap;
|
||||
|
||||
static {
|
||||
delegateMap = new HashMap<>();
|
||||
// plugins
|
||||
delegateMap.put("application", "org.gradle.api.plugins.ApplicationPluginConvention");
|
||||
delegateMap.put("base", "org.gradle.api.plugins.BasePluginConvention");
|
||||
delegateMap.put("java", "org.gradle.api.plugins.JavaPluginConvention");
|
||||
delegateMap.put("war", "org.gradle.api.plugins.WarPluginConvention");
|
||||
// basic closures
|
||||
delegateMap.put("plugins", "org.gradle.plugin.use.PluginDependenciesSpec");
|
||||
delegateMap.put("configurations", "org.gradle.api.artifacts.Configuration");
|
||||
delegateMap.put("dependencySubstitution", "org.gradle.api.artifacts.DependencySubstitutions");
|
||||
delegateMap.put("resolutionStrategy", "org.gradle.api.artifacts.ResolutionStrategy");
|
||||
delegateMap.put("artifacts", "org.gradle.api.artifacts.dsl.ArtifactHandler");
|
||||
delegateMap.put("components", "org.gradle.api.artifacts.dsl.ComponentMetadataHandler");
|
||||
delegateMap.put("modules", "org.gradle.api.artifacts.dsl.ComponentModuleMetadataHandler");
|
||||
delegateMap.put("dependencies", "org.gradle.api.artifacts.dsl.DependencyHandler");
|
||||
delegateMap.put("repositories", "org.gradle.api.artifacts.dsl.RepositoryHandler");
|
||||
delegateMap.put("publishing", "org.gradle.api.publish.PublishingExtension");
|
||||
delegateMap.put("publications", "org.gradle.api.publish.PublicationContainer");
|
||||
delegateMap.put("sourceSets", "org.gradle.api.tasks.SourceSet");
|
||||
delegateMap.put("distributions", "org.gradle.api.distribution.Distribution");
|
||||
delegateMap.put("fileTree", "org.gradle.api.file.ConfigurableFileTree");
|
||||
delegateMap.put("copySpec", "org.gradle.api.file.CopySpec");
|
||||
delegateMap.put("exec", "org.gradle.process.ExecSpec");
|
||||
delegateMap.put("files", "org.gradle.api.file.ConfigurableFileCollection");
|
||||
delegateMap.put("task", "org.gradle.api.Task");
|
||||
}
|
||||
|
||||
public static Map<String, String> getDelegateMap() {
|
||||
return delegateMap;
|
||||
}
|
||||
|
||||
public static String getDefault() {
|
||||
return PROJECT;
|
||||
}
|
||||
|
||||
public static String getSettings() {
|
||||
return SETTINGS;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2021 Microsoft Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Microsoft Corporation - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package com.microsoft.gradle.handlers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.microsoft.gradle.delegate.GradleDelegate;
|
||||
import com.microsoft.gradle.resolver.GradleLibraryResolver;
|
||||
|
||||
import org.apache.bcel.classfile.JavaClass;
|
||||
import org.apache.bcel.classfile.Method;
|
||||
import org.apache.bcel.generic.ObjectType;
|
||||
import org.apache.bcel.generic.Type;
|
||||
import org.codehaus.groovy.ast.expr.MethodCallExpression;
|
||||
import org.eclipse.lsp4j.CompletionItem;
|
||||
import org.eclipse.lsp4j.CompletionItemKind;
|
||||
import org.eclipse.lsp4j.InsertTextFormat;
|
||||
|
||||
public class CompletionHandler {
|
||||
|
||||
private static String BUILD_GRADLE = "build.gradle";
|
||||
private static String SETTING_GRADLE = "settings.gradle";
|
||||
private static String DEPENDENCYHANDLER_CLASS = "org.gradle.api.artifacts.dsl.DependencyHandler";
|
||||
|
||||
public List<CompletionItem> getCompletionItems(MethodCallExpression containingCall, String fileName, GradleLibraryResolver resolver, boolean javaPluginsIncluded) {
|
||||
String delegateClassName = null;
|
||||
if (containingCall == null) {
|
||||
if (fileName.equals(BUILD_GRADLE)) {
|
||||
delegateClassName = GradleDelegate.getDefault();
|
||||
} else if (fileName.equals(SETTING_GRADLE)) {
|
||||
delegateClassName = GradleDelegate.getSettings();
|
||||
}
|
||||
} else {
|
||||
delegateClassName = GradleDelegate.getDelegateMap().get(containingCall.getMethodAsString());
|
||||
}
|
||||
if (delegateClassName == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
JavaClass delegateClass = resolver.getGradleLibraries().get(delegateClassName);
|
||||
if (delegateClass == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return getCompletionItemsFromClass(delegateClass, resolver, javaPluginsIncluded, new HashSet<>());
|
||||
}
|
||||
|
||||
private List<CompletionItem> getCompletionItemsFromClass(JavaClass javaClass, GradleLibraryResolver resolver, boolean javaPluginsIncluded, Set<String> resultSet) {
|
||||
if (javaClass == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<CompletionItem> results = new ArrayList<>();
|
||||
for (String superInterface : javaClass.getInterfaceNames()) {
|
||||
if (resolver.getGradleLibraries().containsKey(superInterface)) {
|
||||
results.addAll(getCompletionItemsFromClass(resolver.getGradleLibraries().get(superInterface), resolver, javaPluginsIncluded, resultSet));
|
||||
}
|
||||
}
|
||||
String superClass = javaClass.getSuperclassName();
|
||||
if (resolver.getGradleLibraries().containsKey(superClass)) {
|
||||
results.addAll(getCompletionItemsFromClass(resolver.getGradleLibraries().get(superClass), resolver, javaPluginsIncluded, resultSet));
|
||||
}
|
||||
List<String> methodNames = new ArrayList<>();
|
||||
Method[] methods = javaClass.getMethods();
|
||||
for (Method method : methods) {
|
||||
StringBuilder labelBuilder = new StringBuilder();
|
||||
String methodName = method.getName();
|
||||
// When parsing a abstract class, we'll get a "<init>" method which can't be called directly,
|
||||
// So we filter it here.
|
||||
if (methodName.equals("<init>")) {
|
||||
continue;
|
||||
}
|
||||
methodNames.add(methodName);
|
||||
labelBuilder.append(methodName);
|
||||
labelBuilder.append("(");
|
||||
for (Type type : method.getArgumentTypes()) {
|
||||
if (type instanceof ObjectType) {
|
||||
String[] classNameSplits = ((ObjectType) type).getClassName().split("\\.");
|
||||
String className = classNameSplits[classNameSplits.length - 1];
|
||||
String variableName = className.substring(0, 1).toLowerCase();
|
||||
labelBuilder.append(className);
|
||||
labelBuilder.append(" ");
|
||||
labelBuilder.append(variableName);
|
||||
labelBuilder.append(",");
|
||||
}
|
||||
}
|
||||
if (labelBuilder.charAt(labelBuilder.length() - 1) == ',') {
|
||||
labelBuilder.deleteCharAt(labelBuilder.length() - 1);
|
||||
}
|
||||
labelBuilder.append(")");
|
||||
String label = labelBuilder.toString();
|
||||
CompletionItem item = new CompletionItem(label);
|
||||
item.setKind(CompletionItemKind.Function);
|
||||
item.setInsertTextFormat(InsertTextFormat.Snippet);
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(methodName);
|
||||
if (label.endsWith("(Closure c)")) {
|
||||
// for single closure, we offer curly brackets
|
||||
builder.append(" {$0}");
|
||||
} else {
|
||||
builder.append("($0)");
|
||||
}
|
||||
item.setInsertText(builder.toString());
|
||||
if (resultSet.add(label)) {
|
||||
results.add(item);
|
||||
}
|
||||
}
|
||||
for (String methodName : methodNames) {
|
||||
if (methodName.startsWith("set") && methodName.length() > 3) {
|
||||
// for cases like setVersion() and getVersion(),
|
||||
// we offer version as a property
|
||||
String getMethod = "get" + methodName.substring(3);
|
||||
if (methodNames.contains(getMethod)) {
|
||||
String property = methodName.substring(3, 4).toLowerCase() + methodName.substring(4);
|
||||
CompletionItem item = new CompletionItem(property);
|
||||
item.setKind(CompletionItemKind.Property);
|
||||
if (resultSet.add(property)) {
|
||||
results.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (javaPluginsIncluded && javaClass.getClassName().equals(DEPENDENCYHANDLER_CLASS)) {
|
||||
// for dependency {}, we offer java configurations if there is any applied java plugin
|
||||
for (String plugin : resolver.getJavaConfigurations()) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(plugin);
|
||||
builder.append("(Object... o)");
|
||||
StringBuilder insertBuilder = new StringBuilder();
|
||||
insertBuilder.append(plugin);
|
||||
insertBuilder.append("($0)");
|
||||
CompletionItem item = new CompletionItem(builder.toString());
|
||||
item.setKind(CompletionItemKind.Function);
|
||||
item.setInsertTextFormat(InsertTextFormat.Snippet);
|
||||
item.setInsertText(insertBuilder.toString());
|
||||
results.add(item);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2021 Microsoft Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License 2.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Microsoft Corporation - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package com.microsoft.gradle.handlers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.lsp4j.DocumentSymbol;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
|
||||
public class DefaultDependenciesHandler {
|
||||
public class DefaultDependencyItem {
|
||||
private String name;
|
||||
private String configuration;
|
||||
private Range range;
|
||||
|
||||
public DefaultDependencyItem(String name, String configuration, Range range) {
|
||||
this.name = name;
|
||||
this.configuration = configuration;
|
||||
this.range = range;
|
||||
}
|
||||
}
|
||||
|
||||
public List<DefaultDependencyItem> getDefaultDependencies(List<DocumentSymbol> symbols) {
|
||||
List<DefaultDependencyItem> dependencies = new ArrayList<>();
|
||||
for (DocumentSymbol symbol : symbols) {
|
||||
String configuration = symbol.getName();
|
||||
String id = symbol.getDetail();
|
||||
Range range = symbol.getRange();
|
||||
DefaultDependencyItem item = new DefaultDependencyItem(id, configuration, range);
|
||||
dependencies.add(item);
|
||||
}
|
||||
return dependencies;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2021 Microsoft Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Microsoft Corporation - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package com.microsoft.gradle.handlers;
|
||||
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.gradle.compile.CompletionVisitor.DependencyItem;
|
||||
import com.microsoft.gradle.utils.LSPUtils;
|
||||
|
||||
import org.eclipse.lsp4j.CompletionItem;
|
||||
import org.eclipse.lsp4j.CompletionItemKind;
|
||||
import org.eclipse.lsp4j.Position;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.TextEdit;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
|
||||
public class DependencyCompletionHandler {
|
||||
private DependencyItem dependency;
|
||||
private Position position;
|
||||
private static String URL_BASIC_SEARCH = "https://search.maven.org/solrsearch/select?q=";
|
||||
|
||||
private enum DependencyCompletionKind {
|
||||
ID, VERSION
|
||||
}
|
||||
|
||||
public List<CompletionItem> getDependencyCompletionItems(DependencyItem dependency, Position position) {
|
||||
this.dependency = dependency;
|
||||
this.position = position;
|
||||
String validText = LSPUtils.getStringBeforePosition(dependency.getText(), dependency.getRange(), position);
|
||||
String[] validTexts = validText.split(":", -1);
|
||||
switch (validTexts.length) {
|
||||
case 1:
|
||||
return getDependenciesForInCompleteGroup(validTexts[0]);
|
||||
case 2:
|
||||
return getDependenciesForInCompleteArtifact(validTexts[0]);
|
||||
case 3:
|
||||
return getDependenciesForVersion(validTexts[0], validTexts[1]);
|
||||
default:
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
private List<CompletionItem> getDependenciesForInCompleteGroup(String group) {
|
||||
if (group.length() < 3) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(URL_BASIC_SEARCH);
|
||||
builder.append(group);
|
||||
// limit the number of result to 50
|
||||
builder.append("&rows=50&wt=json");
|
||||
return getDependenciesFromRestAPI(builder.toString(), DependencyCompletionKind.ID);
|
||||
}
|
||||
|
||||
private List<CompletionItem> getDependenciesForInCompleteArtifact(String group) {
|
||||
if (group.length() < 3) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(URL_BASIC_SEARCH);
|
||||
builder.append("g:%22");
|
||||
builder.append(group);
|
||||
// limit the number of result to 50
|
||||
builder.append("%22&rows=50&wt=json");
|
||||
return getDependenciesFromRestAPI(builder.toString(), DependencyCompletionKind.ID);
|
||||
}
|
||||
|
||||
private List<CompletionItem> getDependenciesForVersion(String group, String artifact) {
|
||||
if (group.length() < 3 || artifact.length() < 3) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(URL_BASIC_SEARCH);
|
||||
builder.append("g:%22");
|
||||
builder.append(group);
|
||||
builder.append("%22+AND+a:%22");
|
||||
builder.append(artifact);
|
||||
// limit the number of result to 50
|
||||
builder.append("%22&core=gav&rows=50&wt=json");
|
||||
return getDependenciesFromRestAPI(builder.toString(), DependencyCompletionKind.VERSION);
|
||||
}
|
||||
|
||||
private List<CompletionItem> getDependenciesFromRestAPI(String url, DependencyCompletionKind kind) {
|
||||
try (InputStreamReader reader = new InputStreamReader(new URL(url).openStream())) {
|
||||
JsonObject jsonResult = new Gson().fromJson(reader, JsonObject.class);
|
||||
JsonObject response = jsonResult.getAsJsonObject("response");
|
||||
JsonArray docs = response.getAsJsonArray("docs");
|
||||
List<CompletionItem> completions = new ArrayList<>();
|
||||
for (int i = 0; i < docs.size(); i++) {
|
||||
JsonElement element = docs.get(i);
|
||||
if (element instanceof JsonObject) {
|
||||
CompletionItem completionItem = new CompletionItem();
|
||||
JsonElement labelContent = ((JsonObject) element).get("id");
|
||||
String label = labelContent.getAsJsonPrimitive().getAsString();
|
||||
TextEdit textEdit = new TextEdit(new Range(this.dependency.getRange().getStart(), this.position), label);
|
||||
completionItem.setTextEdit(Either.forLeft(textEdit));
|
||||
if (kind == DependencyCompletionKind.ID) {
|
||||
completionItem.setLabel(label);
|
||||
completionItem.setTextEdit(Either.forLeft(textEdit));
|
||||
completionItem.setKind(CompletionItemKind.Module);
|
||||
completionItem.setDetail("mavenCentral");
|
||||
} else {
|
||||
String version = ((JsonObject) element).get("v").getAsJsonPrimitive().getAsString();
|
||||
completionItem.setLabel(version);
|
||||
completionItem.setFilterText(label + ":" + version);
|
||||
completionItem.setKind(CompletionItemKind.Constant);
|
||||
completionItem.setDetail("version");
|
||||
}
|
||||
// Currently we have no more than 50 results, so the padding values should have at least 3 digits.
|
||||
completionItem.setSortText(String.format("%03d", i));
|
||||
completions.add(completionItem);
|
||||
}
|
||||
}
|
||||
return completions;
|
||||
} catch (Exception e) {
|
||||
// TODO
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2021 Microsoft Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Microsoft Corporation - initial API and implementation
|
||||
*******************************************************************************/
|
||||
|
||||
package com.microsoft.gradle.manager;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.microsoft.gradle.compile.GradleCompilationUnit;
|
||||
|
||||
import org.codehaus.groovy.control.CompilerConfiguration;
|
||||
import org.codehaus.groovy.control.SourceUnit;
|
||||
import org.codehaus.groovy.control.io.StringReaderSource;
|
||||
import org.eclipse.lsp4j.Position;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.TextDocumentContentChangeEvent;
|
||||
|
||||
import groovy.lang.GroovyClassLoader;
|
||||
|
||||
public class GradleFilesManager {
|
||||
private Map<URI, String> openFiles = new HashMap<>();
|
||||
private Map<URI, GradleCompilationUnit> unitStorage = new HashMap<>();
|
||||
|
||||
public void didOpen(URI uri, String content) {
|
||||
openFiles.put(uri, content);
|
||||
}
|
||||
|
||||
public void didChange(URI uri, TextDocumentContentChangeEvent change) {
|
||||
String oldText = openFiles.get(uri);
|
||||
Range range = change.getRange();
|
||||
if (range == null) {
|
||||
openFiles.put(uri, change.getText());
|
||||
} else {
|
||||
int offsetStart = getOffset(oldText, change.getRange().getStart());
|
||||
int offsetEnd = getOffset(oldText, change.getRange().getEnd());
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(oldText.substring(0, offsetStart));
|
||||
builder.append(change.getText());
|
||||
builder.append(oldText.substring(offsetEnd));
|
||||
openFiles.put(uri, builder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public void didClose(URI uri) {
|
||||
openFiles.remove(uri);
|
||||
}
|
||||
|
||||
public String getContents(URI uri) {
|
||||
if (openFiles.containsKey(uri)) {
|
||||
return openFiles.get(uri);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getOffset(String string, Position position) {
|
||||
int line = position.getLine();
|
||||
int character = position.getCharacter();
|
||||
int currentIndex = 0;
|
||||
if (line > 0) {
|
||||
BufferedReader reader = new BufferedReader(new StringReader(string));
|
||||
try {
|
||||
int readLines = 0;
|
||||
while (true) {
|
||||
char currentChar = (char) reader.read();
|
||||
if (currentChar == -1) {
|
||||
return -1;
|
||||
}
|
||||
currentIndex++;
|
||||
if (currentChar == '\n') {
|
||||
readLines++;
|
||||
if (readLines == line) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
reader.close();
|
||||
} catch (IOException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return currentIndex + character;
|
||||
}
|
||||
|
||||
public GradleCompilationUnit getCompilationUnit(URI uri, Integer version) {
|
||||
if (this.unitStorage.containsKey(uri) && this.unitStorage.get(uri).getVersion().equals(version)) {
|
||||
return this.unitStorage.get(uri);
|
||||
}
|
||||
CompilerConfiguration config = new CompilerConfiguration();
|
||||
GroovyClassLoader classLoader = new GroovyClassLoader(ClassLoader.getSystemClassLoader().getParent(), config, true);
|
||||
GradleCompilationUnit unit = new GradleCompilationUnit(config, null, classLoader, version);
|
||||
SourceUnit sourceUnit = new SourceUnit(uri.toString(),
|
||||
new StringReaderSource(getContents(uri), unit.getConfiguration()),
|
||||
unit.getConfiguration(), unit.getClassLoader(), unit.getErrorCollector());
|
||||
unit.addSource(sourceUnit);
|
||||
this.unitStorage.put(uri, unit);
|
||||
return unit;
|
||||
}
|
||||
|
||||
public GradleCompilationUnit getCompilationUnit(URI uri) {
|
||||
// if there is no version info provided, we return the newest version
|
||||
// when the previous cu exists, otherwise return null
|
||||
if (this.unitStorage.containsKey(uri)) {
|
||||
return this.unitStorage.get(uri);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,240 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2021 Microsoft Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Microsoft Corporation - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package com.microsoft.gradle.resolver;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.bcel.classfile.ClassFormatException;
|
||||
import org.apache.bcel.classfile.ClassParser;
|
||||
import org.apache.bcel.classfile.Field;
|
||||
import org.apache.bcel.classfile.JavaClass;
|
||||
|
||||
public class GradleLibraryResolver {
|
||||
|
||||
private static String JAVA_PLUGIN = "org.gradle.api.plugins.JavaPlugin";
|
||||
|
||||
private Map<String, JavaClass> gradleLibraries = new HashMap<>();
|
||||
private Set<String> javaConfigurations = new HashSet<>();
|
||||
private Set<String> javaPlugins = new HashSet<>();
|
||||
private String gradleHome;
|
||||
private String gradleVersion;
|
||||
private boolean gradleWrapperEnabled;
|
||||
private String gradleUserHome;
|
||||
private Path workspacePath;
|
||||
|
||||
public GradleLibraryResolver() {
|
||||
this.javaPlugins.addAll(Arrays.asList("java", "application", "groovy", "java-library", "war"));
|
||||
}
|
||||
|
||||
public void setGradleHome(String gradleHome) {
|
||||
this.gradleHome = gradleHome;
|
||||
}
|
||||
|
||||
public void setGradleVersion(String gradleVersion) {
|
||||
this.gradleVersion = gradleVersion;
|
||||
}
|
||||
|
||||
public void setGradleWrapperEnabled(boolean gradleWrapperEnabled) {
|
||||
this.gradleWrapperEnabled = gradleWrapperEnabled;
|
||||
}
|
||||
|
||||
public void setGradleUserHome(String gradleUserHome) {
|
||||
this.gradleUserHome = gradleUserHome;
|
||||
}
|
||||
|
||||
public void setWorkspacePath(Path workspacePath) {
|
||||
this.workspacePath = workspacePath;
|
||||
}
|
||||
|
||||
public Map<String, JavaClass> getGradleLibraries() {
|
||||
return this.gradleLibraries;
|
||||
}
|
||||
|
||||
public Set<String> getJavaConfigurations() {
|
||||
return this.javaConfigurations;
|
||||
}
|
||||
|
||||
public void resolve() {
|
||||
Path gradleUserHomePath = (this.gradleUserHome == null) ? Path.of(System.getProperty("user.home"), ".gradle")
|
||||
: Path.of(this.gradleUserHome);
|
||||
File libFile = null;
|
||||
if (this.gradleWrapperEnabled) {
|
||||
libFile = findLibWithWrapper(gradleUserHomePath);
|
||||
} else if (this.gradleVersion != null) {
|
||||
libFile = findLibWithDist(gradleUserHomePath, "gradle-" + this.gradleVersion);
|
||||
} else if (this.gradleHome != null) {
|
||||
Path libPath = Path.of(this.gradleHome).resolve("lib");
|
||||
libFile = findLibFile(libPath.toFile());
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (libFile == null || !libFile.exists()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
JarFile libJar = new JarFile(libFile);
|
||||
getGradleLibraries(libFile.toPath(), libJar);
|
||||
File pluginLibFile = findPluginLibFile(libFile.toPath().getParent().resolve(Path.of("plugins")).toFile());
|
||||
if (pluginLibFile == null) {
|
||||
return;
|
||||
}
|
||||
JarFile pluginLibJar = new JarFile(pluginLibFile);
|
||||
getGradleLibraries(pluginLibFile.toPath(), pluginLibJar);
|
||||
resolveJavaConfigurations();
|
||||
} catch (Exception e) {
|
||||
// Do Nothing
|
||||
}
|
||||
}
|
||||
|
||||
private File findLibWithWrapper(Path gradleUserHomePath) {
|
||||
if (this.workspacePath == null) {
|
||||
return null;
|
||||
}
|
||||
Path propertiesRelativePath = Path.of("gradle", "wrapper", "gradle-wrapper.properties");
|
||||
Path propertiesPath = this.workspacePath.resolve(propertiesRelativePath);
|
||||
File propertiesFile = propertiesPath.toFile();
|
||||
if (!propertiesFile.exists()) {
|
||||
return null;
|
||||
}
|
||||
try (FileInputStream stream = new FileInputStream(propertiesFile)) {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
|
||||
String content = null;
|
||||
while ((content = reader.readLine()) != null) {
|
||||
if (content.startsWith("distributionUrl")) {
|
||||
Pattern p = Pattern.compile("(gradle-(?s)(.*))-bin");
|
||||
Matcher matcher = p.matcher(content);
|
||||
if (matcher.find()) {
|
||||
String gradleDist = matcher.group(1);
|
||||
return findLibWithDist(gradleUserHomePath, gradleDist);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Do Nothing
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private File findLibWithDist(Path gradleUserHomePath, String gradleDist) {
|
||||
Path distPath = gradleUserHomePath.resolve(Path.of("wrapper", "dists"));
|
||||
File distFolder = searchInFolder(gradleDist, distPath.toFile());
|
||||
if (distFolder != null && distFolder.exists()) {
|
||||
Path libPath = distFolder.toPath().resolve("lib");
|
||||
return findLibFile(libPath.toFile());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private File searchInFolder(String gradleDist, File folder) {
|
||||
for (File file : folder.listFiles()) {
|
||||
if (file.isDirectory()) {
|
||||
if (file.getName().equals(gradleDist)) {
|
||||
return file;
|
||||
} else {
|
||||
File searchResult = searchInFolder(gradleDist, file);
|
||||
if (searchResult != null) {
|
||||
return searchResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private File findLibFile(File folder) {
|
||||
for (File file : folder.listFiles()) {
|
||||
String name = file.getName();
|
||||
if (name.startsWith("gradle-core-api") && name.endsWith(".jar")) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
// For Gradle version under 5.6, the name of library file is like gradle-core-${version}.jar
|
||||
for (File file : folder.listFiles()) {
|
||||
String name = file.getName();
|
||||
if (name.startsWith("gradle-core") && name.endsWith(".jar")) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private File findPluginLibFile(File folder) {
|
||||
for (File file : folder.listFiles()) {
|
||||
String name = file.getName();
|
||||
if (name.startsWith("gradle-plugins") && name.endsWith(".jar")) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void getGradleLibraries(Path jarPath, JarFile jarFile) {
|
||||
Enumeration<JarEntry> entries = jarFile.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
if (!entry.getName().endsWith(".class")) {
|
||||
continue;
|
||||
}
|
||||
ClassParser parser = new ClassParser(jarPath.toString(), entry.getName());
|
||||
try {
|
||||
JavaClass javaClass = parser.parse();
|
||||
String className = javaClass.getClassName();
|
||||
this.gradleLibraries.put(className, javaClass);
|
||||
} catch (IOException | ClassFormatException e) {
|
||||
// Do Nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void resolveJavaConfigurations() {
|
||||
JavaClass javaPluginClass = this.gradleLibraries.get(GradleLibraryResolver.JAVA_PLUGIN);
|
||||
if (javaPluginClass == null) {
|
||||
return;
|
||||
}
|
||||
for (Field field : javaPluginClass.getFields()) {
|
||||
if (field.getName().endsWith("CONFIGURATION_NAME")) {
|
||||
this.javaConfigurations.add(removeQuotes(field.getConstantValue().toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String removeQuotes(String original) {
|
||||
// for those fields parsed from class files, we get ""values"", so we remove the starting and ending quotes here
|
||||
if (original.length() < 3) {
|
||||
return original;
|
||||
}
|
||||
return original.substring(1, original.length() - 1);
|
||||
}
|
||||
|
||||
public boolean isJavaPluginsIncluded(Set<String> plugins) {
|
||||
for (String plugin : plugins) {
|
||||
if (this.javaPlugins.contains(plugin)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2021 Microsoft Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Microsoft Corporation - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package com.microsoft.gradle.semantictokens;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class SemanticToken {
|
||||
private final TokenType tokenType;
|
||||
private final int tokenModifiers;
|
||||
private final int line;
|
||||
private final int column;
|
||||
private final int length;
|
||||
|
||||
public SemanticToken(int line, int column, int length, TokenType tokenType, int tokenModifiers) {
|
||||
this.line = line;
|
||||
this.column = column;
|
||||
this.length = length;
|
||||
this.tokenType = tokenType;
|
||||
this.tokenModifiers = tokenModifiers;
|
||||
}
|
||||
|
||||
public TokenType getTokenType() {
|
||||
return tokenType;
|
||||
}
|
||||
|
||||
public int getTokenModifiers() {
|
||||
return tokenModifiers;
|
||||
}
|
||||
|
||||
public int getLine() {
|
||||
return line;
|
||||
}
|
||||
|
||||
public int getColumn() {
|
||||
return column;
|
||||
}
|
||||
|
||||
public int getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
// Note: similar logics as JDT.LS, but in groovy AST ranges start from 1
|
||||
public static List<Integer> encodedTokens(List<SemanticToken> tokens) {
|
||||
tokens.sort(new Comparator<SemanticToken>() {
|
||||
@Override
|
||||
public int compare(final SemanticToken a, final SemanticToken b) {
|
||||
int lineResult = Integer.valueOf(a.getLine()).compareTo(Integer.valueOf(b.getLine()));
|
||||
if (lineResult == 0) {
|
||||
return Integer.valueOf(a.getColumn()).compareTo(Integer.valueOf(b.getColumn()));
|
||||
}
|
||||
return lineResult;
|
||||
}
|
||||
});
|
||||
int numTokens = tokens.size();
|
||||
List<Integer> data = new ArrayList<>(numTokens * 5);
|
||||
int currentLine = 0;
|
||||
int currentColumn = 0;
|
||||
for (int i = 0; i < numTokens; i++) {
|
||||
SemanticToken token = tokens.get(i);
|
||||
int line = token.getLine() - 1;
|
||||
int column = token.getColumn() - 1;
|
||||
if (line < 0 || column < 0) {
|
||||
continue;
|
||||
}
|
||||
int deltaLine = line - currentLine;
|
||||
if (deltaLine != 0) {
|
||||
currentLine = line;
|
||||
currentColumn = 0;
|
||||
}
|
||||
int deltaColumn = column - currentColumn;
|
||||
currentColumn = column;
|
||||
// Disallow duplicate/conflict token (if exists)
|
||||
if (deltaLine != 0 || deltaColumn != 0 || i == 0) {
|
||||
int tokenTypeIndex = token.getTokenType().ordinal();
|
||||
int tokenModifiers = token.getTokenModifiers();
|
||||
data.add(deltaLine);
|
||||
data.add(deltaColumn);
|
||||
data.add(token.getLength());
|
||||
data.add(tokenTypeIndex);
|
||||
data.add(tokenModifiers);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2021 Microsoft Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License 2.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Microsoft Corporation - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package com.microsoft.gradle.semantictokens;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.lsp4j.SemanticTokenModifiers;
|
||||
|
||||
public enum TokenModifier {
|
||||
|
||||
DEFAULT_LIBRARY(SemanticTokenModifiers.DefaultLibrary);
|
||||
|
||||
private String genericName;
|
||||
// See https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html
|
||||
private static List<String> defaultLibrary = List.of("afterEvaluate", "allprojects", "ant", "apply", "artifacts",
|
||||
"beforeEvaluate", "buildscript", "configurations", "configure", "copy", "copySpec", "dependencies", "javaexec",
|
||||
"repositories", "subprojects", "task");
|
||||
|
||||
public final int bitmask = 1 << ordinal();
|
||||
|
||||
TokenModifier(String genericName) {
|
||||
this.genericName = genericName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return genericName;
|
||||
}
|
||||
|
||||
public static boolean isDefaultLibrary(String method) {
|
||||
if (TokenModifier.defaultLibrary.contains(method)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2021 Microsoft Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License 2.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Microsoft Corporation - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package com.microsoft.gradle.semantictokens;
|
||||
|
||||
import org.eclipse.lsp4j.SemanticTokenTypes;
|
||||
|
||||
public enum TokenType {
|
||||
FUNCTION(SemanticTokenTypes.Function), PROPERTY(SemanticTokenTypes.Property), VARIABLE(SemanticTokenTypes.Variable),
|
||||
PARAMETER(SemanticTokenTypes.Parameter);
|
||||
|
||||
private String genericName;
|
||||
|
||||
TokenType(String genericName) {
|
||||
this.genericName = genericName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return genericName;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2021 Microsoft Corporation and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Microsoft Corporation - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package com.microsoft.gradle.utils;
|
||||
|
||||
import org.codehaus.groovy.ast.expr.Expression;
|
||||
import org.codehaus.groovy.ast.stmt.Statement;
|
||||
import org.codehaus.groovy.syntax.SyntaxException;
|
||||
import org.eclipse.lsp4j.Position;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
|
||||
public class LSPUtils {
|
||||
public static Range toRange(SyntaxException exp) {
|
||||
// LSP Range start from 0, while groovy classes start from 1
|
||||
return new Range(new Position(exp.getStartLine() - 1, exp.getStartColumn() - 1),
|
||||
new Position(exp.getEndLine() - 1, exp.getEndColumn() - 1));
|
||||
}
|
||||
|
||||
public static Range toRange(Expression expression) {
|
||||
// LSP Range start from 0, while groovy expressions start from 1
|
||||
return new Range(new Position(expression.getLineNumber() - 1, expression.getColumnNumber() - 1),
|
||||
new Position(expression.getLastLineNumber() - 1, expression.getLastColumnNumber() - 1));
|
||||
}
|
||||
|
||||
public static Range toRange(Statement statement) {
|
||||
// LSP Range start from 0, while groovy expressions start from 1
|
||||
return new Range(new Position(statement.getLineNumber() - 1, statement.getColumnNumber() - 1),
|
||||
new Position(statement.getLastLineNumber() - 1, statement.getLastColumnNumber() - 1));
|
||||
}
|
||||
|
||||
public static Range toDependencyRange(Expression expression) {
|
||||
// For dependency, the string includes open/close quotes should be excluded
|
||||
return new Range(new Position(expression.getLineNumber() - 1, expression.getColumnNumber()),
|
||||
new Position(expression.getLastLineNumber() - 1, expression.getLastColumnNumber() - 2));
|
||||
}
|
||||
|
||||
public static String getStringBeforePosition(String text, Range range, Position position) {
|
||||
Position start = range.getStart();
|
||||
// Since it's used for extract dependency info, we doesn't support multiple line
|
||||
if (start.getLine() != position.getLine()) {
|
||||
return text;
|
||||
}
|
||||
return text.substring(0, position.getCharacter() - start.getCharacter());
|
||||
}
|
||||
}
|
|
@ -4,3 +4,4 @@ include('extension')
|
|||
include('npm-package')
|
||||
include('gradle-plugin-api')
|
||||
include('gradle-plugin')
|
||||
include('gradle-language-server')
|
||||
|
|
Загрузка…
Ссылка в новой задаче