20 Commits

Author SHA1 Message Date
0338351680 Improve app launch speed 2025-09-21 03:04:23 +08:00
e372a0dd7c 🎨 Modify the rendering logic of the checkbox 2025-09-21 01:14:33 +08:00
d597f379ff 🚚 Refactor directory structure 2025-09-21 00:11:40 +08:00
9222a52d91 Modified sql prettier plugin 2025-09-20 19:18:33 +08:00
c3670bb8cd Added dockerfile、lua prettier plugin 2025-09-20 01:30:16 +08:00
2ea3456ff7 Added dart prettier plugin 2025-09-19 19:41:48 +08:00
c9379f0edb Added python prettier plugin 2025-09-19 19:35:48 +08:00
f72010bd69 Added clang prettier plugin 2025-09-19 19:17:13 +08:00
cd027097f8 🐛 Fixed golang prettier plugin issue 2025-09-19 18:39:41 +08:00
9cbbf729c0 🔥 Remove powershell prettier plugin 2025-09-18 00:13:07 +08:00
landaiqing
c26c11e253 🐛 Fixed build issue 2025-09-17 09:54:43 +08:00
338ac358db 🚧 Modify toml,powershell prettier plugin(beta) 2025-09-17 00:12:39 +08:00
a83c7139c9 Added scala、powershell、groovy prettier plugin 2025-09-14 23:45:01 +08:00
42c7d11c09 📦 Optimized packaging 2025-09-13 20:25:19 +08:00
5ca5aa64c7 Added shell prettier plugin 2025-09-13 19:21:06 +08:00
eda7ef771e Added rust prettier plugin 2025-09-13 00:02:17 +08:00
593c4d7783 Added java prettier plugin 2025-09-12 23:01:19 +08:00
d24a522b32 Added php prettier plugin 2025-09-12 20:15:56 +08:00
41afb834ae Added sql prettier plugin 2025-09-12 00:52:19 +08:00
b745329e26 Added golang prettier plugin 2025-09-11 20:42:39 +08:00
196 changed files with 24529 additions and 937 deletions

View File

@@ -18,7 +18,6 @@ import * as ThemeService from "./themeservice.js";
import * as TranslationService from "./translationservice.js";
import * as TrayService from "./trayservice.js";
import * as WindowService from "./windowservice.js";
import * as WindowSnapService from "./windowsnapservice.js";
export {
BackupService,
ConfigService,
@@ -36,8 +35,7 @@ export {
ThemeService,
TranslationService,
TrayService,
WindowService,
WindowSnapService
WindowService
};
export * from "./models.js";

View File

@@ -1,79 +0,0 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
/**
* WindowSnapService 窗口吸附服务
* @module
*/
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import {Call as $Call, Create as $Create} from "@wailsio/runtime";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js";
/**
* Cleanup 清理资源
*/
export function Cleanup(): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(2155505498) as any;
return $resultPromise;
}
/**
* GetCurrentThreshold 获取当前自适应阈值(用于调试或显示)
*/
export function GetCurrentThreshold(): Promise<number> & { cancel(): void } {
let $resultPromise = $Call.ByID(3176419026) as any;
return $resultPromise;
}
/**
* OnWindowSnapConfigChanged 处理窗口吸附配置变更
*/
export function OnWindowSnapConfigChanged(enabled: boolean): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(3794787039, enabled) as any;
return $resultPromise;
}
/**
* RegisterWindow 注册需要吸附管理的窗口
*/
export function RegisterWindow(documentID: number, window: application$0.WebviewWindow | null, title: string): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(1000222723, documentID, window, title) as any;
return $resultPromise;
}
/**
* ServiceShutdown 实现服务关闭接口
*/
export function ServiceShutdown(): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(1172710495) as any;
return $resultPromise;
}
/**
* SetAppReferences 设置应用和主窗口引用
*/
export function SetAppReferences(app: application$0.App | null, mainWindow: application$0.WebviewWindow | null): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(1782093351, app, mainWindow) as any;
return $resultPromise;
}
/**
* SetSnapEnabled 设置是否启用窗口吸附
*/
export function SetSnapEnabled(enabled: boolean): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(2280126835, enabled) as any;
return $resultPromise;
}
/**
* UnregisterWindow 取消注册窗口
*/
export function UnregisterWindow(documentID: number): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(2844230768, documentID) as any;
return $resultPromise;
}

View File

@@ -9,5 +9,6 @@
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<script src="/math.js"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -5,20 +5,20 @@
"type": "module",
"scripts": {
"dev": "vite --host --mode development",
"build:dev": "vue-tsc && vite build --minify false --mode development",
"build": "vue-tsc && vite build --mode production",
"build:dev": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" vue-tsc && cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" vite build --minify false --mode development",
"build": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" vue-tsc && cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" vite build --mode production",
"preview": "vite preview",
"lint": "eslint",
"lint:fix": "eslint --fix"
},
"dependencies": {
"@codemirror/autocomplete": "^6.18.6",
"@codemirror/autocomplete": "^6.18.7",
"@codemirror/commands": "^6.8.1",
"@codemirror/lang-angular": "^0.1.4",
"@codemirror/lang-cpp": "^6.0.3",
"@codemirror/lang-css": "^6.3.1",
"@codemirror/lang-go": "^6.0.1",
"@codemirror/lang-html": "^6.4.9",
"@codemirror/lang-html": "^6.4.10",
"@codemirror/lang-java": "^6.0.2",
"@codemirror/lang-javascript": "^6.2.4",
"@codemirror/lang-json": "^6.0.2",
@@ -33,7 +33,6 @@
"@codemirror/lang-sql": "^6.9.1",
"@codemirror/lang-vue": "^0.1.3",
"@codemirror/lang-wast": "^6.0.2",
"@codemirror/lang-xml": "^6.1.0",
"@codemirror/lang-yaml": "^6.1.2",
"@codemirror/language": "^6.11.3",
"@codemirror/language-data": "^6.5.1",
@@ -41,40 +40,52 @@
"@codemirror/lint": "^6.8.5",
"@codemirror/search": "^6.5.11",
"@codemirror/state": "^6.5.2",
"@codemirror/view": "^6.38.1",
"@codemirror/view": "^6.38.2",
"@cospaia/prettier-plugin-clojure": "^0.0.2",
"@lezer/highlight": "^1.2.1",
"@lezer/lr": "^1.4.2",
"@prettier/plugin-xml": "^3.4.2",
"@reteps/dockerfmt": "^0.3.6",
"@toml-tools/lexer": "^1.0.0",
"@toml-tools/parser": "^1.0.0",
"codemirror": "^6.0.2",
"codemirror-lang-elixir": "^4.0.0",
"colors-named": "^1.0.2",
"colors-named-hex": "^1.0.2",
"franc-min": "^6.2.0",
"groovy-beautify": "^0.0.17",
"hsl-matcher": "^1.2.4",
"lezer": "^0.13.5",
"java-parser": "^3.0.1",
"jsox": "^1.2.123",
"linguist-languages": "^9.0.0",
"php-parser": "^3.2.5",
"pinia": "^3.0.3",
"pinia-plugin-persistedstate": "^4.5.0",
"prettier": "^3.6.2",
"remarkable": "^2.0.1",
"sass": "^1.90.0",
"vue": "^3.5.19",
"vue-i18n": "^11.1.11",
"sass": "^1.92.1",
"sh-syntax": "^0.5.8",
"vue": "^3.5.21",
"vue-i18n": "^11.1.12",
"vue-pick-colors": "^1.8.0",
"vue-router": "^4.5.1"
},
"devDependencies": {
"@eslint/js": "^9.34.0",
"@eslint/js": "^9.35.0",
"@lezer/generator": "^1.8.0",
"@types/node": "^24.3.0",
"@types/node": "^24.3.1",
"@types/remarkable": "^2.0.8",
"@vitejs/plugin-vue": "^6.0.1",
"@wailsio/runtime": "latest",
"eslint": "^9.34.0",
"cross-env": "^7.0.3",
"eslint": "^9.35.0",
"eslint-plugin-vue": "^10.4.0",
"globals": "^16.3.0",
"globals": "^16.4.0",
"typescript": "^5.9.2",
"typescript-eslint": "^8.40.0",
"typescript-eslint": "^8.43.0",
"unplugin-vue-components": "^29.0.0",
"vite": "^7.1.3",
"vite": "^7.1.5",
"vite-plugin-node-polyfills": "^0.24.0",
"vue-eslint-parser": "^10.2.0",
"vue-tsc": "^3.0.6"
}

View File

