-
Notifications
You must be signed in to change notification settings - Fork 14
POM validator #1183
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
POM validator #1183
Changes from 8 commits
Commits
Show all changes
56 commits
Select commit
Hold shift + click to select a range
4dac044
WIP: adding pom.xml validator to the extension
rodinaarssen 8de5482
Launch REPL for open Rascal project.
toinehartman 2d0f50d
Work around nullability analysis.
toinehartman 2b9a392
Fix Maven resolution.
toinehartman 8a99601
Merge branch 'feature/remove-std-scheme' into feature/add-pomxml-vali…
toinehartman 0024640
Merge remote-tracking branch 'origin/feature/remove-std-scheme' into …
toinehartman 3dbbc5e
WIP: Add pom analyzer in Rascal.
toinehartman 37968f2
Improvements.
toinehartman a755a31
Merge remote-tracking branch 'origin/main' into feature/add-pomxml-va…
rodinaarssen 55fca31
Merge remote-tracking branch 'refs/remotes/origin/feature/add-pomxml-…
rodinaarssen b3a8714
PomValidator progress
rodinaarssen e62939f
Added cache for pom.xml files
rodinaarssen b4ec870
Added missing license
rodinaarssen 6754d06
Added keyword parameters for the versions
rodinaarssen 39ada87
Made pom analyzer more robust
rodinaarssen d03ec97
Added support for exclusions
rodinaarssen 30026f5
Wired up pom analyzer in the global analyzer
rodinaarssen bf82636
Added Rascal dependency check to analyzer
rodinaarssen bf215fd
Improved rascal-lsp dependency check
rodinaarssen dbb6033
Removed TS implementation of the pom.xml analyzer
rodinaarssen a49f1b9
Added comment
rodinaarssen dc4c458
Removed reference to removed file
rodinaarssen a61a968
Removed code action that no longer exists
rodinaarssen faaa5d7
Merge remote-tracking branch 'origin/main' into feature/add-pomxml-va…
rodinaarssen 2c651a8
Newline detection is now robust against single-line pom.xml files
rodinaarssen 386eaf9
Added Java functions to obtain Maven dependencies
rodinaarssen 25250ed
Rewrote addDependency TextEdit generation to work on plain nodes
rodinaarssen 65793a5
Cleanup of PomAnalyzer.rsc
rodinaarssen 8c82320
Analyzer now uses proper PomAnalyzer functions
rodinaarssen 65e98dd
Merge branch 'main' into feature/add-pomxml-validator
rodinaarssen 4ef3048
Using SpecificationVersion first to find the active rascal-lsp version
rodinaarssen 9415c2d
Added Rascal commands in favor of plain text edits
rodinaarssen 7b7ba65
Removed pom.xml check for a Rascal dependency; this is moved to Rascal
rodinaarssen 4848d6f
Added custom code action provider for xml files to be able to provide…
rodinaarssen f7fd701
Made Rascal version check robust against running from a target folder
rodinaarssen 4285706
Always read full pom.xml files
rodinaarssen f19683d
Correctly added argument to command
rodinaarssen c62b360
RascalTextDocumentService also handles xml files now
rodinaarssen 73ad8d5
Fixed typo
rodinaarssen 01aafa0
Extracted code action conversion function
rodinaarssen 1d4f791
Prevent RascalTextDocumentService from trying to parse pom.xml files …
rodinaarssen d5701cb
Fixed ShellEvaluatorFactory call after a formal argument got removed …
rodinaarssen 9a72d44
Fixed off-by-one error and added an overload for inferNewline in the …
rodinaarssen bc37a5b
Added tests for the pom.xml analyzer
rodinaarssen 44ca5de
Slightly simplified inferIndentation
rodinaarssen e933c47
Removed unused code
rodinaarssen 75f44a8
Unexported a variable
rodinaarssen 5fc6530
Only provide code actions for pom.xml files instead of for all xml files
rodinaarssen b5c0cba
Using latest RC of Rascal
rodinaarssen 6e2ec42
Removed unused imports
rodinaarssen f8ad534
Fixed CF error
rodinaarssen 71c59be
Incorporated feedback by @DavyLandman
rodinaarssen b549a59
Moved code action provider for pom.xml files to a discrete class
rodinaarssen 828b681
Got rid of fixed back-up versions for rascal and rascal-lsp in the code
rodinaarssen 6dcdf51
Unused import removed
rodinaarssen 0dac9f0
Changed log level
rodinaarssen File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
123 changes: 123 additions & 0 deletions
123
rascal-lsp/src/main/rascal/lsp/lang/xml/PomAnalyzer.rsc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| module lang::xml::PomAnalyzer | ||
|
|
||
| import lang::xml::DOM; | ||
| import lang::xml::IO; | ||
| import analysis::diff::edits::ExecuteTextEdits; | ||
| import analysis::diff::edits::TextEdits; | ||
|
|
||
| import IO; | ||
| import List; | ||
| import Node; | ||
| import String; | ||
| import util::IDEServices; | ||
| import util::Maybe; | ||
|
|
||
| bool(node) hasProperty(str tagName, str tagValue) { | ||
| return bool(node \node) { | ||
| return "<tagName>"(tagValue) <- getChildren(\node); | ||
| }; | ||
| } | ||
|
|
||
| Maybe[&T](node) getProperty(type[&T] _, str tagName) { | ||
| return Maybe[&T](node \node) { | ||
| if ("<tagName>"(&T tagValue) <- getChildren(\node)) { | ||
| return just(tagValue); | ||
| } | ||
| return nothing(); | ||
| }; | ||
| } | ||
|
|
||
| node getChildNode(node n, str name) { | ||
| if (node child <- getChildren(n), name := getName(child)) { | ||
| return child; | ||
| } | ||
| throw "No child with name \'<name>\' in \'<getName(n)>\': <[getName(c) | node c <- getChildren(n)]>"; | ||
| } | ||
|
|
||
| bool(node) hasRascalMplGroup = hasProperty("groupid", "org.rascalmpl"); | ||
|
|
||
| bool(node) isRascalArtifact = hasProperty("artifactid", "rascal"); | ||
|
|
||
| bool(node) isRascalLspArtifact = hasProperty("artifactid", "rascal-lsp"); | ||
|
|
||
| Maybe[node] getRascalDependency(list[node] dependencies) { | ||
| if (dep <- dependencies, hasRascalMplGroup(dep), isRascalArtifact(dep)) { | ||
| return just(dep); | ||
| } | ||
| return nothing(); | ||
| } | ||
|
|
||
| Maybe[node] getRascalLspDependency(list[node] dependencies) { | ||
| if (dep <- dependencies, hasRascalMplGroup(dep), isRascalLspArtifact(dep)) { | ||
| return just(dep); | ||
| } | ||
| return nothing(); | ||
| } | ||
|
|
||
| Maybe[str](node) getVersion = getProperty(#str, "version"); | ||
|
|
||
| str inferRascalVersion(Maybe[str] rascalLsp = nothing()) { | ||
| if (just(str lspVersion) := rascalLsp) { | ||
| // Figure out a compatible version | ||
| return "0.43.0"; | ||
| } | ||
|
|
||
| // Read Rascal version from LSP dependencies | ||
| lspPom = |project://rascal-lsp/pom.xml|; | ||
| if (exists(lspPom), /"dependency"("groupid"("org.rascalmpl"), "artifactid"("rascal"), "version"(str rascalVersion)) := readXML(lspPom)) { | ||
| return rascalVersion; | ||
| } | ||
|
|
||
| xml = readXML(lspPom); | ||
| iprintln(xml); | ||
| return "0.43.0"; | ||
| } | ||
|
|
||
| node dependencyTemplate(str artifactId, str version, str groupId="org.rascalmpl") | ||
| = "dependency"( | ||
| "groupId"(groupId), | ||
| "artifactId"(artifactId), | ||
| "version"(version) | ||
| ); | ||
|
|
||
| node dependenciesTemplate(node dependencies...) | ||
| = makeNode("dependencies", *dependencies); | ||
|
|
||
| str prettyXML(node xml) { | ||
| prettied = xmlPretty(toXML(xml)); | ||
| // `xmlPretty` assumes the node is a document and thus adds an `<?xml ...?>` tag at the start | ||
| return intercalate("\n", split("\n", prettied)[1..]); | ||
| } | ||
|
|
||
| void main(loc pomLoc = |project://rascal-vscode-extension/test-workspace/test-project/pom.xml|) { | ||
| if (node pom := readXML(pomLoc, trackOrigins = true)) { | ||
| // list[TextEdit] edits = []; | ||
| if (project := getChildNode(pom, "project")) { | ||
| if (dependenciesBlock := getChildNode(project, "dependencies")) { | ||
| dependencies = getChildren(dependenciesBlock); | ||
| if (just(rascal) := getRascalDependency(dependencies)) { | ||
| println("Found Rascal dependency: <getVersion(rascal)>"); | ||
| // Check minimal version | ||
| ; | ||
| } else { | ||
| // No Rascal dependency; offer to add one | ||
| println("No Rascal dependency found!"); | ||
| rascalLsp = getRascalLspDependency(dependencies); | ||
| rascalVersion = inferRascalVersion(rascalLsp=getVersion(rascalLsp)); | ||
| insertionPoint = getFirstFrom(getChildren(dependenciesBlock)); | ||
| rascalEdit = insertAfter(dependenciesBlock.src, prettyXML(dependencyTemplate("rascal", rascalVersion))); | ||
| iprintln(rascalEdit); | ||
| applyFileSystemEdits([changed([rascalEdit])]); | ||
| } | ||
| } else { | ||
| println("No dependencies block found"); | ||
| rascalEdit = insertAfter(getChildNode(project, "version").src, xmlPretty(toXML(dependenciesTemplate(dependencyTemplate("rascal", inferRascalVersion()))))); | ||
| iprintln(rascalEdit); | ||
| applyFileSystemEdits([changed([rascalEdit])]); | ||
| } | ||
| } else { | ||
| println("Unrecoverably broken POM!"); | ||
| ; // This a very sparse POM. We can probably not recover from this. | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,154 @@ | ||
| /* | ||
| * Copyright (c) 2018-2025, NWO-I CWI and Swat.engineering | ||
| * All rights reserved. | ||
| * | ||
| * Redistribution and use in source and binary forms, with or without | ||
| * modification, are permitted provided that the following conditions are met: | ||
| * | ||
| * 1. Redistributions of source code must retain the above copyright notice, | ||
| * this list of conditions and the following disclaimer. | ||
| * | ||
| * 2. Redistributions in binary form must reproduce the above copyright notice, | ||
| * this list of conditions and the following disclaimer in the documentation | ||
| * and/or other materials provided with the distribution. | ||
| * | ||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | ||
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| * POSSIBILITY OF SUCH DAMAGE. | ||
| */ | ||
| import * as vscode from 'vscode'; | ||
| import { isRascalProject, MF_DIR } from './RascalMFValidator'; | ||
|
|
||
| const POM_XML_FILE = "pom.xml"; | ||
|
|
||
| export class PomXmlValidator implements vscode.Disposable { | ||
| private readonly diagnostics: vscode.DiagnosticCollection; | ||
| private readonly disposables: vscode.Disposable[] = []; | ||
|
|
||
| constructor (private readonly logger: vscode.LogOutputChannel) { | ||
| logger.info("pom.xml validator starting"); | ||
| this.diagnostics = vscode.languages.createDiagnosticCollection("pom.xml diagnostics"); | ||
|
|
||
| // new projects should be checked | ||
| vscode.workspace.onDidChangeWorkspaceFolders(async ws => { | ||
| for (const added of ws.added) { | ||
| if (await isRascalProject(added.uri)) { | ||
| void this.verifyPomXml(added.uri); | ||
| } | ||
| } | ||
| for (const rem of ws.removed) { | ||
| // clear messages of projects that are no longer in the workspace | ||
| this.diagnostics.delete(this.buildPomXmlChildPath(rem.uri)); | ||
| } | ||
| }, this, this.disposables); | ||
|
|
||
| // check open folders | ||
| for (const openProject of vscode.workspace.workspaceFolders || []) { | ||
| const pomXmlUri = this.buildPomXmlChildPath(openProject.uri); | ||
| void vscode.workspace.fs.stat(pomXmlUri).then(_s => void this.verifyPomXml(pomXmlUri)); | ||
| } | ||
|
|
||
| // watch the file system for changes to pom.xml files | ||
| const watcher = vscode.workspace.createFileSystemWatcher("**/" + POM_XML_FILE, true, false, false); | ||
| watcher.onDidCreate(this.verifyPomXml, this, this.disposables); | ||
| watcher.onDidChange(this.verifyPomXml, this, this.disposables); | ||
| watcher.onDidDelete(e => this.diagnostics.delete(e), this, this.disposables); | ||
| this.disposables.push(watcher); | ||
|
|
||
| this.disposables.push( | ||
| vscode.languages.registerCodeActionsProvider( | ||
| { pattern: "**/" + POM_XML_FILE }, | ||
| new FixPomXmlIssues() | ||
| ) | ||
| ); | ||
| } | ||
|
|
||
| dispose() { | ||
| this.safeDispose(this.diagnostics); | ||
| for (const d of this.disposables) { | ||
| this.safeDispose(d); | ||
| } | ||
| } | ||
|
|
||
| private safeDispose(d: vscode.Disposable): void { | ||
| try { | ||
| d.dispose(); | ||
| } catch (_e) { /* ignore errors */ } | ||
| } | ||
|
|
||
| private async verifyPomXml(file: vscode.Uri) { | ||
| try { | ||
| const pomXmlBody = await vscode.workspace.openTextDocument(file); | ||
| const diagnostics : vscode.Diagnostic[] = []; | ||
|
|
||
| try { | ||
| checkRascalDependency(pomXmlBody, diagnostics); | ||
| } finally { | ||
| this.diagnostics.set(file, diagnostics); | ||
| } | ||
| } catch (_error) { | ||
| // Ignore errors | ||
| } | ||
| } | ||
|
|
||
| private buildPomXmlChildPath(uri: vscode.Uri) { | ||
| return vscode.Uri.joinPath(uri, MF_DIR, POM_XML_FILE); | ||
| } | ||
| } | ||
|
|
||
| enum FixKind { | ||
| noRascalDependency = 1, | ||
| outdatedRascalDependency | ||
| } | ||
|
|
||
| class FixPomXmlIssues implements vscode.CodeActionProvider { | ||
| provideCodeActions(_document: vscode.TextDocument, _range: vscode.Range | vscode.Selection, context: vscode.CodeActionContext, _token: vscode.CancellationToken): vscode.ProviderResult<(vscode.CodeAction | vscode.Command)[]> { | ||
| const result: vscode.CodeAction[] = []; | ||
| for (const diag of context.diagnostics) { | ||
| switch (diag.code) { | ||
| case FixKind.noRascalDependency: { | ||
| const addRascalDependency = new vscode.CodeAction("Add Rascal dependency", vscode.CodeActionKind.Empty); | ||
| addRascalDependency.diagnostics = [diag]; | ||
| addRascalDependency.isPreferred = true; | ||
| // addRascalDependency.command = ... | ||
| // vscode.window.showErrorMessage("..."); | ||
| result.push(addRascalDependency); | ||
| break; | ||
| } | ||
| case FixKind.outdatedRascalDependency: { | ||
| // TODO | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
| } | ||
| const foo = /<!--([^-]*?|-->)/; | ||
|
|
||
| // Pitfall: this regular expression only accepts groupId, artifactId, and version in that particular order, and does not support comments | ||
| const dependencyMatcher = /<dependency>\s*<groupId>([^<]*?)<\/groupId>\s*<artifactId>([^<]*?)<\/artifactId>\s*<version>([^<]*?)<\/version>\s*<\/dependency>/g; | ||
|
|
||
| function checkRascalDependency(pomXmlBody: vscode.TextDocument, diagnostics: vscode.Diagnostic[]) { | ||
| let match: RegExpExecArray | null; | ||
| const pomXmlText = pomXmlBody.getText(); | ||
| while ((match = dependencyMatcher.exec(pomXmlText))) { | ||
| if (match[1] === "org.rascalmpl" && match[2] === "rascal") { | ||
| //TODO: check that version is new enough | ||
| } | ||
| return; | ||
| } | ||
| const diag = new vscode.Diagnostic( | ||
| new vscode.Range(0, 0, 0, 0), "Could not detect a Rascal dependency, please add one to this pom.xml" | ||
| ); | ||
| diag.code = FixKind.noRascalDependency; | ||
| diagnostics.push(diag); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.