From 7350a6f6e92c01370e330e7395765645b5eb7faa Mon Sep 17 00:00:00 2001 From: Mats Larsen Date: Sun, 29 Jan 2023 19:06:14 +0100 Subject: Tsify lib/handlers/formatting.js --- lib/formatters/base.interfaces.ts | 1 + lib/formatters/index.ts | 9 ++- lib/handlers/formatting.js | 135 -------------------------------- lib/handlers/formatting.ts | 146 +++++++++++++++++++++++++++++++++++ static/styles/themes/dark-theme.scss | 3 +- 5 files changed, 157 insertions(+), 137 deletions(-) delete mode 100644 lib/handlers/formatting.js create mode 100644 lib/handlers/formatting.ts diff --git a/lib/formatters/base.interfaces.ts b/lib/formatters/base.interfaces.ts index 32f4b0cd8..855dd15b8 100644 --- a/lib/formatters/base.interfaces.ts +++ b/lib/formatters/base.interfaces.ts @@ -27,6 +27,7 @@ export interface FormatterInfo { exe: string; styles: string[]; type: string; + version: string; explicitVersion?: string; versionArgument?: string; versionReExp?: string; diff --git a/lib/formatters/index.ts b/lib/formatters/index.ts index f12b5e225..85c1ede57 100644 --- a/lib/formatters/index.ts +++ b/lib/formatters/index.ts @@ -23,10 +23,17 @@ // POSSIBILITY OF SUCH DAMAGE. import {makeKeyedTypeGetter} from '../keyed-type'; +import {Keyable} from '../keyed-type.interfaces'; import * as all from './_all'; +import {BaseFormatter} from './base'; +import {FormatterInfo} from './base.interfaces'; export {BaseFormatter} from './base'; export * from './_all'; -export const getFormatterTypeByKey = makeKeyedTypeGetter('formatter', all); +type FormatterClass = { + new (opts: FormatterInfo): BaseFormatter; +} & Keyable; + +export const getFormatterTypeByKey = makeKeyedTypeGetter('formatter', all); diff --git a/lib/handlers/formatting.js b/lib/handlers/formatting.js deleted file mode 100644 index 4c0a5fe45..000000000 --- a/lib/handlers/formatting.js +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) 2018, Compiler Explorer Authors -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -import _ from 'underscore'; - -import * as exec from '../exec'; -import {getFormatterTypeByKey} from '../formatters'; -import {logger} from '../logger'; - -export class FormattingHandler { - constructor(ceProps) { - this.formatters = {}; - this.ceProps = ceProps; - const formatters = _.compact(ceProps('formatters', '').split(':')); - _.each(formatters, this.getFormatterInfo.bind(this)); - } - - async getFormatterInfo(formatter) { - const exe = this.ceProps(`formatter.${formatter}.exe`); - const type = this.ceProps(`formatter.${formatter}.type`); - if (!exe) { - return logger.warn(`Formatter ${formatter} does not have a valid executable. Skipping...`); - } - if (!type) { - return logger.warn(`Formatter ${formatter} does not have a formatter class. Skipping...`); - } - const versionArg = this.ceProps(`formatter.${formatter}.version`, '--version'); - const versionRe = this.ceProps(`formatter.${formatter}.versionRe`, '.*'); - const hasExplicitVersion = this.ceProps(`formatter.${formatter}.explicitVersion`, '') !== ''; - try { - const result = await exec.execute(exe, [versionArg], {}); - const match = result.stdout.match(versionRe); - const formatterClass = getFormatterTypeByKey(type); - const styleList = this.ceProps(`formatter.${formatter}.styles`); - const styles = styleList === '' ? [] : styleList.split(':'); - // If there is an explicit version, grab it. Otherwise try to filter the output - const version = hasExplicitVersion - ? this.ceProps(`formatter.${formatter}.explicitVersion`) - : match - ? match[0] - : result.stdout; - this.formatters[formatter] = new formatterClass({ - name: this.ceProps(`formatter.${formatter}.name`, exe), - exe, - version, - styles, - type, - }); - } catch (err) { - logger.warn(`Error while fetching tool info for ${exe}:`, {err}); - } - } - - async handle(req, res) { - const name = req.params.tool; - const formatter = this.formatters[name]; - // Ensure the formatter exists - if (!formatter) { - return res.status(422).send({ - exit: 2, - answer: `Unknown format tool '${name}'`, - }); - } - // Ensure there is source code to format - if (!req.body || !req.body.source) { - return res.send({exit: 0, answer: ''}); - } - // Ensure the wanted style is valid for the formatter - const style = req.body.base; - if (!formatter.isValidStyle(style)) { - return res.status(422).send({ - exit: 3, - answer: `Style '${style}' is not supported`, - }); - } - try { - // Perform the actual formatting - const result = await formatter.format(req.body.source, { - useSpaces: req.body.useSpaces === undefined ? true : req.body.useSpaces, - tabWidth: req.body.tabWidth === undefined ? 4 : req.body.tabWidth, - baseStyle: req.body.base, - }); - res.send({ - exit: result.code, - answer: result.stdout || result.stderr || '', - }); - } catch (err) { - res.status(500).send({ - exit: 1, - thrown: true, - answer: err.message || 'Internal server error', - }); - } - } - - async internalFormat(formatterName, style, source) { - const formatter = this.formatters[formatterName]; - // Ensure the formatter exists - if (!formatter) { - throw new Error('Unknown formatter name'); - } - // Ensure the wanted style is valid for the formatter - if (!formatter.isValidStyle(style)) { - throw new Error('Unsupported formatter style'); - } - // Perform the actual formatting - const result = await formatter.format(source, { - useSpaces: true, // hard coded for now, TODO should this be changed? - tabWidth: 4, - baseStyle: style, - }); - return [result.stdout || '', result.stderr]; - } -} diff --git a/lib/handlers/formatting.ts b/lib/handlers/formatting.ts new file mode 100644 index 000000000..0457fd281 --- /dev/null +++ b/lib/handlers/formatting.ts @@ -0,0 +1,146 @@ +// Copyright (c) 2018, Compiler Explorer Authors +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +import express from 'express'; +import _ from 'underscore'; + +import * as exec from '../exec'; +import {BaseFormatter, getFormatterTypeByKey} from '../formatters'; +import {logger} from '../logger'; +import {PropertyGetter} from '../properties.interfaces'; + +export class FormattingHandler { + private formatters: Record = {}; + + public constructor(private ceProps: PropertyGetter) { + const formatters = _.compact(ceProps('formatters', '').split(':')); + for (const formatter of formatters) { + this.getFormatterInfo(formatter); + } + } + + private async getFormatterInfo(formatterName: string): Promise { + const exe = this.ceProps(`formatter.${formatterName}.exe`); + const type = this.ceProps(`formatter.${formatterName}.type`); + if (!exe) { + logger.warn(`Formatter ${formatterName} does not have a valid executable. Skipping...`); + return; + } + if (!type) { + logger.warn(`Formatter ${formatterName} does not have a formatter class. Skipping...`); + return; + } + const versionArgument = this.ceProps(`formatter.${formatterName}.version`, '--version'); + const versionRegExp = this.ceProps(`formatter.${formatterName}.versionRe`, '.*'); + const hasExplicitVersion = this.ceProps(`formatter.${formatterName}.explicitVersion`, '') !== ''; + try { + const result = await exec.execute(exe, [versionArgument], {}); + const match = result.stdout.match(versionRegExp); + const formatterClass = getFormatterTypeByKey(type); + const styleList = this.ceProps(`formatter.${formatterName}.styles`); + const styles = styleList === '' ? [] : styleList.split(':'); + // If there is an explicit version, grab it. Otherwise try to filter the output + const version = hasExplicitVersion + ? this.ceProps(`formatter.${formatterName}.explicitVersion`) + : match + ? match[0] + : result.stdout; + this.formatters[formatterName] = new formatterClass({ + name: this.ceProps(`formatter.${formatterName}.name`, exe), + exe, + version, + styles, + type, + }); + } catch (err: unknown) { + logger.warn(`Error while fetching tool info for ${exe}:`, {err}); + } + } + + public async handle(req: express.Request, res: express.Response): Promise { + const name = req.params.tool; + const formatter = this.formatters[name]; + // Ensure the formatter exists + if (!formatter) { + res.status(422).send({ + exit: 2, + answer: `Unknown format tool '${name}'`, + }); + return; + } + // Ensure there is source code to format + if (!req.body || !req.body.source) { + res.send({exit: 0, answer: ''}); + return; + } + // Ensure the wanted style is valid for the formatter + const style = req.body.base; + if (!formatter.isValidStyle(style)) { + res.status(422).send({ + exit: 3, + answer: `Style '${style}' is not supported`, + }); + return; + } + try { + // Perform the actual formatting + const result = await formatter.format(req.body.source, { + useSpaces: req.body.useSpaces === undefined ? true : req.body.useSpaces, + tabWidth: req.body.tabWidth === undefined ? 4 : req.body.tabWidth, + baseStyle: req.body.base, + }); + res.send({ + exit: result.code, + answer: result.stdout || result.stderr || '', + }); + } catch (err: unknown) { + res.status(500).send({ + exit: 1, + thrown: true, + answer: + (err && Object.hasOwn(err, 'message') && (err as Record<'message', 'string'>).message) || + 'Internal server error', + }); + } + } + + async internalFormat(formatterName: string, style: string, source: string): Promise<[string, string]> { + const formatter = this.formatters[formatterName]; + // Ensure the formatter exists + if (!formatter) { + throw new Error('Unknown formatter name'); + } + // Ensure the wanted style is valid for the formatter + if (!formatter.isValidStyle(style)) { + throw new Error('Unsupported formatter style'); + } + // Perform the actual formatting + const result = await formatter.format(source, { + useSpaces: true, // hard coded for now, TODO should this be changed? + tabWidth: 4, + baseStyle: style, + }); + return [result.stdout || '', result.stderr]; + } +} diff --git a/static/styles/themes/dark-theme.scss b/static/styles/themes/dark-theme.scss index d10b48a41..65791676d 100644 --- a/static/styles/themes/dark-theme.scss +++ b/static/styles/themes/dark-theme.scss @@ -492,7 +492,8 @@ textarea.form-control { border-color: #474747 !important; } -.popover .arrow::after, .popover .arrow::before { +.popover .arrow::after, +.popover .arrow::before { border-left-color: #151515 !important; border-right-color: #151515 !important; } -- cgit v1.2.3