@@ -1,7 +1,5 @@
importScripts("guesslang.min.js")
const LANGUAGES = ["json", "py", "html", "sql", "md", "java", "php", "css", "xml", "cpp", "rs", "cs", "rb", "sh", "yaml", "toml", "go", "clj", "ex", "erl", "js", "ts", "swift", "kt", "groovy", "ps1", "dart", "scala"]
const guessLang = new self.GuessLang()
function sendResult(language, confidence, idx) {
@@ -27,20 +25,13 @@ onmessage = (event) => {
guessLang.runModel(content).then((result) => {
if (result.length > 0) {
const lang = result[0]
if (LANGUAGES.includes(lang.languageId) && lang.confidence > 0.15) {
sendResult(lang.languageId, lang.confidence, idx)
// 返回置信度最高的结果
const bestResult = result[0]
if (bestResult.confidence > 0.15) {
sendResult(bestResult.languageId, bestResult.confidence, idx)
return
}
}
for (let lang of result) {
if (LANGUAGES.includes(lang.languageId) && lang.confidence > 0.5) {
sendResult(lang.languageId, lang.confidence, idx)
return
}
}
sendResult("text", 0.0, idx)
}).catch(() => {
sendResult("text", 0.0, idx)

3
frontend/public/math.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,10 @@
import fs from "node:fs/promises";
import initAsync from "./clang-format.js";
const wasm = new URL("./clang-format.wasm", import.meta.url);
export default function (init = fs.readFile(wasm)) {
return initAsync(init);
}
export * from "./clang-format.js";

View File

@@ -0,0 +1,8 @@
import initAsync from "./clang-format.js";
import wasm from "./clang-format.wasm?url";
export default function (input = wasm) {
return initAsync(input);
}
export * from "./clang-format.js";

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
Module.preRun = function customPreRun() {
ENV.PWD = process.cwd();
}

View File

@@ -0,0 +1,858 @@
#!/usr/bin/env python3
#
# ===- git-clang-format - ClangFormat Git Integration -------*- python -*--=== #
#
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
# ===----------------------------------------------------------------------=== #
r"""
clang-format git integration
============================
This file provides a clang-format integration for git. Put it somewhere in your
path and ensure that it is executable. Then, "git clang-format" will invoke
clang-format on the changes in current files or a specific commit.
For further details, run:
git clang-format -h
Requires Python version >=3.8
"""
from __future__ import absolute_import, division, print_function
import argparse
import collections
import contextlib
import errno
import os
import re
import subprocess
import sys
usage = "git clang-format [OPTIONS] [<commit>] [<commit>|--staged] [--] [<file>...]"
desc = """
If zero or one commits are given, run clang-format on all lines that differ
between the working directory and <commit>, which defaults to HEAD. Changes are
only applied to the working directory, or in the stage/index.
Examples:
To format staged changes, i.e everything that's been `git add`ed:
git clang-format
To also format everything touched in the most recent commit:
git clang-format HEAD~1
If you're on a branch off main, to format everything touched on your branch:
git clang-format main
If two commits are given (requires --diff), run clang-format on all lines in the
second <commit> that differ from the first <commit>.
The following git-config settings set the default of the corresponding option:
clangFormat.binary
clangFormat.commit
clangFormat.extensions
clangFormat.style
"""
# Name of the temporary index file in which save the output of clang-format.
# This file is created within the .git directory.
temp_index_basename = "clang-format-index"
Range = collections.namedtuple("Range", "start, count")
def main():
config = load_git_config()
# In order to keep '--' yet allow options after positionals, we need to
# check for '--' ourselves. (Setting nargs='*' throws away the '--', while
# nargs=argparse.REMAINDER disallows options after positionals.)
argv = sys.argv[1:]
try:
idx = argv.index("--")
except ValueError:
dash_dash = []
else:
dash_dash = argv[idx:]
argv = argv[:idx]
default_extensions = ",".join(
[
# From clang/lib/Frontend/FrontendOptions.cpp, all lower case
"c",
"h", # C
"m", # ObjC
"mm", # ObjC++
"cc",
"cp",
"cpp",
"c++",
"cxx",
"hh",
"hpp",
"hxx",
"inc", # C++
"ccm",
"cppm",
"cxxm",
"c++m", # C++ Modules
"cu",
"cuh", # CUDA
"cl", # OpenCL
# Other languages that clang-format supports
"proto",
"protodevel", # Protocol Buffers
"java", # Java
"js",
"mjs",
"cjs", # JavaScript
"ts", # TypeScript
"cs", # C Sharp
"json",
"ipynb", # Json
"sv",
"svh",
"v",
"vh", # Verilog
"td", # TableGen
"txtpb",
"textpb",
"pb.txt",
"textproto",
"asciipb", # TextProto
]
)
p = argparse.ArgumentParser(
usage=usage,
formatter_class=argparse.RawDescriptionHelpFormatter,
description=desc,
)
p.add_argument(
"--binary",
default=config.get("clangformat.binary", "clang-format"),
help="path to clang-format",
),
p.add_argument(
"--commit",
default=config.get("clangformat.commit", "HEAD"),
help="default commit to use if none is specified",
),
p.add_argument(
"--diff",
action="store_true",
help="print a diff instead of applying the changes",
)
p.add_argument(
"--diffstat",
action="store_true",
help="print a diffstat instead of applying the changes",
)
p.add_argument(
"--extensions",
default=config.get("clangformat.extensions", default_extensions),
help=(
"comma-separated list of file extensions to format, "
"excluding the period and case-insensitive"
),
),
p.add_argument(
"-f",
"--force",
action="store_true",
help="allow changes to unstaged files",
)
p.add_argument(
"-p", "--patch", action="store_true", help="select hunks interactively"
)
p.add_argument(
"-q",
"--quiet",
action="count",
default=0,
help="print less information",
)
p.add_argument(
"--staged",
"--cached",
action="store_true",
help="format lines in the stage instead of the working dir",
)
p.add_argument(
"--style",
default=config.get("clangformat.style", None),
help="passed to clang-format",
),
p.add_argument(
"-v",
"--verbose",
action="count",
default=0,
help="print extra information",
)
p.add_argument(
"--diff_from_common_commit",
action="store_true",
help=(
"diff from the last common commit for commits in "
"separate branches rather than the exact point of the "
"commits"
),
)
# We gather all the remaining positional arguments into 'args' since we need
# to use some heuristics to determine whether or not <commit> was present.
# However, to print pretty messages, we make use of metavar and help.
p.add_argument(
"args",
nargs="*",
metavar="<commit>",
help="revision from which to compute the diff",
)
p.add_argument(
"ignored",
nargs="*",
metavar="<file>...",
help="if specified, only consider differences in these files",
)
opts = p.parse_args(argv)
opts.verbose -= opts.quiet
del opts.quiet
commits, files = interpret_args(opts.args, dash_dash, opts.commit)
if len(commits) > 2:
die("at most two commits allowed; %d given" % len(commits))
if len(commits) == 2:
if opts.staged:
die("--staged is not allowed when two commits are given")
if not opts.diff:
die("--diff is required when two commits are given")
elif opts.diff_from_common_commit:
die("--diff_from_common_commit is only allowed when two commits are given")
if os.path.dirname(opts.binary):
opts.binary = os.path.abspath(opts.binary)
changed_lines = compute_diff_and_extract_lines(
commits, files, opts.staged, opts.diff_from_common_commit
)
if opts.verbose >= 1:
ignored_files = set(changed_lines)
filter_by_extension(changed_lines, opts.extensions.lower().split(","))
# The computed diff outputs absolute paths, so we must cd before accessing
# those files.
cd_to_toplevel()
filter_symlinks(changed_lines)
filter_ignored_files(changed_lines, binary=opts.binary)
if opts.verbose >= 1:
ignored_files.difference_update(changed_lines)
if ignored_files:
print(
"Ignoring the following files (wrong extension, symlink, or "
"ignored by clang-format):"
)
for filename in ignored_files:
print(" %s" % filename)
if changed_lines:
print("Running clang-format on the following files:")
for filename in changed_lines:
print(" %s" % filename)
if not changed_lines:
if opts.verbose >= 0:
print("no modified files to format")
return 0
if len(commits) > 1:
old_tree = commits[1]
revision = old_tree
elif opts.staged:
old_tree = create_tree_from_index(changed_lines)
revision = ""
else:
old_tree = create_tree_from_workdir(changed_lines)
revision = None
new_tree = run_clang_format_and_save_to_tree(
changed_lines, revision, binary=opts.binary, style=opts.style
)
if opts.verbose >= 1:
print("old tree: %s" % old_tree)
print("new tree: %s" % new_tree)
if old_tree == new_tree:
if opts.verbose >= 0:
print("clang-format did not modify any files")
return 0
if opts.diff:
return print_diff(old_tree, new_tree)
if opts.diffstat:
return print_diffstat(old_tree, new_tree)
changed_files = apply_changes(
old_tree, new_tree, force=opts.force, patch_mode=opts.patch
)
if (opts.verbose >= 0 and not opts.patch) or opts.verbose >= 1:
print("changed files:")
for filename in changed_files:
print(" %s" % filename)
return 1
def load_git_config(non_string_options=None):
"""Return the git configuration as a dictionary.
All options are assumed to be strings unless in `non_string_options`, in
which is a dictionary mapping option name (in lower case) to either "--bool"
or "--int"."""
if non_string_options is None:
non_string_options = {}
out = {}
for entry in run("git", "config", "--list", "--null").split("\0"):
if entry:
if "\n" in entry:
name, value = entry.split("\n", 1)
else:
# A setting with no '=' ('\n' with --null) is implicitly 'true'
name = entry
value = "true"
if name in non_string_options:
value = run("git", "config", non_string_options[name], name)
out[name] = value
return out
def interpret_args(args, dash_dash, default_commit):
"""Interpret `args` as "[commits] [--] [files]" and return (commits, files).
It is assumed that "--" and everything that follows has been removed from
args and placed in `dash_dash`.
If "--" is present (i.e., `dash_dash` is non-empty), the arguments to its
left (if present) are taken as commits. Otherwise, the arguments are
checked from left to right if they are commits or files. If commits are not
given, a list with `default_commit` is used."""
if dash_dash:
if len(args) == 0:
commits = [default_commit]
else:
commits = args
for commit in commits:
object_type = get_object_type(commit)
if object_type not in ("commit", "tag"):
if object_type is None:
die("'%s' is not a commit" % commit)
else:
die(
"'%s' is a %s, but a commit was expected"
% (commit, object_type)
)
files = dash_dash[1:]
elif args:
commits = []
while args:
if not disambiguate_revision(args[0]):
break
commits.append(args.pop(0))
if not commits:
commits = [default_commit]
files = args
else:
commits = [default_commit]
files = []
return commits, files
def disambiguate_revision(value):
"""Returns True if `value` is a revision, False if it is a file, or dies."""
# If `value` is ambiguous (neither a commit nor a file), the following
# command will die with an appropriate error message.
run("git", "rev-parse", value, verbose=False)
object_type = get_object_type(value)
if object_type is None:
return False
if object_type in ("commit", "tag"):
return True
die("`%s` is a %s, but a commit or filename was expected" % (value, object_type))
def get_object_type(value):
"""Returns a string description of an object's type, or None if it is not
a valid git object."""
cmd = ["git", "cat-file", "-t", value]
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
if p.returncode != 0:
return None
return convert_string(stdout.strip())
def compute_diff_and_extract_lines(commits, files, staged, diff_common_commit):
"""Calls compute_diff() followed by extract_lines()."""
diff_process = compute_diff(commits, files, staged, diff_common_commit)
changed_lines = extract_lines(diff_process.stdout)
diff_process.stdout.close()
diff_process.wait()
if diff_process.returncode != 0:
# Assume error was already printed to stderr.
sys.exit(2)
return changed_lines
def compute_diff(commits, files, staged, diff_common_commit):
"""Return a subprocess object producing the diff from `commits`.
The return value's `stdin` file object will produce a patch with the
differences between the working directory (or stage if --staged is used) and
the first commit if a single one was specified, or the difference between
both specified commits, filtered on `files` (if non-empty).
Zero context lines are used in the patch."""
git_tool = "diff-index"
extra_args = []
if len(commits) == 2:
git_tool = "diff-tree"
if diff_common_commit:
commits = [f"{commits[0]}...{commits[1]}"]
elif staged:
extra_args += ["--cached"]
cmd = ["git", git_tool, "-p", "-U0"] + extra_args + commits + ["--"]
cmd.extend(files)
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
p.stdin.close()
return p
def extract_lines(patch_file):
"""Extract the changed lines in `patch_file`.
The return value is a dictionary mapping filename to a list of (start_line,
line_count) pairs.
The input must have been produced with ``-U0``, meaning unidiff format with
zero lines of context. The return value is a dict mapping filename to a
list of line `Range`s."""
matches = {}
for line in patch_file:
line = convert_string(line)
match = re.search(r"^\+\+\+\ [^/]+/(.*)", line)
if match:
filename = match.group(1).rstrip("\r\n\t")
match = re.search(r"^@@ -[0-9,]+ \+(\d+)(,(\d+))?", line)
if match:
start_line = int(match.group(1))
line_count = 1
if match.group(3):
line_count = int(match.group(3))
if line_count == 0:
line_count = 1
if start_line == 0:
continue
matches.setdefault(filename, []).append(Range(start_line, line_count))
return matches
def filter_by_extension(dictionary, allowed_extensions):
"""Delete every key in `dictionary` that doesn't have an allowed extension.
`allowed_extensions` must be a collection of lowercase file extensions,
excluding the period."""
allowed_extensions = frozenset(allowed_extensions)
for filename in list(dictionary.keys()):
base_ext = filename.rsplit(".", 1)
if len(base_ext) == 1 and "" in allowed_extensions:
continue
if len(base_ext) == 1 or base_ext[1].lower() not in allowed_extensions:
del dictionary[filename]
def filter_symlinks(dictionary):
"""Delete every key in `dictionary` that is a symlink."""
for filename in list(dictionary.keys()):
if os.path.islink(filename):
del dictionary[filename]
def filter_ignored_files(dictionary, binary):
"""Delete every key in `dictionary` that is ignored by clang-format."""
ignored_files = run(binary, "-list-ignored", *dictionary.keys())
if not ignored_files:
return
ignored_files = ignored_files.split("\n")
for filename in ignored_files:
del dictionary[filename]
def cd_to_toplevel():
"""Change to the top level of the git repository."""
toplevel = run("git", "rev-parse", "--show-toplevel")
os.chdir(toplevel)
def create_tree_from_workdir(filenames):
"""Create a new git tree with the given files from the working directory.
Returns the object ID (SHA-1) of the created tree."""
return create_tree(filenames, "--stdin")
def create_tree_from_index(filenames):
# Copy the environment, because the files have to be read from the original
# index.
env = os.environ.copy()
def index_contents_generator():
for filename in filenames:
git_ls_files_cmd = [
"git",
"ls-files",
"--stage",
"-z",
"--",
filename,
]
git_ls_files = subprocess.Popen(
git_ls_files_cmd,
env=env,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
)
stdout = git_ls_files.communicate()[0]
yield convert_string(stdout.split(b"\0")[0])
return create_tree(index_contents_generator(), "--index-info")
def run_clang_format_and_save_to_tree(
changed_lines, revision=None, binary="clang-format", style=None
):
"""Run clang-format on each file and save the result to a git tree.
Returns the object ID (SHA-1) of the created tree."""
# Copy the environment when formatting the files in the index, because the
# files have to be read from the original index.
env = os.environ.copy() if revision == "" else None
def iteritems(container):
try:
return container.iteritems() # Python 2
except AttributeError:
return container.items() # Python 3
def index_info_generator():
for filename, line_ranges in iteritems(changed_lines):
if revision is not None:
if len(revision) > 0:
git_metadata_cmd = [
"git",
"ls-tree",
"%s:%s" % (revision, os.path.dirname(filename)),
os.path.basename(filename),
]
else:
git_metadata_cmd = [
"git",
"ls-files",
"--stage",
"--",
filename,
]
git_metadata = subprocess.Popen(
git_metadata_cmd,
env=env,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
)
stdout = git_metadata.communicate()[0]
mode = oct(int(stdout.split()[0], 8))
else:
mode = oct(os.stat(filename).st_mode)
# Adjust python3 octal format so that it matches what git expects
if mode.startswith("0o"):
mode = "0" + mode[2:]
blob_id = clang_format_to_blob(
filename,
line_ranges,
revision=revision,
binary=binary,
style=style,
env=env,
)
yield "%s %s\t%s" % (mode, blob_id, filename)
return create_tree(index_info_generator(), "--index-info")
def create_tree(input_lines, mode):
"""Create a tree object from the given input.
If mode is '--stdin', it must be a list of filenames. If mode is
'--index-info' is must be a list of values suitable for "git update-index
--index-info", such as "<mode> <SP> <sha1> <TAB> <filename>". Any other
mode is invalid."""
assert mode in ("--stdin", "--index-info")
cmd = ["git", "update-index", "--add", "-z", mode]
with temporary_index_file():
p = subprocess.Popen(cmd, stdin=subprocess.PIPE)
for line in input_lines:
p.stdin.write(to_bytes("%s\0" % line))
p.stdin.close()
if p.wait() != 0:
die("`%s` failed" % " ".join(cmd))
tree_id = run("git", "write-tree")
return tree_id
def clang_format_to_blob(
filename,
line_ranges,
revision=None,
binary="clang-format",
style=None,
env=None,
):
"""Run clang-format on the given file and save the result to a git blob.
Runs on the file in `revision` if not None, or on the file in the working
directory if `revision` is None. Revision can be set to an empty string to
run clang-format on the file in the index.
Returns the object ID (SHA-1) of the created blob."""
clang_format_cmd = [binary]
if style:
clang_format_cmd.extend(["--style=" + style])
clang_format_cmd.extend(
[
"--lines=%s:%s" % (start_line, start_line + line_count - 1)
for start_line, line_count in line_ranges
]
)
if revision is not None:
clang_format_cmd.extend(["--assume-filename=" + filename])
git_show_cmd = [
"git",
"cat-file",
"blob",
"%s:%s" % (revision, filename),
]
git_show = subprocess.Popen(
git_show_cmd, env=env, stdin=subprocess.PIPE, stdout=subprocess.PIPE
)
git_show.stdin.close()
clang_format_stdin = git_show.stdout
else:
clang_format_cmd.extend([filename])
git_show = None
clang_format_stdin = subprocess.PIPE
try:
clang_format = subprocess.Popen(
clang_format_cmd, stdin=clang_format_stdin, stdout=subprocess.PIPE
)
if clang_format_stdin == subprocess.PIPE:
clang_format_stdin = clang_format.stdin
except OSError as e:
if e.errno == errno.ENOENT:
die('cannot find executable "%s"' % binary)
else:
raise
clang_format_stdin.close()
hash_object_cmd = [
"git",
"hash-object",
"-w",
"--path=" + filename,
"--stdin",
]
hash_object = subprocess.Popen(
hash_object_cmd, stdin=clang_format.stdout, stdout=subprocess.PIPE
)
clang_format.stdout.close()
stdout = hash_object.communicate()[0]
if hash_object.returncode != 0:
die("`%s` failed" % " ".join(hash_object_cmd))
if clang_format.wait() != 0:
die("`%s` failed" % " ".join(clang_format_cmd))
if git_show and git_show.wait() != 0:
die("`%s` failed" % " ".join(git_show_cmd))
return convert_string(stdout).rstrip("\r\n")
@contextlib.contextmanager
def temporary_index_file(tree=None):
"""Context manager for setting GIT_INDEX_FILE to a temporary file and
deleting the file afterward."""
index_path = create_temporary_index(tree)
old_index_path = os.environ.get("GIT_INDEX_FILE")
os.environ["GIT_INDEX_FILE"] = index_path
try:
yield
finally:
if old_index_path is None:
del os.environ["GIT_INDEX_FILE"]
else:
os.environ["GIT_INDEX_FILE"] = old_index_path
os.remove(index_path)
def create_temporary_index(tree=None):
"""Create a temporary index file and return the created file's path.
If `tree` is not None, use that as the tree to read in. Otherwise, an
empty index is created."""
gitdir = run("git", "rev-parse", "--git-dir")
path = os.path.join(gitdir, temp_index_basename)
if tree is None:
tree = "--empty"
run("git", "read-tree", "--index-output=" + path, tree)
return path
def print_diff(old_tree, new_tree):
"""Print the diff between the two trees to stdout."""
# We use the porcelain 'diff' and not plumbing 'diff-tree' because the
# output is expected to be viewed by the user, and only the former does nice
# things like color and pagination.
#
# We also only print modified files since `new_tree` only contains the files
# that were modified, so unmodified files would show as deleted without the
# filter.
return subprocess.run(
["git", "diff", "--diff-filter=M", "--exit-code", old_tree, new_tree]
).returncode
def print_diffstat(old_tree, new_tree):
"""Print the diffstat between the two trees to stdout."""
# We use the porcelain 'diff' and not plumbing 'diff-tree' because the
# output is expected to be viewed by the user, and only the former does nice
# things like color and pagination.
#
# We also only print modified files since `new_tree` only contains the files
# that were modified, so unmodified files would show as deleted without the
# filter.
return subprocess.run(
[
"git",
"diff",
"--diff-filter=M",
"--exit-code",
"--stat",
old_tree,
new_tree,
]
).returncode
def apply_changes(old_tree, new_tree, force=False, patch_mode=False):
"""Apply the changes in `new_tree` to the working directory.
Bails if there are local changes in those files and not `force`. If
`patch_mode`, runs `git checkout --patch` to select hunks interactively."""
changed_files = (
run(
"git",
"diff-tree",
"--diff-filter=M",
"-r",
"-z",
"--name-only",
old_tree,
new_tree,
)
.rstrip("\0")
.split("\0")
)
if not force:
unstaged_files = run("git", "diff-files", "--name-status", *changed_files)
if unstaged_files:
print(
"The following files would be modified but have unstaged changes:",
file=sys.stderr,
)
print(unstaged_files, file=sys.stderr)
print("Please commit, stage, or stash them first.", file=sys.stderr)
sys.exit(2)
if patch_mode:
# In patch mode, we could just as well create an index from the new tree
# and checkout from that, but then the user will be presented with a
# message saying "Discard ... from worktree". Instead, we use the old
# tree as the index and checkout from new_tree, which gives the slightly
# better message, "Apply ... to index and worktree". This is not quite
# right, since it won't be applied to the user's index, but oh well.
with temporary_index_file(old_tree):
subprocess.run(["git", "checkout", "--patch", new_tree], check=True)
index_tree = old_tree
else:
with temporary_index_file(new_tree):
run("git", "checkout-index", "-f", "--", *changed_files)
return changed_files
def run(*args, **kwargs):
stdin = kwargs.pop("stdin", "")
verbose = kwargs.pop("verbose", True)
strip = kwargs.pop("strip", True)
for name in kwargs:
raise TypeError("run() got an unexpected keyword argument '%s'" % name)
p = subprocess.Popen(
args,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE,
)
stdout, stderr = p.communicate(input=stdin)
stdout = convert_string(stdout)
stderr = convert_string(stderr)
if p.returncode == 0:
if stderr:
if verbose:
print("`%s` printed to stderr:" % " ".join(args), file=sys.stderr)
print(stderr.rstrip(), file=sys.stderr)
if strip:
stdout = stdout.rstrip("\r\n")
return stdout
if verbose:
print("`%s` returned %s" % (" ".join(args), p.returncode), file=sys.stderr)
if stderr:
print(stderr.rstrip(), file=sys.stderr)
sys.exit(2)
def die(message):
print("error:", message, file=sys.stderr)
sys.exit(2)
def to_bytes(str_input):
# Encode to UTF-8 to get binary data.
if isinstance(str_input, bytes):
return str_input
return str_input.encode("utf-8")
def to_string(bytes_input):
if isinstance(bytes_input, str):
return bytes_input
return bytes_input.encode("utf-8")
def convert_string(bytes_input):
try:
return to_string(bytes_input.decode("utf-8"))
except AttributeError: # 'str' object has no attribute 'decode'.
return str(bytes_input)
except UnicodeError:
return str(bytes_input)
if __name__ == "__main__":
sys.exit(main())

View File

@@ -0,0 +1,216 @@
/**
* Prettier Plugin for C/C++/C#/Java/Protobuf formatting using clang-format WebAssembly
*
* This plugin provides support for formatting multiple languages using the clang-format WASM implementation.
* Supported languages:
* - C / C++
* - Objective-C / Objective-C++
* - C#
* - Java
* - Protocol Buffer (Protobuf)
*
* It supports various file extensions and common clang-format styles.
*/
import type { Plugin, Parser, Printer } from 'prettier';
// Import the clang-format WASM module
import clangFormatInit, { format } from './clang-format-vite.js';
const parserName = 'clang-format';
// Language configuration
const languages = [
{
name: 'C',
aliases: ['c'],
parsers: ['c'],
extensions: ['.c', '.h'],
filenames: ['*.c', '*.h'],
aceMode: 'c_cpp',
tmScope: 'source.c',
linguistLanguageId: 50,
vscodeLanguageIds: ['c']
},
{
name: 'C++',
aliases: ['cpp', 'cxx', 'cc'],
parsers: ['cpp'],
extensions: ['.cpp', '.cxx', '.cc', '.hpp', '.hxx', '.hh', '.C', '.H'],
filenames: ['*.cpp', '*.cxx', '*.cc', '*.hpp', '*.hxx', '*.hh', '*.C', '*.H'],
aceMode: 'c_cpp',
tmScope: 'source.cpp',
linguistLanguageId: 43,
vscodeLanguageIds: ['cpp']
},
{
name: 'Objective-C',
aliases: ['objc', 'objectivec'],
parsers: ['objective-c'],
extensions: ['.m'],
filenames: ['*.m'],
aceMode: 'objectivec',
tmScope: 'source.objc',
linguistLanguageId: 259,
vscodeLanguageIds: ['objective-c']
},
{
name: 'Objective-C++',
aliases: ['objcpp', 'objectivecpp'],
parsers: ['objective-cpp'],
extensions: ['.mm'],
filenames: ['*.mm'],
aceMode: 'objectivec',
tmScope: 'source.objcpp',
linguistLanguageId: 260,
vscodeLanguageIds: ['objective-cpp']
},
{
name: 'C#',
aliases: ['csharp', 'cs'],
parsers: ['cs'],
extensions: ['.cs'],
filenames: ['*.cs'],
aceMode: 'csharp',
tmScope: 'source.cs',
linguistLanguageId: 42,
vscodeLanguageIds: ['csharp']
},
{
name: 'Java',
aliases: ['java'],
parsers: ['java'],
extensions: ['.java'],
filenames: ['*.java'],
aceMode: 'java',
tmScope: 'source.java',
linguistLanguageId: 181,
vscodeLanguageIds: ['java']
},
{
name: 'Protocol Buffer',
aliases: ['protobuf', 'proto'],
parsers: ['proto'],
extensions: ['.proto'],
filenames: ['*.proto'],
aceMode: 'protobuf',
tmScope: 'source.proto',
linguistLanguageId: 297,
vscodeLanguageIds: ['proto']
}
];
// Parser configuration
const clangParser: Parser<string> = {
astFormat: parserName,
parse: (text: string) => text,
locStart: () => 0,
locEnd: (node: string) => node.length,
};
// Lazy initialize clang-format WASM module
let initPromise: Promise<void> | null = null;
let isInitialized = false;
function initClangFormat(): Promise<void> {
if (isInitialized) {
return Promise.resolve();
}
if (!initPromise) {
initPromise = (async () => {
try {
await clangFormatInit();
isInitialized = true;
} catch (error) {
console.warn('Failed to initialize clang-format WASM module:', error);
initPromise = null;
throw error;
}
})();
}
return initPromise;
}
// Printer configuration
const clangPrinter: Printer<string> = {
// @ts-expect-error -- Support async printer like shell plugin
async print(path, options) {
try {
// Wait for initialization to complete
await initClangFormat();
const text = (path as any).getValue ? (path as any).getValue() : path.node;
const style = getClangStyle(options);
// Format using clang-format (synchronous call)
const formatted = format(text, options.filename, style);
return formatted.trim();
} catch (error) {
console.warn('clang-format failed:', error);
// Return original text if formatting fails
return (path as any).getValue ? (path as any).getValue() : path.node;
}
},
};
// Helper function to determine clang-format style
function getClangStyle(options: any): string {
// You can extend this to support more options
const style = options.clangStyle || 'LLVM';
// Support common styles
const validStyles = ['LLVM', 'Google', 'Chromium', 'Mozilla', 'WebKit', 'Microsoft', 'GNU'];
if (validStyles.includes(style)) {
return style;
}
// Default to LLVM style
return 'LLVM';
}
// Plugin options
const options = {
clangStyle: {
since: '0.0.1',
category: 'Format' as const,
type: 'choice' as const,
default: 'LLVM',
description: 'The clang-format style to use',
choices: [
{ value: 'LLVM', description: 'LLVM coding standards' },
{ value: 'Google', description: "Google's C++ style guide" },
{ value: 'Chromium', description: "Chromium's style guide" },
{ value: 'Mozilla', description: "Mozilla's style guide" },
{ value: 'WebKit', description: "WebKit's style guide" },
{ value: 'Microsoft', description: "Microsoft's style guide" },
{ value: 'GNU', description: 'GNU coding standards' }
]
},
filename: {
// since: '0.1.0',
category: 'Config',
type: 'string',
default: undefined,
description: 'Custom filename to use for web_fmt processing (affects language detection)',
}
};
// Plugin object
const clangPlugin: Plugin = {
languages,
parsers: {
[parserName]: clangParser,
},
printers: {
[parserName]: clangPrinter,
},
...options,
};
export default clangPlugin;
export { languages };
export const parsers = clangPlugin.parsers;
export const printers = clangPlugin.printers;

View File

@@ -0,0 +1,105 @@
cmake_minimum_required(VERSION 3.20)
project(clang-format-wasm)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Set Emscripten flags for WASM
if(EMSCRIPTEN)
set(CMAKE_EXECUTABLE_SUFFIX ".js")
# Common Emscripten flags
set(EMSCRIPTEN_FLAGS
-O3
-sWASM=1
-sEXPORTED_RUNTIME_METHODS=['ccall','cwrap']
-sALLOW_MEMORY_GROWTH=1
-sMODULARIZE=1
-sEXPORT_NAME='Module'
-sENVIRONMENT=web,webview,worker
-sUSE_ES6_IMPORT_META=0
--no-entry
)
# Library-specific flags
set(LIB_EMSCRIPTEN_FLAGS
${EMSCRIPTEN_FLAGS}
-sEXPORTED_FUNCTIONS=['_malloc','_free']
--bind
)
# CLI-specific flags
set(CLI_EMSCRIPTEN_FLAGS
${EMSCRIPTEN_FLAGS}
-sEXPORTED_FUNCTIONS=['_main']
-sINVOKE_RUN=0
-sNODERAWFS=1
)
endif()
# Find LLVM
find_package(LLVM REQUIRED CONFIG)
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
# Include LLVM headers
include_directories(${LLVM_INCLUDE_DIRS})
add_definitions(${LLVM_DEFINITIONS})
# Find Clang
find_package(Clang REQUIRED CONFIG)
# Get LLVM components
llvm_map_components_to_libnames(llvm_libs support core)
# Define source files
set(LIB_SOURCES
lib.cc
lib.h
binding.cc
)
set(CLI_SOURCES
cli.cc
)
# Create library target
add_executable(clang-format-wasm ${LIB_SOURCES})
# Link against Clang and LLVM libraries
target_link_libraries(clang-format-wasm
clangFormat
clangToolingCore
clangBasic
clangRewrite
${llvm_libs}
)
# Create CLI target
add_executable(clang-format-cli ${CLI_SOURCES})
target_link_libraries(clang-format-cli
clangFormat
clangToolingCore
clangBasic
clangRewrite
${llvm_libs}
)
# Set Emscripten flags
if(EMSCRIPTEN)
# Configure library target
set_target_properties(clang-format-wasm PROPERTIES
COMPILE_FLAGS "${LIB_EMSCRIPTEN_FLAGS}"
LINK_FLAGS "${LIB_EMSCRIPTEN_FLAGS}"
OUTPUT_NAME "clang-format-esm"
)
# Configure CLI target
set_target_properties(clang-format-cli PROPERTIES
COMPILE_FLAGS "${CLI_EMSCRIPTEN_FLAGS}"
LINK_FLAGS "${CLI_EMSCRIPTEN_FLAGS}"
OUTPUT_NAME "clang-format-cli"
)
endif()

View File

@@ -0,0 +1,117 @@
#include "CustomFileSystem.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Path.h"
#include <emscripten.h>
#include <iostream>
#include <string>
#include <system_error>
using namespace llvm;
using namespace llvm::vfs;
namespace {
bool isRunningOnWindows() {
return EM_ASM_INT({return process.platform == 'win32' ? 1 : 0}) == 1;
}
std::error_code current_path(SmallVectorImpl<char> &result) {
result.clear();
const char *pwd = ::getenv("PWD");
result.append(pwd, pwd + strlen(pwd));
return {};
}
} // namespace
namespace llvm {
namespace vfs {
sys::path::Style getPathStyle() {
static sys::path::Style cachedStyle = sys::path::Style::native;
if (cachedStyle == sys::path::Style::native) {
cachedStyle = isRunningOnWindows() ? sys::path::Style::windows
: sys::path::Style::posix;
}
return cachedStyle;
}
void make_absolute(const Twine &current_directory,
SmallVectorImpl<char> &path) {
StringRef p(path.data(), path.size());
auto pathStyle = getPathStyle();
bool rootDirectory = sys::path::has_root_directory(p, pathStyle);
bool rootName = sys::path::has_root_name(p, pathStyle);
// Already absolute.
if ((rootName || is_style_posix(pathStyle)) && rootDirectory)
return;
// All of the following conditions will need the current directory.
SmallString<128> current_dir;
current_directory.toVector(current_dir);
// Relative path. Prepend the current directory.
if (!rootName && !rootDirectory) {
// Append path to the current directory.
sys::path::append(current_dir, pathStyle, p);
// Set path to the result.
path.swap(current_dir);
return;
}
if (!rootName && rootDirectory) {
StringRef cdrn = sys::path::root_name(current_dir, pathStyle);
SmallString<128> curDirRootName(cdrn.begin(), cdrn.end());
sys::path::append(curDirRootName, pathStyle, p);
// Set path to the result.
path.swap(curDirRootName);
return;
}
if (rootName && !rootDirectory) {
StringRef pRootName = sys::path::root_name(p, pathStyle);
StringRef bRootDirectory =
sys::path::root_directory(current_dir, pathStyle);
StringRef bRelativePath = sys::path::relative_path(current_dir, pathStyle);
StringRef pRelativePath = sys::path::relative_path(p, pathStyle);
SmallString<128> res;
sys::path::append(res, pathStyle, pRootName, bRootDirectory, bRelativePath,
pRelativePath);
path.swap(res);
return;
}
llvm_unreachable("All rootName and rootDirectory combinations should have "
"occurred above!");
}
std::error_code make_absolute(SmallVectorImpl<char> &path) {
if (sys::path::is_absolute(path, getPathStyle()))
return {};
SmallString<128> current_dir;
if (std::error_code ec = current_path(current_dir))
return ec;
make_absolute(current_dir, path);
return {};
}
CustomFileSystem::CustomFileSystem(IntrusiveRefCntPtr<FileSystem> FS)
: ProxyFileSystem(std::move(FS)) {}
std::error_code
CustomFileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const {
return make_absolute(Path);
}
} // namespace vfs
} // namespace llvm

View File

@@ -0,0 +1,27 @@
#ifndef CUSTOM_FILE_SYSTEM_H
#define CUSTOM_FILE_SYSTEM_H
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/VirtualFileSystem.h"
namespace llvm {
namespace vfs {
sys::path::Style getPathStyle();
std::error_code make_absolute(SmallVectorImpl<char> &path);
class CustomFileSystem : public ProxyFileSystem {
public:
CustomFileSystem(IntrusiveRefCntPtr<FileSystem> FS);
std::error_code makeAbsolute(SmallVectorImpl<char> &Path) const override;
};
} // namespace vfs
} // namespace llvm
#endif // CUSTOM_FILE_SYSTEM_H

View File

@@ -0,0 +1,100 @@
# Clang Format WASM Plugin
这是一个基于 clang-format WebAssembly 的 Prettier 插件,支持格式化 C/C++/C#/Java/Protobuf 代码。
## 目录结构
```
clang/
├── src/ # 源码目录
│ ├── scripts/ # 构建和工具脚本
│ │ ├── build.sh # 主构建脚本
│ │ ├── gen_patch.sh # 补丁生成脚本
│ │ └── cli.patch # CLI 修改补丁
│ ├── *.cc # C++ 源文件
│ ├── *.h # C++ 头文件
│ ├── CMakeLists.txt # CMake 构建配置
│ ├── package.json # NPM 包配置
│ ├── clang-format.d.ts # TypeScript 类型定义
│ ├── template.js # JavaScript 模板
│ └── clang-format-diff.py # Python 差异工具
├── *.js # 编译后的 JavaScript 文件
├── *.wasm # 编译后的 WebAssembly 文件
├── *.cjs # CommonJS 格式的 CLI 工具
├── git-clang-format # Git 集成工具
└── index.ts # 插件入口文件
```
## 构建说明
### 前提条件
- Install LLVM and Clang (version 18 or later)
- Install CMake (version 3.27 or later)
- Install Ninja (version 1.11 or later)
### 构建步骤
1. Clone this repository
2. 进入源码目录:
```bash
cd src
```
3. 运行构建脚本:
```bash
./scripts/build.sh
```
构建脚本会:
- 创建 `build` 目录并编译源码
- 将编译结果复制到上级目录(插件目录)
- 生成 WebAssembly 文件和 JavaScript 绑定
- 复制必要的工具和类型定义文件
### 输出文件
构建完成后,插件目录下会包含:
- `clang-format.wasm` - WebAssembly 库文件
- `clang-format.js` - JavaScript 绑定文件
- `clang-format-cli.cjs` - CLI 工具
- `clang-format-cli.wasm` - CLI WebAssembly 文件
- `git-clang-format` - Git 集成工具
- `clang-format-diff.py` - 差异工具
## 开发说明
### 修改源码
- C++ 源文件位于 `src/` 目录下
- 修改后运行 `./scripts/build.sh` 重新构建
- 类型定义文件 `src/clang-format.d.ts` 需要与实际 API 保持同步
### 生成补丁
如果修改了 CLI 相关代码,可以使用:
```bash
./scripts/gen_patch.sh
```
生成补丁文件 `scripts/cli.patch`。
## 使用说明
插件会自动加载编译后的 WebAssembly 文件,支持以下语言:
- C/C++
- Objective-C/C++
- C#
- Java
- Protocol Buffer
支持的 clang-format 样式:
- LLVM
- Google
- Chromium
- Mozilla
- WebKit
- Microsoft
- GNU
- 自定义样式

View File

@@ -0,0 +1,26 @@
#include "lib.h"
#include <emscripten/bind.h>
using namespace emscripten;
EMSCRIPTEN_BINDINGS(my_module) {
register_vector<unsigned>("RangeList");
value_object<Result>("Result")
.field("error", &Result::error)
.field("content", &Result::content);
function<std::string>("version", &version);
function<Result, const std::string, const std::string, const std::string>(
"format", &format);
function<Result, const std::string, const std::string, const std::string,
const std::vector<unsigned>>("format_byte", &format_byte);
function<Result, const std::string, const std::string, const std::string,
const std::vector<unsigned>>("format_line", &format_line);
function<void, const std::string>("set_fallback_style", &set_fallback_style);
function<void, bool>("set_sort_includes", &set_sort_includes);
function<Result, const std::string, const std::string, const std::string>(
"dump_config", &dump_config);
}
int main(void) {}

View File

@@ -0,0 +1,197 @@
#!/usr/bin/env python3
#
# ===- clang-format-diff.py - ClangFormat Diff Reformatter ----*- python -*--===#
#
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
# ===------------------------------------------------------------------------===#
"""
This script reads input from a unified diff and reformats all the changed
lines. This is useful to reformat all the lines touched by a specific patch.
Example usage for git/svn users:
git diff -U0 --no-color --relative HEAD^ | {clang_format_diff} -p1 -i
svn diff --diff-cmd=diff -x-U0 | {clang_format_diff} -i
It should be noted that the filename contained in the diff is used unmodified
to determine the source file to update. Users calling this script directly
should be careful to ensure that the path in the diff is correct relative to the
current working directory.
"""
from __future__ import absolute_import, division, print_function
import argparse
import difflib
import re
import subprocess
import sys
if sys.version_info.major >= 3:
from io import StringIO
else:
from io import BytesIO as StringIO
def main():
parser = argparse.ArgumentParser(
description=__doc__.format(clang_format_diff="%(prog)s"),
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument(
"-i",
action="store_true",
default=False,
help="apply edits to files instead of displaying a diff",
)
parser.add_argument(
"-p",
metavar="NUM",
default=0,
help="strip the smallest prefix containing P slashes",
)
parser.add_argument(
"-regex",
metavar="PATTERN",
default=None,
help="custom pattern selecting file paths to reformat "
"(case sensitive, overrides -iregex)",
)
parser.add_argument(
"-iregex",
metavar="PATTERN",
default=r".*\.(?:cpp|cc|c\+\+|cxx|cppm|ccm|cxxm|c\+\+m|c|cl|h|hh|hpp"
r"|hxx|m|mm|inc|js|ts|proto|protodevel|java|cs|json|ipynb|s?vh?)",
help="custom pattern selecting file paths to reformat "
"(case insensitive, overridden by -regex)",
)
parser.add_argument(
"-sort-includes",
action="store_true",
default=False,
help="let clang-format sort include blocks",
)
parser.add_argument(
"-v",
"--verbose",
action="store_true",
help="be more verbose, ineffective without -i",
)
parser.add_argument(
"-style",
help="formatting style to apply (LLVM, GNU, Google, Chromium, "
"Microsoft, Mozilla, WebKit)",
)
parser.add_argument(
"-fallback-style",
help="The name of the predefined style used as a"
"fallback in case clang-format is invoked with"
"-style=file, but can not find the .clang-format"
"file to use.",
)
parser.add_argument(
"-binary",
default="clang-format",
help="location of binary to use for clang-format",
)
args = parser.parse_args()
# Extract changed lines for each file.
filename = None
lines_by_file = {}
for line in sys.stdin:
match = re.search(r"^\+\+\+\ (.*?/){%s}(.+)" % args.p, line.rstrip())
if match:
filename = match.group(2)
if filename is None:
continue
if args.regex is not None:
if not re.match("^%s$" % args.regex, filename):
continue
else:
if not re.match("^%s$" % args.iregex, filename, re.IGNORECASE):
continue
match = re.search(r"^@@.*\+(\d+)(?:,(\d+))?", line)
if match:
start_line = int(match.group(1))
line_count = 1
if match.group(2):
line_count = int(match.group(2))
# The input is something like
#
# @@ -1, +0,0 @@
#
# which means no lines were added.
if line_count == 0:
continue
# Also format lines range if line_count is 0 in case of deleting
# surrounding statements.
end_line = start_line
if line_count != 0:
end_line += line_count - 1
lines_by_file.setdefault(filename, []).extend(
["--lines", str(start_line) + ":" + str(end_line)]
)
# Reformat files containing changes in place.
has_diff = False
for filename, lines in lines_by_file.items():
if args.i and args.verbose:
print("Formatting {}".format(filename))
command = [args.binary, filename]
if args.i:
command.append("-i")
if args.sort_includes:
command.append("--sort-includes")
command.extend(lines)
if args.style:
command.extend(["--style", args.style])
if args.fallback_style:
command.extend(["--fallback-style", args.fallback_style])
try:
p = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=None,
stdin=subprocess.PIPE,
universal_newlines=True,
)
except OSError as e:
# Give the user more context when clang-format isn't
# found/isn't executable, etc.
raise RuntimeError(
'Failed to run "%s" - %s"' % (" ".join(command), e.strerror)
)
stdout, _stderr = p.communicate()
if p.returncode != 0:
return p.returncode
if not args.i:
with open(filename) as f:
code = f.readlines()
formatted_code = StringIO(stdout).readlines()
diff = difflib.unified_diff(
code,
formatted_code,
filename,
filename,
"(before formatting)",
"(after formatting)",
)
diff_string = "".join(diff)
if len(diff_string) > 0:
has_diff = True
sys.stdout.write(diff_string)
if has_diff:
return 1
if __name__ == "__main__":
sys.exit(main())

View File

@@ -0,0 +1,175 @@
export type InitInput =
| RequestInfo
| URL
| Response
| BufferSource
| WebAssembly.Module;
export default function init(input?: InitInput): Promise<void>;
/**
* The style to use for formatting.
* Supported style values are:
* - `LLVM` - A style complying with the LLVM coding standards.
* - `Google` - A style complying with Googles C++ style guide.
* - `Chromium` - A style complying with Chromiums style guide.
* - `Mozilla` - A style complying with Mozillas style guide.
* - `WebKit` - A style complying with WebKits style guide.
* - `Microsoft` - A style complying with Microsofts style guide.
* - `GNU` - A style complying with the GNU coding standards.
* - A string starting with `{`, for example: `{BasedOnStyle: Chromium, IndentWidth: 4, ...}`.
* - A string which represents `.clang-format` content.
*
*/
export type Style =
| "LLVM"
| "Google"
| "Chromium"
| "Mozilla"
| "WebKit"
| "Microsoft"
| "GNU"
| (string & {});
/**
* The filename to use for determining the language.
*/
export type Filename =
| "main.c"
| "main.cc"
| "main.cxx"
| "main.cpp"
| "main.java"
| "main.js"
| "main.mjs"
| "main.ts"
| "main.json"
| "main.m"
| "main.mm"
| "main.proto"
| "main.cs"
| (string & {});
/**
* Formats the given content using the specified style.
*
* @param {string} content - The content to format.
* @param {Filename} filename - The filename to use for determining the language.
* @param {Style} style - The style to use for formatting.
* Supported style values are:
* - `LLVM` - A style complying with the LLVM coding standards.
* - `Google` - A style complying with Googles C++ style guide.
* - `Chromium` - A style complying with Chromiums style guide.
* - `Mozilla` - A style complying with Mozillas style guide.
* - `WebKit` - A style complying with WebKits style guide.
* - `Microsoft` - A style complying with Microsofts style guide.
* - `GNU` - A style complying with the GNU coding standards.
* - A string starting with `{`, for example: `{BasedOnStyle: Chromium, IndentWidth: 4, ...}`.
* - A string which represents `.clang-format` content.
*
* @returns {string} The formatted content.
* @throws {Error}
*
* @see {@link https://clang.llvm.org/docs/ClangFormatStyleOptions.html}
*/
export declare function format(
content: string,
filename?: Filename,
style?: Style,
): string;
/**
* Both the startLine and endLine are 1-based.
*/
export type LineRange = [startLine: number, endLine: number];
/**
* Both the offset and length are measured in bytes.
*/
export type ByteRange = [offset: number, length: number];
/**
* Formats the specified range of lines in the given content using the specified style.
*
* @param {string} content - The content to format.
* @param {LineRange[]} range - Array<[startLine, endLine]> - The range of lines to format.
* Both startLine and endLine are 1-based.
* Multiple ranges can be formatted by specifying several lines arguments.
* @param {Filename} filename - The filename to use for determining the language.
* @param {Style} style - The style to use for formatting.
* Supported style values are:
* - `LLVM` - A style complying with the LLVM coding standards.
* - `Google` - A style complying with Googles C++ style guide.
* - `Chromium` - A style complying with Chromiums style guide.
* - `Mozilla` - A style complying with Mozillas style guide.
* - `WebKit` - A style complying with WebKits style guide.
* - `Microsoft` - A style complying with Microsofts style guide.
* - `GNU` - A style complying with the GNU coding standards.
* - A string starting with `{`, for example: `{BasedOnStyle: Chromium, IndentWidth: 4, ...}`.
* - A string which represents `.clang-format` content.
*
* @returns {string} The formatted content.
* @throws {Error}
*
* @see {@link https://clang.llvm.org/docs/ClangFormatStyleOptions.html}
*/
export declare function format_line_range(
content: string,
range: ByteRange[] | [[offset: number]],
filename?: Filename,
style?: Style,
): string;
/**
* @deprecated Use `format_line_range` instead.
*/
export declare function formatLineRange(
content: string,
range: ByteRange[] | [[offset: number]],
filename?: Filename,
style?: Style,
): string;
/**
* Formats the specified range of bytes in the given content using the specified style.
*
* @param {string} content - The content to format.
* @param {ByteRange[]} range - Array<[offset, length]> - The range of bytes to format.
* @param {Filename} filename - The filename to use for determining the language.
* @param {Style} style - The style to use for formatting.
* Supported style values are:
* - `LLVM` - A style complying with the LLVM coding standards.
* - `Google` - A style complying with Googles C++ style guide.
* - `Chromium` - A style complying with Chromiums style guide.
* - `Mozilla` - A style complying with Mozillas style guide.
* - `WebKit` - A style complying with WebKits style guide.
* - `Microsoft` - A style complying with Microsofts style guide.
* - `GNU` - A style complying with the GNU coding standards.
* - A string starting with `{`, for example: `{BasedOnStyle: Chromium, IndentWidth: 4, ...}`.
* - A string which represents `.clang-format` content.
*
* @returns {string} The formatted content.
* @throws {Error}
*
* @see {@link https://clang.llvm.org/docs/ClangFormatStyleOptions.html}
*/
export declare function format_byte_range(
content: string,
range: LineRange[],
filename?: Filename,
style?: Style,
): string;
/**
* @deprecated Use `format_byte_range` instead.
*/
export declare function formatByteRange(
content: string,
range: LineRange[],
filename?: Filename,
style?: Style,
): string;
export declare function version(): string;
export declare function set_fallback_style(style: Style): void;

View File

@@ -0,0 +1,748 @@
//===-- clang-format/ClangFormat.cpp - Clang format tool ------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file implements a clang-format tool that automatically formats
/// (fragments of) C++ code.
///
//===----------------------------------------------------------------------===//
#include "clang/../../lib/Format/MatchFilePath.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Version.h"
#include "clang/Format/Format.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/Process.h"
#include <fstream>
#include "CustomFileSystem.h"
using namespace llvm;
using clang::tooling::Replacements;
static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
// Mark all our options with this category, everything else (except for -version
// and -help) will be hidden.
static cl::OptionCategory ClangFormatCategory("Clang-format options");
static cl::list<unsigned>
Offsets("offset",
cl::desc("Format a range starting at this byte offset.\n"
"Multiple ranges can be formatted by specifying\n"
"several -offset and -length pairs.\n"
"Can only be used with one input file."),
cl::cat(ClangFormatCategory));
static cl::list<unsigned>
Lengths("length",
cl::desc("Format a range of this length (in bytes).\n"
"Multiple ranges can be formatted by specifying\n"
"several -offset and -length pairs.\n"
"When only a single -offset is specified without\n"
"-length, clang-format will format up to the end\n"
"of the file.\n"
"Can only be used with one input file."),
cl::cat(ClangFormatCategory));
static cl::list<std::string>
LineRanges("lines",
cl::desc("<start line>:<end line> - format a range of\n"
"lines (both 1-based).\n"
"Multiple ranges can be formatted by specifying\n"
"several -lines arguments.\n"
"Can't be used with -offset and -length.\n"
"Can only be used with one input file."),
cl::cat(ClangFormatCategory));
static cl::opt<std::string>
Style("style", cl::desc(clang::format::StyleOptionHelpDescription),
cl::init(clang::format::DefaultFormatStyle),
cl::cat(ClangFormatCategory));
static cl::opt<std::string>
FallbackStyle("fallback-style",
cl::desc("The name of the predefined style used as a\n"
"fallback in case clang-format is invoked with\n"
"-style=file, but can not find the .clang-format\n"
"file to use. Defaults to 'LLVM'.\n"
"Use -fallback-style=none to skip formatting."),
cl::init(clang::format::DefaultFallbackStyle),
cl::cat(ClangFormatCategory));
static cl::opt<std::string> AssumeFileName(
"assume-filename",
cl::desc("Set filename used to determine the language and to find\n"
".clang-format file.\n"
"Only used when reading from stdin.\n"
"If this is not passed, the .clang-format file is searched\n"
"relative to the current working directory when reading stdin.\n"
"Unrecognized filenames are treated as C++.\n"
"supported:\n"
" CSharp: .cs\n"
" Java: .java\n"
" JavaScript: .js .mjs .cjs .ts\n"
" Json: .json .ipynb\n"
" Objective-C: .m .mm\n"
" Proto: .proto .protodevel\n"
" TableGen: .td\n"
" TextProto: .txtpb .textpb .pb.txt .textproto .asciipb\n"
" Verilog: .sv .svh .v .vh"),
cl::init("<stdin>"), cl::cat(ClangFormatCategory));
static cl::opt<bool> Inplace("i",
cl::desc("Inplace edit <file>s, if specified."),
cl::cat(ClangFormatCategory));
static cl::opt<bool> OutputXML("output-replacements-xml",
cl::desc("Output replacements as XML."),
cl::cat(ClangFormatCategory));
static cl::opt<bool>
DumpConfig("dump-config",
cl::desc("Dump configuration options to stdout and exit.\n"
"Can be used with -style option."),
cl::cat(ClangFormatCategory));
static cl::opt<unsigned>
Cursor("cursor",
cl::desc("The position of the cursor when invoking\n"
"clang-format from an editor integration"),
cl::init(0), cl::cat(ClangFormatCategory));
static cl::opt<bool>
SortIncludes("sort-includes",
cl::desc("If set, overrides the include sorting behavior\n"
"determined by the SortIncludes style flag"),
cl::cat(ClangFormatCategory));
static cl::opt<std::string> QualifierAlignment(
"qualifier-alignment",
cl::desc("If set, overrides the qualifier alignment style\n"
"determined by the QualifierAlignment style flag"),
cl::init(""), cl::cat(ClangFormatCategory));
static cl::opt<std::string> Files(
"files",
cl::desc("A file containing a list of files to process, one per line."),
cl::value_desc("filename"), cl::init(""), cl::cat(ClangFormatCategory));
static cl::opt<bool>
Verbose("verbose", cl::desc("If set, shows the list of processed files"),
cl::cat(ClangFormatCategory));
// Use --dry-run to match other LLVM tools when you mean do it but don't
// actually do it
static cl::opt<bool>
DryRun("dry-run",
cl::desc("If set, do not actually make the formatting changes"),
cl::cat(ClangFormatCategory));
// Use -n as a common command as an alias for --dry-run. (git and make use -n)
static cl::alias DryRunShort("n", cl::desc("Alias for --dry-run"),
cl::cat(ClangFormatCategory), cl::aliasopt(DryRun),
cl::NotHidden);
// Emulate being able to turn on/off the warning.
static cl::opt<bool>
WarnFormat("Wclang-format-violations",
cl::desc("Warnings about individual formatting changes needed. "
"Used only with --dry-run or -n"),
cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden);
static cl::opt<bool>
NoWarnFormat("Wno-clang-format-violations",
cl::desc("Do not warn about individual formatting changes "
"needed. Used only with --dry-run or -n"),
cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden);
static cl::opt<unsigned> ErrorLimit(
"ferror-limit",
cl::desc("Set the maximum number of clang-format errors to emit\n"
"before stopping (0 = no limit).\n"
"Used only with --dry-run or -n"),
cl::init(0), cl::cat(ClangFormatCategory));
static cl::opt<bool>
WarningsAsErrors("Werror",
cl::desc("If set, changes formatting warnings to errors"),
cl::cat(ClangFormatCategory));
namespace {
enum class WNoError { Unknown };
}
static cl::bits<WNoError> WNoErrorList(
"Wno-error",
cl::desc("If set, don't error out on the specified warning type."),
cl::values(
clEnumValN(WNoError::Unknown, "unknown",
"If set, unknown format options are only warned about.\n"
"This can be used to enable formatting, even if the\n"
"configuration contains unknown (newer) options.\n"
"Use with caution, as this might lead to dramatically\n"
"differing format depending on an option being\n"
"supported or not.")),
cl::cat(ClangFormatCategory));
static cl::opt<bool>
ShowColors("fcolor-diagnostics",
cl::desc("If set, and on a color-capable terminal controls "
"whether or not to print diagnostics in color"),
cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden);
static cl::opt<bool>
NoShowColors("fno-color-diagnostics",
cl::desc("If set, and on a color-capable terminal controls "
"whether or not to print diagnostics in color"),
cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden);
static cl::list<std::string> FileNames(cl::Positional,
cl::desc("[@<file>] [<file> ...]"),
cl::cat(ClangFormatCategory));
static cl::opt<bool> FailOnIncompleteFormat(
"fail-on-incomplete-format",
cl::desc("If set, fail with exit code 1 on incomplete format."),
cl::init(false), cl::cat(ClangFormatCategory));
static cl::opt<bool> ListIgnored("list-ignored",
cl::desc("List ignored files."),
cl::cat(ClangFormatCategory), cl::Hidden);
namespace clang {
namespace format {
static FileID createInMemoryFile(StringRef FileName, MemoryBufferRef Source,
SourceManager &Sources, FileManager &Files,
llvm::vfs::InMemoryFileSystem *MemFS) {
MemFS->addFileNoOwn(FileName, 0, Source);
auto File = Files.getOptionalFileRef(FileName);
assert(File && "File not added to MemFS?");
return Sources.createFileID(*File, SourceLocation(), SrcMgr::C_User);
}
// Parses <start line>:<end line> input to a pair of line numbers.
// Returns true on error.
static bool parseLineRange(StringRef Input, unsigned &FromLine,
unsigned &ToLine) {
std::pair<StringRef, StringRef> LineRange = Input.split(':');
return LineRange.first.getAsInteger(0, FromLine) ||
LineRange.second.getAsInteger(0, ToLine);
}
static bool fillRanges(MemoryBuffer *Code,
std::vector<tooling::Range> &Ranges) {
IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
new llvm::vfs::InMemoryFileSystem);
FileManager Files(FileSystemOptions(), InMemoryFileSystem);
DiagnosticOptions DiagOpts;
DiagnosticsEngine Diagnostics(
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), DiagOpts);
SourceManager Sources(Diagnostics, Files);
const auto ID = createInMemoryFile("<irrelevant>", *Code, Sources, Files,
InMemoryFileSystem.get());
if (!LineRanges.empty()) {
if (!Offsets.empty() || !Lengths.empty()) {
errs() << "error: cannot use -lines with -offset/-length\n";
return true;
}
for (const auto &LineRange : LineRanges) {
unsigned FromLine, ToLine;
if (parseLineRange(LineRange, FromLine, ToLine)) {
errs() << "error: invalid <start line>:<end line> pair\n";
return true;
}
if (FromLine < 1) {
errs() << "error: start line should be at least 1\n";
return true;
}
if (FromLine > ToLine) {
errs() << "error: start line should not exceed end line\n";
return true;
}
const auto Start = Sources.translateLineCol(ID, FromLine, 1);
const auto End = Sources.translateLineCol(ID, ToLine, UINT_MAX);
if (Start.isInvalid() || End.isInvalid())
return true;
const auto Offset = Sources.getFileOffset(Start);
const auto Length = Sources.getFileOffset(End) - Offset;
Ranges.push_back(tooling::Range(Offset, Length));
}
return false;
}
if (Offsets.empty())
Offsets.push_back(0);
const bool EmptyLengths = Lengths.empty();
unsigned Length = 0;
if (Offsets.size() == 1 && EmptyLengths) {
Length = Sources.getFileOffset(Sources.getLocForEndOfFile(ID)) - Offsets[0];
} else if (Offsets.size() != Lengths.size()) {
errs() << "error: number of -offset and -length arguments must match.\n";
return true;
}
for (unsigned I = 0, E = Offsets.size(), CodeSize = Code->getBufferSize();
I < E; ++I) {
const auto Offset = Offsets[I];
if (Offset >= CodeSize) {
errs() << "error: offset " << Offset << " is outside the file\n";
return true;
}
if (!EmptyLengths)
Length = Lengths[I];
if (Offset + Length > CodeSize) {
errs() << "error: invalid length " << Length << ", offset + length ("
<< Offset + Length << ") is outside the file.\n";
return true;
}
Ranges.push_back(tooling::Range(Offset, Length));
}
return false;
}
static void outputReplacementXML(StringRef Text) {
// FIXME: When we sort includes, we need to make sure the stream is correct
// utf-8.
size_t From = 0;
size_t Index;
while ((Index = Text.find_first_of("\n\r<&", From)) != StringRef::npos) {
outs() << Text.substr(From, Index - From);
switch (Text[Index]) {
case '\n':
outs() << "&#10;";
break;
case '\r':
outs() << "&#13;";
break;
case '<':
outs() << "&lt;";
break;
case '&':
outs() << "&amp;";
break;
default:
llvm_unreachable("Unexpected character encountered!");
}
From = Index + 1;
}
outs() << Text.substr(From);
}
static void outputReplacementsXML(const Replacements &Replaces) {
for (const auto &R : Replaces) {
outs() << "<replacement "
<< "offset='" << R.getOffset() << "' "
<< "length='" << R.getLength() << "'>";
outputReplacementXML(R.getReplacementText());
outs() << "</replacement>\n";
}
}
static bool
emitReplacementWarnings(const Replacements &Replaces, StringRef AssumedFileName,
const std::unique_ptr<llvm::MemoryBuffer> &Code) {
unsigned Errors = 0;
if (WarnFormat && !NoWarnFormat) {
SourceMgr Mgr;
const char *StartBuf = Code->getBufferStart();
Mgr.AddNewSourceBuffer(
MemoryBuffer::getMemBuffer(StartBuf, AssumedFileName), SMLoc());
for (const auto &R : Replaces) {
SMDiagnostic Diag = Mgr.GetMessage(
SMLoc::getFromPointer(StartBuf + R.getOffset()),
WarningsAsErrors ? SourceMgr::DiagKind::DK_Error
: SourceMgr::DiagKind::DK_Warning,
"code should be clang-formatted [-Wclang-format-violations]");
Diag.print(nullptr, llvm::errs(), ShowColors && !NoShowColors);
if (ErrorLimit && ++Errors >= ErrorLimit)
break;
}
}
return WarningsAsErrors;
}
static void outputXML(const Replacements &Replaces,
const Replacements &FormatChanges,
const FormattingAttemptStatus &Status,
const cl::opt<unsigned> &Cursor,
unsigned CursorPosition) {
outs() << "<?xml version='1.0'?>\n<replacements "
"xml:space='preserve' incomplete_format='"
<< (Status.FormatComplete ? "false" : "true") << "'";
if (!Status.FormatComplete)
outs() << " line='" << Status.Line << "'";
outs() << ">\n";
if (Cursor.getNumOccurrences() != 0) {
outs() << "<cursor>" << FormatChanges.getShiftedCodePosition(CursorPosition)
<< "</cursor>\n";
}
outputReplacementsXML(Replaces);
outs() << "</replacements>\n";
}
class ClangFormatDiagConsumer : public DiagnosticConsumer {
virtual void anchor() {}
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
const Diagnostic &Info) override {
SmallVector<char, 16> vec;
Info.FormatDiagnostic(vec);
errs() << "clang-format error:" << vec << "\n";
}
};
// Returns true on error.
static bool format(StringRef FileName, bool ErrorOnIncompleteFormat = false) {
const bool IsSTDIN = FileName == "-";
if (!OutputXML && Inplace && IsSTDIN) {
errs() << "error: cannot use -i when reading from stdin.\n";
return true;
}
// On Windows, overwriting a file with an open file mapping doesn't work,
// so read the whole file into memory when formatting in-place.
ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
!OutputXML && Inplace
? MemoryBuffer::getFileAsStream(FileName)
: MemoryBuffer::getFileOrSTDIN(FileName, /*IsText=*/true);
if (std::error_code EC = CodeOrErr.getError()) {
errs() << FileName << ": " << EC.message() << "\n";
return true;
}
std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get());
if (Code->getBufferSize() == 0)
return false; // Empty files are formatted correctly.
StringRef BufStr = Code->getBuffer();
const char *InvalidBOM = SrcMgr::ContentCache::getInvalidBOM(BufStr);
if (InvalidBOM) {
errs() << "error: encoding with unsupported byte order mark \""
<< InvalidBOM << "\" detected";
if (!IsSTDIN)
errs() << " in file '" << FileName << "'";
errs() << ".\n";
return true;
}
std::vector<tooling::Range> Ranges;
if (fillRanges(Code.get(), Ranges))
return true;
StringRef AssumedFileName = IsSTDIN ? AssumeFileName : FileName;
if (AssumedFileName.empty()) {
llvm::errs() << "error: empty filenames are not allowed\n";
return true;
}
auto RealFS = vfs::getRealFileSystem();
auto CustomFS = new vfs::CustomFileSystem(RealFS);
IntrusiveRefCntPtr<vfs::FileSystem> CustomFSPtr(CustomFS);
Expected<FormatStyle> FormatStyle =
getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer(),
CustomFSPtr.get(), WNoErrorList.isSet(WNoError::Unknown));
if (!FormatStyle) {
llvm::errs() << toString(FormatStyle.takeError()) << "\n";
return true;
}
StringRef QualifierAlignmentOrder = QualifierAlignment;
FormatStyle->QualifierAlignment =
StringSwitch<FormatStyle::QualifierAlignmentStyle>(
QualifierAlignmentOrder.lower())
.Case("right", FormatStyle::QAS_Right)
.Case("left", FormatStyle::QAS_Left)
.Default(FormatStyle->QualifierAlignment);
if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Left) {
FormatStyle->QualifierOrder = {"const", "volatile", "type"};
} else if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Right) {
FormatStyle->QualifierOrder = {"type", "const", "volatile"};
} else if (QualifierAlignmentOrder.contains("type")) {
FormatStyle->QualifierAlignment = FormatStyle::QAS_Custom;
SmallVector<StringRef> Qualifiers;
QualifierAlignmentOrder.split(Qualifiers, " ", /*MaxSplit=*/-1,
/*KeepEmpty=*/false);
FormatStyle->QualifierOrder = {Qualifiers.begin(), Qualifiers.end()};
}
if (SortIncludes.getNumOccurrences() != 0) {
FormatStyle->SortIncludes = {};
if (SortIncludes)
FormatStyle->SortIncludes.Enabled = true;
}
unsigned CursorPosition = Cursor;
Replacements Replaces = sortIncludes(*FormatStyle, Code->getBuffer(), Ranges,
AssumedFileName, &CursorPosition);
const bool IsJson = FormatStyle->isJson();
// To format JSON insert a variable to trick the code into thinking its
// JavaScript.
if (IsJson && !FormatStyle->DisableFormat) {
auto Err =
Replaces.add(tooling::Replacement(AssumedFileName, 0, 0, "x = "));
if (Err)
llvm::errs() << "Bad Json variable insertion\n";
}
auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), Replaces);
if (!ChangedCode) {
llvm::errs() << toString(ChangedCode.takeError()) << "\n";
return true;
}
// Get new affected ranges after sorting `#includes`.
Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges);
FormattingAttemptStatus Status;
Replacements FormatChanges =
reformat(*FormatStyle, *ChangedCode, Ranges, AssumedFileName, &Status);
Replaces = Replaces.merge(FormatChanges);
if (DryRun) {
return Replaces.size() > (IsJson ? 1u : 0u) &&
emitReplacementWarnings(Replaces, AssumedFileName, Code);
}
if (OutputXML) {
outputXML(Replaces, FormatChanges, Status, Cursor, CursorPosition);
} else {
IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
new llvm::vfs::InMemoryFileSystem);
FileManager Files(FileSystemOptions(), InMemoryFileSystem);
DiagnosticOptions DiagOpts;
ClangFormatDiagConsumer IgnoreDiagnostics;
DiagnosticsEngine Diagnostics(
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), DiagOpts,
&IgnoreDiagnostics, false);
SourceManager Sources(Diagnostics, Files);
FileID ID = createInMemoryFile(AssumedFileName, *Code, Sources, Files,
InMemoryFileSystem.get());
Rewriter Rewrite(Sources, LangOptions());
tooling::applyAllReplacements(Replaces, Rewrite);
if (Inplace) {
if (Rewrite.overwriteChangedFiles())
return true;
} else {
if (Cursor.getNumOccurrences() != 0) {
outs() << "{ \"Cursor\": "
<< FormatChanges.getShiftedCodePosition(CursorPosition)
<< ", \"IncompleteFormat\": "
<< (Status.FormatComplete ? "false" : "true");
if (!Status.FormatComplete)
outs() << ", \"Line\": " << Status.Line;
outs() << " }\n";
}
Rewrite.getEditBuffer(ID).write(outs());
}
}
return ErrorOnIncompleteFormat && !Status.FormatComplete;
}
} // namespace format
} // namespace clang
static void PrintVersion(raw_ostream &OS) {
OS << clang::getClangToolFullVersion("clang-format") << '\n';
}
// Dump the configuration.
static int dumpConfig() {
std::unique_ptr<llvm::MemoryBuffer> Code;
// We can't read the code to detect the language if there's no file name.
if (!FileNames.empty()) {
// Read in the code in case the filename alone isn't enough to detect the
// language.
ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
MemoryBuffer::getFileOrSTDIN(FileNames[0], /*IsText=*/true);
if (std::error_code EC = CodeOrErr.getError()) {
llvm::errs() << EC.message() << "\n";
return 1;
}
Code = std::move(CodeOrErr.get());
}
auto RealFS = vfs::getRealFileSystem();
auto CustomFS = new vfs::CustomFileSystem(RealFS);
IntrusiveRefCntPtr<vfs::FileSystem> CustomFSPtr(CustomFS);
Expected<clang::format::FormatStyle> FormatStyle = clang::format::getStyle(
Style,
FileNames.empty() || FileNames[0] == "-" ? AssumeFileName : FileNames[0],
FallbackStyle, Code ? Code->getBuffer() : "", CustomFSPtr.get());
if (!FormatStyle) {
llvm::errs() << toString(FormatStyle.takeError()) << "\n";
return 1;
}
std::string Config = clang::format::configurationAsText(*FormatStyle);
outs() << Config << "\n";
return 0;
}
using String = SmallString<128>;
static String IgnoreDir; // Directory of .clang-format-ignore file.
static String PrevDir; // Directory of previous `FilePath`.
static SmallVector<String> Patterns; // Patterns in .clang-format-ignore file.
// Check whether `FilePath` is ignored according to the nearest
// .clang-format-ignore file based on the rules below:
// - A blank line is skipped.
// - Leading and trailing spaces of a line are trimmed.
// - A line starting with a hash (`#`) is a comment.
// - A non-comment line is a single pattern.
// - The slash (`/`) is used as the directory separator.
// - A pattern is relative to the directory of the .clang-format-ignore file (or
// the root directory if the pattern starts with a slash).
// - A pattern is negated if it starts with a bang (`!`).
static bool isIgnored(StringRef FilePath) {
using namespace llvm::sys::fs;
if (!is_regular_file(FilePath))
return false;
String Path;
String AbsPath{FilePath};
auto PathStyle = vfs::getPathStyle();
using namespace llvm::sys::path;
vfs::make_absolute(AbsPath);
remove_dots(AbsPath, /*remove_dot_dot=*/true, PathStyle);
if (StringRef Dir{parent_path(AbsPath, PathStyle)}; PrevDir != Dir) {
PrevDir = Dir;
for (;;) {
Path = Dir;
append(Path, PathStyle, ".clang-format-ignore");
if (is_regular_file(Path))
break;
Dir = parent_path(Dir, PathStyle);
if (Dir.empty())
return false;
}
IgnoreDir = convert_to_slash(Dir, PathStyle);
std::ifstream IgnoreFile{Path.c_str()};
if (!IgnoreFile.good())
return false;
Patterns.clear();
for (std::string Line; std::getline(IgnoreFile, Line);) {
if (const auto Pattern{StringRef{Line}.trim()};
// Skip empty and comment lines.
!Pattern.empty() && Pattern[0] != '#') {
Patterns.push_back(Pattern);
}
}
}
if (IgnoreDir.empty())
return false;
const auto Pathname{convert_to_slash(AbsPath, PathStyle)};
for (const auto &Pat : Patterns) {
const bool IsNegated = Pat[0] == '!';
StringRef Pattern{Pat};
if (IsNegated)
Pattern = Pattern.drop_front();
if (Pattern.empty())
continue;
Pattern = Pattern.ltrim();
// `Pattern` is relative to `IgnoreDir` unless it starts with a slash.
// This doesn't support patterns containing drive names (e.g. `C:`).
if (Pattern[0] != '/') {
Path = IgnoreDir;
append(Path, Style::posix, Pattern);
remove_dots(Path, /*remove_dot_dot=*/true, Style::posix);
Pattern = Path;
}
if (clang::format::matchFilePath(Pattern, Pathname) == !IsNegated)
return true;
}
return false;
}
int main(int argc, const char **argv) {
InitLLVM X(argc, argv);
cl::HideUnrelatedOptions(ClangFormatCategory);
cl::SetVersionPrinter(PrintVersion);
cl::ParseCommandLineOptions(
argc, argv,
"A tool to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# "
"code.\n\n"
"If no arguments are specified, it formats the code from standard input\n"
"and writes the result to the standard output.\n"
"If <file>s are given, it reformats the files. If -i is specified\n"
"together with <file>s, the files are edited in-place. Otherwise, the\n"
"result is written to the standard output.\n");
if (Help) {
cl::PrintHelpMessage();
return 0;
}
if (DumpConfig)
return dumpConfig();
if (!Files.empty()) {
std::ifstream ExternalFileOfFiles{std::string(Files)};
std::string Line;
unsigned LineNo = 1;
while (std::getline(ExternalFileOfFiles, Line)) {
FileNames.push_back(Line);
LineNo++;
}
errs() << "Clang-formatting " << LineNo << " files\n";
}
if (FileNames.empty()) {
if (isIgnored(AssumeFileName))
return 0;
return clang::format::format("-", FailOnIncompleteFormat);
}
if (FileNames.size() > 1 &&
(!Offsets.empty() || !Lengths.empty() || !LineRanges.empty())) {
errs() << "error: -offset, -length and -lines can only be used for "
"single file.\n";
return 1;
}
unsigned FileNo = 1;
bool Error = false;
for (const auto &FileName : FileNames) {
const bool Ignored = isIgnored(FileName);
if (ListIgnored) {
if (Ignored)
outs() << FileName << '\n';
continue;
}
if (Ignored)
continue;
if (Verbose) {
errs() << "Formatting [" << FileNo++ << "/" << FileNames.size() << "] "
<< FileName << "\n";
}
Error |= clang::format::format(FileName, FailOnIncompleteFormat);
}
return Error ? 1 : 0;
}

