Skip to content

Commit

Permalink
Add dead_code_scanner tool warnings to the "Problems" pane
Browse files Browse the repository at this point in the history
  • Loading branch information
pmoura committed May 7, 2024
1 parent b1dcad8 commit b334f01
Show file tree
Hide file tree
Showing 5 changed files with 267 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## [0.21.0]

* Add "Logtalk: Scan Dead Code (workspace)" command
* Add `dead_code_scanner` tool warnings to the "Problems" pane
* Update the "Known Issues" section in the readme file

## [0.20.0]
Expand Down
7 changes: 5 additions & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { Utils } from "./utils/utils";
import LogtalkDocumentHighlightProvider from "./features/documentHighlightProvider";
import LogtalkTerminal from "./features/logtalkTerminal";
import LogtalkLinter from "./features/logtalkLinter";
import LogtalkDeadCodeScanner from "./features/logtalkDeadCodeScanner";
import LogtalkHoverProvider from "./features/hoverProvider";
import { LogtalkDeclarationProvider } from "./features/declarationProvider";
import { LogtalkDefinitionProvider } from "./features/definitionProvider";
Expand All @@ -37,6 +38,8 @@ export function activate(context: ExtensionContext) {
loadEditHelpers(subscriptions);
const linter = new LogtalkLinter(context);
linter.activate(subscriptions);
const deadCodeScanner = new LogtalkDeadCodeScanner(context);
deadCodeScanner.activate(subscriptions);

DEBUG ? console.log('Linter Loaded.') : null;

Expand All @@ -49,11 +52,11 @@ export function activate(context: ExtensionContext) {
{ command: "logtalk.make.check", callback: async (uri) => LogtalkTerminal.makeCheck(uri)},
{ command: "logtalk.run.tests", callback: uri => LogtalkTerminal.runTests(uri)},
{ command: "logtalk.run.doclet", callback: uri => LogtalkTerminal.runDoclet(uri)},
{ command: "logtalk.scan.deadCode", callback: uri => LogtalkTerminal.scanForDeadCode(uri)},
{ command: "logtalk.scan.deadCode", callback: uri => LogtalkTerminal.scanForDeadCode(uri, deadCodeScanner)},
{ command: "logtalk.generate.documentation", callback: uri => LogtalkTerminal.genDocumentation(uri)},
{ command: "logtalk.generate.diagrams", callback: uri => LogtalkTerminal.genDiagrams(uri)},
{ command: "logtalk.open", callback: () => LogtalkTerminal.openLogtalk()},
{ command: "logtalk.rscan.deadCode", callback: uri => LogtalkTerminal.rscanForDeadCode(uri)},
{ command: "logtalk.rscan.deadCode", callback: uri => LogtalkTerminal.rscanForDeadCode(uri, deadCodeScanner)},
{ command: "logtalk.run.testers", callback: uri => LogtalkTerminal.runTesters(uri)},
{ command: "logtalk.run.doclets", callback: uri => LogtalkTerminal.runDoclets(uri)},
{ command: "logtalk.open.parentFile", callback: uri => LogtalkTerminal.openParentFile(uri)}
Expand Down
191 changes: 191 additions & 0 deletions src/features/logtalkDeadCodeScanner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
"use strict";

import {
CancellationToken,
CodeActionContext,
CodeActionProvider,
Command,
Diagnostic,
DiagnosticCollection,
DiagnosticSeverity,
Disposable,
ExtensionContext,
languages,
OutputChannel,
Position,
Range,
Selection,
TextDocument,
TextEditorRevealType,
Uri,
window,
workspace,
WorkspaceEdit
} from "vscode";
import * as path from "path";

import LogtalkTerminal from "./logtalkTerminal";

export default class LogtalkDeadCodeScanner implements CodeActionProvider {

public diagnosticCollection: DiagnosticCollection;
public diagnostics: { [docName: string]: Diagnostic[] } = {};
public diagnosticHash = [];
private filePathIds: { [id: string]: string } = {};
private sortedDiagIndex: { [docName: string]: number[] } = {};
private msgRegex = /(((\*|\!)\s{5}.+\n[\*|\!]\s{7}.+\n)|((\*|\!)\s{5}.+\n))[\*|\!]\s{7}.+\n[\*|\!]\s{7}in file\s(.+)\s((at or above line\s(\d+))|(between lines\s(\d+)[-](\d+))|(at line\s(\d+)))/;
private executable: string;
private documentListener: Disposable;
private openDocumentListener: Disposable;
public outputChannel: OutputChannel = null;

constructor(private context: ExtensionContext) {
this.executable = null;
this.loadConfiguration();
}

provideCodeActions(
document: TextDocument,
range: Range,
context: CodeActionContext,
token: CancellationToken
): Command[] | Thenable<Command[]> {
let codeActions: Command[] = [];
return codeActions;
}
private parseIssue(issue: string) {

if(this.diagnosticHash.includes(issue)) {
return true
} else {
this.diagnosticHash.push(issue)
}

let match = issue.match(this.msgRegex)
if (match == null) { return null; }

let severity: DiagnosticSeverity;
if(match[0][0] == '*') {
severity = DiagnosticSeverity.Warning
} else {
severity = DiagnosticSeverity.Error
}

let fileName = path.resolve(match[6]);
console.log(fileName);
let lineFrom = 0,
lineTo = 0;
console.log(match)

if(match[9]) {
lineFrom = parseInt(match[9])-1;
lineTo = parseInt(match[9]);
} else if(match[14]) {
lineFrom = parseInt(match[14])-1;
lineTo = parseInt(match[14]);
} else {
lineFrom = parseInt(match[11])
lineTo = parseInt(match[12])-1
}

let fromCol = 0;
let toCol = 240; // Default horizontal range
let fromPos = new Position(lineFrom, fromCol);
let toPos = new Position(lineTo, toCol);
let range = new Range(fromPos, toPos);
let errMsg = match[1].replace(new RegExp(/\* /,'g'), '').replace(new RegExp(/\! /,'g'), '');
let diag = new Diagnostic(range, errMsg, severity);

if (diag) {
if (!this.diagnostics[fileName]) {
this.diagnostics[fileName] = [diag];
} else {
this.diagnostics[fileName].push(diag);
}
}

}

public lint(textDocument: TextDocument, message) {
this.parseIssue(message);
this.diagnosticCollection.delete(textDocument.uri);

for (let doc in this.diagnostics) {
let index = this.diagnostics[doc]
.map((diag, i) => {
return [diag.range.start.line, i];
})
.sort((a, b) => {
return a[0] - b[0];
});
this.sortedDiagIndex[doc] = index.map(item => {
return item[1];
});
this.diagnosticCollection.set(Uri.file(doc), this.diagnostics[doc]);
}
for (let doc in this.sortedDiagIndex) {
let si = this.sortedDiagIndex[doc];
for (let i = 0; i < si.length; i++) {
let diag = this.diagnostics[doc][si[i]];
let severity = diag.severity === DiagnosticSeverity.Error ? "ERROR" : "Warning";
this.outputChannel.append(message)
}
if (si.length > 0) {
this.outputChannel.show(true);
}
}
}

private loadConfiguration(): void {
let section = workspace.getConfiguration("logtalk");
if (section) {
this.executable = section.get<string>("executable.path", "logtalk");
if (this.documentListener) {
this.documentListener.dispose();
}
if (this.openDocumentListener) {
this.openDocumentListener.dispose();
}
}
}

public activate(subscriptions): void {

this.diagnosticCollection = languages.createDiagnosticCollection('Logtalk Dead Code Scanner');

workspace.onDidChangeConfiguration(
this.loadConfiguration,
this,
subscriptions
);

if (this.outputChannel === null) {
this.outputChannel = window.createOutputChannel("Logtalk Dead Code Scanner");
this.outputChannel.clear();
}

this.loadConfiguration();

// workspace.onDidOpenTextDocument(this.doPlint, this, subscriptions);
workspace.onDidCloseTextDocument(
textDocument => {
this.diagnosticCollection.delete(textDocument.uri);
},
null,
subscriptions
);
}

private outputMsg(msg: string) {
this.outputChannel.append(msg + "\n");
this.outputChannel.show(true);
}

public dispose(): void {
this.documentListener.dispose();
this.openDocumentListener.dispose();
this.diagnosticCollection.clear();
this.diagnosticCollection.dispose();
}

}
73 changes: 69 additions & 4 deletions src/features/logtalkTerminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as jsesc from "jsesc";
import * as fs from "fs";
import { spawn } from "process-promises";
import LogtalkLinter from "./logtalkLinter";
import LogtalkDeadCodeScanner from "./logtalkDeadCodeScanner";
import { isFunction } from "util";
import * as fsp from "fs/promises";
import * as timers from "timers/promises";
Expand Down Expand Up @@ -326,30 +327,94 @@ export default class LogtalkTerminal {
);
}

public static async scanForDeadCode(uri: Uri) {
public static async scanForDeadCode(uri: Uri, deadCodeScanner: LogtalkDeadCodeScanner) {
if (typeof uri === 'undefined') {
uri = window.activeTextEditor.document.uri;
}
let textDocument = null;
let logtalkHome: string = '';
let logtalkUser: string = '';
// Check for Configurations
let section = workspace.getConfiguration("logtalk");
if (section) {
logtalkHome = jsesc(section.get<string>("home.path", "logtalk"));
logtalkUser = jsesc(section.get<string>("user.path", "logtalk"));
} else {
throw new Error("configuration settings error: logtalk");
}
// Open the Text Document
await workspace.openTextDocument(uri).then((document: TextDocument) => { textDocument = document });
// Clear the Scratch Message File
let compilerMessagesFile = `${logtalkUser}/scratch/.messages`;
await fsp.rm(`${compilerMessagesFile}`, { force: true });
// Create the Terminal
LogtalkTerminal.createLogtalkTerm();
const dir0: string = LogtalkTerminal.ensureDir(uri);
const loader0 = path.join(dir0, "loader");
const dir = path.resolve(dir0).split(path.sep).join("/");
const loader = path.resolve(loader0).split(path.sep).join("/");
let goals = `logtalk_load(dead_code_scanner(loader)),logtalk_load('${loader}'),dead_code_scanner::directory('${dir}').\r`;
let goals = `vscode::dead_code('${dir}','${loader}').\r`;
LogtalkTerminal.sendString(goals);
// Parse any compiler errors or warnings
const marker = path.join(dir0, ".vscode_dead_code_scanning_done");
await LogtalkTerminal.waitForFile(marker);
await fsp.rm(marker, { force: true });
if(fs.existsSync(`${compilerMessagesFile}`)) {
const lines = fs.readFileSync(`${compilerMessagesFile}`).toString().split(/\r?\n/);
let message = '';
for (const line of lines) {
message = message + line + '\n';
if(line == '* ' || line == '! ') {
deadCodeScanner.lint(textDocument, message);
message = '';
}
}
}
}

public static async rscanForDeadCode(uri: Uri) {
public static async rscanForDeadCode(uri: Uri, deadCodeScanner: LogtalkDeadCodeScanner) {
if (typeof uri === 'undefined') {
uri = window.activeTextEditor.document.uri;
}
let textDocument = null;
let logtalkHome: string = '';
let logtalkUser: string = '';
// Check for Configurations
let section = workspace.getConfiguration("logtalk");
if (section) {
logtalkHome = jsesc(section.get<string>("home.path", "logtalk"));
logtalkUser = jsesc(section.get<string>("user.path", "logtalk"));
} else {
throw new Error("configuration settings error: logtalk");
}
// Open the Text Document
await workspace.openTextDocument(uri).then((document: TextDocument) => { textDocument = document });
// Clear the Scratch Message File
let compilerMessagesFile = `${logtalkUser}/scratch/.messages`;
await fsp.rm(`${compilerMessagesFile}`, { force: true });
// Create the Terminal
LogtalkTerminal.createLogtalkTerm();
const dir0: string = LogtalkTerminal.getWorkspaceFolder(uri);
const loader0 = path.join(dir0, "loader");
const dir = path.resolve(dir0).split(path.sep).join("/");
const loader = path.resolve(loader0).split(path.sep).join("/");
let goals = `logtalk_load(dead_code_scanner(loader)),logtalk_load('${loader}'),dead_code_scanner::rdirectory('${dir}').\r`;
let goals = `vscode::dead_code_recursive('${dir}','${loader}').\r`;
LogtalkTerminal.sendString(goals);
// Parse any compiler errors or warnings
const marker = path.join(dir0, ".vscode_dead_code_scanning_done");
await LogtalkTerminal.waitForFile(marker);
await fsp.rm(marker, { force: true });
if(fs.existsSync(`${compilerMessagesFile}`)) {
const lines = fs.readFileSync(`${compilerMessagesFile}`).toString().split(/\r?\n/);
let message = '';
for (const line of lines) {
message = message + line + '\n';
if(line == '* ' || line == '! ') {
deadCodeScanner.lint(textDocument, message);
message = '';
}
}
}
}

public static runTesters(uri: Uri) {
Expand Down
2 changes: 1 addition & 1 deletion src/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ export class Utils {

if (pp.status === 0) {
let out = pp.stdout.toString();
console.log("out: " + out);
// console.log("out: " + out);
let match = out.match(/arity=(\d+);name=(.*)/);
if (match) {
// console.log("m1: " + match[1]);
Expand Down

0 comments on commit b334f01

Please sign in to comment.