@language python
"""
Format the current node or selected code snippet using Prettier.
[Dependency Installation]
To support all configured languages, it is recommended to install globally using pnpm (saves disk space):
pnpm add -g prettier @prettier/plugin-pug prettier-plugin-java @prettier/plugin-clang-format
Note: Formatting C/C++ requires clang-format to be installed and available in your system PATH.
"""
import subprocess
import shutil
import re
prettier_bin = shutil.which('prettier')
if not prettier_bin:
g.es("Prettier not found. Please refer to the [Dependency Installation] instructions at the top.", color='red')
else:
# 1. Get the node's default language
d = g.get_directives_dict(p)
node_lang = d.get('language')
# 2. Language-to-parser mapping
parser_map = {
'html': 'html',
'javascript': 'babel',
'typescript': 'typescript',
'css': 'css',
'scss': 'scss',
'pug': 'pug',
'java': 'java',
'cplusplus': 'clang-format',
'cpp': 'clang-format',
'c': 'clang-format',
}
w = c.frame.body.wrapper
has_sel = w.hasSelection()
# 3. Determine the parser to use
parser = None
if has_sel:
i, j = w.getSelectionRange()
raw_text = w.getSelectedText()
target_desc = "selected snippet"
# Sniff for @language directive inside the selected text
match = re.search(r'@language\s+([a-zA-Z0-9_-]+)', raw_text, re.IGNORECASE)
if match:
fragment_lang = match.group(1).lower()
parser = parser_map.get(fragment_lang, fragment_lang)
g.es(f"Auto-detected snippet language: {fragment_lang}", color='purple')
else:
parser = parser_map.get(node_lang)
else:
raw_text = p.b
target_desc = "entire body"
parser = parser_map.get(node_lang)
# ----------------------------------------------------------------
# [Key Fix]: Strip Leo's @language directives to prevent Prettier errors
# ----------------------------------------------------------------
directives = []
def extract_directive(m):
directives.append(m.group(0))
return "" # Remove the line from the text sent to Prettier
# Match whole lines starting with @language (supports multiline and leading whitespace)
text_to_format = re.sub(r'^[ \t]*@language\s+[a-zA-Z0-9_-]+[ \t]*\n?', extract_directive, raw_text, flags=re.IGNORECASE | re.MULTILINE)
if not parser:
g.es(f"Skipped: Cannot determine parser. Node language is '{node_lang}'.", color='orange')
elif not text_to_format.strip():
g.es(f"The {target_desc} is empty or contains only directives. Nothing to format.")
else:
try:
# 4. Run Prettier (enforcing 4-space indentation)
proc = subprocess.Popen(
[prettier_bin, '--parser', parser, '--tab-width', '4'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
shell=False
)
stdout, stderr = proc.communicate(input=text_to_format)
# 5. Handle the result based on the return code
if proc.returncode == 0:
# [Key Fix]: Re-attach the stripped directives to the top of the formatted code
final_text = "".join(directives) + stdout
if raw_text != final_text:
if has_sel:
p.b = p.b[:i] + final_text + p.b[j:]
w.setSelectionRange(i, i + len(final_text))
else:
p.b = final_text
c.redraw()
g.es(f"Formatted {target_desc} (Prettier - {parser})", color='green')
else:
g.es(f"The {target_desc} is already properly formatted (Prettier - {parser})", color='blue')
else:
g.es(f"Formatting failed: {stderr.strip()}", color='red')
except Exception as e:
g.es(f"Execution error: {e}", color='red')