View File

@@ -0,0 +1,323 @@
//===-- clang-format/ClangFormat.cpp - Clang format tool ------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file implements a clang-format tool that automatically formats
/// (fragments of) C++ code.
///
//===----------------------------------------------------------------------===//
#include "lib.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Version.h"
#include "clang/Format/Format.h"
#include "clang/Rewrite/Core/Rewriter.h"
using namespace llvm;
using clang::tooling::Replacements;
static std::string FallbackStyle{clang::format::DefaultFallbackStyle};
static unsigned Cursor{0};
static bool SortIncludes{false};
static std::string QualifierAlignment{""};
static auto Ok(const std::string content) -> Result {
return {false, std::move(content)};
}
static auto Err(const std::string content) -> Result {
return {true, std::move(content)};
}
namespace clang {
namespace format {
static FileID createInMemoryFile(StringRef FileName, MemoryBufferRef Source,
SourceManager &Sources, FileManager &Files,
llvm::vfs::InMemoryFileSystem *MemFS) {
MemFS->addFileNoOwn(FileName, 0, Source);
auto File = Files.getOptionalFileRef(FileName);
assert(File && "File not added to MemFS?");
return Sources.createFileID(*File, SourceLocation(), SrcMgr::C_User);
}
static auto fillRanges(MemoryBuffer *Code, std::vector<tooling::Range> &Ranges)
-> void {
Ranges.push_back(tooling::Range(0, Code->getBuffer().size()));
}
static auto isPredefinedStyle(StringRef style) -> bool {
return StringSwitch<bool>(style.lower())
.Cases("llvm", "chromium", "mozilla", "google", "webkit", "gnu",
"microsoft", "none", "file", true)
.Default(false);
}
static auto format_range(const std::unique_ptr<llvm::MemoryBuffer> code,
const std::string assumedFileName,
const std::string style,
std::vector<tooling::Range> ranges) -> Result {
StringRef BufStr = code->getBuffer();
const char *InvalidBOM = SrcMgr::ContentCache::getInvalidBOM(BufStr);
if (InvalidBOM) {
std::stringstream err;
err << "encoding with unsupported byte order mark \"" << InvalidBOM
<< "\" detected.";
return Err(err.str());
}
StringRef AssumedFileName = assumedFileName;
if (AssumedFileName.empty())
AssumedFileName = "<stdin>";
IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
new llvm::vfs::InMemoryFileSystem);
FileManager Files(FileSystemOptions(), InMemoryFileSystem);
DiagnosticOptions DiagOpts;
DiagnosticsEngine Diagnostics(
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), DiagOpts);
SourceManager Sources(Diagnostics, Files);
StringRef _style = style;
if (!_style.starts_with("{") && !isPredefinedStyle(_style)) {
std::unique_ptr<llvm::MemoryBuffer> DotClangFormat =
MemoryBuffer::getMemBuffer(style);
createInMemoryFile(".clang-format", *DotClangFormat.get(), Sources, Files,
InMemoryFileSystem.get());
_style = "file:.clang-format";
}
llvm::Expected<FormatStyle> FormatStyle =
getStyle(_style, AssumedFileName, FallbackStyle, code->getBuffer(),
InMemoryFileSystem.get(), false);
InMemoryFileSystem.reset();
if (!FormatStyle) {
std::string err = llvm::toString(FormatStyle.takeError());
return Err(err);
}
StringRef QualifierAlignmentOrder = QualifierAlignment;
FormatStyle->QualifierAlignment =
StringSwitch<FormatStyle::QualifierAlignmentStyle>(
QualifierAlignmentOrder.lower())
.Case("right", FormatStyle::QAS_Right)
.Case("left", FormatStyle::QAS_Left)
.Default(FormatStyle->QualifierAlignment);
if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Left) {
FormatStyle->QualifierOrder = {"const", "volatile", "type"};
} else if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Right) {
FormatStyle->QualifierOrder = {"type", "const", "volatile"};
} else if (QualifierAlignmentOrder.contains("type")) {
FormatStyle->QualifierAlignment = FormatStyle::QAS_Custom;
SmallVector<StringRef> Qualifiers;
QualifierAlignmentOrder.split(Qualifiers, " ", /*MaxSplit=*/-1,
/*KeepEmpty=*/false);
FormatStyle->QualifierOrder = {Qualifiers.begin(), Qualifiers.end()};
}
if (SortIncludes) {
FormatStyle->SortIncludes = {};
FormatStyle->SortIncludes.Enabled = true;
}
unsigned CursorPosition = Cursor;
Replacements Replaces = sortIncludes(*FormatStyle, code->getBuffer(), ranges,
AssumedFileName, &CursorPosition);
// To format JSON insert a variable to trick the code into thinking its
// JavaScript.
if (FormatStyle->isJson() && !FormatStyle->DisableFormat) {
auto err =
Replaces.add(tooling::Replacement(AssumedFileName, 0, 0, "x = "));
if (err)
return Err("Bad Json variable insertion");
}
auto ChangedCode =
cantFail(tooling::applyAllReplacements(code->getBuffer(), Replaces));
// Get new affected ranges after sorting `#includes`.
ranges = tooling::calculateRangesAfterReplacements(Replaces, ranges);
FormattingAttemptStatus Status;
Replacements FormatChanges =
reformat(*FormatStyle, ChangedCode, ranges, AssumedFileName, &Status);
Replaces = Replaces.merge(FormatChanges);
return Ok(
cantFail(tooling::applyAllReplacements(code->getBuffer(), Replaces)));
}
static auto format_range(const std::string str,
const std::string assumedFileName,
const std::string style, const bool is_line_range,
const std::vector<unsigned> ranges) -> Result {
ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
MemoryBuffer::getMemBuffer(str);
if (std::error_code EC = CodeOrErr.getError())
return Err(EC.message());
std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get());
if (Code->getBufferSize() == 0)
return Ok(""); // Empty files are formatted correctly.
std::vector<tooling::Range> Ranges;
if (ranges.empty()) {
fillRanges(Code.get(), Ranges);
return format_range(std::move(Code), assumedFileName, style,
std::move(Ranges));
}
IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
new llvm::vfs::InMemoryFileSystem);
FileManager Files(FileSystemOptions(), InMemoryFileSystem);
DiagnosticOptions DiagOpts;
DiagnosticsEngine Diagnostics(
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), DiagOpts);
SourceManager Sources(Diagnostics, Files);
FileID ID = createInMemoryFile("<irrelevant>", *Code, Sources, Files,
InMemoryFileSystem.get());
if (is_line_range) {
for (auto FromLine = begin(ranges); FromLine < end(ranges); FromLine += 2) {
auto ToLine = FromLine + 1;
SourceLocation Start = Sources.translateLineCol(ID, *FromLine, 1);
SourceLocation End = Sources.translateLineCol(ID, *ToLine, UINT_MAX);
if (Start.isInvalid() || End.isInvalid())
return Err("invalid line number");
unsigned Offset = Sources.getFileOffset(Start);
unsigned Length = Sources.getFileOffset(End) - Offset;
Ranges.push_back(tooling::Range(Offset, Length));
}
} else {
if (ranges.size() > 2 && ranges.size() % 2 != 0)
return Err("number of -offset and -length arguments must match");
if (ranges.size() == 1) {
auto offset = begin(ranges);
if (*offset >= Code->getBufferSize()) {
std::stringstream err;
err << "offset " << *offset << " is outside the file";
return Err(err.str());
}
SourceLocation Start =
Sources.getLocForStartOfFile(ID).getLocWithOffset(*offset);
SourceLocation End = Sources.getLocForEndOfFile(ID);
unsigned Offset = Sources.getFileOffset(Start);
unsigned Length = Sources.getFileOffset(End) - Offset;
Ranges.push_back(tooling::Range(Offset, Length));
} else {
for (auto offset = begin(ranges); offset < end(ranges); offset += 2) {
auto length = offset + 1;
if (*offset >= Code->getBufferSize()) {
std::stringstream err;
err << "offset " << *offset << " is outside the file";
return Err(err.str());
}
unsigned end = *offset + *length;
if (end > Code->getBufferSize()) {
std::stringstream err;
err << "invalid length " << *length << ", offset + length (" << end
<< ") is outside the file.";
return Err(err.str());
}
SourceLocation Start =
Sources.getLocForStartOfFile(ID).getLocWithOffset(*offset);
SourceLocation End = Start.getLocWithOffset(*length);
unsigned Offset = Sources.getFileOffset(Start);
unsigned Length = Sources.getFileOffset(End) - Offset;
Ranges.push_back(tooling::Range(Offset, Length));
}
}
}
return format_range(std::move(Code), assumedFileName, style,
std::move(Ranges));
}
static auto format(const std::string str, const std::string assumedFileName,
const std::string style) -> Result {
ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
MemoryBuffer::getMemBuffer(str);
if (std::error_code EC = CodeOrErr.getError())
return Err(EC.message());
std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get());
if (Code->getBufferSize() == 0)
return Ok(""); // Empty files are formatted correctly.
std::vector<tooling::Range> Ranges;
fillRanges(Code.get(), Ranges);
return format_range(std::move(Code), assumedFileName, style,
std::move(Ranges));
}
} // namespace format
} // namespace clang
auto version() -> std::string {
return clang::getClangToolFullVersion("clang-format");
}
auto format(const std::string str, const std::string assumedFileName,
const std::string style) -> Result {
return clang::format::format(str, assumedFileName, style);
}
auto format_byte(const std::string str, const std::string assumedFileName,
const std::string style, const std::vector<unsigned> ranges)
-> Result {
return clang::format::format_range(str, assumedFileName, style, false,
std::move(ranges));
}
auto format_line(const std::string str, const std::string assumedFileName,
const std::string style, const std::vector<unsigned> ranges)
-> Result {
return clang::format::format_range(str, assumedFileName, style, true,
std::move(ranges));
}
auto set_fallback_style(const std::string style) -> void {
FallbackStyle = style;
}
auto set_sort_includes(const bool sort) -> void { SortIncludes = sort; }
auto dump_config(const std::string style, const std::string FileName,
const std::string code) -> Result {
llvm::Expected<clang::format::FormatStyle> FormatStyle =
clang::format::getStyle(style, FileName, FallbackStyle, code);
if (!FormatStyle)
return Err(llvm::toString(FormatStyle.takeError()));
std::string Config = clang::format::configurationAsText(*FormatStyle);
return Ok(Config);
}

View File

@@ -0,0 +1,24 @@
#ifndef CLANG_FORMAT_WASM_LIB_H_
#define CLANG_FORMAT_WASM_LIB_H_
#include <sstream>
struct Result {
bool error;
std::string content;
};
auto version() -> std::string;
auto format(const std::string str, const std::string assumedFileName, const std::string style) -> Result;
auto format_byte(const std::string str,
const std::string assumedFileName,
const std::string style,
const std::vector<unsigned> ranges) -> Result;
auto format_line(const std::string str,
const std::string assumedFileName,
const std::string style,
const std::vector<unsigned> ranges) -> Result;
auto set_fallback_style(const std::string style) -> void;
auto set_sort_includes(const bool sort) -> void;
auto dump_config(const std::string style, const std::string FileName, const std::string code) -> Result;
#endif

View File

@@ -0,0 +1,42 @@
set -Eeo pipefail
cd $(dirname $0)/..
project_root=$(pwd)
rm -rf pkg
mkdir -p pkg build
cd build
export CC=$(which clang)
export CXX=$(which clang++)
emcmake cmake -G Ninja ..
ninja clang-format-wasm
cd $project_root
if [[ ! -z "${WASM_OPT}" ]]; then
wasm-opt -Os build/clang-format-esm.wasm -o build/clang-format-esm-Os.wasm
wasm-opt -Oz build/clang-format-esm.wasm -o build/clang-format-esm-Oz.wasm
fi
SMALLEST_WASM=$(ls -Sr build/clang-format-e*.wasm | head -1)
cp $SMALLEST_WASM pkg/clang-format.wasm
cat src/template.js build/clang-format-esm.js >pkg/clang-format.js
# add shebang
echo '#!/usr/bin/env node' | cat - ./build/clang-format-cli.js >./pkg/clang-format-cli.cjs
cp ./build/clang-format-cli.wasm ./pkg/
cp ./src/clang-format.d.ts src/clang-format-*.js ./pkg/
cp ./package.json LICENSE README.md .npmignore ./pkg/
# copy git-clang-format and clang-format-diff.py
cp ./build/_deps/llvm_project-src/clang/tools/clang-format/git-clang-format ./pkg/
cp ./build/_deps/llvm_project-src/clang/tools/clang-format/clang-format-diff.py ./pkg/
ls -lh ./pkg
# make sure repo is clean
# git diff --exit-code

View File

@@ -0,0 +1,95 @@
diff --git a/src/cli.cc b/src/cli.cc
index 2861005..69ec009 100644
--- a/src/cli.cc
+++ b/src/cli.cc
@@ -12,7 +12,7 @@
///
//===----------------------------------------------------------------------===//
-#include "../../lib/Format/MatchFilePath.h"
+#include "clang/../../lib/Format/MatchFilePath.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/FileManager.h"
@@ -27,6 +27,8 @@
#include "llvm/Support/Process.h"
#include <fstream>
+#include "CustomFileSystem.h"
+
using namespace llvm;
using clang::tooling::Replacements;
@@ -448,9 +450,12 @@ static bool format(StringRef FileName, bool ErrorOnIncompleteFormat = false) {
return true;
}
+ auto RealFS = vfs::getRealFileSystem();
+ auto CustomFS = new vfs::CustomFileSystem(RealFS);
+ IntrusiveRefCntPtr<vfs::FileSystem> CustomFSPtr(CustomFS);
Expected<FormatStyle> FormatStyle =
getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer(),
- nullptr, WNoErrorList.isSet(WNoError::Unknown));
+ CustomFSPtr.get(), WNoErrorList.isSet(WNoError::Unknown));
if (!FormatStyle) {
llvm::errs() << toString(FormatStyle.takeError()) << "\n";
return true;
@@ -571,10 +576,15 @@ static int dumpConfig() {
}
Code = std::move(CodeOrErr.get());
}
+
+ auto RealFS = vfs::getRealFileSystem();
+ auto CustomFS = new vfs::CustomFileSystem(RealFS);
+ IntrusiveRefCntPtr<vfs::FileSystem> CustomFSPtr(CustomFS);
+
Expected<clang::format::FormatStyle> FormatStyle = clang::format::getStyle(
Style,
FileNames.empty() || FileNames[0] == "-" ? AssumeFileName : FileNames[0],
- FallbackStyle, Code ? Code->getBuffer() : "");
+ FallbackStyle, Code ? Code->getBuffer() : "", CustomFSPtr.get());
if (!FormatStyle) {
llvm::errs() << toString(FormatStyle.takeError()) << "\n";
return 1;
@@ -607,24 +617,26 @@ static bool isIgnored(StringRef FilePath) {
String Path;
String AbsPath{FilePath};
+ auto PathStyle = vfs::getPathStyle();
+
using namespace llvm::sys::path;
- make_absolute(AbsPath);
- remove_dots(AbsPath, /*remove_dot_dot=*/true);
+ vfs::make_absolute(AbsPath);
+ remove_dots(AbsPath, /*remove_dot_dot=*/true, PathStyle);
- if (StringRef Dir{parent_path(AbsPath)}; PrevDir != Dir) {
+ if (StringRef Dir{parent_path(AbsPath, PathStyle)}; PrevDir != Dir) {
PrevDir = Dir;
for (;;) {
Path = Dir;
- append(Path, ".clang-format-ignore");
+ append(Path, PathStyle, ".clang-format-ignore");
if (is_regular_file(Path))
break;
- Dir = parent_path(Dir);
+ Dir = parent_path(Dir, PathStyle);
if (Dir.empty())
return false;
}
- IgnoreDir = convert_to_slash(Dir);
+ IgnoreDir = convert_to_slash(Dir, PathStyle);
std::ifstream IgnoreFile{Path.c_str()};
if (!IgnoreFile.good())
@@ -644,7 +656,7 @@ static bool isIgnored(StringRef FilePath) {
if (IgnoreDir.empty())
return false;
- const auto Pathname{convert_to_slash(AbsPath)};
+ const auto Pathname{convert_to_slash(AbsPath, PathStyle)};
for (const auto &Pat : Patterns) {
const bool IsNegated = Pat[0] == '!';
StringRef Pattern{Pat};

View File

@@ -0,0 +1,26 @@
current_dir=$(pwd)
tmp_dir=$(mktemp -d)
cd $tmp_dir
git init
cp $current_dir/build/_deps/llvm_project-src/clang/tools/clang-format/ClangFormat.cpp ./cli.cc
git add -f .
git commit -m "init"
cp $current_dir/src/cli.cc ./cli.cc
git add -f .
git diff \
--cached \
--no-color \
--ignore-space-at-eol \
--no-ext-diff \
--src-prefix=a/src/ \
--dst-prefix=b/src/ \
>$current_dir/scripts/cli.patch || true
rm -rf $tmp_dir

View File

@@ -0,0 +1,146 @@
/* @ts-self-types="./clang-format.d.ts" */
async function load(module) {
if (typeof Response === "function" && module instanceof Response) {
if ("compileStreaming" in WebAssembly) {
try {
return await WebAssembly.compileStreaming(module);
} catch (e) {
if (module.headers.get("Content-Type") !== "application/wasm") {
console.warn(
"`WebAssembly.compileStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n",
e,
);
} else {
throw e;
}
}
}
return module.arrayBuffer();
}
return module;
}
let wasm;
export default async function initAsync(input) {
if (wasm !== undefined) {
return wasm;
}
if (typeof input === "undefined") {
input = new URL("clang-format.wasm", import.meta.url);
}
if (
typeof input === "string" ||
(typeof Request === "function" && input instanceof Request) ||
(typeof URL === "function" && input instanceof URL)
) {
input = fetch(input);
}
wasm = await load(await input).then((wasm) => Module({ wasm }));
assert_init = () => {};
}
function assert_init() {
throw new Error("uninit");
}
export function version() {
assert_init();
return wasm.version();
}
export function set_fallback_style(style) {
assert_init();
wasm.set_fallback_style(style);
}
export function set_sort_includes(sort) {
assert_init();
wasm.set_sort_includes(sort);
}
function unwrap(result) {
const { error, content } = result;
if (error) {
throw Error(content);
}
return content;
}
export function format(content, filename = "<stdin>", style = "LLVM") {
assert_init();
const result = wasm.format(content, filename, style);
return unwrap(result);
}
export function format_line_range(
content,
range,
filename = "<stdin>",
style = "LLVM",
) {
assert_init();
const rangeList = new wasm.RangeList();
for (const [fromLine, toLine] of range) {
if (fromLine < 1) {
throw Error("start line should be at least 1");
}
if (fromLine > toLine) {
throw Error("start line should not exceed end line");
}
rangeList.push_back(fromLine);
rangeList.push_back(toLine);
}
const result = wasm.format_line(content, filename, style, rangeList);
rangeList.delete();
return unwrap(result);
}
export function format_byte_range(
content,
range,
filename = "<stdin>",
style = "LLVM",
) {
assert_init();
const rangeList = new wasm.RangeList();
if (range.length === 1 && range[0].length === 1) {
rangeList.push_back(range[0][0]);
} else {
for (const [offset, length] of range) {
if (offset < 0) {
throw Error("start offset should be at least 0");
}
if (length < 0) {
throw Error("length should be at least 0");
}
rangeList.push_back(offset);
rangeList.push_back(length);
}
}
const result = wasm.format_byte(content, filename, style, rangeList);
rangeList.delete();
return unwrap(result);
}
export function dump_config({
style = "file",
filename = "<stdin>",
code = "",
} = {}) {
assert_init();
const result = wasm.dump_config(style, filename, code);
return unwrap(result);
}
export {
format_byte_range as formatByteRange,
format_line_range as formatLineRange,
};

View File

@@ -0,0 +1,32 @@
export function format(input: string, filename: string, config?: LayoutConfig): string;
interface LayoutConfig {
line_width?: number;
line_ending?: "lf" | "crlf";
language_version?: string;
}
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
export type InitOutput = unknown;
// export type SyncInitInput = BufferSource | WebAssembly.Module;
// /**
// * Instantiates the given `module`, which can either be bytes or
// * a precompiled `WebAssembly.Module`.
// *
// * @param {SyncInitInput} module
// *
// * @returns {InitOutput}
// */
// export function initSync(module: SyncInitInput): InitOutput;
/**
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
* for everything else, calls `WebAssembly.instantiate` directly.
*
* @param {InitInput | Promise<InitInput>} module_or_path
*
* @returns {Promise<InitOutput>}
*/
export default function init(module_or_path?: InitInput | Promise<InitInput>): Promise<InitOutput>;

View File

@@ -0,0 +1,84 @@
import { format as dart_fmt, instantiate, invoke } from "./dart_fmt.mjs";
let wasm;
function get_imports() {}
function init_memory() {}
function normalize(module) {
if (!(module instanceof WebAssembly.Module)) {
return new WebAssembly.Module(module);
}
return module;
}
export default async function (input) {
if (wasm !== undefined) return wasm;
if (typeof input === "undefined") {
input = new URL("dart_fmt.wasm", import.meta.url);
}
const imports = get_imports();
if (
typeof input === "string" ||
(typeof Request === "function" && input instanceof Request) ||
(typeof URL === "function" && input instanceof URL)
) {
input = fetch(input);
}
init_memory(imports);
wasm = await load(await input)
.then(normalize)
.then(instantiate);
invoke(wasm);
return wasm;
}
async function load(module) {
if (typeof Response === "function" && module instanceof Response) {
if ("compileStreaming" in WebAssembly) {
try {
return await WebAssembly.compileStreaming(module);
} catch (e) {
if (module.headers.get("Content-Type") != "application/wasm") {
console.warn(
"`WebAssembly.compileStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n",
e,
);
} else {
throw e;
}
}
}
return module.arrayBuffer();
}
return module;
}
export function format(source, filename = "stdin.dart", config = {}) {
const options = { lineEnding: "\n" };
if (config.line_width) {
options.pageWidth = config.line_width;
}
if (options.line_ending === "crlf") {
options.lineEnding = "\r\n";
}
if(options.language_version) {
options.languageVersion = options.language_version;
}
const result = dart_fmt(source, filename, JSON.stringify(options));
const err = result[0] === "x";
const output = result.slice(1);
if (err) {
throw new Error(output);
}
return output;
}

View File

@@ -0,0 +1,350 @@
// `modulePromise` is a promise to the `WebAssembly.module` object to be
// instantiated.
// `importObjectPromise` is a promise to an object that contains any additional
// imports needed by the module that aren't provided by the standard runtime.
// The fields on this object will be merged into the importObject with which
// the module will be instantiated.
// This function returns a promise to the instantiated module.
export const instantiate = async (modulePromise, importObjectPromise) => {
let dartInstance;
// Prints to the console
function printToConsole(value) {
if (typeof dartPrint == "function") {
dartPrint(value);
return;
}
if (typeof console == "object" && typeof console.log != "undefined") {
console.log(value);
return;
}
if (typeof print == "function") {
print(value);
return;
}
throw "Unable to print message: " + js;
}
// Converts a Dart List to a JS array. Any Dart objects will be converted, but
// this will be cheap for JSValues.
function arrayFromDartList(constructor, list) {
const exports = dartInstance.exports;
const read = exports.$listRead;
const length = exports.$listLength(list);
const array = new constructor(length);
for (let i = 0; i < length; i++) {
array[i] = read(list, i);
}
return array;
}
// A special symbol attached to functions that wrap Dart functions.
const jsWrappedDartFunctionSymbol = Symbol("JSWrappedDartFunction");
function finalizeWrapper(dartFunction, wrapped) {
wrapped.dartFunction = dartFunction;
wrapped[jsWrappedDartFunctionSymbol] = true;
return wrapped;
}
// Imports
const dart2wasm = {
_49: v => v.toString(),
_64: s => {
if (!/^\s*[+-]?(?:Infinity|NaN|(?:\.\d+|\d+(?:\.\d*)?)(?:[eE][+-]?\d+)?)\s*$/.test(s)) {
return NaN;
}
return parseFloat(s);
},
_65: () => {
let stackString = new Error().stack.toString();
let frames = stackString.split('\n');
let drop = 2;
if (frames[0] === 'Error') {
drop += 1;
}
return frames.slice(drop).join('\n');
},
_69: () => {
// On browsers return `globalThis.location.href`
if (globalThis.location != null) {
return globalThis.location.href;
}
return null;
},
_70: () => {
return typeof process != "undefined" &&
Object.prototype.toString.call(process) == "[object process]" &&
process.platform == "win32"
},
_85: s => JSON.stringify(s),
_86: s => printToConsole(s),
_87: a => a.join(''),
_90: (s, t) => s.split(t),
_91: s => s.toLowerCase(),
_92: s => s.toUpperCase(),
_93: s => s.trim(),
_97: (s, p, i) => s.indexOf(p, i),
_98: (s, p, i) => s.lastIndexOf(p, i),
_100: Object.is,
_101: s => s.toUpperCase(),
_102: s => s.toLowerCase(),
_103: (a, i) => a.push(i),
_113: (a, b) => a == b ? 0 : (a > b ? 1 : -1),
_114: a => a.length,
_116: (a, i) => a[i],
_117: (a, i, v) => a[i] = v,
_120: (o, start, length) => new Uint8Array(o.buffer, o.byteOffset + start, length),
_121: (o, start, length) => new Int8Array(o.buffer, o.byteOffset + start, length),
_122: (o, start, length) => new Uint8ClampedArray(o.buffer, o.byteOffset + start, length),
_123: (o, start, length) => new Uint16Array(o.buffer, o.byteOffset + start, length),
_124: (o, start, length) => new Int16Array(o.buffer, o.byteOffset + start, length),
_125: (o, start, length) => new Uint32Array(o.buffer, o.byteOffset + start, length),
_126: (o, start, length) => new Int32Array(o.buffer, o.byteOffset + start, length),
_129: (o, start, length) => new Float32Array(o.buffer, o.byteOffset + start, length),
_130: (o, start, length) => new Float64Array(o.buffer, o.byteOffset + start, length),
_133: (o) => new DataView(o.buffer, o.byteOffset, o.byteLength),
_137: Function.prototype.call.bind(Object.getOwnPropertyDescriptor(DataView.prototype, 'byteLength').get),
_138: (b, o) => new DataView(b, o),
_140: Function.prototype.call.bind(DataView.prototype.getUint8),
_141: Function.prototype.call.bind(DataView.prototype.setUint8),
_142: Function.prototype.call.bind(DataView.prototype.getInt8),
_143: Function.prototype.call.bind(DataView.prototype.setInt8),
_144: Function.prototype.call.bind(DataView.prototype.getUint16),
_145: Function.prototype.call.bind(DataView.prototype.setUint16),
_146: Function.prototype.call.bind(DataView.prototype.getInt16),
_147: Function.prototype.call.bind(DataView.prototype.setInt16),
_148: Function.prototype.call.bind(DataView.prototype.getUint32),
_149: Function.prototype.call.bind(DataView.prototype.setUint32),
_150: Function.prototype.call.bind(DataView.prototype.getInt32),
_151: Function.prototype.call.bind(DataView.prototype.setInt32),
_156: Function.prototype.call.bind(DataView.prototype.getFloat32),
_157: Function.prototype.call.bind(DataView.prototype.setFloat32),
_158: Function.prototype.call.bind(DataView.prototype.getFloat64),
_159: Function.prototype.call.bind(DataView.prototype.setFloat64),
_165: x0 => format = x0,
_166: f => finalizeWrapper(f, function(x0,x1,x2) { return dartInstance.exports._166(f,arguments.length,x0,x1,x2) }),
_184: (c) =>
queueMicrotask(() => dartInstance.exports.$invokeCallback(c)),
_187: (s, m) => {
try {
return new RegExp(s, m);
} catch (e) {
return String(e);
}
},
_188: (x0,x1) => x0.exec(x1),
_189: (x0,x1) => x0.test(x1),
_190: (x0,x1) => x0.exec(x1),
_191: (x0,x1) => x0.exec(x1),
_192: x0 => x0.pop(),
_198: o => o === undefined,
_199: o => typeof o === 'boolean',
_200: o => typeof o === 'number',
_202: o => typeof o === 'string',
_205: o => o instanceof Int8Array,
_206: o => o instanceof Uint8Array,
_207: o => o instanceof Uint8ClampedArray,
_208: o => o instanceof Int16Array,
_209: o => o instanceof Uint16Array,
_210: o => o instanceof Int32Array,
_211: o => o instanceof Uint32Array,
_212: o => o instanceof Float32Array,
_213: o => o instanceof Float64Array,
_214: o => o instanceof ArrayBuffer,
_215: o => o instanceof DataView,
_216: o => o instanceof Array,
_217: o => typeof o === 'function' && o[jsWrappedDartFunctionSymbol] === true,
_220: o => o instanceof RegExp,
_221: (l, r) => l === r,
_222: o => o,
_223: o => o,
_224: o => o,
_225: b => !!b,
_226: o => o.length,
_229: (o, i) => o[i],
_230: f => f.dartFunction,
_231: l => arrayFromDartList(Int8Array, l),
_232: (data, length) => {
const jsBytes = new Uint8Array(length);
const getByte = dartInstance.exports.$uint8ListGet;
for (let i = 0; i < length; i++) {
jsBytes[i] = getByte(data, i);
}
return jsBytes;
},
_233: l => arrayFromDartList(Uint8ClampedArray, l),
_234: l => arrayFromDartList(Int16Array, l),
_235: l => arrayFromDartList(Uint16Array, l),
_236: l => arrayFromDartList(Int32Array, l),
_237: l => arrayFromDartList(Uint32Array, l),
_238: l => arrayFromDartList(Float32Array, l),
_239: l => arrayFromDartList(Float64Array, l),
_240: (data, length) => {
const read = dartInstance.exports.$byteDataGetUint8;
const view = new DataView(new ArrayBuffer(length));
for (let i = 0; i < length; i++) {
view.setUint8(i, read(data, i));
}
return view;
},
_241: l => arrayFromDartList(Array, l),
_242: (s, length) => {
if (length == 0) return '';
const read = dartInstance.exports.$stringRead1;
let result = '';
let index = 0;
const chunkLength = Math.min(length - index, 500);
let array = new Array(chunkLength);
while (index < length) {
const newChunkLength = Math.min(length - index, 500);
for (let i = 0; i < newChunkLength; i++) {
array[i] = read(s, index++);
}
if (newChunkLength < chunkLength) {
array = array.slice(0, newChunkLength);
}
result += String.fromCharCode(...array);
}
return result;
}
,
_243: (s, length) => {
if (length == 0) return '';
const read = dartInstance.exports.$stringRead2;
let result = '';
let index = 0;
const chunkLength = Math.min(length - index, 500);
let array = new Array(chunkLength);
while (index < length) {
const newChunkLength = Math.min(length - index, 500);
for (let i = 0; i < newChunkLength; i++) {
array[i] = read(s, index++);
}
if (newChunkLength < chunkLength) {
array = array.slice(0, newChunkLength);
}
result += String.fromCharCode(...array);
}
return result;
}
,
_244: (s) => {
let length = s.length;
let range = 0;
for (let i = 0; i < length; i++) {
range |= s.codePointAt(i);
}
const exports = dartInstance.exports;
if (range < 256) {
if (length <= 10) {
if (length == 1) {
return exports.$stringAllocate1_1(s.codePointAt(0));
}
if (length == 2) {
return exports.$stringAllocate1_2(s.codePointAt(0), s.codePointAt(1));
}
if (length == 3) {
return exports.$stringAllocate1_3(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2));
}
if (length == 4) {
return exports.$stringAllocate1_4(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3));
}
if (length == 5) {
return exports.$stringAllocate1_5(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3), s.codePointAt(4));
}
if (length == 6) {
return exports.$stringAllocate1_6(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3), s.codePointAt(4), s.codePointAt(5));
}
if (length == 7) {
return exports.$stringAllocate1_7(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3), s.codePointAt(4), s.codePointAt(5), s.codePointAt(6));
}
if (length == 8) {
return exports.$stringAllocate1_8(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3), s.codePointAt(4), s.codePointAt(5), s.codePointAt(6), s.codePointAt(7));
}
if (length == 9) {
return exports.$stringAllocate1_9(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3), s.codePointAt(4), s.codePointAt(5), s.codePointAt(6), s.codePointAt(7), s.codePointAt(8));
}
if (length == 10) {
return exports.$stringAllocate1_10(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3), s.codePointAt(4), s.codePointAt(5), s.codePointAt(6), s.codePointAt(7), s.codePointAt(8), s.codePointAt(9));
}
}
const dartString = exports.$stringAllocate1(length);
const write = exports.$stringWrite1;
for (let i = 0; i < length; i++) {
write(dartString, i, s.codePointAt(i));
}
return dartString;
} else {
const dartString = exports.$stringAllocate2(length);
const write = exports.$stringWrite2;
for (let i = 0; i < length; i++) {
write(dartString, i, s.charCodeAt(i));
}
return dartString;
}
}
,
_247: l => new Array(l),
_251: (o, p) => o[p],
_255: o => String(o),
_260: x0 => x0.index,
_262: x0 => x0.length,
_264: (x0,x1) => x0[x1],
_265: (x0,x1) => x0.exec(x1),
_267: x0 => x0.flags,
_268: x0 => x0.multiline,
_269: x0 => x0.ignoreCase,
_270: x0 => x0.unicode,
_271: x0 => x0.dotAll,
_272: (x0,x1) => x0.lastIndex = x1
};
const baseImports = {
dart2wasm: dart2wasm,
Math: Math,
Date: Date,
Object: Object,
Array: Array,
Reflect: Reflect,
};
const jsStringPolyfill = {
"charCodeAt": (s, i) => s.charCodeAt(i),
"compare": (s1, s2) => {
if (s1 < s2) return -1;
if (s1 > s2) return 1;
return 0;
},
"concat": (s1, s2) => s1 + s2,
"equals": (s1, s2) => s1 === s2,
"fromCharCode": (i) => String.fromCharCode(i),
"length": (s) => s?.length||0,
"substring": (s, a, b) => s.substring(a, b),
};
dartInstance = await WebAssembly.instantiate(await modulePromise, {
...baseImports,
...(await importObjectPromise),
"wasm:js-string": jsStringPolyfill,
});
return dartInstance;
}
// Call the main function for the instantiated module
// `moduleInstance` is the instantiated dart2wasm module
// `args` are any arguments that should be passed into the main function.
export const invoke = (moduleInstance, ...args) => {
moduleInstance.exports.$invokeMain(args);
}
export let format;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,10 @@
import fs from "node:fs/promises";
import initAsync from "./dart_fmt.js";
const wasm = new URL("./dart_fmt.wasm", import.meta.url);
export default function __wbg_init(init = fs.readFile(wasm)) {
return initAsync(init);
}
export * from "./dart_fmt.js";

