Skip to content

Commit

Permalink
clang format
Browse files Browse the repository at this point in the history
  • Loading branch information
immccn123 committed Aug 27, 2024
1 parent cc0aace commit 739bfdc
Show file tree
Hide file tree
Showing 13 changed files with 410 additions and 22 deletions.
26 changes: 24 additions & 2 deletions packages/lg-solution-formatter/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,26 @@
export default formatSolution;

export function formatSolution(sourceStr: string): Promise<string>;
export type Config = {
clang?: {
enabled?: boolean;
config?: string;
};
};
/**
* @typedef {{
* clang?: {
* enabled?: boolean,
* config?: string
* }
* }} Config
*/
/**
* @param {string} sourceStr
* @param {Config} config
*/
export function formatSolution(sourceStr: string, config?: Config): Promise<string>;
/**
* clang-format is not supported.
*
* @param {string} sourceStr
*/
export function formatSolutionSync(sourceStr: string): string;
38 changes: 30 additions & 8 deletions packages/lg-solution-formatter/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,48 @@ import remarkMath from "remark-math";
import remarkStringify from "remark-stringify";
import remarkLfmFmt from "@imkdown/remark-lfm-fmt";
import remarkGfm from "remark-gfm";
import remarkClangFmtWasm from "@imkdown/remark-clang-fmt-wasm";

const solFmtRemark = remark()
.use(remarkMath, { singleDollarTextMath: true })
.use(remarkGfm)
.use(remarkLfmFmt)
.use(remarkStringify, { bullet: "-", rule: "-" });
/**
* @typedef {{
* clang?: {
* enabled?: boolean,
* config?: string
* }
* }} Config
*/

