Skip to content

Commit

Permalink
Merge branch 'develop' into cs/update-ci
Browse files Browse the repository at this point in the history
  • Loading branch information
jdneo authored Jul 25, 2024
2 parents 5706264 + 2243c68 commit 450c39d
Show file tree
Hide file tree
Showing 12 changed files with 336 additions and 160 deletions.
35 changes: 0 additions & 35 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,41 +77,6 @@
"order": 3
}
},
{
"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
}
},
{
"type": "java",
"name": "Attach to Gradle Plugin",
Expand Down
7 changes: 5 additions & 2 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ This extension contains three major components:
- [A Gradle language server](./gradle-language-server) that provides language features such as code completion and diagnostics for gradle script files.
- [A Gradle project importer](./extension/jdtls.ext/com.microsoft.gradle.bs.importer) that imports Gradle projects detected by the [Gradle Build Server](https://github.com/microsoft/build-server-for-gradle) into the workspace. This importer works with the Language Support for Java extension.

# Gradle Server and Gradle Language Server
# Gradle Server

<img src="images/gradle-server-architecture.svg" />
The gradle server is a long-running Java process that include two threads: 1. Build Server 2. Task Server
The gradle server is a long-running Java process that include three threads: 1. Build Server 2. Task Server 3. Language Server

## Build Server
The Gradle Build Server communicates with the Build Client using the [Build Server Protocol](https://build-server-protocol.github.io/) through named pipes.
Expand All @@ -17,6 +17,9 @@ Due to Java's limited support for named pipes on Windows, a TypeScript layer nam

For information about the Build Server itself, visit the [Gradle Build Server](https://github.com/microsoft/build-server-for-gradle).

## Language Server
Language Server provides language features such as code completion and diagnostics for Gradle script files. It communicates with the Language Client using the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) through named pipe.

## Task Server
The task server and client using [gRPC](https://grpc.io/) as the interface between each other. It uses TypeScript (Node.js) on the client and Java on the server. A long running server provides very good performance.

Expand Down
35 changes: 18 additions & 17 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,36 @@ Start by opening an issue using one of the issue templates, or propose a change

## Running the Project

### Build Gradle Server and Gradle Language Server.
### Prerequisites
1. Install [nvm](https://github.com/nvm-sh/nvm)
2. Install [Java version >= 17](https://adoptium.net/)
3. Change directory to the root of the project
4. Select Node version: `nvm use`
5. If using an Apple M1:
3. Select Node version: `nvm use`
4. If using an Apple M1:
- Add `npm_arch=x64` to $HOME/.gradle/gradle.properties
- Add `protoc_platform=osx-x86_64` to $HOME/.gradle/gradle.properties
6. If using Windows:
5. If using Windows:
- The extension uses `[email protected]` dependency which does not work out-of-the-box in Windows (check [this issues](https://github.com/grpc/grpc-node/issues/2338) for details), so you'll need to install some aditional DLLs if the project build is failed.
- Download and start [Build Tools for Visual Studio 2022](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2022).
- Go to the **Individual Components** tab and select the following:
- `MSVC v143 - VS 2022 C++ x64/x86 build tools (latest)` (replacing `x64/x86` with your arch)
- `Windows Universal CRT SDK`
- Click `Install` to add the components.
7. Build project files: `./gradlew build`

Running the build for the first time can take a bit of time, but subsequent builds should be fast.
### Build Gradle Build Server & Gradle Project Importer
Before proceeding with the build steps for Build Task Server & Language Server, you need to build the Gradle Build Server and its client (Gradle Project Importer) first.

1. `cd extension`
2. `git clone https://github.com/microsoft/build-server-for-gradle.git `
3. Build the Importer and Build Server jars: `../gradlew buildJars`

### Build Task Server & Language Server
After building the Gradle Build Server and its client, proceed with the following steps.

1. Change directory to the root of the project

### Build Gradle Project Importer
1. Install [Java version >= 17](https://adoptium.net/)
2. `cd extension`
3. `git clone https://github.com/microsoft/build-server-for-gradle.git `
4. Build the Importer and Build Server jars: `../gradlew buildJars`
2. Build project files: `./gradlew build`

Running the build for the first time can take a bit of time, but subsequent builds should be fast.

## Debugging Gradle plugin

Expand All @@ -51,11 +57,6 @@ The extension uses a Gradle plugin (`com.microsoft.gradle.GradlePlugin`) to get
> ```
> it indicates that the connection attempt to the Gradle Server was too slow. The [GradleBuildClient](/extension/jdtls.ext/com.microsoft.gradle.bs.importer/src/com/microsoft/gradle/bs/importer/ImporterPlugin.java#L107) requires an active Gradle Server to successfully establish a connection. If you encounter this issue, please retry the connection promptly to avoid this error.
## Debugging Gradle Language Server (editing feature related)
1. Run vscode launch configuration `Debug Language Server: Launch Extension`.
2. Run vscode launch configuration `Debug Language Server: Launch Language Server`.
## Development Workflow
Open the root of the project in VS Code.
Expand Down
39 changes: 24 additions & 15 deletions extension/src/Extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { FileWatcher } from "./util/FileWatcher";
import { DependencyTreeItem } from "./views/gradleTasks/DependencyTreeItem";
import { GRADLE_DEPENDENCY_REVEAL } from "./views/gradleTasks/DependencyUtils";
import { GradleDependencyProvider } from "./dependencies/GradleDependencyProvider";
import { isLanguageServerStarted, startLanguageServer } from "./languageServer/languageServer";
import { isLanguageServerStarted, startLanguageClientAndWaitForConnection } from "./languageServer/languageServer";
import { DefaultProjectsTreeDataProvider } from "./views/defaultProject/DefaultProjectsTreeDataProvider";
import {
CompletionKinds,
Expand All @@ -42,7 +42,7 @@ import { BspProxy } from "./bs/BspProxy";

export class Extension {
private readonly bspProxy: BspProxy;
private readonly client: GradleClient;
private readonly taskServerClient: GradleClient;
private readonly server: GradleServer;
private readonly pinnedTasksStore: PinnedTasksStore;
private readonly recentTasksStore: RecentTasksStore;
Expand Down Expand Up @@ -71,7 +71,6 @@ export class Extension {
private readonly onDidTerminalOpen: vscode.Event<vscode.Terminal> = this._onDidTerminalOpen.event;
private recentTerminal: vscode.Terminal | undefined;
private readonly buildServerController: BuildServerController;

public constructor(private readonly context: vscode.ExtensionContext) {
const loggingChannel = vscode.window.createOutputChannel("Gradle for Java");
logger.setLoggingChannel(loggingChannel);
Expand All @@ -92,15 +91,15 @@ export class Extension {
const statusBarItem = vscode.window.createStatusBarItem();
this.bspProxy = new BspProxy(this.context, bspLogger);
this.server = new GradleServer({ host: "localhost" }, context, serverLogger, this.bspProxy);
this.client = new GradleClient(this.server, statusBarItem, clientLogger);
this.taskServerClient = new GradleClient(this.server, statusBarItem, clientLogger);
this.pinnedTasksStore = new PinnedTasksStore(context);
this.recentTasksStore = new RecentTasksStore();
this.taskTerminalsStore = new TaskTerminalsStore();
this.rootProjectsStore = new RootProjectsStore();
this.gradleBuildContentProvider = new GradleBuildContentProvider(this.client);
this.gradleBuildContentProvider = new GradleBuildContentProvider(this.taskServerClient);
this.gradleTaskProvider = new GradleTaskProvider(
this.rootProjectsStore,
this.client,
this.taskServerClient,
this.gradleBuildContentProvider
);
this.gradleDependencyProvider = new GradleDependencyProvider(this.gradleBuildContentProvider);
Expand All @@ -114,7 +113,7 @@ export class Extension {
this.gradleTaskProvider,
this.gradleDependencyProvider,
this.icons,
this.client
this.taskServerClient
);
this.gradleTasksTreeView = vscode.window.createTreeView(GRADLE_TASKS_VIEW, {
treeDataProvider: this.gradleTasksTreeDataProvider,
Expand All @@ -130,7 +129,7 @@ export class Extension {
this.taskTerminalsStore,
this.rootProjectsStore,
this.gradleTaskProvider,
this.client,
this.taskServerClient,
this.icons
);
this.recentTasksTreeView = vscode.window.createTreeView(RECENT_TASKS_VIEW, {
Expand All @@ -140,7 +139,7 @@ export class Extension {
this.defaultProjectsTreeDataProvider = new DefaultProjectsTreeDataProvider(
this.gradleTaskProvider,
this.rootProjectsStore,
this.client,
this.taskServerClient,
this.icons
);
this.defaultProjectsTreeView = vscode.window.createTreeView(GRADLE_DEFAULT_PROJECTS_VIEW, {
Expand All @@ -151,7 +150,12 @@ export class Extension {
this.gradleTaskManager = new GradleTaskManager(context);
this.buildFileWatcher = new FileWatcher("**/*.{gradle,gradle.kts}");
this.gradleWrapperWatcher = new FileWatcher("**/gradle/wrapper/gradle-wrapper.properties");
this.api = new Api(this.client, this.gradleTasksTreeDataProvider, this.gradleTaskProvider, this.icons);
this.api = new Api(
this.taskServerClient,
this.gradleTasksTreeDataProvider,
this.gradleTaskProvider,
this.icons
);

this.commands = new Commands(
this.context,
Expand All @@ -161,7 +165,7 @@ export class Extension {
this.gradleTasksTreeDataProvider,
this.recentTasksTreeDataProvider,
this.gradleDaemonsTreeDataProvider,
this.client,
this.taskServerClient,
this.rootProjectsStore,
this.taskTerminalsStore,
this.recentTasksStore,
Expand Down Expand Up @@ -205,16 +209,21 @@ export class Extension {
)
);

this.client.onDidConnect(() => this.refresh());
this.taskServerClient.onDidConnect(() => this.refresh());
void startLanguageClientAndWaitForConnection(
this.context,
this.gradleBuildContentProvider,
this.rootProjectsStore,
this.server.getLanguageServerPipePath()
);
void this.activate();
void startLanguageServer(this.context, this.gradleBuildContentProvider, this.rootProjectsStore);
void vscode.commands.executeCommand("setContext", "allowParallelRun", getAllowParallelRun());
void vscode.commands.executeCommand("setContext", Context.ACTIVATION_CONTEXT_KEY, true);
}

private storeSubscriptions(): void {
this.context.subscriptions.push(
this.client,
this.taskServerClient,
this.pinnedTasksStore,
this.recentTasksStore,
this.taskTerminalsStore,
Expand Down Expand Up @@ -327,7 +336,7 @@ export class Extension {
}

private async restartServer(): Promise<void> {
await this.client.cancelBuilds();
await this.taskServerClient.cancelBuilds();
await commands.executeCommand("workbench.action.restartExtensionHost");
}

Expand Down
56 changes: 8 additions & 48 deletions extension/src/languageServer/languageServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,80 +2,40 @@
// Licensed under the MIT license.

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 { GradleBuildContentProvider } from "../client/GradleBuildContentProvider";
import { GradleBuild, GradleProject } from "../proto/gradle_pb";
import { RootProjectsStore } from "../stores";
import {
checkEnvJavaExecutable,
getConfigJavaImportGradleHome,
getConfigJavaImportGradleUserHome,
getConfigJavaImportGradleVersion,
getConfigJavaImportGradleWrapperEnabled,
findValidJavaHome,
getJavaExecutablePathFromJavaHome,
} from "../util/config";
import { prepareLanguageServerParams } from "./utils";
const CHANNEL_NAME = "Gradle for Java (Language Server)";

export let isLanguageServerStarted = false;

export async function startLanguageServer(
export async function startLanguageClientAndWaitForConnection(
context: vscode.ExtensionContext,
contentProvider: GradleBuildContentProvider,
rootProjectsStore: RootProjectsStore
rootProjectsStore: RootProjectsStore,
languageServerPipePath: string
): Promise<void> {
void vscode.window.withProgress({ location: vscode.ProgressLocation.Window }, (progress) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
return new Promise<void>(async (resolve, reject) => {
return new Promise<void>(async (resolve) => {
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 = await findValidJavaHome();
let javaCommand;
if (javaHome) {
javaCommand = getJavaExecutablePathFromJavaHome(javaHome);
} else {
if (!checkEnvJavaExecutable()) {
// we have already show error message in gRPC server for no java executable found, so here we will just reject and return
return reject();
}
javaCommand = "java";
}
const args = [
...prepareLanguageServerParams(),
"-jar",
path.resolve(context.extensionPath, "lib", "gradle-language-server.jar"),
];
serverOptions = {
command: javaCommand,
args: args,
};
}
const serverOptions = () => awaitServerConnection(languageServerPipePath);
const languageClient = new LanguageClient("gradle", "Gradle Language Server", serverOptions, clientOptions);
void languageClient.onReady().then(
() => {
Expand All @@ -88,6 +48,7 @@ export async function startLanguageServer(
}
);
const disposable = languageClient.start();

context.subscriptions.push(disposable);
context.subscriptions.push(
vscode.workspace.onDidChangeConfiguration((e) => {
Expand All @@ -102,15 +63,14 @@ export async function startLanguageServer(
});
}

async function awaitServerConnection(port: string): Promise<StreamInfo> {
const addr = parseInt(port);
async function awaitServerConnection(pipeName: string): Promise<StreamInfo> {
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.listen(pipeName, () => {
server.removeListener("error", reject);
});
return server;
Expand Down
Loading

0 comments on commit 450c39d

Please sign in to comment.