Skip to content

Commit

Permalink
Formatter update
Browse files Browse the repository at this point in the history
Adds a formatter support for Ruff, two commands and related settings. Updates the config to handle workspace and global boolean settings.

Resolves #3.
  • Loading branch information
Aeron committed Oct 25, 2023
1 parent 6ae503f commit de9e102
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 4 deletions.
5 changes: 5 additions & 0 deletions Scripts/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ class Config {
let global = nova.config.get(nova.extension.identifier + "." + key);
let workspace = nova.workspace.config.get(nova.extension.identifier + "." + key);

if (typeof global === "boolean" && typeof workspace == "number") {
if (workspace === -1) workspace = null
else workspace = Boolean(workspace)
}

if (workspace !== null && global !== workspace) return workspace;
return global;
}
Expand Down
130 changes: 130 additions & 0 deletions Scripts/formatter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
class Formatter {
constructor(config) {
this.config = config;
}

async getProcess(filename = null) {
const executablePath = nova.path.expanduser(this.config.get("executablePath"));
const commandArguments = this.config.get("commandFormatArguments");
const defaultOptions = (filename)
? (filename !== ".")
? ["--quiet", `--stdin-filename=${filename}`, "-"]
: ["--quiet", filename]
: ["--quiet", "-"];

if (!nova.fs.stat(executablePath)) {
console.error(`Executable ${executablePath} does not exist`);
return;
}

var options = [];

if (commandArguments) {
options = commandArguments
.replaceAll("\n", " ")
.split(" ")
.map((option) => option.trim())
.filter((option) => option !== " ");
}

options = [...options, ...defaultOptions].filter((option) => option !== "");

return new Process(
executablePath,
{
args: ["format", ...Array.from(new Set(options))],
stdio: "pipe",
cwd: nova.workspace.path, // NOTE: must be explicitly set
}
);
}

async getPromiseToFormat(editor) {
if (!this.config.get("formatOnSave")) return;

return new Promise((resolve, reject) => {
this.format(editor, resolve, reject);
});
}

async format(editor, resolve=null, reject=null) {
if (editor.document.isEmpty) {
if (reject) reject("empty file");
return;
}

let process = await this.getProcess(
editor.document.path ? nova.path.basename(editor.document.path) : null
);

if (!process) {
if (reject) reject("no process");
return;
}

const textRange = new Range(0, editor.document.length);
const content = editor.document.getTextInRange(textRange);
const filePath = nova.workspace.relativizePath(editor.document.path);

let outBuffer = [];
let errBuffer = [];

process.onStdout((output) => outBuffer.push(output));
process.onStderr((error) => errBuffer.push(error));
process.onDidExit((status) => {
if (status === 0) {
const formattedContent = outBuffer.join("");

let result = editor.edit((edit) => {
if (formattedContent !== content) {
console.log("Formatting " + filePath);
edit.replace(textRange, formattedContent, InsertTextFormat.PlainText);
} else {
console.log("Nothing to format");
}
});

if (resolve) resolve(result);
} else {
console.error(errBuffer.join(""));
if (reject) reject();
}
});

console.log("Running " + process.command + " " + process.args.join(" "));

process.start();

let writer = process.stdin.getWriter();

writer.ready.then(() => {
writer.write(content);
writer.close();
});
}

async formatWorkspace(workspace) {
let process = await this.getProcess(".");

if (!process) {
return;
}

let errBuffer = [];

process.onStderr((error) => errBuffer.push(error));
process.onDidExit((status) => {
if (status === 0) {
console.log("Formatting the workspace");
} else {
console.error(errBuffer.join(""));
}
});

console.log("Running " + process.command + " " + process.args.join(" "));

process.start();
}
}

module.exports = Formatter;
16 changes: 15 additions & 1 deletion Scripts/main.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
const Config = require("./config");
const IssuesProvider = require("./provider");
const Formatter = require("./Formatter");

exports.activate = function() {
const config = new Config();
const issueCollection = new IssueCollection("ruff");
const issuesProvider = new IssuesProvider(config, issueCollection);
const formatter = new Formatter(config);

console.info("Executable path: " + config.get("executablePath"));
console.info("Command arguments: " + config.get("commandArguments"));
console.info("Command (check) arguments: " + config.get("commandArguments"));
console.info("Check mode: " + config.get("checkMode"));
console.info("Command (format) arguments: " + config.get("commandFormatArguments"));
console.info("Format on save: " + config.get("formatOnSave"));

var assistant = null;

Expand All @@ -34,4 +38,14 @@ exports.activate = function() {
issueCollection.set(editor.document.uri, issues);
});
});

nova.workspace.onDidAddTextEditor((editor) => {
if (editor.document.syntax !== "python") return;
editor.onWillSave(formatter.getPromiseToFormat, formatter);
});

nova.commands.register("formatWithRuff", formatter.format, formatter);
nova.commands.register(
"formatWorkspaceWithRuff", formatter.formatWorkspace, formatter
);
}
48 changes: 45 additions & 3 deletions extension.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"organization": "Aeron",
"description": "Ruff, an extremely fast Python linter, written in Rust, for Nova.",
"version": "1.0.1",
"categories": ["issues", "commands"],
"categories": ["issues", "commands", "formatters"],
"repository": "https://github.com/Aeron/Ruff.novaextension",
"bugs": "https://github.com/Aeron/Ruff.novaextension/issues",
"license": "BSD-2-Clause-Patent",
Expand All @@ -25,6 +25,21 @@
"filters": {
"syntaxes": ["python"]
}
},
{
"title": "Format with Ruff",
"command": "formatWithRuff",
"shortcut": "cmd-shift-B",
"filters": {
"syntaxes": ["python"]
}
}
],
"extensions": [
{
"title": "Format Workspace with Ruff",
"command": "formatWorkspaceWithRuff",
"shortcut": "cmd-shift-opt-B"
}
]
},
Expand Down Expand Up @@ -52,7 +67,7 @@
},
{
"key": "cc.aeron.nova-ruff.commandArguments",
"title": "Command Arguments",
"title": "Command (Check) Arguments",
"description": "Additional arguments. The --output-format and --quiet options are always set.",
"type": "string",
"default": null
Expand All @@ -68,6 +83,19 @@
["-", "Command only"]
],
"default": "onChange"
},
{
"key": "cc.aeron.nova-ruff.commandFormatArguments",
"title": "Command (Format) Arguments",
"description": "Additional arguments. The --quiet option is always set. The --stdin-filename is set conditionally.",
"type": "string",
"default": null
},
{
"key": "cc.aeron.nova-ruff.formatOnSave",
"title": "Format on a file save",
"type": "boolean",
"default": true
}
],
"configWorkspace": [
Expand All @@ -80,7 +108,7 @@
},
{
"key": "cc.aeron.nova-ruff.commandArguments",
"title": "Command Arguments",
"title": "Command (Check) Arguments",
"description": "Additional arguments. The --output-format and --quiet options are always set.",
"type": "string"
},
Expand All @@ -94,6 +122,20 @@
["onSave", "On a file save"],
["-", "Command only"]
]
},
{
"key": "cc.aeron.nova-ruff.commandFormatArguments",
"title": "Command (Format) Arguments",
"description": "Additional arguments. The --quiet option is always set. The --stdin-filename is set conditionally.",
"type": "string",
"default": null
},
{
"key": "cc.aeron.nova-ruff.formatOnSave",
"title": "Format on a file save",
"type": "enum",
"values": [[-1, "Global"], [1, "Yes"], [0, "No"]],
"default": -1
}
]
}

0 comments on commit de9e102

Please sign in to comment.