Skip to content

Commit

Permalink
fix: move extended language formatting to client (#1258)
Browse files Browse the repository at this point in the history
  • Loading branch information
nayeemrmn authored Feb 25, 2025
1 parent 50bebcf commit c60c069
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 62 deletions.
27 changes: 27 additions & 0 deletions client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
"vscode": "^1.77.0"
},
"dependencies": {
"@types/diff": "^7.0.1",
"diff": "^7.0.0",
"dotenv": "^16.4.5",
"jsonc-parser": "^3.2.0",
"semver": "7.5.2",
Expand Down
5 changes: 5 additions & 0 deletions client/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import * as path from "path";
import * as process from "process";
import * as jsoncParser from "jsonc-parser/lib/esm/main.js";
import { semver } from "./semver";
import { log } from "./extension";

// deno-lint-ignore no-explicit-any
export type Callback = (...args: any[]) => unknown;
Expand Down Expand Up @@ -185,6 +186,10 @@ export function startLanguageServer(
{
outputChannel: extensionContext.outputChannel,
middleware: {
provideDocumentFormattingEdits: (document, options, token, next) => {
log("provideDocumentFormattingEdits:", document.uri.toString(), options);
return next(document, options, token)
},
workspace: {
configuration: (params, token, next) => {
const response = next(params, token) as Record<string, unknown>[];
Expand Down
114 changes: 53 additions & 61 deletions client/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import * as util from "util";

import * as vscode from "vscode";
import { registerSidebar } from "./tasks_sidebar";
import { DENO_FORMATTING_EDIT_PROVIDER } from "./formatting";

function handleConfigurationChange(event: vscode.ConfigurationChangeEvent) {
if (
Expand Down Expand Up @@ -82,67 +83,25 @@ export async function activate(
const p2cMap = new Map<string, string>();
extensionContext.clientOptions = {
documentSelector: [
{ scheme: "file", language: "javascript" },
{ scheme: "file", language: "javascriptreact" },
{ scheme: "file", language: "typescript" },
{ scheme: "file", language: "typescriptreact" },
{ scheme: "file", language: "json" },
{ scheme: "file", language: "jsonc" },
{ scheme: "file", language: "markdown" },
{ scheme: "file", language: "html" },
{ scheme: "file", language: "css" },
{ scheme: "file", language: "scss" },
{ scheme: "file", language: "sass" },
{ scheme: "file", language: "less" },
{ scheme: "file", language: "yaml" },
{ scheme: "file", language: "sql" },
{ scheme: "file", language: "svelte" },
{ scheme: "file", language: "vue" },
{ scheme: "file", language: "astro" },
{ scheme: "file", language: "vento" },
{ scheme: "file", language: "nunjucks" },
{ scheme: "untitled", language: "javascript" },
{ scheme: "untitled", language: "javascriptreact" },
{ scheme: "untitled", language: "typescript" },
{ scheme: "untitled", language: "typescriptreact" },
{ scheme: "untitled", language: "json" },
{ scheme: "untitled", language: "jsonc" },
{ scheme: "untitled", language: "markdown" },
{ scheme: "untitled", language: "html" },
{ scheme: "untitled", language: "css" },
{ scheme: "untitled", language: "scss" },
{ scheme: "untitled", language: "sass" },
{ scheme: "untitled", language: "less" },
{ scheme: "untitled", language: "yaml" },
{ scheme: "untitled", language: "sql" },
{ scheme: "untitled", language: "svelte" },
{ scheme: "untitled", language: "vue" },
{ scheme: "untitled", language: "astro" },
{ scheme: "untitled", language: "vento" },
{ scheme: "untitled", language: "nunjucks" },
{ scheme: "deno", language: "javascript" },
{ scheme: "deno", language: "javascriptreact" },
{ scheme: "deno", language: "typescript" },
{ scheme: "deno", language: "typescriptreact" },
{ scheme: "deno", language: "json" },
{ scheme: "deno", language: "jsonc" },
{ scheme: "deno", language: "markdown" },
{ scheme: "deno", language: "html" },
{ scheme: "deno", language: "css" },
{ scheme: "deno", language: "scss" },
{ scheme: "deno", language: "sass" },
{ scheme: "deno", language: "less" },
{ scheme: "deno", language: "yaml" },
{ scheme: "deno", language: "sql" },
{ scheme: "deno", language: "svelte" },
{ scheme: "deno", language: "vue" },
{ scheme: "deno", language: "astro" },
{ scheme: "deno", language: "vento" },
{ scheme: "deno", language: "nunjucks" },
{ notebook: "*", language: "javascript" },
{ notebook: "*", language: "javascriptreact" },
{ notebook: "*", language: "typescript" },
{ notebook: "*", language: "typescriptreact" },
{ language: "javascript", scheme: "file" },
{ language: "javascript", scheme: "untitled" },
{ language: "javascript", scheme: "deno" },
{ language: "javascript", notebook: "*" },
{ language: "javascriptreact", scheme: "file" },
{ language: "javascriptreact", scheme: "untitled" },
{ language: "javascriptreact", scheme: "deno" },
{ language: "javascriptreact", notebook: "*" },
{ language: "typescript", scheme: "file" },
{ language: "typescript", scheme: "untitled" },
{ language: "typescript", scheme: "deno" },
{ language: "typescript", notebook: "*" },
{ language: "typescriptreact", scheme: "file" },
{ language: "typescriptreact", scheme: "untitled" },
{ language: "typescriptreact", scheme: "deno" },
{ language: "typescriptreact", notebook: "*" },
{ language: "json", scheme: "file" },
{ language: "json", scheme: "untitled" },
{ language: "json", scheme: "deno" },
],
uriConverters: {
code2Protocol: (uri) => {
Expand Down Expand Up @@ -240,6 +199,39 @@ export async function activate(
extensionContext.tasksSidebar.refresh();
}
}));
context.subscriptions.push(
vscode.languages.registerDocumentFormattingEditProvider([
{ language: "jsonc", scheme: "file" },
{ language: "jsonc", scheme: "untitled" },
{ language: "markdown", scheme: "file" },
{ language: "markdown", scheme: "untitled" },
{ language: "html", scheme: "file" },
{ language: "html", scheme: "untitled" },
{ language: "css", scheme: "file" },
{ language: "css", scheme: "untitled" },
{ language: "scss", scheme: "file" },
{ language: "scss", scheme: "untitled" },
{ language: "sass", scheme: "file" },
{ language: "sass", scheme: "untitled" },
{ language: "less", scheme: "file" },
{ language: "less", scheme: "untitled" },
{ language: "yaml", scheme: "file" },
{ language: "yaml", scheme: "untitled" },
{ language: "sql", scheme: "file" },
{ language: "sql", scheme: "untitled" },
{ language: "svelte", scheme: "file" },
{ language: "svelte", scheme: "untitled" },
{ language: "vue", scheme: "file" },
{ language: "vue", scheme: "untitled" },
{ language: "astro", scheme: "file" },
{ language: "astro", scheme: "untitled" },
{ language: "astro", scheme: "deno" },
{ language: "vento", scheme: "file" },
{ language: "vento", scheme: "untitled" },
{ language: "nunjucks", scheme: "file" },
{ language: "nunjucks", scheme: "untitled" },
], DENO_FORMATTING_EDIT_PROVIDER),
);

const registerCommand = createRegisterCommand(context);
registerCommand("deno.client.showReferences", commands.showReferences);
Expand Down
102 changes: 102 additions & 0 deletions client/src/formatting.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { diffChars } from "diff";
import * as fs from "fs/promises";
import * as path from "path";
import { spawnSync } from "child_process";
import * as vscode from "vscode";
import { getDenoCommandName } from "./util";

const SUPPORTED_EXTENSIONS_BY_LANGUAGE_ID = new Map([
["jsonc", "jsonc"],
["markdown", "markdown"],
["html", "html"],
["css", "css"],
["scss", "scss"],
["sass", "sass"],
["less", "less"],
["yaml", "yaml"],
["sql", "sql"],
["svelte", "svelte"],
["vue", "vue"],
["astro", "astro"],
["vento", "vto"],
["nunjucks", "njk"],
]);

export const DENO_FORMATTING_EDIT_PROVIDER:
vscode.DocumentFormattingEditProvider = {
async provideDocumentFormattingEdits(document, options, _token) {
// TODO(nayeemrmn): Account for `deno.json` exclude settings.
const ext = SUPPORTED_EXTENSIONS_BY_LANGUAGE_ID.get(document.languageId);
if (!ext) {
throw new Error("Unexpected language ID for client-side formatting.");
}
const command = await getDenoCommandName();
let cwd = path.dirname(document.uri.fsPath);
while (!isDir(cwd)) {
const parentDir = path.dirname(cwd);
if (parentDir == cwd) {
break;
}
cwd = parentDir;
}
const configArgs = [];
if (!options.insertSpaces) {
configArgs.push("--use-tabs");
}
configArgs.push("--indent-width");
configArgs.push(options.tabSize.toString());
const input = document.getText();
const { stdout, error } = spawnSync(command, [
"fmt",
"--unstable-component",
"--unstable-sql",
"--ext",
ext,
...configArgs,
"-",
], {
encoding: "utf-8",
cwd,
stdio: "pipe",
input,
});
if (error) {
throw error;
}
const edits = [];
let currentOffset = 0;
for (const change of diffChars(input, stdout)) {
if (change.added) {
edits.push(
vscode.TextEdit.insert(
document.positionAt(currentOffset),
change.value,
),
);
} else {
const nextOffset = currentOffset + (change.count ?? 0);
if (change.removed) {
edits.push(
vscode.TextEdit.delete(
new vscode.Range(
document.positionAt(currentOffset),
document.positionAt(nextOffset),
),
),
);
}
currentOffset = nextOffset;
}
}
return edits;
},
};

async function isDir(path: string): Promise<boolean> {
try {
const stats = await fs.stat(path);
return stats.isDirectory();
} catch {
return false;
}
}
3 changes: 2 additions & 1 deletion client/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"module": "commonjs",
"target": "es2019",
"lib": [
"ES2019"
"ES2019",
"ES2022.Intl"
],
"outDir": "out",
"rootDir": "src",
Expand Down

0 comments on commit c60c069

Please sign in to comment.