View File

@@ -0,0 +1,8 @@
import initAsync from "./dart_fmt.js";
import wasm from "./dart_fmt.wasm?url";
export default function __wbg_init(input = wasm) {
return initAsync(input);
}
export * from "./dart_fmt.js";

View File

@@ -0,0 +1,132 @@
/**
* Prettier Plugin for Dart formatting using dart_fmt WebAssembly
*
* This plugin provides support for formatting Dart files using the dart_fmt WASM implementation.
* dart_fmt is the official Dart code formatter integrated into the Dart SDK.
*/
import type { Plugin, Parser, Printer } from 'prettier';
// Import the Dart formatter WASM module
import dartInit, { format } from './dart_fmt_vite.js';
const parserName = 'dart';
// Language configuration
const languages = [
{
name: 'Dart',
aliases: ['dart'],
parsers: [parserName],
extensions: ['.dart'],
aceMode: 'dart',
tmScope: 'source.dart',
linguistLanguageId: 103,
vscodeLanguageIds: ['dart']
}
];
// Parser configuration
const dartParser: Parser<string> = {
astFormat: parserName,
parse: (text: string) => text,
locStart: () => 0,
locEnd: (node: string) => node.length,
};
// Lazy initialize Dart WASM module
let initPromise: Promise<void> | null = null;
let isInitialized = false;
function initDart(): Promise<void> {
if (isInitialized) {
return Promise.resolve();
}
if (!initPromise) {
initPromise = (async () => {
try {
await dartInit();
isInitialized = true;
} catch (error) {
console.warn('Failed to initialize Dart WASM module:', error);
initPromise = null;
throw error;
}
})();
}
return initPromise;
}
// Printer configuration
const dartPrinter: Printer<string> = {
// @ts-expect-error -- Support async printer like shell plugin
async print(path, options) {
try {
// Wait for initialization to complete
await initDart();
const text = (path as any).getValue ? (path as any).getValue() : path.node;
const config = getDartConfig(options);
// Format using dart_fmt (synchronous call)
const formatted = format(text, undefined, config);
return formatted.trim();
} catch (error) {
console.warn('Dart formatting failed:', error);
// Return original text if formatting fails
return (path as any).getValue ? (path as any).getValue() : path.node;
}
},
};
// Helper function to create Dart config from Prettier options
function getDartConfig(options: any): any {
const config: any = {};
// Map Prettier options to Dart formatter config
if (options.printWidth !== undefined) {
config.line_width = options.printWidth;
}
if (options.endOfLine !== undefined) {
config.line_ending = options.endOfLine === 'crlf' ? 'crlf' : 'lf';
}
// Dart language version (if specified)
if (options.dartLanguageVersion !== undefined) {
config.language_version = options.dartLanguageVersion;
}
return config;
}
// Plugin options
const options = {
dartLanguageVersion: {
since: '0.0.1',
category: 'Format' as const,
type: 'string' as const,
default: undefined,
description: 'Dart language version (e.g., "3.0", "2.17")'
}
};
// Plugin object
const dartPlugin: Plugin = {
languages,
parsers: {
[parserName]: dartParser,
},
printers: {
[parserName]: dartPrinter,
},
options,
};
export default dartPlugin;
export { languages };
export const parsers = dartPlugin.parsers;
export const printers = dartPlugin.printers;

View File

@@ -0,0 +1,31 @@
import 'dart:convert';
import 'dart:js_interop';
import 'package:dart_fmt/dart_fmt.dart';
class Options {
final int? pageWidth;
final String? lineEnding;
final String? languageVersion;
Options.fromJson(Map<String, dynamic> json)
: pageWidth = json['pageWidth'] as int?,
lineEnding = json['lineEnding'] as String?,
languageVersion = json['languageVersion'] as String?;
}
String formatWrapper(String source, String filename, String options) {
final config = Options.fromJson(jsonDecode(options));
try {
return "o${format(source, filename, pageWidth: config.pageWidth, lineEnding: config.lineEnding, version: config.languageVersion)}";
} catch (e) {
return "x$e";
}
}
@JS('format')
external set formatExport(JSFunction handler);
void main(List<String> arguments) {
formatExport = formatWrapper.toJS;
}

View File

@@ -0,0 +1,5 @@
cd $(dirname $0)/..
dart compile wasm ./lib/binding.dart -o ./build/dart_fmt.wasm

View File

@@ -0,0 +1,14 @@
import 'package:dart_style/dart_style.dart';
import 'package:pub_semver/pub_semver.dart';
String format(String source, String filename,
{int? pageWidth, String? lineEnding, String? version}) {
final languageVersion = version != null
? Version.parse(version)
: DartFormatter.latestLanguageVersion;
final formatter = DartFormatter(
pageWidth: pageWidth,
lineEnding: lineEnding,
languageVersion: languageVersion);
return formatter.format(source, uri: filename);
}

View File

@@ -0,0 +1,10 @@
export type InitInput =
| RequestInfo
| URL
| Response
| BufferSource
| WebAssembly.Module;
export default function initAsync(wasm_url?: InitInput): Promise<void>;
export declare function initSync(module: BufferSource | WebAssembly.Module): void;
export declare function format(input: string): string;

View File

@@ -0,0 +1,355 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// This file has been modified for use by the TinyGo compiler.
const encoder = new TextEncoder("utf-8");
const decoder = new TextDecoder("utf-8");
let reinterpretBuf = new DataView(new ArrayBuffer(8));
var logLine = [];
class TinyGo {
constructor() {
this._callbackTimeouts = new Map();
this._nextCallbackTimeoutID = 1;
const mem = () => {
// The buffer may change when requesting more memory.
return new DataView(this._inst.exports.memory.buffer);
}
const unboxValue = (v_ref) => {
reinterpretBuf.setBigInt64(0, v_ref, true);
const f = reinterpretBuf.getFloat64(0, true);
if (f === 0) {
return undefined;
}
if (!isNaN(f)) {
return f;
}
const id = v_ref & 0xffffffffn;
return this._values[id];
}
const loadValue = (addr) => {
let v_ref = mem().getBigUint64(addr, true);
return unboxValue(v_ref);
}
const boxValue = (v) => {
const nanHead = 0x7FF80000n;
if (typeof v === "number") {
if (isNaN(v)) {
return nanHead << 32n;
}
if (v === 0) {
return (nanHead << 32n) | 1n;
}
reinterpretBuf.setFloat64(0, v, true);
return reinterpretBuf.getBigInt64(0, true);
}
switch (v) {
case undefined:
return 0n;
case null:
return (nanHead << 32n) | 2n;
case true:
return (nanHead << 32n) | 3n;
case false:
return (nanHead << 32n) | 4n;
}
let id = this._ids.get(v);
if (id === undefined) {
id = this._idPool.pop();
if (id === undefined) {
id = BigInt(this._values.length);
}
this._values[id] = v;
this._goRefCounts[id] = 0;
this._ids.set(v, id);
}
this._goRefCounts[id]++;
let typeFlag = 1n;
switch (typeof v) {
case "string":
typeFlag = 2n;
break;
case "symbol":
typeFlag = 3n;
break;
case "function":
typeFlag = 4n;
break;
}
return id | ((nanHead | typeFlag) << 32n);
}
const storeValue = (addr, v) => {
let v_ref = boxValue(v);
mem().setBigUint64(addr, v_ref, true);
}
const loadSlice = (array, len, cap) => {
return new Uint8Array(this._inst.exports.memory.buffer, array, len);
}
const loadSliceOfValues = (array, len, cap) => {
const a = new Array(len);
for (let i = 0; i < len; i++) {
a[i] = loadValue(array + i * 8);
}
return a;
}
const loadString = (ptr, len) => {
return decoder.decode(new DataView(this._inst.exports.memory.buffer, ptr, len));
}
const timeOrigin = Date.now() - performance.now();
this.importObject = {
wasi_snapshot_preview1: {
// https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#fd_write
fd_write: () => 0, // dummy
},
gojs: {
// func ticks() float64
"runtime.ticks": () => {
return timeOrigin + performance.now();
},
// func finalizeRef(v ref)
"syscall/js.finalizeRef": (v_ref) => {
reinterpretBuf.setBigInt64(0, v_ref, true);
const f = reinterpretBuf.getFloat64(0, true);
if (f === 0 || !isNaN(f)) {
return;
}
const id = v_ref & 0xffffffffn;
this._goRefCounts[id]--;
if (this._goRefCounts[id] === 0) {
const v = this._values[id];
this._values[id] = null;
this._ids.delete(v);
this._idPool.push(id);
}
},
// func stringVal(value string) ref
"syscall/js.stringVal": (value_ptr, value_len) => {
const s = loadString(value_ptr, value_len);
return boxValue(s);
},
// func valueGet(v ref, p string) ref
"syscall/js.valueGet": (v_ref, p_ptr, p_len) => {
let prop = loadString(p_ptr, p_len);
let v = unboxValue(v_ref);
let result = Reflect.get(v, prop);
return boxValue(result);
},
// func valueSet(v ref, p string, x ref)
"syscall/js.valueSet": (v_ref, p_ptr, p_len, x_ref) => {
const v = unboxValue(v_ref);
const p = loadString(p_ptr, p_len);
const x = unboxValue(x_ref);
Reflect.set(v, p, x);
},
// func valueIndex(v ref, i int) ref
"syscall/js.valueIndex": (v_ref, i) => {
return boxValue(Reflect.get(unboxValue(v_ref), i));
},
// valueSetIndex(v ref, i int, x ref)
"syscall/js.valueSetIndex": (v_ref, i, x_ref) => {
Reflect.set(unboxValue(v_ref), i, unboxValue(x_ref));
},
// func valueCall(v ref, m string, args []ref) (ref, bool)
"syscall/js.valueCall": (ret_addr, v_ref, m_ptr, m_len, args_ptr, args_len, args_cap) => {
const v = unboxValue(v_ref);
const name = loadString(m_ptr, m_len);
const args = loadSliceOfValues(args_ptr, args_len, args_cap);
try {
const m = Reflect.get(v, name);
storeValue(ret_addr, Reflect.apply(m, v, args));
mem().setUint8(ret_addr + 8, 1);
} catch (err) {
storeValue(ret_addr, err);
mem().setUint8(ret_addr + 8, 0);
}
},
// func valueNew(v ref, args []ref) (ref, bool)
"syscall/js.valueNew": (ret_addr, v_ref, args_ptr, args_len, args_cap) => {
const v = unboxValue(v_ref);
const args = loadSliceOfValues(args_ptr, args_len, args_cap);
try {
storeValue(ret_addr, Reflect.construct(v, args));
mem().setUint8(ret_addr + 8, 1);
} catch (err) {
storeValue(ret_addr, err);
mem().setUint8(ret_addr+ 8, 0);
}
},
// func valueLength(v ref) int
"syscall/js.valueLength": (v_ref) => {
return unboxValue(v_ref).length;
},
// valuePrepareString(v ref) (ref, int)
"syscall/js.valuePrepareString": (ret_addr, v_ref) => {
const s = String(unboxValue(v_ref));
const str = encoder.encode(s);
storeValue(ret_addr, str);
mem().setInt32(ret_addr + 8, str.length, true);
},
// valueLoadString(v ref, b []byte)
"syscall/js.valueLoadString": (v_ref, slice_ptr, slice_len, slice_cap) => {
const str = unboxValue(v_ref);
loadSlice(slice_ptr, slice_len, slice_cap).set(str);
},
}
};
// Go 1.20 uses 'env'. Go 1.21 uses 'gojs'.
// For compatibility, we use both as long as Go 1.20 is supported.
this.importObject.env = this.importObject.gojs;
}
async run(instance) {
this._inst = instance;
this._values = [ // JS values that Go currently has references to, indexed by reference id
NaN,
0,
null,
true,
false,
// fake global
{
set format(fn){ instance.format = fn; },
Array,
Object,
},
this,
];
this._goRefCounts = []; // number of references that Go has to a JS value, indexed by reference id
this._ids = new Map(); // mapping from JS values to reference ids
this._idPool = []; // unused ids that have been garbage collected
this.exited = false; // whether the Go program has exited
while (true) {
const callbackPromise = new Promise((resolve) => {
this._resolveCallbackPromise = () => {
if (this.exited) {
throw new Error("bad callback: Go program has already exited");
}
setTimeout(resolve, 0); // make sure it is asynchronous
};
});
this._inst.exports._start();
if (this.exited) {
break;
}
await callbackPromise;
}
}
_resume() {
if (this.exited) {
throw new Error("Go program has already exited");
}
this._inst.exports.resume();
if (this.exited) {
this._resolveExitPromise();
}
}
_makeFuncWrapper(id) {
const go = this;
return function () {
const event = { id: id, this: this, args: arguments };
go._pendingEvent = event;
go._resume();
return event.result;
};
}
}
/**
* ================== End of wasm_exec.js ==================
*/
/**/let wasm;
/**/async function __load(module, imports) {
/**/ if (typeof Response === 'function' && module instanceof Response) {
/**/ if (typeof WebAssembly.instantiateStreaming === 'function') {
/**/ try { return await WebAssembly.instantiateStreaming(module, imports); }
/**/ catch (e) {
/**/ if (module.headers.get('Content-Type') != 'application/wasm') {
/**/ console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
/**/ } else { throw e; }
/**/ }
/**/ }
/**/ const bytes = await module.arrayBuffer();
/**/ return await WebAssembly.instantiate(bytes, imports);
/**/ } else {
/**/ const instance = await WebAssembly.instantiate(module, imports);
/**/ if (instance instanceof WebAssembly.Instance) return { instance, module };
/**/ else return instance;
/**/ }
/**/}
/**/function __finalize_init(instance) {
/**/ return wasm = instance;
/**/}
/**/function __init_memory(imports, maybe_memory) { }
/**/export function initSync(module) {
/**/ if (wasm !== undefined) return wasm;
/**/
/**/ const go = new TinyGo();
/**/ const imports = go.importObject;
/**/
/**/ __init_memory(imports);
/**/
/**/ if (!(module instanceof WebAssembly.Module)) module = new WebAssembly.Module(module);
/**/
/**/ const instance = new WebAssembly.Instance(module, imports);
/**/
/**/ go.run(instance);
/**/ return __finalize_init(instance, module);
/**/}
/**/export default async function initAsync(input) {
/**/ if (wasm !== undefined) return wasm;
/**/
/**/ if (typeof input === 'undefined') input = new URL('gofmt.wasm', import.meta.url);
/**/
/**/ const go = new TinyGo();
/**/ const imports = go.importObject;
/**/
/**/ if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) {
/**/ input = fetch(input);
/**/ }
/**/
/**/ __init_memory(imports);
/**/
/**/ const { instance, module } = await __load(await input, imports);
/**/
/**/ go.run(instance);
/**/ return __finalize_init(instance, module);
/**/}
/**/export function format(input) {
/**/ const [err, result] = wasm.format(input);
/**/ if (err) {
/**/ throw new Error(result);
/**/ }
/**/ return result;
/**/}
/**/

Binary file not shown.

View File

@@ -0,0 +1,10 @@
import fs from "node:fs/promises";
import initAsync from "./gofmt.js";
const wasm = new URL("./gofmt.wasm", import.meta.url);
export default function (init = fs.readFile(wasm)) {
return initAsync(init);
}
export * from "./gofmt.js";

View File

@@ -0,0 +1,8 @@
import initAsync from "./gofmt.js";
import wasm_url from "./gofmt.wasm?url";
export default function (input = wasm_url) {
return initAsync(input);
}
export * from "./gofmt.js";

View File

@@ -0,0 +1,101 @@
/**
* Prettier Plugin for Go formatting using gofmt WebAssembly
*
* This plugin provides support for formatting Go files using the gofmt WASM implementation.
*/
import type { Plugin, Parser, Printer } from 'prettier';
// Import the gofmt WASM module
import gofmtInit, { format } from './gofmt_vite.js';
const parserName = 'go';
// Language configuration
const languages = [
{
name: 'Go',
aliases: ['go', 'golang'],
parsers: [parserName],
extensions: ['.go'],
aceMode: 'golang',
tmScope: 'source.go',
linguistLanguageId: 132,
vscodeLanguageIds: ['go']
}
];
// Parser configuration
const goParser: Parser<string> = {
astFormat: parserName,
parse: (text: string) => text,
locStart: () => 0,
locEnd: (node: string) => node.length,
};
// Lazy initialize gofmt WASM module
let initPromise: Promise<void> | null = null;
let isInitialized = false;
function initGofmt(): Promise<void> {
if (isInitialized) {
return Promise.resolve();
}
if (!initPromise) {
initPromise = (async () => {
try {
await gofmtInit();
isInitialized = true;
} catch (error) {
console.warn('Failed to initialize gofmt WASM module:', error);
initPromise = null;
throw error;
}
})();
}
return initPromise;
}
// Printer configuration
const goPrinter: Printer<string> = {
// @ts-expect-error -- Support async printer like shell plugin
async print(path, options) {
try {
// Wait for initialization to complete
await initGofmt();
const text = (path as any).getValue ? (path as any).getValue() : path.node;
// Format using gofmt (synchronous call)
const formatted = format(text);
return formatted.trim();
} catch (error) {
console.warn('Go formatting failed:', error);
// Return original text if formatting fails
return (path as any).getValue ? (path as any).getValue() : path.node;
}
},
};
// Plugin options (Go doesn't need additional config options)
const options = {};
// Plugin definition
const goPlugin: Plugin = {
languages,
parsers: {
[parserName]: goParser,
},
printers: {
[parserName]: goPrinter,
},
options,
};
// Export plugin without auto-initialization
export default goPlugin;
export { languages, initGofmt as initialize };
export const parsers = goPlugin.parsers;
export const printers = goPlugin.printers;

View File

@@ -0,0 +1,9 @@
set -Eeo pipefail
cd $(dirname $0)
echo "Building..."
tinygo build -o=../gofmt.wasm -target=wasm -no-debug ./lib.go
echo "Generating JS..."
cp $(tinygo env TINYGOROOT)/targets/wasm_exec.js ../gofmt.js

View File

@@ -0,0 +1,23 @@
package main
import (
"go/format"
"syscall/js"
)
func Format(this js.Value, args []js.Value) any {
input := ([]byte)(args[0].String())
output, err := format.Source(input)
if err != nil {
return []any{true, err.Error()}
}
return []any{false, string(output)}
}
func main() {
done := make(chan bool)
js.Global().Set("format", js.FuncOf(Format))
<-done
}

View File

@@ -0,0 +1,67 @@
/**
* Prettier Plugin for Groovy/Jenkins file formatting
*
* This plugin provides support for formatting Groovy and Jenkins files using the groovy-beautify library.
* It supports .groovy files and Jenkins-related files like Jenkinsfile.
*/
import type { Plugin, Parser, Printer } from 'prettier';
import groovyBeautify from 'groovy-beautify';
const parserName = 'groovy';
// 语言配置
const languages = [
{
name: 'Groovy',
aliases: ['groovy'],
parsers: [parserName],
filenames: ['jenkinsfile', 'Jenkinsfile'],
extensions: ['.jenkinsfile', '.Jenkinsfile', '.groovy'],
aceMode: 'groovy',
tmScope: 'source.groovy',
linguistLanguageId: 142,
vscodeLanguageIds: ['groovy']
},
];
// 解析器配置
const groovyParser: Parser<string> = {
astFormat: parserName,
parse: (text: string) => text,
locStart: () => 0,
locEnd: (node: string) => node.length,
};
// 打印器配置
const groovyPrinter: Printer<string> = {
print: (path, options) => {
try {
return groovyBeautify(path.node, {
width: options.printWidth || 80,
}).trim();
} catch (error) {
return path.node;
}
},
};
const options = {
};
// 插件对象
const groovyPlugin: Plugin = {
languages,
parsers: {
[parserName]: groovyParser,
},
printers: {
[parserName]: groovyPrinter,
},
options,
};
export default groovyPlugin;
export { languages };
export const parsers = groovyPlugin.parsers;
export const printers = groovyPlugin.printers;

View File

@@ -0,0 +1,17 @@
import type { IToken } from "java-parser";
import { type AstPath } from "prettier";
import { type JavaNode, type JavaNonTerminal, type JavaParserOptions } from "./printers/helpers.js";
export declare function determineFormatterOffOnRanges(cst: JavaNonTerminal): void;
export declare function isFullyBetweenFormatterOffOn(path: AstPath<JavaNode>): boolean;
export declare function canAttachComment(node: JavaNode): boolean;
export declare function handleLineComment(commentNode: JavaComment, _: string, options: JavaParserOptions): boolean;
export declare function handleRemainingComment(commentNode: JavaComment): boolean;
export type JavaComment = IToken & {
value: string;
leading: boolean;
trailing: boolean;
printed: boolean;
enclosingNode?: JavaNonTerminal;
precedingNode?: JavaNonTerminal;
followingNode?: JavaNonTerminal;
};

View File

@@ -0,0 +1,199 @@
import { util } from "prettier";
import parser from "./parser.js";
import { isEmptyStatement, isNonTerminal, isTerminal } from "./printers/helpers.js";
const formatterOffOnRangesByCst = new WeakMap();
export function determineFormatterOffOnRanges(cst) {
const { comments } = cst;
if (!comments) {
return;
}
const ranges = comments
.filter(({ image }) => /^(\/\/\s*@formatter:(off|on)\s*|\/\*\s*@formatter:(off|on)\s*\*\/)$/.test(image))
.reduce((ranges, { image, startOffset }) => {
const previous = ranges.at(-1);
if (image.endsWith("off")) {
if ((previous === null || previous === void 0 ? void 0 : previous.on) !== Infinity) {
ranges.push({ off: startOffset, on: Infinity });
}
}
else if ((previous === null || previous === void 0 ? void 0 : previous.on) === Infinity) {
previous.on = startOffset;
}
return ranges;
}, new Array());
formatterOffOnRangesByCst.set(cst, ranges);
}
export function isFullyBetweenFormatterOffOn(path) {
var _a;
const { node, root } = path;
const start = parser.locStart(node);
const end = parser.locEnd(node);
return (((_a = formatterOffOnRangesByCst
.get(root)) === null || _a === void 0 ? void 0 : _a.some(range => range.off < start && end < range.on)) === true);
}
export function canAttachComment(node) {
var _a, _b, _c;
if (isTerminal(node)) {
const { name, CATEGORIES } = node.tokenType;
return (name === "Identifier" ||
(CATEGORIES === null || CATEGORIES === void 0 ? void 0 : CATEGORIES.find(({ name }) => name === "BinaryOperator")) !== undefined);
}
const { children, name } = node;
switch (name) {
case "argumentList":
case "blockStatements":
case "emptyStatement":
case "enumBodyDeclarations":
return false;
case "annotationInterfaceMemberDeclaration":
case "classMemberDeclaration":
case "interfaceMemberDeclaration":
case "methodBody":
return !children.Semicolon;
case "blockStatement":
return !children.statement || !isEmptyStatement(children.statement[0]);
case "classBodyDeclaration":
return !((_a = children.classMemberDeclaration) === null || _a === void 0 ? void 0 : _a[0].children.Semicolon);
case "recordBodyDeclaration":
return !((_c = (_b = children.classBodyDeclaration) === null || _b === void 0 ? void 0 : _b[0].children.classMemberDeclaration) === null || _c === void 0 ? void 0 : _c[0].children.Semicolon);
case "statement":
return !isEmptyStatement(node);
case "statementWithoutTrailingSubstatement":
return !children.emptyStatement;
default:
return true;
}
}
export function handleLineComment(commentNode, _, options) {
return [
handleBinaryExpressionComments,
handleFqnOrRefTypeComments,
handleIfStatementComments,
handleJumpStatementComments,
handleLabeledStatementComments,
handleNameComments
].some(fn => fn(commentNode, options));
}
export function handleRemainingComment(commentNode) {
return [
handleFqnOrRefTypeComments,
handleMethodDeclaratorComments,
handleNameComments,
handleJumpStatementComments
].some(fn => fn(commentNode));
}
function handleBinaryExpressionComments(commentNode, options) {
const { enclosingNode, precedingNode, followingNode } = commentNode;
if (enclosingNode &&
isNonTerminal(enclosingNode) &&
enclosingNode.name === "binaryExpression") {
if (isBinaryOperator(followingNode)) {
if (options.experimentalOperatorPosition === "start") {
util.addLeadingComment(followingNode, commentNode);
}
else {
util.addTrailingComment(followingNode, commentNode);
}
return true;
}
else if (options.experimentalOperatorPosition === "start" &&
isBinaryOperator(precedingNode)) {
util.addLeadingComment(precedingNode, commentNode);
return true;
}
}
return false;
}
function handleFqnOrRefTypeComments(commentNode) {
const { enclosingNode, followingNode } = commentNode;
if (enclosingNode &&
isNonTerminal(enclosingNode) &&
enclosingNode.name === "fqnOrRefType" &&
followingNode) {
util.addLeadingComment(followingNode, commentNode);
return true;
}
return false;
}
function handleIfStatementComments(commentNode) {
const { enclosingNode, precedingNode } = commentNode;
if (enclosingNode &&
isNonTerminal(enclosingNode) &&
enclosingNode.name === "ifStatement" &&
precedingNode &&
isNonTerminal(precedingNode) &&
precedingNode.name === "statement") {
util.addDanglingComment(enclosingNode, commentNode, undefined);
return true;
}
return false;
}
function handleJumpStatementComments(commentNode) {
const { enclosingNode, precedingNode, followingNode } = commentNode;
if (enclosingNode &&
!precedingNode &&
!followingNode &&
isNonTerminal(enclosingNode) &&
["breakStatement", "continueStatement", "returnStatement"].includes(enclosingNode.name)) {
util.addTrailingComment(enclosingNode, commentNode);
return true;
}
return false;
}
function handleLabeledStatementComments(commentNode) {
const { enclosingNode, precedingNode } = commentNode;
if (enclosingNode &&
precedingNode &&
isNonTerminal(enclosingNode) &&
enclosingNode.name === "labeledStatement" &&
isTerminal(precedingNode) &&
precedingNode.tokenType.name === "Identifier") {
util.addLeadingComment(precedingNode, commentNode);
return true;
}
return false;
}
function handleMethodDeclaratorComments(commentNode) {
const { enclosingNode } = commentNode;
if (enclosingNode &&
isNonTerminal(enclosingNode) &&
enclosingNode.name === "methodDeclarator" &&
!enclosingNode.children.receiverParameter &&
!enclosingNode.children.formalParameterList &&
enclosingNode.children.LBrace[0].startOffset < commentNode.startOffset &&
commentNode.startOffset < enclosingNode.children.RBrace[0].startOffset) {
util.addDanglingComment(enclosingNode, commentNode, undefined);
return true;
}
return false;
}
function handleNameComments(commentNode) {
const { enclosingNode, precedingNode } = commentNode;
if (enclosingNode &&
precedingNode &&
isNonTerminal(enclosingNode) &&
isTerminal(precedingNode) &&
precedingNode.tokenType.name === "Identifier" &&
[
"ambiguousName",
"classOrInterfaceTypeToInstantiate",
"expressionName",
"moduleDeclaration",
"moduleName",
"packageDeclaration",
"packageName",
"packageOrTypeName",
"typeName"
].includes(enclosingNode.name)) {
util.addTrailingComment(precedingNode, commentNode);
return true;
}
return false;
}
function isBinaryOperator(node) {
var _a;
return (node !== undefined &&
(isNonTerminal(node)
? node.name === "shiftOperator"
: (_a = node.tokenType.CATEGORIES) === null || _a === void 0 ? void 0 : _a.some(({ name }) => name === "BinaryOperator")));
}

View File

@@ -0,0 +1,563 @@
import type { JavaNode } from "./printers/helpers.js";
declare const _default: {
languages: {
name: string;
parsers: "java"[];
group: string;
tmScope: string;
aceMode: string;
codemirrorMode: string;
codemirrorMimeType: string;
extensions: string[];
linguistLanguageId: number;
vscodeLanguageIds: string[];
}[];
parsers: {
java: {
parse(text: string, options: import("./printers/helpers.js").JavaParserOptions): import("./printers/helpers.js").JavaNonTerminal;
astFormat: string;
hasPragma(text: string): boolean;
locStart(node: JavaNode): number;
locEnd(node: JavaNode): number;
};
};
printers: {
java: {
print(path: import("prettier").AstPath<import("java-parser").ArrayInitializerCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").VariableInitializerListCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").BlockCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").BlockStatementsCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").LocalVariableDeclarationStatementCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").LocalVariableDeclarationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").LabeledStatementCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ExpressionStatementCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").IfStatementCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").AssertStatementCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").SwitchStatementCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").SwitchBlockCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").SwitchBlockStatementGroupCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").SwitchLabelCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").SwitchRuleCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").WhileStatementCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").DoStatementCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").BasicForStatementCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").StatementExpressionListCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").EnhancedForStatementCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").BreakStatementCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ContinueStatementCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ReturnStatementCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ThrowStatementCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").SynchronizedStatementCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").TryStatementCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").CatchesCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").CatchClauseCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").CatchFormalParameterCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").CatchTypeCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").FinallyCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").TryWithResourcesStatementCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ResourceSpecificationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ResourceListCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").YieldStatementCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ForInitCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ForUpdateCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").StatementCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").StatementWithoutTrailingSubstatementCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ForStatementCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").BlockStatementCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").CaseConstantCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").CasePatternCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").EmptyStatementCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").StatementExpressionCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").LocalVariableTypeCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ResourceCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").VariableAccessCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ClassDeclarationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").NormalClassDeclarationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").TypeParametersCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").TypeParameterListCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ClassExtendsCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ClassImplementsCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").InterfaceTypeListCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ClassBodyCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ClassMemberDeclarationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").FieldDeclarationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").VariableDeclaratorListCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").VariableDeclaratorCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").VariableDeclaratorIdCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").UnannPrimitiveTypeWithOptionalDimsSuffixCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").UnannReferenceTypeCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").MethodDeclarationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").MethodHeaderCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").MethodDeclaratorCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ReceiverParameterCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").FormalParameterListCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").VariableParaRegularParameterCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").VariableArityParameterCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ThrowsCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ExceptionTypeListCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").StaticInitializerCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ConstructorDeclarationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ConstructorDeclaratorCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ConstructorBodyCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").UnqualifiedExplicitConstructorInvocationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").QualifiedExplicitConstructorInvocationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").EnumDeclarationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").EnumBodyCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").EnumConstantListCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").EnumConstantCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").EnumBodyDeclarationsCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").RecordDeclarationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").RecordHeaderCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").RecordComponentListCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").RecordComponentCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").VariableArityRecordComponentCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").RecordBodyCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").CompactConstructorDeclarationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").UnannTypeCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").VariableInitializerCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").VariableModifierCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").UnannClassTypeCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ClassBodyDeclarationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").InstanceInitializerCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ClassModifierCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ClassPermitsCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").FieldModifierCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").MethodModifierCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").MethodBodyCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ConstructorModifierCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").SimpleTypeNameCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ExplicitConstructorInvocationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").EnumConstantModifierCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ExceptionTypeCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").FormalParameterCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ResultCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").RecordBodyDeclarationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").RecordComponentModifierCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").UnannClassOrInterfaceTypeCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").UnannInterfaceTypeCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").UnannPrimitiveTypeCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").UnannTypeVariableCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").LambdaExpressionCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").LambdaParametersCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").LambdaParametersWithBracesCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ConciseLambdaParameterListCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").NormalLambdaParameterListCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").RegularLambdaParameterCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ConditionalExpressionCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").BinaryExpressionCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").UnaryExpressionCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").UnaryExpressionNotPlusMinusCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").PrimaryCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").PrimarySuffixCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").FqnOrRefTypeCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").FqnOrRefTypePartFirstCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").FqnOrRefTypePartRestCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").FqnOrRefTypePartCommonCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ParenthesisExpressionCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").PrimitiveCastExpressionCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ReferenceTypeCastExpressionCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").UnqualifiedClassInstanceCreationExpressionCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ClassOrInterfaceTypeToInstantiateCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").MethodInvocationSuffixCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ArgumentListCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ArrayCreationExpressionCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ArrayCreationExpressionWithoutInitializerSuffixCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ArrayCreationWithInitializerSuffixCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").DimExprsCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").DimExprCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ClassLiteralSuffixCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ArrayAccessSuffixCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").MethodReferenceSuffixCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").StringTemplateCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").TextBlockTemplateCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").RecordPatternCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ComponentPatternListCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").GuardCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").TemplateCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").PatternCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ExpressionCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").TypePatternCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").CastExpressionCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").TypeArgumentsOrDiamondCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").DiamondCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ComponentPatternCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").MatchAllPatternCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ConciseLambdaParameterCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").EmbeddedExpressionCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").LambdaBodyCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").LambdaParameterListCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").NormalLambdaParameterCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").LambdaParameterTypeCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").NewExpressionCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").PrimaryPrefixCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").TemplateArgumentCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").InterfaceDeclarationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").NormalInterfaceDeclarationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").InterfaceExtendsCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").InterfaceBodyCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").InterfaceMemberDeclarationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ConstantDeclarationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").InterfaceMethodDeclarationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").AnnotationInterfaceDeclarationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").AnnotationInterfaceBodyCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").AnnotationInterfaceMemberDeclarationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").AnnotationInterfaceElementDeclarationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").DefaultValueCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").AnnotationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ElementValuePairListCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ElementValuePairCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ElementValueArrayInitializerCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ElementValueListCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ElementValueCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").AnnotationInterfaceElementModifierCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ConstantModifierCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").InterfaceModifierCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").InterfacePermitsCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").InterfaceMethodModifierCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").LiteralCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ShiftOperatorCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").BooleanLiteralCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").FloatingPointLiteralCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").IntegerLiteralCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").MethodNameCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").AmbiguousNameCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").TypeNameCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").TypeIdentifierCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ExpressionNameCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").PackageNameCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ModuleNameCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").PackageOrTypeNameCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").CompilationUnitCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").OrdinaryCompilationUnitCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ModularCompilationUnitCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").PackageDeclarationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ImportDeclarationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").TypeDeclarationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ModuleDeclarationCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").RequiresModuleDirectiveCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ExportsModuleDirectiveCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").OpensModuleDirectiveCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").UsesModuleDirectiveCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ProvidesModuleDirectiveCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ModuleDirectiveCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").RequiresModifierCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").PackageModifierCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").PrimitiveTypeCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ReferenceTypeCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").TypeVariableCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").DimsCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").TypeParameterCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").TypeBoundCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").AdditionalBoundCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").TypeArgumentsCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").TypeArgumentListCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").WildcardCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").WildcardBoundsCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").InterfaceTypeCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ClassTypeCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").NumericTypeCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").ClassOrInterfaceTypeCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").FloatingPointTypeCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").IntegralTypeCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").TypeArgumentCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").TypeParameterModifierCstNode & {
comments?: import("./comments.js").JavaComment[];
}> | import("prettier").AstPath<import("java-parser").IToken & {
comments?: import("./comments.js").JavaComment[];
}>, options: import("prettier").ParserOptions<JavaNode>, print: (path: import("prettier").AstPath<JavaNode>) => import("prettier").Doc, args: unknown): import("prettier/doc.js").builders.Doc;
hasPrettierIgnore(path: import("prettier").AstPath<JavaNode>): boolean;
canAttachComment: typeof import("./comments.js").canAttachComment;
isBlockComment(node: JavaNode): boolean;
printComment(commentPath: import("prettier").AstPath<JavaNode>): string | import("prettier/doc.js").builders.Doc[];
getCommentChildNodes(node: JavaNode): any[];
handleComments: {
ownLine: typeof import("./comments.js").handleLineComment;
endOfLine: typeof import("./comments.js").handleLineComment;
remaining: typeof import("./comments.js").handleRemainingComment;
};
};
};
options: {
entrypoint: {
type: "choice";
category: string;
default: string;
choices: {
value: string;
description: string;
}[];
description: string;
};
arrowParens: {
type: "choice";
category: string;
default: string;
choices: {
value: string;
description: string;
}[];
description: string;
};
trailingComma: {
type: "choice";
category: string;
default: string;
choices: {
value: string;
description: string;
}[];
description: string;
};
experimentalOperatorPosition: {
type: "choice";
category: string;
default: string;
choices: {
value: string;
description: string;
}[];
description: string;
};
};
defaultOptions: {
arrowParens: "avoid";
};
};
export default _default;

