|
| 1 | +'use strict'; |
| 2 | + |
| 3 | +import * as fse from "fs-extra"; |
| 4 | +import * as path from "path"; |
| 5 | +import * as vscode from "vscode"; |
| 6 | +import { ExtensionContext, window, commands, Uri, Position, Location, Selection, env } from "vscode"; |
| 7 | +import { Commands } from "./commands"; |
| 8 | +import { apiManager } from "./apiManager"; |
| 9 | +import { supportsLanguageStatus } from "./languageStatusItemFactory"; |
| 10 | +import htmlparser2 = require("htmlparser2"); |
| 11 | + |
| 12 | +export const JAVA_LOMBOK_PATH = "java.lombokPath"; |
| 13 | + |
| 14 | +const languageServerDocumentSelector = [ |
| 15 | + { scheme: 'file', language: 'java' }, |
| 16 | + { scheme: 'jdt', language: 'java' }, |
| 17 | + { scheme: 'untitled', language: 'java' }, |
| 18 | + { pattern: '**/pom.xml' }, |
| 19 | + { pattern: '**/{build,settings}.gradle'}, |
| 20 | + { pattern: '**/{build,settings}.gradle.kts'} |
| 21 | +]; |
| 22 | + |
| 23 | +let activeLombokPath: string = undefined; |
| 24 | +let isLombokCommandInitialized: boolean = false; |
| 25 | + |
| 26 | +export function isLombokSupportEnabled(): boolean { |
| 27 | + return vscode.workspace.getConfiguration().get("java.jdt.ls.lombokSupport.enabled"); |
| 28 | +} |
| 29 | + |
| 30 | +export function isLombokImported(context: ExtensionContext): boolean { |
| 31 | + return context.workspaceState.get(JAVA_LOMBOK_PATH)!==undefined; |
| 32 | +} |
| 33 | + |
| 34 | +export function updateActiveLombokPath(path: string) { |
| 35 | + activeLombokPath = path; |
| 36 | +} |
| 37 | + |
| 38 | +export function isLombokActive(context: ExtensionContext): boolean { |
| 39 | + return activeLombokPath!==undefined; |
| 40 | +} |
| 41 | + |
| 42 | +export function cleanupLombokCache(context: ExtensionContext): boolean { |
| 43 | + const result = isLombokImported(context); |
| 44 | + context.workspaceState.update(JAVA_LOMBOK_PATH, undefined); |
| 45 | + return result; |
| 46 | +} |
| 47 | + |
| 48 | +export function getLombokVersion(context: ExtensionContext): string { |
| 49 | + const reg = /lombok-.*\.jar/; |
| 50 | + const lombokVersion = reg.exec(activeLombokPath)[0].split('.jar')[0]; |
| 51 | + return lombokVersion; |
| 52 | +} |
| 53 | + |
| 54 | +export function addLombokParam(context: ExtensionContext, params: string[]) { |
| 55 | + if (isLombokImported(context)) { |
| 56 | + // Exclude user setting lombok agent parameter |
| 57 | + const reg = /-javaagent:.*[\\|/]lombok.*\.jar/; |
| 58 | + const deleteIndex = []; |
| 59 | + for (let i = 0; i<params.length; i++) { |
| 60 | + if (reg.test(params[i])) { |
| 61 | + deleteIndex.push(i); |
| 62 | + } |
| 63 | + } |
| 64 | + for (let i = deleteIndex.length - 1; i>=0; i--) { |
| 65 | + params.splice(deleteIndex[i], 1); |
| 66 | + } |
| 67 | + // add -javaagent arg to support lombok |
| 68 | + const lombokAgentParam = '-javaagent:' + context.workspaceState.get(JAVA_LOMBOK_PATH); |
| 69 | + params.push(lombokAgentParam); |
| 70 | + updateActiveLombokPath(context.workspaceState.get(JAVA_LOMBOK_PATH)); |
| 71 | + } |
| 72 | +} |
| 73 | + |
| 74 | +export async function checkLombokDependency(context: ExtensionContext) { |
| 75 | + if (!isLombokSupportEnabled()) { |
| 76 | + return; |
| 77 | + } |
| 78 | + const reg = /lombok-.*\.jar/; |
| 79 | + let needReload = false; |
| 80 | + let versionChange = false; |
| 81 | + let currentLombokVersion = ""; |
| 82 | + let previousLombokVersion = ""; |
| 83 | + let currentLombokClasspath = ""; |
| 84 | + const projectUris: string[] = await commands.executeCommand<string[]>(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.GET_ALL_JAVA_PROJECTS); |
| 85 | + for (const projectUri of projectUris) { |
| 86 | + const classpathResult = await apiManager.getApiInstance().getClasspaths(projectUri, {scope: 'runtime'}); |
| 87 | + for (const classpath of classpathResult.classpaths) { |
| 88 | + if (reg.test(classpath)) { |
| 89 | + currentLombokClasspath = classpath; |
| 90 | + if (isLombokImported(context)) { |
| 91 | + currentLombokVersion = reg.exec(classpath)[0]; |
| 92 | + previousLombokVersion = reg.exec(context.workspaceState.get(JAVA_LOMBOK_PATH))[0]; |
| 93 | + if (currentLombokVersion!==previousLombokVersion) { |
| 94 | + needReload = true; |
| 95 | + versionChange = true; |
| 96 | + context.workspaceState.update(JAVA_LOMBOK_PATH, classpath); |
| 97 | + } |
| 98 | + } |
| 99 | + else { |
| 100 | + needReload = true; |
| 101 | + context.workspaceState.update(JAVA_LOMBOK_PATH, currentLombokClasspath); |
| 102 | + } |
| 103 | + break; |
| 104 | + } |
| 105 | + } |
| 106 | + if (needReload) { |
| 107 | + break; |
| 108 | + } |
| 109 | + } |
| 110 | + if (needReload) { |
| 111 | + if (versionChange) { |
| 112 | + const msg = `Lombok version changed from ${previousLombokVersion.split('.jar')[0].split('-')[1]} to ${currentLombokVersion.split('.jar')[0].split('-')[1]} \ |
| 113 | + . Do you want to reload the window to load the new lombok version?`; |
| 114 | + const action = 'Reload Now'; |
| 115 | + const restartId = Commands.RELOAD_WINDOW; |
| 116 | + window.showInformationMessage(msg, action).then((selection) => { |
| 117 | + if (action === selection) { |
| 118 | + commands.executeCommand(restartId); |
| 119 | + } |
| 120 | + }); |
| 121 | + } |
| 122 | + else { |
| 123 | + const msg = `Lombok is detected in your project, please reload the window to enable lombok support.`; |
| 124 | + const action = 'Reload Now'; |
| 125 | + const restartId = Commands.RELOAD_WINDOW; |
| 126 | + window.showInformationMessage(msg, action).then((selection) => { |
| 127 | + if (action === selection) { |
| 128 | + commands.executeCommand(restartId); |
| 129 | + } |
| 130 | + }); |
| 131 | + } |
| 132 | + } |
| 133 | +} |
| 134 | + |
| 135 | +async function getParentBuildFilePath(buildFilePath: string, relativePath: string) { |
| 136 | + const parentBuildFilePath = path.join(path.dirname(buildFilePath), relativePath, path.basename(buildFilePath)); |
| 137 | + if (await fse.pathExists(parentBuildFilePath)) { |
| 138 | + return parentBuildFilePath; |
| 139 | + } |
| 140 | + return undefined; |
| 141 | +} |
| 142 | + |
| 143 | +export function registerLombokConfigureCommand(context: ExtensionContext) { |
| 144 | + if (isLombokCommandInitialized) { |
| 145 | + return; |
| 146 | + } |
| 147 | + context.subscriptions.push(commands.registerCommand(Commands.LOMBOK_CONFIGURE, async (buildFilePath: string) => { |
| 148 | + if (isMavenProject(buildFilePath)) { |
| 149 | + let pos = 0; |
| 150 | + while (true) { |
| 151 | + const document = await vscode.workspace.openTextDocument(Uri.file(buildFilePath)); |
| 152 | + const fullText = document.getText(); |
| 153 | + let tagList: [string, number][] = []; |
| 154 | + let hasParent: boolean = false; |
| 155 | + let getRelativePath: boolean = false; |
| 156 | + let relativePath: string = '..'; |
| 157 | + const parser = new htmlparser2.Parser({ |
| 158 | + onopentag(name, attributes) { |
| 159 | + if (name==="dependency"||tagList.length>0) { |
| 160 | + tagList.push([name, parser.startIndex]); |
| 161 | + } |
| 162 | + if (name==="parent") { |
| 163 | + hasParent = true; |
| 164 | + } |
| 165 | + if (hasParent&&name==="relativePath") { |
| 166 | + getRelativePath = true; |
| 167 | + } |
| 168 | + }, |
| 169 | + ontext(text) { |
| 170 | + if (tagList.length) { |
| 171 | + tagList.push([text, parser.startIndex]); |
| 172 | + } |
| 173 | + if (getRelativePath) { |
| 174 | + relativePath = text; |
| 175 | + } |
| 176 | + }, |
| 177 | + onclosetag(name) { |
| 178 | + if (name==="dependency") { |
| 179 | + tagList.push([name, parser.startIndex]); |
| 180 | + let lombokIndex = -1; |
| 181 | + let versionIndex = -1; |
| 182 | + for (let i = 0; i<tagList.length-1; i++) { |
| 183 | + if (tagList[i][0]==="artifactid"&&tagList[i+1][0]==="lombok") { |
| 184 | + lombokIndex = tagList[i+1][1]; |
| 185 | + } |
| 186 | + if (tagList[i][0]==="version") { |
| 187 | + versionIndex = tagList[i+1][1]; |
| 188 | + } |
| 189 | + } |
| 190 | + if (lombokIndex>=0) { |
| 191 | + pos = lombokIndex; |
| 192 | + if (versionIndex>=0) { |
| 193 | + pos = versionIndex; |
| 194 | + parser.end(); |
| 195 | + } |
| 196 | + } |
| 197 | + tagList = []; |
| 198 | + } |
| 199 | + if (getRelativePath&&name==="relativePath") { |
| 200 | + getRelativePath = false; |
| 201 | + } |
| 202 | + }, |
| 203 | + }); |
| 204 | + parser.write(fullText); |
| 205 | + parser.end(); |
| 206 | + if (pos>0) { |
| 207 | + break; |
| 208 | + } |
| 209 | + if (hasParent) { |
| 210 | + buildFilePath = await getParentBuildFilePath(buildFilePath, relativePath); |
| 211 | + if (buildFilePath===undefined) { |
| 212 | + break; |
| 213 | + } |
| 214 | + } |
| 215 | + else { |
| 216 | + break; |
| 217 | + } |
| 218 | + } |
| 219 | + if (buildFilePath) { |
| 220 | + await commands.executeCommand(Commands.OPEN_BROWSER, Uri.file(buildFilePath)); |
| 221 | + if (pos>0) { |
| 222 | + gotoLombokLocation(pos, buildFilePath); |
| 223 | + } |
| 224 | + } |
| 225 | + } |
| 226 | + else if (isGradleProject(buildFilePath)) { |
| 227 | + while (true) { |
| 228 | + const document = await vscode.workspace.openTextDocument(Uri.file(buildFilePath)); |
| 229 | + const fullText = document.getText(); |
| 230 | + const deleteCommentReg = /\/\/.*|(\/\*[\s\S]*?\*\/)/g; |
| 231 | + const content = fullText.replace(deleteCommentReg, (match) => { |
| 232 | + let newString = ''; |
| 233 | + for (const letter of match) { |
| 234 | + newString += '@'; |
| 235 | + } |
| 236 | + return newString; |
| 237 | + }); |
| 238 | + const lombokReg = /org.projectlombok/; |
| 239 | + const result = lombokReg.exec(content); |
| 240 | + if (result) { |
| 241 | + const pos = result.index; |
| 242 | + await commands.executeCommand(Commands.OPEN_BROWSER, Uri.file(buildFilePath)); |
| 243 | + gotoLombokLocation(pos, buildFilePath); |
| 244 | + break; |
| 245 | + } |
| 246 | + else { |
| 247 | + const currentBuildFilePath = buildFilePath; |
| 248 | + buildFilePath = await getParentBuildFilePath(buildFilePath, ".."); |
| 249 | + if (buildFilePath===undefined) { |
| 250 | + await commands.executeCommand(Commands.OPEN_BROWSER, Uri.file(currentBuildFilePath)); |
| 251 | + break; |
| 252 | + } |
| 253 | + } |
| 254 | + } |
| 255 | + } |
| 256 | + })); |
| 257 | + isLombokCommandInitialized = true; |
| 258 | +} |
| 259 | + |
| 260 | +export namespace LombokVersionItemFactory { |
| 261 | + export function create(text: string, buildFilePath: string): any { |
| 262 | + if (supportsLanguageStatus()) { |
| 263 | + const item = vscode.languages.createLanguageStatusItem("javaLombokVersionItem", languageServerDocumentSelector); |
| 264 | + item.severity = vscode.LanguageStatusSeverity?.Information; |
| 265 | + item.name = "Lombok Version"; |
| 266 | + item.text = text; |
| 267 | + if (buildFilePath) { |
| 268 | + item.command = getLombokChangeCommand(buildFilePath); |
| 269 | + } |
| 270 | + return item; |
| 271 | + } |
| 272 | + return undefined; |
| 273 | + } |
| 274 | + |
| 275 | + export function update(item: any, text: string, buildFilePath: string): void { |
| 276 | + item.text = text; |
| 277 | + if (buildFilePath) { |
| 278 | + item.command = getLombokChangeCommand(buildFilePath); |
| 279 | + } |
| 280 | + } |
| 281 | + |
| 282 | + function getLombokChangeCommand(buildFilePath: string): vscode.Command { |
| 283 | + return { |
| 284 | + title: `Configure Version`, |
| 285 | + command: Commands.LOMBOK_CONFIGURE, |
| 286 | + arguments: [buildFilePath], |
| 287 | + tooltip: `Configure Lombok Version` |
| 288 | + }; |
| 289 | + } |
| 290 | +} |
| 291 | + |
| 292 | +function gotoLombokLocation(position: number, buildFilePath: string): void { |
| 293 | + const newPosition = window.activeTextEditor.document.positionAt(position); |
| 294 | + const newSelection = new Selection(newPosition, newPosition); |
| 295 | + window.activeTextEditor.selection = newSelection; |
| 296 | + const newLocation = new Location(Uri.file(buildFilePath), newPosition); |
| 297 | + commands.executeCommand( |
| 298 | + Commands.GOTO_LOCATION, |
| 299 | + window.activeTextEditor.document.uri, |
| 300 | + window.activeTextEditor.selection.active, |
| 301 | + [newLocation], |
| 302 | + 'goto' |
| 303 | + ); |
| 304 | +} |
| 305 | + |
| 306 | +function isMavenProject(buildFilePath: string): boolean { |
| 307 | + const buildFileNames = ["pom.xml"]; |
| 308 | + for (const buildFileName of buildFileNames) { |
| 309 | + if (buildFilePath.indexOf(buildFileName)>=0) { |
| 310 | + return true; |
| 311 | + } |
| 312 | + } |
| 313 | + return false; |
| 314 | +} |
| 315 | + |
| 316 | +function isGradleProject(buildFilePath: string): boolean { |
| 317 | + const buildFileNames = ["build.gradle", "build.gradle.kts", "settings.gradle", "settings.gradle.kts"]; |
| 318 | + for (const buildFileName of buildFileNames) { |
| 319 | + if (buildFilePath.indexOf(buildFileName)>=0) { |
| 320 | + return true; |
| 321 | + } |
| 322 | + } |
| 323 | + return false; |
| 324 | +} |
0 commit comments