fix: diagnostics do not refresh after editing pom file (#695)

This commit is contained in:
Melody618 2021-08-18 13:31:43 +08:00 коммит произвёл GitHub
Родитель 7856e00c8b
Коммит 38b3f420a7
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
7 изменённых файлов: 119 добавлений и 42 удалений

70
package-lock.json сгенерированный
Просмотреть файл

@ -8,12 +8,14 @@
"version": "0.32.2",
"license": "MIT",
"dependencies": {
"@types/lru-cache": "^5.1.0",
"axios": "^0.21.1",
"expand-home-dir": "0.0.3",
"fast-glob": "^3.2.7",
"fs-extra": "^4.0.3",
"get-port": "^4.2.0",
"lodash": "^4.17.21",
"lru-cache": "^6.0.0",
"md5": "^2.2.1",
"minimatch": "^3.0.4",
"semver": "^5.6.0",
@ -176,6 +178,11 @@
"integrity": "sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==",
"dev": true
},
"node_modules/@types/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz",
"integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw=="
},
"node_modules/@types/md5": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@types/md5/-/md5-2.3.0.tgz",
@ -2523,6 +2530,17 @@
"node": ">=8"
}
},
"node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/map-cache": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
@ -4320,17 +4338,6 @@
"vscode": "^1.52.0"
}
},
"node_modules/vscode-languageclient/node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/vscode-languageclient/node_modules/semver": {
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
@ -4345,11 +4352,6 @@
"node": ">=10"
}
},
"node_modules/vscode-languageclient/node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"node_modules/vscode-languageserver-protocol": {
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz",
@ -4712,6 +4714,11 @@
"integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==",
"dev": true
},
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"node_modules/yargs": {
"version": "13.3.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
@ -4880,6 +4887,11 @@
"integrity": "sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==",
"dev": true
},
"@types/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz",
"integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw=="
},
"@types/md5": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@types/md5/-/md5-2.3.0.tgz",
@ -6748,6 +6760,14 @@
"chalk": "^2.4.2"
}
},
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"requires": {
"yallist": "^4.0.0"
}
},
"map-cache": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
@ -8144,14 +8164,6 @@
"vscode-languageserver-protocol": "3.16.0"
},
"dependencies": {
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"requires": {
"yallist": "^4.0.0"
}
},
"semver": {
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
@ -8159,11 +8171,6 @@
"requires": {
"lru-cache": "^6.0.0"
}
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
}
}
},
@ -8453,6 +8460,11 @@
"integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==",
"dev": true
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"yargs": {
"version": "13.3.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",

Просмотреть файл

@ -705,12 +705,14 @@
"webpack-cli": "^3.3.11"
},
"dependencies": {
"@types/lru-cache": "^5.1.0",
"axios": "^0.21.1",
"expand-home-dir": "0.0.3",
"fast-glob": "^3.2.7",
"fs-extra": "^4.0.3",
"get-port": "^4.2.0",
"lodash": "^4.17.21",
"lru-cache": "^6.0.0",
"md5": "^2.2.1",
"minimatch": "^3.0.4",
"semver": "^5.6.0",

Просмотреть файл

@ -1,8 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import * as fse from "fs-extra";
import * as _ from "lodash";
import { performance } from "perf_hooks";
import * as vscode from "vscode";
import { getRequestDelay, lruCache, MovingAverage } from "./debouncing";
import { mavenExplorerProvider } from "./explorer/mavenExplorerProvider";
import { Dependency } from "./explorer/model/Dependency";
import { MavenProject } from "./explorer/model/MavenProject";
import { UserError } from "./utils/errorUtils";
import { ElementNode, getNodesByTag, XmlTagName } from "./utils/lexerUtils";
@ -16,22 +20,44 @@ class DiagnosticProvider {
const dependencyCollection = vscode.languages.createDiagnosticCollection("Dependency");
this._collection = dependencyCollection;
context.subscriptions.push(this._collection);
context.subscriptions.push(vscode.workspace.onDidCloseTextDocument(doc => {
dependencyCollection.delete(doc.uri);
this.map.clear();
context.subscriptions.push(vscode.workspace.onDidChangeTextDocument(
async e => {
if (e.document.fileName.endsWith("pom.xml")) {
await this.debouncedRefresh(e.document.uri);
}
}));
}
public async refreshDiagnostics(uri: vscode.Uri, conflictNodes: Dependency[]): Promise<vscode.Diagnostic[]> {
this.map.clear();
private updateNodeForDocumentTimeout: NodeJS.Timer;
private async debouncedRefresh(uri: vscode.Uri): Promise<void> {
if (this.updateNodeForDocumentTimeout) {
clearTimeout(this.updateNodeForDocumentTimeout);
}
const timeout: number = getRequestDelay(uri);
this.updateNodeForDocumentTimeout = setTimeout(async () => {
const startTime: number = performance.now();
await this.refreshDiagnostics(uri);
const executionTime: number = performance.now() - startTime;
const movingAverage: MovingAverage = lruCache.get(uri) || new MovingAverage();
movingAverage.update(executionTime);
lruCache.set(uri, movingAverage);
}, timeout);
}
public async refreshDiagnostics(uri: vscode.Uri): Promise<void> {
const diagnostics: vscode.Diagnostic[] = [];
const project: MavenProject | undefined = mavenExplorerProvider.getMavenProject(uri.fsPath);
if (project === undefined) {
throw new Error("Failed to get maven project.");
}
const conflictNodes: Dependency[] = project.conflictNodes;
for (const node of conflictNodes) {
const diagnostic = await this.createDiagnostics(node);
diagnostics.push(diagnostic);
this.map.set(diagnostic, node);
}
this._collection.set(uri, diagnostics);
return diagnostics;
}
public async createDiagnostics(node: Dependency): Promise<vscode.Diagnostic> {
@ -44,8 +70,8 @@ class DiagnosticProvider {
}
public async findConflictRange(filePath: string, gid: string, aid: string): Promise<vscode.Range> {
const contentBuf: Buffer = await fse.readFile(filePath);
const projectNodes: ElementNode[] = getNodesByTag(contentBuf.toString(), XmlTagName.Project);
const pomDocument = await vscode.window.showTextDocument(vscode.Uri.file(filePath), {preserveFocus: true});
const projectNodes: ElementNode[] = getNodesByTag(pomDocument.document.getText(), XmlTagName.Project);
if (projectNodes === undefined || projectNodes.length !== 1) {
throw new UserError("Only support POM file with single <project> node.");
}

33
src/debouncing.ts Normal file
Просмотреть файл

@ -0,0 +1,33 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
import * as LRUCache from "lru-cache";
import * as vscode from "vscode";
export const lruCache: LRUCache<vscode.Uri, MovingAverage> = new LRUCache<vscode.Uri, MovingAverage>(32);
// See: https://github.com/microsoft/vscode/blob/94c9ea46838a9a619aeafb7e8afd1170c967bb55/src/vs/base/common/numbers.ts
export class MovingAverage {
private _n: number = 1;
private _val: number = 0;
public update(value: number): this {
this._val = this._val + (value - this._val) / this._n;
this._n += 1;
return this;
}
public get value(): number {
return this._val;
}
}
export function getRequestDelay(uri: vscode.Uri): number {
const avg: MovingAverage | undefined = lruCache.get(uri);
if (!avg) {
return 350;
}
// See: https://github.com/microsoft/vscode/blob/94c9ea46838a9a619aeafb7e8afd1170c967bb55/src/vs/editor/common/modes/languageFeatureRegistry.ts#L204
return Math.max(350, Math.floor(1.3 * avg.value));
}

Просмотреть файл

@ -22,8 +22,8 @@ export class DependenciesMenu extends Menu implements ITreeItem {
}
public async getChildren() : Promise<Dependency[] | HintNode[]> {
const [treeNodes, conflictNodes] = await parseRawDependencyDataHandler(this.project);
await diagnosticProvider.refreshDiagnostics(vscode.Uri.file(this.project.pomPath), conflictNodes);
const treeNodes = await parseRawDependencyDataHandler(this.project);
await diagnosticProvider.refreshDiagnostics(vscode.Uri.file(this.project.pomPath));
if (treeNodes.length === 0) {
const hintNodes: HintNode[] = [new HintNode("No dependencies")];
return Promise.resolve(hintNodes);

Просмотреть файл

@ -12,6 +12,7 @@ import { Utils } from "../../utils/Utils";
import { EffectivePomProvider } from "../EffectivePomProvider";
import { mavenExplorerProvider } from "../mavenExplorerProvider";
import { DependenciesMenu} from "./DependenciesMenu";
import { Dependency } from "./Dependency";
import { IEffectivePom } from "./IEffectivePom";
import { ITreeItem } from "./ITreeItem";
import { LifecycleMenu } from "./LifecycleMenu";
@ -24,6 +25,7 @@ export class MavenProject implements ITreeItem {
public parent?: MavenProject; // assigned if it's specified as one of parent project's modules
public pomPath: string;
public _fullDependencyText: string;
public conflictNodes: Dependency[];
private ePomProvider: EffectivePomProvider;
private _ePom: any;
private _pom: any;

Просмотреть файл

@ -11,7 +11,7 @@ import { getDependencyTree } from "../handlers/showDependenciesHandler";
const DUPLICATE_INDICATOR: string = "omitted for duplicate";
const CONFLICT_INDICATOR: string = "omitted for conflict";
export async function parseRawDependencyDataHandler(project: MavenProject): Promise<Dependency[][]> {
export async function parseRawDependencyDataHandler(project: MavenProject): Promise<Dependency[]> {
const dependencyTree: string | undefined = await getDependencyTree(project.pomPath);
if (dependencyTree === undefined) {
throw new Error("Failed to generate dependency tree.");
@ -30,7 +30,9 @@ export async function parseRawDependencyDataHandler(project: MavenProject): Prom
const indent: string = " "; // three spaces
const eol: string = "\r\n";
const prefix: string = "+- ";
return await parseTreeNodes(treeContent, eol, indent, prefix, project.pomPath);
const [treeNodes, conflictNodes] = await parseTreeNodes(treeContent, eol, indent, prefix, project.pomPath);
project.conflictNodes = conflictNodes;
return treeNodes;
}
async function parseTreeNodes(treecontent: string, eol: string, indent: string, prefix: string, projectPomPath: string): Promise<Dependency[][]> {