View File

@@ -0,0 +1,29 @@
import options from "./options.js";
import parser from "./parser.js";
import printer from "./printer.js";
export default {
languages: [
{
name: "Java",
parsers: ["java"],
group: "Java",
tmScope: "source.java",
aceMode: "java",
codemirrorMode: "clike",
codemirrorMimeType: "text/x-java",
extensions: [".java"],
linguistLanguageId: 181,
vscodeLanguageIds: ["java"]
}
],
parsers: {
java: parser
},
printers: {
java: printer
},
options,
defaultOptions: {
arrowParens: "avoid"
}
};

View File

@@ -0,0 +1,43 @@
declare const _default: {
entrypoint: {
type: "choice";
category: string;
default: string;
choices: {
value: string;
description: string;
}[];
description: string;
};
arrowParens: {
type: "choice";
category: string;
default: string;
choices: {
value: string;
description: string;
}[];
description: string;
};
trailingComma: {
type: "choice";
category: string;
default: string;
choices: {
value: string;
description: string;
}[];
description: string;
};
experimentalOperatorPosition: {
type: "choice";
category: string;
default: string;
choices: {
value: string;
description: string;
}[];
description: string;
};
};
export default _default;

View File

@@ -0,0 +1,284 @@
export default {
entrypoint: {
type: "choice",
category: "Global",
default: "compilationUnit",
// sed -nr 's/.*\.RULE\(([^,]+),.*/\1/p' $(ls path/to/java-parser/rules/folder/*)
choices: [
{ value: "arrayInitializer", description: "" },
{ value: "variableInitializerList", description: "" },
{ value: "block", description: "" },
{ value: "blockStatements", description: "" },
{ value: "blockStatement", description: "" },
{ value: "localVariableDeclarationStatement", description: "" },
{ value: "localVariableDeclaration", description: "" },
{ value: "localVariableType", description: "" },
{ value: "statement", description: "" },
{ value: "statementWithoutTrailingSubstatement", description: "" },
{ value: "emptyStatement", description: "" },
{ value: "labeledStatement", description: "" },
{ value: "expressionStatement", description: "" },
{ value: "statementExpression", description: "" },
{ value: "ifStatement", description: "" },
{ value: "assertStatement", description: "" },
{ value: "switchStatement", description: "" },
{ value: "switchBlock", description: "" },
{ value: "switchBlockStatementGroup", description: "" },
{ value: "switchLabel", description: "" },
{ value: "switchRule", description: "" },
{ value: "caseConstant", description: "" },
{ value: "casePattern", description: "" },
{ value: "whileStatement", description: "" },
{ value: "doStatement", description: "" },
{ value: "forStatement", description: "" },
{ value: "basicForStatement", description: "" },
{ value: "forInit", description: "" },
{ value: "forUpdate", description: "" },
{ value: "statementExpressionList", description: "" },
{ value: "enhancedForStatement", description: "" },
{ value: "breakStatement", description: "" },
{ value: "continueStatement", description: "" },
{ value: "returnStatement", description: "" },
{ value: "throwStatement", description: "" },
{ value: "synchronizedStatement", description: "" },
{ value: "tryStatement", description: "" },
{ value: "catches", description: "" },
{ value: "catchClause", description: "" },
{ value: "catchFormalParameter", description: "" },
{ value: "catchType", description: "" },
{ value: "finally", description: "" },
{ value: "tryWithResourcesStatement", description: "" },
{ value: "resourceSpecification", description: "" },
{ value: "resourceList", description: "" },
{ value: "resource", description: "" },
{ value: "yieldStatement", description: "" },
{ value: "variableAccess", description: "" },
{ value: "classDeclaration", description: "" },
{ value: "normalClassDeclaration", description: "" },
{ value: "classModifier", description: "" },
{ value: "typeParameters", description: "" },
{ value: "typeParameterList", description: "" },
{ value: "classExtends", description: "" },
{ value: "classImplements", description: "" },
{ value: "interfaceTypeList", description: "" },
{ value: "classPermits", description: "" },
{ value: "classBody", description: "" },
{ value: "classBodyDeclaration", description: "" },
{ value: "classMemberDeclaration", description: "" },
{ value: "fieldDeclaration", description: "" },
{ value: "fieldModifier", description: "" },
{ value: "variableDeclaratorList", description: "" },
{ value: "variableDeclarator", description: "" },
{ value: "variableDeclaratorId", description: "" },
{ value: "variableInitializer", description: "" },
{ value: "unannType", description: "" },
{ value: "unannPrimitiveTypeWithOptionalDimsSuffix", description: "" },
{ value: "unannPrimitiveType", description: "" },
{ value: "unannReferenceType", description: "" },
{ value: "unannClassOrInterfaceType", description: "" },
{ value: "unannClassType", description: "" },
{ value: "unannInterfaceType", description: "" },
{ value: "unannTypeVariable", description: "" },
{ value: "methodDeclaration", description: "" },
{ value: "methodModifier", description: "" },
{ value: "methodHeader", description: "" },
{ value: "result", description: "" },
{ value: "methodDeclarator", description: "" },
{ value: "receiverParameter", description: "" },
{ value: "formalParameterList", description: "" },
{ value: "formalParameter", description: "" },
{ value: "variableParaRegularParameter", description: "" },
{ value: "variableArityParameter", description: "" },
{ value: "variableModifier", description: "" },
{ value: "throws", description: "" },
{ value: "exceptionTypeList", description: "" },
{ value: "exceptionType", description: "" },
{ value: "methodBody", description: "" },
{ value: "instanceInitializer", description: "" },
{ value: "staticInitializer", description: "" },
{ value: "constructorDeclaration", description: "" },
{ value: "constructorModifier", description: "" },
{ value: "constructorDeclarator", description: "" },
{ value: "simpleTypeName", description: "" },
{ value: "constructorBody", description: "" },
{ value: "explicitConstructorInvocation", description: "" },
{ value: "unqualifiedExplicitConstructorInvocation", description: "" },
{ value: "qualifiedExplicitConstructorInvocation", description: "" },
{ value: "enumDeclaration", description: "" },
{ value: "enumBody", description: "" },
{ value: "enumConstantList", description: "" },
{ value: "enumConstant", description: "" },
{ value: "enumConstantModifier", description: "" },
{ value: "enumBodyDeclarations", description: "" },
{ value: "recordDeclaration", description: "" },
{ value: "recordHeader", description: "" },
{ value: "recordComponentList", description: "" },
{ value: "recordComponent", description: "" },
{ value: "variableArityRecordComponent", description: "" },
{ value: "recordComponentModifier", description: "" },
{ value: "recordBody", description: "" },
{ value: "recordBodyDeclaration", description: "" },
{ value: "compactConstructorDeclaration", description: "" },
{ value: "isDims", description: "" },
{ value: "expression", description: "" },
{ value: "lambdaExpression", description: "" },
{ value: "lambdaParameters", description: "" },
{ value: "lambdaParametersWithBraces", description: "" },
{ value: "lambdaParameterList", description: "" },
{ value: "conciseLambdaParameterList", description: "" },
{ value: "normalLambdaParameterList", description: "" },
{ value: "normalLambdaParameter", description: "" },
{ value: "regularLambdaParameter", description: "" },
{ value: "lambdaParameterType", description: "" },
{ value: "conciseLambdaParameter", description: "" },
{ value: "lambdaBody", description: "" },
{ value: "conditionalExpression", description: "" },
{ value: "binaryExpression", description: "" },
{ value: "unaryExpression", description: "" },
{ value: "unaryExpressionNotPlusMinus", description: "" },
{ value: "primary", description: "" },
{ value: "primaryPrefix", description: "" },
{ value: "primarySuffix", description: "" },
{ value: "fqnOrRefType", description: "" },
{ value: "fqnOrRefTypePartRest", description: "" },
{ value: "fqnOrRefTypePartCommon", description: "" },
{ value: "fqnOrRefTypePartFirst", description: "" },
{ value: "parenthesisExpression", description: "" },
{ value: "castExpression", description: "" },
{ value: "primitiveCastExpression", description: "" },
{ value: "referenceTypeCastExpression", description: "" },
{ value: "newExpression", description: "" },
{ value: "unqualifiedClassInstanceCreationExpression", description: "" },
{ value: "classOrInterfaceTypeToInstantiate", description: "" },
{ value: "typeArgumentsOrDiamond", description: "" },
{ value: "diamond", description: "" },
{ value: "methodInvocationSuffix", description: "" },
{ value: "argumentList", description: "" },
{ value: "arrayCreationExpression", description: "" },
{
value: "arrayCreationExpressionWithoutInitializerSuffix",
description: ""
},
{ value: "arrayCreationWithInitializerSuffix", description: "" },
{ value: "dimExprs", description: "" },
{ value: "dimExpr", description: "" },
{ value: "classLiteralSuffix", description: "" },
{ value: "arrayAccessSuffix", description: "" },
{ value: "methodReferenceSuffix", description: "" },
{ value: "templateArgument", description: "" },
{ value: "template", description: "" },
{ value: "stringTemplate", description: "" },
{ value: "textBlockTemplate", description: "" },
{ value: "embeddedExpression", description: "" },
{ value: "pattern", description: "" },
{ value: "typePattern", description: "" },
{ value: "recordPattern", description: "" },
{ value: "componentPatternList", description: "" },
{ value: "componentPattern", description: "" },
{ value: "matchAllPattern", description: "" },
{ value: "guard", description: "" },
{ value: "isRefTypeInMethodRef", description: "" },
{ value: "interfaceDeclaration", description: "" },
{ value: "normalInterfaceDeclaration", description: "" },
{ value: "interfaceModifier", description: "" },
{ value: "interfaceExtends", description: "" },
{ value: "interfacePermits", description: "" },
{ value: "interfaceBody", description: "" },
{ value: "interfaceMemberDeclaration", description: "" },
{ value: "constantDeclaration", description: "" },
{ value: "constantModifier", description: "" },
{ value: "interfaceMethodDeclaration", description: "" },
{ value: "interfaceMethodModifier", description: "" },
{ value: "annotationInterfaceDeclaration", description: "" },
{ value: "annotationInterfaceBody", description: "" },
{ value: "annotationInterfaceMemberDeclaration", description: "" },
{ value: "annotationInterfaceElementDeclaration", description: "" },
{ value: "annotationInterfaceElementModifier", description: "" },
{ value: "defaultValue", description: "" },
{ value: "annotation", description: "" },
{ value: "elementValuePairList", description: "" },
{ value: "elementValuePair", description: "" },
{ value: "elementValue", description: "" },
{ value: "elementValueArrayInitializer", description: "" },
{ value: "elementValueList", description: "" },
{ value: "literal", description: "" },
{ value: "integerLiteral", description: "" },
{ value: "floatingPointLiteral", description: "" },
{ value: "booleanLiteral", description: "" },
{ value: "shiftOperator", description: "" },
{ value: "moduleName", description: "" },
{ value: "packageName", description: "" },
{ value: "typeName", description: "" },
{ value: "expressionName", description: "" },
{ value: "methodName", description: "" },
{ value: "packageOrTypeName", description: "" },
{ value: "ambiguousName", description: "" },
{ value: "compilationUnit", description: "" },
{ value: "ordinaryCompilationUnit", description: "" },
{ value: "modularCompilationUnit", description: "" },
{ value: "packageDeclaration", description: "" },
{ value: "packageModifier", description: "" },
{ value: "importDeclaration", description: "" },
{ value: "typeDeclaration", description: "" },
{ value: "moduleDeclaration", description: "" },
{ value: "moduleDirective", description: "" },
{ value: "requiresModuleDirective", description: "" },
{ value: "exportsModuleDirective", description: "" },
{ value: "opensModuleDirective", description: "" },
{ value: "usesModuleDirective", description: "" },
{ value: "providesModuleDirective", description: "" },
{ value: "requiresModifier", description: "" },
{ value: "primitiveType", description: "" },
{ value: "numericType", description: "" },
{ value: "integralType", description: "" },
{ value: "floatingPointType", description: "" },
{ value: "referenceType", description: "" },
{ value: "classOrInterfaceType", description: "" },
{ value: "classType", description: "" },
{ value: "interfaceType", description: "" },
{ value: "typeVariable", description: "" },
{ value: "dims", description: "" },
{ value: "typeParameter", description: "" },
{ value: "typeParameterModifier", description: "" },
{ value: "typeBound", description: "" },
{ value: "additionalBound", description: "" },
{ value: "typeArguments", description: "" },
{ value: "typeArgumentList", description: "" },
{ value: "typeArgument", description: "" },
{ value: "wildcard", description: "" },
{ value: "wildcardBounds", description: "" }
],
description: "Prettify from the entrypoint, allowing to use prettier on snippet."
},
arrowParens: {
type: "choice",
category: "Java",
default: "always",
choices: [
{ value: "always", description: "" },
{ value: "avoid", description: "" }
],
description: "Include parentheses around a sole arrow function parameter."
},
trailingComma: {
type: "choice",
category: "Java",
default: "all",
choices: [
{ value: "all", description: "" },
{ value: "es5", description: "" },
{ value: "none", description: "" }
],
description: "Print trailing commas wherever possible when multi-line."
},
experimentalOperatorPosition: {
type: "choice",
category: "Java",
default: "end",
choices: [
{ value: "start", description: "" },
{ value: "end", description: "" }
],
description: "Where to print operators when binary expressions wrap lines."
}
};

View File

@@ -0,0 +1,9 @@
import { type JavaNode, type JavaNonTerminal, type JavaParserOptions } from "./printers/helpers.js";
declare const _default: {
parse(text: string, options: JavaParserOptions): JavaNonTerminal;
astFormat: string;
hasPragma(text: string): boolean;
locStart(node: JavaNode): number;
locEnd(node: JavaNode): number;
};
export default _default;

View File

@@ -0,0 +1,24 @@
import { parse } from "java-parser";
import { determineFormatterOffOnRanges } from "./comments.js";
import { isTerminal } from "./printers/helpers.js";
export default {
parse(text, options) {
var _a;
const cst = parse(text, options.entrypoint);
(_a = cst.comments) === null || _a === void 0 ? void 0 : _a.forEach(comment => {
comment.value = comment.image;
});
determineFormatterOffOnRanges(cst);
return cst;
},
astFormat: "java",
hasPragma(text) {
return /^\/\*\*\n\s+\*\s@(format|prettier)\n\s+\*\//.test(text);
},
locStart(node) {
return isTerminal(node) ? node.startOffset : node.location.startOffset;
},
locEnd(node) {
return (isTerminal(node) ? node.endOffset : node.location.endOffset) + 1;
}
};

View File

@@ -0,0 +1,18 @@
import type { AstPath } from "prettier";
import { canAttachComment, handleLineComment, handleRemainingComment } from "./comments.js";
import { type JavaNode } from "./printers/helpers.js";
declare const _default: {
print(path: DistributedAstPath<JavaNode>, options: import("prettier").ParserOptions<JavaNode>, print: (path: AstPath<JavaNode>) => import("prettier").Doc, args: unknown): import("prettier/doc.js").builders.Doc;
hasPrettierIgnore(path: AstPath<JavaNode>): boolean;
canAttachComment: typeof canAttachComment;
isBlockComment(node: JavaNode): boolean;
printComment(commentPath: AstPath<JavaNode>): string | import("prettier/doc.js").builders.Doc[];
getCommentChildNodes(node: JavaNode): any[];
handleComments: {
ownLine: typeof handleLineComment;
endOfLine: typeof handleLineComment;
remaining: typeof handleRemainingComment;
};
};
export default _default;
type DistributedAstPath<T> = T extends any ? AstPath<T> : never;

View File

@@ -0,0 +1,40 @@
import { canAttachComment, handleLineComment, handleRemainingComment, isFullyBetweenFormatterOffOn } from "./comments.js";
import { isNonTerminal, isTerminal, printComment } from "./printers/helpers.js";
import { printerForNodeType } from "./printers/index.js";
export default {
print(path, options, print, args) {
return hasTerminal(path)
? path.node.image
: printerForNodeType(path.node.name)(path, print, options, args);
},
hasPrettierIgnore(path) {
var _a;
const { node } = path;
return (((_a = node.comments) === null || _a === void 0 ? void 0 : _a.some(({ image }) => /^(\/\/\s*prettier-ignore|\/\*\s*prettier-ignore\s*\*\/)$/.test(image))) === true ||
(canAttachComment(node) && isFullyBetweenFormatterOffOn(path)));
},
canAttachComment,
isBlockComment(node) {
return isTerminal(node) && node.tokenType.name === "TraditionalComment";
},
printComment(commentPath) {
const { node } = commentPath;
if (isNonTerminal(node) || node.tokenType.GROUP !== "comments") {
throw new Error(`Not a comment: ${JSON.stringify(node)}`);
}
return printComment(node);
},
getCommentChildNodes(node) {
return isNonTerminal(node)
? Object.values(node.children).flatMap(child => child)
: [];
},
handleComments: {
ownLine: handleLineComment,
endOfLine: handleLineComment,
remaining: handleRemainingComment
}
};
function hasTerminal(path) {
return isTerminal(path.node);
}

View File

@@ -0,0 +1,9 @@
declare const _default: {
arrayInitializer(path: import("prettier").AstPath<import("java-parser").ArrayInitializerCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn, options: import("./helpers.js").JavaParserOptions): import("prettier/doc.js").builders.Group | "{}";
variableInitializerList(path: import("prettier").AstPath<import("java-parser").VariableInitializerListCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): import("prettier/doc.js").builders.Doc[];
};
export default _default;

View File

@@ -0,0 +1,9 @@
import { printArrayInitializer, printList } from "./helpers.js";
export default {
arrayInitializer(path, print, options) {
return printArrayInitializer(path, print, options, "variableInitializerList");
},
variableInitializerList(path, print) {
return printList(path, print, "variableInitializer");
}
};

View File

