fish-lsp
Version:
LSP implementation for fish/fish-shell
182 lines (154 loc) • 4.72 kB
text/typescript
import * as console from 'node:console';
import fs from 'fs';
export interface IConsole {
error(...args: any[]): void;
warn(...args: any[]): void;
info(...args: any[]): void;
log(...args: any[]): void;
}
type _ConsoleMethod = 'error' | 'warn' | 'info' | 'log';
type _CConsole = Console;
export class Logger {
protected _console: IConsole;
/** never print to console */
protected _silence: boolean = true;
/** reformat every log message as json */
protected _onlyJson: boolean = true;
protected logFilePath: string;
private started = false;
constructor(logFilePath: string = '', clear: boolean = true, _console: IConsole = console) {
this.logFilePath = logFilePath;
this._console = _console;
if (clear && this.hasLogFile()) {
this.clearLogFile();
}
}
isStarted(): boolean {
return this.started;
}
start(): void {
this.started = true;
}
toggleSilence() {
this._silence = !this._silence;
}
toggleJson() {
this._onlyJson = !this._onlyJson;
}
hasSilence() {
return this._silence;
}
hasLogFile(): boolean {
return this.logFilePath !== '';
}
clearLogFile(): void {
if (this.hasLogFile()) {
try {
// fs.truncateSync(this.logFilePath, 0);
fs.writeFileSync(this.logFilePath, '');
} catch (error) {
this._console.error(`Error clearing log file: ${error}`);
}
}
}
private logToFile(message: string): void {
if (this.hasLogFile()) {
fs.appendFileSync(this.logFilePath, message + '\n', 'utf-8');
}
}
log(...args: any[]): void {
const formattedMessage = args.map((arg) => {
if (arg instanceof Error) {
return arg.stack || arg.message;
}
if (typeof arg === 'object') {
return JSON.stringify(arg, null, 2);
}
return String(arg);
}).join('\n');
if (!this.hasSilence()) this._console.log(formattedMessage);
if (this.hasLogFile()) this.logToFile(formattedMessage);
}
logAsJson(message: string) {
this.logToFile(JSON.stringify({
date: new Date().toLocaleString(),
message: message,
}));
}
logPropertiesForEachObject<T extends Record<string, any>>(objs: T[], ...keys: (keyof T)[]): void {
objs.forEach((obj, i) => {
// const selectedKeys = keys.filter(key => obj.hasOwnProperty(key));
const selectedKeys = keys.filter(key => Object.prototype.hasOwnProperty.bind(obj, key));
const selectedObj = selectedKeys.reduce((acc, key) => {
acc[key] = obj[key];
return acc;
}, {} as Partial<T>);
const formattedMessage = `${i}: ${JSON.stringify(selectedObj, null, 2)}`;
this._console.log(formattedMessage);
this.logToFile(formattedMessage);
});
}
showLogfileText(): void {
if (!this.hasLogFile()) {
this._console.log('No log file specified');
}
this._console.log('--- Log file name ---');
this._console.log(this.logFilePath);
this._console.log('--- Log file text ---');
this._console.log(fs.readFileSync(this.logFilePath, 'utf-8'));
}
getLoggingOpts() {
return {
logFile: this.hasLogFile(),
silence: this.hasSilence(),
};
}
}
export class JestLogger extends Logger {
private _jestConsole = console;
private _globalConsole = global.console;
constructor() {
super('', false, console);
}
beforeEachTest(): void {
global.console = this._globalConsole;
}
afterEachTest(): void {
global.console = this._jestConsole;
}
}
export let logger: Logger = new Logger();
export function createServerLogger(logFilePath: string, clear: boolean = true, connectionConsole?: IConsole): Logger {
if (!logger.isStarted()) {
logger = new Logger(logFilePath, clear, connectionConsole);
logger.start();
}
return logger;
}
export function createJestLogger(): JestLogger {
return new JestLogger();
}
export function logToStdout(message: string, newline = true): void {
const output: string = `${message}${!!newline && '\n'}`;
process.stdout.write(output);
}
/** util for joining multiple strings and logging to stdout with trailing `\n` */
export function logToStdoutJoined(...message: string[]) {
const output: string = `${message.join('')}\n`;
process.stdout.write(output);
}
/**
* A helper function to wrap default logging behavior for the logger, if it is started.
* - If logger is started, log to logger `logger.log()`
* - If logger is not started, log to stdout `logToStdout()`
*
* @param args - any number of arguments to log
* @returns void
*/
export function log(...args: any[]): void {
if (logger.isStarted()) {
logger.log(...args);
} else {
logToStdout(args.join(''), true);
}
}