Skip to content

Commit

Permalink
refactor - Merge Language Server to Gradle Server (#1525)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jiaaming authored Jul 24, 2024
1 parent bad7388 commit 8e6e7ac
Show file tree
Hide file tree
Showing 9 changed files with 308 additions and 139 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
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
29 changes: 25 additions & 4 deletions extension/src/server/GradleServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Logger } from "../logger/index";
import { NO_JAVA_EXECUTABLE, OPT_RESTART } from "../constant";
import { redHatJavaInstalled } from "../util/config";
import { BspProxy } from "../bs/BspProxy";

import { generateRandomPipeName } from "../util/generateRandomPipeName";
const SERVER_LOGLEVEL_REGEX = /^\[([A-Z]+)\](.*)$/;
const DOWNLOAD_PROGRESS_CHAR = ".";

Expand All @@ -24,18 +24,35 @@ export class GradleServer {
private ready = false;
private taskServerPort: number | undefined;
private restarting = false;

public readonly onDidStart: vscode.Event<null> = this._onDidStart.event;
public readonly onDidStop: vscode.Event<null> = this._onDidStop.event;
private process?: cp.ChildProcessWithoutNullStreams;
private languageServerPipePath: string;

constructor(
private readonly opts: ServerOptions,
private readonly context: vscode.ExtensionContext,
private readonly logger: Logger,
private bspProxy: BspProxy
) {}
) {
this.setLanguageServerPipePath();
}

private setLanguageServerPipePath(): void {
try {
this.languageServerPipePath = generateRandomPipeName();
} catch (error) {
this.languageServerPipePath = "";
this.logger.error("Failed to generate language server pipe path", error.message);
sendInfo("", {
kind: "languageServerPipePathGenerationError",
});
}
}

public getLanguageServerPipePath(): string {
return this.languageServerPipePath;
}
public async start(): Promise<void> {
this.taskServerPort = await getPort();
const cwd = this.context.asAbsolutePath("lib");
Expand All @@ -47,7 +64,11 @@ export class GradleServer {
return;
}
const startBuildServer = redHatJavaInstalled() ? "true" : "false";
const args = [`--port=${this.taskServerPort}`, `--startBuildServer=${startBuildServer}`];
const args = [
`--port=${this.taskServerPort}`,
`--startBuildServer=${startBuildServer}`,
`--languageServerPipePath=${this.languageServerPipePath}`,
];
if (startBuildServer === "true") {
const buildServerPipeName = this.bspProxy.getBuildServerPipeName();
args.push(`--pipeName=${buildServerPipeName}`, `--bundleDir=${bundleDirectory}`);
Expand Down
Loading

0 comments on commit 8e6e7ac

Please sign in to comment.