@@ -0,0 +1,117 @@
import { builders } from "prettier/doc";
import { printSingle } from "./helpers.js";
declare const _default: {
block(path: import("prettier").AstPath<import("java-parser").BlockCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[];
blockStatements(path: import("prettier").AstPath<import("java-parser").BlockStatementsCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
blockStatement: typeof printSingle;
localVariableDeclarationStatement(path: import("prettier").AstPath<import("java-parser").LocalVariableDeclarationStatementCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
localVariableDeclaration(path: import("prettier").AstPath<import("java-parser").LocalVariableDeclarationCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
localVariableType: typeof printSingle;
statement: typeof printSingle;
statementWithoutTrailingSubstatement: typeof printSingle;
emptyStatement(): string;
labeledStatement(path: import("prettier").AstPath<import("java-parser").LabeledStatementCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
expressionStatement(path: import("prettier").AstPath<import("java-parser").ExpressionStatementCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
statementExpression: typeof printSingle;
ifStatement(path: import("prettier").AstPath<import("java-parser").IfStatementCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
assertStatement(path: import("prettier").AstPath<import("java-parser").AssertStatementCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
switchStatement(path: import("prettier").AstPath<import("java-parser").SwitchStatementCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
switchBlock(path: import("prettier").AstPath<import("java-parser").SwitchBlockCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[];
switchBlockStatementGroup(path: import("prettier").AstPath<import("java-parser").SwitchBlockStatementGroupCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
switchLabel(path: import("prettier").AstPath<import("java-parser").SwitchLabelCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): "default" | builders.Group | builders.Doc[];
switchRule(path: import("prettier").AstPath<import("java-parser").SwitchRuleCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
caseConstant: typeof printSingle;
casePattern: typeof printSingle;
whileStatement(path: import("prettier").AstPath<import("java-parser").WhileStatementCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
doStatement(path: import("prettier").AstPath<import("java-parser").DoStatementCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): (string | builders.Group | builders.Doc[])[];
forStatement: typeof printSingle;
basicForStatement(path: import("prettier").AstPath<import("java-parser").BasicForStatementCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
forInit: typeof printSingle;
forUpdate: typeof printSingle;
statementExpressionList(path: import("prettier").AstPath<import("java-parser").StatementExpressionListCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Group;
enhancedForStatement(path: import("prettier").AstPath<import("java-parser").EnhancedForStatementCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Group;
breakStatement(path: import("prettier").AstPath<import("java-parser").BreakStatementCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[] | "break;";
continueStatement(path: import("prettier").AstPath<import("java-parser").ContinueStatementCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[] | "continue;";
returnStatement(path: import("prettier").AstPath<import("java-parser").ReturnStatementCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
throwStatement(path: import("prettier").AstPath<import("java-parser").ThrowStatementCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
synchronizedStatement(path: import("prettier").AstPath<import("java-parser").SynchronizedStatementCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
tryStatement(path: import("prettier").AstPath<import("java-parser").TryStatementCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc;
catches(path: import("prettier").AstPath<import("java-parser").CatchesCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
catchClause(path: import("prettier").AstPath<import("java-parser").CatchClauseCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
catchFormalParameter(path: import("prettier").AstPath<import("java-parser").CatchFormalParameterCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
catchType(path: import("prettier").AstPath<import("java-parser").CatchTypeCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
finally(path: import("prettier").AstPath<import("java-parser").FinallyCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
tryWithResourcesStatement(path: import("prettier").AstPath<import("java-parser").TryWithResourcesStatementCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
resourceSpecification(path: import("prettier").AstPath<import("java-parser").ResourceSpecificationCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Group | "()";
resourceList(path: import("prettier").AstPath<import("java-parser").ResourceListCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
resource: typeof printSingle;
yieldStatement(path: import("prettier").AstPath<import("java-parser").YieldStatementCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
variableAccess: typeof printSingle;
};
export default _default;

View File

@@ -0,0 +1,337 @@
import { builders } from "prettier/doc";
import { call, definedKeys, indentInParentheses, isBinaryExpression, isEmptyStatement, lineEndWithComments, lineStartWithComments, map, onlyDefinedKey, printBlock, printDanglingComments, printSingle, printWithModifiers } from "./helpers.js";
const { group, hardline, ifBreak, indent, join, line, softline } = builders;
export default {
block(path, print) {
const statements = path.node.children.blockStatements
? call(path, print, "blockStatements")
: [];
return printBlock(path, statements.length ? [statements] : []);
},
blockStatements(path, print) {
return join(hardline, map(path, statementPath => {
const { node, previous } = statementPath;
const statement = print(statementPath);
return previous &&
lineStartWithComments(node) > lineEndWithComments(previous) + 1
? [hardline, statement]
: statement;
}, "blockStatement").filter(doc => doc !== ""));
},
blockStatement: printSingle,
localVariableDeclarationStatement(path, print) {
return [call(path, print, "localVariableDeclaration"), ";"];
},
localVariableDeclaration(path, print) {
const declaration = join(" ", [
call(path, print, "localVariableType"),
call(path, print, "variableDeclaratorList")
]);
return printWithModifiers(path, print, "variableModifier", declaration);
},
localVariableType: printSingle,
statement: printSingle,
statementWithoutTrailingSubstatement: printSingle,
emptyStatement() {
return "";
},
labeledStatement(path, print) {
return [
call(path, print, "Identifier"),
": ",
call(path, print, "statement")
];
},
expressionStatement(path, print) {
return [call(path, print, "statementExpression"), ";"];
},
statementExpression: printSingle,
ifStatement(path, print) {
var _a;
const { children } = path.node;
const hasEmptyStatement = isEmptyStatement(children.statement[0]);
const statements = map(path, print, "statement");
const statement = [
"if ",
indentInParentheses(call(path, print, "expression")),
hasEmptyStatement ? ";" : [" ", statements[0]]
];
if (children.Else) {
const danglingComments = printDanglingComments(path);
if (danglingComments.length) {
statement.push(hardline, ...danglingComments, hardline);
}
else {
const elseHasBlock = ((_a = children.statement[0].children
.statementWithoutTrailingSubstatement) === null || _a === void 0 ? void 0 : _a[0].children.block) !==
undefined;
statement.push(elseHasBlock ? " " : hardline);
}
const elseHasEmptyStatement = isEmptyStatement(children.statement[1]);
statement.push("else", elseHasEmptyStatement ? ";" : [" ", statements[1]]);
}
return statement;
},
assertStatement(path, print) {
return ["assert ", ...join([" : "], map(path, print, "expression")), ";"];
},
switchStatement(path, print) {
return join(" ", [
"switch",
indentInParentheses(call(path, print, "expression")),
call(path, print, "switchBlock")
]);
},
switchBlock(path, print) {
const { children } = path.node;
const caseKeys = definedKeys(children, [
"switchBlockStatementGroup",
"switchRule"
]);
const cases = caseKeys.length === 1 ? map(path, print, caseKeys[0]) : [];
return printBlock(path, cases);
},
switchBlockStatementGroup(path, print) {
var _a, _b;
const { children } = path.node;
const switchLabel = call(path, print, "switchLabel");
if (!children.blockStatements) {
return [switchLabel, ":"];
}
const blockStatements = call(path, print, "blockStatements");
const statements = children.blockStatements[0].children.blockStatement;
const onlyStatementIsBlock = statements.length === 1 &&
((_b = (_a = statements[0].children.statement) === null || _a === void 0 ? void 0 : _a[0].children.statementWithoutTrailingSubstatement) === null || _b === void 0 ? void 0 : _b[0].children.block) !== undefined;
return [
switchLabel,
":",
onlyStatementIsBlock
? [" ", blockStatements]
: indent([hardline, blockStatements])
];
},
switchLabel(path, print) {
var _a, _b;
const { children } = path.node;
if (!((_b = (_a = children.caseConstant) !== null && _a !== void 0 ? _a : children.casePattern) !== null && _b !== void 0 ? _b : children.Null)) {
return "default";
}
const values = [];
if (children.Null) {
values.push("null");
if (children.Default) {
values.push("default");
}
}
else {
const valuesKey = onlyDefinedKey(children, [
"caseConstant",
"casePattern"
]);
values.push(...map(path, print, valuesKey));
}
const hasMultipleValues = values.length > 1;
const label = hasMultipleValues
? ["case", indent([line, ...join([",", line], values)])]
: ["case ", values[0]];
return children.guard
? [
group([...label, hasMultipleValues ? line : " "]),
call(path, print, "guard")
]
: group(label);
},
switchRule(path, print) {
const { children } = path.node;
const bodyKey = onlyDefinedKey(children, [
"block",
"expression",
"throwStatement"
]);
const parts = [
call(path, print, "switchLabel"),
" -> ",
call(path, print, bodyKey)
];
if (children.Semicolon) {
parts.push(";");
}
return parts;
},
caseConstant: printSingle,
casePattern: printSingle,
whileStatement(path, print) {
const statement = call(path, print, "statement");
const hasEmptyStatement = isEmptyStatement(path.node.children.statement[0]);
return [
"while ",
indentInParentheses(call(path, print, "expression")),
...[hasEmptyStatement ? ";" : " ", statement]
];
},
doStatement(path, print) {
const hasEmptyStatement = isEmptyStatement(path.node.children.statement[0]);
return [
"do",
hasEmptyStatement ? ";" : [" ", call(path, print, "statement")],
" while ",
indentInParentheses(call(path, print, "expression")),
";"
];
},
forStatement: printSingle,
basicForStatement(path, print) {
const { children } = path.node;
const danglingComments = printDanglingComments(path);
if (danglingComments.length) {
danglingComments.push(hardline);
}
const expressions = ["forInit", "expression", "forUpdate"].map(expressionKey => expressionKey in children ? call(path, print, expressionKey) : "");
const hasEmptyStatement = isEmptyStatement(children.statement[0]);
return [
...danglingComments,
"for ",
expressions.some(expression => expression !== "")
? indentInParentheses(join([";", line], expressions))
: "(;;)",
hasEmptyStatement ? ";" : [" ", call(path, print, "statement")]
];
},
forInit: printSingle,
forUpdate: printSingle,
statementExpressionList(path, print) {
return group(map(path, print, "statementExpression").map((expression, index) => index === 0 ? expression : [",", indent([line, expression])]));
},
enhancedForStatement(path, print) {
var _a;
const statementNode = path.node.children.statement[0];
const forStatement = [
printDanglingComments(path),
"for ",
"(",
call(path, print, "localVariableDeclaration"),
" : ",
call(path, print, "expression"),
")"
];
if (isEmptyStatement(statementNode)) {
forStatement.push(";");
}
else {
const hasStatementBlock = ((_a = statementNode.children.statementWithoutTrailingSubstatement) === null || _a === void 0 ? void 0 : _a[0].children.block) !== undefined;
const statement = call(path, print, "statement");
forStatement.push(hasStatementBlock ? [" ", statement] : indent([line, statement]));
}
return group(forStatement);
},
breakStatement(path, print) {
return path.node.children.Identifier
? ["break ", call(path, print, "Identifier"), ";"]
: "break;";
},
continueStatement(path, print) {
return path.node.children.Identifier
? ["continue ", call(path, print, "Identifier"), ";"]
: "continue;";
},
returnStatement(path, print) {
const { children } = path.node;
const statement = ["return"];
if (children.expression) {
statement.push(" ");
const expression = call(path, print, "expression");
if (isBinaryExpression(children.expression[0])) {
statement.push(group([
ifBreak("("),
indent([softline, expression]),
softline,
ifBreak(")")
]));
}
else {
statement.push(expression);
}
}
statement.push(";");
return statement;
},
throwStatement(path, print) {
return ["throw ", call(path, print, "expression"), ";"];
},
synchronizedStatement(path, print) {
return [
"synchronized ",
indentInParentheses(call(path, print, "expression")),
" ",
call(path, print, "block")
];
},
tryStatement(path, print) {
const { children } = path.node;
if (children.tryWithResourcesStatement) {
return call(path, print, "tryWithResourcesStatement");
}
const blocks = ["try", call(path, print, "block")];
if (children.catches) {
blocks.push(call(path, print, "catches"));
}
if (children.finally) {
blocks.push(call(path, print, "finally"));
}
return join(" ", blocks);
},
catches(path, print) {
return join(" ", map(path, print, "catchClause"));
},
catchClause(path, print) {
return [
"catch ",
indentInParentheses(call(path, print, "catchFormalParameter")),
" ",
call(path, print, "block")
];
},
catchFormalParameter(path, print) {
return join(" ", [
...map(path, print, "variableModifier"),
call(path, print, "catchType"),
call(path, print, "variableDeclaratorId")
]);
},
catchType(path, print) {
return join([line, "| "], [call(path, print, "unannClassType"), ...map(path, print, "classType")]);
},
finally(path, print) {
return ["finally ", call(path, print, "block")];
},
tryWithResourcesStatement(path, print) {
const { children } = path.node;
const blocks = [
"try",
call(path, print, "resourceSpecification"),
call(path, print, "block")
];
if (children.catches) {
blocks.push(call(path, print, "catches"));
}
if (children.finally) {
blocks.push(call(path, print, "finally"));
}
return join(" ", blocks);
},
resourceSpecification(path, print) {
const resources = [call(path, print, "resourceList")];
if (path.node.children.Semicolon) {
resources.push(ifBreak(";"));
}
return indentInParentheses(resources);
},
resourceList(path, print) {
return join([";", line], map(path, print, "resource"));
},
resource: printSingle,
yieldStatement(path, print) {
return ["yield ", call(path, print, "expression"), ";"];
},
variableAccess: printSingle
};

View File

@@ -0,0 +1,157 @@
import type { ClassBodyCstNode, EnumBodyDeclarationsCstNode } from "java-parser";
import type { AstPath } from "prettier";
import { builders } from "prettier/doc";
import { printClassPermits, printClassType, printSingle, type JavaPrintFn } from "./helpers.js";
declare const _default: {
classDeclaration(path: AstPath<import("java-parser").ClassDeclarationCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
normalClassDeclaration(path: AstPath<import("java-parser").NormalClassDeclarationCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
classModifier: typeof printSingle;
typeParameters(path: AstPath<import("java-parser").TypeParametersCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Group;
typeParameterList(path: AstPath<import("java-parser").TypeParameterListCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
classExtends(path: AstPath<import("java-parser").ClassExtendsCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
classImplements(path: AstPath<import("java-parser").ClassImplementsCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Group;
classPermits: typeof printClassPermits;
interfaceTypeList(path: AstPath<import("java-parser").InterfaceTypeListCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Group;
classBody(path: AstPath<ClassBodyCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[];
classBodyDeclaration: typeof printSingle;
classMemberDeclaration(path: AstPath<import("java-parser").ClassMemberDeclarationCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc;
fieldDeclaration(path: AstPath<import("java-parser").FieldDeclarationCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
fieldModifier: typeof printSingle;
variableDeclaratorList(path: AstPath<import("java-parser").VariableDeclaratorListCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Group | builders.Doc[];
variableDeclarator(path: AstPath<import("java-parser").VariableDeclaratorCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc;
variableDeclaratorId(path: AstPath<import("java-parser").VariableDeclaratorIdCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc;
variableInitializer: typeof printSingle;
unannType: typeof printSingle;
unannPrimitiveTypeWithOptionalDimsSuffix(path: AstPath<import("java-parser").UnannPrimitiveTypeWithOptionalDimsSuffixCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc;
unannPrimitiveType: typeof printSingle;
unannReferenceType(path: AstPath<import("java-parser").UnannReferenceTypeCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc;
unannClassOrInterfaceType: typeof printSingle;
unannClassType: typeof printClassType;
unannInterfaceType: typeof printSingle;
unannTypeVariable: typeof printSingle;
methodDeclaration(path: AstPath<import("java-parser").MethodDeclarationCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
methodModifier: typeof printSingle;
methodHeader(path: AstPath<import("java-parser").MethodHeaderCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Group;
result: typeof printSingle;
methodDeclarator(path: AstPath<import("java-parser").MethodDeclaratorCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
receiverParameter(path: AstPath<import("java-parser").ReceiverParameterCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
formalParameterList(path: AstPath<import("java-parser").FormalParameterListCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
formalParameter: typeof printSingle;
variableParaRegularParameter(path: AstPath<import("java-parser").VariableParaRegularParameterCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
variableArityParameter(path: AstPath<import("java-parser").VariableArityParameterCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
variableModifier: typeof printSingle;
throws(path: AstPath<import("java-parser").ThrowsCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
exceptionTypeList(path: AstPath<import("java-parser").ExceptionTypeListCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
exceptionType: typeof printSingle;
methodBody: typeof printSingle;
instanceInitializer: typeof printSingle;
staticInitializer(path: AstPath<import("java-parser").StaticInitializerCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
constructorDeclaration(path: AstPath<import("java-parser").ConstructorDeclarationCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
constructorModifier: typeof printSingle;
constructorDeclarator(path: AstPath<import("java-parser").ConstructorDeclaratorCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
simpleTypeName: typeof printSingle;
constructorBody(path: AstPath<import("java-parser").ConstructorBodyCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[];
explicitConstructorInvocation: typeof printSingle;
unqualifiedExplicitConstructorInvocation(path: AstPath<import("java-parser").UnqualifiedExplicitConstructorInvocationCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
qualifiedExplicitConstructorInvocation(path: AstPath<import("java-parser").QualifiedExplicitConstructorInvocationCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
enumDeclaration(path: AstPath<import("java-parser").EnumDeclarationCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
enumBody(path: AstPath<import("java-parser").EnumBodyCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn, options: import("./helpers.js").JavaParserOptions): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[];
enumConstantList(path: AstPath<import("java-parser").EnumConstantListCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
enumConstant(path: AstPath<import("java-parser").EnumConstantCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
enumConstantModifier: typeof printSingle;
enumBodyDeclarations(path: AstPath<EnumBodyDeclarationsCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
recordDeclaration(path: AstPath<import("java-parser").RecordDeclarationCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
recordHeader(path: AstPath<import("java-parser").RecordHeaderCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Group | "()";
recordComponentList(path: AstPath<import("java-parser").RecordComponentListCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
recordComponent(path: AstPath<import("java-parser").RecordComponentCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Group;
variableArityRecordComponent(path: AstPath<import("java-parser").VariableArityRecordComponentCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
recordComponentModifier: typeof printSingle;
recordBody(path: AstPath<import("java-parser").RecordBodyCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[];
recordBodyDeclaration: typeof printSingle;
compactConstructorDeclaration(path: AstPath<import("java-parser").CompactConstructorDeclarationCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
};
export default _default;

View File

@@ -0,0 +1,446 @@
import { builders } from "prettier/doc";
import { call, each, hasDeclarationAnnotations, hasLeadingComments, indentInParentheses, isBinaryExpression, lineEndWithComments, lineStartWithComments, map, onlyDefinedKey, printBlock, printClassPermits, printClassType, printDanglingComments, printList, printSingle, printWithModifiers } from "./helpers.js";
const { group, hardline, indent, indentIfBreak, join, line, softline } = builders;
export default {
classDeclaration(path, print) {
const declarationKey = onlyDefinedKey(path.node.children, [
"enumDeclaration",
"normalClassDeclaration",
"recordDeclaration"
]);
const declaration = call(path, print, declarationKey);
return printWithModifiers(path, print, "classModifier", declaration, true);
},
normalClassDeclaration(path, print) {
const { classExtends, classImplements, classPermits, typeParameters } = path.node.children;
const header = ["class ", call(path, print, "typeIdentifier")];
if (typeParameters) {
header.push(call(path, print, "typeParameters"));
}
if (classExtends) {
header.push(indent([line, call(path, print, "classExtends")]));
}
if (classImplements) {
header.push(indent([line, call(path, print, "classImplements")]));
}
if (classPermits) {
header.push(indent([line, call(path, print, "classPermits")]));
}
return [group(header), " ", call(path, print, "classBody")];
},
classModifier: printSingle,
typeParameters(path, print) {
return group([
"<",
indent([softline, call(path, print, "typeParameterList")]),
softline,
">"
]);
},
typeParameterList(path, print) {
return printList(path, print, "typeParameter");
},
classExtends(path, print) {
return ["extends ", call(path, print, "classType")];
},
classImplements(path, print) {
return group([
"implements",
indent([line, call(path, print, "interfaceTypeList")])
]);
},
classPermits: printClassPermits,
interfaceTypeList(path, print) {
return group(printList(path, print, "interfaceType"));
},
classBody(path, print) {
return printBlock(path, printClassBodyDeclarations(path, print));
},
classBodyDeclaration: printSingle,
classMemberDeclaration(path, print) {
const { children } = path.node;
return children.Semicolon
? ""
: call(path, print, onlyDefinedKey(children));
},
fieldDeclaration(path, print) {
const declaration = [
call(path, print, "unannType"),
" ",
call(path, print, "variableDeclaratorList"),
";"
];
return printWithModifiers(path, print, "fieldModifier", declaration);
},
fieldModifier: printSingle,
variableDeclaratorList(path, print) {
var _a;
const declarators = map(path, print, "variableDeclarator");
return declarators.length > 1 &&
path.node.children.variableDeclarator.some(({ children }) => children.Equals)
? group(indent(join([",", line], declarators)), {
shouldBreak: ((_a = path.getNode(4)) === null || _a === void 0 ? void 0 : _a.name) !== "forInit"
})
: join(", ", declarators);
},
variableDeclarator(path, print) {
var _a, _b;
const { children } = path.node;
const variableInitializer = (_a = children.variableInitializer) === null || _a === void 0 ? void 0 : _a[0];
const declaratorId = call(path, print, "variableDeclaratorId");
if (!variableInitializer) {
return declaratorId;
}
const expression = (_b = variableInitializer.children.expression) === null || _b === void 0 ? void 0 : _b[0];
const declarator = [declaratorId, " ", call(path, print, "Equals")];
const initializer = call(path, print, "variableInitializer");
if (hasLeadingComments(variableInitializer) ||
(expression && isBinaryExpression(expression))) {
declarator.push(group(indent([line, initializer])));
}
else {
const groupId = Symbol("assignment");
declarator.push(group(indent(line), { id: groupId }), indentIfBreak(initializer, { groupId }));
}
return group(declarator);
},
variableDeclaratorId(path, print) {
const { dims, Underscore } = path.node.children;
if (Underscore) {
return "_";
}
const identifier = call(path, print, "Identifier");
return dims ? [identifier, call(path, print, "dims")] : identifier;
},
variableInitializer: printSingle,
unannType: printSingle,
unannPrimitiveTypeWithOptionalDimsSuffix(path, print) {
const type = call(path, print, "unannPrimitiveType");
return path.node.children.dims ? [type, call(path, print, "dims")] : type;
},
unannPrimitiveType: printSingle,
unannReferenceType(path, print) {
const type = call(path, print, "unannClassOrInterfaceType");
return path.node.children.dims ? [type, call(path, print, "dims")] : type;
},
unannClassOrInterfaceType: printSingle,
unannClassType: printClassType,
unannInterfaceType: printSingle,
unannTypeVariable: printSingle,
methodDeclaration(path, print) {
const declaration = [
call(path, print, "methodHeader"),
path.node.children.methodBody[0].children.Semicolon ? "" : " ",
call(path, print, "methodBody")
];
return printWithModifiers(path, print, "methodModifier", declaration);
},
methodModifier: printSingle,
methodHeader(path, print) {
const { typeParameters, annotation, throws } = path.node.children;
const header = [];
if (typeParameters) {
header.push(call(path, print, "typeParameters"));
}
if (annotation) {
header.push(join(line, map(path, print, "annotation")));
}
header.push(call(path, print, "result"), call(path, print, "methodDeclarator"));
return throws
? group([
...join(" ", header),
group(indent([line, call(path, print, "throws")]))
])
: group(join(" ", header));
},
result: printSingle,
methodDeclarator(path, print) {
const { dims, formalParameterList, receiverParameter } = path.node.children;
const declarator = [call(path, print, "Identifier")];
const parameters = [];
if (receiverParameter) {
parameters.push(call(path, print, "receiverParameter"));
}
if (formalParameterList) {
parameters.push(call(path, print, "formalParameterList"));
}
const items = parameters.length
? join([",", line], parameters)
: printDanglingComments(path);
declarator.push(items.length ? indentInParentheses(items) : "()");
if (dims) {
declarator.push(call(path, print, "dims"));
}
return declarator;
},
receiverParameter(path, print) {
return join(" ", [
...map(path, print, "annotation"),
call(path, print, "unannType"),
path.node.children.Identifier
? [call(path, print, "Identifier"), ".this"]
: "this"
]);
},
formalParameterList(path, print) {
return printList(path, print, "formalParameter");
},
formalParameter: printSingle,
variableParaRegularParameter(path, print) {
return join(" ", [
...map(path, print, "variableModifier"),
call(path, print, "unannType"),
call(path, print, "variableDeclaratorId")
]);
},
variableArityParameter(path, print) {
const type = join(" ", [
...map(path, print, "variableModifier"),
call(path, print, "unannType"),
...map(path, print, "annotation")
]);
return [type, "... ", call(path, print, "Identifier")];
},
variableModifier: printSingle,
throws(path, print) {
return ["throws ", call(path, print, "exceptionTypeList")];
},
exceptionTypeList(path, print) {
return join(", ", map(path, print, "exceptionType"));
},
exceptionType: printSingle,
methodBody: printSingle,
instanceInitializer: printSingle,
staticInitializer(path, print) {
return ["static ", call(path, print, "block")];
},
constructorDeclaration(path, print) {
const declaration = [call(path, print, "constructorDeclarator")];
if (path.node.children.throws) {
declaration.push(group(indent([line, call(path, print, "throws")])));
}
declaration.push(" ", call(path, print, "constructorBody"));
return printWithModifiers(path, print, "constructorModifier", declaration, true);
},
constructorModifier: printSingle,
constructorDeclarator(path, print) {
const { children } = path.node;
const parameters = [];
if (children.receiverParameter) {
parameters.push(call(path, print, "receiverParameter"));
}
if (children.formalParameterList) {
parameters.push(call(path, print, "formalParameterList"));
}
const header = [call(path, print, "simpleTypeName")];
header.push(parameters.length
? indentInParentheses(join([",", line], parameters))
: "()");
return children.typeParameters
? [call(path, print, "typeParameters"), " ", ...header]
: header;
},
simpleTypeName: printSingle,
constructorBody(path, print) {
const { children } = path.node;
const statements = [];
if (children.explicitConstructorInvocation) {
statements.push(call(path, print, "explicitConstructorInvocation"));
}
if (children.blockStatements) {
statements.push(call(path, print, "blockStatements"));
}
return printBlock(path, statements);
},
explicitConstructorInvocation: printSingle,
unqualifiedExplicitConstructorInvocation(path, print) {
const { children } = path.node;
const invocation = [];
if (children.typeArguments) {
invocation.push(call(path, print, "typeArguments"));
}
invocation.push(children.Super ? "super" : "this");
if (children.argumentList) {
invocation.push(group(["(", call(path, print, "argumentList"), ")"]));
}
else {
invocation.push(indentInParentheses(printDanglingComments(path), { shouldBreak: true }));
}
invocation.push(";");
return invocation;
},
qualifiedExplicitConstructorInvocation(path, print) {
const { children } = path.node;
const invocation = [call(path, print, "expressionName"), "."];
if (children.typeArguments) {
invocation.push(call(path, print, "typeArguments"));
}
invocation.push("super");
if (children.argumentList) {
invocation.push(group(["(", call(path, print, "argumentList"), ")"]));
}
else {
invocation.push(indentInParentheses(printDanglingComments(path), { shouldBreak: true }));
}
invocation.push(";");
return invocation;
},
enumDeclaration(path, print) {
const header = ["enum", call(path, print, "typeIdentifier")];
if (path.node.children.classImplements) {
header.push(call(path, print, "classImplements"));
}
return join(" ", [...header, call(path, print, "enumBody")]);
},
enumBody(path, print, options) {
var _a;
const { children } = path.node;
const contents = [];
const hasNonEmptyDeclaration = ((_a = children.enumBodyDeclarations) !== null && _a !== void 0 ? _a : [])
.flatMap(({ children }) => { var _a; return (_a = children.classBodyDeclaration) !== null && _a !== void 0 ? _a : []; })
.some(({ children }) => { var _a; return !((_a = children.classMemberDeclaration) === null || _a === void 0 ? void 0 : _a[0].children.Semicolon); });
if (children.enumConstantList) {
contents.push(call(path, print, "enumConstantList"));
if (!hasNonEmptyDeclaration && options.trailingComma !== "none") {
contents.push(",");
}
}
if (hasNonEmptyDeclaration) {
contents.push(";", hardline, call(path, print, "enumBodyDeclarations"));
}
return printBlock(path, contents.length ? [contents] : []);
},
enumConstantList(path, print) {
return join([",", hardline], map(path, constantPath => {
const constant = print(constantPath);
const { node, previous } = constantPath;
return !previous ||
lineStartWithComments(node) <= lineEndWithComments(previous) + 1
? constant
: [hardline, constant];
}, "enumConstant"));
},
enumConstant(path, print) {
const { argumentList, classBody } = path.node.children;
const initializer = [call(path, print, "Identifier")];
if (argumentList) {
initializer.push(group(["(", call(path, print, "argumentList"), ")"]));
}
if (classBody) {
initializer.push(" ", call(path, print, "classBody"));
}
return printWithModifiers(path, print, "enumConstantModifier", initializer);
},
enumConstantModifier: printSingle,
enumBodyDeclarations(path, print) {
return join(hardline, printClassBodyDeclarations(path, print));
},
recordDeclaration(path, print) {
const { children } = path.node;
const header = ["record ", call(path, print, "typeIdentifier")];
if (children.typeParameters) {
header.push(call(path, print, "typeParameters"));
}
header.push(call(path, print, "recordHeader"));
if (children.classImplements) {
header.push(" ", call(path, print, "classImplements"));
}
return [group(header), " ", call(path, print, "recordBody")];
},
recordHeader(path, print) {
return path.node.children.recordComponentList
? indentInParentheses(call(path, print, "recordComponentList"))
: indentInParentheses(printDanglingComments(path), { shouldBreak: true });
},
recordComponentList(path, print) {
return join([",", line], map(path, componentPath => {
const { node, previous } = componentPath;
const blankLine = previous &&
lineStartWithComments(node) > lineEndWithComments(previous) + 1;
const component = print(componentPath);
return blankLine ? [softline, component] : component;
}, "recordComponent"));
},
recordComponent(path, print) {
const { children } = path.node;
const component = [call(path, print, "unannType")];
if (children.Identifier ||
children.variableArityRecordComponent[0].children.annotation) {
component.push(" ");
}
const suffixKey = onlyDefinedKey(children, [
"Identifier",
"variableArityRecordComponent"
]);
component.push(call(path, print, suffixKey));
return group(join(line, [...map(path, print, "recordComponentModifier"), component]));
},
variableArityRecordComponent(path, print) {
return [
...join(" ", map(path, print, "annotation")),
"... ",
call(path, print, "Identifier")
];
},
recordComponentModifier: printSingle,
recordBody(path, print) {
const declarations = [];
let previousRequiresPadding = false;
each(path, declarationPath => {
var _a, _b, _c, _d;
const declaration = print(declarationPath);
if (declaration === "") {
return;
}
const { node, previous } = declarationPath;
const fieldDeclaration = (_c = (_b = (_a = node.children.classBodyDeclaration) === null || _a === void 0 ? void 0 : _a[0].children.classMemberDeclaration) === null || _b === void 0 ? void 0 : _b[0].children.fieldDeclaration) === null || _c === void 0 ? void 0 : _c[0].children;
const currentRequiresPadding = !fieldDeclaration ||
hasDeclarationAnnotations((_d = fieldDeclaration.fieldModifier) !== null && _d !== void 0 ? _d : []);
const blankLine = declarations.length > 0 &&
(previousRequiresPadding ||
currentRequiresPadding ||
lineStartWithComments(node) > lineEndWithComments(previous) + 1);
declarations.push(blankLine ? [hardline, declaration] : declaration);
previousRequiresPadding = currentRequiresPadding;
}, "recordBodyDeclaration");
return printBlock(path, declarations);
},
recordBodyDeclaration: printSingle,
compactConstructorDeclaration(path, print) {
const declaration = [
call(path, print, "simpleTypeName"),
" ",
call(path, print, "constructorBody")
];
return printWithModifiers(path, print, "constructorModifier", declaration, true);
}
};
function printClassBodyDeclarations(path, print) {
var _a;
if (!path.node.children.classBodyDeclaration) {
return [];
}
const declarations = [];
let previousRequiresPadding = path.node.name === "enumBodyDeclarations" ||
((_a = path.grandparent) === null || _a === void 0 ? void 0 : _a.name) ===
"normalClassDeclaration";
each(path, declarationPath => {
var _a, _b, _c;
const declaration = print(declarationPath);
if (declaration === "") {
return;
}
const { node, previous } = declarationPath;
const fieldDeclaration = (_b = (_a = node.children.classMemberDeclaration) === null || _a === void 0 ? void 0 : _a[0].children.fieldDeclaration) === null || _b === void 0 ? void 0 : _b[0].children;
const currentRequiresPadding = fieldDeclaration
? hasDeclarationAnnotations((_c = fieldDeclaration.fieldModifier) !== null && _c !== void 0 ? _c : [])
: true;
const blankLine = previousRequiresPadding ||
(declarations.length > 0 &&
(currentRequiresPadding ||
lineStartWithComments(node) > lineEndWithComments(previous) + 1));
declarations.push(blankLine ? [hardline, declaration] : declaration);
previousRequiresPadding = currentRequiresPadding;
}, "classBodyDeclaration");
return declarations;
}

View File

@@ -0,0 +1,134 @@
import type { StringTemplateCstNode, TextBlockTemplateCstNode } from "java-parser";
import type { AstPath } from "prettier";
import { builders } from "prettier/doc";
import type { JavaComment } from "../comments.js";
import { printSingle, type JavaPrintFn } from "./helpers.js";
declare const _default: {
expression: typeof printSingle;
lambdaExpression(path: AstPath<import("java-parser").LambdaExpressionCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn, _: import("./helpers.js").JavaParserOptions, args?: unknown): builders.Doc[];
lambdaParameters(path: AstPath<import("java-parser").LambdaParametersCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn, options: import("./helpers.js").JavaParserOptions): builders.Doc;
lambdaParametersWithBraces(path: AstPath<import("java-parser").LambdaParametersWithBracesCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn, options: import("./helpers.js").JavaParserOptions): builders.Doc;
lambdaParameterList: typeof printSingle;
conciseLambdaParameterList(path: AstPath<import("java-parser").ConciseLambdaParameterListCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
normalLambdaParameterList(path: AstPath<import("java-parser").NormalLambdaParameterListCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
normalLambdaParameter: typeof printSingle;
regularLambdaParameter(path: AstPath<import("java-parser").RegularLambdaParameterCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
lambdaParameterType: typeof printSingle;
conciseLambdaParameter: typeof printSingle;
lambdaBody: typeof printSingle;
conditionalExpression(path: AstPath<import("java-parser").ConditionalExpressionCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Doc;
binaryExpression(path: AstPath<import("java-parser").BinaryExpressionCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn, options: import("./helpers.js").JavaParserOptions): builders.Doc;
unaryExpression(path: AstPath<import("java-parser").UnaryExpressionCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
unaryExpressionNotPlusMinus(path: AstPath<import("java-parser").UnaryExpressionNotPlusMinusCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
primary(path: AstPath<import("java-parser").PrimaryCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Doc;
primaryPrefix: typeof printSingle;
primarySuffix(path: AstPath<import("java-parser").PrimarySuffixCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Doc;
fqnOrRefType(path: AstPath<import("java-parser").FqnOrRefTypeCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn, _: import("./helpers.js").JavaParserOptions, args: unknown): builders.Doc[];
fqnOrRefTypePartFirst(path: AstPath<import("java-parser").FqnOrRefTypePartFirstCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
fqnOrRefTypePartRest(path: AstPath<import("java-parser").FqnOrRefTypePartRestCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
fqnOrRefTypePartCommon(path: AstPath<import("java-parser").FqnOrRefTypePartCommonCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Doc;
parenthesisExpression(path: AstPath<import("java-parser").ParenthesisExpressionCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Group | "()" | (string | builders.Indent)[];
castExpression: typeof printSingle;
primitiveCastExpression(path: AstPath<import("java-parser").PrimitiveCastExpressionCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
referenceTypeCastExpression(path: AstPath<import("java-parser").ReferenceTypeCastExpressionCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
newExpression: typeof printSingle;
unqualifiedClassInstanceCreationExpression(path: AstPath<import("java-parser").UnqualifiedClassInstanceCreationExpressionCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
classOrInterfaceTypeToInstantiate(path: AstPath<import("java-parser").ClassOrInterfaceTypeToInstantiateCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
typeArgumentsOrDiamond: typeof printSingle;
diamond(): string;
methodInvocationSuffix(path: AstPath<import("java-parser").MethodInvocationSuffixCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Group | "()";
argumentList(path: AstPath<import("java-parser").ArgumentListCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Group | (builders.Indent | builders.Softline)[] | (builders.BreakParent | builders.Group)[];
arrayCreationExpression(path: AstPath<import("java-parser").ArrayCreationExpressionCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
arrayCreationExpressionWithoutInitializerSuffix(path: AstPath<import("java-parser").ArrayCreationExpressionWithoutInitializerSuffixCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Doc;
arrayCreationWithInitializerSuffix(path: AstPath<import("java-parser").ArrayCreationWithInitializerSuffixCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
dimExprs(path: AstPath<import("java-parser").DimExprsCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
dimExpr(path: AstPath<import("java-parser").DimExprCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
classLiteralSuffix(path: AstPath<import("java-parser").ClassLiteralSuffixCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
arrayAccessSuffix(path: AstPath<import("java-parser").ArrayAccessSuffixCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
methodReferenceSuffix(path: AstPath<import("java-parser").MethodReferenceSuffixCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
templateArgument: typeof printSingle;
template: typeof printSingle;
stringTemplate(path: AstPath<StringTemplateCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Indent;
textBlockTemplate(path: AstPath<TextBlockTemplateCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Indent;
embeddedExpression: typeof printSingle;
pattern: typeof printSingle;
typePattern: typeof printSingle;
recordPattern(path: AstPath<import("java-parser").RecordPatternCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
componentPatternList(path: AstPath<import("java-parser").ComponentPatternListCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
componentPattern: typeof printSingle;
matchAllPattern: typeof printSingle;
guard(path: AstPath<import("java-parser").GuardCstNode & {
comments?: JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
};
export default _default;

View File

@@ -0,0 +1,598 @@
import { builders, utils } from "prettier/doc";
import { call, definedKeys, each, findBaseIndent, flatMap, hasLeadingComments, indentInParentheses, isBinaryExpression, isNonTerminal, isTerminal, map, onlyDefinedKey, printDanglingComments, printList, printName, printSingle } from "./helpers.js";
const { breakParent, conditionalGroup, group, hardline, ifBreak, indent, indentIfBreak, join, line, lineSuffixBoundary, softline } = builders;
const { removeLines, willBreak } = utils;
export default {
expression: printSingle,
lambdaExpression(path, print, _, args = {}) {
var _a;
const hug = (_a = args.hug) !== null && _a !== void 0 ? _a : false;
const parameters = call(path, print, "lambdaParameters");
const expression = [hug ? removeLines(parameters) : parameters, " ->"];
const lambdaExpression = path.node.children.lambdaBody[0].children.expression;
const body = call(path, print, "lambdaBody");
if (lambdaExpression) {
const suffix = indent([line, body]);
expression.push(group(hug ? [suffix, softline] : suffix));
}
else {
expression.push(" ", body);
}
return expression;
},
lambdaParameters(path, print, options) {
const parameters = printSingle(path, print);
return !path.node.children.lambdaParametersWithBraces &&
options.arrowParens === "always"
? ["(", parameters, ")"]
: parameters;
},
lambdaParametersWithBraces(path, print, options) {
var _a;
const { lambdaParameterList } = path.node.children;
if (!lambdaParameterList) {
return "()";
}
const { conciseLambdaParameterList, normalLambdaParameterList } = lambdaParameterList[0].children;
const parameterCount = ((_a = conciseLambdaParameterList === null || conciseLambdaParameterList === void 0 ? void 0 : conciseLambdaParameterList[0].children.conciseLambdaParameter) !== null && _a !== void 0 ? _a : normalLambdaParameterList === null || normalLambdaParameterList === void 0 ? void 0 : normalLambdaParameterList[0].children.normalLambdaParameter).length;
const parameters = call(path, print, "lambdaParameterList");
if (parameterCount > 1) {
return indentInParentheses(parameters);
}
return conciseLambdaParameterList && options.arrowParens === "avoid"
? parameters
: ["(", parameters, ")"];
},
lambdaParameterList: printSingle,
conciseLambdaParameterList(path, print) {
return printList(path, print, "conciseLambdaParameter");
},
normalLambdaParameterList(path, print) {
return printList(path, print, "normalLambdaParameter");
},
normalLambdaParameter: printSingle,
regularLambdaParameter(path, print) {
return join(" ", [
...map(path, print, "variableModifier"),
call(path, print, "lambdaParameterType"),
call(path, print, "variableDeclaratorId")
]);
},
lambdaParameterType: printSingle,
conciseLambdaParameter: printSingle,
lambdaBody: printSingle,
conditionalExpression(path, print) {
var _a;
const binaryExpression = call(path, print, "binaryExpression");
if (!path.node.children.QuestionMark) {
return binaryExpression;
}
const expressions = map(path, print, "expression");
const contents = indent(join(line, [
binaryExpression,
["? ", expressions[0]],
[": ", expressions[1]]
]));
const isNestedTernary = ((_a = path.getNode(4)) === null || _a === void 0 ? void 0 : _a.name) ===
"conditionalExpression";
return isNestedTernary ? contents : group(contents);
},
binaryExpression(path, print, options) {
var _a, _b;
const { children } = path.node;
const operands = flatMap(path, print, definedKeys(children, [
"expression",
"pattern",
"referenceType",
"unaryExpression"
]));
const operators = flatMap(path, operatorPath => {
const { node } = operatorPath;
let image;
if (isTerminal(node)) {
image = node.image;
}
else if (node.children.Less) {
image = "<<";
}
else {
image = node.children.Greater.length === 2 ? ">>" : ">>>";
}
return { image, doc: print(operatorPath) };
}, definedKeys(children, [
"AssignmentOperator",
"BinaryOperator",
"Instanceof",
"shiftOperator"
]));
const hasNonAssignmentOperators = (operators.length > 0 && !children.AssignmentOperator) ||
(children.expression !== undefined &&
isBinaryExpression(children.expression[0]));
const isInList = ((_a = path.getNode(4)) === null || _a === void 0 ? void 0 : _a.name) === "elementValue" ||
((_b = path.getNode(6)) === null || _b === void 0 ? void 0 : _b.name) === "argumentList";
return binary(operands, operators, {
hasNonAssignmentOperators,
isInList,
isRoot: true,
operatorPosition: options.experimentalOperatorPosition
});
},
unaryExpression(path, print) {
return [
...map(path, print, "UnaryPrefixOperator"),
call(path, print, "primary"),
...map(path, print, "UnarySuffixOperator")
];
},
unaryExpressionNotPlusMinus(path, print) {
const { children } = path.node;
const expression = [];
if (children.UnaryPrefixOperatorNotPlusMinus) {
expression.push(...map(path, print, "UnaryPrefixOperatorNotPlusMinus"));
}
expression.push(call(path, print, "primary"));
if (children.UnarySuffixOperator) {
expression.push(...map(path, print, "UnarySuffixOperator"));
}
return join(" ", expression);
},
primary(path, print) {
var _a, _b;
const { children } = path.node;
if (!children.primarySuffix) {
return call(path, print, "primaryPrefix");
}
const methodInvocations = children.primarySuffix
.filter(({ children }) => children.methodInvocationSuffix)
.map(({ children }) => children.methodInvocationSuffix[0].children);
const hasLambdaMethodParameter = methodInvocations.some(({ argumentList }) => argumentList === null || argumentList === void 0 ? void 0 : argumentList[0].children.expression.some(({ children }) => children.lambdaExpression));
const prefixIsCallExpression = children.primaryPrefix[0].children.newExpression;
const callExpressionCount = methodInvocations.length +
(prefixIsCallExpression ? 1 : 0) +
children.primarySuffix.filter(({ children }) => children.unqualifiedClassInstanceCreationExpression).length;
const fqnOrRefType = (_a = children.primaryPrefix[0].children.fqnOrRefType) === null || _a === void 0 ? void 0 : _a[0].children;
const prefixIsMethodInvocation = (fqnOrRefType === null || fqnOrRefType === void 0 ? void 0 : fqnOrRefType.fqnOrRefTypePartRest) !== undefined &&
((_b = children.primarySuffix) === null || _b === void 0 ? void 0 : _b[0].children.methodInvocationSuffix) !== undefined;
const prefixIsStaticMethodInvocation = prefixIsMethodInvocation && isCapitalizedIdentifier(fqnOrRefType);
const prefixIsInstanceMethodInvocation = prefixIsMethodInvocation && !prefixIsStaticMethodInvocation;
const mustBreakForCallExpressions = methodInvocations.length > 2 && hasLambdaMethodParameter;
const separator = mustBreakForCallExpressions ? hardline : softline;
const prefix = [
call(path, prefixPath => print(prefixPath, {
lastSeparator: prefixIsStaticMethodInvocation ||
(prefixIsInstanceMethodInvocation && callExpressionCount === 1)
? ""
: separator
}), "primaryPrefix")
];
const canBreakForCallExpressions = callExpressionCount > 2 ||
(callExpressionCount === 2 && prefixIsInstanceMethodInvocation) ||
willBreak(prefix);
const suffixes = [];
each(path, suffixPath => {
const { node, previous } = suffixPath;
const suffix = print(suffixPath);
if (node.children.Dot) {
if ((canBreakForCallExpressions &&
((!previous && prefixIsCallExpression) ||
(previous === null || previous === void 0 ? void 0 : previous.children.methodInvocationSuffix) ||
(previous === null || previous === void 0 ? void 0 : previous.children.unqualifiedClassInstanceCreationExpression))) ||
(!node.children.templateArgument && willBreak(suffix))) {
suffixes.push(separator);
}
suffixes.push(suffix);
}
else if (previous) {
suffixes.push(suffix);
}
else {
prefix.push(prefixIsInstanceMethodInvocation && callExpressionCount >= 2
? indent(suffix)
: suffix);
}
}, "primarySuffix");
const hasSuffixComments = children.primarySuffix.some(suffix => hasLeadingComments(suffix));
return group(canBreakForCallExpressions || hasSuffixComments
? [prefix, indent(suffixes)]
: [prefix, ...suffixes]);
},
primaryPrefix: printSingle,
primarySuffix(path, print) {
const { children } = path.node;
if (!children.Dot) {
return printSingle(path, print);
}
const suffix = ["."];
if (children.This) {
suffix.push("this");
}
else if (children.Identifier) {
if (children.typeArguments) {
suffix.push(call(path, print, "typeArguments"));
}
suffix.push(call(path, print, "Identifier"));
}
else {
const suffixKey = onlyDefinedKey(children, [
"templateArgument",
"unqualifiedClassInstanceCreationExpression"
]);
suffix.push(call(path, print, suffixKey));
}
return suffix;
},
fqnOrRefType(path, print, _, args) {
var _a;
const lastSeparator = (_a = args.lastSeparator) !== null && _a !== void 0 ? _a : "";
const fqnOrRefType = [
call(path, print, "fqnOrRefTypePartFirst"),
...map(path, partPath => {
const part = print(partPath);
return partPath.isLast
? [willBreak(part) ? hardline : lastSeparator, part]
: part;
}, "fqnOrRefTypePartRest")
];
fqnOrRefType.push(indent(fqnOrRefType.pop()));
return path.node.children.dims
? [fqnOrRefType, call(path, print, "dims")]
: fqnOrRefType;
},
fqnOrRefTypePartFirst(path, print) {
return join(" ", [
...map(path, print, "annotation"),
call(path, print, "fqnOrRefTypePartCommon")
]);
},
fqnOrRefTypePartRest(path, print) {
const common = call(path, print, "fqnOrRefTypePartCommon");
const type = path.node.children.typeArguments
? [call(path, print, "typeArguments"), common]
: common;
return [".", ...join(" ", [...map(path, print, "annotation"), type])];
},
fqnOrRefTypePartCommon(path, print) {
const { children } = path.node;
const keywordKey = onlyDefinedKey(children, ["Identifier", "Super"]);
const keyword = call(path, print, keywordKey);
return children.typeArguments
? [keyword, call(path, print, "typeArguments")]
: keyword;
},
parenthesisExpression(path, print) {
var _a;
const expression = call(path, print, "expression");
const ancestorName = (_a = path.getNode(14)) === null || _a === void 0 ? void 0 : _a.name;
const binaryExpression = path.getNode(8);
return ancestorName &&
["guard", "returnStatement"].includes(ancestorName) &&
binaryExpression &&
binaryExpression.name === "binaryExpression" &&
Object.keys(binaryExpression.children).length === 1
? indentInParentheses(expression)
: ["(", indent(expression), ")"];
},
castExpression: printSingle,
primitiveCastExpression(path, print) {
return [
"(",
call(path, print, "primitiveType"),
") ",
call(path, print, "unaryExpression")
];
},
referenceTypeCastExpression(path, print) {
const { children } = path.node;
const type = call(path, print, "referenceType");
const cast = children.additionalBound
? indentInParentheses(join(line, [type, ...map(path, print, "additionalBound")]))
: ["(", type, ")"];
const expressionKey = onlyDefinedKey(children, [
"lambdaExpression",
"unaryExpressionNotPlusMinus"
]);
return [cast, " ", call(path, print, expressionKey)];
},
newExpression: printSingle,
unqualifiedClassInstanceCreationExpression(path, print) {
const { children } = path.node;
const expression = ["new "];
if (children.typeArguments) {
expression.push(call(path, print, "typeArguments"));
}
expression.push(call(path, print, "classOrInterfaceTypeToInstantiate"), children.argumentList
? group(["(", call(path, print, "argumentList"), ")"])
: "()");
if (children.classBody) {
expression.push(" ", call(path, print, "classBody"));
}
return expression;
},
classOrInterfaceTypeToInstantiate(path, print) {
const { children } = path.node;
const type = children.annotation
? flatMap(path, childPath => [
print(childPath),
isNonTerminal(childPath.node) ? " " : "."
], ["annotation", "Identifier"])
: printName(path, print);
if (children.typeArgumentsOrDiamond) {
type.push(call(path, print, "typeArgumentsOrDiamond"));
}
return type;
},
typeArgumentsOrDiamond: printSingle,
diamond() {
return "<>";
},
methodInvocationSuffix(path, print) {
return path.node.children.argumentList
? group(["(", call(path, print, "argumentList"), ")"])
: indentInParentheses(printDanglingComments(path), { shouldBreak: true });
},
argumentList(path, print) {
var _a, _b, _c, _d;
const expressions = path.node.children.expression;
const lastExpression = expressions.at(-1);
const lastExpressionLambdaBodyExpression = (_b = (_a = lastExpression.children.lambdaExpression) === null || _a === void 0 ? void 0 : _a[0].children.lambdaBody[0].children.expression) === null || _b === void 0 ? void 0 : _b[0].children;
const lastExpressionLambdaBodyTernaryExpression = (_c = lastExpressionLambdaBodyExpression === null || lastExpressionLambdaBodyExpression === void 0 ? void 0 : lastExpressionLambdaBodyExpression.conditionalExpression) === null || _c === void 0 ? void 0 : _c[0].children;
const isHuggable = !lastExpression.comments &&
(!lastExpressionLambdaBodyExpression ||
(lastExpressionLambdaBodyTernaryExpression === null || lastExpressionLambdaBodyTernaryExpression === void 0 ? void 0 : lastExpressionLambdaBodyTernaryExpression.QuestionMark) !== undefined ||
((_d = lastExpressionLambdaBodyTernaryExpression === null || lastExpressionLambdaBodyTernaryExpression === void 0 ? void 0 : lastExpressionLambdaBodyTernaryExpression.binaryExpression) === null || _d === void 0 ? void 0 : _d[0].children.unaryExpression.length) === 1) &&
expressions.findIndex(({ children }) => children.lambdaExpression) ===
expressions.length - 1;
const args = map(path, print, "expression");
const allArgsExpandable = [
indent([softline, ...join([",", line], args)]),
softline
];
if (!isHuggable || willBreak(args.at(-1)[0])) {
return allArgsExpandable;
}
const headArgs = args.slice(0, -1);
const huggedLastArg = path.call(argPath => print(argPath, { hug: true }), "children", "expression", args.length - 1);
const lastArgExpanded = join(", ", [
...headArgs,
group(huggedLastArg, { shouldBreak: true })
]);
if (willBreak(huggedLastArg)) {
return [
breakParent,
conditionalGroup([lastArgExpanded, allArgsExpandable])
];
}
return conditionalGroup([
join(", ", [...headArgs, huggedLastArg]),
lastArgExpanded,
allArgsExpandable
]);
},
arrayCreationExpression(path, print) {
const { children } = path.node;
const typeKey = onlyDefinedKey(children, [
"classOrInterfaceType",
"primitiveType"
]);
const suffixKey = onlyDefinedKey(children, [
"arrayCreationExpressionWithoutInitializerSuffix",
"arrayCreationWithInitializerSuffix"
]);
return ["new ", call(path, print, typeKey), call(path, print, suffixKey)];
},
arrayCreationExpressionWithoutInitializerSuffix(path, print) {
const expressions = call(path, print, "dimExprs");
return path.node.children.dims
? [expressions, call(path, print, "dims")]
: expressions;
},
arrayCreationWithInitializerSuffix(path, print) {
return [
call(path, print, "dims"),
" ",
call(path, print, "arrayInitializer")
];
},
dimExprs(path, print) {
return map(path, print, "dimExpr");
},
dimExpr(path, print) {
return join(" ", [
...map(path, print, "annotation"),
["[", call(path, print, "expression"), "]"]
]);
},
classLiteralSuffix(path, print) {
const lSquares = map(path, print, "LSquare");
const rSquares = map(path, print, "RSquare");
return [
...lSquares.flatMap((lSquare, index) => [lSquare, rSquares[index]]),
".class"
];
},
arrayAccessSuffix(path, print) {
return ["[", call(path, print, "expression"), "]"];
},
methodReferenceSuffix(path, print) {
const { children } = path.node;
const reference = ["::"];
if (children.typeArguments) {
reference.push(call(path, print, "typeArguments"));
}
reference.push(call(path, print, onlyDefinedKey(children, ["Identifier", "New"])));
return reference;
},
templateArgument: printSingle,
template: printSingle,
stringTemplate(path, print) {
return printTemplate(path, print, "StringTemplateBegin", "StringTemplateMid", "StringTemplateEnd");
},
textBlockTemplate(path, print) {
return printTemplate(path, print, "TextBlockTemplateBegin", "TextBlockTemplateMid", "TextBlockTemplateEnd");
},
embeddedExpression: printSingle,
pattern: printSingle,
typePattern: printSingle,
recordPattern(path, print) {
const patterns = path.node.children.componentPatternList
? indentInParentheses(call(path, print, "componentPatternList"))
: "()";
return [call(path, print, "referenceType"), patterns];
},
componentPatternList(path, print) {
return printList(path, print, "componentPattern");
},
componentPattern: printSingle,
matchAllPattern: printSingle,
guard(path, print) {
var _a;
const expression = call(path, print, "expression");
const hasParentheses = ((_a = path.node.children.expression[0].children.conditionalExpression) === null || _a === void 0 ? void 0 : _a[0].children.binaryExpression[0].children.unaryExpression[0].children.primary[0].children.primaryPrefix[0].children.parenthesisExpression) !==
undefined;
return [
"when ",
hasParentheses
? expression
: group([
ifBreak("("),
indent([softline, expression]),
softline,
ifBreak(")")
])
];
}
};
function binary(operands, operators, { hasNonAssignmentOperators = false, isInList = false, isRoot = false, operatorPosition }) {
let levelOperator;
let levelPrecedence;
let level = [];
while (operators.length) {
const nextOperator = operators[0].image;
const nextPrecedence = getOperatorPrecedence(nextOperator);
if (levelPrecedence === undefined || nextPrecedence === levelPrecedence) {
const { image: operator, doc: operatorDoc } = operators.shift();
level.push(operands.shift());
if (levelOperator !== undefined &&
needsParentheses(levelOperator, operator)) {
level = [["(", group(indent(level)), ")"]];
}
const parts = [" ", operatorDoc, line];
if (operatorPosition === "start" && !isAssignmentOperator(operator)) {
parts.reverse();
}
level.push(parts);
levelOperator = operator;
levelPrecedence = nextPrecedence;
}
else if (nextPrecedence < levelPrecedence) {
if (!isRoot) {
break;
}
level.push(operands.shift());
const content = group(indent(level));
operands.unshift(levelOperator !== undefined &&
needsParentheses(levelOperator, nextOperator)
? ["(", content, ")"]
: content);
level = [];
levelOperator = undefined;
levelPrecedence = undefined;
}
else {
const content = binary(operands, operators, { operatorPosition });
operands.unshift(levelOperator !== undefined &&
needsParentheses(nextOperator, levelOperator)
? ["(", indent(content), ")"]
: content);
}
}
level.push(operands.shift());
if (!levelOperator ||
(!isInList &&
!isAssignmentOperator(levelOperator) &&
levelOperator !== "instanceof")) {
return group(level);
}
if (!isRoot || hasNonAssignmentOperators) {
return group(indent(level));
}
const groupId = Symbol("assignment");
return group([
level[0],
group(indent(level[1]), { id: groupId }),
indentIfBreak(level[2], { groupId })
]);
}
const precedencesByOperator = new Map([
["||"],
["&&"],
["|"],
["^"],
["&"],
["==", "!="],
["<", ">", "<=", ">=", "instanceof"],
["<<", ">>", ">>>"],
["+", "-"],
["*", "/", "%"]
].flatMap((operators, index) => operators.map(operator => [operator, index])));
function getOperatorPrecedence(operator) {
var _a;
return (_a = precedencesByOperator.get(operator)) !== null && _a !== void 0 ? _a : -1;
}
function needsParentheses(operator, parentOperator) {
return ((operator === "&&" && parentOperator === "||") ||
(["|", "^", "&", "<<", ">>", ">>>"].includes(parentOperator) &&
getOperatorPrecedence(operator) >
getOperatorPrecedence(parentOperator)) ||
[operator, parentOperator].every(o => ["==", "!="].includes(o)) ||
[operator, parentOperator].every(o => ["<<", ">>", ">>>"].includes(o)) ||
(operator === "*" && parentOperator === "/") ||
(operator === "/" && parentOperator === "*") ||
(operator === "%" && ["+", "-", "*", "/"].includes(parentOperator)) ||
(["*", "/"].includes(operator) && parentOperator === "%"));
}
const assignmentOperators = new Set([
"=",
"*=",
"/=",
"%=",
"+=",
"-=",
"<<=",
">>=",
">>>=",
"&=",
"^=",
"|="
]);
function isAssignmentOperator(operator) {
return assignmentOperators.has(operator);
}
function isCapitalizedIdentifier(fqnOrRefType) {
var _a, _b, _c;
const nextToLastIdentifier = (_c = (_b = [
fqnOrRefType.fqnOrRefTypePartFirst[0],
...((_a = fqnOrRefType.fqnOrRefTypePartRest) !== null && _a !== void 0 ? _a : [])
].at(-2)) === null || _b === void 0 ? void 0 : _b.children.fqnOrRefTypePartCommon[0].children.Identifier) === null || _c === void 0 ? void 0 : _c[0].image;
return /^\p{Uppercase_Letter}/u.test(nextToLastIdentifier !== null && nextToLastIdentifier !== void 0 ? nextToLastIdentifier : "");
}
function printTemplate(path, print, beginKey, midKey, endKey) {
const begin = call(path, ({ node }) => node.image, beginKey);
const mids = map(path, ({ node }) => node.image, midKey);
const end = call(path, ({ node }) => node.image, endKey);
const lines = [begin, ...mids, end].join("").split("\n").slice(1);
const baseIndent = findBaseIndent(lines);
const prefix = "\n" + " ".repeat(baseIndent);
const parts = [begin, ...mids, end].map(image => join(hardline, image.split(prefix)));
return indent([
parts[0],
...map(path, (expressionPath, index) => {
const expression = group([
indent([softline, print(expressionPath), lineSuffixBoundary]),
softline
]);
return index === 0 ? expression : [parts[index], expression];
}, "embeddedExpression"),
parts.at(-1)
]);
}

View File

@@ -0,0 +1,71 @@
import type { AnnotationCstNode, ClassPermitsCstNode, ClassTypeCtx, CstElement, CstNode, ExpressionCstNode, InterfacePermitsCstNode, IToken, StatementCstNode } from "java-parser";
import type { AstPath, Doc, ParserOptions } from "prettier";
import { builders } from "prettier/doc";
import type { JavaComment } from "../comments.js";
export declare function onlyDefinedKey<T extends Record<string, any>, K extends Key<T> & string>(obj: T, options?: K[]): K;
export declare function definedKeys<T extends Record<string, any>, K extends Key<T> & string>(obj: T, options?: K[]): K[];
export declare function printWithModifiers<T extends CstNode, P extends IterProperties<T["children"]>>(path: AstPath<T>, print: JavaPrintFn, modifierChild: P, contents: Doc, noTypeAnnotations?: boolean): builders.Doc[];
export declare function hasDeclarationAnnotations(modifiers: ModifierNode[]): boolean;
export declare function call<T extends CstNode, U, P extends IterProperties<T["children"]>>(path: AstPath<T>, callback: MapCallback<IndexValue<IndexValue<T, "children">, P>, U>, child: P): U;
export declare function each<T extends CstNode, P extends IterProperties<T["children"]>>(path: AstPath<T>, callback: MapCallback<IndexValue<IndexValue<T, "children">, P>, void>, child: P): void;
export declare function map<T extends CstNode, U, P extends IterProperties<T["children"]>>(path: AstPath<T>, callback: MapCallback<IndexValue<IndexValue<T, "children">, P>, U>, child: P): U[];
export declare function flatMap<T extends CstNode, U, P extends IterProperties<T["children"]>>(path: AstPath<T>, callback: MapCallback<IndexValue<IndexValue<T, "children">, P>, U>, children: P[]): U[];
export declare function printSingle(path: AstPath<JavaNonTerminal>, print: JavaPrintFn, _?: JavaParserOptions, args?: unknown): builders.Doc;
export declare function lineStartWithComments(node: JavaNonTerminal): number;
export declare function lineEndWithComments(node: JavaNonTerminal): number;
export declare function printDanglingComments(path: AstPath<JavaNonTerminal>): builders.Doc[];
export declare function printComment(node: JavaTerminal): string | builders.Doc[];
export declare function hasLeadingComments(node: JavaNode): boolean | undefined;
export declare function indentInParentheses(contents: Doc, opts?: {
shouldBreak?: boolean;
}): builders.Group | "()";
export declare function printArrayInitializer<T extends JavaNonTerminal, P extends IterProperties<T["children"]>>(path: AstPath<T>, print: JavaPrintFn, options: JavaParserOptions, child: P): builders.Group | "{}";
export declare function printBlock(path: AstPath<JavaNonTerminal>, contents: Doc[]): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[];
export declare function printName(path: AstPath<JavaNonTerminal & {
children: {
Identifier: IToken[];
};
}>, print: JavaPrintFn): builders.Doc[];
export declare function printList<T extends JavaNonTerminal, P extends IterProperties<T["children"]>>(path: AstPath<T>, print: JavaPrintFn, child: P): builders.Doc[];
export declare function printClassPermits(path: AstPath<ClassPermitsCstNode | InterfacePermitsCstNode>, print: JavaPrintFn): builders.Group;
export declare function printClassType(path: AstPath<JavaNonTerminal & {
children: ClassTypeCtx;
}>, print: JavaPrintFn): builders.Doc[];
export declare function isBinaryExpression(expression: ExpressionCstNode): boolean;
export declare function findBaseIndent(lines: string[]): number;
export declare function isEmptyStatement(statement: StatementCstNode): boolean;
export declare function isNonTerminal(node: CstElement): node is JavaNonTerminal;
export declare function isTerminal(node: CstElement): node is IToken;
export type JavaNode = CstElement & {
comments?: JavaComment[];
};
export type JavaNonTerminal = Exclude<JavaNode, IToken>;
export type JavaTerminal = Exclude<JavaNode, CstNode>;
export type JavaNodePrinters = {
[T in JavaNonTerminal["name"]]: JavaNodePrinter<T>;
};
export type JavaNodePrinter<T> = (path: AstPath<Extract<JavaNonTerminal, {
name: T;
}>>, print: JavaPrintFn, options: JavaParserOptions, args?: unknown) => Doc;
export type JavaPrintFn = (path: AstPath<JavaNode>, args?: unknown) => Doc;
export type JavaParserOptions = ParserOptions<JavaNode> & {
entrypoint?: string;
};
export type IterProperties<T> = T extends any[] ? IndexProperties<T> : ArrayProperties<T>;
type Key<T> = T extends T ? keyof T : never;
type ModifierNode = JavaNonTerminal & {
children: {
annotation?: AnnotationCstNode[];
};
};
type IsTuple<T> = T extends [] ? true : T extends [infer First, ...infer Remain] ? IsTuple<Remain> : false;
type IndexProperties<T extends {
length: number;
}> = IsTuple<T> extends true ? Exclude<Partial<T>["length"], T["length"]> : number;
type ArrayProperties<T> = {
[K in keyof T]: NonNullable<T[K]> extends readonly any[] ? K : never;
}[keyof T];
type ArrayElement<T> = T extends Array<infer E> ? E : never;
type MapCallback<T, U> = (path: AstPath<ArrayElement<T>>, index: number, value: any) => U;
type IndexValue<T, P> = T extends any[] ? P extends number ? T[P] : never : P extends keyof T ? T[P] : never;
export {};

View File

@@ -0,0 +1,239 @@
import { builders } from "prettier/doc";
import parser from "../parser.js";
const { group, hardline, ifBreak, indent, join, line, softline } = builders;
export function onlyDefinedKey(obj, options) {
const keys = definedKeys(obj, options);
if (keys.length === 1) {
return keys[0];
}
throw new Error(keys.length > 1
? `More than one defined key found: ${keys}`
: "No defined keys found");
}
export function definedKeys(obj, options) {
return (options !== null && options !== void 0 ? options : Object.keys(obj)).filter(key => obj[key] !== undefined);
}
const indexByModifier = [
"public",
"protected",
"private",
"abstract",
"default",
"static",
"final",
"transient",
"volatile",
"synchronized",
"native",
"sealed",
"non-sealed",
"strictfp"
].reduce((map, name, index) => map.set(name, index), new Map());
export function printWithModifiers(path, print, modifierChild, contents, noTypeAnnotations = false) {
const declarationAnnotations = [];
const otherModifiers = [];
const typeAnnotations = [];
each(path, modifierPath => {
const { children } = modifierPath.node;
const modifier = print(modifierPath);
if (children.annotation) {
(otherModifiers.length ? typeAnnotations : declarationAnnotations).push(modifier);
}
else {
otherModifiers.push(modifier);
declarationAnnotations.push(...typeAnnotations);
typeAnnotations.length = 0;
}
}, modifierChild);
if (noTypeAnnotations) {
declarationAnnotations.push(...typeAnnotations);
typeAnnotations.length = 0;
}
otherModifiers.sort((a, b) => indexByModifier.get(a) - indexByModifier.get(b));
return join(hardline, [
...declarationAnnotations,
join(" ", [...otherModifiers, ...typeAnnotations, contents])
]);
}
export function hasDeclarationAnnotations(modifiers) {
let hasAnnotation = false;
let hasNonAnnotation = false;
for (const modifier of modifiers) {
if (modifier.children.annotation) {
hasAnnotation = true;
}
else if (hasAnnotation) {
return true;
}
else {
hasNonAnnotation = true;
}
}
return hasAnnotation && !hasNonAnnotation;
}
export function call(path, callback, child) {
return path.map(callback, "children", child)[0];
}
export function each(path, callback, child) {
if (path.node.children[child]) {
path.each(callback, "children", child);
}
}
export function map(path, callback, child) {
return path.node.children[child] ? path.map(callback, "children", child) : [];
}
export function flatMap(path, callback, children) {
return children
.flatMap(child => map(path, callback, child).map((doc, index) => {
const node = path.node.children[child][index];
return {
doc,
startOffset: parser.locStart(node)
};
}))
.sort((a, b) => a.startOffset - b.startOffset)
.map(({ doc }) => doc);
}
export function printSingle(path, print, _, args) {
return call(path, childPath => print(childPath, args), onlyDefinedKey(path.node.children));
}
export function lineStartWithComments(node) {
const { comments, location } = node;
return comments
? Math.min(location.startLine, comments[0].startLine)
: location.startLine;
}
export function lineEndWithComments(node) {
const { comments, location } = node;
return comments
? Math.max(location.endLine, comments.at(-1).endLine)
: location.endLine;
}
export function printDanglingComments(path) {
if (!path.node.comments) {
return [];
}
const comments = [];
path.each(commentPath => {
const comment = commentPath.node;
if (comment.leading || comment.trailing) {
return;
}
comment.printed = true;
comments.push(printComment(comment));
}, "comments");
return join(hardline, comments);
}
export function printComment(node) {
const { image } = node;
const lines = image.split("\n").map(line => line.trim());
return lines.length > 1 &&
lines[0].startsWith("/*") &&
lines.slice(1).every(line => line.startsWith("*")) &&
lines.at(-1).endsWith("*/")
? join(hardline, lines.map((line, index) => (index === 0 ? line : ` ${line}`)))
: image;
}
export function hasLeadingComments(node) {
var _a;
return (_a = node.comments) === null || _a === void 0 ? void 0 : _a.some(({ leading }) => leading);
}
export function indentInParentheses(contents, opts) {
return !Array.isArray(contents) || contents.length
? group(["(", indent([softline, contents]), softline, ")"], opts)
: "()";
}
export function printArrayInitializer(path, print, options, child) {
const list = [];
if (child && child in path.node.children) {
list.push(call(path, print, child));
if (options.trailingComma !== "none") {
list.push(ifBreak(","));
}
}
list.push(...printDanglingComments(path));
return list.length ? group(["{", indent([line, ...list]), line, "}"]) : "{}";
}
export function printBlock(path, contents) {
if (!contents.length) {
const danglingComments = printDanglingComments(path);
return danglingComments.length
? ["{", indent([hardline, ...danglingComments]), hardline, "}"]
: "{}";
}
return group([
"{",
indent([hardline, ...join(hardline, contents)]),
hardline,
"}"
]);
}
export function printName(path, print) {
return join(".", map(path, print, "Identifier"));
}
export function printList(path, print, child) {
return join([",", line], map(path, print, child));
}
export function printClassPermits(path, print) {
return group([
"permits",
indent([line, group(printList(path, print, "typeName"))])
]);
}
export function printClassType(path, print) {
const { children } = path.node;
return definedKeys(children, ["annotation", "Identifier", "typeArguments"])
.flatMap(child => children[child].map((node, index) => ({
child,
index,
startOffset: parser.locStart(node)
})))
.sort((a, b) => a.startOffset - b.startOffset)
.flatMap(({ child, index: childIndex }, index, array) => {
const node = children[child][childIndex];
const next = array.at(index + 1);
const nextNode = next && children[next.child][next.index];
const docs = [path.call(print, "children", child, childIndex)];
if (nextNode) {
if (isNonTerminal(node)) {
docs.push(node.name === "annotation" ? " " : ".");
}
else if (isTerminal(nextNode) || nextNode.name === "annotation") {
docs.push(".");
}
}
return docs;
});
}
export function isBinaryExpression(expression) {
var _a;
const conditionalExpression = (_a = expression.children.conditionalExpression) === null || _a === void 0 ? void 0 : _a[0].children;
if (!conditionalExpression) {
return false;
}
const isTernary = conditionalExpression.QuestionMark !== undefined;
if (isTernary) {
return false;
}
const hasNonAssignmentOperators = Object.values(conditionalExpression.binaryExpression[0].children).some(child => {
var _a;
return isTerminal(child[0]) &&
!((_a = child[0].tokenType.CATEGORIES) === null || _a === void 0 ? void 0 : _a.some(category => category.name === "AssignmentOperator"));
});
return hasNonAssignmentOperators;
}
export function findBaseIndent(lines) {
return lines.length
? Math.min(...lines.map(line => line.search(/\S/)).filter(indent => indent >= 0))
: 0;
}
export function isEmptyStatement(statement) {
var _a;
return (((_a = statement.children.statementWithoutTrailingSubstatement) === null || _a === void 0 ? void 0 : _a[0].children.emptyStatement) !== undefined);
}
export function isNonTerminal(node) {
return !isTerminal(node);
}
export function isTerminal(node) {
return "tokenType" in node;
}

View File

@@ -0,0 +1,2 @@
import type { JavaNodePrinter, JavaNodePrinters } from "./helpers.js";
export declare function printerForNodeType<T extends keyof JavaNodePrinters>(type: T): JavaNodePrinter<T>;

View File

@@ -0,0 +1,13 @@
import arrays from "./arrays.js";
import blocksAndStatements from "./blocks-and-statements.js";
import classes from "./classes.js";
import expressions from "./expressions.js";
import interfaces from "./interfaces.js";
import lexicalStructure from "./lexical-structure.js";
import names from "./names.js";
import packagesAndModules from "./packages-and-modules.js";
import typesValuesAndVariables from "./types-values-and-variables.js";
const printersByNodeType = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, arrays), blocksAndStatements), classes), expressions), interfaces), lexicalStructure), names), packagesAndModules), typesValuesAndVariables);
export function printerForNodeType(type) {
return printersByNodeType[type];
}

View File

@@ -0,0 +1,62 @@
import { builders } from "prettier/doc";
import { printClassPermits, printSingle } from "./helpers.js";
declare const _default: {
interfaceDeclaration(path: import("prettier").AstPath<import("java-parser").InterfaceDeclarationCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
normalInterfaceDeclaration(path: import("prettier").AstPath<import("java-parser").NormalInterfaceDeclarationCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
interfaceModifier: typeof printSingle;
interfaceExtends(path: import("prettier").AstPath<import("java-parser").InterfaceExtendsCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Group;
interfacePermits: typeof printClassPermits;
interfaceBody(path: import("prettier").AstPath<import("java-parser").InterfaceBodyCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[];
interfaceMemberDeclaration(path: import("prettier").AstPath<import("java-parser").InterfaceMemberDeclarationCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc;
constantDeclaration(path: import("prettier").AstPath<import("java-parser").ConstantDeclarationCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
constantModifier: typeof printSingle;
interfaceMethodDeclaration(path: import("prettier").AstPath<import("java-parser").InterfaceMethodDeclarationCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
interfaceMethodModifier: typeof printSingle;
annotationInterfaceDeclaration(path: import("prettier").AstPath<import("java-parser").AnnotationInterfaceDeclarationCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
annotationInterfaceBody(path: import("prettier").AstPath<import("java-parser").AnnotationInterfaceBodyCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Group | "{}" | (string | builders.Indent | builders.Hardline)[];
annotationInterfaceMemberDeclaration(path: import("prettier").AstPath<import("java-parser").AnnotationInterfaceMemberDeclarationCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc;
annotationInterfaceElementDeclaration(path: import("prettier").AstPath<import("java-parser").AnnotationInterfaceElementDeclarationCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
annotationInterfaceElementModifier: typeof printSingle;
defaultValue(path: import("prettier").AstPath<import("java-parser").DefaultValueCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
annotation(path: import("prettier").AstPath<import("java-parser").AnnotationCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
elementValuePairList(path: import("prettier").AstPath<import("java-parser").ElementValuePairListCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
elementValuePair(path: import("prettier").AstPath<import("java-parser").ElementValuePairCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
elementValue: typeof printSingle;
elementValueArrayInitializer(path: import("prettier").AstPath<import("java-parser").ElementValueArrayInitializerCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn, options: import("./helpers.js").JavaParserOptions): builders.Group | "{}";
elementValueList(path: import("prettier").AstPath<import("java-parser").ElementValueListCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Group;
};
export default _default;

View File

@@ -0,0 +1,157 @@
import { builders } from "prettier/doc";
import { call, each, hasDeclarationAnnotations, indentInParentheses, lineEndWithComments, lineStartWithComments, onlyDefinedKey, printArrayInitializer, printBlock, printClassPermits, printList, printSingle, printWithModifiers } from "./helpers.js";
const { group, hardline, indent, join, line } = builders;
export default {
interfaceDeclaration(path, print) {
const declarationKey = onlyDefinedKey(path.node.children, [
"annotationInterfaceDeclaration",
"normalInterfaceDeclaration"
]);
return printWithModifiers(path, print, "interfaceModifier", call(path, print, declarationKey), true);
},
normalInterfaceDeclaration(path, print) {
const { interfaceExtends, interfacePermits, typeParameters } = path.node.children;
const header = ["interface ", call(path, print, "typeIdentifier")];
if (typeParameters) {
header.push(call(path, print, "typeParameters"));
}
if (interfaceExtends) {
header.push(indent([line, call(path, print, "interfaceExtends")]));
}
if (interfacePermits) {
header.push(indent([line, call(path, print, "interfacePermits")]));
}
return [group(header), " ", call(path, print, "interfaceBody")];
},
interfaceModifier: printSingle,
interfaceExtends(path, print) {
return group([
"extends",
indent([line, call(path, print, "interfaceTypeList")])
]);
},
interfacePermits: printClassPermits,
interfaceBody(path, print) {
const declarations = [];
let previousRequiresPadding = false;
each(path, declarationPath => {
var _a, _b, _c, _d;
const declaration = print(declarationPath);
if (declaration === "") {
return;
}
const { node, previous } = declarationPath;
const constantDeclaration = (_a = node.children.constantDeclaration) === null || _a === void 0 ? void 0 : _a[0].children;
const methodDeclaration = (_b = node.children.interfaceMethodDeclaration) === null || _b === void 0 ? void 0 : _b[0].children;
const currentRequiresPadding = (!constantDeclaration && !methodDeclaration) ||
(methodDeclaration === null || methodDeclaration === void 0 ? void 0 : methodDeclaration.methodBody[0].children.block) !== undefined ||
hasDeclarationAnnotations((_d = (_c = constantDeclaration === null || constantDeclaration === void 0 ? void 0 : constantDeclaration.constantModifier) !== null && _c !== void 0 ? _c : methodDeclaration === null || methodDeclaration === void 0 ? void 0 : methodDeclaration.interfaceMethodModifier) !== null && _d !== void 0 ? _d : []);
const blankLine = declarations.length > 0 &&
(previousRequiresPadding ||
currentRequiresPadding ||
lineStartWithComments(node) > lineEndWithComments(previous) + 1);
declarations.push(blankLine ? [hardline, declaration] : declaration);
previousRequiresPadding = currentRequiresPadding;
}, "interfaceMemberDeclaration");
return printBlock(path, declarations);
},
interfaceMemberDeclaration(path, print) {
const { children } = path.node;
return children.Semicolon
? ""
: call(path, print, onlyDefinedKey(children));
},
constantDeclaration(path, print) {
const declaration = [
call(path, print, "unannType"),
" ",
call(path, print, "variableDeclaratorList"),
";"
];
return printWithModifiers(path, print, "constantModifier", declaration);
},
constantModifier: printSingle,
interfaceMethodDeclaration(path, print) {
const declaration = [
call(path, print, "methodHeader"),
path.node.children.methodBody[0].children.Semicolon ? "" : " ",
call(path, print, "methodBody")
];
return printWithModifiers(path, print, "interfaceMethodModifier", declaration);
},
interfaceMethodModifier: printSingle,
annotationInterfaceDeclaration(path, print) {
return join(" ", [
"@interface",
call(path, print, "typeIdentifier"),
call(path, print, "annotationInterfaceBody")
]);
},
annotationInterfaceBody(path, print) {
const declarations = [];
each(path, declarationPath => {
const declaration = print(declarationPath);
if (declaration === "") {
return;
}
declarations.push(declarationPath.isFirst ? declaration : [hardline, declaration]);
}, "annotationInterfaceMemberDeclaration");
return printBlock(path, declarations);
},
annotationInterfaceMemberDeclaration(path, print) {
const { children } = path.node;
return children.Semicolon
? ""
: call(path, print, onlyDefinedKey(children));
},
annotationInterfaceElementDeclaration(path, print) {
const { dims, defaultValue } = path.node.children;
const declaration = [
call(path, print, "unannType"),
" ",
call(path, print, "Identifier"),
"()"
];
if (dims) {
declaration.push(call(path, print, "dims"));
}
if (defaultValue) {
declaration.push(" ", call(path, print, "defaultValue"));
}
declaration.push(";");
return printWithModifiers(path, print, "annotationInterfaceElementModifier", declaration);
},
annotationInterfaceElementModifier: printSingle,
defaultValue(path, print) {
return ["default ", call(path, print, "elementValue")];
},
annotation(path, print) {
const { children } = path.node;
const annotation = ["@", call(path, print, "typeName")];
if (children.elementValue || children.elementValuePairList) {
const valuesKey = onlyDefinedKey(children, [
"elementValue",
"elementValuePairList"
]);
annotation.push(indentInParentheses(call(path, print, valuesKey)));
}
return annotation;
},
elementValuePairList(path, print) {
return printList(path, print, "elementValuePair");
},
elementValuePair(path, print) {
return join(" ", [
call(path, print, "Identifier"),
call(path, print, "Equals"),
call(path, print, "elementValue")
]);
},
elementValue: printSingle,
elementValueArrayInitializer(path, print, options) {
return printArrayInitializer(path, print, options, "elementValueList");
},
elementValueList(path, print) {
return group(printList(path, print, "elementValue"));
}
};

View File

@@ -0,0 +1,14 @@
import { builders } from "prettier/doc";
import { printSingle } from "./helpers.js";
declare const _default: {
literal(path: import("prettier").AstPath<import("java-parser").LiteralCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc;
integerLiteral: typeof printSingle;
floatingPointLiteral: typeof printSingle;
booleanLiteral: typeof printSingle;
shiftOperator(path: import("prettier").AstPath<import("java-parser").ShiftOperatorCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
};
export default _default;

View File

@@ -0,0 +1,29 @@
import { builders } from "prettier/doc";
import { findBaseIndent, map, onlyDefinedKey, printSingle } from "./helpers.js";
const { hardline, indent, join } = builders;
export default {
literal(path, print) {
const { TextBlock } = path.node.children;
if (!TextBlock) {
return printSingle(path, print);
}
const [open, ...lines] = TextBlock[0].image.split("\n");
const baseIndent = findBaseIndent(lines);
const textBlock = join(hardline, [
open,
...lines.map(line => line.slice(baseIndent))
]);
const ancestor = path.getNode(14);
return (ancestor === null || ancestor === void 0 ? void 0 : ancestor.name) === "variableInitializer" ||
((ancestor === null || ancestor === void 0 ? void 0 : ancestor.name) === "binaryExpression" &&
ancestor.children.AssignmentOperator)
? indent(textBlock)
: textBlock;
},
integerLiteral: printSingle,
floatingPointLiteral: printSingle,
booleanLiteral: printSingle,
shiftOperator(path, print) {
return map(path, print, onlyDefinedKey(path.node.children));
}
};

View File

@@ -0,0 +1,12 @@
import { printName, printSingle } from "./helpers.js";
declare const _default: {
typeIdentifier: typeof printSingle;
moduleName: typeof printName;
packageName: typeof printName;
typeName: typeof printName;
expressionName: typeof printName;
methodName: typeof printSingle;
packageOrTypeName: typeof printName;
ambiguousName: typeof printName;
};
export default _default;

View File

@@ -0,0 +1,11 @@
import { printName, printSingle } from "./helpers.js";
export default {
typeIdentifier: printSingle,
moduleName: printName,
packageName: printName,
typeName: printName,
expressionName: printName,
methodName: printSingle,
packageOrTypeName: printName,
ambiguousName: printName
};

View File

@@ -0,0 +1,46 @@
import type { ExportsModuleDirectiveCstNode, ImportDeclarationCstNode, OpensModuleDirectiveCstNode } from "java-parser";
import type { AstPath } from "prettier";
import { builders } from "prettier/doc";
import { printSingle, type JavaPrintFn } from "./helpers.js";
declare const _default: {
compilationUnit(path: AstPath<import("java-parser").CompilationUnitCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
ordinaryCompilationUnit(path: AstPath<import("java-parser").OrdinaryCompilationUnitCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
modularCompilationUnit(path: AstPath<import("java-parser").ModularCompilationUnitCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
packageDeclaration(path: AstPath<import("java-parser").PackageDeclarationCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
packageModifier: typeof printSingle;
importDeclaration(path: AstPath<ImportDeclarationCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc;
typeDeclaration(path: AstPath<import("java-parser").TypeDeclarationCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc;
moduleDeclaration(path: AstPath<import("java-parser").ModuleDeclarationCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
moduleDirective: typeof printSingle;
requiresModuleDirective(path: AstPath<import("java-parser").RequiresModuleDirectiveCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
exportsModuleDirective(path: AstPath<ExportsModuleDirectiveCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
opensModuleDirective(path: AstPath<OpensModuleDirectiveCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
usesModuleDirective(path: AstPath<import("java-parser").UsesModuleDirectiveCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
providesModuleDirective(path: AstPath<import("java-parser").ProvidesModuleDirectiveCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: JavaPrintFn): builders.Doc[];
requiresModifier: typeof printSingle;
};
export default _default;

View File

@@ -0,0 +1,169 @@
import { builders } from "prettier/doc";
import { call, lineEndWithComments, lineStartWithComments, map, printBlock, printDanglingComments, printName, printSingle } from "./helpers.js";
const { group, hardline, indent, join, line } = builders;
export default {
compilationUnit(path, print) {
return [...printDanglingComments(path), printSingle(path, print), hardline];
},
ordinaryCompilationUnit(path, print) {
const { children } = path.node;
const declarations = [];
if (children.packageDeclaration) {
declarations.push(call(path, print, "packageDeclaration"));
}
if (children.importDeclaration) {
const staticCount = sortImports(children.importDeclaration);
const importDeclarations = map(path, print, "importDeclaration").filter(doc => doc !== "");
const staticDeclarations = importDeclarations.slice(0, staticCount);
const nonStaticDeclarations = importDeclarations.slice(staticCount);
declarations.push(...[staticDeclarations, nonStaticDeclarations]
.filter(({ length }) => length)
.map(declarations => join(hardline, declarations)));
}
if (children.typeDeclaration) {
declarations.push(...map(path, print, "typeDeclaration").filter(declaration => declaration !== ""));
}
return join([hardline, hardline], declarations);
},
modularCompilationUnit(path, print) {
const { children } = path.node;
const declarations = [];
if (children.importDeclaration) {
const staticCount = sortImports(children.importDeclaration);
const importDeclarations = map(path, print, "importDeclaration").filter(doc => doc !== "");
const staticDeclarations = importDeclarations.slice(0, staticCount);
const nonStaticDeclarations = importDeclarations.slice(staticCount);
declarations.push(...[staticDeclarations, nonStaticDeclarations]
.filter(({ length }) => length)
.map(declarations => join(hardline, declarations)));
}
declarations.push(call(path, print, "moduleDeclaration"));
return join([hardline, hardline], declarations);
},
packageDeclaration(path, print) {
return join(hardline, [
...map(path, print, "packageModifier"),
["package ", printName(path, print), ";"]
]);
},
packageModifier: printSingle,
importDeclaration(path, print) {
const { children } = path.node;
if (children.emptyStatement) {
return call(path, print, "emptyStatement");
}
const declaration = ["import "];
if (children.Static) {
declaration.push("static ");
}
declaration.push(call(path, print, "packageOrTypeName"));
if (children.Star) {
declaration.push(".*");
}
declaration.push(";");
return declaration;
},
typeDeclaration(path, print) {
return path.node.children.Semicolon ? "" : printSingle(path, print);
},
moduleDeclaration(path, print) {
const { annotation, Open } = path.node.children;
const prefix = [];
if (annotation) {
prefix.push(...map(path, print, "annotation"));
}
if (Open) {
prefix.push("open");
}
const declarations = map(path, declarationPath => {
const declaration = print(declarationPath);
const { node, previous } = declarationPath;
return !previous ||
lineStartWithComments(node) <= lineEndWithComments(previous) + 1
? declaration
: [hardline, declaration];
}, "moduleDirective");
return join(" ", [
...prefix,
"module",
printName(path, print),
printBlock(path, declarations)
]);
},
moduleDirective: printSingle,
requiresModuleDirective(path, print) {
return join(" ", [
"requires",
...map(path, print, "requiresModifier"),
[call(path, print, "moduleName"), ";"]
]);
},
exportsModuleDirective(path, print) {
return printToModuleNamesDirective(path, print, "exports");
},
opensModuleDirective(path, print) {
return printToModuleNamesDirective(path, print, "opens");
},
usesModuleDirective(path, print) {
return ["uses ", call(path, print, "typeName"), ";"];
},
providesModuleDirective(path, print) {
const [firstTypeName, ...restTypeNames] = map(path, print, "typeName");
return [
"provides ",
firstTypeName,
group(indent([
line,
group(indent(["with", line, ...join([",", line], restTypeNames)]))
])),
";"
];
},
requiresModifier: printSingle
};
function sortImports(importDeclarations) {
importDeclarations.sort(({ children: a }, { children: b }) => {
if (a.Static && !b.Static) {
return -1;
}
else if (b.Static && !a.Static) {
return 1;
}
if (!b.packageOrTypeName) {
if (a.packageOrTypeName) {
return -1;
}
return 0;
}
else if (!a.packageOrTypeName) {
return 1;
}
return compareFqn(a.packageOrTypeName[0], b.packageOrTypeName[0]);
});
return importDeclarations.reduce((staticCount, importDeclaration) => importDeclaration.children.Static ? staticCount + 1 : staticCount, 0);
}
function compareFqn(a, b) {
const identifiersA = a.children.Identifier;
const identifiersB = b.children.Identifier;
const minParts = Math.min(identifiersA.length, identifiersB.length);
for (let i = 0; i < minParts; i++) {
const imageA = identifiersA[i].image;
const imageB = identifiersB[i].image;
if (imageA < imageB) {
return -1;
}
else if (imageA > imageB) {
return 1;
}
}
return identifiersA.length - identifiersB.length;
}
function printToModuleNamesDirective(path, print, prefix) {
const directive = [prefix, " ", call(path, print, "packageName")];
if (path.node.children.moduleName) {
const moduleNames = join([",", line], map(path, print, "moduleName"));
directive.push(group(indent([line, group(indent(["to", line, ...moduleNames]))])));
}
directive.push(";");
return directive;
}

View File

@@ -0,0 +1,46 @@
import { builders } from "prettier/doc";
import { printClassType, printSingle } from "./helpers.js";
declare const _default: {
primitiveType(path: import("prettier").AstPath<import("java-parser").PrimitiveTypeCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
numericType: typeof printSingle;
integralType: typeof printSingle;
floatingPointType: typeof printSingle;
referenceType(path: import("prettier").AstPath<import("java-parser").ReferenceTypeCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
classOrInterfaceType: typeof printSingle;
classType: typeof printClassType;
interfaceType: typeof printSingle;
typeVariable(path: import("prettier").AstPath<import("java-parser").TypeVariableCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
dims(path: import("prettier").AstPath<import("java-parser").DimsCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
typeParameter(path: import("prettier").AstPath<import("java-parser").TypeParameterCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
typeParameterModifier: typeof printSingle;
typeBound(path: import("prettier").AstPath<import("java-parser").TypeBoundCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
additionalBound(path: import("prettier").AstPath<import("java-parser").AdditionalBoundCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
typeArguments(path: import("prettier").AstPath<import("java-parser").TypeArgumentsCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Group;
typeArgumentList(path: import("prettier").AstPath<import("java-parser").TypeArgumentListCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
typeArgument: typeof printSingle;
wildcard(path: import("prettier").AstPath<import("java-parser").WildcardCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
wildcardBounds(path: import("prettier").AstPath<import("java-parser").WildcardBoundsCstNode & {
comments?: import("../comments.js").JavaComment[];
}>, print: import("./helpers.js").JavaPrintFn): builders.Doc[];
};
export default _default;

View File

@@ -0,0 +1,90 @@
import { builders } from "prettier/doc";
import { call, definedKeys, flatMap, isNonTerminal, map, onlyDefinedKey, printClassType, printList, printSingle } from "./helpers.js";
const { group, indent, join, line, softline } = builders;
export default {
primitiveType(path, print) {
const { children } = path.node;
const typeKey = onlyDefinedKey(children, ["Boolean", "numericType"]);
return join(" ", [
...map(path, print, "annotation"),
call(path, print, typeKey)
]);
},
numericType: printSingle,
integralType: printSingle,
floatingPointType: printSingle,
referenceType(path, print) {
const { children } = path.node;
const typeKey = onlyDefinedKey(children, [
"primitiveType",
"classOrInterfaceType"
]);
const type = call(path, print, typeKey);
return join(" ", [
...map(path, print, "annotation"),
children.dims ? [type, call(path, print, "dims")] : type
]);
},
classOrInterfaceType: printSingle,
classType: printClassType,
interfaceType: printSingle,
typeVariable(path, print) {
return join(" ", [
...map(path, print, "annotation"),
call(path, print, "Identifier")
]);
},
dims(path, print) {
return flatMap(path, childPath => {
const child = print(childPath);
return isNonTerminal(childPath.node) ? [child, " "] : child;
}, definedKeys(path.node.children, ["annotation", "LSquare", "RSquare"]));
},
typeParameter(path, print) {
const parameter = [
...map(path, print, "typeParameterModifier"),
call(path, print, "typeIdentifier")
];
if (path.node.children.typeBound) {
parameter.push(call(path, print, "typeBound"));
}
return join(" ", parameter);
},
typeParameterModifier: printSingle,
typeBound(path, print) {
const bound = ["extends ", call(path, print, "classOrInterfaceType")];
if (path.node.children.additionalBound) {
bound.push(group(indent([line, ...join(line, map(path, print, "additionalBound"))])));
}
return bound;
},
additionalBound(path, print) {
return ["& ", call(path, print, "interfaceType")];
},
typeArguments(path, print) {
return group([
"<",
indent([softline, call(path, print, "typeArgumentList")]),
softline,
">"
]);
},
typeArgumentList(path, print) {
return printList(path, print, "typeArgument");
},
typeArgument: printSingle,
wildcard(path, print) {
const wildcard = [...map(path, print, "annotation"), "?"];
if (path.node.children.wildcardBounds) {
wildcard.push(call(path, print, "wildcardBounds"));
}
return join(" ", wildcard);
},
wildcardBounds(path, print) {
return [
path.node.children.Extends ? "extends" : "super",
" ",
call(path, print, "referenceType")
];
}
};

View File

@@ -0,0 +1,178 @@
/**
* Prettier Plugin for Lua formatting using StyLua WebAssembly
*
* This plugin provides support for formatting Lua files using the StyLua WASM implementation.
* StyLua is a fast Lua code formatter written in Rust.
*/
import type { Plugin, Parser, Printer } from 'prettier';
// Import the StyLua WASM module
import luaInit, { format, type Config } from './lua_fmt_vite.js';
const parserName = 'lua';
// Language configuration
const languages = [
{
name: 'Lua',
aliases: ['lua'],
parsers: [parserName],
extensions: ['.lua'],
aceMode: 'lua',
tmScope: 'source.lua',
linguistLanguageId: 213,
vscodeLanguageIds: ['lua']
}
];
// Parser configuration
const luaParser: Parser<string> = {
astFormat: parserName,
parse: (text: string) => text,
locStart: () => 0,
locEnd: (node: string) => node.length,
};
// Lazy initialize StyLua WASM module
let initPromise: Promise<void> | null = null;
let isInitialized = false;
function initStyLua(): Promise<void> {
if (isInitialized) {
return Promise.resolve();
}
if (!initPromise) {
initPromise = (async () => {
try {
await luaInit();
isInitialized = true;
} catch (error) {
console.warn('Failed to initialize StyLua WASM module:', error);
initPromise = null;
throw error;
}
})();
}
return initPromise;
}
// Printer configuration
const luaPrinter: Printer<string> = {
// @ts-expect-error -- Support async printer like shell plugin
async print(path, options) {
try {
// Wait for initialization to complete
await initStyLua();
const text = (path as any).getValue ? (path as any).getValue() : path.node;
const config = getStyLuaConfig(options);
// Format using StyLua (synchronous call)
const formatted = format(text, "input.lua", config);
return formatted.trim();
} catch (error) {
console.warn('StyLua formatting failed:', error);
// Return original text if formatting fails
return (path as any).getValue ? (path as any).getValue() : path.node;
}
},
};
// Helper function to create StyLua config from Prettier options
function getStyLuaConfig(options: any): Config {
const config: Config = {};
// Map Prettier options to StyLua config
if (options.useTabs !== undefined) {
config.indent_style = options.useTabs ? 'tab' : 'space';
}
if (options.tabWidth !== undefined) {
config.indent_width = options.tabWidth;
}
if (options.printWidth !== undefined) {
config.line_width = options.printWidth;
}
if (options.endOfLine !== undefined) {
config.line_ending = options.endOfLine === 'crlf' ? 'crlf' : 'lf';
}
if (options.singleQuote !== undefined) {
config.quote_style = options.singleQuote ? 'AutoPreferSingle' : 'AutoPreferDouble';
}
// StyLua specific options
if (options.luaCallParentheses !== undefined) {
config.call_parentheses = options.luaCallParentheses;
}
if (options.luaCollapseSimpleStatement !== undefined) {
config.collapse_simple_statement = options.luaCollapseSimpleStatement;
}
if (options.luaSortRequires !== undefined) {
config.sort_requires = options.luaSortRequires;
}
return config;
}
// Plugin options
const options = {
luaCallParentheses: {
since: '0.0.1',
category: 'Format' as const,
type: 'choice' as const,
default: 'Always',
description: 'How to handle function call parentheses',
choices: [
{ value: 'Always', description: 'Always use parentheses' },
{ value: 'NoSingleString', description: 'Remove parentheses for single string argument' },
{ value: 'NoSingleTable', description: 'Remove parentheses for single table argument' },
{ value: 'None', description: 'Remove parentheses when possible' },
{ value: 'Input', description: 'Keep input formatting' }
]
},
luaCollapseSimpleStatement: {
since: '0.0.1',
category: 'Format' as const,
type: 'choice' as const,
default: 'Never',
description: 'How to handle simple statement collapsing',
choices: [
{ value: 'Never', description: 'Never collapse simple statements' },
{ value: 'FunctionOnly', description: 'Collapse function statements only' },
{ value: 'ConditionalOnly', description: 'Collapse conditional statements only' },
{ value: 'Always', description: 'Always collapse simple statements' }
]
},
luaSortRequires: {
since: '0.0.1',
category: 'Format' as const,
type: 'boolean' as const,
default: false,
description: 'Sort require statements alphabetically'
}
};
// Plugin object
const luaPlugin: Plugin = {
languages,
parsers: {
[parserName]: luaParser,
},
printers: {
[parserName]: luaPrinter,
},
options,
};
export default luaPlugin;
export { languages };
export const parsers = luaPlugin.parsers;
export const printers = luaPlugin.printers;

View File

@@ -0,0 +1,52 @@
/* tslint:disable */
/* eslint-disable */
export function format(input: string, filename: string, config?: Config): string;
interface LayoutConfig {
indent_style?: "tab" | "space";
indent_width?: number;
line_width?: number;
line_ending?: "lf" | "crlf";
}
export interface Config extends LayoutConfig {
quote_style?: "AutoPreferDouble" | "AutoPreferSingle" | "ForceDouble" | "ForceSingle";
call_parentheses?: "Always" | "NoSingleString" | "NoSingleTable" | "None" | "Input";
collapse_simple_statement?: | "Never" | "FunctionOnly" | "ConditionalOnly" | "Always";
sort_requires?: boolean;
}
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
export interface InitOutput {
readonly memory: WebAssembly.Memory;
readonly format: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
readonly __wbindgen_export_0: (a: number, b: number) => number;
readonly __wbindgen_export_1: (a: number, b: number, c: number, d: number) => number;
readonly __wbindgen_export_2: (a: number) => void;
readonly __wbindgen_add_to_stack_pointer: (a: number) => number;
readonly __wbindgen_export_3: (a: number, b: number, c: number) => void;
}
export type SyncInitInput = BufferSource | WebAssembly.Module;
/**
* Instantiates the given `module`, which can either be bytes or
* a precompiled `WebAssembly.Module`.
*
* @param {{ module: SyncInitInput }} module - Passing `SyncInitInput` directly is deprecated.
*
* @returns {InitOutput}
*/
export function initSync(module: { module: SyncInitInput } | SyncInitInput): InitOutput;
/**
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
* for everything else, calls `WebAssembly.instantiate` directly.
*
* @param {{ module_or_path: InitInput | Promise<InitInput> }} module_or_path - Passing `InitInput` directly is deprecated.
*
* @returns {Promise<InitOutput>}
*/
export default function __wbg_init (module_or_path?: { module_or_path: InitInput | Promise<InitInput> } | InitInput | Promise<InitInput>): Promise<InitOutput>;

View File

@@ -0,0 +1,524 @@
let wasm;
const heap = new Array(128).fill(undefined);
heap.push(undefined, null, true, false);
function getObject(idx) { return heap[idx]; }
let WASM_VECTOR_LEN = 0;
let cachedUint8ArrayMemory0 = null;
function getUint8ArrayMemory0() {
if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
}
return cachedUint8ArrayMemory0;
}
const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } );
const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
? function (arg, view) {
return cachedTextEncoder.encodeInto(arg, view);
}
: function (arg, view) {
const buf = cachedTextEncoder.encode(arg);
view.set(buf);
return {
read: arg.length,
written: buf.length
};
});
function passStringToWasm0(arg, malloc, realloc) {
if (realloc === undefined) {
const buf = cachedTextEncoder.encode(arg);
const ptr = malloc(buf.length, 1) >>> 0;
getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
WASM_VECTOR_LEN = buf.length;
return ptr;
}
let len = arg.length;
let ptr = malloc(len, 1) >>> 0;
const mem = getUint8ArrayMemory0();
let offset = 0;
for (; offset < len; offset++) {
const code = arg.charCodeAt(offset);
if (code > 0x7F) break;
mem[ptr + offset] = code;
}
if (offset !== len) {
if (offset !== 0) {
arg = arg.slice(offset);
}
ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
const ret = encodeString(arg, view);
offset += ret.written;
ptr = realloc(ptr, len, offset, 1) >>> 0;
}
WASM_VECTOR_LEN = offset;
return ptr;
}
let cachedDataViewMemory0 = null;
function getDataViewMemory0() {
if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
}
return cachedDataViewMemory0;
}
let heap_next = heap.length;
function addHeapObject(obj) {
if (heap_next === heap.length) heap.push(heap.length + 1);
const idx = heap_next;
heap_next = heap[idx];
heap[idx] = obj;
return idx;
}
function handleError(f, args) {
try {
return f.apply(this, args);
} catch (e) {
wasm.__wbindgen_export_2(addHeapObject(e));
}
}
function isLikeNone(x) {
return x === undefined || x === null;
}
function debugString(val) {
// primitive types
const type = typeof val;
if (type == 'number' || type == 'boolean' || val == null) {
return `${val}`;
}
if (type == 'string') {
return `"${val}"`;
}
if (type == 'symbol') {
const description = val.description;
if (description == null) {
return 'Symbol';
} else {
return `Symbol(${description})`;
}
}
if (type == 'function') {
const name = val.name;
if (typeof name == 'string' && name.length > 0) {
return `Function(${name})`;
} else {
return 'Function';
}
}
// objects
if (Array.isArray(val)) {
const length = val.length;
let debug = '[';
if (length > 0) {
debug += debugString(val[0]);
}
for(let i = 1; i < length; i++) {
debug += ', ' + debugString(val[i]);
}
debug += ']';
return debug;
}
// Test for built-in
const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val));
let className;
if (builtInMatches && builtInMatches.length > 1) {
className = builtInMatches[1];
} else {
// Failed to match the standard '[object ClassName]'
return toString.call(val);
}
if (className == 'Object') {
// we're a user defined class or Object
// JSON.stringify avoids problems with cycles, and is generally much
// easier than looping through ownProperties of `val`.
try {
return 'Object(' + JSON.stringify(val) + ')';
} catch (_) {
return 'Object';
}
}
// errors
if (val instanceof Error) {
return `${val.name}: ${val.message}\n${val.stack}`;
}
// TODO we could test for more things here, like `Set`s and `Map`s.
return className;
}
const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } );
if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); };
function getStringFromWasm0(ptr, len) {
ptr = ptr >>> 0;
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
}
function dropObject(idx) {
if (idx < 132) return;
heap[idx] = heap_next;
heap_next = idx;
}
function takeObject(idx) {
const ret = getObject(idx);
dropObject(idx);
return ret;
}
/**
* @param {string} input
* @param {string} filename
* @param {Config | undefined} [config]
* @returns {string}
*/
export function format(input, filename, config) {
let deferred4_0;
let deferred4_1;
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
const ptr0 = passStringToWasm0(input, wasm.__wbindgen_export_0, wasm.__wbindgen_export_1);
const len0 = WASM_VECTOR_LEN;
const ptr1 = passStringToWasm0(filename, wasm.__wbindgen_export_0, wasm.__wbindgen_export_1);
const len1 = WASM_VECTOR_LEN;
wasm.format(retptr, ptr0, len0, ptr1, len1, isLikeNone(config) ? 0 : addHeapObject(config));
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
var ptr3 = r0;
var len3 = r1;
if (r3) {
ptr3 = 0; len3 = 0;
throw takeObject(r2);
}
deferred4_0 = ptr3;
deferred4_1 = len3;
return getStringFromWasm0(ptr3, len3);
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
wasm.__wbindgen_export_3(deferred4_0, deferred4_1, 1);
}
}
async function __wbg_load(module, imports) {
if (typeof Response === 'function' && module instanceof Response) {
if (typeof WebAssembly.instantiateStreaming === 'function') {
try {
return await WebAssembly.instantiateStreaming(module, imports);
} catch (e) {
if (module.headers.get('Content-Type') != 'application/wasm') {
console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
} else {
throw e;
}
}
}
const bytes = await module.arrayBuffer();
return await WebAssembly.instantiate(bytes, imports);
} else {
const instance = await WebAssembly.instantiate(module, imports);
if (instance instanceof WebAssembly.Instance) {
return { instance, module };
} else {
return instance;
}
}
}
function __wbg_get_imports() {
const imports = {};
imports.wbg = {};
imports.wbg.__wbg_String_8f0eb39a4a4c2f66 = function(arg0, arg1) {
const ret = String(getObject(arg1));
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_export_0, wasm.__wbindgen_export_1);
const len1 = WASM_VECTOR_LEN;
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
};
imports.wbg.__wbg_buffer_71667b1101df19da = function(arg0) {
const ret = getObject(arg0).buffer;
return addHeapObject(ret);
};
imports.wbg.__wbg_call_d68488931693e6ee = function() { return handleError(function (arg0, arg1) {
const ret = getObject(arg0).call(getObject(arg1));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_done_3ca5b09e8598078d = function(arg0) {
const ret = getObject(arg0).done;
return ret;
};
imports.wbg.__wbg_entries_d873dde863e50b8c = function(arg0) {
const ret = Object.entries(getObject(arg0));
return addHeapObject(ret);
};
imports.wbg.__wbg_get_c122b1d576cf1fdb = function(arg0, arg1) {
const ret = getObject(arg0)[arg1 >>> 0];
return addHeapObject(ret);
};
imports.wbg.__wbg_get_ddd82e34e6366fb9 = function() { return handleError(function (arg0, arg1) {
const ret = Reflect.get(getObject(arg0), getObject(arg1));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_instanceof_ArrayBuffer_36214dbc6ea8dd3d = function(arg0) {
let result;
try {
result = getObject(arg0) instanceof ArrayBuffer;
} catch (_) {
result = false;
}
const ret = result;
return ret;
};
imports.wbg.__wbg_instanceof_Map_52afb7722e323e8b = function(arg0) {
let result;
try {
result = getObject(arg0) instanceof Map;
} catch (_) {
result = false;
}
const ret = result;
return ret;
};
imports.wbg.__wbg_instanceof_Uint8Array_0d898f7981fe0a2d = function(arg0) {
let result;
try {
result = getObject(arg0) instanceof Uint8Array;
} catch (_) {
result = false;
}
const ret = result;
return ret;
};
imports.wbg.__wbg_isArray_435f9cb9abc7eccc = function(arg0) {
const ret = Array.isArray(getObject(arg0));
return ret;
};
imports.wbg.__wbg_isSafeInteger_2817b2c8ebdd29d2 = function(arg0) {
const ret = Number.isSafeInteger(getObject(arg0));
return ret;
};
imports.wbg.__wbg_iterator_2a6b115668862130 = function() {
const ret = Symbol.iterator;
return addHeapObject(ret);
};
imports.wbg.__wbg_length_b52c3d528b88468e = function(arg0) {
const ret = getObject(arg0).length;
return ret;
};
imports.wbg.__wbg_length_e9123d1e4db12534 = function(arg0) {
const ret = getObject(arg0).length;
return ret;
};
imports.wbg.__wbg_new_9ed4506807911440 = function(arg0) {
const ret = new Uint8Array(getObject(arg0));
return addHeapObject(ret);
};
imports.wbg.__wbg_next_86c8f7dfb19a94eb = function() { return handleError(function (arg0) {
const ret = getObject(arg0).next();
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_next_b39104aeda52ac60 = function(arg0) {
const ret = getObject(arg0).next;
return addHeapObject(ret);
};
imports.wbg.__wbg_set_e8d9380e866a1e41 = function(arg0, arg1, arg2) {
getObject(arg0).set(getObject(arg1), arg2 >>> 0);
};
imports.wbg.__wbg_value_f82ca5432417c8ff = function(arg0) {
const ret = getObject(arg0).value;
return addHeapObject(ret);
};
imports.wbg.__wbindgen_bigint_from_i64 = function(arg0) {
const ret = arg0;
return addHeapObject(ret);
};
imports.wbg.__wbindgen_bigint_from_u64 = function(arg0) {
const ret = BigInt.asUintN(64, arg0);
return addHeapObject(ret);
};
imports.wbg.__wbindgen_bigint_get_as_i64 = function(arg0, arg1) {
const v = getObject(arg1);
const ret = typeof(v) === 'bigint' ? v : undefined;
getDataViewMemory0().setBigInt64(arg0 + 8 * 1, isLikeNone(ret) ? BigInt(0) : ret, true);
getDataViewMemory0().setInt32(arg0 + 4 * 0, !isLikeNone(ret), true);
};
imports.wbg.__wbindgen_boolean_get = function(arg0) {
const v = getObject(arg0);
const ret = typeof(v) === 'boolean' ? (v ? 1 : 0) : 2;
return ret;
};
imports.wbg.__wbindgen_debug_string = function(arg0, arg1) {
const ret = debugString(getObject(arg1));
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_export_0, wasm.__wbindgen_export_1);
const len1 = WASM_VECTOR_LEN;
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
};
imports.wbg.__wbindgen_error_new = function(arg0, arg1) {
const ret = new Error(getStringFromWasm0(arg0, arg1));
return addHeapObject(ret);
};
imports.wbg.__wbindgen_in = function(arg0, arg1) {
const ret = getObject(arg0) in getObject(arg1);
return ret;
};
imports.wbg.__wbindgen_is_bigint = function(arg0) {
const ret = typeof(getObject(arg0)) === 'bigint';
return ret;
};
imports.wbg.__wbindgen_is_function = function(arg0) {
const ret = typeof(getObject(arg0)) === 'function';
return ret;
};
imports.wbg.__wbindgen_is_object = function(arg0) {
const val = getObject(arg0);
const ret = typeof(val) === 'object' && val !== null;
return ret;
};
imports.wbg.__wbindgen_is_string = function(arg0) {
const ret = typeof(getObject(arg0)) === 'string';
return ret;
};
imports.wbg.__wbindgen_jsval_eq = function(arg0, arg1) {
const ret = getObject(arg0) === getObject(arg1);
return ret;
};
imports.wbg.__wbindgen_jsval_loose_eq = function(arg0, arg1) {
const ret = getObject(arg0) == getObject(arg1);
return ret;
};
imports.wbg.__wbindgen_memory = function() {
const ret = wasm.memory;
return addHeapObject(ret);
};
imports.wbg.__wbindgen_number_get = function(arg0, arg1) {
const obj = getObject(arg1);
const ret = typeof(obj) === 'number' ? obj : undefined;
getDataViewMemory0().setFloat64(arg0 + 8 * 1, isLikeNone(ret) ? 0 : ret, true);
getDataViewMemory0().setInt32(arg0 + 4 * 0, !isLikeNone(ret), true);
};
imports.wbg.__wbindgen_object_clone_ref = function(arg0) {
const ret = getObject(arg0);
return addHeapObject(ret);
};
imports.wbg.__wbindgen_object_drop_ref = function(arg0) {
takeObject(arg0);
};
imports.wbg.__wbindgen_string_get = function(arg0, arg1) {
const obj = getObject(arg1);
const ret = typeof(obj) === 'string' ? obj : undefined;
var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_export_0, wasm.__wbindgen_export_1);
var len1 = WASM_VECTOR_LEN;
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
};
imports.wbg.__wbindgen_string_new = function(arg0, arg1) {
const ret = getStringFromWasm0(arg0, arg1);
return addHeapObject(ret);
};
imports.wbg.__wbindgen_throw = function(arg0, arg1) {
throw new Error(getStringFromWasm0(arg0, arg1));
};
return imports;
}
function __wbg_init_memory(imports, memory) {
}
function __wbg_finalize_init(instance, module) {
wasm = instance.exports;
__wbg_init.__wbindgen_wasm_module = module;
cachedDataViewMemory0 = null;
cachedUint8ArrayMemory0 = null;
return wasm;
}
function initSync(module) {
if (wasm !== undefined) return wasm;
if (typeof module !== 'undefined') {
if (Object.getPrototypeOf(module) === Object.prototype) {
({module} = module)
} else {
console.warn('using deprecated parameters for `initSync()`; pass a single object instead')
}
}
const imports = __wbg_get_imports();
__wbg_init_memory(imports);
if (!(module instanceof WebAssembly.Module)) {
module = new WebAssembly.Module(module);
}
const instance = new WebAssembly.Instance(module, imports);
return __wbg_finalize_init(instance, module);
}
async function __wbg_init(module_or_path) {
if (wasm !== undefined) return wasm;
if (typeof module_or_path !== 'undefined') {
if (Object.getPrototypeOf(module_or_path) === Object.prototype) {
({module_or_path} = module_or_path)
} else {
console.warn('using deprecated parameters for the initialization function; pass a single object instead')
}
}
if (typeof module_or_path === 'undefined') {
module_or_path = new URL('lua_fmt_bg.wasm', import.meta.url);
}
const imports = __wbg_get_imports();
if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) {
module_or_path = fetch(module_or_path);
}
__wbg_init_memory(imports);
const { instance, module } = await __wbg_load(await module_or_path, imports);
return __wbg_finalize_init(instance, module);
}
export { initSync };
export default __wbg_init;

View File

@@ -0,0 +1,9 @@
/* tslint:disable */
/* eslint-disable */
export const memory: WebAssembly.Memory;
export const format: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
export const __wbindgen_export_0: (a: number, b: number) => number;
export const __wbindgen_export_1: (a: number, b: number, c: number, d: number) => number;
export const __wbindgen_export_2: (a: number) => void;
export const __wbindgen_add_to_stack_pointer: (a: number) => number;
export const __wbindgen_export_3: (a: number, b: number, c: number) => void;

View File

@@ -0,0 +1,10 @@
import fs from "node:fs/promises";
import initAsync from "./lua_fmt.js";
const wasm = new URL("./lua_fmt_bg.wasm", import.meta.url);
export default function __wbg_init(init = { module_or_path: fs.readFile(wasm) }) {
return initAsync(init);
}
export * from "./lua_fmt.js";

View File

@@ -0,0 +1,8 @@
import initAsync from "./lua_fmt.js";
import wasm from "./lua_fmt_bg.wasm?url";
export default function __wbg_init(input = { module_or_path: wasm }) {
return initAsync(input);
}
export * from "./lua_fmt.js";

View File

@@ -0,0 +1,25 @@
[package]
name = "lua"
authors.workspace = true
description.workspace = true
edition.workspace = true
homepage.workspace = true
keywords.workspace = true
license.workspace = true
repository.workspace = true
version.workspace = true
[package.metadata.wasm-pack.profile.release]
wasm-opt = ["-Os"]
[dependencies]
serde = { workspace = true, features = ["derive"] }
serde-wasm-bindgen = { workspace = true }
serde_json = { workspace = true, features = ["preserve_order"] }
stylua = { workspace = true, features = ["lua52", "lua53", "lua54"] }
wasm-bindgen = { workspace = true }
[lib]
crate-type = ["cdylib", "rlib"]

View File

@@ -0,0 +1,5 @@
cd $(dirname $0)
project_dir=$(pwd)
wasm-pack build --target=web --scope=wasm-fmt .

View File

@@ -0,0 +1,151 @@
use serde::Deserialize;
use stylua_lib::{
format_code, CallParenType, CollapseSimpleStatement, Config as StyluaConfig, IndentType,
LineEndings, OutputVerification, QuoteStyle, SortRequiresConfig,
};
use wasm_bindgen::prelude::wasm_bindgen;
#[wasm_bindgen]
pub fn format(input: &str, filename: &str, config: Option<Config>) -> Result<String, String> {
let _ = filename;
let config = config
.map(|x| serde_wasm_bindgen::from_value::<LuaConfig>(x.clone()))
.transpose()
.map_err(|op| op.to_string())?
.unwrap_or_default();
format_code(input, config.into(), None, OutputVerification::None).map_err(|e| e.to_string())
}
#[wasm_bindgen(typescript_custom_section)]
const TS_Config: &'static str = r#"
interface LayoutConfig {
indent_style?: "tab" | "space";
indent_width?: number;
line_width?: number;
line_ending?: "lf" | "crlf";
}"#;
#[wasm_bindgen(typescript_custom_section)]
const TS_Config: &'static str = r#"
export interface Config extends LayoutConfig {
quote_style?: "AutoPreferDouble" | "AutoPreferSingle" | "ForceDouble" | "ForceSingle";
call_parentheses?: "Always" | "NoSingleString" | "NoSingleTable" | "None" | "Input";
collapse_simple_statement?: | "Never" | "FunctionOnly" | "ConditionalOnly" | "Always";
sort_requires?: boolean;
}"#;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(typescript_type = "Config")]
pub type Config;
}
#[derive(Deserialize, Clone, Default)]
struct LayoutConfig {
#[serde(alias = "indentStyle")]
indent_style: Option<IndentStyle>,
#[serde(alias = "indentWidth")]
indent_width: Option<u8>,
#[serde(alias = "lineWidth")]
line_width: Option<u16>,
#[serde(alias = "lineEnding")]
line_ending: Option<LineEnding>,
}
#[derive(Deserialize, Clone, Default)]
struct LuaConfig {
#[serde(flatten)]
layout: LayoutConfig,
#[serde(alias = "quoteStyle")]
quote_style: Option<QuoteStyle>,
#[serde(alias = "callParentheses")]
call_parentheses: Option<CallParenType>,
#[serde(alias = "collapseSimpleStatement")]
collapse_simple_statement: Option<CollapseSimpleStatement>,
#[serde(alias = "sortRequires")]
sort_requires: Option<bool>,
}
impl From<LuaConfig> for StyluaConfig {
fn from(val: LuaConfig) -> Self {
let mut config = StyluaConfig::default();
if let Some(indent_style) = val.layout.indent_style {
config.indent_type = indent_style.into();
}
if let Some(indent_width) = val.layout.indent_width {
config.indent_width = indent_width as usize;
}
if let Some(line_width) = val.layout.line_width {
config.column_width = line_width as usize;
}
if let Some(line_ending) = val.layout.line_ending {
config.line_endings = line_ending.into();
}
if let Some(quote_style) = val.quote_style {
config.quote_style = quote_style;
}
if let Some(call_parentheses) = val.call_parentheses {
config.call_parentheses = call_parentheses;
}
if let Some(collapse_simple_statement) = val.collapse_simple_statement {
config.collapse_simple_statement = collapse_simple_statement;
}
if let Some(enabled) = val.sort_requires {
let mut sort_requires = SortRequiresConfig::default();
sort_requires.enabled = enabled;
config.sort_requires = sort_requires;
}
config
}
}
#[derive(Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Clone, Copy, Default)]
enum IndentStyle {
Tab,
#[default]
Space,
}
impl From<IndentStyle> for IndentType {
fn from(val: IndentStyle) -> Self {
match val {
IndentStyle::Tab => Self::Tabs,
IndentStyle::Space => Self::Spaces,
}
}
}
#[derive(Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Clone, Copy, Default)]
enum LineEnding {
#[default]
Lf,
Crlf,
}
impl From<LineEnding> for LineEndings {
fn from(val: LineEnding) -> Self {
match val {
LineEnding::Lf => Self::Unix,
LineEnding::Crlf => Self::Windows,
}
}
}

View File

@@ -0,0 +1,4 @@
import type { Plugin } from "prettier";
declare const plugin: Plugin;
export default plugin;

View File

@@ -0,0 +1,19 @@
export {
languages,
printers,
parsers,
options,
defaultOptions
} from './src/index.mjs';
import { languages, printers, parsers, options, defaultOptions } from './src/index.mjs';
const phpPlugin = {
languages,
printers,
parsers,
options,
defaultOptions
};
export default phpPlugin;

View File

@@ -0,0 +1,111 @@
import { printNumber, normalizeMagicMethodName } from "./util.mjs";
const ignoredProperties = new Set([
"loc",
"range",
"raw",
"comments",
"leadingComments",
"trailingComments",
"parenthesizedExpression",
"parent",
"prev",
"start",
"end",
"tokens",
"errors",
"extra",
]);
/**
* This function takes the existing ast node and a copy, by reference
* We use it for testing, so that we can compare pre-post versions of the AST,
* excluding things we don't care about (like node location, case that will be
* changed by the printer, etc.)
*/
function clean(node, newObj) {
if (node.kind === "string") {
// TODO if options are available in this method, replace with
// newObj.isDoubleQuote = !useSingleQuote(node, options);
delete newObj.isDoubleQuote;
}
if (["array", "list"].includes(node.kind)) {
// TODO if options are available in this method, assign instead of delete
delete newObj.shortForm;
}
if (node.kind === "inline") {
if (node.value.includes("___PSEUDO_INLINE_PLACEHOLDER___")) {
return null;
}
newObj.value = newObj.value.replace(/\n/g, "");
}
// continue ((2)); -> continue 2;
// continue 1; -> continue;
if ((node.kind === "continue" || node.kind === "break") && node.level) {
const { level } = newObj;
if (level.kind === "number") {
newObj.level = level.value === "1" ? null : level;
}
}
// if () {{ }} -> if () {}
if (node.kind === "block") {
if (node.children.length === 1 && node.children[0].kind === "block") {
while (newObj.children[0].kind === "block") {
newObj.children = newObj.children[0].children;
}
}
}
// Normalize numbers
if (node.kind === "number") {
newObj.value = printNumber(node.value);
}
const statements = ["foreach", "for", "if", "while", "do"];
if (statements.includes(node.kind)) {
if (node.body && node.body.kind !== "block") {
newObj.body = {
kind: "block",
children: [newObj.body],
};
} else {
newObj.body = newObj.body ? newObj.body : null;
}
if (node.alternate && node.alternate.kind !== "block") {
newObj.alternate = {
kind: "block",
children: [newObj.alternate],
};
} else {
newObj.alternate = newObj.alternate ? newObj.alternate : null;
}
}
if (node.kind === "usegroup" && typeof node.name === "string") {
newObj.name = newObj.name.replace(/^\\/, "");
}
if (node.kind === "useitem") {
newObj.name = newObj.name.replace(/^\\/, "");
}
if (node.kind === "method" && node.name.kind === "identifier") {
newObj.name.name = normalizeMagicMethodName(newObj.name.name);
}
if (node.kind === "noop") {
return null;
}
}
clean.ignoredProperties = ignoredProperties;
export default clean;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,173 @@
import { doc } from "prettier";
import LINGUIST_LANGUAGES_PHP from "linguist-languages/data/PHP";
import LINGUIST_LANGUAGES_HTML_PHP from "linguist-languages/data/HTML_2b_PHP";
import parse from "./parser.mjs";
import print from "./printer.mjs";
import clean from "./clean.mjs";
import options from "./options.mjs";
import {
handleOwnLineComment,
handleEndOfLineComment,
handleRemainingComment,
getCommentChildNodes,
canAttachComment,
isBlockComment,
} from "./comments.mjs";
import { hasPragma, insertPragma } from "./pragma.mjs";
import { locStart, locEnd } from "./loc.mjs";
const { join, hardline } = doc.builders;
function createLanguage(linguistData, { extend, override }) {
const language = {};
for (const key in linguistData) {
const newKey = key === "languageId" ? "linguistLanguageId" : key;
language[newKey] = linguistData[key];
}
if (extend) {
for (const key in extend) {
language[key] = (language[key] || []).concat(extend[key]);
}
}
for (const key in override) {
language[key] = override[key];
}
return language;
}
const languages = [
createLanguage(LINGUIST_LANGUAGES_PHP, {
override: {
parsers: ["php"],
vscodeLanguageIds: ["php"],
},
}),
createLanguage(LINGUIST_LANGUAGES_HTML_PHP, {
override: {
parsers: ["php"],
vscodeLanguageIds: ["php"],
},
}),
];
const parsers = {
php: {
parse,
astFormat: "php",
locStart,
locEnd,
hasPragma,
},
};
const ignoredKeys = new Set([
"kind",
"loc",
"errors",
"extra",
"comments",
"leadingComments",
"enclosingNode",
"precedingNode",
"followingNode",
]);
function getVisitorKeys(node, nonTraversableKeys) {
return Object.keys(node).filter(
(key) => !nonTraversableKeys.has(key) && !ignoredKeys.has(key)
);
}
const printers = {
php: {
print,
getVisitorKeys,
insertPragma,
massageAstNode: clean,
getCommentChildNodes,
canAttachComment,
isBlockComment,
handleComments: {
ownLine: handleOwnLineComment,
endOfLine: handleEndOfLineComment,
remaining: handleRemainingComment,
},
willPrintOwnComments(path) {
const { node } = path;
return node && node.kind === "noop";
},
printComment(path) {
const comment = path.node;
switch (comment.kind) {
case "commentblock": {
// for now, don't touch single line block comments
if (!comment.value.includes("\n")) {
return comment.value;
}
const lines = comment.value.split("\n");
// if this is a block comment, handle indentation
if (
lines
.slice(1, lines.length - 1)
.every((line) => line.trim()[0] === "*")
) {
return join(
hardline,
lines.map(
(line, index) =>
(index > 0 ? " " : "") +
(index < lines.length - 1 ? line.trim() : line.trimLeft())
)
);
}
// otherwise we can't be sure about indentation, so just print as is
return comment.value;
}
case "commentline": {
return comment.value.trimRight();
}
/* c8 ignore next 2 */
default:
throw new Error(`Not a comment: ${JSON.stringify(comment)}`);
}
},
hasPrettierIgnore(path) {
const isSimpleIgnore = (comment) =>
comment.value.includes("prettier-ignore") &&
!comment.value.includes("prettier-ignore-start") &&
!comment.value.includes("prettier-ignore-end");
const { node, parent: parentNode } = path;
return (
(node &&
node.kind !== "classconstant" &&
node.comments &&
node.comments.length > 0 &&
node.comments.some(isSimpleIgnore)) ||
// For proper formatting, the classconstant ignore formatting should
// run on the "constant" child
(node &&
node.kind === "constant" &&
parentNode &&
parentNode.kind === "classconstant" &&
parentNode.comments &&
parentNode.comments.length > 0 &&
parentNode.comments.some(isSimpleIgnore))
);
},
},
};
const defaultOptions = {
tabWidth: 4,
};
export { languages, printers, parsers, options, defaultOptions };

View File

@@ -0,0 +1,4 @@
const loc = (prop) => (node) => node.loc?.[prop]?.offset;
export const locStart = loc("start");
export const locEnd = loc("end");

Some files were not shown because too many files have changed in this diff Show More