UNPKG

perlnavigator-server

Version:

Perl language server

151 lines (136 loc) 5.81 kB
import { DefinitionParams, DocumentFormattingParams, TextEdit, DocumentRangeFormattingParams, Position, Range, WorkspaceFolder } from "vscode-languageserver/node"; import { TextDocument } from "vscode-languageserver-textdocument"; import { PerlDocument, PerlElem, NavigatorSettings } from "./types"; import { async_execFile, getPerlimportsProfile, nLog } from "./utils"; import { dirname, join } from "path"; import Uri from "vscode-uri"; import { execFileSync } from "child_process"; import { getPerlAssetsPath } from "./assets"; import { startProgress, endProgress } from "./progress"; import { Connection } from "vscode-languageserver/node"; export async function formatDoc( params: DocumentFormattingParams, txtDoc: TextDocument, settings: NavigatorSettings, workspaceFolders: WorkspaceFolder[] | null, connection: Connection ): Promise<TextEdit[] | undefined> { return await maybeReturnEdits(Range.create(Position.create(0, 0), Position.create(txtDoc.lineCount, 0)), txtDoc, settings, workspaceFolders, connection); } export async function formatRange( params: DocumentRangeFormattingParams, txtDoc: TextDocument, settings: NavigatorSettings, workspaceFolders: WorkspaceFolder[] | null, connection: Connection ): Promise<TextEdit[] | undefined> { const offset = params.range.end.character > 0 ? 1 : 0; return await maybeReturnEdits( Range.create(Position.create(params.range.start.line, 0), Position.create(params.range.end.line + offset, 0)), txtDoc, settings, workspaceFolders, connection ); } async function maybeReturnEdits( range: Range, txtDoc: TextDocument, settings: NavigatorSettings, workspaceFolders: WorkspaceFolder[] | null, connection: Connection ): Promise<TextEdit[] | undefined> { const text = txtDoc.getText(range); if (!text) { return; } const progressToken = await startProgress(connection, "Formatting doc", settings); let newSource: string = ""; const fixedImports = await perlimports(txtDoc, text, settings); if (fixedImports) { newSource = fixedImports; } const tidedSource = await perltidy(fixedImports || text, settings, workspaceFolders); if (tidedSource) { newSource = tidedSource; } endProgress(connection, progressToken); if (!newSource) { // If we failed on both tidy and imports return; } const edits: TextEdit = { range: range, newText: newSource, }; return [edits]; } async function perlimports(doc: TextDocument, code: string, settings: NavigatorSettings): Promise<string | undefined> { if (!settings.perlimportsTidyEnabled) return; const importsPath = join(await getPerlAssetsPath(), "perlimportsWrapper.pl"); let cliParams: string[] = [importsPath].concat(getPerlimportsProfile(settings)); cliParams = cliParams.concat(["--filename", Uri.parse(doc.uri).fsPath]); nLog("Now starting perlimports with: " + cliParams.join(" "), settings); try { const process = async_execFile(settings.perlPath, settings.perlParams.concat(cliParams), { timeout: 25000, maxBuffer: 20 * 1024 * 1024 }); process?.child?.stdin?.on("error", (error: any) => { nLog("perlImports Error Caught: ", settings); nLog(error, settings); }); process?.child?.stdin?.write(code); process?.child?.stdin?.end(); const out = await process; return out.stdout; } catch (error: any) { nLog("Attempted to run perlimports tidy " + error.stdout, settings); return; } } async function perltidy(code: string, settings: NavigatorSettings, workspaceFolders: WorkspaceFolder[] | null): Promise<string | undefined> { if (!settings.perltidyEnabled) return; const tidy_path = join(await getPerlAssetsPath(), "tidyWrapper.pl"); let tidyParams: string[] = [tidy_path].concat(getTidyProfile(workspaceFolders, settings)); nLog("Now starting perltidy with: " + tidyParams.join(" "), settings); let output: string | Buffer; try { const process = async_execFile(settings.perlPath, settings.perlParams.concat(tidyParams), { timeout: 25000, maxBuffer: 20 * 1024 * 1024 }); process?.child?.stdin?.on("error", (error: any) => { nLog("PerlTidy Error Caught: ", settings); nLog(error, settings); }); process?.child?.stdin?.write(code); process?.child?.stdin?.end(); const out = await process; output = out.stdout; } catch (error: any) { nLog("Perltidy failed with unknown error", settings); nLog(error, settings); return; } let pieces = output.split("ee4ffa34-a14f-4609-b2e4-bf23565f3262"); if (pieces.length > 1) { return pieces[1]; } else { return; } } function getTidyProfile(workspaceFolders: WorkspaceFolder[] | null, settings: NavigatorSettings): string[] { let profileCmd: string[] = []; if (settings.perltidyProfile) { let profile = settings.perltidyProfile; if (profile.indexOf("$workspaceFolder") != -1) { if (workspaceFolders) { // TODO: Fix this. Only uses the first workspace folder const workspaceUri = Uri.parse(workspaceFolders[0].uri).fsPath; profileCmd.push("--profile"); profileCmd.push(profile.replaceAll("$workspaceFolder", workspaceUri)); } else { nLog("You specified $workspaceFolder in your perltidy path, but didn't include any workspace folders. Ignoring profile.", settings); } } else { profileCmd.push("--profile"); profileCmd.push(profile); } } return profileCmd; }