/**
* @param {string} sourceStr
* @param {Config} config
*/
const formatSolution = async (sourceStr) => {
const file = await solFmtRemark.process(sourceStr);
const formatSolution = async (sourceStr, config = {}) => {
let rem = remark()
.use(remarkMath, { singleDollarTextMath: true })
.use(remarkGfm)
.use(remarkLfmFmt)
.use(remarkStringify, { bullet: "-", rule: "-" });

if (config.clang?.enabled) {
rem = rem.use(remarkClangFmtWasm, config.clang.config);
}

const file = await rem.process(sourceStr);
return String(file);
};

/**
* clang-format is not supported.
*
* @param {string} sourceStr
*/
const formatSolutionSync = (sourceStr) => {
const file = solFmtRemark.processSync(sourceStr);
const file = remark()
.use(remarkMath, { singleDollarTextMath: true })
.use(remarkGfm)
.use(remarkLfmFmt)
.use(remarkStringify, { bullet: "-", rule: "-" })
.processSync(sourceStr);
return String(file);
};

Expand Down
3 changes: 2 additions & 1 deletion packages/lg-solution-formatter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"remark": "^15.0.1",
"remark-gfm": "^4.0.0",
"remark-math": "^6.0.0",
"remark-stringify": "^11.0.0"
"remark-stringify": "^11.0.0",
"@imkdown/remark-clang-fmt-wasm": "workspace:^"
},
"exports": {
"default": "./index.js"
Expand Down
13 changes: 13 additions & 0 deletions packages/remark-clang-fmt-wasm/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
declare module "@wasm-fmt/clang-format/clang-format-vite.js" {
export * from "@wasm-fmt/clang-format";
}

/**
* @param {string} config
*/
export default function remarkClangFmtWasm(
config?: string
): (tree: Root) => Promise<void>;
export type Root = import("mdast").Root;
export type RootContent = import("mdast").RootContent;
export type VFile = import("vfile").VFile;
35 changes: 35 additions & 0 deletions packages/remark-clang-fmt-wasm/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* @typedef {import('mdast').Root} Root
* @typedef {import('mdast').RootContent} RootContent
* @typedef {import('vfile').VFile} VFile
*/


// @ts-ignore
import init, { format } from "@wasm-fmt/clang-format/clang-format-vite.js";
import { visit } from "unist-util-visit";

/**
* @param {string} config
*/
export default function remarkClangFmtWasm(config = "WebKit") {
/**
* The plugin.
*
* @param {Root} tree
* Tree.
* @returns
* Nothing.
*/
return async (tree) => {
await init();

visit(tree, "code", (node) => {
if (node.lang === "cpp" || node.lang === "c++") {
node.value = format(node.value, "main.cc", config);
} else if (node.lang === "c") {
node.value = format(node.value, "main.c", config);
}
});
};
}
26 changes: 26 additions & 0 deletions packages/remark-clang-fmt-wasm/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "@imkdown/remark-clang-fmt-wasm",
"private": true,
"version": "1.0.0",
"type": "module",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"exports": {
"default": "./index.js"
},
"types": "index.d.ts",
"dependencies": {
"@wasm-fmt/clang-format": "^18.1.8",
"unist-util-visit": "^5.0.0"
},
"devDependencies": {
"@types/mdast": "^4.0.3",
"vfile": "^6.0.3"
}
}
4 changes: 4 additions & 0 deletions packages/remark-clang-fmt-wasm/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["index.js"],
}
1 change: 1 addition & 0 deletions packages/solfmt-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@codemirror/state": "^6.4.1",
"@imkdown/lg-solution-formatter": "workspace:^",
"codemirror": "^6.0.1",
"pinia": "^2.2.2",
"vue": "^3.4.38",
"vue-codemirror6": "^1.3.4"
},
Expand Down
70 changes: 61 additions & 9 deletions packages/solfmt-web/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,45 @@ import { MergeView } from "@codemirror/merge";
import { markdown } from "@codemirror/lang-markdown";
import { languages } from "@codemirror/language-data";
import formatSolution, {
formatSolutionSync,
} from "@imkdown/lg-solution-formatter";
import { formatSolution } from "@imkdown/lg-solution-formatter";
import { ref, watch } from "vue";
import { EditorState } from "@codemirror/state";
import { useStore } from "./store";
import { storeToRefs } from "pinia";
import Config from "./components/Config.vue";
let lastTimeout: number = 0;
const code = ref("");
const store = useStore();
const refStore = storeToRefs(store);
store.$subscribe((_mutation, state) => {
localStorage.setItem("state", JSON.stringify(state));
});
const code = refStore.code;
const showFormatted = ref(false),
formatting = ref(false),
showDone = ref(false);
const mergedEditor = ref<HTMLDivElement | null>(null);
const formatRaw = () => {
code.value = formatSolutionSync(code.value);
const formatRaw = async () => {
formatting.value = true;
code.value = await formatSolution(code.value, {
clang: {
enabled: refStore.clangEnabled.value,
config: refStore.clangConfig.value,
},
});
showDone.value = true;
clearTimeout(lastTimeout);
lastTimeout = setTimeout(() => (showDone.value = false), 1000);
formatting.value = false;
};
const formatAndCopy = () => {
Expand All @@ -37,11 +54,23 @@ const formatAndCopy = () => {
const COMMIT_HASH: string = import.meta.env.VITE_COMMIT_HASH;
const MODE = import.meta.env.MODE;
const showConfig = ref(0);
watch(showFormatted, async () => {
if (!showFormatted.value) return;
formatting.value = true;
while (!mergedEditor.value)
await new Promise((resolve) => setTimeout(resolve, 20));
const solution = await formatSolution(code.value, {
clang: {
enabled: refStore.clangEnabled.value,
config: refStore.clangConfig.value,
},
});
formatting.value = false;
new MergeView({
a: {
doc: code.value,
Expand All @@ -52,7 +81,7 @@ watch(showFormatted, async () => {
],
},
b: {
doc: await formatSolution(code.value),
doc: solution,
extensions: [
EditorState.readOnly.of(true),
basicSetup,
Expand All @@ -69,6 +98,10 @@ watch(showFormatted, async () => {
<div class="p-3">
<h1 class="text-2xl inline-block">洛谷题解格式化工具</h1>
<span v-if="showFormatted"> | 差异对比</span>
<span v-else>
| By
<a href="https://imken.moe" class="link link-primary">Imken</a>
</span>
</div>
<div class="w-full">
<div v-if="!showFormatted">
Expand All @@ -87,25 +120,42 @@ watch(showFormatted, async () => {
<div class="p-3">
<div class="join">
<button
class="btn join-item btn-sm btn-neutral"
class="btn join-item btn-sm btn-neutral w-36"
v-on:click="() => (showFormatted = !showFormatted)"
:disabled="formatting"
>
{{ showFormatted ? "返回" : "格式化并展示差异" }}
<span
v-if="formatting"
class="loading loading-ring loading-md"
></span>
<span v-else>
{{ showFormatted ? "返回" : "格式化并展示差异" }}
</span>
</button>
<button
class="btn join-item btn-sm btn-primary w-28"
v-on:click="formatAndCopy"
v-if="!showFormatted"
:disabled="formatting"
>
格式化并复制
</button>
<button
class="btn join-item btn-sm"
v-on:click="formatRaw"
v-if="!showFormatted"
:disabled="formatting"
>
仅格式化
</button>
<button
class="btn join-item btn-sm"
v-on:click="() => showConfig++"
v-if="!showFormatted"
:disabled="formatting"
>
选项设置
</button>
</div>
<button class="btn btn-outline btn-secondary btn-sm mx-2" v-if="showDone">
完成
Expand All @@ -132,6 +182,8 @@ watch(showFormatted, async () => {
<code>{{ COMMIT_HASH.substring(0, 7) }}</code>
</p>
</div>

<Config :open="showConfig" />
</template>

<style>
Expand Down
59 changes: 59 additions & 0 deletions packages/solfmt-web/src/components/Config.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<script lang="ts" setup>
import { storeToRefs } from "pinia";
import { useStore } from "../store";
import { ref, watch } from "vue";
const store = useStore();
const { clangEnabled, clangConfig } = storeToRefs(store);
const model = ref<HTMLDialogElement | null>(null);
const props = defineProps({ open: Number });
watch(props, () => {
if (props.open) model.value?.showModal();
});
const clear = () => localStorage.removeItem("state");
</script>

<template>
<dialog ref="model" id="configModel" class="modal">
<div class="modal-box">
<h3 class="text-lg font-bold">选项设置</h3>
<div class="form-control">
<div class="pb-5">
<label class="label cursor-pointer">
<span class="label-text">启用 clang-format (实验性)</span>
<input type="checkbox" class="toggle" v-model="clangEnabled" />
</label>

<label class="label cursor-pointer" v-if="clangEnabled">
<span class="label-text">
clang-format 设置
<a
href="https://clang.llvm.org/docs/ClangFormatStyleOptions.html"
class="link link-primary"
>帮助</a>
</span>
</label>
<textarea
v-if="clangEnabled"
v-model="clangConfig"
class="textarea textarea-bordered w-full border min-h-40 font-['Fira_Code']"
></textarea>
</div>

<button class="btn btn-sm" v-on:click="() => (clear(), store.$reset())">
重置本地数据
</button>
</div>

<div class="modal-action">
<form method="dialog">
<button class="btn">关闭</button>
</form>
</div>
</div>
</dialog>
</template>
Loading

0 comments on commit 739bfdc

Please sign in to comment.